Commit adb01d49 authored by Stephane Glondu's avatar Stephane Glondu
Browse files

Remove verification and auditing code from booth

It is not updated and gets in the way.
parent 33ba09a2
......@@ -46,25 +46,6 @@ ElGamal.PublicKey = Class.extend({
return {g : this.g.toJSONObject(), p : this.p.toJSONObject(), q : this.q.toJSONObject(), y : this.y.toJSONObject()};
},
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.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.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) {
......@@ -225,23 +206,6 @@ ElGamal.Ciphertext = Class.extend({
return ElGamal.Proof.simulate(this.pk.g, this.pk.y, this.alpha, beta_over_plaintext, this.pk.p, this.pk.q, challenge);
},
verifyProof: function(plaintext, proof, challenge_generator) {
// DH tuple to verify is
// g, y, alpha, beta/m
var beta_over_m = this.beta.multiply(plaintext.m.modInverse(this.pk.p)).mod(this.pk.p);
return proof.verify(this.pk.g, this.pk.y, this.alpha, beta_over_m, this.pk.p, this.pk.q, challenge_generator);
},
verifyDecryptionProof: function(plaintext, proof, challenge_generator) {
// DH tuple to verify is
// g, alpha, y, beta/m
// since the proven dlog is the secret key x, y=g^x.
var beta_over_m = this.beta.multiply(plaintext.m.modInverse(this.pk.p)).mod(this.pk.p);
return proof.verify(this.pk.g, this.alpha, this.pk.y, beta_over_m, this.pk.p, this.pk.q, challenge_generator);
},
generateDisjunctiveProof: function(list_of_plaintexts, real_index, randomness, challenge_generator) {
// go through all plaintexts and simulate the ones that must be simulated.
// note how the interface is as such so that the result does not reveal which is the real proof.
......@@ -288,30 +252,6 @@ ElGamal.Ciphertext = Class.extend({
return new ElGamal.DisjunctiveProof(proofs);
},
verifyDisjunctiveProof: function(list_of_plaintexts, disj_proof, challenge_generator) {
var result = true;
var proofs = disj_proof.proofs;
// for loop because we want to bail out of the inner loop
// if we fail one of the verifications.
for (var i=0; i < list_of_plaintexts.length; i++) {
if (!this.verifyProof(list_of_plaintexts[i], proofs[i]))
return false;
}
// check the overall challenge
// first the one expected from the proofs
var commitments = _(proofs).map(function(proof) {return proof.commitment;});
var expected_challenge = challenge_generator(commitments);
// then the one that is the sum of the previous one.
var sum = new BigInt("0", 10); var self = this;
_(proofs).each(function(proof) {sum = sum.add(proof.challenge).mod(self.pk.q);});
return expected_challenge.equals(sum);
},
equals: function(other) {
return (this.alpha.equals(other.alpha) && this.beta.equals(other.beta));
}
......@@ -390,23 +330,6 @@ ElGamal.Proof = Class.extend({
commitment : {A: this.commitment.A.toJSONObject(), B: this.commitment.B.toJSONObject()},
response : this.response.toJSONObject()
}
},
// verify a DH tuple proof
verify: function(little_g, little_h, big_g, big_h, p, q, challenge_generator) {
// check that little_g^response = A * big_g^challenge
var first_check = little_g.modPow(this.response, p).equals(big_g.modPow(this.challenge, p).multiply(this.commitment.A).mod(p));
// check that little_h^response = B * big_h^challenge
var second_check = little_h.modPow(this.response, p).equals(big_h.modPow(this.challenge, p).multiply(this.commitment.B).mod(p));
var third_check = true;
if (challenge_generator) {
third_check = this.challenge.equals(challenge_generator(this.commitment));
}
return (first_check && second_check && third_check);
}
});
......
......@@ -327,27 +327,6 @@ HELIOS.EncryptedAnswer = Class.extend({
this.randomness = null;
},
// FIXME: should verifyEncryption really generate proofs? Overkill.
verifyEncryption: function(question, pk) {
var result = this.doEncryption(question, this.answer, pk, this.randomness);
// check that we have the same number of ciphertexts
if (result.choices.length != this.choices.length) {
return false;
}
// check the ciphertexts
for (var i=0; i<result.choices.length; i++) {
if (!result.choices[i].equals(this.choices[i])) {
// alert ("oy: " + result.choices[i] + "/" + this.choices[i]);
return false;
}
}
// we made it, we're good
return true;
},
toString: function() {
// get each ciphertext as a JSON string
var choices_strings = _(this.choices).map(function(c) {return c.toString();});
......@@ -454,14 +433,6 @@ HELIOS.EncryptedVote = Class.extend({
});
},
verifyEncryption: function(questions, pk) {
var overall_result = true;
_(this.encrypted_answers).each(function(ea, i) {
overall_result = overall_result && ea.verifyEncryption(questions[i], pk);
});
return overall_result;
},
toJSONObject: function(include_plaintext) {
var answers = _(this.encrypted_answers).map(function(ea,i) {
return ea.toJSONObject(include_plaintext);
......@@ -478,53 +449,6 @@ HELIOS.EncryptedVote = Class.extend({
get_hash: function() {
return b64_sha256(JSON.stringify(this.toJSONObject()));
},
get_audit_trail: function() {
return this.toJSONObject(true);
},
verifyProofs: function(pk, outcome_callback) {
var zero_or_one = UTILS.generate_plaintexts(pk, 0, 1);
var VALID_P = true;
var self = this;
// for each question and associate encrypted answer
_(this.encrypted_answers).each(function(enc_answer, ea_num) {
var overall_result = 1;
// the max number of answers (decides whether this is approval or not and requires an overall proof)
var max = self.election.questions[ea_num].max;
// go through each individual proof
_(enc_answer.choices).each(function(choice, choice_num) {
var result = choice.verifyDisjunctiveProof(zero_or_one, enc_answer.individual_proofs[choice_num], ElGamal.disjunctive_challenge_generator);
outcome_callback(ea_num, choice_num, result, choice);
VALID_P = VALID_P && result;
// keep track of homomorphic product, if needed
if (max != null)
overall_result = choice.multiply(overall_result);
});
if (max != null) {
// possible plaintexts [0, 1, .. , question.max]
var plaintexts = UTILS.generate_plaintexts(pk, self.election.questions[ea_num].min, self.election.questions[ea_num].max);
// check the proof on the overall product
var overall_check = overall_result.verifyDisjunctiveProof(plaintexts, enc_answer.overall_proof, ElGamal.disjunctive_challenge_generator);
outcome_callback(ea_num, null, overall_check, null);
VALID_P = VALID_P && overall_check;
} else {
// check to make sure the overall_proof is null, since it's approval voting
VALID_P = VALID_P && (enc_answer.overall_proof == null)
}
});
return VALID_P;
}
});
......
<h3>Your audited ballot</h3>
<p>
<b><u>IMPORTANT</u></b>: this ballot, now that it has been audited, <em>will not be tallied</em>.<br />
To cast a ballot, you must click the "Back to Voting" button below, re-encrypt it, and choose "cast" instead of "audit."
</p>
<p>
<b>Why?</b> Helios prevents you from auditing and casting the same ballot to provide you with some protection against coercion.
</p>
<p>
<b>Now what?</b> <a onclick="$('#audit_trail').select(); return false;" href="#">Select your ballot audit info</a>, copy it to your clipboard, then use the <a target="_blank" href="single-ballot-verify.html?election_url={$T.election_url}">ballot verifier</a> to verify it.<br />
Once you're satisfied, click the "back to voting" button to re-encrypt and cast your ballot.
</p>
<form action="#">
<textarea name="audit_trail" id="audit_trail" cols="80" rows="10" wrap="soft">
{$T.audit_trail}
</textarea>
<br /><br />
Before going back to voting,<br />
you can post this audited ballot to the Helios tracking center so that others might double-check the verification of this ballot.
<br /><br />
<b>Even if you post your audited ballot, you must go back to voting and choose "cast" if you want your vote to count.</b>
<br /><br />
<input type="button" value="post audited ballot to tracking center" onclick="$(this).attr('disabled', 'disabled');BOOTH.post_audited_ballot();" id="post_audited_ballot_button" class="pretty" />
&nbsp;&nbsp; &nbsp;
<input type="button" value="back to voting" onclick="BOOTH.reset_ciphertexts();BOOTH.show_confirm();" class="pretty" />
</form>
// helper functions for verifying a ballot
// assumes all of Helios machinery is loaded
function verify_ballot(election_raw_json, encrypted_vote_json, status_cb) {
var overall_result = true;
try {
election = HELIOS.Election.fromJSONString(election_raw_json);
var election_hash = election.get_hash();
status_cb("election fingerprint is " + election_hash);
// display ballot fingerprint
encrypted_vote = HELIOS.EncryptedVote.fromJSONObject(encrypted_vote_json, election);
status_cb("smart ballot tracker is " + encrypted_vote.get_hash());
// check the hash
if (election_hash == encrypted_vote.election_hash) {
status_cb("election fingerprint matches ballot");
} else {
overall_result = false;
status_cb("PROBLEM = election fingerprint does not match");
}
// display the ballot as it is claimed to be
status_cb("Ballot Contents:");
_(election.questions).each(function(q, qnum) {
if (q.tally_type != "homomorphic") {
status_cb("WARNING: the tally type for this question is not homomorphic. Verification may fail because this verifier is only set up to handle homomorphic ballots.");
}
var answer_pretty_list = _(encrypted_vote.encrypted_answers[qnum].answer).map(function(aindex, anum) {
return q.answers[aindex];
});
status_cb("Question #" + (qnum+1) + " - " + q.short_name + " : " + answer_pretty_list.join(", "));
});
// verify the encryption
if (encrypted_vote.verifyEncryption(election.questions, election.public_key)) {
status_cb("Encryption Verified");
} else {
overall_result = false;
status_cb("PROBLEM = Encryption doesn't match.");
}
// verify the proofs
if (encrypted_vote.verifyProofs(election.public_key, function(ea_num, choice_num, result) {
})) {
status_cb("Proofs ok.");
} else {
overall_result = false;
status_cb("PROBLEM = Proofs don't work.");
}
} catch (e) {
status_cb('problem parsing election or ballot data structures, malformed inputs: ' + e.toString());
overall_result=false;
}
return overall_result;
}
......@@ -56,7 +56,6 @@ BOOTH.setup_templates = function() {
$('#question_div').setTemplateURL("templates/question.html" + cache_bust);
$('#confirm_div').setTemplateURL("templates/confirm.html" + cache_bust);
$('#seal_div').setTemplateURL("templates/seal.html" + cache_bust);
$('#audit_div').setTemplateURL("templates/audit.html" + cache_bust);
$('#footer').setTemplateURL("templates/footer.html" + cache_bust);
BOOTH.templates_setup_p = true;
......@@ -511,18 +510,6 @@ BOOTH.seal_ballot = function() {
}
};
BOOTH.audit_ballot = function() {
BOOTH.audit_trail = BOOTH.encrypted_ballot_with_plaintexts_serialized || $.toJSON(BOOTH.encrypted_ballot.get_audit_trail());
BOOTH.show($('#audit_div')).processTemplate({'audit_trail' : BOOTH.audit_trail, 'election_url' : BOOTH.election_url});
};
BOOTH.post_audited_ballot = function() {
$.post(BOOTH.election_url + "/post-audited-ballot", {'audited_ballot': BOOTH.audit_trail}, function(result) {
alert('This audited ballot has been posted.\nRemember, this vote will only be used for auditing and will not be tallied.\nClick "back to voting" and cast a new ballot to make sure your vote counts.');
});
};
BOOTH.cast_ballot = function() {
// show progress spinner
$('#loading_div').html('<img src="loading.gif" id="proceed_loading_img" />');
......@@ -538,9 +525,6 @@ BOOTH.cast_ballot = function() {
BOOTH.encrypted_ballot_serialized = null;
BOOTH.encrypted_ballot_with_plaintexts_serialized = null;
// remove audit trail
BOOTH.audit_trail = null;
// we're ready to leave the site
BOOTH.started_p = false;
......@@ -613,9 +597,6 @@ function save_ballot() {
<div id="seal_div" class="panel">
</div>
<div id="audit_div" class="panel">
</div>
</div>
<br clear="both" />
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment