4

In TLS v1.3, there are three secrets from which other secrets/keys are derived:

  • early secret
  • handshake secret
  • master secret

See this diagram.

The early secret does not include the Diffie-Hellman secret and can thus be used before the ServerHello message. However, it's not clear what the rationale of adding a separate handshake secret (HS): the master secret (MS) does not include any additional information compared to the handshake secret (HS).

As we can see in these formulas the derivation of MS from HS does not involve any additional input (H0 being an empty byte sequence):

HS  ← HKDF.Extract(dES,DHE)
dHS ← HKDF.Expand(HS,Label3,H0)
MS  ← HKDF.Extract(dHS,0)

In other words, why this:

             0
             |
             v
   PSK ->  HKDF-Extract = Early Secret
             |
             +-----> Derive-Secret(., "ext binder" | "res binder", "")
             |                     = binder_key
             |
             +-----> Derive-Secret(., "c e traffic", ClientHello)
             |                     = client_early_traffic_secret
             |
             +-----> Derive-Secret(., "e exp master", ClientHello)
             |                     = early_exporter_master_secret
             v
       Derive-Secret(., "derived", "")
             |
             v
   (EC)DHE -> HKDF-Extract = Handshake Secret
             |
             +-----> Derive-Secret(., "c hs traffic",
             |                     ClientHello...ServerHello)
             |                     = client_handshake_traffic_secret
             |
             +-----> Derive-Secret(., "s hs traffic",
             |                     ClientHello...ServerHello)
             |                     = server_handshake_traffic_secret
             v
       Derive-Secret(., "derived", "")
             |
             v
   0 -> HKDF-Extract = Master Secret
             |
             +-----> Derive-Secret(., "c ap traffic",
             |                     ClientHello...server Finished)
             |                     = client_application_traffic_secret_0
             |
             +-----> Derive-Secret(., "s ap traffic",
             |                     ClientHello...server Finished)
             |                     = server_application_traffic_secret_0
             |
             +-----> Derive-Secret(., "exp master",
             |                     ClientHello...server Finished)
             |                     = exporter_master_secret
             |
             +-----> Derive-Secret(., "res master",
                                   ClientHello...client Finished)
                                   = resumption_master_secret

and not something like that:

             0
             |
             v
   PSK ->  HKDF-Extract = Early Secret
             |
             +-----> Derive-Secret(., "ext binder" | "res binder", "")
             |                     = binder_key
             |
             +-----> Derive-Secret(., "c e traffic", ClientHello)
             |                     = client_early_traffic_secret
             |
             +-----> Derive-Secret(., "e exp master", ClientHello)
             |                     = early_exporter_master_secret
             v
       Derive-Secret(., "derived", "")
             |
             v
   (EC)DHE -> HKDF-Extract = Master Secret
             |
             +-----> Derive-Secret(., "c hs traffic",
             |                     ClientHello...ServerHello)
             |                     = client_handshake_traffic_secret
             |
             +-----> Derive-Secret(., "s hs traffic",
             |                     ClientHello...ServerHello)
             |                     = server_handshake_traffic_secret
             |
             +-----> Derive-Secret(., "c ap traffic",
             |                     ClientHello...server Finished)
             |                     = client_application_traffic_secret_0
             |
             +-----> Derive-Secret(., "s ap traffic",
             |                     ClientHello...server Finished)
             |                     = server_application_traffic_secret_0
             |
             +-----> Derive-Secret(., "exp master",
             |                     ClientHello...server Finished)
             |                     = exporter_master_secret
             |
             +-----> Derive-Secret(., "res master",
                                   ClientHello...client Finished)
                                   = resumption_master_secret

1 Answer 1

4

the master secret does not include any additional information compared to the handshake secret.

This isn't accurate. The keys derived from the Master Secret include additional handshake messages in the handshake transcript:

The Handshake Secret includes these messages in the sessions keys derived from the Handshake Secret: ClientHello...ServerHello

The Master Secret includes these messages in the session keys derived from the Master Secret: ClientHello...server Finished

That said, you did included this in your additional example of what the key derivation could be (whether intentional or simply copy pasting).

So I think the next logical question (or perhaps the question at the root of what you posted) is Why have an additional extract operation between the Handshake Keys and the Master Keys.

Every "Extract" operation creates separation between the keys created prior from the keys created after. The idea is if a compromise occurs of the Handshake keys, there is no increased risk of compromise to the Master Secret keys.

Each "step" which includes the Extract, includes as much of the Handshake Transcript as possible, at that point in time.

(1). The first Extract which produces the Early Secret keys includes in the ensuing session keys a transcript hash of only the Client Hello. At this point, this is the only part of the handshake that exists.

The only content which these keys protects would be any Early Data included in with the Client Hello if the Client were initiating a 0-RTT resumption handshake.

(2). The second Extract which produces the Handshake Secret Keys includes in the ensuing session keys a transcript hash of the Client Hello through the Server Hello. Now that more of the handshake has occurred, there is more to "mix in" to the key generation.

The only content which these keys protects would be the Server Certificate, Server Encrypted Extensions, and the Server Finished. (and in cases where Mutual Authentication is used, Certificate Request & Certificate Verify).

After the Client responds, there is now more handshake records to include in the handshake transcript. Therefore, a new Extract operation occurs, and new keys are derived which "mix in" the new handshake records:

(3). The third/final Extract which produces the Master Secret Keys includes in the ensuing session keys a transcript hash of the Client Hello through the Server Finished. These keys will actually protect application data sent within the HTTPS tunnel.


Because of the repeated Extract operation at each of the three "steps" above, the session keys derived from one step cannot possibly provide any hints or clues as to what session keys will be/have been derived in other steps. (or at least, that's the theory)

2
  • "The Master Secret includes these messages in the key calculation: ClientHello...server Finished": AFAIU, the secrets derived from the master secret include these message, but the master secret itself does not (?).
    – ysdx
    Commented Aug 2, 2021 at 21:34
  • @ysdx Yes, that is correct. I'll reword my post to make it more clear.
    – Eddie
    Commented Aug 2, 2021 at 21:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .