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