TOTP / 2FA code debugger

Generate and debug TOTP / 2FA codes: the full RFC 6238 derivation, drift-window verification, and otpauth:// parsing — all in your browser.

Time

Enter a secret to see a code.

otpauth:// URI for this configuration
otpauth://totp/user%40example.com?secret=JBSWY3DPEHPK3PXP&algorithm=SHA1&digits=6&period=30

What the six digits actually are

A TOTP code — the number an authenticator app rolls over every thirty seconds — is not random and not stored anywhere. It is computed, on both your phone and the server, from two things they already share: a secret set up once when you enabled two-factor authentication, and the current time. Because both sides can compute it independently, nothing has to travel between them when you log in; the server just runs the same calculation and checks that its answer matches yours.

TOTP (RFC 6238) is a thin layer over an older scheme, HOTP (RFC 4226). HOTP turns a secret and a counter into a code; TOTP is simply HOTP with the counter set to the number of time steps since the Unix epoch. That is the whole idea: divide the current Unix time by the step length (30 seconds by default) and feed the result to HOTP. This tool shows every step of that calculation, because when a code will not verify, the answer is almost always visible somewhere in the derivation.

The derivation, step by step

Five steps take the secret and the time to the digits you type. The tool prints each one so you can compare against your own implementation:

  • The counter. Take the Unix time in seconds, subtract T0 (0 in every real deployment), divide by the period and floor it. At 30-second steps, any moment in the same half-minute gives the same counter, which is why a code lasts as long as it does.
  • The HMAC. Write the counter as 8 big-endian bytes and compute HMAC(secret, counter) with the chosen hash — SHA-1 for almost every authenticator. That produces a 20-byte digest for SHA-1 (32 for SHA-256, 64 for SHA-512).
  • The offset. Take the low four bits of the digest’s last byte. That is a number from 0 to 15, and it says where in the digest to read — dynamic truncation, so the same secret does not always use the same bytes.
  • The 31-bit value. Read four bytes starting at the offset, and mask off the top bit (to stay clear of any sign confusion). That leaves a 31-bit integer.
  • The code. Take that integer modulo 10^digits and left-pad with zeros. Six digits is the near-universal choice; seven and eight exist and are allowed.

The masking of the top bit and the modulo are the only lossy steps: many different digests map to the same six digits, which is fine, because the code only has to be hard to guess in the thirty seconds it is alive, not unique forever.

Why a code does not match — the three usual causes

A TOTP mismatch never comes with an error message; the code is simply wrong. In practice it is nearly always one of three things, and the panels here are arranged to tell them apart:

  • The secret was read in the wrong format. An authenticator secret is Base32, but the same characters read as hex — or a secret that was actually hex read as Base32 — produce completely different bytes and a completely different code. Switch the format toggle and see which one gives the code you expect.
  • A parameter differs. The overwhelming default is HMAC-SHA-1, 6 digits, a 30-second period. A server using SHA-256, or 8 digits, or a 60-second step, will disagree with an app left on the defaults. The otpauth:// URI carries all three, which is why scanning a QR gets them right and typing a secret by hand often does not.
  • The clocks have drifted. TOTP assumes both sides agree on the time. If a phone’s clock is a minute fast, its code is a minute ahead of the server’s. That is what the drift window is for: verifying a code against the steps on either side of now tells you not just whether it matches but how far the clock is off.

RFC 6238 recommends a validation window of at most one step on each side — enough to absorb ordinary clock skew and the moment-of-rollover race, without widening the guess space more than necessary. This tool defaults to ±1 and lets you widen it while debugging.

Secrets, Base32 and the otpauth:// URI

The secret is shared once, at setup, and everything afterwards is derived from it. Authenticator apps encode it in Base32 (RFC 4648) — the alphabet A–Z and 2–7, which avoids the characters that are easy to misread and survives being printed under a QR code. This tool reads Base32 by default, ignoring the spaces apps add for legibility and the case, and also accepts hex so you can reproduce the RFC 6238 test vectors, whose seeds are given as raw bytes.

The QR code you scan at setup is not magic either: it encodes a plain-text otpauth:// URI that carries the secret and every parameter. Paste one here to fill the fields, or build one from the fields to move a setup between apps:

otpauth://totp/GitHub:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=GitHub&algorithm=SHA1&digits=6&period=30

Only the totp type is accepted. An otpauth://hotp URI carries a counter instead of a period, and treating it as TOTP would silently produce wrong codes — so it is rejected rather than guessed at.

Algorithm, digits and period

RFC 6238 allows HMAC-SHA-1, SHA-256 or SHA-512, six to eight digits, and any step length. In practice the world settled on SHA-1, six digits and thirty seconds, and most authenticator apps only implement that combination — so a server that picks anything else has to hope the user’s app follows, and many do not. The safe choice for interoperability is the boring one.

It is worth addressing the SHA-1 question directly, because the hash tool on this site marks SHA-1 as broken. Both statements are true. SHA-1 is unusable for digital signatures because attackers can manufacture collisions — two documents with the same hash. TOTP does not rely on that property: its security comes from the secret key inside the HMAC, not from the hash being collision-resistant, and HMAC-SHA-1 remains sound. Using SHA-256 here is defensible but buys little, and costs interoperability with apps that only do SHA-1.

What this tool does and does not protect

Everything here runs in your browser. The HMAC is computed with the Web Crypto API on your own device, the same primitive the hash and HMAC tools use, and nothing you type — secret, code or otpauth URI — is uploaded, stored or logged. That is a property of the code being client-side, not a promise you have to take on trust.

That said, a TOTP secret is a second factor: anyone who has it can generate your codes. Pasting a production secret into any web page — this one included — puts it on the clipboard and possibly into browser history, so treat it with the same care as a password, and prefer a throwaway or test secret when you only need to understand the mechanism. The tool exists to debug and to learn, not to be where your real second factor lives.

Frequently asked questions

Is my secret sent to a server?
No. The code is computed in your browser with the Web Crypto API, and nothing you type is uploaded or logged. Because a TOTP secret is a second factor, still be careful pasting a production one — it can pass through the clipboard and browser history like any other text.
Why does my code not match my authenticator app?
Almost always one of three things: the secret was read in the wrong format (Base32 vs hex), a parameter differs from the defaults (most apps are SHA-1, 6 digits, 30-second period), or the device clock has drifted. The derivation panel isolates the first two, and verifying with a ± drift window reveals the third.
What is the difference between TOTP and HOTP?
HOTP (RFC 4226) computes a code from a secret and a counter that increments on each use. TOTP (RFC 6238) is the same construction with the counter set to the number of time steps since the Unix epoch, so the code changes with the clock rather than with a button press. TOTP is what authenticator apps use.
Why is SHA-1 the default when it is broken elsewhere?
SHA-1 is broken for signatures because collisions can be constructed, but HMAC — which TOTP is built on — does not depend on collision resistance; its security rests on the secret key. HMAC-SHA-1 is sound, and it is the only algorithm many authenticator apps implement, so it is both the safe and the compatible default.
Can I pin the code to a specific time?
Yes. Switch the time from live to pinned and enter a Unix timestamp. The code then stays static, which is how you reproduce a published test vector — RFC 6238 gives values at times like 59 and 1111111109 — or check what code was valid at a past moment.
How long should the drift window be?
RFC 6238 recommends at most one step on each side, which is what this tool defaults to. A wider window tolerates a more badly-drifted clock but also enlarges the set of codes a server would accept, so it is a trade-off; ±1 is the standard balance.
What are the digits and period for?
The digits are how many numerals the code has — six almost everywhere, seven or eight where a service wants a little more strength. The period is how many seconds a code stays valid, thirty by default. Both are carried in the otpauth:// URI, and both have to match on the two sides or the codes will not agree.