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