-module(nquic_stateless_reset). -moduledoc """ Stateless reset token generation and detection per RFC 9000 Section 10.3. Stateless reset allows an endpoint to terminate a connection when it has lost state. The token is an HMAC-SHA256 of the connection ID, truncated to 16 bytes. Reset packets look like short header packets to avoid identification. """. -export([build_packet/1, detect/2, generate_token/2]). -doc "Build a stateless reset packet that looks like a short header packet.". -spec build_packet(binary()) -> binary(). build_packet(Token) when byte_size(Token) =:= 16 -> PrefixLen = 5 + rand:uniform(21) - 1, Prefix = crypto:strong_rand_bytes(PrefixLen), <<_:1, _:1, Rest:6>> = <<(binary:first(Prefix))>>, <<0:1, 1:1, Rest:6, (binary:part(Prefix, 1, PrefixLen - 1))/binary, Token/binary>>. -doc """ Check whether a packet is a stateless reset by comparing the last 16 bytes. Comparison is constant-time (RFC 9000 Section 10.3.1: "An endpoint MUST use a comparison that is constant-time with respect to the contents of the token") to avoid timing side channels. """. -spec detect(binary(), binary()) -> boolean(). detect(Packet, Token) when byte_size(Packet) >= 21, byte_size(Token) =:= 16 -> PacketLen = byte_size(Packet), nquic_crypto:constant_time_equal(binary:part(Packet, PacketLen - 16, 16), Token); detect(_, _) -> false. -doc "Generate a stateless reset token from a static key and connection ID.". -spec generate_token(binary(), binary()) -> binary(). generate_token(StaticKey, CID) -> <> = crypto:mac(hmac, sha256, StaticKey, CID), Token.