Verificar uma prova offline
01O que este documento permite #sec_e0af9bcf
O REVIEWIT produz, a cada assinatura, uma atestação selada da qual se pode extrair um pacote de verificação: um arquivo que contém tudo o que é necessário para refazer a demonstração em outro lugar.
Este documento é a especificação completa deste formato. É publicado por uma razão precisa: permitir que um terceiro verifique uma prova sem nós, e mesmo sem nos acreditar. Ele descreve com bastante exatidão a canonicalização, a construção das árvores e a verificação das provas de inclusão para que um auditor, uma direção de sistemas de informação ou um gabinete independente possa escrever seu próprio verificador e obter o mesmo veredicto que o nosso.
Esta é a forma mais elevada da garantia que o REVIEWIT anuncia. Uma plataforma que dissesse « confiem em nós, está selado » pediria exatamente o que este documento torna desnecessário.
02A quem se destina #sec_c1bc9732
A um leitor técnico: a pessoa que verificará uma prova, ou aquela que escreverá a ferramenta que a verifica. Não é necessário para ler um relatório de auditoria, nem para usar o REVIEWIT.
Um auditor que deseje apenas obter um veredicto não precisa ler este texto: a tela « Verificar uma prova » do Workspace Center e a ferramenta de linha de comando aplicam este contrato, sem desvio. Este documento existe para aqueles que se recusam a confiar em um ou no outro — e eles têm razão de poder.
03O que você precisa #sec_48297a56
Quatro elementos, e eles não vêm do mesmo lugar:
- O pacote de verificação — o arquivo em si. Ele é entregue a você com a prova que lhe é oposta, ou você o baixa do REVIEWIT.
- Um verificador — aquele que você escreve a partir deste documento, ou aquele que o REVIEWIT disponibiliza. Ambos devem render o mesmo veredicto; se divergirem, é um defeito, e nos diz respeito.
- O chaveiro público da autoridade — servido por este portal no endereço /.well-known/seal-keyring.json, e entregue com o anexo de segurança do seu contrato. Ele carrega as chaves públicas com as quais se controla a assinatura do selo, e somente ela. Todo o resto — a impressão digital, as raízes de Merkle, o encadeamento, a prova de inclusão — é verificado sem nenhuma chave, a partir do único arquivo.
- A impressão digital da chave raiz — portada pelo anexo de segurança assinado com seu contrato. É ela, e somente ela, que estabelece que o chaveiro que você baixou é realmente o nosso (§8.3).
04O que o arquivo sozinho estabelece, e o que ele não estabelece #sec_192edcae
Sem chaveiro, um pacote permanece inteiramente verificável em sua coerência interna: que o manifesto produz bem a impressão anunciada, que as listas produzem bem as raízes atestadas, que um elemento dado figura bem no perímetro selado.
O que o arquivo sozinho não estabelece, é que este conteúdo foi selado por nós. Um pacote pode se declarar selado sem o ser: esta declaração não vale nada enquanto a assinatura da autoridade não tiver sido controlada com sua chave pública. Um verificador conforme deve, portanto, distinguir estes dois constatos e nunca apresentar o segundo como adquirido a partir do primeiro.
051. Canonicalização, versão 1 #sec_53b1cef4
Um documento canônico é um JSON UTF-8 sem BOM, sem espaço nem retorno à linha, tal que:
- as chaves de um objeto são ordenadas por ordem ordinal dos pontos de código;
- as cadeias são normalizadas Unicode NFC; somente ", \ e os caracteres de controle são escapados; o não-ASCII não é escapado;
- os únicos números são inteiros assinados em 64 bits; nenhum flutuante; uma proporção se expressa para dez mil;
- os instantes estão em tempo universal, no formato yyyy-MM-ddTHH:mm:ss.ffffffZ — seis decimais, o sétimo truncado; as datas civis no formato yyyy-MM-dd;
- um valor não aplicável está ausente; não existe nem null, nem cadeia vazia que faça as vezes de ausência;
- as sequências de bytes estão em hexadecimal minúsculo.
Um documento contendo um null, um flutuante ou uma chave duplicada não é um documento canônico e é recusado.
062. Hashing #sec_9d21b1bf
SHA-256, identificador "SHA-256" portado em cryptography.hash do manifesto.
073. Esquema de árvore, versão 1 #sec_3b2d707a
| Elemento | Definição |
|---|---|
| Prefixos de domínio | folha 0x00, nó 0x01, árvore vazia 0x02, raiz 0x03 |
| Folha | H(0x00 ‖ aleatório ‖ conteúdo) — o aleatório tem 32 bytes, próprio para cada folha |
| Nó | H(0x01 ‖ esquerda ‖ direita) |
| Nível ímpar | o nó órfão é promovido tal qual ao nível superior, nunca duplicado |
| Engajamento | objeto canônico `{"domain":{"empty":2,"leaf":0,"node":1,"root":3},"hash":"SHA-256","leafCount":n,"schema":1,"tree":"scope","decisions"}` |
| Raiz publicada | H(0x03 ‖ engajamento ‖ raiz bruta) para n ≥ 1 |
| Raiz do lote vazio | H(0x02 ‖ engajamento) com leafCount = 0 |
Ordem das folhas. Chave composta (SHA-256(identidade), identidade), comparada byte a byte em lexicográfico não assinado, o segundo termo desempatando apenas as igualdades do primeiro. Duas identidades idênticas em um lote são uma recusa.
Duas árvores. scope : o conteúdo da folha é a identidade estável canônica {"criterion":…,"object":…,"type":…}. decisions : o conteúdo é a decisão canônica (§5). Mesmo ordem, mesma posição para um mesmo item.
Aleatórios. Derivados por SEAL da geração cryptography.secretGeneration, por (árvore, posição). O segredo não figura em nenhum pacote.
084. Prova de inclusão #sec_b535f3ee
{"commitment":{…},"content":hex,"path":[{"side":"left"|"right","sibling":hex} | {"promoted":true} …],"position":p,"root":hex,"salt":hex,"schema":1}
Verificação : recalcular a folha, subir o caminho exigindo a cada nível exatamente o que a estrutura impõe — um irmão do lado certo quando o índice tem um vizinho, uma promoção quando o índice é o último de um nível ímpar —, então H(0x03 ‖ engajamento ‖ nó) e comparar com a raiz atestado. Um caminho mais curto ou mais longo que a estrutura é uma recusa.
095. Manifesto, versão 1 #sec_9dbbc182
Blocos do objeto raiz (chaves ordenadas) : actors, context, cryptography, decisions, governance, header, qualification, scope, snapshot, statement.
- header : attestation, canonicalization (1), complementRank, lot, originLot (ausente para o lote inicial), schema (1).
- decisions[i] : actor, decidedAt, exceptionUntil, identity, justification, origin ( human | defaultRule | systemClosure | nonDecidedUncovered ), previous, rule ( id, motive, version — presente se e somente se a origem não for humana), verdict.
- qualification : closure, conflictOfInterest, coverage ( complete ), decisionMode, origins (quatro contagens, calculadas).
- governance.indicators : os quatro inteiros com collected:true, ou {"collected":false}.
- cryptography : hash, preflight ( {"state":"unavailable"} ou referência), secretGeneration, trees.scope e trees.decisions ( commitment, root ).
Forma selada. A impressão selada — aquela que a ancoragem carrega — é SHA-256 da forma canônica onde scope e decisions são substituídos por seu engajamento {"count":n,"hash":SHA-256(lista canônica)}. As listas viajam apenas no pacote completo; as raízes de Merkle já os engajam.
106. Pacotes #sec_de906bc8
| Campo | Completo | Pontual |
|---|---|---|
| specification | REVIEWIT-SignOff-Verification/1 | idem |
| kind | complete | pontual |
| attestation | forma selada | forma selada |
| digest | SHA-256(attestation) | idem |
| scope.items, decisions | listas completas, em ordem canônica | — |
| salts.scope, salts.decisions | todos os aleatórios, por posição | — |
| proof | — | uma prova, com o aleatório da única folha revelada |
| sealing | {"state":"unanchored"} ; ou anchored com domain, segment, rank, anchoredAt, seal:"pending" ; ou sealed com além disso seal.reference, seal.sealedAt | idem |
117. Procedimento #sec_9a00ec24
- Releer o pacote como documento canônico (§1) ; recusar senão.
- Verificar specification.
- Recalcular SHA-256(attestation) e compará-lo a digest, e à impressão mantida em outro lugar, se for o caso.
- Verificar header.schema, header.canonicalization, cryptography.hash.
- Completo : verificar os engajamento das duas listas ; verificar que a cada posição a decisão carrega a identidade do item ; reconstruir cada árvore desde a lista e os aleatórios (§3) e comparar raiz e leafCount a cryptography.trees.
- Pontual : verificar que a prova visa a raiz e o leafCount atestados para sua árvore ; verificar a prova (§4).
- Ler sealing.state e restituí-lo tal qual : esta versão não verifica nem a ancoragem nem o selo, e um pacote unanchored não é oponível.
Um pacote é válido quando todos os controles executados são bem-sucedidos. Isso não estabelece que ele é selado.
128. Conjunto de chaves públicas e ancoragem por chave raiz #sec_71a42b6e
O selo de um pacote se verifica com a chave pública da autoridade, publicada em um conjunto de chaves : um arquivo JSON, independente dos pacotes, que o terceiro detém.
{
"keys": [ { "kid": "signing:1", "alg": "ecdsa-p256-sha256", "spki": "<DER SubjectPublicKeyInfo, base64>" } ],
"attestation": {
"version": "seal.keyring.v1",
"count": 1,
"digest": "<hex>",
"alg": "ecdsa-p256-sha256",
"rootKid": "keyring-root:1",
"rootSpki": "<DER SubjectPublicKeyInfo de la racine, base64>",
"signature": "<base64>"
}
}
O pacote nomeia sua chave, o conjunto de chaves a fornece. sealing.seal.keyId designa a entrada a empregar. Uma entrada nunca é retirada de um conjunto de chaves : retirá-la tornaria inverificáveis todas as provas que ela selou.
8.1 Impressão do conteúdo
Reter as entradas que carregam ao mesmo tempo kid, alg e spki — uma entrada incompleta não confere nenhuma chave e não entra no cálculo. Ordená-las por kid, em comparação ordinal sobre os pontos de código. Formar uma linha por entrada, kid | alg | spki seguida de uma quebra de linha U+000A. A impressão é o SHA-256 da concatenação UTF-8 dessas linhas, em hexadecimal minúsculo.
A data de criação de uma entrada, se figurar, não entra no cálculo : é um instantâneo de exibição.
8.2 Vetor assinado pela raiz
seal.keyring.v1|<nombre d'entrées retenues>|<empreinte du contenu>
Assinado em forma de texto, codificado em UTF-8, pela chave raiz, segundo attestation.alg. As suítes admitidas são aquelas do selo : ecdsa-p256-sha256 (assinatura ASN.1 DER, RFC 3279) e rsa-pkcs1-sha256 . Um algoritmo desconhecido é uma recusa, jamais uma admissão.
8.3 O que o terceiro detém, e por que a raiz
A âncora do dispositivo é a impressão da chave raiz — SHA-256 de seu SubjectPublicKeyInfo DER, em hexadecimal minúsculo — portada pelo anexo de segurança que o cliente assina com seu contrato.
Ela recai sobre a raiz, jamais sobre o arquivo. Um conjunto de chaves cresce : cada rotação de chave de selamento acrescenta uma entrada. Uma impressão do arquivo se perimaria portanto à primeira rotação, e um auditor detendo o bom arquivo, perimeirado, veria cair uma prova autêntica. A raiz, ela, não muda ; o anexo assinado nunca é reaberto.
8.4 Procedimento de verificação de um conjunto de chaves
- Recalcular a impressão do conteúdo (§8.1) e o número de entradas retidas, e compará-las a attestation.digest e attestation.count. Recalcular primeiro, verificar a assinatura depois : controlar a assinatura sobre a impressão declarada deixaria passar um conjunto de chaves cujo se mudou as chaves conservando o bloco assinado.
- Verificar attestation.signature sobre o vetor (§8.2) com attestation.rootSpki.
- Comparar a impressão de attestation.rootSpki àquela que porta o anexo.
Seis resultados, e apenas um é oponível :
| Resultado | O que é estabelecido |
|---|---|
| Conjunto de chaves ancorado | Assinatura verificada e raiz conforme ao anexo. Único estado oponível. |
| Assinado, raiz não controlada | Nenhuma impressão de anexo fornecida : coerência interna apenas — qualquer um pode assinar um conjunto de chaves com sua própria raiz. |
| Assinado por outra raiz | O arquivo não é aquele que o anexo designa. |
| Alterado | O conteúdo não produz mais a impressão atestado. |
| Atestado inválido | A raiz declarada não assinou este conteúdo. |
| Não ancorado | Nenhum bloco attestation — um conjunto de chaves anterior ao dispositivo. Não é uma falha ; é um arquivo sem âncora. |
Um conjunto de chaves não ancorado permite sempre controlar um selo. O que ele não permite, é estabelecer de onde vem a chave pública empregada — e uma assinatura verificada com uma chave de procedência desconhecida não estabelece nada.