5 * Copyright (c) 2005 Marko Kreen
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
29 * contrib/pgcrypto/pgp-armor.c
38 * BASE64 - duplicated :(
41 static const unsigned char _base64
[] =
42 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
45 pg_base64_encode(const uint8
*src
, unsigned len
, uint8
*dst
)
52 unsigned long buf
= 0;
59 buf
|= *s
<< (pos
<< 3);
68 *p
++ = _base64
[(buf
>> 18) & 0x3f];
69 *p
++ = _base64
[(buf
>> 12) & 0x3f];
70 *p
++ = _base64
[(buf
>> 6) & 0x3f];
71 *p
++ = _base64
[buf
& 0x3f];
84 *p
++ = _base64
[(buf
>> 18) & 0x3f];
85 *p
++ = _base64
[(buf
>> 12) & 0x3f];
86 *p
++ = (pos
== 0) ? _base64
[(buf
>> 6) & 0x3f] : '=';
93 /* probably should use lookup table */
95 pg_base64_decode(const uint8
*src
, unsigned len
, uint8
*dst
)
97 const uint8
*srcend
= src
+ len
,
102 unsigned long buf
= 0;
109 if (c
>= 'A' && c
<= 'Z')
111 else if (c
>= 'a' && c
<= 'z')
113 else if (c
>= '0' && c
<= '9')
131 return PXE_PGP_CORRUPT_ARMOR
;
135 else if (c
== ' ' || c
== '\t' || c
== '\n' || c
== '\r')
138 return PXE_PGP_CORRUPT_ARMOR
;
143 buf
= (buf
<< 6) + b
;
147 *p
++ = (buf
>> 16) & 255;
148 if (end
== 0 || end
> 1)
149 *p
++ = (buf
>> 8) & 255;
150 if (end
== 0 || end
> 2)
158 return PXE_PGP_CORRUPT_ARMOR
;
163 pg_base64_enc_len(unsigned srclen
)
166 * 3 bytes will be converted to 4, linefeed after 76 chars
168 return (srclen
+ 2) / 3 * 4 + srclen
/ (76 * 3 / 4);
172 pg_base64_dec_len(unsigned srclen
)
174 return (srclen
* 3) >> 2;
181 static const char *const armor_header
= "-----BEGIN PGP MESSAGE-----\n";
182 static const char *const armor_footer
= "\n-----END PGP MESSAGE-----\n";
184 /* CRC24 implementation from rfc2440 */
185 #define CRC24_INIT 0x00b704ceL
186 #define CRC24_POLY 0x01864cfbL
188 crc24(const uint8
*data
, unsigned len
)
190 unsigned crc
= CRC24_INIT
;
195 crc
^= (*data
++) << 16;
196 for (i
= 0; i
< 8; i
++)
203 return crc
& 0xffffffL
;
207 pgp_armor_encode(const uint8
*src
, unsigned len
, StringInfo dst
,
208 int num_headers
, char **keys
, char **values
)
213 unsigned crc
= crc24(src
, len
);
215 appendStringInfoString(dst
, armor_header
);
217 for (n
= 0; n
< num_headers
; n
++)
218 appendStringInfo(dst
, "%s: %s\n", keys
[n
], values
[n
]);
219 appendStringInfoChar(dst
, '\n');
221 /* make sure we have enough room to pg_base64_encode() */
222 b64len
= pg_base64_enc_len(len
);
223 enlargeStringInfo(dst
, (int) b64len
);
225 res
= pg_base64_encode(src
, len
, (uint8
*) dst
->data
+ dst
->len
);
227 elog(FATAL
, "overflow - encode estimate too small");
230 if (*(dst
->data
+ dst
->len
- 1) != '\n')
231 appendStringInfoChar(dst
, '\n');
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]);
239 appendStringInfoString(dst
, armor_footer
);
243 find_str(const uint8
*data
, const uint8
*data_end
, const char *str
, int strlen
)
245 const uint8
*p
= data
;
249 if (data_end
- data
< strlen
)
253 p
= memchr(p
, str
[0], data_end
- p
);
256 if (p
+ strlen
> data_end
)
258 if (memcmp(p
, str
, strlen
) == 0)
266 find_header(const uint8
*data
, const uint8
*datend
,
267 const uint8
**start_p
, int is_end
)
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
;
274 /* find header line */
277 p
= find_str(p
, datend
, sep
, strlen(sep
));
279 return PXE_PGP_CORRUPT_ARMOR
;
280 /* it must start at beginning of line */
281 if (p
== data
|| *(p
- 1) == '\n')
288 /* check if header text ok */
289 for (; p
< datend
&& *p
!= '-'; p
++)
291 /* various junk can be there, but definitely not line-feed */
294 return PXE_PGP_CORRUPT_ARMOR
;
296 if (datend
- p
< 5 || memcmp(p
, sep
, 5) != 0)
297 return PXE_PGP_CORRUPT_ARMOR
;
300 /* check if at end of line */
303 if (*p
!= '\n' && *p
!= '\r')
304 return PXE_PGP_CORRUPT_ARMOR
;
307 if (p
< datend
&& *p
== '\n')
314 pgp_armor_decode(const uint8
*src
, int len
, StringInfo dst
)
316 const uint8
*p
= src
;
317 const uint8
*data_end
= src
+ len
;
319 const uint8
*base64_start
,
321 const uint8
*base64_end
= NULL
;
325 int res
= PXE_PGP_CORRUPT_ARMOR
;
328 hlen
= find_header(src
, data_end
, &p
, 0);
334 hlen
= find_header(p
, data_end
, &armor_end
, 1);
338 /* skip comments - find empty line */
339 while (p
< armor_end
&& *p
!= '\n' && *p
!= '\r')
341 p
= memchr(p
, '\n', armor_end
- p
);
345 /* step to start of next line */
351 for (p
= armor_end
; p
>= base64_start
; p
--)
357 if (base64_end
== NULL
)
361 if (pg_base64_decode(p
+ 1, 4, buf
) != 3)
363 crc
= (((long) buf
[0]) << 16) + (((long) buf
[1]) << 8) + (long) buf
[2];
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
);
370 elog(FATAL
, "overflow - decode estimate too small");
373 if (crc24((uint8
*) dst
->data
, res
) == crc
)
376 res
= PXE_PGP_CORRUPT_ARMOR
;
383 * Extracts all armor headers from an ASCII-armored input.
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.
390 pgp_extract_armor_headers(const uint8
*src
, unsigned len
,
391 int *nheaders
, char ***keys
, char ***values
)
393 const uint8
*data_end
= src
+ len
;
395 const uint8
*base64_start
;
396 const uint8
*armor_start
;
397 const uint8
*armor_end
;
409 hlen
= find_header(src
, data_end
, &armor_start
, 0);
411 return PXE_PGP_CORRUPT_ARMOR
;
415 hlen
= find_header(armor_start
, data_end
, &armor_end
, 1);
417 return PXE_PGP_CORRUPT_ARMOR
;
419 /* Count the number of armor header lines. */
422 while (p
< armor_end
&& *p
!= '\n' && *p
!= '\r')
424 p
= memchr(p
, '\n', armor_end
- p
);
426 return PXE_PGP_CORRUPT_ARMOR
;
428 /* step to start of next line */
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.
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';
443 /* Allocate return arrays */
444 *keys
= (char **) palloc(hdrlines
* sizeof(char *));
445 *values
= (char **) palloc(hdrlines
* sizeof(char *));
448 * Split the header lines at newlines and ": " separators, and collect
449 * pointers to the keys and values in the return arrays.
455 /* find end of line */
456 eol
= strchr(line
, '\n');
460 /* if the line ends in CR + LF, strip the CR */
461 if (eol
> line
&& *(eol
- 1) == '\r')
465 /* find colon+space separating the key and value */
466 colon
= strstr(line
, ": ");
468 return PXE_PGP_CORRUPT_ARMOR
;
471 /* shouldn't happen, we counted the number of lines beforehand */
473 elog(ERROR
, "unexpected number of armor header lines");
476 (*values
)[n
] = colon
+ 2;
479 /* step to start of next line */
484 elog(ERROR
, "unexpected number of armor header lines");