Skip to content

Instantly share code, notes, and snippets.

@Grissess
Created July 28, 2016 09:44
Show Gist options
  • Save Grissess/701578ff4471f76f0d434b2a28e43241 to your computer and use it in GitHub Desktop.
Save Grissess/701578ff4471f76f0d434b2a28e43241 to your computer and use it in GitHub Desktop.
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