Aller au contenu

PD-41 — Plan d'implémentation


📚 Navigation User Story | Document | | | ---------- | -- | | 📋 [Spécification](PD-41-specification.md) | | | 🛠️ **Plan d'implémentation** | *(ce document)* | | 🧪 [Tests](PD-41-tests.md) | | | ✅ [Critères d'acceptation](PD-41-acceptability.md) | | | 📝 [Retour d'expérience](PD-41-rex.md) | | [← Retour à crypto-proof](../PD-189-epic.md) · [↑ Index User Story](index.md)

1. Découpage en composants

Architecture cible

src/modules/crypto/pre/
├── pre.module.ts                    # Module NestJS
├── pre.service.ts                   # Service principal (façade)
├── pre.config.ts                    # Configuration et validation
├── interfaces/
│   ├── pre.interface.ts             # Types PRE (ReKey, Capsule, CFrag)
│   ├── pre-artefacts.interface.ts   # Formats PRE-1.0 (annexe 11)
│   └── pre-errors.interface.ts      # Codes d'erreur normatifs
├── providers/
│   ├── umbral.provider.ts           # Wrapper Umbral (bibliothèque)
│   └── umbral-mock.provider.ts      # Mock pour tests unitaires
├── validators/
│   ├── artefact.validator.ts        # Validation des formats PRE-1.0
│   ├── context.validator.ts         # Validation contextId cohérence
│   └── size-limit.validator.ts      # Limites de taille (INV-07)
├── dto/
│   ├── generate-rekey.dto.ts        # DTO generateReKey()
│   ├── re-encrypt.dto.ts            # DTO reEncrypt()
│   └── validate.dto.ts              # DTO validate()
└── __tests__/
    ├── pre.service.spec.ts          # Tests unitaires
    ├── pre.integration.spec.ts      # Tests d'intégration
    └── fixtures/                    # Blocs JSON PRE-1.0 référence

Composants et responsabilités

Composant Responsabilité
PreModule Module NestJS, export du service, injection des dépendances
PreService Façade publique : generateReKey(), reEncrypt(), validate(), getHealth()
PreConfig Configuration (limites de taille, timeouts), validation fail-fast au démarrage
UmbralProvider Wrapper de la bibliothèque Umbral, opérations cryptographiques pures
ArtefactValidator Validation des formats PRE-1.0 (structure, encodage, champs obligatoires)
ContextValidator Vérification cohérence contextId entre artefacts d'une même opération
SizeLimitValidator Contrôle des limites de taille (champs, artefacts, requête, cardinalité)

2. Flux techniques

Flux N1 — Health/Readiness

Client                     PreService                UmbralProvider
  │                            │                           │
  │──── getHealth() ──────────>│                           │
  │                            │──── isReady() ───────────>│
  │                            │<──── { ready, version } ──│
  │<─── { serviceVersion,      │                           │
  │      umbralVersion,        │                           │
  │      status: ready|not } ──│                           │

Flux N2 — generateReKey()

Client                     PreService        Validators         UmbralProvider
  │                            │                 │                    │
  │── generateReKey(dto) ─────>│                 │                    │
  │                            │── validateDto ─>│                    │
  │                            │<─ ok/reject ────│                    │
  │                            │── checkSize ───>│                    │
  │                            │<─ ok/reject ────│                    │
  │                            │── generateKFrags ──────────────────>│
  │                            │<─ KFrag[] ──────────────────────────│
  │                            │── buildReKeyArtefact ───────────────│
  │                            │── auditLog (sans secrets) ──────────│
  │<── ReKey (PRE-1.0) ────────│                 │                    │

Flux N3 — reEncrypt()

Client                     PreService        Validators         UmbralProvider
  │                            │                 │                    │
  │── reEncrypt(dto) ─────────>│                 │                    │
  │                            │── validateArtefacts ─>│              │
  │                            │<─ ok/reject ──────────│              │
  │                            │── validateContextId ─>│              │
  │                            │<─ ok/reject ──────────│              │
  │                            │── checkSize ─────────>│              │
  │                            │<─ ok/reject ──────────│              │
  │                            │── reEncrypt(capsule, kfrag) ────────>│
  │                            │<─ CFrag ────────────────────────────│
  │                            │── buildCFragArtefact ───────────────│
  │                            │── auditLog (sans secrets) ──────────│
  │<── CFrag (PRE-1.0) ────────│                 │                    │

Flux N4 — validate()

Client                     PreService        Validators
  │                            │                 │
  │── validate(artefact) ─────>│                 │
  │                            │── validateFormat ──>│
  │                            │<─ errors[] ────────│
  │                            │── validateContext ─>│
  │                            │<─ errors[] ────────│
  │                            │── auditLog ────────│
  │<── { verdict, codes[] } ───│                 │

2b. Diagrammes Mermaid

Graphe de dépendances des composants

graph TD
    PreModule["PreModule<br/><i>Module NestJS</i>"]
    PreService["PreService<br/><i>Façade publique</i>"]
    PreConfig["PreConfig<br/><i>Configuration</i>"]
    UmbralProvider["UmbralProvider<br/><i>Wrapper Umbral</i>"]
    ArtefactValidator["ArtefactValidator<br/><i>Formats PRE-1.0</i>"]
    ContextValidator["ContextValidator<br/><i>Cohérence contextId</i>"]
    SizeLimitValidator["SizeLimitValidator<br/><i>Limites de taille</i>"]
    AuditLogger["AuditLogger<br/><i>Journalisation sécurisée</i>"]

    PreModule --> PreService
    PreModule --> PreConfig
    PreService --> UmbralProvider
    PreService --> ArtefactValidator
    PreService --> ContextValidator
    PreService --> SizeLimitValidator
    PreService --> AuditLogger
    PreService --> PreConfig
    SizeLimitValidator --> PreConfig
    ArtefactValidator -.->|"valide formats"| UmbralProvider

Séquence — generateReKey (Flux N2)

sequenceDiagram
    participant C as Client
    participant PS as PreService
    participant AV as ArtefactValidator
    participant SV as SizeLimitValidator
    participant UP as UmbralProvider
    participant AL as AuditLogger

    C->>PS: generateReKey(dto)
    PS->>AV: validateDto(dto)
    alt DTO invalide
        AV-->>PS: INVALID_PUBLIC_KEY / INVALID_PARAMETERS
        PS->>AL: logError(contextId, errorCode)
        PS-->>C: throw PreException
    end
    AV-->>PS: ok
    PS->>SV: checkSize(dto)
    alt Dépassement
        SV-->>PS: PAYLOAD_TOO_LARGE
        PS->>AL: logError(contextId, errorCode)
        PS-->>C: throw PreException
    end
    SV-->>PS: ok
    PS->>UP: generateKFrags(ownerPk, recipientPk, t, n)
    UP-->>PS: KFrag[]
    PS->>PS: buildReKeyArtefact(KFrag[], contextId)
    PS->>AL: logSuccess(contextId, artefactType)
    PS-->>C: ReKey (PRE-1.0)

Séquence — reEncrypt (Flux N3)

sequenceDiagram
    participant C as Client
    participant PS as PreService
    participant AV as ArtefactValidator
    participant CV as ContextValidator
    participant SV as SizeLimitValidator
    participant UP as UmbralProvider
    participant AL as AuditLogger

    C->>PS: reEncrypt(dto)
    PS->>AV: validateArtefacts(capsule, reKey)
    alt Artefact invalide
        AV-->>PS: INVALID_ARTEFACT / INVALID_FORMAT
        PS->>AL: logError(contextId, errorCode)
        PS-->>C: throw PreException
    end
    AV-->>PS: ok
    PS->>CV: validateContextId(capsule, reKey)
    alt contextId divergent
        CV-->>PS: INVALID_CONTEXT
        PS->>AL: logError(contextId, errorCode)
        PS-->>C: throw PreException
    end
    CV-->>PS: ok
    PS->>SV: checkSize(dto)
    SV-->>PS: ok
    PS->>UP: reEncrypt(capsule, kfrag)
    alt Erreur Umbral
        UP-->>PS: REKEY_MISMATCH / CRYPTO_FAILURE
        PS->>AL: logError(contextId, errorCode)
        PS-->>C: throw PreException
    end
    UP-->>PS: CFrag
    PS->>PS: buildCFragArtefact(CFrag, contextId)
    PS->>AL: logSuccess(contextId, artefactType)
    PS-->>C: CFrag (PRE-1.0)

3. Mapping invariants → mécanismes

Invariant ID Exigence Mécanisme Composant Observable Risque
INV-01 Non-divulgation plaintext Aucune opération de déchiffrement exposée ; UmbralProvider ne manipule que des artefacts PRE UmbralProvider, PreService Absence de méthode decrypt() ; logs sans payload Faible
INV-02 Pas de clés privées requises API accepte uniquement des clés publiques (33 bytes compressées) ; validation stricte du format ArtefactValidator, DTOs Rejet si champ ressemble à clé privée (>33 bytes) Faible
INV-03 Versionnage obligatoire Champ formatVersion requis et validé ; rejet si absent ou inconnu ArtefactValidator Code UNSUPPORTED_VERSION si invalide Faible
INV-04 Validation déterministe Même entrée → même résultat ; pas de randomisation dans la validation ArtefactValidator, ContextValidator Tests de non-régression TC-VAL-02 Faible
INV-05 Fail-closed Try/catch global avec rejet explicite ; aucun résultat partiel en cas d'erreur PreService (wrapper) Absence d'artefact partiel dans réponses d'erreur Moyen
INV-06 Liaison contextId Extraction et comparaison de contextId sur tous les artefacts d'une opération ContextValidator Code INVALID_CONTEXT si divergence Moyen
INV-07 Limites de taille Validation pré-traitement avec limites configurables (champs, artefacts, requête, cardinalité) SizeLimitValidator, PreConfig Code PAYLOAD_TOO_LARGE si dépassement Moyen
INV-08 Audit sans secrets Logger dédié avec liste blanche de champs ; sanitization avant log PreService, AuditLogger Inspection logs : seuls contextId, keyIds, formatVersion, artefactType, errorCode, timestamp Moyen

4. Mapping critères d'acceptation → mécanismes

Critère ID Mécanisme(s) Composant Observable Risque
CA1 Validation formatVersion contre liste whitelist ["PRE-1.0"] ArtefactValidator Rejet avec UNSUPPORTED_VERSION Faible
CA2 Validation taille/encodage clé publique (44 chars base64url = 33 bytes secp256k1) ArtefactValidator Rejet avec INVALID_PUBLIC_KEY Faible
CA3 Vérification cohérence ReKey/clés publiques lors de reEncrypt UmbralProvider, ArtefactValidator Rejet avec REKEY_MISMATCH Moyen
CA4 Validation structure Capsule (ciphertextDigest 32 bytes, capsule ≤1024 bytes) ArtefactValidator Rejet avec INVALID_ARTEFACT Faible
CA5 Construction ReKey conforme PRE-1.0 avec tous champs obligatoires PreService.buildReKeyArtefact() Artefact JSON valide selon annexe 11 Faible
CA6 Construction CFrag conforme PRE-1.0 avec capsuleDigest, threshold PreService.buildCFragArtefact() Artefact JSON valide selon annexe 11 Faible
CA7 Retour verdict VALID/INVALID avec codes stables (table normative) PreService.validate() Réponse { verdict, codes[] } Faible
CA8 Liste blanche de champs loggables ; sanitization systématique AuditLogger Logs contiennent uniquement champs autorisés Moyen
CA9 Aucune API n'accepte de clé privée ; validation format entrées DTOs, ArtefactValidator Rejet si champ suspect (taille privKey) Faible
CA10 Limites configurables avec valeurs par défaut strictes PreConfig, SizeLimitValidator Rejet PAYLOAD_TOO_LARGE Moyen
CA11 Try/catch global + rejet explicite sans artefact partiel PreService (décorateur/wrapper) Réponse erreur sans données partielles Moyen
CA12 Endpoint health exposant version service, version Umbral, status PreService.getHealth() JSON { serviceVersion, umbralVersion, status } Faible
CA13 Extraction et comparaison contextId avant toute opération ContextValidator Rejet INVALID_CONTEXT si incohérence Moyen
CA14 Mapping code → situation dans le code ; tests de stabilité Constantes, tests TC-NR-01 Codes identiques sur mêmes erreurs Faible

5. Mapping tests (TC-*) → mécanismes + observables

Test ID Référence spec Mécanisme(s) Point(s) d'observation Niveau de test visé
TC-NOM-01 Flux N1, CA12 getHealth() JSON Unit + Integration
TC-NOM-02 Flux N2, CA5, INV-02 generateReKey(), buildReKeyArtefact() ReKey PRE-1.0 valide, pas de privKey Unit + Integration
TC-NOM-03 Flux N3, CA6 reEncrypt(), buildCFragArtefact() CFrag PRE-1.0 valide Unit + Integration
TC-CTX-01 INV-06, CA13 ContextValidator.validateContextCoherence() Succès si contextId cohérent Unit
TC-VAL-01 Flux N4, CA7, INV-04 validate() { verdict: VALID, codes: [] } Unit
TC-VAL-02 CA7, INV-04 validate() répété Mêmes entrées → mêmes résultats Unit
TC-SEC-01 CA8, INV-01/08 AuditLogger, inspection logs Absence champs interdits Integration
TC-AUD-01 CA8, INV-08 AuditLogger Entrée audit pour chaque opération Integration
TC-SEC-02 CA9, INV-02 Validation DTOs Rejet si privKey fournie Unit
TC-ERR-01 E1, CA1 ArtefactValidator.validateFormatVersion() UNSUPPORTED_VERSION Unit
TC-ERR-02 E2, CA2 ArtefactValidator.validatePublicKey() INVALID_PUBLIC_KEY Unit
TC-ERR-03 E3 ArtefactValidator.validateThreshold() INVALID_PARAMETERS Unit
TC-ERR-04 E4, CA3 UmbralProvider.reEncrypt() REKEY_MISMATCH Unit + Integration
TC-ERR-05 E5, CA4 ArtefactValidator.validateCapsule() INVALID_ARTEFACT Unit
TC-ERR-06 E6, CA11 Try/catch UmbralProvider CRYPTO_FAILURE (non déterministe) Unit (mock)
TC-ERR-07 E7, CA10, INV-07 SizeLimitValidator PAYLOAD_TOO_LARGE Unit
TC-ERR-08 E8, CA12 UmbralProvider.isReady() SERVICE_NOT_READY Unit + Integration
TC-ERR-09 E9, CA13, INV-06 ContextValidator INVALID_CONTEXT Unit
TC-FMT-REKEY-01 CA5 ArtefactValidator.validateReKey() Rejet clé publique invalide Unit
TC-FMT-REKEY-02 CA5 ArtefactValidator.validateReKey() Rejet threshold incohérent Unit
TC-FMT-CAPS-01 CA4 ArtefactValidator.validateCapsule() Rejet digest invalide Unit
TC-FMT-CFRAG-01 CA6 ArtefactValidator.validateCFrag() Rejet capsuleDigest invalide Unit
TC-INV-01 INV-01 Inspection code + tests Pas de fuite plaintext observable Code review
TC-NR-01 Stabilité codes Tous les TC-ERR-* Codes identiques entre versions Regression
TC-NR-02 Stabilité readiness TC-NOM-01 Health stable post-upgrade Regression
TC-NR-03 Non fuite logs TC-SEC-01 Scan logs automatisé Regression
TC-NEG-01 Robustesse Tous validators Rejet fail-closed, pas de crash Security
TC-NEG-03 INV-08 AuditLogger Pas de secret en logs même si injecté Security

Note : TC-NEG-02 (Flood/DoS) est hors périmètre PRE (Spec H5, Tests §7). Le rate limiting relève de la couche applicative amont.


6. Gestion des erreurs

Table des codes d'erreur (normative)

Code Situation Composant déclencheur HTTP Status suggéré
INVALID_PARAMETERS Paramètres incohérents (threshold t > n, etc.) ArtefactValidator 400
INVALID_PUBLIC_KEY Clé publique mal encodée ou taille incorrecte ArtefactValidator 400
INVALID_FORMAT Artefact ne respectant pas PRE-1.0 ArtefactValidator 400
INVALID_ARTEFACT Capsule/CFrag structurellement invalide ArtefactValidator 400
INVALID_CONTEXT contextId absent, divergent ou multiple ContextValidator 400
UNSUPPORTED_VERSION formatVersion inconnu ArtefactValidator 400
REKEY_MISMATCH ReKey incompatible avec clés/capsule UmbralProvider 400
PAYLOAD_TOO_LARGE Dépassement limite de taille SizeLimitValidator 413
CRYPTO_FAILURE Erreur interne Umbral UmbralProvider 500
SERVICE_NOT_READY Umbral indisponible PreService 503

Implémentation fail-closed

// Décorateur ou wrapper pour toutes les méthodes publiques
async executeWithFailClosed<T>(operation: () => Promise<T>): Promise<T> {
  try {
    return await operation();
  } catch (error) {
    // Log sans secrets (contextId, errorCode uniquement)
    this.auditLogger.logError({
      contextId: extractContextIdSafe(error),
      errorCode: mapToNormativeCode(error),
      timestamp: new Date().toISOString(),
    });
    // Toujours rejeter avec code normatif, jamais de résultat partiel
    throw new PreException(mapToNormativeCode(error));
  }
}

7. Impacts sécurité

Risques et mitigations

Risque Probabilité Impact Mitigation
Fuite de clé privée dans logs Faible Critique Liste blanche de champs loggables ; revue code
Injection de données dans contextId/metadata Moyenne Moyen Validation stricte ASCII 16-128 chars ; sanitization
DoS via payloads volumineux Moyenne Moyen Limites de taille multi-niveaux ; validation pré-décodage
Erreur partielle exposant état interne Faible Élevé Fail-closed systématique ; pas de résultat partiel
Timing attack sur validation Faible Faible Validation complète même en cas d'erreur précoce

Journalisation sécurisée

Champs autorisés (liste blanche) : - contextId - issuerKeyId / recipientKeyId (identifiants, pas les clés) - formatVersion - artefactType - errorCode - timestamp

Champs interdits (liste noire normative) : - plaintext - payload - capsule (contenu complet) - kfrag / cfrag (contenu complet) - privateKey - Tout secret dérivé

Conformité

  • eIDAS : traçabilité des opérations PRE sans exposition de secrets
  • RGPD : pas de données personnelles dans les artefacts PRE (contextId opaque)
  • PKCS#11 : non applicable (PRE est purement logiciel)

8. Hypothèses techniques

ID Hypothèse Impact si faux
H-01 Une bibliothèque Umbral compatible Node.js/TypeScript est disponible (ex: @aspect-dev/pyumbral bindings ou implémentation JS) Blocage complet ; nécessite évaluation alternatives (Rust+WASM, Python subprocess)
H-02 Le format PRE-1.0 (annexe 11) est stable et ne changera pas pendant l'implémentation Refactoring des validators si changement
H-03 Les limites de taille (annexe B) sont suffisantes pour les cas d'usage ProbatioVault Ajustement configuration sans impact code
H-04 secp256k1 est la courbe utilisée pour toutes les clés PRE Adaptation si autre courbe (P-256, etc.)
H-05 Le service PRE est appelé uniquement depuis le backend (Spec H5) — authN/authZ/rate limiting hors périmètre PRE Contrainte architecturale formalisée dans la spec
H-06 Les opérations Umbral sont synchrones et rapides (<100ms) Sinon : ajouter queuing/async

9. Points de vigilance (risques, dette, pièges)

Risques identifiés

  1. Bibliothèque Umbral : Peu de bindings Node.js matures. Évaluer :
  2. umbral-pre (Rust + NAPI)
  3. pyumbral via child process
  4. Implémentation TypeScript pure (risque sécurité)

  5. Testabilité TC-ERR-06 : Impossible de provoquer déterministiquement une erreur Umbral interne. Couverture indirecte via mock.

  6. Performance : Si volume élevé de reEncrypt(), considérer :

  7. Connection pooling vers Umbral
  8. Cache des validations
  9. Batch processing

Dette technique acceptée

  • Les seuils de taille (annexe B) sont informatifs ; valeurs exactes à définir lors de l'implémentation
  • Le code NON_AUTHORIZED est hors périmètre PRE (géré par couche amont)

Pièges à éviter

  1. Ne jamais logger les artefacts complets (capsule, kfrag, cfrag)
  2. Ne jamais exposer de méthode decrypt() même en interne
  3. Ne jamais accepter de champ privateKey dans les DTOs
  4. Toujours valider le contextId AVANT toute opération cryptographique
  5. Toujours retourner un code normatif (pas de message d'erreur libre)

10. Hors périmètre

  • Chiffrement symétrique des documents (AES-GCM) — géré ailleurs
  • PKI / Gestion des identités — hors périmètre PRE
  • Politiques d'accès métier (qui peut partager quoi) — couche applicative
  • Réseau décentralisé NuCypher (staking, nodes) — non utilisé
  • Rate limiting / AuthN — couche infrastructure amont
  • Révocation des ReKeys — à définir dans une US séparée
  • Résistance quantique — hors périmètre actuel