Last active
August 5, 2020 04:03
-
-
Save markjenkins/297343b6eb487738da94ae0be2215ccd to your computer and use it in GitHub Desktop.
Election of a single candidate by HMAC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Voter code yS2MuD4+iPrmZOoSIY76qLCMQWS/wQxnql8MmpN9AUY= votes for Pirates | |
non interactively | |
echo -e "yS2MuD4+iPrmZOoSIY76qLCMQWS/wQxnql8MmpN9AUY=\nPirates" | python3 hmac_vote.py | |
interactively | |
python3 hmac_vote.py | |
What's your code? > yS2MuD4+iPrmZOoSIY76qLCMQWS/wQxnql8MmpN9AUY= | |
Your code is: | |
yS2MuD4+iPrmZOoSIY76qLCMQWS/wQxnql8MmpN9AUY= | |
1) Ninjas | |
2) Pirates | |
Who are you voting for? > Pirates | |
your ballot is: | |
cjiMwzbLl570aaPCyzUOvu223WamIgGSHnGhbdTj4ME= | |
Voter code yS2MuD4+iPrmZOoSIY76qLCMQWS/wQxnql8MmpN9AUY= votes for Ninjas | |
ZGLCwApfT/35GrhTko3AmsNjZegkcnjCZUoqhuLs/2E= | |
Voter code VZtI0Jykc9DdUdqwfY0Qkg43RZozm0zsmjewIRaG4OE= votes for Pirates | |
xxxUP45I8XgAlYz9bY698jbieK1RaKfwm685AgrOQbk= | |
Voter code VZtI0Jykc9DdUdqwfY0Qkg43RZozm0zsmjewIRaG4OE= votes for Ninjas | |
MWX3Bo5kly78FI/Anj/T4JHPSKqVMUbuwXYTQK8EwnM= | |
Counting ballots | |
python3 hmac_vote_count.py example_voter_codes.txt <<VOTES | |
cjiMwzbLl570aaPCyzUOvu223WamIgGSHnGhbdTj4ME= | |
xxxUP45I8XgAlYz9bY698jbieK1RaKfwm685AgrOQbk= | |
VOTES | |
2 Pirates | |
0 Ninjas | |
0 spoiled |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
yS2MuD4+iPrmZOoSIY76qLCMQWS/wQxnql8MmpN9AUY= | |
VZtI0Jykc9DdUdqwfY0Qkg43RZozm0zsmjewIRaG4OE= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from __future__ import print_function | |
from __future__ import division | |
from random import SystemRandom | |
from base64 import b64encode | |
from sys import argv, version_info | |
if version_info[0] >= 3: | |
def eight_bit_int_to_byte(n): | |
return int.to_bytes(n, 1, 'big') | |
else: | |
eight_bit_int_to_byte = chr | |
sysrandom = SystemRandom() | |
def generate_voting_code(): | |
random_256_bits_as_bytes = b''.join( | |
eight_bit_int_to_byte(sysrandom.getrandbits(8)) | |
for i in range(256//8) ) | |
return b64encode(random_256_bits_as_bytes).decode('ascii') | |
n_codes = 1 if len(argv) < 2 else int(argv[1]) | |
print( '\n'.join( generate_voting_code() for i in range(n_codes) ) ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from __future__ import print_function | |
from __future__ import division | |
from random import SystemRandom | |
import hmac | |
import hashlib | |
import base64 | |
import sys | |
if sys.version_info[0] >= 3: | |
raw_input = input | |
candidates = [ | |
"Pirates", | |
"Ninjas", | |
] | |
SystemRandom().shuffle(candidates) | |
def create_code_signed_ballot(code, vote): | |
ballot_hmac = hmac.new(code.encode('UTF-8'), vote.encode('UTF-8'), | |
digestmod=hashlib.sha256) | |
base_64_bytes = base64.b64encode(ballot_hmac.digest()) | |
return base_64_bytes.decode('ascii') | |
def get_valid_vote(candidates): | |
candidates_enumerated = list(enumerate(candidates, 1)) | |
candidate_lookup_by_number = dict( candidates_enumerated ) | |
while True: | |
print() # blank line | |
for i, candidate in candidates_enumerated: | |
print("%d) %s" % (i, candidate)) | |
candidate_selection_w_newline = raw_input("Who are you voting for? > ") | |
candidate_selection = candidate_selection_w_newline.strip() | |
if candidate_selection in candidates: | |
return candidate_selection | |
else: | |
try: | |
candidate_number = int(candidate_selection) | |
except ValueError: | |
pass | |
else: # only if conversion of candidate_selection to int works | |
if candidate_number in candidate_lookup_by_number: | |
return candidate_lookup_by_number[candidate_number] | |
def get_valid_code(): | |
while True: | |
code_w_newline = raw_input("What's your code? > ") | |
code = code_w_newline.strip() | |
try: | |
decoded_code = base64.b64decode(code) | |
except: | |
print("invalid code") | |
else: | |
if len(decoded_code) == (256//8): | |
break | |
else: | |
print("invalid code") | |
print("Your code is:") | |
print(code) | |
print() | |
return code | |
def main(): | |
code = get_valid_code() | |
vote = get_valid_vote(candidates) | |
code_signed_ballot = create_code_signed_ballot(code, vote) | |
print("your ballot is:") | |
print(code_signed_ballot) | |
if __name__ == "__main__": | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# invoke with argument 1 being a test file with the voting codes | |
# and stdin being the ballots | |
from itertools import product | |
import sys | |
from hmac_vote import candidates, create_code_signed_ballot | |
def construct_possible_ballots(voter_codes, candidates): | |
return { | |
create_code_signed_ballot(voter_code, candidate): | |
(voter_code, candidate) | |
for voter_code, candidate in product(voter_codes, candidates) | |
} # dict comprehension | |
def main(): | |
with open(sys.argv[1]) as f: | |
voter_codes = [line.strip() for line in f ] | |
possible_ballots = construct_possible_ballots(voter_codes, candidates) | |
vote_counts = { candidate: 0 | |
for candidate in candidates } # dict comprehension | |
spoiled = 0 | |
for line in sys.stdin: | |
line_newline_stripped = line.strip() | |
if line_newline_stripped in possible_ballots: | |
voter_code, candidate_voted_for = \ | |
possible_ballots[line_newline_stripped] | |
vote_counts[candidate_voted_for] += 1 | |
# remove all possible ballots for this voter_code | |
# to prevent voting twice | |
for candidate in candidates: | |
del possible_ballots[ create_code_signed_ballot( | |
voter_code, candidate) ] | |
else: | |
spoiled += 1 | |
results = [ (vote_count, candidate) | |
for candidate, vote_count in vote_counts.items() ] | |
print( '\n'.join( | |
"%d %s" % (vote_count, candidate) | |
for vote_count, candidate in results )) | |
print("%d spoiled" % spoiled) | |
if __name__ == "__main__": | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# based on https://stackoverflow.com/a/4618361 | |
# but modified to use SystemRandom, sys.argv[1] and sys.argv[2], and with blocks | |
from random import SystemRandom | |
import sys | |
sysrandom = SystemRandom() | |
with open(sys.argv[1]) as f: | |
lines = f.readlines() | |
sysrandom.shuffle(lines) | |
with open(sys.argv[2], 'w') as f: | |
f.writelines(lines) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment