@metamask-previews/keyring-controller 22.0.2-preview-13f36eb8 → 22.0.2-preview-0013a1bd

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/CHANGELOG.md CHANGED
@@ -9,19 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
- - Added optional `SupportedKeyDerivationOptions` type parameter to the `ExportableKeyEncryptor` type ([#5963](https://github.com/MetaMask/core/pull/5963))
13
- - This type parameter allows specifying the key derivation options supported by the injected encryptor.
14
-
15
- ### Changed
16
-
17
- - **BREAKING:** The `KeyringController` constructor now requires an encryptor supporting the `keyFromPassword`, `exportKey` and `generateSalt` methods ([#5963](https://github.com/MetaMask/core/pull/5963))
18
-
19
- ### Removed
20
-
21
- - **BREAKING:** The `cacheEncryptionKey` parameter has been removed from the `KeyringController` constructor options ([#5963](https://github.com/MetaMask/core/pull/5963))
22
- - This parameter was previously used to enable encryption key in-memory caching, but it is no longer needed as the controller now always uses the latest encryption key.
23
- - **BREAKING:** The `submitEncryptionKey` method does not accept an `encryptionSalt` argument anymore ([#5963](https://github.com/MetaMask/core/pull/5963))
24
- - The encryption salt is now always taken from the vault.
12
+ - Add support for envelope encryption ([#5940](https://github.com/MetaMask/core/pull/5940))
25
13
 
26
14
  ## [22.0.2]
27
15
 
@@ -36,7 +36,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
36
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
- var _KeyringController_instances, _KeyringController_controllerOperationMutex, _KeyringController_vaultOperationMutex, _KeyringController_keyringBuilders, _KeyringController_encryptor, _KeyringController_keyrings, _KeyringController_unsupportedKeyrings, _KeyringController_encryptionKey, _KeyringController_qrKeyringStateListener, _KeyringController_registerMessageHandlers, _KeyringController_getKeyringById, _KeyringController_getKeyringByIdOrDefault, _KeyringController_getKeyringMetadata, _KeyringController_getKeyringBuilderForType, _KeyringController_addQRKeyring, _KeyringController_subscribeToQRKeyringEvents, _KeyringController_unsubscribeFromQRKeyringsEvents, _KeyringController_createNewVaultWithKeyring, _KeyringController_deriveEncryptionKey, _KeyringController_useEncryptionKey, _KeyringController_verifySeedPhrase, _KeyringController_getUpdatedKeyrings, _KeyringController_getSerializedKeyrings, _KeyringController_getSessionState, _KeyringController_restoreSerializedKeyrings, _KeyringController_unlockKeyrings, _KeyringController_updateVault, _KeyringController_isNewEncryptionAvailable, _KeyringController_getAccountsFromKeyrings, _KeyringController_createKeyringWithFirstAccount, _KeyringController_newKeyring, _KeyringController_createKeyring, _KeyringController_clearKeyrings, _KeyringController_restoreKeyring, _KeyringController_destroyKeyring, _KeyringController_removeEmptyKeyrings, _KeyringController_assertNoDuplicateAccounts, _KeyringController_setUnlocked, _KeyringController_assertIsUnlocked, _KeyringController_persistOrRollback, _KeyringController_withRollback, _KeyringController_assertControllerMutexIsLocked, _KeyringController_withControllerLock, _KeyringController_withVaultLock;
39
+ var _KeyringController_instances, _KeyringController_controllerOperationMutex, _KeyringController_vaultOperationMutex, _KeyringController_keyringBuilders, _KeyringController_encryptor, _KeyringController_cacheEncryptionKey, _KeyringController_keyrings, _KeyringController_unsupportedKeyrings, _KeyringController_password, _KeyringController_encryptionKey, _KeyringController_qrKeyringStateListener, _KeyringController_registerMessageHandlers, _KeyringController_getKeyringById, _KeyringController_getKeyringByIdOrDefault, _KeyringController_getKeyringMetadata, _KeyringController_getKeyringBuilderForType, _KeyringController_addQRKeyring, _KeyringController_subscribeToQRKeyringEvents, _KeyringController_unsubscribeFromQRKeyringsEvents, _KeyringController_createNewVaultWithKeyring, _KeyringController_updateCachedEncryptionKey, _KeyringController_verifySeedPhrase, _KeyringController_getUpdatedKeyrings, _KeyringController_getSerializedKeyrings, _KeyringController_getSessionState, _KeyringController_restoreSerializedKeyrings, _KeyringController_unlockKeyrings, _KeyringController_updateVault, _KeyringController_isNewEncryptionAvailable, _KeyringController_isEnvelopeEncryptionEnabled, _KeyringController_getAccountsFromKeyrings, _KeyringController_createKeyringWithFirstAccount, _KeyringController_newKeyring, _KeyringController_createKeyring, _KeyringController_clearKeyrings, _KeyringController_restoreKeyring, _KeyringController_destroyKeyring, _KeyringController_removeEmptyKeyrings, _KeyringController_assertNoDuplicateAccounts, _KeyringController_setUnlocked, _KeyringController_assertIsUnlocked, _KeyringController_persistOrRollback, _KeyringController_withRollback, _KeyringController_assertControllerMutexIsLocked, _KeyringController_withControllerLock, _KeyringController_withVaultLock;
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
41
  exports.KeyringController = exports.getDefaultKeyringState = exports.keyringBuilderFactory = exports.SignTypedDataVersion = exports.AccountImportStrategy = exports.isCustodyKeyring = exports.KeyringTypes = void 0;
42
42
  const util_1 = require("@ethereumjs/util");
@@ -168,6 +168,17 @@ function assertIsValidPassword(password) {
168
168
  throw new Error(constants_1.KeyringControllerError.InvalidEmptyPassword);
169
169
  }
170
170
  }
171
+ /**
172
+ * Assert that the provided cacheEncryptionKey is true.
173
+ *
174
+ * @param cacheEncryptionKey - The cacheEncryptionKey to check.
175
+ * @throws If the cacheEncryptionKey is not true.
176
+ */
177
+ function assertIsCacheEncryptionKeyTrue(cacheEncryptionKey) {
178
+ if (!cacheEncryptionKey) {
179
+ throw new Error(constants_1.KeyringControllerError.CacheEncryptionKeyDisabled);
180
+ }
181
+ }
171
182
  /**
172
183
  * Checks if the provided value is a serialized keyrings array.
173
184
  *
@@ -258,6 +269,7 @@ class KeyringController extends base_controller_1.BaseController {
258
269
  keyrings: { persist: false, anonymous: false },
259
270
  encryptionKey: { persist: false, anonymous: false },
260
271
  encryptionSalt: { persist: false, anonymous: false },
272
+ encryptedEncryptionKey: { persist: true, anonymous: false },
261
273
  },
262
274
  messenger,
263
275
  state: {
@@ -270,17 +282,24 @@ class KeyringController extends base_controller_1.BaseController {
270
282
  _KeyringController_vaultOperationMutex.set(this, new async_mutex_1.Mutex());
271
283
  _KeyringController_keyringBuilders.set(this, void 0);
272
284
  _KeyringController_encryptor.set(this, void 0);
285
+ _KeyringController_cacheEncryptionKey.set(this, void 0);
273
286
  _KeyringController_keyrings.set(this, void 0);
274
287
  _KeyringController_unsupportedKeyrings.set(this, void 0);
288
+ _KeyringController_password.set(this, void 0);
275
289
  _KeyringController_encryptionKey.set(this, void 0);
276
290
  _KeyringController_qrKeyringStateListener.set(this, void 0);
277
291
  __classPrivateFieldSet(this, _KeyringController_keyringBuilders, keyringBuilders
278
292
  ? keyringBuilders.concat(defaultKeyringBuilders)
279
293
  : defaultKeyringBuilders, "f");
280
- assertIsExportableKeyEncryptor(encryptor);
281
294
  __classPrivateFieldSet(this, _KeyringController_encryptor, encryptor, "f");
282
295
  __classPrivateFieldSet(this, _KeyringController_keyrings, [], "f");
283
296
  __classPrivateFieldSet(this, _KeyringController_unsupportedKeyrings, [], "f");
297
+ // This option allows the controller to cache an exported key
298
+ // for use in decrypting and encrypting data without password
299
+ __classPrivateFieldSet(this, _KeyringController_cacheEncryptionKey, Boolean(options.cacheEncryptionKey), "f");
300
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
301
+ assertIsExportableKeyEncryptor(encryptor);
302
+ }
284
303
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_registerMessageHandlers).call(this);
285
304
  }
286
305
  /**
@@ -350,9 +369,11 @@ class KeyringController extends base_controller_1.BaseController {
350
369
  * @param password - Password to unlock keychain.
351
370
  * @param seed - A BIP39-compliant seed phrase as Uint8Array,
352
371
  * either as a string or an array of UTF-8 bytes that represent the string.
372
+ * @param encryptionKey - Optional encryption key to encrypt the new vault. If
373
+ * set, envelope encryption will be used.
353
374
  * @returns Promise resolving when the operation ends successfully.
354
375
  */
355
- async createNewVaultAndRestore(password, seed) {
376
+ async createNewVaultAndRestore(password, seed, encryptionKey) {
356
377
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_persistOrRollback).call(this, async () => {
357
378
  assertIsValidPassword(password);
358
379
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_createNewVaultWithKeyring).call(this, password, {
@@ -361,25 +382,28 @@ class KeyringController extends base_controller_1.BaseController {
361
382
  mnemonic: seed,
362
383
  numberOfAccounts: 1,
363
384
  },
364
- });
385
+ }, encryptionKey);
365
386
  });
366
387
  }
367
388
  /**
368
389
  * Create a new vault and primary keyring.
369
390
  *
370
- * This only works if keyrings are empty. If there is a pre-existing unlocked vault, calling this will have no effect.
371
- * If there is a pre-existing locked vault, it will be replaced.
391
+ * This only works if keyrings are empty. If there is a pre-existing unlocked
392
+ * vault, calling this will have no effect. If there is a pre-existing locked
393
+ * vault, it will be replaced.
372
394
  *
373
395
  * @param password - Password to unlock the new vault.
396
+ * @param encryptionKey - Optional encryption key to encrypt the new vault. If
397
+ * set, envelope encryption will be used.
374
398
  * @returns Promise resolving when the operation ends successfully.
375
399
  */
376
- async createNewVaultAndKeychain(password) {
400
+ async createNewVaultAndKeychain(password, encryptionKey) {
377
401
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_persistOrRollback).call(this, async () => {
378
402
  const accounts = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getAccountsFromKeyrings).call(this);
379
403
  if (!accounts.length) {
380
404
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_createNewVaultWithKeyring).call(this, password, {
381
405
  type: KeyringTypes.hd,
382
- });
406
+ }, encryptionKey);
383
407
  }
384
408
  });
385
409
  }
@@ -408,7 +432,16 @@ class KeyringController extends base_controller_1.BaseController {
408
432
  if (!this.state.vault) {
409
433
  throw new Error(constants_1.KeyringControllerError.VaultError);
410
434
  }
411
- await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decrypt(password, this.state.vault);
435
+ if (this.state.encryptedEncryptionKey) {
436
+ // Envelope encryption mode.
437
+ assertIsExportableKeyEncryptor(__classPrivateFieldGet(this, _KeyringController_encryptor, "f"));
438
+ const decryptedEncryptionKey = (await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decrypt(password, this.state.encryptedEncryptionKey));
439
+ const importedKey = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(decryptedEncryptionKey);
440
+ await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithKey(importedKey, this.state.vault);
441
+ }
442
+ else {
443
+ await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decrypt(password, this.state.vault);
444
+ }
412
445
  }
413
446
  /**
414
447
  * Returns the status of the vault.
@@ -597,7 +630,7 @@ class KeyringController extends base_controller_1.BaseController {
597
630
  try {
598
631
  wallet = ethereumjs_wallet_1.thirdparty.fromEtherWallet(input, password);
599
632
  }
600
- catch (e) {
633
+ catch {
601
634
  wallet = wallet || (await ethereumjs_wallet_1.default.fromV3(input, password, true));
602
635
  }
603
636
  privateKey = (0, utils_1.bytesToHex)(wallet.getPrivateKey());
@@ -655,6 +688,7 @@ class KeyringController extends base_controller_1.BaseController {
655
688
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
656
689
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
657
690
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unsubscribeFromQRKeyringsEvents).call(this);
691
+ __classPrivateFieldSet(this, _KeyringController_password, undefined, "f");
658
692
  __classPrivateFieldSet(this, _KeyringController_encryptionKey, undefined, "f");
659
693
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_clearKeyrings).call(this);
660
694
  this.update((state) => {
@@ -836,10 +870,33 @@ class KeyringController extends base_controller_1.BaseController {
836
870
  * @returns Promise resolving when the operation completes.
837
871
  */
838
872
  changePassword(password) {
873
+ return this.changePasswordAndEncryptionKey(password);
874
+ }
875
+ /**
876
+ * Changes the password and encryption key used to encrypt the vault.
877
+ *
878
+ * @param password - The new password.
879
+ * @param encryptionKey - The new encryption key. If omitted, the encryption
880
+ * key will not be changed.
881
+ * @returns Promise resolving when the operation completes.
882
+ */
883
+ changePasswordAndEncryptionKey(password, encryptionKey) {
839
884
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
840
885
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_persistOrRollback).call(this, async () => {
841
886
  assertIsValidPassword(password);
842
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, password);
887
+ // Update password.
888
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
889
+ // Update encryption key.
890
+ __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_updateCachedEncryptionKey).call(this, encryptionKey);
891
+ // We need to clear encryption key and salt from state
892
+ // to force the controller to re-encrypt the vault using
893
+ // the new password.
894
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
895
+ this.update((state) => {
896
+ delete state.encryptionKey;
897
+ delete state.encryptionSalt;
898
+ });
899
+ }
843
900
  });
844
901
  }
845
902
  /**
@@ -847,13 +904,12 @@ class KeyringController extends base_controller_1.BaseController {
847
904
  * using the given encryption key and salt.
848
905
  *
849
906
  * @param encryptionKey - Key to unlock the keychain.
907
+ * @param encryptionSalt - Salt to unlock the keychain.
850
908
  * @returns Promise resolving when the operation completes.
851
909
  */
852
- async submitEncryptionKey(encryptionKey) {
910
+ async submitEncryptionKey(encryptionKey, encryptionSalt) {
853
911
  const { newMetadata } = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
854
- const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, {
855
- exportedEncryptionKey: encryptionKey,
856
- });
912
+ const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, undefined, encryptionKey, encryptionSalt);
857
913
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setUnlocked).call(this);
858
914
  return result;
859
915
  });
@@ -881,7 +937,7 @@ class KeyringController extends base_controller_1.BaseController {
881
937
  */
882
938
  async submitPassword(password) {
883
939
  const { newMetadata } = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
884
- const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, { password });
940
+ const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, password);
885
941
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setUnlocked).call(this);
886
942
  return result;
887
943
  });
@@ -891,12 +947,6 @@ class KeyringController extends base_controller_1.BaseController {
891
947
  // can attempt to upgrade the vault.
892
948
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
893
949
  if (newMetadata || __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_isNewEncryptionAvailable).call(this)) {
894
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, password, {
895
- // If the vault is being upgraded, we want to ignore the metadata
896
- // that is already in the vault, so we can effectively
897
- // re-encrypt the vault with the new encryption config.
898
- ignoreVaultKeyMetadata: true,
899
- });
900
950
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_updateVault).call(this);
901
951
  }
902
952
  });
@@ -1151,7 +1201,7 @@ class KeyringController extends base_controller_1.BaseController {
1151
1201
  }
1152
1202
  }
1153
1203
  exports.KeyringController = KeyringController;
1154
- _KeyringController_controllerOperationMutex = new WeakMap(), _KeyringController_vaultOperationMutex = new WeakMap(), _KeyringController_keyringBuilders = new WeakMap(), _KeyringController_encryptor = new WeakMap(), _KeyringController_keyrings = new WeakMap(), _KeyringController_unsupportedKeyrings = new WeakMap(), _KeyringController_encryptionKey = new WeakMap(), _KeyringController_qrKeyringStateListener = new WeakMap(), _KeyringController_instances = new WeakSet(), _KeyringController_registerMessageHandlers = function _KeyringController_registerMessageHandlers() {
1204
+ _KeyringController_controllerOperationMutex = new WeakMap(), _KeyringController_vaultOperationMutex = new WeakMap(), _KeyringController_keyringBuilders = new WeakMap(), _KeyringController_encryptor = new WeakMap(), _KeyringController_cacheEncryptionKey = new WeakMap(), _KeyringController_keyrings = new WeakMap(), _KeyringController_unsupportedKeyrings = new WeakMap(), _KeyringController_password = new WeakMap(), _KeyringController_encryptionKey = new WeakMap(), _KeyringController_qrKeyringStateListener = new WeakMap(), _KeyringController_instances = new WeakSet(), _KeyringController_registerMessageHandlers = function _KeyringController_registerMessageHandlers() {
1155
1205
  this.messagingSystem.registerActionHandler(`${name}:signMessage`, this.signMessage.bind(this));
1156
1206
  this.messagingSystem.registerActionHandler(`${name}:signEip7702Authorization`, this.signEip7702Authorization.bind(this));
1157
1207
  this.messagingSystem.registerActionHandler(`${name}:signPersonalMessage`, this.signPersonalMessage.bind(this));
@@ -1220,9 +1270,10 @@ async function _KeyringController_addQRKeyring() {
1220
1270
  * @param keyring - A object containing the params to instantiate a new keyring.
1221
1271
  * @param keyring.type - The keyring type.
1222
1272
  * @param keyring.opts - Optional parameters required to instantiate the keyring.
1273
+ * @param encryptionKey - Optional encryption key to encrypt the vault.
1223
1274
  * @returns A promise that resolves to the state.
1224
1275
  */
1225
- async function _KeyringController_createNewVaultWithKeyring(password, keyring) {
1276
+ async function _KeyringController_createNewVaultWithKeyring(password, keyring, encryptionKey) {
1226
1277
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertControllerMutexIsLocked).call(this);
1227
1278
  if (typeof password !== 'string') {
1228
1279
  throw new TypeError(constants_1.KeyringControllerError.WrongPasswordType);
@@ -1231,56 +1282,21 @@ async function _KeyringController_createNewVaultWithKeyring(password, keyring) {
1231
1282
  delete state.encryptionKey;
1232
1283
  delete state.encryptionSalt;
1233
1284
  });
1234
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, password);
1285
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
1286
+ __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_updateCachedEncryptionKey).call(this, encryptionKey);
1235
1287
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_clearKeyrings).call(this);
1236
1288
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_createKeyringWithFirstAccount).call(this, keyring.type, keyring.opts);
1237
1289
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setUnlocked).call(this);
1238
- }, _KeyringController_deriveEncryptionKey =
1239
- /**
1240
- * Derive the vault encryption key from the provided password, and
1241
- * assign it to the class variable for later use with cryptographic
1242
- * functions.
1243
- *
1244
- * When the controller has a vault in its state, the key is derived
1245
- * using the salt from the vault. If the vault is empty, a new salt
1246
- * is generated and used to derive the key.
1247
- *
1248
- * @param password - The password to use for decryption or derivation.
1249
- * @param options - Options for the key derivation.
1250
- * @param options.ignoreVaultKeyMetadata - Whether to ignore the vault key metadata
1251
- */
1252
- async function _KeyringController_deriveEncryptionKey(password, options = {
1253
- ignoreVaultKeyMetadata: false,
1254
- }) {
1255
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertControllerMutexIsLocked).call(this);
1256
- const { vault } = this.state;
1257
- if (typeof password !== 'string') {
1258
- throw new TypeError(constants_1.KeyringControllerError.WrongPasswordType);
1259
- }
1260
- let salt, keyMetadata;
1261
- if (vault && !options.ignoreVaultKeyMetadata) {
1262
- const parsedVault = JSON.parse(vault);
1263
- salt = parsedVault.salt;
1264
- keyMetadata = parsedVault.keyMetadata;
1290
+ }, _KeyringController_updateCachedEncryptionKey = function _KeyringController_updateCachedEncryptionKey(encryptionKey) {
1291
+ if (!encryptionKey && this.state.encryptedEncryptionKey) {
1292
+ // If no encryption key is provided and we are in envelope encryption
1293
+ // mode, use the cached encryption key. This case occurs when we call
1294
+ // change password without providing a new encryption key.
1295
+ __classPrivateFieldSet(this, _KeyringController_encryptionKey, this.state.encryptionKey, "f");
1265
1296
  }
1266
1297
  else {
1267
- salt = __classPrivateFieldGet(this, _KeyringController_encryptor, "f").generateSalt();
1298
+ __classPrivateFieldSet(this, _KeyringController_encryptionKey, encryptionKey, "f");
1268
1299
  }
1269
- const exportedEncryptionKey = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").exportKey(await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").keyFromPassword(password, salt, true, keyMetadata));
1270
- __classPrivateFieldSet(this, _KeyringController_encryptionKey, {
1271
- salt,
1272
- exported: exportedEncryptionKey,
1273
- }, "f");
1274
- }, _KeyringController_useEncryptionKey = function _KeyringController_useEncryptionKey(encryptionKey, encryptionSalt) {
1275
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertControllerMutexIsLocked).call(this);
1276
- if (typeof encryptionKey !== 'string' ||
1277
- typeof encryptionSalt !== 'string') {
1278
- throw new TypeError(constants_1.KeyringControllerError.WrongEncryptionKeyType);
1279
- }
1280
- __classPrivateFieldSet(this, _KeyringController_encryptionKey, {
1281
- salt: encryptionSalt,
1282
- exported: encryptionKey,
1283
- }, "f");
1284
1300
  }, _KeyringController_verifySeedPhrase =
1285
1301
  /**
1286
1302
  * Internal non-exclusive method to verify the seed phrase.
@@ -1369,6 +1385,7 @@ async function _KeyringController_getSerializedKeyrings({ includeUnsupported } =
1369
1385
  async function _KeyringController_getSessionState() {
1370
1386
  return {
1371
1387
  keyrings: await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getSerializedKeyrings).call(this),
1388
+ password: __classPrivateFieldGet(this, _KeyringController_password, "f"),
1372
1389
  encryptionKey: __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f"),
1373
1390
  };
1374
1391
  }, _KeyringController_restoreSerializedKeyrings =
@@ -1398,28 +1415,63 @@ async function _KeyringController_restoreSerializedKeyrings(serializedKeyrings)
1398
1415
  * Unlock Keyrings, decrypting the vault and deserializing all
1399
1416
  * keyrings contained in it, using a password or an encryption key with salt.
1400
1417
  *
1401
- * @param credentials - The credentials to unlock the keyrings.
1418
+ * @param password - The keyring controller password.
1419
+ * @param encryptionKey - An exported key string to unlock keyrings with.
1420
+ * @param encryptionSalt - The salt used to encrypt the vault.
1402
1421
  * @returns A promise resolving to the deserialized keyrings array.
1403
1422
  */
1404
- async function _KeyringController_unlockKeyrings(credentials) {
1423
+ async function _KeyringController_unlockKeyrings(password, encryptionKey, encryptionSalt) {
1405
1424
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withVaultLock).call(this, async () => {
1406
- if (!this.state.vault) {
1425
+ const { vault: encryptedVault, encryptedEncryptionKey } = this.state;
1426
+ if (!encryptedVault) {
1407
1427
  throw new Error(constants_1.KeyringControllerError.VaultError);
1408
1428
  }
1409
- const parsedEncryptedVault = JSON.parse(this.state.vault);
1410
- if ('password' in credentials) {
1411
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, credentials.password);
1429
+ let vault;
1430
+ const updatedState = {};
1431
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
1432
+ assertIsExportableKeyEncryptor(__classPrivateFieldGet(this, _KeyringController_encryptor, "f"));
1433
+ if (password) {
1434
+ if (encryptedEncryptionKey) {
1435
+ const decryptedEncryptionKey = (await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decrypt(password, encryptedEncryptionKey));
1436
+ const importedKey = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(decryptedEncryptionKey);
1437
+ vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithKey(importedKey, encryptedVault);
1438
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
1439
+ updatedState.encryptionKey = decryptedEncryptionKey;
1440
+ }
1441
+ else {
1442
+ const result = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithDetail(password, encryptedVault);
1443
+ vault = result.vault;
1444
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
1445
+ updatedState.encryptionKey = result.exportedKeyString;
1446
+ updatedState.encryptionSalt = result.salt;
1447
+ }
1448
+ }
1449
+ else {
1450
+ const parsedEncryptedVault = JSON.parse(encryptedVault);
1451
+ if (encryptionSalt !== parsedEncryptedVault.salt) {
1452
+ throw new Error(constants_1.KeyringControllerError.ExpiredCredentials);
1453
+ }
1454
+ if (typeof encryptionKey !== 'string') {
1455
+ throw new TypeError(constants_1.KeyringControllerError.WrongPasswordType);
1456
+ }
1457
+ const importedKey = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(encryptionKey);
1458
+ vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithKey(importedKey, parsedEncryptedVault);
1459
+ // This call is required on the first call because encryptionKey
1460
+ // is not yet inside the memStore
1461
+ updatedState.encryptionKey = encryptionKey;
1462
+ // we can safely assume that encryptionSalt is defined here
1463
+ // because we compare it with the salt from the vault
1464
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1465
+ updatedState.encryptionSalt = encryptionSalt;
1466
+ }
1412
1467
  }
1413
1468
  else {
1414
- const { exportedEncryptionKey } = credentials;
1415
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_useEncryptionKey).call(this, exportedEncryptionKey, parsedEncryptedVault.salt);
1416
- }
1417
- const encryptionKey = __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")?.exported;
1418
- if (!encryptionKey) {
1419
- throw new Error(constants_1.KeyringControllerError.MissingCredentials);
1469
+ if (typeof password !== 'string') {
1470
+ throw new TypeError(constants_1.KeyringControllerError.WrongPasswordType);
1471
+ }
1472
+ vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decrypt(password, encryptedVault);
1473
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
1420
1474
  }
1421
- const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(encryptionKey);
1422
- const vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithKey(key, parsedEncryptedVault);
1423
1475
  if (!isSerializedKeyringsArray(vault)) {
1424
1476
  throw new Error(constants_1.KeyringControllerError.VaultDataError);
1425
1477
  }
@@ -1427,8 +1479,10 @@ async function _KeyringController_unlockKeyrings(credentials) {
1427
1479
  const updatedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getUpdatedKeyrings).call(this);
1428
1480
  this.update((state) => {
1429
1481
  state.keyrings = updatedKeyrings;
1430
- state.encryptionKey = encryptionKey;
1431
- state.encryptionSalt = parsedEncryptedVault.salt;
1482
+ if (updatedState.encryptionKey || updatedState.encryptionSalt) {
1483
+ state.encryptionKey = updatedState.encryptionKey;
1484
+ state.encryptionSalt = updatedState.encryptionSalt;
1485
+ }
1432
1486
  });
1433
1487
  return { keyrings, newMetadata };
1434
1488
  });
@@ -1436,41 +1490,84 @@ async function _KeyringController_unlockKeyrings(credentials) {
1436
1490
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withVaultLock).call(this, async () => {
1437
1491
  // Ensure no duplicate accounts are persisted.
1438
1492
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertNoDuplicateAccounts).call(this);
1439
- if (!__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")) {
1493
+ const { encryptionKey, encryptionSalt, vault } = this.state;
1494
+ // READ THIS CAREFULLY:
1495
+ // We do check if the vault is still considered up-to-date, if not, we would not re-use the
1496
+ // cached key and we will re-generate a new one (based on the password).
1497
+ //
1498
+ // This helps doing seamless updates of the vault. Useful in case we change some cryptographic
1499
+ // parameters to the KDF.
1500
+ const useCachedKey = encryptionKey && vault && __classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated?.(vault);
1501
+ if (!__classPrivateFieldGet(this, _KeyringController_password, "f") && !encryptionKey) {
1440
1502
  throw new Error(constants_1.KeyringControllerError.MissingCredentials);
1441
1503
  }
1442
1504
  const serializedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getSerializedKeyrings).call(this);
1443
1505
  if (!serializedKeyrings.some((keyring) => keyring.type === KeyringTypes.hd)) {
1444
1506
  throw new Error(constants_1.KeyringControllerError.NoHdKeyring);
1445
1507
  }
1446
- const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").exported);
1447
- const encryptedVault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encryptWithKey(key, serializedKeyrings);
1448
- if (__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").salt) {
1449
- // We need to include the salt used to derive
1450
- // the encryption key, to be able to derive it
1451
- // from password again.
1452
- encryptedVault.salt = __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").salt;
1508
+ const updatedState = {};
1509
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
1510
+ assertIsExportableKeyEncryptor(__classPrivateFieldGet(this, _KeyringController_encryptor, "f"));
1511
+ if (useCachedKey) {
1512
+ const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(encryptionKey);
1513
+ const vaultJSON = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encryptWithKey(key, serializedKeyrings);
1514
+ vaultJSON.salt = encryptionSalt;
1515
+ updatedState.vault = JSON.stringify(vaultJSON);
1516
+ }
1517
+ else if (__classPrivateFieldGet(this, _KeyringController_password, "f")) {
1518
+ if (__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")) {
1519
+ // Update cached key.
1520
+ updatedState.encryptionKey = __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f");
1521
+ // Encrypt key and update encrypted key.
1522
+ const encryptedEncryptionKey = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encrypt(__classPrivateFieldGet(this, _KeyringController_password, "f"), __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f"));
1523
+ updatedState.encryptedEncryptionKey = encryptedEncryptionKey;
1524
+ // Encrypt and update vault.
1525
+ const importedKey = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f"));
1526
+ const vaultJSON = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encryptWithKey(importedKey, serializedKeyrings);
1527
+ // Note that we don't need to explicitly append the salt to the
1528
+ // vault here as it's already stored as part of the encrypted
1529
+ // encryption key.
1530
+ updatedState.vault = JSON.stringify(vaultJSON);
1531
+ }
1532
+ else {
1533
+ const { vault: newVault, exportedKeyString } = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encryptWithDetail(__classPrivateFieldGet(this, _KeyringController_password, "f"), serializedKeyrings);
1534
+ updatedState.vault = newVault;
1535
+ updatedState.encryptionKey = exportedKeyString;
1536
+ }
1537
+ }
1538
+ }
1539
+ else {
1540
+ assertIsValidPassword(__classPrivateFieldGet(this, _KeyringController_password, "f"));
1541
+ updatedState.vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encrypt(__classPrivateFieldGet(this, _KeyringController_password, "f"), serializedKeyrings);
1542
+ }
1543
+ if (__classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_isEnvelopeEncryptionEnabled).call(this)) {
1544
+ assertIsCacheEncryptionKeyTrue(__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f"));
1545
+ }
1546
+ if (!updatedState.vault) {
1547
+ throw new Error(constants_1.KeyringControllerError.MissingVaultData);
1453
1548
  }
1454
- const updatedState = {
1455
- vault: JSON.stringify(encryptedVault),
1456
- encryptionKey: __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").exported,
1457
- encryptionSalt: __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").salt,
1458
- };
1459
1549
  const updatedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getUpdatedKeyrings).call(this);
1460
1550
  this.update((state) => {
1461
1551
  state.vault = updatedState.vault;
1462
1552
  state.keyrings = updatedKeyrings;
1463
- state.encryptionKey = updatedState.encryptionKey;
1464
- state.encryptionSalt = updatedState.encryptionSalt;
1553
+ if (updatedState.encryptionKey) {
1554
+ state.encryptionKey = updatedState.encryptionKey;
1555
+ state.encryptionSalt = JSON.parse(updatedState.vault).salt;
1556
+ }
1557
+ if (updatedState.encryptedEncryptionKey) {
1558
+ state.encryptedEncryptionKey = updatedState.encryptedEncryptionKey;
1559
+ }
1465
1560
  });
1466
1561
  return true;
1467
1562
  });
1468
1563
  }, _KeyringController_isNewEncryptionAvailable = function _KeyringController_isNewEncryptionAvailable() {
1469
1564
  const { vault } = this.state;
1470
- if (!vault || !__classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated) {
1565
+ if (!vault || !__classPrivateFieldGet(this, _KeyringController_password, "f") || !__classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated) {
1471
1566
  return false;
1472
1567
  }
1473
1568
  return !__classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated(vault);
1569
+ }, _KeyringController_isEnvelopeEncryptionEnabled = function _KeyringController_isEnvelopeEncryptionEnabled() {
1570
+ return this.state.encryptedEncryptionKey || __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f");
1474
1571
  }, _KeyringController_getAccountsFromKeyrings =
1475
1572
  /**
1476
1573
  * Retrieves all the accounts from keyrings instances
@@ -1555,7 +1652,8 @@ async function _KeyringController_createKeyring(type, data) {
1555
1652
  if (keyring.init) {
1556
1653
  await keyring.init();
1557
1654
  }
1558
- if (type === KeyringTypes.hd && (!(0, utils_1.isObject)(data) || !data.mnemonic)) {
1655
+ if (type === KeyringTypes.hd &&
1656
+ (!(0, utils_1.isObject)(data) || !data.mnemonic)) {
1559
1657
  if (!keyring.generateRandomMnemonic) {
1560
1658
  throw new Error(constants_1.KeyringControllerError.UnsupportedGenerateRandomMnemonic);
1561
1659
  }
@@ -1677,11 +1775,16 @@ async function _KeyringController_assertNoDuplicateAccounts(additionalKeyrings =
1677
1775
  }
1678
1776
  }, _KeyringController_persistOrRollback =
1679
1777
  /**
1680
- * Execute the given function after acquiring the controller lock
1681
- * and save the vault to state after it (only if needed), or rollback to their
1682
- * previous state in case of error.
1778
+ * Execute the given function after acquiring the controller lock and save the
1779
+ * vault to state after it (only if needed), or rollback to their previous
1780
+ * state in case of error.
1781
+ *
1782
+ * ATTENTION: The callback must **not** alter `controller.state`. Any state
1783
+ * change performed by the callback will not be rolled back on error.
1683
1784
  *
1684
- * @param callback - The function to execute.
1785
+ * @param callback - The function to execute. This callback must **not** alter
1786
+ * `controller.state`. Any state change performed by the callback will not be
1787
+ * rolled back on error.
1685
1788
  * @returns The result of the function.
1686
1789
  */
1687
1790
  async function _KeyringController_persistOrRollback(callback) {
@@ -1706,16 +1809,15 @@ async function _KeyringController_persistOrRollback(callback) {
1706
1809
  async function _KeyringController_withRollback(callback) {
1707
1810
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withControllerLock).call(this, async ({ releaseLock }) => {
1708
1811
  const currentSerializedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getSerializedKeyrings).call(this);
1709
- const currentEncryptionKey = __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")?.exported;
1710
- const currentEncryptionSalt = __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")?.salt;
1812
+ const currentPassword = __classPrivateFieldGet(this, _KeyringController_password, "f");
1813
+ const currentEncryptionKey = __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f");
1711
1814
  try {
1712
1815
  return await callback({ releaseLock });
1713
1816
  }
1714
1817
  catch (e) {
1715
- // Keyrings and encryption credentials are restored to their previous state
1716
- if (currentEncryptionKey && currentEncryptionSalt) {
1717
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_useEncryptionKey).call(this, currentEncryptionKey, currentEncryptionSalt);
1718
- }
1818
+ // Previous state is restored.
1819
+ __classPrivateFieldSet(this, _KeyringController_password, currentPassword, "f");
1820
+ __classPrivateFieldSet(this, _KeyringController_encryptionKey, currentEncryptionKey, "f");
1719
1821
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_restoreSerializedKeyrings).call(this, currentSerializedKeyrings);
1720
1822
  throw e;
1721
1823
  }