Skip to content

std/totp ​

HOTP (RFC 4226) and TOTP (RFC 6238) one-time passwords, pure Milo over std/hmac's HMAC-SHA1. This is the algorithm behind Google Authenticator / Authy 2FA codes; output matches oathtool and the RFC test vectors bit-for-bit.

The secret is raw key bytes — decode a base32 otpauth:// secret with std/base32 first. Time and counter are caller-supplied, so the module has no clock dependency and is deterministic to test.

milo
from "std/totp" import { Totp }

Example ​

milo
from "std/base32" import { Base32 }

// decodeLoose tolerates the spaces and missing padding a pasted secret usually has
let key = Base32.decodeLoose("JBSWY3DPEHPK3PXP")!
let code = Totp.generate(key, 1_700_000_000, 30, 6)

Check a submitted code with Totp.verify, never Totp.generate(...) == code: string equality stops at the first wrong digit, which turns a 6-digit code into six independent 10-way guesses. verify compares in constant time.

API reference ​

Totp ​

milo
pub struct Totp

Totp namespace — Totp.hotp(secret,counter,digits), Totp.generate(secret,unixTime,step,digits).

Totp.generate ​

milo
fn Totp.generate(secret: &string, unixTime: i64, step: i64, digits: i64): string

TOTP value for a Unix timestamp (RFC 6238): HOTP over floor(unixTime / step). Use step = 30, digits = 6 for the common authenticator-app setup.

Totp.hotp ​

milo
fn Totp.hotp(secret: &string, counter: i64, digits: i64): string

HOTP value for a moving counter (RFC 4226 §5.3), digits long (6 to 8 is typical), zero-padded.

Totp.verify ​

milo
fn Totp.verify(secret: &string, code: &string, unixTime: i64, step: i64, digits: i64, window: i64): bool

Check a user-supplied code against the codes valid at unixTime, accepting window steps either side (RFC 6238 §5.2 — one step of tolerance is usual, for the user who typed the code as it rolled over).

Exists so that nobody writes Totp.generate(...) == code: string equality stops at the first wrong digit, which turns a 6-digit code into six independent 10-way guesses. This comparison is constant-time.