feat: add Argon2id key derivation with tests
This commit is contained in:
103
crates/idfoto-core/src/crypto.rs
Normal file
103
crates/idfoto-core/src/crypto.rs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
use argon2::{Algorithm, Argon2, Params, Version};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::error::{IdfotoError, Result};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct KdfParams {
|
||||||
|
pub argon2_m: u32,
|
||||||
|
pub argon2_t: u32,
|
||||||
|
pub argon2_p: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for KdfParams {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
argon2_m: 65536,
|
||||||
|
argon2_t: 3,
|
||||||
|
argon2_p: 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn derive_master_key(
|
||||||
|
passphrase: &[u8],
|
||||||
|
image_secret: &[u8; 32],
|
||||||
|
salt: &[u8; 32],
|
||||||
|
params: &KdfParams,
|
||||||
|
) -> Result<[u8; 32]> {
|
||||||
|
let argon2_params = Params::new(
|
||||||
|
params.argon2_m,
|
||||||
|
params.argon2_t,
|
||||||
|
params.argon2_p,
|
||||||
|
Some(32),
|
||||||
|
)
|
||||||
|
.map_err(|e| IdfotoError::Kdf(e.to_string()))?;
|
||||||
|
|
||||||
|
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, argon2_params);
|
||||||
|
|
||||||
|
// Concatenate passphrase + image_secret as the password input
|
||||||
|
let mut password = Vec::with_capacity(passphrase.len() + 32);
|
||||||
|
password.extend_from_slice(passphrase);
|
||||||
|
password.extend_from_slice(image_secret);
|
||||||
|
|
||||||
|
let mut output = [0u8; 32];
|
||||||
|
argon2
|
||||||
|
.hash_password_into(&password, salt, &mut output)
|
||||||
|
.map_err(|e| IdfotoError::Kdf(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn fast_params() -> KdfParams {
|
||||||
|
KdfParams {
|
||||||
|
argon2_m: 256,
|
||||||
|
argon2_t: 1,
|
||||||
|
argon2_p: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn derive_master_key_deterministic() {
|
||||||
|
let passphrase = b"test-passphrase";
|
||||||
|
let image_secret = [0x42u8; 32];
|
||||||
|
let salt = [0x01u8; 32];
|
||||||
|
let params = fast_params();
|
||||||
|
|
||||||
|
let key1 = derive_master_key(passphrase, &image_secret, &salt, ¶ms).unwrap();
|
||||||
|
let key2 = derive_master_key(passphrase, &image_secret, &salt, ¶ms).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(key1, key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn derive_master_key_different_passphrase() {
|
||||||
|
let image_secret = [0x42u8; 32];
|
||||||
|
let salt = [0x01u8; 32];
|
||||||
|
let params = fast_params();
|
||||||
|
|
||||||
|
let key1 = derive_master_key(b"passphrase-one", &image_secret, &salt, ¶ms).unwrap();
|
||||||
|
let key2 = derive_master_key(b"passphrase-two", &image_secret, &salt, ¶ms).unwrap();
|
||||||
|
|
||||||
|
assert_ne!(key1, key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn derive_master_key_different_image_secret() {
|
||||||
|
let passphrase = b"test-passphrase";
|
||||||
|
let salt = [0x01u8; 32];
|
||||||
|
let params = fast_params();
|
||||||
|
|
||||||
|
let image_secret1 = [0x11u8; 32];
|
||||||
|
let image_secret2 = [0x22u8; 32];
|
||||||
|
|
||||||
|
let key1 = derive_master_key(passphrase, &image_secret1, &salt, ¶ms).unwrap();
|
||||||
|
let key2 = derive_master_key(passphrase, &image_secret2, &salt, ¶ms).unwrap();
|
||||||
|
|
||||||
|
assert_ne!(key1, key2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
pub use error::{IdfotoError, Result};
|
pub use error::{IdfotoError, Result};
|
||||||
|
|
||||||
|
pub mod crypto;
|
||||||
|
pub use crypto::{derive_master_key, KdfParams};
|
||||||
|
|||||||
Reference in New Issue
Block a user