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

import static org.junit.Assert.assertEquals;

import com.scytl.slogger.crypto.SecureLoggerCryptographyFactory;
import com.scytl.slogger.crypto.SecureLoggerCryptographyProvider;
import com.scytl.slogger.crypto.SecureLoggerKeyStore;

import org.apache.log4j.Logger;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.spi.ErrorHandler;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;

/**
 * Test SecureAppender works fine with a binary P12 file
 */
public class BinaryP12FileTest extends AbstractSecureAppenderTest {

  private SecureLoggerCryptographyProvider provider;

  @Override
  @Before
  public void setUp() throws Exception {
    super.setUp();
    provider = new SecureLoggerCryptographyFactory().build();
  }

  @Rule
  public ExpectedException thrown = ExpectedException.none();

  @Test
  public void testEqualFileNames()
      throws SecureLoggerException, GeneralSecurityException, IOException {

    thrown.expect(SecureLoggerException.class);

    secureAppender = new SecureAppender();

    secureAppender.setSignaturePkcs12FileName(SIGNATURE_PKCS12_FILE);
    secureAppender.setCipherPkcs12FileName(SIGNATURE_PKCS12_FILE);

    secureAppender.setSignaturePkcs12Password(SIGNATURE_PKCS12_PASSWORD);
    secureAppender.setCipherPkcs12Password(SIGNATURE_PKCS12_PASSWORD);

    secureAppender.doActivateOptions();
  }

  @Test
  public void testEqualKeyStores()
      throws SecureLoggerException, GeneralSecurityException, IOException {

    thrown.expect(SecureLoggerException.class);

    secureAppender = new SecureAppender();

    KeyStore store;
    try (InputStream stream =
        Thread.currentThread().getContextClassLoader().getResourceAsStream(SIGNATURE_PKCS12_FILE)) {
      store = provider.readKeyStore(stream, SIGNATURE_PKCS12_PASSWORD.toCharArray()).getKeyStore();
    }

    secureAppender.setSignaturePkcs12Certificate(store);
    secureAppender.setCipherPkcs12Certificate(store);

    secureAppender.setSignaturePkcs12Password(SIGNATURE_PKCS12_PASSWORD);
    secureAppender.setCipherPkcs12Password(SIGNATURE_PKCS12_PASSWORD);

    secureAppender.doActivateOptions();
  }

  @Test
  public void testEqualPrivateKeys()
      throws SecureLoggerException, GeneralSecurityException, IOException {

    thrown.expect(SecureLoggerException.class);

    secureAppender = new SecureAppender();

    PrivateKey privateKey = getSignaturePrivateKey();
    secureAppender.setSignaturePrivateKey(privateKey);
    secureAppender.setCipherPrivateKey(privateKey);

    secureAppender.doActivateOptions();
  }

  @Test
  public void testInvalidSignatureAlgorithm()
      throws SecureLoggerException, GeneralSecurityException, IOException {

    thrown.expect(SecureLoggerException.class);

    secureAppender = new SecureAppender();

    KeyPairGenerator generator = KeyPairGenerator.getInstance("DSA");
    KeyPair pair = generator.genKeyPair();
    secureAppender.setSignaturePrivateKey(pair.getPrivate());

    secureAppender.setCipherPkcs12FileName(CIPHER_PKCS12_FILE);
    secureAppender.setCipherPkcs12Password(CIPHER_PKCS12_PASSWORD);

    secureAppender.doActivateOptions();
  }

  @Test
  public void testInvalidSignatureKeyLength()
      throws SecureLoggerException, GeneralSecurityException, IOException {

    thrown.expect(SecureLoggerException.class);

    secureAppender = new SecureAppender();

    PrivateKey key = getSignaturePrivateKey();
    KeyPairGenerator generator = KeyPairGenerator.getInstance(key.getAlgorithm());
    generator.initialize(1024, new SecureRandom());
    KeyPair pair = generator.genKeyPair();
    secureAppender.setSignaturePrivateKey(pair.getPrivate());

    secureAppender.setCipherPkcs12FileName(CIPHER_PKCS12_FILE);
    secureAppender.setCipherPkcs12Password(CIPHER_PKCS12_PASSWORD);

    secureAppender.doActivateOptions();
  }

  @Test
  public void testInvalidCipherAlgorithm()
      throws SecureLoggerException, GeneralSecurityException, IOException {

    thrown.expect(SecureLoggerException.class);

    secureAppender = new SecureAppender();

    secureAppender.setSignaturePkcs12FileName(SIGNATURE_PKCS12_FILE);
    secureAppender.setSignaturePkcs12Password(SIGNATURE_PKCS12_PASSWORD);

    KeyPairGenerator generator = KeyPairGenerator.getInstance("DSA");
    KeyPair pair = generator.genKeyPair();
    secureAppender.setCipherPublicKey(pair.getPublic());

    secureAppender.doActivateOptions();
  }

  @Test
  public void testInvalidCipherKeyLength()
      throws SecureLoggerException, GeneralSecurityException, IOException {

    thrown.expect(SecureLoggerException.class);

    secureAppender = new SecureAppender();

    secureAppender.setSignaturePkcs12FileName(SIGNATURE_PKCS12_FILE);
    secureAppender.setSignaturePkcs12Password(SIGNATURE_PKCS12_PASSWORD);

    PrivateKey key = getCipherPrivateKey();
    KeyPairGenerator generator = KeyPairGenerator.getInstance(key.getAlgorithm());
    generator.initialize(1024, new SecureRandom());
    KeyPair pair = generator.genKeyPair();
    secureAppender.setCipherPrivateKey(pair.getPrivate());
    secureAppender.setCipherPublicKey(pair.getPublic());

    secureAppender.doActivateOptions();
  }

  @Test
  public void testInitLogger() throws Exception {

    secureAppender = new SecureAppender();

    secureAppender.setName(NAME);
    secureAppender.setFile(testLogFile.getAbsolutePath());
    secureAppender.setPattern(PATTERN);
    secureAppender.setVerifyPattern(VERIFY_PATTERN);
    secureAppender.setNumberLogLines(NUMBER_LOG_LINES);
    secureAppender.setTimerLogMilliseconds(0);
    secureAppender.setTriggeringTime(TRIGGERING_TIME);

    secureAppender.setSignaturePkcs12FileName(SIGNATURE_PKCS12_FILE);
    secureAppender.setCipherPkcs12FileName(CIPHER_PKCS12_FILE);

    secureAppender.setSignaturePkcs12Password(SIGNATURE_PKCS12_PASSWORD);
    secureAppender.setCipherPkcs12Password(CIPHER_PKCS12_PASSWORD);

    RollingFileAppender fileAppender = new RollingFileAppender();
    ErrorHandler errorHandler = new SecureErrorHandler();
    errorHandler.setBackupAppender(fileAppender);
    secureAppender.setErrorHandler(errorHandler);

    secureAppender.activateOptions();

    Logger logger = Logger.getRootLogger();
    logger.addAppender(secureAppender);
    logger.info("The secure logger is activated now");
    assertEquals("Directory should contain a file", 1, getFolder().getRoot().list().length);
  }

  @Test
  public void testNonMatchingCipherKeys()
      throws SecureLoggerException, GeneralSecurityException, IOException {

    thrown.expect(GeneralSecurityException.class);

    secureAppender = new SecureAppender();

    secureAppender.setSignaturePrivateKey(getSignaturePrivateKey());

    PrivateKey cipherPrivateKey = getCipherPrivateKey();
    KeyPairGenerator generator = KeyPairGenerator.getInstance(cipherPrivateKey.getAlgorithm());
    generator.initialize(2048, new SecureRandom());
    PublicKey cipherPublicKey = generator.generateKeyPair().getPublic();

    secureAppender.setCipherPrivateKey(cipherPrivateKey);
    secureAppender.setCipherPublicKey(cipherPublicKey);

    secureAppender.doActivateOptions();
  }

  private PrivateKey getCipherPrivateKey() throws IOException, GeneralSecurityException {
    SecureLoggerKeyStore store;
    try (InputStream stream =
        Thread.currentThread().getContextClassLoader().getResourceAsStream(CIPHER_PKCS12_FILE)) {
      store = provider.readKeyStore(stream, CIPHER_PKCS12_PASSWORD.toCharArray());
    }
    return provider.getUserCipherPrivateKey(store, CIPHER_PKCS12_PASSWORD.toCharArray());
  }

  private PrivateKey getSignaturePrivateKey() throws IOException, GeneralSecurityException {
    SecureLoggerKeyStore store;
    try (InputStream stream =
        Thread.currentThread().getContextClassLoader().getResourceAsStream(SIGNATURE_PKCS12_FILE)) {
      store = provider.readKeyStore(stream, SIGNATURE_PKCS12_PASSWORD.toCharArray());
    }
    return provider.getUserSignaturePrivateKey(store, SIGNATURE_PKCS12_PASSWORD.toCharArray());
  }
}
