/*
 * 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.KeyPairGenerator;
import java.security.KeyPair;
import java.security.Signature;
import java.security.SignatureException;

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;

    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) {
        }

        try {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA",
                    "BC");
            keyGen.initialize(192);
            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();
        }
        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 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() {
        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() {
        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() {
        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"));

        // 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());
    }

}
