rewrote the base for the program
This commit is contained in:
parent
c8445c75b9
commit
74c29a5c93
9 changed files with 370 additions and 36 deletions
|
@ -1,2 +1,53 @@
|
||||||
|
import hashlib
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
|
||||||
|
|
||||||
class Card:
|
class Card:
|
||||||
...
|
"""
|
||||||
|
Represent the card of an elector.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name: str, pin: int):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
self.private_key = rsa.generate_private_key(
|
||||||
|
public_exponent=65537,
|
||||||
|
key_size=2048,
|
||||||
|
)
|
||||||
|
self._public_key = self.private_key.public_key()
|
||||||
|
|
||||||
|
digest = hashes.Hash(hashes.SHA256())
|
||||||
|
digest.update(pin.to_bytes())
|
||||||
|
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.
|
||||||
|
:param pin_hash: The PIN to check.
|
||||||
|
:return: True if the received hash PIN matches the stored hashed PIN, False otherwise.
|
||||||
|
"""
|
||||||
|
return self.hashed_pin == hashed_pin
|
||||||
|
|
||||||
|
# NOTE(Faraphel): a PKI or a banlist / whitelist shall be checked with self.public_key
|
||||||
|
|
||||||
|
def reply_challenge(self, data: bytes = "encrypted data") -> bytes:
|
||||||
|
"""
|
||||||
|
:param data: The challeng sent by the terminal to sign.
|
||||||
|
:return: The signed challenge.
|
||||||
|
"""
|
||||||
|
signature = self.private_key.sign(
|
||||||
|
data,
|
||||||
|
padding.PSS(
|
||||||
|
mgf=padding.MGF1(hashes.SHA256()),
|
||||||
|
salt_length=padding.PSS.MAX_LENGTH
|
||||||
|
),
|
||||||
|
hashes.SHA256()
|
||||||
|
)
|
||||||
|
return signature
|
||||||
|
|
|
@ -1,6 +1,56 @@
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from cryptography import x509
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
|
||||||
|
|
||||||
class Certificate:
|
class Certificate:
|
||||||
"""
|
"""
|
||||||
A certificate.
|
A certificate.
|
||||||
Wrapper around cryptography.x509.Certificate.
|
Wrapper around cryptography.x509.Certificate.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
issuer: str,
|
||||||
|
issuer_private_key: bytes = None,
|
||||||
|
subject: str,
|
||||||
|
valid_start: datetime,
|
||||||
|
valid_end: datetime
|
||||||
|
):
|
||||||
|
# generate a private key for the certificate
|
||||||
|
self._private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
||||||
|
# get the public key from it
|
||||||
|
self.public_key = self._private_key.public_key()
|
||||||
|
|
||||||
|
# if the issuer private key is not specified, consider it a self-signed certificate
|
||||||
|
if issuer_private_key is None:
|
||||||
|
issuer_private_key = self._private_key
|
||||||
|
|
||||||
|
# create a builder for the certificate
|
||||||
|
builder = x509.CertificateBuilder(
|
||||||
|
issuer_name=x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, issuer)]),
|
||||||
|
subject_name=x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, subject)]),
|
||||||
|
serial_number=x509.random_serial_number(),
|
||||||
|
public_key=self.public_key,
|
||||||
|
not_valid_before=valid_start,
|
||||||
|
not_valid_after=valid_end,
|
||||||
|
)
|
||||||
|
|
||||||
|
# create the certificate by signing it
|
||||||
|
self._certificate = builder.sign(issuer_private_key, algorithm=hashes.SHA256())
|
||||||
|
|
||||||
|
def is_valid(self, date: datetime, issuer_public_key: bytes) -> bool:
|
||||||
|
return (
|
||||||
|
# check if the date is valid
|
||||||
|
self._certificate.not_valid_before <= date <= self._certificate.not_valid_after and
|
||||||
|
# check the signature of the certificate
|
||||||
|
self._certificate.verify_directly_issued_by()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Exemple d'utilisation
|
||||||
|
if __name__ == "__main__":
|
||||||
|
certificate = Certificate("Example Subject")
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
import os
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from cryptography.exceptions import InvalidSignature
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
|
||||||
|
from .Card import Card
|
||||||
|
|
||||||
|
|
||||||
class Machine:
|
class Machine:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -6,48 +16,146 @@ class Machine:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# First DB: list of people authorized to vote on this machine.
|
# First DB: list of people authorized to vote on this machine.
|
||||||
# Public (que name et procuration pas empreinte) ?
|
# Public (que name et procuration pas empreinte) ?
|
||||||
self.emerging_list = [
|
|
||||||
{
|
# fonction insérer élément
|
||||||
"name": "Bob",
|
# fonction check
|
||||||
"personne": (pub_key_bob, Enc(empreinte_bob)),
|
self.emerging_list = {}
|
||||||
"procuration": "vide",
|
#[
|
||||||
},
|
# {
|
||||||
{
|
# "name": "Bob",
|
||||||
"name": "Alice",
|
# "personne": (pub_key_bob, Hash(empreinte_bob)),
|
||||||
"personne": (pub_key_alice, Enc(empreinte_alice)),
|
# "procuration": None,
|
||||||
"procuration": (pub_key_Eve, Enc(empreinte_eve)), # Eve peut voter pour Alice.
|
# },
|
||||||
}
|
# {
|
||||||
]
|
# "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.
|
# Second DB: list of people who voted.
|
||||||
# - public
|
# - public
|
||||||
self.signing_list = [
|
self.signing_list = {}
|
||||||
("pub_key_Alice", "pub_key_Eve", "date", signature("message")),
|
# [
|
||||||
("pub_key_Bob", "pub_key_Bob", "date", signature("message")),
|
# ("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).
|
# Third DB: votes (shuffle when vote is inserted).
|
||||||
# - private
|
# - private
|
||||||
self._vote_list = ["A", "B", "C"]
|
self._vote_list = []
|
||||||
|
# ["A", "B", "C"]
|
||||||
|
|
||||||
def authenticate(self):
|
# If details during the simulation.
|
||||||
pass
|
#def send_challenge(self) -> bytes:
|
||||||
|
# pass
|
||||||
|
#def check_challenge(self, hash_rand: bytes) -> bool:
|
||||||
|
# pass
|
||||||
|
|
||||||
def vote(self, card: Card) -> bool:
|
def search_pubkey(self, pubkey: bytes) -> list[tuple[bytes, bytes]]:
|
||||||
if not authenticate(card):
|
"""
|
||||||
print("la carte peut pas voter dans cette machine")
|
Search pubkey in emerging list.
|
||||||
return False
|
:param pubkey: Public key to search in the emerging list.
|
||||||
|
:return: All entries.
|
||||||
|
"""
|
||||||
|
|
||||||
if card_in_siging_list(card):
|
next(filter(
|
||||||
print("déjà vôté")
|
lambda data: data["personne"][0] == and
|
||||||
return False
|
self.emerging_list
|
||||||
|
))
|
||||||
|
|
||||||
vote = get_vote()
|
def check_fingerprint(self, empreinte: bytes) -> bool:
|
||||||
generate_signature(vote, card)
|
"""
|
||||||
add_vote_to_urn(vote)
|
|
||||||
print("Vote comptabilisé!")
|
:return:
|
||||||
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def check_card(self, card: Card, pin: int) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the card is valid.
|
||||||
|
:param card:
|
||||||
|
:param pin:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# Public key.
|
||||||
|
public_key = card.public_key()
|
||||||
|
|
||||||
|
# Challenge to verify public key.
|
||||||
|
challenge = os.urandom(128)
|
||||||
|
signature = card.reply_challenge(challenge)
|
||||||
|
|
||||||
|
try:
|
||||||
|
public_key.verify(
|
||||||
|
signature,
|
||||||
|
challenge,
|
||||||
|
padding.PSS(
|
||||||
|
mgf=padding.MGF1(hashes.SHA256()),
|
||||||
|
salt_length=padding.PSS.MAX_LENGTH
|
||||||
|
),
|
||||||
|
hashes.SHA256()
|
||||||
|
)
|
||||||
|
except InvalidSignature:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Pin
|
||||||
|
# CHECK(biloute02,faraphel): c’est bon comme ça le digest ?
|
||||||
|
digest = hashes.Hash(hashes.SHA256())
|
||||||
|
digest.update(pin.as_bytes())
|
||||||
|
hashed_pin = digest.finalize()
|
||||||
|
if not card.check_pin(hashed_pin):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Is in emerging list?
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def authenticate(self, card: Card, pin: int, empreinte: bytes) -> bool:
|
||||||
|
# Check if card is v
|
||||||
|
if not self.check_card(card, pin):
|
||||||
|
print("")
|
||||||
|
return False
|
||||||
|
|
||||||
|
pubkey = card.public_key
|
||||||
|
# Check pubkey
|
||||||
|
entries = self.search_pubkey(pubkey)
|
||||||
|
if not entry:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check fingerprint
|
||||||
|
if
|
||||||
|
return True
|
||||||
|
|
||||||
|
def vote(self, card: Card, vote: str) -> bool:
|
||||||
|
public_key = card.public_key
|
||||||
|
|
||||||
|
# Chercher si public_key in signing_list for person.
|
||||||
|
personne = self.search_personne(public_key)
|
||||||
|
if personne is not None:
|
||||||
|
print("La personne peut voter pour elle-même")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Chercher si public_key in signing_list for procuration.
|
||||||
|
mandataire = self.search_procuration(public_key)
|
||||||
|
if mandataire is not None:
|
||||||
|
print("La personne a une procuration")
|
||||||
|
return True
|
||||||
|
|
||||||
|
print("La personne peut plus voter")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# vote = get_vote()
|
||||||
|
# generate_signature(vote, card)
|
||||||
|
# add_vote_to_urn(vote)
|
||||||
|
# print("Vote comptabilisé!")
|
||||||
|
# return True
|
||||||
|
|
||||||
def print_signing_list(self) -> None:
|
def print_signing_list(self) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
23
source/Pki.py
Normal file
23
source/Pki.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
class Pki:
|
||||||
|
"""
|
||||||
|
Represent a Public Key Infrastructure
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Certificate:
|
||||||
|
"""
|
||||||
|
Represent a Certificate
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# create a builder for the certificate
|
||||||
|
builder = x509.CertificateBuilder(
|
||||||
|
issuer_name=x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, issuer)]),
|
||||||
|
subject_name=x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, subject)]),
|
||||||
|
serial_number=x509.random_serial_number(),
|
||||||
|
public_key=self.public_key,
|
||||||
|
not_valid_before=valid_start,
|
||||||
|
not_valid_after=valid_end,
|
||||||
|
)
|
|
@ -4,23 +4,57 @@ from cryptography import x509
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
|
# ETAT
|
||||||
|
|
||||||
|
|
||||||
# generate a private key for the certificate
|
# generate a private key for the certificate
|
||||||
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
admin_private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
|
||||||
# get the public key from it
|
# get the public key from it
|
||||||
public_key = private_key.public_key()
|
admin_public_key = admin_private_key.public_key()
|
||||||
|
|
||||||
# create a builder for the certificate
|
# create a builder for the certificate
|
||||||
builder = x509.CertificateBuilder(
|
builder = x509.CertificateBuilder(
|
||||||
issuer_name=x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, "vote.gouv.fr")]),
|
issuer_name=x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, "vote.gouv.fr")]),
|
||||||
subject_name=x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, "vote.gouv.fr")]),
|
subject_name=x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, "vote.gouv.fr")]),
|
||||||
serial_number=x509.random_serial_number(),
|
serial_number=x509.random_serial_number(),
|
||||||
public_key=public_key,
|
public_key=admin_public_key,
|
||||||
not_valid_before=datetime.now(),
|
not_valid_before=datetime.now(),
|
||||||
not_valid_after=datetime.now() + timedelta(weeks=1),
|
not_valid_after=datetime.now() + timedelta(weeks=1),
|
||||||
)
|
)
|
||||||
|
|
||||||
# create the certificate by signing it
|
# create the certificate by signing it
|
||||||
certificate = builder.sign(private_key, algorithm=hashes.SHA256())
|
admin_certificate = builder.sign(admin_private_key, algorithm=hashes.SHA256())
|
||||||
|
|
||||||
|
|
||||||
print(certificate)
|
print(admin_certificate)
|
||||||
|
|
||||||
|
|
||||||
|
# BUREAU
|
||||||
|
|
||||||
|
|
||||||
|
# generate a private key for the certificate
|
||||||
|
machine_private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
||||||
|
# get the public key from it
|
||||||
|
machine_public_key = machine_private_key.public_key()
|
||||||
|
|
||||||
|
# create a builder for the certificate
|
||||||
|
builder = x509.CertificateBuilder(
|
||||||
|
issuer_name=x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, "vote.gouv.fr")]),
|
||||||
|
subject_name=x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, "machine.vote.gouv.fr")]),
|
||||||
|
serial_number=x509.random_serial_number(),
|
||||||
|
public_key=machine_public_key,
|
||||||
|
not_valid_before=datetime.now(),
|
||||||
|
not_valid_after=datetime.now() + timedelta(weeks=1),
|
||||||
|
)
|
||||||
|
|
||||||
|
# create the certificate by signing it
|
||||||
|
machine_certificate = builder.sign(admin_private_key, algorithm=hashes.SHA256())
|
||||||
|
|
||||||
|
|
||||||
|
print(machine_certificate)
|
||||||
|
# check that the machine
|
||||||
|
machine_certificate.verify_directly_issued_by(admin_certificate)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
|
23
source/wtf/Machine2.py
Normal file
23
source/wtf/Machine2.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
class Machine2:
|
||||||
|
def __init__(self, certificat):
|
||||||
|
self.certificat = certificat
|
||||||
|
self.liste_electorale = {}
|
||||||
|
self.bdd_votes = []
|
||||||
|
self.bdd_preuves = []
|
||||||
|
|
||||||
|
def charger_liste_electorale(self, liste_electorale):
|
||||||
|
self.liste_electorale = liste_electorale
|
||||||
|
|
||||||
|
def authentifier_electeur(self, electeur):
|
||||||
|
return electeur.authentifier() and electeur.carte_election.cle_publique in self.liste_electorale
|
||||||
|
|
||||||
|
def enregistrer_vote(self, preuve_vote):
|
||||||
|
self.bdd_votes.append(preuve_vote['vote'])
|
||||||
|
self.bdd_preuves.append(preuve_vote)
|
||||||
|
|
||||||
|
def publier_resultats(self):
|
||||||
|
return self.bdd_votes
|
||||||
|
|
||||||
|
def fin_de_vote(self):
|
||||||
|
# Bloquer les nouveaux votes
|
||||||
|
self.bloque = True
|
0
source/wtf/__init__.py
Normal file
0
source/wtf/__init__.py
Normal file
18
tests/exemple1.py
Normal file
18
tests/exemple1.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import hashlib
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
|
||||||
|
from source.Card import Card
|
||||||
|
from source.Certificate import Certificate
|
||||||
|
from source.Machine import Machine
|
||||||
|
|
||||||
|
Alice_card = Card("Alice", 6060)
|
||||||
|
|
||||||
|
Machine = Machine()
|
||||||
|
|
||||||
|
|
||||||
|
Machine.authenticate(Alice_card, 6060, azerty)
|
||||||
|
|
||||||
|
|
||||||
|
Machine.vote(Alice_card)
|
27
tests/schema.md
Normal file
27
tests/schema.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Initialisation
|
||||||
|
|
||||||
|
Il à été distribué dans chaque bureau de votes une machine qui servira d'urne
|
||||||
|
|
||||||
|
L'Administrateur à son propre certificat admin
|
||||||
|
L'administrateur crée un certificat pour chaque machine dans les bureaux de votes
|
||||||
|
|
||||||
|
Tout le monde récupère une carte leur permettant de voter dans leur bureau de vote respectif avec
|
||||||
|
un code pin et leurs empreinte digitale
|
||||||
|
|
||||||
|
# Voter
|
||||||
|
|
||||||
|
L'électeur ou son représentant se présente au bureau de vote avec la carte
|
||||||
|
|
||||||
|
Il s'authentifie auprès de la machine
|
||||||
|
|
||||||
|
Il choisi son candidat
|
||||||
|
La machine enregistre son choix et le fait qu'il à voter de manière indépendante.
|
||||||
|
|
||||||
|
# Publication
|
||||||
|
|
||||||
|
A la fin du vote, les votes sont publiés et visible par tous.
|
||||||
|
|
||||||
|
# Difference avec le vote electronique
|
||||||
|
|
||||||
|
On etabli la connexion TLS au debut avant de realiser l'authentification
|
||||||
|
|
Loading…
Reference in a new issue