@metamask-previews/keyring-controller 24.0.0-preview-4419d7e4 → 24.0.0-preview-c2c8112b

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.
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- 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;
12
+ 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;
13
13
  function $importDefault(module) {
14
14
  if (module?.__esModule) {
15
15
  return module.default;
@@ -18,6 +18,7 @@ function $importDefault(module) {
18
18
  }
19
19
  import { isValidPrivate, getBinarySize } from "@ethereumjs/util";
20
20
  import { BaseController } from "@metamask/base-controller";
21
+ import * as encryptorUtils from "@metamask/browser-passworder";
21
22
  import { HdKeyring } from "@metamask/eth-hd-keyring";
22
23
  import { normalize as ethNormalize } from "@metamask/eth-sig-util";
23
24
  import SimpleKeyring from "@metamask/eth-simple-keyring";
@@ -27,7 +28,7 @@ import $Wallet from "ethereumjs-wallet";
27
28
  const { thirdparty: importers } = $Wallet;
28
29
  const Wallet = $importDefault($Wallet);
29
30
  import $lodash from "lodash";
30
- const { cloneDeep, isEqual } = $lodash;
31
+ const { isEqual } = $lodash;
31
32
  // When generating a ULID within the same millisecond, monotonicFactory provides some guarantees regarding sort order.
32
33
  import { ulid } from "ulid";
33
34
  import { KeyringControllerError } from "./constants.mjs";
@@ -112,6 +113,23 @@ function assertHasUint8ArrayMnemonic(keyring) {
112
113
  throw new Error("Can't get mnemonic bytes from keyring");
113
114
  }
114
115
  }
116
+ /**
117
+ * Assert that the provided encryptor supports
118
+ * encryption and encryption key export.
119
+ *
120
+ * @param encryptor - The encryptor to check.
121
+ * @throws If the encryptor does not support key encryption.
122
+ */
123
+ function assertIsExportableKeyEncryptor(encryptor) {
124
+ if (!('importKey' in encryptor &&
125
+ typeof encryptor.importKey === 'function' &&
126
+ 'decryptWithKey' in encryptor &&
127
+ typeof encryptor.decryptWithKey === 'function' &&
128
+ 'encryptWithKey' in encryptor &&
129
+ typeof encryptor.encryptWithKey === 'function')) {
130
+ throw new Error(KeyringControllerError.UnsupportedEncryptionKeyExport);
131
+ }
132
+ }
115
133
  /**
116
134
  * Assert that the provided password is a valid non-empty string.
117
135
  *
@@ -213,11 +231,12 @@ export class KeyringController extends BaseController {
213
231
  * @param options - Initial options used to configure this controller
214
232
  * @param options.encryptor - An optional object for defining encryption schemes.
215
233
  * @param options.keyringBuilders - Set a new name for account.
234
+ * @param options.cacheEncryptionKey - Whether to cache or not encryption key.
216
235
  * @param options.messenger - A restricted messenger.
217
236
  * @param options.state - Initial state to set on this controller.
218
237
  */
219
238
  constructor(options) {
220
- const { encryptor, keyringBuilders, messenger, state } = options;
239
+ const { encryptor = encryptorUtils, keyringBuilders, messenger, state, } = options;
221
240
  super({
222
241
  name,
223
242
  metadata: {
@@ -263,15 +282,22 @@ export class KeyringController extends BaseController {
263
282
  _KeyringController_vaultOperationMutex.set(this, new Mutex());
264
283
  _KeyringController_keyringBuilders.set(this, void 0);
265
284
  _KeyringController_encryptor.set(this, void 0);
285
+ _KeyringController_cacheEncryptionKey.set(this, void 0);
266
286
  _KeyringController_keyrings.set(this, void 0);
267
287
  _KeyringController_unsupportedKeyrings.set(this, void 0);
268
- _KeyringController_encryptionKey.set(this, void 0);
288
+ _KeyringController_password.set(this, void 0);
269
289
  __classPrivateFieldSet(this, _KeyringController_keyringBuilders, keyringBuilders
270
290
  ? keyringBuilders.concat(defaultKeyringBuilders)
271
291
  : defaultKeyringBuilders, "f");
272
292
  __classPrivateFieldSet(this, _KeyringController_encryptor, encryptor, "f");
273
293
  __classPrivateFieldSet(this, _KeyringController_keyrings, [], "f");
274
294
  __classPrivateFieldSet(this, _KeyringController_unsupportedKeyrings, [], "f");
295
+ // This option allows the controller to cache an exported key
296
+ // for use in decrypting and encrypting data without password
297
+ __classPrivateFieldSet(this, _KeyringController_cacheEncryptionKey, Boolean(options.cacheEncryptionKey), "f");
298
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
299
+ assertIsExportableKeyEncryptor(encryptor);
300
+ }
275
301
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_registerMessageHandlers).call(this);
276
302
  }
277
303
  /**
@@ -642,7 +668,7 @@ export class KeyringController extends BaseController {
642
668
  async setLocked() {
643
669
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
644
670
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
645
- __classPrivateFieldSet(this, _KeyringController_encryptionKey, undefined, "f");
671
+ __classPrivateFieldSet(this, _KeyringController_password, undefined, "f");
646
672
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_clearKeyrings).call(this);
647
673
  this.update((state) => {
648
674
  state.isUnlocked = false;
@@ -824,9 +850,22 @@ export class KeyringController extends BaseController {
824
850
  */
825
851
  changePassword(password) {
826
852
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
853
+ // If the password is the same, do nothing.
854
+ if (__classPrivateFieldGet(this, _KeyringController_password, "f") === password) {
855
+ return Promise.resolve();
856
+ }
827
857
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_persistOrRollback).call(this, async () => {
828
858
  assertIsValidPassword(password);
829
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, password);
859
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
860
+ // We need to clear encryption key and salt from state
861
+ // to force the controller to re-encrypt the vault using
862
+ // the new password.
863
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
864
+ this.update((state) => {
865
+ delete state.encryptionKey;
866
+ delete state.encryptionSalt;
867
+ });
868
+ }
830
869
  });
831
870
  }
832
871
  /**
@@ -835,15 +874,12 @@ export class KeyringController extends BaseController {
835
874
  * consistency with the vault salt.
836
875
  *
837
876
  * @param encryptionKey - Key to unlock the keychain.
838
- * @param keyDerivationSalt - Optional salt to unlock the keychain.
877
+ * @param encryptionSalt - Optional salt to unlock the keychain.
839
878
  * @returns Promise resolving when the operation completes.
840
879
  */
841
- async submitEncryptionKey(encryptionKey, keyDerivationSalt) {
880
+ async submitEncryptionKey(encryptionKey, encryptionSalt) {
842
881
  const { newMetadata } = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
843
- const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, {
844
- encryptionKey,
845
- keyDerivationSalt,
846
- });
882
+ const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, undefined, encryptionKey, encryptionSalt);
847
883
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setUnlocked).call(this);
848
884
  return result;
849
885
  });
@@ -870,8 +906,9 @@ export class KeyringController extends BaseController {
870
906
  async exportEncryptionKey() {
871
907
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
872
908
  return await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withControllerLock).call(this, async () => {
873
- assertIsEncryptionKeySet(__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")?.serialized);
874
- return __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").serialized;
909
+ const { encryptionKey } = this.state;
910
+ assertIsEncryptionKeySet(encryptionKey);
911
+ return encryptionKey;
875
912
  });
876
913
  }
877
914
  /**
@@ -883,7 +920,7 @@ export class KeyringController extends BaseController {
883
920
  */
884
921
  async submitPassword(password) {
885
922
  const { newMetadata } = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
886
- const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, { password });
923
+ const result = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_unlockKeyrings).call(this, password);
887
924
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setUnlocked).call(this);
888
925
  return result;
889
926
  });
@@ -893,12 +930,6 @@ export class KeyringController extends BaseController {
893
930
  // can attempt to upgrade the vault.
894
931
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withRollback).call(this, async () => {
895
932
  if (newMetadata || __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_isNewEncryptionAvailable).call(this)) {
896
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, password, {
897
- // If the vault is being upgraded, we want to ignore the metadata
898
- // that is already in the vault, so we can effectively
899
- // re-encrypt the vault with the new encryption config.
900
- useVaultKeyMetadata: false,
901
- });
902
933
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_updateVault).call(this);
903
934
  }
904
935
  });
@@ -960,7 +991,7 @@ export class KeyringController extends BaseController {
960
991
  return keyring.type;
961
992
  }
962
993
  }
963
- _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() {
994
+ _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() {
964
995
  this.messenger.registerActionHandler(`${name}:signMessage`, this.signMessage.bind(this));
965
996
  this.messenger.registerActionHandler(`${name}:signEip7702Authorization`, this.signEip7702Authorization.bind(this));
966
997
  this.messenger.registerActionHandler(`${name}:signPersonalMessage`, this.signPersonalMessage.bind(this));
@@ -1019,60 +1050,10 @@ async function _KeyringController_createNewVaultWithKeyring(password, keyring) {
1019
1050
  delete state.encryptionKey;
1020
1051
  delete state.encryptionSalt;
1021
1052
  });
1022
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, password);
1053
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
1023
1054
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_clearKeyrings).call(this);
1024
1055
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_createKeyringWithFirstAccount).call(this, keyring.type, keyring.opts);
1025
1056
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setUnlocked).call(this);
1026
- }, _KeyringController_deriveEncryptionKey =
1027
- /**
1028
- * Derive the vault encryption key from the provided password, and
1029
- * assign it to the instance variable for later use with cryptographic
1030
- * functions.
1031
- *
1032
- * When the controller has a vault in its state, the key is derived
1033
- * using the salt from the vault. If the vault is empty, a new salt
1034
- * is generated and used to derive the key.
1035
- *
1036
- * @param password - The password to use for decryption or derivation.
1037
- * @param options - Options for the key derivation.
1038
- * @param options.useVaultKeyMetadata - Whether to use the vault key metadata
1039
- */
1040
- async function _KeyringController_deriveEncryptionKey(password, options = {
1041
- useVaultKeyMetadata: true,
1042
- }) {
1043
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertControllerMutexIsLocked).call(this);
1044
- const { vault } = this.state;
1045
- if (typeof password !== 'string') {
1046
- throw new TypeError(KeyringControllerError.WrongPasswordType);
1047
- }
1048
- let salt, keyMetadata;
1049
- if (vault && options.useVaultKeyMetadata) {
1050
- const parsedVault = JSON.parse(vault);
1051
- salt = parsedVault.salt;
1052
- keyMetadata = parsedVault.keyMetadata;
1053
- }
1054
- else {
1055
- salt = __classPrivateFieldGet(this, _KeyringController_encryptor, "f").generateSalt();
1056
- }
1057
- const serializedEncryptionKey = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").exportKey(await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").keyFromPassword(password, salt, true, keyMetadata));
1058
- __classPrivateFieldSet(this, _KeyringController_encryptionKey, {
1059
- salt,
1060
- serialized: serializedEncryptionKey,
1061
- }, "f");
1062
- }, _KeyringController_setEncryptionKey = function _KeyringController_setEncryptionKey(encryptionKey, keyDerivationSalt) {
1063
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertControllerMutexIsLocked).call(this);
1064
- if (typeof encryptionKey !== 'string' ||
1065
- typeof keyDerivationSalt !== 'string') {
1066
- throw new TypeError(KeyringControllerError.WrongEncryptionKeyType);
1067
- }
1068
- const { vault } = this.state;
1069
- if (vault && JSON.parse(vault).salt !== keyDerivationSalt) {
1070
- throw new Error(KeyringControllerError.ExpiredCredentials);
1071
- }
1072
- __classPrivateFieldSet(this, _KeyringController_encryptionKey, {
1073
- salt: keyDerivationSalt,
1074
- serialized: encryptionKey,
1075
- }, "f");
1076
1057
  }, _KeyringController_verifySeedPhrase =
1077
1058
  /**
1078
1059
  * Internal non-exclusive method to verify the seed phrase.
@@ -1153,7 +1134,7 @@ async function _KeyringController_getSerializedKeyrings({ includeUnsupported } =
1153
1134
  return serializedKeyrings;
1154
1135
  }, _KeyringController_getSessionState =
1155
1136
  /**
1156
- * Get a snapshot of session data held by instance variables.
1137
+ * Get a snapshot of session data held by class variables.
1157
1138
  *
1158
1139
  * @returns An object with serialized keyrings, keyrings metadata,
1159
1140
  * and the user password.
@@ -1161,7 +1142,7 @@ async function _KeyringController_getSerializedKeyrings({ includeUnsupported } =
1161
1142
  async function _KeyringController_getSessionState() {
1162
1143
  return {
1163
1144
  keyrings: await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getSerializedKeyrings).call(this),
1164
- encryptionKey: __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f"),
1145
+ password: __classPrivateFieldGet(this, _KeyringController_password, "f"),
1165
1146
  };
1166
1147
  }, _KeyringController_restoreSerializedKeyrings =
1167
1148
  /**
@@ -1190,27 +1171,54 @@ async function _KeyringController_restoreSerializedKeyrings(serializedKeyrings)
1190
1171
  * Unlock Keyrings, decrypting the vault and deserializing all
1191
1172
  * keyrings contained in it, using a password or an encryption key with salt.
1192
1173
  *
1193
- * @param credentials - The credentials to unlock the keyrings.
1174
+ * @param password - The keyring controller password.
1175
+ * @param encryptionKey - An exported key string to unlock keyrings with.
1176
+ * @param encryptionSalt - The salt used to encrypt the vault.
1194
1177
  * @returns A promise resolving to the deserialized keyrings array.
1195
1178
  */
1196
- async function _KeyringController_unlockKeyrings(credentials) {
1179
+ async function _KeyringController_unlockKeyrings(password, encryptionKey, encryptionSalt) {
1197
1180
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withVaultLock).call(this, async () => {
1198
- if (!this.state.vault) {
1181
+ const encryptedVault = this.state.vault;
1182
+ if (!encryptedVault) {
1199
1183
  throw new Error(KeyringControllerError.VaultError);
1200
1184
  }
1201
- const parsedEncryptedVault = JSON.parse(this.state.vault);
1202
- if ('password' in credentials) {
1203
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_deriveEncryptionKey).call(this, credentials.password);
1185
+ let vault;
1186
+ const updatedState = {};
1187
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
1188
+ assertIsExportableKeyEncryptor(__classPrivateFieldGet(this, _KeyringController_encryptor, "f"));
1189
+ if (password) {
1190
+ const result = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithDetail(password, encryptedVault);
1191
+ vault = result.vault;
1192
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
1193
+ updatedState.encryptionKey = result.exportedKeyString;
1194
+ updatedState.encryptionSalt = result.salt;
1195
+ }
1196
+ else {
1197
+ const parsedEncryptedVault = JSON.parse(encryptedVault);
1198
+ if (encryptionSalt && encryptionSalt !== parsedEncryptedVault.salt) {
1199
+ throw new Error(KeyringControllerError.ExpiredCredentials);
1200
+ }
1201
+ else {
1202
+ encryptionSalt = parsedEncryptedVault.salt;
1203
+ }
1204
+ if (typeof encryptionKey !== 'string') {
1205
+ throw new TypeError(KeyringControllerError.WrongPasswordType);
1206
+ }
1207
+ const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(encryptionKey);
1208
+ vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithKey(key, parsedEncryptedVault);
1209
+ // This call is required on the first call because encryptionKey
1210
+ // is not yet inside the memStore
1211
+ updatedState.encryptionKey = encryptionKey;
1212
+ updatedState.encryptionSalt = encryptionSalt;
1213
+ }
1204
1214
  }
1205
1215
  else {
1206
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_setEncryptionKey).call(this, credentials.encryptionKey, credentials.keyDerivationSalt || parsedEncryptedVault.salt);
1207
- }
1208
- const encryptionKey = __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")?.serialized;
1209
- if (!encryptionKey) {
1210
- throw new Error(KeyringControllerError.MissingCredentials);
1216
+ if (typeof password !== 'string') {
1217
+ throw new TypeError(KeyringControllerError.WrongPasswordType);
1218
+ }
1219
+ vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decrypt(password, encryptedVault);
1220
+ __classPrivateFieldSet(this, _KeyringController_password, password, "f");
1211
1221
  }
1212
- const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(encryptionKey);
1213
- const vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").decryptWithKey(key, parsedEncryptedVault);
1214
1222
  if (!isSerializedKeyringsArray(vault)) {
1215
1223
  throw new Error(KeyringControllerError.VaultDataError);
1216
1224
  }
@@ -1218,8 +1226,10 @@ async function _KeyringController_unlockKeyrings(credentials) {
1218
1226
  const updatedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getUpdatedKeyrings).call(this);
1219
1227
  this.update((state) => {
1220
1228
  state.keyrings = updatedKeyrings;
1221
- state.encryptionKey = encryptionKey;
1222
- state.encryptionSalt = parsedEncryptedVault.salt;
1229
+ if (updatedState.encryptionKey || updatedState.encryptionSalt) {
1230
+ state.encryptionKey = updatedState.encryptionKey;
1231
+ state.encryptionSalt = updatedState.encryptionSalt;
1232
+ }
1223
1233
  });
1224
1234
  return { keyrings, newMetadata };
1225
1235
  });
@@ -1227,36 +1237,57 @@ async function _KeyringController_unlockKeyrings(credentials) {
1227
1237
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withVaultLock).call(this, async () => {
1228
1238
  // Ensure no duplicate accounts are persisted.
1229
1239
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertNoDuplicateAccounts).call(this);
1230
- if (!__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f")) {
1240
+ const { encryptionKey, encryptionSalt, vault } = this.state;
1241
+ // READ THIS CAREFULLY:
1242
+ // We do check if the vault is still considered up-to-date, if not, we would not re-use the
1243
+ // cached key and we will re-generate a new one (based on the password).
1244
+ //
1245
+ // This helps doing seamless updates of the vault. Useful in case we change some cryptographic
1246
+ // parameters to the KDF.
1247
+ const useCachedKey = encryptionKey && vault && __classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated?.(vault);
1248
+ if (!__classPrivateFieldGet(this, _KeyringController_password, "f") && !encryptionKey) {
1231
1249
  throw new Error(KeyringControllerError.MissingCredentials);
1232
1250
  }
1233
1251
  const serializedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getSerializedKeyrings).call(this);
1234
1252
  if (!serializedKeyrings.some((keyring) => keyring.type === KeyringTypes.hd)) {
1235
1253
  throw new Error(KeyringControllerError.NoHdKeyring);
1236
1254
  }
1237
- const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").serialized);
1238
- const encryptedVault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encryptWithKey(key, serializedKeyrings);
1239
- // We need to include the salt used to derive
1240
- // the encryption key, to be able to derive it
1241
- // from password again.
1242
- encryptedVault.salt = __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").salt;
1243
- const updatedState = {
1244
- vault: JSON.stringify(encryptedVault),
1245
- encryptionKey: __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").serialized,
1246
- encryptionSalt: __classPrivateFieldGet(this, _KeyringController_encryptionKey, "f").salt,
1247
- };
1255
+ const updatedState = {};
1256
+ if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) {
1257
+ assertIsExportableKeyEncryptor(__classPrivateFieldGet(this, _KeyringController_encryptor, "f"));
1258
+ if (useCachedKey) {
1259
+ const key = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").importKey(encryptionKey);
1260
+ const vaultJSON = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encryptWithKey(key, serializedKeyrings);
1261
+ vaultJSON.salt = encryptionSalt;
1262
+ updatedState.vault = JSON.stringify(vaultJSON);
1263
+ }
1264
+ else if (__classPrivateFieldGet(this, _KeyringController_password, "f")) {
1265
+ const { vault: newVault, exportedKeyString } = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encryptWithDetail(__classPrivateFieldGet(this, _KeyringController_password, "f"), serializedKeyrings);
1266
+ updatedState.vault = newVault;
1267
+ updatedState.encryptionKey = exportedKeyString;
1268
+ }
1269
+ }
1270
+ else {
1271
+ assertIsValidPassword(__classPrivateFieldGet(this, _KeyringController_password, "f"));
1272
+ updatedState.vault = await __classPrivateFieldGet(this, _KeyringController_encryptor, "f").encrypt(__classPrivateFieldGet(this, _KeyringController_password, "f"), serializedKeyrings);
1273
+ }
1274
+ if (!updatedState.vault) {
1275
+ throw new Error(KeyringControllerError.MissingVaultData);
1276
+ }
1248
1277
  const updatedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getUpdatedKeyrings).call(this);
1249
1278
  this.update((state) => {
1250
1279
  state.vault = updatedState.vault;
1251
1280
  state.keyrings = updatedKeyrings;
1252
- state.encryptionKey = updatedState.encryptionKey;
1253
- state.encryptionSalt = updatedState.encryptionSalt;
1281
+ if (updatedState.encryptionKey) {
1282
+ state.encryptionKey = updatedState.encryptionKey;
1283
+ state.encryptionSalt = JSON.parse(updatedState.vault).salt;
1284
+ }
1254
1285
  });
1255
1286
  return true;
1256
1287
  });
1257
1288
  }, _KeyringController_isNewEncryptionAvailable = function _KeyringController_isNewEncryptionAvailable() {
1258
1289
  const { vault } = this.state;
1259
- if (!vault || !__classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated) {
1290
+ if (!vault || !__classPrivateFieldGet(this, _KeyringController_password, "f") || !__classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated) {
1260
1291
  return false;
1261
1292
  }
1262
1293
  return !__classPrivateFieldGet(this, _KeyringController_encryptor, "f").isVaultUpdated(vault);
@@ -1490,13 +1521,13 @@ async function _KeyringController_persistOrRollback(callback) {
1490
1521
  async function _KeyringController_withRollback(callback) {
1491
1522
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_withControllerLock).call(this, async ({ releaseLock }) => {
1492
1523
  const currentSerializedKeyrings = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getSerializedKeyrings).call(this);
1493
- const currentEncryptionKey = cloneDeep(__classPrivateFieldGet(this, _KeyringController_encryptionKey, "f"));
1524
+ const currentPassword = __classPrivateFieldGet(this, _KeyringController_password, "f");
1494
1525
  try {
1495
1526
  return await callback({ releaseLock });
1496
1527
  }
1497
1528
  catch (e) {
1498
- // Keyrings and encryption credentials are restored to their previous state
1499
- __classPrivateFieldSet(this, _KeyringController_encryptionKey, currentEncryptionKey, "f");
1529
+ // Keyrings and password are restored to their previous state
1530
+ __classPrivateFieldSet(this, _KeyringController_password, currentPassword, "f");
1500
1531
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_restoreSerializedKeyrings).call(this, currentSerializedKeyrings);
1501
1532
  throw e;
1502
1533
  }