diff --git a/helios/datatypes/2011/01.py b/helios/datatypes/2011/01.py index a2edc83c6f75aaa00bc73f190fe68d30c5ef01d4..ca5647947782c23536c45eff5f4d43ba58641d04 100644 --- a/helios/datatypes/2011/01.py +++ b/helios/datatypes/2011/01.py @@ -4,5 +4,18 @@ data types for 2011/01 Helios from helios.datatypes import LDObject +class Trustee(LDObject): + """ + a trustee + """ + + FIELDS = ['uuid', 'public_key', 'public_key_hash', 'pok', 'decryption_factors', 'decryption_proofs', 'email'] + STRUCTURED_FIELDS = { + 'pok' : 'pkc/elgamal/DiscreteLogProof', + 'public_key' : 'pkc/elgamal/PublicKey' + } + + # removed some public key processing for now + class Election(LDObject): pass diff --git a/helios/datatypes/__init__.py b/helios/datatypes/__init__.py index d336b00cde8632a7d7ef4279cdbe8cc908edc32e..ef8ed017ec5c5cfb892d0054d74975b5c52421e1 100644 --- a/helios/datatypes/__init__.py +++ b/helios/datatypes/__init__.py @@ -34,7 +34,8 @@ class LDObject(object): data format. For example, a legacy election LDObject instance will wrap an Election object and serialize its fields according to the specs for that version. - To accomodate old JSON types, we allow classes to override default JSON-LD fields. + To accomodate old JSON types, we allow classes to do basically whatever they want, + or to let this base class serialize pure JSON thingies, without the JSON-LD. """ # whether or not to add JSON-LD things @@ -43,26 +44,56 @@ class LDObject(object): # fields to serialize FIELDS = [] + # structured fields + STRUCTURED_FIELDS = {} + + def __init__(self, wrapped_obj): + self.wrapped_obj = wrapped_obj + self.structured_fields = {} + @classmethod - def instantiate(cls, obj): - if not hasattr(obj, 'datatype'): + def get_class(cls, datatype): + # parse datatype string "v31/Election" --> from v31 import Election + parsed_datatype = datatype.split("/") + + # get the module + dynamic_module = __import__(".".join(parsed_datatype[:-1]), globals(), locals(), [], level=-1) + + # go down the attributes to get to the class + dynamic_ptr = dynamic_module + for attr in parsed_datatype[1:]: + dynamic_ptr = getattr(dynamic_ptr, attr) + dynamic_cls = dynamic_ptr + + return dynamic_cls + + @classmethod + def instantiate(cls, obj, datatype=None): + if hasattr(obj, 'datatype') and not datatype: + datatype = getattr(obj, 'datatype') + + if not datatype: raise Exception("no datatype found") - # parse datatype string "v31/Election" --> from v31 import Election - parsed_datatype = obj.datatype.split("/") + # the class + dynamic_cls = cls.get_class(datatype) + + # instantiate it + return_obj = dynamic_cls(obj) - # construct it - dynamic_cls = getattr(__import__(".".join(parsed_datatype[:-1]), globals(), locals(), [], level=-1), parsed_datatype[len(parsed_datatype)-1]) + # go through the subfields and instantiate them too + for subfield_name, subfield_type in dynamic_cls.STRUCTURED_FIELDS.iteritems(): + return_obj.structured_fields[subfield_name] = cls.instantiate(getattr(return_obj.wrapped_obj, subfield_name), datatype = subfield_type) - return dynamic_cls(obj) + return return_obj def set_from_args(self, **kwargs): for f in self.FIELDS: if kwargs.has_key(f): new_val = self.process_value_in(f, kwargs[f]) - setattr(self, f, new_val) + setattr(self.wrapped_obj, f, new_val) else: - setattr(self, f, None) + setattr(self.wrapped_obj, f, None) def serialize(self): return utils.to_json(self.toDict()) @@ -70,17 +101,23 @@ class LDObject(object): def toDict(self, alternate_fields=None): val = {} for f in (alternate_fields or self.FIELDS): - val[f] = self.process_value_out(f, getattr(self, f)) + # is it a structured subfield? + if self.structured_fields.has_key(f): + val[f] = self.structured_fields[f].toDict() + else: + val[f] = self.process_value_out(f, getattr(self.wrapped_obj, f)) return val @classmethod def fromDict(cls, d): + raise Exception("not a good idea yet") + # go through the keys and fix them new_d = {} for k in d.keys(): new_d[str(k)] = d[k] - return cls(**new_d) + return cls(**new_d) @property def hash(self): @@ -125,3 +162,25 @@ class LDObject(object): return other != None and self.uuid == other.uuid + +class ArrayOfObjects(LDObject): + """ + If one type has, as a subtype, an array of things, then this is the structured field used + """ + + def __init__(self, wrapped_array, item_type): + self.item_type = item_type + self.items = [LDObject.instantiate(wrapped_item, item_type) for wrapped_item in wrapped_array] + + def toDict(self): + return [item.serialize() for item in self.items] + +def arrayOf(item_type): + """ + a wrapper for the construtor of the array + returns the constructor + """ + def array_constructor(wrapped_array): + return ArrayOfObjects(wrapped_array, item_type) + + return array_constructor diff --git a/helios/datatypes/core.py b/helios/datatypes/core.py new file mode 100644 index 0000000000000000000000000000000000000000..c0d488c9f2ddb3586217287a641005a3527c3b32 --- /dev/null +++ b/helios/datatypes/core.py @@ -0,0 +1,15 @@ +""" +core data types +""" + +from helios.datatypes import LDObject + +class BigInteger(LDObject): + """ + A big integer is an integer serialized as a string. + We may want to b64 encode here soon. + """ + + def toDict(self): + return str(self.wrapped_obj) + diff --git a/helios/datatypes/legacy.py b/helios/datatypes/legacy.py index 941f15ff1d1003aed5eb6bf1189aaacc05cc01b6..1a10de2fba32425ef10f152df099868b11c8efd5 100644 --- a/helios/datatypes/legacy.py +++ b/helios/datatypes/legacy.py @@ -2,4 +2,34 @@ Legacy datatypes for Helios (v3.0) """ -class +from helios.datatypes import LDObject, arrayOf + +class LegacyObject(LDObject): + USE_JSON_LD = False + +class Election(LegacyObject): + FIELDS = ['uuid', 'questions', 'name', 'short_name', 'description', 'voters_hash', 'openreg', + 'frozen_at', 'public_key', 'cast_url', 'use_voter_aliases', 'voting_starts_at', 'voting_ends_at'] + + STRUCTURED_FIELDS = { + 'public_key' : 'legacy/EGPublicKey' + } + +class EncryptedAnswer(LegacyObject): + FIELDS = ['choices', 'individual_proofs', 'overall_proof', 'randomness', 'answer'] + STRUCTURED_FIELDS = { + 'choices': arrayOf('pkc/elgamal/Ciphertext'), + 'individual_proofs': arrayOf('pkc/elgamal/DisjunctiveProof'), + 'overall_proof' : 'pkc/elgamal/DisjunctiveProof', + 'randomness' : 'core/BigInteger' + # answer is not a structured field, it's an as-is integer + } + +class Voter(LegacyObject): + pass + +class CastVote(LegacyObject): + pass + +class Trustee(LegacyObject): + pass diff --git a/helios/datatypes/pkc/__init__.py b/helios/datatypes/pkc/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4783a0818c27153aa2fa36f252710c81fed43880 --- /dev/null +++ b/helios/datatypes/pkc/__init__.py @@ -0,0 +1,3 @@ +""" +Public Key Cryptography datatypes +""" diff --git a/helios/datatypes/pkc/elgamal.py b/helios/datatypes/pkc/elgamal.py new file mode 100644 index 0000000000000000000000000000000000000000..24729e6399904a9536b96bc61a290e43e9ef2a3a --- /dev/null +++ b/helios/datatypes/pkc/elgamal.py @@ -0,0 +1,21 @@ +""" +data types for 2011/01 Helios +""" + +from helios.datatypes import LDObject + +class DiscreteLogProof(LDObject): + FIELDS = ['challenge', 'commitment', 'response'] + STRUCTURED_FIELDS = { + 'challenge' : 'core/BigInteger', + 'commitment' : 'core/BigInteger', + 'response' : 'core/BigInteger'} + +class PublicKey(LDObject): + FIELDS = ['y', 'p', 'g', 'q'] + STRUCTURED_FIELDS = { + 'y' : 'core/BigInteger', + 'p' : 'core/BigInteger', + 'g' : 'core/BigInteger', + 'q' : 'core/BigInteger'} + diff --git a/helios/tests.py b/helios/tests.py index a6e52525e7717534c168daf53501e2c169aa18e0..7838baf3262f1d9564332e768b4cdf07ad485c66 100644 --- a/helios/tests.py +++ b/helios/tests.py @@ -98,7 +98,7 @@ class ElectionModelTests(TestCase): self.assertTrue(self.election.has_helios_trustee()) - trustee = self.election.get_helios_trustee() + trustee = self.election.get_helios_trustee() self.assertNotEquals(trustee, None) def test_log(self): @@ -221,10 +221,13 @@ class DatatypeTests(TestCase): def setUp(self): self.election = models.Election.objects.all()[0] + self.election.generate_trustee(ELGAMAL_PARAMS) def test_instantiate(self): - ld_obj = datatypes.LDObject.instantiate(self.election) - + ld_obj = datatypes.LDObject.instantiate(self.election.get_helios_trustee(), '2011/01/Trustee') + foo = ld_obj.serialize() + + ## ## Black box tests