Fully implement the vote #8

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

View file

@ -1,6 +1,7 @@
import hashlib import hashlib
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 rsa
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import padding
@ -9,24 +10,20 @@ class Card:
Represent the card of an elector. Represent the card of an elector.
""" """
def __init__(self, name: str, pin: int): def __init__(self, name: str, pin: str):
self.name = name self.name = name
self.private_key = rsa.generate_private_key( self._private_key = rsa.generate_private_key(
public_exponent=65537, public_exponent=65537,
key_size=2048, 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 = hashes.Hash(hashes.SHA256())
digest.update(pin.to_bytes()) digest.update(bytes(pin, 'utf-8'))
self.hashed_pin = digest.finalize() self.hashed_pin = digest.finalize()
# self.activee = False # self.activee = False
@property
def public_key(self):
return self._public_key
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.
@ -42,7 +39,7 @@ class Card:
:param data: The challeng sent by the terminal to sign. :param data: The challeng sent by the terminal to sign.
:return: The signed challenge. :return: The signed challenge.
""" """
signature = self.private_key.sign( signature = self._private_key.sign(
data, data,
padding.PSS( padding.PSS(
mgf=padding.MGF1(hashes.SHA256()), mgf=padding.MGF1(hashes.SHA256()),

View file

@ -1,10 +1,13 @@
import datetime import datetime
import os import os
import random
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 import serialization
from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey, RSAPrivateKey
from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import padding
from .Card import Card from .Card import Card
@ -17,15 +20,15 @@ class Machine:
Represent a machine used to vote 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. # 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) ?
self.private_key = rsa.generate_private_key( self._private_key: RSAPrivateKey = rsa.generate_private_key(
public_exponent=65537, public_exponent=65537,
key_size=2048, 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. # List of electors who can vote on this machine.
self.emerging_list: list[Elector] = emerging_list self.emerging_list: list[Elector] = emerging_list
@ -40,7 +43,7 @@ class Machine:
self.end_of_election: bool = False 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. Search public key of elector in the emerging list.
:param pubkey: Public key of elector to search in the emerging list. :param pubkey: Public key of elector to search in the emerging list.
@ -54,7 +57,7 @@ class Machine:
except StopIteration: except StopIteration:
return None 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. Search the elector with the public key registered as the proxy vote.
:param pubkey: Public key of the mandataire who can proxy vote. :param pubkey: Public key of the mandataire who can proxy vote.
@ -68,7 +71,7 @@ class Machine:
except StopIteration: except StopIteration:
return None return None
def search_proof(self, pubkey: bytes) -> Optional[Proof]: def search_proof(self, pubkey: RSAPublicKey) -> Optional[Proof]:
""" """
Search pubkey in the signature list Search pubkey in the signature list
:param pubkey: Public to search in the signature list. :param pubkey: Public to search in the signature list.
@ -100,7 +103,7 @@ class Machine:
else: else:
return False 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. Check if the card is valid.
:param card: :param card:
@ -108,7 +111,7 @@ class Machine:
:return: :return:
""" """
# Public key transmitted by the card. # 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.
challenge = os.urandom(128) challenge = os.urandom(128)
@ -130,7 +133,7 @@ class Machine:
# Pin verification. # Pin verification.
digest = hashes.Hash(hashes.SHA256()) digest = hashes.Hash(hashes.SHA256())
digest.update(pin.to_bytes()) digest.update(bytes(pin, 'utf-8'))
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é.") print("Mot de passe de la carte refusé.")
@ -139,7 +142,7 @@ class Machine:
# Card has been verified. # Card has been verified.
return True 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. Authenticate a person with its card and its fingerprint.
:param card: Elector card. :param card: Elector card.
@ -149,7 +152,7 @@ class Machine:
""" """
# Check if election is not ended. # Check if election is not ended.
if self.end_of_election: if self.end_of_election:
print("Lélection est terminée") print("Lélection est terminée.")
return False return False
# Check if card is valid. # Check if card is valid.
@ -164,14 +167,16 @@ class Machine:
if elector is None: if elector is None:
print("Lélecteur nest pas inscrit dans la liste électorale.") print("Lélecteur nest pas inscrit dans la liste électorale.")
return False 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? # Is the fingerprint matching?
if not self.check_fingerprint(elector, fingerprint): if not self.check_fingerprint(elector, fingerprint):
print("Erreur de reconnaissance de lempreinte digitale.") print(f"Erreur de reconnaissance de lempreinte digitale de {elector.name}.")
return False return False
# Lélecteur est authentifié. # Lélecteur est authentifié.
print(f"Électeur {elector.name} authentifié")
return True return True
def vote(self, card: Card, vote: str) -> bool: def vote(self, card: Card, vote: str) -> bool:
@ -183,9 +188,11 @@ class Machine:
""" """
# Check if election is not ended. # Check if election is not ended.
if self.end_of_election: if self.end_of_election:
print("Lélection est terminée") print("Lélection est terminée.")
return False return False
# Check if elector can vote and has not vot.
# Vérification si lélecteur peut voter pour lui. # Vérification si lélecteur peut voter pour lui.
elector = self.search_elector(card.public_key) elector = self.search_elector(card.public_key)
if elector is None: if elector is None:
@ -199,23 +206,39 @@ class Machine:
else: else:
# Vérification si le mandataire a déjà voté pour lélecteur. # 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: 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 return False
# Cest ok
print(f"Lélecteur {elector.name} vote par procuration.")
else: else:
# Vérification si lélecteur a déjà voté pour lui. # 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: 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 return False
# Create proof of voting. # Cest ok
print(f"Lélecteur {elector.name} peut voter.")
# Create proof of voting: date + pubkey elector + pubkey mandataire.
date = datetime.datetime.now() date = datetime.datetime.now()
data = bytes(str(date), 'utf-8') + elector.public_key_elector + elector.public_key_mandataire pem_votant: bytes = elector.public_key_elector.public_bytes(
proof_signature = self.private_key.sign( encoding=serialization.Encoding.PEM,
data, 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( padding.PSS(
mgf=padding.MGF1(hashes.SHA256()), mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH salt_length=padding.PSS.MAX_LENGTH
@ -226,25 +249,26 @@ class Machine:
elector.public_key_elector, elector.public_key_elector,
elector.public_key_mandataire, elector.public_key_mandataire,
proof_signature) proof_signature)
print(f"Preuve de vote de {elector.name} ajoutée.")
# Append proof and vote to the databases. # Append proof and vote to the databases.
self.proof_list.append(proof) self.proof_list.append(proof)
# TODO(biloute02): shuffle when vote is inserted.
self._vote_list.append(vote) self._vote_list.append(vote)
random.shuffle(self._vote_list)
print("Vote comptabilisé!") print(f"Vote de {elector.name} comptabilisé!")
return True return True
def cloture_du_vote(self) -> tuple[list[str], bytes]: 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. :return: The list of votes and its signature by the voting machine.
""" """
# Election is closed
self.end_of_election = True self.end_of_election = True
# TODO(biloute02): Binariser la base de vote pour la signature. data = bytes("".join(self._vote_list), 'utf-8')
data = ... vote_list_signature = self._private_key.sign(
vote_list_signature = self.private_key.sign(
data, data,
padding.PSS( padding.PSS(
mgf=padding.MGF1(hashes.SHA256()), mgf=padding.MGF1(hashes.SHA256()),
@ -260,3 +284,13 @@ class Machine:
def print_emerging_list(self) -> None: 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

View file

@ -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: class Elector:
# [ # [
# { # {
# "name": "Bob", # "name": "Bob",
# "votant": (pub_key_bob, Hash(empreinte_bob)), # "votant": (pub_key_bob, Hash(empreinte_bob)),
# "mandataire": None, # "mandataire": (b"", b"")
# }, # },
# { # {
# "name": "Alice", # "name": "Alice",
@ -16,11 +17,30 @@ class Elector:
# } # }
# ] # ]
name: str def __init__(self,
public_key_elector: bytes name: str,
hashed_fingerprint_elector: bytes public_key_elector: RSAPublicKey,
public_key_mandataire: bytes = b"" fingerprint_elector: str,
hashed_fingerprint_mandataire: bytes = b"" 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): def set_mandataire(self, public_key: bytes, hashed_fingerprint: bytes):
self.public_key_mandataire = public_key self.public_key_mandataire = public_key

View file

@ -1,19 +1,24 @@
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
import struct
@dataclass @dataclass
class Proof: class Proof:
date: datetime date: datetime
public_key_votant: bytes public_key_votant: RSAPublicKey
public_key_mandataire: bytes public_key_mandataire: RSAPublicKey
proof_signature: bytes proof_signature: bytes
def to_bytes(self): # def to_bytes(public_key_votant, public):
struct.pack("<d...", self.date.timestamp(), self.public_key_votant, self.public_key_mandataire) # pem_votant: bytes = self.public_key_votant.public_bytes(
# encoding=serialization.Encoding.PEM,
#bytes(str(ici), 'utf-8') # format=serialization.PublicFormat.PKCS1,
bytes(self.date.timestamp()) # )
# 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

View file

@ -4,15 +4,58 @@ from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import padding
from source.Card import Card from source.Card import Card
from source.Certificate import Certificate from source.models.Elector import Elector
#from source.Certificate import Certificate
from source.Machine import Machine from source.Machine import Machine
Alice_card = Card("Alice", 6060) # Création des cartes
Machine = Machine() alice_card = Card("Alice", "6060")
bob_card = Card("Bob", "0100")
eve_card = Card("Eve", "0008")
print(f"carte alice pubkey: {alice_card.public_key}")
print("Cartes délections créent")
input()
# Création de la liste électorale
# Eve peut voter pour Alice
alice_elector = Elector(name="Alice",
public_key_elector=alice_card.public_key,
fingerprint_elector="empreinteA",
public_key_mandataire=eve_card.public_key,
fingerprint_mandataire="empreinteE")
bob_elector = Elector("Bob", bob_card.public_key, "empreinteB")
eve_elector = Elector("Eve", eve_card.public_key, "empreinteE")
emerging_list = [alice_elector, bob_elector, eve_elector]
print(f"alice name: {alice_elector.name}")
print(f"alice pubkey: {alice_elector.public_key_elector}")
print(f"alice pubkey mandataire: {alice_elector.public_key_mandataire}")
print(f"alice empreinte: {alice_elector.hashed_fingerprint_elector}")
print(f"alice empreinte mandataire: {alice_elector.hashed_fingerprint_mandataire}")
print(f"liste démargement: {emerging_list}")
print("Liste électorale crée.")
input()
# Création de la machine
machine = Machine(emerging_list)
print(f"machine pubkey : {machine._public_key.public_bytes()}")
print("Machine pour voter crée")
input()
Machine.authenticate(Alice_card, 6060, "azerty")
# Authentification
# Vote
# Publication des résultats
#Machine.authenticate(Alice_card, 6060, "azerty")
Machine.vote(Alice_card) #Machine.vote(Alice_card)