Fix WASM decrypt: store SPK secret, pass to decrypt_wire_message
Root cause: WASM was regenerating random pre-keys on every call to decrypt_wire_message, instead of using the SPK that was registered with the server. CLI sender encrypts to the registered SPK, but WASM was trying to decrypt with a different random key. Fix: - WasmIdentity now stores spk_secret_bytes internally - SPK secret persisted to localStorage as 'wz-spk' - On load: restored from localStorage, not regenerated - bundle_bytes() uses stored SPK secret (cached, deterministic) - decrypt_wire_message() takes spk_secret_hex parameter - Web UI passes stored SPK to all decrypt calls Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -177,10 +177,25 @@ async function initWasm() {
|
||||
wasmReady = true;
|
||||
}
|
||||
|
||||
let mySpkSecretHex = '';
|
||||
|
||||
function initIdentityFromSeed(hexSeed) {
|
||||
wasmIdentity = WasmIdentity.from_hex_seed(hexSeed);
|
||||
myFingerprint = wasmIdentity.fingerprint();
|
||||
mySeedHex = wasmIdentity.seed_hex();
|
||||
|
||||
// Restore or generate SPK secret
|
||||
const savedSpk = localStorage.getItem('wz-spk');
|
||||
if (savedSpk) {
|
||||
wasmIdentity.set_spk_secret_hex(savedSpk);
|
||||
mySpkSecretHex = savedSpk;
|
||||
dbg('Restored SPK secret from localStorage');
|
||||
} else {
|
||||
mySpkSecretHex = wasmIdentity.spk_secret_hex();
|
||||
localStorage.setItem('wz-spk', mySpkSecretHex);
|
||||
dbg('Generated new SPK secret');
|
||||
}
|
||||
|
||||
localStorage.setItem('wz-seed', mySeedHex);
|
||||
localStorage.setItem('wz-fp', myFingerprint);
|
||||
}
|
||||
@@ -189,8 +204,11 @@ function generateNewIdentity() {
|
||||
wasmIdentity = new WasmIdentity();
|
||||
myFingerprint = wasmIdentity.fingerprint();
|
||||
mySeedHex = wasmIdentity.seed_hex();
|
||||
mySpkSecretHex = wasmIdentity.spk_secret_hex();
|
||||
localStorage.setItem('wz-seed', mySeedHex);
|
||||
localStorage.setItem('wz-fp', myFingerprint);
|
||||
localStorage.setItem('wz-spk', mySpkSecretHex);
|
||||
dbg('New identity, SPK secret stored');
|
||||
}
|
||||
|
||||
function loadSavedIdentity() {
|
||||
@@ -198,7 +216,7 @@ function loadSavedIdentity() {
|
||||
if (!saved) { dbg('No saved seed'); return false; }
|
||||
try {
|
||||
initIdentityFromSeed(saved);
|
||||
dbg('Loaded identity:', myFingerprint);
|
||||
dbg('Loaded identity:', myFingerprint, 'has SPK:', !!mySpkSecretHex);
|
||||
return true;
|
||||
} catch(e) {
|
||||
dbg('Failed to load identity:', e);
|
||||
@@ -295,7 +313,7 @@ async function pollMessages() {
|
||||
let decrypted = false;
|
||||
try {
|
||||
dbg('Trying decrypt as KeyExchange (no session)...');
|
||||
const resultStr = decrypt_wire_message(mySeedHex, bytes, null);
|
||||
const resultStr = decrypt_wire_message(mySeedHex, mySpkSecretHex, bytes, null);
|
||||
const result = JSON.parse(resultStr);
|
||||
dbg('Decrypted!', result.new_session ? 'new session' : 'existing', 'from:', result.sender);
|
||||
|
||||
@@ -323,7 +341,7 @@ async function pollMessages() {
|
||||
for (const [senderFP, sessData] of Object.entries(sessions)) {
|
||||
try {
|
||||
dbg('Trying session for', senderFP);
|
||||
const resultStr = decrypt_wire_message(mySeedHex, bytes, sessData.data);
|
||||
const resultStr = decrypt_wire_message(mySeedHex, mySpkSecretHex, bytes, sessData.data);
|
||||
const result = JSON.parse(resultStr);
|
||||
dbg('Decrypted with session', senderFP, ':', result.text.slice(0, 30));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user