/*
 * 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.mac.factory;

import static org.junit.Assert.assertTrue;

import java.security.Security;
import java.util.Base64;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

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

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.commons.constants.Constants;
import com.scytl.cryptolib.symmetric.key.configuration.SymmetricKeyPolicyFromProperties;
import com.scytl.cryptolib.symmetric.mac.configuration.MacPolicyFromProperties;
import com.scytl.cryptolib.symmetric.service.SymmetricService;

public class MacVerificationTest {

    private static final String TEST_DATA_1 = "test data 1";

    private static final String TEST_DATA_2 = "test data 2";

    private static final String DATA_JS = "Ox2fUJq1gAbX";

    private static final String SECRET_KEY_BASE64_JS =
        "lFoxPuafE3Kk4jcqqHAatdwfsRS8ZcaF9N4gg+85KoA=";

    private static final String MAC_BASE64_JS =
        "6qiFgZH6VWXti+ltAqmBO9CryybDJC6oHDJsD4bxVlI=";

    private static SymmetricKeyPolicyFromProperties _symmetricKeyPolicyFromProperties;

    private static CryptoMac _cryptoMac;

    private static SecretKey _key;

    private static byte[] _mac1;

    private static byte[] _mac2;

    @BeforeClass
    public static void setUp() throws GeneralCryptoLibException {

        Security.addProvider(new BouncyCastleProvider());

        _symmetricKeyPolicyFromProperties =
            new SymmetricKeyPolicyFromProperties(
                Constants.CRYPTOLIB_POLICY_PROPERTIES_FILE_PATH);

        SymmetricService symmetricServiceFromDefaultConstructor =
            new SymmetricService();

        _cryptoMac =
            new MacFactory(new MacPolicyFromProperties(
                Constants.CRYPTOLIB_POLICY_PROPERTIES_FILE_PATH)).create();

        _key = symmetricServiceFromDefaultConstructor.getSecretKeyForHmac();

        _mac1 = _cryptoMac.generate(_key, TEST_DATA_1.getBytes());
        _mac2 = _cryptoMac.generate(_key, TEST_DATA_2.getBytes());
    }

    @AfterClass
    public static void tearDown() {

        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
    }

    @Test
    public void testMacsForSameDataAreSame() {

        boolean mac1Verified =
            _cryptoMac.verify(_key, _mac1, TEST_DATA_1.getBytes());

        boolean mac2Verified =
            _cryptoMac.verify(_key, _mac2, TEST_DATA_2.getBytes());

        assertTrue(mac1Verified && mac2Verified);
    }

    @Test
    public void testMacsForDifferentDataAreDifferent() {

        boolean mac1Verified =
            _cryptoMac.verify(_key, _mac1, TEST_DATA_2.getBytes());

        boolean mac2Verified =
            _cryptoMac.verify(_key, _mac2, TEST_DATA_1.getBytes());

        assertTrue(!mac1Verified && !mac2Verified);
    }

    @Test
    public void testMacsAreSameForJavaAndJavaScript() {

        byte[] keyBytes = Base64.getDecoder().decode(SECRET_KEY_BASE64_JS);
        SecretKeySpec key =
            new SecretKeySpec(keyBytes, _symmetricKeyPolicyFromProperties
                .getHmacSecretKeyAlgorithmAndSpec().getAlgorithm());
        int keyBitLength = key.getEncoded().length * Byte.SIZE;

        byte[] mac = _cryptoMac.generate(key, DATA_JS.getBytes());

        boolean macVerified = _cryptoMac.verify(key, mac, DATA_JS.getBytes());

        String macBase64 = Base64.getEncoder().encodeToString(mac);

        assertTrue(keyBitLength == _symmetricKeyPolicyFromProperties
            .getHmacSecretKeyAlgorithmAndSpec().getKeyLengthInBits());
        assertTrue(macVerified);
        assertTrue(macBase64.equals(MAC_BASE64_JS));
    }
}
