Sign in

    moonbitstack/moontls/record does not have a README file

    Cipher

    pub struct Cipher {
    suite : Suite
    traffic : Traffic
    seq : UInt64
    }

    One direction's record protection: its suite, traffic keys, and the sequence number that keeps their nonces apart. RFC 8446 §5.3 restarts the sequence at zero on every key change.

    Cipher::new

    fn Cipher::new(secret : BytesView, suite? : Suite) -> Cipher

    Record protection under the traffic secret, starting at sequence zero.

    Cipher::nonce

    fn Cipher::nonce(self : Cipher) -> Bytes

    The nonce the next record will be sealed or opened under (RFC 8446 §5.3).

    Cipher::open

    fn Cipher::open(self : Cipher, record : BytesView) -> (Kind, Bytes) raise
    Alert

    Open one whole TLSCiphertext record — header included, since RFC 8446 §5.2 makes that header the AEAD's additional data — into the content type and content sealed inside, advancing the sequence number.

    A tag that does not verify is a BadRecordMac and the sequence does not move, because §5.2 ends the connection there.

    Cipher::rekey

    fn Cipher::rekey(self : Cipher, secret : BytesView) -> Unit

    Install a new traffic secret and restart the sequence number at zero (RFC 8446 §5.3).

    Cipher::seal

    fn Cipher::seal(self : Cipher, kind : Kind, content : BytesView, padding? : Int) -> Bytes raise
    Alert

    Seal content as a TLSCiphertext record (RFC 8446 §5.2) with padding zero octets after the inner content type, and advance the sequence number.

    Cipher::seq

    fn Cipher::seq(self : Cipher) -> UInt64

    The sequence number the next record will use.

    Cipher::traffic

    fn Cipher::traffic(self : Cipher) -> Traffic

    The traffic keys in force.

    Kind

    pub(all) enum Kind {
    ChangeCipherSpec
    Alert
    Handshake
    ApplicationData
    Ack
    Other(Int)
    } derive(Eq,
    Debug
    )

    What a record carries (RFC 8446 §5).

    Other keeps a content type the RFC does not define rather than losing it, but the parser refuses one: an unknown type is an UnexpectedMessage, not something to skip past.

    Kind::code

    fn Kind::code(self : Kind) -> Int

    The octet this content type goes on the wire as.

    Kind::equal

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

    Kind::not_equal

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

    Kind::of

    fn Kind::of(code : Int) -> Kind?

    The content type an octet names, or None for one RFC 8446 §5 does not define — which the record parser turns into an alert rather than tolerating.

    Kind::to_repr

    Record

    pub(all) struct Record {
    kind : Kind
    fragment : Bytes
    } derive(Eq,
    Debug
    )

    One record's content type and fragment (RFC 8446 §5.1).

    For a TLSCiphertext the kind is the outer ApplicationData and the fragment is still sealed.

    Record::equal

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

    Record::not_equal

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

    Record::to_repr

    Suite

    pub(all) enum Suite {
    Aes128Gcm
    Aes256Gcm
    ChaCha20Poly1305
    } derive(Eq,
    Debug
    )

    A TLS 1.3 cipher suite (RFC 8446 §B.4): the AEAD and the hash, which fix the key length.

    Suite::aead

    fn Suite::aead(self : Suite, key : Bytes) -> &
    Aead

    The suite's AEAD over key. Aborts on a key of the wrong length: every key reaching it came from [traffic] at the suite's length.

    Suite::code

    fn Suite::code(self : Suite) -> Int

    The suite's code on the wire.

    Suite::digest

    The hash the suite's key schedule runs.

    Suite::equal

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

    Suite::key_size

    fn Suite::key_size(self : Suite) -> Int

    The AEAD key length.

    Suite::not_equal

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

    Suite::of

    fn Suite::of(code : Int) -> Suite?

    The suite a code names, None for one this library does not implement.

    Suite::to_repr

    Traffic

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

    The AEAD key and IV one direction protects its records with (RFC 8446 §7.3), at TLS_AES_128_GCM_SHA256's lengths.

    Traffic::equal

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

    Traffic::not_equal

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

    Traffic::to_repr

    decode

    fn decode(view : BytesView) -> (Record, Int)? raise
    Alert

    Read one TLSPlaintext record (RFC 8446 §5.1) and how many octets it consumed, or None while the view is short of a whole record.

    decode_encrypted

    fn decode_encrypted(view : BytesView) -> (Record, Int)? raise
    Alert

    Read one TLSCiphertext record (RFC 8446 §5.2) and how many octets it consumed.

    The fragment is the still-sealed encrypted_record, so the ceiling is the larger §5.2 one.

    encode

    fn encode(kind : Kind, fragment : BytesView) -> Bytes raise
    Alert

    Frame fragment as a TLSPlaintext record (RFC 8446 §5.1).

    legacy_record_version is 0x0303 whatever the negotiated version is; a receiver ignores it.
    let header : Int

    The record header: content type, legacy_record_version, and a 16-bit length.

    inner

    fn inner(kind : Kind, content : BytesView, padding? : Int) -> Bytes

    A TLSInnerPlaintext (RFC 8446 §5.2): the content, then the content type the outer header is hiding, then padding zero octets.

    The padding is §5.4's length-hiding knob — a record's length leaks how much was said, and zero octets after the type cost only bandwidth.

    iv_size

    let iv_size : Int

    The IV length AEAD_AES_128_GCM fixes (RFC 5116 §5.1).

    key_size

    let key_size : Int

    The key length TLS_AES_128_GCM_SHA256 fixes: AES-128.

    max_encrypted

    let max_encrypted : Int

    The largest TLSCiphertext encrypted_record (RFC 8446 §5.2): 2^14 + 256 — the fragment limit plus the room the inner content type, the padding and the AEAD tag need.

    max_fragment

    let max_fragment : Int

    The largest TLSPlaintext fragment (RFC 8446 §5.1): 2^14 octets.

    The RFC fixes it, so it is a constant and not a parameter: a record past it is not a record.

    nonce

    fn nonce(iv : BytesView, seq : UInt64) -> Bytes

    The per-record AEAD nonce (RFC 8446 §5.3): the 64-bit sequence number, big- endian and left-padded with zeros to the IV's length, XORed with the static write IV.

    The IV never changes, so the sequence number alone is what keeps two records under one key from ever sharing a nonce.

    tag_size

    let tag_size : Int

    The AEAD tag length AEAD_AES_128_GCM fixes.

    traffic

    fn traffic(secret : BytesView, digest? :
    Digest
    , prefix? : BytesView, len? : Int) -> Traffic

    Derive a direction's record-protection key and IV from its traffic secret (RFC 8446 §7.3).

    QUIC expands the same secret under "quic key"/"quic iv" and adds a header-protection key (RFC 9001 §5.1); over TCP the labels are bare and there is no header to protect.

    unpad

    fn unpad(plaintext : BytesView) -> (Kind, Bytes) raise
    Alert

    Split a TLSInnerPlaintext back into its content type and content (RFC 8446 §5.2).

    Scan back over the zero padding to the last non-zero octet, which is the type. An inner plaintext of nothing but zeros names no type at all, which §5.2 makes an UnexpectedMessage rather than an empty record.

    Source Files