1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
use std::error::Error; use crypto::digest::Digest; use crypto::sha1::Sha1; use reqwest::blocking; const API_URL: &str = "https://api.pwnedpasswords.com/range/"; #[derive(Debug)] pub struct Passwd { pub text: String, pub times_pwned: i32, } pub fn check_for_pwnage(pass: &str) -> Result<Passwd, Box<dyn Error>> { if pass.len() <= 0 { return Err("Password can't be length 0")?; } let hash = get_hash(pass); let res = blocking::get(format!("{}{}", API_URL, &hash[..5]))?.text()?; for line in res.lines() { let values = line.split(':').collect::<Vec<&str>>(); let (hash_suffix, num) = (values[0], values[1]); if format!("{}{}", &hash[..5], hash_suffix).eq(&hash) { return Ok(Passwd { text: pass.to_string(), times_pwned: num.parse()?, }); } } Ok(Passwd { text: pass.to_string(), times_pwned: 0, }) } fn get_hash(pass: &str) -> String { let mut hasher = Sha1::new(); hasher.input_str(pass); hasher.result_str().to_ascii_uppercase() } #[cfg(test)] mod tests { #[test] #[should_panic] fn test_zero_len() { super::check_for_pwnage("").unwrap(); } #[test] fn check_hello_world() { assert_eq!( super::check_for_pwnage("helloworld").unwrap().times_pwned, 16418 ); } }