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