/*
 * 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.commons.sign;

import java.io.IOException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;

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

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.primitives.PrimitivesServiceAPI;
import com.scytl.cryptolib.asymmetric.service.AsymmetricService;
import com.scytl.cryptolib.primitives.service.PrimitivesService;
import com.scytl.products.ov.commons.exception.OvCommonsSignException;
import com.scytl.products.ov.commons.sign.beans.SignedRequestContent;
import com.scytl.products.ov.commons.verify.RequestVerifier;

public class RequestSignerTest {

    private static KeyPair keyPair;

    private static PublicKey publicKey;

    private static PrivateKey privateKey;

    private static AsymmetricService asymmetricService;

    @BeforeClass
    public static void setupTests() throws NoSuchAlgorithmException, GeneralCryptoLibException {

        Security.addProvider(new BouncyCastleProvider());
        asymmetricService = new AsymmetricService();
        keyPair = asymmetricService.getKeyPairForSigning();
        publicKey = keyPair.getPublic();
        privateKey = keyPair.getPrivate();
    }

    @Test
    public void testGenerateAndValidateValidSignature()
            throws IOException, GeneralCryptoLibException, OvCommonsSignException {

        RequestSigner requestSigner = new RequestSigner();
        SignedRequestContent signedRequestContent = new SignedRequestContent("localhost", "get", "body", "originator");

        byte[] signatureBytes = requestSigner.sign(signedRequestContent, privateKey);

        Assert.assertNotNull(signatureBytes);

        RequestVerifier requestVerifier = new RequestVerifier();

        boolean isValidSignature = requestVerifier.verifySignature(signedRequestContent, signatureBytes, publicKey);

        Assert.assertTrue(isValidSignature);

    }

    @Test
    public void testGetBytesMethod() throws IOException, NoSuchAlgorithmException {

        SignedRequestContent object1 = new SignedRequestContent("localhost", "get", "body", "originator");
        byte[] bytes1 = object1.getBytes();

        SignedRequestContent object2 = new SignedRequestContent("localhost", "get", "body", "originator");
        byte[] bytes2 = object2.getBytes();

        Assert.assertArrayEquals(bytes1, bytes2);

        SignedRequestContent object3 = new SignedRequestContent("localhost", "post", "body", "originator");
        byte[] bytes3 = object3.getBytes();

        Assert.assertFalse(Arrays.equals(bytes1, bytes3));

    }

    @Test(expected = OvCommonsSignException.class)
    public void testNothingToSign()
            throws IOException, GeneralCryptoLibException, OvCommonsSignException, NoSuchAlgorithmException,
            InvalidKeySpecException {

        RequestSigner requestSigner = new RequestSigner();

        requestSigner.sign(null, privateKey);

    }

    @Test(expected = OvCommonsSignException.class)
    public void testInvalidKey() throws IOException, GeneralCryptoLibException, OvCommonsSignException {

        RequestSigner requestSigner = new RequestSigner();
        SignedRequestContent signedRequestContent = new SignedRequestContent("localhost", "get", "body", "originator");

        requestSigner.sign(signedRequestContent, null);

    }

    @Test
    public void testKeys() throws GeneralCryptoLibException {

        SignedRequestContent signedRequestContent = new SignedRequestContent("localhost", "get", "body", "originator");

        byte[] objectBytes = signedRequestContent.getBytes();

        PrimitivesServiceAPI primitivesService = new PrimitivesService();
        byte[] objectHash = primitivesService.getHash(objectBytes);

        byte[] signatureBytes = asymmetricService.sign(privateKey, objectHash);

        boolean isValidSignature = asymmetricService.verifySignature(signatureBytes, publicKey, objectHash);

        Assert.assertTrue(isValidSignature);
    }

    @Test
    public void testInvalidSignatureBodyModified()
            throws IOException, GeneralCryptoLibException, OvCommonsSignException {

        RequestSigner requestSigner = new RequestSigner();
        SignedRequestContent originalSignedRequestContent =
            new SignedRequestContent("localhost", "get", "body", "originator");

        byte[] signatureBytes = requestSigner.sign(originalSignedRequestContent, privateKey);

        Assert.assertNotNull(signatureBytes);

        RequestVerifier requestVerifier = new RequestVerifier();

        SignedRequestContent modifiedSignedRequestContent =
            new SignedRequestContent("localhost", "get", "body modified", "originator");

        boolean isValidSignature =
            requestVerifier.verifySignature(modifiedSignedRequestContent, signatureBytes, publicKey);

        Assert.assertFalse(isValidSignature);

    }

    @Test
    public void testEmptyBody() throws IOException, GeneralCryptoLibException, OvCommonsSignException {

        RequestSigner requestSigner = new RequestSigner();
        SignedRequestContent signedRequestContent = new SignedRequestContent("localhost", "get", "", "originator");

        byte[] signatureBytes = requestSigner.sign(signedRequestContent, privateKey);

        Assert.assertNotNull(signatureBytes);

        RequestVerifier requestVerifier = new RequestVerifier();

        boolean isValidSignature = requestVerifier.verifySignature(signedRequestContent, signatureBytes, publicKey);

        Assert.assertTrue(isValidSignature);

    }
}
