Vote électronique : vote démocratique ?

Adrien Foucart | 08 Jun 2015
2xRien - un blog

Le 25 mai 2014, les belges se rendaient aux urnes pour un triple scrutin législatif, régional et européen. Dans 17 des 19 communes bruxelloises, le système de vote utilisé est électronique, et fonctionne depuis 1994 sans accrocs. Cette fois-ci, cependant, il y a un bug. Les résultats se font attendre, les explications aussi. Quelques jours plus tard, les informations commencent à sortir : environ 2000 votes ont du être annulés car le logiciel comptabilisait mal les voix lorsque l’utilisateur était revenu en arrière pour changer son choix.

Le nouveau journal coopératif “Médor” en a fait le sujet son premier dossier d’investigation. Je n’aime pas trop ce dossier, qui en cherchant à présenter les faits d’une manière plus ludiques, sous forme d’enquête, perd je trouve beaucoup en clarté. Le fait qu’une grande partie de leurs informations techniques viennent d’un “hacker” non-identifié n’aide pas non plus à apporter la crédibilité requise. Mais avec leurs informations, celles publiées par l’association PourEVA (Pour une Éthique de Vote Automatisé), et aussi le code source du système de vote ainsi que l’explication du bug publié par le Ministère de l’Intérieur, on peut se faire une bonne idée de ce qui s’est passé.

  1. Partie technique : le Code et le Bug
  2. Un vote démocratique et sécurisé, c’est quoi ?
  3. Vote électronique, stop ou encore ?

1. Partie technique : le Code et le Bug

L’application de vote automatisé JITES a été développée par la société Stésud, rachetée depuis par le groupe NRB via sa filière Civadis. Ils ne sont pas les seuls en cause : le code est contrôlé de manière indépendante avant chaque élection par PricewaterhouseCooper, qui valide que tout est fonctionnel.

En jetant un coup d’oeil sur le code, on comprend très vite pourquoi l’erreur n’a pas pu être détectée : c’est le bordel complet. Fonctions mal ou pas documentées, conventions inexistantes, et surtout manque total de contrôle de la cohérence des données : avant d’enregistrer le vote sur la carte, rien ne vérifie si il y a un problème potentiel, si il n’y a bien qu’une seule liste sélectionnée, si il n’y a pas de candidats de listes différentes sélectionnés… Bref, que tout est en ordre.

Autre manquement majeur : le code tel qu’il est présenté ne permet pas de facilement créer la batterie de tests automatisés qui seule pourrait garantir qu’un changement effectué dans le code ne change rien au comportement du programme. Ce manque de rigueur peut fonctionner pour des programmes où ce n’est pas grave si une partie des utilisateurs tombent sur des bugs, corrigés au fur et à mesure qu’ils sont trouvés. Pour un système de vote, c’est tout simplement impardonnable. Et c’est ce manque de rigueur qui, visiblement, à permis au fameux bug d’apparaître.

Le bug, donc. Il se situe dans la partie du programme permettant de désélectionner un candidat. Lors des élections précédentes, cela ne pouvait se faire que en appuyant sur le bouton “Annuler”. Nouveauté de 2014 : on peut le faire en ré-appuyant sur la case à côté du nom du candidat. Le ministère de l’intérieur nous explique les circonstances dans lesquelles le problème se produit :

  • un électeur sélectionne une liste ;
  • il sélectionne un ou plusieurs candidat(s) ;
  • ensuite il désélectionne ce ou ces candidat(s) ;
  • il revient au menu des listes ;
  • il choisit une autre liste de parti;
  • il poursuit le processus de vote normal.

L’écran incriminé (image prise dans le manuel d’utilisation fourni avec les sources)

Le code gérant cet écran se trouve dans un fichier appelé mod_cand.c. On y retrouve une fonction qui, chaque fois qu’on appuie avec le stylo optique sur l’écran, va vérifier l’action à effectuer. Je mets le code ci-dessous pour ceux que ça intéresse.

static int Cand_Check_Selection(int _iX, int _iY)

{

  int i, x1, x2, y1, y2;

  



  if(_iY > 452) /* At the bottom (Validate/Cancel or

                   go back tho the lists) */

  {

    if(giNbCandidSel != 0) /* Almost one candidate selected */

    {

#ifdef EL2014

      if((_iX > 10) && (_iX < 310)) /* Cancel */ { giNbCandidSel = 0; swpas = 0; swnoir = 0; swconf = 0; Cancel_Selection(giCurrentScr,giCurrentList,1); return(0); } if((_iX > 330) && (_iX < 630)) /* Validation */ { if((gshScrutin[giCurrentScr].nom_s == '4') && (Choix_RLg == 'F')) swVLR = 0; ++giCurrentScr; ptfunct = Scrut_Loop; return(1); } #else if((_iX > 10) && (_iX < 310)) /* Validation */ { ++giCurrentScr; ptfunct = Scrut_Loop; return(1); } if((_iX > 330) && (_iX < 630)) /* Cancel */ { giNbCandidSel = 0; swpas = 0; swnoir = 0; swconf = 0; Cancel_Selection(giCurrentScr,giCurrentList,1); return(0); } #endif return(0); } else /* No candidate selected -> go back to the list selection */

    {

      if((_iX > 110) && (_iX < 530))

      {

#ifdef EL2014

        ptfunct = les_listes;

#else        

        ptfunct = List_Display;

#endif      

        return(1);

      }

      else /* Not managed */

        return(0);

    }

  }

  else /* Candidate's areas */

  {

    for(i=0;i<=giCurrentCandid;i++) /* Who has been selected ? (LightPen) */ { x1 = gaiPosc[i][0]; x2 = gaiPosc[i][1]; y1 = gaiPosc[i][2]; y2 = gaiPosc[i][3]; if(i) /* A specific candidate (not the "head of the list") */ { x1 += giXCapture; x2 -= giYCapture; y1 += giYCapture; y2 -= giYCapture; } if((_iX>=x1) && (_iX<x2)) { if((_iY>=y1) && (_iY<=y2)) /* This area */ { if(arcMemoCandid[giCurrentScr][giCurrentList][i] == 0) /* Unselected -> Selected */

          {

            ++giNbCandidSel; /* cfr iSWDeselectC */

            Cand_Select(giCurrentScr,giCurrentList,i);

            Cand_Update(i);

            Cand_Circle(i);

            Cand_Switch(i);

            return(0);

          }

#ifdef EL2014         

          else /* Selected -> Unselected */

          {

            --giNbCandidSel; /* cfr iSWDeselectC */

            if (giNbCandidSel == 0) {

                swpas = swnoir = swconf = 0;

            }

            Cand_Unselect(giCurrentScr,giCurrentList,i);

            Cand_Update(i);

            Cand_Circle(i);

            Cand_Switch(i);

            return(0);

          }

#endif        

        }

      }

    }

    return(0);

  }

}

En français, ça donne :

  • Si on appuie en bas de l’écran et que l’on a déjà sélectionné au moins un candidat : soit on est sur le bouton “Annuler” et on exécute la fonction “Cancel_Selection”, soit on est sur le bouton “Valider” et on passe au scrutin suivant.
  • Si on appuie en bas de l’écran et qu’aucun candidat n’est sélectionné, on revient à l’écran de choix de la liste pour laquelle on veut voter.
  • Si on appuie sur un des candidats et qu’il n’est pas encore sélectionné : on exécute la fonction “Cand_Select” et on ajoute “1” au compteur de nombre de candidats sélectionnés.
  • Si on appuie sur un des candidats et qu’il est déjà sélectionné : on exécute la fonction “Cand_Unselect” et on retire “1” au même compteur.

C’est donc cette fonction Cand_Unselect qui a été rajoutée pour l’élection de 2014. Ce n’est pas une fonction très compliquée, elle ne fait que quelques lignes :

void Cand_Unselect(int _x, int _y, int _z)

{

  arcMemoCandid[_x][_y][_z] = 0;

  //280613 arcMemoList[_x][_y] = 0;

  //280613 arcMemoScrutin[_x] = 0;

}

La fonction reçoit donc trois chiffres, qui correspondent au scrutin, à la liste, et au candidat à désélectionner. Dans le tableau arcMemoCandid, qui contient pour chaque candidat de chaque liste de chaque scrutin un 0 si le candidat est sélectionné, un 1 sinon, on met la case choisie à 0. C’est simple et direct. Les deux lignes suivantes sont commentées, et ne sont donc pas exécutées : elles désélectionnaient également la liste et le scrutin.

Et c’est là que se situe le problème. Si l’utilisateur désélectionne tous les candidats de la liste, le compteur sera bien mis à zéro. Lorsque l’utilisateur appuie ensuite sur le bas de l’écran pour quitter la liste des candidats, il sera ramené à l’écran des listes sans appeler la fonction Cancel_Selection, et la liste ne sera jamais marquée comme non sélectionnée. Lorsque l’utilisateur sélectionne une autre liste et valide son vote, il y a donc deux listes indiquées comme sélectionnées dans la mémoire. Le bulletin de vote est donc nul.

2. Un vote démocratique et sécurisé, c’est quoi ?

Pour qu’une élection soit réellement démocratique, il faut que chaque citoyen puisse être assuré que son vote est comptabilisé, et que son vote est secret. Pour que ces conditions soient respectées, il importe que, à chaque étape du scrutin, des observateurs puissent contrôler l’absence de fraude. Il y a différents points clés où des erreurs (ou des fraudes) peuvent mettre en doute le résultat des élections :

  • Dans l’isoloir : si le bulletin de vote n’est pas clair ou favorise un ou plusieurs candidats (c’est le cas par exemple aux USA, où les candidats des partis autre que Démocrates et Républicains n’apparaissent pas sur le bulletin de vote, dans beaucoup d’Etats : leur nom doit être rajouté à la main par l’électeur si il veut leur donner une voix).
  • Dans l’urne : si quelqu’un rajoute ou retire des bulletins de l’urne (ou si les données sont modifiées, dans le cas électronique)
  • Lors du comptage au bureau de vote : en ne comptant pas des voix valides, ou en les comptabilisant pour le mauvais partis.
  • Lors du décompte final : en additionnant mal les votes des différents bureaux, pour arriver à un mauvais résultat.

Plus les problèmes apparaissent tard dans le processus, plus il est facile d’y remédier. Si le décompte final est mauvais, on peut refaire les additions. Si le décompte du bureau de vote est mauvais, on peut recompter. Les choses deviennent plus difficiles si ce qui se trouve dans l’urne n’est pas valide. Dans le meilleur des cas, on peut alors identifier les bulletins problématiques et les annuler (c’est ce qui s’est produit avec le bug de 2014). Dans le pire des cas, il faut revoter.

3. Vote électronique, stop ou encore ?

En se basant sur les critères ainsi définis, le vote électronique tel qu’il se passe dans 17 des communes bruxelloises n’est pas démocratique. Il a en effet une lacune énorme et impardonnable : il n’y a aucun moyen pour l’électeur de vérifier que le contenu de la carte magnétique correspond bien à son vote. Il ne peut pas savoir si la machine n’a pas été trafiquée, ou si les données n’ont pas été corrompues entre le moment où il a pu voir son vote sur la machine et le moment où la carte a été éjectée. Les observateurs non plus n’ont aucun moyen de vérifier, pendant le dépouillement, que les bulletins sont correctement comptabilisés. Même si le code était bon (ce qui n’est pas le cas), même si en pratique tout se déroulait parfaitement comme prévu, ce système resterait par nature non-démocratique.

Faut-il alors jeter le vote électronique à la poubelle ? Non ! La solution est simple, et elle est déjà appliquée dans les communes qui ont choisi de mettre en place un système plus moderne. Il s’agit de ne pas stocker le vote sur une carte magnétique, mais sur un papier, sur lequel sera indiqué le vote en toutes lettres de manière lisible, accompagné d’un code barre permettant à un ordinateur de le comptabiliser automatiquement. L’électeur peut vérifier, au moment de son vote, qu’il glisse bien dans l’urne un papier portant le(s) nom(s) qu’il a choisi. Les observateurs peuvent vérifier facilement que le système qui scanne les votes donne bien la même information que celle inscrite en toutes lettres sur le papier. Et en cas de doute ou de problème, on peut toujours tout recompter à la main.

On peut ainsi combiner les avantages du vote électronique (rapidité de comptage, clarté du bulletin de vote…), et la sécurité du vote papier. Il serait dommage que, après le bug de 2014, Bruxelles décide de revenir entièrement en arrière, et retourne au vote papier. Mais si le choix est le vote papier ou le système actuel, c’est le vote papier qui doit primer : le système actuel n’est pas sécurisé, il n’est pas démocratique. Le meilleur choix serait évidemment de passer à un système de vote électronique moderne. Je ne l’ai jamais essayé, mais le système utilisé à Woluwe-Saint-Lambert et Watermael semble un bon candidat…

Si ce post vous fait réagir, vous pouvez contacter l'auteur (moi) par mail: adrien@adfoucart.be