tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }