xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/writer.c (revision c8da0e5fefd3800856b306200a18b2315c7fbb9f)
1 /*-
2  * Copyright (c) 2009 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Alistair Crooks (agc@NetBSD.org)
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 /*
30  * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
31  * All rights reserved.
32  * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
33  * their moral rights under the UK Copyright Design and Patents Act 1988 to
34  * be recorded as the authors of this copyright work.
35  *
36  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
37  * use this file except in compliance with the License.
38  *
39  * You may obtain a copy of the License at
40  *     http://www.apache.org/licenses/LICENSE-2.0
41  *
42  * Unless required by applicable law or agreed to in writing, software
43  * distributed under the License is distributed on an "AS IS" BASIS,
44  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
45  *
46  * See the License for the specific language governing permissions and
47  * limitations under the License.
48  */
49 
50 /** \file
51  * This file contains the base functions used by the writers.
52  */
53 #include "config.h"
54 
55 #include <sys/types.h>
56 
57 #ifdef HAVE_SYS_UIO_H
58 #include <sys/uio.h>
59 #endif
60 
61 #ifdef HAVE_FCNTL_H
62 #include <fcntl.h>
63 #endif
64 
65 #include <stdlib.h>
66 #include <string.h>
67 
68 #ifdef HAVE_UNISTD_H
69 #include <unistd.h>
70 #endif
71 
72 #ifdef HAVE_OPENSSL_CAST_H
73 #include <openssl/cast.h>
74 #endif
75 
76 #include "create.h"
77 #include "writer.h"
78 #include "keyring.h"
79 #include "signature.h"
80 #include "packet.h"
81 #include "packet-parse.h"
82 
83 #include "readerwriter.h"
84 #include "memory.h"
85 #include "netpgpdefs.h"
86 #include "keyring_local.h"
87 #include "version.h"
88 
89 
90 /*
91  * return true if OK, otherwise false
92  */
93 static bool
94 base_write(const void *src, unsigned length, __ops_createinfo_t * info)
95 {
96 	return info->winfo.writer(src, length, &info->errors, &info->winfo);
97 }
98 
99 /**
100  * \ingroup Core_WritePackets
101  *
102  * \param src
103  * \param length
104  * \param info
105  * \return 1 if OK, otherwise 0
106  */
107 
108 bool
109 __ops_write(const void *src, unsigned length, __ops_createinfo_t * info)
110 {
111 	return base_write(src, length, info);
112 }
113 
114 /**
115  * \ingroup Core_WritePackets
116  * \param n
117  * \param length
118  * \param info
119  * \return true if OK, otherwise false
120  */
121 
122 bool
123 __ops_write_scalar(unsigned n, unsigned length, __ops_createinfo_t *info)
124 {
125 	unsigned char   c[1];
126 
127 	while (length-- > 0) {
128 		c[0] = n >> (length * 8);
129 		if (!base_write(c, 1, info)) {
130 			return false;
131 		}
132 	}
133 	return true;
134 }
135 
136 /**
137  * \ingroup Core_WritePackets
138  * \param bn
139  * \param info
140  * \return 1 if OK, otherwise 0
141  */
142 
143 bool
144 __ops_write_mpi(const BIGNUM * bn, __ops_createinfo_t * info)
145 {
146 	unsigned char   buf[NETPGP_BUFSIZ];
147 	unsigned	bits = (unsigned)BN_num_bits(bn);
148 
149 	if (bits > 65535) {
150 		(void) fprintf(stderr, "__ops_write_mpi: too large %u\n", bits);
151 		return false;
152 	}
153 	BN_bn2bin(bn, buf);
154 	return __ops_write_scalar(bits, 2, info) &&
155 		__ops_write(buf, (bits + 7) / 8, info);
156 }
157 
158 /**
159  * \ingroup Core_WritePackets
160  * \param tag
161  * \param info
162  * \return 1 if OK, otherwise 0
163  */
164 
165 bool
166 __ops_write_ptag(__ops_content_tag_t tag, __ops_createinfo_t * info)
167 {
168 	unsigned char   c[1];
169 
170 	c[0] = tag | OPS_PTAG_ALWAYS_SET | OPS_PTAG_NEW_FORMAT;
171 	return base_write(c, 1, info);
172 }
173 
174 /**
175  * \ingroup Core_WritePackets
176  * \param length
177  * \param info
178  * \return 1 if OK, otherwise 0
179  */
180 
181 bool
182 __ops_write_length(unsigned length, __ops_createinfo_t * info)
183 {
184 	unsigned char   c[2];
185 
186 	if (length < 192) {
187 		c[0] = length;
188 		return base_write(c, 1, info);
189 	}
190 	if (length < 8192 + 192) {
191 		c[0] = ((length - 192) >> 8) + 192;
192 		c[1] = (length - 192) % 256;
193 		return base_write(c, 2, info);
194 	}
195 	return __ops_write_scalar(0xff, 1, info) && __ops_write_scalar(length, 4, info);
196 }
197 
198 /*
199  * Note that we finalise from the top down, so we don't use writers below
200  * that have already been finalised
201  */
202 bool
203 writer_info_finalise(__ops_error_t ** errors, __ops_writer_info_t * winfo)
204 {
205 	bool   ret = true;
206 
207 	if (winfo->finaliser) {
208 		ret = winfo->finaliser(errors, winfo);
209 		winfo->finaliser = NULL;
210 	}
211 	if (winfo->next && !writer_info_finalise(errors, winfo->next)) {
212 		winfo->finaliser = NULL;
213 		return false;
214 	}
215 	return ret;
216 }
217 
218 void
219 writer_info_delete(__ops_writer_info_t * winfo)
220 {
221 	/* we should have finalised before deleting */
222 	if (winfo->finaliser) {
223 		(void) fprintf(stderr, "writer_info_delete: not finalised\n");
224 		return;
225 	}
226 	if (winfo->next) {
227 		writer_info_delete(winfo->next);
228 		free(winfo->next);
229 		winfo->next = NULL;
230 	}
231 	if (winfo->destroyer) {
232 		winfo->destroyer(winfo);
233 		winfo->destroyer = NULL;
234 	}
235 	winfo->writer = NULL;
236 }
237 
238 /**
239  * \ingroup Core_Writers
240  *
241  * Set a writer in info. There should not be another writer set.
242  *
243  * \param info The info structure
244  * \param writer
245  * \param finaliser
246  * \param destroyer
247  * \param arg The argument for the writer and destroyer
248  */
249 void
250 __ops_writer_set(__ops_createinfo_t * info,
251 	       __ops_writer_t * writer,
252 	       __ops_writer_finaliser_t * finaliser,
253 	       __ops_writer_destroyer_t * destroyer,
254 	       void *arg)
255 {
256 	if (info->winfo.writer) {
257 		(void) fprintf(stderr, "__ops_writer_set: already set\n");
258 	} else {
259 		info->winfo.writer = writer;
260 		info->winfo.finaliser = finaliser;
261 		info->winfo.destroyer = destroyer;
262 		info->winfo.arg = arg;
263 	}
264 }
265 
266 /**
267  * \ingroup Core_Writers
268  *
269  * Push a writer in info. There must already be another writer set.
270  *
271  * \param info The info structure
272  * \param writer
273  * \param finaliser
274  * \param destroyer
275  * \param arg The argument for the writer and destroyer
276  */
277 void
278 __ops_writer_push(__ops_createinfo_t * info,
279 		__ops_writer_t * writer,
280 		__ops_writer_finaliser_t * finaliser,
281 		__ops_writer_destroyer_t * destroyer,
282 		void *arg)
283 {
284 	__ops_writer_info_t *copy = calloc(1, sizeof(*copy));
285 
286 	if (info->winfo.writer == NULL) {
287 		(void) fprintf(stderr, "__ops_writer_push: no orig writer\n");
288 	} else {
289 		*copy = info->winfo;
290 		info->winfo.next = copy;
291 
292 		info->winfo.writer = writer;
293 		info->winfo.finaliser = finaliser;
294 		info->winfo.destroyer = destroyer;
295 		info->winfo.arg = arg;
296 	}
297 }
298 
299 void
300 __ops_writer_pop(__ops_createinfo_t * info)
301 {
302 	__ops_writer_info_t *next;
303 
304 	/* Make sure the finaliser has been called. */
305 	if (info->winfo.finaliser) {
306 		(void) fprintf(stderr,
307 			"__ops_writer_pop: finaliser not called\n");
308 	} else if (info->winfo.next == NULL) {
309 		(void) fprintf(stderr,
310 			"__ops_writer_pop: not a stacked writer\n");
311 	} else {
312 		if (info->winfo.destroyer) {
313 			info->winfo.destroyer(&info->winfo);
314 		}
315 		next = info->winfo.next;
316 		info->winfo = *next;
317 		free(next);
318 	}
319 }
320 
321 /**
322  * \ingroup Core_Writers
323  *
324  * Close the writer currently set in info.
325  *
326  * \param info The info structure
327  */
328 bool
329 __ops_writer_close(__ops_createinfo_t * info)
330 {
331 	bool   ret = writer_info_finalise(&info->errors, &info->winfo);
332 
333 	writer_info_delete(&info->winfo);
334 	return ret;
335 }
336 
337 /**
338  * \ingroup Core_Writers
339  *
340  * Get the arg supplied to __ops_createinfo_set_writer().
341  *
342  * \param winfo The writer_info structure
343  * \return The arg
344  */
345 void           *
346 __ops_writer_get_arg(__ops_writer_info_t * winfo)
347 {
348 	return winfo->arg;
349 }
350 
351 /**
352  * \ingroup Core_Writers
353  *
354  * Write to the next writer down in the stack.
355  *
356  * \param src The data to write.
357  * \param length The length of src.
358  * \param errors A place to store errors.
359  * \param winfo The writer_info structure.
360  * \return Success - if false, then errors should contain the error.
361  */
362 bool
363 __ops_stacked_write(const void *src, unsigned length,
364 		  __ops_error_t ** errors, __ops_writer_info_t * winfo)
365 {
366 	return winfo->next->writer(src, length, errors, winfo->next);
367 }
368 
369 /**
370  * \ingroup Core_Writers
371  *
372  * Free the arg. Many writers just have a calloc()ed lump of storage, this
373  * function releases it.
374  *
375  * \param winfo the info structure.
376  */
377 void
378 __ops_writer_generic_destroyer(__ops_writer_info_t * winfo)
379 {
380 	(void) free(__ops_writer_get_arg(winfo));
381 }
382 
383 /**
384  * \ingroup Core_Writers
385  *
386  * A writer that just writes to the next one down. Useful for when you
387  * want to insert just a finaliser into the stack.
388  */
389 bool
390 __ops_writer_passthrough(const unsigned char *src,
391 		       unsigned length,
392 		       __ops_error_t ** errors,
393 		       __ops_writer_info_t * winfo)
394 {
395 	return __ops_stacked_write(src, length, errors, winfo);
396 }
397 
398 /**************************************************************************/
399 
400 /**
401  * \struct dash_escaped_t
402  */
403 typedef struct {
404 	unsigned   seen_nl:1;
405 	unsigned   seen_cr:1;
406 	__ops_create_sig_t *sig;
407 	__ops_memory_t   *trailing;
408 }               dash_escaped_t;
409 
410 static bool
411 dash_escaped_writer(const unsigned char *src,
412 		    unsigned length,
413 		    __ops_error_t ** errors,
414 		    __ops_writer_info_t * winfo)
415 {
416 	dash_escaped_t *dash = __ops_writer_get_arg(winfo);
417 	unsigned        n;
418 
419 	if (__ops_get_debug_level(__FILE__)) {
420 		unsigned int    i = 0;
421 		fprintf(stderr, "dash_escaped_writer writing %d:\n", length);
422 		for (i = 0; i < length; i++) {
423 			fprintf(stderr, "0x%02x ", src[i]);
424 			if (!((i + 1) % 16)) {
425 				fprintf(stderr, "\n");
426 			} else if (!((i + 1) % 8)) {
427 				fprintf(stderr, "  ");
428 			}
429 		}
430 		fprintf(stderr, "\n");
431 	}
432 	/* XXX: make this efficient */
433 	for (n = 0; n < length; ++n) {
434 		unsigned        l;
435 
436 		if (dash->seen_nl) {
437 			if (src[n] == '-' && !__ops_stacked_write("- ", 2, errors, winfo)) {
438 				return false;
439 			}
440 			dash->seen_nl = false;
441 		}
442 		dash->seen_nl = src[n] == '\n';
443 
444 		if (dash->seen_nl && !dash->seen_cr) {
445 			if (!__ops_stacked_write("\r", 1, errors, winfo)) {
446 				return false;
447 			}
448 			__ops_sig_add_data(dash->sig, "\r", 1);
449 		}
450 		dash->seen_cr = src[n] == '\r';
451 
452 		if (!__ops_stacked_write(&src[n], 1, errors, winfo)) {
453 			return false;
454 		}
455 
456 		/* trailing whitespace isn't included in the signature */
457 		if (src[n] == ' ' || src[n] == '\t') {
458 			__ops_memory_add(dash->trailing, &src[n], 1);
459 		} else {
460 			if ((l = __ops_memory_get_length(dash->trailing)) != 0) {
461 				if (!dash->seen_nl && !dash->seen_cr) {
462 					__ops_sig_add_data(dash->sig,
463 					 __ops_memory_get_data(dash->trailing),
464 							       l);
465 				}
466 				__ops_memory_clear(dash->trailing);
467 			}
468 			__ops_sig_add_data(dash->sig, &src[n], 1);
469 		}
470 	}
471 
472 	return true;
473 }
474 
475 /**
476  * \param winfo
477  */
478 static void
479 dash_escaped_destroyer(__ops_writer_info_t * winfo)
480 {
481 	dash_escaped_t *dash = __ops_writer_get_arg(winfo);
482 
483 	__ops_memory_free(dash->trailing);
484 	(void) free(dash);
485 }
486 
487 /**
488  * \ingroup Core_WritersNext
489  * \brief Push Clearsigned Writer onto stack
490  * \param info
491  * \param sig
492  */
493 bool
494 __ops_writer_push_clearsigned(__ops_createinfo_t * info,
495 			    __ops_create_sig_t * sig)
496 {
497 	static const char     header[] =
498 		"-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: ";
499 	const char     *hash = __ops_text_from_hash(__ops_sig_get_hash(sig));
500 	dash_escaped_t *dash = calloc(1, sizeof(*dash));
501 	bool   rtn;
502 
503 	rtn = (__ops_write(header, sizeof(header) - 1, info) &&
504 		__ops_write(hash, strlen(hash), info) &&
505 		__ops_write("\r\n\r\n", 4, info));
506 
507 	if (rtn == false) {
508 		OPS_ERROR(&info->errors, OPS_E_W,
509 			"Error pushing clearsigned header");
510 		free(dash);
511 		return rtn;
512 	}
513 	dash->seen_nl = true;
514 	dash->sig = sig;
515 	dash->trailing = __ops_memory_new();
516 	__ops_writer_push(info, dash_escaped_writer, NULL,
517 			dash_escaped_destroyer, dash);
518 	return rtn;
519 }
520 
521 
522 /**
523  * \struct base64_t
524  */
525 typedef struct {
526 	unsigned        pos;
527 	unsigned char   t;
528 	unsigned        checksum;
529 }               base64_t;
530 
531 static const char     b64map[] =
532 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
533 
534 static bool
535 base64_writer(const unsigned char *src,
536 	      unsigned length, __ops_error_t ** errors,
537 	      __ops_writer_info_t * winfo)
538 {
539 	base64_t   *base64 = __ops_writer_get_arg(winfo);
540 	unsigned        n;
541 
542 	for (n = 0; n < length;) {
543 		base64->checksum = __ops_crc24(base64->checksum, src[n]);
544 		if (base64->pos == 0) {
545 			/* XXXXXX00 00000000 00000000 */
546 			if (!__ops_stacked_write(&b64map[(unsigned)src[n] >> 2],
547 					1, errors, winfo)) {
548 				return false;
549 			}
550 
551 			/* 000000XX xxxx0000 00000000 */
552 			base64->t = (src[n++] & 3) << 4;
553 			base64->pos = 1;
554 		} else if (base64->pos == 1) {
555 			/* 000000xx XXXX0000 00000000 */
556 			base64->t += (unsigned)src[n] >> 4;
557 			if (!__ops_stacked_write(&b64map[base64->t], 1,
558 					errors, winfo)) {
559 				return false;
560 			}
561 
562 			/* 00000000 0000XXXX xx000000 */
563 			base64->t = (src[n++] & 0xf) << 2;
564 			base64->pos = 2;
565 		} else if (base64->pos == 2) {
566 			/* 00000000 0000xxxx XX000000 */
567 			base64->t += (unsigned)src[n] >> 6;
568 			if (!__ops_stacked_write(&b64map[base64->t], 1,
569 					errors, winfo)) {
570 				return false;
571 			}
572 
573 			/* 00000000 00000000 00XXXXXX */
574 			if (!__ops_stacked_write(&b64map[src[n++] & 0x3f], 1,
575 					errors, winfo)) {
576 				return false;
577 			}
578 
579 			base64->pos = 0;
580 		}
581 	}
582 
583 	return true;
584 }
585 
586 static bool
587 sig_finaliser(__ops_error_t ** errors,
588 		    __ops_writer_info_t * winfo)
589 {
590 	base64_t   *base64 = __ops_writer_get_arg(winfo);
591 	static const char     trailer[] = "\r\n-----END PGP SIGNATURE-----\r\n";
592 	unsigned char   c[3];
593 
594 	if (base64->pos) {
595 		if (!__ops_stacked_write(&b64map[base64->t], 1, errors,
596 				winfo)) {
597 			return false;
598 		}
599 		if (base64->pos == 1 && !__ops_stacked_write("==", 2, errors,
600 				winfo)) {
601 			return false;
602 		}
603 		if (base64->pos == 2 && !__ops_stacked_write("=", 1, errors,
604 				winfo)) {
605 			return false;
606 		}
607 	}
608 	/* Ready for the checksum */
609 	if (!__ops_stacked_write("\r\n=", 3, errors, winfo)) {
610 		return false;
611 	}
612 
613 	base64->pos = 0;		/* get ready to write the checksum */
614 
615 	c[0] = base64->checksum >> 16;
616 	c[1] = base64->checksum >> 8;
617 	c[2] = base64->checksum;
618 	/* push the checksum through our own writer */
619 	if (!base64_writer(c, 3, errors, winfo)) {
620 		return false;
621 	}
622 
623 	return __ops_stacked_write(trailer, sizeof(trailer) - 1, errors, winfo);
624 }
625 
626 /**
627  * \struct linebreak_t
628  */
629 typedef struct {
630 	unsigned        pos;
631 }               linebreak_t;
632 
633 #define BREAKPOS	76
634 
635 static bool
636 linebreak_writer(const unsigned char *src,
637 		 unsigned length,
638 		 __ops_error_t ** errors,
639 		 __ops_writer_info_t * winfo)
640 {
641 	linebreak_t *linebreak = __ops_writer_get_arg(winfo);
642 	unsigned        n;
643 
644 	for (n = 0; n < length; ++n, ++linebreak->pos) {
645 		if (src[n] == '\r' || src[n] == '\n') {
646 			linebreak->pos = 0;
647 		}
648 
649 		if (linebreak->pos == BREAKPOS) {
650 			if (!__ops_stacked_write("\r\n", 2, errors, winfo)) {
651 				return false;
652 			}
653 			linebreak->pos = 0;
654 		}
655 		if (!__ops_stacked_write(&src[n], 1, errors, winfo)) {
656 			return false;
657 		}
658 	}
659 
660 	return true;
661 }
662 
663 /**
664  * \ingroup Core_WritersNext
665  * \brief Push armoured signature on stack
666  * \param info
667  */
668 bool
669 __ops_writer_use_armored_sig(__ops_createinfo_t * info)
670 {
671 	static const char     header[] =
672 			"\r\n-----BEGIN PGP SIGNATURE-----\r\nVersion: "
673 			NETPGP_VERSION_STRING
674 			"\r\n\r\n";
675 	base64_t   *base64;
676 
677 	__ops_writer_pop(info);
678 	if (__ops_write(header, sizeof(header) - 1, info) == false) {
679 		OPS_ERROR(&info->errors, OPS_E_W,
680 			"Error switching to armoured signature");
681 		return false;
682 	}
683 	__ops_writer_push(info, linebreak_writer, NULL,
684 			__ops_writer_generic_destroyer,
685 			calloc(1, sizeof(linebreak_t)));
686 	base64 = calloc(1, sizeof(*base64));
687 	if (!base64) {
688 		OPS_MEMORY_ERROR(&info->errors);
689 		return false;
690 	}
691 	base64->checksum = CRC24_INIT;
692 	__ops_writer_push(info, base64_writer, sig_finaliser,
693 			__ops_writer_generic_destroyer, base64);
694 	return true;
695 }
696 
697 static bool
698 armoured_message_finaliser(__ops_error_t ** errors,
699 			   __ops_writer_info_t * winfo)
700 {
701 	/* TODO: This is same as sig_finaliser apart from trailer. */
702 	base64_t   *base64 = __ops_writer_get_arg(winfo);
703 	static const char     trailer[] = "\r\n-----END PGP MESSAGE-----\r\n";
704 	unsigned char   c[3];
705 
706 	if (base64->pos) {
707 		if (!__ops_stacked_write(&b64map[base64->t], 1, errors,
708 				winfo)) {
709 			return false;
710 		}
711 		if (base64->pos == 1 && !__ops_stacked_write("==", 2, errors,
712 				winfo)) {
713 			return false;
714 		}
715 		if (base64->pos == 2 && !__ops_stacked_write("=", 1, errors,
716 				winfo)) {
717 			return false;
718 		}
719 	}
720 	/* Ready for the checksum */
721 	if (!__ops_stacked_write("\r\n=", 3, errors, winfo)) {
722 		return false;
723 	}
724 
725 	base64->pos = 0;		/* get ready to write the checksum */
726 
727 	c[0] = base64->checksum >> 16;
728 	c[1] = base64->checksum >> 8;
729 	c[2] = base64->checksum;
730 	/* push the checksum through our own writer */
731 	if (!base64_writer(c, 3, errors, winfo)) {
732 		return false;
733 	}
734 
735 	return __ops_stacked_write(trailer, sizeof(trailer) - 1, errors, winfo);
736 }
737 
738 /**
739  \ingroup Core_WritersNext
740  \brief Write a PGP MESSAGE
741  \todo replace with generic function
742 */
743 void
744 __ops_writer_push_armoured_message(__ops_createinfo_t * info)
745 {
746 	static const char     header[] = "-----BEGIN PGP MESSAGE-----\r\n";
747 	base64_t   *base64;
748 
749 	__ops_write(header, sizeof(header) - 1, info);
750 	__ops_write("\r\n", 2, info);
751 	base64 = calloc(1, sizeof(*base64));
752 	base64->checksum = CRC24_INIT;
753 	__ops_writer_push(info, base64_writer, armoured_message_finaliser,
754 		__ops_writer_generic_destroyer, base64);
755 }
756 
757 static bool
758 armoured_finaliser(__ops_armor_type_t type, __ops_error_t ** errors,
759 		   __ops_writer_info_t * winfo)
760 {
761 	static const char     tail_pubkey[] =
762 			"\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n";
763 	static const char     tail_private_key[] =
764 			"\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n";
765 	const char           *tail = NULL;
766 	unsigned int    sz_tail = 0;
767 	base64_t   *base64;
768 	unsigned char   c[3];
769 
770 	switch (type) {
771 	case OPS_PGP_PUBLIC_KEY_BLOCK:
772 		tail = tail_pubkey;
773 		sz_tail = sizeof(tail_pubkey) - 1;
774 		break;
775 
776 	case OPS_PGP_PRIVATE_KEY_BLOCK:
777 		tail = tail_private_key;
778 		sz_tail = sizeof(tail_private_key) - 1;
779 		break;
780 
781 	default:
782 		(void) fprintf(stderr, "armoured_finaliser: unusual type\n");
783 		return false;
784 	}
785 
786 	base64 = __ops_writer_get_arg(winfo);
787 
788 	if (base64->pos) {
789 		if (!__ops_stacked_write(&b64map[base64->t], 1, errors,
790 				winfo)) {
791 			return false;
792 		}
793 		if (base64->pos == 1 && !__ops_stacked_write("==", 2, errors,
794 				winfo)) {
795 			return false;
796 		}
797 		if (base64->pos == 2 && !__ops_stacked_write("=", 1, errors,
798 				winfo)) {
799 			return false;
800 		}
801 	}
802 	/* Ready for the checksum */
803 	if (!__ops_stacked_write("\r\n=", 3, errors, winfo)) {
804 		return false;
805 	}
806 
807 	base64->pos = 0;		/* get ready to write the checksum */
808 
809 	c[0] = base64->checksum >> 16;
810 	c[1] = base64->checksum >> 8;
811 	c[2] = base64->checksum;
812 	/* push the checksum through our own writer */
813 	if (!base64_writer(c, 3, errors, winfo)) {
814 		return false;
815 	}
816 
817 	return __ops_stacked_write(tail, sz_tail, errors, winfo);
818 }
819 
820 static bool
821 armoured_pubkey_finaliser(__ops_error_t ** errors,
822 			      __ops_writer_info_t * winfo)
823 {
824 	return armoured_finaliser(OPS_PGP_PUBLIC_KEY_BLOCK, errors, winfo);
825 }
826 
827 static bool
828 armoured_private_key_finaliser(__ops_error_t ** errors,
829 			       __ops_writer_info_t * winfo)
830 {
831 	return armoured_finaliser(OPS_PGP_PRIVATE_KEY_BLOCK, errors, winfo);
832 }
833 
834 /* \todo use this for other armoured types */
835 /**
836  \ingroup Core_WritersNext
837  \brief Push Armoured Writer on stack (generic)
838 */
839 void
840 __ops_writer_push_armoured(__ops_createinfo_t * info, __ops_armor_type_t type)
841 {
842 	static char     hdr_pubkey[] =
843 			"-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: "
844 			NETPGP_VERSION_STRING
845 			"\r\n\r\n";
846 	static char     hdr_private_key[] =
847 			"-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: "
848 			NETPGP_VERSION_STRING
849 			"\r\n\r\n";
850 	char           *header = NULL;
851 	unsigned int    sz_hdr = 0;
852 	bool(*finaliser) (__ops_error_t ** errors, __ops_writer_info_t * winfo);
853 	base64_t   *base64;
854 
855 	finaliser = NULL;
856 	switch (type) {
857 	case OPS_PGP_PUBLIC_KEY_BLOCK:
858 		header = hdr_pubkey;
859 		sz_hdr = sizeof(hdr_pubkey) - 1;
860 		finaliser = armoured_pubkey_finaliser;
861 		break;
862 
863 	case OPS_PGP_PRIVATE_KEY_BLOCK:
864 		header = hdr_private_key;
865 		sz_hdr = sizeof(hdr_private_key) - 1;
866 		finaliser = armoured_private_key_finaliser;
867 		break;
868 
869 	default:
870 		(void) fprintf(stderr,
871 			"__ops_writer_push_armoured: unusual type\n");
872 		return;
873 	}
874 
875 	__ops_write(header, sz_hdr, info);
876 
877 	__ops_writer_push(info, linebreak_writer, NULL,
878 			__ops_writer_generic_destroyer,
879 			calloc(1, sizeof(linebreak_t)));
880 
881 	base64 = calloc(1, sizeof(*base64));
882 	base64->checksum = CRC24_INIT;
883 	__ops_writer_push(info, base64_writer, finaliser,
884 			__ops_writer_generic_destroyer, base64);
885 }
886 
887 /**************************************************************************/
888 
889 typedef struct {
890 	__ops_crypt_t    *crypt;
891 	int             free_crypt;
892 }               crypt_t;
893 
894 /*
895  * This writer simply takes plaintext as input,
896  * encrypts it with the given key
897  * and outputs the resulting encrypted text
898  */
899 static bool
900 encrypt_writer(const unsigned char *src,
901 	       unsigned length,
902 	       __ops_error_t ** errors,
903 	       __ops_writer_info_t * winfo)
904 {
905 
906 #define BUFSZ 1024		/* arbitrary number */
907 	unsigned char   encbuf[BUFSZ];
908 	unsigned        remaining = length;
909 	unsigned        done = 0;
910 
911 	crypt_t    *pgp_encrypt = (crypt_t *) __ops_writer_get_arg(winfo);
912 
913 	if (!__ops_is_sa_supported(pgp_encrypt->crypt->alg)) {
914 		(void) fprintf(stderr, "encrypt_writer: not supported\n");
915 		return false;
916 	}
917 
918 	while (remaining) {
919 		unsigned        len = remaining < BUFSZ ? remaining : BUFSZ;
920 		/* memcpy(buf,src,len); // \todo copy needed here? */
921 
922 		pgp_encrypt->crypt->cfb_encrypt(pgp_encrypt->crypt, encbuf,
923 					src + done, len);
924 
925 		if (__ops_get_debug_level(__FILE__)) {
926 			int             i = 0;
927 
928 			fprintf(stderr, "WRITING:\nunencrypted: ");
929 			for (i = 0; i < 16; i++) {
930 				fprintf(stderr, "%2x ", src[done + i]);
931 			}
932 			fprintf(stderr, "\n");
933 			fprintf(stderr, "encrypted:   ");
934 			for (i = 0; i < 16; i++) {
935 				fprintf(stderr, "%2x ", encbuf[i]);
936 			}
937 			fprintf(stderr, "\n");
938 		}
939 		if (!__ops_stacked_write(encbuf, len, errors, winfo)) {
940 			if (__ops_get_debug_level(__FILE__)) {
941 				fprintf(stderr,
942 "encrypted_writer got error from stacked write, returning\n");
943 			}
944 			return false;
945 		}
946 		remaining -= len;
947 		done += len;
948 	}
949 
950 	return true;
951 }
952 
953 static void
954 encrypt_destroyer(__ops_writer_info_t * winfo)
955 {
956 	crypt_t    *pgp_encrypt = (crypt_t *) __ops_writer_get_arg(winfo);
957 
958 	if (pgp_encrypt->free_crypt) {
959 		free(pgp_encrypt->crypt);
960 	}
961 	free(pgp_encrypt);
962 }
963 
964 /**
965 \ingroup Core_WritersNext
966 \brief Push Encrypted Writer onto stack (create SE packets)
967 */
968 void
969 __ops_writer_push_encrypt_crypt(__ops_createinfo_t * cinfo,
970 			      __ops_crypt_t * pgp_crypt)
971 {
972 	/* Create encrypt to be used with this writer */
973 	/* Remember to free this in the destroyer */
974 
975 	crypt_t    *pgp_encrypt = calloc(1, sizeof(*pgp_encrypt));
976 
977 	/* Setup the encrypt */
978 
979 	pgp_encrypt->crypt = pgp_crypt;
980 	pgp_encrypt->free_crypt = 0;
981 
982 	/* And push writer on stack */
983 	__ops_writer_push(cinfo, encrypt_writer, NULL, encrypt_destroyer,
984 			pgp_encrypt);
985 }
986 
987 /**************************************************************************/
988 
989 typedef struct {
990 	__ops_crypt_t    *crypt;
991 }               encrypt_se_ip_t;
992 
993 static bool	encrypt_se_ip_writer(const unsigned char *,
994 		     unsigned,
995 		     __ops_error_t **,
996 		     __ops_writer_info_t *);
997 static void     encrypt_se_ip_destroyer(__ops_writer_info_t *);
998 
999 /* */
1000 
1001 /**
1002 \ingroup Core_WritersNext
1003 \brief Push Encrypted SE IP Writer onto stack
1004 */
1005 void
1006 __ops_writer_push_encrypt_se_ip(__ops_createinfo_t * cinfo,
1007 			      const __ops_keydata_t * pub_key)
1008 {
1009 	__ops_crypt_t    *encrypted;
1010 	unsigned char  *iv = NULL;
1011 
1012 	/* Create se_ip to be used with this writer */
1013 	/* Remember to free this in the destroyer */
1014 	encrypt_se_ip_t *se_ip = calloc(1, sizeof(*se_ip));
1015 
1016 	/* Create and write encrypted PK session key */
1017 	__ops_pk_sesskey_t *encrypted_pk_sesskey;
1018 	encrypted_pk_sesskey = __ops_create_pk_sesskey(pub_key);
1019 	__ops_write_pk_sesskey(cinfo, encrypted_pk_sesskey);
1020 
1021 	/* Setup the se_ip */
1022 	encrypted = calloc(1, sizeof(*encrypted));
1023 	__ops_crypt_any(encrypted,
1024 			encrypted_pk_sesskey->symm_alg);
1025 	iv = calloc(1, encrypted->blocksize);
1026 	encrypted->set_iv(encrypted, iv);
1027 	encrypted->set_key(encrypted, &encrypted_pk_sesskey->key[0]);
1028 	__ops_encrypt_init(encrypted);
1029 
1030 	se_ip->crypt = encrypted;
1031 
1032 	/* And push writer on stack */
1033 	__ops_writer_push(cinfo, encrypt_se_ip_writer, NULL,
1034 			encrypt_se_ip_destroyer, se_ip);
1035 	/* tidy up */
1036 	free(encrypted_pk_sesskey);
1037 	free(iv);
1038 }
1039 
1040 static bool
1041 encrypt_se_ip_writer(const unsigned char *src,
1042 		     unsigned length,
1043 		     __ops_error_t ** errors,
1044 		     __ops_writer_info_t * winfo)
1045 {
1046 	encrypt_se_ip_t *se_ip = __ops_writer_get_arg(winfo);
1047 	bool   rtn = true;
1048 	__ops_memory_t   *mem_literal;
1049 	__ops_createinfo_t *cinfo_literal;
1050 	__ops_memory_t   *mem_compressed;
1051 	__ops_createinfo_t *cinfo_compressed;
1052 	__ops_memory_t   *my_mem;
1053 	__ops_createinfo_t *my_cinfo;
1054 	const unsigned int bufsz = 128;	/* initial value; gets expanded as
1055 					 * necessary */
1056 
1057 	__ops_setup_memory_write(&cinfo_literal, &mem_literal, bufsz);
1058 	__ops_setup_memory_write(&cinfo_compressed, &mem_compressed, bufsz);
1059 	__ops_setup_memory_write(&my_cinfo, &my_mem, bufsz);
1060 
1061 	/* create literal data packet from source data */
1062 	__ops_write_litdata(src, (const int)length,
1063 			OPS_LDT_BINARY, cinfo_literal);
1064 	if (__ops_memory_get_length(mem_literal) <= length) {
1065 		(void) fprintf(stderr, "encrypt_se_ip_writer: bad length\n");
1066 		return false;
1067 	}
1068 
1069 	/* create compressed packet from literal data packet */
1070 	__ops_write_compressed(__ops_memory_get_data(mem_literal),
1071 			     __ops_memory_get_length(mem_literal),
1072 			     cinfo_compressed);
1073 
1074 	/* create SE IP packet set from this compressed literal data */
1075 	__ops_write_se_ip_pktset(__ops_memory_get_data(mem_compressed),
1076 			       __ops_memory_get_length(mem_compressed),
1077 			       se_ip->crypt, my_cinfo);
1078 	if (__ops_memory_get_length(my_mem) <=
1079 			__ops_memory_get_length(mem_compressed)) {
1080 		(void) fprintf(stderr,
1081 				"encrypt_se_ip_writer: bad comp length\n");
1082 		return false;
1083 	}
1084 
1085 	/* now write memory to next writer */
1086 	rtn = __ops_stacked_write(__ops_memory_get_data(my_mem),
1087 				__ops_memory_get_length(my_mem),
1088 				errors, winfo);
1089 
1090 	__ops_memory_free(my_mem);
1091 	__ops_memory_free(mem_compressed);
1092 	__ops_memory_free(mem_literal);
1093 
1094 	return rtn;
1095 }
1096 
1097 static void
1098 encrypt_se_ip_destroyer(__ops_writer_info_t * winfo)
1099 {
1100 	encrypt_se_ip_t *se_ip = __ops_writer_get_arg(winfo);
1101 
1102 	(void) free(se_ip->crypt);
1103 	(void) free(se_ip);
1104 }
1105 
1106 bool
1107 __ops_write_se_ip_pktset(const unsigned char *data,
1108 		       const unsigned int len,
1109 		       __ops_crypt_t * crypted,
1110 		       __ops_createinfo_t * cinfo)
1111 {
1112 	__ops_createinfo_t	*cinfo_mdc;
1113 	__ops_memory_t		*mem_mdc;
1114 	unsigned char		 hashed[OPS_SHA1_HASH_SIZE];
1115 	unsigned char		*preamble;
1116 	const size_t		 sz_mdc = 1 + 1 + OPS_SHA1_HASH_SIZE;
1117 	size_t			 sz_preamble;
1118 	size_t			 sz_buf;
1119 
1120 	sz_preamble = crypted->blocksize + 2;
1121 	preamble = calloc(1, sz_preamble);
1122 	sz_buf = sz_preamble + len + sz_mdc;
1123 
1124 	if (!__ops_write_ptag(OPS_PTAG_CT_SE_IP_DATA, cinfo) ||
1125 	    !__ops_write_length(1 + sz_buf, cinfo) ||
1126 	    !__ops_write_scalar(SE_IP_DATA_VERSION, 1, cinfo)) {
1127 		free(preamble);
1128 		return 0;
1129 	}
1130 	__ops_random(preamble, crypted->blocksize);
1131 	preamble[crypted->blocksize] = preamble[crypted->blocksize - 2];
1132 	preamble[crypted->blocksize + 1] = preamble[crypted->blocksize - 1];
1133 
1134 	if (__ops_get_debug_level(__FILE__)) {
1135 		unsigned int    i = 0;
1136 
1137 		fprintf(stderr, "\npreamble: ");
1138 		for (i = 0; i < sz_preamble; i++) {
1139 			fprintf(stderr, " 0x%02x", preamble[i]);
1140 		}
1141 		fprintf(stderr, "\n");
1142 	}
1143 	/* now construct MDC packet and add to the end of the buffer */
1144 
1145 	__ops_setup_memory_write(&cinfo_mdc, &mem_mdc, sz_mdc);
1146 
1147 	__ops_calc_mdc_hash(preamble, sz_preamble, data, len, &hashed[0]);
1148 
1149 	__ops_write_mdc(hashed, cinfo_mdc);
1150 
1151 	if (__ops_get_debug_level(__FILE__)) {
1152 		unsigned int    i = 0;
1153 		size_t          sz_plaintext = len;
1154 		size_t          sz_mdc2 = 1 + 1 + OPS_SHA1_HASH_SIZE;
1155 		unsigned char  *mdc = NULL;
1156 
1157 		(void) fprintf(stderr, "\nplaintext: ");
1158 		for (i = 0; i < sz_plaintext; i++) {
1159 			(void) fprintf(stderr, " 0x%02x", data[i]);
1160 		}
1161 		(void) fprintf(stderr, "\n");
1162 
1163 		(void) fprintf(stderr, "\nmdc: ");
1164 		mdc = __ops_memory_get_data(mem_mdc);
1165 		for (i = 0; i < sz_mdc2; i++) {
1166 			(void) fprintf(stderr, " 0x%02x", mdc[i]);
1167 		}
1168 		(void) fprintf(stderr, "\n");
1169 	}
1170 	/* and write it out */
1171 
1172 	__ops_writer_push_encrypt_crypt(cinfo, crypted);
1173 
1174 	if (__ops_get_debug_level(__FILE__)) {
1175 		(void) fprintf(stderr,
1176 			"writing %" PRIsize "u + %d + %" PRIsize "u\n",
1177 			sz_preamble, len, __ops_memory_get_length(mem_mdc));
1178 	}
1179 
1180 	if (!__ops_write(preamble, sz_preamble, cinfo) ||
1181 	    !__ops_write(data, len, cinfo) ||
1182 	    !__ops_write(__ops_memory_get_data(mem_mdc),
1183 	    		__ops_memory_get_length(mem_mdc), cinfo)) {
1184 		/* \todo fix cleanup here and in old code functions */
1185 		return 0;
1186 	}
1187 
1188 	__ops_writer_pop(cinfo);
1189 
1190 	/* cleanup  */
1191 	__ops_teardown_memory_write(cinfo_mdc, mem_mdc);
1192 	(void) free(preamble);
1193 
1194 	return 1;
1195 }
1196 
1197 typedef struct {
1198 	int             fd;
1199 }               writer_fd_t;
1200 
1201 static bool
1202 fd_writer(const unsigned char *src, unsigned length,
1203 	  __ops_error_t ** errors,
1204 	  __ops_writer_info_t * winfo)
1205 {
1206 	writer_fd_t *writer = __ops_writer_get_arg(winfo);
1207 	int             n = write(writer->fd, src, length);
1208 
1209 	if (n == -1) {
1210 		OPS_SYSTEM_ERROR_1(errors, OPS_E_W_WRITE_FAILED, "write",
1211 				   "file descriptor %d", writer->fd);
1212 		return false;
1213 	}
1214 	if ((unsigned) n != length) {
1215 		OPS_ERROR_1(errors, OPS_E_W_WRITE_TOO_SHORT,
1216 			    "file descriptor %d", writer->fd);
1217 		return false;
1218 	}
1219 	return true;
1220 }
1221 
1222 static void
1223 writer_fd_destroyer(__ops_writer_info_t * winfo)
1224 {
1225 	free(__ops_writer_get_arg(winfo));
1226 }
1227 
1228 /**
1229  * \ingroup Core_WritersFirst
1230  * \brief Write to a File
1231  *
1232  * Set the writer in info to be a stock writer that writes to a file
1233  * descriptor. If another writer has already been set, then that is
1234  * first destroyed.
1235  *
1236  * \param info The info structure
1237  * \param fd The file descriptor
1238  *
1239  */
1240 
1241 void
1242 __ops_writer_set_fd(__ops_createinfo_t * info, int fd)
1243 {
1244 	writer_fd_t *writer = calloc(1, sizeof(*writer));
1245 
1246 	writer->fd = fd;
1247 	__ops_writer_set(info, fd_writer, NULL, writer_fd_destroyer, writer);
1248 }
1249 
1250 static bool
1251 memory_writer(const unsigned char *src, unsigned length,
1252 	      __ops_error_t ** errors,
1253 	      __ops_writer_info_t * winfo)
1254 {
1255 	__ops_memory_t   *mem = __ops_writer_get_arg(winfo);
1256 
1257 	__OPS_USED(errors);
1258 	__ops_memory_add(mem, src, length);
1259 	return true;
1260 }
1261 
1262 /**
1263  * \ingroup Core_WritersFirst
1264  * \brief Write to memory
1265  *
1266  * Set a memory writer.
1267  *
1268  * \param info The info structure
1269  * \param mem The memory structure
1270  * \note It is the caller's responsiblity to call __ops_memory_free(mem)
1271  * \sa __ops_memory_free()
1272  */
1273 
1274 void
1275 __ops_writer_set_memory(__ops_createinfo_t * info, __ops_memory_t * mem)
1276 {
1277 	__ops_writer_set(info, memory_writer, NULL, NULL, mem);
1278 }
1279 
1280 /**************************************************************************/
1281 
1282 typedef struct {
1283 	__ops_hash_alg_t hash_alg;
1284 	__ops_hash_t      hash;
1285 	unsigned char  *hashed;
1286 }               skey_checksum_t;
1287 
1288 static bool
1289 skey_checksum_writer(const unsigned char *src, const unsigned length, __ops_error_t ** errors, __ops_writer_info_t * winfo)
1290 {
1291 	skey_checksum_t *sum = __ops_writer_get_arg(winfo);
1292 	bool   rtn = true;
1293 
1294 	/* add contents to hash */
1295 	sum->hash.add(&sum->hash, src, length);
1296 
1297 	/* write to next stacked writer */
1298 	rtn = __ops_stacked_write(src, length, errors, winfo);
1299 
1300 	/* tidy up and return */
1301 	return rtn;
1302 }
1303 
1304 static bool
1305 skey_checksum_finaliser(__ops_error_t **errors, __ops_writer_info_t * winfo)
1306 {
1307 	skey_checksum_t *sum = __ops_writer_get_arg(winfo);
1308 
1309 	if (errors) {
1310 		printf("errors in skey_checksum_finaliser\n");
1311 	}
1312 	sum->hash.finish(&sum->hash, sum->hashed);
1313 	return true;
1314 }
1315 
1316 static void
1317 skey_checksum_destroyer(__ops_writer_info_t * winfo)
1318 {
1319 	skey_checksum_t *sum = __ops_writer_get_arg(winfo);
1320 
1321 	free(sum);
1322 }
1323 
1324 /**
1325 \ingroup Core_WritersNext
1326 \param cinfo
1327 \param seckey
1328 */
1329 void
1330 __ops_push_skey_checksum_writer(__ops_createinfo_t *cinfo,
1331 		__ops_seckey_t *seckey)
1332 {
1333 	/* XXX: push a SHA-1 checksum writer (and change s2k to 254). */
1334 	skey_checksum_t *sum = calloc(1, sizeof(*sum));
1335 
1336 	/* configure the arg */
1337 	sum->hash_alg = seckey->hash_alg;
1338 	sum->hashed = &seckey->checkhash[0];
1339 
1340 	/* init the hash */
1341 	__ops_hash_any(&sum->hash, sum->hash_alg);
1342 	sum->hash.init(&sum->hash);
1343 
1344 	__ops_writer_push(cinfo, skey_checksum_writer,
1345 		skey_checksum_finaliser, skey_checksum_destroyer, sum);
1346 }
1347 
1348 /**************************************************************************/
1349 
1350 #define MAX_PARTIAL_DATA_LENGTH 1073741824
1351 
1352 typedef struct {
1353 	__ops_crypt_t    *crypt;
1354 	__ops_memory_t   *mem_data;
1355 	__ops_memory_t   *mem_literal;
1356 	__ops_createinfo_t *cinfo_literal;
1357 	__ops_memory_t   *mem_se_ip;
1358 	__ops_createinfo_t *cinfo_se_ip;
1359 	__ops_hash_t      hash;
1360 }               str_enc_se_ip_t;
1361 
1362 
1363 static bool
1364 str_enc_se_ip_writer(const unsigned char *src,
1365 			    unsigned length,
1366 			    __ops_error_t ** errors,
1367 			    __ops_writer_info_t * winfo);
1368 
1369 static bool
1370 str_enc_se_ip_finaliser(__ops_error_t ** errors,
1371 			       __ops_writer_info_t * winfo);
1372 
1373 static void     str_enc_se_ip_destroyer(__ops_writer_info_t * winfo);
1374 
1375 /* */
1376 
1377 /**
1378 \ingroup Core_WritersNext
1379 \param cinfo
1380 \param pub_key
1381 */
1382 void
1383 __ops_push_stream_enc_se_ip(__ops_createinfo_t * cinfo,
1384 				     const __ops_keydata_t * pub_key)
1385 {
1386 	__ops_crypt_t    *encrypted;
1387 	unsigned char  *iv = NULL;
1388 	const unsigned int bufsz = 1024;
1389 	/* Create arg to be used with this writer */
1390 	/* Remember to free this in the destroyer */
1391 	str_enc_se_ip_t *se_ip = calloc(1, sizeof(*se_ip));
1392 	/* Create and write encrypted PK session key */
1393 	__ops_pk_sesskey_t *encrypted_pk_sesskey;
1394 
1395 	encrypted_pk_sesskey = __ops_create_pk_sesskey(pub_key);
1396 	__ops_write_pk_sesskey(cinfo, encrypted_pk_sesskey);
1397 
1398 	/* Setup the se_ip */
1399 	encrypted = calloc(1, sizeof(*encrypted));
1400 	__ops_crypt_any(encrypted,
1401 			encrypted_pk_sesskey->symm_alg);
1402 	iv = calloc(1, encrypted->blocksize);
1403 	encrypted->set_iv(encrypted, iv);
1404 	encrypted->set_key(encrypted, &encrypted_pk_sesskey->key[0]);
1405 	__ops_encrypt_init(encrypted);
1406 
1407 	se_ip->crypt = encrypted;
1408 
1409 	se_ip->mem_data = __ops_memory_new();
1410 	__ops_memory_init(se_ip->mem_data, bufsz);
1411 
1412 	se_ip->mem_literal = NULL;
1413 	se_ip->cinfo_literal = NULL;
1414 
1415 	__ops_setup_memory_write(&se_ip->cinfo_se_ip, &se_ip->mem_se_ip,
1416 			bufsz);
1417 
1418 	/* And push writer on stack */
1419 	__ops_writer_push(cinfo,
1420 			str_enc_se_ip_writer,
1421 			str_enc_se_ip_finaliser,
1422 			str_enc_se_ip_destroyer, se_ip);
1423 	/* tidy up */
1424 	free(encrypted_pk_sesskey);
1425 	free(iv);
1426 }
1427 
1428 
1429 static unsigned int
1430 __ops_calc_partial_data_length(unsigned int len)
1431 {
1432 	int             i;
1433 	unsigned int    mask = MAX_PARTIAL_DATA_LENGTH;
1434 
1435 	if (len == 0) {
1436 		(void) fprintf(stderr,
1437 				"__ops_calc_partial_data_length: 0 len\n");
1438 		return 0;
1439 	}
1440 	if (len > MAX_PARTIAL_DATA_LENGTH) {
1441 		return MAX_PARTIAL_DATA_LENGTH;
1442 	}
1443 	for (i = 0; i <= 30; i++) {
1444 		if (mask & len) {
1445 			break;
1446 		}
1447 		mask >>= 1;
1448 	}
1449 
1450 	return mask;
1451 }
1452 
1453 static bool
1454 __ops_write_partial_data_length(unsigned int len,
1455 			      __ops_createinfo_t * info)
1456 {
1457 	/* len must be a power of 2 from 0 to 30 */
1458 	int             i;
1459 	unsigned char   c[1];
1460 
1461 	for (i = 0; i <= 30; i++) {
1462 		if ((len >> i) & 1) {
1463 			break;
1464 		}
1465 	}
1466 
1467 	c[0] = 224 + i;
1468 	return __ops_write(c, 1, info);
1469 }
1470 
1471 static bool
1472 __ops_stream_write_litdata(const unsigned char *data,
1473 			      unsigned int len,
1474 			      __ops_createinfo_t * info)
1475 {
1476 	while (len > 0) {
1477 		size_t          pdlen = __ops_calc_partial_data_length(len);
1478 
1479 		__ops_write_partial_data_length(pdlen, info);
1480 		__ops_write(data, pdlen, info);
1481 		data += pdlen;
1482 		len -= pdlen;
1483 	}
1484 	return true;
1485 }
1486 
1487 static          bool
1488 __ops_stream_write_litdata_first(const unsigned char *data,
1489 				    unsigned int len,
1490 				    const __ops_litdata_type_t type,
1491 				    __ops_createinfo_t * info)
1492 {
1493 	/* \todo add filename  */
1494 	/* \todo add date */
1495 	/* \todo do we need to check text data for <cr><lf> line endings ? */
1496 
1497 	size_t          sz_towrite = 1 + 1 + 4 + len;
1498 	size_t          sz_pd = __ops_calc_partial_data_length(sz_towrite);
1499 
1500 	if (sz_pd < 512) {
1501 		(void) fprintf(stderr,
1502 			"__ops_stream_write_litdata_first: bad sz_pd\n");
1503 		return false;
1504 	}
1505 
1506 	__ops_write_ptag(OPS_PTAG_CT_LITERAL_DATA, info);
1507 	__ops_write_partial_data_length(sz_pd, info);
1508 	__ops_write_scalar((unsigned)type, 1, info);
1509 	__ops_write_scalar(0, 1, info);
1510 	__ops_write_scalar(0, 4, info);
1511 	__ops_write(data, sz_pd - 6, info);
1512 
1513 	data += (sz_pd - 6);
1514 	sz_towrite -= sz_pd;
1515 
1516 	__ops_stream_write_litdata(data, sz_towrite, info);
1517 	return true;
1518 }
1519 
1520 static          bool
1521 __ops_stream_write_litdata_last(const unsigned char *data,
1522 				   unsigned int len,
1523 				   __ops_createinfo_t * info)
1524 {
1525 	__ops_write_length(len, info);
1526 	__ops_write(data, len, info);
1527 	return true;
1528 }
1529 
1530 static          bool
1531 __ops_stream_write_se_ip(const unsigned char *data,
1532 		       unsigned int len,
1533 		       str_enc_se_ip_t * se_ip,
1534 		       __ops_createinfo_t * cinfo)
1535 {
1536 	size_t          pdlen;
1537 
1538 	while (len > 0) {
1539 		pdlen = __ops_calc_partial_data_length(len);
1540 		__ops_write_partial_data_length(pdlen, cinfo);
1541 
1542 		__ops_writer_push_encrypt_crypt(cinfo, se_ip->crypt);
1543 		__ops_write(data, pdlen, cinfo);
1544 		__ops_writer_pop(cinfo);
1545 
1546 		se_ip->hash.add(&se_ip->hash, data, pdlen);
1547 
1548 		data += pdlen;
1549 		len -= pdlen;
1550 	}
1551 	return true;
1552 }
1553 
1554 static          bool
1555 __ops_stream_write_se_ip_first(const unsigned char *data,
1556 			     unsigned int len,
1557 			     str_enc_se_ip_t * se_ip,
1558 			     __ops_createinfo_t * cinfo)
1559 {
1560 	size_t          sz_preamble = se_ip->crypt->blocksize + 2;
1561 	size_t          sz_towrite = sz_preamble + 1 + len;
1562 	unsigned char  *preamble = calloc(1, sz_preamble);
1563 	size_t          sz_pd = __ops_calc_partial_data_length(sz_towrite);
1564 
1565 	if (sz_pd < 512) {
1566 		(void) fprintf(stderr,
1567 			"__ops_stream_write_se_ip_first: bad sz_pd\n");
1568 		return false;
1569 	}
1570 
1571 	__ops_write_ptag(OPS_PTAG_CT_SE_IP_DATA, cinfo);
1572 	__ops_write_partial_data_length(sz_pd, cinfo);
1573 	__ops_write_scalar(SE_IP_DATA_VERSION, 1, cinfo);
1574 
1575 	__ops_writer_push_encrypt_crypt(cinfo, se_ip->crypt);
1576 
1577 	__ops_random(preamble, se_ip->crypt->blocksize);
1578 	preamble[se_ip->crypt->blocksize] =
1579 			preamble[se_ip->crypt->blocksize - 2];
1580 	preamble[se_ip->crypt->blocksize + 1] =
1581 			preamble[se_ip->crypt->blocksize - 1];
1582 
1583 	__ops_hash_any(&se_ip->hash, OPS_HASH_SHA1);
1584 	se_ip->hash.init(&se_ip->hash);
1585 
1586 	__ops_write(preamble, sz_preamble, cinfo);
1587 	se_ip->hash.add(&se_ip->hash, preamble, sz_preamble);
1588 
1589 	__ops_write(data, sz_pd - sz_preamble - 1, cinfo);
1590 	se_ip->hash.add(&se_ip->hash, data, sz_pd - sz_preamble - 1);
1591 
1592 	data += (sz_pd - sz_preamble - 1);
1593 	sz_towrite -= sz_pd;
1594 
1595 	__ops_writer_pop(cinfo);
1596 
1597 	__ops_stream_write_se_ip(data, sz_towrite, se_ip, cinfo);
1598 
1599 	free(preamble);
1600 
1601 	return true;
1602 }
1603 
1604 static          bool
1605 __ops_stream_write_se_ip_last(const unsigned char *data,
1606 			    unsigned int len,
1607 			    str_enc_se_ip_t * se_ip,
1608 			    __ops_createinfo_t * cinfo)
1609 {
1610 	__ops_createinfo_t	*cinfo_mdc;
1611 	__ops_memory_t		*mem_mdc;
1612 	unsigned char		 c[1];
1613 	unsigned char		 hashed[OPS_SHA1_HASH_SIZE];
1614 	const size_t		 sz_mdc = 1 + 1 + OPS_SHA1_HASH_SIZE;
1615 	size_t			 sz_buf = len + sz_mdc;
1616 
1617 	se_ip->hash.add(&se_ip->hash, data, len);
1618 
1619 	/* MDC packet tag */
1620 	c[0] = 0xD3;
1621 	se_ip->hash.add(&se_ip->hash, &c[0], 1);
1622 
1623 	/* MDC packet len */
1624 	c[0] = OPS_SHA1_HASH_SIZE;
1625 	se_ip->hash.add(&se_ip->hash, &c[0], 1);
1626 
1627 	/* finish */
1628 	se_ip->hash.finish(&se_ip->hash, hashed);
1629 
1630 	__ops_setup_memory_write(&cinfo_mdc, &mem_mdc, sz_mdc);
1631 	__ops_write_mdc(hashed, cinfo_mdc);
1632 
1633 	/* write length of last se_ip chunk */
1634 	__ops_write_length(sz_buf, cinfo);
1635 
1636 	/* encode everting */
1637 	__ops_writer_push_encrypt_crypt(cinfo, se_ip->crypt);
1638 
1639 	__ops_write(data, len, cinfo);
1640 	__ops_write(__ops_memory_get_data(mem_mdc),
1641 			__ops_memory_get_length(mem_mdc), cinfo);
1642 
1643 	__ops_writer_pop(cinfo);
1644 
1645 	__ops_teardown_memory_write(cinfo_mdc, mem_mdc);
1646 
1647 	return true;
1648 }
1649 
1650 static bool
1651 str_enc_se_ip_writer(const unsigned char *src,
1652 			    unsigned length,
1653 			    __ops_error_t ** errors,
1654 			    __ops_writer_info_t * winfo)
1655 {
1656 	str_enc_se_ip_t *se_ip = __ops_writer_get_arg(winfo);
1657 	bool   rtn = true;
1658 
1659 	if (se_ip->cinfo_literal == NULL) {	/* first literal data chunk
1660 						 * is not yet written */
1661 		size_t          datalength;
1662 
1663 		__ops_memory_add(se_ip->mem_data, src, length);
1664 		datalength = __ops_memory_get_length(se_ip->mem_data);
1665 
1666 		/* 4.2.2.4. Partial Body Lengths */
1667 		/* The first partial length MUST be at least 512 octets long. */
1668 		if (datalength < 512) {
1669 			return true;	/* will wait for more data or
1670 						 * end of stream             */
1671 		}
1672 		__ops_setup_memory_write(&se_ip->cinfo_literal,
1673 				&se_ip->mem_literal, datalength + 32);
1674 		__ops_stream_write_litdata_first(
1675 				__ops_memory_get_data(se_ip->mem_data),
1676 				datalength,
1677 				OPS_LDT_BINARY,
1678 				se_ip->cinfo_literal);
1679 
1680 		__ops_stream_write_se_ip_first(
1681 				__ops_memory_get_data(se_ip->mem_literal),
1682 				__ops_memory_get_length(se_ip->mem_literal),
1683 				se_ip, se_ip->cinfo_se_ip);
1684 	} else {
1685 		__ops_stream_write_litdata(src, length, se_ip->cinfo_literal);
1686 		__ops_stream_write_se_ip(
1687 				__ops_memory_get_data(se_ip->mem_literal),
1688 				__ops_memory_get_length(se_ip->mem_literal),
1689 				se_ip, se_ip->cinfo_se_ip);
1690 	}
1691 
1692 	/* now write memory to next writer */
1693 	rtn = __ops_stacked_write(__ops_memory_get_data(se_ip->mem_se_ip),
1694 				__ops_memory_get_length(se_ip->mem_se_ip),
1695 				errors, winfo);
1696 
1697 	__ops_memory_clear(se_ip->mem_literal);
1698 	__ops_memory_clear(se_ip->mem_se_ip);
1699 
1700 	return rtn;
1701 }
1702 
1703 static bool
1704 str_enc_se_ip_finaliser(__ops_error_t ** errors,
1705 			       __ops_writer_info_t * winfo)
1706 {
1707 	str_enc_se_ip_t *se_ip = __ops_writer_get_arg(winfo);
1708 	/* write last chunk of data */
1709 
1710 	if (se_ip->cinfo_literal == NULL) {
1711 		/* first literal data chunk was not written */
1712 		/* so we know the total length of data, write a simple packet */
1713 
1714 		/* create literal data packet from buffered data */
1715 		__ops_setup_memory_write(&se_ip->cinfo_literal,
1716 				       &se_ip->mem_literal,
1717 				 __ops_memory_get_length(se_ip->mem_data) + 32);
1718 
1719 		__ops_write_litdata(__ops_memory_get_data(se_ip->mem_data),
1720 			(const int)__ops_memory_get_length(se_ip->mem_data),
1721 			OPS_LDT_BINARY,
1722 			se_ip->cinfo_literal);
1723 
1724 		/* create SE IP packet set from this literal data */
1725 		__ops_write_se_ip_pktset(
1726 				__ops_memory_get_data(se_ip->mem_literal),
1727 				__ops_memory_get_length(se_ip->mem_literal),
1728 				se_ip->crypt, se_ip->cinfo_se_ip);
1729 
1730 	} else {
1731 		/* finish writing */
1732 		__ops_stream_write_litdata_last(NULL, 0, se_ip->cinfo_literal);
1733 		__ops_stream_write_se_ip_last(
1734 				__ops_memory_get_data(se_ip->mem_literal),
1735 				__ops_memory_get_length(se_ip->mem_literal),
1736 				se_ip, se_ip->cinfo_se_ip);
1737 	}
1738 
1739 	/* now write memory to next writer */
1740 	return __ops_stacked_write(__ops_memory_get_data(se_ip->mem_se_ip),
1741 				 __ops_memory_get_length(se_ip->mem_se_ip),
1742 				 errors, winfo);
1743 }
1744 
1745 static void
1746 str_enc_se_ip_destroyer(__ops_writer_info_t * winfo)
1747 {
1748 	str_enc_se_ip_t *se_ip = __ops_writer_get_arg(winfo);
1749 
1750 	__ops_memory_free(se_ip->mem_data);
1751 	__ops_teardown_memory_write(se_ip->cinfo_literal, se_ip->mem_literal);
1752 	__ops_teardown_memory_write(se_ip->cinfo_se_ip, se_ip->mem_se_ip);
1753 
1754 	se_ip->crypt->decrypt_finish(se_ip->crypt);
1755 
1756 	(void) free(se_ip->crypt);
1757 	(void) free(se_ip);
1758 }
1759