/**************************************************************************** * * * cryptlib SSL v3/TLS Crypto Routines * * Copyright Peter Gutmann 1998-2010 * * * ****************************************************************************/ #if defined( INC_ALL ) #include "crypt.h" #include "misc_rw.h" #include "session.h" #include "ssl.h" #else #include "crypt.h" #include "enc_dec/misc_rw.h" #include "session/session.h" #include "session/ssl.h" #endif /* Compiler-specific includes */ #ifdef USE_SSL /**************************************************************************** * * * Utility Functions * * * ****************************************************************************/ /* Write packet metadata for input to the MAC/ICV authentication process: seq_num || type || version || length */ CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \ static int writePacketMetadata( OUT_BUFFER( dataMaxLength, *dataLength ) \ void *data, IN_LENGTH_SHORT_MIN( 16 ) \ const int dataMaxLength, OUT_LENGTH_BOUNDED_Z( dataMaxLength ) \ int *dataLength, IN_RANGE( 0, 255 ) const int type, IN_INT_Z const long seqNo, IN_RANGE( SSL_MINOR_VERSION_TLS, \ SSL_MINOR_VERSION_TLS12 ) \ const int version, IN_LENGTH_Z const int payloadLength ) { STREAM stream; int status; assert( isWritePtr( data, dataMaxLength ) ); assert( isWritePtr( dataLength, sizeof( int ) ) ); REQUIRES( dataMaxLength >= 16 && dataMaxLength < MAX_INTLENGTH_SHORT ); REQUIRES( type >= 0 && type <= 255 ); REQUIRES( seqNo >= 0 ); REQUIRES( version >= SSL_MINOR_VERSION_TLS && \ version <= SSL_MINOR_VERSION_TLS12 ); REQUIRES( payloadLength >= 0 && payloadLength <= MAX_PACKET_SIZE ); /* Clear return values */ memset( data, 0, min( 16, dataMaxLength ) ); *dataLength = 0; /* Write the sequence number, packet type, version, and length information to the output buffer */ sMemOpen( &stream, data, dataMaxLength ); writeUint64( &stream, seqNo ); sputc( &stream, type ); sputc( &stream, SSL_MAJOR_VERSION ); sputc( &stream, version ); status = writeUint16( &stream, payloadLength ); if( cryptStatusOK( status ) ) *dataLength = stell( &stream ); sMemDisconnect( &stream ); return( status ); } /**************************************************************************** * * * Encrypt/Decrypt Functions * * * ****************************************************************************/ /* Encrypt/decrypt a data block (in mose cases this also includes the MAC, which has been added to the data by the caller). The handling of length arguments for these is a bit tricky, for encryption the input is { data, payloadLength } which is padded (if necessary) and the padded length returned in '*dataLength', for decryption the entire data block will be processed but only 'processedDataLength' bytes of result are valid output */ CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \ int encryptData( const SESSION_INFO *sessionInfoPtr, INOUT_BUFFER( dataMaxLength, *dataLength ) \ BYTE *data, IN_DATALENGTH const int dataMaxLength, OUT_DATALENGTH_Z int *dataLength, IN_DATALENGTH const int payloadLength ) { int length = payloadLength, status; assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( data, dataMaxLength ) ); assert( isWritePtr( dataLength, sizeof( int ) ) ); REQUIRES( dataMaxLength > 0 && dataMaxLength < MAX_BUFFER_SIZE ); REQUIRES( payloadLength > 0 && \ payloadLength <= MAX_PACKET_SIZE + 20 && \ payloadLength <= sessionInfoPtr->sendBufSize && \ payloadLength <= dataMaxLength ); /* Clear return value */ *dataLength = 0; /* If it's a block cipher then we add end-of-block and message padding. We can't pad GCM because the spec doesn't allow it */ if( sessionInfoPtr->cryptBlocksize > 1 ) { #ifdef USE_SSL3 const int maxPadSize = ( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL ) ? \ sessionInfoPtr->cryptBlocksize : MESSAGE_PADDING_SIZE; const int padSize = ( maxPadSize - 1 ) - \ ( payloadLength & ( maxPadSize - 1 ) ); #else const int padSize = ( MESSAGE_PADDING_SIZE - 1 ) - \ ( payloadLength & ( MESSAGE_PADDING_SIZE - 1 ) ); #endif /* USE_SSL3 */ int i; ENSURES( padSize >= 0 && padSize < MESSAGE_PADDING_SIZE && \ length + padSize + 1 <= dataMaxLength ); /* Add the PKCS #5-style padding (PKCS #5 uses n, TLS uses n-1) */ for( i = 0; i < padSize + 1; i++ ) data[ length++ ] = intToByte( padSize ); } /* Encrypt the data and optional padding */ status = krnlSendMessage( sessionInfoPtr->iCryptOutContext, IMESSAGE_CTX_ENCRYPT, data, length ); if( cryptStatusError( status ) ) return( status ); *dataLength = length; /* If we're using GCM then we have to append the ICV to the data */ if( sessionInfoPtr->protocolFlags & SSL_PFLAG_GCM ) { MESSAGE_DATA msgData; REQUIRES( length + sessionInfoPtr->authBlocksize <= dataMaxLength ); setMessageData( &msgData, data + length, sessionInfoPtr->authBlocksize ); status = krnlSendMessage( sessionInfoPtr->iCryptOutContext, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_ICV ); if( cryptStatusError( status ) ) return( status ); *dataLength += sessionInfoPtr->authBlocksize; } return( CRYPT_OK ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \ int decryptData( SESSION_INFO *sessionInfoPtr, INOUT_BUFFER_FIXED( dataLength ) \ BYTE *data, IN_DATALENGTH const int dataLength, OUT_DATALENGTH_Z int *processedDataLength ) { int length = dataLength, padSize, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( data, dataLength ) ); assert( isWritePtr( processedDataLength, sizeof( int ) ) ); REQUIRES( dataLength > 0 && \ dataLength <= sessionInfoPtr->receiveBufEnd && \ dataLength < MAX_BUFFER_SIZE ); /* Clear return value */ *processedDataLength = 0; /* Decrypt the data */ status = krnlSendMessage( sessionInfoPtr->iCryptInContext, IMESSAGE_CTX_DECRYPT, data, length ); if( cryptStatusError( status ) ) { retExt( status, ( status, SESSION_ERRINFO, "Packet decryption failed" ) ); } /* If we're using GCM then we have to check the ICV that follows the data */ if( sessionInfoPtr->protocolFlags & SSL_PFLAG_GCM ) { MESSAGE_DATA msgData; setMessageData( &msgData, data + length, sessionInfoPtr->authBlocksize ); status = krnlSendMessage( sessionInfoPtr->iCryptInContext, IMESSAGE_COMPARE, &msgData, MESSAGE_COMPARE_ICV ); if( cryptStatusError( status ) ) { retExt( CRYPT_ERROR_SIGNATURE, ( CRYPT_ERROR_SIGNATURE, SESSION_ERRINFO, "Bad message ICV for packet type %d, length %d", data[ 0 ], length ) ); } } /* If it's a stream cipher then there's no padding present */ if( sessionInfoPtr->cryptBlocksize <= 1 ) { *processedDataLength = length; return( CRYPT_OK ); } /* If it's a block cipher, we need to remove end-of-block padding. Up until TLS 1.1 the spec was silent about any requirement to check the padding (and for SSLv3 it didn't specify the padding format at all) so it's not really safe to reject an SSL message if we don't find the correct padding because many SSL implementations didn't process the padded data space in any way, leaving it containing whatever was there before (which can include old plaintext (!!)). Almost all TLS implementations get it right (even though in TLS 1.0 there was only a requirement to generate, but not to check, the PKCS #5-style padding). Because of this we only check the padding bytes if we're talking TLS. First we make sure that the padding information looks OK. TLS allows up to 256 bytes of padding (only GnuTLS actually seems to use this capability though) so we can't check for a sensible (small) padding length, however we can check this for SSL, which is good because for that we can't check the padding itself. There's no easy way to perform these checks in a timing-independent manner because we're using them to reject completely malformed packets (out-of-bounds array references), but hopefully the few cycles difference won't be measurable in the overall scheme of things */ padSize = byteToInt( data[ dataLength - 1 ] ); if( padSize < 0 || padSize > 255 || \ ( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL && \ padSize > sessionInfoPtr->cryptBlocksize - 1 ) ) { retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Invalid encryption padding value 0x%02X (%d)", padSize, padSize ) ); } length -= padSize + 1; if( length < 0 || length > MAX_BUFFER_SIZE ) { retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Encryption padding adjustment value %d is greater " "than packet length %d", padSize, dataLength ) ); } /* Check for PKCS #5-type padding (PKCS #5 uses n, TLS uses n-1) if necessary, in a timing-independent manner */ if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS ) { int value = 0, i; for( i = 0; i < padSize; i++ ) value |= data[ length + i ] ^ padSize; if( value != 0 ) { retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Invalid encryption padding byte, expected 0x%02X", padSize ) ); } } *processedDataLength = length; return( CRYPT_OK ); } /**************************************************************************** * * * SSL MAC Functions * * * ****************************************************************************/ #ifdef USE_SSL3 /* Proto-HMAC padding data */ #define PROTOHMAC_PAD1_VALUE 0x36 #define PROTOHMAC_PAD2_VALUE 0x5C #define PROTOHMAC_PAD1 "\x36\x36\x36\x36\x36\x36\x36\x36" \ "\x36\x36\x36\x36\x36\x36\x36\x36" \ "\x36\x36\x36\x36\x36\x36\x36\x36" \ "\x36\x36\x36\x36\x36\x36\x36\x36" \ "\x36\x36\x36\x36\x36\x36\x36\x36" \ "\x36\x36\x36\x36\x36\x36\x36\x36" #define PROTOHMAC_PAD2 "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" \ "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" \ "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" \ "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" \ "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" \ "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" /* Perform an SSL MAC of a data block. We have to provide special-case handling of zero-length blocks since some versions of OpenSSL send these as a kludge in SSL/TLS 1.0 to work around chosen-IV attacks. In the following functions we don't check the return value of every single component MAC operation since it would lead to endless sequences of 'status = x; if( cSOK( x ) ) ...' chains, on the remote chance that there's some transient failure in a single component operation it'll be picked up at the end anyway when the overall MAC check fails */ CHECK_RETVAL STDC_NONNULL_ARG( ( 3 ) ) \ static int macDataSSL( IN_HANDLE const CRYPT_CONTEXT iHashContext, IN_ALGO const CRYPT_ALGO_TYPE hashAlgo, IN_BUFFER( macSecretLength ) \ const void *macSecret, IN_LENGTH_SHORT const int macSecretLength, IN_INT_Z const long seqNo, IN_BUFFER_OPT( dataLength ) const void *data, IN_DATALENGTH_Z const int dataLength, IN_RANGE( 0, 255 ) const int type ) { MESSAGE_DATA msgData; STREAM stream; BYTE buffer[ 128 + 8 ]; const int padSize = ( hashAlgo == CRYPT_ALGO_MD5 ) ? 48 : 40; int length DUMMY_INIT, status; assert( isReadPtr( macSecret, macSecretLength ) ); assert( ( data == NULL && dataLength == 0 ) || \ isReadPtr( data, dataLength ) ); REQUIRES( isHandleRangeValid( iHashContext ) ); REQUIRES( hashAlgo == CRYPT_ALGO_MD5 || \ hashAlgo == CRYPT_ALGO_SHA1 ); REQUIRES( macSecretLength > 0 && \ macSecretLength < MAX_INTLENGTH_SHORT ); REQUIRES( seqNo >= 0 ); REQUIRES( ( data == NULL && dataLength == 0 ) || \ ( data != NULL && \ dataLength > 0 && dataLength <= MAX_PACKET_SIZE ) ); REQUIRES( type >= 0 && type <= 255 ); /* Set up the sequence number and length data */ memset( buffer, PROTOHMAC_PAD1_VALUE, padSize ); sMemOpen( &stream, buffer + padSize, 128 - padSize ); writeUint64( &stream, seqNo ); sputc( &stream, type ); status = writeUint16( &stream, dataLength ); if( cryptStatusOK( status ) ) length = stell( &stream ); sMemDisconnect( &stream ); if( cryptStatusError( status ) ) return( status ); /* Reset the hash context and generate the inner portion of the MAC: hash( MAC_secret || pad1 || seq_num || type || length || data ) */ krnlSendMessage( iHashContext, IMESSAGE_DELETEATTRIBUTE, NULL, CRYPT_CTXINFO_HASHVALUE ); krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) macSecret, macSecretLength ); krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, buffer, padSize + length ); if( dataLength > 0 ) krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) data, dataLength ); status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, "", 0 ); if( cryptStatusError( status ) ) return( status ); /* Extract the inner hash value */ memset( buffer, PROTOHMAC_PAD2_VALUE, padSize ); setMessageData( &msgData, buffer + padSize, CRYPT_MAX_HASHSIZE ); status = krnlSendMessage( iHashContext, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); if( cryptStatusError( status ) ) return( status ); /* Generate the outer portion of the handshake message's MAC: hash( MAC_secret || pad2 || inner_hash ) */ krnlSendMessage( iHashContext, IMESSAGE_DELETEATTRIBUTE, NULL, CRYPT_CTXINFO_HASHVALUE ); krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) macSecret, macSecretLength ); krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, buffer, padSize + msgData.length ); return( krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, "", 0 ) ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \ int createMacSSL( INOUT SESSION_INFO *sessionInfoPtr, INOUT_BUFFER( dataMaxLength, *dataLength ) void *data, IN_DATALENGTH const int dataMaxLength, OUT_DATALENGTH_Z int *dataLength, IN_DATALENGTH const int payloadLength, IN_RANGE( 0, 255 ) const int type ) { SSL_INFO *sslInfo = sessionInfoPtr->sessionSSL; MESSAGE_DATA msgData; int status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( data, dataMaxLength ) ); assert( isWritePtr( dataLength, sizeof( int ) ) ); REQUIRES( dataMaxLength > 0 && dataMaxLength < MAX_BUFFER_SIZE ); REQUIRES( payloadLength > 0 && payloadLength <= MAX_PACKET_SIZE && \ payloadLength + sessionInfoPtr->authBlocksize <= dataMaxLength ); REQUIRES( type >= 0 && type <= 255 ); /* Clear return value */ *dataLength = 0; /* MAC the payload */ status = macDataSSL( sessionInfoPtr->iAuthOutContext, sessionInfoPtr->integrityAlgo, sslInfo->macWriteSecret, sessionInfoPtr->authBlocksize, sslInfo->writeSeqNo, data, payloadLength, type ); if( cryptStatusError( status ) ) return( status ); sslInfo->writeSeqNo++; /* Append the MAC value to the end of the packet */ ENSURES( rangeCheck( payloadLength, sessionInfoPtr->authBlocksize, dataMaxLength ) ); setMessageData( &msgData, ( BYTE * ) data + payloadLength, sessionInfoPtr->authBlocksize ); status = krnlSendMessage( sessionInfoPtr->iAuthOutContext, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); if( cryptStatusError( status ) ) return( status ); *dataLength = payloadLength + msgData.length; return( CRYPT_OK ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \ int checkMacSSL( INOUT SESSION_INFO *sessionInfoPtr, IN_BUFFER( dataLength ) const void *data, IN_DATALENGTH const int dataLength, IN_DATALENGTH_Z const int payloadLength, IN_RANGE( 0, 255 ) const int type, const BOOLEAN noReportError ) { SSL_INFO *sslInfo = sessionInfoPtr->sessionSSL; MESSAGE_DATA msgData; int status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isReadPtr( data, dataLength ) ); REQUIRES( dataLength > 0 && dataLength < MAX_BUFFER_SIZE ); REQUIRES( payloadLength >= 0 && payloadLength <= MAX_PACKET_SIZE && \ payloadLength + sessionInfoPtr->authBlocksize <= dataLength ); REQUIRES( type >= 0 && type <= 255 ); /* MAC the payload. If the payload length is zero then there's no data payload, this can happen with some versions of OpenSSL that send zero-length blocks as a kludge to work around pre-TLS 1.1 chosen-IV attacks */ if( payloadLength <= 0 ) { status = macDataSSL( sessionInfoPtr->iAuthInContext, sessionInfoPtr->integrityAlgo, sslInfo->macReadSecret, sessionInfoPtr->authBlocksize, sslInfo->readSeqNo, NULL, 0, type ); } else { status = macDataSSL( sessionInfoPtr->iAuthInContext, sessionInfoPtr->integrityAlgo, sslInfo->macReadSecret, sessionInfoPtr->authBlocksize, sslInfo->readSeqNo, data, payloadLength, type ); } if( cryptStatusError( status ) ) return( status ); sslInfo->readSeqNo++; /* Compare the calculated MAC to the MAC present at the end of the data */ ENSURES( rangeCheckZ( payloadLength, sessionInfoPtr->authBlocksize, dataLength ) ); setMessageData( &msgData, ( BYTE * ) data + payloadLength, sessionInfoPtr->authBlocksize ); status = krnlSendMessage( sessionInfoPtr->iAuthInContext, IMESSAGE_COMPARE, &msgData, MESSAGE_COMPARE_HASH ); if( cryptStatusError( status ) ) { /* If the error message has already been set at a higher level, don't update the error information */ if( noReportError ) return( CRYPT_ERROR_SIGNATURE ); retExt( CRYPT_ERROR_SIGNATURE, ( CRYPT_ERROR_SIGNATURE, SESSION_ERRINFO, "Bad message MAC for packet type %d, length %d", type, dataLength ) ); } return( CRYPT_OK ); } #endif /* USE_SSL3 */ /**************************************************************************** * * * TLS MAC Functions * * * ****************************************************************************/ /* Perform a TLS MAC of a data block. We have to provide special-case handling of zero-length blocks since some versions of OpenSSL send these as a kludge in SSL/TLS 1.0 to work around chosen-IV attacks. In the following functions we don't check the return value of every single component MAC operation since it would lead to endless sequences of 'status = x; if( cSOK( x ) ) ...' chains, on the remote chance that there's some transient failure in a single component operation it'll be picked up at the end anyway when the overall MAC check fails */ CHECK_RETVAL \ static int macDataTLS( IN_HANDLE const CRYPT_CONTEXT iHashContext, IN_INT_Z const long seqNo, IN_RANGE( SSL_MINOR_VERSION_TLS, \ SSL_MINOR_VERSION_TLS12 ) const int version, IN_BUFFER_OPT( ivLength ) const void *iv, IN_LENGTH_IV_Z const int ivLength, IN_BUFFER_OPT( dataLength ) const void *data, IN_DATALENGTH_Z const int dataLength, IN_RANGE( 0, 255 ) const int type ) { BYTE buffer[ 64 + CRYPT_MAX_IVSIZE + 8 ]; int length, status; assert( ( iv == NULL && ivLength == 0 ) || \ isReadPtr( iv, ivLength ) ); assert( ( data == NULL && dataLength == 0 ) || \ isReadPtr( data, dataLength ) ); REQUIRES( isHandleRangeValid( iHashContext ) ); REQUIRES( seqNo >= 0 ); REQUIRES( version >= SSL_MINOR_VERSION_TLS && \ version <= SSL_MINOR_VERSION_TLS12 ); REQUIRES( ( iv == NULL && ivLength == 0 ) || \ ( iv != NULL && \ ivLength > 0 && ivLength <= CRYPT_MAX_IVSIZE ) ); REQUIRES( ( data == NULL && dataLength == 0 ) || \ ( data != NULL && \ dataLength > 0 && dataLength <= MAX_PACKET_SIZE ) ); REQUIRES( type >= 0 && type <= 255 ); /* Set up the packet metadata to be MACed */ status = writePacketMetadata( buffer, 64, &length, type, seqNo, version, dataLength + ivLength ); if( cryptStatusError( status ) ) return( status ); if( ivLength > 0 ) { /* If we're using an explicit IV, append it to the metadata for MAC'ing */ memcpy( buffer + length, iv, ivLength ); length += ivLength; } /* Reset the hash context and generate the MAC: HMAC( metadata || (IV) || data ) */ krnlSendMessage( iHashContext, IMESSAGE_DELETEATTRIBUTE, NULL, CRYPT_CTXINFO_HASHVALUE ); krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, buffer, length ); if( dataLength > 0 ) { krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) data, dataLength ); } return( krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, "", 0 ) ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \ int createMacTLS( INOUT SESSION_INFO *sessionInfoPtr, INOUT_BUFFER( dataMaxLength, *dataLength ) void *data, IN_DATALENGTH const int dataMaxLength, OUT_DATALENGTH_Z int *dataLength, IN_DATALENGTH const int payloadLength, IN_RANGE( 0, 255 ) const int type ) { SSL_INFO *sslInfo = sessionInfoPtr->sessionSSL; MESSAGE_DATA msgData; int status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( data, dataMaxLength ) ); assert( isWritePtr( dataLength, sizeof( int ) ) ); REQUIRES( dataMaxLength > 0 && dataMaxLength < MAX_BUFFER_SIZE ); REQUIRES( payloadLength > 0 && payloadLength <= MAX_PACKET_SIZE && \ payloadLength + sessionInfoPtr->authBlocksize <= dataMaxLength ); REQUIRES( type >= 0 && type <= 255 ); /* Clear return values */ *dataLength = 0; /* MAC the payload. When wrapping a packet the IV is treaded as part of the payload so it's always passed in as { NULL, 0 }, it's only when unwrapping that it's stripped before the payload data is copied into the session buffer and so needs to be passed in explicitly */ status = macDataTLS( sessionInfoPtr->iAuthOutContext, sslInfo->writeSeqNo, sessionInfoPtr->version, NULL, 0, data, payloadLength, type ); if( cryptStatusError( status ) ) return( status ); sslInfo->writeSeqNo++; /* Append the MAC value to the end of the packet */ ENSURES( rangeCheck( payloadLength, sessionInfoPtr->authBlocksize, dataMaxLength ) ); setMessageData( &msgData, ( BYTE * ) data + payloadLength, sessionInfoPtr->authBlocksize ); status = krnlSendMessage( sessionInfoPtr->iAuthOutContext, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); if( cryptStatusError( status ) ) return( status ); *dataLength = payloadLength + msgData.length; return( CRYPT_OK ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \ int checkMacTLS( INOUT SESSION_INFO *sessionInfoPtr, IN_BUFFER( dataLength ) const void *data, IN_DATALENGTH const int dataLength, IN_DATALENGTH_Z const int payloadLength, IN_RANGE( 0, 255 ) const int type, const BOOLEAN noReportError ) { SSL_INFO *sslInfo = sessionInfoPtr->sessionSSL; MESSAGE_DATA msgData; const void *ivPtr = NULL; int ivLength = 0, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isReadPtr( data, dataLength ) ); REQUIRES( dataLength > 0 && dataLength < MAX_BUFFER_SIZE ); REQUIRES( payloadLength >= 0 && payloadLength <= MAX_PACKET_SIZE && \ payloadLength + sessionInfoPtr->authBlocksize <= dataLength ); REQUIRES( type >= 0 && type <= 255 ); /* MAC the payload. If the payload length is zero then there's no data payload, this can happen with some versions of OpenSSL that send zero-length blocks as a kludge to work around pre-TLS 1.1 chosen-IV attacks */ if( ( sessionInfoPtr->protocolFlags & SSL_PFLAG_ENCTHENMAC ) && \ sslInfo->ivSize > 0 ) { /* When using encrypt-then-MAC and TLS 1.1+ explicit IVs, the IV is authenticated alongside the encrypted payload data */ ivPtr = sslInfo->iv; ivLength = sslInfo->ivSize; } if( payloadLength <= 0 ) { status = macDataTLS( sessionInfoPtr->iAuthInContext, sslInfo->readSeqNo, sessionInfoPtr->version, ivPtr, ivLength, NULL, 0, type ); } else { status = macDataTLS( sessionInfoPtr->iAuthInContext, sslInfo->readSeqNo, sessionInfoPtr->version, ivPtr, ivLength, data, payloadLength, type ); } if( cryptStatusError( status ) ) return( status ); sslInfo->readSeqNo++; /* Compare the calculated MAC to the MAC present at the end of the data */ ENSURES( rangeCheckZ( payloadLength, sessionInfoPtr->authBlocksize, dataLength ) ); setMessageData( &msgData, ( BYTE * ) data + payloadLength, sessionInfoPtr->authBlocksize ); status = krnlSendMessage( sessionInfoPtr->iAuthInContext, IMESSAGE_COMPARE, &msgData, MESSAGE_COMPARE_HASH ); if( cryptStatusError( status ) ) { /* If the error message has already been set at a higher level, don't update the error information */ if( noReportError ) return( CRYPT_ERROR_SIGNATURE ); retExt( CRYPT_ERROR_SIGNATURE, ( CRYPT_ERROR_SIGNATURE, SESSION_ERRINFO, "Bad message MAC for packet type %d, length %d", type, dataLength ) ); } return( CRYPT_OK ); } /**************************************************************************** * * * TLS GCM Functions * * * ****************************************************************************/ #ifdef USE_GCM /* Perform a TLS GCM integrity check of a data block. This differs somewhat from the more conventional MACing routines because GCM combines the ICV generation with encryption, so all that we're actually doing is generating the initial stage of the ICV over the packet metadata handled as GCM AAD */ CHECK_RETVAL \ int macDataTLSGCM( IN_HANDLE const CRYPT_CONTEXT iCryptContext, IN_INT_Z const long seqNo, IN_RANGE( SSL_MINOR_VERSION_TLS, \ SSL_MINOR_VERSION_TLS12 ) const int version, IN_LENGTH_Z const int payloadLength, IN_RANGE( 0, 255 ) const int type ) { MESSAGE_DATA msgData; BYTE buffer[ 64 + 8 ]; int length, status; REQUIRES( isHandleRangeValid( iCryptContext ) ); REQUIRES( seqNo >= 0 ); REQUIRES( version >= SSL_MINOR_VERSION_TLS && \ version <= SSL_MINOR_VERSION_TLS12 ); REQUIRES( payloadLength >= 0 && payloadLength <= MAX_PACKET_SIZE ); REQUIRES( type >= 0 && type <= 255 ); /* Set up the packet metadata to be MACed */ status = writePacketMetadata( buffer, 64, &length, type, seqNo, version, payloadLength ); if( cryptStatusError( status ) ) return( status ); /* Send the AAD to the GCM context for inclusion in the ICV calculation */ setMessageData( &msgData, buffer, length ); return( krnlSendMessage( iCryptContext, IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_AAD ) ); } #endif /* USE_GCM */ /**************************************************************************** * * * Handshake Message Hash/MAC Functions * * * ****************************************************************************/ /* Perform assorted hashing of a data block, a dual dual MD5+SHA-1 hash for SSL or straight SHA-2 hash for TLS 1.2+. Since this is part of an ongoing message exchange (in other words a failure potentially won't be detected for some time) we check each return value. This processing was originally done using a dual MD5+SHA-1 hash, however TLS 1.2+ switched to using a single SHA-2 hash, because of this we have to explicitly check which hashing option we're using (in some cases it might be both since we have to speculatively hash initial messages until we've agreed on a version) and only use that hash. In addition to the overall hashing we may also be running a separate hash of the messages that stops before the other hashing does if certificate- based client authentication is being used. This would add even more overhead to the whole process, however since it's only used with TLS 1.2+ and in that case is restricted to using SHA-2 via hashing preferences sent in the hello messages, we can obtain the necessary hash value by cloning the SHA-2 context when we have to generate or verify the client signature */ CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \ static int hashHSData( const SSL_HANDSHAKE_INFO *handshakeInfo, IN_BUFFER( dataLength ) const void *data, IN_DATALENGTH const int dataLength ) { int status; assert( isReadPtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); assert( isReadPtr( data, dataLength ) ); REQUIRES( dataLength > 0 && dataLength < MAX_BUFFER_SIZE ); if( handshakeInfo->md5context != CRYPT_ERROR ) { status = krnlSendMessage( handshakeInfo->md5context, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) data, dataLength ); if( cryptStatusOK( status ) ) status = krnlSendMessage( handshakeInfo->sha1context, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) data, dataLength ); if( cryptStatusError( status ) ) return( status ); } if( handshakeInfo->sha2context != CRYPT_ERROR ) { status = krnlSendMessage( handshakeInfo->sha2context, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) data, dataLength ); if( cryptStatusError( status ) ) return( status ); } #ifdef CONFIG_SUITEB if( handshakeInfo->sha384context != CRYPT_ERROR ) { status = krnlSendMessage( handshakeInfo->sha384context, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) data, dataLength ); if( cryptStatusError( status ) ) return( status ); } #endif /* CONFIG_SUITEB */ return( CRYPT_OK ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \ int hashHSPacketRead( const SSL_HANDSHAKE_INFO *handshakeInfo, INOUT STREAM *stream ) { const int dataLength = sMemDataLeft( stream ); void *data; int status; assert( isReadPtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); assert( isWritePtr( stream, sizeof( STREAM ) ) ); REQUIRES( dataLength > 0 && dataLength < MAX_BUFFER_SIZE ); /* On a read we've just processed the packet header and everything that's left in the stream is the data to be MACd. Note that we can't use sMemGetDataBlockRemaining() for this because that returns the entire available buffer, not just the amount of data in the buffer */ status = sMemGetDataBlock( stream, &data, dataLength ); if( cryptStatusOK( status ) ) { ANALYSER_HINT( data != NULL ); status = hashHSData( handshakeInfo, data, dataLength ); } return( status ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \ int hashHSPacketWrite( const SSL_HANDSHAKE_INFO *handshakeInfo, INOUT STREAM *stream, IN_DATALENGTH_Z const int offset ) { const int dataStart = offset + SSL_HEADER_SIZE; const int dataLength = stell( stream ) - dataStart; void *data; int status; assert( isReadPtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); assert( isWritePtr( stream, sizeof( STREAM ) ) ); REQUIRES( offset >= 0 && offset < MAX_BUFFER_SIZE ); REQUIRES( dataLength > 0 && dataLength < MAX_BUFFER_SIZE ); /* On a write we've just finished writing the packet and everything but the header needs to be MACd */ status = sMemGetDataBlockAbs( stream, dataStart, &data, dataLength ); if( cryptStatusOK( status ) ) { ANALYSER_HINT( data != NULL ); status = hashHSData( handshakeInfo, data, dataLength ); } return( status ); } /* Complete the dual MD5/SHA1 hash/MAC or SHA2 MAC that's used in the finished message. There are no less than three variations of this, one for SSL's dual MAC, one for TLS 1.0 - 1.1's IPsec cargo cult 96-bit PRF'd dual MAC, and one for TLS 1.2's similarly cargo-cult 96-bit PRF'd single MAC. We don't check the return value of every single component MAC operation since it would lead to endless sequences of 'status = x; if( cSOK( x ) ) ...' chains, on the remote chance that there's some transient failure in a single component operation it'll be picked up at the end anyway when the overall MAC check fails */ #ifdef USE_SSL3 CHECK_RETVAL STDC_NONNULL_ARG( ( 3, 5, 6, 8 ) ) \ int completeSSLDualMAC( IN_HANDLE const CRYPT_CONTEXT md5context, IN_HANDLE const CRYPT_CONTEXT sha1context, OUT_BUFFER( hashValuesMaxLen, *hashValuesLen ) BYTE *hashValues, IN_LENGTH_SHORT_MIN( MD5MAC_SIZE + SHA1MAC_SIZE ) \ const int hashValuesMaxLen, OUT_LENGTH_BOUNDED_Z( hashValuesMaxLen ) \ int *hashValuesLen, IN_BUFFER( labelLength ) const char *label, IN_RANGE( 1, 64 ) const int labelLength, IN_BUFFER( masterSecretLen ) const BYTE *masterSecret, IN_LENGTH_SHORT const int masterSecretLen ) { MESSAGE_DATA msgData; int status; assert( isWritePtr( hashValues, hashValuesMaxLen ) ); assert( isWritePtr( hashValuesLen, sizeof( int ) ) ); assert( isReadPtr( label, labelLength ) ); assert( isReadPtr( masterSecret, masterSecretLen ) ); REQUIRES( isHandleRangeValid( md5context ) ); REQUIRES( isHandleRangeValid( sha1context ) ); REQUIRES( hashValuesMaxLen >= MD5MAC_SIZE + SHA1MAC_SIZE && \ hashValuesMaxLen < MAX_INTLENGTH_SHORT ); REQUIRES( labelLength > 0 && labelLength <= 64 ); REQUIRES( masterSecretLen > 0 && masterSecretLen < MAX_INTLENGTH_SHORT ); /* Clear return value */ *hashValuesLen = 0; /* Generate the inner portion of the handshake message's MAC: hash( handshake_messages || cl/svr_label || master_secret || pad1 ) Note that the SHA-1 pad size is 40 bytes and not 44 (to get a total length of 64 bytes), this is due to an error in the spec */ krnlSendMessage( md5context, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) label, labelLength ); krnlSendMessage( sha1context, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) label, labelLength ); krnlSendMessage( md5context, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) masterSecret, masterSecretLen ); krnlSendMessage( sha1context, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) masterSecret, masterSecretLen ); krnlSendMessage( md5context, IMESSAGE_CTX_HASH, PROTOHMAC_PAD1, 48 ); krnlSendMessage( sha1context, IMESSAGE_CTX_HASH, PROTOHMAC_PAD1, 40 ); krnlSendMessage( md5context, IMESSAGE_CTX_HASH, "", 0 ); krnlSendMessage( sha1context, IMESSAGE_CTX_HASH, "", 0 ); setMessageData( &msgData, hashValues, MD5MAC_SIZE ); status = krnlSendMessage( md5context, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); if( cryptStatusOK( status ) ) { setMessageData( &msgData, hashValues + MD5MAC_SIZE, SHA1MAC_SIZE ); status = krnlSendMessage( sha1context, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); } if( cryptStatusError( status ) ) return( status ); /* Reset the hash contexts */ krnlSendMessage( md5context, IMESSAGE_DELETEATTRIBUTE, NULL, CRYPT_CTXINFO_HASHVALUE ); krnlSendMessage( sha1context, IMESSAGE_DELETEATTRIBUTE, NULL, CRYPT_CTXINFO_HASHVALUE ); /* Generate the outer portion of the handshake message's MAC: hash( master_secret || pad2 || inner_hash ) */ krnlSendMessage( md5context, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) masterSecret, masterSecretLen ); krnlSendMessage( sha1context, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) masterSecret, masterSecretLen ); krnlSendMessage( md5context, IMESSAGE_CTX_HASH, PROTOHMAC_PAD2, 48 ); krnlSendMessage( sha1context, IMESSAGE_CTX_HASH, PROTOHMAC_PAD2, 40 ); krnlSendMessage( md5context, IMESSAGE_CTX_HASH, hashValues, MD5MAC_SIZE ); krnlSendMessage( sha1context, IMESSAGE_CTX_HASH, hashValues + MD5MAC_SIZE, SHA1MAC_SIZE ); krnlSendMessage( md5context, IMESSAGE_CTX_HASH, "", 0 ); krnlSendMessage( sha1context, IMESSAGE_CTX_HASH, "", 0 ); setMessageData( &msgData, hashValues, MD5MAC_SIZE ); status = krnlSendMessage( md5context, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); if( cryptStatusError( status ) ) return( status ); setMessageData( &msgData, hashValues + MD5MAC_SIZE, SHA1MAC_SIZE ); status = krnlSendMessage( sha1context, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); if( cryptStatusOK( status ) ) *hashValuesLen = MD5MAC_SIZE + SHA1MAC_SIZE; return( status ); } #endif /* USE_SSL3 */ CHECK_RETVAL STDC_NONNULL_ARG( ( 3, 5, 6, 8 ) ) \ int completeTLSHashedMAC( IN_HANDLE const CRYPT_CONTEXT md5context, IN_HANDLE const CRYPT_CONTEXT sha1context, OUT_BUFFER( hashValuesMaxLen, *hashValuesLen ) BYTE *hashValues, IN_LENGTH_SHORT_MIN( TLS_HASHEDMAC_SIZE ) \ const int hashValuesMaxLen, OUT_LENGTH_BOUNDED_Z( hashValuesMaxLen ) \ int *hashValuesLen, IN_BUFFER( labelLength ) const char *label, IN_RANGE( 1, 64 ) const int labelLength, IN_BUFFER( masterSecretLen ) \ const BYTE *masterSecret, IN_LENGTH_SHORT const int masterSecretLen ) { MECHANISM_DERIVE_INFO mechanismInfo; MESSAGE_DATA msgData; BYTE hashBuffer[ 64 + ( CRYPT_MAX_HASHSIZE * 2 ) + 8 ]; int status; assert( isWritePtr( hashValues, hashValuesMaxLen ) ); assert( isWritePtr( hashValuesLen, sizeof( int ) ) ); assert( isReadPtr( label, labelLength ) ); assert( isReadPtr( masterSecret, masterSecretLen ) ); REQUIRES( isHandleRangeValid( md5context ) ); REQUIRES( isHandleRangeValid( sha1context ) ); REQUIRES( hashValuesMaxLen >= TLS_HASHEDMAC_SIZE && \ hashValuesMaxLen < MAX_INTLENGTH_SHORT ); REQUIRES( labelLength > 0 && labelLength <= 64 && \ labelLength + MD5MAC_SIZE + SHA1MAC_SIZE <= \ 64 + ( CRYPT_MAX_HASHSIZE * 2 ) ); /* Clear return value */ *hashValuesLen = 0; memcpy( hashBuffer, label, labelLength ); /* Complete the hashing and get the MD5 and SHA-1 hashes */ krnlSendMessage( md5context, IMESSAGE_CTX_HASH, "", 0 ); krnlSendMessage( sha1context, IMESSAGE_CTX_HASH, "", 0 ); setMessageData( &msgData, hashBuffer + labelLength, MD5MAC_SIZE ); status = krnlSendMessage( md5context, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); if( cryptStatusOK( status ) ) { setMessageData( &msgData, hashBuffer + labelLength + MD5MAC_SIZE, SHA1MAC_SIZE ); status = krnlSendMessage( sha1context, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); } if( cryptStatusError( status ) ) return( status ); /* Generate the TLS check value. This isn't really a hash or a MAC but is generated by feeding the MD5 and SHA1 hashes of the handshake messages into the TLS key derivation (PRF) function and truncating the result to 12 bytes (96 bits) IPsec cargo cult protocol design purposes: TLS_PRF( label || MD5_hash || SHA1_hash ) */ setMechanismDeriveInfo( &mechanismInfo, hashValues, TLS_HASHEDMAC_SIZE, ( MESSAGE_CAST ) masterSecret, masterSecretLen, ( CRYPT_ALGO_TYPE ) CRYPT_USE_DEFAULT, hashBuffer, labelLength + MD5MAC_SIZE + SHA1MAC_SIZE, 1 ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_DERIVE, &mechanismInfo, MECHANISM_DERIVE_TLS ); if( cryptStatusOK( status ) ) *hashValuesLen = TLS_HASHEDMAC_SIZE; return( status ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 4, 5, 7 ) ) \ int completeTLS12HashedMAC( IN_HANDLE const CRYPT_CONTEXT sha2context, OUT_BUFFER( hashValuesMaxLen, *hashValuesLen ) \ BYTE *hashValues, IN_LENGTH_SHORT_MIN( TLS_HASHEDMAC_SIZE ) \ const int hashValuesMaxLen, OUT_LENGTH_BOUNDED_Z( hashValuesMaxLen ) \ int *hashValuesLen, IN_BUFFER( labelLength ) const char *label, IN_RANGE( 1, 64 ) const int labelLength, IN_BUFFER( masterSecretLen ) \ const BYTE *masterSecret, IN_LENGTH_SHORT const int masterSecretLen ) { MECHANISM_DERIVE_INFO mechanismInfo; MESSAGE_DATA msgData; BYTE hashBuffer[ 64 + ( CRYPT_MAX_HASHSIZE * 2 ) + 8 ]; int macSize, status; assert( isWritePtr( hashValues, hashValuesMaxLen ) ); assert( isWritePtr( hashValuesLen, sizeof( int ) ) ); assert( isReadPtr( label, labelLength ) ); assert( isReadPtr( masterSecret, masterSecretLen ) ); REQUIRES( isHandleRangeValid( sha2context ) ); REQUIRES( hashValuesMaxLen >= TLS_HASHEDMAC_SIZE && \ hashValuesMaxLen < MAX_INTLENGTH_SHORT ); REQUIRES( labelLength > 0 && labelLength <= 64 && \ labelLength + CRYPT_MAX_HASHSIZE <= 64 + ( CRYPT_MAX_HASHSIZE ) ); /* Clear return value */ *hashValuesLen = 0; memcpy( hashBuffer, label, labelLength ); /* Get the MAC size */ status = krnlSendMessage( sha2context, IMESSAGE_GETATTRIBUTE, &macSize, CRYPT_CTXINFO_BLOCKSIZE ); if( cryptStatusError( status ) ) return( status ); /* Complete the hashing and get the SHA-2 hash */ krnlSendMessage( sha2context, IMESSAGE_CTX_HASH, "", 0 ); setMessageData( &msgData, hashBuffer + labelLength, macSize ); status = krnlSendMessage( sha2context, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); if( cryptStatusError( status ) ) return( status ); /* Generate the TLS check value. This isn't really a hash or a MAC but is generated by feeding the SHA-2 hash of the handshake messages into the TLS key derivation (PRF) function and truncating the result to 12 bytes (96 bits) for IPsec cargo cult protocol design purposes: TLS_PRF( label || SHA2_hash ) */ setMechanismDeriveInfo( &mechanismInfo, hashValues, TLS_HASHEDMAC_SIZE, ( MESSAGE_CAST ) masterSecret, masterSecretLen, CRYPT_ALGO_SHA2, hashBuffer, labelLength + macSize, 1 ); if( macSize != bitsToBytes( 256 ) ) mechanismInfo.hashParam = macSize; status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_DERIVE, &mechanismInfo, MECHANISM_DERIVE_TLS12 ); if( cryptStatusOK( status ) ) *hashValuesLen = TLS_HASHEDMAC_SIZE; return( status ); } /**************************************************************************** * * * Client-Auth Signature Functions * * * ****************************************************************************/ /* Create/check the signature on an SSL certificate verify message. SSLv3/TLS use a weird signature format that dual-MACs (SSLv3) or hashes (TLS) all of the handshake messages exchanged to date (SSLv3 additionally hashes in further data like the master secret), then signs them using nonstandard PKCS #1 RSA without the ASN.1 wrapper (that is, it uses the raw concatenated SHA-1 and MD5 MAC (SSL) or hash (TLS) of the handshake messages with PKCS #1 padding prepended), unless we're using DSA in which case it drops the MD5 MAC/hash and uses only the SHA-1 one. This is an incredible pain to support because it requires running a parallel hash of handshake messages that terminates before the main hashing does, further hashing/MAC'ing of additional data, and the use of weird nonstandard data formats and signature mechanisms that aren't normally supported by anything. For example if the signing is to be done via a smart card then we can't use the standard PKCS #1 sig mechanism, we can't even use raw RSA and kludge the format together ourselves because some PKCS #11 implementations don't support the _X509 (raw) mechanism, what we have to do is tunnel the nonstandard sig.format information down through several cryptlib layers and then hope that the PKCS #11 implementation that we're using (a) supports this format and (b) gets it right. Another problem (which only occurs for SSLv3) is that the MAC requires the use of the master secret, which isn't available for several hundred more lines of code, so we have to delay producing any more data packets until the master secret is available (either that or squirrel all packets away in some buffer somewhere so that they can be hashed later), which severely screws up the handshake processing flow. TLS is slightly better here since it simply signs MD5-hash || SHA1-hash without requiring the use of the master secret, but even then it requires speculatively running an MD5 and SHA-1 hash of all messages on every exchange on the remote chance that the client will be using client certificates. TLS 1.2 finally moved to using standard signatures (PKCS #1 for RSA, conventional signatures for DSA/ECDSA), but still requires the speculative hashing of handshake messages. The chances of all of this custom data and signature handling working correctly are fairly low, and in any case there's no advantage to the weird mechanism and format used in SSL/TLS, all that we actually need to do is sign the client and server nonces to ensure signature freshness. Because of this what we actually do is just this, after which we create a standard PKCS #1 signature via the normal cryptlib mechanisms, which guarantees that it'll work with native cryptlib as well as any crypto hardware implementation. Since client certificates are hardly ever used and when they are it's in a closed environment, it's extremely unlikely that anyone will ever notice. There'll be far more problems in trying to use the nonstandard SSL/TLS signature mechanism than there are with using a standard (but not-in-the-spec) one. The one exception to this is, as already mentioned above, TLS 1.2+, for which we can finally use a standard signature. In this case we take a clone of the SHA-2 context that's been used to hash the handshake messages so far (the use of SHA-2 for this is enforced by the judicious use of TLS extensions, see the comments in ssl_ext.c for more on this) and sign that */ CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \ static int createCertVerifyAltHash( const SSL_HANDSHAKE_INFO *handshakeInfo, OUT_HANDLE_OPT CRYPT_CONTEXT *iHashContext ) { MESSAGE_CREATEOBJECT_INFO createInfo; BYTE nonceBuffer[ 64 + SSL_NONCE_SIZE + SSL_NONCE_SIZE + 8 ]; int status; assert( isReadPtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); assert( isWritePtr( iHashContext, sizeof( CRYPT_CONTEXT ) ) ); /* Clear return value */ *iHashContext = CRYPT_ERROR; /* Hash the client and server nonces */ setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_SHA1 ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT, &createInfo, OBJECT_TYPE_CONTEXT ); if( cryptStatusError( status ) ) return( status ); memcpy( nonceBuffer, "certificate verify", 18 ); memcpy( nonceBuffer + 18, handshakeInfo->clientNonce, SSL_NONCE_SIZE ); memcpy( nonceBuffer + 18 + SSL_NONCE_SIZE, handshakeInfo->serverNonce, SSL_NONCE_SIZE ); status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH, nonceBuffer, 18 + SSL_NONCE_SIZE + SSL_NONCE_SIZE ); if( cryptStatusOK( status ) ) status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH, nonceBuffer, 0 ); if( cryptStatusError( status ) ) { krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT ); return( status ); } *iHashContext = createInfo.cryptHandle; return( CRYPT_OK ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 4 ) ) \ static int getSessionHash( IN_HANDLE const CRYPT_CONTEXT iHashContext, OUT_BUFFER( hashValueMaxLen, *hashValueLen ) \ void *hashValue, IN_LENGTH_HASH const int hashValueMaxLen, OUT_LENGTH_HASH_Z int *hashValueLen ) { MESSAGE_DATA msgData; int status; assert( isWritePtr( hashValue, hashValueMaxLen ) ); assert( isWritePtr( hashValueLen, sizeof( int ) ) ); REQUIRES( isHandleRangeValid( iHashContext ) ); REQUIRES( hashValueMaxLen >= 16 && \ hashValueMaxLen <= CRYPT_MAX_HASHSIZE ); /* Clear return values */ memset( hashValue, 0, min( 16, hashValueMaxLen ) ); *hashValueLen = 0; /* Wrap up the hashing and record the hash value */ status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, "", 0 ); if( cryptStatusError( status ) ) return( status ); setMessageData( &msgData, hashValue, hashValueMaxLen ); status = krnlSendMessage( iHashContext, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_HASHVALUE ); if( cryptStatusError( status ) ) return( status ); *hashValueLen = msgData.length; return( CRYPT_OK ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \ int createSessionHash( const SESSION_INFO *sessionInfoPtr, INOUT SSL_HANDSHAKE_INFO *handshakeInfo ) { CRYPT_CONTEXT iHashContext; int status; assert( isReadPtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); /* Clone the current hash state, complete the hashing for the cloned context(s), and get the hash value(s) */ if( sessionInfoPtr->version < SSL_MINOR_VERSION_TLS12 ) { int hash1Size, hash2Size; /* TLS < 1.2 uses an MD5/SHA1 dual hash so we have to extract both hash values and concatenate them */ status = cloneHashContext( handshakeInfo->md5context, &iHashContext ); if( cryptStatusError( status ) ) return( status ); status = getSessionHash( iHashContext, handshakeInfo->sessionHash, CRYPT_MAX_HASHSIZE, &hash1Size ); krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT ); if( cryptStatusError( status ) ) return( status ); status = cloneHashContext( handshakeInfo->sha1context, &iHashContext ); if( cryptStatusError( status ) ) return( status ); status = getSessionHash( iHashContext, handshakeInfo->sessionHash + hash1Size, CRYPT_MAX_HASHSIZE - hash1Size, &hash2Size ); if( cryptStatusError( status ) ) { krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT ); return( status ); } handshakeInfo->sessionHashSize = hash1Size + hash2Size; } else { /* TLS 1.2 uses a single hash value */ if( handshakeInfo->sha2context != CRYPT_ERROR ) status = cloneHashContext( handshakeInfo->sha2context, &iHashContext ); else status = cloneHashContext( handshakeInfo->sha1context, &iHashContext ); if( cryptStatusError( status ) ) return( status ); status = getSessionHash( iHashContext, handshakeInfo->sessionHash, CRYPT_MAX_HASHSIZE, &handshakeInfo->sessionHashSize ); if( cryptStatusError( status ) ) { krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT ); return( status ); } } handshakeInfo->sessionHashContext = iHashContext; DEBUG_PRINT(( "Session hash:\n" )); DEBUG_DUMP_DATA( handshakeInfo->sessionHash, handshakeInfo->sessionHashSize ); return( CRYPT_OK ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \ int createCertVerify( const SESSION_INFO *sessionInfoPtr, INOUT SSL_HANDSHAKE_INFO *handshakeInfo, INOUT STREAM *stream ) { void *dataPtr; int dataLength, length DUMMY_INIT, status; assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isReadPtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); assert( isWritePtr( stream, sizeof( STREAM ) ) ); /* Get a pointer to the signature data block */ status = sMemGetDataBlockRemaining( stream, &dataPtr, &dataLength ); if( cryptStatusError( status ) ) return( status ); /* Create the signature. The reason for the min() part of the expression is that iCryptCreateSignature() gets suspicious of very large buffer sizes, for example when the user has specified the use of a huge send buffer */ if( sessionInfoPtr->version < SSL_MINOR_VERSION_TLS12 ) { CRYPT_CONTEXT iHashContext; /* Create the hash of the data to sign if necessary */ status = createCertVerifyAltHash( handshakeInfo, &iHashContext ); if( cryptStatusError( status ) ) return( status ); /* See the note above about the complexities of handling the ever- changing pre-TLS 1.2 signature format */ status = iCryptCreateSignature( dataPtr, min( dataLength, MAX_INTLENGTH_SHORT - 1 ), &length, CRYPT_FORMAT_CRYPTLIB, sessionInfoPtr->privateKey, iHashContext, NULL ); krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT ); } else { status = iCryptCreateSignature( dataPtr, min( dataLength, MAX_INTLENGTH_SHORT - 1 ), &length, CRYPT_IFORMAT_TLS12, sessionInfoPtr->privateKey, handshakeInfo->sessionHashContext, NULL ); krnlSendNotifier( handshakeInfo->sessionHashContext, IMESSAGE_DECREFCOUNT ); handshakeInfo->sessionHashContext = CRYPT_ERROR; } if( cryptStatusOK( status ) ) status = sSkip( stream, length, MAX_INTLENGTH_SHORT ); return( status ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \ int checkCertVerify( INOUT SESSION_INFO *sessionInfoPtr, INOUT SSL_HANDSHAKE_INFO *handshakeInfo, INOUT STREAM *stream, IN_LENGTH_SHORT_MIN( MIN_CRYPT_OBJECTSIZE ) \ const int sigLength ) { void *dataPtr; int status; assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isReadPtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); assert( isWritePtr( stream, sizeof( STREAM ) ) ); REQUIRES( sigLength >= MIN_CRYPT_OBJECTSIZE && \ sigLength < MAX_INTLENGTH_SHORT ); /* Get a pointer to the signature data block */ status = sMemGetDataBlock( stream, &dataPtr, sigLength ); if( cryptStatusError( status ) ) return( status ); ANALYSER_HINT( dataPtr != NULL ); /* Verify the signature. The reason for the min() part of the expression is that iCryptCheckSignature() gets suspicious of very large buffer sizes, for example when the user has specified the use of a huge send buffer */ if( sessionInfoPtr->version < SSL_MINOR_VERSION_TLS12 ) { CRYPT_CONTEXT iHashContext; /* See the note above about the complexities of handling the ever- changing pre-TLS 1.2 signature format. To catch any use of one of these formats, we check for a non-cryptlib signature being passed to us by checking for the absence of an ASN.1 SEQUENCE tag and report it as a signature-verification failures. In theory if there's ever any demand for support for this, we could take advantage of the fact that the signature can really only be RSA (DSA is gone and ECC is TLS 1.2-only) and perform a raw RSA public-key operation on the signature data, extract the lowest 20 bytes (the SHA-1 portion), and compare it with the hash data */ if( *( ( BYTE * ) dataPtr ) != 0x30 ) { assert( DEBUG_WARN ); retExt( CRYPT_ERROR_SIGNATURE, ( CRYPT_ERROR_SIGNATURE, SESSION_ERRINFO, "Couldn't verify old-style (pre-TLS 1.2) client " "certificate-verify message" ) ); } /* Create the hash of the data to verify if necessary */ status = createCertVerifyAltHash( handshakeInfo, &iHashContext ); if( cryptStatusError( status ) ) return( status ); status = iCryptCheckSignature( dataPtr, min( sigLength, MAX_INTLENGTH_SHORT - 1 ), CRYPT_FORMAT_CRYPTLIB, sessionInfoPtr->iKeyexAuthContext, iHashContext, CRYPT_UNUSED, NULL ); krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT ); } else { status = iCryptCheckSignature( dataPtr, min( sigLength, MAX_INTLENGTH_SHORT - 1 ), CRYPT_IFORMAT_TLS12, sessionInfoPtr->iKeyexAuthContext, handshakeInfo->sessionHashContext, CRYPT_UNUSED, NULL ); krnlSendNotifier( handshakeInfo->sessionHashContext, IMESSAGE_DECREFCOUNT ); handshakeInfo->sessionHashContext = CRYPT_ERROR; #ifdef CONFIG_SUITEB_TESTS if( cryptStatusOK( status ) ) { int sigKeySize; status = krnlSendMessage( sessionInfoPtr->iKeyexAuthContext, IMESSAGE_GETATTRIBUTE, &sigKeySize, CRYPT_CTXINFO_KEYSIZE ); if( cryptStatusOK( status ) ) { DEBUG_PRINT(( "Verified client's P%d authentication.\n", bytesToBits( sigKeySize ) )); } } #endif /* CONFIG_SUITEB_TESTS */ } if( cryptStatusError( status ) ) { retExt( status, ( status, SESSION_ERRINFO, "Verification of client's certificate-verify message " "failed" ) ); } return( CRYPT_OK ); } /**************************************************************************** * * * Keyex Signature Functions * * * ****************************************************************************/ /* Create/check the signature on the server key data */ CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 5 ) ) \ static int createKeyexHash( const SSL_HANDSHAKE_INFO *handshakeInfo, OUT_HANDLE_OPT CRYPT_CONTEXT *hashContext, IN_ALGO const CRYPT_ALGO_TYPE hashAlgo, IN_INT_SHORT_Z const int hashParam, IN_BUFFER( keyDataLength ) const void *keyData, IN_LENGTH_SHORT const int keyDataLength ) { CRYPT_CONTEXT iHashContext; MESSAGE_CREATEOBJECT_INFO createInfo; BYTE nonceBuffer[ SSL_NONCE_SIZE + SSL_NONCE_SIZE + 8 ]; int status; assert( isReadPtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); assert( isWritePtr( hashContext, sizeof( CRYPT_CONTEXT ) ) ); assert( isReadPtr( keyData, keyDataLength ) ); REQUIRES( hashAlgo >= CRYPT_ALGO_FIRST_HASH && \ hashAlgo <= CRYPT_ALGO_LAST_HASH ); REQUIRES( hashParam >= 0 && hashParam < MAX_INTLENGTH_SHORT ); REQUIRES( keyDataLength > 0 && keyDataLength < MAX_INTLENGTH_SHORT ); /* Clear return value */ *hashContext = CRYPT_ERROR; /* Create the hash context */ setMessageCreateObjectInfo( &createInfo, hashAlgo ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT, &createInfo, OBJECT_TYPE_CONTEXT ); if( cryptStatusError( status ) ) return( status ); iHashContext = createInfo.cryptHandle; if( hashParam != 0 ) { status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE, ( MESSAGE_CAST ) &hashParam, CRYPT_CTXINFO_BLOCKSIZE ); if( cryptStatusError( status ) ) { krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT ); return( status ); } } /* Hash the client and server nonces and key data */ memcpy( nonceBuffer, handshakeInfo->clientNonce, SSL_NONCE_SIZE ); memcpy( nonceBuffer + SSL_NONCE_SIZE, handshakeInfo->serverNonce, SSL_NONCE_SIZE ); status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, nonceBuffer, SSL_NONCE_SIZE + SSL_NONCE_SIZE ); if( cryptStatusOK( status ) ) status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, ( MESSAGE_CAST ) keyData, keyDataLength ); if( cryptStatusOK( status ) ) status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, nonceBuffer, 0 ); if( cryptStatusError( status ) ) { krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT ); return( status ); } *hashContext = iHashContext; return( CRYPT_OK ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 4 ) ) \ int createKeyexSignature( INOUT SESSION_INFO *sessionInfoPtr, INOUT SSL_HANDSHAKE_INFO *handshakeInfo, INOUT STREAM *stream, IN_BUFFER( keyDataLength ) const void *keyData, IN_LENGTH_SHORT const int keyDataLength ) { CRYPT_CONTEXT md5Context DUMMY_INIT, shaContext; void *dataPtr; int dataLength, sigLength DUMMY_INIT, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isReadPtr( keyData, keyDataLength ) ); REQUIRES( keyDataLength > 0 && keyDataLength < MAX_INTLENGTH_SHORT ); /* Hash the data to be signed */ status = createKeyexHash( handshakeInfo, &shaContext, ( handshakeInfo->keyexSigHashAlgo != CRYPT_ALGO_NONE ) ? \ handshakeInfo->keyexSigHashAlgo : CRYPT_ALGO_SHA1, handshakeInfo->keyexSigHashAlgoParam, keyData, keyDataLength ); if( cryptStatusError( status ) ) return( status ); if( sessionInfoPtr->version < SSL_MINOR_VERSION_TLS12 ) { status = createKeyexHash( handshakeInfo, &md5Context, CRYPT_ALGO_MD5, 0, keyData, keyDataLength ); if( cryptStatusError( status ) ) { krnlSendNotifier( shaContext, IMESSAGE_DECREFCOUNT ); return( status ); } } INJECT_FAULT( SESSION_BADSIG_HASH, SESSION_BADSIG_HASH_SSL_1 ); /* Sign the hashes. The reason for the min() part of the expression is that iCryptCreateSignature() gets suspicious of very large buffer sizes, for example when the user has specified the use of a huge send buffer */ status = sMemGetDataBlockRemaining( stream, &dataPtr, &dataLength ); if( cryptStatusOK( status ) ) { if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS12 ) { status = iCryptCreateSignature( dataPtr, min( dataLength, \ MAX_INTLENGTH_SHORT - 1 ), &sigLength, CRYPT_IFORMAT_TLS12, sessionInfoPtr->privateKey, shaContext, NULL ); } else { SIGPARAMS sigParams; initSigParams( &sigParams ); sigParams.iSecondHash = shaContext; status = iCryptCreateSignature( dataPtr, min( dataLength, \ MAX_INTLENGTH_SHORT - 1 ), &sigLength, CRYPT_IFORMAT_SSL, sessionInfoPtr->privateKey, md5Context, &sigParams ); } } if( sessionInfoPtr->version < SSL_MINOR_VERSION_TLS12 ) krnlSendNotifier( md5Context, IMESSAGE_DECREFCOUNT ); krnlSendNotifier( shaContext, IMESSAGE_DECREFCOUNT ); if( cryptStatusError( status ) ) return( status ); return( sSkip( stream, sigLength, MAX_INTLENGTH_SHORT ) ); } CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 4 ) ) \ int checkKeyexSignature( INOUT SESSION_INFO *sessionInfoPtr, INOUT SSL_HANDSHAKE_INFO *handshakeInfo, INOUT STREAM *stream, IN_BUFFER( keyDataLength ) const void *keyData, IN_LENGTH_SHORT const int keyDataLength, const BOOLEAN isECC ) { CRYPT_CONTEXT md5Context DUMMY_INIT, shaContext; CRYPT_ALGO_TYPE hashAlgo = CRYPT_ALGO_SHA1; void *dataPtr; int dataLength, keyexKeySize, sigKeySize DUMMY_INIT, hashParam = 0; int status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isReadPtr( keyData, keyDataLength ) ); REQUIRES( keyDataLength > 0 && keyDataLength < MAX_INTLENGTH_SHORT ); /* Make sure that there's enough data present for at least a minimal- length signature and get a pointer to the signature data */ if( sMemDataLeft( stream ) < ( isECC ? \ MIN_PKCSIZE_ECCPOINT : MIN_PKCSIZE ) ) return( CRYPT_ERROR_BADDATA ); status = sMemGetDataBlockRemaining( stream, &dataPtr, &dataLength ); if( cryptStatusError( status ) ) return( status ); ANALYSER_HINT( dataPtr != NULL ); /* TLS 1.2+ precedes the signature itself with an indication of the hash and signature algorithm that's required to verify it, so if we're using this format then we have to process the identifiers before we can create the signature-verification hashes. We disallow SHA1 since the whole point of TLS 1.2 was to move away from it, and a poll on the ietf-tls list indicated that all known implementations (both of them) work fine with this configuration */ if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS12 ) { static const MAP_TABLE hashAlgoIDTbl[] = { { 1000, CRYPT_ALGO_SHAng }, #ifdef CONFIG_SUITEB { TLS_HASHALGO_SHA384, CRYPT_ALGO_SHA2 }, #endif /* CONFIG_SUITEB */ { TLS_HASHALGO_SHA2, CRYPT_ALGO_SHA2 }, #if 0 /* 2/11/11 Disabled option for SHA1 after poll on ietf-tls list */ { TLS_HASHALGO_SHA1, CRYPT_ALGO_SHA1 }, #endif /* 0 */ { CRYPT_ERROR, 0 }, { CRYPT_ERROR, 0 } }; int cryptHashAlgo, tlsHashAlgo; /* Get the hash algorithm that we need to use. We don't care about the signature algorithm since we've already been given the public key for it */ status = tlsHashAlgo = sgetc( stream ); if( cryptStatusError( status ) ) return( status ); ( void ) sgetc( stream ); if( tlsHashAlgo <= TLS_HASHALGO_NONE || \ tlsHashAlgo >= TLS_HASHALGO_LAST ) return( CRYPT_ERROR_NOTAVAIL ); status = mapValue( tlsHashAlgo, &cryptHashAlgo, hashAlgoIDTbl, FAILSAFE_ARRAYSIZE( hashAlgoIDTbl, MAP_TABLE ) ); if( cryptStatusError( status ) ) return( CRYPT_ERROR_NOTAVAIL ); hashAlgo = cryptHashAlgo; /* int vs.enum */ if( tlsHashAlgo == TLS_HASHALGO_SHA384 ) hashParam = bitsToBytes( 384 ); } /* Hash the data to be signed */ status = createKeyexHash( handshakeInfo, &shaContext, hashAlgo, hashParam, keyData, keyDataLength ); if( cryptStatusError( status ) ) return( status ); if( sessionInfoPtr->version < SSL_MINOR_VERSION_TLS12 ) { status = createKeyexHash( handshakeInfo, &md5Context, CRYPT_ALGO_MD5, 0, keyData, keyDataLength ); if( cryptStatusError( status ) ) { krnlSendNotifier( shaContext, IMESSAGE_DECREFCOUNT ); return( status ); } } /* Check the signature on the hashes. The reason for the min() part of the expression is that iCryptCheckSignature() gets suspicious of very large buffer sizes, for example when the user has specified the use of a huge send buffer */ if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS12 ) { status = iCryptCheckSignature( dataPtr, min( dataLength, MAX_INTLENGTH_SHORT - 1 ), CRYPT_IFORMAT_TLS12, sessionInfoPtr->iKeyexCryptContext, shaContext, CRYPT_UNUSED, NULL ); } else { status = iCryptCheckSignature( dataPtr, min( dataLength, MAX_INTLENGTH_SHORT - 1 ), CRYPT_IFORMAT_SSL, sessionInfoPtr->iKeyexCryptContext, md5Context, shaContext, NULL ); } if( sessionInfoPtr->version < SSL_MINOR_VERSION_TLS12 ) krnlSendNotifier( md5Context, IMESSAGE_DECREFCOUNT ); krnlSendNotifier( shaContext, IMESSAGE_DECREFCOUNT ); if( cryptStatusError( status ) ) return( status ); /* Make sure that the relative strengths of the keyex and signature keys match. This is just a general precaution for RSA/DSA, but is mandated for ECC with Suite B in order to make the appropriate fashion statement (see the comment below). When performing the check we allow a small amount of wiggle room to deal with keygen differences */ status = krnlSendMessage( handshakeInfo->dhContext, IMESSAGE_GETATTRIBUTE, &keyexKeySize, CRYPT_CTXINFO_KEYSIZE ); if( cryptStatusOK( status ) ) status = krnlSendMessage( sessionInfoPtr->iKeyexCryptContext, IMESSAGE_GETATTRIBUTE, &sigKeySize, CRYPT_CTXINFO_KEYSIZE ); if( cryptStatusError( status ) ) return( status ); if( isECC ) { /* For ECC with Suite B the signature key size has to match the keyex key size otherwise fashion dictums are violated (if you could just sign size n with size n+1 then you wouldn't need hashsize n/n+1 and keysize n/n+1 and whatnot) */ if( sigKeySize < keyexKeySize - bitsToBytes( 8 ) ) { DEBUG_PRINT(( "Server used P%d keyex but only P%d signature.\n", bytesToBits( keyexKeySize ), bytesToBits( sigKeySize ) )); return( CRYPT_ERROR_NOSECURE ); } #ifdef CONFIG_SUITEB if( ( sessionInfoPtr->protocolFlags & SSL_PFLAG_SUITEB ) && \ sigKeySize > keyexKeySize + bitsToBytes( 8 ) ) return( CRYPT_ERROR_NOSECURE ); #ifdef CONFIG_SUITEB_TESTS DEBUG_PRINT(( "Verified server's P%d keyex with P%d signature.\n", bytesToBits( keyexKeySize ), bytesToBits( sigKeySize ) )); #endif /* CONFIG_SUITEB_TESTS */ #endif /* CONFIG_SUITEB */ } else { #if 1 /* For conventional keyex/signatures things get a bit complicated, using (say) a 1024-bit key to sign a 1536-bit keyex seems like a mismatch, but then the 1024-bit key may be regenerated relatively frequently while the 1536-bit DH parameters may be static and shared with everyone else on earth, making the high-value 1536- bit key a more viable target for attack than the singleton 1024- bit one. Because of this we allow a difference of up to 512 bits between the signing key and the keyex key */ if( sigKeySize < keyexKeySize - bitsToBytes( 512 + 32 ) ) return( CRYPT_ERROR_NOSECURE ); #else /* For conventional keyex/signatures the bounds are a bit looser because non-ECC keygen mechanisms can result in a wider variation of actual vs. nominal key size */ if( sigKeySize < keyexKeySize - bitsToBytes( 32 ) ) return( CRYPT_ERROR_NOSECURE ); #endif /* 0 */ } /* Skip the signature data */ return( readUniversal16( stream ) ); } #endif /* USE_SSL */