dhTest.cpp   [plain text]


/*
 * dhTest - simple Diffie-Hellman exerciser. 
 */
#include <stdlib.h>
#include <stdio.h>
#include <Security/cssm.h>
#include "cspwrap.h"
#include "common.h"
#include <security_cdsa_utils/cuFileIo.h>
#include <strings.h>

#define USAGE_DEF		"noUsage"
#define LOOPS_DEF		10
#define KEY_SIZE_DEF	512
#define DERIVE_KEY_SIZE	128
#define DERIVE_KEY_ALG	CSSM_ALGID_AES

static void usage(char **argv)
{
	printf("usage: %s [options]\n", argv[0]);
	printf("Options:\n");
	printf("  k=keySize (default = %d)\n", KEY_SIZE_DEF);
	printf("  l=loops (0=forever)\n");
	printf("  p=pauseInterval (default=0, no pause)\n");
	printf("  D (CSP/DL; default = bare CSP)\n");
	printf("  o=fileName (dump key and param blobs to filename)\n");
	printf("  i=filename (obtain param blobs from filename)\n");
	printf("  8 (private key in PKCS8 format, default is PKCS3)\n");
	printf("  x (public key in X509 format, default is PKCS3)\n");
	printf("  f (public key is ref form; default is raw)\n");
	printf("  q(uiet)\n");
	printf("  v(erbose))\n");
	exit(1);
}

/*
 * Generate a Diffie-Hellman key pair. Optionally allows specification of 
 * algorithm parameters, and optionally returns algorithm parameters if
 * we generate them. 
 */
static int dhKeyGen(
	CSSM_CSP_HANDLE		cspHand,
	CSSM_KEY_PTR		pubKey,
	CSSM_KEY_PTR		privKey,
	const CSSM_DATA		*inParams,		// optional 
	CSSM_DATA_PTR		outParams,		// optional, we malloc
	uint32				keySizeInBits,
	CSSM_KEYBLOB_FORMAT privForm,
	CSSM_KEYBLOB_FORMAT pubForm,
	CSSM_BOOL			pubIsRef,
	CSSM_BOOL			quiet)
{
	CSSM_RETURN		crtn;
	CSSM_CC_HANDLE	ccHand;
	CSSM_DATA		labelData = { strlen(USAGE_DEF), (uint8 *)USAGE_DEF };
	
	if(inParams && outParams) {
		printf("***dhKeyGen: inParams and outParams are mutually "
			"exclusive.\n");
		return -1;
	}
	memset(pubKey, 0, sizeof(CSSM_KEY));
	memset(privKey, 0, sizeof(CSSM_KEY));
	
	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
		CSSM_ALGID_DH,
		keySizeInBits,
		NULL,					// Seed
		NULL,					// Salt
		NULL,					// StartDate
		NULL,					// EndDate
		inParams,				// Params, may be NULL
		&ccHand);
	if(crtn) {
		printError("CSSM_CSP_CreateKeyGenContext", crtn);
		return testError(quiet);
	}
	
	if((inParams == NULL) && (outParams != NULL)) {
		/* explicitly generate params and return them to caller */
		outParams->Data = NULL;
		outParams->Length = 0;
		crtn = CSSM_GenerateAlgorithmParams(ccHand, 
			keySizeInBits, outParams);
		if(crtn) {
			printError("CSSM_GenerateAlgorithmParams", crtn);
			return testError(quiet);
		}
	}

	uint32 privAttr = CSSM_KEYATTR_RETURN_REF;
	uint32 pubAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	if(privForm != CSSM_KEYBLOB_RAW_FORMAT_NONE) {
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT,
			sizeof(uint32),	
			CAT_Uint32,
			NULL,
			privForm);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_PRIVATE_KEY"
				"_FORMAT)", crtn);
			return crtn;
		}
		privAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	}
	if(pubIsRef) {
		pubAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
	}
	else if(pubForm != CSSM_KEYBLOB_RAW_FORMAT_NONE) {
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
			sizeof(uint32),	
			CAT_Uint32,
			NULL,
			pubForm);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY"
				"_FORMAT)", crtn);
			return crtn;
		}
	}
	crtn = CSSM_GenerateKeyPair(ccHand,
		CSSM_KEYUSE_DERIVE,		// only legal use of a Diffie-Hellman key 
		pubAttr,
		&labelData,
		pubKey,
		/* private key specification */
		CSSM_KEYUSE_DERIVE,
		privAttr,
		&labelData,				// same labels
		NULL,					// CredAndAclEntry
		privKey);
	if(crtn) {
		printError("CSSM_GenerateKeyPair", crtn);
			return testError(quiet);
	}
	
	CSSM_DeleteContext(ccHand);
	return crtn;
}

/*
 * Perform Diffie-Hellman key exchange. 
 * Given "our" private key (in the form of a CSSM_KEY) and "their" public
 * key (in the form of a raw blob of bytes), cook up a symmetric key.
 */
static int dhKeyExchange(
	CSSM_CSP_HANDLE	cspHand,
	CSSM_KEY_PTR	myPrivKey,
	CSSM_KEY_PTR	theirPubKey,
	CSSM_KEY_PTR	derivedKey,				// RETURNED
	uint32			deriveKeySizeInBits,
	CSSM_ALGORITHMS	derivedKeyAlg,
	uint32			derivedKeyUsage,
	uint32			derivedKeyAttr,
	CSSM_BOOL		quiet)
{
	CSSM_RETURN 			crtn;
	CSSM_ACCESS_CREDENTIALS	creds;
	CSSM_CC_HANDLE			ccHand;
	CSSM_DATA				labelData = { strlen(USAGE_DEF), (uint8 *)USAGE_DEF };
	
	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	memset(derivedKey, 0, sizeof(CSSM_KEY));
	
	crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
		CSSM_ALGID_DH,
		derivedKeyAlg,
		deriveKeySizeInBits,
		&creds,
		myPrivKey,		// BaseKey
		0,				// IterationCount
		0,				// Salt
		0,				// Seed
		&ccHand);
	if(crtn) {
		printError("CSSM_CSP_CreateDeriveKeyContext", crtn);
		return testError(quiet);
	}
	
	/* 
	 * Public key passed in as CSSM_DATA *Param - only if
	 * the pub key is in raw PKCS3 form
	 */
	CSSM_DATA nullParam = {0, NULL};
	CSSM_DATA_PTR paramPtr;
	CSSM_KEYHEADER &hdr = theirPubKey->KeyHeader;
	if((hdr.BlobType == CSSM_KEYBLOB_RAW) &&
	   (hdr.Format == CSSM_KEYBLOB_RAW_FORMAT_PKCS3)) {
		/* simple case */
		paramPtr = &theirPubKey->KeyData;
	}
	else {
		/* add this pub key as a context attr */
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_PUBLIC_KEY,
			sizeof(CSSM_KEY),	
			CAT_Ptr,
			(void *)theirPubKey,
			0);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY)",
				crtn);
			return crtn;
		}
		paramPtr = &nullParam;
	}
	crtn = CSSM_DeriveKey(ccHand,
		paramPtr,
		derivedKeyUsage,
		derivedKeyAttr,
		&labelData,
		NULL,				// cread/acl
		derivedKey);
	if(crtn) {
		printError("CSSM_DeriveKey", crtn);
	}
	CSSM_DeleteContext(ccHand);
	return crtn;
}

int doTest(
	CSSM_CSP_HANDLE		cspHand,
	const CSSM_DATA		*inParams,		// optional
	CSSM_DATA_PTR		outParams,		// optional
	uint32				keySizeInBits,
	CSSM_KEYBLOB_FORMAT	privForm,
	CSSM_KEYBLOB_FORMAT	pubForm,
	CSSM_BOOL			pubIsRef,
	CSSM_BOOL			quiet)
{
	CSSM_KEY	myPriv;
	CSSM_KEY	myPub;
	CSSM_KEY	theirPriv;
	CSSM_KEY	theirPub;
	int			rtn = 0;
	
	/* generate two key pairs */
	if(dhKeyGen(cspHand,
		&myPub,
		&myPriv,
		inParams,
		outParams,
		keySizeInBits,
		privForm,
		pubForm,
		pubIsRef,
		quiet)) {
			return 1;
	}
	
	/* note this MUST match the params either specified or generated in previous
	 * call */
	if((inParams == NULL) && (outParams == NULL)) {
		printf("***BRRZAP! Must provide a way to match D-H parameters!\n");
		exit(1);
	}
	const CSSM_DATA *theParams = inParams;
	if(theParams == NULL) {
		theParams = outParams;
	}
	if(dhKeyGen(cspHand,
		&theirPub,
		&theirPriv,
		theParams,
		NULL,				// outParams
		keySizeInBits,
		privForm,
		pubForm,
		pubIsRef,
		quiet)) {
			return 1;
	}
	
	/* derive two keys, ensure they match */
	CSSM_KEY myDerive;
	CSSM_KEY theirDerive;
	if(dhKeyExchange(cspHand,
		&myPriv,
		&theirPub,
		&myDerive,
		DERIVE_KEY_SIZE,
		DERIVE_KEY_ALG,
		CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
		CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
		quiet)) {
			return testError(quiet);
	}
	if(dhKeyExchange(cspHand,
		&theirPriv,
		&myPub,
		&theirDerive,
		DERIVE_KEY_SIZE,
		DERIVE_KEY_ALG,
		CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
		CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
		quiet)) {
			return testError(quiet);
	}
	
	if(!appCompareCssmData(&myDerive.KeyData, &theirDerive.KeyData)) {
		printf("***Key Exchange data miscompare***\n");
		rtn = testError(quiet);
	}
	cspFreeKey(cspHand, &myPub);
	cspFreeKey(cspHand, &myPriv);
	cspFreeKey(cspHand, &theirPub);
	cspFreeKey(cspHand, &theirPriv);
	cspFreeKey(cspHand, &myDerive);
	cspFreeKey(cspHand, &theirDerive);
	return rtn;
}

int main(int argc, char **argv)
{
	int		 				arg;
	char					*argp;
	CSSM_CSP_HANDLE 		cspHand;
	unsigned				loop;
	int 					i;
	CSSM_DATA				inParams = {0, NULL};
	CSSM_DATA				outParams = {0, NULL};
	CSSM_DATA_PTR			inParamPtr = NULL;
	CSSM_DATA_PTR			outParamPtr = NULL;
	
	/* user-spec'd parameters */
	unsigned				keySize = KEY_SIZE_DEF;
	unsigned				pauseInterval = 0;
	unsigned				loops = LOOPS_DEF;
	CSSM_BOOL				quiet = CSSM_FALSE;
	CSSM_BOOL				verbose = CSSM_FALSE;
	CSSM_BOOL				bareCsp = CSSM_TRUE;
	char					*inFileName = NULL;
	char					*outFileName = NULL;
	/* default: "use default blob form", i.e., PKCS3 */
	CSSM_KEYBLOB_FORMAT		privForm = CSSM_KEYBLOB_RAW_FORMAT_NONE;
	CSSM_KEYBLOB_FORMAT		pubForm = CSSM_KEYBLOB_RAW_FORMAT_NONE;
	CSSM_BOOL				pubIsRef = CSSM_FALSE;
	
	for(arg=1; arg<argc; arg++) { 
		argp = argv[arg];
	    switch(argp[0]) {
			case 'k':
				keySize = atoi(&argp[2]);
				break;
		    case 'l':
				loops = atoi(&argp[2]);
				break;
		    case 'p':
				pauseInterval = atoi(&argp[2]);
				break;
			case 'i':
				inFileName = &argp[2];
				break;
			case 'o':
				outFileName = &argp[2];
				break;
			case 'D':
				bareCsp = CSSM_FALSE;
				break;
			case '8':
				privForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS8;
				break;
			case 'x':
				pubForm = CSSM_KEYBLOB_RAW_FORMAT_X509;
				break;
			case 'f':
				pubIsRef = CSSM_TRUE;
				break;
			case 'q':
				quiet = CSSM_TRUE;
				break;
		    case 'v':
		    	verbose = CSSM_TRUE;
				break;
			default:
				usage(argv);
		}
	}
	
	/* Actually this test does NOT run with CSPDL */
	if(!bareCsp) {
		printf("***%s does not run with CSPDL; aborting.\n", argv[0]);
		exit(1);
	}
	
	cspHand = cspDlDbStartup(bareCsp, NULL);
	if(cspHand == 0) {
		exit(1);
	}
	
	/* optionally fetch algorithm parameters from a file */
	if(inFileName) {
		unsigned len;
		int r = readFile(inFileName, &inParams.Data, &len);
		if(r) {
			printf("***Can't read parameters from %s; aborting.\n",
				inFileName);
			exit(1);
		}
		inParams.Length = len;
		/* constant from now on */
		inParamPtr = &inParams;
	}
	else {
		/* first time thru, no user-supplied parameters; generate them and
		 * save in outParams */
		outParamPtr = &outParams;
	}
	printf("Starting dhTest; args: ");
	for(i=1; i<argc; i++) {
		printf("%s ", argv[i]);
	}
	printf("\n");
	for(loop=1; ; loop++) {
		if(!quiet) {
			printf("...Loop %d\n", loop);
		}
		i = doTest(cspHand, inParamPtr, outParamPtr, keySize, privForm, 
			pubForm, pubIsRef, quiet);
		if(i) {
			break;
		}
		if(loop == 1) {
			/* first time thru */
			if(outFileName) {
				/* save parameters for another run */
				i = writeFile(outFileName, outParams.Data,
					outParams.Length);
				if(i) {
					printf("***Error writing params to %s; continuing.\n",
						outFileName);
				}
				else {
					if(!quiet) {
						printf("...wrote %lu bytes to %s\n", 
							outParams.Length, outFileName);
					}
				}
			}
			if(!inFileName) {
				/* from now on, use the parameters we just generated */
				inParamPtr = &outParams;
			}
			/* and in any case don't fetch any more params */
			outParamPtr = NULL;
		}
		if(loops && (loop == loops)) {
			break;
		}
		if(pauseInterval && ((loop % pauseInterval) == 0)) {
			char inch;
			
			fpurge(stdin);
			printf("Hit CR to proceed, q to quit: ");
			inch = getchar();
			if(inch == 'q') {
				break;
			}
		}
	}
	CSSM_ModuleDetach(cspHand);
	if(!quiet) {
		printf("OK\n");
	}
	return 0;
}