moonbitstack/mooncrypt/spec does not have a README file

    Aead

    pub(open) trait Aead {
    fn seal(Self, nonce~ : BytesView, plain~ : BytesView, aad~ : BytesView) -> Bytes
    fn open(Self, nonce~ : BytesView, cipher~ : BytesView, aad~ : BytesView) -> Bytes raise Broken
    }

    Authenticated encryption with associated data.

    [open] raises rather than returning an option, because "the tag did not match" is the one outcome a caller must not silently treat as empty plaintext. cipher~ is the ciphertext with its tag appended, the layout every AEAD construction in use writes.

    Block

    pub(open) trait Block {
    fn block(Self) -> Int
    fn encrypt(Self, BytesView) -> Bytes
    fn decrypt(Self, BytesView) -> Bytes
    }

    A block cipher: a keyed permutation of a fixed-size block.

    It is the thing a mode of operation is built on, which is why it is a contract rather than a concrete type — GCM is defined for any 128-bit block cipher, so it takes one of these and SM4-GCM costs nothing more than an SM4 that implements it.

    Both directions are here although a counter mode needs only [encrypt]: a permutation that cannot be inverted is not a block cipher, and a mode such as CBC needs the inverse.

    Hash

    pub(open) trait Hash {
    fn write(Self, BytesView) -> Unit
    fn finish(Self) -> Bytes
    fn reset(Self) -> Unit
    fn size(Self) -> Int
    fn block(Self) -> Int
    }

    A cryptographic hash, as a state you feed and then read.

    Five methods and no more, because everything mooncrypt builds on top of a hash needs exactly these: HMAC needs [block] to size its pads, HKDF and PBKDF2 need [size] to count output blocks, and every one-shot needs [write] then [finish]. A third-party digest becomes usable throughout the library the moment it implements this.

    [finish] does not consume the state, so a caller may keep writing and read again; [reset] returns it to the initial vector, which is how HMAC reuses one allocation for both passes.

    Mac

    pub(open) trait Mac {
    fn write(Self, BytesView) -> Unit
    fn finish(Self) -> Bytes
    fn size(Self) -> Int
    }

    A message authentication code: a hash that a key has been mixed into.

    It is deliberately not a [Hash] — a MAC has no block size worth exposing and must never be used where an unkeyed digest is expected.

    Signer

    pub(open) trait Signer {
    fn sign(Self, BytesView) -> Bytes
    }

    Something that holds a private key and can sign with it.

    Verifier

    pub(open) trait Verifier {
    fn verify(Self, BytesView, BytesView) -> Bool
    }

    Something that holds a public key and can check a signature.

    It answers Bool and never raises: a bad signature is an expected answer, not an exceptional one, and raising here invites a caller to swallow the error and carry on.

    Broken

    pub(all) suberror Broken {
    Tag
    Size(want~ : Int, got~ : Int)
    Weak
    } derive(Eq,
    Debug
    )

    A ciphertext that did not authenticate, or a key that cannot be used.

    Broken::equal

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

    Broken::not_equal

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

    Broken::to_repr

    digest

    fn digest(h : &Hash, msg : BytesView) -> Bytes

    One whole message through a hash, in one call.

    It resets first, so passing a state that has already been used is not a silent corruption of the result.
    fn eq(a : BytesView, b : BytesView) -> Bool

    Compare two byte strings in time that does not depend on their contents.

    The obvious loop returns at the first differing byte, and the time it took tells an attacker how long a prefix they guessed right — enough to recover a tag or a token byte by byte across many requests. This one reads both strings to the end and folds the differences together.

    Length is not treated as a secret: unequal lengths answer immediately, which is what every library does, because the length is visible on the wire anyway.

    This is the only comparison mooncrypt offers. Use it for tags, digests and anything else derived from a key.

    wipe

    fn wipe(buf : FixedArray[Byte]) -> Unit

    Overwrite a buffer with zeroes.

    Key material lives longer than the code that used it: a scratch array stays in whatever memory the runtime hands out next until something else writes over it. Erasing at the end of the operation shortens that window. Under a garbage collector it is a best-effort measure, not a guarantee — a copy the collector moved is out of reach — which is why keys are held in [FixedArray] here rather than in [Bytes], where they could not be erased at all.

    Source Files