TOTP: cómo Google Authenticator genera códigos sin Internet
Google Authenticator, Microsoft Authenticator, Authy y cualquier otra aplicación autenticadora pueden producir códigos aunque el teléfono esté en modo avión. Durante la configuración, un QR entrega un secreto y parámetros de la cuenta. El teléfono conserva ese secreto y el verificador dispone del mismo material para validar códigos. El QR no establece una conexión permanente: aprovisiona una credencial.
Del reloj a un contador
TOTP significa Time-based One-Time Password. En español, generador de contraseñas de un solo uso basado en tiempo. Usa HOTP con un contador derivado del tiempo. Con época inicial T₀, hora Unix t e intervalo X, se calcula:
C = floor((t − T₀) / X)
H = HMAC(K, contador C en 8 bytes big-endian)
S = truncado_dinamico(H) # entero de 31 bits
OTP = S mod 10^d # d dígitos, con ceros iniciales
Un perfil habitual usa pasos de 30 segundos y seis dígitos. El teléfono y el servidor obtienen el mismo resultado si coinciden el secreto, el contador y los parámetros. RFC 6238 define la variante temporal y admite HMAC con SHA-1, SHA-256 o SHA-512; los dos extremos deben usar la misma opción.
Implementación en C#
Así se ve el pseudocódigo de arriba en código real. El truncado dinámico (offset, la máscara 0x7F en el primer byte) es exactamente lo que describe RFC 4226:
using System;
using System.Security.Cryptography;
public static class Totp
{
public static string GenerateCode(
byte[] secretKey, DateTimeOffset timestamp, int digits = 6, int stepSeconds = 30)
{
long counter = timestamp.ToUnixTimeSeconds() / stepSeconds;
byte[] counterBytes = BitConverter.GetBytes(counter);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(counterBytes); // RFC 6238 exige big-endian
}
using var hmac = new HMACSHA1(secretKey);
byte[] hash = hmac.ComputeHash(counterBytes);
int offset = hash[^1] & 0x0F;
int truncated =
((hash[offset] & 0x7F) << 24) |
((hash[offset + 1] & 0xFF) << 16) |
((hash[offset + 2] & 0xFF) << 8) |
(hash[offset + 3] & 0xFF);
int otp = truncated % (int)Math.Pow(10, digits);
return otp.ToString().PadLeft(digits, '0');
}
}
Esto es útil para entender el algoritmo, pero no lo escribas a mano en producción: la verificación necesita comparar contra una pequeña ventana de contadores vecinos (para tolerar el desfase de reloj del que habla la siguiente sección) sin abrir una ventana tan grande que facilite adivinar el código. Una librería auditada como Otp.NET ya resuelve eso:
// dotnet add package Otp.NET
var totp = new OtpNet.Totp(secretKey);
string code = totp.ComputeTotp();
bool isValid = totp.VerifyTotp(
userInput, out long timeStepMatched, new VerificationWindow(previous: 1, future: 1));
Qué significa “sin Internet”
Generar el código es local. Enviar un formulario de acceso a un sitio sigue requiriendo conectividad desde el dispositivo con el que te autenticas. La hora del teléfono debe ser suficientemente correcta. El servidor puede aceptar una ventana pequeña de contadores vecinos para compensar desfases, con un equilibrio entre tolerancia y superficie de intento.
Por qué seis dígitos no bastan por sí solos
Un código de seis dígitos tiene un millón de combinaciones. Su seguridad depende del secreto, su corta vigencia, los límites de intentos y el contexto de autenticación. El verificador debe impedir reutilizar un código que ya validó correctamente dentro de su vigencia. Aceptar indiscriminadamente muchos intervalos amplía las oportunidades de adivinación.
El secreto debe protegerse tanto en el dispositivo como en el servidor; no puede sustituirse por un hash de contraseña convencional si el verificador necesita recalcular HMAC. Los respaldos y la recuperación también forman parte de la protección de la cuenta. No se debe publicar el QR real en una guía.
Relación con otras técnicas
Las firmas HMAC de webhooks y solicitudes API comparten la idea de autenticar un mensaje con un secreto. Sin embargo, sus mensajes, formatos y mecanismos contra repetición difieren; no son necesariamente códigos TOTP. Un código válido puede ser capturado mediante phishing y retransmitido mientras sigue vigente. TOTP añade un factor, pero no vincula criptográficamente la respuesta al dominio del sitio como otros mecanismos de autenticación.
Prompt para integrar TOTP con una IA
Si vas a pedirle a un asistente de IA que agregue TOTP a un sistema que ya tiene autenticación, el prompt no debería traer código de memoria. Cada base de código guarda ese flujo distinto. Lo que sí vale la pena fijar de antemano es la metodología: que investigue antes de escribir, que no invente un mecanismo de notificaciones si ya existe uno, y sobre todo que no se salte la recuperación de cuenta cuando el usuario pierde el teléfono. Uso este prompt, en inglés, porque ahí responden mejor la mayoría de los modelos:
You are implementing TOTP-based two-factor authentication (compatible with
Google Authenticator, Microsoft Authenticator, Authy) into an EXISTING
codebase that already has its own authentication system. Do not assume
its shape — discover it.
1. Create a new branch before making any changes (e.g. feature/totp-2fa).
2. Analyze the existing authentication flow before writing any code:
- Locate where user credentials are verified (login endpoint/service).
- Locate how the user's identity/session is issued after login (JWT,
session cookie, etc.) and where that logic lives.
- Locate the user-notification mechanism (email/SMS service) already
used elsewhere in the codebase, since account-recovery flows will
need it.
- If a notification mechanism does NOT exist, stop and report that
explicitly — do not silently build a one-off email sender. Ask how
the project wants outbound notifications handled before proceeding.
3. Propose the enrollment flow (QR provisioning, secret storage — the
secret must be stored encrypted at rest, never logged, never returned
in any API response after initial setup) and the verification flow
(accept a small window of adjacent time steps to tolerate clock drift;
reject a code that already succeeded once within its validity window).
4. Define and implement an account-recovery protocol for when the user
loses access to their authenticator device — at minimum:
- One-time backup/recovery codes generated at enrollment, stored
hashed (not plaintext), shown to the user exactly once.
- A documented manual-recovery path (e.g. identity-verified support
flow) for when backup codes are also unavailable, using whatever
notification mechanism you found in step 2.
- Both paths must invalidate the old TOTP secret and force
re-enrollment.
5. Before finishing: summarize what you changed, what you found already
in place vs. what you had to build from scratch, and flag anything
you're not fully confident about (especially around session
invalidation and secret storage) for human review — this touches
authentication, so do not merge without a security-focused review.