Origens: criptografia e autenticação eram problemas separados

Durante a maior parte da história da criptografia aplicada, confidencialidade e integridade foram projetadas de forma independente. A cifra escondia a mensagem; o MAC (message authentication code, o código de autenticação de mensagem) provava que ela não tinha sido alterada. Juntar as duas coisas ficava por conta de quem projetava o protocolo, e havia três jeitos óbvios de fazer isso:

  • Encrypt-then-MAC: primeiro criptografa, depois autentica o texto cifrado (ciphertext). Tem prova formal de segurança.
  • MAC-then-encrypt: primeiro autentica o texto claro (plaintext), depois criptografa os dois juntos. Foi a escolha do SSL e do TLS.
  • Encrypt-and-MAC: faz as duas coisas sobre o texto claro, de forma independente. Foi a escolha do .

Só a primeira é segura em qualquer situação, e a indústria foi justamente nas outras duas. Levou uma década de ataques para ficar claro por que essa escolha importava: no MAC-then-encrypt, o receptor precisa descriptografar antes de poder verificar, ou seja, ele processa bytes controlados pelo atacante antes de saber se são autênticos. Todo ataque de padding oracle explora exatamente essa brecha.

O histórico é bem concreto:

  • O resultado de 2002 sobre padding oracle mostrou que, se o sistema deixa escapar se o padding (o preenchimento que completa o último bloco) era válido ou não, ele acaba entregando o texto claro, um byte de cada vez.
  • O (2011) explorou os vetores de inicialização previsíveis do encadeamento no TLS 1.0.
  • O (2013) extraiu texto claro da diferença de tempo entre um padding válido e um inválido; com isso, até as implementações que devolviam mensagens de erro idênticas vazavam informação pelo tempo que levavam para falhar.
  • O (2014) forçava um downgrade para o SSL 3.0 e explorava o padding mal especificado dessa versão para ler bytes.

Cada correção deixou o código mais delicado: verificar o padding em tempo constante é realmente difícil de programar, e um único return antecipado traz o vazamento de volta. A verdadeira lição está nesse padrão: uma construção que exige cuidado de todo implementador, mais cedo ou mais tarde, vai cair na mão de um que não teve esse cuidado.

O que o AEAD mudou

O (authenticated encryption with associated data, ou criptografia autenticada com dados associados) coloca essa combinação dentro da própria primitiva, em vez de deixá-la a cargo do protocolo. Uma chave, uma chamada, uma saída, e a descriptografia falha como um todo: ou a tag de autenticação confere e você recebe o texto claro, ou não confere e você não recebe nada para processar.

A parte dos "dados associados" é importante e muitas vezes passa batido: ela autentica headers (cabeçalhos) que precisam ficar visíveis, mas não podem ser alterados, como o tipo, a versão e o número de sequência do registro (o record do TLS), e assim amarra o payload (a carga útil) criptografado ao seu contexto. Sem isso, o atacante pode deixar o texto cifrado intacto e trocar o envelope em volta dele.

As duas construções dominantes:

  • -: modo contador (counter mode) com um autenticador baseado em corpo de Galois. É muito rápido onde o processador tem instruções de AES e de multiplicação sem carry (carry-less multiply), o que inclui quase todo o hardware de servidor.
  • ChaCha20-Poly1305: uma cifra de fluxo (stream cipher) com um autenticador separado, projetada para ser implementada em software com tempo constante. É mais rápida onde não há instruções de AES, o que vale para boa parte do mundo móvel e embarcado.

O TLS 1.3 tirou do protocolo todas as cipher suites (as suítes de cifras) que não são AEAD: não marcou como obsoletas, removeu de vez. É por isso que a lista de cifras dele tem cinco entradas, e não centenas.

Duas formas de criptografar um registro

Depois que o handshake TLS define as chaves, cada registro com dados da aplicação precisa ser criptografado, para que ninguém que esteja escutando a conexão consiga ler, e autenticado, para que nenhum atacante consiga adulterar. Uma cipher suite pode fazer isso de dois jeitos, e o trecho do nome que indica o modo diz qual deles ela usa.

Uma cifra AEAD (authenticated encryption with associated data), como AES-GCM, AES- ou ChaCha20-Poly1305 (o AES é o Advanced Encryption Standard, e o CCM é o modo Counter with CBC-MAC dele), faz as duas tarefas numa única primitiva. Você entrega o texto claro e os dados associados, e ela devolve o texto cifrado mais uma tag de autenticação. Na descriptografia, a tag e os dados são verificados juntos e, se algo foi alterado, tudo falha numa operação só.

Uma cifra CBC (cipher block chaining, o encadeamento de blocos) só faz a criptografia. Para ter integridade, a suíte junta essa cifra com um (hash-based message authentication code) separado, indicado pelo hash no final do nome da suíte (por exemplo, o SHA em TLS_RSA_WITH_AES_128_CBC_SHA). Quem encaixa as duas operações é o protocolo, e a ordem em que elas acontecem faz uma diferença enorme.

Por que o MAC-then-encrypt deu errado

Originalmente, o TLS usava MAC-then-encrypt (MAC é o message authentication code): calcular o HMAC sobre o texto claro, anexar o resultado, completar com padding até fechar o bloco e, por fim, criptografar tudo com CBC. O problema é que o receptor precisa descriptografar e remover o padding antes de conferir o MAC, então um padding malformado e um MAC errado são detectados em etapas diferentes. Se o atacante consegue ver se um registro falhou no padding ou no MAC, seja por uma mensagem de erro, seja só pelo tempo de resposta, ele descobre um bit do texto claro a cada tentativa. Repetindo o processo, esse bit vira a mensagem inteira.

Isso é um padding oracle, e não é teoria. Uma série de ataques ao longo dos anos, como BEAST, Lucky 13 e POODLE, explorou a proteção CBC dos registros do TLS. Cada um foi corrigido com código de tempo constante cada vez mais delicado, mas a construção de base continuava gerando variantes novas. A conclusão da indústria foi parar de usá-la.

Por que o AEAD virou o padrão

O AEAD acaba com essa classe inteira de problemas. Não existe uma etapa de padding separada para o atacante sondar, nem uma etapa de MAC separada para ele cronometrar, porque verificação e descriptografia são uma operação só, que ou dá certo, ou falha. O TLS 1.3 leva isso até o fim e só permite cifras AEAD; suítes CBC simplesmente não fazem parte do TLS 1.3. No TLS 1.2 as duas convivem, e é por isso que o decodificador marca como fraca uma suíte CBC com HMAC-SHA1, mesmo quando a cifra é AES.

Se o nome da suíte tem GCM, CCM ou POLY1305, você está olhando para AEAD. Se tem CBC, é a construção antiga, e num serviço moderno ela deveria ser, no máximo, um fallback.

A obrigação que vem com o AEAD: nunca repetir um nonce

O AEAD troca a classe dos padding oracles por uma única exigência, que não perdoa. Um mesmo par de chave e nonce (o número que só pode ser usado uma vez) nunca pode ser reutilizado. No AES-GCM a consequência é grave, e vale dizer com todas as letras: repetir um nonce com a mesma chave vaza o XOR dos dois textos claros e permite recuperar a subchave de autenticação, o que deixa o atacante forjar mensagens dali em diante. Isso não é perda parcial de segurança; é quebra.

Onde isso costuma dar errado na prática:

  • Nonces aleatórios de 96 bits em volume muito alto, quando as colisões do paradoxo do aniversário deixam de ser teóricas. Foi isso que motivou o XChaCha20, cujo nonce de 192 bits torna uma colisão muito menos provável, e o AES-GCM-SIV, que tolera muito melhor a reutilização.
  • Contadores que reiniciam, normalmente depois do restore de uma , do rollback de um snapshot ou de um dispositivo que volta a funcionar com o mesmo material de chave.
  • Arquiteturas stateless, em que dois nós compartilham uma chave e cada um escolhe os nonces por conta própria.

O TLS 1.3 evita tudo isso porque deriva o nonce do número de sequência, em vez de transmiti-lo; assim, só há reutilização se houver uma falha no próprio protocolo. Quem constrói a própria camada de registros precisa resolver isso de forma consciente.

O outro trade-off é o tamanho do registro. Cada registro carrega uma tag, de 16 bytes nas duas construções dominantes; então muitos registros pequenos custam, em proporção, mais overhead do que poucos registros grandes, e registros grandes aumentam a latência, porque nada é entregue enquanto o registro inteiro não for verificado.

Onde a escolha aparece na prática

  • Bibliotecas TLS definem a lista padrão de suítes, e nas versões modernas o AEAD virou a única opção que vale a pena.
  • Criptografia de disco e de volume muitas vezes usa modos que preservam o tamanho dos dados, e não AEAD, porque um setor não pode crescer para caber uma tag; esse é um motivo legítimo para ainda existirem construções sem AEAD em sistemas atuais.
  • Criptografia na camada de aplicação (libsodium, Tink, SDKs de gerenciamento de chaves das nuvens), em que a orientação honesta é usar uma biblioteca que cuide dos nonces, em vez de montar as primitivas na mão.
  • Protocolos com camada de registros própria (QUIC, , SSH): hoje todos usam AEAD por padrão, e todos derivam os nonces de contadores, exatamente pelo motivo explicado acima, embora o SSH ainda ofereça AES-CTR com um MAC separado.

A regra que vale em qualquer contexto: use AEAD, deixe o nonce por conta de uma biblioteca e trate qualquer projeto em que você mesmo escolhe os nonces como algo que precisa de revisão.

O trade-off que o AEAD ainda exige

O AEAD não elimina os pontos delicados; só muda o lugar deles. Toda criptografia AEAD precisa de um nonce, um número usado uma única vez, e a segurança do GCM (Galois/Counter Mode), em especial, desmorona se o mesmo nonce for reutilizado com a mesma chave. O TLS gerencia os nonces para você com um contador por conexão; então o perigo está nos sistemas que montam o próprio framing das mensagens, e não no TLS em si. Mas é por esse motivo que os projetos com AEAD se preocupam tanto com a forma de gerar os nonces.

O outro trade-off é o tamanho da tag de autenticação. As suítes padrão de GCM e Poly1305 usam uma tag de 16 bytes. As suítes CCM_8 cortam a tag para 8 bytes, para economizar overhead em links de baixa capacidade, e isso enfraquece a integridade de forma mensurável. É por isso que a TLS_AES_128_CCM_8_SHA256 é uma suíte AEAD legítima do TLS 1.3 que a (Internet Assigned Numbers Authority) ainda assim não marca como recomendada: o modo é moderno, mas a tag curta é uma concessão que a internet em geral não precisa fazer.

Fontes

O comportamento de protocolo descrito acima é normativo, e os dois ataques que o motivaram estão documentados com seus números . É aqui que se reconfere ambos.