#include <stdio.h>
#include <string.h>
#include <Security/cssm.h>
#include <Security/x509defs.h>
#include <Security/oidsattr.h>
#include <Security/oidscert.h>
#include <Security/certextensions.h>
#include <Security/SecTrust.h>
#include <Security/SecTrustSettingsPriv.h>
#include <security_cdsa_utils/cuOidParser.h>
#include <security_cdsa_utils/cuPrintCert.h>
#include <utilLib/common.h>
#include <utilLib/cspwrap.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <clAppUtils/clutils.h>
#include <clAppUtils/certVerify.h>
#include <clAppUtils/tpUtils.h>
#include <Security/SecAsn1Coder.h>
#include <Security/X509Templates.h>
#define ENC_TBS_BLOB "encodedTbs.der"
#define DEC_TBS_BLOB "decodedTbs.der"
static void usage(char **argv)
{
printf("Usage: %s [options]\n", argv[0]);
printf("Options:\n");
printf(" w -- writeBlobs\n");
printf(" e -- allow expired roots\n");
printf(" t -- use Trust Settings\n");
printf(" q -- quiet\n");
printf(" v -- verbose\n");
exit(1);
}
static const uint8 anchor_46_derIssuer_bytes[] = {
0x30, 0x81, 0x9b, 0x31, 0x0b, 0x30, 0x09, 0x06,
0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x48, 0x55,
0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
0x07, 0x13, 0x08, 0x42, 0x75, 0x64, 0x61, 0x70,
0x65, 0x73, 0x74, 0x31, 0x27, 0x30, 0x25, 0x06,
0x03, 0x55, 0x04, 0x0a, 0x13, 0x1e, 0x4e, 0x65,
0x74, 0x4c, 0x6f, 0x63, 0x6b, 0x20, 0x48, 0x61,
0x6c, 0x6f, 0x7a, 0x61, 0x74, 0x62, 0x69, 0x7a,
0x74, 0x6f, 0x6e, 0x73, 0x61, 0x67, 0x69, 0x20,
0x4b, 0x66, 0x74, 0x2e, 0x31, 0x1a, 0x30, 0x18,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x11, 0x54,
0x61, 0x6e, 0x75, 0x73, 0x69, 0x74, 0x76, 0x61,
0x6e, 0x79, 0x6b, 0x69, 0x61, 0x64, 0x6f, 0x6b,
0x31, 0x34, 0x30, 0x32, 0x06, 0x03, 0x55, 0x04,
0x03, 0x13, 0x2b, 0x4e, 0x65, 0x74, 0x4c, 0x6f,
0x63, 0x6b, 0x20, 0x45, 0x78, 0x70, 0x72, 0x65,
0x73, 0x73, 0x7a, 0x20, 0x28, 0x43, 0x6c, 0x61,
0x73, 0x73, 0x20, 0x43, 0x29, 0x20, 0x54, 0x61,
0x6e, 0x75, 0x73, 0x69, 0x74, 0x76, 0x61, 0x6e,
0x79, 0x6b, 0x69, 0x61, 0x64, 0x6f
};
static const CSSM_DATA anchor_46_derIssuer = { 158, (uint8 *)anchor_46_derIssuer_bytes };
static const uint8 anchor_53_derIssuer_bytes[] = {
0x30, 0x81, 0xaf, 0x31, 0x0b, 0x30, 0x09, 0x06,
0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x48, 0x55,
0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04,
0x08, 0x13, 0x07, 0x48, 0x75, 0x6e, 0x67, 0x61,
0x72, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03,
0x55, 0x04, 0x07, 0x13, 0x08, 0x42, 0x75, 0x64,
0x61, 0x70, 0x65, 0x73, 0x74, 0x31, 0x27, 0x30,
0x25, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1e,
0x4e, 0x65, 0x74, 0x4c, 0x6f, 0x63, 0x6b, 0x20,
0x48, 0x61, 0x6c, 0x6f, 0x7a, 0x61, 0x74, 0x62,
0x69, 0x7a, 0x74, 0x6f, 0x6e, 0x73, 0x61, 0x67,
0x69, 0x20, 0x4b, 0x66, 0x74, 0x2e, 0x31, 0x1a,
0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
0x11, 0x54, 0x61, 0x6e, 0x75, 0x73, 0x69, 0x74,
0x76, 0x61, 0x6e, 0x79, 0x6b, 0x69, 0x61, 0x64,
0x6f, 0x6b, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03,
0x55, 0x04, 0x03, 0x13, 0x2d, 0x4e, 0x65, 0x74,
0x4c, 0x6f, 0x63, 0x6b, 0x20, 0x4b, 0x6f, 0x7a,
0x6a, 0x65, 0x67, 0x79, 0x7a, 0x6f, 0x69, 0x20,
0x28, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x41,
0x29, 0x20, 0x54, 0x61, 0x6e, 0x75, 0x73, 0x69,
0x74, 0x76, 0x61, 0x6e, 0x79, 0x6b, 0x69, 0x61,
0x64, 0x6f
};
static const CSSM_DATA anchor_53_derIssuer = { 178, (uint8 *)anchor_53_derIssuer_bytes };
static const uint8 anchor_60_derIssuer_bytes[] = {
0x30, 0x81, 0x99, 0x31, 0x0b, 0x30, 0x09, 0x06,
0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x48, 0x55,
0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
0x07, 0x13, 0x08, 0x42, 0x75, 0x64, 0x61, 0x70,
0x65, 0x73, 0x74, 0x31, 0x27, 0x30, 0x25, 0x06,
0x03, 0x55, 0x04, 0x0a, 0x13, 0x1e, 0x4e, 0x65,
0x74, 0x4c, 0x6f, 0x63, 0x6b, 0x20, 0x48, 0x61,
0x6c, 0x6f, 0x7a, 0x61, 0x74, 0x62, 0x69, 0x7a,
0x74, 0x6f, 0x6e, 0x73, 0x61, 0x67, 0x69, 0x20,
0x4b, 0x66, 0x74, 0x2e, 0x31, 0x1a, 0x30, 0x18,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x11, 0x54,
0x61, 0x6e, 0x75, 0x73, 0x69, 0x74, 0x76, 0x61,
0x6e, 0x79, 0x6b, 0x69, 0x61, 0x64, 0x6f, 0x6b,
0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04,
0x03, 0x13, 0x29, 0x4e, 0x65, 0x74, 0x4c, 0x6f,
0x63, 0x6b, 0x20, 0x55, 0x7a, 0x6c, 0x65, 0x74,
0x69, 0x20, 0x28, 0x43, 0x6c, 0x61, 0x73, 0x73,
0x20, 0x42, 0x29, 0x20, 0x54, 0x61, 0x6e, 0x75,
0x73, 0x69, 0x74, 0x76, 0x61, 0x6e, 0x79, 0x6b,
0x69, 0x61, 0x64, 0x6f
};
static const CSSM_DATA anchor_60_derIssuer = { 156, (uint8 *)anchor_60_derIssuer_bytes };
static const uint8 turk1_derIssuer_bytes[] = {
0x30, 0x81, 0xb7, 0x31, 0x3f, 0x30, 0x3d, 0x06,
0x03, 0x55, 0x04, 0x03, 0x0c, 0x36, 0x54, 0xc3,
0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54,
0x20, 0x45, 0x6c, 0x65, 0x6b, 0x74, 0x72, 0x6f,
0x6e, 0x69, 0x6b, 0x20, 0x53, 0x65, 0x72, 0x74,
0x69, 0x66, 0x69, 0x6b, 0x61, 0x20, 0x48, 0x69,
0x7a, 0x6d, 0x65, 0x74, 0x20, 0x53, 0x61, 0xc4,
0x9f, 0x6c, 0x61, 0x79, 0xc4, 0xb1, 0x63, 0xc4,
0xb1, 0x73, 0xc4, 0xb1, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x0c, 0x02, 0x54,
0x52, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55,
0x04, 0x07, 0x0c, 0x06, 0x41, 0x4e, 0x4b, 0x41,
0x52, 0x41, 0x31, 0x56, 0x30, 0x54, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x0c, 0x4d, 0x28, 0x63, 0x29,
0x20, 0x32, 0x30, 0x30, 0x35, 0x20, 0x54, 0xc3,
0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54,
0x20, 0x42, 0x69, 0x6c, 0x67, 0x69, 0x20, 0xc4,
0xb0, 0x6c, 0x65, 0x74, 0x69, 0xc5, 0x9f, 0x69,
0x6d, 0x20, 0x76, 0x65, 0x20, 0x42, 0x69, 0x6c,
0x69, 0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x47, 0xc3,
0xbc, 0x76, 0x65, 0x6e, 0x6c, 0x69, 0xc4, 0x9f,
0x69, 0x20, 0x48, 0x69, 0x7a, 0x6d, 0x65, 0x74,
0x6c, 0x65, 0x72, 0x69, 0x20, 0x41, 0x2e, 0xc5,
0x9e, 0x2e
};
static const CSSM_DATA turk1_derIssuer = { 186, (uint8 *)turk1_derIssuer_bytes };
static const uint8 turk2_derIssuer_bytes[] = {
0x30, 0x81, 0xbe, 0x31, 0x3f, 0x30, 0x3d, 0x06,
0x03, 0x55, 0x04, 0x03, 0x0c, 0x36, 0x54, 0xc3,
0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54,
0x20, 0x45, 0x6c, 0x65, 0x6b, 0x74, 0x72, 0x6f,
0x6e, 0x69, 0x6b, 0x20, 0x53, 0x65, 0x72, 0x74,
0x69, 0x66, 0x69, 0x6b, 0x61, 0x20, 0x48, 0x69,
0x7a, 0x6d, 0x65, 0x74, 0x20, 0x53, 0x61, 0xc4,
0x9f, 0x6c, 0x61, 0x79, 0xc4, 0xb1, 0x63, 0xc4,
0xb1, 0x73, 0xc4, 0xb1, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x54,
0x52, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55,
0x04, 0x07, 0x0c, 0x06, 0x41, 0x6e, 0x6b, 0x61,
0x72, 0x61, 0x31, 0x5d, 0x30, 0x5b, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x0c, 0x54, 0x54, 0xc3, 0x9c,
0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20,
0x42, 0x69, 0x6c, 0x67, 0x69, 0x20, 0xc4, 0xb0,
0x6c, 0x65, 0x74, 0x69, 0xc5, 0x9f, 0x69, 0x6d,
0x20, 0x76, 0x65, 0x20, 0x42, 0x69, 0x6c, 0x69,
0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x47, 0xc3, 0xbc,
0x76, 0x65, 0x6e, 0x6c, 0x69, 0xc4, 0x9f, 0x69,
0x20, 0x48, 0x69, 0x7a, 0x6d, 0x65, 0x74, 0x6c,
0x65, 0x72, 0x69, 0x20, 0x41, 0x2e, 0xc5, 0x9e,
0x2e, 0x20, 0x28, 0x63, 0x29, 0x20, 0x4b, 0x61,
0x73, 0xc4, 0xb1, 0x6d, 0x20, 0x32, 0x30, 0x30,
0x35
};
static const CSSM_DATA turk2_derIssuer = { 193, (uint8 *)turk2_derIssuer_bytes };
static const uint8 turk3_derIssuer_bytes[] = {
0x30, 0x81, 0xbf, 0x31, 0x3f, 0x30, 0x3d, 0x06,
0x03, 0x55, 0x04, 0x03, 0x0c, 0x36, 0x54, 0xc3,
0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54,
0x20, 0x45, 0x6c, 0x65, 0x6b, 0x74, 0x72, 0x6f,
0x6e, 0x69, 0x6b, 0x20, 0x53, 0x65, 0x72, 0x74,
0x69, 0x66, 0x69, 0x6b, 0x61, 0x20, 0x48, 0x69,
0x7a, 0x6d, 0x65, 0x74, 0x20, 0x53, 0x61, 0xc4,
0x9f, 0x6c, 0x61, 0x79, 0xc4, 0xb1, 0x63, 0xc4,
0xb1, 0x73, 0xc4, 0xb1, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x54,
0x52, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55,
0x04, 0x07, 0x0c, 0x06, 0x41, 0x6e, 0x6b, 0x61,
0x72, 0x61, 0x31, 0x5e, 0x30, 0x5c, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x0c, 0x55, 0x54, 0xc3, 0x9c,
0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20,
0x42, 0x69, 0x6c, 0x67, 0x69, 0x20, 0xc4, 0xb0,
0x6c, 0x65, 0x74, 0x69, 0xc5, 0x9f, 0x69, 0x6d,
0x20, 0x76, 0x65, 0x20, 0x42, 0x69, 0x6c, 0x69,
0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x47, 0xc3, 0xbc,
0x76, 0x65, 0x6e, 0x6c, 0x69, 0xc4, 0x9f, 0x69,
0x20, 0x48, 0x69, 0x7a, 0x6d, 0x65, 0x74, 0x6c,
0x65, 0x72, 0x69, 0x20, 0x41, 0x2e, 0xc5, 0x9e,
0x2e, 0x20, 0x28, 0x63, 0x29, 0x20, 0x41, 0x72,
0x61, 0x6c, 0xc4, 0xb1, 0x6b, 0x20, 0x32, 0x30,
0x30, 0x37
};
static const CSSM_DATA turk3_derIssuer = { 194, (uint8 *)turk3_derIssuer_bytes };
static const uint8 globalSignRoot_derIssuer_bytes[] = {
0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31,
0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a,
0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d,
0x73, 0x61, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03,
0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f, 0x6f,
0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19,
0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67,
0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
0x41
};
static const CSSM_DATA globalSignRoot_derIssuer = { 89, (uint8 *)globalSignRoot_derIssuer_bytes };
static const uint8 swisssign_derIssuer_bytes[] = {
0x30, 0x76, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x48, 0x31,
0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a,
0x13, 0x09, 0x53, 0x77, 0x69, 0x73, 0x73, 0x53,
0x69, 0x67, 0x6e, 0x31, 0x32, 0x30, 0x30, 0x06,
0x03, 0x55, 0x04, 0x03, 0x13, 0x29, 0x53, 0x77,
0x69, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x20,
0x43, 0x41, 0x20, 0x28, 0x52, 0x53, 0x41, 0x20,
0x49, 0x4b, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x36,
0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x31, 0x38,
0x3a, 0x30, 0x30, 0x3a, 0x35, 0x38, 0x29, 0x31,
0x1f, 0x30, 0x1d, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10,
0x63, 0x61, 0x40, 0x53, 0x77, 0x69, 0x73, 0x73,
0x53, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d
};
static const CSSM_DATA swisssign_derIssuer = { 120, (uint8 *)swisssign_derIssuer_bytes };
class FieldArray {
public:
FieldArray(
CSSM_FIELD *fields,
uint32 numFields,
CSSM_CL_HANDLE clHand);
FieldArray(
uint32 size);
~FieldArray();
void appendField(CSSM_FIELD &field);
CSSM_FIELD &fieldAt(uint32 index);
int fieldForOid(
const CSSM_OID &oid,
unsigned n, CSSM_FIELD *&found);
CSSM_FIELD *mFields;
uint32 mNumFields; uint32 mMallocdSize; CSSM_CL_HANDLE mClHand;
};
FieldArray::FieldArray(
CSSM_FIELD *fields,
uint32 numFields,
CSSM_CL_HANDLE clHand)
{
mFields = fields;
mNumFields = numFields;
mMallocdSize = 0;
mClHand = clHand;
}
FieldArray::FieldArray(
uint32 size)
{
unsigned len = sizeof(CSSM_FIELD) * size;
mFields = (CSSM_FIELD_PTR)malloc(len);
memset(mFields, 0, len);
mNumFields = 0;
mMallocdSize = size;
mClHand = 0;
}
FieldArray::~FieldArray()
{
if(mMallocdSize != 0) {
free(mFields);
}
else {
CSSM_RETURN crtn = CSSM_CL_FreeFields(mClHand,
mNumFields, &mFields);
if(crtn) {
printError("CSSM_CL_FreeFields", crtn);
}
}
mFields = NULL;
mNumFields = 0;
mMallocdSize = 0;
}
void FieldArray::appendField(
CSSM_FIELD &field)
{
if(mMallocdSize == 0) {
printf("***Attempt to append to a read-only FieldArray\n");
exit(1);
}
if(mNumFields >= mMallocdSize) {
printf("***Attempt to append past present size of FieldArray\n");
exit(1);
}
mFields[mNumFields] = field;
mNumFields++;
}
CSSM_FIELD &FieldArray::fieldAt(
uint32 index)
{
if(index >= mNumFields) {
printf("***Attempt to access past present size of FieldArray\n");
exit(1);
}
return mFields[index];
}
int FieldArray::fieldForOid(
const CSSM_OID &oid,
unsigned n, CSSM_FIELD *&found) {
unsigned foundDex = 0;
for(unsigned dex=0; dex<mNumFields; dex++) {
CSSM_FIELD &field = mFields[dex];
if(appCompareCssmData(&field.FieldOid, &oid)) {
if(foundDex == n) {
found = &field;
return 0;
}
foundDex++;
}
}
printf("FieldArray::fieldForOid field not found\n");
return 1;
}
static unsigned nssArraySize(
const void **array)
{
unsigned count = 0;
if (array) {
while (*array++) {
count++;
}
}
return count;
}
static void doPrintCert(
const CSSM_DATA &cert)
{
printCert(cert.Data, cert.Length, CSSM_TRUE);
}
#define USE_SKIPPED_EXTENS 1
#if USE_SKIPPED_EXTENS
static const CSSM_OID *skippedExtens[] = { &CSSMOID_PolicyMappings,
&CSSMOID_PolicyConstraints
};
#define NUM_SKIPPED_EXTENS \
(sizeof(skippedExtens) / sizeof(skippedExtens[0]))
#endif
static const CSSM_DATA *skippedCerts[] = {
&anchor_46_derIssuer,
&anchor_53_derIssuer,
&anchor_60_derIssuer,
&turk1_derIssuer,
&turk2_derIssuer,
&turk3_derIssuer,
&globalSignRoot_derIssuer,
&swisssign_derIssuer
};
#define NUM_SKIPPED_CERTS (sizeof(skippedCerts) / sizeof(skippedCerts[0]))
static bool skipThisCert(
const NSS_TBSCertificate &tbs)
{
unsigned dex;
#if USE_SKIPPED_EXTENS
unsigned numExtens = nssArraySize((const void **)tbs.extensions);
for(dex=0; dex<numExtens; dex++) {
NSS_CertExtension *exten = tbs.extensions[dex];
CSSM_OID *oid = &exten->extnId;
for(unsigned skipDex=0; skipDex<NUM_SKIPPED_EXTENS; skipDex++) {
if(appCompareCssmData(skippedExtens[skipDex], oid)) {
return true;
}
}
}
#endif
for(dex=0; dex<NUM_SKIPPED_CERTS; dex++) {
if(appCompareCssmData(skippedCerts[dex], &tbs.derIssuer)) {
return true;
}
}
return false;
}
typedef enum {
FT_Unknown, FT_Normal, FT_ReadOnly, FT_NotTBS, FT_ExtenParsed, FT_ExtenUnknown } FieldType;
typedef struct {
const CSSM_OID *oid;
FieldType type;
} FieldOidType;
FieldOidType knownFields[] = {
{ &CSSMOID_X509V1Version, FT_Normal },
{ &CSSMOID_X509V1SerialNumber, FT_Normal },
{ &CSSMOID_X509V1IssuerNameCStruct, FT_Normal },
{ &CSSMOID_X509V1SubjectNameCStruct, FT_Normal },
{ &CSSMOID_X509V1SignatureAlgorithmTBS, FT_Normal },
{ &CSSMOID_X509V1SignatureAlgorithm, FT_NotTBS },
{ &CSSMOID_X509V1ValidityNotBefore, FT_Normal },
{ &CSSMOID_X509V1ValidityNotAfter, FT_Normal },
{ &CSSMOID_X509V1CertificateIssuerUniqueId, FT_Normal },
{ &CSSMOID_X509V1CertificateSubjectUniqueId, FT_Normal },
{ &CSSMOID_X509V1SubjectPublicKeyCStruct, FT_Normal },
{ &CSSMOID_CSSMKeyStruct, FT_ReadOnly },
{ &CSSMOID_X509V1Signature, FT_NotTBS },
{ &CSSMOID_X509V1IssuerName, FT_ReadOnly }, { &CSSMOID_X509V1SubjectName, FT_ReadOnly }, { &CSSMOID_X509V1IssuerNameStd, FT_ReadOnly }, { &CSSMOID_X509V1SubjectNameStd,FT_ReadOnly },
{ &CSSMOID_KeyUsage, FT_ExtenParsed },
{ &CSSMOID_BasicConstraints, FT_ExtenParsed },
{ &CSSMOID_ExtendedKeyUsage, FT_ExtenParsed } ,
{ &CSSMOID_SubjectKeyIdentifier, FT_ExtenParsed } ,
{ &CSSMOID_AuthorityKeyIdentifier, FT_ExtenParsed } ,
{ &CSSMOID_SubjectAltName, FT_ExtenParsed } ,
{ &CSSMOID_IssuerAltName, FT_ExtenParsed } ,
{ &CSSMOID_CertificatePolicies, FT_ExtenParsed } ,
{ &CSSMOID_NetscapeCertType, FT_ExtenParsed } ,
{ &CSSMOID_CrlDistributionPoints, FT_ExtenParsed },
{ &CSSMOID_AuthorityInfoAccess, FT_ExtenParsed },
{ &CSSMOID_SubjectInfoAccess, FT_ExtenParsed },
{ &CSSMOID_X509V3CertificateExtensionCStruct, FT_ExtenUnknown },
{ &CSSMOID_QC_Statements, FT_ExtenParsed },
{ &CSSMOID_NameConstraints, FT_ExtenParsed },
{ &CSSMOID_PolicyMappings, FT_ExtenParsed },
{ &CSSMOID_PolicyConstraints, FT_ExtenParsed },
};
#define NUM_KNOWN_FIELDS (sizeof(knownFields) / sizeof(knownFields[0]))
static FieldType typeForOid(
const CSSM_OID &oid)
{
for(unsigned dex=0; dex<NUM_KNOWN_FIELDS; dex++) {
FieldOidType &ft = knownFields[dex];
if(appCompareCssmData(&oid, ft.oid)) {
return ft.type;
}
}
return FT_Unknown;
}
static const char *fieldTypeStr(
FieldType type)
{
switch(type) {
case FT_Unknown: return "FT_Unknown";
case FT_Normal: return "FT_Normal";
case FT_ReadOnly: return "FT_ReadOnly";
case FT_NotTBS: return "FT_NotTBS";
case FT_ExtenParsed: return "FT_ExtenParsed";
case FT_ExtenUnknown: return "FT_ExtenUnknown";
default:
printf("***BRRZAP!\n");
exit(1);
}
}
static const uint8 emptyAuthKeyId[2] = {0x30, 0};
static int vfyExtens(
FieldArray &extenFields,
const CSSM_DATA &cert, CSSM_BOOL quiet)
{
for(unsigned dex=0; dex<extenFields.mNumFields; dex++) {
CSSM_FIELD &extenField = extenFields.fieldAt(dex);
FieldType type = typeForOid(extenField.FieldOid);
FieldType expectType;
CSSM_DATA &fieldValue = extenField.FieldValue;
CSSM_X509_EXTENSION *exten = (CSSM_X509_EXTENSION *)fieldValue.Data;
if((exten == NULL) ||
(fieldValue.Length != sizeof(CSSM_X509_EXTENSION))) {
doPrintCert(cert);
printf("***Malformed CSSM_X509_EXTENSION\n");
if(testError(quiet)) {
return 1;
}
continue;
}
if((exten->BERvalue.Data == NULL) ||
(exten->value.parsedValue == NULL)) {
printf("***Malformed CSSM_X509_EXTENSION (1)\n");
return 1;
}
switch(exten->format) {
case CSSM_X509_DATAFORMAT_ENCODED:
if(type != FT_ExtenUnknown) {
doPrintCert(cert);
printf("***Entension format ENCODED, expected PARSED\n");
if(testError(quiet)) {
return 1;
}
}
if(appCompareCssmData(&exten->extnId, &CSSMOID_PolicyConstraints)) {
printf("...skipping policyConstraints extension per <rdar://8265523> (fix me!)\n");
break;
}
if(appCompareCssmData(&exten->extnId, &CSSMOID_PolicyMappings)) {
printf("...skipping policyMappings extension per <rdar://8265523> (fix me!)\n");
break;
}
expectType = typeForOid(exten->extnId);
if(expectType != FT_Unknown) {
if(appCompareCssmData(&exten->extnId, &CSSMOID_AuthorityKeyIdentifier) &&
(exten->BERvalue.Length == 2) &&
!memcmp(emptyAuthKeyId, exten->BERvalue.Data, 2)) {
printf("...skipping bogus swisssign AuthorityKeyId\n");
break;
}
doPrintCert(cert);
printf("***underlying exten type %s, expect Unknown\n",
fieldTypeStr(expectType));
if(testError(quiet)) {
return 1;
}
}
break;
case CSSM_X509_DATAFORMAT_PARSED:
if(type != FT_ExtenParsed) {
doPrintCert(cert);
printf("***Entension format PARSED, expected ENCODED\n");
if(testError(quiet)) {
return 1;
}
}
if(exten->value.parsedValue == NULL) {
doPrintCert(cert);
printf("***Parsed extension with NULL parsedValue\n");
if(testError(quiet)) {
return 1;
}
}
break;
default:
doPrintCert(cert);
printf("***Unknown Entension format %u\n",
exten->format);
if(testError(quiet)) {
return 1;
}
break;
}
}
return 0;
}
static int buildTbs(
CSSM_CL_HANDLE clHand,
const CSSM_DATA &rawCert,
FieldArray &allFields, FieldArray &extenFields, CSSM_BOOL quiet,
CSSM_BOOL verbose,
CSSM_BOOL writeBlobs)
{
SecAsn1CoderRef coder;
OSStatus ortn = SecAsn1CoderCreate(&coder);
if(ortn) {
cssmPerror("SecAsn1CoderCreate", ortn);
return testError(quiet);
}
NSS_SignedCertOrCRL signedCert; memset(&signedCert, 0, sizeof(signedCert));
if(SecAsn1DecodeData(coder, &rawCert, kSecAsn1SignedCertOrCRLTemplate,
&signedCert)) {
doPrintCert(rawCert);
printf("***Error decoding cert to kSecAsn1SignedCertOrCRL\n");
return testError(quiet);
}
NSS_Certificate fullCert; memset(&fullCert, 0, sizeof(fullCert));
if(SecAsn1DecodeData(coder, &rawCert, kSecAsn1SignedCertTemplate,
&fullCert)) {
doPrintCert(rawCert);
printf("***Error decoding cert to kSecAsn1Certificate\n");
return testError(quiet);
}
NSS_TBSCertificate &tbs = fullCert.tbs;
unsigned numExtens = nssArraySize((const void **)tbs.extensions);
if(numExtens != extenFields.mNumFields) {
doPrintCert(rawCert);
printf("***NSS says %u extens, CL says %u\n", numExtens,
(unsigned)extenFields.mNumFields);
return testError(quiet);
}
if(skipThisCert(tbs)) {
if(verbose) {
printf(" ...skipping TBS blob check\n");
}
SecAsn1CoderRelease(coder);
return 0;
}
unsigned numUnknowns = 0;
for(unsigned dex=0; dex<numExtens; dex++) {
NSS_CertExtension *exten = tbs.extensions[dex];
CSSM_OID &oid = exten->extnId;
FieldType type = typeForOid(oid);
CSSM_FIELD *found = NULL;
int rtn;
switch(type) {
case FT_ExtenParsed:
rtn = extenFields.fieldForOid(oid, 0, found);
break;
case FT_Unknown:
rtn = extenFields.fieldForOid(
CSSMOID_X509V3CertificateExtensionCStruct,
numUnknowns++,
found);
break;
default:
doPrintCert(rawCert);
printf("***HEY! buildTBS was given a bogus extension!\n");
return 1;
}
if(rtn) {
doPrintCert(rawCert);
printf("***buildTBS could not find extension in CL's fields\n");
return testError(quiet);
}
allFields.appendField(*found);
}
CSSM_RETURN crtn;
CSSM_DATA clTbs = {0, NULL};
crtn = CSSM_CL_CertCreateTemplate(clHand,
allFields.mNumFields,
allFields.mFields,
&clTbs);
if(crtn) {
doPrintCert(rawCert);
printError("CSSM_CL_CertCreateTemplate", crtn);
return testError(quiet);
}
int ourRtn = 0;
if(!appCompareCssmData(&clTbs, &signedCert.tbsBlob)) {
doPrintCert(rawCert);
printf("***Encoded TBS does not match decoded TBS.\n");
if(writeBlobs) {
writeFile(ENC_TBS_BLOB, clTbs.Data, clTbs.Length);
writeFile(DEC_TBS_BLOB, signedCert.tbsBlob.Data,
signedCert.tbsBlob.Length);
printf("...wrote TBS blobs to %s and %s\n",
ENC_TBS_BLOB, DEC_TBS_BLOB);
}
ourRtn = testError(quiet);
}
CSSM_FREE(clTbs.Data);
SecAsn1CoderRelease(coder);
return ourRtn;
}
static int verifyRoot(
CSSM_TP_HANDLE tpHand,
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
const CSSM_DATA &cert,
CSSM_BOOL allowExpired,
CSSM_BOOL useTrustSettings,
CSSM_BOOL quiet)
{
BlobList blobs;
blobs.addBlob(cert, CSSM_TRUE);
int i;
const char *certStatus;
if(useTrustSettings) {
certStatus = "0:0x314";
}
else {
certStatus = "0:0x1C";
}
CSSM_BOOL expireEnable = CSSM_FALSE;
for(int dex=0; dex<2; dex++) {
i = certVerifySimple(tpHand, clHand, cspHand,
blobs, blobs, CSSM_FALSE, CSSM_TRUE, expireEnable,
CVP_Basic,
NULL, CSSM_FALSE, NULL, 0, NULL, 0, NULL, 1, &certStatus, useTrustSettings,
quiet,
CSSM_FALSE); if(i == 0) {
if(dex == 1) {
printf("...warning: expired root detected. Be aware.\n");
}
return 0;
}
if(!allowExpired) {
return i;
}
expireEnable = CSSM_TRUE;
if(useTrustSettings) {
certStatus = "0:0x315";
}
else {
certStatus = "0:0x1d";
}
}
return i;
}
static int doTest(
CSSM_CL_HANDLE clHand,
CSSM_TP_HANDLE tpHand,
CSSM_CSP_HANDLE cspHand,
const CSSM_DATA &cert,
CSSM_BOOL allowExpired,
CSSM_BOOL quiet,
CSSM_BOOL verbose,
CSSM_BOOL writeBlobs,
CSSM_BOOL useTrustSettings)
{
if(verifyRoot(tpHand, clHand, cspHand, cert, allowExpired,
useTrustSettings, quiet)) {
doPrintCert(cert);
printf("***This anchor does not self-verify!\n");
return testError(quiet);
}
CSSM_FIELD_PTR certFields;
uint32 numFields;
CSSM_RETURN crtn = CSSM_CL_CertGetAllFields(clHand, &cert, &numFields,
&certFields);
if(crtn) {
printError("CSSM_CL_CertGetAllFields", crtn);
doPrintCert(cert);
printf("***The CL can not parse this anchor!\n");
return testError(quiet);
}
FieldArray parsed(certFields, numFields, clHand);
FieldArray forCreate(numFields); FieldArray extenFields(numFields);
for(unsigned dex=0; dex<numFields; dex++) {
CSSM_FIELD &parsedField = parsed.fieldAt(dex);
FieldType type = typeForOid(parsedField.FieldOid);
switch(type) {
case FT_Normal:
forCreate.appendField(parsedField);
break;
case FT_ReadOnly:
case FT_NotTBS:
break;
case FT_ExtenParsed:
case FT_ExtenUnknown:
extenFields.appendField(parsedField);
break;
default:
doPrintCert(cert);
printf("***This anchor contains an unknown field!\n");
if(testError(quiet)) {
return 1;
}
forCreate.appendField(parsedField);
break;
}
}
if(vfyExtens(extenFields, cert, quiet)) {
return 1;
}
return buildTbs(clHand, cert, forCreate, extenFields, quiet,
verbose, writeBlobs);
}
int main(int argc, char **argv)
{
CSSM_BOOL quiet = CSSM_FALSE;
CSSM_BOOL verbose = CSSM_FALSE;
CSSM_BOOL writeBlobs = CSSM_FALSE;
CSSM_BOOL allowExpired = CSSM_FALSE;
CSSM_BOOL useTrustSettings = CSSM_FALSE;
for(int arg=1; arg<argc; arg++) {
switch(argv[arg][0]) {
case 'q':
quiet = CSSM_TRUE;
break;
case 'v':
verbose = CSSM_TRUE;
break;
case 'w':
writeBlobs = CSSM_TRUE;
break;
case 't':
useTrustSettings = CSSM_TRUE;
break;
case 'e':
allowExpired = CSSM_TRUE;
break;
default:
usage(argv);
}
}
printf("Starting anchorTest; args: ");
for(int i=1; i<argc; i++) {
printf("%s ", argv[i]);
}
printf("\n");
CFArrayRef cfAnchors;
OSStatus ortn;
CSSM_DATA *anchors;
unsigned numAnchors;
ortn = getSystemAnchors(&cfAnchors, &anchors, &numAnchors);
if(ortn) {
exit(1);
}
if(numAnchors < 50) {
printf("***Hey! I can only find %u anchors; there should be way more than that.\n",
numAnchors);
exit(1);
}
CSSM_CL_HANDLE clHand = clStartup();
CSSM_TP_HANDLE tpHand = tpStartup();
CSSM_CSP_HANDLE cspHand = cspStartup();
if((clHand == 0) || (tpHand == 0) || (cspHand == 0)) {
return 0;
}
int rtn = 0;
for(unsigned dex=0; dex<numAnchors; dex++) {
if(!quiet) {
printf("...anchor %u\n", dex);
}
rtn = doTest(clHand, tpHand, cspHand, anchors[dex], allowExpired,
quiet, verbose, writeBlobs, useTrustSettings);
if(rtn) {
break;
}
}
if(rtn == 0) {
if(!quiet) {
printf("...%s success.\n", argv[0]);
}
}
return rtn;
}