/*
 * 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 org.junit.Assert.assertFalse;

import java.security.Security;
import java.util.List;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.cryptoapi.Ciphertext;
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.proofs.bean.ProofPreComputedValues;
import com.scytl.cryptolib.proofs.proof.Proof;
import com.scytl.cryptolib.proofs.utils.ProofsTestDataGenerator;

/**
 * Tests of the Proofs service API for plaintext exponent equality zero
 * knowledge proofs of knowledge.
 */
public class PlaintextExponentEqualityProofTest {

    private static final int NUM_BASE_ELEMENTS = 7;

    private static ProofsService _proofsServiceForDefaultPolicy;

    private static ZpSubgroup _zpSubgroup;

    private static Witness _witness1;

    private static Witness _witness2;

    private static List<ZpGroupElement> _baseElements;

    private static Ciphertext _ciphertext;

    private static Proof _proof;

    private static ProofPreComputedValues _preComputedValues;

    private static ZpSubgroup _differentZpSubgroup;

    private static List<ZpGroupElement> _differentBaseElements;

    @BeforeClass
    public static void setUp() throws Exception {

        Security.addProvider(new BouncyCastleProvider());

        _proofsServiceForDefaultPolicy = new ProofsService();

        _zpSubgroup = MathematicalTestDataGenerator.getZpSubgroup();

        _witness1 = ElGamalTestDataGenerator.getWitness(_zpSubgroup);
        _witness2 = ElGamalTestDataGenerator.getWitness(_zpSubgroup);

        _baseElements = MathematicalTestDataGenerator
            .getZpGroupElements(_zpSubgroup, NUM_BASE_ELEMENTS);

        _ciphertext = ProofsTestDataGenerator
            .getPlaintextExponentEqualityProofCiphertext(_zpSubgroup,
                _baseElements, _witness1, _witness2);

        _proof = _proofsServiceForDefaultPolicy
            .createProofProverAPI(_zpSubgroup)
            .createPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, _witness1, _witness2);

        _preComputedValues = _proofsServiceForDefaultPolicy
            .createProofPreComputerAPI(_zpSubgroup)
            .preComputePlaintextExponentEqualityProof(_baseElements);

        _differentZpSubgroup =
            MathematicalTestDataGenerator.getQrSubgroup();

        do {
            _differentBaseElements = MathematicalTestDataGenerator
                .getZpGroupElements(_zpSubgroup, NUM_BASE_ELEMENTS);
        } while (_differentBaseElements.equals(_baseElements));
    }

    @AfterClass
    public static void tearDown() {

        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
    }

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

    @Test
    public final void whenGenerateAndVerifyProofThenOk()
            throws GeneralCryptoLibException {

        Assert.assertTrue(_proofsServiceForDefaultPolicy
            .createProofVerifierAPI(_zpSubgroup)
            .verifyPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, _proof));
    }

    @Test
    public final void whenGenerateProofUsingPreComputedValuesAndVerifyThenOk()
            throws GeneralCryptoLibException {

        Proof proof = _proofsServiceForDefaultPolicy
            .createProofProverAPI(_zpSubgroup)
            .createPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, _witness1, _witness2, _preComputedValues);

        Assert.assertTrue(_proofsServiceForDefaultPolicy
            .createProofVerifierAPI(_zpSubgroup)
            .verifyPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, proof));
    }

    @Test
    public final void whenSerializeAndDeserializeProofThenOk()
            throws GeneralCryptoLibException {

        Proof deserializedProof = Proof.fromJson(_proof.toJson());

        Assert.assertTrue(_proofsServiceForDefaultPolicy
            .createProofVerifierAPI(_zpSubgroup)
            .verifyPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, deserializedProof));
    }

    @Test
    public final void whenGenerateProofUsingInvalidPreComputedValuesThenVerificationIsFalse()
            throws GeneralCryptoLibException {

        ProofPreComputedValues differentPreComputedValues;
        do {
            differentPreComputedValues = _proofsServiceForDefaultPolicy
                .createProofPreComputerAPI(_zpSubgroup)
                .preComputePlaintextExponentEqualityProof(
                    _differentBaseElements);
        } while (differentPreComputedValues.getExponents()
            .equals(_preComputedValues.getExponents()));

        Proof proofPreComputed = _proofsServiceForDefaultPolicy
            .createProofProverAPI(_zpSubgroup)
            .createPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, _witness1, _witness2,
                differentPreComputedValues);

        Assert.assertFalse(_proofsServiceForDefaultPolicy
            .createProofVerifierAPI(_zpSubgroup)
            .verifyPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, proofPreComputed));
    }

    @Test
    public final void whenGenerateProofUsingInvalidPlaintextThenVerificationIsFalse()
            throws GeneralCryptoLibException {

        Proof proof = _proofsServiceForDefaultPolicy
            .createProofProverAPI(_zpSubgroup)
            .createPlaintextExponentEqualityProof(_ciphertext,
                _differentBaseElements, _witness1, _witness2);

        Assert.assertFalse(_proofsServiceForDefaultPolicy
            .createProofVerifierAPI(_zpSubgroup)
            .verifyPlaintextExponentEqualityProof(_ciphertext,
                _differentBaseElements, proof));
    }

    @Test
    public final void whenGenerateProofUsingInvalidFirstWitnessThenVerificationIsFalse()
            throws GeneralCryptoLibException {

        Witness differentWitness1;
        do {
            differentWitness1 =
                ElGamalTestDataGenerator.getWitness(_zpSubgroup);
        } while (differentWitness1.getExponent()
            .equals(_witness1.getExponent()));

        Proof proof = _proofsServiceForDefaultPolicy
            .createProofProverAPI(_zpSubgroup)
            .createPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, differentWitness1, _witness2);

        Assert.assertFalse(_proofsServiceForDefaultPolicy
            .createProofVerifierAPI(_zpSubgroup)
            .verifyPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, proof));
    }

    @Test
    public final void whenGenerateProofUsingInvalidSecondWitnessThenVerificationIsFalse()
            throws GeneralCryptoLibException {

        Witness differentWitness2;
        do {
            differentWitness2 =
                ElGamalTestDataGenerator.getWitness(_zpSubgroup);
        } while (differentWitness2.getExponent()
            .equals(_witness2.getExponent()));

        Proof proof = _proofsServiceForDefaultPolicy
            .createProofProverAPI(_zpSubgroup)
            .createPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, _witness1, differentWitness2);

        Assert.assertFalse(_proofsServiceForDefaultPolicy
            .createProofVerifierAPI(_zpSubgroup)
            .verifyPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, proof));
    }

    @Test
    public final void whenVerifyProofUsingInvalidCiphertextThenVerificationIsFalse()
            throws GeneralCryptoLibException {

        Ciphertext differentCiphertext = ProofsTestDataGenerator
            .getPlaintextExponentEqualityProofCiphertext(_zpSubgroup,
                _differentBaseElements, _witness1, _witness2);

        assertFalse(_proofsServiceForDefaultPolicy
            .createProofVerifierAPI(_zpSubgroup)
            .verifyPlaintextExponentEqualityProof(differentCiphertext,
                _baseElements, _proof));
    }

    @Test
    public final void whenVerifyProofUsingInvalidPlaintextThenVerificationIsFalse()
            throws GeneralCryptoLibException {

        Assert.assertFalse(_proofsServiceForDefaultPolicy
            .createProofVerifierAPI(_zpSubgroup)
            .verifyPlaintextExponentEqualityProof(_ciphertext,
                _differentBaseElements, _proof));
    }

    @Test
    public final void whenGenerateProofUsingInvalidMathematicalGroupThenExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(
            "The exponent should be of the same Zp subgroup order as this Zp group element");

        _proofsServiceForDefaultPolicy
            .createProofProverAPI(_differentZpSubgroup)
            .createPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, _witness1, _witness2);
    }

    @Test
    public final void whenVerifyProofUsingInvalidMathematicalGroupThenExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(
            "All elements of list of public values must be elements of mathematical group associated with proof.");

        _proofsServiceForDefaultPolicy
            .createProofVerifierAPI(_differentZpSubgroup)
            .verifyPlaintextExponentEqualityProof(_ciphertext,
                _baseElements, _proof);
    }
}
