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

📄 ssh2.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 5 页
字号:
		}
	return( LENGTH_SIZE + length );
	}

/* Encode/decode a value as an SSHv2 MPI.  The decoded value is always the
   DH keyex MPI, so we can perform some special-case checks on it */

int encodeMPI( BYTE *buffer, const BYTE *value,
			   const int valueLength )
	{
	BYTE *bufPtr = buffer;
	const int mpiValueLength = valueLength + \
							   ( ( value[ 0 ] & 0x80 ) ? 1 : 0 );

	if( buffer != NULL )
		{
		mputLong( bufPtr, mpiValueLength );
		if( value[ 0 ] & 0x80 )
			*bufPtr++ = 0;	/* MPIs are signed values */
		memcpy( bufPtr, value, valueLength );
		}
	return( LENGTH_SIZE + mpiValueLength );
	}

static int readKeyexMPI( SESSION_INFO *sessionInfoPtr, BYTE *value,
						 const BYTE *bufPtr, const int nominalLength )
	{
	int length;

	if( bufPtr[ 0 ] || bufPtr[ 1 ] || \
		( bufPtr[ 2 ] & ~( ( CRYPT_MAX_PKCSIZE << 1 ) - 1 ) ) )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid MPI length header 0x%02X 0x%02X 0x%02X 0x%02X", 
				bufPtr[ 0 ], bufPtr[ 1 ], bufPtr[ 2 ], bufPtr[ 3 ] );
	length = ( ( int ) bufPtr[ 2 ] << 8 ) | bufPtr[ 3 ];
	if( length < nominalLength - 8 || length > nominalLength + 1 )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid MPI length %d, nominal length is %d", length,
				nominalLength );
	bufPtr += LENGTH_SIZE;
	while( !*bufPtr && length > 1 )
		{
		/* Strip leading zero padding */
		bufPtr++;
		length--;
		}
	if( length < nominalLength - 8 || length > nominalLength )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid normalised MPI length %d, nominal length is %d",
				length, nominalLength );
	memcpy( value, bufPtr, length );
	return( length );
	}

/* Hash a value encoded as an SSH string and as an MPI */

int hashAsString( const CRYPT_CONTEXT iHashContext,
				  const BYTE *data, const int dataLength )
	{
	BYTE buffer[ 128 ], *bufPtr = buffer;
	int status;

	/* Prepend the string length to the data and hash it.  If it'll fit into
	   the buffer we copy it over to save a kernel call */
	mputLong( bufPtr, dataLength );
	if( dataLength <= 128 - LENGTH_SIZE )
		{
		memcpy( buffer + LENGTH_SIZE, data, dataLength );
		status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, buffer,
								  LENGTH_SIZE + dataLength );
		}
	else
		{
		krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, buffer,
						 LENGTH_SIZE );
		status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
								  ( void * ) data, dataLength );
		}
	zeroise( buffer, 128 );

	return( status );
	}

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 = LENGTH_SIZE;

	/* Prepend the MPI length to the data and hash it.  Since this is often
	   sensitive data, we don't take a local copy but hash it in two parts */
	mputLong( bufPtr, length );
	if( data[ 0 ] & 0x80 )
		{
		/* MPIs are signed values */
		*bufPtr++ = 0;
		headerLength++;
		}
	krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, buffer, headerLength );
	return( krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
							  ( void * ) data, dataLength ) );
	}

/* 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,
							   const HASHINFO initialHashInfo, const BYTE *nonce,
							   const BYTE *data, const int dataLen )
	{
	RESOURCE_DATA msgData;
	HASHINFO hashInfo;
	BYTE buffer[ CRYPT_MAX_KEYSIZE ];
	int status;

	/* Complete the hashing */
	memcpy( hashInfo, initialHashInfo, sizeof( HASHINFO ) );
	if( nonce != NULL )
		hashFunction( hashInfo, NULL, nonce, 1, HASH_CONTINUE );
	hashFunction( hashInfo, buffer, data, dataLen, HASH_END );
	if( attributeSize > 20 )
		{
		/* 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 in the initial hash info and the data part is
		   the output of the hash step above */
		memcpy( hashInfo, initialHashInfo, sizeof( HASHINFO ) );
		hashFunction( hashInfo, buffer + 20, buffer, 20, HASH_END );
		}
	zeroise( hashInfo, sizeof( HASHINFO ) );

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

	return( status );
	}

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

int initSecurityInfo( SESSION_INFO *sessionInfoPtr,
					  SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	HASHFUNCTION hashFunction;
	HASHINFO initialHashInfo;
	const BOOLEAN isClient = \
				( sessionInfoPtr->flags & SESSION_ISSERVER ) ? FALSE : TRUE;
	const int mpiLength = handshakeInfo->secretValueLength + \
				( ( handshakeInfo->secretValue[ 0 ] & 0x80 ) ? 1 : 0 );
	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 )
		/* Blowfish has a variable-length key so we have to explicitly
		   specify its length */
		keySize = SSH2_FIXED_KEY_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;

	/* 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 )

	   Note the apparently redundant double hashing of the exchange hash, 
	   this is required because the spec refers to it by two different names,
	   the exchange hash and the session ID, and then requires that both be
	   hashed (actually it's a bit more complex than that, with issues 
	   related to re-keying, but for now it acts as a re-hash of the same
	   data).

	   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, NULL );
	if( ( sessionInfoPtr->protocolFlags & SSH_PFLAG_NOHASHSECRET ) )
		{
		/* Some implementations erroneously omit the shared secret when
		   creating the keying material.  This is suboptimal but not fatal,
		   since the shared secret is also hashed into the exchange hash */
		hashFunction( initialHashInfo, NULL, handshakeInfo->sessionID,
					  handshakeInfo->sessionIDlength, HASH_START );
		}
	else
		{
		BYTE header[ 8 ], *headerPtr = header;

		mputLong( 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 );
		}

	/* 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 was in later RFC drafts
	   set to the HMAC block size.  Some implementations erroneously used 
	   the fixed-size key, so we adjust the HMAC key size if we're talking
	   to one of these */
	if( !isStreamCipher( sessionInfoPtr->cryptAlgo ) )
		{
		status = loadCryptovariable( isClient ? \
										sessionInfoPtr->iCryptOutContext : \
										sessionInfoPtr->iCryptInContext,
									 CRYPT_CTXINFO_IV, ivSize,
									 hashFunction, initialHashInfo, "A",
									 handshakeInfo->sessionID,
									 handshakeInfo->sessionIDlength );
		if( cryptStatusOK( status ) )
			status = loadCryptovariable( isClient ? \
											sessionInfoPtr->iCryptInContext : \
											sessionInfoPtr->iCryptOutContext,
										 CRYPT_CTXINFO_IV, ivSize,
										 hashFunction, initialHashInfo, "B",
										 handshakeInfo->sessionID,
										 handshakeInfo->sessionIDlength );
		}
	if( cryptStatusOK( status ) )
		status = loadCryptovariable( isClient ? \
										sessionInfoPtr->iCryptOutContext : \
										sessionInfoPtr->iCryptInContext,
									 CRYPT_CTXINFO_KEY, keySize,
									 hashFunction, initialHashInfo, "C",
									 handshakeInfo->sessionID,
									 handshakeInfo->sessionIDlength );
	if( cryptStatusOK( status ) )
		status = loadCryptovariable( isClient ? \
										sessionInfoPtr->iCryptInContext : \
										sessionInfoPtr->iCryptOutContext,
									 CRYPT_CTXINFO_KEY, keySize,
									 hashFunction, initialHashInfo, "D",
									 handshakeInfo->sessionID,
									 handshakeInfo->sessionIDlength );
	if( cryptStatusOK( status ) )
		status = loadCryptovariable( isClient ? \
										sessionInfoPtr->iAuthOutContext : \
										sessionInfoPtr->iAuthInContext,
									 CRYPT_CTXINFO_KEY,
									 ( sessionInfoPtr->protocolFlags & \
									   SSH_PFLAG_HMACKEYSIZE ) ? \
										SSH2_FIXED_KEY_SIZE : \
										sessionInfoPtr->authBlocksize,
									 hashFunction, initialHashInfo, "E",
									 handshakeInfo->sessionID,
									 handshakeInfo->sessionIDlength );
	if( cryptStatusOK( status ) )
		status = loadCryptovariable( isClient ? \
										sessionInfoPtr->iAuthInContext : \
										sessionInfoPtr->iAuthOutContext,
									 CRYPT_CTXINFO_KEY,
									 ( sessionInfoPtr->protocolFlags & \
									   SSH_PFLAG_HMACKEYSIZE ) ? \
										SSH2_FIXED_KEY_SIZE : \
										sessionInfoPtr->authBlocksize,
									 hashFunction, initialHashInfo, "F",
									 handshakeInfo->sessionID,
									 handshakeInfo->sessionIDlength );
	return( status );
	}

/* MAC the payload of a data packet.  Since we may not have the whole packet
   available at once, we can do this in one go or incrementally */

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 || length || payload )

	   During the handshake process we have the entire packet at hand 
	   (dataLength == packetDataLength) and can process it at once.  When 
	   we're processing payload data (dataLength a subset of 
	   packetDataLength) 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 length, as well as the current 
		   data length */
		mputLong( bufPtr, seqNo );
		mputLong( bufPtr, length );
		krnlSendMessage( iMacContext, IMESSAGE_DELETEATTRIBUTE, NULL,
						 CRYPT_CTXINFO_HASHVALUE );
		krnlSendMessage( iMacContext, IMESSAGE_CTX_HASH, buffer,
						 LENGTH_SIZE + LENGTH_SIZE );
		}
	krnlSendMessage( iMacContext, IMESSAGE_CTX_HASH, ( void * ) data,

⌨️ 快捷键说明

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