v0.0.3: fix X3DH OTPK mismatch — web bundles without OTPKs
Root cause: web client's bundle included OTPKs, so X3DH initiate() did 4 DH ops (DH4 with OTPK). But decrypt_wire_message() called respond() with None for OTPK, doing only 3 DH ops. Different DH concat → different shared secret → decrypt fails. Fix: web client bundles have one_time_pre_key: None. initiate() skips DH4 when no OTPK present. respond() also skips DH4 with None. Both sides now do exactly 3 DH ops → shared secrets match. OTPKs are an anti-replay optimization, not required for E2E. Will add OTPK support to web client in Phase 2 with proper server-side OTPK storage and consumption tracking. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
warzone/Cargo.lock
generated
10
warzone/Cargo.lock
generated
@@ -2555,7 +2555,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "warzone-client"
|
||||
version = "0.0.2"
|
||||
version = "0.0.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -2584,7 +2584,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "warzone-mule"
|
||||
version = "0.0.2"
|
||||
version = "0.0.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2593,7 +2593,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "warzone-protocol"
|
||||
version = "0.0.2"
|
||||
version = "0.0.3"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bincode",
|
||||
@@ -2616,7 +2616,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "warzone-server"
|
||||
version = "0.0.2"
|
||||
version = "0.0.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -2642,7 +2642,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "warzone-wasm"
|
||||
version = "0.0.2"
|
||||
version = "0.0.3"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bincode",
|
||||
|
||||
@@ -9,7 +9,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.2"
|
||||
version = "0.0.3"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.75"
|
||||
|
||||
@@ -160,7 +160,7 @@ let peerBundles = {}; // peerFP -> bundle bytes
|
||||
let pollTimer = null;
|
||||
let wasmReady = false;
|
||||
|
||||
const VERSION = '0.0.2';
|
||||
const VERSION = '0.0.3';
|
||||
let DEBUG = true; // toggle with /debug command
|
||||
|
||||
function dbg(...args) {
|
||||
|
||||
@@ -8,7 +8,7 @@ use wasm_bindgen::prelude::*;
|
||||
use warzone_protocol::identity::{IdentityKeyPair, PublicIdentity, Seed};
|
||||
use warzone_protocol::message::WireMessage;
|
||||
use warzone_protocol::prekey::{
|
||||
generate_one_time_pre_keys, generate_signed_pre_key, OneTimePreKeyPublic, PreKeyBundle,
|
||||
generate_signed_pre_key, PreKeyBundle,
|
||||
};
|
||||
use warzone_protocol::ratchet::RatchetState;
|
||||
use warzone_protocol::x3dh;
|
||||
@@ -115,16 +115,14 @@ impl WasmIdentity {
|
||||
timestamp: js_sys::Date::now() as i64 / 1000,
|
||||
};
|
||||
|
||||
let otpks = generate_one_time_pre_keys(0, 10);
|
||||
|
||||
// No OTPKs for web client (can't store secrets for them reliably).
|
||||
// initiate() will skip DH4 when one_time_pre_key is None.
|
||||
// This is safe — OTPKs are an anti-replay optimization, not required.
|
||||
Ok(PreKeyBundle {
|
||||
identity_key: *self.pub_id.signing.as_bytes(),
|
||||
identity_encryption_key: *self.pub_id.encryption.as_bytes(),
|
||||
signed_pre_key: spk,
|
||||
one_time_pre_key: Some(OneTimePreKeyPublic {
|
||||
id: otpks[0].id,
|
||||
public_key: *otpks[0].public.as_bytes(),
|
||||
}),
|
||||
one_time_pre_key: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -222,15 +220,11 @@ 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(),
|
||||
identity_encryption_key: *bob_pub.encryption.as_bytes(),
|
||||
signed_pre_key: bob_spk,
|
||||
one_time_pre_key: Some(OneTimePreKeyPublic {
|
||||
id: bob_otpks[0].id,
|
||||
public_key: *bob_otpks[0].public.as_bytes(),
|
||||
}),
|
||||
one_time_pre_key: None,
|
||||
};
|
||||
let _bob_bundle_bytes = bincode::serialize(&bob_bundle)
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
||||
|
||||
Reference in New Issue
Block a user