Перейти к содержимому
ecdsa.com
WebCrypto crypto.subtleWebCrypto

Сообщение об ошибке

OperationError: The operation failed for an operation-specific reason

WebCrypto crypto.subtle — browsers & Node

Что это означает

Намеренно расплывчатая универсальная ошибка WebCrypto: сбой произошёл в самом примитиве. Для decrypt() с AES-GCM она означает, что тег аутентификации не прошёл проверку: неверный ключ, неверный IV, неверные дополнительные данные или изменённый шифртекст. Спецификация оставляет сообщение общим, чтобы реализации не раскрывали, почему криптографическая проверка не прошла. Для sign() и deriveBits() то же имя охватывает внутренние сбои примитива, поэтому операция в трассировке стека важнее текста сообщения.

Почему это происходит

Как исправить

  1. 1.

    Передавайте IV вместе с шифртекстом и декодируйте симметрично

    Добавляйте IV в начало шифртекста и отделяйте его при расшифровании, чтобы стороны никогда не расходились; везде используйте один вариант base64.

    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

Связанные ошибки

Открыть всю базу ошибок подписей