Trustvice befindet sich in der eingeschränkten Beta. Bei der Registrierung oder Anmeldung müssen Sie das Beta-Tester-Passwort angeben, das Sie erhalten haben, oder eines anfordern unter preview@trustvice.com. Vielen Dank.
Im Inneren des Belegformats: kanonisches JSON, Ed25519 und publizierte Schlüssel
Ein Rundgang Feld für Feld durch den Trustvice-Belegumschlag: warum wir eine kanonische Form nach RFC 8785 signieren statt der übertragenen Bytes, wie Key-IDs aus den Schlüsseln selbst abgeleitet werden und was das publizierte Key-Set verspricht.
Von The Trustvice team··6 Min. Lesen
Ein Beleg hat eine Aufgabe: von jedem geprüft zu werden, noch in Jahren, mit Code, den nicht wir geschrieben haben. Diese Anforderung prägte jede Entscheidung im Format. Dieser Beitrag geht Umschlag, Kanonisierung, Signatur und Key-Set durch und erklärt jeweils die Gründe.
Belege in diesem Format teilen einen Umschlag; nur der body unterscheidet sich von einem Belegtyp zum nächsten. Signierte Nachweise wie ein Proof-of-Erasure nutzen einen verwandten Umschlag, mit subject und claims statt body, unter derselben Kanonisierung, Signatur und demselben Key-Set wie unten beschrieben.
Immer "1". Die Version des Belegformats, keine API-Version.
receiptId
Eine ULID mit 26 Zeichen, nach Erstellungszeit sortierbar.
issuedAt
ISO-8601-Zeitpunkt in UTC mit Millisekunden, von Trustvice beim Signieren gesetzt.
type
Der Belegtyp. body.type ist immer gleich.
signatureKeyId
Die kid des Signaturschlüssels im publizierten Key-Set: 22 Zeichen base64url.
body
Die typspezifischen Fakten.
signature
Die Ed25519-Signatur, base64url ohne Padding: 86 Zeichen.
Aus dem Aufbau der Signatur folgen zwei Eigenschaften, und beide zählen für jeden, der einen Prüfer schreibt.
Jedes Feld außer signature ist signiert. Das gilt auch für jedes Feld, das eine künftige Version ergänzt. Ein Prüfer darf unbekannte Felder vor der Prüfung nicht verwerfen, sonst scheitern gültige Belege. Neue optionale Felder kommen in Version 1 nur hinzu; Leser sollten Unbekanntes ignorieren, es beim Prüfen aber behalten.
Der Beleg nennt weder Ihren Workspace noch Ihr Konto. Der Workspace ist der Bereich, in dem Sie Belege auflisten und abrufen, steht aber nicht im signierten Umschlag. Ein Beleg ist eine in sich geschlossene Aussage über einen Fakt, kein Beweis, wer ihn anforderte.
Eine Beleg-ID ist eine ULID: 26 Zeichen Crockford-Base32. Die ersten zehn Zeichen sind ein 48-Bit-Zeitstempel in Millisekunden, die übrigen sechzehn zufällig. IDs sortieren sich also nach Erstellungszeit.
Die Zeit in der ID entsteht einige Millisekunden versetzt zu issuedAt. Behandeln Sie issuedAt als signierte Zeit und die ID als Kennung.
Eine Signatur deckt Bytes ab, keine Bedeutung. JSON kennt viele Byte-Schreibweisen desselben Werts: Keys in beliebiger Reihenfolge, optionaler Leerraum, 4 oder 4.0, "é" oder "\u00e9". Signierten wir die zufällig gesendeten Bytes, würde ein Proxy, der die Antwort neu einrückt, oder eine Bibliothek, die beim Speichern Keys umsortiert, einen völlig gültigen Beleg brechen.
Die Signatur deckt daher eine kanonische Form ab, definiert in RFC 8785, dem JSON Canonicalization Scheme (JCS). Jede korrekte Implementierung macht aus demselben geparsten Wert dieselben Bytes. Für einen Beleg laufen die Regeln auf fünf hinaus:
Keys sind sortiert, auf jeder Verschachtelungsebene, nach UTF-16-Codeeinheit.
Kein Leerraum zwischen Tokens.
Zahlen nutzen die ECMAScript-Darstellung als String: Ganzzahlen ohne Dezimalpunkt oder Exponent. Beleg-Bodys enthalten nur Ganzzahlen.
Strings nutzen minimales JSON-Escaping; Nicht-ASCII-Zeichen werden als rohes UTF-8 ausgegeben, nie als \uXXXX, und ein Schrägstrich wird nicht escaped.
Arrays behalten ihre Reihenfolge, und null- und false-Werte bleiben erhalten, statt zu entfallen.
Die signierte Nachricht ist die JCS-Form des Umschlags ohne das Feld signature, als UTF-8 kodiert. Beim Signieren werden Werte, die sich nicht überall identisch darstellen lassen, etwa NaN, Unendlichkeiten, undefined oder bigint, abgelehnt statt umgewandelt.
Die praktische Folge für Sie: Sie können einen Beleg hübsch formatieren, in einer Datenbank speichern, die Keys umsortiert, oder durch jedes JSON-Tool schicken, das Werte erhält, und er prüft weiterhin. Was Sie nicht können: einen Wert ändern, ein null-Feld entfernen oder eine Zahl runden. Genau diese Änderungen soll ein Prüfer erkennen.
Die Signatur ist reines Ed25519 nach RFC 8032: ohne Pre-Hash und ohne Kontext-String. Wir wählten es, weil es klein, schnell und deterministisch ist und in den Standardbibliotheken steckt, die man schon hat. Node ab 20, aktuelle Browser, Deno, Bun und Cloudflare Workers prüfen es allein mit WebCrypto; Python mit dem Paket cryptography; OpenSSL 3 aus der Shell. Ein Prüfer für Trustvice-Belege sollte nie eine Trustvice-Bibliothek brauchen.
Eine Signatur hat 64 Bytes, das sind die 86 base64url-Zeichen im Umschlag. Ein öffentlicher Schlüssel hat 32 Bytes.
Trustvice hält die privaten Schlüssel in Hardware-Sicherheitsmodulen von AWS KMS, ein Schlüssel je Datenregion, und sie verlassen diese nie. Der Signierer sendet die kanonischen Bytes an KMS und erhält eine Signatur zurück; er kann signieren, aber keinen Schlüssel exportieren.
Die signatureKeyId eines Belegs entspricht der kid eines Schlüssels im publizierten Set. Diese kid ist kein von uns gewähltes Label. Sie wird aus dem Schlüssel berechnet:
text
kid = base64url( first 16 bytes of SHA-256( the 32 raw bytes of x ) )
ohne Padding, was 22 Zeichen ergibt. Daraus folgen zwei nützliche Dinge.
Erstens ist ein Eintrag im Key-Set selbstprüfend: Jeder kann die kid aus x neu berechnen, und ein Eintrag, bei dem sie nicht passen, ist ungültig. In JavaScript:
Zweitens liegt die signatureKeyId in den signierten Bytes, also lässt sich ein Beleg nicht auf einen anderen Schlüssel umlenken, ohne ihn ungültig zu machen. Und da die kid aus x abgeleitet ist, kann ein geändertes x für eine kid, die Sie schon haben, nicht legitim sein.
Die öffentlichen Schlüssel sind ein JSON Web Key Set unter https://trustvice.com/.well-known/receipt-keys.json, das per HTTP 308 auf https://trustvice.com/api/v1/receipts/keys weiterleitet. Es wird als JSON ausgeliefert, eine Stunde cachebar und mit Access-Control-Allow-Origin: *, sodass eine Seite auf Ihrer eigenen Domain es laden und Belege im Browser prüfen kann.
Zum Prüfen brauchen Sie nur kid, kty, crv und x. Der Satz kann mehrere Schlüssel enthalten, und zurückgezogene Schlüssel bleiben veröffentlicht. Belege eines Workspace können im Lauf der Zeit verschiedene Key-IDs tragen, schlagen Sie den Schlüssel also immer je Beleg nach.
Das Set wird aus publizierter Konfiguration gebaut, die Prüfung hängt also nie von einem Aufruf bei AWS ab. Es enthält immer nur öffentliche Felder.
Standardmäßig vertraut ein Prüfer dem, was der Schlüssel-Endpunkt im Moment der Abfrage liefert, und das ist nur so stark wie diese TLS-Verbindung. Für Beweise, die zählen, pinnen Sie das Set: Laden Sie es einmal aus einem vertrauenswürdigen Netz, berechnen Sie jede kid aus ihrem x neu, legen Sie die Paare dort ab, wo sie sich nur per Review ändern, und prüfen Sie gegen diese Datei. Aktualisieren Sie sie nach Plan und prüfen Sie neue Schlüssel vor der Aufnahme.
Bewahren Sie das verwendete Key-Set neben jedem Beleg auf, den Sie als Beweis behalten. Ein Beleg samt dem Key-Set, gegen das er geprüft wurde, ist für sich allein prüfbar.
Ein Beleg nennt den Schlüssel, der ihn signierte, daher berührt ein neuer Schlüssel im Set nie ältere Belege. Heute gibt es keine automatische Rotation, und Schlüssel haben keinen Ablauf. Rotation ist eine manuelle Zeremonie außerhalb des Systems, und das Set kennt keine Markierung für ausgemusterte oder widerrufene Schlüssel. Rotation ist additiv: Ein ausgemusterter Schlüssel bleibt publiziert, damit von ihm signierte Belege prüfbar bleiben. Das ist eine Policy, nichts, was der Schlüssel-Endpunkt erzwingt, und ein Grund mehr für eine eigene gepinnte Kopie.
Es gibt auch keine Widerrufsliste. Würde ein Signaturschlüssel je kompromittiert, wäre die Abhilfe, ihn nicht mehr zu publizieren; dann scheiterte jeder Beleg darunter mit unknown_key, echte eingeschlossen. Eine eigene Kopie des Key-Sets vom Zeitpunkt, an dem Sie einen Beleg annahmen, und die Erfassung des Belegs in einem System unter Ihrer Kontrolle halten Ihre Beweise trotzdem nutzbar.
Fügen Sie einen beliebigen Trustvice-Beleg in den Prüfer ein. Er prüft die Signatur in Ihrem Browser gegen unsere veröffentlichten Schlüssel, ganz ohne Konto.