xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/writer.c (revision 3816d47b2c42fcd6e549e3407f842a5b1a1d23ad)
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.18 2009/11/20 15:23:37 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 		if (linebreak->pos == BREAKPOS) {
658 			if (!__ops_stacked_write("\r\n", 2, errors, writer)) {
659 				return 0;
660 			}
661 			linebreak->pos = 0;
662 		}
663 		if (!__ops_stacked_write(&src[n], 1, errors, writer)) {
664 			return 0;
665 		}
666 	}
667 
668 	return 1;
669 }
670 
671 /**
672  * \ingroup Core_WritersNext
673  * \brief Push armoured signature on stack
674  * \param output
675  */
676 unsigned
677 __ops_writer_use_armored_sig(__ops_output_t *output)
678 {
679 	static const char     header[] =
680 			"\r\n-----BEGIN PGP SIGNATURE-----\r\nVersion: "
681 			NETPGP_VERSION_STRING
682 			"\r\n\r\n";
683 	linebreak_t	*linebreak;
684 	base64_t   	*base64;
685 
686 	__ops_writer_pop(output);
687 	if (__ops_write(output, header, sizeof(header) - 1) == 0) {
688 		OPS_ERROR(&output->errors, OPS_E_W,
689 			"Error switching to armoured signature");
690 		return 0;
691 	}
692 	if ((linebreak = calloc(1, sizeof(*linebreak))) == NULL) {
693 		OPS_ERROR(&output->errors, OPS_E_W,
694 			"__ops_writer_use_armored_sig: Bad alloc");
695 		return 0;
696 	}
697 	__ops_writer_push(output, linebreak_writer, NULL,
698 			generic_destroyer,
699 			linebreak);
700 	base64 = calloc(1, sizeof(*base64));
701 	if (!base64) {
702 		OPS_MEMORY_ERROR(&output->errors);
703 		return 0;
704 	}
705 	base64->checksum = CRC24_INIT;
706 	__ops_writer_push(output, base64_writer, sig_finaliser,
707 			generic_destroyer, base64);
708 	return 1;
709 }
710 
711 static unsigned
712 armoured_message_finaliser(__ops_error_t **errors, __ops_writer_t *writer)
713 {
714 	/* TODO: This is same as sig_finaliser apart from trailer. */
715 	static const char	*trailer = "\r\n-----END PGP MESSAGE-----\r\n";
716 	unsigned char		 c[3];
717 	base64_t		*base64;
718 
719 	base64 = __ops_writer_get_arg(writer);
720 	if (base64->pos) {
721 		if (!__ops_stacked_write(&b64map[base64->t], 1, errors,
722 				writer)) {
723 			return 0;
724 		}
725 		if (base64->pos == 1 &&
726 		    !__ops_stacked_write("==", 2, errors, writer)) {
727 			return 0;
728 		}
729 		if (base64->pos == 2 &&
730 		    !__ops_stacked_write("=", 1, errors, writer)) {
731 			return 0;
732 		}
733 	}
734 	/* Ready for the checksum */
735 	if (!__ops_stacked_write("\r\n=", 3, errors, writer)) {
736 		return 0;
737 	}
738 
739 	base64->pos = 0;		/* get ready to write the checksum */
740 
741 	c[0] = base64->checksum >> 16;
742 	c[1] = base64->checksum >> 8;
743 	c[2] = base64->checksum;
744 	/* push the checksum through our own writer */
745 	if (!base64_writer(c, 3, errors, writer)) {
746 		return 0;
747 	}
748 
749 	return __ops_stacked_write(trailer, strlen(trailer), errors, writer);
750 }
751 
752 /**
753  \ingroup Core_WritersNext
754  \brief Write a PGP MESSAGE
755  \todo replace with generic function
756 */
757 void
758 __ops_writer_push_armor_msg(__ops_output_t *output)
759 {
760 	static const char	 header[] = "-----BEGIN PGP MESSAGE-----\r\n";
761 	linebreak_t		*linebreak;
762 	base64_t		*base64;
763 
764 	__ops_write(output, header, sizeof(header) - 1);
765 	__ops_write(output, "\r\n", 2);
766 	if ((linebreak = calloc(1, sizeof(*linebreak))) == NULL) {
767 		(void) fprintf(stderr,
768 			"__ops_writer_push_armor_msg: bad lb alloc\n");
769 		return;
770 	}
771 	__ops_writer_push(output, linebreak_writer, NULL,
772 		generic_destroyer,
773 		linebreak);
774 	if ((base64 = calloc(1, sizeof(*base64))) == NULL) {
775 		(void) fprintf(stderr,
776 			"__ops_writer_push_armor_msg: bad alloc\n");
777 		return;
778 	}
779 	base64->checksum = CRC24_INIT;
780 	__ops_writer_push(output, base64_writer,
781 		armoured_message_finaliser, generic_destroyer,
782 		base64);
783 }
784 
785 static unsigned
786 armoured_finaliser(__ops_armor_type_t type,
787 			__ops_error_t **errors,
788 			__ops_writer_t *writer)
789 {
790 	static const char     tail_pubkey[] =
791 			"\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n";
792 	static const char     tail_private_key[] =
793 			"\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n";
794 	unsigned char		 c[3];
795 	unsigned int		 sz_tail = 0;
796 	const char		*tail = NULL;
797 	base64_t		*base64;
798 
799 	switch (type) {
800 	case OPS_PGP_PUBLIC_KEY_BLOCK:
801 		tail = tail_pubkey;
802 		sz_tail = sizeof(tail_pubkey) - 1;
803 		break;
804 
805 	case OPS_PGP_PRIVATE_KEY_BLOCK:
806 		tail = tail_private_key;
807 		sz_tail = sizeof(tail_private_key) - 1;
808 		break;
809 
810 	default:
811 		(void) fprintf(stderr, "armoured_finaliser: unusual type\n");
812 		return 0;
813 	}
814 	base64 = __ops_writer_get_arg(writer);
815 	if (base64->pos) {
816 		if (!__ops_stacked_write(&b64map[base64->t], 1, errors,
817 				writer)) {
818 			return 0;
819 		}
820 		if (base64->pos == 1 && !__ops_stacked_write("==", 2, errors,
821 				writer)) {
822 			return 0;
823 		}
824 		if (base64->pos == 2 && !__ops_stacked_write("=", 1, errors,
825 				writer)) {
826 			return 0;
827 		}
828 	}
829 	/* Ready for the checksum */
830 	if (!__ops_stacked_write("\r\n=", 3, errors, writer)) {
831 		return 0;
832 	}
833 	base64->pos = 0;		/* get ready to write the checksum */
834 	c[0] = base64->checksum >> 16;
835 	c[1] = base64->checksum >> 8;
836 	c[2] = base64->checksum;
837 	/* push the checksum through our own writer */
838 	if (!base64_writer(c, 3, errors, writer)) {
839 		return 0;
840 	}
841 	return __ops_stacked_write(tail, sz_tail, errors, writer);
842 }
843 
844 static unsigned
845 armored_pubkey_fini(__ops_error_t **errors, __ops_writer_t *writer)
846 {
847 	return armoured_finaliser(OPS_PGP_PUBLIC_KEY_BLOCK, errors, writer);
848 }
849 
850 static unsigned
851 armored_privkey_fini(__ops_error_t **errors, __ops_writer_t *writer)
852 {
853 	return armoured_finaliser(OPS_PGP_PRIVATE_KEY_BLOCK, errors, writer);
854 }
855 
856 /* \todo use this for other armoured types */
857 /**
858  \ingroup Core_WritersNext
859  \brief Push Armoured Writer on stack (generic)
860 */
861 void
862 __ops_writer_push_armoured(__ops_output_t *output, __ops_armor_type_t type)
863 {
864 	static char     hdr_pubkey[] =
865 			"-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: "
866 			NETPGP_VERSION_STRING
867 			"\r\n\r\n";
868 	static char     hdr_private_key[] =
869 			"-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: "
870 			NETPGP_VERSION_STRING
871 			"\r\n\r\n";
872 	unsigned int    sz_hdr = 0;
873 	unsigned	(*finaliser) (__ops_error_t **, __ops_writer_t *);
874 	base64_t	*base64;
875 	linebreak_t	*linebreak;
876 	char           *header = NULL;
877 
878 	finaliser = NULL;
879 	switch (type) {
880 	case OPS_PGP_PUBLIC_KEY_BLOCK:
881 		header = hdr_pubkey;
882 		sz_hdr = sizeof(hdr_pubkey) - 1;
883 		finaliser = armored_pubkey_fini;
884 		break;
885 
886 	case OPS_PGP_PRIVATE_KEY_BLOCK:
887 		header = hdr_private_key;
888 		sz_hdr = sizeof(hdr_private_key) - 1;
889 		finaliser = armored_privkey_fini;
890 		break;
891 
892 	default:
893 		(void) fprintf(stderr,
894 			"__ops_writer_push_armoured: unusual type\n");
895 		return;
896 	}
897 	if ((linebreak = calloc(1, sizeof(*linebreak))) == NULL) {
898 		(void) fprintf(stderr,
899 			"__ops_writer_push_armoured: bad alloc\n");
900 		return;
901 	}
902 	__ops_write(output, header, sz_hdr);
903 	__ops_writer_push(output, linebreak_writer, NULL,
904 			generic_destroyer,
905 			linebreak);
906 	if ((base64 = calloc(1, sizeof(*base64))) == NULL) {
907 		(void) fprintf(stderr,
908 			"__ops_writer_push_armoured: bad alloc\n");
909 		return;
910 	}
911 	base64->checksum = CRC24_INIT;
912 	__ops_writer_push(output, base64_writer, finaliser,
913 			generic_destroyer, base64);
914 }
915 
916 /**************************************************************************/
917 
918 typedef struct {
919 	__ops_crypt_t    *crypt;
920 	int             free_crypt;
921 } crypt_t;
922 
923 /*
924  * This writer simply takes plaintext as input,
925  * encrypts it with the given key
926  * and outputs the resulting encrypted text
927  */
928 static unsigned
929 encrypt_writer(const unsigned char *src,
930 	       unsigned len,
931 	       __ops_error_t **errors,
932 	       __ops_writer_t *writer)
933 {
934 #define BUFSZ 1024		/* arbitrary number */
935 	unsigned char   encbuf[BUFSZ];
936 	unsigned        remaining;
937 	unsigned        done = 0;
938 	crypt_t		*pgp_encrypt;
939 
940 	remaining = len;
941 	pgp_encrypt = (crypt_t *) __ops_writer_get_arg(writer);
942 	if (!__ops_is_sa_supported(pgp_encrypt->crypt->alg)) {
943 		(void) fprintf(stderr, "encrypt_writer: not supported\n");
944 		return 0;
945 	}
946 	while (remaining > 0) {
947 		unsigned        size = (remaining < BUFSZ) ? remaining : BUFSZ;
948 
949 		/* memcpy(buf,src,size); // \todo copy needed here? */
950 		pgp_encrypt->crypt->cfb_encrypt(pgp_encrypt->crypt, encbuf,
951 					src + done, size);
952 
953 		if (__ops_get_debug_level(__FILE__)) {
954 			int             i = 0;
955 
956 			(void) fprintf(stderr, "WRITING:\nunencrypted: ");
957 			for (i = 0; i < 16; i++) {
958 				(void) fprintf(stderr, "%2x ", src[done + i]);
959 			}
960 			(void) fprintf(stderr, "\nencrypted:   ");
961 			for (i = 0; i < 16; i++) {
962 				(void) fprintf(stderr, "%2x ", encbuf[i]);
963 			}
964 			(void) fprintf(stderr, "\n");
965 		}
966 		if (!__ops_stacked_write(encbuf, size, errors, writer)) {
967 			if (__ops_get_debug_level(__FILE__)) {
968 				fprintf(stderr,
969 					"encrypted_writer: stacked write\n");
970 			}
971 			return 0;
972 		}
973 		remaining -= size;
974 		done += size;
975 	}
976 
977 	return 1;
978 }
979 
980 static void
981 encrypt_destroyer(__ops_writer_t *writer)
982 {
983 	crypt_t    *pgp_encrypt = (crypt_t *) __ops_writer_get_arg(writer);
984 
985 	if (pgp_encrypt->free_crypt) {
986 		free(pgp_encrypt->crypt);
987 	}
988 	free(pgp_encrypt);
989 }
990 
991 /**
992 \ingroup Core_WritersNext
993 \brief Push Encrypted Writer onto stack (create SE packets)
994 */
995 void
996 __ops_push_enc_crypt(__ops_output_t *output, __ops_crypt_t *pgp_crypt)
997 {
998 	/* Create encrypt to be used with this writer */
999 	/* Remember to free this in the destroyer */
1000 	crypt_t    *pgp_encrypt;
1001 
1002 	if ((pgp_encrypt = calloc(1, sizeof(*pgp_encrypt))) == NULL) {
1003 		(void) fprintf(stderr, "__ops_push_enc_crypt: bad alloc\n");
1004 	} else {
1005 		/* Setup the encrypt */
1006 		pgp_encrypt->crypt = pgp_crypt;
1007 		pgp_encrypt->free_crypt = 0;
1008 		/* And push writer on stack */
1009 		__ops_writer_push(output, encrypt_writer, NULL,
1010 			encrypt_destroyer, pgp_encrypt);
1011 	}
1012 }
1013 
1014 /**************************************************************************/
1015 
1016 typedef struct {
1017 	__ops_crypt_t    *crypt;
1018 } encrypt_se_ip_t;
1019 
1020 static unsigned	encrypt_se_ip_writer(const unsigned char *,
1021 		     unsigned,
1022 		     __ops_error_t **,
1023 		     __ops_writer_t *);
1024 static void     encrypt_se_ip_destroyer(__ops_writer_t *);
1025 
1026 /* */
1027 
1028 /**
1029 \ingroup Core_WritersNext
1030 \brief Push Encrypted SE IP Writer onto stack
1031 */
1032 void
1033 __ops_push_enc_se_ip(__ops_output_t *output, const __ops_key_t *pubkey)
1034 {
1035 	unsigned char	*iv;
1036 	__ops_crypt_t	*encrypted;
1037 	/* Create se_ip to be used with this writer */
1038 	/* Remember to free this in the destroyer */
1039 	encrypt_se_ip_t *se_ip;
1040 	__ops_pk_sesskey_t *encrypted_pk_sesskey;
1041 
1042 	if ((se_ip = calloc(1, sizeof(*se_ip))) == NULL) {
1043 		(void) fprintf(stderr, "__ops_push_enc_se_ip: bad alloc\n");
1044 		return;
1045 	}
1046 
1047 	/* Create and write encrypted PK session key */
1048 	encrypted_pk_sesskey = __ops_create_pk_sesskey(pubkey);
1049 	__ops_write_pk_sesskey(output, encrypted_pk_sesskey);
1050 
1051 	/* Setup the se_ip */
1052 	if ((encrypted = calloc(1, sizeof(*encrypted))) == NULL) {
1053 		free(se_ip);
1054 		(void) fprintf(stderr, "__ops_push_enc_se_ip: bad alloc\n");
1055 		return;
1056 	}
1057 	__ops_crypt_any(encrypted, encrypted_pk_sesskey->symm_alg);
1058 	if ((iv = calloc(1, encrypted->blocksize)) == NULL) {
1059 		free(se_ip);
1060 		free(encrypted);
1061 		(void) fprintf(stderr, "__ops_push_enc_se_ip: bad alloc\n");
1062 		return;
1063 	}
1064 	encrypted->set_iv(encrypted, iv);
1065 	encrypted->set_crypt_key(encrypted, &encrypted_pk_sesskey->key[0]);
1066 	__ops_encrypt_init(encrypted);
1067 
1068 	se_ip->crypt = encrypted;
1069 
1070 	/* And push writer on stack */
1071 	__ops_writer_push(output, encrypt_se_ip_writer, NULL,
1072 			encrypt_se_ip_destroyer, se_ip);
1073 	/* tidy up */
1074 	free(encrypted_pk_sesskey);
1075 	free(iv);
1076 }
1077 
1078 static unsigned
1079 encrypt_se_ip_writer(const unsigned char *src,
1080 		     unsigned len,
1081 		     __ops_error_t **errors,
1082 		     __ops_writer_t *writer)
1083 {
1084 	const unsigned int	 bufsz = 128;
1085 	encrypt_se_ip_t		*se_ip = __ops_writer_get_arg(writer);
1086 	__ops_output_t		*litoutput;
1087 	__ops_output_t		*zoutput;
1088 	__ops_output_t		*output;
1089 	__ops_memory_t		*litmem;
1090 	__ops_memory_t		*zmem;
1091 	__ops_memory_t		*localmem;
1092 	unsigned		 ret = 1;
1093 
1094 	__ops_setup_memory_write(&litoutput, &litmem, bufsz);
1095 	__ops_setup_memory_write(&zoutput, &zmem, bufsz);
1096 	__ops_setup_memory_write(&output, &localmem, bufsz);
1097 
1098 	/* create literal data packet from source data */
1099 	__ops_write_litdata(litoutput, src, (const int)len, OPS_LDT_BINARY);
1100 	if (__ops_mem_len(litmem) <= len) {
1101 		(void) fprintf(stderr, "encrypt_se_ip_writer: bad len\n");
1102 		return 0;
1103 	}
1104 
1105 	/* create compressed packet from literal data packet */
1106 	__ops_writez(__ops_mem_data(litmem), __ops_mem_len(litmem), zoutput);
1107 
1108 	/* create SE IP packet set from this compressed literal data */
1109 	__ops_write_se_ip_pktset(__ops_mem_data(zmem),
1110 			       __ops_mem_len(zmem),
1111 			       se_ip->crypt, output);
1112 	if (__ops_mem_len(localmem) <= __ops_mem_len(zmem)) {
1113 		(void) fprintf(stderr,
1114 				"encrypt_se_ip_writer: bad comp len\n");
1115 		return 0;
1116 	}
1117 
1118 	/* now write memory to next writer */
1119 	ret = __ops_stacked_write(__ops_mem_data(localmem),
1120 				__ops_mem_len(localmem),
1121 				errors, writer);
1122 
1123 	__ops_memory_free(localmem);
1124 	__ops_memory_free(zmem);
1125 	__ops_memory_free(litmem);
1126 
1127 	return ret;
1128 }
1129 
1130 static void
1131 encrypt_se_ip_destroyer(__ops_writer_t *writer)
1132 {
1133 	encrypt_se_ip_t	*se_ip;
1134 
1135 	se_ip = __ops_writer_get_arg(writer);
1136 	free(se_ip->crypt);
1137 	free(se_ip);
1138 }
1139 
1140 unsigned
1141 __ops_write_se_ip_pktset(const unsigned char *data,
1142 		       const unsigned int len,
1143 		       __ops_crypt_t *crypted,
1144 		       __ops_output_t *output)
1145 {
1146 	__ops_output_t	*mdcoutput;
1147 	__ops_memory_t	*mdc;
1148 	unsigned char	 hashed[OPS_SHA1_HASH_SIZE];
1149 	unsigned char	*preamble;
1150 	const size_t	 sz_mdc = 1 + 1 + OPS_SHA1_HASH_SIZE;
1151 	size_t		 sz_preamble;
1152 	size_t		 sz_buf;
1153 
1154 	sz_preamble = crypted->blocksize + 2;
1155 	if ((preamble = calloc(1, sz_preamble)) == NULL) {
1156 		(void) fprintf(stderr, "__ops_write_se_ip_pktset: bad alloc\n");
1157 		return 0;
1158 	}
1159 	sz_buf = sz_preamble + len + sz_mdc;
1160 
1161 	if (!__ops_write_ptag(output, OPS_PTAG_CT_SE_IP_DATA) ||
1162 	    !__ops_write_length(output, 1 + sz_buf) ||
1163 	    !__ops_write_scalar(output, SE_IP_DATA_VERSION, 1)) {
1164 		free(preamble);
1165 		return 0;
1166 	}
1167 	__ops_random(preamble, crypted->blocksize);
1168 	preamble[crypted->blocksize] = preamble[crypted->blocksize - 2];
1169 	preamble[crypted->blocksize + 1] = preamble[crypted->blocksize - 1];
1170 
1171 	if (__ops_get_debug_level(__FILE__)) {
1172 		unsigned int    i;
1173 
1174 		fprintf(stderr, "\npreamble: ");
1175 		for (i = 0; i < sz_preamble; i++) {
1176 			fprintf(stderr, " 0x%02x", preamble[i]);
1177 		}
1178 		fprintf(stderr, "\n");
1179 	}
1180 
1181 	/* now construct MDC packet and add to the end of the buffer */
1182 	__ops_setup_memory_write(&mdcoutput, &mdc, sz_mdc);
1183 	__ops_calc_mdc_hash(preamble, sz_preamble, data, len, &hashed[0]);
1184 	__ops_write_mdc(hashed, mdcoutput);
1185 
1186 	if (__ops_get_debug_level(__FILE__)) {
1187 		unsigned int    i;
1188 		size_t          sz_plaintext = len;
1189 		size_t          sz_mdc2 = 1 + 1 + OPS_SHA1_HASH_SIZE;
1190 		unsigned char  *digest;
1191 
1192 		(void) fprintf(stderr, "\nplaintext: ");
1193 		for (i = 0; i < sz_plaintext; i++) {
1194 			(void) fprintf(stderr, " 0x%02x", data[i]);
1195 		}
1196 		(void) fprintf(stderr, "\n");
1197 
1198 		(void) fprintf(stderr, "\nmdc: ");
1199 		digest = __ops_mem_data(mdc);
1200 		for (i = 0; i < sz_mdc2; i++) {
1201 			(void) fprintf(stderr, " 0x%02x", digest[i]);
1202 		}
1203 		(void) fprintf(stderr, "\n");
1204 	}
1205 
1206 	/* and write it out */
1207 	__ops_push_enc_crypt(output, crypted);
1208 	if (__ops_get_debug_level(__FILE__)) {
1209 		(void) fprintf(stderr,
1210 			"writing %" PRIsize "u + %u + %" PRIsize "u\n",
1211 			sz_preamble, len, __ops_mem_len(mdc));
1212 	}
1213 	if (!__ops_write(output, preamble, sz_preamble) ||
1214 	    !__ops_write(output, data, len) ||
1215 	    !__ops_write(output, __ops_mem_data(mdc), __ops_mem_len(mdc))) {
1216 		/* \todo fix cleanup here and in old code functions */
1217 		return 0;
1218 	}
1219 
1220 	__ops_writer_pop(output);
1221 
1222 	/* cleanup  */
1223 	__ops_teardown_memory_write(mdcoutput, mdc);
1224 	free(preamble);
1225 
1226 	return 1;
1227 }
1228 
1229 typedef struct {
1230 	int             fd;
1231 } writer_fd_t;
1232 
1233 static unsigned
1234 fd_writer(const unsigned char *src, unsigned len,
1235 	  __ops_error_t **errors,
1236 	  __ops_writer_t *writer)
1237 {
1238 	writer_fd_t	*writerfd;
1239 	int              n;
1240 
1241 	writerfd = __ops_writer_get_arg(writer);
1242 	n = write(writerfd->fd, src, len);
1243 	if (n == -1) {
1244 		OPS_SYSTEM_ERROR_1(errors, OPS_E_W_WRITE_FAILED, "write",
1245 				   "file descriptor %d", writerfd->fd);
1246 		return 0;
1247 	}
1248 	if ((unsigned) n != len) {
1249 		OPS_ERROR_1(errors, OPS_E_W_WRITE_TOO_SHORT,
1250 			    "file descriptor %d", writerfd->fd);
1251 		return 0;
1252 	}
1253 	return 1;
1254 }
1255 
1256 static void
1257 writer_fd_destroyer(__ops_writer_t *writer)
1258 {
1259 	free(__ops_writer_get_arg(writer));
1260 }
1261 
1262 /**
1263  * \ingroup Core_WritersFirst
1264  * \brief Write to a File
1265  *
1266  * Set the writer in output to be a stock writer that writes to a file
1267  * descriptor. If another writer has already been set, then that is
1268  * first destroyed.
1269  *
1270  * \param output The output structure
1271  * \param fd The file descriptor
1272  *
1273  */
1274 
1275 void
1276 __ops_writer_set_fd(__ops_output_t *output, int fd)
1277 {
1278 	writer_fd_t	*writer;
1279 
1280 	if ((writer = calloc(1, sizeof(*writer))) == NULL) {
1281 		(void) fprintf(stderr, "__ops_writer_set_fd: bad alloc\n");
1282 	} else {
1283 		writer->fd = fd;
1284 		__ops_writer_set(output, fd_writer, NULL, writer_fd_destroyer, writer);
1285 	}
1286 }
1287 
1288 static unsigned
1289 memory_writer(const unsigned char *src,
1290 		unsigned len,
1291 		__ops_error_t **errors,
1292 		__ops_writer_t *writer)
1293 {
1294 	__ops_memory_t   *mem;
1295 
1296 	__OPS_USED(errors);
1297 	mem = __ops_writer_get_arg(writer);
1298 	__ops_memory_add(mem, src, len);
1299 	return 1;
1300 }
1301 
1302 /**
1303  * \ingroup Core_WritersFirst
1304  * \brief Write to memory
1305  *
1306  * Set a memory writer.
1307  *
1308  * \param output The output structure
1309  * \param mem The memory structure
1310  * \note It is the caller's responsiblity to call __ops_memory_free(mem)
1311  * \sa __ops_memory_free()
1312  */
1313 
1314 void
1315 __ops_writer_set_memory(__ops_output_t *output, __ops_memory_t *mem)
1316 {
1317 	__ops_writer_set(output, memory_writer, NULL, NULL, mem);
1318 }
1319 
1320 /**************************************************************************/
1321 
1322 typedef struct {
1323 	__ops_hash_alg_t	 hash_alg;
1324 	__ops_hash_t		 hash;
1325 	unsigned char		*hashed;
1326 } skey_checksum_t;
1327 
1328 static unsigned
1329 skey_checksum_writer(const unsigned char *src,
1330 	const unsigned len,
1331 	__ops_error_t **errors,
1332 	__ops_writer_t *writer)
1333 {
1334 	skey_checksum_t	*sum;
1335 	unsigned	 ret = 1;
1336 
1337 	sum = __ops_writer_get_arg(writer);
1338 	/* add contents to hash */
1339 	sum->hash.add(&sum->hash, src, len);
1340 	/* write to next stacked writer */
1341 	ret = __ops_stacked_write(src, len, errors, writer);
1342 	/* tidy up and return */
1343 	return ret;
1344 }
1345 
1346 static unsigned
1347 skey_checksum_finaliser(__ops_error_t **errors, __ops_writer_t *writer)
1348 {
1349 	skey_checksum_t *sum;
1350 
1351 	sum = __ops_writer_get_arg(writer);
1352 	if (errors) {
1353 		printf("errors in skey_checksum_finaliser\n");
1354 	}
1355 	sum->hash.finish(&sum->hash, sum->hashed);
1356 	return 1;
1357 }
1358 
1359 static void
1360 skey_checksum_destroyer(__ops_writer_t *writer)
1361 {
1362 	skey_checksum_t *sum;
1363 
1364 	sum = __ops_writer_get_arg(writer);
1365 	free(sum);
1366 }
1367 
1368 /**
1369 \ingroup Core_WritersNext
1370 \param output
1371 \param seckey
1372 */
1373 void
1374 __ops_push_checksum_writer(__ops_output_t *output, __ops_seckey_t *seckey)
1375 {
1376 	/* XXX: push a SHA-1 checksum writer (and change s2k to 254). */
1377 	skey_checksum_t *sum;
1378 
1379 	if ((sum = calloc(1, sizeof(*sum))) == NULL) {
1380 		(void) fprintf(stderr, "__ops_push_checksum_writer: bad alloc\n");
1381 	} else {
1382 		/* configure the arg */
1383 		sum->hash_alg = seckey->hash_alg;
1384 		sum->hashed = seckey->checkhash;
1385 		/* init the hash */
1386 		__ops_hash_any(&sum->hash, sum->hash_alg);
1387 		if (!sum->hash.init(&sum->hash)) {
1388 			(void) fprintf(stderr,
1389 				"__ops_push_checksum_writer: bad hash init\n");
1390 			/* just continue and die */
1391 			/* XXX - agc - no way to return failure */
1392 		}
1393 		__ops_writer_push(output, skey_checksum_writer,
1394 			skey_checksum_finaliser, skey_checksum_destroyer, sum);
1395 	}
1396 }
1397 
1398 /**************************************************************************/
1399 
1400 #define MAX_PARTIAL_DATA_LENGTH 1073741824
1401 
1402 typedef struct {
1403 	__ops_crypt_t	*crypt;
1404 	__ops_memory_t	*mem_data;
1405 	__ops_memory_t	*litmem;
1406 	__ops_output_t	*litoutput;
1407 	__ops_memory_t	*se_ip_mem;
1408 	__ops_output_t	*se_ip_out;
1409 	__ops_hash_t	 hash;
1410 } str_enc_se_ip_t;
1411 
1412 
1413 static unsigned
1414 str_enc_se_ip_writer(const unsigned char *src,
1415 			    unsigned len,
1416 			    __ops_error_t **errors,
1417 			    __ops_writer_t *writer);
1418 
1419 static unsigned
1420 str_enc_se_ip_finaliser(__ops_error_t **errors,
1421 			       __ops_writer_t * writer);
1422 
1423 static void     str_enc_se_ip_destroyer(__ops_writer_t *writer);
1424 
1425 /* */
1426 
1427 /**
1428 \ingroup Core_WritersNext
1429 \param output
1430 \param pubkey
1431 */
1432 void
1433 __ops_push_stream_enc_se_ip(__ops_output_t *output, const __ops_key_t *pubkey)
1434 {
1435 	__ops_pk_sesskey_t	*encrypted_pk_sesskey;
1436 	const unsigned int	 bufsz = 1024;
1437 	str_enc_se_ip_t		*se_ip;
1438 	__ops_crypt_t		*encrypted;
1439 	unsigned char		*iv;
1440 
1441 	if ((se_ip = calloc(1, sizeof(*se_ip))) == NULL) {
1442 		(void) fprintf(stderr,
1443 			"__ops_push_stream_enc_se_ip: bad alloc\n");
1444 		return;
1445 	}
1446 	encrypted_pk_sesskey = __ops_create_pk_sesskey(pubkey);
1447 	__ops_write_pk_sesskey(output, encrypted_pk_sesskey);
1448 
1449 	/* Setup the se_ip */
1450 	if ((encrypted = calloc(1, sizeof(*encrypted))) == NULL) {
1451 		free(se_ip);
1452 		(void) fprintf(stderr,
1453 			"__ops_push_stream_enc_se_ip: bad alloc\n");
1454 		return;
1455 	}
1456 	__ops_crypt_any(encrypted, encrypted_pk_sesskey->symm_alg);
1457 	if ((iv = calloc(1, encrypted->blocksize)) == NULL) {
1458 		free(encrypted);
1459 		free(se_ip);
1460 		(void) fprintf(stderr,
1461 			"__ops_push_stream_enc_se_ip: bad alloc\n");
1462 		return;
1463 	}
1464 	encrypted->set_iv(encrypted, iv);
1465 	encrypted->set_crypt_key(encrypted, &encrypted_pk_sesskey->key[0]);
1466 	__ops_encrypt_init(encrypted);
1467 
1468 	se_ip->crypt = encrypted;
1469 
1470 	se_ip->mem_data = __ops_memory_new();
1471 	__ops_memory_init(se_ip->mem_data, bufsz);
1472 
1473 	se_ip->litmem = NULL;
1474 	se_ip->litoutput = NULL;
1475 
1476 	__ops_setup_memory_write(&se_ip->se_ip_out, &se_ip->se_ip_mem, bufsz);
1477 
1478 	/* And push writer on stack */
1479 	__ops_writer_push(output,
1480 			str_enc_se_ip_writer,
1481 			str_enc_se_ip_finaliser,
1482 			str_enc_se_ip_destroyer, se_ip);
1483 	/* tidy up */
1484 	free(encrypted_pk_sesskey);
1485 	free(iv);
1486 }
1487 
1488 
1489 /* calculate the partial data length */
1490 static unsigned int
1491 __ops_partial_data_len(unsigned int len)
1492 {
1493 	unsigned int    mask = MAX_PARTIAL_DATA_LENGTH;
1494 	int             i;
1495 
1496 	if (len == 0) {
1497 		(void) fprintf(stderr, "__ops_partial_data_len: 0 len\n");
1498 		return 0;
1499 	}
1500 	if (len > MAX_PARTIAL_DATA_LENGTH) {
1501 		return MAX_PARTIAL_DATA_LENGTH;
1502 	}
1503 	for (i = 0; i <= 30; i++) {
1504 		if (mask & len) {
1505 			break;
1506 		}
1507 		mask >>= 1;
1508 	}
1509 	return mask;
1510 }
1511 
1512 static unsigned
1513 __ops_write_partial_len(unsigned int len, __ops_output_t *output)
1514 {
1515 	/* len must be a power of 2 from 0 to 30 */
1516 	unsigned char   c;
1517 	int             i;
1518 
1519 	for (i = 0; i <= 30; i++) {
1520 		if ((len >> i) & 1) {
1521 			break;
1522 		}
1523 	}
1524 	c = 224 + i;
1525 	return __ops_write(output, &c, 1);
1526 }
1527 
1528 static unsigned
1529 stream_write_litdata(__ops_output_t *output,
1530 			const unsigned char *data,
1531 			unsigned len)
1532 {
1533 	while (len > 0) {
1534 		size_t          pdlen = __ops_partial_data_len(len);
1535 
1536 		__ops_write_partial_len(pdlen, output);
1537 		__ops_write(output, data, pdlen);
1538 		data += pdlen;
1539 		len -= pdlen;
1540 	}
1541 	return 1;
1542 }
1543 
1544 static unsigned
1545 stream_write_litdata_first(__ops_output_t *output,
1546 				const unsigned char *data,
1547 				unsigned int len,
1548 				const __ops_litdata_type_t type)
1549 {
1550 	/* \todo add filename  */
1551 	/* \todo add date */
1552 	/* \todo do we need to check text data for <cr><lf> line endings ? */
1553 
1554 	size_t          sz_towrite;
1555 	size_t          sz_pd;
1556 
1557 	sz_towrite = 1 + 1 + 4 + len;
1558 	sz_pd = __ops_partial_data_len(sz_towrite);
1559 	if (sz_pd < 512) {
1560 		(void) fprintf(stderr,
1561 			"stream_write_litdata_first: bad sz_pd\n");
1562 		return 0;
1563 	}
1564 	__ops_write_ptag(output, OPS_PTAG_CT_LITDATA);
1565 	__ops_write_partial_len(sz_pd, output);
1566 	__ops_write_scalar(output, (unsigned)type, 1);
1567 	__ops_write_scalar(output, 0, 1);
1568 	__ops_write_scalar(output, 0, 4);
1569 	__ops_write(output, data, sz_pd - 6);
1570 
1571 	data += (sz_pd - 6);
1572 	sz_towrite -= sz_pd;
1573 
1574 	return stream_write_litdata(output, data, sz_towrite);
1575 }
1576 
1577 static unsigned
1578 stream_write_litdata_last(__ops_output_t *output,
1579 				const unsigned char *data,
1580 				unsigned int len)
1581 {
1582 	__ops_write_length(output, len);
1583 	return __ops_write(output, data, len);
1584 }
1585 
1586 static unsigned
1587 stream_write_se_ip(__ops_output_t *output,
1588 			const unsigned char *data,
1589 			unsigned int len,
1590 			str_enc_se_ip_t *se_ip)
1591 {
1592 	size_t          pdlen;
1593 
1594 	while (len > 0) {
1595 		pdlen = __ops_partial_data_len(len);
1596 		__ops_write_partial_len(pdlen, output);
1597 
1598 		__ops_push_enc_crypt(output, se_ip->crypt);
1599 		__ops_write(output, data, pdlen);
1600 		__ops_writer_pop(output);
1601 
1602 		se_ip->hash.add(&se_ip->hash, data, pdlen);
1603 
1604 		data += pdlen;
1605 		len -= pdlen;
1606 	}
1607 	return 1;
1608 }
1609 
1610 static unsigned
1611 stream_write_se_ip_first(__ops_output_t *output,
1612 			const unsigned char *data,
1613 			unsigned int len,
1614 			str_enc_se_ip_t *se_ip)
1615 {
1616 	unsigned char  *preamble;
1617 	size_t          blocksize;
1618 	size_t          sz_preamble;
1619 	size_t          sz_towrite;
1620 	size_t          sz_pd;
1621 
1622 	blocksize = se_ip->crypt->blocksize;
1623 	sz_preamble = blocksize + 2;
1624 	sz_towrite = sz_preamble + 1 + len;
1625 	if ((preamble = calloc(1, sz_preamble)) == NULL) {
1626 		(void) fprintf(stderr,
1627 			"stream_write_se_ip_first: bad alloc\n");
1628 		return 0;
1629 	}
1630 	sz_pd = __ops_partial_data_len(sz_towrite);
1631 	if (sz_pd < 512) {
1632 		free(preamble);
1633 		(void) fprintf(stderr,
1634 			"stream_write_se_ip_first: bad sz_pd\n");
1635 		return 0;
1636 	}
1637 	__ops_write_ptag(output, OPS_PTAG_CT_SE_IP_DATA);
1638 	__ops_write_partial_len(sz_pd, output);
1639 	__ops_write_scalar(output, SE_IP_DATA_VERSION, 1);
1640 	__ops_push_enc_crypt(output, se_ip->crypt);
1641 
1642 	__ops_random(preamble, blocksize);
1643 	preamble[blocksize] = preamble[blocksize - 2];
1644 	preamble[blocksize + 1] = preamble[blocksize - 1];
1645 	__ops_hash_any(&se_ip->hash, OPS_HASH_SHA1);
1646 	if (!se_ip->hash.init(&se_ip->hash)) {
1647 		free(preamble);
1648 		(void) fprintf(stderr,
1649 			"stream_write_se_ip_first: bad hash init\n");
1650 		return 0;
1651 	}
1652 	__ops_write(output, preamble, sz_preamble);
1653 	se_ip->hash.add(&se_ip->hash, preamble, sz_preamble);
1654 	__ops_write(output, data, sz_pd - sz_preamble - 1);
1655 	se_ip->hash.add(&se_ip->hash, data, sz_pd - sz_preamble - 1);
1656 	data += (sz_pd - sz_preamble - 1);
1657 	sz_towrite -= sz_pd;
1658 	__ops_writer_pop(output);
1659 	stream_write_se_ip(output, data, sz_towrite, se_ip);
1660 	free(preamble);
1661 	return 1;
1662 }
1663 
1664 static unsigned
1665 stream_write_se_ip_last(__ops_output_t *output,
1666 			const unsigned char *data,
1667 			unsigned int len,
1668 			str_enc_se_ip_t *se_ip)
1669 {
1670 	__ops_output_t	*mdcoutput;
1671 	__ops_memory_t	*mdcmem;
1672 	unsigned char	 c;
1673 	unsigned char	 hashed[OPS_SHA1_HASH_SIZE];
1674 	const size_t	 sz_mdc = 1 + 1 + OPS_SHA1_HASH_SIZE;
1675 	size_t		 sz_buf = len + sz_mdc;
1676 
1677 	se_ip->hash.add(&se_ip->hash, data, len);
1678 
1679 	/* MDC packet tag */
1680 	c = MDC_PKT_TAG;
1681 	se_ip->hash.add(&se_ip->hash, &c, 1);
1682 
1683 	/* MDC packet len */
1684 	c = OPS_SHA1_HASH_SIZE;
1685 	se_ip->hash.add(&se_ip->hash, &c, 1);
1686 
1687 	/* finish */
1688 	se_ip->hash.finish(&se_ip->hash, hashed);
1689 
1690 	__ops_setup_memory_write(&mdcoutput, &mdcmem, sz_mdc);
1691 	__ops_write_mdc(hashed, mdcoutput);
1692 
1693 	/* write length of last se_ip chunk */
1694 	__ops_write_length(output, sz_buf);
1695 
1696 	/* encode everting */
1697 	__ops_push_enc_crypt(output, se_ip->crypt);
1698 
1699 	__ops_write(output, data, len);
1700 	__ops_write(output, __ops_mem_data(mdcmem), __ops_mem_len(mdcmem));
1701 
1702 	__ops_writer_pop(output);
1703 
1704 	__ops_teardown_memory_write(mdcoutput, mdcmem);
1705 
1706 	return 1;
1707 }
1708 
1709 static unsigned
1710 str_enc_se_ip_writer(const unsigned char *src,
1711 			    unsigned len,
1712 			    __ops_error_t **errors,
1713 			    __ops_writer_t *writer)
1714 {
1715 	str_enc_se_ip_t	*se_ip = __ops_writer_get_arg(writer);
1716 	unsigned	 ret = 1;
1717 
1718 	if (se_ip->litoutput == NULL) {	/* first literal data chunk
1719 						 * is not yet written */
1720 		size_t          datalength;
1721 
1722 		__ops_memory_add(se_ip->mem_data, src, len);
1723 		datalength = __ops_mem_len(se_ip->mem_data);
1724 
1725 		/* 4.2.2.4. Partial Body Lengths */
1726 		/* The first partial length MUST be at least 512 octets long. */
1727 		if (datalength < 512) {
1728 			return 1;	/* will wait for more data or
1729 						 * end of stream             */
1730 		}
1731 		__ops_setup_memory_write(&se_ip->litoutput,
1732 				&se_ip->litmem, datalength + 32);
1733 		stream_write_litdata_first(se_ip->litoutput,
1734 				__ops_mem_data(se_ip->mem_data),
1735 				datalength,
1736 				OPS_LDT_BINARY);
1737 
1738 		stream_write_se_ip_first(se_ip->se_ip_out,
1739 				__ops_mem_data(se_ip->litmem),
1740 				__ops_mem_len(se_ip->litmem), se_ip);
1741 	} else {
1742 		stream_write_litdata(se_ip->litoutput, src, len);
1743 		stream_write_se_ip(se_ip->se_ip_out,
1744 				__ops_mem_data(se_ip->litmem),
1745 				__ops_mem_len(se_ip->litmem), se_ip);
1746 	}
1747 
1748 	/* now write memory to next writer */
1749 	ret = __ops_stacked_write(__ops_mem_data(se_ip->se_ip_mem),
1750 				__ops_mem_len(se_ip->se_ip_mem),
1751 				errors, writer);
1752 
1753 	__ops_memory_clear(se_ip->litmem);
1754 	__ops_memory_clear(se_ip->se_ip_mem);
1755 
1756 	return ret;
1757 }
1758 
1759 static unsigned
1760 str_enc_se_ip_finaliser(__ops_error_t **errors, __ops_writer_t *writer)
1761 {
1762 	str_enc_se_ip_t *se_ip = __ops_writer_get_arg(writer);
1763 	/* write last chunk of data */
1764 
1765 	if (se_ip->litoutput == NULL) {
1766 		/* first literal data chunk was not written */
1767 		/* so we know the total length of data, write a simple packet */
1768 
1769 		/* create literal data packet from buffered data */
1770 		__ops_setup_memory_write(&se_ip->litoutput, &se_ip->litmem,
1771 				 __ops_mem_len(se_ip->mem_data) + 32);
1772 
1773 		__ops_write_litdata(se_ip->litoutput,
1774 			__ops_mem_data(se_ip->mem_data),
1775 			(const int)__ops_mem_len(se_ip->mem_data),
1776 			OPS_LDT_BINARY);
1777 
1778 		/* create SE IP packet set from this literal data */
1779 		__ops_write_se_ip_pktset(
1780 				__ops_mem_data(se_ip->litmem),
1781 				__ops_mem_len(se_ip->litmem),
1782 				se_ip->crypt, se_ip->se_ip_out);
1783 
1784 	} else {
1785 		/* finish writing */
1786 		stream_write_litdata_last(se_ip->litoutput, NULL, 0);
1787 		stream_write_se_ip_last(se_ip->se_ip_out,
1788 				__ops_mem_data(se_ip->litmem),
1789 				__ops_mem_len(se_ip->litmem), se_ip);
1790 	}
1791 
1792 	/* now write memory to next writer */
1793 	return __ops_stacked_write(__ops_mem_data(se_ip->se_ip_mem),
1794 				 __ops_mem_len(se_ip->se_ip_mem),
1795 				 errors, writer);
1796 }
1797 
1798 static void
1799 str_enc_se_ip_destroyer(__ops_writer_t *writer)
1800 {
1801 	str_enc_se_ip_t *se_ip = __ops_writer_get_arg(writer);
1802 
1803 	__ops_memory_free(se_ip->mem_data);
1804 	__ops_teardown_memory_write(se_ip->litoutput, se_ip->litmem);
1805 	__ops_teardown_memory_write(se_ip->se_ip_out, se_ip->se_ip_mem);
1806 
1807 	se_ip->crypt->decrypt_finish(se_ip->crypt);
1808 
1809 	free(se_ip->crypt);
1810 	free(se_ip);
1811 }
1812