本文へスキップ
ecdsa.com
WebCrypto crypto.subtleWebCrypto

エラーメッセージ

OperationError: The operation failed for an operation-specific reason

WebCrypto crypto.subtle — browsers & Node

このエラーの意味

WebCrypto が意図的にあいまいにしている包括的なエラーで、プリミティブ自体が失敗したことを示します。AES-GCM の decrypt() では、認証タグの検証に失敗したことを意味します。鍵、IV、追加データのいずれかが間違っているか、暗号文が改変されています。仕様では、暗号のチェックが失敗した理由を実装が漏らさないよう、メッセージを汎用的なものにしています。sign() と deriveBits() では、同じ名前がプリミティブ内部の失敗を表すため、メッセージのテキストよりも、スタックトレースに出ている操作のほうが重要です。

発生する理由

修正方法

  1. 1.

    IV と暗号文を一緒に送り、両側で同じ方式でデコードする

    IV を暗号文の先頭に付けて復号時に分割すれば、両側の認識が常に一致します。base64 の種類はすべての場所で 1 つに統一してください。

    js
    // encrypt
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, data);
    const packed = new Uint8Array([...iv, ...new Uint8Array(ct)]);
    
    // decrypt
    const buf = packedFromWire;
    const pt = await crypto.subtle.decrypt(
      { name: "AES-GCM", iv: buf.slice(0, 12) }, key, buf.slice(12),
    );
  2. 2.

    データ破損ではなく完全性の判定として扱う

    decrypt が OperationError をスローしたときの正しい対応は、「この暗号文はこの鍵のもとでは真正ではない」と判断して拒否することです。パディングを調整して再試行したり、認証のないモードにフォールバックしたりしてはいけません。チェックが失敗すること自体が、セキュリティ機能なのです。

  3. 3.

    両側で鍵のフィンガープリントを比較する

    両方の鍵をエクスポートしてハッシュを計算してください。ダイジェストが異なれば、それだけで謎が解けます。

    js
    const raw = await crypto.subtle.exportKey("raw", key);
    const fp = await crypto.subtle.digest("SHA-256", raw);
    console.log(Buffer.from(fp).toString("hex").slice(0, 16)); // compare on both sides

関連するエラー

署名エラーデータベースをすべて見る