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

📄 pgp_rd.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*						 cryptlib PGP Key Read Routines						*
*						Copyright Peter Gutmann 1992-2007					*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "crypt.h"
  #include "keyset.h"
  #include "misc_rw.h"
  #include "pgp_rw.h"
  #include "pgp_key.h"
#else
  #include "crypt.h"
  #include "keyset/keyset.h"
  #include "misc/misc_rw.h"
  #include "misc/pgp_rw.h"
  #include "keyset/pgp_key.h"
#endif /* Compiler-specific includes */

#ifdef USE_PGPKEYS

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

/* Get the size of an encoded MPI and skip the payload data */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
static int getMPIsize( INOUT STREAM *stream, 
					   IN_LENGTH_PKC const int minMpiSize, 
					   IN_LENGTH_PKC const int maxMpiSize,
					   OUT_LENGTH_SHORT_Z int *length )
	{
	const long position = stell( stream );
	int dummy, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( length, sizeof( int ) ) );

	REQUIRES( minMpiSize > 0 && minMpiSize <= CRYPT_MAX_PKCSIZE );
	REQUIRES( maxMpiSize > 0 && maxMpiSize >= minMpiSize && \
			  maxMpiSize <= CRYPT_MAX_PKCSIZE );

	/* Clear return value */
	*length = 0;
	
	status = readInteger16Ubits( stream, NULL, &dummy, minMpiSize, 
								 maxMpiSize );
	if( cryptStatusError( status ) )
		return( status );
	*length = ( int ) stell( stream ) - position;

	return( CRYPT_OK );
	}

/* Determine the minimum allowed packet size for a given packet type.  The 
   minimum-length packet that we can encounter is a single-byte trust 
   packet, then two bytes for a userID, and three bytes for a marker 
   packet.  Other than that all packets must be at least eight bytes in 
   length */

CHECK_RETVAL_RANGE( 0, 8 ) \
static int getMinPacketSize( IN_BYTE const int packetType )
	{
	ENSURES_EXT( ( packetType >= 0 && \
				   packetType <= 0xFF ), 0 );
				 /* This could be any packet type including new values not
				    covered by the PGP_PACKET_TYPE range so we can't be too
					picky about values */

	return( ( packetType == PGP_PACKET_TRUST ) ? 1 : \
			( packetType == PGP_PACKET_USERID ) ? 2 : \
			( packetType == PGP_PACKET_MARKER ) ? 3 : 8 );
	}

/* Scan a sequence of key packets to find the extent of the packet group.  In
   addition to simply scanning, this function handles over-long packets by
   reporting their overall length and returning OK_SPECIAL, and will try to 
   resync to a packet group if it starts in the middle of an arbitrary packet 
   collection, for example due to skipping of an over-long packet found 
   earlier */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int scanPacketGroup( IN_BUFFER( dataLength ) const void *data, 
							IN_LENGTH_SHORT const int dataLength,
							OUT_LENGTH_SHORT_Z int *packetGroupLength )
	{
	STREAM stream;
	BOOLEAN firstPacket = TRUE, skipPackets = FALSE;
	int endPos = 0, iterationCount = 0, status = CRYPT_OK;

	assert( isReadPtr( data, dataLength ) );
	assert( isWritePtr( packetGroupLength, sizeof( int ) ) );

	REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );

	/* Clear return value */
	*packetGroupLength = 0;

	sMemConnect( &stream, data, dataLength );
	do
		{
		long length;
		int ctb, type;

		/* Get the next CTB.  If it's the start of another packet group,
		   we're done */
		ctb = status = sPeek( &stream );
		if( cryptStatusOK( status ) && !( pgpIsCTB( ctb ) ) )
			status = CRYPT_ERROR_BADDATA;
		if( cryptStatusError( status ) )
			{
			sMemDisconnect( &stream );
			return( status );
			}
		type = pgpGetPacketType( ctb );
		if( firstPacket )
			{
			/* If the packet group doesn't start with the expected packet
			   type, skip packets to try and resync */
			if( type != PGP_PACKET_PUBKEY && type != PGP_PACKET_SECKEY )
				skipPackets = TRUE;
			firstPacket = FALSE;
			}
		else
			{
			if( type == PGP_PACKET_PUBKEY || type == PGP_PACKET_SECKEY )
				{
				/* We've found the start of a new packet group, remember 
				   where the current group ends and exit */
				sMemDisconnect( &stream );
				*packetGroupLength = endPos;

				return( skipPackets ? OK_SPECIAL : CRYPT_OK );
				}
			}

		/* Skip the current packet in the buffer */
		status = pgpReadPacketHeader( &stream, NULL, &length, \
									  getMinPacketSize( type ) );
		if( cryptStatusOK( status ) )
			{
			endPos = stell( &stream ) + length;
			status = sSkip( &stream, length );
			}
		if( cryptStatusError( status ) )
			{
			sMemDisconnect( &stream );
			return( status );
			}
		}
	while( endPos < dataLength && \
		   iterationCount++ < FAILSAFE_ITERATIONS_LARGE );
	ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
	sMemDisconnect( &stream );
	*packetGroupLength = endPos;

	/* If we skipped packets or consumed all of the input in the buffer and 
	   there's more present beyond that, tell the caller to discard the
	   data and try again */
	return( ( skipPackets || endPos > dataLength ) ? OK_SPECIAL : CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Process Key Packet Components					*
*																			*
****************************************************************************/

/* Read the information needed to decrypt a private key */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readPrivateKeyDecryptionInfo( INOUT STREAM *stream, 
										 INOUT PGP_KEYINFO *keyInfo )
	{
	const int ctb = sgetc( stream );
	int ivSize = 8, value, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( keyInfo, sizeof( PGP_KEYINFO ) ) );

	/* Clear return values */
	keyInfo->cryptAlgo = keyInfo->hashAlgo = CRYPT_ALGO_NONE;
	keyInfo->saltSize = keyInfo->keySetupIterations = 0;

	/* Before we go any further make sure that we were at least able to read 
	   the CTB */
	if( cryptStatusError( ctb ) )
		return( ctb );

	/* If no encryption is being used we mark the key as unusable.  This 
	   isn't exactly the correct thing to do, but storing plaintext private 
	   keys on disk is extremely dangerous and we probably shouldn't be
	   using them, and in any case an attempt to import an unencrypted key 
	   will trigger so many security check failures in the key unwrap code 
	   that it's not even worth trying */
	if( ctb == 0 )
		return( CRYPT_ERROR_NOSECURE );

	/* If it's a direct algorithm specifier then it's a PGP 2.x packet with 
	   raw IDEA encryption */
	if( ctb == PGP_ALGO_IDEA )
		{
		keyInfo->cryptAlgo = CRYPT_ALGO_IDEA;
		keyInfo->hashAlgo = CRYPT_ALGO_MD5;
		status = sread( stream, keyInfo->iv, ivSize );
		if( cryptStatusError( status ) )
			return( status );
		keyInfo->ivSize = ivSize;

		return( CRYPT_OK );
		}

	/* Must be an S2K specifier */
	if( ctb != PGP_S2K && ctb != PGP_S2K_HASHED )
		return( CRYPT_ERROR_BADDATA );

	/* Get the key wrap algorithm and S2K information.  We have to save a 
	   copy of the raw PGP algorithm ID for later examination to determine
	   the AES key size to use */
	value = sPeek( stream );
	status = readPgpAlgo( stream, &keyInfo->cryptAlgo, 
						  PGP_ALGOCLASS_PWCRYPT );
	if( cryptStatusError( status ) )
		{
		if( status == CRYPT_ERROR_NOTAVAIL )
			{
			/* Unknown algorithm type, skip this packet */
			return( OK_SPECIAL );
			}

		 return( status );
		 }
	if( keyInfo->cryptAlgo == CRYPT_ALGO_AES )
		{
		/* PGP uses three different algorithm IDs to identify AES with 
		   different key sizes (ugh) so we have to remember the key size 
		   alongside the algorithm type for this algorithm */
		keyInfo->aesKeySize = ( value == PGP_ALGO_AES_128 ) ? 16 : \
							  ( value == PGP_ALGO_AES_192 ) ? 24 : 32;
		ivSize = 16;
		}
	status = value = sgetc( stream );
	if( cryptStatusError( status ) )
		return( status );
	if( value != 0 && value != 1 && value != 3 )
		return( OK_SPECIAL );
	status = readPgpAlgo( stream, &keyInfo->hashAlgo, 
						  PGP_ALGOCLASS_HASH );
	if( cryptStatusError( status ) )
		{
		if( status == CRYPT_ERROR_NOTAVAIL )
			{
			/* Unknown algorithm type, skip this packet */
			return( OK_SPECIAL );
			}

		 return( status );
		 }
	if( value != 0 )
		{
		/* It's a salted hash */
		status = sread( stream, keyInfo->salt, PGP_SALTSIZE );
		if( cryptStatusError( status ) )
			return( status );
		keyInfo->saltSize = PGP_SALTSIZE;
		}
	if( value == 3 )
		{
		long iterations;

		/* It's a 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 that 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.  Note that there's a mutant GPG build used 
		   with loop-AES that uses 8M setup iterations, why this is used and 
		   why it writes PGP keys with this setting is uncertain but 
		   cryptlib will reject keys with this value as being outside the 
		   range of sane values */
		value = sgetc( stream );
		if( cryptStatusError( value ) )
			return( value );
		iterations = ( 16 + ( ( long ) value & 0x0F ) ) << ( value >> 4 );
		if( iterations <= 0 || iterations > MAX_KEYSETUP_ITERATIONS || \
			iterations >= MAX_INTLENGTH )
			return( CRYPT_ERROR_BADDATA );
		keyInfo->keySetupIterations = ( int ) iterations;
		}
	if( ctb == PGP_S2K_HASHED )
		{
		/* The legacy PGP 2.x key integrity protection format used a simple 
		   16-bit additive checksum of the encrypted MPI payload, the newer 
		   OpenPGP format uses a SHA-1 MDC.  There's also a halfway format 
		   used in older OpenPGP versions that still uses the 16-bit 
		   checksum but encrypts the entire MPI data block rather than just 
		   the payload */
		keyInfo->hashedChecksum = TRUE;
		}
	status = sread( stream, keyInfo->iv, ivSize );
	if( cryptStatusError( status ) )
		return( status );
	keyInfo->ivSize = ivSize;

	return( CRYPT_OK );
	}

/* Read public-key components */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int readPublicKeyComponents( INOUT STREAM *stream, 
									INOUT PGP_KEYINFO *keyInfo,
									OUT_INT_Z int *pubKeyComponentLength )
	{
	int pgpPkcAlgo, length, totalLength = 1, status;
					/* Initial length 1 is for the algorithm ID byte */

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( keyInfo, sizeof( PGP_KEYINFO ) ) );
	assert( isWritePtr( pubKeyComponentLength, sizeof( int ) ) );

	/* Clear return value */
	*pubKeyComponentLength = 0;

	/* Get the public-key algorithm type */
	status = pgpPkcAlgo = sgetc( stream );
	if( cryptStatusError( status ) )
		return( status );

	/* RSA: n + e.  The LSBs of n serve as the PGP 2.x key ID so we copy the 
	   data out before continuing */
	if( pgpPkcAlgo == PGP_ALGO_RSA || pgpPkcAlgo == PGP_ALGO_RSA_ENCRYPT || \
		pgpPkcAlgo == PGP_ALGO_RSA_SIGN )
		{
		keyInfo->pkcAlgo = CRYPT_ALGO_RSA;
		if( pgpPkcAlgo != PGP_ALGO_RSA_SIGN )
			keyInfo->usageFlags = KEYMGMT_FLAG_USAGE_CRYPT;
		if( pgpPkcAlgo != PGP_ALGO_RSA_ENCRYPT )
			keyInfo->usageFlags |= KEYMGMT_FLAG_USAGE_SIGN;
		status = getMPIsize( stream, MIN_PKCSIZE, CRYPT_MAX_PKCSIZE, 
							 &length );			/* n */
		if( cryptStatusError( status ) )
			return( status );
		totalLength += length;
		assert( PGP_KEYID_SIZE < MIN_PKCSIZE );
	
		/* Move back and copy out the last PGP_KEYID_SIZE bytes of n as the PGP 
		   2.x key ID */
		status = sseek( stream, stell( stream ) - PGP_KEYID_SIZE );
		if( cryptStatusOK( status ) )
			status = sread( stream, keyInfo->pgpKeyID, PGP_KEYID_SIZE );
		if( cryptStatusOK( status ) )
			status = getMPIsize( stream, 1, CRYPT_MAX_PKCSIZE, &length );
		if( cryptStatusError( status ) )		/* e */
			return( status );
		*pubKeyComponentLength = totalLength + length;

⌨️ 快捷键说明

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