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
Falha de autenticação do AES-GCM
comumDescriptografar com uma chave diferente da usada para criptografar, um IV que não corresponde, additionalData ausente ou diferente, ou um texto cifrado que perdeu no transporte a tag de 16 bytes anexada ao final. O GCM é autenticado: qualquer um desses casos faz a operação inteira falhar, em vez de retornar lixo.
Texto cifrado truncado ou recodificado
comumBase64 tratado de forma inconsistente (alfabeto URL-safe versus padrão), uma coluna do banco de dados cortando bytes ou conversões entre string e binário corrompendo o buffer. A verificação da tag detecta todas essas alterações — é para isso que ela existe.
Parâmetros de derivação fora do intervalo
ocasionalderiveBits/deriveKey com um comprimento impossível de atender, PBKDF2 com zero iterações ou um cálculo ECDH com entradas inconsistentes aparecem como OperationError em vários runtimes.
Como corrigir
- 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.
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.
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