/* 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;
void *tmp = NULL;
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);
tmp = threadData->p2;
threadData->p2 = (
void *)&sig;
}
while (iters--) {
threadData->status = (*op)(session, threadData->p1,
threadData->p2, threadData->p3);
if (threadData->status != SECSuccess) {
break;
}
threadData->count++;
}
if (threadData->isSign) {
threadData->p2 = tmp;
}
return;
}
void
genericThread(
void *data)
{
ThreadData *threadData = (ThreadData *)data;
int iters = threadData->iters;
unsigned char sigData[256];
SECItem sig;
void *tmp = NULL;
threadData->status = SECSuccess;
threadData->count = 0;
if (threadData->isSign) {
sig.data = sigData;
sig.len =
sizeof(sigData);
tmp = threadData->p2;
threadData->p2 = (
void *)&sig;
}
while (iters--) {
threadData->status = (*threadData->op)(threadData->p1,
threadData->p2, threadData->p3);
if (threadData->status != SECSuccess) {
break;
}
threadData->count++;
}
if (threadData->isSign) {
threadData->p2 = tmp;
}
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(name_c, name_v) \
if (usefreebl) { \
printf(
"Testing %s using freebl implementation...\n", name_c); \
rv = ectest_curve_freebl(name_v, iterations, numThreads); \
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)
{
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(
"NIST-P256", ECCurve_NIST_P256);
ECTEST_NAMED(
"NIST-P384", ECCurve_NIST_P384);
ECTEST_NAMED(
"NIST-P521", ECCurve_NIST_P521);
ECTEST_NAMED(
"Curve25519", ECCurve25519);
}
cleanup:
rv |= SECOID_Shutdown();
RNG_RNGShutdown();
if (rv != SECSuccess) {
printf(
"Error: exiting with error value\n");
}
return rv;
}