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