X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/extenTest/extenTest.cpp diff --git a/SecurityTests/clxutils/extenTest/extenTest.cpp b/SecurityTests/clxutils/extenTest/extenTest.cpp new file mode 100644 index 00000000..48f3f889 --- /dev/null +++ b/SecurityTests/clxutils/extenTest/extenTest.cpp @@ -0,0 +1,1546 @@ +/* + * extenTest - verify encoding and decoding of extensions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} + +/* dummy RDN - subject and issuer - we aren't testing this */ +CB_NameOid dummyRdn[] = +{ + { "Apple Computer", &CSSMOID_OrganizationName }, + { "Doug Mitchell", &CSSMOID_CommonName } +}; +#define NUM_DUMMY_NAMES (sizeof(dummyRdn) / sizeof(CB_NameOid)) + +/* + * Static components we reuse for each encode/decode. + */ +static CSSM_X509_NAME *dummyName; +static CSSM_X509_TIME *notBefore; // UTC-style "not before" time +static CSSM_X509_TIME *notAfter; // UTC-style "not after" time +static CSSM_KEY subjPrivKey; +static CSSM_KEY subjPubKey; + +static CSSM_BOOL randBool() +{ + unsigned r = genRand(1, 0x10000000); + return (r & 0x1) ? CSSM_TRUE : CSSM_FALSE; +} + +/* Fill a CSSM_DATA with random data. Its referent is allocd with malloc. */ +static void randData( + CSSM_DATA_PTR data, + uint8 maxLen) +{ + data->Data = (uint8 *)malloc(maxLen); + simpleGenData(data, 1, maxLen); +} + +/* + * Various compare tests + */ +int compBool( + CSSM_BOOL pre, + CSSM_BOOL post, + const char *desc) +{ + if(pre == post) { + return 0; + } + printf("***Boolean miscompare on %s\n", desc); + /* in case a CSSM_TRUE isn't exactly right... */ + 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; + + /* set two random valid bits */ + *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; + + /* set two random valid bits */ + *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 --- + +/* a static array of meaningless OIDs, use 1.. NUM_SKU_OIDS */ +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; dexnumPurposes; dex++) { + rtn += compCssmData(ekupre->purposes[dex], + ekupost->purposes[dex], "CE_ExtendedKeyUsage.purposes"); + } + return rtn; +} + + +#pragma mark --- general purpose X509 name generator --- + +/* Attr/Value pairs, pick one of NUM_ATTR_STRINGS */ +static char *attrStrings[] = { + (char *)"thisName", + (char *)"anotherName", + (char *)"someOtherName" +}; +#define NUM_ATTR_STRINGS 3 + +/* A/V type, pick one of NUM_ATTR_TYPES */ +static CSSM_OID attrTypes[] = { + CSSMOID_Surname, + CSSMOID_CountryName, + CSSMOID_OrganizationName, + CSSMOID_Description +}; +#define NUM_ATTR_TYPES 4 + +/* A/V tag, pick one of NUM_ATTR_TAGS */ +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; atvDexAttributeTypeAndValue[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; atvDexnumberOfPairs; 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; rdnDexRelativeDistinguishedName[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; rdnDexnumberOfRDNs; 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; rdnDexnumberOfRDNs; 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: + { + /* X509_NAME, the hard one */ + 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); + // one at a time + //names->numNames = 1; + names->generalName = (CE_GeneralName *)malloc(names->numNames * + sizeof(CE_GeneralName)); + memset(names->generalName, 0, names->numNames * sizeof(CE_GeneralName)); + + for(unsigned i=0; inumNames; 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++; + } + + /* nameType-specific compare */ + 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; dexnumNames; 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; dexnumNames; 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 = genRand(1,3); + // one at a time + 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; dexnumDistPoints; dex++) { + CE_CRLDistributionPoint *pt = &cdp->distPoints[dex]; + + /* all fields optional */ + if(randBool()) { + CE_DistributionPointName *dpn = pt->distPointName = + (CE_DistributionPointName *)malloc( + sizeof(CE_DistributionPointName)); + memset(dpn, 0, sizeof(CE_DistributionPointName)); + + /* CE_DistributionPointName has two flavors */ + 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); + } + } /* creating CE_DistributionPointName */ + + pt->reasonsPresent = randBool(); + if(pt->reasonsPresent) { + CE_CrlDistReasonFlags *cdr = &pt->reasons; + /* set two random valid bits */ + *cdr = 0; + *cdr |= 1 << genRand(0,7); + *cdr |= 1 << genRand(0,7); + } + + /* make sure at least one present */ + 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; dexnumDistPoints; 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; dexnumDistPoints; 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; + + /* all three fields optional */ + + 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) { + /* force at least one to be present */ + 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); // genNamesCreate mallocd + free(akid->generalNames); // we mallocd + } + if(akid->serialNumber.Data) { + free(akid->serialNumber.Data); + } +} + +#pragma mark --- CE_CertPolicies --- + +/* random OIDs, pick 1..NUM_CP_OIDS */ +static CSSM_OID cpOids[] = +{ + CSSMOID_EmailAddress, + CSSMOID_UnstructuredName, + CSSMOID_ContentType, + CSSMOID_MessageDigest +}; +#define NUM_CP_OIDS 4 + +/* CPS strings, pick one of NUM_CPS_STR */ +static char *someCPSs[] = +{ + (char *)"http://www.apple.com", + (char *)"https://cdnow.com", + (char *)"ftp:backwards.com" +}; +#define NUM_CPS_STR 3 + +/* make these looks like real sequences */ +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); + //cp->numPolicies = 1; + unsigned len = sizeof(CE_PolicyInformation) * cp->numPolicies; + cp->policies = (CE_PolicyInformation *)malloc(len); + memset(cp->policies, 0, len); + + for(unsigned polDex=0; polDexnumPolicies; 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; cpiDexpolicyQualifiers[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; polDexnumPolicies; 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; qiDexnumPolicyQualifiers; 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; polDexnumPolicies; polDex++) { + CE_PolicyInformation *pi = &cp->policies[polDex]; + free(pi->policyQualifiers); + } + free(cp->policies); +} + +#pragma mark --- CE_AuthorityInfoAccess --- + +/* random OIDs, pick 1..NUM_AI_OIDS */ +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; dexnumAccessDescriptions; 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; dexnumAccessDescriptions; 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; dexnumAccessDescriptions; dex++) { + CE_AccessDescription *ad = &aia->accessDescriptions[dex]; + genNameFree(&ad->accessLocation); + } + free(aia->accessDescriptions); +} + +#pragma mark --- CE_QC_Statements --- + +/* a static array of CE_QC_Statement.statementId */ +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 = genRand(1,3); + 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; dexqcStatements[dex]; + unsigned whichOid = genRand(0, NUM_QCS_OIDS-1); + qcs->statementId = qcsOids[whichOid]; + + /* three legal combos of (semanticsInfo, otherInfo), constrained by whichOid */ + unsigned coin = genRand(1, 2); + switch(coin) { + case 1: + /* nothing */ + break; + case 2: + { + /* + * CSSMOID_OID_QCS_SYNTAX_V2 --> semanticsInfo + * other --> otherInfo + */ + if(whichOid == WHICH_QCS_V2) { + CE_SemanticsInformation *si = (CE_SemanticsInformation *)malloc( + sizeof(CE_SemanticsInformation)); + qcs->semanticsInfo = si; + memset(si, 0, sizeof(CE_SemanticsInformation)); + + /* flip a coin; heads --> semanticsIdentifier */ + coin = genRand(1, 2); + if(coin == 2) { + si->semanticsIdentifier = (CSSM_OID *)malloc(sizeof(CSSM_OID)); + *si->semanticsIdentifier = qcsOids[0]; + } + + /* flip a coin; heads --> nameRegistrationAuthorities */ + /* also gen this one if semanticsInfo is empty */ + coin = genRand(1, 2); + if((coin == 2) || (si->semanticsIdentifier == NULL)) { + si->nameRegistrationAuthorities = (CE_NameRegistrationAuthorities *) + malloc(sizeof(CE_NameRegistrationAuthorities)); + genNamesCreate(si->nameRegistrationAuthorities); + } + } + else { + /* ASN_ANY - just take an encoded NULL */ + 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; dexqcStatements[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; dexqcStatements[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 --- + +/* + * Define one extension test. + */ + +/* + * Cook up this extension with random, reasonable values. + * Incoming pointer refers to extension-specific C struct, mallocd + * and zeroed by main test routine. + */ +typedef void (*extenCreateFcn)(void *arg); + +/* + * Compare two instances of this extension. Return number of + * compare errors. + */ +typedef unsigned (*extenCompareFcn)( + const void *preEncode, + const void *postEncode); + +/* + * Free struct components mallocd in extenCreateFcn. Do not free + * the outer struct. + */ +typedef void (*extenFreeFcn)(void *arg); + +typedef struct { + /* three extension-specific functions */ + extenCreateFcn createFcn; + extenCompareFcn compareFcn; + extenFreeFcn freeFcn; + + /* size of C struct passed to all three functions */ + unsigned extenSize; + + /* the OID for this extension */ + CSSM_OID extenOid; + + /* description for error logging and blob writing */ + const char *extenDescr; + + /* command-line letter for this one */ + char extenLetter; + +} ExtenTest; + +/* empty freeFcn means no extension-specific resources to free */ +#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, // all blobs to this file if non-NULL + bool displayExtens, + OidParser &parser) +{ + /* + * 1. Cook up a random and reasonable instance of the C struct + * associated with this extension. + */ + void *preEncode = CSSM_MALLOC(extenTest.extenSize); + memset(preEncode, 0, extenTest.extenSize); + extenTest.createFcn(preEncode); + + /* + * Cook up the associated CSSM_X509_EXTENSION. + */ + 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; + + /* encode the extension in a TBSCert */ + CSSM_DATA_PTR rawCert = CB_MakeCertTemplate(clHand, + 0x12345678, // serial number + dummyName, + dummyName, + notBefore, + notAfter, + &subjPubKey, + SIG_ALG, + NULL, // subjUniqueId + NULL, // issuerUniqueId + &extnPre, // extensions + 1); // numExtensions + if(rawCert == NULL) { + printf("Error generating template; aborting.\n"); + /* show what we tried to encode */ + printExten(extnPre, true, parser); + return 1; + } + + /* sign the cert */ + CSSM_DATA signedCert = {0, NULL}; + CSSM_CC_HANDLE sigHand; + CSSM_RETURN crtn = CSSM_CSP_CreateSignatureContext(cspHand, + SIG_ALG, + NULL, // no passphrase for now + &subjPrivKey, + &sigHand); + if(crtn) { + printError("CreateSignatureContext", crtn); + return 1; + } + + crtn = CSSM_CL_CertSign(clHand, + sigHand, + rawCert, // CertToBeSigned + NULL, // SignScope per spec + 0, // ScopeSize per spec + &signedCert); + if(crtn) { + printError("CSSM_CL_CertSign", crtn); + /* show what we tried to encode */ + 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); + } + + /* snag the same extension from the encoded cert */ + 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); + + /* show what we tried to encode and decode */ + 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); + + /* verify the fields we generated */ + 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; + } + + /* down to extension-specific compare */ + rtn += extenTest.compareFcn(preEncode, extnPost->value.parsedValue); + + if(rtn) { + /* print preencode only on error */ + printExten(extnPre, true, parser); + } + if(displayExtens || rtn) { + printExten(*extnPost, false, parser); + } + + /* free the allocated data */ + 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; + + /* user-specificied params */ + 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