TOTP is easy

2025/08/01

One-time passwords are everywhere. Let’s learn how to generate one yourself. The algorithm is simple: calculate an HMAC over an 8-byte counter and truncate it for display. Truncation involves using the last four bits of the hash to determine an offset, from which four bytes are extracted. The most significant bit of these four bytes is masked out (to avoid negative values), and the remaining 31 bits are treated as a big-endian integer. Finally, the decimal representation of this integer is truncated to the required length.

The counter must be synchronised between the generator and validator. For HOTP it is the sequential index of the password; for TOTP it is derived from current time.

The algorithm translates to this Python program:

import hmac, base64, hashlib, time

def hotp(secret: bytes, counter: int, length: int = 6):
    h = hmac.new(
        base64.b32decode(secret),
        counter.to_bytes(8, "big"),
        hashlib.sha1,
    ).digest()
    o = h[-1] & 0xF
    res = int.from_bytes(h[o : o + 4], "big") & 0x7FFFFFFF
    return str(res % 10**length).zfill(length)

def totp(secret: bytes, period: int = 30):
    return hotp(secret, int(time.time()) // period)

print(f"HOTP: {hotp('SDACNQ37BVMBQ3Q5', 1)}")
print(f"TOTP: {totp('SDACNQ37BVMBQ3Q5')}")
HOTP: 795526
TOTP: 467487