📄 ssh2_cry.c
字号:
const HASHINFO initialHashInfo,
IN_BUFFER( nonceLen ) \
const BYTE *nonce, const int nonceLen,
IN_BUFFER( dataLen ) \
const BYTE *data, const int dataLen )
{
MESSAGE_DATA msgData;
HASHINFO hashInfo;
BYTE buffer[ CRYPT_MAX_KEYSIZE + 8 ];
int status;
assert( isHandleRangeValid( iCryptContext ) );
assert( attribute == CRYPT_CTXINFO_IV || \
attribute == CRYPT_CTXINFO_KEY );
assert( attributeSize >= 8 && attributeSize <= 40 );
assert( hashFunction != NULL );
assert( isReadPtr( initialHashInfo, sizeof( HASHINFO ) ) );
assert( isReadPtr( nonce, nonceLen ) );
assert( isReadPtr( data, dataLen ) );
/* Complete the hashing */
memcpy( hashInfo, initialHashInfo, sizeof( HASHINFO ) );
hashFunction( hashInfo, NULL, 0, nonce, nonceLen, HASH_STATE_CONTINUE );
hashFunction( hashInfo, buffer, CRYPT_MAX_KEYSIZE, data, dataLen,
HASH_STATE_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, CRYPT_MAX_KEYSIZE - 20,
buffer, 20, HASH_STATE_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 );
}
/* Initialise and destroy the security contexts */
int initSecurityContextsSSH( SESSION_INFO *sessionInfoPtr )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
int status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
setMessageCreateObjectInfo( &createInfo, sessionInfoPtr->cryptAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CONTEXT );
if( cryptStatusOK( status ) )
{
sessionInfoPtr->iCryptInContext = createInfo.cryptHandle;
setMessageCreateObjectInfo( &createInfo, sessionInfoPtr->cryptAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT, &createInfo,
OBJECT_TYPE_CONTEXT );
}
if( cryptStatusOK( status ) )
{
sessionInfoPtr->iCryptOutContext = createInfo.cryptHandle;
krnlSendMessage( sessionInfoPtr->iCryptInContext,
IMESSAGE_GETATTRIBUTE, &sessionInfoPtr->cryptBlocksize,
CRYPT_CTXINFO_BLOCKSIZE );
}
#ifdef USE_SSH1
if( cryptStatusOK( status ) && sessionInfoPtr->version == 1 && \
sessionInfoPtr->cryptAlgo == CRYPT_ALGO_IDEA )
{
const CRYPT_MODE_TYPE cryptMode = CRYPT_MODE_CFB;
/* SSHv1 uses stream ciphers in places, for which we have to set the
mode explicitly */
status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
IMESSAGE_SETATTRIBUTE,
( void * ) &cryptMode,
CRYPT_CTXINFO_MODE );
if( cryptStatusOK( status ) )
{
status = krnlSendMessage( sessionInfoPtr->iCryptOutContext,
IMESSAGE_SETATTRIBUTE,
( void * ) &cryptMode,
CRYPT_CTXINFO_MODE );
}
}
if( sessionInfoPtr->version == 2 ) /* Continue on to cSOK() check */
#endif /* USE_SSH1 */
if( cryptStatusOK( status ) )
{
setMessageCreateObjectInfo( &createInfo, sessionInfoPtr->integrityAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT, &createInfo,
OBJECT_TYPE_CONTEXT );
if( cryptStatusOK( status ) )
{
sessionInfoPtr->iAuthInContext = createInfo.cryptHandle;
setMessageCreateObjectInfo( &createInfo,
sessionInfoPtr->integrityAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT, &createInfo,
OBJECT_TYPE_CONTEXT );
}
if( cryptStatusOK( status ) )
{
sessionInfoPtr->iAuthOutContext = createInfo.cryptHandle;
krnlSendMessage( sessionInfoPtr->iAuthInContext,
IMESSAGE_GETATTRIBUTE,
&sessionInfoPtr->authBlocksize,
CRYPT_CTXINFO_BLOCKSIZE );
}
}
if( cryptStatusError( status ) )
{
/* One or more of the contexts couldn't be created, destroy all the
contexts that have been created so far */
destroySecurityContextsSSH( sessionInfoPtr );
}
return( status );
}
void destroySecurityContextsSSH( SESSION_INFO *sessionInfoPtr )
{
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
/* Destroy any active contexts */
if( sessionInfoPtr->iKeyexCryptContext != CRYPT_ERROR )
{
krnlSendNotifier( sessionInfoPtr->iKeyexCryptContext,
IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iKeyexCryptContext = CRYPT_ERROR;
}
if( sessionInfoPtr->iCryptInContext != CRYPT_ERROR )
{
krnlSendNotifier( sessionInfoPtr->iCryptInContext,
IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iCryptInContext = CRYPT_ERROR;
}
if( sessionInfoPtr->iCryptOutContext != CRYPT_ERROR )
{
krnlSendNotifier( sessionInfoPtr->iCryptOutContext,
IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iCryptOutContext = CRYPT_ERROR;
}
if( sessionInfoPtr->iAuthInContext != CRYPT_ERROR )
{
krnlSendNotifier( sessionInfoPtr->iAuthInContext,
IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iAuthInContext = CRYPT_ERROR;
}
if( sessionInfoPtr->iAuthOutContext != CRYPT_ERROR )
{
krnlSendNotifier( sessionInfoPtr->iAuthOutContext,
IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iAuthOutContext = CRYPT_ERROR;
}
}
/* 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 = isServer( sessionInfoPtr ) ? FALSE : TRUE;
int keySize, ivSize, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
/* Create the security contexts required for the session */
status = initSecurityContextsSSH( 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
{
status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
IMESSAGE_GETATTRIBUTE, &keySize,
CRYPT_CTXINFO_KEYSIZE );
if( cryptStatusError( status ) )
return( status );
}
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 re-used 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_SHA1, &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, 0, handshakeInfo->sessionID,
handshakeInfo->sessionIDlength, HASH_STATE_START );
}
else
{
STREAM stream;
BYTE header[ 8 + 8 ];
const int mpiLength = \
sizeofInteger32( handshakeInfo->secretValue,
handshakeInfo->secretValueLength ) - \
LENGTH_SIZE;
int headerLength = DUMMY_INIT;
/* Hash the shared secret as an MPI. We can't use hashAsMPI() for
this because it works with contexts rather than the internal hash
functions used here */
sMemOpen( &stream, header, 8 );
status = writeUint32( &stream, mpiLength );
if( handshakeInfo->secretValue[ 0 ] & 0x80 )
{
/* MPIs are signed values */
status = sputc( &stream, 0 );
}
if( cryptStatusOK( status ) )
headerLength = stell( &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
hashFunction( initialHashInfo, NULL, 0, header, headerLength,
HASH_STATE_START );
hashFunction( initialHashInfo, NULL, 0, handshakeInfo->secretValue,
handshakeInfo->secretValueLength, HASH_STATE_CONTINUE );
hashFunction( initialHashInfo, NULL, 0, handshakeInfo->sessionID,
handshakeInfo->sessionIDlength, HASH_STATE_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 use
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", 1,
handshakeInfo->sessionID,
handshakeInfo->sessionIDlength );
if( cryptStatusOK( status ) )
status = loadCryptovariable( isClient ? \
sessionInfoPtr->iCryptInContext : \
sessionInfoPtr->iCryptOutContext,
CRYPT_CTXINFO_IV, ivSize,
hashFunction, initialHashInfo, "B", 1,
handshakeInfo->sessionID,
handshakeInfo->sessionIDlength );
}
if( cryptStatusOK( status ) )
status = loadCryptovariable( isClient ? \
sessionInfoPtr->iCryptOutContext : \
sessionInfoPtr->iCryptInContext,
CRYPT_CTXINFO_KEY, keySize,
hashFunction, initialHashInfo, "C", 1,
handshakeInfo->sessionID,
handshakeInfo->sessionIDlength );
if( cryptStatusOK( status ) )
status = loadCryptovariable( isClient ? \
sessionInfoPtr->iCryptInContext : \
sessionInfoPtr->iCryptOutContext,
CRYPT_CTXINFO_KEY, keySize,
hashFunction, initialHashInfo, "D", 1,
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", 1,
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", 1,
handshakeInfo->sessionID,
handshakeInfo->sessionIDlength );
return( status );
}
/****************************************************************************
* *
* Hash/MAC Data *
* *
****************************************************************************/
/* Hash a value encoded as an SSH string and as an MPI */
int hashAsString( const CRYPT_CONTEXT iHashContext,
const BYTE *data, const int dataLength )
{
STREAM stream;
BYTE buffer[ 128 + 8 ];
int length = DUMMY_INIT, status;
assert( isHandleRangeValid( iHashContext ) );
assert( isReadPtr( data, dataLength ) );
/* 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 */
sMemOpen( &stream, buffer, 128 );
status = writeUint32( &stream, dataLength );
if( cryptStatusOK( status ) && dataLength <= sMemDataLeft( &stream ) )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -