Esta é uma tradução informativa. A versão em inglês é a oficial e prevalece. Ler a versão em inglês

Conteúdo e hashing

Como o Label 309 vincula um registro ao seu conteúdo — o mapa de hashes, aquilo a que o digest se compromete e os compromissos de Merkle para ancorar muitos itens sob uma única raiz.

O hash do conteúdo é a afirmação. Tudo o que um registro Label 309 declara sobre existência decorre de um digest criptográfico dos bytes do conteúdo, ancorado na blockchain sob o rótulo de metadados 309. Esta página define como esse digest é transportado, exatamente aquilo a que ele se compromete e como uma única raiz de 32 bytes pode representar um lote arbitrariamente grande de itens.

O mapa hashes

Cada item de um registro carrega um mapa hashes: um mapa CBOR que associa um identificador de algoritmo a um digest bruto de 32 bytes.

CBOR
hashes = {
  "sha2-256": h'…32 bytes…',      ; key = algorithm id, value = raw digest
}

As chaves são identificadores em string de texto retirados do registro de hashes; os valores são strings de bytes brutos, nunca codificados em hexadecimal. O mapa deve conter ao menos uma entrada, e todo algoritmo de hash registrado produz exatamente 32 bytes:

IdentificadorAlgoritmoReferênciaDigest
sha2-256SHA-256FIPS 180-432 B
blake2b-256BLAKE2b-256RFC 769332 B

Ambos os identificadores são de implementação obrigatória para os verificadores, de modo que um registro de hash único sob qualquer um deles é validado em qualquer lugar. Um verificador que encontra um identificador desconhecido rejeita o registro com um código de erro estável, em vez de ignorar a entrada silenciosamente. O registro completo, incluindo os espaços reservados para algoritmos pós-quânticos, está em Registros de algoritmos.

Usar um mapa CBOR — em vez de arrays paralelos ou uma lista de subobjetos {alg, digest} — tem três consequências que fazem parte do contrato de formato. Algoritmos duplicados são impossíveis por construção, porque as chaves de um mapa CBOR são únicas. A ordenação canônica é automática, porque o CBOR canônico ordena as chaves pelos seus bytes codificados; assim, dois produtores que expressam o mesmo conjunto de hashes emitem mapas idênticos byte a byte, e qualquer assinatura no nível do registro sobre eles permanece estável. E a forma não exige validação por entrada: um validador estrutural apenas verifica se cada chave está registrada e se cada valor tem o comprimento de digest do algoritmo correspondente.

Aquilo a que o hash se compromete

O digest se compromete com os bytes do conteúdo — a sequência exata de bytes que o produtor está datando. Cada entrada de um mapa hashes deve ser o digest dessa mesma sequência de bytes sob o algoritmo nomeado; um registro cujas entradas descrevem textos claros diferentes é não conforme. Quando os bytes do conteúdo estão disponíveis a um verificador, ele deve recalcular cada digest e rejeitar o registro se algum deles não coincidir.

Quando um registro carrega um envelope de criptografia (enc), o hash vincula o texto claro, nunca o texto cifrado. Isso é proposital: uma prova de existência (PoE) existe para que um autor possa, mais tarde, revelar o texto claro e provar que ele existia em determinado momento. Aplicar hash ao texto cifrado provaria apenas que algum blob criptografado existiu, o que nada diz sobre o conteúdo subjacente. Assim, um registro selado ainda prova exatamente qual texto claro foi datado: o destinatário descriptografa, recalcula os digests do texto claro e os confronta com o compromisso registrado na blockchain. Um item que carrega enc deve, portanto, ter ao menos uma entrada de hash de conteúdo; sem ela, não haveria afirmação de texto claro contra a qual recalcular.

Vínculo com o texto claro, mesmo quando selado

O digest registrado na blockchain de um registro selado é o digest do texto claro. O próprio texto cifrado fica em uma URI endereçada por conteúdo, ar:// ou ipfs://, de modo que os bytes devolvidos por um gateway de armazenamento são verificáveis contra o endereço sem que se confie no gateway; um destinatário descriptografa e recalcula o hash do texto claro para fechar o ciclo de volta à afirmação registrada na blockchain.

Um hash, ou vários

Um único hash de conteúdo é plenamente conforme. Para todos os hashes de 256 bits do registro, os melhores ataques de segunda pré-imagem conhecidos situam-se em torno de 2^256 no cenário clássico — um único hash de 256 bits bem escolhido já cobre o modelo de ameaça realista ao longo da vida útil de arquivamento de um registro, e os validadores estruturais não emitem aviso algum para registros de entrada única.

Um produtor pode acrescentar uma segunda entrada de uma família de design independente, como uma defesa em profundidade opcional — combinando sha2-256 (SHA-2: construção Merkle–Damgård) com blake2b-256 (BLAKE2: uma construção HAIFA sobre uma permutação derivada do ChaCha). Como as duas famílias não compartilham linhagem estrutural, um registro que carrega ambas só é enfraquecido se as duas famílias caírem para a criptoanálise ao mesmo tempo. O custo é um digest extra de 32 bytes, mais seu identificador curto, por item; a escolha é do produtor, e nunca é obrigatória.

Compromissos de Merkle em lote

Um único hash de conteúdo ancora um único conteúdo. Para ancorar uma coleção arbitrariamente grande — um conjunto de 500 artefatos de CI, um fluxo de eventos IoT, um lote de logs de auditoria — o Label 309 define um array de nível superior merkle[]. Cada entrada se compromete com uma lista ordenada de folhas de 32 bytes, com uma única raiz de 32 bytes publicada na blockchain; as próprias folhas ordenadas ficam fora da cadeia.

CBOR
merkle = [
  {
    "alg":        "rfc9162-sha256",
    "root":       h'…32 bytes…',   ; canonical root over the ordered leaves
    "leaf_count": 4,               ; binds the on-chain root to the leaf-list size
    "uris":       [ … ],           ; OPTIONAL — where the off-chain leaves list lives
  },
]

O algoritmo de compromisso registrado é rfc9162-sha256: o Merkle Tree Hash da RFC 9162 §2.1.1, com SHA-256 como hash subjacente. Trata-se de uma construção de compromisso sobre listas, distinta do registro de hashes de conteúdo — uma raiz de Merkle se compromete com a estrutura de uma lista de folhas, enquanto um digest sha2-256 se compromete com bytes de texto claro — e por isso fica em seu próprio array, e não dentro de hashes. O leaf_count registrado na blockchain vincula a raiz ao tamanho da lista off-chain, impedindo uma substituição que reconstruiria uma árvore de tamanho diferente compartilhando a raiz para alguma posição de folha.

Construção da árvore

A construção distingue folhas de nós internos por meio de um prefixo de separação de domínio de um byte — 0x00 para folhas, 0x01 para nós internos — de modo que um atacante não consiga forjar um nó interno que colida com uma folha. Para uma lista ordenada L = (d_0, …, d_{n-1}) de valores de 32 bytes com n ≥ 1, o Merkle Tree Hash é definido recursivamente:

MTH(L) = SHA-256(0x00 || d_0)                            when n == 1
MTH(L) = SHA-256(0x01 || MTH(L[0:k]) || MTH(L[k:n]))     when n > 1
         where k is the largest power of 2 strictly less than n

Uma consequência fundamental: uma única folha recebe hash como SHA-256(0x00 || d_0), e não como a folha nua. A raiz de uma árvore de uma só folha, portanto, nunca é igual à própria folha. Os produtores que queiram datar um único conteúdo devem usar diretamente uma entrada sha2-256 ou blake2b-256, e não uma árvore de Merkle de uma folha. Uma árvore vazia (n == 0) é proibida.

A construção é sensível à ordem — permutar as folhas produz uma raiz diferente —, portanto os produtores devem tratar a lista de folhas como uma sequência ordenada e preservar essa ordem ao longo da publicação, do arquivamento e de qualquer geração de prova posterior.

A lista de folhas off-chain

A raiz não tem utilidade sem a lista de folhas; por isso os produtores persistem as folhas ordenadas fora da cadeia. O artefato canônico é um documento cardano-poe-merkle-leaves-v1, codificado como CBOR canônico (RFC 8949): uma raiz de 32 bytes, o array ordenado de folhas de 32 bytes e a contagem de folhas.

CDDL
leaves-list = {
  "format":     "cardano-poe-merkle-leaves-v1",
  "tree_alg":   tstr,                   ; registered list-commitment algorithm id
  "root":       bytes .size 32,         ; raw 32 bytes, not hex
  "leaves":     [ + bytes .size 32 ],   ; ordered raw 32-byte leaves
  "leaf_count": 1..4294967295,          ; 1 .. 2^32-1; MUST equal the length of `leaves`
  ? "leaf_alg": tstr,                   ; informative; no verification semantics
}

Um verificador resolve a lista off-chain, recalcula a raiz a partir de suas leaves usando a construção acima e a confronta byte a byte com a merkle[i].root registrada na blockchain; o leaf_count do arquivo deve ser igual tanto ao leaf_count registrado na blockchain quanto a len(leaves). Este contêiner em CBOR canônico é a única forma normativa da lista de folhas — não há projeção JSON nem serialização alternativa, de modo que duas implementações que trocam uma lista de folhas trocam sempre documentos comparáveis byte a byte.

Provas de inclusão

O objetivo do agrupamento em lote é a divulgação seletiva: provar que um item estava na lista comprometida sem republicar — nem sequer revelar — o restante. Uma prova de inclusão para uma folha é a lista ordenada dos hashes dos nós irmãos ao longo do caminho dessa folha até a raiz: um caminho de irmãos O(log n). Um verificador recombina a folha e os irmãos subindo a árvore conforme a RFC 9162 e aceita a prova se, e somente se, a raiz reconstruída for igual à raiz publicada byte a byte.

Como as árvores da RFC 9162 não são preenchidas até uma potência de dois, uma folha na borda direita de uma árvore desbalanceada pode ter um caminho mais curto do que uma folha do lado completo. A verificação autoritativa é, portanto, algorítmica — a recombinação reproduz a raiz? — e nunca uma comparação do comprimento da prova.

Por que o agrupamento em lote importa

Uma única transação e uma única raiz de 32 bytes podem representar milhares ou milhões de folhas. Qualquer pessoa que detenha uma prova O(log n) pode, mais tarde, demonstrar que "este item estava na minha lista", enquanto cada folha não divulgada permanece privada — a raiz nada revela sobre as folhas com as quais se compromete.