Skip to content
Snippets Groups Projects
Commit 53d42233 authored by Ben Adida's avatar Ben Adida
Browse files

continued porting code to modular architecture for serialization and algorithms

parent 3c4dfb86
Branches
Tags
No related merge requests found
...@@ -12,14 +12,19 @@ from django.utils import simplejson as json ...@@ -12,14 +12,19 @@ from django.utils import simplejson as json
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
class JSONField(models.TextField): class JSONField(models.TextField):
"""JSONField is a generic textfield that neatly serializes/unserializes """
JSON objects seamlessly""" JSONField is a generic textfield that neatly serializes/unserializes
JSON objects seamlessly.
deserialization_params added on 2011-01-09 to provide additional hints at deserialization time
"""
# Used so to_python() is called # Used so to_python() is called
__metaclass__ = models.SubfieldBase __metaclass__ = models.SubfieldBase
def __init__(self, json_type=None, **kwargs): def __init__(self, json_type=None, deserialization_params=None, **kwargs):
self.json_type = json_type self.json_type = json_type
self.deserialization_params = deserialization_params
super(JSONField, self).__init__(**kwargs) super(JSONField, self).__init__(**kwargs)
def to_python(self, value): def to_python(self, value):
...@@ -38,7 +43,7 @@ class JSONField(models.TextField): ...@@ -38,7 +43,7 @@ class JSONField(models.TextField):
parsed_value = json.loads(value) parsed_value = json.loads(value)
if self.json_type and parsed_value: if self.json_type and parsed_value:
parsed_value = self.json_type.fromJSONDict(parsed_value) parsed_value = self.json_type.fromJSONDict(parsed_value, **self.deserialization_params)
return parsed_value return parsed_value
......
"""
ElGamal Algorithms for the Helios Voting System
This is a copy of algs.py now made more El-Gamal specific in naming,
for modularity purposes.
Ben Adida
ben@adida.net
"""
import math, hashlib, logging
import randpool, number
import numtheory
from algs import Utils
class Cryptosystem(object):
def __init__(self):
self.p = None
self.q = None
self.g = None
@classmethod
def generate(cls, n_bits):
"""
generate an El-Gamal environment. Returns an instance
of ElGamal(), with prime p, group size q, and generator g
"""
EG = cls()
# find a prime p such that (p-1)/2 is prime q
EG.p = Utils.random_safe_prime(n_bits)
# q is the order of the group
# FIXME: not always p-1/2
EG.q = (EG.p-1)/2
# find g that generates the q-order subgroup
while True:
EG.g = Utils.random_mpz_lt(EG.p)
if pow(EG.g, EG.q, EG.p) == 1:
break
return EG
def generate_keypair(self):
"""
generates a keypair in the setting
"""
keypair = KeyPair()
keypair.generate(self.p, self.q, self.g)
return keypair
class KeyPair(object):
def __init__(self):
self.pk = PublicKey()
self.sk = SecretKey()
def generate(self, p, q, g):
"""
Generate an ElGamal keypair
"""
self.pk.g = g
self.pk.p = p
self.pk.q = q
self.sk.x = Utils.random_mpz_lt(p)
self.pk.y = pow(g, self.sk.x, p)
self.sk.pk = self.pk
class PublicKey:
def __init__(self):
self.y = None
self.p = None
self.g = None
self.q = None
def encrypt_with_r(self, plaintext, r, encode_message= False):
"""
expecting plaintext.m to be a big integer
"""
ciphertext = Ciphertext()
ciphertext.pk = self
# make sure m is in the right subgroup
if encode_message:
y = plaintext.m + 1
if pow(y, self.q, self.p) == 1:
m = y
else:
m = -y % self.p
else:
m = plaintext.m
ciphertext.alpha = pow(self.g, r, self.p)
ciphertext.beta = (m * pow(self.y, r, self.p)) % self.p
return ciphertext
def encrypt_return_r(self, plaintext):
"""
Encrypt a plaintext and return the randomness just generated and used.
"""
r = Utils.random_mpz_lt(self.q)
ciphertext = self.encrypt_with_r(plaintext, r)
return [ciphertext, r]
def encrypt(self, plaintext):
"""
Encrypt a plaintext, obscure the randomness.
"""
return self.encrypt_return_r(plaintext)[0]
def __mul__(self,other):
if other == 0 or other == 1:
return self
# check p and q
if self.p != other.p or self.q != other.q or self.g != other.g:
raise Exception("incompatible public keys")
result = PublicKey()
result.p = self.p
result.q = self.q
result.g = self.g
result.y = (self.y * other.y) % result.p
return result
def verify_sk_proof(self, dlog_proof, challenge_generator = None):
"""
verify the proof of knowledge of the secret key
g^response = commitment * y^challenge
"""
left_side = pow(self.g, dlog_proof.response, self.p)
right_side = (dlog_proof.commitment * pow(self.y, dlog_proof.challenge, self.p)) % self.p
expected_challenge = challenge_generator(dlog_proof.commitment) % self.q
return ((left_side == right_side) and (dlog_proof.challenge == expected_challenge))
class SecretKey:
def __init__(self):
self.x = None
self.pk = None
def decryption_factor(self, ciphertext):
"""
provide the decryption factor, not yet inverted because of needed proof
"""
return pow(ciphertext.alpha, self.x, self.pk.p)
def decryption_factor_and_proof(self, ciphertext, challenge_generator=None):
"""
challenge generator is almost certainly
EG_fiatshamir_challenge_generator
"""
if not challenge_generator:
challenge_generator = fiatshamir_challenge_generator
dec_factor = self.decryption_factor(ciphertext)
proof = ZKProof.generate(self.pk.g, ciphertext.alpha, self.x, self.pk.p, self.pk.q, challenge_generator)
return dec_factor, proof
def decrypt(self, ciphertext, dec_factor = None, decode_m=False):
"""
Decrypt a ciphertext. Optional parameter decides whether to encode the message into the proper subgroup.
"""
if not dec_factor:
dec_factor = self.decryption_factor(ciphertext)
m = (Utils.inverse(dec_factor, self.pk.p) * ciphertext.beta) % self.pk.p
if decode_m:
# get m back from the q-order subgroup
if m < self.pk.q:
y = m
else:
y = -m % self.pk.p
return Plaintext(y-1, self.pk)
else:
return Plaintext(m, self.pk)
def prove_decryption(self, ciphertext):
"""
given g, y, alpha, beta/(encoded m), prove equality of discrete log
with Chaum Pedersen, and that discrete log is x, the secret key.
Prover sends a=g^w, b=alpha^w for random w
Challenge c = sha1(a,b) with and b in decimal form
Prover sends t = w + xc
Verifier will check that g^t = a * y^c
and alpha^t = b * beta/m ^ c
"""
m = (Utils.inverse(pow(ciphertext.alpha, self.x, self.pk.p), self.pk.p) * ciphertext.beta) % self.pk.p
beta_over_m = (ciphertext.beta * Utils.inverse(m, self.pk.p)) % self.pk.p
# pick a random w
w = Utils.random_mpz_lt(self.pk.q)
a = pow(self.pk.g, w, self.pk.p)
b = pow(ciphertext.alpha, w, self.pk.p)
c = int(hashlib.sha1(str(a) + "," + str(b)).hexdigest(),16)
t = (w + self.x * c) % self.pk.q
return m, {
'commitment' : {'A' : str(a), 'B': str(b)},
'challenge' : str(c),
'response' : str(t)
}
def prove_sk(self, challenge_generator):
"""
Generate a PoK of the secret key
Prover generates w, a random integer modulo q, and computes commitment = g^w mod p.
Verifier provides challenge modulo q.
Prover computes response = w + x*challenge mod q, where x is the secret key.
"""
w = Utils.random_mpz_lt(self.pk.q)
commitment = pow(self.pk.g, w, self.pk.p)
challenge = challenge_generator(commitment) % self.pk.q
response = (w + (self.x * challenge)) % self.pk.q
return DLogProof(commitment, challenge, response)
class Plaintext:
def __init__(self, m = None, pk = None):
self.m = m
self.pk = pk
class Ciphertext:
def __init__(self, alpha=None, beta=None, pk=None):
self.pk = pk
self.alpha = alpha
self.beta = beta
def __mul__(self,other):
"""
Homomorphic Multiplication of ciphertexts.
"""
if type(other) == int and (other == 0 or other == 1):
return self
if self.pk != other.pk:
logging.info(self.pk)
logging.info(other.pk)
raise Exception('different PKs!')
new = Ciphertext()
new.pk = self.pk
new.alpha = (self.alpha * other.alpha) % self.pk.p
new.beta = (self.beta * other.beta) % self.pk.p
return new
def reenc_with_r(self, r):
"""
We would do this homomorphically, except
that's no good when we do plaintext encoding of 1.
"""
new_c = Ciphertext()
new_c.alpha = (self.alpha * pow(self.pk.g, r, self.pk.p)) % self.pk.p
new_c.beta = (self.beta * pow(self.pk.y, r, self.pk.p)) % self.pk.p
new_c.pk = self.pk
return new_c
def reenc_return_r(self):
"""
Reencryption with fresh randomness, which is returned.
"""
r = Utils.random_mpz_lt(self.pk.q)
new_c = self.reenc_with_r(r)
return [new_c, r]
def reenc(self):
"""
Reencryption with fresh randomness, which is kept obscured (unlikely to be useful.)
"""
return self.reenc_return_r()[0]
def __eq__(self, other):
"""
Check for ciphertext equality.
"""
if other == None:
return False
return (self.alpha == other.alpha and self.beta == other.beta)
def generate_encryption_proof(self, plaintext, randomness, challenge_generator):
"""
Generate the disjunctive encryption proof of encryption
"""
# random W
w = Utils.random_mpz_lt(self.pk.q)
# build the proof
proof = ZKProof()
# compute A=g^w, B=y^w
proof.commitment['A'] = pow(self.pk.g, w, self.pk.p)
proof.commitment['B'] = pow(self.pk.y, w, self.pk.p)
# generate challenge
proof.challenge = challenge_generator(proof.commitment);
# Compute response = w + randomness * challenge
proof.response = (w + (randomness * proof.challenge)) % self.pk.q;
return proof;
def simulate_encryption_proof(self, plaintext, challenge=None):
# generate a random challenge if not provided
if not challenge:
challenge = Utils.random_mpz_lt(self.pk.q)
proof = ZKProof()
proof.challenge = challenge
# compute beta/plaintext, the completion of the DH tuple
beta_over_plaintext = (self.beta * Utils.inverse(plaintext.m, self.pk.p)) % self.pk.p
# random response, does not even need to depend on the challenge
proof.response = Utils.random_mpz_lt(self.pk.q);
# now we compute A and B
proof.commitment['A'] = (Utils.inverse(pow(self.alpha, proof.challenge, self.pk.p), self.pk.p) * pow(self.pk.g, proof.response, self.pk.p)) % self.pk.p
proof.commitment['B'] = (Utils.inverse(pow(beta_over_plaintext, proof.challenge, self.pk.p), self.pk.p) * pow(self.pk.y, proof.response, self.pk.p)) % self.pk.p
return proof
def generate_disjunctive_encryption_proof(self, plaintexts, real_index, randomness, challenge_generator):
# note how the interface is as such so that the result does not reveal which is the real proof.
proofs = [None for p in plaintexts]
# go through all plaintexts and simulate the ones that must be simulated.
for p_num in range(len(plaintexts)):
if p_num != real_index:
proofs[p_num] = self.simulate_encryption_proof(plaintexts[p_num])
# the function that generates the challenge
def real_challenge_generator(commitment):
# set up the partial real proof so we're ready to get the hash
proofs[real_index] = ZKProof()
proofs[real_index].commitment = commitment
# get the commitments in a list and generate the whole disjunctive challenge
commitments = [p.commitment for p in proofs]
disjunctive_challenge = challenge_generator(commitments);
# now we must subtract all of the other challenges from this challenge.
real_challenge = disjunctive_challenge
for p_num in range(len(proofs)):
if p_num != real_index:
real_challenge = real_challenge - proofs[p_num].challenge
# make sure we mod q, the exponent modulus
return real_challenge % self.pk.q
# do the real proof
real_proof = self.generate_encryption_proof(plaintexts[real_index], randomness, real_challenge_generator)
# set the real proof
proofs[real_index] = real_proof
return ZKDisjunctiveProof(proofs)
def verify_encryption_proof(self, plaintext, proof):
"""
Checks for the DDH tuple g, y, alpha, beta/plaintext.
(PoK of randomness r.)
Proof contains commitment = {A, B}, challenge, response
"""
# check that g^response = A * alpha^challenge
first_check = (pow(self.pk.g, proof.response, self.pk.p) == ((pow(self.alpha, proof.challenge, self.pk.p) * proof.commitment['A']) % self.pk.p))
# check that y^response = B * (beta/m)^challenge
beta_over_m = (self.beta * Utils.inverse(plaintext.m, self.pk.p)) % self.pk.p
second_check = (pow(self.pk.y, proof.response, self.pk.p) == ((pow(beta_over_m, proof.challenge, self.pk.p) * proof.commitment['B']) % self.pk.p))
# print "1,2: %s %s " % (first_check, second_check)
return (first_check and second_check)
def verify_disjunctive_encryption_proof(self, plaintexts, proof, challenge_generator):
"""
plaintexts and proofs are all lists of equal length, with matching.
overall_challenge is what all of the challenges combined should yield.
"""
for i in range(len(plaintexts)):
# if a proof fails, stop right there
if not self.verify_encryption_proof(plaintexts[i], proof.proofs[i]):
print "bad proof %s, %s, %s" % (i, plaintexts[i], proof.proofs[i])
return False
# logging.info("made it past the two encryption proofs")
# check the overall challenge
return (challenge_generator([p.commitment for p in proof.proofs]) == (sum([p.challenge for p in proof.proofs]) % self.pk.q))
def verify_decryption_proof(self, plaintext, proof):
"""
Checks for the DDH tuple g, alpha, y, beta/plaintext
(PoK of secret key x.)
"""
return False
def verify_decryption_factor(self, dec_factor, dec_proof, public_key):
"""
when a ciphertext is decrypted by a dec factor, the proof needs to be checked
"""
pass
def decrypt(self, decryption_factors, public_key):
"""
decrypt a ciphertext given a list of decryption factors (from multiple trustees)
For now, no support for threshold
"""
running_decryption = self.beta
for dec_factor in decryption_factors:
running_decryption = (running_decryption * Utils.inverse(dec_factor, public_key.p)) % public_key.p
return running_decryption
def to_string(self):
return "%s,%s" % (self.alpha, self.beta)
@classmethod
def from_string(cls, str):
"""
expects alpha,beta
"""
split = str.split(",")
return cls.from_dict({'alpha' : split[0], 'beta' : split[1]})
class ZKProof(object):
def __init__(self):
self.commitment = {'A':None, 'B':None}
self.challenge = None
self.response = None
@classmethod
def generate(cls, little_g, little_h, x, p, q, challenge_generator):
"""
generate a DDH tuple proof, where challenge generator is
almost certainly EG_fiatshamir_challenge_generator
"""
# generate random w
w = Utils.random_mpz_lt(q)
# create proof instance
proof = cls()
# compute A = little_g^w, B=little_h^w
proof.commitment['A'] = pow(little_g, w, p)
proof.commitment['B'] = pow(little_h, w, p)
# get challenge
proof.challenge = challenge_generator(proof.commitment)
# compute response
proof.response = (w + (x * proof.challenge)) % q
# return proof
return proof
def verify(self, little_g, little_h, big_g, big_h, p, q, challenge_generator=None):
"""
Verify a DH tuple proof
"""
# check that little_g^response = A * big_g^challenge
first_check = (pow(little_g, self.response, p) == ((pow(big_g, self.challenge, p) * self.commitment['A']) % p))
# check that little_h^response = B * big_h^challenge
second_check = (pow(little_h, self.response, p) == ((pow(big_h, self.challenge, p) * self.commitment['B']) % p))
# check the challenge?
third_check = True
if challenge_generator:
third_check = (self.challenge == challenge_generator(self.commitment))
return (first_check and second_check and third_check)
class ZKDisjunctiveProof:
def __init__(self, proofs = None):
self.proofs = proofs
class DLogProof(object):
def __init__(self, commitment, challenge, response):
self.commitment = commitment
self.challenge = challenge
self.response = response
def disjunctive_challenge_generator(commitments):
array_to_hash = []
for commitment in commitments:
array_to_hash.append(str(commitment['A']))
array_to_hash.append(str(commitment['B']))
string_to_hash = ",".join(array_to_hash)
return int(hashlib.sha1(string_to_hash).hexdigest(),16)
# a challenge generator for Fiat-Shamir with A,B commitment
def fiatshamir_challenge_generator(commitment):
return disjunctive_challenge_generator([commitment])
def DLog_challenge_generator(commitment):
string_to_hash = str(commitment)
return int(hashlib.sha1(string_to_hash).hexdigest(),16)
...@@ -79,9 +79,12 @@ class LDObject(object): ...@@ -79,9 +79,12 @@ class LDObject(object):
# fields to serialize # fields to serialize
FIELDS = [] FIELDS = []
# structured fields # structured fields are other LD objects, not simple types
STRUCTURED_FIELDS = {} STRUCTURED_FIELDS = {}
# the underlying object type, which contains algorithms, to instantiate by default
WRAPPED_OBJ_CLASS = None
def __init__(self, wrapped_obj): def __init__(self, wrapped_obj):
self.wrapped_obj = wrapped_obj self.wrapped_obj = wrapped_obj
self.structured_fields = {} self.structured_fields = {}
...@@ -151,16 +154,48 @@ class LDObject(object): ...@@ -151,16 +154,48 @@ class LDObject(object):
val[f] = self.process_value_out(f, getattr(self.wrapped_obj, f)) val[f] = self.process_value_out(f, getattr(self.wrapped_obj, f))
return val return val
def loadDataFromDict(self, d):
"""
load data from a dictionary
"""
# the structured fields
structured_fields = self.STRUCTURED_FIELDS.keys()
# go through the fields and set them properly
# on the newly instantiated object
for f in self.FIELDS:
if f in structured_fields:
# a structured ld field, recur
sub_ld_object = self.fromDict(d[f], type_hint = self.STRUCTURED_FIELDS[f])
self.structured_fields[f] = sub_ld_object
# set the field on the wrapped object too
setattr(self.wrapped_obj, f, sub_ld_object.wrapped_obj)
else:
# a simple type
new_val = self.process_value_in(f, d[f])
setattr(self.wrapped_obj, f, new_val)
@classmethod @classmethod
def fromDict(cls, d): def fromDict(cls, d, type_hint=None):
raise Exception("not a good idea yet") # the LD type is either in d or in type_hint
# FIXME: get this from the dictionary itself
ld_type = type_hint
# get the LD class so we know what wrapped object to instantiate
ld_cls = cls.get_class(ld_type)
wrapped_obj_cls = ld_cls.WRAPPED_OBJ_CLASS
wrapped_obj = wrapped_obj_cls()
# go through the keys and fix them # then instantiate the LD object and load the data
new_d = {} ld_obj = ld_cls(wrapped_obj)
for k in d.keys(): ld_obj.loadDataFromDict(d)
new_d[str(k)] = d[k]
return cls(**new_d) return ld_obj
fromJSONDict = fromDict
@property @property
def hash(self): def hash(self):
...@@ -181,7 +216,7 @@ class LDObject(object): ...@@ -181,7 +216,7 @@ class LDObject(object):
return field_value return field_value
def _process_value_in(self, field_name, field_value): def _process_value_in(self, field_name, field_value):
return None return field_value
def process_value_out(self, field_name, field_value): def process_value_out(self, field_name, field_value):
""" """
......
...@@ -9,6 +9,7 @@ class BigInteger(LDObject): ...@@ -9,6 +9,7 @@ class BigInteger(LDObject):
A big integer is an integer serialized as a string. A big integer is an integer serialized as a string.
We may want to b64 encode here soon. We may want to b64 encode here soon.
""" """
WRAPPED_OBJ_CLASS = int
def toDict(self): def toDict(self):
if self.wrapped_obj: if self.wrapped_obj:
...@@ -16,6 +17,10 @@ class BigInteger(LDObject): ...@@ -16,6 +17,10 @@ class BigInteger(LDObject):
else: else:
return None return None
def loadDataFromDict(self, d):
"take a string and cast it to an int -- which is a big int too"
self.wrapped_obj = int(d)
class Timestamp(LDObject): class Timestamp(LDObject):
def toDict(self): def toDict(self):
if self.wrapped_obj: if self.wrapped_obj:
......
...@@ -18,6 +18,7 @@ class DictObject(object): ...@@ -18,6 +18,7 @@ class DictObject(object):
## ##
class LegacyObject(LDObject): class LegacyObject(LDObject):
WRAPPED_OBJ_CLASS = dict
USE_JSON_LD = False USE_JSON_LD = False
class Election(LegacyObject): class Election(LegacyObject):
...@@ -142,3 +143,15 @@ class DLogProof(LegacyObject): ...@@ -142,3 +143,15 @@ class DLogProof(LegacyObject):
super(DLogProof, self).__init__(DictObject(wrapped_obj)) super(DLogProof, self).__init__(DictObject(wrapped_obj))
else: else:
super(DLogProof, self).__init__(wrapped_obj) super(DLogProof, self).__init__(wrapped_obj)
class Result(LegacyObject):
pass
class Questions(LegacyObject):
pass
class Tally(LegacyObject):
pass
class Eligibility(LegacyObject):
pass
...@@ -3,6 +3,7 @@ data types for 2011/01 Helios ...@@ -3,6 +3,7 @@ data types for 2011/01 Helios
""" """
from helios.datatypes import LDObject from helios.datatypes import LDObject
from helios.crypto import elgamal
class DiscreteLogProof(LDObject): class DiscreteLogProof(LDObject):
FIELDS = ['challenge', 'commitment', 'response'] FIELDS = ['challenge', 'commitment', 'response']
...@@ -12,6 +13,8 @@ class DiscreteLogProof(LDObject): ...@@ -12,6 +13,8 @@ class DiscreteLogProof(LDObject):
'response' : 'core/BigInteger'} 'response' : 'core/BigInteger'}
class PublicKey(LDObject): class PublicKey(LDObject):
WRAPPED_OBJ_CLASS = elgamal.PublicKey
FIELDS = ['y', 'p', 'g', 'q'] FIELDS = ['y', 'p', 'g', 'q']
STRUCTURED_FIELDS = { STRUCTURED_FIELDS = {
'y' : 'core/BigInteger', 'y' : 'core/BigInteger',
......
...@@ -57,13 +57,22 @@ class Election(HeliosModel): ...@@ -57,13 +57,22 @@ class Election(HeliosModel):
private_p = models.BooleanField(default=False, null=False) private_p = models.BooleanField(default=False, null=False)
description = models.TextField() description = models.TextField()
public_key = JSONField(algs.EGPublicKey, null=True) public_key = JSONField(datatypes.LDObject,
private_key = JSONField(algs.EGSecretKey, null=True) deserialization_params = {'type_hint' : 'legacy/EGPublicKey'},
questions = JSONField(null=True) null=True)
private_key = JSONField(datatypes.LDObject,
deserialization_params = {'type_hint' : 'legacy/EGSecretKey'},
null=True)
questions = JSONField(datatypes.LDObject,
deserialization_params = {'type_hint' : 'legacy/Questions'},
null=True)
# eligibility is a JSON field, which lists auth_systems and eligibility details for that auth_system, e.g. # eligibility is a JSON field, which lists auth_systems and eligibility details for that auth_system, e.g.
# [{'auth_system': 'cas', 'constraint': [{'year': 'u12'}, {'year':'u13'}]}, {'auth_system' : 'password'}, {'auth_system' : 'openid', 'constraint': [{'host':'http://myopenid.com'}]}] # [{'auth_system': 'cas', 'constraint': [{'year': 'u12'}, {'year':'u13'}]}, {'auth_system' : 'password'}, {'auth_system' : 'openid', 'constraint': [{'host':'http://myopenid.com'}]}]
eligibility = JSONField(null=True) eligibility = JSONField(datatypes.LDObject,
deserialization_params = {'type_hint' : 'legacy/Eligibility'},
null=True)
# open registration? # open registration?
# this is now used to indicate the state of registration, # this is now used to indicate the state of registration,
...@@ -115,12 +124,17 @@ class Election(HeliosModel): ...@@ -115,12 +124,17 @@ class Election(HeliosModel):
# encrypted tally, each a JSON string # encrypted tally, each a JSON string
# used only for homomorphic tallies # used only for homomorphic tallies
encrypted_tally = JSONField(electionalgs.Tally, null=True) encrypted_tally = JSONField(datatypes.LDObject,
deserialization_params={'type_hint': 'legacy/Tally'},
null=True)
# results of the election # results of the election
result = JSONField(null=True) result = JSONField(datatypes.LDObject,
deserialization_params = {'type_hint' : 'legacy/Result'},
null=True)
# decryption proof, a JSON object # decryption proof, a JSON object
# no longer needed since it's all trustees
result_proof = JSONField(null=True) result_proof = JSONField(null=True)
@property @property
...@@ -585,7 +599,9 @@ class Voter(HeliosModel): ...@@ -585,7 +599,9 @@ class Voter(HeliosModel):
alias = models.CharField(max_length = 100, null=True) alias = models.CharField(max_length = 100, null=True)
# we keep a copy here for easy tallying # we keep a copy here for easy tallying
vote = JSONField(electionalgs.EncryptedVote, null=True) vote = JSONField(datatypes.LDObject,
deserialization_params = {'type_hint': 'legacy/EncryptedVote'},
null=True)
vote_hash = models.CharField(max_length = 100, null=True) vote_hash = models.CharField(max_length = 100, null=True)
cast_at = models.DateTimeField(auto_now_add=False, null=True) cast_at = models.DateTimeField(auto_now_add=False, null=True)
...@@ -745,8 +761,9 @@ class CastVote(HeliosModel): ...@@ -745,8 +761,9 @@ class CastVote(HeliosModel):
# the reference to the voter provides the voter_uuid # the reference to the voter provides the voter_uuid
voter = models.ForeignKey(Voter) voter = models.ForeignKey(Voter)
# a json array, which should contain election_uuid and election_hash # the actual encrypted vote
vote = JSONField(electionalgs.EncryptedVote) vote = JSONField(datatypes.LDObject,
deserialization_params = {'type_hint' : 'legacy/EncryptedVote'})
# cache the hash of the vote # cache the hash of the vote
vote_hash = models.CharField(max_length=100) vote_hash = models.CharField(max_length=100)
...@@ -866,20 +883,31 @@ class Trustee(HeliosModel): ...@@ -866,20 +883,31 @@ class Trustee(HeliosModel):
secret = models.CharField(max_length=100) secret = models.CharField(max_length=100)
# public key # public key
public_key = JSONField(algs.EGPublicKey, null=True) public_key = JSONField(datatypes.LDObject,
deserialization_params = {'type_hint': 'legacy/EGPublicKey'},
null=True)
public_key_hash = models.CharField(max_length=100) public_key_hash = models.CharField(max_length=100)
# secret key # secret key
# if the secret key is present, this means # if the secret key is present, this means
# Helios is playing the role of the trustee. # Helios is playing the role of the trustee.
secret_key = JSONField(algs.EGSecretKey, null=True) secret_key = JSONField(datatypes.LDObject,
deserialization_params = {'type_hint': 'legacy/EGSecretKey'},
null=True)
# proof of knowledge of secret key # proof of knowledge of secret key
pok = JSONField(algs.DLogProof, null=True) pok = JSONField(datatypes.LDObject,
deserialization_params = {'type_hint': 'legacy/DLogProof'},
null=True)
# decryption factors # decryption factors
decryption_factors = JSONField(null=True) decryption_factors = JSONField(datatypes.LDObject,
decryption_proofs = JSONField(null=True) deserialization_params = {'type_hint' : datatypes.arrayOf(datatypes.arrayOf('core/BigInteger'))},
null=True)
decryption_proofs = JSONField(datatypes.LDObject,
deserialization_params = {'type_hint' : datatypes.arrayOf(datatypes.arrayOf('legacy/DLogProof'))},
null=True)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
""" """
......
...@@ -227,6 +227,15 @@ class DatatypeTests(TestCase): ...@@ -227,6 +227,15 @@ class DatatypeTests(TestCase):
ld_obj = datatypes.LDObject.instantiate(self.election.get_helios_trustee(), '2011/01/Trustee') ld_obj = datatypes.LDObject.instantiate(self.election.get_helios_trustee(), '2011/01/Trustee')
foo = ld_obj.serialize() foo = ld_obj.serialize()
def test_from_dict(self):
ld_obj = datatypes.LDObject.fromDict({
'y' : '1234',
'p' : '23434',
'g' : '2343243242',
'q' : '2343242343434'}, type_hint = 'pkc/elgamal/PublicKey')
import pdb; pdb.set_trace()
## ##
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment