01 · Punto de partida
El generador correcto para cada trabajo
Un simulador quiere millones de valores por segundo y repetirlos con una semilla. Un sistema de claves quiere lo opuesto: que nadie pueda predecir ni repetir el próximo token aunque haya visto mil anteriores. Usar el generador equivocado sale caro: sorteo predecible o simulación de 10 horas que no se puede depurar.
Los Temas 12 a 17 te dieron velocidad y calidad estadística. Aquí agregamos la tercera dimensión: la imprevisibilidad ante un adversario. Esa es la frontera entre un PRNG de simulación y un CSPRNG.
- ¿Por qué MT/PCG/Philox, aun siendo modernos, no sirven para claves?
- ¿Qué garantiza un CSPRNG que un PRNG no promete?
- ¿Por qué el CSPRNG es más lento y no se siembra a mano?
- ¿Cuándo usar
random,numpyy cuándosecrets?
02 · Definición
PRNG frente a CSPRNG
PRNG simulación
MT, PCG, Philox, xoshiro: semilla manual, secuencia repetible, gigas por segundo. Predecible con observación suficiente.
CSPRNG
ChaCha20, AES-CTR_DRBG, getrandom / BCrypt: entropía del SO, re-siembra automática, pasa el next-bit test.
Next-bit test
Si conociendo todos los bits hasta n ningún atacante eficiente predice el bit n+1 mejor que 50 %, es criptográficamente seguro.
| Aspecto | Simulación (random / numpy) | Cripto (secrets / os.urandom) |
|---|---|---|
| Semilla | Manual, p. ej. seed(42) | Entropía del SO, no la elegís |
| Reproducible | Sí (ideal depurar) | No (ideal no predecir) |
| Velocidad | Muy rápido, vectorizable | Más lento, llamada al sistema |
| Si se filtra el estado | Se predice todo lo futuro | Resiste: re-seed y backtracking limitado |
03 · Cómo funciona un CSPRNG
Entropía + cifrado + re-siembra
El SO junta ruido real (tiempos de interrupciones, sensores, jitter) en un pozo de entropía y lo estira con un cifrado (ChaCha20, AES) en modo DRBG. Cada llamada mezcla estado nuevo: aunque roben el estado actual, el pasado y el futuro tras re-seed siguen protegidos.
- Entropía
- Ruido físico del sistema (
/dev/urandom,getrandom(),BCryptGenRandom). No es semilla elegida: es azar del entorno. - DRBG
- Generador determinista que estira poca entropía en muchos bytes seguros (Hash_DRBG, HMAC_DRBG, CTR_DRBG según NIST 800-90A).
- Reseed
- Re-inyección periódica de entropía fresca: limita el daño si el estado se compromete.
Bien usado
secrets para lo secreto
Claves, tokens, passwords, sorteos con premio: secrets.token_hex(), secrets.randbelow(). Sin semilla manual.
Mal usado
random para lo secreto
Con 624 salidas se clona MT; con pocas se clona un LCG. Un token con random es un cartel de “adiviname”.
04 · Ejemplos
Qué va de cada lado
- 1Simulación Monte Carlo.
Mil millones de réplicas reproducibles con
default_rng(123). Velocidad yspawnmandan (Temas 37–38). - 2Token de sesión / reset de clave.
secrets.token_urlsafe(32). Cada llamado es único aunque el atacante vea mil tokens previos. - 3Sorteo con premio / loot pagado.
Hay incentivo para predecir: va del lado cripto (
secrets.randbelow(n)), auditado y sin semilla fija en producción.
05 · Implementación en Python
secrets frente a random
La librería estándar lo deja claro: random es para modelado y juegos sin adversario; secrets es para claves y tokens. Fijate que secrets ni siquiera acepta semilla.
Python en tu navegador. Ejecutá dos veces el bloque random: repite. El bloque secrets nunca repite.
import random
random.seed(42)
print([round(random.random(), 6) for _ in range(3)])
random.seed(42)
print([round(random.random(), 6) for _ in range(3)])
Misma semilla, misma secuencia: perfecto para depurar, fatal para un token.
Tokens que no se repiten
import secrets
print(secrets.token_hex(8))
print(secrets.token_hex(8))
print(secrets.randbelow(100), secrets.randbelow(100))
print(secrets.choice(["oro", "plata", "bronce"]))
06 · Exploración
Laboratorio: velocidad frente a imprevisibilidad
Compará tres fuentes del navegador: LCG didáctico (predecible y con semilla), Math.random (rápido, no seguro) y crypto.getRandomValues (seguro). Medimos tiempo real para N valores y si la secuencia se repite con la misma semilla.
Predecir o no predecir
LCG · Math.random · crypto
LCG con semilla 42: repetible al milímetro.
La forma de la curva no delata seguridad: lo que importa es si conociendo el pasado se puede predecir el futuro y de dónde sale la semilla.
Preguntas para explorar
- Generá dos veces con LCG y semilla 42: ¿cambia el primer valor? Repetí con crypto: ¿qué pasa?
- Subí N a 500 y compará tiempos: ¿cuál es más lento? ¿Lo usarías para mil millones de réplicas?
- Si publicás la semilla de un sorteo con LCG, ¿puede un participante calcular el ganador antes del sorteo?
Ver respuestas sugeridas
- LCG repite exacto; crypto cambia siempre porque ignora tu semilla y bebe entropía del sistema.
- Crypto suele ser más lento (llamada al sistema): para simulación masiva es un despilfarro; para un token el costo es irrelevante.
- Sí: con la fórmula y la semilla reconstruye todo. Los sorteos con incentivo van con CSPRNG y auditoría, nunca con semilla pública.
07 · Comprensión
Confusiones frecuentes
«random es seguro porque nadie sabe mi semilla»
Seguridad por oscuridad: con salidas observadas se reconstruye el estado (MT con 624 valores, LCG con 2–3). La semilla se filtra en logs, repos y backups.
«Uso crypto para la simulación, así es de máxima calidad»
Pagás lentitud y perdés reproducibilidad y streams paralelos. Calidad estadística ya la dan PCG/Philox; seguridad no la necesitás contra la naturaleza.
«Math.random es criptográfico»
No: en navegadores es un PRNG rápido (xorshift128+ y variantes según motor), sin garantía de imprevisibilidad. Para tokens en JS usá crypto.getRandomValues.
«Hago rand() % n y listo»
El módulo introduce sesgo si n no divide al rango (Tema 35). Para sorteo justo usá secrets.randbelow(n), que hace rechazo interno sin sesgo.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: token de sesión
Generá dos tokens URL-safe de 32 bytes y verificá que son distintos. ¿Por qué no fijarías semilla aquí?
import secrets
a = secrets.token_urlsafe(32)
b = secrets.token_urlsafe(32)
print(a)
print(b)
print(a != b)
Ver solución razonada
Debe dar True (colisión imposible en la práctica). Fijar semilla destruiría la gracia: cualquiera repetiría tus “secretos”. secrets ni ofrece seed() a propósito.
Ejercicio 2: el sorteo justo
Sorteá un ganador entre 1000 participantes sin sesgo módulo. Repetilo 3 veces.
import secrets
for _ in range(3):
print(secrets.randbelow(1000))
Ver solución
Tres números distintos casi seguro, cada uno con probabilidad 1/1000. Con rand() % 1000 los valores bajos tendrían una milésima extra según el rango base.
Ejercicio 3: clonar un LCG con 3 datos
Observá 3 salidas de un LCG conocido (a=1103515245, c=12345, m=2**31) y predecí la cuarta resolviendo el estado. ¿Qué prueba esto sobre random para claves?
Ver una posible respuesta
a, c, m = 1103515245, 12345, 2**31
x = 42
obs = []
for _ in range(3):
x = (a * x + c) % m
obs.append(x / m)
print([round(v, 6) for v in obs])
# atacante que ve obs y conoce a, c, m recupera x y predice:
x_next = (a * round(obs[-1] * m) + c) % m
print("predicho:", round(x_next / m, 6))
Si un LCG de juguete se clona con 1 dato, MT se clona con 624: ningún PRNG de simulación resiste a un observador. Para secreto, CSPRNG o nada.
09 · Síntesis
Ideas para recordar
- PRNG = velocidad + reproducibilidad; CSPRNG = imprevisibilidad + entropía.
- MT/PCG/Philox se clonan observando salidas: prohibidos para claves y sorteos con incentivo.
- En Python:
random/NumPy para simular,secrets/os.urandompara lo secreto. - En JS:
Math.randompara demos,crypto.getRandomValuespara tokens. - Evitar
% nen sorteos: usarrandbelowsin sesgo.
En el próximo tema volvemos al cauce de simulación: generación de números uniformes, de enteros del generador a U(0,1).