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 
41 static const unsigned char _base64[] =
42 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
43 
44 static int
45 pg_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 */
94 static int
95 pg_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
131  return PXE_PGP_CORRUPT_ARMOR;
132  }
133  b = 0;
134  }
135  else if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
136  continue;
137  else
138  return PXE_PGP_CORRUPT_ARMOR;
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)
158  return PXE_PGP_CORRUPT_ARMOR;
159  return p - dst;
160 }
161 
162 static unsigned
163 pg_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 
171 static unsigned
172 pg_base64_dec_len(unsigned srclen)
173 {
174  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 crc24(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 
206 void
207 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  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 
242 static const uint8 *
243 find_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 
265 static int
266 find_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)
279  return PXE_PGP_CORRUPT_ARMOR;
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;
294  return PXE_PGP_CORRUPT_ARMOR;
295  }
296  if (datend - p < 5 || memcmp(p, sep, 5) != 0)
297  return PXE_PGP_CORRUPT_ARMOR;
298  p += 5;
299 
300  /* check if at end of line */
301  if (p < datend)
302  {
303  if (*p != '\n' && *p != '\r')
304  return PXE_PGP_CORRUPT_ARMOR;
305  if (*p == '\r')
306  p++;
307  if (p < datend && *p == '\n')
308  p++;
309  }
310  return p - *start_p;
311 }
312 
313 int
314 pgp_armor_decode(const uint8 *src, int len, StringInfo dst)
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;
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  }
378 out:
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  */
389 int
390 pgp_extract_armor_headers(const uint8 *src, unsigned len,
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)
411  return PXE_PGP_CORRUPT_ARMOR;
412  armor_start += hlen;
413 
414  /* armor end */
415  hlen = find_header(armor_start, data_end, &armor_end, 1);
416  if (hlen <= 0)
417  return PXE_PGP_CORRUPT_ARMOR;
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)
426  return PXE_PGP_CORRUPT_ARMOR;
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)
468  return PXE_PGP_CORRUPT_ARMOR;
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
unsigned char uint8
Definition: c.h:490
size_t Size
Definition: c.h:584
#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:69
int i
Definition: isn.c:72
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
static const uint8 * find_str(const uint8 *data, const uint8 *data_end, const char *str, int strlen)
Definition: pgp-armor.c:243
#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 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:94
void enlargeStringInfo(StringInfo str, int needed)
Definition: stringinfo.c:286
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:179
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:191