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