Les protocoles de vote électronique
Contraintes générales Un bon protocole de vote doit satisfaire plusieurs contraintes : Seuls les électeurs peuvent voter. Un électeur ne peut voter qu'une seule fois. Personne ne peut savoir pour qui un électeur a voté. Personne ne peut dupliquer le vote d'un électeur. Personne ne peut modifier le vote d'un électeur. Un électeur peut vérifier que son vote a bien été pris en compte.
Protocole dit « à deux piliers de contrôle » Difficulté de satisfaire la troisième contrainte : l'arbitre ne doit pas pouvoir établir de relation entre un vote et une identité physique. Système à deux piliers : l’arbitre se sépare en deux entités, un « vérificateur » et un « comptable ». Le vérificateur est chargé de l’attribution d’identifiants uniques aux électeurs. Il est incapable de déchiffrer un vote. Le comptable est chargé de dépouiller les votes. Il n'a pas connaissance de l'identité réelle des électeurs.
Déroulement du vote 1.Le vérificateur envoie à chaque électeur un numéro de validation unique généré aléatoirement, et crypté avec la clé publique de l'électeur. Il possède une liste de correspondance : numéro de validation / personne physique. 2.Le vérificateur envoie la liste des numéros de validation au comptable, cryptée avec la clé publique du comptable. 3.Chaque électeur génère un identifiant aléatoire et envoie au comptable sont numéro de validation accompagné d'un message crypté avec la clé publique du comptable, message contenant le vote et l'identifiant. M = (Valid, Ec(Id, V)).
Déroulement du vote (suite) 4.Le comptable vérifie que le numéro de validation figure bien sur sa liste, et que la personne n'ait pas déjà voté. Il marque ensuite cet électeur comme ayant voté. 5.A la fin du vote, le comptable décrypte tous les messages avec sa clé privée, compte les votes et publie la liste des votes / identifiants. 6.Chaque électeur peut vérifier que son vote a bien été pris en compte grâce à l'identifiant, connu de lui seul.
Système à deux piliers : conclusion Ce système est très intéressant car il est sans doute le plus fiable, et peut être mis en œuvre assez simplement. Cependant, une faille réside en ce qu'une entente entre le vérificateur et le comptable pourrait violer le caractère anonyme du vote.
ANDOS - définition ANDOS signifie « All-or-Nothing Disclosure of Secrets ». Le principe de base d’ANDOS est de permettre qu’un fournisseur puisse révéler un secret à un acquéreur sans savoir de quel secret il est question. L’acquéreur ne doit pas pour autant avoir accès aux autres secrets du fournisseur.
Protocoles de vote basé sur « ANDOS » Dans notre cas, le fournisseur est l’arbitre, les acquéreurs sont les électeurs et les secrets sont des identifiants. L’arbitre, bien qu’ayant distribué les identifiants, ne pourra pas faire le lien entre un vote et un électeur physique. Nous revenons donc à un système à 1 pilier.
Un protocole basique fondé sur ANDOS 1.L’arbitre dispose d'une paire de clés publique/privée. 2.Il crypte la liste des identifiants avec sa clé publique. 3.Il l’envoie à un électeur. 4.L’électeur génère un nombre aléatoire, le crypte avec la clé publique de l‘arbitre, le multiplie par le secret de son choix (déjà crypté) et envoie le résultat à l’arbitre. 5.L’arbitre décrypte ce message avec sa clé privée et envoie le résultat à l’électeur. 6.Celui-ci divise le message par son nombre aléatoire et détient ainsi le secret.
Un protocole basique fondé sur ANDOS (suite) Il s’agit du protocole que nous avons implémenté. Il repose sur le principe d’authentification anonyme. Cependant, dans cette version basique, ce protocole contient une faille critique : deux électeurs peuvent « piocher » le même identifiant! Dans un tel cas, seul le premier à voter verra son vote comptabilisé. La meilleure façon de minimiser la faille sans changer le protocole serait de réduire la probabilité d’apparition d’un tel cas. Pour cela, il faut augmenter le nombre d’identifiants que l’arbitre distribuera.
Protocole avancé fondé sur ANDOS Il pallie au problème du protocole précédent. C'est le protocole décrit dans le livre Applied Cryptography de Bruce Schneier. L’idée est de faire interagir les électeurs entre eux pour prévenir l’apparition de doublons dans les identifiants distribués. Comme nous le verrons, la fiabilité a un prix!
Protocole avancé fondé sur ANDOS (suite) Avant d'entrer dans les détails du protocole, il faut expliciter le terme de "FBI" (Fixed Bit Index) de deux nombres. Soient x et y deux nombres de n bits. Le FBI de x et y est l'ensemble des index de bits "i" pour lesquels x(i) = y(i). Par exemple, pour X = et Y = 10111, FBI(X, Y) = {1, 4} (lire les index de bits de droite à gauche).
Protocole avancé fondé sur ANDOS (suite) Le protocole en lui-même : il est valable pour au moins deux acquéreurs de secrets, appelons-les Bob et Carole. Alice sera notre fournisseur. k sera le nombre de secrets. 1.Alice génère une paire de clés publique/privée dédiée pour Bob, et une autre dédiée pour Carole. 2.Bob génère k nombres aléatoires et les révèle à Carole. Carole fait de même pour Bob. 3.Soit b l'indice du secret que Bob désire. Bob prend le b- ième nombre de la liste qu'il a reçu de Carole, C(b), et le crypte avec la clé publique d'Alice qui lui est dédiée. Il calcule et envoie à Carole le résultat du FBI de C(b) et E(C(b)). Carole suit le même processus de son côté.
Protocole avancé fondé sur ANDOS (suite) 4.Pour chaque nombre de la liste qu'il a envoyé à Carole au début, Bob remplace chaque bit de ce nombre qui n'est pas dans le FBI reçu de Carole par son complément. Puis il envoie ces résultats à Alice. Carole suit toujours le même processus de son côté. 5.Alice décrypte l'ensemble reçu de Carole avec la clé privée assignée à Bob. Pour chaque nombre de la liste, elle calcule le XOR avec le secret associé et envoie les résultats à Bob. Elle fait de même pour Carole avec les nombres reçus de Bob. 6.Bob trouve son secret en calculant le XOR de C(b) et du b-ième nombre reçu. Carole fait de même.
Protocole avancé fondé sur ANDOS (suite) La procédure est bien plus complexe, et assure que personne n'obtienne le même secret. Cependant, elle a aussi ses inconvénients : La complexité du problème croit de façon exponentielle lorsqu'on augmente le nombre d'électeurs. D'un point de vue pratique, le réseau risque d'être fortement perturbé. La procédure implique que les participants soient tous simultanément présents lors de l'attribution des identifiants, ce qui semble difficile à réaliser dans la réalité pour un vote de grande ampleur. De plus, une panne chez le moindre client signifierait l'échec de toute a procédure. Sans parler de panne, la moindre erreur, volontaire ou non, dans les échanges de données de la part d'un électeur peut signifier l'écroulement de tout le système.
Protocole avancé fondé sur ANDOS (suite) Détail de la complexité
Conclusion Les trois protocoles ont chacun leurs inconvénients, aucun des trois n'est parfait. Le premier est sans doute celui qui pose le moins de problème. Pour être mis en oeuvre il faudra s'assurer qu'il n'y ait pas d'entente entre les deux "arbitres" du vote. Le second protocole dispose d'une faille majeure qui interdit son utilisation pour un vote important. Enfin, le dernier protocole sera surtout limité par le nombre d'électeurs impliqués. On peut imaginer son utilisation pour des votes de très petite ampleur, ou en morcelant au maximum les élections en de nombreux points de vote.
Implémentation d'un système de vote électronique utilisant un protocole ANDOS
Choix des outils Nous avons choisi le langage de programmation Java pour développer les programmes. Java est un langage idéal pour la programmation d'applications en cryptographie, grâce entre autres à la classe BigInteger qui simplifie au maximum la manipulation des grands nombres, ou encore la classe SecureRandom pour la génération de nombres aléatoires. De plus, son caractère multi plateformes favorise le déploiement du programme. Pour les fonctions de cryptographie, nous avons utilisé un cryptage RSA fourni par la bibliothèque cryptix (
Déroulement des élections Concrètement, les élections se déroulent en deux phases : Dans un premier temps, attribution des identifiants via un protocole ANDOS. Ensuite, la phase de vote elle-même. Notons que ces deux phases peuvent être espacées dans le temps. Pour établir un parallèle avec une élection classique, la première phase correspondrait à l'attribution de la carte d'électeur pour chaque participant, et la seconde phase correspondrait au vote dans l'isoloir, chaque électeur étant muni de sa carte. On peut imaginer que le même identifiant ANDOS puisse servir pour d'autres élections du même type exactement comme une carte d'électeur.
Note quant à cette implémentation Dans cette implémentation, nous considérons que l'adresse IP du client est équivalente à l'identité physique de la personne. Dans la réalité, il faudra bien sûr trouver un autre moyen. On peut par exemple imaginer que l'identifiant soit attribué à la mairie, sous présentation de la carte d'identité tout en prenant garde à ce que seul l'électeur ait la possibilité de voir son numéro d'identification.
Exécution des programmes Phase d'attribution des identifiants : 1.L'arbitre lance le programme Server, au choix avec ou sans l'argument "-console" pour le choix de l'interface utilisateur. 2.Les personnes désirant voter lancent le programme ANDOSClient qui leur retournera leur numéro d'identification. Ces personnes doivent préalablement s'être physiquement authentifiées auprès de l'arbitre, ce qui signifiera ici que l'arbitre possède l'adresse IP correspondante dans sa liste d'électeurs. 3.Une fois cette phase terminée, l'arbitre peut fermer le serveur réseau.
Exécution des programmes (suite) Phase de vote : 1.Avant d'ouvrir la session des votes, l'arbitre doit avoir créé un fichier nommé "election.txt" qui contient sur la première ligne l'intitulé de l'élection, et sur chaque ligne suivante le nom d'un candidat. 2.L'arbitre déclare l'ouverture de la session des votes en cliquant sur le bouton StartVote. 3.Les personnes désirant voter lancent le programme Elections. Ils fournissent leur numéro d'identification, puis sélectionnent un candidat dans la liste, et enfin valident leur vote. Ils recevront un accusé de réception témoignant de leur vote, au cas où il y ait un litige. 4.L'arbitre déclare la session des votes terminée et ferme le serveur. Il peut commencer le décompte des votes, ceux-ci étant stockés dans le fichier "voteData2.txt".
Mécanismes (0/11) 1ère phase de l’élection, côté serveur : Le serveur créé un objet « ANDOSServer ». Lors de sa construction, l'objet « ANDOSServer » procède à : l'initialisation de la liste des personnes ayant reçu leur identifiant à false. la génération de la paire de clés RSA (1024 bits). l'appel de la méthode generateIDs. l'appel startServer. Puis il attend qu'on lui annonce la fin de la première phase des élections.
Mécanismes (1/11) Méthode generateIDs Elle génère un nombre prédéterminé d'identifiants qu'elle entre dans le tableau allIDs[ ], tout en s'assurant de ne pas entrer deux fois le même nombre. Puis, elle crypte chacun de ces nombres avec la clé publique, et sauvegarde ces données cryptées dans le tableau allEIDs[ ]. Méthode startServer Le serveur va être lancé dans un processus différent. Dès qu'un client voudra se connecter, le serveur créera une instance de la classe ServerSideProtocol1 dans un nouveau processus, lequel sera chargé de tous les échanges avec le client.
Mécanismes (2/11) Objet ServerSideProtocol1 Au lancement du processus, il va obtenir l'adresse IP du client et vérifier que celui-ci soit bien un électeur autorisé. Si ce n'est pas le cas, il lui retournera un message d'erreur, sinon il appelle la méthode proceedANDOS. Méthode proceedANDOS Cette méthode exécute exactement la procédure ANDOS que nous avons déjà décrite. ssp.sendObject(new Integer(maxElecteurs)); for (int i = 0; i < maxElecteurs; i++) ssp.sendObject(allEIDs[i]); ssp.sendObject(pubKey); BigInteger cryptedID = (BigInteger)ssp.receiveObject(); BigInteger decryptedID = RSAAlgorithm.rsa(cryptedID, privKey.getModulus(), privKey.getExponent()); ssp.sendObject(decryptedID); aVote[((ServerSideProtocol1)ssp).iVoter] = true; Puis la connexion avec le client est coupée.
Mécanismes (3/11) 1ère phase de l’élection, côté client : C'est le programme que doit lancer l'électeur pour obtenir son identifiant. Il gère l'ensemble du protocole ANDOS côté client. Fonction main Lance l'interface réseau. Reçoit et affiche un message d'acceptation (ou de refus) du serveur. Reçoit la liste cryptée des identifiants. En choisit un au hasard. Reçoit la clé publique RSA du serveur. Créé un nombre aléatoire. Crypte ce nombre avec la clé publique RSA. Multiplie le résultat par l'identifiant choisi, lui aussi crypté. Envoie le résultat au serveur. Reçoit une valeur en provenance du serveur correspondant au résultat précédent décrypté. Divise ce résultat par le nombre aléatoire créé précédemment. C'est l'identifiant recherché. Affiche l'identifiant.
Mécanismes (4/11) 2ème phase de l’élection, côté serveur : L’objet ElectionsServer prend le relais sur ANDOSServer. Constructeur Il reçoit en paramètre la liste des identifiants. Appelle getStoredData. Appelle startServer. Attend, tant qu'on ne lui spécifie pas de s'arrêter via l'interface utilisateur.
Mécanismes (5/11) Méthode getStoredData Elle va ouvrir le fichier "election.txt" pour y lire les informations relatives aux élections : intitulé de l'élection et liste des candidats. Lancement du serveur réseau. S'agissant du même serveur réseau que lors de la première phase, son comportement sera strictement identique au détail près qu'il n'instanciera non pas la classe ServerSideProtocol1, mais la classe ServerSideProtocol2.
Mécanismes (6/11) Objet ServerSideProtocol2 Durant la phase de vote, la communication client/serveur est un peu spéciale : le protocole ne se réalise pas d'un trait, mais en plusieurs étapes qui nécessitent à chaque fois de ré-établir une connexion. Aussi à chaque fois qu'il se connecte, le client doit spécifier à quelle étape il se trouve en envoyant un numéro d'étape. Ainsi, la première chose que fait l'objet ServerSideProtocol2 est de recevoir le numéro d'étape, et en fonction de celui-ci traitera le client différemment. Première étape : envoie au client l'intitulé de l'élection et la liste des candidats. Deuxième étape : il reçoit l'identifiant et le vote crypté. Il vérifie que l'identifiant est valide, et que la personne n'a pas déjà voté. Enfin, il appelle la méthode notifyNewVote, et renvoie le vote crypté au client en guise d'accusé de réception. Troisième étape : il reçoit l'identifiant et la clé privée de l'électeur, vérifie la validité de l'identifiant et appelle la méthode notifyNewDecryptionKey. Enfin, il ferme la connexion avec le client.
Mécanismes (7/11) Méthode notifyNewVote Elle ajoute juste le couple à la liste des votes reçus, et l'écrira également dans un fichier publique. Méthode notifyNewDecryptionKey Elle parcourt la liste des votes cryptés jusqu'à trouver celui correspondant à l'identifiant donné, puis décrypte ce vote avec la clé qu'elle a reçu, le divise par l'identifiant et enfin publie le résultat dans un fichier.
Mécanismes (8/11) 2ème phase de l’élection, côté client : L'électeur lance ce programme pour voter une fois qu'il possède son identifiant. Constructeur Au démarrage, une paire de clés RSA est générée. Ensuite, le programme attend que l'électeur renseigne son identifiant via l'interface graphique. A ce moment-là l'interface graphique appelle la méthode connecter.
Mécanismes (9/11) Méthode connecter Elle obtient alors l'identifiant par l'interface graphique Ouvre une connexion avec le serveur. Précise au serveur qu'elle se trouve à l'étape 0. Reçoit l'intitulé de l'élection ainsi que la liste des candidats. Réfère de cette liste à l'utilisateur via l'interface graphique. Ferme l'interface réseau.
Mécanismes (10/11) Méthode voter Elle est appelée lorsque l'électeur a choisi un candidat. Elle obtient alors le numéro du candidat. Crypte ce nombre avec la clé RSA publique. Lance une interface réseau. Envoie le code d'étape 1. Envoie l'identifiant et le vote crypté. Reçoit l'acceptation (ou le refus) du serveur, puis un accusé de réception du vote. Ferme la connexion, puis sauvegarde dans un fichier l'accusé de réception. Puis elle ouvre à nouveau une connexion. Envoie au serveur le code d'étape 2. Envoie l'identifiant et la clé privée. Reçoit à nouveau l'acceptation (ou le refus) de l'identifiant par le serveur. Ferme l'interface réseau.
Mécanismes (11/11) Méthode voter (détail du code) int dVote = w_IHMElecteurs1.lst_Candidats.getSelectedIndex(); BigInteger vote = RSAAlgorithm.rsa(identifier.multiply(BigInteger.valueOf(dVote)), pubKey.getModulus(), pubKey.getExponent()); cni = new ClientNetworkInterface2(serverIP, port); cni.sendObject(new Integer(1)); cni.sendObject(identifier); cni.sendObject(vote); int code = ((Integer)cni.receiveObject()).intValue(); String msg = (String)cni.receiveObject(); if (code == 0) { new Messages ("Message du serveur : " + msg, null); cni.closeInterface(); return; } vote = (BigInteger)cni.receiveObject(); cni.closeInterface(); sauvegarderAccuseReception(vote); cni = new ClientNetworkInterface2(serverIP, port); cni.sendObject(new Integer(2)); cni.sendObject(identifier); cni.sendObject(privKey); code = ((Integer)cni.receiveObject()).intValue(); msg = msg + " \n" + (String)cni.receiveObject(); new Messages ("Message du serveur : " + msg, null); cni.closeInterface();