Aller au contenu

Chaîne de Signatures ECDSA — NF525

1. Algorithme cryptographique

Paramètre Valeur
Algorithme ECDSA (Elliptic Curve Digital Signature Algorithm)
Courbe SECP256R1 (P-256, NIST)
Hash SHA-256
Bibliothèque cryptography (Python)

2. Principe de chaînage

Chaque enregistrement (commande ou événement d'audit) contient : - signature : la signature ECDSA de l'enregistrement courant - previous_signature / previous_hash : la signature de l'enregistrement précédent

Le premier enregistrement utilise la valeur sentinelle "GENESIS" comme previous.

┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  Entrée #1   │───▶│  Entrée #2   │───▶│  Entrée #3   │
│              │    │              │    │              │
│ prev: GENESIS│    │ prev: sig#1  │    │ prev: sig#2  │
│ sig:  sig#1  │    │ sig:  sig#2  │    │ sig:  sig#3  │
└──────────────┘    └──────────────┘    └──────────────┘

3. Données signées

3.1 Commandes (orders)

Format de la donnée signée :

"{timestamp ISO}|{total_ttc}|{uuid}|{previous_signature}"

Exemple :

"2026-02-16T10:49:53+00:00|25.80|a1b2c3d4-...|GENESIS"

3.2 Journal d'audit (audit_logs)

Format de la donnée signée :

"{timestamp ISO}|{event_type}|{payload_hash_sha256_16chars}|{previous_hash}"

Le payload_hash est un SHA-256 tronqué à 16 caractères du JSON du payload, pour des raisons de compacité dans la signature.

4. Implémentation

4.1 Signature (core/security.py)

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec

class CryptoService:
    def __init__(self):
        self._private_key = ec.generate_private_key(ec.SECP256R1())
        self._public_key = self._private_key.public_key()

    def sign(self, data: str) -> str:
        signature = self._private_key.sign(
            data.encode('utf-8'),
            ec.ECDSA(hashes.SHA256())
        )
        return binascii.hexlify(signature).decode('utf-8')

4.2 Vérification de chaîne (services/audit_service.py)

def verify_chain(db: Session) -> dict:
    entries = db.query(AuditLog).order_by(AuditLog.id).all()
    for i, entry in enumerate(entries):
        expected_prev = entries[i-1].signature if i > 0 else "GENESIS"
        if entry.previous_hash != expected_prev:
            return {"valid": False, "broken_at": entry.id}
    return {"valid": True, "total_entries": len(entries)}

5. Gestion des clés

Aspect Méthode actuelle Méthode production
Génération À chaque démarrage (éphémère) Clés persistées dans certs/
Stockage En mémoire Fichiers PEM chiffrés
Rotation Non applicable Annuelle avec archivage
Sauvegarde Non applicable Coffre-fort numérique (HSM)

6. Propriétés de sécurité

  1. Inaltérabilité : Toute modification d'un enregistrement rompt la chaîne de signatures
  2. Détection : L'endpoint GET /audit/verify peut détecter automatiquement toute rupture
  3. Non-répudiation : Les signatures ECDSA prouvent l'authenticité des données
  4. Irréversibilité : Il est computationnellement impossible de recalculer une signature sans la clé privée

Code Source — Fiscal Repo

Accès direct au code

Le code source du périmètre fiscal est accessible en lecture sur le dépôt GitHub dédié. Un accès vous sera fourni en tant que collaborateur Read-only sur demande.

Fichier Rôle Lien GitHub
core/security.py Génération des clés ECDSA et signatures → Voir sur GitHub
services/compliance_service.py Clôtures Z et mensuelles chaînées → Voir sur GitHub
models/order.py Champs signature et previous_signature → Voir sur GitHub
services/order_service.py Application du chaînage à la création → Voir sur GitHub