hd-wallet-ui 2.0.2 → 2.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hd-wallet-ui",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "HD Wallet modal UI — login, keys, identity, trust map, and security bond. Attach to any button in your app.",
5
5
  "type": "module",
6
6
  "main": "src/app.js",
@@ -40,7 +40,7 @@
40
40
  "buffer": "^6.0.3",
41
41
  "flatbuffers": "^25.9.23",
42
42
  "flatc-wasm": "^26.1.15",
43
- "hd-wallet-wasm": "^2.0.2",
43
+ "hd-wallet-wasm": "^2.0.3",
44
44
  "qrcode": "^1.5.3",
45
45
  "spacedatastandards.org": "^23.3.3-0.3.4",
46
46
  "vcard-cryptoperson": "^1.1.11"
package/src/app.js CHANGED
@@ -10,10 +10,6 @@
10
10
  // =============================================================================
11
11
 
12
12
  import initHDWallet, { Curve, getSigningKey, getEncryptionKey, buildSigningPath, buildEncryptionPath, WellKnownCoinType } from 'hd-wallet-wasm';
13
- import { x25519, ed25519 } from '@noble/curves/ed25519';
14
- import { secp256k1 } from '@noble/curves/secp256k1';
15
- import { p256 } from '@noble/curves/p256';
16
- import { sha256 as sha256Noble } from '@noble/hashes/sha256';
17
13
  import { keccak_256 } from '@noble/hashes/sha3';
18
14
  import QRCode from 'qrcode';
19
15
  import { Buffer } from 'buffer';
@@ -172,41 +168,55 @@ function bytesToBase64(bytes) {
172
168
  }
173
169
 
174
170
  // =============================================================================
175
- // SHA-256 and HKDF (WebCrypto-based)
171
+ // WASM-backed cryptographic helpers
176
172
  // =============================================================================
177
173
 
174
+ function hdWallet() {
175
+ if (!state.hdWalletModule) throw new Error('HD wallet WASM module not initialized');
176
+ return state.hdWalletModule;
177
+ }
178
+
179
+ function asUint8Array(value) {
180
+ if (value instanceof Uint8Array) return value;
181
+ if (ArrayBuffer.isView(value)) {
182
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
183
+ }
184
+ if (value instanceof ArrayBuffer) return new Uint8Array(value);
185
+ throw new Error('Expected byte array');
186
+ }
187
+
178
188
  async function sha256(data) {
179
- const hash = await crypto.subtle.digest('SHA-256', data);
180
- return new Uint8Array(hash);
189
+ return hdWallet().utils.sha256(asUint8Array(data));
181
190
  }
182
191
 
183
192
  async function hkdf(ikm, salt, info, length) {
184
- const key = await crypto.subtle.importKey('raw', ikm, 'HKDF', false, ['deriveBits']);
185
- const derived = await crypto.subtle.deriveBits(
186
- { name: 'HKDF', hash: 'SHA-256', salt, info },
187
- key,
188
- length * 8
189
- );
190
- return new Uint8Array(derived);
193
+ return hdWallet().utils.hkdf(asUint8Array(ikm), asUint8Array(salt), asUint8Array(info), length);
191
194
  }
192
195
 
193
196
  async function aesGcmEncryptJson(keyBytes, obj, aadStr) {
194
197
  if (!(keyBytes instanceof Uint8Array)) throw new Error('Invalid AES key');
195
- const iv = crypto.getRandomValues(new Uint8Array(12));
196
- const cryptoKey = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-GCM' }, false, ['encrypt']);
197
- const alg = { name: 'AES-GCM', iv };
198
- if (aadStr) alg.additionalData = new TextEncoder().encode(aadStr);
198
+ const iv = hdWallet().utils.getRandomBytes(12);
199
199
  const plaintext = new TextEncoder().encode(JSON.stringify(obj));
200
- const ciphertext = await crypto.subtle.encrypt(alg, cryptoKey, plaintext);
201
- return { iv, ciphertext: new Uint8Array(ciphertext) };
200
+ const aad = aadStr ? new TextEncoder().encode(aadStr) : new Uint8Array(0);
201
+ const { ciphertext, tag } = hdWallet().utils.aesGcm.encrypt(keyBytes, plaintext, iv, aad);
202
+ const sealed = new Uint8Array(ciphertext.length + tag.length);
203
+ sealed.set(ciphertext, 0);
204
+ sealed.set(tag, ciphertext.length);
205
+ return { iv, ciphertext: sealed };
202
206
  }
203
207
 
204
208
  async function aesGcmDecryptJson(keyBytes, iv, ciphertextBytes, aadStr) {
205
209
  if (!(keyBytes instanceof Uint8Array)) throw new Error('Invalid AES key');
206
- const cryptoKey = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-GCM' }, false, ['decrypt']);
207
- const alg = { name: 'AES-GCM', iv };
208
- if (aadStr) alg.additionalData = new TextEncoder().encode(aadStr);
209
- const plaintext = await crypto.subtle.decrypt(alg, cryptoKey, ciphertextBytes);
210
+ const sealed = asUint8Array(ciphertextBytes);
211
+ if (sealed.length <= 16) throw new Error('Ciphertext is too short');
212
+ const aad = aadStr ? new TextEncoder().encode(aadStr) : new Uint8Array(0);
213
+ const plaintext = hdWallet().utils.aesGcm.decrypt(
214
+ keyBytes,
215
+ sealed.subarray(0, sealed.length - 16),
216
+ sealed.subarray(sealed.length - 16),
217
+ asUint8Array(iv),
218
+ aad
219
+ );
210
220
  return JSON.parse(new TextDecoder().decode(plaintext));
211
221
  }
212
222
 
@@ -215,35 +225,39 @@ async function aesGcmDecryptJson(keyBytes, iv, ciphertextBytes, aadStr) {
215
225
  // =============================================================================
216
226
 
217
227
  function generateKeyPair(curveType) {
228
+ const w = hdWallet();
218
229
  if (curveType === Curve.SECP256K1) {
219
- const privateKey = secp256k1.utils.randomPrivateKey();
220
- const publicKey = secp256k1.getPublicKey(privateKey, true);
221
- return { privateKey, publicKey };
230
+ return generateEcKeyPair(w, Curve.SECP256K1, 32);
222
231
  }
223
232
  if (curveType === Curve.X25519) {
224
- const privateKey = x25519.utils.randomPrivateKey();
225
- const publicKey = x25519.getPublicKey(privateKey);
233
+ const privateKey = w.utils.getRandomBytes(32);
234
+ const publicKey = w.curves.x25519.publicKey(privateKey);
226
235
  return { privateKey, publicKey };
227
236
  }
228
237
  throw new Error(`Unsupported curve type: ${curveType}`);
229
238
  }
230
239
 
240
+ function generateEcKeyPair(w, curveType, privateKeyLength) {
241
+ let lastError = null;
242
+ for (let i = 0; i < 16; i += 1) {
243
+ const privateKey = w.utils.getRandomBytes(privateKeyLength);
244
+ try {
245
+ const publicKey = w.curves.publicKeyFromPrivate(privateKey, curveType);
246
+ return { privateKey, publicKey };
247
+ } catch (err) {
248
+ lastError = err;
249
+ privateKey.fill(0);
250
+ }
251
+ }
252
+ throw lastError || new Error('Failed to generate curve keypair');
253
+ }
254
+
231
255
  async function p256GenerateKeyPairAsync() {
232
- const keyPair = await crypto.subtle.generateKey(
233
- { name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']
234
- );
235
- const rawPublic = await crypto.subtle.exportKey('raw', keyPair.publicKey);
236
- const pkcs8Private = await crypto.subtle.exportKey('pkcs8', keyPair.privateKey);
237
- return { publicKey: new Uint8Array(rawPublic), privateKey: new Uint8Array(pkcs8Private) };
256
+ return generateEcKeyPair(hdWallet(), Curve.P256, 32);
238
257
  }
239
258
 
240
259
  async function p384GenerateKeyPairAsync() {
241
- const keyPair = await crypto.subtle.generateKey(
242
- { name: 'ECDSA', namedCurve: 'P-384' }, true, ['sign', 'verify']
243
- );
244
- const rawPublic = await crypto.subtle.exportKey('raw', keyPair.publicKey);
245
- const pkcs8Private = await crypto.subtle.exportKey('pkcs8', keyPair.privateKey);
246
- return { publicKey: new Uint8Array(rawPublic), privateKey: new Uint8Array(pkcs8Private) };
260
+ return generateEcKeyPair(hdWallet(), Curve.P384, 48);
247
261
  }
248
262
 
249
263
  // =============================================================================
@@ -437,7 +451,7 @@ function deriveKeysFromHDRoot(hdRoot) {
437
451
 
438
452
  // SOL signing key m/44'/501'/0'/0/0 — ed25519
439
453
  const solSigning = getSigningKey(hdRoot, 501, 0, 0);
440
- const ed25519PubKey = ed25519.getPublicKey(solSigning.privateKey);
454
+ const ed25519PubKey = hdWallet().curves.ed25519.publicKeyFromSeed(solSigning.privateKey);
441
455
 
442
456
  return {
443
457
  secp256k1: { privateKey: btcSigning.privateKey, publicKey: btcSigning.publicKey },
@@ -470,7 +484,7 @@ function deriveAllAddressesFromHD() {
470
484
  const solPath = buildSigningPath(501, 0, 0);
471
485
  const solDerived = state.hdRoot.derivePath(solPath);
472
486
  const solPrivKey = solDerived.privateKey();
473
- solAddress = generateSolAddress(ed25519.getPublicKey(solPrivKey));
487
+ solAddress = generateSolAddress(hdWallet().curves.ed25519.publicKeyFromSeed(solPrivKey));
474
488
  } catch (e) {
475
489
  console.error('Failed to derive SOL address:', e);
476
490
  }
@@ -549,7 +563,7 @@ function deriveAddressForPath(coinType, account, index) {
549
563
  if (coinType === 501) {
550
564
  // Solana: ed25519
551
565
  const privKey = derived.privateKey();
552
- const pubKey = ed25519.getPublicKey(privKey);
566
+ const pubKey = hdWallet().curves.ed25519.publicKeyFromSeed(privKey);
553
567
  return { address: generateSolAddress(pubKey), publicKey: pubKey, path };
554
568
  }
555
569
 
@@ -2182,7 +2196,7 @@ function deriveKeyFromPath(path) {
2182
2196
 
2183
2197
  function deriveX25519FromSeed(seed) {
2184
2198
  const privateKey = new Uint8Array(seed);
2185
- const publicKey = x25519.getPublicKey(privateKey);
2199
+ const publicKey = hdWallet().curves.x25519.publicKey(privateKey);
2186
2200
  return {
2187
2201
  privateKey,
2188
2202
  publicKey: new Uint8Array(publicKey),
@@ -2191,7 +2205,7 @@ function deriveX25519FromSeed(seed) {
2191
2205
 
2192
2206
  function deriveSecp256k1FromSeed(seed) {
2193
2207
  const privateKey = new Uint8Array(seed);
2194
- const publicKey = secp256k1.getPublicKey(privateKey, true);
2208
+ const publicKey = hdWallet().curves.publicKeyFromPrivate(privateKey, Curve.SECP256K1);
2195
2209
  return {
2196
2210
  privateKey,
2197
2211
  publicKey: new Uint8Array(publicKey),
@@ -2200,7 +2214,7 @@ function deriveSecp256k1FromSeed(seed) {
2200
2214
 
2201
2215
  function deriveP256FromSeed(seed) {
2202
2216
  const privateKey = new Uint8Array(seed);
2203
- const publicKey = p256.getPublicKey(privateKey, true);
2217
+ const publicKey = hdWallet().curves.publicKeyFromPrivate(privateKey, Curve.P256);
2204
2218
  return {
2205
2219
  privateKey,
2206
2220
  publicKey: new Uint8Array(publicKey),
@@ -2493,7 +2507,7 @@ function login(keys) {
2493
2507
  try {
2494
2508
  const sdnSigning = getSigningKey(state.hdRoot, 0, 0, 0);
2495
2509
  const sdnPrivKey = sdnSigning.privateKey;
2496
- const sdnPubKey = ed25519.getPublicKey(sdnPrivKey);
2510
+ const sdnPubKey = hdWallet().curves.ed25519.publicKeyFromSeed(sdnPrivKey);
2497
2511
  // Don't keep derived private key bytes around longer than needed.
2498
2512
  if (sdnPrivKey instanceof Uint8Array) sdnPrivKey.fill(0);
2499
2513
  const xpub = state.hdRoot.toXpub();
@@ -2507,7 +2521,7 @@ function login(keys) {
2507
2521
  : message;
2508
2522
  const signing = getSigningKey(state.hdRoot, 0, 0, 0);
2509
2523
  try {
2510
- return ed25519.sign(msgBytes, signing.privateKey);
2524
+ return hdWallet().curves.ed25519.sign(msgBytes, signing.privateKey);
2511
2525
  } finally {
2512
2526
  if (signing?.privateKey instanceof Uint8Array) signing.privateKey.fill(0);
2513
2527
  }
@@ -3400,7 +3414,7 @@ function signVCard(vcardText) {
3400
3414
 
3401
3415
  const body = getSignableBody(vcardText);
3402
3416
  const messageBytes = new TextEncoder().encode(body);
3403
- const signature = ed25519.sign(messageBytes, state.wallet.ed25519.privateKey);
3417
+ const signature = hdWallet().curves.ed25519.sign(messageBytes, state.wallet.ed25519.privateKey);
3404
3418
  const sigB64 = toBase64(signature);
3405
3419
 
3406
3420
  // Encode signature + derivation path (coinType=501, account=0, index=0)
@@ -3491,7 +3505,7 @@ function verifyVCardSignature(vcardText) {
3491
3505
  const pubKeyBytes = Uint8Array.from(atob(ed25519PubB64), c => c.charCodeAt(0));
3492
3506
 
3493
3507
  try {
3494
- const valid = ed25519.verify(sigBytes, messageBytes, pubKeyBytes);
3508
+ const valid = hdWallet().curves.ed25519.verify(messageBytes, sigBytes, pubKeyBytes);
3495
3509
  return { verified: valid, path, publicKey: ed25519PubB64, error: valid ? null : 'Signature invalid' };
3496
3510
  } catch (e) {
3497
3511
  return { verified: false, path, publicKey: ed25519PubB64, error: e.message };
@@ -5942,13 +5956,12 @@ export async function init(rootElement, options = {}) {
5942
5956
 
5943
5957
  // Auto-login with saved PKI keys if no stored wallet
5944
5958
  if (hasSavedKeys && !hasStoredWallet) {
5945
- const tempEd25519Seed = new Uint8Array(32);
5946
- crypto.getRandomValues(tempEd25519Seed);
5959
+ const tempEd25519Seed = hdWallet().utils.getRandomBytes(32);
5947
5960
  const tempKeys = {
5948
5961
  x25519: generateKeyPair(Curve.X25519),
5949
5962
  ed25519: {
5950
5963
  privateKey: tempEd25519Seed,
5951
- publicKey: ed25519.getPublicKey(tempEd25519Seed),
5964
+ publicKey: hdWallet().curves.ed25519.publicKeyFromSeed(tempEd25519Seed),
5952
5965
  },
5953
5966
  secp256k1: generateKeyPair(Curve.SECP256K1),
5954
5967
  p256: await p256GenerateKeyPairAsync(),
@@ -12,6 +12,8 @@
12
12
  * @module wallet-storage
13
13
  */
14
14
 
15
+ import initHDWallet from 'hd-wallet-wasm';
16
+
15
17
  // =============================================================================
16
18
  // Storage Keys
17
19
  // =============================================================================
@@ -31,6 +33,23 @@ const AES_GCM_IV_LENGTH = 12;
31
33
  // (This is still not a substitute for rate-limiting when an attacker can query online.)
32
34
  const PIN_PBKDF2_ITERATIONS = 600000;
33
35
  const LEGACY_PIN_PBKDF2_ITERATIONS = 100000;
36
+ const AES_GCM_TAG_LENGTH = 16;
37
+
38
+ let walletPromise = null;
39
+
40
+ function getWallet() {
41
+ if (!walletPromise) walletPromise = initHDWallet();
42
+ return walletPromise;
43
+ }
44
+
45
+ function asUint8Array(value) {
46
+ if (value instanceof Uint8Array) return value;
47
+ if (ArrayBuffer.isView(value)) {
48
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
49
+ }
50
+ if (value instanceof ArrayBuffer) return new Uint8Array(value);
51
+ throw new Error('Expected byte array');
52
+ }
34
53
 
35
54
  // =============================================================================
36
55
  // Storage Method Enum
@@ -73,10 +92,9 @@ function base64ToUint8Array(base64) {
73
92
  /**
74
93
  * Generate cryptographically secure random bytes
75
94
  */
76
- function generateRandomBytes(length) {
77
- const bytes = new Uint8Array(length);
78
- crypto.getRandomValues(bytes);
79
- return bytes;
95
+ async function generateRandomBytes(length) {
96
+ const wallet = await getWallet();
97
+ return wallet.utils.getRandomBytes(length);
80
98
  }
81
99
 
82
100
  // =============================================================================
@@ -95,29 +113,9 @@ function generateRandomBytes(length) {
95
113
  */
96
114
  async function hkdfDerive(inputKeyMaterial, salt, info, length) {
97
115
  const encoder = new TextEncoder();
98
-
99
- // Import the input key material
100
- const keyMaterial = await crypto.subtle.importKey(
101
- 'raw',
102
- inputKeyMaterial,
103
- 'HKDF',
104
- false,
105
- ['deriveBits']
106
- );
107
-
108
- // Derive bits using HKDF
109
- const derivedBits = await crypto.subtle.deriveBits(
110
- {
111
- name: 'HKDF',
112
- hash: 'SHA-256',
113
- salt: salt,
114
- info: encoder.encode(info)
115
- },
116
- keyMaterial,
117
- length * 8
118
- );
119
-
120
- return new Uint8Array(derivedBits);
116
+ const wallet = await getWallet();
117
+ const infoBytes = typeof info === 'string' ? encoder.encode(info) : asUint8Array(info);
118
+ return wallet.utils.hkdf(asUint8Array(inputKeyMaterial), asUint8Array(salt), infoBytes, length);
121
119
  }
122
120
 
123
121
  /**
@@ -174,31 +172,11 @@ async function deriveKeyFromPIN(pin, storedSalt, iterations = PIN_PBKDF2_ITERATI
174
172
  const pinBytes = encoder.encode(pin);
175
173
 
176
174
  // Use stored salt or generate new one
177
- const salt = storedSalt || generateRandomBytes(16);
178
-
179
- // Import PIN as key material
180
- const keyMaterial = await crypto.subtle.importKey(
181
- 'raw',
182
- pinBytes,
183
- 'PBKDF2',
184
- false,
185
- ['deriveBits']
186
- );
187
-
188
- // Use PBKDF2 with high iteration count for PIN (since PINs have low entropy)
189
- const derivedBits = await crypto.subtle.deriveBits(
190
- {
191
- name: 'PBKDF2',
192
- hash: 'SHA-256',
193
- salt: salt,
194
- iterations
195
- },
196
- keyMaterial,
197
- 256
198
- );
175
+ const salt = storedSalt || await generateRandomBytes(16);
176
+ const wallet = await getWallet();
199
177
 
200
178
  return {
201
- keyMaterial: new Uint8Array(derivedBits),
179
+ keyMaterial: wallet.utils.pbkdf2(pinBytes, salt, iterations, 32),
202
180
  salt
203
181
  };
204
182
  }
@@ -236,7 +214,7 @@ export async function isPRFLikelySupported() {
236
214
  /**
237
215
  * Generate WebAuthn challenge
238
216
  */
239
- function generateChallenge() {
217
+ async function generateChallenge() {
240
218
  return generateRandomBytes(32);
241
219
  }
242
220
 
@@ -274,8 +252,8 @@ export async function registerPasskey(options = {}) {
274
252
  userDisplayName = 'Wallet User'
275
253
  } = options;
276
254
 
277
- const challenge = generateChallenge();
278
- const userId = generateRandomBytes(16);
255
+ const challenge = await generateChallenge();
256
+ const userId = await generateRandomBytes(16);
279
257
  const prfInputs = createPRFInputs();
280
258
 
281
259
  const publicKeyCredentialCreationOptions = {
@@ -327,9 +305,7 @@ export async function registerPasskey(options = {}) {
327
305
  // PRF not available — derive key material from credential ID + a fixed salt.
328
306
  const rawId = new Uint8Array(credential.rawId);
329
307
  const salt = new TextEncoder().encode('wallet-storage-credid-fallback-v1');
330
- const base = await crypto.subtle.importKey('raw', rawId, 'HKDF', false, ['deriveBits']);
331
- const bits = await crypto.subtle.deriveBits({ name: 'HKDF', hash: 'SHA-256', salt, info: new Uint8Array(0) }, base, 256);
332
- keyMaterial = new Uint8Array(bits);
308
+ keyMaterial = await hkdfDerive(rawId, salt, new Uint8Array(0), 32);
333
309
  }
334
310
 
335
311
  return {
@@ -350,7 +326,7 @@ export async function authenticatePasskey(credentialId) {
350
326
  throw new Error('Passkeys are not supported on this device');
351
327
  }
352
328
 
353
- const challenge = generateChallenge();
329
+ const challenge = await generateChallenge();
354
330
  const prfInputs = createPRFInputs();
355
331
  const credentialIdBytes = base64ToUint8Array(credentialId);
356
332
 
@@ -390,9 +366,7 @@ export async function authenticatePasskey(credentialId) {
390
366
  // PRF not available — derive key material from credential ID + a fixed salt.
391
367
  const rawId = new Uint8Array(assertion.rawId);
392
368
  const salt = new TextEncoder().encode('wallet-storage-credid-fallback-v1');
393
- const base = await crypto.subtle.importKey('raw', rawId, 'HKDF', false, ['deriveBits']);
394
- const bits = await crypto.subtle.deriveBits({ name: 'HKDF', hash: 'SHA-256', salt, info: new Uint8Array(0) }, base, 256);
395
- keyMaterial = new Uint8Array(bits);
369
+ keyMaterial = await hkdfDerive(rawId, salt, new Uint8Array(0), 32);
396
370
  }
397
371
 
398
372
  return { keyMaterial, hasPRF };
@@ -408,47 +382,37 @@ export async function authenticatePasskey(credentialId) {
408
382
  async function encryptData(data, encryptionKey, aad) {
409
383
  const encoder = new TextEncoder();
410
384
  const plaintext = encoder.encode(JSON.stringify(data));
411
- const iv = generateRandomBytes(AES_GCM_IV_LENGTH);
412
-
413
- const cryptoKey = await crypto.subtle.importKey(
414
- 'raw',
415
- encryptionKey,
416
- { name: 'AES-GCM' },
417
- false,
418
- ['encrypt']
385
+ const iv = await generateRandomBytes(AES_GCM_IV_LENGTH);
386
+ const wallet = await getWallet();
387
+ const { ciphertext, tag } = wallet.utils.aesGcm.encrypt(
388
+ asUint8Array(encryptionKey),
389
+ plaintext,
390
+ iv,
391
+ aad ? asUint8Array(aad) : new Uint8Array(0)
419
392
  );
393
+ const sealed = new Uint8Array(ciphertext.length + tag.length);
394
+ sealed.set(ciphertext, 0);
395
+ sealed.set(tag, ciphertext.length);
420
396
 
421
- const alg = { name: 'AES-GCM', iv };
422
- if (aad) alg.additionalData = aad;
423
-
424
- const ciphertext = await crypto.subtle.encrypt(
425
- alg,
426
- cryptoKey,
427
- plaintext
428
- );
429
-
430
- return { iv, ciphertext: new Uint8Array(ciphertext) };
397
+ return { iv, ciphertext: sealed };
431
398
  }
432
399
 
433
400
  /**
434
401
  * Decrypt data using AES-256-GCM
435
402
  */
436
403
  async function decryptData(ciphertext, encryptionKey, iv, aad) {
437
- const cryptoKey = await crypto.subtle.importKey(
438
- 'raw',
439
- encryptionKey,
440
- { name: 'AES-GCM' },
441
- false,
442
- ['decrypt']
443
- );
444
-
445
- const alg = { name: 'AES-GCM', iv };
446
- if (aad) alg.additionalData = aad;
404
+ const sealed = asUint8Array(ciphertext);
405
+ if (sealed.length <= AES_GCM_TAG_LENGTH) {
406
+ throw new Error('Ciphertext is too short');
407
+ }
447
408
 
448
- const plaintext = await crypto.subtle.decrypt(
449
- alg,
450
- cryptoKey,
451
- ciphertext
409
+ const wallet = await getWallet();
410
+ const plaintext = wallet.utils.aesGcm.decrypt(
411
+ asUint8Array(encryptionKey),
412
+ sealed.subarray(0, sealed.length - AES_GCM_TAG_LENGTH),
413
+ sealed.subarray(sealed.length - AES_GCM_TAG_LENGTH),
414
+ asUint8Array(iv),
415
+ aad ? asUint8Array(aad) : new Uint8Array(0)
452
416
  );
453
417
 
454
418
  const decoder = new TextDecoder();