From c30d02b51225ac9c23d2b7cf496fb445e982b182 Mon Sep 17 00:00:00 2001 From: ironhaven Date: Mon, 7 Mar 2022 23:10:31 -0600 Subject: [PATCH] Use last byte of HMAC output for truncating This is to use the least significant byte of the HMAC regardless of size. RFC 6328 (TOTP) Section 1.2 says you can use SHA-1 SHA-256 or SHA-512 with the same algorithm of RFC 4226 (HTOP). This seems ok until you realize that all the new HMACs have different output sizes and HTOP only expects a 20 byte fixed MAC. It is not completely clear if RFC 4226 Section 5.3 means "get the bottom 4 bits from byte at offset 19" or "get the 4 least significant bits". Other implementations (https://github.com/pyauth/pyotp/blob/6568c1a83af8e0229f3c4b28d03552d601e2b7fe/src/pyotp/otp.py#L28) and Wikipedia read the "Dynamic Truncation" algorithm to be the last 4 bits of the MAC, so I think this implementation should follow the others. --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1ca07dc..8bb2c94 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,7 +140,7 @@ impl> TOTP { /// Will generate a token according to the provided timestamp in seconds pub fn generate(&self, time: u64) -> String { let result: &[u8] = &self.sign(time); - let offset = (result[19] & 15) as usize; + let offset = (result.last().unwrap() & 15) as usize; let result = u32::from_be_bytes(result[offset..offset + 4].try_into().unwrap()) & 0x7fff_ffff; format!( "{1:00$}", @@ -244,13 +244,13 @@ mod tests { #[test] fn generates_token_sha256() { let totp = TOTP::new(Algorithm::SHA256, 6, 1, 1, "TestSecret"); - assert_eq!(totp.generate(1000).as_str(), "423657"); + assert_eq!(totp.generate(1000).as_str(), "480200"); } #[test] fn generates_token_sha512() { let totp = TOTP::new(Algorithm::SHA512, 6, 1, 1, "TestSecret"); - assert_eq!(totp.generate(1000).as_str(), "416767"); + assert_eq!(totp.generate(1000).as_str(), "850500"); } #[test]