diff --git a/README.md b/README.md index 0a5fd53..3f1f16e 100644 --- a/README.md +++ b/README.md @@ -131,19 +131,19 @@ expect(decryptedEmail).toStrictEqual(email); // keystore const userEmail = 'user email'; -const secretKey = genSymmetricKey(); -const { encryptionKeystore, recoveryKeystore, recoveryCodes } = await createEncryptionAndRecoveryKeystores( +const password = 'user password'; +const { encryptionKeystore, recoveryKeystore, recoveryCodes, salt } = await createEncryptionAndRecoveryKeystores( userEmail, - secretKey, + password ); -const resultEnc = await openEncryptionKeystore(encryptionKeystore, secretKey); +const resultEnc = await openEncryptionKeystore(encryptionKeystore, password, salt); const resultRec = await openRecoveryKeystore(recoveryCodes, recoveryKeystore); expect(resultEnc).toStrictEqual(resultRec); // Email storage and search -// Between sessions emails are stored encrypted in IndexedDB. The encryption key is derived from user's baseKey +// Between sessions emails are stored encrypted in IndexedDB. The encryption key is derived from user's mnemonic // During the session, all emails are decrypted and stored in the cache (up to 600 MB, if excides - we delete oldests emails) // For search, we build a search index from cache, then use Flexsearch for the search. // The search is doen separately for email content, subject, sender and recivers. @@ -151,9 +151,13 @@ expect(resultEnc).toStrictEqual(resultRec); // Open IndexedDB database const userID = 'user ID'; const db = await openDatabase(userID); +const mnemonic = genMnemonic(); -// Derive database key -const key = await deriveDatabaseKey(baseKey); +// Derive key for encrypting emails before storing them in a local database +const key = await deriveDatabaseKey(mnemonic); + +// Derive key for encrypting email draft +const key = await deriveDatabaseKey(mnemonic); // Encrypt and store one or several emails await encryptAndStoreEmail(email, key, db); diff --git a/package.json b/package.json index 43b458d..2057c86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "internxt-crypto", - "version": "1.1.1", + "version": "1.2.0", "main": "dist/index.js", "types": "dist/index.d.ts", "module": "dist/index.js", diff --git a/src/constants.ts b/src/constants.ts index febbcc1..432484e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,8 +3,9 @@ export const IV_LEN_BYTES = 12; export const CONTEXT_ENC_KEYSTORE = 'CRYPTO library 2025-07-30 16:18:03 key for opening encryption keys keystore'; export const CONTEXT_RECOVERY = 'CRYPTO library 2025-07-30 16:20:00 key for account recovery'; -export const CONTEXT_INDEX = 'CRYPTO library 2025-07-30 17:20:00 key for protecting current search indices'; -export const CONTEXT_DERIVE = 'CRYPTO library 2025-08-27 17:08:00 derive one key from two keys'; +export const CONTEXT_DATABASE = 'CRYPTO library 2025-07-30 17:20:00 key for protecting current search indices'; +export const CONTEXT_DRAFT = 'CRYPTO library 2026-04-23 10:56:00 key for protecting email drafts'; +export const CONTEXT_TWO_KEYS = 'CRYPTO library 2025-08-27 17:08:00 derive one key from two keys'; // Second recommended parameter set from RFC 9106 export const ARGON2ID_PARALLELISM = 3; diff --git a/src/derive-key/deriveKeysFromKey.ts b/src/derive-key/deriveKeysFromKey.ts index 10ada44..2fa65a0 100644 --- a/src/derive-key/deriveKeysFromKey.ts +++ b/src/derive-key/deriveKeysFromKey.ts @@ -1,5 +1,5 @@ import { blake3 } from '@noble/hashes/blake3.js'; -import { AES_KEY_BYTE_LENGTH, CONTEXT_DERIVE, CONTEXT_INDEX } from '../constants'; +import { AES_KEY_BYTE_LENGTH, CONTEXT_TWO_KEYS } from '../constants'; import { UTF8ToUint8 } from '../utils'; /** @@ -27,18 +27,8 @@ export function deriveSymmetricKeyFromTwoKeys(key1: Uint8Array, key2: Uint8Array throw new Error(`Input key length must be exactly ${AES_KEY_BYTE_LENGTH} bytes`); } const key = blake3(key1, { key: key2 }); - return deriveSymmetricKeyFromContext(CONTEXT_DERIVE, key); + return deriveSymmetricKeyFromContext(CONTEXT_TWO_KEYS, key); } catch (error) { throw new Error('Failed to derive symmetric key from two keys and context', { cause: error }); } } - -/** - * Derives database encryption key for the given user - * - * @param baseKey - The base key (NOT PASSWORD!) - * @returns The symmetric key for protecting database - */ -export const deriveDatabaseKey = async (baseKey: Uint8Array): Promise => { - return deriveSymmetricKeyFromContext(CONTEXT_INDEX, baseKey); -}; diff --git a/src/derive-key/deriveKeysFromMnemonic.ts b/src/derive-key/deriveKeysFromMnemonic.ts new file mode 100644 index 0000000..873f74d --- /dev/null +++ b/src/derive-key/deriveKeysFromMnemonic.ts @@ -0,0 +1,16 @@ +import { mnemonicToBytes } from '../utils'; +import { deriveSymmetricKeyFromContext } from './deriveKeysFromKey'; + +/** + * Derives encryption key from the user's mnemonic and context string + * + * @param mnemonic - The user's mnemonic (machine-generated with secure PRNG) + * @param context - The context string. + * The context string should be hardcoded, globally unique, and application-specific. + * @returns The symmetric key for protecting database + */ +export const deriveKeyFromMnemonic = async (mnemonic: string, context: string): Promise => { + // mnemonic is always machine-generated with secure PRNG, so it is safe to convert it to bytes without additional processing + const entropy = mnemonicToBytes(mnemonic); + return deriveSymmetricKeyFromContext(context, entropy); +}; diff --git a/src/derive-key/index.ts b/src/derive-key/index.ts index 6efca8e..301b455 100644 --- a/src/derive-key/index.ts +++ b/src/derive-key/index.ts @@ -1 +1,2 @@ export * from './deriveKeysFromKey'; +export * from './deriveKeysFromMnemonic'; diff --git a/src/derive-password/core.ts b/src/derive-password/core.ts index 4bd9e60..c95e680 100644 --- a/src/derive-password/core.ts +++ b/src/derive-password/core.ts @@ -3,10 +3,8 @@ import { ARGON2ID_ITERATIONS, ARGON2ID_MEMORY_SIZE, ARGON2ID_PARALLELISM, - ARGON2ID_SALT_BYTE_LENGTH, ARGON2ID_OUTPUT_BYTE_LENGTH, } from '../constants'; -import { randomBytes } from '@noble/hashes/utils.js'; /** * Calculates hash using the argon2id password-hashing function @@ -37,12 +35,3 @@ export async function argon2( outputType: 'binary', }); } - -/** - * Samples a salt - * - * @returns The salt - */ -export function sampleSalt(): Uint8Array { - return randomBytes(ARGON2ID_SALT_BYTE_LENGTH); -} diff --git a/src/derive-password/deriveKeysFromPassword.ts b/src/derive-password/deriveKeysFromPassword.ts index 2a84d76..6c533fc 100644 --- a/src/derive-password/deriveKeysFromPassword.ts +++ b/src/derive-password/deriveKeysFromPassword.ts @@ -1,4 +1,15 @@ -import { argon2, sampleSalt } from './core'; +import { argon2 } from './core'; +import { ARGON2ID_SALT_BYTE_LENGTH } from '../constants'; +import { randomBytes } from '@noble/hashes/utils.js'; + +/** + * Samples a salt + * + * @returns The salt + */ +export function generateSalt(): Uint8Array { + return randomBytes(ARGON2ID_SALT_BYTE_LENGTH); +} /** * Derives a symmetric key from a user's password with a randomly sampled salt @@ -8,7 +19,7 @@ import { argon2, sampleSalt } from './core'; */ export async function getKeyFromPassword(password: string): Promise<{ key: Uint8Array; salt: Uint8Array }> { try { - const salt = sampleSalt(); + const salt = generateSalt(); const key = await argon2(password, salt); return { key, salt }; } catch (error) { diff --git a/src/email-crypto/emailKeys.ts b/src/email-crypto/emailKeys.ts index 9f36d1e..17a5898 100644 --- a/src/email-crypto/emailKeys.ts +++ b/src/email-crypto/emailKeys.ts @@ -1,5 +1,7 @@ import { genHybridKeys } from '../hybrid-crypto'; import { HybridKeyPair } from '../types'; +import { deriveKeyFromMnemonic } from '../derive-key'; +import { CONTEXT_DATABASE, CONTEXT_DRAFT } from '../constants'; /** * Generates public and private keys for email encryption. @@ -9,3 +11,23 @@ import { HybridKeyPair } from '../types'; export async function generateEmailKeys(): Promise { return genHybridKeys(); } + +/** + * Derives database encryption key for the given user + * + * @param mnemonic - The user's mnemonic (machine-generated with secure PRNG) + * @returns The symmetric key for protecting database + */ +export const deriveDatabaseKey = async (mnemonic: string): Promise => { + return deriveKeyFromMnemonic(mnemonic, CONTEXT_DATABASE); +}; + +/** + * Derives email draft encryption key for the given user + * + * @param mnemonic - The user's mnemonic (machine-generated with secure PRNG) + * @returns The symmetric key for protecting email drafts + */ +export const deriveEmailDraftKey = async (mnemonic: string): Promise => { + return deriveKeyFromMnemonic(mnemonic, CONTEXT_DRAFT); +}; diff --git a/src/index.ts b/src/index.ts index 1c8e81f..157aa97 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ export { deriveSecretKey, generateEccKeys } from './asymmetric-crypto'; -export { deriveSymmetricKeyFromTwoKeys, deriveSymmetricKeyFromContext, deriveDatabaseKey } from './derive-key'; -export { getKeyFromPassword, getKeyFromPasswordAndSalt } from './derive-password'; +export { deriveSymmetricKeyFromTwoKeys, deriveSymmetricKeyFromContext, deriveKeyFromMnemonic } from './derive-key'; +export { getKeyFromPassword, getKeyFromPasswordAndSalt, generateSalt } from './derive-password'; export { encryptEmailHybrid, encryptEmailHybridForMultipleRecipients, @@ -11,6 +11,8 @@ export { decryptEmailBody, encryptEmailBody, encryptEmailBodyWithKey, + deriveDatabaseKey, + deriveEmailDraftKey, } from './email-crypto'; export { hashDataArray, diff --git a/src/keystore-crypto/core.ts b/src/keystore-crypto/core.ts index 353bd15..a251df5 100644 --- a/src/keystore-crypto/core.ts +++ b/src/keystore-crypto/core.ts @@ -1,8 +1,9 @@ import { encryptSymmetrically, decryptSymmetrically } from '../symmetric-crypto'; -import { base64ToUint8Array, uint8ArrayToBase64, UTF8ToUint8, mnemonicToBytes } from '../utils'; -import { deriveSymmetricKeyFromContext } from '../derive-key'; +import { base64ToUint8Array, uint8ArrayToBase64, UTF8ToUint8 } from '../utils'; +import { deriveKeyFromMnemonic, deriveSymmetricKeyFromContext } from '../derive-key'; import { CONTEXT_ENC_KEYSTORE, CONTEXT_RECOVERY } from '../constants'; import { EncryptedKeystore, HybridKeyPair, KeystoreType } from '../types'; +import { getKeyFromPasswordAndSalt, getKeyFromPassword } from '../derive-password'; /** * Encrypts the user's hybrid key using symmetric encryption to get a keystore @@ -64,20 +65,46 @@ export async function decryptKeystoreContent( /** * Derives a secret key for protecting the recovery keystore * - * @param recoveryCodes - The recovery codes + * @param recoveryCodes - The recovery codes (machine-generated with secure PRNG) * @returns The derived secret key for protecting the recovery keystore */ export async function deriveRecoveryKey(recoveryCodes: string): Promise { - const recoverCodesArray = mnemonicToBytes(recoveryCodes); - return deriveSymmetricKeyFromContext(CONTEXT_RECOVERY, recoverCodesArray); + return deriveKeyFromMnemonic(recoveryCodes, CONTEXT_RECOVERY); +} + +/** + * Derives a secret key for protecting the encryption keystore from user's mnemonic + * + * @param mnemonic - The user's mnemonic (machine-generated with secure PRNG) + * @returns The derived secret key for protecting the encryption keystore + */ +export async function deriveEncryptionKeystoreKeyFromMnemonic(mnemonic: string): Promise { + return deriveKeyFromMnemonic(mnemonic, CONTEXT_ENC_KEYSTORE); } /** * Derives a secret key for protecting the encryption keystore * - * @param baseKey - The base secret key from which a new key secret will be derived + * @param password - The user's password + * @param salt - The keystore salt * @returns The derived secret key for protecting the encryption keystore */ -export async function deriveEncryptionKeystoreKey(baseKey: Uint8Array): Promise { +export async function deriveEncryptionKeystoreKey(password: string, salt: Uint8Array): Promise { + const baseKey = await getKeyFromPasswordAndSalt(password, salt); return deriveSymmetricKeyFromContext(CONTEXT_ENC_KEYSTORE, baseKey); } + +/** + * Derives a secret key for protecting the encryption keystore + * + * @param password - The user's password + * @param salt - The keystore salt + * @returns The derived secret key for protecting the encryption keystore + */ +export async function deriveNewEncryptionKeystoreKey( + password: string, +): Promise<{ secretKey: Uint8Array; salt: Uint8Array }> { + const { key, salt } = await getKeyFromPassword(password); + const secretKey = deriveSymmetricKeyFromContext(CONTEXT_ENC_KEYSTORE, key); + return { secretKey, salt }; +} diff --git a/src/keystore-crypto/emailEncryptionKey.ts b/src/keystore-crypto/emailEncryptionKey.ts index a0538f1..bf6ce51 100644 --- a/src/keystore-crypto/emailEncryptionKey.ts +++ b/src/keystore-crypto/emailEncryptionKey.ts @@ -1,35 +1,46 @@ import { EncryptedKeystore, KeystoreType, HybridKeyPair } from '../types'; import { genMnemonic } from '../utils'; -import { encryptKeystoreContent, decryptKeystoreContent, deriveEncryptionKeystoreKey, deriveRecoveryKey } from './core'; +import { + encryptKeystoreContent, + decryptKeystoreContent, + deriveEncryptionKeystoreKey, + deriveNewEncryptionKeystoreKey, + deriveRecoveryKey, +} from './core'; import { genHybridKeys } from '../hybrid-crypto'; /** * Generates hybrid keys and creates encrypted main and recovery keystores - * The main keystore encryption key is derived from the base key (stored in session storage) + * The main keystore encryption key is derived from the user's password * The recovery keystore encryption key is derived from the recovery codes * + * @param userEmail - The user's email + * @param password - The user's password + * @returns The encryption keys + * * @returns The encryption and recovery keystores, recovery codes and hybrid keys */ export async function createEncryptionAndRecoveryKeystores( userEmail: string, - baseKey: Uint8Array, + password: string, ): Promise<{ encryptionKeystore: EncryptedKeystore; recoveryKeystore: EncryptedKeystore; recoveryCodes: string; keys: HybridKeyPair; + salt: Uint8Array; }> { try { const keys = genHybridKeys(); - const secretKey = await deriveEncryptionKeystoreKey(baseKey); + const { secretKey, salt } = await deriveNewEncryptionKeystoreKey(password); const encryptionKeystore = await encryptKeystoreContent(secretKey, keys, userEmail, KeystoreType.ENCRYPTION); const recoveryCodes = genMnemonic(); const recoveryKey = await deriveRecoveryKey(recoveryCodes); const recoveryKeystore = await encryptKeystoreContent(recoveryKey, keys, userEmail, KeystoreType.RECOVERY); - return { encryptionKeystore, recoveryKeystore, recoveryCodes, keys }; + return { encryptionKeystore, recoveryKeystore, recoveryCodes, keys, salt }; } catch (error) { throw new Error('Failed to create encryption and recovery keystores', { cause: error }); } @@ -37,21 +48,23 @@ export async function createEncryptionAndRecoveryKeystores( /** * Opens the encryption keystore and returns the email encryption keys - * The decryption key is derived from the base key (stored in session storage) + * The decryption key is derived from the user password * * @param encryptedKeystore - The encrypted keystore containing encryption keys - * @param baseKey - The base key from which the decryption key will be derived + * @param password - The user's password + * @param salt - The keystore's salt * @returns The encryption keys */ export async function openEncryptionKeystore( encryptedKeystore: EncryptedKeystore, - baseKey: Uint8Array, + password: string, + salt: Uint8Array, ): Promise { try { if (encryptedKeystore.type != KeystoreType.ENCRYPTION) { throw new Error('Input is invalid'); } - const secretKey = await deriveEncryptionKeystoreKey(baseKey); + const secretKey = await deriveEncryptionKeystoreKey(password, salt); const keys = await decryptKeystoreContent(secretKey, encryptedKeystore); return keys; } catch (error) { @@ -61,7 +74,7 @@ export async function openEncryptionKeystore( /** * Opens the recovery keystore and returns the email encryption keys - * The decryption key is derived from the base key (stored in session storage) + * The decryption key is derived from the recovery codes (machine-generated mnemonic) * * @param recoveryCodes - The user's recovery codes * @param encryptedKeystore - The encrypted keystore containing encryption keys diff --git a/tests/derive-keys/deriveKeys.test.ts b/tests/derive-keys/deriveKeys.test.ts index 3838ab4..86f30e9 100644 --- a/tests/derive-keys/deriveKeys.test.ts +++ b/tests/derive-keys/deriveKeys.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { deriveSymmetricKeyFromTwoKeys, deriveSymmetricKeyFromContext, deriveDatabaseKey } from '../../src/derive-key'; +import { deriveSymmetricKeyFromTwoKeys, deriveSymmetricKeyFromContext } from '../../src/derive-key'; import { uint8ArrayToHex } from '../../src/utils'; import { AES_KEY_BYTE_LENGTH } from '../../src/constants'; import { genSymmetricKey } from '../../src/symmetric-crypto'; @@ -38,12 +38,4 @@ describe('Test derive key', () => { /Failed to derive symmetric key from two keys/, ); }); - - it('should derive symmetric key for database encryption', async () => { - const baseKey = genSymmetricKey(); - const key = await deriveDatabaseKey(baseKey); - expect(key.length).toBe(AES_KEY_BYTE_LENGTH); - const key2 = await deriveDatabaseKey(baseKey); - expect(key2).toStrictEqual(key); - }); }); diff --git a/tests/derive-keys/deriveKeysFromPwd.test.ts b/tests/derive-keys/deriveKeysFromPwd.test.ts index 085ba19..1b1a97f 100644 --- a/tests/derive-keys/deriveKeysFromPwd.test.ts +++ b/tests/derive-keys/deriveKeysFromPwd.test.ts @@ -1,12 +1,12 @@ import { describe, expect, it } from 'vitest'; -import { getKeyFromPasswordAndSalt, getKeyFromPassword } from '../../src/derive-password'; +import { getKeyFromPasswordAndSalt, getKeyFromPassword, generateSalt } from '../../src/derive-password'; -import { argon2, sampleSalt } from '../../src/derive-password/core'; +import { argon2 } from '../../src/derive-password/core'; import { uint8ArrayToHex } from '../../src/utils'; describe('Test Argon2', () => { const testPassword = 'text demo'; - const testSalt = sampleSalt(); + const testSalt = generateSalt(); it('should get correct key from the password', async () => { const testParallelism = 7; @@ -29,8 +29,8 @@ describe('Test Argon2', () => { }); it('should generate different salt each time', async () => { - const testSalt1 = uint8ArrayToHex(sampleSalt()); - const testSalt2 = uint8ArrayToHex(sampleSalt()); + const testSalt1 = uint8ArrayToHex(generateSalt()); + const testSalt2 = uint8ArrayToHex(generateSalt()); expect(testSalt1).not.toBe(testSalt2); }); diff --git a/tests/email-crypto/core.test.ts b/tests/email-crypto/core.test.ts index 0f8f675..1b513ea 100644 --- a/tests/email-crypto/core.test.ts +++ b/tests/email-crypto/core.test.ts @@ -1,8 +1,9 @@ import { describe, expect, it } from 'vitest'; import { EmailBody } from '../../src/types'; -import { decryptEmailBody, encryptEmailBody } from '../../src/email-crypto/core'; -import { generateUuid } from '../../src/utils'; +import { decryptEmailBody, encryptEmailBody, deriveDatabaseKey, deriveEmailDraftKey } from '../../src/email-crypto'; +import { generateUuid, genMnemonic } from '../../src/utils'; import { genSymmetricKey } from '../../src/symmetric-crypto'; +import { AES_KEY_BYTE_LENGTH } from '../../src/constants'; describe('Test email crypto functions', () => { const emailBody: EmailBody = { @@ -44,4 +45,28 @@ describe('Test email crypto functions', () => { badEmail.self = badEmail; await expect(encryptEmailBody(badEmail, aux)).rejects.toThrowError(/Failed to symmetrically encrypt email body/); }); + + it('should derive symmetric key for database encryption', async () => { + const mnemonic = genMnemonic(); + const key = await deriveDatabaseKey(mnemonic); + expect(key.length).toBe(AES_KEY_BYTE_LENGTH); + const key2 = await deriveDatabaseKey(mnemonic); + expect(key2).toStrictEqual(key); + }); + + it('should derive symmetric key for email draft encryption', async () => { + const mnemonic = genMnemonic(); + const key = await deriveEmailDraftKey(mnemonic); + expect(key.length).toBe(AES_KEY_BYTE_LENGTH); + const key2 = await deriveEmailDraftKey(mnemonic); + expect(key2).toStrictEqual(key); + }); + + it('should derive symmetric key for email draft encryption', async () => { + const mnemonic = genMnemonic(); + const keyDatabase = await deriveDatabaseKey(mnemonic); + const keyDraft = await deriveEmailDraftKey(mnemonic); + expect(keyDatabase.length).toBe(keyDraft.length); + expect(keyDraft).not.toStrictEqual(keyDatabase); + }); }); diff --git a/tests/keystore-crypto/emailEncryptionKeys.test.ts b/tests/keystore-crypto/emailEncryptionKeys.test.ts index e717471..4eb27c0 100644 --- a/tests/keystore-crypto/emailEncryptionKeys.test.ts +++ b/tests/keystore-crypto/emailEncryptionKeys.test.ts @@ -4,12 +4,10 @@ import { openEncryptionKeystore, openRecoveryKeystore, } from '../../src/keystore-crypto'; -import { genSymmetricKey } from '../../src/symmetric-crypto'; import { XWING_PUBLIC_KEY_LENGTH, XWING_SECRET_KEY_LENGTH } from '../../src/constants'; describe('Test keystore create/open functions', async () => { const mockUserEmail = 'mock user email'; - const secretKey = genSymmetricKey(); beforeEach(() => { vi.clearAllMocks(); @@ -17,11 +15,12 @@ describe('Test keystore create/open functions', async () => { }); it('should successfully create and open encryption keystore', async () => { - const { encryptionKeystore, recoveryKeystore, recoveryCodes } = await createEncryptionAndRecoveryKeystores( + const password = 'user password'; + const { encryptionKeystore, recoveryKeystore, recoveryCodes, salt } = await createEncryptionAndRecoveryKeystores( mockUserEmail, - secretKey, + password, ); - const resultEnc = await openEncryptionKeystore(encryptionKeystore, secretKey); + const resultEnc = await openEncryptionKeystore(encryptionKeystore, password, salt); const resultRec = await openRecoveryKeystore(recoveryCodes, recoveryKeystore); expect(resultEnc).toStrictEqual(resultRec); @@ -31,13 +30,14 @@ describe('Test keystore create/open functions', async () => { expect(resultEnc.secretKey.length).toBe(XWING_SECRET_KEY_LENGTH); }); - it('should throw an error if no base key for keystore opening', async () => { - const { encryptionKeystore, recoveryKeystore } = await createEncryptionAndRecoveryKeystores( + it('should throw an error if no password for keystore opening', async () => { + const password = 'user password'; + const { encryptionKeystore, recoveryKeystore, salt } = await createEncryptionAndRecoveryKeystores( mockUserEmail, - secretKey, + password, ); - await expect(openEncryptionKeystore(encryptionKeystore, new Uint8Array([]))).rejects.toThrowError( + await expect(openEncryptionKeystore(encryptionKeystore, '', salt)).rejects.toThrowError( /Failed to open encryption keystore/, ); await expect(openRecoveryKeystore('', recoveryKeystore)).rejects.toThrowError(/Failed to open recovery keystore/); diff --git a/tests/keystore-crypto/keys.test.ts b/tests/keystore-crypto/keys.test.ts index 67d5208..f794f01 100644 --- a/tests/keystore-crypto/keys.test.ts +++ b/tests/keystore-crypto/keys.test.ts @@ -1,21 +1,23 @@ import { describe, expect, it } from 'vitest'; import { deriveEncryptionKeystoreKey, deriveRecoveryKey } from '../../src/keystore-crypto/core'; -import { genSymmetricKey } from '../../src/symmetric-crypto'; import { AES_KEY_BYTE_LENGTH } from '../../src/constants'; import { genMnemonic } from '../../src/utils'; +import { generateSalt } from '../../src/derive-password'; describe('Test keystore key generation functions', () => { it('correct symmetric key length', async () => { - const baseKey = genSymmetricKey(); - const key = await deriveEncryptionKeystoreKey(baseKey); + const password = 'user password'; + const salt = generateSalt(); + const key = await deriveEncryptionKeystoreKey(password, salt); expect(key.length).toBe(AES_KEY_BYTE_LENGTH); }); it('should give different derived keys for the same baseKey', async () => { const codes = genMnemonic(); - const baseKey = genSymmetricKey(); + const password = 'user password'; + const salt = generateSalt(); - const encryptionKey = await deriveEncryptionKeystoreKey(baseKey); + const encryptionKey = await deriveEncryptionKeystoreKey(password, salt); const recoveryKey = await deriveRecoveryKey(codes); expect(encryptionKey).not.toStrictEqual(recoveryKey); diff --git a/tsconfig.json b/tsconfig.json index 40dd4a6..7212553 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "declaration": true, "declarationMap": true, "emitDeclarationOnly": true, + "rootDir": "./src", "outDir": "dist", "strict": true, "esModuleInterop": true,