Windows2003-3790/termsrv/common/tssec/sesskey.c
2020-09-30 16:53:55 +02:00

666 lines
14 KiB
C

/*++
Copyright (c) 1994-1998 Microsoft Corporation
Module Name:
sesskey.c
Abstract:
Contains common client/server code that generate session key.
Author:
Madan Appiah (madana) 24-Jan-1998
Environment:
User Mode - Win32
Revision History:
--*/
#include <seccom.h>
VOID
Salt8ByteKey(
LPBYTE pbKey,
DWORD dwSaltBytes
)
/*++
Routine Description:
This macro function salts the first 1 or 3 bytes of the 8 bytes key to a
known value in order to make it a 40-bit key.
Arguments:
pbKey - pointer to a 8 bytes key buffer.
dwSaltBytes - this value should be either 1 or 3
Return Value:
None.
--*/
{
ASSERT( (dwSaltBytes == 1) || (dwSaltBytes == 3) );
if( dwSaltBytes == 1 ) {
//
// for 56-bit encryption, salt first byte only.
//
*pbKey++ = 0xD1 ;
}
else if (dwSaltBytes == 3) {
//
// for 40-bit encryption, salt first 3 bytes.
//
*pbKey++ = 0xD1 ;
*pbKey++ = 0x26 ;
*pbKey = 0x9E ;
}
return;
}
VOID
FinalHash(
LPRANDOM_KEYS_PAIR pKeyPair,
LPBYTE pbKey
)
/*++
Routine Description:
This macro function hashes the final key with the client and server randoms.
Arguments:
pKeyPair - pointer a random key pair structure.
pbKey - pointer to a key buffer, the final key is returned back in the same
buffer.
Return Value:
None.
--*/
{
MD5_CTX Md5Hash;
//
// final_key = MD5(key + clientRandom + serverRandom)
//
MD5Init (&Md5Hash);
MD5Update(&Md5Hash, pbKey, MAX_SESSION_KEY_SIZE);
MD5Update(&Md5Hash, pKeyPair->clientRandom, RANDOM_KEY_LENGTH);
MD5Update(&Md5Hash, pKeyPair->serverRandom, RANDOM_KEY_LENGTH);
MD5Final (&Md5Hash);
//
// copy the final key back to the input buffer.
//
ASSERT( MD5DIGESTLEN >= MAX_SESSION_KEY_SIZE );
memcpy(pbKey, Md5Hash.digest, MAX_SESSION_KEY_SIZE);
return;
}
VOID
MakeMasterKey(
LPRANDOM_KEYS_PAIR pKeyPair,
LPSTR FAR *ppszSalts,
LPBYTE pbPreMaster,
LPBYTE pbMaster
)
/*++
Routine Description:
This macro function makes a master secret using a pre-master secret.
Arguments:
pKeyPair - pointer a key pair structure.
ppszSalts - pointer to a salt key strings array.
pbPreMaster - pointer to a pre-master secret key buffer.
pbMaster - pointer to a master secret key buffer.
Return Value:
None.
--*/
{
DWORD i;
MD5_CTX Md5Hash;
A_SHA_CTX ShaHash;
BYTE bShaHashValue[A_SHA_DIGEST_LEN];
//
//initialize all buffers with zero
//
memset( pbMaster, 0, PRE_MASTER_SECRET_LEN);
memset( bShaHashValue, 0, A_SHA_DIGEST_LEN);
for ( i = 0 ; i < 3 ; i++) {
//
// SHA(ppszSalts[i] + pre-master + clientRandom + serverRandom)
//
A_SHAInit(&ShaHash);
A_SHAUpdate(&ShaHash, ppszSalts[i], strlen(ppszSalts[i]));
A_SHAUpdate(&ShaHash, pbPreMaster, PRE_MASTER_SECRET_LEN );
A_SHAUpdate(
&ShaHash,
pKeyPair->clientRandom,
sizeof(pKeyPair->clientRandom) );
A_SHAUpdate(
&ShaHash,
pKeyPair->serverRandom,
sizeof(pKeyPair->serverRandom) );
A_SHAFinal(&ShaHash, bShaHashValue);
//
// MD5(pre_master + SHA-hash)
//
MD5Init(&Md5Hash);
MD5Update(&Md5Hash, pbPreMaster, PRE_MASTER_SECRET_LEN );
MD5Update(&Md5Hash, bShaHashValue, A_SHA_DIGEST_LEN);
MD5Final(&Md5Hash);
//
// copy part of the master secret.
//
memcpy(
pbMaster + (i * MD5DIGESTLEN),
Md5Hash.digest,
MD5DIGESTLEN);
}
return;
}
VOID
MakePreMasterSecret(
LPRANDOM_KEYS_PAIR pKeyPair,
LPBYTE pbPreMasterSecret
)
/*++
Routine Description:
This function makes a pre-master secret for the initial session key.
Arguments:
pKeyPair - pointer a key pair structure.
pbPreMasterSecret - pointer to a pre-master secret key buffer, it is
PRE_MASTER_SECRET_LEN bytes long.
Return Value:
None.
--*/
{
//
// copy PRE_MASTER_SECRET_LEN/2 bytes from clientRandom first.
//
memcpy(
pbPreMasterSecret,
pKeyPair->clientRandom,
PRE_MASTER_SECRET_LEN/2 );
//
// copy PRE_MASTER_SECRET_LEN/2 bytes from serverRandom next.
//
memcpy(
pbPreMasterSecret + PRE_MASTER_SECRET_LEN/2,
pKeyPair->serverRandom,
PRE_MASTER_SECRET_LEN/2 );
return;
}
VOID
GenerateMasterSecret(
LPRANDOM_KEYS_PAIR pKeyPair,
LPBYTE pbPreMasterSecret
)
/*++
Routine Description:
This function creates a master secret key using the pre-master key and
random key pair.
Arguments:
pKeyPair - pointer a key pair structure.
pbPreMasterSecret - pointer to a pre-master secret key buffer, it is
PRE_MASTER_SECRET_LEN bytes long.
Return Value:
None.
--*/
{
BYTE abMasterSecret[PRE_MASTER_SECRET_LEN];
LPSTR apszSalts[3] = { "A","BB","CCC" } ;
ASSERT( PRE_MASTER_SECRET_LEN == 3 * MD5DIGESTLEN );
//
// make master secret.
//
MakeMasterKey(
pKeyPair,
(LPSTR FAR *)apszSalts,
pbPreMasterSecret,
(LPBYTE)abMasterSecret );
//
// copy master secret in the return buffer.
//
memcpy( pbPreMasterSecret, abMasterSecret, PRE_MASTER_SECRET_LEN);
return;
}
VOID
UpdateKey(
LPBYTE pbStartKey,
LPBYTE pbCurrentKey,
DWORD dwKeyLength
)
/*++
Routine Description:
This function updates a key.
Arguments:
pbStartKey - pointer to the start session key buffer.
pbCurrentKey - pointer to the current session key buffer, new session key is
copied to this buffer on return.
dwKeyLength - length of the key.
Return Value:
None.
--*/
{
A_SHA_CTX SHAHash;
MD5_CTX MD5Hash;
BYTE abSHADigest[A_SHA_DIGEST_LEN];
//
// make a SHA(pbStartKey + g_abPad1 + pbCurrentKey) hash.
//
A_SHAInit(&SHAHash);
A_SHAUpdate(&SHAHash, pbStartKey, dwKeyLength);
A_SHAUpdate(&SHAHash, (unsigned char *)g_abPad1, 40);
A_SHAUpdate(&SHAHash, pbCurrentKey, dwKeyLength);
A_SHAFinal(&SHAHash, abSHADigest);
//
// make a MD5(pbStartKey + g_abPad2 + SHAHash) hash.
//
MD5Init(&MD5Hash);
MD5Update(&MD5Hash, pbStartKey, dwKeyLength);
MD5Update(&MD5Hash, g_abPad2, 48);
MD5Update(&MD5Hash, abSHADigest, A_SHA_DIGEST_LEN);
MD5Final(&MD5Hash);
ASSERT( dwKeyLength <= MD5DIGESTLEN );
memcpy(pbCurrentKey, MD5Hash.digest, (UINT)dwKeyLength);
return;
}
BOOL
MakeSessionKeys(
LPRANDOM_KEYS_PAIR pKeyPair,
LPBYTE pbEncryptKey,
struct RC4_KEYSTRUCT FAR *prc4EncryptKey,
LPBYTE pbDecryptKey,
struct RC4_KEYSTRUCT FAR *prc4DecryptKey,
LPBYTE pbMACSaltKey,
DWORD dwKeyStrength,
LPDWORD pdwKeyLength,
DWORD dwEncryptionLevel
)
/*++
Routine Description:
Make the server session key using the client and server random keys.
Assume : the encrypt and decrypt buffer presented are
atleast MAX_SESSION_KEY_SIZE (16) bytes long.
Arguments:
pKeyPair - pointer a key pair structure.
pbEncryptKey - pointer to a buffer where the encryption key is stored.
prc4EncryptKey - pointer to a RC4 encrypt key structure.
pbDecryptKey - pointer to a buffer where the decryption key is stored.
prc4DecryptKey - pointer to a RC4 decrypt key structure.
pbMACSaltKey - pointer to a buffer where the message authentication key is
stored.
dwKeyStrength - specify key strength to use.
pdwKeyLength - pointer to a location where the length of the above
encryption/decryption key is returned.
dwEncryptionLevel - encryption level, used to select the encryption
algorithm.
Return Value:
TRUE - if successfully created the session key.
FALSE - otherwise.
--*/
{
BYTE abPreMasterSecret[PRE_MASTER_SECRET_LEN];
BYTE abMasterSessionKey[PRE_MASTER_SECRET_LEN];
LPSTR apszSalts[3] = { "X","YY","ZZZ" } ;
DWORD dwSaltLen;
//
// make a pre-master secret.
//
MakePreMasterSecret( pKeyPair, (LPBYTE)abPreMasterSecret );
//
// generate master secret.
//
GenerateMasterSecret( pKeyPair, (LPBYTE)abPreMasterSecret );
//
// make a master session key for all three session keys (encrypt, decrypt
// and MACSalt).
//
MakeMasterKey(
pKeyPair,
(LPSTR FAR *)apszSalts,
(LPBYTE)abPreMasterSecret,
(LPBYTE)abMasterSessionKey );
ASSERT( PRE_MASTER_SECRET_LEN == 3 * MAX_SESSION_KEY_SIZE );
//
// copy first part of the master key as MAC salt key.
//
memcpy(
pbMACSaltKey,
(LPBYTE)abMasterSessionKey,
MAX_SESSION_KEY_SIZE );
//
// copy second part of the master key as encrypt key and final hash it.
//
memcpy(
pbEncryptKey,
(LPBYTE)abMasterSessionKey + MAX_SESSION_KEY_SIZE,
MAX_SESSION_KEY_SIZE );
FinalHash( pKeyPair, pbEncryptKey );
//
// copy second part of the master key as decrypt key and final hash it.
//
memcpy(
pbDecryptKey,
(LPBYTE)abMasterSessionKey + MAX_SESSION_KEY_SIZE * 2,
MAX_SESSION_KEY_SIZE );
FinalHash( pKeyPair, pbDecryptKey );
//
// finally select the key length.
//
ASSERT( MAX_SESSION_KEY_SIZE == 16 );
dwSaltLen = 0;
switch ( dwKeyStrength ) {
case SM_40BIT_ENCRYPTION_FLAG:
*pdwKeyLength = MAX_SESSION_KEY_SIZE/2;
dwSaltLen = 3;
break;
case SM_56BIT_ENCRYPTION_FLAG:
*pdwKeyLength = MAX_SESSION_KEY_SIZE/2;
dwSaltLen = 1;
break;
case SM_128BIT_ENCRYPTION_FLAG:
ASSERT( g_128bitEncryptionEnabled );
*pdwKeyLength = MAX_SESSION_KEY_SIZE;
break;
default:
//
// we shouldn't reach here.
//
ASSERT( FALSE );
*pdwKeyLength = MAX_SESSION_KEY_SIZE/2;
dwSaltLen = 1;
break;
}
if( dwSaltLen ) {
Salt8ByteKey( pbMACSaltKey, dwSaltLen );
Salt8ByteKey( pbEncryptKey, dwSaltLen );
Salt8ByteKey( pbDecryptKey, dwSaltLen );
}
//
// finally make rc4 keys.
//
// use microsoft version of rc4 algorithm (super fast!) for level 1 and
// level 2 encryption, for level 3 use RSA rc4 algorithm.
//
if( dwEncryptionLevel <= 2 ) {
msrc4_key( prc4EncryptKey, (UINT)*pdwKeyLength, pbEncryptKey );
msrc4_key( prc4DecryptKey, (UINT)*pdwKeyLength, pbDecryptKey );
}
else {
rc4_key( prc4EncryptKey, (UINT)*pdwKeyLength, pbEncryptKey );
rc4_key( prc4DecryptKey, (UINT)*pdwKeyLength, pbDecryptKey );
}
return( TRUE );
}
BOOL
UpdateSessionKey(
LPBYTE pbStartKey,
LPBYTE pbCurrentKey,
DWORD dwKeyStrength,
DWORD dwKeyLength,
struct RC4_KEYSTRUCT FAR *prc4Key,
DWORD dwEncryptionLevel
)
/*++
Routine Description:
Update the session key using the current and start session keys.
Arguments:
pbStartKey - pointer to the start session key buffer.
pbCurrentKey - pointer to the current session key buffer, new session key is
copied to this buffer on return.
dwKeyStrength - specify key strength to use.
dwKeyLength - length of the key.
prc4Key - pointer to a RC4 key structure.
dwEncryptionLevel - encryption level, used to select the encryption
algorithm.
Return Value:
TRUE - if the successfully update the key.
FALSE - otherwise.
--*/
{
DWORD dwSaltLen;
//
// update current key first.
//
UpdateKey( pbStartKey, pbCurrentKey, dwKeyLength );
//
// use microsoft version of rc4 algorithm (super fast!) for level 1 and
// level 2 encryption, for level 3 use RSA rc4 algorithm.
//
if( dwEncryptionLevel <= 2 ) {
//
// re-initialized RC4 table.
//
msrc4_key( prc4Key, (UINT)dwKeyLength, pbCurrentKey );
//
// scramble the current key.
//
msrc4( prc4Key, (UINT)dwKeyLength, pbCurrentKey );
}
else {
//
// re-initialized RC4 table.
//
rc4_key( prc4Key, (UINT)dwKeyLength, pbCurrentKey );
//
// scramble the current key.
//
rc4( prc4Key, (UINT)dwKeyLength, pbCurrentKey );
}
//
// salt the key appropriately.
//
dwSaltLen = 0;
switch ( dwKeyStrength ) {
case SM_40BIT_ENCRYPTION_FLAG:
ASSERT( dwKeyLength = MAX_SESSION_KEY_SIZE/2 );
dwSaltLen = 3;
break;
case SM_56BIT_ENCRYPTION_FLAG:
ASSERT( dwKeyLength = MAX_SESSION_KEY_SIZE/2 );
dwSaltLen = 1;
break;
case SM_128BIT_ENCRYPTION_FLAG:
ASSERT( g_128bitEncryptionEnabled );
ASSERT( dwKeyLength = MAX_SESSION_KEY_SIZE );
break;
default:
//
// we shouldn't reach here.
//
ASSERT( FALSE );
ASSERT( dwKeyLength = MAX_SESSION_KEY_SIZE/2 );
dwSaltLen = 1;
break;
}
if( dwSaltLen ) {
Salt8ByteKey( pbCurrentKey, dwSaltLen );
}
//
// re-initialized RC4 table again.
//
if( dwEncryptionLevel <= 2 ) {
msrc4_key( prc4Key, (UINT)dwKeyLength, pbCurrentKey );
}
else {
rc4_key( prc4Key, (UINT)dwKeyLength, pbCurrentKey );
}
return( TRUE );
}