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, ¶mSet); 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, ¶mSet); 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 }