moonbitstack/moonquic/crypto does not have a README file

    Keys

    pub(all) struct Keys {
    key : Bytes
    iv : Bytes
    hp : Bytes
    suite : Suite
    }

    One direction's packet-protection keys (RFC 9001 §5.1): the AEAD key, the IV each packet's nonce is built from, and the header-protection key.
    impl Eq for Keys

    Keys::equal

    fn Keys::equal(self : Keys, other : Keys) -> Bool

    Keys::mask

    fn Keys::mask(self : Keys, sample : BytesView) -> Bytes

    The header-protection mask for a ciphertext sample (RFC 9001 §5.4.2).

    Keys::nonce

    fn Keys::nonce(self : Keys, number : Int64) -> Bytes

    The AEAD nonce for a packet number: the number, left-padded to the IV's length, XORed with the IV (RFC 9001 §5.3). The number occupies the low eight bytes and any higher IV bytes pass through.

    Keys::nonce_at

    fn Keys::nonce_at(self : Keys, path : UInt64, number : Int64) -> Bytes

    The AEAD nonce for a packet on path path, whose packet numbers start over at zero (draft-ietf-quic-multipath §2.4). The draft composes a 96-bit Path-and-Packet-Number from the 32 bits of the path id in network byte order, two zero bits, and the 62-bit packet number, and XORs it with the IV: two paths may then use the same number without using the same nonce. Path 0 gives the number RFC 9001 §5.3 describes, so a connection with one path computes exactly what it did before.

    Keys::not_equal

    fn Keys::not_equal(x : Keys, y : Keys) -> Bool

    Keys::of

    fn Keys::of(secret : BytesView, suite? : Suite, version? : Version) -> Keys

    The keys a traffic secret expands to (RFC 9001 §5.1), under the three labels the version fixes and at the lengths the suite fixes.

    Keys::open

    fn Keys::open(self : Keys, packet : BytesView, largest~ : Int64, at? : Int, path? : UInt64) -> (Bytes, Int64)?

    A received packet opened: header protection removed, the packet number recovered against largest, and the payload AEAD-opened with the recovered header as associated data (RFC 9001 §5.3–§5.4).

    largest is the largest packet number already received in this space, or -1 when none has been: the number on the wire is truncated, and RFC 9000 §A.3 recovers the full one from what the receiver has seen.

    at is where the packet number starts. Leave it out for a long header, whose own Length field says where; a short header needs it, because how long the connection ID is is something only the receiver knows.

    None when the packet is too short to sample, when a long header does not parse, or when the tag does not match — one answer for every way of being unreadable, because a receiver's response to all of them is to drop the packet.

    Keys::protect

    fn Keys::protect(self : Keys, packet : BytesView, at~ : Int, size~ : Int) -> Bytes

    Apply header protection: XOR the first byte's protected bits and the size packet-number bytes at at with the mask (RFC 9001 §5.4.1).

    The sample starts at at + 4, the fixed position that assumes the longest packet-number field whatever this packet's is, so a receiver can take the sample before it knows how long the field is.

    Keys::seal

    fn Keys::seal(self : Keys, header : BytesView, payload : BytesView, number~ : Int64, path? : UInt64) -> Bytes

    A protected packet: seal payload with header as associated data, then protect the header (RFC 9001 §5.3–§5.4).

    header ends in the packet number, and its first byte's low two bits say how many bytes that is, so both header forms are handled without being told which is which — build it with @packet.Long::header or @packet.Short::header.

    Keys::to_repr

    Keys::unprotect

    fn Keys::unprotect(self : Keys, packet : BytesView, at~ : Int) -> (Bytes, Int)

    Remove header protection: recover the first byte, read the packet-number length from its low two bits, and unmask that many bytes. Returns the unprotected packet and the recovered length (RFC 9001 §5.4.1).

    Suite

    pub(all) struct Suite {
    digest :
    Digest

    key : Int
    iv : Int
    hp : Int
    tag : Int
    aead : (Bytes) -> &
    Aead

    mask : (Bytes, BytesView) -> Bytes
    }

    The cipher a packet is protected with (RFC 9001 §5.1, §5.3, §5.4.3): how many bytes of key, IV, header-protection key and authentication tag a traffic secret expands to, which hash the expansion runs under, the AEAD that seals a payload, and the cipher that turns a ciphertext sample into a header-protection mask.

    The preset is AEAD_AES_128_GCM with SHA-256, which §5.2 fixes for Initial packets and every space uses until TLS negotiates otherwise. aes256 and chacha20 are the other two; a suite beyond these is a @spec.Aead implementation away and needs no change here.

    Suite::new

    fn Suite::new(digest? :
    Digest
    , key? : Int, iv? : Int, hp? : Int, tag? : Int, aead? : (Bytes) -> &
    Aead
    , mask? : (Bytes, BytesView) -> Bytes) -> Suite

    A suite by name, each part defaulting to the AES-128-GCM preset.

    Version

    pub(all) struct Version {
    salt : Bytes
    key : Bytes
    iv : Bytes
    hp : Bytes
    ku : Bytes
    retry : Bytes
    nonce : Bytes
    } derive(Eq,
    Debug
    )

    The constants a QUIC version fixes (RFC 9001 §5.2, §5.8): the Initial salt, the four HKDF labels, and the key and nonce a Retry's integrity tag is computed under.

    The preset is version 1. RFC 9369 changes every one of them for version 2 — the salt and the Retry key and nonce by its §3.3, and all four labels by prefixing them with quicv2 — which is why a version is a value here and not a constant in the code.

    Version::equal

    fn Version::equal(Version, Version) -> Bool

    Version::new

    fn Version::new(salt? : Bytes, key? : Bytes, iv? : Bytes, hp? : Bytes, ku? : Bytes, retry? : Bytes, nonce? : Bytes) -> Version

    A version's constants by name, each defaulting to version 1's.

    Version::not_equal

    fn Version::not_equal(x : Version, y : Version) -> Bool

    Version::of

    fn Version::of(number : UInt) -> Version?

    The constants of the version a long header carries, or None for a version this library does not speak — the answer that decides whether a first flight can be read at all.

    Version::to_repr

    aes256

    let aes256 : Suite

    AEAD_AES_256_GCM with SHA-384 (RFC 9001 §5.3).

    aes_gcm

    fn aes_gcm(key : Bytes) -> &
    Aead

    AES-GCM over a key of the length the caller passes: AES-128 for sixteen bytes, AES-256 for thirty-two.

    A key of another length aborts rather than raising. Key lengths come from a suite, which is a constant of the program, so a wrong one is a mistake in the code and not something a peer can provoke.

    aes_mask

    fn aes_mask(hp : Bytes, sample : BytesView) -> Bytes

    The AES header-protection mask: the first five bytes of one ECB block over the sample (RFC 9001 §5.4.3).

    application

    fn application(master : BytesView, transcript : BytesView, client~ : Bool, suite? : Suite, version? : Version) -> Keys

    This endpoint's Application (1-RTT) keys, from the master secret over the ClientHello..server Finished transcript (RFC 9001 §5.2).

    chacha20

    let chacha20 : Suite

    AEAD_CHACHA20_POLY1305 with SHA-256 (RFC 9001 §5.3, §5.4.4).

    chacha_mask

    fn chacha_mask(hp : Bytes, sample : BytesView) -> Bytes

    The ChaCha20 header-protection mask (RFC 9001 §5.4.4): the sample's first four bytes are the block counter, little-endian, the other twelve the nonce, and the mask the first five bytes of that keystream block.

    chacha_poly

    fn chacha_poly(key : Bytes) -> &
    Aead

    ChaCha20-Poly1305 over a 32-byte key.

    handshake

    fn handshake(handshake_secret : BytesView, transcript : BytesView, client~ : Bool, suite? : Suite, version? : Version) -> Keys

    This endpoint's Handshake-space keys, from the handshake traffic secret over the ClientHello..ServerHello transcript (RFC 9001 §5.2).

    initial

    fn initial(dcid : BytesView, suite? : Suite, version? : Version) -> (Keys, Keys)

    The client's and server's Initial keys, in that order (RFC 9001 §5.2).

    These are the one part of the handshake that needs no TLS exchange: both sides can compute them from the connection ID alone, which is also why Initial packets are protected against off-path observers rather than against anybody at all.
    fn next(secret : BytesView, suite? : Suite, version? : Version) -> (Bytes, Keys)

    Roll to the next key generation: the updated traffic secret and the keys it expands to, which is what an endpoint installs when it flips the Key Phase bit.

    retry_ok

    fn retry_ok(packet : BytesView, odcid : BytesView, suite? : Suite, version? : Version) -> Bool

    Whether a Retry packet's trailing integrity tag is the right one for odcid.

    The comparison accumulates the difference rather than stopping at the first byte that differs, so how long it takes says nothing about how much of the tag was right.

    retry_tag

    fn retry_tag(body : BytesView, odcid : BytesView, suite? : Suite, version? : Version) -> Bytes

    A Retry packet's integrity tag (RFC 9001 §5.8), over everything up to but not including the tag, bound to the original Destination Connection ID.

    The associated data is the Retry Pseudo-Packet: the ODCID's length byte, the ODCID, then the Retry packet. The plaintext is empty, so the AEAD's output is the tag alone.

    sample

    let sample : Int

    How many bytes of ciphertext a header-protection mask is sampled from (RFC 9001 §5.4.2). Every AEAD QUIC defines samples sixteen, so this is the protocol's number rather than a suite's.

    secret

    fn secret(dcid : BytesView, suite? : Suite, version? : Version) -> Bytes

    The Initial secret both endpoints share: HKDF-Extract(salt, dcid) over the Destination Connection ID from the client's first packet (RFC 9001 §5.2).

    suite

    let suite : Suite

    AEAD_AES_128_GCM with SHA-256 (RFC 9001 §5.2).

    token_ok

    fn token_ok(key : BytesView, token : BytesView, suite? : Suite) -> Bool

    Whether a token's trailing tag is the one key gives for everything before it. As with the Retry tag, the comparison accumulates, so its duration says nothing about the match.

    token_tag

    fn token_tag(key : BytesView, body : BytesView, suite? : Suite) -> Bytes

    A tag binding body to key, for a token an endpoint hands out and takes back (RFC 9000 §8.1.2's address validation token). The construction is the Retry integrity tag's: an AEAD over an empty plaintext with body as associated data, so the answer is the tag alone and a forgery costs a tag forgery.

    update

    fn update(secret : BytesView, suite? : Suite, version? : Version) -> Bytes

    The next generation's 1-RTT traffic secret (RFC 9001 §6.1). Applying it again advances another generation; the Key Phase bit in the short header says which generation protects a packet.

    version

    let version : Version

    QUIC version 1 (RFC 9001 §5.2 and §5.8).

    version2

    let version2 : Version

    QUIC version 2 (RFC 9369 §3.3, Appendix A.5): a different Initial salt, four labels with v2 in front of the word they carry, and a Retry tag under its own key and nonce.

    zero_rtt

    fn zero_rtt(secret : BytesView, suite? : Suite, version? : Version) -> Keys

    A 0-RTT packet's keys from the client's early traffic secret (RFC 9001 §5.2). The early secret is TLS's to derive; expanding it into key, IV and header-protection key is the same as for any other traffic secret.

    Source Files