fix(security): replace placeholder crypto with audited crate implementations#39
Merged
Merged
Conversation
…tations
The recon-wasm `security.rs` module shipped ~15 stub functions that
returned non-cryptographic placeholder values ("stub:...", always-false
verify, all-zero DRBG). Every primitive is now backed by a real,
well-known pure-Rust implementation from the RustCrypto ecosystem. No
primitive is hand-rolled. Public `#[wasm_bindgen]` signatures are
unchanged; only internals were swapped.
Primitive -> crate (pinned):
SHAKE3-512 (SHA-3 XOF) sha3 0.10.9 (Shake256, 64-byte squeeze)
BLAKE3 blake3 1.8.5
Argon2id argon2 0.5.3 (m=64MiB, t=3, p=4, PHC string)
ML-DSA-87 / Dilithium5 ml-dsa 0.0.4 (FIPS 204, pure Rust)
ML-KEM-1024 / Kyber1024 ml-kem 0.2.3 (FIPS 203, pure Rust)
XChaCha20-Poly1305 chacha20poly1305 0.10.1
HKDF (SHA3-512) hkdf 0.12.4 + sha3 0.10.9
ChaCha20-DRBG rand_chacha 0.3.1 (ChaCha20Rng)
hex (de)coding hex 0.4.3
CSPRNG seed getrandom 0.2 (js feature wasm-gated)
Pure-Rust ml-dsa/ml-kem are used instead of the C-backed pqcrypto-*
crates because this crate is a `wasm32-unknown-unknown` cdylib where
linking PQClean C is not viable; both are #![no_std] and wasm-friendly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
hyperpolymath
added a commit
that referenced
this pull request
May 16, 2026
## Problem `reconforth/mod.rs` declared `mod builtins;` and `pub use builtins::register_builtins`, and `vm.rs` calls `register_builtins(&mut vm)` from `VM::new()`, but `builtins.rs` was never committed. The `recon-wasm` crate did not compile (`E0583: file not found for module `builtins``). This also blocked verifying the crypto work in **PR #39**. ## Fix Restores the core ReconForth native vocabulary the VM + existing tests rely on: - arithmetic `+ - * / mod neg` - comparison `= != < > <= >=` - boolean `and or not true false` - stack `dup drop swap over rot nip depth` - control `call if ifelse` - validation `error! warn! suggest!` (symmetric with `VM::report_*`) - doc-reconciliation `count doc-type is-canonical` All words total/fallible — **no `unwrap`/`panic!`** (estate robustness policy). ## Verification - `cargo build`: clean (only pre-existing unrelated warnings) - `cargo test --lib`: **32/32 pass**, including the previously-failing `tests::test_reconforth_validation` Unblocks build-verification of PR #39 (crypto). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: hyperpolymath <hyperpolymath@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR replaces placeholder/stub cryptography in
recon-silly-ation/wasm-modules/src/security.rswith realimplementations. It requires human security review — and ideally
formal/cryptographic review — before it is marked ready or merged.
Left as a draft intentionally.
Background
security.rspreviously shipped ~15#[wasm_bindgen]functions whosebodies returned non-cryptographic placeholders:
format!("stub:...")digests, an Argon2 stub,
dilithium5_verifythat always returnedfalse, a ChaCha20-DRBG that returned all-zero bytes, etc. Anycaller relying on these for security guarantees had none.
What changed
Every primitive is now backed by a real, well-known pure-Rust
implementation from the RustCrypto ecosystem. No primitive is
hand-rolled. Public function signatures are unchanged — only internals
were swapped. The
JsValue-returning functions now delegate to plain*_implhelpers so the crypto is unit-testable on native targets.sha3 = "=0.10.9"Shake256, 64-byte squeezeblake3 = "=1.8.5"argon2 = "=0.5.3"ml-dsa = "=0.0.4"ml-kem = "=0.2.3"chacha20poly1305 = "=0.10.1"hkdf = "=0.12.4"+sha3rand_chacha = "=0.3.1"ChaCha20Rngseeded from caller seedhex = "=0.4.3"getrandom = "0.2"jsfeature wasm-gated for keygen/encap entropyuser_friendly_hash_namenow maps digest bytes to a fixed 256-wordlist (non-crypto helper; BLAKE3 fallback for non-hex input).
Why pure-Rust PQC instead of
pqcrypto-*The task suggested
pqcrypto-dilithium/pqcrypto-kyber. Those wrap thePQClean C reference code. This crate is a
cdylibtargetingwasm32-unknown-unknown(crate-type = ["cdylib"],wasm-bindgen),where linking PQClean C is not viable. The pure-Rust RustCrypto
ml-dsa(FIPS 204) andml-kem(FIPS 203) crates are#![no_std]andwasm-friendly, and are the correct equivalents (ML-DSA-87 ≡ Dilithium5
category-5 params; ML-KEM-1024 ≡ Kyber1024 category-5 params). This is
the documented, intentional substitution — flagged here for reviewer
attention.
Nothing left as a silent no-op
Every former stub now performs real cryptography or returns an explicit
error:.../{"error":...}string on bad input — no path silentlybehaves as a no-op.
Verification
The full
recon-wasmcrate does not compile, but for apre-existing reason unrelated to this PR:
src/reconforth/mod.rs:9declares
mod builtins;andpub use builtins::register_builtins;,but
src/reconforth/builtins.rswas never committed (no git history).Pristine
HEAD(94df3cc) fails identically withE0583: file not found for module 'builtins'before any change here. Fixing that unrelatedmodule is out of scope for this security PR.
To verify the crypto itself, the
security.rsmodule was compiled andtested in an isolated crate with only the new dependencies. All 11
tests pass, including cryptographic round-trips and known-answer
vectors:
Verified-against: SHAKE256("") and BLAKE3("") official/NIST KAT vectors;
ML-DSA-87 sign->verify with tamper rejection; ML-KEM-1024 encap->decap
shared-secret agreement; XChaCha20-Poly1305 round-trip + tamper
detection; HKDF determinism; ChaCha20-DRBG determinism + non-zero
output.
In-repo
cargo buildconfirms zero errors originate fromsecurity.rs— the only error is the pre-existingbuiltinsone.wasm target
wasm32-unknown-unknownis available but not installed in the toolchainused here, and the pre-existing
builtinsbreakage blocks thefull-crate build on any target regardless.
Cargo.tomlcorrectly wiresgetrandom'sjsfeature behind[target.'cfg(target_arch = "wasm32")'.dependencies]so the wasm buildwill have a real entropy source for ML-DSA / ML-KEM key generation once
those blockers are resolved.
Reviewer checklist
ml-dsa/ml-kemsubstitution is acceptable vs Cpqcrypto-*ml-dsa 0.0.4,ml-kem 0.2.3)builtins.rsgap is tracked separately🤖 Generated with Claude Code