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