diff --git a/packages/argon2/__test__/argon2.spec.ts b/packages/argon2/__test__/argon2.spec.ts index fed6a5e7..ed4a691c 100644 --- a/packages/argon2/__test__/argon2.spec.ts +++ b/packages/argon2/__test__/argon2.spec.ts @@ -2,7 +2,7 @@ import { randomBytes } from 'crypto' import test from 'ava' -import { Algorithm, hash, verify, Version } from '../index.js' +import { Algorithm, hash, hashRaw, verify, Version } from '../index.js' const passwordString = 'some_string123' const passwordBuffer = Buffer.from(passwordString) @@ -43,6 +43,16 @@ test('should be able to hash string', async (t) => { ) }) +test('should be able to hashRaw string with a defined salt', async (t) => { + await t.notThrowsAsync(() => hash('whatever')) + await t.notThrowsAsync(() => + hashRaw('whatever', { + secret: randomBytes(32), + salt: randomBytes(32), + }), + ) +}) + test('should be able to verify hashed string', async (t) => { const PASSWORD = 'Argon2_is_the_best_algorithm_ever' t.true(await verify(await hash(PASSWORD), PASSWORD)) @@ -88,7 +98,7 @@ test('should return memoryCost error', async (t) => { }), ) - t.is(error.message, 'memory cost is too small') + t.is(error?.message, 'memory cost is too small') }) test('should return timeCost error', async (t) => { @@ -98,7 +108,7 @@ test('should return timeCost error', async (t) => { }), ) - t.is(error.message, 'time cost is too small') + t.is(error?.message, 'time cost is too small') }) test('should return parallelism error', async (t) => { @@ -109,5 +119,5 @@ test('should return parallelism error', async (t) => { }), ) - t.is(error.message, 'not enough threads') + t.is(error?.message, 'not enough threads') }) diff --git a/packages/argon2/index.d.ts b/packages/argon2/index.d.ts index 749497b8..8f5704e4 100644 --- a/packages/argon2/index.d.ts +++ b/packages/argon2/index.d.ts @@ -64,6 +64,7 @@ export interface Options { algorithm?: Algorithm version?: Version secret?: Buffer + salt?: Buffer } export function hash( password: string | Buffer, diff --git a/packages/argon2/src/lib.rs b/packages/argon2/src/lib.rs index 66d8442c..9773e21b 100644 --- a/packages/argon2/src/lib.rs +++ b/packages/argon2/src/lib.rs @@ -89,6 +89,7 @@ pub struct Options { pub algorithm: Option, pub version: Option, pub secret: Option, + pub salt: Option, } impl Options { @@ -122,6 +123,7 @@ impl Task for HashTask { fn compute(&mut self) -> Result { let salt = SaltString::generate(&mut OsRng); + let hasher = self.options.to_argon(); hasher .map_err(|err| Error::new(Status::InvalidArg, format!("{err}")))? @@ -181,7 +183,6 @@ impl Task for RawHashTask { type JsValue = Buffer; fn compute(&mut self) -> Result { - let salt = SaltString::generate(&mut OsRng); let hasher = self .options .to_argon() @@ -192,10 +193,19 @@ impl Task for RawHashTask { .unwrap_or(Params::DEFAULT_OUTPUT_LEN); let mut output = vec![0; output_len]; - hasher - .hash_password_into(self.password.as_slice(), salt.as_bytes(), &mut output) - .map_err(|err| Error::new(Status::GenericFailure, format!("{err}"))) - .map(|_| output) + match &self.options.salt { + Some(buf) => hasher.hash_password_into(self.password.as_slice(), buf.as_ref(), &mut output), + None => { + let generated_salt = SaltString::generate(&mut OsRng); + hasher.hash_password_into( + self.password.as_slice(), + generated_salt.as_bytes(), + &mut output, + ) + } + } + .map_err(|err| Error::new(Status::GenericFailure, format!("{err}"))) + .map(|_| output) } fn resolve(&mut self, _env: Env, output: Self::Output) -> Result {