@enbox/browser 0.3.39 → 0.3.41
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.
- package/dist/browser.mjs +16 -16
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/browser-connect-handler.js +18 -8
- package/dist/esm/browser-connect-handler.js.map +1 -1
- package/dist/esm/dweb-connect-client.js +267 -190
- package/dist/esm/dweb-connect-client.js.map +1 -1
- package/dist/esm/dweb-connect-messages.js +54 -0
- package/dist/esm/dweb-connect-messages.js.map +1 -0
- package/dist/esm/dweb-connect-wallet.js +209 -0
- package/dist/esm/dweb-connect-wallet.js.map +1 -0
- package/dist/esm/index.js +9 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/types/browser-connect-handler.d.ts +1 -1
- package/dist/types/browser-connect-handler.d.ts.map +1 -1
- package/dist/types/dweb-connect-client.d.ts +123 -59
- package/dist/types/dweb-connect-client.d.ts.map +1 -1
- package/dist/types/dweb-connect-messages.d.ts +64 -0
- package/dist/types/dweb-connect-messages.d.ts.map +1 -0
- package/dist/types/dweb-connect-wallet.d.ts +101 -0
- package/dist/types/dweb-connect-wallet.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -4
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +9 -6
- package/src/browser-connect-handler.ts +20 -10
- package/src/dweb-connect-client.ts +327 -251
- package/src/dweb-connect-messages.ts +91 -0
- package/src/dweb-connect-wallet.ts +258 -0
- package/src/index.ts +24 -5
- package/dist/esm/dweb-connect-crypto.js +0 -112
- package/dist/esm/dweb-connect-crypto.js.map +0 -1
- package/dist/types/dweb-connect-crypto.d.ts +0 -52
- package/dist/types/dweb-connect-crypto.d.ts.map +0 -1
- package/src/dweb-connect-crypto.ts +0 -156
|
@@ -1,156 +0,0 @@
|
|
|
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, ['deriveBits']);
|
|
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.fromCodePoint(bytes[i]);
|
|
141
|
-
}
|
|
142
|
-
let b64 = btoa(binary).replaceAll('+', '-').replaceAll('/', '_');
|
|
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.replaceAll('-', '+').replaceAll('_', '/');
|
|
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.codePointAt(i)!;
|
|
154
|
-
}
|
|
155
|
-
return bytes;
|
|
156
|
-
}
|