/*
 * 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.products.ov.sdm.domain.service.impl;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.Date;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.scytl.cryptolib.api.certificates.CertificatesServiceAPI;
import com.scytl.cryptolib.api.elgamal.ElGamalServiceAPI;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.asymmetric.service.AsymmetricService;
import com.scytl.cryptolib.certificates.bean.CertificateData;
import com.scytl.cryptolib.certificates.bean.RootCertificateData;
import com.scytl.cryptolib.certificates.bean.ValidityDates;
import com.scytl.cryptolib.certificates.bean.X509DistinguishedName;
import com.scytl.cryptolib.certificates.service.CertificatesService;
import com.scytl.cryptolib.elgamal.bean.ElGamalEncryptionParameters;
import com.scytl.cryptolib.elgamal.bean.ElGamalPublicKey;
import com.scytl.cryptolib.elgamal.cryptoapi.CryptoAPIElGamalKeyPairGenerator;
import com.scytl.cryptolib.elgamal.service.ElGamalService;

/**
 * Tests of {@link CCPublicKeySignatureValidatorImpl}.
 */
public class CCPublicKeySignatureValidatorImplTest {
    private static final String VERIFICATION_CARD_SET_ID =
        "verificationCardSetId";

    private static final String ELECTORL_AUTHORITY_ID =
        "electoralAuthorityId";

    private static final String ELECTION_EVENT_ID = "electionEventId";

    private static final String ENCRYPTION_PARAMETERS_JSON =
        "{\"encryptionParams\":{\"p\":\"AMoJqsCm6UG4B+FGO1Vh6qJcCJcMAVyZFIbZnTIZyD3XAQSTLdxVZxrXm5ZNQMajKAmjUfb3/BV84OgDZaNMXQziV2Uw6hQ70jUcswZ6ZfHAQBy+kwSozDM1LA6ndqp0JIGmPD7LqFIhv9ly7427ksgX1LjNR1tUInVaCS26dfP5wkZyCxwa9/3TapFpFugek+WSJqozaDSd89WAuqXrbBu0FMJWvBVvJapsjfJLEOXTHzdeWef9X5M4FY4Uk+/0WaDVZXy5JhcdoxuNU6Oy0/9buDrPfzlg5a26gpV5ei7XHfykYxdBIPUQoAmraSyUp/byhSDcQu6GJxkfknfAORU=\",\"q\":\"AJT/piLUjl6zN9D/Pc4GYsAG0KAS57H7xJ/mqb+ycbIl\",\"g\":\"ALNkOUZfTDbTMsCYUAKBAAzPdl7422rYGhca6GUAwEPQMlexuIZfIoft+ww54NDqGrUb4TFyw8Yy+y+9Ryb7AXNockro4XYfUjN6eiwOrPxfWG3r9BxqReWZRp74uQ2Ec1toVBrpHaXQNETOJpPBThMKKqrgIuPeAdMRM9U9viku8X0IDmHcqoNaI2o2fvFLFaxlzt5h4kzeJ0A5Twn8wwp5Uln0KcTBJf3H904Icq1u9WTvCTa1jQkoSzkNDQ1VABNFWBa/hW/RGAG7YqTurYHQB+9sMRpOiN10X+6ihdF5VgGpCgXfquKCSqa7I44vq2Vr9+Bm34Gjy+uDOJWrilg=\"}}";

    private static AsymmetricService asymmetricService;

    private static X509Certificate rootCACertificate;

    private static X509Certificate nodeCACertificate;

    private static PrivateKey signerPrivateKey;

    private static X509Certificate signerCertificate;

    private static ElGamalPublicKey key;

    private CCPublicKeySignatureValidatorImpl validator;

    @BeforeClass
    public static void beforeClass() throws GeneralCryptoLibException {
        if (Security
            .getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }

        asymmetricService = new AsymmetricService();
        CertificatesServiceAPI certificatesService =
            new CertificatesService();
        ElGamalServiceAPI elGamalService = new ElGamalService();

        Date from = new Date();
        Date to = new Date(from.getTime() + 1000);
        ValidityDates dates = new ValidityDates(from, to);

        KeyPair rootPair = asymmetricService.getKeyPairForSigning();
        RootCertificateData rootData = new RootCertificateData();
        rootData.setSubjectDn(
            new X509DistinguishedName.Builder("root", "ES").build());
        rootData.setSubjectPublicKey(rootPair.getPublic());
        rootData.setValidityDates(dates);
        rootCACertificate =
            certificatesService.createRootAuthorityX509Certificate(
                rootData, rootPair.getPrivate()).getCertificate();

        KeyPair nodePair = asymmetricService.getKeyPairForSigning();
        CertificateData nodeData = new CertificateData();
        nodeData.setSubjectDn(
            new X509DistinguishedName.Builder("node", "ES").build());
        nodeData.setSubjectPublicKey(nodePair.getPublic());
        nodeData.setValidityDates(dates);
        nodeData.setIssuerDn(rootData.getSubjectDn());
        nodeCACertificate =
            certificatesService.createIntermediateAuthorityX509Certificate(
                nodeData, rootPair.getPrivate()).getCertificate();

        KeyPair signerPair = asymmetricService.getKeyPairForSigning();
        signerPrivateKey = signerPair.getPrivate();
        CertificateData signerData = new CertificateData();
        signerData.setSubjectDn(
            new X509DistinguishedName.Builder("signer", "ES").build());
        signerData.setSubjectPublicKey(signerPair.getPublic());
        signerData.setValidityDates(dates);
        signerData.setIssuerDn(nodeData.getSubjectDn());
        signerCertificate = certificatesService
            .createSignX509Certificate(signerData, nodePair.getPrivate())
            .getCertificate();

        CryptoAPIElGamalKeyPairGenerator generator =
            elGamalService.getElGamalKeyPairGenerator();
        ElGamalEncryptionParameters parameters =
            ElGamalEncryptionParameters
                .fromJson(ENCRYPTION_PARAMETERS_JSON);
        key = generator.generateKeys(parameters, 2).getPublicKeys();
    }

    @Before
    public void setUp() {
        validator =
            new CCPublicKeySignatureValidatorImpl(asymmetricService);
    }

    @Test
    public void testCheckChoiceCodesEncryptionKeySignature()
            throws GeneralCryptoLibException, SignatureException {
        byte[][] data = {key.toJson().getBytes(UTF_8),
                ELECTION_EVENT_ID.getBytes(UTF_8),
                VERIFICATION_CARD_SET_ID.getBytes(UTF_8) };
        byte[] signature = asymmetricService.sign(signerPrivateKey, data);
        X509Certificate[] chain =
            {signerCertificate, nodeCACertificate, rootCACertificate };
        validator.checkChoiceCodesEncryptionKeySignature(signature, chain,
            key, ELECTION_EVENT_ID, VERIFICATION_CARD_SET_ID);
    }

    @Test(expected = SignatureException.class)
    public void testCheckChoiceCodesEncryptionKeySignatureBadChain()
            throws GeneralCryptoLibException, SignatureException {
        byte[][] data = {key.toJson().getBytes(UTF_8),
                ELECTION_EVENT_ID.getBytes(UTF_8),
                VERIFICATION_CARD_SET_ID.getBytes(UTF_8) };
        byte[] signature = asymmetricService.sign(signerPrivateKey, data);
        X509Certificate[] chain = {signerCertificate, rootCACertificate };
        validator.checkChoiceCodesEncryptionKeySignature(signature, chain,
            key, ELECTION_EVENT_ID, VERIFICATION_CARD_SET_ID);
    }

    @Test(expected = SignatureException.class)
    public void testCheckChoiceCodesEncryptionKeySignatureBadSignature()
            throws GeneralCryptoLibException, SignatureException {
        byte[][] data = {key.toJson().getBytes(UTF_8),
                ELECTION_EVENT_ID.getBytes(UTF_8) };
        byte[] signature = asymmetricService.sign(signerPrivateKey, data);
        X509Certificate[] chain =
            {signerCertificate, nodeCACertificate, rootCACertificate };
        validator.checkChoiceCodesEncryptionKeySignature(signature, chain,
            key, ELECTION_EVENT_ID, VERIFICATION_CARD_SET_ID);
    }

    @Test
    public void testCheckMixingKeySignature()
            throws GeneralCryptoLibException, SignatureException {
        byte[][] data = {key.toJson().getBytes(UTF_8),
                ELECTION_EVENT_ID.getBytes(UTF_8),
                ELECTORL_AUTHORITY_ID.getBytes(UTF_8) };
        byte[] signature = asymmetricService.sign(signerPrivateKey, data);
        X509Certificate[] chain =
            {signerCertificate, nodeCACertificate, rootCACertificate };
        validator.checkMixingKeySignature(signature, chain, key,
            ELECTION_EVENT_ID, ELECTORL_AUTHORITY_ID);
    }

    @Test(expected = SignatureException.class)
    public void testCheckMixingKeySignatureBadChain()
            throws GeneralCryptoLibException, SignatureException {
        byte[][] data = {key.toJson().getBytes(UTF_8),
                ELECTION_EVENT_ID.getBytes(UTF_8),
                ELECTORL_AUTHORITY_ID.getBytes(UTF_8) };
        byte[] signature = asymmetricService.sign(signerPrivateKey, data);
        X509Certificate[] chain = {signerCertificate, rootCACertificate };
        validator.checkMixingKeySignature(signature, chain, key,
            ELECTION_EVENT_ID, ELECTORL_AUTHORITY_ID);
    }

    @Test(expected = SignatureException.class)
    public void testCheckMixingKeySignatureBadSignature()
            throws GeneralCryptoLibException, SignatureException {
        byte[][] data = {key.toJson().getBytes(UTF_8),
                ELECTION_EVENT_ID.getBytes(UTF_8) };
        byte[] signature = asymmetricService.sign(signerPrivateKey, data);
        X509Certificate[] chain =
            {signerCertificate, nodeCACertificate, rootCACertificate };
        validator.checkMixingKeySignature(signature, chain, key,
            ELECTION_EVENT_ID, ELECTORL_AUTHORITY_ID);
    }
}
