/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.math.BigInteger;
import java.security.KeyStore;
import org.cryptacular.FailListener;
import org.cryptacular.generator.Nonce;
import org.cryptacular.generator.sp80038a.BigIntegerCounterNonce;
import org.cryptacular.generator.sp80038a.LongCounterNonce;
import org.cryptacular.generator.sp80038a.RBGNonce;
import org.cryptacular.io.FileResource;
import org.cryptacular.spec.BufferedBlockCipherSpec;
import org.cryptacular.util.ByteUtil;
import org.cryptacular.util.StreamUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.*;

/**
 * Unit test for {@link BufferedBlockCipherBean}.
 *
 * @author  Middleware Services
 */
@Listeners(FailListener.class)
public class BufferedBlockCipherBeanTest
{
  @DataProvider(name = "test-arrays")
  public Object[][] getTestArrays()
  {
    return
      new Object[][] {
        new Object[] {
          // Plaintext is NOT multiple of block size
          "Able was I ere I saw elba.",
          "AES/CBC/PKCS5",
          new RBGNonce(16),
        },
        // Plaintext is multiple of block size
        new Object[] {
          "Four score and seven years ago, our forefathers ",
          "Blowfish/CBC/None",
          new RBGNonce(8),
        },
        // OFB
        new Object[] {
          "Have you passed through this night?",
          "Blowfish/OFB/PKCS5Padding",
          new LongCounterNonce(),
        },
        // CFB
        new Object[] {
          "I went to the woods because I wished to live deliberately, to front only the essential facts of life",
          "AES/CFB/PKCS5Padding",
          new RBGNonce(16),
        },
      };
  }

  @DataProvider(name = "test-streams")
  public Object[][] getTestStreams()
  {
    return
      new Object[][] {
        new Object[] {
          "src/test/resources/plaintexts/lorem-5000.txt",
          "AES/CBC/PKCS7",
          new RBGNonce(16),
        },
        new Object[] {
          "src/test/resources/plaintexts/lorem-1200.txt",
          "Twofish/OFB/NULL",
          new BigIntegerCounterNonce(BigInteger.ONE, 16),
        },
        new Object[] {
          "src/test/resources/plaintexts/lorem-1200.txt",
          "AES/CFB/PKCS5",
          new RBGNonce(16),
        },
        new Object[] {
          "src/test/resources/plaintexts/lorem-1200.txt",
          "AES/ECB/PKCS5",
          new RBGNonce(16),
        },
      };
  }


  @Test(dataProvider = "test-arrays")
  public void testEncryptDecryptArray(final String input, final String cipherSpecString, final Nonce nonce)
    throws Exception
  {
    final BufferedBlockCipherSpec cipherSpec = BufferedBlockCipherSpec.parse(cipherSpecString);
    final BufferedBlockCipherBean cipherBean = new BufferedBlockCipherBean(
      cipherSpec, getTestKeyStore(), "vtcrypt", "vtcrypt", nonce);

    final byte[] ciphertext = cipherBean.encrypt(ByteUtil.toBytes(input));
    assertThat(ByteUtil.toString(cipherBean.decrypt(ciphertext))).isEqualTo(input);
  }


  @Test(dataProvider = "test-streams")
  public void testEncryptDecryptStream(final String path, final String cipherSpecString, final Nonce nonce)
    throws Exception
  {
    final BufferedBlockCipherSpec cipherSpec = BufferedBlockCipherSpec.parse(cipherSpecString);
    final BufferedBlockCipherBean cipherBean = new BufferedBlockCipherBean(
      cipherSpec, getTestKeyStore(), "vtcrypt", "vtcrypt", nonce);

    final ByteArrayOutputStream tempOut = new ByteArrayOutputStream(8192);
    cipherBean.encrypt(StreamUtil.makeStream(new File(path)), tempOut);

    final ByteArrayInputStream tempIn = new ByteArrayInputStream(tempOut.toByteArray());
    final ByteArrayOutputStream finalOut = new ByteArrayOutputStream(8192);
    cipherBean.decrypt(tempIn, finalOut);
    assertThat(ByteUtil.toString(finalOut.toByteArray())).isEqualTo(ByteUtil.toString(StreamUtil.readAll(path)));
  }

  private static KeyStore getTestKeyStore()
  {
    final KeyStoreFactoryBean bean = new KeyStoreFactoryBean(
      new FileResource(new File("src/test/resources/keystores/cipher-bean.jceks")), "JCEKS", "vtcrypt");
    return bean.newInstance();
  }
}
