/* ***************************************************************** *
 * 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 "x509.h"
#include "jkl.h"
#include "jdl.h"
#include "functiontrace.h"
#include "oidchecker.h"
#include "pkixvalidator.h"


//------------------------------------------------------------
// X.509/PKIX CRL extensions
//------------------------------------------------------------

static unsigned long CRLNumber_val[4]                  = {2,5,29,20};
static unsigned long DeltaCRLIndicator_val[4]          = {2,5,29,27};
static unsigned long IssuingDistributionPoint_val[4]   = {2,5,29,28};
static unsigned long AuthorityKeyIdentifier_val[4]     = {2,5,29,35};
static unsigned long IssuerAlternativeName_val[4]      = {2,5,29,18};


//------------------------------------------------------------
// X.509/PKIX CRL entry extensions
//------------------------------------------------------------

static unsigned long ReasonCode_val[4]                 = {2,5,29,21};
static unsigned long InstructionCode_val[4]            = {2,5,29,23};
static unsigned long InvalidityDate_val[4]             = {2,5,29,24};
static unsigned long CertificateIssuer_val[4]          = {2,5,29,29};


//------------------------------------------------------------
// global data (INI data initialized in jonahtp.cpp)
//------------------------------------------------------------

extern bool _iniAllowExpiredCRLs;


//------------------------------------------------------------
// method: validateCertificateNotOnCRL 
//------------------------------------------------------------

void
pkix_validator::validateCertificateNotOnCRL(pkix_certificate& subject, 
                                            pkix_certificate& issuer)
{
   TPTRACE(jonahtp_trace_info, "void pkix_validator::validateCertificateNotOnCRL(pkix_certificate&,pkix_certificate&)");

   uint32 status;

   //------------------------------------------------------------
   // Locate the best CRL available from this issuer
   //------------------------------------------------------------

   bool found;
   pkix_crl crl;

   getBestCRL(issuer, crl, found);

   if (false == found)
      throw TPException(0);

   //------------------------------------------------------------
   // Validate the CRL: signature, extensions, dates
   //------------------------------------------------------------

   validateCRL(crl, issuer);

   //------------------------------------------------------------
   // Finally, is subject on CRL?  Check any CRL entry extensions
   //------------------------------------------------------------

   bool isCritical;

   if ( crl.tbsCertList.revokedCertificates.is_present() )
   {
      for (int i = 0; i < crl.tbsCertList.revokedCertificates.get_child_count(); i++)
      {
         RevokedCertificate* child = crl.tbsCertList.revokedCertificates.get_child(i);
         if ( child->userCertificate == subject.tbsCertificate.serialNumber )
         {
#if 0
            child->revocationDate;
            for (int j = 0; j < child->crlEntryExtensions.get_child_count(); j++)
            {
               x509_Extension* child = child->crlEntryExtensions.get_child(j);
               if (child->extnID.is_equal(ReasonCode_val,4))
               {
               }
               else if (child->extnID.is_equal(InstructionCode_val,4))
               {
               }
               else if (child->extnID.is_equal(InvalidityDate_val,4))
               {
               }
               else if (child->extnID.is_equal(CertificateIssuer_val,4))
               {
               }
               else if (isCritical)
               {
                  throw TPException(TP_EXTS_UNKNOWN_CRITICAL_EXT);
               }
            }
#endif
            throw TPException(0);
         }
      }
   }
}


//------------------------------------------------------------
// method: getBestCRL 
//------------------------------------------------------------

/*
CRL version, if present, shall be v2 (we don't support v1 CRLs)
validate inner/outer signature algorithmIdentifier
validate signature
require CRL number be present
when extensions repsent, versoin must be 2 (value is 1)
issuer name must be present 
this update: issue date of this CRL
next update: next the next CRL will be issued. (an optional field)
revoked certificates: a list, name by serial number; and revocationDate

Extensions: only if version 2 CRL; must be a sequence of one or more CRL extensions
validation fail is encounter critical unknown extension
(Conforming: require AKID and CRL number):
   
CRL number: conforming must use; is non-critical

Delta CRL Indicator: is critical (we don't support)
IssuingDistributionPoint: we don't support:w

CRL validation MUST fail if it encounters a critical CRL entry extension which it does not
know who to crocess; all CRL entry extensions in PKIX are non-critical.
recommend use of reason codes and invalidiy date when information is avialble;
invalidity date: 
certificate issuer: we do not support (since its related to issuign distribution point ext)
*/

void
pkix_validator::getBestCRL(pkix_certificate& issuer, 
                           pkix_crl& crl, 
                           bool& found)
{
   uint32 status = 0;

   //------------------------------------------------------------
   // Did caller provide dataSource handles to query CRLs?
   //------------------------------------------------------------

   if (!_DBList || _DBList->NumHandles == 0)
      throw TPException(TP_NO_CRL_DATASOURCES);

   //------------------------------------------------------------
   // Retrieve all CRLs from this issuer
   // Each CRL must be considered: 
   // First Pass: look for a single CRL from a single data source
   //------------------------------------------------------------

   for (uint32 i = 0; i < _DBList->NumHandles; i++)
   {
      if ((status = JDL_QueryCRL(_DBList->DLDBHandle[i], 
                                 issuer.tbsCertificate.subject, 
                                 crl)) != 0)
         throw TPException(0);

      // retreive a list of CRLs from source 
      // evalidate each one: 
      // issuer name match
      // signature match
      // date match
      // any failure, skip and keep processing, until we find
      // the "best" CRL or set of CRLs
      // so, its possible we may reject some of CRLs but will still continue
      // processing; when we get gpp list, sort by cRLNumber.
      // Winnow list of found CRLs to best one
      // 
   }

   //------------------------------------------------------------
   // XXX: TO: code that multiple CRLs could be retrieved;
   // XXX: need code sort them by CRL number and take most recent
   // IF # CRLS = 0, error: cannot find CRLs
   //------------------------------------------------------------
}


//------------------------------------------------------------
// method: validateCRL 
//------------------------------------------------------------

void
pkix_validator::validateCRL(pkix_crl& crl, 
                            pkix_certificate& issuer)
{
   TPTRACE(jonahtp_trace_info, "void pkix_validator::validateCRLSignature(pkix_crl&,pkix_certificate&)");

   uint32 status = 0;

   validateInnerOuterAlgorithmsAgree(crl.tbsCertList.signature, 
                                     crl.signatureAlgorithm);

   validateCRLSignature(crl, issuer);

   //------------------------------------------------------------
   // check CRL version
   //------------------------------------------------------------

   long crlVersion = 0;
   if ( crl.tbsCertList.version.is_present() )
   {
      if ((status = crl.tbsCertList.version.get_value(crlVersion)) != 0)
         throw TPASNException(status);
      if (crlVersion != 1)
         throw TPException(0);
//       throw TPException(TP_CRL_INVALID_VERSION);
   }

   //------------------------------------------------------------
   // check names match
   //------------------------------------------------------------

   if ( !(crl.tbsCertList.issuer == issuer.tbsCertificate.subject) )
      throw TPException(0);
//    throw TPException(TP_CRL_ISSUER_NAME_MISMATCH);

   //------------------------------------------------------------
   // check CRL date(s)
   //------------------------------------------------------------

   // check dates: thisUpdate/nextUpdate (next update is an optional field)

   // is validation time begore|between|after this data range?

   // is CRL expired, will we use or trust? (check INI)
   // compare _validationTime to crl.tbcCertList.thisUpdate

   // require CRL number to be present

   validateCRLExtensions(crl, issuer);
}


//------------------------------------------------------------
// method: validateCRLSignature 
//------------------------------------------------------------

void
pkix_validator::validateCRLSignature(pkix_crl& crl, 
                                     pkix_certificate& issuer)
{
   TPTRACE(jonahtp_trace_info, "void pkix_validator::validateCRLSignature(pkix_crl&,pkix_certificate&)");

   uint32 status = 0;

   bool isVerified;

   if ((status = JKL_ValidateCRLSignature(crl, issuer, _cspHandle, isVerified)) != 0)
      throw TPASNException(status);

   if (isVerified == false)
      throw TPASNException(status);
}


//------------------------------------------------------------
// method: validateCRLExtensions 
//------------------------------------------------------------

void
pkix_validator::validateCRLExtensions(pkix_crl& crl, 
                                      pkix_certificate& issuer)
{
   TPTRACE(jonahtp_trace_info, "void pkix_validator::validateCRLExtensions(pkix_crl&,pkix_certificate&)");

   int status = 0;

   // no validation to perform if CRL has no extensions

   if ( !crl.isExtensionsPresent() )
   {
      return;
   }

   // iterate over extensions

   OIDChecker duplicate_oids;

   int children = crl.tbsCertList.crlExtensions.value.get_child_count();
   for (int i = 0; i < children ; i++)
   {
      bool isCritical;

      x509_Extension* child = crl.tbsCertList.crlExtensions.value[i];
      if ((status = child->critical.get_value(isCritical)) != 0)
         throw TPASNException(status);

      //------------------------------------------------------------
      // check only one instance of any extension apppears
      //------------------------------------------------------------

      if ( !duplicate_oids.insert(child->extnID) )
         throw TPException(TP_EXTS_MULTIPLE_EXTS);

      //------------------------------------------------------------
      // validate the extensions we know about:
      // unsupported: IssuingDistributionPoint, DeltaCRLIndicator
      //------------------------------------------------------------

      if (child->extnID.is_equal(AuthorityKeyIdentifier_val,4))
      {
         XAuthorityKeyIdentifier& authorityKeyIdentifier = crl.authorityKeyIdentifier();
         validateAuthorityKeyIdentifier(authorityKeyIdentifier, isCritical, issuer);
      }
      else if (child->extnID.is_equal(IssuerAlternativeName_val,4))
      {
         XIssuerAltName& issuerAltName = crl.issuerAltName();
         validateIssuerAltName(issuerAltName, isCritical, issuer);
      }
      else if (child->extnID.is_equal(CRLNumber_val,4))
      {
         ; // OK, we validate these elsewhere
      }
      else if (isCritical)
      {
         throw TPException(TP_EXTS_UNKNOWN_CRITICAL_EXT);
      }
   }
}
