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#
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:
guardada := Crypto.hash_password("minha-senha")
# pbkdf2_sha256$200000$<sal>$<derivada>
Crypto.verify_password("minha-senha", guardada) # yes
Crypto.verify_password("outra", guardada) # nohash_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#
assinatura := Crypto.hmac(chave, mensagem)
Crypto.hmac_verify(chave, mensagem, assinatura) # yes/noUse para verificar que uma mensagem veio de quem diz ter vindo — webhooks, tokens, cookies assinados.
Comparação em tempo constante#
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.
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#
DFVAULT1 | iterações | sal (16) | nonce (12) | etiqueta (16) | dadosO 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#
Cofre.apagar_seguro("contrato.pdf") # sobrescreve, depois removeAleatoriedade segura#
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#
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#
Crypto.mask("4111111111111111") # ************1111
Crypto.mask(cpf, 3) # mostra os 3 últimos