search

Found

info Overview

Validate RFC 4226 HOTP codes against a ±N counter window. Generates 6, 7 or 8-digit HMAC-SHA-1 codes from a Base32 secret and flags counter resync drift.

📘 How to Use

  1. Enter the Base32 shared secret and counter value
  2. Pick the digit length (6/7/8) and validation window ±N
  3. Type a received code to check for a match inside the window

HOTP (RFC 4226) Counter Validator

RFC 4648 Base32 (A-Z and 2-7). Spaces and dashes are ignored.

The C value in RFC 4226 §5.3. Non-negative integer; HOTP advances per counter, not per time step.

Output OTP length. Google Authenticator uses 6 digits, Yubico tokens often 8.

Search range ±N counters (0-10). Servers (RFC §7.4) should reject any C at or below an already-accepted value.

Enter the OTP a user sent and a match within the window is highlighted in green.

HOTP for current counter

Window codes

Offset Counter Code
Copied!

※ HOTP(K, C) = Truncate(HMAC-SHA-1(K, C)) mod 10^d. C is 8-byte big-endian; the last 4 bits give a dynamic offset whose 4 bytes (high bit cleared) form the modulus input.

※ Servers typically allow ±N drift and advance C to the matched value + 1 after a successful login (RFC §7.4).

Article

HOTP (RFC 4226) Counter Validator|Test ±N Drift Windows in Your Browser

Generate HMAC-SHA-1 HOTP codes from a Base32 secret and counter, then check whether a received code lands inside the ±N counter window. Reproduce RFC 4226 behaviour from plain inputs, no server required.

💡 About this tool

HOTP (RFC 4226) is a one-time-password scheme whose moving factor is a counter, not the clock. It powers hardware tokens like Yubico's OATH-HOTP slot and counter-based codes that back up SMS. Because it needs no clock alignment, it suits offline devices and environments where time sync is unreliable.

The headache with counter-based OTP is drift. The token's counter increments every time the button is pressed, but the server's counter advances only after a successful login. A user who fans the token a few times pushes the two out of step, and codes stop validating. RFC 4226 absorbs this with a look-ahead window: the server recomputes the next few HOTP values, compares them against the submitted one, and on a hit advances its counter to the matched value + 1.

This validator lays out every code inside that window at once. The current-counter row is highlighted, and the row matching your submitted code is shown in a separate colour — so you can reason about how far ahead a server should accept, or debug a token, without spinning up a backend. It is deliberately separate from time-based TOTP; reach for a TOTP tool when the clock is the moving factor.

🧐 Frequently Asked Questions

How is HOTP different from TOTP? The moving factor differs. HOTP uses an event counter; TOTP uses time. A HOTP code stays valid until it is used, whereas a TOTP code self-expires after 30–60 seconds. This tool is HOTP only.

What format does the shared secret take? RFC 4648 Base32 (A–Z and 2–7). Spaces and dashes are ignored and trailing = padding is optional. Any character that is not valid Base32 triggers an error.

What does the ±N window mean? The span of codes checked on either side of the current counter. A larger N tolerates more drift but widens the brute-force surface. RFC §7.4 recommends rejecting any counter at or below one already accepted.

Which digit length should I choose? The OTP output length. Google Authenticator uses 6 digits; Yubico tokens are often 8. Match your token's spec.

Where does the computation run? Entirely in your browser via the Web Crypto API; the shared secret you enter is never sent to a server.

📚 Why the dynamic truncation step exists

The clever part of HOTP is dynamic truncation. The last 4 bits of the 20-byte HMAC-SHA-1 output are read as an offset, four bytes are sliced from there, and the top bit is masked to 0 to form a 31-bit integer before the modulo. That high-bit mask is not cosmetic: it sidesteps implementations that would otherwise treat the slice as a signed integer and disagree on the result. RFC 4226 Appendix D pins this down with test vectors — secret 12345678901234567890 yields 755224 at counter 0 and 287082 at counter 1 — so any implementation can be checked line by line. Because the procedure is frozen this precisely, a token and a server from different vendors land on the same code, and the later TOTP standard simply reused the same truncation on top of a time-derived counter.