⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ssl.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:
			{
			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 + -