1/* Copyright (c) 1998,2003-2006,2008 Apple Inc.
2 *
3 * caVerify.cpp
4 *
5 * Verify proper detection of basicConstraints.cA
6 */
7
8#include <utilLib/common.h>
9#include <utilLib/cspwrap.h>
10#include <security_cdsa_utils/cuFileIo.h>
11#include <clAppUtils/CertBuilderApp.h>
12#include <clAppUtils/clutils.h>
13#include <clAppUtils/timeStr.h>
14#include <clAppUtils/tpUtils.h>
15#include <security_cdsa_utils/cuPrintCert.h>
16#include <stdlib.h>
17#include <stdio.h>
18#include <string.h>
19#include <Security/cssm.h>
20#include <Security/x509defs.h>
21#include <Security/oidsattr.h>
22#include <Security/oidscert.h>
23#include <Security/oidsalg.h>
24#include <Security/certextensions.h>
25#include <Security/cssmapple.h>
26#include <string.h>
27
28/* define nonzero to build on Puma */
29#define PUMA_BUILD			0
30
31/* default key and signature algorithm */
32#define SIG_ALG_DEFAULT		CSSM_ALGID_SHA1WithRSA
33#define KEY_ALG_DEFAULT		CSSM_ALGID_RSA
34
35#define NUM_CERTS_DEF		5		/* default is random from 2 to this */
36#define NUM_LOOPS_DEF		100
37
38#if		PUMA_BUILD
39extern "C" {
40	void cssmPerror(const char *how, CSSM_RETURN error);
41}
42#endif	/* PUMA_BUILD */
43
44static void usage(char **argv)
45{
46	printf("Usage: %s [options]\n", argv[0]);
47	printf("Options:\n");
48	printf("   n=numCerts (default=random from 2 to %d)\n", NUM_CERTS_DEF);
49	printf("   a=alg  where alg is s(RSA/SHA1), 5(RSA/MD5), f(FEE/MD5), "
50			"F(FEE/SHA1), e(ECDSA), E(ANSI/ECDSA), 6(ECDSA/SHA256)\n");
51	printf("   k=keySizeInBits\n");
52	printf("   c (dump certs on error)\n");
53	printf("   l=numLoops (default = %d)\n", NUM_LOOPS_DEF);
54	exit(1);
55}
56
57void showCerts(
58	const CSSM_DATA *certs,
59	unsigned numCerts)
60{
61	unsigned i;
62
63	for(i=0; i<numCerts; i++) {
64		printf("======== cert %d ========\n", i);
65		printCert(certs[i].Data, certs[i].Length, CSSM_FALSE);
66		printf("\n\n");
67	}
68}
69
70void writeCerts(
71	const CSSM_DATA *certs,
72	unsigned numCerts)
73{
74	unsigned i;
75	char fileName[80];
76
77	for(i=0; i<numCerts; i++) {
78		sprintf(fileName, "caVerifyCert%u.cer", i);
79		writeFile(fileName, certs[i].Data, certs[i].Length);
80		printf("....wrote %lu bytes to %s\n", certs[i].Length, fileName);
81	}
82}
83
84/*
85 * Generate a cert chain using specified key pairs and extensions.
86 * The last cert in the chain (certs[numCerts-1]) is a root cert,
87 * self-signed.
88 */
89static CSSM_RETURN tpGenCertsExten(
90	CSSM_CSP_HANDLE		cspHand,
91	CSSM_CL_HANDLE		clHand,
92	CSSM_ALGORITHMS		sigAlg,			/* CSSM_ALGID_SHA1WithRSA, etc. */
93	unsigned			numCerts,
94	/* per-cert arrays */
95	const char 			*nameBase,		/* C string */
96	CSSM_KEY_PTR		pubKeys,		/* array of public keys */
97	CSSM_KEY_PTR		privKeys,		/* array of private keys */
98	CSSM_X509_EXTENSION	**extensions,
99	unsigned			*numExtensions,
100	CSSM_DATA_PTR		certs,			/* array of certs RETURNED here */
101	const char			*notBeforeStr,	/* from genTimeAtNowPlus() */
102	const char			*notAfterStr)	/* from genTimeAtNowPlus() */
103
104{
105	int 				dex;
106	CSSM_RETURN			crtn;
107	CSSM_X509_NAME		*issuerName = NULL;
108	CSSM_X509_NAME		*subjectName = NULL;
109	CSSM_X509_TIME		*notBefore;			// UTC-style "not before" time
110	CSSM_X509_TIME		*notAfter;			// UTC-style "not after" time
111	CSSM_DATA_PTR		rawCert = NULL;		// from CSSM_CL_CertCreateTemplate
112	CSSM_DATA			signedCert;			// from CSSM_CL_CertSign
113	uint32				rtn;
114	CSSM_KEY_PTR		signerKey;			// signs the cert
115	CSSM_CC_HANDLE		signContext;
116	char				nameStr[100];
117	CSSM_DATA_PTR		thisCert;			// ptr into certs[]
118	CB_NameOid			nameOid;
119
120	nameOid.oid = &CSSMOID_OrganizationName;	// const
121	nameOid.string = nameStr;
122
123	/* main loop - once per keypair/cert - starting at end/root */
124	for(dex=numCerts-1; dex>=0; dex--) {
125		thisCert = &certs[dex];
126
127		thisCert->Data = NULL;
128		thisCert->Length = 0;
129
130		sprintf(nameStr, "%s%04d", nameBase, dex);
131		if(issuerName == NULL) {
132			/* last (root) cert - subject same as issuer */
133			issuerName = CB_BuildX509Name(&nameOid, 1);
134			/* self-signed */
135			signerKey = &privKeys[dex];
136		}
137		else {
138			/* previous subject becomes current issuer */
139			CB_FreeX509Name(issuerName);
140			issuerName = subjectName;
141			signerKey = &privKeys[dex+1];
142		}
143		subjectName = CB_BuildX509Name(&nameOid, 1);
144		if((subjectName == NULL) || (issuerName == NULL)) {
145			printf("Error creating X509Names\n");
146			crtn = CSSMERR_CSSM_MEMORY_ERROR;
147			break;
148		}
149
150		/*
151		 * not before/after in Y2k-compliant generalized time format.
152		 * These come preformatted from our caller.
153		 */
154		notBefore = CB_BuildX509Time(0, notBeforeStr);
155		notAfter  = CB_BuildX509Time(0, notAfterStr);
156
157		/*
158		 * Cook up cert template
159		 * Note serial number would be app-specified in real world
160		 */
161		rawCert = CB_MakeCertTemplate(clHand,
162			0x12345 + dex,	// serial number
163			issuerName,
164			subjectName,
165			notBefore,
166			notAfter,
167			&pubKeys[dex],
168			sigAlg,
169			NULL,						// subj unique ID
170			NULL,						// issuer unique ID
171			extensions[dex],			// extensions
172			numExtensions[dex]);		// numExtensions
173
174		if(rawCert == NULL) {
175			crtn = CSSM_ERRCODE_INTERNAL_ERROR;
176			break;
177		}
178
179		/* Free the stuff we allocd to get here */
180		CB_FreeX509Time(notBefore);
181		CB_FreeX509Time(notAfter);
182
183		/**** sign the cert ****/
184		/* 1. get a signing context */
185		crtn = CSSM_CSP_CreateSignatureContext(cspHand,
186				sigAlg,
187				NULL,			// no passphrase for now
188				signerKey,
189				&signContext);
190		if(crtn) {
191			printError("CreateSignatureContext", crtn);
192			break;
193		}
194
195		/* 2. use CL to sign the cert */
196		signedCert.Data = NULL;
197		signedCert.Length = 0;
198		crtn = CSSM_CL_CertSign(clHand,
199			signContext,
200			rawCert,			// CertToBeSigned
201			NULL,				// SignScope per spec
202			0,					// ScopeSize per spec
203			&signedCert);
204		if(crtn) {
205			printError("CSSM_CL_CertSign", crtn);
206			break;
207		}
208
209		/* 3. delete signing context */
210		crtn = CSSM_DeleteContext(signContext);
211		if(crtn) {
212			printError("CSSM_DeleteContext", crtn);
213			break;
214		}
215
216		/*
217		 * CSSM_CL_CertSign() returned us a mallocd CSSM_DATA. Copy
218		 * its fields to caller's cert.
219		 */
220		certs[dex] = signedCert;
221
222		/* and the raw unsigned cert as well */
223		appFreeCssmData(rawCert, CSSM_TRUE);
224		rtn = 0;
225	}
226
227	/* free resources */
228	if(issuerName != NULL) {
229		CB_FreeX509Name(issuerName);
230	}
231	if(subjectName != NULL) {
232		CB_FreeX509Name(subjectName);
233	}
234	return crtn;
235}
236
237static int doTest(
238	CSSM_CSP_HANDLE	cspHand,		// CSP handle
239	CSSM_CL_HANDLE	clHand,			// CL handle
240	CSSM_TP_HANDLE	tpHand,			// TP handle
241	unsigned		numCerts,		// >= 2
242	CSSM_KEY_PTR	pubKeys,
243	CSSM_KEY_PTR	privKeys,
244	CSSM_ALGORITHMS	sigAlg,
245	CSSM_BOOL		expectFail,
246	CSSM_BOOL		dumpCerts,
247	CSSM_BOOL		quiet)
248{
249	CSSM_DATA_PTR		certs;
250	CSSM_X509_EXTENSION	**extens;
251	unsigned			*numExtens;
252	char 				*notBeforeStr = genTimeAtNowPlus(0);
253	char 				*notAfterStr = genTimeAtNowPlus(10000);
254	unsigned			certDex;
255	CE_BasicConstraints	*bc;
256	CSSM_X509_EXTENSION *thisExten;
257	CE_BasicConstraints *thisBc;
258	const char			*failMode = "not set - internal error";
259	CSSM_RETURN			crtn;
260	unsigned 			badCertDex = 0;
261
262	certs = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA) * numCerts);
263	memset(certs, 0, sizeof(CSSM_DATA) * numCerts);
264
265	/*
266	 * For now just zero or one extension per cert - basicConstraints.
267	 * Eventually we'll want to test keyUsage as well.
268	 */
269	extens = (CSSM_X509_EXTENSION **)malloc(sizeof(CSSM_X509_EXTENSION *) * numCerts);
270	memset(extens, 0, sizeof(CSSM_X509_EXTENSION *) * numCerts);
271	numExtens = (unsigned *)malloc(sizeof(unsigned) * numCerts);
272	bc = (CE_BasicConstraints *)malloc(sizeof(CE_BasicConstraints) * numCerts);
273
274	/*
275	 * Set up all extensions for success
276	 */
277	for(certDex=0; certDex<numCerts; certDex++) {
278		extens[certDex] = (CSSM_X509_EXTENSION *)malloc(sizeof(CSSM_X509_EXTENSION));
279		numExtens[certDex] = 1;
280		thisExten = extens[certDex];
281		thisBc = &bc[certDex];
282
283		thisExten->extnId = CSSMOID_BasicConstraints;
284		thisExten->critical = CSSM_TRUE;
285		thisExten->format = CSSM_X509_DATAFORMAT_PARSED;
286		thisExten->value.parsedValue = thisBc;
287		thisExten->BERvalue.Data = NULL;
288		thisExten->BERvalue.Length = 0;
289
290		if(certDex == 0) {
291			/* leaf - flip coin to determine presence of basicConstraints */
292			int coin = genRand(1,2);
293			if(coin == 1) {
294				/* basicConstraints, !cA */
295				thisBc->cA = CSSM_FALSE;
296				thisBc->pathLenConstraintPresent = CSSM_FALSE;
297				thisBc->pathLenConstraint = 0;
298			}
299			else {
300				/* !basicConstraints, !cA by default */
301				numExtens[certDex] = 0;
302			}
303		}
304		else if(certDex == (numCerts-1)) {
305			/* root - flip coin to determine presence of basicConstraints */
306			int coin = genRand(1,2);
307			if(coin == 1) {
308				/* basicConstraints, cA */
309				thisBc->cA = CSSM_TRUE;
310				/* flip coin to determine present of pathLenConstraint */
311				coin = genRand(1,2);
312				if(coin == 1) {
313					thisBc->pathLenConstraintPresent = CSSM_FALSE;
314					thisBc->pathLenConstraint = 0;
315				}
316				else {
317					thisBc->pathLenConstraintPresent = CSSM_TRUE;
318					thisBc->pathLenConstraint = genRand(certDex-1, numCerts+1);
319				}
320			}
321			else {
322				/* !basicConstraints, cA by default */
323				numExtens[certDex] = 0;
324			}
325		}
326		else {
327			/* intermediate = cA required */
328			thisBc->cA = CSSM_TRUE;
329			/* flip coin to determine presence of pathLenConstraint */
330			int coin = genRand(1,2);
331			if(coin == 1) {
332				thisBc->pathLenConstraintPresent = CSSM_FALSE;
333				thisBc->pathLenConstraint = 0;
334			}
335			else {
336				thisBc->pathLenConstraintPresent = CSSM_TRUE;
337				thisBc->pathLenConstraint = genRand(certDex-1, numCerts+1);
338			}
339		}
340	}
341
342	if(expectFail) {
343		/* introduce a failure */
344		if(numCerts == 2) {
345			/* only possible failure is explicit !cA in root */
346			/* don't assume presence of BC exten */
347			badCertDex = 1;
348			thisExten = extens[badCertDex];
349			thisBc = &bc[badCertDex];
350			thisBc->cA = CSSM_FALSE;
351			thisBc->pathLenConstraintPresent = CSSM_FALSE;
352			bc->pathLenConstraint = 0;
353			numExtens[badCertDex] = 1;
354			failMode = "Explicit !cA in root";
355		}
356		else {
357			/* roll the dice to select an intermediate cert */
358			badCertDex = genRand(1, numCerts-2);
359			thisExten = extens[badCertDex];
360			if((thisExten == NULL) || (numExtens[badCertDex] == 0)) {
361				printf("***INTERNAL SCREWUP\n");
362				exit(1);
363			}
364			thisBc = &bc[badCertDex];
365
366			/*
367			 * roll die: fail by
368			 *   -- no BasicConstraints
369			 *   -- !cA
370			 *   -- bad pathLenConstraint
371			 */
372			int die = genRand(1,3);
373			if((die == 1) &&
374			   (badCertDex != 1)) {		// last cA doesn't need pathLenConstraint
375				thisBc->pathLenConstraintPresent = CSSM_TRUE;
376				thisBc->pathLenConstraint = badCertDex - 2;	// one short
377				failMode = "Short pathLenConstraint";
378			}
379			else if(die == 2) {
380				thisBc->cA = CSSM_FALSE;
381				failMode = "Explicit !cA in intermediate";
382			}
383			else {
384				/* no extension */
385				numExtens[badCertDex] = 0;
386				failMode = "No BasicConstraints in intermediate";
387			}
388		}
389	}
390	if(!quiet && expectFail) {
391		printf("   ...bad cert at index %d: %s\n", badCertDex, failMode);
392	}
393
394	/* here we go - create cert chain */
395	crtn = tpGenCertsExten(cspHand,
396		clHand,
397		sigAlg,
398		numCerts,
399		"caVerify",		// nameBase
400		pubKeys,
401		privKeys,
402		extens,
403		numExtens,
404		certs,
405		notBeforeStr,
406		notAfterStr);
407	if(crtn) {
408		printError("tpGenCertsExten", crtn);
409		return crtn;	// and leak like crazy
410	}
411
412	CSSM_CERTGROUP  cgrp;
413	memset(&cgrp, 0, sizeof(CSSM_CERTGROUP));
414	cgrp.NumCerts = numCerts;
415	#if		PUMA_BUILD
416	cgrp.CertGroupType = CSSM_CERTGROUP_ENCODED_CERT;
417	#else
418	/* Jaguar */
419	cgrp.CertGroupType = CSSM_CERTGROUP_DATA;
420	#endif	/* PUMA_BUILD */
421	cgrp.CertType = CSSM_CERT_X_509v3;
422	cgrp.CertEncoding = CSSM_CERT_ENCODING_DER;
423	cgrp.GroupList.CertList = certs;
424
425	#if		PUMA_BUILD
426	crtn = tpCertGroupVerify(tpHand,
427		clHand,
428		cspHand,
429		NULL,						// DlDbList
430		&CSSMOID_APPLE_X509_BASIC,	// SSL requires built-in root match
431		&cgrp,
432		/* pass in OUR ROOT as anchors */
433		(CSSM_DATA_PTR)&certs[numCerts-1],		// anchorCerts
434		1,
435		CSSM_TP_STOP_ON_POLICY,
436		CSSM_FALSE,					// allowExpired
437		NULL);						// vfyResult
438	#else
439	/* Jaguar */
440	crtn = tpCertGroupVerify(tpHand,
441		clHand,
442		cspHand,
443		NULL,						// DlDbList
444		&CSSMOID_APPLE_TP_SSL,		// may want to parameterize this
445		NULL,						// fieldOpts for server name
446		NULL,						// actionDataPtr for allow expired
447		NULL,						// policyOpts
448		&cgrp,
449		/* pass in OUR ROOT as anchors */
450		(CSSM_DATA_PTR)&certs[numCerts-1],		// anchorCerts
451		1,
452		CSSM_TP_STOP_ON_POLICY,
453		NULL,						// cssmTimeStr
454		NULL);						// vfyResult
455	#endif	/* PUMA_BUILD */
456	if(expectFail) {
457		if(crtn != CSSMERR_TP_VERIFY_ACTION_FAILED) {
458			cssmPerror("***Expected error TP_VERIFY_ACTION_FAILED; got ", crtn);
459			printf("   Expected failure due to %s\n", failMode);
460			if(dumpCerts) {
461				showCerts(certs, numCerts);
462				writeCerts(certs, numCerts);
463			}
464			return testError(quiet);
465		}
466	}
467	else if(crtn) {
468		cssmPerror("Unexpected failure on tpCertGroupVerify", crtn);
469		if(dumpCerts) {
470			showCerts(certs, numCerts);
471		}
472		return testError(quiet);
473	}
474
475	/* clean up */
476	return 0;
477}
478
479int main(int argc, char **argv)
480{
481	CSSM_CL_HANDLE	clHand;			// CL handle
482	CSSM_CSP_HANDLE	cspHand;		// CSP handle
483	CSSM_TP_HANDLE	tpHand;
484	CSSM_KEY_PTR	pubKeys;
485	CSSM_KEY_PTR	privKeys;
486	CSSM_RETURN		crtn;
487	int				arg;
488	unsigned		certDex;
489	unsigned		loopNum;
490	unsigned		maxCerts;
491
492	/* user-spec'd variables */
493	CSSM_ALGORITHMS	keyAlg = KEY_ALG_DEFAULT;
494	CSSM_ALGORITHMS sigAlg = SIG_ALG_DEFAULT;
495	uint32			keySizeInBits = CSP_KEY_SIZE_DEFAULT;
496	unsigned 		numCerts = 0;			// means random per loop
497	unsigned 		numLoops = NUM_LOOPS_DEF;
498	CSSM_BOOL		quiet = CSSM_FALSE;
499	CSSM_BOOL		dumpCerts = CSSM_FALSE;
500
501	for(arg=1; arg<argc; arg++) {
502		switch(argv[arg][0]) {
503			case 'a':
504				if((argv[arg][1] == '\0') || (argv[arg][2] == '\0')) {
505					usage(argv);
506				}
507				switch(argv[arg][2]) {
508					case 's':
509						keyAlg = CSSM_ALGID_RSA;
510						sigAlg = CSSM_ALGID_SHA1WithRSA;
511						break;
512					case '5':
513						keyAlg = CSSM_ALGID_RSA;
514						sigAlg = CSSM_ALGID_MD5WithRSA;
515						break;
516					case 'f':
517						keyAlg = CSSM_ALGID_FEE;
518						sigAlg = CSSM_ALGID_FEE_MD5;
519						break;
520					case 'F':
521						keyAlg = CSSM_ALGID_FEE;
522						sigAlg = CSSM_ALGID_FEE_SHA1;
523						break;
524					case 'e':
525						keyAlg = CSSM_ALGID_FEE;
526						sigAlg = CSSM_ALGID_SHA1WithECDSA;
527						break;
528					case 'E':
529						keyAlg = CSSM_ALGID_ECDSA;
530						sigAlg = CSSM_ALGID_SHA1WithECDSA;
531						break;
532					case '6':
533						keyAlg = CSSM_ALGID_ECDSA;
534						sigAlg = CSSM_ALGID_SHA256WithECDSA;
535						break;
536					default:
537						usage(argv);
538				}
539				break;
540		    case 'k':
541				keySizeInBits = atoi(&argv[arg][2]);
542				break;
543		    case 'l':
544				numLoops = atoi(&argv[arg][2]);
545				break;
546		    case 'n':
547				numCerts = atoi(&argv[arg][2]);
548				break;
549		    case 'c':
550				dumpCerts = CSSM_TRUE;
551				break;
552		    case 'q':
553				quiet = CSSM_TRUE;
554				break;
555			default:
556				usage(argv);
557		}
558	}
559
560	/* connect to CL, TP, and CSP */
561	clHand = clStartup();
562	if(clHand == 0) {
563		return 0;
564	}
565	tpHand = tpStartup();
566	if(tpHand == 0) {
567		return 0;
568	}
569	cspHand = cspStartup();
570	if(cspHand == 0) {
571		return 0;
572	}
573
574	if(numCerts == 0) {
575		maxCerts = NUM_CERTS_DEF;	// random, this is the max $
576	}
577	else {
578		maxCerts = numCerts;		// user-specd
579	}
580
581	printf("Starting caVerify; args: ");
582	for(unsigned i=1; i<(unsigned)argc; i++) {
583		printf("%s ", argv[i]);
584	}
585	printf("\n");
586
587	/* cook up maxCerts key pairs */
588	if(!quiet) {
589		printf("generating key pairs...\n");
590	}
591	pubKeys  = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY) * maxCerts);
592	privKeys = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY) * maxCerts);
593	for(certDex=0; certDex<maxCerts; certDex++) {
594		crtn = cspGenKeyPair(cspHand,
595			keyAlg,
596			"a key pair",
597			9,
598			keySizeInBits,
599			&pubKeys[certDex],
600			CSSM_FALSE,			// pubIsRef - should work both ways, but not yet
601			CSSM_KEYUSE_VERIFY,
602			CSSM_KEYBLOB_RAW_FORMAT_NONE,
603			&privKeys[certDex],
604			CSSM_TRUE,			// privIsRef - doesn't matter
605			CSSM_KEYUSE_SIGN,
606			CSSM_KEYBLOB_RAW_FORMAT_NONE,
607			CSSM_FALSE);
608		if(crtn) {
609			printError("cspGenKeyPair", crtn);
610			printf("***error generating key pair. Aborting.\n");
611			exit(1);
612		}
613	}
614
615	for(loopNum=0; loopNum<numLoops; loopNum++) {
616		unsigned thisNumCerts;
617
618		/* random: num certs and whether this loop is to test a failure */
619		if(numCerts) {
620			thisNumCerts = numCerts;
621		}
622		else {
623			thisNumCerts = genRand(2, NUM_CERTS_DEF);
624		}
625		int coin = genRand(1,2);
626		CSSM_BOOL expectFail = (coin == 1) ? CSSM_TRUE : CSSM_FALSE;
627		if(!quiet) {
628			printf("...loop %d numCerts %u expectFail %s\n", loopNum,
629				thisNumCerts, expectFail ? "TRUE" : "FALSE");
630		}
631		if(doTest(cspHand,
632			clHand,
633			tpHand,
634			thisNumCerts,
635			pubKeys,
636			privKeys,
637			sigAlg,
638			expectFail,
639			dumpCerts,
640			quiet)) {
641				break;
642		}
643	}
644	/* clean up */
645	return 0;
646}
647
648
649