--- /dev/null
+/*
+ * Copyright (c) 2008 Apple Inc. All Rights Reserved.
+ *
+ * ecdhTest.cpp - Test Elliptic Curve Diffie-Hellman key exchange.
+ *
+ * Created Jan. 1 2008 by Doug Mitchell.
+ */
+
+#include <stdlib.h>
+#include <strings.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <Security/cssm.h>
+#include "cspwrap.h"
+#include "common.h"
+
+#define LOOPS_DEF 32
+#define KEY_SIZE_DEF 256
+
+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(" X (X9.63 key derivation)\n");
+ printf(" l=loops (0=forever)\n");
+ printf(" D (CSP/DL; default = bare CSP)\n");
+ printf(" q(uiet)\n");
+ printf(" v(erbose))\n");
+ exit(1);
+}
+
+#define LABEL_DEF "noLabel"
+#define MAX_SHARED_INFO_LEN 400
+#define MAX_DERIVED_SIZE 1024
+
+static int doECDH(
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_KEY_PTR privKey,
+ /*
+ * pubKey:
+ * Ref form - use key as pubKey as is
+ * X509 form - use as is
+ * OCTET_STRING form - use key data as Param
+ */
+ CSSM_KEY_PTR pubKey,
+ CSSM_BOOL bareCsp, // false --> derive ref key and NULL-wrap it
+ CSSM_BOOL x963KDF,
+ CSSM_DATA *sharedInfo,
+ uint32 deriveSizeInBits,
+ CSSM_BOOL quiet,
+ CSSM_BOOL verbose,
+
+ /* result RETURNED here */
+ CSSM_KEY_PTR derivedKey)
+
+{
+ CSSM_DATA paramData = {0, NULL};
+ CSSM_KEY_PTR contextPubKey = NULL;
+ CSSM_KEYHEADER_PTR hdr = &pubKey->KeyHeader;
+
+ if((hdr->BlobType == CSSM_KEYBLOB_RAW) &&
+ (hdr->Format == CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING)) {
+ paramData = pubKey->KeyData;
+ }
+ else {
+ contextPubKey = pubKey;
+ }
+
+ /* create key derivation context */
+ CSSM_RETURN crtn;
+ CSSM_ACCESS_CREDENTIALS creds;
+ CSSM_CC_HANDLE ccHand;
+
+ memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+
+ CSSM_ALGORITHMS deriveAlg;
+ if(x963KDF) {
+ deriveAlg = CSSM_ALGID_ECDH_X963_KDF;
+ }
+ else {
+ deriveAlg = CSSM_ALGID_ECDH;
+ }
+
+ crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
+ deriveAlg,
+ CSSM_ALGID_RC4, // doesn't matter, just give us the bits
+ deriveSizeInBits,
+ &creds,
+ privKey, // BaseKey
+ 0, // IterationCount
+ sharedInfo, // Salt
+ 0, // Seed
+ &ccHand);
+ if(crtn) {
+ printError("CSSM_CSP_CreateDeriveKeyContext", crtn);
+ return testError(quiet);
+ }
+
+ if(contextPubKey != NULL) {
+ /* add pub key as a context attr */
+ crtn = AddContextAttribute(ccHand,
+ CSSM_ATTRIBUTE_PUBLIC_KEY,
+ sizeof(CSSM_KEY),
+ CAT_Ptr,
+ (void *)contextPubKey,
+ 0);
+ if(crtn) {
+ printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY)",
+ crtn);
+ return crtn;
+ }
+ }
+
+ /* D-H derive key */
+ CSSM_DATA labelData = { strlen(LABEL_DEF), (uint8 *)LABEL_DEF };
+ CSSM_KEYATTR_FLAGS keyAttr = bareCsp ?
+ (CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE) :
+ (CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE);
+ memset(derivedKey, 0, sizeof(CSSM_KEY));
+ crtn = CSSM_DeriveKey(ccHand,
+ ¶mData,
+ CSSM_KEYUSE_ANY,
+ keyAttr,
+ &labelData,
+ NULL, // cread/acl
+ derivedKey);
+ if(crtn) {
+ printError("CSSM_DeriveKey", crtn);
+ }
+ CSSM_DeleteContext(ccHand);
+ if(crtn) {
+ return testError(quiet);
+ }
+
+ if(!bareCsp) {
+ /* Got a ref key, give caller raw */
+ CSSM_KEY refKey = *derivedKey;
+ crtn = cspRefKeyToRaw(cspHand, &refKey, derivedKey);
+ cspFreeKey(cspHand, &refKey);
+ }
+ return 0;
+}
+
+/* define public key style */
+typedef enum {
+ PKT_Ref, /* ref key */
+ PKT_Wrap, /* generate ref key, wrap to OCTET_STRING */
+ PKT_X509, /* raw key X509 format */
+ PKT_Octet /* generate to OCTET_STRING form */
+} PubKeyType;
+
+#define BoolStr(v) (v ? "true " : "false")
+
+static const char *KeyStypeStr(
+ PubKeyType keyType)
+{
+ switch(keyType) {
+ case PKT_Ref: return "Ref";
+ case PKT_Wrap: return "Ref->Wrap";
+ case PKT_X509: return "X509";
+ case PKT_Octet: return "X9.62";
+ default: return "BRRZAP";
+ }
+}
+
+static int doTest(
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_BOOL ourKeysRef, /* our keys are reference */
+ CSSM_BOOL theirPrivKeyRef, /* their private key is reference */
+ PubKeyType theirPubKeyType,
+ unsigned keySizeBits,
+ CSSM_BOOL bareCsp,
+ CSSM_BOOL x963KDF,
+ CSSM_BOOL useSharedInfo, /* use the optional SharedInfo for x963KDF */
+ CSSM_BOOL verbose,
+ CSSM_BOOL quiet)
+{
+
+ CSSM_RETURN crtn;
+ CSSM_KEY ourPriv;
+ CSSM_KEY ourPub;
+ bool ourKeysGend = false;
+ bool theirKeysGend = false;
+ bool wrappedTheirPub = false;
+ bool wrappedOurPub = false;
+ bool derivedKey1 = false;
+ bool derivedKey2 = false;
+ CSSM_DATA sharedInfo = {0, NULL};
+ uint32 deriveSizeInBits;
+
+ if(x963KDF) {
+ /* arbitrary derived size */
+ deriveSizeInBits = genRand(1, MAX_DERIVED_SIZE);
+ }
+ else {
+ deriveSizeInBits = keySizeBits;
+ }
+ if(useSharedInfo) {
+ /* length should be totally arbitrary */
+ appSetupCssmData(&sharedInfo, MAX_SHARED_INFO_LEN);
+ simpleGenData(&sharedInfo, 1, MAX_SHARED_INFO_LEN);
+ }
+
+
+ if(!quiet) {
+ if(x963KDF) {
+ printf("...sharedInfoLen %4lu deriveSize %4lu ",
+ (unsigned long)sharedInfo.Length, (unsigned long)deriveSizeInBits);
+ }
+ else {
+ printf("...");
+ }
+ printf("ourRef %s theirPrivRef %s theirPub %s\n",
+ BoolStr(ourKeysRef), BoolStr(theirPrivKeyRef),
+ KeyStypeStr(theirPubKeyType));
+ }
+
+ crtn = cspGenKeyPair(cspHand, CSSM_ALGID_ECDSA,
+ LABEL_DEF, strlen(LABEL_DEF), keySizeBits,
+ &ourPub, ourKeysRef, CSSM_KEYUSE_DERIVE, CSSM_KEYBLOB_RAW_FORMAT_NONE,
+ &ourPriv, ourKeysRef, CSSM_KEYUSE_DERIVE, CSSM_KEYBLOB_RAW_FORMAT_NONE,
+ CSSM_FALSE);
+ if(crtn) {
+ return testError(quiet);
+ }
+ ourKeysGend = true;
+
+ CSSM_KEY theirPriv;
+ CSSM_KEY theirPub; /* the generated one */
+ CSSM_KEY theirWrappedPub; /* optional NULL unwrap */
+ CSSM_KEY_PTR theirPubPtr;
+ CSSM_KEY ourWrappedPub; /* optional NULL unwrap */
+ CSSM_KEY_PTR ourPubPtr;
+ CSSM_KEY derived1;
+ CSSM_KEY derived2;
+ CSSM_BOOL pubIsRef = CSSM_FALSE;
+ CSSM_KEYBLOB_FORMAT blobForm = CSSM_KEYBLOB_RAW_FORMAT_NONE;
+ int ourRtn = 0;
+
+ switch(theirPubKeyType) {
+ case PKT_Ref:
+ case PKT_Wrap:
+ pubIsRef = CSSM_TRUE;
+ break;
+ case PKT_X509:
+ pubIsRef = CSSM_FALSE;
+ break;
+ case PKT_Octet:
+ pubIsRef = CSSM_FALSE;
+ blobForm = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
+ break;
+ }
+
+ crtn = cspGenKeyPair(cspHand, CSSM_ALGID_ECDSA,
+ LABEL_DEF, strlen(LABEL_DEF), keySizeBits,
+ &theirPub, pubIsRef, CSSM_KEYUSE_DERIVE, CSSM_KEYBLOB_RAW_FORMAT_NONE,
+ &theirPriv, theirPrivKeyRef, CSSM_KEYUSE_DERIVE, CSSM_KEYBLOB_RAW_FORMAT_NONE,
+ CSSM_FALSE);
+ if(crtn) {
+ ourRtn = testError(quiet);
+ goto errOut;
+ }
+
+ if(theirPubKeyType == PKT_Wrap) {
+ /*
+ * This test mode is here mainly to ring out the key wrap and
+ * OCTET_STRING format functionality in the CrypkitCSP, it's
+ * not really relevant to ECDH...
+ */
+ crtn = cspRefKeyToRawWithFormat(cspHand, &theirPub,
+ CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING, &theirWrappedPub);
+ if(crtn) {
+ ourRtn = testError(quiet);
+ goto errOut;
+ }
+ theirPubPtr = &theirWrappedPub;
+ wrappedTheirPub = true;
+ }
+ else {
+ theirPubPtr = &theirPub;
+ }
+
+ if(!bareCsp) {
+ /*
+ * For CSPDL, convert our pub key to OCTET_STRING format so it
+ * is sent as a Param - can't send a ref key (or any other pub
+ * key) in the context
+ */
+ crtn = cspRefKeyToRawWithFormat(cspHand, &ourPub,
+ CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING, &ourWrappedPub);
+ if(crtn) {
+ ourRtn = testError(quiet);
+ goto errOut;
+ }
+ ourPubPtr = &ourWrappedPub;
+ wrappedOurPub = true;
+ }
+ else {
+ ourPubPtr = &ourPub;
+ }
+
+ /*
+ * Here we go, do the two sides of D-H key agreement, results to
+ * to CSSM_KEYs.
+ */
+ ourRtn = doECDH(cspHand, &ourPriv, theirPubPtr, bareCsp,
+ x963KDF, useSharedInfo ? &sharedInfo : NULL,
+ deriveSizeInBits, quiet, verbose, &derived1);
+ if(ourRtn) {
+ goto errOut;
+ }
+ ourRtn = doECDH(cspHand, &theirPriv, ourPubPtr, bareCsp,
+ x963KDF, useSharedInfo ? &sharedInfo : NULL,
+ deriveSizeInBits, quiet, verbose, &derived2);
+ if(ourRtn) {
+ goto errOut;
+ }
+
+ if(!appCompareCssmData(&derived1.KeyData, &derived2.KeyData)) {
+ printf("***Data Miscompare on ECDH key derivation\n");
+ }
+errOut:
+ if(ourKeysGend) {
+ cspFreeKey(cspHand, &ourPub);
+ cspFreeKey(cspHand, &ourPriv);
+ }
+ if(theirKeysGend) {
+ cspFreeKey(cspHand, &theirPub);
+ cspFreeKey(cspHand, &theirPriv);
+ }
+ if(wrappedTheirPub) {
+ cspFreeKey(cspHand, &theirWrappedPub);
+ }
+ if(wrappedOurPub) {
+ cspFreeKey(cspHand, &ourWrappedPub);
+ }
+ if(derivedKey1) {
+ cspFreeKey(cspHand, &derived1);
+ }
+ if(derivedKey2) {
+ cspFreeKey(cspHand, &derived2);
+ }
+ if(sharedInfo.Data != NULL) {
+ appFreeCssmData(&sharedInfo, CSSM_FALSE);
+ }
+ return ourRtn;
+}
+
+int main(int argc, char **argv)
+{
+ int arg;
+ char *argp;
+ CSSM_CSP_HANDLE cspHand;
+ unsigned loop;
+ int ourRtn = 0;
+
+ unsigned keySize = KEY_SIZE_DEF;
+ unsigned loops = LOOPS_DEF;
+ CSSM_BOOL quiet = CSSM_FALSE;
+ CSSM_BOOL verbose = CSSM_FALSE;
+ CSSM_BOOL bareCsp = CSSM_TRUE;
+ CSSM_BOOL x963KDF = CSSM_FALSE;
+
+ for(arg=1; arg<argc; arg++) {
+ argp = argv[arg];
+ switch(argp[0]) {
+ case 'k':
+ keySize = atoi(&argp[2]);
+ break;
+ case 'X':
+ x963KDF = true;
+ break;
+ case 'l':
+ loops = atoi(&argp[2]);
+ break;
+ case 'D':
+ bareCsp = CSSM_FALSE;
+ break;
+ case 'q':
+ quiet = CSSM_TRUE;
+ break;
+ case 'v':
+ verbose = CSSM_TRUE;
+ break;
+ default:
+ usage(argv);
+ }
+ }
+ testStartBanner("ecdhTest", argc, argv);
+
+ cspHand = cspDlDbStartup(bareCsp, NULL);
+ if(cspHand == 0) {
+ exit(1);
+ }
+
+ for(loop=1; ; loop++) {
+ if(!quiet) {
+ printf("...Loop %d\n", loop);
+ }
+
+ /* test mode from l.s. bits of loop counter */
+
+ CSSM_BOOL ourKeysRef = (loop & 0x04) ? CSSM_TRUE : CSSM_FALSE;
+ CSSM_BOOL theirPrivKeyRef = (loop & 0x08) ? CSSM_TRUE : CSSM_FALSE;
+ PubKeyType theirPubKeyType;
+ switch(loop & 0x03) {
+ case 0:
+ theirPubKeyType = PKT_Ref;
+ break;
+ case 1:
+ theirPubKeyType = PKT_Wrap;
+ break;
+ case 2:
+ theirPubKeyType = PKT_X509;
+ break;
+ default:
+ theirPubKeyType = PKT_Octet;
+ break;
+ }
+
+ if(!bareCsp) {
+ /*
+ * Generated keys have to be reference
+ * pub keys have to be passed as Param
+ */
+ ourKeysRef = CSSM_TRUE;
+ theirPrivKeyRef = CSSM_TRUE;
+ theirPubKeyType = PKT_Wrap;
+ }
+
+ CSSM_BOOL useSharedInfo = CSSM_FALSE;
+ if(x963KDF & ((loop & 0x01) == 0)) {
+ useSharedInfo = CSSM_TRUE;
+ }
+ ourRtn = doTest(cspHand, ourKeysRef, theirPrivKeyRef, theirPubKeyType,
+ keySize, bareCsp, x963KDF, useSharedInfo, verbose, quiet);
+ if(ourRtn) {
+ break;
+ }
+ if(loops && (loop == loops)) {
+ break;
+ }
+ }
+ CSSM_ModuleDetach(cspHand);
+ if((ourRtn == 0) && !quiet) {
+ printf("OK\n");
+ }
+
+ return ourRtn;
+}