From 03aee796d896ad2532eb19ed4b4c4a8e87aaaa1d Mon Sep 17 00:00:00 2001 From: Sam Hadow Date: Thu, 24 Sep 2026 10:59:02 +0200 Subject: [PATCH] separate KDF (not a proper KDF for now) --- src/kdf/kdf.rs | 26 ++++++++++++++++++++++++++ src/kdf/mod.rs | 6 ++++++ src/kdf/test.rs | 26 ++++++++++++++++++++++++++ src/main.rs | 34 ++++++++++++++++------------------ 4 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 src/kdf/kdf.rs create mode 100644 src/kdf/mod.rs create mode 100644 src/kdf/test.rs diff --git a/src/kdf/kdf.rs b/src/kdf/kdf.rs new file mode 100644 index 0000000..b4ed638 --- /dev/null +++ b/src/kdf/kdf.rs @@ -0,0 +1,26 @@ +//! Password-to-key derivation. +//! +//! This currently uses SHA3-256 directly and truncates the result to 24 bytes +//! because `KeccakAead` accepts keys up to 24 bytes. +//! +//! This is not a proper password-based KDF. There is currently no salt, +//! work factor, memory hardness, or iteration count. + +use crate::keccak::sha3_256; + +/// Length of the key produced for `KeccakAead`. +pub const DERIVED_KEY_LEN: usize = 24; + +/// Derive a 24-byte encryption key from a password. +/// +/// The password is encoded as UTF-8 and hashed using the project's +/// SHA3-256 implementation. The first 24 bytes of the digest are used +/// as the AEAD key. +pub fn derive_file_key(password: &str) -> [u8; DERIVED_KEY_LEN] { + let digest = sha3_256(password.as_bytes()); + + let mut key = [0u8; DERIVED_KEY_LEN]; + key.copy_from_slice(&digest[..DERIVED_KEY_LEN]); + + key +} diff --git a/src/kdf/mod.rs b/src/kdf/mod.rs new file mode 100644 index 0000000..12767e4 --- /dev/null +++ b/src/kdf/mod.rs @@ -0,0 +1,6 @@ +mod kdf; + +#[cfg(test)] +mod test; + +pub use kdf::derive_file_key; diff --git a/src/kdf/test.rs b/src/kdf/test.rs new file mode 100644 index 0000000..1e55ce5 --- /dev/null +++ b/src/kdf/test.rs @@ -0,0 +1,26 @@ +use super::*; +use crate::kdf::kdf::DERIVED_KEY_LEN; + +#[test] +fn key_has_expected_length() { + assert_eq!(DERIVED_KEY_LEN, 24); + + let key = derive_file_key("test password"); + assert_eq!(key.len(), DERIVED_KEY_LEN); +} + +#[test] +fn same_password_same_key() { + let a = derive_file_key("correct horse battery staple"); + let b = derive_file_key("correct horse battery staple"); + + assert_eq!(a, b); +} + +#[test] +fn different_passwords_produce_different_keys() { + let a = derive_file_key("password1"); + let b = derive_file_key("password2"); + + assert_ne!(a, b); +} diff --git a/src/main.rs b/src/main.rs index 8c99790..f4eea31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,12 +4,14 @@ use std::path::PathBuf; use clap::{Parser, Subcommand}; mod file_crypt; +mod kdf; mod keccak; mod keccak_aead; -use file_crypt::{MAX_KEY_LEN, decrypt_file, encrypt_file}; +use file_crypt::{decrypt_file, encrypt_file}; +use kdf::derive_file_key; -/// File encryption utility using KeccakAead. +/// Encrypt and decrypt files using KeccakAead. #[derive(Parser, Debug)] #[command( name = "filecrypt-rs", @@ -54,18 +56,6 @@ enum Command { }, } -/// Derive the fixed-length AEAD key from a password. -/// -/// SHA-3-256 produces 32 bytes, while `KeccakAead` currently accepts keys from 16 through 24 bytes. Therefore we use the first 24 bytes. -fn derive_key(password: &str) -> [u8; MAX_KEY_LEN] { - let digest = keccak::sha3_256(password.as_bytes()); - - let mut key = [0u8; MAX_KEY_LEN]; - key.copy_from_slice(&digest[..MAX_KEY_LEN]); - - key -} - fn main() -> Result<(), Box> { let cli = Cli::parse(); @@ -75,11 +65,15 @@ fn main() -> Result<(), Box> { output, password, } => { - let key = derive_key(&password); + let key = derive_file_key(&password); encrypt_file(&input, &output, &key)?; - println!("Encrypted '{}' -> '{}'", input.display(), output.display()); + println!( + "Encrypted '{}' -> '{}'", + input.display(), + output.display() + ); } Command::Decrypt { @@ -87,11 +81,15 @@ fn main() -> Result<(), Box> { output, password, } => { - let key = derive_key(&password); + let key = derive_file_key(&password); decrypt_file(&input, &output, &key)?; - println!("Decrypted '{}' -> '{}'", input.display(), output.display()); + println!( + "Decrypted '{}' -> '{}'", + input.display(), + output.display() + ); } }