You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
665 lines
14 KiB
665 lines
14 KiB
/*++
|
|
|
|
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 );
|
|
}
|