tor-browser

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

kem.c (16332B)


      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 "kem.h"
      7 #include "pkcs11i.h"
      8 #include "pkcs11n.h"
      9 #include "secerr.h"
     10 #include "secitem.h"
     11 #include "secport.h"
     12 #include "softoken.h"
     13 
     14 /* change to the largest KEM Secret Bytes value supported */
     15 #define MAX_SHARED_SECRET_BYTES KYBER_SHARED_SECRET_BYTES
     16 
     17 KyberParams
     18 sftk_kyber_PK11ParamToInternal(CK_ML_KEM_PARAMETER_SET_TYPE pk11ParamSet)
     19 {
     20    switch (pk11ParamSet) {
     21 #ifndef NSS_DISABLE_KYBER
     22        case CKP_NSS_KYBER_768_ROUND3:
     23            return params_kyber768_round3;
     24 #endif
     25        case CKP_NSS_ML_KEM_768:
     26        case CKP_ML_KEM_768:
     27            return params_ml_kem768;
     28        case CKP_ML_KEM_1024:
     29            return params_ml_kem1024;
     30        default:
     31            return params_kyber_invalid;
     32    }
     33 }
     34 
     35 SECItem *
     36 sftk_kyber_AllocPubKeyItem(KyberParams params, SECItem *pubkey)
     37 {
     38    switch (params) {
     39 #ifndef NSS_DISABLE_KYBER
     40        case params_kyber768_round3:
     41        case params_kyber768_round3_test_mode:
     42 #endif
     43        case params_ml_kem768:
     44        case params_ml_kem768_test_mode:
     45            return SECITEM_AllocItem(NULL, pubkey, KYBER768_PUBLIC_KEY_BYTES);
     46        case params_ml_kem1024:
     47        case params_ml_kem1024_test_mode:
     48            return SECITEM_AllocItem(NULL, pubkey, MLKEM1024_PUBLIC_KEY_BYTES);
     49        default:
     50            return NULL;
     51    }
     52 }
     53 
     54 SECItem *
     55 sftk_kyber_AllocPrivKeyItem(KyberParams params, SECItem *privkey)
     56 {
     57    switch (params) {
     58 #ifndef NSS_DISABLE_KYBER
     59        case params_kyber768_round3:
     60        case params_kyber768_round3_test_mode:
     61 #endif
     62        case params_ml_kem768:
     63        case params_ml_kem768_test_mode:
     64            return SECITEM_AllocItem(NULL, privkey, KYBER768_PRIVATE_KEY_BYTES);
     65        case params_ml_kem1024:
     66        case params_ml_kem1024_test_mode:
     67            return SECITEM_AllocItem(NULL, privkey, MLKEM1024_PRIVATE_KEY_BYTES);
     68        default:
     69            return NULL;
     70    }
     71 }
     72 
     73 SECItem *
     74 sftk_kyber_AllocCiphertextItem(KyberParams params, SECItem *ciphertext)
     75 {
     76    switch (params) {
     77 #ifndef NSS_DISABLE_KYBER
     78        case params_kyber768_round3:
     79        case params_kyber768_round3_test_mode:
     80 #endif
     81        case params_ml_kem768:
     82        case params_ml_kem768_test_mode:
     83            return SECITEM_AllocItem(NULL, ciphertext, KYBER768_CIPHERTEXT_BYTES);
     84        case params_ml_kem1024:
     85        case params_ml_kem1024_test_mode:
     86            return SECITEM_AllocItem(NULL, ciphertext, MLKEM1024_CIPHERTEXT_BYTES);
     87        default:
     88            return NULL;
     89    }
     90 }
     91 
     92 static PRBool
     93 sftk_kem_ValidateMechanism(CK_MECHANISM_PTR pMechanism)
     94 {
     95    if (!pMechanism) {
     96        return PR_FALSE;
     97    }
     98    switch (pMechanism->mechanism) {
     99 #ifndef NSS_DISABLE_KYBER
    100        case CKM_NSS_KYBER:
    101 #endif
    102        case CKM_NSS_ML_KEM:
    103        case CKM_ML_KEM:
    104            return PR_TRUE;
    105        default:
    106            return PR_FALSE;
    107    }
    108 }
    109 
    110 /* this is a generic call the returns the paramSet as a CKU_LONG. if the
    111 * param set is in a different attribute or mechanism structure, that would
    112 * be based on the mechanism. The meaning of the paramter set is alway
    113 * mechanism specific */
    114 static CK_RV
    115 sftk_kem_getParamSet(CK_MECHANISM_PTR pMechanism, SFTKObject *key,
    116                     CK_ULONG *paramSet)
    117 {
    118    CK_RV crv = CKR_MECHANISM_INVALID;
    119 
    120    switch (pMechanism->mechanism) {
    121 #ifndef NSS_DISABLE_KYBER
    122        case CKM_NSS_KYBER:
    123 #endif
    124        case CKM_NSS_ML_KEM:
    125            /* CKM_NSS_KYBER and CKM_NSS_ML_KEM has cases were we were
    126             * given parameters form the application. Retain that symantic for
    127             * compatibility */
    128            if ((pMechanism->pParameter) &&
    129                pMechanism->ulParameterLen == sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)) {
    130                PR_STATIC_ASSERT(sizeof(CK_ML_KEM_PARAMETER_SET_TYPE) == sizeof(CK_LONG));
    131                *paramSet = *(CK_ULONG *)pMechanism->pParameter;
    132                crv = CKR_OK;
    133                break;
    134            }
    135            /* fall through to the official PKCS #11 way of getting the
    136             * parameter set */
    137        case CKM_ML_KEM:
    138            crv = sftk_GetULongAttribute(key, CKA_PARAMETER_SET, paramSet);
    139            if (crv == CKR_OK) {
    140                break;
    141            }
    142            /* we accept all key types a equivalent here, so the parameter
    143             * set could be attached to the old vendor specific attribute.
    144             * We might use the vendor specific key when bypassing pairwise
    145             * consistency checks */
    146            crv = sftk_GetULongAttribute(key, CKA_NSS_PARAMETER_SET, paramSet);
    147            break;
    148        default:
    149            break;
    150    }
    151    return crv;
    152 }
    153 
    154 static CK_ULONG
    155 sftk_kem_CiphertextLen(CK_MECHANISM_PTR pMechanism, CK_ULONG paramSet)
    156 {
    157    /* the switch here is redundant now, but we will eventually have
    158     * other unrelated KEM operations and the meaning of paramSet
    159     * is dependent of the mechanism type (in general). Since there
    160     * is not overlap between the Vendor specific mechanisms here
    161     * and the stand ones, we'll just accept them all here. */
    162    switch (pMechanism->mechanism) {
    163 #ifndef NSS_DISABLE_KYBER
    164        case CKM_NSS_KYBER:
    165 #endif
    166        case CKM_NSS_ML_KEM:
    167        case CKM_ML_KEM:
    168            switch (paramSet) {
    169 #ifndef NSS_DISABLE_KYBER
    170                case CKP_NSS_KYBER_768_ROUND3:
    171 #endif
    172                case CKP_NSS_ML_KEM_768:
    173                case CKP_ML_KEM_768:
    174                    return KYBER768_CIPHERTEXT_BYTES;
    175                case CKP_ML_KEM_1024:
    176                    return MLKEM1024_CIPHERTEXT_BYTES;
    177                default:
    178                    break;
    179            }
    180        default:
    181            break;
    182    }
    183    return 0;
    184 }
    185 
    186 /* C_Encapsulate takes a public encapsulation key hPublicKey, a secret
    187 * phKey, and outputs a ciphertext (i.e. encapsulaton) of this secret in
    188 * pCiphertext. */
    189 CK_RV
    190 NSC_EncapsulateKey(CK_SESSION_HANDLE hSession,
    191                   CK_MECHANISM_PTR pMechanism,
    192                   CK_OBJECT_HANDLE hPublicKey,
    193                   CK_ATTRIBUTE_PTR pTemplate,
    194                   CK_ULONG ulAttributeCount,
    195                   /* out */ CK_BYTE_PTR pCiphertext,
    196                   /* out */ CK_ULONG_PTR pulCiphertextLen,
    197                   /* out */ CK_OBJECT_HANDLE_PTR phKey)
    198 {
    199    SFTKSession *session = NULL;
    200    SFTKSlot *slot = NULL;
    201 
    202    SFTKObject *key = NULL;
    203 
    204    SFTKObject *encapsulationKeyObject = NULL;
    205    SFTKAttribute *encapsulationKey = NULL;
    206 
    207    CK_RV crv;
    208    SFTKFreeStatus status;
    209    CK_ULONG paramSet = 0; /* use a generic U_LONG so we can handle
    210                            * different param set types based on the
    211                            * Mechanism value */
    212    KyberParams kyberParams;
    213 
    214    CHECK_FORK();
    215 
    216    if (!pMechanism || !phKey || !pulCiphertextLen) {
    217        return CKR_ARGUMENTS_BAD;
    218    }
    219 
    220    if (!sftk_kem_ValidateMechanism(pMechanism)) {
    221        return CKR_MECHANISM_INVALID;
    222    }
    223    *phKey = CK_INVALID_HANDLE;
    224 
    225    session = sftk_SessionFromHandle(hSession);
    226    if (session == NULL) {
    227        return CKR_SESSION_HANDLE_INVALID;
    228    }
    229    slot = sftk_SlotFromSessionHandle(hSession);
    230    if (slot == NULL) {
    231        crv = CKR_SESSION_HANDLE_INVALID;
    232        goto cleanup;
    233    }
    234 
    235    key = sftk_NewObject(slot);
    236    if (key == NULL) {
    237        crv = CKR_HOST_MEMORY;
    238        goto cleanup;
    239    }
    240    for (unsigned long int i = 0; i < ulAttributeCount; i++) {
    241        crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i]));
    242        if (crv != CKR_OK) {
    243            goto cleanup;
    244        }
    245    }
    246 
    247    encapsulationKeyObject = sftk_ObjectFromHandle(hPublicKey, session);
    248    if (encapsulationKeyObject == NULL) {
    249        crv = CKR_KEY_HANDLE_INVALID;
    250        goto cleanup;
    251    }
    252    encapsulationKey = sftk_FindAttribute(encapsulationKeyObject, CKA_VALUE);
    253    if (encapsulationKey == NULL) {
    254        crv = CKR_KEY_HANDLE_INVALID;
    255        goto cleanup;
    256    }
    257 
    258    crv = sftk_kem_getParamSet(pMechanism, encapsulationKeyObject, &paramSet);
    259    if (crv != CKR_OK) {
    260        goto cleanup;
    261    }
    262 
    263    CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism, paramSet);
    264    if (!pCiphertext || *pulCiphertextLen < ciphertextLen || ciphertextLen == 0) {
    265        *pulCiphertextLen = ciphertextLen;
    266        crv = CKR_BUFFER_TOO_SMALL;
    267        goto cleanup;
    268    }
    269 
    270    SECItem ciphertext = { siBuffer, pCiphertext, ciphertextLen };
    271    SECItem pubKey = { siBuffer, encapsulationKey->attrib.pValue, encapsulationKey->attrib.ulValueLen };
    272 
    273    /* The length of secretBuf can be increased if we ever support other KEMs
    274     * by changing the define at the top of this file */
    275    uint8_t secretBuf[MAX_SHARED_SECRET_BYTES] = { 0 };
    276    SECItem secret = { siBuffer, secretBuf, sizeof secretBuf };
    277 
    278    switch (pMechanism->mechanism) {
    279 #ifndef NSS_DISABLE_KYBER
    280        case CKM_NSS_KYBER:
    281 #endif
    282        case CKM_NSS_ML_KEM:
    283        case CKM_ML_KEM:
    284            PORT_Assert(secret.len >= KYBER_SHARED_SECRET_BYTES);
    285            kyberParams = sftk_kyber_PK11ParamToInternal(paramSet);
    286            SECStatus rv = Kyber_Encapsulate(kyberParams, /* seed */ NULL,
    287                                             &pubKey, &ciphertext, &secret);
    288            if (rv != SECSuccess) {
    289                crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED;
    290                goto cleanup;
    291            }
    292 
    293            crv = sftk_forceAttribute(key, CKA_VALUE, sftk_item_expand(&secret));
    294            if (crv != CKR_OK) {
    295                goto cleanup;
    296            }
    297 
    298            crv = sftk_handleObject(key, session);
    299            if (crv != CKR_OK) {
    300                goto cleanup;
    301            }
    302 
    303            /* We wrote the ciphertext out directly in Kyber_Encapsulate */
    304            *phKey = key->handle;
    305            *pulCiphertextLen = ciphertext.len;
    306            break;
    307        default:
    308            crv = CKR_MECHANISM_INVALID;
    309            goto cleanup;
    310    }
    311 
    312 cleanup:
    313    if (session) {
    314        sftk_FreeSession(session);
    315    }
    316    if (key) {
    317        status = sftk_FreeObject(key);
    318        if (status == SFTK_DestroyFailure) {
    319            return CKR_DEVICE_ERROR;
    320        }
    321    }
    322    if (encapsulationKeyObject) {
    323        status = sftk_FreeObject(encapsulationKeyObject);
    324        if (status == SFTK_DestroyFailure) {
    325            return CKR_DEVICE_ERROR;
    326        }
    327    }
    328    if (encapsulationKey) {
    329        sftk_FreeAttribute(encapsulationKey);
    330    }
    331    return crv;
    332 }
    333 
    334 CK_RV
    335 NSC_DecapsulateKey(CK_SESSION_HANDLE hSession,
    336                   CK_MECHANISM_PTR pMechanism,
    337                   CK_OBJECT_HANDLE hPrivateKey,
    338                   CK_ATTRIBUTE_PTR pTemplate,
    339                   CK_ULONG ulAttributeCount,
    340                   CK_BYTE_PTR pCiphertext,
    341                   CK_ULONG ulCiphertextLen,
    342                   /* out */ CK_OBJECT_HANDLE_PTR phKey)
    343 {
    344    SFTKSession *session = NULL;
    345    SFTKSlot *slot = NULL;
    346 
    347    SFTKObject *key = NULL;
    348 
    349    SFTKObject *decapsulationKeyObject = NULL;
    350    SFTKAttribute *decapsulationKey = NULL;
    351    CK_ULONG paramSet = 0; /* use a generic U_LONG so we can handle
    352                            * different param set types based on the
    353                            * Mechanism value */
    354 
    355    CK_RV crv;
    356    SFTKFreeStatus status;
    357    KyberParams kyberParams;
    358 
    359    CHECK_FORK();
    360 
    361    if (!pMechanism || !pCiphertext || !pTemplate || !phKey) {
    362        return CKR_ARGUMENTS_BAD;
    363    }
    364 
    365    if (!sftk_kem_ValidateMechanism(pMechanism)) {
    366        return CKR_MECHANISM_INVALID;
    367    }
    368    *phKey = CK_INVALID_HANDLE;
    369 
    370    session = sftk_SessionFromHandle(hSession);
    371    if (session == NULL) {
    372        return CKR_SESSION_HANDLE_INVALID;
    373    }
    374    slot = sftk_SlotFromSessionHandle(hSession);
    375    if (slot == NULL) {
    376        crv = CKR_SESSION_HANDLE_INVALID;
    377        goto cleanup;
    378    }
    379 
    380    key = sftk_NewObject(slot);
    381    if (key == NULL) {
    382        crv = CKR_HOST_MEMORY;
    383        goto cleanup;
    384    }
    385    for (unsigned long int i = 0; i < ulAttributeCount; i++) {
    386        crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i]));
    387        if (crv != CKR_OK) {
    388            goto cleanup;
    389        }
    390    }
    391 
    392    decapsulationKeyObject = sftk_ObjectFromHandle(hPrivateKey, session);
    393    if (decapsulationKeyObject == NULL) {
    394        crv = CKR_KEY_HANDLE_INVALID;
    395        goto cleanup;
    396    }
    397    decapsulationKey = sftk_FindAttribute(decapsulationKeyObject, CKA_VALUE);
    398    if (decapsulationKey == NULL) {
    399        crv = CKR_KEY_HANDLE_INVALID;
    400        goto cleanup;
    401    }
    402 
    403    crv = sftk_kem_getParamSet(pMechanism, decapsulationKeyObject, &paramSet);
    404    if (crv != CKR_OK) {
    405        goto cleanup;
    406    }
    407 
    408    CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism, paramSet);
    409    if (!pCiphertext || ulCiphertextLen != ciphertextLen || ciphertextLen == 0) {
    410        crv = CKR_ARGUMENTS_BAD;
    411        goto cleanup;
    412    }
    413 
    414    SECItem privKey = { siBuffer, decapsulationKey->attrib.pValue,
    415                        decapsulationKey->attrib.ulValueLen };
    416    SECItem ciphertext = { siBuffer, pCiphertext, ulCiphertextLen };
    417 
    418    /* The length of secretBuf can be increased if we ever support other KEMs
    419     * by changing the define at the top of this file */
    420    uint8_t secretBuf[MAX_SHARED_SECRET_BYTES] = { 0 };
    421    SECItem secret = { siBuffer, secretBuf, sizeof secretBuf };
    422 
    423    switch (pMechanism->mechanism) {
    424 #ifndef NSS_DISABLE_KYBER
    425        case CKM_NSS_KYBER:
    426 #endif
    427        case CKM_NSS_ML_KEM:
    428        case CKM_ML_KEM:
    429            kyberParams = sftk_kyber_PK11ParamToInternal(paramSet);
    430            PORT_Assert(secret.len >= KYBER_SHARED_SECRET_BYTES);
    431            SECStatus rv = Kyber_Decapsulate(kyberParams, &privKey,
    432                                             &ciphertext, &secret);
    433            if (rv != SECSuccess) {
    434                crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED;
    435                goto cleanup;
    436            }
    437 
    438            crv = sftk_forceAttribute(key, CKA_VALUE, sftk_item_expand(&secret));
    439            if (crv != CKR_OK) {
    440                goto cleanup;
    441            }
    442 
    443            crv = sftk_handleObject(key, session);
    444            if (crv != CKR_OK) {
    445                goto cleanup;
    446            }
    447            *phKey = key->handle;
    448            break;
    449        default:
    450            crv = CKR_MECHANISM_INVALID;
    451            goto cleanup;
    452    }
    453 
    454 cleanup:
    455    if (session) {
    456        sftk_FreeSession(session);
    457    }
    458    if (key) {
    459        status = sftk_FreeObject(key);
    460        if (status == SFTK_DestroyFailure) {
    461            return CKR_DEVICE_ERROR;
    462        }
    463    }
    464    if (decapsulationKeyObject) {
    465        status = sftk_FreeObject(decapsulationKeyObject);
    466        if (status == SFTK_DestroyFailure) {
    467            return CKR_DEVICE_ERROR;
    468        }
    469    }
    470    if (decapsulationKey) {
    471        sftk_FreeAttribute(decapsulationKey);
    472    }
    473    return crv;
    474 }
    475 
    476 /* PKCS #11 final spec moved som the the arguments around (to make
    477 * NSC_EncapsulateKey and NSC_DecapsulateKey match, keep the old version for
    478 * the vendor defined for backward compatibility */
    479 CK_RV
    480 NSC_Encapsulate(CK_SESSION_HANDLE hSession,
    481                CK_MECHANISM_PTR pMechanism,
    482                CK_OBJECT_HANDLE hPublicKey,
    483                CK_ATTRIBUTE_PTR pTemplate,
    484                CK_ULONG ulAttributeCount,
    485                /* out */ CK_OBJECT_HANDLE_PTR phKey,
    486                /* out */ CK_BYTE_PTR pCiphertext,
    487                /* out */ CK_ULONG_PTR pulCiphertextLen)
    488 {
    489    return NSC_EncapsulateKey(hSession, pMechanism, hPublicKey,
    490                              pTemplate, ulAttributeCount,
    491                              pCiphertext, pulCiphertextLen, phKey);
    492 }
    493 
    494 CK_RV
    495 NSC_Decapsulate(CK_SESSION_HANDLE hSession,
    496                CK_MECHANISM_PTR pMechanism,
    497                CK_OBJECT_HANDLE hPrivateKey,
    498                CK_BYTE_PTR pCiphertext,
    499                CK_ULONG ulCiphertextLen,
    500                CK_ATTRIBUTE_PTR pTemplate,
    501                CK_ULONG ulAttributeCount,
    502                /* out */ CK_OBJECT_HANDLE_PTR phKey)
    503 {
    504    return NSC_DecapsulateKey(hSession, pMechanism, hPrivateKey, pTemplate,
    505                              ulAttributeCount, pCiphertext, ulCiphertextLen,
    506                              phKey);
    507 }