/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.cryptolib.proofs.service;

import static junitparams.JUnitParamsRunner.$;

import java.security.Security;

import junitparams.JUnitParamsRunner;
import junitparams.Parameters;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.AfterClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;

import com.scytl.cryptolib.api.exceptions.CryptoLibException;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.cryptoapi.Witness;
import com.scytl.cryptolib.elgamal.utils.ElGamalTestDataGenerator;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;
import com.scytl.cryptolib.mathematical.groups.utils.MathematicalTestDataGenerator;
import com.scytl.cryptolib.primitives.primes.utils.PrimitivesTestDataGenerator;
import com.scytl.cryptolib.primitives.securerandom.constants.SecureRandomConstants;
import com.scytl.cryptolib.proofs.bean.ProofPreComputedValues;
import com.scytl.cryptolib.proofs.proof.Proof;
import com.scytl.cryptolib.test.tools.utils.CommonTestDataGenerator;

/**
 * Tests of Proofs service API input validation for Schnorr zero knowledge
 * proofs of knowledge.
 */
@RunWith(JUnitParamsRunner.class)
public class SchnorrProofValidationTest {

    private static ProofsService _proofsServiceForDefaultPolicy;

    private static ZpSubgroup _zpSubgroup;

    private static String _voterId;

    private static String _electionEventId;

    private static Witness _witness;

    private static ZpGroupElement _exponentiatedGenerator;

    private static String _whiteSpaceString;

    static {
        try {
            setUp();
        } catch (GeneralCryptoLibException e) {
            throw new CryptoLibException("Setup failed for class "
                + SchnorrProofValidationTest.class.getSimpleName());
        }
    }

    public static void setUp() throws GeneralCryptoLibException {

        Security.addProvider(new BouncyCastleProvider());

        _proofsServiceForDefaultPolicy = new ProofsService();

        _zpSubgroup = MathematicalTestDataGenerator.getZpSubgroup();

        int numChars =
            CommonTestDataGenerator.getInt(1,
                SecureRandomConstants.MAXIMUM_GENERATED_STRING_LENGTH);
        _voterId = PrimitivesTestDataGenerator.getString64(numChars);
        _electionEventId =
            PrimitivesTestDataGenerator.getString64(numChars);

        _witness = ElGamalTestDataGenerator.getWitness(_zpSubgroup);
        ZpGroupElement generatorElement =
            new ZpGroupElement(_zpSubgroup.getG(), _zpSubgroup);
        _exponentiatedGenerator =
            generatorElement.exponentiate(_witness.getExponent());

        _whiteSpaceString =
            CommonTestDataGenerator
                .getWhiteSpaceString(CommonTestDataGenerator.getInt(1,
                    SecureRandomConstants.MAXIMUM_GENERATED_STRING_LENGTH));
    }

    @AfterClass
    public static void tearDown() {

        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
    }

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    @Parameters(method = "createSchnorrProof")
    public void testSchnorrProofCreationValidation(ZpSubgroup group,
            String voterID, String electionEventID,
            ZpGroupElement exponentiatedGenerator, Witness witness,
            String errorMsg) throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        _proofsServiceForDefaultPolicy.createProofProverAPI(group)
            .createSchnorrProof(voterID, electionEventID,
                exponentiatedGenerator, witness);
    }

    public static Object[] createSchnorrProof() {

        return $(
            $(null, _voterId, _electionEventId, _exponentiatedGenerator,
                _witness, "Zp subgroup is null."),
            $(_zpSubgroup, null, _electionEventId,
                _exponentiatedGenerator, _witness, "Voter ID is null."),
            $(_zpSubgroup, "", _electionEventId, _exponentiatedGenerator,
                _witness, "Voter ID is blank."),
            $(_zpSubgroup, _whiteSpaceString, _electionEventId,
                _exponentiatedGenerator, _witness, "Voter ID is blank."),
            $(_zpSubgroup, _voterId, null, _exponentiatedGenerator,
                _witness, "Election event ID is null."),
            $(_zpSubgroup, _voterId, "", _exponentiatedGenerator,
                _witness, "Election event ID is blank."),
            $(_zpSubgroup, _voterId, _whiteSpaceString,
                _exponentiatedGenerator, _witness,
                "Election event ID is blank."),
            $(_zpSubgroup, _voterId, _electionEventId, null, _witness,
                "Exponentiated generator element is null."),
            $(_zpSubgroup, _voterId, _electionEventId,
                _exponentiatedGenerator, null, "Witness is null."));
    }

    @Test
    @Parameters(method = "createSchnorrProofUsingPreComputedValues")
    public void testSchnorrProofCreationUsingPreComputedValuesValidation(
            ZpSubgroup group, String voterID, String electionEventID,
            ZpGroupElement exponentiatedGenerator, Witness witness,
            ProofPreComputedValues preComputedValues, String errorMsg)
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        _proofsServiceForDefaultPolicy.createProofProverAPI(group)
            .createSchnorrProof(voterID, electionEventID,
                exponentiatedGenerator, witness, preComputedValues);
    }

    public static Object[] createSchnorrProofUsingPreComputedValues()
            throws GeneralCryptoLibException {

        ProofPreComputedValues preComputedValues =
            _proofsServiceForDefaultPolicy.createProofPreComputerAPI(
                _zpSubgroup).preComputeSchnorrProof();

        return $(
            $(null, _voterId, _electionEventId, _exponentiatedGenerator,
                _witness, preComputedValues, "Zp subgroup is null."),
            $(_zpSubgroup, null, _electionEventId,
                _exponentiatedGenerator, _witness, preComputedValues,
                "Voter ID is null."),
            $(_zpSubgroup, "", _electionEventId, _exponentiatedGenerator,
                _witness, preComputedValues, "Voter ID is blank."),
            $(_zpSubgroup, _whiteSpaceString, _electionEventId,
                _exponentiatedGenerator, _witness, preComputedValues,
                "Voter ID is blank."),
            $(_zpSubgroup, _voterId, null, _exponentiatedGenerator,
                _witness, preComputedValues, "Election event ID is null."),
            $(_zpSubgroup, _voterId, "", _exponentiatedGenerator,
                _witness, preComputedValues, "Election event ID is blank."),
            $(_zpSubgroup, _voterId, _whiteSpaceString,
                _exponentiatedGenerator, _witness, preComputedValues,
                "Election event ID is blank."),
            $(_zpSubgroup, _voterId, _electionEventId, null, _witness,
                preComputedValues,
                "Exponentiated generator element is null."),
            $(_zpSubgroup, _voterId, _electionEventId,
                _exponentiatedGenerator, null, preComputedValues,
                "Witness is null."),
            $(_zpSubgroup, _voterId, _electionEventId,
                _exponentiatedGenerator, _witness, null,
                "Pre-computed values object is null."));
    }

    @Test
    @Parameters(method = "verifySchnorrProof")
    public void testSchnorrProofVerificationValidation(ZpSubgroup group,
            ZpGroupElement exponentiatedGenerator, String voterID,
            String electionEventID, Proof proof, String errorMsg)
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        _proofsServiceForDefaultPolicy.createProofVerifierAPI(group)
            .verifySchnorrProof(exponentiatedGenerator, voterID,
                electionEventID, proof);
    }

    public static Object[] verifySchnorrProof()
            throws GeneralCryptoLibException {

        Proof proof =
            _proofsServiceForDefaultPolicy.createProofProverAPI(
                _zpSubgroup).createSchnorrProof(_voterId,
                _electionEventId, _exponentiatedGenerator, _witness);

        return $(
            $(null, _exponentiatedGenerator, _voterId, _electionEventId,
                proof, "Zp subgroup is null."),
            $(_zpSubgroup, null, _voterId, _electionEventId, proof,
                "Exponentiated generator element is null."),
            $(_zpSubgroup, _exponentiatedGenerator, null,
                _electionEventId, proof, "Voter ID is null."),
            $(_zpSubgroup, _exponentiatedGenerator, "", _electionEventId,
                proof, "Voter ID is blank."),
            $(_zpSubgroup, _exponentiatedGenerator, _whiteSpaceString,
                _electionEventId, proof, "Voter ID is blank."),
            $(_zpSubgroup, _exponentiatedGenerator, _voterId, null, proof,
                "Election event ID is null."),
            $(_zpSubgroup, _exponentiatedGenerator, _voterId, "", proof,
                "Election event ID is blank."),
            $(_zpSubgroup, _exponentiatedGenerator, _voterId,
                _whiteSpaceString, proof, "Election event ID is blank."),
            $(_zpSubgroup, _exponentiatedGenerator, _voterId,
                _electionEventId, null, "Schnorr proof is null."));
    }
}
