/*
 * 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.certificates.utils;

import java.security.KeyPair;
import java.security.Security;
import java.util.Date;

import javax.naming.ConfigurationException;

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

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.asymmetric.utils.AsymmetricTestDataGenerator;
import com.scytl.cryptolib.certificates.bean.X509CertificateType;
import com.scytl.cryptolib.certificates.bean.X509DistinguishedName;
import com.scytl.cryptolib.certificates.configuration.X509CertificateValidationData;
import com.scytl.cryptolib.certificates.configuration.X509CertificateValidationResult;
import com.scytl.cryptolib.certificates.configuration.X509CertificateValidationType;
import com.scytl.cryptolib.certificates.factory.CryptoX509Certificate;

/**
 * Tests of X509 certificate validator.
 */
public class X509CertificateValidatorTest {

    private static KeyPair _rootKeyPair;

    private static KeyPair _intermediateKeyPair;

    private static KeyPair _keyPair;

    private static X509DistinguishedName _rootSubjectDn;

    private static X509DistinguishedName _intermediateSubjectDn;

    private static X509DistinguishedName _subjectDn;

    private static X509CertificateValidationType[] _validationTypes;

    @BeforeClass
    public static void setUp()
            throws ConfigurationException, GeneralCryptoLibException {

        Security.addProvider(new BouncyCastleProvider());

        _rootKeyPair = AsymmetricTestDataGenerator.getKeyPairForSigning();
        _rootSubjectDn =
            X509CertificateTestDataGenerator.getRootDistinguishedName();

        _intermediateKeyPair =
            AsymmetricTestDataGenerator.getKeyPairForSigning();
        _intermediateSubjectDn = X509CertificateTestDataGenerator
            .getIntermediateDistinguishedName();

        _keyPair = AsymmetricTestDataGenerator.getKeyPairForSigning();
        _subjectDn =
            X509CertificateTestDataGenerator.getDistinguishedName();

        _validationTypes = new X509CertificateValidationType[5];
        _validationTypes[0] = X509CertificateValidationType.DATE;
        _validationTypes[1] = X509CertificateValidationType.SUBJECT;
        _validationTypes[2] = X509CertificateValidationType.ISSUER;
        _validationTypes[3] = X509CertificateValidationType.SIGNATURE;
        _validationTypes[4] = X509CertificateValidationType.KEY_TYPE;
    }

    @AfterClass
    public static void tearDown() {

        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
    }

    @Test
    public void checkRootAuthorityCertificateValidityWithCorrectData()
            throws GeneralCryptoLibException {

        CryptoX509Certificate rootCertificate =
            (CryptoX509Certificate) X509CertificateTestDataGenerator
                .getRootAuthorityX509Certificate(_rootKeyPair);

        Date date = new Date(System.currentTimeMillis());
        X509CertificateValidationData validationData =
            new X509CertificateValidationData.Builder().addDate(date)
                .addSubjectDn(_rootSubjectDn).addIssuerDn(_rootSubjectDn)
                .addKeyType(X509CertificateType.CERTIFICATE_AUTHORITY)
                .addCaPublicKey(_rootKeyPair.getPublic()).build();

        X509CertificateValidator validator = new X509CertificateValidator(
            rootCertificate, validationData, _validationTypes);

        X509CertificateValidationResult validationResult =
            validator.validate();

        Assert.assertTrue(
            "Unexpected validation result. expected:<success> but was:<failure>",
            validationResult.isValidated());
        Assert.assertEquals("Unexpected number of failed validations", 0,
            validationResult.getFailedValidationTypes().size());
    }

    @Test
    public void checkIntermediateAuthorityCertificateValidityWithCorrectData()
            throws GeneralCryptoLibException {

        CryptoX509Certificate intermediateCertificate =
            (CryptoX509Certificate) X509CertificateTestDataGenerator
                .getIntermediateAuthorityX509Certificate(
                    _intermediateKeyPair, _rootKeyPair);

        Date date = new Date(System.currentTimeMillis());
        X509CertificateValidationData validationData =
            new X509CertificateValidationData.Builder().addDate(date)
                .addSubjectDn(_intermediateSubjectDn)
                .addIssuerDn(_rootSubjectDn)
                .addKeyType(X509CertificateType.CERTIFICATE_AUTHORITY)
                .addCaPublicKey(_rootKeyPair.getPublic()).build();

        X509CertificateValidator validator = new X509CertificateValidator(
            intermediateCertificate, validationData, _validationTypes);

        X509CertificateValidationResult validationResult =
            validator.validate();

        Assert.assertTrue(
            "Unexpected validation result. expected:<success> but was:<failure>",
            validationResult.isValidated());
        Assert.assertEquals("Unexpected number of failed validations", 0,
            validationResult.getFailedValidationTypes().size());
    }

    @Test
    public void checkSignCertificateValidityWithCorrectData()
            throws GeneralCryptoLibException {

        CryptoX509Certificate signCertificate =
            (CryptoX509Certificate) X509CertificateTestDataGenerator
                .getSignX509Certificate(_keyPair, _rootKeyPair);

        Date date = new Date(System.currentTimeMillis());
        X509CertificateValidationData validationData =
            new X509CertificateValidationData.Builder().addDate(date)
                .addSubjectDn(_subjectDn).addIssuerDn(_rootSubjectDn)
                .addKeyType(X509CertificateType.SIGN)
                .addCaPublicKey(_rootKeyPair.getPublic()).build();

        X509CertificateValidator validator = new X509CertificateValidator(
            signCertificate, validationData, _validationTypes);

        X509CertificateValidationResult validationResult =
            validator.validate();

        Assert.assertTrue(
            "Unexpected validation result. expected:<success> but was:<failure>",
            validationResult.isValidated());
        Assert.assertEquals("Unexpected number of failed validations", 0,
            validationResult.getFailedValidationTypes().size());
    }

    @Test
    public void checkEncryptionCertificateValidityWithCorrectData()
            throws GeneralCryptoLibException {

        CryptoX509Certificate encryptionCertificate =
            (CryptoX509Certificate) X509CertificateTestDataGenerator
                .getEncryptionX509Certificate(_keyPair, _rootKeyPair);

        Date date = new Date(System.currentTimeMillis());
        X509CertificateValidationData validationData =
            new X509CertificateValidationData.Builder().addDate(date)
                .addSubjectDn(_subjectDn).addIssuerDn(_rootSubjectDn)
                .addKeyType(X509CertificateType.ENCRYPT)
                .addCaPublicKey(_rootKeyPair.getPublic()).build();

        X509CertificateValidator validator = new X509CertificateValidator(
            encryptionCertificate, validationData, _validationTypes);

        X509CertificateValidationResult validationResult =
            validator.validate();

        Assert.assertTrue(
            "Unexpected validation result. expected:<success> but was:<failure>",
            validationResult.isValidated());
        Assert.assertEquals("Unexpected number of failed validations", 0,
            validationResult.getFailedValidationTypes().size());
    }
}
