Created
September 28, 2023 00:38
-
-
Save cynecx/f87f089fb459a2a78dc34223deeed591 to your computer and use it in GitHub Desktop.
TOTP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use std::{ | |
| process::exit, | |
| time::{SystemTime, UNIX_EPOCH}, | |
| }; | |
| use hmac::{Hmac, Mac}; | |
| use sha1::Sha1; | |
| type HmacSha1 = Hmac<Sha1>; | |
| fn hotp(key: &[u8], counter: u64) -> u32 { | |
| let counter = counter.to_be_bytes(); | |
| let mut hmac = HmacSha1::new_from_slice(key).unwrap(); | |
| hmac.update(&counter); | |
| let mac: [u8; 20] = hmac.finalize().into_bytes().into(); | |
| let offset = (mac[19] & 0xF) as usize; | |
| let snum = u32::from_be_bytes(mac[offset..offset + 4].try_into().unwrap()) & 0x7fffffff; | |
| snum % 1000000 | |
| } | |
| fn totp(key: &[u8]) -> u32 { | |
| let counter = SystemTime::now() | |
| .duration_since(UNIX_EPOCH) | |
| .unwrap() | |
| .as_secs() | |
| / 30; | |
| hotp(key, counter) | |
| } | |
| fn decode_b32(val: &str) -> Option<Vec<u8>> { | |
| let mut iter = val.bytes().map(|val| match val { | |
| b'A'..=b'Z' => Some(val - b'A'), | |
| b'2'..=b'7' => Some(26 + val - b'2'), | |
| _ => None, | |
| }); | |
| let mut decoded = Vec::with_capacity(val.len() * 5 / 8); | |
| let mut bits = 5; | |
| let mut state = iter.next()??; | |
| for val in iter { | |
| let val = val?; | |
| if bits >= 3 { | |
| let need = 8 - bits; | |
| decoded.push(state << need | (val >> (5 - need))); | |
| bits = 5 - need; | |
| state = val & ((1 << bits) - 1); | |
| } else { | |
| bits += 5; | |
| state = (state << 5) | val; | |
| } | |
| } | |
| if bits == 8 { | |
| decoded.push(state); | |
| } | |
| if decoded.is_empty() { | |
| None | |
| } else { | |
| Some(decoded) | |
| } | |
| } | |
| fn main() { | |
| let Some(encoded_key) = std::env::args().nth(1) else { | |
| eprintln!("missing totp key"); | |
| exit(1); | |
| }; | |
| let Some(decoded_key) = decode_b32(&encoded_key) else { | |
| eprintln!("invalid base32 encoded key"); | |
| exit(1); | |
| }; | |
| println!("{}", totp(&decoded_key)); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment