FatoCadeia de Custódia

Documento Técnico · Hash & Assinatura · Público

Especificação de Geração de Hash
em Gravações Fato

Especificação detalhada do procedimento de cálculo, encadeamento e assinatura criptográfica dos hashes que asseguram a integridade de cada gravação. Acompanha o Documento Técnico de Segurança.

Versão
1.1
Vigência
18 abr 2026
Algoritmos
SHA-256 · Ed25519
Conformidade
FIPS 180-4 · RFC 8032
§ 1 · Especificação Resumida

Parâmetros aplicáveis

Os parâmetros abaixo regem a geração de hash em todas as gravações finalizadas pela plataforma Fato a partir da versão 1.1.

Função de hashSHA-256 (FIPS 180-4 § 6.2)
Tamanho do hash256 bits — 32 bytes — 64 caracteres hexadecimais
Tamanho do bloco de vídeo≈ 1 segundo · segmento fMP4 · variável conforme bitrate
Encadeamentohash_n = SHA-256(hash_{n−1} ‖ bloco_n)
Estado inicialhash_{−1} = b"" (sequência vazia)
Hash final do arquivoSHA-256 do conteúdo binário completo
Assinatura do manifestoEd25519 (RFC 8032)
Resistência a colisão≈ 2128 operações — impraticável com hardware atual [4]
§ 2 · Função SHA-256

Justificativa da escolha

SHA-256 é função de hash criptográfica padronizada pelo NIST em FIPS 180-4 [1]. Transforma sequência binária de comprimento arbitrário em saída fixa de 256 bits. Três propriedades a tornam adequada à cadeia de custódia de evidências digitais:

DeterminismoA mesma entrada produz invariavelmente a mesma saída; permite verificação repetida por terceiros independentes.
SensibilidadeA alteração de um único bit na entrada modifica aproximadamente 50% dos bits da saída (efeito avalanche).
UnidirecionalidadeÉ computacionalmente inviável recuperar a entrada a partir do hash, ou produzir entrada distinta com idêntico hash.
Justificativa frente a alternativas

SHA-256 foi escolhida em detrimento de SHA-3 e BLAKE3 pela ampla aceitação em ferramentas de perícia digital nacionais e internacionais, pelo suporte universal em sistemas operacionais e linguagens de programação, e pela maturidade do algoritmo (publicação inicial em 2002, revisão em 2015).

§ 3 · Hash de Bloco

Cálculo individual por segmento

O cliente envia o vídeo ao servidor em segmentos fMP4 de aproximadamente um segundo. Para cada segmento recebido, o servidor calcula o hash imediatamente — antes mesmo da confirmação da escrita em disco. Os hashes individuais são mantidos em memória durante a sessão e descartados após o encerramento, prevalecendo o hash encadeado (§ 4).

# Servidor: cálculo do hash de bloco
bloco       = receber_via_websocket()
hash_bloco  = sha256(bloco).hexdigest()

gravar_em_disco(arquivo_temp, bloco)
registrar(blocos_hashes, hash_bloco)
§ 4 · Encadeamento

Estrutura cumulativa de hashes

O hash de cada bloco é calculado sobre a concatenação do hash do bloco anterior (estado da cadeia) e do conteúdo binário do bloco corrente. A construção é semelhante à de uma cadeia de blocos: a alteração de qualquer segmento exige o recálculo de todos os hashes subsequentes, e a nova cadeia diferirá da registrada no manifesto contra-assinado pelo servidor.

# Construção da cadeia (executada no servidor)
hash_chain = b""                              # estado inicial vazio

for bloco_n in blocos_recebidos:
    entrada    = hash_chain + bloco_n         # concatena estado + bloco
    hash_chain = sha256(entrada).digest()     # novo estado da cadeia

chain_hash_final = hash_chain.hex()           # registrado no manifesto
Por que encadear, em vez de hashear o arquivo único

A construção encadeada permite verificação progressiva durante a captura (o servidor identifica blocos corrompidos imediatamente) e demonstra a ordem temporal dos blocos — garantia que o hash único do arquivo finalizado, isoladamente, não oferece.

§ 5 · Hash do Arquivo Final

Cálculo após finalização

Concomitantemente ao encadeamento, o servidor calcula também o SHA-256 simples do arquivo MP4 finalizado. Trata-se do valor de verificação mais simples para terceiros, dado que pode ser recalculado por ferramentas de linha de comando padrão.

$ shasum -a 256 evidencia_8f3c91.mp4
c2d4e1f8a3b9...0e2  evidencia_8f3c91.mp4

$ jq -r '.manifest | fromjson | .file_sha256' manifest.json
c2d4e1f8a3b9...0e2

# Coincidência atesta integridade byte-a-byte do arquivo recebido.
§ 6 · Assinatura ed25519

Sobre o manifesto

O hash, isoladamente, atesta apenas que o arquivo não sofreu alteração desde determinado instante. A assinatura digital estabelece quem garantiu aquele instante e quando. O servidor assina o manifesto JSON — que contém todos os hashes — com sua chave ed25519 persistente, gerada uma única vez na primeira inicialização da plataforma.

# Servidor: composição e assinatura do manifesto
manifesto = {
    "session_id":                  session_id,
    "client_pubkey":               pubkey_do_cliente,
    "metadata_signature":          metadata_sig_cliente,
    "file_sha256":                 hash_arquivo_completo,
    "events_log_sha256":           hash_log_de_eventos,
    "terminal_chunk_hash_server":  hash_terminal_do_servidor,
    "terminal_chunk_hash_client":  hash_terminal_do_cliente,
    "chunk_chain_verified":        os_dois_conferem,
    "tamper_detected":             tamper_detected,
    "tamper_events_sha256":        hash_dos_eventos_de_adulteracao,
}

# JSON canônico: chaves ordenadas e separadores compactos. É esta string
# exata que é assinada, e é a sequência de bytes guardada em envelope
# ["manifest"] que o verificador confere — ele NÃO reserializa o JSON, porque
# reserializar pode produzir bytes diferentes dos que foram assinados.
manifesto_canonico = json.dumps(
    manifesto, sort_keys=True, separators=(",", ":"), ensure_ascii=False
)
assinatura = chave_servidor.sign(manifesto_canonico.encode("utf-8"))

# Um único artefato: o envelope carrega a string assinada, a assinatura e a
# chave pública que o pacote declara — conferida contra a chave confiável
# informada em --server-pubkey, nunca aceita por si só.
envelope = {
    "manifest":           manifesto_canonico,
    "signature":          base64url(assinatura),
    "server_public_key":  base64url(chave_servidor.public_key),
}

gravar_em_s3("manifest.json", envelope)
Chave do servidorGerada uma vez na primeira inicialização. Persistida em data/server_key.seed com permissão restrita ao processo.
Chave públicaPublicada no portal. Pode ser fixada (pinning) por escritórios de advocacia para verificação prévia.
Serialização canônicaO JSON é serializado com chaves ordenadas alfabeticamente, de modo a tornar a verificação determinística em qualquer biblioteca conforme RFC 8259.
§ 7 · Implementação de Referência

Verificador independente em Python

A forma mais simples de verificar é a página /verify do portal: envie o pacote .zip da evidência e informe o SHA-256 do vídeo. Para verificação offline e independente da infraestrutura Fato, o projeto autônomo verifier executa as mesmas conferências sobre os artefatos extraídos do pacote — o vídeo, o manifest.json assinado e o log de eventos selado —, exigindo apenas a biblioteca cryptography.

O comando abaixo é a verificação real, não um exemplo. O verificador é um único arquivo Python auditável na íntegra, com uma única dependência de runtime: baixar verify.py (SHA-256 bf853e2ac29dff87ed40dd4523aa561e976a032f4077de2a7099a45299c2b38f). Requer Python 3.9 ou superior.

pip install 'cryptography>=42,<47'

python verify.py --manifest manifest.json \
                 --file <gravacao>.webm \
                 --events <id>.events.jsonl \
                 --server-pubkey <chave-publica-fato>

Ele confere: a assinatura ed25519 do servidor sobre o manifesto canônico, contra a chave confiável informada em --server-pubkey — nunca contra a chave que o próprio pacote anuncia; o SHA-256 do vídeo; o SHA-256 do log de eventos selado; a assinatura de metadados do cliente, quando o quadro assinado for fornecido com --client-metadata; e a comparação entre os hashes terminais da cadeia de blocos atestados pelo cliente e pelo servidor, somada ao veredito selado pelo próprio servidor — o principal indicador de adulteração no envio.

Limite desta última conferência, declarado explicitamente: ela compara dois valores atestados dentro do manifesto assinado. O verificador não recalcula a cadeia a partir da gravação, porque os limites e o conteúdo de cada bloco não são registrados em lugar nenhum e não podem ser reconstruídos offline. É a conferência da assinatura do servidor que torna esses dois valores confiáveis.

Código de saída 0 significa verificação completa aprovada; 1, reprovada; 3, incompleta — algum elo não pôde ser conferido com os artefatos fornecidos, o que não equivale a aprovação.

§ 8 · Esclarecimentos

Questões recorrentes

§ 8.1A re-codificação do vídeo preserva a verificabilidade?

Não. Qualquer alteração do conteúdo binário (re-encoding, corte, conversão de contêiner) altera o hash do arquivo. Para entrega em formato distinto, recomenda-se preservar o arquivo original Fato como prova e gerar a cópia adicional com hash próprio anexo.

§ 8.2A indisponibilidade do servidor inviabiliza a verificação?

Não. A verificação independe da infraestrutura Fato. Bastam o arquivo de vídeo, o manifesto, a assinatura do servidor e as duas chaves públicas. O servidor é necessário apenas para download dos artefatos.

§ 8.3O hash atesta a veracidade do conteúdo capturado?

Não. O hash atesta que o vídeo corresponde, byte a byte, ao que foi capturado pelo cliente naquele momento. A análise probatória da veracidade do conteúdo exibido permanece sob competência do operador do Direito.

§ 8.4Por que ed25519 em detrimento de RSA?

Ed25519 oferece nível de segurança equivalente a RSA-3072 com chaves de 32 bytes (contra 384 bytes em RSA) e desempenho substancialmente superior em assinatura e verificação. É o padrão recomendado pelo NIST FIPS 186-5 [3] e adotado em sistemas de cadeia de custódia de evidências digitais.

§ 9 · Referências

Bibliografia normativa

  1. NIST FIPS 180-4 Secure Hash Standard (SHS). National Institute of Standards and Technology, agosto de 2015. nvlpubs.nist.gov/.../FIPS.180-4
  2. RFC 8032 — Josefsson & Liusvaara, Edwards-Curve Digital Signature Algorithm (EdDSA). IETF, janeiro de 2017. rfc-editor.org/rfc/rfc8032
  3. NIST FIPS 186-5 Digital Signature Standard (DSS). National Institute of Standards and Technology, fevereiro de 2023. nvlpubs.nist.gov/.../FIPS.186-5
  4. NIST SP 800-107 Rev. 1 Recommendation for Applications Using Approved Hash Algorithms. Agosto de 2012.
  5. RFC 8259 — Bray, The JavaScript Object Notation (JSON) Data Interchange Format. IETF, dezembro de 2017. rfc-editor.org/rfc/rfc8259
Fato · Especificação de Hash · Versão 1.1Vigência 18.04.2026