Ir para o conteúdo
HexaTransfer
Voltar ao blog
Criptografia e seguranca

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_secretstream da 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.memzero ou por anulação de referências
  • Monitorize WebAssembly.Memory.buffer.byteLength em 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