Add mixnet verification to workflow
This commit is contained in:
parent
a242f1f666
commit
239c7be952
@ -46,6 +46,8 @@ else:
|
|||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
class SHA256:
|
class SHA256:
|
||||||
|
nbits = 256
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
if is_python:
|
if is_python:
|
||||||
self.impl = hashlib.sha256()
|
self.impl = hashlib.sha256()
|
||||||
|
@ -16,12 +16,13 @@
|
|||||||
|
|
||||||
from eos.core.bigint import *
|
from eos.core.bigint import *
|
||||||
from eos.core.objects import *
|
from eos.core.objects import *
|
||||||
|
from eos.core.hashing import *
|
||||||
|
|
||||||
class BitStream(EosObject):
|
class BitStream(EosObject):
|
||||||
def __init__(self, value=None):
|
def __init__(self, value=None, nbits=None):
|
||||||
if value:
|
if value:
|
||||||
self.impl = value
|
self.impl = value
|
||||||
self.nbits = self.impl.nbits()
|
self.nbits = nbits if nbits else self.impl.nbits()
|
||||||
else:
|
else:
|
||||||
self.impl = ZERO
|
self.impl = ZERO
|
||||||
self.nbits = 0
|
self.nbits = 0
|
||||||
@ -58,6 +59,17 @@ class BitStream(EosObject):
|
|||||||
self.ptr += nbits
|
self.ptr += nbits
|
||||||
self.nbits += nbits
|
self.nbits += nbits
|
||||||
|
|
||||||
|
# Append to the end without affecting ptr
|
||||||
|
def append(self, bits, nbits=None):
|
||||||
|
if nbits is None:
|
||||||
|
nbits = bits.nbits()
|
||||||
|
if nbits < bits.nbits():
|
||||||
|
raise Exception('Too many bits to append to BitString')
|
||||||
|
|
||||||
|
self.impl = (self.impl << nbits) | bits
|
||||||
|
self.nbits += nbits
|
||||||
|
self.remaining += nbits
|
||||||
|
|
||||||
def read_string(self):
|
def read_string(self):
|
||||||
length = self.read(32)
|
length = self.read(32)
|
||||||
length = length.__int__() # JS attempts to call this twice if we do it in one line
|
length = length.__int__() # JS attempts to call this twice if we do it in one line
|
||||||
@ -124,3 +136,23 @@ class BitStream(EosObject):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def deserialise(cls, value):
|
def deserialise(cls, value):
|
||||||
return cls(value)
|
return cls(value)
|
||||||
|
|
||||||
|
class InfiniteHashBitStream(BitStream):
|
||||||
|
def __init__(self, seed):
|
||||||
|
self.sha = SHA256()
|
||||||
|
self.sha.update_bigint(seed)
|
||||||
|
self.ctr = 0
|
||||||
|
self.sha.update_text(str(self.ctr))
|
||||||
|
|
||||||
|
super().__init__(self.sha.hash_as_bigint(), self.sha.nbits)
|
||||||
|
|
||||||
|
def read(self, nbits=None):
|
||||||
|
# 11000110110
|
||||||
|
# ^----
|
||||||
|
if nbits is None:
|
||||||
|
nbits = self.remaining
|
||||||
|
while nbits > self.remaining:
|
||||||
|
self.ctr += 1
|
||||||
|
self.sha.update_text(str(self.ctr))
|
||||||
|
self.append(self.sha.hash_as_bigint(), self.sha.nbits)
|
||||||
|
return super().read(nbits)
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
from eos.core.bigint import *
|
from eos.core.bigint import *
|
||||||
from eos.core.objects import *
|
from eos.core.objects import *
|
||||||
|
from eos.core.hashing import *
|
||||||
from eos.base.election import *
|
from eos.base.election import *
|
||||||
from eos.psr.bitstream import *
|
from eos.psr.bitstream import *
|
||||||
from eos.psr.crypto import *
|
from eos.psr.crypto import *
|
||||||
@ -48,16 +49,30 @@ class Trustee(EmbeddedObject):
|
|||||||
email = StringField()
|
email = StringField()
|
||||||
|
|
||||||
class MixChallengeResponse(EmbeddedObject):
|
class MixChallengeResponse(EmbeddedObject):
|
||||||
index = IntField()
|
challenge_index = IntField()
|
||||||
|
response_index = IntField()
|
||||||
reenc = EmbeddedObjectListField(BigInt)
|
reenc = EmbeddedObjectListField(BigInt)
|
||||||
rand = EmbeddedObjectField(BigInt)
|
rand = EmbeddedObjectField(BigInt)
|
||||||
|
|
||||||
class MixingTrustee(Trustee):
|
class MixingTrustee(Trustee):
|
||||||
mixed_questions = ListField(EmbeddedObjectListField(BlockEncryptedAnswer))
|
mixed_questions = ListField(EmbeddedObjectListField(BlockEncryptedAnswer))
|
||||||
commitments = ListField(EmbeddedObjectListField(BigInt))
|
commitments = ListField(EmbeddedObjectListField(BigInt))
|
||||||
challenge = EmbeddedObjectField(BigInt)
|
challenge = EmbeddedObjectListField(BigInt)
|
||||||
response = ListField(EmbeddedObjectListField(MixChallengeResponse))
|
response = ListField(EmbeddedObjectListField(MixChallengeResponse))
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.mixnets = [] # TODO: Remove this stuff
|
||||||
|
|
||||||
|
def compute_challenge(self, question_num):
|
||||||
|
sha = SHA256()
|
||||||
|
trustees = self.recurse_parents(Election).mixing_trustees
|
||||||
|
for i in range(len(trustees)):
|
||||||
|
sha.update_text(EosObject.to_json(MixingTrustee._fields['mixed_questions'].element_field.serialise(trustees[i].mixed_questions[question_num])))
|
||||||
|
for i in range(self._instance[1]):
|
||||||
|
sha.update_text(EosObject.to_json(MixingTrustee._fields['response'].element_field.serialise(trustees[i].response[question_num])))
|
||||||
|
return sha
|
||||||
|
|
||||||
class PSRElection(Election):
|
class PSRElection(Election):
|
||||||
_db_name = Election._name
|
_db_name = Election._name
|
||||||
|
|
||||||
|
@ -242,7 +242,8 @@ class ElectionTestCase(EosTestCase):
|
|||||||
# Do the mix
|
# Do the mix
|
||||||
for i in range(len(election.questions)):
|
for i in range(len(election.questions)):
|
||||||
for j in range(len(election.mixing_trustees)):
|
for j in range(len(election.mixing_trustees)):
|
||||||
mixnet = RPCMixnet(j)
|
# Wouldn't happen server-side IRL
|
||||||
|
election.mixing_trustees[j].mixnets.append(RPCMixnet(j))
|
||||||
if j > 0:
|
if j > 0:
|
||||||
orig_answers = election.mixing_trustees[j - 1].mixed_questions[i]
|
orig_answers = election.mixing_trustees[j - 1].mixed_questions[i]
|
||||||
else:
|
else:
|
||||||
@ -250,7 +251,7 @@ class ElectionTestCase(EosTestCase):
|
|||||||
for voter in election.voters:
|
for voter in election.voters:
|
||||||
for ballot in voter.ballots:
|
for ballot in voter.ballots:
|
||||||
orig_answers.append(ballot.encrypted_answers[i])
|
orig_answers.append(ballot.encrypted_answers[i])
|
||||||
shuffled_answers, commitments = mixnet.shuffle(orig_answers)
|
shuffled_answers, commitments = election.mixing_trustees[j].mixnets[i].shuffle(orig_answers)
|
||||||
election.mixing_trustees[j].mixed_questions.append(EosList(shuffled_answers))
|
election.mixing_trustees[j].mixed_questions.append(EosList(shuffled_answers))
|
||||||
election.mixing_trustees[j].commitments.append(EosList(commitments))
|
election.mixing_trustees[j].commitments.append(EosList(commitments))
|
||||||
|
|
||||||
@ -261,7 +262,30 @@ class ElectionTestCase(EosTestCase):
|
|||||||
election.workflow.get_task('eos.psr.workflow.TaskVerifyMixes').enter()
|
election.workflow.get_task('eos.psr.workflow.TaskVerifyMixes').enter()
|
||||||
election.save()
|
election.save()
|
||||||
|
|
||||||
# TODO
|
# Record challenge responses
|
||||||
|
for i in range(len(election.questions)):
|
||||||
|
for j in range(len(election.mixing_trustees)):
|
||||||
|
trustee = election.mixing_trustees[j]
|
||||||
|
if j % 2 == 0:
|
||||||
|
trustee.challenge.append(trustee.compute_challenge(i).hash_as_bigint())
|
||||||
|
else:
|
||||||
|
trustee.challenge.append(election.mixing_trustees[j - 1].challenge[i])
|
||||||
|
challenge_bs = InfiniteHashBitStream(trustee.challenge[i])
|
||||||
|
|
||||||
|
trustee.response.append(EosList())
|
||||||
|
|
||||||
|
nbits = BigInt(len(trustee.mixed_questions[i])).nbits()
|
||||||
|
for k in range(len(trustee.mixed_questions[i])):
|
||||||
|
challenge_bit = challenge_bs.read(1)
|
||||||
|
should_reveal = ((j % 2) == (challenge_bit % 2))
|
||||||
|
if should_reveal:
|
||||||
|
response = trustee.mixnets[i].challenge(k)
|
||||||
|
trustee.response[i].append(MixChallengeResponse(
|
||||||
|
challenge_index=k,
|
||||||
|
response_index=response[0],
|
||||||
|
reenc=response[1],
|
||||||
|
rand=response[2]
|
||||||
|
))
|
||||||
|
|
||||||
election.workflow.get_task('eos.psr.workflow.TaskVerifyMixes').exit()
|
election.workflow.get_task('eos.psr.workflow.TaskVerifyMixes').exit()
|
||||||
election.save()
|
election.save()
|
||||||
|
Loading…
Reference in New Issue
Block a user