-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpkt.c
More file actions
243 lines (216 loc) · 6.11 KB
/
pkt.c
File metadata and controls
243 lines (216 loc) · 6.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*
* Routines that work on unarmored pgp packets
*/
#include <string.h>
#include "pgpr_internal.h"
/*
* PGP packet decoding
*
* Note that we reject indefinite length/partial bodies and lengths >= 16 MByte
* right away so that we do not have to worry about integer overflows.
*/
/** \ingroup pgpr
* Decode length in old format packet headers.
* @param s pointer to packet (including tag)
* @param slen buffer size
* @param[out] *lenp decoded length
* @return packet header length, 0 on error
*/
static inline size_t pgprOldLen(const uint8_t *s, size_t slen, size_t *lenp)
{
size_t dlen, hlen;
if (slen < 2)
return 0;
hlen = 1 << (s[0] & 0x3);
/* Reject indefinite length packets and check bounds */
if (hlen == 8 || slen < hlen + 1)
return 0;
if (hlen == 1)
dlen = s[1];
else if (hlen == 2)
dlen = s[1] << 8 | s[2];
else if (hlen == 4 && s[1] == 0)
dlen = s[2] << 16 | s[3] << 8 | s[4];
else
return 0;
if (slen - (1 + hlen) < dlen)
return 0;
*lenp = dlen;
return hlen + 1;
}
/** \ingroup pgpr
* Decode length from 1, 2, or 5 octet body length encoding, used in
* new format packet headers.
* Partial body lengths are (intentionally) not supported.
* @param s pointer to packet (including tag)
* @param slen buffer size
* @param[out] *lenp decoded length
* @return packet header length, 0 on error
*/
static inline size_t pgprNewLen(const uint8_t *s, size_t slen, size_t *lenp)
{
size_t dlen, hlen;
if (slen > 1 && s[1] < 192) {
hlen = 2;
dlen = s[1];
} else if (slen > 3 && s[1] < 224) {
hlen = 3;
dlen = (((s[1]) - 192) << 8) + s[2] + 192;
} else if (slen > 6 && s[1] == 255 && s[2] == 0) {
hlen = 6;
dlen = s[3] << 16 | s[4] << 8 | s[5];
} else {
return 0;
}
if (slen - hlen < dlen)
return 0;
*lenp = dlen;
return hlen;
}
pgprRC pgprDecodePkt(const uint8_t *p, size_t plen, pgprPkt *pkt)
{
pgprRC rc = PGPR_ERROR_CORRUPT_PGP_PACKET; /* assume failure */
/* Valid PGP packet header must always have two or more bytes in it */
if (p && plen >= 2 && p[0] & 0x80) {
size_t hlen;
if (p[0] & 0x40) {
/* New format packet, body length encoding in second byte */
hlen = pgprNewLen(p, plen, &pkt->blen);
pkt->tag = (p[0] & 0x3f);
} else {
/* Old format packet */
hlen = pgprOldLen(p, plen, &pkt->blen);
pkt->tag = (p[0] >> 2) & 0xf;
}
/* Does the packet header and its body fit in our boundaries? */
if (hlen && (hlen + pkt->blen <= plen)) {
pkt->head = p;
pkt->body = pkt->head + hlen;
rc = PGPR_OK;
}
}
return rc;
}
pgprRC pgprSignatureParse(const uint8_t *pkts, size_t pktslen, pgprItem *ret, char **lints)
{
pgprItem item = NULL;
pgprRC rc = PGPR_ERROR_CORRUPT_PGP_PACKET; /* assume failure */
pgprPkt pkt;
if (lints)
*lints = NULL;
if (pktslen > PGPR_MAX_OPENPGP_BYTES || pgprDecodePkt(pkts, pktslen, &pkt))
goto exit;
if (pkt.tag != PGPRTAG_SIGNATURE) {
rc = PGPR_ERROR_UNEXPECTED_PGP_PACKET;
goto exit;
}
item = pgprItemNew(pkt.tag);
if (!item) {
rc = PGPR_ERROR_NO_MEMORY;
goto exit;
}
rc = pgprParseSig(&pkt, item);
/* treat trailing data as error */
if (rc == PGPR_OK && (pkt.body - pkt.head) + pkt.blen != pktslen)
rc = PGPR_ERROR_CORRUPT_PGP_PACKET;
exit:
if (ret && rc == PGPR_OK)
*ret = item;
else {
if (lints && rc != PGPR_OK)
pgprAddLint(item, lints, rc);
pgprItemFree(item);
}
return rc;
}
pgprRC pgprPubkeyParse(const uint8_t *pkts, size_t pktslen, pgprItem *ret, char **lints)
{
pgprItem key = NULL;
pgprRC rc = PGPR_ERROR_CORRUPT_PGP_PACKET; /* assume failure */
pgprPkt pkt;
if (lints)
*lints = NULL;
if (pktslen > PGPR_MAX_OPENPGP_BYTES || pgprDecodePkt(pkts, pktslen, &pkt))
goto exit;
if (pkt.tag != PGPRTAG_PUBLIC_KEY) {
rc = PGPR_ERROR_UNEXPECTED_PGP_PACKET;
goto exit;
}
key = pgprItemNew(pkt.tag);
if (!key) {
rc = PGPR_ERROR_NO_MEMORY;
goto exit;
}
/* use specialized certificate parsing implementation */
rc = pgprParseCertificate(pkts, pktslen, key);
exit:
if (ret && rc == PGPR_OK)
*ret = key;
else {
if (lints && rc != PGPR_OK)
pgprAddLint(key, lints, rc);
pgprItemFree(key);
}
return rc;
}
pgprRC pgprPubkeyParseSubkeys(const uint8_t *pkts, size_t pktslen,
pgprItem key, pgprItem **subkeys, int *subkeysCount)
{
return pgprParseCertificateSubkeys(pkts, pktslen, key, subkeys, subkeysCount);
}
pgprRC pgprPubkeyCertLen(const uint8_t *pkts, size_t pktslen, size_t *certlen)
{
const uint8_t *p = pkts;
const uint8_t *pend = pkts + pktslen;
pgprPkt pkt;
while (p < pend) {
if (pgprDecodePkt(p, (pend - p), &pkt))
return PGPR_ERROR_CORRUPT_PGP_PACKET;
if ((pkt.tag == PGPRTAG_PUBLIC_KEY || pkt.tag == PGPRTAG_SECRET_KEY) && pkts != p) {
pktslen = p - pkts;
break;
}
p += (pkt.body - pkt.head) + pkt.blen;
}
*certlen = pktslen;
return PGPR_OK;
}
static pgprRC parse_key_fp(const uint8_t *pkts, size_t pktslen, pgprItem key)
{
pgprPkt pkt;
memset(key, 0, sizeof(*key));
if (pgprDecodePkt(pkts, pktslen, &pkt))
return PGPR_ERROR_CORRUPT_PGP_PACKET;
if (pkt.tag != PGPRTAG_PUBLIC_KEY && pkt.tag != PGPRTAG_PUBLIC_SUBKEY)
return PGPR_ERROR_UNEXPECTED_PGP_PACKET;
key->tag = pkt.tag;
return pgprParseKeyFp(&pkt, key);
}
pgprRC pgprPubkeyKeyID(const uint8_t *pkts, size_t pktslen, pgprKeyID_t keyid)
{
struct pgprItem_s key;
pgprRC rc = parse_key_fp(pkts, pktslen, &key);
if (rc == PGPR_OK && !(key.saved & PGPRITEM_SAVED_ID))
rc = PGPR_ERROR_INTERNAL;
if (rc == PGPR_OK)
memcpy(keyid, key.keyid, sizeof(key.keyid));
return rc;
}
pgprRC pgprPubkeyFingerprint(const uint8_t *pkts, size_t pktslen,
uint8_t **fp, size_t *fp_len, int *fp_version)
{
struct pgprItem_s key;
pgprRC rc = parse_key_fp(pkts, pktslen, &key);
if (rc == PGPR_OK && !(key.saved & PGPRITEM_SAVED_FP))
rc = PGPR_ERROR_INTERNAL;
if (rc == PGPR_OK) {
uint8_t *fp_dup = pgprMemdup(key.fp, key.fp_len);
if (!fp_dup)
return PGPR_ERROR_NO_MEMORY;
*fp = fp_dup;
*fp_len = key.fp_len;
if (fp_version)
*fp_version = key.fp_version;
}
return rc;
}