Signatures et clés d'accès
Un reçu porte deux signatures : celle de l'entreprise, attestant l'offre qu'elle a faite, et celle du citoyen, attestant la réponse qu'il a donnée. Elles utilisent des suites différentes parce que les deux parties sont des signataires de nature différente.
La signature d'entreprise : eddsa-jcs-2022
L'entreprise signe l'offre avec eddsa-jcs-2022 : une signature détachée
Ed25519 sur les octets JCS-canoniques de l'offre, publiée comme une preuve W3C
Data Integrity.
companySig = Ed25519-sign( JCS(offer), companySecretKey )
La signature est exposée comme un DataIntegrityProof :
{
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"created": "2026-06-26T15:00:00Z",
"verificationMethod": "did:web:agreely.ca:c:acme#kms-1",
"proofPurpose": "assertionMethod",
"proofValue": "z3Ge8...base58btc..."
}
proofValue est la signature de 64 octets sous forme de chaîne multibase
base58btc préfixée par z.
Le DID de l'entreprise : did:web, hébergé par Agreely
Une entreprise signe en tant que did:web:agreely.ca:c:{slug}. Agreely héberge le
document à https://agreely.ca/c/{slug}/did.json. Le document est lié au
domaine réel et vérifié de l'entreprise via alsoKnownAs, de sorte qu'un
vérificateur voit à la fois l'identifiant à faible friction hébergé par Agreely et
le domaine prouvé. Une entreprise sans domaine vérifié n'a pas de document
résoluble, ce qui est l'exigence de liaison. did:web est le bon choix ici
précisément parce qu'une entreprise est un domaine; la mise à niveau ultérieure
remplace simplement l'hôte par le propre domaine de l'entreprise.
La clé est publiée sous forme de Multikey publicKeyMultibase, de sorte qu'un
vérificateur la résout depuis le document DID et contrôle companySig sans aucune
confiance en Agreely. Voir documents DID.
La signature du citoyen : les clés d'accès WebAuthn
Les citoyens ne détiennent jamais de fichier de clé privée. Ils signent avec une clé d'accès WebAuthn : l'authentificateur de plateforme (Face ID, Touch ID, une clé de sécurité) détient le secret, et le reçu porte l'assertion qui en résulte. La bibliothèque garantit, gratuitement, que le défi de l'assertion égale celui qu'Agreely a émis et que la signature se vérifie par rapport à la clé publique COSE stockée.
Il y a deux défis différents, et la différence compte.
Intégration : un défi aléatoire
L'inscription et la connexion utilisent un défi aléatoire de 32 octets, le flux WebAuthn standard. Cela prouve que la personne contrôle l'authentificateur. L'inscription exige la vérification de l'utilisateur et une clé résidente (découvrable), et accepte les justificatifs ES256, EdDSA ou RS256.
Approbation : un défi keccak lié au consentement
Lorsque la personne approuve un consentement, le défi n'est pas aléatoire. C'est un hachage keccak256 qui lie la signature exactement à cette offre et à sa racine de Merkle :
challenge = keccak256( JCS(context) || nonce32 )
où le contexte de liaison est figé ainsi :
{
"issuer": "did:web:agreely.ca:c:acme",
"subject": "did:agreely:citizen:9F8K...",
"requestId": "0x...",
"action": "grant",
"grantedAt": "2026-06-26T15:04:05Z",
"merkleRoot": "0x..."
}
L'assertion qui atterrit dans le reçu est une preuve WebAuthnAssertion qui porte
le verificationMethod (le fragment #passkey-n du DID), le challenge, ainsi
que les authenticatorData, clientDataJSON et signature bruts. Un vérificateur
résout la clé COSE qui était valide à grantedAt et contrôle
l'assertion par rapport à elle.
Ensuite
- Registre sur la chaîne : ancrer la racine et les révocations.
- Le reçu VC : les deux preuves dans un seul document.