Aller au contenu

PD-282 — Rapport de confrontation (Gate 5 — Plan d'implémentation)

Ce rapport est produit par l'orchestrateur Claude avant la gate PMO 5. Il confronte les documents produits pour identifier convergences, divergences et zones d'ombre.

1. Sources confrontées

Document Étape d'origine Auteur
PD-282-specification.md Étape 1 (Spécification) ChatGPT
PD-282-tests.md Étape 2 (Tests contractuels) ChatGPT
PD-282-plan.md Étape 4 (Plan) Claude Opus 4.6
Code Contracts (YAML) Étape 4 (Plan) Claude Opus 4.6
Review Phase 1 v2 Étape 5 (Review externe) OpenCode

2. Convergences

CONV-01 — Pipeline cryptographique. Les cinq documents s'accordent sur le pipeline de scellement : canonicalisation JCS RFC 8785 → hash SHA3-384 → signature ECDSA P-384 via HSM. L'identifiant algorithme ECDSA-P384-SHA3-384 est cohérent dans la spec (§5.1.1), le plan (DA-03), les code contracts (envelope-seal-pipeline) et les tests (TC-NOM-01).

CONV-02 — Exclusion de envelopeSeal du payload signé. INV-282-02 est respecté uniformément : spec §5.1.1, plan §4.2 (pipeline étape 3), code contracts (INV-282-02: Le champ envelopeSeal DOIT être exclu), tests TC-NOM-01 et TC-ERR-02.

CONV-03 — Structure envelopeSeal et verificationMaterial. Les formats, regex, bornes de taille et encodages définis en spec §5.1 sont repris identiquement dans les code contracts (format-validation) et testés via TC-ERR-06, TC-NEG-01 à TC-NEG-10.

CONV-04 — État SEALED terminal et immutabilité. Les quatre sources convergent : INV-282-08/09/10 en spec, trigger PD-272 dans le plan (DA-02), code contracts (proof-envelope-entity), tests TC-NOM-05/06, TC-INV-09/10.

CONV-05 — Détection de secrets. Patterns interdits (privateKey, secretKey, sessionToken, hmacSecret, dek), recherche case-insensitive, scan pré-scellement : cohérent entre spec INV-282-06, plan §4.2, code contracts (secret-detection), tests TC-INV-06 et TC-NEG-08.

CONV-06 — OCSP dégradé. Le scénario timeout OCSP → validationPolicy=OCSP_UNAVAILABLE + ocspResponses=[] est contractualisé identiquement : spec ERR-05, plan §4.3 (étape 3), code contracts (verification-material-assembly, ocsp-client), tests TC-ERR-05 et TC-NOM-04.

CONV-07 — Vérification historique après rotation de clé. INV-282-11 (kid + chaine embarquée) : cohérent entre spec, plan §4.4, code contracts (offline-verification), tests TC-NOM-07.

CONV-08 — Normalisation certSerialNumber. Normalisation uppercase à l'ingestion : spec §5.1.2, code contracts (format-validation, verification-material-assembly). Pas de divergence.

CONV-09 — ocspResponses absent vs vide. Spec : absent = rejet, vide = autorisé avec policy dégradée. Code contracts : ocspResponses=undefined interdit, [] obligatoire si vide. Tests TC-NEG-06. Convergence confirmée.

CONV-10 — Atomicité et comportement crash. Spec §5.5, plan §4.2 (INSERT atomique), tests TC-NOM-08 : convergence sur rollback pré-commit / DB autoritatif post-commit.

CONV-11 — Dépendances inter-PD. PD-272 (immutabilité), PD-81 (TSA), PD-280 (états), PD-37 (JCS) : cohérent entre spec §10.1, plan §6, code contracts.

CONV-12 — Encodages base64url vs base64. signature en base64url sans padding, certificateChain et ocspResponses[].response en base64 standard : cohérent partout.

3. Divergences

Les conflits ne sont JAMAIS lissés. Chaque divergence est rendue visible.


DIV-01 — Migration DDL vs mention « Aucune stratégie de migration DDL applicable »

  • Spec §5.6 : « Aucune modification de colonne existante contractualisée dans PD-282 » ET « Mention obligatoire : Aucune stratégie de migration DDL applicable ».
  • Plan DA-01 : Introduit une migration TypeORM PD282-AddEnvelopeSealColumn.ts ajoutant une colonne envelope_seal JSONB nullable à legal_composite_proof.
  • Code contracts (proof-envelope-entity) : Liste explicitement le fichier de migration src/database/migrations/*-PD282-AddEnvelopeSealColumn.ts.
  • Review Phase 1 v2 : Écart identifié comme BLOQUANT — « Rupture de conformité contractuelle directe sur le périmètre de persistance ».
  • Analyse factuelle : La spec distingue « Aucune modification de colonne existante » (vrai : aucune colonne existante n'est modifiée) et « Aucune stratégie de migration DDL applicable » (faux : un ADD COLUMN est une opération DDL). Le plan ajoute une colonne nouvelle, ce qui contredit la seconde mention. La formulation spec est ambiguë : elle semble vouloir dire « pas de migration de données complexe » mais la mention obligatoire est catégorique.
  • Impact : Contradiction formelle entre spec et plan. Si interprétation littérale de la spec, le plan viole le contrat. Si interprétation intentionnelle, la spec doit être amendée.

DIV-02 — Machine à états UNSEALED/SEALED : observabilité des transitions

  • Spec §5.4 : Définit UNSEALED (non terminal) avec transitions UNSEALED → SEALED (autorisée), UNSEALED → UNSEALED (autorisée), et SEALED → * (interdit).
  • Plan DA-02 : « Pas de colonne seal_status. L'INSERT est toujours atomique en état SEALED. » UNSEALED est transitoire, en mémoire uniquement.
  • Code contracts (proof-envelope-entity) : Interdit « Créer un état UNSEALED persisté en base ».
  • Tests TC-NOM-06 : « Transition UNSEALED → SEALED demandée (préconditions valides) → Transition acceptée ».
  • Tests TC-INV-09/10 : Testent le refus de SEALED → * et SEALED → UNSEALED.
  • Review Phase 1 v2 : Écart identifié comme BLOQUANT — « Les tests d'états contractuels ne sont pas démontrables de manière auditée et reproductible ».
  • Analyse factuelle : La spec contractualise une machine à états à deux états observables. Le plan rend l'un d'eux non observable (mémoire uniquement). TC-NOM-06 demande une transition UNSEALED → SEALED mais si l'INSERT est toujours SEALED, la transition n'est jamais observable. TC-INV-09/10 restent testables (tentatives de mutation post-INSERT). La divergence porte sur UNSEALED → SEALED et UNSEALED → UNSEALED.
  • Impact : Les tests de transition positive (UNSEALED → SEALED) ne sont pas démontrables avec le design proposé. Le mécanisme de test doit être clarifié (test en mémoire ? mock ? assertion pré-INSERT ?).

DIV-03 — Comportement OCSP status=revoked : rejet non contractualisé dans la spec

  • Spec §5.1.2 : ocspResponses[].status est un enum good|revoked|unknown. Aucun cas d'erreur ne traite revoked.
  • Spec §6 ERR-05 : Couvre uniquement le timeout OCSP, pas le cas revoked.
  • Plan §3 Reserve ECT-02 : « Le VerificationMaterialAssembler rejette la finalisation si status=revoked (exception OCSP_CERT_REVOKED) ».
  • Code contracts (verification-material-assembly) : « Si un OCSP response a status=revoked : rejet finalisation (certificat révoqué) ».
  • Review Phase 1 v2 : Écart identifié comme MAJEUR — « Introduction d'un comportement de rejet non spécifié à date ».
  • Analyse factuelle : Le plan et les code contracts ajoutent un comportement (rejet sur revoked) qui n'est ni dans la spec ni dans les cas d'erreur. La spec inclut revoked comme valeur d'enum valide mais ne prescrit pas de comportement associé. Le plan invoque un amendement futur de la spec.
  • Impact : Comportement d'implémentation non couvert par le contrat spec actuel. Soit la spec doit être amendée, soit le plan doit retirer ce comportement.

DIV-04 — Mode B : « optionnelle » vs « constitutive »

  • Plan §3 Reserve ECT-01 : « Le service OfflineVerificationService implémente Mode A ET Mode B. Le test nominal Mode B sera ajouté. »
  • Plan §4.4 (pipeline étape 8) : « Mode B uniquement : vérification OCSP en ligne optionnelle ».
  • Code contracts (offline-verification) : « Mode B : utilise les données embarquées + vérification en ligne optionnelle ».
  • Spec §2 (Inclus) : « Vérification tierce en mode offline strict (air-gapped) et mode online public (sans service ProbatioVault) ».
  • Review Phase 1 v2 : Écart identifié comme MAJEUR — « Deux formulations incompatibles sur la nature de la vérification Mode B ».
  • Analyse factuelle : Le mot « optionnelle » dans le plan §4.4 et les code contracts porte sur la vérification OCSP en ligne au sein de Mode B, pas sur Mode B lui-même. Cependant la formulation est ambiguë : un lecteur peut comprendre que la totalité de Mode B est optionnelle. La spec définit Mode B comme vérification online publique — elle est structurelle, pas optionnelle.
  • Impact : Ambiguïté rédactionnelle. Risque d'implémentation où Mode B serait traitée comme facultative.

DIV-05 — INV-282-07 : couverture effective du chiffrement au repos des artefacts temporaires

  • Spec INV-282-07 : « Tout artefact crypto temporaire (clé/fragment/DEK/ReKey) est chiffré au repos (AES-256-GCM ou envelope HSM) ».
  • Plan §5 : INV-282-07 couvert par seal-pipeline — « Hash non réversible, clé dans HSM ».
  • Code contracts (envelope-seal-pipeline) : « Aucun artefact crypto temporaire en clair (hash et signature sont non-réversibles, clé privée reste dans HSM) ».
  • Tests TC-INV-07 : « Chaque artefact est chiffré au repos (AES-256-GCM ou envelope HSM). Aucune persistance en clair détectée. »
  • Review Phase 1 v2 : Écart identifié comme MAJEUR — « Le plan remplace la vérification d'un invariant de chiffrement au repos par un audit statique ».
  • Analyse factuelle : La spec exige un chiffrement actif au repos. Le plan et les code contracts argumentent qu'il n'y a pas d'artefact temporaire à chiffrer (hash non réversible, clé dans HSM). Le test TC-INV-07 suppose que des artefacts existent et vérifie leur chiffrement. Divergence entre le plan (« rien à chiffrer ») et le test (« vérifier que c'est chiffré »).
  • Impact : Si des artefacts temporaires transitent effectivement en mémoire ou en fichiers (buffers, DER fragments), l'invariant n'est pas couvert par le design. Le test TC-INV-07 n'a pas de mécanisme décrit pour s'exécuter dans le plan.

DIV-06 — signedAt non couvert par le sceau : divergence d'interprétation entre spec et plan

  • Spec §5.1.1 : signedAt est un champ de envelopeSeal.
  • Spec INV-282-02 : envelopeSeal est exclu du calcul du sceau.
  • Plan §3 Reserve DIV-01 : « Accepté par design. Mitigation : validationTimestamp dans le hash + token TSA PD-81. »
  • Tests : Aucun test dédié vérifiant la non-altérabilité ou la mitigation de signedAt.
  • Analyse factuelle : Par construction, signedAt peut être modifié sans invalider le sceau. Le plan documente cette décision comme acceptable, mais la spec ne mentionne pas cette limitation ni cette mitigation. Les tests ne couvrent pas ce scénario.
  • Impact : Risque de substitution temporelle sur signedAt sans détection par le sceau. La mitigation (TSA PD-81) n'est pas testée dans le cadre PD-282.

DIV-07 — Code contracts : contraintes ajoutées hors périmètre spec

  • Code contracts (envelope-seal-pipeline) : « Utiliser Math.random() pour quelque identifiant que ce soit (crypto.randomUUID() obligatoire) ».
  • Spec PD-282 : Aucune mention de crypto.randomUUID() ni de Math.random().
  • Review Phase 1 v2 : Écart identifié comme MINEUR — « Pattern interdit ajouté sans ancrage explicite dans la spec PD-282 ».
  • Analyse factuelle : Cette contrainte provient d'une règle projet transversale (REX PD-63, CLAUDE.md). Elle est pertinente opérationnellement mais n'est pas contractualisée dans PD-282. Les code contracts incluent d'autres contraintes implémentation bas niveau (ex : « Utiliser exclusivement JsonCanonicalizeService PD-37 ») qui sont des choix d'implémentation, pas des invariants spec.
  • Impact : Mineur. Glissement de périmètre contractuel : les code contracts mélangent invariants spec et bonnes pratiques projet. Pas de risque fonctionnel mais difficulté d'audit de conformité stricte spec ↔ contracts.

4. Zones d'ombre

ZO-01 — Environnement de référence performance (Q-02). La spec contractualise des bornes P95 (latence scellement 500ms, overhead 17KB) mais Q-02 reste ouvert : aucun hôte cible (CPU/RAM/réseau) n'est défini. Le plan ne résout pas Q-02. Les tests TC-NR-05 et les bornes §5.2 ne sont pas exécutables sans cette définition. Aucun document ne propose de chemin de résolution.

ZO-02 — Alignement PD-280 états métier (Q-04). La spec mentionne la compatibilité avec PD-280 (modèle d'états PENDING/INDETERMINATE). Le plan liste PD-280 comme « Disponible ». Mais aucun document ne détaille l'articulation entre UNSEALED/SEALED (PD-282) et PENDING/INDETERMINATE (PD-280). Le test TC-NR-02 vérifie la compatibilité mais sans critère objectif.

ZO-03 — Inclusion root CA dans les chaînes (Q-05). La spec pose la question. Le plan DA-06 mentionne « intermédiaires et root configurés via ConfigService » mais ne tranche pas si le root CA est obligatoirement inclus dans certificateChain / tsaCertificateChain / eidasCertificateChain ou s'il peut être omis (confiance au trust-store du vérificateur).

ZO-04 — Source exacte de eidasCertificateChain. Les code contracts (verification-material-assembly) mentionnent « source : LegalMandate.certificateChainRef ». Cette référence n'apparaît ni dans la spec ni dans le plan. Aucun document ne contractualise cette source.

ZO-05 — Concurrence de finalisation. Le plan HD-03 mentionne que generateProof() est appelé dans une transaction avec lock existant. Aucun test dédié ne vérifie le comportement en cas de double appel concurrent. TC-NOM-08 couvre le crash mais pas la concurrence.

ZO-06 — Statut des dépendances inter-PD. Le plan §6 utilise les statuts « Disponible / À ajouter ». La review note l'absence de statuts contractuels (DONE / TODO / STUB acceptable). Aucun document ne confirme formellement que PD-272, PD-81, PD-280, PD-37 sont effectivement déployés et testables sur la branche cible.

ZO-07 — Mécanisme de réconciliation post-crash (AMB-04). Le plan référence un « reconciliation handler existant dans le module legal-pre ». Ni la spec ni les tests ne vérifient ce mécanisme. TC-NOM-08 mentionne « réconciliation asynchrone possible » mais sans assertion.

ZO-08 — OCSP du certificat HSM ProbatioVault (RSC-01). Le plan note que le certificat HSM ProbatioVault n'a pas de responder OCSP public et renvoie à une story future. Cela signifie que le propre certificat de signature de l'enveloppe n'a pas de statut de révocation vérifiable par un tiers. Aucun document ne contractualise cette limitation ni ses conséquences sur la force probante.

5. Recommandation

  • Procéder — convergence confirmée, aucun conflit bloquant
  • Rework nécessaire — divergences à résoudre avant de continuer
  • Escalade — décision humaine requise sur un point structurant

Justification : Deux divergences sont identifiées comme bloquantes par la review externe (DIV-01, DIV-02). DIV-03 et DIV-05 portent sur des comportements non contractualisés ou des invariants non négociables insuffisamment couverts. La résolution de DIV-01 (amendement spec §5.6 ou retrait de la migration) et DIV-02 (mécanisme de test observable pour la machine à états) est requise avant passage en gate.