Parte: 2 — Criptografía aplicada · Fuente: Cryptography Engineering (Ferguson/Schneier/Kohno) e IETF RFC 2104 ⏱️ Duración estimada: 90 min · Nivel: Intermedio
Entender qué es un código de autenticación de mensajes (MAC), por qué la integridad sin autenticación no basta, y cómo HMAC combina una función hash con una clave secreta para garantizar que un mensaje proviene de quien dice y no fue alterado. El alumno aprenderá también el orden correcto de combinar cifrado y MAC (Encrypt-then-MAC) y por qué la comparación debe ser en tiempo constante.
Al finalizar, el alumno podrá:
| # | Tema | Por qué importa |
|---|---|---|
| 1 | MAC: definición y objetivo | Autenticidad de mensajes |
| 2 | Construcción HMAC | Estándar seguro y simple |
| 3 | HMAC vs hash con clave concatenada | Evita extensión de longitud |
| 4 | Encrypt-then-MAC vs MAC-then-Encrypt | Orden correcto |
| 5 | Comparación en tiempo constante | Evita fugas de timing |
| 6 | Claves y rotación | Gestión práctica |
| 7 | HMAC en la práctica (JWT, APIs) | Uso real |
Un digest garantiza que un dato no cambió si el digest llegó por un canal fiable. Pero si viaja junto al mensaje, un atacante que altere el mensaje recalcula el digest y nadie lo nota: un hash sin clave no aporta autenticidad ninguna. Un MAC (Message Authentication Code) resuelve eso incorporando una clave secreta compartida: solo quien la conoce puede generar una etiqueta válida, así que verificarla prueba a la vez que el mensaje no se alteró y que proviene de alguien que posee la clave.
La diferencia con una firma digital (clase 054) es el tipo de clave y su consecuencia jurídica. El MAC usa una clave simétrica, que ambas partes conocen; por eso el receptor puede fabricar mensajes indistinguibles de los del emisor y no hay no repudio. La firma usa clave privada, que solo tiene el emisor, y por eso sí lo hay. El MAC es más rápido y suficiente cuando ambas partes se confían mutuamente.
La construcción intuitiva —H(clave ‖ mensaje)— es exactamente la que rompe el ataque de
extensión de longitud de la clase anterior: con SHA-256, un atacante que vea el
mensaje y su etiqueta puede añadir datos al final y calcular la etiqueta correcta sin
conocer la clave. Poner la clave al final (H(mensaje ‖ clave)) traslada el problema a
las colisiones del hash. HMAC resuelve ambos casos con una construcción anidada de dos
pasadas:
HMAC(K, m) = H( (K ⊕ opad) ‖ H( (K ⊕ ipad) ‖ m ) )
El hash interno procesa el mensaje con una versión de la clave; el externo vuelve a hashear ese resultado con otra versión. Esa anidación neutraliza la extensión de longitud y tiene, además, una demostración de seguridad: HMAC es seguro incluso si la función hash subyacente pierde resistencia a colisión, y por eso HMAC-SHA1 siguió siendo aceptable como MAC durante años después de que SHA-1 cayera para firmas.
Cuando hay que cifrar y autenticar por separado, el orden de composición no es indiferente y la comunidad tardó años y varias vulnerabilidades en fijar la respuesta correcta. Encrypt-then-MAC —cifrar primero y calcular el MAC sobre el texto cifrado— es la única de las tres combinaciones que es segura en general. Su virtud práctica es enorme: el receptor verifica el MAC antes de descifrar nada, así que descarta cualquier mensaje manipulado sin ejecutar la lógica de descifrado ni la de relleno, lo que elimina de raíz el padding oracle de la clase 060.
Las otras dos son problemáticas. MAC-then-encrypt (lo que hacía TLS 1.2) obliga a descifrar antes de poder verificar, que es precisamente la ventana que explotan Lucky13 y POODLE. Encrypt-and-MAC puede filtrar información sobre el texto claro a través del MAC. La conclusión moderna es la misma que en la clase 047: no compongas tú; usa un AEAD (clase 059) que ya trae la composición resuelta y probada.
Un detalle de implementación arruina lo anterior si se descuida. Comparar la etiqueta
recibida con la calculada usando == o memcmp corta en el primer byte distinto, así que
el tiempo de respuesta revela cuántos bytes iniciales acertó el atacante, que puede
entonces construir una etiqueta válida byte a byte con unos miles de intentos. La
verificación debe hacerse en tiempo constante, recorriendo siempre la longitud
completa (hmac.compare_digest en Python, crypto/subtle en Go). Es un ejemplo temprano
de la idea que domina la clase 060: en criptografía, el tiempo es un canal de salida.
HMAC(k,m) = H((k⊕opad) || H((k⊕ipad) || m)). Seguro con cualquier hash decente, inmune a extensión de longitud.| Término | Definición concisa |
|---|---|
| MAC | Etiqueta con clave secreta que prueba integridad y origen |
| Autenticidad | Garantía de que el mensaje viene de quien dice |
| Clave simétrica compartida | La misma clave genera y verifica el MAC |
| No repudio | Imposibilidad de negar la autoría; el MAC no lo da |
| HMAC | MAC estándar con dos pasadas de hash anidadas |
ipad / opad |
Constantes de relleno interno y externo de HMAC |
| Extensión de longitud | Ataque que HMAC neutraliza por su anidación |
| Encrypt-then-MAC | Cifrar y luego autenticar el cifrado; la composición segura |
| MAC-then-encrypt | Orden inseguro usado por TLS 1.2; habilita Lucky13 |
| Encrypt-and-MAC | Orden que puede filtrar información del texto claro |
| Tiempo constante | Comparación que no depende de dónde falla la coincidencia |
compare_digest |
Función de comparación segura en la stdlib de Python |
| Rotación de claves | Renovar periódicamente la clave del MAC |
| JWT (HS256) | Token web firmado con HMAC-SHA256 |
openssl version
pip install cryptography
Laboratorio local. Las claves de HMAC son de práctica.
bash
echo -n "mensaje" | openssl dgst -sha256 -hmac "clave-secreta"
python
import hmac, hashlib
key = b"clave-secreta"
tag = hmac.new(key, b"mensaje", hashlib.sha256).hexdigest()
# verificación en tiempo constante
ok = hmac.compare_digest(tag, tag)
print(tag, ok)
Detecta manipulación: envía (mensaje, tag); el receptor recomputa el HMAC. Cambia un carácter del mensaje y comprueba que la verificación falla.
Encrypt-then-MAC: cifra con AES-CBC, calcula HMAC-SHA256 sobre IV || ciphertext, y verifica el MAC antes de descifrar. Documenta por qué esto previene padding oracle (clase 060).
Timing (concepto): implementa una comparación ingenua byte a byte con salida temprana y razona por qué filtra información; sustitúyela por compare_digest.
hash(clave || mensaje) es inseguro y HMAC no.Implementa un canal autenticado sencillo: el emisor envía (IV, ciphertext, HMAC) y el receptor rechaza cualquier mensaje manipulado sin descifrarlo. Criterio de aceptación: alterar cualquier byte del IV, del texto cifrado o del tag provoca rechazo, y solo los mensajes íntegros se descifran.
| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
| MAC-then-Encrypt con CBC | Habilita padding oracle; usa Encrypt-then-MAC o AEAD |
Comparar tags con == |
Fuga de timing; usa compare_digest |
| Reutilizar la clave de cifrado como clave de MAC | Debilita ambas; deriva claves separadas |
| Verificar el MAC después de descifrar | Ya procesaste datos no autenticados; verifica antes |
| Hash simple como "firma" de API | No autentica origen; usa HMAC con clave secreta |
❓ ¿HMAC o AEAD? Si ya cifras, prefiere AEAD (GCM, ChaCha20-Poly1305), que integra el MAC. HMAC brilla para autenticar datos que no ciframos (tokens, webhooks).
❓ ¿Puedo usar HMAC-MD5? HMAC-MD5 resiste mejor que MD5 solo, pero usa HMAC-SHA256 por buenas prácticas.
❓ ¿El MAC da confidencialidad? No; solo autenticidad e integridad. Combínalo con cifrado para confidencialidad.
Clase 051 — Funciones hash: SHA-2, SHA-3 y sus propiedades