moonbitstack/moontls/hs does not have a README file

    Choice

    pub(all) enum Choice {
    Chosen(group~ : Int, key~ : Bytes, scheme~ : Int)
    Retry(Int)
    } derive(Eq,
    Debug
    )

    What a server settled on after reading a ClientHello.

    Choice::equal

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

    Choice::not_equal

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

    Choice::to_repr

    Client

    pub(all) enum Client {
    WaitServerHello
    WaitEncryptedExtensions
    WaitCertOrRequest
    WaitCert
    WaitCertVerify
    WaitFinished
    Connected
    } derive(Eq,
    Debug
    )

    A client's handshake state (RFC 8446 Appendix A.1), after the ClientHello has been sent.

    Sending the ClientHello is the client's own action, not a received message, so START is not a state here — a client that has not sent one has no handshake to be in.

    Client::equal

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

    Client::new

    fn Client::new() -> Client

    The state a client is in having just sent its ClientHello.

    Client::not_equal

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

    Client::recv

    The next client state on receiving a message of kind (RFC 8446 A.1).

    A message that does not belong in the current state raises UnexpectedMessage (§6.2) — which is what the peer is owed, rather than being ignored into an ambiguous state.

    Client::to_repr

    Handshake

    pub struct Handshake {
    state : Server
    transcript :
    Transcript

    buffered : Bytes
    hello : Bytes
    }

    A server-side handshake in progress.

    It drives the state machine over a byte stream: [Handshake::feed] appends received octets, splits off every complete message, adds each to the running transcript and advances the state, buffering a trailing partial message for the next feed. The server's own flight folds in through [Handshake::sent], so the transcript stays in message order.

    A pure core: no keys and no socket. Whoever has those wraps it.

    Handshake::feed

    Feed received octets: process every complete handshake message now available — add it to the transcript and advance the state — buffering any trailing partial message. Answers the message types processed, in order.

    Handshake::hello

    fn Handshake::hello(self : Handshake) -> Bytes

    The raw ClientHello the handshake received, empty until one arrives.

    A server needs it to pull the client's key_share and run the ECDHE the handshake secrets come from.

    Handshake::is_connected

    fn Handshake::is_connected(self : Handshake) -> Bool

    Whether the handshake has completed.

    Handshake::negotiate

    fn Handshake::negotiate(self : Handshake, groups? : ArrayView[Int], schemes? : ArrayView[Int]) -> Choice raise
    Alert

    Negotiate the ClientHello this handshake received.

    Raises DecodeError before one has arrived at all, which is what an empty message decodes to.

    Handshake::new

    A fresh handshake, awaiting the ClientHello.

    Handshake::sent

    fn Handshake::sent(self : Handshake, message : BytesView) -> Unit

    Fold a message the server sends — ServerHello, EncryptedExtensions, Certificate and the rest — into the transcript, keeping message order.

    Handshake::sent_flight

    fn Handshake::sent_flight(self : Handshake, client_cert? : Bool) -> Unit raise
    Alert

    Advance the state past the server's own flight, waiting for a client certificate when one was asked for.

    Handshake::state

    fn Handshake::state(self : Handshake) -> Server

    The state the handshake is in.

    Handshake::transcript

    fn Handshake::transcript(self : Handshake) -> Bytes

    The transcript hash over every message seen so far, in order.

    Server

    pub(all) enum Server {
    Start
    RecvdClientHello
    WaitCert
    WaitCertVerify
    WaitFinished
    Connected
    } derive(Eq,
    Debug
    )

    A server's handshake state (RFC 8446 Appendix A.2).

    Server::equal

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

    Server::new

    fn Server::new() -> Server

    The state a server is in awaiting the ClientHello.

    Server::not_equal

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

    Server::recv

    The next server state on receiving a message of kind (RFC 8446 A.2): the ClientHello that opens the handshake, then the client's second flight.

    Server::sent_flight

    fn Server::sent_flight(self : Server, client_cert? : Bool) -> Server raise
    Alert

    The server's own move after the ClientHello: it negotiates and sends its whole flight, then waits for the client's Certificate when one was asked for, or straight for the client's Finished.

    Sending the flight in any other state is this endpoint's bug and not the peer's, so it raises InternalError rather than an alert that blames the other side.

    Server::to_repr

    groups

    let groups : Array[Int]

    The named groups this build can run a key exchange for.

    x25519 alone: offering a group the key exchange cannot honour would be a lie the handshake discovers too late. It is a parameter on [negotiate] rather than a fixed list, so a build with more groups says so.

    is_retry

    fn is_retry(sh :
    Server
    ) -> Bool

    Whether a ServerHello is really a HelloRetryRequest.

    negotiate

    Negotiate a decoded ClientHello (RFC 8446 §4.1.1).

    Checks it offers TLS 1.3, picks the first group and signature scheme it lists that this build runs, and takes its key share for the chosen group. Raises the alert §6 names for each way that fails — a missing mandatory extension, a client that does not speak 1.3, nothing in common, or a key share contradicting the rest of the message — and answers Retry when the chosen group is one the client offered but sent no share for.

    groups and schemes are what this endpoint can do; they default to what this build implements.

    negotiate_message

    fn negotiate_message(message : BytesView, groups? : ArrayView[Int], schemes? : ArrayView[Int]) -> Choice raise
    Alert

    Negotiate a raw ClientHello handshake message: unframe it, decode it, and run [negotiate].

    A message that is not a well-formed ClientHello raises DecodeError (§6.2) rather than vanishing into a None.

    read_retry_share

    fn read_retry_share(view : BytesView) -> Int?

    The group such a payload names, or None if it is not two octets.

    retry

    fn retry(session_id : BytesView, suite : Int, group : Int) ->
    Server

    A HelloRetryRequest asking the client to come back with a key share for group (RFC 8446 §4.1.4): the sentinel random, the client's session_id echoed back untouched, the chosen suite, supported_versions, and the group.

    It encodes through @msg.server_hello like any other ServerHello.

    retry_group

    fn retry_group(sh :
    Server
    ) -> Int?

    The group a HelloRetryRequest asks for a share of, or None if the message is an ordinary ServerHello or carries no key_share.

    retry_random

    let retry_random : Bytes

    The ServerHello.random that marks a HelloRetryRequest (RFC 8446 §4.1.3): the SHA-256 of "HelloRetryRequest".

    A HelloRetryRequest travels as a ServerHello — same type, same framing — distinguished only by this sentinel, so a receiver must compare against it before reading the message as a real ServerHello. One that does not will try a key exchange against a key_share carrying no key.

    retry_share

    fn retry_share(group : Int) -> Bytes

    A HelloRetryRequest's key_share payload (RFC 8446 §4.2.8): the selected group alone. There is no key — asking for one is the whole point.

    schemes

    let schemes : Array[Int]

    The signature schemes this build can verify a CertificateVerify under.

    server_hello

    fn server_hello(random : BytesView, session_id : BytesView, suite : Int, extensions? : ArrayView[
    Ext
    ]) ->
    Server

    A ServerHello answering a negotiated ClientHello (RFC 8446 §4.1.3).

    The supported_versions extension naming TLS 1.3 is prepended, because §4.1.3 puts the negotiated version there rather than in the message's frozen legacy_version.

    Source Files