/*
 * 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.symmetric.service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.Security;

import javax.crypto.SecretKey;

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.primitives.primes.bean.TestCryptoDerivedKey;
import com.scytl.cryptolib.primitives.primes.utils.PrimitivesTestDataGenerator;
import com.scytl.cryptolib.primitives.securerandom.constants.SecureRandomConstants;
import com.scytl.cryptolib.test.tools.utils.CommonTestDataGenerator;

/**
 * Tests of the symmetric service API.
 */
public class SymmetricServiceTest {

    static final private int MAXIMUM_DATA_ARRAY_LENGTH = 10;

    static final private int LARGE_DATA_LENGTH = 10000000;

    private static SymmetricService _symmetricServiceForDefaultPolicy;

    private static SecretKey _secretKeyForEncryption;

    private static SecretKey _secretKeyForMac;

    private static int _dataByteLength;

    private static byte[] _data;

    @BeforeClass
    public static void setUp() throws GeneralCryptoLibException {

        Security.addProvider(new BouncyCastleProvider());

        _symmetricServiceForDefaultPolicy = new SymmetricService();

        _secretKeyForEncryption =
            _symmetricServiceForDefaultPolicy.getSecretKeyForEncryption();

        _secretKeyForMac =
            _symmetricServiceForDefaultPolicy.getSecretKeyForHmac();

        _dataByteLength =
            CommonTestDataGenerator.getInt(1,
                SecureRandomConstants.MAXIMUM_GENERATED_BYTE_ARRAY_LENGTH);

        _data = PrimitivesTestDataGenerator.getByteArray(_dataByteLength);

    }

    @AfterClass
    public static void tearDown() {

        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
    }

    @Test
    public void testWhenSymmetricallyEncryptAndDecryptThenOk()
            throws GeneralCryptoLibException {

        byte[] encryptedData =
            _symmetricServiceForDefaultPolicy.encrypt(
                _secretKeyForEncryption, _data);

        byte[] decryptedData =
            _symmetricServiceForDefaultPolicy.decrypt(
                _secretKeyForEncryption, encryptedData);

        Assert.assertArrayEquals(decryptedData, _data);
    }

    @Test
    public void testWhenSymmetricallyEncryptAndDecryptWithLargeDataThenOk()
            throws GeneralCryptoLibException {

        byte[] data =
            PrimitivesTestDataGenerator.getByteArray(LARGE_DATA_LENGTH);

        byte[] encryptedData =
            _symmetricServiceForDefaultPolicy.encrypt(
                _secretKeyForEncryption, data);

        byte[] decryptedData =
            _symmetricServiceForDefaultPolicy.decrypt(
                _secretKeyForEncryption, encryptedData);

        Assert.assertArrayEquals(decryptedData, data);
    }

    @Test
    public void testWhenGenerateAndVerifyMacThenOk()
            throws GeneralCryptoLibException {

        byte[] mac =
            _symmetricServiceForDefaultPolicy.getMac(_secretKeyForMac,
                _data);

        boolean verified =
            _symmetricServiceForDefaultPolicy.verifyMac(_secretKeyForMac,
                mac, _data);

        Assert.assertTrue(verified);
    }

    @Test
    public void testWhenGenerateAndVerifyMacForMultipleDataElementsThenOk()
            throws GeneralCryptoLibException {

        byte[][] dataArray =
            PrimitivesTestDataGenerator.getByteArrayArray(_dataByteLength,
                CommonTestDataGenerator.getInt(2,
                    MAXIMUM_DATA_ARRAY_LENGTH));

        byte[] mac =
            _symmetricServiceForDefaultPolicy.getMac(_secretKeyForMac,
                dataArray);

        boolean verified =
            _symmetricServiceForDefaultPolicy.verifyMac(_secretKeyForMac,
                mac, dataArray);

        Assert.assertTrue(verified);
    }

    @Test
    public void testWhenGenerateAndVerifyMacFromDataInputStreamThenOk()
            throws GeneralCryptoLibException, IOException {

        InputStream dataInputStream = new ByteArrayInputStream(_data);

        byte[] mac =
            _symmetricServiceForDefaultPolicy.getMac(_secretKeyForMac,
                dataInputStream);

        dataInputStream.reset();

        boolean verified =
            _symmetricServiceForDefaultPolicy.verifyMac(_secretKeyForMac,
                mac, dataInputStream);

        Assert.assertTrue(verified);
    }

    @Test
    public void testWhenGetSecretKeyForEncryptionFromDerivedKeyThenOk()
            throws GeneralCryptoLibException {

        com.scytl.cryptolib.primitives.primes.bean.TestCryptoDerivedKey derivedKeyForEncryption =
            new TestCryptoDerivedKey(_secretKeyForEncryption.getEncoded());

        SecretKey secretKey =
            _symmetricServiceForDefaultPolicy
                .getSecretKeyForEncryptionFromDerivedKey(derivedKeyForEncryption);

        Assert.assertArrayEquals(secretKey.getEncoded(),
            _secretKeyForEncryption.getEncoded());
    }

    @Test
    public void testWhenGetSecretKeyForMacFromDerivedKeyThenOk()
            throws GeneralCryptoLibException {

        TestCryptoDerivedKey derivedKeyForMac =
            new TestCryptoDerivedKey(_secretKeyForMac.getEncoded());

        SecretKey secretKey =
            _symmetricServiceForDefaultPolicy
                .getSecretKeyForEncryptionFromBytes(derivedKeyForMac
                    .getEncoded());

        Assert.assertArrayEquals(secretKey.getEncoded(),
            _secretKeyForMac.getEncoded());
    }

    @Test
    public void testWhenGetSecretKeyForEncryptionFromBytesThenOk()
            throws GeneralCryptoLibException {

        SecretKey secretKey =
            _symmetricServiceForDefaultPolicy
                .getSecretKeyForEncryptionFromBytes(_secretKeyForEncryption
                    .getEncoded());

        Assert.assertArrayEquals(secretKey.getEncoded(),
            _secretKeyForEncryption.getEncoded());
    }
}
