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

📄 ssh.c

📁 提供了很多种加密算法和CA认证及相关服务如CMP、OCSP等的开发
💻 C
📖 第 1 页 / 共 5 页
字号:
		{
		krnlSendMessage( iHashContext, RESOURCE_IMESSAGE_CTX_HASH, 
						 buffer, LENGTH_SIZE );
		status = krnlSendMessage( iHashContext, RESOURCE_IMESSAGE_CTX_HASH, 
								  ( void * ) data, dataLength );
		}
	zeroise( buffer, 64 );

	return( status );
	}

static int hashAsMPI( const CRYPT_CONTEXT iHashContext,
					  const BYTE *data, const int dataLength )
	{
	BYTE buffer[ 8 ], *bufPtr = buffer;
	const int length = ( data[ 0 ] & 0x80 ) ? dataLength + 1 : dataLength;
	int headerLength, status;

	/* Prepend the MPI length to the data and hash it.  Since this is 
	   probably sensitive data, we don't take a local copy but hash it
	   in two parts */
	mputBLong( bufPtr, length );
	if( data[ 0 ] & 0x80 )
		*bufPtr++ = 0;
	headerLength = ( int ) ( bufPtr - buffer );
	krnlSendMessage( iHashContext, RESOURCE_IMESSAGE_CTX_HASH, 
					 buffer, headerLength );
	status = krnlSendMessage( iHashContext, RESOURCE_IMESSAGE_CTX_HASH, 
							  ( void * ) data, dataLength );

	return( status );
	}

/* Complete the hashing necessary to generate a cryptovariable and send it 
   to a context */

static int loadCryptovariable( const CRYPT_CONTEXT iCryptContext,
							   const CRYPT_ATTRIBUTE_TYPE attribute,
							   const int attributeSize, HASHFUNCTION hashFunction, 
							   void *initialHashInfo, const BYTE *nonce, 
							   const BYTE *data, const int dataLen )
	{
	RESOURCE_DATA msgData;
	BYTE hashInfo[ MAX_HASHINFO_SIZE ], buffer[ CRYPT_MAX_KEYSIZE ];
	int status;

	/* Complete the hashing */
	memcpy( hashInfo, initialHashInfo, MAX_HASHINFO_SIZE );
	if( nonce != NULL )
		hashFunction( hashInfo, NULL, nonce, 1, HASH_CONTINUE );
	hashFunction( hashInfo, buffer, data, dataLen, HASH_END );
	zeroise( hashInfo, MAX_HASHINFO_SIZE );

	/* If we need more data than the hashing will provide in one go,
	   generate a second block as:
		hash( shared_secret || exchange_hash || data )
	   where the shared secret and exchange hash are present as the
	   precomputed data and the data is the output of the initial 
	   hashing */
	if( attributeSize > 20 )
		{
		memcpy( hashInfo, initialHashInfo, MAX_HASHINFO_SIZE );
		hashFunction( hashInfo, buffer + 20, buffer, 20, HASH_END );
		}

	/* Send the data to the context */
	setResourceData( &msgData, buffer, attributeSize );
	status = krnlSendMessage( iCryptContext, 
							  RESOURCE_IMESSAGE_SETATTRIBUTE_S, 
							  &msgData, attribute );
	zeroise( buffer, CRYPT_MAX_KEYSIZE );

	return( status );
	}

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

static int initSecurityInfo2( SESSION_INFO *sessionInfoPtr,
							  SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	HASHFUNCTION hashFunction;
	BYTE initialHashInfo[ MAX_HASHINFO_SIZE ];
	BYTE header[ 8 ], *headerPtr = header;
	const int mpiLength = ( handshakeInfo->secretValue[ 0 ] & 0x80 ) ? \
							handshakeInfo->secretValueLength + 1 : \
							handshakeInfo->secretValueLength;
	int hashSize, keySize, ivSize, status;

	/* Get the hash algorithm information and pre-hash the shared secret and
	   exchange hash, which are reused for all cryptovariables.  The overall
	   hashing is:
		hash( MPI( shared_secret ) || exchange_hash || \
			  nonce || exchange_hash )
	   Before we can hash the shared secret we have to convert it into MPI 
	   form, which we do by generating a pseudo-header and hashing that
	   separately.  The nonce is "A", "B", "C", ... */
	getHashParameters( CRYPT_ALGO_SHA, &hashFunction, &hashSize );
	mputBLong( headerPtr, mpiLength );
	if( handshakeInfo->secretValue[ 0 ] & 0x80 )
		*headerPtr++ = 0;
	hashFunction( initialHashInfo, NULL, header, headerPtr - header, 
				  HASH_START );
	hashFunction( initialHashInfo, NULL, handshakeInfo->secretValue, 
				  handshakeInfo->secretValueLength, HASH_CONTINUE );
	hashFunction( initialHashInfo, NULL, handshakeInfo->sessionID, 
				  handshakeInfo->sessionIDlength, HASH_CONTINUE );

	/* Create the security contexts required for the session */
	status = initSecurityContexts( sessionInfoPtr );
	if( cryptStatusError( status ) )
		return( status );
	if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
		/* Blowfish has a variable-length key so we have to explicitly 
		   specify its length */
		keySize = SSH2_FIXED_KEY_SIZE;
	else
		krnlSendMessage( sessionInfoPtr->iCryptInContext,
						 RESOURCE_IMESSAGE_GETATTRIBUTE, &keySize,
						 CRYPT_CTXINFO_KEYSIZE );
	if( krnlSendMessage( sessionInfoPtr->iCryptInContext,
						 RESOURCE_IMESSAGE_GETATTRIBUTE, &ivSize,
						 CRYPT_CTXINFO_IVSIZE ) == CRYPT_ERROR_NOTAVAIL )
		/* It's a stream cipher */
		ivSize = 0;

	/* Load the cryptovariables.  The order is:
		client_write_iv, server_write_iv
		client_write_key, server_write_key
		client_write_mac, server_write_mac
	   Although HMAC has a variable-length key and should therefore follow
	   the SSH2_FIXED_KEY_SIZE rule, the key size is in fact set to the
	   HMAC block size (the ssh.com SSHv2 software up to to version 
	   v2.3.x in fact used the fixed-size key, but later versions (and
	   everything else) used the HMAC block size for the key size) */
	status = loadCryptovariable( sessionInfoPtr->iCryptOutContext, 
								 CRYPT_CTXINFO_IV, ivSize,
								 hashFunction, initialHashInfo, "A",
								 handshakeInfo->sessionID, 
								 handshakeInfo->sessionIDlength );
	if( cryptStatusOK( status ) )
		status = loadCryptovariable( sessionInfoPtr->iCryptInContext, 
									 CRYPT_CTXINFO_IV, ivSize,
									 hashFunction, initialHashInfo, "B",
									 handshakeInfo->sessionID, 
									 handshakeInfo->sessionIDlength );
	if( cryptStatusOK( status ) )
		status = loadCryptovariable( sessionInfoPtr->iCryptOutContext, 
									 CRYPT_CTXINFO_KEY, keySize,
									 hashFunction, initialHashInfo, "C",
									 handshakeInfo->sessionID, 
									 handshakeInfo->sessionIDlength );
	if( cryptStatusOK( status ) )
		status = loadCryptovariable( sessionInfoPtr->iCryptInContext, 
									 CRYPT_CTXINFO_KEY, keySize,
									 hashFunction, initialHashInfo, "D",
									 handshakeInfo->sessionID, 
									 handshakeInfo->sessionIDlength );
	if( cryptStatusOK( status ) )
		status = loadCryptovariable( sessionInfoPtr->iAuthOutContext, 
									 CRYPT_CTXINFO_KEY, 
									 sessionInfoPtr->authBlocksize,
									 hashFunction, initialHashInfo, "E",
									 handshakeInfo->sessionID, 
									 handshakeInfo->sessionIDlength );
	if( cryptStatusOK( status ) )
		status = loadCryptovariable( sessionInfoPtr->iAuthInContext, 
									 CRYPT_CTXINFO_KEY,
									 sessionInfoPtr->authBlocksize,
									 hashFunction, initialHashInfo, "F",
									 handshakeInfo->sessionID, 
									 handshakeInfo->sessionIDlength );
	return( status );
	}

/* Read an SSHv2 packet */

typedef enum { MAC_START, MAC_END, MAC_ALL } MAC_TYPE;

static BOOLEAN macPayload( const CRYPT_CONTEXT iMacContext,
						   const long seqNo, const BYTE *data, 
						   const int dataLength, const int packetDataLength, 
						   const MAC_TYPE macType )
	{
	int status;

	/* MAC the data and compare the result to the stored MAC:
		HMAC( seqNo || payload )
	   During the hansdshake process we have the entire packet at hand and
	   can process it at once, when we're processing payload data we have
	   to process the header separately in order to determine how much more
	   we have to read, so we have to MAC the packet in two parts */
	if( macType == MAC_START || macType == MAC_ALL )
		{
		BYTE buffer[ 16 ], *bufPtr = buffer;
		int length = ( macType == MAC_ALL ) ? dataLength : packetDataLength;

		assert( ( macType == MAC_ALL && packetDataLength == 0 ) || \
				( macType == MAC_START && packetDataLength > dataLength ) );

		/* Since the payload had the length stripped during the speculative 
		   read, we have to reconstruct it and hash it separately before we 
		   hash the data.  If we're doing the hash in parts, the amount of
		   data being hashed won't match the overall length so the caller
		   needs to supply the overall packet as well as current data 
		   lengths */
		mputBLong( bufPtr, seqNo );
		mputBLong( bufPtr, length );
		krnlSendMessage( iMacContext, RESOURCE_IMESSAGE_DELETEATTRIBUTE, NULL, 
						 CRYPT_CTXINFO_HASHVALUE );
		krnlSendMessage( iMacContext, RESOURCE_IMESSAGE_CTX_HASH, 
						 buffer, LENGTH_SIZE + LENGTH_SIZE );
		}
	krnlSendMessage( iMacContext, RESOURCE_IMESSAGE_CTX_HASH, 
					 ( void * ) data, dataLength );
	if( macType == MAC_END || macType == MAC_ALL )
		{
		RESOURCE_DATA msgData;
		BYTE macBuffer[ CRYPT_MAX_HASHSIZE ];

		krnlSendMessage( iMacContext, RESOURCE_IMESSAGE_CTX_HASH, "", 0 );
		setResourceData( &msgData, macBuffer, CRYPT_MAX_HASHSIZE );
		status = krnlSendMessage( iMacContext, RESOURCE_IMESSAGE_GETATTRIBUTE_S, 
								  &msgData, CRYPT_CTXINFO_HASHVALUE );
		if( cryptStatusError( status ) || \
			memcmp( macBuffer, data + dataLength, msgData.length ) )
			return( FALSE );
		}

	return( TRUE );
	}

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

	/* Server is disconnecting, find out why */
	bufPtr++;				/* Skip packet type */
	mgetBLong( bufPtr );	/* Reason code */
	length = mgetBLong( bufPtr );
	if( length > MAX_ERRMSG_SIZE - 30 )
		return( CRYPT_ERROR_OVERFLOW );
	strcpy( sessionInfoPtr->errorMessage, "Received SSH server message: " );
	memcpy( sessionInfoPtr->errorMessage + 29, bufPtr, length );
	sessionInfoPtr->errorMessage[ 29 + length ] = '\0';

	return( CRYPT_ERROR_READ );
	}

static int readPacket2( SESSION_INFO *sessionInfoPtr, int expectedType )
	{
	BYTE *dataStartPtr;
	long length;
	int padLength = 0, packetType;

	/* Alongside the expected packets the server can send us all sorts of nop
	   messages, ranging from explicit nops (SSH2_MSG_IGNORE) through to
	   general chattiness (SSH2_MSG_DEBUG, SSH2_MSG_USERAUTH_BANNER).  
	   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
		{
		BYTE *bufPtr = sessionInfoPtr->receiveBuffer;
		int status;

		/* Read the SSHv2 packet header:
			uint32		length
			byte		padLen
		  [	byte		type - checked but not removed ]
			byte[]		data
			byte[]		padding
			byte[]		MAC

		  SSHv2 encrypts everything (including the length) so we need to
		  speculatively read ahead for the minimum packet size and decrypt
		  that in order to figure out what to do */
		status = sread( &sessionInfoPtr->stream, 
						sessionInfoPtr->receiveBuffer, MIN_PACKET_SIZE );
		if( cryptStatusError( status ) )
			{
			sNetGetErrorInfo( &sessionInfoPtr->stream, 
							  sessionInfoPtr->errorMessage,
							  &sessionInfoPtr->errorCode );
			return( status );
			}
		if( sessionInfoPtr->flags & SESSION_ISSECURE )
			{
			status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
									  RESOURCE_IMESSAGE_CTX_DECRYPT, 
									  sessionInfoPtr->receiveBuffer, 
									  MIN_PACKET_SIZE );
			if( cryptStatusError( status ) )
				return( status );
			}
		length = mgetBLong( bufPtr );
		if( sessionInfoPtr->flags & SESSION_ISSECURE )
			/* The MAC size isn't included in the packet length */
			length += sessionInfoPtr->authBlocksize;
		if( length < MIN_PACKET_SIZE - LENGTH_SIZE || \
			length > sessionInfoPtr->receiveBufSize - ( MIN_PACKET_SIZE + 8 ) )
			return( CRYPT_ERROR_BADDATA );
		memmove( sessionInfoPtr->receiveBuffer, 
				 sessionInfoPtr->receiveBuffer + LENGTH_SIZE,
				 PACKET_REMAINDER_SIZE );
		if( length > PACKET_REMAINDER_SIZE )	/* Change cipherspec = 0-len */
			status = \
				sread( &sessionInfoPtr->stream, 
					   sessionInfoPtr->receiveBuffer + PACKET_REMAINDER_SIZE, 
					   length - PACKET_REMAINDER_SIZE );
		if( cryptStatusError( status ) )
			{
			sNetGetErrorInfo( &sessionInfoPtr->stream, 
							  sessionInfoPtr->errorMessage,
							  &sessionInfoPtr->errorCode );
			return( status );
			}
		if( sessionInfoPtr->flags & SESSION_ISSECURE )
			{
			/* Decrypt the remainder of the packet except for the MAC */
			status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
						RESOURCE_IMESSAGE_CTX_DECRYPT, 
						sessionInfoPtr->receiveBuffer + PACKET_REMAINDER_SIZE, 
						length - ( PACKET_REMAINDER_SIZE + \
								   sessionInfoPtr->authBlocksize ) );
			if( cryptStatusError( status ) )
				return( status );

			/* MAC the decrypted payload */
			if( !macPayload( sessionInfoPtr->iAuthInContext, 
					sessionInfoPtr->readSeqNo, sessionInfoPtr->receiveBuffer,
					length - sessionInfoPtr->authBlocksize, 0, MAC_ALL ) )
				/* If we're expecting a service accept packet after a change 
				   cipherspec 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 */
				return( ( expectedType == SSH2_MSG_SERVICE_ACCEPT ) ? 
						CRYPT_ERROR_WRONGKEY : CRYPT_ERROR

⌨️ 快捷键说明

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