Especificação dos formatos humanproof
Este documento é para quem quer conferir um recibo da Hummand sem depender da Hummand, inclusive escrevendo o próprio verificador. Com ele, a chave pública e os vetores de teste, uma implementação independente confere assinatura, hash, cadeia e coerência e chega aos mesmos resultados que o verificador deste site.
Qualquer pessoa pode implementar esta especificação e usar os vetores para testar a própria implementação, sem pedir autorização. O texto deste documento está sob a licença CC BY 4.0; os schemas, os vetores e o conferir.mjs, sob a Apache-2.0.
- Arquivos
- Vetores de teste (.zip)Recibos, lotes, pontos e provas de ancoragem das revisões 1 e 2, assinados com chaves de teste, os inválidos, a canonicalização, os gênesis e o indice.json com o resultado esperado de cada um.
- Schema do humanproof/1JSON Schema, draft 2020-12.
- Schema do humanproof-acesso/1JSON Schema, draft 2020-12.
- Schema do humanproof/1, revisão 2JSON Schema, draft 2020-12.
- Schema do humanproof-acesso/1, revisão 2JSON Schema, draft 2020-12.
- SHA256SUMS.txtOs hashes dos cinco arquivos acima.
- Raízes da ICP-Brasil (JSON)As raízes em que o verificador deste site confia para o carimbo do tempo, baixadas do repositório oficial do ITI, com o SHA-256 de cada uma para conferir por outra fonte.
- Nota técnica para períciaPara o perito: como a evidência é produzida, o que cada hash permite conferir e como conferir sem a Hummand.
- Chave pública da Hummand (JWKS)A chave com que os recibos de produção são conferidos.
1. Convenções
As palavras DEVE, NÃO DEVE, DEVERIA e PODE têm o sentido da RFC 2119. Um verificador conforme é o que cumpre todo DEVE desta especificação e dá, para cada vetor do índice (seção 11), o resultado ali declarado.
A implementação de referência é a biblioteca humanproof-core, em Rust, a mesma que assina no servidor da Hummand, que roda no verificador de linha de comando humanproof-verify e, compilada para WebAssembly, no verificador público em labs.hummand.com.br/verificar. Onde este documento e a implementação de referência divergirem, a divergência é defeito a corrigir, e os vetores dizem qual dos dois está certo.
2. Modelo
- Emissor: a Hummand. Assina cada objeto com uma chave cuja parte pública está publicada.
- Tenant: o cliente da Hummand. Cada tenant tem duas cadeias próprias, a de atos e a de acessos, e nenhum recibo de um tenant referencia recibo de outro.
- Recibo do ato (
humanproof/1): emitido quando um ato termina, qualquer que seja o resultado. Positivo, negativo, expirado, cancelado, falha técnica e revogação são todos assinados. - Recibo de acesso (
humanproof-acesso/1): emitido quando alguém abre a evidência de um ato ou o dossiê compartilhado. Tentativa negada também é assinada. - Ponto de conferência (
humanproof-ponto/1e/2): afirmação assinada de quantos recibos uma cadeia tinha num instante e qual era o último. É o que torna detectável o corte do fim. - Prova de ancoragem (
humanproof-ancoragem/1): um ponto de conferência ou um recibo preso a um carimbo do tempo de uma Autoridade de Carimbo do Tempo (ACT), um terceiro que não é a Hummand. A do ponto atesta que a cadeia do tenant já tinha aqueles recibos naquele instante; a do recibo, que aquele recibo já existia, e se confere só com ele. - Verificador: qualquer pessoa ou programa que confere os objetos. Não precisa de acesso à Hummand além da chave pública.
- Revisão: a versão das regras de um formato, marcada no objeto pelo membro
revisao(seção 16). Objeto sem o membro é da revisão 1.
Nenhum dos formatos carrega biometria, imagem, template ou dado do titular em claro. O que diz respeito à pessoa entra como hash, e o schema de cada formato rejeita campo desconhecido.
3. Canonicalização
Todo hash e toda assinatura destes formatos são calculados sobre a forma canônica JCS (RFC 8785) do objeto, com uma restrição: números só como inteiros seguros.
- A entrada DEVE ser I-JSON (RFC 7493): UTF-8 válido, sem nomes de membro duplicados no mesmo objeto. Um verificador DEVE recusar o texto com nome duplicado, em qualquer profundidade, antes de qualquer outra conferência.
- Números DEVEM ser inteiros com |n| ≤ 2^53 − 1, escritos na entrada sem fração, sem expoente e sem
-0. Entrada com número fora disso DEVE ser recusada, e não normalizada:1.0,1e3,1.5,-0e9007199254740992são recusados. - Saída sem espaço em branco fora das strings.
- Membros de objeto ordenados pelos nomes, comparados como sequências de unidades de código UTF-16, não de bytes UTF-8. As duas ordens divergem para caracteres fora do plano básico:
"😀"(U+1F600, unidades D83D DE00) vem antes de"~"(U+FF5E). - Arrays mantêm a ordem.
- Strings escritas como
JSON.stringifyas escreve:"e\escapados com barra invertida;\b,\f,\n,\re\tna forma curta; os demais caracteres de controle abaixo de U+0020 como\useguido de quatro hexadecimais minúsculos; todo o resto literal em UTF-8, inclusive/, U+007F e U+2028. Escapes da entrada são desfeitos antes. - Números em decimal, sem zeros à esquerda;
true,falseenullliterais.
O arquivo canonicalizacao.json dos vetores traz cada uma dessas regras como par entrada e saída, com o hash da saída, e as entradas que DEVEM ser recusadas com canonico: null.
4. Hash
hash(x) = "sha256:" + hex(SHA-256(UTF-8(x))), com 64 hexadecimais minúsculos. Todo campo de hash destes formatos tem essa forma, e um verificador DEVE recusar o que não tiver.
- Hash de um objeto: o hash do JCS do objeto inteiro, assinatura inclusa. É o que entra no
prev_hashdo objeto seguinte, e por isso a cadeia prende também as assinaturas. - Gênesis de atos de um tenant:
hash("humanproof/1:genesis:" + tenant.id). - Gênesis de acessos de um tenant:
hash("humanproof-acesso/1:genesis:" + tenant.id).
Nos dois gênesis o hash é sobre a string concatenada, sem JCS. O arquivo genesis.json traz os valores dos tenants de teste.
5. Assinatura e chaves
- Payload assinado (revisão 1; a da revisão 2 está na seção 16.1): o JCS do objeto sem o membro
assinatura.kidealgficam dentro do payload e, portanto, cobertos pela assinatura. - Algoritmos:
ES256(ECDSA sobre P-256 com SHA-256) eEdDSA(Ed25519). O emissor da Hummand assina comES256, com a chave privada num módulo de chaves gerenciado, de onde ela não sai.EdDSAfaz parte do formato e é exercitado nos vetores. - Codificação: base64url sem preenchimento (RFC 4648 §5) dos bytes crus da assinatura. No
ES256,r || scom 32 bytes cada, como em JWS (RFC 7518 §3.4), nunca DER. NoEdDSA, os 64 bytes da assinatura. - Chaves: um JWKS (RFC 7517). Cada chave tem
kid,ktyecrv(EC/P-256ouOKP/Ed25519), as coordenadas públicas ealg. O JWKS do emissor da Hummand está emhttps://gateway.hummand.com.br/.well-known/humanproof/keys.json. - Seleção: o verificador DEVE escolher a chave pelo
kiddo objeto e DEVE exigir que oalgdo objeto corresponda ao tipo da chave. Não há negociação:algque não bate com a chave é falha de assinatura, antes de qualquer conta.kidausente do JWKS também. - Um JWKS pode ter mais de uma chave, e uma cadeia pode atravessar a troca de chave e de algoritmo: a cadeia é por hash e não depende de chave.
6. O recibo do ato humanproof/1
A estrutura exata está no JSON Schema humanproof-1.schema.json (draft 2020-12), que é parte desta especificação e rejeita campo desconhecido. Os membros obrigatórios:
| Membro | Conteúdo |
|---|---|
formato |
"humanproof/1" |
recibo_id, ato_id |
identificadores opacos |
tenant |
{ id, nome_publico } |
tipo |
tipo do ato, como presenca.recorrente ou mandato.pessoa |
nivel |
inteiro de 0 a 3: o nível efetivamente atingido, não o pedido |
resultado |
positivo, negativo, expirado, cancelado, falhou ou revogado |
motivo_codigo |
código curto do motivo, ou null |
sujeito |
ref_cliente_hash (HMAC-SHA256, com um segredo por tenant guardado pelo emissor, sobre a referência que o cliente deu à pessoa, na forma de hash, ou null) e, quando houve, referencia com o hash da referência comparada |
contexto |
hash do JCS do contexto que o cliente enviou e chaves_declaradas, os nomes das chaves; o conteúdo nunca |
consentimentos |
por camada: kind, versao, texto_hash e ts. texto_hash é o hash do identificador do consentimento (finalidade, escopo e versão), não do texto exibido; o texto de cada versão se pede ao emissor |
etapas |
nome, resultado (ok ou o motivo curto da falha) e ts |
fontes |
fonte, a versão do software do emissor (não a do componente de terceiro) e, quando houver, o componente usado |
garantias |
humano_vivo, mesma_pessoa_da_referencia, identidade_civil, consentiu (verdadeiro, falso ou null quando a etapa não ocorreu) e biometria_retida, sempre false |
mandato_ref, encadeamento, revisado_por |
referência ao mandato, posição num grupo de atos encadeados e marca de revisão humana, cada um null quando não se aplica |
politica_versao |
a política aplicada |
ts_criado, ts_concluido |
RFC 3339 em UTC, com sufixo Z |
prev_hash |
hash do recibo anterior do mesmo tenant, ou o gênesis de atos |
kid, alg, assinatura |
seção 5 |
Membros opcionais, por tipo de ato: backfill; escopo e mandatario na concessão de mandato; verificacao_mandato no uso de mandato; documento_anexo; assinatura_juridica; recibos_individuais e humanos_distintos em atos compostos; gatilho.
Três pontos de leitura:
contexto.hashsó confere com quem tem o contexto. Quem tem o contexto original recalculahash(JCS(contexto))sozinho; o verificador confere só a forma do hash. Ato sem contexto usa o hash de{}. Recibos emitidos antes de 2026-09-13 cujo contexto tinha caractere fora do ASCII guardam o hash de uma receita anterior, que escapava esses caracteres em vez de escrevê-los em UTF-8; para contexto só ASCII as duas receitas dão o mesmo valor.etapas[].tsé o instante em que a etapa terminou, quando o emissor o mediu. Quando não mediu, o emissor põe ali ots_concluidodo ato, e o recibo continua válido. Nada no recibo distingue os dois casos.inconclusivonunca é resultado. O ato que passa por revisão humana sai comopositivoounegativo, comrevisado_porpreenchido: o id do ato em que quem revisou provou a própria presença, na revisão feita pelo emissor, ou, desde 2026-10-29,apikey:<id>, a chave com que o cliente decidiu pela integração. A revisão decide o ato, não a presença:garantias.humano_vivoé sempre o veredito do componente.
7. Coerência do humanproof/1
Além do schema e da assinatura, o verificador DEVE aplicar as regras abaixo. Elas garantem que o recibo não se contradiz, não que o que ele afirma aconteceu fora dele.
resultadodo vocabulário;nivelentre 0 e 3;kidnão vazio; todo hash na forma da seção 4;garantias.biometria_retidafalso.positivoexige todas as etapasok.positivode nível 2 ou mais, semmandato_ref, exige etapapresencacomok,garantias.humano_vivoverdadeiro e, quando hásujeito.referencia,garantias.mesma_pessoa_da_referenciaverdadeiro.positivode nível 3 exigegarantias.identidade_civilverdadeiro.revisado_porsó empositivoounegativo.revogadoexigemandato_ref, o mandato revogado.- Tempos em RFC 3339;
ts_concluidonão antecedets_criado; nenhuma etapa depois dets_concluido; etapas em ordem não decrescente dets. Uma etapa pode antecederts_criado, porque o consentimento é colhido antes de o ato nascer. - Concessão de mandato.
escopoemandatarioandam juntos.escopo.acoesnão vazio;escopo.revogavelpresente;escopo.validade_ateem RFC 3339 e, na concessão (semmandato_ref), posterior ats_criado: mandato que nasce vencido não autoriza nada.mandatario.tipoépessoaouagente, emandatario.ref_hashé hash. - Uso de mandato.
verificacao_mandatoexigemandato_refe as quatro afirmaçõesvalido,dentro_escopo,dentro_validadeerevogado. Elas autorizam quando as três primeiras são verdadeiras e a última falsa, e o recibo épositivoexatamente quando autorizam.motivo_codigoprecisa ser sustentado por elas:mandato_invalidoexigevalidofalso;mandato_revogado,revogadoverdadeiro;mandato_vencido,dentro_validadefalso;fora_de_escopo,dentro_escopofalso. A etapaverificacao_mandatoéokexatamente quando as quatro autorizam.escopoeverificacao_mandatonunca aparecem no mesmo recibo.
O verificador não confere o mandato referenciado contra o recibo da concessão: quem quer a prova toda verifica os dois recibos, que estão na mesma cadeia do tenant.