Livres blancs techniques

Documentation technique approfondie sur la cryptographie de PDF Pro

Publié : 16 avril 2025 Dernière mise à jour : 16 avril 2026 Version : 2.0

Index des livres blancs

  1. Transfert de fichiers chiffré de bout en bout
  2. Signature cryptographique de documents axée sur la confidentialité

Transfert de fichiers chiffré de bout en bout

Livre blanc PDF Pro WP-001 — Architecture et sécurité

1.1 Résumé

Ce livre blanc décrit l'architecture du système de Transfert sécurisé de PDF Pro, un mécanisme de transfert de fichiers chiffré de bout en bout dans lequel les fichiers sont chiffrés côté client avec AES-256-GCM avant le téléversement. Le serveur ne stocke qu'un texte chiffré opaque et n'a jamais la possibilité de déchiffrer, d'inspecter ou de lire le contenu des fichiers. Le système prend en charge deux modes de clé : une clé aléatoire générée automatiquement (transportée via le fragment d'URL, qui n'est jamais envoyé au serveur) ou une clé dérivée d'une phrase secrète via PBKDF2. Dans les deux modes, la clé de chiffrement n'existe que dans les navigateurs de l'expéditeur et du destinataire. Ce document détaille les primitives cryptographiques, le flux de données, le modèle de menace et les limites honnêtes du système. Note terminologique : ce système n'utilise pas de preuves à divulgation nulle de connaissance (ZKP). Lorsque nous disons que le serveur a une « connaissance nulle » de vos fichiers, nous voulons dire que — par conséquence du chiffrement côté client — le serveur ne détient que du texte chiffré qu'il ne peut pas déchiffrer.

1.2 Vue d'ensemble de l'architecture

Le système de Transfert sécurisé suit une séparation client-serveur stricte où toutes les opérations cryptographiques se déroulent exclusivement dans le navigateur :

Le serveur est intentionnellement conçu pour être un « tuyau muet » pour les données chiffrées. Il n'a connaissance ni de la clé de chiffrement, ni de la phrase secrète, ni du contenu des fichiers. Cela est imposé architecturalement, et non par simple politique.

Principe de conception : Le serveur doit pouvoir être compromis sans compromettre les données utilisateur. Même avec un accès complet à la base de données et l'exécution de code côté serveur, un attaquant ne peut pas déchiffrer les fichiers transférés.

1.3 Chiffrement : AES-256-GCM via la Web Crypto API

Tout le chiffrement de fichiers utilise l'algorithme de chiffrement authentifié AES-256-GCM, accessible via la Web Crypto API native du navigateur. Cela fournit à la fois la confidentialité (les données ne peuvent pas être lues) et l'intégrité (les données ne peuvent pas être modifiées sans détection).

1.3.1 Paramètres de l'algorithme

ParamètreValeurJustification
AlgorithmeAES-256-GCMApprouvé NIST, chiffrement authentifié avec données associées (AEAD)
Taille de clé256 bitsLongueur de clé AES maximale ; offre 128 bits de sécurité contre la force brute
Taille IV96 bits (12 octets)Longueur d'IV recommandée par le NIST pour le mode GCM
Taille du tag128 bits (16 octets)Tag d'authentification de longueur maximale pour une protection d'intégrité optimale
Génération d'IVcrypto.getRandomValues()Générateur de nombres aléatoires cryptographiquement sûr

1.3.2 Implémentation du chiffrement

// Flux de chiffrement simplifié (Web Crypto API)

const iv = crypto.getRandomValues(new Uint8Array(12));
const salt = crypto.getRandomValues(new Uint8Array(16));

// Dérive la clé depuis la phrase secrète (voir Section 1.4)
const key = await deriveKey(passphrase, salt);

// Chiffre le fichier
const ciphertext = await crypto.subtle.encrypt(
  { name: "AES-GCM", iv: iv },
  key,
  fileArrayBuffer
);

// Paquet : [salt (16o)] + [iv (12o)] + [texte chiffré + tag]
const blob = concatenate(salt, iv, ciphertext);

Le blob chiffré final est une concaténation de trois composants : le sel de 16 octets (utilisé pour la dérivation de clé), l'IV de 12 octets (utilisé pour AES-GCM) et le texte chiffré avec le tag d'authentification GCM ajouté de 16 octets. Ce blob est ce que le serveur reçoit et stocke.

1.4 Génération et transport de clé

Le Transfert sécurisé prend en charge deux modes de clé mutuellement exclusifs. Le mode est sélectionné par l'expéditeur lors de la création du transfert.

1.4.1 Mode A — Clé générée automatiquement (par défaut)

Dans le mode par défaut, aucune phrase secrète n'est impliquée. Le navigateur génère directement une clé AES de 256 bits cryptographiquement aléatoire :

1.4.2 Mode B — Transfert protégé par phrase secrète

Lorsque l'expéditeur choisit de définir une phrase secrète, la clé est dérivée de cette phrase secrète via PBKDF2 :

1.4.3 Paramètres PBKDF2 (Mode B)

ParamètreValeurJustification
AlgorithmePBKDF2Recommandé NIST SP 800-132 ; largement audité
Fonction de hachageSHA-256Hachage cryptographique standard ; sortie de 256 bits
Itérations600 000Conforme à la recommandation OWASP 2023 pour PBKDF2-SHA256
Taille du sel128 bits (16 octets)Unique par transfert ; empêche les attaques par tables arc-en-ciel
Longueur de clé en sortie256 bitsCorrespond à l'exigence de clé AES-256

1.4.4 Implémentation de la dérivation de clé (Mode B)

async function deriveKey(passphrase, salt) {
  // Importe la phrase secrète comme matériel de clé brute
  const keyMaterial = await crypto.subtle.importKey(
    "raw",
    new TextEncoder().encode(passphrase),
    { name: "PBKDF2" },
    false,
    ["deriveKey"]
  );

  // Dérive la clé AES-256-GCM
  return crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: salt,
      iterations: 600000,
      hash: "SHA-256"
    },
    keyMaterial,
    { name: "AES-GCM", length: 256 },
    false,
    ["encrypt", "decrypt"]
  );
}

Note de sécurité : La sécurité dépend de l'entropie de la phrase secrète en mode B. Nous recommandons des phrases secrètes de 12+ caractères mêlant majuscules, minuscules, chiffres et symboles.

1.5 Flux de données

Le cycle de vie complet d'un transfert sécurisé suit ce parcours :

1.5.1 Envoi (téléversement) — Mode A (clé générée automatiquement)

  1. L'expéditeur sélectionne un fichier dans le navigateur.
  2. Le navigateur génère une clé AES-GCM aléatoire de 256 bits et un IV aléatoire de 12 octets via crypto.getRandomValues().
  3. Le navigateur chiffre le fichier avec AES-256-GCM, produisant texte chiffré + tag d'authentification.
  4. Le navigateur concatène [IV | texte chiffré+tag] en un seul blob.
  5. Le navigateur téléverse le blob chiffré sur le serveur via HTTPS.
  6. Le serveur stocke le blob dans Supabase Storage et crée un enregistrement de métadonnées (ID de transfert, expiration, limite de téléchargement, nom de fichier, taille de fichier).
  7. Le serveur retourne une URL de transfert. Le navigateur ajoute la clé exportée au fragment d'URL (#).
  8. L'expéditeur partage l'URL complète (avec fragment) avec le destinataire.

1.5.2 Envoi (téléversement) — Mode B (phrase secrète)

  1. L'expéditeur sélectionne un fichier et saisit une phrase secrète dans le navigateur.
  2. Le navigateur génère un sel aléatoire de 16 octets et un IV de 12 octets via crypto.getRandomValues().
  3. Le navigateur dérive une clé AES-256 à partir de la phrase secrète via PBKDF2 (600K itérations).
  4. Le navigateur chiffre le fichier avec AES-256-GCM, produisant texte chiffré + tag d'authentification.
  5. Le navigateur concatène [sel | IV | texte chiffré+tag] en un seul blob.
  6. Le navigateur téléverse le blob chiffré sur le serveur via HTTPS.
  7. Le serveur stocke le blob dans Supabase Storage et crée un enregistrement de métadonnées (ID de transfert, expiration, limite de téléchargement, nom de fichier, taille de fichier).
  8. Le serveur retourne une URL de transfert contenant l'ID de transfert.
  9. L'expéditeur partage l'URL de transfert et la phrase secrète avec le destinataire via des canaux distincts.

1.5.3 Réception (téléchargement) — Mode A

  1. Le destinataire ouvre l'URL complète du transfert (avec la clé dans le fragment).
  2. Le navigateur extrait la clé AES du fragment d'URL (jamais envoyée au serveur).
  3. Le navigateur télécharge le blob chiffré depuis le serveur via HTTPS.
  4. Le navigateur extrait l'IV (12 premiers octets) du blob.
  5. Le navigateur déchiffre le texte chiffré avec AES-256-GCM en utilisant la clé et l'IV extraits.
  6. Si le déchiffrement réussit (le tag GCM est validé), le fichier en clair est présenté à l'utilisateur.
  7. Le serveur met à jour le compteur de téléchargements et, si le mode « brûler à la lecture » est activé, supprime le blob.

1.5.4 Réception (téléchargement) — Mode B

  1. Le destinataire ouvre l'URL de transfert et saisit la phrase secrète dans le navigateur.
  2. Le navigateur télécharge le blob chiffré depuis le serveur via HTTPS.
  3. Le navigateur extrait le sel (16 premiers octets) et l'IV (12 octets suivants) du blob.
  4. Le navigateur dérive la clé AES-256 à partir de la phrase secrète + sel via PBKDF2 (600K itérations).
  5. Le navigateur déchiffre le texte chiffré avec AES-256-GCM en utilisant la clé dérivée et l'IV.
  6. Si le déchiffrement réussit (le tag GCM est validé), le fichier en clair est présenté à l'utilisateur.
  7. Si le déchiffrement échoue (mauvaise phrase secrète = mauvaise clé = tag GCM invalide), une erreur est affichée.
  8. Le serveur met à jour le compteur de téléchargements et, si le mode « brûler à la lecture » est activé, supprime le blob.

1.6 Ce que le serveur stocke vs. ce qu'il ne voit jamais

Élément de donnéesLe serveur a-t-il accès ?Détails
Blob chiffré (sel + IV + texte chiffré + tag)OuiDonnées binaires opaques ; le serveur ne peut pas interpréter le contenu
ID de transfertOuiLes identifiants de transfert sont des UUID v4 générés par gen_random_uuid() de PostgreSQL, fournissant 122 bits d'aléa cryptographique issus du CSPRNG du serveur
Nom de fichier d'origineOuiStocké dans les métadonnées pour affichage au destinataire
Taille de fichier d'origineOuiStockée dans les métadonnées à des fins d'affichage
Horodatage d'expirationOuiUtilisé pour l'application de la suppression automatique
Nombre / limite de téléchargementsOuiUtilisé pour l'application du mode « brûler à la lecture » et des limites de téléchargement
ID utilisateur de l'expéditeur (si authentifié)OuiLie le transfert au compte pour la gestion
Phrase secrèteNon — jamaisJamais envoyée au serveur ; ne quitte jamais le navigateur
Clé de chiffrement dérivéeNon — jamaisN'existe qu'en mémoire du navigateur pendant le chiffrement/déchiffrement
Contenu en clair du fichierNon — jamaisSeul le texte chiffré atteint le serveur
Nombre d'itérations PBKDF2NonCodé en dur dans le client ; non transmis au serveur

1.7 Expiration automatique et « brûler à la lecture »

Tous les transferts sécurisés sont éphémères par conception. Aucune option de stockage permanent n'est proposée.

1.7.1 Options d'expiration

OptionComportementApplication
Brûler à la lectureLe blob chiffré est supprimé immédiatement après le premier téléchargement réussiCôté serveur : le blob est supprimé du stockage après la fin du flux de téléchargement
1 heureSuppression automatique après 1 heure, indépendamment du statut de téléchargementCôté serveur : tâche de nettoyage planifiée + vérification d'expiration à l'accès
24 heuresSuppression automatique après 24 heuresIdem ci-dessus
7 joursConservation maximale ; suppression automatique après 7 joursIdem ci-dessus

1.7.2 Garantie de suppression

Lorsqu'un transfert expire ou est brûlé à la lecture, le blob chiffré est définitivement supprimé de Supabase Storage. L'enregistrement de métadonnées est conservé pendant 30 jours dans un état « supprimé en douceur » (à des fins d'enquête sur les abus) avant d'être définitivement purgé. L'enregistrement de métadonnées ne contient ni la clé de chiffrement, ni la phrase secrète, ni aucune information susceptible de servir à reconstituer le fichier.

1.8 Modèle de menace et atténuations

MenaceVecteur d'attaqueAtténuation
Compromission du serveur L'attaquant obtient un accès complet à la base de données et au stockage Toutes les données stockées sont du texte chiffré AES-256-GCM. L'attaquant n'obtient que des blobs opaques. Sans la phrase secrète, forcer brutalement de l'AES 256 bits est computationnellement infaisable.
Interception réseau (MITM) L'attaquant intercepte les données en transit Tout le trafic utilise TLS 1.3. Même si TLS est cassé, l'attaquant n'obtient que des blobs chiffrés (comme dans la compromission du serveur).
Phrase secrète faible L'attaquant force brutalement une phrase secrète courte ou courante PBKDF2 avec 600K itérations rend chaque tentative coûteuse en calcul. Une phrase secrète de 4 caractères nécessiterait tout de même un calcul significatif. L'interface impose une longueur minimale et fournit un retour sur la robustesse.
Interception de phrase secrète L'attaquant intercepte la phrase secrète partagée entre l'expéditeur et le destinataire La phrase secrète est partagée hors bande (pas via notre système). Nous recommandons un canal différent de celui de l'URL de transfert. Cela relève de la responsabilité de l'utilisateur.
Falsification du code côté client L'attaquant modifie le JavaScript servi à l'utilisateur Tous les actifs sont servis via HTTPS avec le CDN de Vercel. Les hachages Subresource Integrity (SRI) protègent contre la falsification au niveau du CDN. Les utilisateurs peuvent vérifier le code source dans les DevTools du navigateur.
Extraction de mémoire L'attaquant extrait la clé de chiffrement de la mémoire du navigateur Les clés Web Crypto API sont marquées non extractibles lorsque possible. Les clés dérivées n'existent en mémoire que pendant l'opération de chiffrement/déchiffrement. L'isolation mémoire du navigateur fournit une protection au niveau OS.
Attaque par rejeu L'attaquant rejoue un blob chiffré capturé Chaque transfert a un ID unique et est protégé par des limites de téléchargement et une expiration. Les transferts « brûler à la lecture » sont supprimés après le premier accès.

1.8.1 Hypothèses

1.8.2 Hors périmètre

1.9 Limitations et divulgation honnête

Limitations honnêtes : Aucun système de sécurité n'est parfait. Nous croyons en la divulgation transparente des limites connues.


Signature cryptographique de documents axée sur la confidentialité

Livre blanc PDF Pro WP-002 — Architecture et sécurité

2.1 Résumé

Ce livre blanc décrit l'architecture du système de Signature confidentielle de PDF Pro, un mécanisme de signature cryptographique de documents où le PDF ne quitte jamais le navigateur du signataire. Le système utilise ECDSA avec la courbe P-256 et un hachage SHA-256 pour produire des signatures numériques détachées. Seuls le hachage du document, la signature cryptographique et la clé publique sont transmis au serveur. Les clés privées sont générées, chiffrées et stockées exclusivement dans le navigateur de l'utilisateur via IndexedDB, protégées par une dérivation de clé PBKDF2 (600 000 itérations) et un chiffrement AES-256-GCM. Ce document détaille l'architecture complète de signature et de vérification, le modèle de gestion des clés, le schéma de la charge utile signée, la conception de la piste d'audit, le modèle de menace et les limites honnêtes.

2.2 Vue d'ensemble de l'architecture

Le système de Signature confidentielle est conçu autour d'une contrainte fondamentale : le document PDF ne doit jamais être transmis au serveur. Cette contrainte est appliquée architecturalement via un modèle de signature détachée.

Garantie fondamentale : Le serveur ne voit jamais, ne reçoit jamais et ne traite jamais le document PDF. La seule donnée liée au document que le serveur reçoit est un hachage SHA-256 — une valeur de 256 bits de taille fixe à partir de laquelle le document d'origine ne peut pas être reconstitué.

2.3 Algorithme de signature : ECDSA P-256 / SHA-256

2.3.1 Paramètres de l'algorithme

ParamètreValeurJustification
Algorithme de signatureECDSA (Elliptic Curve Digital Signature Algorithm)NIST FIPS 186-4 ; signatures compactes ; sécurité élevée par bit de clé
CourbeP-256 (secp256r1 / prime256v1)Approuvée NIST ; niveau de sécurité 128 bits ; large prise en charge par la Web Crypto API
Fonction de hachageSHA-256NIST FIPS 180-4 ; condensé de 256 bits ; résistant aux collisions
Taille de cléClé privée 256 bits, clé publique 512 bits (non compressée)Standard pour P-256 ; équivalent à environ 3072 bits RSA
Taille de signature64 octets (r : 32 octets, s : 32 octets)Compact ; adapté au stockage et à la transmission
Format de signatureIEEE P1363 (r || s brut)Sortie native de la Web Crypto API ; encodée en base64url pour le stockage

Encodage de signature : ECDSA avec P-256 de la Web Crypto API produit une signature brute de 64 octets composée de deux entiers de 32 octets (r || s) au format big-endian de largeur fixe. Cette sortie brute est encodée en base64url pour le stockage. Ce N'est PAS encodé en DER — c'est le format IEEE P1363 que la Web Crypto API produit nativement.

2.3.2 Flux de signature

// 1. Hacher le document PDF (côté client)
const fileBuffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest("SHA-256", fileBuffer);
const hashHex = Array.from(new Uint8Array(hashBuffer))
  .map(b => b.toString(16).padStart(2, '0')).join('');

// 2. Signer le hachage avec la clé privée (côté client)
const signature = await crypto.subtle.sign(
  { name: "ECDSA", hash: "SHA-256" },
  privateKey,   // CryptoKey provenant d'IndexedDB (déchiffrée)
  hashBuffer
);

// 3. Envoyer au serveur : hachage + signature + clé publique (PAS le PDF)
await submitSignature({
  documentHash: hashHex,
  signature: base64Encode(signature),
  publicKey: exportedPublicKeyJWK
});

2.4 Gestion des clés : éphémères vs. persistantes

Type de cléContexteCycle de vieStockage
Éphémère Utilisateurs invités (non connectés) Générée par session ; détruite à la fermeture de l'onglet Uniquement en mémoire (objet CryptoKey) ; jamais persistée
Persistante Utilisateurs authentifiés (connectés) Générée une fois ; persistée entre sessions ; révocable IndexedDB (chiffrée avec PBKDF2 + AES-256-GCM)

2.4.1 Génération de clé

// Génère une paire de clés ECDSA P-256 (Web Crypto API)
const keyPair = await crypto.subtle.generateKey(
  {
    name: "ECDSA",
    namedCurve: "P-256"
  },
  true,   // extractible (nécessaire pour le chiffrement + le stockage)
  ["sign", "verify"]
);

// Exporte la clé publique au format JWK pour l'enregistrement serveur
const publicKeyJWK = await crypto.subtle.exportKey("jwk", keyPair.publicKey);

// Exporte la clé privée au format JWK pour le stockage chiffré
const privateKeyJWK = await crypto.subtle.exportKey("jwk", keyPair.privateKey);

Format de clé publique : Les clés publiques sont exportées et stockées au format JWK (JSON Web Key). L'empreinte de clé est calculée comme SHA-256 du JWK canonique contenant uniquement les champs publics {crv, kty, x, y} avec les clés triées par ordre alphabétique.

2.5 Protection des clés privées : PBKDF2 + AES-GCM → IndexedDB

Les clés privées persistantes ne sont jamais stockées en clair. Avant d'être écrites dans IndexedDB, la clé privée (exportée en JWK JSON) est chiffrée selon le même schéma que le Transfert sécurisé :

2.5.1 Paramètres de protection

ParamètreValeur
Dérivation de cléPBKDF2-SHA256, 600 000 itérations
Sel16 octets aléatoires (par clé)
ChiffrementAES-256-GCM
IV12 octets aléatoires (par chiffrement)
EntréeJWK de clé privée (chaîne JSON, encodée UTF-8)
Sortie stockée dans IndexedDB{ salt, iv, ciphertext, publicKeyJWK, keyId, createdAt }

2.5.2 Schéma de stockage

// Structure d'enregistrement IndexedDB pour une clé de signature chiffrée
{
  "keyId":       "identifiant-unique-uuid-v4",
  "publicKey":   { /* format JWK, non chiffré */ },
  "encryptedPrivateKey": {
    "salt":       "16-octets-encodes-en-base64",
    "iv":         "12-octets-encodes-en-base64",
    "ciphertext": "texte-chiffre-aes-gcm-encode-en-base64"
  },
  "algorithm":   "ECDSA",
  "curve":       "P-256",
  "createdAt":   "2026-04-16T00:00:00.000Z",
  "userId":      "id-utilisateur-supabase-auth"
}

Récupération de clé : Si l'utilisateur oublie sa phrase secrète de signature, la clé privée ne peut pas être récupérée. Nous ne disposons ni de la phrase secrète, ni de la clé dérivée, ni d'aucun mécanisme pour contourner la protection PBKDF2 + AES-GCM. Les utilisateurs doivent exporter des sauvegardes de clé.

2.6 Modèle de signature détachée

PDF Pro utilise un modèle de signature détachée, ce qui signifie que la signature est stockée séparément du document. C'est le mécanisme essentiel de confidentialité : le document ne quitte jamais l'appareil du signataire.

2.6.1 Ce qui est envoyé au serveur

2.6.2 Ce qui N'est PAS envoyé au serveur

Assurance cryptographique : SHA-256 est une fonction de hachage à sens unique. À partir du seul hachage e3b0c44298fc1c14..., elle fournit une forte assurance cryptographique, sous les hypothèses de sécurité d'ECDSA et de SHA-256, que le document d'origine ne peut pas être reconstitué. Le hachage ne révèle rien sur le contenu, la longueur ou la structure du document, si ce n'est confirmer son identité une fois re-haché.

2.7 Schéma de charge utile signée v1.0

Le schéma JSON suivant définit l'enregistrement complet de signature stocké sur le serveur :

{
  "schemaVersion":  "1.0",
  "signatureId":    "uuid-v4",
  "documentHash":   "sha256-hex-64-caracteres",
  "hashAlgorithm":  "SHA-256",
  "signature":      "signature-ecdsa-encodee-base64",
  "signatureAlgorithm": "ECDSA",
  "curve":          "P-256",
  "publicKey": {
    "kty": "EC",
    "crv": "P-256",
    "x":   "coordonnee-x-encodee-base64url",
    "y":   "coordonnee-y-encodee-base64url"
  },
  "signer": {
    "identityLevel": "authenticated | self-asserted",
    "displayName":   "chaîne ou null",
    "email":         "chaîne ou null",
    "userId":        "supabase-uid ou null"
  },
  "timestamp":      "ISO-8601-UTC",
  "metadata": {
    "fileName":     "nom-de-fichier-original.pdf",
    "fileSize":     123456,
    "pageCount":    12,
    "clientVersion": "2.0.0",
    "userAgent":    "chaine-user-agent-navigateur"
  },
  "auditChain": {
    "previousEventHash": "sha256-de-l-evenement-precedent ou null",
    "eventHash":         "sha256-de-cet-enregistrement"
  }
}

2.8 Flux de vérification

La vérification de signature est un processus en deux phases : une vérification cryptographique côté client suivie d'une contre-vérification côté serveur.

2.8.1 Phase 1 : Vérification cryptographique côté client

  1. L'utilisateur charge un PDF dans le navigateur.
  2. Le navigateur calcule le hachage SHA-256 du PDF chargé.
  3. Le navigateur interroge le serveur sur les enregistrements de signature correspondant à ce hachage.
  4. Pour chaque enregistrement de signature retourné, le navigateur effectue une vérification ECDSA :
    const isValid = await crypto.subtle.verify(
      { name: "ECDSA", hash: "SHA-256" },
      importedPublicKey,
      signatureBuffer,
      hashBuffer
    );
  5. Si isValid === true, la signature est cryptographiquement valide : le document n'a pas été modifié depuis sa signature et la signature a été produite par le détenteur de la clé privée correspondante.

2.8.2 Phase 2 : Contre-vérification côté serveur

  1. Le serveur confirme que la clé publique de l'enregistrement de signature est enregistrée pour un utilisateur connu (pour les signatures authentifiées).
  2. Le serveur confirme que l'enregistrement de signature n'a pas été révoqué.
  3. Le serveur confirme l'intégrité de la piste d'audit (validation de la chaîne de hachages).
  4. Le serveur retourne le niveau d'identité du signataire et son statut d'enregistrement.

2.8.3 Résultats de vérification

RésultatSignification
Valide (authentifié)La signature est cryptographiquement valide ET la clé publique appartient à un utilisateur PDF Pro enregistré et authentifié.
Valide (auto-déclaré)La signature est cryptographiquement valide mais l'identité du signataire est auto-déclarée (utilisateur invité ou nom non vérifié).
InvalideLa vérification cryptographique a échoué. Le document a été modifié depuis sa signature, ou la signature est corrompue.
RévoquéeLa signature était valide mais a été explicitement révoquée par le signataire.
Aucune signature trouvéeAucun enregistrement de signature n'existe pour ce hachage de document.

2.9 Piste d'audit : événements chaînés par hachage

Chaque événement de signature et de vérification est enregistré dans une piste d'audit infalsifiable. Les événements sont chaînés par hachage : chaque événement inclut le hachage SHA-256 de l'événement précédent, formant une chaîne en ajout uniquement, à la manière d'une blockchain.

2.9.1 Types d'événements d'audit

Type d'événementDéclencheurDonnées enregistrées
KEY_REGISTEREDL'utilisateur enregistre une nouvelle clé publiqueClé publique JWK, ID utilisateur, horodatage
DOCUMENT_SIGNEDL'utilisateur signe un documentHachage du document, signature, clé publique, identité du signataire, horodatage
SIGNATURE_VERIFIEDTout utilisateur vérifie une signatureHachage du document, résultat de la vérification, infos sur le vérificateur (si authentifié), horodatage
SIGNATURE_REVOKEDLe signataire révoque une signatureID de signature, raison de la révocation, horodatage
KEY_REVOKEDL'utilisateur révoque une clé publiqueID de clé publique, raison de la révocation, horodatage

2.9.2 Structure de la chaîne de hachages

// Chaque événement d'audit inclut :
{
  "eventId":            "uuid-v4",
  "eventType":          "DOCUMENT_SIGNED",
  "timestamp":          "ISO-8601-UTC",
  "data":               { /* charge utile spécifique à l'événement */ },
  "previousEventHash": "sha256-du-json-de-l-evenement-precedent",
  "eventHash":          "sha256-du-json-de-cet-evenement-sans-eventHash"
}

// Détection de falsification : pour vérifier la chaîne, calculez :
// SHA-256(JSON.stringify(événement sans le champ eventHash))
// et confirmez que le résultat correspond à eventHash.
// Confirmez ensuite que previousEventHash correspond à eventHash de l'événement précédent.

Si un événement de la chaîne est modifié, tous les liens de hachage suivants seront brisés, rendant la falsification immédiatement détectable. Cela garantit fortement l'intégrité de la piste d'audit.

Limitation importante : La chaîne de hachages rend la falsification détectable au sein de la séquence d'événements enregistrée. Cependant, un administrateur de base de données disposant d'un accès direct pourrait théoriquement supprimer et reconstruire la chaîne. Des garanties plus fortes nécessiteraient un horodatage externe ou une attestation par un tiers, ce qui n'est pas implémenté dans cette version.

2.10 Niveaux d'identité

NiveauExigencesPropriétés de confianceCas d'usage
Authentifié Utilisateur PDF Pro connecté avec e-mail vérifié ; paire de clés persistante enregistrée sur le compte E-mail vérifié par Supabase Auth ; clé publique liée à un compte authentifié ; piste d'audit liée à l'ID utilisateur Documents professionnels, contrats, accords formels
Auto-déclaré Utilisateur invité ou utilisateur authentifié avec nom saisi soi-même ; paire de clés éphémère ou persistante Intégrité cryptographique garantie ; l'identité du signataire est auto-déclarée et non vérifiée indépendamment Signature rapide, documents personnels, accords informels

Note : Dans l'interface du produit, « self_asserted » peut être affiché comme « Identité auto-déclarée » ou « Signature invité ». « authenticated » peut être affiché comme « Compte authentifié ». Ce sont des libellés d'affichage pour les mêmes niveaux d'identité sous-jacents.

Liaison d'identité : Une signature « authentifiée » signifie que la clé publique est enregistrée sur un compte PDF Pro avec un e-mail vérifié. Cela ne signifie PAS que l'identité réelle du signataire a été vérifiée par pièce d'identité officielle, biométrie ou vérification en personne. Nous n'effectuons pas de contrôles KYC (Know Your Customer).

2.11 Modèle de menace

MenaceVecteur d'attaqueAtténuation
Attaque par rejeu L'attaquant copie une signature valide et l'applique à un autre document La signature est liée au hachage SHA-256 du document spécifique. Un document différent aura un hachage différent et la vérification ECDSA échouera.
Falsification L'attaquant crée une signature valide sans la clé privée La sécurité d'ECDSA P-256 repose sur le problème du logarithme discret sur courbes elliptiques (ECDLP). Falsifier une signature sans la clé privée est computationnellement infaisable (niveau de sécurité 128 bits).
Substitution de clé L'attaquant enregistre sa propre clé publique et prétend qu'une signature a été apposée par un autre Les signatures authentifiées lient la clé publique à un e-mail vérifié. La piste d'audit enregistre quelle clé a signé quel document. Les événements d'enregistrement de clé sont chaînés par hachage.
Remplacement de document L'attaquant modifie un PDF signé et prétend que la signature reste valide Toute modification du PDF change son hachage SHA-256. La signature existante échouera à la vérification face au nouveau hachage. Trouver une collision (un document différent avec le même hachage) nécessite environ 2^128 opérations.
Vol de clé privée L'attaquant extrait la clé privée chiffrée d'IndexedDB La clé privée est chiffrée avec AES-256-GCM, dérivée par PBKDF2 (600K itérations). Sans la phrase secrète, déchiffrer la clé est computationnellement infaisable.
Compromission du serveur L'attaquant obtient un accès complet au serveur Le serveur ne détient que des clés publiques et des enregistrements de signature. Les clés privées n'atteignent jamais le serveur. Un attaquant ne peut pas falsifier de nouvelles signatures. Il pourrait supprimer ou modifier des enregistrements existants, mais les ruptures de la chaîne de hachages seraient détectables.
Falsification de la piste d'audit L'attaquant modifie les événements de la piste d'audit sur le serveur Événements chaînés par hachage : modifier un événement rompt la chaîne à partir de ce point. Des outils de vérification indépendants peuvent détecter les ruptures de chaîne.

2.11.1 Hypothèses

2.11.2 Hors périmètre

2.12 Divulgation honnête

Ce que les signatures PDF Pro NE sont PAS :

2.12.1 Pour quoi les signatures PDF Pro SONT adaptées

Notre engagement : Nous construisons la sécurité par l'architecture, et non par le marketing. Ces livres blancs décrivent exactement comment fonctionnent nos systèmes, y compris leurs limites. Nous estimons que les utilisateurs soucieux de la sécurité méritent une transparence technique complète. Si vous avez des questions sur un aspect quelconque de notre architecture, contactez-nous à info@webdesign9.com.