Mapa da aula
Aula 09 publicada. Mapa, exemplo, EX017.
flowchart TD
N0["9.1 Interfaces acima de implementações"]
N1["9.2 Validação de entradas"]
N2["9.3 Testes além dos exemplos"]
N3["9.4 Dependências"]
N4["Nota de engenharia"]
N5["Síntese"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5
N5 --> EX["Exemplo resolvido"]
EX --> Q["Exercício EX017"]
Desenho lógico
flowchart LR
N0["9.1 Interfaces acima de implementações"]
N1["9.2 Validação de entradas"]
N2["9.3 Testes além dos exemplos"]
N3["9.4 Dependências"]
N4["Nota de engenharia"]
N5["Síntese"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX017"]
[9.1 Interfaces acima de implementações]
|
v
[9.2 Validação de entradas]
|
v
[9.3 Testes além dos exemplos]
|
v
[9.4 Dependências]
|
v
[Nota de engenharia]
|
v
[Síntese]
|
v
[Exemplo resolvido] --> [Exercício EX017]
Objetivos de aprendizagem
Você vai (1) estruturar o código de modo que os algoritmos sejam substituíveis; (2) interpretar e validar entradas não confiáveis, como certificados e compartilhamentos de chave; (3) escrever testes baseados em propriedades e de resposta conhecida; e (4) gerenciar dependências de bibliotecas criptográficas. O capítulo parte do Capítulo 4 e prepara a agilidade (Capítulo 39).
9.1 Interfaces acima de implementações
Escreva o código contra interfaces — kem.encapsulate(public_key), signer.sign(message) —, e não contra funções específicas de um algoritmo. Um identificador de algoritmo, escolhido pela política, seleciona a implementação. Essa é a base de programação da agilidade criptográfica (CSWP 39): a migração passa a ser uma mudança de configuração mais testes, e não uma reescrita.
O arquivo de política nomeia um algoritmo por uso, como ML-DSA-65 para assinaturas de documentos eletrônicos, e o código nunca o menciona. Quando a BPA passar mais tarde de uma configuração híbrida para uma puramente pós-quântica, a mudança será uma linha na política mais a execução dos testes. A revisão de código verifica que nenhum módulo contorna a interface importando diretamente uma biblioteca de algoritmo.
9.2 Validação de entradas
As chaves e os textos cifrados pós-quânticos têm comprimentos fixos por conjunto de parâmetros. A FIPS 203 exige verificações nas chaves de encapsulamento (uma verificação de módulo de que os coeficientes codificados estão abaixo de q) e nas chaves de desencapsulamento antes do uso. O código deve rejeitar comprimentos errados antes de chamar uma biblioteca e nunca "preencher" ou "truncar" para fazer as entradas caberem.
Uma chave de encapsulamento ML-KEM-768 precisa ter exatamente 1.184 bytes, e um texto cifrado exatamente 1.088. Verificar esses comprimentos primeiro dá uma mensagem de erro clara e evita passar dados malformados para um código que poderia se comportar de forma imprevisível. A verificação de módulo detecta chaves cujos coeficientes foram codificados com valores de 3329 ou mais, que um par malicioso poderia enviar para sondar fraquezas de implementação.
9.3 Testes além dos exemplos
Os testes de resposta conhecida confirmam saídas exatas. Os testes baseados em propriedades verificam invariantes sobre muitas entradas aleatórias: decapsulate(encapsulate(pk)) produz a mesma chave; uma assinatura é válida para a mensagem assinada e falha para qualquer mudança de um bit. Os testes negativos importam tanto quanto os positivos.
Um teste de propriedade para o assinador da BPA executa mil mensagens aleatórias, assina cada uma, verifica cada uma e depois inverte um bit aleatório da mensagem e da assinatura, esperando que as duas verificações falhem. Um teste que só verificasse a verificação bem-sucedida passaria mesmo que verify sempre retornasse verdadeiro, que é exatamente o bug que os testes negativos existem para detectar.
9.4 Dependências
Fixe as versões das bibliotecas criptográficas, acompanhe os seus alertas de segurança e registre quais algoritmos e conjuntos de parâmetros cada uma expõe. Nomes da época dos rascunhos (por exemplo, "Kyber768") e nomes finais (ML-KEM-768) não são intercambiáveis: a norma final mudou detalhes, então as implementações dos rascunhos não interoperam com a FIPS 203.
O arquivo de dependências da BPA registra o nome da biblioteca, a versão exata e os algoritmos que essa versão oferece sob os nomes finais. Quando uma versão da biblioteca renomeia ou remove um algoritmo de rascunho, o build falha no momento da atualização, e não no handshake de um parceiro. O acompanhamento dos alertas importa porque as bibliotecas pós-quânticas são mais novas e tiveram mais correções de segurança por ano do que o código RSA maduro.
9.5 Exemplo resolvido
O serviço de documentos eletrônicos da BPA chama rsa_sign() em 41 lugares. A refatoração introduz Signer, com as implementações RSA-PSS-3072 e ML-DSA-65, selecionadas por um arquivo de política. Uma suíte de testes percorre cada caminho de chamada com as duas. O diff é grande uma vez; a próxima migração será pequena.
Os 41 pontos de chamada são encontrados primeiro com uma busca por rsa_sign, e depois cada um é examinado em busca de pressupostos como um comprimento fixo de assinatura de 384 bytes numa coluna do banco de dados. As assinaturas ML-DSA-65 têm 3.309 bytes, então a mudança de esquema faz parte do mesmo item de trabalho. O arquivo de política começa com RSA-PSS-3072, para que nada mude para os usuários até a troca.
9.6 Erros comuns e limites
- Fixar no código nomes de algoritmos e comprimentos de chave.
- Tratar as versões de rascunho e final dos algoritmos como iguais.
- Testar apenas os caminhos de sucesso.
Nomes fixos no código transformam cada migração futura numa mudança de código em todos os pontos de chamada, que é exatamente o que a refatoração com Signer eliminou. As versões de rascunho e final diferem em detalhes, então uma biblioteca que ainda oferece o Kyber768 não interopera com o ML-KEM-768. Testar só os caminhos de sucesso deixa a validação e o tratamento de erros sem verificação, e os atacantes miram justamente esses caminhos com chaves e textos cifrados malformados.
Nota de engenharia
As interfaces também precisam de identificadores de algoritmo versionados que viajem com os dados. Uma assinatura guardada hoje precisa carregar um identificador que nomeie o seu algoritmo e conjunto de parâmetros, para que um verificador daqui a dez anos selecione a implementação certa e possa recusar as aposentadas. Sem identificadores, os sistemas adivinham pelo comprimento, o que quebra assim que dois algoritmos compartilham um tamanho. Trate o identificador como parte do conteúdo assinado ou autenticado sempre que o formato permitir, para que um atacante não possa rotular de novo uma assinatura fraca como forte. É esse pequeno hábito de projeto que depois torna segura, e não apenas conveniente, a migração guiada por política do Capítulo 39.
Exercícios
- EX017 — Projeto de interface. Escreva uma interface
KEM(nomes de métodos e tipos) adequada ao ML-KEM e a um futuro HQC. Rubrica: 3 = nenhum pressuposto específico de algoritmo, como tamanhos. - EX018 — Verificação de comprimento. Escreva um validador que rejeite uma chave de encapsulamento ML-KEM-768 de comprimento errado (correto: 1.184 bytes). Rubrica: 3 = rejeita, nunca preenche.
Síntese
Algoritmos substituíveis começam no código: interfaces, identificadores escolhidos pela política, validação estrita, testes de propriedade e negativos e um acompanhamento de dependências que distingue rascunho de final.