Public API Reference

Side-channel claim limit. Nothing on this page claims constant-time behaviour or side-channel resistance for any target: the side-channel harness covers nothing (finding A48) and no measurement exists. The Rust and C cores are designed for constant time and unmeasured; managed runtimes (JIT, GC, bounds checks) make no timing claim. Where this page names constant-time or side-channel techniques, in prose or in code, they are design intent or recommendations, not measured properties.

Release line: 1.0.0-rc.7 (release candidate; no package is published on a public registry yet) Licence: Apache-2.0 Source of truth: documents/public-release/api-contract.md (contract version 1.0.0-draft.5) and the release catalog, documents/public-release/catalog.json, in the source repository. This page summarises them; where they and this page differ, they win.

What the public API is

The release catalog lists 49 profiles in 16 algorithm families, 45 of them mandatory, across 10 targets (Rust, C, Swift, Go, Python, TypeScript, Kotlin, Java, C#, WASM). A profile is one algorithm, one parameter set, one wire encoding and one operation set, named by its catalog identifier (ml-kem-768, falcon-512.r3-padded, …).

The public entry surface for the post-quantum profiles is a facade over the validated algorithm implementations. At this revision the facade admits 37 profiles:

family profiles kind operations
Falcon (Round 3) falcon-512.r3-compressed, falcon-512.r3-padded, falcon-1024.r3-compressed, falcon-1024.r3-padded signature keygen, sign, verify
ML-KEM (FIPS 203) ml-kem-512, ml-kem-768, ml-kem-1024 KEM keygen, encapsulate, decapsulate
ML-DSA (FIPS 204) ml-dsa-44, ml-dsa-65, ml-dsa-87 signature keygen, sign, verify, with a context string
SLH-DSA (FIPS 205) slh-dsa-sha2-{128,192,256}{s,f}, slh-dsa-shake-{128,192,256}{s,f} (12) signature keygen, sign, verify, with a context string
SMAUG-T (KpqC) smaug-t.v1_2_0.mode1, .mode3, .mode5 KEM keygen, encapsulate, decapsulate
HAETAE (KpqC) haetae-2, haetae-3, haetae-5 signature keygen, sign, verify, with a context string
AIMer v3 (KpqC) aimer-{128,192,256}{s,f} (6) signature keygen, sign, verify, with a context string
NTRU+ (KpqC) ntru-plus-768, ntru-plus-864, ntru-plus-1152 KEM keygen, encapsulate, decapsulate (decapsulation can fail)

The braces are shorthand in this table only; each identifier is its own profile with its own module and types.

Which targets carry a facade:

target facade per-algorithm API
Rust metamui-crypto crate (metamui_crypto::<profile module>) the metamui-<algorithm> crates, no stability promise from the contract
TypeScript / WASM @metamui/crypto @metamui/<algorithm>
Python metamui_crypto.facade the metamui_crypto.<module> modules
Swift MetaMUICrypto umbrella product (since 2026-10-08) one SwiftPM product per module (MetaMUIFalcon, MetaMUIMLKem, …)
C none; per-algorithm API only is the decision for 1.0 CMake package metamui-crypto-c, targets metamui::<target>, headers under include/metamui/
Go, Java, Kotlin, C# none yet (planned after 1.0) per-algorithm modules: mlkem.New(mlkem.MLKEM768), new MLKem768(), MLKem768(), new MLKEM768()

The classical profiles (blake3, sha3-256, shake-256, keccak-256, aes-256-gcm, hkdf-sha256, ed25519, sr25519) are outside the facade in every target and are reached through each target’s per-algorithm modules; see the language pages linked at the end.

Rules every facade is held to

  1. Explicit, exact, non-substituting selection. There is no default profile and no “strongest available”. Identifiers resolve by exact, case-sensitive match; falcon512, FALCON-512.R3-PADDED and a family name such as ml-kem are refused with an unsupported profile error. Nothing is substituted.
  2. Validated types at every byte boundary. A constructor checks length, header and, where the standard defines one, structure (the FIPS 203 §7.2 modulus check on an encapsulation key, the §7.3 hash check on a decapsulation key); a value that exists is well-formed for its profile. Secret types are zeroized on drop and print no material.
  3. No boolean verify. Verification returns success or an invalid signature error that carries no reason.
  4. Context strings are required arguments. ML-DSA, SLH-DSA, HAETAE and AIMer sign and verify under a context of at most 255 bytes; the caller that wants none passes the empty string. A signature made under one context does not verify under another.
  5. Two rejection behaviours. ML-KEM and SMAUG-T reject implicitly: a well-sized but invalid ciphertext decapsulates to a pseudorandom secret, never an error. NTRU+ rejects explicitly with a decapsulation failed error and returns no secret. The two are not interchangeable.
  6. Raw standard encodings only. No length prefix, version tag or envelope is produced or parsed. For example ML-KEM-768: encapsulation key 1184 bytes, decapsulation key 2400, ciphertext 1088, shared secret 32; Falcon-512: public key 897, secret key 1281, padded signature exactly 666, compressed signature at most 752.
  7. Operating-system randomness. The easy entry points draw from the OS; Rust and the bindings that have a CSPRNG abstraction also take a caller RNG. No seeded, deterministic or known-answer entry point is public facade API.

The per-algorithm surfaces of C, Swift products, Go, Java/Kotlin and C# do not meet every rule above (typed selectors instead of profile strings, boolean verify, some seeded entry points exported); the contract records each deviation per target in its §12.4 and tracks them as follow-up work.

Error model

Rust (metamui_crypto::Error) and Python (metamui_crypto.facade) spell the same set:

Rust variant Python exception when
UnsupportedProfile { requested } UnsupportedProfileError identifier not admitted, or a family name
InvalidLength { what, expected, actual } InvalidLengthError a key, signature, ciphertext or context of the wrong length
InvalidHeader { what, expected, actual } InvalidHeaderError wrong Falcon header byte
MalformedKey { what } MalformedKeyError right length and header, but the bytes do not decode to a key
InvalidSignature InvalidSignatureError verification failed; no reason
SigningFailed { reason }, KeyGenerationFailed { reason }, EncapsulationFailed { reason } SigningFailedError, KeyGenerationFailedError, EncapsulationFailedError backend failure
EntropyUnavailable EntropyUnavailableError the OS randomness source refused a request
DecapsulationFailed DecapsulationFailedError NTRU+ explicit rejection only

Executed examples

Both blocks below were run on 2026-10-16 against this source tree at the 1.0.0-rc.7 revision (Rust: a consumer crate with a path dependency on metamui-crypto-rust/metamui-crypto, cargo run --release; Python: pip install metamui-crypto-python/ into a fresh Python 3.12 virtual environment, then python -I on the block). The runs are recorded in the source repository’s documents/public-release/evidence/229-documentation.md (2026-10-16 section).

Rust

use metamui_crypto::{falcon512, mldsa65, mlkem768, Profile};

fn main() -> Result<(), metamui_crypto::Error> {
    let mut rng = rand::rngs::OsRng;

    let kp = falcon512::generate_keypair(&mut rng)?;
    let sig = falcon512::sign_padded(&kp.secret_key, b"hello", &mut rng)?;
    falcon512::verify_padded(&kp.public_key, b"hello", &sig)?;

    let kem = mlkem768::generate_keypair(&mut rng)?;
    let (ct, ss) = mlkem768::encapsulate(&kem.encapsulation_key, &mut rng)?;
    assert_eq!(mlkem768::decapsulate(&kem.decapsulation_key, &ct)?, ss);

    let dsa = mldsa65::generate_keypair(&mut rng)?;
    let sig = mldsa65::sign(&dsa.secret_key, b"hello", b"my-protocol/v1", &mut rng)?;
    mldsa65::verify(&dsa.public_key, b"hello", b"my-protocol/v1", &sig)?;
    assert!(mldsa65::verify(&dsa.public_key, b"hello", b"", &sig).is_err()); // another context

    assert!(Profile::parse("haetae").is_err()); // a family is not a profile
    Ok(())
}

Python

from metamui_crypto.facade import profile

sig = profile("ml-dsa-65")
keys = sig.generate_keypair()
signature = sig.sign(keys.secret_key, b"message", context=b"my-app")
sig.verify(keys.public_key, b"message", signature, context=b"my-app")   # raises InvalidSignatureError on failure

kem = profile("ml-kem-768")
ek, dk = kem.generate_keypair()
ct, ss = kem.encapsulate(ek)
assert kem.decapsulate(dk, ct) == ss

The other languages’ examples are on the Quick Start page; each of those was run against a locally built candidate package on 2026-10-09.

What is not part of the public API

Per-language pages

Rust · Python · TypeScript · WASM · Swift · C · Go · Java · Kotlin · C#

Threat-model pages per algorithm: Threat Models.