moonbitstack/moonquic/crypto does not have a README file

    Keys

    pub(all) struct Keys {
    key : Bytes
    iv : Bytes
    hp : Bytes
    } derive(Eq,
    Debug
    )

    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.

    Keys::equal

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

    Keys::mask

    fn Keys::mask(self : Keys, sample : BytesView, suite? : Suite) -> 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::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, suite? : Suite) -> (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, suite? : Suite) -> 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, suite? : Suite) -> 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, suite? : Suite) -> (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. AES-256-GCM is { ..suite,key: 32 }; a suite this library has no cipher for — ChaCha20-Poly1305, say — 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, which is a record away; this library ships no version-2 constants because it has not checked them against that RFC.

    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::to_repr

    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).

    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).

    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).

    Source Files