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: