Skip to content

Commit fc79f40

Browse files
ccharlygantunesr
andauthored
refactor(encryptor): align Encryptor methods to match @metamask/browser-passworder (#9203)
Align `Encryptor` methods to match `@metamask/browser-passworder` as much as possible. Co-authored-by: Gustavo Antunes <17601467+gantunesr@users.noreply.github.com>
1 parent b6bf467 commit fc79f40

File tree

13 files changed

+651
-216
lines changed

13 files changed

+651
-216
lines changed

app/core/Encryptor/Encryptor.test.ts

Lines changed: 182 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { NativeModules } from 'react-native';
22
import { Encryptor } from './Encryptor';
3-
import { ENCRYPTION_LIBRARY, LEGACY_DERIVATION_PARAMS } from './constants';
3+
import { ENCRYPTION_LIBRARY, LEGACY_DERIVATION_OPTIONS } from './constants';
44

55
const Aes = NativeModules.Aes;
66
const AesForked = NativeModules.AesForked;
@@ -9,14 +9,12 @@ describe('Encryptor', () => {
99
let encryptor: Encryptor;
1010

1111
beforeEach(() => {
12-
encryptor = new Encryptor({ derivationParams: LEGACY_DERIVATION_PARAMS });
12+
encryptor = new Encryptor({
13+
keyDerivationOptions: LEGACY_DERIVATION_OPTIONS,
14+
});
1315
});
1416

1517
describe('encrypt', () => {
16-
afterEach(() => {
17-
jest.clearAllMocks();
18-
});
19-
2018
it('should encrypt an object correctly', async () => {
2119
const password = 'testPassword';
2220
const objectToEncrypt = { key: 'value' };
@@ -41,45 +39,36 @@ describe('Encryptor', () => {
4139
pbkdf2AesForkedSpy: jest.SpyInstance;
4240

4341
beforeEach(() => {
44-
decryptAesSpy = jest
45-
.spyOn(Aes, 'decrypt')
46-
.mockResolvedValue('{"mockData": "mockedPlainText"}');
47-
pbkdf2AesSpy = jest
48-
.spyOn(Aes, 'pbkdf2')
49-
.mockResolvedValue('mockedAesKey');
50-
decryptAesForkedSpy = jest
51-
.spyOn(AesForked, 'decrypt')
52-
.mockResolvedValue('{"mockData": "mockedPlainText"}');
53-
pbkdf2AesForkedSpy = jest
54-
.spyOn(AesForked, 'pbkdf2')
55-
.mockResolvedValue('mockedAesForkedKey');
42+
decryptAesSpy = jest.spyOn(Aes, 'decrypt');
43+
pbkdf2AesSpy = jest.spyOn(Aes, 'pbkdf2');
44+
decryptAesForkedSpy = jest.spyOn(AesForked, 'decrypt');
45+
pbkdf2AesForkedSpy = jest.spyOn(AesForked, 'pbkdf2');
5646
});
5747

5848
afterEach(() => {
59-
decryptAesSpy.mockRestore();
60-
pbkdf2AesSpy.mockRestore();
61-
decryptAesForkedSpy.mockRestore();
62-
pbkdf2AesForkedSpy.mockRestore();
49+
jest.clearAllMocks();
6350
});
6451

6552
it.each([
66-
{
67-
lib: ENCRYPTION_LIBRARY.original,
68-
expectedKey: 'mockedAesKey',
69-
expectedPBKDF2Args: ['testPassword', 'mockedSalt', 5000, 256],
70-
description:
71-
'with original library and legacy iterations number for key generation',
72-
},
73-
{
74-
lib: 'random-lib', // Assuming not using "original" should lead to AesForked
75-
expectedKey: 'mockedAesForkedKey',
76-
expectedPBKDF2Args: ['testPassword', 'mockedSalt'],
77-
description:
78-
'with library different to "original" and legacy iterations number for key generation',
79-
},
53+
[
54+
'with original library and legacy iterations number for key generation',
55+
{
56+
lib: ENCRYPTION_LIBRARY.original,
57+
expectedKeyValue: 'mockedKey',
58+
expectedPBKDF2Args: ['testPassword', 'mockedSalt', 5000, 256],
59+
},
60+
],
61+
[
62+
'with library different to "original" and legacy iterations number for key generation',
63+
{
64+
lib: 'random-lib', // Assuming not using "original" should lead to AesForked
65+
expectedKeyValue: 'mockedKeyForked',
66+
expectedPBKDF2Args: ['testPassword', 'mockedSalt'],
67+
},
68+
],
8069
])(
81-
'decrypts a string correctly $description',
82-
async ({ lib, expectedKey, expectedPBKDF2Args }) => {
70+
'decrypts a string correctly %s',
71+
async (_, { lib, expectedKeyValue, expectedPBKDF2Args }) => {
8372
const password = 'testPassword';
8473
const mockVault = {
8574
cipher: 'mockedCipher',
@@ -98,7 +87,11 @@ describe('Encryptor', () => {
9887
lib === ENCRYPTION_LIBRARY.original
9988
? decryptAesSpy
10089
: decryptAesForkedSpy,
101-
).toHaveBeenCalledWith(mockVault.cipher, expectedKey, mockVault.iv);
90+
).toHaveBeenCalledWith(
91+
mockVault.cipher,
92+
expectedKeyValue,
93+
mockVault.iv,
94+
);
10295
expect(
10396
lib === ENCRYPTION_LIBRARY.original
10497
? pbkdf2AesSpy
@@ -117,7 +110,7 @@ describe('Encryptor', () => {
117110
iv: 'mockedIV',
118111
salt: 'mockedSalt',
119112
lib: 'original',
120-
keyMetadata: LEGACY_DERIVATION_PARAMS,
113+
keyMetadata: LEGACY_DERIVATION_OPTIONS,
121114
}),
122115
),
123116
).toBe(true);
@@ -136,4 +129,153 @@ describe('Encryptor', () => {
136129
).toBe(false);
137130
});
138131
});
132+
133+
describe('keyFromPassword', () => {
134+
it.each([
135+
[
136+
'exportable with original lib',
137+
{
138+
lib: ENCRYPTION_LIBRARY.original,
139+
exportable: true,
140+
keyMetadata: LEGACY_DERIVATION_OPTIONS,
141+
},
142+
],
143+
[
144+
'non-exportable with original lib',
145+
{
146+
lib: ENCRYPTION_LIBRARY.original,
147+
exportable: false,
148+
keyMetadata: LEGACY_DERIVATION_OPTIONS,
149+
},
150+
],
151+
[
152+
'exportable with random lib',
153+
{
154+
lib: 'random-lib',
155+
exportable: true,
156+
keyMetadata: LEGACY_DERIVATION_OPTIONS,
157+
},
158+
],
159+
[
160+
'non-exportable with random lib',
161+
{
162+
lib: 'random-lib',
163+
exportable: false,
164+
keyMetadata: LEGACY_DERIVATION_OPTIONS,
165+
},
166+
],
167+
])(
168+
'generates a key with the right attributes: %s',
169+
async (_, { lib, exportable, keyMetadata }) => {
170+
const key = await encryptor.keyFromPassword(
171+
'mockPassword',
172+
encryptor.generateSalt(),
173+
exportable,
174+
keyMetadata,
175+
lib,
176+
);
177+
178+
expect(key.key).not.toBe(undefined);
179+
expect(key.lib).toBe(lib);
180+
expect(key.exportable).toBe(exportable);
181+
expect(key.keyMetadata).toBe(keyMetadata);
182+
},
183+
);
184+
});
185+
186+
describe('exportKey', () => {
187+
it('exports a key', async () => {
188+
const key = await encryptor.keyFromPassword(
189+
'mockPassword',
190+
encryptor.generateSalt(),
191+
true,
192+
);
193+
194+
const exportedKey = await encryptor.exportKey(key);
195+
expect(exportedKey).not.toBe(undefined);
196+
});
197+
198+
it('does not export a key if not exportable', async () => {
199+
const key = await encryptor.keyFromPassword(
200+
'mockPassword',
201+
encryptor.generateSalt(),
202+
false,
203+
);
204+
205+
expect(async () => await encryptor.exportKey(key)).rejects.toThrow(
206+
'Key is not exportable',
207+
);
208+
});
209+
});
210+
211+
describe('importKey', () => {
212+
const serializeKey = (data: object) =>
213+
Buffer.from(JSON.stringify(data)).toString('base64');
214+
215+
it('imports a key', async () => {
216+
const testKey = await encryptor.keyFromPassword(
217+
'mockPassword',
218+
encryptor.generateSalt(),
219+
true,
220+
);
221+
const exportedKey = await encryptor.exportKey(testKey);
222+
223+
const key = await encryptor.importKey(exportedKey);
224+
expect(key).toStrictEqual(testKey);
225+
});
226+
227+
it.each([
228+
'',
229+
'{}',
230+
Buffer.from('').toString('base64'),
231+
Buffer.from('{ not: json }').toString('base64'),
232+
])('does not import a bad serialized key: %s', async (badFormattedKey) => {
233+
expect(
234+
async () => await encryptor.importKey(badFormattedKey),
235+
).rejects.toThrow('Invalid exported key serialization format');
236+
});
237+
238+
it.each([
239+
[
240+
'missing lib',
241+
{
242+
exportable: true,
243+
key: 'a-key',
244+
keyMetadata: LEGACY_DERIVATION_OPTIONS,
245+
},
246+
],
247+
[
248+
'missing key',
249+
{
250+
lib: ENCRYPTION_LIBRARY.original,
251+
exportable: true,
252+
keyMetadata: LEGACY_DERIVATION_OPTIONS,
253+
},
254+
],
255+
[
256+
'missing keyMetadata',
257+
{
258+
lib: ENCRYPTION_LIBRARY.original,
259+
exportable: true,
260+
key: 'a-key',
261+
},
262+
],
263+
[
264+
'invalid keyMetadata',
265+
{
266+
lib: ENCRYPTION_LIBRARY.original,
267+
exportable: true,
268+
key: 'a-key',
269+
keyMatadata: {},
270+
},
271+
],
272+
])(
273+
'does not import a bad structured key: %s',
274+
async (_, badStructuredKey) => {
275+
expect(
276+
async () => await encryptor.importKey(serializeKey(badStructuredKey)),
277+
).rejects.toThrow('Invalid exported key structure');
278+
},
279+
);
280+
});
139281
});

0 commit comments

Comments
 (0)