Trustvice est en bêta restreinte. À l’inscription ou à la connexion, vous devez saisir le mot de passe de bêta-testeur que vous avez reçu ou en demander un à preview@trustvice.com. Merci.
Chargement…
Ingénierie
Vérification
Tutoriel
Vérifier un reçu hors ligne, en une quarantaine de lignes de code
Un exemple concret qui construit un vérifieur de reçus sans dépendance avec WebCrypto, le contrôle contre un vecteur de test signé, prouve qu’il rejette les reçus altérés, puis porte la même procédure en Python et en shell.
Par The Trustvice team··Lecture 5 min
La façon la plus solide de contrôler un reçu Trustvice, c’est avec du code que vous maîtrisez, sur une machine que vous maîtrisez, contre des clés que vous avez épinglées vous-même. Cet article construit ce vérifieur à partir de rien. À la fin, vous aurez un seul module JavaScript sans dépendance, un vecteur de test qui prouve qu’il marche, et une liste de cas qui prouvent qu’il échoue quand il le doit.
Commencer gratuitement
Trustvice 360
Déclarez les règles. Faites vérifier chaque exécution.
Agents, preuves, gouvernance, commerce et divulgation sont dans Trustvice 360. Inscrivez-vous gratuitement et ouvrez toutes les suites depuis un seul tableau de bord.
Node 20 ou plus, ou tout runtime dont crypto.subtle gère Ed25519 : navigateurs actuels, Deno, Bun et Cloudflare Workers conviennent tous. Aucun paquet à installer. Pas besoin non plus de compte Trustvice : le vecteur de test ci-dessous est signé par une clé jetable générée pour cet article.
Enregistrez ceci sous receipt.json. C’est un reçu de test signé avec cette clé jetable. Son corps n’est qu’illustratif ; ce qui compte, c’est qu’il est réellement signé.
Pour de vrais reçus, vous remplacerez keys.json par le jeu publié sur https://trustvice.com/.well-known/receipt-keys.json, idéalement une copie que vous avez épinglée. La procédure ne change pas.
La signature couvre la forme canonique RFC 8785 du reçu sans son membre signature. En JavaScript, JSON.stringify produit déjà la forme numérique ECMAScript et l’échappement minimal des chaînes. Reste à trier les clés d’objet, récursivement, par unité de code UTF-16, ce qui est l’ordre par défaut d’Array.prototype.sort :
js
function canonicalize(value) {
if (Array.isArray(value)) return value.map(canonicalize);
if (value && typeof value === "object") {
return Object.fromEntries(
Object.keys(value).sort().map((key) => [key, canonicalize(value[key])]),
);
}
return value;
}
exportconst jcs = (value) => JSON.stringify(canonicalize(value));
Pour le vecteur de test, la chaîne canonique tient sur une ligne sans espace :
Étape 4 : contrôler la forme, trouver la clé, vérifier#
Voici le vérifieur lui-même. Il ne lève jamais d’exception sur une entrée hostile ; il renvoie l’une de trois raisons, les mêmes que la page de vérification et l’API.
Notez la ligne de déstructuration. Elle retire signature et garde tout le reste, y compris des membres que ce code ne connaît pas. C’est voulu : chaque membre sauf signature est signé, donc un vérifieur qui écarte un membre qu’il ne reconnaît pas rejetterait des reçus valides.
Réunissez les trois extraits dans verify-receipt.mjs. C’est tout le vérifieur.
Un petit wrapper en ligne de commande lit les fichiers et sort avec 0 pour un reçu valide, 1 sinon. Il accepte aussi l’enveloppe { "receipt": … } que renvoie l’API :
Un vérifieur qui ne dit jamais que « valide » est pire que rien. Modifiez receipt.json et lancez chaque cas :
Modification
Résultat attendu
byteLength de 4 à 5
bad_signature
N’importe quel caractère d’issuedAt ou de receiptId
bad_signature
Ajouter "note": "x" au premier niveau
bad_signature, car chaque membre sauf signature est signé
Le dernier caractère de signature
bad_signature
signatureKeyId à AAAAAAAAAAAAAAAAAAAAAA
unknown_key
body.type à autre chose que type
malformed_envelope
Tout le fichier à {"version":"1"}
malformed_envelope, sans exception
Gardez-les comme tests automatisés à côté de votre vérifieur. Ils ne coûtent rien et attrapent les erreurs que nous voyons le plus souvent dans les portages.
Le kid dérive de la clé : le base64url des 16 premiers octets du SHA-256 des 32 octets bruts de x. Pour la clé de test, cela donne LfPAIisXFvNmFhD6vBQbBw. Le recalculer confirme que l’entrée de votre fichier de clés est cohérente :
js
exportasyncfunction keyIdOf(x) {
const digest = await crypto.subtle.digest("SHA-256", base64urlToBytes(x));
let bin = "";
for (const b ofnew Uint8Array(digest).slice(0, 16)) bin += String.fromCharCode(b);
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}
La procédure est partout la même, mais deux langages demandent de l’attention.
Python.json.dumps n’est pas JCS : il écrit les flottants entiers sous la forme 1.0, complète les exposants et échappe le non-ASCII par défaut. Un portage Python a besoin d’un petit sérialiseur RFC 8785 qui trie les clés par unité de code UTF-16, et peut vérifier avec Ed25519PublicKey du paquet cryptography.
Shell. Avec jq et OpenSSL 3, jq -S -c 'del(.signature)' produit les octets canoniques des reçus, car leurs corps ne contiennent que chaînes, entiers, booléens, null, tableaux et objets. Ce n’est pas une implémentation JCS générale, mais pour un reçu, soit elle concorde, soit elle échoue ; elle n’en valide jamais un qu’elle ne devrait pas. La clé publique est enveloppée en SubjectPublicKeyInfo DER en préfixant un en-tête fixe de 12 octets aux 32 octets bruts, et openssl pkeyutl -verify -rawin fait le reste. Le LibreSSL livré avec macOS n’y arrive pas ; installez OpenSSL 3.
Une signature valide prouve l’origine, l’intégrité et une heure signée : le détenteur d’une clé de signature Trustvice a signé exactement ces octets, rien n’a changé depuis, et Trustvice affirme l’heure dans issuedAt. Elle ne prouve pas qu’un fait que vous avez fourni est vrai, et elle ne contient pas votre contenu. Pour montrer qu’un document concorde avec un reçu, hachez-le vous-même et comparez. Nous traitons ces limites dans Ce que prouve un reçu, et ce qu’il ne prouve pas.
Pour un contrôle rapide sans écrire de code, collez n’importe quel reçu dans /verify. La même procédure tourne dans votre navigateur et ne nous envoie jamais le reçu.