WebAssembly暗号化:ブラウザでネイティブに近い暗号速度
WebAssemblyでブラウザのネイティブに近い暗号化速度を実現。WASM暗号実装を比較しJavaScriptの代わりにWASMを使うべき場面を解説。
WASM にコンパイルされた暗号ライブラリはブラウザ内でネイティブC速度の約60〜80%で動作し、これは純粋 JavaScript 実装の3〜10倍高速です。 Web Crypto API に存在しない暗号プリミティブ( ChaCha20-Poly1305 、 Argon2id 、 XChaCha20 、 Kyber などのポスト量子 KEM )においては WASM が実用的なパフォーマンスへの現実的な経路です。 libsodium.js が主流の選択肢で、200KBの WASM バイナリで全 libsodium API を提供します。 AES-256-GCM のような Web Crypto がサポートするプリミティブでは、ブラウザのネイティブ・ハードウェア加速実装が WASM を大幅に上回るため、 WASM が常に正解ではありません。
ネイティブ Web Crypto が優位な場面
AES-GCM 、 AES-CBC 、 AES-CTR 、 PBKDF2 、 HMAC 、 RSA-OAEP 、 ECDH 、 ECDSA 、 SHA-256/384/512 など Web Crypto が公開しているアルゴリズムでは、ブラウザのネイティブ実装が x86 の AES-NI とモバイルの ARMv8 crypto extensions によるハードウェア加速を使用します。
AES-256-GCM の典型的なスループットは次の通りです。
- Web Crypto( Chrome on Apple Silicon ):1.7GB/s
- Web Crypto( Chrome on Intel x86 with AES-NI ):1.2GB/s
- libsodium WASM AES-GCM:400〜800MB/s
- ネイティブ OpenSSL の参考値:3〜5GB/s
WASM はサンドボックス内から AES-NI にアクセスできないため、ビットスライス AES 実装にフォールバックします。低速ですが定数時間です。 Web Crypto がサポートするものは必ず Web Crypto を最初に使います。
WASM が優位な場面
Web Crypto に存在しないアルゴリズムでは WASM が選択肢になります。
- ChaCha20-Poly1305:AES-NI のないデバイスでは AES より高速。2026年現在、ブラウザのネイティブサポートなし。
- XChaCha20-Poly1305:192ビット nonce でランダム nonce 使用がどのスケールでも安全。
- Argon2id:PHC 優勝のパスワードハッシュ関数。ブラウザのネイティブサポートなし。
- X25519 / Ed25519: Safari 17 と Firefox 129 がネイティブサポートを追加したが、 WASM が依然として可搬性の高いパス。
- BLAKE2b / BLAKE3:Web Crypto にない高速ハッシュ関数。
- Kyber 、 Dilithium 、 SPHINCS+:ポスト量子プリミティブ。ライブラリの領域。
- ストリーミング AEAD: libsodium の
crypto_secretstreamは大容量ファイルのストリーミング暗号化を扱いやすくする。 Web Crypto に相当機能なし。
これらでは JavaScript 実装が存在しますが WASM より5〜20倍遅いです。1GBファイルに ChaCha20-Poly1305 を使う場合、 WASM は約2秒、純粋JSは15〜40秒かかります。
libsodium.js:主力ライブラリ
libsodium-wrappers( Emscripten でビルドされた libsodium と JS ラッパー)が定番です。200KB の WASM と約50KBの JS ラッパー(gzip圧縮後)です。
import _sodium from 'libsodium-wrappers';
await _sodium.ready;
const sodium = _sodium;
// 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
);
初期ページレンダリングをブロックしないよう動的にロードします。
async function getSodium() {
if (!window._sodium) {
const mod = await import('libsodium-wrappers');
await mod.ready;
window._sodium = mod;
}
return window._sodium;
}
crypto_secretstream による大容量ファイルのストリーミング
大容量ファイル転送には libsodium の crypto_secretstream_xchacha20poly1305 がブラウザで最もクリーンなストリーミング AEAD です。
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
);
// 最終チャンクは TAG_FINAL で切り捨て攻撃を受信側が検出できるようにする
const chunkLast = sodium.crypto_secretstream_xchacha20poly1305_push(
state, plaintextLast, null,
sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
);
TAG_FINAL マーカーにより受信側がトランケーション攻撃を検出できます。攻撃者が末尾のチャンクを削除してもクライアントでの復号が失敗します。 Web Crypto の AES-GCM にはこの特性がなく、自前でトランケーション検出を実装する必要があります。
ブラウザでの Argon2
ブラウザでのパスワードベース鍵導出には、2026年のベストプラクティスとして WASM 経由の Argon2id が推奨されます。選択肢は次の通りです。
- argon2-browser:Argon2 専用 WASM ライブラリ、約200KB
- libsodium.js:すでに libsodium を使っている場合、
crypto_pwhash経由で Argon2id @noble/hashes:純粋JS Argon2id、約15KBのバンドル、 WASM より3〜5倍低速
OWASP 2024 のベースライン(m=19MiB、t=2、p=1)は300〜500msで実行されます。より強固なパラメータ(m=64MiB、t=3、p=4)は現代ハードウェアで1〜2秒です。
WASM 経由のポスト量子暗号
Kyber(鍵カプセル化)と Dilithium(署名)は NIST が2024年に ML-KEM と ML-DSA として標準化しました。 liboqs-js などの WASM 版は JavaScript 実装より高速で監査性が高いです。ファイル転送では、ポスト量子 KEM により対称ファイル鍵を量子安全な公開鍵プリミティブでラップできます。ハイブリッドスキーム(ML-KEM + X25519)は古典攻撃者と将来の量子攻撃者の両方から保護します。
バンドルサイズの考慮
WASM バイナリはJSバンドルの一部として出荷されるか、遅延フェッチされます。おおよそのサイズ(gzip圧縮後)は次の通りです。
- libsodium.js デフォルト:200KB
- libsodium.js sumo:600KB
- argon2-browser:200KB
- liboqs-js(ポスト量子):1MB以上
ブラウザでの暗号化がメイン機能のアプリでは WASM を初期ロードに含めることは合理的です。 rel="modulepreload" ヒントまたは Service Worker キャッシュで後続ロードを即時にします。
コンパイルとインスタンス化のオーバーヘッド
WebAssembly.instantiate() はバイナリを解析・コンパイルしますが、200KBモジュールでデスクトップ20〜100ms、モバイル100〜500msかかります。これはセッションあたり1回のみです。 WebAssembly.instantiateStreaming() はダウンロードとコンパイルをパイプライン処理し、通常の instantiate() より30〜50%の起動時間を節約します。
WASM 固有のメモリ管理の注意点
WASM モジュールはリニアページ(各64KiB)でメモリを割り当てます。複数GBのファイル暗号化では次の点に注意します。ファイル全体を WASM メモリにロードせずチャンクをストリームし、各チャンク後に sodium.memzero または参照をnullにしてバッファを解放します。 Memory64(Chrome で部分実装済み)は4GBの上限を除去しますが、現時点ではチャンク処理が信頼性の高い手段です。
実用的なレシピ
2026年のファイル転送アプリ向けに整理すると次の通りです。
- AES-256-GCM 、 PBKDF2 、 HMAC 、 SHA-256 、 RSA-OAEP には Web Crypto を使用
- パスワードハッシュには WASM 経由の libsodium.js で Argon2id を使用
- 大容量ファイルのストリーミング暗号化には libsodium.js の
crypto_secretstreamを使用 - Ed25519/X25519 はサポートされている場合はネイティブを使用し、そうでなければ libsodium にフォールバック
- WASM はユーザー操作後に遅延ロードして初期バンドルを軽量に保つ
- ターゲットデバイスでベンチマークする — デスクトップの数値がモバイルに当てはまると思わない
hexatransfer.comでお試しください — 無料、登録不要、最大10GB。
エンドツーエンド暗号化で大容量ファイルを安全に送信
エンドツーエンド暗号化で最大10GBのファイルを無料で転送。アカウント不要。ファイルはアップロード前にブラウザで暗号化されるため、他の誰にも読まれません。
ファイルを送信