Skip to content

Commit e717139

Browse files
fix: properly decrypt legacy state blobs (#2472)
Fixes a problem where encrypted state blobs produced before `keyMetadata` was added would fail to decrypt. The encryptor would attempt to use the latest key derivation parameters when no `keyMetadata` was passed in, causing a failure to decrypt: https://github.com/MetaMask/metamask-extension/blob/ed5ec2d28f7e118803c6fdc74abe90db4e00f480/app/scripts/lib/encryptor-factory.ts#L78-L82 This PR simply falls back to the legacy key derivation parameters whenever `keyMetadata` isn't present strictly for decryption purposes. The default key derivation parameters are still in use for all encryption.
1 parent 1bf7e4f commit e717139

4 files changed

Lines changed: 52 additions & 3 deletions

File tree

packages/snaps-controllers/coverage.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"branches": 91.64,
2+
"branches": 91.68,
33
"functions": 96.77,
44
"lines": 97.89,
55
"statements": 97.57

packages/snaps-controllers/src/snaps/SnapController.test.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ import {
9595
sleep,
9696
} from '../test-utils';
9797
import { delay } from '../utils';
98+
import { LEGACY_ENCRYPTION_KEY_DERIVATION_OPTIONS } from './constants';
9899
import { SnapsRegistryStatus } from './registry';
99100
import type { SnapControllerState } from './SnapController';
100101
import {
@@ -8178,6 +8179,42 @@ describe('SnapController', () => {
81788179
snapController.destroy();
81798180
});
81808181

8182+
it('uses legacy decryption where needed', async () => {
8183+
const messenger = getSnapControllerMessenger();
8184+
8185+
const state = { foo: 'bar' };
8186+
8187+
const { data, iv, salt } = JSON.parse(
8188+
await encrypt(
8189+
ENCRYPTION_KEY,
8190+
state,
8191+
undefined,
8192+
undefined,
8193+
LEGACY_ENCRYPTION_KEY_DERIVATION_OPTIONS,
8194+
),
8195+
);
8196+
8197+
const snapController = getSnapController(
8198+
getSnapControllerOptions({
8199+
messenger,
8200+
state: {
8201+
snaps: {
8202+
[MOCK_SNAP_ID]: getPersistedSnapObject(),
8203+
},
8204+
snapStates: {
8205+
[MOCK_SNAP_ID]: JSON.stringify({ data, iv, salt }),
8206+
},
8207+
},
8208+
}),
8209+
);
8210+
8211+
expect(
8212+
await messenger.call('SnapController:getSnapState', MOCK_SNAP_ID, true),
8213+
).toStrictEqual(state);
8214+
8215+
snapController.destroy();
8216+
});
8217+
81818218
it('throws an error if the state is corrupt', async () => {
81828219
const messenger = getSnapControllerMessenger();
81838220

packages/snaps-controllers/src/snaps/SnapController.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,10 @@ import {
128128
setDiff,
129129
withTimeout,
130130
} from '../utils';
131-
import { ALLOWED_PERMISSIONS } from './constants';
131+
import {
132+
ALLOWED_PERMISSIONS,
133+
LEGACY_ENCRYPTION_KEY_DERIVATION_OPTIONS,
134+
} from './constants';
132135
import type { SnapLocation } from './location';
133136
import { detectSnapLocation } from './location';
134137
import type {
@@ -1676,7 +1679,9 @@ export class SnapController extends BaseController<
16761679
snapId,
16771680
salt,
16781681
useCache,
1679-
keyMetadata,
1682+
// When decrypting state we expect key metadata to be present.
1683+
// If it isn't present, we assume that the Snap state we are decrypting is old enough to use the legacy encryption params.
1684+
keyMetadata: keyMetadata ?? LEGACY_ENCRYPTION_KEY_DERIVATION_OPTIONS,
16801685
});
16811686
const decryptedState = await this.#encryptor.decryptWithKey(key, parsed);
16821687

packages/snaps-controllers/src/snaps/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,10 @@ export const ALLOWED_PERMISSIONS = Object.freeze([
1313
SnapEndowments.TransactionInsight,
1414
SnapEndowments.SignatureInsight,
1515
]);
16+
17+
export const LEGACY_ENCRYPTION_KEY_DERIVATION_OPTIONS = {
18+
algorithm: 'PBKDF2' as const,
19+
params: {
20+
iterations: 10_000,
21+
},
22+
};

0 commit comments

Comments
 (0)