Cryptography · Protocol Construction
Key Confirmation Ordering and What It Prevents
Why a Finished message must come after the peer can no longer change the transcript, what an explicit key-confirmation exchange adds, and which attacks a misordered exchange leaves open.
A key exchange produces a shared secret. It does not, by itself, produce any evidence that the peer computed the same secret, nor that the peer is the entity the other side thinks it is talking to. Key confirmation is the set of messages that close that gap: short, cheap, and positioned at a point in the message sequence where they can cover everything that must be agreed. The ordering of those messages relative to the rest of the handshake is not stylistic. Move one earlier and the protocol becomes vulnerable to a class of attack that no amount of MAC strength repairs.
The property being established
What key confirmation establishes is narrow and precise: that the party sending the confirmation holds the key, and that it computed that key from the same message sequence the receiver observed. It is not authentication in the identity sense. Authentication says who the peer is; confirmation says the peer has the key. A protocol typically needs both, and they are provided by different mechanisms: signatures or certificates for identity, and a MAC over the transcript under a key derived from the exchange for confirmation.
The reason the two cannot be collapsed is that a signature can be replayed. An attacker who records an entire handshake between a legitimate client and server, including the certificate and the signed transcript hash, can present the same signature to a third party in a new connection if the signature is not bound to a value the third party contributes. Binding it to the transcript fixes replay between sessions but not reflection between roles, and a MAC over the transcript fixes both only if the transcript includes a fresh contribution from each side and the MAC direction is distinct.
The standard construction is therefore: a nonce or ephemeral key share from each side, a signature over the transcript by the authenticating party, and a MAC over the transcript by each party in turn, with distinct keys per direction. TLS 1.3 composes exactly this, with the CertificateVerify signature and the Finished MACs, and the derivation labels that keep the two directions apart are described in domain separation and labelled key derivation.
Why Finished comes last in its flight
In TLS 1.3 the server’s second flight is: ServerHello, EncryptedExtensions, Certificate, CertificateVerify, Finished. The client’s second flight is Certificate, CertificateVerify, Finished. In each case the Finished MAC is computed over the transcript that includes every preceding message of that flight, and it is sent after them.
The constraint that forces this ordering is that Finished must cover the messages it is confirming. If the server sent Finished before CertificateVerify, the transcript at the point of the MAC would not include the signature, and an attacker could strip CertificateVerify from the flight without invalidating the MAC. The client would then complete the handshake believing the server had authenticated with a certificate, when in fact it had only demonstrated knowledge of a key derived from the exchange. With anonymous key exchange that is a man-in-the-middle.
The same argument explains why the server’s Finished covers the client’s ClientHello and not merely the server’s own flight. The client’s key share is in that message, so including it binds the confirmation to the specific ephemeral keys in use. A server Finished that covered only the server’s flight would be a value that could be lifted into a session with a different client key share, and the resulting traffic keys would differ, so the connection would fail later; but the failure would arrive after the client had sent its own certificate and signature, which is information an attacker may want.
There is a subtler ordering requirement inside the client’s flight. The client’s Finished covers the client’s CertificateVerify, which is the point at which the client’s identity becomes bound. Because the client’s Finished is also the first message encrypted under the client’s application traffic key in some designs, and because the server must verify the client’s Finished before it can trust anything the client says, the server cannot act on client application data until that MAC verifies. Implementations that read ahead into the first application record before checking Finished have inverted the order, and the resulting protocol accepts data from an unconfirmed peer.
Implicit versus explicit confirmation
TLS 1.3 relies on implicit confirmation in both directions. The server’s Finished arrives before the client’s, and the client’s ability to verify it is itself evidence that the server holds the handshake secrets; the client’s Finished, arriving later, gives the server the same evidence in the other direction. No separate confirmation message is needed, because the Finished MAC already is one.
IKEv2, in RFC 7296, is a protocol that does rely on explicit confirmation. Its second exchange, IKE_AUTH, carries the initiator’s identity and authentication payload and then the responder’s, and each authentication payload covers the nonces and the Diffie–Hellman public values of both sides through a signed or MACed octet stream. Neither side sends traffic on the child SA until that exchange completes, which places the confirmation at the end of a flight rather than in a separate round.
SPEKE and its descendants make the point sharply. The protocol derives a strong key from a password, and the security claim is that an active attacker gets exactly one online guess. That claim holds only if a failed guess is detectable by the honest party through a confirmation step whose failure does not leak more than one bit. A confirmation that reveals which party failed, or that is sent before the exchange has committed to the password-derived key, converts the single-guess bound into something weaker.
ISO/IEC 9798-3 describes the same primitive at a higher level of abstraction: entity authentication mechanisms in which a claimant proves knowledge of a private key by signing a challenge that the verifier generated, with the challenge and response ordered so that the response cannot be replayed. A protocol that adopts that mechanism but places the signature before the verifier’s challenge has kept the cryptography and discarded the guarantee.
Reflection and the direction bit
Consider a protocol where both sides confirm by sending $\mathrm{MAC}(K, \text{transcript})$. A client and a server compute the same value over the same transcript, so an attacker who observes the client’s confirmation can forward it to the client as if it were the server’s. The client verifies its own MAC and concludes the server has confirmed. The attacker has completed a handshake with no key of its own.
Three fixes exist and protocols use them in combination. Distinct keys per direction, derived with role-specific labels, so the two MACs are values under different keys. Distinct constants in the MAC input, such as "client finished" versus "server finished", so the same key produces different values. Or distinct positions in a transcript that already differs by sender, which is only true if the transcript records who sent what, which most byte-oriented transcripts do not.
TLS 1.3 does not use a constant in the Finished input; it relies entirely on distinct keys. The client’s Finished is a MAC under client_finished_key, the server’s under server_finished_key, and both are derived from different traffic secrets. An implementation that derived both from one secret and relied on the transcript to differ would be relying on the transcript recording direction, which a concatenation of message bytes does not do.
Where confirmation sits relative to early data
A client that sends early data (0-RTT) sends application data before any confirmation has occurred, using a key derived from a pre-shared key and the first ClientHello. The server can accept those records without having confirmed the client’s possession of the PSK beyond the binder, which is a weaker check than a Finished MAC because the binder is computed before the server’s fresh contribution exists. The protocol’s answer is to require that 0-RTT data be replayable-safe at the application layer, because the protocol cannot promise it is fresh.
The ordering rule that follows is that 0-RTT ends at the point where the server’s Finished arrives. After that, the client must not send further early data, and the server must not accept it, because the confirmed keys now exist and using the unconfirmed ones would be a downgrade. An implementation that keeps accepting early records after the handshake completes has left a channel open whose key is not the confirmed one; the details of the binder computation are in PSK binders and early data.
The failure modes worth testing
Key confirmation bugs are rarely in the MAC. They are in what the MAC covers and when it is checked.
Confirmation over a partial transcript. If the confirmation covers only the messages the sender itself produced, the receiver’s contribution is unbound. The test is to delete a message from the receiver’s flight and see whether the sender’s confirmation still verifies.
Confirmation verified after use. An implementation that decrypts and delivers application data before checking the peer’s Finished has a window in which unconfirmed data is acted upon. The test is to replace the Finished with garbage and confirm that no application data reaches the upper layer.
package handshake
import "errors"
var ErrHandshakeIncomplete = errors.New("peer Finished not verified")
type conn struct {
peerFinishedVerified bool
}
// deliverApplication refuses to hand records upward until the peer's
// Finished has verified against the transcript covering every handshake
// message received so far.
func (c *conn) deliverApplication(plaintext []byte) ([]byte, error) {
if !c.peerFinishedVerified {
return nil, ErrHandshakeIncomplete
}
return plaintext, nil
}
Buffering instead of failing closed is the variant that looks harmless and is not: the buffered records were encrypted under keys derived before the peer was confirmed, and releasing them after confirmation does not retroactively bind them to the confirmed state.
Confirmation keys derived before the final key exchange message. In a protocol with more than one key exchange round, a confirmation key derived from the state before the last round does not bind that round. The test is to modify the last round’s public value and confirm that the confirmation fails.
Missing confirmation in one direction. A protocol where only the initiator confirms leaves the responder unable to distinguish a peer that holds the key from an attacker replaying the initiator’s flight. The responder’s first encrypted message serves as implicit confirmation only if the responder actually encrypts under the negotiated key and the initiator checks it.
None of these are fixed by a longer MAC or a stronger hash. They are fixed by placing the confirmation at the end of the flight, covering the full transcript, deriving its key from the final state, and checking it before any dependent action. The transcript that makes the coverage meaningful is the one described in transcript hashing and handshake binding, and the state it commits to must be the same state the two sides later use for traffic keys, or the confirmation will attest to a session that does not exist.
References
- RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 — IETF
- ISO/IEC 9798-3: Entity Authentication Mechanisms Using Digital Signature Techniques — ISO/IEC
- RFC 7296: Internet Key Exchange Protocol Version 2 (IKEv2) — IETF