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

#include <certpol.h>
#include <oid.h>

// Not Yet Implemented: 
// Right now these routines assume a single section for Cert Policy
// When an RA with many CAs (and many Cert Policies) is supported, 
// most of these routines will have to take the name of the section to lookup policy from.
// Or maybe there should be a cert policy object with the issuer as a member variable. 
		// Not Yet Implemented: 
		// Need to check lifetime too 
		// Need to align proposed UI with required data structures 
	// Not implemented yet: 
	// It returns a list of fields that do not pass the verification checks. 
	// If there are any unrecognized or unsupported extensions marked critical 
	// verification will fail with an indication of what fields are unsupported. 
	// If any non-critical, unsupported extensions are requested, the call is successful 
	// (since those fields will be ignored on creation), and the unsupported fields are 
	// indicated in the list of fields that do not pass verification. 
// Should we really require the IDs? If so, do we need to provide
// a utility program to the RA to put them in?
// to be implemented - who specifies alg when CA generates key?


// OIDs for the extensions documented in PKIX

bool CP_IsAuthKeyOid(const asn_oid &oid) {
	asn_oid foo; 
	return oid_equal(oid, authKeyOid, 4);
}
bool CP_IsSubjKeyOid(const asn_oid &oid){
	return oid_equal(oid, subjKeyOid, 4);
}
bool CP_IsKeyUsageOid(const asn_oid &oid){
	return oid_equal(oid, keyUsageOid, 4);
}
bool CP_IsPrivKeyPeriodOid(const asn_oid &oid){
	unsigned long p[] = {2, 5, 29, 16};
	return oid_equal(oid, p, 4);
}
bool CP_IsCertPolOid(const asn_oid &oid){
	return oid_equal(oid, certPolOid, 4);
}
bool CP_IsPolMapOid(const asn_oid &oid){
	unsigned long p[] = {2, 5, 29, 33};
	return oid_equal(oid, p, 4);
}
bool CP_IsSubjAltOid(const asn_oid &oid){
	unsigned long p[] = {2, 5, 29, 17};
	return oid_equal(oid, p, 4);
}
bool CP_IsIssuerAltOid(const asn_oid &oid){
	unsigned long p[] = {2, 5, 29, 18};
	return oid_equal(oid, p, 4);
}
bool CP_IsSubjDirOid(const asn_oid &oid){
	unsigned long p[] = {2, 5, 29, 9};
	return oid_equal(oid, p, 4);
}
bool CP_IsBasicConstOid(const asn_oid &oid){
	return oid_equal(oid, basicConstOid, 4);
}
bool CP_IsNameConstOid(const asn_oid &oid){
	unsigned long p[] = {2, 5, 29, 30};
	return oid_equal(oid, p, 4);
}
bool CP_IsPoldConstOid(const asn_oid &oid){
	unsigned long p[] = {2, 5, 29, 36};
	return oid_equal(oid, p, 4);
}
bool CP_IsExtKeyUsageOid(const asn_oid &oid){
	unsigned long p[] = {2, 5, 29, 37};
	return oid_equal(oid, p, 4);
}
bool CP_IsCrlDistPointOid(const asn_oid &oid){
	unsigned long p[] = {2, 5, 29, 31};
	return oid_equal(oid, p, 4);
}
bool CP_IsAuthInfoAccessOid(const asn_oid &oid){
	unsigned long p[] = {1, 3, 6, 1, 5, 7, 1};
	return oid_equal(oid, p, 7);
}

// OIDs for algorithms documented in PKIX
bool CP_IsRsaEncryptionOid(const asn_oid &oid){
	unsigned long p[] = {1, 2, 840, 113549, 1, 1, 1};
	return oid_equal(oid, p, 7);
}
bool CP_IsDhPublicNumberOid(const asn_oid &oid){
	unsigned long p[] = {1, 2, 840, 10046, 2, 1};
	return oid_equal(oid, p, 6);
}
bool CP_IsDsaOid(const asn_oid &oid){
	return oid_equal(oid, dssOid, 6);
}

// bit mask
static const long allOnes = (long) pow(2, ((sizeof (long)) * 8)) - 1; 

// OIDs for policy qualifiers
bool CP_IsCpsOid(const asn_oid &oid){
	return oid_equal(oid, cpsOid, 8);
}
bool CP_IsUnoticeOid(const asn_oid &oid){
	return oid_equal(oid, unoticeOid, 8);
}

// Not Yet Implemented: 
// Right now these routines assume a single section for Cert Policy
// When an RA with many CAs (and many Cert Policies) is supported, 
// most of these routines will have to take the name of the section to lookup policy from.
// Or maybe there should be a cert policy object with the issuer as a member variable. 

// return critical value for an extension
bool CP_IsCritical(const x509_Extension *extension) {
	bool crit;
	
	extension->critical.get_value(crit);
	return crit;
}

// Check that the signature algorithm is supported by searching for it in the .ini 

int CP_VerifySigAlg(const AlgorithmIdentifier &signature){
	char key[STRSIZE], alg[STRSIZE];
	int i, status = 0;
	asn_oid oid;
	bool found; 

	if (!signature.algorithm.is_present())
		return POL_ALG_REQ; 

	// Search for a matching algorithm in the .ini file

	for (i = 1; ; i++){
		sprintf(key, CP_SIGALG "%d", i);
		if (found = IniReadString(CP_SECTION, key, alg, STRSIZE)) {
			// Set the OID based on the algorithm name 
			if ((status = oid.set_value(alg)) != 0)
				return status; 
			if (oid == signature.algorithm)
				return 0; 
		}
		else // We've exhausted the list of supported algorithms 
			break; 
	}			
	return POL_ALG_UNSUP;
}

// Check that the issuer is a supported CA (from the RA) or the CA itself (from the CA). 

int CP_VerifyIssuer(const asn_x500name &issuer) {
	int i;
	char key[STRSIZE], ca[STRSIZE]; 
	bool found; 
	asn_x500name name;
	r_buffer_t buff;

	if (!issuer.is_present())
		return POL_ISS_REQ;

	if (IniAmICA()) {
		if (!IniReadString(GEN_SECTION, GEN_NAME, ca, STRSIZE)) 
			return POL_ISS_UNSUP; 

		buff.data = (unsigned char *) ca;
		buff.data_len = strlen(ca);
		name.set_value_UTF8(buff); 
		// compare it to the requested issuer
		if (name == issuer)
			return 0;
		else 
			return POL_ISS_UNSUP; 
			
	}

		// loop until find the issuer or run out of configured names 
	else
		for (i = 1; ; i++) {
			sprintf(key, GEN_ISSUER "%d", i);
	
			if (found = IniReadString(GEN_SECTION, key, ca, STRSIZE)) {
				buff.data = (unsigned char *) ca;
				buff.data_len = strlen(ca);
				name.set_value_UTF8(buff); 
				// compare it to the requested issuer
				if (name == issuer)
					return 0;
			}
			else 		// We've run out of Issuers to check
				return POL_ISS_UNSUP; 
		}
}
	
int CP_VerifyValidity(const OptionalValidity &validity) {
	bool start; 

	
	// If the start time is specified, can it be (EE to RA or RA to CA) 
	if (validity.notBefore.is_present()) {
		IniReadBoolean(CP_SECTION, CP_STARTTIME, start, CP_STARTTIMEDEF);
		if (!start)
			return POL_NO_START; 
	}

		// Is the lifetime less than or equal to the max cert lifetime (from the .ini)
		
		// Not Yet Implemented: 
		// Need to check lifetime too 
		// Need to align proposed UI with required data structures 
	
	return 0; 

}

int CP_VerifySubject(const asn_x500name &subject) {

	if (!subject.is_present())
		return POL_SUBJ_REQ; 

	return 0; 

}

// If a subject key is present, check that the EE/RA is allowed to set it. 
int CP_VerifySubjKey(const SubjectPublicKeyInfo &subjkey) {
	bool subj; 

	if (subjkey.is_present()) {
		IniReadBoolean(CP_SECTION, CP_KEYSPEC, subj, CP_KEYSPECDEF);
		if (!subj)
			return POL_NO_KEY; 
	}
	else if (subjkey.algorithm.is_present() || subjkey.subjectPublicKey.is_present())
		return POL_ALL_PKEY_INFO; 

// to be implemented - who specifies alg when CA generates key?

	return 0; 
}

// Serial number can not be present (filled in by the CA) 
int CP_VerifySerial(const asn_integer &serialNumber) {
	
	if (serialNumber.is_present())
		return POL_NO_SERIAL;
	else
		return 0; 
}

// Authority and Subject Key IDs have the same checks 

int CP_VerifyKeyId(const x509_Extensions &extensions, const int index) {

	// Key ID is set by CA
	if (index >= 0)
		return POL_KEYID_CA;		
	return 0; 
}

int CP_VerifyUsageAlg(const XKeyUsage &keyuse, const SubjectPublicKeyInfo &subjkey, const int ca) {
	
	long mask, usage;

	keyuse.get_value(usage);

	if (CP_IsRsaEncryptionOid(subjkey.algorithm.algorithm)) {
		if (ca < 0) { // Basic constraints are not allowed in PKIX non-CA certs
			mask =  allOnes ^ (rsaKeyUsage);
			if ((usage & mask) != 0)
				// some bits in usage were not in the allowed set 
				return POL_RSA_USAGE;
		}
		else { // RSA CA cert 
			mask =  allOnes ^ (rsaCaKeyUsage);
			if ((usage & mask) != 0)
				// some bits in usage were not in the allowed set 
				return POL_RSA_CA_USAGE;

			// If certsign or crlsign usage appear, neither key encipher and data encipher can
			mask = allOnes ^ (USAGE_keyCertSign & USAGE_cRLSign);
			if ((usage & mask) != 0) // There are more usages 
				if ((usage & USAGE_keyEncipherment) || (usage & USAGE_dataEncipherment))
					return POL_RSA_SIGN_ENCIPHER;
		}
	}
	else if (CP_IsDhPublicNumberOid(subjkey.algorithm.algorithm)) {
		mask = allOnes ^ dhKeyUsage;
		if ((usage & mask) != 0)
			return POL_DH_USAGE; 
		// Can't use the same key for encipering and deciphering 
		if ((usage & USAGE_encipherOnly) && (usage & USAGE_decipherOnly))
			return POL_DH_ENDECIPHER;
	}
	else if (CP_IsDsaOid(subjkey.algorithm.algorithm)) {
		if (ca < 0) { // Basic constraints are not allowed in PKIX non-CA certs
			mask =  allOnes ^ (dsaKeyUsage);
			if ((usage & mask) != 0)
				// some bits in usage were not in the allowed set 
				return POL_DSA_USAGE;
		}
		else { // DSA CA cert 
			mask =  allOnes ^ (dsaCaKeyUsage);
			if ((usage & mask) != 0)
				// some bits in usage were not in the allowed set 
				return POL_DSA_CA_USAGE;
		}
	}
	
	// Else, we don't know the algorithm; make no checks on its Key Usage

	return 0; 
}

int CP_VerifyKeyUsage(const x509_Extensions &extensions, int index, 
					  const SubjectPublicKeyInfo &subjkey, int ca) {
	bool b; 
	XKeyUsage keyuse;
	r_buffer_t buff;

	// If it's not found, return one way or the other 
	if (index < 0) {
		IniReadBoolean(CP_SECTION, CP_USAGEREQ, b, CP_USAGEREQDEF);
		if (b)
			return POL_USAGE_REQ;
		else 
			return 0; 
	}

	// Is Key Usage supported at this site? 
	IniReadBoolean(CP_SECTION, CP_USAGESUP, b, CP_USAGESUPDEF);
	if (!b)
		return POL_NO_USAGE;

	// It should be critical 
	if (!CP_IsCritical(extensions[index]))
		return POL_KEYUSAGE_CRIT; 
	
	// Check that Key Usages are consonent with the subject's algorithm and CA-hood

	extensions[index]->extnValue.get_value(buff.data, buff.data_len); 
	keyuse.read(buff);

	return (CP_VerifyUsageAlg(keyuse, subjkey, ca)); 

}

// Find the policy number in the .ini file for this policy
// Returns 0 if none found

int CP_FindPolicy(const asn_oid &poloid) {
	int polnum;
	char key[STRSIZE], name[STRSIZE];
	asn_oid oid;

	for (polnum = 1; ; polnum++) {
		sprintf(key, CP_POLNAME "%d", polnum);

		if (IniReadString(CP_SECTION, key, name, STRSIZE)) {
			// Get the OID for the named policy, if it exists
			if (oid.set_value(name) == 0)
				if (oid == poloid)
					return polnum; 
		}
		else 		// We've run out of Policies to check
			break; 
	}
	return 0; 
}

int CP_VerifyUnotice(const UserNotice &notice, const int polnum) {
	char key[STRSIZE], name[STRSIZE], text[EXPTEXTSIZE]; 
	int i, notnum; 
	long val; 
	DisplayText s; 
	asn_ia5String o; 

	if (notice.explicitText.is_present()){
		// Get the User Notice for the current policy
		sprintf(key, CP_USERNOTICE "%d", polnum); 
		// Explicit text has its own size limitation			
		if (!IniReadString(CP_SECTION, key, text, EXPTEXTSIZE))
			return POL_NO_UNOTICE; 
		s.set_value_UTF8(text); 
		if (!(notice.explicitText == s))
			return POL_UNOTICE_WRONG; 
	}
	
	if (notice.noticeRef.is_present()) {
		// Get the User Notice Organization for the current policy
		sprintf(key, CP_UNOTPOL "%d" CP_UNOTORG, polnum); 
		if (!IniReadString(CP_SECTION, key, name, STRSIZE))
			return POL_NO_UNOTICE; 
		o.set_value_UTF8(name);
		if (!(notice.noticeRef.organization == o))
			return POL_UNOTICE_WRONG; 

		// Check the notice numbers
		for (i = 1; i <= notice.noticeRef.noticeNumbers.get_child_count(); i++){
			sprintf(key, CP_UNOTPOL "%d" CP_UNOTNOTICE "%d", polnum, i);
			if (IniReadInteger(CP_SECTION, key, notnum)) {
				// We require the sequences of notice numbers to match 
				notice.noticeRef.noticeNumbers[i]->get_value(val);
				if (val != notnum)
					return POL_UNOTICE_WRONG;
			}
			else
				// more notice numbers in the cert than configured 
				return POL_UNOTICE_WRONG;
		}
	}
	return 0; 
}

int CP_FindCpsQual(const PolicyInformation *pol) {
	int i;
	bool found = false; 
	PolicyQualifierInfo *qual; 

	for (i = 0; i < pol->policyQualifiers.get_child_count(); i++) {
		qual = pol->policyQualifiers[i];
		if (CP_IsCpsOid(qual->policyQualifierId)) {
			found = true;
			break;
		}
	}

	if (found)
		return i;

	else
		return -1;
}
					
int CP_FindUnoticeQual(const PolicyInformation *pol) {
	int i;
	bool found = false;
	PolicyQualifierInfo *qual; 

	for (i = 0; i < pol->policyQualifiers.get_child_count(); i++) {
		qual = pol->policyQualifiers[i];
		if (CP_IsUnoticeOid(qual->policyQualifierId)) {
			found = true;
			break;
		}
	}

	if (found) 
		return i;

	else
		return -1;
}

int CP_VerifyPolicy(const x509_Extensions &extensions, const int index) {
	bool b; 
	int polnum, i, j, status; 
	char key[STRSIZE], name[STRSIZE];
	r_buffer_t buff;
	XCertificatePolicies pols; 
	PolicyQualifierInfo *qual; 
	asn_ia5String n; 
	
	// Check if policy is absent, and if it is required 
	if (index < 0) {
		IniReadBoolean(CP_SECTION, CP_POLREQ, b, CP_POLREQDEF);
		if (b)
			return POL_POLICY_REQ;
		else 
			return 0; 
	}

	// Check if the policy should be critical
	IniReadBoolean(CP_SECTION, CP_POLCRIT, b, CP_POLCRITDEF); 
	if (b) {
		if (!CP_IsCritical(extensions[index]))
			return POL_POLICY_CRIT; 
	}

	extensions[index]->extnValue.get_value(buff.data, buff.data_len); 
	pols.read(buff);
	
	// For each policy specified ...
	for (i = 0; i < pols.get_child_count(); i++) {

		// Check that each policy is a supported one 

		// Find the policy number in the ini file for this oid
		polnum = CP_FindPolicy(pols[i]->policyIdentifier);
		if (polnum < 1)
				return POL_POL_UNSUP; 

		// The policy's CPS or user notification is accurate, if it is specified
		
		j = CP_FindCpsQual(pols[i]); 
		if (j >= 0) {
			// Get the CPS for the current policy
			sprintf(key, CP_CPS "%d", polnum); 
			if (!IniReadString(CP_SECTION, key, name, STRSIZE))
				return POL_NO_CPS; 
			n.set_value_UTF8(name); 
			if (!(qual->qualifier.cPSuri == n))
				return POL_CPS_WRONG; 
		}

		j = CP_FindUnoticeQual(pols[i]); 
		if (j >= 0) {
			if ((status = CP_VerifyUnotice(qual->qualifier.userNotice, polnum)) != 0)
				return status; 
		}

	} // End of loop through specified policies in cert 

	return 0; 
}

int CP_VerifyBasicConst(const x509_Extensions &extensions, const int index) {
	r_buffer_t buff; 
	XBasicConstraints constraint; 
	bool b; 
	int status; 

	// If there are none, there's nothing to check
	if (index < 0)
		return 0;

	if ((status = extensions[index]->extnValue.get_value(buff.data, buff.data_len)) != 0)
		return status; 
	if ((status = constraint.read(buff)) != 0)
		return status; 

	// If there is one, CA is true and critical
	if ((status = constraint.cA.get_value(b)) != 0)
		return status; 
	if (!b)
		return POL_CA_REQ; 
	if (!CP_IsCritical(extensions[index]))
		return POL_CA_CRIT; 

	return 0; 
}

int CP_VerifyNameConst(const x509_Extensions &extensions, const int index, const int ca) {
	XNameConstraints constraints;
	r_buffer_t buff; 
	int i; 

	// If there are none, there's nothing to check
	if (index < 0)
		return 0;

	// Name constraints, if there, are critical
	if (!CP_IsCritical(extensions[index]))
		return POL_NAME_CRIT;

	// Name constraints are only on CA certs
	if (ca < 0)
		return POL_NAME_CA;

	extensions[index]->extnValue.get_value(buff.data, buff.data_len); 
	constraints.read(buff);

	// Check that min is 0 (default) and max is absent for permitted and excluded subtrees

	for (i = 0; i < constraints.permittedSubtrees.get_child_count(); i++) {
		if (!constraints.permittedSubtrees[i]->minimum.is_default_value())
			return POL_NAME_MIN;
		if (constraints.permittedSubtrees[i]->maximum.is_present())
			return POL_NAME_MAX; 
	}

	for (i = 0; i < constraints.excludedSubtrees.get_child_count(); i++) {
		if (!constraints.excludedSubtrees[i]->minimum.is_default_value())
			return POL_NAME_MIN;
		if (constraints.excludedSubtrees[i]->maximum.is_present())
			return POL_NAME_MAX; 
	}

	return 0; 
}

int CP_NewCertVerify(const CertTemplate &cert) {
	ExtArray extensions;

	return CP_NewCertVerify(cert, extensions); 
}

// CP_NewCertVerify

// This must be called before the CA makes any modifications to the new cert request
// as its checks include ensuring that any incoming values are allowed to be set by
// the caller. 

int CP_NewCertVerify(const CertTemplate &cert, ExtArray &indices) {

	int	status, i;
	long v;
	x509_Extension *ext; 

	// Initialize each extension index to non-existent
	for (i = 0; i < CP_EXTENSIONS; i++)
		indices[i] = -1;

	// Check that I am an RA or a CA 
	if (!IniAmIRA() && !IniAmICA())
		return POL_NOT_RA_CA;

	// Check the signature algorithm 
	if ((status = CP_VerifySigAlg(cert.signingAlg.value)) != 0)
		return status; 

	// Check that the version number is right
	// Our certs all have ID extensions, so must be V3
	if ((status = cert.version.value.get_value(v)) != 0)
		return status;
	if (v != 2)
		return POL_V3_REQ; 

	// Check that the issuer supported 
	if ((status = CP_VerifyIssuer(cert.issuer.value)) != 0)
		return status;

	// Check the start and end times 
	if ((status = CP_VerifyValidity(cert.validity.value)) != 0)
		return status;

	// Check the subject name
	if ((status = CP_VerifySubject(cert.subject.value)) != 0)
		return status; 

	// Check the subject public key information 
	if ((status = CP_VerifySubjKey(cert.publicKey.value)) != 0)
		return status;
	
	// Key IDs can not be present (as they are filled in by the CA) 
	if (cert.issuerUID.value.is_present() || 
		cert.subjectUID.value.is_present())
		return POL_NO_ID; 

	// Check the serial number 
	if ((status = CP_VerifySerial(cert.serialNumber.value)) != 0)
		return status; 

	// Determine which extensions were specified

	for (i = 0; i < cert.extensions.value.get_child_count(); i++) {
		ext = cert.extensions.value[i];

		// Each OID clause ensures that the extension does not appear more than once

		if (CP_IsAuthKeyOid(ext->extnID)){
			if (indices[authKeyExt] >= 0)
				return POL_NO_DUP;
			indices[authKeyExt] = i;
		}
		else if (CP_IsSubjKeyOid(ext->extnID)) {
			if (indices[subjKeyExt] >= 0)
				return POL_NO_DUP;
			indices[subjKeyExt] = i;
		}
		else if (CP_IsKeyUsageOid(ext->extnID)) {
			if (indices[keyUsageExt] >= 0)
				return POL_NO_DUP;
			indices[keyUsageExt] = i;
		}
			
		// ignore Private Key Period
		
		else if (CP_IsCertPolOid(ext->extnID)){
			if (indices[certPolExt] >= 0)
				return POL_NO_DUP;
			indices[certPolExt] = i;
		}

		// ignore Policy Mapping
		// ignore Subject and Issuer Alt Names
		// ignore Subject Directory Attributes

		else if (CP_IsBasicConstOid(ext->extnID)){
			if (indices[basicConstExt] >= 0)
				return POL_NO_DUP;
			indices[basicConstExt] = i;
		}
		else if (CP_IsNameConstOid(ext->extnID)) {
			if (indices[nameConstExt] >= 0)
				return POL_NO_DUP;
			indices[nameConstExt] = i;
		}

		// ignore Policy constraints
		// ignore extended key usage
		// ignore CRL distribution point
		// ignore authority information access

		// No extensions that we do not support can be both requested and marked critical 
		else if (CP_IsCritical(cert.extensions.value[i]))
				return POL_CRIT_UNSUP;

		else    // security stop gap until we return the fields we don't support
			return POL_EXT_UNSUP; 
	}

	// Check each of the extensions we support

	if ((status = CP_VerifyKeyId(cert.extensions.value, indices[authKeyExt])) != 0) 
		return status;
	if ((status = CP_VerifyKeyId(cert.extensions.value, indices[subjKeyExt])) != 0) 
		return status;
	if (( status = CP_VerifyKeyUsage(cert.extensions.value, indices[keyUsageExt], 
		cert.publicKey.value, indices[basicConstExt])) != 0)
		return status;
	if ((status = CP_VerifyPolicy(cert.extensions.value, indices[certPolExt])) != 0)
		return status;
	if ((status = CP_VerifyBasicConst(cert.extensions.value, indices[basicConstExt])) != 0)
		return status;
	if ((status = CP_VerifyNameConst(cert.extensions.value, indices[nameConstExt], 
				indices[basicConstExt])) != 0)
		return status;

	// Not implemented yet: 
	// It returns a list of fields that do not pass the verification checks. 
	// If there are any unrecognized or unsupported extensions marked critical 
	// verification will fail with an indication of what fields are unsupported. 
	// If any non-critical, unsupported extensions are requested, the call is successful 
	// (since those fields will be ignored on creation), and the unsupported fields are 
	// indicated in the list of fields that do not pass verification. 


	// success
	return 0; 
}