certSerialEncodeTest.cpp   [plain text]


/* Copyright (c) 2006 Apple Computer, Inc.
 *
 * certSerialEncodeTest.cpp
 *
 * Verify proper encoding of unsigned integer as a DER_encoded signed integer.
 * Verifies Radar 4471281.
 *
 */

#include <utilLib/common.h>
#include <utilLib/cspwrap.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <clAppUtils/clutils.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <Security/cssm.h>
#include <Security/x509defs.h>
#include <Security/oidsattr.h>
#include <Security/oidscert.h>
#include <Security/oidsalg.h>
#include <Security/certextensions.h>
#include <Security/cssmapple.h>
#include <string.h>

#define SUBJ_KEY_LABEL		"subjectKey"
#define ROOT_KEY_LABEL		"rootKey"
/* default key and signature algorithm */
#define SIG_ALG_DEFAULT		CSSM_ALGID_SHA1WithRSA
#define SIG_OID_DEFAULT		CSSMOID_SHA1WithRSA
#define KEY_ALG_DEFAULT		CSSM_ALGID_RSA

/* for write certs/keys option */
#define ROOT_CERT_FILE_NAME		"ssRootCert.cer"
#define SUBJ_CERT_FILE_NAME		"ssSubjCert.cer"

/* public key in ref form, TP supports this as of 1/30/02 */
#define PUB_KEY_IS_REF			CSSM_TRUE

static void usage(char **argv)
{
	printf("Usage: %s [options]\n", argv[0]);
	printf("Options:\n");
	printf("    w[rite certs]\n");
	printf("    p(ause for MallocDebug)\n");
	printf("    q(uiet)\n");
	exit(1);
}

/*
 * RDN components
 */
static CSSM_APPLE_TP_NAME_OID rootRdn[] = 
{
	{ "Apple Computer",					&CSSMOID_OrganizationName },
	{ "The Big Cheesy Debug Root",		&CSSMOID_CommonName }
};
#define NUM_ROOT_NAMES	(sizeof(rootRdn) / sizeof(CSSM_APPLE_TP_NAME_OID))

/* test cases */
typedef struct {
	uint32		serialIn;		/* --> CSSM_TP_SubmitCredRequest */
	CSSM_SIZE	expectLen;	
	const uint8	*expect;
} SerialNumber;

/* 0x7f */
static const uint8 sn0_Data[1] = {0x7f};
static const SerialNumber sn0 = {0x7f, 1, sn0_Data };

/* 0x80 */
static const uint8 sn1_Data[2] = {0x00, 0x80};
static const SerialNumber sn1 = {0x80, 2, sn1_Data };

/* 0x7ff */
static const uint8 sn2_Data[2] = {0x07, 0xff};
static const SerialNumber sn2 = {0x7ff, 2, sn2_Data };

/* 0x80ff */
static const uint8 sn3_Data[3] = {0x00, 0x80, 0xff};
static const SerialNumber sn3 = {0x80ff, 3, sn3_Data };

/* 0xfffffff */
static const uint8 sn4_Data[4] = {0x0f, 0xff, 0xff, 0xff};
static const SerialNumber sn4 = {0xfffffff, 4, sn4_Data };

/* 0x0fffffff */
static const uint8 sn5_Data[4] = {0x0f, 0xff, 0xff, 0xff};
static const SerialNumber sn5 = {0x0fffffff, 4, sn5_Data };

/* 0x80000000 */
static const uint8 sn6_Data[5] = {0x00, 0x80, 0x00, 0x00, 0x00};
static const SerialNumber sn6 = {0x80000000, 5, sn6_Data };

static const SerialNumber *serialNumbers[] = {
	&sn0, &sn1, &sn2, &sn3, &sn4, &sn5, &sn6
};
#define NUM_SERIAL_NUMS (sizeof(serialNumbers) / sizeof(serialNumbers[0]))

static int doTest(
	CSSM_CL_HANDLE	clHand,			// CL handle
	CSSM_CSP_HANDLE	cspHand,		// CSP handle
	CSSM_TP_HANDLE	tpHand,			// TP handle
	CSSM_KEY_PTR	subjPubKey,
	CSSM_KEY_PTR	signerPrivKey,
	uint32			serialNumIn,
	CSSM_SIZE		serialNumExpLen,
	const uint8		*serialNumExp,
	CSSM_BOOL		quiet,
	CSSM_BOOL 		writeBlobs)
{
	CSSM_DATA					refId;			// mallocd by CSSM_TP_SubmitCredRequest
	CSSM_APPLE_TP_CERT_REQUEST	certReq;
	CSSM_TP_REQUEST_SET			reqSet;
	sint32						estTime;
	CSSM_BOOL					confirmRequired;
	CSSM_TP_RESULT_SET_PTR		resultSet;
	CSSM_ENCODED_CERT			*encCert;
	CSSM_TP_CALLERAUTH_CONTEXT 	CallerAuthContext;
	CSSM_FIELD					policyId;
	CSSM_RETURN					crtn;
	CSSM_DATA					*signedRootCert;
	int							ourRtn = 0;
	CSSM_DATA_PTR 				foundSerial = NULL;
	CSSM_HANDLE 				resultHand = 0;
	uint32 						numFields;

	/* certReq for root */
	memset(&certReq, 0, sizeof(CSSM_APPLE_TP_CERT_REQUEST));
	certReq.cspHand = cspHand;
	certReq.clHand = clHand;
	certReq.serialNumber = serialNumIn;
	certReq.numSubjectNames = NUM_ROOT_NAMES;
	certReq.subjectNames = rootRdn;
	certReq.numIssuerNames = 0;
	certReq.issuerNames = NULL;
	certReq.certPublicKey = subjPubKey;
	certReq.issuerPrivateKey = signerPrivKey;
	certReq.signatureAlg = CSSM_ALGID_SHA1WithRSA;
	certReq.signatureOid = CSSMOID_SHA1WithRSA;
	certReq.notBefore = 0;			// now
	certReq.notAfter = 10000;		// seconds from now
	certReq.numExtensions = 0;
	certReq.extensions = NULL;
	
	reqSet.NumberOfRequests = 1;
	reqSet.Requests = &certReq;
	
	/* a big CSSM_TP_CALLERAUTH_CONTEXT just to specify an OID */
	memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
	memset(&policyId, 0, sizeof(CSSM_FIELD));
	policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN;
	CallerAuthContext.Policy.NumberOfPolicyIds = 1;
	CallerAuthContext.Policy.PolicyIds = &policyId;
	
	/* generate root cert */
	if(!quiet) {
		printf("Creating root cert...\n");
	}
	crtn = CSSM_TP_SubmitCredRequest(tpHand,
		NULL,				// PreferredAuthority
		CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
		&reqSet,
		&CallerAuthContext,	
		&estTime,
		&refId);
	if(crtn) {
		printError("CSSM_TP_SubmitCredRequest", crtn);
		ourRtn = -1;
		goto errOut;
	}
	crtn = CSSM_TP_RetrieveCredResult(tpHand,
		&refId,
		NULL,				// CallerAuthCredentials
		&estTime,
		&confirmRequired,
		&resultSet);
	if(crtn) {
		printError("CSSM_TP_RetrieveCredResult", crtn);
		ourRtn = -1;
		goto errOut;
	}
	if(resultSet == NULL) {
		printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
		ourRtn = -1;
		goto errOut;
	}
	encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
	signedRootCert = &encCert->CertBlob;
	if(writeBlobs) {
		writeFile(ROOT_CERT_FILE_NAME, signedRootCert->Data, signedRootCert->Length);
		printf("...wrote %lu bytes to %s\n", signedRootCert->Length, 
			ROOT_CERT_FILE_NAME);
	}

	/* make sure it self-verifies */
	crtn = CSSM_CL_CertVerify(clHand, 0 /* CCHandle */,
		signedRootCert, signedRootCert,
		NULL, 0);
	if(crtn) {
		cssmPerror("CSSM_CL_CertVerify", crtn);
		printf("***Created cert does not self-verify\n");
		ourRtn = -1;
		goto errOut;
	}

	/* extract the field we're interested in verifying */
	crtn = CSSM_CL_CertGetFirstFieldValue(clHand, signedRootCert,
		&CSSMOID_X509V1SerialNumber, &resultHand, &numFields, &foundSerial);
	if(crtn) {
		cssmPerror("CSSM_CL_CertGetFirstFieldValue(serialNumber)", crtn);
		printf("***Can't obtain serial number\n");
		ourRtn = -1;
		goto errOut;
	}
	CSSM_CL_CertAbortQuery(clHand, resultHand);
	if(foundSerial->Length != serialNumExpLen) {
		printf("***expected serialNumber len 0x%lu, got 0x%lu\n",
			(unsigned long)serialNumExpLen, (unsigned long)foundSerial->Length);
		ourRtn = -1;
		goto errOut;
	}
	for(unsigned dex=0; dex<serialNumExpLen; dex++) {
		if(foundSerial->Data[dex] != serialNumExp[dex]) {
			printf("***SerialNumber mismatch at index %u: exp %02X got %02X\n",
				dex, (unsigned)serialNumExp[dex], 
				(unsigned)foundSerial->Data[dex]);
			ourRtn = -1;
		}
	}
	/* free retrieved serial number and the result set itself */
	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SerialNumber, foundSerial);
	CSSM_FREE(signedRootCert->Data);
	CSSM_FREE(encCert);
	CSSM_FREE(resultSet);
	/* Per the spec, this is supposed to be Opaque to us and the TP is supposed to free
	 * it when it goes out of scope...but libsecurity_keychains's 
	 * CertificateRequest::submitDotMac() frees this...that would have to change
	 * in order for the TP to free this properly. Someday maybe. No big deal. 
	 */
	CSSM_FREE(refId.Data);
errOut:
	return ourRtn;
}

int main(int argc, char **argv)
{
	CSSM_CL_HANDLE	clHand;			// CL handle
	CSSM_CSP_HANDLE	cspHand;		// CSP handle
	CSSM_TP_HANDLE	tpHand;			// TP handle
	CSSM_KEY		rootPubKey;		// root's RSA public key blob
	CSSM_KEY		rootPrivKey;	// root's RSA private key - ref format
	CSSM_RETURN		crtn;
	int				arg;
	unsigned 		dex;
	int 			ourRtn = 0;
	uint32			keySizeInBits = 512;
	CSSM_BOOL		doPause = CSSM_FALSE;

	/* user-spec'd variables */
	CSSM_BOOL		writeBlobs = CSSM_FALSE;
	CSSM_BOOL		quiet = CSSM_FALSE;

	for(arg=1; arg<argc; arg++) {
		switch(argv[arg][0]) {
			case 'w':
				writeBlobs = CSSM_TRUE;
				break;
			case 'q':
				quiet = CSSM_TRUE;
				break;
			case 'p':
				doPause = CSSM_TRUE;
				break;
			default:
				usage(argv);
		}
	}
	
	testStartBanner("certSerialEncodeTest", argc, argv);
	
	
	/* connect to CL, TP, and CSP */
	clHand = clStartup();
	if(clHand == 0) {
		return -1;
	}
	tpHand = tpStartup();
	if(tpHand == 0) {
		return -1;
	}
	cspHand = cspStartup();
	if(cspHand == 0) {
		return -1;
	}

	/* cook up key pair for self-signed cert */
	crtn = cspGenKeyPair(cspHand,
		CSSM_ALGID_RSA,
		ROOT_KEY_LABEL,
		strlen(ROOT_KEY_LABEL),
		keySizeInBits,
		&rootPubKey,
		CSSM_FALSE,			// pubIsRef - should work both ways, but not yet
		CSSM_KEYUSE_VERIFY,
		CSSM_KEYBLOB_RAW_FORMAT_NONE,
		&rootPrivKey,
		writeBlobs ? CSSM_FALSE : CSSM_TRUE,	// privIsRef
		CSSM_KEYUSE_SIGN,
		CSSM_KEYBLOB_RAW_FORMAT_NONE,
		CSSM_FALSE);
	if(crtn) {
		ourRtn = -1;
		goto abort;
	}

	for(dex=0; dex<NUM_SERIAL_NUMS; dex++) {
		const SerialNumber *sn = serialNumbers[dex];
		if(!quiet) {
			printf("...testing serial number 0x%lx\n", (unsigned long)sn->serialIn);
		}
		ourRtn = doTest(clHand, cspHand, tpHand,
			&rootPubKey, &rootPrivKey,
			sn->serialIn, sn->expectLen, sn->expect,
			quiet, writeBlobs);
		if(ourRtn) {
			break;
		}
		if(doPause) {
			fpurge(stdin);
			printf("Pausing for MallocDebug. a to abort, anything else to continue: ");
			if(getchar() == 'a') {
				break;
			}
		}
	}

	cspFreeKey(cspHand, &rootPubKey);
	cspFreeKey(cspHand, &rootPrivKey);

abort:
	if(cspHand != 0) {
		CSSM_ModuleDetach(cspHand);
	}
	if(clHand != 0) {
		CSSM_ModuleDetach(clHand);
	}
	if(tpHand != 0) {
		CSSM_ModuleDetach(tpHand);
	}

	if((ourRtn == 0) && !quiet) {
		printf("certSerialEncodeTest test succeeded\n");
	}
	return ourRtn;
}