From 8926f163dad9d32dc06c3a142658a4e11d9c12c1 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Wed, 30 Sep 2026 15:48:51 +0200 Subject: refactor(hub): the transport holds an identity, never a private key Two public keys, sign() and shared(); the apps take transport.signFn. What holds the keys (this page, or the desktop main process) is the identity's business alone. Co-Authored-By: Claude Opus 5.5 --- packages/meshbay-hub/src/meshbay_hub/static/crypto.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/crypto.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/crypto.js b/packages/meshbay-hub/src/meshbay_hub/static/crypto.js index 0ca8ee5..27e97d3 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/crypto.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/crypto.js @@ -278,17 +278,17 @@ async function verifyChatSignature(deviceRaw, groupId, epoch, nonce, ct, sig) { // ── GEK unwrapping (ECIES) ───────────────────────────────────────────────────── -async function unwrapGEK(bundle, skXPkcs8, pkXRaw) { +/** + * Unwrap a group key wrapped for our X25519 key. `shared(pkEphRaw)` is the + * agreement with the ephemeral key — done by whatever holds the private key + * (the identity object, transport.js), so this never sees one. + */ +async function unwrapGEK(bundle, shared, pkXRaw) { const pkEphRaw = b64decode(bundle.pk_eph_b64); const nonce = b64decode(bundle.nonce_b64); const wrapped = b64decode(bundle.wrapped_b64); - const skX = await crypto.subtle.importKey( - 'pkcs8', skXPkcs8, { name: 'X25519' }, false, ['deriveBits']); - const pkEph = await crypto.subtle.importKey( - 'raw', pkEphRaw, { name: 'X25519' }, false, []); - const sharedBits = await crypto.subtle.deriveBits( - { name: 'X25519', public: pkEph }, skX, 256); + const sharedBits = await shared(pkEphRaw); const sharedKey = await crypto.subtle.importKey( 'raw', sharedBits, 'HKDF', false, ['deriveKey']); -- cgit v1.2.3