HOTP Explained — What It Is, How It Works & HOTP Examples
Two-factor authentication protects billions of accounts. Most people interact with it daily without knowing the names of the underlying standards. They scan QR codes, enter six-digit codes, and move on. But behind those codes sit two distinct technologies — and understanding the difference matters if you're building security systems, evaluating authentication options, or simply want to understand what's actually protecting your accounts.
This guide covers HOTP — what it is, how it works, where it differs from its more widely-used sibling TOTP, and when it makes sense to use it.
What Is HOTP?
HOTP stands for HMAC-based One-Time Password. It's an algorithm that generates one-time passwords using a cryptographic hash function (HMAC-SHA1) combined with a shared secret key and an incrementing counter. Each time a password is generated, the counter increases by one — making each code unique and ensuring it can never be reused.
HOTP was standardized by the IETF in RFC 4226, published in December 2005. It forms the foundation of the OATH (Initiative for Open Authentication) framework — which is why you'll frequently encounter the term OATH HOTP in security documentation, vendor specifications, and authentication system requirements.
Before TOTP existed, HOTP was the primary standard for software-based one-time password generation. Many physical hardware tokens — RSA SecurID, Yubico OTP, and similar devices — are built on HOTP's counter-based architecture.
HOTP Meaning: Breaking Down the Acronym
H — HMAC-based: The algorithm uses HMAC (Hash-based Message Authentication Code) to combine the secret key and counter into a cryptographically secure output. HMAC-SHA1 is the default hash function specified in RFC 4226, though implementations can use stronger variants.
O — One-Time: Each code can only be used once. After a code is verified, the counter increments and that specific code becomes permanently invalid. Even within the validity window, replay attacks fail because the server tracks which counter value was last used.
T — hmac-based one-Time: (see above)
P — Password: The output is a numeric password — typically six to eight digits — that the user enters to prove possession of the shared secret key without revealing the key itself.
Together, HOTP creates a system where both parties — your device and the authentication server — can independently generate identical codes by sharing the same secret key and maintaining synchronized counters, without ever transmitting the key itself over any network.
How HOTP Works: The Technical Process
Understanding HOTP at a technical level reveals why it's both elegant and occasionally problematic in practice. The algorithm runs through three distinct phases.
Phase 1: Generating the HMAC Value
The algorithm takes two inputs: the shared secret key (K) and the current counter value (C). These are fed into the HMAC-SHA1 function:
HMAC-SHA1(K, C)
This produces a 20-byte (160-bit) hash value. The hash output changes completely with every counter increment — a property called the avalanche effect — meaning even a counter change of 1 produces a completely different hash.
Phase 2: Dynamic Truncation
A 20-byte value contains too much information to present as a usable password. Dynamic truncation extracts a 4-byte portion from the hash using an offset derived from the last byte of the HMAC output:
offset = HMAC_result & 0xf P = HMAC_result & 0x7fffffff
The bitwise AND with 0x7fffffff removes the most significant bit to avoid signed integer complications across different programming environments.
Phase 3: Computing the Final Code
The truncated integer is reduced to the desired number of digits (typically 6 or 8) using modulo arithmetic:
HOTP value = P mod 10^Digit // For 6-digit codes: HOTP value = P mod 1000000
The result is zero-padded to the specified length if necessary — producing the familiar six-digit code that users enter during authentication.
HOTP Example: Walking Through a Real Calculation
Let's work through a concrete HOTP example to make this tangible. RFC 4226 itself includes test vectors using the following parameters:
Parameter Value Secret Key (K) 12345678901234567890 Counter (C) = 0 → HOTP = 755224 Counter (C) = 1 → HOTP = 287082 Counter (C) = 2 → HOTP = 359152 Counter (C) = 3 → HOTP = 969429 Counter (C) = 4 → HOTP = 338314
Notice that each counter value produces a completely different six-digit code. The relationship between counter values and output codes is cryptographically unpredictable — knowing codes at counter 0 through 4 gives you zero information about the code at counter 5.
This predictable test vector serves a critical purpose in development: if your HOTP implementation produces these exact codes for this exact secret and counter sequence, you know your implementation is correct. Developers use known test vectors to validate their code before deploying to production.
For testing TOTP implementations (the time-based variant), our free online TOTP code generator lets you verify codes against known secrets without setting up full authenticator infrastructure.
OATH HOTP: Understanding the Standard Framework
The term OATH HOTP appears frequently in enterprise security documentation. OATH — the Initiative for Open Authentication — is an industry consortium that developed open standards for strong authentication. HOTP (RFC 4226) and TOTP (RFC 6238) are both OATH standards.
When vendors describe their products as "OATH HOTP compliant," they mean:
- The device or software implements HOTP exactly as specified in RFC 4226 - It will interoperate with any other OATH HOTP compliant system - The vendor accepts third-party security validation against the published specification
This standardization is genuinely valuable. An OATH HOTP hardware token from one manufacturer works with authentication servers from completely different vendors. No proprietary lock-in. No compatibility issues. Just a published standard that any implementation can follow.
HOTP vs TOTP: The Critical Difference
TOTP (Time-based One-Time Password) is a direct evolution of HOTP. The core algorithm is identical — HMAC-SHA1 truncated to six digits — with one critical change: the counter is replaced by the current timestamp.
Feature HOTP TOTP Counter Type Event counter (increments on use) Time counter (increments every 30s) Code Validity Until used (no expiry) 30 seconds from generation Offline Use ✅ Full offline support ✅ Full offline support Sync Requirement Counter sync between device and server Clock sync (automatic on most devices) Replay Attack Risk Low (used codes invalidated immediately) Very low (30-second window closes fast) Counter Drift High risk if tokens pressed without logging in No counter drift (time-synchronized) Best Use Case Hardware tokens, YubiKey OTP Software authenticator apps RFC Standard RFC 4226 (2005) RFC 6238 (2011)
The counter drift problem deserves special attention. In HOTP, both the token and server maintain synchronized counters. If a user generates several codes without logging in — pressing the hardware token button multiple times accidentally or testing it — the token's counter advances while the server's counter stays at the last verified value.
Most HOTP implementations handle this by allowing a "look-ahead" window — verifying codes not just for the current counter but for the next N counter values. If the user's token is 10 presses ahead, the server accepts any code within the next 10-50 counter values and re-syncs. But if drift becomes too large, manual re-synchronization is required.
TOTP eliminated this problem entirely by anchoring both sides to the current time. Clock drift on modern networked devices is typically under one second — easily handled by accepting codes from the previous and next 30-second windows.
When HOTP Makes Sense Today
Given that TOTP solves HOTP's counter drift problem, you might wonder whether HOTP still has a role. It does — in specific contexts where its counter-based nature is actually an advantage.
Hardware Security Tokens
Physical hardware tokens that don't have real-time clocks benefit from HOTP's counter-based approach. The token generates a code on button press. The server validates it and increments its expected counter. No time synchronization required. No battery drain from maintaining a real-time clock. This is why devices like classic Yubico OTP (not FIDO2) use counter-based HOTP under the hood.
Environments Without Reliable Time Sources
Embedded systems, industrial controllers, and offline devices that can't reliably access time servers benefit from HOTP's independence from time synchronization. The counter is maintained locally and doesn't require network time protocol (NTP) access.
Single-Use Link Tokens
Email verification links, one-time login codes, and magic links often use HOTP-style counter-based single-use tokens. Each link contains a specific counter value. Once verified, that counter is invalidated. The "use once and expire" property maps naturally to HOTP's design.
Compliance with Legacy Systems
Enterprise environments running authentication infrastructure built when HOTP was the primary standard often maintain HOTP support to avoid disrupting existing hardware token deployments. Migrating thousands of physical tokens across an organization is expensive and disruptive — HOTP support in modern systems maintains backward compatibility.
Implementing HOTP: Code Examples
For developers implementing HOTP in applications, here are reference implementations in common languages that follow RFC 4226 exactly:
Python Implementation:
import hmac import hashlib import struct def hotp(secret: bytes, counter: int, digits: int = 6) -> str: # Step 1: Generate HMAC-SHA1 counter_bytes = struct.pack('>Q', counter) hmac_hash = hmac.new(secret, counter_bytes, hashlib.sha1).digest() # Step 2: Dynamic truncation offset = hmac_hash & 0x0f truncated = struct.unpack('>I', hmac_hash) truncated &= 0x7fffffff # Step 3: Compute OTP otp = truncated % (10 ** digits) return str(otp).zfill(digits) # Example usage secret = b'12345678901234567890' for counter in range(5): print(f"Counter {counter}: {hotp(secret, counter)}")
Node.js Implementation:
const crypto = require('crypto'); function hotp(secret, counter, digits = 6) { // Step 1: Generate HMAC-SHA1 const counterBuffer = Buffer.alloc(8); counterBuffer.writeBigUInt64BE(BigInt(counter)); const hmac = crypto.createHmac('sha1', secret); hmac.update(counterBuffer); const hash = hmac.digest(); // Step 2: Dynamic truncation const offset = hash & 0x0f; const truncated = ( ((hash & 0x7f)

















