PD-282 — Specification Review
Story : PD-282 — ProofEnvelope auto-vérifiable : scellement HSM global et matériel eIDAS/OCSP embarqué
Auditeur : Claude (Opus 4.6)
Date : 2026-03-02
Documents analysés :
- PD-282-specification.md
- PD-282-tests.md
Synthèse
| Gravité | Nombre |
| Bloquant | 3 |
| Majeur | 9 |
| Mineur | 6 |
| Total | 18 |
Constats détaillés
AMB-01 — Liste de patterns anti-secrets extensible sans gouvernance
Type : Ambiguïté
Référence : Spec §4 INV-282-06
Description : L'invariant qualifie la liste de patterns interdits de « minimaux » (privateKey, secretKey, sessionToken, hmacSecret, dek). Le terme « minimaux » implique que la liste peut être étendue, mais aucun mécanisme de gouvernance n'est défini : qui ajoute un pattern, quand, avec quel processus de validation, et quel impact sur les enveloppes déjà scellées avec l'ancienne liste.
Impact : Un implémenteur ne peut pas déterminer le contrat exact. Deux implémentations pourraient diverger sur la liste effective. L'extension non gouvernée pourrait invalider des flux de production.
Gravité : Majeur
AMB-02 — Rôle respectif de envelopeSeal.certificateChain vs verificationMaterial.*CertificateChain
Type : Ambiguïté
Référence : Spec §5.1.1 (certificateChain[]) et §5.1.2 (tsaCertificateChain[], eidasCertificateChain[])
Description : La spec définit trois tableaux de chaînes de certificats sans expliciter leur rôle fonctionnel respectif. Le lecteur doit inférer que `envelopeSeal.certificateChain` sert à vérifier le sceau global, `tsaCertificateChain` à vérifier le token TSA (PD-81), et `eidasCertificateChain` les signatures eIDAS. Aucune section ne contractualise cette sémantique.
Impact : Un implémenteur ou vérificateur tiers pourrait confondre les chaînes, utiliser la mauvaise chaîne pour une vérification donnée, ou considérer certaines chaînes comme redondantes.
Gravité : Majeur
Type : Ambiguïté
Référence : Spec §5.1.1 (certificateChain[]) et §5.1.2 (ocspResponses[].response)
Description : Deux labels de format sont utilisés — « base64-DER » pour les certificats et « base64-DER-OCSP » pour les réponses OCSP — mais la regex de validation est identique (`^[A-Za-z0-9+/]+={0,2}$`). Le spec ne définit pas si « base64-DER-OCSP » désigne un encodage distinct (DER d'un OCSPResponse ASN.1) ou une simple convention de nommage.
Impact : Sans distinction formelle, un implémenteur pourrait accepter un certificat DER comme réponse OCSP (ou inversement) si seule la regex est vérifiée.
Gravité : Mineur
AMB-04 — « Mécanisme de réconciliation existant » non référencé
Type : Ambiguïté
Référence : Spec §5.5 Atomicité multi-composant
Description : Le scénario crash post-commit mentionne un « rattrapage asynchrone par mécanisme de réconciliation existant » sans référence à un PD, un module, ou une documentation. Ce mécanisme est présenté comme un fait acquis mais aucun contrat ne permet de vérifier son existence ou sa compatibilité avec les invariants PD-282.
Impact : Si le mécanisme n'existe pas ou n'est pas compatible avec l'immutabilité PD-282, le comportement post-crash est indéfini.
Gravité : Majeur
AMB-05 — Ordre temporel implicite : verificationMaterial avant calcul du sceau
Type : Ambiguïté
Référence : Spec §4 INV-282-02 + §5.1.1 + §5.1.2
Description : Le sceau couvre l'enveloppe complète hors `envelopeSeal` (INV-282-02). `verificationMaterial` fait partie du contenu scellé. Cela implique que `verificationMaterial` (OCSP, chaînes, timestamps) DOIT être assemblé AVANT le calcul du sceau. Cet ordonnancement temporel est une contrainte d'implémentation critique jamais explicitée.
Impact : Un implémenteur pourrait tenter de sceller d'abord puis ajouter le verificationMaterial, produisant un sceau qui ne couvre pas le matériel de vérification.
Gravité : Majeur
Type : Ambiguïté
Référence : Spec §5.1.2 (ocspResponses, comportement si invalide)
Description : Le tableau de format indique « si vide: autorisé uniquement avec policy dégradée ». Le terme « policy dégradée » n'est pas défini ; il faut croiser avec ERR-05 et l'enum `validationPolicy` pour comprendre qu'il désigne `OCSP_UNAVAILABLE`. L'enum n'a que deux valeurs, donc l'équivalence est déductible, mais le contrat devrait être explicite.
Impact : Mineur en l'état (deux valeurs d'enum), mais risque d'ambiguïté si l'enum est étendue.
Gravité : Mineur
AMB-07 — Absence de distinction « champ absent » vs « tableau vide » pour ocspResponses
Type : Ambiguïté
Référence : Spec §5.1.2 (ocspResponses) + Tests §7 TC-NEG-06
Description : La spec distingue « absent » (rejet) de « vide » (autorisé avec OCSP_UNAVAILABLE). Cependant, TC-NEG-06 dit « ocspResponses absent → Rejet finalisation » sans préciser que « absent » signifie « clé JSON manquante » et non « tableau vide [] ». La distinction est structurellement critique (schema validation vs business logic) mais implicite.
Impact : Un implémenteur de tests pourrait tester le mauvais cas (tableau vide au lieu de clé absente, ou inversement).
Gravité : Mineur
Type : Contradiction
Référence : Spec §6 ERR-05 (note)
Description : La note ERR-05 déclare qu'« un mécanisme de monitoring DOIT alerter si le taux d'enveloppes avec OCSP_UNAVAILABLE dépasse 10% sur 1h », puis affirme que ce mécanisme est « hors périmètre fonctionnel de PD-282 » tout en stipulant qu'il « DOIT être implémenté dans l'infrastructure de monitoring existante ». Un DOIT hors périmètre est une contradiction : soit c'est dans le périmètre (et un invariant manque), soit c'est une recommandation (et « DOIT » est inapproprié).
Impact : Aucune story ne porte ce requirement. Risque d'oubli ou de contestation lors de l'implémentation.
Gravité : Majeur
CTR-02 — Stockage des nouveaux champs vs « Aucune migration DDL applicable »
Type : Contradiction
Référence : Spec §5.6 + §5.1.1 + §5.1.2
Description : La spec introduit deux structures de données (`envelopeSeal`, `verificationMaterial`) avec des contrats de format détaillés, puis déclare « Aucune stratégie de migration DDL applicable ». Le modèle de stockage n'est pas précisé. Si ces champs sont dans une colonne `jsonb` existante, aucun DDL n'est requis. Si une colonne ou table dédiée est nécessaire, la mention §5.6 est fausse. L'absence de précision rend le contrat invérifiable.
Impact : Un implémenteur ne peut pas déterminer si un DDL est attendu ou interdit.
Gravité : Mineur
IST-01 — Mode B (vérification online publique) sans test nominal positif
Type : Incohérence Spec↔Tests
Référence : Spec §2 (Inclus), §3 (Définitions Mode B) / Tests (absent)
Description : La spec inclut explicitement la « Vérification tierce en mode online public (sans service ProbatioVault) » et définit Mode B (blockchain/OCSP/CRL publics). TC-NOM-04 couvre uniquement Mode A (air-gapped). TC-ERR-08 couvre l'échec dans les deux modes. Aucun test nominal ne vérifie le chemin positif Mode B.
Impact : Mode B est contractualisé en périmètre mais n'a aucune preuve de conformité positive.
Gravité : Bloquant
IST-02 — TC-NOM-08 (atomicité) non rattaché à un invariant
Type : Incohérence Spec↔Tests
Référence : Tests §3 TC-NOM-08 / Tests §2 Matrice de couverture
Description : TC-NOM-08 teste les scénarios crash pre/post-commit (§5.5) mais n'apparaît pas dans la matrice de couverture §2 car aucun invariant ne couvre l'atomicité. Le test existe mais n'est formellement rattaché à aucun invariant ni critère d'acceptation.
Impact : Un audit de couverture basé sur la matrice conclura que l'atomicité n'est pas testée, alors qu'un test existe. Traçabilité incomplète.
Gravité : Mineur
IST-03 — Normalisation certSerialNumber non testée
Type : Incohérence Spec↔Tests
Référence : Spec §5.1.2 (règle de normalisation certSerialNumber) / Tests (absent)
Description : La spec contractualise une normalisation uppercase à l'ingestion pour `certSerialNumber` (case-insensitive acceptée en entrée, uppercase stocké). Aucun test ne vérifie : (a) qu'une entrée en minuscules/mixte est acceptée, (b) que la forme normalisée est persistée, (c) que les comparaisons utilisent la forme normalisée.
Impact : La normalisation pourrait ne pas être implémentée sans détection par la suite de tests.
Gravité : Majeur
IST-04 — Pas de test négatif pour producedAt > validationTimestamp
Type : Incohérence Spec↔Tests
Référence : Spec CA-05 / Tests §3 TC-NOM-03 (THEN clause)
Description : TC-NOM-03 vérifie que `producedAt <= validationTimestamp` en cas nominal. Mais aucun test ne spécifie le comportement lorsque cette condition est violée (producedAt dans le futur par rapport à validationTimestamp). La spec ne définit pas si c'est un rejet, un warning, ou un état accepté.
Impact : Comportement indéfini en cas d'incohérence temporelle OCSP. Un implémenteur pourrait accepter silencieusement des snapshots OCSP postérieurs au scellement.
Gravité : Majeur
IST-05 — Comportement à la finalisation si OCSP status = « revoked » non spécifié ni testé
Type : Incohérence Spec↔Tests
Référence : Spec §5.1.2 (ocspResponses[].status enum good|revoked|unknown) / Tests (absent)
Description : La spec définit l'enum `status` incluant `revoked` comme valeur valide, mais ne spécifie pas le comportement métier lorsque le statut OCSP d'un certificat de la chaîne est « revoked » au moment de la finalisation. Aucun test ne couvre ce cas. L'enveloppe pourrait être scellée avec un certificat révoqué sans alerte ni rejet.
Impact : Une enveloppe scellée contenant un statut OCSP « revoked » aurait une force probante contestable devant un auditeur ou tribunal (certificat de confiance révoqué au moment de la preuve).
Gravité : Bloquant
IST-06 — Pas de test pour substitution inter-enveloppes de composants internes
Type : Incohérence Spec↔Tests
Référence : Spec §4 INV-282-01 (justification « Empêche substitution de composants valides inter-enveloppes ») / Tests §7 TC-NEG-09
Description : TC-NEG-09 teste la copie de `envelopeSeal` d'une enveloppe vers une autre. Mais INV-282-01 mentionne la substitution de « composants » (pluriel). Aucun test ne vérifie la substitution d'un composant interne (ex : remplacement du `verificationMaterial` d'une enveloppe par celui d'une autre, le seal restant celui de l'enveloppe originale). Ce cas est couvert architecturalement (le seal protège le contenu) mais sans test explicite.
Impact : La justification d'INV-282-01 invoque un scénario d'attaque sans test dédié couvrant la substitution de composants internes.
Gravité : Mineur
HD-01 — signedAt exclu du sceau : horodatage de scellement falsifiable
Type : Hypothèse dangereuse
Référence : Spec §5.1.1 (envelopeSeal.signedAt) + §4 INV-282-02
Description : `signedAt` est un champ de `envelopeSeal`, qui est exclu intégralement du calcul du sceau (INV-282-02). Contrairement à `algorithm` (implicitement lié par la vérification cryptographique), `kid` (lié par la lookup de clé), et `certificateChain` (liée par la correspondance clé publique), `signedAt` n'a aucune liaison implicite ni explicite avec le sceau. Un attaquant avec accès en écriture au stockage peut modifier `signedAt` sans invalider la vérification.
Impact : Dans un contexte de conformité légale (eIDAS), l'horodatage de scellement est un élément probatoire. Sa falsification sans détection compromet la chronologie des preuves. Le `validationTimestamp` (dans verificationMaterial, protégé par le sceau) subsiste comme ancrage temporel, mais la divergence non détectable entre `signedAt` et `validationTimestamp` crée une incohérence exploitable.
Gravité : Bloquant
HD-02 — Déterminisme JCS après aller-retour base de données (jsonb)
Type : Hypothèse dangereuse
Référence : Spec §4 INV-282-02 + §10.1 (PostgreSQL jsonb)
Description : Le sceau est calculé sur la canonicalisation JCS (RFC 8785) du contenu. PostgreSQL `jsonb` normalise les valeurs JSON à l'ingestion (réordonnancement des clés, normalisation des nombres). Si le contenu est persisté en jsonb puis relu pour vérification, la forme sérialisée pourrait différer de celle utilisée au moment du scellement (par exemple, un nombre `1.0` sérialisé comme `1` par PostgreSQL). La spec ne précise pas si le sceau est calculé sur la représentation pre-persistance ou post-round-trip.
Impact : Vérification échouant sur des enveloppes intactes (faux négatif) si la canonicalisation diffère après round-trip.
Gravité : Majeur
HD-03 — Finalisation concurrente sur la même enveloppe UNSEALED
Type : Hypothèse dangereuse
Référence : Spec §5.4 (machine à états) + §5.5 (atomicité)
Description : La spec contractualise UNSEALED → SEALED comme autorisée et l'atomicité ACID du commit. Cependant, elle ne traite pas la concurrence : deux requêtes simultanées de finalisation sur la même enveloppe UNSEALED. L'atomicité ACID garantit l'isolation transactionnelle au niveau base, mais sans verrou applicatif explicite (SELECT FOR UPDATE, optimistic locking), une race condition pourrait produire deux tentatives de scellement dont une échouerait de manière non contractualisée.
Impact : Comportement d'erreur non spécifié en cas de concurrence. Un double scellement ou une erreur technique non métier pourrait se produire.
Gravité : Majeur
RSC-01 — Absence de statut OCSP pour le certificat de scellement propre
Type : Risque sécu/conformité
Référence : Spec §5.1.2 (verificationMaterial) + §5.1.1 (envelopeSeal.certificateChain)
Description : Le `verificationMaterial` contient des réponses OCSP pour les chaînes TSA et eIDAS, mais aucune réponse OCSP pour le certificat de scellement ProbatioVault (issu de `envelopeSeal.certificateChain`). En Mode A (air-gapped), un vérificateur tiers ne peut pas établir que le certificat de scellement n'était pas révoqué au moment de la génération. Le processus de révocation des clés HSM est explicitement hors périmètre (§2 Exclu), mais l'absence de preuve de non-révocation du certificat de scellement dans l'enveloppe elle-même est un trou d'auditabilité.
Impact : Force probante contestable si le certificat de scellement est ultérieurement révoqué : aucune preuve embarquée n'atteste de sa validité au moment T.
Gravité : Majeur
Récapitulatif par gravité
Bloquants (3)
| ID | Type | Référence | Résumé |
| IST-01 | Incohérence Spec↔Tests | Mode B | Aucun test nominal positif pour Mode B (périmètre contractualisé) |
| IST-05 | Incohérence Spec↔Tests | OCSP status=revoked | Comportement non spécifié ni testé si certificat révoqué à la finalisation |
| HD-01 | Hypothèse dangereuse | signedAt | Horodatage de scellement falsifiable (exclu du sceau, aucune liaison implicite) |
Majeurs (9)
| ID | Type | Référence | Résumé |
| AMB-01 | Ambiguïté | INV-282-06 | Liste patterns anti-secrets extensible sans gouvernance |
| AMB-02 | Ambiguïté | certificateChain[] | Rôles des trois chaînes de certificats non différenciés |
| AMB-04 | Ambiguïté | §5.5 | Mécanisme de réconciliation existant non référencé |
| AMB-05 | Ambiguïté | §5.2 | Ordre temporel assembly verificationMaterial vs sceau implicite |
| CTR-01 | Contradiction | ERR-05 note | Exigence monitoring DOIT mais hors périmètre |
| IST-03 | Incohérence Spec↔Tests | certSerialNumber | Normalisation uppercase contractualisée mais non testée |
| IST-04 | Incohérence Spec↔Tests | producedAt | Pas de test négatif si producedAt > validationTimestamp |
| HD-02 | Hypothèse dangereuse | JCS + jsonb | Déterminisme canonicalisation après round-trip PostgreSQL |
| HD-03 | Hypothèse dangereuse | concurrence | Finalisation concurrente non contractualisée |
| RSC-01 | Risque sécu/conformité | OCSP sealing cert | Pas de preuve de non-révocation du certificat de scellement |
Mineurs (6)
| ID | Type | Référence | Résumé |
| AMB-03 | Ambiguïté | base64-DER-OCSP | Label de format non défini |
| AMB-06 | Ambiguïté | policy dégradée | Terme non formellement équivalent à OCSP_UNAVAILABLE |
| AMB-07 | Ambiguïté | ocspResponses absent vs vide | Distinction structurelle implicite |
| CTR-02 | Contradiction | §5.6 | Modèle de stockage des nouveaux champs non précisé |
| IST-02 | Incohérence Spec↔Tests | TC-NOM-08 | Test atomicité non rattaché à un invariant dans la matrice |
| IST-06 | Incohérence Spec↔Tests | INV-282-01 | Substitution composants internes non testée explicitement |