Encriptação com WebAssembly: velocidade quase nativa no navegador
Use WebAssembly para atingir velocidades de encriptação quase nativas no navegador. Compare implementações WASM de criptografia e saiba quando usar WASM em vez de JavaScript puro.
As bibliotecas de criptografia compiladas para WASM correm a cerca de 60-80% da velocidade nativa em C no navegador, o que é 3-10x mais rápido que as implementações em JavaScript puro. Para primitivos de encriptação que faltam na Web Crypto API (ChaCha20-Poly1305, Argon2id, XChaCha20, KEMs pós-quânticos como Kyber), o WASM é o caminho prático para um desempenho utilizável. A libsodium.js é a opção dominante, fornecendo a API libsodium completa com um binário WASM de 200 KB. Para primitivos suportados pela Web Crypto como AES-256-GCM, a implementação nativa com aceleração hardware do navegador supera facilmente o WASM, pelo que o WASM nem sempre é a ferramenta certa. Este guia cobre quando recorrer a ele e como comparar as trocas.
Quando a Web Crypto nativa ganha
Para algoritmos que a Web Crypto já expõe — AES-GCM, AES-CBC, AES-CTR, PBKDF2, HMAC, RSA-OAEP, ECDH, ECDSA, SHA-256/384/512 — a implementação nativa do navegador usa aceleração hardware via AES-NI em x86 e extensões crypto ARMv8 em dispositivos móveis. Débito típico AES-256-GCM:
- Web Crypto (Chrome em Apple Silicon): 1,7 GB/s
- Web Crypto (Chrome em Intel x86 com AES-NI): 1,2 GB/s
- libsodium WASM AES-GCM: 400-800 MB/s
- OpenSSL nativo de referência: 3-5 GB/s
O WASM não tem acesso a AES-NI dentro da sandbox; recai em implementações AES bitsliced que são mais lentas mas em tempo constante. Para qualquer coisa que a Web Crypto suporte, use-a primeiro.
Onde o WASM ganha
Para algoritmos que faltam na Web Crypto:
- ChaCha20-Poly1305: mais rápido que AES em dispositivos sem AES-NI. Sem suporte nativo no navegador em 2026.
- XChaCha20-Poly1305: nonces de 192 bits tornam o uso de nonces aleatórios seguro em qualquer escala.
- Argon2id: a função de hash de palavras-passe vencedora do PHC. Sem suporte nativo no navegador.
- X25519 / Ed25519: Safari 17 e Firefox 129 adicionaram suporte nativo, mas o WASM ainda é o caminho portátil.
- BLAKE2b / BLAKE3: funções de hash rápidas não presentes na Web Crypto.
- Kyber, Dilithium, SPHINCS+: primitivos pós-quânticos; território de bibliotecas.
- AEAD em streaming: o
crypto_secretstreamda libsodium trata a encriptação de streaming de ficheiros grandes de forma limpa; a Web Crypto não tem equivalente.
Para estes, existem implementações JavaScript mas são 5-20x mais lentas que WASM. Num ficheiro de 1 GB com ChaCha20-Poly1305, o WASM corre em ~2 segundos enquanto o JS puro demora 15-40 segundos.
libsodium.js: o motor de trabalho
A libsodium-wrappers (a libsodium compilada com Emscripten com wrappers JS) é a biblioteca de referência. 200 KB WASM + ~50 KB wrapper JS, comprimido.
import _sodium from 'libsodium-wrappers';
await _sodium.ready;
const sodium = _sodium;
// Encriptação autenticada 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
);
A build "sumo" (inclui mais algoritmos) tem ~600 KB; a build padrão cobre 90% dos casos de uso incluindo AEAD, hash de palavras-passe (Argon2id), X25519 e Ed25519.
Carregue dinamicamente para que o binário WASM não bloqueie o render inicial da página:
async function getSodium() {
if (!window._sodium) {
const mod = await import('libsodium-wrappers');
await mod.ready;
window._sodium = mod;
}
return window._sodium;
}
AEAD em streaming via crypto_secretstream
Para transferência de ficheiros grandes, o crypto_secretstream_xchacha20poly1305 da libsodium é o AEAD em streaming mais limpo no 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
);
// O último bloco usa TAG_FINAL para que o receptor detecte truncagem
const chunkLast = sodium.crypto_secretstream_xchacha20poly1305_push(
state, plaintextLast, null,
sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
);
O marcador TAG_FINAL permite ao receptor detectar ataques de truncagem — se um atacante eliminar blocos finais, a desencriptação no cliente falha. O AES-GCM da Web Crypto não tem esta propriedade; teria de construir a detecção de truncagem ele próprio (contagem de blocos no AAD, ou um hash global do ficheiro verificado após a desencriptação).
Argon2 no navegador
Para derivação de chaves baseada em palavra-passe no navegador, o Argon2id via WASM é a melhor prática em 2026. Opções:
- argon2-browser: biblioteca WASM dedicada a Argon2, ~200 KB
- libsodium.js: Argon2id via
crypto_pwhash, se já usa libsodium - @noble/hashes: Argon2id em JS puro, ~15 KB de bundle, 3-5x mais lento que WASM
Exemplo com 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, ou seja 64 MiB
parallelism: 4,
hashLen: 32,
});
// result.hash é um Uint8Array que pode usar como chave AES-256
Calibre os parâmetros para a espera alvo do utilizador. A linha de base OWASP 2024 (m=19 MiB, t=2, p=1) corre em ~300-500 ms. Mais forte (m=64 MiB, t=3, p=4) corre em ~1-2 segundos em hardware moderno.
Criptografia pós-quântica via WASM
O Kyber (encapsulamento de chaves) e o Dilithium (assinaturas) foram padronizados pelo NIST em 2024 como ML-KEM e ML-DSA. Existem implementações JavaScript (pq-crystals tem uma referência) mas as versões WASM como liboqs-js são mais rápidas e mais auditáveis.
Para transferência de ficheiros, os KEMs pós-quânticos permitem encapsular uma chave simétrica de ficheiro com um primitivo de chave pública seguro quanticamente. Os esquemas híbridos (ML-KEM + X25519) protegem contra atacantes clássicos e quânticos futuros. O Chrome incluiu ML-KEM em handshakes TLS na versão 116 (2023), mas o WASM ao nível da aplicação permanece o caminho para chaves de conteúdo de ficheiros.
Considerações sobre o tamanho do bundle
Os binários WASM são enviados como parte do seu bundle JS ou carregados preguiçosamente. Tamanhos aproximados (comprimidos):
- libsodium.js padrão: 200 KB
- libsodium.js sumo: 600 KB
- argon2-browser: 200 KB
- liboqs-js (pós-quântico): 1 MB+
Para uma landing page que encripta no navegador, 200-300 KB de WASM é tolerável se carregado preguiçosamente após interacção do utilizador. Para uma aplicação que faz encriptação como funcionalidade principal, enviar WASM no carregamento inicial é razoável. Use hints rel="modulepreload" ou cache de service worker para manter os carregamentos subsequentes instantâneos.
Verifique o painel de rede nas ferramentas de desenvolvimento do navegador para confirmar que o binário WASM está em cache após o primeiro carregamento. Headers de cache mal configurados podem causar re-downloads em cada visita.
Sobrecarga de compilação e instanciação
WebAssembly.instantiate() analisa e compila o binário, o que demora 20-100 ms para um módulo de 200 KB em desktop, 100-500 ms em dispositivos móveis. Isto acontece uma vez por sessão. Guarde o módulo compilado em IndexedDB via serialização WebAssembly.Module para carregamentos subsequentes mais rápidos.
WebAssembly.instantiateStreaming() encadeia download e compilação, poupando 30-50% do tempo de arranque comparado com instantiate() numa resposta obtida:
const response = fetch('/sodium.wasm');
const { instance } = await WebAssembly.instantiateStreaming(response, importObject);
Armadilhas na gestão de memória
Os módulos WASM alocam memória em páginas lineares de 64 KiB. A libsodium começa com memória inicial pequena e cresce conforme necessário, mas o crescimento ilimitado pode atingir os limites do navegador (tipicamente 4 GB de cap de memória linear em WASM de 32 bits). Para encriptação de ficheiros multi-GB:
- Processe blocos em streaming através do módulo WASM, não carregue o ficheiro completo em memória WASM
- Liberte buffers após cada bloco via
sodium.memzeroou por anulação de referências - Monitorize
WebAssembly.Memory.buffer.byteLengthem sessões longas
O Memory64 (proposto, parcialmente implementado no Chrome) remove o limite de 4 GB. Por agora, o processamento em blocos é o caminho fiável.
Considerações de segurança específicas do WASM
O WASM corre na mesma origem que a página que o carregou, herdando as mesmas regras de CSP e CORS. Se um atacante injectar código na sua origem (XSS), pode chamar os exports do seu módulo WASM. O WASM não fornece uma fronteira de segurança adicional.
Garantias de tempo constante: a maioria das bibliotecas de criptografia compiladas para WASM preserva as propriedades de tempo constante do código C de origem, mas a tradução WASM-para-JIT pode introduzir código de tempo variável em algumas plataformas. Os logs de auditoria da libsodium notam algumas preocupações de tempo constante específicas do WASM. Para a maioria dos modelos de ameaça isto é negligenciável; para defesa contra atacantes locais com medições de timing precisas, considere a diferença.
A receita prática
Para uma aplicação de transferência de ficheiros em 2026:
- Use Web Crypto para AES-256-GCM, PBKDF2, HMAC, SHA-256, RSA-OAEP se precisar de RSA
- Use libsodium.js via WASM para hash de palavras-passe Argon2id
- Use libsodium.js para encriptação em streaming de ficheiros grandes (
crypto_secretstream) - Use Ed25519/X25519 nativo onde suportado, recaia em libsodium caso contrário
- Carregue WASM preguiçosamente após interacção do utilizador para manter o bundle inicial leve
- Faça benchmarks em dispositivos alvo; não assuma que os números do desktop correspondem ao mobile
O HexaTransfer mantém-se na Web Crypto AES-GCM para a encriptação de ficheiros em massa porque o caminho com aceleração hardware é o mais rápido. O WASM fica reservado para algoritmos que as APIs nativas não conseguem fornecer.
Experimente em hexatransfer.com — gratuito, sem conta, até 10 GB.
Envie arquivos grandes com segurança e criptografia de ponta a ponta
Transfira arquivos de até 10 GB gratuitamente com criptografia de ponta a ponta. Sem necessidade de conta. Seus arquivos são criptografados no navegador antes do envio — ninguém mais pode lê-los.
Enviar um arquivo