From de3b74bb9d986102df8b5d41988bfa288f6e1dc3 Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Fri, 27 Mar 2026 09:19:01 +0400 Subject: [PATCH] v0.0.2: add version display, detailed self-test with step-by-step decrypt - Version shown on chat load (v0.0.2) - Self-test now does step-by-step: X3DH shared secret comparison, then manual ratchet init + decrypt (not via decrypt_wire_message) - Shows: rng output, shared_match, alice/bob shared secrets, decrypt result - This isolates whether X3DH or ratchet or AEAD fails Co-Authored-By: Claude Opus 4.6 (1M context) --- warzone/Cargo.lock | 11 ++-- warzone/Cargo.toml | 2 +- .../crates/warzone-server/src/routes/web.rs | 5 +- warzone/crates/warzone-wasm/Cargo.toml | 1 + warzone/crates/warzone-wasm/src/lib.rs | 50 +++++++++++-------- 5 files changed, 39 insertions(+), 30 deletions(-) diff --git a/warzone/Cargo.lock b/warzone/Cargo.lock index 1554bd3..4ced791 100644 --- a/warzone/Cargo.lock +++ b/warzone/Cargo.lock @@ -2555,7 +2555,7 @@ dependencies = [ [[package]] name = "warzone-client" -version = "0.1.0" +version = "0.0.2" dependencies = [ "anyhow", "argon2", @@ -2584,7 +2584,7 @@ dependencies = [ [[package]] name = "warzone-mule" -version = "0.1.0" +version = "0.0.2" dependencies = [ "anyhow", "clap", @@ -2593,7 +2593,7 @@ dependencies = [ [[package]] name = "warzone-protocol" -version = "0.1.0" +version = "0.0.2" dependencies = [ "base64", "bincode", @@ -2616,7 +2616,7 @@ dependencies = [ [[package]] name = "warzone-server" -version = "0.1.0" +version = "0.0.2" dependencies = [ "anyhow", "axum", @@ -2642,7 +2642,7 @@ dependencies = [ [[package]] name = "warzone-wasm" -version = "0.1.0" +version = "0.0.2" dependencies = [ "base64", "bincode", @@ -2650,6 +2650,7 @@ dependencies = [ "getrandom 0.2.17", "hex", "js-sys", + "rand", "serde", "serde_json", "uuid", diff --git a/warzone/Cargo.toml b/warzone/Cargo.toml index f05dd70..c263550 100644 --- a/warzone/Cargo.toml +++ b/warzone/Cargo.toml @@ -9,7 +9,7 @@ members = [ ] [workspace.package] -version = "0.1.0" +version = "0.0.2" edition = "2021" license = "MIT" rust-version = "1.75" diff --git a/warzone/crates/warzone-server/src/routes/web.rs b/warzone/crates/warzone-server/src/routes/web.rs index 7692323..3fd12f9 100644 --- a/warzone/crates/warzone-server/src/routes/web.rs +++ b/warzone/crates/warzone-server/src/routes/web.rs @@ -160,6 +160,7 @@ let peerBundles = {}; // peerFP -> bundle bytes let pollTimer = null; let wasmReady = false; +const VERSION = '0.0.2'; let DEBUG = true; // toggle with /debug command function dbg(...args) { @@ -453,8 +454,8 @@ async function enterChat() { await registerKey(); addSys('Identity loaded: ' + myFingerprint); addSys('Key registered with server'); - addSys('DM: paste peer fingerprint or @alias above'); - addSys('/alias · /g · /glist · /info · /clear'); + addSys('v' + VERSION + ' | DM: paste peer fingerprint or @alias above'); + addSys('/alias · /g · /glist · /info · /selftest · /reset · /debug'); const savedPeer = localStorage.getItem('wz-peer'); if (savedPeer) $peerInput.value = savedPeer; diff --git a/warzone/crates/warzone-wasm/Cargo.toml b/warzone/crates/warzone-wasm/Cargo.toml index cd9a046..436a23c 100644 --- a/warzone/crates/warzone-wasm/Cargo.toml +++ b/warzone/crates/warzone-wasm/Cargo.toml @@ -19,6 +19,7 @@ hex.workspace = true bincode.workspace = true x25519-dalek.workspace = true ed25519-dalek.workspace = true +rand.workspace = true uuid = { version = "1", features = ["v4", "serde", "js"] } # profile.release is set at workspace root diff --git a/warzone/crates/warzone-wasm/src/lib.rs b/warzone/crates/warzone-wasm/src/lib.rs index 6afa0db..b85df74 100644 --- a/warzone/crates/warzone-wasm/src/lib.rs +++ b/warzone/crates/warzone-wasm/src/lib.rs @@ -204,6 +204,11 @@ impl WasmSession { #[wasm_bindgen] pub fn self_test() -> Result { + // Check randomness works + let mut rng_test = [0u8; 8]; + rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut rng_test); + let rng_hex = hex::encode(rng_test); + // Alice let alice_seed = Seed::generate(); let alice_id = alice_seed.derive_identity(); @@ -216,6 +221,7 @@ pub fn self_test() -> Result { // Bob's pre-key bundle let (bob_spk_secret, bob_spk) = generate_signed_pre_key(&bob_id, 1); + let bob_spk_secret_bytes = bob_spk_secret.to_bytes(); let bob_otpks = generate_one_time_pre_keys(0, 5); let bob_bundle = PreKeyBundle { identity_key: *bob_pub.signing.as_bytes(), @@ -237,43 +243,43 @@ pub fn self_test() -> Result { let encrypted = alice_ratchet.encrypt(b"hello from WASM self-test") .map_err(|e| JsValue::from_str(&format!("encrypt: {}", e)))?; - let wire = WireMessage::KeyExchange { + // Clone encrypted for later use (wire takes ownership) + let encrypted_clone = encrypted.clone(); + + let _wire = WireMessage::KeyExchange { sender_fingerprint: alice_pub.fingerprint.to_string(), sender_identity_encryption_key: *alice_pub.encryption.as_bytes(), ephemeral_public: *x3dh_result.ephemeral_public.as_bytes(), used_one_time_pre_key_id: x3dh_result.used_one_time_pre_key_id, ratchet_message: encrypted, }; - let wire_bytes = bincode::serialize(&wire) - .map_err(|e| JsValue::from_str(&e.to_string()))?; - - // Bob decrypts using decrypt_wire_message - let bob_spk_hex = hex::encode(bob_spk_secret.to_bytes()); - let bob_seed_hex = hex::encode(bob_seed.0); - - let result_str = decrypt_wire_message(&bob_seed_hex, &bob_spk_hex, &wire_bytes, None)?; - let result: serde_json::Value = serde_json::from_str(&result_str) - .map_err(|e| JsValue::from_str(&e.to_string()))?; - - let text = result.get("text").and_then(|v| v.as_str()).unwrap_or("MISSING"); - - // More detailed info + // Step-by-step Bob-side decrypt (NOT using decrypt_wire_message) let alice_shared_hex = hex::encode(x3dh_result.shared_secret); - // Bob's side: compute shared secret for comparison + // Bob: X3DH respond let bob_shared = x3dh::respond( &bob_id, &bob_spk_secret, None, &alice_pub.encryption, &x3dh_result.ephemeral_public, - ).map_err(|e| JsValue::from_str(&format!("X3DH respond direct: {}", e)))?; + ).map_err(|e| JsValue::from_str(&format!("X3DH respond: {}", e)))?; let bob_shared_hex = hex::encode(bob_shared); - let shared_match = alice_shared_hex == bob_shared_hex; + // Bob: init ratchet + // Need a fresh copy of spk_secret (bob_spk_secret was moved into respond) + let bob_spk_secret2 = x25519_dalek::StaticSecret::from(bob_spk_secret_bytes); + let mut bob_ratchet = RatchetState::init_bob(bob_shared, bob_spk_secret2); + + // Bob: decrypt + let decrypt_result = bob_ratchet.decrypt(&encrypted_clone); + let decrypt_text = match &decrypt_result { + Ok(plain) => String::from_utf8_lossy(plain).to_string(), + Err(e) => format!("DECRYPT_ERROR: {}", e), + }; + Ok(format!( - "alice_fp={}, bob_fp={}, wire_bytes={}, alice_shared={}..., bob_shared={}..., shared_match={}, decrypted='{}', PASS={}", - alice_pub.fingerprint, bob_pub.fingerprint, wire_bytes.len(), - &alice_shared_hex[..16], &bob_shared_hex[..16], shared_match, - text, text == "hello from WASM self-test" + "rng={}, shared_match={}, alice_shared={}..., bob_shared={}..., decrypt='{}', PASS={}", + rng_hex, shared_match, &alice_shared_hex[..16], &bob_shared_hex[..16], + decrypt_text, decrypt_text == "hello from WASM self-test" )) }