mirror of
https://github.com/deltachat/deltachat-core.git
synced 2025-10-06 03:50:08 +02:00
1797 lines
43 KiB
C
1797 lines
43 KiB
C
/*-
|
|
* 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-netpgp.h"
|
|
|
|
#ifdef HAVE_SYS_CDEFS_H
|
|
#include <sys/cdefs.h>
|
|
#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 <sys/types.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_OPENSSL_CAST_H
|
|
#include <openssl/cast.h>
|
|
#endif
|
|
|
|
#include "create-netpgp.h"
|
|
#include "writer-netpgp.h"
|
|
#include "keyring-netpgp.h"
|
|
#include "signature-netpgp.h"
|
|
#include "packet-netpgp.h"
|
|
#include "packet-parse.h"
|
|
#include "readerwriter-netpgp.h"
|
|
#include "memory-netpgp.h"
|
|
#include "netpgpdefs.h"
|
|
#include "version-netpgp.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 <cr><lf> 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);
|
|
}
|