📄 pgpprsbin.c
字号:
/*
* pgpPrsBin.c -- Binary Parser
*
* Written by: Colin Plumb and Derek Atkins <warlord@MIT.EDU>
*
* $Id: pgpPrsBin.c,v 1.65 1999/04/14 18:51:26 hal Exp $
*/
#include "pgpConfig.h"
#include <stdio.h>
#include <string.h>
#include "pgpDebug.h"
#include "pgpPktByte.h"
#include "pgpPrsAsc.h"
#include "pgpPrsBin.h"
#include "pgpAnnotate.h"
#include "pgpCFBPriv.h"
#include "pgpSymmetricCipherPriv.h"
#include "pgpCiphrMod.h"
#include "pgpCompress.h"
#include "pgpCompMod.h"
#include "pgpEncodePriv.h"
#include "pgpFIFO.h"
#include "pgpHashPriv.h"
#include "pgpHashMod.h"
#include "pgpHeader.h"
#include "pgpMem.h"
#include "pgpEnv.h"
#include "pgpErrors.h"
#include "pgpPipeline.h"
#include "pgpSig.h"
#include "pgpTextFilt.h"
#include "pgpUsuals.h"
#include "pgpContext.h"
#define PARSERMAGIC 0xfeedf00d
/* Only accept this much data per call to parsePacket */
#define PARSER_SIZE_LIMIT 512
/*
* There are two formats for PGP packets. Both are equivalent as far as
* the rest of the code is concerned.
* The original PGP packet format:
* - Packet byte: 10ttttll, where tttt = packet type, and ll = length of length
* - Length field, 1/2/4/0 bytes, big-endian, based on ll bits
* - Body
* This format is simple and small, but requires knowing the size of the
* enclosed data before you can emit the length field.
*
* The one-pass PGP 3 format:
* - Packet byte 11tttttt, where tttttt = packet type
* - Zero or more non-final subpackets, starting with a byte of the form
* 111sssss, followed by 2^s bytes of data
* - A final subpacket, which starts with either 0sssssss, 10sssssss,
* or 110sssss ssssssss, holding up to 2^7 + 2^6 + 2^13 - 1 bytes.
* (The formats encode lengths of 0-127, 128-191, and 192-8383 bytes.)
*
* Generally, a PGP implementation will emit non-final subpackets in the
* 1K to 8K range, followed by one final subpacket. However, any size
* may be used, down to one byte. This flexibility allows data to be
* flushed out and a stream to be brought up to date at any byte boundary
* by emitting a combination of non-final subpackets which sum to the
* desired boundary.
*
* To keep track of both of these formats, there is a Header,
* which describes the parsing state of the current subpacket header on
* an arbitrary byte boundary. It has three members:
* lenbytes - the count of the number of bytes remaining in the
* current big-endian subpacket size. This may be as large
* as 4. As additional bytes are read, lenbytes is decremented
* and "pktlen" is increased with the appropriately shifted
* byte.
* more - a flag which is true if the current subpacket is non-terminal,
* so another subpacket is expected.
* pktlen - the length of the current subpacket. If lenbytes is non-zero,
* the value here is an underestimate. When this is 0, a new
* packet or subpacket (depending on the more flag) is expected.
*
* To keep track of the state of the input stream, a Header structure is
* used, but data that has already been read from the Header structure
* is kept around as well. There are two data structures for this:
* - The FIFO contains a copy of the raw input data up to the point
* marked by "passptr".
* - The buffer, which also holds a prefix of the input data, but a
* parsed version. A portion of the two overlap, and what can be
* kept in the buffer, is. This always includes the tail of the last
* subpacket that has been read. If this is also the first subpacket,
* nothing actually needs to get copied to the FIFO at all.
*
* The buffer ends up looking like this:
*
* /------------------------ passptr - First byte not passed through to FIFO.
* |
* v
* +------------------------------------+
* | |
* +------------------------------------+
* ^ ^
* | |
* | \--------- bufend - End of packet header/start of read-ahead
* \--------------------- bufptr - Beginning of intra-packet header
*
* It works like this: The data from the FIFO, plus passptr through bufend,
* is the raw data that's been processed. Passptr is usually before
* bufptr, with the gap filled by the packet's external header
* (packet header byte plus length or first subpacket header), and
* then bufptr through bufend is shared between the two.
*
* If the amount of header data needed exceeds the size of the first
* subpacket, then the data up to and including the second subpacket
* header is copied to the FIFO, and as much of the second subpacket as
* is needed is read into the buffer immediately following the first
* subpacket, with no intervening subpacket header, and "passptr" is
* set to point to the beginning of the second subpacket data.
*
* The data between bufend and readptr is, however, raw input data
* including subpacket headers that have not yet been parsed.
*/
/* Size of buffer - must hold a whole ESK or SIG packet */
/* 4096 bit ElGamal ESK's will be a bit over 1024 bytes */
/* Some X.509 sigs can be several times this */
/* However this is OK, they are only in keyring and get passed through */
#define MAXPKTSIZE 1100
static size_t nextScope (PGPPipeline *myself, PGPByte const *buf,
size_t size, PGPError *error);
static size_t nextESK (PGPPipeline *myself, PGPByte const *buf,
size_t size, PGPError *error);
static size_t parsePacket (PGPPipeline *myself, PGPByte const *buf,
size_t size, PGPError *error);
static size_t parseKey (PGPPipeline *myself, PGPByte const *buf,
size_t size, PGPError *error);
/* Used to recognize the beginning of a recursive literal packet */
static char const pgp_message_begin[] = "-----BEGIN PGP ";
/* Information needed to parse a subpacket header */
typedef struct Header {
PGPUInt32 pktlen; /* Length of current subpacket */
unsigned lenbytes; /* Number of length bytes to come */
PGPByte more; /* More subpackets to come */
} Header;
/* Information needed to parse a data stream */
typedef struct Input {
PGPByte buffer[MAXPKTSIZE]; /* Buffer of parsed data */
PGPFifoContext *fifo; /* FIFO of raw data */
PGPByte const *passptr; /* Pointer into buffer */
PGPByte const *bufptr; /* Pointer into buffer */
PGPByte *bufend; /* Pointer into buffer */
Header head; /* Current subpacket status */
PGPByte silent_trunc; /* Complain if out of input? */
} Input;
typedef struct PrsBinContext {
PGPPipeline pipe;
Input input;
PGPPipeline *tail;
PGPPipeline *nextparser;
PGPPipeline **end;
PGPHashListRef hashes;
PGPEnv const *env;
int state; /* Used my bumerous parse functions */
int end_scope; /* Annotation at end of this scope, or 0 */
PGPByte subtype; /* Type of literal packet (t vs. b) */
int sig1pass; /* Within 1-pass signature scope */
int sig1nest; /* Number of unpaired 1pass headers nested */
/* Flags */
PGPByte sepsig; /* 1-pass separate signature */
PGPByte findpkt; /* Searching for a new packet */
PGPByte eof; /* No more input available, ever */
PGPByte needcallback;
PGPByte nopurge; /* Suppress purge of input buffer */
PGPUICb const *ui;
void *ui_arg;
DEBUG_STRUCT_CONSTRUCTOR( PrsBinContext )
} PrsBinContext;
/* Return true if the end of the current packet has been reached */
static int
inputFinished(Header const *head)
{
return !head->pktlen && !head->lenbytes && !head->more;
}
/*
* Return a pointer to, and then length of, the subpacket header at the
* current position in the input buffer, using the context of "head".
*/
static PGPByte const *
inputHeadPeek(Header const *head, PGPByte const *buf, size_t size,
size_t *len)
{
pgpAssert(buf || !size);
if (head->lenbytes) {
*len = (size < head->lenbytes) ? size : (size_t)head->lenbytes;
return buf;
}
if (head->pktlen) {
*len = 0;
return buf;
}
if (!head->more) {
*len = 0;
return (PGPByte const *)0;
}
if (!size) {
*len = 0;
return buf;
}
if (buf[0] == 0xff && size >= 5)
*len = 5;
else if (buf[0] == 0xff)
*len = size;
else if ((buf[0] & 0xe0) == 0xc0 && size > 1)
*len = 2;
else
*len = 1;
return buf;
}
/*
* Skip the upcoming header, adjusting the state in "head" appropriately.
* Return the number of bytes consumed (<= size).
*/
static size_t
inputHeadSeek(Header *head, PGPByte const *buf, size_t size)
{
unsigned i;
unsigned char b;
/* No data, don't care */
if (!size)
return 0;
pgpAssert(buf);
/* If we're in the middle of a header, read the tail of it */
if (head->lenbytes) {
if (size > head->lenbytes)
size = (size_t)head->lenbytes;
i = (unsigned)size;
do {
head->pktlen += (PGPUInt32)*buf++ << (8*--head->lenbytes);
} while (--i);
return size;
}
/* Are we not at the end of a packet? */
if (head->pktlen || !head->more)
return 0;
/* Otherwise, we're starting a new subpacket header */
b = buf[0];
if (b < 0xc0) {
head->pktlen = (PGPUInt32)b;
head->more = 0;
} else if (b < 0xe0) {
head->pktlen = ((PGPUInt32)b << 8) - (0xc000 - 0xc0);
head->more = 0;
if (size == 1) {
head->lenbytes = 1;
} else {
head->pktlen += (PGPUInt32)buf[1];
return 2;
}
} else if (b == 0xff) {
if (size > 5)
size = 5;
i = (unsigned)size-1;
head->more = 0;
head->lenbytes = 4;
head->pktlen = 0;
do {
head->pktlen += (PGPUInt32)*++buf << (8*--head->lenbytes);
} while (--i);
return size;
} else if (b >= 0xe0) {
head->pktlen = (PGPUInt32)1 << (b & 31);
}
return 1;
}
static void
inputReset(Input *input)
{
input->passptr = input->bufptr = input->bufend = input->buffer;
input->head.pktlen = 0;
input->head.lenbytes = 0;
input->head.more = 0;
}
/*
* Start parsing a packet with the given header byte.
* Returns 0 if it's a normal, good header byte, and -1
* if it's a bogus header byte, in which case the rest of the
* input is taken as the body.
*/
static int
inputStart(Input *input, PGPByte b)
{
/* Store first byte in buffer */
input->passptr = input->buffer;
input->buffer[0] = b;
input->bufend = input->buffer+1;
input->head.pktlen = 0;
if (IS_NEW_PKTBYTE(b)) {
/* New style */
input->head.lenbytes = 0;
input->head.more = 1;
input->silent_trunc = 0;
} else if (IS_OLD_PKTBYTE(b)) {
/* Old style */
input->head.lenbytes = LLEN_TO_BYTES(PKTBYTE_LLEN(b));
input->head.more = 0;
input->silent_trunc = 0;
if (PKTBYTE_LLEN(b) == 3) {
input->silent_trunc = 1;
input->head.pktlen = (PGPUInt32)-1;
}
} else {
/* Erroneous! */
input->bufptr = input->buffer;
input->head.pktlen = (PGPUInt32)-1;
input->head.lenbytes = 0;
input->head.more = 0;
input->silent_trunc = 1;
return -1;
}
/* Usual case wrapup */
input->bufptr = input->bufend;
return 0;
}
/*
* Return a pointer to, and the length of, the next available packet payload
* bytes. These bytes will come from either the read-ahead buffer or the
* input buffer (buf, size), as appropriate. Returns a NULL pointer when
* there is no more data available in the packet. If "size" is 0 and
* this is not the end of the packet, this returns the "buf" pointer,
* which may or may not be NULL, at the caller's discretion.
*
* This may, in some situations (like when "size" is 1 and sitting
* on a subpacket header), return the "buf" pointer but a zero length
* even when size is non-zero.
* In that case, the current packet is not finished, but no data is available
* in the current input buffer. You need to call inputSeek(0) to give
* it a chance to consume the current input buffer.
*/
static PGPByte const *
inputPeek(Input const *input, PGPByte const *buf, size_t size, size_t *len)
{
/* If we have no buffer, then size must be 0 */
pgpAssert(buf || !size);
/* If we have data buffered, use that. */
if (input->bufend != input->bufptr) {
*len = (size_t)(input->bufend - input->bufptr);
return input->bufptr;
}
/*
* If we are in the middle of a subpacket header, no data available,
* but more to come.
*/
if (input->head.lenbytes) {
*len = 0;
return buf;
}
/*
* If we're at the end of a subpacket, no data available, and
* there may or may not be more to come.
*/
if (!input->head.pktlen) {
*len = 0;
return input->head.more ? buf : 0;
}
/* Return the appropriate data from the input buffer */
if (size > input->head.pktlen)
size = (size_t)input->head.pktlen;
*len = size;
return buf;
}
/*
* Skip over "len" bytes (where len <= the amount returned from the latest
* call to inputPeek!) of input from the input stream (either the context
* buffer or the supplied external buffer).
*/
static size_t
inputSeek(Input *input, PGPByte const *buf, size_t size, size_t len)
{
pgpAssert(buf || !size);
/* If we have data in the input buffer, skip that */
if (input->bufend != input->bufptr) {
pgpAssert(len <= (size_t)(input->bufend - input->bufptr));
input->bufptr += len;
return 0;
}
/* Make sure the skip is legal */
pgpAssert(!len || !input->head.lenbytes);
pgpAssert(len <= input->head.pktlen);
pgpAssert(len <= size);
/* Skip the data */
input->head.pktlen -= len;
/* Skip any additional header that follows */
return len + inputHeadSeek(&input->head, buf+len, size-len);
}
/*
* Copy the input buffer to the FIFO.
*/
static PGPError
inputToFifo(Input *input)
{
size_t len, written;
len = (size_t)(input->bufend - input->passptr);
if (len) {
written = pgpFifoWrite (&pgpByteFifoDesc, input->fifo,
input->passptr, len);
input->passptr += written;
if (written != len)
return kPGPError_OutOfMemory;
}
return( kPGPError_NoErr );
}
/*
* Try to ensure that the next "desired" bytes of input are contiguous
* and thus inputPeek will return them in one call. This will copy
* bytes from the external buffer to the input buffer. If it does,
* the number of bytes copied is returned.
*
* If this needs to squeeze subpacket header bytes out of the read-ahead
* buffer to make the buffer contiguous, it copies the bytes to the FIFO
* so they'll be available for raw reading.
*/
static size_t
inputMerge(Input *input, PGPByte const *buf, size_t size, unsigned desired,
PGPError *error)
{
size_t size0;
size_t s, t;
unsigned avail;
pgpAssert(buf || !size);
*error = kPGPError_NoErr; /* No error */
s = (size_t)(input->bufend - input->bufptr);
if (s >= desired)
return 0;
desired -= (unsigned)s;
size0 = size;
while (desired && size) {
/* Suck in the following header, if any */
if (!inputHeadPeek(&input->head, buf, size, &s))
break; /* End of packet */
/* If we have a header, deal with it. */
if (s) {
/*
* Do something with the header - put it in the buffer
* before bufptr if possible, or in the FIFO if not.
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -