Created
July 28, 2016 09:44
-
-
Save Grissess/701578ff4471f76f0d434b2a28e43241 to your computer and use it in GitHub Desktop.
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
import argparse, hashlib, json | |
from random import SystemRandom | |
from shcs.rsa.crypto import Cipher as RSA | |
from shcs.pad import LSBPadding | |
from shcs import modular | |
parser = argparse.ArgumentParser() | |
parser.add_argument('-h', '--hash', dest='hash', default='sha512', help='Hash function to use for commitment') | |
class SENPAI(object): | |
# ALICE, PHASE 1: Make an x or y. The highbits for x and y MAY be equal IFF ALICE chooses both to have the same semantics (e.g., both "no") | |
# Returns both the unpadded x or y, and a randomly-selected s concatenated into it, for commitment purposes. | |
def make_choice(self, highbits, randbits, rand): | |
rbs = rand.getrandbits(randbits) | |
return highbits << randbits | rbs, rbs | |
# ALICE, PHASE 1: Commit to a given value (usually s from at least one choice). hashfunc should be previously unused. | |
# Returns a digest. | |
def make_commitment(self, s, hashfunc): | |
hashfunc.update(s) | |
return hashfunc.digest() | |
# ALICE, PHASE 1: Prepare BOB's packet, giving BOB all the necessary information in the system. | |
# Returns a mapping object suitable for use with most of BOB's PHASE 1 functions. | |
def alice_phase1(self, key, padder, choices, commitments, choicenames, randbits): | |
return { | |
'key': {'n': key.n, 'e': key.e}, | |
'padding': padder.bits, | |
'randbits': randbits, | |
'choices': [key.encrypt(padder.pad(choice)) for choice in choices], | |
'commitments': commitments, | |
'choicenames': choicenames, | |
} | |
# BOB, PHASE 1: Returns available choices in a packet. | |
def get_choicenames(self, p1pkt): | |
return p1pkt['choicenames'] | |
# BOB, PHASE 1: Map a choice name to a choice index. | |
def get_choiceidx(self, p1pkt, chname): | |
return p1pkt['choicenames'].index(chname) | |
# BOB, PHASE 1: Make a decision and prepare an ALICE PHASE 2 packet. | |
# Returns a secret r, and a mapping object suitable for use with most of ALICE'S PHASE 2 functions. | |
def bob_phase1(self, p1pkt, chidx, rand): | |
key = RSA(p1pkt['key']['n'], p1pkt['key']['e']) | |
sec = rand.randint(2, key.n - 1) | |
return sec, { | |
'choice': (p1pkt['choices'][chidx] * key.encrypt(sec)) % key.n, | |
} | |
# ALICE, PHASE 2: Decrypt BOB's PHASE 1 packet, which is a choice multiplied by a secret, and send that back. | |
# Returns a mapping object suitable for use with BOB's PHASE 2 functions. | |
def alice_phase2(self, p1pkt, key): | |
return { | |
'result': key.decrypt(p1pkt['choice']), | |
} | |
# BOB, PHASE 2: Cancel secret r from a choice and unpad it to reveal its highbits and rbs (preimage of s). | |
# It is then possible to determine if rbs was a commited s (if that s is in the given commitments), which | |
# allows both BOB and ALICE to ascertain the legitimacy of the choice and rbs, respectively. | |
# If no such commitment exists, ALICE will nonetheless reveal committed choices in PHASE 3, after the | |
# decision is made. | |
# Returns the highbits (the "real choice"), whether or not rbs was a valid preimage (a fairly strong trust indicator), and a mapping object suitable for ALICE's PHASE 3. | |
def bob_phase2(self, sec, hashfunc, p1pkt, p2pkt): | |
choice = (p2pkt['result'] * modular.modinv(sec, p1pkt['key']['n'])) % p1pkt['key']['n'] | |
highbits, rbs = choice >> p1pkt['randbits'], choice & ((1 << p1pkt['randbits']) - 1) | |
hashfunc.update(rbs) | |
commitment = hashfunc.digest() | |
return highbits, commitment in p1pkt['commitments'], { | |
'highbits': highbits, | |
'rbs': rbs, | |
} | |
# ALICE, PHASE 3: Determines the result and secret from BOB. | |
# Returns the highbits (the "real choice"), whether or not BOB's choice was valid at all (a certain trust indicator), and a packet for BOB's PHASE 3. | |
def alice_phase3(self, key, choices, randbits, padder, hashgen, p2pkt): | |
result = [] | |
choice = p2pkt['highbits'] << randbits | p2pkt['rbs'] | |
found = False | |
for ech in choices: | |
ch = padder.unpad(key.decrypt(ech)) | |
if choice == ech: | |
found = True | |
rbs = ch & ((1 << randbits) - 1) | |
hashfunc = hashgen() | |
s = self.make_commitment(rbs, hasfunc) | |
if s in commitments: | |
result.append(ch) | |
return p2pkt['highbits'], found, { | |
'commitchoices': result | |
} | |
# BOB, PHASE 3: Determines whether ALICE sent valid commitments. At this point, the decision is already made. | |
# Returns True if all commitments were valid and complete (a strong trust indicator) | |
def bob_phase3(self, hashgen, p1pkt, p3pkt): | |
comset = set(p1pkt['commitments']) | |
chset | |
for ech in p3pkt['commitchoices']: | |
hashfunc = hashgen() | |
hashfunc.update(ech) | |
comset.discard(hashfunc.digest()) | |
return not comset |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment