use sha2::{Digest, Sha384};
pub struct SasWords<'a> {
pub w1: &'a str,
pub w2: &'a str,
}
pub struct Dictionary<'a> { words: &'a [&'a str] }
impl<'a> Dictionary<'a> {
pub fn builtin() -> Self {
let mut v: Vec<String> = Vec::with_capacity(1024);
for i in 0..1024 { v.push(format!("w{:04}", i)); }
let leaked: Vec<&'static str> = v.into_iter().map(|s| Box::leak(s.into_boxed_str()) as &str).collect();
let leaked_ref: &'static [&'static str] = Box::leak(leaked.into_boxed_slice());
Dictionary { words: leaked_ref }
}
pub fn from_slice(words: &'a [&'a str]) -> Self { Self { words } }
pub fn get(&self, idx: usize) -> &'a str { self.words[idx % self.words.len()] }
}
pub fn two_word_sas<'a>(dict: &'a Dictionary<'a>, k_verify: &[u8]) -> SasWords<'a> {
let mut h = Sha384::new();
h.update(k_verify);
let out = h.finalize();
let b0 = out[0] as u32;
let b1 = out[1] as u32;
let b2 = out[2] as u32;
let idx1 = ((b0 << 2) | (b1 >> 6)) & 0x3ff; let idx2 = (b1 & 0x3f) << 4 | (b2 >> 4); let w1 = dict.get(idx1 as usize);
let w2 = dict.get(idx2 as usize);
SasWords { w1, w2 }
}
pub fn load_dictionary_file(path: &std::path::Path) -> Option<Dictionary<'static>> {
use std::fs;
let data = fs::read_to_string(path).ok()?;
let mut words: Vec<&str> = data.lines().map(|l| l.trim()).filter(|l| !l.is_empty() && !l.starts_with('#')).collect();
if words.len() < 1024 { return None; }
words.truncate(1024);
let leaked: Vec<&'static str> = words.into_iter().map(|s| Box::leak(s.to_string().into_boxed_str()) as &str).collect();
let leaked_slice: &'static [&'static str] = Box::leak(leaked.into_boxed_slice());
Some(Dictionary::from_slice(leaked_slice))
}