Skip to main content

simple_steam_totp/
lib.rs

1use std::time::{SystemTime};
2
3use crypto::hmac::Hmac;
4use crypto::mac::Mac;
5use crypto::sha1::Sha1;
6
7const INTERVAL : u64 = 30;
8const ALPHABET : &str = "23456789BCDFGHJKMNPQRTVWXY";
9
10/// Generate a Steam Authenticator code for the given shared secret (expected as a base64 encoded string).
11/// 
12/// You can find the shared secret within your Steam Desktop Authenticator maFile.
13/// 
14/// # Example
15/// ```
16/// use simple_steam_totp::{generate};
17/// 
18/// println!("{}", generate("V59i4SUqNiuYDrssYyMz62RSI9k=").unwrap());
19/// ```
20pub fn generate(shared_secret: &str) -> Result<String, &'static str> {
21    let counter : u64 = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() / INTERVAL;
22    let decoded_shared_secret = match base64::decode(shared_secret) {
23        Ok(decoded) => decoded,
24        Err(_) => return Err("Error decoding base64 shared secret"),
25    };
26
27    let mut hmac = Hmac::new(Sha1::new(), decoded_shared_secret.as_slice());
28    hmac.input(counter.to_be_bytes().as_slice());
29    let code = hmac.result().code().to_vec();
30    let start = (code[19] & 0xf) as usize;
31    let mut fullcode = (((code[start] & 0x7f) as u32) << 24) |
32                  ((code[start + 1] as u32) << 16) |
33                  ((code[start + 2] as u32) << 8) |
34                  (code[start + 3] as u32);
35
36    let mut otp : [u8; 5] = [0; 5];
37    for i in 0..5 {
38        otp[i] = ALPHABET.as_bytes()[fullcode as usize % ALPHABET.len()];
39        fullcode /= ALPHABET.len() as u32;
40    }
41
42    Ok(String::from_utf8(otp.to_vec()).unwrap())
43}