381 lines
11 KiB
C
381 lines
11 KiB
C
/* 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 "kem.h"
|
|
#include "pkcs11i.h"
|
|
#include "pkcs11n.h"
|
|
#include "secerr.h"
|
|
#include "secitem.h"
|
|
#include "secport.h"
|
|
#include "softoken.h"
|
|
|
|
KyberParams
|
|
sftk_kyber_PK11ParamToInternal(CK_NSS_KEM_PARAMETER_SET_TYPE pk11ParamSet)
|
|
{
|
|
switch (pk11ParamSet) {
|
|
case CKP_NSS_KYBER_768_ROUND3:
|
|
return params_kyber768_round3;
|
|
case CKP_NSS_ML_KEM_768:
|
|
return params_ml_kem768;
|
|
default:
|
|
return params_kyber_invalid;
|
|
}
|
|
}
|
|
|
|
SECItem *
|
|
sftk_kyber_AllocPubKeyItem(KyberParams params, SECItem *pubkey)
|
|
{
|
|
switch (params) {
|
|
case params_kyber768_round3:
|
|
case params_kyber768_round3_test_mode:
|
|
case params_ml_kem768:
|
|
case params_ml_kem768_test_mode:
|
|
return SECITEM_AllocItem(NULL, pubkey, KYBER768_PUBLIC_KEY_BYTES);
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
SECItem *
|
|
sftk_kyber_AllocPrivKeyItem(KyberParams params, SECItem *privkey)
|
|
{
|
|
switch (params) {
|
|
case params_kyber768_round3:
|
|
case params_kyber768_round3_test_mode:
|
|
case params_ml_kem768:
|
|
case params_ml_kem768_test_mode:
|
|
return SECITEM_AllocItem(NULL, privkey, KYBER768_PRIVATE_KEY_BYTES);
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
SECItem *
|
|
sftk_kyber_AllocCiphertextItem(KyberParams params, SECItem *ciphertext)
|
|
{
|
|
switch (params) {
|
|
case params_kyber768_round3:
|
|
case params_kyber768_round3_test_mode:
|
|
case params_ml_kem768:
|
|
case params_ml_kem768_test_mode:
|
|
return SECITEM_AllocItem(NULL, ciphertext, KYBER768_CIPHERTEXT_BYTES);
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static PRBool
|
|
sftk_kyber_ValidateParams(const CK_NSS_KEM_PARAMETER_SET_TYPE *params)
|
|
{
|
|
if (!params) {
|
|
return PR_FALSE;
|
|
}
|
|
switch (*params) {
|
|
case CKP_NSS_KYBER_768_ROUND3:
|
|
case CKP_NSS_ML_KEM_768:
|
|
return PR_TRUE;
|
|
default:
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
static PRBool
|
|
sftk_kem_ValidateMechanism(CK_MECHANISM_PTR pMechanism)
|
|
{
|
|
if (!pMechanism) {
|
|
return PR_FALSE;
|
|
}
|
|
switch (pMechanism->mechanism) {
|
|
case CKM_NSS_KYBER:
|
|
case CKM_NSS_ML_KEM:
|
|
return pMechanism->ulParameterLen == sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE) && sftk_kyber_ValidateParams(pMechanism->pParameter);
|
|
default:
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
static CK_ULONG
|
|
sftk_kem_CiphertextLen(CK_MECHANISM_PTR pMechanism)
|
|
{
|
|
#ifdef DEBUG
|
|
if (!sftk_kem_ValidateMechanism(pMechanism)) {
|
|
PORT_Assert(0);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* Assumes pMechanism has been validated with sftk_kem_ValidateMechanism */
|
|
CK_NSS_KEM_PARAMETER_SET_TYPE *pParameterSet = pMechanism->pParameter;
|
|
switch (*pParameterSet) {
|
|
case CKP_NSS_KYBER_768_ROUND3:
|
|
case CKP_NSS_ML_KEM_768:
|
|
return KYBER768_CIPHERTEXT_BYTES;
|
|
default:
|
|
/* unreachable if pMechanism has been validated */
|
|
PORT_Assert(0);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* C_Encapsulate takes a public encapsulation key hPublicKey, a secret
|
|
* phKey, and outputs a ciphertext (i.e. encapsulaton) of this secret in
|
|
* pCiphertext. */
|
|
CK_RV
|
|
NSC_Encapsulate(CK_SESSION_HANDLE hSession,
|
|
CK_MECHANISM_PTR pMechanism,
|
|
CK_OBJECT_HANDLE hPublicKey,
|
|
CK_ATTRIBUTE_PTR pTemplate,
|
|
CK_ULONG ulAttributeCount,
|
|
/* out */ CK_OBJECT_HANDLE_PTR phKey,
|
|
/* out */ CK_BYTE_PTR pCiphertext,
|
|
/* out */ CK_ULONG_PTR pulCiphertextLen)
|
|
{
|
|
SFTKSession *session = NULL;
|
|
SFTKSlot *slot = NULL;
|
|
|
|
SFTKObject *key = NULL;
|
|
|
|
SFTKObject *encapsulationKeyObject = NULL;
|
|
SFTKAttribute *encapsulationKey = NULL;
|
|
|
|
CK_RV crv;
|
|
SFTKFreeStatus status;
|
|
|
|
CHECK_FORK();
|
|
|
|
if (!pMechanism || !phKey || !pulCiphertextLen) {
|
|
return CKR_ARGUMENTS_BAD;
|
|
}
|
|
|
|
if (!sftk_kem_ValidateMechanism(pMechanism)) {
|
|
return CKR_MECHANISM_INVALID;
|
|
}
|
|
|
|
CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism);
|
|
if (!pCiphertext || *pulCiphertextLen < ciphertextLen) {
|
|
*pulCiphertextLen = ciphertextLen;
|
|
return CKR_KEY_SIZE_RANGE;
|
|
}
|
|
*phKey = CK_INVALID_HANDLE;
|
|
|
|
session = sftk_SessionFromHandle(hSession);
|
|
if (session == NULL) {
|
|
return CKR_SESSION_HANDLE_INVALID;
|
|
}
|
|
slot = sftk_SlotFromSessionHandle(hSession);
|
|
if (slot == NULL) {
|
|
crv = CKR_SESSION_HANDLE_INVALID;
|
|
goto cleanup;
|
|
}
|
|
|
|
key = sftk_NewObject(slot);
|
|
if (key == NULL) {
|
|
crv = CKR_HOST_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
for (unsigned long int i = 0; i < ulAttributeCount; i++) {
|
|
crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i]));
|
|
if (crv != CKR_OK) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
encapsulationKeyObject = sftk_ObjectFromHandle(hPublicKey, session);
|
|
if (encapsulationKeyObject == NULL) {
|
|
crv = CKR_KEY_HANDLE_INVALID;
|
|
goto cleanup;
|
|
}
|
|
encapsulationKey = sftk_FindAttribute(encapsulationKeyObject, CKA_VALUE);
|
|
if (encapsulationKey == NULL) {
|
|
crv = CKR_KEY_HANDLE_INVALID;
|
|
goto cleanup;
|
|
}
|
|
|
|
SECItem ciphertext = { siBuffer, pCiphertext, ciphertextLen };
|
|
SECItem pubKey = { siBuffer, encapsulationKey->attrib.pValue, encapsulationKey->attrib.ulValueLen };
|
|
|
|
/* The length of secretBuf can be increased if we ever support other KEMs */
|
|
uint8_t secretBuf[KYBER_SHARED_SECRET_BYTES] = { 0 };
|
|
SECItem secret = { siBuffer, secretBuf, sizeof secretBuf };
|
|
|
|
switch (pMechanism->mechanism) {
|
|
case CKM_NSS_KYBER:
|
|
case CKM_NSS_ML_KEM:
|
|
PORT_Assert(secret.len == KYBER_SHARED_SECRET_BYTES);
|
|
CK_NSS_KEM_PARAMETER_SET_TYPE *pParameter = pMechanism->pParameter;
|
|
KyberParams kyberParams = sftk_kyber_PK11ParamToInternal(*pParameter);
|
|
SECStatus rv = Kyber_Encapsulate(kyberParams, /* seed */ NULL, &pubKey, &ciphertext, &secret);
|
|
if (rv != SECSuccess) {
|
|
crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
crv = sftk_forceAttribute(key, CKA_VALUE, sftk_item_expand(&secret));
|
|
if (crv != CKR_OK) {
|
|
goto cleanup;
|
|
}
|
|
|
|
crv = sftk_handleObject(key, session);
|
|
if (crv != CKR_OK) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* We wrote the ciphertext out directly in Kyber_Encapsulate */
|
|
*phKey = key->handle;
|
|
*pulCiphertextLen = ciphertext.len;
|
|
break;
|
|
default:
|
|
crv = CKR_MECHANISM_INVALID;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
if (session) {
|
|
sftk_FreeSession(session);
|
|
}
|
|
if (key) {
|
|
status = sftk_FreeObject(key);
|
|
if (status == SFTK_DestroyFailure) {
|
|
return CKR_DEVICE_ERROR;
|
|
}
|
|
}
|
|
if (encapsulationKeyObject) {
|
|
status = sftk_FreeObject(encapsulationKeyObject);
|
|
if (status == SFTK_DestroyFailure) {
|
|
return CKR_DEVICE_ERROR;
|
|
}
|
|
}
|
|
if (encapsulationKey) {
|
|
sftk_FreeAttribute(encapsulationKey);
|
|
}
|
|
return crv;
|
|
}
|
|
|
|
CK_RV
|
|
NSC_Decapsulate(CK_SESSION_HANDLE hSession,
|
|
CK_MECHANISM_PTR pMechanism,
|
|
CK_OBJECT_HANDLE hPrivateKey,
|
|
CK_BYTE_PTR pCiphertext,
|
|
CK_ULONG ulCiphertextLen,
|
|
CK_ATTRIBUTE_PTR pTemplate,
|
|
CK_ULONG ulAttributeCount,
|
|
/* out */ CK_OBJECT_HANDLE_PTR phKey)
|
|
{
|
|
SFTKSession *session = NULL;
|
|
SFTKSlot *slot = NULL;
|
|
|
|
SFTKObject *key = NULL;
|
|
|
|
SFTKObject *decapsulationKeyObject = NULL;
|
|
SFTKAttribute *decapsulationKey = NULL;
|
|
|
|
CK_RV crv;
|
|
SFTKFreeStatus status;
|
|
|
|
CHECK_FORK();
|
|
|
|
if (!pMechanism || !pCiphertext || !pTemplate || !phKey) {
|
|
return CKR_ARGUMENTS_BAD;
|
|
}
|
|
|
|
if (!sftk_kem_ValidateMechanism(pMechanism)) {
|
|
return CKR_MECHANISM_INVALID;
|
|
}
|
|
|
|
CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism);
|
|
if (ulCiphertextLen < ciphertextLen) {
|
|
return CKR_ARGUMENTS_BAD;
|
|
}
|
|
*phKey = CK_INVALID_HANDLE;
|
|
|
|
session = sftk_SessionFromHandle(hSession);
|
|
if (session == NULL) {
|
|
return CKR_SESSION_HANDLE_INVALID;
|
|
}
|
|
slot = sftk_SlotFromSessionHandle(hSession);
|
|
if (slot == NULL) {
|
|
crv = CKR_SESSION_HANDLE_INVALID;
|
|
goto cleanup;
|
|
}
|
|
|
|
key = sftk_NewObject(slot);
|
|
if (key == NULL) {
|
|
crv = CKR_HOST_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
for (unsigned long int i = 0; i < ulAttributeCount; i++) {
|
|
crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i]));
|
|
if (crv != CKR_OK) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
decapsulationKeyObject = sftk_ObjectFromHandle(hPrivateKey, session);
|
|
if (decapsulationKeyObject == NULL) {
|
|
crv = CKR_KEY_HANDLE_INVALID;
|
|
goto cleanup;
|
|
}
|
|
decapsulationKey = sftk_FindAttribute(decapsulationKeyObject, CKA_VALUE);
|
|
if (decapsulationKey == NULL) {
|
|
crv = CKR_KEY_HANDLE_INVALID;
|
|
goto cleanup;
|
|
}
|
|
|
|
SECItem privKey = { siBuffer, decapsulationKey->attrib.pValue, decapsulationKey->attrib.ulValueLen };
|
|
SECItem ciphertext = { siBuffer, pCiphertext, ulCiphertextLen };
|
|
|
|
/* The length of secretBuf can be increased if we ever support other KEMs */
|
|
uint8_t secretBuf[KYBER_SHARED_SECRET_BYTES] = { 0 };
|
|
SECItem secret = { siBuffer, secretBuf, sizeof secretBuf };
|
|
|
|
switch (pMechanism->mechanism) {
|
|
case CKM_NSS_KYBER:
|
|
case CKM_NSS_ML_KEM:
|
|
PORT_Assert(secret.len == KYBER_SHARED_SECRET_BYTES);
|
|
CK_NSS_KEM_PARAMETER_SET_TYPE *pParameter = pMechanism->pParameter;
|
|
KyberParams kyberParams = sftk_kyber_PK11ParamToInternal(*pParameter);
|
|
SECStatus rv = Kyber_Decapsulate(kyberParams, &privKey, &ciphertext, &secret);
|
|
if (rv != SECSuccess) {
|
|
crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
crv = sftk_forceAttribute(key, CKA_VALUE, sftk_item_expand(&secret));
|
|
if (crv != CKR_OK) {
|
|
goto cleanup;
|
|
}
|
|
|
|
crv = sftk_handleObject(key, session);
|
|
if (crv != CKR_OK) {
|
|
goto cleanup;
|
|
}
|
|
*phKey = key->handle;
|
|
break;
|
|
default:
|
|
crv = CKR_MECHANISM_INVALID;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
if (session) {
|
|
sftk_FreeSession(session);
|
|
}
|
|
if (key) {
|
|
status = sftk_FreeObject(key);
|
|
if (status == SFTK_DestroyFailure) {
|
|
return CKR_DEVICE_ERROR;
|
|
}
|
|
}
|
|
if (decapsulationKeyObject) {
|
|
status = sftk_FreeObject(decapsulationKeyObject);
|
|
if (status == SFTK_DestroyFailure) {
|
|
return CKR_DEVICE_ERROR;
|
|
}
|
|
}
|
|
if (decapsulationKey) {
|
|
sftk_FreeAttribute(decapsulationKey);
|
|
}
|
|
return crv;
|
|
}
|