| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include "blapi.h" |
| #include "ec.h" |
| #include "ecl-curve.h" |
| #include "prprf.h" |
| #include "basicutil.h" |
| #include "pkcs11.h" |
| #include "nspr.h" |
| #include <stdio.h> |
| |
| #define __PASTE(x, y) x##y |
| |
| /* |
| * Get the NSS specific PKCS #11 function names. |
| */ |
| #undef CK_PKCS11_FUNCTION_INFO |
| #undef CK_NEED_ARG_LIST |
| |
| #define CK_EXTERN extern |
| #define CK_PKCS11_FUNCTION_INFO(func) \ |
| CK_RV __PASTE(NS, func) |
| #define CK_NEED_ARG_LIST 1 |
| |
| #include "pkcs11f.h" |
| |
| typedef SECStatus (*op_func)(void *, void *, void *); |
| typedef SECStatus (*pk11_op_func)(CK_SESSION_HANDLE, void *, void *, void *); |
| |
| typedef struct ThreadDataStr { |
| op_func op; |
| void *p1; |
| void *p2; |
| void *p3; |
| int iters; |
| PRLock *lock; |
| int count; |
| SECStatus status; |
| int isSign; |
| } ThreadData; |
| |
| typedef SECItem SECKEYECParams; |
| |
| void |
| PKCS11Thread(void *data) |
| { |
| ThreadData *threadData = (ThreadData *)data; |
| pk11_op_func op = (pk11_op_func)threadData->op; |
| int iters = threadData->iters; |
| unsigned char sigData[256]; |
| SECItem sig; |
| CK_SESSION_HANDLE session; |
| CK_RV crv; |
| |
| threadData->status = SECSuccess; |
| threadData->count = 0; |
| |
| /* get our thread's session */ |
| PR_Lock(threadData->lock); |
| crv = NSC_OpenSession(1, CKF_SERIAL_SESSION, NULL, 0, &session); |
| PR_Unlock(threadData->lock); |
| if (crv != CKR_OK) { |
| return; |
| } |
| |
| if (threadData->isSign) { |
| sig.data = sigData; |
| sig.len = sizeof(sigData); |
| threadData->p2 = (void *)&sig; |
| } |
| |
| while (iters--) { |
| threadData->status = (*op)(session, threadData->p1, |
| threadData->p2, threadData->p3); |
| if (threadData->status != SECSuccess) { |
| break; |
| } |
| threadData->count++; |
| } |
| return; |
| } |
| |
| void |
| genericThread(void *data) |
| { |
| ThreadData *threadData = (ThreadData *)data; |
| int iters = threadData->iters; |
| unsigned char sigData[256]; |
| SECItem sig; |
| |
| threadData->status = SECSuccess; |
| threadData->count = 0; |
| |
| if (threadData->isSign) { |
| sig.data = sigData; |
| sig.len = sizeof(sigData); |
| threadData->p2 = (void *)&sig; |
| } |
| |
| while (iters--) { |
| threadData->status = (*threadData->op)(threadData->p1, |
| threadData->p2, threadData->p3); |
| if (threadData->status != SECSuccess) { |
| break; |
| } |
| threadData->count++; |
| } |
| return; |
| } |
| |
| /* Time iter repetitions of operation op. */ |
| 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) |
| { |
| double dUserTime; |
| int i, total; |
| PRIntervalTime startTime, totalTime; |
| PRThread **threadIDs; |
| ThreadData *threadData; |
| pk11_op_func pk11_op = (pk11_op_func)opfunc; |
| SECStatus rv; |
| |
| /* verify operation works before testing performance */ |
| if (session) { |
| rv = (*pk11_op)(session, param1, param2, param3); |
| } else { |
| rv = (*opfunc)(param1, param2, param3); |
| } |
| if (rv != SECSuccess) { |
| SECU_PrintError("Error:", op); |
| return rv; |
| } |
| |
| /* get Data structures */ |
| threadIDs = (PRThread **)PORT_Alloc(numThreads * sizeof(PRThread *)); |
| threadData = (ThreadData *)PORT_Alloc(numThreads * sizeof(ThreadData)); |
| |
| startTime = PR_Now(); |
| if (numThreads == 1) { |
| for (i = 0; i < iters; i++) { |
| if (session) { |
| rv = (*pk11_op)(session, param1, param2, param3); |
| } else { |
| rv = (*opfunc)(param1, param2, param3); |
| } |
| if (rv != SECSuccess) { |
| PORT_Free(threadIDs); |
| PORT_Free(threadData); |
| SECU_PrintError("Error:", op); |
| return rv; |
| } |
| } |
| total = iters; |
| } else { |
| for (i = 0; i < numThreads; i++) { |
| threadData[i].op = opfunc; |
| threadData[i].p1 = (void *)param1; |
| threadData[i].p2 = (void *)param2; |
| threadData[i].p3 = (void *)param3; |
| threadData[i].iters = iters; |
| threadData[i].lock = lock; |
| threadData[i].isSign = isSign; |
| threadIDs[i] = PR_CreateThread(PR_USER_THREAD, threadFunc, |
| (void *)&threadData[i], PR_PRIORITY_NORMAL, |
| PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); |
| } |
| |
| total = 0; |
| for (i = 0; i < numThreads; i++) { |
| PR_JoinThread(threadIDs[i]); |
| /* check the status */ |
| total += threadData[i].count; |
| } |
| } |
| |
| totalTime = PR_Now() - startTime; |
| /* SecondsToInterval seems to be broken here ... */ |
| dUserTime = (double)totalTime / (double)1000000; |
| if (dUserTime) { |
| printf(" %-15s count:%4d sec: %3.2f op/sec: %6.2f\n", |
| op, total, dUserTime, (double)total / dUserTime); |
| if (rate) { |
| *rate = ((double)total) / dUserTime; |
| } |
| } |
| PORT_Free(threadIDs); |
| PORT_Free(threadData); |
| |
| return SECSuccess; |
| } |
| |
| /* Test curve using specific field arithmetic. */ |
| #define ECTEST_NAMED_GFP(name_c, name_v) \ |
| if (usefreebl) { \ |
| printf("Testing %s using freebl implementation...\n", name_c); \ |
| rv = ectest_curve_freebl(name_v, iterations, numThreads, ec_field_GFp); \ |
| if (rv != SECSuccess) \ |
| goto cleanup; \ |
| printf("... okay.\n"); \ |
| } \ |
| if (usepkcs11) { \ |
| printf("Testing %s using pkcs11 implementation...\n", name_c); \ |
| rv = ectest_curve_pkcs11(name_v, iterations, numThreads); \ |
| if (rv != SECSuccess) \ |
| goto cleanup; \ |
| printf("... okay.\n"); \ |
| } |
| |
| /* Test curve using specific field arithmetic. */ |
| #define ECTEST_NAMED_CUSTOM(name_c, name_v) \ |
| if (usefreebl) { \ |
| printf("Testing %s using freebl implementation...\n", name_c); \ |
| rv = ectest_curve_freebl(name_v, iterations, numThreads, ec_field_plain); \ |
| if (rv != SECSuccess) \ |
| goto cleanup; \ |
| printf("... okay.\n"); \ |
| } \ |
| if (usepkcs11) { \ |
| printf("Testing %s using pkcs11 implementation...\n", name_c); \ |
| rv = ectest_curve_pkcs11(name_v, iterations, numThreads); \ |
| if (rv != SECSuccess) \ |
| goto cleanup; \ |
| printf("... okay.\n"); \ |
| } |
| |
| #define PK11_SETATTRS(x, id, v, l) \ |
| (x)->type = (id); \ |
| (x)->pValue = (v); \ |
| (x)->ulValueLen = (l); |
| |
| SECStatus |
| PKCS11_Derive(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hKey, |
| CK_MECHANISM *pMech, int *dummy) |
| { |
| CK_RV crv; |
| CK_OBJECT_HANDLE newKey; |
| CK_BBOOL cktrue = CK_TRUE; |
| CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; |
| CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; |
| CK_ATTRIBUTE keyTemplate[3]; |
| CK_ATTRIBUTE *attrs = keyTemplate; |
| |
| PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass)); |
| attrs++; |
| PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType)); |
| attrs++; |
| PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, 1); |
| attrs++; |
| |
| crv = NSC_DeriveKey(session, pMech, *hKey, keyTemplate, 3, &newKey); |
| if (crv != CKR_OK) { |
| printf("Derive Failed CK_RV=0x%x\n", (int)crv); |
| return SECFailure; |
| } |
| return SECSuccess; |
| } |
| |
| SECStatus |
| PKCS11_Sign(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hKey, |
| SECItem *sig, SECItem *digest) |
| { |
| CK_RV crv; |
| CK_MECHANISM mech; |
| CK_ULONG sigLen = sig->len; |
| |
| mech.mechanism = CKM_ECDSA; |
| mech.pParameter = NULL; |
| mech.ulParameterLen = 0; |
| |
| crv = NSC_SignInit(session, &mech, *hKey); |
| if (crv != CKR_OK) { |
| printf("Sign Failed CK_RV=0x%x\n", (int)crv); |
| return SECFailure; |
| } |
| crv = NSC_Sign(session, digest->data, digest->len, sig->data, &sigLen); |
| if (crv != CKR_OK) { |
| printf("Sign Failed CK_RV=0x%x\n", (int)crv); |
| return SECFailure; |
| } |
| sig->len = (unsigned int)sigLen; |
| return SECSuccess; |
| } |
| |
| SECStatus |
| PKCS11_Verify(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hKey, |
| SECItem *sig, SECItem *digest) |
| { |
| CK_RV crv; |
| CK_MECHANISM mech; |
| |
| mech.mechanism = CKM_ECDSA; |
| mech.pParameter = NULL; |
| mech.ulParameterLen = 0; |
| |
| crv = NSC_VerifyInit(session, &mech, *hKey); |
| if (crv != CKR_OK) { |
| printf("Verify Failed CK_RV=0x%x\n", (int)crv); |
| return SECFailure; |
| } |
| crv = NSC_Verify(session, digest->data, digest->len, sig->data, sig->len); |
| if (crv != CKR_OK) { |
| printf("Verify Failed CK_RV=0x%x\n", (int)crv); |
| return SECFailure; |
| } |
| return SECSuccess; |
| } |
| |
| /* Performs basic tests of elliptic curve cryptography over prime fields. |
| * If tests fail, then it prints an error message, aborts, and returns an |
| * error code. Otherwise, returns 0. */ |
| SECStatus |
| ectest_curve_pkcs11(ECCurveName curve, int iterations, int numThreads) |
| { |
| CK_OBJECT_HANDLE ecPriv; |
| CK_OBJECT_HANDLE ecPub; |
| CK_SESSION_HANDLE session; |
| SECItem sig; |
| SECItem digest; |
| SECKEYECParams ecParams; |
| CK_MECHANISM mech; |
| CK_ECDH1_DERIVE_PARAMS ecdh_params; |
| unsigned char sigData[256]; |
| unsigned char digestData[20]; |
| unsigned char pubKeyData[256]; |
| PRLock *lock = NULL; |
| double signRate, deriveRate = 0; |
| CK_ATTRIBUTE template; |
| SECStatus rv; |
| CK_RV crv; |
| |
| ecParams.data = NULL; |
| ecParams.len = 0; |
| rv = SECU_ecName2params(curve, &ecParams); |
| if (rv != SECSuccess) { |
| goto cleanup; |
| } |
| |
| crv = NSC_OpenSession(1, CKF_SERIAL_SESSION, NULL, 0, &session); |
| if (crv != CKR_OK) { |
| printf("OpenSession Failed CK_RV=0x%x\n", (int)crv); |
| return SECFailure; |
| } |
| |
| PORT_Memset(digestData, 0xa5, sizeof(digestData)); |
| digest.data = digestData; |
| digest.len = sizeof(digestData); |
| sig.data = sigData; |
| sig.len = sizeof(sigData); |
| |
| template.type = CKA_EC_PARAMS; |
| template.pValue = ecParams.data; |
| template.ulValueLen = ecParams.len; |
| mech.mechanism = CKM_EC_KEY_PAIR_GEN; |
| mech.pParameter = NULL; |
| mech.ulParameterLen = 0; |
| crv = NSC_GenerateKeyPair(session, &mech, |
| &template, 1, NULL, 0, &ecPub, &ecPriv); |
| if (crv != CKR_OK) { |
| printf("GenerateKeyPair Failed CK_RV=0x%x\n", (int)crv); |
| return SECFailure; |
| } |
| |
| template.type = CKA_EC_POINT; |
| template.pValue = pubKeyData; |
| template.ulValueLen = sizeof(pubKeyData); |
| crv = NSC_GetAttributeValue(session, ecPub, &template, 1); |
| if (crv != CKR_OK) { |
| printf("GenerateKeyPair Failed CK_RV=0x%x\n", (int)crv); |
| return SECFailure; |
| } |
| |
| ecdh_params.kdf = CKD_NULL; |
| ecdh_params.ulSharedDataLen = 0; |
| ecdh_params.pSharedData = NULL; |
| ecdh_params.ulPublicDataLen = template.ulValueLen; |
| ecdh_params.pPublicData = template.pValue; |
| |
| mech.mechanism = CKM_ECDH1_DERIVE; |
| mech.pParameter = (void *)&ecdh_params; |
| mech.ulParameterLen = sizeof(ecdh_params); |
| |
| lock = PR_NewLock(); |
| |
| if (ecCurve_map[curve]->usage & KU_KEY_AGREEMENT) { |
| rv = M_TimeOperation(PKCS11Thread, (op_func)PKCS11_Derive, "ECDH_Derive", |
| &ecPriv, &mech, NULL, iterations, numThreads, |
| lock, session, 0, &deriveRate); |
| if (rv != SECSuccess) { |
| goto cleanup; |
| } |
| } |
| |
| if (ecCurve_map[curve]->usage & KU_DIGITAL_SIGNATURE) { |
| rv = M_TimeOperation(PKCS11Thread, (op_func)PKCS11_Sign, "ECDSA_Sign", |
| (void *)&ecPriv, &sig, &digest, iterations, numThreads, |
| lock, session, 1, &signRate); |
| if (rv != SECSuccess) { |
| goto cleanup; |
| } |
| printf(" ECDHE max rate = %.2f\n", (deriveRate + signRate) / 4.0); |
| /* get a signature */ |
| rv = PKCS11_Sign(session, &ecPriv, &sig, &digest); |
| if (rv != SECSuccess) { |
| goto cleanup; |
| } |
| rv = M_TimeOperation(PKCS11Thread, (op_func)PKCS11_Verify, "ECDSA_Verify", |
| (void *)&ecPub, &sig, &digest, iterations, numThreads, |
| lock, session, 0, NULL); |
| if (rv != SECSuccess) { |
| goto cleanup; |
| } |
| } |
| |
| cleanup: |
| if (lock) { |
| PR_DestroyLock(lock); |
| } |
| return rv; |
| } |
| |
| SECStatus |
| ECDH_DeriveWrap(ECPrivateKey *priv, ECPublicKey *pub, int *dummy) |
| { |
| SECItem secret; |
| unsigned char secretData[256]; |
| SECStatus rv; |
| |
| secret.data = secretData; |
| secret.len = sizeof(secretData); |
| |
| rv = ECDH_Derive(&pub->publicValue, &pub->ecParams, |
| &priv->privateValue, 0, &secret); |
| SECITEM_FreeItem(&secret, PR_FALSE); |
| return rv; |
| } |
| |
| /* Performs basic tests of elliptic curve cryptography over prime fields. |
| * If tests fail, then it prints an error message, aborts, and returns an |
| * error code. Otherwise, returns 0. */ |
| SECStatus |
| ectest_curve_freebl(ECCurveName curve, int iterations, int numThreads, |
| ECFieldType fieldType) |
| { |
| ECParams ecParams = { 0 }; |
| ECPrivateKey *ecPriv = NULL; |
| ECPublicKey ecPub; |
| SECItem sig; |
| SECItem digest; |
| unsigned char sigData[256]; |
| unsigned char digestData[20]; |
| double signRate, deriveRate = 0; |
| SECStatus rv = SECFailure; |
| PLArenaPool *arena; |
| SECItem ecEncodedParams = { siBuffer, NULL, 0 }; |
| |
| arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| if (!arena) { |
| return SECFailure; |
| } |
| |
| if ((curve < ECCurve_noName) || (curve > ECCurve_pastLastCurve)) { |
| PORT_FreeArena(arena, PR_FALSE); |
| return SECFailure; |
| } |
| |
| rv = SECU_ecName2params(curve, &ecEncodedParams); |
| if (rv != SECSuccess) { |
| goto cleanup; |
| } |
| EC_FillParams(arena, &ecEncodedParams, &ecParams); |
| |
| PORT_Memset(digestData, 0xa5, sizeof(digestData)); |
| digest.data = digestData; |
| digest.len = sizeof(digestData); |
| sig.data = sigData; |
| sig.len = sizeof(sigData); |
| |
| rv = EC_NewKey(&ecParams, &ecPriv); |
| if (rv != SECSuccess) { |
| goto cleanup; |
| } |
| ecPub.ecParams = ecParams; |
| ecPub.publicValue = ecPriv->publicValue; |
| |
| if (ecCurve_map[curve]->usage & KU_KEY_AGREEMENT) { |
| rv = M_TimeOperation(genericThread, (op_func)ECDH_DeriveWrap, "ECDH_Derive", |
| ecPriv, &ecPub, NULL, iterations, numThreads, 0, 0, 0, &deriveRate); |
| if (rv != SECSuccess) { |
| goto cleanup; |
| } |
| } |
| |
| if (ecCurve_map[curve]->usage & KU_DIGITAL_SIGNATURE) { |
| rv = M_TimeOperation(genericThread, (op_func)ECDSA_SignDigest, "ECDSA_Sign", |
| ecPriv, &sig, &digest, iterations, numThreads, 0, 0, 1, &signRate); |
| if (rv != SECSuccess) |
| goto cleanup; |
| printf(" ECDHE max rate = %.2f\n", (deriveRate + signRate) / 4.0); |
| rv = ECDSA_SignDigest(ecPriv, &sig, &digest); |
| if (rv != SECSuccess) { |
| goto cleanup; |
| } |
| rv = M_TimeOperation(genericThread, (op_func)ECDSA_VerifyDigest, "ECDSA_Verify", |
| &ecPub, &sig, &digest, iterations, numThreads, 0, 0, 0, NULL); |
| if (rv != SECSuccess) { |
| goto cleanup; |
| } |
| } |
| |
| cleanup: |
| SECITEM_FreeItem(&ecEncodedParams, PR_FALSE); |
| PORT_FreeArena(arena, PR_FALSE); |
| if (ecPriv) { |
| PORT_FreeArena(ecPriv->ecParams.arena, PR_FALSE); |
| } |
| return rv; |
| } |
| |
| /* Prints help information. */ |
| void |
| printUsage(char *prog) |
| { |
| printf("Usage: %s [-i iterations] [-t threads ] [-ans] [-fp] [-Al]\n" |
| "-a: ansi\n-n: nist\n-s: secp\n-f: usefreebl\n-p: usepkcs11\n-A: all\n", |
| prog); |
| } |
| |
| /* Performs tests of elliptic curve cryptography over prime fields If |
| * tests fail, then it prints an error message, aborts, and returns an |
| * error code. Otherwise, returns 0. */ |
| int |
| main(int argv, char **argc) |
| { |
| int ansi = 0; |
| int nist = 0; |
| int secp = 0; |
| int usefreebl = 0; |
| int usepkcs11 = 0; |
| int i; |
| SECStatus rv = SECSuccess; |
| int iterations = 100; |
| int numThreads = 1; |
| |
| const CK_C_INITIALIZE_ARGS pk11args = { |
| NULL, NULL, NULL, NULL, CKF_LIBRARY_CANT_CREATE_OS_THREADS, |
| (void *)"flags=readOnly,noCertDB,noModDB", NULL |
| }; |
| |
| /* read command-line arguments */ |
| for (i = 1; i < argv; i++) { |
| if (PL_strcasecmp(argc[i], "-i") == 0) { |
| i++; |
| iterations = atoi(argc[i]); |
| } else if (PL_strcasecmp(argc[i], "-t") == 0) { |
| i++; |
| numThreads = atoi(argc[i]); |
| } else if (PL_strcasecmp(argc[i], "-A") == 0) { |
| ansi = nist = secp = 1; |
| usepkcs11 = usefreebl = 1; |
| } else if (PL_strcasecmp(argc[i], "-a") == 0) { |
| ansi = 1; |
| } else if (PL_strcasecmp(argc[i], "-n") == 0) { |
| nist = 1; |
| } else if (PL_strcasecmp(argc[i], "-s") == 0) { |
| secp = 1; |
| } else if (PL_strcasecmp(argc[i], "-p") == 0) { |
| usepkcs11 = 1; |
| } else if (PL_strcasecmp(argc[i], "-f") == 0) { |
| usefreebl = 1; |
| } else { |
| printUsage(argc[0]); |
| return 0; |
| } |
| } |
| |
| if ((ansi | nist | secp) == 0) { |
| nist = 1; |
| } |
| if ((usepkcs11 | usefreebl) == 0) { |
| usefreebl = 1; |
| } |
| |
| rv = RNG_RNGInit(); |
| if (rv != SECSuccess) { |
| SECU_PrintError("Error:", "RNG_RNGInit"); |
| return -1; |
| } |
| RNG_SystemInfoForRNG(); |
| |
| rv = SECOID_Init(); |
| if (rv != SECSuccess) { |
| SECU_PrintError("Error:", "SECOID_Init"); |
| goto cleanup; |
| } |
| |
| if (usepkcs11) { |
| CK_RV crv = NSC_Initialize((CK_VOID_PTR)&pk11args); |
| if (crv != CKR_OK) { |
| fprintf(stderr, "NSC_Initialize failed crv=0x%x\n", (unsigned int)crv); |
| return SECFailure; |
| } |
| } |
| |
| /* specific arithmetic tests */ |
| if (nist) { |
| ECTEST_NAMED_GFP("NIST-P256", ECCurve_NIST_P256); |
| ECTEST_NAMED_GFP("NIST-P384", ECCurve_NIST_P384); |
| ECTEST_NAMED_GFP("NIST-P521", ECCurve_NIST_P521); |
| ECTEST_NAMED_CUSTOM("Curve25519", ECCurve25519); |
| } |
| |
| cleanup: |
| rv |= SECOID_Shutdown(); |
| RNG_RNGShutdown(); |
| |
| if (rv != SECSuccess) { |
| printf("Error: exiting with error value\n"); |
| } |
| return rv; |
| } |