static int writeCertID( INOUT STREAM *stream, IN_HANDLE const CRYPT_CONTEXT iCryptCert ) { MESSAGE_DATA msgData; BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ]; int essCertIDSize, payloadSize, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); REQUIRES( isHandleRangeValid( iCryptCert ) ); /* Find out how big the payload will be */ setMessageData( &msgData, certHash, CRYPT_MAX_HASHSIZE ); status = krnlSendMessage( iCryptCert, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CERTINFO_FINGERPRINT_SHA1 ); if( cryptStatusError( status ) ) return( status ); essCertIDSize = ( int ) sizeofObject( msgData.length ); payloadSize = objSize( objSize( objSize( essCertIDSize ) ) ); /* Write the signing certificate ID information */ writeSequence( stream, sizeofOID( OID_ESS_CERTID ) + \ ( int ) sizeofObject( payloadSize ) ); writeOID( stream, OID_ESS_CERTID ); writeSet( stream, payloadSize ); writeSequence( stream, objSize( objSize( essCertIDSize ) ) ); writeSequence( stream, objSize( essCertIDSize ) ); writeSequence( stream, essCertIDSize ); return( writeOctetString( stream, certHash, msgData.length, DEFAULT_TAG ) ); }
CHECK_RETVAL \ static int sizeofCertID( IN_HANDLE const CRYPT_CONTEXT iCryptCert ) { const int essCertIDSize = objSize( objSize( objSize( objSize( 20 ) ) ) ); /* Infinitely-nested SHA-1 hash */ REQUIRES( isHandleRangeValid( iCryptCert ) ); return( objSize( sizeofOID( OID_ESS_CERTID ) + \ sizeofObject( essCertIDSize ) ) ); }
static int writeKeyDerivationInfo( INOUT STREAM *stream, IN_HANDLE const CRYPT_CONTEXT iCryptContext ) { MESSAGE_DATA msgData; BYTE salt[ CRYPT_MAX_HASHSIZE + 8 ]; int saltLength, keySetupIterations, prfAlgo = DUMMY_INIT; int derivationInfoSize, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); REQUIRES( isHandleRangeValid( iCryptContext ) ); /* Get the key derivation information */ status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE, &keySetupIterations, CRYPT_CTXINFO_KEYING_ITERATIONS ); if( cryptStatusOK( status ) ) status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE, &prfAlgo, CRYPT_CTXINFO_KEYING_ALGO ); if( cryptStatusError( status ) ) return( status ); setMessageData( &msgData, salt, CRYPT_MAX_HASHSIZE ); status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_KEYING_SALT ); if( cryptStatusError( status ) ) return( status ); saltLength = msgData.length; derivationInfoSize = ( int ) sizeofObject( saltLength ) + \ sizeofShortInteger( keySetupIterations ); if( prfAlgo != CRYPT_ALGO_HMAC_SHA1 ) derivationInfoSize += sizeofAlgoID( prfAlgo ); /* Write the PBKDF2 information */ writeConstructed( stream, sizeofOID( OID_PBKDF2 ) + ( int ) sizeofObject( derivationInfoSize ), CTAG_KK_DA ); writeOID( stream, OID_PBKDF2 ); writeSequence( stream, derivationInfoSize ); writeOctetString( stream, salt, saltLength, DEFAULT_TAG ); status = writeShortInteger( stream, keySetupIterations, DEFAULT_TAG ); if( prfAlgo != CRYPT_ALGO_HMAC_SHA1 ) status = writeAlgoID( stream, prfAlgo ); zeroise( salt, CRYPT_MAX_HASHSIZE ); return( status ); }
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 writeDN( INOUT STREAM *stream, IN_OPT const DN_PTR *dnComponentList, IN_TAG const int tag ) { DN_COMPONENT *dnComponentPtr; int size, iterationCount, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( dnComponentList == NULL || \ isReadPtr( dnComponentList, sizeof( DN_COMPONENT ) ) ); REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) ); /* Special case for empty DNs */ if( dnComponentList == NULL ) return( writeConstructed( stream, 0, tag ) ); status = preEncodeDN( ( DN_COMPONENT * ) dnComponentList, &size ); if( cryptStatusError( status ) ) return( status ); /* Write the DN */ writeConstructed( stream, size, tag ); for( dnComponentPtr = ( DN_COMPONENT * ) dnComponentList, \ iterationCount = 0; dnComponentPtr != NULL && \ iterationCount < FAILSAFE_ITERATIONS_MED; dnComponentPtr = dnComponentPtr->next, iterationCount++ ) { const DN_COMPONENT_INFO *dnComponentInfo = dnComponentPtr->typeInfo; BYTE dnString[ MAX_ATTRIBUTE_SIZE + 8 ]; int dnStringLength; /* Write the RDN wrapper */ if( dnComponentPtr->encodedRDNdataSize > 0 ) { /* If it's the start of an RDN, write the RDN header */ writeSet( stream, dnComponentPtr->encodedRDNdataSize ); } writeSequence( stream, dnComponentPtr->encodedAVAdataSize ); status = swrite( stream, dnComponentInfo->oid, sizeofOID( dnComponentInfo->oid ) ); if( cryptStatusError( status ) ) return( status ); /* Convert the string to an ASN.1-compatible format and write it out */ status = copyToAsn1String( dnString, MAX_ATTRIBUTE_SIZE, &dnStringLength, dnComponentPtr->value, dnComponentPtr->valueLength, dnComponentPtr->valueStringType ); if( cryptStatusError( status ) ) return( status ); if( dnComponentPtr->asn1EncodedStringType == BER_STRING_IA5 && \ !dnComponentInfo->ia5OK ) { /* If an IA5String isn't allowed in this instance, use a T61String instead */ dnComponentPtr->asn1EncodedStringType = BER_STRING_T61; } status = writeCharacterString( stream, dnString, dnStringLength, dnComponentPtr->asn1EncodedStringType ); if( cryptStatusError( status ) ) return( status ); } ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED ); return( CRYPT_OK ); }
static int preEncodeDN( INOUT DN_COMPONENT *dnComponentPtr, OUT_LENGTH_SHORT_Z int *length ) { int size = 0, iterationCount; assert( isWritePtr( dnComponentPtr, sizeof( DN_COMPONENT ) ) ); assert( isWritePtr( length, sizeof( int ) ) ); /* Clear return value */ *length = 0; assert( isReadPtr( dnComponentPtr, sizeof( DN_COMPONENT ) ) ); #if 0 /* 18/7/08 Should never happen */ /* If we're being fed an entry in the middle of a DN, move back to the start */ for( iterationCount = 0; dnComponentPtr->prev != NULL && \ iterationCount < FAILSAFE_ITERATIONS_MED; dnComponentPtr = dnComponentPtr->prev, iterationCount++ ); ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED ); #endif /* 0 */ ENSURES( dnComponentPtr->prev == NULL ); /* Walk down the DN pre-encoding each AVA */ for( iterationCount = 0; dnComponentPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MED; dnComponentPtr = dnComponentPtr->next, iterationCount++ ) { DN_COMPONENT *rdnStartPtr = dnComponentPtr; int innerIterationCount; /* Calculate the size of every AVA in this RDN */ for( innerIterationCount = 0; dnComponentPtr != NULL && \ innerIterationCount < FAILSAFE_ITERATIONS_MED; dnComponentPtr = dnComponentPtr->next, innerIterationCount++ ) { const DN_COMPONENT_INFO *dnComponentInfo = dnComponentPtr->typeInfo; int dnStringLength, status; status = getAsn1StringInfo( dnComponentPtr->value, dnComponentPtr->valueLength, &dnComponentPtr->valueStringType, &dnComponentPtr->asn1EncodedStringType, &dnStringLength ); if( cryptStatusError( status ) ) return( status ); dnComponentPtr->encodedAVAdataSize = ( int ) \ sizeofOID( dnComponentInfo->oid ) + \ sizeofObject( dnStringLength ); dnComponentPtr->encodedRDNdataSize = 0; rdnStartPtr->encodedRDNdataSize += ( int ) \ sizeofObject( dnComponentPtr->encodedAVAdataSize ); if( !( dnComponentPtr->flags & DN_FLAG_CONTINUED ) ) break; } ENSURES( innerIterationCount < FAILSAFE_ITERATIONS_MED ); /* Calculate the overall size of the RDN */ size += ( int ) sizeofObject( rdnStartPtr->encodedRDNdataSize ); /* If the inner loop terminated because it reached the end of the DN then we need to explicitly exit the outer loop as well before it tries to follow the 'next' link in the dnComponentPtr */ if( dnComponentPtr == NULL ) break; } ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED ); *length = size; return( CRYPT_OK ); }
static int oidToText( IN_BUFFER( binaryOidLen ) const BYTE *binaryOID, IN_LENGTH_OID const int binaryOidLen, OUT_BUFFER( maxOidLen, *oidLen ) char *oid, IN_LENGTH_SHORT_MIN( 16 ) const int maxOidLen, OUT_LENGTH_SHORT_Z int *oidLen ) { long value = 0; int i, length = 0, subLen; assert( isReadPtr( binaryOID, binaryOidLen ) ); assert( isWritePtr( oid, maxOidLen ) ); assert( isWritePtr( oidLen, sizeof( int ) ) ); REQUIRES( binaryOidLen >= MIN_OID_SIZE && \ binaryOidLen <= MAX_OID_SIZE && \ binaryOidLen == sizeofOID( binaryOID ) ); REQUIRES( maxOidLen >= 16 && maxOidLen < MAX_INTLENGTH_SHORT ); /* Clear return values */ memset( oid, 0, min( 16, maxOidLen ) ); *oidLen = 0; for( i = 2; i < binaryOidLen; i++ ) { const BYTE data = binaryOID[ i ]; const long valTmp = value << 7; /* Pick apart the encoding */ if( value == 0 && data == 0x80 ) { /* Invalid leading zero value, ( 0x80 & 0x7F ) == 0 */
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 ); }
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \ static int writeNonCMSheader( INOUT STREAM *stream, IN_BUFFER( oidLength ) const BYTE *oid, IN_RANGE( 1, MAX_OID_SIZE ) const int oidLength, IN_LENGTH_SHORT const int length, IN_LENGTH_SHORT const int attrDataLength ) { assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isReadPtr( oid, oidLength ) ); REQUIRES( oidLength >= MIN_OID_SIZE && oidLength <= MAX_OID_SIZE ); REQUIRES( length > 0 && length < MAX_INTLENGTH_SHORT ); REQUIRES( attrDataLength > 0 && attrDataLength < MAX_INTLENGTH_SHORT ); writeSequence( stream, ( int ) \ ( sizeofOID( oid ) + \ sizeofObject( sizeofObject( length ) ) + \ sizeofObject( attrDataLength ) ) ); swrite( stream, oid, oidLength ); writeConstructed( stream, ( int ) sizeofObject( length ), 0 ); return( writeSequence( stream, length ) ); } /* Write the MAC data at the end of the keyset */ CHECK_RETVAL_LENGTH STDC_NONNULL_ARG( ( 1 ) ) \ static int sizeofMacData( const PKCS12_INFO *pkcs12info ) { assert( isReadPtr( pkcs12info, sizeof( PKCS12_INFO ) ) ); return( sizeofObject( \
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 ) ); }
static int writePkiHeader( INOUT STREAM *stream, INOUT SESSION_INFO *sessionInfoPtr, INOUT CMP_PROTOCOL_INFO *protocolInfo ) { CRYPT_HANDLE senderNameObject = DUMMY_INIT, recipNameObject = DUMMY_INIT; STREAM nullStream; MESSAGE_DATA msgData; #ifdef USE_FULL_HEADERS const BOOLEAN sendFullHeader = TRUE; #else BOOLEAN sendFullHeader = FALSE; #endif /* USE_FULL_HEADERS */ BOOLEAN sendClibID = FALSE, sendCertID = FALSE, sendMacInfo = FALSE; BOOLEAN sendUserID = FALSE; int senderNameLength, recipNameLength, attributeLength = 0; int protInfoLength = DUMMY_INIT, totalLength, hashAlgo, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) ); /* Determine which of the many unnecessary and inexplicable bits of the CMP header we actually have to send: sendCertID: Sent on the first message where it's required (which isn't necessarily the first message in an exchange, for example it's not used in an ir/ip), either to identify the CA's cert in a CTL sent in a PKIBoot response or to identify the signing certificate when we're using signature-based message authentication. sendClibID: Sent on the first message to tell the other side that this is a cryptlib client/server. sendFullHeader: Sent if the other side isn't running cryptlib, unless we're doing PKIBoot, for which we couldn't send full headers even if we wanted to sendMacInfo: Sent if we're using MAC integrity protection and the the other side isn't running cryptlib, or if this is the first message. sendUserID: Sent on the first message or if we're sending full headers, provided that it's actually available to send */ if( !( sessionInfoPtr->protocolFlags & CMP_PFLAG_CERTIDSENT ) && \ ( ( isServer( sessionInfoPtr ) && \ protocolInfo->operation == CTAG_PB_GENM ) || \ !protocolInfo->useMACsend ) ) sendCertID = TRUE; if( !( sessionInfoPtr->protocolFlags & CMP_PFLAG_CLIBIDSENT ) ) sendClibID = TRUE; #ifndef USE_FULL_HEADERS if( !protocolInfo->isCryptlib && \ protocolInfo->operation != CTAG_PB_GENM ) sendFullHeader = TRUE; #endif /* !USE_FULL_HEADERS */ if( protocolInfo->useMACsend && \ !( protocolInfo->isCryptlib && \ ( sessionInfoPtr->protocolFlags & CMP_PFLAG_MACINFOSENT ) ) ) sendMacInfo = TRUE; if( ( sendFullHeader || \ !( sessionInfoPtr->protocolFlags & CMP_PFLAG_USERIDSENT ) ) && \ ( protocolInfo->userIDsize > 0 ) ) sendUserID = TRUE; REQUIRES( !sendFullHeader || !protocolInfo->headerRead || \ ( protocolInfo->userIDsize > 0 && \ protocolInfo->userIDsize < MAX_INTLENGTH_SHORT ) ); REQUIRES( protocolInfo->transIDsize > 0 && \ protocolInfo->transIDsize < MAX_INTLENGTH_SHORT ); /* Get any other state information that we may need */ status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &hashAlgo, CRYPT_OPTION_ENCR_HASH ); ENSURES( cryptStatusOK( status ) ); protocolInfo->hashAlgo = hashAlgo; /* int vs.enum */ /* Determine how big the sender and recipient information will be. We shouldn't need to send a recipient name for an ir because it won't usually be known yet, but various implementations can't handle a zero-length GeneralName so we supply it if it's available even though it's redundant */ if( sendFullHeader ) { status = initDNInfo( sessionInfoPtr, &senderNameObject, &recipNameObject, &senderNameLength, &recipNameLength, ( protocolInfo->operation == CTAG_PB_IR ) ? \ TRUE : FALSE, protocolInfo->cryptOnlyKey ); if( cryptStatusError( status ) ) return( status ); } else { /* We're not using sender or recipient information since it doesn't serve any useful purpose, just set the fields to an empty SEQUENCE */ senderNameLength = recipNameLength = sizeofObject( 0 ); } /* Determine how big the remaining header data will be */ sMemNullOpen( &nullStream ); if( protocolInfo->useMACsend ) { status = writeMacInfo( &nullStream, protocolInfo, sendMacInfo ); } else { status = writeContextAlgoID( &nullStream, protocolInfo->authContext, protocolInfo->hashAlgo ); } if( cryptStatusOK( status ) ) protInfoLength = stell( &nullStream ); sMemClose( &nullStream ); if( cryptStatusError( status ) ) return( status ); if( sendClibID ) { attributeLength += sizeofObject( \ sizeofOID( OID_CRYPTLIB_PRESENCECHECK ) + \ sizeofObject( 0 ) ); } if( sendCertID ) { const int certIDsize = sizeofCertID( protocolInfo->authContext ); ENSURES( certIDsize > 0 && certIDsize < MAX_INTLENGTH_SHORT ); attributeLength += certIDsize; } totalLength = sizeofShortInteger( CMP_VERSION ) + \ objSize( senderNameLength ) + objSize( recipNameLength ) + \ objSize( protInfoLength ) + \ objSize( sizeofObject( protocolInfo->transIDsize ) ); if( sendUserID ) totalLength += objSize( sizeofObject( protocolInfo->userIDsize ) ); if( sendFullHeader ) { if( protocolInfo->senderNonceSize > 0 ) totalLength += objSize( \ sizeofObject( protocolInfo->senderNonceSize ) ); if( protocolInfo->recipNonceSize > 0 ) totalLength += objSize( \ sizeofObject( protocolInfo->recipNonceSize ) ); } if( attributeLength > 0 ) totalLength += objSize( objSize( attributeLength ) ); /* Perform an early check for data-size problems before we go through all of the following code */ if( sizeofObject( totalLength ) <= 0 || \ sizeofObject( totalLength ) > sMemDataLeft( stream ) ) return( CRYPT_ERROR_OVERFLOW ); /* Write the PKI header wrapper, version information, and sender and recipient names if there's name information present */ writeSequence( stream, totalLength ); writeShortInteger( stream, CMP_VERSION, DEFAULT_TAG ); if( sendFullHeader ) { writeConstructed( stream, senderNameLength, 4 ); if( senderNameObject != CRYPT_ERROR ) { status = exportAttributeToStream( stream, senderNameObject, CRYPT_IATTRIBUTE_SUBJECT ); if( cryptStatusError( status ) ) return( status ); } else writeSequence( stream, 0 ); writeConstructed( stream, recipNameLength, 4 ); if( recipNameObject != CRYPT_ERROR ) { status = exportAttributeToStream( stream, recipNameObject, CRYPT_IATTRIBUTE_SUBJECT ); } else status = writeSequence( stream, 0 ); } else { /* This is one of the portions of CMP where an optional field is marked as mandatory, to balance out the mandatory fields that are marked as optional. To work around this we write the names as zero-length DNs */ writeConstructed( stream, senderNameLength, 4 ); writeSequence( stream, 0 ); writeConstructed( stream, recipNameLength, 4 ); status = writeSequence( stream, 0 ); } if( cryptStatusError( status ) ) return( status ); /* Write the protection information, assorted nonces and IDs, and extra information that the other side may be able to make use of */ writeConstructed( stream, protInfoLength, CTAG_PH_PROTECTIONALGO ); if( protocolInfo->useMACsend ) { status = writeMacInfo( stream, protocolInfo, sendMacInfo ); sessionInfoPtr->protocolFlags |= CMP_PFLAG_MACINFOSENT; } else { status = writeContextAlgoID( stream, protocolInfo->authContext, protocolInfo->hashAlgo ); } if( cryptStatusError( status ) ) return( status ); if( sendUserID ) { /* We're using full headers or we're the client sending our first message, identify the sender key */ writeConstructed( stream, objSize( protocolInfo->userIDsize ), CTAG_PH_SENDERKID ); writeOctetString( stream, protocolInfo->userID, protocolInfo->userIDsize, DEFAULT_TAG ); DEBUG_PRINT(( "%s: Writing userID.\n", protocolInfo->isServer ? "SVR" : "CLI" )); DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI", protocolInfo->userID, protocolInfo->userIDsize ); sessionInfoPtr->protocolFlags |= CMP_PFLAG_USERIDSENT; } writeConstructed( stream, objSize( protocolInfo->transIDsize ), CTAG_PH_TRANSACTIONID ); status = writeOctetString( stream, protocolInfo->transID, protocolInfo->transIDsize, DEFAULT_TAG ); if( cryptStatusError( status ) ) return( status ); if( sendFullHeader ) { if( protocolInfo->senderNonceSize > 0 ) { /* We're using nonces, generate a new sender nonce (the initial nonce will have been set when the protocol state was initialised) */ setMessageData( &msgData, protocolInfo->senderNonce, protocolInfo->senderNonceSize ); krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE ); writeConstructed( stream, objSize( protocolInfo->senderNonceSize ), CTAG_PH_SENDERNONCE ); status = writeOctetString( stream, protocolInfo->senderNonce, protocolInfo->senderNonceSize, DEFAULT_TAG ); } if( protocolInfo->recipNonceSize > 0 ) { writeConstructed( stream, objSize( protocolInfo->recipNonceSize ), CTAG_PH_RECIPNONCE ); status = writeOctetString( stream, protocolInfo->recipNonce, protocolInfo->recipNonceSize, DEFAULT_TAG ); } } if( cryptStatusError( status ) ) return( status ); if( attributeLength > 0 ) { ENSURES( sendClibID || sendCertID ); /* We haven't sent any messages yet, let the other side know that we're running cryptlib and identify our signing certificate as required */ writeConstructed( stream, objSize( attributeLength ), CTAG_PH_GENERALINFO ); writeSequence( stream, attributeLength ); if( sendClibID ) { sessionInfoPtr->protocolFlags |= CMP_PFLAG_CLIBIDSENT; writeSequence( stream, sizeofOID( OID_CRYPTLIB_PRESENCECHECK ) + \ sizeofObject( 0 ) ); writeOID( stream, OID_CRYPTLIB_PRESENCECHECK ); status = writeSet( stream, 0 ); } if( sendCertID ) { sessionInfoPtr->protocolFlags |= CMP_PFLAG_CERTIDSENT; status = writeCertID( stream, protocolInfo->authContext ); } } return( status ); }