📄 ssh2_cry.c
字号:
{
/* The data will fit into the buffer, copy it so that it can be
hashed in one go */
status = swrite( &stream, data, dataLength );
}
if( cryptStatusOK( status ) )
{
length = stell( &stream );
status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
buffer, length );
}
if( cryptStatusOK( status ) && length < dataLength )
{
/* The data didn't fit into the buffer, hash it separately */
status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
( void * ) data, dataLength );
}
sMemClose( &stream );
return( status );
}
int hashAsMPI( const CRYPT_CONTEXT iHashContext, const BYTE *data,
const int dataLength )
{
STREAM stream;
BYTE buffer[ 8 + 8 ];
const int length = ( data[ 0 ] & 0x80 ) ? dataLength + 1 : dataLength;
int headerLength = DUMMY_INIT, status;
assert( isHandleRangeValid( iHashContext ) );
assert( isReadPtr( data, dataLength ) );
/* 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 */
sMemOpen( &stream, buffer, 8 );
status = writeUint32( &stream, length );
if( data[ 0 ] & 0x80 )
{
/* MPIs are signed values */
status = sputc( &stream, 0 );
}
if( cryptStatusOK( status ) )
headerLength = stell( &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
buffer, headerLength );
if( cryptStatusOK( status ) )
status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
( void * ) data, dataLength );
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 */
static int macDataSSH( const CRYPT_CONTEXT iMacContext, const long seqNo,
IN_BUFFER( dataLength ) \
const BYTE *data, const int dataLength,
const int packetDataLength, const MAC_TYPE macType )
{
int status;
assert( isHandleRangeValid( iMacContext ) );
assert( macType == MAC_END || seqNo >= 2 );
/* Since SSH starts counting packets from the first one but
unlike SSL doesn't MAC them, the seqNo is already nonzero
when we start */
assert( dataLength == 0 || \
isReadPtr( data, dataLength ) );
assert( packetDataLength >= 0 && packetDataLength < MAX_INTLENGTH );
assert( macType > MAC_NONE && macType < MAC_LAST );
/* MAC the data and either compare the result to the stored MAC or
append the MAC value to the data:
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 )
{
STREAM stream;
BYTE headerBuffer[ 16 + 8 ];
int length = ( macType == MAC_ALL ) ? dataLength : packetDataLength;
int headerLength = DUMMY_INIT;
assert( ( macType == MAC_ALL && packetDataLength == 0 ) || \
( macType == MAC_START && packetDataLength >= dataLength ) );
/* Since the payload had the length stripped during the speculative
read if we're MAC'ing read data, 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 */
sMemOpen( &stream, headerBuffer, 16 );
writeUint32( &stream, seqNo );
status = writeUint32( &stream, length );
if( cryptStatusOK( status ) )
headerLength = stell( &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
krnlSendMessage( iMacContext, IMESSAGE_DELETEATTRIBUTE, NULL,
CRYPT_CTXINFO_HASHVALUE );
status = krnlSendMessage( iMacContext, IMESSAGE_CTX_HASH,
headerBuffer, headerLength );
if( cryptStatusError( status ) )
return( status );
}
if( dataLength > 0 )
{
status = krnlSendMessage( iMacContext, IMESSAGE_CTX_HASH,
( void * ) data, dataLength );
if( cryptStatusError( status ) )
return( status );
}
if( macType == MAC_END || macType == MAC_ALL )
{
status = krnlSendMessage( iMacContext, IMESSAGE_CTX_HASH, "", 0 );
if( cryptStatusError( status ) )
return( status );
}
return( CRYPT_OK );
}
int checkMacSSH( const CRYPT_CONTEXT iMacContext, const long seqNo,
const BYTE *data, const int dataMaxLength,
const int dataLength, const int packetDataLength,
const MAC_TYPE macType, const int macLength )
{
MESSAGE_DATA msgData;
int status;
assert( isHandleRangeValid( iMacContext ) );
assert( macType == MAC_END || seqNo >= 2 );
assert( isReadPtr( data, dataMaxLength ) );
assert( ( macType == MAC_START && \
dataMaxLength == dataLength ) || \
( ( macType == MAC_ALL || macType == MAC_END ) &&
dataLength + macLength <= dataMaxLength ) );
assert( packetDataLength >= 0 && packetDataLength < MAX_INTLENGTH );
assert( macType > MAC_NONE && macType < MAC_LAST );
assert( macLength >= 16 && macLength <= CRYPT_MAX_HASHSIZE );
/* Sanity-check the state */
if( dataLength + \
( ( macType == MAC_START ) ? 0 : macLength ) > dataMaxLength )
retIntError();
/* MAC the payload */
status = macDataSSH( iMacContext, seqNo, data, dataLength,
packetDataLength, macType );
if( cryptStatusError( status ) )
return( status );
/* If we're starting the ongoing hashing of a data block, we're done */
if( macType == MAC_START )
return( CRYPT_OK );
/* Compare the calculated MAC value to the stored MAC value */
setMessageData( &msgData, ( BYTE * ) data + dataLength, macLength );
return( krnlSendMessage( iMacContext, IMESSAGE_COMPARE, &msgData,
MESSAGE_COMPARE_HASH ) );
}
int createMacSSH( const CRYPT_CONTEXT iMacContext, const long seqNo,
BYTE *data, const int dataMaxLength, const int dataLength )
{
MESSAGE_DATA msgData;
BYTE mac[ CRYPT_MAX_HASHSIZE + 8 ];
int status;
assert( isHandleRangeValid( iMacContext ) );
assert( seqNo >= 2 );
assert( isWritePtr( data, dataLength ) );
/* MAC the payload */
status = macDataSSH( iMacContext, seqNo, data, dataLength, 0, MAC_ALL );
if( cryptStatusError( status ) )
return( status );
/* Write the calculated MAC value to the stream */
setMessageData( &msgData, mac, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( iMacContext, IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CTXINFO_HASHVALUE );
if( cryptStatusError( status ) )
return( status );
assert( dataLength + msgData.length <= dataMaxLength );
memcpy( data + dataLength, mac, msgData.length );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Miscellaneous Functions *
* *
****************************************************************************/
/* Complete the DH key agreement */
int completeKeyex( SESSION_INFO *sessionInfoPtr,
SSH_HANDSHAKE_INFO *handshakeInfo,
const BOOLEAN isServer )
{
KEYAGREE_PARAMS keyAgreeParams;
MESSAGE_DATA msgData;
STREAM stream;
int status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
/* Read the other side's key agreement information. Note that the size
check has already been performed at a higher level when the overall
key agreement value was read, this is a secondary check of the MPI
payload */
memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) );
if( isServer )
sMemConnect( &stream, handshakeInfo->clientKeyexValue,
handshakeInfo->clientKeyexValueLength );
else
sMemConnect( &stream, handshakeInfo->serverKeyexValue,
handshakeInfo->serverKeyexValueLength );
status = readInteger32( &stream, keyAgreeParams.publicValue,
&keyAgreeParams.publicValueLen, MIN_PKCSIZE,
CRYPT_MAX_PKCSIZE );
sMemDisconnect( &stream );
if( cryptStatusOK( status ) && \
!isValidDHsize( keyAgreeParams.publicValueLen,
handshakeInfo->serverKeySize, 0 ) )
status = CRYPT_ERROR_BADDATA;
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid DH phase 1 MPI" ) );
}
/* Perform phase 2 of the DH key agreement */
status = krnlSendMessage( handshakeInfo->iServerCryptContext,
IMESSAGE_CTX_DECRYPT, &keyAgreeParams,
sizeof( KEYAGREE_PARAMS ) );
if( cryptStatusOK( status ) )
{
memcpy( handshakeInfo->secretValue, keyAgreeParams.wrappedKey,
keyAgreeParams.wrappedKeyLen );
handshakeInfo->secretValueLength = keyAgreeParams.wrappedKeyLen;
}
zeroise( &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) );
if( cryptStatusError( status ) )
return( status );
/* If we're using ephemeral DH, hash the requested keyex key length(s)
and DH p and g values. Since this has been deferred until long after
the keyex negotiation took place (so that the original data isn't
available any more), we have to recreate the original encoded values
here */
if( handshakeInfo->requestedServerKeySize > 0 )
{
BYTE keyexBuffer[ 128 + ( CRYPT_MAX_PKCSIZE * 2 ) + 8 ];
const int extraLength = LENGTH_SIZE + sizeofString32( "ssh-dh", 6 );
status = krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_CTX_HASH,
handshakeInfo->encodedReqKeySizes,
handshakeInfo->encodedReqKeySizesLength );
if( cryptStatusError( status ) )
return( status );
setMessageData( &msgData, keyexBuffer,
128 + ( CRYPT_MAX_PKCSIZE * 2 ) );
status = krnlSendMessage( handshakeInfo->iServerCryptContext,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_KEY_SSH );
if( cryptStatusOK( status ) )
{
status = krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_CTX_HASH,
keyexBuffer + extraLength,
msgData.length - extraLength );
}
if( cryptStatusError( status ) )
return( status );
}
/* Hash the client and server DH values and shared secret */
status = krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_CTX_HASH,
handshakeInfo->clientKeyexValue,
handshakeInfo->clientKeyexValueLength );
if( cryptStatusOK( status ) )
status = krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_CTX_HASH,
handshakeInfo->serverKeyexValue,
handshakeInfo->serverKeyexValueLength );
if( cryptStatusOK( status ) )
status = hashAsMPI( handshakeInfo->iExchangeHashcontext,
handshakeInfo->secretValue,
handshakeInfo->secretValueLength );
if( cryptStatusError( status ) )
return( status );
/* Complete the hashing to obtain the exchange hash and then hash *that*
to get the hash that the server signs and sends to the client. The
overall hashed data for the exchange hash is:
string V_C, client version string (CR and NL excluded)
string V_S, server version string (CR and NL excluded)
string I_C, client hello
string I_S, server hello
string K_S, the host key
[[ uint32 min, min.preferred keyex key size for ephemeral DH ]]
[ uint32 n, preferred keyex key size for ephemeral DH ]
[[ uint32 max, max.preferred keyex key size for ephemeral DH ]]
[ mpint p, DH p for ephemeral DH ]
[ mpint g, DH g for ephemeral DH ]
mpint e, client DH keyex value
mpint f, server DH keyex value
mpint K, the shared secret
The client and server version string ahd hellos and the host key were
hashed inline during the handshake. The optional parameters are for
negotiated DH values (see the conditional-hashing code above). The
double-optional parameters are for the revised version of the DH
negotiation mechanism, the original only had n, the revised version
allowed a { min, n, max } range */
status = krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_CTX_HASH, "", 0 );
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, handshakeInfo->sessionID,
CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CTXINFO_HASHVALUE );
if( cryptStatusOK( status ) )
handshakeInfo->sessionIDlength = msgData.length;
}
if( cryptStatusError( status ) )
return( status );
krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_DELETEATTRIBUTE, NULL,
CRYPT_CTXINFO_HASHVALUE );
krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_CTX_HASH, handshakeInfo->sessionID,
handshakeInfo->sessionIDlength );
return( krnlSendMessage( handshakeInfo->iExchangeHashcontext,
IMESSAGE_CTX_HASH, "", 0 ) );
}
#endif /* USE_SSH */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -