use hmac::{Hmac, Mac};
use sha1::Sha1;
type hmacsha1 = Hmac<Sha1>;
fn main() {
let key = "JBSWY3DPEHPK3PXP";
let step: u32 = 30;
let skew: u32 = 1;
let mut hmac = hmacsha1::new_from_slice(key.as_bytes()).unwrap();
let time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let time = time as u32 / step;
hmac.update(&time.to_be_bytes());
let mut result = hmac.finalize().into_bytes().to_vec();
let offset = result.last().unwrap() & 15;
let binary = (result[offset as usize] as u32 & 0x7f) << 24
| (result[(offset + 1) as usize] as u32 & 0xff) << 16
| (result[(offset + 2) as usize] as u32 & 0xff) << 8
| (result[(offset + 3) as usize] as u32 & 0xff);
let mut fin = binary % 10_u32.pow(6);
let fin_str = fin.to_string();
println!("{:?}", result.last());
println!("{:?}", fin_str);
}
When I run this code for the secret, I do not get the expected output. I have followed the totp ref sheet and worked to the implementation they give in Java, but it doesn't work. Can someone see what I'm not seeing?
I would expect the proper result
Generally, the key for HOTP or TOTP is given as a base32 string, or sometimes, a hex or base64 string, although these are less common. However, you're using the string literally as the key instead of decoding it.
You probably want to use the base32 crate to decode this into a byte value.
In addition, I might suggest writing your code to be a bit more generic over the key and timestamp, which would allow you to write some tests here. It's clear that it doesn't produce “the proper result,” but it's a lot easier to know whether your code does in fact do that if you can write some tests for known values. This is especially true since it involves time, which is a moving target.