From b3f5696e4f9e7969e8bbab8f3dcd41c114046d03 Mon Sep 17 00:00:00 2001 From: Corentin Mors Date: Fri, 3 Mar 2023 22:22:09 +0100 Subject: [PATCH 1/3] Support salt input on Argon2 Signed-off-by: Corentin Mors --- packages/argon2/__test__/argon2.spec.ts | 16 +++++++++++++--- packages/argon2/index.d.ts | 1 + packages/argon2/src/lib.rs | 15 +++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/argon2/__test__/argon2.spec.ts b/packages/argon2/__test__/argon2.spec.ts index fed6a5e7..8662fa5a 100644 --- a/packages/argon2/__test__/argon2.spec.ts +++ b/packages/argon2/__test__/argon2.spec.ts @@ -43,6 +43,16 @@ test('should be able to hash string', async (t) => { ) }) +test('should be able to hash string with a defined salt', async (t) => { + await t.notThrowsAsync(() => hash('whatever')) + await t.notThrowsAsync(() => + hash('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..149c7a49 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 { @@ -121,7 +122,12 @@ impl Task for HashTask { type JsValue = String; fn compute(&mut self) -> Result { - let salt = SaltString::generate(&mut OsRng); + let salt: SaltString; + if let Some(provided_salt) = &self.options.salt { + salt = SaltString::new(std::str::from_utf8(provided_salt.as_ref()).unwrap()).unwrap() + } else { + salt = SaltString::generate(&mut OsRng); + } let hasher = self.options.to_argon(); hasher .map_err(|err| Error::new(Status::InvalidArg, format!("{err}")))? @@ -181,7 +187,12 @@ impl Task for RawHashTask { type JsValue = Buffer; fn compute(&mut self) -> Result { - let salt = SaltString::generate(&mut OsRng); + let salt: SaltString; + if let Some(provided_salt) = &self.options.salt { + salt = SaltString::new(std::str::from_utf8(provided_salt.as_ref()).unwrap()).unwrap() + } else { + salt = SaltString::generate(&mut OsRng); + } let hasher = self .options .to_argon() From d1e04e6da5b8e80bb6efbe09520d3663afe65f33 Mon Sep 17 00:00:00 2001 From: Corentin Mors Date: Fri, 3 Mar 2023 23:01:58 +0100 Subject: [PATCH 2/3] Support salt input on Argon2 - try 2 Signed-off-by: Corentin Mors --- packages/argon2/__test__/argon2.spec.ts | 6 +++--- packages/argon2/src/lib.rs | 17 +++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/argon2/__test__/argon2.spec.ts b/packages/argon2/__test__/argon2.spec.ts index 8662fa5a..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,10 +43,10 @@ test('should be able to hash string', async (t) => { ) }) -test('should be able to hash string with a defined salt', async (t) => { +test('should be able to hashRaw string with a defined salt', async (t) => { await t.notThrowsAsync(() => hash('whatever')) await t.notThrowsAsync(() => - hash('whatever', { + hashRaw('whatever', { secret: randomBytes(32), salt: randomBytes(32), }), diff --git a/packages/argon2/src/lib.rs b/packages/argon2/src/lib.rs index 149c7a49..ded4f50c 100644 --- a/packages/argon2/src/lib.rs +++ b/packages/argon2/src/lib.rs @@ -122,12 +122,8 @@ impl Task for HashTask { type JsValue = String; fn compute(&mut self) -> Result { - let salt: SaltString; - if let Some(provided_salt) = &self.options.salt { - salt = SaltString::new(std::str::from_utf8(provided_salt.as_ref()).unwrap()).unwrap() - } else { - salt = SaltString::generate(&mut OsRng); - } + let salt = SaltString::generate(&mut OsRng); + let hasher = self.options.to_argon(); hasher .map_err(|err| Error::new(Status::InvalidArg, format!("{err}")))? @@ -187,11 +183,12 @@ impl Task for RawHashTask { type JsValue = Buffer; fn compute(&mut self) -> Result { - let salt: SaltString; + let generated_salt = SaltString::generate(&mut OsRng); + let salt: &[u8]; if let Some(provided_salt) = &self.options.salt { - salt = SaltString::new(std::str::from_utf8(provided_salt.as_ref()).unwrap()).unwrap() + salt = provided_salt.as_ref() } else { - salt = SaltString::generate(&mut OsRng); + salt = generated_salt.as_bytes(); } let hasher = self .options @@ -204,7 +201,7 @@ impl Task for RawHashTask { let mut output = vec![0; output_len]; hasher - .hash_password_into(self.password.as_slice(), salt.as_bytes(), &mut output) + .hash_password_into(self.password.as_slice(), salt, &mut output) .map_err(|err| Error::new(Status::GenericFailure, format!("{err}"))) .map(|_| output) } From a827bc192a541701598c2ce9529e3ea6ca84b3e7 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sun, 5 Mar 2023 14:34:29 +0800 Subject: [PATCH 3/3] Avoid generate salt if it's provided --- packages/argon2/src/lib.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/argon2/src/lib.rs b/packages/argon2/src/lib.rs index ded4f50c..9773e21b 100644 --- a/packages/argon2/src/lib.rs +++ b/packages/argon2/src/lib.rs @@ -183,13 +183,6 @@ impl Task for RawHashTask { type JsValue = Buffer; fn compute(&mut self) -> Result { - let generated_salt = SaltString::generate(&mut OsRng); - let salt: &[u8]; - if let Some(provided_salt) = &self.options.salt { - salt = provided_salt.as_ref() - } else { - salt = generated_salt.as_bytes(); - } let hasher = self .options .to_argon() @@ -200,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, &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 {