Age Owner 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
1866 tgl 45 CBC 10 : pg_base64_encode(const uint8 *src, unsigned len, uint8 *dst)
46 : {
47 : uint8 *p,
6482 bruce 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
1866 tgl 95 178 : pg_base64_decode(const uint8 *src, unsigned len, uint8 *dst)
96 : {
6482 bruce 97 178 : const uint8 *srcend = src + len,
6385 98 178 : *s = src;
6482 99 178 : uint8 *p = dst;
100 : char c;
101 178 : unsigned b = 0;
102 178 : unsigned long buf = 0;
103 178 : int pos = 0,
6385 104 178 : end = 0;
105 :
6482 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
6482 bruce 131 UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
132 : }
6482 bruce 133 CBC 116 : b = 0;
134 : }
135 1332 : else if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
136 1332 : continue;
137 : else
6482 bruce 138 UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
139 :
140 : /*
141 : * add it to buffer
142 : */
6482 bruce 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)
6482 bruce 158 UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
6482 bruce 159 CBC 178 : return p - dst;
160 : }
161 :
162 : static unsigned
1866 tgl 163 10 : pg_base64_enc_len(unsigned srclen)
164 : {
165 : /*
166 : * 3 bytes will be converted to 4, linefeed after 76 chars
167 : */
6482 bruce 168 10 : return (srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4);
169 : }
170 :
171 : static unsigned
1866 tgl 172 89 : pg_base64_dec_len(unsigned srclen)
173 : {
6482 bruce 174 89 : return (srclen * 3) >> 2;
175 : }
176 :
177 : /*
178 : * PGP armor
179 : */
180 :
181 : static const char *armor_header = "-----BEGIN PGP MESSAGE-----\n";
182 : static const char *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
3112 heikki.linnakangas 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;
6482 bruce 213 10 : unsigned crc = crc24(src, len);
214 :
3118 heikki.linnakangas 215 10 : appendStringInfoString(dst, armor_header);
216 :
3112 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() */
1866 tgl 222 10 : b64len = pg_base64_enc_len(len);
3118 heikki.linnakangas 223 10 : enlargeStringInfo(dst, (int) b64len);
224 :
1866 tgl 225 10 : res = pg_base64_encode(src, len, (uint8 *) dst->data + dst->len);
3118 heikki.linnakangas 226 10 : if (res > b64len)
3118 heikki.linnakangas 227 UBC 0 : elog(FATAL, "overflow - encode estimate too small");
3118 heikki.linnakangas 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);
6482 bruce 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)
6482 bruce 248 UBC 0 : return NULL;
6482 bruce 249 CBC 207 : if (data_end - data < strlen)
6482 bruce 250 UBC 0 : return NULL;
6385 bruce 251 CBC 222 : while (p < data_end)
252 : {
6482 253 222 : p = memchr(p, str[0], data_end - p);
254 222 : if (p == NULL)
6482 bruce 255 UBC 0 : return NULL;
6482 bruce 256 CBC 222 : if (p + strlen > data_end)
6482 bruce 257 UBC 0 : return NULL;
6482 bruce 258 CBC 222 : if (memcmp(p, str, strlen) == 0)
259 207 : return p;
260 15 : p++;
261 : }
6482 bruce 262 UBC 0 : return NULL;
263 : }
264 :
265 : static int
6482 bruce 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)
6482 bruce 279 UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
280 : /* it must start at beginning of line */
6482 bruce 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;
6482 bruce 294 UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
295 : }
6482 bruce 296 CBC 206 : if (datend - p < 5 || memcmp(p, sep, 5) != 0)
6482 bruce 297 UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
6482 bruce 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')
6482 bruce 304 UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
6482 bruce 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
3118 heikki.linnakangas 314 89 : pgp_armor_decode(const uint8 *src, int len, StringInfo dst)
315 : {
6482 bruce 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)
6482 bruce 330 UBC 0 : goto out;
6482 bruce 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)
6482 bruce 336 UBC 0 : goto out;
337 :
338 : /* skip comments - find empty line */
6482 bruce 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)
6482 bruce 343 UBC 0 : goto out;
344 :
345 : /* step to start of next line */
6482 bruce 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)
6482 bruce 358 UBC 0 : goto out;
359 :
360 : /* decode crc */
1866 tgl 361 CBC 89 : if (pg_base64_decode(p + 1, 4, buf) != 3)
6482 bruce 362 UBC 0 : goto out;
6385 bruce 363 CBC 89 : crc = (((long) buf[0]) << 16) + (((long) buf[1]) << 8) + (long) buf[2];
364 :
365 : /* decode data */
1866 tgl 366 89 : blen = (int) pg_base64_dec_len(len);
3118 heikki.linnakangas 367 89 : enlargeStringInfo(dst, blen);
1866 tgl 368 89 : res = pg_base64_decode(base64_start, base64_end - base64_start, (uint8 *) dst->data);
3118 heikki.linnakangas 369 89 : if (res > blen)
3118 heikki.linnakangas 370 UBC 0 : elog(FATAL, "overflow - decode estimate too small");
3118 heikki.linnakangas 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 : }
6482 bruce 378 UBC 0 : out:
6482 bruce 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
3112 heikki.linnakangas 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)
3112 heikki.linnakangas 411 UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
3112 heikki.linnakangas 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)
3112 heikki.linnakangas 417 UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
418 :
419 : /* Count the number of armor header lines. */
3112 heikki.linnakangas 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)
3112 heikki.linnakangas 426 UBC 0 : return PXE_PGP_CORRUPT_ARMOR;
427 :
428 : /* step to start of next line */
3112 heikki.linnakangas 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)
3112 heikki.linnakangas 473 UBC 0 : elog(ERROR, "unexpected number of armor header lines");
474 :
3112 heikki.linnakangas 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)
3112 heikki.linnakangas 484 UBC 0 : elog(ERROR, "unexpected number of armor header lines");
485 :
3112 heikki.linnakangas 486 CBC 12 : *nheaders = n;
487 12 : return 0;
488 : }
|