-module(nquic_protocol_zero_rtt).
-moduledoc """
0-RTT (early data) packet-protection key installation.
Derives and installs the client 0-RTT keys into `#conn_state{}` for
both the resumption-less path (keyed off the ClientHello hash) and the
PSK resumption path (keyed off the pre-shared key). RFC 9001 §4.3 /
RFC 8446 §7.1 early secret derivation. Behaviour is identical for both
entry points except the early-secret input.
""".
-include("nquic_conn.hrl").
-export([
install_zero_rtt_keys/3,
install_zero_rtt_keys_psk/4
]).
-type cipher() :: aes_128_gcm | aes_256_gcm | chacha20_poly1305.
-spec install(binary(), cipher(), nquic_protocol:state()) -> nquic_protocol:state().
install(EarlySecret, Cipher, State) ->
Version = State#conn_state.version,
{Key, IV, HP} = nquic_keys:derive_packet_protection(EarlySecret, Cipher, Version),
ZeroRTTKeys = #{client => nquic_keys:make_role_keys(Cipher, Key, IV, HP)},
Crypto0 = State#conn_state.crypto,
NewKeys = (Crypto0#conn_crypto.keys)#{rtt0 => ZeroRTTKeys},
State#conn_state{crypto = Crypto0#conn_crypto{keys = NewKeys}}.
-doc "Install 0-RTT keys derived from the ClientHello hash and cipher.".
-spec install_zero_rtt_keys(binary(), cipher(), nquic_protocol:state()) ->
nquic_protocol:state().
install_zero_rtt_keys(ClientHelloHash, Cipher, State) ->
Hash = nquic_keys:cipher_to_hash(Cipher),
EarlySecret = nquic_keys:early_secrets(ClientHelloHash, Hash),
install(EarlySecret, Cipher, State).
-doc "Install 0-RTT keys with a pre-shared key from session resumption.".
-spec install_zero_rtt_keys_psk(binary(), binary(), cipher(), nquic_protocol:state()) ->
nquic_protocol:state().
install_zero_rtt_keys_psk(PSK, ClientHelloHash, Cipher, State) ->
Hash = nquic_keys:cipher_to_hash(Cipher),
EarlySecret = nquic_keys:early_secrets(PSK, ClientHelloHash, Hash),
install(EarlySecret, Cipher, State).