diff --git a/eos/base/election.py b/eos/base/election.py index 4b44a2d..888242a 100644 --- a/eos/base/election.py +++ b/eos/base/election.py @@ -35,7 +35,7 @@ class Ballot(EmbeddedObject): election_id = UUIDField() election_hash = StringField() - answers = EmbeddedObjectListField(is_hashed=False) + answers = EmbeddedObjectListField(is_hashed=False) # Used for ballots to be audited def deaudit(self): encrypted_answers_deaudit = EosList() @@ -118,19 +118,23 @@ class ListChoiceQuestion(Question): if len(answer.choices) == 0: return '(blank votes)' return ', '.join([self.choices[choice] for choice in answer.choices]) - -class ApprovalQuestion(ListChoiceQuestion): - pass + + def max_bits(self): + answer = self.answer_type(choices=list(range(len(self.choices)))) + return len(EosObject.to_json(EosObject.serialise_and_wrap(answer))) * 8 class ApprovalAnswer(Answer): choices = ListField(IntField()) -class PreferentialQuestion(ListChoiceQuestion): - pass +class ApprovalQuestion(ListChoiceQuestion): + answer_type = ApprovalAnswer class PreferentialAnswer(Answer): choices = ListField(IntField()) +class PreferentialQuestion(ListChoiceQuestion): + answer_type = PreferentialAnswer + class RawResult(Result): plaintexts = ListField(EmbeddedObjectListField()) answers = EmbeddedObjectListField() diff --git a/eos/core/objects/__init__.py b/eos/core/objects/__init__.py index aa915dc..4014b42 100644 --- a/eos/core/objects/__init__.py +++ b/eos/core/objects/__init__.py @@ -60,8 +60,9 @@ class DBInfo: dbinfo = DBInfo() def db_connect(db_name, db_uri='mongodb://localhost:27017/', db_type='mongodb'): - dbinfo.provider = eos.core.db.db_providers[db_type](db_name, db_uri) - dbinfo.provider.connect() + if is_python: + dbinfo.provider = eos.core.db.db_providers[db_type](db_name, db_uri) + dbinfo.provider.connect() # Fields # ====== diff --git a/eos/psr/bitstream.py b/eos/psr/bitstream.py index 2be2498..daff402 100644 --- a/eos/psr/bitstream.py +++ b/eos/psr/bitstream.py @@ -111,18 +111,29 @@ class BitStream(EosObject): return self + def pad_by(self, padding_size, pad_at_end=False): + if padding_size > 0: + if pad_at_end: + # Suitable for structured data + self.seek(self.nbits) + self.write(ZERO, padding_size) + else: + # Suitable for raw numbers + self.nbits += padding_size + return self # For convenient chaining + + def pad_to(self, target_size, pad_at_end=False): + if self.nbits < target_size: + diff = target_size - self.nbits + self.pad_by(diff, pad_at_end) + return self + # Make the size of this BitStream a multiple of the block_size def multiple_of(self, block_size, pad_at_end=False): if self.nbits % block_size != 0: diff = block_size - (self.nbits % block_size) - if pad_at_end: - # Suitable for structured data - self.seek(self.nbits) - self.write(ZERO, diff) - else: - # Suitable for raw numbers - self.nbits += diff - return self # For convenient chaining + self.pad_by(diff, pad_at_end) + return self def map(self, func, block_size): if self.nbits % block_size != 0: diff --git a/eos/psr/crypto.py b/eos/psr/crypto.py index fdf9553..660b723 100644 --- a/eos/psr/crypto.py +++ b/eos/psr/crypto.py @@ -41,7 +41,7 @@ class CyclicGroup(EmbeddedObject): crypto_method = BigInt.crypto_random if crypto_random else BigInt.noncrypto_random return crypto_method(ZERO, self.q - ONE) -# RFC 3526 +# RFC 3526, 2048-bit MODP Group DEFAULT_GROUP = CyclicGroup( p=BigInt('FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF', 16), g=TWO diff --git a/eos/psr/election.py b/eos/psr/election.py index 42cc16c..c23e207 100644 --- a/eos/psr/election.py +++ b/eos/psr/election.py @@ -27,10 +27,14 @@ class BlockEncryptedAnswer(EncryptedAnswer): blocks = EmbeddedObjectListField() @classmethod - def encrypt(cls, pk, obj): + def encrypt(cls, pk, obj, nbits=None): pt = EosObject.to_json(EosObject.serialise_and_wrap(obj)) bs = BitStream() bs.write_string(pt) + if nbits is not None: + if bs.nbits > nbits: + raise Exception('Message is too big') + bs.pad_to(nbits, True) bs.multiple_of(pk.nbits(), True) ct = bs.map(pk.encrypt, pk.nbits()) diff --git a/eos/psr/tests.py b/eos/psr/tests.py index 5b47db5..29bf25e 100644 --- a/eos/psr/tests.py +++ b/eos/psr/tests.py @@ -153,7 +153,12 @@ class BlockEGTestCase(EosTestCase): ct = BlockEncryptedAnswer.encrypt(self.sk.public_key, obj) _, m = ct.decrypt(self.sk) + self.assertEqualJSON(obj, m) + # Force another block + ct2 = BlockEncryptedAnswer.encrypt(self.sk.public_key, obj, (len(ct.blocks) * self.sk.public_key.nbits()) + 1) + self.assertEqual(len(ct2.blocks), len(ct.blocks) + 1) + _, m = ct2.decrypt(self.sk) self.assertEqualJSON(obj, m) class MixnetTestCase(EosTestCase): @@ -177,7 +182,7 @@ class MixnetTestCase(EosTestCase): def do_mixnet(mix_order): # Set up mixnet - mixnet = RPCMixnet(mix_order) + mixnet = RPCMixnet(mix_order=mix_order) # Mix answers shuffled_answers, commitments = mixnet.shuffle(answers) diff --git a/eosweb/core/static/js/booth_worker.js b/eosweb/core/static/js/booth_worker.js index 815f11e..acfa65d 100644 --- a/eosweb/core/static/js/booth_worker.js +++ b/eosweb/core/static/js/booth_worker.js @@ -21,9 +21,10 @@ isLibrariesLoaded = false; function generateEncryptedVote(election, answers) { encrypted_answers = []; - for (var answer_json of answers) { + for (var q_num = 0; q_num < answers.length; q_num++) { + answer_json = answers[q_num]; answer = eosjs.eos.core.objects.__all__.EosObject.deserialise_and_unwrap(answer_json, null); - encrypted_answer = eosjs.eos.psr.election.__all__.BlockEncryptedAnswer.encrypt(election.public_key, answer); + encrypted_answer = eosjs.eos.psr.election.__all__.BlockEncryptedAnswer.encrypt(election.public_key, answer, election.questions.__getitem__(q_num).max_bits()); encrypted_answers.push(eosjs.eos.core.objects.__all__.EosObject.serialise_and_wrap(encrypted_answer, null)); }