/*++ 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 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 ); }