Aller au contenu
HexaTransfer
Retour au blog
Chiffrement et securite

Chiffrement WebAssembly : vitesse crypto quasi native dans le navigateur

Utilisez WebAssembly pour atteindre des vitesses quasi natives dans le navigateur. Comparez les implémentations WASM crypto.

Les bibliothèques crypto compilées en WASM atteignent environ 60 à 80 % de la vitesse d'un programme natif en C dans le navigateur, soit 3 à 10 fois plus vite que les implémentations JavaScript pures. Pour les primitives de chiffrement absentes de la Web Crypto API — ChaCha20-Poly1305, Argon2id, XChaCha20, les KEMs post-quantiques comme Kyber — WASM est la voie réaliste vers des performances utilisables. libsodium.js est la solution dominante : elle fournit l'API libsodium complète dans un binaire WASM de 200 Ko. Pour les primitives déjà supportées nativement comme AES-256-GCM, l'implémentation matérielle du navigateur bat largement le WASM, donc ce dernier n'est pas toujours le bon outil. Ce guide explique quand l'utiliser et comment mesurer les compromis.

Quand la Web Crypto native l'emporte

Pour les algorithmes déjà exposés par la Web Crypto — AES-GCM, AES-CBC, AES-CTR, PBKDF2, HMAC, RSA-OAEP, ECDH, ECDSA, SHA-256/384/512 — l'implémentation native du navigateur bénéficie de l'accélération matérielle via AES-NI sur x86 et les extensions crypto ARMv8 sur mobile. Débit typique pour AES-256-GCM :

  • Web Crypto (Chrome sur Apple Silicon) : 1,7 Go/s
  • Web Crypto (Chrome sur Intel x86 avec AES-NI) : 1,2 Go/s
  • libsodium WASM AES-GCM : 400–800 Mo/s
  • Référence OpenSSL natif : 3–5 Go/s

Le WASM n'a pas accès à AES-NI depuis l'intérieur du bac à sable ; il retombe sur des implémentations AES en bitslice, plus lentes mais à temps constant. Pour tout ce que la Web Crypto supporte, utilisez-la en priorité.

Là où le WASM gagne

Pour les algorithmes absents de la Web Crypto :

  • ChaCha20-Poly1305 : plus rapide qu'AES sur les appareils sans AES-NI. Aucun support natif dans les navigateurs en 2026.
  • XChaCha20-Poly1305 : des nonces de 192 bits rendent l'usage de nonces aléatoires sûr à toute échelle.
  • Argon2id : la fonction de hachage de mots de passe gagnante du PHC. Aucun support natif.
  • X25519 / Ed25519 : Safari 17 et Firefox 129 ont ajouté le support natif, mais WASM reste la voie portable.
  • BLAKE2b / BLAKE3 : fonctions de hachage rapides absentes de la Web Crypto.
  • Kyber, Dilithium, SPHINCS+ : primitives post-quantiques ; domaine réservé aux bibliothèques.
  • AEAD en flux : crypto_secretstream de libsodium gère proprement le chiffrement en flux pour les gros fichiers ; la Web Crypto n'a pas d'équivalent.

Pour ces cas, des implémentations JavaScript existent mais sont 5 à 20 fois plus lentes que WASM. Sur un fichier de 1 Go avec ChaCha20-Poly1305, WASM s'exécute en environ 2 secondes, là où du JS pur prend 15 à 40 secondes.

libsodium.js : le moteur de travail

libsodium-wrappers (libsodium compilé avec Emscripten et des wrappers JS) est la bibliothèque de référence. 200 Ko WASM + ~50 Ko de wrapper JS, compressés en gzip.

import _sodium from 'libsodium-wrappers';
await _sodium.ready;
const sodium = _sodium;

// Chiffrement authentifié 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
);

Le build « sumo » (avec plus d'algorithmes) fait ~600 Ko ; le build par défaut couvre 90 % des cas incluant AEAD, hachage de mots de passe (Argon2id), X25519 et Ed25519.

Chargez dynamiquement pour que le binaire WASM ne bloque pas le rendu initial de la page :

async function getSodium() {
  if (!window._sodium) {
    const mod = await import('libsodium-wrappers');
    await mod.ready;
    window._sodium = mod;
  }
  return window._sodium;
}

AEAD en flux via crypto_secretstream

Pour le transfert de gros fichiers, crypto_secretstream_xchacha20poly1305 de libsodium est l'AEAD en flux le plus propre disponible dans le navigateur :

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
);
// Le dernier fragment utilise TAG_FINAL pour que le destinataire détecte les troncatures
const chunkLast = sodium.crypto_secretstream_xchacha20poly1305_push(
  state, plaintextLast, null,
  sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
);

Le marqueur TAG_FINAL permet au destinataire de détecter les attaques par troncature — si un attaquant supprime des fragments de fin, le déchiffrement échoue côté client. AES-GCM de la Web Crypto n'a pas cette propriété ; vous devriez construire vous-même la détection de troncature (comptage de fragments dans l'AAD, ou hash global vérifié après déchiffrement).

Argon2 dans le navigateur

Pour la dérivation de clé à partir d'un mot de passe dans le navigateur, Argon2id via WASM est la meilleure pratique en 2026. Options :

  • argon2-browser : bibliothèque WASM Argon2 dédiée, ~200 Ko
  • libsodium.js : Argon2id via crypto_pwhash, si vous utilisez déjà libsodium
  • @noble/hashes : Argon2id en JS pur, ~15 Ko de bundle, 3 à 5 fois plus lent que WASM

Exemple avec argon2-browser :

import argon2 from 'argon2-browser';
const result = await argon2.hash({
  pass: password,
  salt: salt, // Uint8Array, 16+ octets
  type: argon2.ArgonType.Argon2id,
  time: 3,
  mem: 65536, // Kio, soit 64 Mio
  parallelism: 4,
  hashLen: 32,
});
// result.hash est un Uint8Array utilisable comme clé AES-256

Calibrez les paramètres selon le temps d'attente acceptable pour vos utilisateurs. La référence OWASP 2024 (m=19 Mio, t=2, p=1) s'exécute en ~300–500 ms. Un réglage plus robuste (m=64 Mio, t=3, p=4) prend ~1–2 secondes sur du matériel récent.

Crypto post-quantique via WASM

Kyber (encapsulation de clé) et Dilithium (signatures) ont été standardisés par le NIST en 2024 sous les noms ML-KEM et ML-DSA. Des implémentations JavaScript existent (pq-crystals dispose d'une référence), mais les versions WASM comme liboqs-js sont plus rapides et plus auditables.

Pour le transfert de fichiers, les KEMs post-quantiques permettent d'encapsuler une clé symétrique avec une primitive à clé publique résistante aux quantiques. Les schémas hybrides (ML-KEM + X25519) protègent à la fois contre les attaquants classiques et les futurs attaquants quantiques. Chrome a intégré ML-KEM dans les poignées de main TLS à partir de la version 116 (2023), mais le WASM applicatif reste la voie pour les clés de contenu de fichiers.

Considérations sur la taille du bundle

Les binaires WASM s'expédient avec votre bundle JS ou sont chargés paresseusement. Tailles approximatives (gzip) :

  • libsodium.js par défaut : 200 Ko
  • libsodium.js sumo : 600 Ko
  • argon2-browser : 200 Ko
  • liboqs-js (post-quantique) : 1 Mo+

Pour une page de destination qui chiffre dans le navigateur, 200–300 Ko de WASM est tolérable si chargé après une interaction utilisateur. Pour une application dont le chiffrement est la fonctionnalité principale, intégrer le WASM au chargement initial est raisonnable. Utilisez des hints rel="modulepreload" ou le cache du service worker pour rendre les chargements ultérieurs instantanés.

Vérifiez dans le panneau réseau des outils de développement que le binaire WASM est bien mis en cache après le premier chargement. Des en-têtes de cache mal configurés peuvent provoquer des re-téléchargements à chaque visite.

Surcharge de compilation et d'instanciation

WebAssembly.instantiate() parse et compile le binaire, ce qui prend 20–100 ms pour un module de 200 Ko sur desktop, et 100–500 ms sur mobile. Cela se produit une fois par session. Mettez en cache le module compilé dans IndexedDB via la sérialisation de WebAssembly.Module pour des chargements ultérieurs plus rapides.

WebAssembly.instantiateStreaming() pipeline le téléchargement et la compilation, économisant 30–50 % du temps de démarrage par rapport à instantiate() :

const response = fetch('/sodium.wasm');
const { instance } = await WebAssembly.instantiateStreaming(response, importObject);

Pièges de la gestion mémoire

Les modules WASM allouent de la mémoire en pages linéaires de 64 Kio. libsodium démarre avec une mémoire initiale faible et croît selon les besoins, mais une croissance non bornée peut atteindre les limites du navigateur (généralement 4 Go de mémoire linéaire en WASM 32 bits). Pour chiffrer des fichiers de plusieurs gigaoctets :

  • Faites passer les fragments par le module WASM, ne chargez pas le fichier entier en mémoire WASM
  • Libérez les buffers après chaque fragment via sodium.memzero ou en annulant les références
  • Surveillez WebAssembly.Memory.buffer.byteLength dans les sessions longues

Memory64 (proposé, partiellement implémenté dans Chrome) supprime la limite de 4 Go. En attendant, le traitement par fragments est la voie fiable.

Considérations de sécurité propres au WASM

Le WASM s'exécute dans la même origine que la page qui l'a chargé, héritant des mêmes règles CSP et CORS. Si un attaquant injecte du code dans votre origine (XSS), il peut appeler les exports de votre module WASM. Le WASM ne fournit pas de frontière de sécurité supplémentaire.

Garanties à temps constant : la plupart des bibliothèques crypto compilées en WASM préservent les propriétés à temps constant du source C, mais la traduction WASM-vers-JIT peut introduire du code à temps variable sur certaines plateformes. Pour la plupart des modèles de menace, c'est négligeable ; pour se défendre contre des attaquants locaux avec des mesures de timing précises, prenez en compte cet écart.

La recette pratique

Pour une application de transfert de fichiers en 2026 :

  • Utilisez la Web Crypto pour AES-256-GCM, PBKDF2, HMAC, SHA-256, RSA-OAEP si vous avez besoin de RSA
  • Utilisez libsodium.js via WASM pour le hachage de mots de passe Argon2id
  • Utilisez libsodium.js pour le chiffrement en flux de gros fichiers (crypto_secretstream)
  • Utilisez Ed25519/X25519 natif là où c'est supporté, sinon repliez-vous sur libsodium
  • Chargez le WASM paresseusement après une interaction utilisateur pour alléger le bundle initial
  • Effectuez des benchmarks sur vos appareils cibles ; ne supposez pas que les chiffres desktop correspondent au mobile

HexaTransfer reste sur la Web Crypto AES-GCM pour le chiffrement en masse, car la voie accélérée par le matériel est la plus rapide. WASM est réservé aux algorithmes que les API natives ne peuvent pas fournir.

Essayez sur hexatransfer.com — gratuit, sans compte, jusqu'à 10 Go.

Envoyez vos fichiers volumineux en toute sécurité avec le chiffrement de bout en bout

Transférez des fichiers jusqu'à 10 Go gratuitement avec le chiffrement de bout en bout. Aucun compte requis. Vos fichiers sont chiffrés dans votre navigateur avant l'envoi — personne d'autre ne peut les lire.

Envoyer un fichier