/*
 * Copyright 2002-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * imitations under the License.
 */

package test;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.ECGenParameterSpec;

import org.apache.security.juice.generators.ECKeyPairGeneratorOpenSSL;

import java.math.BigInteger;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;

import com.clarkware.junitperf.TimedTest;

/**
 * Unit tests to test the DSA signature.
 * 
 * The class generates a DSA signature with the openSSL functions and compares
 * the result with a DSA signature generated by the BouncyCastle implementation
 * and vices versa.
 * 
 * Created on 01.10.2005
 * 
 * @author Werner Dittmann (werner@apache.org)
 * 
 */

public class OpenSSLTestSignaturesECDSA extends TestCase {

    static KeyPair keyPair = null;

    Signature sigJuiceSign = null;

    Signature sigBCSign = null;

    Signature sigJuiceVerify = null;

    Signature sigBCVerify = null;

    static byte[] plainData = null;
    
    static String curve = null;

    byte[] signatureDataJuice = null;

    byte[] signatureDataBC = null;

    static {
        try {
            Class<?> c = Class
                    .forName("org.apache.security.juice.provider.JuiCEProviderOpenSSL");
            java.security.Security.addProvider((java.security.Provider) c
                    .newInstance());
            c = Class
                    .forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
            java.security.Security.addProvider((java.security.Provider) c
                    .newInstance());
        } catch (Throwable t) {
        }

        plainData = new byte[1024];
        for (int i = 0; i < plainData.length; i++) {
            plainData[i] = (byte) (i + 1);
        }
    }

    public OpenSSLTestSignaturesECDSA(String name) {
        super(name);
    }

    private void genKey(String curveName) {
        try {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA",
                    "BC");
            keyGen.initialize(new ECGenParameterSpec(curveName));
            keyPair = keyGen.generateKeyPair();

            // Following code only for debugging - needs specific function
            // in native lib as well.

            // ECPublicKey BCpubKey = (ECPublicKey) keyPair.getPublic();
            // ECPrivateKey BCprivKey = (ECPrivateKey) keyPair.getPrivate();
            //
            // BigInteger biPriv = BCprivKey.getD();
            // byte[] privb = biPriv.toByteArray();
            // System.out.println("Private key value: " + biPriv.toString(16));
            //
            // BigInteger biPubX = BCpubKey.getQ().getX().toBigInteger();
            // BigInteger biPubY = BCpubKey.getQ().getY().toBigInteger();
            //
            // System.out.println("Public key values: " + BCpubKey.toString());
            //
            // ECNamedCurveParameterSpec enps = (ECNamedCurveParameterSpec)
            // BCpubKey
            // .getParameters();
            // System.out.println("Curve name: " + enps.getName());
            //
            // BigInteger biq = ((ECCurve.Fp) enps.getCurve()).getQ();
            // System.out.println("Curve Q: " + biq.toString(16));
            //
            // ECPoint ep = enps.getG();
            // System.out.println("GX: " +
            // ep.getX().toBigInteger().toString(16));
            //
            // BigInteger biY = ep.getY().toBigInteger();
            // System.out.println("GY: " + biY.toString(16));
            //
            // int [] lengths = new int[2];
            // ECKeyPairGeneratorOpenSSL.generateECKeyPrivOpenSSL(enps.getName(),
            // privb,
            // privb.length, lengths);

        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void setSignaturesSSL(String sigName) {

        try {
            /*
             * initialize ciphers: use the openSSL implementation and the
             * standard BC implementation (as reference)
             */
            sigJuiceSign = Signature.getInstance(sigName, "JuiCE");
            sigJuiceVerify = Signature.getInstance(sigName, "JuiCE");
            /*
             * Check if cipher providers are correct, i.e the correct provider
             * was chosen.
             */
            assertEquals("Wrong class of openSSL provider", sigJuiceSign
                    .getProvider().getClass().getName(),
                    "org.apache.security.juice.provider.JuiCEProviderOpenSSL");

            /*
             * Initialize the Ciphers
             */
            try {
                sigJuiceSign.initSign(keyPair.getPrivate());
                sigJuiceVerify.initVerify(keyPair.getPublic());
            } catch (InvalidKeyException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void setSignaturesBC(String sigName) {

        try {
            /*
             * initialize ciphers: use the openSSL implementation and the
             * standard BC implementation (as reference)
             */
            sigBCSign = Signature.getInstance(sigName, "BC");
            sigBCVerify = Signature.getInstance(sigName, "BC");

            /*
             * Check if cipher providers are correct, i.e the correct provider
             * was chosen.
             */
            assertEquals("Wrong class of BC provider", sigBCSign.getProvider()
                    .getClass().getName(),
                    "org.bouncycastle.jce.provider.BouncyCastleProvider");
            /*
             * Initialize the Ciphers
             */
            try {
                sigBCSign.initSign(keyPair.getPrivate());
                sigBCVerify.initVerify(keyPair.getPublic());
            } catch (InvalidKeyException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    void sign() {
        try {
            sigJuiceSign.update(plainData);
            signatureDataJuice = sigJuiceSign.sign();

            sigBCSign.update(plainData);
            signatureDataBC = sigBCSign.sign();

        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SignatureException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    void verify() {
        try {
            sigJuiceVerify.update(plainData);
            assertTrue("Verify Juice Signature with Juice failed",
                    sigJuiceVerify.verify(signatureDataJuice));

            sigJuiceVerify.update(plainData);
            assertTrue("Verify BC Signature with Juice failed", sigJuiceVerify
                    .verify(signatureDataBC));

            sigBCVerify.update(plainData);
            assertTrue("Verify Juice Signature with BC failed", sigBCVerify
                    .verify(signatureDataJuice));

            sigBCVerify.update(plainData);
            assertTrue("Verify BC Signature with BC failed", sigBCVerify
                    .verify(signatureDataBC));
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SignatureException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void signVerifySSL() {
        genKey(curve);        
        setSignaturesSSL("ECDSA");
        try {
            sigJuiceSign.update(plainData);
            signatureDataJuice = sigJuiceSign.sign();
            sigJuiceVerify.update(plainData);
            sigJuiceVerify.verify(signatureDataJuice);
        } catch (SignatureException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void signVerifyBC() {
        genKey(curve);
        setSignaturesBC("ECDSA");
        try {
            sigBCSign.update(plainData);
            signatureDataBC = sigBCSign.sign();
            sigBCVerify.update(plainData);
            sigBCVerify.verify(signatureDataBC);
        } catch (SignatureException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    void performTest() {
        sign();
        verify();
    }

    public void testSHA1_ECDSA_PRIME192V1() {
        genKey("prime192v1");
        setSignaturesSSL("ECDSA");
        setSignaturesBC("ECDSA");
        performTest();
    }

    public void testSHA1_ECDSA_SECP224R1() {
        genKey("secp224r1");
        setSignaturesSSL("ECDSA");
        setSignaturesBC("ECDSA");
        performTest();
    }

    public void testSHA1_ECDSA_SECT163R2() {
        genKey("sect163r2");
        setSignaturesSSL("ECDSA");
        setSignaturesBC("ECDSA");
        performTest();
    }

    /**
     * JUnit suite <p/>
     * 
     * @return a junit test suite
     */
    public static Test suite() {
        TestSuite suite = new TestSuite();
        suite.addTest(new OpenSSLTestSignaturesECDSA(
                "testSHA1_ECDSA_PRIME192V1"));
        suite
                .addTest(new OpenSSLTestSignaturesECDSA(
                        "testSHA1_ECDSA_SECP224R1"));
        suite
                .addTest(new OpenSSLTestSignaturesECDSA(
                        "testSHA1_ECDSA_SECT163R2"));
        return suite;
    }

    public static Test performance() {
        TestSuite suite = new TestSuite();
        suite.addTest(new TimedTest(new OpenSSLTestSignaturesDSA(
                "signVerifySSL"), 1000));
        suite.addTest(new TimedTest(
                new OpenSSLTestSignaturesDSA("signVerifyBC"), 1000));

        return suite;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        junit.textui.TestRunner.run(suite());
        
        if (args.length > 0) {
            curve = args[0];
            System.out.println("Performance testing for curve: " + curve);
            junit.textui.TestRunner.run(performance());
        }
    }

}
