Skip to content

Commit

Permalink
added verification of trustees
Browse files Browse the repository at this point in the history
  • Loading branch information
benadida committed Sep 14, 2008
1 parent b70bbcd commit 149de80
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 29 deletions.
4 changes: 2 additions & 2 deletions index.yaml
Expand Up @@ -17,7 +17,7 @@ indexes:
- name: exponent
direction: desc

# Used 110 times in query history.
# Used 126 times in query history.
- kind: KeyShare
properties:
- name: election
Expand All @@ -43,7 +43,7 @@ indexes:
- name: tallied_at
- name: cast_id

# Used 199 times in query history.
# Used 206 times in query history.
- kind: Voter
properties:
- name: election
Expand Down
43 changes: 34 additions & 9 deletions static/elgamal.js
Expand Up @@ -49,17 +49,22 @@ ElGamal.PublicKey = Class.extend({
verifyKnowledgeOfSecretKey: function(proof, challenge_generator) {
// if challenge_generator is present, we have to check that the challenge was properly generated.
if (challenge_generator != null) {
if (!proof.challenge.equals(challenge_generator(proof.s))) {
if (!proof.challenge.equals(challenge_generator(proof.commitment))) {
return false;
}
}

// verify that g^response = s * y^challenge
var check = this.g.modPow(proof.response, this.p).equals(this.y.modPow(proof.challenge, this.p).multiply(proof.s).mod(this.p));
var check = this.g.modPow(proof.response, this.p).equals(this.y.modPow(proof.challenge, this.p).multiply(proof.commitment).mod(this.p));

return check;
},

// check if the decryption factor is correct for this public key, given the proof
verifyDecryptionFactor: function(ciphertext, decryption_factor, decryption_proof, challenge_generator) {
return decryption_proof.verify(this.g, ciphertext.alpha, this.y, decryption_factor, this.p, this.q, challenge_generator);
},

multiply: function(other) {
// base condition
if (other == 0 || other == 1) {
Expand All @@ -74,6 +79,10 @@ ElGamal.PublicKey = Class.extend({

var new_pk = new ElGamal.PublicKey(this.p, this.q, this.g, this.y.multiply(other.y).mod(this.p));
return new_pk;
},

equals: function(other) {
return (this.p.equals(other.p) && this.q.equals(other.q) && this.g.equals(other.g) && this.y.equals(other.y));
}

});
Expand Down Expand Up @@ -141,19 +150,16 @@ ElGamal.SecretKey = Class.extend({
// generate random w
var w = Random.getRandomInteger(this.pk.q);

// FIXME: build an abstraction for this single PoK of discrete log
var proof = {};

// compute s = g^w for random w.
proof.s = this.pk.g.modPow(w, this.pk.p);
var s = this.pk.g.modPow(w, this.pk.p);

// get challenge
proof.challenge = challenge_generator(proof.s);
var challenge = challenge_generator(s);

// compute response = w + x * challenge
proof.response = w.add(this.x.multiply(proof.challenge).mod(this.pk.q));
var response = w.add(this.x.multiply(challenge).mod(this.pk.q));

return proof;
return new ElGamal.DLogProof(s, challenge, response);
}
});

Expand Down Expand Up @@ -482,6 +488,25 @@ ElGamal.encrypt = function(pk, plaintext, r) {
return new ElGamal.Ciphertext(alpha, beta, pk);
};

//
// DLog Proof
//
ElGamal.DLogProof = Class.extend({
init: function(commitment, challenge, response) {
this.commitment = commitment;
this.challenge = challenge;
this.response = response;
},

toJSONObject: function() {
return {'challenge' : this.challenge.toJSONObject(), 'commitment': this.commitment.toJSONObject(), 'response': this.response.toJSONObject()};
}
});

ElGamal.DLogProof.fromJSONObject = function(d) {
return new ElGamal.DLogProof(BigInt.fromJSONObject(d.commitment || d.s), BigInt.fromJSONObject(d.challenge), BigInt.fromJSONObject(d.response));
};

// a challenge generator based on a list of commitments of
// proofs of knowledge of plaintext. Just appends A and B with commas.
ElGamal.disjunctive_challenge_generator = function(commitments) {
Expand Down
45 changes: 45 additions & 0 deletions static/helios.js
Expand Up @@ -397,3 +397,48 @@ HELIOS.EncryptedVote.fromJSONObject = function(d, election) {
return ev;
};

//
// distributed decryption : Trustees
//

// a utility function for jsonifying a list of lists of items
HELIOS.jsonify_list_of_lists = function(lol) {
if (!lol)
return null;

return $(lol).map(function(i, sublist) {return $(sublist).map(function(j, item) {return item.toJSONObject();})});
};

// a utility function for doing the opposite with an item-level de-jsonifier
HELIOS.dejsonify_list_of_lists = function(lol, item_dejsonifier) {
if (!lol)
return null;

return $(lol).map(function(i, sublist) {return $(sublist).map(function(j, item) {return item_dejsonifier(item);})});
}

HELIOS.Trustee = Class.extend({
init: function(email, name, pk, pok, decryption_factors, decryption_proofs) {
this.email = email;
this.name = name;
this.pk = pk;
this.pok = pok;
this.decryption_factors = decryption_factors;
this.decryption_proofs = decryption_proofs;
},

toJSONObject: function() {
return {
'decryption_factors' : HELIOS.jsonify_list_of_lists(this.decryption_factors),
'decryption_proofs' : HELIOS.jsonify_list_of_list(this.decryption_proofs),
'email' : this.email, 'name' : this.name, 'pk' : this.pk.toJSONObject(), 'pok' : this.pok.toJSONObject()
}
}
});

HELIOS.Trustee.fromJSONObject = function(d) {
return new HELIOS.Trustee(d.email, d.name,
ElGamal.PublicKey.fromJSONObject(d.pk), ElGamal.DLogProof.fromJSONObject(d.pok),
HELIOS.dejsonify_list_of_lists(d.decryption_factors, BigInt.fromJSONObject),
HELIOS.dejsonify_list_of_lists(d.decryption_proofs, ElGamal.Proof.fromJSONObject));
};
100 changes: 82 additions & 18 deletions templates/election/verifier.tmpl
Expand Up @@ -124,27 +124,91 @@ function verify_ballots_and_tally() {

// get the results
Helios.get_election_result({'election_id': ELECTION_ID}, function(results) {
// get the proof
Helios.get_election_result_proof({'election_id': ELECTION_ID}, function(results_proof) {
\$(TALLY).each(function(q_num, q) {
result_append("Question #" + q_num + ": " + ELECTION.questions[q_num].short_name);
\$(q).each(function(a_num, a) {
var plaintext = new ElGamal.Plaintext(ELECTION.pk.g.modPow(BigInt.fromInt(results[q_num][a_num]), ELECTION.pk.p), ELECTION.pk);
var proof = ElGamal.Proof.fromJSONObject(results_proof[q_num][a_num]);
var check = a.verifyDecryptionProof(plaintext, proof);

VALID_P = VALID_P && check;

result_append("- " + ELECTION.questions[q_num].answers[a_num] + " - " + results[q_num][a_num] + " -- " + pretty_result(check));
// load the trustees in case there are some
Helios.get_election_trustees({'election_id' : ELECTION_ID}, function(trustees) {
// if length of trustees is 0, get the single proof
if (trustees.length ==0 ) {
// get the proof
Helios.get_election_result_proof({'election_id': ELECTION_ID}, function(results_proof) {
\$(TALLY).each(function(q_num, q) {
result_append("Question #" + q_num + ": " + ELECTION.questions[q_num].short_name);
\$(q).each(function(a_num, a) {
var plaintext = new ElGamal.Plaintext(ELECTION.pk.g.modPow(BigInt.fromInt(results[q_num][a_num]), ELECTION.pk.p), ELECTION.pk);
var proof = ElGamal.Proof.fromJSONObject(results_proof[q_num][a_num]);
var check = a.verifyDecryptionProof(plaintext, proof);

VALID_P = VALID_P && check;

result_append("- " + ELECTION.questions[q_num].answers[a_num] + " - " + results[q_num][a_num] + " -- " + pretty_result(check));
});

});

if (VALID_P)
result_append("ELECTION VERIFIED!");
else
result_append("ELECTION FAILED!");

});
} else {
result_append("Trustees found.")

trustees = \$(trustees).map(function(i, trustee) {return HELIOS.Trustee.fromJSONObject(trustee)});

var combined_key = 1;

// verify the keys
\$(trustees).each(function(i, trustee) {
if (trustee.pk.verifyKnowledgeOfSecretKey(trustee.pok, ElGamal.fiatshamir_dlog_challenge_generator)) {
result_append("Trustee PK for " + trustee.email + " VERIFIED.");
} else {
result_append("==== ERROR for PK of trustee " + trustee.email);
}

combined_key = trustee.pk.multiply(combined_key);
});

});
// verify the combination of the keys into the final public key
if (combined_key.equals(ELECTION.pk)) {
result_append("election public key MATCHES");
} else {
result_append("==== ERROR, election public key doesn't match");
}

// verify the individual results.
\$(TALLY).each(function(q_num, q) {
result_append("Question #" + q_num + ": " + ELECTION.questions[q_num].short_name);
\$(q).each(function(a_num, a) {
var check = true;
result_append("Answer #" + a_num);
var decryption_factors = [];
// go through the trustees' decryption factors
\$(trustees).each(function(t_num, trustee) {
if (trustee.pk.verifyDecryptionFactor(a, trustee.decryption_factors[q_num][a_num],
trustee.decryption_proofs[q_num][a_num], ElGamal.fiatshamir_challenge_generator)) {
result_append("-- Trustee " + trustee.email + " decryption factor verifies");
} else {
result_append("==== ERROR: Trustee " + trustee.email + " decryption factor does not verify");
check= false;
}

decryption_factors.push(trustee.decryption_factors[q_num][a_num]);
});

// recheck decryption factors
var expected_value = ELECTION.pk.g.modPow(BigInt.fromInt(results[q_num][a_num]), ELECTION.pk.p);
var recomputed_value = a.decrypt(decryption_factors).getM();
if (expected_value.equals(recomputed_value)) {
result_append("--- choice VERIFIED");
} else {
result_append("==== ERROR: choice not verified");
check = false;
}

if (VALID_P)
result_append("ELECTION VERIFIED!");
else
result_append("ELECTION FAILED!");

result_append("- " + ELECTION.questions[q_num].answers[a_num] + " - " + results[q_num][a_num] + " -- " + pretty_result(check));
});
});
}
});
});

Expand Down

0 comments on commit 149de80

Please sign in to comment.