Following Wikipedia: Time-based one-time password and Wikipedia: HMAC-based one-time password, is this Rust implementation of the TOTP/HOTP algorithm correct?
As far as I can see, this matches the Implementation from the Node OTPAuth library, however when testing using an Online Tool I am getting a different OTP. Leading me to wonder if there is an issue with my implementation, or if this is just a difference in the underlying SHA1 implementaiton between Rust and Node.
use std::time::{SystemTime, UNIX_EPOCH}; use hmac::{Hmac, Mac}; use sha1::Sha1; type HmacSha1 = Hmac<Sha1>; pub fn gen_hotp(secret_key: &str) -> u32 { let mut mac = HmacSha1::new_from_slice(secret_key.as_bytes()).unwrap(); mac.update(&get_counter().to_be_bytes()); let to_truncate = mac.finalize().into_bytes(); truncate(to_truncate.as_slice()) } /// Calculate `C` using seconds since UNIX Epoch, with a period of `30s` fn get_counter() -> u64 { let secs = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("We should not be behind the Epoch.") .as_secs() as f64; f64::floor(secs / 30.0) as u64 } /// Truncate the HMAC fn truncate(mac: &[u8]) -> u32 { let byte_offset: usize = (mac[19] & 0x0F) as usize; let result = (((mac[byte_offset + 0]) as u32) << 24) | (((mac[byte_offset + 1]) as u32) << 16) | (((mac[byte_offset + 2]) as u32) << 8) | (((mac[byte_offset + 3]) as u32) << 0); (result & 0x7FFF_FFFF) % 1_000_000 }
Crates used: