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

📄 ssh1.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 5 页
字号:
	if( sessionInfoPtr->keyFingerprintSize > 0 )
		{
		/* There's an existing fingerprint value, make sure that it matches 
		   what we just calculated */
		if( sessionInfoPtr->keyFingerprintSize != hashSize || \
			memcmp( sessionInfoPtr->keyFingerprint, fingerPrint, hashSize ) )
			retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
					"Server key didn't match fingerprint" );
		}
	else
		{
		/* Remember the value for the caller */
		memcpy( sessionInfoPtr->keyFingerprint, fingerPrint, hashSize );
		sessionInfoPtr->keyFingerprintSize = hashSize;
		}
	return( CRYPT_OK );
	}

/* Generate a response to an RSA authentication challenge */

static void generateChallengeResponse( BYTE *response, 
									   const SSH_HANDSHAKE_INFO *handshakeInfo,
									   const BYTE *challenge )
	{
	HASHFUNCTION hashFunction;
	HASHINFO hashInfo;

	/* Hash the session ID and challenge:
		hash( sessionID || challenge ) */
	getHashParameters( CRYPT_ALGO_MD5, &hashFunction, NULL );
	hashFunction( hashInfo, NULL, ( BYTE * ) handshakeInfo->sessionID, 
				  handshakeInfo->sessionIDlength, HASH_START );
	hashFunction( hashInfo, response, ( BYTE * ) challenge,
				  SSH1_CHALLENGE_SIZE, HASH_END );
	}

/* Process the public key data.  The preceding key length value isn't useful 
   because it contains the nominal key size in bits rather than the size of 
   the following data, so we have to dig into the data to find out how much 
   there is.  In addition we need to take a copy of the key modulus since 
   it's needed later for calculating the session ID */

static int processPublickeyData( SSH_HANDSHAKE_INFO *handshakeInfo, 
								 const void *data, const int dataLength,
								 const BOOLEAN isServerKey, 
								 SESSION_INFO *sessionInfoPtr )
	{
	BYTE *dataPtr = ( BYTE * ) data, *ePtr;
	int nominalLength, eLength, nLength;

	nominalLength = ( int ) mgetLong( dataPtr );
	nominalLength = bitsToBytes( nominalLength );
	if( nominalLength < bitsToBytes( MIN_PKCSIZE_BITS ) || \
		nominalLength > CRYPT_MAX_PKCSIZE )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid public key size %d", nominalLength );
	eLength = mgetWord( dataPtr );
	eLength = bitsToBytes( eLength );
	if( LENGTH_SIZE + SSH1_MPI_LENGTH_SIZE + eLength + \
					  SSH1_MPI_LENGTH_SIZE + nominalLength > dataLength )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid exponent size %d for key size %d", eLength,
				nominalLength );
	ePtr = dataPtr;
	dataPtr += eLength;
	nLength = mgetWord( dataPtr );
	nLength = bitsToBytes( nLength );
	if( nLength != nominalLength )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Public key size %d doesn't match modulus size %d", 
				nominalLength, nLength );
	if( isServerKey )
		{
		memcpy( handshakeInfo->serverModulus, dataPtr, nLength );
		handshakeInfo->serverModulusLength = nLength;
		}
	else
		{
		memcpy( handshakeInfo->hostModulus, dataPtr, nLength );
		handshakeInfo->hostModulusLength = nLength;
		}
	if( sessionInfoPtr != NULL )
		{
		int status;

		status = processKeyFingerprint( sessionInfoPtr, dataPtr, nLength, 
										ePtr, eLength );
		if( cryptStatusError( status ) )
			return( status );
		}
	
	return( LENGTH_SIZE + SSH1_MPI_LENGTH_SIZE + eLength + \
						  SSH1_MPI_LENGTH_SIZE + nLength );
	}

/* Set up the security information required for the session */

static int initSecurityInfoSSH1( SESSION_INFO *sessionInfoPtr,
								 SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	RESOURCE_DATA msgData;
	int keySize, ivSize, status;

	/* Create the security contexts required for the session */
	status = initSecurityContexts( sessionInfoPtr );
	if( cryptStatusError( status ) )
		return( status );
	if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
		/* For Blowfish the session key size doesn't match the default 
		   Blowfish key size so we explicitly specify its length */
		keySize = SSH1_SECRET_SIZE;
	else
		krnlSendMessage( sessionInfoPtr->iCryptInContext,
						 IMESSAGE_GETATTRIBUTE, &keySize,
						 CRYPT_CTXINFO_KEYSIZE );
	if( krnlSendMessage( sessionInfoPtr->iCryptInContext,
						 IMESSAGE_GETATTRIBUTE, &ivSize,
						 CRYPT_CTXINFO_IVSIZE ) == CRYPT_ERROR_NOTAVAIL )
		/* It's a stream cipher */
		ivSize = 0;

	/* Load the keys.  For RC4, which is IV-less, the session key is split 
	   into two parts, with the first part being the receive key and the 
	   second part being the send key.  For other algorithms, the entire
	   session key is used for both send and receive contexts, leading to
	   a simple attack on the first data block since the initial IV is all 
	   zeroes */
	setMessageData( &msgData, ( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_RC4 ) ? \
					handshakeInfo->secretValue + 16 : handshakeInfo->secretValue,
					keySize );
	status = krnlSendMessage( sessionInfoPtr->iCryptOutContext, 
							  IMESSAGE_SETATTRIBUTE_S, &msgData, 
							  CRYPT_CTXINFO_KEY );
	if( cryptStatusOK( status ) )
		{
		setMessageData( &msgData, handshakeInfo->secretValue, keySize );
		status = krnlSendMessage( sessionInfoPtr->iCryptInContext, 
								  IMESSAGE_SETATTRIBUTE_S, &msgData, 
								  CRYPT_CTXINFO_KEY );
		}
	if( cryptStatusOK( status ) && ivSize > 0 )
		{
		static const char iv[ CRYPT_MAX_IVSIZE ] = { 0 };

		setMessageData( &msgData, ( void * ) iv, ivSize );
		krnlSendMessage( sessionInfoPtr->iCryptOutContext, 
						 IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_IV );
		setMessageData( &msgData, ( void * ) iv, ivSize );
		krnlSendMessage( sessionInfoPtr->iCryptInContext, 
						 IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_IV );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* If we're talking to a cryptlib peer, set up the MAC context which is 
	   used instead of a CRC32.  The key we use for this is taken from the 
	   end of the SSH secret data, which isn't used for any cipher except
	   Blowfish */
	if( sessionInfoPtr->flags & SESSION_ISCRYPTLIB )
		{
		setMessageData( &msgData, 
				handshakeInfo->secretValue + ( SSH1_SECRET_SIZE - 16 ), 16 );
		status = krnlSendMessage( sessionInfoPtr->iAuthInContext, 
								  IMESSAGE_SETATTRIBUTE_S, &msgData, 
								  CRYPT_CTXINFO_KEY );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* We've set up the security info, from now on all data is encrypted */
	sessionInfoPtr->flags |= SESSION_ISSECURE;

	return( CRYPT_OK );
	}

/* Read an SSH packet */

static int decryptPayload( SESSION_INFO *sessionInfoPtr, BYTE *buffer, 
						   const int length )
	{
	int status;

	/* Decrypt the payload, with handling for SSH's Blowfish endianness bug.
	   This may not be a true bug but more a problem in the spec, since the
	   original was rather vague about the endianness of the byte -> long
	   conversion */
	if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
		longReverse( ( unsigned long * ) buffer, length );
	status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
							  IMESSAGE_CTX_DECRYPT, buffer, length );
	if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
		longReverse( ( unsigned long * ) buffer, length );
	return( status );
	}

static BOOLEAN checksumPayload( SESSION_INFO *sessionInfoPtr, 
								const BYTE *buffer, const int length )
	{
	const int dataLength = length - SSH1_CRC_SIZE;	/* CRC isn't part of payload */
	BYTE *bufPtr = ( BYTE * ) buffer + dataLength;
	unsigned long crc32, storedCrc32;

	/* Calculate the checksum over the padding, type, and data and make sure 
	   that it matches the transmitted value */
	if( ( sessionInfoPtr->flags & ( SESSION_ISCRYPTLIB | SESSION_ISSECURE ) ) == \
								  ( SESSION_ISCRYPTLIB | SESSION_ISSECURE ) )
		crc32 = calculateTruncatedMAC( sessionInfoPtr->iAuthInContext,
									   buffer, dataLength );
	else
		crc32 = calculateCRC( buffer, dataLength );
	storedCrc32 = mgetLong( bufPtr );
	return( ( crc32 == storedCrc32 ) ? TRUE : FALSE );
	}

static int getDisconnectInfo( SESSION_INFO *sessionInfoPtr, BYTE *bufPtr )
	{
	int length;

	/* Server is disconnecting, find out why */
	length = mgetLong( bufPtr );
	if( length > MAX_ERRMSG_SIZE - 32 )
		retExt( sessionInfoPtr, CRYPT_ERROR_OVERFLOW,
				"Invalid error information size %d", length );
	strcpy( sessionInfoPtr->errorMessage, "Received SSHv1 server message: " );
	memcpy( sessionInfoPtr->errorMessage + 31, bufPtr, length );
	sessionInfoPtr->errorMessage[ 31 + length ] = '\0';

	return( CRYPT_ERROR_READ );
	}

static int readPacketSSH1( SESSION_INFO *sessionInfoPtr, int expectedType )
	{
	BYTE *bufPtr = sessionInfoPtr->receiveBuffer;
	long length;
	int padLength, packetType;

	/* Alongside the expected packets the server can also send us all sorts 
	   of no-op messages, ranging from explicit no-ops (SSH_MSG_IGNORE) 
	   through to general chattiness (SSH_MSG_DEBUG).  Because we can 
	   receive any quantity of these at any time, we have to run the receive 
	   code in a loop to strip them out */
	do
		{
		const BYTE *lengthPtr = bufPtr;
		int status;

		/* Read the SSHv1 packet header:

			uint32		length (excluding padding)
			byte[]		padding
			byte		type
			byte[]		data
			uint32		crc32

		  The padding length is implicitly calculated as 
		  8 - ( length & 7 ) bytes, and the CRC is calculated over the 
		  padding, type, and data */
		assert( sessionInfoPtr->receiveBufEnd == 0 );
		status = readFixedHeader( sessionInfoPtr, LENGTH_SIZE );
		if( cryptStatusError( status ) )
			return( status );
		assert( status == LENGTH_SIZE );
		length = mgetLong( lengthPtr );
		padLength = 8 - ( length & 7 );
		if( length < SSH1_HEADER_SIZE || \
			length + padLength >= sessionInfoPtr->receiveBufSize )
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Invalid packet length %d", length );
		status = sread( &sessionInfoPtr->stream, 
						sessionInfoPtr->receiveBuffer, padLength + length );
		if( cryptStatusError( status ) )
			{
			sNetGetErrorInfo( &sessionInfoPtr->stream, 
							  sessionInfoPtr->errorMessage,
							  &sessionInfoPtr->errorCode );
			return( status );
			}
		if( status != padLength + length )
			retExt( sessionInfoPtr, CRYPT_ERROR_TIMEOUT,
					"Timeout during packet remainder read, only got %d of "
					"%d bytes", status, padLength + length );
		if( sessionInfoPtr->flags & SESSION_ISSECURE )
			{
			status = decryptPayload( sessionInfoPtr, 
									 sessionInfoPtr->receiveBuffer, 
									 padLength + length );
			if( cryptStatusError( status ) )
				return( status );
			}
		if( !checksumPayload( sessionInfoPtr, sessionInfoPtr->receiveBuffer, 
							  padLength + length ) )
			/* If we're expecting a success packet after a key exchange or an 
			   immediate post key-exchange packet and don't get it then it's 
			   more likely that the problem is due to the wrong key being 
			   used than data corruption, so we return a wrong key error 
			   instead of bad data */
			retExt( sessionInfoPtr, ( expectedType == SSH1_SMSG_SUCCESS ) ? \
						CRYPT_ERROR_WRONGKEY : CRYPT_ERROR_BADDATA,
					"Bad message checksum" );
		packetType = sessionInfoPtr->receiveBuffer[ padLength ];
		}
	while( packetType == SSH1_MSG_IGNORE || packetType == SSH1_MSG_DEBUG );
	length -= ID_SIZE + UINT_SIZE;	/* Remove fixed fields */

	/* Make sure we either got what we asked for or one of the allowed 
	   special-case packets */
	if( packetType == SSH1_MSG_DISCONNECT )
		return( getDisconnectInfo( sessionInfoPtr, 
					sessionInfoPtr->receiveBuffer + padLength + ID_SIZE ) );
	if( expectedType == SSH1_MSG_SPECIAL_USEROPT )
		{
		/* Sending an SSH1_CMSG_USER can result in an SSH1_SMSG_FAILURE if the 
		   user needs some form of authentiction to log on, so we have to 
		   filter this and convert it into a CRYPT_OK/OK_SPECIAL value to 
		   let the caller know whether they have to send a password or not */
		if( packetType == SSH1_SMSG_SUCCESS )
			return( CRYPT_OK );
		if( packetType == SSH1_SMSG_FAILURE )
			return( OK_SPECIAL );
		}
	if( expectedType == SSH1_MSG_SPECIAL_PWOPT )
		{
		/* If we're reading a response to a password then getting a failure
		   response is valid (even if it's not what we're expecting) since
		   it's an indication that an incorrect password was used rather than
		   that there was some general type of failure */
		if( packetType == SSH1_SMSG_FAILURE )
			retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
					"Server indicated incorrect password was used" );
		expectedType = SSH1_SMSG_SUCCESS;
		}
	if( expectedType == SSH1_MSG_SPECIAL_RSAOPT )
		{
		/* If we're reading a response to an RSA key ID then getting a 
		   failure response is valid (even if it's not what we're expecting) 
		   since it's an indication that an incorrect key was used rather 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -