ch.ge.ve.protopoc.service.protocol.DefaultAuthority.java Source code

Java tutorial

Introduction

Here is the source code for ch.ge.ve.protopoc.service.protocol.DefaultAuthority.java

Source

/*-------------------------------------------------------------------------------------------------
 - #%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.protocol;

import ch.ge.ve.protopoc.service.algorithm.*;
import ch.ge.ve.protopoc.service.exception.IncorrectBallotRuntimeException;
import ch.ge.ve.protopoc.service.exception.IncorrectConfirmationRuntimeException;
import ch.ge.ve.protopoc.service.exception.InvalidShuffleProofRuntimeException;
import ch.ge.ve.protopoc.service.model.*;
import ch.ge.ve.protopoc.service.model.polynomial.Point;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigInteger;
import java.security.KeyPair;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Default implementation of the {@link AuthorityService} interface
 */
public class DefaultAuthority implements AuthorityService {
    private static final Logger log = LoggerFactory.getLogger(DefaultAuthority.class);
    private final Logger perfLog = LoggerFactory.getLogger("PerformanceStats");
    private final int j;
    private final BulletinBoardService bulletinBoardService;
    private final KeyEstablishmentAlgorithms keyEstablishmentAlgorithms;
    private final ElectionPreparationAlgorithms electionPreparationAlgorithms;
    private final VoteCastingAuthorityAlgorithms voteCastingAuthorityAlgorithms;
    private final VoteConfirmationAuthorityAlgorithms voteConfirmationAuthorityAlgorithms;
    private final MixingAuthorityAlgorithms mixingAuthorityAlgorithms;
    private final DecryptionAuthorityAlgorithms decryptionAuthorityAlgorithms;
    private final Queue<Long> ballotVerificationTimes = new ConcurrentLinkedQueue<>();
    private final Queue<Long> queryResponseTimes = new ConcurrentLinkedQueue<>();
    private final Queue<Long> confirmationVerificationTimes = new ConcurrentLinkedQueue<>();
    private final Queue<Long> finalizationComputationTimes = new ConcurrentLinkedQueue<>();
    private EncryptionPublicKey myPublicKey;
    private EncryptionPrivateKey myPrivateKey;
    private EncryptionPublicKey systemPublicKey;
    private PublicParameters publicParameters;
    private ElectionSet electionSet;
    private ElectorateData electorateData;
    private List<Point> publicCredentials;
    private Queue<BallotEntry> ballotEntries = new ConcurrentLinkedQueue<>();
    private Queue<ConfirmationEntry> confirmationEntries = new ConcurrentLinkedQueue<>();

    public DefaultAuthority(int j, BulletinBoardService bulletinBoardService,
            KeyEstablishmentAlgorithms keyEstablishmentAlgorithms,
            ElectionPreparationAlgorithms electionPreparationAlgorithms,
            VoteCastingAuthorityAlgorithms voteCastingAuthorityAlgorithms,
            VoteConfirmationAuthorityAlgorithms voteConfirmationAuthorityAlgorithms,
            MixingAuthorityAlgorithms mixingAuthorityAlgorithms,
            DecryptionAuthorityAlgorithms decryptionAuthorityAlgorithms) {
        this.j = j;
        this.bulletinBoardService = bulletinBoardService;
        this.keyEstablishmentAlgorithms = keyEstablishmentAlgorithms;
        this.electionPreparationAlgorithms = electionPreparationAlgorithms;
        this.voteCastingAuthorityAlgorithms = voteCastingAuthorityAlgorithms;
        this.voteConfirmationAuthorityAlgorithms = voteConfirmationAuthorityAlgorithms;
        this.mixingAuthorityAlgorithms = mixingAuthorityAlgorithms;
        this.decryptionAuthorityAlgorithms = decryptionAuthorityAlgorithms;
    }

    @Override
    public void generateKeys() {
        publicParameters = bulletinBoardService.getPublicParameters();
        KeyPair keyPair = keyEstablishmentAlgorithms.generateKeyPair(publicParameters.getEncryptionGroup());
        myPrivateKey = ((EncryptionPrivateKey) keyPair.getPrivate());
        myPublicKey = ((EncryptionPublicKey) keyPair.getPublic());
        bulletinBoardService.publishKeyPart(j, myPublicKey);
    }

    @Override
    public void buildPublicKey() {
        Preconditions.checkState(myPrivateKey != null,
                "The encryption keys need to have been generated beforehand");
        Preconditions.checkState(myPublicKey != null, "The encryption keys need to have been generated beforehand");

        List<EncryptionPublicKey> publicKeyParts = bulletinBoardService.getPublicKeyParts();
        Preconditions.checkArgument(publicKeyParts.size() == publicParameters.getS(),
                "There should be as many keys as there are authorities");
        Preconditions.checkArgument(publicKeyParts.get(j).equals(myPublicKey),
                "The j-th key share should be equal to this authority's");

        systemPublicKey = keyEstablishmentAlgorithms.getPublicKey(publicKeyParts);
    }

    @Override
    public void generateElectorateData() {
        log.info(String.format("Authority %d generating electorate data", j));
        electionSet = bulletinBoardService.getElectionSet();
        electorateData = electionPreparationAlgorithms.genElectorateData(electionSet);

        bulletinBoardService.publishPublicCredentials(j, electorateData.getD_hat());
    }

    @Override
    public List<SecretVoterData> getPrivateCredentials() {
        Preconditions.checkState(electorateData != null, "The electorate data should have been generated first");
        return electorateData.getD();
    }

    @Override
    public void buildPublicCredentials() {
        List<List<Point>> publicCredentialsParts = bulletinBoardService.getPublicCredentialsParts();
        publicCredentials = electionPreparationAlgorithms.getPublicCredentials(publicCredentialsParts);
    }

    @Override
    public ObliviousTransferResponse handleBallot(Integer voterIndex, BallotAndQuery ballotAndQuery) {
        Preconditions.checkState(publicCredentials != null,
                "The public credentials need to have been retrieved first");

        log.info(String.format("Authority %d handling ballot", j));

        Stopwatch stopwatch = Stopwatch.createStarted();
        List<BigInteger> publicIdentificationCredentials = publicCredentials.stream().map(p -> p.x)
                .collect(Collectors.toList());
        if (!voteCastingAuthorityAlgorithms.checkBallot(voterIndex, ballotAndQuery, systemPublicKey,
                publicIdentificationCredentials, ballotEntries)) {
            throw new IncorrectBallotRuntimeException(
                    String.format("Ballot for voter %d was deemed invalid", voterIndex));
        }
        stopwatch.stop();
        ballotVerificationTimes.add(stopwatch.elapsed(TimeUnit.MILLISECONDS));

        stopwatch.reset().start();
        ObliviousTransferResponseAndRand responseAndRand = voteCastingAuthorityAlgorithms.genResponse(voterIndex,
                ballotAndQuery.getBold_a(), systemPublicKey, electionSet.getBold_n(), electorateData.getK(),
                electorateData.getP());
        ballotEntries.add(new BallotEntry(voterIndex, ballotAndQuery, responseAndRand.getBold_r()));
        ObliviousTransferResponse beta = responseAndRand.getBeta();
        stopwatch.stop();
        queryResponseTimes.add(stopwatch.elapsed(TimeUnit.MILLISECONDS));

        return beta;
    }

    @Override
    public FinalizationCodePart handleConfirmation(Integer voterIndex, Confirmation confirmation)
            throws IncorrectConfirmationRuntimeException {
        Preconditions.checkState(publicCredentials != null,
                "The public credentials need to have been retrieved first");
        Stopwatch stopwatch = Stopwatch.createStarted();
        List<BigInteger> publicConfirmationCredentials = publicCredentials.stream().map(p -> p.y)
                .collect(Collectors.toList());

        if (!voteConfirmationAuthorityAlgorithms.checkConfirmation(voterIndex, confirmation,
                publicConfirmationCredentials, ballotEntries, confirmationEntries)) {
            throw new IncorrectConfirmationRuntimeException(
                    "Confirmation for voter " + voterIndex + " was deemed invalid");
        }
        stopwatch.stop();
        confirmationVerificationTimes.add(stopwatch.elapsed(TimeUnit.MILLISECONDS));

        confirmationEntries.add(new ConfirmationEntry(voterIndex, confirmation));

        stopwatch.reset().start();
        FinalizationCodePart finalization = voteConfirmationAuthorityAlgorithms.getFinalization(voterIndex,
                electorateData.getP(), ballotEntries);
        stopwatch.stop();
        finalizationComputationTimes.add(stopwatch.elapsed(TimeUnit.MILLISECONDS));

        return finalization;
    }

    @Override
    public void startMixing() {
        log.info("Authority " + j + " started mixing");
        List<Encryption> encryptions = mixingAuthorityAlgorithms.getEncryptions(ballotEntries, confirmationEntries);
        mixAndPublish(encryptions);
    }

    @Override
    public void mixAgain() {
        log.info("Authority " + j + " performing additional shuffle");
        List<Encryption> previousShuffle = bulletinBoardService.getPreviousShuffle(j - 1);
        mixAndPublish(previousShuffle);
    }

    private void mixAndPublish(List<Encryption> encryptions) {
        Stopwatch shuffleWatch = Stopwatch.createStarted();
        Shuffle shuffle = mixingAuthorityAlgorithms.genShuffle(encryptions, systemPublicKey);
        shuffleWatch.stop();
        perfLog.info(
                String.format("Authority %d : shuffled in %dms", j, shuffleWatch.elapsed(TimeUnit.MILLISECONDS)));
        Stopwatch shuffleProofWatch = Stopwatch.createStarted();
        ShuffleProof shuffleProof = mixingAuthorityAlgorithms.genShuffleProof(encryptions,
                shuffle.getBold_e_prime(), shuffle.getBold_r_prime(), shuffle.getPsy(), systemPublicKey);
        shuffleProofWatch.stop();
        perfLog.info(String.format("Authority %d : generated shuffle proof in %dms", j,
                shuffleProofWatch.elapsed(TimeUnit.MILLISECONDS)));

        bulletinBoardService.publishShuffleAndProof(j, shuffle.getBold_e_prime(), shuffleProof);
    }

    @Override
    public void startPartialDecryption() {
        log.info("Authority " + j + " starting decryption");
        ShufflesAndProofs shufflesAndProofs = bulletinBoardService.getShufflesAndProofs();
        List<Encryption> encryptions = mixingAuthorityAlgorithms.getEncryptions(ballotEntries, confirmationEntries);

        List<ShuffleProof> shuffleProofs = shufflesAndProofs.getShuffleProofs();
        List<List<Encryption>> shuffles = shufflesAndProofs.getShuffles();
        Stopwatch checkShuffleWatch = Stopwatch.createStarted();
        if (!decryptionAuthorityAlgorithms.checkShuffleProofs(shuffleProofs, encryptions, shuffles, systemPublicKey,
                j)) {
            throw new InvalidShuffleProofRuntimeException("At least one shuffle proof was invalid");
        }
        checkShuffleWatch.stop();
        perfLog.info(String.format("Authority %d : checked shuffle proof in %dms", j,
                checkShuffleWatch.elapsed(TimeUnit.MILLISECONDS)));

        BigInteger secretKey = myPrivateKey.getPrivateKey();
        List<Encryption> finalShuffle = shuffles.get(publicParameters.getS() - 1);
        Stopwatch decryptionWatch = Stopwatch.createStarted();
        List<BigInteger> partialDecryptions = decryptionAuthorityAlgorithms.getPartialDecryptions(finalShuffle,
                secretKey);
        decryptionWatch.stop();
        perfLog.info(String.format("Authority %d : decrypted in %dms", j,
                decryptionWatch.elapsed(TimeUnit.MILLISECONDS)));

        BigInteger publicKey = myPublicKey.getPublicKey();
        Stopwatch decryptionProofWatch = Stopwatch.createStarted();
        DecryptionProof decryptionProof = decryptionAuthorityAlgorithms.genDecryptionProof(secretKey, publicKey,
                finalShuffle, partialDecryptions);
        decryptionProofWatch.stop();
        perfLog.info(String.format("Authority %d : decryption proof in %dms", j,
                decryptionProofWatch.elapsed(TimeUnit.MILLISECONDS)));

        bulletinBoardService.publishPartialDecryptionAndProof(j, partialDecryptions, decryptionProof);
    }

    public LongSummaryStatistics getBallotVerificationStats() {
        return ballotVerificationTimes.stream().mapToLong(Long::valueOf).summaryStatistics();
    }

    public LongSummaryStatistics getQueryResponseStats() {
        return queryResponseTimes.stream().mapToLong(Long::valueOf).summaryStatistics();
    }

    public LongSummaryStatistics getConfirmationVerificationStats() {
        return confirmationVerificationTimes.stream().mapToLong(Long::valueOf).summaryStatistics();
    }

    public LongSummaryStatistics getFinalizationComputationStats() {
        return finalizationComputationTimes.stream().mapToLong(Long::valueOf).summaryStatistics();
    }

}