Skip to content

Commit afc318d

Browse files
committed
Export encryption details
Details of encryption methods used are now exported.
1 parent e66f3ca commit afc318d

File tree

2 files changed

+104
-41
lines changed

2 files changed

+104
-41
lines changed

src/index.ts

Lines changed: 80 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
type DetailedEncryptionResult = {
22
vault: string;
33
exportedKeyString: string;
4+
keyDerivationOptions: KeyDerivationOptions;
45
};
56

67
type EncryptionResult = {
@@ -35,7 +36,7 @@ export async function encrypt<R>(
3536
key?: CryptoKey,
3637
salt: string = generateSalt(),
3738
): Promise<string> {
38-
const cryptoKey = key || (await keyFromPassword(password, salt));
39+
const cryptoKey = key || (await keyFromPassword({ password, salt })).key;
3940
const payload = await encryptWithKey(cryptoKey, dataObj);
4041
payload.salt = salt;
4142
return JSON.stringify(payload);
@@ -55,13 +56,17 @@ export async function encryptWithDetail<R>(
5556
dataObj: R,
5657
salt = generateSalt(),
5758
): Promise<DetailedEncryptionResult> {
58-
const key = await keyFromPassword(password, salt);
59+
const { key, keyDerivationOptions } = await keyFromPassword({
60+
password,
61+
salt,
62+
});
5963
const exportedKeyString = await exportKey(key);
6064
const vault = await encrypt(password, dataObj, key, salt);
6165

6266
return {
6367
vault,
6468
exportedKeyString,
69+
keyDerivationOptions,
6570
};
6671
}
6772

@@ -117,7 +122,7 @@ export async function decrypt(
117122
const payload = JSON.parse(text);
118123
const { salt } = payload;
119124

120-
const cryptoKey = key || (await keyFromPassword(password, salt));
125+
const cryptoKey = key || (await keyFromPassword({ password, salt })).key;
121126

122127
const result = await decryptWithKey(cryptoKey, payload);
123128
return result;
@@ -137,7 +142,11 @@ export async function decryptWithDetail(
137142
): Promise<DetailedDecryptResult> {
138143
const payload = JSON.parse(text);
139144
const { salt } = payload;
140-
const key = await keyFromPassword(password, salt);
145+
146+
const { key } = await keyFromPassword({
147+
password,
148+
salt,
149+
});
141150
const exportedKeyString = await exportKey(key);
142151
const vault = await decrypt(password, text, key);
143152

@@ -211,42 +220,96 @@ export async function exportKey(key: CryptoKey): Promise<string> {
211220
return JSON.stringify(exportedKey);
212221
}
213222

223+
type AllowedImportAlgorithms = 'PBKDF2';
224+
type AllowedDerivationAlgorithms = {
225+
name: 'PBKDF2';
226+
iterations: 10000;
227+
hash: 'SHA-256';
228+
};
229+
type AllowedDerivedKeyAlgorithms = {
230+
name: 'AES-GCM';
231+
length: 256;
232+
};
233+
234+
export type KeyDerivationOptions = {
235+
/**
236+
* The algorithm used to import a key from the password
237+
* (see {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey}).
238+
*/
239+
importAlgorithm?: AllowedImportAlgorithms;
240+
/**
241+
* The algorithm used to derive an encryption/decryption key
242+
* from the imported key (see {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey}).
243+
*/
244+
derivationAlgorithm?: AllowedDerivationAlgorithms;
245+
/**
246+
* The algorithm the derived key will be used for
247+
* (see {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey}).
248+
*/
249+
derivedKeyAlgorithm?: AllowedDerivedKeyAlgorithms;
250+
};
251+
214252
/**
215253
* Generate a CryptoKey from a password and random salt.
216254
*
217-
* @param password - The password to use to generate key.
218-
* @param salt - The salt string to use in key derivation.
219-
* @returns A CryptoKey for encryption and decryption.
255+
* @param options - Key derivation options.
256+
* @param options.keyDerivationOptions - Key derivation options.
257+
* @param options.password - The password to use to generate key.
258+
* @param options.salt - The salt string to use in key derivation.
259+
* @returns The derived key, along with all encryption options used.
220260
*/
221-
export async function keyFromPassword(
222-
password: string,
223-
salt: string,
224-
): Promise<CryptoKey> {
261+
export async function keyFromPassword({
262+
keyDerivationOptions = {},
263+
password,
264+
salt,
265+
}: {
266+
keyDerivationOptions?: KeyDerivationOptions;
267+
password: string;
268+
salt: string;
269+
}): Promise<{
270+
keyDerivationOptions: KeyDerivationOptions;
271+
key: CryptoKey;
272+
}> {
225273
const passBuffer = Buffer.from(password, STRING_ENCODING);
226274
const saltBuffer = Buffer.from(salt, 'base64');
275+
const importAlgorithm = keyDerivationOptions.importAlgorithm || 'PBKDF2';
276+
const derivationAlgorithm = keyDerivationOptions.derivationAlgorithm || {
277+
name: 'PBKDF2',
278+
iterations: 10000,
279+
hash: 'SHA-256',
280+
};
281+
const derivedKeyAlgorithm = keyDerivationOptions.derivedKeyAlgorithm || {
282+
name: 'AES-GCM',
283+
length: 256,
284+
};
227285

228286
const key = await global.crypto.subtle.importKey(
229287
'raw',
230288
passBuffer,
231-
{ name: 'PBKDF2' },
289+
importAlgorithm,
232290
false,
233291
['deriveBits', 'deriveKey'],
234292
);
235293

236294
const derivedKey = await global.crypto.subtle.deriveKey(
237295
{
238-
name: 'PBKDF2',
296+
...derivationAlgorithm,
239297
salt: saltBuffer,
240-
iterations: 10000,
241-
hash: 'SHA-256',
242298
},
243299
key,
244-
{ name: DERIVED_KEY_FORMAT, length: 256 },
300+
derivedKeyAlgorithm,
245301
true,
246302
['encrypt', 'decrypt'],
247303
);
248304

249-
return derivedKey;
305+
return {
306+
key: derivedKey,
307+
keyDerivationOptions: {
308+
importAlgorithm,
309+
derivationAlgorithm,
310+
derivedKeyAlgorithm,
311+
},
312+
};
250313
}
251314

252315
/**

test/index.spec.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,10 @@ test('encryptor:encrypt using key then decrypt', async ({ page }) => {
216216

217217
const encryptedData = await page.evaluate(
218218
async (args) => {
219-
const key = await window.encryptor.keyFromPassword(
220-
args.password,
221-
args.salt,
222-
);
219+
const { key } = await window.encryptor.keyFromPassword({
220+
password: args.password,
221+
salt: args.salt,
222+
});
223223
return await window.encryptor.encryptWithKey(key, args.data);
224224
},
225225
{ data, password, salt },
@@ -248,10 +248,10 @@ test('encryptor:encrypt using key then decrypt using wrong password', async ({
248248

249249
const encryptedData = await page.evaluate(
250250
async (args) => {
251-
const key = await window.encryptor.keyFromPassword(
252-
args.password,
253-
args.salt,
254-
);
251+
const { key } = await window.encryptor.keyFromPassword({
252+
password: args.password,
253+
salt: args.salt,
254+
});
255255
return await window.encryptor.encryptWithKey(key, args.data);
256256
},
257257
{ data, password, salt },
@@ -288,10 +288,10 @@ test('encryptor:encrypt then decrypt using key', async ({ page }) => {
288288

289289
const decryptedData = await page.evaluate(
290290
async (args) => {
291-
const key = await window.encryptor.keyFromPassword(
292-
args.password,
293-
args.salt,
294-
);
291+
const { key } = await window.encryptor.keyFromPassword({
292+
password: args.password,
293+
salt: args.salt,
294+
});
295295
return await window.encryptor.decryptWithKey(key, args.encryptedPayload);
296296
},
297297
{ encryptedPayload, password, salt },
@@ -319,10 +319,10 @@ test('encryptor:encrypt then decrypt using key derived from wrong password', asy
319319
await expect(
320320
page.evaluate(
321321
async (args) => {
322-
const key = await window.encryptor.keyFromPassword(
323-
args.wrongPassword,
324-
args.salt,
325-
);
322+
const { key } = await window.encryptor.keyFromPassword({
323+
password: args.wrongPassword,
324+
salt: args.salt,
325+
});
326326
return await window.encryptor.decryptWithKey(
327327
key,
328328
args.encryptedPayload,
@@ -344,10 +344,10 @@ test('encryptor:decrypt encrypted data using key', async ({ page }) => {
344344

345345
const decryptedData = await page.evaluate(
346346
async (args) => {
347-
const key = await window.encryptor.keyFromPassword(
348-
args.password,
349-
args.salt,
350-
);
347+
const { key } = await window.encryptor.keyFromPassword({
348+
password: args.password,
349+
salt: args.salt,
350+
});
351351
return await window.encryptor.decryptWithKey(key, args.encryptedPayload);
352352
},
353353
{ encryptedPayload, password, salt },
@@ -369,10 +369,10 @@ test('encryptor:decrypt encrypted data using key derived from wrong password', a
369369
await expect(
370370
page.evaluate(
371371
async (args) => {
372-
const key = await window.encryptor.keyFromPassword(
373-
args.wrongPassword,
374-
args.salt,
375-
);
372+
const { key } = await window.encryptor.keyFromPassword({
373+
password: args.wrongPassword,
374+
salt: args.salt,
375+
});
376376
return await window.encryptor.decryptWithKey(
377377
key,
378378
args.encryptedPayload,

0 commit comments

Comments
 (0)