Aller au contenu

PD-72 — Spécification canonique contractuelle du worker PRE de transfert coffre entreprise vers salarié

1. Objectif

La User Story PD-72 doit définir un workflow asynchrone contractuel, testable et traçable permettant le transfert probatoire d’un document RH scellé depuis le coffre entreprise vers le coffre personnel du salarié, via PRE (Proxy Re-Encryption), sans exposition du document en clair, avec preuve de remise, journalisation append-only et ancrage de preuve composite.

2. Périmètre / Hors périmètre

Inclus

  • Orchestration d’un job BullMQ pre-transfer déclenché à l’état document SEALED.
  • Gestion de l’entité métier DocumentTransfer avec machine d’états explicite.
  • Appel orchestré aux modules existants : PRE engine (PD-41), HSM, TSA RFC3161, Merkle batch, anchoring, audit WORM, preuve composite.
  • Émission de l’événement probatoire TRANSFER_EMPLOYEE.
  • Génération de l’acte de remise signé et notification salarié (in-app + email neutre).
  • Application de la policy RH copy / transfer avec action de révocation entreprise si transfer.
  • Gestion des erreurs, retries, idempotence et anti-replay.

Exclu

  • Implémentation interne des primitives cryptographiques (PRE/HSM/TSA/Merkle/blockchain).
  • UX de consentement salarié (suppose employee_consent = TRUE au moment nominal).
  • Définition du format interne du proof_bundle.json (ProofEnvelope canonical existant).
  • Validation juridique externe de l’opposabilité probatoire (hors périmètre logiciel testable).

3. Définitions

  • PRE : Proxy Re-Encryption, re-chiffrement sans déchiffrement du contenu.
  • Zero-Knowledge (contexte story) : le worker ne déchiffre jamais le document ni K_doc.
  • DocumentTransfer : agrégat de suivi d’un transfert entreprise → salarié.
  • Policy copy : transfert sans révocation d’accès entreprise.
  • Policy transfer : transfert avec révocation d’accès entreprise après succès.
  • Événement TRANSFER_EMPLOYEE : trace probatoire signée, horodatée, ancrée.
  • État terminal : état sans transition sortante autorisée.
  • Replay : tentative de retraitement logique d’un même transfert déjà finalisé.
  • Horodatage TSA : jeton RFC 3161 associé à l’événement probatoire.

4. Invariants (non négociables)

ID Règle Justification
INV-01 K_docEntreprise n’est jamais modifiée par PD-72. Intégrité cryptographique source.
INV-02 Le worker ne déchiffre jamais le document ni wrap_Entreprise. Conformité Zero-Knowledge.
INV-03 rk (ou artefact PRE temporaire équivalent) est détruit en mémoire immédiatement après re-chiffrement. Réduction de surface d’exposition.
INV-04 Aucun secret cryptographique temporaire n’est persisté en clair (enveloppe AES-256-GCM/HSM obligatoire). Invariant envelope encryption (crypto).
INV-05 Toute trace TRANSFER_EMPLOYEE est append-only, signée HSM, puis intégrée à un Merkle root ancré publiquement. Non-répudiation et auditabilité.
INV-06 Si transfer_policy = transfer, l’accès entreprise au contenu est révoqué après transfert validé. Exigence RGPD métier.
INV-07 Le workflow est idempotent : un même DocumentTransfer.id ne produit jamais double signature, double acte, double ancrage logique. Robustesse en retry/replay.
INV-08 Machine d’états fermée : toute transition non listée est explicitement interdite et journalisée. Prévisibilité et testabilité.
INV-09 Auteur et validateur de preuve/gate doivent être distincts. Validation croisée (gouvernance).

5. Flux nominaux

5.1 Modèle de données contractuel (DocumentTransfer)

Champs obligatoires :

  • id, document_id, status, source_vault_id, target_vault_id, transfer_policy, retry_count, created_at, updated_at
  • rk_id, proof_id, error_code, completed_at : nullable selon état

Formats et contraintes (définis une seule fois ici, référencés ailleurs) :

Donnée Format / encodage Taille Jeu de caractères Case Regex Invalide
DocumentTransfer.id UUID v4 texte 36 chars [0-9a-f-] insensitive à l’entrée, normalisé lowercase ^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ rejet
document_id UUID v4 36 chars idem idem idem rejet
source_vault_id UUID v4 36 chars idem idem idem rejet
target_vault_id UUID v4 36 chars idem idem idem rejet
status enum 1 valeur ASCII uppercase + _ sensitive liste fermée rejet
transfer_policy enum 1 valeur ASCII lowercase sensitive ^(copy|transfer)$ rejet
rk_id identifiant opaque PRE 1..128 chars ASCII printable sans espace sensitive ^[!-~]{1,128}$ rejet + alerte sécurité
proof_id UUID v4 36 chars [0-9a-f-] insensitive à l’entrée regex UUID v4 rejet
error_code code métier 1..32 chars [A-Z0-9_-] sensitive ^[A-Z0-9_-]{1,32}$ clamp impossible, rejet
retry_count entier non signé 0..3 numérique n/a ^[0-3]$ rejet
hash_doc (événement) SHA3-256 hex 64 chars [0-9a-f] sensitive ^[0-9a-f]{64}$ rejet
timestamp (événement) RFC3339 UTC 20..35 chars UTF-8 sensitive validation RFC3339 rejet
hsm_signature base64 44..4096 chars base64 std sensitive ^[A-Za-z0-9+/=]+$ rejet
tx_id blockchain identifiant opaque ancrage 1..128 chars ASCII printable sans espace sensitive ^[!-~]{1,128}$ rejet

5.2 Machine d’états et transitions

États : PENDING, READY_FOR_TRANSFER, TRANSFER_IN_PROGRESS, TRANSFERRED, PROOF_PENDING_ANCHOR, ANCHOR_CONFIRMED, FAILED, BLOCKED_WAITING_CONSENT.

Transitions sortantes explicites :

  • PENDINGREADY_FOR_TRANSFER : garde = compte salarié actif ET clé publique certifiée ET employee_consent = TRUE. Si compte absent → BLOCKED_WAITING_CONSENT (PRE-01). Si clé invalide → FAILED (PRE-02).
  • READY_FOR_TRANSFERTRANSFER_IN_PROGRESS : garde = vérification d'autorisation de l'émetteur. Mécanisme : le job BullMQ est déclenché par un événement interne DOCUMENT_SEALED émis par le module document-service (seul producteur autorisé). Le worker vérifie que source_vault_id du job correspond au vault_id du document en base. Si mismatch → rejet + alerte sécurité. Aucune API externe ne peut déclencher directement le job.
  • TRANSFER_IN_PROGRESSTRANSFERRED (re-chiffrement + zeroization OK), ou FAILED.
  • TRANSFERREDPROOF_PENDING_ANCHOR.
  • PROOF_PENDING_ANCHORANCHOR_CONFIRMED (confirmation batch reçue avant ANCHOR_TIMEOUT_HOURS, défaut 24h), ou FAILED (timeout dépassé ou erreur technique).
  • FAILEDREADY_FOR_TRANSFER : garde = retry_count < max_retries (défaut 3) ET erreur récupérable (PRE-03, PRE-04, timeout ancrage). Backoff exponentiel : delay = base_delay * 2^(retry_count) avec base_delay = 30s. Erreurs non récupérables (PRE-02, PRE-07) : pas de retry, FAILED terminal.
  • BLOCKED_WAITING_CONSENTREADY_FOR_TRANSFER si consentement devenu valide; sinon maintien.
  • ANCHOR_CONFIRMED* : INTERDITE (état terminal, résolution manuelle uniquement).
  • FAILED terminal (retry_count >= max_retries ou erreur non récupérable) → * : INTERDITE, escalade manuelle obligatoire.

Transitions retour (obligatoires) :

  • TRANSFERRED → TRANSFER_IN_PROGRESS : INTERDITE (immutabilité post re-chiffrement).
  • PROOF_PENDING_ANCHOR → TRANSFERRED : INTERDITE (événement déjà émis).
  • ANCHOR_CONFIRMED → PROOF_PENDING_ANCHOR : INTERDITE (finalité probatoire).
  • BLOCKED_WAITING_CONSENT → PENDING : INTERDITE (repréparation non nécessaire).
  • Downgrade policy (transfer vers copy) après TRANSFER_IN_PROGRESS : INTERDITE (verrou contractuel du transfert en cours).

Invariant états dédié : INV-08-transitions (couverture complète et interdictions explicites).

5.3 Flux nominal

  1. Réception événement DOCUMENT_SEALED éligible RH. Vérification d'autorisation : l'événement doit provenir d'un émetteur authentifié et le source_vault_id doit correspondre au propriétaire du document.
  2. Création DocumentTransfer en PENDING.
  3. Vérification préconditions (compte salarié actif, clé publique certifiée, consentement vrai). Si OK → transition READY_FOR_TRANSFER. Si compte absent → BLOCKED_WAITING_CONSENT. Si clé invalide → FAILED.
  4. Passage TRANSFER_IN_PROGRESS et appel PRE re-encryption aveugle.
  5. Zeroization immédiate des artefacts PRE temporaires.
  6. Passage TRANSFERRED, création événement TRANSFER_EMPLOYEE, signature HSM.
  7. Inclusion Merkle + horodatage TSA + planification ancrage batch.
  8. Passage PROOF_PENDING_ANCHOR, génération acte de remise signé.
  9. Notification salarié.
  10. Si transfer_policy=transfer, révocation accès entreprise.
  11. À confirmation batch: ANCHOR_CONFIRMED + completed_at.

5.4 Atomicité multi-composant (DB + async)

Scope Synchrone/Async Garantie
Écritures DocumentTransfer Synchrone transactionnel ACID
Journal append-only Async post-commit idempotent, retry-safe
Merkle/TSA/ancrage Async post-commit rattrapage par réconciliation
Crash pré-commit n/a rollback, aucun artefact persistant
Crash post-commit n/a état DB source de vérité, reprise worker

5bis. Diagrammes Mermaid

5bis.1 Diagramme d’états — DocumentTransfer (INV-08)

stateDiagram-v2
    [*] --> PENDING : DOCUMENT_SEALED reçu

    PENDING --> READY_FOR_TRANSFER : compte actif ∧ clé certifiée ∧ consent=TRUE
    PENDING --> BLOCKED_WAITING_CONSENT : compte absent (PRE-01)
    PENDING --> FAILED : clé invalide (PRE-02)

    BLOCKED_WAITING_CONSENT --> READY_FOR_TRANSFER : consentement devenu valide

    READY_FOR_TRANSFER --> TRANSFER_IN_PROGRESS : autorisation émetteur vérifiée

    TRANSFER_IN_PROGRESS --> TRANSFERRED : re-chiffrement PRE + zeroization OK (INV-03)
    TRANSFER_IN_PROGRESS --> FAILED : erreur technique

    TRANSFERRED --> PROOF_PENDING_ANCHOR : événement signé HSM + Merkle (INV-05)

    PROOF_PENDING_ANCHOR --> ANCHOR_CONFIRMED : confirmation batch < ANCHOR_TIMEOUT_HOURS
    PROOF_PENDING_ANCHOR --> FAILED : timeout ou erreur technique

    FAILED --> READY_FOR_TRANSFER : retry_count < max_retries ∧ erreur récupérable (INV-07)

    ANCHOR_CONFIRMED --> [*]

    note right of ANCHOR_CONFIRMED : État terminal (INV-08)\nAucune transition sortante
    note right of FAILED : Terminal si retry_count ≥ 3\nou erreur non récupérable\n(PRE-02, PRE-07)
    note left of TRANSFER_IN_PROGRESS : K_doc jamais déchiffrée (INV-02)\nrk détruit après usage (INV-03)

5bis.2 Diagramme de séquence — Flux nominal (§5.3)

sequenceDiagram
    participant DS as DocumentService
    participant W as Worker PRE-Transfer
    participant DB as PostgreSQL
    participant PRE as PRE Engine (PD-41)
    participant HSM as HSM (PD-37)
    participant TSA as TSA RFC3161 (PD-39)
    participant MK as Merkle Batch (PD-55)
    participant AUD as Audit WORM
    participant NOT as Notification

    DS->>W: événement DOCUMENT_SEALED
    W->>DB: CREATE DocumentTransfer [PENDING]

    Note over W: Vérification préconditions
    W->>DB: UPDATE status → READY_FOR_TRANSFER

    W->>DB: UPDATE status → TRANSFER_IN_PROGRESS
    W->>PRE: re-chiffrement aveugle (wrap_Entreprise → wrap_Salarié)
    PRE-->>W: wrap_Salarié + rk_id
    Note over W: Zeroization rk immédiate (INV-03)

    W->>DB: UPDATE status → TRANSFERRED
    W->>HSM: signer événement TRANSFER_EMPLOYEE
    HSM-->>W: hsm_signature (INV-05)
    W->>AUD: journal append-only (INV-05)

    W->>TSA: horodatage RFC3161
    TSA-->>W: jeton TSA
    W->>MK: inclusion Merkle + planification ancrage batch

    W->>DB: UPDATE status → PROOF_PENDING_ANCHOR
    W->>W: génération acte de remise signé
    W->>NOT: notification salarié (contenu neutre)

    alt transfer_policy = transfer
        W->>DB: révocation accès entreprise (INV-06)
    end

    MK-->>W: confirmation batch (async)
    W->>DB: UPDATE status → ANCHOR_CONFIRMED + completed_at

6. Cas d’erreur

Code Cas Réponse contractuelle État cible
PRE-01 Salarié sans compte actif rejet métier + notification RH, DocumentTransfer créé en PENDING puis transitionné vers BLOCKED_WAITING_CONSENT BLOCKED_WAITING_CONSENT
PRE-02 Clé publique invalide/non certifiée rejet + alerte sécurité FAILED
PRE-03 Signature HSM indisponible retry automatique jusqu’à 3 FAILED puis READY_FOR_TRANSFER si retry restant
PRE-04 TSA indisponible retry automatique borné FAILED puis reprise
PRE-05 Batch blockchain différé non bloquant PROOF_PENDING_ANCHOR
PRE-06 Replay détecté rejet idempotent, aucun nouvel artefact état inchangé
PRE-07 Donnée au format invalide (§5.1) rejet strict, journal sécurité FAILED ou refus création
PRE-08 Retry max atteint (retry_count=3) escalade manuelle obligatoire FAILED terminal

7. Critères d’acceptation (testables)

ID Critère Observable
CA-01 Le salarié ouvre le document depuis son coffre après transfert réussi. accès autorisé côté salarié, lecture valide.
CA-02 En policy transfer, l’entreprise ne peut plus accéder au contenu après finalisation. tentative accès entreprise refusée.
CA-03 Un événement TRANSFER_EMPLOYEE signé est créé une seule fois. unicité par DocumentTransfer.id, signature vérifiable.
CA-04 La chaîne preuve Merkle→TSA→ancrage est complète. proof_id résolu + tx_id présent en confirmation.
CA-05 Relancer le même job ne crée aucune duplication métier. compteurs artefacts inchangés, état stable.
CA-06 rk/artefacts PRE temporaires ne sont jamais persistés en clair. audit stockage: aucune valeur secrète en clair.
CA-07 Performance transfert hors batch respecte SLA. latence traitement P95 ≤ 3000 ms.
CA-08 Toutes transitions interdites sont rejetées explicitement. erreur métier + log transition interdite.
CA-09 Après 3 retries échoués, aucune relance automatique n’est effectuée. retry_count=3, état FAILED terminal.
CA-10 Notification salarié est neutre (sans contenu sensible). contenu notification sans payload documentaire.

8. Scénarios de test (Given / When / Then)

  1. Flux nominal copy
  2. Given document SEALED, consentement vrai, policy copy
  3. When job pre-transfer s’exécute sans erreur
  4. Then état final ANCHOR_CONFIRMED, accès salarié OK, accès entreprise conservé

  5. Flux nominal transfer

  6. Given document SEALED, policy transfer
  7. When transfert et preuve sont finalisés
  8. Then révocation entreprise effective et vérifiable

  9. Clé publique invalide

  10. Given clé publique salarié invalide
  11. When vérification préconditions s’exécute
  12. Then transfert rejeté, FAILED, alerte sécurité émise

  13. TSA indisponible récupérable

  14. Given TSA indisponible temporairement
  15. When job échoue puis retry
  16. Then reprise réussie avant retry_count=3

  17. Batch ancrage différé

  18. Given preuve signée et Merkle inclus, ancrage non encore confirmé
  19. When fin du traitement nominal
  20. Then état PROOF_PENDING_ANCHOR non bloquant

  21. Replay

  22. Given un DocumentTransfer.id déjà finalisé
  23. When même message est rejoué
  24. Then aucun nouvel événement/signature/acte n’est créé

  25. Transition interdite

  26. Given état ANCHOR_CONFIRMED
  27. When demande de transition vers un autre état
  28. Then rejet explicite + log sécurité

  29. Retry max atteint

  30. Given 3 échecs consécutifs
  31. When une nouvelle relance automatique est tentée
  32. Then refus automatique, escalade manuelle requise

  33. Validation formats

  34. Given hash_doc hors regex ^[0-9a-f]{64}$
  35. When création événement probatoire
  36. Then rejet strict avec code format invalide

  37. Zeroization

    • Given re-chiffrement terminé
    • When contrôle post-traitement est exécuté
    • Then aucun artefact PRE temporaire exploitable n’est récupérable

9. Hypothèses explicites

ID Hypothèse Impact si faux
H-01 Modules PD-41, PD-37, PD-39, PD-55 sont disponibles avec SLA contractuel. Blocage du flux nominal et montée d’erreurs techniques.
H-02 employee_consent est fourni par une source de vérité fiable. Risque de blocage abusif ou transfert non autorisé.
H-03 La vérification d’identité des coffres source/cible est déjà implémentée hors PD-72. Risque de transfert vers mauvais coffre.
H-04 Les IDs aléatoires suivent UUID v4. Non-conformité format/interop si autre schéma.
H-05 La conformité légale finale de l’“opposabilité” relève d’un audit externe. Point juridique hors validation logicielle automatisée.

10. Contraintes techniques et points à clarifier

10.1 Contraintes techniques (obligatoire)

  • Projet cible : ProbatioVault-backend
  • Stack contractuelle : NestJS + TypeORM + PostgreSQL + BullMQ (aucune référence Swift/Spring autorisée).
  • Sécurité runtime : exécution worker en environnement isolé, accès strictement limité aux modules requis.
  • Migration DDL colonne existante : Aucune applicable (PD-72 introduit une nouvelle entité; pas de type-change/nullable-change de colonne existante identifiée).
  • Contraintes inter-modules :
  • Aucune route HTTP cross-module à protéger spécifiquement dans PD-72 (flux asynchrone).
  • Interactions modules obligatoires : PRE, HSM, TSA, Merkle, anchoring, audit, preuve.
  • Échec contrat module externe => erreur explicite + état conforme §6.
  • Learnings imposés :
  • IDs aléatoires : UUID v4.
  • Vérification signature HSM sans double-hash.
  • Comparaison timing-safe pour tokens sécurité.
  • Typage sémantique distinct des identifiants métier.

10.2 Bornes numériques contractualisées

Paramètre Défaut Min Max Unité Contexte Percentile Hors bornes
retry_count 0 0 3 tentatives worker n/a rejet incrément >3
max_retries 3 1 3 tentatives worker n/a clamp interdit, rejet config
Latence transfert (hors batch) 3000 500 3000 ms backend prod P95 alerte SLO + non conformité ENF
Débit cible mensuel 10000 1000 50000 transferts/mois cluster prod n/a alerte capacité

10.3 SLA temporels (transitions avec temps)

SLA Défaut Min Max Configurabilité Expiration / dépassement
Délai max traitement nominal (hors batch) 3s 0.5s 3s non FAILED + retry
Fenêtre retry automatique 3 tentatives 1 3 non passage FAILED terminal si max atteint
Délai attente ancrage (PROOF_PENDING_ANCHOR) 24h 1h 72h oui (env var ANCHOR_TIMEOUT_HOURS) FAILED + alerte ops + escalade manuelle

10.4 Points clarifiés (v2)

  1. SLA ancrage : défaut 24h, min 1h, max 72h, configurable via ANCHOR_TIMEOUT_HOURS. Dépassement → FAILED + alerte ops.
  2. Format hsm_signature : base64 standard (RFC 4648) de la signature DER ECDSA P-384. Regex : ^[A-Za-z0-9+/=]+$, taille 44..4096 chars.
  3. Format tx_id : identifiant opaque dépendant du réseau d’ancrage, ASCII printable 1..128 chars. Le format exact est défini par le module anchoring (PD-55).
  4. Notification RH BLOCKED_WAITING_CONSENT : email neutre au service RH (1 seul envoi, pas de répétition automatique). Anti-spam : déduplication par DocumentTransfer.id.
  5. Epic / chemin : crypto-proof/PD-72-worker-pre-transfer dans ProbatioVault-backend.

Références

  • Epic : PD-189 — CRYPTO
  • JIRA : PD-72
  • Repos concernés : ProbatioVault-backend
  • Documents associés : expression de besoin PD-72, Architecture Technique RH v4.1, fiche brevet Legal PRE n°803, PD-41, PD-37, PD-39, PD-55, PD-38