static int enableSidechannelProtection( INOUT PKC_INFO *pkcInfo, IN_ALGO const CRYPT_ALGO_TYPE cryptAlgo ) { assert( isWritePtr( pkcInfo, sizeof( PKC_INFO ) ) ); REQUIRES( isEccAlgo( cryptAlgo ) ); /* Use constant-time modexp() to protect the private key from timing channels. (There really isn't much around in the way of side-channel protection for the ECC computations, for every operation we use a new random secret value as the point multiplier so there doesn't seem to be much scope for timing attacks. In the absence of anything better to do, we set the constant-time modexp() flag just for warm fuzzies) */ BN_set_flags( &pkcInfo->eccParam_d, BN_FLG_EXP_CONSTTIME ); /* Checksum the bignums to try and detect fault attacks. Since we're setting the checksum at this point there's no need to check the return value */ ( void ) calculateBignumChecksum( pkcInfo, cryptAlgo ); return( CRYPT_OK ); }
OUT PKC_INFO *contextData, const CAPABILITY_INFO *capabilityInfoPtr, IN_BUFFER( publicKeyDataLength ) \ const void *publicKeyData, IN_LENGTH_SHORT_MIN( MIN_PKCSIZE ) \ const int publicKeyDataLength ) { STREAM stream; int status; assert( isWritePtr( staticContextInfo, sizeof( CONTEXT_INFO ) ) ); assert( isWritePtr( contextData, sizeof( PKC_INFO ) ) ); assert( isReadPtr( capabilityInfoPtr, sizeof( CAPABILITY_INFO ) ) ); assert( isReadPtr( publicKeyData, publicKeyDataLength ) ); REQUIRES( ( isEccAlgo( capabilityInfoPtr->cryptAlgo ) && \ publicKeyDataLength >= MIN_PKCSIZE_ECCPOINT && \ publicKeyDataLength < MAX_INTLENGTH_SHORT ) || \ ( !isEccAlgo( capabilityInfoPtr->cryptAlgo ) && \ publicKeyDataLength >= MIN_PKCSIZE && \ publicKeyDataLength < MAX_INTLENGTH_SHORT ) ); /* Initialise a static context to read the key data into */ status = staticInitContext( staticContextInfo, CONTEXT_PKC, capabilityInfoPtr, contextData, sizeof( PKC_INFO ), NULL ); if( cryptStatusError( status ) ) return( status ); /* Read the key data into the static context and calculate the keyIDs. We can do this now that the data is in a native context rather than
BOOLEAN sanityCheckCapability( const CAPABILITY_INFO *capabilityInfoPtr ) { CRYPT_ALGO_TYPE cryptAlgo = capabilityInfoPtr->cryptAlgo; assert( isReadPtr( capabilityInfoPtr, sizeof( CAPABILITY_INFO ) ) ); /* Check the algorithm and mode parameters. We check for an algorithm name one shorter than the maximum because as returned to an external caller it's an ASCIZ string so we need to allow room for the terminator */ if( cryptAlgo <= CRYPT_ALGO_NONE || cryptAlgo >= CRYPT_ALGO_LAST ) return( FALSE ); if( capabilityInfoPtr->algoName == NULL || \ capabilityInfoPtr->algoNameLen < 3 || \ capabilityInfoPtr->algoNameLen > CRYPT_MAX_TEXTSIZE - 1 ) return( FALSE ); /* Make sure that the minimum functions are present. We don't check for the presence of the keygen function since the symmetric capabilities use the generic keygen and the hash capabilities don't do keygen at all */ if( capabilityInfoPtr->selfTestFunction == NULL || \ capabilityInfoPtr->getInfoFunction == NULL ) return( FALSE ); if( !sanityCheckFunctionality( capabilityInfoPtr, cryptAlgo ) ) return( FALSE ); /* Make sure that the algorithm/mode-specific parameters are consistent */ if( capabilityInfoPtr->minKeySize > capabilityInfoPtr->keySize || \ capabilityInfoPtr->maxKeySize < capabilityInfoPtr->keySize ) return( FALSE ); if( isConvAlgo( cryptAlgo ) ) { if( ( capabilityInfoPtr->blockSize < bitsToBytes( 8 ) || \ capabilityInfoPtr->blockSize > CRYPT_MAX_IVSIZE ) || \ ( capabilityInfoPtr->minKeySize < MIN_KEYSIZE || \ capabilityInfoPtr->maxKeySize > CRYPT_MAX_KEYSIZE ) ) return( FALSE ); if( capabilityInfoPtr->keySize > MAX_WORKING_KEYSIZE ) return( FALSE ); /* Requirement for key wrap */ if( capabilityInfoPtr->initParamsFunction == NULL || \ capabilityInfoPtr->initKeyFunction == NULL ) return( FALSE ); if( !isStreamCipher( cryptAlgo ) && \ capabilityInfoPtr->blockSize < bitsToBytes( 64 ) ) return( FALSE ); return( TRUE ); } /* Check any remaining algorithm types */ if( isPkcAlgo( cryptAlgo ) ) { const int minKeySize = isEccAlgo( cryptAlgo ) ? \ MIN_PKCSIZE_ECC : MIN_PKCSIZE; if( capabilityInfoPtr->blockSize != 0 || \ ( capabilityInfoPtr->minKeySize < minKeySize || \ capabilityInfoPtr->maxKeySize > CRYPT_MAX_PKCSIZE ) ) return( FALSE ); if( capabilityInfoPtr->initKeyFunction == NULL || \ capabilityInfoPtr->generateKeyFunction == NULL ) return( FALSE ); return( TRUE ); } if( isHashAlgo( cryptAlgo ) ) { if( ( capabilityInfoPtr->blockSize < bitsToBytes( 128 ) || \ capabilityInfoPtr->blockSize > CRYPT_MAX_HASHSIZE ) || \ ( capabilityInfoPtr->minKeySize != 0 || \ capabilityInfoPtr->keySize != 0 || \ capabilityInfoPtr->maxKeySize != 0 ) ) return( FALSE ); return( TRUE ); } if( isMacAlgo( cryptAlgo ) ) { if( ( capabilityInfoPtr->blockSize < bitsToBytes( 128 ) || \ capabilityInfoPtr->blockSize > CRYPT_MAX_HASHSIZE ) || \ ( capabilityInfoPtr->minKeySize < MIN_KEYSIZE || \ capabilityInfoPtr->maxKeySize > CRYPT_MAX_KEYSIZE ) ) return( FALSE ); if( capabilityInfoPtr->keySize > MAX_WORKING_KEYSIZE ) return( FALSE ); /* Requirement for key wrap */ if( capabilityInfoPtr->initKeyFunction == NULL ) return( FALSE ); return( TRUE ); } if( isSpecialAlgo( cryptAlgo ) ) { if( capabilityInfoPtr->blockSize != 0 || \ capabilityInfoPtr->minKeySize < bitsToBytes( 128 ) || \ capabilityInfoPtr->maxKeySize > CRYPT_MAX_KEYSIZE ) return( FALSE ); if( capabilityInfoPtr->initKeyFunction == NULL ) return( FALSE ); return( TRUE ); } retIntError_Boolean(); }
int exchangeServerKeys( INOUT SESSION_INFO *sessionInfoPtr, INOUT SSL_HANDSHAKE_INFO *handshakeInfo ) { STREAM *stream = &handshakeInfo->stream; int length, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); /* Read the response from the client and, if we're expecting a client certificate, make sure that it's present */ status = readHSPacketSSL( sessionInfoPtr, handshakeInfo, &length, SSL_MSG_HANDSHAKE ); if( cryptStatusError( status ) ) return( status ); sMemConnect( stream, sessionInfoPtr->receiveBuffer, length ); if( clientCertAuthRequired( sessionInfoPtr ) ) { /* Read the client certificate chain and make sure that the certificate being presented is valid for access */ status = readCheckClientCerts( sessionInfoPtr, handshakeInfo, stream ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } /* Read the next packet(s) if necessary */ status = refreshHSStream( sessionInfoPtr, handshakeInfo ); if( cryptStatusError( status ) ) return( status ); } /* Process the client key exchange packet: byte ID = SSL_HAND_CLIENT_KEYEXCHANGE uint24 len DH: uint16 yLen byte[] y ECDH: uint16 ecPointLen byte[] ecPoint PSK: uint16 userIDLen byte[] userID RSA: [ uint16 encKeyLen -- TLS 1.x ] byte[] rsaPKCS1( byte[2] { 0x03, 0x0n } || byte[46] random ) */ status = checkHSPacketHeader( sessionInfoPtr, stream, &length, SSL_HAND_CLIENT_KEYEXCHANGE, UINT16_SIZE + 1 ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } if( isKeyxAlgo( handshakeInfo->keyexAlgo ) ) { KEYAGREE_PARAMS keyAgreeParams; const BOOLEAN isECC = isEccAlgo( handshakeInfo->keyexAlgo ); memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) ); if( isECC ) { status = readEcdhValue( stream, keyAgreeParams.publicValue, CRYPT_MAX_PKCSIZE, &keyAgreeParams.publicValueLen ); } else { status = readInteger16UChecked( stream, keyAgreeParams.publicValue, &keyAgreeParams.publicValueLen, MIN_PKCSIZE, CRYPT_MAX_PKCSIZE ); } if( cryptStatusError( status ) ) { sMemDisconnect( stream ); /* Some misconfigured clients may use very short keys, we perform a special-case check for these and return a more specific message than the generic bad-data error */ if( status == CRYPT_ERROR_NOSECURE ) { retExt( CRYPT_ERROR_NOSECURE, ( CRYPT_ERROR_NOSECURE, SESSION_ERRINFO, "Insecure key used in key exchange" ) ); } retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Invalid DH/ECDH phase 2 key agreement data" ) ); } /* Perform phase 2 of the DH/ECDH key agreement */ status = krnlSendMessage( handshakeInfo->dhContext, IMESSAGE_CTX_DECRYPT, &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ); if( cryptStatusError( status ) ) { zeroise( &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ); sMemDisconnect( stream ); retExt( status, ( status, SESSION_ERRINFO, "Invalid DH/ECDH phase 2 key agreement value" ) ); } if( isECC ) { const int xCoordLen = ( keyAgreeParams.wrappedKeyLen - 1 ) / 2; /* The output of the ECDH operation is an ECC point, but for some unknown reason TLS only uses the x coordinate and not the full point. To work around this we have to rewrite the point as a standalone x coordinate, which is relatively easy because we're using an "uncompressed" point format: +---+---------------+---------------+ |04 | qx | qy | +---+---------------+---------------+ |<- fldSize --> |<- fldSize --> | */ REQUIRES( keyAgreeParams.wrappedKeyLen >= MIN_PKCSIZE_ECCPOINT && \ keyAgreeParams.wrappedKeyLen <= MAX_PKCSIZE_ECCPOINT && \ ( keyAgreeParams.wrappedKeyLen & 1 ) == 1 && \ keyAgreeParams.wrappedKey[ 0 ] == 0x04 ); memmove( keyAgreeParams.wrappedKey, keyAgreeParams.wrappedKey + 1, xCoordLen ); keyAgreeParams.wrappedKeyLen = xCoordLen; } ENSURES( rangeCheckZ( 0, keyAgreeParams.wrappedKeyLen, CRYPT_MAX_PKCSIZE + CRYPT_MAX_TEXTSIZE ) ); memcpy( handshakeInfo->premasterSecret, keyAgreeParams.wrappedKey, keyAgreeParams.wrappedKeyLen ); handshakeInfo->premasterSecretSize = keyAgreeParams.wrappedKeyLen; zeroise( &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ); } else { if( handshakeInfo->authAlgo == CRYPT_ALGO_NONE ) { const ATTRIBUTE_LIST *attributeListPtr; BYTE userID[ CRYPT_MAX_TEXTSIZE + 8 ]; /* Read the client user ID and make sure that it's a valid user. Handling non-valid users is somewhat problematic, we can either bail out immediately or invent a fake password for the (non-)user and continue with that. The problem with this is that it doesn't really help hide whether the user is valid or not due to the fact that we're still vulnerable to a timing attack because it takes considerably longer to generate the random password than it does to read a fixed password string from memory, so an attacker can tell from the timing whether the username is valid or not. In addition usability research on real-world users indicates that this actually reduces security while having little to no tangible benefit. Because of this we don't try and fake out the valid/invalid user name indication but just exit immediately if an invalid name is found */ length = readUint16( stream ); if( length < 1 || length > CRYPT_MAX_TEXTSIZE || \ cryptStatusError( sread( stream, userID, length ) ) ) { sMemDisconnect( stream ); retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Invalid client user ID" ) ); } attributeListPtr = \ findSessionInfoEx( sessionInfoPtr->attributeList, CRYPT_SESSINFO_USERNAME, userID, length ); if( attributeListPtr == NULL ) { sMemDisconnect( stream ); retExt( CRYPT_ERROR_WRONGKEY, ( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO, "Unknown user name '%s'", sanitiseString( userID, CRYPT_MAX_TEXTSIZE, length ) ) ); } /* Select the attribute with the user ID and move on to the associated password */ sessionInfoPtr->attributeListCurrent = \ ( ATTRIBUTE_LIST * ) attributeListPtr; attributeListPtr = attributeListPtr->next; ENSURES( attributeListPtr != NULL && \ attributeListPtr->attributeID == CRYPT_SESSINFO_PASSWORD ); /* Create the shared premaster secret from the user password */ status = createSharedPremasterSecret( \ handshakeInfo->premasterSecret, CRYPT_MAX_PKCSIZE + CRYPT_MAX_TEXTSIZE, &handshakeInfo->premasterSecretSize, attributeListPtr->value, attributeListPtr->valueLength, ( attributeListPtr->flags & ATTR_FLAG_ENCODEDVALUE ) ? \ TRUE : FALSE ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); retExt( status, ( status, SESSION_ERRINFO, "Couldn't create master secret from shared " "secret/password value" ) ); } } else { BYTE wrappedKey[ CRYPT_MAX_PKCSIZE + 8 ]; if( sessionInfoPtr->version <= SSL_MINOR_VERSION_SSL ) { /* The original Netscape SSL implementation didn't provide a length for the encrypted key and everyone copied that so it became the de facto standard way to do it (sic faciunt omnes. The spec itself is ambiguous on the topic). This was fixed in TLS (although the spec is still ambigous) so the encoding differs slightly between SSL and TLS. To work around this we have to duplicate a certain amount of the integer-read code here */ if( isShortPKCKey( length ) ) status = CRYPT_ERROR_NOSECURE; else { if( length < MIN_PKCSIZE || length > CRYPT_MAX_PKCSIZE || \ cryptStatusError( sread( stream, wrappedKey, length ) ) ) status = CRYPT_ERROR_BADDATA; } } else { status = readInteger16UChecked( stream, wrappedKey, &length, MIN_PKCSIZE, CRYPT_MAX_PKCSIZE ); } if( cryptStatusError( status ) ) { sMemDisconnect( stream ); /* Some misconfigured clients may use very short keys, we perform a special-case check for these and return a more specific message than the generic bad-data */ if( status == CRYPT_ERROR_NOSECURE ) { retExt( CRYPT_ERROR_NOSECURE, ( CRYPT_ERROR_NOSECURE, SESSION_ERRINFO, "Insecure key used in key exchange" ) ); } retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Invalid RSA encrypted key data" ) ); } /* Decrypt the pre-master secret */ status = unwrapPremasterSecret( sessionInfoPtr, handshakeInfo, wrappedKey, length ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } } } /* If we're expecting a client certificate, process the client certificate verify */ if( clientCertAuthRequired( sessionInfoPtr ) ) { const BOOLEAN isECC = isEccAlgo( handshakeInfo->keyexAlgo ); /* Since the client certificate-verify message requires the hash of all handshake packets up to this point, we have to interrupt the processing to calculate the hash before we continue */ status = createCertVerifyHash( sessionInfoPtr, handshakeInfo ); if( cryptStatusError( status ) ) return( status ); /* Read the next packet(s) if necessary */ status = refreshHSStream( sessionInfoPtr, handshakeInfo ); if( cryptStatusError( status ) ) return( status ); /* Process the client certificate verify packet: byte ID = SSL_HAND_CLIENT_CERTVERIFY uint24 len byte[] signature */ status = checkHSPacketHeader( sessionInfoPtr, stream, &length, SSL_HAND_CLIENT_CERTVERIFY, isECC ? MIN_PKCSIZE_ECCPOINT : \ MIN_PKCSIZE ); if( cryptStatusOK( status ) ) status = checkCertVerify( sessionInfoPtr, handshakeInfo, stream, length ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } } sMemDisconnect( stream ); return( CRYPT_OK ); }
int beginServerHandshake( INOUT SESSION_INFO *sessionInfoPtr, INOUT SSL_HANDSHAKE_INFO *handshakeInfo ) { STREAM *stream = &handshakeInfo->stream; SCOREBOARD_LOOKUP_RESULT lookupResult = DUMMY_INIT_STRUCT; MESSAGE_DATA msgData; int length, resumedSessionID = CRYPT_ERROR; int packetOffset, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); /* Read and process the client hello */ status = readHSPacketSSL( sessionInfoPtr, handshakeInfo, &length, SSL_MSG_FIRST_HANDSHAKE ); if( cryptStatusError( status ) ) return( status ); sMemConnect( stream, sessionInfoPtr->receiveBuffer, length ); status = processHelloSSL( sessionInfoPtr, handshakeInfo, stream, TRUE ); sMemDisconnect( stream ); if( cryptStatusError( status ) ) { if( status != OK_SPECIAL ) return( status ); /* The client has sent us a sessionID in an attempt to resume a previous session, see if it's in the session cache */ resumedSessionID = \ lookupScoreboardEntry( sessionInfoPtr->sessionSSL->scoreboardInfoPtr, SCOREBOARD_KEY_SESSIONID_SVR, handshakeInfo->sessionID, handshakeInfo->sessionIDlength, &lookupResult ); #ifdef CONFIG_SUITEB_TESTS resumedSessionID = CRYPT_ERROR; /* Disable for Suite B tests */ #endif /* CONFIG_SUITEB_TESTS */ } /* Handle session resumption. If it's a new session or the session data has expired from the cache, generate a new session ID */ if( cryptStatusError( resumedSessionID ) ) { setMessageData( &msgData, handshakeInfo->sessionID, SESSIONID_SIZE ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE ); if( cryptStatusError( status ) ) return( status ); handshakeInfo->sessionIDlength = SESSIONID_SIZE; } else { /* We're resuming a previous session, remember the premaster secret */ status = attributeCopyParams( handshakeInfo->premasterSecret, SSL_SECRET_SIZE, &handshakeInfo->premasterSecretSize, lookupResult.data, lookupResult.dataSize ); ENSURES( cryptStatusOK( status ) ); } /* Get the nonce that's used to randomise all crypto operations and set up the server DH/ECDH context if necessary */ setMessageData( &msgData, handshakeInfo->serverNonce, SSL_NONCE_SIZE ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE ); if( cryptStatusOK( status ) && isKeyxAlgo( handshakeInfo->keyexAlgo ) ) { status = initDHcontextSSL( &handshakeInfo->dhContext, NULL, 0, ( handshakeInfo->authAlgo != CRYPT_ALGO_NONE ) ? \ sessionInfoPtr->privateKey : CRYPT_UNUSED, isEccAlgo( handshakeInfo->keyexAlgo ) ? \ handshakeInfo->eccCurveID : CRYPT_ECCCURVE_NONE ); } if( cryptStatusError( status ) ) return( status ); /* Build the server hello, certificate, optional certificate request, and done packets: byte ID = SSL_HAND_SERVER_HELLO uint24 len byte[2] version = { 0x03, 0x0n } byte[32] nonce byte sessIDlen byte[] sessID uint16 suite byte copr = 0 [ uint16 extListLen -- RFC 3546/RFC 4366 byte extType uint16 extLen byte[] extData ] ... We have to be careful how we handle extensions because the RFC makes the rather optimistic assumption that implementations can handle the presence of unexpected data at the end of the hello packet, to avoid problems with this we avoid sending extensions unless they're in response to extensions already sent by the client */ status = openPacketStreamSSL( stream, sessionInfoPtr, CRYPT_USE_DEFAULT, SSL_MSG_HANDSHAKE ); if( cryptStatusError( status ) ) return( status ); status = continueHSPacketStream( stream, SSL_HAND_SERVER_HELLO, &packetOffset ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } sputc( stream, SSL_MAJOR_VERSION ); sputc( stream, sessionInfoPtr->version ); swrite( stream, handshakeInfo->serverNonce, SSL_NONCE_SIZE ); sputc( stream, handshakeInfo->sessionIDlength ); swrite( stream, handshakeInfo->sessionID, handshakeInfo->sessionIDlength ); writeUint16( stream, handshakeInfo->cipherSuite ); status = sputc( stream, 0 ); /* No compression */ if( handshakeInfo->hasExtensions ) status = writeServerExtensions( stream, handshakeInfo ); if( cryptStatusOK( status ) ) status = completeHSPacketStream( stream, packetOffset ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } /* If it's a resumed session then the server hello is followed immediately by the change cipherspec, which is sent by the shared handshake completion code */ if( !cryptStatusError( resumedSessionID ) ) { status = completePacketStreamSSL( stream, 0 ); if( cryptStatusOK( status ) ) status = hashHSPacketWrite( handshakeInfo, stream, 0 ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } /* Tell the caller that it's a resumed session, leaving the stream open in order to write the change cipherspec message that follows the server hello in a resumed session */ DEBUG_PRINT(( "Resuming session with client based on " "sessionID = \n" )); DEBUG_DUMP_DATA( handshakeInfo->sessionID, handshakeInfo->sessionIDlength ); return( OK_SPECIAL ); } /* ... (optional server supplemental data) byte ID = SSL_HAND_SUPPLEMENTAL_DATA uint24 len uint16 type uint16 len byte[] value ... */ /* ... (optional server certificate chain) ... */ if( handshakeInfo->authAlgo != CRYPT_ALGO_NONE ) { status = writeSSLCertChain( sessionInfoPtr, stream ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } } /* ... (optional server keyex) byte ID = SSL_HAND_SERVER_KEYEXCHANGE uint24 len DH: uint16 dh_pLen byte[] dh_p uint16 dh_gLen byte[] dh_g uint16 dh_YsLen byte[] dh_Ys [ byte hashAlgoID -- TLS 1.2 ] [ byte sigAlgoID -- TLS 1.2 ] uint16 signatureLen byte[] signature ECDH: byte curveType uint16 namedCurve uint8 ecPointLen -- NB uint8 not uint16 byte[] ecPoint [ byte hashAlgoID -- TLS 1.2 ] [ byte sigAlgoID -- TLS 1.2 ] uint16 signatureLen byte[] signature */ if( isKeyxAlgo( handshakeInfo->keyexAlgo ) ) { KEYAGREE_PARAMS keyAgreeParams; void *keyData = DUMMY_INIT_PTR; int keyDataOffset, keyDataLength = DUMMY_INIT; /* Perform phase 1 of the DH/ECDH key agreement process */ memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) ); status = krnlSendMessage( handshakeInfo->dhContext, IMESSAGE_CTX_ENCRYPT, &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ); if( cryptStatusError( status ) ) { zeroise( &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ); sMemDisconnect( stream ); return( status ); } /* Write the DH/ECDH key parameters and public value and sign them */ status = continueHSPacketStream( stream, SSL_HAND_SERVER_KEYEXCHANGE, &packetOffset ); if( cryptStatusError( status ) ) { zeroise( &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ); sMemDisconnect( stream ); return( status ); } keyDataOffset = stell( stream ); status = exportAttributeToStream( stream, handshakeInfo->dhContext, CRYPT_IATTRIBUTE_KEY_SSL ); if( cryptStatusOK( status ) ) { if( isEccAlgo( handshakeInfo->keyexAlgo ) ) { sputc( stream, keyAgreeParams.publicValueLen ); swrite( stream, keyAgreeParams.publicValue, keyAgreeParams.publicValueLen ); } else { status = writeInteger16U( stream, keyAgreeParams.publicValue, keyAgreeParams.publicValueLen ); } } if( cryptStatusOK( status ) ) { keyDataLength = stell( stream ) - keyDataOffset; status = sMemGetDataBlockAbs( stream, keyDataOffset, &keyData, keyDataLength ); } if( cryptStatusOK( status ) ) { status = createKeyexSignature( sessionInfoPtr, handshakeInfo, stream, keyData, keyDataLength ); } zeroise( &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ); if( cryptStatusOK( status ) ) status = completeHSPacketStream( stream, packetOffset ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } } /* ... (optional request for client certificate authentication) byte ID = SSL_HAND_SERVER_CERTREQUEST uint24 len byte certTypeLen byte[] certType = { RSA, DSA, ECDSA } [ uint16 sigHashListLen -- TLS 1.2 ] [ byte hashAlgoID -- TLS 1.2 ] [ byte sigAlgoID -- TLS 1.2 ] uint16 caNameListLen = 4 uint16 caNameLen = 2 byte[] caName = { 0x30, 0x00 } ... */ if( clientCertAuthRequired( sessionInfoPtr ) ) { status = continueHSPacketStream( stream, SSL_HAND_SERVER_CERTREQUEST, &packetOffset ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } status = writeCertRequest( sessionInfoPtr, handshakeInfo, stream ); if( cryptStatusOK( status ) ) status = completeHSPacketStream( stream, packetOffset ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } } /* ... byte ID = SSL_HAND_SERVER_HELLODONE uint24 len = 0 */ status = continueHSPacketStream( stream, SSL_HAND_SERVER_HELLODONE, &packetOffset ); if( cryptStatusOK( status ) ) status = completeHSPacketStream( stream, packetOffset ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } /* Send the combined server packets to the client. We perform the assorted hashing of the packets in between the network ops where it's effectively free */ status = sendPacketSSL( sessionInfoPtr, stream, FALSE ); if( cryptStatusOK( status ) ) status = hashHSPacketWrite( handshakeInfo, stream, 0 ); sMemDisconnect( stream ); return( status ); }
BOOLEAN sanityCheckCapability( const CAPABILITY_INFO *capabilityInfoPtr, const BOOLEAN asymmetricOK ) { CRYPT_ALGO_TYPE cryptAlgo = capabilityInfoPtr->cryptAlgo; assert( isReadPtr( capabilityInfoPtr, sizeof( CAPABILITY_INFO ) ) ); /* Check the algorithm and mode parameters. We check for an algorithm name one shorter than the maximum because as returned to an external caller it's an ASCIZ string so we need to allow room for the terminator */ if( cryptAlgo <= CRYPT_ALGO_NONE || cryptAlgo >= CRYPT_ALGO_LAST || \ capabilityInfoPtr->algoName == NULL || \ capabilityInfoPtr->algoNameLen < 3 || \ capabilityInfoPtr->algoNameLen > CRYPT_MAX_TEXTSIZE - 1 ) return( FALSE ); /* Make sure that the minimum functions are present. We don't check for the presence of the keygen function since the symmetric capabilities use the generic keygen and the hash capabilities don't do keygen at all */ if( capabilityInfoPtr->selfTestFunction == NULL || \ capabilityInfoPtr->getInfoFunction == NULL ) return( FALSE ); if( isStreamCipher( cryptAlgo ) ) { if( capabilityInfoPtr->encryptOFBFunction == NULL || \ capabilityInfoPtr->decryptOFBFunction == NULL ) return( FALSE ); } else { if( asymmetricOK ) { /* If asymmetric capabilities (e.g. decrypt but not encrypt, present in some tinkertoy tokens) are permitted then we only check that there's at least one useful capability available */ if( capabilityInfoPtr->decryptFunction == NULL && \ capabilityInfoPtr->signFunction == NULL ) return( FALSE ); } else { if( !isSpecialAlgo( cryptAlgo ) ) { /* We need at least one mechanism pair to be able to do anything useful with the capability */ if( ( capabilityInfoPtr->encryptFunction == NULL || \ capabilityInfoPtr->decryptFunction == NULL ) && \ ( capabilityInfoPtr->encryptCBCFunction == NULL || \ capabilityInfoPtr->decryptCBCFunction == NULL ) && \ ( capabilityInfoPtr->encryptCFBFunction == NULL || \ capabilityInfoPtr->decryptCFBFunction == NULL ) && \ ( capabilityInfoPtr->encryptOFBFunction == NULL || \ capabilityInfoPtr->decryptOFBFunction == NULL ) && \ ( capabilityInfoPtr->encryptGCMFunction == NULL || \ capabilityInfoPtr->decryptGCMFunction == NULL ) && \ ( capabilityInfoPtr->signFunction == NULL || \ capabilityInfoPtr->sigCheckFunction == NULL ) ) return( FALSE ); } } } /* Make sure that the algorithm/mode-specific parameters are consistent */ if( capabilityInfoPtr->minKeySize > capabilityInfoPtr->keySize || \ capabilityInfoPtr->maxKeySize < capabilityInfoPtr->keySize ) return( FALSE ); if( isConvAlgo( cryptAlgo ) ) { if( ( capabilityInfoPtr->blockSize < bitsToBytes( 8 ) || \ capabilityInfoPtr->blockSize > CRYPT_MAX_IVSIZE ) || \ ( capabilityInfoPtr->minKeySize < MIN_KEYSIZE || \ capabilityInfoPtr->maxKeySize > CRYPT_MAX_KEYSIZE ) ) return( FALSE ); if( capabilityInfoPtr->keySize > MAX_WORKING_KEYSIZE ) return( FALSE ); /* Requirement for key wrap */ if( capabilityInfoPtr->initParamsFunction == NULL || \ capabilityInfoPtr->initKeyFunction == NULL ) return( FALSE ); if( !isStreamCipher( cryptAlgo ) && \ capabilityInfoPtr->blockSize < bitsToBytes( 64 ) ) return( FALSE ); if( ( capabilityInfoPtr->encryptCBCFunction != NULL && \ capabilityInfoPtr->decryptCBCFunction == NULL ) || \ ( capabilityInfoPtr->encryptCBCFunction == NULL && \ capabilityInfoPtr->decryptCBCFunction != NULL ) ) return( FALSE ); if( ( capabilityInfoPtr->encryptCFBFunction != NULL && \ capabilityInfoPtr->decryptCFBFunction == NULL ) || \ ( capabilityInfoPtr->encryptCFBFunction == NULL && \ capabilityInfoPtr->decryptCFBFunction != NULL ) ) return( FALSE ); if( ( capabilityInfoPtr->encryptOFBFunction != NULL && \ capabilityInfoPtr->decryptOFBFunction == NULL ) || \ ( capabilityInfoPtr->encryptOFBFunction == NULL && \ capabilityInfoPtr->decryptOFBFunction != NULL ) ) return( FALSE ); if( ( capabilityInfoPtr->encryptGCMFunction != NULL && \ capabilityInfoPtr->decryptGCMFunction == NULL ) || \ ( capabilityInfoPtr->encryptGCMFunction == NULL && \ capabilityInfoPtr->decryptGCMFunction != NULL ) ) return( FALSE ); return( TRUE ); } /* We've checked the conventional algorithms, beyond this point there shouldn't be any conventional encryption modes present */ if( capabilityInfoPtr->encryptCBCFunction != NULL || \ capabilityInfoPtr->decryptCBCFunction != NULL || \ capabilityInfoPtr->encryptCFBFunction != NULL || \ capabilityInfoPtr->decryptCFBFunction != NULL || \ capabilityInfoPtr->encryptOFBFunction != NULL || \ capabilityInfoPtr->decryptOFBFunction != NULL || \ capabilityInfoPtr->encryptGCMFunction != NULL || \ capabilityInfoPtr->decryptGCMFunction != NULL ) return( FALSE ); /* Check any remaining algorithm types */ if( isPkcAlgo( cryptAlgo ) ) { const int minKeySize = isEccAlgo( cryptAlgo ) ? \ MIN_PKCSIZE_ECC : MIN_PKCSIZE; if( capabilityInfoPtr->blockSize != 0 || \ ( capabilityInfoPtr->minKeySize < minKeySize || \ capabilityInfoPtr->maxKeySize > CRYPT_MAX_PKCSIZE ) ) return( FALSE ); if( capabilityInfoPtr->initKeyFunction == NULL || \ capabilityInfoPtr->generateKeyFunction == NULL ) return( FALSE ); return( TRUE ); } if( isHashAlgo( cryptAlgo ) ) { if( ( capabilityInfoPtr->blockSize < bitsToBytes( 128 ) || \ capabilityInfoPtr->blockSize > CRYPT_MAX_HASHSIZE ) || \ ( capabilityInfoPtr->minKeySize != 0 || \ capabilityInfoPtr->keySize != 0 || \ capabilityInfoPtr->maxKeySize != 0 ) ) return( FALSE ); return( TRUE ); } if( isMacAlgo( cryptAlgo ) ) { if( ( capabilityInfoPtr->blockSize < bitsToBytes( 128 ) || \ capabilityInfoPtr->blockSize > CRYPT_MAX_HASHSIZE ) || \ ( capabilityInfoPtr->minKeySize < MIN_KEYSIZE || \ capabilityInfoPtr->maxKeySize > CRYPT_MAX_KEYSIZE ) ) return( FALSE ); if( capabilityInfoPtr->keySize > MAX_WORKING_KEYSIZE ) return( FALSE ); /* Requirement for key wrap */ if( capabilityInfoPtr->initKeyFunction == NULL ) return( FALSE ); return( TRUE ); } if( isSpecialAlgo( cryptAlgo ) ) { if( capabilityInfoPtr->blockSize != 0 || \ capabilityInfoPtr->minKeySize < bitsToBytes( 128 ) || \ capabilityInfoPtr->maxKeySize > CRYPT_MAX_KEYSIZE ) return( FALSE ); if( capabilityInfoPtr->encryptFunction != NULL || \ capabilityInfoPtr->decryptFunction != NULL ) return( FALSE ); if( capabilityInfoPtr->initKeyFunction == NULL ) return( FALSE ); return( TRUE ); } retIntError_Boolean(); }