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

📄 pgp_deen.c

📁 提供了很多种加密算法和CA认证及相关服务如CMP、OCSP等的开发
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*					 cryptlib PGP De-enveloping Routines					*
*					 Copyright Peter Gutmann 1996-2002						*
*																			*
****************************************************************************/

#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
  #include "envelope.h"
  #include "pgp.h"
#elif defined( INC_CHILD )
  #include "../envelope/envelope.h"
  #include "../envelope/pgp.h"
#else
  #include "envelope/envelope.h"
  #include "envelope/pgp.h"
#endif /* Compiler-specific includes */

/****************************************************************************
*																			*
*								Utility Routines							*
*																			*
****************************************************************************/

/* Get information on a PGP data packet, checking that there's enough
   data left to get the packet information */

static int getPacketInfo( STREAM *stream, ENVELOPE_INFO *envelopeInfoPtr,
						  long *length )
	{
	long localLength;
	BYTE ctb;

	/* We always need at least two more bytes to do anything */
	if( sMemDataLeft( stream ) < 2 )
		return( CRYPT_ERROR_UNDERFLOW );

	/* Peek at the CTB and figure out whether we've got enough data left to
	   read the header */
	ctb = sgetc( stream );
	if( ( ctb & PGP_CTB_OPENPGP ) == PGP_CTB_OPENPGP )
		{
		/* OpenPGP has an awkward variable-length encoding which requires
		   that we burrow further down into the data to get the actual
		   length, to avoid problems with having to undo this we assume a
		   worst-case length of 5 bytes.  This is safe because the shortest
		   possible packet type, a conventionally-encrypted data packet with
		   a 1-byte payload, contains a minimum of 11 bytes of data (8-byte
		   IV, 2 bytes of repeated IV data, and 1 byte of payload) */
		if( sMemDataLeft( stream ) < 5 )
			{
			sungetc( stream );
			return( CRYPT_ERROR_UNDERFLOW );
			}
		envelopeInfoPtr->version = PGP_VERSION_OPENPGP;
		}
	else
		if( ctb == PGP_CTB_COMPRESSED )
			{
			/* Normally we reject any indefinite-length packets since these
			   can't be processed sensibly (PGP 2.x, which used intermediate
			   files for everything, just read to EOF, OpenPGP deprecates
			   them because this doesn't exactly lead to portable
			   implementations).  However, compressed-data packets can only
			   be stored in this manner and can be processed because the user
			   has to explicitly flush the data at some point and we assume
			   that this is EOF.  This isn't anywhere near as clean as the
			   PKCS #7/CMS/SMIME equivalent where we've got an explicit end-
			   of-data indication, but it does the trick */
			if( length != NULL )
				*length = CRYPT_UNUSED;
			return( PGP_PACKET_COPR );
			}
		else
			{
			static const int lengthOfLength[ 4 ] = { 1, 2, 4, 0 };

			if( sMemDataLeft( stream ) < lengthOfLength[ ctb & 3 ] )
				{
				sungetc( stream );
				return( CRYPT_ERROR_UNDERFLOW );
				}
			envelopeInfoPtr->version = PGP_VERSION_2;
			}

	/* Now that the header is present as plaintext, parse it */
	localLength = pgpGetLength( stream, ctb );
	if( cryptStatusError( localLength ) )
		return( ( int ) localLength );
	if( length != NULL )
		*length = localLength;

	/* Extract and return the packet type */
	return( ( ( ctb & PGP_CTB_OPENPGP ) == PGP_CTB_OPENPGP ) ? \
			( ctb & 0x3F ) : ( ( ctb >> 2 ) & 0x0F ) );
	}

/* Read various PGP algorithm IDs and convert them to a cryptlib algorithm 
   type */

static int readHashAlgorithmID( STREAM *stream )
	{
	switch( sgetc( stream ) )
		{
		case PGP_ALGO_MD2:
			return( CRYPT_ALGO_MD2 );

		case PGP_ALGO_MD5:
			return( CRYPT_ALGO_MD5 );

		case PGP_ALGO_SHA:
			return( CRYPT_ALGO_SHA );

		case PGP_ALGO_RIPEMD160:
			return( CRYPT_ALGO_RIPEMD160 );
		}

	return( CRYPT_ALGO_NONE );
	}

static int readSignAlgorithmID( STREAM *stream )
	{
	switch( sgetc( stream ) )
		{
		case PGP_ALGO_RSA:
		case PGP_ALGO_RSA_SIGN:
			return( CRYPT_ALGO_RSA );

		case PGP_ALGO_DSA:
			return( CRYPT_ALGO_DSA );
		}

	return( CRYPT_ERROR_NOTAVAIL );
	}

/****************************************************************************
*																			*
*						Read Key Exchange/Signature Packets					*
*																			*
****************************************************************************/

/* Process signature subpackets */

static int processSignatureSubpackets( STREAM *stream,
									   CONTENT_LIST *contentListItem,
									   const int length,
									   const int startOffset,
									   const BOOLEAN isAuthenticated )
	{
	const int endPos = ( int ) stell( stream ) + length;

	while( stell( stream ) < endPos )
		{
		const int subpacketLength = ( int ) pgpGetLength( stream,
														  PGP_CTB_OPENPGP );
		const int type = sgetc( stream );

		if( cryptStatusError( subpacketLength ) )
			return( subpacketLength );

		/* If it's an unrecognised subpacket with the critical flag set,
		   reject the signature.  The range check isn't complete since there
		   are a few holes in the range, but since the holes presumably exist
		   because of deprecated subpacket types, any new packets will be
		   added at the end so it's safe to use */
		if( ( type & 0x80 ) && ( ( type & 0x7F ) > PGP_SUBPACKET_LAST ) )
			return( CRYPT_ERROR_NOTAVAIL );

		/* If it's a key ID and we haven't already set this from a preceding
		   one-pass signature packet (which can happen with detached sigs),
		   set it now */
		if( type == PGP_SUBPACKET_KEYID && !contentListItem->keyIDsize )
			{
			sread( stream, contentListItem->keyID, PGP_KEYID_SIZE );
			contentListItem->keyIDsize = PGP_KEYID_SIZE;
			continue;
			}

		/* If it's a type-and-value packet, see whether it's one of
		   ours */
		if( type == PGP_SUBPACKET_TYPEANDVALUE )
			{
			BYTE nameBuffer[ 32 ];
			static const char *nameString = "issuerAndSerialNumber";
			int nameLength, valueLength;

			sSkip( stream, 4 );		/* Flags */
			nameLength = ( sgetc( stream ) << 8 ) | sgetc( stream );
			valueLength = ( sgetc( stream ) << 8 ) | sgetc( stream );
			if( nameLength != strlen( nameString ) || \
				valueLength < 16 || valueLength > 2048 )
				{
				sSkip( stream, nameLength + valueLength );
				continue;
				}
			sread( stream, nameBuffer, nameLength );
			if( memcmp( nameBuffer, nameString, nameLength ) )
				{
				sSkip( stream, valueLength );
				continue;
				}

			/* It's an issuerAndSerialNumber, remember it for later */
			contentListItem->issuerAndSerialNumber = \
						( BYTE * ) contentListItem->object + \
						( ( int ) stell( stream ) - startOffset );
			contentListItem->issuerAndSerialNumberSize = valueLength;
			sSkip( stream, valueLength );
			continue;
			}

		/* It's something else, skip it and continue */
		sSkip( stream, subpacketLength - 1 );
		}

	return( sGetStatus( stream ) );
	}

/* Read various PGP packets:

   SKE:	byte	version = 4
		byte	cryptAlgo
		byte	stringToKey specifier
		byte[]	stringToKey data */

static int readSKEPacket( STREAM *stream, CONTENT_LIST *contentListItem )
	{
	int value, status;

	/* Check the packet version and get the password hash algorithm */
	if( sgetc( stream ) != 4 )
		return( CRYPT_ERROR_BADDATA );
	switch( sgetc( stream ) )
		{
		case PGP_ALGO_IDEA:
			contentListItem->cryptAlgo = CRYPT_ALGO_IDEA;
			break;

		case PGP_ALGO_3DES:
			contentListItem->cryptAlgo = CRYPT_ALGO_3DES;
			break;

		case PGP_ALGO_CAST5:
			contentListItem->cryptAlgo = CRYPT_ALGO_CAST;
			break;

		case PGP_ALGO_BLOWFISH:
			contentListItem->cryptAlgo = CRYPT_ALGO_BLOWFISH;
			break;

		case PGP_ALGO_AES_128:
			contentListItem->cryptAlgo = CRYPT_ALGO_AES;
			break;

		default:
			return( CRYPT_ERROR_NOTAVAIL );
		}
	contentListItem->cryptMode = CRYPT_MODE_CFB;
	contentListItem->envInfo = CRYPT_ENVINFO_PASSWORD;

	/* Read the S2K specifier */
	value = sgetc( stream );
	if( value != 0 && value != 1 && value != 3 )
		return( CRYPT_ERROR_BADDATA );
	if( ( contentListItem->hashAlgo = \
					readHashAlgorithmID( stream ) ) == CRYPT_ALGO_NONE )
		return( CRYPT_ERROR_NOTAVAIL );
	if( value == 0 )
		/* It's a straight hash, we're done */
		return( CRYPT_OK );
	status = sread( stream, contentListItem->saltOrIV, PGP_SALTSIZE );
	if( cryptStatusError( status ) )
		return( status );
	contentListItem->saltOrIVsize = PGP_SALTSIZE;
	if( value == 3 )
		{
		/* Salted iterated hash, get the iteration count, limited to a sane
		   value.  The "iteration count" is actually a count of how many
		   bytes are hashed, this is because the "iterated hashing" treats
		   the salt + password as an infinitely-repeated sequence of values
		   and hashes the resulting string for PGP-iteration-count bytes
		   worth.  The value we calculate here (to prevent overflow on 16-bit
		   machines) is the count without the base * 64 scaling, this also
		   puts the range within the value of the standard sanity check */
		value = sgetc( stream );
		if( cryptStatusError( status ) )
			return( status );
		contentListItem->keySetupIterations = \
				( 16 + ( ( long ) value & 0x0F ) ) << ( value >> 4 );
		if( contentListItem->keySetupIterations > MAX_KEYSETUP_ITERATIONS )
			return( CRYPT_ERROR_BADDATA );
		}

	return( CRYPT_OK );
	}

/* PKE:	byte	version = 2 or 3
		byte[8]	keyID
		byte	PKC algo
		mpi(s)	encrypted session key */

static int readPKEPacket( STREAM *stream, CONTENT_LIST *contentListItem )
	{
	int offset = ( int ) stell( stream );
	int value, status;

	/* Check the packet version and get the PGP key ID and algorithm */
	value = sgetc( stream );
	if( value != PGP_VERSION_2 && value != PGP_VERSION_3 )
		return( CRYPT_ERROR_BADDATA );
	status = sread( stream, contentListItem->keyID, PGP_KEYID_SIZE );
	if( cryptStatusError( status ) )
		return( status );
	contentListItem->keyIDsize = PGP_KEYID_SIZE;
	switch( sgetc( stream ) )
		{
		case PGP_ALGO_RSA:
		case PGP_ALGO_RSA_ENCRYPT:
			contentListItem->cryptAlgo = CRYPT_ALGO_RSA;
			break;

		case PGP_ALGO_ELGAMAL:
			contentListItem->cryptAlgo = CRYPT_ALGO_ELGAMAL;
			break;

		default:
			return( CRYPT_ERROR_NOTAVAIL );
		}
	contentListItem->envInfo = CRYPT_ENVINFO_PRIVATEKEY;

	/* Read the encrypted key */
	offset = ( int ) stell( stream ) - offset;
	status = pgpReadMPI( stream, NULL );
	if( cryptStatusError( status ) )
		return( status );
	contentListItem->payload = ( BYTE * ) contentListItem->object + offset;
	contentListItem->payloadSize = bitsToBytes( status ) + 2;
	if( contentListItem->payloadSize < 56 || \
		contentListItem->payloadSize > CRYPT_MAX_PKCSIZE )
		return( CRYPT_ERROR_BADDATA );
	if( contentListItem->cryptAlgo == CRYPT_ALGO_RSA )
		return( CRYPT_OK );
	status = pgpReadMPI( stream, NULL );
	if( cryptStatusError( status ) )
		return( status );
	contentListItem->payloadSize += bitsToBytes( status ) + 2;
	if( contentListItem->payloadSize < ( 56 * 2 ) || \
		contentListItem->payloadSize > ( CRYPT_MAX_PKCSIZE * 2 ) )
		return( CRYPT_ERROR_BADDATA );

	return( CRYPT_OK );
	}

/* Signature info:
		byte	version = 3
		byte	sigType
		byte	hashAlgo
		byte	sigAlgo
		byte[8]	keyID
		byte	1 */

static int readOnepassSigPacket( STREAM *stream, CONTENT_LIST *contentListItem )
	{
	int status;

	/* Check the packet version, skip the sig.type, get the hash algorithm
	   and check the signature algorithm */
	if( sgetc( stream ) != PGP_VERSION_3 )
		return( CRYPT_ERROR_BADDATA );
	sgetc( stream );
	if( ( contentListItem->hashAlgo = \
					readHashAlgorithmID( stream ) ) == CRYPT_ALGO_NONE )
		return( CRYPT_ERROR_NOTAVAIL );
	if( ( contentListItem->cryptAlgo = \
					readSignAlgorithmID( stream ) ) == CRYPT_ALGO_NONE )
		return( CRYPT_ERROR_NOTAVAIL );
	contentListItem->envInfo = CRYPT_ENVINFO_SIGNATURE;

	/* Get the PGP key ID and make sure this isn't a nested signature */
	status = sread( stream, contentListItem->keyID, PGP_KEYID_SIZE );
	if( cryptStatusError( status ) )
		return( status );
	contentListItem->keyIDsize = PGP_KEYID_SIZE;
	return( ( sgetc( stream ) != 1 ) ? CRYPT_ERROR_BADDATA : CRYPT_OK );
	}

/* Signature:

	v3:	byte	version = 3		v4: byte	version = 4
		byte	infoLen = 5			byte	sigType
			byte	sigType			byte	sigAlgo
			byte[4]	sig.time		byte	hashAlgo
		byte[8]	keyID				byte[2]	length of auth.attributes
		byte	sigAlgo				byte[]	authenticated attributes
		byte	hashAlgo			byte[2]	length of unauth.attributes
		byte[2]	hash check			byte[]	unauthenticated attributes
		mpi(s)	signature			byte[2]	hash check
									mpi(s)	signature */

static int readSigPacket( STREAM *stream, CONTENT_LIST *contentListItem )
	{
	int offset = ( int ) stell( stream );
	int value, status;

	/* Check the packet version */
	value = sgetc( stream );
	if( value != PGP_VERSION_2 && value != PGP_VERSION_3 && \
		value != PGP_VERSION_OPENPGP )
		return( CRYPT_ERROR_BADDATA );
	contentListItem->envInfo = CRYPT_ENVINFO_SIGNATURE;

	/* If it's not an OpenPGP packet, read it as a PGP 2.x-format
	   signature */
	if( value != PGP_VERSION_OPENPGP )
		{
		/* Read the additional signature information */
		if( sgetc( stream ) != 5 )
			return( CRYPT_ERROR_BADDATA );
		contentListItem->extraData = ( BYTE * ) contentListItem->object + \
									 ( ( int ) stell( stream ) - offset );
		contentListItem->extraDataLength = 5;
		sSkip( stream, 5 );

		/* Read the signer keyID, signature and hash algorithm */
		status = sread( stream, contentListItem->keyID, PGP_KEYID_SIZE );
		if( cryptStatusError( status ) )
			return( status );
		contentListItem->keyIDsize = PGP_KEYID_SIZE;
		if( ( contentListItem->cryptAlgo = \
					readSignAlgorithmID( stream ) ) == CRYPT_ALGO_NONE )
			return( CRYPT_ERROR_NOTAVAIL );
		if( ( contentListItem->hashAlgo = \
					readHashAlgorithmID( stream ) ) == CRYPT_ALGO_NONE )
			return( CRYPT_ERROR_NOTAVAIL );

		/* Skip the hash check and read the signature, recording the start

⌨️ 快捷键说明

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