--- /dev/null
+/*
+ * 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;
+}