Java tutorial
/*------------------------------------------------------------------------------------------------- - #%L - - chvote-protocol-poc - - %% - - Copyright (C) 2016 - 2017 Rpublique et Canton de Genve - - %% - - This program is free software: you can redistribute it and/or modify - - it under the terms of the GNU Affero General Public License as published by - - the Free Software Foundation, either version 3 of the License, or - - (at your option) any later version. - - - - This program 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 Affero General Public License - - along with this program. If not, see <http://www.gnu.org/licenses/>. - - #L% - -------------------------------------------------------------------------------------------------*/ package ch.ge.ve.protopoc.service.algorithm; import ch.ge.ve.protopoc.service.exception.IncompatibleParametersRuntimeException; import ch.ge.ve.protopoc.service.exception.NotEnoughPrimesInGroupException; import ch.ge.ve.protopoc.service.model.*; import ch.ge.ve.protopoc.service.model.polynomial.Point; import ch.ge.ve.protopoc.service.support.ByteArrayUtils; import ch.ge.ve.protopoc.service.support.Conversion; import ch.ge.ve.protopoc.service.support.Hash; import ch.ge.ve.protopoc.service.support.RandomGenerator; import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import static ch.ge.ve.protopoc.arithmetic.BigIntegerArithmetic.modExp; import static java.math.BigInteger.ONE; /** * Algorithms related to the vote casting phase, performed by the authorities */ public class VoteCastingAuthorityAlgorithms { private static final Logger log = LoggerFactory.getLogger(VoteConfirmationAuthorityAlgorithms.class); private final PublicParameters publicParameters; private final ElectionSet electionSet; private final GeneralAlgorithms generalAlgorithms; private final RandomGenerator randomGenerator; private final Hash hash; private final Conversion conversion = new Conversion(); public VoteCastingAuthorityAlgorithms(PublicParameters publicParameters, ElectionSet electionSet, GeneralAlgorithms generalAlgorithms, RandomGenerator randomGenerator, Hash hash) { this.publicParameters = publicParameters; this.electionSet = electionSet; this.generalAlgorithms = generalAlgorithms; this.randomGenerator = randomGenerator; this.hash = hash; } /** * Algorithm 7.22: CheckBallot * * @param i the voter index * @param alpha the submitted ballot, including the oblivious transfer query * @param pk the encryption public key * @param bold_x_hat the vector of public voter credentials * @param upper_b the current ballot list * @return true if the ballot was valid */ public boolean checkBallot(Integer i, BallotAndQuery alpha, EncryptionPublicKey pk, List<BigInteger> bold_x_hat, Collection<BallotEntry> upper_b) { Preconditions.checkNotNull(i); Preconditions.checkNotNull(alpha); List<BigInteger> bold_a = alpha.getBold_a(); Preconditions.checkNotNull(bold_a); Preconditions.checkArgument(bold_a.stream().allMatch(generalAlgorithms::isMember), "All of the a_j's must be members of G_q"); Preconditions.checkArgument(generalAlgorithms.isMember(alpha.getB()), "b must be a member of G_q"); int numberOfSelections = bold_a.size(); Preconditions.checkArgument(numberOfSelections > 0); Voter voter = electionSet.getVoters().get(i); int k_i = electionSet.getElections().stream().filter(e -> electionSet.isEligible(voter, e)) .mapToInt(Election::getNumberOfSelections).sum(); Preconditions.checkArgument(numberOfSelections == k_i, "A voter may not submit more than his allowed number of selections"); Preconditions.checkNotNull(pk); Preconditions.checkNotNull(bold_x_hat); Preconditions.checkElementIndex(i, bold_x_hat.size()); Preconditions.checkNotNull(upper_b); BigInteger p = publicParameters.getEncryptionGroup().getP(); BigInteger x_hat_i = bold_x_hat.get(i); if (!hasBallot(i, upper_b) && alpha.getX_hat().compareTo(x_hat_i) == 0) { BigInteger a = bold_a.stream().reduce(BigInteger::multiply).orElse(ONE).mod(p); return checkBallotProof(alpha.getPi(), alpha.getX_hat(), a, alpha.getB(), pk); } return false; } /** * Algorithm 7.23: HasBallot * * @param i the voter index * @param B the current ballot list * @return true if any ballot in the list matches the given voter index, false otherwise */ public boolean hasBallot(Integer i, Collection<BallotEntry> B) { Preconditions.checkNotNull(i); Preconditions.checkNotNull(B); return B.stream().anyMatch(b_j -> b_j.getI().equals(i)); } /** * Algorithm 7.24: CheckBallotProof * * @param pi the proof * @param x_hat public voting credential * @param a first part of ElGamal encryption * @param b second part of ElGamal encryption * @param pk the encryption public key * @return true if the proof is valid, false otherwise */ public boolean checkBallotProof(NonInteractiveZKP pi, BigInteger x_hat, BigInteger a, BigInteger b, EncryptionPublicKey pk) { Preconditions.checkNotNull(pi); List<BigInteger> t = pi.getT(); Preconditions.checkNotNull(t); List<BigInteger> s = pi.getS(); Preconditions.checkNotNull(s); Preconditions.checkNotNull(x_hat); Preconditions.checkNotNull(a); Preconditions.checkNotNull(b); Preconditions.checkNotNull(pk); Preconditions.checkNotNull(pk.getPublicKey()); Preconditions.checkArgument(t.size() == 3, "t contains three elements"); Preconditions.checkArgument(generalAlgorithms.isMember_G_q_hat(t.get(0)), "t_1 must be in G_q_hat"); Preconditions.checkArgument(generalAlgorithms.isMember(t.get(1)), "t_2 must be in G_q"); Preconditions.checkArgument(generalAlgorithms.isMember(t.get(2)), "t_3 must be in G_q"); Preconditions.checkArgument(s.size() == 3, "s contains three elements"); BigInteger s_1 = s.get(0); BigInteger s_2 = s.get(1); BigInteger s_3 = s.get(2); Preconditions.checkArgument(generalAlgorithms.isInZ_q_hat(s_1), "s_1 must be in Z_q_hat"); Preconditions.checkArgument(generalAlgorithms.isMember(s_2), "s_2 must be in G_q"); Preconditions.checkArgument(generalAlgorithms.isInZ_q(s_3), "s_3 must be in Z_q"); Preconditions.checkArgument(pk.getEncryptionGroup() == publicParameters.getEncryptionGroup()); log.debug(String.format("checkBallotProof: a = %s", a)); BigInteger p = publicParameters.getEncryptionGroup().getP(); BigInteger q = publicParameters.getEncryptionGroup().getQ(); BigInteger g = publicParameters.getEncryptionGroup().getG(); BigInteger p_hat = publicParameters.getIdentificationGroup().getP_hat(); BigInteger g_hat = publicParameters.getIdentificationGroup().getG_hat(); int tau = publicParameters.getSecurityParameters().getTau(); BigInteger[] y = new BigInteger[] { x_hat, a, b }; BigInteger[] t_array = new BigInteger[3]; t.toArray(t_array); BigInteger c = generalAlgorithms.getNIZKPChallenge(y, t_array, tau); log.debug(String.format("checkBallotProof: c = %s", c)); BigInteger t_prime_1 = modExp(x_hat, c.negate(), p_hat).multiply(modExp(g_hat, s_1, p_hat)).mod(p_hat); BigInteger t_prime_2 = modExp(a, c.negate(), p).multiply(s_2).multiply(modExp(pk.getPublicKey(), s_3, p)) .mod(p); BigInteger t_prime_3 = modExp(b, c.negate(), p).multiply(modExp(g, s_3, p)).mod(p); return t_array[0].compareTo(t_prime_1) == 0 && t_array[1].compareTo(t_prime_2) == 0 && t_array[2].compareTo(t_prime_3) == 0; } /** * Algorithm 7.25: GenResponse * * @param i the voter index * @param bold_a the vector of the queries * @param pk the encryption public key * @param bold_n the vector of number of candidates per election * @param bold_K the matrix of number of selections per voter per election * @param upper_bold_p the matrix of points per voter per candidate * @return the OT response, along with the randomness used * @throws IncompatibleParametersRuntimeException if not enough primes exist in the encryption group for the number of candidates */ public ObliviousTransferResponseAndRand genResponse(Integer i, List<BigInteger> bold_a, EncryptionPublicKey pk, List<Integer> bold_n, List<List<Integer>> bold_K, List<List<Point>> upper_bold_p) { Preconditions.checkArgument(bold_a.stream().allMatch(generalAlgorithms::isMember), "All queries a_i must be in G_q"); Preconditions.checkArgument(pk.getPublicKey().compareTo(BigInteger.ONE) != 0, "The encryption key may not be 1"); Preconditions.checkArgument(generalAlgorithms.isMember(pk.getPublicKey()), "The public key must be a member of G_q"); BigInteger p_prime = publicParameters.getPrimeField().getP_prime(); Preconditions.checkArgument( upper_bold_p.stream().flatMap(Collection::stream) .allMatch(point -> BigInteger.ZERO.compareTo(point.x) <= 0 && point.x.compareTo(p_prime) < 0 && BigInteger.ZERO.compareTo(point.y) <= 0 && point.y.compareTo(p_prime) < 0), "All points' coordinates must be in Z_p_prime"); Preconditions.checkArgument(bold_K.size() > 0); final int t = bold_K.get(0).size(); Preconditions.checkArgument(bold_K.stream().allMatch(bold_k_i -> bold_k_i.size() == t)); final int n = bold_n.stream().reduce((a, b) -> a + b).orElse(0); Preconditions.checkArgument(upper_bold_p.size() > 0); Preconditions.checkArgument(upper_bold_p.stream().allMatch(bold_p_i -> bold_p_i.size() == n)); final int k_sum = bold_K.get(i).stream().reduce((a, b) -> a + b).orElse(0); Preconditions.checkArgument(bold_a.size() == k_sum); BigInteger q = publicParameters.getEncryptionGroup().getQ(); BigInteger p = publicParameters.getEncryptionGroup().getP(); int upper_l_m = publicParameters.getUpper_l_m(); List<BigInteger> bold_b = new ArrayList<>(); byte[][] bold_c = new byte[n][]; List<BigInteger> bold_d = new ArrayList<>(); List<BigInteger> bold_r = new ArrayList<>(); List<BigInteger> bold_p; try { bold_p = generalAlgorithms.getPrimes(n); } catch (NotEnoughPrimesInGroupException e) { throw new IncompatibleParametersRuntimeException(e); } int u = 0; // index 0 based, as opposed to the specification 1 based int v = 0; // same comment for (int j = 0; j < t; j++) { BigInteger r_j = randomGenerator.randomInZq(q); Integer k_ij = bold_K.get(i).get(j); for (int l = 0; l < k_ij; l++) { bold_b.add(modExp(bold_a.get(u++), r_j, p)); } Integer n_j = bold_n.get(j); for (int l = 0; l < n_j; l++) { Point point_iv = upper_bold_p.get(i).get(v); @SuppressWarnings("SuspiciousNameCombination") byte[] M_v = ByteArrayUtils.concatenate(conversion.toByteArray(point_iv.x, upper_l_m / 2), conversion.toByteArray(point_iv.y, upper_l_m / 2)); log.debug(String.format("Encoding point %s as %s", point_iv, Arrays.toString(M_v))); BigInteger k = modExp(bold_p.get(v), r_j, p); byte[] bold_upper_k = new byte[0]; int l_m = (int) Math .ceil((double) upper_l_m / publicParameters.getSecurityParameters().getUpper_l()); for (int z = 1; z <= l_m; z++) { bold_upper_k = ByteArrayUtils.concatenate(bold_upper_k, hash.recHash_L(k, BigInteger.valueOf(z))); } bold_upper_k = ByteArrayUtils.truncate(bold_upper_k, upper_l_m); bold_c[v] = ByteArrayUtils.xor(M_v, bold_upper_k); log.debug(String.format("bold_c[%d] = %s", v, Arrays.toString(bold_c[v]))); v++; } bold_d.add(modExp(pk.getPublicKey(), r_j, p)); bold_r.add(r_j); } ObliviousTransferResponse beta = new ObliviousTransferResponse(bold_b, bold_c, bold_d); return new ObliviousTransferResponseAndRand(beta, bold_r); } }