Cifrado WebAssembly: velocidad cripto casi nativa en el navegador
Usa WebAssembly para velocidades de cifrado casi nativas. Compara implementaciones WASM crypto y aprende cuándo usar WASM sobre JavaScript puro.
Las bibliotecas cripto compiladas a WASM funcionan en el navegador al 60-80% de la velocidad nativa en C, lo que supone 3-10 veces más rápido que las implementaciones en JavaScript puro. Para primitivos de cifrado ausentes en la Web Crypto API (ChaCha20-Poly1305, Argon2id, XChaCha20, KEMs post-cuánticos como Kyber), WASM es el camino práctico hacia un rendimiento utilizable. libsodium.js es la opción dominante, proporcionando la API completa de libsodium con un binario WASM de 200 KB. Para primitivos que Web Crypto ya admite, como AES-256-GCM, la implementación nativa del navegador con aceleración hardware supera con claridad a WASM, por lo que WASM no siempre es la herramienta adecuada. Esta guía cubre cuándo usarlo y cómo comparar los compromisos.
Cuándo gana la Web Crypto nativa
Para los algoritmos que Web Crypto ya expone — AES-GCM, AES-CBC, AES-CTR, PBKDF2, HMAC, RSA-OAEP, ECDH, ECDSA, SHA-256/384/512 — la implementación nativa del navegador usa aceleración hardware mediante AES-NI en x86 y extensiones cripto ARMv8 en móvil. Rendimiento típico de AES-256-GCM:
- Web Crypto (Chrome en Apple Silicon): 1,7 GB/s
- Web Crypto (Chrome en Intel x86 con AES-NI): 1,2 GB/s
- libsodium WASM AES-GCM: 400-800 MB/s
- OpenSSL nativo de referencia: 3-5 GB/s
WASM no tiene acceso a AES-NI desde dentro del sandbox; recurre a implementaciones AES en bitslicing que son más lentas pero en tiempo constante. Para todo lo que Web Crypto admite, úsalo primero.
Dónde gana WASM
Para algoritmos ausentes en Web Crypto:
- ChaCha20-Poly1305: más rápido que AES en dispositivos sin AES-NI. Sin soporte nativo en navegadores en 2026.
- XChaCha20-Poly1305: los nonces de 192 bits hacen que el uso de nonces aleatorios sea seguro a cualquier escala.
- Argon2id: la función de hash de contraseñas ganadora del PHC. Sin soporte nativo en navegadores.
- X25519 / Ed25519: Safari 17 y Firefox 129 añadieron soporte nativo, pero WASM sigue siendo el camino portable.
- BLAKE2b / BLAKE3: funciones hash rápidas no incluidas en Web Crypto.
- Kyber, Dilithium, SPHINCS+: primitivos post-cuánticos; territorio de bibliotecas exclusivamente.
- AEAD en streaming:
crypto_secretstreamde libsodium gestiona el cifrado en streaming de archivos grandes de forma limpia; Web Crypto no tiene equivalente.
Para estos, existen implementaciones en JavaScript pero son 5-20 veces más lentas que WASM. En un archivo de 1 GB con ChaCha20-Poly1305, WASM tarda ~2 segundos mientras JS puro tarda 15-40 segundos.
libsodium.js: el caballo de batalla
libsodium-wrappers (la libsodium compilada con Emscripten y envolturas JS) es la biblioteca de referencia. 200 KB de WASM + ~50 KB de envoltura JS, comprimidos con gzip.
import _sodium from 'libsodium-wrappers';
await _sodium.ready;
const sodium = _sodium;
// Cifrado autenticado ChaCha20-Poly1305
const key = sodium.crypto_aead_xchacha20poly1305_ietf_keygen();
const nonce = sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
const ciphertext = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
plaintext, null, null, nonce, key
);
La compilación "sumo" (incluye más algoritmos) tiene ~600 KB; la compilación predeterminada cubre el 90% de los casos de uso, incluidos AEAD, hash de contraseñas (Argon2id), X25519 y Ed25519.
Cárgala dinámicamente para que el binario WASM no bloquee el renderizado inicial de la página:
async function getSodium() {
if (!window._sodium) {
const mod = await import('libsodium-wrappers');
await mod.ready;
window._sodium = mod;
}
return window._sodium;
}
AEAD en streaming mediante crypto_secretstream
Para la transferencia de archivos grandes, crypto_secretstream_xchacha20poly1305 de libsodium es el AEAD en streaming más limpio en el navegador:
const { state, header } = sodium.crypto_secretstream_xchacha20poly1305_init_push(key);
const chunk1 = sodium.crypto_secretstream_xchacha20poly1305_push(
state, plaintext1, null,
sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
);
// El último fragmento usa TAG_FINAL para que el receptor detecte truncamientos
const chunkLast = sodium.crypto_secretstream_xchacha20poly1305_push(
state, plaintextLast, null,
sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
);
El marcador TAG_FINAL permite al receptor detectar ataques de truncamiento — si un atacante elimina los fragmentos finales, el descifrado en el cliente falla. AES-GCM de Web Crypto no tiene esta propiedad; tendrías que construir la detección de truncamiento tú mismo (recuento de fragmentos en AAD, o un hash total del archivo verificado tras el descifrado).
Argon2 en el navegador
Para la derivación de claves basada en contraseña en el navegador, Argon2id mediante WASM es la mejor práctica en 2026. Opciones:
- argon2-browser: biblioteca WASM dedicada a Argon2, ~200 KB
- libsodium.js: Argon2id mediante
crypto_pwhash, si ya usas libsodium - @noble/hashes: Argon2id en JS puro, ~15 KB de bundle, 3-5 veces más lento que WASM
Ejemplo con argon2-browser:
import argon2 from 'argon2-browser';
const result = await argon2.hash({
pass: password,
salt: salt, // Uint8Array, 16+ bytes
type: argon2.ArgonType.Argon2id,
time: 3,
mem: 65536, // KiB, es decir, 64 MiB
parallelism: 4,
hashLen: 32,
});
// result.hash es un Uint8Array que puedes usar como clave AES-256
Calibra los parámetros según la espera que acepta el usuario objetivo. La línea base OWASP 2024 (m=19 MiB, t=2, p=1) tarda ~300-500 ms. Más fuerte (m=64 MiB, t=3, p=4) tarda ~1-2 segundos en hardware moderno.
Cripto post-cuántica mediante WASM
Kyber (encapsulación de claves) y Dilithium (firmas) fueron estandarizados por NIST en 2024 como ML-KEM y ML-DSA. Existen implementaciones JavaScript (pq-crystals tiene una referencia), pero las versiones WASM como liboqs-js son más rápidas y más auditables.
Para la transferencia de archivos, los KEMs post-cuánticos permiten envolver una clave simétrica de archivo con un primitivo de clave pública resistente a cuántica. Los esquemas híbridos (ML-KEM + X25519) protegen contra atacantes tanto clásicos como cuánticos futuros. Chrome incluyó ML-KEM en los handshakes TLS en la versión 116 (2023), pero WASM a nivel de aplicación sigue siendo el camino para las claves de contenido de archivos.
Consideraciones sobre el tamaño del bundle
Los binarios WASM se incluyen en tu bundle JS o se obtienen de forma diferida. Tamaños aproximados (comprimidos con gzip):
- libsodium.js por defecto: 200 KB
- libsodium.js sumo: 600 KB
- argon2-browser: 200 KB
- liboqs-js (post-cuántico): 1 MB+
Para una página de destino que cifra en el navegador, 200-300 KB de WASM es tolerable si se carga de forma diferida tras la interacción del usuario. Para una aplicación que hace del cifrado su función principal, incluir WASM en la carga inicial es razonable. Usa sugerencias rel="modulepreload" o caché de service worker para que las cargas posteriores sean instantáneas.
Comprueba el panel de red en las herramientas de desarrollo del navegador para confirmar que el binario WASM se almacena en caché tras la primera carga. Las cabeceras de caché mal configuradas pueden provocar re-descargas en cada visita.
Sobrecarga de compilación e instanciación
WebAssembly.instantiate() analiza y compila el binario, lo que tarda 20-100 ms para un módulo de 200 KB en escritorio y 100-500 ms en móvil. Esto ocurre una vez por sesión. Almacena en caché el módulo compilado en IndexedDB mediante la serialización de WebAssembly.Module para cargas posteriores más rápidas.
WebAssembly.instantiateStreaming() paraleliza la descarga y la compilación, ahorrando el 30-50% del tiempo de inicio comparado con instantiate() sobre una respuesta obtenida:
const response = fetch('/sodium.wasm');
const { instance } = await WebAssembly.instantiateStreaming(response, importObject);
Problemas de gestión de memoria
Los módulos WASM asignan memoria en páginas lineales (64 KiB cada una). libsodium comienza con poca memoria inicial y crece según sea necesario, pero el crecimiento ilimitado puede alcanzar los límites del navegador (típicamente un límite de memoria lineal de 4 GB en WASM de 32 bits). Para cifrado de archivos de varios GB:
- Haz pasar fragmentos a través del módulo WASM en streaming, no cargues el archivo completo en memoria WASM
- Libera buffers tras cada fragmento mediante
sodium.memzeroo poniendo referencias a null - Monitoriza
WebAssembly.Memory.buffer.byteLengthen sesiones de larga duración
Memory64 (propuesto, parcialmente implementado en Chrome) elimina el límite de 4 GB. Por ahora, el procesamiento por fragmentos es el camino fiable.
Consideraciones de seguridad específicas de WASM
WASM se ejecuta en el mismo origen que la página que lo cargó, heredando las mismas reglas de CSP y CORS. Si un atacante inyecta código en tu origen (XSS), puede llamar a las exportaciones de tu módulo WASM. WASM no proporciona una barrera de seguridad adicional.
Garantías de tiempo constante: la mayoría de las bibliotecas cripto compiladas a WASM conservan las propiedades de tiempo constante del código fuente C, pero la traducción de WASM a JIT puede introducir código de tiempo variable en algunas plataformas. Los registros de auditoría de libsodium señalan algunas preocupaciones de tiempo constante específicas de WASM. Para la mayoría de los modelos de amenaza esto es insignificante; para la defensa contra atacantes locales con mediciones de temporización precisas, considera la diferencia.
La receta práctica
Para una aplicación de transferencia de archivos en 2026:
- Usa Web Crypto para AES-256-GCM, PBKDF2, HMAC, SHA-256, RSA-OAEP si necesitas RSA
- Usa libsodium.js vía WASM para el hash de contraseñas Argon2id
- Usa libsodium.js para el cifrado en streaming de archivos grandes (
crypto_secretstream) - Usa Ed25519/X25519 nativo donde esté disponible, recurre a libsodium en caso contrario
- Carga WASM de forma diferida tras la interacción del usuario para mantener ligero el bundle inicial
- Haz benchmarks en los dispositivos objetivo; no asumas que los números de escritorio se aplican al móvil
HexaTransfer se mantiene en AES-GCM de Web Crypto para el cifrado masivo de archivos porque el camino acelerado por hardware es el más rápido. WASM se reserva para los algoritmos que las APIs nativas no pueden ofrecer.
Pruébalo en hexatransfer.com — gratis, sin cuenta, hasta 10 GB.
Envía archivos grandes de forma segura con cifrado de extremo a extremo
Transfiere archivos de hasta 10 GB gratis con cifrado de extremo a extremo. Sin necesidad de cuenta. Tus archivos se cifran en tu navegador antes de subirlos: nadie más puede leerlos.
Enviar un archivo