LCOV - code coverage report
Current view: top level - contrib/pgcrypto - pgp-armor.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 273 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 10 0
Legend: Lines:     hit not hit

            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 : }
        

Generated by: LCOV version 2.3.2-1