/* ***************************************************************** *
 * 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>
#ifdef WIN32
#include <crtdbg.h>
#endif
#include "jkl.h"
#include "test_jkl.h"


//------------------------------------------------------------
// test data
//------------------------------------------------------------

CSSM_ALGORITHMS cssmKeyPairType[] = { CSSM_ALGID_DSA, CSSM_ALGID_RSA };
int numOfSigAlgs[] = { 1, 3 };
CSSM_ALGORITHMS sigAlgs[2][3] = 
{ 
   { CSSM_ALGID_SHA1WithDSA, CSSM_ALGID_NONE, CSSM_ALGID_NONE },
   { CSSM_ALGID_MD2WithRSA, CSSM_ALGID_MD5WithRSA, CSSM_ALGID_SHA1WithRSA } 
};

char* sigAlgsString[2][3] = 
{ 
   { "CSSM_ALGID_SHA1WithDSA", 0, 0 },
   { "CSSM_ALGID_MD2WithRSA", "CSSM_ALGID_MD5WithRSA", "CSSM_ALGID_SHA1WithRSA" } 
};


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

void
TestHighLevel_Chooser();

void
TestHighLevel_3DES();

void
TestHighLevel_GenerateRandomData();

void
TestHighLevel_GenerateKeyPair();

void
TestHighLevel_SignData();


//------------------------------------------------------------
// function: main
//------------------------------------------------------------

int main()
{
   int rc;

#ifdef WIN32
  /* 
   * Set the debug-heap flag to keep freed blocks in the
   * heap's linked list - This will allow us to catch any
   * inadvertent use of freed memory
   */
   int tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
   tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF;
   tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF;
   _CrtSetDbgFlag(tmpDbgFlag);
#endif
   
   try
   {
      TestHighLevelAPIs();
      TestLowLevelAPIs();
   }
   catch (TestException& error)
   {
   }
   catch (char*)
   {
   }
   return rc;
}

bool operator==(const CSSM_GUID& lhs, const CSSM_GUID& rhs)
{
   return (lhs.Data1 == rhs.Data1 &&
           lhs.Data2 == rhs.Data2 &&
           lhs.Data3 == rhs.Data3 &&
           lhs.Data4[0] == rhs.Data4[0] &&
           lhs.Data4[1] == rhs.Data4[1] &&
           lhs.Data4[2] == rhs.Data4[2] &&
           lhs.Data4[3] == rhs.Data4[3] &&
           lhs.Data4[4] == rhs.Data4[4] &&
           lhs.Data4[5] == rhs.Data4[5] &&
           lhs.Data4[6] == rhs.Data4[6] &&
           lhs.Data4[7] == rhs.Data4[7] );
}

bool operator!=(const CSSM_GUID& lhs, const CSSM_GUID& rhs)
{
   return !(lhs == rhs);
}

//------------------------------------------------------------
// function: TestHighLevelAPIs
//------------------------------------------------------------

void
TestHighLevelAPIs()
{
   uint32 status = 0;

   CSSM_MEMORY_FUNCS memoryFuncs = { DefaultMalloc, DefaultFree, DefaultRealloc, DefaultCalloc, 0 };

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

   TestHighLevel_Chooser();
   TestHighLevel_3DES();
   TestHighLevel_SignData();
   TestHighLevel_GenerateRandomData();
   TestHighLevel_GenerateKeyPair();
}


//------------------------------------------------------------
// function: TestHighLevel_Chooser
//------------------------------------------------------------

void 
TestHighLevel_Chooser()
{
   uint32 status = 0;

   CSSM_GUID cssmGUIDQuery;

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

   // sanity check equality operator

   assert(cssmGUID[0] != cssmGUID[1]);

   // set Cylink, then BSAFE, then Cylink again as preferred CSPs

   assert(JKL_SetPreferredCSP(cssmGUID[0]) == 0);
   assert(JKL_SetPreferredCSP(cssmGUID[1]) == 0);
   assert(JKL_SetPreferredCSP(cssmGUID[0]) == 0);

   // we know Cylink does not support RSA; verify this fact

   assert(true  == JKL_AlgorithmIsSupported(cssmGUID[0], CSSM_ALGID_DSA));
   assert(false == JKL_AlgorithmIsSupported(cssmGUID[0], CSSM_ALGID_RSA));
   assert(false == JKL_AlgorithmIsSupported(cssmGUID[0], CSSM_ALGID_MD2WithRSA));
   assert(false == JKL_AlgorithmIsSupported(cssmGUID[0], CSSM_ALGID_MD5WithRSA));
   assert(false == JKL_AlgorithmIsSupported(cssmGUID[0], CSSM_ALGID_SHA1WithRSA));

   // BSAFE however, does

   assert(true == JKL_AlgorithmIsSupported(cssmGUID[1], CSSM_ALGID_DSA));
   assert(true == JKL_AlgorithmIsSupported(cssmGUID[1], CSSM_ALGID_RSA));
   assert(true == JKL_AlgorithmIsSupported(cssmGUID[1], CSSM_ALGID_MD2WithRSA));
   assert(true == JKL_AlgorithmIsSupported(cssmGUID[1], CSSM_ALGID_MD5WithRSA));
   assert(true == JKL_AlgorithmIsSupported(cssmGUID[1], CSSM_ALGID_SHA1WithRSA));

   // set BSAFE, then Cylink as preferred DSA provider

   for (int i = 0; i < 2; i++)
   {
      assert(JKL_SetPreferredCSP(cssmGUID[i], CSSM_ALGID_DSA) == 0);
      assert(JKL_SetPreferredCSP(cssmGUID[i], CSSM_ALGID_SHA1WithDSA) == 0);

      assert(JKL_GetPreferredCSP(cssmGUIDQuery, CSSM_ALGID_DSA) == 0);
      assert(cssmGUIDQuery == cssmGUID[i]);
      assert(JKL_GetPreferredCSP(cssmGUIDQuery, CSSM_ALGID_SHA1WithDSA) == 0);
      assert(cssmGUIDQuery == cssmGUID[i]);
   }
}


//------------------------------------------------------------
// function: TestHighLevel_GenerateRandomData
//------------------------------------------------------------

void
TestHighLevel_GenerateRandomData()
{
   uint32 status = 0;

   unsigned int randomDataSize[] = {10,100,500,1000};

   for (int i = 1; i < sizeof(randomDataSize)/sizeof(unsigned int); i++)
   {
      asn_octetstring randomData;

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


//------------------------------------------------------------
// function: TestHighLevel_GenerateKeyPair
//------------------------------------------------------------

void
TestHighLevel_GenerateKeyPair()
{
   uint32 status = 0;

   for (int i = 0; i < sizeof(cssmKeyPairType)/sizeof(CSSM_ALGORITHMS); i++)
   {
      uint32 modulusBits = 512;
      SubjectPublicKeyInfo subjectPublicKeyInfo;
      PrivateKeyInfo privateKeyInfo;

      if ((status = JKL_GenerateKeyPair(cssmKeyPairType[i],
                                        modulusBits,
                                        subjectPublicKeyInfo,
                                        privateKeyInfo)) != 0)
         throw TestException(status);

      long keySizeInBits1;

      if ((status = JKL_ComputeKeySizeInBits(subjectPublicKeyInfo, keySizeInBits1)) != 0)
         throw TestException(status);

      cout << "Key size (derived from SubjectPublicKeyInfo): " << keySizeInBits1 << endl;

      long keySizeInBits2;

      if ((status = JKL_ComputeKeySizeInBits(privateKeyInfo, keySizeInBits2)) != 0)
         throw TestException(status);

      cout << "Key size (derived from PrivateKeyInfo):       " << keySizeInBits2 << endl;

      if (keySizeInBits1 != keySizeInBits2)
         throw "inconsistent key size calculation";
   }
}


//------------------------------------------------------------
// function: TestHighLevel_3DES
//------------------------------------------------------------

void
TestHighLevel_3DES()
{
   uint32 status = 0;

   int numOfKeys = 3;

   unsigned int randomDataSize[] = {10,100,500,1000};

   CSSM_GUID cssmGUID[2];
   cssmGUID[0] = * (CSSM_GUID_PTR)JKL_Get_CylinkCsp_GUID();
   cssmGUID[1] = * (CSSM_GUID_PTR)JKL_Get_SwCsp_GUID();
   assert(JKL_SetPreferredCSP(cssmGUID[0]) == 0);

   for (int i = 1; i < sizeof(randomDataSize)/sizeof(unsigned int); i++)
   {
      // generate DES key

      asn_octetstring desKey;
      asn_octetstring IV;

      if ((status = JKL_3DES_GenerateKey(desKey,
                                         IV,
                                         numOfKeys)) != 0)
         throw TestException(status);

      // generate sample data to encrypt

      asn_octetstring dataToEncrypt;

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

      // encrypt data

      asn_octetstring encryptedData;

      if ((status = JKL_3DES_EncryptData(desKey,
                                         IV,
                                         numOfKeys,
                                         dataToEncrypt,
                                         encryptedData)) != 0)
         throw TestException(status);

      // decrypt data

      asn_octetstring decryptedData;

      if ((status = JKL_3DES_DecryptData(desKey,
                                         IV,
                                         numOfKeys,
                                         encryptedData,// dataToDecrypt,
                                         decryptedData)) != 0)
         throw TestException(status);

      // verify decryption

      if (! (dataToEncrypt == decryptedData) )
         throw TestException(status);
   }
}


//------------------------------------------------------------
// function: TestHighLevel_SignData
//------------------------------------------------------------

void
TestHighLevel_SignData()
{
   uint32 status = 0;

   for (int i = 0; i < sizeof(cssmKeyPairType)/sizeof(CSSM_ALGORITHMS); i++)
   {
      uint32 modulusBits = 512;
      SubjectPublicKeyInfo subjectPublicKeyInfo;
      PrivateKeyInfo privateKeyInfo;

      // generate key pair

      if ((status = JKL_GenerateKeyPair(cssmKeyPairType[i],
                                        modulusBits,
                                        subjectPublicKeyInfo,
                                        privateKeyInfo)) != 0)
         throw TestException(status);

      // generate some random data to sign

      asn_octetstring randomData;

      if ((status = JKL_GenerateRandomData(250, randomData)) != 0)
         throw TestException(status);
      
      for (int j = 0; j < numOfSigAlgs[i]; j++)
      {
         CSSM_ALGORITHMS cssmSignatureAlgorithmID = sigAlgs[i][j];
         cout << "Signing with " << sigAlgsString[i][j] << endl;

         // sign the data

         buffer_t dataToSign;

         if ((status = randomData.get_value(dataToSign.data, dataToSign.data_len)) != 0)
            throw TestException(status);

         asn_bitstring signature;

         if ((status = JKL_SignData(privateKeyInfo,
                                    subjectPublicKeyInfo,
                                    cssmSignatureAlgorithmID,
                                    dataToSign,
                                    signature)) != 0)
         throw TestException(status);
   
         // verify the data
      }      
   }
}
