An improved fork of NetBSD's OpenPGP implementation; maintained by the p≡p foundation.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
netpgp-et/src/packet-parse.c

3780 lines
104 KiB

/*-
* 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
* \brief Parser for OpenPGP packets
*/
#include "config.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$");
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_OPENSSL_CAST_H
#include <openssl/cast.h>
#endif
#ifdef HAVE_GSKSSL
#include <gskcms.h>
#endif
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include "packet.h"
#include "packet-parse.h"
#include "keyring.h"
#include "errors.h"
#include "packet-show.h"
#include "create.h"
#include "readerwriter.h"
#include "netpgpdefs.h"
#include "crypto.h"
#include "netpgpdigest.h"
#define ERRP(cbinfo, cont, err) do { \
cont.u.error = err; \
CALLBACK(PGP_PARSER_ERROR, cbinfo, &cont); \
return 0; \
/*NOTREACHED*/ \
} while(/*CONSTCOND*/0)
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) (((a)>(b))?(a):(b))
#endif
/**
* limread_data reads the specified amount of the subregion's data
* into a data_t structure
*
* \param data Empty structure which will be filled with data
* \param len Number of octets to read
* \param subregion
* \param stream How to parse
*
* \return 1 on success, 0 on failure
*/
static int
limread_data(pgp_data_t *data, unsigned len,
pgp_region_t *subregion, pgp_stream_t *stream)
{
data->len = len;
if (subregion->length - subregion->readc < len) {
(void) fprintf(stderr, "limread_data: bad length\n");
return 0;
}
data->contents = calloc(1, data->len);
if (!data->contents) {
return 0;
}
return pgp_limited_read(stream, data->contents, data->len, subregion,
&stream->errors, &stream->readinfo, &stream->cbinfo);
}
/**
* read_data reads the remainder of the subregion's data
* into a data_t structure
*
* \param data
* \param subregion
* \param stream
*
* \return 1 on success, 0 on failure
*/
static int
read_data(pgp_data_t *data, pgp_region_t *region, pgp_stream_t *stream)
{
int cc;
cc = region->length - region->readc;
return (cc >= 0) ? limread_data(data, (unsigned)cc, region, stream) : 0;
}
/**
* Reads the remainder of the subregion as a string.
* It is the user's responsibility to free the memory allocated here.
*/
static int
read_unsig_str(uint8_t **str, pgp_region_t *subregion,
pgp_stream_t *stream)
{
size_t len;
len = subregion->length - subregion->readc;
if ((*str = calloc(1, len + 1)) == NULL) {
return 0;
}
if (len &&
!pgp_limited_read(stream, *str, len, subregion, &stream->errors,
&stream->readinfo, &stream->cbinfo)) {
return 0;
}
(*str)[len] = '\0';
return 1;
}
static int
read_string(char **str, pgp_region_t *subregion, pgp_stream_t *stream)
{
return read_unsig_str((uint8_t **) str, subregion, stream);
}
void
pgp_init_subregion(pgp_region_t *subregion, pgp_region_t *region)
{
(void) memset(subregion, 0x0, sizeof(*subregion));
subregion->parent = region;
}
/*
* XXX: replace pgp_ptag_t with something more appropriate for limiting reads
*/
/* data from partial blocks is queued up in virtual block in stream */
static int
read_partial_data(pgp_stream_t *stream,
pgp_reader_t *readinfo,
void *dest, size_t length)
{
unsigned n;
if (pgp_get_debug_level(__FILE__)) {
(void) fprintf(stderr, "fd_reader: coalesced data, off %d\n",
readinfo->virtualoff);
}
n = MIN(readinfo->virtualc - readinfo->virtualoff, (unsigned)length);
(void) memcpy(dest, &readinfo->virtualpkt[readinfo->virtualoff], n);
readinfo->virtualoff += n;
if (readinfo->virtualoff == readinfo->virtualc) {
free(readinfo->virtualpkt);
readinfo->virtualpkt = NULL;
readinfo->virtualc = readinfo->virtualoff = 0;
}
return (int)n;
}
/**
* low-level function to read data from reader function
*
* Use this function, rather than calling the reader directly.
*
* If the accumulate flag is set in *stream, the function
* adds the read data to the accumulated data, and updates
* the accumulated length. This is useful if, for example,
* the application wants access to the raw data as well as the
* parsed data.
*
* This function will also try to read the entire amount asked for, but not
* if it is over INT_MAX. Obviously many callers will know that they
* never ask for that much and so can avoid the extra complexity of
* dealing with return codes and filled-in lengths.
*
* \param *dest
* \param *plength
* \param flags
* \param *stream
*
* \return PGP_R_OK
* \return PGP_R_PARTIAL_READ
* \return PGP_R_EOF
* \return PGP_R_EARLY_EOF
*
* \sa #pgp_reader_ret_t for details of return codes
*/
static int
sub_base_read(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
{
size_t n;
/* reading more than this would look like an error */
if (length > INT_MAX)
length = INT_MAX;
for (n = 0; n < length;) {
int r;
if (!readinfo->coalescing && readinfo->virtualc && readinfo->virtualoff < readinfo->virtualc) {
r = read_partial_data(stream, readinfo, (char*) dest + n, length - n);
}else{
r = readinfo->reader(stream, (char *) dest + n,
length - n, errors,
readinfo, cbinfo);
}
if (r > (int)(length - n)) {
(void) fprintf(stderr, "sub_base_read: bad read\n");
return 0;
}
if (r < 0) {
return r;
}
if (r == 0) {
break;
}
n += (unsigned)r;
}
if (n == 0) {
return 0;
}
if (readinfo->accumulate) {
if (readinfo->asize < readinfo->alength) {
(void) fprintf(stderr, "sub_base_read: bad size\n");
return 0;
}
if (readinfo->alength + n > readinfo->asize) {
uint8_t *temp;
readinfo->asize = (readinfo->asize * 2) + (unsigned)n;
temp = realloc(readinfo->accumulated, readinfo->asize);
if (temp == NULL) {
(void) fprintf(stderr,
"sub_base_read: bad alloc\n");
return 0;
}
readinfo->accumulated = temp;
}
if (readinfo->asize < readinfo->alength + n) {
(void) fprintf(stderr, "sub_base_read: bad realloc\n");
return 0;
}
(void) memcpy(readinfo->accumulated + readinfo->alength, dest,
n);
}
/* we track length anyway, because it is used for packet offsets */
readinfo->alength += (unsigned)n;
/* and also the position */
readinfo->position += (unsigned)n;
return (int)n;
}
int
pgp_stacked_read(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
{
return sub_base_read(stream, dest, length, errors, readinfo->next, cbinfo);
}
/* This will do a full read so long as length < MAX_INT */
static int
base_read(uint8_t *dest, size_t length, pgp_stream_t *stream)
{
return sub_base_read(stream, dest, length, &stream->errors, &stream->readinfo,
&stream->cbinfo);
}
/*
* Read a full size_t's worth. If the return is < than length, then
* *last_read tells you why - < 0 for an error, == 0 for EOF
*/
static size_t
full_read(pgp_stream_t *stream, uint8_t *dest,
size_t length,
int *last_read,
pgp_error_t **errors,
pgp_reader_t *readinfo,
pgp_cbdata_t *cbinfo)
{
size_t t;
int r = 0; /* preset in case some loon calls with length
* == 0 */
for (t = 0; t < length;) {
r = sub_base_read(stream, dest + t, length - t, errors, readinfo,
cbinfo);
if (r <= 0) {
*last_read = r;
return t;
}
t += (size_t)r;
}
*last_read = r;
return t;
}
/** Read a scalar value of selected length from reader.
*
* Read an unsigned scalar value from reader in Big Endian representation.
*
* This function does not know or care about packet boundaries. It
* also assumes that an EOF is an error.
*
* \param *result The scalar value is stored here
* \param *reader Our reader
* \param length How many bytes to read
* \return 1 on success, 0 on failure
*/
static unsigned
_read_scalar(unsigned *result, unsigned length,
pgp_stream_t *stream)
{
unsigned t = 0;
if (length > sizeof(*result)) {
(void) fprintf(stderr, "_read_scalar: bad length\n");
return 0;
}
while (length--) {
uint8_t c;
int r;
r = base_read(&c, 1, stream);
if (r != 1)
return 0;
t = (t << 8) + c;
}
*result = t;
return 1;
}
/**
* \ingroup Core_ReadPackets
* \brief Read bytes from a region within the packet.
*
* Read length bytes into the buffer pointed to by *dest.
* Make sure we do not read over the packet boundary.
* Updates the Packet Tag's pgp_ptag_t::readc.
*
* If length would make us read over the packet boundary, or if
* reading fails, we call the callback with an error.
*
* Note that if the region is indeterminate, this can return a short
* read - check region->last_read for the length. EOF is indicated by
* a success return and region->last_read == 0 in this case (for a
* region of known length, EOF is an error).
*
* This function makes sure to respect packet boundaries.
*
* \param dest The destination buffer
* \param length How many bytes to read
* \param region Pointer to packet region
* \param errors Error stack
* \param readinfo Reader info
* \param cbinfo Callback info
* \return 1 on success, 0 on error
*/
unsigned
pgp_limited_read(pgp_stream_t *stream, uint8_t *dest,
size_t length,
pgp_region_t *region,
pgp_error_t **errors,
pgp_reader_t *readinfo,
pgp_cbdata_t *cbinfo)
{
size_t r;
int lr;
if (!region->indeterminate &&
region->readc + length > region->length) {
PGP_ERROR_1(errors, PGP_E_P_NOT_ENOUGH_DATA, "%s",
"Not enough data");
return 0;
}
r = full_read(stream, dest, length, &lr, errors, readinfo, cbinfo);
if (lr < 0) {
PGP_ERROR_1(errors, PGP_E_R_READ_FAILED, "%s", "Read failed");
return 0;
}
if (!region->indeterminate && r != length) {
PGP_ERROR_1(errors, PGP_E_R_READ_FAILED, "%s", "Read failed");
return 0;
}
region->last_read = (unsigned)r;
do {
region->readc += (unsigned)r;
if (region->parent && region->length > region->parent->length) {
(void) fprintf(stderr,
"ops_limited_read: bad length\n");
return 0;
}
} while ((region = region->parent) != NULL);
return 1;
}
/**
\ingroup Core_ReadPackets
\brief Call pgp_limited_read on next in stack
*/
unsigned
pgp_stacked_limited_read(pgp_stream_t *stream, uint8_t *dest, unsigned length,
pgp_region_t *region,
pgp_error_t **errors,
pgp_reader_t *readinfo,
pgp_cbdata_t *cbinfo)
{
return pgp_limited_read(stream, dest, length, region, errors,
readinfo->next, cbinfo);
}
static unsigned
limread(uint8_t *dest, unsigned length,
pgp_region_t *region, pgp_stream_t *info)
{
return pgp_limited_read(info, dest, length, region, &info->errors,
&info->readinfo, &info->cbinfo);
}
#if 0
static unsigned
exact_limread(uint8_t *dest, unsigned len,
pgp_region_t *region,
pgp_stream_t *stream)
{
unsigned ret;
stream->exact_read = 1;
ret = limread(dest, len, region, stream);
stream->exact_read = 0;
return ret;
}
#endif
/** Skip over length bytes of this packet.
*
* Calls limread() to skip over some data.
*
* This function makes sure to respect packet boundaries.
*
* \param length How many bytes to skip
* \param *region Pointer to packet region
* \param *stream How to parse
* \return 1 on success, 0 on error (calls the cb with PGP_PARSER_ERROR in limread()).
*/
static int
limskip(unsigned length, pgp_region_t *region, pgp_stream_t *stream)
{
uint8_t buf[NETPGP_BUFSIZ];
while (length > 0) {
unsigned n = length % NETPGP_BUFSIZ;
if (!limread(buf, n, region, stream)) {
return 0;
}
length -= n;
}
return 1;
}
/** Read a scalar.
*
* Read a big-endian scalar of length bytes, respecting packet
* boundaries (by calling limread() to read the raw data).
*
* This function makes sure to respect packet boundaries.
*
* \param *dest The scalar value is stored here
* \param length How many bytes make up this scalar (at most 4)
* \param *region Pointer to current packet region
* \param *stream How to parse
* \param *cb The callback
* \return 1 on success, 0 on error (calls the cb with PGP_PARSER_ERROR in limread()).
*
* \see RFC4880 3.1
*/
static int
limread_scalar(unsigned *dest,
unsigned len,
pgp_region_t *region,
pgp_stream_t *stream)
{
uint8_t c[4] = "";
unsigned t;
unsigned n;
if (len > 4) {
(void) fprintf(stderr, "limread_scalar: bad length\n");
return 0;
}
/*LINTED*/
if (/*CONSTCOND*/sizeof(*dest) < 4) {
(void) fprintf(stderr, "limread_scalar: bad dest\n");
return 0;
}
if (!limread(c, len, region, stream)) {
return 0;
}
for (t = 0, n = 0; n < len; ++n) {
t = (t << 8) + c[n];
}
*dest = t;
return 1;
}
/** Read a scalar.
*
* Read a big-endian scalar of length bytes, respecting packet
* boundaries (by calling limread() to read the raw data).
*
* The value read is stored in a size_t, which is a different size
* from an unsigned on some platforms.
*
* This function makes sure to respect packet boundaries.
*
* \param *dest The scalar value is stored here
* \param length How many bytes make up this scalar (at most 4)
* \param *region Pointer to current packet region
* \param *stream How to parse
* \param *cb The callback
* \return 1 on success, 0 on error (calls the cb with PGP_PARSER_ERROR in limread()).
*
* \see RFC4880 3.1
*/
static int
limread_size_t(size_t *dest,
unsigned length,
pgp_region_t *region,
pgp_stream_t *stream)
{
unsigned tmp;
/*
* Note that because the scalar is at most 4 bytes, we don't care if
* size_t is bigger than usigned
*/
if (!limread_scalar(&tmp, length, region, stream))
return 0;
*dest = tmp;
return 1;
}
/** Read a timestamp.
*
* Timestamps in OpenPGP are unix time, i.e. seconds since The Epoch (1.1.1970). They are stored in an unsigned scalar
* of 4 bytes.
*
* This function reads the timestamp using limread_scalar().
*
* This function makes sure to respect packet boundaries.
*
* \param *dest The timestamp is stored here
* \param *ptag Pointer to current packet's Packet Tag.
* \param *reader Our reader
* \param *cb The callback
* \return see limread_scalar()
*
* \see RFC4880 3.5
*/
static int
limited_read_time(time_t *dest, pgp_region_t *region,
pgp_stream_t *stream)
{
uint8_t c;
time_t mytime = 0;
int i;
/*
* Cannot assume that time_t is 4 octets long -
* SunOS 5.10 and NetBSD both have 64-bit time_ts.
*/
if (/* CONSTCOND */sizeof(time_t) == 4) {
return limread_scalar((unsigned *)(void *)dest, 4, region, stream);
}
for (i = 0; i < 4; i++) {
if (!limread(&c, 1, region, stream)) {
return 0;
}
mytime = (mytime << 8) + c;
}
*dest = mytime;
return 1;
}
/**
* \ingroup Core_MPI
* Read a multiprecision integer.
*
* Large numbers (multiprecision integers, MPI) are stored in OpenPGP in two parts. First there is a 2 byte scalar
* indicating the length of the following MPI in Bits. Then follow the bits that make up the actual number, most
* significant bits first (Big Endian). The most significant bit in the MPI is supposed to be 1 (unless the MPI is
* encrypted - then it may be different as the bit count refers to the plain text but the bits are encrypted).
*
* Unused bits (i.e. those filling up the most significant byte from the left to the first bits that counts) are
* supposed to be cleared - I guess. XXX - does anything actually say so?
*
* This function makes sure to respect packet boundaries.
*
* \param **pgn return the integer there - the BIGNUM is created by BN_bin2bn() and probably needs to be freed
* by the caller XXX right ben?
* \param *ptag Pointer to current packet's Packet Tag.
* \param *reader Our reader
* \param *cb The callback
* \return 1 on success, 0 on error (by limread_scalar() or limread() or if the MPI is not properly formed (XXX
* see comment below - the callback is called with a PGP_PARSER_ERROR in case of an error)
*
* \see RFC4880 3.2
*/
static int
#ifdef HAVE_OPENSSL
limread_mpi(BIGNUM **pbn, pgp_region_t *region, pgp_stream_t *stream)
#endif
#ifdef HAVE_GSKSSL
limread_mpi(gsk_buffer *pbn, pgp_region_t *region, pgp_stream_t *stream)
#endif
{
uint8_t buf[NETPGP_BUFSIZ] = "";
/* an MPI has a 2 byte length part.
* Length is given in bits, so the
* largest we should ever need for
* the buffer is NETPGP_BUFSIZ bytes. */
unsigned length;
unsigned nonzero;
unsigned ret;
//stream->reading_mpi_len = 1;
ret = (unsigned)limread_scalar(&length, 2, region, stream);
//stream->reading_mpi_len = 0;
if (!ret)
return 0;
nonzero = length & 7; /* there should be this many zero bits in the
* MS byte */
if (!nonzero)
nonzero = 8;
length = (length + 7) / 8;
if (length == 0) {
/* if we try to read a length of 0, then fail */
if (pgp_get_debug_level(__FILE__)) {
(void) fprintf(stderr, "limread_mpi: 0 length\n");
}
return 0;
}
if (length > NETPGP_BUFSIZ) {
(void) fprintf(stderr, "limread_mpi: bad length\n");
return 0;
}
if (!limread(buf, length, region, stream)) {
return 0;
}
if (((unsigned)buf[0] >> nonzero) != 0 ||
!((unsigned)buf[0] & (1U << (nonzero - 1U)))) {
PGP_ERROR_1(&stream->errors, PGP_E_P_MPI_FORMAT_ERROR,
"%s", "MPI Format error");
/* XXX: Ben, one part of
* this constraint does
* not apply to
* encrypted MPIs the
* draft says. -- peter */
return 0;
}
#ifdef HAVE_OPENSSL
*pbn = BN_bin2bn(buf, (int)length, NULL);
#endif
#ifdef HAVE_GSKSSL
pbn->length=length;
pbn->data=malloc(length);
memcpy(pbn->data,buf,length);
#endif
printf("%s reached end\n",__FUNCTION__);
return 1;
}
static unsigned read_new_length(unsigned *, pgp_stream_t *);
/* allocate space, read, and stash data away in a virtual pkt */
static void
streamread(pgp_stream_t *stream, unsigned c)
{
int cc;
pgp_reader_t *readinfo = &stream->readinfo;
readinfo->virtualpkt = realloc(readinfo->virtualpkt, readinfo->virtualc + c);
cc = readinfo->reader(stream, &readinfo->virtualpkt[readinfo->virtualc],
c, &stream->errors, readinfo, &stream->cbinfo);
readinfo->virtualc += cc;
}
/* coalesce all the partial blocks together */
static int
coalesce_blocks(pgp_stream_t *stream, unsigned length)
{
unsigned c;
unsigned r;
pgp_reader_t *readinfo = &stream->readinfo;
readinfo->coalescing = 1;
/* already read a partial block length - prime the array */
streamread(stream, length);
while ((r = read_new_length(&c, stream)) && readinfo->partial_read) {
/* length we read is partial - add to end of array */
streamread(stream, c);
}
/* not partial - add the last extent to the end of the array */
if(r > 0) streamread(stream, c);
readinfo->coalescing = 0;
return 1;
}
/** Read some data with a New-Format length from reader.
*
* \sa Internet-Draft RFC4880.txt Section 4.2.2
*
* \param *length Where the decoded length will be put
* \param *stream How to parse
* \return 1 if OK, else 0
*
*/
static unsigned
read_new_length(unsigned *length, pgp_stream_t *stream)
{
uint8_t c;
pgp_reader_t *readinfo = &stream->readinfo;
readinfo->partial_read = 0;
if (base_read(&c, 1, stream) != 1) {
return 0;
}
if (c < 192) {
/* 1. One-octet packet */
*length = c;
return 1;
}
if (c < 224) {
/* 2. Two-octet packet */
unsigned t = (c - 192) << 8;
if (base_read(&c, 1, stream) != 1) {
return 0;
}
*length = t + c + 192;
return 1;
}
if (c < 255) {
/* 3. Partial Body Length */
readinfo->partial_read = 1;
*length = 1 << (c & 0x1f);
if (!readinfo->coalescing) {
/* we have been called from coalesce_blocks -
* just return with the partial length */
coalesce_blocks(stream, *length);
*length = readinfo->virtualc;
}
return 1;
}
/* 4. Five-Octet packet */
return _read_scalar(length, 4, stream);
}
/** Read the length information for a new format Packet Tag.
*
* New style Packet Tags encode the length in one to five octets. This function reads the right amount of bytes and
* decodes it to the proper length information.
*
* This function makes sure to respect packet boundaries.
*
* \param *length return the length here
* \param *ptag Pointer to current packet's Packet Tag.
* \param *reader Our reader
* \param *cb The callback
* \return 1 on success, 0 on error (by limread_scalar() or limread() or if the MPI is not properly formed (XXX
* see comment below)
*
* \see RFC4880 4.2.2
* \see pgp_ptag_t
*/
static int
limited_read_new_length(unsigned *length, pgp_region_t *region,
pgp_stream_t *stream)
{
uint8_t c = 0x0;
pgp_reader_t *readinfo = &stream->readinfo;
if (!limread(&c, 1, region, stream)) {
return 0;
}
if (c < 192) {
*length = c;
return 1;
}
if (c < 224) {
unsigned t = (c - 192) << 8;
if (!limread(&c, 1, region, stream)) {
return 0;
}
*length = t + c + 192;
return 1;
}
if (c < 255) {
readinfo->partial_read = 1;
*length = 1 << (c & 0x1f);
if (!readinfo->coalescing) {
/* we have been called from coalesce_blocks -
* just return with the partial length */
coalesce_blocks(stream, *length);
*length = readinfo->virtualc;
}
return 1;
}
return limread_scalar(length, 4, region, stream);
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
void
pgp_data_free(pgp_data_t *data)
{
free(data->contents);
data->contents = NULL;
data->len = 0;
}
void
pgp_data_dup(pgp_data_t *dst, const pgp_data_t *src)
{
dst->contents = calloc(1, src->len);
memcpy(dst->contents, src->contents, src->len);
dst->len = src->len;
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
static void
string_free(char **str)
{
free(*str);
*str = NULL;
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
/* ! Free packet memory, set pointer to NULL */
void
pgp_subpacket_free(pgp_subpacket_t *packet)
{
free(packet->raw);
packet->raw = NULL;
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
static void
headers_free(pgp_headers_t *headers)
{
unsigned n;
for (n = 0; n < headers->headerc; ++n) {
free(headers->headers[n].key);
free(headers->headers[n].value);
}
free(headers->headers);
headers->headers = NULL;
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
static void
cleartext_trailer_free(struct pgp_hash_t **trailer)
{
free(*trailer);
*trailer = NULL;
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
static void
cmd_get_passphrase_free(pgp_seckey_passphrase_t *skp)
{
if (skp->passphrase && *skp->passphrase) {
free(*skp->passphrase);
*skp->passphrase = NULL;
}
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
#ifdef HAVE_OPENSSL
static void
free_BN(BIGNUM **pp)
{
BN_free(*pp);
*pp = NULL;
}
#endif
#ifdef HAVE_GSKSSL
static void
free_BN(gsk_buffer *buf)
{
gsk_free_buffer(buf);
}
#endif
#ifdef HAVE_OPENSSL
static void
dup_BN(BIGNUM **dst, const BIGNUM *src)
{
*dst = BN_dup(src);
}
#endif
#ifdef HAVE_GSKSSL
static void
dup_BN(gsk_buffer *dst, gsk_buffer src)
{
gsk_copy_buffer(&src,dst);
}
#endif
void
copy_sig_info(pgp_sig_info_t *dst, const pgp_sig_info_t *src)
{
printf("%s...\n",__FUNCTION__);
(void) memcpy(dst, src, sizeof(*src));
printf("%s: src->v4_hashlen %d\n",__FUNCTION__,src->v4_hashlen);
if ((dst->v4_hashed = calloc(1, src->v4_hashlen)) == NULL) {
(void) fprintf(stderr, "copy_sig_info: bad alloc\n");
} else {
(void) memcpy(dst->v4_hashed, src->v4_hashed, src->v4_hashlen);
}
switch (src->key_alg) {
case PGP_PKA_RSA:
case PGP_PKA_RSA_SIGN_ONLY:
dup_BN(&dst->sig.rsa.sig, src->sig.rsa.sig);
break;
case PGP_PKA_DSA:
dup_BN(&dst->sig.dsa.r, src->sig.dsa.r);
dup_BN(&dst->sig.dsa.s, src->sig.dsa.s);
break;
case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
dup_BN(&dst->sig.elgamal.r, src->sig.elgamal.r);
dup_BN(&dst->sig.elgamal.s, src->sig.elgamal.s);
break;
case PGP_PKA_PRIVATE00:
case PGP_PKA_PRIVATE01:
case PGP_PKA_PRIVATE02:
case PGP_PKA_PRIVATE03:
case PGP_PKA_PRIVATE04:
case PGP_PKA_PRIVATE05:
case PGP_PKA_PRIVATE06:
case PGP_PKA_PRIVATE07:
case PGP_PKA_PRIVATE08:
case PGP_PKA_PRIVATE09:
case PGP_PKA_PRIVATE10:
pgp_data_dup(&dst->sig.unknown, &src->sig.unknown);
break;
default:
(void) fprintf(stderr, "sig_dup: bad sig type\n");
}
}
/**
* \ingroup Core_Create
* \brief Free the memory used when parsing a signature
* \param sig
*/
void
pgp_free_sig_info(pgp_sig_info_t *info)
{
free(info->v4_hashed);
switch (info->key_alg) {
case PGP_PKA_RSA:
case PGP_PKA_RSA_SIGN_ONLY:
#ifdef HAVE_OPENSSL_BN_H
free_BN(&info->sig.rsa.sig);
#endif
break;
case PGP_PKA_DSA:
#ifdef HAVE_OPENSSL_BN_H
free_BN(&info->sig.dsa.r);
free_BN(&info->sig.dsa.s);
#endif
break;
case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
#ifdef HAVE_OPENSSL_BN_H
free_BN(&info->sig.elgamal.r);
free_BN(&info->sig.elgamal.s);
#endif
break;
case PGP_PKA_PRIVATE00:
case PGP_PKA_PRIVATE01:
case PGP_PKA_PRIVATE02:
case PGP_PKA_PRIVATE03:
case PGP_PKA_PRIVATE04:
case PGP_PKA_PRIVATE05:
case PGP_PKA_PRIVATE06:
case PGP_PKA_PRIVATE07:
case PGP_PKA_PRIVATE08:
case PGP_PKA_PRIVATE09:
case PGP_PKA_PRIVATE10:
pgp_data_free(&info->sig.unknown);
break;
default:
(void) fprintf(stderr, "info-free: bad info-type\n");
}
memset(info, 0, sizeof(pgp_sig_info_t));
}
static void
sig_free(pgp_sig_t *sig)
{
pgp_free_sig_info(&sig->info);
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
/* ! Free any memory allocated when parsing the packet content */
void
pgp_parser_content_free(pgp_packet_t *c)
{
switch (c->tag) {
case PGP_PARSER_PTAG:
case PGP_PTAG_CT_COMPRESSED:
case PGP_PTAG_SS_CREATION_TIME:
case PGP_PTAG_SS_EXPIRATION_TIME:
case PGP_PTAG_SS_KEY_EXPIRY:
case PGP_PTAG_SS_TRUST:
case PGP_PTAG_SS_ISSUER_KEY_ID:
case PGP_PTAG_CT_1_PASS_SIG:
case PGP_PTAG_SS_PRIMARY_USER_ID:
case PGP_PTAG_SS_REVOCABLE:
case PGP_PTAG_SS_REVOCATION_KEY:
case PGP_PTAG_CT_LITDATA_HEADER:
case PGP_PTAG_CT_LITDATA_BODY:
case PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY:
case PGP_PTAG_CT_UNARMOURED_TEXT:
case PGP_PTAG_CT_ARMOUR_TRAILER:
case PGP_PTAG_CT_SIGNATURE_HEADER:
case PGP_PTAG_CT_SE_DATA_HEADER:
case PGP_PTAG_CT_SE_IP_DATA_HEADER:
case PGP_PTAG_CT_SE_IP_DATA_BODY:
case PGP_PTAG_CT_MDC:
case PGP_GET_SECKEY:
break;
case PGP_PTAG_CT_SIGNED_CLEARTEXT_HEADER:
headers_free(&c->u.cleartext_head);
break;
case PGP_PTAG_CT_ARMOUR_HEADER:
headers_free(&c->u.armour_header.headers);
break;
case PGP_PTAG_CT_SIGNED_CLEARTEXT_TRAILER:
cleartext_trailer_free(&c->u.cleartext_trailer);
break;
case PGP_PTAG_CT_TRUST:
pgp_data_free(&c->u.trust);
break;
case PGP_PTAG_CT_SIGNATURE:
case PGP_PTAG_CT_SIGNATURE_FOOTER:
sig_free(&c->u.sig);
break;
case PGP_PTAG_CT_PUBLIC_KEY:
case PGP_PTAG_CT_PUBLIC_SUBKEY:
pgp_pubkey_free(&c->u.pubkey);
break;
case PGP_PTAG_CT_USER_ID:
pgp_userid_free(&c->u.userid);
break;
case PGP_PTAG_SS_SIGNERS_USER_ID:
pgp_userid_free(&c->u.ss_signer);
break;
case PGP_PTAG_CT_USER_ATTR:
pgp_data_free(&c->u.userattr);
break;
case PGP_PTAG_SS_PREFERRED_SKA:
pgp_data_free(&c->u.ss_skapref);
break;
case PGP_PTAG_SS_PREFERRED_HASH:
pgp_data_free(&c->u.ss_hashpref);
break;
case PGP_PTAG_SS_PREF_COMPRESS:
pgp_data_free(&c->u.ss_zpref);
break;
case PGP_PTAG_SS_KEY_FLAGS:
pgp_data_free(&c->u.ss_key_flags);
break;
case PGP_PTAG_SS_KEYSERV_PREFS:
pgp_data_free(&c->u.ss_key_server_prefs);
break;
case PGP_PTAG_SS_FEATURES:
pgp_data_free(&c->u.ss_features);
break;
case PGP_PTAG_SS_NOTATION_DATA:
pgp_data_free(&c->u.ss_notation.name);
pgp_data_free(&c->u.ss_notation.value);
break;
case PGP_PTAG_SS_REGEXP:
string_free(&c->u.ss_regexp);
break;
case PGP_PTAG_SS_POLICY_URI:
string_free(&c->u.ss_policy);
break;
case PGP_PTAG_SS_PREF_KEYSERV:
string_free(&c->u.ss_keyserv);
break;
case PGP_PTAG_SS_USERDEFINED00:
case PGP_PTAG_SS_USERDEFINED01:
case PGP_PTAG_SS_USERDEFINED02:
case PGP_PTAG_SS_USERDEFINED03:
case PGP_PTAG_SS_USERDEFINED04:
case PGP_PTAG_SS_USERDEFINED05:
case PGP_PTAG_SS_USERDEFINED06:
case PGP_PTAG_SS_USERDEFINED07:
case PGP_PTAG_SS_USERDEFINED08:
case PGP_PTAG_SS_USERDEFINED09:
case PGP_PTAG_SS_USERDEFINED10:
pgp_data_free(&c->u.ss_userdef);
break;
case PGP_PTAG_SS_RESERVED:
pgp_data_free(&c->u.ss_unknown);
break;
case PGP_PTAG_SS_REVOCATION_REASON:
string_free(&c->u.ss_revocation.reason);
break;
case PGP_PTAG_SS_EMBEDDED_SIGNATURE:
pgp_data_free(&c->u.ss_embedded_sig);
break;
case PGP_PARSER_PACKET_END:
pgp_subpacket_free(&c->u.packet);
break;
case PGP_PARSER_ERROR:
case PGP_PARSER_ERRCODE:
break;
case PGP_PTAG_CT_SECRET_KEY:
case PGP_PTAG_CT_ENCRYPTED_SECRET_KEY:
pgp_seckey_free(&c->u.seckey);
break;
case PGP_PTAG_CT_PK_SESSION_KEY:
case PGP_PTAG_CT_ENCRYPTED_PK_SESSION_KEY:
pgp_pk_sesskey_free(&c->u.pk_sesskey);
break;
case PGP_GET_PASSPHRASE:
cmd_get_passphrase_free(&c->u.skey_passphrase);
break;
default:
if (c->tag >= 0x200 && c->tag < 0x300) {
// ignore unknown signature subpackets, they should have been
// ignored by parsing as well
} else {
fprintf(stderr, "Can't free %d (0x%x)\n", c->tag, c->tag);
}
}
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
void
pgp_pk_sesskey_free(pgp_pk_sesskey_t *sk)
{
switch (sk->alg) {
case PGP_PKA_RSA:
free_BN(&sk->params.rsa.encrypted_m);
break;
case PGP_PKA_ELGAMAL:
free_BN(&sk->params.elgamal.g_to_k);
free_BN(&sk->params.elgamal.encrypted_m);
break;
default:
(void) fprintf(stderr, "pgp_pk_sesskey_free: bad alg\n");
break;
}
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
/* ! Free the memory used when parsing a public key */
void
pgp_pubkey_free(pgp_pubkey_t *p)
{
switch (p->alg) {
case PGP_PKA_RSA:
case PGP_PKA_RSA_ENCRYPT_ONLY:
case PGP_PKA_RSA_SIGN_ONLY:
free_BN(&p->key.rsa.n);
free_BN(&p->key.rsa.e);
break;
case PGP_PKA_DSA:
free_BN(&p->key.dsa.p);
free_BN(&p->key.dsa.q);
free_BN(&p->key.dsa.g);
free_BN(&p->key.dsa.y);
break;
case PGP_PKA_ELGAMAL:
case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
free_BN(&p->key.elgamal.p);
free_BN(&p->key.elgamal.g);
free_BN(&p->key.elgamal.y);
break;
case PGP_PKA_NOTHING:
/* nothing to free */
break;
default:
(void) fprintf(stderr, "pgp_pubkey_free: bad alg\n");
}
memset(p, 0, sizeof(*p));
}
int
pgp_pubkey_dup(pgp_pubkey_t *dst, pgp_pubkey_t *src)
{
memcpy(dst, src, sizeof(*src));
switch (src->alg) {
case PGP_PKA_RSA:
case PGP_PKA_RSA_ENCRYPT_ONLY:
case PGP_PKA_RSA_SIGN_ONLY:
dup_BN(&dst->key.rsa.n, src->key.rsa.n);
dup_BN(&dst->key.rsa.e, src->key.rsa.e);
break;
case PGP_PKA_DSA:
dup_BN(&dst->key.dsa.p, src->key.dsa.p);
dup_BN(&dst->key.dsa.q, src->key.dsa.q);
dup_BN(&dst->key.dsa.g, src->key.dsa.g);
dup_BN(&dst->key.dsa.y, src->key.dsa.y);
break;
case PGP_PKA_ELGAMAL:
case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
dup_BN(&dst->key.elgamal.p, src->key.elgamal.p);
dup_BN(&dst->key.elgamal.g, src->key.elgamal.g);
dup_BN(&dst->key.elgamal.y, src->key.elgamal.y);
break;
case PGP_PKA_NOTHING:
/* nothing to dup */
break;
default:
(void) fprintf(stderr, "pgp_pubkey_dup: bad alg\n");
return 0;
}
/*TODO alloc error handling */
return 1;
}
/**
\ingroup Core_ReadPackets
*/
static int
parse_pubkey_data(pgp_pubkey_t *key, pgp_region_t *region,
pgp_stream_t *stream)
{
uint8_t c = 0x0;
if (region->readc != 0) {
/* We should not have read anything so far */
(void) fprintf(stderr, "parse_pubkey_data: bad length\n");
return 0;
}
if (!limread(&c, 1, region, stream)) {
return 0;
}
key->version = (pgp_version_t)c;
switch (key->version) {
case PGP_V2:
case PGP_V3:
case PGP_V4:
break;
default:
PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_PUBLIC_KEY_VRSN,
"Bad public key version (0x%02x)", key->version);
return 0;
}
if (!limited_read_time(&key->birthtime, region, stream)) {
return 0;
}
key->days_valid = 0;
if ((key->version == 2 || key->version == 3) &&
!limread_scalar(&key->days_valid, 2, region, stream)) {
return 0;
}
if (!limread(&c, 1, region, stream)) {
return 0;
}
key->alg = c;
switch (key->alg) {
case PGP_PKA_DSA:
if (!limread_mpi(&key->key.dsa.p, region, stream) ||
!limread_mpi(&key->key.dsa.q, region, stream) ||
!limread_mpi(&key->key.dsa.g, region, stream) ||
!limread_mpi(&key->key.dsa.y, region, stream)) {
return 0;
}
break;
case PGP_PKA_RSA:
case PGP_PKA_RSA_ENCRYPT_ONLY:
case PGP_PKA_RSA_SIGN_ONLY:
if (!limread_mpi(&key->key.rsa.n, region, stream) ||
!limread_mpi(&key->key.rsa.e, region, stream)) {
return 0;
}
break;
case PGP_PKA_ELGAMAL:
case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
if (!limread_mpi(&key->key.elgamal.p, region, stream) ||
!limread_mpi(&key->key.elgamal.g, region, stream) ||
!limread_mpi(&key->key.elgamal.y, region, stream)) {
return 0;
}
break;
default:
PGP_ERROR_1(&stream->errors,
PGP_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,
"Unsupported Public Key algorithm (%s)",
pgp_show_pka(key->alg));
return 0;
}
return 1;
}
/**
* \ingroup Core_ReadPackets
* \brief Parse a public key packet.
*
* This function parses an entire v3 (== v2) or v4 public key packet for RSA, ElGamal, and DSA keys.
*
* Once the key has been parsed successfully, it is passed to the callback.
*
* \param *ptag Pointer to the current Packet Tag. This function should consume the entire packet.
* \param *reader Our reader
* \param *cb The callback
* \return 1 on success, 0 on error
*
* \see RFC4880 5.5.2
*/
static int
parse_pubkey(pgp_content_enum tag, pgp_region_t *region,
pgp_stream_t *stream)
{
pgp_packet_t pkt;
if (!parse_pubkey_data(&pkt.u.pubkey, region, stream)) {
(void) fprintf(stderr, "parse_pubkey: parse_pubkey_data failed\n");
return 0;
}
/* XXX: this test should be done for all packets, surely? */
if (region->readc != region->length) {
PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
"Unconsumed data (%d)", region->length - region->readc);
return 0;
}
CALLBACK(tag, &stream->cbinfo, &pkt);
return 1;
}
/**
* \ingroup Core_ReadPackets
* \brief Parse one user attribute packet.
*
* User attribute packets contain one or more attribute subpackets.
* For now, handle the whole packet as raw data.
*/
static int
parse_userattr(pgp_region_t *region, pgp_stream_t *stream)
{
pgp_packet_t pkt;
/*
* xxx- treat as raw data for now. Could break down further into
* attribute sub-packets later - rachel
*/
if (region->readc != 0) {
/* We should not have read anything so far */
(void) fprintf(stderr, "parse_userattr: bad length\n");
return 0;
}
if (!read_data(&pkt.u.userattr, region, stream)) {
return 0;
}
CALLBACK(PGP_PTAG_CT_USER_ATTR, &stream->cbinfo, &pkt);
return 1;
}
/**
\ingroup Core_Create
\brief Free allocated memory
*/
/* ! Free the memory used when parsing this packet type */
void
pgp_userid_free(uint8_t **id)
{
free(*id);
*id = NULL;
}
/**
* \ingroup Core_ReadPackets
* \brief Parse a user id.
*
* This function parses an user id packet, which is basically just a char array the size of the packet.
*
* The char array is to be treated as an UTF-8 string.
*
* The userid gets null terminated by this function. Freeing it is the responsibility of the caller.
*
* Once the userid has been parsed successfully, it is passed to the callback.
*
* \param *ptag Pointer to the Packet Tag. This function should consume the entire packet.
* \param *reader Our reader
* \param *cb The callback
* \return 1 on success, 0 on error
*
* \see RFC4880 5.11
*/
static int
parse_userid(pgp_region_t *region, pgp_stream_t *stream)
{
pgp_packet_t pkt;
if (region->readc != 0) {
/* We should not have read anything so far */
(void) fprintf(stderr, "parse_userid: bad length\n");
return 0;
}
if ((pkt.u.userid = calloc(1, region->length + 1)) == NULL) {
(void) fprintf(stderr, "parse_userid: bad alloc\n");
return 0;
}
if (region->length &&
!limread(pkt.u.userid, region->length, region,
stream)) {
return 0;
}
pkt.u.userid[region->length] = 0x0;
CALLBACK(PGP_PTAG_CT_USER_ID, &stream->cbinfo, &pkt);
return 1;
}
static pgp_hash_t *
parse_hash_find(pgp_stream_t *stream, const uint8_t *keyid)
{
pgp_hashtype_t *hp;
size_t n;
for (n = 0, hp = stream->hashes; n < stream->hashc; n++, hp++) {
if (memcmp(hp->keyid, keyid, PGP_KEY_ID_SIZE) == 0) {
return &hp->hash;
}
}
return NULL;
}
/**
* \ingroup Core_Parse
* \brief Parse a version 3 signature.
*
* This function parses an version 3 signature packet, handling RSA and DSA signatures.
*
* Once the signature has been parsed successfully, it is passed to the callback.
*
* \param *ptag Pointer to the Packet Tag. This function should consume the entire packet.
* \param *reader Our reader
* \param *cb The callback
* \return 1 on success, 0 on error
*
* \see RFC4880 5.2.2
*/
static int
parse_v3_sig(pgp_region_t *region,
pgp_stream_t *stream)
{
pgp_packet_t pkt;
uint8_t c = 0x0;
/* clear signature */
(void) memset(&pkt.u.sig, 0x0, sizeof(pkt.u.sig));
pkt.u.sig.info.version = PGP_V3;
/* hash info length */
if (!limread(&c, 1, region, stream)) {
return 0;
}
if (c != 5) {
ERRP(&stream->cbinfo, pkt, "bad hash info length");
}
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.sig.info.type = (pgp_sig_type_t)c;
/* XXX: check signature type */
if (!limited_read_time(&pkt.u.sig.info.birthtime, region, stream)) {
return 0;
}
pkt.u.sig.info.birthtime_set = 1;
if (!limread(pkt.u.sig.info.signer_id, PGP_KEY_ID_SIZE, region,
stream)) {
return 0;
}
pkt.u.sig.info.signer_id_set = 1;
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.sig.info.key_alg = (pgp_pubkey_alg_t)c;
/* XXX: check algorithm */
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.sig.info.hash_alg = (pgp_hash_alg_t)c;
/* XXX: check algorithm */
if (!limread(pkt.u.sig.hash2, 2, region, stream)) {
return 0;
}
switch (pkt.u.sig.info.key_alg) {
case PGP_PKA_RSA:
case PGP_PKA_RSA_SIGN_ONLY:
if (!limread_mpi(&pkt.u.sig.info.sig.rsa.sig, region, stream)) {
return 0;
}
break;
case PGP_PKA_DSA:
if (!limread_mpi(&pkt.u.sig.info.sig.dsa.r, region, stream) ||
!limread_mpi(&pkt.u.sig.info.sig.dsa.s, region, stream)) {
return 0;
}
break;
case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
if (!limread_mpi(&pkt.u.sig.info.sig.elgamal.r, region,
stream) ||
!limread_mpi(&pkt.u.sig.info.sig.elgamal.s, region,
stream)) {
return 0;
}
break;
default:
PGP_ERROR_1(&stream->errors,
PGP_E_ALG_UNSUPPORTED_SIGNATURE_ALG,
"Unsupported signature key algorithm (%s)",
pgp_show_pka(pkt.u.sig.info.key_alg));
return 0;
}
if (region->readc != region->length) {
PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
"Unconsumed data (%d)",
region->length - region->readc);
return 0;
}
if (pkt.u.sig.info.signer_id_set) {
pkt.u.sig.hash = parse_hash_find(stream,
pkt.u.sig.info.signer_id);
}
CALLBACK(PGP_PTAG_CT_SIGNATURE, &stream->cbinfo, &pkt);
return 1;
}
/**
* \ingroup Core_ReadPackets
* \brief Parse one signature sub-packet.
*
* Version 4 signatures can have an arbitrary amount of (hashed and
* unhashed) subpackets. Subpackets are used to hold optional
* attributes of subpackets.
*
* This function parses one such signature subpacket.
*
* Once the subpacket has been parsed successfully, it is passed to the callback.
*
* \param *ptag Pointer to the Packet Tag. This function should consume the entire subpacket.
* \param *reader Our reader
* \param *cb The callback
* \return 1 on success, 0 on error
*
* \see RFC4880 5.2.3
*/
static int
parse_one_sig_subpacket(pgp_sig_t *sig,
pgp_region_t *region,
pgp_stream_t *stream)
{
pgp_region_t subregion;
pgp_packet_t pkt;
uint8_t bools = 0x0;
uint8_t c = 0x0;
unsigned doread = 1;
unsigned t8;
unsigned t7;
pgp_init_subregion(&subregion, region);
if (!limited_read_new_length(&subregion.length, region, stream)) {
return 0;
}
if (subregion.length > region->length) {
ERRP(&stream->cbinfo, pkt, "Subpacket too long");
}
if (!limread(&c, 1, &subregion, stream)) {
return 0;
}
t8 = (c & 0x7f) / 8;
t7 = 1 << (c & 7);
pkt.critical = (unsigned)c >> 7;
pkt.tag = (pgp_content_enum)(PGP_PTAG_SIG_SUBPKT_BASE + (c & 0x7f));
/* Application wants it delivered raw */
if (stream->ss_raw[t8] & t7) {
pkt.u.ss_raw.tag = pkt.tag;
pkt.u.ss_raw.length = subregion.length - 1;
pkt.u.ss_raw.raw = calloc(1, pkt.u.ss_raw.length);
if (pkt.u.ss_raw.raw == NULL) {
(void) fprintf(stderr, "parse_one_sig_subpacket: bad alloc\n");
return 0;
}
if (!limread(pkt.u.ss_raw.raw, (unsigned)pkt.u.ss_raw.length,
&subregion, stream)) {
return 0;
}
CALLBACK(PGP_PTAG_RAW_SS, &stream->cbinfo, &pkt);
return 1;
}
switch (pkt.tag) {
case PGP_PTAG_SS_CREATION_TIME:
case PGP_PTAG_SS_EXPIRATION_TIME:
case PGP_PTAG_SS_KEY_EXPIRY:
if (!limited_read_time(&pkt.u.ss_time, &subregion, stream))
return 0;
if (pkt.tag == PGP_PTAG_SS_CREATION_TIME) {
sig->info.birthtime = pkt.u.ss_time;
sig->info.birthtime_set = 1;
}
if (pkt.tag == PGP_PTAG_SS_EXPIRATION_TIME) {
sig->info.duration = pkt.u.ss_time;
sig->info.duration_set = 1;
}
if (pkt.tag == PGP_PTAG_SS_KEY_EXPIRY) {
sig->info.key_expiry = pkt.u.ss_time;
sig->info.key_expiry_set = 1;
}
break;
case PGP_PTAG_SS_TRUST:
if (!limread(&pkt.u.ss_trust.level, 1, &subregion, stream) ||
!limread(&pkt.u.ss_trust.amount, 1, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_REVOCABLE:
if (!limread(&bools, 1, &subregion, stream)) {
return 0;
}
pkt.u.ss_revocable = !!bools;
break;
case PGP_PTAG_SS_ISSUER_KEY_ID:
if (!limread(pkt.u.ss_issuer, PGP_KEY_ID_SIZE, &subregion, stream)) {
return 0;
}
(void) memcpy(sig->info.signer_id, pkt.u.ss_issuer, PGP_KEY_ID_SIZE);
sig->info.signer_id_set = 1;
break;
case PGP_PTAG_SS_PREFERRED_SKA:
if (!read_data(&pkt.u.ss_skapref, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_PREFERRED_HASH:
if (!read_data(&pkt.u.ss_hashpref, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_PREF_COMPRESS:
if (!read_data(&pkt.u.ss_zpref, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_PRIMARY_USER_ID:
if (!limread(&bools, 1, &subregion, stream)) {
return 0;
}
pkt.u.ss_primary_userid = !!bools;
sig->info.primary_userid = pkt.u.ss_primary_userid;
break;
case PGP_PTAG_SS_KEY_FLAGS:
if (!read_data(&pkt.u.ss_key_flags, &subregion, stream)) {
return 0;
}
if(pkt.u.ss_key_flags.len > 0){
/* Only one byte is defined in rfc4880 for now */
sig->info.key_flags = pkt.u.ss_key_flags.contents[0];
sig->info.key_flags_set = 1;
}
break;
case PGP_PTAG_SS_KEYSERV_PREFS:
if (!read_data(&pkt.u.ss_key_server_prefs, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_FEATURES:
if (!read_data(&pkt.u.ss_features, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_SIGNERS_USER_ID:
if (!read_unsig_str(&pkt.u.ss_signer, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_EMBEDDED_SIGNATURE:
/* \todo should do something with this sig? */
if (!read_data(&pkt.u.ss_embedded_sig, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_NOTATION_DATA:
if (!limread_data(&pkt.u.ss_notation.flags, 4,
&subregion, stream)) {
return 0;
}
if (!limread_size_t(&pkt.u.ss_notation.name.len, 2,
&subregion, stream)) {
return 0;
}
if (!limread_size_t(&pkt.u.ss_notation.value.len, 2,
&subregion, stream)) {
return 0;
}
if (!limread_data(&pkt.u.ss_notation.name,
(unsigned)pkt.u.ss_notation.name.len,
&subregion, stream)) {
return 0;
}
if (!limread_data(&pkt.u.ss_notation.value,
(unsigned)pkt.u.ss_notation.value.len,
&subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_POLICY_URI:
if (!read_string(&pkt.u.ss_policy, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_REGEXP:
if (!read_string(&pkt.u.ss_regexp, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_PREF_KEYSERV:
if (!read_string(&pkt.u.ss_keyserv, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_USERDEFINED00:
case PGP_PTAG_SS_USERDEFINED01:
case PGP_PTAG_SS_USERDEFINED02:
case PGP_PTAG_SS_USERDEFINED03:
case PGP_PTAG_SS_USERDEFINED04:
case PGP_PTAG_SS_USERDEFINED05:
case PGP_PTAG_SS_USERDEFINED06:
case PGP_PTAG_SS_USERDEFINED07:
case PGP_PTAG_SS_USERDEFINED08:
case PGP_PTAG_SS_USERDEFINED09:
case PGP_PTAG_SS_USERDEFINED10:
if (!read_data(&pkt.u.ss_userdef, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_RESERVED:
if (!read_data(&pkt.u.ss_unknown, &subregion, stream)) {
return 0;
}
break;
case PGP_PTAG_SS_REVOCATION_REASON:
/* first byte is the machine-readable code */
if (!limread(&pkt.u.ss_revocation.code, 1, &subregion, stream)) {
return 0;
}
/* the rest is a human-readable UTF-8 string */
if (!read_string(&pkt.u.ss_revocation.reason, &subregion,
stream)) {
return 0;
}
break;
case PGP_PTAG_SS_REVOCATION_KEY:
/* octet 0 = class. Bit 0x80 must be set */
if (!limread(&pkt.u.ss_revocation_key.class, 1,
&subregion, stream)) {
return 0;
}
if (!(pkt.u.ss_revocation_key.class & 0x80)) {
printf("Warning: PGP_PTAG_SS_REVOCATION_KEY class: "
"Bit 0x80 should be set\n");
return 0;
}
/* octet 1 = algid */
if (!limread(&pkt.u.ss_revocation_key.algid, 1,
&subregion, stream)) {
return 0;
}
/* octets 2-21 = fingerprint */
if (!limread(&pkt.u.ss_revocation_key.fingerprint[0],
PGP_FINGERPRINT_SIZE, &subregion, stream)) {
return 0;
}
break;
default:
// Original code, will report as error
if (stream->ss_parsed[t8] & t7) {
PGP_ERROR_1(&stream->errors, PGP_E_PROTO_UNKNOWN_SS,
"Unknown signature subpacket type (%d)",
c & 0x7f);
}
/*
Otherwise ignore the unknown subpacket:
RFC 4880: "An implementation SHOULD ignore any subpacket of a type that it does
not recognize."
*/
if (!read_data(&pkt.u.ss_embedded_sig, &subregion, stream)) {
return 0;
}
pgp_data_free(&pkt.u.ss_embedded_sig);
break;
}
/* Application doesn't want it delivered parsed */
if (!(stream->ss_parsed[t8] & t7)) {
if (pkt.critical) {
PGP_ERROR_1(&stream->errors,
PGP_E_PROTO_CRITICAL_SS_IGNORED,
"Critical signature subpacket ignored (%d)",
c & 0x7f);
}
if (!doread &&
!limskip(subregion.length - 1, &subregion, stream)) {
return 0;
}
if (doread) {
pgp_parser_content_free(&pkt);
}
return 1;
}
if (doread && subregion.readc != subregion.length) {
PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
"Unconsumed data (%d)",
subregion.length - subregion.readc);
pgp_parser_content_free(&pkt);
return 0;
}
CALLBACK(pkt.tag, &stream->cbinfo, &pkt);
return 1;
}
/**
* \ingroup Core_ReadPackets
* \brief Parse several signature subpackets.
*
* Hashed and unhashed subpacket sets are preceded by an octet count that specifies the length of the complete set.
* This function parses this length and then calls parse_one_sig_subpacket() for each subpacket until the
* entire set is consumed.
*
* This function does not call the callback directly, parse_one_sig_subpacket() does for each subpacket.
*
* \param *ptag Pointer to the Packet Tag.
* \param *reader Our reader
* \param *cb The callback
* \return 1 on success, 0 on error
*
* \see RFC4880 5.2.3
*/
static int
parse_sig_subpkts(pgp_sig_t *sig,
pgp_region_t *region,
pgp_stream_t *stream)
{
pgp_region_t subregion;
pgp_packet_t pkt;
pgp_init_subregion(&subregion, region);
if (!limread_scalar(&subregion.length, 2, region, stream)) {
return 0;
}
if (subregion.length > region->length) {
ERRP(&stream->cbinfo, pkt, "Subpacket set too long");
}
while (subregion.readc < subregion.length) {
if (!parse_one_sig_subpacket(sig, &subregion, stream)) {
return 0;
}
}
if (subregion.readc != subregion.length) {
if (!limskip(subregion.length - subregion.readc,
&subregion, stream)) {
ERRP(&stream->cbinfo, pkt,
"parse_sig_subpkts: subpacket length read mismatch");
}
ERRP(&stream->cbinfo, pkt, "Subpacket length mismatch");
}
return 1;
}
/**
* \ingroup Core_ReadPackets
* \brief Parse a version 4 signature.
*
* This function parses a version 4 signature including all its hashed and unhashed subpackets.
*
* Once the signature packet has been parsed successfully, it is passed to the callback.
*
* \param *ptag Pointer to the Packet Tag.
* \param *reader Our reader
* \param *cb The callback
* \return 1 on success, 0 on error
*
* \see RFC4880 5.2.3
*/
static int
parse_v4_sig(pgp_region_t *region, pgp_stream_t *stream)
{
pgp_packet_t pkt;
uint8_t c = 0x0;
if (pgp_get_debug_level(__FILE__)) {
fprintf(stderr, "\nparse_v4_sig\n");
}
/* clear signature */
(void) memset(&pkt.u.sig, 0x0, sizeof(pkt.u.sig));
/*
* We need to hash the packet data from version through the hashed
* subpacket data
*/
pkt.u.sig.v4_hashstart = stream->readinfo.alength - 1;
/* Set version,type,algorithms */
pkt.u.sig.info.version = PGP_V4;
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.sig.info.type = (pgp_sig_type_t)c;
if (pgp_get_debug_level(__FILE__)) {
fprintf(stderr, "signature type=%d (%s)\n",
pkt.u.sig.info.type,
pgp_show_sig_type(pkt.u.sig.info.type));
}
/* XXX: check signature type */
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.sig.info.key_alg = (pgp_pubkey_alg_t)c;
/* XXX: check key algorithm */
if (pgp_get_debug_level(__FILE__)) {
(void) fprintf(stderr, "key_alg=%d (%s)\n",
pkt.u.sig.info.key_alg,
pgp_show_pka(pkt.u.sig.info.key_alg));
}
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.sig.info.hash_alg = (pgp_hash_alg_t)c;
/* XXX: check hash algorithm */
if (pgp_get_debug_level(__FILE__)) {
fprintf(stderr, "hash_alg=%d %s\n",
pkt.u.sig.info.hash_alg,
pgp_show_hash_alg(pkt.u.sig.info.hash_alg));
}
CALLBACK(PGP_PTAG_CT_SIGNATURE_HEADER, &stream->cbinfo, &pkt);
if (!parse_sig_subpkts(&pkt.u.sig, region, stream)) {
return 0;
}
pkt.u.sig.info.v4_hashlen = stream->readinfo.alength - pkt.u.sig.v4_hashstart;
printf("%s pkt.u.sig.info.v4_hashlen %d\n",__FUNCTION__,pkt.u.sig.info.v4_hashlen);
if (pgp_get_debug_level(__FILE__)) {
fprintf(stderr, "v4_hashlen=%zd\n", pkt.u.sig.info.v4_hashlen);
}
/* copy hashed subpackets */
if (pkt.u.sig.info.v4_hashed) {
free(pkt.u.sig.info.v4_hashed);
}
pkt.u.sig.info.v4_hashed = calloc(1, pkt.u.sig.info.v4_hashlen);
if (pkt.u.sig.info.v4_hashed == NULL) {
(void) fprintf(stderr, "parse_v4_sig: bad alloc\n");
return 0;
}
if (!stream->readinfo.accumulate) {
/* We must accumulate, else we can't check the signature */
fprintf(stderr, "*** ERROR: must set accumulate to 1\n");
goto error_unalloc_v4_hashed;
}
(void) memcpy(pkt.u.sig.info.v4_hashed,
stream->readinfo.accumulated + pkt.u.sig.v4_hashstart,
pkt.u.sig.info.v4_hashlen);
if (!parse_sig_subpkts(&pkt.u.sig, region, stream)) {
goto error_unalloc_v4_hashed;
}
if (!limread(pkt.u.sig.hash2, 2, region, stream)) {
goto error_unalloc_v4_hashed;
}
switch (pkt.u.sig.info.key_alg) {
case PGP_PKA_RSA:
if (!limread_mpi(&pkt.u.sig.info.sig.rsa.sig, region, stream)) {
goto error_unalloc_v4_hashed;
}
if (pgp_get_debug_level(__FILE__)) {
(void) fprintf(stderr, "parse_v4_sig: RSA: sig is\n");
#ifdef HAVE_OPENSSL_BN_H
BN_print_fp(stderr, pkt.u.sig.info.sig.rsa.sig);
#endif
(void) fprintf(stderr, "\n");
}
break;
case PGP_PKA_DSA:
if (!limread_mpi(&pkt.u.sig.info.sig.dsa.r, region, stream)) {
/*
* usually if this fails, it just means we've reached
* the end of the keyring
*/
if (pgp_get_debug_level(__FILE__)) {
(void) fprintf(stderr,
"Error reading DSA r field in signature");
}
goto error_unalloc_v4_hashed;
}
if (!limread_mpi(&pkt.u.sig.info.sig.dsa.s, region, stream)) {
ERRP(&stream->cbinfo, pkt,
"Error reading DSA s field in signature");
}
break;
case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
if (!limread_mpi(&pkt.u.sig.info.sig.elgamal.r, region,
stream) ||
!limread_mpi(&pkt.u.sig.info.sig.elgamal.s, region,
stream)) {
goto error_unalloc_v4_hashed;
}
break;
case PGP_PKA_PRIVATE00:
case PGP_PKA_PRIVATE01:
case PGP_PKA_PRIVATE02:
case PGP_PKA_PRIVATE03:
case PGP_PKA_PRIVATE04:
case PGP_PKA_PRIVATE05:
case PGP_PKA_PRIVATE06:
case PGP_PKA_PRIVATE07:
case PGP_PKA_PRIVATE08:
case PGP_PKA_PRIVATE09:
case PGP_PKA_PRIVATE10:
if (!read_data(&pkt.u.sig.info.sig.unknown, region, stream)) {
goto error_unalloc_v4_hashed;
}
break;
default:
PGP_ERROR_1(&stream->errors, PGP_E_ALG_UNSUPPORTED_SIGNATURE_ALG,
"Bad v4 signature key algorithm (%s)",
pgp_show_pka(pkt.u.sig.info.key_alg));
goto error_unalloc_v4_hashed;
}
if (region->readc != region->length) {
PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
"Unconsumed data (%d)",
region->length - region->readc);
goto error_unalloc_v4_hashed;
}
CALLBACK(PGP_PTAG_CT_SIGNATURE_FOOTER, &stream->cbinfo, &pkt);
return 1;
error_unalloc_v4_hashed:
free(pkt.u.sig.info.v4_hashed);
return 0;
}
/**
* \ingroup Core_ReadPackets
* \brief Parse a signature subpacket.
*
* This function calls the appropriate function to handle v3 or v4 signatures.
*
* Once the signature packet has been parsed successfully, it is passed to the callback.
*
* \param *ptag Pointer to the Packet Tag.
* \param *reader Our reader
* \param *cb The callback
* \return 1 on success, 0 on error
*/
static int
parse_sig(pgp_region_t *region, pgp_stream_t *stream)
{
pgp_packet_t pkt;
uint8_t c = 0x0;
if (region->readc != 0) {
/* We should not have read anything so far */
(void) fprintf(stderr, "parse_sig: bad length\n");
return 0;
}
(void) memset(&pkt, 0x0, sizeof(pkt));
if (!limread(&c, 1, region, stream)) {
return 0;
}
if (c == 2 || c == 3) {
return parse_v3_sig(region, stream);
}
if (c == 4) {
return parse_v4_sig(region, stream);
}
PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_SIGNATURE_VRSN,
"Bad signature version (%d)", c);
return 0;
}
/**
\ingroup Core_ReadPackets
\brief Parse Compressed packet
*/
static int
parse_compressed(pgp_region_t *region, pgp_stream_t *stream)
{
pgp_packet_t pkt;
uint8_t c = 0x0;
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.compressed = (pgp_compression_type_t)c;
CALLBACK(PGP_PTAG_CT_COMPRESSED, &stream->cbinfo, &pkt);
/*
* The content of a compressed data packet is more OpenPGP packets
* once decompressed, so recursively handle them
*/
return pgp_decompress(region, stream, pkt.u.compressed);
}
/* XXX: this could be improved by sharing all hashes that are the */
/* same, then duping them just before checking the signature. */
static void
parse_hash_init(pgp_stream_t *stream, pgp_hash_alg_t type,
const uint8_t *keyid)
{
pgp_hashtype_t *hash;
hash = realloc(stream->hashes,
(stream->hashc + 1) * sizeof(*stream->hashes));
if (hash == NULL) {
(void) fprintf(stderr, "parse_hash_init: bad alloc 0\n");
/* just continue and die here */
/* XXX - agc - no way to return failure */
} else {
stream->hashes = hash;
}
hash = &stream->hashes[stream->hashc++];
pgp_hash_any(&hash->hash, type);
if (!hash->hash.init(&hash->hash)) {
(void) fprintf(stderr, "parse_hash_init: bad alloc\n");
/* just continue and die here */
/* XXX - agc - no way to return failure */
}
(void) memcpy(hash->keyid, keyid, sizeof(hash->keyid));
}
/**
\ingroup Core_ReadPackets
\brief Parse a One Pass Signature packet
*/
static int
parse_one_pass(pgp_region_t * region, pgp_stream_t * stream)
{
pgp_packet_t pkt;
uint8_t c = 0x0;
if (!limread(&pkt.u.one_pass_sig.version, 1, region, stream)) {
return 0;
}
if (pkt.u.one_pass_sig.version != 3) {
PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_ONE_PASS_SIG_VRSN,
"Bad one-pass signature version (%d)",
pkt.u.one_pass_sig.version);
return 0;
}
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.one_pass_sig.sig_type = (pgp_sig_type_t)c;
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.one_pass_sig.hash_alg = (pgp_hash_alg_t)c;
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.one_pass_sig.key_alg = (pgp_pubkey_alg_t)c;
if (!limread(pkt.u.one_pass_sig.keyid,
(unsigned)sizeof(pkt.u.one_pass_sig.keyid),
region, stream)) {
return 0;
}
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.one_pass_sig.nested = !!c;
CALLBACK(PGP_PTAG_CT_1_PASS_SIG, &stream->cbinfo, &pkt);
/* XXX: we should, perhaps, let the app choose whether to hash or not */
parse_hash_init(stream, pkt.u.one_pass_sig.hash_alg,
pkt.u.one_pass_sig.keyid);
return 1;
}
/**
\ingroup Core_ReadPackets
\brief Parse a Trust packet
*/
static int
parse_trust(pgp_region_t *region, pgp_stream_t *stream)
{
pgp_packet_t pkt;
if (!read_data(&pkt.u.trust, region, stream)) {
return 0;
}
CALLBACK(PGP_PTAG_CT_TRUST, &stream->cbinfo, &pkt);
return 1;
}
static void
parse_hash_data(pgp_stream_t *stream, const void *data,
size_t length)
{
size_t n;
for (n = 0; n < stream->hashc; ++n) {
stream->hashes[n].hash.add(&stream->hashes[n].hash, data, (unsigned)length);
}
}
/**
\ingroup Core_ReadPackets
\brief Parse a Literal Data packet
*/
static int
parse_litdata(pgp_region_t *region, pgp_stream_t *stream)
{
pgp_memory_t *mem;
pgp_packet_t pkt;
uint8_t c = 0x0;
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.litdata_header.format = (pgp_litdata_enum)c;
if (!limread(&c, 1, region, stream)) {
return 0;
}
if (!limread((uint8_t *)pkt.u.litdata_header.filename,
(unsigned)c, region, stream)) {
return 0;
}
pkt.u.litdata_header.filename[c] = '\0';
if (!limited_read_time(&pkt.u.litdata_header.mtime, region, stream)) {
return 0;
}
CALLBACK(PGP_PTAG_CT_LITDATA_HEADER, &stream->cbinfo, &pkt);
mem = pkt.u.litdata_body.mem = pgp_memory_new();
pgp_memory_init(pkt.u.litdata_body.mem,
(unsigned)((region->length * 101) / 100) + 12);
pkt.u.litdata_body.data = mem->buf;
while (region->readc < region->length) {
unsigned readc = region->length - region->readc;
if (!limread(mem->buf, readc, region, stream)) {
return 0;
}
pkt.u.litdata_body.length = readc;
parse_hash_data(stream, pkt.u.litdata_body.data, region->length);
CALLBACK(PGP_PTAG_CT_LITDATA_BODY, &stream->cbinfo, &pkt);
}
/* XXX - get rid of mem here? */
return 1;
}
/**
* \ingroup Core_Create
*
* pgp_seckey_free() frees the memory associated with "key". Note that
* the key itself is not freed.
*
* \param key
*/
void
pgp_seckey_free(pgp_seckey_t *key)
{
switch (key->pubkey.alg) {
case PGP_PKA_RSA:
case PGP_PKA_RSA_ENCRYPT_ONLY:
case PGP_PKA_RSA_SIGN_ONLY:
free_BN(&key->key.rsa.d);
free_BN(&key->key.rsa.p);
free_BN(&key->key.rsa.q);
free_BN(&key->key.rsa.u);
break;
case PGP_PKA_DSA:
free_BN(&key->key.dsa.x);
break;
default:
(void) fprintf(stderr,
"pgp_seckey_free: Unknown algorithm: %d (%s)\n",
key->pubkey.alg,
pgp_show_pka(key->pubkey.alg));
}
free(key->checkhash);
pgp_pubkey_free(&key->pubkey);
memset(key, 0, sizeof(*key));
}
int
pgp_seckey_dup(pgp_seckey_t *dst, pgp_seckey_t *src)
{
memcpy(dst, src, sizeof(*src));
switch (src->pubkey.alg) {
case PGP_PKA_RSA:
case PGP_PKA_RSA_ENCRYPT_ONLY:
case PGP_PKA_RSA_SIGN_ONLY:
dup_BN(&dst->key.rsa.d, src->key.rsa.d);
dup_BN(&dst->key.rsa.p, src->key.rsa.p);
dup_BN(&dst->key.rsa.q, src->key.rsa.q);
dup_BN(&dst->key.rsa.u, src->key.rsa.u);
break;
case PGP_PKA_DSA:
dup_BN(&dst->key.dsa.x, src->key.dsa.x);
break;
default:
(void) fprintf(stderr,
"pgp_seckey_dup: Unknown algorithm: %d (%s)\n",
src->pubkey.alg,
pgp_show_pka(src->pubkey.alg));
return 0;
}
if(src->checkhash) {
if ((dst->checkhash = calloc(1, PGP_CHECKHASH_SIZE)) == NULL) {
(void) fprintf(stderr, "pgp_seckey_dup: bad alloc\n");
return 0;
}else{
memcpy(dst->checkhash, src->checkhash, PGP_CHECKHASH_SIZE);
}
}
pgp_pubkey_dup(&dst->pubkey, &src->pubkey);
/*TODO alloc error handling */
return 1;
}
static int
consume_packet(pgp_region_t *region, pgp_stream_t *stream, unsigned warn)
{
pgp_packet_t pkt;
pgp_data_t remainder;
if (region->indeterminate) {
ERRP(&stream->cbinfo, pkt,
"Can't consume indeterminate packets");
}
if (read_data(&remainder, region, stream)) {
/* now throw it away */
pgp_data_free(&remainder);
if (warn) {
PGP_ERROR_1(&stream->errors, PGP_E_P_PACKET_CONSUMED,
"%s", "Warning: packet consumer");
}
return 1;
}
PGP_ERROR_1(&stream->errors, PGP_E_P_PACKET_NOT_CONSUMED,
"%s", (warn) ? "Warning: Packet was not consumed" :
"Packet was not consumed");
return warn;
}
/**
* \ingroup Core_ReadPackets
* \brief Parse a secret key
*/
static int
parse_seckey(pgp_content_enum tag, pgp_region_t *region, pgp_stream_t *stream)
{
pgp_packet_t pkt;
pgp_region_t encregion;
pgp_region_t *saved_region = NULL;
pgp_crypt_t decrypt;
pgp_hash_t checkhash;
unsigned blocksize;
unsigned crypted;
uint8_t c = 0x0;
int ret = 1;
if (pgp_get_debug_level(__FILE__)) {
fprintf(stderr, "\n---------\nparse_seckey:\n");
fprintf(stderr,
"region length=%u, readc=%u, remainder=%u\n",
region->length, region->readc,
region->length - region->readc);
}
(void) memset(&pkt, 0x0, sizeof(pkt));
if (!parse_pubkey_data(&pkt.u.seckey.pubkey, region, stream)) {
return 0;
}
if (pgp_get_debug_level(__FILE__)) {
fprintf(stderr, "parse_seckey: public key parsed\n");
//pgp_print_pubkey(&pkt.u.seckey.pubkey);
}
//stream->reading_v3_secret = (pkt.u.seckey.pubkey.version != PGP_V4);
if (pkt.u.seckey.pubkey.version != PGP_V4){
(void) fprintf(stderr,
"parse_seckey: Only V4 is supported\n");
return 0;
}
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.seckey.s2k_usage = (pgp_s2k_usage_t)c;
if (pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED ||
pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED_AND_HASHED) {
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.seckey.alg = (pgp_symm_alg_t)c;
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.seckey.s2k_specifier = (pgp_s2k_specifier_t)c;
switch (pkt.u.seckey.s2k_specifier) {
case PGP_S2KS_SIMPLE:
case PGP_S2KS_SALTED:
case PGP_S2KS_ITERATED_AND_SALTED:
break;
default:
(void) fprintf(stderr,
"parse_seckey: bad seckey\n");
return 0;
}
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.seckey.hash_alg = (pgp_hash_alg_t)c;
if (pkt.u.seckey.s2k_specifier != PGP_S2KS_SIMPLE &&
!limread(pkt.u.seckey.salt, 8, region, stream)) {
return 0;
}
if (pkt.u.seckey.s2k_specifier ==
PGP_S2KS_ITERATED_AND_SALTED) {
if (!limread(&c, 1, region, stream)) {
return 0;
}
pkt.u.seckey.octetc =
(16 + ((unsigned)c & 15)) <<
(((unsigned)c >> 4) + 6);
}
} else if (pkt.u.seckey.s2k_usage != PGP_S2KU_NONE) {
/* this is V3 style, looks just like a V4 simple hash */
pkt.u.seckey.alg = (pgp_symm_alg_t)c;
pkt.u.seckey.s2k_usage = PGP_S2KU_ENCRYPTED;
pkt.u.seckey.s2k_specifier = PGP_S2KS_SIMPLE;
pkt.u.seckey.hash_alg = PGP_HASH_MD5;
}
crypted = pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED ||
pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED_AND_HASHED;
if (crypted) {
pgp_packet_t seckey;
pgp_hash_t hashes[(PGP_MAX_KEY_SIZE + PGP_MIN_HASH_SIZE - 1) / PGP_MIN_HASH_SIZE];
unsigned passlen;
uint8_t key[PGP_MAX_KEY_SIZE + PGP_MAX_HASH_SIZE];
char *passphrase;
int hashsize;
int keysize;
int n;
if (pgp_get_debug_level(__FILE__)) {
(void) fprintf(stderr, "crypted seckey\n");
}
blocksize = pgp_block_size(pkt.u.seckey.alg);
if (blocksize == 0 || blocksize > PGP_MAX_BLOCK_SIZE) {
(void) fprintf(stderr,
"parse_seckey: bad blocksize\n");
return 0;
}
if (!limread(pkt.u.seckey.iv, blocksize, region, stream)) {
return 0;
}
(void) memset(&seckey, 0x0, sizeof(seckey));
passphrase = NULL;
seckey.u.skey_passphrase.passphrase = &passphrase;
seckey.u.skey_passphrase.seckey = &pkt.u.seckey;
CALLBACK(PGP_GET_PASSPHRASE, &stream->cbinfo, &seckey);
if (!passphrase) {
if (pgp_get_debug_level(__FILE__)) {
/* \todo make into proper error */
(void) fprintf(stderr,
"parse_seckey: can't get passphrase\n");
}
if (!consume_packet(region, stream, 0)) {
return 0;
}
CALLBACK(PGP_PTAG_CT_ENCRYPTED_SECRET_KEY,
&stream->cbinfo, &pkt);
return 1;
}
keysize = pgp_key_size(pkt.u.seckey.alg);
if (keysize == 0 || keysize > PGP_MAX_KEY_SIZE) {
(void) fprintf(stderr,
"parse_seckey: bad keysize\n");
return 0;
}
/* Hardcoded SHA1 for just now */
pkt.u.seckey.hash_alg = PGP_HASH_SHA1;
hashsize = pgp_hash_size(pkt.u.seckey.hash_alg);
if (hashsize == 0 || hashsize > PGP_MAX_HASH_SIZE) {
(void) fprintf(stderr,
"parse_seckey: bad hashsize\n");
return 0;
}
for (n = 0; n * hashsize < keysize; ++n) {
int i;
pgp_hash_any(&hashes[n],
pkt.u.seckey.hash_alg);
if (!hashes[n].init(&hashes[n])) {
(void) fprintf(stderr,
"parse_seckey: bad alloc\n");
return 0;
}
/* preload hashes with zeroes... */
for (i = 0; i < n; ++i) {
hashes[n].add(&hashes[n],
(const uint8_t *) "", 1);
}
}
passlen = (unsigned)strlen(passphrase);
for (n = 0; n * hashsize < keysize; ++n) {
unsigned i;
switch (pkt.u.seckey.s2k_specifier) {
case PGP_S2KS_SALTED:
hashes[n].add(&hashes[n],
pkt.u.seckey.salt,
PGP_SALT_SIZE);
/* FALLTHROUGH */
case PGP_S2KS_SIMPLE:
hashes[n].add(&hashes[n],
(uint8_t *)passphrase, (unsigned)passlen);
break;
case PGP_S2KS_ITERATED_AND_SALTED:
for (i = 0; i < pkt.u.seckey.octetc;
i += passlen + PGP_SALT_SIZE) {
unsigned j;