/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

 
//------------------------------------------------------------
// includes
//------------------------------------------------------------

#include <assert.h>
#include <iostream.h>
#include "jkl.h"
#include "test_jkl.h"


//------------------------------------------------------------
// test functions
//------------------------------------------------------------

void
testDigest(CSSM_HANDLE cspHandle[], uint32 numOfCsps);

void
testKeyConversions(CSSM_HANDLE cspHandle1, CSSM_HANDLE cspHandle2);

void
test3DES(CSSM_HANDLE cspHandle[], uint32 numOfCsps);


//------------------------------------------------------------
// function: TestLowLevelAPIs
//------------------------------------------------------------

void
TestLowLevelAPIs()
{
   uint32 status = 0;

   CSSM_HANDLE cspHandle[2];
   CSSM_MEMORY_FUNCS memoryFuncs = { DefaultMalloc, DefaultFree, DefaultRealloc, DefaultCalloc, 0 };

   //------------------------------------------------------------
   // Initialize CDSA
   //------------------------------------------------------------

   if ((status = JKL_InitializeCSSM(memoryFuncs)) != 0)
      throw TestException(status);

   //------------------------------------------------------------
   // Attach to 2 CSPs
   //------------------------------------------------------------

   CSSM_GUID cspGuid[2];
   cspGuid[0] = * (CSSM_GUID_PTR)JKL_Get_CylinkCsp_GUID();
   cspGuid[1] = * (CSSM_GUID_PTR)JKL_Get_SwCsp_GUID();

   for (int i = 0; i < 2; i++)
   {
      if ((status = JKL_AttachCSP(cspGuid[i], cspHandle[i])) != 0)
         throw TestException(status);
   }

   //------------------------------------------------------------
   // Test Cases
   //------------------------------------------------------------

   testDigest(cspHandle, 2);
   test3DES(cspHandle, 2);
   testKeyConversions(cspHandle[0], cspHandle[1]);

   //------------------------------------------------------------
   // Detach from CDSA framework
   //------------------------------------------------------------

   for (i = 0; i < 2; i++)
   {
      if ((status = JKL_ModuleDetach(cspHandle[i])) != 0)
         throw TestException(status);
   }
}


//------------------------------------------------------------
// function: testDigest
//------------------------------------------------------------

void
testDigest(CSSM_HANDLE cspHandle[], uint32 numOfCsps)
{
   uint32 status = 0;
   uint32 i;
   uint32 j;

   //------------------------------------------------------------
   // Goal: Ensure cross-CSP compatibility of digest output.
   //------------------------------------------------------------

   uint32 randomDataSize = 256;
   CSSM_DATA cssmRandomData;

   for (i = 0; i < numOfCsps; i++)
   {
      //------------------------------------------------------------
      // Generate random data as sample hash input
      //------------------------------------------------------------

      if ((status = JKL_GenerateRandomData(cspHandle[i],
                                           randomDataSize,
                                           cssmRandomData)) != 0)
         throw TestException(status);

      //------------------------------------------------------------
      // Have each CSP hash the data
      //------------------------------------------------------------
   
      asn_octetstring* digest = new asn_octetstring[numOfCsps];
   
      for (j = 0; j < numOfCsps; j++)
      {
         if ((status = JKL_DigestData(cspHandle[j],
                                      CSSM_ALGID_SHA1,
                                      cssmRandomData,
                                      digest[j])) != 0)
         throw TestException(status);
      }

      //------------------------------------------------------------
      // Verify all digests are the same
      //------------------------------------------------------------
   
      for (j = 1; j < numOfCsps; j++)
      {
         if (! (digest[0] == digest[j]) )
            throw "CSP Digest Output Not Consistent";
      }

      //------------------------------------------------------------
      // Clean up allocated resources
      //------------------------------------------------------------
   
      delete [] digest;
      JKL_FreeData(cssmRandomData);
   }
}


//------------------------------------------------------------
// function: testKeyConversions
//------------------------------------------------------------

void
testKeyConversions(CSSM_HANDLE cspHandle1, 
                   CSSM_HANDLE cspHandle2)
{
   uint32 status = 0;

   //------------------------------------------------------------
   // Goal: Test ASN/CSSM/ASN conversion routines.
   // CSSM_KEY formats are CSP specific. ASN conversion routines
   // convert to platform idependent DER encodings.
   //------------------------------------------------------------

   CSSM_KEY cssmPublicKey[2];
   CSSM_KEY cssmPrivateKey[2];
   CSSM_ALGORITHMS cssmKeyPairType = CSSM_ALGID_DSA;

   //------------------------------------------------------------
   // Generate sample key pairs
   //------------------------------------------------------------

   if ((status = JKL_GenerateKeyPair(cspHandle1,
                                     cssmKeyPairType,
                                     512,
                                     cssmPublicKey[0],
                                     cssmPrivateKey[0])) != 0)
      throw TestException(status);

   if ((status = JKL_GenerateKeyPair(cspHandle1,
                                     cssmKeyPairType,
                                     512,
                                     cssmPublicKey[1],
                                     cssmPrivateKey[1])) != 0)
      throw TestException(status);
   
   //------------------------------------------------------------
   // Convert keys from CSSM to ASN
   //------------------------------------------------------------

   SubjectPublicKeyInfo subjectPublicKeyInfo;

   if ((status = JKL_cssm_to_asn(cssmPublicKey[0], subjectPublicKeyInfo)) != 0)
      throw TestException(status);

   PrivateKeyInfo privateKeyInfo;

   if ((status = JKL_cssm_to_asn(cssmPrivateKey[0], privateKeyInfo)) != 0)
      throw TestException(status);

   //------------------------------------------------------------
   // same data; go from ASN to CSSM
   //------------------------------------------------------------

   CSSM_KEY cssmPublicKeyTest;
   CSSM_KEY cssmPrivateKeyTest;

   if ((status = JKL_asn_to_cssm(subjectPublicKeyInfo,
                                 cssmPublicKeyTest,
                                 *(CSSM_GUID_PTR)JKL_Get_CylinkCsp_GUID())) != 0)
      throw TestException(status);

   if ((status = JKL_asn_to_cssm(privateKeyInfo,
                                 subjectPublicKeyInfo,
                                 cssmPrivateKeyTest,
                                 *(CSSM_GUID_PTR)JKL_Get_CylinkCsp_GUID())) != 0)
      throw TestException(status);

   //------------------------------------------------------------
   // Verify CSSM->ASN->CSSM transformation is identity operator
   //------------------------------------------------------------

   if ( false == JKL_CompareData(cssmPublicKey[0].KeyData, cssmPublicKeyTest.KeyData) )
      throw "CSSM to ASN conversion failed";

   if ( false == JKL_CompareData(cssmPrivateKey[0].KeyData, cssmPrivateKeyTest.KeyData) )
      throw "CSSM to ASN conversion failed";

   //------------------------------------------------------------
   // Clean up allocated resources
   //------------------------------------------------------------

   JKL_FreeData(cssmPublicKeyTest);
   JKL_FreeData(cssmPrivateKeyTest);
   JKL_FreeData(cssmPublicKey[0]);
   JKL_FreeData(cssmPrivateKey[0]);
   JKL_FreeData(cssmPublicKey[1]);
   JKL_FreeData(cssmPrivateKey[1]);
}


//------------------------------------------------------------
// function: test3DES
//------------------------------------------------------------

void
test3DES(CSSM_HANDLE cspHandle[], uint32 numOfCsps)
{
   uint32 status = 0;
   uint32 i;
   uint32 j;
   uint32 k;

   //------------------------------------------------------------
   // Prepare some random data to encrypt
   //------------------------------------------------------------

   uint32 DATA_TO_ENCRYPT_ARRAY_SIZE = 10;
   uint32 randomDataSize[10] = {7,56,98,127,512,1100,1987,2010,3001,5000};
   CSSM_DATA_PTR cssmDataToEncrypt = (CSSM_DATA_PTR)DefaultMalloc(DATA_TO_ENCRYPT_ARRAY_SIZE*sizeof(CSSM_DATA));

   for (i = 0; i < DATA_TO_ENCRYPT_ARRAY_SIZE; i++)
   {
      if ((status = JKL_GenerateRandomData(cspHandle[0],
                                           randomDataSize[i],
                                           cssmDataToEncrypt[i])) != 0)
         throw TestException(status);
   }

   //------------------------------------------------------------
   // Encrypt/Decrypt Sample Data with Each CSP
   //------------------------------------------------------------

   for (i = 0; i < numOfCsps; i++)
   {
      cout << "CSP: " << i << endl;
      for (j = 0; j < DATA_TO_ENCRYPT_ARRAY_SIZE; j++)
      {
         int numOfKeys = 3;
         CSSM_KEY cssmSecretKey;
         CSSM_DATA cssmInitializationVector;

         //------------------------------------------------------------
         // Generate Key and IV
         //------------------------------------------------------------

         if ((status = JKL_3DES_GenerateKey(cspHandle[i], cssmSecretKey, numOfKeys)) != 0)
            throw TestException(status);

         if ((status = JKL_DES_GenerateIV(cspHandle[i], cssmInitializationVector)) != 0)
            throw TestException(status);

         //------------------------------------------------------------
         // Encrypt Data
         //------------------------------------------------------------

         CSSM_DATA cssmEncryptedData;
         if ((status = JKL_3DES_EncryptData(cspHandle[i],
                                            numOfKeys,
                                            cssmInitializationVector,
                                            cssmSecretKey,
                                            cssmDataToEncrypt[j],
                                            cssmEncryptedData)) != 0)
            throw TestException(status);

         //------------------------------------------------------------
         // Decrypt Data
         //------------------------------------------------------------

         CSSM_DATA cssmDecryptedData;
         if ((status = JKL_3DES_DecryptData(cspHandle[i],
                                            numOfKeys,
                                            cssmInitializationVector,
                                            cssmSecretKey,
                                            cssmEncryptedData,//cssmDataToDecrypt,
                                            cssmDecryptedData)) != 0)
            throw TestException(status);

         //------------------------------------------------------------
         // Verify Decryption Returns Output and Encryption Input
         //------------------------------------------------------------

         if ( false == JKL_CompareData(cssmDataToEncrypt[j], cssmDecryptedData) )
            throw "Decryption Failed";

         //------------------------------------------------------------
         // This next test is just downright evil: we want to do the 
         // same encryption using the other installed CSPs; in theory
         // the encrypted output should be the same.  To do encryption, 
         // use the high-level encryption routines that takes
         // ASN objects, instead of CSSM inputs.
         //------------------------------------------------------------

         for (k = 0; k < numOfCsps; k++)
         {

            // don't test against self...

            if (k == i) 
               continue;

            // convert CSSM_KEYs to ASN objects

            asn_octetstring asnKey;
            asn_octetstring asnIV;
            asn_octetstring asnDataToEncrypt;
            asn_octetstring asnEncryptedData;

            if ((status = asnKey.set_value(cssmSecretKey.KeyData.Data, cssmSecretKey.KeyData.Length)) != 0)
               throw TestException(status);
            if ((status = asnIV.set_value(cssmInitializationVector.Data, cssmInitializationVector.Length)) != 0)
               throw TestException(status);
            if ((status = asnDataToEncrypt.set_value(cssmDataToEncrypt[j].Data, cssmDataToEncrypt[j].Length)) != 0)
               throw TestException(status);

            // use high-level API to do same encrpyion with alternative CSP

            // JKL_SetPreferredCSP( cspHandle[k], ALG_3DES );
            if ((status = JKL_3DES_EncryptData(asnKey,
                                               asnIV,
                                               numOfKeys,
                                               asnDataToEncrypt,
                                               asnEncryptedData)) != 0)
               throw TestException(status);

            // verify that encrypted output (for same key/IV) is identical, regardless of CSP

            CSSM_DATA cssmAltEncryptedData;
            if ((status = asnEncryptedData.get_value(cssmAltEncryptedData.Data, cssmAltEncryptedData.Length)) != 0)
               throw TestException(status);

            if ( false == JKL_CompareData(cssmEncryptedData, cssmAltEncryptedData) )
               throw "Encryption Failed";

            asn_octetstring asnDecryptedData;

            // JKL_SetPreferredCSP( cspHandle[k], ALG_3DES );
            if ((status = JKL_3DES_DecryptData(asnKey,
                                               asnIV,
                                               numOfKeys,
                                               asnEncryptedData, // dataToDecrypt,
                                               asnDecryptedData)) != 0)
               throw TestException(status);

            // verify that decrypted output (for same key/IV) is identical, regardless of CSP

            CSSM_DATA cssmAltDecryptedData;
            if ((status = asnDecryptedData.get_value(cssmAltDecryptedData.Data, cssmAltDecryptedData.Length)) != 0)
               throw TestException(status);

            if ( false == JKL_CompareData(cssmDecryptedData, cssmAltDecryptedData) )
               throw "Encryption Failed";
         }

         //------------------------------------------------------------
         // Clean up allocated resources
         //------------------------------------------------------------

         JKL_FreeData(cssmSecretKey);
         JKL_FreeData(cssmInitializationVector);
         JKL_FreeData(cssmEncryptedData);
         JKL_FreeData(cssmDecryptedData);
      }
   }

   //------------------------------------------------------------
   // Clean up allocated resources
   //------------------------------------------------------------

   JKL_FreeData(cssmDataToEncrypt, DATA_TO_ENCRYPT_ARRAY_SIZE);
}


//********************************************************************************
//
// memory management
//
//********************************************************************************


//------------------------------------------------------------
// function: DefaultMalloc
//------------------------------------------------------------

void*
DefaultMalloc(uint32 size,
              void* allocRef)
{
   return malloc(size);
}


//------------------------------------------------------------
// function: DefaultFree
//------------------------------------------------------------

void
DefaultFree(void* memPtr,
            void* allocRef)
{
   free(memPtr);
}


//------------------------------------------------------------
// function: DefaultRealloc
//------------------------------------------------------------

void*
DefaultRealloc(void* memPtr,
               uint32 size,
               void* allocRef)
{
   return realloc(memPtr, size);
}


//------------------------------------------------------------
// function: DefaultCalloc
//------------------------------------------------------------

void*
DefaultCalloc(uint32 num,
              uint32 size,
              void* allocRef)
{
   return calloc(num, size);
}


