#include <security_cdsa_utils/cuFileIo.h>
#include <clAppUtils/CertBuilderApp.h>
#include <utilLib/common.h>
#include <utilLib/cspwrap.h>
#include <clAppUtils/clutils.h>
#include <security_cdsa_utils/cuPrintCert.h>
#include <security_cdsa_utils/cuOidParser.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <Security/cssm.h>
#include <Security/x509defs.h>
#include <Security/oidsattr.h>
#include <Security/oidscert.h>
#include <Security/certextensions.h>
#define KEY_ALG CSSM_ALGID_RSA
#define SIG_ALG CSSM_ALGID_SHA1WithRSA
#define KEY_SIZE_BITS CSP_RSA_KEY_SIZE_DEFAULT
#define SUBJ_KEY_LABEL "subjectKey"
#define LOOPS_DEF 10
static void usage(char **argv)
{
printf("Usage: %s [options]\n", argv[0]);
printf("Options:\n");
printf(" e=extenSpec (default = all)\n");
printf(" k keyUsage\n");
printf(" b basicConstraints\n");
printf(" x extendedKeyUsage\n");
printf(" s subjectKeyId\n");
printf(" a authorityKeyId\n");
printf(" t SubjectAltName\n");
printf(" i IssuerAltName\n");
printf(" c certPolicies\n");
printf(" n netscapeCertType\n");
printf(" p CRLDistributionPoints\n");
printf(" A AuthorityInfoAccess\n");
printf(" S SubjectInfoAccess\n");
printf(" q QualifiedCertStatements\n");
printf(" w(rite blobs)\n");
printf(" f=fileName (default is extension-specific file name)\n");
printf(" d(isplay certs)\n");
printf(" l=loops (default = %d)\n", LOOPS_DEF);
printf(" p(ause on each loop)\n");
printf(" P(ause on each cert)\n");
exit(1);
}
CB_NameOid dummyRdn[] =
{
{ "Apple Computer", &CSSMOID_OrganizationName },
{ "Doug Mitchell", &CSSMOID_CommonName }
};
#define NUM_DUMMY_NAMES (sizeof(dummyRdn) / sizeof(CB_NameOid))
static CSSM_X509_NAME *dummyName;
static CSSM_X509_TIME *notBefore; static CSSM_X509_TIME *notAfter; static CSSM_KEY subjPrivKey;
static CSSM_KEY subjPubKey;
static CSSM_BOOL randBool()
{
unsigned r = genRand(1, 0x10000000);
return (r & 0x1) ? CSSM_TRUE : CSSM_FALSE;
}
static void randData(
CSSM_DATA_PTR data,
uint8 maxLen)
{
data->Data = (uint8 *)malloc(maxLen);
simpleGenData(data, 1, maxLen);
}
int compBool(
CSSM_BOOL pre,
CSSM_BOOL post,
const char *desc)
{
if(pre == post) {
return 0;
}
printf("***Boolean miscompare on %s\n", desc);
switch(post) {
case CSSM_FALSE:
case CSSM_TRUE:
break;
default:
printf("*** post value is %d expected %d\n",
(int)post, (int)pre);
break;
}
return 1;
}
static int compCssmData(
CSSM_DATA &d1,
CSSM_DATA &d2,
const char *desc)
{
if(appCompareCssmData(&d1, &d2)) {
return 0;
}
printf("CSSM_DATA miscompare on %s\n", desc);
return 1;
}
#pragma mark ----- individual extension tests -----
#pragma mark --- CE_KeyUsage ---
static void kuCreate(void *arg)
{
CE_KeyUsage *ku = (CE_KeyUsage *)arg;
*ku = 0;
*ku |= 1 << genRand(7, 15);
*ku |= 1 << genRand(7, 15);
}
static unsigned kuCompare(const void *pre, const void *post)
{
const CE_KeyUsage *kuPre = (CE_KeyUsage *)pre;
const CE_KeyUsage *kuPost = (CE_KeyUsage *)post;
if(*kuPre != *kuPost) {
printf("***Miscompare in CE_KeyUsage\n");
return 1;
}
return 0;
}
#pragma mark --- CE_BasicConstraints ---
static void bcCreate(void *arg)
{
CE_BasicConstraints *bc = (CE_BasicConstraints *)arg;
bc->cA = randBool();
bc->pathLenConstraintPresent = randBool();
if(bc->pathLenConstraintPresent) {
bc->pathLenConstraint = genRand(1,10);
}
}
static unsigned bcCompare(const void *pre, const void *post)
{
const CE_BasicConstraints *bcpre = (CE_BasicConstraints *)pre;
const CE_BasicConstraints *bcpost = (CE_BasicConstraints *)post;
unsigned rtn = 0;
rtn += compBool(bcpre->cA, bcpost->cA, "BasicConstraints.cA");
rtn += compBool(bcpre->pathLenConstraintPresent,
bcpost->pathLenConstraintPresent,
"BasicConstraints.pathLenConstraintPresent");
if(bcpre->pathLenConstraint != bcpost->pathLenConstraint) {
printf("BasicConstraints.pathLenConstraint mismatch\n");
rtn++;
}
return rtn;
}
#pragma mark --- CE_SubjectKeyID ---
static void skidCreate(void *arg)
{
CSSM_DATA_PTR skid = (CSSM_DATA_PTR)arg;
randData(skid, 16);
}
static unsigned skidCompare(const void *pre, const void *post)
{
CSSM_DATA_PTR spre = (CSSM_DATA_PTR)pre;
CSSM_DATA_PTR spost = (CSSM_DATA_PTR)post;
return compCssmData(*spre, *spost, "SubjectKeyID");
}
static void skidFree(void *arg)
{
CSSM_DATA_PTR skid = (CSSM_DATA_PTR)arg;
free(skid->Data);
}
#pragma mark --- CE_NetscapeCertType ---
static void nctCreate(void *arg)
{
CE_NetscapeCertType *nct = (CE_NetscapeCertType *)arg;
*nct = 0;
*nct |= 1 << genRand(8, 15);
*nct |= 1 << genRand(8, 15);
}
static unsigned nctCompare(const void *pre, const void *post)
{
const CE_NetscapeCertType *nPre = (CE_NetscapeCertType *)pre;
const CE_NetscapeCertType *nPost = (CE_NetscapeCertType *)post;
if(*nPre != *nPost) {
printf("***Miscompare in CE_NetscapeCertType\n");
return 1;
}
return 0;
}
#pragma mark --- CE_ExtendedKeyUsage ---
CSSM_OID ekuOids[] = {
CSSMOID_CrlNumber,
CSSMOID_CrlReason,
CSSMOID_HoldInstructionCode,
CSSMOID_InvalidityDate
};
#define NUM_SKU_OIDS 4
static void ekuCreate(void *arg)
{
CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)arg;
eku->numPurposes = genRand(1, NUM_SKU_OIDS);
eku->purposes = ekuOids;
}
static unsigned ekuCompare(const void *pre, const void *post)
{
CE_ExtendedKeyUsage *ekupre = (CE_ExtendedKeyUsage *)pre;
CE_ExtendedKeyUsage *ekupost = (CE_ExtendedKeyUsage *)post;
if(ekupre->numPurposes != ekupost->numPurposes) {
printf("CE_ExtendedKeyUsage.numPurposes miscompare\n");
return 1;
}
unsigned rtn = 0;
for(unsigned dex=0; dex<ekupre->numPurposes; dex++) {
rtn += compCssmData(ekupre->purposes[dex],
ekupost->purposes[dex], "CE_ExtendedKeyUsage.purposes");
}
return rtn;
}
#pragma mark --- general purpose X509 name generator ---
static char *attrStrings[] = {
(char *)"thisName",
(char *)"anotherName",
(char *)"someOtherName"
};
#define NUM_ATTR_STRINGS 3
static CSSM_OID attrTypes[] = {
CSSMOID_Surname,
CSSMOID_CountryName,
CSSMOID_OrganizationName,
CSSMOID_Description
};
#define NUM_ATTR_TYPES 4
static char attrTags[] = {
BER_TAG_PRINTABLE_STRING,
BER_TAG_IA5_STRING,
BER_TAG_T61_STRING
};
#define NUM_ATTR_TAGS 3
static void rdnCreate(
CSSM_X509_RDN_PTR rdn)
{
unsigned numPairs = genRand(1,4);
rdn->numberOfPairs = numPairs;
unsigned len = numPairs * sizeof(CSSM_X509_TYPE_VALUE_PAIR);
rdn->AttributeTypeAndValue =
(CSSM_X509_TYPE_VALUE_PAIR_PTR)malloc(len);
memset(rdn->AttributeTypeAndValue, 0, len);
for(unsigned atvDex=0; atvDex<numPairs; atvDex++) {
CSSM_X509_TYPE_VALUE_PAIR &pair =
rdn->AttributeTypeAndValue[atvDex];
unsigned die = genRand(1, NUM_ATTR_TYPES);
pair.type = attrTypes[die - 1];
die = genRand(1, NUM_ATTR_STRINGS);
char *str = attrStrings[die - 1];
pair.value.Data = (uint8 *)str;
pair.value.Length = strlen(str);
die = genRand(1, NUM_ATTR_TAGS);
pair.valueType = attrTags[die - 1];
}
}
static unsigned rdnCompare(
CSSM_X509_RDN_PTR rdn1,
CSSM_X509_RDN_PTR rdn2)
{
if(rdn1->numberOfPairs != rdn2->numberOfPairs) {
printf("***Mismatch in numberOfPairs\n");
return 1;
}
unsigned rtn = 0;
for(unsigned atvDex=0; atvDex<rdn1->numberOfPairs; atvDex++) {
CSSM_X509_TYPE_VALUE_PAIR &p1 =
rdn1->AttributeTypeAndValue[atvDex];
CSSM_X509_TYPE_VALUE_PAIR &p2 =
rdn2->AttributeTypeAndValue[atvDex];
if(p1.valueType != p2.valueType) {
printf("***valueType miscompare\n");
rtn++;
}
if(compCssmData(p1.type, p2.type, "ATV.type")) {
rtn++;
}
if(compCssmData(p1.value, p2.value, "ATV.value")) {
rtn++;
}
}
return rtn;
}
static void rdnFree(
CSSM_X509_RDN_PTR rdn)
{
free(rdn->AttributeTypeAndValue);
}
static void x509NameCreate(
CSSM_X509_NAME_PTR x509Name)
{
memset(x509Name, 0, sizeof(*x509Name));
unsigned numRdns = genRand(1,4);
x509Name->numberOfRDNs = numRdns;
unsigned len = numRdns * sizeof(CSSM_X509_RDN);
x509Name->RelativeDistinguishedName = (CSSM_X509_RDN_PTR)malloc(len);
memset(x509Name->RelativeDistinguishedName, 0, len);
for(unsigned rdnDex=0; rdnDex<numRdns; rdnDex++) {
CSSM_X509_RDN &rdn = x509Name->RelativeDistinguishedName[rdnDex];
rdnCreate(&rdn);
}
}
static unsigned x509NameCompare(
const CSSM_X509_NAME_PTR n1,
const CSSM_X509_NAME_PTR n2)
{
if(n1->numberOfRDNs != n2->numberOfRDNs) {
printf("***Mismatch in numberOfRDNs\n");
return 1;
}
unsigned rtn = 0;
for(unsigned rdnDex=0; rdnDex<n1->numberOfRDNs; rdnDex++) {
CSSM_X509_RDN &rdn1 = n1->RelativeDistinguishedName[rdnDex];
CSSM_X509_RDN &rdn2 = n2->RelativeDistinguishedName[rdnDex];
rtn += rdnCompare(&rdn1, &rdn2);
}
return rtn;
}
static void x509NameFree(
CSSM_X509_NAME_PTR n)
{
for(unsigned rdnDex=0; rdnDex<n->numberOfRDNs; rdnDex++) {
CSSM_X509_RDN &rdn = n->RelativeDistinguishedName[rdnDex];
rdnFree(&rdn);
}
free(n->RelativeDistinguishedName);
}
#pragma mark --- general purpose GeneralNames generator ---
#define SOME_URL_1 "http://foo.bar.com"
#define SOME_URL_2 "http://bar.foo.com"
#define SOME_DNS_1 "Some DNS"
#define SOME_DNS_2 "Another DNS"
unsigned char someIpAdr_1[] = {208, 161, 124, 209 };
unsigned char someIpAdr_2[] = {10, 0, 61, 5};
static void genNameCreate(CE_GeneralName *name)
{
unsigned type = genRand(1, 5);
const char *src;
unsigned char *usrc;
switch(type) {
case 1:
name->nameType = GNT_URI;
name->berEncoded = CSSM_FALSE;
src = randBool() ? SOME_URL_1 : SOME_URL_2;
appCopyData(src, strlen(src), &name->name);
break;
case 2:
name->nameType = GNT_RegisteredID;
name->berEncoded = CSSM_FALSE;
appCopyData(CSSMOID_SubjectDirectoryAttributes.Data,
CSSMOID_SubjectDirectoryAttributes.Length,
&name->name);
break;
case 3:
name->nameType = GNT_DNSName;
name->berEncoded = CSSM_FALSE;
src = randBool() ? SOME_DNS_1 : SOME_DNS_2;
appCopyData(src, strlen(src), &name->name);
break;
case 4:
name->nameType = GNT_IPAddress;
name->berEncoded = CSSM_FALSE;
usrc = randBool() ? someIpAdr_1 : someIpAdr_2;
appCopyData(usrc, 4, &name->name);
break;
case 5:
{
name->nameType = GNT_DirectoryName;
name->berEncoded = CSSM_FALSE;
appSetupCssmData(&name->name, sizeof(CSSM_X509_NAME));
x509NameCreate((CSSM_X509_NAME_PTR)name->name.Data);
}
}
}
static void genNamesCreate(void *arg)
{
CE_GeneralNames *names = (CE_GeneralNames *)arg;
names->numNames = genRand(1, 3);
names->generalName = (CE_GeneralName *)malloc(names->numNames *
sizeof(CE_GeneralName));
memset(names->generalName, 0, names->numNames * sizeof(CE_GeneralName));
for(unsigned i=0; i<names->numNames; i++) {
CE_GeneralName *name = &names->generalName[i];
genNameCreate(name);
}
}
static unsigned genNameCompare(
CE_GeneralName *npre,
CE_GeneralName *npost)
{
unsigned rtn = 0;
if(npre->nameType != npost->nameType) {
printf("***CE_GeneralName.nameType miscompare\n");
rtn++;
}
if(compBool(npre->berEncoded, npost->berEncoded,
"CE_GeneralName.berEncoded")) {
rtn++;
}
switch(npre->nameType) {
case GNT_RFC822Name:
rtn += compCssmData(npre->name, npost->name,
"CE_GeneralName.RFC822Name");
break;
case GNT_DNSName:
rtn += compCssmData(npre->name, npost->name,
"CE_GeneralName.DNSName");
break;
case GNT_URI:
rtn += compCssmData(npre->name, npost->name,
"CE_GeneralName.URI");
break;
case GNT_IPAddress:
rtn += compCssmData(npre->name, npost->name,
"CE_GeneralName.RFIPAddressC822Name");
break;
case GNT_RegisteredID:
rtn += compCssmData(npre->name, npost->name,
"CE_GeneralName.RegisteredID");
break;
case GNT_DirectoryName:
rtn += x509NameCompare((CSSM_X509_NAME_PTR)npre->name.Data,
(CSSM_X509_NAME_PTR)npost->name.Data);
break;
default:
printf("****BRRZAP! genNamesCompare needs work\n");
rtn++;
}
return rtn;
}
static unsigned genNamesCompare(const void *pre, const void *post)
{
const CE_GeneralNames *gnPre = (CE_GeneralNames *)pre;
const CE_GeneralNames *gnPost = (CE_GeneralNames *)post;
unsigned rtn = 0;
if((gnPre == NULL) || (gnPost == NULL)) {
printf("***Bad GenNames pointer\n");
return 1;
}
if(gnPre->numNames != gnPost->numNames) {
printf("***CE_GeneralNames.numNames miscompare\n");
return 1;
}
for(unsigned dex=0; dex<gnPre->numNames; dex++) {
CE_GeneralName *npre = &gnPre->generalName[dex];
CE_GeneralName *npost = &gnPost->generalName[dex];
rtn += genNameCompare(npre, npost);
}
return rtn;
}
static void genNameFree(CE_GeneralName *n)
{
switch(n->nameType) {
case GNT_DirectoryName:
x509NameFree((CSSM_X509_NAME_PTR)n->name.Data);
CSSM_FREE(n->name.Data);
break;
default:
CSSM_FREE(n->name.Data);
break;
}
}
static void genNamesFree(void *arg)
{
const CE_GeneralNames *gn = (CE_GeneralNames *)arg;
for(unsigned dex=0; dex<gn->numNames; dex++) {
CE_GeneralName *n = (CE_GeneralName *)&gn->generalName[dex];
genNameFree(n);
}
free(gn->generalName);
}
#pragma mark --- CE_CRLDistPointsSyntax ---
static void cdpCreate(void *arg)
{
CE_CRLDistPointsSyntax *cdp = (CE_CRLDistPointsSyntax *)arg;
cdp->numDistPoints = 1;
unsigned len = sizeof(CE_CRLDistributionPoint) * cdp->numDistPoints;
cdp->distPoints = (CE_CRLDistributionPoint *)malloc(len);
memset(cdp->distPoints, 0, len);
for(unsigned dex=0; dex<cdp->numDistPoints; dex++) {
CE_CRLDistributionPoint *pt = &cdp->distPoints[dex];
if(randBool()) {
CE_DistributionPointName *dpn = pt->distPointName =
(CE_DistributionPointName *)malloc(
sizeof(CE_DistributionPointName));
memset(dpn, 0, sizeof(CE_DistributionPointName));
if(randBool()) {
dpn->nameType = CE_CDNT_FullName;
dpn->dpn.fullName = (CE_GeneralNames *)malloc(
sizeof(CE_GeneralNames));
memset(dpn->dpn.fullName, 0, sizeof(CE_GeneralNames));
genNamesCreate(dpn->dpn.fullName);
}
else {
dpn->nameType = CE_CDNT_NameRelativeToCrlIssuer;
dpn->dpn.rdn = (CSSM_X509_RDN_PTR)malloc(
sizeof(CSSM_X509_RDN));
memset(dpn->dpn.rdn, 0, sizeof(CSSM_X509_RDN));
rdnCreate(dpn->dpn.rdn);
}
}
pt->reasonsPresent = randBool();
if(pt->reasonsPresent) {
CE_CrlDistReasonFlags *cdr = &pt->reasons;
*cdr = 0;
*cdr |= 1 << genRand(0,7);
*cdr |= 1 << genRand(0,7);
}
if((!pt->distPointName && !pt->reasonsPresent) || randBool()) {
pt->crlIssuer = (CE_GeneralNames *)malloc(sizeof(CE_GeneralNames));
memset(pt->crlIssuer, 0, sizeof(CE_GeneralNames));
genNamesCreate(pt->crlIssuer);
}
}
}
static unsigned cdpCompare(const void *pre, const void *post)
{
CE_CRLDistPointsSyntax *cpre = (CE_CRLDistPointsSyntax *)pre;
CE_CRLDistPointsSyntax *cpost = (CE_CRLDistPointsSyntax *)post;
if(cpre->numDistPoints != cpost->numDistPoints) {
printf("***CE_CRLDistPointsSyntax.numDistPoints miscompare\n");
return 1;
}
unsigned rtn = 0;
for(unsigned dex=0; dex<cpre->numDistPoints; dex++) {
CE_CRLDistributionPoint *ptpre = &cpre->distPoints[dex];
CE_CRLDistributionPoint *ptpost = &cpost->distPoints[dex];
if(ptpre->distPointName) {
if(ptpost->distPointName == NULL) {
printf("***NULL distPointName post decode\n");
rtn++;
goto checkReason;
}
CE_DistributionPointName *dpnpre = ptpre->distPointName;
CE_DistributionPointName *dpnpost = ptpost->distPointName;
if(dpnpre->nameType != dpnpost->nameType) {
printf("***CE_DistributionPointName.nameType miscompare\n");
rtn++;
goto checkReason;
}
if(dpnpre->nameType == CE_CDNT_FullName) {
rtn += genNamesCompare(dpnpre->dpn.fullName, dpnpost->dpn.fullName);
}
else {
rtn += rdnCompare(dpnpre->dpn.rdn, dpnpost->dpn.rdn);
}
}
else if(ptpost->distPointName != NULL) {
printf("***NON NULL distPointName post decode\n");
rtn++;
}
checkReason:
if(ptpre->reasons != ptpost->reasons) {
printf("***CE_CRLDistributionPoint.reasons miscompare\n");
rtn++;
}
if(ptpre->crlIssuer) {
if(ptpost->crlIssuer == NULL) {
printf("***NULL crlIssuer post decode\n");
rtn++;
continue;
}
CE_GeneralNames *gnpre = ptpre->crlIssuer;
CE_GeneralNames *gnpost = ptpost->crlIssuer;
rtn += genNamesCompare(gnpre, gnpost);
}
else if(ptpost->crlIssuer != NULL) {
printf("***NON NULL crlIssuer post decode\n");
rtn++;
}
}
return rtn;
}
static void cdpFree(void *arg)
{
CE_CRLDistPointsSyntax *cdp = (CE_CRLDistPointsSyntax *)arg;
for(unsigned dex=0; dex<cdp->numDistPoints; dex++) {
CE_CRLDistributionPoint *pt = &cdp->distPoints[dex];
if(pt->distPointName) {
CE_DistributionPointName *dpn = pt->distPointName;
if(dpn->nameType == CE_CDNT_FullName) {
genNamesFree(dpn->dpn.fullName);
free(dpn->dpn.fullName);
}
else {
rdnFree(dpn->dpn.rdn);
free(dpn->dpn.rdn);
}
free(dpn);
}
if(pt->crlIssuer) {
genNamesFree(pt->crlIssuer);
free(pt->crlIssuer);
}
}
free(cdp->distPoints);
}
#pragma mark --- CE_AuthorityKeyID ---
static void authKeyIdCreate(void *arg)
{
CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)arg;
akid->keyIdentifierPresent = randBool();
if(akid->keyIdentifierPresent) {
randData(&akid->keyIdentifier, 16);
}
akid->generalNamesPresent = randBool();
if(akid->generalNamesPresent) {
akid->generalNames =
(CE_GeneralNames *)malloc(sizeof(CE_GeneralNames));
memset(akid->generalNames, 0, sizeof(CE_GeneralNames));
genNamesCreate(akid->generalNames);
}
if(!akid->keyIdentifierPresent & !akid->generalNamesPresent) {
akid->serialNumberPresent = CSSM_TRUE;
}
else {
akid->serialNumberPresent = randBool();
}
if(akid->serialNumberPresent) {
randData(&akid->serialNumber, 16);
}
}
static unsigned authKeyIdCompare(const void *pre, const void *post)
{
CE_AuthorityKeyID *akpre = (CE_AuthorityKeyID *)pre;
CE_AuthorityKeyID *akpost = (CE_AuthorityKeyID *)post;
unsigned rtn = 0;
if(compBool(akpre->keyIdentifierPresent, akpost->keyIdentifierPresent,
"CE_AuthorityKeyID.keyIdentifierPresent")) {
rtn++;
}
else if(akpre->keyIdentifierPresent) {
rtn += compCssmData(akpre->keyIdentifier,
akpost->keyIdentifier, "CE_AuthorityKeyID.keyIdentifier");
}
if(compBool(akpre->generalNamesPresent, akpost->generalNamesPresent,
"CE_AuthorityKeyID.generalNamesPresent")) {
rtn++;
}
else if(akpre->generalNamesPresent) {
rtn += genNamesCompare(akpre->generalNames,
akpost->generalNames);
}
if(compBool(akpre->serialNumberPresent, akpost->serialNumberPresent,
"CE_AuthorityKeyID.serialNumberPresent")) {
rtn++;
}
else if(akpre->serialNumberPresent) {
rtn += compCssmData(akpre->serialNumber,
akpost->serialNumber, "CE_AuthorityKeyID.serialNumber");
}
return rtn;
}
static void authKeyIdFree(void *arg)
{
CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)arg;
if(akid->keyIdentifier.Data) {
free(akid->keyIdentifier.Data);
}
if(akid->generalNames) {
genNamesFree(akid->generalNames); free(akid->generalNames); }
if(akid->serialNumber.Data) {
free(akid->serialNumber.Data);
}
}
#pragma mark --- CE_CertPolicies ---
static CSSM_OID cpOids[] =
{
CSSMOID_EmailAddress,
CSSMOID_UnstructuredName,
CSSMOID_ContentType,
CSSMOID_MessageDigest
};
#define NUM_CP_OIDS 4
static char *someCPSs[] =
{
(char *)"http://www.apple.com",
(char *)"https://cdnow.com",
(char *)"ftp:backwards.com"
};
#define NUM_CPS_STR 3
static uint8 someUnotice[] = {0x30, 0x03, BER_TAG_BOOLEAN, 1, 0xff};
static uint8 someOtherData[] = {0x30, 0x02, BER_TAG_NULL, 0};
static void cpCreate(void *arg)
{
CE_CertPolicies *cp = (CE_CertPolicies *)arg;
cp->numPolicies = genRand(1,3);
unsigned len = sizeof(CE_PolicyInformation) * cp->numPolicies;
cp->policies = (CE_PolicyInformation *)malloc(len);
memset(cp->policies, 0, len);
for(unsigned polDex=0; polDex<cp->numPolicies; polDex++) {
CE_PolicyInformation *pi = &cp->policies[polDex];
unsigned die = genRand(1, NUM_CP_OIDS);
pi->certPolicyId = cpOids[die - 1];
unsigned numQual = genRand(1,3);
pi->numPolicyQualifiers = numQual;
len = sizeof(CE_PolicyQualifierInfo) * numQual;
pi->policyQualifiers = (CE_PolicyQualifierInfo *)
malloc(len);
memset(pi->policyQualifiers, 0, len);
for(unsigned cpiDex=0; cpiDex<numQual; cpiDex++) {
CE_PolicyQualifierInfo *qi =
&pi->policyQualifiers[cpiDex];
if(randBool()) {
qi->policyQualifierId = CSSMOID_QT_CPS;
die = genRand(1, NUM_CPS_STR);
qi->qualifier.Data = (uint8 *)someCPSs[die - 1];
qi->qualifier.Length = strlen((char *)qi->qualifier.Data);
}
else {
qi->policyQualifierId = CSSMOID_QT_UNOTICE;
if(randBool()) {
qi->qualifier.Data = someUnotice;
qi->qualifier.Length = 5;
}
else {
qi->qualifier.Data = someOtherData;
qi->qualifier.Length = 4;
}
}
}
}
}
static unsigned cpCompare(const void *pre, const void *post)
{
CE_CertPolicies *cppre = (CE_CertPolicies *)pre;
CE_CertPolicies *cppost = (CE_CertPolicies *)post;
if(cppre->numPolicies != cppost->numPolicies) {
printf("CE_CertPolicies.numPolicies mismatch\n");
return 1;
}
unsigned rtn = 0;
for(unsigned polDex=0; polDex<cppre->numPolicies; polDex++) {
CE_PolicyInformation *pipre = &cppre->policies[polDex];
CE_PolicyInformation *pipost = &cppost->policies[polDex];
rtn += compCssmData(pipre->certPolicyId, pipost->certPolicyId,
"CE_PolicyInformation.certPolicyId");
if(pipre->numPolicyQualifiers != pipost->numPolicyQualifiers) {
printf("CE_PolicyInformation.CE_PolicyInformation mismatch\n");
rtn++;
continue;
}
for(unsigned qiDex=0; qiDex<pipre->numPolicyQualifiers; qiDex++) {
CE_PolicyQualifierInfo *qipre = &pipre->policyQualifiers[qiDex];
CE_PolicyQualifierInfo *qipost = &pipost->policyQualifiers[qiDex];
rtn += compCssmData(qipre->policyQualifierId,
qipost->policyQualifierId,
"CE_PolicyQualifierInfo.policyQualifierId");
rtn += compCssmData(qipre->qualifier,
qipost->qualifier,
"CE_PolicyQualifierInfo.qualifier");
}
}
return rtn;
}
static void cpFree(void *arg)
{
CE_CertPolicies *cp = (CE_CertPolicies *)arg;
for(unsigned polDex=0; polDex<cp->numPolicies; polDex++) {
CE_PolicyInformation *pi = &cp->policies[polDex];
free(pi->policyQualifiers);
}
free(cp->policies);
}
#pragma mark --- CE_AuthorityInfoAccess ---
static CSSM_OID aiOids[] =
{
CSSMOID_AD_OCSP,
CSSMOID_AD_CA_ISSUERS,
CSSMOID_AD_TIME_STAMPING,
CSSMOID_AD_CA_REPOSITORY
};
#define NUM_AI_OIDS 4
static void aiaCreate(void *arg)
{
CE_AuthorityInfoAccess *aia = (CE_AuthorityInfoAccess *)arg;
aia->numAccessDescriptions = genRand(1,3);
unsigned len = aia->numAccessDescriptions * sizeof(CE_AccessDescription);
aia->accessDescriptions = (CE_AccessDescription *)malloc(len);
memset(aia->accessDescriptions, 0, len);
for(unsigned dex=0; dex<aia->numAccessDescriptions; dex++) {
CE_AccessDescription *ad = &aia->accessDescriptions[dex];
int die = genRand(1, NUM_AI_OIDS);
ad->accessMethod = aiOids[die - 1];
genNameCreate(&ad->accessLocation);
}
}
static unsigned aiaCompare(const void *pre, const void *post)
{
CE_AuthorityInfoAccess *apre = (CE_AuthorityInfoAccess *)pre;
CE_AuthorityInfoAccess *apost = (CE_AuthorityInfoAccess *)post;
unsigned rtn = 0;
if(apre->numAccessDescriptions != apost->numAccessDescriptions) {
printf("***CE_AuthorityInfoAccess.numAccessDescriptions miscompare\n");
return 1;
}
for(unsigned dex=0; dex<apre->numAccessDescriptions; dex++) {
CE_AccessDescription *adPre = &apre->accessDescriptions[dex];
CE_AccessDescription *adPost = &apost->accessDescriptions[dex];
if(compCssmData(adPre->accessMethod, adPost->accessMethod,
"CE_AccessDescription.accessMethod")) {
rtn++;
}
rtn += genNameCompare(&adPre->accessLocation, &adPost->accessLocation);
}
return rtn;
}
static void aiaFree(void *arg)
{
CE_AuthorityInfoAccess *aia = (CE_AuthorityInfoAccess *)arg;
for(unsigned dex=0; dex<aia->numAccessDescriptions; dex++) {
CE_AccessDescription *ad = &aia->accessDescriptions[dex];
genNameFree(&ad->accessLocation);
}
free(aia->accessDescriptions);
}
#pragma mark --- CE_QC_Statements ---
static const CSSM_OID qcsOids[] = {
CSSMOID_OID_QCS_SYNTAX_V1,
CSSMOID_OID_QCS_SYNTAX_V2,
CSSMOID_ETSI_QCS_QC_COMPLIANCE,
};
#define NUM_QCS_OIDS 3
#define WHICH_QCS_V2 1
static void qcsCreate(void *arg)
{
CE_QC_Statements *qcss = (CE_QC_Statements *)arg;
unsigned numQcs = 1;
qcss->numQCStatements = numQcs;
qcss->qcStatements = (CE_QC_Statement *)malloc(numQcs * sizeof(CE_QC_Statement));
memset(qcss->qcStatements, 0, numQcs * sizeof(CE_QC_Statement));
for(unsigned dex=0; dex<numQcs; dex++) {
CE_QC_Statement *qcs = &qcss->qcStatements[dex];
unsigned whichOid = genRand(0, NUM_QCS_OIDS-1);
qcs->statementId = qcsOids[whichOid];
unsigned coin = genRand(1, 2);
switch(coin) {
case 1:
break;
case 2:
{
if(whichOid == WHICH_QCS_V2) {
CE_SemanticsInformation *si = (CE_SemanticsInformation *)malloc(
sizeof(CE_SemanticsInformation));
qcs->semanticsInfo = si;
memset(si, 0, sizeof(CE_SemanticsInformation));
coin = genRand(1, 2);
if(coin == 2) {
si->semanticsIdentifier = (CSSM_OID *)malloc(sizeof(CSSM_OID));
*si->semanticsIdentifier = qcsOids[0];
}
coin = genRand(1, 2);
if((coin == 2) || (si->semanticsIdentifier == NULL)) {
si->nameRegistrationAuthorities = (CE_NameRegistrationAuthorities *)
malloc(sizeof(CE_NameRegistrationAuthorities));
genNamesCreate(si->nameRegistrationAuthorities);
}
}
else {
CSSM_DATA *otherInfo = (CSSM_DATA *)malloc(sizeof(CSSM_DATA));
otherInfo->Data = (uint8 *)malloc(2);
otherInfo->Data[0] = 5;
otherInfo->Data[1] = 0;
otherInfo->Length = 2;
qcs->otherInfo = otherInfo;
}
break;
}
}
}
}
static unsigned qcsCompare(const void *pre, const void *post)
{
CE_QC_Statements *qpre = (CE_QC_Statements *)pre;
CE_QC_Statements *qpost = (CE_QC_Statements *)post;
uint32 numQcs = qpre->numQCStatements;
if(numQcs != qpost->numQCStatements) {
printf("***numQCStatements miscompare\n");
return 1;
}
unsigned rtn = 0;
for(unsigned dex=0; dex<numQcs; dex++) {
CE_QC_Statement *qcsPre = &qpre->qcStatements[dex];
CE_QC_Statement *qcsPost = &qpost->qcStatements[dex];
if(compCssmData(qcsPre->statementId, qcsPost->statementId,
"CE_QC_Statement.statementId")) {
rtn++;
}
if(qcsPre->semanticsInfo) {
if(qcsPost->semanticsInfo == NULL) {
printf("***semanticsInfo in pre but not in post\n");
rtn++;
}
else {
CE_SemanticsInformation *siPre = qcsPre->semanticsInfo;
CE_SemanticsInformation *siPost = qcsPost->semanticsInfo;
if((siPre->semanticsIdentifier == NULL) != (siPost->semanticsIdentifier == NULL)) {
printf("***mismatch in presence of semanticsIdentifier\n");
rtn++;
}
else if(siPre->semanticsIdentifier) {
if(compCssmData(*siPre->semanticsIdentifier, *siPost->semanticsIdentifier,
"CE_SemanticsInformation.semanticsIdentifier")) {
rtn++;
}
}
if((siPre->nameRegistrationAuthorities == NULL) !=
(siPost->nameRegistrationAuthorities == NULL)) {
printf("***mismatch in presence of nameRegistrationAuthorities\n");
rtn++;
}
else if(siPre->nameRegistrationAuthorities) {
rtn += genNamesCompare(siPre->nameRegistrationAuthorities,
siPost->nameRegistrationAuthorities);
}
}
}
else if(qcsPost->semanticsInfo != NULL) {
printf("***semanticsInfo in post but not in pre\n");
rtn++;
}
if(qcsPre->otherInfo) {
if(qcsPost->otherInfo == NULL) {
printf("***otherInfo in pre but not in post\n");
rtn++;
}
else {
if(compCssmData(*qcsPre->otherInfo, *qcsPre->otherInfo,
"CE_QC_Statement.otherInfo")) {
rtn++;
}
}
}
else if(qcsPost->otherInfo != NULL) {
printf("***otherInfo in post but not in pre\n");
rtn++;
}
}
return rtn;
}
static void qcsFree(void *arg)
{
CE_QC_Statements *qcss = (CE_QC_Statements *)arg;
uint32 numQcs = qcss->numQCStatements;
for(unsigned dex=0; dex<numQcs; dex++) {
CE_QC_Statement *qcs = &qcss->qcStatements[dex];
if(qcs->semanticsInfo) {
CE_SemanticsInformation *si = qcs->semanticsInfo;
if(si->semanticsIdentifier) {
free(si->semanticsIdentifier);
}
if(si->nameRegistrationAuthorities) {
genNamesFree(si->nameRegistrationAuthorities);
free(si->nameRegistrationAuthorities);
}
free(qcs->semanticsInfo);
}
if(qcs->otherInfo) {
free(qcs->otherInfo->Data);
free(qcs->otherInfo);
}
}
free(qcss->qcStatements);
}
#pragma mark --- test definitions ---
typedef void (*extenCreateFcn)(void *arg);
typedef unsigned (*extenCompareFcn)(
const void *preEncode,
const void *postEncode);
typedef void (*extenFreeFcn)(void *arg);
typedef struct {
extenCreateFcn createFcn;
extenCompareFcn compareFcn;
extenFreeFcn freeFcn;
unsigned extenSize;
CSSM_OID extenOid;
const char *extenDescr;
char extenLetter;
} ExtenTest;
#define NO_FREE NULL
static ExtenTest extenTests[] = {
{ kuCreate, kuCompare, NO_FREE,
sizeof(CE_KeyUsage), CSSMOID_KeyUsage,
"KeyUsage", 'k' },
{ bcCreate, bcCompare, NO_FREE,
sizeof(CE_BasicConstraints), CSSMOID_BasicConstraints,
"BasicConstraints", 'b' },
{ ekuCreate, ekuCompare, NO_FREE,
sizeof(CE_ExtendedKeyUsage), CSSMOID_ExtendedKeyUsage,
"ExtendedKeyUsage", 'x' },
{ skidCreate, skidCompare, skidFree,
sizeof(CSSM_DATA), CSSMOID_SubjectKeyIdentifier,
"SubjectKeyID", 's' },
{ authKeyIdCreate, authKeyIdCompare, authKeyIdFree,
sizeof(CE_AuthorityKeyID), CSSMOID_AuthorityKeyIdentifier,
"AuthorityKeyID", 'a' },
{ genNamesCreate, genNamesCompare, genNamesFree,
sizeof(CE_GeneralNames), CSSMOID_SubjectAltName,
"SubjectAltName", 't' },
{ genNamesCreate, genNamesCompare, genNamesFree,
sizeof(CE_GeneralNames), CSSMOID_IssuerAltName,
"IssuerAltName", 'i' },
{ nctCreate, nctCompare, NO_FREE,
sizeof(CE_NetscapeCertType), CSSMOID_NetscapeCertType,
"NetscapeCertType", 'n' },
{ cdpCreate, cdpCompare, cdpFree,
sizeof(CE_CRLDistPointsSyntax), CSSMOID_CrlDistributionPoints,
"CRLDistPoints", 'p' },
{ cpCreate, cpCompare, cpFree,
sizeof(CE_CertPolicies), CSSMOID_CertificatePolicies,
"CertPolicies", 'c' },
{ aiaCreate, aiaCompare, aiaFree,
sizeof(CE_AuthorityInfoAccess), CSSMOID_AuthorityInfoAccess,
"AuthorityInfoAccess", 'A' },
{ aiaCreate, aiaCompare, aiaFree,
sizeof(CE_AuthorityInfoAccess), CSSMOID_SubjectInfoAccess,
"SubjectInfoAccess", 'S' },
{ qcsCreate, qcsCompare, qcsFree,
sizeof(CE_QC_Statements), CSSMOID_QC_Statements,
"QualifiedCertStatements", 'q' },
};
#define NUM_EXTEN_TESTS (sizeof(extenTests) / sizeof(ExtenTest))
static void printExten(
CSSM_X509_EXTENSION &extn,
bool preEncode,
OidParser &parser)
{
CSSM_FIELD field;
field.FieldOid = extn.extnId;
field.FieldValue.Data = (uint8 *)&extn;
field.FieldValue.Length = sizeof(CSSM_X509_EXTENSION);
printf("=== %s:\n", preEncode ? "PRE-ENCODE" : "POST-DECODE" );
printCertField(field, parser, CSSM_TRUE);
}
static int doTest(
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
ExtenTest &extenTest,
bool writeBlobs,
const char *constFileName, bool displayExtens,
OidParser &parser)
{
void *preEncode = CSSM_MALLOC(extenTest.extenSize);
memset(preEncode, 0, extenTest.extenSize);
extenTest.createFcn(preEncode);
CSSM_X509_EXTENSION extnPre;
extnPre.extnId = extenTest.extenOid;
extnPre.critical = randBool();
extnPre.format = CSSM_X509_DATAFORMAT_PARSED;
extnPre.value.parsedValue = preEncode;
extnPre.BERvalue.Data = NULL;
extnPre.BERvalue.Length = 0;
CSSM_DATA_PTR rawCert = CB_MakeCertTemplate(clHand,
0x12345678, dummyName,
dummyName,
notBefore,
notAfter,
&subjPubKey,
SIG_ALG,
NULL, NULL, &extnPre, 1); if(rawCert == NULL) {
printf("Error generating template; aborting.\n");
printExten(extnPre, true, parser);
return 1;
}
CSSM_DATA signedCert = {0, NULL};
CSSM_CC_HANDLE sigHand;
CSSM_RETURN crtn = CSSM_CSP_CreateSignatureContext(cspHand,
SIG_ALG,
NULL, &subjPrivKey,
&sigHand);
if(crtn) {
printError("CreateSignatureContext", crtn);
return 1;
}
crtn = CSSM_CL_CertSign(clHand,
sigHand,
rawCert, NULL, 0, &signedCert);
if(crtn) {
printError("CSSM_CL_CertSign", crtn);
printExten(extnPre, true, parser);
return 1;
}
CSSM_DeleteContext(sigHand);
if(writeBlobs) {
char fileName[200];
if(constFileName) {
strcpy(fileName, constFileName);
}
else {
sprintf(fileName, "%scert.der", extenTest.extenDescr);
}
writeFile(fileName, signedCert.Data, signedCert.Length);
printf("...wrote %lu bytes to %s\n", signedCert.Length, fileName);
}
CSSM_DATA_PTR postField;
CSSM_HANDLE resultHand;
uint32 numFields;
crtn = CSSM_CL_CertGetFirstFieldValue(clHand,
&signedCert,
&extenTest.extenOid,
&resultHand,
&numFields,
&postField);
if(crtn) {
printf("****Extension field not found on decode for %s\n",
extenTest.extenDescr);
printError("CSSM_CL_CertGetFirstFieldValue", crtn);
printExten(extnPre, true, parser);
return 1;
}
if(numFields != 1) {
printf("****GetFirstFieldValue: expect 1 value, got %u\n",
(unsigned)numFields);
return 1;
}
CSSM_CL_CertAbortQuery(clHand, resultHand);
CSSM_X509_EXTENSION *extnPost = (CSSM_X509_EXTENSION *)postField->Data;
if((extnPost == NULL) ||
(postField->Length != sizeof(CSSM_X509_EXTENSION))) {
printf("***Malformed CSSM_X509_EXTENSION (1) after decode\n");
return 1;
}
int rtn = 0;
rtn += compBool(extnPre.critical, extnPost->critical,
"CSSM_X509_EXTENSION.critical");
rtn += compCssmData(extnPre.extnId, extnPost->extnId,
"CSSM_X509_EXTENSION.extnId");
if(extnPost->format != CSSM_X509_DATAFORMAT_PARSED) {
printf("***Expected CSSM_X509_DATAFORMAT_PARSED (%x(x), got %x(x)\n",
CSSM_X509_DATAFORMAT_PARSED, extnPost->format);
}
if(extnPost->value.parsedValue == NULL) {
printf("***no parsedValue pointer!\n");
return 1;
}
rtn += extenTest.compareFcn(preEncode, extnPost->value.parsedValue);
if(rtn) {
printExten(extnPre, true, parser);
}
if(displayExtens || rtn) {
printExten(*extnPost, false, parser);
}
if(extenTest.freeFcn) {
extenTest.freeFcn(preEncode);
}
CSSM_CL_FreeFieldValue(clHand, &extenTest.extenOid, postField);
CSSM_FREE(rawCert->Data);
CSSM_FREE(rawCert);
CSSM_FREE(signedCert.Data);
CSSM_FREE(preEncode);
return rtn;
}
static void doPause(bool pause)
{
if(!pause) {
return;
}
fpurge(stdin);
printf("CR to continue ");
getchar();
}
int main(int argc, char **argv)
{
CSSM_CL_HANDLE clHand;
CSSM_CSP_HANDLE cspHand;
CSSM_RETURN crtn;
int arg;
int rtn;
OidParser parser;
char *argp;
unsigned i;
unsigned minExtenNum = 0;
unsigned maxExtenNum = NUM_EXTEN_TESTS-1;
bool writeBlobs = false;
bool displayExtens = false;
bool quiet = false;
unsigned loops = LOOPS_DEF;
bool pauseLoop = false;
bool pauseCert = false;
const char *constFileName = NULL;
for(arg=1; arg<argc; arg++) {
argp = argv[arg];
switch(argp[0]) {
case 'w':
writeBlobs = true;
break;
case 'd':
displayExtens = true;
break;
case 'q':
quiet = true;
break;
case 'p':
pauseLoop = true;
break;
case 'P':
pauseCert = true;
break;
case 'l':
loops = atoi(&argp[2]);
break;
case 'f':
constFileName = &argp[2];
break;
case 'e':
if(argp[1] != '=') {
usage(argv);
}
for(i=0; i<NUM_EXTEN_TESTS; i++) {
if(extenTests[i].extenLetter == argp[2]) {
minExtenNum = maxExtenNum = i;
break;
}
}
if(i == NUM_EXTEN_TESTS) {
usage(argv);
}
break;
default:
usage(argv);
}
}
clHand = clStartup();
if(clHand == 0) {
return 0;
}
cspHand = cspStartup();
if(cspHand == 0) {
return 0;
}
printf("Starting extenTest; args: ");
for(i=1; i<(unsigned)argc; i++) {
printf("%s ", argv[i]);
}
printf("\n");
crtn = cspGenKeyPair(cspHand,
KEY_ALG,
SUBJ_KEY_LABEL,
strlen(SUBJ_KEY_LABEL),
KEY_SIZE_BITS,
&subjPubKey,
CSSM_FALSE, CSSM_KEYUSE_VERIFY,
CSSM_KEYBLOB_RAW_FORMAT_NONE,
&subjPrivKey,
CSSM_TRUE, CSSM_KEYUSE_SIGN,
CSSM_KEYBLOB_RAW_FORMAT_NONE,
CSSM_FALSE);
if(crtn) {
exit(1);
}
dummyName = CB_BuildX509Name(dummyRdn, NUM_DUMMY_NAMES);
if(dummyName == NULL) {
printf("CB_BuildX509Name failure");
exit(1);
}
notBefore = CB_BuildX509Time(0);
notAfter = CB_BuildX509Time(10000);
for(unsigned loop=0; loop<loops; loop++) {
if(!quiet) {
printf("...loop %u\n", loop);
}
for(unsigned extenDex=minExtenNum; extenDex<=maxExtenNum; extenDex++) {
rtn = doTest(clHand, cspHand, extenTests[extenDex],
writeBlobs, constFileName, displayExtens, parser);
if(rtn) {
break;
}
doPause(pauseCert);
}
if(rtn) {
break;
}
doPause(pauseLoop);
}
if(rtn) {
printf("***%s FAILED\n", argv[0]);
}
else if(!quiet) {
printf("...%s passed\n", argv[0]);
}
CB_FreeX509Name(dummyName);
CB_FreeX509Time(notBefore);
CB_FreeX509Time(notAfter);
CSSM_ModuleDetach(cspHand);
CSSM_ModuleDetach(clHand);
return 0;
}