/*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Alistair Crooks (agc@NetBSD.org) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) * All rights reserved. * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted * their moral rights under the UK Copyright Design and Patents Act 1988 to * be recorded as the authors of this copyright work. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. * * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. */ /** \file * This file contains the base functions used by the writers. */ #include "config.h" #ifdef HAVE_SYS_CDEFS_H #include #endif #if defined(__NetBSD__) __COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved."); __RCSID("$NetBSD: writer.c,v 1.33 2012/03/05 02:20:18 christos Exp $"); #endif #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_OPENSSL_CAST_H #include #endif #include "create.h" #include "writer.h" #include "keyring.h" #include "signature.h" #include "packet.h" #include "packet-parse.h" #include "readerwriter.h" #include "memory.h" #include "netpgpdefs.h" #include "version.h" #include "netpgpdigest.h" /* * return 1 if OK, otherwise 0 */ static unsigned base_write(pgp_output_t *out, const void *src, unsigned len) { return out->writer.writer(src, len, &out->errors, &out->writer); } /** * \ingroup Core_WritePackets * * \param src * \param len * \param output * \return 1 if OK, otherwise 0 */ unsigned pgp_write(pgp_output_t *output, const void *src, unsigned len) { return base_write(output, src, len); } /** * \ingroup Core_WritePackets * \param n * \param len * \param output * \return 1 if OK, otherwise 0 */ unsigned pgp_write_scalar(pgp_output_t *output, unsigned n, unsigned len) { uint8_t c; while (len-- > 0) { c = n >> (len * 8); if (!base_write(output, &c, 1)) { return 0; } } return 1; } /** * \ingroup Core_WritePackets * \param bn * \param output * \return 1 if OK, otherwise 0 */ unsigned pgp_write_mpi(pgp_output_t *output, const BIGNUM *bn) { unsigned bits; uint8_t buf[NETPGP_BUFSIZ]; bits = (unsigned)BN_num_bits(bn); if (bits > 65535) { (void) fprintf(stderr, "pgp_write_mpi: too large %u\n", bits); return 0; } BN_bn2bin(bn, buf); return pgp_write_scalar(output, bits, 2) && pgp_write(output, buf, (bits + 7) / 8); } /** * \ingroup Core_WritePackets * \param tag * \param output * \return 1 if OK, otherwise 0 */ unsigned pgp_write_ptag(pgp_output_t *output, pgp_content_enum tag) { uint8_t c; c = tag | PGP_PTAG_ALWAYS_SET | PGP_PTAG_NEW_FORMAT; return base_write(output, &c, 1); } /** * \ingroup Core_WritePackets * \param len * \param output * \return 1 if OK, otherwise 0 */ unsigned pgp_write_length(pgp_output_t *output, unsigned len) { uint8_t c[2]; if (len < 192) { c[0] = len; return base_write(output, c, 1); } if (len < 8192 + 192) { c[0] = ((len - 192) >> 8) + 192; c[1] = (len - 192) % 256; return base_write(output, c, 2); } return pgp_write_scalar(output, 0xff, 1) && pgp_write_scalar(output, len, 4); } /* * Note that we finalise from the top down, so we don't use writers below * that have already been finalised */ unsigned pgp_writer_info_finalise(pgp_error_t **errors, pgp_writer_t *writer) { unsigned ret = 1; if (writer->finaliser) { ret = writer->finaliser(errors, writer); writer->finaliser = NULL; } if (writer->next && !pgp_writer_info_finalise(errors, writer->next)) { writer->finaliser = NULL; return 0; } return ret; } void pgp_writer_info_delete(pgp_writer_t *writer) { /* we should have finalised before deleting */ if (writer->finaliser) { (void) fprintf(stderr, "pgp_writer_info_delete: not done\n"); return; } if (writer->next) { pgp_writer_info_delete(writer->next); free(writer->next); writer->next = NULL; } if (writer->destroyer) { writer->destroyer(writer); writer->destroyer = NULL; } writer->writer = NULL; } /** * \ingroup Core_Writers * * Set a writer in output. There should not be another writer set. * * \param output The output structure * \param writer * \param finaliser * \param destroyer * \param arg The argument for the writer and destroyer */ void pgp_writer_set(pgp_output_t *output, pgp_writer_func_t *writer, pgp_writer_finaliser_t *finaliser, pgp_writer_destroyer_t *destroyer, void *arg) { if (output->writer.writer) { (void) fprintf(stderr, "pgp_writer_set: already set\n"); } else { output->writer.writer = writer; output->writer.finaliser = finaliser; output->writer.destroyer = destroyer; output->writer.arg = arg; } } /** * \ingroup Core_Writers * * Push a writer in output. There must already be another writer set. * * \param output The output structure * \param writer * \param finaliser * \param destroyer * \param arg The argument for the writer and destroyer */ void pgp_writer_push(pgp_output_t *output, pgp_writer_func_t *writer, pgp_writer_finaliser_t *finaliser, pgp_writer_destroyer_t *destroyer, void *arg) { pgp_writer_t *copy; if ((copy = calloc(1, sizeof(*copy))) == NULL) { (void) fprintf(stderr, "pgp_writer_push: bad alloc\n"); } else if (output->writer.writer == NULL) { (void) fprintf(stderr, "pgp_writer_push: no orig writer\n"); } else { *copy = output->writer; output->writer.next = copy; output->writer.writer = writer; output->writer.finaliser = finaliser; output->writer.destroyer = destroyer; output->writer.arg = arg; } } void pgp_writer_pop(pgp_output_t *output) { pgp_writer_t *next; /* Make sure the finaliser has been called. */ if (output->writer.finaliser) { (void) fprintf(stderr, "pgp_writer_pop: finaliser not called\n"); } else if (output->writer.next == NULL) { (void) fprintf(stderr, "pgp_writer_pop: not a stacked writer\n"); } else { if (output->writer.destroyer) { output->writer.destroyer(&output->writer); } next = output->writer.next; output->writer = *next; free(next); } } /** * \ingroup Core_Writers * * Close the writer currently set in output. * * \param output The output structure */ unsigned pgp_writer_close(pgp_output_t *output) { unsigned ret; ret = pgp_writer_info_finalise(&output->errors, &output->writer); pgp_writer_info_delete(&output->writer); return ret; } /** * \ingroup Core_Writers * * Get the arg supplied to pgp_createinfo_set_writer(). * * \param writer The writer_info structure * \return The arg */ void * pgp_writer_get_arg(pgp_writer_t *writer) { return writer->arg; } /** * \ingroup Core_Writers * * Write to the next writer down in the stack. * * \param src The data to write. * \param len The length of src. * \param errors A place to store errors. * \param writer The writer_info structure. * \return Success - if 0, then errors should contain the error. */ static unsigned stacked_write(pgp_writer_t *writer, const void *src, unsigned len, pgp_error_t ** errors) { return writer->next->writer(src, len, errors, writer->next); } /** * \ingroup Core_Writers * * Free the arg. Many writers just have a calloc()ed lump of storage, this * function releases it. * * \param writer the info structure. */ static void generic_destroyer(pgp_writer_t *writer) { free(pgp_writer_get_arg(writer)); } /** * \ingroup Core_Writers * * A writer that just writes to the next one down. Useful for when you * want to insert just a finaliser into the stack. */ unsigned pgp_writer_passthrough(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { return stacked_write(writer, src, len, errors); } /**************************************************************************/ /** * \struct dashesc_t */ typedef struct { unsigned seen_nl:1; unsigned seen_cr:1; pgp_create_sig_t *sig; pgp_memory_t *trailing; } dashesc_t; static unsigned dash_esc_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { dashesc_t *dash = pgp_writer_get_arg(writer); unsigned n; if (pgp_get_debug_level(__FILE__)) { unsigned i = 0; (void) fprintf(stderr, "dash_esc_writer writing %u:\n", len); for (i = 0; i < len; i++) { fprintf(stderr, "0x%02x ", src[i]); if (((i + 1) % 16) == 0) { (void) fprintf(stderr, "\n"); } else if (((i + 1) % 8) == 0) { (void) fprintf(stderr, " "); } } (void) fprintf(stderr, "\n"); } /* XXX: make this efficient */ for (n = 0; n < len; ++n) { unsigned l; if (dash->seen_nl) { if (src[n] == '-' && !stacked_write(writer, "- ", 2, errors)) { return 0; } dash->seen_nl = 0; } dash->seen_nl = src[n] == '\n'; if (dash->seen_nl && !dash->seen_cr) { if (!stacked_write(writer, "\r", 1, errors)) { return 0; } pgp_sig_add_data(dash->sig, "\r", 1); } dash->seen_cr = src[n] == '\r'; if (!stacked_write(writer, &src[n], 1, errors)) { return 0; } /* trailing whitespace isn't included in the signature */ if (src[n] == ' ' || src[n] == '\t') { pgp_memory_add(dash->trailing, &src[n], 1); } else { if ((l = (unsigned)pgp_mem_len(dash->trailing)) != 0) { if (!dash->seen_nl && !dash->seen_cr) { pgp_sig_add_data(dash->sig, pgp_mem_data(dash->trailing), l); } pgp_memory_clear(dash->trailing); } pgp_sig_add_data(dash->sig, &src[n], 1); } } return 1; } /** * \param writer */ static void dash_escaped_destroyer(pgp_writer_t *writer) { dashesc_t *dash; dash = pgp_writer_get_arg(writer); pgp_memory_free(dash->trailing); free(dash); } /** * \ingroup Core_WritersNext * \brief Push Clearsigned Writer onto stack * \param output * \param sig */ unsigned pgp_writer_push_clearsigned(pgp_output_t *output, pgp_create_sig_t *sig) { static const char header[] = "-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: "; const char *hash; dashesc_t *dash; unsigned ret; hash = pgp_text_from_hash(pgp_sig_get_hash(sig)); if ((dash = calloc(1, sizeof(*dash))) == NULL) { PGP_ERROR_1(&output->errors, PGP_E_W, "%s", "Bad alloc"); return 0; } ret = (pgp_write(output, header, (unsigned)(sizeof(header) - 1)) && pgp_write(output, hash, (unsigned)strlen(hash)) && pgp_write(output, "\r\n\r\n", 4)); if (ret == 0) { PGP_ERROR_1(&output->errors, PGP_E_W, "%s", "Error pushing clearsigned header"); free(dash); return ret; } dash->seen_nl = 1; dash->sig = sig; dash->trailing = pgp_memory_new(); pgp_writer_push(output, dash_esc_writer, NULL, dash_escaped_destroyer, dash); return ret; } /** * \struct base64_t */ typedef struct { unsigned pos; uint8_t t; unsigned checksum; } base64_t; static const char b64map[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static unsigned base64_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { base64_t *base64; unsigned n; base64 = pgp_writer_get_arg(writer); for (n = 0; n < len;) { base64->checksum = pgp_crc24(base64->checksum, src[n]); if (base64->pos == 0) { /* XXXXXX00 00000000 00000000 */ if (!stacked_write(writer, &b64map[(unsigned)src[n] >> 2], 1, errors)) { return 0; } /* 000000XX xxxx0000 00000000 */ base64->t = (src[n++] & 3) << 4; base64->pos = 1; } else if (base64->pos == 1) { /* 000000xx XXXX0000 00000000 */ base64->t += (unsigned)src[n] >> 4; if (!stacked_write(writer, &b64map[base64->t], 1, errors)) { return 0; } /* 00000000 0000XXXX xx000000 */ base64->t = (src[n++] & 0xf) << 2; base64->pos = 2; } else if (base64->pos == 2) { /* 00000000 0000xxxx XX000000 */ base64->t += (unsigned)src[n] >> 6; if (!stacked_write(writer, &b64map[base64->t], 1, errors)) { return 0; } /* 00000000 00000000 00XXXXXX */ if (!stacked_write(writer, &b64map[src[n++] & 0x3f], 1, errors)) { return 0; } base64->pos = 0; } } return 1; } static unsigned sig_finaliser(pgp_error_t **errors, pgp_writer_t *writer) { static const char trail[] = "\r\n-----END PGP SIGNATURE-----\r\n"; base64_t *base64; uint8_t c[3]; base64 = pgp_writer_get_arg(writer); if (base64->pos) { if (!stacked_write(writer, &b64map[base64->t], 1, errors)) { return 0; } if (base64->pos == 1 && !stacked_write(writer, "==", 2, errors)) { return 0; } if (base64->pos == 2 && !stacked_write(writer, "=", 1, errors)) { return 0; } } /* Ready for the checksum */ if (!stacked_write(writer, "\r\n=", 3, errors)) { return 0; } base64->pos = 0; /* get ready to write the checksum */ c[0] = base64->checksum >> 16; c[1] = base64->checksum >> 8; c[2] = base64->checksum; /* push the checksum through our own writer */ if (!base64_writer(c, 3, errors, writer)) { return 0; } return stacked_write(writer, trail, (unsigned)(sizeof(trail) - 1), errors); } /** * \struct linebreak_t */ typedef struct { unsigned pos; } linebreak_t; #define BREAKPOS 76 static unsigned linebreak_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { linebreak_t *linebreak; unsigned n; linebreak = pgp_writer_get_arg(writer); for (n = 0; n < len; ++n, ++linebreak->pos) { if (src[n] == '\r' || src[n] == '\n') { linebreak->pos = 0; } if (linebreak->pos == BREAKPOS) { if (!stacked_write(writer, "\r\n", 2, errors)) { return 0; } linebreak->pos = 0; } if (!stacked_write(writer, &src[n], 1, errors)) { return 0; } } return 1; } /** * \ingroup Core_WritersNext * \brief Push armoured signature on stack * \param output */ unsigned pgp_writer_use_armored_sig(pgp_output_t *output) { static const char header[] = "\r\n-----BEGIN PGP SIGNATURE-----\r\nVersion: " NETPGP_VERSION_STRING "\r\n\r\n"; linebreak_t *linebreak; base64_t *base64; pgp_writer_pop(output); if (pgp_write(output, header, (unsigned)(sizeof(header) - 1)) == 0) { PGP_ERROR_1(&output->errors, PGP_E_W, "%s", "Error switching to armoured signature"); return 0; } if ((linebreak = calloc(1, sizeof(*linebreak))) == NULL) { PGP_ERROR_1(&output->errors, PGP_E_W, "%s", "pgp_writer_use_armored_sig: Bad alloc"); return 0; } pgp_writer_push(output, linebreak_writer, NULL, generic_destroyer, linebreak); base64 = calloc(1, sizeof(*base64)); if (!base64) { PGP_MEMORY_ERROR(&output->errors); return 0; } base64->checksum = CRC24_INIT; pgp_writer_push(output, base64_writer, sig_finaliser, generic_destroyer, base64); return 1; } static unsigned armoured_message_finaliser(pgp_error_t **errors, pgp_writer_t *writer) { /* TODO: This is same as sig_finaliser apart from trailer. */ static const char trailer[] = "\r\n-----END PGP MESSAGE-----\r\n"; base64_t *base64; uint8_t c[3]; base64 = pgp_writer_get_arg(writer); if (base64->pos) { if (!stacked_write(writer, &b64map[base64->t], 1, errors)) { return 0; } if (base64->pos == 1 && !stacked_write(writer, "==", 2, errors)) { return 0; } if (base64->pos == 2 && !stacked_write(writer, "=", 1, errors)) { return 0; } } /* Ready for the checksum */ if (!stacked_write(writer, "\r\n=", 3, errors)) { return 0; } base64->pos = 0; /* get ready to write the checksum */ c[0] = base64->checksum >> 16; c[1] = base64->checksum >> 8; c[2] = base64->checksum; /* push the checksum through our own writer */ if (!base64_writer(c, 3, errors, writer)) { return 0; } return stacked_write(writer, trailer, (unsigned)strlen(trailer), errors); } /** \ingroup Core_WritersNext \brief Write a PGP MESSAGE \todo replace with generic function */ void pgp_writer_push_armor_msg(pgp_output_t *output) { static const char header[] = "-----BEGIN PGP MESSAGE-----\r\n"; linebreak_t *linebreak; base64_t *base64; pgp_write(output, header, (unsigned)(sizeof(header) - 1)); pgp_write(output, "\r\n", 2); if ((linebreak = calloc(1, sizeof(*linebreak))) == NULL) { (void) fprintf(stderr, "pgp_writer_push_armor_msg: bad lb alloc\n"); return; } pgp_writer_push(output, linebreak_writer, NULL, generic_destroyer, linebreak); if ((base64 = calloc(1, sizeof(*base64))) == NULL) { (void) fprintf(stderr, "pgp_writer_push_armor_msg: bad alloc\n"); return; } base64->checksum = CRC24_INIT; pgp_writer_push(output, base64_writer, armoured_message_finaliser, generic_destroyer, base64); } static unsigned armoured_finaliser(pgp_armor_type_t type, pgp_error_t **errors, pgp_writer_t *writer) { static const char tail_pubkey[] = "\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n"; static const char tail_private_key[] = "\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n"; const char *tail = NULL; unsigned tailsize = 0; base64_t *base64; uint8_t c[3]; switch (type) { case PGP_PGP_PUBLIC_KEY_BLOCK: tail = tail_pubkey; tailsize = sizeof(tail_pubkey) - 1; break; case PGP_PGP_PRIVATE_KEY_BLOCK: tail = tail_private_key; tailsize = sizeof(tail_private_key) - 1; break; default: (void) fprintf(stderr, "armoured_finaliser: unusual type\n"); return 0; } base64 = pgp_writer_get_arg(writer); if (base64->pos) { if (!stacked_write(writer, &b64map[base64->t], 1, errors)) { return 0; } if (base64->pos == 1 && !stacked_write(writer, "==", 2, errors)) { return 0; } if (base64->pos == 2 && !stacked_write(writer, "=", 1, errors)) { return 0; } } /* Ready for the checksum */ if (!stacked_write(writer, "\r\n=", 3, errors)) { return 0; } base64->pos = 0; /* get ready to write the checksum */ c[0] = base64->checksum >> 16; c[1] = base64->checksum >> 8; c[2] = base64->checksum; /* push the checksum through our own writer */ if (!base64_writer(c, 3, errors, writer)) { return 0; } return stacked_write(writer, tail, tailsize, errors); } static unsigned armored_pubkey_fini(pgp_error_t **errors, pgp_writer_t *writer) { return armoured_finaliser(PGP_PGP_PUBLIC_KEY_BLOCK, errors, writer); } static unsigned armored_privkey_fini(pgp_error_t **errors, pgp_writer_t *writer) { return armoured_finaliser(PGP_PGP_PRIVATE_KEY_BLOCK, errors, writer); } /* \todo use this for other armoured types */ /** \ingroup Core_WritersNext \brief Push Armoured Writer on stack (generic) */ void pgp_writer_push_armoured(pgp_output_t *output, pgp_armor_type_t type) { static char hdr_pubkey[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: " NETPGP_VERSION_STRING "\r\n\r\n"; static char hdr_private_key[] = "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: " NETPGP_VERSION_STRING "\r\n\r\n"; unsigned hdrsize = 0; unsigned (*finaliser) (pgp_error_t **, pgp_writer_t *); base64_t *base64; linebreak_t *linebreak; char *header = NULL; finaliser = NULL; switch (type) { case PGP_PGP_PUBLIC_KEY_BLOCK: header = hdr_pubkey; hdrsize = sizeof(hdr_pubkey) - 1; finaliser = armored_pubkey_fini; break; case PGP_PGP_PRIVATE_KEY_BLOCK: header = hdr_private_key; hdrsize = sizeof(hdr_private_key) - 1; finaliser = armored_privkey_fini; break; default: (void) fprintf(stderr, "pgp_writer_push_armoured: unusual type\n"); return; } if ((linebreak = calloc(1, sizeof(*linebreak))) == NULL) { (void) fprintf(stderr, "pgp_writer_push_armoured: bad alloc\n"); return; } pgp_write(output, header, hdrsize); pgp_writer_push(output, linebreak_writer, NULL, generic_destroyer, linebreak); if ((base64 = calloc(1, sizeof(*base64))) == NULL) { (void) fprintf(stderr, "pgp_writer_push_armoured: bad alloc\n"); return; } base64->checksum = CRC24_INIT; pgp_writer_push(output, base64_writer, finaliser, generic_destroyer, base64); } /**************************************************************************/ typedef struct { pgp_crypt_t *crypt; int free_crypt; } crypt_t; /* * This writer simply takes plaintext as input, * encrypts it with the given key * and outputs the resulting encrypted text */ static unsigned encrypt_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { #define BUFSZ 1024 /* arbitrary number */ uint8_t encbuf[BUFSZ]; unsigned remaining; unsigned done = 0; crypt_t *pgp_encrypt; remaining = len; pgp_encrypt = (crypt_t *) pgp_writer_get_arg(writer); if (!pgp_is_sa_supported(pgp_encrypt->crypt->alg)) { (void) fprintf(stderr, "encrypt_writer: not supported\n"); return 0; } while (remaining > 0) { unsigned size = (remaining < BUFSZ) ? remaining : BUFSZ; /* memcpy(buf,src,size); // \todo copy needed here? */ pgp_encrypt->crypt->cfb_encrypt(pgp_encrypt->crypt, encbuf, src + done, size); if (pgp_get_debug_level(__FILE__)) { hexdump(stderr, "unencrypted", &src[done], 16); hexdump(stderr, "encrypted", encbuf, 16); } if (!stacked_write(writer, encbuf, size, errors)) { if (pgp_get_debug_level(__FILE__)) { fprintf(stderr, "encrypted_writer: stacked write\n"); } return 0; } remaining -= size; done += size; } return 1; } static void encrypt_destroyer(pgp_writer_t *writer) { crypt_t *pgp_encrypt; pgp_encrypt = (crypt_t *) pgp_writer_get_arg(writer); if (pgp_encrypt->free_crypt) { free(pgp_encrypt->crypt); } free(pgp_encrypt); } /** \ingroup Core_WritersNext \brief Push Encrypted Writer onto stack (create SE packets) */ void pgp_push_enc_crypt(pgp_output_t *output, pgp_crypt_t *pgp_crypt) { /* Create encrypt to be used with this writer */ /* Remember to free this in the destroyer */ crypt_t *pgp_encrypt; if ((pgp_encrypt = calloc(1, sizeof(*pgp_encrypt))) == NULL) { (void) fprintf(stderr, "pgp_push_enc_crypt: bad alloc\n"); } else { /* Setup the encrypt */ pgp_encrypt->crypt = pgp_crypt; pgp_encrypt->free_crypt = 0; /* And push writer on stack */ pgp_writer_push(output, encrypt_writer, NULL, encrypt_destroyer, pgp_encrypt); } } /**************************************************************************/ typedef struct { pgp_crypt_t *crypt; } encrypt_se_ip_t; static unsigned encrypt_se_ip_writer(const uint8_t *, unsigned, pgp_error_t **, pgp_writer_t *); static void encrypt_se_ip_destroyer(pgp_writer_t *); /* */ /** \ingroup Core_WritersNext \brief Push Encrypted SE IP Writer onto stack */ int pgp_push_enc_se_ip(pgp_output_t *output, const pgp_key_t *pubkey, const char *cipher) { pgp_pk_sesskey_t *encrypted_pk_sesskey; encrypt_se_ip_t *se_ip; pgp_crypt_t *encrypted; uint8_t *iv; if ((se_ip = calloc(1, sizeof(*se_ip))) == NULL) { (void) fprintf(stderr, "pgp_push_enc_se_ip: bad alloc\n"); return 0; } /* Create and write encrypted PK session key */ if ((encrypted_pk_sesskey = pgp_create_pk_sesskey(pubkey, cipher)) == NULL) { (void) fprintf(stderr, "pgp_push_enc_se_ip: null pk sesskey\n"); return 0; } pgp_write_pk_sesskey(output, encrypted_pk_sesskey); /* Setup the se_ip */ if ((encrypted = calloc(1, sizeof(*encrypted))) == NULL) { free(se_ip); (void) fprintf(stderr, "pgp_push_enc_se_ip: bad alloc\n"); return 0; } pgp_crypt_any(encrypted, encrypted_pk_sesskey->symm_alg); if ((iv = calloc(1, encrypted->blocksize)) == NULL) { free(se_ip); free(encrypted); (void) fprintf(stderr, "pgp_push_enc_se_ip: bad alloc\n"); return 0; } encrypted->set_iv(encrypted, iv); encrypted->set_crypt_key(encrypted, &encrypted_pk_sesskey->key[0]); pgp_encrypt_init(encrypted); se_ip->crypt = encrypted; /* And push writer on stack */ pgp_writer_push(output, encrypt_se_ip_writer, NULL, encrypt_se_ip_destroyer, se_ip); /* tidy up */ free(encrypted_pk_sesskey); free(iv); return 1; } static unsigned encrypt_se_ip_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { const unsigned bufsz = 128; encrypt_se_ip_t *se_ip = pgp_writer_get_arg(writer); pgp_output_t *litoutput; pgp_output_t *zoutput; pgp_output_t *output; pgp_memory_t *litmem; pgp_memory_t *zmem; pgp_memory_t *localmem; unsigned ret = 1; pgp_setup_memory_write(&litoutput, &litmem, bufsz); pgp_setup_memory_write(&zoutput, &zmem, bufsz); pgp_setup_memory_write(&output, &localmem, bufsz); /* create literal data packet from source data */ pgp_write_litdata(litoutput, src, (const int)len, PGP_LDT_BINARY); if (pgp_mem_len(litmem) <= len) { (void) fprintf(stderr, "encrypt_se_ip_writer: bad len\n"); return 0; } /* create compressed packet from literal data packet */ pgp_writez(zoutput, pgp_mem_data(litmem), (unsigned)pgp_mem_len(litmem)); /* create SE IP packet set from this compressed literal data */ pgp_write_se_ip_pktset(output, pgp_mem_data(zmem), (unsigned)pgp_mem_len(zmem), se_ip->crypt); if (pgp_mem_len(localmem) <= pgp_mem_len(zmem)) { (void) fprintf(stderr, "encrypt_se_ip_writer: bad comp len\n"); return 0; } /* now write memory to next writer */ ret = stacked_write(writer, pgp_mem_data(localmem), (unsigned)pgp_mem_len(localmem), errors); pgp_memory_free(localmem); pgp_memory_free(zmem); pgp_memory_free(litmem); return ret; } static void encrypt_se_ip_destroyer(pgp_writer_t *writer) { encrypt_se_ip_t *se_ip; se_ip = pgp_writer_get_arg(writer); free(se_ip->crypt); free(se_ip); } unsigned pgp_write_se_ip_pktset(pgp_output_t *output, const uint8_t *data, const unsigned len, pgp_crypt_t *crypted) { pgp_output_t *mdcoutput; pgp_memory_t *mdc; uint8_t hashed[PGP_SHA1_HASH_SIZE]; uint8_t *preamble; const size_t mdcsize = 1 + 1 + PGP_SHA1_HASH_SIZE; size_t preamblesize; size_t bufsize; preamblesize = crypted->blocksize + 2; if ((preamble = calloc(1, preamblesize)) == NULL) { (void) fprintf(stderr, "pgp_write_se_ip_pktset: bad alloc\n"); return 0; } bufsize = preamblesize + len + mdcsize; if (!pgp_write_ptag(output, PGP_PTAG_CT_SE_IP_DATA) || !pgp_write_length(output, (unsigned)(1 + bufsize)) || !pgp_write_scalar(output, PGP_SE_IP_DATA_VERSION, 1)) { free(preamble); return 0; } pgp_random(preamble, crypted->blocksize); preamble[crypted->blocksize] = preamble[crypted->blocksize - 2]; preamble[crypted->blocksize + 1] = preamble[crypted->blocksize - 1]; if (pgp_get_debug_level(__FILE__)) { hexdump(stderr, "preamble", preamble, preamblesize); } /* now construct MDC packet and add to the end of the buffer */ pgp_setup_memory_write(&mdcoutput, &mdc, mdcsize); pgp_calc_mdc_hash(preamble, preamblesize, data, len, hashed); pgp_write_mdc(mdcoutput, hashed); if (pgp_get_debug_level(__FILE__)) { hexdump(stderr, "plaintext", data, len); hexdump(stderr, "mdc", pgp_mem_data(mdc), PGP_SHA1_HASH_SIZE + 1 + 1); } /* and write it out */ pgp_push_enc_crypt(output, crypted); if (pgp_get_debug_level(__FILE__)) { (void) fprintf(stderr, "writing %" PRIsize "u + %u + %" PRIsize "u\n", preamblesize, len, pgp_mem_len(mdc)); } if (!pgp_write(output, preamble, (unsigned)preamblesize) || !pgp_write(output, data, len) || !pgp_write(output, pgp_mem_data(mdc), (unsigned)pgp_mem_len(mdc))) { /* \todo fix cleanup here and in old code functions */ return 0; } pgp_writer_pop(output); /* cleanup */ pgp_teardown_memory_write(mdcoutput, mdc); free(preamble); return 1; } typedef struct { int fd; } writer_fd_t; static unsigned fd_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { writer_fd_t *writerfd; int n; writerfd = pgp_writer_get_arg(writer); n = (int)write(writerfd->fd, src, len); if (n == -1) { PGP_SYSTEM_ERROR_1(errors, PGP_E_W_WRITE_FAILED, "write", "file descriptor %d", writerfd->fd); return 0; } if ((unsigned) n != len) { PGP_ERROR_1(errors, PGP_E_W_WRITE_TOO_SHORT, "file descriptor %d", writerfd->fd); return 0; } return 1; } static void writer_fd_destroyer(pgp_writer_t *writer) { free(pgp_writer_get_arg(writer)); } /** * \ingroup Core_WritersFirst * \brief Write to a File * * Set the writer in output to be a stock writer that writes to a file * descriptor. If another writer has already been set, then that is * first destroyed. * * \param output The output structure * \param fd The file descriptor * */ void pgp_writer_set_fd(pgp_output_t *output, int fd) { writer_fd_t *writer; if ((writer = calloc(1, sizeof(*writer))) == NULL) { (void) fprintf(stderr, "pgp_writer_set_fd: bad alloc\n"); } else { writer->fd = fd; pgp_writer_set(output, fd_writer, NULL, writer_fd_destroyer, writer); } } static unsigned memory_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { pgp_memory_t *mem; __PGP_USED(errors); mem = pgp_writer_get_arg(writer); pgp_memory_add(mem, src, len); return 1; } /** * \ingroup Core_WritersFirst * \brief Write to memory * * Set a memory writer. * * \param output The output structure * \param mem The memory structure * \note It is the caller's responsiblity to call pgp_memory_free(mem) * \sa pgp_memory_free() */ void pgp_writer_set_memory(pgp_output_t *output, pgp_memory_t *mem) { pgp_writer_set(output, memory_writer, NULL, NULL, mem); } /**************************************************************************/ typedef struct { pgp_hash_alg_t hash_alg; pgp_hash_t hash; uint8_t *hashed; } skey_checksum_t; static unsigned skey_checksum_writer(const uint8_t *src, const unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { skey_checksum_t *sum; unsigned ret = 1; sum = pgp_writer_get_arg(writer); /* add contents to hash */ sum->hash.add(&sum->hash, src, len); /* write to next stacked writer */ ret = stacked_write(writer, src, len, errors); /* tidy up and return */ return ret; } static unsigned skey_checksum_finaliser(pgp_error_t **errors, pgp_writer_t *writer) { skey_checksum_t *sum; sum = pgp_writer_get_arg(writer); if (errors && *errors) { printf("errors in skey_checksum_finaliser\n"); } (*sum->hash.finish)(&sum->hash, sum->hashed); return 1; } static void skey_checksum_destroyer(pgp_writer_t *writer) { skey_checksum_t *sum; sum = pgp_writer_get_arg(writer); free(sum); } /** \ingroup Core_WritersNext \param output \param seckey */ void pgp_push_checksum_writer(pgp_output_t *output, pgp_seckey_t *seckey) { /* XXX: push a SHA-1 checksum writer (and change s2k to 254). */ skey_checksum_t *sum; unsigned hashsize; if ((sum = calloc(1, sizeof(*sum))) == NULL) { (void) fprintf(stderr, "pgp_push_checksum_writer: bad alloc\n"); } else { /* configure the arg */ /* Hardcoded SHA1 for just now */ sum->hash_alg = PGP_HASH_SHA1; hashsize = pgp_hash_size(sum->hash_alg); if ((sum->hashed = seckey->checkhash) == NULL) { sum->hashed = seckey->checkhash = calloc(1, hashsize); } /* init the hash */ pgp_hash_any(&sum->hash, sum->hash_alg); if (!sum->hash.init(&sum->hash)) { (void) fprintf(stderr, "pgp_push_checksum_writer: bad hash init\n"); /* just continue and die */ /* XXX - agc - no way to return failure */ } pgp_writer_push(output, skey_checksum_writer, skey_checksum_finaliser, skey_checksum_destroyer, sum); } } /**************************************************************************/ #define MAX_PARTIAL_DATA_LENGTH 1073741824 typedef struct { pgp_crypt_t *crypt; pgp_memory_t *mem_data; pgp_memory_t *litmem; pgp_output_t *litoutput; pgp_memory_t *se_ip_mem; pgp_output_t *se_ip_out; pgp_hash_t hash; } str_enc_se_ip_t; static unsigned str_enc_se_ip_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer); static unsigned str_enc_se_ip_finaliser(pgp_error_t **errors, pgp_writer_t * writer); static void str_enc_se_ip_destroyer(pgp_writer_t *writer); /* */ /** \ingroup Core_WritersNext \param output \param pubkey */ void pgp_push_stream_enc_se_ip(pgp_output_t *output, const pgp_key_t *pubkey, const char *cipher) { pgp_pk_sesskey_t *encrypted_pk_sesskey; str_enc_se_ip_t *se_ip; const unsigned bufsz = 1024; pgp_crypt_t *encrypted; uint8_t *iv; if ((se_ip = calloc(1, sizeof(*se_ip))) == NULL) { (void) fprintf(stderr, "pgp_push_stream_enc_se_ip: bad alloc\n"); return; } encrypted_pk_sesskey = pgp_create_pk_sesskey(pubkey, cipher); pgp_write_pk_sesskey(output, encrypted_pk_sesskey); /* Setup the se_ip */ if ((encrypted = calloc(1, sizeof(*encrypted))) == NULL) { free(se_ip); (void) fprintf(stderr, "pgp_push_stream_enc_se_ip: bad alloc\n"); return; } pgp_crypt_any(encrypted, encrypted_pk_sesskey->symm_alg); if ((iv = calloc(1, encrypted->blocksize)) == NULL) { free(encrypted); free(se_ip); (void) fprintf(stderr, "pgp_push_stream_enc_se_ip: bad alloc\n"); return; } encrypted->set_iv(encrypted, iv); encrypted->set_crypt_key(encrypted, &encrypted_pk_sesskey->key[0]); pgp_encrypt_init(encrypted); se_ip->crypt = encrypted; se_ip->mem_data = pgp_memory_new(); pgp_memory_init(se_ip->mem_data, bufsz); se_ip->litmem = NULL; se_ip->litoutput = NULL; pgp_setup_memory_write(&se_ip->se_ip_out, &se_ip->se_ip_mem, bufsz); /* And push writer on stack */ pgp_writer_push(output, str_enc_se_ip_writer, str_enc_se_ip_finaliser, str_enc_se_ip_destroyer, se_ip); /* tidy up */ free(encrypted_pk_sesskey); free(iv); } /* calculate the partial data length */ static unsigned partial_data_len(unsigned len) { unsigned mask; int i; if (len == 0) { (void) fprintf(stderr, "partial_data_len: 0 len\n"); return 0; } if (len > MAX_PARTIAL_DATA_LENGTH) { return MAX_PARTIAL_DATA_LENGTH; } mask = MAX_PARTIAL_DATA_LENGTH; for (i = 0; i <= 30; i++) { if (mask & len) { break; } mask >>= 1; } return mask; } static unsigned write_partial_len(pgp_output_t *output, unsigned len) { /* len must be a power of 2 from 0 to 30 */ uint8_t c; int i; for (i = 0; i <= 30; i++) { if ((len >> i) & 1) { break; } } c = 224 + i; return pgp_write(output, &c, 1); } static unsigned stream_write_litdata(pgp_output_t *output, const uint8_t *data, unsigned len) { size_t pdlen; while (len > 0) { pdlen = partial_data_len(len); write_partial_len(output, (unsigned)pdlen); pgp_write(output, data, (unsigned)pdlen); data += pdlen; len -= (unsigned)pdlen; } return 1; } static unsigned stream_write_litdata_first(pgp_output_t *output, const uint8_t *data, unsigned len, const pgp_litdata_enum type) { /* \todo add filename */ /* \todo add date */ /* \todo do we need to check text data for line endings ? */ unsigned sz_towrite; size_t sz_pd; sz_towrite = 1 + 1 + 4 + len; sz_pd = (size_t)partial_data_len(sz_towrite); if (sz_pd < 512) { (void) fprintf(stderr, "stream_write_litdata_first: bad sz_pd\n"); return 0; } pgp_write_ptag(output, PGP_PTAG_CT_LITDATA); write_partial_len(output, (unsigned)sz_pd); pgp_write_scalar(output, (unsigned)type, 1); pgp_write_scalar(output, 0, 1); pgp_write_scalar(output, 0, 4); pgp_write(output, data, (unsigned)(sz_pd - 6)); data += (sz_pd - 6); sz_towrite -= (unsigned)sz_pd; return stream_write_litdata(output, data, (unsigned)sz_towrite); } static unsigned stream_write_litdata_last(pgp_output_t *output, const uint8_t *data, unsigned len) { pgp_write_length(output, len); return pgp_write(output, data, len); } static unsigned stream_write_se_ip(pgp_output_t *output, const uint8_t *data, unsigned len, str_enc_se_ip_t *se_ip) { size_t pdlen; while (len > 0) { pdlen = partial_data_len(len); write_partial_len(output, (unsigned)pdlen); pgp_push_enc_crypt(output, se_ip->crypt); pgp_write(output, data, (unsigned)pdlen); pgp_writer_pop(output); se_ip->hash.add(&se_ip->hash, data, (unsigned)pdlen); data += pdlen; len -= (unsigned)pdlen; } return 1; } static unsigned stream_write_se_ip_first(pgp_output_t *output, const uint8_t *data, unsigned len, str_enc_se_ip_t *se_ip) { uint8_t *preamble; size_t blocksize; size_t preamblesize; size_t sz_towrite; size_t sz_pd; blocksize = se_ip->crypt->blocksize; preamblesize = blocksize + 2; sz_towrite = preamblesize + 1 + len; if ((preamble = calloc(1, preamblesize)) == NULL) { (void) fprintf(stderr, "stream_write_se_ip_first: bad alloc\n"); return 0; } sz_pd = (size_t)partial_data_len((unsigned)sz_towrite); if (sz_pd < 512) { free(preamble); (void) fprintf(stderr, "stream_write_se_ip_first: bad sz_pd\n"); return 0; } pgp_write_ptag(output, PGP_PTAG_CT_SE_IP_DATA); write_partial_len(output, (unsigned)sz_pd); pgp_write_scalar(output, PGP_SE_IP_DATA_VERSION, 1); pgp_push_enc_crypt(output, se_ip->crypt); pgp_random(preamble, blocksize); preamble[blocksize] = preamble[blocksize - 2]; preamble[blocksize + 1] = preamble[blocksize - 1]; pgp_hash_any(&se_ip->hash, PGP_HASH_SHA1); if (!se_ip->hash.init(&se_ip->hash)) { free(preamble); (void) fprintf(stderr, "stream_write_se_ip_first: bad hash init\n"); return 0; } pgp_write(output, preamble, (unsigned)preamblesize); se_ip->hash.add(&se_ip->hash, preamble, (unsigned)preamblesize); pgp_write(output, data, (unsigned)(sz_pd - preamblesize - 1)); se_ip->hash.add(&se_ip->hash, data, (unsigned)(sz_pd - preamblesize - 1)); data += (sz_pd - preamblesize - 1); sz_towrite -= sz_pd; pgp_writer_pop(output); stream_write_se_ip(output, data, (unsigned)sz_towrite, se_ip); free(preamble); return 1; } static unsigned stream_write_se_ip_last(pgp_output_t *output, const uint8_t *data, unsigned len, str_enc_se_ip_t *se_ip) { pgp_output_t *mdcoutput; pgp_memory_t *mdcmem; const size_t mdcsize = 1 + 1 + PGP_SHA1_HASH_SIZE; uint8_t c; uint8_t hashed[PGP_SHA1_HASH_SIZE]; size_t bufsize = len + mdcsize; se_ip->hash.add(&se_ip->hash, data, len); /* MDC packet tag */ c = MDC_PKT_TAG; se_ip->hash.add(&se_ip->hash, &c, 1); /* MDC packet len */ c = PGP_SHA1_HASH_SIZE; se_ip->hash.add(&se_ip->hash, &c, 1); /* finish */ se_ip->hash.finish(&se_ip->hash, hashed); pgp_setup_memory_write(&mdcoutput, &mdcmem, mdcsize); pgp_write_mdc(mdcoutput, hashed); /* write length of last se_ip chunk */ pgp_write_length(output, (unsigned)bufsize); /* encode everting */ pgp_push_enc_crypt(output, se_ip->crypt); pgp_write(output, data, len); pgp_write(output, pgp_mem_data(mdcmem), (unsigned)pgp_mem_len(mdcmem)); pgp_writer_pop(output); pgp_teardown_memory_write(mdcoutput, mdcmem); return 1; } static unsigned str_enc_se_ip_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { str_enc_se_ip_t *se_ip; unsigned ret; size_t datalength; se_ip = pgp_writer_get_arg(writer); ret = 1; if (se_ip->litoutput == NULL) { /* first literal data chunk is not yet written */ pgp_memory_add(se_ip->mem_data, src, len); datalength = pgp_mem_len(se_ip->mem_data); /* 4.2.2.4. Partial Body Lengths */ /* The first partial length MUST be at least 512 octets long. */ if (datalength < 512) { return 1; /* will wait for more data or * end of stream */ } pgp_setup_memory_write(&se_ip->litoutput, &se_ip->litmem, datalength + 32); stream_write_litdata_first(se_ip->litoutput, pgp_mem_data(se_ip->mem_data), (unsigned)datalength, PGP_LDT_BINARY); stream_write_se_ip_first(se_ip->se_ip_out, pgp_mem_data(se_ip->litmem), (unsigned)pgp_mem_len(se_ip->litmem), se_ip); } else { stream_write_litdata(se_ip->litoutput, src, len); stream_write_se_ip(se_ip->se_ip_out, pgp_mem_data(se_ip->litmem), (unsigned)pgp_mem_len(se_ip->litmem), se_ip); } /* now write memory to next writer */ ret = stacked_write(writer, pgp_mem_data(se_ip->se_ip_mem), (unsigned)pgp_mem_len(se_ip->se_ip_mem), errors); pgp_memory_clear(se_ip->litmem); pgp_memory_clear(se_ip->se_ip_mem); return ret; } /* write last chunk of data */ static unsigned str_enc_se_ip_finaliser(pgp_error_t **errors, pgp_writer_t *writer) { str_enc_se_ip_t *se_ip; se_ip = pgp_writer_get_arg(writer); if (se_ip->litoutput == NULL) { /* first literal data chunk was not written */ /* so we know the total length of data, write a simple packet */ /* create literal data packet from buffered data */ pgp_setup_memory_write(&se_ip->litoutput, &se_ip->litmem, pgp_mem_len(se_ip->mem_data) + 32); pgp_write_litdata(se_ip->litoutput, pgp_mem_data(se_ip->mem_data), (const int)pgp_mem_len(se_ip->mem_data), PGP_LDT_BINARY); /* create SE IP packet set from this literal data */ pgp_write_se_ip_pktset(se_ip->se_ip_out, pgp_mem_data(se_ip->litmem), (unsigned)pgp_mem_len(se_ip->litmem), se_ip->crypt); } else { /* finish writing */ stream_write_litdata_last(se_ip->litoutput, NULL, 0); stream_write_se_ip_last(se_ip->se_ip_out, pgp_mem_data(se_ip->litmem), (unsigned)pgp_mem_len(se_ip->litmem), se_ip); } /* now write memory to next writer */ return stacked_write(writer, pgp_mem_data(se_ip->se_ip_mem), (unsigned)pgp_mem_len(se_ip->se_ip_mem), errors); } static void str_enc_se_ip_destroyer(pgp_writer_t *writer) { str_enc_se_ip_t *se_ip; se_ip = pgp_writer_get_arg(writer); pgp_memory_free(se_ip->mem_data); pgp_teardown_memory_write(se_ip->litoutput, se_ip->litmem); pgp_teardown_memory_write(se_ip->se_ip_out, se_ip->se_ip_mem); se_ip->crypt->decrypt_finish(se_ip->crypt); free(se_ip->crypt); free(se_ip); }