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) <noreply@anthropic.com>
This commit is contained in:
11
warzone/Cargo.lock
generated
11
warzone/Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -9,7 +9,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
version = "0.0.2"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.75"
|
||||
|
||||
@@ -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 <name> · /g <group> · /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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -204,6 +204,11 @@ impl WasmSession {
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn self_test() -> Result<String, JsValue> {
|
||||
// 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<String, JsValue> {
|
||||
|
||||
// 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<String, JsValue> {
|
||||
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"
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user