Pular para o conteúdo
ecdsa.com
WebCrypto crypto.subtleWebCrypto

Mensagem de erro

OperationError: The operation failed for an operation-specific reason

WebCrypto crypto.subtle — browsers & Node

O que significa

O erro genérico do WebCrypto, vago de propósito: a própria primitiva falhou. Em decrypt() com AES-GCM, significa que a verificação da tag de autenticação falhou — chave errada, IV errado, dados adicionais errados ou texto cifrado modificado. A especificação mantém a mensagem genérica para que as implementações não revelem por que uma verificação criptográfica falhou. Em sign() e deriveBits(), o mesmo nome cobre falhas internas da primitiva, então a operação no seu stack trace importa mais que o texto da mensagem.

Por que acontece

Como corrigir

  1. 1.

    Enviar o IV junto com o texto cifrado e decodificar de forma simétrica

    Coloque o IV na frente do texto cifrado e separe-os ao descriptografar, para que os dois lados sempre estejam de acordo; use a mesma variante de base64 em todo lugar.

    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.

    Tratar como veredito de integridade, não como corrupção

    Quando decrypt lança OperationError, a resposta correta é “este texto cifrado não é autêntico com esta chave” — rejeite-o. Não tente de novo com ajustes de padding nem recorra a modos sem autenticação; a falha da verificação é o próprio recurso de segurança.

  3. 3.

    Comparar as impressões digitais das chaves nos dois lados

    Exporte as duas chaves e calcule o hash de cada uma; digests diferentes resolvem o mistério de uma vez.

    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

Erros relacionados

Ver toda a base de erros de assinatura