// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
///|
/// 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.
pub(open) trait Hash {
/// Absorb more message.
fn write(Self, BytesView) -> Unit
/// The digest of everything written so far.
fn finish(Self) -> Bytes
/// Forget everything written, as if newly constructed.
fn reset(Self) -> Unit
/// The digest length in bytes.
fn size(Self) -> Int
/// The compression block length in bytes — what HMAC pads its key to.
fn block(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.
pub(open) trait Mac {
fn write(Self, BytesView) -> Unit
fn finish(Self) -> Bytes
fn size(Self) -> Int
}
///|
/// 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.
pub(open) trait Block {
/// The block length in bytes.
fn block(Self) -> Int
/// Encrypt one block. The input must be exactly [`block`] bytes.
fn encrypt(Self, BytesView) -> Bytes
/// Decrypt one block. The input must be exactly [`block`] bytes.
fn decrypt(Self, BytesView) -> Bytes
}
///|
/// 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.
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
}
///|
/// Something that holds a private key and can sign with it.
pub(open) trait Signer {
fn sign(Self, BytesView) -> Bytes
}
///|
/// 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.
pub(open) trait Verifier {
fn verify(Self, BytesView, BytesView) -> Bool
}
///|
/// A ciphertext that did not authenticate, or a key that cannot be used.
pub(all) suberror Broken {
/// The authentication tag does not match: the ciphertext, the associated
/// data, the key or the nonce is not what sealed it.
Tag
/// A nonce, key or ciphertext of the wrong length, with what was expected.
Size(want~ : Int, got~ : Int)
/// The right number of bytes, but a value the algorithm must refuse: a
/// low-order Curve25519 point, whose shared secret is all zeroes and so was
/// chosen by the peer rather than agreed with it.
Weak
} derive(Eq, Debug)
///|
pub extend Broken with Eq::{equal, not_equal}
///|
pub extend Broken with Debug::{to_repr}
///|
/// 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.
pub fn eq(a : BytesView, b : BytesView) -> Bool {
if a.length() != b.length() {
return false
}
let mut diff = 0
for i in 0.. Unit {
for i in 0.. Bytes {
h.reset()
h.write(msg)
h.finish()
}