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 (6568c1a83a/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.
This commit is contained in:
ironhaven 2022-03-07 23:10:31 -06:00 committed by GitHub
parent d1d6fc0411
commit c30d02b512
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 3 additions and 3 deletions

View File

@ -140,7 +140,7 @@ impl<T: AsRef<[u8]>> TOTP<T> {
/// 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]