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 ¶mData, 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