/* * This file is produced by Sarah Jamie Lewis, Olivier Pereira and Vanessa Teague to demonstrate a trapdoor in * the SwissVote-Scytl implementation of Bayer-Groth proofs. * * It is intended to be applied to our four demonstration proof transcripts, made available along with * this file. The transcripts pass verification but change the outcome of the election. * * March 1st, 2019. * */ package com.scytl.products.ov.mixnet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.math.BigInteger; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import com.scytl.products.ov.mixnet.commons.io.*; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException; import com.scytl.cryptolib.elgamal.bean.ElGamalPublicKey; import com.scytl.cryptolib.elgamal.cryptoapi.Ciphertext; import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup; import com.scytl.products.ov.mixnet.commons.ballots.ElGamalEncryptedBallots; import com.scytl.products.ov.mixnet.commons.beans.proofs.ShuffleProof; import com.scytl.products.ov.mixnet.commons.homomorphic.impl.GjosteenElGamal; import com.scytl.products.ov.mixnet.commons.proofs.bg.commitments.CommitmentParams; import com.scytl.products.ov.mixnet.commons.tools.CiphertextTools; import com.scytl.products.ov.mixnet.commons.tools.MatrixArranger; import com.scytl.products.ov.mixnet.commons.tools.MultiExponentiation; import com.scytl.products.ov.mixnet.commons.tools.MultiExponentiationImpl; import com.scytl.products.ov.mixnet.commons.validation.EncryptedBallotsDuplicationValidator; import com.scytl.products.ov.mixnet.commons.validation.EncryptedBallotsValidator; import com.scytl.products.ov.mixnet.commons.validation.EncryptedBallotsValidatorManager; import com.scytl.products.ov.mixnet.proofs.bg.shuffle.ShuffleProofVerifier; import static com.scytl.products.ov.mixnet.commons.io.CommitmentParamsReader.readCommitmentParamsFromStream; public class BaseBGMixnetIOVerifierITest { private static ZpSubgroup zp; private static int m; private static int n; private static int numiterations; private static CommitmentParams commitmentParams; private static GjosteenElGamal elgamal; private static JSONProofsReader proofsReader; private static ElGamalEncryptedBallotsLoader elgamalEncryptedBallotsLoader; private static MultiExponentiation limMultiExpo; private static CiphertextTools ciphertextTools; // Comment out as appropriate depending on whether you're running the full test or the zero test private static boolean CHEATING2 = true; private static String zeroFlag = ""; //private static String zeroFlag = "-zero"; static String twoFlag = CHEATING2 ? "-2" : ""; private static String proofsPath="/tmp/SwissVoteTest/proofs"+twoFlag+zeroFlag+"/"; @BeforeClass public static void setUp() throws IOException, GeneralCryptoLibException { BigInteger p = new BigInteger("15294034768093677312256663166625633354362303"); BigInteger q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2)); BigInteger g = new BigInteger("2"); zp = new ZpSubgroup(g, p, q); ElGamalPublicKey pubKey = ElgamalPublicKeyReader.readPublicKeyFromStream(new FileInputStream(proofsPath+"publicKey"+twoFlag+zeroFlag+".out")); elgamal = new GjosteenElGamal(zp,pubKey); m = 2; n = 2; proofsReader = new JSONProofsReader(); final List listValidators = new ArrayList<>(); listValidators.add(new EncryptedBallotsDuplicationValidator()); EncryptedBallotsValidatorManager validator = new EncryptedBallotsValidatorManager(listValidators); elgamalEncryptedBallotsLoader = new ElGamalEncryptedBallotsLoader(validator); limMultiExpo = MultiExponentiationImpl.getInstance(); ciphertextTools = new CiphertextTools(limMultiExpo); } private static CommitmentParams readCommitmentParamsFromFile() throws IOException { final Path pathCommitmentParamsFile = Paths.get(proofsPath+"commitmentParams"+twoFlag+zeroFlag+".out"); FileInputStream fis = new FileInputStream(pathCommitmentParamsFile.toFile()); return readCommitmentParamsFromStream(zp, fis); } @Test public void givenSmallConfigWhenShuffleThenOK() throws IOException, GeneralCryptoLibException { // ///////////////////////////////////////////////// // // Verify the proofs // // ///////////////////////////////////////////////// final ElGamalEncryptedBallots encryptedBallots_VerifierCopy = elgamalEncryptedBallotsLoader.loadCSV(zp, new FileInputStream(proofsPath+"input"+twoFlag+zeroFlag+".csv")); final Ciphertext[][] originalCiphertexts_VerifierCopy = MatrixArranger.arrangeInCiphertextMatrix(encryptedBallots_VerifierCopy, m, n); final ElGamalEncryptedBallots reencryptedBallots; reencryptedBallots = elgamalEncryptedBallotsLoader.loadCSV(zp, new FileInputStream( Paths.get(proofsPath+"output"+twoFlag+zeroFlag+".csv").toFile())); final Ciphertext[][] reencryptedCiphertexts_VerifierCopy = MatrixArranger.arrangeInCiphertextMatrix(reencryptedBallots, m, n); commitmentParams = readCommitmentParamsFromFile(); System.out.println(commitmentParams.getG()[0]); final ShuffleProofVerifier verifier = new ShuffleProofVerifier(zp, elgamal, commitmentParams, originalCiphertexts_VerifierCopy, reencryptedCiphertexts_VerifierCopy, m, n, 0, numiterations, ciphertextTools, limMultiExpo); final ShuffleProof shuffleProof_VerifierCopy = proofsReader.read( new FileInputStream(Paths.get(proofsPath+"complete-proof"+twoFlag+zeroFlag+".json").toFile())); System.out.println("Shuffle Proof: "+ shuffleProof_VerifierCopy.getSecondAnswer().toString()); final boolean testResult = verifier.verifyProof(shuffleProof_VerifierCopy.getInitialMessage(), shuffleProof_VerifierCopy.getFirstAnswer(), shuffleProof_VerifierCopy.getSecondAnswer()); final String errorMsg = "proofs failed to validate"; Assert.assertTrue(errorMsg, testResult); } }