Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * pgp-armor.c
3 : : * PGP ascii-armor.
4 : : *
5 : : * Copyright (c) 2005 Marko Kreen
6 : : * All rights reserved.
7 : : *
8 : : * Redistribution and use in source and binary forms, with or without
9 : : * modification, are permitted provided that the following conditions
10 : : * are met:
11 : : * 1. Redistributions of source code must retain the above copyright
12 : : * notice, this list of conditions and the following disclaimer.
13 : : * 2. Redistributions in binary form must reproduce the above copyright
14 : : * notice, this list of conditions and the following disclaimer in the
15 : : * documentation and/or other materials provided with the distribution.
16 : : *
17 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 : : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 : : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 : : * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 : : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 : : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 : : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 : : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 : : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 : : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 : : * SUCH DAMAGE.
28 : : *
29 : : * contrib/pgcrypto/pgp-armor.c
30 : : */
31 : :
32 : : #include "postgres.h"
33 : :
34 : : #include "pgp.h"
35 : : #include "px.h"
36 : :
37 : : /*
38 : : * BASE64 - duplicated :(
39 : : */
40 : :
41 : : static const unsigned char _base64[] =
42 : : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
43 : :
44 : : static int
2237 tgl@sss.pgh.pa.us 45 :CBC 10 : pg_base64_encode(const uint8 *src, unsigned len, uint8 *dst)
46 : : {
47 : : uint8 *p,
6853 bruce@momjian.us 48 : 10 : *lend = dst + 76;
49 : : const uint8 *s,
50 : 10 : *end = src + len;
51 : 10 : int pos = 2;
52 : 10 : unsigned long buf = 0;
53 : :
54 : 10 : s = src;
55 : 10 : p = dst;
56 : :
57 [ + + ]: 131 : while (s < end)
58 : : {
59 : 121 : buf |= *s << (pos << 3);
60 : 121 : pos--;
61 : 121 : s++;
62 : :
63 : : /*
64 : : * write it out
65 : : */
66 [ + + ]: 121 : if (pos < 0)
67 : : {
68 : 37 : *p++ = _base64[(buf >> 18) & 0x3f];
69 : 37 : *p++ = _base64[(buf >> 12) & 0x3f];
70 : 37 : *p++ = _base64[(buf >> 6) & 0x3f];
71 : 37 : *p++ = _base64[buf & 0x3f];
72 : :
73 : 37 : pos = 2;
74 : 37 : buf = 0;
75 : : }
76 [ + + ]: 121 : if (p >= lend)
77 : : {
78 : 1 : *p++ = '\n';
79 : 1 : lend = p + 76;
80 : : }
81 : : }
82 [ + + ]: 10 : if (pos != 2)
83 : : {
84 : 6 : *p++ = _base64[(buf >> 18) & 0x3f];
85 : 6 : *p++ = _base64[(buf >> 12) & 0x3f];
86 [ + + ]: 6 : *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
87 : 6 : *p++ = '=';
88 : : }
89 : :
90 : 10 : return p - dst;
91 : : }
92 : :
93 : : /* probably should use lookup table */
94 : : static int
2237 tgl@sss.pgh.pa.us 95 : 178 : pg_base64_decode(const uint8 *src, unsigned len, uint8 *dst)
96 : : {
6853 bruce@momjian.us 97 : 178 : const uint8 *srcend = src + len,
6756 98 : 178 : *s = src;
6853 99 : 178 : uint8 *p = dst;
100 : : char c;
101 : 178 : unsigned b = 0;
102 : 178 : unsigned long buf = 0;
103 : 178 : int pos = 0,
6756 104 : 178 : end = 0;
105 : :
6853 106 [ + + ]: 85130 : while (s < srcend)
107 : : {
108 : 84952 : c = *s++;
109 [ + + + + ]: 84952 : if (c >= 'A' && c <= 'Z')
110 : 35636 : b = c - 'A';
111 [ + + + - ]: 49316 : else if (c >= 'a' && c <= 'z')
112 : 32762 : b = c - 'a' + 26;
113 [ + + + + ]: 16554 : else if (c >= '0' && c <= '9')
114 : 12460 : b = c - '0' + 52;
115 [ + + ]: 4094 : else if (c == '+')
116 : 1302 : b = 62;
117 [ + + ]: 2792 : else if (c == '/')
118 : 1344 : b = 63;
119 [ + + ]: 1448 : else if (c == '=')
120 : : {
121 : : /*
122 : : * end sequence
123 : : */
124 [ + + ]: 116 : if (!end)
125 : : {
126 [ + + ]: 67 : if (pos == 2)
127 : 49 : end = 1;
128 [ + - ]: 18 : else if (pos == 3)
129 : 18 : end = 2;
130 : : else
6853 bruce@momjian.us 131 :UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
132 : : }
6853 bruce@momjian.us 133 :CBC 116 : b = 0;
134 : : }
135 [ + + + - : 1332 : else if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
- + - - ]
136 : 1332 : continue;
137 : : else
6853 bruce@momjian.us 138 :UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
139 : :
140 : : /*
141 : : * add it to buffer
142 : : */
6853 bruce@momjian.us 143 :CBC 83620 : buf = (buf << 6) + b;
144 : 83620 : pos++;
145 [ + + ]: 83620 : if (pos == 4)
146 : : {
147 : 20905 : *p++ = (buf >> 16) & 255;
148 [ + + + + ]: 20905 : if (end == 0 || end > 1)
149 : 20856 : *p++ = (buf >> 8) & 255;
150 [ + + - + ]: 20905 : if (end == 0 || end > 2)
151 : 20838 : *p++ = buf & 255;
152 : 20905 : buf = 0;
153 : 20905 : pos = 0;
154 : : }
155 : : }
156 : :
157 [ - + ]: 178 : if (pos != 0)
6853 bruce@momjian.us 158 :UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
6853 bruce@momjian.us 159 :CBC 178 : return p - dst;
160 : : }
161 : :
162 : : static unsigned
2237 tgl@sss.pgh.pa.us 163 : 10 : pg_base64_enc_len(unsigned srclen)
164 : : {
165 : : /*
166 : : * 3 bytes will be converted to 4, linefeed after 76 chars
167 : : */
311 168 : 10 : return (srclen + 2) / 3 * 4 + srclen / (76 * 3 / 4);
169 : : }
170 : :
171 : : static unsigned
2237 172 : 89 : pg_base64_dec_len(unsigned srclen)
173 : : {
6853 bruce@momjian.us 174 : 89 : return (srclen * 3) >> 2;
175 : : }
176 : :
177 : : /*
178 : : * PGP armor
179 : : */
180 : :
181 : : static const char *const armor_header = "-----BEGIN PGP MESSAGE-----\n";
182 : : static const char *const armor_footer = "\n-----END PGP MESSAGE-----\n";
183 : :
184 : : /* CRC24 implementation from rfc2440 */
185 : : #define CRC24_INIT 0x00b704ceL
186 : : #define CRC24_POLY 0x01864cfbL
187 : : static long
188 : 99 : crc24(const uint8 *data, unsigned len)
189 : : {
190 : 99 : unsigned crc = CRC24_INIT;
191 : : int i;
192 : :
193 [ + + ]: 62552 : while (len--)
194 : : {
195 : 62453 : crc ^= (*data++) << 16;
196 [ + + ]: 562077 : for (i = 0; i < 8; i++)
197 : : {
198 : 499624 : crc <<= 1;
199 [ + + ]: 499624 : if (crc & 0x1000000)
200 : 250230 : crc ^= CRC24_POLY;
201 : : }
202 : : }
203 : 99 : return crc & 0xffffffL;
204 : : }
205 : :
206 : : void
3483 heikki.linnakangas@i 207 : 10 : pgp_armor_encode(const uint8 *src, unsigned len, StringInfo dst,
208 : : int num_headers, char **keys, char **values)
209 : : {
210 : : int n;
211 : : int res;
212 : : unsigned b64len;
6853 bruce@momjian.us 213 : 10 : unsigned crc = crc24(src, len);
214 : :
3489 heikki.linnakangas@i 215 : 10 : appendStringInfoString(dst, armor_header);
216 : :
3483 217 [ + + ]: 17 : for (n = 0; n < num_headers; n++)
218 : 7 : appendStringInfo(dst, "%s: %s\n", keys[n], values[n]);
219 : 10 : appendStringInfoChar(dst, '\n');
220 : :
221 : : /* make sure we have enough room to pg_base64_encode() */
2237 tgl@sss.pgh.pa.us 222 : 10 : b64len = pg_base64_enc_len(len);
3489 heikki.linnakangas@i 223 : 10 : enlargeStringInfo(dst, (int) b64len);
224 : :
2237 tgl@sss.pgh.pa.us 225 : 10 : res = pg_base64_encode(src, len, (uint8 *) dst->data + dst->len);
3489 heikki.linnakangas@i 226 [ - + ]: 10 : if (res > b64len)
3489 heikki.linnakangas@i 227 [ # # ]:UBC 0 : elog(FATAL, "overflow - encode estimate too small");
3489 heikki.linnakangas@i 228 :CBC 10 : dst->len += res;
229 : :
230 [ + + ]: 10 : if (*(dst->data + dst->len - 1) != '\n')
231 : 6 : appendStringInfoChar(dst, '\n');
232 : :
233 : 10 : appendStringInfoChar(dst, '=');
234 : 10 : appendStringInfoChar(dst, _base64[(crc >> 18) & 0x3f]);
235 : 10 : appendStringInfoChar(dst, _base64[(crc >> 12) & 0x3f]);
236 : 10 : appendStringInfoChar(dst, _base64[(crc >> 6) & 0x3f]);
237 : 10 : appendStringInfoChar(dst, _base64[crc & 0x3f]);
238 : :
239 : 10 : appendStringInfoString(dst, armor_footer);
6853 bruce@momjian.us 240 : 10 : }
241 : :
242 : : static const uint8 *
243 : 207 : find_str(const uint8 *data, const uint8 *data_end, const char *str, int strlen)
244 : : {
245 : 207 : const uint8 *p = data;
246 : :
247 [ - + ]: 207 : if (!strlen)
6853 bruce@momjian.us 248 :UBC 0 : return NULL;
6853 bruce@momjian.us 249 [ - + ]:CBC 207 : if (data_end - data < strlen)
6853 bruce@momjian.us 250 :UBC 0 : return NULL;
6756 bruce@momjian.us 251 [ + - ]:CBC 222 : while (p < data_end)
252 : : {
6853 253 : 222 : p = memchr(p, str[0], data_end - p);
254 [ - + ]: 222 : if (p == NULL)
6853 bruce@momjian.us 255 :UBC 0 : return NULL;
6853 bruce@momjian.us 256 [ - + ]:CBC 222 : if (p + strlen > data_end)
6853 bruce@momjian.us 257 :UBC 0 : return NULL;
6853 bruce@momjian.us 258 [ + + ]:CBC 222 : if (memcmp(p, str, strlen) == 0)
259 : 207 : return p;
260 : 15 : p++;
261 : : }
6853 bruce@momjian.us 262 :UBC 0 : return NULL;
263 : : }
264 : :
265 : : static int
6853 bruce@momjian.us 266 :CBC 206 : find_header(const uint8 *data, const uint8 *datend,
267 : : const uint8 **start_p, int is_end)
268 : : {
269 : 206 : const uint8 *p = data;
270 : : static const char *start_sep = "-----BEGIN";
271 : : static const char *end_sep = "-----END";
272 [ + + ]: 206 : const char *sep = is_end ? end_sep : start_sep;
273 : :
274 : : /* find header line */
275 : : while (1)
276 : : {
277 : 207 : p = find_str(p, datend, sep, strlen(sep));
278 [ - + ]: 207 : if (p == NULL)
6853 bruce@momjian.us 279 :UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
280 : : /* it must start at beginning of line */
6853 bruce@momjian.us 281 [ + + + + ]:CBC 207 : if (p == data || *(p - 1) == '\n')
282 : : break;
283 : 1 : p += strlen(sep);
284 : : }
285 : 206 : *start_p = p;
286 : 206 : p += strlen(sep);
287 : :
288 : : /* check if header text ok */
289 [ + - + + ]: 3412 : for (; p < datend && *p != '-'; p++)
290 : : {
291 : : /* various junk can be there, but definitely not line-feed */
292 [ + - ]: 3206 : if (*p >= ' ')
293 : 3206 : continue;
6853 bruce@momjian.us 294 :UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
295 : : }
6853 bruce@momjian.us 296 [ + - - + ]:CBC 206 : if (datend - p < 5 || memcmp(p, sep, 5) != 0)
6853 bruce@momjian.us 297 :UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
6853 bruce@momjian.us 298 :CBC 206 : p += 5;
299 : :
300 : : /* check if at end of line */
301 [ + + ]: 206 : if (p < datend)
302 : : {
303 [ + + - + ]: 205 : if (*p != '\n' && *p != '\r')
6853 bruce@momjian.us 304 :UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
6853 bruce@momjian.us 305 [ + + ]:CBC 205 : if (*p == '\r')
306 : 2 : p++;
307 [ + - + - ]: 205 : if (p < datend && *p == '\n')
308 : 205 : p++;
309 : : }
310 : 206 : return p - *start_p;
311 : : }
312 : :
313 : : int
3489 heikki.linnakangas@i 314 : 89 : pgp_armor_decode(const uint8 *src, int len, StringInfo dst)
315 : : {
6853 bruce@momjian.us 316 : 89 : const uint8 *p = src;
317 : 89 : const uint8 *data_end = src + len;
318 : : long crc;
319 : : const uint8 *base64_start,
320 : : *armor_end;
321 : 89 : const uint8 *base64_end = NULL;
322 : : uint8 buf[4];
323 : : int hlen;
324 : : int blen;
325 : 89 : int res = PXE_PGP_CORRUPT_ARMOR;
326 : :
327 : : /* armor start */
328 : 89 : hlen = find_header(src, data_end, &p, 0);
329 [ - + ]: 89 : if (hlen <= 0)
6853 bruce@momjian.us 330 :UBC 0 : goto out;
6853 bruce@momjian.us 331 :CBC 89 : p += hlen;
332 : :
333 : : /* armor end */
334 : 89 : hlen = find_header(p, data_end, &armor_end, 1);
335 [ - + ]: 89 : if (hlen <= 0)
6853 bruce@momjian.us 336 :UBC 0 : goto out;
337 : :
338 : : /* skip comments - find empty line */
6853 bruce@momjian.us 339 [ + - + + :CBC 168 : while (p < armor_end && *p != '\n' && *p != '\r')
+ - ]
340 : : {
341 : 79 : p = memchr(p, '\n', armor_end - p);
342 [ - + ]: 79 : if (!p)
6853 bruce@momjian.us 343 :UBC 0 : goto out;
344 : :
345 : : /* step to start of next line */
6853 bruce@momjian.us 346 :CBC 79 : p++;
347 : : }
348 : 89 : base64_start = p;
349 : :
350 : : /* find crc pos */
351 [ + - ]: 624 : for (p = armor_end; p >= base64_start; p--)
352 [ + + ]: 624 : if (*p == '=')
353 : : {
354 : 89 : base64_end = p - 1;
355 : 89 : break;
356 : : }
357 [ - + ]: 89 : if (base64_end == NULL)
6853 bruce@momjian.us 358 :UBC 0 : goto out;
359 : :
360 : : /* decode crc */
2237 tgl@sss.pgh.pa.us 361 [ - + ]:CBC 89 : if (pg_base64_decode(p + 1, 4, buf) != 3)
6853 bruce@momjian.us 362 :UBC 0 : goto out;
6756 bruce@momjian.us 363 :CBC 89 : crc = (((long) buf[0]) << 16) + (((long) buf[1]) << 8) + (long) buf[2];
364 : :
365 : : /* decode data */
2237 tgl@sss.pgh.pa.us 366 : 89 : blen = (int) pg_base64_dec_len(len);
3489 heikki.linnakangas@i 367 : 89 : enlargeStringInfo(dst, blen);
2237 tgl@sss.pgh.pa.us 368 : 89 : res = pg_base64_decode(base64_start, base64_end - base64_start, (uint8 *) dst->data);
3489 heikki.linnakangas@i 369 [ - + ]: 89 : if (res > blen)
3489 heikki.linnakangas@i 370 [ # # ]:UBC 0 : elog(FATAL, "overflow - decode estimate too small");
3489 heikki.linnakangas@i 371 [ - + ]:CBC 89 : if (res >= 0)
372 : : {
373 [ + + ]: 89 : if (crc24((uint8 *) dst->data, res) == crc)
374 : 88 : dst->len += res;
375 : : else
376 : 1 : res = PXE_PGP_CORRUPT_ARMOR;
377 : : }
6853 bruce@momjian.us 378 :UBC 0 : out:
6853 bruce@momjian.us 379 :CBC 89 : return res;
380 : : }
381 : :
382 : : /*
383 : : * Extracts all armor headers from an ASCII-armored input.
384 : : *
385 : : * Returns 0 on success, or PXE_* error code on error. On success, the
386 : : * number of headers and their keys and values are returned in *nheaders,
387 : : * *nkeys and *nvalues.
388 : : */
389 : : int
3483 heikki.linnakangas@i 390 : 14 : pgp_extract_armor_headers(const uint8 *src, unsigned len,
391 : : int *nheaders, char ***keys, char ***values)
392 : : {
393 : 14 : const uint8 *data_end = src + len;
394 : : const uint8 *p;
395 : : const uint8 *base64_start;
396 : : const uint8 *armor_start;
397 : : const uint8 *armor_end;
398 : : Size armor_len;
399 : : char *line;
400 : : char *nextline;
401 : : char *eol,
402 : : *colon;
403 : : int hlen;
404 : : char *buf;
405 : : int hdrlines;
406 : : int n;
407 : :
408 : : /* armor start */
409 : 14 : hlen = find_header(src, data_end, &armor_start, 0);
410 [ - + ]: 14 : if (hlen <= 0)
3483 heikki.linnakangas@i 411 :UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
3483 heikki.linnakangas@i 412 :CBC 14 : armor_start += hlen;
413 : :
414 : : /* armor end */
415 : 14 : hlen = find_header(armor_start, data_end, &armor_end, 1);
416 [ - + ]: 14 : if (hlen <= 0)
3483 heikki.linnakangas@i 417 :UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
418 : :
419 : : /* Count the number of armor header lines. */
3483 heikki.linnakangas@i 420 :CBC 14 : hdrlines = 0;
421 : 14 : p = armor_start;
422 [ + + + + : 40 : while (p < armor_end && *p != '\n' && *p != '\r')
+ + ]
423 : : {
424 : 26 : p = memchr(p, '\n', armor_end - p);
425 [ - + ]: 26 : if (!p)
3483 heikki.linnakangas@i 426 :UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
427 : :
428 : : /* step to start of next line */
3483 heikki.linnakangas@i 429 :CBC 26 : p++;
430 : 26 : hdrlines++;
431 : : }
432 : 14 : base64_start = p;
433 : :
434 : : /*
435 : : * Make a modifiable copy of the part of the input that contains the
436 : : * headers. The returned key/value pointers will point inside the buffer.
437 : : */
438 : 14 : armor_len = base64_start - armor_start;
439 : 14 : buf = palloc(armor_len + 1);
440 : 14 : memcpy(buf, armor_start, armor_len);
441 : 14 : buf[armor_len] = '\0';
442 : :
443 : : /* Allocate return arrays */
444 : 14 : *keys = (char **) palloc(hdrlines * sizeof(char *));
445 : 14 : *values = (char **) palloc(hdrlines * sizeof(char *));
446 : :
447 : : /*
448 : : * Split the header lines at newlines and ": " separators, and collect
449 : : * pointers to the keys and values in the return arrays.
450 : : */
451 : 14 : n = 0;
452 : 14 : line = buf;
453 : : for (;;)
454 : : {
455 : : /* find end of line */
456 : 60 : eol = strchr(line, '\n');
457 [ + + ]: 37 : if (!eol)
458 : 12 : break;
459 : 25 : nextline = eol + 1;
460 : : /* if the line ends in CR + LF, strip the CR */
461 [ + - + + ]: 25 : if (eol > line && *(eol - 1) == '\r')
462 : 2 : eol--;
463 : 25 : *eol = '\0';
464 : :
465 : : /* find colon+space separating the key and value */
466 : 25 : colon = strstr(line, ": ");
467 [ + + ]: 25 : if (!colon)
468 : 2 : return PXE_PGP_CORRUPT_ARMOR;
469 : 23 : *colon = '\0';
470 : :
471 : : /* shouldn't happen, we counted the number of lines beforehand */
472 [ - + ]: 23 : if (n >= hdrlines)
3483 heikki.linnakangas@i 473 [ # # ]:UBC 0 : elog(ERROR, "unexpected number of armor header lines");
474 : :
3483 heikki.linnakangas@i 475 :CBC 23 : (*keys)[n] = line;
476 : 23 : (*values)[n] = colon + 2;
477 : 23 : n++;
478 : :
479 : : /* step to start of next line */
480 : 23 : line = nextline;
481 : : }
482 : :
483 [ - + ]: 12 : if (n != hdrlines)
3483 heikki.linnakangas@i 484 [ # # ]:UBC 0 : elog(ERROR, "unexpected number of armor header lines");
485 : :
3483 heikki.linnakangas@i 486 :CBC 12 : *nheaders = n;
487 : 12 : return 0;
488 : : }
|