Aller au contenu

PD-38 — Plan d'implémentation


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

Objectif

Implémenter le calcul d'empreinte SHA3-256 sur les documents chiffrés pour garantir leur intégrité probatoire.

Choix techniques retenus

  • Algorithme : SHA3-256 (NIST FIPS 202)
  • Bibliothèque : js-sha3 + Node.js crypto
  • Streaming : Support fichiers > 10MB
  • Format : 64 caractères hexadécimaux

Architecture ciblée

src/modules/crypto/
└── hash.service.ts           # Service de hachage

src/modules/documents/
└── services/
    └── document-secure.service.ts  # Intégration hash

Découpage technique

Phase 1 : Hash Service

  1. Créer HashService :
@Injectable()
export class HashService {
  hash(data: string | Buffer): string {
    if (!data || (Buffer.isBuffer(data) && data.length === 0)) {
      throw new BadRequestException('Cannot hash empty data');
    }
    return sha3_256(data);
  }

  hashDocument(file: Buffer): string {
    if (!file || !Buffer.isBuffer(file) || file.length === 0) {
      throw new BadRequestException('Invalid file');
    }
    return sha3_256(file);
  }
}

Phase 2 : Streaming

  1. Implémenter hashDocumentStream(stream) :
  2. Utiliser Node.js crypto.createHash('sha3-256')
  3. Traiter par chunks
  4. Valider stream non vide
async hashDocumentStream(stream: Readable): Promise<string> {
  return new Promise((resolve, reject) => {
    const hash = crypto.createHash('sha3-256');
    let hasData = false;

    stream
      .on('data', (chunk) => {
        hasData = true;
        hash.update(chunk);
      })
      .on('end', () => {
        if (!hasData) reject(new Error('Empty stream'));
        else resolve(hash.digest('hex'));
      })
      .on('error', reject);
  });
}

Phase 3 : Validation

  1. Implémenter verify(data, expectedHash) :
  2. Calculer hash
  3. Comparer en constant-time
  4. Retourner boolean

  5. Implémenter isValidHashFormat(hash) :

  6. Regex /^[a-f0-9]{64}$/i
  7. Valider longueur 64 chars

Phase 4 : Intégration documents

  1. Dans DocumentSecureService.create() :
  2. Recevoir fichier chiffré
  3. Calculer hash SHA3-256
  4. Stocker dans colonne file_hash

  5. Vérification doublon :

  6. Index unique sur (user_id, file_hash)
  7. Rejeter si hash déjà existant

Phase 5 : API

  1. Endpoint vérification :
  2. GET /documents/:id/hash
  3. Retourner hash pour vérification client

Phase 6 : Tests

  1. Tests vecteurs NIST FIPS 202
  2. Tests fichiers vides (rejet)
  3. Tests streaming grands fichiers
  4. Tests unicité hash par user

Points de vigilance

  • Fichier chiffré : Hash sur ciphertext, pas plaintext
  • Empty file : Toujours rejeter
  • Collision : Théoriquement impossible (256 bits)
  • Performance : Streaming pour fichiers > 10MB

Hors périmètre

  • Stockage OVH (→ autre US)
  • Arbre de Merkle (→ PD-60+)
  • Signature HSM du hash (→ PD-37)