Expression de besoin — PD-282¶
ProofEnvelope : scellement HSM (PROOF-14) + matériel de validation eIDAS (PROOF-05b)¶
| Champ | Valeur |
|---|---|
| Story | PD-282 |
| Titre | ProofEnvelope : scellement HSM (PROOF-14) + matériel validation eIDAS (PROOF-05b) |
| Labels | crypto, legal-compliance, rfc-pv-proof |
| Priorité | Medium |
| Auteur | Claude (orchestrateur IA) |
| Date | 2026-03-02 |
| Version | 1.0 |
| RFC de référence | PV-PROOF-001 v1.1.0 (2026-03-01) |
1. Contexte et problématique¶
ProbatioVault génère des preuves numériques probantes sous forme de ProofEnvelopes — des structures JSON signées contenant l'ensemble des éléments cryptographiques attestant de l'intégrité, de l'horodatage et de l'authenticité d'un document ou d'un événement.
Aujourd'hui, la vérification d'une ProofEnvelope nécessite un accès aux services internes de ProbatioVault : le service HSM pour la validation de signature, le service TSA pour la vérification de l'horodatage, et le service de vérification de certificats pour la chaîne de confiance eIDAS. Cette dépendance pose trois problèmes majeurs :
Problème 1 — Absence de scellement global. Chaque composant de la preuve (hash documentaire, signature HSM, jeton TSA, preuve Merkle) est vérifié indépendamment, mais il n'existe pas de sceau cryptographique couvrant l'enveloppe dans son ensemble. Un attaquant pourrait théoriquement substituer un composant valide par un autre composant valide provenant d'une enveloppe différente, sans que la vérification unitaire ne détecte l'incohérence.
Problème 2 — Impossibilité de vérification autonome. Un tiers (auditeur judiciaire, expert technique, juge) souhaitant vérifier la validité d'une preuve doit interroger les services ProbatioVault. En cas d'indisponibilité de la plateforme, de litige commercial, ou de procédure contradictoire, cette dépendance invalide de facto la valeur probante de l'enveloppe.
Problème 3 — Absence de preuve de non-révocation au moment T. Les certificats eIDAS et HSM peuvent être révoqués après la génération de la preuve. Sans snapshot des réponses OCSP et des chaînes de certificats au moment de la génération, un tiers ne peut pas distinguer une preuve générée avant la révocation d'une preuve générée après.
La RFC PV-PROOF-001 v1.1.0 répond à ces trois problèmes via deux mécanismes complémentaires : PROOF-14 (scellement HSM de l'enveloppe complète) et PROOF-05b (inclusion du matériel de vérification eIDAS).
2. Objectif¶
Rendre chaque ProofEnvelope auto-vérifiable : un tiers disposant uniquement du fichier JSON de l'enveloppe et d'un outil cryptographique standard (OpenSSL, Bouncy Castle, ou équivalent) doit pouvoir vérifier l'intégrité et l'authenticité de la preuve sans aucune connexion aux services ProbatioVault.
Concrètement, cette story doit :
- Sceller chaque ProofEnvelope finalisée avec une signature HSM ECDSA P-384 couvrant l'intégralité du contenu canonicalisé (JCS) de l'enveloppe.
- Inclure dans l'enveloppe tout le matériel cryptographique nécessaire à la vérification offline : chaîne de certificats TSA, chaîne de certificats eIDAS, réponses OCSP capturées au moment T, et horodatage de validation.
- Garantir que toute modification, même d'un seul octet, du contenu de l'enveloppe invalide le sceau.
- Préserver la compatibilité avec le trigger d'immutabilité PostgreSQL (PD-272) et le modèle d'états PENDING/INDETERMINATE (PD-280).
3. Périmètre fonctionnel¶
3.1. Lot 1 — MUST-HAVE (livré dans PD-282)¶
PROOF-14 : Scellement HSM de l'enveloppe¶
Le scellement consiste à produire un champ envelopeSeal ajouté à la ProofEnvelope après la persistance de tous les autres champs. Le processus est le suivant :
- Canonicalisation JCS : L'enveloppe complète (sans le champ
envelopeSeal) est sérialisée en JSON Canonicalization Scheme (RFC 8785) via leJsonCanonicalizeServiceexistant. - Hachage SHA3-384 : Le contenu canonicalisé est haché avec SHA3-384 (famille SHA-3, résistante aux attaques par extension de longueur).
- Signature ECDSA P-384 : Le hash est signé par le HSM via le
HsmServiceexistant, avec la clé de scellement identifiée par sonkid(Key ID). - Construction du champ
envelopeSeal:
{
"envelopeSeal": {
"algorithm": "ECDSA-P384-SHA3-384",
"signature": "<base64url>",
"kid": "pv-master-signing-2026",
"signedAt": "2026-03-02T14:30:00.000Z",
"certificateChain": ["<base64-DER-cert-1>", "<base64-DER-cert-2>"]
}
}
- Persistance : L'enveloppe complète (avec
envelopeSeal) est persistée. Le trigger d'immutabilité (PD-272) bloque toute modification ultérieure.
Le certificateChain dans le sceau contient la chaîne complète de la clé HSM de scellement, permettant au tiers de vérifier la signature sans interroger ProbatioVault.
PROOF-05b : Matériel de vérification eIDAS¶
Un nouveau champ verificationMaterial est ajouté à la ProofEnvelope, contenant :
{
"verificationMaterial": {
"tsaCertificateChain": ["<base64-DER>", "..."],
"eidasCertificateChain": ["<base64-DER>", "..."],
"ocspResponses": [
{
"certSerialNumber": "...",
"response": "<base64-DER-OCSP>",
"producedAt": "2026-03-02T14:29:58.000Z",
"status": "good"
}
],
"validationTimestamp": "2026-03-02T14:30:00.000Z",
"validationPolicy": "GENERATION_TIME_SNAPSHOT"
}
}
Les OCSP responses sont capturées au moment de la génération de la preuve et incluses en tant que snapshots. Elles attestent que les certificats n'étaient pas révoqués à l'instant T de génération. La politique de validation est explicite : GENERATION_TIME_SNAPSHOT signifie que le matériel reflète l'état des certificats au moment de la génération, pas au moment de la vérification.
3.2. Lot 2 — Découplable (story séparée si PD-280 non mergé)¶
PENDING explicite¶
Le modèle d'états de PD-280 introduit un état PENDING explicite pour les ProofEnvelopes en cours de constitution (avant finalisation). Ce lot inclut :
- Mapping de l'absence de sceau (
envelopeSeal === null) vers l'étatPENDING. - Exposition de l'état via l'API REST.
- Règle de compatibilité formelle : si PD-280 n'est pas mergé,
null == PENDINGest traité côté API uniquement sans colonne BDD dédiée.
Recommandation PO : découpler ce lot si PD-280 n'est pas prêt au moment de l'implémentation. Le scellement et le matériel de vérification sont indépendants du modèle d'états.
4. Modes de vérification¶
L'enveloppe scellée doit supporter deux modes de vérification par un tiers, correspondant à deux niveaux d'exigence.
4.1. Mode A — Offline strict (air-gapped)¶
Le vérificateur dispose uniquement du fichier JSON de la ProofEnvelope. Aucune connexion réseau n'est requise.
Étapes de vérification :
- Extraction du champ
envelopeSeal: Le vérificateur isole le sceau et reconstitue le contenu de l'enveloppe sans ce champ. - Canonicalisation JCS : Le contenu reconstitué est sérialisé en JCS (RFC 8785). Le vérificateur utilise une implémentation JCS locale (disponible dans toutes les langues majeures).
- Hachage SHA3-384 : Le contenu canonicalisé est haché.
- Vérification de signature ECDSA : Le hash est vérifié contre la signature du sceau, en utilisant la clé publique extraite du
certificateChaininclus dans le sceau. - Validation de la chaîne de certificats : Le vérificateur remonte la chaîne jusqu'à une racine de confiance (CA eIDAS). Les certificats intermédiaires sont inclus dans l'enveloppe.
- Vérification OCSP : Les réponses OCSP incluses dans
verificationMaterialattestent de la non-révocation des certificats au moment T. Le vérificateur vérifie la signature de chaque réponse OCSP. - Vérifications internes : Hash documentaire, signature HSM du document, jeton TSA, preuve Merkle vers la racine — chaque composant est vérifié avec les clés et certificats inclus dans l'enveloppe.
Limites du mode A : La vérification blockchain (ancrage Merkle root) n'est pas possible en mode air-gapped. Le vérificateur peut vérifier la cohérence interne (document hash -> Merkle tree -> root), mais pas que la root a été effectivement ancrée on-chain.
4.2. Mode B — Indépendant de PV mais online public¶
Le vérificateur dispose du fichier JSON et d'un accès internet public (sans accès aux services ProbatioVault).
Étapes supplémentaires par rapport au mode A :
- Vérification blockchain : Le vérificateur interroge un noeud public ou un explorer blockchain pour confirmer que la Merkle root déclarée dans l'enveloppe a été effectivement ancrée on-chain à l'horodatage attendu.
- Vérification OCSP live (optionnelle) : Le vérificateur peut interroger le répondeur OCSP de la CA pour obtenir le statut actuel des certificats, en complément du snapshot inclus dans l'enveloppe.
- Vérification CRL (optionnelle) : Le vérificateur peut télécharger la CRL courante de la CA pour une vérification exhaustive.
Ce mode ne dépend pas de ProbatioVault : toutes les sources consultées (nodes blockchain, répondeurs OCSP, CRL Distribution Points) sont des services publics opérés par des tiers de confiance.
5. Contraintes techniques¶
5.1. HSM¶
| Paramètre | Valeur |
|---|---|
| Algorithme | ECDSA |
| Courbe | P-384 (secp384r1) |
| Hash | SHA3-384 |
| Format signature | DER-encoded, exposé en base64url |
| Key ID | Champ kid dans le sceau |
| Labels production | Préfixe pv-master-* |
| Labels test | Préfixe pv-test-* |
| Rotation | Le kid + certificateChain permettent au tiers de retrouver la clé publique historique |
La rotation de clé HSM ne doit pas invalider les enveloppes existantes. Chaque enveloppe porte son propre kid et sa propre chaîne de certificats, rendant la vérification indépendante de l'état courant du HSM.
5.2. eIDAS et TSA¶
| Paramètre | Valeur |
|---|---|
| Politique de validation | GENERATION_TIME_SNAPSHOT — snapshot au moment T |
| OCSP | Réponses capturées à la génération, incluses en DER |
| Chaînes de certificats | TSA + eIDAS, format DER encodé en base64 |
| CRL | Non incluse (trop volumineuse). OCSP suffit pour le mode offline |
L'inclusion des OCSP responses est suffisante pour le mode offline. Les CRL sont disponibles en mode B via les CRL Distribution Points encodés dans les certificats.
5.3. Canonicalisation¶
La canonicalisation JCS (RFC 8785) est le standard choisi pour la sérialisation déterministe du JSON. Le JsonCanonicalizeService existant implémente déjà cette norme. Points d'attention :
- L'enveloppe est canonicalisée sans le champ
envelopeSeal(ce champ est exclu avant la canonicalisation, puis ajouté après). - L'ordre d'exclusion doit être déterministe : supprimer la clé
envelopeSealdu dictionnaire avant la canonicalisation. - Les nombres flottants suivent la norme IEEE 754 avec la sérialisation ECMAScript.
5.4. Latence et performance¶
| Paramètre | Contrainte |
|---|---|
| Moment du scellement | Synchrone, post-persist dans generateProof() |
| Appels HSM | 1 signature par enveloppe (le scellement) |
| Appels OCSP | N appels (1 par certificat dans les chaînes TSA + eIDAS) |
| Latence cible | < 500ms pour le scellement complet (HSM + OCSP) |
| Parallélisation | Les appels OCSP peuvent être parallélisés |
Le scellement est synchrone : generateProof() ne retourne pas tant que le sceau n'est pas persisté. Le trigger d'immutabilité (PD-272) s'applique immédiatement après.
5.5. Taille de l'enveloppe¶
L'inclusion des chaînes de certificats et des réponses OCSP augmente la taille de l'enveloppe. Estimation :
| Composant | Taille estimée |
|---|---|
| Chaîne certificats TSA | ~3-5 KB (2-3 certs) |
| Chaîne certificats eIDAS | ~3-5 KB (2-3 certs) |
| Chaîne certificats HSM | ~2-3 KB (1-2 certs) |
| Réponses OCSP | ~2-4 KB (2-4 réponses) |
| Signature sceau | ~96 bytes (P-384) |
| Total overhead | ~10-17 KB |
Cet overhead est acceptable pour des enveloppes dont la taille typique est de 5-50 KB. La taille totale reste largement en dessous des limites PostgreSQL (jsonb supporte jusqu'à 255 MB) et des seuils de transfert API.
6. Critères d'acceptation¶
Critères fonctionnels (Jira)¶
| ID | Critère | Lot |
|---|---|---|
| CA-01 | Toute ProofEnvelope finalisée possède un champ envelopeSeal avec une signature HSM valide | 1 |
| CA-02 | La vérification par un tiers (recalcul JCS + SHA3-384 + ECDSA verify P-384) réussit | 1 |
| CA-03 | La modification d'un seul octet de l'enveloppe provoque l'échec de la vérification du sceau | 1 |
| CA-04 | verificationMaterial inclut tsaCertificateChain, eidasCertificateChain, ocspResponses, validationTimestamp | 1 |
| CA-05 | Les réponses OCSP sont capturées au moment de la génération (snapshot à l'instant T) | 1 |
| CA-06 | Aucun secret cryptographique dans l'enveloppe (clés privées, tokens de session HSM) | 1 |
| CA-07 | Le trigger d'immutabilité bloque toujours UPDATE/DELETE après persistance | 1 |
| CA-08 | Pipeline CI vert (lint + tests + Sonar QG) | 1 |
Critères de vérification (ajoutés)¶
| ID | Critère | Lot |
|---|---|---|
| CA-09 | Mode A (offline) : un script de vérification autonome valide l'enveloppe sans connexion réseau, en utilisant uniquement les données incluses dans le JSON | 1 |
| CA-10 | Mode A (offline) : la chaîne de certificats HSM incluse dans envelopeSeal.certificateChain remonte jusqu'à une racine de confiance | 1 |
| CA-11 | Mode B (online public) : la Merkle root déclarée dans l'enveloppe est retrouvable sur un explorer blockchain public | 2* |
| CA-12 | Rotation de clé HSM : une enveloppe scellée avec une clé historique (kid antérieur) reste vérifiable après rotation | 1 |
| CA-13 | La politique de validation GENERATION_TIME_SNAPSHOT est explicitement déclarée dans verificationMaterial.validationPolicy | 1 |
*CA-11 dépend de l'infrastructure blockchain existante et peut être vérifié indépendamment.
7. Dépendances et prérequis¶
Dépendances internes¶
| Story | Composant | Statut | Impact |
|---|---|---|---|
| PD-81 | LegalCompositeProof | Livré | Structure de base de la ProofEnvelope |
| PD-272 | Immutability trigger | Livré | Le trigger doit couvrir l'enveloppe avec sceau |
| PD-280 | PENDING/INDETERMINATE | En cours | Lot 2 dépend de ce modèle d'états |
Briques existantes réutilisées¶
| Service | Usage dans PD-282 |
|---|---|
JsonCanonicalizeService | Canonicalisation JCS de l'enveloppe avant hachage |
HsmService | Signature ECDSA P-384 du hash de l'enveloppe |
TsaClientService | Récupération de la chaîne de certificats TSA |
SignatureVerificationService | Vérification locale (tests + mode A) |
Prérequis techniques¶
- Le
HsmServicedoit exposer une méthode de signature avec retour dukidet de la chaîne de certificats associée. Si cette méthode n'existe pas, elle doit être ajoutée dans le cadre de PD-282. - Le
TsaClientServicedoit permettre la récupération de la chaîne de certificats TSA. Si seul le jeton TSA est retourné aujourd'hui, l'extraction de la chaîne doit être ajoutée. - Un mécanisme d'appel OCSP doit être disponible (via un
OcspClientServiceexistant ou à créer). Les répondeurs OCSP sont identifiés via l'extension AIA (Authority Information Access) des certificats.
8. Hors périmètre¶
Les éléments suivants sont explicitement exclus de PD-282 :
- Outil CLI de vérification : Un outil de vérification packagé pour les tiers (CLI, bibliothèque, application web) n'est pas dans le périmètre. Seul un script de test interne démontrant la faisabilité du mode A est requis (CA-09).
- Interface utilisateur : Aucune modification de l'application front-end. Le scellement est un processus backend transparent.
- Ancrage blockchain : Le mécanisme d'ancrage de la Merkle root on-chain est un processus existant. PD-282 ne modifie pas ce processus.
- Révocation de clé HSM : La procédure de révocation d'une clé HSM compromise (re-scellement des enveloppes, notification des tiers) est un sujet distinct.
- Format de preuve alternatif : Pas de génération en format standard (CAdES, XAdES, PAdES). Le format est le JSON natif ProbatioVault.
- Validation LTV (Long-Term Validation) : L'archivage long terme avec re-horodatage périodique (norme ETSI EN 319 122) n'est pas couvert. Le
GENERATION_TIME_SNAPSHOTest suffisant pour la validité initiale. - Gestion du Lot 2 (PENDING explicite) si PD-280 n'est pas mergé : sera traité dans une story dédiée.
9. Risques identifiés¶
| # | Risque | Probabilité | Impact | Mitigation |
|---|---|---|---|---|
| R1 | PD-280 non mergé au moment de l'implémentation | Moyenne | Faible | Lot 2 découplé. Lot 1 est indépendant du modèle d'états |
| R2 | Latence OCSP excessive (répondeurs externes lents) | Moyenne | Moyen | Parallélisation des appels OCSP. Timeout avec fallback graceful (inclure un champ ocspUnavailable: true si timeout) |
| R3 | Taille d'enveloppe trop importante pour certains cas d'usage | Faible | Faible | Overhead estimé ~10-17 KB, acceptable. Monitoring à mettre en place |
| R4 | Changement de format JCS entre versions de la bibliothèque | Faible | Élevé | Verrouiller la version de la bibliothèque JCS. Tests de non-régression sur la canonicalisation |
| R5 | Répondeur OCSP indisponible au moment de la génération | Faible | Moyen | Stratégie dégradée : générer l'enveloppe avec ocspResponses: [] et validationPolicy: "OCSP_UNAVAILABLE". Le scellement reste valide, mais le mode A perd la preuve de non-révocation |
| R6 | Incompatibilité trigger immutabilité avec le scellement post-persist | Faible | Élevé | Le scellement doit être fait AVANT le trigger, ou le trigger doit autoriser l'ajout du sceau comme opération atomique unique post-INSERT |
| R7 | Clé HSM de scellement différente de la clé de signature documentaire | Faible | Faible | Clarifier dans la spécification si la même clé ou deux clés distinctes sont utilisées. Deux clés recommandées (séparation des responsabilités) |
Le risque R6 est critique : il faut vérifier que le trigger d'immutabilité de PD-272 permet l'ajout du champ envelopeSeal après l'INSERT initial. Si le trigger bloque tout UPDATE, le scellement doit être intégré dans la même transaction que l'INSERT (INSERT de l'enveloppe complète avec sceau). Cela implique que le scellement se fasse AVANT la persistance, pas après.
10. Questions ouvertes restantes¶
| # | Question | Impact | Proposition |
|---|---|---|---|
| Q1 | Le trigger d'immutabilité PD-272 autorise-t-il un UPDATE pour ajouter envelopeSeal post-INSERT, ou faut-il insérer l'enveloppe complète (avec sceau) en une seule opération ? | Architecture | Vérifier le code du trigger. Si blocage total, sceller avant INSERT dans la même transaction |
| Q2 | Le HsmService expose-t-il déjà le kid et la chaîne de certificats associée lors d'une signature, ou faut-il enrichir l'interface ? | Implémentation | Auditer l'interface existante du HsmService. Enrichir si nécessaire |
| Q3 | Faut-il utiliser la même clé HSM pour le scellement de l'enveloppe et pour la signature du document, ou deux clés distinctes ? | Sécurité | Deux clés distinctes recommandées (séparation des responsabilités : pv-master-signing-* pour documents, pv-master-sealing-* pour enveloppes) |
| Q4 | Quel comportement en cas d'indisponibilité du répondeur OCSP : bloquer la génération ou générer avec matériel incomplet ? | Disponibilité | Générer avec matériel incomplet + flag explicite ocspUnavailable. La preuve reste scellée, seule la preuve de non-révocation est absente |
| Q5 | Le OcspClientService existe-t-il ou doit-il être créé dans le cadre de PD-282 ? | Implémentation | Vérifier l'existant. Si absent, créer un service minimal (requête HTTP vers le répondeur OCSP identifié par l'extension AIA du certificat) |
| Q6 | Faut-il inclure les certificats racine (root CA) dans les chaînes, ou s'arrêter aux intermédiaires (le tiers fournit sa propre trust store) ? | Interopérabilité | Inclure la chaîne complète jusqu'à la racine. Le tiers peut ignorer la racine s'il a sa propre trust store, mais l'inclusion facilite la vérification autonome |
Annexe A — Flux de scellement (séquence simplifiée)¶
generateProof()
│
├── 1. Assembler ProofEnvelope (hash doc, signature HSM doc, TSA token, Merkle proof)
│
├── 2. Capturer le matériel de vérification
│ ├── Récupérer tsaCertificateChain via TsaClientService
│ ├── Récupérer eidasCertificateChain
│ ├── Appeler OCSP pour chaque certificat (parallélisé)
│ └── Construire verificationMaterial { chains, ocspResponses, validationTimestamp, policy }
│
├── 3. Ajouter verificationMaterial à l'enveloppe
│
├── 4. Sceller l'enveloppe
│ ├── Exclure le champ envelopeSeal (absent à ce stade)
│ ├── Canonicaliser (JCS / RFC 8785)
│ ├── Hasher (SHA3-384)
│ ├── Signer via HsmService (ECDSA P-384)
│ └── Construire envelopeSeal { algorithm, signature, kid, signedAt, certificateChain }
│
├── 5. Ajouter envelopeSeal à l'enveloppe
│
├── 6. Persister (INSERT atomique — trigger immutabilité actif)
│
└── 7. Retourner ProofEnvelope complète
Annexe B — Invariants RFC applicables¶
| Invariant | Nom | Description |
|---|---|---|
| INV-11 | EnvelopeSealValid | Le sceau de l'enveloppe doit être cryptographiquement valide. Vérifié par recalcul JCS + SHA3-384 + ECDSA verify avec la clé publique du certificat identifié par kid |
| INV-12 | OfflineVerificationMaterialSufficient | Le matériel de vérification doit être suffisant pour une validation offline. Vérifié par la présence et la validité de : tsaCertificateChain, eidasCertificateChain, ocspResponses, validationTimestamp |