How an authenticator app's 6-digit code is made: TOTP step by step

How your authenticator app's code matches the server without asking it, traced through the RFC test values: time steps, HMAC, dynamic truncation, server checks, and the phishing gap.

DevelopmentSecurity

Your authenticator app’s 6-digit code changes every 30 seconds. The app never asks the server for it, yet the server accepts it. Both sides hold the same secret key, follow the same time-step rules and compute the code independently, and their clocks are close enough to agree. That calculation is TOTP (Time-Based One-Time Password), defined in RFC 6238.

The calculation

First the current Unix time becomes a counter T = floor((current time - T0) / X). With the defaults X = 30 seconds and T0 = 0, T is 1 at 59 seconds and 2 at 60 seconds.

The rest is HOTP, the counter-based algorithm in RFC 4226. The shared secret key is used to compute HMAC-SHA-1 (RFC 2104) over T as an 8-byte counter, giving 20 bytes. HMAC is a message authentication code that mixes in the key and applies a hash function twice; RFC 6238 says TOTP MAY use SHA-256 or SHA-512 instead. Dynamic truncation then takes the low 4 bits of the last byte as an offset, reads 4 bytes from there, and clears the top bit to avoid signed/unsigned confusion. That 31-bit integer modulo 10^digits is the code, padded with leading zeros if it is short.

With the RFC test key 12345678901234567890 and T = 1, HMAC-SHA-1 is 75a48a19d4cbe100644e8ac1397eea747a2d33ab (RFC 4226 Appendix D). The last byte ab gives offset 11; the bytes there are c1 39 7e ea, and clearing the top bit gives 1,094,287,082. Modulo 10^6 that is 287082; modulo 10^8 it is 94287082, the 59-second value in the RFC 6238 table.

Verification and replay

The server computes T from the time it receives the code. RFC 6238 says to compare against earlier time steps too, but recommends allowing at most one step for network delay, since a wider window gives attackers more time. A validated code MUST NOT be accepted again. NIST SP 800-63B-4, guidance for US federal government systems, likewise requires accepting an OTP only once while it is valid (SHALL) and requires rate limiting for OTPs shorter than 64 bits (SHALL). A 6-digit code, with a million possible values, falls under that rule; unless specified otherwise, consecutive failures are limited to 100. The server recomputes codes with the same key, so stored keys must be strongly protected.

Weak against phishing

TOTP stops an attacker who has only your password, but not one who collects the code itself. NIST SP 800-63B-4 classifies OTP authentication as not phishing-resistant. A typed code is not bound to the session being authenticated, so a fake site can relay it to the real one while it is valid, and the login can succeed. In this guidance, phishing resistance comes only from cryptographic authentication with channel binding or verifier name binding such as WebAuthn. For US federal systems, AAL2 verifiers must offer at least one phishing-resistant option (SHALL), and AAL3 requires phishing resistance.

RFC 6238, 4226 and 2104 are Informational RFCs written for interoperability, not Standards Track documents. NIST’s SHALL requirements are normative only within the scope of US government systems.

References

  • RFC 6238, TOTP: Time-Based One-Time Password Algorithm (2011)
  • RFC 4226, HOTP: An HMAC-Based One-Time Password Algorithm (2005)
  • RFC 2104, HMAC: Keyed-Hashing for Message Authentication (1997)
  • NIST SP 800-63B-4 (2025)

Back to the blog