Skip to content

fix(security): replace placeholder crypto with audited crate implementations#39

Merged
hyperpolymath merged 1 commit into
mainfrom
fix/real-crypto-security-module
May 16, 2026
Merged

fix(security): replace placeholder crypto with audited crate implementations#39
hyperpolymath merged 1 commit into
mainfrom
fix/real-crypto-security-module

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

⚠️ SECURITY-SENSITIVE — DRAFT, DO NOT MERGE WITHOUT REVIEW

This PR replaces placeholder/stub cryptography in
recon-silly-ation/wasm-modules/src/security.rs with real
implementations. It requires human security review — and ideally
formal/cryptographic review — before it is marked ready or merged.

Left as a draft intentionally.

Background

security.rs previously shipped ~15 #[wasm_bindgen] functions whose
bodies returned non-cryptographic placeholders: format!("stub:...")
digests, an Argon2 stub, dilithium5_verify that always returned
false
, a ChaCha20-DRBG that returned all-zero bytes, etc. Any
caller 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
*_impl helpers so the crypto is unit-testable on native targets.

Primitive Crate (pinned) Notes
SHAKE3-512 (SHA-3 XOF) sha3 = "=0.10.9" Shake256, 64-byte squeeze
BLAKE3 blake3 = "=1.8.5"
Argon2id argon2 = "=0.5.3" m=64 MiB, 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" 32-byte key, 24-byte nonce
HKDF (SHA3-512) hkdf = "=0.12.4" + sha3 HKDF-Extract/Expand over SHA3-512
ChaCha20-DRBG rand_chacha = "=0.3.1" ChaCha20Rng seeded from caller seed
hex (de)coding hex = "=0.4.3"
CSPRNG seed getrandom = "0.2" js feature wasm-gated for keygen/encap entropy

user_friendly_hash_name now maps digest bytes to a fixed 256-word
list (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 the
PQClean C reference code. This crate is a cdylib targeting
wasm32-unknown-unknown (crate-type = ["cdylib"], wasm-bindgen),
where linking PQClean C is not viable. The pure-Rust RustCrypto
ml-dsa (FIPS 204) and ml-kem (FIPS 203) crates are #![no_std] and
wasm-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.

Note: ml-dsa is currently 0.0.4 and ml-kem 0.2.3. Both carry
upstream "not independently audited" warnings (typical for the
RustCrypto PQC crates pre-1.0). This is called out explicitly for the
security reviewer: the classical primitives use mature audited
crates; the PQC primitives use the best available pure-Rust
implementations, which are community-reviewed but pre-audit. A
reviewer may wish to gate PQC use accordingly.

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 silently
behaves as a no-op.

Verification

The full recon-wasm crate does not compile, but for a
pre-existing reason unrelated to this PR
: src/reconforth/mod.rs:9
declares mod builtins; and pub use builtins::register_builtins;,
but src/reconforth/builtins.rs was never committed (no git history).
Pristine HEAD (94df3cc) fails identically with E0583: file not found for module 'builtins' before any change here. Fixing that unrelated
module is out of scope for this security PR.

To verify the crypto itself, the security.rs module was compiled and
tested in an isolated crate with only the new dependencies. All 11
tests pass, including cryptographic round-trips and known-answer
vectors:

   Compiling ml-kem v0.2.3
   Compiling ml-dsa v0.0.4
   Compiling chacha20poly1305 v0.10.1
   Compiling argon2 v0.5.3
   Compiling hkdf v0.12.4
   Compiling rand_chacha v0.3.1
   Compiling hex v0.4.3
   Compiling secverify v0.0.0 (/tmp/secverify)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 16.72s

running 11 tests
test security::tests::test_blake3_hash_known_vector ... ok
test security::tests::test_chacha20_drbg_deterministic_and_length ... ok
test security::tests::test_hkdf_shake512_deterministic ... ok
test security::tests::test_shake3_512_hash_known_vector ... ok
test security::tests::test_user_friendly_hash_name_is_words ... ok
test security::tests::test_user_friendly_hash_name_non_hex_fallback ... ok
test security::tests::test_xchacha20poly1305_round_trip ... ok
test security::tests::test_xchacha20poly1305_tamper_detected ... ok
test security::tests::test_kyber1024_encap_decap_round_trip ... ok
test security::tests::test_dilithium5_sign_verify_round_trip ... ok
test security::tests::test_argon2id_hash_phc_format ... ok

test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

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 build confirms zero errors originate from
security.rs
— the only error is the pre-existing builtins one.

wasm target

wasm32-unknown-unknown is available but not installed in the toolchain
used here, and the pre-existing builtins breakage blocks the
full-crate build on any target regardless. Cargo.toml correctly wires
getrandom's js feature behind
[target.'cfg(target_arch = "wasm32")'.dependencies] so the wasm build
will have a real entropy source for ML-DSA / ML-KEM key generation once
those blockers are resolved.

Reviewer checklist

  • Confirm pure-Rust ml-dsa/ml-kem substitution is acceptable vs C pqcrypto-*
  • Accept (or gate) pre-audit PQC crate versions (ml-dsa 0.0.4, ml-kem 0.2.3)
  • Review Argon2id parameters (64 MiB / t=3 / p=4) for the deployment
  • Review nonce/key handling expectations at call sites (caller-supplied nonce contract unchanged)
  • Decide whether the unrelated builtins.rs gap is tracked separately

🤖 Generated with Claude Code

…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>
@hyperpolymath hyperpolymath marked this pull request as ready for review May 16, 2026 14:56
@hyperpolymath hyperpolymath merged commit 9b53b0e into main May 16, 2026
19 of 21 checks passed
@hyperpolymath hyperpolymath deleted the fix/real-crypto-security-module branch May 16, 2026 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant