snippets/TOTP-algorithm/Rust-TOTP-algorithm.md

1.4 KiB

Implementation of the TOTP algorithm in Rust

use base32::decode;
use chrono::Utc;
use sha1::{digest::Update, Digest, Sha1};

const INTERVAL: u64 = 30;
const TOTP_KEY: &str = "BOMY6BV5BWVJEWJ4ZLDYKN3LSIS736B3";

fn generate() {
    // Pre-process data
    let epoch = Utc::now().timestamp() as u64;
    let msg = epoch / INTERVAL;
    let bytes_msg = msg.to_be_bytes();

    let key_decoded = decode(base32::Alphabet::RFC4648 { padding: false }, TOTP_KEY).unwrap();

    // HMAC-SHA1
    let mut key_pad = key_decoded.clone();
    key_pad.resize(64, 0);

    let i_key_pad: Vec<u8> = key_pad.iter().map(|&x| x ^ 0x36).collect();
    let o_key_pad: Vec<u8> = key_pad.iter().map(|&x| x ^ 0x5C).collect();

    let mut i_key_pad_msg = Vec::new();
    i_key_pad_msg.extend_from_slice(&i_key_pad);
    i_key_pad_msg.extend_from_slice(&bytes_msg);

    let i_key_pad_msg_hashed = Sha1::new().chain(&i_key_pad_msg).finalize();

    let mut o_key_pad_msg = Vec::new();
    o_key_pad_msg.extend_from_slice(&o_key_pad);
    o_key_pad_msg.extend_from_slice(&i_key_pad_msg_hashed);

    let hmac_result = Sha1::new().chain(&o_key_pad_msg).finalize();

    // TOTP Algorithm
    let start = (hmac_result[19] & 0xF) as usize;
    let raw_bytes: [u8; 4] = hmac_result[start..start + 4].try_into().unwrap();
    let raw_token = u32::from_be_bytes(raw_bytes);
    let raw_token = raw_token & 0x7F_FF_FF_FF;
    let token = raw_token % 1_000_000;

    println!("{:06}", token);
}