- 0x710 — Teoría de la información
- 0x720 — Tiempo de ejecución (notación O)
- 0x730 — Cifrado simétrico
- 0x740 — Cifrado asimétrico
- 0x750 — Cifrado híbrido
- 0x760 — Cracking de contraseñas
- 0x770 — Cifrado Wi-Fi 802.11b
- 0x780 — Ataques a WEP
Tres conceptos que dan forma a toda la criptografía:
- Confidencialidad — solo el destinatario lee el mensaje.
- Integridad — el mensaje no ha sido modificado.
- Autenticación — el remitente es quien dice ser.
Una primitiva no garantiza las tres. AES-CBC da confidencialidad pero no integridad → necesitas un MAC (HMAC) encima. AES-GCM las da las dos en una sola operación. AEAD (Authenticated Encryption with Associated Data) es el estándar moderno: AES-GCM, ChaCha20-Poly1305.
H(X) = -Σ p(x) · log₂(p(x)) — bits de incertidumbre. Una password "password" tiene entropía 0 (todo el mundo la conoce). Una password de 12 caracteres aleatorios [a-zA-Z0-9] tiene 12 · log₂(62) ≈ 71 bits.
Para keys criptográficas: necesitas al menos 128 bits de entropía real para AES-128, 256 para AES-256. Sacarlo de /dev/urandom o getrandom() — nunca de rand() ni de timestamps.
Un buen cifrado tiene:
- Confusión: la relación entre key y ciphertext es complicada. Cambiar 1 bit de key cambia ~la mitad de los bits del ciphertext.
- Difusión: un cambio en 1 bit de plaintext cambia ~la mitad de los bits del ciphertext (avalancha).
AES cumple ambas tras 10/12/14 rondas. Si un cifrado nuevo no muestra avalancha → no es seguro.
La fortaleza de un cifrado se mide en cuántas operaciones tarda romperlo. Notación O resume el crecimiento:
| Notación | Significado | Ejemplo |
|---|---|---|
| O(1) | constante | hash lookup |
| O(log n) | logarítmica | búsqueda binaria |
| O(n) | lineal | recorrer un array |
| O(n log n) | linearítmica | merge sort |
| O(n²) | cuadrática | bubble sort |
| O(2ⁿ) | exponencial | brute force key |
| O(n!) | factorial | TSP por fuerza bruta |
Una key de 128 bits implica 2¹²⁸ posibilidades. A 1 billón de pruebas/segundo (10¹²), tarda:
2^128 / 10^12 / 60 / 60 / 24 / 365 = 1.08 × 10^19 años
Por eso AES-128 se considera seguro contra fuerza bruta clásica. Cuántica cambia el cálculo: Grover lo reduce a 2⁶⁴ → AES-256 sí, AES-128 dudoso a 30+ años vista.
codigo/0x720/timing.c mide cuánto tarda una búsqueda lineal vs binaria sobre arrays de tamaños crecientes.
Misma key para cifrar y descifrar. Rápido, pero el reto es distribuir la key sin que un tercero la vea.
| Algoritmo | Estado | Notas |
|---|---|---|
| DES | Roto | 56 bits, brute-forceable en horas. Sólo legacy. |
| 3DES | Deprecado | 112 bits efectivos, lento. |
| AES | Estándar | 128/192/256 bits. |
| ChaCha20 | Moderno | Stream cipher, rápido en CPU sin AES-NI, usado en TLS 1.3. |
| Blowfish/Twofish | Legacy | Reemplazados por AES. |
| RC4 | Roto | Sesgos en los primeros bytes; no usar. |
AES procesa bloques de 16 bytes. Para datos más largos hay que combinar bloques con un modo:
| Modo | Cómo | Problema |
|---|---|---|
| ECB | Cada bloque cifrado independientemente | bloques iguales → ciphertext igual (ECB penguin) |
| CBC | Cada bloque XOR con el anterior antes de cifrar | requiere IV; padding oracle attacks |
| CTR | Convierte block cipher en stream cipher | sin integridad → necesita MAC |
| GCM | CTR + GHASH (autenticación) | AEAD; el modo recomendado |
| OCB | AEAD eficiente | menos extendido, patentes históricas |
Regla práctica: usa AES-256-GCM o ChaCha20-Poly1305. Cualquier otra cosa requiere justificación.
Un valor único por mensaje que evita que cifrar el mismo plaintext con la misma key produzca el mismo ciphertext. Reglas:
- Para CBC: IV aleatorio, se transmite en claro junto al ciphertext.
- Para CTR/GCM: IV/nonce único pero no necesariamente secreto. Reusar nonce con la misma key en GCM = catástrofe (recuperación de la key de autenticación).
Implementación de AES-256-CBC y AES-256-GCM con OpenSSL: codigo/0x730/aes_cbc.c, codigo/0x730/aes_gcm.c.
Cada parte tiene un par de keys: pública (se comparte) y privada (se guarda). Lo cifrado con la pública solo lo descifra la privada (y viceversa). Resuelve el problema de distribución de keys que tiene la simétrica.
| Algoritmo | Para qué | Tamaño típico |
|---|---|---|
| RSA | Cifrado, firma | 2048 / 3072 / 4096 bits |
| DH (Diffie-Hellman) | Acuerdo de key | mismo orden que RSA |
| ECDH | DH sobre curvas elípticas | 256 / 384 bits → equivale a RSA 3072+ |
| ECDSA / Ed25519 | Firma | 256 bits |
Asimétrica es mucho más lenta que simétrica (factores 100x-1000x). Por eso nunca se usa para datos largos.
- Elegir dos primos grandes
p,q. n = p · q. φ(n) = (p-1)(q-1).- Elegir
ecoprimo con φ(n) (típicamentee = 65537). - Calcular
d = e^-1 mod φ(n). Pública =(n, e). Privada =(n, d).
Cifrar: c = m^e mod n. Descifrar: m = c^d mod n. La seguridad depende de que factorizar n sea difícil.
- e = 3 sin padding (textbook RSA): factible recuperar plaintext si
m^3 < n. - Padding PKCS#1 v1.5 → padding oracle (Bleichenbacher). Solucionado con OAEP.
- Primos predecibles porque el RNG falló (Heartbleed-era keys).
- Misma
nreutilizada entre dispositivos: GCD entre losnrevela primos compartidos.
Para cualquier código nuevo: OAEP para cifrar, PSS para firmar, o mejor aún Ed25519/X25519.
Lo que usa todo el mundo (TLS, Signal, PGP, age):
- Generar key simétrica aleatoria K.
- Cifrar el mensaje con K usando AES-GCM (rápido).
- Cifrar K con la pública RSA del destinatario (lento, pero K es solo 32 bytes).
- Enviar (K cifrada, mensaje cifrado).
El destinatario descifra K con su privada y luego descifra el mensaje con K.
TLS 1.3 hace esto con un giro: usa ECDHE (Diffie-Hellman efímero sobre curvas elípticas) para acordar la K, y los certificados RSA/ECDSA solo para firmar (no para cifrar K). Esto da forward secrecy: aunque alguien capture el tráfico hoy y robe la privada del servidor mañana, no puede descifrar las sesiones pasadas.
Las contraseñas no se almacenan en claro. Se almacenan como hash + salt, opcionalmente con factor de coste (PBKDF2, bcrypt, scrypt, Argon2).
$id$rounds=N$salt$hash
id |
Algoritmo | Estado |
|---|---|---|
1 |
MD5-crypt | inseguro |
5 |
SHA-256-crypt | aceptable |
6 |
SHA-512-crypt | aceptable, default en muchas distros |
2a/2b/2y |
bcrypt | bueno |
argon2id |
Argon2id | el recomendado desde 2016 |
- John the Ripper — formato auto-detectado, muy completo.
john --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt john --show hashes.txt
- hashcat — GPU, mucho más rápido para hashes paralelizables.
hashcat -m 1800 hashes.txt rockyou.txt # SHA-512-crypt hashcat -m 3200 hashes.txt rockyou.txt # bcrypt
- Hash-Identifier / hashid / haiti — identificar formato.
- rockyou.txt (
/usr/share/wordlists/rockyou.txt.gzen Kali) — 14M passwords reales filtradas. - SecLists (https://github.com/danielmiessler/SecLists) — colecciones especializadas.
- Generadas a medida con
cewl(scrapea palabras de un sitio web),crunch,maskprocessor.
| Ataque | Cómo |
|---|---|
| Diccionario | hash(palabra) por cada palabra del wordlist |
| Diccionario + reglas | aplica mutaciones (P@ssw0rd!, password123...) |
| Máscara | brute force estructurado: ?l?l?l?l?d?d |
| Híbrido | wordlist + máscara concatenados |
| Rainbow tables | precomputado, ya no son útiles si hay salt |
codigo/0x760/crack_demo.c — implementación didáctica de un cracker de SHA-256 sin sal contra un wordlist en memoria. No usar en producción, es ilustrativo.
| Esquema | Año | Estado |
|---|---|---|
| WEP | 1997 | Roto desde 2001. Recuperable en minutos. |
| WPA (TKIP) | 2003 | Roto parcialmente (Beck-Tews); evitable. |
| WPA2 (CCMP/AES) | 2004 | Roto el handshake (KRACK 2017), pero limitado. |
| WPA3 (SAE) | 2018 | Estado del arte; fallo Dragonblood 2019 (parcheado). |
WEP es del libro. WPA2 con SAE/PSK fuerte sigue siendo lo mínimo aceptable hoy.
sudo airmon-ng start wlan0 # modo monitor
sudo airodump-ng -c 6 --bssid XX:XX:XX:XX:XX:XX -w cap wlan0mon
sudo aireplay-ng -0 5 -a XX:XX:XX:XX:XX:XX wlan0mon # deauth para forzar reconnect
hashcat -m 22000 cap.hccapx rockyou.txt # crack offlineWPA2-PSK con password fuerte (>20 chars random) es brute-force-impracticable. PSK del operador típica (12 chars de la pegatina del router) → crackeable en horas con GPU.
WEP usa RC4 con una key de 40 o 104 bits + IV de 24 bits.
- IV reusado. Solo 2²⁴ ≈ 16M posibilidades. En una red activa se reciclan en horas, dando dos ciphertexts con la misma keystream → XOR de ambos elimina la keystream.
- Sesgo de RC4 (FMS attack). Algunos IVs ("weak IVs") revelan info sobre la key.
- PTW attack (2007): recupera key de 104 bits con ~40000 paquetes capturados. Minutos en una red con tráfico.
sudo airmon-ng start wlan0
sudo airodump-ng -c <ch> --bssid <bssid> -w wep wlan0mon # captura
sudo aireplay-ng -3 -b <bssid> -h <mac_atac> wlan0mon # ARP replay para generar tráfico
aircrack-ng -b <bssid> wep-01.cap # crackingWEP no se ve casi nunca en 2025 — pero hay routers olvidados en pisos viejos y vending machines. Si lo encuentras es legacy puro y debe migrarse.
- No inventes algoritmos.
- No inventes modos.
- No inventes integraciones.
- Usa libsodium o el wrapper de tu lenguaje (Go:
crypto/, Python:cryptography, Rust:ring/rustcrypto). Toda implementación nueva en C es donde aparecen los bugs.
codigo/0x740/rsa_demo.c muestra RSA con OpenSSL EVP, codigo/0x730/aes_gcm.c muestra AES-256-GCM. Ambos usan APIs modernas de OpenSSL 3.
Siguiente: 0x800 — Conclusión.