@metamask-previews/keyring-controller 24.0.0-preview-70abd50a → 24.0.0-preview-1f85e85a

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
@@ -7,26 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ### Added
11
-
12
- - Added optional `EncryptionKey`, `SupportedKeyDerivationOptions` and `EncryptionResult` type parameters to the `KeyringController`, `ExportableKeyEncryptor` and `KeyringControllerOptions` types ([#5963](https://github.com/MetaMask/core/pull/5963))
13
- - This type parameter allows specifying the encryption key, key derivation options and encryption result types supported by the injected encryptor, defaulting to `@metamask/browser-passworder` types.
14
-
15
- ### Changed
16
-
17
- - **BREAKING:** The `KeyringController` constructor options now require an encryptor ([#5963](https://github.com/MetaMask/core/pull/5963))
18
- - The `encryptor` constructor option was previously optional and defaulted to an instance of `@metamask/browser-passworder`.
19
- - **BREAKING:** The `GenericEncryptor` and `ExportableKeyEncryptor` types have been merged into a single `Encryptor` type ([#5963](https://github.com/MetaMask/core/pull/5963))
20
-
21
- ### Removed
22
-
23
- - **BREAKING:** The `cacheEncryptionKey` parameter has been removed from the `KeyringController` constructor options ([#5963](https://github.com/MetaMask/core/pull/5963))
24
- - 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.
25
-
26
- ### Fixed
27
-
28
- - Fixed incorrect type for `decryptWithKey` method of `ExportableKeyEncryptor` ([#5963](https://github.com/MetaMask/core/pull/5963))
29
-
30
10
  ## [24.0.0]
31
11
 
32
12
  ### Changed
@@ -36,11 +36,12 @@ 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_registerMessageHandlers, _KeyringController_getKeyringById, _KeyringController_getKeyringByIdOrDefault, _KeyringController_getKeyringMetadata, _KeyringController_getKeyringBuilderForType, _KeyringController_createNewVaultWithKeyring, _KeyringController_deriveEncryptionKey, _KeyringController_setEncryptionKey, _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_registerMessageHandlers, _KeyringController_getKeyringById, _KeyringController_getKeyringByIdOrDefault, _KeyringController_getKeyringMetadata, _KeyringController_getKeyringBuilderForType, _KeyringController_createNewVaultWithKeyring, _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;
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");
43
43
  const base_controller_1 = require("@metamask/base-controller");
44
+ const encryptorUtils = __importStar(require("@metamask/browser-passworder"));
44
45
  const eth_hd_keyring_1 = require("@metamask/eth-hd-keyring");
45
46
  const eth_sig_util_1 = require("@metamask/eth-sig-util");
46
47
  const eth_simple_keyring_1 = __importDefault(require("@metamask/eth-simple-keyring"));
@@ -135,6 +136,23 @@ function assertHasUint8ArrayMnemonic(keyring) {
135
136
  throw new Error("Can't get mnemonic bytes from keyring");
136
137
  }
137
138
  }
139
+ /**
140
+ * Assert that the provided encryptor supports
141
+ * encryption and encryption key export.
142
+ *
143
+ * @param encryptor - The encryptor to check.
144
+ * @throws If the encryptor does not support key encryption.
145
+ */
146
+ function assertIsExportableKeyEncryptor(encryptor) {
147
+ if (!('importKey' in encryptor &&
148
+ typeof encryptor.importKey === 'function' &&
149
+ 'decryptWithKey' in encryptor &&
150
+ typeof encryptor.decryptWithKey === 'function' &&
151
+ 'encryptWithKey' in encryptor &&
152
+ typeof encryptor.encryptWithKey === 'function')) {
153
+ throw new Error(constants_1.KeyringControllerError.UnsupportedEncryptionKeyExport);
154
+ }
155
+ }
138
156
  /**
139
157
  * Assert that the provided password is a valid non-empty string.
140
158
  *
@@ -236,11 +254,12 @@ class KeyringController extends base_controller_1.BaseController {
236
254
  * @param options - Initial options used to configure this controller
237
255
  * @param options.encryptor - An optional object for defining encryption schemes.
238
256
  * @param options.keyringBuilders - Set a new name for account.
257
+ * @param options.cacheEncryptionKey - Whether to cache or not encryption key.
239
258
  * @param options.messenger - A restricted messenger.
240
259
  * @param options.state - Initial state to set on this controller.
241
260
  */
242
261
  constructor(options) {
243
- const { encryptor, keyringBuilders, messenger, state } = options;
262
+ const { encryptor = encryptorUtils, keyringBuilders, messenger, state, } = options;
244
263
  super({
245
264
  name,
246
265
  metadata: {
@@ -286,15 +305,22 @@ class KeyringController extends base_controller_1.BaseController {
286
305
  _KeyringController_vaultOperationMutex.set(this, new async_mutex_1.Mutex());
287
306
  _KeyringController_keyringBuilders.set(this, void 0);
288
307
  _KeyringController_encryptor.set(this, void 0);
308
+ _KeyringController_cacheEncryptionKey.set(this, void 0);
289
309
  _KeyringController_keyrings.set(this, void 0);
290
310
  _KeyringController_unsupportedKeyrings.set(this, void 0);
291
- _KeyringController_encryptionKey.set(this, void 0);
311
+ _KeyringController_password.set(this, void 0);
292
312
  __classPrivateFieldSet(this, _KeyringController_keyringBuilders, keyringBuilders
293
313
  ? keyringBuilders.concat(defaultKeyringBuilders)
294
314
  : defaultKeyringBuilders, "f");
295
315
  __classPrivateFieldSet(this, _KeyringController_encryptor, encryptor, "f");
296
316
  __classPrivateFieldSet(this, _KeyringController_keyrings, [], "f");
297
317
  __classPrivateFieldSet(this, _KeyringController_unsupportedKeyrings, [], "f");
318
+ // This option allows the controller to cache an exported key
319
+ // for use in decrypting and encrypting data without password
320
+ __classPrivateFieldSet(this, _KeyringController_cacheEncryptionKey, Boolean(options.cacheEncryptionKey), "f");
321
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
322
+ assertIsExportableKeyEncryptor(encryptor);
323
+ }
298
324
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_registerMessageHandlers).call(this);
299
325
  }
300
326
  /**
@@ -665,7 +691,7 @@ class KeyringController extends base_controller_1.BaseController {
665
691
  async setLocked() {
666
692
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
667
693
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
668
- __classPrivateFieldSet(this, _KeyringController_encryptionKey, undefined, "f");
694
+ __classPrivateFieldSet(this, _KeyringController_password, undefined, "f");
669
695
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_clearKeyrings).call(this);
670
696
  this.update((state) => {
671
697
  state.isUnlocked = false;
@@ -847,11 +873,22 @@ class KeyringController extends base_controller_1.BaseController {
847
873
  */
848
874
  changePassword(password) {
849
875
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
876
+ // If the password is the same, do nothing.
877
+ if (__classPrivateFieldGet(this, _KeyringController_password, "f") === password) {
878
+ return Promise.resolve();
879
+ }
850
880
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_persistOrRollback).call(this, async () => {
851
881
  assertIsValidPassword(password);
852
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, password, {
853
- ignoreExistingVault: true,
854
- });
882
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
883
+ // We need to clear encryption key and salt from state
884
+ // to force the controller to re-encrypt the vault using
885
+ // the new password.
886
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
887
+ this.update((state) => {
888
+ delete state.encryptionKey;
889
+ delete state.encryptionSalt;
890
+ });
891
+ }
855
892
  });
856
893
  }
857
894
  /**
@@ -860,15 +897,12 @@ class KeyringController extends base_controller_1.BaseController {
860
897
  * consistency with the vault salt.
861
898
  *
862
899
  * @param encryptionKey - Key to unlock the keychain.
863
- * @param keyDerivationSalt - Optional salt to unlock the keychain.
900
+ * @param encryptionSalt - Optional salt to unlock the keychain.
864
901
  * @returns Promise resolving when the operation completes.
865
902
  */
866
- async submitEncryptionKey(encryptionKey, keyDerivationSalt) {
903
+ async submitEncryptionKey(encryptionKey, encryptionSalt) {
867
904
  const { newMetadata } = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
868
- const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, {
869
- encryptionKey,
870
- keyDerivationSalt,
871
- });
905
+ const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, undefined, encryptionKey, encryptionSalt);
872
906
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setUnlocked).call(this);
873
907
  return result;
874
908
  });
@@ -895,8 +929,9 @@ class KeyringController extends base_controller_1.BaseController {
895
929
  async exportEncryptionKey() {
896
930
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
897
931
  return await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withControllerLock).call(this, async () => {
898
- assertIsEncryptionKeySet(__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")?.serialized);
899
- return __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").serialized;
932
+ const { encryptionKey } = this.state;
933
+ assertIsEncryptionKeySet(encryptionKey);
934
+ return encryptionKey;
900
935
  });
901
936
  }
902
937
  /**
@@ -908,7 +943,7 @@ class KeyringController extends base_controller_1.BaseController {
908
943
  */
909
944
  async submitPassword(password) {
910
945
  const { newMetadata } = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
911
- const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, { password });
946
+ const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, password);
912
947
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setUnlocked).call(this);
913
948
  return result;
914
949
  });
@@ -918,12 +953,6 @@ class KeyringController extends base_controller_1.BaseController {
918
953
  // can attempt to upgrade the vault.
919
954
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
920
955
  if (newMetadata || __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_isNewEncryptionAvailable).call(this)) {
921
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, password, {
922
- // If the vault is being upgraded, we want to ignore the metadata
923
- // that is already in the vault, so we can effectively
924
- // re-encrypt the vault with the new encryption config.
925
- ignoreExistingVault: true,
926
- });
927
956
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_updateVault).call(this);
928
957
  }
929
958
  });
@@ -986,7 +1015,7 @@ class KeyringController extends base_controller_1.BaseController {
986
1015
  }
987
1016
  }
988
1017
  exports.KeyringController = KeyringController;
989
- _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_instances = new WeakSet(), _KeyringController_registerMessageHandlers = function _KeyringController_registerMessageHandlers() {
1018
+ _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_instances = new WeakSet(), _KeyringController_registerMessageHandlers = function _KeyringController_registerMessageHandlers() {
990
1019
  this.messenger.registerActionHandler(`${name}:signMessage`, this.signMessage.bind(this));
991
1020
  this.messenger.registerActionHandler(`${name}:signEip7702Authorization`, this.signEip7702Authorization.bind(this));
992
1021
  this.messenger.registerActionHandler(`${name}:signPersonalMessage`, this.signPersonalMessage.bind(this));
@@ -1045,70 +1074,10 @@ async function _KeyringController_createNewVaultWithKeyring(password, keyring) {
1045
1074
  delete state.encryptionKey;
1046
1075
  delete state.encryptionSalt;
1047
1076
  });
1048
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, password, {
1049
- ignoreExistingVault: true,
1050
- });
1077
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
1051
1078
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_clearKeyrings).call(this);
1052
1079
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_createKeyringWithFirstAccount).call(this, keyring.type, keyring.opts);
1053
1080
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setUnlocked).call(this);
1054
- }, _KeyringController_deriveEncryptionKey =
1055
- /**
1056
- * Derive the vault encryption key from the provided password, and
1057
- * assign it to the instance variable for later use with cryptographic
1058
- * functions.
1059
- *
1060
- * When the controller has a vault in its state, the key is derived
1061
- * using the salt from the vault. If the vault is empty, a new salt
1062
- * is generated and used to derive the key.
1063
- *
1064
- * If `options.ignoreExistingVault` is set to `false`, the existing
1065
- * vault is completely ignored: the new key won't be able to decrypt
1066
- * the existing vault, and should be used to re-encrypt it.
1067
- *
1068
- * @param password - The password to use for decryption or derivation.
1069
- * @param options - Options for the key derivation.
1070
- * @param options.ignoreExistingVault - Whether to use the existing vault salt and key metadata
1071
- */
1072
- async function _KeyringController_deriveEncryptionKey(password, options = {
1073
- ignoreExistingVault: false,
1074
- }) {
1075
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertControllerMutexIsLocked).call(this);
1076
- const { vault } = this.state;
1077
- if (typeof password !== 'string') {
1078
- throw new TypeError(constants_1.KeyringControllerError.WrongPasswordType);
1079
- }
1080
- let serializedEncryptionKey, salt;
1081
- if (vault && !options.ignoreExistingVault) {
1082
- // The `decryptWithDetail` method is being used here instead of
1083
- // `keyFromPassword` + `exportKey` to let the encryptor handle
1084
- // any legacy encryption formats and metadata that might be
1085
- // present (or absent) in the vault.
1086
- const { exportedKeyString, salt: existingSalt } = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithDetail(password, vault);
1087
- serializedEncryptionKey = exportedKeyString;
1088
- salt = existingSalt;
1089
- }
1090
- else {
1091
- salt = __classPrivateFieldGet(this, _KeyringController_encryptor, "f").generateSalt();
1092
- serializedEncryptionKey = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").exportKey(await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").keyFromPassword(password, salt, true));
1093
- }
1094
- __classPrivateFieldSet(this, _KeyringController_encryptionKey, {
1095
- salt,
1096
- serialized: serializedEncryptionKey,
1097
- }, "f");
1098
- }, _KeyringController_setEncryptionKey = function _KeyringController_setEncryptionKey(encryptionKey, keyDerivationSalt) {
1099
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertControllerMutexIsLocked).call(this);
1100
- if (typeof encryptionKey !== 'string' ||
1101
- typeof keyDerivationSalt !== 'string') {
1102
- throw new TypeError(constants_1.KeyringControllerError.WrongEncryptionKeyType);
1103
- }
1104
- const { vault } = this.state;
1105
- if (vault && JSON.parse(vault).salt !== keyDerivationSalt) {
1106
- throw new Error(constants_1.KeyringControllerError.ExpiredCredentials);
1107
- }
1108
- __classPrivateFieldSet(this, _KeyringController_encryptionKey, {
1109
- salt: keyDerivationSalt,
1110
- serialized: encryptionKey,
1111
- }, "f");
1112
1081
  }, _KeyringController_verifySeedPhrase =
1113
1082
  /**
1114
1083
  * Internal non-exclusive method to verify the seed phrase.
@@ -1189,7 +1158,7 @@ async function _KeyringController_getSerializedKeyrings({ includeUnsupported } =
1189
1158
  return serializedKeyrings;
1190
1159
  }, _KeyringController_getSessionState =
1191
1160
  /**
1192
- * Get a snapshot of session data held by instance variables.
1161
+ * Get a snapshot of session data held by class variables.
1193
1162
  *
1194
1163
  * @returns An object with serialized keyrings, keyrings metadata,
1195
1164
  * and the user password.
@@ -1197,7 +1166,7 @@ async function _KeyringController_getSerializedKeyrings({ includeUnsupported } =
1197
1166
  async function _KeyringController_getSessionState() {
1198
1167
  return {
1199
1168
  keyrings: await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getSerializedKeyrings).call(this),
1200
- encryptionKey: __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f"),
1169
+ password: __classPrivateFieldGet(this, _KeyringController_password, "f"),
1201
1170
  };
1202
1171
  }, _KeyringController_restoreSerializedKeyrings =
1203
1172
  /**
@@ -1226,27 +1195,54 @@ async function _KeyringController_restoreSerializedKeyrings(serializedKeyrings)
1226
1195
  * Unlock Keyrings, decrypting the vault and deserializing all
1227
1196
  * keyrings contained in it, using a password or an encryption key with salt.
1228
1197
  *
1229
- * @param credentials - The credentials to unlock the keyrings.
1198
+ * @param password - The keyring controller password.
1199
+ * @param encryptionKey - An exported key string to unlock keyrings with.
1200
+ * @param encryptionSalt - The salt used to encrypt the vault.
1230
1201
  * @returns A promise resolving to the deserialized keyrings array.
1231
1202
  */
1232
- async function _KeyringController_unlockKeyrings(credentials) {
1203
+ async function _KeyringController_unlockKeyrings(password, encryptionKey, encryptionSalt) {
1233
1204
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withVaultLock).call(this, async () => {
1234
- if (!this.state.vault) {
1205
+ const encryptedVault = this.state.vault;
1206
+ if (!encryptedVault) {
1235
1207
  throw new Error(constants_1.KeyringControllerError.VaultError);
1236
1208
  }
1237
- const parsedEncryptedVault = JSON.parse(this.state.vault);
1238
- if ('password' in credentials) {
1239
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, credentials.password);
1209
+ let vault;
1210
+ const updatedState = {};
1211
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
1212
+ assertIsExportableKeyEncryptor(__classPrivateFieldGet(this, _KeyringController_encryptor, "f"));
1213
+ if (password) {
1214
+ const result = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithDetail(password, encryptedVault);
1215
+ vault = result.vault;
1216
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
1217
+ updatedState.encryptionKey = result.exportedKeyString;
1218
+ updatedState.encryptionSalt = result.salt;
1219
+ }
1220
+ else {
1221
+ const parsedEncryptedVault = JSON.parse(encryptedVault);
1222
+ if (encryptionSalt && encryptionSalt !== parsedEncryptedVault.salt) {
1223
+ throw new Error(constants_1.KeyringControllerError.ExpiredCredentials);
1224
+ }
1225
+ else {
1226
+ encryptionSalt = parsedEncryptedVault.salt;
1227
+ }
1228
+ if (typeof encryptionKey !== 'string') {
1229
+ throw new TypeError(constants_1.KeyringControllerError.WrongPasswordType);
1230
+ }
1231
+ const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(encryptionKey);
1232
+ vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithKey(key, parsedEncryptedVault);
1233
+ // This call is required on the first call because encryptionKey
1234
+ // is not yet inside the memStore
1235
+ updatedState.encryptionKey = encryptionKey;
1236
+ updatedState.encryptionSalt = encryptionSalt;
1237
+ }
1240
1238
  }
1241
1239
  else {
1242
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setEncryptionKey).call(this, credentials.encryptionKey, credentials.keyDerivationSalt || parsedEncryptedVault.salt);
1243
- }
1244
- const encryptionKey = __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")?.serialized;
1245
- if (!encryptionKey) {
1246
- throw new Error(constants_1.KeyringControllerError.MissingCredentials);
1240
+ if (typeof password !== 'string') {
1241
+ throw new TypeError(constants_1.KeyringControllerError.WrongPasswordType);
1242
+ }
1243
+ vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decrypt(password, encryptedVault);
1244
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
1247
1245
  }
1248
- const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(encryptionKey);
1249
- const vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithKey(key, parsedEncryptedVault);
1250
1246
  if (!isSerializedKeyringsArray(vault)) {
1251
1247
  throw new Error(constants_1.KeyringControllerError.VaultDataError);
1252
1248
  }
@@ -1254,8 +1250,10 @@ async function _KeyringController_unlockKeyrings(credentials) {
1254
1250
  const updatedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getUpdatedKeyrings).call(this);
1255
1251
  this.update((state) => {
1256
1252
  state.keyrings = updatedKeyrings;
1257
- state.encryptionKey = encryptionKey;
1258
- state.encryptionSalt = parsedEncryptedVault.salt;
1253
+ if (updatedState.encryptionKey || updatedState.encryptionSalt) {
1254
+ state.encryptionKey = updatedState.encryptionKey;
1255
+ state.encryptionSalt = updatedState.encryptionSalt;
1256
+ }
1259
1257
  });
1260
1258
  return { keyrings, newMetadata };
1261
1259
  });
@@ -1263,36 +1261,57 @@ async function _KeyringController_unlockKeyrings(credentials) {
1263
1261
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withVaultLock).call(this, async () => {
1264
1262
  // Ensure no duplicate accounts are persisted.
1265
1263
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertNoDuplicateAccounts).call(this);
1266
- if (!__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")) {
1264
+ const { encryptionKey, encryptionSalt, vault } = this.state;
1265
+ // READ THIS CAREFULLY:
1266
+ // We do check if the vault is still considered up-to-date, if not, we would not re-use the
1267
+ // cached key and we will re-generate a new one (based on the password).
1268
+ //
1269
+ // This helps doing seamless updates of the vault. Useful in case we change some cryptographic
1270
+ // parameters to the KDF.
1271
+ const useCachedKey = encryptionKey && vault && __classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated?.(vault);
1272
+ if (!__classPrivateFieldGet(this, _KeyringController_password, "f") && !encryptionKey) {
1267
1273
  throw new Error(constants_1.KeyringControllerError.MissingCredentials);
1268
1274
  }
1269
1275
  const serializedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getSerializedKeyrings).call(this);
1270
1276
  if (!serializedKeyrings.some((keyring) => keyring.type === KeyringTypes.hd)) {
1271
1277
  throw new Error(constants_1.KeyringControllerError.NoHdKeyring);
1272
1278
  }
1273
- const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").serialized);
1274
- const encryptedVault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encryptWithKey(key, serializedKeyrings);
1275
- // We need to include the salt used to derive
1276
- // the encryption key, to be able to derive it
1277
- // from password again.
1278
- encryptedVault.salt = __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").salt;
1279
- const updatedState = {
1280
- vault: JSON.stringify(encryptedVault),
1281
- encryptionKey: __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").serialized,
1282
- encryptionSalt: __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").salt,
1283
- };
1279
+ const updatedState = {};
1280
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
1281
+ assertIsExportableKeyEncryptor(__classPrivateFieldGet(this, _KeyringController_encryptor, "f"));
1282
+ if (useCachedKey) {
1283
+ const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(encryptionKey);
1284
+ const vaultJSON = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encryptWithKey(key, serializedKeyrings);
1285
+ vaultJSON.salt = encryptionSalt;
1286
+ updatedState.vault = JSON.stringify(vaultJSON);
1287
+ }
1288
+ else if (__classPrivateFieldGet(this, _KeyringController_password, "f")) {
1289
+ const { vault: newVault, exportedKeyString } = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encryptWithDetail(__classPrivateFieldGet(this, _KeyringController_password, "f"), serializedKeyrings);
1290
+ updatedState.vault = newVault;
1291
+ updatedState.encryptionKey = exportedKeyString;
1292
+ }
1293
+ }
1294
+ else {
1295
+ assertIsValidPassword(__classPrivateFieldGet(this, _KeyringController_password, "f"));
1296
+ updatedState.vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encrypt(__classPrivateFieldGet(this, _KeyringController_password, "f"), serializedKeyrings);
1297
+ }
1298
+ if (!updatedState.vault) {
1299
+ throw new Error(constants_1.KeyringControllerError.MissingVaultData);
1300
+ }
1284
1301
  const updatedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getUpdatedKeyrings).call(this);
1285
1302
  this.update((state) => {
1286
1303
  state.vault = updatedState.vault;
1287
1304
  state.keyrings = updatedKeyrings;
1288
- state.encryptionKey = updatedState.encryptionKey;
1289
- state.encryptionSalt = updatedState.encryptionSalt;
1305
+ if (updatedState.encryptionKey) {
1306
+ state.encryptionKey = updatedState.encryptionKey;
1307
+ state.encryptionSalt = JSON.parse(updatedState.vault).salt;
1308
+ }
1290
1309
  });
1291
1310
  return true;
1292
1311
  });
1293
1312
  }, _KeyringController_isNewEncryptionAvailable = function _KeyringController_isNewEncryptionAvailable() {
1294
1313
  const { vault } = this.state;
1295
- if (!vault || !__classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated) {
1314
+ if (!vault || !__classPrivateFieldGet(this, _KeyringController_password, "f") || !__classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated) {
1296
1315
  return false;
1297
1316
  }
1298
1317
  return !__classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated(vault);
@@ -1526,13 +1545,13 @@ async function _KeyringController_persistOrRollback(callback) {
1526
1545
  async function _KeyringController_withRollback(callback) {
1527
1546
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withControllerLock).call(this, async ({ releaseLock }) => {
1528
1547
  const currentSerializedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getSerializedKeyrings).call(this);
1529
- const currentEncryptionKey = (0, lodash_1.cloneDeep)(__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f"));
1548
+ const currentPassword = __classPrivateFieldGet(this, _KeyringController_password, "f");
1530
1549
  try {
1531
1550
  return await callback({ releaseLock });
1532
1551
  }
1533
1552
  catch (e) {
1534
- // Keyrings and encryption credentials are restored to their previous state
1535
- __classPrivateFieldSet(this, _KeyringController_encryptionKey, currentEncryptionKey, "f");
1553
+ // Keyrings and password are restored to their previous state
1554
+ __classPrivateFieldSet(this, _KeyringController_password, currentPassword, "f");
1536
1555
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_restoreSerializedKeyrings).call(this, currentSerializedKeyrings);
1537
1556
  throw e;
1538
1557
  }