/* ***************************************************************** *
 * 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.                                                        *
 * ***************************************************************** */


// CP_NewCertCreate
#include <certpol.h>
#include <Jonah.h>
#include <ApiNotify.h>
#include <stdio.h>


//Copy all the values we understand from the template to the cert 
int CP_TmpToCert(const CertTemplate &tmplt, x509_certificate &cert) {
  int status = 0; 
  buffer_t buff; 
  unsigned char message[512];
	
  if (tmplt.version.value.is_present()) {
    long ver;
   
    if ((status = tmplt.version.value.get_value(ver)) !=0) {
      sprintf((char *)message,
              "Error %lu extracting version from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    if ((status = cert.tbsCertificate.version.value.set_value(ver)) != 0) {
      sprintf((char *)message,
              "Error %lu setting version in certificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    }
  }

  if (tmplt.serialNumber.value.is_present()) {
    unsigned char * ptr;
    uint32 len;

    if ((status = tmplt.serialNumber.value.get_value(ptr, len)) !=0) {
      sprintf((char *)message,
              "Error %lu getting serial number from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    if ((status = cert.tbsCertificate.serialNumber.set_value(ptr, len)) != 0) {
      sprintf((char *)message,
              "Error %lu setting serial number in certificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    };
  }

  if (tmplt.signingAlg.value.is_present()) {

    buff.clear();
    if ((status = tmplt.signingAlg.value.write(buff)) !=0) {
      sprintf((char *)message,
              "Error %lu getting signing algorithm from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    if ((status = cert.tbsCertificate.signature.read(buff)) != 0) {
      sprintf((char *)message,
              "Error %lu setting signing algorithm in cetificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    };
  }

  if (tmplt.issuer.value.is_present()) {

    buff.clear();
    if ((status = tmplt.issuer.value.write(buff)) !=0) {
      sprintf((char *)message,
              "Error %lu getting issuer from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    if ((status = cert.tbsCertificate.issuer.read(buff)) != 0) {
      sprintf((char *)message,
              "Error %lu setting issuer in certificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    };
    if (buff.data_len != 0) {
      sprintf((char *)message,
              "%d bytes of unconsumed data transferring issuer name to certificate",
              buff.data_len);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return ASN_UNCONSUMED_DATA;
    };
  }

  if (tmplt.validity.value.notBefore.value.is_present()) {

    buff.clear();
    if ((status = tmplt.validity.value.notBefore.value.write(buff)) !=0) {
      sprintf((char *)message,
              "Error %lu getting startDate from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    if ((status = cert.tbsCertificate.validity.notBefore.read(buff)) != 0) {
      sprintf((char *)message,
              "Error %lu setting startDate in certificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    };
    if (buff.data_len != 0) {
      sprintf((char *)message,
              "%d bytes of unconsumed data transferring startdate to certificate",
              buff.data_len);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return ASN_UNCONSUMED_DATA;
    };
  }

  if (tmplt.validity.value.notAfter.value.is_present()) {

    buff.clear();
    if ((status = tmplt.validity.value.notAfter.value.write(buff)) !=0) {
      sprintf((char *)message,
              "Error %lu getting endDate from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    if ((status = cert.tbsCertificate.validity.notAfter.read(buff)) != 0) {
      sprintf((char *)message,
              "Error %lu setting endDate in certificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    };
    if (buff.data_len != 0) {
      sprintf((char *)message,
              "%d bytes of unconsumed data transferring enddate to certificate",
              buff.data_len);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return ASN_UNCONSUMED_DATA;
    };
  }

  if (tmplt.subject.value.is_present()) {

    buff.clear();
    if ((status = tmplt.subject.value.write(buff)) !=0) {
      sprintf((char *)message,
              "Error %lu getting subject from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    if ((status = cert.tbsCertificate.subject.read(buff)) != 0) {
      sprintf((char *)message,
              "Error %lu setting subject in certificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    };
    if (buff.data_len != 0) {
      sprintf((char *)message,
              "%d bytes of unconsumed data transferring subject name to certificate",
              buff.data_len);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return ASN_UNCONSUMED_DATA;
    };
  }

  if (tmplt.publicKey.value.is_present()) {

    buff.clear();
    if ((status = tmplt.publicKey.value.write(buff)) !=0) {
      sprintf((char *)message,
              "Error %lu getting public-key from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    if ((status = cert.tbsCertificate.subjectPublicKeyInfo.read(buff)) != 0) {
      sprintf((char *)message,
              "Error %lu setting public-key in certificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    };
    if (buff.data_len != 0) {
      sprintf((char *)message,
              "%d bytes of unconsumed data transferring publicKey to certificate",
              buff.data_len);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return ASN_UNCONSUMED_DATA;
    };
  }

  if (tmplt.issuerUID.value.is_present()) {

    buff.clear();
    if ((status = tmplt.issuerUID.value.write(buff)) !=0) {
      sprintf((char *)message,
              "Error %lu getting issuerUID from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    if ((status = cert.tbsCertificate.issuerUniqueID.read(buff)) != 0) {
      sprintf((char *)message,
              "Error %lu setting issuerUID in certificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    };
    if (buff.data_len != 0) {
      sprintf((char *)message,
              "%d bytes of unconsumed data transferring issuer UID to certificate",
              buff.data_len);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return ASN_UNCONSUMED_DATA;
    };
  }

  if (tmplt.subjectUID.value.is_present()) {

    buff.clear();
    if ((status = tmplt.subjectUID.value.write(buff)) !=0) {
      sprintf((char *)message,
              "Error %lu getting subjectUID from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    if ((status = cert.tbsCertificate.subjectUniqueID.read(buff)) != 0) {
      sprintf((char *)message,
              "Error %lu setting subjectUID in certificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    };
    if (buff.data_len != 0) {
      sprintf((char *)message,
              "%d bytes of unconsumed data transferring subject UID to certificate",
              buff.data_len);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return ASN_UNCONSUMED_DATA;
    };
  }

  if (tmplt.extensions.value.is_present()) {

    buff.clear();

    if ((status = tmplt.extensions.value.write(buff)) !=0) {
      sprintf((char *)message,
              "Error %lu getting extensions from template",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status;
    };
    sprintf((char *)message,
            "Copying %d bytes of extensions",
            buff.data_len);
    ApiDisplay(DISPLAY_LOGDEBUG, message);

    if ((status = cert.tbsCertificate.extensions.value.read(buff)) != 0) {
      sprintf((char *)message,
              "Error %lu setting extensions in certificate",
              status);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return status; 
    };
    if (buff.data_len != 0) {
      sprintf((char *)message,
              "%d bytes of unconsumed data transferring extensions to certificate",
              buff.data_len);
      ApiDisplay(DISPLAY_LOGERROR, message);
      return ASN_UNCONSUMED_DATA;
    };
  }

  return 0; 
}

// If the serial number isn't stored in the .bini, start at 1 
// Use the current serial number, then increment it in store for the next use
int CP_SetSerial(asn_integer &sn) {
	int status, i;

	// Not yet implemented:
	// Currently using a bini section of ini, since bini support doesn't exist. 
	if (!IniReadInteger(BINI_SECTION, BINI_SERIAL, i)) 
		// Initializing the serial number count
		i = 0;
	i++; 
	if (!IniWriteInteger(BINI_SECTION, BINI_SERIAL, i))
		return POL_BINI_PROB; 
	// This should be unnecessary, but isn't right now (bug) 
	if (!IniWriteFile())
		return POL_BINI_PROB;

	if ((status = sn.set_value(i)) != 0)
		return status; 

	return 0; 
}

int CP_FindSubjIdExt(x509_Extensions &extensions) {
	int i;
	x509_Extension *ext;
	bool found = false; 

	for (i = 0; i < extensions.get_child_count(); i++) {
		ext = extensions[i];
		if (CP_IsSubjKeyOid(ext->extnID)) {
			found = true;
			break;
		}
	}

	if (found)
		return i;
	else
		return -1; 

}

int CP_SetAuthId(x509_Extensions &extensions, const r_buffer_t &authkeyid){
	int status = 0; 
	x509_Extension *idext; 
	unsigned char c[] = "abc";

	idext = extensions.add_child();

	// This extension is marked non-critical; the default 
	if ((status = idext->extnID.set_value(authKeyOid, 4)) != 0)
		return status; 
	if ((status = idext->extnValue.set_value(authkeyid.data, authkeyid.data_len)) != 0)
		return status; 

	return status; 

}

// Set the subject key ID. We use the standard PKIX alg, of a four bit 
// type field set to 0100 followed by the least significant 60 bits of the 
// SHA-1 hash of the bit string subjectPublicKey

int CP_SetSubjId(const asn_bitstring &pkey, x509_Extensions &extensions) {
	int status; 
	unsigned char c[] = "abc";
	unsigned int i; 
	r_buffer_t dataToDigest;
	buffer_t digest;
	uint8 hash[8] = {0,0,0,0,0,0,0,0}; // 64 bits for the value for the ID 
	x509_Extension *idext; 
	AlgorithmIdentifier alg; 
	CSSM_ALGORITHMS calg = CSSM_ALGID_SHA1;

	if ((status = pkey.get_value(dataToDigest.data, dataToDigest.data_len)) != 0)
		return status; 
	
	if ((status = CR_DigestData(calg, dataToDigest, digest)) != 0)
		return status; 

	// Use the low 60 bits of the SHA hash 
	for (i = 0; i < 7; i++) {
		if (i < digest.data_len) 
			hash[i] = digest.data[i];
	}
	hash[7] = 4; // bit pattern 0100

	idext = extensions.add_child();

	// This extension is marked non-critical; the default 
	if ((status = idext->extnID.set_value(subjKeyOid, 4)) != 0)
		return status; 

	if ((status = idext->extnValue.set_value(hash, 8)) != 0)
		return status; 

	return 0; 
}


int CP_NewCertPolicy(int index, x509_Extensions &extensions) {
	int status = 0, i, polnum, j, notnum; 
	buffer_t buff; 
	XCertificatePolicies pols; 
	char key[STRSIZE], name[STRSIZE], text[EXPTEXTSIZE]; 
	PolicyQualifierInfo *qual; 
	asn_integer *num;

	if (index < 0) 
		return 0;

	if ((status = extensions[index]->extnValue.get_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = pols.read(buff)) != 0)
		return status; 
		
	// For each policy specified ...
	for (i = 0; i < pols.get_child_count(); i++) {
		
		// Find the policy number in the ini file for this oid
		polnum = CP_FindPolicy(pols[i]->policyIdentifier);
		if (polnum < 1) // This shouldn't happen; Verify should catch all these 
			return POL_POL_UNSUP; 

		// Find out if CPS and/or user notification is supported for this policy

		// Get the CPS for the current policy
		sprintf(key, CP_CPS "%d", polnum); 
		if (IniReadString(CP_SECTION, key, name, STRSIZE)) {
			j = CP_FindCpsQual(pols[i]);
			if (j < 0) {
				qual = pols[i]->policyQualifiers.add_child();
				if ((status = qual->policyQualifierId.set_value(cpsOid, 4)) != 0)
					return status;
				if ((status = qual->qualifier.cPSuri.set_value_UTF8(name)) != 0)
					return status;
			}
		}
			
		// Get the User Notice for the current policy
		sprintf(key, CP_USERNOTICE "%d", polnum); 
		if (IniReadString(CP_SECTION, key, text, EXPTEXTSIZE)) {
			j = CP_FindUnoticeQual(pols[i]);
			if (j < 0) 
				qual = pols[i]->policyQualifiers.add_child();
			else
				qual = pols[i]->policyQualifiers[j]; 

			if (!qual->qualifier.userNotice.explicitText.is_present())
				if ((status = qual->qualifier.userNotice.explicitText.set_value_IA5(text)) != 0)
					return status; 
		}
				
		// Get the User Notice Organization for the current policy
		sprintf(key, CP_UNOTPOL "%d" CP_UNOTORG, polnum); 
		if (IniReadString(CP_SECTION, key, name, STRSIZE)) {
			if (!qual->qualifier.userNotice.noticeRef.organization.is_present()) 
				if ((status = 
					qual->qualifier.userNotice.noticeRef.organization.set_value_IA5(name)) != 0)
					return status; 
		}

		// Check the notice numbers - they'll either be exactly correct or not there
		if (!qual->qualifier.userNotice.noticeRef.noticeNumbers.is_present()){
			for (i = 1; ; i++){
				sprintf(key, CP_UNOTPOL "%d" CP_UNOTNOTICE "%d", polnum, i);
				if (IniReadInteger(CP_SECTION, key, notnum)) {
					// Need to add that noticenumbers child first
					num = qual->qualifier.userNotice.noticeRef.noticeNumbers.add_child();
					if ((status = num->set_value(notnum)) != 0)
						return status; 
				}
				else // no more notice numbers in the .ini
					break; 
			}
		}
	}		
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = extensions[index]->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status; 

	return 0; 
}


// If the caller does not specify a public key, we create one with no value
// to send to the function that does the real work. 

int CP_NewCertCreate(const CertTemplate &tmplt, 
                     x509_certificate &cert) {
	asn_bitstring pubkey;
	return CP_NewCertCreate(tmplt, cert, pubkey);
}

// This routine makes CDSA calls and assumes CSSM has been initialized

int CP_NewCertCreate(const CertTemplate &tmplt, 
                     x509_certificate &cert, 
                     const asn_bitstring &pubkey) {
  int status = 0, duration, i, j, b, c; 
  ExtArray indices;
  time_t t;
  struct tm *tp; 
  buffer_t inbuff;
  buffer_t outbuff, buff;
  unsigned int y, m, d, h, mn, s, a;
  r_buffer_t authkeyid;
  x509_certificate mycert;
#ifdef NOCRYPTO
  unsigned char gorp[5] = {'g', 'o', 'r', 'p', 0};
#endif

// Check that I am a CA 
  if (!IniAmICA())
    return POL_NOT_CA;

// Call CP_NewCertVerify to verify all the settings that the RA should have checked. 
// Do this before changing the cert request state in ways not allowed to the RA. 
  if ((status = CP_NewCertVerify(tmplt, indices)) != 0) {
    ApiDisplay(DISPLAY_LOGERROR, 
               (utf8String)"The certificate request failed validity checks");
    return status;
  };

  ApiDisplay(DISPLAY_LOGDEBUG, 
             (utf8String)"Creating a certificate");

// Move the values from the template into the cert
  if ((status = CP_TmpToCert(tmplt, cert)) != 0)
    return status;	

// Set the serial number
  if ((status = CP_SetSerial(cert.tbsCertificate.serialNumber)) != 0)
    return status; 

// Get the values our of our cert that we'll need
	
#ifndef NOCRYPTO
  if ((status = CR_GetMyCert(mycert)) != 0)
    return status; 

  if ((i = CP_FindSubjIdExt(mycert.tbsCertificate.extensions.value)) >= 0) {
    mycert.tbsCertificate.extensions.value[i]->extnValue.get_value(
                                                authkeyid.data, authkeyid.data_len);
  }
  else
    return POL_NO_SUBJ;
#else
  authkeyid.data = gorp;
  authkeyid.data_len = sizeof(gorp);
#endif


// Set the authority key based on our subject key id
  if ((status = CP_SetAuthId(cert.tbsCertificate.extensions.value, authkeyid)) != 0)
    return status; 

// If the public key is locally generated, move it to the certificate
  if (pubkey.is_present()) {

    if ((status = pubkey.get_value(buff.data, buff.data_len)) != 0)
      return status; 
    if ((status = cert.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.set_value(
                                                         buff.data, buff.data_len)) != 0)
      return status;
    if ((status = buff.clear()) != 0)
      return status; 
  }

// Set the subject ID in the new cert
  if ((status = CP_SetSubjId(cert.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey, 
                             cert.tbsCertificate.extensions.value)) != 0)
    return status; 

// Initialize the beginning of the validity time, if it has not been set
  if (!cert.tbsCertificate.validity.notBefore.is_present()) {

// Get the current time
    t = time(NULL);
    tp = gmtime(&t);

// Date through 2049 is UTCTime
    if (tp->tm_year <= 149) {

// Got tp from gmtime() - it's already a Zulu time
      if ((status = cert.tbsCertificate.validity.notBefore.utcTime.set_value(
                                   tp->tm_year + 1900, 
                                   tp->tm_mon + 1, 
                                   tp->tm_mday, 
                                   tp->tm_hour, 
                                   tp->tm_min, 
                                   tp->tm_sec, 
                                   0, 0)) != 0)
        return status; 
      if ((status = cert.tbsCertificate.validity.notBefore.select(0)) != 0)
        return status; 
    }
    else {
      if ((status = cert.tbsCertificate.validity.notBefore.generalizedTime.set_value(
                                   tp->tm_year + 1900, 
                                   tp->tm_mon + 1, 
                                   tp->tm_mday, 
                                   tp->tm_hour, 
                                   tp->tm_min, 
                                   tp->tm_sec, 
                                   0, 
                                   0, 0)) != 0)
        return status; 

      if ((status = cert.tbsCertificate.validity.notBefore.select(1)) != 0)
        return status; 
    }

  }
		 
// Default the ending of the validity time, if it has not been set
  if (!cert.tbsCertificate.validity.notAfter.is_present()) {

// Get the default lifetime
    IniReadInteger(CP_SECTION, CP_LIFETIMEDEF, duration, CP_LIFETIMEDEFDEF);

// Get the start time
    if (cert.tbsCertificate.validity.notBefore.utcTime.is_present()) {
      if ((status = cert.tbsCertificate.validity.notBefore.utcTime.get_value(
                                                    y, m, d, h, mn, s, i, j)) != 0)
        return status; 
    }
    else {
      if ((status = cert.tbsCertificate.validity.notBefore.generalizedTime.get_value(
                                                    y, m, d, h, mn, s, a, b, c)) != 0)
        return status; 
    }

    tp->tm_year = y - 1900;
    tp->tm_mon = m;
    tp->tm_mday = d; 
    tp->tm_hour = h + duration;
    tp->tm_min = mn;
    tp->tm_sec = s; 

// Canonicalize the modified time 
    t = mktime(tp); 

    tp = gmtime(&t);

// Date through 2049 is UTCTime
    if (tp->tm_year <= 149) {

      if ((status = cert.tbsCertificate.validity.notAfter.utcTime.set_value(
                                            tp->tm_year + 1900, 
                                            tp->tm_mon + 1,
                                            tp->tm_mday,
                                            tp->tm_hour, 
                                            tp->tm_min, 
                                            tp->tm_sec, 
                                            0, 0)) != 0)
        return status; 
      if ((status = cert.tbsCertificate.validity.notAfter.select(0)) != 0)
        return status; 
    }
    else {
      if ((status = cert.tbsCertificate.validity.notAfter.generalizedTime.set_value(
                                            tp->tm_year + 1900, 
                                            tp->tm_mon + 1, 
                                            tp->tm_mday, 
                                            tp->tm_hour, 
                                            tp->tm_min, 
                                            tp->tm_sec, 
                                            0, 
                                            0, 0)) != 0)
        return status; 

      if ((status = cert.tbsCertificate.validity.notAfter.select(1)) != 0)
        return status; 
    }
  }
		 
// If needed, fill in the CPS and/or user notice
  if ((status = CP_NewCertPolicy(indices[certPolExt], 
                                 cert.tbsCertificate.extensions.value)) != 0)
    return status;

// Based on the algorithm in cert.tbsCertificate.signature, sign the 
// cert.tbsCertificate's DER encoding 

  if ((status = cert.tbsCertificate.write(inbuff)) != 0)
    return status; 

#ifndef NOCRYPTO
  if ((status = CR_SignData(cert.tbsCertificate.signature, 
                            inbuff, 
                            outbuff)) != 0)
    return status; 
#else
// Built with NOCRYPTO set, therefore kludge a fake signature to keep 
// the ASN classes happy...

  outbuff.clear();
  outbuff.append("John Hancock");

#endif


// Put the signature in cert.signature
  // bitstrings take their length in bits, not bytes 
  if ((status = cert.signature.set_value(outbuff.data, (outbuff.data_len*8))) != 0)
    return status; 

// Put the algorithm in cert.signatureAlgorithm 
  if ((status = cert.tbsCertificate.signature.algorithm.get_value(buff)) != 0)
    return status; 

  if ((status = cert.signatureAlgorithm.algorithm.set_value(buff.data, buff.data_len)) != 0)
    return status; 

  return 0; 	
}