11type DetailedEncryptionResult = {
22 vault : string ;
33 exportedKeyString : string ;
4+ keyDerivationOptions : KeyDerivationOptions ;
45} ;
56
67type 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 ;
@@ -129,15 +134,22 @@ export async function decrypt(
129134 *
130135 * @param password - The password to decrypt with.
131136 * @param text - The encrypted vault to decrypt.
137+ * @param keyDerivationOptions - Key derivation options.
132138 * @returns The decrypted vault along with the salt and exported key.
133139 */
134140export async function decryptWithDetail (
135141 password : string ,
136142 text : string ,
143+ keyDerivationOptions ?: KeyDerivationOptions ,
137144) : Promise < DetailedDecryptResult > {
138145 const payload = JSON . parse ( text ) ;
139146 const { salt } = payload ;
140- const key = await keyFromPassword ( password , salt ) ;
147+
148+ const { key } = await keyFromPassword ( {
149+ password,
150+ salt,
151+ ...( keyDerivationOptions || { } ) ,
152+ } ) ;
141153 const exportedKeyString = await exportKey ( key ) ;
142154 const vault = await decrypt ( password , text , key ) ;
143155
@@ -211,42 +223,96 @@ export async function exportKey(key: CryptoKey): Promise<string> {
211223 return JSON . stringify ( exportedKey ) ;
212224}
213225
226+ type AllowedImportAlgorithms = 'PBKDF2' ;
227+ type AllowedDerivationAlgorithms = {
228+ name : 'PBKDF2' ;
229+ iterations : 10000 ;
230+ hash : 'SHA-256' ;
231+ } ;
232+ type AllowedDerivedKeyAlgorithms = {
233+ name : 'AES-GCM' ;
234+ length : 256 ;
235+ } ;
236+
237+ type KeyDerivationOptions = {
238+ /**
239+ * The algorithm used to import a key from the password
240+ * (see {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey}).
241+ */
242+ importAlgorithm ?: AllowedImportAlgorithms ;
243+ /**
244+ * The algorithm used to derive an encryption/decryption key
245+ * from the imported key (see {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey}).
246+ */
247+ derivationAlgorithm ?: AllowedDerivationAlgorithms ;
248+ /**
249+ * The algorithm the derived key will be used for
250+ * (see {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey}).
251+ */
252+ derivedKeyAlgorithm ?: AllowedDerivedKeyAlgorithms ;
253+ } ;
254+
214255/**
215256 * Generate a CryptoKey from a password and random salt.
216257 *
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.
258+ * @param options - Key derivation options.
259+ * @param options.keyDerivationOptions - Key derivation options.
260+ * @param options.password - The password to use to generate key.
261+ * @param options.salt - The salt string to use in key derivation.
262+ * @returns The derived key, along with all encryption options used.
220263 */
221- export async function keyFromPassword (
222- password : string ,
223- salt : string ,
224- ) : Promise < CryptoKey > {
264+ export async function keyFromPassword ( {
265+ keyDerivationOptions = { } ,
266+ password,
267+ salt,
268+ } : {
269+ keyDerivationOptions ?: KeyDerivationOptions ;
270+ password : string ;
271+ salt : string ;
272+ } ) : Promise < {
273+ keyDerivationOptions : KeyDerivationOptions ;
274+ key : CryptoKey ;
275+ } > {
225276 const passBuffer = Buffer . from ( password , STRING_ENCODING ) ;
226277 const saltBuffer = Buffer . from ( salt , 'base64' ) ;
278+ const importAlgorithm = keyDerivationOptions . importAlgorithm || 'PBKDF2' ;
279+ const derivationAlgorithm = keyDerivationOptions . derivationAlgorithm || {
280+ name : 'PBKDF2' ,
281+ iterations : 10000 ,
282+ hash : 'SHA-256' ,
283+ } ;
284+ const derivedKeyAlgorithm = keyDerivationOptions . derivedKeyAlgorithm || {
285+ name : 'AES-GCM' ,
286+ length : 256 ,
287+ } ;
227288
228289 const key = await global . crypto . subtle . importKey (
229290 'raw' ,
230291 passBuffer ,
231- { name : 'PBKDF2' } ,
292+ importAlgorithm ,
232293 false ,
233294 [ 'deriveBits' , 'deriveKey' ] ,
234295 ) ;
235296
236297 const derivedKey = await global . crypto . subtle . deriveKey (
237298 {
238- name : 'PBKDF2' ,
299+ ... derivationAlgorithm ,
239300 salt : saltBuffer ,
240- iterations : 10000 ,
241- hash : 'SHA-256' ,
242301 } ,
243302 key ,
244- { name : DERIVED_KEY_FORMAT , length : 256 } ,
303+ derivedKeyAlgorithm ,
245304 true ,
246305 [ 'encrypt' , 'decrypt' ] ,
247306 ) ;
248307
249- return derivedKey ;
308+ return {
309+ key : derivedKey ,
310+ keyDerivationOptions : {
311+ importAlgorithm,
312+ derivationAlgorithm,
313+ derivedKeyAlgorithm,
314+ } ,
315+ } ;
250316}
251317
252318/**
0 commit comments