Pular para o conteúdo

Criptografia

Hashes, senhas, HMAC, aleatoriedade segura e cifragem de arquivo com Arcane.Crypto.

O escopo#

Arcane.Crypto traz primitivas da biblioteca padrão do Python — hashes, HMAC, derivação de senha, codificações e aleatoriedade criptográfica — mais cifragem de arquivo com ChaCha20-Poly1305, escrita aqui.

Hashes#

dataforge
adopt Arcane.Crypto as Crypto

Crypto.sha256("DataForge")
Crypto.sha512(dados)    Crypto.blake2b(dados)
Crypto.md5(dados)       Crypto.sha1(dados)
Crypto.hash(dados, "sha384")
Crypto.hash_file(caminho, "sha256")

Senhas#

Este é o uso mais importante do módulo, e o que mais se erra:

dataforge
guardada := Crypto.hash_password("minha-senha")
# pbkdf2_sha256$200000$<sal>$<derivada>

Crypto.verify_password("minha-senha", guardada)     # yes
Crypto.verify_password("outra", guardada)           # no

hash_password deriva a senha com sal aleatório e 200 mil iterações de PBKDF2. Guarde a string inteira — ela contém o algoritmo, o número de iterações e o sal.

HMAC — autenticar mensagens#

dataforge
assinatura := Crypto.hmac(chave, mensagem)
Crypto.hmac_verify(chave, mensagem, assinatura)     # yes/no

Use para verificar que uma mensagem veio de quem diz ter vindo — webhooks, tokens, cookies assinados.

Comparação em tempo constante#

dataforge
Crypto.constant_time_equals(recebido, esperado)

Comparar segredos com is vaza informação pelo tempo: uma comparação que falha no primeiro caractere retorna mais rápido que uma que falha no último. hmac_verify já faz isso internamente.

Cifrar um arquivo#

Guardar um contrato, um backup ou um .env em disco pede uma cifra de verdade — e não o xor_cipher que também mora neste módulo, que é brinquedo educativo.

dataforge
adopt Arcane.Crypto as Cofre

Cofre.cifrar_arquivo("contrato.pdf", "contrato.dfv", "minha senha")
Cofre.decifrar_arquivo("contrato.dfv", "de-volta.pdf", "minha senha")

Cofre.cifrar_pasta("relatorios", "relatorios.dfv", "minha senha")
Cofre.e_cifrado("contrato.dfv")              # yes
Cofre.informacao_do_cofre("contrato.dfv")    # o cabeçalho, sem a senha

É ChaCha20-Poly1305 (RFC 8439): a cifra embaralha, e o Poly1305 assina. Sem a assinatura, cifrar não bastaria — quem intercepta pode virar bits do texto cifrado, e como a cifra é XOR, isso vira bits do texto claro. A chave sai da senha por PBKDF2-SHA256 com 600 mil iterações.

O formato#

text
DFVAULT1 | iterações | sal (16) | nonce (12) | etiqueta (16) | dados

O cabeçalho não é segredo — ele diz como decifrar, e precisa ser lido antes de haver chave. Mas é autenticado junto com os dados: baixar as iterações para 1, na esperança de enfraquecer a derivação, invalida a etiqueta e o arquivo é recusado.

Por que escrever a cifra foi defensável#

O conselho de não escrever a própria criptografia vale principalmente contra dois riscos: errar o algoritmo, e vazar o segredo pelo tempo que a operação leva.

  • Errar o algoritmo — os vetores oficiais do RFC 8439 estão na suíte de testes. Uma implementação que os reproduz byte a byte está certa; não há meio-termo.
  • Vazar pelo tempo — é o motivo de a escolha ser ChaCha20 e não AES. O AES em software depende de tabelas, e o tempo de ler uma tabela varia com o que está em cache — que depende da chave. ChaCha20 não tem tabela nenhuma: é soma, rotação e XOR, sempre nas mesmas posições.

Apagar de verdade#

dataforge
Cofre.apagar_seguro("contrato.pdf")      # sobrescreve, depois remove

Aleatoriedade segura#

dataforge
Crypto.random_token(32)       # URL-safe, para links e sessões
Crypto.random_hex(32)
Crypto.random_bytes(32)
Crypto.random_password(16)
Crypto.uuid()
Crypto.short_id(12)

Todas usam secrets, não random. A diferença: random é previsível a partir de saídas anteriores — inaceitável para tokens de sessão ou reset de senha.

Codificações#

dataforge
Crypto.base64_encode(dados)     Crypto.base64_decode(texto)
Crypto.base64url_encode(dados)  # sem padding, para URLs
Crypto.hex_encode(dados)        Crypto.hex_decode(texto)

Mascarar para exibir#

dataforge
Crypto.mask("4111111111111111")     # ************1111
Crypto.mask(cpf, 3)                  # mostra os 3 últimos