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

📄 cmp_rd.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:
/****************************************************************************
*																			*
*								Read CMP Messages							*
*						Copyright Peter Gutmann 1999-2007					*
*																			*
****************************************************************************/

#include <stdio.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "asn1.h"
  #include "asn1_ext.h"
  #include "session.h"
  #include "cmp.h"
#else
  #include "crypt.h"
  #include "misc/asn1.h"
  #include "misc/asn1_ext.h"
  #include "session/session.h"
  #include "session/cmp.h"
#endif /* Compiler-specific includes */

#ifdef USE_CMP

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

/* Read the CMP/Entrust MAC information:

	macInfo ::= SEQUENCE {
		algoID			OBJECT IDENTIFIER (entrustMAC),
		algoParams		SEQUENCE {
			salt		OCTET STRING,
			pwHashAlgo	AlgorithmIdentifier (SHA-1)
			iterations	INTEGER,
			macAlgo		AlgorithmIdentifier (HMAC-SHA1)
			} OPTIONAL
		} */

static int readMacInfo( STREAM *stream, CMP_PROTOCOL_INFO *protocolInfo,
						const void *password, const int passwordLength,
						INOUT ERROR_INFO *errorInfo )
	{
	MESSAGE_CREATEOBJECT_INFO createInfo;
	BYTE salt[ CRYPT_MAX_HASHSIZE + 8 ];
	long value;
	int saltLength, iterations, status;

	/* Read the various parameter fields */
	readSequence( stream, NULL );
	status = readFixedOID( stream, OID_ENTRUST_MAC,
						   sizeofOID( OID_ENTRUST_MAC ) );
	if( cryptStatusError( status ) )
		{
		/* If we don't find this OID we specifically report it as an unknown
		   algorithm problem rather than a generic bad data error */
		protocolInfo->pkiFailInfo = CMPFAILINFO_BADALG;
		retExt( status, 
				( status, errorInfo, "Unrecognised MAC algorithm" ) );
		}
	if( peekTag( stream ) == BER_NULL )
		{
		/* No parameters, use the same values as for the previous
		   transaction */
		return( CRYPT_OK );
		}
	readSequence( stream, NULL );
	readOctetString( stream, salt, &saltLength, 4, CRYPT_MAX_HASHSIZE );
	readUniversal( stream );			/* pwHashAlgo */
	readShortInteger( stream, &value );
	status = readUniversal( stream );	/* macAlgo */
	if( cryptStatusError( status ) )
		retExt( status, 
				( status, errorInfo, "Invalid MAC algorithm information" ) );
	iterations = ( int ) value;
	if( iterations < 1 || iterations > CMP_MAX_PASSWORD_ITERATIONS )
		{
		/* Prevent DoS attacks due to excessive iteration counts (bad
		   algorithm is about the most appropriate error we can return
		   here).  The spec never defines any appropriate limits for this
		   value, which leads to interesting effects when submitting a
		   request for bignum iterations to some implementations */
		protocolInfo->pkiFailInfo = CMPFAILINFO_BADALG;
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, errorInfo, 
				  "Invalid MAC iteration count %d", iterations ) );
		}

	/* If we're the responder and the MAC parameters aren't set yet, set
	   them based on the initiator's values.  If we're using MAC protection
	   and the parameters match our original MAC, reuse the MAC context.
	   As usual the spec is ambiguous over the use of the MAC info, leaving
	   it possible for implementations to re-key the MAC on a per-message
	   basis.  We try and cache MAC info as much as possible to reduce the
	   performance hit from re-keying for each message */
	if( protocolInfo->saltSize <= 0 )
		{
		status = initMacInfo( protocolInfo->iMacContext, password,
							  passwordLength, salt, saltLength, iterations );
		memcpy( protocolInfo->salt, salt, saltLength );
		protocolInfo->saltSize = saltLength;
		protocolInfo->iterations = iterations;
		if( cryptStatusError( status ) )
			retExt( status,
					( status, errorInfo, 
					  "Couldn't initialise MAC information" ) );
		return( CRYPT_OK );
		}
	if( protocolInfo->iterations && \
		saltLength == protocolInfo->saltSize && \
		!memcmp( salt, protocolInfo->salt, saltLength ) && \
		iterations == protocolInfo->iterations )
		{
		protocolInfo->useAltMAC = FALSE;
		return( CRYPT_OK );
		}
	protocolInfo->useAltMAC = TRUE;	/* Use the alternative MAC context */

	/* If we've got an alternative MAC context using the parameters from a
	   previous message already set up, reuse this */
	if( protocolInfo->iAltMacContext != CRYPT_ERROR && \
		saltLength == protocolInfo->altSaltSize && \
		!memcmp( salt, protocolInfo->altSalt, saltLength ) && \
		iterations == protocolInfo->altIterations )
		return( CRYPT_OK );

	/* This is a new set of parameters, create a new altMAC context with
	   them */
	setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_HMAC_SHA );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
							  &createInfo, OBJECT_TYPE_CONTEXT );
	if( cryptStatusError( status ) )
		return( status );
	status = initMacInfo( createInfo.cryptHandle, password, passwordLength,
						  salt, saltLength, iterations );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		retExt( status,
				( status, errorInfo, 
				  "Couldn't initialise alternative MAC information" ) );
		}
	if( protocolInfo->iAltMacContext != CRYPT_ERROR )
		krnlSendNotifier( protocolInfo->iAltMacContext,
						  IMESSAGE_DECREFCOUNT );
	protocolInfo->iAltMacContext = createInfo.cryptHandle;
	memcpy( protocolInfo->altSalt, salt, saltLength );
	protocolInfo->altSaltSize = saltLength;
	protocolInfo->altIterations = iterations;

	return( CRYPT_OK );
	}

/* Read a cert encrypted with CMP's garbled reinvention of CMS content:

	EncryptedCert ::= SEQUENCE {
		dummy			[0]	... OPTIONAL,		-- Ignored
		cekAlg			[1]	AlgorithmIdentifier,-- CEK algorithm
		encCEK			[2]	BIT STRING,			-- Encrypted CEK
		dummy			[3]	... OPTIONAL,		-- Ignored
		dummy			[4] ... OPTIONAL,		-- Ignored
		encData			BIT STRING				-- Encrypted cert
		} */

static int readEncryptedDataInfo( STREAM *stream, void **encKeyPtrPtr, 
								  int *encKeyLength, void **encCertPtrPtr, 
								  int *encCertLength )
	{
	void *dataPtr;
	int length, status;

	/* Clear return values */
	*encKeyPtrPtr = *encCertPtrPtr = NULL;
	*encKeyLength = *encCertLength = 0;

	status = readBitStringHole( stream, &length, 56, 
								CTAG_EV_ENCCEK );
	if( cryptStatusError( status ) )			/* Encrypted CEK */
		return( status );
	if( length < MIN_PKCSIZE || length > CRYPT_MAX_PKCSIZE )
		return( CRYPT_ERROR_BADDATA );
	status = sMemGetDataBlock( stream, &dataPtr, length );
	if( cryptStatusError( status ) )
		return( status );
	sSkip( stream, length );
	*encKeyPtrPtr = dataPtr;
	*encKeyLength = length;
	if( peekTag( stream ) == MAKE_CTAG( CTAG_EV_DUMMY2 ) )
		readUniversal( stream );				/* Junk */
	if( peekTag( stream ) == MAKE_CTAG( CTAG_EV_DUMMY3 ) )
		readUniversal( stream );				/* Junk */
	status = readBitStringHole( stream, &length, 128, DEFAULT_TAG );
	if( cryptStatusError( status ) )			/* Encrypted cert */
		return( status );
	if( length < 128 || length > 8192 )
		return( CRYPT_ERROR_BADDATA );
	status = sMemGetDataBlock( stream, &dataPtr, length );
	if( cryptStatusError( status ) )
		return( status );
	*encCertPtrPtr = dataPtr;
	*encCertLength = length;
	return( sSkip( stream, length ) );
	}

static int readEncryptedCert( STREAM *stream,
							  const CRYPT_CONTEXT iImportContext,
							  INOUT ERROR_INFO *errorInfo )
	{
	CRYPT_CONTEXT iSessionKey;
	MECHANISM_WRAP_INFO mechanismInfo;
	QUERY_INFO queryInfo;
	void *encKeyPtr, *encCertPtr;
	int encKeyLength, encCertLength, status;

	/* Read the CEK algorithm identifier and encrypted CEK.  All of the
	   values are optional although there's no indication of why or what
	   you're supposed to do if they're not present (OTOH for others there's
	   no indication of what you're supposed to do when they're present
	   either) so we treat an absent required value as an error and ignore
	   the others */
	readSequence( stream, NULL );
	if( peekTag( stream ) == MAKE_CTAG( CTAG_EV_DUMMY1 ) )
		readUniversal( stream );				/* Junk */
	status = readContextAlgoID( stream, &iSessionKey, &queryInfo,
								CTAG_EV_CEKALGO );
	if( cryptStatusError( status ) )			/* CEK algo */
		{
		retExt( status,
				( status, errorInfo, 
				  "Invalid encrypted certificate CEK algorithm" ) );
		}
	status = readEncryptedDataInfo( stream, &encKeyPtr, &encKeyLength, 
									&encCertPtr, &encCertLength );
	if( cryptStatusOK( status ) && \
		( queryInfo.cryptMode == CRYPT_MODE_ECB || \
		  queryInfo.cryptMode == CRYPT_MODE_CBC ) )
		{
		int blockSize;

		/* Make sure that the data length is valid.  Checking at this point
		   saves a lot of unnecessary processing and allows us to return a
		   more meaningful error code */
		status = krnlSendMessage( iSessionKey, IMESSAGE_GETATTRIBUTE, 
								  &blockSize, CRYPT_CTXINFO_BLOCKSIZE );
		if( cryptStatusError( status ) || \
			( queryInfo.size % blockSize ) != 0 )
			status = CRYPT_ERROR_BADDATA;
		}
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( iSessionKey, IMESSAGE_DECREFCOUNT );
		retExt( status,
				( status, errorInfo, 
				  "Invalid encrypted certificate CEK data" ) );
		}

	/* Copy the encrypted key to the buffer and import it into the session
	   key context */
	setMechanismWrapInfo( &mechanismInfo, encKeyPtr, encKeyLength,
						  NULL, 0, iSessionKey, iImportContext );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_IMPORT,
							  &mechanismInfo, MECHANISM_ENC_PKCS1 );
	clearMechanismInfo( &mechanismInfo );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( iSessionKey, IMESSAGE_DECREFCOUNT );
		retExt( status,
				( status, errorInfo, 
				  "Couldn't decrypt encrypted certificate CEK" ) );
		}

	/* Decrypt the returned cert */
	status = krnlSendMessage( iSessionKey, IMESSAGE_CTX_DECRYPT,
							  encCertPtr, encCertLength );
	krnlSendNotifier( iSessionKey, IMESSAGE_DECREFCOUNT );
	if( cryptStatusError( status ) )
		{
		retExt( status,
				( status, errorInfo, 
				  "Couldn't decrypt returned encrypted certificate using "
				  "CEK" ) );
		}
	return( CRYPT_OK );
	}

/* Read the kitchen-sink field in the PKI header */

static int readGeneralInfo( STREAM *stream, CMP_PROTOCOL_INFO *protocolInfo )
	{
	int generalInfoEndPos = stell( stream ), length;
	int iterationCount = 0, status;

	/* Go through the various attributes looking for anything that we can
	   use */
	readConstructed( stream, NULL, CTAG_PH_GENERALINFO );
	status = readSequence( stream, &length );
	generalInfoEndPos += length;
	while( cryptStatusOK( status ) && \
		   stell( stream ) < generalInfoEndPos && \
		   iterationCount++ < FAILSAFE_ITERATIONS_MED )
		{
		BYTE oid[ MAX_OID_SIZE + 8 ];

		/* Read the attribute.  Since there are only two attribute types
		   that we use, we hardcode the read in here rather than performing
		   a general-purpose attribute read */
		readSequence( stream, NULL );
		status = readEncodedOID( stream, oid, MAX_OID_SIZE, &length, 
								 BER_OBJECT_IDENTIFIER );
		if( cryptStatusError( status ) )
			break;

		/* Process the cryptlib presence-check value */
		if( length == sizeofOID( OID_CRYPTLIB_PRESENCECHECK ) && \
			!memcmp( oid, OID_CRYPTLIB_PRESENCECHECK, length ) )
			{
			/* The other side is running cryptlib, we can make some common-
			   sense assumptions about its behaviour */
			protocolInfo->isCryptlib = TRUE;
			status = readSet( stream, NULL );/* Attribute */
			continue;
			}

		/* Check for the ESSCertID, which fixes CMP's broken cert
		   identification mechanism */
		if( length == sizeofOID( OID_ESS_CERTID ) && \
			!memcmp( oid, OID_ESS_CERTID, length ) )
			{
			int endPos;

			/* Extract the cert hash from the ESSCertID */
			readSet( stream, NULL );		/* Attribute */
			readSequence( stream, NULL );	/* SigningCerts */
			readSequence( stream, NULL );	/* Certs */
			readSequence( stream, &length );/* ESSCertID */
			endPos = stell( stream ) + length;
			status = readOctetString( stream, protocolInfo->certID,
									  &protocolInfo->certIDsize,
									  8, CRYPT_MAX_HASHSIZE );
			if( cryptStatusOK( status ) && \
				protocolInfo->certIDsize != KEYID_SIZE )
				status = CRYPT_ERROR_BADDATA;
			if( cryptStatusError( status ) )
				continue;
			protocolInfo->certIDchanged = TRUE;

⌨️ 快捷键说明

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