@enbox/browser 0.1.26 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
16
16
  step((generator = generator.apply(thisArg, _arguments || [])).next());
17
17
  });
18
18
  };
19
+ import { decryptPostMessagePayload, generateEphemeralKeyPair } from './dweb-connect-crypto.js';
19
20
  /**
20
21
  * Open a wallet popup and run the DWeb Connect postMessage flow.
21
22
  *
@@ -42,6 +43,19 @@ function initClient(options) {
42
43
  if (!popup) {
43
44
  throw new Error('[@enbox/auth] Popup blocked. Allow popups for this site to connect to a wallet.');
44
45
  }
46
+ // Generate an ephemeral ECDH keypair for this connect session.
47
+ // The public key is sent to the wallet so it can encrypt the response
48
+ // containing delegate private keys and decryption material.
49
+ let dappKeyPair;
50
+ let dappPublicKeyBase64url;
51
+ try {
52
+ const ephemeral = yield generateEphemeralKeyPair();
53
+ dappKeyPair = ephemeral.keyPair;
54
+ dappPublicKeyBase64url = ephemeral.publicKeyBase64url;
55
+ }
56
+ catch (_a) {
57
+ // crypto.subtle unavailable (e.g. non-secure context) — skip encryption.
58
+ }
45
59
  return new Promise((resolve, reject) => {
46
60
  let settled = false;
47
61
  const cleanup = () => {
@@ -77,24 +91,49 @@ function initClient(options) {
77
91
  }
78
92
  const { type } = (_a = event.data) !== null && _a !== void 0 ? _a : {};
79
93
  if (type === 'dweb-connect-loaded') {
80
- // Wallet is ready — send the authorization request.
94
+ // Wallet is ready — send the authorization request with ephemeral public key.
81
95
  popup.postMessage({
82
96
  type: 'dweb-connect-authorization-request',
83
97
  did,
84
98
  permissions: permissionRequests,
99
+ ephemeralPublicKey: dappPublicKeyBase64url,
85
100
  }, walletOrigin);
86
101
  return;
87
102
  }
88
103
  if (type === 'dweb-connect-authorization-response') {
89
104
  clearInterval(pollClosed);
90
105
  cleanup();
106
+ // Handle encrypted response (wallet supports ECDH channel encryption).
107
+ const encrypted = event.data.encryptedPayload;
108
+ if (encrypted && dappKeyPair) {
109
+ decryptPostMessagePayload(encrypted, dappKeyPair).then((payload) => {
110
+ var _a, _b, _c, _d, _e, _f;
111
+ const p = payload;
112
+ if (!p.delegateDid || !p.grants) {
113
+ resolve(undefined);
114
+ return;
115
+ }
116
+ resolve({
117
+ delegatePortableDid: p.delegateDid,
118
+ delegateGrants: p.grants,
119
+ connectedDid: (_b = (_a = p.connectedDid) !== null && _a !== void 0 ? _a : did) !== null && _b !== void 0 ? _b : p.delegateDid.uri,
120
+ delegateDecryptionKeys: (_c = p.delegateDecryptionKeys) !== null && _c !== void 0 ? _c : undefined,
121
+ delegateContextKeys: (_d = p.delegateContextKeys) !== null && _d !== void 0 ? _d : undefined,
122
+ delegateMultiPartyProtocols: (_e = p.delegateMultiPartyProtocols) !== null && _e !== void 0 ? _e : undefined,
123
+ sessionRevocations: (_f = p.sessionRevocations) !== null && _f !== void 0 ? _f : undefined,
124
+ });
125
+ }).catch(() => {
126
+ // Decryption failed — treat as denied.
127
+ resolve(undefined);
128
+ });
129
+ return;
130
+ }
131
+ // Plaintext fallback (wallet doesn't support encryption yet).
91
132
  const { delegateDid, connectedDid: walletConnectedDid, grants, delegateDecryptionKeys, delegateContextKeys, delegateMultiPartyProtocols, sessionRevocations, } = event.data;
92
133
  if (!delegateDid || !grants) {
93
- // User denied the request.
94
134
  resolve(undefined);
95
135
  return;
96
136
  }
97
- // connectedDid priority: wallet response > dapp-provided > delegate DID
98
137
  resolve({
99
138
  delegatePortableDid: delegateDid,
100
139
  delegateGrants: grants,
@@ -1 +1 @@
1
- {"version":3,"file":"dweb-connect-client.js","sourceRoot":"","sources":["../../src/dweb-connect-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dweb-connect-client.js","sourceRoot":"","sources":["../../src/dweb-connect-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * ECDH + AES-256-GCM encryption for the DWeb Connect postMessage channel.
3
+ *
4
+ * Protects the authorization response (delegate private keys, decryption
5
+ * keys, grants) from same-origin XSS interception. Both the dapp and
6
+ * wallet generate ephemeral P-256 keypairs, exchange public keys during
7
+ * the handshake, derive a shared AES-256-GCM key via ECDH + HKDF, and
8
+ * encrypt/decrypt the payload.
9
+ *
10
+ * P-256 is used (instead of X25519) because `crypto.subtle.deriveKey`
11
+ * with ECDH requires a NIST curve in all major browsers. The keys are
12
+ * ephemeral — generated fresh for each connect session.
13
+ *
14
+ * @module
15
+ */
16
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
17
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
18
+ return new (P || (P = Promise))(function (resolve, reject) {
19
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
20
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
21
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
22
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
23
+ });
24
+ };
25
+ const ECDH_PARAMS = { name: 'ECDH', namedCurve: 'P-256' };
26
+ const AES_KEY_PARAMS = { name: 'AES-GCM', length: 256 };
27
+ const HKDF_INFO = new TextEncoder().encode('dweb-connect-v1');
28
+ /**
29
+ * Generate an ephemeral P-256 ECDH keypair for a single connect session.
30
+ *
31
+ * @returns The keypair and the public key as a base64url-encoded raw export.
32
+ */
33
+ export function generateEphemeralKeyPair() {
34
+ return __awaiter(this, void 0, void 0, function* () {
35
+ const keyPair = yield crypto.subtle.generateKey(ECDH_PARAMS, false, ['deriveKey']);
36
+ const rawPub = yield crypto.subtle.exportKey('raw', keyPair.publicKey);
37
+ return { keyPair, publicKeyBase64url: toBase64url(new Uint8Array(rawPub)) };
38
+ });
39
+ }
40
+ /**
41
+ * Derive a shared AES-256-GCM key from a local ECDH private key and a
42
+ * remote public key.
43
+ */
44
+ function deriveSharedKey(localPrivateKey, remotePublicKeyBase64url) {
45
+ return __awaiter(this, void 0, void 0, function* () {
46
+ const remotePubRaw = fromBase64url(remotePublicKeyBase64url);
47
+ const remotePublicKey = yield crypto.subtle.importKey('raw', remotePubRaw, ECDH_PARAMS, false, []);
48
+ // ECDH → raw shared secret, then HKDF → AES-256-GCM key.
49
+ const sharedBits = yield crypto.subtle.deriveBits({ name: 'ECDH', public: remotePublicKey }, localPrivateKey, 256);
50
+ const hkdfKey = yield crypto.subtle.importKey('raw', sharedBits, 'HKDF', false, ['deriveKey']);
51
+ return crypto.subtle.deriveKey({ name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(32), info: HKDF_INFO }, hkdfKey, AES_KEY_PARAMS, false, ['encrypt', 'decrypt']);
52
+ });
53
+ }
54
+ /**
55
+ * Encrypt a JSON-serializable payload for postMessage transport.
56
+ *
57
+ * Called by the **wallet** side. Uses the wallet's ephemeral private key
58
+ * and the dapp's ephemeral public key to derive the shared AES key.
59
+ *
60
+ * @returns The encrypted payload plus the wallet's public key (for the dapp to derive the same shared key).
61
+ */
62
+ export function encryptPostMessagePayload(payload, walletKeyPair, walletPublicKeyBase64url, dappPublicKeyBase64url) {
63
+ return __awaiter(this, void 0, void 0, function* () {
64
+ const sharedKey = yield deriveSharedKey(walletKeyPair.privateKey, dappPublicKeyBase64url);
65
+ const iv = crypto.getRandomValues(new Uint8Array(12));
66
+ const plaintext = new TextEncoder().encode(JSON.stringify(payload));
67
+ const ciphertextBuf = yield crypto.subtle.encrypt({ name: 'AES-GCM', iv }, sharedKey, plaintext);
68
+ return {
69
+ ciphertext: toBase64url(new Uint8Array(ciphertextBuf)),
70
+ iv: toBase64url(iv),
71
+ walletPublicKey: walletPublicKeyBase64url,
72
+ };
73
+ });
74
+ }
75
+ /**
76
+ * Decrypt a postMessage payload received from the wallet.
77
+ *
78
+ * Called by the **dapp** side. Uses the dapp's ephemeral private key
79
+ * and the wallet's ephemeral public key to derive the same shared key.
80
+ */
81
+ export function decryptPostMessagePayload(encrypted, dappKeyPair) {
82
+ return __awaiter(this, void 0, void 0, function* () {
83
+ const sharedKey = yield deriveSharedKey(dappKeyPair.privateKey, encrypted.walletPublicKey);
84
+ const iv = fromBase64url(encrypted.iv);
85
+ const ciphertext = fromBase64url(encrypted.ciphertext);
86
+ const plaintextBuf = yield crypto.subtle.decrypt({ name: 'AES-GCM', iv }, sharedKey, ciphertext);
87
+ return JSON.parse(new TextDecoder().decode(plaintextBuf));
88
+ });
89
+ }
90
+ // ── Base64url helpers ────────────────────────────────────────────
91
+ function toBase64url(bytes) {
92
+ let binary = '';
93
+ for (let i = 0; i < bytes.length; i++) {
94
+ binary += String.fromCharCode(bytes[i]);
95
+ }
96
+ let b64 = btoa(binary).replace(/\+/g, '-').replace(/\//g, '_');
97
+ // Strip trailing padding without a backtracking-vulnerable regex.
98
+ while (b64.endsWith('=')) {
99
+ b64 = b64.slice(0, -1);
100
+ }
101
+ return b64;
102
+ }
103
+ function fromBase64url(str) {
104
+ const padded = str.replace(/-/g, '+').replace(/_/g, '/');
105
+ const binary = atob(padded);
106
+ const bytes = new Uint8Array(binary.length);
107
+ for (let i = 0; i < binary.length; i++) {
108
+ bytes[i] = binary.charCodeAt(i);
109
+ }
110
+ return bytes;
111
+ }
112
+ //# sourceMappingURL=dweb-connect-crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dweb-connect-crypto.js","sourceRoot":"","sources":["../../src/dweb-connect-crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;AAeH,MAAM,WAAW,GAAmB,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;AAC1E,MAAM,cAAc,GAAwB,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAC7E,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;AAE9D;;;;GAIG;AACH,MAAM,UAAgB,wBAAwB;;QAI5C,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;QACnF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QACvE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;IAC9E,CAAC;CAAA;AAED;;;GAGG;AACH,SAAe,eAAe,CAC5B,eAA0B,EAC1B,wBAAgC;;QAEhC,MAAM,YAAY,GAAG,aAAa,CAAC,wBAAwB,CAAC,CAAC;QAC7D,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACnD,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,CAC5C,CAAC;QAEF,yDAAyD;QACzD,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAC/C,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAyB,EAChE,eAAe,EACf,GAAG,CACJ,CAAC;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAC3C,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,CAChD,CAAC;QAEF,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAC5B,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,UAAU,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAC5E,OAAO,EACP,cAAc,EACd,KAAK,EACL,CAAC,SAAS,EAAE,SAAS,CAAC,CACvB,CAAC;IACJ,CAAC;CAAA;AAED;;;;;;;GAOG;AACH,MAAM,UAAgB,yBAAyB,CAC7C,OAAgC,EAChC,aAA4B,EAC5B,wBAAgC,EAChC,sBAA8B;;QAE9B,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,aAAa,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;QAC1F,MAAM,EAAE,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QAEpE,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAC/C,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAkB,EACvC,SAAS,EACT,SAAS,CACV,CAAC;QAEF,OAAO;YACL,UAAU,EAAQ,WAAW,CAAC,IAAI,UAAU,CAAC,aAAa,CAAC,CAAC;YAC5D,EAAE,EAAgB,WAAW,CAAC,EAAE,CAAC;YACjC,eAAe,EAAG,wBAAwB;SAC3C,CAAC;IACJ,CAAC;CAAA;AAED;;;;;GAKG;AACH,MAAM,UAAgB,yBAAyB,CAC7C,SAAsC,EACtC,WAA0B;;QAE1B,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC;QAC3F,MAAM,EAAE,GAAG,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,UAAU,GAAG,aAAa,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAEvD,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAC9C,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAkB,EACvC,SAAS,EACT,UAAU,CACX,CAAC;QAEF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;IAC5D,CAAC;CAAA;AAED,oEAAoE;AAEpE,SAAS,WAAW,CAAC,KAAiB;IACpC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/D,kEAAkE;IAClE,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAAC,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAAC,CAAC;IACrD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/dist/esm/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './web-features.js';
2
2
  export { BrowserConnectHandler, DEFAULT_WALLETS } from './browser-connect-handler.js';
3
3
  export { DWebConnect } from './dweb-connect-client.js';
4
+ export { encryptPostMessagePayload, generateEphemeralKeyPair } from './dweb-connect-crypto.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEtF,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEtF,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"dweb-connect-client.d.ts","sourceRoot":"","sources":["../../src/dweb-connect-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,KAAK,EAAE,wBAAwB,EAAqC,MAAM,cAAc,CAAC;AAEhG,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAElB,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,kBAAkB,EAAE,wBAAwB,EAAE,CAAC;IAE/C;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,iBAAe,UAAU,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CA6G/F;AAED;;;;;;;;;;;GAWG;AACH,iBAAe,kBAAkB,CAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,SAAQ,GACd,OAAO,CAAC,OAAO,CAAC,CA+ClB;AAED,eAAO,MAAM,WAAW;;;CAAqC,CAAC"}
1
+ {"version":3,"file":"dweb-connect-client.d.ts","sourceRoot":"","sources":["../../src/dweb-connect-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,KAAK,EAAE,wBAAwB,EAAqC,MAAM,cAAc,CAAC;AAKhG,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAElB,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,kBAAkB,EAAE,wBAAwB,EAAE,CAAC;IAE/C;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,iBAAe,UAAU,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAoJ/F;AAED;;;;;;;;;;;GAWG;AACH,iBAAe,kBAAkB,CAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,SAAQ,GACd,OAAO,CAAC,OAAO,CAAC,CA+ClB;AAED,eAAO,MAAM,WAAW;;;CAAqC,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * ECDH + AES-256-GCM encryption for the DWeb Connect postMessage channel.
3
+ *
4
+ * Protects the authorization response (delegate private keys, decryption
5
+ * keys, grants) from same-origin XSS interception. Both the dapp and
6
+ * wallet generate ephemeral P-256 keypairs, exchange public keys during
7
+ * the handshake, derive a shared AES-256-GCM key via ECDH + HKDF, and
8
+ * encrypt/decrypt the payload.
9
+ *
10
+ * P-256 is used (instead of X25519) because `crypto.subtle.deriveKey`
11
+ * with ECDH requires a NIST curve in all major browsers. The keys are
12
+ * ephemeral — generated fresh for each connect session.
13
+ *
14
+ * @module
15
+ */
16
+ /** Exported public key as raw bytes (base64url-encoded for postMessage). */
17
+ export type ExportedPublicKey = string;
18
+ /** Encrypted payload sent via postMessage. */
19
+ export interface EncryptedPostMessagePayload {
20
+ /** The encrypted JSON payload as base64url. */
21
+ ciphertext: string;
22
+ /** AES-GCM initialization vector as base64url. */
23
+ iv: string;
24
+ /** The wallet's ephemeral P-256 public key as base64url raw bytes. */
25
+ walletPublicKey: string;
26
+ }
27
+ /**
28
+ * Generate an ephemeral P-256 ECDH keypair for a single connect session.
29
+ *
30
+ * @returns The keypair and the public key as a base64url-encoded raw export.
31
+ */
32
+ export declare function generateEphemeralKeyPair(): Promise<{
33
+ keyPair: CryptoKeyPair;
34
+ publicKeyBase64url: ExportedPublicKey;
35
+ }>;
36
+ /**
37
+ * Encrypt a JSON-serializable payload for postMessage transport.
38
+ *
39
+ * Called by the **wallet** side. Uses the wallet's ephemeral private key
40
+ * and the dapp's ephemeral public key to derive the shared AES key.
41
+ *
42
+ * @returns The encrypted payload plus the wallet's public key (for the dapp to derive the same shared key).
43
+ */
44
+ export declare function encryptPostMessagePayload(payload: Record<string, unknown>, walletKeyPair: CryptoKeyPair, walletPublicKeyBase64url: string, dappPublicKeyBase64url: string): Promise<EncryptedPostMessagePayload>;
45
+ /**
46
+ * Decrypt a postMessage payload received from the wallet.
47
+ *
48
+ * Called by the **dapp** side. Uses the dapp's ephemeral private key
49
+ * and the wallet's ephemeral public key to derive the same shared key.
50
+ */
51
+ export declare function decryptPostMessagePayload(encrypted: EncryptedPostMessagePayload, dappKeyPair: CryptoKeyPair): Promise<Record<string, unknown>>;
52
+ //# sourceMappingURL=dweb-connect-crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dweb-connect-crypto.d.ts","sourceRoot":"","sources":["../../src/dweb-connect-crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEvC,8CAA8C;AAC9C,MAAM,WAAW,2BAA2B;IAC1C,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,EAAE,EAAE,MAAM,CAAC;IACX,sEAAsE;IACtE,eAAe,EAAE,MAAM,CAAC;CACzB;AAMD;;;;GAIG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC;IACxD,OAAO,EAAE,aAAa,CAAC;IACvB,kBAAkB,EAAE,iBAAiB,CAAC;CACvC,CAAC,CAID;AAmCD;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,aAAa,EAAE,aAAa,EAC5B,wBAAwB,EAAE,MAAM,EAChC,sBAAsB,EAAE,MAAM,GAC7B,OAAO,CAAC,2BAA2B,CAAC,CAgBtC;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,2BAA2B,EACtC,WAAW,EAAE,aAAa,GACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAYlC"}
@@ -3,4 +3,6 @@ export { BrowserConnectHandler, DEFAULT_WALLETS } from './browser-connect-handle
3
3
  export type { BrowserConnectHandlerOptions, WalletOption } from './browser-connect-handler.js';
4
4
  export { DWebConnect } from './dweb-connect-client.js';
5
5
  export type { DWebConnectClientOptions } from './dweb-connect-client.js';
6
+ export { encryptPostMessagePayload, generateEphemeralKeyPair } from './dweb-connect-crypto.js';
7
+ export type { EncryptedPostMessagePayload } from './dweb-connect-crypto.js';
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACtF,YAAY,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,YAAY,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACtF,YAAY,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,YAAY,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAC/F,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enbox/browser",
3
- "version": "0.1.26",
3
+ "version": "0.2.1",
4
4
  "description": "Enbox tools and features to use in the browser",
5
5
  "type": "module",
6
6
  "main": "./dist/esm/index.js",
@@ -55,8 +55,8 @@
55
55
  "access": "public"
56
56
  },
57
57
  "dependencies": {
58
- "@enbox/agent": "0.6.1",
59
- "@enbox/auth": "0.6.21",
58
+ "@enbox/agent": "0.6.3",
59
+ "@enbox/auth": "0.6.23",
60
60
  "@enbox/dids": "0.1.0"
61
61
  },
62
62
  "devDependencies": {
@@ -12,6 +12,9 @@ import type { ConnectResult } from '@enbox/auth';
12
12
  import type { PortableDid } from '@enbox/dids';
13
13
  import type { ConnectPermissionRequest, DwnDataEncodedRecordsWriteMessage } from '@enbox/agent';
14
14
 
15
+ import type { EncryptedPostMessagePayload } from './dweb-connect-crypto.js';
16
+ import { decryptPostMessagePayload, generateEphemeralKeyPair } from './dweb-connect-crypto.js';
17
+
15
18
  /** Options for initiating a DWeb Connect flow via popup. */
16
19
  export interface DWebConnectClientOptions {
17
20
  /** Base URL of the wallet app (e.g. "https://wallet.enbox.org"). */
@@ -76,6 +79,20 @@ async function initClient(options: DWebConnectClientOptions): Promise<ConnectRes
76
79
  );
77
80
  }
78
81
 
82
+ // Generate an ephemeral ECDH keypair for this connect session.
83
+ // The public key is sent to the wallet so it can encrypt the response
84
+ // containing delegate private keys and decryption material.
85
+ let dappKeyPair: CryptoKeyPair | undefined;
86
+ let dappPublicKeyBase64url: string | undefined;
87
+
88
+ try {
89
+ const ephemeral = await generateEphemeralKeyPair();
90
+ dappKeyPair = ephemeral.keyPair;
91
+ dappPublicKeyBase64url = ephemeral.publicKeyBase64url;
92
+ } catch {
93
+ // crypto.subtle unavailable (e.g. non-secure context) — skip encryption.
94
+ }
95
+
79
96
  return new Promise<ConnectResult | undefined>((resolve, reject) => {
80
97
  let settled = false;
81
98
 
@@ -113,11 +130,12 @@ async function initClient(options: DWebConnectClientOptions): Promise<ConnectRes
113
130
  const { type } = event.data ?? {};
114
131
 
115
132
  if (type === 'dweb-connect-loaded') {
116
- // Wallet is ready — send the authorization request.
133
+ // Wallet is ready — send the authorization request with ephemeral public key.
117
134
  popup.postMessage({
118
- type : 'dweb-connect-authorization-request',
135
+ type : 'dweb-connect-authorization-request',
119
136
  did,
120
- permissions : permissionRequests,
137
+ permissions : permissionRequests,
138
+ ephemeralPublicKey : dappPublicKeyBase64url,
121
139
  }, walletOrigin);
122
140
  return;
123
141
  }
@@ -126,6 +144,32 @@ async function initClient(options: DWebConnectClientOptions): Promise<ConnectRes
126
144
  clearInterval(pollClosed);
127
145
  cleanup();
128
146
 
147
+ // Handle encrypted response (wallet supports ECDH channel encryption).
148
+ const encrypted = event.data.encryptedPayload as EncryptedPostMessagePayload | undefined;
149
+ if (encrypted && dappKeyPair) {
150
+ decryptPostMessagePayload(encrypted, dappKeyPair).then((payload: Record<string, unknown>) => {
151
+ const p = payload as any;
152
+ if (!p.delegateDid || !p.grants) {
153
+ resolve(undefined);
154
+ return;
155
+ }
156
+ resolve({
157
+ delegatePortableDid : p.delegateDid as PortableDid,
158
+ delegateGrants : p.grants as DwnDataEncodedRecordsWriteMessage[],
159
+ connectedDid : p.connectedDid ?? did ?? (p.delegateDid as PortableDid).uri,
160
+ delegateDecryptionKeys : p.delegateDecryptionKeys ?? undefined,
161
+ delegateContextKeys : p.delegateContextKeys ?? undefined,
162
+ delegateMultiPartyProtocols : p.delegateMultiPartyProtocols ?? undefined,
163
+ sessionRevocations : p.sessionRevocations ?? undefined,
164
+ });
165
+ }).catch(() => {
166
+ // Decryption failed — treat as denied.
167
+ resolve(undefined);
168
+ });
169
+ return;
170
+ }
171
+
172
+ // Plaintext fallback (wallet doesn't support encryption yet).
129
173
  const {
130
174
  delegateDid,
131
175
  connectedDid: walletConnectedDid,
@@ -137,12 +181,10 @@ async function initClient(options: DWebConnectClientOptions): Promise<ConnectRes
137
181
  } = event.data;
138
182
 
139
183
  if (!delegateDid || !grants) {
140
- // User denied the request.
141
184
  resolve(undefined);
142
185
  return;
143
186
  }
144
187
 
145
- // connectedDid priority: wallet response > dapp-provided > delegate DID
146
188
  resolve({
147
189
  delegatePortableDid : delegateDid as PortableDid,
148
190
  delegateGrants : grants as DwnDataEncodedRecordsWriteMessage[],
@@ -0,0 +1,156 @@
1
+ /**
2
+ * ECDH + AES-256-GCM encryption for the DWeb Connect postMessage channel.
3
+ *
4
+ * Protects the authorization response (delegate private keys, decryption
5
+ * keys, grants) from same-origin XSS interception. Both the dapp and
6
+ * wallet generate ephemeral P-256 keypairs, exchange public keys during
7
+ * the handshake, derive a shared AES-256-GCM key via ECDH + HKDF, and
8
+ * encrypt/decrypt the payload.
9
+ *
10
+ * P-256 is used (instead of X25519) because `crypto.subtle.deriveKey`
11
+ * with ECDH requires a NIST curve in all major browsers. The keys are
12
+ * ephemeral — generated fresh for each connect session.
13
+ *
14
+ * @module
15
+ */
16
+
17
+ /** Exported public key as raw bytes (base64url-encoded for postMessage). */
18
+ export type ExportedPublicKey = string;
19
+
20
+ /** Encrypted payload sent via postMessage. */
21
+ export interface EncryptedPostMessagePayload {
22
+ /** The encrypted JSON payload as base64url. */
23
+ ciphertext: string;
24
+ /** AES-GCM initialization vector as base64url. */
25
+ iv: string;
26
+ /** The wallet's ephemeral P-256 public key as base64url raw bytes. */
27
+ walletPublicKey: string;
28
+ }
29
+
30
+ const ECDH_PARAMS: EcKeyGenParams = { name: 'ECDH', namedCurve: 'P-256' };
31
+ const AES_KEY_PARAMS: AesDerivedKeyParams = { name: 'AES-GCM', length: 256 };
32
+ const HKDF_INFO = new TextEncoder().encode('dweb-connect-v1');
33
+
34
+ /**
35
+ * Generate an ephemeral P-256 ECDH keypair for a single connect session.
36
+ *
37
+ * @returns The keypair and the public key as a base64url-encoded raw export.
38
+ */
39
+ export async function generateEphemeralKeyPair(): Promise<{
40
+ keyPair: CryptoKeyPair;
41
+ publicKeyBase64url: ExportedPublicKey;
42
+ }> {
43
+ const keyPair = await crypto.subtle.generateKey(ECDH_PARAMS, false, ['deriveKey']);
44
+ const rawPub = await crypto.subtle.exportKey('raw', keyPair.publicKey);
45
+ return { keyPair, publicKeyBase64url: toBase64url(new Uint8Array(rawPub)) };
46
+ }
47
+
48
+ /**
49
+ * Derive a shared AES-256-GCM key from a local ECDH private key and a
50
+ * remote public key.
51
+ */
52
+ async function deriveSharedKey(
53
+ localPrivateKey: CryptoKey,
54
+ remotePublicKeyBase64url: string,
55
+ ): Promise<CryptoKey> {
56
+ const remotePubRaw = fromBase64url(remotePublicKeyBase64url);
57
+ const remotePublicKey = await crypto.subtle.importKey(
58
+ 'raw', remotePubRaw, ECDH_PARAMS, false, [],
59
+ );
60
+
61
+ // ECDH → raw shared secret, then HKDF → AES-256-GCM key.
62
+ const sharedBits = await crypto.subtle.deriveBits(
63
+ { name: 'ECDH', public: remotePublicKey } as EcdhKeyDeriveParams,
64
+ localPrivateKey,
65
+ 256,
66
+ );
67
+
68
+ const hkdfKey = await crypto.subtle.importKey(
69
+ 'raw', sharedBits, 'HKDF', false, ['deriveKey'],
70
+ );
71
+
72
+ return crypto.subtle.deriveKey(
73
+ { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(32), info: HKDF_INFO },
74
+ hkdfKey,
75
+ AES_KEY_PARAMS,
76
+ false,
77
+ ['encrypt', 'decrypt'],
78
+ );
79
+ }
80
+
81
+ /**
82
+ * Encrypt a JSON-serializable payload for postMessage transport.
83
+ *
84
+ * Called by the **wallet** side. Uses the wallet's ephemeral private key
85
+ * and the dapp's ephemeral public key to derive the shared AES key.
86
+ *
87
+ * @returns The encrypted payload plus the wallet's public key (for the dapp to derive the same shared key).
88
+ */
89
+ export async function encryptPostMessagePayload(
90
+ payload: Record<string, unknown>,
91
+ walletKeyPair: CryptoKeyPair,
92
+ walletPublicKeyBase64url: string,
93
+ dappPublicKeyBase64url: string,
94
+ ): Promise<EncryptedPostMessagePayload> {
95
+ const sharedKey = await deriveSharedKey(walletKeyPair.privateKey, dappPublicKeyBase64url);
96
+ const iv = crypto.getRandomValues(new Uint8Array(12));
97
+ const plaintext = new TextEncoder().encode(JSON.stringify(payload));
98
+
99
+ const ciphertextBuf = await crypto.subtle.encrypt(
100
+ { name: 'AES-GCM', iv } as AesGcmParams,
101
+ sharedKey,
102
+ plaintext,
103
+ );
104
+
105
+ return {
106
+ ciphertext : toBase64url(new Uint8Array(ciphertextBuf)),
107
+ iv : toBase64url(iv),
108
+ walletPublicKey : walletPublicKeyBase64url,
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Decrypt a postMessage payload received from the wallet.
114
+ *
115
+ * Called by the **dapp** side. Uses the dapp's ephemeral private key
116
+ * and the wallet's ephemeral public key to derive the same shared key.
117
+ */
118
+ export async function decryptPostMessagePayload(
119
+ encrypted: EncryptedPostMessagePayload,
120
+ dappKeyPair: CryptoKeyPair,
121
+ ): Promise<Record<string, unknown>> {
122
+ const sharedKey = await deriveSharedKey(dappKeyPair.privateKey, encrypted.walletPublicKey);
123
+ const iv = fromBase64url(encrypted.iv);
124
+ const ciphertext = fromBase64url(encrypted.ciphertext);
125
+
126
+ const plaintextBuf = await crypto.subtle.decrypt(
127
+ { name: 'AES-GCM', iv } as AesGcmParams,
128
+ sharedKey,
129
+ ciphertext,
130
+ );
131
+
132
+ return JSON.parse(new TextDecoder().decode(plaintextBuf));
133
+ }
134
+
135
+ // ── Base64url helpers ────────────────────────────────────────────
136
+
137
+ function toBase64url(bytes: Uint8Array): string {
138
+ let binary = '';
139
+ for (let i = 0; i < bytes.length; i++) {
140
+ binary += String.fromCharCode(bytes[i]);
141
+ }
142
+ let b64 = btoa(binary).replace(/\+/g, '-').replace(/\//g, '_');
143
+ // Strip trailing padding without a backtracking-vulnerable regex.
144
+ while (b64.endsWith('=')) { b64 = b64.slice(0, -1); }
145
+ return b64;
146
+ }
147
+
148
+ function fromBase64url(str: string): Uint8Array {
149
+ const padded = str.replace(/-/g, '+').replace(/_/g, '/');
150
+ const binary = atob(padded);
151
+ const bytes = new Uint8Array(binary.length);
152
+ for (let i = 0; i < binary.length; i++) {
153
+ bytes[i] = binary.charCodeAt(i);
154
+ }
155
+ return bytes;
156
+ }
package/src/index.ts CHANGED
@@ -2,4 +2,6 @@ export * from './web-features.js';
2
2
  export { BrowserConnectHandler, DEFAULT_WALLETS } from './browser-connect-handler.js';
3
3
  export type { BrowserConnectHandlerOptions, WalletOption } from './browser-connect-handler.js';
4
4
  export { DWebConnect } from './dweb-connect-client.js';
5
- export type { DWebConnectClientOptions } from './dweb-connect-client.js';
5
+ export type { DWebConnectClientOptions } from './dweb-connect-client.js';
6
+ export { encryptPostMessagePayload, generateEphemeralKeyPair } from './dweb-connect-crypto.js';
7
+ export type { EncryptedPostMessagePayload } from './dweb-connect-crypto.js';