Fully implement the vote #8

Merged
faraphel merged 10 commits from base into main 2024-07-05 11:09:12 +02:00
2 changed files with 186 additions and 94 deletions
Showing only changes of commit e3015a5e81 - Show all commits

View file

@ -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

View file

@ -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): cest 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 nest 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 lempreinte 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 lutilisateur est authentifiée.
return True print("Lélecteur nest 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