ecperf.c (18504B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "blapi.h" 6 #include "ec.h" 7 #include "ecl-curve.h" 8 #include "prprf.h" 9 #include "basicutil.h" 10 #include "pkcs11.h" 11 #include "nspr.h" 12 #include <stdio.h> 13 14 #define __PASTE(x, y) x##y 15 16 /* 17 * Get the NSS specific PKCS #11 function names. 18 */ 19 #undef CK_PKCS11_FUNCTION_INFO 20 #undef CK_NEED_ARG_LIST 21 22 #define CK_EXTERN extern 23 #define CK_PKCS11_FUNCTION_INFO(func) \ 24 CK_RV __PASTE(NS, func) 25 #define CK_NEED_ARG_LIST 1 26 27 #include "pkcs11f.h" 28 29 typedef SECStatus (*op_func)(void *, void *, void *); 30 typedef SECStatus (*pk11_op_func)(CK_SESSION_HANDLE, void *, void *, void *); 31 32 typedef struct ThreadDataStr { 33 op_func op; 34 void *p1; 35 void *p2; 36 void *p3; 37 int iters; 38 PRLock *lock; 39 int count; 40 SECStatus status; 41 int isSign; 42 } ThreadData; 43 44 typedef SECItem SECKEYECParams; 45 46 void 47 PKCS11Thread(void *data) 48 { 49 ThreadData *threadData = (ThreadData *)data; 50 pk11_op_func op = (pk11_op_func)threadData->op; 51 int iters = threadData->iters; 52 unsigned char sigData[256]; 53 SECItem sig; 54 CK_SESSION_HANDLE session; 55 CK_RV crv; 56 void *tmp = NULL; 57 58 threadData->status = SECSuccess; 59 threadData->count = 0; 60 61 /* get our thread's session */ 62 PR_Lock(threadData->lock); 63 crv = NSC_OpenSession(1, CKF_SERIAL_SESSION, NULL, 0, &session); 64 PR_Unlock(threadData->lock); 65 if (crv != CKR_OK) { 66 return; 67 } 68 69 if (threadData->isSign) { 70 sig.data = sigData; 71 sig.len = sizeof(sigData); 72 tmp = threadData->p2; 73 threadData->p2 = (void *)&sig; 74 } 75 76 while (iters--) { 77 threadData->status = (*op)(session, threadData->p1, 78 threadData->p2, threadData->p3); 79 if (threadData->status != SECSuccess) { 80 break; 81 } 82 threadData->count++; 83 } 84 85 if (threadData->isSign) { 86 threadData->p2 = tmp; 87 } 88 return; 89 } 90 91 void 92 genericThread(void *data) 93 { 94 ThreadData *threadData = (ThreadData *)data; 95 int iters = threadData->iters; 96 unsigned char sigData[256]; 97 SECItem sig; 98 void *tmp = NULL; 99 100 threadData->status = SECSuccess; 101 threadData->count = 0; 102 103 if (threadData->isSign) { 104 sig.data = sigData; 105 sig.len = sizeof(sigData); 106 tmp = threadData->p2; 107 threadData->p2 = (void *)&sig; 108 } 109 110 while (iters--) { 111 threadData->status = (*threadData->op)(threadData->p1, 112 threadData->p2, threadData->p3); 113 if (threadData->status != SECSuccess) { 114 break; 115 } 116 threadData->count++; 117 } 118 119 if (threadData->isSign) { 120 threadData->p2 = tmp; 121 } 122 return; 123 } 124 125 /* Time iter repetitions of operation op. */ 126 SECStatus 127 M_TimeOperation(void (*threadFunc)(void *), 128 op_func opfunc, char *op, void *param1, void *param2, 129 void *param3, int iters, int numThreads, PRLock *lock, 130 CK_SESSION_HANDLE session, int isSign, double *rate) 131 { 132 double dUserTime; 133 int i, total; 134 PRIntervalTime startTime, totalTime; 135 PRThread **threadIDs; 136 ThreadData *threadData; 137 pk11_op_func pk11_op = (pk11_op_func)opfunc; 138 SECStatus rv; 139 140 /* verify operation works before testing performance */ 141 if (session) { 142 rv = (*pk11_op)(session, param1, param2, param3); 143 } else { 144 rv = (*opfunc)(param1, param2, param3); 145 } 146 if (rv != SECSuccess) { 147 SECU_PrintError("Error:", op); 148 return rv; 149 } 150 151 /* get Data structures */ 152 threadIDs = (PRThread **)PORT_Alloc(numThreads * sizeof(PRThread *)); 153 threadData = (ThreadData *)PORT_Alloc(numThreads * sizeof(ThreadData)); 154 155 startTime = PR_Now(); 156 if (numThreads == 1) { 157 for (i = 0; i < iters; i++) { 158 if (session) { 159 rv = (*pk11_op)(session, param1, param2, param3); 160 } else { 161 rv = (*opfunc)(param1, param2, param3); 162 } 163 if (rv != SECSuccess) { 164 PORT_Free(threadIDs); 165 PORT_Free(threadData); 166 SECU_PrintError("Error:", op); 167 return rv; 168 } 169 } 170 total = iters; 171 } else { 172 for (i = 0; i < numThreads; i++) { 173 threadData[i].op = opfunc; 174 threadData[i].p1 = (void *)param1; 175 threadData[i].p2 = (void *)param2; 176 threadData[i].p3 = (void *)param3; 177 threadData[i].iters = iters; 178 threadData[i].lock = lock; 179 threadData[i].isSign = isSign; 180 threadIDs[i] = PR_CreateThread(PR_USER_THREAD, threadFunc, 181 (void *)&threadData[i], PR_PRIORITY_NORMAL, 182 PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); 183 } 184 185 total = 0; 186 for (i = 0; i < numThreads; i++) { 187 PR_JoinThread(threadIDs[i]); 188 /* check the status */ 189 total += threadData[i].count; 190 } 191 } 192 193 totalTime = PR_Now() - startTime; 194 /* SecondsToInterval seems to be broken here ... */ 195 dUserTime = (double)totalTime / (double)1000000; 196 if (dUserTime) { 197 printf(" %-15s count:%4d sec: %3.2f op/sec: %6.2f\n", 198 op, total, dUserTime, (double)total / dUserTime); 199 if (rate) { 200 *rate = ((double)total) / dUserTime; 201 } 202 } 203 PORT_Free(threadIDs); 204 PORT_Free(threadData); 205 206 return SECSuccess; 207 } 208 209 /* Test curve using specific field arithmetic. */ 210 #define ECTEST_NAMED(name_c, name_v) \ 211 if (usefreebl) { \ 212 printf("Testing %s using freebl implementation...\n", name_c); \ 213 rv = ectest_curve_freebl(name_v, iterations, numThreads); \ 214 if (rv != SECSuccess) \ 215 goto cleanup; \ 216 printf("... okay.\n"); \ 217 } \ 218 if (usepkcs11) { \ 219 printf("Testing %s using pkcs11 implementation...\n", name_c); \ 220 rv = ectest_curve_pkcs11(name_v, iterations, numThreads); \ 221 if (rv != SECSuccess) \ 222 goto cleanup; \ 223 printf("... okay.\n"); \ 224 } 225 226 #define PK11_SETATTRS(x, id, v, l) \ 227 (x)->type = (id); \ 228 (x)->pValue = (v); \ 229 (x)->ulValueLen = (l); 230 231 SECStatus 232 PKCS11_Derive(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hKey, 233 CK_MECHANISM *pMech, int *dummy) 234 { 235 CK_RV crv; 236 CK_OBJECT_HANDLE newKey; 237 CK_BBOOL cktrue = CK_TRUE; 238 CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; 239 CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; 240 CK_ATTRIBUTE keyTemplate[3]; 241 CK_ATTRIBUTE *attrs = keyTemplate; 242 243 PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass)); 244 attrs++; 245 PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType)); 246 attrs++; 247 PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, 1); 248 attrs++; 249 250 crv = NSC_DeriveKey(session, pMech, *hKey, keyTemplate, 3, &newKey); 251 if (crv != CKR_OK) { 252 printf("Derive Failed CK_RV=0x%x\n", (int)crv); 253 return SECFailure; 254 } 255 return SECSuccess; 256 } 257 258 SECStatus 259 PKCS11_Sign(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hKey, 260 SECItem *sig, SECItem *digest) 261 { 262 CK_RV crv; 263 CK_MECHANISM mech; 264 CK_ULONG sigLen = sig->len; 265 266 mech.mechanism = CKM_ECDSA; 267 mech.pParameter = NULL; 268 mech.ulParameterLen = 0; 269 270 crv = NSC_SignInit(session, &mech, *hKey); 271 if (crv != CKR_OK) { 272 printf("Sign Failed CK_RV=0x%x\n", (int)crv); 273 return SECFailure; 274 } 275 crv = NSC_Sign(session, digest->data, digest->len, sig->data, &sigLen); 276 if (crv != CKR_OK) { 277 printf("Sign Failed CK_RV=0x%x\n", (int)crv); 278 return SECFailure; 279 } 280 sig->len = (unsigned int)sigLen; 281 return SECSuccess; 282 } 283 284 SECStatus 285 PKCS11_Verify(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hKey, 286 SECItem *sig, SECItem *digest) 287 { 288 CK_RV crv; 289 CK_MECHANISM mech; 290 291 mech.mechanism = CKM_ECDSA; 292 mech.pParameter = NULL; 293 mech.ulParameterLen = 0; 294 295 crv = NSC_VerifyInit(session, &mech, *hKey); 296 if (crv != CKR_OK) { 297 printf("Verify Failed CK_RV=0x%x\n", (int)crv); 298 return SECFailure; 299 } 300 crv = NSC_Verify(session, digest->data, digest->len, sig->data, sig->len); 301 if (crv != CKR_OK) { 302 printf("Verify Failed CK_RV=0x%x\n", (int)crv); 303 return SECFailure; 304 } 305 return SECSuccess; 306 } 307 308 /* Performs basic tests of elliptic curve cryptography over prime fields. 309 * If tests fail, then it prints an error message, aborts, and returns an 310 * error code. Otherwise, returns 0. */ 311 SECStatus 312 ectest_curve_pkcs11(ECCurveName curve, int iterations, int numThreads) 313 { 314 CK_OBJECT_HANDLE ecPriv; 315 CK_OBJECT_HANDLE ecPub; 316 CK_SESSION_HANDLE session; 317 SECItem sig; 318 SECItem digest; 319 SECKEYECParams ecParams; 320 CK_MECHANISM mech; 321 CK_ECDH1_DERIVE_PARAMS ecdh_params; 322 unsigned char sigData[256]; 323 unsigned char digestData[20]; 324 unsigned char pubKeyData[256]; 325 PRLock *lock = NULL; 326 double signRate, deriveRate = 0; 327 CK_ATTRIBUTE template; 328 SECStatus rv; 329 CK_RV crv; 330 331 ecParams.data = NULL; 332 ecParams.len = 0; 333 rv = SECU_ecName2params(curve, &ecParams); 334 if (rv != SECSuccess) { 335 goto cleanup; 336 } 337 338 crv = NSC_OpenSession(1, CKF_SERIAL_SESSION, NULL, 0, &session); 339 if (crv != CKR_OK) { 340 printf("OpenSession Failed CK_RV=0x%x\n", (int)crv); 341 return SECFailure; 342 } 343 344 PORT_Memset(digestData, 0xa5, sizeof(digestData)); 345 digest.data = digestData; 346 digest.len = sizeof(digestData); 347 sig.data = sigData; 348 sig.len = sizeof(sigData); 349 350 template.type = CKA_EC_PARAMS; 351 template.pValue = ecParams.data; 352 template.ulValueLen = ecParams.len; 353 mech.mechanism = CKM_EC_KEY_PAIR_GEN; 354 mech.pParameter = NULL; 355 mech.ulParameterLen = 0; 356 crv = NSC_GenerateKeyPair(session, &mech, 357 &template, 1, NULL, 0, &ecPub, &ecPriv); 358 if (crv != CKR_OK) { 359 printf("GenerateKeyPair Failed CK_RV=0x%x\n", (int)crv); 360 return SECFailure; 361 } 362 363 template.type = CKA_EC_POINT; 364 template.pValue = pubKeyData; 365 template.ulValueLen = sizeof(pubKeyData); 366 crv = NSC_GetAttributeValue(session, ecPub, &template, 1); 367 if (crv != CKR_OK) { 368 printf("GenerateKeyPair Failed CK_RV=0x%x\n", (int)crv); 369 return SECFailure; 370 } 371 372 ecdh_params.kdf = CKD_NULL; 373 ecdh_params.ulSharedDataLen = 0; 374 ecdh_params.pSharedData = NULL; 375 ecdh_params.ulPublicDataLen = template.ulValueLen; 376 ecdh_params.pPublicData = template.pValue; 377 378 mech.mechanism = CKM_ECDH1_DERIVE; 379 mech.pParameter = (void *)&ecdh_params; 380 mech.ulParameterLen = sizeof(ecdh_params); 381 382 lock = PR_NewLock(); 383 384 if (ecCurve_map[curve]->usage & KU_KEY_AGREEMENT) { 385 rv = M_TimeOperation(PKCS11Thread, (op_func)PKCS11_Derive, "ECDH_Derive", 386 &ecPriv, &mech, NULL, iterations, numThreads, 387 lock, session, 0, &deriveRate); 388 if (rv != SECSuccess) { 389 goto cleanup; 390 } 391 } 392 393 if (ecCurve_map[curve]->usage & KU_DIGITAL_SIGNATURE) { 394 rv = M_TimeOperation(PKCS11Thread, (op_func)PKCS11_Sign, "ECDSA_Sign", 395 (void *)&ecPriv, &sig, &digest, iterations, numThreads, 396 lock, session, 1, &signRate); 397 if (rv != SECSuccess) { 398 goto cleanup; 399 } 400 printf(" ECDHE max rate = %.2f\n", (deriveRate + signRate) / 4.0); 401 /* get a signature */ 402 rv = PKCS11_Sign(session, &ecPriv, &sig, &digest); 403 if (rv != SECSuccess) { 404 goto cleanup; 405 } 406 rv = M_TimeOperation(PKCS11Thread, (op_func)PKCS11_Verify, "ECDSA_Verify", 407 (void *)&ecPub, &sig, &digest, iterations, numThreads, 408 lock, session, 0, NULL); 409 if (rv != SECSuccess) { 410 goto cleanup; 411 } 412 } 413 414 cleanup: 415 if (lock) { 416 PR_DestroyLock(lock); 417 } 418 return rv; 419 } 420 421 SECStatus 422 ECDH_DeriveWrap(ECPrivateKey *priv, ECPublicKey *pub, int *dummy) 423 { 424 SECItem secret; 425 unsigned char secretData[256]; 426 SECStatus rv; 427 428 secret.data = secretData; 429 secret.len = sizeof(secretData); 430 431 rv = ECDH_Derive(&pub->publicValue, &pub->ecParams, 432 &priv->privateValue, 0, &secret); 433 SECITEM_FreeItem(&secret, PR_FALSE); 434 return rv; 435 } 436 437 /* Performs basic tests of elliptic curve cryptography over prime fields. 438 * If tests fail, then it prints an error message, aborts, and returns an 439 * error code. Otherwise, returns 0. */ 440 SECStatus 441 ectest_curve_freebl(ECCurveName curve, int iterations, int numThreads) 442 { 443 ECParams ecParams = { 0 }; 444 ECPrivateKey *ecPriv = NULL; 445 ECPublicKey ecPub; 446 SECItem sig; 447 SECItem digest; 448 unsigned char sigData[256]; 449 unsigned char digestData[20]; 450 double signRate, deriveRate = 0; 451 SECStatus rv = SECFailure; 452 PLArenaPool *arena; 453 SECItem ecEncodedParams = { siBuffer, NULL, 0 }; 454 455 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 456 if (!arena) { 457 return SECFailure; 458 } 459 460 if ((curve < ECCurve_noName) || (curve > ECCurve_pastLastCurve)) { 461 PORT_FreeArena(arena, PR_FALSE); 462 return SECFailure; 463 } 464 465 rv = SECU_ecName2params(curve, &ecEncodedParams); 466 if (rv != SECSuccess) { 467 goto cleanup; 468 } 469 EC_FillParams(arena, &ecEncodedParams, &ecParams); 470 471 PORT_Memset(digestData, 0xa5, sizeof(digestData)); 472 digest.data = digestData; 473 digest.len = sizeof(digestData); 474 sig.data = sigData; 475 sig.len = sizeof(sigData); 476 477 rv = EC_NewKey(&ecParams, &ecPriv); 478 if (rv != SECSuccess) { 479 goto cleanup; 480 } 481 ecPub.ecParams = ecParams; 482 ecPub.publicValue = ecPriv->publicValue; 483 484 if (ecCurve_map[curve]->usage & KU_KEY_AGREEMENT) { 485 rv = M_TimeOperation(genericThread, (op_func)ECDH_DeriveWrap, "ECDH_Derive", 486 ecPriv, &ecPub, NULL, iterations, numThreads, 0, 0, 0, &deriveRate); 487 if (rv != SECSuccess) { 488 goto cleanup; 489 } 490 } 491 492 if (ecCurve_map[curve]->usage & KU_DIGITAL_SIGNATURE) { 493 rv = M_TimeOperation(genericThread, (op_func)ECDSA_SignDigest, "ECDSA_Sign", 494 ecPriv, &sig, &digest, iterations, numThreads, 0, 0, 1, &signRate); 495 if (rv != SECSuccess) 496 goto cleanup; 497 printf(" ECDHE max rate = %.2f\n", (deriveRate + signRate) / 4.0); 498 rv = ECDSA_SignDigest(ecPriv, &sig, &digest); 499 if (rv != SECSuccess) { 500 goto cleanup; 501 } 502 rv = M_TimeOperation(genericThread, (op_func)ECDSA_VerifyDigest, "ECDSA_Verify", 503 &ecPub, &sig, &digest, iterations, numThreads, 0, 0, 0, NULL); 504 if (rv != SECSuccess) { 505 goto cleanup; 506 } 507 } 508 509 cleanup: 510 SECITEM_FreeItem(&ecEncodedParams, PR_FALSE); 511 PORT_FreeArena(arena, PR_FALSE); 512 if (ecPriv) { 513 PORT_FreeArena(ecPriv->ecParams.arena, PR_FALSE); 514 } 515 return rv; 516 } 517 518 /* Prints help information. */ 519 void 520 printUsage(char *prog) 521 { 522 printf("Usage: %s [-i iterations] [-t threads ] [-ans] [-fp] [-Al]\n" 523 "-a: ansi\n-n: nist\n-s: secp\n-f: usefreebl\n-p: usepkcs11\n-A: all\n", 524 prog); 525 } 526 527 /* Performs tests of elliptic curve cryptography over prime fields If 528 * tests fail, then it prints an error message, aborts, and returns an 529 * error code. Otherwise, returns 0. */ 530 int 531 main(int argv, char **argc) 532 { 533 int ansi = 0; 534 int nist = 0; 535 int secp = 0; 536 int usefreebl = 0; 537 int usepkcs11 = 0; 538 int i; 539 SECStatus rv = SECSuccess; 540 int iterations = 100; 541 int numThreads = 1; 542 543 const CK_C_INITIALIZE_ARGS pk11args = { 544 NULL, NULL, NULL, NULL, CKF_LIBRARY_CANT_CREATE_OS_THREADS, 545 (void *)"flags=readOnly,noCertDB,noModDB", NULL 546 }; 547 548 /* read command-line arguments */ 549 for (i = 1; i < argv; i++) { 550 if (PL_strcasecmp(argc[i], "-i") == 0) { 551 i++; 552 iterations = atoi(argc[i]); 553 } else if (PL_strcasecmp(argc[i], "-t") == 0) { 554 i++; 555 numThreads = atoi(argc[i]); 556 } else if (PL_strcasecmp(argc[i], "-A") == 0) { 557 ansi = nist = secp = 1; 558 usepkcs11 = usefreebl = 1; 559 } else if (PL_strcasecmp(argc[i], "-a") == 0) { 560 ansi = 1; 561 } else if (PL_strcasecmp(argc[i], "-n") == 0) { 562 nist = 1; 563 } else if (PL_strcasecmp(argc[i], "-s") == 0) { 564 secp = 1; 565 } else if (PL_strcasecmp(argc[i], "-p") == 0) { 566 usepkcs11 = 1; 567 } else if (PL_strcasecmp(argc[i], "-f") == 0) { 568 usefreebl = 1; 569 } else { 570 printUsage(argc[0]); 571 return 0; 572 } 573 } 574 575 if ((ansi | nist | secp) == 0) { 576 nist = 1; 577 } 578 if ((usepkcs11 | usefreebl) == 0) { 579 usefreebl = 1; 580 } 581 582 rv = RNG_RNGInit(); 583 if (rv != SECSuccess) { 584 SECU_PrintError("Error:", "RNG_RNGInit"); 585 return -1; 586 } 587 RNG_SystemInfoForRNG(); 588 589 rv = SECOID_Init(); 590 if (rv != SECSuccess) { 591 SECU_PrintError("Error:", "SECOID_Init"); 592 goto cleanup; 593 } 594 595 if (usepkcs11) { 596 CK_RV crv = NSC_Initialize((CK_VOID_PTR)&pk11args); 597 if (crv != CKR_OK) { 598 fprintf(stderr, "NSC_Initialize failed crv=0x%x\n", (unsigned int)crv); 599 return SECFailure; 600 } 601 } 602 603 /* specific arithmetic tests */ 604 if (nist) { 605 ECTEST_NAMED("NIST-P256", ECCurve_NIST_P256); 606 ECTEST_NAMED("NIST-P384", ECCurve_NIST_P384); 607 ECTEST_NAMED("NIST-P521", ECCurve_NIST_P521); 608 ECTEST_NAMED("Curve25519", ECCurve25519); 609 } 610 611 cleanup: 612 rv |= SECOID_Shutdown(); 613 RNG_RNGShutdown(); 614 615 if (rv != SECSuccess) { 616 printf("Error: exiting with error value\n"); 617 } 618 return rv; 619 }