#include "CertPol.h"
#include "Init.h"
#include "test.h"
#include <iostream.h>

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


static unsigned long randomOid[] = {1, 2, 3, 4, 5};
static unsigned long myPolicyOid[] = {1, 34, 67, 7};
static unsigned long myLitePolicyOid[] = {8, 4, 1, 0}; 

int CP_FindExtension(const x509_Extensions &extensions, unsigned long p[], int n) {
	int i; 
	x509_Extension *ext; 

	for (i = 0; i < extensions.get_child_count(); i++) {
		ext = extensions[i];
		if (CP_OidsEqual(ext->extnID, p, n))
			return i;
	}

	return -1; 

}

int CP_TestSimple(void) {
	int status; 
	CertTemplate cert; 
	
	// Test the simplest default policy 
	if ((status = CP_MakeSimpleCert(cert)) != 0)
		return status;
	
	if ((status = CP_NewCertVerify(cert)) != 0)
		return status; 

	cout << "Success: ";
	return status;
}

int CP_TestAlgErrors(void) {
	int status; 
	CertTemplate cert; 
	
	if ((status = CP_MakeSimpleCert(cert)) != 0)
		return status; 

	// Bad alg	
	if ((status = cert.signingAlg.value.algorithm.set_value(randomOid, 5)) == 0) {
		if ((status = CP_NewCertVerify(cert)) != POL_ALG_UNSUP)
			return status; 
	}


	cout << "Success: ";
	return 0;

}

int CP_TestValidity(void) {
	int status;
	CertTemplate cert;
	long t;
	struct tm *tp; 

	if ((status = CP_MakeSimpleCert(cert)) != 0)
		return status; 

	t = time(NULL);
	tp = localtime(&t);

	// Date through 2049 is UTCTime
	// need to correct for tm_isdst? Need to convert to Zulu time? 
	cert.validity.value.notBefore.value.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);
	free(tp);

	if ((status = CP_NewCertVerify(cert)) != 0) 
		return status; 

	cout << "Success: ";
	return status;

}

int CP_TestVersion(void) {
	int status; 
	CertTemplate cert; 
	
	if ((status = CP_MakeSimpleCert(cert) != 0))
		return status; 

	// bad version
	if ((status = cert.version.value.set_value(0)) == 0){
		if ((status = CP_NewCertVerify(cert)) != POL_V3_REQ)
			return status; 
	}
	cout << "Success: ";
	return 0;

}

int CP_TestIssuerErrors(void) {
	int status; 
	CertTemplate cert; 
	
	if ((status = CP_MakeSimpleCert(cert)) != 0)
		return status; 

	// Bad issuer	
	if ((status = cert.signingAlg.value.algorithm.set_value(randomOid, 5)) == 0){
		if ((status = CP_NewCertVerify(cert)) != POL_ALG_UNSUP)
			return status; 
	}
	cout << "Success: ";
	return 0;

}

int CP_TestPubKey() {
	int status;
	CertTemplate cert;
	unsigned char c[] = "zz";

	if ((status = CP_MakeSimpleCert(cert)) != 0)
		return status; 

	if ((status = 
		cert.publicKey.value.algorithm.algorithm.set_value(dssOid, 6)) == 0) {
		if ((status = CP_NewCertVerify(cert)) != POL_ALL_PKEY_INFO)
			return status; 
	}
	if ((status = 
		cert.publicKey.value.subjectPublicKey.set_value(c, 2)) == 0) {
		if ((status = CP_NewCertVerify(cert)) != 0)
			return status; 
	}

	cout << "Success: ";
	return 0; 
}

int CP_TestUid(void) {
	int status;
	CertTemplate cert;
	unsigned char c[1] = {'a'};

	if ((status = CP_MakeSimpleCert(cert)) != 0)
		return status; 

	if ((status = cert.issuerUID.value.set_value(c, 1)) == 0) {
		if ((status = CP_NewCertVerify(cert)) != POL_NO_ID)
			return status; 
	}

	cout << "Success: ";
	return 0; 
}

int CP_TestSerial(void) {
	int status;
	CertTemplate cert;

	if ((status = CP_MakeSimpleCert(cert)) != 0)
		return status; 

	if ((status = cert.serialNumber.value.set_value(1)) == 0) {
		if ((status = CP_NewCertVerify(cert)) != POL_NO_SERIAL)
			return status; 
	}

	cout << "Success: ";
	return 0; 
}

int CP_TestKeyId(void) {
	int status, i;
	CertTemplate cert;
	x509_Extension *ext; 

	if ((status = CP_MakeSimpleCert(cert)) != 0)
		return status; 

	if ((i = CP_FindExtension(cert.extensions.value, authKeyOid, 4)) >= 0) {
		ext = cert.extensions.value[i];
		if ((status = ext->critical.set_value(true)) == 0) {
			if ((status = CP_NewCertVerify(cert)) != POL_KEYID_CRIT)
				return status; 
			ext->critical.set_value(false);
		}
		if ((status = ext->empty()) == 0) {
			if ((status = CP_NewCertVerify(cert)) != POL_KEYID_REQ)
				return status; 
		}
	}
	cout << "Success: ";
	return 0; 
}

int CP_TestCaKeyUsage(void) {
	int status;
	CertTemplate cert;
	x509_Extension *caext, *keyext; 
	buffer_t buff; 
	XBasicConstraints constraint; 
	XKeyUsage keyuse;
	unsigned char c[1] = {'a'};

	if ((status = CP_MakeSimpleCert(cert)) != 0)
		return status; 

	keyext = cert.extensions.value.add_child();
	if ((status = keyext->extnID.set_value(keyUsageOid, 4)) != 0)
		return status; 
	if ((status = keyext->critical.set_value(false)) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != POL_KEYUSAGE_CRIT)
		return status; 

	if ((status = keyext->critical.set_value(true)) != 0)
		return status; 

	// incorrect key usage for DSS 

	if ((status = 
		cert.publicKey.value.algorithm.algorithm.set_value(dssOid, 6)) != 0)
		return status; 
	if ((status = 
		cert.publicKey.value.subjectPublicKey.set_value(c, 2)) != 0) 
		return status; 

	keyuse.set_value(dhKeyUsage); 
	if ((status = keyuse.write(buff)) != 0)
		return status; 
	if ((status = keyext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != POL_DSA_USAGE)
		return status; 

	// Correct key usage for DSS 
	keyuse.set_value(dsaKeyUsage); 
	if ((status = keyuse.write(buff)) != 0)
		return status; 
	if ((status = keyext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != 0)
		return status; 

	// CA tests 
	caext = cert.extensions.value.add_child();
	if ((status = caext->extnID.set_value(basicConstOid, 4)) != 0)
		return status; 

	//If basic constraint exists, it must indicate CA
	constraint.cA.set_value(false); 
	if ((status = constraint.write(buff)) != 0)
		return status; 
	if ((status = caext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = caext->critical.set_value(true)) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != POL_CA_REQ)
		return status; 

	// If basic constraint exists, it must be critical 
	constraint.cA.set_value(true); 
	if ((status = constraint.write(buff)) != 0)
		return status; 
	if ((status = caext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = caext->critical.set_value(false)) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != POL_CA_CRIT)
		return status; 

	// Turn critical on basic const back on
	if ((status = caext->critical.set_value(true)) != 0)
		return status; 

	// Full correct key usage for a DSS CA 
	keyuse.set_value(dsaCaKeyUsage); 
	if ((status = keyuse.write(buff)) != 0)
		return status; 
	if ((status = keyext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != 0)
		return status; 

	cout << "Success: ";
	return 0; 
}

int CP_TestPolicy(void){
	int status;
	CertTemplate cert;
	x509_Extension *ext;
	buffer_t buff; 
	XCertificatePolicies pols; 
	PolicyInformation *polinfo; 
	PolicyQualifierInfo *qual; 
	asn_integer *num;

	if ((status = CP_MakeSimpleCert(cert)) != 0)
		return status; 

	ext = cert.extensions.value.add_child();
	if ((status = ext->extnID.set_value(certPolOid, 4)) != 0)
		return status; 
	polinfo = pols.add_child();

	// Unsupported policy oid
	if ((status = polinfo->policyIdentifier.set_value(randomOid, 4)) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != POL_POL_UNSUP)
		return status; 
	
	// Supported policy oid
	if ((status = polinfo->policyIdentifier.set_value(myPolicyOid, 4)) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != 0)
		return status; 

	// User notice qualifier 
	qual = polinfo->policyQualifiers.add_child();
	if ((status = qual->policyQualifierId.set_value(unoticeOid, 4)) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != 0)
		return status; 

	// Bad user notice text
	if ((status = qual->qualifier.userNotice.explicitText.set_value_UTF8("foo, bar, and baz")) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != POL_UNOTICE_WRONG)
		return status; 

	// correct unotice text
	if ((status = qual->qualifier.userNotice.explicitText.set_value_UTF8(
		"This is some very lawyerly statement for the relying party to read and make decisions based on")) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != 0)
		return status; 

	if ((status = qual->qualifier.userNotice.noticeRef.organization.set_value_UTF8("no org I know of")) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != POL_UNOTICE_WRONG)
		return status; 
	
	if ((status = qual->qualifier.userNotice.noticeRef.organization.set_value_UTF8("Iris Associates")) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != 0)
		return status; 

	num = qual->qualifier.userNotice.noticeRef.noticeNumbers.add_child();
	if ((status = num->set_value(1)) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != POL_UNOTICE_WRONG)
		return status; 
	
	
	if ((status = num->set_value(3)) != 0)
		return status;
	num = qual->qualifier.userNotice.noticeRef.noticeNumbers.add_child();
	if ((status = num->set_value(17)) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != 0)
		return status; 

	// a policy with a CPS

	polinfo = pols.add_child();

	// Supported policy oid
	if ((status = polinfo->policyIdentifier.set_value(myLitePolicyOid, 4)) != 0)
		return status;

	// no CPS
	qual = polinfo->policyQualifiers.add_child();
	if ((status = qual->policyQualifierId.set_value(cpsOid, 4)) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != POL_NO_CPS)
		return status; 

	// bad CPS
	if ((status = qual->qualifier.cPSuri.set_value_UTF8("foo, bar, and baz")) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != POL_CPS_WRONG)
		return status; 
	
	// good cps
	if ((status = qual->qualifier.cPSuri.set_value_UTF8("http://www.mycompany.com/cps.html")) != 0)
		return status;
	if ((status = pols.write(buff)) != 0)
		return status; 
	if ((status = ext->extnValue.set_value(buff.data, buff.data_len)) != 0)
		return status;
	if ((status = buff.clear()) != 0)
		return status; 
	if ((status = CP_NewCertVerify(cert)) != 0)
		return status; 
	

	cout << "Success: ";

	return 0; 
}

int main(int argc, char* argv[])
{
	int status;

	// intialize ini file, ana, and object store 
	// Test as an RA 
	if ((status = Init(Init_RA)) != 0)
		return status; 

	cout << "Testing Simple  ";
	status = CP_TestSimple();
	cout << "Status " << status << "\n";

	cout << "Testing AlgErrors  ";
	status = CP_TestAlgErrors();
	cout << "Status " << status << "\n";

	cout << "Testing Validity  ";
	status = CP_TestValidity();
	cout << "Status " << status << "\n";

	cout << "Testing Version  " ;
	status = CP_TestVersion();
	cout << "Status " << status << "\n";

	cout << "Testing Pub Key Info  ";
	status = CP_TestPubKey();
	cout << "Status " << status << "\n";

	cout << "Testing UID  ";
	status = CP_TestUid();
	cout << "Status " << status << "\n";

	cout << "Testing Serial number  ";
	status = CP_TestSerial();
	cout << "Status " << status << "\n";

	cout << "Testing Key ID  ";
	status = CP_TestKeyId();
	cout << "Status " << status << "\n";

	cout << "Testing Key Usage and CA constraint  ";
	status = CP_TestCaKeyUsage();
	cout << "Status " << status << "\n";

	cout << "Testing Policy and qualifiers  ";
	status = CP_TestPolicy();
	cout << "Status " << status << "\n";

  return status; 

}