Anleitungen

Anleitungen · Teil 6 von 6

Aufnahmezertifikate

Du hast eine Merkle-Wurzel unter Label 309 veröffentlicht und dazu die off-chain Blätterliste, die ihr zugrunde liegt. Diese Wurzel ist eine vollkommen solide Festlegung — aber nichts, das du einer Kollegin in die Hand drücken, an einen Vertrag anhängen oder einem Gerichtsschriftsatz beilegen könntest. Genau diese Übergabe leistet ein Aufnahmezertifikat: eine kleine, in sich geschlossene Datei, die ein oder mehrere Blätter an die veröffentlichte Wurzel bindet, jeden zur Neuableitung dieser Wurzel nötigen Geschwisterknoten einbettet und die Cardano-Transaktion benennt, deren Blockzeitpunkt das Ganze bezeugt. Jede und jeder kann es offline verifizieren, gegen jeden beliebigen Explorer, ohne Konto und ohne Vertrauen in die Person, die es erstellt hat.

Wenn dir OpenTimestamps geläufig ist: Es ist dieselbe Idee — eine portable Quittung über einen Existenznachweis — mit zwei bewussten Unterschieden. Die Zeitstempel-Autorität ist die Blockzeit der Cardano-Blockchain, kein Kalender-Server. Und der Nachweis wird vollständig clientseitig erzeugt und verifiziert: kein Gateway, kein Aussteller-Server, an keiner Stelle Vertrauen in uns. Es ist das Existenznachweis-Pendant zu einer .ots-Datei, auf Cardano verankert und eigenständig verifizierbar.

Was ein Zertifikat beweist — und was nicht

Ein Zertifikat erhebt genau zwei kryptografische Behauptungen, jede von jeder und jedem unabhängig überprüfbar:

  1. Aufnahme. Ein gegebenes leaf sitzt an Position index eines RFC 9162-SHA-256-Merkle-Baums der Größe tree_size, dessen Wurzel root ist. Bewiesen wird dies, indem die Wurzel aus dem Blatt, seinem Index, der Baumgröße und dem eingebetteten Geschwisterpfad neu berechnet wird. Das ist in sich geschlossen — die Zertifikatsdatei allein genügt.
  2. Verankerung. Diese root taucht wortgleich im Feld merkle[].root des Label-309-Datensatzes auf, den die Transaktion tx_hash trägt. Bewiesen wird dies, indem man diese Transaktion auf irgendeinem öffentlichen Cardano-Explorer ausliest und die Bytes vergleicht. Dafür braucht es die Zertifikatsdatei plus einen Explorer — sonst nichts.

Zusammen beweisen sie, dass der Inhalt des Blattes zu oder vor dem Blockzeitpunkt von tx_hash existierte.

Was ein Zertifikat NICHT beweist

Die Zeit wird von der öffentlichen Blockchain behauptet, nicht kryptografisch in den Nachweis eingebunden — genau wie bei OpenTimestamps und Chainpoint vertraust du der Blockzeit der Chain, niemals der Person, die das Zertifikat erstellt hat. Ein Zertifikat ist kein eIDAS-„qualifizierter" elektronischer Zeitstempel (RFC 3161 / eine qualifizierte TSA); es ist ein blockchain-verankerter Zeitstempel — ein starkes, untermauerndes Beweismittel für eine zeitliche Behauptung, in derselben Kategorie wie andere Blockchain-Zeitstempel, und der Wortlaut in der Datei sagt genau das. Es sagt nichts darüber aus, wer den Inhalt verfasst hat (das ist eine optionale Datensatzsignatur — siehe Signaturen), und es beweist nicht, dass der Inhalt nicht schon früher bekannt war. Es beweist Existenz bis zu einem Stichtag, nicht Urheberschaft und nicht Neuheit.

Warum der Nachweis unsigniert ist

Eine strikte IETF-COSE-Quittung ist ein COSE_Sign1, dessen Payload die Merkle-Wurzel ist, signiert von einer Autorität. Hier ist die Autorität die Blockchain, kein Schlüssel, den wir besitzen — die Wurzel mit unserem Schlüssel zu signieren würde also Server-Vertrauen wiedereinführen und die Eigenschaft der eigenständigen Verifizierbarkeit brechen, auf der der gesamte Standard ruht. Stattdessen gibt das Zertifikat die IETF-CBOR-Struktur des Aufnahmebeweises exakt wie spezifiziert aus und trägt den Blockchain-Anker anstelle der Signatur. Die Beweismathematik ist byteidentisch mit der IETF-Kodierung; sie ist bewusst unsigniert und blockchain-verankert.

Das JSON-Zertifikat

Das primäre Artefakt ist label-309-inclusion-certificate-v1: eine sowohl für Menschen als auch für Maschinen lesbare JSON-Datei. Eine Datei deckt ein ODER viele Blätter ab, und jeder Eintrag bettet seinen vollständigen Geschwisterpfad ein, sodass die Datei für immer neu verifizierbar bleibt — kein Arweave-Abruf, kein Gateway, kein ursprünglicher Veröffentlicher nötig.

contract.cert.json
{
  "format": "label-309-inclusion-certificate-v1",
  "generated_at": "2026-06-16T12:00:00.000Z",   // informational only, never trusted
  "anchor": {
    "chain": "cardano",
    "network": "mainnet",
    "tx_hash": "…64hex…",
    "metadata_label": 309,
    "block_time": 1781611200,                    // POSIX seconds — explorer-asserted
    "block_time_iso": "2026-06-16T12:00:00.000Z",
    "block_height": 12345678,                    // optional; explorer-asserted
    "explorer_urls": [
      "https://cardanoscan.io/transaction/…",
      "https://adastat.net/transactions/…"
    ]
  },
  "merkle": {
    "tree_alg": "rfc9162-sha256",
    "root": "…64hex…",
    "tree_size": 1024,                           // === the on-chain leaf_count
    "leaves_list_uri": "ar://<txid>"             // optional source reference
  },
  "items": [
    {
      "leaf": "…64hex…",                         // the content hash committed as a leaf
      "leaf_alg": "sha2-256",                    // how to hash a file to reproduce `leaf`
      "index": 42,
      "proof": ["…64hex…", "…64hex…"],           // siblings, leaf→root; [] for a single-leaf tree
      "verified": true,                          // proof recomputes to merkle.root at build time
      "label": "contract.pdf"                    // optional note/filename
    }
  ],
  "claim": "Each listed hash was included in a Merkle tree whose root was published on the Cardano blockchain in the referenced transaction under metadata label 309; therefore each hash provably existed on or before the stated block time.",
  "verification": {
    "method": "RFC 9162 (Certificate Transparency) SHA-256 inclusion proof. For each item, recompute the Merkle root from leaf+index+tree_size+proof and compare to merkle.root; then confirm merkle.root equals the merkle[].root in the label 309 record of anchor.tx_hash on any public Cardano explorer.",
    "requires_trust_in_cardanowall": false,
    "time_asserted_by": "Cardano blockchain (block time), via public explorers"
  }
}

Ein paar Details, die zu kennen sich lohnt:

  • root, leaf und jeder Eintrag von proof[] sind rohe 32-Byte-Werte, dargestellt als Hex. Erzeuger geben Kleinbuchstaben aus; ein Verifizierer akzeptiert beide Schreibweisen und weist jedes Nicht-Hex-Zeichen oder jede Zeichenkette ungerader Länge zurück.
  • Das gespeicherte "verified": true ist das Ergebnis des Erzeugers zur Erstellungszeit. Ein Verifizierer vertraut ihm nie — er berechnet den Beweis selbst neu und meldet sein eigenes Urteil. Ein Blatt, das im Baum nicht gefunden wurde, wird mit "verified": false und einem "error"-Feld vermerkt, niemals stillschweigend verworfen, damit die Datei über Fehlschläge ehrlich bleibt.
  • block_time ist der vom Explorer behauptete POSIX-Zeitstempel des einschließenden Blocks. block_time_iso ist dessen UTC-Darstellung — nur zur Bequemlichkeit.

Der CBOR-Aufnahmebeweis (COSE / RFC 9162-konform)

Neben dem JSON kann jeder Eintrag als kompaktes .cbor-Artefakt exportiert werden, dessen Beweisstruktur byteidentisch mit draft-ietf-cose-merkle-tree-proofs ist. Das bedeutet: Jeder RFC 9162- / COSE-Verifizierer für verifizierbare Datenstrukturen kann die Beweismathematik direkt lesen — der Interop-Kern ist standardgemäß, nicht hausgemacht. Es trägt keine absolute Blockzeit und keinen juristischen Text (das lebt im JSON); es ist nur der portable Beweiskern, und es ist aus dem oben genannten Grund blockchain-verankert und unsigniert.

Der nackte IETF-Aufnahmebeweis — bstr .cbor [tree_size, leaf_index, inclusion_path], der Wert 1 der verifizierbaren Datenstruktur (vds) für RFC 9162 SHA-256 — lässt sich für einen reinen COSE-Verifizierer für sich allein extrahieren; der Cardano-Anker wird daneben als kleine Map anstelle der Sign1-Signatur getragen.

Erstellen und verifizieren mit dem Werkzeug

Das Zertifikatsformat ist Teil des öffentlichen, Gateway-agnostischen Werkzeugs: das SDK @cardanowall/sdk-ts (mit byteidentischen Zwillingen in Python und Rust), die cardanowall CLI und die Verifizierer-Oberflächen in den Apps. Die Erstellungs- und Verifizierungsmathematik ist rein und offline — nur das Auflösen frischer Chain-Fakten berührt das Netzwerk.

Mit dem CLI

certificate build nimmt die Blätterliste, die Ziele (rohes Blatt-Hex oder zu hashende Dateien) und die Transaktion entgegen, deren Anker-Fakten es auflöst. certificate verify führt den Aufnahmebeweis je Eintrag erneut aus und gibt den Anker aus, den du noch on-chain bestätigen musst:

terminal
# Build a certificate for two files against a published root.
cardanowall certificate build \
  --leaves-list leaves.cbor \
  --tx <tx-hash> \
  --file contract.pdf --file exhibit-a.png \
  --out contract.cert.json

# Re-verify the proofs offline — no network, no trust in the producer.
cardanowall certificate verify contract.cert.json

Exit-Code 0 bedeutet, dass sich jeder Eintragsbeweis zur Wurzel neu berechnet; ein Code ungleich null kennzeichnet einen Aufnahmefehler, fehlerhafte Eingabe oder einen E/A-Fehler — damit fügt es sich direkt in CI ein. Jeder einzelne Eintrag lässt sich auch in die kanonische Form { tree_alg, tree_size, index, leaf, proof[] } extrahieren und mit cardanowall merkle verify prüfen.

Mit dem TypeScript-SDK

Die certificate-API ist rein — du holst die Bytes der Blätterliste mit dem plattformeigenen fetch und reichst sie hinein; der Krypto-Pfad erreicht nie das Netzwerk:

build-and-verify.ts
import { certificate, merkle } from '@cardanowall/sdk-ts';

// `leaves` comes from decodeLeavesList(...) over the fetched leaves-list bytes.
const cert = certificate.buildInclusionCertificate({
  anchor,                       // chain facts resolved from the tx
  merkle: { treeAlg: 'rfc9162-sha256', root, treeSize: leaves.length },
  leaves,
  targets: [{ leaf, leafAlg: 'sha2-256', label: 'contract.pdf' }],
});

// Pure re-verification from the certificate alone — no Arweave, no chain.
const result = certificate.verifyInclusionCertificate(cert);
console.log(result.ok);          // true when every item's proof recomputes to root
console.log(result.anchorClaim); // the anchor you confirm on a public explorer

// Each item also re-verifies through the plain Merkle predicate.
const item = cert.items[0];
const ok = merkle.merkleSha2256VerifyInclusion(
  leafBytes, item.index, cert.merkle.tree_size, proofBytes, rootBytes,
);

verifyInclusionCertificate meldet das Beweisurteil und gibt den behaupteten Anker wieder; diesen Anker on-chain zu bestätigen ist dein separater, expliziter Schritt — das Ergebnisobjekt sagt es dir. Dasselbe Modul ist Byte für Byte in den Python- (certificate) und Rust- (certificate) SDKs gespiegelt.

Im Browser, auf einer Transaktionsseite

Ein Label-309-Datensatz, der eine merkle[]-Festlegung trägt, zeigt auf seiner Transaktionsseite ein Aufnahme-Panel. Füge einen oder mehrere Hex-Hashes ein oder ziehe die Originaldateien hinein, um sie clientseitig zu hashen; die Seite holt die Blätterliste direkt aus dem inhaltsadressierten Speicher in deinem Browser, berechnet jeden Beweis neu, zeigt je Eintrag ein grünes/rotes Urteil und bietet das JSON, das CBOR und ein druckbares PDF zum Download. Das PDF bettet das vollständige JSON als Dateianhang ein, sodass es selbst das maschinenverifizierbare Artefakt ist — nicht nur ein Bild davon. Nichts davon berührt einen privaten Server.

Der Verifizierungsalgorithmus, von Anfang bis Ende

Um ein Zertifikat unabhängig zu verifizieren — in deinem eigenen Code, ganz ohne Werkzeug von uns — tu genau Folgendes:

  1. Verwirf fehlerhafte Felder. Jeder root/leaf/proof[]-Eintrag muss Hex gerader Länge sein, das zu 32 Byte dekodiert; tree_size und jeder index müssen sichere Ganzzahlen mit index < tree_size und 1 ≤ tree_size ≤ 2³² − 1 sein.
  2. Berechne die Wurzel jedes Eintrags neu. Falte gemäß RFC 9162 §2.1.3.2 das Blatt (leaf = SHA-256(0x00 ‖ leaf_digest)) mit seinem Geschwisterpfad (node = SHA-256(0x01 ‖ L ‖ R)), wobei du an der größten Zweierpotenz strikt unterhalb der laufenden Teilbaumgröße aufspaltest, und vergleiche das Ergebnis Byte für Byte mit merkle.root. Ein Baum mit einem einzigen Blatt hat einen leeren Beweis.
  3. Bestätige den Anker on-chain. Rufe anchor.tx_hash auf irgendeinem öffentlichen Cardano-Explorer ab, lies seine Label-309-Metadaten und bestätige, dass merkle.root gleich dem merkle[].root des Datensatzes ist. Die Blockzeit, die du dort abliest, ist der Zeitstempel, den das Zertifikat behauptet.

Schritte 1–2 sind der in sich geschlossene Teil — sie verlassen deine Maschine nie. Schritt 3 ist der eine Netzwerk-Lesevorgang, und er geht an einen Explorer deiner Wahl, niemals an die Person, die das Zertifikat erstellt hat.

Eine Datei, für immer verifizierbar

Weil jeder Geschwisterknoten auf dem Pfad eingebettet ist, verifiziert ein Zertifikat noch lange weiter, nachdem die Blätterliste, das ursprüngliche Gateway oder der Erzeuger verschwunden sind. Die zwei Prüfungen — die Wurzel aus der Datei neu berechnen, die Wurzel on-chain bestätigen — sind alles, was jemals jemand braucht. Wie Wurzel und Blätterliste überhaupt erst entstehen, beschreibt Stapelweise mit Merkle, und Verifizierung zeigt das vollständige Verifizierer-Modell, in das sich die Chain-seitige Prüfung einfügt.