Header And Logo

PostgreSQL
| The world's most advanced open source database.

pgp-armor.c

Go to the documentation of this file.
00001 /*
00002  * pgp-armor.c
00003  *      PGP ascii-armor.
00004  *
00005  * Copyright (c) 2005 Marko Kreen
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions
00010  * are met:
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  *
00029  * contrib/pgcrypto/pgp-armor.c
00030  */
00031 
00032 #include "postgres.h"
00033 
00034 #include "px.h"
00035 #include "mbuf.h"
00036 #include "pgp.h"
00037 
00038 /*
00039  * BASE64 - duplicated :(
00040  */
00041 
00042 static const unsigned char _base64[] =
00043 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00044 
00045 static int
00046 b64_encode(const uint8 *src, unsigned len, uint8 *dst)
00047 {
00048     uint8      *p,
00049                *lend = dst + 76;
00050     const uint8 *s,
00051                *end = src + len;
00052     int         pos = 2;
00053     unsigned long buf = 0;
00054 
00055     s = src;
00056     p = dst;
00057 
00058     while (s < end)
00059     {
00060         buf |= *s << (pos << 3);
00061         pos--;
00062         s++;
00063 
00064         /*
00065          * write it out
00066          */
00067         if (pos < 0)
00068         {
00069             *p++ = _base64[(buf >> 18) & 0x3f];
00070             *p++ = _base64[(buf >> 12) & 0x3f];
00071             *p++ = _base64[(buf >> 6) & 0x3f];
00072             *p++ = _base64[buf & 0x3f];
00073 
00074             pos = 2;
00075             buf = 0;
00076         }
00077         if (p >= lend)
00078         {
00079             *p++ = '\n';
00080             lend = p + 76;
00081         }
00082     }
00083     if (pos != 2)
00084     {
00085         *p++ = _base64[(buf >> 18) & 0x3f];
00086         *p++ = _base64[(buf >> 12) & 0x3f];
00087         *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
00088         *p++ = '=';
00089     }
00090 
00091     return p - dst;
00092 }
00093 
00094 /* probably should use lookup table */
00095 static int
00096 b64_decode(const uint8 *src, unsigned len, uint8 *dst)
00097 {
00098     const uint8 *srcend = src + len,
00099                *s = src;
00100     uint8      *p = dst;
00101     char        c;
00102     unsigned    b = 0;
00103     unsigned long buf = 0;
00104     int         pos = 0,
00105                 end = 0;
00106 
00107     while (s < srcend)
00108     {
00109         c = *s++;
00110         if (c >= 'A' && c <= 'Z')
00111             b = c - 'A';
00112         else if (c >= 'a' && c <= 'z')
00113             b = c - 'a' + 26;
00114         else if (c >= '0' && c <= '9')
00115             b = c - '0' + 52;
00116         else if (c == '+')
00117             b = 62;
00118         else if (c == '/')
00119             b = 63;
00120         else if (c == '=')
00121         {
00122             /*
00123              * end sequence
00124              */
00125             if (!end)
00126             {
00127                 if (pos == 2)
00128                     end = 1;
00129                 else if (pos == 3)
00130                     end = 2;
00131                 else
00132                     return PXE_PGP_CORRUPT_ARMOR;
00133             }
00134             b = 0;
00135         }
00136         else if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
00137             continue;
00138         else
00139             return PXE_PGP_CORRUPT_ARMOR;
00140 
00141         /*
00142          * add it to buffer
00143          */
00144         buf = (buf << 6) + b;
00145         pos++;
00146         if (pos == 4)
00147         {
00148             *p++ = (buf >> 16) & 255;
00149             if (end == 0 || end > 1)
00150                 *p++ = (buf >> 8) & 255;
00151             if (end == 0 || end > 2)
00152                 *p++ = buf & 255;
00153             buf = 0;
00154             pos = 0;
00155         }
00156     }
00157 
00158     if (pos != 0)
00159         return PXE_PGP_CORRUPT_ARMOR;
00160     return p - dst;
00161 }
00162 
00163 static unsigned
00164 b64_enc_len(unsigned srclen)
00165 {
00166     /*
00167      * 3 bytes will be converted to 4, linefeed after 76 chars
00168      */
00169     return (srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4);
00170 }
00171 
00172 static unsigned
00173 b64_dec_len(unsigned srclen)
00174 {
00175     return (srclen * 3) >> 2;
00176 }
00177 
00178 /*
00179  * PGP armor
00180  */
00181 
00182 static const char *armor_header = "-----BEGIN PGP MESSAGE-----\n\n";
00183 static const char *armor_footer = "\n-----END PGP MESSAGE-----\n";
00184 
00185 /* CRC24 implementation from rfc2440 */
00186 #define CRC24_INIT 0x00b704ceL
00187 #define CRC24_POLY 0x01864cfbL
00188 static long
00189 crc24(const uint8 *data, unsigned len)
00190 {
00191     unsigned    crc = CRC24_INIT;
00192     int         i;
00193 
00194     while (len--)
00195     {
00196         crc ^= (*data++) << 16;
00197         for (i = 0; i < 8; i++)
00198         {
00199             crc <<= 1;
00200             if (crc & 0x1000000)
00201                 crc ^= CRC24_POLY;
00202         }
00203     }
00204     return crc & 0xffffffL;
00205 }
00206 
00207 int
00208 pgp_armor_encode(const uint8 *src, unsigned len, uint8 *dst)
00209 {
00210     int         n;
00211     uint8      *pos = dst;
00212     unsigned    crc = crc24(src, len);
00213 
00214     n = strlen(armor_header);
00215     memcpy(pos, armor_header, n);
00216     pos += n;
00217 
00218     n = b64_encode(src, len, pos);
00219     pos += n;
00220 
00221     if (*(pos - 1) != '\n')
00222         *pos++ = '\n';
00223 
00224     *pos++ = '=';
00225     pos[3] = _base64[crc & 0x3f];
00226     crc >>= 6;
00227     pos[2] = _base64[crc & 0x3f];
00228     crc >>= 6;
00229     pos[1] = _base64[crc & 0x3f];
00230     crc >>= 6;
00231     pos[0] = _base64[crc & 0x3f];
00232     pos += 4;
00233 
00234     n = strlen(armor_footer);
00235     memcpy(pos, armor_footer, n);
00236     pos += n;
00237 
00238     return pos - dst;
00239 }
00240 
00241 static const uint8 *
00242 find_str(const uint8 *data, const uint8 *data_end, const char *str, int strlen)
00243 {
00244     const uint8 *p = data;
00245 
00246     if (!strlen)
00247         return NULL;
00248     if (data_end - data < strlen)
00249         return NULL;
00250     while (p < data_end)
00251     {
00252         p = memchr(p, str[0], data_end - p);
00253         if (p == NULL)
00254             return NULL;
00255         if (p + strlen > data_end)
00256             return NULL;
00257         if (memcmp(p, str, strlen) == 0)
00258             return p;
00259         p++;
00260     }
00261     return NULL;
00262 }
00263 
00264 static int
00265 find_header(const uint8 *data, const uint8 *datend,
00266             const uint8 **start_p, int is_end)
00267 {
00268     const uint8 *p = data;
00269     static const char *start_sep = "-----BEGIN";
00270     static const char *end_sep = "-----END";
00271     const char *sep = is_end ? end_sep : start_sep;
00272 
00273     /* find header line */
00274     while (1)
00275     {
00276         p = find_str(p, datend, sep, strlen(sep));
00277         if (p == NULL)
00278             return PXE_PGP_CORRUPT_ARMOR;
00279         /* it must start at beginning of line */
00280         if (p == data || *(p - 1) == '\n')
00281             break;
00282         p += strlen(sep);
00283     }
00284     *start_p = p;
00285     p += strlen(sep);
00286 
00287     /* check if header text ok */
00288     for (; p < datend && *p != '-'; p++)
00289     {
00290         /* various junk can be there, but definitely not line-feed  */
00291         if (*p >= ' ')
00292             continue;
00293         return PXE_PGP_CORRUPT_ARMOR;
00294     }
00295     if (datend - p < 5 || memcmp(p, sep, 5) != 0)
00296         return PXE_PGP_CORRUPT_ARMOR;
00297     p += 5;
00298 
00299     /* check if at end of line */
00300     if (p < datend)
00301     {
00302         if (*p != '\n' && *p != '\r')
00303             return PXE_PGP_CORRUPT_ARMOR;
00304         if (*p == '\r')
00305             p++;
00306         if (p < datend && *p == '\n')
00307             p++;
00308     }
00309     return p - *start_p;
00310 }
00311 
00312 int
00313 pgp_armor_decode(const uint8 *src, unsigned len, uint8 *dst)
00314 {
00315     const uint8 *p = src;
00316     const uint8 *data_end = src + len;
00317     long        crc;
00318     const uint8 *base64_start,
00319                *armor_end;
00320     const uint8 *base64_end = NULL;
00321     uint8       buf[4];
00322     int         hlen;
00323     int         res = PXE_PGP_CORRUPT_ARMOR;
00324 
00325     /* armor start */
00326     hlen = find_header(src, data_end, &p, 0);
00327     if (hlen <= 0)
00328         goto out;
00329     p += hlen;
00330 
00331     /* armor end */
00332     hlen = find_header(p, data_end, &armor_end, 1);
00333     if (hlen <= 0)
00334         goto out;
00335 
00336     /* skip comments - find empty line */
00337     while (p < armor_end && *p != '\n' && *p != '\r')
00338     {
00339         p = memchr(p, '\n', armor_end - p);
00340         if (!p)
00341             goto out;
00342 
00343         /* step to start of next line */
00344         p++;
00345     }
00346     base64_start = p;
00347 
00348     /* find crc pos */
00349     for (p = armor_end; p >= base64_start; p--)
00350         if (*p == '=')
00351         {
00352             base64_end = p - 1;
00353             break;
00354         }
00355     if (base64_end == NULL)
00356         goto out;
00357 
00358     /* decode crc */
00359     if (b64_decode(p + 1, 4, buf) != 3)
00360         goto out;
00361     crc = (((long) buf[0]) << 16) + (((long) buf[1]) << 8) + (long) buf[2];
00362 
00363     /* decode data */
00364     res = b64_decode(base64_start, base64_end - base64_start, dst);
00365 
00366     /* check crc */
00367     if (res >= 0 && crc24(dst, res) != crc)
00368         res = PXE_PGP_CORRUPT_ARMOR;
00369 out:
00370     return res;
00371 }
00372 
00373 unsigned
00374 pgp_armor_enc_len(unsigned len)
00375 {
00376     return b64_enc_len(len) + strlen(armor_header) + strlen(armor_footer) + 16;
00377 }
00378 
00379 unsigned
00380 pgp_armor_dec_len(unsigned len)
00381 {
00382     return b64_dec_len(len);
00383 }