Capítulo 4 — Programação I
Mapa da aula
Aula 04 publicada. Mapa, exemplo, EX007.
flowchart TD
N0["4.1 Bytes, não strings"]
N1["4.2 Aleatoriedade"]
N2["4.3 Hashes e MACs"]
N3["4.4 Testes"]
N4["4.6 Atividade prática"]
N5["Síntese"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5
N5 --> EX["Exemplo resolvido"]
EX --> Q["Exercício EX007"]
Desenho lógico
flowchart LR
N0["4.1 Bytes, não strings"]
N1["4.2 Aleatoriedade"]
N2["4.3 Hashes e MACs"]
N3["4.4 Testes"]
N4["4.6 Atividade prática"]
N5["Síntese"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX007"]
[4.1 Bytes, não strings]
|
v
[4.2 Aleatoriedade]
|
v
[4.3 Hashes e MACs]
|
v
[4.4 Testes]
|
v
[4.6 Atividade prática]
|
v
[Síntese]
|
v
[Exemplo resolvido] --> [Exercício EX007]
Objetivos de aprendizagem
Você vai (1) escrever programas Python pequenos e testáveis com a biblioteca padrão; (2) tratar corretamente bytes, codificações e inteiros; (3) usar com segurança hashlib, hmac e secrets; e (4) reconhecer por que você não deve implementar criptografia de produção por conta própria. O capítulo prepara os laboratórios usados ao longo do livro.
4.1 Bytes, não strings
As funções criptográficas operam sobre bytes. O str do Python é texto; converta com uma codificação explícita ("text".encode("utf-8")). Misturar os dois causa bugs sutis, como calcular o hash da representação textual de um objeto bytes ("b'...'") em vez dos próprios bytes. Os inteiros se convertem com int.from_bytes(b, "big") e n.to_bytes(length, "big"); declare sempre a ordem dos bytes e o comprimento, porque especificações de protocolo como a FIPS 203 definem codificações exatas.
Um sintoma comum da mistura de tipos são dois sistemas calculando hashes diferentes para a mesma mensagem porque um codificou o texto como UTF-8 e o outro como Latin-1. Fixar a codificação na fronteira e passar bytes em todo o interior elimina essa classe de bug. Comprimento e ordem dos bytes também importam: a FIPS 203 codifica os coeficientes num leiaute de bits preciso, e um teste contra os vetores da norma é a única verificação confiável.
4.2 Aleatoriedade
Use secrets.token_bytes() para chaves e nonces; nunca random, que é um gerador pseudoaleatório previsível, feito para simulações. Os laboratórios deste livro usam random apenas para gerar dados sintéticos e parâmetros de brinquedo, e dizem isso.
O random do Python parte de um estado que pode ser recuperado depois de observadas saídas suficientes, de modo que qualquer chave produzida por ele pode ser prevista. O secrets obtém os dados do gerador criptográfico do sistema operacional. Manter os dois visivelmente separados no código, por exemplo nunca importando random em módulos de produção, facilita as revisões e impede que código de brinquedo escorregue para sistemas reais.
4.3 Hashes e MACs
Estão disponíveis hashlib.sha256, hashlib.sha3_256 e hashlib.shake_256; o SHA-3 e o SHAKE são usados dentro do ML-KEM e do ML-DSA. hmac.new(key, msg, hashlib.sha256) calcula um código de autenticação de mensagem, e hmac.compare_digest compara tags em tempo constante — um == comum pode vazar, pelo tempo, onde dois valores começam a diferir.
O SHAKE é uma função de saída extensível: pode produzir quantos bytes de saída forem necessários, o que o ML-KEM usa para expandir sementes em matrizes. Um MAC difere de um hash porque precisa de uma chave secreta; sem ela, qualquer um pode recalcular o hash de uma mensagem modificada. A comparação em tempo constante importa sobretudo quando o atacante pode enviar muitos palpites e medir os tempos de resposta.
4.4 Testes
Toda função recebe um teste com resposta conhecida. Em criptografia, os testes de resposta conhecida (KATs) publicados com as normas permitem confirmar que uma implementação produz exatamente os bytes esperados. O Cryptographic Algorithm Validation Program do NIST publica vetores de teste para os algoritmos aprovados.
Um KAT fixa as entradas, inclusive o que normalmente seria aleatório, de modo que a saída esperada é uma única sequência exata de bytes. Passar nos KATs prova que a implementação coincide com a norma nessas entradas; não prova resistência a canais laterais nem a mau uso. Os engenheiros executam os KATs na integração contínua, para que uma atualização de biblioteca que mude as saídas seja detectada antes da implantação.
4.5 Exemplo resolvido: um log de auditoria em cadeia de hashes
A BPA quer logs que evidenciem adulteração. Cada entrada guarda h_i = SHA-256(h_{i-1} || entry_i). Um programa curto calcula a cadeia, e um teste verifica que alterar qualquer entrada muda todos os hashes posteriores. O programa usa bytes do começo ao fim, codificações fixas e compare_digest. Esse é um uso sólido de primitivas padronizadas; projetar um novo esquema de assinatura não seria.
A cadeia detecta modificação, remoção e reordenação, porque cada mudança altera todos os hashes posteriores. Ela não impede que alguém com acesso de escrita reconstrua a cadeia inteira, por isso a BPA publica o hash mais recente diariamente num sistema separado. O programa é curto o bastante para ser revisado por completo, e toda primitiva que ele usa já é padronizada.
4.6 Atividade prática
Escreva a função da cadeia de hashes com um teste, depois acrescente um bug deliberado (calcular o hash de str(entry) em vez de entry) e veja o teste detectá-lo.
O bug deliberado mostra por que os testes precisam comparar bytes exatos: o hash de str(entry) é calculado sobre o texto "b'...'", com aspas e um prefixo, então todos os hashes posteriores mudam, embora o conteúdo do log pareça idêntico. Escreva primeiro o teste que falha, confirme que ele falha com o bug, depois corrija o código e confirme que ele passa. Mantenha o teste na suíte para que o erro não volte.
4.7 Erros comuns e limites
- Usar
randompara chaves. - Comparar segredos com
==. - Implementar você mesmo o ML-KEM ou o ML-DSA para produção: a segurança da implementação (Capítulo 31) é mais difícil do que a correção.
Cada erro já causou falhas reais. Números aleatórios previsíveis expuseram chaves, comparações simples vazaram tags de autenticação pelo tempo e implementações caseiras de algoritmos padronizados passaram nos testes de correção enquanto vazavam segredos pelo tempo ou pelo consumo de energia. A BPA usa bibliotecas verificadas para o ML-KEM e o ML-DSA e reserva o próprio código para a cola em volta delas, que testa com KATs.
Exercícios
- EX007 — Bug de codificação. Explique por que
hashlib.sha256(str(b"abc").encode())difere dehashlib.sha256(b"abc")e que classe de bug isso representa. Rubrica: 3 = correto e generalizado. - EX008 — Cadeia de hashes. Escreva a função da cadeia de hashes e um teste que detecte adulteração. Rubrica: 3 = executa, os testes passam, usa bytes e
compare_digest.
Síntese
O código criptográfico é exato em bytes, usa aleatoriedade segura, compara segredos em tempo constante e é testado contra respostas conhecidas; os engenheiros compõem primitivas verificadas em vez de inventá-las.
Continue estudando — capítulo 04 de 50
← Capítulo 03 · Índice de capítulos do curso · Capítulo 05 →
Siga para a próxima lição escrita.