| 1 | /* |
| 2 | * dhTest - simple Diffie-Hellman exerciser. |
| 3 | */ |
| 4 | #include <stdlib.h> |
| 5 | #include <stdio.h> |
| 6 | #include <Security/cssm.h> |
| 7 | #include "cspwrap.h" |
| 8 | #include "common.h" |
| 9 | #include <security_cdsa_utils/cuFileIo.h> |
| 10 | #include <strings.h> |
| 11 | |
| 12 | #define USAGE_DEF "noUsage" |
| 13 | #define LOOPS_DEF 10 |
| 14 | #define KEY_SIZE_DEF 512 |
| 15 | #define DERIVE_KEY_SIZE 128 |
| 16 | #define DERIVE_KEY_ALG CSSM_ALGID_AES |
| 17 | |
| 18 | static void usage(char **argv) |
| 19 | { |
| 20 | printf("usage: %s [options]\n", argv[0]); |
| 21 | printf("Options:\n"); |
| 22 | printf(" k=keySize (default = %d)\n", KEY_SIZE_DEF); |
| 23 | printf(" l=loops (0=forever)\n"); |
| 24 | printf(" p=pauseInterval (default=0, no pause)\n"); |
| 25 | printf(" D (CSP/DL; default = bare CSP)\n"); |
| 26 | printf(" o=fileName (dump key and param blobs to filename)\n"); |
| 27 | printf(" i=filename (obtain param blobs from filename)\n"); |
| 28 | printf(" 8 (private key in PKCS8 format, default is PKCS3)\n"); |
| 29 | printf(" x (public key in X509 format, default is PKCS3)\n"); |
| 30 | printf(" f (public key is ref form; default is raw)\n"); |
| 31 | printf(" q(uiet)\n"); |
| 32 | printf(" v(erbose))\n"); |
| 33 | exit(1); |
| 34 | } |
| 35 | |
| 36 | /* |
| 37 | * Generate a Diffie-Hellman key pair. Optionally allows specification of |
| 38 | * algorithm parameters, and optionally returns algorithm parameters if |
| 39 | * we generate them. |
| 40 | */ |
| 41 | static int dhKeyGen( |
| 42 | CSSM_CSP_HANDLE cspHand, |
| 43 | CSSM_KEY_PTR pubKey, |
| 44 | CSSM_KEY_PTR privKey, |
| 45 | const CSSM_DATA *inParams, // optional |
| 46 | CSSM_DATA_PTR outParams, // optional, we malloc |
| 47 | uint32 keySizeInBits, |
| 48 | CSSM_KEYBLOB_FORMAT privForm, |
| 49 | CSSM_KEYBLOB_FORMAT pubForm, |
| 50 | CSSM_BOOL pubIsRef, |
| 51 | CSSM_BOOL quiet) |
| 52 | { |
| 53 | CSSM_RETURN crtn; |
| 54 | CSSM_CC_HANDLE ccHand; |
| 55 | CSSM_DATA labelData = { strlen(USAGE_DEF), (uint8 *)USAGE_DEF }; |
| 56 | |
| 57 | if(inParams && outParams) { |
| 58 | printf("***dhKeyGen: inParams and outParams are mutually " |
| 59 | "exclusive.\n"); |
| 60 | return -1; |
| 61 | } |
| 62 | memset(pubKey, 0, sizeof(CSSM_KEY)); |
| 63 | memset(privKey, 0, sizeof(CSSM_KEY)); |
| 64 | |
| 65 | crtn = CSSM_CSP_CreateKeyGenContext(cspHand, |
| 66 | CSSM_ALGID_DH, |
| 67 | keySizeInBits, |
| 68 | NULL, // Seed |
| 69 | NULL, // Salt |
| 70 | NULL, // StartDate |
| 71 | NULL, // EndDate |
| 72 | inParams, // Params, may be NULL |
| 73 | &ccHand); |
| 74 | if(crtn) { |
| 75 | printError("CSSM_CSP_CreateKeyGenContext", crtn); |
| 76 | return testError(quiet); |
| 77 | } |
| 78 | |
| 79 | if((inParams == NULL) && (outParams != NULL)) { |
| 80 | /* explicitly generate params and return them to caller */ |
| 81 | outParams->Data = NULL; |
| 82 | outParams->Length = 0; |
| 83 | crtn = CSSM_GenerateAlgorithmParams(ccHand, |
| 84 | keySizeInBits, outParams); |
| 85 | if(crtn) { |
| 86 | printError("CSSM_GenerateAlgorithmParams", crtn); |
| 87 | return testError(quiet); |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | uint32 privAttr = CSSM_KEYATTR_RETURN_REF; |
| 92 | uint32 pubAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE; |
| 93 | if(privForm != CSSM_KEYBLOB_RAW_FORMAT_NONE) { |
| 94 | crtn = AddContextAttribute(ccHand, |
| 95 | CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT, |
| 96 | sizeof(uint32), |
| 97 | CAT_Uint32, |
| 98 | NULL, |
| 99 | privForm); |
| 100 | if(crtn) { |
| 101 | printError("AddContextAttribute(CSSM_ATTRIBUTE_PRIVATE_KEY" |
| 102 | "_FORMAT)", crtn); |
| 103 | return crtn; |
| 104 | } |
| 105 | privAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE; |
| 106 | } |
| 107 | if(pubIsRef) { |
| 108 | pubAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE; |
| 109 | } |
| 110 | else if(pubForm != CSSM_KEYBLOB_RAW_FORMAT_NONE) { |
| 111 | crtn = AddContextAttribute(ccHand, |
| 112 | CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT, |
| 113 | sizeof(uint32), |
| 114 | CAT_Uint32, |
| 115 | NULL, |
| 116 | pubForm); |
| 117 | if(crtn) { |
| 118 | printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY" |
| 119 | "_FORMAT)", crtn); |
| 120 | return crtn; |
| 121 | } |
| 122 | } |
| 123 | crtn = CSSM_GenerateKeyPair(ccHand, |
| 124 | CSSM_KEYUSE_DERIVE, // only legal use of a Diffie-Hellman key |
| 125 | pubAttr, |
| 126 | &labelData, |
| 127 | pubKey, |
| 128 | /* private key specification */ |
| 129 | CSSM_KEYUSE_DERIVE, |
| 130 | privAttr, |
| 131 | &labelData, // same labels |
| 132 | NULL, // CredAndAclEntry |
| 133 | privKey); |
| 134 | if(crtn) { |
| 135 | printError("CSSM_GenerateKeyPair", crtn); |
| 136 | return testError(quiet); |
| 137 | } |
| 138 | |
| 139 | CSSM_DeleteContext(ccHand); |
| 140 | return crtn; |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | * Perform Diffie-Hellman key exchange. |
| 145 | * Given "our" private key (in the form of a CSSM_KEY) and "their" public |
| 146 | * key (in the form of a raw blob of bytes), cook up a symmetric key. |
| 147 | */ |
| 148 | static int dhKeyExchange( |
| 149 | CSSM_CSP_HANDLE cspHand, |
| 150 | CSSM_KEY_PTR myPrivKey, |
| 151 | CSSM_KEY_PTR theirPubKey, |
| 152 | CSSM_KEY_PTR derivedKey, // RETURNED |
| 153 | uint32 deriveKeySizeInBits, |
| 154 | CSSM_ALGORITHMS derivedKeyAlg, |
| 155 | uint32 derivedKeyUsage, |
| 156 | uint32 derivedKeyAttr, |
| 157 | CSSM_BOOL quiet) |
| 158 | { |
| 159 | CSSM_RETURN crtn; |
| 160 | CSSM_ACCESS_CREDENTIALS creds; |
| 161 | CSSM_CC_HANDLE ccHand; |
| 162 | CSSM_DATA labelData = { strlen(USAGE_DEF), (uint8 *)USAGE_DEF }; |
| 163 | |
| 164 | memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); |
| 165 | memset(derivedKey, 0, sizeof(CSSM_KEY)); |
| 166 | |
| 167 | crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand, |
| 168 | CSSM_ALGID_DH, |
| 169 | derivedKeyAlg, |
| 170 | deriveKeySizeInBits, |
| 171 | &creds, |
| 172 | myPrivKey, // BaseKey |
| 173 | 0, // IterationCount |
| 174 | 0, // Salt |
| 175 | 0, // Seed |
| 176 | &ccHand); |
| 177 | if(crtn) { |
| 178 | printError("CSSM_CSP_CreateDeriveKeyContext", crtn); |
| 179 | return testError(quiet); |
| 180 | } |
| 181 | |
| 182 | /* |
| 183 | * Public key passed in as CSSM_DATA *Param - only if |
| 184 | * the pub key is in raw PKCS3 form |
| 185 | */ |
| 186 | CSSM_DATA nullParam = {0, NULL}; |
| 187 | CSSM_DATA_PTR paramPtr; |
| 188 | CSSM_KEYHEADER &hdr = theirPubKey->KeyHeader; |
| 189 | if((hdr.BlobType == CSSM_KEYBLOB_RAW) && |
| 190 | (hdr.Format == CSSM_KEYBLOB_RAW_FORMAT_PKCS3)) { |
| 191 | /* simple case */ |
| 192 | paramPtr = &theirPubKey->KeyData; |
| 193 | } |
| 194 | else { |
| 195 | /* add this pub key as a context attr */ |
| 196 | crtn = AddContextAttribute(ccHand, |
| 197 | CSSM_ATTRIBUTE_PUBLIC_KEY, |
| 198 | sizeof(CSSM_KEY), |
| 199 | CAT_Ptr, |
| 200 | (void *)theirPubKey, |
| 201 | 0); |
| 202 | if(crtn) { |
| 203 | printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY)", |
| 204 | crtn); |
| 205 | return crtn; |
| 206 | } |
| 207 | paramPtr = &nullParam; |
| 208 | } |
| 209 | crtn = CSSM_DeriveKey(ccHand, |
| 210 | paramPtr, |
| 211 | derivedKeyUsage, |
| 212 | derivedKeyAttr, |
| 213 | &labelData, |
| 214 | NULL, // cread/acl |
| 215 | derivedKey); |
| 216 | if(crtn) { |
| 217 | printError("CSSM_DeriveKey", crtn); |
| 218 | } |
| 219 | CSSM_DeleteContext(ccHand); |
| 220 | return crtn; |
| 221 | } |
| 222 | |
| 223 | int doTest( |
| 224 | CSSM_CSP_HANDLE cspHand, |
| 225 | const CSSM_DATA *inParams, // optional |
| 226 | CSSM_DATA_PTR outParams, // optional |
| 227 | uint32 keySizeInBits, |
| 228 | CSSM_KEYBLOB_FORMAT privForm, |
| 229 | CSSM_KEYBLOB_FORMAT pubForm, |
| 230 | CSSM_BOOL pubIsRef, |
| 231 | CSSM_BOOL quiet) |
| 232 | { |
| 233 | CSSM_KEY myPriv; |
| 234 | CSSM_KEY myPub; |
| 235 | CSSM_KEY theirPriv; |
| 236 | CSSM_KEY theirPub; |
| 237 | int rtn = 0; |
| 238 | |
| 239 | /* generate two key pairs */ |
| 240 | if(dhKeyGen(cspHand, |
| 241 | &myPub, |
| 242 | &myPriv, |
| 243 | inParams, |
| 244 | outParams, |
| 245 | keySizeInBits, |
| 246 | privForm, |
| 247 | pubForm, |
| 248 | pubIsRef, |
| 249 | quiet)) { |
| 250 | return 1; |
| 251 | } |
| 252 | |
| 253 | /* note this MUST match the params either specified or generated in previous |
| 254 | * call */ |
| 255 | if((inParams == NULL) && (outParams == NULL)) { |
| 256 | printf("***BRRZAP! Must provide a way to match D-H parameters!\n"); |
| 257 | exit(1); |
| 258 | } |
| 259 | const CSSM_DATA *theParams = inParams; |
| 260 | if(theParams == NULL) { |
| 261 | theParams = outParams; |
| 262 | } |
| 263 | if(dhKeyGen(cspHand, |
| 264 | &theirPub, |
| 265 | &theirPriv, |
| 266 | theParams, |
| 267 | NULL, // outParams |
| 268 | keySizeInBits, |
| 269 | privForm, |
| 270 | pubForm, |
| 271 | pubIsRef, |
| 272 | quiet)) { |
| 273 | return 1; |
| 274 | } |
| 275 | |
| 276 | /* derive two keys, ensure they match */ |
| 277 | CSSM_KEY myDerive; |
| 278 | CSSM_KEY theirDerive; |
| 279 | if(dhKeyExchange(cspHand, |
| 280 | &myPriv, |
| 281 | &theirPub, |
| 282 | &myDerive, |
| 283 | DERIVE_KEY_SIZE, |
| 284 | DERIVE_KEY_ALG, |
| 285 | CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT, |
| 286 | CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, |
| 287 | quiet)) { |
| 288 | return testError(quiet); |
| 289 | } |
| 290 | if(dhKeyExchange(cspHand, |
| 291 | &theirPriv, |
| 292 | &myPub, |
| 293 | &theirDerive, |
| 294 | DERIVE_KEY_SIZE, |
| 295 | DERIVE_KEY_ALG, |
| 296 | CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT, |
| 297 | CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, |
| 298 | quiet)) { |
| 299 | return testError(quiet); |
| 300 | } |
| 301 | |
| 302 | if(!appCompareCssmData(&myDerive.KeyData, &theirDerive.KeyData)) { |
| 303 | printf("***Key Exchange data miscompare***\n"); |
| 304 | rtn = testError(quiet); |
| 305 | } |
| 306 | cspFreeKey(cspHand, &myPub); |
| 307 | cspFreeKey(cspHand, &myPriv); |
| 308 | cspFreeKey(cspHand, &theirPub); |
| 309 | cspFreeKey(cspHand, &theirPriv); |
| 310 | cspFreeKey(cspHand, &myDerive); |
| 311 | cspFreeKey(cspHand, &theirDerive); |
| 312 | return rtn; |
| 313 | } |
| 314 | |
| 315 | int main(int argc, char **argv) |
| 316 | { |
| 317 | int arg; |
| 318 | char *argp; |
| 319 | CSSM_CSP_HANDLE cspHand; |
| 320 | unsigned loop; |
| 321 | int i; |
| 322 | CSSM_DATA inParams = {0, NULL}; |
| 323 | CSSM_DATA outParams = {0, NULL}; |
| 324 | CSSM_DATA_PTR inParamPtr = NULL; |
| 325 | CSSM_DATA_PTR outParamPtr = NULL; |
| 326 | |
| 327 | /* user-spec'd parameters */ |
| 328 | unsigned keySize = KEY_SIZE_DEF; |
| 329 | unsigned pauseInterval = 0; |
| 330 | unsigned loops = LOOPS_DEF; |
| 331 | CSSM_BOOL quiet = CSSM_FALSE; |
| 332 | CSSM_BOOL verbose = CSSM_FALSE; |
| 333 | CSSM_BOOL bareCsp = CSSM_TRUE; |
| 334 | char *inFileName = NULL; |
| 335 | char *outFileName = NULL; |
| 336 | /* default: "use default blob form", i.e., PKCS3 */ |
| 337 | CSSM_KEYBLOB_FORMAT privForm = CSSM_KEYBLOB_RAW_FORMAT_NONE; |
| 338 | CSSM_KEYBLOB_FORMAT pubForm = CSSM_KEYBLOB_RAW_FORMAT_NONE; |
| 339 | CSSM_BOOL pubIsRef = CSSM_FALSE; |
| 340 | |
| 341 | for(arg=1; arg<argc; arg++) { |
| 342 | argp = argv[arg]; |
| 343 | switch(argp[0]) { |
| 344 | case 'k': |
| 345 | keySize = atoi(&argp[2]); |
| 346 | break; |
| 347 | case 'l': |
| 348 | loops = atoi(&argp[2]); |
| 349 | break; |
| 350 | case 'p': |
| 351 | pauseInterval = atoi(&argp[2]); |
| 352 | break; |
| 353 | case 'i': |
| 354 | inFileName = &argp[2]; |
| 355 | break; |
| 356 | case 'o': |
| 357 | outFileName = &argp[2]; |
| 358 | break; |
| 359 | case 'D': |
| 360 | bareCsp = CSSM_FALSE; |
| 361 | break; |
| 362 | case '8': |
| 363 | privForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS8; |
| 364 | break; |
| 365 | case 'x': |
| 366 | pubForm = CSSM_KEYBLOB_RAW_FORMAT_X509; |
| 367 | break; |
| 368 | case 'f': |
| 369 | pubIsRef = CSSM_TRUE; |
| 370 | break; |
| 371 | case 'q': |
| 372 | quiet = CSSM_TRUE; |
| 373 | break; |
| 374 | case 'v': |
| 375 | verbose = CSSM_TRUE; |
| 376 | break; |
| 377 | default: |
| 378 | usage(argv); |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | /* Actually this test does NOT run with CSPDL */ |
| 383 | if(!bareCsp) { |
| 384 | printf("***%s does not run with CSPDL; aborting.\n", argv[0]); |
| 385 | exit(1); |
| 386 | } |
| 387 | |
| 388 | cspHand = cspDlDbStartup(bareCsp, NULL); |
| 389 | if(cspHand == 0) { |
| 390 | exit(1); |
| 391 | } |
| 392 | |
| 393 | /* optionally fetch algorithm parameters from a file */ |
| 394 | if(inFileName) { |
| 395 | unsigned len; |
| 396 | int r = readFile(inFileName, &inParams.Data, &len); |
| 397 | if(r) { |
| 398 | printf("***Can't read parameters from %s; aborting.\n", |
| 399 | inFileName); |
| 400 | exit(1); |
| 401 | } |
| 402 | inParams.Length = len; |
| 403 | /* constant from now on */ |
| 404 | inParamPtr = &inParams; |
| 405 | } |
| 406 | else { |
| 407 | /* first time thru, no user-supplied parameters; generate them and |
| 408 | * save in outParams */ |
| 409 | outParamPtr = &outParams; |
| 410 | } |
| 411 | printf("Starting dhTest; args: "); |
| 412 | for(i=1; i<argc; i++) { |
| 413 | printf("%s ", argv[i]); |
| 414 | } |
| 415 | printf("\n"); |
| 416 | for(loop=1; ; loop++) { |
| 417 | if(!quiet) { |
| 418 | printf("...Loop %d\n", loop); |
| 419 | } |
| 420 | i = doTest(cspHand, inParamPtr, outParamPtr, keySize, privForm, |
| 421 | pubForm, pubIsRef, quiet); |
| 422 | if(i) { |
| 423 | break; |
| 424 | } |
| 425 | if(loop == 1) { |
| 426 | /* first time thru */ |
| 427 | if(outFileName) { |
| 428 | /* save parameters for another run */ |
| 429 | i = writeFile(outFileName, outParams.Data, |
| 430 | outParams.Length); |
| 431 | if(i) { |
| 432 | printf("***Error writing params to %s; continuing.\n", |
| 433 | outFileName); |
| 434 | } |
| 435 | else { |
| 436 | if(!quiet) { |
| 437 | printf("...wrote %lu bytes to %s\n", |
| 438 | outParams.Length, outFileName); |
| 439 | } |
| 440 | } |
| 441 | } |
| 442 | if(!inFileName) { |
| 443 | /* from now on, use the parameters we just generated */ |
| 444 | inParamPtr = &outParams; |
| 445 | } |
| 446 | /* and in any case don't fetch any more params */ |
| 447 | outParamPtr = NULL; |
| 448 | } |
| 449 | if(loops && (loop == loops)) { |
| 450 | break; |
| 451 | } |
| 452 | if(pauseInterval && ((loop % pauseInterval) == 0)) { |
| 453 | char inch; |
| 454 | |
| 455 | fpurge(stdin); |
| 456 | printf("Hit CR to proceed, q to quit: "); |
| 457 | inch = getchar(); |
| 458 | if(inch == 'q') { |
| 459 | break; |
| 460 | } |
| 461 | } |
| 462 | } |
| 463 | CSSM_ModuleDetach(cspHand); |
| 464 | if(!quiet) { |
| 465 | printf("OK\n"); |
| 466 | } |
| 467 | return 0; |
| 468 | } |