SD-JWT · Token Status List · base45
La réponse est déjà dans le code
Un justificatif qui se prouve lui-même. Signature, expiration, révocation et claims, tout est contrôlé sans qu'une seule requête ne quitte l'appareil.
TypeScript, zéro dépendance à l'exécution, WebCrypto uniquement. Node, navigateurs et React Native, parce que l'appareil qui vérifie est le plus souvent un téléphone.
Ce que cela fait, concrètement
Une attestation est une affirmation que quelqu'un a signée. « Cette personne a plus de 18 ans. » « Cette personne peut conduire. » C'est la signature qui lui donne sa valeur : elle dit qu'une autorité répond de cette phrase et que personne ne l'a modifiée ensuite.
Sur papier, et dans un PDF, une seule signature couvre tout le document. Pour en prouver une ligne, il faut tout remettre. Montrez votre permis de conduire uniquement pour prouver votre âge et votre interlocuteur apprend aussi votre adresse, votre numéro de permis et votre date de naissance exacte. Il n'a jamais demandé cela. Il l'a maintenant.
La divulgation sélective supprime ce marché. L'émetteur signe chaque donnée séparément, et l'attestation ne transporte qu'une empreinte de chacune. Vous choisissez ce que vous révélez, et le reste demeure une empreinte qui ne dit rien de son contenu. La signature reste valable, car elle n'a jamais porté sur un bloc indivisible.
Tout cela tient dans le code que le vérificateur lit : la signature, les empreintes et les données que vous avez choisi de montrer. Rien n'est téléchargé pendant la vérification, donc une porte, un autobus, un dispensaire rural ou un avion en vol peuvent vérifier une attestation sans aucun réseau.
Il reste une pièce, et c'est précisément celle qu'on oublie. Tout ce qui peut être scanné peut être photographié. Celui qui présente doit donc aussi signer un défi nouveau avec une clé privée qui ne quitte jamais son appareil. Sans cette étape, la capture d'écran de l'attestation d'une autre personne passerait. Cette bibliothèque refuse toute présentation qui en est dépourvue.
Faite pour le moment où il n'y a pas de réseau
J'ai construit l'eCNH, le permis de conduire numérique brésilien, utilisé par plus de 40 millions de personnes. Ce qui m'a le plus appris n'était pas l'application. C'était le bord de la route : un agent scannant un permis sur une nationale avec une barre de réseau, ou aucune, et qui a besoin d'un oui ou d'un non en moins d'une seconde.
Tout ce qu'il faut pour cette réponse tient dans le code. La signature prouve l'émetteur, les claims sont là, et la seule chose qui vienne de l'extérieur est la clé publique de l'émetteur, qui change si rarement qu'on peut la livrer avec l'application et la rafraîchir une fois par semaine.
Des bibliothèques existent pour cela. Ce sont des SDK d'entreprise : lourds, liés au profil d'un seul pays, et écrits comme si vous travailliez déjà dans l'identité numérique. Voici la version qu'un ingénieur produit ajoute un mardi.
Trois parties, et aucune n'est un serveur
Signer chaque claim une fois
L'émetteur décide quels claims pourront être retenus plus tard, et signe une empreinte pour chacun d'eux.
N'envoyer que ce qui est demandé
Le portefeuille laisse de côté les claims qu'il garde. La signature de l'émetteur reste valable sur ce qui reste.
Répondre sans demander à personne
Signature, expiration, révocation et chaque divulgation, contrôlées face à une clé embarquée sur l'appareil.
Prouvez que vous avez plus de 18 ans sans livrer votre date de naissance
Les lois sur le contrôle d'âge arrivent plus vite que les outils. L'implémentation habituelle fait téléverser au client une photo de sa pièce d'identité chez un tiers, ce qui est un désastre pour la vie privée et une fuite en attente.
La divulgation sélective fait cela correctement. Le vérificateur ne peut pas apprendre la date de naissance même s'il le veut, parce que cette valeur n'a jamais quitté le portefeuille. La propriété est cryptographique, pas une promesse dans une politique de confidentialité.
// Le justificatif contient nom, adresse, date de naissance et numéro de document.
// Le bar reçoit un booléen.
const presentation = await present(credential, { disclose: ['over_18'] })
const result = await verify(presentation, { trust })
result.claims // { over_18: true }
result.claims.birth_date // undefined, et n'a jamais été transmis
result.withheld // 4, et il ne peut pas dire lesquels
Des chiffres réels, y compris celui qui dérange
Un permis de conduire réaliste, huit claims, validité de cinq ans, pointeur vers une liste de statut. Mesuré, pas estimé :
| Justificatif | Caractères | Version du QR |
|---|---|---|
| tout visible | ~740 | 18, se scanne bien |
| les huit claims divulgables | ~1590 | 27, trop dense |
presenting only over_18 | ~1115 | 22, encore dense |
fits() vous dire où vous en êtes avant d'imprimer quoi que ce soit.
Ne me croyez pas sur parole
Le playground exécute toute la bibliothèque dans votre navigateur et lui envoie huit attaques réelles. Chacune affiche le code de refus qu'elle attend, de sorte que vous pouvez contrôler la bibliothèque face à ses propres affirmations plutôt que de faire confiance à un README.
Pourquoi celle-ci, et quand s'abstenir
Cinq raisons de la choisir, puis la partie honnête.
- Environ 1 300 lignes qu'on peut lireAssez petite pour qu'une seule personne la lise entièrement en un après-midi et sache ce qu'elle fait. Une sécurité qu'on ne peut pas lire est une sécurité acceptée sur parole.
- Aucune dépendanceRien venu de npm à l'exécution. Il n'y a pas d'autre chaîne d'approvisionnement à auditer que celle-ci, et rien qui puisse changer sous vos pieds la semaine prochaine.
- Elle refuse ce qu'elle ne peut pas vérifier entièrementPas de validation partielle, pas d'avertissement qu'on peut ignorer. Chaque échec revient sous la forme d'un motif nommé, que vous pouvez journaliser et traiter, jamais d'un simple faux.
- Elle tourne là où la vérification a lieuNode, Deno, Bun, navigateurs, React Native et edge workers. Le même code partout, sur la cryptographie standard déjà intégrée à la plateforme.
- Testée de trois façonsDes tests unitaires, les vecteurs officiels publiés avec la RFC 9901, et des tests de propriétés qui lui lancent des entrées aléatoires et malformées en cherchant le cas qui passerait au travers.
Et quand ce n'est pas le bon outil.
- Il vous faut des permis de conduire ISO 18013-5C'est un autre encodage, CBOR et COSE, et une autre norme. Cette bibliothèque parle SD-JWT, le format qu'utilisent les portefeuilles européens et les spécifications OpenID. Problème voisin, travail différent.
- Vous voulez un portefeuille completCeci vérifie et présente. Cela ne stocke pas les attestations, ne gère ni les appareils ni l'inscription. C'est une pièce, faite pour vivre à l'intérieur de votre produit.
- Il vous faut un audit externe avant un déploiement réglementéIl n'en existe pas encore. Les tests et les vecteurs de la RFC sont publics, chaque ligne aussi, mais pour un déploiement réglementé prévoyez un budget pour votre propre revue.
Installer
npm i qredential
Node 20 ou plus récent, tous les navigateurs actuels, React Native. Environ 1 300 lignes de code sans aucune dépendance à l'exécution, précisément pour que le lire avant de lui faire confiance soit réaliste.
Des normes, pas des inventions
- SD-JWT, RFC 9901divulgation sélective et liaison de clé, imbriquée et récursive, le mécanisme qu'utilise le portefeuille d'identité européen
- SD-JWT VCla forme du justificatif
- Token Status Listune révocation qui fonctionne depuis une copie en cache
- base45, RFC 9285l'enveloppe du QR, choisie pour la compatibilité des lecteurs
Pas de mDL ISO 18013-5, qui est du CBOR et du COSE plutôt que du JWT. C'est à la feuille de route, et un justificatif qui utilise quelque chose de non pris en charge est refusé plutôt que compris à moitié. Revendiquer la moitié d'une norme de conformité est pire que ne rien revendiquer.