Header And Logo

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

pgp-encrypt.c

Go to the documentation of this file.
00001 /*
00002  * pgp-encrypt.c
00003  *    OpenPGP encrypt.
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-encrypt.c
00030  */
00031 
00032 #include "postgres.h"
00033 
00034 #include <time.h>
00035 
00036 #include "mbuf.h"
00037 #include "px.h"
00038 #include "pgp.h"
00039 
00040 
00041 #define MDC_DIGEST_LEN 20
00042 #define STREAM_ID 0xE0
00043 #define STREAM_BLOCK_SHIFT  14
00044 
00045 static uint8 *
00046 render_newlen(uint8 *h, int len)
00047 {
00048     if (len <= 191)
00049     {
00050         *h++ = len & 255;
00051     }
00052     else if (len > 191 && len <= 8383)
00053     {
00054         *h++ = ((len - 192) >> 8) + 192;
00055         *h++ = (len - 192) & 255;
00056     }
00057     else
00058     {
00059         *h++ = 255;
00060         *h++ = (len >> 24) & 255;
00061         *h++ = (len >> 16) & 255;
00062         *h++ = (len >> 8) & 255;
00063         *h++ = len & 255;
00064     }
00065     return h;
00066 }
00067 
00068 static int
00069 write_tag_only(PushFilter *dst, int tag)
00070 {
00071     uint8       hdr = 0xC0 | tag;
00072 
00073     return pushf_write(dst, &hdr, 1);
00074 }
00075 
00076 static int
00077 write_normal_header(PushFilter *dst, int tag, int len)
00078 {
00079     uint8       hdr[8];
00080     uint8      *h = hdr;
00081 
00082     *h++ = 0xC0 | tag;
00083     h = render_newlen(h, len);
00084     return pushf_write(dst, hdr, h - hdr);
00085 }
00086 
00087 
00088 /*
00089  * MAC writer
00090  */
00091 
00092 static int
00093 mdc_init(PushFilter *dst, void *init_arg, void **priv_p)
00094 {
00095     int         res;
00096     PX_MD      *md;
00097 
00098     res = pgp_load_digest(PGP_DIGEST_SHA1, &md);
00099     if (res < 0)
00100         return res;
00101 
00102     *priv_p = md;
00103     return 0;
00104 }
00105 
00106 static int
00107 mdc_write(PushFilter *dst, void *priv, const uint8 *data, int len)
00108 {
00109     PX_MD      *md = priv;
00110 
00111     px_md_update(md, data, len);
00112     return pushf_write(dst, data, len);
00113 }
00114 
00115 static int
00116 mdc_flush(PushFilter *dst, void *priv)
00117 {
00118     int         res;
00119     uint8       pkt[2 + MDC_DIGEST_LEN];
00120     PX_MD      *md = priv;
00121 
00122     /*
00123      * create mdc pkt
00124      */
00125     pkt[0] = 0xD3;
00126     pkt[1] = 0x14;              /* MDC_DIGEST_LEN */
00127     px_md_update(md, pkt, 2);
00128     px_md_finish(md, pkt + 2);
00129 
00130     res = pushf_write(dst, pkt, 2 + MDC_DIGEST_LEN);
00131     memset(pkt, 0, 2 + MDC_DIGEST_LEN);
00132     return res;
00133 }
00134 
00135 static void
00136 mdc_free(void *priv)
00137 {
00138     PX_MD      *md = priv;
00139 
00140     px_md_free(md);
00141 }
00142 
00143 static const PushFilterOps mdc_filter = {
00144     mdc_init, mdc_write, mdc_flush, mdc_free
00145 };
00146 
00147 
00148 /*
00149  * Encrypted pkt writer
00150  */
00151 #define ENCBUF 8192
00152 struct EncStat
00153 {
00154     PGP_CFB    *ciph;
00155     uint8       buf[ENCBUF];
00156 };
00157 
00158 static int
00159 encrypt_init(PushFilter *next, void *init_arg, void **priv_p)
00160 {
00161     struct EncStat *st;
00162     PGP_Context *ctx = init_arg;
00163     PGP_CFB    *ciph;
00164     int         resync = 1;
00165     int         res;
00166 
00167     /* should we use newer packet format? */
00168     if (ctx->disable_mdc == 0)
00169     {
00170         uint8       ver = 1;
00171 
00172         resync = 0;
00173         res = pushf_write(next, &ver, 1);
00174         if (res < 0)
00175             return res;
00176     }
00177     res = pgp_cfb_create(&ciph, ctx->cipher_algo,
00178                          ctx->sess_key, ctx->sess_key_len, resync, NULL);
00179     if (res < 0)
00180         return res;
00181 
00182     st = px_alloc(sizeof(*st));
00183     memset(st, 0, sizeof(*st));
00184     st->ciph = ciph;
00185 
00186     *priv_p = st;
00187     return ENCBUF;
00188 }
00189 
00190 static int
00191 encrypt_process(PushFilter *next, void *priv, const uint8 *data, int len)
00192 {
00193     int         res;
00194     struct EncStat *st = priv;
00195     int         avail = len;
00196 
00197     while (avail > 0)
00198     {
00199         int         tmplen = avail > ENCBUF ? ENCBUF : avail;
00200 
00201         res = pgp_cfb_encrypt(st->ciph, data, tmplen, st->buf);
00202         if (res < 0)
00203             return res;
00204 
00205         res = pushf_write(next, st->buf, tmplen);
00206         if (res < 0)
00207             return res;
00208 
00209         data += tmplen;
00210         avail -= tmplen;
00211     }
00212     return 0;
00213 }
00214 
00215 static void
00216 encrypt_free(void *priv)
00217 {
00218     struct EncStat *st = priv;
00219 
00220     memset(st, 0, sizeof(*st));
00221     px_free(st);
00222 }
00223 
00224 static const PushFilterOps encrypt_filter = {
00225     encrypt_init, encrypt_process, NULL, encrypt_free
00226 };
00227 
00228 /*
00229  * Write Streamable pkts
00230  */
00231 
00232 struct PktStreamStat
00233 {
00234     int         final_done;
00235     int         pkt_block;
00236 };
00237 
00238 static int
00239 pkt_stream_init(PushFilter *next, void *init_arg, void **priv_p)
00240 {
00241     struct PktStreamStat *st;
00242 
00243     st = px_alloc(sizeof(*st));
00244     st->final_done = 0;
00245     st->pkt_block = 1 << STREAM_BLOCK_SHIFT;
00246     *priv_p = st;
00247 
00248     return st->pkt_block;
00249 }
00250 
00251 static int
00252 pkt_stream_process(PushFilter *next, void *priv, const uint8 *data, int len)
00253 {
00254     int         res;
00255     uint8       hdr[8];
00256     uint8      *h = hdr;
00257     struct PktStreamStat *st = priv;
00258 
00259     if (st->final_done)
00260         return PXE_BUG;
00261 
00262     if (len == st->pkt_block)
00263         *h++ = STREAM_ID | STREAM_BLOCK_SHIFT;
00264     else
00265     {
00266         h = render_newlen(h, len);
00267         st->final_done = 1;
00268     }
00269 
00270     res = pushf_write(next, hdr, h - hdr);
00271     if (res < 0)
00272         return res;
00273 
00274     return pushf_write(next, data, len);
00275 }
00276 
00277 static int
00278 pkt_stream_flush(PushFilter *next, void *priv)
00279 {
00280     int         res;
00281     uint8       hdr[8];
00282     uint8      *h = hdr;
00283     struct PktStreamStat *st = priv;
00284 
00285     /* stream MUST end with normal packet. */
00286     if (!st->final_done)
00287     {
00288         h = render_newlen(h, 0);
00289         res = pushf_write(next, hdr, h - hdr);
00290         if (res < 0)
00291             return res;
00292         st->final_done = 1;
00293     }
00294     return 0;
00295 }
00296 
00297 static void
00298 pkt_stream_free(void *priv)
00299 {
00300     struct PktStreamStat *st = priv;
00301 
00302     memset(st, 0, sizeof(*st));
00303     px_free(st);
00304 }
00305 
00306 static const PushFilterOps pkt_stream_filter = {
00307     pkt_stream_init, pkt_stream_process, pkt_stream_flush, pkt_stream_free
00308 };
00309 
00310 int
00311 pgp_create_pkt_writer(PushFilter *dst, int tag, PushFilter **res_p)
00312 {
00313     int         res;
00314 
00315     res = write_tag_only(dst, tag);
00316     if (res < 0)
00317         return res;
00318 
00319     return pushf_create(res_p, &pkt_stream_filter, NULL, dst);
00320 }
00321 
00322 /*
00323  * Text conversion filter
00324  */
00325 
00326 static int
00327 crlf_process(PushFilter *dst, void *priv, const uint8 *data, int len)
00328 {
00329     const uint8 *data_end = data + len;
00330     const uint8 *p2,
00331                *p1 = data;
00332     int         line_len;
00333     static const uint8 crlf[] = {'\r', '\n'};
00334     int         res = 0;
00335 
00336     while (p1 < data_end)
00337     {
00338         p2 = memchr(p1, '\n', data_end - p1);
00339         if (p2 == NULL)
00340             p2 = data_end;
00341 
00342         line_len = p2 - p1;
00343 
00344         /* write data */
00345         res = 0;
00346         if (line_len > 0)
00347         {
00348             res = pushf_write(dst, p1, line_len);
00349             if (res < 0)
00350                 break;
00351             p1 += line_len;
00352         }
00353 
00354         /* write crlf */
00355         while (p1 < data_end && *p1 == '\n')
00356         {
00357             res = pushf_write(dst, crlf, 2);
00358             if (res < 0)
00359                 break;
00360             p1++;
00361         }
00362     }
00363     return res;
00364 }
00365 
00366 static const PushFilterOps crlf_filter = {
00367     NULL, crlf_process, NULL, NULL
00368 };
00369 
00370 /*
00371  * Initialize literal data packet
00372  */
00373 static int
00374 init_litdata_packet(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
00375 {
00376     int         res;
00377     int         hdrlen;
00378     uint8       hdr[6];
00379     uint32      t;
00380     PushFilter *pkt;
00381     int         type;
00382 
00383     /*
00384      * Create header
00385      */
00386 
00387     if (ctx->text_mode)
00388         type = ctx->unicode_mode ? 'u' : 't';
00389     else
00390         type = 'b';
00391 
00392     /*
00393      * Store the creation time into packet. The goal is to have as few known
00394      * bytes as possible.
00395      */
00396     t = (uint32) time(NULL);
00397 
00398     hdr[0] = type;
00399     hdr[1] = 0;
00400     hdr[2] = (t >> 24) & 255;
00401     hdr[3] = (t >> 16) & 255;
00402     hdr[4] = (t >> 8) & 255;
00403     hdr[5] = t & 255;
00404     hdrlen = 6;
00405 
00406     res = write_tag_only(dst, PGP_PKT_LITERAL_DATA);
00407     if (res < 0)
00408         return res;
00409 
00410     res = pushf_create(&pkt, &pkt_stream_filter, ctx, dst);
00411     if (res < 0)
00412         return res;
00413 
00414     res = pushf_write(pkt, hdr, hdrlen);
00415     if (res < 0)
00416     {
00417         pushf_free(pkt);
00418         return res;
00419     }
00420 
00421     *pf_res = pkt;
00422     return 0;
00423 }
00424 
00425 /*
00426  * Initialize compression filter
00427  */
00428 static int
00429 init_compress(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
00430 {
00431     int         res;
00432     uint8       type = ctx->compress_algo;
00433     PushFilter *pkt;
00434 
00435     res = write_tag_only(dst, PGP_PKT_COMPRESSED_DATA);
00436     if (res < 0)
00437         return res;
00438 
00439     res = pushf_create(&pkt, &pkt_stream_filter, ctx, dst);
00440     if (res < 0)
00441         return res;
00442 
00443     res = pushf_write(pkt, &type, 1);
00444     if (res >= 0)
00445         res = pgp_compress_filter(pf_res, ctx, pkt);
00446 
00447     if (res < 0)
00448         pushf_free(pkt);
00449 
00450     return res;
00451 }
00452 
00453 /*
00454  * Initialize encdata packet
00455  */
00456 static int
00457 init_encdata_packet(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
00458 {
00459     int         res;
00460     int         tag;
00461 
00462     if (ctx->disable_mdc)
00463         tag = PGP_PKT_SYMENCRYPTED_DATA;
00464     else
00465         tag = PGP_PKT_SYMENCRYPTED_DATA_MDC;
00466 
00467     res = write_tag_only(dst, tag);
00468     if (res < 0)
00469         return res;
00470 
00471     return pushf_create(pf_res, &pkt_stream_filter, ctx, dst);
00472 }
00473 
00474 /*
00475  * write prefix
00476  */
00477 static int
00478 write_prefix(PGP_Context *ctx, PushFilter *dst)
00479 {
00480     uint8       prefix[PGP_MAX_BLOCK + 2];
00481     int         res,
00482                 bs;
00483 
00484     bs = pgp_get_cipher_block_size(ctx->cipher_algo);
00485     res = px_get_random_bytes(prefix, bs);
00486     if (res < 0)
00487         return res;
00488 
00489     prefix[bs + 0] = prefix[bs - 2];
00490     prefix[bs + 1] = prefix[bs - 1];
00491 
00492     res = pushf_write(dst, prefix, bs + 2);
00493     memset(prefix, 0, bs + 2);
00494     return res < 0 ? res : 0;
00495 }
00496 
00497 /*
00498  * write symmetrically encrypted session key packet
00499  */
00500 
00501 static int
00502 symencrypt_sesskey(PGP_Context *ctx, uint8 *dst)
00503 {
00504     int         res;
00505     PGP_CFB    *cfb;
00506     uint8       algo = ctx->cipher_algo;
00507 
00508     res = pgp_cfb_create(&cfb, ctx->s2k_cipher_algo,
00509                          ctx->s2k.key, ctx->s2k.key_len, 0, NULL);
00510     if (res < 0)
00511         return res;
00512 
00513     pgp_cfb_encrypt(cfb, &algo, 1, dst);
00514     pgp_cfb_encrypt(cfb, ctx->sess_key, ctx->sess_key_len, dst + 1);
00515 
00516     pgp_cfb_free(cfb);
00517     return ctx->sess_key_len + 1;
00518 }
00519 
00520 /* 5.3: Symmetric-Key Encrypted Session-Key */
00521 static int
00522 write_symenc_sesskey(PGP_Context *ctx, PushFilter *dst)
00523 {
00524     uint8       pkt[256];
00525     int         pktlen;
00526     int         res;
00527     uint8      *p = pkt;
00528 
00529     *p++ = 4;                   /* 5.3 - version number  */
00530     *p++ = ctx->s2k_cipher_algo;
00531 
00532     *p++ = ctx->s2k.mode;
00533     *p++ = ctx->s2k.digest_algo;
00534     if (ctx->s2k.mode > 0)
00535     {
00536         memcpy(p, ctx->s2k.salt, 8);
00537         p += 8;
00538     }
00539     if (ctx->s2k.mode == 3)
00540         *p++ = ctx->s2k.iter;
00541 
00542     if (ctx->use_sess_key)
00543     {
00544         res = symencrypt_sesskey(ctx, p);
00545         if (res < 0)
00546             return res;
00547         p += res;
00548     }
00549 
00550     pktlen = p - pkt;
00551     res = write_normal_header(dst, PGP_PKT_SYMENCRYPTED_SESSKEY, pktlen);
00552     if (res >= 0)
00553         res = pushf_write(dst, pkt, pktlen);
00554 
00555     memset(pkt, 0, pktlen);
00556     return res;
00557 }
00558 
00559 /*
00560  * key setup
00561  */
00562 static int
00563 init_s2k_key(PGP_Context *ctx)
00564 {
00565     int         res;
00566 
00567     if (ctx->s2k_cipher_algo < 0)
00568         ctx->s2k_cipher_algo = ctx->cipher_algo;
00569 
00570     res = pgp_s2k_fill(&ctx->s2k, ctx->s2k_mode, ctx->s2k_digest_algo);
00571     if (res < 0)
00572         return res;
00573 
00574     return pgp_s2k_process(&ctx->s2k, ctx->s2k_cipher_algo,
00575                            ctx->sym_key, ctx->sym_key_len);
00576 }
00577 
00578 static int
00579 init_sess_key(PGP_Context *ctx)
00580 {
00581     int         res;
00582 
00583     if (ctx->use_sess_key || ctx->pub_key)
00584     {
00585         ctx->sess_key_len = pgp_get_cipher_key_size(ctx->cipher_algo);
00586         res = px_get_random_bytes(ctx->sess_key, ctx->sess_key_len);
00587         if (res < 0)
00588             return res;
00589     }
00590     else
00591     {
00592         ctx->sess_key_len = ctx->s2k.key_len;
00593         memcpy(ctx->sess_key, ctx->s2k.key, ctx->s2k.key_len);
00594     }
00595 
00596     return 0;
00597 }
00598 
00599 /*
00600  * combine
00601  */
00602 int
00603 pgp_encrypt(PGP_Context *ctx, MBuf *src, MBuf *dst)
00604 {
00605     int         res;
00606     int         len;
00607     uint8      *buf;
00608     PushFilter *pf,
00609                *pf_tmp;
00610 
00611     /*
00612      * do we have any key
00613      */
00614     if (!ctx->sym_key && !ctx->pub_key)
00615         return PXE_ARGUMENT_ERROR;
00616 
00617     /* MBuf writer */
00618     res = pushf_create_mbuf_writer(&pf, dst);
00619     if (res < 0)
00620         goto out;
00621 
00622     /*
00623      * initialize symkey
00624      */
00625     if (ctx->sym_key)
00626     {
00627         res = init_s2k_key(ctx);
00628         if (res < 0)
00629             goto out;
00630     }
00631 
00632     res = init_sess_key(ctx);
00633     if (res < 0)
00634         goto out;
00635 
00636     /*
00637      * write keypkt
00638      */
00639     if (ctx->pub_key)
00640         res = pgp_write_pubenc_sesskey(ctx, pf);
00641     else
00642         res = write_symenc_sesskey(ctx, pf);
00643     if (res < 0)
00644         goto out;
00645 
00646     /* encrypted data pkt */
00647     res = init_encdata_packet(&pf_tmp, ctx, pf);
00648     if (res < 0)
00649         goto out;
00650     pf = pf_tmp;
00651 
00652     /* encrypter */
00653     res = pushf_create(&pf_tmp, &encrypt_filter, ctx, pf);
00654     if (res < 0)
00655         goto out;
00656     pf = pf_tmp;
00657 
00658     /* hasher */
00659     if (ctx->disable_mdc == 0)
00660     {
00661         res = pushf_create(&pf_tmp, &mdc_filter, ctx, pf);
00662         if (res < 0)
00663             goto out;
00664         pf = pf_tmp;
00665     }
00666 
00667     /* prefix */
00668     res = write_prefix(ctx, pf);
00669     if (res < 0)
00670         goto out;
00671 
00672     /* compressor */
00673     if (ctx->compress_algo > 0 && ctx->compress_level > 0)
00674     {
00675         res = init_compress(&pf_tmp, ctx, pf);
00676         if (res < 0)
00677             goto out;
00678         pf = pf_tmp;
00679     }
00680 
00681     /* data streamer */
00682     res = init_litdata_packet(&pf_tmp, ctx, pf);
00683     if (res < 0)
00684         goto out;
00685     pf = pf_tmp;
00686 
00687 
00688     /* text conversion? */
00689     if (ctx->text_mode && ctx->convert_crlf)
00690     {
00691         res = pushf_create(&pf_tmp, &crlf_filter, ctx, pf);
00692         if (res < 0)
00693             goto out;
00694         pf = pf_tmp;
00695     }
00696 
00697     /*
00698      * chain complete
00699      */
00700 
00701     len = mbuf_grab(src, mbuf_avail(src), &buf);
00702     res = pushf_write(pf, buf, len);
00703     if (res >= 0)
00704         res = pushf_flush(pf);
00705 out:
00706     pushf_free_all(pf);
00707     return res;
00708 }