/* This file is part of GNUnet. (C) 2014 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gnunet.voting; import com.google.common.base.Optional; import org.gnunet.construct.Construct; import org.gnunet.construct.Message; import org.gnunet.construct.NestedMessage; import org.gnunet.secretsharing.Ciphertext; import org.gnunet.secretsharing.Parameters; import org.gnunet.secretsharing.ThresholdPublicKey; import org.gnunet.util.BigIntegers; import org.gnunet.util.Configuration; import org.gnunet.util.Strings; import org.gnunet.util.crypto.EcdsaPublicKey; import org.gnunet.util.crypto.SignedContentMessage; import java.math.BigInteger; import java.util.Arrays; public class EncryptedVote implements Message, SignedContentMessage { @NestedMessage public Ciphertext v; @NestedMessage public DisjunctionZkp disjunctionZkp; @NestedMessage public EcdsaPublicKey voterPublicKey; /** * Construct an EncryptedVote by parsing it from the configuration. * The voter public key must be passed separately. * * @param cfg configuration * @param voterPublicKey public key of the voter * @return the encrypted vote */ public static EncryptedVote parseFromConfiguration(Configuration cfg, EcdsaPublicKey voterPublicKey) { Optional optVal = cfg.getValueString("vote", "ENCRYPTED_VOTE_VAL"); if (!optVal.isPresent()) { return null; } Optional optZkp = cfg.getValueString("vote", "ZKP"); if (!optZkp.isPresent()) { return null; } EncryptedVote encryptedVote = new EncryptedVote(); encryptedVote.voterPublicKey = voterPublicKey; encryptedVote.v = Ciphertext.fromString(optVal.get()); System.out.println("reading, string size " + optZkp.get().length()); byte[] zkpData = Strings.stringToData(optZkp.get()); if (null == zkpData) { throw new InvalidBallotException("could not read ZKP from ballot"); } encryptedVote.disjunctionZkp = Construct.parseAs(zkpData, DisjunctionZkp.class); return encryptedVote; } public static EncryptedVote fromChoice(int choiceId, ThresholdPublicKey thresholdPublicKey, EcdsaPublicKey voterPublicKey, BigInteger[] generators) { EncryptedVote encryptedVote = new EncryptedVote(); encryptedVote.v = new Ciphertext(); encryptedVote.voterPublicKey = voterPublicKey; encryptedVote.disjunctionZkp = new DisjunctionZkp(); encryptedVote.disjunctionZkp.chaumPedersenZkps = new ChaumPedersenZkp[generators.length]; encryptedVote.disjunctionZkp.numProofs = generators.length; // the discrete logarithm BigInteger alpha = Parameters.randomQ(); // the secret for the ZKP BigInteger w = Parameters.randomQ(); BigInteger h = new BigInteger(1, thresholdPublicKey.bits); BigInteger g = Parameters.elgamalG; BigInteger p = Parameters.elgamalP; BigInteger q = Parameters.elgamalQ; BigInteger x = Parameters.modPowG(alpha); BigInteger y = h.modPow(alpha, p).multiply(generators[choiceId]).mod(p); encryptedVote.v.c_1 = BigIntegers.serializeUnsigned(x, Parameters.elgamalBits); encryptedVote.v.c_2 = BigIntegers.serializeUnsigned(y, Parameters.elgamalBits); // sum of all the simulation challenges BigInteger d_sim_sum = BigInteger.ZERO; // generate simulated proofs for (int i = 0; i < generators.length; i++) { if (i == choiceId) { continue; } BigInteger r = Parameters.randomQ(); BigInteger d = Parameters.randomQ(); BigInteger a = g.modPow(r, p).multiply(x.modPow(d, p)).mod(p); BigInteger b = h.modPow(r, p).multiply(y.multiply(generators[i].modInverse(p)).modPow(d, p)).mod(p); d_sim_sum = d_sim_sum.add(d); ChaumPedersenZkp zkp = new ChaumPedersenZkp(); zkp.challenge_d = BigIntegers.serializeUnsigned(d, Parameters.elgamalBits); zkp.response_r = BigIntegers.serializeUnsigned(r, Parameters.elgamalBits); zkp.commit_a = BigIntegers.serializeUnsigned(a, Parameters.elgamalBits); zkp.commit_b = BigIntegers.serializeUnsigned(b, Parameters.elgamalBits); if (!zkp.verifySim(x, y, generators[i], h)) { throw new AssertionError("crypto not working"); } encryptedVote.disjunctionZkp.chaumPedersenZkps[i] = zkp; } ChaumPedersenZkp zkp = new ChaumPedersenZkp(); encryptedVote.disjunctionZkp.chaumPedersenZkps[choiceId] = zkp; BigInteger a = g.modPow(w, p); BigInteger b = h.modPow(w, p); zkp.commit_a = BigIntegers.serializeUnsigned(a, Parameters.elgamalBits); zkp.commit_b = BigIntegers.serializeUnsigned(b, Parameters.elgamalBits); BigInteger c = encryptedVote.disjunctionZkp.computeChallengeFromCommits(); BigInteger d = c.subtract(d_sim_sum).mod(q); BigInteger r = w.subtract(alpha.multiply(d)).mod(q); zkp.challenge_d = BigIntegers.serializeUnsigned(d, Parameters.elgamalBits); zkp.response_r = BigIntegers.serializeUnsigned(r, Parameters.elgamalBits); if (!zkp.verifySim(x,y,generators[choiceId], h)) { throw new AssertionError("crypto (2) not working"); } encryptedVote.disjunctionZkp.challenge_c = BigIntegers.serializeUnsigned(c, Parameters.elgamalBits); if (!encryptedVote.disjunctionZkp.verifyChallenge()) { throw new AssertionError("crypto not working (3)"); } return encryptedVote; } public void writeToConfiguration(Configuration cfg) { cfg.setValueString("vote", "ENCRYPTED_VOTE_VAL", v.toString()); byte[] zkpData = Construct.toBinary(disjunctionZkp); String zkpString = Strings.dataToString(zkpData); if (zkpString.length() != Strings.getEncodedStringLength(zkpData.length)) throw new AssertionError("fail"); if (zkpData.length != Strings.getDecodedDataLength(zkpString.length())) throw new AssertionError("fail, got " + zkpData.length + " expected " + Strings.getDecodedDataLength(zkpString.length()) + "for string length" + zkpString.length()); System.out.println("everyting ok, size binary size" + zkpData.length +" str size " + zkpString.length()); byte[] zkpData2 = Strings.stringToData(zkpString); if (!Arrays.equals(zkpData, zkpData2)) { throw new AssertionError("something wrong"); } System.out.println("everyting ok, size binary size" + zkpData.length +" str size " + zkpString.length()); cfg.setValueString("vote", "ZKP", zkpString); } public boolean verify() { return false; } }