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