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.
8. O recibo de acesso humanproof-acesso/1
Mesma canonicalização, hash, assinatura e chaves das seções 3 a 5. Schema em humanproof-acesso-1.schema.json, também fechado.
| Membro | Conteúdo |
|---|---|
formato |
"humanproof-acesso/1" |
acesso_id, ato_id |
o acesso e o ato cuja evidência foi aberta |
tenant |
{ id, nome_publico } |
ator |
{ papel, id }, com id opaco, nunca nome nem e-mail |
motivo_hash |
hash do JCS da string do motivo, isto é, do texto entre aspas; o texto não entra |
componentes |
um por registro tocado: registro, registro_id e desfecho (aberto, apagado, nunca_houve, negado ou erro) |
desfecho |
do acesso: concedido, negado ou erro |
ts_acesso |
RFC 3339 em UTC, com Z |
prev_hash |
hash do acesso anterior do tenant, ou o gênesis de acessos |
kid, alg, assinatura |
seção 5 |
Papéis e prefixos do ator.id:
papel |
prefixo | quem |
|---|---|---|
cliente |
usuario: |
usuário do painel do tenant |
operador_hummand |
operador: |
operação da Hummand |
revisor_hummand |
revisor: |
revisão humana da Hummand |
integracao |
apikey: |
integração do tenant |
terceiro |
compartilhamento: |
quem abriu, sem conta, um dossiê que o cliente compartilhou por link; o id é o do compartilhamento, nunca o da pessoa |
Coerência que o verificador DEVE aplicar:
concedidoexige ao menos um componente e nenhumnegado.negadoexige todos os componentesnegado; a lista vazia é aceita, porque a recusa pode vir antes de tocar registro.erroadmite qualquer mistura.ator.papelconhecido eator.idcom o prefixo do papel, seguido de pelo menos um caractere.ts_acessoem RFC 3339 comZ;prev_hashemotivo_hashna forma de hash;kidnão vazio.
O recibo de acesso cobre a abertura pelo caminho oficial, que é o único que o emissor enxerga.
9. A cadeia
Cada tenant tem uma cadeia de atos e uma de acessos, independentes, cada uma com o próprio gênesis. Um lote é verificado assim:
- Agrupar os objetos por
(formato, tenant.id). Cada grupo é uma cadeia. Um verificador que recebe um arquivo como uma cadeia só DEVE recusar o que mistura tenants ou formatos. - Calcular o hash de cada objeto e ligar cada um ao objeto cujo hash é o seu
prev_hash. A ordem de entrada não importa. - A cadeia é contínua quando há exatamente um objeto cujo
prev_hashnão é hash de outro objeto do lote, e cada objeto tem no máximo um sucessor. - Ler o início: se esse primeiro objeto aponta para o gênesis do formato e do tenant, a cadeia está verificada desde o gênesis; se aponta para outro hash, é contínua dentro do lote, de origem externa, e DEVE ser apresentada assim, nunca como completa.
- Problemas que o verificador DEVE relatar: mais de um início (buraco no meio ou objeto de outra história), dois objetos com o mesmo
prev_hash(bifurcação), ciclo e objeto repetido.
A cadeia não promete ordem de tempo: um ato expirado conclui na data de expiração, que pode ser posterior à de atos emitidos depois dele.
O que a cadeia não acusa. Remover os últimos objetos deixa uma cadeia contínua desde o gênesis, e nada dentro do lote diz quantos deveriam existir. Para isso existe o ponto de conferência.
10. O ponto de conferência
humanproof-ponto/1, só da cadeia de atos:
{
"formato": "humanproof-ponto/1",
"tenant": { "id": "..." },
"total": 128,
"ultimo_hash": "sha256:...",
"ts": "2026-09-13T21:00:00Z",
"kid": "...", "alg": "ES256", "assinatura": "..."
}
humanproof-ponto/2, das duas cadeias, ambas obrigatórias:
{
"formato": "humanproof-ponto/2",
"tenant": { "id": "..." },
"cadeias": {
"atos": { "total": 128, "ultimo_hash": "sha256:..." },
"acessos": { "total": 7, "ultimo_hash": "sha256:..." }
},
"ts": "2026-09-24T21:00:00Z",
"kid": "...", "alg": "ES256", "assinatura": "..."
}
total é quantos objetos a cadeia tinha até ultimo_hash, inclusive. Cadeia vazia tem total 0 e ultimo_hash igual ao gênesis daquela cadeia. Assinatura como na seção 5. O /1 continua aceito e não afirma nada sobre acessos: conferir acessos contra ele DEVE ser recusado, nunca aprovado por omissão.
Conferir um lote contra um ponto, por cadeia:
| Situação | Leitura |
|---|---|
| o ponto não confere (assinatura) ou é de outro tenant | contradiz |
total 0 e ultimo_hash igual ao gênesis |
confere: o ponto afirma que nada havia |
o lote contém ultimo_hash e, se começa no gênesis, na posição total |
confere |
o lote começa no gênesis e contém ultimo_hash em outra posição |
contradiz |
o lote começa no gênesis e não contém ultimo_hash |
contradiz: cortado, bifurcado ou reescrito, ou é anterior ao ponto |
o lote não começa no gênesis e não contém ultimo_hash |
inconclusivo: pode ser uma janela anterior ao ponto |
O verificador DEVERIA mostrar as duas contagens, a do lote e a do ponto, e não só a leitura.
O ponto vale pela distância no tempo. Um ponto pedido junto com o lote vem da mesma versão da história e não prova nada; o que prova é o ponto que o auditor guardou antes e compara depois.
11. Procedimento completo e vetores
Para cada objeto, nesta ordem, relatando cada etapa em separado:
- Ler como I-JSON, recusando nome duplicado, e identificar
formato. Formato desconhecido DEVE ser recusado. Identificar também a revisão (seção 16): semrevisao, revisão 1; comrevisao2, as regras das seções 16 a 20; outro valor DEVE ser recusado. - Validar contra o schema do formato.
- Selecionar a chave pelo
kide conferiralgcontra ela (seção 5). - Conferir a assinatura sobre o payload.
- Aplicar a coerência do formato (seções 7 e 8).
Um objeto é válido quando passa nas cinco. Para um lote, depois disso, verificar cada cadeia (seção 9) e, se houver ponto guardado, conferir cada cadeia contra ele (seção 10).
Os vetores acompanham este documento. Foram assinados com chaves de teste derivadas de rótulos públicos, sem valor fora dos testes: o escalar P-256 é o SHA-256 da string humanproof/1:chave-de-teste:p256 (a segunda, humanproof/1:chave-de-teste:p256:2), a semente Ed25519 é o SHA-256 de humanproof/1:chave-de-teste:ed25519, e as assinaturas são determinísticas (RFC 6979). Os arquivos de chave pública são jwks.json, jwks-rotacao.json e jwks-eddsa.json, e revisao-2/jwks.json para os vetores da revisão 2, com revisao-2/jwks-chave-vencida.json e revisao-2/jwks-chave-comprometida.json, que trazem a mesma chave com validade declarada e recusam o recibo válido.
indice.jsonlista cada vetor com a operação a aplicar e o resultado esperado.recibo: o objeto sozinho.cadeia: o array deve formar uma cadeia de um formato e um tenant.lote: o array agrupado por formato e tenant.ponto: o array conferido contra o ponto indicado.ancoragem: a prova conferida com o JWKS e a raiz confiável indicada emraizes.esperado.falhasdiz em que etapas a verificação falha (leitura, quando o texto não é I-JSON;schema,assinatura,coerencia,cadeia,ponto; na ancoragem,assinaturado ponto,caminhoecarimbo); vazio é vetor válido.esperado.validoé verdadeiro exatamente quandoesperado.falhasé vazio.esperado.inicioégenesisouexterno;esperado.pontoéconfere,inconclusivooucontradiz.canonicalizacao.json: pares de entrada e saída da seção 3.genesis.json: os gênesis dos tenants de teste e ummotivo_hashde exemplo.ancoragem/: as provas válidas, os carimbos de uma ACT de teste (RSA com SHA-512 e ECDSA P-256, emitidos pelo OpenSSL) e os certificados DER das raízes, a de teste e uma estranha.revisao-2/: os vetores da revisão 2 (seções 16 a 20), com a própriajwks.json: o recibo do ato, um recibo com duas assinaturas, um recibo adulterado, os inválidos da coerência nova (a dupla aprovação com o mesmo operador nas duas posições, uma extensão crítica desconhecida emcrit, um motivo do registro que não cabe no resultado) e um recibo da revisão 1 concluído depois da data de corte, o recibo de acesso, um lote conferido contra o ponto (lote.jsoneponto.json) e as provas de ancoragem de ponto e de recibo, com o carimbo e a cabeça que elas usam.
Uma implementação conforme dá, para cada entrada do índice, exatamente as falhas, o início e a leitura declarados. O mesmo vetor pode aparecer com operações diferentes: o lote cortado no fim é uma cadeia válida desde o gênesis e contradiz o ponto, e é isso que mostra para que o ponto existe.
12. Versões
Mudança de formato é formato novo (humanproof/2, por exemplo); um formato publicado não é editado. Esta especificação pode ganhar valores aditivos dentro de um formato, como o papel terceiro acrescentado em 2026-10-19, e o documento registra cada um com data. Na versão 1.1 do documento, do mesmo dia da 1.0, a recusa de nome duplicado passou de DEVERIA a DEVE, com o vetor invalidos/nome-duplicado.json. A versão 1.2, também de 2026-10-19, acrescenta o formato humanproof-ancoragem/1 (seção 15). A versão 1.3, de 2026-10-20, acrescenta a ele a prova de recibo (recibo_hash), antes de o formato ter qualquer prova emitida em produção. A versão 1.4, de 2026-10-29, corrige três descrições que não batiam com o emissor (o texto_hash é o hash do identificador do consentimento, não do texto; a versão em fontes é a do software do emissor, não a do componente; o segredo do ref_cliente_hash fica com o emissor) e descreve o revisado_por da revisão feita pelo cliente; nenhum formato mudou. A versão 2.0, de 2026-10-30, acrescenta a revisão 2 dos formatos (seções 16 a 20), com a data de corte, e as licenças do texto e dos artefatos; o que descreve a revisão 1 não mudou. A versão 2.1, também de 2026-10-30, corrige descrições sem mudar formato, schema nem vetor: a ancoragem ainda não está ativa em produção (seções 13 a 15 e 20); sem a chave, o compromisso não liga dois recibos da mesma pessoa, mas referencia.hash é igual em recibos que usaram a mesma imagem (17.2); participantes e mandatario trazem ref_sal (17 e 17.6); o recibo identifica o componente, e a versão registrada depende da revisão (13); o que cada hash permite conferir, por revisão (14); os vetores da revisão 2 e esperado.valido (11); a validade por chave é admitida pelo formato, não exigida (16.2). A versão 2.2, também de 2026-10-30, acrescenta vetores sem mudar formato nem schema: quatro recibos inválidos em revisao-2/invalidos/ (regra 11 da dupla aprovação, crit, motivo fora do resultado e revisão 1 depois do corte) e dois JWKS com a chave vencida ou comprometida antes da emissão, contra os quais o recibo válido falha na assinatura (11). A versão 2.3, de 2026-11-01, acrescenta a data de corte do ponto (seção 16) e corrige a conferência da validade por chave, que passava sem conferir quando o instante da emissão não era RFC 3339 e agora recusa; nenhum formato, schema ou vetor muda. Um verificador anterior recusa o valor que não conhece, porque os schemas são fechados; nunca o aceita por omissão.
13. O que os formatos não provam
- O recibo afirma o que as fontes declaradas sustentam, e nada além. A presença viva é o veredito de um componente de terceiro, identificado no recibo, que decide por critério probabilístico; o recibo registra o veredito, não um escore. Na revisão 1, a versão em
fontesé a do software do emissor, não a do componente; na revisão 2,versao_componentepode sernao_informada. - O recibo não prova a legitimidade da referência que o cliente enviou: uma boa comparação contra a referência errada é confiança alta na pessoa errada.
- A cadeia e o ponto cobrem o que foi emitido. Um ato que nunca virou recibo não aparece em cadeia nenhuma.
- O recibo de acesso cobre a abertura pelo caminho oficial. Acesso por fora dele não gera recibo.
- Coerência é ausência de contradição interna, não confirmação externa.
- O compromisso da referência (17.2) prova de quem é o recibo só para quem tem a chave de referência e a referência; ele não diz se a referência que o cliente cadastrou é de fato da pessoa.
- O carimbo atesta quando o ponto ou o recibo existia, não que o que os recibos afirmam aconteceu. Recibos emitidos entre uma ancoragem e a seguinte ficam sem âncora até ela. A ancoragem ainda não está ativa em produção: até lá, nenhum recibo nem ponto do emissor tem âncora.
14. Segurança
- Confiança na chave. A assinatura prova só que quem tinha a chave privada assinou. O verificador DEVE obter o JWKS pelo endereço do emissor ou guardá-lo de antemão, e NÃO DEVE confiar numa chave que veio no mesmo pacote que os recibos: quem forja um pacote forja também a chave que vai dentro dele. A chave do pacote serve só para comparar.
- Nomes duplicados. Dois parsers que resolvem nomes duplicados de modos diferentes (o primeiro vence, o último vence) leriam o mesmo texto como recibos diferentes. Por isso a entrada DEVE ser I-JSON, e um verificador DEVE recusar o texto com nome duplicado em vez de escolher um dos valores. O vetor
invalidos/nome-duplicado.jsoné um recibo negativo verdadeiro com um segundoresultado, positivo, no começo do objeto: quem fica com o último valor confere a assinatura, e quem fica com o primeiro lê um positivo. - Chave de referência. Quem tiver a chave de referência de um tenant consegue testar referências candidatas contra os compromissos dos recibos daquele tenant. Ela não é trocada (não há rotação), porque trocá-la impediria conferir os recibos antigos, e deve ser guardada como senha.
- Privacidade. Na revisão 1,
ref_cliente_hashé HMAC com um segredo por tenant, guardado pelo emissor, porque o hash puro de um documento de identificação se reverte por enumeração; só o emissor o recompõe. Também na revisão 1,texto_hashsó é conferível por quem tem a finalidade, o escopo e a versão do consentimento. Na revisão 2,ref_compromissoé conferível por quem tem a chave de referência e a referência (17.2), etexto_hashpor qualquer pessoa com o texto publicado (17.3). Nas duas revisões,motivo_hashecontexto.hashsó são conferíveis por quem tem o texto original. - Ponto. Ver a seção 10: só o ponto guardado antes da conferência acusa o corte do fim, ou o ponto ancorado (seção 15), quando a ancoragem estiver ativa em produção.
- Raízes da ancoragem. O carimbo vale o que vale a raiz em que o verificador confia. As raízes DEVEM vir da fonte oficial (para a ICP-Brasil, o ITI), nunca do mesmo pacote que traz a prova.
15. A prova de ancoragem humanproof-ancoragem/1
O emissor faz periodicamente uma árvore de Merkle com duas espécies de folha e pede a uma ACT um carimbo do tempo RFC 3161 sobre a raiz:
- o
humanproof-ponto/2assinado de cada tenant com recibo novo, que protege a cadeia inteira contra o corte do fim; - cada recibo emitido desde a ancoragem anterior, de ato ou de acesso, pelo hash (seção 4), que prova a existência daquele recibo para quem só tem ele.
Cada folha tem a sua prova. A do ponto:
{
"formato": "humanproof-ancoragem/1",
"ponto": { "formato": "humanproof-ponto/2", "...": "o ponto assinado do tenant" },
"caminho": [
{ "lado": "direita", "hash": "sha256:..." },
{ "lado": "direita", "hash": "sha256:..." }
],
"raiz": "sha256:...",
"carimbo": "<token RFC 3161, DER, em base64 com preenchimento>"
}
A do recibo troca ponto por recibo_hash, o hash do recibo:
{
"formato": "humanproof-ancoragem/1",
"recibo_hash": "sha256:...",
"caminho": [ { "lado": "esquerda", "hash": "sha256:..." } ],
"raiz": "sha256:...",
"carimbo": "<token RFC 3161, DER, em base64 com preenchimento>"
}
formato, caminho, raiz e carimbo são obrigatórios, com exatamente um entre ponto e recibo_hash; nenhum outro membro é aceito, e nos itens de caminho só lado e hash. A prova não tem assinatura da Hummand: o ponto dentro dela é assinado, o recibo é assinado e está nas mãos de quem confere, e o carimbo é assinado pela ACT. A prova de recibo não traz nada do recibo além do hash: nem tenant, nem ato.
A árvore segue a RFC 6962 §2.1, sobre os 32 bytes de cada hash:
- folha de ponto:
SHA-256(0x00 || JCS(ponto)), com a assinatura do ponto; - folha de recibo:
SHA-256(0x00 || "humanproof-ancoragem/1:recibo:" || recibo_hash), com o hash na formasha256:<hex>, em UTF-8. A pré-imagem da folha de ponto começa por{e a de recibo porh: uma nunca se passa pela outra; - nó:
SHA-256(0x01 || esquerda || direita); - com n folhas, n ≥ 2, a raiz é o nó das raízes das k primeiras e das n − k restantes, sendo k a maior potência de dois menor que n; com uma folha, a raiz é a folha.
caminho lista os irmãos da folha até a raiz, do mais baixo ao mais alto; lado diz de que lado o irmão está. Subir é: começar na folha e, para cada item, fazer o nó com o irmão à esquerda ou à direita. O caminho vazio é a árvore de uma folha.
O carimbo é um TimeStampToken (RFC 3161 §2.4.2): um ContentInfo CMS SignedData (RFC 5652) com conteúdo TSTInfo. O messageImprint usa SHA-256, e o hashedMessage são os 32 bytes da raiz.
Conferir uma prova, relatando cada etapa:
- Forma: os membros acima,
raiz,recibo_hashe cadahashna forma da seção 4,carimboem base64. - Folha: na prova de ponto, o ponto confere com o JWKS da Hummand, como na seção 10. Na prova de recibo, quem confere tem o recibo e verifica que o hash dele (seção 4) é
recibo_hash; a prova vale para aquele recibo e nenhum outro. - Caminho: a folha, subindo pelo caminho, dá exatamente
raiz. - Carimbo, sobre os bytes de
raiz:- o
TSTInfocarimba esses 32 bytes, com SHA-256; - há um só
SignerInfo, e seus atributos assinados têmcontent-typeigual aid-ct-TSTInfoemessage-digestigual ao resumo doTSTInfo, com odigestAlgorithmdoSignerInfo(SHA-256, SHA-384 ou SHA-512); - a assinatura, sobre a codificação DER dos atributos assinados com a etiqueta de SET, confere com a chave do certificado da ACT, que vem no carimbo e é identificado pelo
sid. Algoritmos: RSA PKCS #1 v1.5 com SHA-256, SHA-384 ou SHA-512, e ECDSA P-256 com SHA-256; - o certificado da ACT tem uso estendido
timeStamping; - a cadeia sobe do certificado da ACT até uma das raízes confiáveis do verificador, cada certificado assinado pelo de cima e válido no instante
genTime, e todo emissor intermediário combasicConstraintsde AC.
- o
- Na prova de ponto, o
tsdo ponto não é posterior aogenTime.
A prova é válida quando passa em todas. O verificador DEVERIA mostrar o genTime, a política, a ACT e a raiz a que a cadeia chegou.
O que cada uma prova: a de recibo, que aquele recibo existia no instante genTime, o que basta a quem recebe um dossiê com ele, sem ver nenhum outro recibo do tenant. A de ponto, que a cadeia do tenant tinha total recibos terminando em ultimo_hash naquele instante; o lote do tenant é conferido contra esse ponto como contra um ponto guardado (seção 10), e o auditor não precisa ter guardado um ponto antes.
O que a conferência offline não faz, e o verificador DEVE dizer quando relevante: consultar a lista de revogação da ACT, que exige rede; conferir o atributo signing-certificate do carimbo (a assinatura e a cadeia já prendem o certificado); aceitar RSASSA-PSS.
Estado em produção. A ancoragem ainda não está ativa em produção. O formato, os vetores e o verificador estão prontos; o emissor ainda não emite provas de ancoragem.
16. A revisão 2
Desde 2026-10-30 os quatro formatos têm uma revisão 2, marcada pelo membro "revisao": 2. Não é formato novo: formato continua humanproof/1, humanproof-acesso/1, humanproof-ponto/2 e humanproof-ancoragem/1. Objeto sem revisao é da revisão 1 e segue as seções 6 a 10 e 15; objeto com revisao 2 segue as seções 16 a 20; qualquer outro valor de revisao DEVE ser recusado. Canonicalização, hash e cadeia (seções 3, 4 e 9) são os mesmos nas duas revisões, e uma cadeia pode ter recibos das duas.
Data de corte: 2026-10-30T05:21:39Z, o instante em que o emissor da Hummand passou a emitir a revisão 2. Um recibo do ato sem revisao com ts_concluido posterior a esse instante DEVE ser recusado na coerência, mesmo com a assinatura válida: depois do corte, a revisão 1 não serve para fugir das regras da 2. Os recibos da revisão 1 concluídos até o corte continuam válidos.
Data de corte do ponto: 2026-11-01T22:51:16Z, o instante em que o emissor deixou de emitir o humanproof-ponto/1 e o /2 sem revisao. Um ponto sem revisao com ts posterior a esse instante, ou com ts que não é RFC 3339, DEVE ser recusado na coerência, mesmo com a assinatura válida, pelo mesmo motivo do recibo: sem o corte, o ponto da revisão 1, que não tem validade por chave, serviria para fugir da revogação da revisão 2. É outra data porque o ponto da revisão 1 continuou saindo depois do corte do recibo. Os pontos sem revisao emitidos até esse instante continuam válidos.
Schemas da revisão 2: humanproof-1-revisao-2.schema.json e humanproof-acesso-1-revisao-2.schema.json, fechados como os da revisão 1.
16.1 Assinaturas
Na revisão 2, kid, alg e assinatura saem e entra assinaturas, uma lista de { papel, kid, alg, valor }:
- cada entrada assina o JCS de
{ "recibo": <o objeto sem o membro assinaturas>, "assinatura": { "papel", "kid", "alg" } }, de modo que o papel, a chave e o algoritmo de cada assinatura ficam cobertos por ela mesma, e outra assinatura pode entrar depois sem invalidar as anteriores; valorcom a codificação da seção 5;- papéis:
emissor, obrigatório, e os reservadosemissor_complementar,testemunhaecliente. O verificador DEVE exigir ao menos uma assinaturaemissore DEVE conferir todas as que houver; - o hash do objeto (seção 4) continua sendo o do JCS do objeto inteiro, com as assinaturas.
A mesma regra vale para o recibo de acesso e para o ponto da revisão 2.
16.2 Chaves com validade
Na revisão 2, cada chave do JWKS PODE trazer valida_de, valida_ate, estado (ativa, aposentada ou comprometida) e, quando comprometida, comprometida_desde. O verificador DEVE recusar a assinatura cuja chave, no instante ts_emitido do objeto, ainda não valia, já não valia ou estava comprometida; estado desconhecido também é recusa. A validade por chave é admitida pelo formato, não exigida: chave sem esses membros vale sempre, como na revisão 1, e não ter prazo declarado não torna a chave inválida. A chave atual do emissor não tem prazo declarado. kid repetido no JWKS DEVE ser recusado, porque o verificador teria de escolher uma das chaves. Uma chave aposentada continua publicada no JWKS do emissor, para que recibos antigos continuem conferíveis depois de uma troca.
17. O recibo do ato, revisão 2
| Membro | Conteúdo |
|---|---|
formato, revisao |
"humanproof/1" e 2 |
recibo_id, ato_id |
identificadores opacos |
emissor |
{ id, jwks_uri }: quem emitiu e onde estão as chaves |
tenant |
{ id, nome_publico } |
tipo |
tipo do ato, como na revisão 1 |
resultado |
positivo, negativo, expirado, cancelado, falhou ou revogado |
motivo |
null no positivo; nos demais, um código do registro daquele resultado (17.1) |
nivel |
{ exigido, atingido }, inteiros de 0 a 3: o que a política pedia e o que o ato atingiu |
politica |
{ versao, hash }: a versão e o hash do JCS da política aplicada ao tipo |
sujeito |
ref_compromisso e ref_sal, juntos ou ambos ausentes, e referencia quando houve comparação (17.2) |
contexto |
{ hash, chaves_declaradas }, com as chaves em ordem e sem repetição |
consentimentos |
[{ camada, versao, idioma, texto_hash, ts }], com camada em geral, biometrico ou escopo (17.3) |
etapas |
[{ nome, resultado, ts, medido }]; medido diz se o instante foi medido ou herdado do fim do ato |
fontes |
[{ fonte, fornecedor, componente, versao_componente, versao_adaptador, ambiente, certificacao? }]; versao_componente pode ser nao_informada; certificacao = { esquema, laboratorio, relatorio, data } |
garantias |
humano_vivo, mesma_pessoa_da_referencia, identidade_civil e consentiu, cada uma { valor, sustentada_por }; biometria_retida, sempre false (17.4) |
evidencia_tecnica |
o que a fonte mediu, sem escore: deteccao_ataque, deteccao_injecao e dispositivo quando houver, e politica_de_limiar com o id e o hash da política de comparação, nunca os valores calibrados |
participantes |
[{ papel, ref_compromisso, ref_sal, presenca_recibo_hash? }], com ref_compromisso e ref_sal calculados como em 17.2, com papel em sujeito, assistente, mandatario, testemunha, revisor ou operador |
revisao_humana |
null ou { via, revisor, protocolo, ts } (17.5) |
mandato |
null ou exatamente um de concessao, uso e revogacao (17.6) |
ingredientes |
[{ papel, alg, hash }]: o termo exibido, o documento anexo, o gatilho e outras evidências, só pelo hash |
ts_criado, ts_concluido, ts_emitido |
RFC 3339 em UTC com milissegundos; ts_emitido é o instante da assinatura |
expira_em |
o prazo do ato |
retencao |
{ ate, base }: até quando o emissor guarda o registro do ato, e por qual base |
prev_hash |
hash do recibo anterior do tenant, ou o gênesis de atos (seção 4) |
assinaturas |
seção 16.1 |
Opcionais: inclusao, reservado para a prova de inclusão num log; extensoes, um mapa { "<espaço de nomes>": { ... } }; e crit, a lista das extensões que o verificador precisa entender. Extensão citada em crit e ausente, ou citada e desconhecida do verificador, DEVE ser recusada; as demais extensões PODEM ser ignoradas.
17.1 Motivos por resultado
resultado |
motivo |
|---|---|
positivo |
null |
negativo |
presenca_negada, comparacao_negada, identidade_negada, passkey_negada, consentimento_recusado, revisao_negou, mandato_invalido, mandato_revogado, mandato_vencido, fora_de_escopo |
expirado |
prazo |
cancelado |
cancelado_pelo_cliente, cancelado_pela_pessoa |
falhou |
falha_tecnica_fonte, falha_tecnica_emissor |
revogado |
revogado_pelo_mandante |
17.2 O compromisso da referência
O recibo não traz a referência que o cliente deu à pessoa, como CPF, matrícula ou número de benefício. Traz:
ref_sal: 16 bytes aleatórios, novos a cada recibo, em 32 hexadecimais;ref_compromisso="sha256:" + hex(HMAC-SHA256(chave, bytes(ref_sal) || UTF-8(referência))), em quechaveé a chave de referência do tenant, de 32 bytes, ebytes(ref_sal)são os 16 bytes que o hexadecimal representa.
Sem a chave, o compromisso não revela a referência nem liga dois recibos da mesma pessoa, porque o sal muda; referencia.hash, quando presente, é igual em recibos que usaram a mesma imagem. Com a chave, que o cliente obtém no painel, ele recalcula o compromisso a partir da referência e confere sozinho que o recibo é daquela pessoa. referencia, quando houve comparação, é { tipo, origem, hash, anexada_em }: o hash da imagem de referência usada, a origem (api, painel, ato_anterior ou base_oficial) e quando ela foi anexada. Quem guardou a imagem confere o hash.
17.3 O texto do consentimento
texto_hash é hash(JCS(texto)): o SHA-256 da forma canônica da string do texto que a pessoa viu, na versão e no idioma indicados. Os textos estão publicados em https://hummand.com.br/consentimentos, com o hash de cada um, e um texto publicado não muda: redação nova é versão nova. ts é o instante em que o emissor registrou o aceite, pelo relógio dele. É a diferença para a revisão 1, cujo texto_hash era o hash do identificador do consentimento (seção 6).
17.4 Quem sustenta cada garantia
valor é verdadeiro, falso ou null (a etapa não ocorreu). sustentada_por diz de onde vem a afirmação: componente (o componente técnico da fonte), revisao (revisão humana), declaracao_cliente, base_oficial ou mandato; é null exatamente quando valor é null.
17.5 Revisão humana
via é emissor ou cliente. Na revisão pelo emissor, revisor é o hash do recibo em que quem revisou provou a própria presença; na revisão pelo cliente, é apikey:<id>, a chave com que a integração decidiu. A revisão decide o ato, não a presença: nenhuma garantia biométrica é sustentada por revisao.
17.6 Mandato
concessao:{ escopo, mandatario }, nos tiposmandato.pessoaemandato.agente.escopotrazacoes,limitestipados ({ valor, unidade, moeda?, janela? }),validade_ateerevogavel;mandatariotraztipo(pessoaouagente, coerente com o tipo do ato),ref_compromissoeref_sal, calculados como em 17.2, e PODE trazerchave_agente.uso:{ mandato_recibo_hash, acao, valores_hash, executor, nonce, expira_em, chave_agente?, verificacao }, no tipoato.delegado, comverificacao={ valido, dentro_escopo, dentro_validade, revogado }.revogacao:{ mandato_recibo_hash }, no resultadorevogado.
O mandato é apontado pelo hash do recibo da concessão, não por um id: quem tem os dois recibos confere que um aponta para o outro.
18. Coerência da revisão 2
Além do schema e das assinaturas (16.1 e 16.2), o verificador DEVE aplicar:
motivonullexatamente no positivo, e do registro do resultado nos demais (17.1).- Positivo exige
nivel.atingido≥nivel.exigidoe todas as etapasok. - Positivo de nível 2 ou mais, fora do
ato.delegado, exige etapapresencaokehumano_vivoverdadeiro sustentado porcomponente, com a fontehummand.presencadeclarada. Comsujeito.referencia, exige também etapacomparacaookemesma_pessoa_da_referenciaverdadeira sustentada porcomponente. - Positivo de nível 3 exige
identidade_civilverdadeira sustentada porbase_oficial. Positivo de nível 1 exige etapapasskeyok. ato.delegadoé sempre de nível 0: não há humano novo.- Garantia
nullsemsustentada_por, e garantia afirmada comsustentada_por. Nenhuma garantia biométrica sustentada porrevisao. - Etapa biométrica (
presenca,comparacao) exige consentimento da camadabiometricoanterior a ela econsentiuverdadeiro. - Nenhuma etapa repetida;
ts_criado≤ts_concluido≤ts_emitido; etapas e consentimentos não posteriores ats_concluido; etapas em ordem não decrescente. contexto.chaves_declaradasem ordem e sem repetição;ref_compromissoeref_saljuntos.revisao_humanasó em positivo ou negativo; pelo emissor, com o hash de um recibo; pelo cliente, comapikey:.operador.dupla: a dupla aprovação são duas posições encadeadas, e só a que fecha o par lista operadores. Quando o recibo lista dois ou mais participantesoperador, são no máximo dois, comref_compromissodiferentes (pessoas distintas), e o primeiro trazpresenca_recibo_hash, o hash do recibo da posição anterior; o segundo é o próprio ato e não traz presença. Com menos de dois operadores listados (a primeira posição), a regra não se aplica.- Mandato: cada variante no tipo de 17.6;
ato.delegadoexigeuso; o uso é positivo exatamente quando a verificação autoriza (as três primeiras afirmações verdadeiras erevogadofalso), com omotivosustentado por ela, e não conclui positivo depois deexpira_em; a concessão temvalidade_ateposterior ats_criado. critcomo na seção 17.
Como na revisão 1, coerência é ausência de contradição interna, não confirmação externa.
19. Acesso e ponto, revisão 2
Recibo de acesso. O mesmo papel da seção 8, com revisao 2, emissor, ts_emitido e assinaturas (16.1), e:
ato_recibo_hash: o hash do recibo do ato cuja evidência foi aberta, além doato_id;motivo_hash=hash(JCS({ "motivo": <texto>, "sal": <motivo_sal> })), commotivo_salno próprio recibo: sem o sal, o mesmo motivo daria o mesmo hash em todos os acessos;- cada componente traz também
conteudo_hash, o hash do que foi entregue, emodo,telaouarquivo; cada um pode sernull.
A coerência da seção 8 vale igual.
Ponto de conferência. O humanproof-ponto/2 da revisão 2 ganha revisao, emissor, assinaturas e ponto_anterior_hash, o hash do ponto anterior do mesmo tenant (null no primeiro): os pontos de um tenant formam uma cadeia, e um ponto suprimido do meio fica visível para quem guardou os vizinhos. cadeias passa a ser um mapa aberto, em que atos e acessos são obrigatórios e outras cadeias podem entrar depois. O ts vai em milissegundos. A conferência de um lote contra o ponto é a da seção 10.
20. Ancoragem, revisão 2
A prova humanproof-ancoragem/1 da revisão 2 traz, além do que a seção 15 descreve, revisao, arvore, tamanho e cabeca_anterior:
- Árvore completada. As folhas reais, pontos e recibos como na seção 15, são seguidas de folhas de preenchimento até
tamanho, a menor potência de dois que é ao mesmo tempo ≥ 1024 e ≥ o número de folhas reais. A folha de preenchimentoiéSHA-256(0x00 || "humanproof-ancoragem/1:preenchimento:" || i), comiem decimal. Assim o tamanho da árvore não revela quantos clientes e recibos houve no ciclo.arvoreé a raiz dessa árvore, e o caminho tem exatamente log2(tamanho) irmãos. - Cabeça encadeada. O carimbo da ACT cobre a cabeça, não a árvore:
cabeça = SHA-256(0x02 || cabeça anterior || arvore || tamanho), com a cabeça anterior em 32 bytes (32 zeros no primeiro ciclo) etamanhoem 8 bytes big-endian.raizé essa cabeça, ecabeca_anterioré a do ciclo anterior, ounull. - Lista pública. O emissor publica as cabeças dos ciclos em
https://gateway.hummand.com.br/v1/ancoragens/cabecas, com a árvore, o tamanho, a anterior e o carimbo de cada uma. Quem guarda a lista vê que cada ciclo cobre o anterior e que o emissor não carimbou duas histórias.
Conferir a prova da revisão 2: as etapas da seção 15, com a folha subindo até arvore em vez de raiz; o caminho com o comprimento da árvore completa; tamanho potência de dois e ≥ 1024; e raiz igual à cabeça calculada de cabeca_anterior, arvore e tamanho.
Estado em produção. A ancoragem ainda não está ativa em produção; a lista existe e fica vazia até lá.