📄 ssl.c
字号:
isKeyxAlgo( algorithm ) ? "key exchange authentication" : \
"encryption" );
}
return( CRYPT_OK );
}
int writeSSLCertChain( SESSION_INFO *sessionInfoPtr, STREAM *stream )
{
int packetOffset, certListOffset, certListEndPos, status;
packetOffset = continueHSPacketStream( stream, SSL_HAND_CERTIFICATE );
if( sessionInfoPtr->privateKey == CRYPT_ERROR )
{
/* If there's no private key available, write an empty cert chain */
writeUint24( stream, 0 );
return( completeHSPacketStream( stream, packetOffset ) );
}
/* Write a dummy length and export the cert list to the stream */
writeUint24( stream, 0 );
certListOffset = stell( stream );
status = exportCertToStream( stream, sessionInfoPtr->privateKey,
CRYPT_ICERTFORMAT_SSL_CERTCHAIN );
if( cryptStatusError( status ) )
return( status );
certListEndPos = stell( stream );
/* Go back and insert the length, then wrap up the packet */
sseek( stream, certListOffset - LENGTH_SIZE );
writeUint24( stream, certListEndPos - certListOffset );
sseek( stream, certListEndPos );
return( completeHSPacketStream( stream, packetOffset ) );
}
/****************************************************************************
* *
* Shared Connect Functions *
* *
****************************************************************************/
/* Pre-encoded finished message templates that we can hash when we're
creating our own finished message */
#define FINISHED_TEMPLATE_SIZE 4
static const FAR_BSS SSL_MESSAGE_TEMPLATE finishedTemplate[] = {
/* byte ID = SSL_HAND_FINISHED
uint24 len = 16 + 20 (SSL), 12 (TLS) */
{ SSL_HAND_FINISHED, 0, 0, MD5MAC_SIZE + SHA1MAC_SIZE },
{ SSL_HAND_FINISHED, 0, 0, TLS_HASHEDMAC_SIZE },
{ SSL_HAND_FINISHED, 0, 0, TLS_HASHEDMAC_SIZE },
{ SSL_HAND_FINISHED, 0, 0, TLS_HASHEDMAC_SIZE },
};
/* Read/write the handshake completion data (change cipherspec + finised) */
static int readHandshakeCompletionData( SESSION_INFO *sessionInfoPtr,
const BYTE *hashValues )
{
STREAM stream;
BYTE macBuffer[ MD5MAC_SIZE + SHA1MAC_SIZE + 8 ];
const int macValueLength = \
( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL ) ? \
MD5MAC_SIZE + SHA1MAC_SIZE : TLS_HASHEDMAC_SIZE;
int length, value, status;
/* Process the other side's change cipher spec:
byte type = SSL_MSG_CHANGE_CIPHER_SPEC
byte[2] version = { 0x03, 0x0n }
uint16 len = 1
byte 1 */
status = length = readPacketSSL( sessionInfoPtr, NULL,
SSL_MSG_CHANGE_CIPHER_SPEC );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
value = sgetc( &stream );
sMemDisconnect( &stream );
if( value != 1 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid change cipher spec packet payload, expected 0x01, "
"got 0x%02X", value );
/* Change cipher spec was the last message not subject to security
encapsulation so we turn on security for the read channel after
seeing it. In addition if we're using TLS 1.1 explicit IVs the
effective header size changes because of the extra IV data, so we
record the size of the additional IV data and update the receive
buffer start offset to accomodate it */
sessionInfoPtr->flags |= SESSION_ISSECURE_READ;
if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS11 && \
sessionInfoPtr->cryptBlocksize > 1 )
{
sessionInfoPtr->sessionSSL->ivSize = sessionInfoPtr->cryptBlocksize;
sessionInfoPtr->receiveBufStartOfs += sessionInfoPtr->cryptBlocksize;
}
/* Process the other side's finished. Since this is the first chance that
we have to test whether our crypto keys are set up correctly, we
report problems with decryption or MAC'ing or a failure to find any
recognisable header as a wrong key rather than a bad data error:
byte ID = SSL_HAND_FINISHED
uint24 len
SSLv3 TLS
byte[16] MD5 MAC byte[12] hashedMAC
byte[20] SHA-1 MAC */
status = length = readPacketSSL( sessionInfoPtr, NULL,
SSL_MSG_HANDSHAKE );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
status = unwrapPacketSSL( sessionInfoPtr, &stream, SSL_MSG_HANDSHAKE );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
if( status == CRYPT_ERROR_BADDATA || \
status == CRYPT_ERROR_SIGNATURE )
retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
"Decrypted data was corrupt, probably due to incorrect "
"encryption keys being negotiated during the handshake" );
return( status );
}
status = length = checkHSPacketHeader( sessionInfoPtr, &stream,
SSL_HAND_FINISHED,
macValueLength );
if( !cryptStatusError( status ) )
{
if( length != macValueLength )
/* A length mis-match can only be an overflow, since an
underflow would be caught by checkHSPacketHeader() */
status = CRYPT_ERROR_OVERFLOW;
else
status = sread( &stream, macBuffer, macValueLength );
}
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
{
if( status == CRYPT_ERROR_BADDATA )
retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
"Invalid handshake finished packet, probably due to "
"incorrect encryption keys being negotiated during the "
"handshake" );
return( status );
}
/* Make sure that the dual MAC/hashed MAC of all preceding messages is
valid */
if( memcmp( hashValues, macBuffer, macValueLength ) )
retExt( sessionInfoPtr, CRYPT_ERROR_SIGNATURE,
"Bad MAC for handshake messages, handshake messages were "
"corrupted/modified" );
return( CRYPT_OK );
}
static int writeHandshakeCompletionData( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo,
const BYTE *hashValues,
const BOOLEAN continuedStream )
{
STREAM *stream = &handshakeInfo->stream;
int offset = 0, ccsEndPos, status;
/* Build the change cipher spec packet:
byte type = SSL_MSG_CHANGE_CIPHER_SPEC
byte[2] version = { 0x03, 0x0n }
uint16 len = 1
byte 1
Since change cipher spec is its own protocol, we use SSL-level packet
encoding rather than handshake protocol-level encoding */
if( continuedStream )
offset = continuePacketStreamSSL( stream, sessionInfoPtr,
SSL_MSG_CHANGE_CIPHER_SPEC );
else
openPacketStreamSSL( stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
SSL_MSG_CHANGE_CIPHER_SPEC );
sputc( stream, 1 );
completePacketStreamSSL( stream, offset );
ccsEndPos = stell( stream );
/* Change cipher spec was the last message not subject to security
encapsulation so we turn on security for the write channel after
seeing it. In addition if we're using TLS 1.1 explicit IVs the
effective header size changes because of the extra IV data, so we
record the size of the additional IV data and update the receive
buffer start offset to accomodate it */
sessionInfoPtr->flags |= SESSION_ISSECURE_WRITE;
if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS11 && \
sessionInfoPtr->cryptBlocksize > 1 )
{
sessionInfoPtr->sessionSSL->ivSize = sessionInfoPtr->cryptBlocksize;
sessionInfoPtr->sendBufStartOfs += sessionInfoPtr->cryptBlocksize;
}
/* Build the finished packet. The initiator sends the MAC of the
contents of every handshake packet before the finished packet, the
responder sends the MAC of the contents of every packet before its own
finished packet but including the MAC of the initiator's packet
contents:
byte ID = SSL_HAND_FINISHED
uint24 len
SSLv3 TLS
byte[16] MD5 MAC byte[12] hashedMAC
byte[20] SHA-1 MAC */
continuePacketStreamSSL( stream, sessionInfoPtr,
SSL_MSG_HANDSHAKE );
offset = continueHSPacketStream( stream, SSL_HAND_FINISHED );
swrite( stream, hashValues,
( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL ) ? \
MD5MAC_SIZE + SHA1MAC_SIZE : TLS_HASHEDMAC_SIZE );
completeHSPacketStream( stream, offset );
status = wrapPacketSSL( sessionInfoPtr, stream, ccsEndPos );
if( cryptStatusOK( status ) )
status = sendPacketSSL( sessionInfoPtr, stream,
TRUE );
sMemDisconnect( stream );
return( status );
}
/* Complete the handshake with the client or server. The logic gets a bit
complex here because the roles of the client and server are reversed if
we're resuming a session:
Normal Resumed
Client Server Client Server
------ ------ ------ ------
<--- ... Hello --->
KeyEx ---> <--- Hello
CCS ---> <--- CCS
Fin ---> <--- Fin
<--- CCS CCS --->
<--- Fin Fin --->
Because of this the handshake-completion step treats the two sides as
initiator and responder rather than client and server. The overall flow
is then:
dualMAC( initiator );
if( !initiator )
read initiator CCS + Fin;
dualMAC( responder );
send initiator/responder CCS + Fin;
if( initiator )
read responder CCS + Fin; */
static int completeHandshake( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo,
const BOOLEAN isClient,
const BOOLEAN isResumedSession )
{
CRYPT_CONTEXT initiatorMD5context, initiatorSHA1context;
CRYPT_CONTEXT responderMD5context, responderSHA1context;
BYTE masterSecret[ SSL_SECRET_SIZE + 8 ];
BYTE keyBlock[ MAX_KEYBLOCK_SIZE + 8 ];
BYTE initiatorHashes[ CRYPT_MAX_HASHSIZE * 2 ];
BYTE responderHashes[ CRYPT_MAX_HASHSIZE * 2 ];
const void *sslInitiatorString, *sslResponderString;
const void *tlsInitiatorString, *tlsResponderString;
const BOOLEAN isInitiator = isResumedSession ? !isClient : isClient;
int status;
assert( MAX_KEYBLOCK_SIZE >= ( sessionInfoPtr->authBlocksize + \
handshakeInfo->cryptKeysize +
sessionInfoPtr->cryptBlocksize ) * 2 );
assert( handshakeInfo->authAlgo == CRYPT_ALGO_NONE || \
handshakeInfo->premasterSecretSize >= SSL_SECRET_SIZE );
/* Perform the necessary juggling of values for the reversed message
flow of resumed sessions */
if( isResumedSession )
{
/* Resumed session, initiator = server, responder = client */
initiatorMD5context = handshakeInfo->serverMD5context;
initiatorSHA1context = handshakeInfo->serverSHA1context;
responderMD5context = handshakeInfo->clientMD5context;
responderSHA1context = handshakeInfo->clientSHA1context;
sslInitiatorString = SSL_SENDER_SERVERLABEL;
sslResponderString = SSL_SENDER_CLIENTLABEL;
tlsInitiatorString = "server finished";
tlsResponderString = "client finished";
}
else
{
/* Normal session, initiator = client, responder = server */
initiatorMD5context = handshakeInfo->clientMD5context;
initiatorSHA1context = handshakeInfo->clientSHA1context;
responderMD5context = handshakeInfo->serverMD5context;
responderSHA1context = handshakeInfo->serverSHA1context;
sslInitiatorString = SSL_SENDER_CLIENTLABEL;
sslResponderString = SSL_SENDER_SERVERLABEL;
tlsInitiatorString = "client finished";
tlsResponderString = "server finished";
}
/* Create the security contexts required for the session */
status = initSecurityContextsSSL( sessionInfoPtr );
if( cryptStatusError( status ) )
return( status );
/* Convert the premaster secret into the master secret */
if( !isResumedSession )
{
status = premasterToMaster( sessionInfoPtr, handshakeInfo,
masterSecret, SSL_SECRET_SIZE );
if( cryptStatusError( status ) )
return( status );
/* Everything is OK so far, add the master secret to the session
cache */
sessionInfoPtr->sessionSSL->sessionCacheID = \
addSessionCacheEntry( handshakeInfo->sessionID,
handshakeInfo->sessionIDlength,
masterSecret, SSL_SECRET_SIZE,
FALSE );
}
else
/* We've already got the master secret present from the session that
we're resuming from, reuse that */
memcpy( masterSecret, handshakeInfo->premasterSecret,
handshakeInfo->premasterSecretSize );
/* Convert the master secret into keying material. Unfortunately we
can't delete it yet because it's still needed to calculate the MAC
for the handshake messages */
status = masterToKeys( sessionInfoPtr, handshakeInfo, masterSecret,
SSL_SECRET_SIZE, keyBlock, MAX_KEYBLOCK_SIZE );
if( cryptStatusError( status ) )
{
zeroise( masterSecret, SSL_SECRET_SIZE );
return( status );
}
/* Load the keys and secrets */
status = loadKeys( sessionInfoPtr, handshakeInfo, isClient, keyBlock );
zeroise( keyBlock, MAX_KEYBLOCK_SIZE );
if( cryptStatusError( status ) )
{
zeroise( masterSecret, SSL_SECRET_SIZE );
return( status );
}
/* Complete the dual-MAC hashing of the initiator-side messages and, if
we're the responder, check that the MACs match the ones supplied by
the initiator */
if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
status = completeSSLDualMAC( initiatorMD5context, initiatorSHA1context,
initiatorHashes, sslInitiatorString,
masterSecret );
else
status = completeTLSHashedMAC( initiatorMD5context, initiatorSHA1context,
initiatorHashes, tlsInitiatorString,
masterSecret );
if( cryptStatusOK( status ) && !isInitiator )
status = readHandshakeCompletionData( sessionInfoPtr, initiatorHashes );
if( cryptStatusError( status ) )
{
zeroise( masterSecret, SSL_SECRET_SIZE );
return( status );
}
/* Now that we have the initiator MACs, complete the dual-MAC hashing of
the responder-side messages and destroy the master secret. We haven't
created the full message yet at this point so we manually hash the
individual pieces so that we can get rid of the master secret */
krnlSendMessage( responderMD5context, IMESSAGE_CTX_HASH,
( void * ) finishedTemplate[ sessionInfoPtr->version ],
FINISHED_TEMPLATE_SIZE );
krnlSendMessage( responderSHA1context, IMESSAGE_CTX_HASH,
( void * ) finishedTemplate[ sessionInfoPtr->version ],
FINISHED_TEMPLATE_SIZE );
if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
{
krnlSendMessage( responderMD5context, IMESSAGE_CTX_HASH,
initiatorHashes, MD5MAC_SIZE + SHA1MAC_SIZE );
krnlSendMessage( responderSHA1context, IMESSAGE_CTX_HASH,
initiatorHashes, MD5MAC_SIZE + SHA1MAC_SIZE );
status = completeSSLDualMAC( responderMD5context, responderSHA1context,
responderHashes, sslResponderString,
masterSecret );
}
else
{
krnlSendMessage( responderMD5context, IMESSAGE_CTX_HASH,
initiatorHashes, TLS_HASHEDMAC_SIZE );
krnlSendMessage( responderSHA1context, IMESSAGE_CTX_HASH,
initiatorHashes, TLS_HASHEDMAC_SIZE );
status = completeTLSHashedMAC( responderMD5context, responderSHA1context,
responderHashes, tlsResponderString,
masterSecret );
}
zeroise( masterSecret, SSL_SECRET_SIZE );
if( cryptStatusError( status ) )
return( status );
/* Send our MACs to the other side and read back their response if
necessary */
status = writeHandshakeCompletionData( sessionInfoPtr, handshakeInfo,
isInitiator ? initiatorHashes : \
responderHashes,
( isClient && !isResumedSession ) || \
( !isClient && isResumedSession ) );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -