mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-05-12 02:55:18 -04:00

git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-OpenPGP@5048 b45a01b8-16f6-495d-af2f-9b41ad6348cc
3261 lines
83 KiB
C
3261 lines
83 KiB
C
/*
|
|
* 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 <openssl/cast.h>
|
|
|
|
#include <openpgpsdk/callback.h>
|
|
#include <openpgpsdk/packet.h>
|
|
#include <openpgpsdk/packet-parse.h>
|
|
#include <openpgpsdk/keyring.h>
|
|
#include <openpgpsdk/util.h>
|
|
#include <openpgpsdk/compress.h>
|
|
#include <openpgpsdk/errors.h>
|
|
#include <openpgpsdk/readerwriter.h>
|
|
#include <openpgpsdk/packet-show.h>
|
|
#include <openpgpsdk/std_print.h>
|
|
#include <openpgpsdk/create.h>
|
|
#include <openpgpsdk/hash.h>
|
|
|
|
#include "parse_local.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifndef WIN32
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
|
|
#include <openpgpsdk/final.h>
|
|
|
|
static int debug=0;
|
|
|
|
/**
|
|
* limited_read_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 pinfo How to parse
|
|
*
|
|
* \return 1 on success, 0 on failure
|
|
*/
|
|
static int limited_read_data(ops_data_t *data,unsigned int len,
|
|
ops_region_t *subregion,ops_parse_info_t *pinfo)
|
|
{
|
|
data->len = len;
|
|
|
|
assert(subregion->length-subregion->length_read >= len);
|
|
|
|
data->contents=malloc(data->len);
|
|
if (!data->contents)
|
|
return 0;
|
|
|
|
if (!ops_limited_read(data->contents, data->len,subregion,&pinfo->errors,
|
|
&pinfo->rinfo,&pinfo->cbinfo))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* read_data reads the remainder of the subregion's data
|
|
* into a data_t structure
|
|
*
|
|
* \param data
|
|
* \param subregion
|
|
* \param pinfo
|
|
*
|
|
* \return 1 on success, 0 on failure
|
|
*/
|
|
static int read_data(ops_data_t *data,ops_region_t *subregion,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
int len;
|
|
|
|
len=subregion->length-subregion->length_read;
|
|
|
|
if ( len >= 0 ) {
|
|
return(limited_read_data(data,len,subregion,pinfo));
|
|
}
|
|
return 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_unsigned_string(unsigned char **str,ops_region_t *subregion,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
int len=0;
|
|
|
|
len=subregion->length-subregion->length_read;
|
|
|
|
*str=malloc(len+1);
|
|
if(!(*str))
|
|
return 0;
|
|
|
|
if(len && !ops_limited_read(*str,len,subregion,&pinfo->errors,
|
|
&pinfo->rinfo,&pinfo->cbinfo))
|
|
return 0;
|
|
|
|
/*! ensure the string is NULL-terminated */
|
|
|
|
(*str)[len]='\0';
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int read_string(char **str, ops_region_t *subregion, ops_parse_info_t *pinfo)
|
|
{
|
|
return (read_unsigned_string((unsigned char **)str, subregion, pinfo));
|
|
}
|
|
|
|
void ops_init_subregion(ops_region_t *subregion,ops_region_t *region)
|
|
{
|
|
memset(subregion,'\0',sizeof *subregion);
|
|
subregion->parent=region;
|
|
}
|
|
|
|
/*! macro to save typing */
|
|
#define C content.content
|
|
|
|
/* XXX: replace ops_ptag_t with something more appropriate for limiting
|
|
reads */
|
|
|
|
/**
|
|
* 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 *pinfo, 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 *pinfo
|
|
*
|
|
* \return OPS_R_OK
|
|
* \return OPS_R_PARTIAL_READ
|
|
* \return OPS_R_EOF
|
|
* \return OPS_R_EARLY_EOF
|
|
*
|
|
* \sa #ops_reader_ret_t for details of return codes
|
|
*/
|
|
|
|
static int sub_base_read(void *dest,size_t length,ops_error_t **errors,
|
|
ops_reader_info_t *rinfo,ops_parse_cb_info_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=rinfo->reader((char*)dest+n,length-n,errors,rinfo,cbinfo);
|
|
|
|
assert(r <= (int)(length-n));
|
|
|
|
// XXX: should we save the error and return what was read so far?
|
|
if(r < 0)
|
|
return r;
|
|
|
|
if(r == 0)
|
|
break;
|
|
|
|
n+=r;
|
|
}
|
|
|
|
if(n == 0)
|
|
return 0;
|
|
|
|
if(rinfo->accumulate)
|
|
{
|
|
assert(rinfo->asize >= rinfo->alength);
|
|
if(rinfo->alength+n > rinfo->asize)
|
|
{
|
|
rinfo->asize=rinfo->asize*2+n;
|
|
rinfo->accumulated=realloc(rinfo->accumulated,rinfo->asize);
|
|
}
|
|
assert(rinfo->asize >= rinfo->alength+n);
|
|
memcpy(rinfo->accumulated+rinfo->alength,dest,n);
|
|
}
|
|
// we track length anyway, because it is used for packet offsets
|
|
rinfo->alength+=n;
|
|
// and also the position
|
|
rinfo->position+=n;
|
|
|
|
return n;
|
|
}
|
|
|
|
int ops_stacked_read(void *dest,size_t length,ops_error_t **errors,
|
|
ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo)
|
|
{ return sub_base_read(dest,length,errors,rinfo->next,cbinfo); }
|
|
|
|
/* This will do a full read so long as length < MAX_INT */
|
|
static int base_read(unsigned char *dest,size_t length,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
return sub_base_read(dest,length,&pinfo->errors,&pinfo->rinfo,
|
|
&pinfo->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(unsigned char *dest,size_t length,int *last_read,
|
|
ops_error_t **errors,ops_reader_info_t *rinfo,
|
|
ops_parse_cb_info_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(dest+t,length-t,errors,rinfo,cbinfo);
|
|
|
|
if(r <= 0)
|
|
{
|
|
*last_read=r;
|
|
return t;
|
|
}
|
|
|
|
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 ops_true on success, ops_false on failure
|
|
*/
|
|
static ops_boolean_t _read_scalar(unsigned *result,unsigned length,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned t=0;
|
|
|
|
assert (length <= sizeof(*result));
|
|
|
|
while(length--)
|
|
{
|
|
unsigned char c[1];
|
|
int r;
|
|
|
|
r=base_read(c,1,pinfo);
|
|
if(r != 1)
|
|
return ops_false;
|
|
t=(t << 8)+c[0];
|
|
}
|
|
|
|
*result=t;
|
|
return ops_true;
|
|
}
|
|
|
|
/**
|
|
* \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 ops_ptag_t::length_read.
|
|
*
|
|
* 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 rinfo Reader info
|
|
* \param cbinfo Callback info
|
|
* \return ops_true on success, ops_false on error
|
|
*/
|
|
ops_boolean_t ops_limited_read(unsigned char *dest,size_t length,
|
|
ops_region_t *region,ops_error_t **errors,
|
|
ops_reader_info_t *rinfo,
|
|
ops_parse_cb_info_t *cbinfo)
|
|
{
|
|
size_t r;
|
|
int lr;
|
|
|
|
if(!region->indeterminate && region->length_read+length > region->length)
|
|
{
|
|
OPS_ERROR(errors,OPS_E_P_NOT_ENOUGH_DATA,"Not enough data");
|
|
return ops_false;
|
|
}
|
|
|
|
r=full_read(dest,length,&lr,errors,rinfo,cbinfo);
|
|
|
|
if(lr < 0)
|
|
{
|
|
OPS_ERROR(errors,OPS_E_R_READ_FAILED,"Read failed");
|
|
return ops_false;
|
|
}
|
|
|
|
if(!region->indeterminate && r != length)
|
|
{
|
|
OPS_ERROR(errors,OPS_E_R_READ_FAILED,"Read failed");
|
|
return ops_false;
|
|
}
|
|
|
|
region->last_read=r;
|
|
do
|
|
{
|
|
region->length_read+=r;
|
|
assert(!region->parent || region->length <= region->parent->length);
|
|
}
|
|
while((region=region->parent));
|
|
|
|
return ops_true;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Call ops_limited_read on next in stack
|
|
*/
|
|
ops_boolean_t ops_stacked_limited_read(unsigned char *dest,unsigned length,
|
|
ops_region_t *region,
|
|
ops_error_t **errors,
|
|
ops_reader_info_t *rinfo,
|
|
ops_parse_cb_info_t *cbinfo)
|
|
{ return ops_limited_read(dest,length,region,errors,rinfo->next,cbinfo); }
|
|
|
|
static ops_boolean_t limited_read(unsigned char *dest,unsigned length,
|
|
ops_region_t *region,ops_parse_info_t *info)
|
|
{
|
|
return ops_limited_read(dest,length,region,&info->errors,
|
|
&info->rinfo,&info->cbinfo);
|
|
}
|
|
|
|
static ops_boolean_t exact_limited_read(unsigned char *dest,unsigned length,
|
|
ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
ops_boolean_t ret;
|
|
|
|
pinfo->exact_read=ops_true;
|
|
ret=limited_read(dest,length,region,pinfo);
|
|
pinfo->exact_read=ops_false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/** Skip over length bytes of this packet.
|
|
*
|
|
* Calls limited_read() 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 *pinfo How to parse
|
|
* \return 1 on success, 0 on error (calls the cb with OPS_PARSER_ERROR in limited_read()).
|
|
*/
|
|
static int limited_skip(unsigned length,ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char buf[8192];
|
|
|
|
while(length)
|
|
{
|
|
int n=length%8192;
|
|
if(!limited_read(buf,n,region,pinfo))
|
|
return 0;
|
|
length-=n;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/** Read a scalar.
|
|
*
|
|
* Read a big-endian scalar of length bytes, respecting packet
|
|
* boundaries (by calling limited_read() 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 *pinfo How to parse
|
|
* \param *cb The callback
|
|
* \return 1 on success, 0 on error (calls the cb with OPS_PARSER_ERROR in limited_read()).
|
|
*
|
|
* \see RFC4880 3.1
|
|
*/
|
|
static int limited_read_scalar(unsigned *dest,unsigned length,
|
|
ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[4]="";
|
|
unsigned t;
|
|
unsigned n;
|
|
|
|
assert(length <= 4);
|
|
assert(sizeof(*dest) >= 4);
|
|
if(!limited_read(c,length,region,pinfo))
|
|
return 0;
|
|
|
|
for(t=0,n=0 ; n < length ; ++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 limited_read() 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 *pinfo How to parse
|
|
* \param *cb The callback
|
|
* \return 1 on success, 0 on error (calls the cb with OPS_PARSER_ERROR in limited_read()).
|
|
*
|
|
* \see RFC4880 3.1
|
|
*/
|
|
static int limited_read_size_t_scalar(size_t *dest,unsigned length,
|
|
ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned tmp;
|
|
|
|
assert(sizeof(*dest) >= 4);
|
|
|
|
/* Note that because the scalar is at most 4 bytes, we don't care
|
|
if size_t is bigger than usigned */
|
|
if(!limited_read_scalar(&tmp,length,region,pinfo))
|
|
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 limited_read_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 limited_read_scalar()
|
|
*
|
|
* \see RFC4880 3.5
|
|
*/
|
|
static int limited_read_time(time_t *dest,ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
/*
|
|
* Cannot assume that time_t is 4 octets long -
|
|
* there is at least one architecture (SunOS 5.10) where it is 8.
|
|
*/
|
|
if (sizeof(*dest)==4)
|
|
{
|
|
return limited_read_scalar((unsigned *)dest,4,region,pinfo);
|
|
}
|
|
else
|
|
{
|
|
time_t mytime=0;
|
|
int i=0;
|
|
unsigned char c[1];
|
|
for (i=0; i<4; i++)
|
|
{
|
|
if (!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
mytime=(mytime << 8) + c[0];
|
|
}
|
|
*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 limited_read_scalar() or limited_read() or if the MPI is not properly formed (XXX
|
|
* see comment below - the callback is called with a OPS_PARSER_ERROR in case of an error)
|
|
*
|
|
* \see RFC4880 3.2
|
|
*/
|
|
static int limited_read_mpi(BIGNUM **pbn,ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned length;
|
|
unsigned nonzero;
|
|
unsigned char buf[8192]=""; /* an MPI has a 2 byte length part. Length
|
|
is given in bits, so the largest we should
|
|
ever need for the buffer is 8192 bytes. */
|
|
ops_boolean_t ret;
|
|
|
|
pinfo->reading_mpi_length=ops_true;
|
|
ret=limited_read_scalar(&length,2,region,pinfo);
|
|
|
|
pinfo->reading_mpi_length=ops_false;
|
|
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;
|
|
|
|
assert(length <= 8192);
|
|
if(!limited_read(buf,length,region,pinfo))
|
|
return 0;
|
|
|
|
if((buf[0] >> nonzero) != 0 || !(buf[0]&(1 << (nonzero-1))))
|
|
{
|
|
OPS_ERROR(&pinfo->errors,OPS_E_P_MPI_FORMAT_ERROR,"MPI Format error"); /* XXX: Ben, one part of this constraint does not apply to encrypted MPIs the draft says. -- peter */
|
|
return 0;
|
|
}
|
|
|
|
*pbn=BN_bin2bn(buf,length,NULL);
|
|
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 *pinfo How to parse
|
|
* \return ops_true if OK, else ops_false
|
|
*
|
|
*/
|
|
|
|
static ops_boolean_t read_new_length(unsigned *length,ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[1];
|
|
|
|
if(base_read(c,1,pinfo) != 1)
|
|
return ops_false;
|
|
if(c[0] < 192)
|
|
{
|
|
// 1. One-octet packet
|
|
*length=c[0];
|
|
return ops_true;
|
|
}
|
|
|
|
else if (c[0]>=192 && c[0]<=223)
|
|
{
|
|
// 2. Two-octet packet
|
|
unsigned t=(c[0]-192) << 8;
|
|
|
|
if(base_read(c,1,pinfo) != 1)
|
|
return ops_false;
|
|
*length=t+c[0]+192;
|
|
return ops_true;
|
|
}
|
|
|
|
else if (c[0]==255)
|
|
{
|
|
// 3. Five-Octet packet
|
|
return _read_scalar(length,4,pinfo);
|
|
}
|
|
|
|
else if (c[0]>=224 && c[0]<255)
|
|
{
|
|
// 4. Partial Body Length
|
|
OPS_ERROR(&pinfo->errors,OPS_E_UNIMPLEMENTED,
|
|
"New format Partial Body Length fields not yet implemented");
|
|
return ops_false;
|
|
}
|
|
return ops_false;
|
|
}
|
|
|
|
/** 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 limited_read_scalar() or limited_read() or if the MPI is not properly formed (XXX
|
|
* see comment below)
|
|
*
|
|
* \see RFC4880 4.2.2
|
|
* \see ops_ptag_t
|
|
*/
|
|
static int limited_read_new_length(unsigned *length,ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[1]="";
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
if(c[0] < 192)
|
|
{
|
|
*length=c[0];
|
|
return 1;
|
|
}
|
|
if(c[0] < 255)
|
|
{
|
|
unsigned t=(c[0]-192) << 8;
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
*length=t+c[0]+192;
|
|
return 1;
|
|
}
|
|
return limited_read_scalar(length,4,region,pinfo);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
static void data_free(ops_data_t *data)
|
|
{
|
|
free(data->contents);
|
|
data->contents=NULL;
|
|
data->len=0;
|
|
}
|
|
|
|
/**
|
|
\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 ops_packet_free(ops_packet_t *packet)
|
|
{
|
|
free(packet->raw);
|
|
packet->raw=NULL;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
void ops_headers_free(ops_headers_t *headers)
|
|
{
|
|
unsigned n;
|
|
|
|
for(n=0 ; n < headers->nheaders ; ++n)
|
|
{
|
|
free(headers->headers[n].key);
|
|
free(headers->headers[n].value);
|
|
}
|
|
free(headers->headers);
|
|
headers->headers=NULL;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
void ops_signed_cleartext_trailer_free(ops_signed_cleartext_trailer_t *trailer)
|
|
{
|
|
free(trailer->hash);
|
|
trailer->hash=NULL;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
void ops_cmd_get_passphrase_free(ops_secret_key_passphrase_t *skp)
|
|
{
|
|
if (skp->passphrase && *skp->passphrase)
|
|
{
|
|
free(*skp->passphrase);
|
|
*skp->passphrase=NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
/*! Free any memory allocated when parsing the packet content */
|
|
void ops_parser_content_free(ops_parser_content_t *c)
|
|
{
|
|
switch(c->tag)
|
|
{
|
|
case OPS_PARSER_PTAG:
|
|
case OPS_PTAG_CT_COMPRESSED:
|
|
case OPS_PTAG_SS_CREATION_TIME:
|
|
case OPS_PTAG_SS_EXPIRATION_TIME:
|
|
case OPS_PTAG_SS_KEY_EXPIRATION_TIME:
|
|
case OPS_PTAG_SS_TRUST:
|
|
case OPS_PTAG_SS_ISSUER_KEY_ID:
|
|
case OPS_PTAG_CT_ONE_PASS_SIGNATURE:
|
|
case OPS_PTAG_SS_PRIMARY_USER_ID:
|
|
case OPS_PTAG_SS_REVOCABLE:
|
|
case OPS_PTAG_SS_REVOCATION_KEY:
|
|
case OPS_PTAG_CT_LITERAL_DATA_HEADER:
|
|
case OPS_PTAG_CT_LITERAL_DATA_BODY:
|
|
case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY:
|
|
case OPS_PTAG_CT_UNARMOURED_TEXT:
|
|
case OPS_PTAG_CT_ARMOUR_TRAILER:
|
|
case OPS_PTAG_CT_SIGNATURE_HEADER:
|
|
case OPS_PTAG_CT_SE_DATA_HEADER:
|
|
case OPS_PTAG_CT_SE_IP_DATA_HEADER:
|
|
case OPS_PTAG_CT_SE_IP_DATA_BODY:
|
|
case OPS_PTAG_CT_MDC:
|
|
case OPS_PARSER_CMD_GET_SECRET_KEY:
|
|
break;
|
|
|
|
case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER:
|
|
ops_headers_free(&c->content.signed_cleartext_header.headers);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_ARMOUR_HEADER:
|
|
ops_headers_free(&c->content.armour_header.headers);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER:
|
|
ops_signed_cleartext_trailer_free(&c->content.signed_cleartext_trailer);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_TRUST:
|
|
ops_trust_free(&c->content.trust);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_SIGNATURE:
|
|
case OPS_PTAG_CT_SIGNATURE_FOOTER:
|
|
ops_signature_free(&c->content.signature);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_PUBLIC_KEY:
|
|
case OPS_PTAG_CT_PUBLIC_SUBKEY:
|
|
ops_public_key_free(&c->content.public_key);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_USER_ID:
|
|
ops_user_id_free(&c->content.user_id);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_SIGNERS_USER_ID:
|
|
ops_user_id_free(&c->content.ss_signers_user_id);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_USER_ATTRIBUTE:
|
|
ops_user_attribute_free(&c->content.user_attribute);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_PREFERRED_SKA:
|
|
ops_ss_preferred_ska_free(&c->content.ss_preferred_ska);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_PREFERRED_HASH:
|
|
ops_ss_preferred_hash_free(&c->content.ss_preferred_hash);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_PREFERRED_COMPRESSION:
|
|
ops_ss_preferred_compression_free(&c->content.ss_preferred_compression);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_KEY_FLAGS:
|
|
ops_ss_key_flags_free(&c->content.ss_key_flags);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_KEY_SERVER_PREFS:
|
|
ops_ss_key_server_prefs_free(&c->content.ss_key_server_prefs);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_FEATURES:
|
|
ops_ss_features_free(&c->content.ss_features);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_NOTATION_DATA:
|
|
ops_ss_notation_data_free(&c->content.ss_notation_data);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_REGEXP:
|
|
ops_ss_regexp_free(&c->content.ss_regexp);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_POLICY_URI:
|
|
ops_ss_policy_url_free(&c->content.ss_policy_url);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_PREFERRED_KEY_SERVER:
|
|
ops_ss_preferred_key_server_free(&c->content.ss_preferred_key_server);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_USERDEFINED00:
|
|
case OPS_PTAG_SS_USERDEFINED01:
|
|
case OPS_PTAG_SS_USERDEFINED02:
|
|
case OPS_PTAG_SS_USERDEFINED03:
|
|
case OPS_PTAG_SS_USERDEFINED04:
|
|
case OPS_PTAG_SS_USERDEFINED05:
|
|
case OPS_PTAG_SS_USERDEFINED06:
|
|
case OPS_PTAG_SS_USERDEFINED07:
|
|
case OPS_PTAG_SS_USERDEFINED08:
|
|
case OPS_PTAG_SS_USERDEFINED09:
|
|
case OPS_PTAG_SS_USERDEFINED10:
|
|
ops_ss_userdefined_free(&c->content.ss_userdefined);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_RESERVED:
|
|
ops_ss_reserved_free(&c->content.ss_unknown);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_REVOCATION_REASON:
|
|
ops_ss_revocation_reason_free(&c->content.ss_revocation_reason);
|
|
break;
|
|
|
|
case OPS_PTAG_SS_EMBEDDED_SIGNATURE:
|
|
ops_ss_embedded_signature_free(&c->content.ss_embedded_signature);
|
|
break;
|
|
|
|
case OPS_PARSER_PACKET_END:
|
|
ops_packet_free(&c->content.packet);
|
|
break;
|
|
|
|
case OPS_PARSER_ERROR:
|
|
case OPS_PARSER_ERRCODE:
|
|
break;
|
|
|
|
case OPS_PTAG_CT_SECRET_KEY:
|
|
case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY:
|
|
ops_secret_key_free(&c->content.secret_key);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_PK_SESSION_KEY:
|
|
case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY:
|
|
ops_pk_session_key_free(&c->content.pk_session_key);
|
|
break;
|
|
|
|
case OPS_PARSER_CMD_GET_SK_PASSPHRASE:
|
|
ops_cmd_get_passphrase_free(&c->content.secret_key_passphrase);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,"Can't free %d (0x%x)\n",c->tag,c->tag);
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
static void free_BN(BIGNUM **pp)
|
|
{
|
|
BN_free(*pp);
|
|
*pp=NULL;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
void ops_pk_session_key_free(ops_pk_session_key_t *sk)
|
|
{
|
|
switch(sk->algorithm)
|
|
{
|
|
case OPS_PKA_RSA:
|
|
free_BN(&sk->parameters.rsa.encrypted_m);
|
|
break;
|
|
|
|
case OPS_PKA_ELGAMAL:
|
|
free_BN(&sk->parameters.elgamal.g_to_k);
|
|
free_BN(&sk->parameters.elgamal.encrypted_m);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
/*! Free the memory used when parsing a public key */
|
|
void ops_public_key_free(ops_public_key_t *p)
|
|
{
|
|
switch(p->algorithm)
|
|
{
|
|
case OPS_PKA_RSA:
|
|
case OPS_PKA_RSA_ENCRYPT_ONLY:
|
|
case OPS_PKA_RSA_SIGN_ONLY:
|
|
free_BN(&p->key.rsa.n);
|
|
free_BN(&p->key.rsa.e);
|
|
break;
|
|
|
|
case OPS_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 OPS_PKA_ELGAMAL:
|
|
case OPS_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 0:
|
|
// nothing to free
|
|
// break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
*/
|
|
static int parse_public_key_data(ops_public_key_t *key,ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[1]="";
|
|
|
|
assert (region->length_read == 0); /* We should not have read anything so far */
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
key->version=c[0];
|
|
if(key->version < 2 || key->version > 4)
|
|
{
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_BAD_PUBLIC_KEY_VRSN,
|
|
"Bad public key version (0x%02x)",key->version);
|
|
return 0;
|
|
}
|
|
|
|
if(!limited_read_time(&key->creation_time,region,pinfo))
|
|
return 0;
|
|
|
|
key->days_valid=0;
|
|
if((key->version == 2 || key->version == 3)
|
|
&& !limited_read_scalar(&key->days_valid,2,region,pinfo))
|
|
return 0;
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
|
|
key->algorithm=c[0];
|
|
|
|
switch(key->algorithm)
|
|
{
|
|
case OPS_PKA_DSA:
|
|
if(!limited_read_mpi(&key->key.dsa.p,region,pinfo)
|
|
|| !limited_read_mpi(&key->key.dsa.q,region,pinfo)
|
|
|| !limited_read_mpi(&key->key.dsa.g,region,pinfo)
|
|
|| !limited_read_mpi(&key->key.dsa.y,region,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PKA_RSA:
|
|
case OPS_PKA_RSA_ENCRYPT_ONLY:
|
|
case OPS_PKA_RSA_SIGN_ONLY:
|
|
if(!limited_read_mpi(&key->key.rsa.n,region,pinfo)
|
|
|| !limited_read_mpi(&key->key.rsa.e,region,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PKA_ELGAMAL:
|
|
case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
|
|
if(!limited_read_mpi(&key->key.elgamal.p,region,pinfo)
|
|
|| !limited_read_mpi(&key->key.elgamal.g,region,pinfo)
|
|
|| !limited_read_mpi(&key->key.elgamal.y,region,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
default:
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,"Unsupported Public Key algorithm (%s)",ops_show_pka(key->algorithm));
|
|
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_public_key(ops_content_tag_t tag,ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
ops_parser_content_t content;
|
|
|
|
if(!parse_public_key_data(&C.public_key,region,pinfo))
|
|
return 0;
|
|
|
|
// XXX: this test should be done for all packets, surely?
|
|
if(region->length_read != region->length)
|
|
{
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA,
|
|
"Unconsumed data (%d)", region->length-region->length_read);
|
|
return 0;
|
|
}
|
|
|
|
CBP(pinfo,tag,&content);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
/*! Free the memory used when parsing this signature sub-packet type */
|
|
void ops_ss_regexp_free(ops_ss_regexp_t *regexp)
|
|
{
|
|
string_free(®exp->text);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
/*! Free the memory used when parsing this signature sub-packet type */
|
|
void ops_ss_policy_url_free(ops_ss_policy_url_t *policy_url)
|
|
{
|
|
string_free(&policy_url->text);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
/*! Free the memory used when parsing this signature sub-packet type */
|
|
void ops_ss_preferred_key_server_free(ops_ss_preferred_key_server_t *preferred_key_server)
|
|
{
|
|
string_free(&preferred_key_server->text);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
/*! Free the memory used when parsing this packet type */
|
|
void ops_user_attribute_free(ops_user_attribute_t *user_att)
|
|
{
|
|
data_free(&user_att->data);
|
|
}
|
|
|
|
/**
|
|
* \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_user_attribute(ops_region_t *region, ops_parse_info_t *pinfo)
|
|
{
|
|
|
|
ops_parser_content_t content;
|
|
|
|
/* xxx- treat as raw data for now. Could break down further
|
|
into attribute sub-packets later - rachel */
|
|
|
|
assert(region->length_read == 0); /* We should not have read anything so far */
|
|
|
|
if(!read_data(&C.user_attribute.data,region,pinfo))
|
|
return 0;
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_USER_ATTRIBUTE,&content);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free allocated memory
|
|
*/
|
|
/*! Free the memory used when parsing this packet type */
|
|
void ops_user_id_free(ops_user_id_t *id)
|
|
{
|
|
free(id->user_id);
|
|
id->user_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_user_id(ops_region_t *region,ops_parse_info_t *pinfo)
|
|
{
|
|
ops_parser_content_t content;
|
|
|
|
assert(region->length_read == 0); /* We should not have read anything so far */
|
|
|
|
C.user_id.user_id=malloc(region->length+1); /* XXX should we not like check malloc's return value? */
|
|
|
|
if(region->length && !limited_read(C.user_id.user_id,region->length,region,
|
|
pinfo))
|
|
return 0;
|
|
|
|
C.user_id.user_id[region->length]='\0'; /* terminate the string */
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_USER_ID,&content);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \ingroup Core_Create
|
|
* \brief Free the memory used when parsing a private/experimental PKA signature
|
|
* \param unknown_sig
|
|
*/
|
|
void free_unknown_sig_pka(ops_unknown_signature_t *unknown_sig)
|
|
{
|
|
data_free(&unknown_sig->data);
|
|
}
|
|
|
|
/**
|
|
* \ingroup Core_Create
|
|
* \brief Free the memory used when parsing a signature
|
|
* \param sig
|
|
*/
|
|
void ops_signature_free(ops_signature_t *sig)
|
|
{
|
|
switch(sig->info.key_algorithm)
|
|
{
|
|
case OPS_PKA_RSA:
|
|
case OPS_PKA_RSA_SIGN_ONLY:
|
|
free_BN(&sig->info.signature.rsa.sig);
|
|
break;
|
|
|
|
case OPS_PKA_DSA:
|
|
free_BN(&sig->info.signature.dsa.r);
|
|
free_BN(&sig->info.signature.dsa.s);
|
|
break;
|
|
|
|
case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
|
|
free_BN(&sig->info.signature.elgamal.r);
|
|
free_BN(&sig->info.signature.elgamal.s);
|
|
break;
|
|
|
|
case OPS_PKA_PRIVATE00:
|
|
case OPS_PKA_PRIVATE01:
|
|
case OPS_PKA_PRIVATE02:
|
|
case OPS_PKA_PRIVATE03:
|
|
case OPS_PKA_PRIVATE04:
|
|
case OPS_PKA_PRIVATE05:
|
|
case OPS_PKA_PRIVATE06:
|
|
case OPS_PKA_PRIVATE07:
|
|
case OPS_PKA_PRIVATE08:
|
|
case OPS_PKA_PRIVATE09:
|
|
case OPS_PKA_PRIVATE10:
|
|
free_unknown_sig_pka(&sig->info.signature.unknown);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \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_signature(ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[1]="";
|
|
ops_parser_content_t content;
|
|
|
|
// clear signature
|
|
memset(&C.signature,'\0',sizeof C.signature);
|
|
|
|
C.signature.info.version=OPS_V3;
|
|
|
|
/* hash info length */
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
if(c[0] != 5)
|
|
ERRP(pinfo,"bad hash info length");
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.signature.info.type=c[0];
|
|
/* XXX: check signature type */
|
|
|
|
if(!limited_read_time(&C.signature.info.creation_time,region,pinfo))
|
|
return 0;
|
|
C.signature.info.creation_time_set=ops_true;
|
|
|
|
if(!limited_read(C.signature.info.signer_id,OPS_KEY_ID_SIZE,region,pinfo))
|
|
return 0;
|
|
C.signature.info.signer_id_set=ops_true;
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.signature.info.key_algorithm=c[0];
|
|
/* XXX: check algorithm */
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.signature.info.hash_algorithm=c[0];
|
|
/* XXX: check algorithm */
|
|
|
|
if(!limited_read(C.signature.hash2,2,region,pinfo))
|
|
return 0;
|
|
|
|
switch(C.signature.info.key_algorithm)
|
|
{
|
|
case OPS_PKA_RSA:
|
|
case OPS_PKA_RSA_SIGN_ONLY:
|
|
if(!limited_read_mpi(&C.signature.info.signature.rsa.sig,region,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PKA_DSA:
|
|
if(!limited_read_mpi(&C.signature.info.signature.dsa.r,region,pinfo)
|
|
|| !limited_read_mpi(&C.signature.info.signature.dsa.s,region,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
|
|
if(!limited_read_mpi(&C.signature.info.signature.elgamal.r,region,pinfo)
|
|
|| !limited_read_mpi(&C.signature.info.signature.elgamal.s,region,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
default:
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_SIGNATURE_ALG,
|
|
"Unsupported signature key algorithm (%s)",
|
|
ops_show_pka(C.signature.info.key_algorithm));
|
|
return 0;
|
|
}
|
|
|
|
if(region->length_read != region->length)
|
|
{
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA,"Unconsumed data (%d)",region->length-region->length_read);
|
|
return 0;
|
|
}
|
|
|
|
if(C.signature.info.signer_id_set)
|
|
C.signature.hash=ops_parse_hash_find(pinfo,C.signature.info.signer_id);
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_SIGNATURE,&content);
|
|
|
|
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_signature_subpacket(ops_signature_t *sig,
|
|
ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
ops_region_t subregion;
|
|
unsigned char c[1]="";
|
|
ops_parser_content_t content;
|
|
unsigned t8,t7;
|
|
ops_boolean_t read=ops_true;
|
|
unsigned char bool[1]="";
|
|
|
|
ops_init_subregion(&subregion,region);
|
|
if(!limited_read_new_length(&subregion.length,region,pinfo))
|
|
return 0;
|
|
|
|
if(subregion.length > region->length)
|
|
ERRP(pinfo,"Subpacket too long");
|
|
|
|
if(!limited_read(c,1,&subregion,pinfo))
|
|
return 0;
|
|
|
|
t8=(c[0]&0x7f)/8;
|
|
t7=1 << (c[0]&7);
|
|
|
|
content.critical=c[0] >> 7;
|
|
content.tag=OPS_PTAG_SIGNATURE_SUBPACKET_BASE+(c[0]&0x7f);
|
|
|
|
/* Application wants it delivered raw */
|
|
if(pinfo->ss_raw[t8]&t7)
|
|
{
|
|
C.ss_raw.tag=content.tag;
|
|
C.ss_raw.length=subregion.length-1;
|
|
C.ss_raw.raw=malloc(C.ss_raw.length);
|
|
if(!limited_read(C.ss_raw.raw,C.ss_raw.length,&subregion,pinfo))
|
|
return 0;
|
|
CBP(pinfo,OPS_PTAG_RAW_SS,&content);
|
|
return 1;
|
|
}
|
|
|
|
switch(content.tag)
|
|
{
|
|
case OPS_PTAG_SS_CREATION_TIME:
|
|
case OPS_PTAG_SS_EXPIRATION_TIME:
|
|
case OPS_PTAG_SS_KEY_EXPIRATION_TIME:
|
|
if(!limited_read_time(&C.ss_time.time,&subregion,pinfo))
|
|
return 0;
|
|
if(content.tag == OPS_PTAG_SS_CREATION_TIME)
|
|
{
|
|
sig->info.creation_time=C.ss_time.time;
|
|
sig->info.creation_time_set=ops_true;
|
|
}
|
|
break;
|
|
|
|
case OPS_PTAG_SS_TRUST:
|
|
if(!limited_read(&C.ss_trust.level,1,&subregion,pinfo)
|
|
|| !limited_read(&C.ss_trust.amount,1,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_REVOCABLE:
|
|
if(!limited_read(bool,1,&subregion,pinfo))
|
|
return 0;
|
|
C.ss_revocable.revocable=!!bool[0];
|
|
break;
|
|
|
|
case OPS_PTAG_SS_ISSUER_KEY_ID:
|
|
if(!limited_read(C.ss_issuer_key_id.key_id,OPS_KEY_ID_SIZE,
|
|
&subregion,pinfo))
|
|
return 0;
|
|
memcpy(sig->info.signer_id,C.ss_issuer_key_id.key_id,OPS_KEY_ID_SIZE);
|
|
sig->info.signer_id_set=ops_true;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_PREFERRED_SKA:
|
|
if(!read_data(&C.ss_preferred_ska.data,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_PREFERRED_HASH:
|
|
if(!read_data(&C.ss_preferred_hash.data,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_PREFERRED_COMPRESSION:
|
|
if(!read_data(&C.ss_preferred_compression.data,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_PRIMARY_USER_ID:
|
|
if(!limited_read (bool,1,&subregion,pinfo))
|
|
return 0;
|
|
C.ss_primary_user_id.primary_user_id = !!bool[0];
|
|
break;
|
|
|
|
case OPS_PTAG_SS_KEY_FLAGS:
|
|
if(!read_data(&C.ss_key_flags.data,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_KEY_SERVER_PREFS:
|
|
if(!read_data(&C.ss_key_server_prefs.data,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_FEATURES:
|
|
if(!read_data(&C.ss_features.data,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_SIGNERS_USER_ID:
|
|
if(!read_unsigned_string(&C.ss_signers_user_id.user_id,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_EMBEDDED_SIGNATURE:
|
|
// \todo should do something with this sig?
|
|
if (!read_data(&C.ss_embedded_signature.sig,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_NOTATION_DATA:
|
|
if(!limited_read_data(&C.ss_notation_data.flags,4,&subregion,pinfo))
|
|
return 0;
|
|
if(!limited_read_size_t_scalar(&C.ss_notation_data.name.len,2,
|
|
&subregion,pinfo))
|
|
return 0;
|
|
if(!limited_read_size_t_scalar(&C.ss_notation_data.value.len,2,
|
|
&subregion,pinfo))
|
|
return 0;
|
|
if(!limited_read_data(&C.ss_notation_data.name,
|
|
C.ss_notation_data.name.len,&subregion,pinfo))
|
|
return 0;
|
|
if(!limited_read_data(&C.ss_notation_data.value,
|
|
C.ss_notation_data.value.len,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_POLICY_URI:
|
|
if(!read_string(&C.ss_policy_url.text,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_REGEXP:
|
|
if(!read_string(&C.ss_regexp.text,&subregion, pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_PREFERRED_KEY_SERVER:
|
|
if(!read_string(&C.ss_preferred_key_server.text,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_USERDEFINED00:
|
|
case OPS_PTAG_SS_USERDEFINED01:
|
|
case OPS_PTAG_SS_USERDEFINED02:
|
|
case OPS_PTAG_SS_USERDEFINED03:
|
|
case OPS_PTAG_SS_USERDEFINED04:
|
|
case OPS_PTAG_SS_USERDEFINED05:
|
|
case OPS_PTAG_SS_USERDEFINED06:
|
|
case OPS_PTAG_SS_USERDEFINED07:
|
|
case OPS_PTAG_SS_USERDEFINED08:
|
|
case OPS_PTAG_SS_USERDEFINED09:
|
|
case OPS_PTAG_SS_USERDEFINED10:
|
|
if(!read_data(&C.ss_userdefined.data,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_RESERVED:
|
|
if(!read_data(&C.ss_unknown.data,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_REVOCATION_REASON:
|
|
/* first byte is the machine-readable code */
|
|
if(!limited_read(&C.ss_revocation_reason.code,1,&subregion,pinfo))
|
|
return 0;
|
|
|
|
/* the rest is a human-readable UTF-8 string */
|
|
if(!read_string(&C.ss_revocation_reason.text,&subregion,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PTAG_SS_REVOCATION_KEY:
|
|
/* octet 0 = class. Bit 0x80 must be set */
|
|
if(!limited_read (&C.ss_revocation_key.class,1,&subregion,pinfo))
|
|
return 0;
|
|
if(!(C.ss_revocation_key.class&0x80))
|
|
{
|
|
printf("Warning: OPS_PTAG_SS_REVOCATION_KEY class: "
|
|
"Bit 0x80 should be set\n");
|
|
return 0;
|
|
}
|
|
|
|
/* octet 1 = algid */
|
|
if(!limited_read(&C.ss_revocation_key.algid,1,&subregion,pinfo))
|
|
return 0;
|
|
|
|
/* octets 2-21 = fingerprint */
|
|
if(!limited_read(&C.ss_revocation_key.fingerprint[0],20,&subregion,
|
|
pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
default:
|
|
if(pinfo->ss_parsed[t8]&t7)
|
|
OPS_ERROR_1(&pinfo->errors, OPS_E_PROTO_UNKNOWN_SS,
|
|
"Unknown signature subpacket type (%d)", c[0]&0x7f);
|
|
read=ops_false;
|
|
break;
|
|
}
|
|
|
|
/* Application doesn't want it delivered parsed */
|
|
if(!(pinfo->ss_parsed[t8]&t7))
|
|
{
|
|
if(content.critical)
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_CRITICAL_SS_IGNORED,
|
|
"Critical signature subpacket ignored (%d)",
|
|
c[0]&0x7f);
|
|
if(!read && !limited_skip(subregion.length-1,&subregion,pinfo))
|
|
return 0;
|
|
// printf("skipped %d length %d\n",c[0]&0x7f,subregion.length);
|
|
if(read)
|
|
ops_parser_content_free(&content);
|
|
return 1;
|
|
}
|
|
|
|
if(read && subregion.length_read != subregion.length)
|
|
{
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA,
|
|
"Unconsumed data (%d)",
|
|
subregion.length-subregion.length_read);
|
|
return 0;
|
|
}
|
|
|
|
CBP(pinfo,content.tag,&content);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this signature sub-packet type
|
|
\param ss_preferred_ska
|
|
*/
|
|
void ops_ss_preferred_ska_free(ops_ss_preferred_ska_t *ss_preferred_ska)
|
|
{
|
|
data_free(&ss_preferred_ska->data);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this signature sub-packet type
|
|
\param ss_preferred_hash
|
|
*/
|
|
void ops_ss_preferred_hash_free(ops_ss_preferred_hash_t *ss_preferred_hash)
|
|
{
|
|
data_free(&ss_preferred_hash->data);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this signature sub-packet type
|
|
*/
|
|
void ops_ss_preferred_compression_free(ops_ss_preferred_compression_t *ss_preferred_compression)
|
|
{
|
|
data_free(&ss_preferred_compression->data);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this signature sub-packet type
|
|
*/
|
|
void ops_ss_key_flags_free(ops_ss_key_flags_t *ss_key_flags)
|
|
{
|
|
data_free(&ss_key_flags->data);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this signature sub-packet type
|
|
*/
|
|
void ops_ss_features_free(ops_ss_features_t *ss_features)
|
|
{
|
|
data_free(&ss_features->data);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this signature sub-packet type
|
|
*/
|
|
void ops_ss_key_server_prefs_free(ops_ss_key_server_prefs_t *ss_key_server_prefs)
|
|
{
|
|
data_free(&ss_key_server_prefs->data);
|
|
}
|
|
|
|
/**
|
|
* \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_signature_subpacket() for each subpacket until the
|
|
* entire set is consumed.
|
|
*
|
|
* This function does not call the callback directly, parse_one_signature_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_signature_subpackets(ops_signature_t *sig,
|
|
ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
ops_region_t subregion;
|
|
ops_parser_content_t content;
|
|
|
|
ops_init_subregion(&subregion,region);
|
|
if(!limited_read_scalar(&subregion.length,2,region,pinfo))
|
|
return 0;
|
|
|
|
if(subregion.length > region->length)
|
|
ERRP(pinfo,"Subpacket set too long");
|
|
|
|
while(subregion.length_read < subregion.length)
|
|
if(!parse_one_signature_subpacket(sig,&subregion,pinfo))
|
|
return 0;
|
|
|
|
if(subregion.length_read != subregion.length)
|
|
{
|
|
if(!limited_skip(subregion.length-subregion.length_read,&subregion,
|
|
pinfo))
|
|
ERRP(pinfo,"Read failed while recovering from subpacket length mismatch");
|
|
ERRP(pinfo,"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_signature(ops_region_t *region,ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[1]="";
|
|
ops_parser_content_t content;
|
|
|
|
//debug=1;
|
|
if (debug)
|
|
{ fprintf(stderr, "\nparse_v4_signature\n"); }
|
|
|
|
// clear signature
|
|
memset(&C.signature,'\0',sizeof C.signature);
|
|
|
|
/* We need to hash the packet data from version through the hashed subpacket data */
|
|
|
|
C.signature.v4_hashed_data_start=pinfo->rinfo.alength-1;
|
|
|
|
/* Set version,type,algorithms */
|
|
|
|
C.signature.info.version=OPS_V4;
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.signature.info.type=c[0];
|
|
if (debug)
|
|
{ fprintf(stderr, "signature type=%d\n", C.signature.info.type); }
|
|
|
|
/* XXX: check signature type */
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.signature.info.key_algorithm=c[0];
|
|
/* XXX: check algorithm */
|
|
if (debug)
|
|
{ fprintf(stderr, "key_algorithm=%d\n", C.signature.info.key_algorithm); }
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.signature.info.hash_algorithm=c[0];
|
|
/* XXX: check algorithm */
|
|
if (debug)
|
|
{ fprintf(stderr, "hash_algorithm=%d %s\n", C.signature.info.hash_algorithm, ops_show_hash_algorithm(C.signature.info.hash_algorithm)); }
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_SIGNATURE_HEADER,&content);
|
|
|
|
if(!parse_signature_subpackets(&C.signature,region,pinfo))
|
|
return 0;
|
|
|
|
C.signature.info.v4_hashed_data_length=pinfo->rinfo.alength
|
|
-C.signature.v4_hashed_data_start;
|
|
|
|
// copy hashed subpackets
|
|
if (C.signature.info.v4_hashed_data)
|
|
free(C.signature.info.v4_hashed_data);
|
|
C.signature.info.v4_hashed_data=ops_mallocz(C.signature.info.v4_hashed_data_length);
|
|
|
|
if (!pinfo->rinfo.accumulate)
|
|
{
|
|
/* We must accumulate, else we can't check the signature */
|
|
fprintf(stderr,"*** ERROR: must set accumulate to true\n");
|
|
assert(0);
|
|
}
|
|
|
|
memcpy(C.signature.info.v4_hashed_data,
|
|
pinfo->rinfo.accumulated+C.signature.v4_hashed_data_start,
|
|
C.signature.info.v4_hashed_data_length);
|
|
|
|
if(!parse_signature_subpackets(&C.signature,region,pinfo))
|
|
return 0;
|
|
|
|
if(!limited_read(C.signature.hash2,2,region,pinfo))
|
|
return 0;
|
|
|
|
switch(C.signature.info.key_algorithm)
|
|
{
|
|
case OPS_PKA_RSA:
|
|
if(!limited_read_mpi(&C.signature.info.signature.rsa.sig,region,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PKA_DSA:
|
|
if(!limited_read_mpi(&C.signature.info.signature.dsa.r,region,pinfo))
|
|
ERRP(pinfo,"Error reading DSA r field in signature");
|
|
if (!limited_read_mpi(&C.signature.info.signature.dsa.s,region,pinfo))
|
|
ERRP(pinfo,"Error reading DSA s field in signature");
|
|
break;
|
|
|
|
case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
|
|
if(!limited_read_mpi(&C.signature.info.signature.elgamal.r,region,pinfo)
|
|
|| !limited_read_mpi(&C.signature.info.signature.elgamal.s,region,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
case OPS_PKA_PRIVATE00:
|
|
case OPS_PKA_PRIVATE01:
|
|
case OPS_PKA_PRIVATE02:
|
|
case OPS_PKA_PRIVATE03:
|
|
case OPS_PKA_PRIVATE04:
|
|
case OPS_PKA_PRIVATE05:
|
|
case OPS_PKA_PRIVATE06:
|
|
case OPS_PKA_PRIVATE07:
|
|
case OPS_PKA_PRIVATE08:
|
|
case OPS_PKA_PRIVATE09:
|
|
case OPS_PKA_PRIVATE10:
|
|
if (!read_data(&C.signature.info.signature.unknown.data,region,pinfo))
|
|
return 0;
|
|
break;
|
|
|
|
default:
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_SIGNATURE_ALG,
|
|
"Bad v4 signature key algorithm (%s)",
|
|
ops_show_pka(C.signature.info.key_algorithm));
|
|
return 0;
|
|
}
|
|
|
|
if(region->length_read != region->length)
|
|
{
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA,
|
|
"Unconsumed data (%d)",
|
|
region->length-region->length_read);
|
|
return 0;
|
|
}
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_SIGNATURE_FOOTER,&content);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \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_signature(ops_region_t *region,ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[1]="";
|
|
ops_parser_content_t content;
|
|
|
|
assert(region->length_read == 0); /* We should not have read anything so far */
|
|
|
|
memset(&content,'\0',sizeof content);
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
|
|
if(c[0] == 2 || c[0] == 3)
|
|
return parse_v3_signature(region,pinfo);
|
|
else if(c[0] == 4)
|
|
return parse_v4_signature(region,pinfo);
|
|
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_BAD_SIGNATURE_VRSN,
|
|
"Bad signature version (%d)",c[0]);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Parse Compressed packet
|
|
*/
|
|
static int parse_compressed(ops_region_t *region,ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[1]="";
|
|
ops_parser_content_t content;
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
|
|
C.compressed.type=c[0];
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_COMPRESSED,&content);
|
|
|
|
/* The content of a compressed data packet is more OpenPGP packets
|
|
once decompressed, so recursively handle them */
|
|
|
|
return ops_decompress(region,pinfo,C.compressed.type);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Parse a One Pass Signature packet
|
|
*/
|
|
static int parse_one_pass(ops_region_t *region,ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[1]="";
|
|
ops_parser_content_t content;
|
|
|
|
if(!limited_read(&C.one_pass_signature.version,1,region,pinfo))
|
|
return 0;
|
|
if(C.one_pass_signature.version != 3)
|
|
{
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_BAD_ONE_PASS_SIG_VRSN,
|
|
"Bad one-pass signature version (%d)",
|
|
C.one_pass_signature.version);
|
|
return 0;
|
|
}
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.one_pass_signature.sig_type=c[0];
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.one_pass_signature.hash_algorithm=c[0];
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.one_pass_signature.key_algorithm=c[0];
|
|
|
|
if(!limited_read(C.one_pass_signature.keyid,
|
|
sizeof C.one_pass_signature.keyid,region,pinfo))
|
|
return 0;
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.one_pass_signature.nested=!!c[0];
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_ONE_PASS_SIGNATURE,&content);
|
|
|
|
// XXX: we should, perhaps, let the app choose whether to hash or not
|
|
ops_parse_hash_init(pinfo,C.one_pass_signature.hash_algorithm,
|
|
C.one_pass_signature.keyid);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this signature sub-packet type
|
|
*/
|
|
void ops_ss_userdefined_free(ops_ss_userdefined_t *ss_userdefined)
|
|
{
|
|
data_free(&ss_userdefined->data);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this signature sub-packet type
|
|
*/
|
|
void ops_ss_reserved_free(ops_ss_unknown_t *ss_unknown)
|
|
{
|
|
data_free(&ss_unknown->data);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this signature sub-packet type
|
|
*/
|
|
void ops_ss_notation_data_free(ops_ss_notation_data_t *ss_notation_data)
|
|
{
|
|
data_free(&ss_notation_data->name);
|
|
data_free(&ss_notation_data->value);
|
|
}
|
|
|
|
void ops_ss_embedded_signature_free(ops_ss_embedded_signature_t *ss_embedded_signature)
|
|
{
|
|
data_free(&ss_embedded_signature->sig);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this signature sub-packet type
|
|
*/
|
|
void ops_ss_revocation_reason_free(ops_ss_revocation_reason_t *ss_revocation_reason)
|
|
{
|
|
string_free(&ss_revocation_reason->text);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_Create
|
|
\brief Free the memory used when parsing this packet type
|
|
*/
|
|
void ops_trust_free(ops_trust_t *trust)
|
|
{
|
|
data_free(&trust->data);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Parse a Trust packet
|
|
*/
|
|
static int
|
|
parse_trust (ops_region_t *region, ops_parse_info_t *pinfo)
|
|
{
|
|
ops_parser_content_t content;
|
|
|
|
if(!read_data(&C.trust.data,region,pinfo))
|
|
return 0;
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_TRUST, &content);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Parse a Literal Data packet
|
|
*/
|
|
static int parse_literal_data(ops_region_t *region,ops_parse_info_t *pinfo)
|
|
{
|
|
ops_parser_content_t content;
|
|
unsigned char c[1]="";
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.literal_data_header.format=c[0];
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
if(!limited_read((unsigned char *)C.literal_data_header.filename,c[0],
|
|
region,pinfo))
|
|
return 0;
|
|
C.literal_data_header.filename[c[0]]='\0';
|
|
|
|
if(!limited_read_time(&C.literal_data_header.modification_time,region,pinfo))
|
|
return 0;
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_LITERAL_DATA_HEADER,&content);
|
|
|
|
while(region->length_read < region->length)
|
|
{
|
|
unsigned l=region->length-region->length_read;
|
|
|
|
if(l > sizeof C.literal_data_body.data)
|
|
l=sizeof C.literal_data_body.data;
|
|
|
|
if(!limited_read(C.literal_data_body.data,l,region,pinfo))
|
|
return 0;
|
|
|
|
C.literal_data_body.length=l;
|
|
|
|
ops_parse_hash_data(pinfo,C.literal_data_body.data,l);
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_LITERAL_DATA_BODY,&content);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \ingroup Core_Create
|
|
*
|
|
* ops_secret_key_free() frees the memory associated with "key". Note that
|
|
* the key itself is not freed.
|
|
*
|
|
* \param key
|
|
*/
|
|
|
|
void ops_secret_key_free(ops_secret_key_t *key)
|
|
{
|
|
switch(key->public_key.algorithm)
|
|
{
|
|
case OPS_PKA_RSA:
|
|
case OPS_PKA_RSA_ENCRYPT_ONLY:
|
|
case OPS_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 OPS_PKA_DSA:
|
|
free_BN(&key->key.dsa.x);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,"ops_secret_key_free: Unknown algorithm: %d (%s)\n",key->public_key.algorithm, ops_show_pka(key->public_key.algorithm));
|
|
//assert(0);
|
|
}
|
|
|
|
ops_public_key_free(&key->public_key);
|
|
}
|
|
|
|
static int consume_packet(ops_region_t *region,ops_parse_info_t *pinfo,
|
|
ops_boolean_t warn)
|
|
{
|
|
ops_data_t remainder;
|
|
ops_parser_content_t content;
|
|
|
|
if(region->indeterminate)
|
|
ERRP(pinfo,"Can't consume indeterminate packets");
|
|
|
|
if(read_data(&remainder,region,pinfo))
|
|
{
|
|
/* now throw it away */
|
|
data_free(&remainder);
|
|
if(warn)
|
|
OPS_ERROR(&pinfo->errors,OPS_E_P_PACKET_CONSUMED,"Warning: packet consumer");
|
|
}
|
|
else if(warn)
|
|
OPS_ERROR(&pinfo->errors,OPS_E_P_PACKET_NOT_CONSUMED,"Warning: Packet was not consumed");
|
|
else
|
|
{
|
|
OPS_ERROR(&pinfo->errors,OPS_E_P_PACKET_NOT_CONSUMED,"Packet was not consumed");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \ingroup Core_ReadPackets
|
|
* \brief Parse a secret key
|
|
*/
|
|
static int parse_secret_key(ops_region_t *region,ops_parse_info_t *pinfo)
|
|
{
|
|
ops_parser_content_t content;
|
|
unsigned char c[1]="";
|
|
ops_crypt_t decrypt;
|
|
int ret=1;
|
|
ops_region_t encregion;
|
|
ops_region_t *saved_region=NULL;
|
|
size_t checksum_length=2;
|
|
ops_hash_t checkhash;
|
|
int blocksize;
|
|
ops_boolean_t crypted;
|
|
|
|
if (debug)
|
|
{ fprintf(stderr,"\n---------\nparse_secret_key:\n");
|
|
fprintf(stderr,"region length=%d, length_read=%d, remainder=%d\n", region->length, region->length_read, region->length-region->length_read);
|
|
}
|
|
|
|
memset(&content,'\0',sizeof content);
|
|
if(!parse_public_key_data(&C.secret_key.public_key,region,pinfo))
|
|
return 0;
|
|
|
|
if (debug)
|
|
{
|
|
fprintf(stderr,"parse_secret_key: public key parsed\n");
|
|
ops_print_public_key(&C.secret_key.public_key);
|
|
}
|
|
|
|
pinfo->reading_v3_secret=C.secret_key.public_key.version != OPS_V4;
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.secret_key.s2k_usage=c[0];
|
|
if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED)
|
|
checksum_length=20;
|
|
|
|
if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED
|
|
|| C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED)
|
|
{
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.secret_key.algorithm=c[0];
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.secret_key.s2k_specifier=c[0];
|
|
|
|
assert(C.secret_key.s2k_specifier == OPS_S2KS_SIMPLE
|
|
|| C.secret_key.s2k_specifier == OPS_S2KS_SALTED
|
|
|| C.secret_key.s2k_specifier == OPS_S2KS_ITERATED_AND_SALTED);
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.secret_key.hash_algorithm=c[0];
|
|
|
|
if(C.secret_key.s2k_specifier != OPS_S2KS_SIMPLE
|
|
&& !limited_read(C.secret_key.salt,8,region,pinfo))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if(C.secret_key.s2k_specifier == OPS_S2KS_ITERATED_AND_SALTED)
|
|
{
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.secret_key.octet_count=(16+(c[0]&15)) << ((c[0] >> 4)+6);
|
|
}
|
|
}
|
|
else if(C.secret_key.s2k_usage != OPS_S2KU_NONE)
|
|
{
|
|
// this is V3 style, looks just like a V4 simple hash
|
|
C.secret_key.algorithm=C.secret_key.s2k_usage;
|
|
C.secret_key.s2k_usage=OPS_S2KU_ENCRYPTED;
|
|
C.secret_key.s2k_specifier=OPS_S2KS_SIMPLE;
|
|
C.secret_key.hash_algorithm=OPS_HASH_MD5;
|
|
}
|
|
|
|
crypted=C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED
|
|
|| C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED;
|
|
|
|
if(crypted)
|
|
{
|
|
int n;
|
|
ops_parser_content_t pc;
|
|
char *passphrase;
|
|
unsigned char key[OPS_MAX_KEY_SIZE+OPS_MAX_HASH_SIZE];
|
|
ops_hash_t hashes[(OPS_MAX_KEY_SIZE+OPS_MIN_HASH_SIZE-1)/OPS_MIN_HASH_SIZE];
|
|
int keysize;
|
|
int hashsize;
|
|
size_t l;
|
|
|
|
blocksize=ops_block_size(C.secret_key.algorithm);
|
|
assert(blocksize > 0 && blocksize <= OPS_MAX_BLOCK_SIZE);
|
|
|
|
if(!limited_read(C.secret_key.iv,blocksize,region,pinfo))
|
|
return 0;
|
|
|
|
memset(&pc,'\0',sizeof pc);
|
|
passphrase=NULL;
|
|
pc.content.secret_key_passphrase.passphrase=&passphrase;
|
|
pc.content.secret_key_passphrase.secret_key=&C.secret_key;
|
|
CBP(pinfo,OPS_PARSER_CMD_GET_SK_PASSPHRASE,&pc);
|
|
if(!passphrase)
|
|
{
|
|
if (debug)
|
|
{
|
|
// \todo make into proper error
|
|
fprintf(stderr,"parse_secret_key: can't get passphrase\n");
|
|
}
|
|
|
|
if(!consume_packet(region,pinfo,ops_false))
|
|
return 0;
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_ENCRYPTED_SECRET_KEY,&content);
|
|
|
|
return 1;
|
|
}
|
|
|
|
keysize=ops_key_size(C.secret_key.algorithm);
|
|
assert(keysize > 0 && keysize <= OPS_MAX_KEY_SIZE);
|
|
|
|
hashsize=ops_hash_size(C.secret_key.hash_algorithm);
|
|
assert(hashsize > 0 && hashsize <= OPS_MAX_HASH_SIZE);
|
|
|
|
for(n=0 ; n*hashsize < keysize ; ++n)
|
|
{
|
|
int i;
|
|
|
|
ops_hash_any(&hashes[n],C.secret_key.hash_algorithm);
|
|
hashes[n].init(&hashes[n]);
|
|
// preload hashes with zeroes...
|
|
for(i=0 ; i < n ; ++i)
|
|
hashes[n].add(&hashes[n],(unsigned char *)"",1);
|
|
}
|
|
|
|
l=strlen(passphrase);
|
|
|
|
for(n=0 ; n*hashsize < keysize ; ++n)
|
|
{
|
|
unsigned i;
|
|
|
|
switch(C.secret_key.s2k_specifier)
|
|
{
|
|
case OPS_S2KS_SALTED:
|
|
hashes[n].add(&hashes[n],C.secret_key.salt,OPS_SALT_SIZE);
|
|
// flow through...
|
|
case OPS_S2KS_SIMPLE:
|
|
hashes[n].add(&hashes[n],(unsigned char*)passphrase,l);
|
|
break;
|
|
|
|
case OPS_S2KS_ITERATED_AND_SALTED:
|
|
for(i=0 ; i < C.secret_key.octet_count ; i+=l+OPS_SALT_SIZE)
|
|
{
|
|
int j=l+OPS_SALT_SIZE;
|
|
|
|
if(i+j > C.secret_key.octet_count && i != 0)
|
|
j=C.secret_key.octet_count-i;
|
|
|
|
hashes[n].add(&hashes[n],C.secret_key.salt,
|
|
j > OPS_SALT_SIZE ? OPS_SALT_SIZE : j);
|
|
if(j > OPS_SALT_SIZE)
|
|
hashes[n].add(&hashes[n],(unsigned char *)passphrase,j-OPS_SALT_SIZE);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
for(n=0 ; n*hashsize < keysize ; ++n)
|
|
{
|
|
int r=hashes[n].finish(&hashes[n],key+n*hashsize);
|
|
assert(r == hashsize);
|
|
}
|
|
|
|
free(passphrase);
|
|
|
|
ops_crypt_any(&decrypt,C.secret_key.algorithm);
|
|
if (debug)
|
|
{
|
|
unsigned int i=0;
|
|
fprintf(stderr,"\nREADING:\niv=");
|
|
for (i=0; i<ops_block_size(C.secret_key.algorithm); i++)
|
|
{
|
|
fprintf(stderr, "%02x ", C.secret_key.iv[i]);
|
|
}
|
|
fprintf(stderr,"\n");
|
|
fprintf(stderr,"key=");
|
|
for (i=0; i<CAST_KEY_LENGTH; i++)
|
|
{
|
|
fprintf(stderr, "%02x ", key[i]);
|
|
}
|
|
fprintf(stderr,"\n");
|
|
}
|
|
decrypt.set_iv(&decrypt,C.secret_key.iv);
|
|
decrypt.set_key(&decrypt,key);
|
|
|
|
// now read encrypted data
|
|
|
|
ops_reader_push_decrypt(pinfo,&decrypt,region);
|
|
|
|
/* Since all known encryption for PGP doesn't compress, we can
|
|
limit to the same length as the current region (for now).
|
|
*/
|
|
ops_init_subregion(&encregion,NULL);
|
|
encregion.length=region->length-region->length_read;
|
|
if(C.secret_key.public_key.version != OPS_V4)
|
|
{
|
|
encregion.length-=2;
|
|
}
|
|
saved_region=region;
|
|
region=&encregion;
|
|
}
|
|
|
|
if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED)
|
|
{
|
|
ops_hash_sha1(&checkhash);
|
|
ops_reader_push_hash(pinfo,&checkhash);
|
|
}
|
|
else
|
|
{
|
|
ops_reader_push_sum16(pinfo);
|
|
}
|
|
|
|
switch(C.secret_key.public_key.algorithm)
|
|
{
|
|
case OPS_PKA_RSA:
|
|
case OPS_PKA_RSA_ENCRYPT_ONLY:
|
|
case OPS_PKA_RSA_SIGN_ONLY:
|
|
if(!limited_read_mpi(&C.secret_key.key.rsa.d,region,pinfo)
|
|
|| !limited_read_mpi(&C.secret_key.key.rsa.p,region,pinfo)
|
|
|| !limited_read_mpi(&C.secret_key.key.rsa.q,region,pinfo)
|
|
|| !limited_read_mpi(&C.secret_key.key.rsa.u,region,pinfo))
|
|
ret=0;
|
|
|
|
break;
|
|
|
|
case OPS_PKA_DSA:
|
|
|
|
if(!limited_read_mpi(&C.secret_key.key.dsa.x,region,pinfo))
|
|
ret=0;
|
|
break;
|
|
|
|
default:
|
|
OPS_ERROR_2(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,"Unsupported Public Key algorithm %d (%s)",C.secret_key.public_key.algorithm,ops_show_pka(C.secret_key.public_key.algorithm));
|
|
ret=0;
|
|
// assert(0);
|
|
}
|
|
|
|
if (debug)
|
|
{
|
|
fprintf(stderr,"4 MPIs read\n");
|
|
// ops_print_secret_key_verbose(OPS_PTAG_CT_SECRET_KEY, &C.secret_key);
|
|
}
|
|
|
|
pinfo->reading_v3_secret=ops_false;
|
|
|
|
if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED)
|
|
{
|
|
unsigned char hash[20];
|
|
|
|
ops_reader_pop_hash(pinfo);
|
|
checkhash.finish(&checkhash,hash);
|
|
|
|
if(crypted && C.secret_key.public_key.version != OPS_V4)
|
|
{
|
|
ops_reader_pop_decrypt(pinfo);
|
|
region=saved_region;
|
|
}
|
|
|
|
if(ret)
|
|
{
|
|
if(!limited_read(C.secret_key.checkhash,20,region,pinfo))
|
|
return 0;
|
|
|
|
if(memcmp(hash,C.secret_key.checkhash,20))
|
|
ERRP(pinfo,"Hash mismatch in secret key");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned short sum;
|
|
|
|
sum=ops_reader_pop_sum16(pinfo);
|
|
|
|
if(crypted && C.secret_key.public_key.version != OPS_V4)
|
|
{
|
|
ops_reader_pop_decrypt(pinfo);
|
|
region=saved_region;
|
|
}
|
|
|
|
if(ret)
|
|
{
|
|
if(!limited_read_scalar(&C.secret_key.checksum,2,region,
|
|
pinfo))
|
|
return 0;
|
|
|
|
if(sum != C.secret_key.checksum)
|
|
ERRP(pinfo,"Checksum mismatch in secret key");
|
|
}
|
|
}
|
|
|
|
if(crypted && C.secret_key.public_key.version == OPS_V4)
|
|
{
|
|
ops_reader_pop_decrypt(pinfo);
|
|
}
|
|
|
|
assert(!ret || region->length_read == region->length);
|
|
|
|
if(!ret)
|
|
return 0;
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_SECRET_KEY,&content);
|
|
|
|
if (debug)
|
|
{ fprintf(stderr, "--- end of parse_secret_key\n\n"); }
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Parse a Public Key Session Key packet
|
|
*/
|
|
static int parse_pk_session_key(ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[1]="";
|
|
ops_parser_content_t content;
|
|
ops_parser_content_t pc;
|
|
|
|
int n;
|
|
BIGNUM *enc_m;
|
|
unsigned k;
|
|
const ops_secret_key_t *secret;
|
|
unsigned char cs[2];
|
|
unsigned char* iv;
|
|
|
|
// Can't rely on it being CAST5
|
|
// \todo FIXME RW
|
|
// const size_t sz_unencoded_m_buf=CAST_KEY_LENGTH+1+2;
|
|
const size_t sz_unencoded_m_buf=1024;
|
|
unsigned char unencoded_m_buf[sz_unencoded_m_buf];
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.pk_session_key.version=c[0];
|
|
if(C.pk_session_key.version != OPS_PKSK_V3)
|
|
{
|
|
OPS_ERROR_1(&pinfo->errors, OPS_E_PROTO_BAD_PKSK_VRSN,
|
|
"Bad public-key encrypted session key version (%d)",
|
|
C.pk_session_key.version);
|
|
return 0;
|
|
}
|
|
|
|
if(!limited_read(C.pk_session_key.key_id,
|
|
sizeof C.pk_session_key.key_id,region,pinfo))
|
|
return 0;
|
|
|
|
if (debug)
|
|
{
|
|
int i;
|
|
int x=sizeof C.pk_session_key.key_id;
|
|
printf("session key: public key id: x=%d\n",x);
|
|
for (i=0; i<x; i++)
|
|
printf("%2x ", C.pk_session_key.key_id[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.pk_session_key.algorithm=c[0];
|
|
switch(C.pk_session_key.algorithm)
|
|
{
|
|
case OPS_PKA_RSA:
|
|
if(!limited_read_mpi(&C.pk_session_key.parameters.rsa.encrypted_m,
|
|
region,pinfo))
|
|
return 0;
|
|
enc_m=C.pk_session_key.parameters.rsa.encrypted_m;
|
|
break;
|
|
|
|
case OPS_PKA_ELGAMAL:
|
|
if(!limited_read_mpi(&C.pk_session_key.parameters.elgamal.g_to_k,
|
|
region,pinfo)
|
|
|| !limited_read_mpi(&C.pk_session_key.parameters.elgamal.encrypted_m,
|
|
region,pinfo))
|
|
return 0;
|
|
enc_m=C.pk_session_key.parameters.elgamal.encrypted_m;
|
|
break;
|
|
|
|
default:
|
|
OPS_ERROR_1(&pinfo->errors, OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,
|
|
"Unknown public key algorithm in session key (%s)",
|
|
ops_show_pka(C.pk_session_key.algorithm));
|
|
return 0;
|
|
}
|
|
|
|
memset(&pc,'\0',sizeof pc);
|
|
secret=NULL;
|
|
pc.content.get_secret_key.secret_key=&secret;
|
|
pc.content.get_secret_key.pk_session_key=&C.pk_session_key;
|
|
|
|
CBP(pinfo,OPS_PARSER_CMD_GET_SECRET_KEY,&pc);
|
|
|
|
if(!secret)
|
|
{
|
|
CBP(pinfo,OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY,&content);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// n=ops_decrypt_mpi(buf,sizeof buf,enc_m,secret);
|
|
n=ops_decrypt_and_unencode_mpi(unencoded_m_buf,sizeof unencoded_m_buf,enc_m,secret);
|
|
|
|
if(n < 1)
|
|
{
|
|
ERRP(pinfo,"decrypted message too short");
|
|
return 0;
|
|
}
|
|
|
|
// PKA
|
|
C.pk_session_key.symmetric_algorithm=unencoded_m_buf[0];
|
|
|
|
if (!ops_is_sa_supported(C.pk_session_key.symmetric_algorithm))
|
|
{
|
|
// ERR1P
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_SYMMETRIC_ALG,
|
|
"Symmetric algorithm %s not supported",
|
|
ops_show_symmetric_algorithm(C.pk_session_key.symmetric_algorithm));
|
|
return 0;
|
|
}
|
|
|
|
k=ops_key_size(C.pk_session_key.symmetric_algorithm);
|
|
|
|
if((unsigned)n != k+3)
|
|
{
|
|
OPS_ERROR_2(&pinfo->errors,OPS_E_PROTO_DECRYPTED_MSG_WRONG_LEN,
|
|
"decrypted message wrong length (got %d expected %d)",
|
|
n,k+3);
|
|
return 0;
|
|
}
|
|
|
|
assert(k <= sizeof C.pk_session_key.key);
|
|
|
|
memcpy(C.pk_session_key.key,unencoded_m_buf+1,k);
|
|
|
|
if (debug)
|
|
{
|
|
printf("session key recovered (len=%d):\n",k);
|
|
unsigned int j;
|
|
for(j=0; j<k; j++)
|
|
printf("%2x ", C.pk_session_key.key[j]);
|
|
printf("\n");
|
|
}
|
|
|
|
C.pk_session_key.checksum=unencoded_m_buf[k+1]+(unencoded_m_buf[k+2] << 8);
|
|
if (debug)
|
|
{
|
|
printf("session key checksum: %2x %2x\n", unencoded_m_buf[k+1], unencoded_m_buf[k+2]);
|
|
}
|
|
|
|
// Check checksum
|
|
|
|
ops_calc_session_key_checksum(&C.pk_session_key, &cs[0]);
|
|
if (unencoded_m_buf[k+1]!=cs[0] || unencoded_m_buf[k+2]!=cs[1])
|
|
{
|
|
OPS_ERROR_4(&pinfo->errors, OPS_E_PROTO_BAD_SK_CHECKSUM,
|
|
"Session key checksum wrong: expected %2x %2x, got %2x %2x",
|
|
cs[0], cs[1], unencoded_m_buf[k+1], unencoded_m_buf[k+2]);
|
|
return 0;
|
|
}
|
|
|
|
// all is well
|
|
CBP(pinfo,OPS_PTAG_CT_PK_SESSION_KEY,&content);
|
|
|
|
ops_crypt_any(&pinfo->decrypt,C.pk_session_key.symmetric_algorithm);
|
|
iv=ops_mallocz(pinfo->decrypt.blocksize);
|
|
pinfo->decrypt.set_iv(&pinfo->decrypt, iv);
|
|
pinfo->decrypt.set_key(&pinfo->decrypt,C.pk_session_key.key);
|
|
ops_encrypt_init(&pinfo->decrypt);
|
|
return 1;
|
|
}
|
|
|
|
// XXX: make this static?
|
|
int ops_decrypt_se_data(ops_content_tag_t tag,ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
int r=1;
|
|
ops_crypt_t *decrypt=ops_parse_get_decrypt(pinfo);
|
|
|
|
if(decrypt)
|
|
{
|
|
unsigned char buf[OPS_MAX_BLOCK_SIZE+2]="";
|
|
size_t b=decrypt->blocksize;
|
|
// ops_parser_content_t content;
|
|
ops_region_t encregion;
|
|
|
|
|
|
ops_reader_push_decrypt(pinfo,decrypt,region);
|
|
|
|
ops_init_subregion(&encregion,NULL);
|
|
encregion.length=b+2;
|
|
|
|
if(!exact_limited_read(buf,b+2,&encregion,pinfo))
|
|
return 0;
|
|
|
|
if(buf[b-2] != buf[b] || buf[b-1] != buf[b+1])
|
|
{
|
|
ops_reader_pop_decrypt(pinfo);
|
|
OPS_ERROR_4(&pinfo->errors, OPS_E_PROTO_BAD_SYMMETRIC_DECRYPT,
|
|
"Bad symmetric decrypt (%02x%02x vs %02x%02x)",
|
|
buf[b-2],buf[b-1],buf[b],buf[b+1]);
|
|
return 0;
|
|
}
|
|
|
|
if(tag == OPS_PTAG_CT_SE_DATA_BODY)
|
|
{
|
|
decrypt->decrypt_resync(decrypt);
|
|
decrypt->block_encrypt(decrypt,decrypt->civ,decrypt->civ);
|
|
}
|
|
|
|
|
|
r=ops_parse(pinfo);
|
|
|
|
ops_reader_pop_decrypt(pinfo);
|
|
}
|
|
else
|
|
{
|
|
ops_parser_content_t content;
|
|
|
|
while(region->length_read < region->length)
|
|
{
|
|
unsigned l=region->length-region->length_read;
|
|
|
|
if(l > sizeof C.se_data_body.data)
|
|
l=sizeof C.se_data_body.data;
|
|
|
|
if(!limited_read(C.se_data_body.data,l,region,pinfo))
|
|
return 0;
|
|
|
|
C.se_data_body.length=l;
|
|
|
|
CBP(pinfo,tag,&content);
|
|
}
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int ops_decrypt_se_ip_data(ops_content_tag_t tag,ops_region_t *region,
|
|
ops_parse_info_t *pinfo)
|
|
{
|
|
int r=1;
|
|
ops_crypt_t *decrypt=ops_parse_get_decrypt(pinfo);
|
|
|
|
if(decrypt)
|
|
{
|
|
ops_reader_push_decrypt(pinfo,decrypt,region);
|
|
ops_reader_push_se_ip_data(pinfo,decrypt,region);
|
|
|
|
r=ops_parse(pinfo);
|
|
|
|
// assert(0);
|
|
ops_reader_pop_se_ip_data(pinfo);
|
|
ops_reader_pop_decrypt(pinfo);
|
|
}
|
|
else
|
|
{
|
|
ops_parser_content_t content;
|
|
|
|
while(region->length_read < region->length)
|
|
{
|
|
unsigned l=region->length-region->length_read;
|
|
|
|
if(l > sizeof C.se_data_body.data)
|
|
l=sizeof C.se_data_body.data;
|
|
|
|
if(!limited_read(C.se_data_body.data,l,region,pinfo))
|
|
return 0;
|
|
|
|
C.se_data_body.length=l;
|
|
|
|
CBP(pinfo,tag,&content);
|
|
}
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Read a Symmetrically Encrypted packet
|
|
*/
|
|
static int parse_se_data(ops_region_t *region,ops_parse_info_t *pinfo)
|
|
{
|
|
ops_parser_content_t content;
|
|
|
|
/* there's no info to go with this, so just announce it */
|
|
CBP(pinfo,OPS_PTAG_CT_SE_DATA_HEADER,&content);
|
|
|
|
/* The content of an encrypted data packet is more OpenPGP packets
|
|
once decrypted, so recursively handle them */
|
|
return ops_decrypt_se_data(OPS_PTAG_CT_SE_DATA_BODY,region,pinfo);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Read a Symmetrically Encrypted Integrity Protected packet
|
|
*/
|
|
static int parse_se_ip_data(ops_region_t *region,ops_parse_info_t *pinfo)
|
|
{
|
|
unsigned char c[1]="";
|
|
ops_parser_content_t content;
|
|
|
|
if(!limited_read(c,1,region,pinfo))
|
|
return 0;
|
|
C.se_ip_data_header.version=c[0];
|
|
assert(C.se_ip_data_header.version == OPS_SE_IP_V1);
|
|
|
|
/* The content of an encrypted data packet is more OpenPGP packets
|
|
once decrypted, so recursively handle them */
|
|
return ops_decrypt_se_ip_data(OPS_PTAG_CT_SE_IP_DATA_BODY,region,pinfo);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Read a MDC packet
|
|
*/
|
|
static int parse_mdc(ops_region_t *region, ops_parse_info_t *pinfo)
|
|
{
|
|
ops_parser_content_t content;
|
|
|
|
if (!limited_read((unsigned char *)&C.mdc,OPS_SHA1_HASH_SIZE,region,pinfo))
|
|
return 0;
|
|
|
|
CBP(pinfo,OPS_PTAG_CT_MDC,&content);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \ingroup Core_ReadPackets
|
|
* \brief Parse one packet.
|
|
*
|
|
* This function parses the packet tag. It computes the value of the
|
|
* content tag and then calls the appropriate function to handle the
|
|
* content.
|
|
*
|
|
* \param *pinfo How to parse
|
|
* \param *pktlen On return, will contain number of bytes in packet
|
|
* \return 1 on success, 0 on error, -1 on EOF */
|
|
static int ops_parse_one_packet(ops_parse_info_t *pinfo,
|
|
unsigned long *pktlen)
|
|
{
|
|
unsigned char ptag[1];
|
|
ops_parser_content_t content;
|
|
int r;
|
|
ops_region_t region;
|
|
ops_boolean_t indeterminate=ops_false;
|
|
|
|
C.ptag.position=pinfo->rinfo.position;
|
|
|
|
r=base_read(ptag,1,pinfo);
|
|
|
|
// errors in the base read are effectively EOF.
|
|
if(r <= 0)
|
|
return -1;
|
|
|
|
*pktlen=0;
|
|
|
|
if(!(*ptag&OPS_PTAG_ALWAYS_SET))
|
|
{
|
|
C.error.error="Format error (ptag bit not set)";
|
|
CBP(pinfo,OPS_PARSER_ERROR,&content);
|
|
return 0;
|
|
}
|
|
C.ptag.new_format=!!(*ptag&OPS_PTAG_NEW_FORMAT);
|
|
if(C.ptag.new_format)
|
|
{
|
|
C.ptag.content_tag=*ptag&OPS_PTAG_NF_CONTENT_TAG_MASK;
|
|
C.ptag.length_type=0;
|
|
if(!read_new_length(&C.ptag.length,pinfo))
|
|
return 0;
|
|
|
|
}
|
|
else
|
|
{
|
|
ops_boolean_t rb;
|
|
|
|
C.ptag.content_tag=(*ptag&OPS_PTAG_OF_CONTENT_TAG_MASK)
|
|
>> OPS_PTAG_OF_CONTENT_TAG_SHIFT;
|
|
C.ptag.length_type=*ptag&OPS_PTAG_OF_LENGTH_TYPE_MASK;
|
|
switch(C.ptag.length_type)
|
|
{
|
|
case OPS_PTAG_OF_LT_ONE_BYTE:
|
|
rb=_read_scalar(&C.ptag.length,1,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_OF_LT_TWO_BYTE:
|
|
rb=_read_scalar(&C.ptag.length,2,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_OF_LT_FOUR_BYTE:
|
|
rb=_read_scalar(&C.ptag.length,4,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_OF_LT_INDETERMINATE:
|
|
C.ptag.length=0;
|
|
indeterminate=ops_true;
|
|
rb=ops_true;
|
|
break;
|
|
}
|
|
if(!rb)
|
|
return 0;
|
|
}
|
|
|
|
CBP(pinfo,OPS_PARSER_PTAG,&content);
|
|
|
|
ops_init_subregion(®ion,NULL);
|
|
region.length=C.ptag.length;
|
|
region.indeterminate=indeterminate;
|
|
switch(C.ptag.content_tag)
|
|
{
|
|
case OPS_PTAG_CT_SIGNATURE:
|
|
r=parse_signature(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_PUBLIC_KEY:
|
|
case OPS_PTAG_CT_PUBLIC_SUBKEY:
|
|
r=parse_public_key(C.ptag.content_tag,®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_TRUST:
|
|
r=parse_trust(®ion, pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_USER_ID:
|
|
r=parse_user_id(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_COMPRESSED:
|
|
r=parse_compressed(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_ONE_PASS_SIGNATURE:
|
|
r=parse_one_pass(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_LITERAL_DATA:
|
|
r=parse_literal_data(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_USER_ATTRIBUTE:
|
|
r=parse_user_attribute(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_SECRET_KEY:
|
|
r=parse_secret_key(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_SECRET_SUBKEY:
|
|
r=parse_secret_key(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_PK_SESSION_KEY:
|
|
r=parse_pk_session_key(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_SE_DATA:
|
|
r=parse_se_data(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_SE_IP_DATA:
|
|
r=parse_se_ip_data(®ion,pinfo);
|
|
break;
|
|
|
|
case OPS_PTAG_CT_MDC:
|
|
r=parse_mdc(®ion, pinfo);
|
|
break;
|
|
|
|
default:
|
|
OPS_ERROR_1(&pinfo->errors,OPS_E_P_UNKNOWN_TAG,
|
|
"Unknown content tag 0x%x", C.ptag.content_tag);
|
|
r=0;
|
|
}
|
|
|
|
/* Ensure that the entire packet has been consumed */
|
|
|
|
if(region.length != region.length_read && !region.indeterminate)
|
|
if(!consume_packet(®ion,pinfo,ops_false))
|
|
r=-1;
|
|
|
|
// also consume it if there's been an error?
|
|
// \todo decide what to do about an error on an
|
|
// indeterminate packet
|
|
if (r==0)
|
|
{
|
|
if (!consume_packet(®ion,pinfo,ops_false))
|
|
r=-1;
|
|
}
|
|
|
|
/* set pktlen */
|
|
|
|
*pktlen=pinfo->rinfo.alength;
|
|
|
|
/* do callback on entire packet, if desired and there was no error */
|
|
|
|
if(r > 0 && pinfo->rinfo.accumulate)
|
|
{
|
|
C.packet.length=pinfo->rinfo.alength;
|
|
C.packet.raw=pinfo->rinfo.accumulated;
|
|
pinfo->rinfo.accumulated=NULL;
|
|
pinfo->rinfo.asize=0;
|
|
CBP(pinfo,OPS_PARSER_PACKET_END,&content);
|
|
}
|
|
pinfo->rinfo.alength=0;
|
|
|
|
if(r < 0)
|
|
return -1;
|
|
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* \ingroup Core_ReadPackets
|
|
*
|
|
* \brief Parse packets from an input stream until EOF or error.
|
|
*
|
|
* \details Setup the necessary parsing configuration in "pinfo" before calling ops_parse().
|
|
*
|
|
* That information includes :
|
|
*
|
|
* - a "reader" function to be used to get the data to be parsed
|
|
*
|
|
* - a "callback" function to be called when this library has identified
|
|
* a parseable object within the data
|
|
*
|
|
* - whether the calling function wants the signature subpackets returned raw, parsed or not at all.
|
|
*
|
|
* After returning, pinfo->errors holds any errors encountered while parsing.
|
|
*
|
|
* \param pinfo Parsing configuration
|
|
* \return 1 on success in all packets, 0 on error in any packet
|
|
*
|
|
* \sa CoreAPI Overview
|
|
*
|
|
* \sa ops_print_errors(), ops_parse_and_print_errors()
|
|
*
|
|
* Example code
|
|
* \code
|
|
ops_parse_cb_t* example_callback();
|
|
void example()
|
|
{
|
|
int fd=0;
|
|
ops_parse_info_t *pinfo=NULL;
|
|
char *filename="pubring.gpg";
|
|
|
|
// setup pinfo to read from file with example callback
|
|
fd=ops_setup_file_read(&pinfo, filename, NULL, example_callback, ops_false);
|
|
|
|
// specify how we handle signature subpackets
|
|
ops_parse_options(pinfo, OPS_PTAG_SS_ALL, OPS_PARSE_PARSED);
|
|
|
|
if (!ops_parse(pinfo))
|
|
ops_print_errors(pinfo->errors);
|
|
|
|
ops_teardown_file_read(pinfo,fd);
|
|
}
|
|
* \endcode
|
|
*/
|
|
|
|
int ops_parse(ops_parse_info_t *pinfo)
|
|
{
|
|
int r;
|
|
unsigned long pktlen;
|
|
|
|
do
|
|
{
|
|
r=ops_parse_one_packet(pinfo,&pktlen);
|
|
} while (r != -1);
|
|
|
|
return pinfo->errors ? 0 : 1;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Parse packets and print any errors
|
|
* \param pinfo Parsing configuration
|
|
* \return 1 on success in all packets, 0 on error in any packet
|
|
* \sa CoreAPI Overview
|
|
* \sa ops_parse()
|
|
*/
|
|
|
|
int ops_parse_and_print_errors(ops_parse_info_t *pinfo)
|
|
{
|
|
int r;
|
|
r=ops_parse(pinfo);
|
|
ops_print_errors(pinfo->errors);
|
|
return pinfo->errors ? 0 : 1;
|
|
}
|
|
|
|
/**
|
|
* \ingroup Core_ReadPackets
|
|
*
|
|
* \brief Specifies whether one or more signature
|
|
* subpacket types should be returned parsed; or raw; or ignored.
|
|
*
|
|
* \param pinfo Pointer to previously allocated structure
|
|
* \param tag Packet tag. OPS_PTAG_SS_ALL for all SS tags; or one individual signature subpacket tag
|
|
* \param type Parse type
|
|
* \todo Make all packet types optional, not just subpackets */
|
|
void ops_parse_options(ops_parse_info_t *pinfo,
|
|
ops_content_tag_t tag,
|
|
ops_parse_type_t type)
|
|
{
|
|
int t8,t7;
|
|
|
|
if(tag == OPS_PTAG_SS_ALL)
|
|
{
|
|
int n;
|
|
|
|
for(n=0 ; n < 256 ; ++n)
|
|
ops_parse_options(pinfo,OPS_PTAG_SIGNATURE_SUBPACKET_BASE+n,
|
|
type);
|
|
return;
|
|
}
|
|
|
|
assert(tag >= OPS_PTAG_SIGNATURE_SUBPACKET_BASE
|
|
&& tag <= OPS_PTAG_SIGNATURE_SUBPACKET_BASE+NTAGS-1);
|
|
t8=(tag-OPS_PTAG_SIGNATURE_SUBPACKET_BASE)/8;
|
|
t7=1 << ((tag-OPS_PTAG_SIGNATURE_SUBPACKET_BASE)&7);
|
|
switch(type)
|
|
{
|
|
case OPS_PARSE_RAW:
|
|
pinfo->ss_raw[t8] |= t7;
|
|
pinfo->ss_parsed[t8] &= ~t7;
|
|
break;
|
|
|
|
case OPS_PARSE_PARSED:
|
|
pinfo->ss_raw[t8] &= ~t7;
|
|
pinfo->ss_parsed[t8] |= t7;
|
|
break;
|
|
|
|
case OPS_PARSE_IGNORE:
|
|
pinfo->ss_raw[t8] &= ~t7;
|
|
pinfo->ss_parsed[t8] &= ~t7;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Creates a new zero-ed ops_parse_info_t struct
|
|
\sa ops_parse_info_delete()
|
|
*/
|
|
ops_parse_info_t *ops_parse_info_new(void)
|
|
{ return ops_mallocz(sizeof(ops_parse_info_t)); }
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Free ops_parse_info_t struct and its contents
|
|
\sa ops_parse_info_new()
|
|
*/
|
|
void ops_parse_info_delete(ops_parse_info_t *pinfo)
|
|
{
|
|
ops_parse_cb_info_t *cbinfo,*next;
|
|
|
|
for(cbinfo=pinfo->cbinfo.next ; cbinfo ; cbinfo=next)
|
|
{
|
|
next=cbinfo->next;
|
|
free(cbinfo);
|
|
}
|
|
if(pinfo->rinfo.destroyer)
|
|
pinfo->rinfo.destroyer(&pinfo->rinfo);
|
|
ops_free_errors(pinfo->errors);
|
|
if(pinfo->rinfo.accumulated)
|
|
free(pinfo->rinfo.accumulated);
|
|
free(pinfo);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Returns the parse_info's reader_info
|
|
\return Pointer to the reader_info inside the parse_info
|
|
*/
|
|
ops_reader_info_t *ops_parse_get_rinfo(ops_parse_info_t *pinfo)
|
|
{ return &pinfo->rinfo; }
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Sets the parse_info's callback
|
|
This is used when adding the first callback in a stack of callbacks.
|
|
\sa ops_parse_cb_push()
|
|
*/
|
|
|
|
void ops_parse_cb_set(ops_parse_info_t *pinfo,ops_parse_cb_t *cb,void *arg)
|
|
{
|
|
pinfo->cbinfo.cb=cb;
|
|
pinfo->cbinfo.arg=arg;
|
|
pinfo->cbinfo.errors=&pinfo->errors;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Adds a further callback to a stack of callbacks
|
|
\sa ops_parse_cb_set()
|
|
*/
|
|
void ops_parse_cb_push(ops_parse_info_t *pinfo,ops_parse_cb_t *cb,void *arg)
|
|
{
|
|
ops_parse_cb_info_t *cbinfo=malloc(sizeof *cbinfo);
|
|
|
|
*cbinfo=pinfo->cbinfo;
|
|
pinfo->cbinfo.next=cbinfo;
|
|
ops_parse_cb_set(pinfo,cb,arg);
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Returns callback's arg
|
|
*/
|
|
void *ops_parse_cb_get_arg(ops_parse_cb_info_t *cbinfo)
|
|
{ return cbinfo->arg; }
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Returns callback's errors
|
|
*/
|
|
void *ops_parse_cb_get_errors(ops_parse_cb_info_t *cbinfo)
|
|
{ return cbinfo->errors; }
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Calls the parse_cb_info's callback if present
|
|
\return Return value from callback, if present; else OPS_FINISHED
|
|
*/
|
|
ops_parse_cb_return_t ops_parse_cb(const ops_parser_content_t *content,
|
|
ops_parse_cb_info_t *cbinfo)
|
|
{
|
|
if(cbinfo->cb)
|
|
return cbinfo->cb(content,cbinfo);
|
|
else
|
|
return OPS_FINISHED;
|
|
}
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Calls the next callback in the stack
|
|
\return Return value from callback
|
|
*/
|
|
ops_parse_cb_return_t ops_parse_stacked_cb(const ops_parser_content_t *content,
|
|
ops_parse_cb_info_t *cbinfo)
|
|
{ return ops_parse_cb(content,cbinfo->next); }
|
|
|
|
/**
|
|
\ingroup Core_ReadPackets
|
|
\brief Returns the parse_info's errors
|
|
\return parse_info's errors
|
|
*/
|
|
ops_error_t *ops_parse_info_get_errors(ops_parse_info_t *pinfo)
|
|
{ return pinfo->errors; }
|
|
|
|
ops_crypt_t *ops_parse_get_decrypt(ops_parse_info_t *pinfo)
|
|
{
|
|
if(pinfo->decrypt.algorithm)
|
|
return &pinfo->decrypt;
|
|
return NULL;
|
|
}
|
|
|
|
// XXX: this could be improved by sharing all hashes that are the
|
|
// same, then duping them just before checking the signature.
|
|
void ops_parse_hash_init(ops_parse_info_t *pinfo,ops_hash_algorithm_t type,
|
|
const unsigned char *keyid)
|
|
{
|
|
ops_parse_hash_info_t *hash;
|
|
|
|
pinfo->hashes=realloc(pinfo->hashes,
|
|
(pinfo->nhashes+1)*sizeof *pinfo->hashes);
|
|
hash=&pinfo->hashes[pinfo->nhashes++];
|
|
|
|
ops_hash_any(&hash->hash,type);
|
|
hash->hash.init(&hash->hash);
|
|
memcpy(hash->keyid,keyid,sizeof hash->keyid);
|
|
}
|
|
|
|
void ops_parse_hash_data(ops_parse_info_t *pinfo,const void *data,
|
|
size_t length)
|
|
{
|
|
size_t n;
|
|
|
|
for(n=0 ; n < pinfo->nhashes ; ++n)
|
|
pinfo->hashes[n].hash.add(&pinfo->hashes[n].hash,data,length);
|
|
}
|
|
|
|
ops_hash_t *ops_parse_hash_find(ops_parse_info_t *pinfo,
|
|
const unsigned char keyid[OPS_KEY_ID_SIZE])
|
|
{
|
|
size_t n;
|
|
|
|
for(n=0 ; n < pinfo->nhashes ; ++n)
|
|
if(!memcmp(pinfo->hashes[n].keyid,keyid,OPS_KEY_ID_SIZE))
|
|
return &pinfo->hashes[n].hash;
|
|
return NULL;
|
|
}
|
|
|
|
/* vim:set textwidth=120: */
|
|
/* vim:set ts=8: */
|