SD-JWT · Token Status List · base45
Die Antwort steckt schon im Code
Ein Nachweis, der sich selbst belegt. Signatur, Ablauf, Sperrstatus und Claims, alles geprüft, ohne dass eine einzige Anfrage das Gerät verlässt.
TypeScript, null Laufzeitabhängigkeiten, nur WebCrypto. Node, Browser und React Native, denn das prüfende Gerät ist meistens ein Telefon.
Was das konkret tut
Ein Nachweis ist eine Aussage, die jemand signiert hat. „Diese Person ist über 18.“ „Diese Person darf Auto fahren.“ Erst die Signatur macht den Satz etwas wert: Sie sagt, dass eine Behörde dafür einsteht und dass niemand danach etwas geändert hat.
Auf Papier, und in einem PDF, deckt eine Signatur das ganze Dokument ab. Um eine einzige Zeile zu beweisen, geben Sie alles heraus. Zeigen Sie den Führerschein nur für Ihr Alter, und Ihr Gegenüber erfährt auch Ihre Adresse, Ihre Führerscheinnummer und Ihr genaues Geburtsdatum. Danach gefragt hat niemand. Jetzt hat er es trotzdem.
Selektive Offenlegung beendet diesen Handel. Der Aussteller signiert jede Angabe einzeln, und der Nachweis trägt nur einen Fingerabdruck von jeder. Sie wählen, was Sie zeigen, der Rest bleibt ein Fingerabdruck, der nichts über seinen Inhalt verrät. Die Signatur stimmt weiterhin, denn sie lag nie über einem unteilbaren Block.
All das passt in den Code, den der Prüfer scannt: die Signatur, die Fingerabdrücke und die Angaben, die Sie zeigen wollten. Während der Prüfung wird nichts nachgeladen, also können eine Tür, ein Bus, eine Landarztpraxis oder ein Flugzeug auf Reiseflughöhe ganz ohne Netz prüfen.
Ein Teil fehlt noch, und genau der wird gern vergessen. Alles, was sich scannen lässt, lässt sich fotografieren. Deshalb muss die vorzeigende Person zusätzlich eine frische Challenge mit einem privaten Schlüssel signieren, der ihr Gerät nie verlässt. Ohne diesen Schritt käme der Screenshot eines fremden Nachweises durch. Diese Bibliothek weist jede Vorlage zurück, der er fehlt.
Gebaut für den Moment ohne Empfang
Ich habe den eCNH gebaut, Brasiliens digitalen Führerschein, den über 40 Millionen Menschen nutzen. Am meisten gelehrt hat mich nicht die App. Es war der Straßenrand: eine Beamtin, die auf der Landstraße einen Führerschein scannt, mit einem Balken Empfang oder gar keinem, und die in unter einer Sekunde ein Ja oder Nein braucht.
Alles, was man für diese Antwort braucht, passt in den Code. Die Signatur belegt den Aussteller, die Claims stehen direkt da, und das Einzige von außen ist der öffentliche Schlüssel des Ausstellers, der sich so selten ändert, dass man ihn mitliefern und wöchentlich auffrischen kann.
Bibliotheken dafür gibt es. Es sind Unternehmens-SDKs: schwer, an das Profil eines einzigen Landes gebunden und so geschrieben, als arbeiteten Sie bereits in der Identitätsbranche. Dies ist die Fassung, die ein Produktentwickler an einem Dienstag einbaut.
Drei Beteiligte, und keiner davon ist ein Server
Jeden Claim einmal signieren
Der Aussteller entscheidet, welche Claims später zurückgehalten werden dürfen, und signiert für jeden davon einen Digest.
Nur senden, was gefragt ist
Die Wallet lässt die Claims weg, die sie behält. Die Signatur des Ausstellers gilt weiterhin für das, was übrig bleibt.
Antworten, ohne jemanden zu fragen
Signatur, Ablauf, Sperrstatus und jede Offenlegung, geprüft gegen einen auf dem Gerät hinterlegten Schlüssel.
Beweisen Sie, dass Sie über 18 sind, ohne Ihren Geburtstag herzugeben
Gesetze zur Altersprüfung kommen schneller als die Werkzeuge. Die übliche Umsetzung lässt Kundschaft ein Foto des Ausweises bei Dritten hochladen, was ein Datenschutzdesaster ist und ein Leck, das nur auf seinen Tag wartet.
Selektive Offenlegung macht es richtig. Der Prüfer kann das Geburtsdatum nicht erfahren, selbst wenn er wollte, weil dieser Wert die Wallet nie verlassen hat. Die Eigenschaft ist kryptografisch, kein Versprechen in einer Datenschutzerklärung.
// Der Nachweis enthält Name, Adresse, Geburtsdatum und Dokumentnummer.
// Die Bar bekommt einen Wahrheitswert.
const presentation = await present(credential, { disclose: ['over_18'] })
const result = await verify(presentation, { trust })
result.claims // { over_18: true }
result.claims.birth_date // undefined, und wurde nie übertragen
result.withheld // 4, und es kann nicht sagen, welche vier
Echte Zahlen, auch die unangenehme
Ein realistischer Führerschein, acht Claims, fünf Jahre Gültigkeit, Zeiger auf eine Statusliste. Gemessen, nicht geschätzt:
| Nachweis | Zeichen | QR-Version |
|---|---|---|
| alles sichtbar | ~740 | 18, scannt gut |
| alle acht Claims offenlegbar | ~1590 | 27, zu dicht |
presenting only over_18 | ~1115 | 22, immer noch dicht |
fits() sagen, wo Sie stehen, bevor Sie irgendetwas drucken.
Glauben Sie mir nicht aufs Wort
Der Playground führt die ganze Bibliothek in Ihrem Browser aus und schickt ihr acht echte Angriffe. Jeder druckt den Ablehnungscode, den er erwartet, sodass Sie die Bibliothek gegen ihre eigenen Behauptungen prüfen können, statt einer README zu vertrauen.
Warum diese, und wann nicht
Fünf Gründe dafür, und danach der ehrliche Teil.
- Rund 1.300 Zeilen, die man wirklich liestKlein genug, dass eine Person sie an einem Nachmittag vollständig liest und weiß, was sie tut. Sicherheit, die man nicht lesen kann, ist Sicherheit auf Vertrauensbasis.
- Keine AbhängigkeitenZur Laufzeit nichts aus npm. Es gibt keine Lieferkette zu prüfen außer dieser, und nichts, was sich nächste Woche unter Ihnen verändert.
- Sie verweigert, was sie nicht vollständig prüfen kannKein teilweises Bestehen, keine Warnung, die man übergehen darf. Jeder Fehlschlag kommt als benannter Grund zurück, den Sie protokollieren und behandeln können, nie als blankes Falsch.
- Sie läuft dort, wo geprüft wirdNode, Deno, Bun, Browser, React Native und Edge Worker. Überall derselbe Code, auf der Standardkryptografie, die die Plattform schon mitbringt.
- Auf drei Arten getestetUnit-Tests, die offiziellen Testvektoren aus RFC 9901, und Property-Tests, die zufällige, fehlerhafte Eingaben dagegenwerfen und nach dem Fall suchen, der durchrutscht.
Und wann sie das falsche Werkzeug ist.
- Sie brauchen mobile Führerscheine nach ISO 18013-5Das ist eine andere Kodierung, CBOR und COSE, und ein anderer Standard. Diese Bibliothek spricht SD-JWT, das Format der europäischen Wallets und der OpenID-Spezifikationen. Verwandtes Problem, andere Aufgabe.
- Sie wollen eine ganze WalletDas hier prüft und legt vor. Es speichert keine Nachweise, verwaltet keine Geräte und übernimmt kein Onboarding. Es ist ein Teil, gedacht für den Einbau in Ihr Produkt.
- Sie brauchen ein externes Audit vor einem regulierten EinsatzDas gibt es noch nicht. Die Tests und die RFC-Vektoren sind öffentlich, jede Zeile ebenso, aber für einen regulierten Einsatz sollten Sie Budget für die eigene Prüfung einplanen.
Installieren
npm i qredential
Node 20 oder neuer, jeder aktuelle Browser, React Native. Rund 1.300 Zeilen Quellcode ohne Laufzeitabhängigkeiten, genau damit es realistisch ist, ihn zu lesen, bevor Sie ihm vertrauen.
Standards, keine Erfindungen
- SD-JWT, RFC 9901selektive Offenlegung und Key Binding, verschachtelt und rekursiv, der Mechanismus, den die europäische Identitäts-Wallet nutzt
- SD-JWT VCdie Gestalt des Nachweises
- Token Status ListSperrprüfung, die aus einer zwischengespeicherten Kopie funktioniert
- base45, RFC 9285der QR-Umschlag, gewählt wegen der Lesegerätkompatibilität
Kein ISO 18013-5 mDL, das CBOR und COSE statt JWT ist. Es steht auf der Liste, und ein Nachweis, der etwas Nichtunterstütztes verwendet, wird abgelehnt statt halb verstanden. Die Hälfte eines Konformitätsstandards zu behaupten ist schlimmer, als ihn gar nicht zu behaupten.