From cc243e516334dd4e7a52e97017e446705258631c Mon Sep 17 00:00:00 2001 From: biloute02 Date: Thu, 4 Jul 2024 19:31:20 +0200 Subject: [PATCH] Function to end election. Convert type of pubkey from bytes to RSAPublicKey. --- source/Card.py | 15 +++---- source/Machine.py | 90 +++++++++++++++++++++++++++------------- source/models/Elector.py | 36 ++++++++++++---- source/models/Proof.py | 21 ++++++---- tests/exemple1.py | 53 ++++++++++++++++++++--- 5 files changed, 157 insertions(+), 58 deletions(-) diff --git a/source/Card.py b/source/Card.py index 2687892..a261724 100644 --- a/source/Card.py +++ b/source/Card.py @@ -1,6 +1,7 @@ import hashlib from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey from cryptography.hazmat.primitives.asymmetric import padding @@ -9,24 +10,20 @@ class Card: Represent the card of an elector. """ - def __init__(self, name: str, pin: int): + def __init__(self, name: str, pin: str): self.name = name - self.private_key = rsa.generate_private_key( + self._private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, ) - self._public_key = self.private_key.public_key() + self.public_key: RSAPublicKey = self._private_key.public_key() digest = hashes.Hash(hashes.SHA256()) - digest.update(pin.to_bytes()) + digest.update(bytes(pin, 'utf-8')) self.hashed_pin = digest.finalize() # self.activee = False - @property - def public_key(self): - return self._public_key - def check_pin(self, hashed_pin: bytes) -> bool: """ Check if the card is valid by comparing hashed PIN. @@ -42,7 +39,7 @@ class Card: :param data: The challeng sent by the terminal to sign. :return: The signed challenge. """ - signature = self.private_key.sign( + signature = self._private_key.sign( data, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), diff --git a/source/Machine.py b/source/Machine.py index 6841eb7..32690e2 100644 --- a/source/Machine.py +++ b/source/Machine.py @@ -1,10 +1,13 @@ import datetime import os +import random from typing import Optional from cryptography.exceptions import InvalidSignature from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey, RSAPrivateKey from cryptography.hazmat.primitives.asymmetric import padding from .Card import Card @@ -17,15 +20,15 @@ class Machine: Represent a machine used to vote """ - def __init__(self, emerging_list: list[Proof]): + def __init__(self, emerging_list: list[Elector]): # First model: list of people authorized to vote on this machine. # Public (que name et procuration pas empreinte) ? - self.private_key = rsa.generate_private_key( + self._private_key: RSAPrivateKey = rsa.generate_private_key( public_exponent=65537, key_size=2048, ) - self._public_key = self.private_key.public_key() + self.public_key: RSAPublicKey = self._private_key.public_key() # List of electors who can vote on this machine. self.emerging_list: list[Elector] = emerging_list @@ -40,7 +43,7 @@ class Machine: self.end_of_election: bool = False - def search_elector(self, pubkey: bytes) -> Optional[Elector]: + def search_elector(self, pubkey: RSAPublicKey) -> Optional[Elector]: """ Search public key of elector in the emerging list. :param pubkey: Public key of elector to search in the emerging list. @@ -54,7 +57,7 @@ class Machine: except StopIteration: return None - def search_proxy_vote(self, pubkey: bytes) -> Optional[Elector]: + def search_proxy_vote(self, pubkey: RSAPublicKey) -> Optional[Elector]: """ Search the elector with the public key registered as the proxy vote. :param pubkey: Public key of the mandataire who can proxy vote. @@ -68,7 +71,7 @@ class Machine: except StopIteration: return None - def search_proof(self, pubkey: bytes) -> Optional[Proof]: + def search_proof(self, pubkey: RSAPublicKey) -> Optional[Proof]: """ Search pubkey in the signature list :param pubkey: Public to search in the signature list. @@ -100,7 +103,7 @@ class Machine: else: return False - def check_card(self, card: Card, pin: int) -> bool: + def check_card(self, card: Card, pin: str) -> bool: """ Check if the card is valid. :param card: @@ -108,7 +111,7 @@ class Machine: :return: """ # Public key transmitted by the card. - public_key = card.public_key() + public_key = card.public_key # Challenge to verify public key. challenge = os.urandom(128) @@ -130,7 +133,7 @@ class Machine: # Pin verification. digest = hashes.Hash(hashes.SHA256()) - digest.update(pin.to_bytes()) + digest.update(bytes(pin, 'utf-8')) hashed_pin = digest.finalize() if not card.check_pin(hashed_pin): print("Mot de passe de la carte refusé.") @@ -139,7 +142,7 @@ class Machine: # Card has been verified. return True - def authenticate(self, card: Card, pin: int, fingerprint: str) -> bool: + def authenticate(self, card: Card, pin: str, fingerprint: str) -> bool: """ Authenticate a person with its card and its fingerprint. :param card: Elector card. @@ -149,7 +152,7 @@ class Machine: """ # Check if election is not ended. if self.end_of_election: - print("L’élection est terminée") + print("L’élection est terminée.") return False # Check if card is valid. @@ -164,14 +167,16 @@ class Machine: if elector is None: print("L’électeur n’est pas inscrit dans la liste électorale.") return False - print("L’électeur peut voter") + print(f"L’électeur {elector.name} vote par procuration.") + print(f"L’électeur {elector.name} peut voter.") # Is the fingerprint matching? if not self.check_fingerprint(elector, fingerprint): - print("Erreur de reconnaissance de l’empreinte digitale.") + print(f"Erreur de reconnaissance de l’empreinte digitale de {elector.name}.") return False # L’électeur est authentifié. + print(f"Électeur {elector.name} authentifié") return True def vote(self, card: Card, vote: str) -> bool: @@ -183,9 +188,11 @@ class Machine: """ # Check if election is not ended. if self.end_of_election: - print("L’élection est terminée") + print("L’élection est terminée.") return False + # Check if elector can vote and has not vot. + # Vérification si l’électeur peut voter pour lui. elector = self.search_elector(card.public_key) if elector is None: @@ -199,23 +206,39 @@ class Machine: else: # Vérification si le mandataire a déjà voté pour l’électeur. - proof = self.search_proof(elector.public_key) + proof = self.search_proof(elector.public_key_elector) if proof is not None: - print("L’électeur a déjà voté par procuration pour ...") + print(f"L’électeur {elector.name} a déjà voté.") return False + # C’est ok + print(f"L’électeur {elector.name} vote par procuration.") + else: # Vérification si l’électeur a déjà voté pour lui. - proof = self.search_proof(elector.public_key) + proof = self.search_proof(elector.public_key_elector) if proof is not None: - print("L’électeur ... a déjà voté pour lui") + print(f"L’électeur {elector.name} a déjà voté.") return False - # Create proof of voting. + # C’est ok + print(f"L’électeur {elector.name} peut voter.") + + # Create proof of voting: date + pubkey elector + pubkey mandataire. 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, + pem_votant: bytes = elector.public_key_elector.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.PKCS1, + ) + pem_mandataire: bytes = elector.public_key_mandataire.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.PKCS1, + ) + concatenation: bytes = bytes(str(date), 'utf-8') + pem_votant + pem_mandataire + + # Create proof signature. + proof_signature = self._private_key.sign( + concatenation, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH @@ -226,25 +249,26 @@ class Machine: elector.public_key_elector, elector.public_key_mandataire, proof_signature) + print(f"Preuve de vote de {elector.name} ajoutée.") # Append proof and vote to the databases. self.proof_list.append(proof) - # TODO(biloute02): shuffle when vote is inserted. self._vote_list.append(vote) + random.shuffle(self._vote_list) - print("Vote comptabilisé!") + print(f"Vote de {elector.name} comptabilisé!") return True def cloture_du_vote(self) -> tuple[list[str], bytes]: """ - End of the elction, publishes the voting results. + End of the election, publishes the voting results. :return: The list of votes and its signature by the voting machine. """ + # Election is closed self.end_of_election = True - # TODO(biloute02): Binariser la base de vote pour la signature. - data = ... - vote_list_signature = self.private_key.sign( + data = bytes("".join(self._vote_list), 'utf-8') + vote_list_signature = self._private_key.sign( data, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), @@ -260,3 +284,13 @@ class Machine: def print_emerging_list(self) -> None: ... + # def to_bytes(public_key_votant, public): + # pem_votant: bytes = self.public_key_votant.public_bytes( + # encoding=serialization.Encoding.PEM, + # format=serialization.PublicFormat.PKCS1, + # ) + # pem_mandataire: bytes = self.public_key_mandataire.public_bytes( + # encoding=serialization.Encoding.PEM, + # format=serialization.PublicFormat.PKCS1, + # ) + # return bytes(str(self.date), 'utf-8') + pem_votant + pem_mandataire diff --git a/source/models/Elector.py b/source/models/Elector.py index eaa412e..0fec163 100644 --- a/source/models/Elector.py +++ b/source/models/Elector.py @@ -1,13 +1,14 @@ -from dataclasses import dataclass +from typing import Optional +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey -@dataclass class Elector: # [ # { # "name": "Bob", # "votant": (pub_key_bob, Hash(empreinte_bob)), - # "mandataire": None, + # "mandataire": (b"", b"") # }, # { # "name": "Alice", @@ -16,11 +17,30 @@ class Elector: # } # ] - name: str - public_key_elector: bytes - hashed_fingerprint_elector: bytes - public_key_mandataire: bytes = b"" - hashed_fingerprint_mandataire: bytes = b"" + def __init__(self, + name: str, + public_key_elector: RSAPublicKey, + fingerprint_elector: str, + public_key_mandataire: Optional[RSAPublicKey] = None, + fingerprint_mandataire: Optional[str] = None): + self.name = name + self.public_key_elector = public_key_elector + + if public_key_mandataire is None: + self.public_key_mandataire = public_key_elector + else: + self.public_key_mandataire = public_key_mandataire + + digest = hashes.Hash(hashes.SHA256()) + digest.update(bytes(fingerprint_elector, 'utf-8')) + self.hashed_fingerprint_elector: bytes = digest.finalize() + + if fingerprint_mandataire is None: + self.hashed_fingerprint_mandataire = self.hashed_fingerprint_elector + else: + digest = hashes.Hash(hashes.SHA256()) + digest.update(bytes(fingerprint_mandataire, 'utf-8')) + self.hashed_fingerprint_mandataire: bytes = digest.finalize() def set_mandataire(self, public_key: bytes, hashed_fingerprint: bytes): self.public_key_mandataire = public_key diff --git a/source/models/Proof.py b/source/models/Proof.py index 38699ae..16a4772 100644 --- a/source/models/Proof.py +++ b/source/models/Proof.py @@ -1,19 +1,24 @@ from dataclasses import dataclass from datetime import datetime +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey -import struct @dataclass class Proof: date: datetime - public_key_votant: bytes - public_key_mandataire: bytes + public_key_votant: RSAPublicKey + public_key_mandataire: RSAPublicKey proof_signature: bytes - def to_bytes(self): - struct.pack("