ThinkAlexandria / BoringAuth

Straightforward password, passphrase, TOTP, and HOTP user authentication
Other
57 stars 9 forks source link

TOTP / HOTP does not match other implementations? #6

Open djc opened 3 years ago

djc commented 3 years ago

In testing this crate's TOTP implementation against Yubico Authenticator, I found that the (default, so 6-digit SHA-1) TOTP codes generated by Yubico Authenticator were not accepted by TOTP::is_valid(). I tested another implementation that I wrote many years ago in Python, whose codes did match Yubico Authenticator. I then came up with a Rust implementation that matches Yubico Authenticator and my older Python code:

fn totp_valid(secret: &[u8], code: &str, tolerance: u64) -> bool {
    assert!(tolerance < 7);
    let testing = u32::from_str(code).unwrap();
    let anchor = SystemTime::now()
        .duration_since(time::UNIX_EPOCH)
        .unwrap()
        .as_secs()
        / 30;
    let key = hmac::Key::new(hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY, secret);
    let mut tested = [false; 16];

    for i in (7u64 - tolerance)..(8 + tolerance) {
        let current = (anchor + i) - 8;
        let tag = hmac::sign(&key, &current.to_be_bytes()[..]);
        let offset = (tag.as_ref()[19] & 15) as usize;
        let p = u32::from_be_bytes(tag.as_ref()[offset..offset + 4].try_into().unwrap());
        tested[i as usize] = dbg!((p & 0x7fff_ffff) % 1_000_000) == testing;
    }

    tested.iter().fold(false, |acc, p| acc | p)
}
peterjoel commented 3 years ago

I also couldn't make it work with Google Authenticator. Are there some specific settings that are needed for compatibility?