/* ***************************************************************** *
 * 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 <limits.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "cssm.h"
#include "dsa.h"
#include "jklutil.h"

//*****************************************************************************
//
// General Usage Notes:
//
// Cylink Data Formats:
// --------------------
//
//    Public Key  : (p,q,g,y) as (length/value) pairs, where sizeof(length) = sizeof(long)
//    Private Key : (p,q,g,y,x) as (length/value) pairs 
//    Signature   : (r,s) as (value/value), no length bytes, r, s each 20 bytes long.
//
// Cylink does not pad leading 0x00 on high-bit integers.  Cylink arrays are
// in little-endian format and must be reversed prior to DER conversion.
//
// Three steps to convert from a Cylink integer a DER integer:
//
//    (1) Reverse Bytes; then
//    (2) Pad with 0x00 byte if high-bit of first byte is set;
//        This ensures value is interpreted as a positive DER integer.
//    (3) Passed resulting length/value to asn_integer set_value method
//        which will add ASN.1 class/tag.
//
// BSAFE Data Formats:
// --------------------
//
//    Public Key   : DER-encoded SubjectPublicKeyInfo
//    Private Key  : DER-encoded PrivateKeyInfo
//    Signature    : DER-encoded BIT STRING
//
// This module provides APIs that convert to/from CSSM formats to DER.
//
//*****************************************************************************


//*****************************************************************************
//
// ASN to CSSM conversion routines.
//
//*****************************************************************************


//------------------------------------------------------------
// function: asn_to_cssm
//------------------------------------------------------------

uint32 
asn_to_cssm(asn_integer& P,
            asn_integer& Q,
            asn_integer& G,
            asn_integer& Y,
            asn_integer& X,
            CSSM_DATA& cssmPrivateKey)
{
   uint32 status = 0;

   unsigned char* p;
   unsigned char* q;
   unsigned char* g;
   unsigned char* y;
   unsigned char* x;
   unsigned plen;
   unsigned qlen;
   unsigned glen;
   unsigned ylen;
   unsigned xlen;

   // Extract DER integer representation

   if ((status = P.get_value(p, plen)) != 0)
         return status;
   if ((status = Q.get_value(q, qlen)) != 0)
         return status;
   if ((status = G.get_value(g, glen)) != 0)
         return status;
   if ((status = Y.get_value(y, ylen)) != 0)
         return status;
   if ((status = X.get_value(x, xlen)) != 0)
         return status;

   // Remove any DER integer leading 0x00 bytes

   if (plen && 0x00 == p[0]) { plen--; p++; }
   if (qlen && 0x00 == q[0]) { qlen--; q++; }
   if (glen && 0x00 == g[0]) { glen--; g++; }
   if (ylen && 0x00 == y[0]) { ylen--; y++; }
   if (xlen && 0x00 == x[0]) { xlen--; x++; }

   // Cylink reverse magic 

   reverse_array(p, plen);
   reverse_array(q, qlen);
   reverse_array(g, glen);
   reverse_array(y, ylen);
   reverse_array(x, xlen);

   // Put the pieces together

   size_t uint32len      = sizeof(uint32);
   size_t neededBytes    = 5*uint32len+plen+qlen+glen+ylen+xlen;
   cssmPrivateKey.Data   = (uint8*)DefaultMalloc(neededBytes);
   cssmPrivateKey.Length = neededBytes;

   unsigned char* cssmBuffer = cssmPrivateKey.Data;

   memcpy(cssmBuffer, &plen, uint32len);   cssmBuffer += uint32len;
   memcpy(cssmBuffer, p,     plen);        cssmBuffer += plen;
   memcpy(cssmBuffer, &qlen, uint32len);   cssmBuffer += uint32len;
   memcpy(cssmBuffer, q,     qlen);        cssmBuffer += qlen;
   memcpy(cssmBuffer, &glen, uint32len);   cssmBuffer += uint32len;
   memcpy(cssmBuffer, g,     glen);        cssmBuffer += glen;
   memcpy(cssmBuffer, &ylen, uint32len);   cssmBuffer += uint32len;
   memcpy(cssmBuffer, y,     ylen);        cssmBuffer += ylen;
   memcpy(cssmBuffer, &xlen, uint32len);   cssmBuffer += uint32len;
   memcpy(cssmBuffer, x,     xlen);

   return status;
}


//------------------------------------------------------------
// function: asn_to_cssm
//------------------------------------------------------------

uint32
asn_to_cssm(DssParms& dssParms, 
            asn_integer& dsaPublicKey,
            CSSM_DATA& cssmPublicKey)
{
   uint32 status = 0;

   unsigned char* p;
   unsigned char* q;
   unsigned char* g;
   unsigned char* y;
   unsigned plen;
   unsigned qlen;
   unsigned glen;
   unsigned ylen;

   // Extract DER integer representation

   if ((status = dssParms.p.get_value(p, plen)) != 0)
         return status;
   if ((status = dssParms.q.get_value(q, qlen)) != 0)
         return status;
   if ((status = dssParms.g.get_value(g, glen)) != 0)
         return status;
   if ((status = dsaPublicKey.get_value(y, ylen)) != 0)
         return status;

   // Remove any DER integer leading 0x00 bytes

   if (plen && 0x00 == p[0]) { plen--; p++; }
   if (qlen && 0x00 == q[0]) { qlen--; q++; }
   if (glen && 0x00 == g[0]) { glen--; g++; }
   if (ylen && 0x00 == y[0]) { ylen--; y++; }

   // Cylink reverse magic 

   reverse_array(p, plen);
   reverse_array(q, qlen);
   reverse_array(g, glen);
   reverse_array(y, ylen);

   // Put the pieces together

   size_t uint32len     = sizeof(uint32);
   size_t neededBytes   = 4*uint32len+plen+qlen+glen+ylen;
   cssmPublicKey.Data   = (uint8*)DefaultMalloc(neededBytes);
   cssmPublicKey.Length = neededBytes;

   unsigned char* cssmBuffer = cssmPublicKey.Data;

   memcpy(cssmBuffer, &plen, uint32len);   cssmBuffer += uint32len;
   memcpy(cssmBuffer, p,     plen);        cssmBuffer += plen;
   memcpy(cssmBuffer, &qlen, uint32len);   cssmBuffer += uint32len;
   memcpy(cssmBuffer, q,     qlen);        cssmBuffer += qlen;
   memcpy(cssmBuffer, &glen, uint32len);   cssmBuffer += uint32len;
   memcpy(cssmBuffer, g,     glen);        cssmBuffer += glen;
   memcpy(cssmBuffer, &ylen, uint32len);   cssmBuffer += uint32len;
   memcpy(cssmBuffer, y,     ylen);

   return status;
}


//------------------------------------------------------------
// function: asn_to_cssm
//------------------------------------------------------------

uint32
asn_to_cssm(DssSigValue& dsaSignature,
            CSSM_DATA& cssmSignature)
{
   uint32 status = 0;

   unsigned char* r;
   unsigned char* s;
   unsigned rlen;
   unsigned slen;

   // Extract DER integer representation

   if ((status = dsaSignature.r.get_value(r, rlen)) != 0)
         return status;
   if ((status = dsaSignature.ss.get_value(s, slen)) != 0)
         return status;

   // Remove any DER integer leading 0x00 bytes

   if (rlen && 0x00 == r[0]) { rlen--; r++; }
   if (slen && 0x00 == s[0]) { slen--; s++; }
   
   // Cylink reverse magic 

   reverse_array(r, rlen);
   reverse_array(s, slen);

   // Put the pieces together

   cssmSignature.Data   = (uint8*)DefaultMalloc(rlen+slen);
   cssmSignature.Length = rlen+slen;
   memcpy(cssmSignature.Data,      r, rlen);
   memcpy(cssmSignature.Data+rlen, s, slen);

   return status;
}


//*****************************************************************************
//
// CSSM to ASN conversion routines.
//
//*****************************************************************************


//------------------------------------------------------------
// function: cssm_to_asn
//------------------------------------------------------------

uint32
cssm_to_asn(const CSSM_DATA& cssmSignature, 
            DssSigValue& dsaSignature)
{
   uint32 status = 0;

   unsigned char* p = cssmSignature.Data;

   // populate r

   if ((status = normalize_der_integer(dsaSignature.r, p, 20)) != 0)
      return status;

   p += 20;

   // populate s

   if ((status = normalize_der_integer(dsaSignature.ss, p, 20)) != 0)
      return status;

   return status;
}


//------------------------------------------------------------
// function: cssm_to_asn
//------------------------------------------------------------

uint32
cssm_to_asn(const CSSM_DATA& cssmPublicKey, 
            DssParms& dssParms, 
            asn_integer& dsaPublicKey)
{
   uint32 status = 0;

   unsigned char* p = cssmPublicKey.Data;

   for (int i = 0; i < 4; i++)
   {
      uint32 dsaParamLength;
      memcpy(&dsaParamLength, p, sizeof(uint32));
      p += sizeof(uint32);
      switch (i)
      {
      case 0:
         // populate p
         if ((status = normalize_der_integer(dssParms.p, p, dsaParamLength)) != 0)
            return status;
         break;
      case 1:
         // populate q
         if ((status = normalize_der_integer(dssParms.q, p, dsaParamLength)) != 0)
            return status;
         break;
      case 2:
         // populate g
         if ((status = normalize_der_integer(dssParms.g, p, dsaParamLength)) != 0)
            return status;
         break;
      case 3:
         // populate y
         if ((status = normalize_der_integer(dsaPublicKey, p, dsaParamLength)) != 0)
            return status;
         break;
      default:
         return CSSM_CSP_UNKNOWN_ALGORITHM;
         break;
      }
      p += dsaParamLength;
   }
   return status;
}


//------------------------------------------------------------
// function: cssm_to_asn
//------------------------------------------------------------

uint32
cssm_to_asn(const CSSM_DATA& cssmPrivateKey, 
            DssPrivateParms& dssPrivateParms, 
            asn_integer& dsaPublicKey,
            asn_integer& dsaPrivateKey)
{
   uint32 status = 0;

   unsigned char* p = cssmPrivateKey.Data;

   for (int i = 0; i < 5; i++)
   {
      uint32 dsaParamLength;
      memcpy(&dsaParamLength, p, sizeof(uint32));
      p += sizeof(uint32);
      switch (i)
      {
      case 0:
         // populate p
         if ((status = normalize_der_integer(dssPrivateParms.p, p, dsaParamLength)) != 0)
            return status;
         break;
      case 1:
         // populate q
         if ((status = normalize_der_integer(dssPrivateParms.q, p, dsaParamLength)) != 0)
            return status;
         break;
      case 2:
         // populate g
         if ((status = normalize_der_integer(dssPrivateParms.g, p, dsaParamLength)) != 0)
            return status;
         break;
      case 3:
         // populate y
         if ((status = normalize_der_integer(dsaPublicKey, p, dsaParamLength)) != 0)
            return status;
         break;
      case 4:
         // populate x
         if ((status = normalize_der_integer(dsaPrivateKey, p, dsaParamLength)) != 0)
            return status;
         break;
      default:
         return CSSM_CSP_UNKNOWN_ALGORITHM;
        break;
      }
      p += dsaParamLength;
   }

   return status;
}


//*****************************************************************************
//
// Misc Byte Swapping Routines.
//
//*****************************************************************************


//------------------------------------------------------------
// function: normalize_der_integer
// ASSUMPTION: Input is a Cylink integer
//------------------------------------------------------------

uint32
normalize_der_integer(asn_integer& value, unsigned char* p, int length)
{
   uint32 status = 0;

   reverse_array(p, length);
   if (p[0] & 0x80)
   {
      unsigned char* der = add_leading_zero(p, length);
      if ((status = value.set_value(der, length+1)) != 0)
      {
         delete [] der;
         return status;
      }
      delete [] der;
   }
   else 
   {
      if ((status = value.set_value(p, length)) != 0)
         return status;
   }

   return status;
}


//------------------------------------------------------------
// function: add_leading_zero
//------------------------------------------------------------

static unsigned char kZero[1] = {0x00};

unsigned char*
add_leading_zero(unsigned char* p, unsigned length)
{
   unsigned char* q = new unsigned char[length+1];
   memcpy(q, kZero, 1);
   memcpy(q+1, p, length);
   return q;
}


//------------------------------------------------------------
// function: reverse_array
//------------------------------------------------------------

void
reverse_array(unsigned char* p, int length)
{
   unsigned char tmp;
   for (int i = 0; i < length/2; i++)
   {
      tmp = p[i];
      p[i] = p[length-i-1];
      p[length-i-1] = tmp;
   }
}


//------------------------------------------------------------
// function: copy_cssm_data
//------------------------------------------------------------

void
copy_cssm_data(CSSM_DATA& copyTo, 
               const CSSM_DATA& copyFrom)
{
   copyTo.Data   = (unsigned char*)DefaultMalloc(copyFrom.Length);
   copyTo.Length = copyFrom.Length;
   memcpy(copyTo.Data, copyFrom.Data, copyFrom.Length);
}


//------------------------------------------------------------
// function: JKL_3DES_DecryptData
//------------------------------------------------------------

uint32
compute3DESAlgorithm(int numOfKeys)
{
   // which DES algorithm to use?

   uint32 algorithmID = CSSM_ALGID_NONE;

   if (3 == numOfKeys)
      algorithmID = CSSM_ALGID_3DES_3KEY;
   else if (2 == numOfKeys)
      algorithmID = CSSM_ALGID_3DES_2KEY;
   else if (1 == numOfKeys)
      algorithmID = CSSM_ALGID_3DES_1KEY;

   return algorithmID;
}

