📄 ssl.c
字号:
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid cipher suite information" ) );
}
suiteLength /= UINT16_SIZE;
}
status = processCipherSuite( sessionInfoPtr, handshakeInfo, stream,
suiteLength );
if( cryptStatusError( status ) )
return( status );
/* Process the compression suite information. Since we don't implement
compression, all that we need to do is check that the format is valid
and then skip the suite information */
if( isServer )
{
/* If we're reading the client hello, the packet contains a
selection of suites preceded by a suite count */
suiteLength = sgetc( stream );
if( cryptStatusError( suiteLength ) || suiteLength < 1 )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid compression suite information" ) );
}
}
status = sSkip( stream, suiteLength );
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid compression algorithm information" ) );
}
/* If there's extra data present at the end of the packet, check for TLS
extension data */
if( endPos - stell( stream ) > 0 )
{
status = processExtensions( sessionInfoPtr, stream,
endPos - stell( stream ) );
if( cryptStatusError( status ) )
return( status );
handshakeInfo->hasExtensions = TRUE;
}
return( potentiallyResumedSession ? OK_SPECIAL : CRYPT_OK );
}
/****************************************************************************
* *
* Certificate-handling Functions *
* *
****************************************************************************/
/* Read/write an SSL cert chain:
byte ID = SSL_HAND_CERTIFICATE
uint24 len
uint24 certListLen
uint24 certLen | 1...n certs ordered
byte[] cert | leaf -> root */
int readSSLCertChain( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo, STREAM *stream,
CRYPT_CERTIFICATE *iCertChain,
const BOOLEAN isServer )
{
CRYPT_ALGO_TYPE algorithm;
CRYPT_CERTIFICATE iLocalCertChain;
const ATTRIBUTE_LIST *fingerprintPtr = \
findSessionInfo( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SERVER_FINGERPRINT );
MESSAGE_DATA msgData;
BYTE certFingerprint[ CRYPT_MAX_HASHSIZE + 8 ];
const char *peerTypeName = isServer ? "Client" : "Server";
int certFingerprintLength, chainLength, length, status;
/* Clear return value */
*iCertChain = CRYPT_ERROR;
/* Make sure that the packet header is in order */
status = checkHSPacketHeader( sessionInfoPtr, stream, &length,
SSL_HAND_CERTIFICATE,
isServer ? 0 : LENGTH_SIZE + MIN_CERTSIZE );
if( cryptStatusError( status ) )
return( status );
if( isServer && length < LENGTH_SIZE + MIN_CERTSIZE )
{
/* There is a special case in which a too-short cert packet is valid
and that's where it constitutes the TLS equivalent of an SSL
no-certs alert. SSLv3 sent an SSL_ALERT_NO_CERTIFICATE alert to
indicate that the client doesn't have a cert, which is handled by
the readHSPacketSSL() call. TLS changed this to send an empty
cert packet instead, supposedly because it lead to implementation
problems (presumably it's necessary to create a state machine-
based implementation to reproduce these problems, whatever they
are). The TLS 1.0 spec is ambiguous as to what constitutes an
empty packet, it could be either a packet with a length of zero
or a packet containing a zero-length cert list so we check for
both. TLS 1.1 fixed this to say that that certListLen entry has
a length of zero. To report this condition we fake the error
indicators for consistency with the status obtained from an SSLv3
no-cert alert */
if( length == 0 || length == LENGTH_SIZE )
{
ERROR_INFO *errorInfo = &sessionInfoPtr->errorInfo;
errorInfo->errorCode = SSL_ALERT_NO_CERTIFICATE;
retExt( CRYPT_ERROR_PERMISSION,
( CRYPT_ERROR_PERMISSION, SESSION_ERRINFO,
"Received TLS alert message: No certificate" ) );
}
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid certificate chain" ) );
}
chainLength = readUint24( stream );
if( cryptStatusError( chainLength ) || \
chainLength < MIN_CERTSIZE || chainLength != length - LENGTH_SIZE )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid cert chain length %d, should be %d",
chainLength, length - LENGTH_SIZE ) );
}
/* Import the cert chain. This isn't a true cert chain (in the sense
of being degenerate PKCS #7 SignedData) but a special-case SSL-
encoded cert chain */
status = importCertFromStream( stream, &iLocalCertChain,
CRYPT_ICERTTYPE_SSL_CERTCHAIN,
chainLength );
if( cryptStatusError( status ) )
{
/* There are sufficient numbers of broken certs around that if we
run into a problem importing one we provide a custom error
message telling the user to try again with a reduced compliance
level */
if( status == CRYPT_ERROR_BADDATA || status == CRYPT_ERROR_INVALID )
{
retExt( status,
( status, SESSION_ERRINFO,
"%s provided a broken/invalid certificate, try again "
"with a reduced level of certificate compliance "
"checking", peerTypeName ) );
}
retExt( status,
( status, SESSION_ERRINFO, "Invalid certificate chain" ) );
}
/* Get information on the chain */
status = krnlSendMessage( iLocalCertChain, IMESSAGE_GETATTRIBUTE,
&algorithm, CRYPT_CTXINFO_ALGO );
if( cryptStatusError( status ) )
{
krnlSendNotifier( iLocalCertChain, IMESSAGE_DECREFCOUNT );
return( status );
}
setMessageData( &msgData, certFingerprint, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( iLocalCertChain, IMESSAGE_GETATTRIBUTE_S,
&msgData,
( fingerprintPtr != NULL && \
fingerprintPtr->valueLength == 16 ) ? \
CRYPT_CERTINFO_FINGERPRINT_MD5 : \
CRYPT_CERTINFO_FINGERPRINT_SHA );
if( cryptStatusError( status ) )
{
krnlSendNotifier( iLocalCertChain, IMESSAGE_DECREFCOUNT );
return( status );
}
certFingerprintLength = msgData.length;
if( !isServer && algorithm != handshakeInfo->authAlgo )
{
krnlSendNotifier( iLocalCertChain, IMESSAGE_DECREFCOUNT );
retExt( CRYPT_ERROR_WRONGKEY,
( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO,
"%s key algorithm %d doesn't match negotiated algorithm "
"%d", peerTypeName, algorithm, handshakeInfo->authAlgo ) );
}
/* Either compare the cert fingerprint to a supplied one or save it for
the caller to examine */
if( fingerprintPtr != NULL )
{
/* The caller has supplied a cert fingerprint, compare it to the
received cert's fingerprint to make sure that we're talking to
the right system */
if( fingerprintPtr->valueLength != certFingerprintLength || \
memcmp( fingerprintPtr->value, certFingerprint,
certFingerprintLength ) )
{
krnlSendNotifier( iLocalCertChain, IMESSAGE_DECREFCOUNT );
retExt( CRYPT_ERROR_WRONGKEY,
( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO,
"%s key didn't match key fingerprint", peerTypeName ) );
}
}
else
{
/* Remember the cert fingerprint in case the caller wants to check
it. We don't worry if the add fails, it's a minor thing and not
worth aborting the handshake for */
( void ) addSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SERVER_FINGERPRINT,
certFingerprint, certFingerprintLength );
}
/* Make sure that we can perform the required operation using the key
that we've been given. For a client key we need signing capability,
for a server key using DH key agreement we also need signing
capability to authenticate the DH parameters, and for a server key
using RSA key transport we need encryption capability. This
operation also performs a variety of additional checks alongside the
obvious one, so it's a good general health check before we go any
further */
status = krnlSendMessage( iLocalCertChain, IMESSAGE_CHECK, NULL,
isServer || isKeyxAlgo( algorithm ) ? \
MESSAGE_CHECK_PKC_SIGCHECK : \
MESSAGE_CHECK_PKC_ENCRYPT );
if( cryptStatusError( status ) )
{
krnlSendNotifier( iLocalCertChain, IMESSAGE_DECREFCOUNT );
retExt( CRYPT_ERROR_WRONGKEY,
( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO,
"%s provided a key incapable of being used for %s",
peerTypeName,
isServer ? "client authentication" : \
isKeyxAlgo( algorithm ) ? "key exchange authentication" : \
"encryption" ) );
}
*iCertChain = iLocalCertChain;
return( CRYPT_OK );
}
int writeSSLCertChain( SESSION_INFO *sessionInfoPtr, STREAM *stream )
{
int packetOffset, certListOffset = DUMMY_INIT, 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 */
status = writeUint24( stream, 0 );
if( cryptStatusOK( status ) )
{
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
typedef BYTE SSL_MESSAGE_TEMPLATE[ FINISHED_TEMPLATE_SIZE ];
static const SSL_MESSAGE_TEMPLATE FAR_BSS 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 = readHSPacketSSL( sessionInfoPtr, NULL, &length,
SSL_MSG_CHANGE_CIPHER_SPEC );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
value = sgetc( &stream );
sMemDisconnect( &stream );
if( value != 1 )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"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 = readHSPacketSSL( sessionInfoPtr, NULL, &length,
SSL_MSG_HANDSHAKE );
if( cryptStatusError( status ) )
return( status );
status = unwrapPacketSSL( sessionInfoPtr, sessionInfoPtr->receiveBuffer,
length, &length, SSL_MSG_HANDSHAKE );
if( cryptStatusError( status ) )
{
if( status == CRYPT_ERROR_BADDATA || \
status == CRYPT_ERROR_SIGNATURE )
{
retExtErr( CRYPT_ERROR_WRONGKEY,
( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO,
SESSION_ERRINFO,
"Decrypted data was corrupt, probably due to "
"incorrect encryption keys being negotiated "
"during the handshake: " ) );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -