From 91297944791a36f30302ef8c86dd69ebeb177671 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Wed, 30 Sep 2026 15:06:14 +0200 Subject: feat: bundles sealed per node under the passphrase and the hub's pepper The session key is M = HKDF(Argon2(passphrase) || pepper, account id); each node's bundle key and the playlist key derive from it. Bundles are MBK3, bound to account and node; MBK1/MBK2 are refused by name, never replaced silently. Playlists move to key v2 and are re-sealed over unreadable node copies. Co-Authored-By: Claude Opus 5.5 --- packages/meshbay-hub/tests/test_recovery_key.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'packages/meshbay-hub/tests/test_recovery_key.py') diff --git a/packages/meshbay-hub/tests/test_recovery_key.py b/packages/meshbay-hub/tests/test_recovery_key.py index e43e6de..c574597 100644 --- a/packages/meshbay-hub/tests/test_recovery_key.py +++ b/packages/meshbay-hub/tests/test_recovery_key.py @@ -84,10 +84,11 @@ const fp = async (key) => hex(await webcrypto.subtle.encrypt( const kB = await K.deriveRecoveryKey(raw, 'acc-B'); const skEd = new Uint8Array([1, 2, 3]); const skX = new Uint8Array([4, 5, 6]); - const blob = await K.encryptBundleWithKey(skEd, skX, kA); + const sealedFor = { userId: 'acc-A', nodePk: 'node-key' }; + const blob = await K.encryptBundle(skEd, skX, kA, { ...sealedFor, pepperVersion: 0 }); let wrongRejected = false; - try { await K.decryptBundleWithKey(blob, kB); } catch { wrongRejected = true; } - const opened = await K.decryptBundleWithKey(blob, kA); + try { await K.decryptBundle(blob, kB, sealedFor); } catch { wrongRejected = true; } + const opened = await K.decryptBundle(blob, kA, sealedFor); out.recovery_wrap_isolates = wrongRejected && opened.skEd === btoa(String.fromCharCode(1, 2, 3)) && opened.skX === btoa(String.fromCharCode(4, 5, 6)); -- cgit v1.2.3