separate KDF (not a proper KDF for now)

This commit is contained in:
2026-09-24 10:59:02 +02:00
parent cbce59293d
commit 03aee796d8
4 changed files with 74 additions and 18 deletions
+26
View File
@@ -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
}
+6
View File
@@ -0,0 +1,6 @@
mod kdf;
#[cfg(test)]
mod test;
pub use kdf::derive_file_key;
+26
View File
@@ -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);
}
+16 -18
View File
@@ -4,12 +4,14 @@ use std::path::PathBuf;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
mod file_crypt; mod file_crypt;
mod kdf;
mod keccak; mod keccak;
mod keccak_aead; 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)] #[derive(Parser, Debug)]
#[command( #[command(
name = "filecrypt-rs", 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<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let cli = Cli::parse(); let cli = Cli::parse();
@@ -75,11 +65,15 @@ fn main() -> Result<(), Box<dyn Error>> {
output, output,
password, password,
} => { } => {
let key = derive_key(&password); let key = derive_file_key(&password);
encrypt_file(&input, &output, &key)?; encrypt_file(&input, &output, &key)?;
println!("Encrypted '{}' -> '{}'", input.display(), output.display()); println!(
"Encrypted '{}' -> '{}'",
input.display(),
output.display()
);
} }
Command::Decrypt { Command::Decrypt {
@@ -87,11 +81,15 @@ fn main() -> Result<(), Box<dyn Error>> {
output, output,
password, password,
} => { } => {
let key = derive_key(&password); let key = derive_file_key(&password);
decrypt_file(&input, &output, &key)?; decrypt_file(&input, &output, &key)?;
println!("Decrypted '{}' -> '{}'", input.display(), output.display()); println!(
"Decrypted '{}' -> '{}'",
input.display(),
output.display()
);
} }
} }