# flare.quic.retry

> A module of flare_mlake's API.

QUIC server Retry + Version Negotiation packet codecs (RFC 9000 §8.1 / §17.2.1 / §17.2.5, RFC 9001 §5.8).

When a server wants to validate a client's source address before
committing handshake state, it answers the client Initial with a
**Retry** packet carrying an opaque address-validation **token**. The
client replays the token in a fresh Initial; the server validates it and
proceeds. This module ships:

* :func:`mint_retry_token` / :func:`validate_retry_token` -- an
  HMAC-SHA256 keyed token that binds the client's address and the
  original Destination Connection ID with an issue timestamp, so a token
  is single-path and expires. The server holds the HMAC key; the token
  is opaque to the client.
* :func:`encode_retry_packet` -- assemble the full Retry packet
  including the RFC 9001 §5.8 Retry Integrity Tag (AEAD_AES_128_GCM over
  the Retry Pseudo-Packet with the fixed v1 retry key/nonce).
* :func:`verify_retry_integrity` -- recompute + compare the integrity
  tag of a received Retry (client-side check).
* :func:`encode_version_negotiation` -- build the Version Negotiation
  packet a server sends when a client Initial carries an unsupported
  QUIC version (RFC 9000 §6.1).

Not sans-I/O: the Retry Integrity Tag routes through the OpenSSL AEAD
FFI (:class:`flare.quic.crypto.OpenSslQuicCrypto`). The token HMAC and
the version-negotiation builder are pure, but the module as a whole
links the crypto backend, so it lives outside the sans-I/O set.

References:
- RFC 9000 §8.1 "Address Validation", §17.2.1 / §17.2.5, §6.1.
- RFC 9001 §5.8 "Retry Packet Integrity".

## Functions

### `mint_retry_token`

```mojo
def mint_retry_token(server_key: List[UInt8], client_addr: List[UInt8], odcid: ConnectionId, issued_ms: UInt64) -> List[UInt8]
```

Issue an opaque Retry token bound to ``client_addr`` and ``odcid``, stamped at ``issued_ms``.

Wire layout (server-private; opaque to the client):
``version(1) || issued_ms(8) || odcid_len(1) || odcid ||
HMAC-SHA256(server_key, mac_message)``.

### `validate_retry_token`

```mojo
def validate_retry_token(server_key: List[UInt8], token: List[UInt8], client_addr: List[UInt8], now_ms: UInt64, max_age_ms: UInt64) -> Optional[ConnectionId]
```

Validate a replayed Retry token. Returns the recovered original DCID when the token is authentic, address-matched, and unexpired; ``None`` otherwise. Never raises on a malformed token -- a forged or truncated token is simply rejected (returns ``None``).

### `mint_new_token`

```mojo
def mint_new_token(server_key: List[UInt8], client_ip: List[UInt8], issued_ms: UInt64) -> List[UInt8]
```

A token for a NEW_TOKEN frame, bound to the client's IP address (not its port, which a later connection may not share): ``version(1) || issued_ms(8) || HMAC-SHA256(key, mac_message)``.

### `validate_new_token`

```mojo
def validate_new_token(server_key: List[UInt8], token: List[UInt8], client_ip: List[UInt8], now_ms: UInt64, max_age_ms: UInt64) -> Bool
```

Whether ``token`` is a NEW_TOKEN token this server issued to ``client_ip`` within ``max_age_ms``. A malformed one is just rejected.

### `retry_integrity_tag`

```mojo
def retry_integrity_tag(odcid: ConnectionId, retry_header_and_token: List[UInt8]) -> List[UInt8]
```

Compute the RFC 9001 §5.8 Retry Integrity Tag: AEAD_AES_128_GCM over the Retry Pseudo-Packet (ODCID-len || ODCID || the Retry packet body without the tag) with the fixed v1 retry key/nonce and an empty plaintext. The 16-byte output is the tag.

### `encode_retry_packet`

```mojo
def encode_retry_packet(version: UInt32, dcid: ConnectionId, scid: ConnectionId, token: List[UInt8], odcid: ConnectionId) -> List[UInt8]
```

Build a complete Retry packet (RFC 9000 §17.2.5).

``dcid`` is the client's Source CID (echoed as the new DCID),
``scid`` is the server's freshly chosen Source CID, ``token`` is the
address-validation token (see :func:`mint_retry_token`), and
``odcid`` is the Destination CID from the triggering client Initial
-- it feeds the integrity tag but is not carried on the wire.

### `verify_retry_integrity`

```mojo
def verify_retry_integrity(packet: List[UInt8], odcid: ConnectionId) -> Bool
```

Recompute the Retry Integrity Tag of ``packet`` and compare it to the trailing 16 bytes. Used by a client to authenticate a received Retry against the original DCID it sent.

### `encode_version_negotiation`

```mojo
def encode_version_negotiation(dcid: ConnectionId, scid: ConnectionId, versions: List[UInt32]) -> List[UInt8]
```

Build a Version Negotiation packet (RFC 9000 §6.1 / §17.2.1).

A server sends this in response to a client Initial whose version it
does not support. ``dcid`` MUST be the client Initial's Source CID
and ``scid`` the client Initial's Destination CID (the fields are
echoed swapped so the client can match the response). ``versions``
is the server's supported-version list; the all-zero Version field
marks the packet as VN.

## Aliases

### `NEW_TOKEN_VERSION`

```mojo
comptime NEW_TOKEN_VERSION = UInt8(2)
```

First byte of a NEW_TOKEN token; a Retry token starts with ``_RETRY_TOKEN_VERSION``, so the server tells them apart (sec 8.1.3).
