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 readDsaPrivateKeyOld( INOUT STREAM *stream, INOUT CONTEXT_INFO *contextInfoPtr ) { CRYPT_ALGO_TYPE cryptAlgo; PKC_INFO *dlpKey = contextInfoPtr->ctxPKC; int dummy, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) ); REQUIRES( contextInfoPtr->type == CONTEXT_PKC && \ contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_DSA ); /* Skip the PKCS #8 wrapper */ readSequence( stream, NULL ); /* Outer wrapper */ readShortInteger( stream, NULL ); /* Version */ status = readAlgoIDparam( stream, &cryptAlgo, &dummy, ALGOID_CLASS_PKC ); if( cryptStatusError( status ) || cryptAlgo != CRYPT_ALGO_DSA ) return( CRYPT_ERROR_BADDATA ); /* Read the DSA parameters if we haven't already got them via the associated public key/certificate */ if( BN_is_zero( &dlpKey->dlpParam_p ) ) { readSequence( stream, NULL ); /* Parameter wrapper */ status = readBignumChecked( stream, &dlpKey->dlpParam_p, DLPPARAM_MIN_P, DLPPARAM_MAX_P, NULL ); if( cryptStatusOK( status ) ) status = readBignumChecked( stream, &dlpKey->dlpParam_q, DLPPARAM_MIN_Q, DLPPARAM_MAX_Q, NULL ); if( cryptStatusOK( status ) ) status = readBignumChecked( stream, &dlpKey->dlpParam_g, DLPPARAM_MIN_G, DLPPARAM_MAX_G, NULL ); } else status = readUniversal( stream ); if( cryptStatusError( status ) ) return( status ); /* Read the DSA private key component */ status = readOctetStringHole( stream, NULL, 20, DEFAULT_TAG ); if( cryptStatusOK( status ) ) /* OCTET STRING encapsulation */ status = readBignum( stream, &dlpKey->dlpParam_x, DLPPARAM_MIN_X, DLPPARAM_MAX_X, &dlpKey->dlpParam_p ); return( status ); }
static int readAVA( INOUT STREAM *stream, OUT_INT_Z int *type, OUT_LENGTH_SHORT_Z int *length, OUT_TAG_ENCODED_Z int *stringTag ) { const DN_COMPONENT_INFO *dnComponentInfo; BYTE oid[ MAX_OID_SIZE + 8 ]; int oidLength, tag, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( type, sizeof( int ) ) ); assert( isWritePtr( length, sizeof( int ) ) ); assert( isWritePtr( stringTag, sizeof( int ) ) ); /* Clear return values */ *type = 0; *length = 0; *stringTag = 0; /* Read the start of the AVA and determine the type from the AttributeType field. If we find something that we don't recognise we indicate it as a non-component type that can be read or written but not directly accessed by the user (although it can still be accessed using the cursor functions) */ readSequence( stream, NULL ); status = readEncodedOID( stream, oid, MAX_OID_SIZE, &oidLength, BER_OBJECT_IDENTIFIER ); if( cryptStatusError( status ) ) return( status ); dnComponentInfo = findDNInfoByOID( oid, oidLength ); if( dnComponentInfo == NULL ) { /* If we don't recognise the component type at all, skip it */ status = readUniversal( stream ); return( cryptStatusError( status ) ? status : OK_SPECIAL ); } *type = dnComponentInfo->type; /* We've reached the data value, make sure that it's in order. When we read the wrapper around the string type with readGenericHole() we have to allow a minimum length of zero instead of one because of broken AVAs with zero-length strings */ tag = peekTag( stream ); if( cryptStatusError( tag ) ) return( tag ); if( tag == BER_BITSTRING ) return( readAVABitstring( stream, length, stringTag ) ); *stringTag = tag; return( readGenericHole( stream, length, 0, tag ) ); }
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 ); }
static int readConfigOption( INOUT STREAM *stream, IN_HANDLE CRYPT_USER iCryptUser ) { CRYPT_ATTRIBUTE_TYPE attributeType; const BUILTIN_OPTION_INFO *builtinOptionInfoPtr; MESSAGE_DATA msgData; void *dataPtr DUMMY_INIT_PTR; long optionCode; int value, tag, length, status; /* Read the wrapper and option index and map it to the actual option. If we find an unknown index or one that shouldn't be writeable to persistent storage, we skip it and continue. This is done to handle new options that may have been added after this version of cryptlib was built (for unknown indices) and because the stored configuration options are an untrusted source so we have to check for attempts to feed in bogus values (for non-writeable options) */ readSequence( stream, NULL ); status = readShortInteger( stream, &optionCode ); if( cryptStatusError( status ) ) return( status ); if( optionCode < 0 || optionCode > LAST_OPTION_INDEX ) { /* Unknown option, ignore it */ return( readUniversal( stream ) ); } builtinOptionInfoPtr = getBuiltinOptionInfoByCode( optionCode ); if( builtinOptionInfoPtr == NULL || \ builtinOptionInfoPtr->index < 0 || \ builtinOptionInfoPtr->index > LAST_OPTION_INDEX || \ builtinOptionInfoPtr->index == CRYPT_UNUSED ) { /* Unknown option, ignore it */ return( readUniversal( stream ) ); } attributeType = builtinOptionInfoPtr->option; /* Read the option value and set the option. We don't treat a failure to set the option as a problem since the user probably doesn't want the entire system to fail because of a bad configuration option, and in any case we'll fall back to a safe default value */ status = tag = peekTag( stream ); if( cryptStatusError( status ) ) return( status ); if( tag == BER_BOOLEAN || tag == BER_INTEGER ) { /* It's a numeric value, read the appropriate type and try and set the option */ if( tag == BER_BOOLEAN ) status = readBoolean( stream, &value ); else { long integer; status = readShortInteger( stream, &integer ); if( cryptStatusOK( status ) ) value = ( int ) integer; } if( cryptStatusError( status ) ) return( status ); ( void ) krnlSendMessage( iCryptUser, IMESSAGE_SETATTRIBUTE, &value, attributeType ); return( CRYPT_OK ); } /* It's a string value, set the option straight from the encoded data */ status = readGenericHole( stream, &length, 1, BER_STRING_UTF8 ); if( cryptStatusOK( status ) ) status = sMemGetDataBlock( stream, &dataPtr, length ); if( cryptStatusOK( status ) ) status = sSkip( stream, length, SSKIP_MAX ); if( cryptStatusError( status ) ) return( status ); setMessageData( &msgData, dataPtr, length ); ( void ) krnlSendMessage( iCryptUser, IMESSAGE_SETATTRIBUTE_S, &msgData, attributeType ); return( CRYPT_OK ); }
int queryAsn1Object( INOUT void *streamPtr, OUT QUERY_INFO *queryInfo ) { QUERY_INFO basicQueryInfo; STREAM *stream = streamPtr; const long startPos = stell( stream ); int status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) ); /* Clear return value */ memset( queryInfo, 0, sizeof( QUERY_INFO ) ); /* Determine basic object information. This also verifies that all of the object data is present in the stream */ status = getObjectInfo( stream, &basicQueryInfo ); if( cryptStatusError( status ) ) return( status ); /* Call the appropriate routine to find out more about the object */ switch( basicQueryInfo.type ) { case CRYPT_OBJECT_ENCRYPTED_KEY: { const READKEK_FUNCTION readKekFunction = \ getReadKekFunction( KEYEX_CMS ); if( readKekFunction == NULL ) return( CRYPT_ERROR_NOTAVAIL ); status = readKekFunction( stream, queryInfo ); break; } case CRYPT_OBJECT_PKCENCRYPTED_KEY: { const READKEYTRANS_FUNCTION readKeytransFunction = \ getReadKeytransFunction( ( basicQueryInfo.formatType == CRYPT_FORMAT_CMS ) ? \ KEYEX_CMS : KEYEX_CRYPTLIB ); if( readKeytransFunction == NULL ) return( CRYPT_ERROR_NOTAVAIL ); status = readKeytransFunction( stream, queryInfo ); break; } case CRYPT_OBJECT_SIGNATURE: { const READSIG_FUNCTION readSigFunction = \ getReadSigFunction( ( basicQueryInfo.formatType == CRYPT_FORMAT_CMS ) ? \ SIGNATURE_CMS : SIGNATURE_CRYPTLIB ); if( readSigFunction == NULL ) return( CRYPT_ERROR_NOTAVAIL ); status = readSigFunction( stream, queryInfo ); break; } case CRYPT_OBJECT_NONE: /* New, unrecognised RecipientInfo type */ status = readUniversal( stream ); break; default: retIntError(); } sseek( stream, startPos ); if( cryptStatusError( status ) ) { zeroise( queryInfo, sizeof( QUERY_INFO ) ); return( status ); } /* Augment the per-object query information with the basic query information that we got earlier */ queryInfo->formatType = basicQueryInfo.formatType; queryInfo->type = basicQueryInfo.type; queryInfo->size = basicQueryInfo.size; queryInfo->version = basicQueryInfo.version; 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 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 ); }
static int readEccPrivateKeyOld( INOUT STREAM *stream, INOUT CONTEXT_INFO *contextInfoPtr ) { CRYPT_ALGO_TYPE cryptAlgo; PKC_INFO *eccKey = contextInfoPtr->ctxPKC; long value; int tag, length, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) ); REQUIRES( contextInfoPtr->type == CONTEXT_PKC && \ contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_ECDSA ); /* Read the ECC key components. These were never standardised in any PKCS standard, nor in the PKCS #12 RFC. RFC 5915 "Elliptic Curve Private Key Structure" specifies the format for PKCS #8 as: ECPrivateKey ::= SEQUENCE { version INTEGER (1), privateKey OCTET STRING, parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, publicKey [1] BIT STRING OPTIONAL } but this isn't what's present in encoded forms created by OpenSSL. Instead it's: ECSomething ::= SEQUENCE { version INTEGER (0), parameters SEQUENCE { type OBJECT IDENTIFIER ecPublicKey, namedCurve OBJECT IDENTIFIER } something OCTET STRING { key ECPrivateKey -- As above } } so we have to tunnel into this in order to find the PKCS #8-like data that we're actually interested in. Note that we can't use the ECC p value for a range check because it hasn't been set yet, all that we have at this point is a curve ID */ readSequence( stream, NULL ); /* Outer wrapper */ status = readShortInteger( stream, &value );/* Version */ if( cryptStatusError( status ) || value != 0 ) return( CRYPT_ERROR_BADDATA ); status = readAlgoIDparam( stream, &cryptAlgo, &length, ALGOID_CLASS_PKC ); if( cryptStatusError( status ) || cryptAlgo != CRYPT_ALGO_ECDSA ) return( CRYPT_ERROR_BADDATA ); readUniversal( stream ); /* Named curve */ readOctetStringHole( stream, NULL, MIN_PKCSIZE_ECC_THRESHOLD, DEFAULT_TAG ); /* OCTET STRING hole wrapper */ readSequence( stream, NULL ); /* ECPrivateKey wrapper */ status = readShortInteger( stream, &value ); /* Version */ if( cryptStatusError( status ) || value != 1 ) return( CRYPT_ERROR_BADDATA ); /* We've finalled made it down to the private key value. At this point we can't use readBignumTag() directly because it's designed to read either standard INTEGERs (via DEFAULT_TAG) or context-specific tagged items, so passing in a BER_OCTETSTRING will be interpreted as [4] IMPLICIT INTEGER rather than an OCTET STRING-tagged integer. To get around this we read the tag separately and tell readBignumTag() to skip the tag read */ tag = readTag( stream ); if( cryptStatusError( tag ) || tag != BER_OCTETSTRING ) return( CRYPT_ERROR_BADDATA ); return( readBignumTag( stream, &eccKey->eccParam_d, ECCPARAM_MIN_D, ECCPARAM_MAX_D, NULL, NO_TAG ) ); }
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 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 ); }
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 ) ); }