Aller au contenu

PD-294 — Spécification : Revue (Gate 3)

  • Rôle : auditeur technique indépendant
  • Portée : PD-294-specification.md (contractuelle) + PD-294-tests.md (audit limité)
  • Sans amélioration, sans reformulation, sans implémentation

1. Synthèse

Gravité Nombre
Bloquant 2
Majeur 9
Mineur 10

Deux écarts Bloquants concernent la conformité algorithmique au RFC 9162 (§5.8 incomplet par rapport à §2.1.3.2 de la norme) et une contradiction interne non résolue sur proof_version (Q-294-01 acknowledged dans la spec elle-même). Plusieurs hypothèses dangereuses (H-294-02, ordonnancement merklePath, tree_size int32) ne sont pas testées et exposent le contrat à des défaillances silencieuses.


2. Écarts détaillés

E-01 — Algorithme RFC 9162 §2.1.3.2 incomplet

  • Type : Contradiction (spec ↔ norme revendiquée) + Incohérence Spec↔Tests
  • Référence : spec §5.8 "Flux nominal C" étape 3 ; INV-294-06-rfc-verify-applicable ; TC-NOM-06 ; TC-ERR-09
  • Description : §5.8 étape 3 décrit la boucle comme si (fn LSB = 1) OU (fn == sn) alors r = H(0x01||p||r) sinon r = H(0x01||r||p), puis right-shift de fn et sn. Le RFC 9162 §2.1.3.2 impose en plus, dans la branche (fn LSB = 1) OU (fn == sn), une étape intermédiaire : si LSB(fn) n'est pas set, right-shift fn et sn équitablement jusqu'à ce que LSB(fn) soit set OU fn == 0. Cette étape interne est absente de la spec. Elle ne se déclenche que dans les arbres non complets (dernier feuillet sur branche droite incomplète), ce que couvrent les vecteurs RFC 9162 §4.12. Aucun scénario de test (TC-NOM-06 "vecteur figé", TC-ERR-09 "vecteur altéré") ne contraint ce cas edge.
  • Impact : INV-294-06 "DOIT être vérifiable par RFC 9162 §2.1.3.2" n'est pas satisfaisable en l'état ; l'algorithme §5.8 produira une racine erronée pour des arbres avec tree_size non puissance de 2 et certains index de feuilles. Conséquence : faux négatifs (preuves valides rejetées) potentiels en production. Les tests passeraient avec un vecteur complaisant.
  • Gravité : Bloquant

E-02 — Contradiction interne acknowledged non résolue sur proof_version

  • Type : Contradiction
  • Référence : §3 "Définitions" ; §5.1 ligne 1 ; INV-294-05-discriminant ; §10.2 Q-294-01
  • Description : La spec déclare elle-même en §10.2 (Q-294-01) que le besoin source mentionne proof_version -> 1 (inféré) en lecture v1, tandis que le contrat §3/§5.1/INV-294-05 fixe : absent = v1, =2 = v2, toute autre valeur = rejet (ERR-294-01). Cas concret non traité : un enregistrement legacy persisté avec proof_version=1 (si l'amont a infère et persisté la valeur) serait rejeté alors qu'il est sémantiquement v1. INV-294-05 et §5.1 ligne 1 "entrée: absent ou 2 autorisés" excluent explicitement 1 en entrée. §9 Hypothèses tests marque cette ambiguïté "Bloquant".
  • Impact : Rejet massif de preuves historiques possibles si l'amont persiste proof_version=1. Contrat de lecture non univoque. La spec reconnaît elle-même le blocage (§10.2 "Validation PO requise").
  • Gravité : Bloquant

E-03 — Hypothèse hash_algorithm amont non testée (H-294-02)

  • Type : Hypothèse dangereuse + Risque sécu/conformité
  • Référence : §9 H-294-02 ; §5.5 "forçage strict vers sha3-256" ; INV-294-02 ; CA-294-02 ; CA-294-09 ; TC-NOM-02 ; TC-NOM-06
  • Description : §5.5 impose "forçage strict de hashAlgorithm v1 vers sha3-256" sans vérifier que l'amont (PD-54/PD-56) produit effectivement du SHA3-256. Si la chaîne amont utilise SHA-256 (ambiguïté historique backend), la normalisation v2 labelise hash_algorithm='sha3-256' alors que les hash sources sont en SHA-256. Les vérifications RFC adaptée SHA3-256 (§5.8) échoueront silencieusement avec ERR-294-09 côté vérifieur, mais le label émis sera mensonger. Aucun test (TC-NOM-02, TC-NOM-06) ne valide la concordance algorithme-label.
  • Impact : Destruction silencieuse de la valeur probatoire des preuves legacy (label incorrect, vérification impossible). Risque eIDAS / RGPD sur l'intégrité déclarée.
  • Gravité : Majeur

E-04 — tree_size en int32 : divergence RFC 9162

  • Type : Contradiction (spec ↔ norme revendiquée)
  • Référence : §5.1 ligne tree_size ; §5.2 borne max 2147483647 ; §9 H-294-03 ; INV-294-06
  • Description : RFC 9162 §2.1.3 définit tree_size comme uint64. La spec le restreint à int32 signé (<=2147483647) sur la base de H-294-03 "DB PostgreSQL INTEGER". La revendication "aligné RFC 9162" (§1 Objectif) est donc partiellement fausse. Une preuve RFC 9162 native avec tree_size > 2^31-1 ou avec index ≥ 2^31 serait rejetée (ERR-294-03/02), alors même qu'elle est conforme RFC.
  • Impact : Interopérabilité RFC 9162 partielle, borne arbitraire. Si un partenaire externe adresse l'API avec un log ≥ int32, rejet non justifiable par la norme.
  • Gravité : Majeur

E-05 — INV-294-10 envelope-encryption hors périmètre déclaré

  • Type : Contradiction interne + Règle non testable
  • Référence : §2 Hors périmètre (implémentation hors périmètre) ; INV-294-10-envelope-encryption ; CA-294-12 ; TC-NOM-09 ("sous prérequis") ; TC-NEG-05 ; §9 "Règles non testables"
  • Description : INV-294-10 introduit une exigence sécurité (chiffrement au repos AES-256-GCM/enveloppe HSM) avec clause conditionnelle "s'il existe dans le flux". §2 déclare explicitement que l'implémentation des fonctions applicatives (getMerkleProof, verifyMerkleProof) est hors périmètre — il n'y a donc pas de flux crypto nouveau introduit par PD-294. La spec §9 des tests reconnaît la non-testabilité exhaustive. Invariant ni observable ni borné au périmètre.
  • Impact : Invariant fantôme qui bloque Gate 3 / Gate 8 sans moyen de le satisfaire formellement. Soit il est hors scope (et doit disparaître), soit le scope §2 est faux.
  • Gravité : Majeur

E-06 — Transitions interdites §5.4 sans code d'erreur normé

  • Type : Règle non testable + Incohérence Spec↔Tests
  • Référence : §5.4 transitions INTERDITE ; INV-294-08 ; §6 table d'erreurs ; TC-ERR-11 ; §9 "Règles non testables"
  • Description : §5.4 interdit explicitement CANONICAL_V2_READY -> LEGACY_V1_DETECTED et REJECTED_INVALID -> * mais §6 ne définit aucun ERR-294-* pour "transition refusée". TC-ERR-11 observe "journal de refus" sans code HTTP/erreur normé. La spec reconnaît elle-même (§9 tests) que le code exact n'est pas standardisé.
  • Impact : Impossible de contractualiser le comportement API en cas de tentative interdite. Conformité INV-294-08 réduite à un test journalisation, trivialement contournable.
  • Gravité : Majeur

E-07 — Ordonnancement merklePath v1 non contractualisé

  • Type : Hypothèse dangereuse (implicite)
  • Référence : §3 "Inclusion path" ("bas en haut") ; §5.5 mapping merklePath -> inclusion_path ("renommage") ; INV-294-06 ; TC-NOM-02
  • Description : §5.5 décrit le mapping comme un simple "renommage". Aucune clause ne contraint l'ordre des éléments v1 à être bottom-up. Si l'amont (PD-54/PD-56) produit un merklePath top-down ou dans un ordre autre, la normalisation produira une structure v2 syntaxiquement correcte mais cryptographiquement incorrecte. INV-294-06 serait violé silencieusement.
  • Impact : Destruction silencieuse de la probité des preuves legacy au moment de la lecture. Aucun test ne vérifie l'ordre original vs l'ordre attendu.
  • Gravité : Majeur

E-08 — ERR-294-10 non mappé dans la matrice de couverture

  • Type : Incohérence Spec↔Tests
  • Référence : §6 ERR-294-10 (preuve absente) ; §2 tests matrice de couverture ; TC-ERR-10 ; §5 tests "Tests d'invariants"
  • Description : ERR-294-10 est défini au §6 de la spec mais n'est mappé à aucun invariant ni critère d'acceptation dans la matrice §2 des tests. TC-ERR-10 existe mais n'apparaît pas dans la matrice inverse invariant ↔ test. Angle mort formel de traçabilité.
  • Impact : Risque qu'un critère de rejet 404 soit modifié sans détection par la Gate 8 (non-régression non protégée).
  • Gravité : Majeur

E-09 — Cas proof_version=1 en entrée non traité

  • Type : Ambiguïté + Contradiction latente
  • Référence : INV-294-05 ; §5.1 ligne 1 (entrée : "absent ou 2") ; §3 "Définitions" ; §6 ERR-294-01
  • Description : Couplé à E-02, le cas d'une entrée API avec proof_version=1 explicite est ambigu : §5.1 n'autorise en entrée que absent ou =2, donc =1 serait rejeté (ERR-294-01) par INV-294-05. Mais §3 définit proof_version comme "absent = v1 legacy en entrée", ce qui suggère que =1 explicite devrait être aussi reconnu legacy. Comportement non univoque.
  • Impact : Clients qui auraient inféré proof_version=1 pour leur interopérabilité seraient rejetés. Non déterministe selon l'implémenteur.
  • Gravité : Majeur

E-10 — Absence de vérification hsm_signature avant normalisation

  • Type : Risque sécurité / conformité
  • Référence : §2 Hors périmètre (hsm_signature exclu) ; §5.5 mapping ; INV-294-06
  • Description : Le normalizer v1 -> v2 (§5.5, §5.7) applique les renommages et le forçage hash_algorithm='sha3-256' sans vérifier au préalable l'intégrité de la preuve source (hsm_signature ou tsa_token). Un attaquant fournissant une structure v1 forgée verrait sa preuve normalisée et restituée en v2 avec un label d'algorithme confiance. §2 exclut l'implémentation de ces vérifications mais la spec ne prescrit pas le pré-requis "vérifier hsm_signature avant normalisation" comme invariant.
  • Impact : Angle d'attaque pour forgerie de preuve avec label de confiance sha3-256.
  • Gravité : Majeur

E-11 — Validateur ERR-294-08 non localisé

  • Type : Règle non testable
  • Référence : §5.1 ligne treeId ; INV-294-07 ; §6 ERR-294-08 ; TC-ERR-08
  • Description : ERR-294-08 sanctionne une fuite de treeId/hashAlgorithmVersion en sortie par HTTP 500. La spec n'indique s'effectue la détection (middleware NestJS ? décorateur JSON Schema ? post-sérialisation ? test contractuel CI ?). TC-ERR-08 exige "exécution du validateur de contrat" sans point d'insertion normé.
  • Impact : Risque d'implémentation laxe (validateur seulement en tests, pas en runtime prod). Invariant défensif non garanti.
  • Gravité : Majeur

E-12 — Ambiguïté terminologique "single-write" (pattern REX PD-250)

  • Type : Ambiguïté terminologique
  • Référence : §3 "Dual-read / single-write" ; INV-294-03 ; §5.7 "sans écrire en base"
  • Description : Le terme "single-write" est utilisé dans §3 et INV-294-03 pour désigner "émission/export uniquement en v2". Il peut raisonnablement s'interpréter comme "une seule écriture DB" (write-once storage) ou comme "un seul format de sortie". §5.7 confirme l'absence d'écriture DB lors de la lecture v1, ce qui n'est pas la même notion que "émettre en v2 uniquement".
  • Impact : Interprétation divergente possible entre rédacteur spec, implémenteur backend et reviewer (REX PD-250 §10.1 : clarity oscillante non détectée comme contradiction).
  • Gravité : Mineur

E-13 — Étape §5.8 #2 sans code d'erreur

  • Type : Ambiguïté
  • Référence : §5.8 étape 2 ; §6 ERR-294-02
  • Description : §5.8 étape 2 impose leaf_index < tree_size mais ne référence pas ERR-294-02 en cas d'échec. Cohérence avec §6 implicite.
  • Impact : Risque d'implémentation divergente (exception générique vs code contractuel).
  • Gravité : Mineur

E-14 — Diagramme d'état : transitions initiales non listées en texte

  • Type : Incohérence diagramme ↔ texte (axe 5bis)
  • Référence : §5bis diagramme d'état ([*] --> LEGACY_V1_DETECTED, [*] --> CANONICAL_V2_READY) ; §5.4 liste textuelle
  • Description : Le diagramme Mermaid montre deux transitions initiales depuis [*], mais §5.4 ne les liste pas comme transitions autorisées. Asymétrie diagramme ↔ texte.
  • Impact : Ambiguïté sur les états d'entrée autorisés (un enregistrement peut-il naître directement CANONICAL_V2_READY sans passer par LEGACY_V1_DETECTED ?).
  • Gravité : Mineur

E-15 — Diagramme de séquence : conditionnelle fn LSB / fn==sn omise

  • Type : Incohérence diagramme ↔ texte (axe 5bis)
  • Référence : §5bis diagramme de séquence ("r = SHA3-256(0x01 || p || r) OU SHA3-256(0x01 || r || p)") ; §5.8 étape 3
  • Description : Le diagramme présente l'alternative sans condition. La bookkeeping fn, sn et la condition de branchement §5.8 étape 3 sont absentes du diagramme.
  • Impact : Lecteur pressé peut comprendre que l'ordre de concaténation est libre. Cohérent avec E-01 (l'algorithme lui-même est simplifié de manière erronée).
  • Gravité : Mineur

E-16 — Terminologie émission / exposition non unifiée

  • Type : Ambiguïté terminologique (pattern REX PD-250)
  • Référence : INV-294-01 "preuve émise" ; INV-294-07 "merkle_proof v2 exposé" ; §5.6 "retourne merkle_proof" ; CA-294-01 "preuve émise" ; CA-294-05 "en v2 exposé"
  • Description : Les termes "émise", "exposée", "retournée" désignent apparemment la même surface (réponse API) mais ne sont jamais définis comme synonymes. Pour une spec contractuelle, ces surfaces devraient être identifiées (API publique vs export batch vs log d'audit).
  • Impact : Un invariant formulé avec "émise" pourrait être interprété comme couvrant uniquement la sortie API et laisser un export probatoire non contraint.
  • Gravité : Mineur

E-17 — CA-294-11 / INV-294-04 portée temporelle non bornée

  • Type : Règle non testable (partiellement)
  • Référence : INV-294-04-no-retrowrite ; CA-294-11 ; TC-NR-01 ; TC-NOM-02
  • Description : "Aucune preuve v1 existante n'est réécrite par PD-294" est un invariant temporel non borné. Les tests ne couvrent qu'un instantané (diff avant/après une lecture). Une réécriture asynchrone ultérieure ne serait pas détectée.
  • Impact : Invariant faiblement vérifiable en continu. Nécessiterait une sonde d'observabilité permanente non prescrite.
  • Gravité : Mineur

E-18 — Relation inclusion_path.length / tree_size implicite

  • Type : Hypothèse dangereuse (implicite)
  • Référence : §5.1 / §5.2 bornes inclusion_path.length [0,31] ; §5.8 algorithme
  • Description : inclusion_path.length=0 n'est sémantiquement valide que si tree_size=1. Pour tree_size >= 2, length >= 1. La spec autorise length=0 sans conditionner à tree_size=1, et TC-NOM-05 teste PATH-0 sans préciser tree_size. Relation implicite enforcée uniquement par §5.8 (résultat sn=0, r=event_hash).
  • Impact : Risque de preuve length=0, tree_size=5 acceptée à la validation de format (§5.1) et rejetée seulement à la vérification crypto (ERR-294-09). Deux niveaux de rejet pour une même cause logique.
  • Gravité : Mineur

E-19 — Potentiel anti-énumération ERR-294-10 vs ERR-294-06

  • Type : Risque sécurité / conformité
  • Référence : §6 ERR-294-10 (404) et ERR-294-06 (400)
  • Description : Un client abusif peut distinguer "preuve absente" (404) de "identifiant mal formé" (400). Si l'identifiant respecte le format mais n'existe pas, on obtient 404 ; sinon 400. Ceci permet une énumération par sondage sur des espaces d'ID corrélés. Pattern documenté dans les learnings universels (REX PD-85 : uniformiser "not found" / "not accessible").
  • Impact : Fuite d'information de présence de preuves (enumeration de journaux existants).
  • Gravité : Mineur

E-20 — Q-294-03 {{LEARNINGS}} non renseigné

  • Type : Hypothèse dangereuse (processus)
  • Référence : §10.2 Q-294-03 ; placeholder {{LEARNINGS}} de la revue
  • Description : La spec acknowledged que le bloc learnings historiques n'a pas été injecté. Sur un domaine sécuritaire (preuve probatoire), cela signifie que des anti-patterns connus (ex. catch-absorb, validation format ≠ fonctionnelle, crypto roundtrip test) n'ont pas été croisés systématiquement.
  • Impact : Risque d'omission d'invariant déjà capturé par les REX précédents. Déjà partiellement visible (E-03, E-07, E-11 relèvent de patterns PD-282/PD-283).
  • Gravité : Mineur

E-21 — INV-294-09 invariant documentaire non contractuel

  • Type : Règle non testable (runtime)
  • Référence : INV-294-09-format-single-source ; TC-NOM-08 (contrôle documentaire)
  • Description : INV-294-09 contraint la rédaction de la spec elle-même ("formats définis uniquement en §5.1"). Ce n'est pas une propriété du système en exécution. Classé comme invariant technique NON négociable alors qu'il relève d'une règle documentaire.
  • Impact : Pollue la matrice d'invariants runtime. Gate 8 pourrait exiger une preuve d'exécution inapplicable.
  • Gravité : Mineur

3. Vérifications positives (non exhaustives)

Éléments jugés conformes et testables en l'état (mentionnés à titre informatif, sans complément) :

  • §5.1 — définition univoque des formats hash (^[a-f0-9]{64}$, case-sensitive) avec codes d'erreur dédiés.
  • §5.5 — table de mapping v1 -> v2 exhaustive sur les 7 champs.
  • §6 — association code HTTP / code erreur dédiée (hors transitions interdites, cf. E-06).
  • §5.4 — explicitation des transitions interdites en machine d'états (hors codes d'erreur, cf. E-06).
  • Matrice §2 des tests — couvre l'essentiel des invariants (hors ERR-294-10, cf. E-08).

4. Recommandation QA

Verdict : Non conforme en l'état — 2 écarts Bloquants rendent la spec non acceptable au titre contractuel avant résolution :

  • E-01 : algorithme §5.8 incomplet vs RFC 9162 §2.1.3.2 (revendication d'alignement fausse).
  • E-02 : contradiction interne acknowledged non résolue sur proof_version v1.

Les 9 écarts Majeurs doivent être tranchés avant Gate 5. Les 10 écarts Mineurs peuvent être adressés en parallèle ou documentés comme dette contractuelle.


Références

  • PD-294-specification.md (§1–§10)
  • PD-294-tests.md (§1–§10)
  • RFC 9162 §2.1.3, §2.1.3.2, §4.12
  • Learnings universels : REX PD-250 §10.1 (clarity oscillante), PD-85 (enumeration), PD-282/PD-283 (validation format ≠ fonctionnelle)