Skip to content

Commit

Permalink
Use argon2id by default
Browse files Browse the repository at this point in the history
  • Loading branch information
paberr authored and jsdanielh committed May 1, 2024
1 parent ff495df commit 6b75bc9
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 10 deletions.
5 changes: 3 additions & 2 deletions hash/src/argon2kdf.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use argon2::Config;
pub use argon2::Error as Argon2Error;
pub use argon2::{Error as Argon2Error, Variant as Argon2Variant};

// Taken from https://github.com/nimiq/core-js/blob/c98d56b2dd967d9a9c9a97fe4c54bfaac743aa0c/src/main/generic/utils/crypto/CryptoWorkerImpl.js#L146
const MEMORY_COST: u32 = 512;
Expand All @@ -9,12 +9,13 @@ pub fn compute_argon2_kdf(
salt: &[u8],
iterations: u32,
derived_key_length: usize,
variant: Argon2Variant,
) -> Result<Vec<u8>, Argon2Error> {
let config = Config {
time_cost: iterations,
hash_length: derived_key_length as u32,
mem_cost: MEMORY_COST,
variant: argon2::Variant::Argon2d,
variant,
..Default::default()
};

Expand Down
8 changes: 7 additions & 1 deletion hash/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,13 @@ fn it_can_compute_argon2_kdf() {
let password = "test";
let salt = "nimiqrocks!";

let res = argon2kdf::compute_argon2_kdf(password.as_bytes(), salt.as_bytes(), 1, 32);
let res = argon2kdf::compute_argon2_kdf(
password.as_bytes(),
salt.as_bytes(),
1,
32,
argon2::Variant::Argon2d,
);
assert_eq!(
res.unwrap(),
hex::decode("8c259fdcc2ad6799df728c11e895a3369e9dbae6a3166ebc3b353399fc565524").unwrap()
Expand Down
67 changes: 60 additions & 7 deletions utils/src/otp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{

use clear_on_drop::clear::Clear;
use nimiq_database_value::{FromDatabaseValue, IntoDatabaseValue};
use nimiq_hash::argon2kdf::{compute_argon2_kdf, Argon2Error};
use nimiq_hash::argon2kdf::{compute_argon2_kdf, Argon2Error, Argon2Variant};
use nimiq_serde::{Deserialize, Serialize};
use rand::{rngs::OsRng, RngCore};

Expand Down Expand Up @@ -81,8 +81,9 @@ impl<T: Clear + Deserialize + Serialize> Unlocked<T> {
password: &[u8],
iterations: u32,
salt_length: usize,
algorithm: Algorithm,
) -> Result<Self, Argon2Error> {
let locked = Locked::create(&secret, password, iterations, salt_length)?;
let locked = Locked::create(&secret, password, iterations, salt_length, algorithm)?;
Ok(Unlocked {
data: ClearOnDrop::new(secret),
lock: locked,
Expand All @@ -96,6 +97,7 @@ impl<T: Clear + Deserialize + Serialize> Unlocked<T> {
password,
OtpLock::<T>::DEFAULT_ITERATIONS,
OtpLock::<T>::DEFAULT_SALT_LENGTH,
Algorithm::default(),
)
}

Expand Down Expand Up @@ -130,12 +132,43 @@ impl<T: Clear + Deserialize + Serialize> Deref for Unlocked<T> {
}
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Algorithm {
Argon2d = 0,

/// With side-channel protection.
Argon2id = 2,
}

impl Algorithm {
pub fn backwards_compatible_default() -> Algorithm {
Self::Argon2d
}
}

impl From<Algorithm> for Argon2Variant {
fn from(value: Algorithm) -> Self {
match value {
Algorithm::Argon2d => Argon2Variant::Argon2d,
Algorithm::Argon2id => Argon2Variant::Argon2id,
}
}
}

impl Default for Algorithm {
fn default() -> Self {
Self::Argon2id
}
}

// Locked container
#[derive(Serialize, Deserialize)]
pub struct Locked<T: Clear + Deserialize + Serialize> {
lock: Vec<u8>,
salt: Vec<u8>,
iterations: u32,
#[serde(default = "Algorithm::backwards_compatible_default")]
algorithm: Algorithm,
phantom: PhantomData<T>,
}

Expand All @@ -146,8 +179,9 @@ impl<T: Clear + Deserialize + Serialize> Locked<T> {
password: &[u8],
iterations: u32,
salt_length: usize,
algorithm: Algorithm,
) -> Result<Self, Argon2Error> {
let result = Locked::create(&secret, password, iterations, salt_length)?;
let result = Locked::create(&secret, password, iterations, salt_length, algorithm)?;

// Remove secret from memory.
secret.clear();
Expand All @@ -162,13 +196,21 @@ impl<T: Clear + Deserialize + Serialize> Locked<T> {
password,
OtpLock::<T>::DEFAULT_ITERATIONS,
OtpLock::<T>::DEFAULT_SALT_LENGTH,
Algorithm::default(),
)
}

/// Calling code should make sure to clear the password from memory after use.
/// The integrity of the output value is not checked.
pub fn unlock_unchecked(self, password: &[u8]) -> Result<Unlocked<T>, Locked<T>> {
let key_opt = Self::otp(&self.lock, password, self.iterations, &self.salt).ok();
let key_opt = Self::otp(
&self.lock,
password,
self.iterations,
&self.salt,
self.algorithm,
)
.ok();
let mut key = if let Some(key_content) = key_opt {
key_content
} else {
Expand Down Expand Up @@ -197,8 +239,10 @@ impl<T: Clear + Deserialize + Serialize> Locked<T> {
password: &[u8],
iterations: u32,
salt: &[u8],
algorithm: Algorithm,
) -> Result<Vec<u8>, Argon2Error> {
let mut key = compute_argon2_kdf(password, salt, iterations, secret.len())?;
let mut key =
compute_argon2_kdf(password, salt, iterations, secret.len(), algorithm.into())?;
assert_eq!(key.len(), secret.len());

for (key_byte, secret_byte) in key.iter_mut().zip(secret.iter()) {
Expand All @@ -213,9 +257,10 @@ impl<T: Clear + Deserialize + Serialize> Locked<T> {
password: &[u8],
iterations: u32,
salt: Vec<u8>,
algorithm: Algorithm,
) -> Result<Self, Argon2Error> {
let mut data = secret.serialize_to_vec();
let lock = Self::otp(&data, password, iterations, &salt)?;
let lock = Self::otp(&data, password, iterations, &salt, algorithm)?;

// Always overwrite unencrypted vector.
for byte in data.iter_mut() {
Expand All @@ -226,6 +271,7 @@ impl<T: Clear + Deserialize + Serialize> Locked<T> {
lock,
salt,
iterations,
algorithm,
phantom: PhantomData,
})
}
Expand All @@ -235,10 +281,11 @@ impl<T: Clear + Deserialize + Serialize> Locked<T> {
password: &[u8],
iterations: u32,
salt_length: usize,
algorithm: Algorithm,
) -> Result<Self, Argon2Error> {
let mut salt = vec![0; salt_length];
OsRng.fill_bytes(salt.as_mut_slice());
Self::lock(secret, password, iterations, salt)
Self::lock(secret, password, iterations, salt, algorithm)
}

pub fn into_otp_lock(self) -> OtpLock<T> {
Expand Down Expand Up @@ -301,12 +348,14 @@ impl<T: Clear + Deserialize + Serialize> OtpLock<T> {
password: &[u8],
iterations: u32,
salt_length: usize,
algorithm: Algorithm,
) -> Result<Self, Argon2Error> {
Ok(OtpLock::Unlocked(Unlocked::new(
secret,
password,
iterations,
salt_length,
algorithm,
)?))
}

Expand All @@ -317,6 +366,7 @@ impl<T: Clear + Deserialize + Serialize> OtpLock<T> {
password,
Self::DEFAULT_ITERATIONS,
Self::DEFAULT_SALT_LENGTH,
Algorithm::default(),
)
}

Expand All @@ -326,12 +376,14 @@ impl<T: Clear + Deserialize + Serialize> OtpLock<T> {
password: &[u8],
iterations: u32,
salt_length: usize,
algorithm: Algorithm,
) -> Result<Self, Argon2Error> {
Ok(OtpLock::Locked(Locked::new(
secret,
password,
iterations,
salt_length,
algorithm,
)?))
}

Expand All @@ -342,6 +394,7 @@ impl<T: Clear + Deserialize + Serialize> OtpLock<T> {
password,
Self::DEFAULT_ITERATIONS,
Self::DEFAULT_SALT_LENGTH,
Algorithm::default(),
)
}

Expand Down

0 comments on commit 6b75bc9

Please sign in to comment.