The bulk encryption and MAC keys are mostly derived from the master key, but often include a number of other items, depending on the protocol and cipher suite used.
The process is the same for both client and server:
Note When performing SSL "reconnects", the protocol engine may perform the above procedure several times, using the same master key. This enables the client and server to have multiple (often simultaneous) connections, each using different bulk encryption and MAC keys, without doing any RSA operations.
As with all CSPs, it is important that good thread-safe practices are used. Thread counts of several dozen are not unusual.
The protocol engine's source code is typically;
BOOL fClient = <are we a client?>;
CRYPT_DATA_BLOB Data;
HCRYPTHASH hMasterHash;
// Finish creating the master_secret.
switch(<protocol being used>)
{
case <PCT 1.0>:
// Specify clear key value.
Data.pbData = pClearKey;
Data.cbData = cbClearKey;
CryptSetKeyParam(hMasterKey, KP_CLEAR_KEY,
(PBYTE)&Data, 0);
// Specify the CH_CHALLENGE_DATA.
Data.pbData = pChallenge;
Data.cbData = cbChallenge;
CryptSetKeyParam(hMasterKey, KP_CLIENT_RANDOM,
(PBYTE)&Data, 0);
// Specify the SH_CONNECTION_ID_DATA.
Data.pbData = pConnectionID;
Data.cbData = cbConnectionID;
CryptSetKeyParam(hMasterKey, KP_SERVER_RANDOM,
(PBYTE)&Data, 0);
// Specify the SH_CERTIFICATE_DATA.
Data.pbData = pbServerCertificate;
Data.cbData = cbServerCertificate;
CryptSetKeyParam(hMasterKey, KP_CERTIFICATE,
(PBYTE)&Data, 0);
break;
case <SSL 2.0>:
// Specify clear key value.
Data.pbData = pClearKey;
Data.cbData = cbClearKey;
CryptSetKeyParam(hMasterKey, KP_CLEAR_KEY,
(PBYTE)&Data, 0);
// Specify the CH_CHALLENGE_DATA.
Data.pbData = pChallenge;
Data.cbData = cbChallenge;
CryptSetKeyParam(hMasterKey, KP_CLIENT_RANDOM,
(BYTE*)&Data, 0);
// Specify the SH_CONNECTION_ID_DATA.
Data.pbData = pConnectionID;
Data.cbData = cbConnectionID;
CryptSetKeyParam(hMasterKey, KP_SERVER_RANDOM,
(BYTE*)&Data, 0);
break;
case <SSL 3.0>:
case <TLS 1.0>:
// Specify client_random.
Data.pbData = pClientRandom;
Data.cbData = cbClientRandom;
CryptSetKeyParam(hMasterKey, KP_CLIENT_RANDOM,
(PBYTE)&Data, 0);
// Specify server_random.
Data.pbData = pServerRandom;
Data.cbData = cbServerRandom;
CryptSetKeyParam(hMasterKey, KP_SERVER_RANDOM,
(PBYTE)&Data, 0);
}
// Create the master hash object from the master key.
CryptCreateHash(hProv, CALG_SCHANNEL_MASTER_HASH,
hMasterKey, 0, &hMasterHash);
// Derive read key from the master hash object.
CryptDeriveKey(hProv,
CALG_SCHANNEL_ENC_KEY,
hMasterHash,
fClient ? CRYPT_SERVER : 0,
&hReadKey);
// Derive write key from the master hash object.
CryptDeriveKey(hProv,
CALG_SCHANNEL_ENC_KEY,
hMasterHash,
fClient ? 0 : CRYPT_SERVER,
&hWriteKey);
if(<protocol being used> != <SSL 2.0>)
{
// Derive read MAC from the master hash object.
CryptDeriveKey(hProv,
CALG_SCHANNEL_MAC_KEY,
hMasterHash,
fClient ? CRYPT_SERVER : 0,
&hReadMAC);
// Derive write MAC from the master hash object.
CryptDeriveKey(hProv,
CALG_SCHANNEL_MAC_KEY,
hMasterHash,
fClient ? 0 : CRYPT_SERVER,
&hWriteMAC);
}
CryptDestroyHash(hMasterHash);