📄 cmp_rd.c
字号:
/****************************************************************************
* *
* Read CMP Messages *
* Copyright Peter Gutmann 1999-2003 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "asn1_rw.h"
#include "asn1s_rw.h"
#include "session.h"
#include "cmp.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../misc/asn1_rw.h"
#include "../misc/asn1s_rw.h"
#include "../session/session.h"
#include "../session/cmp.h"
#else
#include "crypt.h"
#include "misc/asn1_rw.h"
#include "misc/asn1s_rw.h"
#include "session/session.h"
#include "session/cmp.h"
#endif /* Compiler-specific includes */
/* Prototypes for functions in lib_sign.c */
int checkRawSignature( const void *signature, const int signatureLength,
const CRYPT_CONTEXT iSigCheckContext,
const CRYPT_CONTEXT iHashContext );
#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,
void *errorInfo )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
BYTE salt[ CRYPT_MAX_HASHSIZE ];
long value;
int saltLength, iterations, status;
/* Read the various parameter fields */
readSequence( stream, NULL );
status = readOID( stream, 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( errorInfo, status, "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, CRYPT_MAX_HASHSIZE );
readUniversal( stream ); /* pwHashAlgo */
readShortInteger( stream, &value );
status = readUniversal( stream ); /* macAlgo */
if( cryptStatusError( status ) )
retExt( errorInfo, status, "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( errorInfo, CRYPT_ERROR_BADDATA,
"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( errorInfo, status,
"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( errorInfo, status,
"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 readEncryptedCert( STREAM *stream,
const CRYPT_CONTEXT iImportContext,
void *errorInfo )
{
CRYPT_CONTEXT iSessionKey;
MECHANISM_WRAP_INFO mechanismInfo;
QUERY_INFO queryInfo;
BYTE *encKeyPtr;
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( errorInfo, status,
"Invalid encrypted certificate CEK algorithm" );
status = readBitStringHole( stream, &encKeyLength, CTAG_EV_ENCCEK );
if( cryptStatusOK( status ) && /* Encrypted CEK */
( encKeyLength < 56 || encKeyLength > CRYPT_MAX_PKCSIZE ) )
status = CRYPT_ERROR_OVERFLOW;
if( cryptStatusOK( status ) )
{
encKeyPtr = sMemBufPtr( stream );
sSkip( stream, encKeyLength );
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, &encCertLength, DEFAULT_TAG );
}
if( cryptStatusOK( status ) && /* Encrypted cert */
( encCertLength < 128 || encCertLength > 8192 ) )
status = CRYPT_ERROR_BADDATA;
if( cryptStatusOK( status ) && \
encCertLength > sMemDataLeft( stream ) )
return( CRYPT_ERROR_UNDERFLOW );
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 */
krnlSendMessage( iSessionKey, IMESSAGE_GETATTRIBUTE, &blockSize,
CRYPT_CTXINFO_BLOCKSIZE );
if( queryInfo.size % blockSize )
status = CRYPT_ERROR_BADDATA;
}
if( cryptStatusError( status ) )
{
krnlSendNotifier( iSessionKey, IMESSAGE_DECREFCOUNT );
retExt( errorInfo, status,
"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,
CRYPT_UNUSED );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_IMPORT,
&mechanismInfo, MECHANISM_PKCS1 );
clearMechanismInfo( &mechanismInfo );
if( cryptStatusError( status ) )
{
krnlSendNotifier( iSessionKey, IMESSAGE_DECREFCOUNT );
retExt( errorInfo, status,
"Couldn't decrypt encrypted certificate CEK" );
}
/* Decrypt the returned cert */
status = krnlSendMessage( iSessionKey, IMESSAGE_CTX_DECRYPT,
sMemBufPtr( stream ), encCertLength );
krnlSendNotifier( iSessionKey, IMESSAGE_DECREFCOUNT );
if( cryptStatusError( status ) )
retExt( errorInfo, status,
"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, 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 )
{
BYTE oid[ MAX_OID_SIZE ];
/* 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 = readRawObject( stream, oid, &length, MAX_OID_SIZE,
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,
CRYPT_MAX_HASHSIZE );
if( cryptStatusOK( status ) && \
protocolInfo->certIDsize != KEYID_SIZE )
status = CRYPT_ERROR_BADDATA;
if( cryptStatusError( status ) )
continue;
protocolInfo->certIDchanged = TRUE;
if( stell( stream ) < endPos )
/* Skip the issuerSerial if there's one present. We can't
really do much with it in this form without rewriting it
into the standard issuerAndSerialNumber form, but in any
case we don't need it because we've already got the cert
ID */
status = readUniversal( stream );
continue;
}
/* It's something that we don't recognise, skip it */
status = readUniversal( stream );
}
return( status );
}
#endif /* USE_CMP */
/****************************************************************************
* *
* Read Status Info *
* *
****************************************************************************/
/* The following code is shared between CMP and TSP due to TSP's use of
random elements cut & pasted from CMP without any real understanding of
their function or semantics */
#if defined( USE_CMP ) || defined( USE_TSP )
/* Map a PKI failure info value to an error string */
static char *getFailureString( const int value )
{
static const FAR_BSS char *failureStrings[] = {
"Unrecognized or unsupported Algorithm Identifier",
"The integrity check failed (e.g. signature did not verify)",
"This transaction is not permitted or supported",
"The messageTime was not sufficiently close to the system time as "
"defined by local policy",
"No certificate could be found matching the provided criteria",
"The data submitted has the wrong format",
"The authority indicated in the request is different from the one "
"creating the response token",
"The requester's data is incorrect (used for notary services)",
"Timestamp is missing but should be there (by policy)",
"The proof-of-possession failed",
"The certificate has already been revoked",
"The certificate has already been confirmed",
"Invalid integrity, password based instead of signature or vice "
"versa",
"Invalid recipient nonce, either missing or wrong value",
"The TSA's time source is not available",
"The requested TSA policy is not supported by the TSA",
"The requested extension is not supported by the TSA",
"The additional information requested could not be understood or is "
"not available",
"Invalid sender nonce, either missing or wrong size",
"Invalid certificate template or missing mandatory information",
"Signer of the message unknown or not trusted",
"The transaction identifier is already in use",
"The version of the message is not supported",
"The sender was not authorized to make the preceding request or "
"perform the preceding action",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -