📄 ssl.c
字号:
[ hdr | IV | data | MAC | pad ] |
+------------------+ | Unwrap, removes MAC, pad,
| | | returns data length
buffer length
Processing of the header and IV during unwrapping have already been
performed during the packet header read, so the two functions aren't
quite isometric */
static int wrapData( SESSION_INFO *sessionInfoPtr, BYTE *buffer,
const int length, const int type )
{
BYTE *bufPtr = buffer;
const int ivSize = \
( sessionInfoPtr->protocolFlags & SSL_PFLAG_EXPLICITIV ) ? \
sessionInfoPtr->cryptBlocksize : 0;
int startOffset = sessionInfoPtr->sendBufStartOfs, dataLength;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( length >= 0 && length <= MAX_PACKET_SIZE );
assert( isWritePtr( buffer, length ) );
assert( startOffset >= SSL_HEADER_SIZE );
/* MAC the payload */
if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
dataLength = macDataSSL( sessionInfoPtr, bufPtr + startOffset,
length, type, FALSE );
else
dataLength = macDataTLS( sessionInfoPtr, bufPtr + startOffset,
length, type, FALSE );
if( cryptStatusError( dataLength ) )
return( dataLength );
/* If it's TLS 1.1 or newer and we're using a block cipher, prepend
the IV to the data */
if( ivSize > 0 )
{
RESOURCE_DATA msgData;
assert( startOffset >= SSL_HEADER_SIZE + ivSize );
startOffset -= ivSize;
setMessageData( &msgData, bufPtr + startOffset, ivSize );
krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
}
/* Encrypt the payload */
dataLength = encryptData( sessionInfoPtr, bufPtr + startOffset,
dataLength + ivSize );
if( cryptStatusError( dataLength ) )
return( dataLength );
/* Add the packet wrapper */
*bufPtr++ = type;
*bufPtr++ = SSL_MAJOR_VERSION;
*bufPtr++ = sessionInfoPtr->version;
mputWord( bufPtr, dataLength );
return( startOffset + dataLength );
}
static int unwrapData( SESSION_INFO *sessionInfoPtr, BYTE *buffer,
const int length, const int type )
{
BOOLEAN badDecrypt = FALSE;
int dataLength, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( length >= 0 && length <= MAX_PACKET_SIZE + 20 + \
sessionInfoPtr->cryptBlocksize );
assert( isWritePtr( buffer, length ) );
/* Make sure that the length is a multiple of the block cipher size */
if( sessionInfoPtr->cryptBlocksize > 1 && \
( length % sessionInfoPtr->cryptBlocksize ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid packet length %d relative to cipher block size %d",
length, sessionInfoPtr->cryptBlocksize );
/* Decrypt the packet in the buffer. We allow zero-length blocks (once
the padding is stripped) because some versions of OpenSSL send these
as a kludge to work around chosen-IV attacks */
dataLength = decryptData( sessionInfoPtr, buffer, length );
if( cryptStatusError( dataLength ) )
{
/* If there's a padding error, don't exit immediately but record
that there was a problem for after we've done the MAC'ing.
Delaying the error reporting until then helps prevent timing
attacks of the kind described by Brice Canvel, Alain Hiltgen,
Serge Vaudenay, and Martin Vuagnoux in "Password Interception
in a SSL/TLS Channel", Crypto'03, LNCS No.2729, p.583. These
are close to impossible in most cases because we delay sending
the close notify over a much longer period than the MAC vs.non-
MAC time difference and because it requires repeatedly connecting
with a fixed-format secret such as a password at the same location
in the packet (which MS Outlook manages to do, however), but we
take this step anyway just to be safe */
if( dataLength == CRYPT_ERROR_BADDATA )
{
badDecrypt = TRUE;
dataLength = length;
}
else
return( dataLength );
}
dataLength -= sessionInfoPtr->authBlocksize;
if( dataLength < 0 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid data payload length %d", dataLength );
/* MAC the decrypted data */
if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
status = macDataSSL( sessionInfoPtr, buffer, dataLength, type, TRUE );
else
status = macDataTLS( sessionInfoPtr, buffer, dataLength, type, TRUE );
if( badDecrypt )
/* Report the delayed decrypt error, held to this point to make
timing attacks more difficult. The extended error info will have
been overwritten by the error info from the MAC'ing code, but
either message is appropriate */
return( CRYPT_ERROR_BADDATA );
if( cryptStatusError( status ) )
return( status );
return( dataLength );
}
/* Write an SSL cert chain:
byte ID = 0x0B
uint24 len
uint24 certListLen
uint24 certLen | 1...n certs ordered
byte[] cert | leaf -> root */
int writeSSLCertChain( SESSION_INFO *sessionInfoPtr, BYTE *buffer )
{
CRYPT_CERTIFICATE iCryptCert;
BYTE *bufPtr = buffer, *lengthPtr;
int length = 0, status;
/* Write the packet header and leave room for the packet length and
cert list length */
*bufPtr++ = SSL_HAND_CERTIFICATE;
lengthPtr = bufPtr;
bufPtr += LENGTH_SIZE + LENGTH_SIZE; /* len + certListLen */
/* Lock the cert chain for our exclusive use and select the leaf cert,
export each cert in turn until we reach the root, and unlock it again
to allow others access */
krnlSendMessage( sessionInfoPtr->privateKey, IMESSAGE_GETDEPENDENT,
&iCryptCert, OBJECT_TYPE_CERTIFICATE );
status = krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE,
MESSAGE_VALUE_TRUE, CRYPT_IATTRIBUTE_LOCKED );
if( cryptStatusError( status ) )
return( status );
krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE,
MESSAGE_VALUE_CURSORFIRST,
CRYPT_CERTINFO_CURRENT_CERTIFICATE );
do
{
RESOURCE_DATA msgData;
setMessageData( &msgData, bufPtr + LENGTH_SIZE,
sessionInfoPtr->sendBufSize - \
( bufPtr + LENGTH_SIZE - sessionInfoPtr->sendBuffer ) );
status = krnlSendMessage( sessionInfoPtr->privateKey,
IMESSAGE_CRT_EXPORT, &msgData,
CRYPT_CERTFORMAT_CERTIFICATE );
*bufPtr++ = 0;
mputWord( bufPtr, msgData.length );
bufPtr += msgData.length;
length += msgData.length + LENGTH_SIZE;
}
while( cryptStatusOK( status ) && \
krnlSendMessage( sessionInfoPtr->privateKey,
IMESSAGE_SETATTRIBUTE, MESSAGE_VALUE_CURSORNEXT,
CRYPT_CERTINFO_CURRENT_CERTIFICATE ) == CRYPT_OK );
krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE, MESSAGE_VALUE_FALSE,
CRYPT_IATTRIBUTE_LOCKED );
if( cryptStatusError( status ) )
return( status );
/* Go back and add the overall packet length and cert chain length at the
start of the packet */
*lengthPtr++ = 0; /* len */
mputWord( lengthPtr, length + LENGTH_SIZE );
*lengthPtr++ = 0; /* certListLen */
mputWord( lengthPtr, length );
return( ID_SIZE + LENGTH_SIZE + LENGTH_SIZE + length );
}
/* Read/write an SSL certificate verify message:
byte ID = 0x0F
uint24 len
byte[] signature
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 raw,
non-PKCS #1 RSA (that is, it uses the private key to encrypt the
concatenated SHA-1 and MD5 MAC or hash of the handshake messages), 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, 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 info down through several
cryptlib layers and then hope that the PKCS #11 implementation 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, which severely screws up the handshake processing flow.
The chances of all of this working correctly are fairly low, and in any
case there's no advantage to the weird mechanism and format used in
SSL/TLS, all 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
certs 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 */
int processCertVerify( const SESSION_INFO *sessionInfoPtr,
const SSL_HANDSHAKE_INFO *handshakeInfo,
void *signature, const int signatureLength,
const int signatureMaxLength )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
BYTE nonceBuffer[ 64 + SSL_NONCE_SIZE + SSL_NONCE_SIZE ];
int length, status;
/* Hash the client and server nonces */
setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_SHA );
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 );
krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
nonceBuffer, 18 + SSL_NONCE_SIZE + SSL_NONCE_SIZE );
krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
nonceBuffer, 0 );
/* Create or verify the signature as appropriate */
if( signatureLength )
status = iCryptCheckSignatureEx( signature, signatureLength,
CRYPT_FORMAT_CRYPTLIB,
sessionInfoPtr->iKeyexAuthContext,
createInfo.cryptHandle, NULL );
else
status = iCryptCreateSignatureEx( signature, &length,
signatureMaxLength,
CRYPT_FORMAT_CRYPTLIB,
sessionInfoPtr->privateKey,
createInfo.cryptHandle,
CRYPT_UNUSED, CRYPT_UNUSED );
krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
return( ( cryptStatusOK( status ) && !signatureLength ) ? \
length : status );
}
/* Process version information from a peer */
int processVersionInfo( SESSION_INFO *sessionInfoPtr, const int version )
{
switch( version )
{
case SSL_MINOR_VERSION_SSL:
/* If the other side can't do TLS, fall back to SSL */
if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS )
sessionInfoPtr->version = SSL_MINOR_VERSION_SSL;
break;
case SSL_MINOR_VERSION_TLS:
/* If the other side can't do TLS 1.1, fall back to TLS 1.0 */
if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS11 )
sessionInfoPtr->version = SSL_MINOR_VERSION_TLS;
break;
case SSL_MINOR_VERSION_TLS11:
break;
default:
/* If we're the server and the client has offered a vaguely
sensible version, fall back to the highest version we
support */
if( ( sessionInfoPtr->flags && SESSION_ISSERVER ) && \
version <= 5 )
{
sessionInfoPtr->version = SSL_MINOR_VERSION_TLS11;
break;
}
/* It's nothing we can handle */
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid protocol version %d", version );
}
return( CRYPT_OK );
}
/* Wrap a handshake packet, taking as input a data packet with a 5-byte gap
at the start for the header and wrapping it up as appropriate in the
SSL/TLS packet encapsulation
byte type = 22 (handshake)
byte[2] version = { 0x03, 0x0n }
uint16 len */
void wrapHandshakePacket( void *data, const int length,
const int protocolVersion )
{
BYTE *dataPtr = data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -