1/*
2 * clearPubKeyTest.cpp
3 *
4 * Test CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT. This cannot be run on a handsoff environment;
5 * it forces Keychain unlock dialogs.
6 */
7
8#include <stdlib.h>
9#include <strings.h>
10#include <stdio.h>
11#include <unistd.h>
12#include <Security/Security.h>
13#include "cspwrap.h"
14#include "common.h"
15
16#define KEYCHAIN_NAME	"/tmp/clearPubKey.keychain"
17#define KEYCHAIN_PWD	"pwd"
18#define KEY_ALG			CSSM_ALGID_RSA
19#define KEYSIZE			1024
20#define ENCRALG			CSSM_ALGID_RSA
21
22static void usage(char **argv)
23{
24	printf("usage: %s -v(erbose)\n", argv[0]);
25	exit(1);
26}
27
28static void printNoDialog()
29{
30	printf("*** If you get a keychain unlock dialog here the test is failing ***\n");
31}
32
33static void printExpectDialog()
34{
35	printf("*** You MUST get a keychain unlock dialog here (password = '%s') ***\n",
36		KEYCHAIN_PWD);
37}
38
39static bool didGetDialog()
40{
41	fpurge(stdin);
42	printf("Enter 'y' if you just got a keychain unlock dialog: ");
43	if(getchar() == 'y') {
44		return 1;
45	}
46	printf("***Well, you really should have. Test failed.\n");
47	return 0;
48}
49
50static void verboseDisp(bool verbose, const char *str)
51{
52	if(verbose) {
53		printf("...%s\n", str);
54	}
55}
56
57/* generate key pair, optionally storing the public key in encrypted form */
58static int genKeyPair(
59	bool pubKeyIsEncrypted,
60	SecKeychainRef kcRef,
61	SecKeyRef *pubKeyRef,
62	SecKeyRef *privKeyRef)
63{
64	/* gather keygen args */
65	CSSM_ALGORITHMS keyAlg = KEY_ALG;
66	uint32 keySizeInBits = KEYSIZE;
67	CSSM_KEYUSE pubKeyUsage = CSSM_KEYUSE_ANY;
68	uint32 pubKeyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT;
69	if(pubKeyIsEncrypted) {
70		pubKeyAttr |= CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT;
71	}
72	CSSM_KEYUSE privKeyUsage = CSSM_KEYUSE_ANY;
73	uint32 privKeyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE |
74						 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE;
75
76	OSStatus ortn = SecKeyCreatePair(kcRef, keyAlg, keySizeInBits, 0,
77		pubKeyUsage, pubKeyAttr,
78		privKeyUsage, privKeyAttr,
79		NULL,		// default initial access for now
80		pubKeyRef, privKeyRef);
81	if(ortn) {
82		cssmPerror("SecKeyCreatePair", ortn);
83		return 1;
84	}
85	return 0;
86}
87
88/* encrypt something with a public key */
89static int pubKeyEncrypt(
90	SecKeyRef pubKeyRef)
91{
92	const CSSM_KEY *cssmKey;
93	OSStatus ortn;
94	CSSM_CSP_HANDLE cspHand;
95
96	ortn = SecKeyGetCSSMKey(pubKeyRef, &cssmKey);
97	if(ortn) {
98		cssmPerror("SecKeyGetCSSMKey", ortn);
99		return -1;
100	}
101	ortn = SecKeyGetCSPHandle(pubKeyRef, &cspHand);
102	if(ortn) {
103		cssmPerror("SecKeyGetCSPHandle", ortn);
104		return -1;
105	}
106
107	char *ptext = "something to encrypt";
108	CSSM_DATA ptextData = {strlen(ptext), (uint8 *)ptext};
109	CSSM_DATA ctextData = { 0, NULL };
110	CSSM_RETURN crtn;
111
112	crtn = cspEncrypt(cspHand, CSSM_ALGID_RSA,
113		0, CSSM_PADDING_PKCS1,	// mode/pad
114		cssmKey, NULL,
115		0, 0,	// effect/rounds
116		NULL,	// IV
117		&ptextData, &ctextData, CSSM_FALSE);
118	if(crtn) {
119		return -1;
120	}
121	/* slighyly hazardous, allocated by CSPDL's allocator */
122	free(ctextData.Data);
123	return 0;
124}
125
126int main(int argc, char **argv)
127{
128	bool verbose = false;
129
130	int arg;
131	while ((arg = getopt(argc, argv, "vh")) != -1) {
132		switch (arg) {
133			case 'v':
134				verbose = true;
135				break;
136			case 'h':
137				usage(argv);
138		}
139	}
140	if(optind != argc) {
141		usage(argv);
142	}
143
144	printNoDialog();
145
146	/* initial setup */
147	verboseDisp(verbose, "deleting keychain");
148	unlink(KEYCHAIN_NAME);
149
150	verboseDisp(verbose, "creating keychain");
151	SecKeychainRef kcRef = NULL;
152	OSStatus ortn = SecKeychainCreate(KEYCHAIN_NAME,
153		strlen(KEYCHAIN_PWD), KEYCHAIN_PWD,
154		false, NULL, &kcRef);
155	if(ortn) {
156		cssmPerror("SecKeychainCreate", ortn);
157		exit(1);
158	}
159
160	/*
161	 * 1. Generate key pair with cleartext public key.
162	 *    Ensure we can use the public key when keychain is locked with no
163	 *    user interaction.
164	 */
165
166	/* generate key pair, cleartext public key */
167	verboseDisp(verbose, "creating key pair, cleartext public key");
168	SecKeyRef pubKeyRef = NULL;
169	SecKeyRef privKeyRef = NULL;
170	if(genKeyPair(false, kcRef, &pubKeyRef, &privKeyRef)) {
171		exit(1);
172	}
173
174	/* Use generated cleartext public key with locked keychain */
175	verboseDisp(verbose, "locking keychain, exporting public key");
176	SecKeychainLock(kcRef);
177	CFDataRef exportData = NULL;
178	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
179	if(ortn) {
180		cssmPerror("SecKeychainCreate", ortn);
181		exit(1);
182	}
183	CFRelease(exportData);
184
185	verboseDisp(verbose, "locking keychain, encrypting with public key");
186	SecKeychainLock(kcRef);
187	if(pubKeyEncrypt(pubKeyRef)) {
188		exit(1);
189	}
190
191	/* reset */
192	verboseDisp(verbose, "deleting keys");
193	ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef);
194	if(ortn) {
195		cssmPerror("SecKeychainItemDelete", ortn);
196		exit(1);
197	}
198	ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef);
199	if(ortn) {
200		cssmPerror("SecKeychainItemDelete", ortn);
201		exit(1);
202	}
203	CFRelease(pubKeyRef);
204	CFRelease(privKeyRef);
205
206	/*
207	 * 2. Generate key pair with encrypted public key.
208	 *    Ensure that user interaction is required when we use the public key
209	 *    when keychain is locked.
210	 */
211
212	verboseDisp(verbose, "programmatically unlocking keychain");
213	ortn = SecKeychainUnlock(kcRef, strlen(KEYCHAIN_PWD), KEYCHAIN_PWD, TRUE);
214	if(ortn) {
215		cssmPerror("SecKeychainItemDelete", ortn);
216		exit(1);
217	}
218
219	/* generate key pair, encrypted public key */
220	verboseDisp(verbose, "creating key pair, encrypted public key");
221	if(genKeyPair(true, kcRef, &pubKeyRef, &privKeyRef)) {
222		exit(1);
223	}
224
225	/* Use generated encrypted public key with locked keychain */
226	verboseDisp(verbose, "locking keychain, exporting public key");
227	SecKeychainLock(kcRef);
228	printExpectDialog();
229	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
230	if(ortn) {
231		cssmPerror("SecKeychainCreate", ortn);
232		exit(1);
233	}
234	/* we'll use that exported blob later to test import */
235	if(!didGetDialog()) {
236		exit(1);
237	}
238
239	verboseDisp(verbose, "locking keychain, encrypting with public key");
240	SecKeychainLock(kcRef);
241	printExpectDialog();
242	if(pubKeyEncrypt(pubKeyRef)) {
243		exit(1);
244	}
245	if(!didGetDialog()) {
246		exit(1);
247	}
248
249	/* reset */
250	printNoDialog();
251	verboseDisp(verbose, "locking keychain");
252	SecKeychainLock(kcRef);
253	verboseDisp(verbose, "deleting keys");
254	ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef);
255	if(ortn) {
256		cssmPerror("SecKeychainItemDelete", ortn);
257		exit(1);
258	}
259	ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef);
260	if(ortn) {
261		cssmPerror("SecKeychainItemDelete", ortn);
262		exit(1);
263	}
264	CFRelease(pubKeyRef);
265	CFRelease(privKeyRef);
266
267	/*
268	 * 3. Import public key, storing in cleartext. Ensure that the import
269	 *    doesn't require unlock, and ensure we can use the public key
270	 *    when keychain is locked with no user interaction.
271	 */
272
273	printNoDialog();
274	verboseDisp(verbose, "locking keychain");
275	SecKeychainLock(kcRef);
276
277	/* import public key - default is in the clear */
278	verboseDisp(verbose, "importing public key, store in the clear (default)");
279	CFArrayRef outArray = NULL;
280	SecExternalFormat format = kSecFormatOpenSSL;
281	SecExternalItemType type = kSecItemTypePublicKey;
282	ortn = SecKeychainItemImport(exportData,
283		NULL, &format, &type,
284		0, NULL,
285		kcRef, &outArray);
286	if(ortn) {
287		cssmPerror("SecKeychainItemImport", ortn);
288		exit(1);
289	}
290	CFRelease(exportData);
291	if(CFArrayGetCount(outArray) != 1) {
292		printf("***Unexpected outArray size (%ld) after import\n",
293			(long)CFArrayGetCount(outArray));
294		exit(1);
295	}
296	pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0);
297	if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) {
298		printf("***Unexpected item type after import\n");
299		exit(1);
300	}
301
302	/* Use imported cleartext public key with locked keychain */
303	verboseDisp(verbose, "locking keychain, exporting public key");
304	SecKeychainLock(kcRef);
305	exportData = NULL;
306	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
307	if(ortn) {
308		cssmPerror("SecKeychainItemExport", ortn);
309		exit(1);
310	}
311	/* we'll use exportData again */
312
313	verboseDisp(verbose, "locking keychain, encrypting with public key");
314	SecKeychainLock(kcRef);
315	if(pubKeyEncrypt(pubKeyRef)) {
316		exit(1);
317	}
318
319	/* reset */
320	verboseDisp(verbose, "deleting key");
321	ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef);
322	if(ortn) {
323		cssmPerror("SecKeychainItemDelete", ortn);
324		exit(1);
325	}
326	CFRelease(pubKeyRef);
327
328	/*
329	 * Import public key, storing in encrypted form.
330	 * Ensure that user interaction is required when we use the public key
331	 * when keychain is locked.
332	 */
333
334	/* import public key, encrypted in the keychain */
335	SecKeyImportExportParameters impExpParams;
336	memset(&impExpParams, 0, sizeof(impExpParams));
337	impExpParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
338	impExpParams.keyAttributes = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE |
339								 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT;
340	verboseDisp(verbose, "importing public key, store encrypted");
341	printExpectDialog();
342	outArray = NULL;
343	format = kSecFormatOpenSSL;
344	type = kSecItemTypePublicKey;
345	ortn = SecKeychainItemImport(exportData,
346		NULL, &format, &type,
347		0, &impExpParams,
348		kcRef, &outArray);
349	if(ortn) {
350		cssmPerror("SecKeychainItemImport", ortn);
351		exit(1);
352	}
353	if(!didGetDialog()) {
354		exit(1);
355	}
356	CFRelease(exportData);
357	if(CFArrayGetCount(outArray) != 1) {
358		printf("***Unexpected outArray size (%ld) after import\n",
359			(long)CFArrayGetCount(outArray));
360		exit(1);
361	}
362	pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0);
363	if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) {
364		printf("***Unexpected item type after import\n");
365		exit(1);
366	}
367
368	/* Use imported encrypted public key with locked keychain */
369	verboseDisp(verbose, "locking keychain, exporting public key");
370	SecKeychainLock(kcRef);
371	printExpectDialog();
372	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
373	if(ortn) {
374		cssmPerror("SecKeychainItemExport", ortn);
375		exit(1);
376	}
377	if(!didGetDialog()) {
378		exit(1);
379	}
380	CFRelease(exportData);
381
382	verboseDisp(verbose, "locking keychain, encrypting with public key");
383	SecKeychainLock(kcRef);
384	printExpectDialog();
385	if(pubKeyEncrypt(pubKeyRef)) {
386		exit(1);
387	}
388	if(!didGetDialog()) {
389		exit(1);
390	}
391
392	SecKeychainDelete(kcRef);
393	printf("...test succeeded.\n");
394	return 0;
395}
396