1/*
2 * Examine and test a keychain's identity
3 */
4#include <Security/Security.h>
5#include <stdlib.h>
6#include <stdio.h>
7#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
8#include <security_cdsa_utils/cuPrintCert.h>
9#include <utilLib/common.h>
10#include <utilLib/cspwrap.h>
11#include <clAppUtils/clutils.h>
12
13typedef enum {
14	KC_Nop,
15	KC_GetInfo,
16	KC_LockKC,
17	KC_UnlockKC,
18	KC_SignVfy,
19	KC_KeyCertInfo
20} KcOp;
21
22static void usage(char **argv)
23{
24	printf("Usage: %s keychain|- cmd [options]\n", argv[0]);
25	printf("Command:\n");
26	printf("  i  get KC info\n");
27	printf("  k  get key and cert info\n");
28	printf("  l  lock\n");
29	printf("  u  unlock\n");
30	printf("  s  sign and verify\n");
31	printf("Options:\n");
32	printf("  p=passphrase\n");
33	printf("Specifying '-' for keychain means NULL, default\n");
34	exit(1);
35}
36
37static void showError(
38	OSStatus ortn,
39	const char *msg)
40{
41	const char *errStr = NULL;
42	switch(ortn) {
43		case errSecItemNotFound:
44			errStr = "errSecItemNotFound"; break;
45		case errSecNoSuchKeychain:
46			errStr = "errSecNoSuchKeychain"; break;
47		case errSecNotAvailable:
48			errStr = "errSecNotAvailable"; break;
49		/* more? */
50		default:
51			if(ortn < (CSSM_BASE_ERROR +
52					(CSSM_ERRORCODE_MODULE_EXTENT * 8))) {
53				/* assume CSSM error */
54				errStr = cssmErrToStr(ortn);
55			}
56			break;
57
58	}
59	if(errStr) {
60		printf("***Error on %s: %s\n", msg, errStr);
61	}
62	else {
63		printf("***Error on %s: %d(d)\n", msg, (int)ortn);
64	}
65}
66
67static void printDataAsHex(
68	const CSSM_DATA *d,
69	unsigned maxToPrint = 0)		// optional, 0 means print it all
70{
71	unsigned i;
72	bool more = false;
73	uint32 len = d->Length;
74	uint8 *cp = d->Data;
75
76	if((maxToPrint != 0) && (len > maxToPrint)) {
77		len = maxToPrint;
78		more = true;
79	}
80	for(i=0; i<len; i++) {
81		printf("%02X ", ((unsigned char *)cp)[i]);
82	}
83	if(more) {
84		printf("...\n");
85	}
86	else {
87		printf("\n");
88	}
89}
90
91static void printKeyHeader(
92	const CSSM_KEYHEADER &hdr)
93{
94	printf("   Algorithm       : ");
95	switch(hdr.AlgorithmId) {
96		case CSSM_ALGID_RSA:
97			printf("RSA\n");
98			break;
99		case CSSM_ALGID_DSA:
100			printf("DSA\n");
101			break;
102		case CSSM_ALGID_FEE:
103			printf("FEE\n");
104			break;
105		case CSSM_ALGID_DH:
106			printf("Diffie-Hellman\n");
107			break;
108		default:
109			printf("Unknown(%u(d), 0x%x)\n", (unsigned)hdr.AlgorithmId,
110				(unsigned)hdr.AlgorithmId);
111	}
112	printf("   Key Size        : %u bits\n",
113		(unsigned)hdr.LogicalKeySizeInBits);
114	printf("   Key Use         : ");
115	CSSM_KEYUSE usage = hdr.KeyUsage;
116	if(usage & CSSM_KEYUSE_ANY) {
117		printf("CSSM_KEYUSE_ANY ");
118	}
119	if(usage & CSSM_KEYUSE_ENCRYPT) {
120		printf("CSSM_KEYUSE_ENCRYPT ");
121	}
122	if(usage & CSSM_KEYUSE_DECRYPT) {
123		printf("CSSM_KEYUSE_DECRYPT ");
124	}
125	if(usage & CSSM_KEYUSE_SIGN) {
126		printf("CSSM_KEYUSE_SIGN ");
127	}
128	if(usage & CSSM_KEYUSE_VERIFY) {
129		printf("CSSM_KEYUSE_VERIFY ");
130	}
131	if(usage & CSSM_KEYUSE_SIGN_RECOVER) {
132		printf("CSSM_KEYUSE_SIGN_RECOVER ");
133	}
134	if(usage & CSSM_KEYUSE_VERIFY_RECOVER) {
135		printf("CSSM_KEYUSE_VERIFY_RECOVER ");
136	}
137	if(usage & CSSM_KEYUSE_WRAP) {
138		printf("CSSM_KEYUSE_WRAP ");
139	}
140	if(usage & CSSM_KEYUSE_UNWRAP) {
141		printf("CSSM_KEYUSE_UNWRAP ");
142	}
143	if(usage & CSSM_KEYUSE_DERIVE) {
144		printf("CSSM_KEYUSE_DERIVE ");
145	}
146	printf("\n");
147
148}
149
150static OSStatus getIdentity(
151	SecKeychainRef kcRef,
152	CSSM_KEYUSE keyUse,
153	SecIdentityRef &idRef)
154{
155	SecIdentitySearchRef srchRef = nil;
156	OSStatus ortn = SecIdentitySearchCreate(kcRef, keyUse, &srchRef);
157	if(ortn) {
158		showError(ortn, "SecIdentitySearchCreate");
159		return ortn;
160	}
161	ortn = SecIdentitySearchCopyNext(srchRef, &idRef);
162	if(ortn) {
163		showError(ortn, "SecIdentitySearchCopyNext");
164		return ortn;
165	}
166	if(CFGetTypeID(idRef) != SecIdentityGetTypeID()) {
167		printf("SecIdentitySearchCopyNext CFTypeID failure!\n");
168		return paramErr;
169	}
170	return noErr;
171}
172
173static OSStatus getKeyCertInfo(
174	SecCertificateRef certRef,
175	SecKeyRef keyRef,
176	CSSM_KEY_PTR cssmKey,
177	CSSM_CSP_HANDLE cspHand)
178{
179	/* display the private key */
180	if(cssmKey == NULL) {
181		printf("   ***malformed CSSM_KEY\n");
182	}
183	else {
184		printf("Private Key        :\n");
185		printKeyHeader(cssmKey->KeyHeader);
186		printf("   Key Blob        : ");
187		printDataAsHex(&cssmKey->KeyData, 8);
188	}
189
190	/* and the cert */
191	CSSM_DATA certData;
192	OSStatus ortn = SecCertificateGetData(certRef, &certData);
193	if(ortn) {
194		showError(ortn, "SecCertificateGetData");
195		return ortn;
196	}
197	printf("\nCertificate        :\n");
198	printCert((unsigned char *)certData.Data, (unsigned)certData.Length,
199		CSSM_TRUE);
200	return noErr;
201}
202
203#define SIG_ALG		CSSM_ALGID_SHA1WithRSA
204
205static OSStatus signVfy(
206	SecCertificateRef certRef,
207	SecKeyRef keyRef,
208	CSSM_KEY_PTR cssmKey,
209	CSSM_CSP_HANDLE cspHand)
210{
211	uint8 someData[] = {0,1,2,3,4,5,6,7,8};
212	CSSM_DATA ptext = {sizeof(someData), someData};
213	CSSM_DATA sig = {0, NULL};
214	CSSM_RETURN crtn;
215
216	/* sign with CSPDL */
217	crtn = cspSign(cspHand, SIG_ALG, cssmKey, &ptext, &sig);
218	if(crtn) {
219		printf("Error signing with private key\n");
220		return crtn;
221	}
222
223	/* attach to CL */
224	CSSM_CL_HANDLE clHand = clStartup();
225	if(clHand == 0) {
226		printf("***Error attaching to CL\n");
227		return ioErr;
228	}
229
230	/* get the public key from the cert */
231	CSSM_DATA certData;
232	OSStatus ortn = SecCertificateGetData(certRef, &certData);
233	if(ortn) {
234		showError(ortn, "SecCertificateGetData");
235		return ortn;
236	}
237	CSSM_KEY_PTR pubKey = NULL;
238	crtn = CSSM_CL_CertGetKeyInfo(clHand, &certData, &pubKey);
239	if(crtn) {
240		printError("CSSM_CL_CertGetKeyInfo", crtn);
241		return crtn;
242	}
243
244	/* attach to raw CSP */
245	CSSM_CSP_HANDLE rawCspHand = cspStartup();
246	if(rawCspHand == 0) {
247		printf("***Error attaching to raw CSP\n");
248		return ioErr;
249	}
250
251	/* verify with raw CSP and raw public key */
252	crtn = cspSigVerify(rawCspHand, SIG_ALG, pubKey, &ptext,
253		&sig, CSSM_OK);
254	if(crtn) {
255		printf("Error verifying with public key\n");
256		return crtn;
257	}
258
259	/* free everything */
260	CSSM_ModuleDetach(rawCspHand);
261	CSSM_ModuleDetach(clHand);
262	printf("...sign with private key, vfy with cert OK\n");
263	return noErr;
264}
265
266/* get cert and private key (in Sec and CSSM form) from identity */
267static OSStatus getKeyCert(
268	SecIdentityRef 		idRef,
269	SecCertificateRef	&certRef,		// RETURNED
270	SecKeyRef			&keyRef,		// private key, RETURNED
271	CSSM_KEY_PTR		&cssmKey)		// private key, RETURNED
272{
273	OSStatus ortn = SecIdentityCopyCertificate(idRef, &certRef);
274	if(ortn) {
275		showError(ortn, "SecIdentityCopyCertificate");
276		return ortn;
277	}
278	ortn = SecIdentityCopyPrivateKey(idRef, &keyRef);
279	if(ortn) {
280		showError(ortn, "SecIdentityCopyPrivateKey");
281		return ortn;
282	}
283	ortn = SecKeyGetCSSMKey(keyRef, (const CSSM_KEY **)&cssmKey);
284	if(ortn) {
285		showError(ortn, "SecKeyGetCSSMKey");
286	}
287	return ortn;
288}
289
290int main(int argc, char **argv)
291{
292	SecKeychainRef 		kcRef = nil;
293	OSStatus 			ortn;
294	int					arg;
295	char				*argp;
296
297	/* user-spec'd variables */
298	KcOp				op = KC_Nop;
299	char 				*pwd = NULL;
300	char				*kcName;
301
302	if(argc < 3) {
303		usage(argv);
304	}
305	kcName = argv[1];
306	if(!strcmp("-", kcName)) {
307		/* null - no open */
308		kcName = NULL;
309	}
310	switch(argv[2][0]) {
311		case 'i':
312			op = KC_GetInfo; break;
313		case 'l':
314			op = KC_LockKC; break;
315		case 'u':
316			op = KC_UnlockKC; break;
317		case 's':
318			op = KC_SignVfy; break;
319		case 'k':
320			op = KC_KeyCertInfo; break;
321		default:
322			usage(argv);
323	}
324	for(arg=3; arg<argc; arg++) {
325		argp = argv[arg];
326		switch(argp[0]) {
327			case 'p':
328				pwd = &argp[2];
329				break;
330			default:
331				usage(argv);
332		}
333	}
334
335	if(kcName != NULL) {
336		ortn = SecKeychainOpen(kcName, &kcRef);
337		if(ortn) {
338			showError(ortn, "SecKeychainOpen");
339			printf("Cannot open keychain at %s. Aborting.\n", kcName);
340			exit(1);
341		}
342	}
343
344	/* handle trivial commands right now */
345	switch(op) {
346		case KC_LockKC:
347			ortn = SecKeychainLock(kcRef);
348			if(ortn) {
349				showError(ortn, "SecKeychainLock");
350				exit(1);
351			}
352			printf("...keychain %s locked.\n", argv[1]);
353			exit(0);
354
355		case KC_UnlockKC:
356			if(pwd == NULL) {
357				printf("***Warning: unlocking with no password\n");
358			}
359			ortn = SecKeychainUnlock(kcRef,
360				pwd ? strlen(pwd) : 0,
361				pwd,
362				pwd ? true : false);		// usePassword
363			if(ortn) {
364				showError(ortn, "SecKeychainUnlock");
365				exit(1);
366			}
367			printf("...keychain %s unlocked.\n", argv[1]);
368			exit(0);
369
370		case KC_GetInfo:
371		{
372			SecKeychainStatus kcStat;
373			ortn = SecKeychainGetStatus(kcRef, &kcStat);
374			if(ortn) {
375				showError(ortn, "SecKeychainGetStatus");
376				exit(1);
377			}
378			printf("...SecKeychainStatus = %u ( ", (unsigned)kcStat);
379			if(kcStat & kSecUnlockStateStatus) {
380				printf("UnlockState ");
381			}
382			if(kcStat & kSecReadPermStatus) {
383				printf("RdPerm ");
384			}
385			if(kcStat & kSecWritePermStatus) {
386				printf("WrPerm ");
387			}
388			printf(")\n");
389			exit(0);
390		}
391
392		default:
393			/* more processing below */
394			break;
395	}
396
397	/* remaining cmds need an identity */
398	SecIdentityRef idRef;
399	ortn = getIdentity(kcRef, CSSM_KEYUSE_SIGN, idRef);
400	if(ortn) {
401		printf("***No identity found in keychain %s. Aborting.\n", kcName);
402		exit(1);
403	}
404
405	/* and a CSP */
406	CSSM_CSP_HANDLE cspHand;
407	ortn = SecKeychainGetCSPHandle(kcRef, &cspHand);
408	if(ortn) {
409		showError(ortn, "SecKeychainGetCSPHandle");
410		exit(1);
411	}
412
413	/* and the cert and keys */
414	SecCertificateRef certRef = nil;
415	SecKeyRef keyRef = nil;
416	CSSM_KEY_PTR privKey = NULL;
417	ortn = getKeyCert(idRef, certRef, keyRef, privKey);
418	if(ortn) {
419		printf("***Incomplete identity\n");
420		exit(1);
421	}
422
423	switch(op) {
424		case KC_KeyCertInfo:
425			ortn = getKeyCertInfo(certRef, keyRef, privKey, cspHand);
426			break;
427		case KC_SignVfy:
428			ortn = signVfy(certRef, keyRef, privKey, cspHand);
429			break;
430		default:
431			printf("BRRRZAP!\n");
432			exit(1);
433	}
434	CFRelease(idRef);
435	if(kcRef) {
436		CFRelease(kcRef);
437	}
438	return (int)ortn;
439}
440