Verificação
Os três papéis de verificador do Label 309, os estados de veredito, a profundidade de finalidade e o catálogo tipado de erros — como qualquer pessoa chega à mesma resposta usando apenas infraestrutura pública.
O Label 309 é verificado, nunca apenas afirmado. Quem publica ancora o hash de um conteúdo na Cardano sob o rótulo de metadados 309; a partir daí, a afirmação se sustenta pelos próprios bytes, e qualquer pessoa de posse da referência de transação pode conferi-la. Esta página define como essa conferência funciona: os três papéis de verificador, o que cada um faz e não toca, os estados de veredito que emitem, a profundidade de confirmação abaixo da qual um veredito permanece provisório e o catálogo tipado de erros que faz duas implementações independentes concordarem na mesma falha para a mesma entrada.
A propriedade que define o padrão é a independência de serviço. Um verificador em conformidade chega ao seu veredito usando apenas a blockchain pública, um explorador ou gateway Cardano escolhido pelo próprio verificador e — para afirmações sobre conteúdo e para registros selados — gateways de armazenamento endereçado por conteúdo que o verificador também escolhe. Ele nunca contata quem publicou. O padrão não nomeia nenhum provedor específico; o gateway é uma entrada fornecida pelo operador.
Três papéis, cada um uma extensão estrita
A verificação é organizada em camadas. Cada papel faz tudo o que o papel acima dele faz e então acrescenta uma capacidade. Um papel inferior já é, por si só, um verificador completo e útil — ele apenas prova menos.
| Papel | Acrescenta | Toca |
|---|---|---|
| Validador estrutural | conformidade de esquema + de domínio sobre os bytes do registro | nada — uma função pura |
| Verificador público | resolução de cadeia, inclusão on-chain, verificação de assinaturas | um explorador Cardano + gateways de conteúdo |
| Verificador do destinatário | tentativa de decifragem de uma carga útil selada, recálculo do hash do texto claro | a chave privada do próprio verificador |
Validador estrutural — uma função pura sobre os bytes
O validador estrutural é uma única função que vai de uma cadeia de bytes a um resultado. Ele não realiza nenhuma E/S, nenhuma verificação de assinatura criptográfica e nenhuma decifragem. Nunca vê uma rede, uma transação ou uma chave. Dada a mesma entrada, ele retorna a mesma saída, sempre, em qualquer lugar — e é isso que permite executá-lo antes da submissão, dentro das ferramentas de quem publica, dentro de um indexador de terceiros ou dentro de uma ferramenta de arquivamento que confirma a boa formação a longo prazo, tudo isso sem servidor.
Seu fluxo é fixo:
1. resource bounds — a local, non-normative guard on input size; never a
Label 309 conformance error.
2. canonical decode — decode with a canonical-CBOR decoder (RFC 8949 §4.2.1):
definite lengths, sorted map keys, no duplicate keys,
valid UTF-8. Any malformed or non-canonical input →
a single MALFORMED_CBOR.
3. schema parse — type, length, and the chunk/length bounds; a strict
object mode that rejects unknown fields.
4. domain rules — cross-field constraints the schema cannot express:
registry membership, the items-or-merkle rule, COSE
structural shape, URI reconstruction, envelope shape.
5. result — { valid, record } with optional warnings/info, or a
sorted list of typed issues.O validador é independente de perfil: ele analisa o esquema v1 completo,
independentemente do subconjunto sobre o qual um verificador posterior pretenda agir.
Erros reprovam o registro; entradas de aviso e de informação são expostas, mas o
deixam válido. De forma crucial, ele confirma a forma de um COSE_Sign1 — array de
quatro elementos, carga útil destacada (null), um cabeçalho protegido bem formado —
mas nunca verifica a assinatura, e nunca rejeita um registro apenas porque o
algoritmo de assinatura é um que ele não reconhece (isso é marcado como
SIGNATURE_UNSUPPORTED, severidade info, e o registro permanece válido). Verificar
a assinatura é tarefa do verificador público. Veja O registro
para o esquema que este passo impõe.
Verificador público — cadeia, inclusão e assinaturas
O verificador público sobrepõe a cadeia ao validador estrutural. Dada uma referência de transação Cardano, ele:
- Resolve um explorador escolhido pelo verificador. A cadeia de exploradores é
uma entrada; o verificador os tenta na ordem indicada, buscando o CBOR bruto da
transação on-chain — nunca a projeção de metadados em JSON do explorador. A
visão JSON colapsa os tipos principais do CBOR em uma união JSON e descarta a
ordem das chaves do mapa, a moldura de comprimento definido e a distinção entre
bytes e texto, de modo que um verificador que recodificasse a partir dela não
conseguiria reproduzir a entrada de assinatura byte a byte e toda assinatura de um
registro em conformidade falharia. A resposta negativa de um único provedor não
é autoritativa quanto à cadeia — a transação pode estar on-chain e apenas ser
desconhecida daquele provedor —, então o verificador consulta todos os provedores
restantes antes de emitir
TX_NOT_FOUND; se todos os provedores estiverem inacessíveis, ele emitePROVIDER_UNAVAILABLE. - Vincula os bytes buscados à referência da transação. Antes de ler qualquer
coisa de uma transação buscada, o verificador recalcula o
blake2b-256sobre os bytes do corpo da transação buscada — por definição do ledger, o id da transação — e rejeita a resposta diante de qualquer divergência com o hash solicitado. Em seguida, recalcula oblake2b-256sobre os bytes de dados auxiliares buscados e rejeita diante de qualquer divergência com oauxiliary_data_hashdo corpo já verificado. Ambos os digests são calculados sobre os bytes exatamente como buscados, nunca uma recodificação. Uma resposta que falhe em qualquer das verificações carrega bytes comprovadamente errados e é descartada; se nenhum provedor produzir uma resposta que sobreviva ao vínculo, o relatório carregaTX_INTEGRITY_MISMATCH. Depois desse passo, cada byte do registro e da transação ao redor está criptograficamente comprometido com o hash fornecido por quem chama — nenhum explorador pode substituir, emendar ou truncar o registro sem produzir uma segunda pré-imagem de blake2b-256. - Desembrulha os dados auxiliares. O ledger da era Conway admite três
codificações de dados auxiliares, todas válidas: um mapa de metadados simples e
sem tag, um array de dois elementos
[ metadata, scripts ]e um mapa com tag 259, com os metadados sob a chave0. O verificador DEVE aceitar as três e despachar puramente pelo tipo e pela tag de CBOR no nível superior — um valor com tag 259 é a forma de mapa por chave, um array sem tag é a forma de dois elementos e um mapa sem tag é sempre o próprio mapa de metadados. Ele NÃO DEVE inspecionar as chaves de um mapa para adivinhar a forma; qualquer outra forma de nível superior, ou qualquer tag que não seja a 259, éMALFORMED_CBOR. Se a transação vinculada não carregar metadados sob o rótulo 309, o verificador emiteMETADATA_NOT_FOUND— um resultado atribuível ao registro, porque a própria transação vinculada prova a ausência: nenhum provedor poderia ter removido os metadados sem falhar no vínculo. - Remonta o array de fragmentos do corpo inteiro. O valor do rótulo 309 é o corpo do registro em CBOR canônico dividido em um array de cadeias de bytes de ≤ 64 bytes. O verificador concatena os elementos em ordem, byte a byte — devolvendo os bytes brutos, sem passo de recodificação — para que a verificação de CBOR canônico ainda possa detectar uma codificação on-chain fora de conformidade.
- Valida estruturalmente o corpo remontado (o papel acima). Uma rejeição do
validador interrompe o relatório com o veredito
failed. - Verifica a profundidade de confirmação (mais abaixo). Uma transação abaixo do
limiar para aqui com o veredito
pending. - Verifica cada assinatura no nível do registro sob Ed25519 estrito. Ele não decifra. A resolução de assinaturas, a carga útil com separação de domínio e as regras estritas de verificação estão especificadas em Assinaturas.
- Busca e confere por hash o conteúdo, responsabilizando o registro apenas pelos bytes que ele consegue atribuir ao próprio endereço de conteúdo de uma URI (mais abaixo). Ele não decifra.
Por que CBOR bruto, e não JSON
Uma assinatura é calculada sobre o CBOR canônico, byte a byte, do corpo do registro. Uma projeção JSON dos metadados é, por construção, uma projeção com perdas — ela não consegue retornar exatamente a esses bytes. Recodificar a partir do JSON quebra toda assinatura de um registro em conformidade. O CBOR bruto da transação é a única entrada autoritativa para qualquer verificação criptográfica; uma visão em JSON serve para exibição a pessoas, depois que a verificação já passou.
Verificador do destinatário — decifrar e recalcular
O verificador do destinatário é um verificador público que, além disso, possui uma chave privada. Para um item selado endereçado a ele, ele tenta decifrar os slots de chave on-chain com sua chave, recupera a chave de conteúdo em caso de sucesso, decifra o texto cifrado e então recalcula os hashes do texto claro contra o compromisso registrado on-chain — fechando o ciclo entre os bytes cifrados e a afirmação de existência do conteúdo. Como todo item selado carrega pelo menos uma entrada de hash de conteúdo, esse recálculo sempre tem algo concreto com que se comparar. O envelope selado, os slots de chave e a construção de desembrulho estão especificados em PoE selada.
Antes de o destinatário tocar qualquer primitiva KEM ou AEAD, ele reaplica as mesmas
verificações de forma e de recursos do envelope que o validador estrutural já executou,
rejeitando primeiro um envelope estruturalmente inválido ou superdimensionado. Duas
dessas barreiras pré-primitiva são limites de recursos fixados pela implantação, não
campos de transmissão: os limites de referência são MAX_SLOTS = 1024 slots e 65536
bytes para o envelope enc decodificado, ambos muito acima do teto de ~16 KiB de
metadados de transação da Cardano que limita qualquer registro honesto, de modo que um
registro que exceda qualquer um deles é malformado e é rejeitado com
ENC_SLOTS_TOO_MANY ou ENC_ENVELOPE_TOO_LARGE antes de uma única primitiva ser
executada. As implantações PODEM restringi-los.
Uma verificação de forma tem peso de segurança: o material de encapsulamento DEVE ser
distinto dentro de um mesmo slots[] — todos os valores de epk no caso de x25519,
ou todos os valores de kem_ct no caso de mlkem768x25519. Uma duplicata dentro do
registro é rejeitada com ENC_SLOTS_DUPLICATE_KEM_MATERIAL antes que
qualquer primitiva KEM ou AEAD seja executada, porque um epk/kem_ct repetido
quebraria a unicidade da chave por slot da qual o envoltório de nonce zero depende. O
reúso de chave entre registros ou entre chaves é uma obrigação do produtor e não é
detectável pelo verificador; só a duplicata dentro do registro o é. As três
verificações são estruturais — o validador as impõe em todo registro (os códigos de
material duplicado, de contagem de slots e de envelope decodificado são códigos da
Parte A); o destinatário simplesmente as roda de novo como defesa em profundidade.
O próprio desembrulho são duas etapas criptográficas que o destinatário reproduz a
partir do envelope, nunca de qualquer entrada lateral. Primeiro, ele recalcula o hash
da transcrição de slots slots_hash uma vez, antes do laço, e o mantém constante ao
longo de cada slot:
slots_hash = SHA-256("cardano-poe-slots-transcript-v1" || canonicalEncode(SLOTS_TRANSCRIPT)),
em que a transcrição fixa os campos de cabeçalho, o conjunto de slots em trânsito
(cada campo de slot é uma única cadeia de bytes — não há fracionamento por campo a
normalizar) e a alegação de hash do item por meio de hashes_hash. Vincular o
hashes_hash é o que permite ao destinatário confirmar que o envelope foi selado
para esta exata alegação de hash apenas a partir dos bytes on-chain, antes de
qualquer busca de texto cifrado — um envelope colado em um item com um mapa hashes
diferente falha no MAC. Ele itera todos os slots sem interrupção antecipada; um
slot só é aceito quando a chave de conteúdo que ele produz também reproduz o
slots_mac presente em trânsito sobre aquele slots_hash constante. A aceitação
também incorpora um bit de
validade independente do segredo: no caminho clássico, um slot forjado para levar o
segredo compartilhado X25519 ao valor todo-zero zera esse bit (uma comparação em tempo
constante contra 0^32), a KEK é selecionada em tempo constante para uma KEK fictícia
derivada de 0^32 de modo que o laço realize trabalho idêntico, e o bit governa a
aceitação do slot — um slot com ECDH inválido nunca pode abrir, independentemente de seu
envoltório ou MAC. Tanto a abertura do envoltório quanto a abertura posterior do
conteúdo são atômicas: em uma falha de tag AEAD, elas não retornam texto em claro, e
o candidato que devolvem (a chave envolvida ou o texto em claro do conteúdo) é um valor
fictício, fixo ou pseudoaleatório, independente do texto cifrado que falhou — nenhum
texto em claro não verificado é jamais liberado.
A chave de um destinatário PODE legitimamente corresponder a mais de um slot: um
produtor pode selar a mesma chave de conteúdo para o mesmo destinatário em vários slots,
cada um com material KEM por slot fresco, para preencher a contagem de destinatários —
uma técnica de privacidade válida e distinta da rejeição de material de encapsulamento
duplicado, que dispara apenas em um epk/kem_ct idêntico. O verificador seleciona a
chave do primeiro slot correspondente e NÃO DEVE rejeitar apenas porque vários
slots corresponderam. A única anomalia que ele DEVE rejeitar são dois slots
correspondentes que recuperam chaves de conteúdo diferentes (comparadas em tempo
constante): o laço carrega um bit cek_conflict e expõe a única falha genérica se
qualquer correspondência posterior produzir uma chave que difira da selecionada. Isso é
defesa em profundidade — sob o comprometimento do conjunto de slots, uma correspondência
com chave distinta já é inviável, de modo que a verificação simplesmente falha de forma
fechada.
Depois, sobre a chave de criptografia de conteúdo recuperada, ele deriva a chave de
conteúdo (uma folha de HKDF dessa chave, com salt enc.nonce) e abre o texto cifrado
em STREAM segmentado bloco a bloco, verificando a tag de cada bloco antes de
liberar o texto claro daquele bloco. O AAD por bloco é vazio: todo o contexto de
cabeçalho já está vinculado à chave de forma transitiva por meio do slots_mac, de
modo que inverter qualquer campo de cabeçalho muda o que o destinatário deriva e o
fluxo não abre. O truncamento é detectado pelo sinalizador final — um bloco final
ausente, dados após ele, um sinalizador final em um bloco não final, ou um bloco não
final curto falham todos como TAMPERED_CIPHERTEXT. O formato segmentado não impõe
nenhum teto criptográfico de carga útil (o contador de blocos de 88 bits admite
2^88 blocos); um máximo prático é uma política de negação de serviço da implantação,
imposta de forma incremental à medida que o fluxo é lido. No caminho da frase secreta,
o destinatário primeiro lê o cabeçalho de compromisso inicial de 32 bytes e o compara
em tempo constante antes de abrir qualquer bloco.
O caminho do destinatário é onde o catálogo de erros mostra toda a sua precisão: ele distingue o caso em que nenhum slot aceitou esta chave (destinatário errado) do caso em que um slot aceitou a chave, mas o conjunto de slots ou o texto cifrado foi adulterado. Essas são afirmações de segurança diferentes e carregam códigos diferentes (mais abaixo). Um chamador não confiável, porém, recebe exatamente uma forma de falha genérica, qualquer que seja a causa — nenhum slot abriu, o conjunto de slots foi adulterado ou a tag de conteúdo falhou — e a resposta nunca revela qual caso ocorreu, nem qual slot correspondeu; os códigos tipados são um diagnóstico interno apenas para um chamador local confiável.
A temporização segue um modelo explícito. O verificador PODE retornar na verificação de não correspondência — antes da decifragem do conteúdo — de modo que um não destinatário e um destinatário levem tempos mensuravelmente diferentes. Essa diferença revela apenas destinatário versus não destinatário, nunca qual slot correspondeu nem qualquer material de chave. Uma temporização uniforme entre um não destinatário e um destinatário cujo texto cifrado falha ao abrir NÃO é exigida, e uma abertura de conteúdo fictícia NÃO DEVE ser obrigatória — isso imporia o custo da decifragem de conteúdo a todo transeunte. A garantia de tempo constante que de fato vale é a entre slots: dentro da passada de uma única chave privada, o laço percorre todos os slots com comparações em tempo constante, de modo que nada revele qual slot, se algum, aquela chave desembrulha.
Finalidade: profundidade de confirmação
A liquidação na Cardano é probabilística. Uma transação com um bloco de profundidade
ainda pode ser orfanizada por um reorg curto; uma transação com muitos blocos de
profundidade já liquidou com probabilidade esmagadora. Um verificador que classificasse
como valid um registro com um único bloco de profundidade permitiria que um atacante
reancorasse um registro contraditório em uma bifurcação concorrente e obtivesse um
veredito de "valid" em ambos — quebrando silenciosamente a premissa de apenas-acréscimo
sobre a qual repousa toda a prova.
Por isso, enquanto o registro permanece abaixo de um limiar de profundidade de
confirmação, o verificador o reporta como pending, não failed. O limiar de uso
geral RECOMENDADO é de ≥ 15 blocos (cerca de cinco minutos). O limiar é uma
política do verificador, não uma constante do formato de fios: implantações que lidam
com registros de alto valor ou de natureza probatória DEVEM elevá-lo em direção à
finalidade rígida, e um verificador DEVE expor o limiar que utilizou para que os
consumidores possam sobrepor uma política mais estrita. Um registro pending está bem
formado e on-chain; ele apenas ainda não liquidou com profundidade suficiente, e pode
resolver para valid em uma nova tentativa posterior.
A profundidade de confirmação, a altura do bloco e o horário do bloco são fatos
afirmados pelo explorador que o verificador nunca fabrica. O vínculo com a
referência da transação torna o conteúdo da transação livre de confiança, mas os
fatos da cadeia a respeito dela não são deriváveis dos bytes — o CBOR da transação
não carrega carimbo de tempo nem altura. A profundidade é calculada como
(altura do tip do explorador que resolve) − (altura do bloco que inclui a transação) + 1,
de modo que uma transação no bloco do tip tem profundidade exatamente 1; o horário do
bloco é o carimbo de tempo POSIX (segundos inteiros, UTC) do slot do bloco que inclui
a transação, retirado do campo de tempo do explorador, nunca recalculado pelo
verificador. Como esses fatos repousam sobre a palavra do explorador, um verificador
DEVERIA resolvê-los a partir de pelo menos dois exploradores independentes e expor
qualquer divergência; implantações para as quais o horário do bloco tem peso decisivo
— notarização legal, disputas de prazo — DEVEM conferi-lo de forma cruzada. O
relatório carrega a profundidade resolvida ao lado do limiar contra o qual ela foi
comparada, e o block_time (com block_slot quando disponível).
Estados de veredito
Um verificador conclui em um de quatro vereditos legíveis por máquina, cada um
emparelhado um a um com um código de saída de processo, para que um chamador — um
portão de CI, um monitor, um script — consiga distinguir uma falha atribuível ao
registro de uma falha operacional transitória sem precisar analisar o relatório
estruturado. O princípio que governa é a atribuição: condenar um registro exige
evidência que os próprios bytes do registro — ou bytes atribuivelmente vinculados às
suas referências — de fato forneçam. Nenhuma má conduta de provedor pode fabricar um
failed.
| Veredito | Saída | Significado |
|---|---|---|
| valid | 0 | toda verificação executada pelo verificador retornou ok; nenhuma questão de severidade error está presente. |
| failed | 1 | uma falha atribuível ao registro: o validador estrutural rejeitou os bytes, uma assinatura não verificou, um hash atribuível divergiu, a transação vinculada não carrega metadados sob o rótulo 309 (METADATA_NOT_FOUND), ou uma regra de deny-host disparou. |
| unverifiable | 2 | nenhum erro atribuível ao registro, mas uma verificação obrigatória não pôde rodar ou não pôde ser atribuída — a transação não resolveu, nenhuma resposta de provedor sobreviveu ao vínculo (TX_INTEGRITY_MISMATCH), ou conteúdo/texto cifrado comprometido não pôde ser obtido ou atribuído. O mesmo registro pode verificar como valid em uma nova tentativa ou sob um gateway diferente. |
| pending | 3 | estruturalmente bem formado e on-chain, mas abaixo do limiar de profundidade de confirmação (INSUFFICIENT_CONFIRMATIONS); pode liquidar. Nenhum resultado de um registro pendente pode ser apresentado como definitivo. |
Falhas de runtime do host do verificador que não são atribuíveis ao registro usam códigos de saída 4 ou superiores e não correspondem a veredito algum.
Um veredito valid NÃO DEVE ser reportado quando qualquer questão de severidade
error estiver presente; um registro PODE ser valid com uma lista warnings
e/ou info não vazia. Nenhuma camada pode "suavizar" um erro, transformando-o em aviso
para fazer um registro passar. Cada veredito é reservado para o seu próprio caso: o
estado pending nunca é usado em substituição a valid ou failed, e uma falha
atribuível ao provedor é unverifiable, nunca failed.
Um piso de comprometimento governa a disponibilidade: quando a conferência de
conteúdo rodou, mas falhas de disponibilidade não deixaram nenhum comprometimento
de conteúdo do registro de fato verificado, o veredito é unverifiable, nunca
valid — um veredito valid significa que ao menos um comprometimento de conteúdo
foi conferido. Os resultados de integridade não são afetados pelo piso: bytes
atribuíveis que falham em um comprometimento produzem failed independentemente do
que mais estivesse disponível.
Vínculo com o endereço de conteúdo e atribuição
O passo 8 (e, para o verificador do destinatário, a decifragem) busca bytes de gateways de armazenamento endereçados por conteúdo escolhidos pelo verificador — e os gateways não são confiáveis. A regra de veredito acima gira em torno de uma única pergunta que o verificador DEVE conseguir responder sobre cada fluxo de bytes buscado: esses bytes podem ser atribuídos à própria URI? Ambos os esquemas são endereçados por conteúdo, então podem:
ipfs://— recalcular o CID sobre o conteúdo buscado (o multihash diretamente, no caso de um CID de codec bruto; digests bloco a bloco ao longo do caminho resolvido, no caso de um CID em forma de DAG).ar://— validar a transação Arweave assinada e recalcular a árvore de Merkle dos blocos contra seudata_root; no caso de um data item ANS-104, recalcular o deep-hash, verificar a assinatura do proprietário e conferir que o SHA-256 da assinatura é igual ao id da URI.
Bytes que satisfazem os próprios digests do registro não precisam de verificação de vínculo — o comprometimento do registro é pelo menos tão forte quanto o da camada de armazenamento. Onde o vínculo é aplicado, a atribuição decide o que uma divergência significa:
- Bytes atribuíveis — vínculo verificado, ou fornecidos fora de banda por quem
chama — são imputados ao registro quando falham em um comprometimento:
URI_INTEGRITY_MISMATCHpara o conteúdo do item (uma falha de integridade rígida, independentemente do que qualquer URI irmã contenha), a famíliaMERKLE_*para uma lista de folhas,TAMPERED_CIPHERTEXTpara um blob de texto cifrado atribuível. Vereditofailed. - Bytes não atribuíveis — vínculo não verificado, ou verificado e falho —
incriminam o provedor que serve, nunca o registro:
URI_PROVIDER_INTEGRITY_MISMATCH(aviso). O verificador prossegue com as URIs e os gateways restantes; uma alegação que fique sem bytes atribuíveis termina em um resultado de disponibilidade (CONTENT_UNAVAILABLE,MERKLE_LEAVES_UNAVAILABLE,CIPHERTEXT_UNAVAILABLE), vereditounverifiable— exatamente como se nada tivesse sido buscado.
Esta é a contraparte, do lado do armazenamento, do vínculo com a referência da
transação: um gateway mal-intencionado só pode degradar a disponibilidade, nunca o
veredito. URI_INTEGRITY_MISMATCH e URI_PROVIDER_INTEGRITY_MISMATCH são, portanto,
códigos distintos — o primeiro condena o registro, o segundo condena um provedor — e
um verificador que os confundisse permitiria que um gateway hostil forjasse um
failed. O recálculo do hash do texto claro pós-decifragem não precisa de qualificador
de atribuição: o texto cifrado que abre sob o envelope autenticado é atribuído pelo
próprio AEAD, de modo que uma divergência de hash do texto claro ali
(URI_INTEGRITY_MISMATCH) é sempre atribuível ao registro e força failed.
O catálogo tipado de erros
Todo modo de falha resolve para um código de um único catálogo fechado. Os códigos
seguem o estilo SCREAMING_SNAKE_CASE, e uma implementação em conformidade DEVE
emitir exatamente essas cadeias — nunca o código interno em minúsculas de um parser,
nunca uma mensagem em texto livre. Duas implementações em duas linguagens, alcançando a
mesma entrada, emitem o mesmo código; a lista normativa completa é fixada byte a byte
pela suíte de testes de conformidade, e o catálogo é imutável (códigos só são
acrescentados por emenda).
Modelo de severidade
Toda questão carrega uma de três severidades, e a distinção é determinante:
- error — invalida o veredito. Um resultado
validnão pode coexistir com nenhumerror. - warning — uma anomalia de execução não fatal (um único gateway falhou, uma lista
de folhas estava apenas parcialmente disponível) que não impede o
valid. - info — uma não verificação deliberada: um aspecto que o verificador escolheu não avaliar (um campo fora de seu perfil, um algoritmo opcional não reconhecido). Uma entrada de info não é um erro suavizado e nunca é usada como tal.
Um código fica à parte: INSUFFICIENT_CONFIRMATIONS mapeia para o veredito pending,
e não para uma severidade, porque o registro está bem formado e apenas aguarda a
liquidação.
Famílias de erros
O catálogo se agrupa em famílias. Um conjunto representativo — não exaustivo — de códigos:
| Família | Severidade | Códigos representativos |
|---|---|---|
| CBOR malformado / não canônico | error | MALFORMED_CBOR, CHUNK_TOO_LARGE |
| Esquema | error | SCHEMA_TYPE_MISMATCH, SCHEMA_MISSING_REQUIRED, SCHEMA_UNKNOWN_FIELD, SCHEMA_EMPTY_RECORD |
| Algoritmo não suportado | error | UNSUPPORTED_HASH_ALG, UNSUPPORTED_AEAD_ALG, UNSUPPORTED_KEM_ALG, UNSUPPORTED_MERKLE_COMMIT_ALG |
| Explorador / metadados | error / pending | TX_NOT_FOUND, PROVIDER_UNAVAILABLE, TX_INTEGRITY_MISMATCH (→ unverifiable), METADATA_NOT_FOUND (→ failed); INSUFFICIENT_CONFIRMATIONS (→ pending) |
| Assinatura | error / info | MALFORMED_SIG_COSE_SIGN1, SIGNER_KEY_UNRESOLVED, SIGNATURE_INVALID, WALLET_ADDRESS_MISMATCH; SIGNATURE_UNSUPPORTED (info) |
| Cifragem / KEM / embrulho | error | KEM_EPK_LENGTH_MISMATCH, KEM_CT_LENGTH_MISMATCH, WRAP_LENGTH_MISMATCH, ENC_SLOTS_DUPLICATE_KEM_MATERIAL, ENC_SLOTS_TOO_MANY, ENC_ENVELOPE_TOO_LARGE, ENC_REQUIRES_CONTENT_HASH |
| Resultado da decifragem | error | WRONG_DECRYPTION_INPUT_SHAPE, WRONG_RECIPIENT_KEY, TAMPERED_HEADER, TAMPERED_CIPHERTEXT, KDF_DERIVATION_FAILED |
| URI / conteúdo | error / warning | INVALID_URI, URI_TARGET_FORBIDDEN, URI_INTEGRITY_MISMATCH, CONTENT_UNAVAILABLE (→ unverifiable), CIPHERTEXT_UNAVAILABLE (→ unverifiable); URI_PROVIDER_INTEGRITY_MISMATCH, URI_FETCH_FAILED (avisos) |
| Compromisso de lista Merkle | error / warning / info | MERKLE_ROOT_MISMATCH, SCHEMA_MERKLE_LEAF_COUNT_MISMATCH; MERKLE_LEAVES_UNAVAILABLE (dupla); MERKLE_UNSUPPORTED (dupla) |
| Independência de serviço | error | SERVICE_INDEPENDENCE_VIOLATION |
Os códigos de rede/política (TX_NOT_FOUND, PROVIDER_UNAVAILABLE,
CONTENT_UNAVAILABLE, CIPHERTEXT_UNAVAILABLE) — e o TX_INTEGRITY_MISMATCH, cuja
divergência é comprovável contra os provedores, e não contra o registro — têm
severidade error na lista de questões (eles bloqueiam um veredito valid), mas
não são atribuíveis ao registro, de modo que mapeiam para unverifiable, nunca
failed. O URI_PROVIDER_INTEGRITY_MISMATCH é o aviso por busca sob o mesmo
princípio. Alguns poucos códigos carregam severidade dupla — ENC_UNSUPPORTED,
MERKLE_UNSUPPORTED, OUT_OF_PROFILE_SKIPPED e MERKLE_LEAVES_UNAVAILABLE —, lidos
como info/warning por padrão e promovidos a error em um contexto estrito (o papel
de destinatário, a escalada de apenas-merkle, o modo estrito de ponta a ponta ou o piso
de comprometimento).
O caminho do destinatário é a família mais precisa em termos de diagnóstico. Para um
chamador local confiável, uma decifragem malsucedida resolve para um de três
códigos internos: WRONG_RECIPIENT_KEY significa que nenhum slot aceitou a chave
fornecida (nenhuma chave de conteúdo chegou a ser recuperada); TAMPERED_HEADER
significa que uma chave foi recuperada, mas a chave de conteúdo candidata não reproduziu
slots_mac sobre slots_hash (um slot, um campo de cabeçalho ou o próprio slots_mac
foi alterado); TAMPERED_CIPHERTEXT significa que o conjunto de slots estava íntegro,
mas a tag AEAD do conteúdo falhou depois que a chave foi recuperada. Os três são
estruturalmente distinguíveis para esse chamador local, e a fronteira entre eles não
vaza nenhum material de chave. Para um chamador externo não confiável, os três se
reduzem à única falha genérica descrita acima, indistinguível pela forma da resposta.
Sob o modelo de temporização, um retorno antecipado e permitido na não correspondência
separa WRONG_RECIPIENT_KEY (um não destinatário) dos dois resultados de adulteração do
lado do destinatário, que não carregam nenhuma distinção adicional entre si — de modo
que a precisão diagnóstica nunca se torna um oráculo seletivo de slot ou de chave.
Como os códigos se mapeiam nos quatro vereditos
Um código emitido pelo validador estrutural significa que os bytes do registro não
estão em conformidade. O verificador público interrompe imediatamente o relatório com o
veredito failed — saída 1 — e a lista de questões do validador, sem executar nenhum
trabalho adicional de cadeia ou criptografia. Um código emitido somente depois que a
validação estrutural passou — uma assinatura que não verificou, um hash atribuível
que não conferiu, METADATA_NOT_FOUND na transação vinculada — é igualmente failed:
cada um é atribuível ao registro, evidência que os próprios bytes do registro fornecem.
Uma falha transitória ou atribuível ao provedor é um veredito diferente: conteúdo que
não pôde ser buscado ou atribuído, um explorador que estava inacessível, ou um provedor
que serviu bytes que falham no vínculo com a referência da transação mapeiam todos para
unverifiable — saída 2 —, porque nenhum é culpa do registro e o mesmo registro pode
verificar como valid em uma nova tentativa. A distinção que o código de saída
preserva é, portanto, tripla entre os estados de falha: failed atribuível ao registro
(1), unverifiable operacional ou atribuível ao provedor (2) e pending abaixo do
limiar (3).
Alguns poucos códigos carregam uma severidade que depende do contexto. Um verificador
que lê um registro mais rico do que seu perfil declarado (por exemplo, um verificador
de apenas hash que encontra um item selado) reporta o campo extra como info em um
contexto de exibição e ainda assim valida a afirmação de hash, ou como error em um
contexto de auditoria estrita de ponta a ponta. Da mesma forma, um algoritmo de
assinatura opcional não reconhecido é info — a afirmação de existência do conteúdo
não depende dele — de modo que uma prova pública de apenas hash permanece valid mesmo
quando uma assinatura informativa não pode ser verificada.
A independência de serviço não é opcional
A verificação nunca volta a contatar quem publicou. Toda chamada de saída que um
verificador em conformidade faz é dirigida a infraestrutura escolhida pelo operador —
um explorador Cardano para a transação e gateways de armazenamento endereçado por
conteúdo para quaisquer bytes ar:// ou ipfs://. Isso é imposto estruturalmente, não
é uma promessa em comentário de código:
- Toda chamada de rede passa por um único invólucro de saída que registra
url,method,status, contagem de bytes e finalidade de cada chamada — sucesso, falha e nova tentativa por igual — em uma trilha de auditoria obrigatória no relatório. Um verificador que não consegue produzir essa trilha não consegue provar sua independência. - Esse invólucro aceita uma lista de hosts proibidos (deny-host) fornecida pela
implantação e faz falhar de imediato qualquer chamada a um host correspondente, com
SERVICE_INDEPENDENCE_VIOLATION. A lista é uma entrada do operador — uma suíte de conformidade a preenche com os próprios domínios de quem implementa — não uma constante do formato de fios do Label 309. - Um arcabouço de conformidade executa o verificador contra transações de fixture
congeladas em uma rede onde os próprios domínios do operador não resolvem para lugar
algum, e verifica que o verificador ainda retorna
valid. A verificação é feita na camada de rede do sistema operacional, não vasculhando o código-fonte — um verificador que alcançasse um host proibido por um IP fixo no código passaria em uma varredura de fonte, mas falharia neste teste.
O verificador precisa de um explorador Cardano acessível e de nada específico da implementação. Gateways de conteúdo, uma chave privada de destinatário e uma lista de folhas off-chain são entradas opcionais que habilitam, respectivamente, as verificações de conteúdo, de destinatário e de Merkle. Nenhuma delas é um serviço que o padrão nomeie, e nenhuma delas é quem publicou.
Páginas relacionadas
- O registro — o formato de fios contra o qual o validador estrutural confere: o rótulo 309, a forma do mapa, a remontagem dos fragmentos e o esquema CDDL.
- Assinaturas — a construção COSE_Sign1 no nível do registro, a carga útil com separação de domínio e as regras estritas de verificação Ed25519.
- PoE selada — o envelope de cifragem, os slots de chave do destinatário e o desembrulho que o verificador do destinatário realiza.
PoE selada
O envelope de criptografia do Label 309 — como um remetente sela o conteúdo para uma ou mais chaves de destinatários, enquanto a cadeia carrega apenas o hash do texto claro e os slots de chave protegidos, nunca o texto claro e nunca os destinatários.
Modelo de segurança
O que um verificador Label 309 confia e o que não confia. A invariante de verificabilidade independente, as garantias de privacidade da PoE selada, as regras criptográficas normativas que toda implementação deve cumprir e os limites conhecidos do formato de transmissão.