From 6f44e222b65428ab6ebe82c0a3adeaa0d3071fb6 Mon Sep 17 00:00:00 2001 From: Mateus Dal Bianco Date: Fri, 13 Oct 2023 10:43:55 +0200 Subject: [PATCH] create sync methods for password hashing --- package.json | 2 +- src/declarations/scrypt-js.d.ts | 16 --------------- .../getHKDFKeysFromPassword.spec.ts | 17 +++++++++++++++- .../getHKDFKeysFromPassword.ts | 10 ++++++++++ src/hashPassword/hashPassword.spec.ts | 11 +++++++++- src/hashPassword/hashPassword.ts | 20 ++++++------------- yarn.lock | 8 ++++---- 7 files changed, 47 insertions(+), 37 deletions(-) delete mode 100644 src/declarations/scrypt-js.d.ts diff --git a/package.json b/package.json index 94acf86c..e3e3a935 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "lodash": "4.17.21", "node-fetch": "2.6.0", "randombytes": "2.1.0", - "scrypt-js": "2.0.4", + "scrypt-js": "3.0.1", "sha3": "2.0.4", "smart-buffer": "4.2.0", "tiny-secp256k1": "1.1.3" diff --git a/src/declarations/scrypt-js.d.ts b/src/declarations/scrypt-js.d.ts deleted file mode 100644 index 0b711232..00000000 --- a/src/declarations/scrypt-js.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Types on DefinitelyTyped are wrong -declare module 'scrypt-js' { - type Callback = (error: Error, progress: number, key: string) => any - - function scrypt( - password: Buffer, - salt: Buffer, - N: number, - r: number, - p: number, - dkLen: number, - callback: Callback - ): void - - export = scrypt -} diff --git a/src/getHKDFKeysFromPassword/getHKDFKeysFromPassword.spec.ts b/src/getHKDFKeysFromPassword/getHKDFKeysFromPassword.spec.ts index e79b8b2c..bbc06b1d 100644 --- a/src/getHKDFKeysFromPassword/getHKDFKeysFromPassword.spec.ts +++ b/src/getHKDFKeysFromPassword/getHKDFKeysFromPassword.spec.ts @@ -1,4 +1,4 @@ -import getHKDFKeysFromPassword from './getHKDFKeysFromPassword' +import getHKDFKeysFromPassword, { syncGetHKDFKeysFromPassword } from './getHKDFKeysFromPassword' import stringify from '../stringify' @@ -16,8 +16,23 @@ testVectors.forEach((vector, idx) => }) ) +testVectors.forEach((vector, idx) => + test(`passes test vector ${idx + 1} (sync)`, () => { + const output = syncGetHKDFKeysFromPassword(vector.password, vector.salt) + + expect(stringify(output.authKey)).toBe(vector.authKey) + expect(stringify(output.encryptionKey)).toBe(vector.encryptionKey) + }) +) + test('generates symmetrical keys', async () => { const output = await getHKDFKeysFromPassword(password, salt) expect(output.authKey.length).toBe(output.encryptionKey.length) }) + +test('generates symmetrical keys (sync)', () => { + const output = syncGetHKDFKeysFromPassword(password, salt) + + expect(output.authKey.length).toBe(output.encryptionKey.length) +}) diff --git a/src/getHKDFKeysFromPassword/getHKDFKeysFromPassword.ts b/src/getHKDFKeysFromPassword/getHKDFKeysFromPassword.ts index 188beebe..4292fb1f 100644 --- a/src/getHKDFKeysFromPassword/getHKDFKeysFromPassword.ts +++ b/src/getHKDFKeysFromPassword/getHKDFKeysFromPassword.ts @@ -1,6 +1,7 @@ import hkdf from 'futoin-hkdf' import hashPassword from '../hashPassword' import HKDFKeys from '../types/HKDFKeys' +import { syncHashPassword } from '../hashPassword/hashPassword' /* HKDF parameters @@ -26,3 +27,12 @@ export default async function getHKDFKeysFromPassword(password: string, salt: st encryptionKey: hkdf(hashed, length, { hash, info: 'encryption', salt }) } } + +export function syncGetHKDFKeysFromPassword(password: string, salt: string): HKDFKeys { + const hashed = syncHashPassword(password, salt) + + return { + authKey: hkdf(hashed, length, { hash, info: 'auth', salt }), + encryptionKey: hkdf(hashed, length, { hash, info: 'encryption', salt }) + } +} diff --git a/src/hashPassword/hashPassword.spec.ts b/src/hashPassword/hashPassword.spec.ts index 9d49e681..cfd6831e 100644 --- a/src/hashPassword/hashPassword.spec.ts +++ b/src/hashPassword/hashPassword.spec.ts @@ -1,4 +1,4 @@ -import hashPassword from './hashPassword' +import hashPassword, { syncHashPassword } from './hashPassword' import stringify from '../stringify' @@ -10,3 +10,12 @@ test('should properly hash a password', async () => { expect(stringify(output)).toBe(expectation) }) + +test('should properly hash a password (sync)', () => { + const password = 'hunter2' + const salt = 'b0cd9948365b' + const output = syncHashPassword(password, salt) + const expectation = 'bd38c1f44bfc5e6d0ac281c9b87cbd97e5a79134c960d856e4268fe2ca16f5e9' + + expect(stringify(output)).toBe(expectation) +}) diff --git a/src/hashPassword/hashPassword.ts b/src/hashPassword/hashPassword.ts index db4d86bd..7b5fb0b6 100644 --- a/src/hashPassword/hashPassword.ts +++ b/src/hashPassword/hashPassword.ts @@ -1,4 +1,4 @@ -import scrypt from 'scrypt-js' +import { scrypt, syncScrypt } from 'scrypt-js' import normalizeString from '../utils/normalizeString' @@ -17,18 +17,10 @@ const dkLen = 32 * Hashes a plaintext password via the * [scrypt key derivation function](https://en.wikipedia.org/wiki/Scrypt). */ -export default function hashPassword(password: string, salt: string): Promise { - return new Promise(res => - scrypt(normalizeString(password), normalizeString(salt), N, r, p, dkLen, (error: Error, _: number, key: string) => { - // throw instead of reject because this will only happen on code error - if (error) { - throw error - } +export default async function hashPassword(password: string, salt: string): Promise { + return Buffer.from(await scrypt(normalizeString(password), normalizeString(salt), N, r, p, dkLen)) +} - // Progress tracker unhandled, we can incorporate it later if needed - if (key) { - return res(Buffer.from(key)) - } - }) - ) +export function syncHashPassword(password: string, salt: string): Buffer { + return Buffer.from(syncScrypt(normalizeString(password), normalizeString(salt), N, r, p, dkLen)) } diff --git a/yarn.lock b/yarn.lock index f59fd8e9..52229397 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4597,10 +4597,10 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scrypt-js@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" - integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== +scrypt-js@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== secp256k1@^3.0.1: version "3.7.1"