Aller au contenu

PD-36 — Retour d'expérience


Navigation User Story | Document | | | ---------- | -- | | [Spécification](PD-36-specification.md) | | | [Plan d'implémentation](PD-36-plan.md) | | | [Critères d'acceptation](PD-36-acceptability.md) | | | **Retour d'expérience** | *(ce document)* | [Retour à crypto-proof](../PD-189-epic.md) · [Index User Story](index.md)

1. Résumé exécutif

PD-36 implémente un client HSM Cloud PKCS#11 pour le backend NestJS de ProbatioVault, permettant les opérations cryptographiques sensibles (signature, vérification, mTLS) sans jamais exposer les clés privées hors du HSM.

Bilan quantitatif

Métrique Valeur
Fichiers créés/modifiés 8
Tests unitaires 66 (100% pass)
Écarts identifiés 4
Écarts résolus 4/4
Verdict final ACCEPTÉ

Écarts traités

ID Niveau Description Résolution
E-01 BLOQUANT mTLS PKCS#11 signing non opérationnel OpenSSL ENGINE API avec URI PKCS#11 RFC 7512
E-02 MAJEUR CSR uniquement ECDSA, pas RSA Détection automatique du type de clé, RSA-PSS SHA-384
E-03 MAJEUR Pas de pool de sessions HSM HsmSessionPool avec timeout et renouvellement
E-04 MINEUR Validation config HSM tardive Fail-fast dans validateHsmConfig()

2. Points fluides

2.1 Architecture modulaire NestJS

L'intégration avec le lifecycle NestJS (OnModuleInit/OnModuleDestroy) s'est avérée naturelle et robuste. La séparation Provider/Operations/Service facilite les tests et la maintenance.

src/modules/crypto/hsm/
├── hsm.module.ts                    # Module NestJS
├── hsm.service.ts                   # Service principal (facade)
├── hsm.config.ts                    # Configuration + validation
├── interfaces/
│   └── pkcs11.interface.ts          # Types PKCS#11
└── providers/
    └── cloudhsm-pkcs11.provider.ts  # Provider + Operations

2.2 Chargement dynamique de pkcs11js

Le chargement dynamique (await import('pkcs11js')) évite les erreurs de compilation native en CI/test où CloudHSM n'est pas disponible.

2.3 Tests unitaires avec mocks

La stratégie de mock complet du provider PKCS#11 permet une couverture de 100% sans dépendance au HSM réel. Les 66 tests couvrent tous les scénarios incluant les 22 tests E-01 pour mTLS.


3. Points difficiles

3.1 E-01 : mTLS avec signature HSM (BLOQUANT)

Problème : Node.js ne permet pas de fournir une clé privée "opaque" pour TLS. L'implémentation initiale tentait d'utiliser directement le certificat sans déléguer la signature au HSM.

Solution : Utilisation de l'API OpenSSL ENGINE via tls.createSecureContext() :

const secureContextOptions: tls.SecureContextOptions = {
  cert: certBuffer,
  privateKeyEngine: engineName,      // 'cloudhsm' ou 'pkcs11'
  privateKeyIdentifier: pkcs11Uri,   // pkcs11:token=cavium;object=key-label;type=private
};

Complexité : Cette API est marquée @deprecated dans Node.js (OpenSSL ENGINE API deprecated in 3.0), mais reste la seule option viable. Un commentaire NOSONAR documente cette décision.

3.2 E-02 : Support RSA dans CSR et export public key

Problème : L'implémentation initiale ne supportait que ECDSA pour la génération de CSR et l'export de clé publique.

Solution : Ajout de la détection automatique du type de clé (CKK_RSA vs CKK_EC) et construction du SPKI approprié :

  • ECDSA : Export direct de CKA_EC_POINT
  • RSA : Construction manuelle du SPKI via buildRsaSpki(modulus, publicExponent)

3.3 Construction ASN.1/DER manuelle

PKCS#11 ne fournit pas de génération CSR native. L'implémentation manuelle des structures DER (SEQUENCE, SET, BIT STRING, OID encoding) a nécessité une attention particulière aux détails de la spécification X.509.


4. Hypothèses révélées tardivement

4.1 OpenSSL ENGINE requis côté système

L'utilisation de privateKeyEngine nécessite que l'engine CloudHSM soit installé et configuré côté système (/etc/cloudhsm/cloudhsm_client.cfg). Cette dépendance n'était pas explicitement documentée dans la spécification.

Impact : Documentation d'installation mise à jour, variable CLOUDHSM_OPENSSL_ENGINE ajoutée à la configuration.

4.2 CloudHSM SDK 5 vs SDK 3

Les templates de génération de clé diffèrent entre SDK 3 et SDK 5. Le SDK 5 dérive implicitement CKA_CLASS/CKA_KEY_TYPE du mécanisme, et ajouter ces attributs explicitement provoque CKR_ATTRIBUTE_VALUE_INVALID.

Impact : Templates simplifiés, commentaires explicatifs ajoutés.


5. Invariants complexes

5.1 INV-1 : Clés non-extractables

Les clés privées ne doivent JAMAIS quitter le HSM.

Cet invariant est garanti par CloudHSM (CKA_EXTRACTABLE=FALSE implicite). L'implémentation mTLS via OpenSSL ENGINE respecte cet invariant car la signature TLS est déléguée au HSM.

Vérification : Aucun appel à C_WrapKey ou export de clé privée dans le code.

5.2 INV-2 : Sessions HSM proprement fermées

Les sessions HSM doivent être fermées proprement après usage.

Garanti par :

  • OnModuleDestroy qui appelle finalize()
  • HsmSessionPool.closeAll() qui ferme toutes les sessions poolées
  • Try/catch systématique avec logging des erreurs de fermeture

6. Dette technique

6.1 DT-01 : OpenSSL ENGINE API deprecated

Élément Valeur
Localisation hsm.service.ts:buildMtlsAgent()
Nature API privateKeyEngine deprecated (OpenSSL 3.0)
Risque Retrait futur dans Node.js
Mitigation NOSONAR documenté, veille sur évolutions Node.js
Action recommandée Migrer vers OSSL_PROVIDER quand supporté par Node.js

6.2 DT-02 : Parsing ASN.1 simplifié

Élément Valeur
Localisation cloudhsm-pkcs11.provider.ts:parseCertificate()
Nature Parsing DER manuel vs bibliothèque dédiée
Risque Cas limites non gérés (certificats complexes)
Mitigation Fallback vers valeurs par défaut si parsing échoue
Action recommandée Envisager @peculiar/asn1-x509 pour parsing robuste

7. Risques résiduels

7.1 Disponibilité HSM

Risque Indisponibilité temporaire du HSM (réseau VPN, maintenance AWS)
Probabilité Faible
Impact Élevé (blocage des opérations crypto)
Mitigation actuelle Pas de fallback mock (HSM obligatoire), erreurs explicites
Recommandation Monitoring applicatif, alertes sur erreurs HSM

7.2 Expiration de sessions longues

Risque Sessions HSM expirées pendant opérations longues
Probabilité Faible (pool avec renouvellement)
Impact Moyen (échec opération en cours)
Mitigation actuelle HsmSessionPool renouvelle avant expiration (marge 30s)
Recommandation Tests de charge avec opérations longues

8. Améliorations de processus

8.1 Spécification mTLS plus explicite

La spécification initiale mentionnait "authentification mTLS" sans détailler le mécanisme de délégation de signature. Une section dédiée aux contraintes techniques (OpenSSL ENGINE, PKCS#11 URI) aurait évité l'écart E-01.

Suggestion : Ajouter une section "Contraintes d'implémentation" dans le template de spécification pour les US touchant à des APIs système.

8.2 Tests d'intégration CloudHSM

Les tests unitaires avec mocks sont exhaustifs, mais ne valident pas le comportement réel du HSM. La stratégie on-demand CloudHSM (annexe PD-36) permet des tests d'intégration périodiques.

Suggestion : Pipeline CI dédié "HSM Integration" exécuté hebdomadairement.


9. Enseignements clés

Pour le projet

  1. mTLS avec HSM : L'API OpenSSL ENGINE reste la seule option viable en Node.js pour mTLS avec clé HSM. Documenter cette contrainte pour les futures US.

  2. PKCS#11 URI (RFC 7512) : Standard essentiel pour identifier les objets HSM. Format : pkcs11:token=<token>;object=<label>;type=private.

  3. Pool de sessions : Indispensable pour éviter les timeouts sur applications long-running. Le pattern acquire/release est robuste.

Pour l'équipe

  1. Validation fail-fast : Les erreurs de configuration doivent être détectées au démarrage, pas à l'exécution d'une opération.

  2. Tests mocks vs intégration : Les mocks permettent une couverture rapide, mais ne remplacent pas les tests d'intégration périodiques.

  3. Documentation API deprecated : Documenter explicitement (NOSONAR + commentaires) les usages d'API deprecated avec justification et plan de migration.


Annexes