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
45 20 : pg_base64_encode(const uint8 *src, unsigned len, uint8 *dst)
46 : {
47 : uint8 *p,
48 20 : *lend = dst + 76;
49 : const uint8 *s,
50 20 : *end = src + len;
51 20 : int pos = 2;
52 20 : unsigned long buf = 0;
53 :
54 20 : s = src;
55 20 : p = dst;
56 :
57 262 : while (s < end)
58 : {
59 242 : buf |= *s << (pos << 3);
60 242 : pos--;
61 242 : s++;
62 :
63 : /*
64 : * write it out
65 : */
66 242 : if (pos < 0)
67 : {
68 74 : *p++ = _base64[(buf >> 18) & 0x3f];
69 74 : *p++ = _base64[(buf >> 12) & 0x3f];
70 74 : *p++ = _base64[(buf >> 6) & 0x3f];
71 74 : *p++ = _base64[buf & 0x3f];
72 :
73 74 : pos = 2;
74 74 : buf = 0;
75 : }
76 242 : if (p >= lend)
77 : {
78 2 : *p++ = '\n';
79 2 : lend = p + 76;
80 : }
81 : }
82 20 : if (pos != 2)
83 : {
84 12 : *p++ = _base64[(buf >> 18) & 0x3f];
85 12 : *p++ = _base64[(buf >> 12) & 0x3f];
86 12 : *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
87 12 : *p++ = '=';
88 : }
89 :
90 20 : return p - dst;
91 : }
92 :
93 : /* probably should use lookup table */
94 : static int
95 356 : pg_base64_decode(const uint8 *src, unsigned len, uint8 *dst)
96 : {
97 356 : const uint8 *srcend = src + len,
98 356 : *s = src;
99 356 : uint8 *p = dst;
100 : char c;
101 356 : unsigned b = 0;
102 356 : unsigned long buf = 0;
103 356 : int pos = 0,
104 356 : end = 0;
105 :
106 170260 : while (s < srcend)
107 : {
108 169904 : c = *s++;
109 169904 : if (c >= 'A' && c <= 'Z')
110 71272 : b = c - 'A';
111 98632 : else if (c >= 'a' && c <= 'z')
112 65524 : b = c - 'a' + 26;
113 33108 : else if (c >= '0' && c <= '9')
114 24920 : b = c - '0' + 52;
115 8188 : else if (c == '+')
116 2604 : b = 62;
117 5584 : else if (c == '/')
118 2688 : b = 63;
119 2896 : else if (c == '=')
120 : {
121 : /*
122 : * end sequence
123 : */
124 232 : if (!end)
125 : {
126 134 : if (pos == 2)
127 98 : end = 1;
128 36 : else if (pos == 3)
129 36 : end = 2;
130 : else
131 0 : return PXE_PGP_CORRUPT_ARMOR;
132 : }
133 232 : b = 0;
134 : }
135 2664 : else if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
136 2664 : continue;
137 : else
138 0 : return PXE_PGP_CORRUPT_ARMOR;
139 :
140 : /*
141 : * add it to buffer
142 : */
143 167240 : buf = (buf << 6) + b;
144 167240 : pos++;
145 167240 : if (pos == 4)
146 : {
147 41810 : *p++ = (buf >> 16) & 255;
148 41810 : if (end == 0 || end > 1)
149 41712 : *p++ = (buf >> 8) & 255;
150 41810 : if (end == 0 || end > 2)
151 41676 : *p++ = buf & 255;
152 41810 : buf = 0;
153 41810 : pos = 0;
154 : }
155 : }
156 :
157 356 : if (pos != 0)
158 0 : return PXE_PGP_CORRUPT_ARMOR;
159 356 : return p - dst;
160 : }
161 :
162 : static unsigned
163 20 : pg_base64_enc_len(unsigned srclen)
164 : {
165 : /*
166 : * 3 bytes will be converted to 4, linefeed after 76 chars
167 : */
168 20 : return (srclen + 2) / 3 * 4 + srclen / (76 * 3 / 4);
169 : }
170 :
171 : static unsigned
172 178 : pg_base64_dec_len(unsigned srclen)
173 : {
174 178 : 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 198 : crc24(const uint8 *data, unsigned len)
189 : {
190 198 : unsigned crc = CRC24_INIT;
191 : int i;
192 :
193 125104 : while (len--)
194 : {
195 124906 : crc ^= (*data++) << 16;
196 1124154 : for (i = 0; i < 8; i++)
197 : {
198 999248 : crc <<= 1;
199 999248 : if (crc & 0x1000000)
200 500460 : crc ^= CRC24_POLY;
201 : }
202 : }
203 198 : return crc & 0xffffffL;
204 : }
205 :
206 : void
207 20 : 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;
213 20 : unsigned crc = crc24(src, len);
214 :
215 20 : appendStringInfoString(dst, armor_header);
216 :
217 34 : for (n = 0; n < num_headers; n++)
218 14 : appendStringInfo(dst, "%s: %s\n", keys[n], values[n]);
219 20 : appendStringInfoChar(dst, '\n');
220 :
221 : /* make sure we have enough room to pg_base64_encode() */
222 20 : b64len = pg_base64_enc_len(len);
223 20 : enlargeStringInfo(dst, (int) b64len);
224 :
225 20 : res = pg_base64_encode(src, len, (uint8 *) dst->data + dst->len);
226 20 : if (res > b64len)
227 0 : elog(FATAL, "overflow - encode estimate too small");
228 20 : dst->len += res;
229 :
230 20 : if (*(dst->data + dst->len - 1) != '\n')
231 12 : appendStringInfoChar(dst, '\n');
232 :
233 20 : appendStringInfoChar(dst, '=');
234 20 : appendStringInfoChar(dst, _base64[(crc >> 18) & 0x3f]);
235 20 : appendStringInfoChar(dst, _base64[(crc >> 12) & 0x3f]);
236 20 : appendStringInfoChar(dst, _base64[(crc >> 6) & 0x3f]);
237 20 : appendStringInfoChar(dst, _base64[crc & 0x3f]);
238 :
239 20 : appendStringInfoString(dst, armor_footer);
240 20 : }
241 :
242 : static const uint8 *
243 414 : find_str(const uint8 *data, const uint8 *data_end, const char *str, int strlen)
244 : {
245 414 : const uint8 *p = data;
246 :
247 414 : if (!strlen)
248 0 : return NULL;
249 414 : if (data_end - data < strlen)
250 0 : return NULL;
251 444 : while (p < data_end)
252 : {
253 444 : p = memchr(p, str[0], data_end - p);
254 444 : if (p == NULL)
255 0 : return NULL;
256 444 : if (p + strlen > data_end)
257 0 : return NULL;
258 444 : if (memcmp(p, str, strlen) == 0)
259 414 : return p;
260 30 : p++;
261 : }
262 0 : return NULL;
263 : }
264 :
265 : static int
266 412 : find_header(const uint8 *data, const uint8 *datend,
267 : const uint8 **start_p, int is_end)
268 : {
269 412 : const uint8 *p = data;
270 : static const char *start_sep = "-----BEGIN";
271 : static const char *end_sep = "-----END";
272 412 : const char *sep = is_end ? end_sep : start_sep;
273 :
274 : /* find header line */
275 : while (1)
276 : {
277 414 : p = find_str(p, datend, sep, strlen(sep));
278 414 : if (p == NULL)
279 0 : return PXE_PGP_CORRUPT_ARMOR;
280 : /* it must start at beginning of line */
281 414 : if (p == data || *(p - 1) == '\n')
282 : break;
283 2 : p += strlen(sep);
284 : }
285 412 : *start_p = p;
286 412 : p += strlen(sep);
287 :
288 : /* check if header text ok */
289 6824 : for (; p < datend && *p != '-'; p++)
290 : {
291 : /* various junk can be there, but definitely not line-feed */
292 6412 : if (*p >= ' ')
293 6412 : continue;
294 0 : return PXE_PGP_CORRUPT_ARMOR;
295 : }
296 412 : if (datend - p < 5 || memcmp(p, sep, 5) != 0)
297 0 : return PXE_PGP_CORRUPT_ARMOR;
298 412 : p += 5;
299 :
300 : /* check if at end of line */
301 412 : if (p < datend)
302 : {
303 410 : if (*p != '\n' && *p != '\r')
304 0 : return PXE_PGP_CORRUPT_ARMOR;
305 410 : if (*p == '\r')
306 4 : p++;
307 410 : if (p < datend && *p == '\n')
308 410 : p++;
309 : }
310 412 : return p - *start_p;
311 : }
312 :
313 : int
314 178 : pgp_armor_decode(const uint8 *src, int len, StringInfo dst)
315 : {
316 178 : const uint8 *p = src;
317 178 : const uint8 *data_end = src + len;
318 : long crc;
319 : const uint8 *base64_start,
320 : *armor_end;
321 178 : const uint8 *base64_end = NULL;
322 : uint8 buf[4];
323 : int hlen;
324 : int blen;
325 178 : int res = PXE_PGP_CORRUPT_ARMOR;
326 :
327 : /* armor start */
328 178 : hlen = find_header(src, data_end, &p, 0);
329 178 : if (hlen <= 0)
330 0 : goto out;
331 178 : p += hlen;
332 :
333 : /* armor end */
334 178 : hlen = find_header(p, data_end, &armor_end, 1);
335 178 : if (hlen <= 0)
336 0 : goto out;
337 :
338 : /* skip comments - find empty line */
339 336 : while (p < armor_end && *p != '\n' && *p != '\r')
340 : {
341 158 : p = memchr(p, '\n', armor_end - p);
342 158 : if (!p)
343 0 : goto out;
344 :
345 : /* step to start of next line */
346 158 : p++;
347 : }
348 178 : base64_start = p;
349 :
350 : /* find crc pos */
351 1248 : for (p = armor_end; p >= base64_start; p--)
352 1248 : if (*p == '=')
353 : {
354 178 : base64_end = p - 1;
355 178 : break;
356 : }
357 178 : if (base64_end == NULL)
358 0 : goto out;
359 :
360 : /* decode crc */
361 178 : if (pg_base64_decode(p + 1, 4, buf) != 3)
362 0 : goto out;
363 178 : crc = (((long) buf[0]) << 16) + (((long) buf[1]) << 8) + (long) buf[2];
364 :
365 : /* decode data */
366 178 : blen = (int) pg_base64_dec_len(len);
367 178 : enlargeStringInfo(dst, blen);
368 178 : res = pg_base64_decode(base64_start, base64_end - base64_start, (uint8 *) dst->data);
369 178 : if (res > blen)
370 0 : elog(FATAL, "overflow - decode estimate too small");
371 178 : if (res >= 0)
372 : {
373 178 : if (crc24((uint8 *) dst->data, res) == crc)
374 176 : dst->len += res;
375 : else
376 2 : res = PXE_PGP_CORRUPT_ARMOR;
377 : }
378 0 : out:
379 178 : 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
390 28 : pgp_extract_armor_headers(const uint8 *src, unsigned len,
391 : int *nheaders, char ***keys, char ***values)
392 : {
393 28 : 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 28 : hlen = find_header(src, data_end, &armor_start, 0);
410 28 : if (hlen <= 0)
411 0 : return PXE_PGP_CORRUPT_ARMOR;
412 28 : armor_start += hlen;
413 :
414 : /* armor end */
415 28 : hlen = find_header(armor_start, data_end, &armor_end, 1);
416 28 : if (hlen <= 0)
417 0 : return PXE_PGP_CORRUPT_ARMOR;
418 :
419 : /* Count the number of armor header lines. */
420 28 : hdrlines = 0;
421 28 : p = armor_start;
422 80 : while (p < armor_end && *p != '\n' && *p != '\r')
423 : {
424 52 : p = memchr(p, '\n', armor_end - p);
425 52 : if (!p)
426 0 : return PXE_PGP_CORRUPT_ARMOR;
427 :
428 : /* step to start of next line */
429 52 : p++;
430 52 : hdrlines++;
431 : }
432 28 : 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 28 : armor_len = base64_start - armor_start;
439 28 : buf = palloc(armor_len + 1);
440 28 : memcpy(buf, armor_start, armor_len);
441 28 : buf[armor_len] = '\0';
442 :
443 : /* Allocate return arrays */
444 28 : *keys = (char **) palloc(hdrlines * sizeof(char *));
445 28 : *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 28 : n = 0;
452 28 : line = buf;
453 : for (;;)
454 : {
455 : /* find end of line */
456 120 : eol = strchr(line, '\n');
457 74 : if (!eol)
458 24 : break;
459 50 : nextline = eol + 1;
460 : /* if the line ends in CR + LF, strip the CR */
461 50 : if (eol > line && *(eol - 1) == '\r')
462 4 : eol--;
463 50 : *eol = '\0';
464 :
465 : /* find colon+space separating the key and value */
466 50 : colon = strstr(line, ": ");
467 50 : if (!colon)
468 4 : return PXE_PGP_CORRUPT_ARMOR;
469 46 : *colon = '\0';
470 :
471 : /* shouldn't happen, we counted the number of lines beforehand */
472 46 : if (n >= hdrlines)
473 0 : elog(ERROR, "unexpected number of armor header lines");
474 :
475 46 : (*keys)[n] = line;
476 46 : (*values)[n] = colon + 2;
477 46 : n++;
478 :
479 : /* step to start of next line */
480 46 : line = nextline;
481 : }
482 :
483 24 : if (n != hdrlines)
484 0 : elog(ERROR, "unexpected number of armor header lines");
485 :
486 24 : *nheaders = n;
487 24 : return 0;
488 : }
|