package org.bouncycastle.jce.provider.test;

import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.SimpleTestResult;
import org.bouncycastle.util.test.Test;
import org.bouncycastle.util.test.TestResult;

/**
 * test out the various PBE modes, making sure the JCE implementations
 * are compatible woth the light weight ones.
 */
public class PBETest implements Test
{
    private Cipher makePBECipher(
        String  algorithm,
        int     mode,
        char[]  password,
        byte[]  salt,
        int     iterationCount)
        throws Exception
    {
        PBEKeySpec          pbeSpec = new PBEKeySpec(password);
        SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(algorithm, "BC");
        PBEParameterSpec    defParams = new PBEParameterSpec(salt, iterationCount);

        Cipher cipher = Cipher.getInstance(algorithm, "BC");

        cipher.init(mode, keyFact.generateSecret(pbeSpec), defParams);

        return cipher;
    }

    private boolean arrayEquals(
        byte[]  a,
        byte[]  b)
    {
        if (a.length != b.length)
        {
            return false;
        }

        for (int i = 0; i != a.length; i++)
        {
            if (a[i] != b[i])
            {
                return false;
            }
        }

        return true;
    }

    public TestResult perform()
    {
        byte[] input = Hex.decode("1234567890abcdefabcdef1234567890fedbca098765");

        try
        {
            //
            // DES
            //
            Cipher  cEnc = Cipher.getInstance("DES/CBC/PKCS7Padding", "BC");

            cEnc.init(Cipher.ENCRYPT_MODE,
                new SecretKeySpec(Hex.decode("30e69252758e5346"), "DES"),
                new IvParameterSpec(Hex.decode("7c1c1ab9c454a688")));

            byte[]  out = cEnc.doFinal(input);

            char[]  password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };

            Cipher  cDec = makePBECipher(
                                "PBEWithSHA1AndDES",
                                Cipher.DECRYPT_MODE,
                                password,
                                Hex.decode("7d60435f02e9e0ae"),
                                2048);

            byte[]  in = cDec.doFinal(out);

            if (!arrayEquals(input, in))
            {
                return new SimpleTestResult(false, getName() + ": DES failed");
            }

            //
            // DESede
            //
            cEnc = Cipher.getInstance("DESede/CBC/PKCS7Padding", "BC");

            cEnc.init(Cipher.ENCRYPT_MODE,
                new SecretKeySpec(Hex.decode("732f2d33c801732b7206756cbd44f9c1c103ddd97c7cbe8e"), "DES"),
                new IvParameterSpec(Hex.decode("b07bf522c8d608b8")));

            out = cEnc.doFinal(input);

            cDec = makePBECipher(
                                "PBEWithSHAAnd3-KeyTripleDES-CBC",
                                Cipher.DECRYPT_MODE,
                                password,
                                Hex.decode("7d60435f02e9e0ae"),
                                2048);

            in = cDec.doFinal(out);

            if (!arrayEquals(input, in))
            {
                return new SimpleTestResult(false, getName() + ": DESede failed");
            }

            //
            // 40Bit RC2
            //
            cEnc = Cipher.getInstance("RC2/CBC/PKCS7Padding", "BC");

            cEnc.init(Cipher.ENCRYPT_MODE,
                new SecretKeySpec(Hex.decode("732f2d33c8"), "RC2"),
                new IvParameterSpec(Hex.decode("b07bf522c8d608b8")));

            out = cEnc.doFinal(input);

            cDec = makePBECipher(
                                "PBEWithSHAAnd40BitRC2-CBC",
                                Cipher.DECRYPT_MODE,
                                password,
                                Hex.decode("7d60435f02e9e0ae"),
                                2048);

            in = cDec.doFinal(out);

            if (!arrayEquals(input, in))
            {
                return new SimpleTestResult(false, getName() + ": RC2 failed");
            }

            //
            // 128bit RC4
            //
            cEnc = Cipher.getInstance("RC4", "BC");

            cEnc.init(Cipher.ENCRYPT_MODE,
                new SecretKeySpec(Hex.decode("732f2d33c801732b7206756cbd44f9c1"), "RC4"));

            out = cEnc.doFinal(input);

            cDec = makePBECipher(
                                "PBEWithSHAAnd128BitRC4",
                                Cipher.DECRYPT_MODE,
                                password,
                                Hex.decode("7d60435f02e9e0ae"),
                                2048);

            in = cDec.doFinal(out);

            if (!arrayEquals(input, in))
            {
                return new SimpleTestResult(false, getName() + ": RC4 failed");
            }

            return new SimpleTestResult(true, getName() + ": Okay");
        }
        catch (Exception e)
        {
e.printStackTrace();
            return new SimpleTestResult(false, getName() + ": exception - " + e.toString());
        }
    }

    public String getName()
    {
        return "PBETest";
    }

    public static void main(
        String[]    args)
    {
        Security.addProvider(new BouncyCastleProvider());

        Test            test = new PBETest();
        TestResult      result = test.perform();

        System.out.println(result.toString());
    }
}
