CRYPTO-DESIGN.md — Modelo de Cifragem do Anotae¶
Documento técnico descrevendo a implementação de criptografia do Anotae. Referência para auditoria de segurança.
Versão: 1.0 Última atualização: 2026-04-30 Implementado em: v0.2
1. Visão geral¶
O Anotae usa criptografia em múltiplas camadas para proteger dados clínicos em repouso:
Camada 1: OS filesystem encryption (responsabilidade do usuário)
Camada 2: Vault SQLite (futuro: sqlite3mc page-level encryption)
Camada 3: Campo-level encryption (ChaCha20-Poly1305 AEAD) ← IMPLEMENTADO
Camada 4: Integridade SHA-256 por encounter ← IMPLEMENTADO
2. KDF — Derivação de chave-mestra¶
Algoritmo: Argon2id (RFC 9106, vencedor da Password Hashing Competition 2015)
Parâmetros padrão:
- time_cost = 3 (iterações)
- memory_cost = 65536 (64 MiB em KiB)
- parallelism = 1
- hash_len = 32 (bytes)
Parâmetros fallback (hardware < 4 GB RAM):
- time_cost = 2
- memory_cost = 19456 (~19 MiB)
- parallelism = 1
Detecção de hardware: os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES"). Se falhar (Windows), assume hardware adequado.
Salt: 32 bytes gerados por secrets.token_bytes(32) na criação do vault. Armazenado em texto claro em vault_meta (necessário para derivar a chave).
Implementação: anotae/persistence/crypto.py::derive_master_key()
3. Derivação de sub-chaves¶
Algoritmo: HKDF-SHA256 (RFC 5869)
A master key é derivada em 3 sub-chaves com contextos distintos:
| Chave | Info (contexto HKDF) | Uso |
|---|---|---|
vault_key |
anotae-vault-encryption-key-v1 |
Criptografia do vault (futuro sqlite3mc) + verificação de senha |
field_key |
anotae-field-encryption-key-v1 |
Criptografia de campos PII (CPF) |
audit_key |
anotae-audit-mac-key-v1 |
MAC de audit logs (futuro) |
Isolamento: Comprometimento de uma chave não compromete as outras (HKDF com info distinto garante independência).
Implementação: anotae/persistence/crypto.py::derive_keys()
4. Criptografia de campos — ChaCha20-Poly1305¶
Algoritmo: ChaCha20-Poly1305 AEAD (RFC 8439)
- Cifra autenticada (confidencialidade + integridade)
- Nonce: 12 bytes gerados por secrets.token_bytes(12)
- Tag: 16 bytes de autenticação (embutido no output)
Formato de saída: nonce (12 bytes) || ciphertext || tag (16 bytes)
Campos cifrados atualmente:
- professional_cpf → professional_cpf_encrypted (BLOB no SQLite)
Implementação:
- anotae/persistence/crypto.py::encrypt_field()
- anotae/persistence/crypto.py::decrypt_field()
5. Verificação de senha¶
Na criação do vault, um token de verificação é cifrado com vault_key:
Na abertura, o token é decifrado. Se falhar (InvalidTag), a senha está errada.
Isto evita armazenar hash da senha — a verificação é implícita.
Implementação: anotae/persistence/vault.py::_verify_password()
6. Integridade — SHA-256¶
Cada encounter tem hash SHA-256 calculado sobre representação canônica JSON (campos clínicos, excluindo hash e audit_log).
Verificação: secrets.compare_digest() para comparação em tempo constante (evita timing attacks).
Implementação:
- anotae/persistence/crypto.py::compute_encounter_hash()
- anotae/persistence/crypto.py::verify_encounter_hash()
- anotae/core/models.py::Encounter.canonical_bytes()
7. Memória — Limpeza de chaves¶
Best-effort em Python — a linguagem não garante controle total sobre memória.
VaultKeys.secure_zero(): zera vault_key, field_key, audit_key viactypes.memset- Chamado em
VaultManager.close() - Master key zerada imediatamente após derivação das sub-chaves
Limitações conhecidas: Python pode manter cópias em memória (GC, interning). Mitigação completa requer extensão C ou linguagem com controle manual de memória.
Implementação: anotae/persistence/crypto.py::_secure_zero_bytes()
8. Backup¶
- Backups são cópias do vault.db (já cifrado em campo-level)
- Hash SHA-256 do arquivo de backup para verificação de integridade
- Retenção configurável (padrão 7)
9. Dependências criptográficas¶
| Pacote | Versão | Uso | Licença |
|---|---|---|---|
argon2-cffi |
≥23.1 | KDF Argon2id | MIT |
cryptography |
≥42.0 | ChaCha20-Poly1305, HKDF-SHA256 | Apache-2.0/BSD |
Ambas mantidas ativamente com histórico de segurança sólido.
10. Ameaças mitigadas¶
| Ameaça | Mitigação |
|---|---|
| Roubo do vault.db | Campos PII cifrados com ChaCha20-Poly1305 |
| Brute-force da senha | Argon2id com m=64MiB t=3 (custo alto por tentativa) |
| Tampering de encounters | SHA-256 por encounter + comparação tempo constante |
| Chave em memória após logout | secure_zero (best-effort) |
| Reuso de nonce | Nonce aleatório 12 bytes por operação via secrets |
Este documento deve ser revisado a cada mudança no modelo criptográfico.