X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/certLabelTest/certLabelTest.cpp diff --git a/SecurityTests/clxutils/certLabelTest/certLabelTest.cpp b/SecurityTests/clxutils/certLabelTest/certLabelTest.cpp new file mode 100644 index 00000000..e3ab6e36 --- /dev/null +++ b/SecurityTests/clxutils/certLabelTest/certLabelTest.cpp @@ -0,0 +1,426 @@ +/* + * certLabelTest.cpp - test SecCertificateInferLabel(), in particular, Radar + * 3529689 (teletex strings) and 4746055 (add Description + * in parentheses) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void usage(char **argv) +{ + printf("usage: %s [options]\n", argv[0]); + printf("Options:\n"); + printf(" -p -- pause for leaks check\n"); + printf(" -q -- quiet\n"); + /* etc. */ + exit(1); +} + +#define KEY_SIZE 1024 +#define KEY_ALG CSSM_ALGID_RSA +#define SIG_ALG CSSM_ALGID_SHA1WithRSA +#define CERT_FILE_OUT "/tmp/certLabelTest.cer" + +/* + * Here's the definitive string for Radar 3529689. + * BER tag = Teletex/T61, encoding = kCFStringEncodingISOLatin1. + * I hope Herr Petersen does not mind. + */ +static const unsigned char JurgenPetersen[] = +{ + 0x4a, 0xf8, 0x72, 0x67, 0x65, 0x6e, 0x20, 0x4e, + 0xf8, 0x72, 0x67, 0x61, 0x61, 0x72, 0x64, 0x20, + 0x50, 0x65, 0x74, 0x65, 0x72, 0x73, 0x65, 0x6e +}; + +/* + * Name/OID pair used in buildX509Name(). + * This logic is like the CB_BuildX509Name() code in clAppUtils/CertBuilderApp.cpp, + * with the addition of the berTag specification, and data is specified as a void * + * and size_t. + */ +typedef struct { + const void *nameVal; + CSSM_SIZE nameLen; + const CSSM_OID *oid; + CSSM_BER_TAG berTag; +} NameOid; + +/* + * Build up a CSSM_X509_NAME from an arbitrary list of name/OID/tag triplets. + * We do one a/v pair per RDN. + */ +static CSSM_X509_NAME *buildX509Name( + const NameOid *nameArray, + unsigned numNames) +{ + CSSM_X509_NAME *top = (CSSM_X509_NAME *)appMalloc(sizeof(CSSM_X509_NAME), 0); + if(top == NULL) { + return NULL; + } + top->numberOfRDNs = numNames; + top->RelativeDistinguishedName = + (CSSM_X509_RDN_PTR)appMalloc(sizeof(CSSM_X509_RDN) * numNames, 0); + if(top->RelativeDistinguishedName == NULL) { + return NULL; + } + CSSM_X509_RDN_PTR rdn; + const NameOid *nameOid; + unsigned nameDex; + for(nameDex=0; nameDexRelativeDistinguishedName[nameDex]; + nameOid = &nameArray[nameDex]; + rdn->numberOfPairs = 1; + rdn->AttributeTypeAndValue = (CSSM_X509_TYPE_VALUE_PAIR_PTR) + appMalloc(sizeof(CSSM_X509_TYPE_VALUE_PAIR), 0); + CSSM_X509_TYPE_VALUE_PAIR_PTR atvp = rdn->AttributeTypeAndValue; + if(atvp == NULL) { + return NULL; + } + appCopyCssmData(nameOid->oid, &atvp->type); + atvp->valueType = nameOid->berTag; + atvp->value.Length = nameOid->nameLen; + atvp->value.Data = (uint8 *)CSSM_MALLOC(nameOid->nameLen); + memmove(atvp->value.Data, nameOid->nameVal, nameOid->nameLen); + } + return top; +} + +/* just make these static and reuse them */ +static CSSM_X509_TIME *notBefore; +static CSSM_X509_TIME *notAfter; + +/* + * Core test routine. + * -- build a cert with issuer and subject as per specified name components + * -- extract inferred label + * -- compare inferred label to expected value + * -- if labelIsCommonName true, verify that SecCertificateCopyCommonName() yields + * the same string as inferred label + */ +static int doTest( + const char *testName, + bool quiet, + CSSM_CSP_HANDLE cspHand, + CSSM_CL_HANDLE clHand, + CSSM_KEY_PTR privKey, + CSSM_KEY_PTR pubKey, + + /* input names - one or two */ + const void *name1Val, + CSSM_SIZE name1Len, + CSSM_BER_TAG berTag1, + const CSSM_OID *name1Oid, + const void *name2Val, // optional + CSSM_SIZE name2Len, + CSSM_BER_TAG berTag2, + const CSSM_OID *name2Oid, + + /* expected label */ + CFStringRef expectedLabel, + bool labelIsCommonName) +{ + if(!quiet) { + printf("...%s\n", testName); + } + + /* build the subject/issuer name */ + NameOid nameArray[2] = { {name1Val, name1Len, name1Oid, berTag1 }, + {name2Val, name2Len, name2Oid, berTag2 } }; + unsigned numNames = name2Val ? 2 : 1; + + CSSM_X509_NAME *name = buildX509Name(nameArray, numNames); + if(name == NULL) { + printf("***buildX509Name screwup\n"); + return -1; + } + + /* build the cert template */ + CSSM_DATA_PTR certTemp = CB_MakeCertTemplate( + clHand, 0x123456, + name, name, + notBefore, notAfter, + pubKey, SIG_ALG, + NULL, NULL, // subject/issuer UniqueID + NULL, 0); // extensions + if(certTemp == NULL) { + printf("***CB_MakeCertTemplate screwup\n"); + 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 + privKey, + &sigHand); + if(crtn) { + /* should never happen */ + cssmPerror("CSSM_CSP_CreateSignatureContext", crtn); + return 1; + } + crtn = CSSM_CL_CertSign(clHand, + sigHand, + certTemp, // CertToBeSigned + NULL, // SignScope per spec + 0, // ScopeSize per spec + &signedCert); + if(crtn) { + cssmPerror("CSSM_CL_CertSign", crtn); + return 1; + } + CSSM_DeleteContext(sigHand); + CSSM_FREE(certTemp->Data); + CSSM_FREE(certTemp); + + /* + * OK, we have a signed cert. + * Turn it into a SecCertificateRef and get the inferred label. + */ + OSStatus ortn; + SecCertificateRef certRef; + ortn = SecCertificateCreateFromData(&signedCert, + CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, + &certRef); + if(ortn) { + cssmPerror("SecCertificateCreateFromData", ortn); + return -1; + } + CFStringRef inferredLabel; + ortn = SecCertificateInferLabel(certRef, &inferredLabel); + if(ortn) { + cssmPerror("SecCertificateCreateFromData", ortn); + return -1; + } + CFComparisonResult res = CFStringCompare(inferredLabel, expectedLabel, 0); + if(res != kCFCompareEqualTo) { + fprintf(stderr, "*** label miscompare in test '%s' ***\n", testName); + fprintf(stderr, "expected label : "); + CFShow(expectedLabel); + fprintf(stderr, "inferred label : "); + CFShow(inferredLabel); + if(writeFile(CERT_FILE_OUT, signedCert.Data, signedCert.Length)) { + fprintf(stderr, "***Error writing cert to %s\n", CERT_FILE_OUT); + } + else { + fprintf(stderr, "...write %lu bytes to %s\n", (unsigned long)signedCert.Length, + CERT_FILE_OUT); + } + return -1; + } + + if(labelIsCommonName) { + CFStringRef commonName = NULL; + ortn = SecCertificateCopyCommonName(certRef, &commonName); + if(ortn) { + cssmPerror("SecCertificateCopyCommonName", ortn); + return -1; + } + res = CFStringCompare(inferredLabel, commonName, 0); + if(res != kCFCompareEqualTo) { + printf("*** CommonName miscompare in test '%s' ***\n", testName); + printf("Common Name : '"); + CFShow(commonName); + printf("'\n"); + printf("inferred label : '"); + CFShow(inferredLabel); + printf("'\n"); + if(writeFile(CERT_FILE_OUT, signedCert.Data, signedCert.Length)) { + printf("***Error writing cert to %s\n", CERT_FILE_OUT); + } + else { + printf("...write %lu bytes to %s\n", (unsigned long)signedCert.Length, + CERT_FILE_OUT); + } + return -1; + } + CFRelease(commonName); + } + CFRelease(certRef); + CSSM_FREE(signedCert.Data); + CB_FreeX509Name(name); + CFRelease(inferredLabel); + return 0; +} + +int main(int argc, char **argv) +{ + bool quiet = false; + bool doPause = false; + + int arg; + while ((arg = getopt(argc, argv, "pqh")) != -1) { + switch (arg) { + case 'q': + quiet = true; + break; + case 'p': + doPause = true; + break; + case 'h': + usage(argv); + } + } + if(optind != argc) { + usage(argv); + } + + testStartBanner("certLabelTest", argc, argv); + + CSSM_CL_HANDLE clHand = clStartup(); + CSSM_CSP_HANDLE cspHand = cspStartup(); + + /* create a key pair */ + CSSM_RETURN crtn; + CSSM_KEY pubKey; + CSSM_KEY privKey; + + crtn = cspGenKeyPair(cspHand, KEY_ALG, + "someLabel", 8, + KEY_SIZE, + &pubKey, CSSM_FALSE, CSSM_KEYUSE_ANY, CSSM_KEYBLOB_RAW_FORMAT_NONE, + &privKey, CSSM_FALSE, CSSM_KEYUSE_ANY, CSSM_KEYBLOB_RAW_FORMAT_NONE, + CSSM_FALSE); + if(crtn) { + printf("***Error generating RSA key pair. Aborting.\n"); + exit(1); + } + + /* common params, reused for each test */ + notBefore = CB_BuildX509Time(0); + notAfter = CB_BuildX509Time(100000); + + /* + * Grind thru test cases. + */ + int ourRtn; + + /* very basic */ + ourRtn = doTest("simple ASCII common name", quiet, + cspHand, clHand, &privKey, &pubKey, + "Simple Name", strlen("Simple Name"), BER_TAG_PRINTABLE_STRING, &CSSMOID_CommonName, + NULL, 0, BER_TAG_UNKNOWN, NULL, + CFSTR("Simple Name"), true); + if(ourRtn) { + exit(1); + } + + /* test concatentation of description */ + ourRtn = doTest("ASCII common name plus ASCII description", quiet, + cspHand, clHand, &privKey, &pubKey, + "Simple Name", strlen("Simple Name"), BER_TAG_PRINTABLE_STRING, &CSSMOID_CommonName, + "Description", strlen("Description"), BER_TAG_PRINTABLE_STRING, &CSSMOID_Description, + CFSTR("Simple Name (Description)"), false); + if(ourRtn) { + exit(1); + } + + /* basic, specifying UTF8 (should be same as PRINTABLE) */ + ourRtn = doTest("simple UTF8 common name", quiet, + cspHand, clHand, &privKey, &pubKey, + "Simple Name", strlen("Simple Name"), BER_TAG_PKIX_UTF8_STRING, &CSSMOID_CommonName, + NULL, 0, BER_TAG_UNKNOWN, NULL, + CFSTR("Simple Name"), true); + if(ourRtn) { + exit(1); + } + + /* label from org name instead of common name */ + ourRtn = doTest("label from OrgName", quiet, + cspHand, clHand, &privKey, &pubKey, + "Simple Name", strlen("Simple Name"), BER_TAG_PRINTABLE_STRING, &CSSMOID_OrganizationName, + NULL, 0, BER_TAG_UNKNOWN, NULL, + CFSTR("Simple Name"), false); + if(ourRtn) { + exit(1); + } + + /* label from orgUnit name instead of common name */ + ourRtn = doTest("label from OrgUnit", quiet, + cspHand, clHand, &privKey, &pubKey, + "Simple Name", strlen("Simple Name"), BER_TAG_PRINTABLE_STRING, &CSSMOID_OrganizationalUnitName, + NULL, 0, BER_TAG_UNKNOWN, NULL, + CFSTR("Simple Name"), false); + if(ourRtn) { + exit(1); + } + + /* label from orgUnit name, description is ignored (it's only used if the + * label comes from CommonName) */ + ourRtn = doTest("label from OrgUnit, description is ignored", quiet, + cspHand, clHand, &privKey, &pubKey, + "Simple Name", strlen("Simple Name"), BER_TAG_PRINTABLE_STRING, &CSSMOID_OrganizationalUnitName, + "Description", strlen("Description"), BER_TAG_PRINTABLE_STRING, &CSSMOID_Description, + CFSTR("Simple Name"), false); + if(ourRtn) { + exit(1); + } + + /* Radar 3529689: T61/Teletex, ISOLatin encoding, commonName only */ + CFStringRef t61Str = CFStringCreateWithBytes(NULL, JurgenPetersen, sizeof(JurgenPetersen), + kCFStringEncodingISOLatin1, true); + ourRtn = doTest("T61/Teletex name from Radar 3529689", quiet, + cspHand, clHand, &privKey, &pubKey, + JurgenPetersen, sizeof(JurgenPetersen), BER_TAG_TELETEX_STRING, &CSSMOID_CommonName, + NULL, 0, BER_TAG_UNKNOWN, NULL, + t61Str, true); + if(ourRtn) { + exit(1); + } + + /* Now convert that ISOLatin into Unicode and try with that */ + CFDataRef unicodeStr = CFStringCreateExternalRepresentation(NULL, t61Str, + kCFStringEncodingUnicode, 0); + if(unicodeStr == NULL) { + printf("***Error converting to Unicode\n"); + exit(1); + } + ourRtn = doTest("Unicode CommonName", quiet, + cspHand, clHand, &privKey, &pubKey, + CFDataGetBytePtr(unicodeStr), CFDataGetLength(unicodeStr), + BER_TAG_PKIX_BMP_STRING, &CSSMOID_CommonName, + NULL, 0, BER_TAG_UNKNOWN, NULL, + t61Str, true); + if(ourRtn) { + exit(1); + } + CFRelease(unicodeStr); + + /* Mix up ISOLatin Common Name and ASCII Description to ensure that the encodings + * of the two components are handled separately */ + CFMutableStringRef combo = CFStringCreateMutable(NULL, 0); + CFStringAppend(combo, t61Str); + CFStringAppendCString(combo, " (Description)", kCFStringEncodingASCII); + ourRtn = doTest("ISOLatin Common Name and ASCII Description", quiet, + cspHand, clHand, &privKey, &pubKey, + JurgenPetersen, sizeof(JurgenPetersen), BER_TAG_TELETEX_STRING, &CSSMOID_CommonName, + "Description", strlen("Description"), BER_TAG_PRINTABLE_STRING, &CSSMOID_Description, + combo, false); + if(ourRtn) { + exit(1); + } + CFRelease(combo); + CFRelease(t61Str); + + if(doPause) { + fpurge(stdin); + printf("Pausing for leaks testing; CR to continue: "); + getchar(); + } + if(!quiet) { + printf("...success\n"); + } + return 0; +}