Skip to content

v0.2.0

Latest

Choose a tag to compare

@cslashm cslashm released this 29 Jul 16:15

[0.2.0] - 2026-07-29

The second pre-1.0 cut. Highlights: the shared krypteia-tessera hash
crate (Keccak / SHA-2 now live in exactly one place), a deliberate
three-tier quantica API (facade / hazmat / internal), pre-hash
signatures (HashML-DSA / HashSLH-DSA), the SLH-DSA SHA-2 parameter sets,
conformant external SLH-DSA sign/verify, and the first crates.io
publication of the C-ABI (*_ffi) and WebAssembly (*_wasm) bindings.
Embedded: the memory self-alloc allocator now builds on no-CAS targets
(Cortex-M0, ESP32-C3). The public Rust API remains pre-1.0 and
semver-unlocked.

Changed

  • Project hosting migrated from Codeberg to GitHub. Codeberg amended
    its Terms of Service (effective 2026-07-22) to bar projects "that
    consist predominantly of code written by generative AI tools".
    Krypteia is developed in tandem with an AI assistant and documents
    that fact — CONTRIBUTING.md mandates a Co-Authored-By: trailer
    naming the model and context window on every AI-assisted commit,
    precisely so the Git history stays an auditable record of which tool
    produced which line. Rather than obscure that trail or argue the
    threshold, the project moved: the canonical repository is now
    https://github.com/cslashm/krypteia, CI runs on GitHub Actions
    (.github/workflows/, ported from .forgejo/), and the documentation
    pack is served from GitHub Pages. The doc domain
    (https://krypteia-rs.dev) is unchanged. The Codeberg repository is
    archived read-only so the links recorded in the published v0.1 crate
    metadata keep resolving.
  • quantica API re-tiered before the v0.2 publish (breaking vs
    0.1, allowed on the pre-1.0 line and reserved by the v0.1 README).
    The public surface now has three deliberate tiers:
    • Facade (guaranteed): the typed MlKem/MlDsa/SlhDsa entry
      points, key/signature wrappers, params, rng, prehash, secret.
    • hazmat (new feature, off by default): the expert building-block
      tier — byte encodings, samplers, Decompose/hints, the SLH-DSA
      address/tweakable-hash surface, the raw-slice algorithm facades
      (ml_kem::kem, ml_dsa::dsa, slh_dsa::slh) and the deterministic
      *_internal CAVP entries. No stability, misuse-resistance or
      side-channel promise on this tier — it may change in any release.

      The KAT suites that drive the deterministic entries
      (ml_kem_kat, wycheproof, kat_extra) and krypteia-quantica-ffi opt in
      explicitly.
    • Internal (pub(crate)): ntt, the unauthenticated kpke
      component, WOTS+/XMSS/FORS/hypertree, the masking/shuffling
      countermeasures and the SHA-3 shims — no longer reachable, no longer
      a compatibility surface. This also removes the accidental exposure
      asymmetry between the ML-KEM and ML-DSA countermeasure modules.
  • arcana assumes its public arithmetic layer (additive): the
    SEC1 helpers are lifted to ecc::{compress_pubkey, decompress_pubkey}
    (both validate on-curve internally), and ecc::field / ecc::curve /
    rsa::bigint are now documented as a deliberate expert tier with an
    explicit contract — CT split (scalar_mul_point and point_double
    straight-line/CT; point_add/double_scalar_mul variable-time,
    public values only),
    the no-validation rule on raw point ops, and the variable-time warning
    on BigInt. Stability on this layer is best-effort pre-1.0.

Added

  • arcana::ecc::ed448 — full Ed448 (RFC 8032 §5.2). Edwards448
    "Goldilocks" over GF(2⁴⁴⁸−2²²⁴−1), cofactor 4, SHAKE256-based hashing,
    57-byte keys and 114-byte signatures. All three modes — pure Ed448,
    Ed448ctx (context string) and Ed448ph (prehash) — verified byte-exact
    against the RFC 8032 §7.4 / §7.5 test vectors. The secret-dependent
    scalar multiplication is constant-time from the first commit
    (ct_select double-and-add, mirroring the Ed25519 path). Closes the
    v0.1 Ed448-stub known limitation.
  • krypteia-tessera — new shared hash crate. no_std + alloc,
    zero runtime dependencies; the single home for every hash in the
    workspace. SHA-2 (224/256/384/512/512-224/512-256), SHA-3
    (224/256/384/512), SHAKE128/256, cSHAKE128/256, MGF1, SHA-1,
    RIPEMD-160 and BLAKE2b/2s, behind two minimal traits — Digest
    (OUTPUT_LEN / BLOCK_LEN) and Xof. Every family is KAT-validated;
    hash compression is deliberately not constant-time (public-data inputs;
    secret-dependent comparison / zeroization stays in silentops).
  • SLH-DSA SHA-2 parameter sets (FIPS 205 §11.2). Six new parameter
    sets — Sha2_128s/f, Sha2_192s/f, Sha2_256s/f — alongside the
    existing SHAKE sets, behind a TweakableHash abstraction on the
    Params trait (ShakeFamily / Sha2Family). The SHA-2 instantiation
    uses SHA-256 for F/PRF and the compressed 22-byte ADRSᶜ, with SHA-256
    at category 1 and SHA-512 at categories 3/5 for H_msg/PRF_msg/T_ℓ/H
    (FIPS 205 §11.2). Byte-exact against the NIST ACVP keyGen/sigGen/sigVer
    SHA-2 vectors.
  • Pre-hash signatures — HashML-DSA & HashSLH-DSA. New
    sign_prehash / verify_prehash on both schemes (FIPS 204 Alg 4/5,
    FIPS 205 Alg 23/25) with a shared PreHash selector covering the full
    approved set (SHA-224/256/384/512, SHA-512/224, SHA-512/256,
    SHA3-224/256/384/512, SHAKE128 → 256 bits, SHAKE256 → 512 bits). The
    OID DER byte strings are verified against NIST.FIPS.204 §5.4 /
    Algorithm 4 (full 11-byte TLV). Validated byte-exact against the ACVP
    pre-hash sigGen/sigVer groups, driven through the public typed API.
  • C-ABI and WebAssembly bindings published on crates.io, under the
    krypteia-* namespace like every other published crate:
    krypteia-quantica-ffi, krypteia-arcana-ffi, krypteia-tessera-ffi
    (staticlib + cdylib + hand-written C headers) and
    krypteia-quantica-wasm, krypteia-arcana-wasm, krypteia-tessera-wasm
    (wasm-bindgen) flip from publish = false to published, each exercised by
    a runtime harness (the C test_*.c suites via tools/ffi-tests.sh, and
    the Node wasm-bindgen tests). Their directories keep the bare names
    (quantica_ffi/) and each pins [lib] name to the bare form, so the built
    artefacts remain libquantica_ffi.{a,so} and the C headers, link recipes
    and wasm-pack invocations are unchanged. As pre-1.0 crates their
    ABI/JS surface may still change in a minor release.

Changed

  • BREAKING — SLH-DSA public sign / verify now implement the FIPS 205
    external algorithm.
    They take a context string ctx and apply the
    domain-separation wrap M′ = 0x00 ‖ len(ctx) ‖ ctx ‖ M before the
    internal algorithm, so signatures interoperate with conformant external
    SLH-DSA verifiers; previously they signed the raw message via the
    internal algorithm (non-conformant). This changes the signature bytes
    for a given (key, message) — a deliberate conformance break, acceptable
    pre-1.0 (like the 256f fix). Pass ctx = b"" for the no-context case.
    ML-DSA already exposed the external ctx path. Adds
    SlhDsaError::ContextTooLong (raised when |ctx| > 255).
  • BREAKING — the FIPS internal functions are now crate-private.
    ML-DSA dsa::{keygen,sign,verify}_internal and SLH-DSA
    slh::slh_{keygen,sign,verify}_internal are pub(crate), and the
    redundant MlDsa / SlhDsa *_internal inherent methods are removed:
    the only public way to sign is the conformant external path. The
    internal functions bypass domain separation and were a public footgun;
    a designed low-level / custom-M′ wrapper may be re-introduced on real
    demand (roadmap). ML-KEM internals are unchanged (a KEM has no M′/ctx
    layer). The ACVP internal-interface vectors moved to in-crate
    #[cfg(test)] KATs (ml_dsa::acvp_internal, slh_dsa::acvp_internal).
  • arcana hash functions relocated into krypteia-tessera.
    arcana::hash is now a thin façade that re-exports the shared types;
    the per-algorithm module paths (arcana::hash::sha256::Sha256, …) and
    arcana's public Hasher / Xof traits are unchanged, bridged to the
    crate by two blanket impls (impl<T: tessera::Digest> Hasher for T,
    impl<T: tessera::Xof> Xof for T). No algorithm or behavioural change:
    the implementations were moved verbatim and re-validated against the
    unchanged NIST CAVP hash KATs, NIST ACVP and Google Wycheproof corpora
    (arcana lib 291/291, CAVP 11/11, ACVP 16/16, Wycheproof 6/6). Removes
    ~2.3k lines of SHA-2 / SHA-3 duplication on the arcana side.
  • quantica Keccak relocated into krypteia-tessera too.
    quantica::sha3 is now a shim re-exporting tessera::sha3::KeccakState
    (+ the FIPS 202 rate constants); the ML-KEM / ML-DSA / SLH-DSA sha3
    wrappers and call sites are unchanged. With this, Keccak exists in
    exactly one place
    in the workspace. Both cores are plain,
    data-oblivious Keccak-f[1600], so no side-channel property changes; an
    A/B throughput harness (quantica_bench keccak_bench) measured the two
    at parity. Byte-exact: ML-KEM / ML-DSA KAT, SLH-DSA KAT (incl 256f),
    and the M0 thumbv6m no_std build all green on the shared core.

Removed

  • Third-party kat_MLKEM_*.rsp vectors (from
    post-quantum-cryptography/KAT) and their tests. Their ML-KEM key
    derivation diverges from the authoritative NIST ACVP vectors — our
    ACVP-validated keygen_internal reproduces their pk with neither
    (d, z) nor (z, d) — so they are not a reliable FIPS 203 oracle
    (likely a pre-final Kyber round-3 derivation, missing the FIPS 203
    G(d ‖ k) domain byte). ML-KEM conformance remains fully covered by
    the NIST ACVP suite and Wycheproof. Full rationale is documented in
    quantica/tests/kat_extra.rs.

Fixed

  • SLH-DSA-SHAKE-256f produced non-conformant signatures. The
    idx_tree mask computed 1u64 << (H - H'), which overflows for the
    256f parameter set (H - H' = 64): in release builds the shift wraps
    to 1 << 0, forcing idx_tree to 0 and yielding signatures that are
    self-consistent but non-conformant to FIPS 205. Fixed at both the sign
    and verify sites (quantica/src/slh_dsa/slh.rs); the previously
    #[ignore]-d 256f siggen/sigver KATs now pass. Only 256f was affected
    (every other parameter set has H - H' ≤ 63).
  • memory self-alloc now builds on no-CAS embedded targets. The
    self-alloc spin-lock used AtomicBool::compare_exchange_weak, which
    core does not expose on targets without hardware CAS, so
    krypteia-memory --features self-alloc failed to compile (E0599) on
    Cortex-M0 (thumbv6m) and the ESP32-C3-class RV32 imc — the M0/ESP32-C3
    support advertised since v0.1. Replaced with a per-target interrupt-masking
    critical section (ARM PRIMASK, RISC-V mstatus.MIE); CAS-capable targets
    keep the lock-free spin-lock. Branch-free asm, no public API change.