Esempio n. 1
0
static int readGeneralInfo( INOUT STREAM *stream, 
							INOUT CMP_PROTOCOL_INFO *protocolInfo )
	{
	long endPos;
	int length, iterationCount, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );

	/* Go through the various attributes looking for anything that we can
	   use */
	readConstructed( stream, NULL, CTAG_PH_GENERALINFO );
	status = readSequence( stream, &length );
	if( cryptStatusError( status ) )
		return( status );
	endPos = stell( stream ) + length;
	for( iterationCount = 0; 
		 stell( stream ) < endPos && \
			iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ )
		{
		status = readGeneralInfoAttribute( stream, protocolInfo );
		if( cryptStatusError( status ) )
			return( status );
		}
	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );

	return( status );
	}
Esempio n. 2
0
static int readUserID( INOUT STREAM *stream, 
					   INOUT CMP_PROTOCOL_INFO *protocolInfo )
	{
	BYTE userID[ CRYPT_MAX_HASHSIZE + 8 ];
	int userIDsize, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );

	/* Read the PKI user ID that we'll need to handle the integrity 
	   protection on the message */
	readConstructed( stream, NULL, CTAG_PH_SENDERKID );
	status = readOctetString( stream, userID, &userIDsize, 8, 
							  CRYPT_MAX_HASHSIZE );
	if( cryptStatusError( status ) )
		return( status );
	ANALYSER_HINT( userIDsize >= 8 && userIDsize <= CRYPT_MAX_HASHSIZE );

	/* If there's already been a previous transaction (which means that we 
	   have PKI user information present) and the current transaction 
	   matches what was used in the previous one, we don't have to update 
	   the user information */
	if( protocolInfo->userIDsize == userIDsize && \
		!memcmp( protocolInfo->userID, userID, userIDsize ) )
		{
		DEBUG_PRINT(( "%s: Skipped repeated userID.\n",
					  protocolInfo->isServer ? "SVR" : "CLI" ));
		DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI", 
						protocolInfo->userID, protocolInfo->userIDsize );
		return( CRYPT_OK );
		}

	/* Record the new or changed the PKI user information and delete the 
	   MAC context associated with the previous user if necessary */
	memcpy( protocolInfo->userID, userID, userIDsize );
	protocolInfo->userIDsize = userIDsize;
	protocolInfo->userIDchanged = TRUE;
	if( protocolInfo->iMacContext != CRYPT_ERROR )
		{
		krnlSendNotifier( protocolInfo->iMacContext,
						  IMESSAGE_DECREFCOUNT );
		protocolInfo->iMacContext = CRYPT_ERROR;
		}
	DEBUG_PRINT(( "%s: Read new userID.\n",
				  protocolInfo->isServer ? "SVR" : "CLI" ));
	DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI", 
					protocolInfo->userID, protocolInfo->userIDsize );

	return( CRYPT_OK );
	}
Esempio n. 3
0
static int readProtectionAlgo( INOUT STREAM *stream, 
							   INOUT CMP_PROTOCOL_INFO *protocolInfo )
	{
	CRYPT_ALGO_TYPE cryptAlgo, hashAlgo;
	int hashParam, streamPos, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );

	/* Read the wrapper.  If there's a problem we exit immediately since an 
	   error status from the readAlgoIDex() that follows is interpreted to 
	   indicate the presence of the weird Entrust MAC rather than a real 
	   error */
	status = readConstructed( stream, NULL, CTAG_PH_PROTECTIONALGO );
	if( cryptStatusError( status ) )
		return( status );
	streamPos = stell( stream );
	status = readAlgoIDex( stream, &cryptAlgo, &hashAlgo, &hashParam,
						   ALGOID_CLASS_PKCSIG );
	if( cryptStatusOK( status ) )
		{
		/* Make sure that it's a recognised signature algorithm to avoid
		   false positives if the other side sends some bizarre algorithm 
		   ID */
		if( !isSigAlgo( cryptAlgo ) )
			return( CRYPT_ERROR_NOTAVAIL );

		/* It's a recognised signature algorithm, use the CA certificate to 
		   verify it rather than the MAC */
		protocolInfo->useMACreceive = FALSE;
		protocolInfo->hashAlgo = hashAlgo;
		protocolInfo->hashParam = hashParam;

		return( CRYPT_OK );
		}
	ENSURES( cryptStatusError( status ) );

	/* It's nothing normal, it must be the Entrust MAC algorithm information, 
	   remember where it starts so that we can process it later */
	sClearError( stream );
	protocolInfo->macInfoPos = streamPos;
	status = readUniversal( stream );
	protocolInfo->useMACreceive = TRUE;

	return( status );
	}
Esempio n. 4
0
static int readCmsKek( INOUT STREAM *stream, 
					   OUT QUERY_INFO *queryInfo )
	{
	long value;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );

	/* Clear return value */
	memset( queryInfo, 0, sizeof( QUERY_INFO ) );

	/* Read the header */
	readConstructed( stream, NULL, CTAG_RI_KEKRI );
	status = readShortInteger( stream, &value );
	if( cryptStatusError( status ) )
		return( status );
	if( value != KEK_VERSION )
		return( CRYPT_ERROR_BADDATA );

	return( CRYPT_ERROR_NOTAVAIL );
	}
Esempio n. 5
0
static int readKeyDerivationInfo( INOUT STREAM *stream, 
								  OUT QUERY_INFO *queryInfo )
	{
	long endPos, value;
	int length, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );

	/* Clear return value */
	memset( queryInfo, 0, sizeof( QUERY_INFO ) );

	/* Read the outer wrapper and key derivation algorithm OID */
	readConstructed( stream, NULL, CTAG_KK_DA );
	status = readFixedOID( stream, OID_PBKDF2, sizeofOID( OID_PBKDF2 ) );
	if( cryptStatusError( status ) )
		return( status );

	/* Read the PBKDF2 parameters, limiting the salt and iteration count to
	   sane values */
	status = readSequence( stream, &length );
	if( cryptStatusError( status ) )
		return( status );
	endPos = stell( stream ) + length;
	readOctetString( stream, queryInfo->salt, &queryInfo->saltLength, 
					 2, CRYPT_MAX_HASHSIZE );
	status = readShortInteger( stream, &value );
	if( cryptStatusError( status ) )
		return( status );
	if( value < 1 || value > MAX_KEYSETUP_ITERATIONS )
		return( CRYPT_ERROR_BADDATA );
	queryInfo->keySetupIterations = ( int ) value;
	queryInfo->keySetupAlgo = CRYPT_ALGO_HMAC_SHA1;
	if( stell( stream ) < endPos && \
		sPeek( stream ) == BER_INTEGER )
		{
		/* There's an optional key length that may override the default 
		   key size present, read it.  Note that we compare the upper
		   bound to MAX_WORKING_KEYSIZE rather than CRYPT_MAX_KEYSIZE,
		   since this is a key used directly with an encryption algorithm
		   rather than a generic keying value that may be hashed down to 
		   size */
		status = readShortInteger( stream, &value );
		if( cryptStatusError( status ) )
			return( status );
		if( value < MIN_KEYSIZE || value > MAX_WORKING_KEYSIZE )
			return( CRYPT_ERROR_BADDATA );
		queryInfo->keySize = ( int ) value;
		}
	if( stell( stream ) < endPos )
		{
		CRYPT_ALGO_TYPE prfAlgo;
	
		/* There's a non-default hash algorithm ID present, read it */
		status = readAlgoID( stream, &prfAlgo, ALGOID_CLASS_HASH );
		if( cryptStatusError( status ) )
			return( status );
		queryInfo->keySetupAlgo = prfAlgo;
		}

	return( CRYPT_OK );
	}
static int readCertAttributes( INOUT STREAM *stream, 
							   INOUT PKCS15_INFO *pkcs15infoPtr,
							   IN_LENGTH const int endPos )
	{
	int length, status = CRYPT_OK;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) );

	REQUIRES( endPos > 0 && endPos > stell( stream ) && \
			  endPos < MAX_INTLENGTH );

	if( peekTag( stream ) == BER_BOOLEAN )			/* Authority flag */
		status = readUniversal( stream );
	if( canContinue( stream, status, endPos ) &&	/* Identifier */
		peekTag( stream ) == BER_SEQUENCE )
		status = readUniversal( stream );
	if( canContinue( stream, status, endPos ) &&	/* Thumbprint */
		peekTag( stream ) == MAKE_CTAG( CTAG_CA_DUMMY ) )
		status = readUniversal( stream );
	if( canContinue( stream, status, endPos ) &&	/* Trusted usage */
		peekTag( stream ) == MAKE_CTAG( CTAG_CA_TRUSTED_USAGE ) )
		{
		readConstructed( stream, NULL, CTAG_CA_TRUSTED_USAGE );
		status = readBitString( stream, &pkcs15infoPtr->trustedUsage );
		}
	if( canContinue( stream, status, endPos ) &&	/* Identifiers */
		peekTag( stream ) == MAKE_CTAG( CTAG_CA_IDENTIFIERS ) )
		{
		status = readConstructed( stream, &length, CTAG_CA_IDENTIFIERS );
		if( cryptStatusOK( status ) )
			status = readKeyIdentifiers( stream, pkcs15infoPtr, 
										 stell( stream ) + length );
		}
	if( canContinue( stream, status, endPos ) &&	/* Implicitly trusted */
		peekTag( stream ) == MAKE_CTAG_PRIMITIVE( CTAG_CA_TRUSTED_IMPLICIT ) )
		status = readBooleanTag( stream, &pkcs15infoPtr->implicitTrust,
								 CTAG_CA_TRUSTED_IMPLICIT );
	if( canContinue( stream, status, endPos ) &&	/* Validity */
		peekTag( stream ) == MAKE_CTAG( CTAG_CA_VALIDTO ) )
		{
		/* Due to miscommunication between PKCS #15 and 7816-15 there are 
		   two ways to encode the validity information for certificates, one 
		   based on the format used elsewhere in PKCS #15 (for PKCS #15) and 
		   the other based on the format used in certificates (for 7816-15).  
		   Luckily they can be distinguished by the tagging type */
		readConstructed( stream, NULL, CTAG_CA_VALIDTO );
		readUTCTime( stream, &pkcs15infoPtr->validFrom );
		status = readUTCTime( stream, &pkcs15infoPtr->validTo );
		}
	else
		{
		if( canContinue( stream, status, endPos ) &&	/* Start date */
			peekTag( stream ) == BER_TIME_GENERALIZED )
			status = readGeneralizedTime( stream, &pkcs15infoPtr->validFrom );
		if( canContinue( stream, status, endPos ) &&	/* End date */
			peekTag( stream ) == MAKE_CTAG_PRIMITIVE( CTAG_CA_VALIDTO ) )
			status = readGeneralizedTimeTag( stream, &pkcs15infoPtr->validTo,
											 CTAG_CA_VALIDTO );
		}

	return( status );
	}
Esempio n. 7
0
static int readRsaPrivateKey( INOUT STREAM *stream, 
							  INOUT CONTEXT_INFO *contextInfoPtr )
	{
	PKC_INFO *rsaKey = contextInfoPtr->ctxPKC;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );

	REQUIRES( contextInfoPtr->type == CONTEXT_PKC && \
			  contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_RSA );

	/* Read the header */
	status = readSequence( stream, NULL );
	if( cryptStatusOK( status ) && \
		peekTag( stream ) == MAKE_CTAG( 0 ) )
		{
		/* Erroneously written in older code */
		status = readConstructed( stream, NULL, 0 );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* Read the key components */
	if( peekTag( stream ) == MAKE_CTAG_PRIMITIVE( 0 ) )
		{
		/* The public components may already have been read when we read a
		   corresponding public key or certificate so we only read them if
		   they're not already present */
		if( BN_is_zero( &rsaKey->rsaParam_n ) && \
			BN_is_zero( &rsaKey->rsaParam_e ) )
			{
			status = readBignumTag( stream, &rsaKey->rsaParam_n, 
									RSAPARAM_MIN_N, RSAPARAM_MAX_N, 
									NULL, 0 );
			if( cryptStatusOK( status ) )
				{
				status = readBignumTag( stream, &rsaKey->rsaParam_e, 
										RSAPARAM_MIN_E, RSAPARAM_MAX_E, 
										&rsaKey->rsaParam_n, 1 );
				}
			}
		else
			{
			/* The key components are already present, skip them */
			REQUIRES( !BN_is_zero( &rsaKey->rsaParam_n ) && \
					  !BN_is_zero( &rsaKey->rsaParam_e ) );
			readUniversal( stream );
			status = readUniversal( stream );
			}
		if( cryptStatusError( status ) )
			return( status );
		}
	if( peekTag( stream ) == MAKE_CTAG_PRIMITIVE( 2 ) )
		{
		/* d isn't used so we skip it */
		status = readUniversal( stream );
		if( cryptStatusError( status ) )
			return( status );
		}
	status = readBignumTag( stream, &rsaKey->rsaParam_p, 
							RSAPARAM_MIN_P, RSAPARAM_MAX_P, 
							&rsaKey->rsaParam_n, 3 );
	if( cryptStatusOK( status ) )
		status = readBignumTag( stream, &rsaKey->rsaParam_q, 
								RSAPARAM_MIN_Q, RSAPARAM_MAX_Q, 
								&rsaKey->rsaParam_n, 4 );
	if( cryptStatusError( status ) )
		return( status );
	if( peekTag( stream ) == MAKE_CTAG_PRIMITIVE( 5 ) )
		{
		status = readBignumTag( stream, &rsaKey->rsaParam_exponent1, 
								RSAPARAM_MIN_EXP1, RSAPARAM_MAX_EXP1, 
								&rsaKey->rsaParam_n, 5 );
		if( cryptStatusOK( status ) )
			status = readBignumTag( stream, &rsaKey->rsaParam_exponent2, 
									RSAPARAM_MIN_EXP2, RSAPARAM_MAX_EXP2, 
									&rsaKey->rsaParam_n, 6 );
		if( cryptStatusOK( status ) )
			status = readBignumTag( stream, &rsaKey->rsaParam_u, 
									RSAPARAM_MIN_U, RSAPARAM_MAX_U, 
									&rsaKey->rsaParam_n, 7 );
		}
	return( status );
	}
Esempio n. 8
0
static int readRsaPrivateKeyOld( INOUT STREAM *stream, 
								 INOUT CONTEXT_INFO *contextInfoPtr )
	{
	CRYPT_ALGO_TYPE cryptAlgo;
	PKC_INFO *rsaKey = contextInfoPtr->ctxPKC;
	const int startPos = stell( stream );
	int length, endPos, iterationCount, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );

	REQUIRES( contextInfoPtr->type == CONTEXT_PKC && \
			  contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_RSA );

	/* Skip the PKCS #8 wrapper.  When we read the OCTET STRING 
	   encapsulation we use MIN_PKCSIZE_THRESHOLD rather than MIN_PKCSIZE
	   so that a too-short key will get to readBignumChecked(), which
	   returns an appropriate error code */
	readSequence( stream, &length );	/* Outer wrapper */
	readShortInteger( stream, NULL );	/* Version */
	status = readAlgoID( stream, &cryptAlgo, ALGOID_CLASS_PKC );
	if( cryptStatusError( status ) || cryptAlgo != CRYPT_ALGO_RSA )
		return( CRYPT_ERROR_BADDATA );
	status = readOctetStringHole( stream, NULL, 
								  ( 2 * MIN_PKCSIZE_THRESHOLD ) + \
									( 5 * ( MIN_PKCSIZE_THRESHOLD / 2 ) ), 
								  DEFAULT_TAG );
	if( cryptStatusError( status ) )	/* OCTET STRING encapsulation */
		return( status );

	/* Read the header */
	readSequence( stream, NULL );
	status = readShortInteger( stream, NULL );
	if( cryptStatusError( status ) )
		return( status );

	/* Read the RSA key components, skipping n and e if we've already got 
	   them via the associated public key/certificate */
	if( BN_is_zero( &rsaKey->rsaParam_n ) )
		{
		status = readBignumChecked( stream, &rsaKey->rsaParam_n,
									RSAPARAM_MIN_N, RSAPARAM_MAX_N, 
									NULL );
		if( cryptStatusOK( status ) )
			status = readBignum( stream, &rsaKey->rsaParam_e,
								 RSAPARAM_MIN_E, RSAPARAM_MAX_E,
								 &rsaKey->rsaParam_n );
		}
	else
		{
		readUniversal( stream );
		status = readUniversal( stream );
		}
	if( cryptStatusOK( status ) )
		{
		/* d isn't used so we skip it */
		status = readUniversal( stream );
		}
	if( cryptStatusOK( status ) )
		status = readBignum( stream, &rsaKey->rsaParam_p,
							 RSAPARAM_MIN_P, RSAPARAM_MAX_P,
							 &rsaKey->rsaParam_n );
	if( cryptStatusOK( status ) )
		status = readBignum( stream, &rsaKey->rsaParam_q,
							 RSAPARAM_MIN_Q, RSAPARAM_MAX_Q,
							 &rsaKey->rsaParam_n );
	if( cryptStatusOK( status ) )
		status = readBignum( stream, &rsaKey->rsaParam_exponent1,
							 RSAPARAM_MIN_EXP1, RSAPARAM_MAX_EXP1,
							 &rsaKey->rsaParam_n );
	if( cryptStatusOK( status ) )
		status = readBignum( stream, &rsaKey->rsaParam_exponent2,
							 RSAPARAM_MIN_EXP2, RSAPARAM_MAX_EXP2,
							 &rsaKey->rsaParam_n );
	if( cryptStatusOK( status ) )
		status = readBignum( stream, &rsaKey->rsaParam_u,
							 RSAPARAM_MIN_U, RSAPARAM_MAX_U,
							 &rsaKey->rsaParam_n );
	if( cryptStatusError( status ) )
		return( status );

	/* Check whether there are any attributes present */
	if( stell( stream ) >= startPos + length )
		return( CRYPT_OK );

	/* Read the attribute wrapper */
	status = readConstructed( stream, &length, 0 );
	if( cryptStatusError( status ) )
		return( status );
	endPos = stell( stream ) + length;

	/* Read the collection of attributes.  Unlike any other key-storage 
	   format, PKCS #8 stores the key usage information as an X.509 
	   attribute alongside the encrypted private key data so we have to
	   process whatever attributes may be present in order to find the
	   keyUsage (if there is any) in order to set the object action 
	   permissions */
	for( iterationCount = 0;
		 stell( stream ) < endPos && \
			iterationCount < FAILSAFE_ITERATIONS_MED;
		 iterationCount++ )
		{
		BYTE oid[ MAX_OID_SIZE + 8 ];
		int oidLength, actionFlags, value;

		/* Read the attribute.  Since there's only one attribute type that 
		   we can 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, &oidLength, 
								 BER_OBJECT_IDENTIFIER );
		if( cryptStatusError( status ) )
			return( status );

		/* If it's not a key-usage attribute, we can't do much with it */
		if( oidLength != sizeofOID( OID_X509_KEYUSAGE ) || \
			memcmp( oid, OID_X509_KEYUSAGE, oidLength ) )
			{
			status = readUniversal( stream );
			if( cryptStatusError( status ) )
				return( status );
			continue;
			}

		/* Read the keyUsage attribute and convert it into cryptlib action 
		   permissions */
		readSet( stream, NULL );
		status = readBitString( stream, &value );
		if( cryptStatusError( status ) )
			return( status );
		actionFlags = ACTION_PERM_NONE;
		if( value & ( KEYUSAGE_SIGN | KEYUSAGE_CA ) )
			{
			actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_SIGN, \
										   ACTION_PERM_ALL ) | \
						   MK_ACTION_PERM( MESSAGE_CTX_SIGCHECK, \
										   ACTION_PERM_ALL );
			}
		if( value & KEYUSAGE_CRYPT )
			{
			actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_ENCRYPT, \
										   ACTION_PERM_ALL ) | \
						   MK_ACTION_PERM( MESSAGE_CTX_DECRYPT, \
										   ACTION_PERM_ALL );
			}
#if 0	/* 11/6/13 Windows sets these flags to what are effectively
				   gibberish values (dataEncipherment for a signing key,
				   digitalSignature for an encryption key) so in order
				   to be able to use the key we have to ignore the keyUsage 
				   settings, in the same way that every other application 
				   seems to */
		if( actionFlags == ACTION_PERM_NONE )
			return( CRYPT_ERROR_NOTAVAIL );
		status = krnlSendMessage( contextInfoPtr->objectHandle, 
								  IMESSAGE_SETATTRIBUTE, &actionFlags, 
								  CRYPT_IATTRIBUTE_ACTIONPERMS );
		if( cryptStatusError( status ) )
			return( status );
#else
		assert( actionFlags != ACTION_PERM_NONE );	/* Warn in debug mode */
#endif /* 0 */
		}
	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );

	return( CRYPT_OK );
	}
Esempio n. 9
0
static int readPkiHeader( INOUT STREAM *stream, 
						  INOUT CMP_PROTOCOL_INFO *protocolInfo,
						  INOUT ERROR_INFO *errorInfo,
						  const BOOLEAN isServerInitialMessage )
	{
	int length, endPos, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
	assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) );

	/* Clear per-message state information */
	protocolInfo->userIDchanged = protocolInfo->certIDchanged = \
		protocolInfo->useMACreceive = FALSE;
	protocolInfo->macInfoPos = CRYPT_ERROR;
	protocolInfo->senderDNPtr = NULL;
	protocolInfo->senderDNlength = 0;
	protocolInfo->headerRead = FALSE;

	/* Read the wrapper and skip the static information, which matches what 
	   we sent and is protected by the MAC so there's little point in 
	   looking at it */
	status = readSequence( stream, &length );
	if( cryptStatusError( status ) )
		return( status );
	endPos = stell( stream ) + length;
	readShortInteger( stream, NULL );		/* Version */
	if( !protocolInfo->isCryptlib )
		{
		/* The ID of the key used for integrity protection (or in general
		   the identity of the sender) can be specified either as the sender
		   DN or the senderKID or both, or in some cases even indirectly via
		   the transaction ID.  With no real guidance as to which one to 
		   use, implementors are using any of these options to identify the 
		   key.  Since we need to check that the integrity-protection key 
		   that we're using is correct so that we can report a more 
		   appropriate error than bad signature or bad data, we need to 
		   remember the sender DN for later in case this is the only form of 
		   key identification provided.  Unfortunately since the sender DN 
		   can't uniquely identify a certificate, if this is the only 
		   identifier that we're given then the caller can still get a bad 
		   signature error, yet another one of CMPs many wonderful features */
		status = readConstructed( stream, &protocolInfo->senderDNlength, 4 );
		if( cryptStatusOK( status ) && protocolInfo->senderDNlength > 0 )
			{
			status = sMemGetDataBlock( stream, &protocolInfo->senderDNPtr, 
									   protocolInfo->senderDNlength );
			if( cryptStatusOK( status ) )
				status = readUniversal( stream );
			}
		}
	else
		{
		/* cryptlib includes a proper certID so the whole signer
		   identification mess is avoided and we can ignore the sender DN */
		status = readUniversal( stream );	/* Sender DN */
		}
	if( cryptStatusOK( status ) )
		status = readUniversal( stream );	/* Recipient DN */
	if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_MESSAGETIME ) )
		status = readUniversal( stream );	/* Message time */
	if( cryptStatusError( status ) )
		{
		retExt( CRYPT_ERROR_BADDATA, 
				( CRYPT_ERROR_BADDATA, errorInfo, 
				  "Invalid DN information in PKI header" ) );
		}
	if( peekTag( stream ) != MAKE_CTAG( CTAG_PH_PROTECTIONALGO ) )
		{
		/* The message was sent without integrity protection, report it as
		   a signature error rather than the generic bad data error that
		   we'd get from the following read */
		retExt( CRYPT_ERROR_SIGNATURE,
				( CRYPT_ERROR_SIGNATURE, errorInfo, 
				  "Message was sent without integrity protection" ) );
		}
	status = readProtectionAlgo( stream, protocolInfo );
	if( cryptStatusError( status ) )
		{
		retExt( status,
				( status, errorInfo, 
				  "Invalid integrity protection information in PKI "
				  "header" ) );
		}
	if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_SENDERKID ) )
		{								/* Sender protection keyID */
		status = readUserID( stream, protocolInfo );
		if( cryptStatusError( status ) )
			{
			retExt( status,
					( status, errorInfo, 
					  "Invalid PKI user ID in PKI header" ) );
			}
		}
	else
		{
		/* If we're the server, the client must provide a PKI user ID in the
		   first message unless we got one in an earlier transaction */
		if( isServerInitialMessage && protocolInfo->userIDsize <= 0 )
			{
			retExt( CRYPT_ERROR_BADDATA, 
					( CRYPT_ERROR_BADDATA, errorInfo, 
					  "Missing PKI user ID in PKI header" ) );
			}
		}
	if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_RECIPKID ) )
		readUniversal( stream );			/* Recipient protection keyID */

	/* Record the transaction ID (which is effectively the nonce) or make 
	   sure that it matches the one that we sent.  There's no real need to 
	   do an explicit duplicate check since a replay attempt will be 
	   rejected as a duplicate by the certificate store and the locking 
	   performed at that level makes it a much better place to catch 
	   duplicates, but we do it anyway because it doesn't cost anything and
	   we can catch at least some problems a bit earlier */
	status = readConstructed( stream, NULL, CTAG_PH_TRANSACTIONID );
	if( cryptStatusError( status ) )
		{
		retExt( status, 
				( status, errorInfo, 
				  "Missing transaction ID in PKI header" ) );
		}
	status = readTransactionID( stream, protocolInfo, 
								isServerInitialMessage );
	if( cryptStatusError( status ) )
		{
		protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE;
		retExt( status, 
				( status, errorInfo, 
				  ( status == CRYPT_ERROR_SIGNATURE ) ? \
				  "Returned message transaction ID doesn't match our "
						"transaction ID" : \
				  "Invalid transaction ID in PKI header" ) );
		}

	/* Read the sender nonce, which becomes the new recipient nonce, and skip
	   the recipient nonce if there's one present.  These values may be
	   absent, either because the other side doesn't implement them or
	   because they're not available, for example because it's sending a
	   response to an error that occurred before it could read the nonce from
	   a request.  In any case we don't bother checking the nonce values
	   since the transaction ID serves the same purpose */
	if( stell( stream ) < endPos && \
		peekTag( stream ) == MAKE_CTAG( CTAG_PH_SENDERNONCE ) )
		{
		readConstructed( stream, NULL, CTAG_PH_SENDERNONCE );
		status = readOctetString( stream, protocolInfo->recipNonce,
								  &protocolInfo->recipNonceSize,
								  4, CRYPT_MAX_HASHSIZE );
		if( cryptStatusError( status ) )
			{
			protocolInfo->pkiFailInfo = CMPFAILINFO_BADSENDERNONCE;
			retExt( status,
					( status, errorInfo, 
					  "Invalid sender nonce in PKI header" ) );
			}
		}
	if( stell( stream ) < endPos && \
		peekTag( stream ) == MAKE_CTAG( CTAG_PH_RECIPNONCE ) )
		{
		readConstructed( stream, NULL, CTAG_PH_RECIPNONCE );
		status = readUniversal( stream );
		if( cryptStatusError( status ) )
			{
			protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE;
			retExt( status,
					( status, errorInfo, 
					  "Invalid recipient nonce in PKI header" ) );
			}
		}

	/* Remember that we've successfully read enough of the header 
	   information to generate a response */
	protocolInfo->headerRead = TRUE;

	/* Skip any further junk and process the general information if there is 
	   any */
	if( stell( stream ) < endPos && \
		peekTag( stream ) == MAKE_CTAG( CTAG_PH_FREETEXT ) )
		{
		status = readUniversal( stream );	/* Junk */
		if( cryptStatusError( status ) )
			return( status );
		}
	if( stell( stream ) < endPos && \
		peekTag( stream ) == MAKE_CTAG( CTAG_PH_GENERALINFO ) )
		{
		status = readGeneralInfo( stream, protocolInfo );
		if( cryptStatusError( status ) )
			{
			retExt( status,
					( status, errorInfo, 
					  "Invalid generalInfo information in PKI header" ) );
			}
		}

	return( CRYPT_OK );
	}