Fully implement the vote #8
2 changed files with 186 additions and 94 deletions
|
@ -30,7 +30,7 @@ class Card:
|
||||||
def check_pin(self, hashed_pin: bytes) -> bool:
|
def check_pin(self, hashed_pin: bytes) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if the card is valid by comparing hashed PIN.
|
Check if the card is valid by comparing hashed PIN.
|
||||||
:param pin_hash: The PIN to check.
|
:param hashed_pin: The PIN to check.
|
||||||
:return: True if the received hash PIN matches the stored hashed PIN, False otherwise.
|
:return: True if the received hash PIN matches the stored hashed PIN, False otherwise.
|
||||||
"""
|
"""
|
||||||
return self.hashed_pin == hashed_pin
|
return self.hashed_pin == hashed_pin
|
||||||
|
|
|
@ -1,81 +1,104 @@
|
||||||
|
import datetime
|
||||||
import os
|
import os
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from cryptography.exceptions import InvalidSignature
|
from cryptography.exceptions import InvalidSignature
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
from cryptography.hazmat.primitives.asymmetric import padding
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
|
||||||
from .Card import Card
|
from .Card import Card
|
||||||
|
from .models.Elector import Elector
|
||||||
|
from .models.Proof import Proof
|
||||||
|
|
||||||
|
|
||||||
class Machine:
|
class Machine:
|
||||||
"""
|
"""
|
||||||
|
Represent a machine used to vote
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, emerging_list: list[Proof]):
|
||||||
# First DB: list of people authorized to vote on this machine.
|
# First model: list of people authorized to vote on this machine.
|
||||||
# Public (que name et procuration pas empreinte) ?
|
# Public (que name et procuration pas empreinte) ?
|
||||||
|
|
||||||
# fonction insérer élément
|
self.private_key = rsa.generate_private_key(
|
||||||
# fonction check
|
public_exponent=65537,
|
||||||
self.emerging_list = {}
|
key_size=2048,
|
||||||
#[
|
)
|
||||||
# {
|
self._public_key = self.private_key.public_key()
|
||||||
# "name": "Bob",
|
|
||||||
# "personne": (pub_key_bob, Hash(empreinte_bob)),
|
|
||||||
# "procuration": None,
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "name": "Alice",
|
|
||||||
# "personne": (pub_key_alice, Hash(empreinte_alice)),
|
|
||||||
# "procuration": (pub_key_Eve, Hash(empreinte_eve)), # Eve peut voter pour Alice.
|
|
||||||
# }
|
|
||||||
# {
|
|
||||||
# "name": "Eve",
|
|
||||||
# "personne": (pub_key_eve, Hash(empreinte_eve)),
|
|
||||||
# "procuration": None
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# Second DB: list of people who voted.
|
# List of electors who can vote on this machine.
|
||||||
# - public
|
self.emerging_list: list[Elector] = emerging_list
|
||||||
self.signing_list = {}
|
|
||||||
# [
|
|
||||||
# ("pub_key_Alice", "pub_key_Eve", "date", signature("message")),
|
|
||||||
# ("pub_key_Bob", "pub_key_Bob", "date", signature("message")),
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# Third DB: votes (shuffle when vote is inserted).
|
# List of people who voted.
|
||||||
# - private
|
self.proof_list: list[Proof] = []
|
||||||
self._vote_list = []
|
|
||||||
|
# Private votes (shuffle when a vote is inserted).
|
||||||
# ["A", "B", "C"]
|
# ["A", "B", "C"]
|
||||||
|
# NOTE(Faraphel): rename candidates ?
|
||||||
|
self._vote_list: list[str] = []
|
||||||
|
|
||||||
# If details during the simulation.
|
self.end_of_election: bool = False
|
||||||
#def send_challenge(self) -> bytes:
|
|
||||||
# pass
|
|
||||||
#def check_challenge(self, hash_rand: bytes) -> bool:
|
|
||||||
# pass
|
|
||||||
|
|
||||||
def search_pubkey(self, pubkey: bytes) -> list[tuple[bytes, bytes]]:
|
def search_elector(self, pubkey: bytes) -> Optional[Elector]:
|
||||||
"""
|
"""
|
||||||
Search pubkey in emerging list.
|
Search public key of elector in the emerging list.
|
||||||
:param pubkey: Public key to search in the emerging list.
|
:param pubkey: Public key of elector to search in the emerging list.
|
||||||
:return: All entries.
|
:return: The first elector who matches or None.
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
next(filter(
|
return next(filter(
|
||||||
lambda data: data["personne"][0] == and
|
lambda elector: elector.public_key_elector == pubkey,
|
||||||
self.emerging_list
|
self.emerging_list
|
||||||
))
|
))
|
||||||
|
except StopIteration:
|
||||||
|
return None
|
||||||
|
|
||||||
def check_fingerprint(self, empreinte: bytes) -> bool:
|
def search_proxy_vote(self, pubkey: bytes) -> Optional[Elector]:
|
||||||
"""
|
"""
|
||||||
|
Search the elector with the public key registered as the proxy vote.
|
||||||
:return:
|
:param pubkey: Public key of the mandataire who can proxy vote.
|
||||||
|
:return: The elector with the proxy vote pubkey registered.
|
||||||
"""
|
"""
|
||||||
return True
|
try:
|
||||||
|
return next(filter(
|
||||||
|
lambda elector: elector.public_key_mandataire == pubkey,
|
||||||
|
self.emerging_list
|
||||||
|
))
|
||||||
|
except StopIteration:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def search_proof(self, pubkey: bytes) -> Optional[Proof]:
|
||||||
|
"""
|
||||||
|
Search pubkey in the signature list
|
||||||
|
:param pubkey: Public to search in the signature list.
|
||||||
|
:return: The first elector with pubkey which voted or None.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return next(filter(
|
||||||
|
lambda proof: proof.public_key_elector == pubkey,
|
||||||
|
self.proof_list
|
||||||
|
))
|
||||||
|
except StopIteration:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def check_fingerprint(self, elector: Elector, fingerprint: str) -> bool:
|
||||||
|
"""
|
||||||
|
Hash the fingerprint and check if it authenticates the elector.
|
||||||
|
:param elector: Elector to authenticate.
|
||||||
|
:param fingerprint: Fingerprint to check.
|
||||||
|
:return: True if the fingerprint authenticates the mandataire or the voter else False.
|
||||||
|
"""
|
||||||
|
# Is the fingerprint matches?
|
||||||
|
digest = hashes.Hash(hashes.SHA256())
|
||||||
|
digest.update(bytes(fingerprint, 'utf-8'))
|
||||||
|
hashed_fingerprint = digest.finalize()
|
||||||
|
|
||||||
|
if (hashed_fingerprint == elector.hashed_fingerprint_mandataire or
|
||||||
|
hashed_fingerprint == elector.hashed_fingerprint_elector):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def check_card(self, card: Card, pin: int) -> bool:
|
def check_card(self, card: Card, pin: int) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -84,7 +107,7 @@ class Machine:
|
||||||
:param pin:
|
:param pin:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
# Public key.
|
# Public key transmitted by the card.
|
||||||
public_key = card.public_key()
|
public_key = card.public_key()
|
||||||
|
|
||||||
# Challenge to verify public key.
|
# Challenge to verify public key.
|
||||||
|
@ -102,59 +125,134 @@ class Machine:
|
||||||
hashes.SHA256()
|
hashes.SHA256()
|
||||||
)
|
)
|
||||||
except InvalidSignature:
|
except InvalidSignature:
|
||||||
|
print("Carte invalide : échec du challenge.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Pin
|
# Pin verification.
|
||||||
# CHECK(biloute02,faraphel): c’est bon comme ça le digest ?
|
|
||||||
digest = hashes.Hash(hashes.SHA256())
|
digest = hashes.Hash(hashes.SHA256())
|
||||||
digest.update(pin.as_bytes())
|
digest.update(pin.to_bytes())
|
||||||
hashed_pin = digest.finalize()
|
hashed_pin = digest.finalize()
|
||||||
if not card.check_pin(hashed_pin):
|
if not card.check_pin(hashed_pin):
|
||||||
|
print("Mot de passe de la carte refusé.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Card has been verified.
|
||||||
|
return True
|
||||||
|
|
||||||
|
def authenticate(self, card: Card, pin: int, fingerprint: str) -> bool:
|
||||||
|
"""
|
||||||
|
Authenticate a person with its card and its fingerprint.
|
||||||
|
:param card: Elector card.
|
||||||
|
:param pin: Elector card pin.
|
||||||
|
:param fingerprint: Elector fingerprint associated with its card public key.
|
||||||
|
:return: True if the elector is registered in the emerging list, else False.
|
||||||
|
"""
|
||||||
|
# Check if election is not ended.
|
||||||
|
if self.end_of_election:
|
||||||
|
print("L’élection est terminée")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check if card is valid.
|
||||||
|
if not self.check_card(card, pin):
|
||||||
|
print("Carte de l’électeur invalide.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Is in emerging list?
|
# Is in emerging list?
|
||||||
|
elector = self.search_elector(card.public_key)
|
||||||
|
if elector is None:
|
||||||
|
elector = self.search_proxy_vote(card.public_key)
|
||||||
|
if elector is None:
|
||||||
|
print("L’électeur n’est pas inscrit dans la liste électorale.")
|
||||||
|
return False
|
||||||
|
print("L’électeur peut voter")
|
||||||
|
|
||||||
return True
|
# Is the fingerprint matching?
|
||||||
|
if not self.check_fingerprint(elector, fingerprint):
|
||||||
def authenticate(self, card: Card, pin: int, empreinte: bytes) -> bool:
|
print("Erreur de reconnaissance de l’empreinte digitale.")
|
||||||
# Check if card is v
|
|
||||||
if not self.check_card(card, pin):
|
|
||||||
print("")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
pubkey = card.public_key
|
# L’électeur est authentifié.
|
||||||
# Check pubkey
|
|
||||||
entries = self.search_pubkey(pubkey)
|
|
||||||
if not entry:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check fingerprint
|
|
||||||
if
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def vote(self, card: Card, vote: str) -> bool:
|
def vote(self, card: Card, vote: str) -> bool:
|
||||||
public_key = card.public_key
|
"""
|
||||||
|
Vote with the card (user must have been authenticated previously).
|
||||||
|
:param card: Elector card
|
||||||
|
:param vote: Elector vote or proxy vote
|
||||||
|
:return: True if the vote has been inserted else False.
|
||||||
|
"""
|
||||||
|
# Check if election is not ended.
|
||||||
|
if self.end_of_election:
|
||||||
|
print("L’élection est terminée")
|
||||||
|
return False
|
||||||
|
|
||||||
# Chercher si public_key in signing_list for person.
|
# Vérification si l’électeur peut voter pour lui.
|
||||||
personne = self.search_personne(public_key)
|
elector = self.search_elector(card.public_key)
|
||||||
if personne is not None:
|
if elector is None:
|
||||||
print("La personne peut voter pour elle-même")
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Chercher si public_key in signing_list for procuration.
|
# Vérification si l’électeur est un mandataire.
|
||||||
mandataire = self.search_procuration(public_key)
|
elector = self.search_proxy_vote(card.public_key)
|
||||||
if mandataire is not None:
|
if elector is None:
|
||||||
print("La personne a une procuration")
|
# Ne devrait pas arriver si l’utilisateur est authentifiée.
|
||||||
return True
|
print("L’électeur n’est pas inscrit dans la liste électorale.")
|
||||||
|
return False
|
||||||
|
|
||||||
print("La personne peut plus voter")
|
else:
|
||||||
return False
|
# Vérification si le mandataire a déjà voté pour l’électeur.
|
||||||
|
proof = self.search_proof(elector.public_key)
|
||||||
|
if proof is not None:
|
||||||
|
print("L’électeur a déjà voté par procuration pour ...")
|
||||||
|
return False
|
||||||
|
|
||||||
# vote = get_vote()
|
else:
|
||||||
# generate_signature(vote, card)
|
# Vérification si l’électeur a déjà voté pour lui.
|
||||||
# add_vote_to_urn(vote)
|
proof = self.search_proof(elector.public_key)
|
||||||
# print("Vote comptabilisé!")
|
if proof is not None:
|
||||||
# return True
|
print("L’électeur ... a déjà voté pour lui")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Create proof of voting.
|
||||||
|
date = datetime.datetime.now()
|
||||||
|
data = bytes(str(date), 'utf-8') + elector.public_key_elector + elector.public_key_mandataire
|
||||||
|
proof_signature = self.private_key.sign(
|
||||||
|
data,
|
||||||
|
padding.PSS(
|
||||||
|
mgf=padding.MGF1(hashes.SHA256()),
|
||||||
|
salt_length=padding.PSS.MAX_LENGTH
|
||||||
|
),
|
||||||
|
hashes.SHA256()
|
||||||
|
)
|
||||||
|
proof = Proof(date,
|
||||||
|
elector.public_key_elector,
|
||||||
|
elector.public_key_mandataire,
|
||||||
|
proof_signature)
|
||||||
|
|
||||||
|
# Append proof and vote to the databases.
|
||||||
|
self.proof_list.append(proof)
|
||||||
|
# TODO(biloute02): shuffle when vote is inserted.
|
||||||
|
self._vote_list.append(vote)
|
||||||
|
|
||||||
|
print("Vote comptabilisé!")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def cloture_du_vote(self) -> tuple[list[str], bytes]:
|
||||||
|
"""
|
||||||
|
End of the elction, publishes the voting results.
|
||||||
|
:return: The list of votes and its signature by the voting machine.
|
||||||
|
"""
|
||||||
|
self.end_of_election = True
|
||||||
|
|
||||||
|
# TODO(biloute02): Binariser la base de vote pour la signature.
|
||||||
|
data = ...
|
||||||
|
vote_list_signature = self.private_key.sign(
|
||||||
|
data,
|
||||||
|
padding.PSS(
|
||||||
|
mgf=padding.MGF1(hashes.SHA256()),
|
||||||
|
salt_length=padding.PSS.MAX_LENGTH
|
||||||
|
),
|
||||||
|
hashes.SHA256()
|
||||||
|
)
|
||||||
|
return self._vote_list, vote_list_signature
|
||||||
|
|
||||||
def print_signing_list(self) -> None:
|
def print_signing_list(self) -> None:
|
||||||
...
|
...
|
||||||
|
@ -162,9 +260,3 @@ class Machine:
|
||||||
def print_emerging_list(self) -> None:
|
def print_emerging_list(self) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
def cloture_du_vote(self) -> tuple[list[vote], signature]:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
machine = Machine()
|
|
||||||
machine.emerging_list
|
|
Loading…
Reference in a new issue