There are a few things off in your description.
First, you use the term "hkdf". There is such a thing as HKDF, which is a key derivation function that internally uses HMAC; however, HKDF is not used in TLS. TLS defines its own key derivation function, which it consistently calls "PRF". It is described in section 5 of the standard. That function is also based on HMAC, but with a different structure. The PRF has three input parameters:
- the "secret";
- the "seed";
- the "label".
Exactly what goes into each of these parameters depends on the context. Normally, the "label" is a constant string that qualifies the usage point (e.g. "client finished"
when computing the client's Finished
message). The "seed" is made of elements from the handshake, that are normally observable, i.e. non secret. The "secret" input is where goes any actually secret data. The PRF will mix all these values together with a fair dosage of HMAC invocations, into a sequence of bytes of arbitrary length.
In TLS, a key exchange mechanism is used, that results in a "pre-master secret". When RSA key exchange is used, the "pre-master" is generated randomly by the client and encrypted with the server's RSA public key. When using Diffie-Hellman key exchange, the "pre-master" is the encoding of the integer that results from DH ($g^{ab} \pmod p$). When using ECDH (Diffie-Hellman over an elliptic curve), the pre-master secret is the $X$ coordinate of the computed DH point ($abG$). Note that there is no question of hashing at this point: the pre-master secret is a sequence of bytes whose length and contents depend on the used key exchange method.
The master secret is computed from the pre-master secret using the PRF. This is described in section 8. The label is "master secret"
and the seed is the concatenation of the client random and server random (these two values are 32-byte each, generated randomly for each handshake, and exchanged in the ClientHello
and ServerHello
messages). The master secret has length 48 bytes, exactly.
From the master secret are derived the keys and IV needed for symmetric encryption. This is the "key block" that you show. Note that the sizes for the various keys in the key block depend on the negotiated cipher suite. For instance, if the cipher suite uses AES/GCM (e.g. TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
), then there is no MAC key (or, equivalently, the MAC key length is 0), since GCM mode ensures both encryption and integrity check with the same key. Key block computation uses the PRF, with this time the master secret as "secret" input; the "seed" is the concatenation of the server and client randoms. Mind the order! This time, the server random comes first.
The Finished
messages have contents which are computed with, again, the PRF. The contents of the Finished
message are 12 bytes obtained by invoking the PRF with, as "secret" input, the master secret; the "seed" is the hash of all previous handshake messages. The "label" differs depending on who, between the client and the server, sends that specific Finished
message. Since the Finished
messages are sent after the ChangeCipherSpec
, they are encrypted and MACed, using the algorithms and keys which have just been negotiated. In that sense, the keys and nonces (the client and server randoms) are involved multiple times.
Thus, in your description, use of "hkdf" terminology is spurious, as well as the MAC usage, in particular with regards to the Finished
messages.
A nice summary of the SSL handshake is this diagram, which is part of the TLS 1.2 standard:
Client Server
ClientHello -------->
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<-------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished -------->
[ChangeCipherSpec]
<-------- Finished
Application Data <-------> Application Data
Figure 1. Message flow for a full handshake
Note that after the ClientKeyExchange
message from the client, and before the ChangeCipherSpec
, there may be a CertificateVerify
message (this is sent by the client when the server requests a client certificate, and the client sends one, and this is not full-static DH or ECDH).
The "hash of all previous handshake messages" that is used as an input to the PRF for the computation of the Finished
messages really includes all previous messages for this handshake, starting with the ClientHello
. Notably, when the server responds with its own Finished
message, the hash used in that second Finished
message will include the contents of the first Finished
message. Also note that the ChangeCipherSpec
message is not formally a handshake message (though it may occur only at very specific times within the handshake) so its contents are not used for these hashes.
The complete TLS handshake is quite complicated, mostly for historical reasons. If you want to formalize its use of cryptographic algorithms, you have little choice but to invest a few hours of reading time to get through all the details. I suggest starting with this answer.