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.
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:
| Identificador | Algoritmo | Referência | Digest |
|---|---|---|---|
sha2-256 | SHA-256 | FIPS 180-4 | 32 B |
blake2b-256 | BLAKE2b-256 | RFC 7693 | 32 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.
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 nUma 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.
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.
O registro
O formato de dados do Label 309 — onde o registro fica sob o rótulo de metadados 309, o formato do seu mapa, as regras de CBOR canônico, o fracionamento para transporte e o esquema CDDL.
Registros de identificadores de algoritmos
Os registros de identificadores nomeados para hashes, AEADs, KEMs, KDFs e assinaturas — e a regra de agilidade que torna a migração pós-quântica aditiva, em vez de incompatível.