Line data Source code
1 : /*
2 : * pgp-encrypt.c
3 : * OpenPGP encrypt.
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-encrypt.c
30 : */
31 :
32 : #include "postgres.h"
33 :
34 : #include <time.h>
35 :
36 : #include "mbuf.h"
37 : #include "pgp.h"
38 : #include "px.h"
39 :
40 : #define MDC_DIGEST_LEN 20
41 : #define STREAM_ID 0xE0
42 : #define STREAM_BLOCK_SHIFT 14
43 :
44 : static uint8 *
45 0 : render_newlen(uint8 *h, int len)
46 : {
47 0 : if (len <= 191)
48 : {
49 0 : *h++ = len & 255;
50 0 : }
51 0 : else if (len > 191 && len <= 8383)
52 : {
53 0 : *h++ = ((len - 192) >> 8) + 192;
54 0 : *h++ = (len - 192) & 255;
55 0 : }
56 : else
57 : {
58 0 : *h++ = 255;
59 0 : *h++ = (len >> 24) & 255;
60 0 : *h++ = (len >> 16) & 255;
61 0 : *h++ = (len >> 8) & 255;
62 0 : *h++ = len & 255;
63 : }
64 0 : return h;
65 : }
66 :
67 : static int
68 0 : write_tag_only(PushFilter *dst, int tag)
69 : {
70 0 : uint8 hdr = 0xC0 | tag;
71 :
72 0 : return pushf_write(dst, &hdr, 1);
73 0 : }
74 :
75 : static int
76 0 : write_normal_header(PushFilter *dst, int tag, int len)
77 : {
78 0 : uint8 hdr[8];
79 0 : uint8 *h = hdr;
80 :
81 0 : *h++ = 0xC0 | tag;
82 0 : h = render_newlen(h, len);
83 0 : return pushf_write(dst, hdr, h - hdr);
84 0 : }
85 :
86 :
87 : /*
88 : * MAC writer
89 : */
90 :
91 : static int
92 0 : mdc_init(PushFilter *dst, void *init_arg, void **priv_p)
93 : {
94 0 : int res;
95 0 : PX_MD *md;
96 :
97 0 : res = pgp_load_digest(PGP_DIGEST_SHA1, &md);
98 0 : if (res < 0)
99 0 : return res;
100 :
101 0 : *priv_p = md;
102 0 : return 0;
103 0 : }
104 :
105 : static int
106 0 : mdc_write(PushFilter *dst, void *priv, const uint8 *data, int len)
107 : {
108 0 : PX_MD *md = priv;
109 :
110 0 : px_md_update(md, data, len);
111 0 : return pushf_write(dst, data, len);
112 0 : }
113 :
114 : static int
115 0 : mdc_flush(PushFilter *dst, void *priv)
116 : {
117 0 : int res;
118 0 : uint8 pkt[2 + MDC_DIGEST_LEN];
119 0 : PX_MD *md = priv;
120 :
121 : /*
122 : * create mdc pkt
123 : */
124 0 : pkt[0] = 0xD3;
125 0 : pkt[1] = 0x14; /* MDC_DIGEST_LEN */
126 0 : px_md_update(md, pkt, 2);
127 0 : px_md_finish(md, pkt + 2);
128 :
129 0 : res = pushf_write(dst, pkt, 2 + MDC_DIGEST_LEN);
130 0 : px_memset(pkt, 0, 2 + MDC_DIGEST_LEN);
131 0 : return res;
132 0 : }
133 :
134 : static void
135 0 : mdc_free(void *priv)
136 : {
137 0 : PX_MD *md = priv;
138 :
139 0 : px_md_free(md);
140 0 : }
141 :
142 : static const PushFilterOps mdc_filter = {
143 : mdc_init, mdc_write, mdc_flush, mdc_free
144 : };
145 :
146 :
147 : /*
148 : * Encrypted pkt writer
149 : */
150 : #define ENCBUF 8192
151 : struct EncStat
152 : {
153 : PGP_CFB *ciph;
154 : uint8 buf[ENCBUF];
155 : };
156 :
157 : static int
158 0 : encrypt_init(PushFilter *next, void *init_arg, void **priv_p)
159 : {
160 0 : struct EncStat *st;
161 0 : PGP_Context *ctx = init_arg;
162 0 : PGP_CFB *ciph;
163 0 : int resync = 1;
164 0 : int res;
165 :
166 : /* should we use newer packet format? */
167 0 : if (ctx->disable_mdc == 0)
168 : {
169 0 : uint8 ver = 1;
170 :
171 0 : resync = 0;
172 0 : res = pushf_write(next, &ver, 1);
173 0 : if (res < 0)
174 0 : return res;
175 0 : }
176 0 : res = pgp_cfb_create(&ciph, ctx->cipher_algo,
177 0 : ctx->sess_key, ctx->sess_key_len, resync, NULL);
178 0 : if (res < 0)
179 0 : return res;
180 :
181 0 : st = palloc0_object(struct EncStat);
182 0 : st->ciph = ciph;
183 :
184 0 : *priv_p = st;
185 0 : return ENCBUF;
186 0 : }
187 :
188 : static int
189 0 : encrypt_process(PushFilter *next, void *priv, const uint8 *data, int len)
190 : {
191 0 : int res;
192 0 : struct EncStat *st = priv;
193 0 : int avail = len;
194 :
195 0 : while (avail > 0)
196 : {
197 0 : int tmplen = avail > ENCBUF ? ENCBUF : avail;
198 :
199 0 : res = pgp_cfb_encrypt(st->ciph, data, tmplen, st->buf);
200 0 : if (res < 0)
201 0 : return res;
202 :
203 0 : res = pushf_write(next, st->buf, tmplen);
204 0 : if (res < 0)
205 0 : return res;
206 :
207 0 : data += tmplen;
208 0 : avail -= tmplen;
209 0 : }
210 0 : return 0;
211 0 : }
212 :
213 : static void
214 0 : encrypt_free(void *priv)
215 : {
216 0 : struct EncStat *st = priv;
217 :
218 0 : if (st->ciph)
219 0 : pgp_cfb_free(st->ciph);
220 0 : px_memset(st, 0, sizeof(*st));
221 0 : pfree(st);
222 0 : }
223 :
224 : static const PushFilterOps encrypt_filter = {
225 : encrypt_init, encrypt_process, NULL, encrypt_free
226 : };
227 :
228 : /*
229 : * Write Streamable pkts
230 : */
231 :
232 : struct PktStreamStat
233 : {
234 : int final_done;
235 : int pkt_block;
236 : };
237 :
238 : static int
239 0 : pkt_stream_init(PushFilter *next, void *init_arg, void **priv_p)
240 : {
241 0 : struct PktStreamStat *st;
242 :
243 0 : st = palloc_object(struct PktStreamStat);
244 0 : st->final_done = 0;
245 0 : st->pkt_block = 1 << STREAM_BLOCK_SHIFT;
246 0 : *priv_p = st;
247 :
248 0 : return st->pkt_block;
249 0 : }
250 :
251 : static int
252 0 : pkt_stream_process(PushFilter *next, void *priv, const uint8 *data, int len)
253 : {
254 0 : int res;
255 0 : uint8 hdr[8];
256 0 : uint8 *h = hdr;
257 0 : struct PktStreamStat *st = priv;
258 :
259 0 : if (st->final_done)
260 0 : return PXE_BUG;
261 :
262 0 : if (len == st->pkt_block)
263 0 : *h++ = STREAM_ID | STREAM_BLOCK_SHIFT;
264 : else
265 : {
266 0 : h = render_newlen(h, len);
267 0 : st->final_done = 1;
268 : }
269 :
270 0 : res = pushf_write(next, hdr, h - hdr);
271 0 : if (res < 0)
272 0 : return res;
273 :
274 0 : return pushf_write(next, data, len);
275 0 : }
276 :
277 : static int
278 0 : pkt_stream_flush(PushFilter *next, void *priv)
279 : {
280 0 : int res;
281 0 : uint8 hdr[8];
282 0 : uint8 *h = hdr;
283 0 : struct PktStreamStat *st = priv;
284 :
285 : /* stream MUST end with normal packet. */
286 0 : if (!st->final_done)
287 : {
288 0 : h = render_newlen(h, 0);
289 0 : res = pushf_write(next, hdr, h - hdr);
290 0 : if (res < 0)
291 0 : return res;
292 0 : st->final_done = 1;
293 0 : }
294 0 : return 0;
295 0 : }
296 :
297 : static void
298 0 : pkt_stream_free(void *priv)
299 : {
300 0 : struct PktStreamStat *st = priv;
301 :
302 0 : px_memset(st, 0, sizeof(*st));
303 0 : pfree(st);
304 0 : }
305 :
306 : static const PushFilterOps pkt_stream_filter = {
307 : pkt_stream_init, pkt_stream_process, pkt_stream_flush, pkt_stream_free
308 : };
309 :
310 : int
311 0 : pgp_create_pkt_writer(PushFilter *dst, int tag, PushFilter **res_p)
312 : {
313 0 : int res;
314 :
315 0 : res = write_tag_only(dst, tag);
316 0 : if (res < 0)
317 0 : return res;
318 :
319 0 : return pushf_create(res_p, &pkt_stream_filter, NULL, dst);
320 0 : }
321 :
322 : /*
323 : * Text conversion filter
324 : */
325 :
326 : static int
327 0 : crlf_process(PushFilter *dst, void *priv, const uint8 *data, int len)
328 : {
329 0 : const uint8 *data_end = data + len;
330 0 : const uint8 *p2,
331 0 : *p1 = data;
332 0 : int line_len;
333 : static const uint8 crlf[] = {'\r', '\n'};
334 0 : int res = 0;
335 :
336 0 : while (p1 < data_end)
337 : {
338 0 : p2 = memchr(p1, '\n', data_end - p1);
339 0 : if (p2 == NULL)
340 0 : p2 = data_end;
341 :
342 0 : line_len = p2 - p1;
343 :
344 : /* write data */
345 0 : res = 0;
346 0 : if (line_len > 0)
347 : {
348 0 : res = pushf_write(dst, p1, line_len);
349 0 : if (res < 0)
350 0 : break;
351 0 : p1 += line_len;
352 0 : }
353 :
354 : /* write crlf */
355 0 : while (p1 < data_end && *p1 == '\n')
356 : {
357 0 : res = pushf_write(dst, crlf, 2);
358 0 : if (res < 0)
359 0 : break;
360 0 : p1++;
361 : }
362 : }
363 0 : return res;
364 0 : }
365 :
366 : static const PushFilterOps crlf_filter = {
367 : NULL, crlf_process, NULL, NULL
368 : };
369 :
370 : /*
371 : * Initialize literal data packet
372 : */
373 : static int
374 0 : init_litdata_packet(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
375 : {
376 0 : int res;
377 0 : int hdrlen;
378 0 : uint8 hdr[6];
379 0 : uint32 t;
380 0 : PushFilter *pkt;
381 0 : int type;
382 :
383 : /*
384 : * Create header
385 : */
386 :
387 0 : if (ctx->text_mode)
388 0 : type = ctx->unicode_mode ? 'u' : 't';
389 : else
390 0 : type = 'b';
391 :
392 : /*
393 : * Store the creation time into packet. The goal is to have as few known
394 : * bytes as possible.
395 : */
396 0 : t = (uint32) time(NULL);
397 :
398 0 : hdr[0] = type;
399 0 : hdr[1] = 0;
400 0 : hdr[2] = (t >> 24) & 255;
401 0 : hdr[3] = (t >> 16) & 255;
402 0 : hdr[4] = (t >> 8) & 255;
403 0 : hdr[5] = t & 255;
404 0 : hdrlen = 6;
405 :
406 0 : res = write_tag_only(dst, PGP_PKT_LITERAL_DATA);
407 0 : if (res < 0)
408 0 : return res;
409 :
410 0 : res = pushf_create(&pkt, &pkt_stream_filter, ctx, dst);
411 0 : if (res < 0)
412 0 : return res;
413 :
414 0 : res = pushf_write(pkt, hdr, hdrlen);
415 0 : if (res < 0)
416 : {
417 0 : pushf_free(pkt);
418 0 : return res;
419 : }
420 :
421 0 : *pf_res = pkt;
422 0 : return 0;
423 0 : }
424 :
425 : /*
426 : * Initialize compression filter
427 : */
428 : static int
429 0 : init_compress(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
430 : {
431 0 : int res;
432 0 : uint8 type = ctx->compress_algo;
433 0 : PushFilter *pkt;
434 :
435 0 : res = write_tag_only(dst, PGP_PKT_COMPRESSED_DATA);
436 0 : if (res < 0)
437 0 : return res;
438 :
439 0 : res = pushf_create(&pkt, &pkt_stream_filter, ctx, dst);
440 0 : if (res < 0)
441 0 : return res;
442 :
443 0 : res = pushf_write(pkt, &type, 1);
444 0 : if (res >= 0)
445 0 : res = pgp_compress_filter(pf_res, ctx, pkt);
446 :
447 0 : if (res < 0)
448 0 : pushf_free(pkt);
449 :
450 0 : return res;
451 0 : }
452 :
453 : /*
454 : * Initialize encdata packet
455 : */
456 : static int
457 0 : init_encdata_packet(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
458 : {
459 0 : int res;
460 0 : int tag;
461 :
462 0 : if (ctx->disable_mdc)
463 0 : tag = PGP_PKT_SYMENCRYPTED_DATA;
464 : else
465 0 : tag = PGP_PKT_SYMENCRYPTED_DATA_MDC;
466 :
467 0 : res = write_tag_only(dst, tag);
468 0 : if (res < 0)
469 0 : return res;
470 :
471 0 : return pushf_create(pf_res, &pkt_stream_filter, ctx, dst);
472 0 : }
473 :
474 : /*
475 : * write prefix
476 : */
477 : static int
478 0 : write_prefix(PGP_Context *ctx, PushFilter *dst)
479 : {
480 0 : uint8 prefix[PGP_MAX_BLOCK + 2];
481 0 : int res,
482 : bs;
483 :
484 0 : bs = pgp_get_cipher_block_size(ctx->cipher_algo);
485 0 : if (!pg_strong_random(prefix, bs))
486 0 : return PXE_NO_RANDOM;
487 :
488 0 : prefix[bs + 0] = prefix[bs - 2];
489 0 : prefix[bs + 1] = prefix[bs - 1];
490 :
491 0 : res = pushf_write(dst, prefix, bs + 2);
492 0 : px_memset(prefix, 0, bs + 2);
493 0 : return res < 0 ? res : 0;
494 0 : }
495 :
496 : /*
497 : * write symmetrically encrypted session key packet
498 : */
499 :
500 : static int
501 0 : symencrypt_sesskey(PGP_Context *ctx, uint8 *dst)
502 : {
503 0 : int res;
504 0 : PGP_CFB *cfb;
505 0 : uint8 algo = ctx->cipher_algo;
506 :
507 0 : res = pgp_cfb_create(&cfb, ctx->s2k_cipher_algo,
508 0 : ctx->s2k.key, ctx->s2k.key_len, 0, NULL);
509 0 : if (res < 0)
510 0 : return res;
511 :
512 0 : pgp_cfb_encrypt(cfb, &algo, 1, dst);
513 0 : pgp_cfb_encrypt(cfb, ctx->sess_key, ctx->sess_key_len, dst + 1);
514 :
515 0 : pgp_cfb_free(cfb);
516 0 : return ctx->sess_key_len + 1;
517 0 : }
518 :
519 : /* 5.3: Symmetric-Key Encrypted Session-Key */
520 : static int
521 0 : write_symenc_sesskey(PGP_Context *ctx, PushFilter *dst)
522 : {
523 0 : uint8 pkt[256];
524 0 : int pktlen;
525 0 : int res;
526 0 : uint8 *p = pkt;
527 :
528 0 : *p++ = 4; /* 5.3 - version number */
529 0 : *p++ = ctx->s2k_cipher_algo;
530 :
531 0 : *p++ = ctx->s2k.mode;
532 0 : *p++ = ctx->s2k.digest_algo;
533 0 : if (ctx->s2k.mode > 0)
534 : {
535 0 : memcpy(p, ctx->s2k.salt, 8);
536 0 : p += 8;
537 0 : }
538 0 : if (ctx->s2k.mode == 3)
539 0 : *p++ = ctx->s2k.iter;
540 :
541 0 : if (ctx->use_sess_key)
542 : {
543 0 : res = symencrypt_sesskey(ctx, p);
544 0 : if (res < 0)
545 0 : return res;
546 0 : p += res;
547 0 : }
548 :
549 0 : pktlen = p - pkt;
550 0 : res = write_normal_header(dst, PGP_PKT_SYMENCRYPTED_SESSKEY, pktlen);
551 0 : if (res >= 0)
552 0 : res = pushf_write(dst, pkt, pktlen);
553 :
554 0 : px_memset(pkt, 0, pktlen);
555 0 : return res;
556 0 : }
557 :
558 : /*
559 : * key setup
560 : */
561 : static int
562 0 : init_s2k_key(PGP_Context *ctx)
563 : {
564 0 : int res;
565 :
566 0 : if (ctx->s2k_cipher_algo < 0)
567 0 : ctx->s2k_cipher_algo = ctx->cipher_algo;
568 :
569 0 : res = pgp_s2k_fill(&ctx->s2k, ctx->s2k_mode, ctx->s2k_digest_algo, ctx->s2k_count);
570 0 : if (res < 0)
571 0 : return res;
572 :
573 0 : return pgp_s2k_process(&ctx->s2k, ctx->s2k_cipher_algo,
574 0 : ctx->sym_key, ctx->sym_key_len);
575 0 : }
576 :
577 : static int
578 0 : init_sess_key(PGP_Context *ctx)
579 : {
580 0 : if (ctx->use_sess_key || ctx->pub_key)
581 : {
582 0 : ctx->sess_key_len = pgp_get_cipher_key_size(ctx->cipher_algo);
583 0 : if (!pg_strong_random(ctx->sess_key, ctx->sess_key_len))
584 0 : return PXE_NO_RANDOM;
585 0 : }
586 : else
587 : {
588 0 : ctx->sess_key_len = ctx->s2k.key_len;
589 0 : memcpy(ctx->sess_key, ctx->s2k.key, ctx->s2k.key_len);
590 : }
591 :
592 0 : return 0;
593 0 : }
594 :
595 : /*
596 : * combine
597 : */
598 : int
599 0 : pgp_encrypt(PGP_Context *ctx, MBuf *src, MBuf *dst)
600 : {
601 0 : int res;
602 0 : int len;
603 0 : uint8 *buf;
604 0 : PushFilter *pf,
605 : *pf_tmp;
606 :
607 : /*
608 : * do we have any key
609 : */
610 0 : if (!ctx->sym_key && !ctx->pub_key)
611 0 : return PXE_ARGUMENT_ERROR;
612 :
613 : /* MBuf writer */
614 0 : res = pushf_create_mbuf_writer(&pf, dst);
615 0 : if (res < 0)
616 0 : goto out;
617 :
618 : /*
619 : * initialize sym_key
620 : */
621 0 : if (ctx->sym_key)
622 : {
623 0 : res = init_s2k_key(ctx);
624 0 : if (res < 0)
625 0 : goto out;
626 0 : }
627 :
628 0 : res = init_sess_key(ctx);
629 0 : if (res < 0)
630 0 : goto out;
631 :
632 : /*
633 : * write keypkt
634 : */
635 0 : if (ctx->pub_key)
636 0 : res = pgp_write_pubenc_sesskey(ctx, pf);
637 : else
638 0 : res = write_symenc_sesskey(ctx, pf);
639 0 : if (res < 0)
640 0 : goto out;
641 :
642 : /* encrypted data pkt */
643 0 : res = init_encdata_packet(&pf_tmp, ctx, pf);
644 0 : if (res < 0)
645 0 : goto out;
646 0 : pf = pf_tmp;
647 :
648 : /* encrypter */
649 0 : res = pushf_create(&pf_tmp, &encrypt_filter, ctx, pf);
650 0 : if (res < 0)
651 0 : goto out;
652 0 : pf = pf_tmp;
653 :
654 : /* hasher */
655 0 : if (ctx->disable_mdc == 0)
656 : {
657 0 : res = pushf_create(&pf_tmp, &mdc_filter, ctx, pf);
658 0 : if (res < 0)
659 0 : goto out;
660 0 : pf = pf_tmp;
661 0 : }
662 :
663 : /* prefix */
664 0 : res = write_prefix(ctx, pf);
665 0 : if (res < 0)
666 0 : goto out;
667 :
668 : /* compressor */
669 0 : if (ctx->compress_algo > 0 && ctx->compress_level > 0)
670 : {
671 0 : res = init_compress(&pf_tmp, ctx, pf);
672 0 : if (res < 0)
673 0 : goto out;
674 0 : pf = pf_tmp;
675 0 : }
676 :
677 : /* data streamer */
678 0 : res = init_litdata_packet(&pf_tmp, ctx, pf);
679 0 : if (res < 0)
680 0 : goto out;
681 0 : pf = pf_tmp;
682 :
683 :
684 : /* text conversion? */
685 0 : if (ctx->text_mode && ctx->convert_crlf)
686 : {
687 0 : res = pushf_create(&pf_tmp, &crlf_filter, ctx, pf);
688 0 : if (res < 0)
689 0 : goto out;
690 0 : pf = pf_tmp;
691 0 : }
692 :
693 : /*
694 : * chain complete
695 : */
696 :
697 0 : len = mbuf_grab(src, mbuf_avail(src), &buf);
698 0 : res = pushf_write(pf, buf, len);
699 0 : if (res >= 0)
700 0 : res = pushf_flush(pf);
701 : out:
702 0 : pushf_free_all(pf);
703 0 : return res;
704 0 : }
|