static int readTransactionID( INOUT STREAM *stream, INOUT CMP_PROTOCOL_INFO *protocolInfo, const BOOLEAN isServerInitialMessage ) { BYTE buffer[ CRYPT_MAX_HASHSIZE + 8 ]; int length, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) ); /* If this is the first message and we're the server, record the transaction ID for later */ if( isServerInitialMessage ) { status = readOctetString( stream, protocolInfo->transID, &protocolInfo->transIDsize, 4, CRYPT_MAX_HASHSIZE ); if( cryptStatusError( status ) ) return( status ); DEBUG_PRINT(( "%s: Read initial transID.\n", protocolInfo->isServer ? "SVR" : "CLI" )); DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI", protocolInfo->transID, protocolInfo->transIDsize ); return( CRYPT_OK ); } /* Make sure that the transaction ID for this message matches the recorded value (the bad signature error code is the best that we can provide here) */ status = readOctetString( stream, buffer, &length, 4, CRYPT_MAX_HASHSIZE ); if( cryptStatusError( status ) ) return( status ); ANALYSER_HINT( length >= 4 && length <= CRYPT_MAX_HASHSIZE ); DEBUG_PRINT(( "%s: Read transID.\n", protocolInfo->isServer ? "SVR" : "CLI" )); DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI", protocolInfo->transID, protocolInfo->transIDsize ); if( protocolInfo->transIDsize != length || \ memcmp( protocolInfo->transID, buffer, length ) ) return( CRYPT_ERROR_SIGNATURE ); return( CRYPT_OK ); }
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 ); }
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 ); }
int status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( salt, saltMaxLen ) ); assert( isWritePtr( saltLen, sizeof( int ) ) ); assert( isWritePtr( iterations, sizeof( int ) ) ); REQUIRES( saltMaxLen >= 16 && saltMaxLen < MAX_INTLENGTH_SHORT ); /* Clear return values */ memset( salt, 0, min( 16, saltMaxLen ) ); *saltLen = *iterations = 0; /* Read the wrapper and salt value */ readSequence( stream, NULL ); status = readOctetString( stream, salt, saltLen, 1, saltMaxLen ); if( cryptStatusError( status ) ) return( status ); /* Read the iteration count and make sure that it's within a sensible range */ status = readShortInteger( stream, &intValue ); if( cryptStatusError( status ) ) return( status ); if( intValue < 1 || intValue > MAX_KEYSETUP_ITERATIONS ) return( CRYPT_ERROR_BADDATA ); *iterations = ( int ) intValue; return( CRYPT_OK ); }
static int readKeyIdentifiers( INOUT STREAM *stream, INOUT PKCS15_INFO *pkcs15infoPtr, IN_LENGTH const int endPos ) { int iterationCount, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) ); REQUIRES( endPos > 0 && endPos > stell( stream ) && \ endPos < MAX_INTLENGTH ); for( status = CRYPT_OK, iterationCount = 0; cryptStatusOK( status ) && stell( stream ) < endPos && \ iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ ) { long value; int payloadLength; /* Read each identifier type and copy the useful ones into the PKCS #15 information */ readSequence( stream, &payloadLength ); status = readShortInteger( stream, &value ); if( cryptStatusError( status ) ) return( status ); switch( value ) { case PKCS15_KEYID_ISSUERANDSERIALNUMBER: { HASHFUNCTION_ATOMIC hashFunctionAtomic; void *iAndSPtr = DUMMY_INIT_PTR; int iAndSLength, hashSize; /* If we've already got the iAndSID, use that version instead */ if( pkcs15infoPtr->iAndSIDlength > 0 ) { status = readUniversal( stream ); continue; } /* Hash the full issuerAndSerialNumber to get an iAndSID */ getHashAtomicParameters( CRYPT_ALGO_SHA1, 0, &hashFunctionAtomic, &hashSize ); status = getStreamObjectLength( stream, &iAndSLength ); if( cryptStatusOK( status ) ) status = sMemGetDataBlock( stream, &iAndSPtr, iAndSLength ); if( cryptStatusOK( status ) ) status = sSkip( stream, iAndSLength ); if( cryptStatusError( status ) ) return( status ); hashFunctionAtomic( pkcs15infoPtr->iAndSID, KEYID_SIZE, iAndSPtr, iAndSLength ); pkcs15infoPtr->iAndSIDlength = hashSize; break; } case PKCS15_KEYID_SUBJECTKEYIDENTIFIER: status = readOctetString( stream, pkcs15infoPtr->keyID, &pkcs15infoPtr->keyIDlength, 8, CRYPT_MAX_HASHSIZE ); break; case PKCS15_KEYID_ISSUERANDSERIALNUMBERHASH: /* If we've already got the iAndSID by hashing the issuerAndSerialNumber, use that version instead */ if( pkcs15infoPtr->iAndSIDlength > 0 ) { status = readUniversal( stream ); continue; } status = readOctetString( stream, pkcs15infoPtr->iAndSID, &pkcs15infoPtr->iAndSIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_ISSUERNAMEHASH: status = readOctetString( stream, pkcs15infoPtr->issuerNameID, &pkcs15infoPtr->issuerNameIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_SUBJECTNAMEHASH: status = readOctetString( stream, pkcs15infoPtr->subjectNameID, &pkcs15infoPtr->subjectNameIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_PGP2: status = readOctetString( stream, pkcs15infoPtr->pgp2KeyID, &pkcs15infoPtr->pgp2KeyIDlength, PGP_KEYID_SIZE, PGP_KEYID_SIZE ); break; case PKCS15_KEYID_OPENPGP: status = readOctetString( stream, pkcs15infoPtr->openPGPKeyID, &pkcs15infoPtr->openPGPKeyIDlength, PGP_KEYID_SIZE, PGP_KEYID_SIZE ); break; default: status = readUniversal( stream ); } } if( iterationCount >= FAILSAFE_ITERATIONS_MED ) { /* This could be either an internal error or some seriously malformed data, since we can't tell without human intervention we throw a debug exception but otherwise treat it as bad data */ DEBUG_DIAG(( "Encountered more than %d key IDs", FAILSAFE_ITERATIONS_MED )); assert( DEBUG_WARN ); return( CRYPT_ERROR_BADDATA ); } return( status ); }
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 ); }
static int readGeneralInfoAttribute( INOUT STREAM *stream, INOUT CMP_PROTOCOL_INFO *protocolInfo ) { BYTE oid[ MAX_OID_SIZE + 8 ]; int length, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) ); /* 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 ) ) return( status ); /* 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; return( readUniversal( stream ) ); /* Attribute */ } /* Check for the ESSCertID, which fixes CMP's broken certificate identification mechanism */ if( length == sizeofOID( OID_ESS_CERTID ) && \ !memcmp( oid, OID_ESS_CERTID, length ) ) { BYTE certID[ CRYPT_MAX_HASHSIZE + 8 ]; int certIDsize, endPos; /* Extract the certificate hash from the ESSCertID */ readSet( stream, NULL ); /* Attribute */ readSequence( stream, NULL ); /* SigningCerts */ readSequence( stream, NULL ); /* Certs */ status = readSequence( stream, &length ); /* ESSCertID */ if( cryptStatusError( status ) ) return( status ); endPos = stell( stream ) + length; status = readOctetString( stream, certID, &certIDsize, KEYID_SIZE, KEYID_SIZE ); if( cryptStatusError( status ) ) return( status ); if( protocolInfo->certIDsize != KEYID_SIZE || \ memcmp( certID, protocolInfo->certID, KEYID_SIZE ) ) { /* The certificate used for authentication purposes has changed, remember the new certID */ memcpy( protocolInfo->certID, certID, KEYID_SIZE ); protocolInfo->certIDsize = KEYID_SIZE; 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, but in any case we don't need it because we've already got the certificate ID */ status = readUniversal( stream ); } return( status ); } /* It's something that we don't recognise, skip it */ return( readUniversal( stream ) ); }