📄 ssl.c
字号:
/****************************************************************************
* *
* cryptlib SSL v3/TLS Session Management *
* Copyright Peter Gutmann 1998-2004 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "misc_rw.h"
#include "session.h"
#include "ssl.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../misc/misc_rw.h"
#include "session.h"
#include "ssl.h"
#else
#include "crypt.h"
#include "misc/misc_rw.h"
#include "session/session.h"
#include "session/ssl.h"
#endif /* Compiler-specific includes */
#ifdef USE_SSL
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Initialise and destroy the handshake state information */
static void destroyHandshakeInfo( SSL_HANDSHAKE_INFO *handshakeInfo )
{
/* Destroy any active contexts. We need to do this here (even though
it's also done in the general session code) to provide a clean exit in
case the session activation fails, so that a second activation attempt
doesn't overwrite still-active contexts */
destroyHandshakeCryptInfo( handshakeInfo );
zeroise( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) );
}
static int initHandshakeInfo( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo,
const BOOLEAN isServer )
{
memset( handshakeInfo, 0, sizeof( SSL_HANDSHAKE_INFO ) );
if( isServer )
{
initSSLserverProcessing( handshakeInfo );
/* Check whether the server key is signature-capable. If it is,
it can be used to authenticate a DH key exchange (the default
server key encryption capability only handles RSA key
exchange) */
if( cryptStatusOK( \
krnlSendMessage( sessionInfoPtr->privateKey,
IMESSAGE_CHECK, NULL,
MESSAGE_CHECK_PKC_SIGN ) ) )
handshakeInfo->serverSigKey = TRUE;
}
else
initSSLclientProcessing( handshakeInfo );
return( initHandshakeCryptInfo( handshakeInfo ) );
}
/* SSL uses 24-bit lengths in some places even though the maximum packet
length is only 16 bits (actually it's limited even further by the spec
to 14 bits). To handle this odd length, we define our own read/
writeUint24() functions that always set the high byte to zero */
int readUint24( STREAM *stream )
{
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
status = sgetc( stream );
if( cryptStatusError( status ) )
return( status );
if( status != 0 )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
return( readUint16( stream ) );
}
int writeUint24( STREAM *stream, const int length )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length >= 0 && length < CRYPT_MAX_IVSIZE + MAX_PACKET_SIZE + \
CRYPT_MAX_HASHSIZE + CRYPT_MAX_IVSIZE );
sputc( stream, 0 );
return( writeUint16( stream, length ) );
}
/* Choose the best cipher suite from a list of suites. There are a pile of
DH cipher suites, in practice only DHE is used, DH requires the use of
X9.42 DH certs (there aren't any) and DH_anon uses unauthenticated DH
which implementers seem to have an objection to even though it's not much
different in effect from the way RSA cipher suites are used in practice.
To keep things simple for the caller, we only allow RSA auth for DH key
agreement and not DSA, since the former also automatically works for the
far more common RSA key exchange that's usually used for key setup */
int processCipherSuite( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo,
STREAM *stream, const int noSuites )
{
const static struct {
const int cipherSuite;
const CRYPT_ALGO_TYPE keyexAlgo, authAlgo, cryptAlgo, macAlgo;
const int cryptKeySize, macBlockSize;
} cipherSuiteInfo[] = {
/* PSK suites */
{ TLS_PSK_WITH_3DES_EDE_CBC_SHA,
CRYPT_ALGO_NONE, CRYPT_ALGO_NONE, CRYPT_ALGO_3DES,
CRYPT_ALGO_HMAC_SHA, 24, SHA1MAC_SIZE },
{ TLS_PSK_WITH_AES_256_CBC_SHA,
CRYPT_ALGO_NONE, CRYPT_ALGO_NONE, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 32, SHA1MAC_SIZE },
{ TLS_PSK_WITH_AES_128_CBC_SHA,
CRYPT_ALGO_NONE, CRYPT_ALGO_NONE, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 16, SHA1MAC_SIZE },
{ TLS_PSK_WITH_RC4_128_SHA,
CRYPT_ALGO_NONE, CRYPT_ALGO_NONE, CRYPT_ALGO_RC4,
CRYPT_ALGO_HMAC_SHA, 16, SHA1MAC_SIZE },
#ifdef PREFER_DH_SUITES
/* 3DES with DH */
{ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_RSA, CRYPT_ALGO_3DES,
CRYPT_ALGO_HMAC_SHA, 24, SHA1MAC_SIZE },
/* { TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_DSA, CRYPT_ALGO_3DES,
CRYPT_ALGO_HMAC_SHA, 24, SHA1MAC_SIZE }, */
/* AES with DH */
{ TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_RSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 32, SHA1MAC_SIZE },
/* { TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_DSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 32, SHA1MAC_SIZE }, */
{ TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_RSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 16, SHA1MAC_SIZE },
/* { TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_DSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 16, SHA1MAC_SIZE }, */
/* 3DES with RSA */
{ SSL_RSA_WITH_3DES_EDE_CBC_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_RSA, CRYPT_ALGO_3DES,
CRYPT_ALGO_HMAC_SHA, 24, SHA1MAC_SIZE },
/* AES with RSA */
{ TLS_RSA_WITH_AES_256_CBC_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_RSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 32, SHA1MAC_SIZE },
{ TLS_RSA_WITH_AES_128_CBC_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_RSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 16, SHA1MAC_SIZE },
#else
/* 3DES with RSA */
{ SSL_RSA_WITH_3DES_EDE_CBC_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_RSA, CRYPT_ALGO_3DES,
CRYPT_ALGO_HMAC_SHA, 24, SHA1MAC_SIZE },
/* AES with RSA */
{ TLS_RSA_WITH_AES_256_CBC_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_RSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 32, SHA1MAC_SIZE },
{ TLS_RSA_WITH_AES_128_CBC_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_RSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 16, SHA1MAC_SIZE },
/* 3DES with DH */
{ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_RSA, CRYPT_ALGO_3DES,
CRYPT_ALGO_HMAC_SHA, 24, SHA1MAC_SIZE },
/* { TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_DSA, CRYPT_ALGO_3DES,
CRYPT_ALGO_HMAC_SHA, 24, SHA1MAC_SIZE }, */
/* AES with DH */
{ TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_RSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 32, SHA1MAC_SIZE },
/* { TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_DSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 32, SHA1MAC_SIZE }, */
{ TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_RSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 16, SHA1MAC_SIZE },
/* { TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_DSA, CRYPT_ALGO_AES,
CRYPT_ALGO_HMAC_SHA, 16, SHA1MAC_SIZE }, */
#endif /* PREFER_DH_SUITES */
/* IDEA + RSA */
{ SSL_RSA_WITH_IDEA_CBC_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_RSA, CRYPT_ALGO_IDEA,
CRYPT_ALGO_HMAC_SHA, 16, SHA1MAC_SIZE },
/* RC4 + RSA */
{ SSL_RSA_WITH_RC4_128_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_RSA, CRYPT_ALGO_RC4,
CRYPT_ALGO_HMAC_SHA, 16, SHA1MAC_SIZE },
{ SSL_RSA_WITH_RC4_128_MD5,
CRYPT_ALGO_RSA, CRYPT_ALGO_RSA, CRYPT_ALGO_RC4,
CRYPT_ALGO_HMAC_MD5, 16, MD5MAC_SIZE },
/* DES + RSA */
{ SSL_RSA_WITH_DES_CBC_SHA,
CRYPT_ALGO_RSA, CRYPT_ALGO_RSA, CRYPT_ALGO_DES,
CRYPT_ALGO_HMAC_SHA, 8, SHA1MAC_SIZE },
{ TLS_DHE_RSA_WITH_DES_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_RSA, CRYPT_ALGO_DES,
CRYPT_ALGO_HMAC_SHA, 8, SHA1MAC_SIZE },
/* { TLS_DHE_DSS_WITH_DES_CBC_SHA,
CRYPT_ALGO_DH, CRYPT_ALGO_DSA, CRYPT_ALGO_DES,
CRYPT_ALGO_HMAC_SHA, 8, SHA1MAC_SIZE }, */
/* End-of-list marker */
{ SSL_NULL_WITH_NULL,
CRYPT_ALGO_NONE, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE, 0, 0 }
};
CRYPT_QUERY_INFO queryInfo;
const BOOLEAN isServer = ( sessionInfoPtr->flags & SESSION_ISSERVER ) ? \
TRUE : FALSE;
int currentSuiteIndex = 999, i, status;
for( i = 0; i < noSuites; i++ )
{
int currentSuite, suiteInfoIndex;
/* If we're reading an SSLv2 hello and it's an SSLv2 suite (the high
byte is nonzero), skip it and continue */
if( handshakeInfo->isSSLv2 )
{
currentSuite = sgetc( stream );
if( cryptStatusError( currentSuite ) )
retExt( sessionInfoPtr, currentSuite,
"Invalid cipher suite information" );
if( currentSuite != 0 )
{
readUint16( stream );
continue;
}
}
/* Get the cipher suite info */
currentSuite = readUint16( stream );
if( cryptStatusError( currentSuite ) )
retExt( sessionInfoPtr, currentSuite,
"Invalid cipher suite information" );
#if 0 /* When resuming a cached session, the client is required to offer
as one of its suites the original suite that was used. There is
no good reason for this requirement (it's probable that the spec
is intending that there be at least one cipher suite, and that if
there's only one it should really be the one originally
negotiated) and it complicates implementation of shared-secret
key sessions, so we don't perform this check */
/* If we have to match a specific suite and this isn't it,
continue */
if( requiredSuite > 0 && requiredSuite != currentSuite )
continue;
#endif /* 0 */
/* If we're the client and we got back our canary method-of-last-
resort suite from the server, the server is incapable of handling
non-crippled crypto. Veni, vidi, volo in domum redire */
if( !isServer && currentSuite == SSL_RSA_EXPORT_WITH_RC4_40_MD5 )
retExt( sessionInfoPtr, CRYPT_ERROR_NOSECURE,
"Server rejected attempt to connect using non-crippled "
"encryption" );
/* Try and find the info for the proposed cipher suite */
for( suiteInfoIndex = 0; \
cipherSuiteInfo[ suiteInfoIndex ].cipherSuite != SSL_NULL_WITH_NULL && \
cipherSuiteInfo[ suiteInfoIndex ].cipherSuite != currentSuite; \
suiteInfoIndex++ );
if( cipherSuiteInfo[ suiteInfoIndex ].cipherSuite == SSL_NULL_WITH_NULL )
continue;
/* If the new suite is less preferred than the existing one, don't
try and work with it */
if( suiteInfoIndex >= currentSuiteIndex )
continue;
/* Make sure that the required algorithms are available. We don't
have to check the MAC algorithms since MD5 and SHA-1 are always
available as they're required for the handshake */
if( !algoAvailable( cipherSuiteInfo[ suiteInfoIndex ].cryptAlgo ) )
continue;
if( ( cipherSuiteInfo[ suiteInfoIndex ].keyexAlgo != \
cipherSuiteInfo[ suiteInfoIndex ].authAlgo ) && \
!algoAvailable( cipherSuiteInfo[ suiteInfoIndex ].keyexAlgo ) )
continue;
/* If it's a DH suite and the server key can't be used for signing
(needed to authenticate the DH exchange), we can't use the DH
suite */
if( isServer && !handshakeInfo->serverSigKey && \
cipherSuiteInfo[ suiteInfoIndex ].keyexAlgo == CRYPT_ALGO_DH )
continue;
/* We've found a more-preferred available suite, go with that */
currentSuiteIndex = suiteInfoIndex;
}
if( currentSuiteIndex > 50 )
/* We couldn't find anything to use, exit */
retExt( sessionInfoPtr, CRYPT_ERROR_NOTAVAIL,
"No encryption algorithm compatible with the remote system "
"could be found" );
/* We got a cipher suite that we can handle, set up the crypto info */
handshakeInfo->cipherSuite = cipherSuiteInfo[ currentSuiteIndex ].cipherSuite;
handshakeInfo->keyexAlgo = cipherSuiteInfo[ currentSuiteIndex ].keyexAlgo;
handshakeInfo->authAlgo = cipherSuiteInfo[ currentSuiteIndex ].authAlgo;
handshakeInfo->cryptKeysize = cipherSuiteInfo[ currentSuiteIndex ].cryptKeySize;
sessionInfoPtr->cryptAlgo = cipherSuiteInfo[ currentSuiteIndex ].cryptAlgo;
sessionInfoPtr->integrityAlgo = cipherSuiteInfo[ currentSuiteIndex ].macAlgo;
if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
/* SSL uses a proto-HMAC which requires that we synthesize it from
raw hash functionality */
sessionInfoPtr->integrityAlgo = \
( sessionInfoPtr->integrityAlgo == CRYPT_ALGO_HMAC_MD5 ) ? \
CRYPT_ALGO_MD5 : CRYPT_ALGO_SHA;
sessionInfoPtr->authBlocksize = cipherSuiteInfo[ currentSuiteIndex ].macBlockSize;
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_QUERYCAPABILITY, &queryInfo,
sessionInfoPtr->cryptAlgo );
if( cryptStatusError( status ) )
return( status );
sessionInfoPtr->cryptBlocksize = queryInfo.blockSize;
return( CRYPT_OK );
}
/* Process RFC 3546 TLS extensions:
uint16 extListLen | RFC 3546
uint16 extType
uint16 extLen
byte[] extData */
static int processExtensions( SESSION_INFO *sessionInfoPtr, STREAM *stream,
const int length )
{
int endPos = stell( stream ) + length, extListLen;
/* Read the extension header and make sure that it's valid */
if( length < UINT16_SIZE + UINT16_SIZE + UINT16_SIZE + 1 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"TLS hello contains %d bytes extraneous data", length );
extListLen = readUint16( stream );
if( cryptStatusError( extListLen ) || \
extListLen != length - UINT16_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid TLS extension data length %d, should be %d",
extListLen, length - UINT16_SIZE );
/* Process the extensions */
while( stell( stream ) < endPos )
{
int type, extLen, value;
/* Get the next extension */
type = readUint16( stream );
extLen = readUint16( stream );
if( cryptStatusError( extLen ) || extLen < 1 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid TLS extension list item header" );
/* Process the extension data. The internal structure of some of
these things shows that they were created by ASN.1 people... */
switch( type )
{
case TLS_EXT_SERVER_NAME:
{
int listLen;
/* Response: Send zero-length reply to peer:
uint16 listLen
byte nameType
uint16 nameLen
byte[] name */
listLen = readUint16( stream );
if( cryptStatusError( listLen ) || \
listLen != extLen - UINT16_SIZE || \
listLen < 1 + UINT16_SIZE || \
cryptStatusError( sSkip( stream, listLen ) ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid TLS host name extension" );
/* Parsing of further SEQUENCE OF SEQUENCE data omitted */
break;
}
case TLS_EXT_MAX_FRAGMENT_LENTH:
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -