// Deriving the TLS 1.3 handshake-space keys from a real x25519 ECDHE exchange (RFC 8446
// §7.1). Once the ClientHello and ServerHello have carried the two ephemeral key_shares, each
// endpoint owns its private key and the peer's public key, and computes the same shared
// secret. That shared secret feeds the key schedule — Early Secret → Handshake Secret — and
// then the ClientHello..ServerHello transcript derives each side's handshake traffic secret,
// from which `quic_handshake_keys` makes the AEAD key/iv/hp. This is the glue that turns the
// key_share codec, x25519, and the key schedule into the handshake secret a real connection
// derives, instead of one passed in from a test vector.
///|
/// The x25519 ECDHE shared secret from an endpoint's private key and the peer's public key.
pub fn tls13_ecdhe_shared(private_key : Bytes, peer_public : Bytes) -> Bytes {
x25519(private_key, peer_public)
}
///|
/// The Handshake Secret for an x25519 ECDHE exchange with no PSK (RFC 8446 §7.1): the key
/// schedule extracts the shared secret under the Early Secret's derived salt.
pub fn tls13_handshake_secret_from_ecdhe(
private_key : Bytes,
peer_public : Bytes,
) -> Bytes {
tls13_handshake_secret(
tls13_early_secret(Bytes::make(32, b'\x00')),
tls13_ecdhe_shared(private_key, peer_public),
)
}
///|
/// An endpoint's handshake traffic secret from the ECDHE exchange and the ClientHello..
/// ServerHello transcript hash (RFC 8446 §7.1): the client secret when `is_client`, otherwise
/// the server secret. Both peers derive matching secrets because they share the same DHE.
pub fn tls13_ecdhe_handshake_traffic_secret(
private_key : Bytes,
peer_public : Bytes,
transcript_hash : Bytes,
is_client : Bool,
) -> Bytes {
let handshake_secret = tls13_handshake_secret_from_ecdhe(
private_key, peer_public,
)
if is_client {
tls13_client_hs_traffic_secret(handshake_secret, transcript_hash)
} else {
tls13_server_hs_traffic_secret(handshake_secret, transcript_hash)
}
}
///|
/// The client's offered x25519 public key from a ClientHello message: parse it and pull the
/// x25519 key_share. `None` if the message is not a ClientHello or offers no x25519 share.
fn tls13_client_hello_x25519(client_hello : Bytes) -> Bytes? {
let (msg_type, body) = match tls_parse_handshake(client_hello[:]) {
Some(v) => v
None => return None
}
if msg_type != tls_client_hello {
return None
}
let ch = match decode_client_hello(body[:]) {
Some(v) => v
None => return None
}
let mut client_public : Bytes? = None
for share in tls_client_hello_key_shares(ch.extensions) {
if share.0 == tls_group_x25519 {
client_public = Some(share.1)
}
}
client_public
}
///|
/// The server's handshake traffic secret derived from a received ClientHello message alone:
/// pull the client's x25519 key_share and run the ECDHE with the server's ephemeral
/// `server_private` over the ClientHello..ServerHello `transcript_hash`. `None` if the
/// message is not a ClientHello or offers no x25519 share.
pub fn tls13_server_hs_secret_from_client_hello(
client_hello : Bytes,
server_private : Bytes,
transcript_hash : Bytes,
) -> Bytes? {
match tls13_client_hello_x25519(client_hello) {
Some(pub_key) =>
Some(
tls13_ecdhe_handshake_traffic_secret(
server_private, pub_key, transcript_hash, false,
),
)
None => None
}
}
///|
/// Both handshake traffic secrets a server derives from a received ClientHello and its own
/// ephemeral private key (RFC 8446 §7.1), as `(server_secret, client_secret)`: pull the
/// client's x25519 key_share, run the ECDHE with `server_private` over the ClientHello..
/// ServerHello `transcript_hash`, and derive each side's secret. `None` if the message is
/// not a ClientHello or offers no x25519 share. A server protects its own flight with the
/// server secret and verifies the client Finished with the client secret — deriving both
/// from the wire ClientHello, never from the peer's copy.
pub fn tls13_ecdhe_secrets_from_client_hello(
client_hello : Bytes,
server_private : Bytes,
transcript_hash : Bytes,
) -> (Bytes, Bytes)? {
match tls13_client_hello_x25519(client_hello) {
Some(pub_key) =>
Some(
(
tls13_ecdhe_handshake_traffic_secret(
server_private, pub_key, transcript_hash, false,
),
tls13_ecdhe_handshake_traffic_secret(
server_private, pub_key, transcript_hash, true,
),
),
)
None => None
}
}