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