📄 cmp.c
字号:
}
/* Read a PKI header and make sure it matches the header we sent (for EE
or non-initial CA/RA messages) or set up the EE information in response
to an initial message (for an initial CA/RA message). We ignore all the
redundant fields in the header which don't directly affect the protocol,
based on the results of CMP interop testing this appears to be standard
practice among implementors (it also helps get around problems with
implementations which get the fields wrong, since most of the fields
aren't generally useful it doesn't affect the processing while making the
code more tolerant of implementation errors) */
static int readPkiHeader( STREAM *stream, CMP_PROTOCOL_INFO *protocolInfo,
const BOOLEAN isInitialMessage )
{
CRYPT_ALGO cryptAlgo, hashAlgo;
BYTE buffer[ CRYPT_MAX_HASHSIZE ];
int length, streamPos, endPos, status;
/* Clear per-message state information */
protocolInfo->macInfoPos = protocolInfo->authKeyIDpos = CRYPT_ERROR;
protocolInfo->authKeyIDlength = 0;
/* Read the wrapper and skip the static info, which matches what we sent
and is protected by the MAC so there's little point in looking at
it */
readSequence( stream, &length );
endPos = stell( stream ) + length;
readShortInteger( stream, NULL ); /* Version */
if( !protocolInfo->isCryptlib )
{
/* The ID of the key used for integrity protection can be specified
either as the sender DN or the senderKID or both, with no real
guidance as to which one to use implementors are using any of
these three options to identify the key. Since we need to check
that the integrity-protection key we're using is correct so 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. If all we've got
is the sender DN and it doesn't uniquely identify a cert the
caller will get an inappropriate error when we try and verify the
signature, but that's yet another CMP flaw which we can't really
fix */
readConstructed( stream, &protocolInfo->authKeyIDlength, 4 );
protocolInfo->authKeyIDpos = stell( stream );
readUniversal( stream ); /* Sender DN */
}
else
/* cryptlib includes a proper certID so the whole signer
identification mess is avoided and we can ignore the sender DN */
readUniversal( stream ); /* Sender DN */
readUniversal( stream ); /* Recipient */
if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_MESSAGETIME ) )
readUniversal( stream ); /* Message time */
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 we'd
get from the following read */
status = CRYPT_ERROR_SIGNATURE;
else
status = readConstructed( stream, NULL, CTAG_PH_PROTECTIONALGO );
if( cryptStatusError( status ) )
/* If there was a problem we should exit now since an error status
from the following readAlgoIDex() is interpreted to indicate the
presence of the weird Entrust MAC rather than a real error */
return( status );
streamPos = ( int ) stell( stream );
status = readAlgoIDex( stream, &cryptAlgo, &hashAlgo, NULL );
if( cryptStatusOK( status ) )
{
/* It's a known signature algorithm, use the CA cert to verify it
rather than the MAC */
protocolInfo->useMACreceive = FALSE;
protocolInfo->hashAlgo = hashAlgo;
}
else
{
/* It's nothing normal, it must be the Entrust MAC algorithm info,
remember where it starts so we can process it later */
sClearError( stream );
protocolInfo->macInfoPos = streamPos;
readUniversal( stream );
protocolInfo->useMACreceive = TRUE;
}
if( isInitialMessage )
{
/* Read the keyID which we'll need to figure out how to handle the
integrity protection on the message */
readConstructed( stream, NULL, CTAG_PH_SENDERKID );
status = readOctetString( stream, protocolInfo->userID,
&protocolInfo->userIDsize,
CRYPT_MAX_HASHSIZE );
if( cryptStatusError( status ) )
return( status );
}
else
if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_SENDERKID ) )
readUniversal( stream ); /* Sender protection keyID */
if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_RECIPKID ) )
readUniversal( stream ); /* Recipient protection keyID */
/* Record the transaction ID / make sure it matches the one we sent.
There's no need to do an explicit duplicate check since a replay
attempt will be rejected as a duplicate by the cert store, and the
locking performed at that level makes it a much better place to catch
duplicates */
readConstructed( stream, NULL, CTAG_PH_TRANSACTIONID );
if( isInitialMessage )
/* This is the first message, record the transaction ID for later */
status = readOctetString( stream, protocolInfo->transID,
&protocolInfo->transIDsize,
CRYPT_MAX_HASHSIZE );
else
{
/* Make sure the transaction ID for this message matches the recorded
value (the bad recipient nonce error code is the best we can
provide here) */
status = readOctetString( stream, buffer, &length,
CRYPT_MAX_HASHSIZE );
if( cryptStatusOK( status ) && \
( protocolInfo->transIDsize != length || \
memcmp( protocolInfo->transID, buffer, length ) ) )
{
protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE;
status = CRYPT_ERROR_BADDATA;
}
#ifdef SKIP_IO
puts( "CMP: Bypassing nonce check." );
status = CRYPT_OK;
#endif /* SKIP_IO */
}
if( cryptStatusError( status ) )
return( status );
/* Read the sender nonce, which becomes the new recipient nonce, and skip
the recipient nonce if there's one present. We don't bother checking
the nonces since the transaction ID serves the same purpose */
readConstructed( stream, NULL, CTAG_PH_SENDERNONCE );
status = readOctetString( stream, protocolInfo->recipNonce,
&protocolInfo->recipNonceSize,
CRYPT_MAX_HASHSIZE );
if( cryptStatusError( status ) )
protocolInfo->pkiFailInfo = CMPFAILINFO_BADSENDERNONCE;
else
if( !isInitialMessage )
{
readConstructed( stream, NULL, CTAG_PH_RECIPNONCE );
status = readUniversal( stream ); /* Recipient nonce */
if( cryptStatusError( status ) )
protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE;
}
if( cryptStatusError( status ) )
return( status );
/* Generate a new sender nonce and see if there's anything useful present
in the general info. Note that since cryptlib nonces are based on a
hash of the time they're guessable, but since they're entirely
redundant anyway this isn't a problem, all we need is a nonrepeating
value to keep the other side happy */
getNonce( protocolInfo->senderNonce, protocolInfo->senderNonceSize );
if( stell( stream ) < endPos && \
peekTag( stream ) == MAKE_CTAG( CTAG_PH_FREETEXT ) )
readUniversal( stream ); /* Junk */
if( stell( stream ) < endPos && \
peekTag( stream ) == MAKE_CTAG( CTAG_PH_GENERALINFO ) )
{
int generalInfoEndPos = ( int ) stell( stream );
/* There's general info present, some of this may contain cryptlib-
provided data so we go through the various attributes looking for
anything we can use */
readConstructed( stream, NULL, CTAG_PH_GENERALINFO );
readSequence( stream, &length );
generalInfoEndPos += length;
while( stell( stream ) < generalInfoEndPos )
{
BYTE oid[ MAX_OID_SIZE ];
/* Read the attribute. Since there are only two attribute types
which we use, we hardcode the read in here rather than
performing a general-purpose attribute read */
readSequence( stream, NULL );
status = readRawObject( stream, oid, &length, MAX_OID_SIZE,
BER_OBJECT_IDENTIFIER );
if( cryptStatusError( status ) )
return( status );
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;
else
if( length == sizeofOID( OID_ESS_CERTID ) && \
!memcmp( oid, OID_ESS_CERTID, length ) )
{
/* Extract the issuerAndSerialNumber from the cert ID.
This also provides an optional cert hash, but we
don't use this at the moment */
readSet( stream, NULL );
readSequence( stream, NULL );
readSequence( stream, NULL );
if( peekTag( stream ) == BER_OCTETSTRING )
readUniversal( stream );
protocolInfo->authKeyIDpos = ( int ) stell( stream );
readSequence( stream, &length );
protocolInfo->authKeyIDlength = \
( int ) sizeofObject( length );
sSkip( stream, length );
continue;
}
readUniversal( stream );
}
}
if( stell( stream ) < endPos )
sseek( stream, endPos );
return( sGetStatus( stream ) );
}
/* Write a PKI request into a session's send buffer */
static int writePkiMessage( SESSION_INFO *sessionInfoPtr,
CMP_PROTOCOL_INFO *protocolInfo,
const CMPBODY_TYPE bodyType )
{
BYTE protInfo[ CRYPT_MAX_PKCSIZE * 2 ], headerBuffer[ 8 ];
STREAM stream;
int headerSize, protInfoSize, status;
/* Write the header and payload so we can MAC/sign it */
sMemOpen( &stream, sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufSize );
status = writePkiHeader( &stream, sessionInfoPtr, protocolInfo );
if( cryptStatusOK( status ) )
{
switch( bodyType )
{
case CMPBODY_NORMAL:
if( sessionInfoPtr->flags & SESSION_ISSERVER )
status = writeResponseBody( &stream, sessionInfoPtr,
protocolInfo );
else
status = writeRequestBody( &stream, sessionInfoPtr,
protocolInfo );
break;
case CMPBODY_CONFIRMATION:
status = writeConfBody( &stream, sessionInfoPtr,
protocolInfo );
break;
case CMPBODY_ACK:
writeConstructed( &stream, objSize( sizeofNull() ),
CTAG_PB_PKICONF );
writeSequence( &stream, sizeofNull() );
writeNull( &stream, DEFAULT_TAG );
break;
case CMPBODY_ERROR:
status = writeErrorBody( &stream, sessionInfoPtr,
protocolInfo );
break;
default:
assert( NOTREACHED );
}
}
if( cryptStatusError( status ) )
{
sMemClose( &stream );
return( status );
}
/* Generate the MAC or signature as appropriate */
if( protocolInfo->useMACsend )
{
BYTE macValue[ CRYPT_MAX_HASHSIZE ];
status = hashMessageContents( protocolInfo->iMacContext,
sessionInfoPtr->receiveBuffer, stell( &stream ) );
if( cryptStatusOK( status ) )
{
RESOURCE_DATA msgData;
setResourceData( &msgData, macValue, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( protocolInfo->iMacContext,
RESOURCE_IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CTXINFO_HASHVALUE );
protInfoSize = msgData.length;
}
if( cryptStatusOK( status ) )
{
STREAM macStream;
/* Write the MAC value with BIT STRING encapsulation */
sMemOpen( &macStream, protInfo, CRYPT_MAX_PKCSIZE * 2 );
writeTag( &macStream, BER_BITSTRING );
writeLength( &macStream, protInfoSize + 1 );/* +1 for bitstring */
sputc( &macStream, 0 );
swrite( &macStream, macValue, protInfoSize );
protInfoSize = stell( &macStream );
sMemDisconnect( &macStream );
}
}
else
{
MESSAGE_CREATEOBJECT_INFO createInfo;
/* Hash the data and create the signature */
setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_SHA );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
RESOURCE_IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CONTEXT );
if( cryptStatusOK( status ) )
{
status = hashMessageContents( createInfo.cryptHandle,
sessionInfoPtr->receiveBuffer, stell( &stream ) );
if( cryptStatusOK( status ) )
status = createRawSignature( protInfo, &protInfoSize,
sessionInfoPtr->privateKey,
createInfo.cryptHandle );
krnlSendNotifier( createInfo.cryptHandle, RESOURCE_IMESSAGE_DECREFCOUNT );
}
}
if( cryptStatusError( status ) )
{
sMemClose( &stream );
return( status );
}
/* Attach the MAC/signature to the payload */
writeConstructed( &stream, protInfoSize, CTAG_PM_PROTECTION );
swrite( &stream, protInfo, protInfoSize );
sessionInfoPtr->receiveBufEnd = stell( &stream );
status = sGetStatus( &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -