⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pgpprsbin.c

📁 著名的加密软件的应用于电子邮件中
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * pgpPrsBin.c -- Binary Parser
 *
 * Copyright (C) 1995-1997 Pretty Good Privacy, Inc. All rights reserved.
 *
 * Written by:	Colin Plumb and Derek Atkins <warlord@MIT.EDU>
 *
 * $Id: pgpPrsBin.c,v 1.9.2.3 1997/06/07 09:51:00 mhw Exp $
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>

#include "pgpDebug.h"
#include "pgpPktByte.h"
#include "pgpPrsBin.h"
#include "pgpAnnotate.h"
#include "pgpCFB.h"
#include "pgpCipher.h"
#include "pgpCiphrMod.h"
#include "pgpCompress.h"
#include "pgpCompMod.h"
#include "pgpFIFO.h"
#include "pgpHash.h"
#include "pgpHashMod.h"
#include "pgpHeader.h"
#include "pgpMem.h"
#include "pgpEnv.h"
#include "pgpErr.h"
#include "pgpPipeline.h"
#include "pgpSig.h"
#include "pgpTextFilt.h"
#include "pgpUsuals.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 struct 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 */
#define MAXPKTSIZE		1100


static size_t nextScope (struct PgpPipeline *myself, byte const *buf,
			size_t size, int *error);
static size_t nextESK (struct PgpPipeline *myself, byte const *buf,
		size_t size, int *error);
static size_t parsePacket (struct PgpPipeline *myself, byte const *buf,
			size_t size, int *error);
static size_t parseKey (struct PgpPipeline *myself, byte const *buf,
			size_t size, int *error);

/* Information needed to parse a subpacket header */
struct Header {
	word32 pktlen;		/* Length of current subpacket */
	unsigned lenbytes;	/* Number of length bytes to come */
	byte more;		/* More subpackets to come */
};

/* Information needed to parse a data stream */
struct Input {
	byte buffer[MAXPKTSIZE];	/* Buffer of parsed data */
	struct PgpFifoContext *fifo;	/* FIFO of raw data */
	byte const *passptr;	/* Pointer into buffer */
	byte const *bufptr;	/* Pointer into buffer */
	byte *bufend;	 	/* Pointer into buffer */
	struct Header head;	/* Current subpacket status */
	byte silent_trunc;	/* Complain if out of input? */
};

struct Context {
	struct Input input;
	struct PgpPipeline *tail;
	struct PgpPipeline *nextparser;
	struct PgpPipeline **end;
	struct PgpHashContext *hashes;
	struct PgpEnv const *env;
	int numhashes;
	int state;		/* Used my bumerous parse functions */
	int end_scope;		/* Annotation at end of this scope, or 0 */
	byte subtype;	 	/* Type of literal packet (t vs. b) */
	int sig1pass;	 	/* Within 1-pass signature scope */
	int sig1nest;	 	/* Number of unpaired 1pass headers nested */
	/* Flags */
	byte sepsig;		/* 1-pass separate signature */
	byte findpkt;	 	/* Searching for a new packet */
	byte eof;	 	/* No more input available, ever */
	byte needcallback;
};

/* Return true if the end of the current packet has been reached */
static int
inputFinished(struct 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 byte const *
inputHeadPeek(struct Header const *head, byte 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 (byte const *)0;
	}
	if (!size) {
		*len = 0;
		return buf;
	}
	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(struct Header *head, byte 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 += (word32)*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 = (word32)b;
		head->more = 0;
	} else if (b < 0xe0) {
		head->pktlen = ((word32)b << 8) - (0xc000 - 0xc0);
		head->more = 0;
		if (size == 1) {
			head->lenbytes = 1;
		} else {
			head->pktlen += (word32)buf[1];
			return 2;
		}
	} else if (b >= 0xe0) {
		head->pktlen = (word32)1 << (b & 31);
	}
	return 1;
}

static void
inputReset(struct 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(struct Input *input, byte 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 = (word32)-1;
		}
	} else {
		/* Erroneous! */
		input->bufptr = input->buffer;
		input->head.pktlen = (word32)-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 byte const *
inputPeek(struct Input const *input, byte 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(struct Input *input, byte 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 int
inputToFifo(struct 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 PGPERR_NOMEM;
	}
	return 0;
}

/*
 * 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(struct Input *input, byte const *buf, size_t size, unsigned desired,
int *error)
{
	size_t size0;
	size_t s, t;
	unsigned avail;

	pgpAssert(buf || !size);

	*error = 0;	/* 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.
			*/
			if (input->bufptr == input->bufend) {
				memcpy(input->bufend, buf, s);
				(void)inputHeadSeek(&input->head, buf, s);
				input->bufptr = input->bufend += s;
				buf += s;
				size -= s;
			} else {
				/* Write what's already in the buffer */
				*error = inputToFifo(input);
				if (*error)
					break;
				/* Write the current header to the FIFO */
				t = pgpFifoWrite (&pgpByteFifoDesc,
			input->fifo, buf, s);
				(void)inputHeadSeek(&input->head, buf, t);
				buf += t;
				size -= t;
				if (t != s) {
					*error = PGPERR_NOMEM;
					break;
				}
			}
		}

		/* Normal data to be copied */
		avail = desired;
		if (avail > size)
			avail = (unsigned)size;
		if (avail > input->head.pktlen)
			avail = (unsigned)input->head.pktlen;
		s = input->buffer+sizeof(input->buffer)-input->bufend;
		if (avail > s) {
			if (!s)
				break;
			avail = (unsigned)s;
		}
		memcpy(input->bufend, buf, avail);
		input->bufend += avail;
		input->head.pktlen -= avail;
		size -= avail;
		desired -= avail;
	}

	return size0-size;
}

/* Return true if there's more packet in here than will fit */
static int
inputOverfull(struct Input const *input)
{
	return input->bufend == input->buffer+sizeof(input->buffer) &&
		!inputFinished(&input->head);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -