/***************************************************************************
 * 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_asn.h"
#include "cssm2asn.h"


//------------------------------------------------------------
// Known CDSA CSP addins
//------------------------------------------------------------

CSSM_GUID IBM_CYLINK_CSP_GUID = { 0xba047041, 0x31f4, 0x11d2, { 0xb1, 0xd4, 0x0, 0x20, 0x35, 0x68, 0xb, 0x0 } };
CSSM_GUID IBM_BSAFE_CSP_GUID  = { 0xdda0c1e0, 0x7b73, 0x11d0, { 0x8e, 0x0c, 0x0, 0x04, 0xac, 0x60, 0x2b, 0x18 } };


//------------------------------------------------------------
// supported signature algorithms
//------------------------------------------------------------

unsigned long md2WithRSAEncryption_val[7]       = {1,2,840,113549,1,1,2};
unsigned long md5WithRSAEncryption_val[7]       = {1,2,840,113549,1,1,4};
unsigned long sha1WithRSAEncryption_val[7]      = {1,2,840,113549,1,1,5};
unsigned long dsaWithSHA1_val[6]                = {1,2,840,10040,4,3};


//------------------------------------------------------------
// supported public key algorithms
// NOTE: dsa_alt is produced by BSAFE when creating DSA public key
//------------------------------------------------------------

unsigned long rsaEncryption_val[7]              = {1,2,840,113549,1,1,1};
unsigned long dsa_val[6]                        = {1,2,840,10040,4,1};
unsigned long dsa_alt_val[6]                    = {1,3,14,3,2,12};


//------------------------------------------------------------
// private utility functions
//------------------------------------------------------------

// Note: These function convert between ASN structures and
//       the Key and Signature data structures produced by 
//       the IBM Cylink CSP.

int  cssm_to_asn(const CSSM_DATA& cssmPublicKey,
                 DssParms& dssParms, 
                 asn_integer& dsaPublicKey);
int  cssm_to_asn(const CSSM_DATA& cssmSignature, 
                 DssSigValue& dsaSignature);

int asn_to_cssm(DssParms& dssParms, 
                 asn_integer& dsaPublicKey,
                 CSSM_DATA& cssmPublicKey);
int  asn_to_cssm(DssSigValue& dsaSignature,
                 CSSM_DATA& cssmSignature);


// public interface: asn_to_cssm

//------------------------------------------------------------
// asn_to_cssm: ASN Algorithm Identifier to CSSM algorithm identifier
//------------------------------------------------------------

int
asn_to_cssm(AlgorithmIdentifier& algorithmIdentifier,
            uint32& cssmSignatureAlgorithmID)
{
   int status = 0;

   // NOTE: same procedure for BSAFE or Cylink CSP

   if ( algorithmIdentifier.algorithm.is_equal(dsaWithSHA1_val, 6) )
   {
      cssmSignatureAlgorithmID = CSSM_ALGID_SHA1WithDSA;
   } 
   else if ( algorithmIdentifier.algorithm.is_equal(md5WithRSAEncryption_val, 7) )
   {
      cssmSignatureAlgorithmID = CSSM_ALGID_MD5WithRSA;
   } 
   else if ( algorithmIdentifier.algorithm.is_equal(md2WithRSAEncryption_val, 7) )
   {
      cssmSignatureAlgorithmID = CSSM_ALGID_MD2WithRSA;
   }
   else if ( algorithmIdentifier.algorithm.is_equal(sha1WithRSAEncryption_val, 7) )
   {
      cssmSignatureAlgorithmID = CSSM_ALGID_SHA1WithRSA;
   }
   else
   {
      status = -1;
//    throw TPException(TP_VERIFY_UNKNOWN_SIGNATURE_ALGORITHM);
   }

   return status;
}


//------------------------------------------------------------
// asn_to_cssm: ASN SubjectPublicKeyInfo to CSSM_KEY
//------------------------------------------------------------

int 
asn_to_cssm(SubjectPublicKeyInfo& subjectPublicKeyInfo,
            CSSM_KEY& cssmPublicKey,
            const CSSM_GUID& cspGUID)
{
   int status = 0;

   if ( CSSM_TRUE == CSSM_CompareGuids(cspGUID, IBM_BSAFE_CSP_GUID) )
   {
      //------------------------------------------------------------
      // BSAFE CSP
      //------------------------------------------------------------

      buffer_t signerPublicKeyInfo;
   
      if ((status = subjectPublicKeyInfo.write(signerPublicKeyInfo)) != 0)
         return -1;
//       throw TPASNException(status);
   
      memset(&cssmPublicKey, 0, sizeof(CSSM_KEY));

      if ( subjectPublicKeyInfo.algorithm.algorithm.is_equal(rsaEncryption_val, 7) )
      {
         cssmPublicKey.KeyHeader.AlgorithmId  = CSSM_ALGID_RSA_PKCS;
      }
      else if ( subjectPublicKeyInfo.algorithm.algorithm.is_equal(dsa_val, 6) ||
                subjectPublicKeyInfo.algorithm.algorithm.is_equal(dsa_alt_val, 6) )
      {
         cssmPublicKey.KeyHeader.AlgorithmId  = CSSM_ALGID_DSA;
      }
      else
      {
         return -1;
//       throw TPException(TP_VERIFY_UNKNOWN_PUBLIC_KEY_ALGORITHM);
      }

      cssmPublicKey.KeyHeader.HeaderVersion   = CSSM_KEYHEADER_VERSION;
      cssmPublicKey.KeyHeader.BlobType        = CSSM_KEYBLOB_RAW_BERDER;
      cssmPublicKey.KeyHeader.Format          = CSSM_KEYBLOB_RAW_FORMAT_BSAFE;
      cssmPublicKey.KeyHeader.KeyClass        = CSSM_KEYCLASS_PUBLIC_KEY;
      cssmPublicKey.KeyHeader.KeyUsage        = CSSM_KEYUSE_ANY;
      cssmPublicKey.KeyHeader.WrapAlgorithmId = CSSM_ALGID_NONE;
      cssmPublicKey.KeyData.Data              = signerPublicKeyInfo.data;
      cssmPublicKey.KeyData.Length            = signerPublicKeyInfo.data_len;

      signerPublicKeyInfo.detach();
   }
   else
   {
      memset(&cssmPublicKey, 0, sizeof(CSSM_KEY));

      cssmPublicKey.KeyHeader.HeaderVersion   = CSSM_KEYHEADER_VERSION;
      cssmPublicKey.KeyHeader.BlobType        = CSSM_KEYBLOB_RAW;
      cssmPublicKey.KeyHeader.Format          = CSSM_KEYBLOB_RAW_FORMAT_NONE;
      cssmPublicKey.KeyHeader.AlgorithmId     = CSSM_ALGID_DSA;
      cssmPublicKey.KeyHeader.KeyClass        = CSSM_KEYCLASS_PUBLIC_KEY;
      cssmPublicKey.KeyHeader.KeyUsage        = CSSM_KEYUSE_ANY;
      cssmPublicKey.KeyHeader.WrapAlgorithmId = CSSM_ALGID_NONE;

      buffer_t dssParmsBuffer;
      DssParms dssParms;

      if ((status = subjectPublicKeyInfo.algorithm.parameters.write(dssParmsBuffer)) != 0)
         return (status);
         //throw TPASNException(status);

      if ((status = dssParms.read(dssParmsBuffer)) != 0)
         return (status);
         //throw TPASNException(status);

      unsigned char* p;
      unsigned bc;
      if ((status = subjectPublicKeyInfo.subjectPublicKey.get_value(p, bc)) != 0)
         return (status);
         //throw TPASNException(status);
   
      r_buffer_t dsaPublicKeyBuffer;
      asn_integer dsaPublicKey;
      dsaPublicKeyBuffer.data     = p;
      dsaPublicKeyBuffer.data_len = bc/8;
      if ((status = dsaPublicKey.read(dsaPublicKeyBuffer)) != 0)
         return (status);
         //throw TPASNException(status);
      
      // CSP-specific stuff: (Length,Value) interpretation
            
      asn_to_cssm(dssParms, dsaPublicKey, cssmPublicKey.KeyData);
   }
   return status;
}


//------------------------------------------------------------
// asn_to_cssm: ASN BIT STRING to CSSM_DATA signature
//------------------------------------------------------------

int
asn_to_cssm(asn_bitstring& signature,
            CSSM_DATA& cssmSignature,
            const CSSM_GUID& cspGUID)
{
   int status = 0;

   //------------------------------------------------------------
   // extract bit string (same for BSAFE and Cylink)
   //------------------------------------------------------------

   size_t numOfBits;
   size_t numOfBytes;
   unsigned char* p;

   if ((status = signature.get_value(p, numOfBits)) != 0)
      return (status);
      //throw TPASNException(status);
   
   // compute number of bytes
   
   numOfBytes = numOfBits/CHAR_BIT;
   
   // add one if not even multiple of CHAR_BIT
   
   if (numOfBits % CHAR_BIT)
      numOfBytes++;

   if ( CSSM_TRUE == CSSM_CompareGuids(cspGUID, IBM_BSAFE_CSP_GUID) )
   {
      //------------------------------------------------------------
      // BSAFE: contents of BIT STRING is signature
      //------------------------------------------------------------
      cssmSignature.Data   = new unsigned char[numOfBytes];
      cssmSignature.Length = numOfBytes;
      memcpy(cssmSignature.Data, p, numOfBytes);
   }
   else
   {
      //------------------------------------------------------------
      // Cylink: contents of BIT STRING is SEQUENCE {r, s}
      //------------------------------------------------------------

      r_buffer_t copyBuffer;

      copyBuffer.data     = p;
      copyBuffer.data_len = numOfBytes;

      DssSigValue dsaSignature;
      if ((status = dsaSignature.read(copyBuffer)) != 0)
         return status;
         //throw TPASNException(status);
      asn_to_cssm(dsaSignature, cssmSignature);
   }
   return status;
}


// public interface: cssm_to_asn

//------------------------------------------------------------
// cssm_to_asn: CSSM_DATA signature to ASN BIT STRING
//------------------------------------------------------------

int
cssm_to_asn(const CSSM_DATA& cssmSignature,
            asn_bitstring& signature,
            const CSSM_GUID& cspGUID)
{
   int status = 0;

   if ( CSSM_TRUE == CSSM_CompareGuids(cspGUID, IBM_BSAFE_CSP_GUID) )
   {
      //------------------------------------------------------------
      // BSAFE CSP returns X.509 DER BIT STRING
      //------------------------------------------------------------

      r_buffer_t signatureBuffer;
      signatureBuffer.data     = cssmSignature.Data;
      signatureBuffer.data_len = cssmSignature.Length;
      if ((status = signature.set_value(signatureBuffer.data,signatureBuffer.data_len*8)) != 0)
         return status;
         //throw TPASNException(status);
   
   }
   else
// CYLINK CSP doesn't fill in GUID in CSSM_KEY yet...
// if ( CSSM_TRUE == CSSM_CompareGuids(signerPrivateKey.KeyHeader.CspId, IBM_CYLINK_CSP_GUID) )
   {
      //------------------------------------------------------------
      // Cylink CSP returns r (20 bytes), s (20 bytes)
      //------------------------------------------------------------

      DssSigValue dsaSignature;
      cssm_to_asn(cssmSignature, dsaSignature);
      buffer_t signatureBuffer;
      if ((status = dsaSignature.write(signatureBuffer)) != 0)
         return status;
         //throw TPASNException(status);
      if ((status = signature.set_value(signatureBuffer.data,signatureBuffer.data_len*8)) != 0)
         return status;
         //throw TPASNException(status);
   }
// else
// {
//    throw "unsupported CSP addin module";
// }
   return status;
}


//------------------------------------------------------------
// function: cssm_to_asn: CSSM_KEY to ASN SubjectPublicKeyInfo
//------------------------------------------------------------

int
cssm_to_asn(const CSSM_KEY& cssmPublicKey,
            SubjectPublicKeyInfo& subjectPublicKeyInfo)
{
   int status = 0;

   if ( CSSM_TRUE == CSSM_CompareGuids(cssmPublicKey.KeyHeader.CspId, IBM_BSAFE_CSP_GUID) )
   {
      //--------------------
      // BSAFE CSP returns X.509 DER SubjectPublicKeyInfo
      //--------------------

      r_buffer_t keyBuffer;

      keyBuffer.data     = cssmPublicKey.KeyData.Data;
      keyBuffer.data_len = cssmPublicKey.KeyData.Length;
      if ((status = subjectPublicKeyInfo.read(keyBuffer)) != 0)
         return status;
         //throw TPASNException(status);
   }
   else
// CYLINK CSP doesn't fill in GUID in CSSM_KEY yet...
// if ( CSSM_TRUE == CSSM_CompareGuids(cssmPublicKey.KeyHeader.CspId, IBM_CYLINK_CSP_GUID) )
   {

      //--------------------
      // Cylink CSP returns {length/value,length/value,length/value,length/value}
      //--------------------

      DssParms dssParms;
      asn_integer dsaPublicKey;
      cssm_to_asn(cssmPublicKey.KeyData, dssParms, dsaPublicKey);

      //--------------------
      // set OID
      //--------------------

      if ((status = subjectPublicKeyInfo.algorithm.algorithm.set_value(dsa_val, 6)) != 0)
         return status;
         //throw TPASNException(status);

      //--------------------
      // set parameters
      //--------------------

      buffer_t paramBuffer;

      if ((status = dssParms.write(paramBuffer)) != 0)
         return status;
         //throw TPASNException(status);
      if ((status = subjectPublicKeyInfo.algorithm.parameters.read(paramBuffer)) != 0)
         return status;
         //throw TPASNException(status);

      //--------------------
      // set BIT STRING
      //--------------------

      buffer_t publicKeyBuffer;

      if ((status = dsaPublicKey.write(publicKeyBuffer)) != 0)
         return status;
         //throw TPASNException(status);
      if ((status = subjectPublicKeyInfo.subjectPublicKey.set_value(publicKeyBuffer.data, 8*publicKeyBuffer.data_len)) != 0)
         return status;
         //throw TPASNException(status);
   }
   //else 
   //{
   //   throw "Unsupported CSP";
   //}
   return status;
}


//------------------------------------------------------------
// function: cssm_to_asn: CSSM algorithmId to ASN AlgorithmIdentifier
//------------------------------------------------------------

int
cssm_to_asn(uint32 cssmSignatureAlgorithmID,
            AlgorithmIdentifier& algorithmIdentifier)
{
   int status = 0;

   //--------------------
   // define ASN NULL
   //--------------------
   
   unsigned char nullBytes[] = {0x05,0x00};
   r_buffer_t nullBuffer;
   nullBuffer.data     = nullBytes;
   nullBuffer.data_len = 2;

   //--------------------
   // define algorithm identifier
   //--------------------

   if ( CSSM_ALGID_SHA1WithDSA == cssmSignatureAlgorithmID )
   {
      if ((status = algorithmIdentifier.algorithm.set_value(dsaWithSHA1_val, 6)) != 0)
         return status;
         //throw TPASNException(status);
   }
   else if ( CSSM_ALGID_MD5WithRSA == cssmSignatureAlgorithmID )
   {
      if ((status = algorithmIdentifier.algorithm.set_value(md5WithRSAEncryption_val, 7)) != 0)
         return status;
         //throw TPASNException(status);
      if ((status = algorithmIdentifier.parameters.read(nullBuffer)) != 0)
         return status;
         //throw TPASNException(status);
   } 
   else if ( CSSM_ALGID_MD2WithRSA == cssmSignatureAlgorithmID )
   {
      if ((status = algorithmIdentifier.algorithm.set_value(md2WithRSAEncryption_val, 7)) != 0)
         return status;
         //throw TPASNException(status);
      if ((status = algorithmIdentifier.parameters.read(nullBuffer)) != 0)
         return status;
         //throw TPASNException(status);
   }
   else if ( CSSM_ALGID_SHA1WithRSA == cssmSignatureAlgorithmID )
   {
      if ((status = algorithmIdentifier.algorithm.set_value(sha1WithRSAEncryption_val, 7)) != 0)
         return status;
         //throw TPASNException(status);
      if ((status = algorithmIdentifier.parameters.read(nullBuffer)) != 0)
         return status;
         //throw TPASNException(status);
   }
   else
   {
      return -1;
      throw "Unsupported signature algorithm type";
   }
   return status;
}


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

int
asn_to_cssm(DssSigValue& dsaSignature,
            CSSM_DATA& cssmSignature)
{
   //------------------------------------------------------------
   // Cylink CSP format of signature:
   // r, s, where each occupies 20 bytes
   //------------------------------------------------------------

   int status = 0;

   unsigned char* r;
   unsigned char* s;
   unsigned int  rl;
   unsigned int  sl;

   if ((status = dsaSignature.r.get_value(r, rl)) != 0)
         return status;
         //throw TPASNException(status);

   if ((status = dsaSignature.ss.get_value(s, sl)) != 0)
         return status;
         //throw TPASNException(status);

   cssmSignature.Data   = new unsigned char[rl+sl];
   cssmSignature.Length = rl+sl;
   memcpy(cssmSignature.Data,    r, rl);
   memcpy(cssmSignature.Data+rl, s, sl);

   return status;
}


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

int
cssm_to_asn(const CSSM_DATA& cssmSignature, 
            DssSigValue& dsaSignature)
{
   //------------------------------------------------------------
   // Cylink CSP format of signature:
   // r, s, where each occupies 20 bytes
   //------------------------------------------------------------

   int status = 0;

   unsigned char* p = cssmSignature.Data;

   if ((status = dsaSignature.r.set_value(p, 20)) != 0)
         return status;
      //throw TPASNException(status);

   p += 20;

   if ((status = dsaSignature.ss.set_value(p, 20)) != 0)
         return status;
      //throw TPASNException(status);

   return status;
}


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

int
asn_to_cssm(DssParms& dssParms, 
            asn_integer& dsaPublicKey,
            CSSM_DATA& cssmPublicKey)
{
   //------------------------------------------------------------
   // Cylink CSP format of public key:
   // L(p), p, L(q), q, L(g), g, L(y), y
   // where L(x) = uint32 length of buffer x
   //------------------------------------------------------------

   int status = 0;

   unsigned char* p;
   unsigned char* q;
   unsigned char* g;
   unsigned char* y;
   unsigned int  pl;
   unsigned int  ql;
   unsigned int  gl;
   unsigned int  yl;

   if ((status = dssParms.p.get_value(p, pl)) != 0)
         return status;
      //throw TPASNException(status);
   if ((status = dssParms.q.get_value(q, ql)) != 0)
         return status;
      //throw TPASNException(status);
   if ((status = dssParms.g.get_value(g, gl)) != 0)
         return status;
      //throw TPASNException(status);
   if ((status = dsaPublicKey.get_value(y, yl)) != 0)
         return status;
      //throw TPASNException(status);

   size_t ll = sizeof(uint32);
   size_t neededBytes   = 4*ll+pl+ql+gl+yl;
   cssmPublicKey.Data   = new unsigned char[neededBytes];
   cssmPublicKey.Length = neededBytes;

   unsigned char* buffer = cssmPublicKey.Data;

   memcpy(buffer, &pl, ll);
   buffer += ll;
   memcpy(buffer, p, pl);
   buffer += pl;
   memcpy(buffer, &ql, ll);
   buffer += ll;
   memcpy(buffer, q, ql);
   buffer += ql;
   memcpy(buffer, &gl, ll);
   buffer += ll;
   memcpy(buffer, g, gl);
   buffer += gl;
   memcpy(buffer, &yl, ll);
   buffer += ll;
   memcpy(buffer, y, yl);

   return status;
}


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

int
cssm_to_asn(const CSSM_DATA& cssmPublicKey, 
            DssParms& dssParms, 
            asn_integer& dsaPublicKey)
{
   //------------------------------------------------------------
   // Cylink CSP format of public key:
   // L(p), p, L(q), q, L(g), g, L(y), y
   // where L(x) = uint32 length of buffer x
   //------------------------------------------------------------

   int 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:
         if ((status = dssParms.p.set_value(p, dsaParamLength)) != 0)
            return status;
            //throw TPASNException(status);
         break;
      case 1:
         if ((status = dssParms.q.set_value(p, dsaParamLength)) != 0)
            return status;
            //throw TPASNException(status);
         break;
      case 2:
         if ((status = dssParms.g.set_value(p, dsaParamLength)) != 0)
            return status;
            //throw TPASNException(status);
         break;
      case 3:
         if ((status = dsaPublicKey.set_value(p, dsaParamLength)) != 0)
            return status;
            //throw TPASNException(status);
         break;
      default:
         return -1;
        //throw TPASNException(status);
        break;
      }
      p += dsaParamLength;
   }
   return status;
}


