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.
- Arquivos
- Vetores de teste (.zip)Recibos, lotes e pontos 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.
- SHA256SUMS.txtOs hashes dos três 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.
- 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.
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: 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 segredo do tenant 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 |
etapas |
nome, resultado (ok ou o motivo curto da falha) e ts |
fontes |
fonte, versão e, quando houver, o componente de terceiro 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.
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. - 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.
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.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.
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. 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 e a versão, não um escore.
- 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 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.
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. - Privacidade.
ref_cliente_hashé HMAC com segredo do tenant, porque o hash puro de um documento de identificação se reverte por enumeração.motivo_hash,contexto.hashetexto_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).
- 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.