Daniel sent us this one — he wants to know what's actually happening when his password manager spits out a new six-digit code every thirty seconds. How does the server know which code is valid at that exact moment, without asking? And why does the whole thing occasionally break? He's right that it usually traces back to clock drift, and he wants to know who built this thing and how the math works under the hood.
The answer is so much more elegant than most people realize. That code isn't random. It's not being transmitted. Your phone and the server are computing the exact same deterministic function — same secret, same clock — in total silence. They never exchange a single packet.
Two devices, zero communication, perfectly synchronized output. It sounds like a magic trick.
It does, and the trick is: they both know what time it is. Specifically, Unix time — the number of seconds since midnight, January first, nineteen seventy. They divide that by thirty, round down, and feed the result into a hash function along with a shared secret. Same inputs, same output.
The server isn't "accepting" your code — it's predicting it.
It's computing what your code should be, then comparing. If they match, you're in. And that shared secret — the one you scanned from the QR code — is the only thing that makes your thirty-second window different from anyone else's.
Which means the QR code is just a delivery mechanism. The real crown jewel is that string of characters.
Base32-encoded, sitting inside what's called an otpauth URI. You could type it by hand if you wanted to. The QR code just saves you from transcribing about thirty characters of what looks like alphabet soup.
Daniel's right about the bugs. When this breaks, it's almost always clocks drifting apart. Your phone thinks it's time window forty-seven thousand and twelve, the server thinks it's forty-seven thousand and eleven, and suddenly the math doesn't match.
The standard actually anticipates this. There's a built-in look-ahead window — the server typically accepts codes from the current thirty-second window and the one immediately before it. But if your phone's clock has drifted by two minutes, that's four windows. The system can't compensate, and you're locked out of every account at once.
Which is a special kind of panic the first time it happens.
Here's what makes this worth understanding right now. Everyone's talking about passkeys and WebAuthn — and those are better, we'll get to why — but TOTP is still the most widely deployed second factor on the planet. Hundreds of millions of people use it every day. Almost nobody knows what's actually happening under the hood.
Daniel's prompt gets at something deeper too. He mentions that two-factor authentication is partly a fallback for password reuse — a second lock because humans are going to human. And he's not wrong. But TOTP specifically solves a problem that's genuinely hard: proving you possess a secret without ever sending that secret over the network.
Your password gets transmitted every time you log in. The TOTP secret never leaves your device. The server has its own copy from when you set it up, and from that point on, both sides just do math.
Let's unpack that math. Because the algorithm itself is surprisingly straightforward once you see it step by step.
Before we dive into the algorithm, let's frame the puzzle properly — because the fact that this works at all is kind of astonishing. You scan a QR code once. From that moment on, your phone and a server somewhere in a data center independently generate the same six-digit number every thirty seconds. They never speak again. No handshake, no heartbeat, no network packet.
Yet they stay synchronized for years.
The only two things they share are that secret from the QR code and an agreement about what time it is. That's it. The entire system rests on two RFCs published by a group called the Initiative for Open Authentication — OATH. They formed in two thousand four specifically to create open standards for strong authentication, because before that, every two-factor system was proprietary and incompatible.
This was an industry consortium saying, let's stop reinventing the wheel and build something everyone can use.
The first piece came out in December two thousand five — RFC four two two six, the HMAC-based One-Time Password, or HOTP. That's the core engine. Then in May twenty eleven, they published RFC six two three eight, which layers time on top of the counter-based system to create TOTP. The authors on the TOTP RFC were David M'Raihi from Verisign, Salah Machani from Diversinet, Mingliang Pei from Symantec, and Johan Rydell from Portwise. The HOTP RFC added four more names, including Mihir Bellare from UC San Diego — he's a heavyweight in cryptographic theory.
You've got industry practitioners and an academic cryptographer collaborating on an open standard. That's a pretty good pedigree.
The core insight they built on is deceptively simple. HOTP takes a counter and a secret, hashes them together, and produces a code. TOTP just replaces the counter with the current time window. The server doesn't need to ask your phone anything because it already knows both inputs — it stored the secret when you set up two-factor authentication, and it knows what time it is.
Which brings us back to Daniel's original question. How does the server know what code you're seeing? It does the same math you're doing. Same secret, same clock, same hash function. The only difference is who's holding the calculator.
Let's walk through the actual formula. It starts with TOTP equals HOTP of K and T — where K is the shared secret, and T is the time step. T is calculated as the floor of the current Unix time minus T-zero, divided by X. Default T-zero is the Unix epoch — January first nineteen seventy at midnight UTC. Default X is thirty seconds. So right now, July sixth two thousand twenty-six, Unix time is somewhere around one point seven eight billion seconds. Divide by thirty, round down — that's your time step.
Both devices are doing that same division independently. They agree on what "now" means because they agree on Unix time, and they agree on the thirty-second window. That's the synchronization.
And that time step number — call it T — gets fed into HOTP. The HOTP algorithm does three things. Step one: compute HMAC-SHA-one of the shared secret K and that counter value T. HMAC is a keyed hash construction — it takes your secret, mixes it with the counter, and produces a twenty-byte output. Twenty bytes is one hundred sixty bits of pseudorandom noise.
That's the SHA-one part everyone gets nervous about.
Which we'll get to in a second, because the concern is mostly misplaced here. Step two is the clever part — it's called dynamic truncation. You take the last byte of that twenty-byte hash, and you use its lower four bits as a number between zero and fifteen. That number tells you where to start reading a four-byte chunk from the hash. So if the last byte is, say, hex five-A, you take the lower four bits — that's ten — and you start at byte ten, pulling bytes ten through thirteen.
The offset isn't fixed — it's baked into the hash output itself. That's elegant.
It's beautiful. It means an attacker can't predict which four bytes you'll extract without computing the whole HMAC first. Then you take those four bytes, mask off the most significant bit to avoid signed versus unsigned integer confusion — so you're left with a thirty-one-bit number — and that's your raw output. Step three: take that number modulo ten to the sixth, which is one million. That gives you a number between zero and nine hundred ninety-nine thousand nine hundred ninety-nine. Pad with leading zeros to six digits, and that's your code.
Let's make this concrete. Daniel mentioned the Base32 string you see when you hit "Enter Manually." The classic example from the spec is JBSWY3DPFQQHO33SNRSCC — which, when decoded from Base32, is the ASCII string "Hello, world!" That's the actual shared secret.
So imagine that's your secret K. At a given time step — say T equals fifty-nine million and something — you feed "Hello, world!" and that counter into HMAC-SHA-one. The algorithm produces a twenty-byte hash. You look at the last byte. If it's hex zero-F, the lower four bits are fifteen, which is the maximum offset — so you'd actually wrap around using the RFC's modulo-nineteen rule, but typically you land somewhere in the middle. Let's say the offset points to bytes thirteen through sixteen. You grab those four bytes, mask the top bit, and you've got some integer. Modulo one million, pad to six digits, and that number appears on your screen.
The server, holding the same "Hello, world!" secret and computing the same time step, gets the same integer. No communication required.
Now, about SHA-one. People hear "SHA-one" and think "broken hash function," and they're not wrong — researchers demonstrated collision attacks against SHA-one years ago. But here's why TOTP doesn't care. The RFC explicitly addresses this. Section six of RFC four two two six states that "the outputs of the dynamic truncation on distinct inputs are uniformly and independently distributed strings." The HMAC construction doesn't rely on collision resistance — it relies on the pseudorandomness of the underlying compression function. And even if you could create a SHA-one collision, you'd only get the same HMAC output for two different counters, which buys an attacker nothing — they still can't reverse the hash to recover the secret, and they can't predict future outputs.
Plus you're throwing away sixteen of the twenty bytes in the truncation step. Even if someone found a theoretical weakness in the full hash output, the part you're actually using is a tiny, unpredictable slice.
The truncation step is a natural hedge. An attacker who magically broke SHA-one's collision resistance tomorrow still couldn't derive your six-digit code without the secret. The security model here is brute force — pure and simple — and the RFC gives us the exact probability. The chance of a successful guess is s times v divided by ten to the d, where s is the look-ahead window size, v is the number of verification attempts, and d is the number of digits.
With a look-ahead window of two time steps and a single guess, you're looking at two in a million. And the server should be throttling you after a handful of failures.
Which brings us to that look-ahead window. RFC six two three eight section six recommends accepting one to two time steps backward. That's why a code generated at the very start of a thirty-second window often still works fifty-nine seconds later — the server is checking the current window and the previous one. But the RFC also warns: "the longer a prover has not sent an OTP to a validation system, the longer — potentially — the accumulated clock drift." If your phone hasn't authenticated to a particular service in weeks and its clock has drifted by ninety seconds, you're three windows off. The look-ahead window can't save you.
The drift problem is worse than people think. If your phone's NTP sync silently fails — which happens more often than anyone admits, especially on cellular networks that don't prioritize time accuracy — you don't get a warning. Your codes just stop working. And the diagnostic is circular: you can't log in to check if the service is down, because you can't log in.
It's every account at once. That's the brutal part. Your TOTP codes are all generated from the same device clock. If your phone drifts two minutes off, every single service rejects you simultaneously. There's no per-account drift — the clock is global.
Which is why the "Enter Manually" button is both a convenience and a liability. When you tap that, you see the raw Base32 secret. The string that, if someone steals it, lets them generate valid codes forever — not just for thirty seconds, but indefinitely, until you rotate the secret.
The QR code is just a visual encoding of the otpauth URI. The secret itself is the crown jewel. And here's the architectural problem that most people miss: TOTP is a shared-secret system. Both your phone and the server hold the same key. If an attacker breaches the server's database — and we've seen credential database breaches at major services repeatedly — they walk away with every user's TOTP secret. From that moment, they can generate valid codes at will. No phishing required, no real-time interception.
Compare that to WebAuthn, where your device holds a private key and the server only has a public key. Stealing the server's database gives an attacker exactly nothing they can use to authenticate.
It's the difference between symmetric and asymmetric cryptography. TOTP uses a symmetric key — one secret, two locations. WebAuthn uses an asymmetric key pair — the private key never leaves your device. The shared-secret model was the right tradeoff in two thousand eleven, when TOTP was standardized, because asymmetric crypto on consumer hardware wasn't ubiquitous. Now it is, which is why the industry is shifting.
TOTP is secure against network attacks — nobody can intercept your code and replay it, because it's expired in thirty seconds — but it's vulnerable to server-side breaches in a way that passkeys simply aren't.
That thirty-second window is itself a tradeoff. The RFC's authors knew this. A code generated at the start of a window is valid for up to fifty-nine seconds — the full thirty seconds of the current window plus the look-ahead tolerance. Shorter windows would reduce that exposure, but they'd also increase clock-sync failures. The RFC settles on "at most one time step is allowed as the network delay" — meaning the server checks the current window and the previous one. Two steps, sixty seconds of validity.
Which makes the brute force math tight but not perfect. The probability formula from the RFC — s times v divided by ten to the d — gives you two in a million per guess with standard parameters. That's why throttling matters. RFC four two two six explicitly recommends locking accounts after a threshold of failed attempts, or imposing progressive delays. Without throttling, an attacker who can fire requests rapidly could eventually brute force a six-digit code — two in a million per attempt adds up over millions of attempts.
There's one more ticking clock buried in the spec that almost nobody talks about. RFC six two three eight explicitly requires that implementations "MUST support a time value T larger than a thirty-two-bit integer when it is beyond the year twenty thirty-eight." That's the Unix time overflow — January nineteenth, twenty thirty-eight, when the thirty-two-bit signed integer that represents Unix seconds rolls over to negative two billion. Any legacy TOTP system still using thirty-two-bit time will silently fail that day.
Which means somewhere, in some embedded system or ancient server rack, there's a TOTP implementation that will simply stop working in twelve years. And the people maintaining it probably don't know.
The RFC authors saw it coming in twenty eleven and put the warning right in the spec. Whether every implementer read that paragraph is a different question.
The whole elegant system — the synchronized clocks, the deterministic hashing, the dynamic truncation — it all rests on three fragile assumptions. That your device's clock is accurate. That the server's secret database hasn't been breached. And that the integer storing Unix time hasn't overflowed. Two of those three are invisible to the user until the moment they fail.
What do you actually do with all this? Three things, and they're simple enough that most people skip them.
First one's almost too obvious. Enable automatic time sync on your phone. The majority of TOTP failures trace back to clock drift, and the fix is usually toggling the "set time automatically" setting off and back on. If your codes suddenly stop working everywhere, check your device clock before you assume the service is down.
That's the diagnostic most people run in reverse. They try a different network, they reboot, they reinstall the app — meanwhile their phone is forty-five seconds off and every TOTP computation is silently wrong.
Second: treat your TOTP secrets like passwords. That "Enter Manually" button reveals the raw Base32 string. If your password manager gets compromised, that string lets an attacker generate valid codes indefinitely — not just for thirty seconds. You need to rotate every two-factor secret immediately.
The shared secret doesn't expire. A stolen password you can change. A stolen TOTP secret you have to revoke and reissue, and most people don't realize they need to do both. They change the password and think they're safe.
Third, and this is the one that makes security people nod and everyone else uncomfortable. For high-value accounts — email, banking, anything with financial or identity implications — move to WebAuthn or passkeys if the service supports it. Those eliminate the shared secret problem entirely. The server never holds anything an attacker can steal to impersonate you.
TOTP is better than SMS, dramatically better. But it's not the endgame. It's a symmetric system in a world that's moving asymmetric. Every TOTP secret stored on a server is a single point of failure waiting for a breach.
The broader point Daniel's question gets at — the thing worth sitting with — is that the elegance of TOTP is also its vulnerability. It solves a hard synchronization problem without any network communication. Two devices, one shared secret, one clock. But that same design means you're trusting three things every thirty seconds: that your device clock is accurate, that the server's clock is accurate, and that the secret database hasn't been breached.
Any one of those fails, and the math stops working. No error message explains why. The code just doesn't match, and you're locked out.
Which brings us to the question Daniel's prompt implicitly raises but doesn't quite ask. Where does this technology go from here? TOTP is deployed across millions of services, it's the fallback when passkeys aren't supported, and yet its shared-secret architecture is fundamentally the wrong shape for where authentication is heading.
I think about this as a stepping stone that's going to be with us for a very long time. WebAuthn is objectively better — asymmetric keys, no shared secret to steal, phishing-resistant by design. But TOTP has fifteen years of deployment inertia. Every bank, every email provider, every SaaS platform has it baked in. Replacing that isn't a technology problem, it's a coordination problem.
The year twenty thirty-eight issue adds a hard deadline nobody's planning for. The RFC explicitly requires sixty-four-bit time support, but somewhere out there is a thirty-two-bit embedded system running a TOTP implementation that will silently roll over to negative two billion on January nineteenth. The people who set it up might be retired by then.
Or the company might not exist anymore. And the failure mode is brutal — the math keeps running, the codes keep generating, they just don't match. No crash, no error log, just locked-out users who can't figure out why.
The thing Daniel's watching every thirty seconds — that little six-digit refresh — is simultaneously one of the most elegant pieces of cryptographic engineering ever deployed at scale, and a system with an expiration date baked into its assumptions. Synchronized clocks, a shared secret, and a hash function. That's the whole thing.
Two devices that never speak to each other, computing the same number in total silence, held together by nothing but an agreement about what time it is. Next time you tap that code, that's what you're witnessing.
Now: Hilbert's daily fun fact.
Hilbert: In the late sixteen hundreds, the Yámana people of Tierra del Fuego developed a system of over forty distinct hand gestures used exclusively by women during periods of ritual silence — essentially a full sign-language dialect that converted to about three words per second, roughly the speaking rate of modern conversational English.
...three words per second in Tierra del Fuego.
This has been My Weird Prompts. Thanks to our producer Hilbert Flumingtop. If you enjoyed this, leave us a review wherever you listen — it helps other people find the show. We'll be back soon with another one.