1/*
2 * Copyright (c) 2008 Apple Inc. All Rights Reserved.
3 *
4 * ecdhTest.cpp - Test Elliptic Curve Diffie-Hellman key exchange.
5 *
6 * Created Jan. 1 2008 by Doug Mitchell.
7 */
8
9#include <stdlib.h>
10#include <strings.h>
11#include <stdio.h>
12#include <unistd.h>
13#include <Security/cssm.h>
14#include "cspwrap.h"
15#include "common.h"
16
17#define LOOPS_DEF			32
18#define KEY_SIZE_DEF		256
19
20static void usage(char **argv)
21{
22	printf("usage: %s [options]\n", argv[0]);
23	printf("Options:\n");
24	printf("  k=keySize (default = %d)\n", KEY_SIZE_DEF);
25	printf("  X (X9.63 key derivation)\n");
26	printf("  l=loops (0=forever)\n");
27	printf("  D (CSP/DL; default = bare CSP)\n");
28	printf("  q(uiet)\n");
29	printf("  v(erbose))\n");
30	exit(1);
31}
32
33#define LABEL_DEF				"noLabel"
34#define MAX_SHARED_INFO_LEN		400
35#define MAX_DERIVED_SIZE		1024
36
37static int doECDH(
38	CSSM_CSP_HANDLE cspHand,
39	CSSM_KEY_PTR	privKey,
40	/*
41	 * pubKey:
42	 *   Ref form - use key as pubKey as is
43	 *   X509 form - use as is
44	 *   OCTET_STRING form - use key data as Param
45	 */
46	CSSM_KEY_PTR	pubKey,
47	CSSM_BOOL		bareCsp,	// false --> derive ref key and NULL-wrap it
48	CSSM_BOOL		x963KDF,
49	CSSM_DATA		*sharedInfo,
50	uint32			deriveSizeInBits,
51	CSSM_BOOL		quiet,
52	CSSM_BOOL		verbose,
53
54	/* result RETURNED here */
55	CSSM_KEY_PTR	derivedKey)
56
57{
58	CSSM_DATA paramData = {0, NULL};
59	CSSM_KEY_PTR contextPubKey = NULL;
60	CSSM_KEYHEADER_PTR hdr = &pubKey->KeyHeader;
61
62	if((hdr->BlobType == CSSM_KEYBLOB_RAW) &&
63	   (hdr->Format == CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING)) {
64		paramData = pubKey->KeyData;
65	}
66	else {
67		contextPubKey = pubKey;
68	}
69
70	/* create key derivation context */
71	CSSM_RETURN 			crtn;
72	CSSM_ACCESS_CREDENTIALS	creds;
73	CSSM_CC_HANDLE			ccHand;
74
75	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
76
77	CSSM_ALGORITHMS deriveAlg;
78	if(x963KDF) {
79		deriveAlg = CSSM_ALGID_ECDH_X963_KDF;
80	}
81	else {
82		deriveAlg = CSSM_ALGID_ECDH;
83	}
84
85	crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
86		deriveAlg,
87		CSSM_ALGID_RC4,	// doesn't matter, just give us the bits
88		deriveSizeInBits,
89		&creds,
90		privKey,		// BaseKey
91		0,				// IterationCount
92		sharedInfo,		// Salt
93		0,				// Seed
94		&ccHand);
95	if(crtn) {
96		printError("CSSM_CSP_CreateDeriveKeyContext", crtn);
97		return testError(quiet);
98	}
99
100	if(contextPubKey != NULL) {
101		/* add pub key as a context attr */
102		crtn = AddContextAttribute(ccHand,
103			CSSM_ATTRIBUTE_PUBLIC_KEY,
104			sizeof(CSSM_KEY),
105			CAT_Ptr,
106			(void *)contextPubKey,
107			0);
108		if(crtn) {
109			printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY)",
110				crtn);
111			return crtn;
112		}
113	}
114
115	/* D-H derive key */
116	CSSM_DATA	labelData = { strlen(LABEL_DEF), (uint8 *)LABEL_DEF };
117	CSSM_KEYATTR_FLAGS keyAttr = bareCsp ?
118		(CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE) :
119		(CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE);
120	memset(derivedKey, 0, sizeof(CSSM_KEY));
121	crtn = CSSM_DeriveKey(ccHand,
122		&paramData,
123		CSSM_KEYUSE_ANY,
124		keyAttr,
125		&labelData,
126		NULL,				// cread/acl
127		derivedKey);
128	if(crtn) {
129		printError("CSSM_DeriveKey", crtn);
130	}
131	CSSM_DeleteContext(ccHand);
132	if(crtn) {
133		return testError(quiet);
134	}
135
136	if(!bareCsp) {
137		/* Got a ref key, give caller raw */
138		CSSM_KEY refKey = *derivedKey;
139		crtn = cspRefKeyToRaw(cspHand, &refKey, derivedKey);
140		cspFreeKey(cspHand, &refKey);
141	}
142	return 0;
143}
144
145/* define public key style */
146typedef enum {
147	PKT_Ref,		/* ref key */
148	PKT_Wrap,		/* generate ref key, wrap to OCTET_STRING */
149	PKT_X509,		/* raw key X509 format */
150	PKT_Octet		/* generate to OCTET_STRING form */
151} PubKeyType;
152
153#define BoolStr(v)	(v ? "true " : "false")
154
155static const char *KeyStypeStr(
156	PubKeyType keyType)
157{
158	switch(keyType) {
159		case PKT_Ref:	return "Ref";
160		case PKT_Wrap:	return "Ref->Wrap";
161		case PKT_X509:	return "X509";
162		case PKT_Octet:	return "X9.62";
163		default:		return "BRRZAP";
164	}
165}
166
167static int doTest(
168	CSSM_CSP_HANDLE cspHand,
169	CSSM_BOOL		ourKeysRef,			/* our keys are reference */
170	CSSM_BOOL		theirPrivKeyRef,	/* their private key is reference */
171	PubKeyType		theirPubKeyType,
172	unsigned		keySizeBits,
173	CSSM_BOOL		bareCsp,
174	CSSM_BOOL		x963KDF,
175	CSSM_BOOL		useSharedInfo,		/* use the optional SharedInfo for x963KDF */
176	CSSM_BOOL		verbose,
177	CSSM_BOOL		quiet)
178{
179
180	CSSM_RETURN crtn;
181	CSSM_KEY ourPriv;
182	CSSM_KEY ourPub;
183	bool ourKeysGend = false;
184	bool theirKeysGend = false;
185	bool wrappedTheirPub = false;
186	bool wrappedOurPub = false;
187	bool derivedKey1 = false;
188	bool derivedKey2 = false;
189	CSSM_DATA sharedInfo = {0, NULL};
190	uint32 deriveSizeInBits;
191
192	if(x963KDF) {
193		/* arbitrary derived size */
194		deriveSizeInBits = genRand(1, MAX_DERIVED_SIZE);
195	}
196	else {
197		deriveSizeInBits = keySizeBits;
198	}
199	if(useSharedInfo) {
200		/* length should be totally arbitrary */
201		appSetupCssmData(&sharedInfo, MAX_SHARED_INFO_LEN);
202		simpleGenData(&sharedInfo, 1, MAX_SHARED_INFO_LEN);
203	}
204
205
206	if(!quiet) {
207		if(x963KDF) {
208			printf("...sharedInfoLen %4lu deriveSize %4lu ",
209				(unsigned long)sharedInfo.Length, (unsigned long)deriveSizeInBits);
210		}
211		else {
212			printf("...");
213		}
214		printf("ourRef %s theirPrivRef %s theirPub %s\n",
215			BoolStr(ourKeysRef), BoolStr(theirPrivKeyRef),
216			KeyStypeStr(theirPubKeyType));
217	}
218
219	crtn = cspGenKeyPair(cspHand, CSSM_ALGID_ECDSA,
220		LABEL_DEF, strlen(LABEL_DEF), keySizeBits,
221		&ourPub, ourKeysRef, CSSM_KEYUSE_DERIVE, CSSM_KEYBLOB_RAW_FORMAT_NONE,
222		&ourPriv, ourKeysRef, CSSM_KEYUSE_DERIVE, CSSM_KEYBLOB_RAW_FORMAT_NONE,
223		CSSM_FALSE);
224	if(crtn) {
225		return testError(quiet);
226	}
227	ourKeysGend = true;
228
229	CSSM_KEY theirPriv;
230	CSSM_KEY theirPub;			/* the generated one */
231	CSSM_KEY theirWrappedPub;	/* optional NULL unwrap */
232	CSSM_KEY_PTR theirPubPtr;
233	CSSM_KEY ourWrappedPub;	/* optional NULL unwrap */
234	CSSM_KEY_PTR ourPubPtr;
235	CSSM_KEY derived1;
236	CSSM_KEY derived2;
237	CSSM_BOOL pubIsRef = CSSM_FALSE;
238	CSSM_KEYBLOB_FORMAT blobForm = CSSM_KEYBLOB_RAW_FORMAT_NONE;
239	int ourRtn = 0;
240
241	switch(theirPubKeyType) {
242		case PKT_Ref:
243		case PKT_Wrap:
244			pubIsRef = CSSM_TRUE;
245			break;
246		case PKT_X509:
247			pubIsRef = CSSM_FALSE;
248			break;
249		case PKT_Octet:
250			pubIsRef = CSSM_FALSE;
251			blobForm = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
252			break;
253	}
254
255	crtn = cspGenKeyPair(cspHand, CSSM_ALGID_ECDSA,
256		LABEL_DEF, strlen(LABEL_DEF), keySizeBits,
257		&theirPub, pubIsRef, CSSM_KEYUSE_DERIVE, CSSM_KEYBLOB_RAW_FORMAT_NONE,
258		&theirPriv, theirPrivKeyRef, CSSM_KEYUSE_DERIVE, CSSM_KEYBLOB_RAW_FORMAT_NONE,
259		CSSM_FALSE);
260	if(crtn) {
261		ourRtn = testError(quiet);
262		goto errOut;
263	}
264
265	if(theirPubKeyType == PKT_Wrap) {
266		/*
267		 * This test mode is here mainly to ring out the key wrap and
268		 * OCTET_STRING format functionality in the CrypkitCSP, it's
269		 * not really relevant to ECDH...
270		 */
271		crtn = cspRefKeyToRawWithFormat(cspHand, &theirPub,
272			CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING, &theirWrappedPub);
273		if(crtn) {
274			ourRtn = testError(quiet);
275			goto errOut;
276		}
277		theirPubPtr = &theirWrappedPub;
278		wrappedTheirPub = true;
279	}
280	else {
281		theirPubPtr = &theirPub;
282	}
283
284	if(!bareCsp) {
285		/*
286		 * For CSPDL, convert our pub key to OCTET_STRING format so it
287		 * is sent as a Param - can't send a ref key (or any other pub
288		 * key) in the context
289		 */
290		crtn = cspRefKeyToRawWithFormat(cspHand, &ourPub,
291			CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING, &ourWrappedPub);
292		if(crtn) {
293			ourRtn = testError(quiet);
294			goto errOut;
295		}
296		ourPubPtr = &ourWrappedPub;
297		wrappedOurPub = true;
298	}
299	else {
300		ourPubPtr = &ourPub;
301	}
302
303	/*
304	 * Here we go, do the two sides of D-H key agreement, results to
305	 * to CSSM_KEYs.
306	 */
307	ourRtn = doECDH(cspHand, &ourPriv, theirPubPtr, bareCsp,
308		x963KDF, useSharedInfo ? &sharedInfo : NULL,
309		deriveSizeInBits, quiet, verbose, &derived1);
310	if(ourRtn) {
311		goto errOut;
312	}
313	ourRtn = doECDH(cspHand, &theirPriv, ourPubPtr, bareCsp,
314		x963KDF, useSharedInfo ? &sharedInfo : NULL,
315		deriveSizeInBits, quiet, verbose, &derived2);
316	if(ourRtn) {
317		goto errOut;
318	}
319
320	if(!appCompareCssmData(&derived1.KeyData, &derived2.KeyData)) {
321		printf("***Data Miscompare on ECDH key derivation\n");
322	}
323errOut:
324	if(ourKeysGend) {
325		cspFreeKey(cspHand, &ourPub);
326		cspFreeKey(cspHand, &ourPriv);
327	}
328	if(theirKeysGend) {
329		cspFreeKey(cspHand, &theirPub);
330		cspFreeKey(cspHand, &theirPriv);
331	}
332	if(wrappedTheirPub) {
333		cspFreeKey(cspHand, &theirWrappedPub);
334	}
335	if(wrappedOurPub) {
336		cspFreeKey(cspHand, &ourWrappedPub);
337	}
338	if(derivedKey1) {
339		cspFreeKey(cspHand, &derived1);
340	}
341	if(derivedKey2) {
342		cspFreeKey(cspHand, &derived2);
343	}
344	if(sharedInfo.Data != NULL) {
345		appFreeCssmData(&sharedInfo, CSSM_FALSE);
346	}
347	return ourRtn;
348}
349
350int main(int argc, char **argv)
351{
352	int		 		arg;
353	char			*argp;
354	CSSM_CSP_HANDLE cspHand;
355	unsigned		loop;
356	int				ourRtn = 0;
357
358	unsigned	keySize = KEY_SIZE_DEF;
359	unsigned	loops = LOOPS_DEF;
360	CSSM_BOOL	quiet = CSSM_FALSE;
361	CSSM_BOOL	verbose = CSSM_FALSE;
362	CSSM_BOOL	bareCsp = CSSM_TRUE;
363	CSSM_BOOL	x963KDF = CSSM_FALSE;
364
365	for(arg=1; arg<argc; arg++) {
366		argp = argv[arg];
367	    switch(argp[0]) {
368			case 'k':
369				keySize = atoi(&argp[2]);
370				break;
371			case 'X':
372				x963KDF = true;
373				break;
374		    case 'l':
375				loops = atoi(&argp[2]);
376				break;
377			case 'D':
378				bareCsp = CSSM_FALSE;
379				break;
380			case 'q':
381				quiet = CSSM_TRUE;
382				break;
383		    case 'v':
384		    	verbose = CSSM_TRUE;
385				break;
386			default:
387				usage(argv);
388		}
389	}
390	testStartBanner("ecdhTest", argc, argv);
391
392	cspHand = cspDlDbStartup(bareCsp, NULL);
393	if(cspHand == 0) {
394		exit(1);
395	}
396
397	for(loop=1; ; loop++) {
398		if(!quiet) {
399			printf("...Loop %d\n", loop);
400		}
401
402		/* test mode from l.s. bits of loop counter */
403
404		CSSM_BOOL ourKeysRef = (loop & 0x04) ? CSSM_TRUE : CSSM_FALSE;
405		CSSM_BOOL theirPrivKeyRef = (loop & 0x08) ? CSSM_TRUE : CSSM_FALSE;
406		PubKeyType theirPubKeyType;
407		switch(loop & 0x03) {
408			case 0:
409				theirPubKeyType = PKT_Ref;
410				break;
411			case 1:
412				theirPubKeyType = PKT_Wrap;
413				break;
414			case 2:
415				theirPubKeyType = PKT_X509;
416				break;
417			default:
418				theirPubKeyType = PKT_Octet;
419				break;
420		}
421
422		if(!bareCsp) {
423			/*
424			 * Generated keys have to be reference
425			 * pub keys have to be passed as Param
426			 */
427			ourKeysRef = CSSM_TRUE;
428			theirPrivKeyRef = CSSM_TRUE;
429			theirPubKeyType = PKT_Wrap;
430		}
431
432		CSSM_BOOL useSharedInfo = CSSM_FALSE;
433		if(x963KDF & ((loop & 0x01) == 0)) {
434			useSharedInfo = CSSM_TRUE;
435		}
436		ourRtn = doTest(cspHand, ourKeysRef, theirPrivKeyRef, theirPubKeyType,
437			keySize, bareCsp, x963KDF, useSharedInfo, verbose, quiet);
438		if(ourRtn) {
439			break;
440		}
441		if(loops && (loop == loops)) {
442			break;
443		}
444	}
445	CSSM_ModuleDetach(cspHand);
446	if((ourRtn == 0) && !quiet) {
447		printf("OK\n");
448	}
449
450	return ourRtn;
451}
452