Parte: 2 — Criptografía aplicada · Fuente: Serious Cryptography (Aumasson) y NIST SP 800-90A ⏱️ Duración estimada: 90 min · Nivel: Intermedio
Comprender por qué la aleatoriedad es el cimiento silencioso de toda la criptografía: claves, nonces, IVs, salts y tokens dependen de ella. El alumno aprenderá la diferencia entre un PRNG estadístico (predecible) y un CSPRNG (criptográficamente seguro), de dónde viene la entropía del sistema operativo, y por qué fallos de aleatoriedad han roto sistemas reales (Debian OpenSSL 2008, claves RSA con primos compartidos).
Al finalizar, el alumno podrá:
secrets, os.urandom).random para seguridad.| # | Tema | Por qué importa |
|---|---|---|
| 1 | Entropía y fuentes | Origen de la aleatoriedad |
| 2 | PRNG vs CSPRNG | Predecible vs seguro |
| 3 | /dev/urandom y getrandom() | API del SO |
| 4 | DRBG (SP 800-90A) | Generadores deterministas seguros |
| 5 | APIs seguras (secrets) |
Uso correcto en código |
| 6 | Fallos famosos | Aprender de desastres |
| 7 | Pruebas de aleatoriedad | Detectar sesgos |
Cada clave, cada IV, cada nonce, cada salt, cada token de sesión y cada nonce de ECDSA
sale de un generador de números aleatorios. Si ese generador es predecible, todo lo
construido encima se derrumba por muy correcta que sea la criptografía: no hay que
romper AES si se puede adivinar la clave. Es la dependencia menos visible de la parte y la
que más desastres silenciosos ha causado.
La distinción clave es entre PRNG y CSPRNG. Un PRNG estadístico —el random de
cualquier lenguaje, un Mersenne Twister— produce números que parecen aleatorios y sirven
para simulaciones o videojuegos, pero es determinista y reconstruible: observando unas
cuantas salidas se recupera el estado interno y se predicen todas las siguientes. Un
CSPRNG añade dos garantías que lo hacen apto para criptografía:
impredecibilidad hacia delante (conocer salidas pasadas no permite predecir las
futuras) y resistencia al compromiso del estado (conocer el estado actual no permite
reconstruir las salidas pasadas).
La recomendación profesional es casi aburrida y por eso conviene subrayarla: no
construyas tu propio generador ni siembres uno tú. El kernel recoge entropía de fuentes
físicas —tiempos de interrupción, ruido de dispositivos, instrucciones de hardware como
RDSEED— y la usa para sembrar un CSPRNG que expone al espacio de usuario. En Linux la
llamada correcta es getrandom() o leer /dev/urandom; en Windows, BCryptGenRandom.
Desde los lenguajes, secrets u os.urandom en Python, crypto.randomBytes en Node,
crypto/rand en Go.
Conviene desactivar un mito persistente: /dev/random no es "más seguro" que
/dev/urandom. En los kernels modernos, una vez que el pool está sembrado, ambos
producen material de la misma calidad; la diferencia histórica era que /dev/random
bloqueaba, lo que provocaba cuelgues en arranque y llevaba a los desarrolladores a
soluciones peores. El único momento delicado es el arranque temprano, cuando aún no se
ha acumulado entropía: por eso getrandom() bloquea hasta que el pool esté inicializado y
después no vuelve a hacerlo.
La lista de fallos es corta pero devastadora y merece conocerse porque el patrón se
repite. Debian OpenSSL (2006-2008): un parche bienintencionado eliminó una fuente de
entropía y redujo el espacio de claves a 32 768 posibilidades; durante dos años, cada
clave SSH y cada certificado generado en Debian y derivados fue enumerable en segundos.
Sony PlayStation 3 (2010): nonce k constante en ECDSA, clave de firma de código
recuperada, consola abierta. Carteras de Bitcoin en Android (2013): un fallo en el
proveedor de aleatoriedad de la plataforma repitió nonces de ECDSA y permitió robar claves
privadas y fondos.
Dos patrones adicionales de la práctica diaria: las máquinas virtuales restauradas desde
un snapshot repiten el estado del generador y pueden emitir los mismos nonces dos veces
—especialmente peligroso con AES-GCM—; y los dispositivos embebidos que generan claves
en el primer arranque, cuando aún no hay entropía, acaban produciendo claves idénticas en
miles de unidades. La comprobación práctica es el laboratorio de esta clase: distinguir
por pruebas estadísticas una salida de random de una de secrets, y aprender que
parecer aleatorio y ser impredecible no son lo mismo.
| Término | Definición concisa |
|---|---|
| Entropía | Medida de la incertidumbre real disponible como aleatoriedad |
| PRNG | Generador determinista; parece aleatorio pero es predecible |
| CSPRNG | Generador apto para criptografía: impredecible hacia delante |
| Semilla (seed) | Valor inicial del generador; si es adivinable, todo lo es |
| Estado interno | Datos del generador cuyo conocimiento predice las salidas |
| Mersenne Twister | PRNG estadístico común; nunca para criptografía |
getrandom() |
Llamada al sistema recomendada en Linux |
/dev/urandom |
Fuente de aleatoriedad del kernel; equivalente en calidad |
BCryptGenRandom |
API equivalente en Windows |
secrets / os.urandom |
APIs seguras en Python |
| DRBG | Generador determinista normalizado (NIST SP 800-90A) |
| Debian OpenSSL | Fallo que redujo el espacio de claves a 32 768 |
| Nonce repetido | Consecuencia típica de un generador defectuoso |
| Snapshot de VM | Restaurar estado del generador y repetir valores |
| Pruebas de aleatoriedad | Baterías estadísticas que detectan sesgos |
python3 -c "import secrets; print('ok')"
# opcional: pruebas estadísticas
which rngtest dieharder 2>/dev/null || echo "opcional"
Todo el trabajo es local y de análisis.
python
import os, secrets
clave = os.urandom(32) # 256 bits para AES
nonce = os.urandom(12) # nonce GCM
token = secrets.token_urlsafe(32) # token de sesión
print(clave.hex(), token)
Contraejemplo predecible. Muestra que random.seed(tiempo) + random.getrandbits produce salidas reproducibles si el atacante conoce el instante; concluye que random no sirve para seguridad.
Analiza sesgos. Genera 1 MB con un CSPRNG y con un PRNG mal sembrado; corre pruebas estadísticas (o cuenta frecuencias de bits) y compara.
Estudia el fallo Debian 2008. Investiga cómo un parche que redujo la entropía dejó el espacio de claves en apenas ~32.767 posibilidades y por qué hubo que regenerar millones de claves.
Nonces únicos. Simula la generación de nonces para AES-GCM y verifica (con un conjunto) que no se repiten en el volumen esperado.
random y secrets en Python.time() produce salidas predecibles.Escribe una utilidad que genere claves, nonces y tokens exclusivamente con un CSPRNG, y un test que verifique ausencia de repeticiones en una gran muestra de nonces y una distribución de bits cercana a 50/50. Criterio de aceptación: la muestra no presenta nonces repetidos y la proporción de unos/ceros se desvía menos de un umbral razonable; el código no usa ningún PRNG estadístico.
| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
| Claves reproducibles | Semilla predecible; usa os.urandom/secrets |
Uso de random/rand() para claves |
No es CSPRNG; sustitúyelo |
| Nonces repetidos | Generación insegura; usa CSPRNG o contador único |
| Baja entropía en arranque | Bloquea hasta sembrar (getrandom) o añade fuente de hardware |
| Reutilizar tokens de sesión | Genera uno nuevo por sesión con suficiente longitud |
❓ ¿/dev/urandom o /dev/random?
En Linux moderno, urandom/getrandom() es la elección correcta una vez sembrado; random puede bloquear innecesariamente.
❓ ¿Cuántos bits necesito para un token? Al menos 128 bits (16 bytes) de aleatoriedad real; 256 bits para márgenes amplios.
❓ ¿Por qué la mala aleatoriedad es tan peligrosa? Porque compromete claves y nonces de golpe; un CSPRNG débil hace inútil el mejor algoritmo.
Clase 057 — Almacenamiento seguro de contraseñas: bcrypt, scrypt y Argon2