use md5::digest::{Digest, FixedOutput, Reset, Update};
use std::marker::PhantomData;
const ONE_MEGABYTE: usize = 1_048_576;
const PASSWD_BUF_LEN: usize = 64;
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct LocalizedKey<'a, D> {
bytes: Vec<u8>,
_digest_type: PhantomData<&'a D>,
}
impl<'a, D> LocalizedKey<'a, D> {
pub(crate) fn bytes(&self) -> &[u8] {
&self.bytes
}
}
impl<'a, D> LocalizedKey<'a, D>
where
D: Update + FixedOutput + Reset + Default + Clone,
{
pub fn new(passwd: &[u8], engine_id: &[u8]) -> Self {
let bytes = Self::key_from_passwd(passwd, engine_id);
Self {
bytes,
_digest_type: PhantomData,
}
}
fn key_from_passwd(passwd: &[u8], engine_id: &[u8]) -> Vec<u8> {
assert!(
!passwd.is_empty(),
"password for localized key cannot be empty"
);
let mut passwd_buf = vec![0; PASSWD_BUF_LEN];
let mut passwd_index = 0;
let passwd_len = passwd.len();
let mut hashing_fn = D::default();
for _ in (0..ONE_MEGABYTE).step_by(PASSWD_BUF_LEN) {
for byte in passwd_buf.iter_mut() {
*byte = passwd[passwd_index % passwd_len];
passwd_index += 1;
}
hashing_fn.update(&passwd_buf);
}
let key = hashing_fn.finalize_reset();
passwd_buf.clear();
passwd_buf.extend_from_slice(&key);
passwd_buf.extend_from_slice(engine_id);
passwd_buf.extend_from_slice(&key);
hashing_fn.update(&passwd_buf);
hashing_fn.finalize().to_vec()
}
}
pub trait WithLocalizedKey<'a, D> {
fn with_localized_key(localized_key: LocalizedKey<'a, D>) -> Self;
}
#[cfg(test)]
mod tests {
use super::*;
use md5::Md5;
use sha1::Sha1;
#[test]
fn it_constructs_localized_key_with_md5() {
let engine_id = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x02];
let result = LocalizedKey::<Md5>::new(b"maplesyrup", &engine_id);
let expected = [
0x52, 0x6f, 0x5e, 0xed, 0x9f, 0xcc, 0xe2, 0x6f, 0x89, 0x64, 0xc2, 0x93, 0x07, 0x87,
0xd8, 0x2b,
];
assert_eq!(result.bytes, expected);
}
#[test]
fn it_constructs_localized_key_with_sha1() {
let engine_id = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x02];
let result = LocalizedKey::<Sha1>::new(b"maplesyrup", &engine_id);
let expected = [
0x66, 0x95, 0xfe, 0xbc, 0x92, 0x88, 0xe3, 0x62, 0x82, 0x23, 0x5f, 0xc7, 0x15, 0x1f,
0x12, 0x84, 0x97, 0xb3, 0x8f, 0x3f,
];
assert_eq!(result.bytes, expected);
}
#[test]
#[should_panic]
fn it_panics_with_empty_passwd() {
let engine_id = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x02];
LocalizedKey::<Sha1>::new(b"", &engine_id);
}
}