summaryrefslogtreecommitdiffstats
path: root/contrib/pgcrypto/pgp-armor.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /contrib/pgcrypto/pgp-armor.c
parentInitial commit. (diff)
downloadpostgresql-14-46651ce6fe013220ed397add242004d764fc0153.tar.xz
postgresql-14-46651ce6fe013220ed397add242004d764fc0153.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/pgcrypto/pgp-armor.c')
-rw-r--r--contrib/pgcrypto/pgp-armor.c488
1 files changed, 488 insertions, 0 deletions
diff --git a/contrib/pgcrypto/pgp-armor.c b/contrib/pgcrypto/pgp-armor.c
new file mode 100644
index 0000000..679779a
--- /dev/null
+++ b/contrib/pgcrypto/pgp-armor.c
@@ -0,0 +1,488 @@
+/*
+ * pgp-armor.c
+ * PGP ascii-armor.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-armor.c
+ */
+
+#include "postgres.h"
+
+#include "pgp.h"
+#include "px.h"
+
+/*
+ * BASE64 - duplicated :(
+ */
+
+static const unsigned char _base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int
+pg_base64_encode(const uint8 *src, unsigned len, uint8 *dst)
+{
+ uint8 *p,
+ *lend = dst + 76;
+ const uint8 *s,
+ *end = src + len;
+ int pos = 2;
+ unsigned long buf = 0;
+
+ s = src;
+ p = dst;
+
+ while (s < end)
+ {
+ buf |= *s << (pos << 3);
+ pos--;
+ s++;
+
+ /*
+ * write it out
+ */
+ if (pos < 0)
+ {
+ *p++ = _base64[(buf >> 18) & 0x3f];
+ *p++ = _base64[(buf >> 12) & 0x3f];
+ *p++ = _base64[(buf >> 6) & 0x3f];
+ *p++ = _base64[buf & 0x3f];
+
+ pos = 2;
+ buf = 0;
+ }
+ if (p >= lend)
+ {
+ *p++ = '\n';
+ lend = p + 76;
+ }
+ }
+ if (pos != 2)
+ {
+ *p++ = _base64[(buf >> 18) & 0x3f];
+ *p++ = _base64[(buf >> 12) & 0x3f];
+ *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
+ *p++ = '=';
+ }
+
+ return p - dst;
+}
+
+/* probably should use lookup table */
+static int
+pg_base64_decode(const uint8 *src, unsigned len, uint8 *dst)
+{
+ const uint8 *srcend = src + len,
+ *s = src;
+ uint8 *p = dst;
+ char c;
+ unsigned b = 0;
+ unsigned long buf = 0;
+ int pos = 0,
+ end = 0;
+
+ while (s < srcend)
+ {
+ c = *s++;
+ if (c >= 'A' && c <= 'Z')
+ b = c - 'A';
+ else if (c >= 'a' && c <= 'z')
+ b = c - 'a' + 26;
+ else if (c >= '0' && c <= '9')
+ b = c - '0' + 52;
+ else if (c == '+')
+ b = 62;
+ else if (c == '/')
+ b = 63;
+ else if (c == '=')
+ {
+ /*
+ * end sequence
+ */
+ if (!end)
+ {
+ if (pos == 2)
+ end = 1;
+ else if (pos == 3)
+ end = 2;
+ else
+ return PXE_PGP_CORRUPT_ARMOR;
+ }
+ b = 0;
+ }
+ else if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ continue;
+ else
+ return PXE_PGP_CORRUPT_ARMOR;
+
+ /*
+ * add it to buffer
+ */
+ buf = (buf << 6) + b;
+ pos++;
+ if (pos == 4)
+ {
+ *p++ = (buf >> 16) & 255;
+ if (end == 0 || end > 1)
+ *p++ = (buf >> 8) & 255;
+ if (end == 0 || end > 2)
+ *p++ = buf & 255;
+ buf = 0;
+ pos = 0;
+ }
+ }
+
+ if (pos != 0)
+ return PXE_PGP_CORRUPT_ARMOR;
+ return p - dst;
+}
+
+static unsigned
+pg_base64_enc_len(unsigned srclen)
+{
+ /*
+ * 3 bytes will be converted to 4, linefeed after 76 chars
+ */
+ return (srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4);
+}
+
+static unsigned
+pg_base64_dec_len(unsigned srclen)
+{
+ return (srclen * 3) >> 2;
+}
+
+/*
+ * PGP armor
+ */
+
+static const char *armor_header = "-----BEGIN PGP MESSAGE-----\n";
+static const char *armor_footer = "\n-----END PGP MESSAGE-----\n";
+
+/* CRC24 implementation from rfc2440 */
+#define CRC24_INIT 0x00b704ceL
+#define CRC24_POLY 0x01864cfbL
+static long
+crc24(const uint8 *data, unsigned len)
+{
+ unsigned crc = CRC24_INIT;
+ int i;
+
+ while (len--)
+ {
+ crc ^= (*data++) << 16;
+ for (i = 0; i < 8; i++)
+ {
+ crc <<= 1;
+ if (crc & 0x1000000)
+ crc ^= CRC24_POLY;
+ }
+ }
+ return crc & 0xffffffL;
+}
+
+void
+pgp_armor_encode(const uint8 *src, unsigned len, StringInfo dst,
+ int num_headers, char **keys, char **values)
+{
+ int n;
+ int res;
+ unsigned b64len;
+ unsigned crc = crc24(src, len);
+
+ appendStringInfoString(dst, armor_header);
+
+ for (n = 0; n < num_headers; n++)
+ appendStringInfo(dst, "%s: %s\n", keys[n], values[n]);
+ appendStringInfoChar(dst, '\n');
+
+ /* make sure we have enough room to pg_base64_encode() */
+ b64len = pg_base64_enc_len(len);
+ enlargeStringInfo(dst, (int) b64len);
+
+ res = pg_base64_encode(src, len, (uint8 *) dst->data + dst->len);
+ if (res > b64len)
+ elog(FATAL, "overflow - encode estimate too small");
+ dst->len += res;
+
+ if (*(dst->data + dst->len - 1) != '\n')
+ appendStringInfoChar(dst, '\n');
+
+ appendStringInfoChar(dst, '=');
+ appendStringInfoChar(dst, _base64[(crc >> 18) & 0x3f]);
+ appendStringInfoChar(dst, _base64[(crc >> 12) & 0x3f]);
+ appendStringInfoChar(dst, _base64[(crc >> 6) & 0x3f]);
+ appendStringInfoChar(dst, _base64[crc & 0x3f]);
+
+ appendStringInfoString(dst, armor_footer);
+}
+
+static const uint8 *
+find_str(const uint8 *data, const uint8 *data_end, const char *str, int strlen)
+{
+ const uint8 *p = data;
+
+ if (!strlen)
+ return NULL;
+ if (data_end - data < strlen)
+ return NULL;
+ while (p < data_end)
+ {
+ p = memchr(p, str[0], data_end - p);
+ if (p == NULL)
+ return NULL;
+ if (p + strlen > data_end)
+ return NULL;
+ if (memcmp(p, str, strlen) == 0)
+ return p;
+ p++;
+ }
+ return NULL;
+}
+
+static int
+find_header(const uint8 *data, const uint8 *datend,
+ const uint8 **start_p, int is_end)
+{
+ const uint8 *p = data;
+ static const char *start_sep = "-----BEGIN";
+ static const char *end_sep = "-----END";
+ const char *sep = is_end ? end_sep : start_sep;
+
+ /* find header line */
+ while (1)
+ {
+ p = find_str(p, datend, sep, strlen(sep));
+ if (p == NULL)
+ return PXE_PGP_CORRUPT_ARMOR;
+ /* it must start at beginning of line */
+ if (p == data || *(p - 1) == '\n')
+ break;
+ p += strlen(sep);
+ }
+ *start_p = p;
+ p += strlen(sep);
+
+ /* check if header text ok */
+ for (; p < datend && *p != '-'; p++)
+ {
+ /* various junk can be there, but definitely not line-feed */
+ if (*p >= ' ')
+ continue;
+ return PXE_PGP_CORRUPT_ARMOR;
+ }
+ if (datend - p < 5 || memcmp(p, sep, 5) != 0)
+ return PXE_PGP_CORRUPT_ARMOR;
+ p += 5;
+
+ /* check if at end of line */
+ if (p < datend)
+ {
+ if (*p != '\n' && *p != '\r')
+ return PXE_PGP_CORRUPT_ARMOR;
+ if (*p == '\r')
+ p++;
+ if (p < datend && *p == '\n')
+ p++;
+ }
+ return p - *start_p;
+}
+
+int
+pgp_armor_decode(const uint8 *src, int len, StringInfo dst)
+{
+ const uint8 *p = src;
+ const uint8 *data_end = src + len;
+ long crc;
+ const uint8 *base64_start,
+ *armor_end;
+ const uint8 *base64_end = NULL;
+ uint8 buf[4];
+ int hlen;
+ int blen;
+ int res = PXE_PGP_CORRUPT_ARMOR;
+
+ /* armor start */
+ hlen = find_header(src, data_end, &p, 0);
+ if (hlen <= 0)
+ goto out;
+ p += hlen;
+
+ /* armor end */
+ hlen = find_header(p, data_end, &armor_end, 1);
+ if (hlen <= 0)
+ goto out;
+
+ /* skip comments - find empty line */
+ while (p < armor_end && *p != '\n' && *p != '\r')
+ {
+ p = memchr(p, '\n', armor_end - p);
+ if (!p)
+ goto out;
+
+ /* step to start of next line */
+ p++;
+ }
+ base64_start = p;
+
+ /* find crc pos */
+ for (p = armor_end; p >= base64_start; p--)
+ if (*p == '=')
+ {
+ base64_end = p - 1;
+ break;
+ }
+ if (base64_end == NULL)
+ goto out;
+
+ /* decode crc */
+ if (pg_base64_decode(p + 1, 4, buf) != 3)
+ goto out;
+ crc = (((long) buf[0]) << 16) + (((long) buf[1]) << 8) + (long) buf[2];
+
+ /* decode data */
+ blen = (int) pg_base64_dec_len(len);
+ enlargeStringInfo(dst, blen);
+ res = pg_base64_decode(base64_start, base64_end - base64_start, (uint8 *) dst->data);
+ if (res > blen)
+ elog(FATAL, "overflow - decode estimate too small");
+ if (res >= 0)
+ {
+ if (crc24((uint8 *) dst->data, res) == crc)
+ dst->len += res;
+ else
+ res = PXE_PGP_CORRUPT_ARMOR;
+ }
+out:
+ return res;
+}
+
+/*
+ * Extracts all armor headers from an ASCII-armored input.
+ *
+ * Returns 0 on success, or PXE_* error code on error. On success, the
+ * number of headers and their keys and values are returned in *nheaders,
+ * *nkeys and *nvalues.
+ */
+int
+pgp_extract_armor_headers(const uint8 *src, unsigned len,
+ int *nheaders, char ***keys, char ***values)
+{
+ const uint8 *data_end = src + len;
+ const uint8 *p;
+ const uint8 *base64_start;
+ const uint8 *armor_start;
+ const uint8 *armor_end;
+ Size armor_len;
+ char *line;
+ char *nextline;
+ char *eol,
+ *colon;
+ int hlen;
+ char *buf;
+ int hdrlines;
+ int n;
+
+ /* armor start */
+ hlen = find_header(src, data_end, &armor_start, 0);
+ if (hlen <= 0)
+ return PXE_PGP_CORRUPT_ARMOR;
+ armor_start += hlen;
+
+ /* armor end */
+ hlen = find_header(armor_start, data_end, &armor_end, 1);
+ if (hlen <= 0)
+ return PXE_PGP_CORRUPT_ARMOR;
+
+ /* Count the number of armor header lines. */
+ hdrlines = 0;
+ p = armor_start;
+ while (p < armor_end && *p != '\n' && *p != '\r')
+ {
+ p = memchr(p, '\n', armor_end - p);
+ if (!p)
+ return PXE_PGP_CORRUPT_ARMOR;
+
+ /* step to start of next line */
+ p++;
+ hdrlines++;
+ }
+ base64_start = p;
+
+ /*
+ * Make a modifiable copy of the part of the input that contains the
+ * headers. The returned key/value pointers will point inside the buffer.
+ */
+ armor_len = base64_start - armor_start;
+ buf = palloc(armor_len + 1);
+ memcpy(buf, armor_start, armor_len);
+ buf[armor_len] = '\0';
+
+ /* Allocate return arrays */
+ *keys = (char **) palloc(hdrlines * sizeof(char *));
+ *values = (char **) palloc(hdrlines * sizeof(char *));
+
+ /*
+ * Split the header lines at newlines and ": " separators, and collect
+ * pointers to the keys and values in the return arrays.
+ */
+ n = 0;
+ line = buf;
+ for (;;)
+ {
+ /* find end of line */
+ eol = strchr(line, '\n');
+ if (!eol)
+ break;
+ nextline = eol + 1;
+ /* if the line ends in CR + LF, strip the CR */
+ if (eol > line && *(eol - 1) == '\r')
+ eol--;
+ *eol = '\0';
+
+ /* find colon+space separating the key and value */
+ colon = strstr(line, ": ");
+ if (!colon)
+ return PXE_PGP_CORRUPT_ARMOR;
+ *colon = '\0';
+
+ /* shouldn't happen, we counted the number of lines beforehand */
+ if (n >= hdrlines)
+ elog(ERROR, "unexpected number of armor header lines");
+
+ (*keys)[n] = line;
+ (*values)[n] = colon + 2;
+ n++;
+
+ /* step to start of next line */
+ line = nextline;
+ }
+
+ if (n != hdrlines)
+ elog(ERROR, "unexpected number of armor header lines");
+
+ *nheaders = n;
+ return 0;
+}