1/*
2 * ocspTool - simple OCSP request/response generator and parser
3 */
4#include <Security/Security.h>
5#include <Security/SecAsn1Coder.h>
6#include <Security/ocspTemplates.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <unistd.h>
10#include <string.h>
11#include <sys/types.h>
12#include <security_cdsa_utils/cuFileIo.h>
13#include <security_cdsa_utils/cuCdsaUtils.h>
14#include <security_cdsa_utils/cuOidParser.h>
15#include <security_cdsa_utils/cuPrintCert.h>
16#include <clAppUtils/CertParser.h>
17#include <clAppUtils/timeStr.h>
18#include <clAppUtils/identPicker.h>
19#include <CommonCrypto/CommonDigest.h>
20#include <security_ocspd/ocspExtensions.h>
21#include <security_ocspd/ocspdUtils.h>
22#include <utilLib/common.h>
23#include "ocspUtils.h"
24#include "ocspRequest.h"
25#include "ocspNetwork.h"
26#include "findOcspUrl.h"
27
28static void usage(char **argv)
29{
30	printf("Usage: %s cmd [option...]\n", argv[0]);
31	printf("cmds:\n");
32	printf("   g generate request\n");
33	printf("   G parse request\n");
34	printf("   r generate reply\n");
35	printf("   R parse reply\n");
36	printf("   p generate OCSP request, post, get reply (use -p and/or -o)\n");
37	printf("Options\n");
38	printf("  -c cert_file         -- for generating request\n");
39	printf("  -C issuer_cert_file  -- for generating request\n");
40	printf("  -i in_file           -- for parsing\n");
41	printf("  -o out_file          -- for generating\n");
42	printf("  -s status            -- cert status: g(ood)|r(evoked)|u(nknown)\n");
43	printf("  -r crlReason         -- integer 0..8\n");
44	printf("  -k keychain          -- keychain containing signing cert\n");
45	printf("  -p                   -- parse reply from post op\n");
46	printf("  -u responderURI      -- OCSP responder here, not from cert's AIA extension\n");
47	printf("  -v                   -- verbose; e.g., print certs\n");
48	exit(1);
49}
50
51void doIndent(int indent)
52{
53	for(int dex=0; dex<indent; dex++) {
54		printf(" ");
55	}
56}
57
58static void printString(
59	const CSSM_DATA *str)
60{
61	unsigned i;
62	char *cp = (char *)str->Data;
63	for(i=0; i<str->Length; i++) {
64		printf("%c", *cp++);
65	}
66	printf("\n");
67}
68
69static void printDataAsHex(
70	const CSSM_DATA *d,
71	unsigned maxToPrint = 0)		// optional, 0 means print it all
72{
73	unsigned i;
74	bool more = false;
75	uint32 len = d->Length;
76	uint8 *cp = d->Data;
77
78	if((maxToPrint != 0) && (len > maxToPrint)) {
79		len = maxToPrint;
80		more = true;
81	}
82	for(i=0; i<len; i++) {
83		printf("%02X ", ((unsigned char *)cp)[i]);
84	}
85	if(more) {
86		printf("...\n");
87	}
88	else {
89		printf("\n");
90	}
91}
92
93static void printTaggedItem(
94	const NSS_TaggedItem &ti)
95{
96	switch(ti.tag) {
97		case BER_TAG_PRINTABLE_STRING:
98		case BER_TAG_T61_STRING:
99		case BER_TAG_IA5_STRING:
100		case BER_TAG_UTC_TIME:
101		case BER_TAG_GENERALIZED_TIME:
102			printString(&ti.item);
103			break;
104		default:
105			printDataAsHex(&ti.item, 0);
106	}
107}
108
109static void printName(
110	const NSS_Name &name,
111	int indent)
112{
113	OidParser parser;
114
115	unsigned numRdns = ocspdArraySize((const void **)name.rdns);
116	for(unsigned rdnDex=0; rdnDex<numRdns; rdnDex++) {
117		NSS_RDN *rdn = name.rdns[rdnDex];
118		unsigned numATVs = ocspdArraySize((const void **)rdn->atvs);
119		for(unsigned atvDex=0; atvDex<numATVs; atvDex++) {
120			NSS_ATV *atv = rdn->atvs[atvDex];
121			char buf[OID_PARSER_STRING_SIZE];
122			parser.oidParse(atv->type.Data, atv->type.Length, buf);
123			doIndent(indent);
124			printf("%s : ", buf);
125			printTaggedItem(atv->value);
126		}
127	}
128}
129
130static uint8 nullParam[2] = {5, 0};
131
132/*
133 * Given the following, create a ResponseData (to be signed by caller).
134 *
135 *		cert status (CS_{Good,Revoked,Unknown})
136 *		cert being verified
137 *		issuer cert
138 *		this update time
139 *		next update time (optional)
140 *		nonce (optional)
141 */
142static int genTbsResp(
143	SecAsn1CoderRef coder,			// result in this coder's address space
144	CSSM_CL_HANDLE clHand,
145	SecAsn1OCSPCertStatusTag status,
146	CE_CrlReason reason,			// for CS_Revoked
147	const CSSM_DATA	&subjectCert,
148	const CSSM_DATA &issuerCert,
149	unsigned thisUpdate,			// required, seconds from now
150	unsigned nextUpdate,			// optional, seconds from now, 0 ==> skip
151	const CSSM_DATA *nonce,			// optional
152	CSSM_DATA &encodedTbs)			// allocated in coder space and RETURNED
153{
154	char *nextUpdStr = NULL;
155	CSSM_DATA nextUpdateData;
156	char *thisUpdStr = NULL;
157	CSSM_DATA *thisUpdateData;
158	SecAsn1OCSPResponseData responseData;
159	OCSPNonce *nonceExt = NULL;
160	char *producedAt = NULL;
161	SecAsn1OCSPSingleResponse singleResp;
162	SecAsn1OCSPSingleResponse *respArray[2] = {&singleResp, NULL};
163	SecAsn1OCSPResponderID responderID;
164	NSS_CertExtension *extenArray[2] = {NULL, NULL};
165
166	/* SingleResponse */
167	memset(&singleResp, 0, sizeof(singleResp));
168
169	/* SingleResponse.CertID */
170	SecAsn1OCSPCertID &certId = singleResp.certID;
171	CertParser parser(clHand);
172	CertParser issuerParser(clHand);
173	CSSM_RETURN crtn = parser.initWithData(subjectCert);
174	if(crtn) {
175		cssmPerror("CertParser.initWithData for subject cert", crtn);
176		return -1;
177	}
178	crtn = issuerParser.initWithData(issuerCert);
179	if(crtn) {
180		cssmPerror("CertParser.initWithData for issuer", crtn);
181		return -1;
182	}
183
184	/* algId refers to the hash we'll perform in issuer name and key */
185	certId.algId.algorithm = CSSMOID_SHA1;
186	certId.algId.parameters.Data = nullParam;
187	certId.algId.parameters.Length = sizeof(nullParam);
188
189	/* SHA1(issuerName) */
190	CSSM_DATA issuerName = {0, NULL};
191	issuerName.Data = (uint8 *)parser.fieldForOid(CSSMOID_X509V1IssuerNameStd,
192		issuerName.Length);
193	if(issuerName.Data == NULL) {
194		printf("***Error fetching issuer name. Aborting.\n");
195		return 1;
196	}
197	uint8 issuerNameHash[CC_SHA1_DIGEST_LENGTH];
198	ocspdSha1(issuerName.Data, issuerName.Length, issuerNameHash);
199
200	/* SHA1(issuer public key) */
201	CSSM_KEY_PTR pubKey = NULL;
202	CSSM_SIZE pubKeyLen = sizeof(CSSM_KEY);
203	pubKey = (CSSM_KEY_PTR)issuerParser.fieldForOid(CSSMOID_CSSMKeyStruct, pubKeyLen);
204	if(pubKey == NULL) {
205		printf("***Error fetching public key from issuer cert. Aborting.\n");
206		return 1;
207	}
208	uint8 pubKeyHash[CC_SHA1_DIGEST_LENGTH];
209	ocspdSha1(pubKey->KeyData.Data, pubKey->KeyData.Length, pubKeyHash);
210
211	/* serial number */
212	CSSM_DATA serialNum = {0, NULL};
213	serialNum.Data = (uint8 *)parser.fieldForOid(CSSMOID_X509V1SerialNumber,
214		serialNum.Length);
215	if(serialNum.Data == NULL) {
216		printf("***Error fetching serial number. Aborting.\n");
217		return 1;
218	}
219
220	/* build the CertID from those components */
221	certId.issuerNameHash.Data = issuerNameHash;
222	certId.issuerNameHash.Length = CC_SHA1_DIGEST_LENGTH;
223	certId.issuerPubKeyHash.Data = pubKeyHash;
224	certId.issuerPubKeyHash.Length = CC_SHA1_DIGEST_LENGTH;
225	certId.serialNumber = serialNum;
226
227	/* SingleResponse.CertStatus - to be encoded on its own */
228	SecAsn1OCSPCertStatus certStatus;
229	memset(&certStatus, 0, sizeof(certStatus));
230	SecAsn1OCSPRevokedInfo revokedInfo;
231	char *revokedAt = NULL;
232	CSSM_DATA reasonData;
233	OSStatus ortn;
234
235	if(status == CS_Revoked) {
236		/* cook up SecAsn1OCSPRevokedInfo */
237		certStatus.revokedInfo = &revokedInfo;
238		revokedAt = appTimeAtNowPlus(-3600, TIME_GEN);
239		revokedInfo.revocationTime.Data = (uint8 *)revokedAt;
240		revokedInfo.revocationTime.Length = strlen(revokedAt);
241		uint8 theReason = reason;
242		reasonData.Data = &theReason;
243		reasonData.Length = 1;
244		revokedInfo.revocationReason = &reasonData;
245		ortn = SecAsn1EncodeItem(coder, &certStatus,
246			kSecAsn1OCSPCertStatusRevokedTemplate,
247			&singleResp.certStatus);
248	}
249	else {
250		ortn = SecAsn1EncodeItem(coder, &certStatus,
251			kSecAsn1OCSPCertStatusGoodTemplate,
252			&singleResp.certStatus);
253	}
254	if(ortn) {
255		printf("***Error encoding certStatus\n");
256		goto errOut;
257	}
258
259	/* SingleResponse.thisUpdate */
260	thisUpdStr = appTimeAtNowPlus(thisUpdate, TIME_GEN);
261	thisUpdateData = &singleResp.thisUpdate;
262	thisUpdateData->Data = (uint8 *)thisUpdStr;
263	thisUpdateData->Length = strlen(thisUpdStr);
264
265	/* SingleResponse.nextUpdate, optional */
266	if(nextUpdate) {
267		nextUpdStr = appTimeAtNowPlus(nextUpdate, TIME_GEN);
268		nextUpdateData.Data = (uint8 *)nextUpdStr;
269		nextUpdateData.Length = strlen(nextUpdStr);
270		singleResp.nextUpdate = &nextUpdateData;
271	}
272
273	/* Single Extensions - none for now */
274
275	/* Now up to ResponseData */
276	memset(&responseData, 0, sizeof(responseData));
277
278	/* skip version */
279
280	/*
281	 * ResponseData.responderID: KeyHash (of signer); we already got this for CertID.
282	 * WE have to encode this one separately and drop it in as an ASN_ANY.
283	 */
284	responderID.byKey = certId.issuerPubKeyHash;
285	ortn = SecAsn1EncodeItem(coder, &responderID,
286		kSecAsn1OCSPResponderIDAsKeyTemplate,
287		&responseData.responderID);
288	if(ortn) {
289		printf("***Error encoding responderID\n");
290		goto errOut;
291	}
292
293	/* ResponseData.producedAt = now */
294	producedAt = appTimeAtNowPlus(0, TIME_GEN);
295	responseData.producedAt.Data = (uint8 *)producedAt;
296	responseData.producedAt.Length = strlen(producedAt);
297
298	/* ResponseData.responses - one of 'em */
299	responseData.responses = respArray;
300
301	/* ResponseData.responseExtensions - optionally one, nonce */
302	if(nonce) {
303		nonceExt = new OCSPNonce(coder, false, *nonce);
304		extenArray[0] = nonceExt->nssExt();
305		responseData.responseExtensions = extenArray;
306	}
307	else {
308		responseData.responseExtensions = NULL;
309	}
310
311	/* encode it */
312	encodedTbs.Data = NULL;
313	encodedTbs.Length = 0;
314	ortn = SecAsn1EncodeItem(coder, &responseData,
315		kSecAsn1OCSPResponseDataTemplate,
316		&encodedTbs);
317	if(ortn) {
318		printf("***Error encoding SecAsn1OCSPResponseData\n");
319	}
320errOut:
321	/* free resources */
322	if(revokedAt) {
323		CSSM_FREE(revokedAt);
324	}
325	if(thisUpdStr) {
326		CSSM_FREE(thisUpdStr);
327	}
328	if(nextUpdStr) {
329		CSSM_FREE(nextUpdStr);
330	}
331	if(nonceExt) {
332		delete nonceExt;
333	}
334	return ortn;
335}
336
337static int genOcspReq(
338	CSSM_CL_HANDLE clHand,
339	const unsigned char *certFile,
340	unsigned certFileLen,
341	const unsigned char *issuerCertFile,
342	unsigned issuerCertFileLen,
343	unsigned char **outFile,		// RETURNED
344	unsigned *outFileLen)			// RETURNED
345{
346	CertParser parser(clHand);
347	CertParser issuerParser(clHand);
348	CSSM_DATA certData = {certFileLen, (uint8 *)certFile};
349	CSSM_RETURN crtn;
350	crtn = parser.initWithData(certData);
351	if(crtn) {
352		cssmPerror("CertParser.initWithData for subject cert", crtn);
353		return -1;
354	}
355	certData.Data = (uint8 *)issuerCertFile;
356	certData.Length = issuerCertFileLen;
357	crtn = issuerParser.initWithData(certData);
358	if(crtn) {
359		cssmPerror("CertParser.initWithData for issuer", crtn);
360		return -1;
361	}
362
363	/*
364	 * One single request, no extensions
365	 */
366	SecAsn1OCSPRequest singleReq;
367	memset(&singleReq, 0, sizeof(singleReq));
368	SecAsn1OCSPCertID &certId = singleReq.reqCert;
369
370	/* algId refers to the hash we'll perform in issuer name and key */
371	certId.algId.algorithm = CSSMOID_SHA1;
372	certId.algId.parameters.Data = nullParam;
373	certId.algId.parameters.Length = sizeof(nullParam);
374
375	/* SHA1(issuerName) */
376	CSSM_DATA issuerName = {0, NULL};
377	issuerName.Data = (uint8 *)parser.fieldForOid(CSSMOID_X509V1IssuerNameStd,
378		issuerName.Length);
379	if(issuerName.Data == NULL) {
380		printf("***Error fetching issuer name. Aborting.\n");
381		return 1;
382	}
383	uint8 issuerNameHash[CC_SHA1_DIGEST_LENGTH];
384	ocspdSha1(issuerName.Data, issuerName.Length, issuerNameHash);
385
386	/* SHA1(issuer public key) */
387	CSSM_KEY_PTR pubKey = NULL;
388	CSSM_SIZE pubKeyLen = sizeof(CSSM_KEY);
389	pubKey = (CSSM_KEY_PTR)issuerParser.fieldForOid(CSSMOID_CSSMKeyStruct, pubKeyLen);
390	if(pubKey == NULL) {
391		printf("***Error fetching public key from issuer cert. Aborting.\n");
392		return 1;
393	}
394	uint8 pubKeyHash[CC_SHA1_DIGEST_LENGTH];
395	ocspdSha1(pubKey->KeyData.Data, pubKey->KeyData.Length, pubKeyHash);
396
397	/* serial number */
398	CSSM_DATA serialNum = {0, NULL};
399	serialNum.Data = (uint8 *)parser.fieldForOid(CSSMOID_X509V1SerialNumber,
400		serialNum.Length);
401	if(serialNum.Data == NULL) {
402		printf("***Error fetching serial number. Aborting.\n");
403		return 1;
404	}
405
406	/* build the CertID from those components */
407	certId.issuerNameHash.Data = issuerNameHash;
408	certId.issuerNameHash.Length = CC_SHA1_DIGEST_LENGTH;
409	certId.issuerPubKeyHash.Data = pubKeyHash;
410	certId.issuerPubKeyHash.Length = CC_SHA1_DIGEST_LENGTH;
411	certId.serialNumber = serialNum;
412
413	/*
414	 * Build top level request with one entry in requestList, no signature,
415	 * one extension (a nonce)
416	 */
417	SecAsn1OCSPSignedRequest signedReq;
418	SecAsn1OCSPRequest *reqArray[2] = { &singleReq, NULL };
419	SecAsn1OCSPTbsRequest &tbs = signedReq.tbsRequest;
420	memset(&signedReq, 0, sizeof(signedReq));
421	uint8 version = 0;
422	CSSM_DATA vers = {1, &version};
423	tbs.version = &vers;
424	tbs.requestList = reqArray;
425
426	/* one extension - the nonce */
427	SecAsn1CoderRef coder;
428	SecAsn1CoderCreate(&coder);
429	uint8 nonceBytes[8] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0};
430	CSSM_DATA nonceData = {8, nonceBytes};
431	OCSPNonce *nonce = new OCSPNonce(coder, false, nonceData);
432	NSS_CertExtension *extenArray[2] = {nonce->nssExt(), NULL};
433	tbs.requestExtensions = extenArray;
434
435	/* Encode */
436	OSStatus ortn;
437	CSSM_DATA encoded = {0, NULL};
438	ortn = SecAsn1EncodeItem(coder, &signedReq, kSecAsn1OCSPSignedRequestTemplate,
439		&encoded);
440	if(ortn) {
441		printf("***Error encoding SecAsn1OCSPSignedRequest\n");
442	}
443	else {
444		*outFile = (unsigned char *)malloc(encoded.Length);
445		*outFileLen = encoded.Length;
446		memmove(*outFile, encoded.Data, encoded.Length);
447	}
448	SecAsn1CoderRelease(coder);
449	return (int)ortn;
450}
451
452static void dumpCertID(
453	SecAsn1OCSPCertID *certID,
454	int indent)
455{
456	doIndent(indent);
457	printf("algId            : ");
458	printDataAsHex(&certID->algId.algorithm);
459
460	doIndent(indent);
461	printf("issuerNameHash   : ");
462	printDataAsHex(&certID->issuerNameHash);
463
464	doIndent(indent);
465	printf("issuerPubKeyHash : ");
466	printDataAsHex(&certID->issuerPubKeyHash);
467
468	doIndent(indent);
469	printf("serialNumber     : ");
470	printDataAsHex(&certID->serialNumber);
471}
472
473static void printCritical(
474	int indent,
475	OCSPExtension *ocspExt)
476{
477	doIndent(indent);
478	printf("Critical           : %s\n", ocspExt->critical() ? "true" : "false");
479}
480
481static void printOcspExt(
482	SecAsn1CoderRef coder,
483	NSS_CertExtension *nssExt,
484	int indent)
485{
486	OCSPExtension *ocspExt = NULL;
487	try {
488		ocspExt = OCSPExtension::createFromNSS(coder, *nssExt);
489	}
490	catch(...) {
491		doIndent(indent);
492		printf("***Error thrown parsing extension\n");
493		return;
494	}
495	switch(ocspExt->tag()) {
496		case OET_Unknown:
497			doIndent(indent);
498			printf("Extension type: Unknown\n");
499			printCritical(indent, ocspExt);
500			return;
501		case OET_Nonce:
502		{
503			doIndent(indent);
504			printf("Extension type     : Nonce\n");
505			printCritical(indent, ocspExt);
506			doIndent(indent);
507			OCSPNonce *nonce = dynamic_cast<OCSPNonce *>(ocspExt);
508			if(nonce == NULL) {
509				printf("***dynamic_cast failure in OCSPNonce!\n");
510				return;
511			}
512			printf("nonce value        : ");
513			printDataAsHex(&nonce->nonce());
514			break;
515		}
516		case OET_CrlReference:
517			doIndent(indent);
518			printf("Extension type     : CrlReference");
519			printCritical(indent, ocspExt);
520			/* TBD */
521			return;
522		case OET_AcceptResponse:
523			doIndent(indent);
524			printf("Extension type     : AcceptResponse");
525			printCritical(indent, ocspExt);
526			/* TBD */
527			return;
528		case OET_ArchiveCutoff:
529			doIndent(indent);
530			printf("Extension type     : ArchiveCutoff");
531			printCritical(indent, ocspExt);
532			/* TBD */
533			return;
534		case OET_ServiceLocator:
535			doIndent(indent);
536			printf("Extension type     : ServiceLocator");
537			printCritical(indent, ocspExt);
538			/* TBD */
539			return;
540		default:
541			/* this code is out of sync with ocspExtensions.{h,cpp} */
542			doIndent(indent);
543			printf("Extension type     : unrecognized - code sync error");
544			printCritical(indent, ocspExt);
545			return;
546
547	}
548}
549
550static int parseOcspReq(
551	CSSM_CL_HANDLE clHand,
552	unsigned char *inFile,
553	unsigned inFileLen,
554	bool verbose)
555{
556	SecAsn1CoderRef coder;
557	SecAsn1OCSPSignedRequest signedReq;
558	SecAsn1OCSPTbsRequest &tbs = signedReq.tbsRequest;
559	OSStatus ortn;
560	int indent;
561	unsigned numExts;
562	unsigned numReqs;
563
564	SecAsn1CoderCreate(&coder);
565	memset(&signedReq, 0, sizeof(signedReq));
566
567	ortn = SecAsn1Decode(coder, inFile, inFileLen, kSecAsn1OCSPSignedRequestTemplate,
568		&signedReq);
569	if(ortn) {
570		printf("***Error decoding SecAsn1OCSPSignedRequest\n");
571		goto errOut;
572	}
573	printf("SecAsn1OCSPSignedRequest:\n");
574
575	printf("SecAsn1OCSPTbsRequest:\n");
576	indent = 2;
577	if(tbs.version) {
578		doIndent(indent);
579		printf("Version : ");
580		printDataAsHex(tbs.version);
581	}
582	if(tbs.requestorName) {
583		doIndent(indent);
584		printf("NSS_GeneralName found; print it later maybe\n");
585	}
586	numReqs = ocspdArraySize((const void **)tbs.requestList);
587	for(unsigned dex=0; dex<numReqs; dex++) {
588		SecAsn1OCSPRequest *req = tbs.requestList[dex];
589		doIndent(indent);
590		printf("Request List Entry %u\n", dex);
591		indent += 2;
592		doIndent(indent);
593		printf("CertID:\n");
594		indent += 2;
595		SecAsn1OCSPCertID *certID = &req->reqCert;
596		dumpCertID(certID, indent);
597		indent -= 2;
598		numExts = ocspdArraySize((const void **)req->extensions);
599		for(unsigned extDex=0; extDex<numExts; extDex++) {
600			doIndent(indent);
601			printf("singleExtension[%u]\n", extDex);
602			printOcspExt(coder, req->extensions[dex], indent + 2);
603		}
604		indent -= 2;
605	}
606
607	numExts = ocspdArraySize((const void **)tbs.requestExtensions);
608	for(unsigned extDex=0; extDex<numExts; extDex++) {
609		doIndent(indent);
610		printf("requestExtension[%u]\n", extDex);
611		printOcspExt(coder, tbs.requestExtensions[extDex], indent + 2);
612	}
613
614	indent -= 2;
615
616	if(signedReq.signature) {
617		printf("SecAsn1OCSPSignature:\n");
618		indent += 2;
619		doIndent(indent);
620		printf("==unparsed for now ==\n");
621		/* ... */
622		indent -= 2;
623	}
624errOut:
625	SecAsn1CoderRelease(coder);
626	return ortn;
627}
628
629static int genOcspResp(
630	CSSM_CL_HANDLE clHand,
631	SecAsn1OCSPCertStatusTag status,
632	CE_CrlReason reason,			// for CS_Revoked
633	const unsigned char *subjectCert,
634	unsigned subjectCertLen,
635	const unsigned char *issuerCert,
636	unsigned issuerCertLen,
637	SecIdentityRef signer,
638	unsigned char **outData,
639	unsigned *outDataLen)
640{
641	SecAsn1CoderRef coder;
642	SecAsn1CoderCreate(&coder);
643
644	CSSM_DATA subjectCertData = {subjectCertLen, (uint8 *)subjectCert};
645	CSSM_DATA issuerCertData = {issuerCertLen, (uint8 *)issuerCert};
646	uint8 nonceBytes[8] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0};
647	CSSM_DATA nonceData = {8, nonceBytes};
648	CSSM_DATA tbs;
649	CSSM_DATA encoded = {0, NULL};
650	SecAsn1OCSPResponse topResponse;
651	SecAsn1OCSPResponseBytes responseBytes;
652	uint8 responseStatusByte;
653	CSSM_DATA resp = {0, NULL};
654	CSSM_DATA sig = {0, NULL};
655
656	int irtn = genTbsResp(coder, clHand, status, reason,
657		subjectCertData, issuerCertData,
658		0,			// thisUpdate
659		2600 * 24,	// next update
660		&nonceData,
661		tbs);
662	if(irtn) {
663		printf("***Error encoding tbsResp\n");
664		return irtn;
665	}
666
667	/*
668	 * That's the TBSResponseData. Sign it.
669	 */
670	OSStatus ortn;
671	SecAsn1OCSPBasicResponse basicResp;
672	memset(&basicResp, 0, sizeof(basicResp));
673	ortn = ocspSign(signer, tbs, CSSM_ALGID_SHA1WithRSA, sig);
674	if(ortn) {
675		printf("***Error signing basicResponse.\n");
676		goto errOut;
677	}
678	basicResp.algId.algorithm = CSSMOID_SHA1WithRSA;
679	basicResp.algId.parameters.Data = nullParam;
680	basicResp.algId.parameters.Length = sizeof(nullParam);
681	basicResp.tbsResponseData = tbs;
682	basicResp.sig = sig;
683	/* ASN1 encoder needs to know length in bits */
684	basicResp.sig.Length *= 8;
685	/* no certs for now */
686	/* encode SecAsn1OCSPBasicResponse */
687
688	ortn = SecAsn1EncodeItem(coder, &basicResp, kSecAsn1OCSPBasicResponseTemplate,
689		&encoded);
690	if(ortn) {
691		printf("***Error encoding SecAsn1OCSPBasicResponse\n");
692	}
693
694	/* put that into a SecAsn1OCSPResponse */
695	responseBytes.responseType = CSSMOID_PKIX_OCSP_BASIC;
696	responseBytes.response = encoded;
697	responseStatusByte = RS_Success;
698	topResponse.responseStatus.Data = &responseStatusByte;
699	topResponse.responseStatus.Length = 1;
700	topResponse.responseBytes = &responseBytes;
701	ortn = SecAsn1EncodeItem(coder, &topResponse, kSecAsn1OCSPResponseTemplate,
702		&resp);
703	if(ortn) {
704		printf("***Error encoding SecAsn1OCSPBasicResponse\n");
705		goto errOut;
706	}
707
708	/* TA DA */
709	*outData = (unsigned char *)malloc(resp.Length);
710	*outDataLen = resp.Length;
711	memmove(*outData, resp.Data, resp.Length);
712errOut:
713	SecAsn1CoderRelease(coder);
714	if(sig.Data) {
715		APP_FREE(sig.Data);
716	}
717	return ortn;
718}
719
720/* decode and parse tbsResponseData, sitting in SecAsn1OCSPBasicResponse as an
721 * ASN_ANY */
722static int 	parseResponseData(
723	SecAsn1CoderRef coder,
724	int indent,
725	const CSSM_DATA &tbsResponseData)
726{
727	SecAsn1OCSPResponseData	respData;
728	SecAsn1OCSPResponderID responderID;
729	uint8 tag;
730	const SecAsn1Template *templ;
731	unsigned numExts;
732
733	memset(&respData, 0, sizeof(respData));
734	OSStatus ortn = SecAsn1DecodeData(coder, &tbsResponseData,
735		kSecAsn1OCSPResponseDataTemplate, &respData);
736	if(ortn) {
737		printf("***Error decoding ResponseData\n");
738		return 1;
739	}
740	if(respData.version && respData.version->Data) {
741		doIndent(indent);
742		printf("version: %u\n", respData.version->Data[0]);
743	}
744	doIndent(indent);
745	printf("ResponderID:\n");
746	indent += 2;
747	memset(&responderID, 0, sizeof(responderID));
748	if(respData.responderID.Data == NULL) {
749		doIndent(indent);
750		printf("***Malformed(empty)***\n");
751		return 1;
752	}
753
754	/* lame-o choice processing */
755	tag = respData.responderID.Data[0] & SEC_ASN1_TAGNUM_MASK;
756	switch(tag) {
757		case RIT_Name:
758			templ = kSecAsn1OCSPResponderIDAsNameTemplate;
759			break;
760		case RIT_Key:
761			templ = kSecAsn1OCSPResponderIDAsKeyTemplate;
762			break;
763		default:
764			doIndent(indent);
765			printf("**Unknown tag for ResponderID (%u)\n", tag);
766			return 1;
767	}
768	ortn = SecAsn1DecodeData(coder, &respData.responderID, templ, &responderID);
769	if(ortn) {
770		doIndent(indent);
771		printf("***Error decoding ResponderID\n");
772		return 1;
773	}
774	doIndent(indent);
775	switch(tag) {
776		case RIT_Name:
777			printf("byName:\n");
778			printName((NSS_Name &)responderID.byName, indent + 2);
779			break;
780		case RIT_Key:
781			printf("byKey : ");
782			printDataAsHex(&responderID.byKey);
783			break;
784	}
785	indent -= 2;		// end of ResponderID
786
787	doIndent(indent);
788	printf("producedAt: ");
789	printString(&respData.producedAt);
790	unsigned numResps = ocspdArraySize((const void **)respData.responses);
791	doIndent(indent);
792	printf("Num responses: %u\n", numResps);
793	for(unsigned dex=0; dex<numResps; dex++) {
794		SecAsn1OCSPSingleResponse *resp = respData.responses[dex];
795		doIndent(indent);
796		printf("Response %u:\n", dex);
797		indent += 2;
798		doIndent(indent);
799		printf("CertID:\n");
800		dumpCertID(&resp->certID, indent + 2);
801
802		doIndent(indent);
803		printf("certStatus: ");
804		/* lame-o choice processing */
805		tag = resp->certStatus.Data[0] & SEC_ASN1_TAGNUM_MASK;
806		switch(tag) {
807			case CS_Good:
808				printf("Good\n");
809				break;
810			case CS_Unknown:
811				printf("Unknown\n");
812				break;
813			default:
814				printf("**MALFORMED cert status tag (%u)\n", tag);
815				break;
816			case CS_Revoked:
817			{
818				printf("Revoked\n");
819				doIndent(indent);
820				SecAsn1OCSPCertStatus certStatus;
821				memset(&certStatus, 0, sizeof(certStatus));
822				ortn = SecAsn1DecodeData(coder, &resp->certStatus,
823					kSecAsn1OCSPCertStatusRevokedTemplate, &certStatus);
824				if(ortn) {
825					doIndent(indent);
826					printf("***error parsing RevokedInfo\n");
827					break;
828				}
829				if(certStatus.revokedInfo == NULL) {
830					doIndent(indent);
831					printf("***GAK! Malformed (empty) revokedInfo\n");break;
832				}
833				printf("RevokedIndfo:\n");
834				indent += 2;
835				doIndent(indent);
836				printf("revocationTime: ");
837				printString(&certStatus.revokedInfo->revocationTime);
838				if(certStatus.revokedInfo->revocationReason) {
839					doIndent(indent);
840					printf("reason: %u\n",
841						certStatus.revokedInfo->revocationReason->Data[0]);
842				}
843				indent -= 2;		// end of RevokedInfo
844				break;
845			}
846		}	/* switch cert status tag */
847
848		doIndent(indent);
849		printf("thisUpdate: ");
850		printString(&resp->thisUpdate);
851
852		if(resp->nextUpdate) {
853			doIndent(indent);
854			printf("nextUpdate: ");
855			printString(resp->nextUpdate);
856		}
857
858		numExts = ocspdArraySize((const void **)resp->singleExtensions);
859		for(unsigned extDex=0; extDex<numExts; extDex++) {
860			doIndent(indent);
861			printf("singleExtensions[%u]\n", extDex);
862			printOcspExt(coder, resp->singleExtensions[extDex], indent + 2);
863		}
864
865		indent -= 2;		// end of resp[dex]
866	}
867
868	numExts = ocspdArraySize((const void **)respData.responseExtensions);
869	for(unsigned extDex=0; extDex<numExts; extDex++) {
870		doIndent(indent);
871		printf("responseExtensions[%u]\n", extDex);
872		printOcspExt(coder, respData.responseExtensions[extDex], indent + 2);
873	}
874	return 0;
875}
876
877static int parseOcspResp(
878	CSSM_CL_HANDLE clHand,
879	unsigned char *inFile,
880	unsigned inFileLen,
881	bool verbose)
882{
883	SecAsn1OCSPResponse topResp;
884	SecAsn1CoderRef coder;
885	OSStatus ortn;
886	int indent = 0;
887	const char *str;
888	SecAsn1OCSPBasicResponse basicResp;
889	unsigned numCerts = 0;
890
891	SecAsn1CoderCreate(&coder);
892	memset(&topResp, 0, sizeof(topResp));
893	ortn = SecAsn1Decode(coder, inFile, inFileLen, kSecAsn1OCSPResponseTemplate,
894		&topResp);
895	if(ortn) {
896		printf("***Error decoding SecAsn1OCSPResponse\n");
897		goto errOut;
898	}
899	printf("OCSPResponse:\n");
900	indent += 2;
901	doIndent(indent);
902	printf("responseStatus: ");
903	if(topResp.responseStatus.Length == 0) {
904		printf("**MALFORMED**\n");
905	}
906	else {
907		switch(topResp.responseStatus.Data[0]) {
908			case RS_Success: str = "RS_Success"; break;
909			case RS_MalformedRequest: str = "RS_MalformedRequest"; break;
910			case RS_InternalError: str = "RS_InternalError"; break;
911			case RS_TryLater: str = "RS_TryLater"; break;
912			case RS_Unused: str = "RS_Unused"; break;
913			case RS_SigRequired: str = "RS_SigRequired"; break;
914			case RS_Unauthorized: str = "RS_Unauthorized"; break;
915			default: str = "MALFORMED (unknown enum)\n"; break;
916		}
917		printf("%s (%u(d))\n", str, topResp.responseStatus.Data[0]);
918	}
919	doIndent(indent);
920	printf("ResponseBytes: ");
921	if(topResp.responseBytes == NULL) {
922		printf("empty\n");
923		goto errOut;
924	}
925	printf("\n");
926	indent += 2;
927	doIndent(indent);
928	printf("responseType: ");
929	if(appCompareCssmData(&topResp.responseBytes->responseType,
930			&CSSMOID_PKIX_OCSP_BASIC)) {
931		str = "ocsp-basic";
932	}
933	else {
934		str = "Unknown type\n";
935	}
936	printf("%s\n", str);
937
938	/* decode the BasicOCSPResponse */
939	memset(&basicResp, 0, sizeof(basicResp));
940	ortn = SecAsn1DecodeData(coder, &topResp.responseBytes->response,
941		kSecAsn1OCSPBasicResponseTemplate, &basicResp);
942	if(ortn) {
943		printf("***Error decoding BasicOCSPResponse\n");
944		goto errOut;
945	}
946
947	doIndent(indent);
948	printf("BasicOCSPResponse:\n");
949	indent += 2;
950	doIndent(indent);
951	printf("ResponseData:\n");
952	parseResponseData(coder, indent + 2, basicResp.tbsResponseData);
953	doIndent(indent);
954	printf("sig: ");
955	printDataAsHex(&basicResp.sig, 8);
956	numCerts = ocspdArraySize((const void **)basicResp.certs);
957	doIndent(indent);
958	printf("Num Certs: %u\n", numCerts);
959
960	if(verbose) {
961		for(unsigned dex=0; dex<numCerts; dex++) {
962			printf("+++++++++++++++++++++++++ Cert %u +++++++++++++++++++++++++\n", dex);
963			printCert(basicResp.certs[dex]->Data, basicResp.certs[dex]->Length,
964				CSSM_FALSE);
965			printf("+++++++++++++++++++++++ End Cert %u +++++++++++++++++++++++\n", dex);
966		}
967	}
968	indent -= 2;		// end of BasicOCSPResponse
969	indent -= 2;		// end of ResponseBytes
970	indent -= 2;		// end of OCSPResponse
971errOut:
972	SecAsn1CoderRelease(coder);
973	return ortn;
974}
975
976static int postOcspReq(
977	CSSM_CL_HANDLE clHand,
978	const unsigned char *certFile,
979	unsigned certFileLen,
980	const unsigned char *issuerCertFile,
981	unsigned issuerCertFileLen,
982	const char *responderURI,
983	bool doParse,
984	bool verbose,
985	unsigned char **outFile,		// RETURNED
986	unsigned *outFileLen)			// RETURNED
987{
988	auto_ptr<CertParser> subject;
989	auto_ptr<CertParser> issuer;
990	CSSM_DATA uriData = {0, NULL};
991	CSSM_DATA *url = NULL;
992
993	try {
994		CSSM_DATA cdata = {certFileLen, (uint8 *)certFile};
995		subject.reset(new CertParser(clHand, cdata));
996	}
997	catch(...) {
998		printf("***Error parsing subject cert. Aborting.\n");
999		return -1;
1000	}
1001	try {
1002		CSSM_DATA cdata = {issuerCertFileLen, (uint8 *)issuerCertFile};
1003		issuer.reset(new CertParser(clHand, cdata));
1004	}
1005	catch(...) {
1006		printf("***Error parsing issuer cert. Aborting.\n");
1007		return -1;
1008	}
1009
1010	SecAsn1CoderRef coder;
1011	SecAsn1CoderCreate(&coder);
1012	/* subsequent errors to errOut: */
1013	int ourRtn = 0;
1014	const CSSM_DATA *derReq = NULL;
1015	auto_ptr<OCSPRequest> ocspReq;
1016
1017	if(responderURI != NULL) {
1018		uriData.Data = (uint8 *)responderURI;
1019		uriData.Length = strlen(responderURI);
1020		url = &uriData;
1021	}
1022	else {
1023		/* get OCSP URL from subject cert */
1024		url = ocspUrlFromCert(*subject, coder);
1025		if(url == NULL) {
1026			printf("Sorry, no can do.\n");
1027			ourRtn = -1;
1028			goto errOut;
1029		}
1030	}
1031
1032	/* create DER-encoded OCSP request for subject */
1033	try {
1034		ocspReq.reset(new OCSPRequest(*subject, *issuer, false));
1035		derReq = ocspReq->encode();
1036	}
1037	catch(...) {
1038		printf("***Error creating OCSP request. Aborting.\n");
1039		ourRtn = -1;
1040		goto errOut;
1041	}
1042
1043	/* do it */
1044	CSSM_DATA ocspResp;
1045	CSSM_RETURN crtn;
1046	crtn = ocspdHttpPost(coder, *url, *derReq, ocspResp);
1047	if(crtn) {
1048		printf("***Error fetching OCSP response***\n");
1049		cssmPerror("ocspdHttpPost", crtn);
1050		ourRtn = -1;
1051		goto errOut;
1052	}
1053	*outFile = ocspResp.Data;
1054	*outFileLen = ocspResp.Length;
1055	if(doParse) {
1056		parseOcspResp(clHand, ocspResp.Data, ocspResp.Length, verbose);
1057	}
1058	/* copy out */
1059	*outFile = (unsigned char *)malloc(ocspResp.Length);
1060	*outFileLen = ocspResp.Length;
1061	memmove(*outFile, ocspResp.Data, ocspResp.Length);
1062
1063errOut:
1064	SecAsn1CoderRelease(coder);
1065	return ourRtn;
1066}
1067
1068typedef enum {
1069	op_genReq,
1070	op_parseReq,
1071	op_genReply,
1072	op_parseResp,
1073	op_post
1074} ocspOp;
1075
1076int main(int argc, char **argv)
1077{
1078	if(argc < 2) {
1079		usage(argv);
1080	}
1081	ocspOp op;
1082	switch(argv[1][0]) {
1083		case 'g': op = op_genReq; break;
1084		case 'G': op = op_parseReq; break;
1085		case 'r': op = op_genReply; break;
1086		case 'R': op = op_parseResp; break;
1087		case 'p': op = op_post; break;
1088		default: usage(argv);
1089	}
1090
1091	/* user defined vars */
1092	char *inFile = NULL;
1093	char *outFile = NULL;
1094	char *inCertName = NULL;
1095	char *issuerCertName = NULL;
1096	SecAsn1OCSPCertStatusTag certStatus = CS_Good;
1097	CE_CrlReason crlReason = CE_CR_Unspecified;
1098	char *kcName = NULL;
1099	bool verbose = false;
1100	bool doParse = false;
1101	const char *responderURI = NULL;
1102
1103    extern int optind;
1104	optind = 2;
1105    extern char *optarg;
1106	int arg;
1107    while ((arg = getopt(argc, argv, "c:C:i:o:s:r:k:phvu:")) != -1) {
1108		switch (arg) {
1109			case 'c':
1110				inCertName = optarg;
1111				break;
1112			case 'C':
1113				issuerCertName = optarg;
1114				break;
1115			case 'i':
1116				inFile = optarg;
1117				break;
1118			case 'o':
1119				outFile = optarg;
1120				break;
1121			case 's':
1122				switch(optarg[0]) {
1123					case 'g':
1124						certStatus = CS_Good;
1125						break;
1126					case 'r':
1127						certStatus = CS_Revoked;
1128						break;
1129					case 'u':
1130						certStatus = CS_Unknown;
1131						break;
1132					default:
1133						printf("***Unrecognized certStatus***\n");
1134						usage(argv);
1135				}
1136				break;
1137			case 'r':
1138				crlReason = atoi(optarg);
1139				break;
1140			case 'k':
1141				kcName = optarg;
1142				break;
1143			case 'v':
1144				verbose = 1;
1145				break;
1146			case 'p':
1147				doParse = true;
1148				break;
1149			case 'u':
1150				responderURI = optarg;
1151				break;
1152			default:
1153			case '?':
1154				usage(argv);
1155		}
1156	}
1157	if(optind != argc) {
1158		/* this happens if you give getopt() an arg which doesn't start with '-' */
1159		usage(argv);
1160	}
1161
1162	unsigned char *certData = NULL;
1163	unsigned certDataLen = 0;
1164	unsigned char *issuerCertData = NULL;
1165	unsigned issuerCertDataLen = 0;
1166	unsigned char *inData = NULL;
1167	unsigned inDataLen = 0;
1168	unsigned char *outData = NULL;
1169	unsigned outDataLen = 0;
1170	SecKeychainRef kcRef = NULL;
1171	OSStatus ortn;
1172
1173	if(inCertName) {
1174		if(readFile(inCertName, &certData, &certDataLen)) {
1175			printf("***Error reading cert file %s. Aborting.\n", inCertName);
1176			exit(1);
1177		}
1178	}
1179	if(issuerCertName) {
1180		if(readFile(issuerCertName, &issuerCertData, &issuerCertDataLen)) {
1181			printf("***Error reading cert file %s. Aborting.\n", issuerCertName);
1182			exit(1);
1183		}
1184	}
1185	if(inFile) {
1186		if(readFile(inFile, &inData, &inDataLen)) {
1187			printf("***Error reading input file %s. Aborting.\n", inFile);
1188			exit(1);
1189		}
1190	}
1191	if(kcName) {
1192		ortn = SecKeychainOpen(kcName, &kcRef);
1193		if(ortn) {
1194			cssmPerror("SecKeychainOpen", ortn);
1195			return ortn;
1196		}
1197	}
1198	CSSM_CL_HANDLE clHand = cuClStartup();
1199
1200	switch(op) {
1201		case op_genReq:
1202			ortn = genOcspReq(clHand, certData, certDataLen,
1203				issuerCertData, issuerCertDataLen,
1204				&outData, &outDataLen);
1205			break;
1206		case op_parseReq:
1207			ortn = parseOcspReq(clHand, inData, inDataLen, verbose);
1208			break;
1209		case op_genReply:
1210		{
1211			SecIdentityRef idRef = NULL;
1212			ortn = sslSimpleIdentPicker(kcRef, &idRef);
1213			if(ortn) {
1214				printf("***Error choosing identity. Aborting.\n");
1215				exit(1);
1216			}
1217			ortn = genOcspResp(clHand, certStatus, crlReason,
1218				certData, certDataLen, issuerCertData, issuerCertDataLen,
1219				idRef, &outData, &outDataLen);
1220			CFRelease(idRef);
1221			break;
1222		}
1223		case op_parseResp:
1224			ortn = parseOcspResp(clHand, inData, inDataLen, verbose);
1225			break;
1226		case op_post:
1227			ortn = postOcspReq(clHand, certData, certDataLen,
1228				issuerCertData, issuerCertDataLen, responderURI,
1229				doParse, verbose,
1230				&outData, &outDataLen);
1231			break;
1232		default:
1233			printf("Op %s is not yet implemented.\n", argv[1]);
1234			exit(1);
1235	}
1236
1237	if(ortn == 0) {
1238		if(outData != NULL) {
1239			if(outFile== NULL) {
1240				printf("...generated %u bytes but no place to write it.\n", outDataLen);
1241			}
1242			else {
1243				ortn = writeFile(outFile, outData, outDataLen);
1244				if(ortn) {
1245					printf("***Error writing output to %s.\n", outFile);
1246				}
1247				else {
1248					printf("...wrote %u bytes to %s\n", outDataLen, outFile);
1249				}
1250			}
1251		}
1252	}
1253	return ortn;
1254}
1255