C_RET cryptQueryObject( C_IN void C_PTR objectData, C_IN int objectDataLength, C_OUT CRYPT_OBJECT_INFO C_PTR cryptObjectInfo ) { QUERY_INFO queryInfo = DUMMY_INIT_STRUCT; /* If USE_PGP undef'd */ STREAM stream; int value, length = objectDataLength, status; /* Perform basic error checking and clear the return value */ if( objectDataLength <= MIN_CRYPT_OBJECTSIZE || \ objectDataLength >= MAX_INTLENGTH ) return( CRYPT_ERROR_PARAM2 ); if( !isReadPtr( objectData, objectDataLength ) ) return( CRYPT_ERROR_PARAM1 ); if( !isWritePtrConst( cryptObjectInfo, sizeof( CRYPT_OBJECT_INFO ) ) ) return( CRYPT_ERROR_PARAM3 ); memset( cryptObjectInfo, 0, sizeof( CRYPT_OBJECT_INFO ) ); /* Query the object. This is just a wrapper for the lower-level object- query functions. Note that we use sPeek() rather than peekTag() because we want to continue processing (or at least checking for) PGP data if it's no ASN.1 */ sMemConnect( &stream, ( void * ) objectData, length ); status = value = sPeek( &stream ); if( cryptStatusError( status ) ) { sMemDisconnect( &stream ); return( status ); } if( value == BER_SEQUENCE || value == MAKE_CTAG( CTAG_RI_PWRI ) ) status = queryAsn1Object( &stream, &queryInfo ); else { #ifdef USE_PGP status = queryPgpObject( &stream, &queryInfo ); #else status = CRYPT_ERROR_BADDATA; #endif /* USE_PGP */ } sMemDisconnect( &stream ); if( cryptStatusError( status ) ) return( status ); /* Copy the externally-visible fields across */ cryptObjectInfo->objectType = queryInfo.type; cryptObjectInfo->cryptAlgo = queryInfo.cryptAlgo; cryptObjectInfo->cryptMode = queryInfo.cryptMode; if( queryInfo.type == CRYPT_OBJECT_SIGNATURE ) cryptObjectInfo->hashAlgo = queryInfo.hashAlgo; if( queryInfo.type == CRYPT_OBJECT_ENCRYPTED_KEY && \ queryInfo.saltLength > 0 ) { memcpy( cryptObjectInfo->salt, queryInfo.salt, queryInfo.saltLength ); cryptObjectInfo->saltSize = queryInfo.saltLength; if( queryInfo.keySetupAlgo != CRYPT_ALGO_NONE ) cryptObjectInfo->hashAlgo = queryInfo.keySetupAlgo; } return( CRYPT_OK ); }
static int readPubkeyAttributes( INOUT STREAM *stream, INOUT PKCS15_INFO *pkcs15infoPtr, IN_LENGTH const int endPos, const BOOLEAN isPubKeyObject ) { int usageFlags, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) ); REQUIRES( endPos > 0 && endPos > stell( stream ) && \ endPos < MAX_INTLENGTH ); status = readBitString( stream, &usageFlags ); /* Usage flags */ if( canContinue( stream, status, endPos ) && /* Native flag */ peekTag( stream ) == BER_BOOLEAN ) status = readUniversal( stream ); if( canContinue( stream, status, endPos ) && /* Access flags */ peekTag( stream ) == BER_BITSTRING ) status = readUniversal( stream ); if( canContinue( stream, status, endPos ) && /* Key reference */ peekTag( stream ) == BER_INTEGER ) status = readUniversal( stream ); 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( CTAG_KA_VALIDTO ) ) status = readGeneralizedTimeTag( stream, &pkcs15infoPtr->validTo, CTAG_KA_VALIDTO ); if( cryptStatusError( status ) ) return( status ); if( isPubKeyObject ) pkcs15infoPtr->pubKeyUsage = usageFlags; else pkcs15infoPtr->privKeyUsage = usageFlags; return( CRYPT_OK ); }
static int getObjectInfo( INOUT STREAM *stream, OUT QUERY_INFO *queryInfo ) { const long startPos = stell( stream ); long value; int tag, length, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) ); /* Clear return value */ memset( queryInfo, 0, sizeof( QUERY_INFO ) ); /* We always need at least MIN_CRYPT_OBJECTSIZE more bytes to do anything */ if( sMemDataLeft( stream ) < MIN_CRYPT_OBJECTSIZE ) return( CRYPT_ERROR_UNDERFLOW ); /* Get the type, length, and version information */ status = getStreamObjectLength( stream, &length ); if( cryptStatusError( status ) ) return( status ); queryInfo->formatType = CRYPT_FORMAT_CRYPTLIB; queryInfo->size = length; tag = peekTag( stream ); if( cryptStatusError( tag ) ) return( tag ); readGenericHole( stream, NULL, 16, tag ); status = readShortInteger( stream, &value ); if( cryptStatusError( status ) ) return( status ); queryInfo->version = value; switch( tag ) { case BER_SEQUENCE: /* This could be a signature or a PKC-encrypted key, see what follows */ switch( value ) { case KEYTRANS_VERSION: case KEYTRANS_EX_VERSION: queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY; break; case SIGNATURE_VERSION: case SIGNATURE_EX_VERSION: queryInfo->type = CRYPT_OBJECT_SIGNATURE; break; default: return( CRYPT_ERROR_BADDATA ); } if( value == KEYTRANS_VERSION || value == SIGNATURE_VERSION ) queryInfo->formatType = CRYPT_FORMAT_CMS; break; case MAKE_CTAG( CTAG_RI_KEYAGREE ): /* It's CMS' wierd X9.42-inspired key agreement mechanism, we can't do much with this (mind you neither can anyone else) so we should probably really treat it as a CRYPT_ERROR_BADDATA if we encounter it rather than just ignoring it */ queryInfo->type = CRYPT_OBJECT_NONE; DEBUG_DIAG(( "Found keyAgreeRecipientInfo" )); assert( DEBUG_WARN ); break; case MAKE_CTAG( CTAG_RI_PWRI ): queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY; break; default: queryInfo->type = CRYPT_OBJECT_NONE; if( tag > MAKE_CTAG( CTAG_RI_PWRI ) && \ tag <= MAKE_CTAG( CTAG_RI_MAX ) ) { /* This is probably a new RecipientInfo type, skip it */ DEBUG_DIAG(( "Found unknown RecipientInfo %X", tag )); assert( DEBUG_WARN ); break; } return( CRYPT_ERROR_BADDATA ); } /* Reset the stream and make sure that all of the data is present */ sseek( stream, startPos ); return( sMemDataLeft( stream ) < queryInfo->size ? \ CRYPT_ERROR_UNDERFLOW : 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 ); }
{ WILDCARD_OID, CRYPT_ATTRIBUTE_NONE }, { NULL, 0 }, { NULL, 0 } }; /* Permitted object subtypes. PKCS #15 uses context-specific tagging to identify the subtypes within an object type so we store a list of permitted tags for each object type */ typedef struct { PKCS15_OBJECT_TYPE type; /* Object type */ int subTypes[ 7 ]; /* Subtype tags */ } ALLOWED_ATTRIBUTE_TYPES; static const ALLOWED_ATTRIBUTE_TYPES allowedTypesTbl[] = { { PKCS15_OBJECT_PUBKEY, { BER_SEQUENCE, MAKE_CTAG( CTAG_PK_ECC ), MAKE_CTAG( CTAG_PK_DH ), MAKE_CTAG( CTAG_PK_DSA ), MAKE_CTAG( CTAG_PK_KEA ), CRYPT_ERROR, CRYPT_ERROR } }, { PKCS15_OBJECT_PRIVKEY, { BER_SEQUENCE, MAKE_CTAG( CTAG_PK_ECC ), MAKE_CTAG( CTAG_PK_DH ), MAKE_CTAG( CTAG_PK_DSA ), MAKE_CTAG( CTAG_PK_KEA ), CRYPT_ERROR, CRYPT_ERROR } }, { PKCS15_OBJECT_CERT, { BER_SEQUENCE, CRYPT_ERROR, CRYPT_ERROR } }, { PKCS15_OBJECT_SECRETKEY, { CRYPT_ERROR, CRYPT_ERROR } }, { PKCS15_OBJECT_DATA, { MAKE_CTAG( CTAG_DO_OIDDO ), CRYPT_ERROR, CRYPT_ERROR } }, { PKCS15_OBJECT_NONE, { CRYPT_ERROR, CRYPT_ERROR } }, { PKCS15_OBJECT_NONE, { CRYPT_ERROR, CRYPT_ERROR } } };
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 ); }
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 ); }