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