Plateforme de confiance
Le fonctionnement interne de la plateforme : ce qu'elle fait pendant le vote, les règles qu'elle s'impose, et ce que le registre permet d'en contrôler ensuite.
Cette page décrit ces règles au niveau des principes, comme le livre blanc : base de données, fenêtre de signature, transport, publication, gestion des secrets, hébergement. Elles ne remplacent pas la vérification : chaque affirmation de la plateforme est publiée dans le registre et se contrôle sans elle.
Protocole v1
Digne de confiance pendant le vote, vérifiable ensuite.
La plateforme est un tiers de confiance dont la confiance est bornée dans le temps. Cette exposition est une décision de conception délibérée et documentée, pas un oubli.
-
Pendant le vote
Ce qui lui est confié
Elle sert la page de vote, remet les clés secrètes dans le mode de provisionnement par défaut et reçoit chaque bulletin signé sur la session VoteLink du votant. Pendant cette fenêtre, elle sait transitoirement qui vote et, dans le mode par défaut, quelle clé elle a remise à qui.
-
Ensuite
Ce qui se vérifie sans elle
Chacune de ses affirmations est matérialisée dans le registre public et vérifiable de façon indépendante. Les deux faces du registre, émargement et bulletins, sont publiées de sorte qu'on ne puisse pas les joindre en un lien votant-bulletin.
Le niveau de confiance se règle par élection : en mode BYO-only, où chaque votant apporte sa clé, la plateforme ne détient jamais le secret de signature d'aucun votant, et la fenêtre de confiance se réduit à la seule réception des bulletins.
Ce que « tiers de confiance » engage, et n'engage pas, est écrit dans la charte. Ce n'est ni un agrément, ni une certification délivrée par une autorité, ni un label.
La charte du tiers de confiancePostgreSQL, arbitre unique.
Chaque invariant exposé aux utilisateurs est imposé dans PostgreSQL par des écritures conditionnelles gardées et des contraintes d'unicité, jamais par de l'état en mémoire d'un processus : correct par construction, quel que soit le nombre de workers et d'instances, sans ordre de verrous ni interblocage.
Les règles inscrites dans le schéma
-
ballots (election_id, key_image)unicité - Une image de clé ne vote qu'une fois par élection. Filet de sécurité qui tient même face à un bogue applicatif.
-
ring_keys (election_id, public_key)unicité - Une clé publique n'apparaît qu'une fois parmi les membres d'anneau de l'élection.
-
chain_blocks (election_id, block_number)unicité - Le filet absolu contre les fourches : un écrivain concurrent perdant échoue et réessaie contre la nouvelle tête de chaîne.
-
ballotsajout strict · déclencheur - Tout UPDATE ou DELETE lève une exception. Seule exception codifiée : le drapeau is_chain_published, basculé de false à true dans la transaction qui valide un bloc.
-
chain_blocksajout strict · déclencheur - Même règle pour les ancres de chaîne ; seule exception : le statut OpenTimestamps, à sens unique pending → completed.
-
ballots.payloadbytea - Les octets signés, stockés tels quels. Jamais dans un type JSON normalisant : jsonb réordonne les clés et réécrit les espaces, ce qui casserait la vérification exacte à l'octet.
-
ballots.idUUID v4, sans horodatage - Un identifiant aléatoire, et aucune date de création : rien ne trahit l'ordre de dépôt.
Des écritures sans course possible
- Des écritures gardées. La bascule de has_voted, le plafond de distribution des clés et la réclamation d'une clé sont des UPDATE conditionnels : zéro ligne modifiée vaut refus. Jamais de lecture-modification-écriture en mémoire.
- La cryptographie hors verrou. La vérification de signature, pur calcul sur des données publiques, s'exécute avant la transaction d'écriture : la cryptographie ne tient jamais un verrou.
- Une élection ouverte, réaffirmée. Au dépôt, l'état ouvert de l'élection est réaffirmé dans la transaction, sérialisée contre la clôture : un bulletin est validé avant la clôture et publié par elle, ou refusé. Jamais un bulletin stocké que la chaîne exclurait.
- Des blocs sérialisés. La production des blocs passe par des tâches en file d'attente, sérialisées par élection ; la ligne validée en base est l'arbitre de chaque bloc.
Ne garder que le nécessaire, ne rien dater.
La plateforme ne stocke rien dont le protocole n'a pas besoin et ne publie rien qui date ou identifie un bulletin. L'horodatage des votes est traité comme un canal auxiliaire, supprimé de bout en bout.
Minimisation
- Les données du votant. Son e-mail, et de façon optionnelle son nom et son poids : rien de plus n'est demandé à l'organisateur.
- Les secrets chiffrés, puis effacés. Les clés secrètes de vote sont chiffrées au repos et supprimées à leur point d'usage. Une fois remise, la clé n'existe plus en base : elle ne survit, chiffrée, que dans un cache de 180 minutes au plus pour une nouvelle remise, et en est effacée au vote.
- La mémoire nettoyée. Les secrets transitoires sont mis à zéro explicitement, côté PHP (objets-valeurs adossés à sodium_memzero) comme côté Rust/WASM (tampons mis à zéro).
- Aucun traceur. Ni analytique ni traçage, sur la page de vote comme sur le reste du site : seuls subsistent les journaux du serveur.
Hygiène de chronologie
- Aucun horodatage de bulletin. Ni en base ni dans le registre ; des identifiants UUID v4 aléatoires sur toute la surface publique.
- Une publication par lots. Les bulletins sortent par lots d'au moins 600, selon un calendrier randomisé, triés par image de clé ; l'émargement est publié une seule fois, à la clôture.
- Le nettoyage physique. PostgreSQL estampille chaque ligne avec la transaction qui l'a écrite : un bulletin et sa bascule d'émargement partagent un instant la même estampille, invisible dans un export logique mais lisible avec un accès direct à la base. Une tâche bi-quotidienne réécrit ces lignes sous une transaction muette unique puis exécute un VACUUM : une estampille fraîche survit au plus un cycle de nettoyage.
Protéger le moment où la clé est en mémoire.
La clé secrète du votant ne vit dans son navigateur que le temps de la page de vote. C'est le moment le plus exposé côté client, et celui que la plateforme protège le plus strictement.
La page de vote
- Autonome. Une page sans framework, hors de l'application de l'organisateur. Chaque navigation hors du vote est un rechargement complet, qui efface la clé de la mémoire.
- La CSP la plus stricte. Premier-parti uniquement, aucun script tiers, aucune analytique ; exécution WASM autorisée, envois de formulaires désactivés au profit d'une soumission fetch uniquement.
- L'intégrité du code qui signe. Les assets compilés portent des empreintes d'intégrité de sous-ressource (SHA-384) : un nœud CDN compromis ne peut pas altérer silencieusement le chemin de code de signature.
- Son propre sous-domaine. Le vote vit sur voting.condorcet.vote, avec des cookies limités à l'hôte : son état de session est isolé du reste du site.
Transport, liens et sessions
- TLS jusqu'à l'application. Le chiffrement TLS est assuré par le serveur web de l'application lui-même, Caddy (écrit en Go, via FrankenPHP), sans proxy intermédiaire. Ses certificats Let's Encrypt sont émis et renouvelés automatiquement, tout accès HTTP est redirigé vers HTTPS, et ses réglages TLS modernes s'appliquent par défaut : TLS 1.3 privilégié, 1.2 au minimum.
- Aucun journal d'accès sur le vote. Le sous-domaine de vote n'est jamais journalisé : ses adresses portent le jeton du VoteLink, et des journaux horodatés recréeraient la chronologie des votes que la base refuse de conserver.
- Directement vers l'origine, sur TLS. Anneaux, distribution des clés et dépôt des bulletins voyagent directement entre le navigateur et la plateforme. Aucun CDN ne s'interpose pour les données de vote ; seuls les assets statiques compilés sont mis en cache périphérique.
- Un lien de capacité, sans compte. Le votant ne crée ni compte ni mot de passe : son VoteLink, envoyé par e-mail par la plateforme, est une capacité au porteur. Seule l'empreinte du jeton est stockée ; le lien est rattaché à un seul votant, sa session a un périmètre restreint et il est soumis à une limitation de débit.
- Des sessions côté serveur. Les sessions sont tenues dans Valkey (Redis). Côté navigateur, seul l'état strictement nécessaire : session, langue, thème.
- Le reste du site. Tout, scripts et WASM compris, est servi en première partie, sous une CSP stricte ; aucune origine tierce sur les pages publiques.
- Les organisateurs. Connexion par mot de passe ou passkey, second facteur TOTP optionnel, tentatives limitées par identifiant et par adresse IP.
Ce que l'application ne peut pas réécrire.
Un bloc est valide si et seulement si ses octets se hachent vers l'empreinte validée et que sa signature se vérifie, quel que soit l'endroit d'où on le lit. Le stockage ajoute ses propres barrières.
Isolation de la publication
- Un stockage à part. Les fichiers de la chaîne vivent dans leur propre bucket de stockage objet, avec leurs propres identifiants d'accès, séparés de tout autre contenu stocké et servis par leur propre nom d'hôte CDN : un défaut dans un chemin de téléversement ne peut jamais écrire dans la chaîne.
- Une écriture unique. Le bucket active le verrouillage d'objets du fournisseur : une fois écrit, un fichier de bloc ne peut être ni écrasé ni supprimé, même avec les identifiants de stockage de la plateforme. Il complète les déclencheurs en base et la chaîne d'empreintes.
- Des octets bruts. Les blocs sont servis tels quels ; aucune couche du framework ne peut les reformater. Le contenu étant déterministe, une écriture rejouée est idempotente.
La clé de signature
- Un outil épinglé. Chaque bloc est signé en OpenPGP v6 (RFC 9580, Ed25519) par une version épinglée et embarquée de Sequoia sq, dans des tâches de fond ; aucune mathématique de signature côté PHP.
- Des clés privées hors de la base. Les certificats publics sont en base ; les clés privées n'y entrent jamais. Elles sont conservées dans le gestionnaire de secrets d'OVHcloud (OKMS), selon les standards de sécurité les plus exigeants : chiffrées au repos, un domaine isolé par environnement, un accès par certificat client en TLS mutuel. OKMS ne fait que les garder : la signature reste locale, lit une version figée de la clé et en contrôle l'empreinte.
- Une rotation qui confine. À la rotation, la clé sortante reçoit une expiration à l'instant même et ses versions sont désactivées : une clé retirée vérifie toujours, elle ne signe plus.
- Ce que la signature ne prouve pas. Elle prouve que la plateforme a produit un bloc, pas qu'elle n'en a pas réécrit l'histoire, puisqu'elle détient la clé. L'immuabilité vient de la mise en miroir indépendante, et l'ancrage OpenTimestamps de chaque bloc prouve quand ses octets existaient.
Où elle tourne, comment elle est construite.
L'exploitation au sens large n'est pas décrite ici ; seuls figurent les points qui portent une garantie.
Hébergement
- En France. La plateforme est hébergée en France et tout est servi depuis son propre domaine : aucun service tiers ne se charge dans le navigateur des votants.
- Une pile resserrée. PostgreSQL pour les données ; Valkey pour les sessions, les files et le cache, jamais comme stockage principal. Des files de tâches isolées, pour qu'une rafale sur l'une ne prive jamais les autres de workers.
- Des secrets hors de la configuration. Sur les environnements distants, aucun identifiant ne vit dans les fichiers d'environnement : tous sont tenus par OKMS, le gestionnaire de secrets d'OVHcloud, avec un domaine isolé par environnement.
Discipline d'ingénierie
- La vraie cryptographie en test. La suite de tests exerce le vrai module WASM, jamais un simulacre, côté serveur comme côté navigateur, jusqu'à des parcours de vote complets pilotés par navigateur.
- Des vecteurs figés. Le cœur cryptographique fige des vecteurs de protocole : tout changement silencieux d'une chaîne de domaine, d'un format ou d'une disposition d'octets fait échouer le build. Il exécute aussi des balayages de malléabilité bit à bit et des cibles de fuzzing sur tous ses parseurs, et audite ses dépendances en CI.
- Des spécifications synchrones. Les spécifications sont versionnées avec le code et tenues en phase avec lui par convention explicite ; le livre blanc et ces pages en sont la forme publiée.
Ce qui reste affaire de confiance.
Ces limites sont documentées, pas dissimulées. Elles tiennent surtout à la fenêtre du vote ; ensuite, le registre borne ce qu'une plateforme pourrait faire sans être vue.
Ce qu'il faut lui confier
- Une plateforme malveillante au moment du vote. En mode clé générée par le serveur, elle pourrait précalculer l'image de clé de chaque clé qu'elle a remise et désanonymiser les bulletins. Les clés importées ou enrôlées retirent ce pouvoir ; les élections BYO-only suppriment tout secret détenu par le serveur.
- Une participation fabriquée. Elle pourrait marquer un absent comme votant et ajouter un bulletin dans les bornes publiées ; seul le votant concerné peut le détecter, en vérifiant sa propre ligne d'émargement.
- Le navigateur pendant la signature. Une injection de script pendant la fenêtre de signature reste le risque résiduel côté client : la CSP le minimise sans pouvoir l'éliminer.
- Les coordonnées au repos. Noms et e-mails des votants sont détenus en clair, car les VoteLink partent par e-mail et l'organisateur a besoin d'une liste lisible : une compromission de la base les exposerait. Les clés secrètes de vote, elles, sont toujours chiffrées.
- Le stockage physique. Les journaux WAL archivés, les sauvegardes et les journaux d'accès web restent une question d'infrastructure et de rétention.
- Une revue externe, à venir. Une revue indépendante du protocole et de son implémentation est prévue ; son rapport sera lié depuis la page sécurité.
Ce qui borne cette confiance
- Le registre. Signatures en anneau valides, aucune clé ayant signé deux fois, pas plus de bulletins que de votants enregistrés : tout se contrôle à partir du seul registre publié.
- Le recompte. Le résultat se recalcule à partir du registre, avec un logiciel qui n'est pas celui de la plateforme.
- La chaîne et ses miroirs. Des fichiers bruts, signés et chaînés, que chacun peut conserver : une réécriture devient manifeste pour tout témoin qui a gardé une tête de chaîne antérieure.
- Le code qui compte. Le cœur cryptographique (CryptoVote) et la bibliothèque de dépouillement (CondorcetPHP) sont open source : ce qui signe, vérifie et dépouille s'inspecte.
- Des engagements écrits. La charte dit ce que la plateforme s'engage à faire, et à ne pas faire : un registre publié n'est jamais retiré ni modifié à la demande de l'organisateur.
Voir aussi
- Registre public Ce que chaque élection publie, indépendamment de son transport : définition, anneaux, émargement, bulletins.
- Clés de la plateforme Les certificats OpenPGP v6 qui signent les blocs, leur rotation et leur point d'accès machine.
- Procédure de vérification Vérifier une élection de bout en bout : à partir du registre seul, ou avec la blockchain.