COOLJAPAN
← All posts

OxiCrypto 0.2.1 Released — ML-DSA-87's Stack Footprint Cut to 2 MiB, Genuine No-Std Core Builds, and a Truncated-HMAC Panic Fixed

OxiCrypto 0.2.1 measures ML-DSA-87's worker-thread stack down to 2 MiB from a hardcoded 8 MiB, ships a default-on alloc feature for genuine no_std core-only builds, adds a PQ-to-AEAD hybrid encryption test, and fixes a truncated-HMAC panic — the sovereign Pure Rust cryptography layer for the COOLJAPAN ecosystem.

release oxicrypto pure-rust cooljapan noffi cryptography no-std post-quantum security

A post-quantum signature scheme that used to demand a guessed 8 MiB of stack “to be safe” now runs on a measured 2 MiB — and two of its sibling crates just got a genuinely heap-free build to go with it.

Today we released OxiCrypto 0.2.1 — a release built around measuring instead of guessing. A new stack_safe module pins ML-DSA-87’s worker-thread stack requirement at 2 MiB instead of a hardcoded 8 MiB. A new default-on alloc Cargo feature makes oxicrypto-core and oxicrypto-hash genuinely buildable against bare core. A new integration test wires a full PQ-KEM → HKDF → AEAD hybrid encryption round trip through the facade crate. And a security fix closes a denial-of-service panic in truncated HMAC verification.

No OpenSSL. No BoringSSL. No ring, no aws-lc-rs in the default closure. No FFI. No -sys crates. And as of 0.2.1, no heap allocator required either, if you don’t want one: oxicrypto-core and oxicrypto-hash now build with cargo build --no-default-features and link only core, no extern crate alloc anywhere in the tree. The rest of the workspace keeps compiling to a single static binary with zero apt-get install and no C toolchain, exactly as it did in 0.2.0.

Why OxiCrypto 0.2.1 is a game changer

Even a Pure Rust crypto stack accumulates its own quiet pain points:

OxiCrypto 0.2.1 ends all of that.

Technical Deep Dive: stack safety, alloc-free builds, and the hybrid path

  1. Stack safety — oxicrypto-pq::stack_safe. run_on_large_stack<F: FnOnce() -> T + Send, T: Send>(f: F) -> Result<T, CryptoError> spawns f on a scoped worker thread sized to OXICRYPTO_MLDSA_STACK via thread::Builder::stack_size + spawn_scoped. The three mldsa87_*_stack_safe helpers wrap keygen/sign/verify around it and exchange owned byte vectors — a 32-byte seed, a 2592-byte verifying key, a 4627-byte signature — rather than the typed SigningKey87/VerifyingKey87/Signature87 structs, so the large ml-dsa working set (the expand_a NTT matrix plus the y/w/cs1/cs2/z/ct0 vectors) never has to cross back over the thread boundary. crates/oxicrypto-pq/tests/stack_safe.rs proves the round trip on the default libtest worker-thread stack — no manual high-stack wrapper needed by the caller.
  2. Alloc-free surface — oxicrypto-core / oxicrypto-hash. The new alloc feature (default-on) gates Vec/String/Box re-exports, SecretVec, the KeyGenerator trait, and the *_to_vec/seal_*/open_*/agree_to_vec/mac_to_vec default trait methods in oxicrypto-core; in oxicrypto-hash it gates hash_to_vec, blake3_xof, parallel_hash*_xof, the streaming HashBuilder, and the SHAKE/cSHAKE/TupleHash XOF helpers. What survives --no-default-features: the inherent hash_fixed::<N>() methods and Hash::hash/hash_to_array::<N>(), exercised end to end in the new crates/oxicrypto-hash/tests/no_alloc.rs over SHA-256, SHA-512, and BLAKE3.
  3. The hybrid encryption path — the facade crate. oxicrypto is the only crate that sees oxicrypto-pq, oxicrypto-kdf, and oxicrypto-aead simultaneously, so pq_hybrid_encryption.rs lives there: encapsulate with a Kem implementation, run HKDF-SHA-256 extract-then-expand (hkdf_sha256_extract / hkdf_sha256_expand, RFC 5869) over the shared secret with a domain-separated salt and info string, then seal/open with aead_impl(AeadAlgo::Aes256Gcm). This closes three previously-deferred cross-crate TODOs (pq↔kdf, pq↔aead, aead↔pq) without inverting the dependency graph.
  4. AEAD internals + the MAC fix. Aes128Gcm/Aes256Gcm, ChaCha20Poly1305/XChaCha20Poly1305, Aes128GcmSiv/Aes256GcmSiv, OCB3, and the STREAM chunked-AEAD construction all move to aead 0.6’s AeadInOut trait generation, with nonce/tag construction going through Nonce::<C>::try_from/Tag::<C>::try_from instead of a length-panicking GenericArray::from_slice. Separately, oxicrypto-mac’s verify_truncated/mac_truncated now validate an inclusive 16..=digest_len range before slicing, closing the panic path.

Getting Started

cargo add oxicrypto
[dependencies]
oxicrypto = "0.2.1"

# Post-quantum primitives (off by default):
oxicrypto = { version = "0.2.1", features = ["pq-preview"] }

Hashing:

use oxicrypto::{blake3, sha256, sha512};

let digest = sha256(b"hello world");
let digest = sha512(b"hello world");
let digest = blake3(b"hello world");

Authenticated encryption, keys and nonce drawn from the crate’s own RNG:

use oxicrypto::{aead_impl, random_bytes, random_nonce, AeadAlgo};

let aead = aead_impl(AeadAlgo::Aes256Gcm);
let key = random_bytes(32)?;       // 32 bytes for AES-256-GCM
let nonce = random_nonce::<12>()?; // 12-byte nonce; must never repeat for a given key

let ct = aead.seal_to_vec(&key, &nonce, b"aad", b"plaintext")?;
let pt = aead.open_to_vec(&key, &nonce, b"aad", &ct)?;
assert_eq!(pt, b"plaintext");

What’s New in 0.2.1

Tips

This is the foundation

OxiCrypto depends on nothing — it’s the foundation layer the rest of the COOLJAPAN cryptography stack stands on. It is already depended on by:

Around it sit sibling Pure Rust projects removing their own native dependency from the floor of the ecosystem — OxiArc (compression, replacing zip/flate2/zstd), OxiBLAS, OxiFFT, and OxiZ (the Pure Rust SMT solver).

Repository: https://github.com/cool-japan/oxicrypto

Star the repo if you want a post-quantum signature scheme whose stack use is measured instead of guessed, and cryptographic primitives that build with no heap at all when you need them to.

The era of shipping a round, over-conservative stack-size number because nobody measured the real one is over. Pure Rust cryptography is here — measured, sovereign, and FFI-free.

KitaSan at COOLJAPAN OÜ July 18, 2026

↑ Back to all posts