From 2a30f22670855f8a00d5288df63130aa58ee81f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Thu, 22 Oct 2026 13:09:34 +0200 Subject: [PATCH 1/4] Add wc_Pkcs11StoreKey_ex to import keys as token objects wc_Pkcs11StoreKey creates session objects only, so an imported key is gone once the session closes. wc_Pkcs11StoreKey_ex takes a persistent flag that creates the objects with CKA_TOKEN set instead, so they stay on the token and can be used by id or label later. Persistent objects need a read/write session and a logged in user. A persistent private or secret key also gets CKA_PRIVATE and CKA_SENSITIVE set and CKA_EXTRACTABLE cleared, so it can be neither read without a login nor exported while it stays on the token. wolfSSL never reads the value of such a key back, only public attributes such as the RSA modulus. Public key objects get CKA_TOKEN set and CKA_PRIVATE cleared, as the default for CKA_PRIVATE is up to the token and a private public key would be hidden from sessions that are not logged in. A persistent RSA key gets a public key object on the token too, which public key operations on a key loaded by name look up. A persistent key needs an id or label, or it could not be found again, and neither may be in use by a token key of the same type: wolfSSL finds keys by either one, and two keys with one name make lookups ambiguous. The call fails with BAD_FUNC_ARG or BAD_STATE_E otherwise. An HMAC key is checked under its HMAC key type and as the generic secret that wc_Pkcs11StoreKey falls back to. Objects of other types, such as the certificate that usually shares the key's id, do not clash, since lookups check the key type. The create helpers take the setting from a new tokenObjects bit in Pkcs11Session, which Pkcs11OpenSession clears, so sessions used for crypto operations keep creating their temporary keys as session objects. wc_Pkcs11StoreKey calls the new function with persistent set to 0, and a NULL token or key now fails with BAD_FUNC_ARG. The key lookup helpers are built for ML-DSA and ML-KEM only builds too. A persistent store does not need a session from wc_Pkcs11Token_Open, as its objects outlive the session the call opens and closes. wc_Pkcs11StoreKey still returns BAD_STATE_E without one. --- doc/dox_comments/header_files/pkcs11.h | 34 +- tests/api/test_pkcs11.c | 155 ++++++++- tests/api/test_pkcs11.h | 2 + wolfcrypt/src/wc_pkcs11.c | 427 +++++++++++++++++++++++-- wolfssl/wolfcrypt/wc_pkcs11.h | 3 + 5 files changed, 586 insertions(+), 35 deletions(-) diff --git a/doc/dox_comments/header_files/pkcs11.h b/doc/dox_comments/header_files/pkcs11.h index 7871ab43317..bd4757cdfab 100644 --- a/doc/dox_comments/header_files/pkcs11.h +++ b/doc/dox_comments/header_files/pkcs11.h @@ -59,7 +59,7 @@ void wc_Pkcs11Token_Close(Pkcs11Token* token); stored too. \return 0 on success. - \return BAD_FUNC_ARG when token is NULL. + \return BAD_FUNC_ARG when token or key is NULL. \return BAD_STATE_E when no session was opened with wc_Pkcs11Token_Open. \return MEMORY_E when memory allocation fails. \return NOT_COMPILED_IN when the key type is not supported, or the token @@ -77,6 +77,38 @@ void wc_Pkcs11Token_Close(Pkcs11Token* token); int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key); +/*! + \ingroup PKCS11 + + \brief Stores a private key on the token like wc_Pkcs11StoreKey. With + persistent set, the objects are created with CKA_TOKEN set and stay on + the token after the session closes; this needs a read/write session and + a logged in token, but not wc_Pkcs11Token_Open. A persistent private or + secret key also gets CKA_PRIVATE and CKA_SENSITIVE set and + CKA_EXTRACTABLE cleared, and needs an id or label that no token key of + the type has, or the call fails with BAD_FUNC_ARG or BAD_STATE_E. + + \return 0 on success. + \return BAD_FUNC_ARG when token or key is NULL, or a persistent key has + neither id nor label. + \return BAD_STATE_E when persistent is 0 and no session was opened with + wc_Pkcs11Token_Open, or a token key of the type already has the + persistent key's id or label. + \return MEMORY_E when a memory allocation fails. + \return NOT_COMPILED_IN when the key type or mechanism is not available. + \return WC_HW_E when a PKCS#11 call fails. + + \param token Token to store the key on. + \param type One of the PKCS11_KEY_TYPE_* values. + \param clear Clear the private key data from the software key. + \param key Key object of the type. + \param persistent Store the key as token objects when non-zero. + + \sa wc_Pkcs11StoreKey +*/ +int wc_Pkcs11StoreKey_ex(Pkcs11Token* token, int type, int clear, + void* key, int persistent); + /*! \ingroup PKCS11 */ diff --git a/tests/api/test_pkcs11.c b/tests/api/test_pkcs11.c index 09e54e79385..c065aa7422d 100644 --- a/tests/api/test_pkcs11.c +++ b/tests/api/test_pkcs11.c @@ -296,6 +296,9 @@ typedef struct TestPkcs11Obj { CK_BBOOL sign; CK_BBOOL derive; CK_BBOOL decrypt; + CK_BBOOL isPrivate; + CK_BBOOL sensitive; + CK_BBOOL extractable; } TestPkcs11Obj; static const byte test_pkcs11_modulus[256] = { 0xc0, 0x01 }; @@ -314,8 +317,16 @@ static int test_pkcs11_fail_pub_create = 0; static int test_pkcs11_no_ecdh = 0; static int test_pkcs11_no_ecdsa = 0; static int test_pkcs11_ecdsa_verify_only = 0; +static int test_pkcs11_no_hmac_type = 0; +static CK_OBJECT_HANDLE test_pkcs11_sign_key = 0; +static int test_pkcs11_fail_copy = 0; +static int test_pkcs11_fail_destroy = 0; +static byte test_pkcs11_pq_pub[2600]; +static CK_ULONG test_pkcs11_pq_pub_len = 0; +static CK_ULONG test_pkcs11_pq_param = 0; static int test_pkcs11_fail_modulus = 0; static int test_pkcs11_fail_keygen = 0; +static int test_pkcs11_session_no_point = 0; static byte test_pkcs11_ec_point[2 + 65]; static TestPkcs11Obj test_pkcs11_last_priv; static const byte test_pkcs11_secret[32] = { 0x5a, 0x5a }; @@ -333,6 +344,7 @@ static void test_pkcs11_reset(void) test_pkcs11_fail_pub_create = 0; test_pkcs11_fail_modulus = 0; test_pkcs11_fail_keygen = 0; + test_pkcs11_session_no_point = 0; } static TestPkcs11Obj* test_pkcs11_obj(CK_OBJECT_HANDLE h) @@ -356,6 +368,26 @@ static int test_pkcs11_count(CK_BBOOL onToken) } #endif +/* 1 when every token private key carries the protecting attributes and no + * public key does. */ +static int test_pkcs11_token_keys_protected(void) +{ + int i; + const TestPkcs11Obj* o; + + for (i = 0; i < TEST_PKCS11_MAX_OBJS; i++) { + o = &test_pkcs11_objs[i]; + if (!o->used || !o->onToken) + continue; + if ((o->cls == CKO_PRIVATE_KEY || o->cls == CKO_SECRET_KEY) && + (!o->isPrivate || !o->sensitive || o->extractable)) + return 0; + if (o->cls == CKO_PUBLIC_KEY && o->isPrivate) + return 0; + } + return 1; +} + /* Applies the template to obj and returns a TEST_PKCS11_FIND_* mask. */ static CK_ULONG test_pkcs11_apply(TestPkcs11Obj* obj, CK_ATTRIBUTE_PTR tmpl, CK_ULONG cnt) @@ -391,6 +423,15 @@ static CK_ULONG test_pkcs11_apply(TestPkcs11Obj* obj, CK_ATTRIBUTE_PTR tmpl, else if (tmpl[i].type == CKA_DECRYPT) { obj->decrypt = *(CK_BBOOL*)tmpl[i].pValue; } + else if (tmpl[i].type == CKA_PRIVATE) { + obj->isPrivate = *(CK_BBOOL*)tmpl[i].pValue; + } + else if (tmpl[i].type == CKA_SENSITIVE) { + obj->sensitive = *(CK_BBOOL*)tmpl[i].pValue; + } + else if (tmpl[i].type == CKA_EXTRACTABLE) { + obj->extractable = *(CK_BBOOL*)tmpl[i].pValue; + } else if (tmpl[i].type == CKA_LABEL && tmpl[i].ulValueLen <= TEST_PKCS11_MAX_ID_LEN) { XMEMCPY(obj->label, tmpl[i].pValue, tmpl[i].ulValueLen); @@ -416,10 +457,16 @@ static CK_RV test_pkcs11_store_create(CK_SESSION_HANDLE session, return TEST_CKR_GENERAL_ERROR; XMEMSET(&test_pkcs11_objs[i], 0, sizeof(test_pkcs11_objs[i])); + /* Defaults as in SoftHSM2. */ + test_pkcs11_objs[i].extractable = CK_TRUE; + test_pkcs11_objs[i].isPrivate = CK_TRUE; (void)test_pkcs11_apply(&test_pkcs11_objs[i], tmpl, cnt); if (test_pkcs11_fail_pub_create && test_pkcs11_objs[i].cls == CKO_PUBLIC_KEY) return TEST_CKR_GENERAL_ERROR; + if (test_pkcs11_no_hmac_type && + test_pkcs11_objs[i].keyType == CKK_SHA256_HMAC) + return TEST_CKR_GENERAL_ERROR; test_pkcs11_objs[i].used = 1; if (test_pkcs11_objs[i].cls == CKO_PRIVATE_KEY) test_pkcs11_last_priv = test_pkcs11_objs[i]; @@ -435,7 +482,7 @@ static CK_RV test_pkcs11_store_destroy(CK_SESSION_HANDLE session, (void)session; test_pkcs11_destroy_calls++; - if (obj == NULL) + if (obj == NULL || test_pkcs11_fail_destroy) return TEST_CKR_GENERAL_ERROR; obj->used = 0; return CKR_OK; @@ -482,7 +529,16 @@ static CK_RV test_pkcs11_store_get_attr(CK_SESSION_HANDLE session, } else if (tmpl[i].type == CKA_EC_POINT) { val = test_pkcs11_ec_point; - len = sizeof(test_pkcs11_ec_point); + if (!test_pkcs11_session_no_point || obj->onToken) + len = sizeof(test_pkcs11_ec_point); + } + else if (tmpl[i].type == CKA_VALUE && obj->cls == CKO_PUBLIC_KEY) { + val = test_pkcs11_pq_pub; + len = test_pkcs11_pq_pub_len; + } + else if (tmpl[i].type == CKA_PARAMETER_SET) { + val = (const byte*)&test_pkcs11_pq_param; + len = sizeof(test_pkcs11_pq_param); } else if (tmpl[i].type == CKA_VALUE) { val = test_pkcs11_secret; @@ -549,6 +605,23 @@ static CK_RV test_pkcs11_store_find_final(CK_SESSION_HANDLE session) return CKR_OK; } +static CK_RV test_pkcs11_store_copy(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR tmpl, CK_ULONG cnt, + CK_OBJECT_HANDLE_PTR newObject) +{ + TestPkcs11Obj* obj = test_pkcs11_obj(object); + CK_RV rv; + + if (obj == NULL || test_pkcs11_fail_copy) + return TEST_CKR_GENERAL_ERROR; + rv = test_pkcs11_store_create(session, NULL, 0, newObject); + if (rv == CKR_OK) { + test_pkcs11_objs[*newObject - 1] = *obj; + (void)test_pkcs11_apply(&test_pkcs11_objs[*newObject - 1], tmpl, cnt); + } + return rv; +} + static CK_RV test_pkcs11_store_mech_info(CK_SLOT_ID slotId, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR info) { @@ -556,13 +629,18 @@ static CK_RV test_pkcs11_store_mech_info(CK_SLOT_ID slotId, if (info == NULL || (type != CKM_ECDSA && type != CKM_RSA_X_509 && type != CKM_EC_KEY_PAIR_GEN && type != CKM_ECDH1_DERIVE && - type != CKM_RSA_PKCS_KEY_PAIR_GEN) || + type != CKM_RSA_PKCS_KEY_PAIR_GEN && type != CKM_AES_GCM && + type != CKM_AES_CBC && type != CKM_SHA256_HMAC && + type != CKM_ML_DSA && type != CKM_ML_KEM && + type != CKM_ML_DSA_KEY_PAIR_GEN && + type != CKM_ML_KEM_KEY_PAIR_GEN) || (type == CKM_ECDH1_DERIVE && test_pkcs11_no_ecdh) || (type == CKM_ECDSA && test_pkcs11_no_ecdsa)) return CKR_MECHANISM_INVALID; XMEMSET(info, 0, sizeof(*info)); info->flags = CKF_SIGN | CKF_VERIFY | CKF_DECRYPT; + info->ulMaxKeySize = 0x10000; /* Some modules leave CKF_DERIVE out for CKM_ECDH1_DERIVE. */ if (type == CKM_ECDH1_DERIVE) info->flags = 0; @@ -571,6 +649,16 @@ static CK_RV test_pkcs11_store_mech_info(CK_SLOT_ID slotId, return CKR_OK; } +static CK_RV test_pkcs11_store_sign_init(CK_SESSION_HANDLE session, + CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) +{ + (void)session; + (void)mechanism; + + test_pkcs11_sign_key = key; + return test_pkcs11_obj(key) != NULL ? CKR_OK : TEST_CKR_GENERAL_ERROR; +} + static CK_RV test_pkcs11_store_decrypt_init(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) { @@ -631,6 +719,10 @@ static CK_RV test_pkcs11_store_keygen(CK_SESSION_HANDLE session, return TEST_CKR_GENERAL_ERROR; if (mechanism->mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN) keyType = CKK_RSA; + else if (mechanism->mechanism == CKM_ML_DSA_KEY_PAIR_GEN) + keyType = CKK_ML_DSA; + else if (mechanism->mechanism == CKM_ML_KEM_KEY_PAIR_GEN) + keyType = CKK_ML_KEM; for (i = 0; i < pubCnt; i++) { if (pubTmpl[i].type == CKA_PUBLIC_EXPONENT && pubTmpl[i].ulValueLen <= sizeof(test_pkcs11_keygen_exp)) { @@ -658,6 +750,7 @@ static void test_pkcs11_store_setup(Pkcs11Token* token, CK_FUNCTION_LIST* func) XMEMSET(func, 0, sizeof(*func)); func->C_GetMechanismInfo = test_pkcs11_store_mech_info; func->C_CreateObject = test_pkcs11_store_create; + func->C_CopyObject = test_pkcs11_store_copy; func->C_DestroyObject = test_pkcs11_store_destroy; func->C_GetAttributeValue = test_pkcs11_store_get_attr; func->C_FindObjectsInit = test_pkcs11_store_find_init; @@ -666,7 +759,7 @@ static void test_pkcs11_store_setup(Pkcs11Token* token, CK_FUNCTION_LIST* func) func->C_GenerateKeyPair = test_pkcs11_store_keygen; func->C_DecryptInit = test_pkcs11_store_decrypt_init; func->C_Decrypt = test_pkcs11_store_decrypt; - func->C_SignInit = test_pkcs11_store_decrypt_init; + func->C_SignInit = test_pkcs11_store_sign_init; func->C_Sign = test_pkcs11_store_sign; func->C_DeriveKey = test_pkcs11_store_derive; token->func = func; @@ -676,6 +769,9 @@ static void test_pkcs11_store_setup(Pkcs11Token* token, CK_FUNCTION_LIST* func) test_pkcs11_no_ecdh = 0; test_pkcs11_no_ecdsa = 0; test_pkcs11_ecdsa_verify_only = 0; + test_pkcs11_no_hmac_type = 0; + test_pkcs11_fail_copy = 0; + test_pkcs11_fail_destroy = 0; test_pkcs11_reset(); } @@ -733,7 +829,6 @@ int test_wc_Pkcs11_FreeKeepsTokenKey(void) return EXPECT_RESULT(); } -#ifdef TEST_PKCS11_EC_KEYGEN static int test_pkcs11_make_ecc(ecc_key* key, WC_RNG* rng, const byte* id, int idLen) { @@ -747,7 +842,49 @@ static int test_pkcs11_make_ecc(ecc_key* key, WC_RNG* rng, const byte* id, #endif return ret; } -#endif + +int test_wc_Pkcs11_StoreKeyPersistent(void) +{ + static const byte id[] = { 0x01, 0x02, 0x03, 0x04 }; + Pkcs11Token token; + CK_FUNCTION_LIST func; + ecc_key key; + WC_RNG rng; + int i; + EXPECT_DECLS; + + test_pkcs11_store_setup(&token, &func); + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(test_pkcs11_make_ecc(&key, &rng, id, (int)sizeof(id)), 0); + + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 0), + 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 2); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + + test_pkcs11_reset(); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 1), + 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + for (i = 0; i < TEST_PKCS11_MAX_OBJS; i++) { + if (test_pkcs11_objs[i].used) { + ExpectIntEQ(test_pkcs11_objs[i].idLen, sizeof(id)); + ExpectBufEQ(test_pkcs11_objs[i].id, id, sizeof(id)); + } + } + + test_pkcs11_reset(); + ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_EC, 0, &key), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + + wc_ecc_free(&key); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + + return EXPECT_RESULT(); +} #if defined(TEST_PKCS11_EC_KEYGEN) && defined(HAVE_ECC_KEY_IMPORT) static CK_OBJECT_HANDLE test_pkcs11_find_obj(CK_OBJECT_CLASS cls, @@ -1255,6 +1392,12 @@ int test_wc_Pkcs11_KeyGenSessionClosed(void) ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_EC, 0, &soft), WC_NO_ERR_TRACE(BAD_STATE_E)); ExpectIntEQ(test_pkcs11_opens, 0); + /* A persistent store needs no open session. */ + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &soft, 1), + 0); + ExpectIntEQ(test_pkcs11_opens, 1); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + test_pkcs11_reset(); #ifdef TEST_PKCS11_RSA_KEYGEN ExpectIntEQ(wc_InitRsaKey_Id(&rsa, (unsigned char*)id, (int)sizeof(id), NULL, TEST_PKCS11_DEVID), 0); diff --git a/tests/api/test_pkcs11.h b/tests/api/test_pkcs11.h index ff7089a6729..f6476f59359 100644 --- a/tests/api/test_pkcs11.h +++ b/tests/api/test_pkcs11.h @@ -30,6 +30,7 @@ int test_wc_Pkcs11_EcdsaSigDecode(void); int test_wc_Pkcs11_FreeKeepsTokenKey(void); +int test_wc_Pkcs11_StoreKeyPersistent(void); int test_wc_Pkcs11_RsaIdNoLeak(void); int test_wc_Pkcs11_EcKeyUsage(void); int test_wc_Pkcs11_RsaKeyGenError(void); @@ -40,6 +41,7 @@ int test_wc_Pkcs11_KeyGenSessionClosed(void); #define TEST_PKCS11_DECLS \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_EcdsaSigDecode), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_FreeKeepsTokenKey), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_StoreKeyPersistent), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaIdNoLeak), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_EcKeyUsage), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaKeyGenError), \ diff --git a/wolfcrypt/src/wc_pkcs11.c b/wolfcrypt/src/wc_pkcs11.c index 09bdb96613d..32b3345c102 100644 --- a/wolfcrypt/src/wc_pkcs11.c +++ b/wolfcrypt/src/wc_pkcs11.c @@ -75,8 +75,10 @@ #endif -#if (defined(HAVE_ECC) && !defined(NO_PKCS11_ECDH)) || \ - defined(WOLFSSL_HAVE_MLKEM) +#if (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) || defined(HAVE_ECC) || \ + defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_MLKEM) || \ + (!defined(NO_AES) && (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || \ + !defined(NO_HMAC) /* Pointer to false required for templates. */ static CK_BBOOL ckFalse = CK_FALSE; #endif @@ -88,6 +90,30 @@ static CK_BBOOL ckFalse = CK_FALSE; static CK_BBOOL ckTrue = CK_TRUE; #endif +#if (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) || defined(HAVE_ECC) || \ + defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_MLKEM) || \ + (!defined(NO_AES) && (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || \ + !defined(NO_HMAC) +/* Attributes of a private or secret key created as a token object. */ +static CK_ATTRIBUTE pkcs11TokenKeyAttrs[] = { + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_PRIVATE, &ckTrue, sizeof(ckTrue) }, + { CKA_SENSITIVE, &ckTrue, sizeof(ckTrue) }, + { CKA_EXTRACTABLE, &ckFalse, sizeof(ckFalse) } +}; +#define PKCS11_TOKEN_KEY_ATTRS 4 +#endif + +#if (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) || defined(HAVE_ECC) || \ + defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_MLKEM) +/* Attributes of a public key created as a token object. */ +static CK_ATTRIBUTE pkcs11TokenPubKeyAttrs[] = { + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) } +}; +#define PKCS11_TOKEN_PUB_KEY_ATTRS 2 +#endif + #ifndef NO_RSA /* Pointer to RSA key type required for templates. */ static CK_KEY_TYPE rsaKeyType = CKK_RSA; @@ -243,7 +269,10 @@ static void pkcs11_dump_template(const char* name, CK_ATTRIBUTE* templ, } else #endif -#if defined(HAVE_ECC) && !defined(NO_PKCS11_ECDH) +#if (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) || defined(HAVE_ECC) || \ + defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_MLKEM) || \ + (!defined(NO_AES) && (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || \ + !defined(NO_HMAC) if (templ[i].pValue == &ckFalse) { XSNPRINTF(line, sizeof(line), "%25s: FALSE", type); WOLFSSL_MSG(line); @@ -1135,6 +1164,7 @@ static int Pkcs11OpenSession(Pkcs11Token* token, Pkcs11Session* session, session->func = token->func; session->slotId = token->slotId; session->version = token->version; + session->tokenObjects = 0; session->closes = (session->handle != token->handle); } @@ -1280,7 +1310,7 @@ static int Pkcs11CreateSecretKey(CK_OBJECT_HANDLE* key, Pkcs11Session* session, { int ret = 0; CK_RV rv; - /* Empty entries for optional label/ID. */ + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE keyTemplateEncDec[] = { { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, @@ -1288,9 +1318,13 @@ static int Pkcs11CreateSecretKey(CK_OBJECT_HANDLE* key, Pkcs11Session* session, { CKA_DECRYPT, &ckTrue, sizeof(ckTrue) }, { CKA_VALUE, NULL, 0 }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; - /* Empty entries for optional label/ID. */ + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE keyTemplateSignVfy[] = { { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, @@ -1298,10 +1332,14 @@ static int Pkcs11CreateSecretKey(CK_OBJECT_HANDLE* key, Pkcs11Session* session, { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, { CKA_VALUE, NULL, 0 }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; CK_ATTRIBUTE* keyTemplate = NULL; - /* 5 mandatory entries + 2 optional. */ + /* 5 mandatory entries + 6 optional. */ int keyTmplCnt = 5; WOLFSSL_MSG("PKCS#11: Create Secret Key"); @@ -1333,6 +1371,11 @@ static int Pkcs11CreateSecretKey(CK_OBJECT_HANDLE* key, Pkcs11Session* session, keyTemplate[keyTmplCnt].ulValueLen = idLen; keyTmplCnt++; } + if (session->tokenObjects) { + XMEMCPY(&keyTemplate[keyTmplCnt], pkcs11TokenKeyAttrs, + sizeof(pkcs11TokenKeyAttrs)); + keyTmplCnt += PKCS11_TOKEN_KEY_ATTRS; + } PKCS11_DUMP_TEMPLATE("Secret Key", keyTemplate, keyTmplCnt); /* Create an object containing key data for device to use. */ @@ -1364,7 +1407,7 @@ static int Pkcs11CreateRsaPrivateKey(CK_OBJECT_HANDLE* privateKey, { int ret = 0; CK_RV rv; - /* Empty entries for optional label/ID. */ + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE keyTemplate[] = { { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, { CKA_KEY_TYPE, &rsaKeyType, sizeof(rsaKeyType) }, @@ -1379,10 +1422,14 @@ static int Pkcs11CreateRsaPrivateKey(CK_OBJECT_HANDLE* privateKey, { CKA_COEFFICIENT, NULL, 0 }, { CKA_PUBLIC_EXPONENT, NULL, 0 }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; - /* Mandatory entries + 2 optional. */ - CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 2; + /* Mandatory entries + 6 optional. */ + CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 6; /* Set the modulus and private key data. */ keyTemplate[ 4].pValue = rsaKey->n.raw.buf; @@ -1414,6 +1461,11 @@ static int Pkcs11CreateRsaPrivateKey(CK_OBJECT_HANDLE* privateKey, keyTemplate[keyTmplCnt].ulValueLen = rsaKey->idLen; keyTmplCnt++; } + if (session->tokenObjects) { + XMEMCPY(&keyTemplate[keyTmplCnt], pkcs11TokenKeyAttrs, + sizeof(pkcs11TokenKeyAttrs)); + keyTmplCnt += PKCS11_TOKEN_KEY_ATTRS; + } PKCS11_DUMP_TEMPLATE("RSA Private Key", keyTemplate, keyTmplCnt); rv = session->func->C_CreateObject(session->handle, keyTemplate, keyTmplCnt, @@ -1425,6 +1477,92 @@ static int Pkcs11CreateRsaPrivateKey(CK_OBJECT_HANDLE* privateKey, return ret; } + +/** + * Create a PKCS#11 object containing the RSA public key data. + * + * @param [out] publicKey Handle to public key object. + * @param [in] session Session object. + * @param [in] rsaKey RSA key with the modulus and public exponent. + * @return BAD_FUNC_ARG when the modulus or exponent is zero. + * @return WC_HW_E when a PKCS#11 library call fails. + * @return MEMORY_E when a memory allocation fails. + * @return 0 on success. + */ +static int Pkcs11CreateRsaPublicKey(CK_OBJECT_HANDLE* publicKey, + Pkcs11Session* session, RsaKey* rsaKey) +{ + int ret = 0; + CK_RV rv; + byte* data = NULL; + int modSz; + int expSz; + /* Empty entries for optional label/ID/token. */ + CK_ATTRIBUTE keyTemplate[] = { + { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, + { CKA_KEY_TYPE, &rsaKeyType, sizeof(rsaKeyType) }, + { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, + { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, + { CKA_MODULUS, NULL, 0 }, + { CKA_PUBLIC_EXPONENT, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 } + }; + /* Mandatory entries + 4 optional. */ + CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 4; + + modSz = mp_unsigned_bin_size(&rsaKey->n); + expSz = mp_unsigned_bin_size(&rsaKey->e); + if (modSz <= 0 || expSz <= 0) + ret = BAD_FUNC_ARG; + if (ret == 0) { + data = (byte*)XMALLOC((size_t)(modSz + expSz), rsaKey->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (data == NULL) + ret = MEMORY_E; + } + if (ret == 0) + ret = mp_to_unsigned_bin(&rsaKey->n, data); + if (ret == 0) + ret = mp_to_unsigned_bin(&rsaKey->e, data + modSz); + if (ret == 0) { + keyTemplate[4].pValue = data; + keyTemplate[4].ulValueLen = (CK_ULONG)modSz; + keyTemplate[5].pValue = data + modSz; + keyTemplate[5].ulValueLen = (CK_ULONG)expSz; + if (rsaKey->labelLen > 0) { + keyTemplate[keyTmplCnt].type = CKA_LABEL; + keyTemplate[keyTmplCnt].pValue = rsaKey->label; + keyTemplate[keyTmplCnt].ulValueLen = rsaKey->labelLen; + keyTmplCnt++; + } + if (rsaKey->idLen > 0) { + keyTemplate[keyTmplCnt].type = CKA_ID; + keyTemplate[keyTmplCnt].pValue = rsaKey->id; + keyTemplate[keyTmplCnt].ulValueLen = rsaKey->idLen; + keyTmplCnt++; + } + if (session->tokenObjects) { + XMEMCPY(&keyTemplate[keyTmplCnt], pkcs11TokenPubKeyAttrs, + sizeof(pkcs11TokenPubKeyAttrs)); + keyTmplCnt += PKCS11_TOKEN_PUB_KEY_ATTRS; + } + + PKCS11_DUMP_TEMPLATE("RSA Public Key", keyTemplate, keyTmplCnt); + rv = session->func->C_CreateObject(session->handle, keyTemplate, + keyTmplCnt, publicKey); + PKCS11_RV("C_CreateObject", rv); + if (rv != CKR_OK) { + ret = WC_HW_E; + } + } + + XFREE(data, rsaKey->heap, DYNAMIC_TYPE_TMP_BUFFER); + + return ret; +} #endif /* !NO_RSA && WOLFSSL_KEY_GEN */ #ifdef HAVE_ECC @@ -1489,7 +1627,7 @@ static int Pkcs11CreateEccPublicKey(CK_OBJECT_HANDLE* publicKey, word32 len; CK_RV rv; CK_UTF8CHAR params[ECC_MAX_OID_LEN]; - /* Empty entries for optional label/ID. */ + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE keyTemplate[] = { { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, { CKA_KEY_TYPE, &ecKeyType, sizeof(ecKeyType) }, @@ -1497,10 +1635,12 @@ static int Pkcs11CreateEccPublicKey(CK_OBJECT_HANDLE* publicKey, { CKA_EC_PARAMS, params, 0 }, { CKA_EC_POINT, NULL, 0 }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; - /* Mandatory entries + 2 optional. */ - CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 2; + /* Mandatory entries + 4 optional. */ + CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 4; if (public_key->labelLen > 0) { keyTemplate[keyTmplCnt].type = CKA_LABEL; @@ -1514,6 +1654,11 @@ static int Pkcs11CreateEccPublicKey(CK_OBJECT_HANDLE* publicKey, keyTemplate[keyTmplCnt].ulValueLen = public_key->idLen; keyTmplCnt++; } + if (session->tokenObjects) { + XMEMCPY(&keyTemplate[keyTmplCnt], pkcs11TokenPubKeyAttrs, + sizeof(pkcs11TokenPubKeyAttrs)); + keyTmplCnt += PKCS11_TOKEN_PUB_KEY_ATTRS; + } ret = Pkcs11EccSetParams(public_key, keyTemplate, 3); if (ret == 0) { @@ -1572,7 +1717,7 @@ static int Pkcs11CreateEccPrivateKey(CK_OBJECT_HANDLE* privateKey, int ret = 0; CK_RV rv; CK_UTF8CHAR params[ECC_MAX_OID_LEN]; - /* Empty entries for optional sign/derive/label/ID. */ + /* Empty entries for optional sign/derive/label/ID/token. */ CK_ATTRIBUTE keyTemplate[] = { { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, { CKA_KEY_TYPE, &ecKeyType, sizeof(ecKeyType) }, @@ -1581,10 +1726,14 @@ static int Pkcs11CreateEccPrivateKey(CK_OBJECT_HANDLE* privateKey, { 0, NULL, 0 }, { 0, NULL, 0 }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; - /* Mandatory entries + 4 optional. */ - CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 4; + /* Mandatory entries + 8 optional. */ + CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 8; if ((usage & PKCS11_ECC_SIGN) != 0) { keyTemplate[keyTmplCnt].type = CKA_SIGN; @@ -1611,6 +1760,11 @@ static int Pkcs11CreateEccPrivateKey(CK_OBJECT_HANDLE* privateKey, keyTemplate[keyTmplCnt].ulValueLen = private_key->idLen; keyTmplCnt++; } + if (session->tokenObjects) { + XMEMCPY(&keyTemplate[keyTmplCnt], pkcs11TokenKeyAttrs, + sizeof(pkcs11TokenKeyAttrs)); + keyTmplCnt += PKCS11_TOKEN_KEY_ATTRS; + } ret = Pkcs11EccSetParams(private_key, keyTemplate, 2); if (ret == 0) { @@ -1668,9 +1822,11 @@ static int Pkcs11CreateMlKemPublicKey(CK_OBJECT_HANDLE* handle, { CKA_PARAMETER_SET, ¶m_set, sizeof(param_set) }, { 0, NULL, 0 }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, }; CK_ULONG keyTmplCnt = - sizeof(keyTemplate) / sizeof(*keyTemplate) - 2; + sizeof(keyTemplate) / sizeof(*keyTemplate) - 4; if (mechInfo == NULL || (key->flags & MLKEM_FLAG_PUB_SET) == 0) { ret = BAD_FUNC_ARG; @@ -1687,6 +1843,11 @@ static int Pkcs11CreateMlKemPublicKey(CK_OBJECT_HANDLE* handle, keyTemplate[keyTmplCnt].ulValueLen = key->idLen; keyTmplCnt++; } + if (ret == 0 && session->tokenObjects) { + XMEMCPY(&keyTemplate[keyTmplCnt], pkcs11TokenPubKeyAttrs, + sizeof(pkcs11TokenPubKeyAttrs)); + keyTmplCnt += PKCS11_TOKEN_PUB_KEY_ATTRS; + } if (ret == 0) { switch (key->type) { #ifndef WOLFSSL_NO_ML_KEM @@ -1766,9 +1927,13 @@ static int Pkcs11CreateMlKemPrivateKey(CK_OBJECT_HANDLE* privateKey, { CKA_PARAMETER_SET, ¶m_set, sizeof(param_set) }, { 0, NULL, 0 }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, }; CK_ULONG keyTmplCnt = - sizeof(keyTemplate) / sizeof(*keyTemplate) - 2; + sizeof(keyTemplate) / sizeof(*keyTemplate) - 6; if (mechInfo == NULL || (key->flags & MLKEM_FLAG_PRIV_SET) == 0) { ret = BAD_FUNC_ARG; @@ -1785,6 +1950,11 @@ static int Pkcs11CreateMlKemPrivateKey(CK_OBJECT_HANDLE* privateKey, keyTemplate[keyTmplCnt].ulValueLen = key->idLen; keyTmplCnt++; } + if (ret == 0 && session->tokenObjects) { + XMEMCPY(&keyTemplate[keyTmplCnt], pkcs11TokenKeyAttrs, + sizeof(pkcs11TokenKeyAttrs)); + keyTmplCnt += PKCS11_TOKEN_KEY_ATTRS; + } if (ret == 0) { switch (key->type) { #ifndef WOLFSSL_NO_ML_KEM @@ -1869,7 +2039,7 @@ static int Pkcs11CreateMldsaPublicKey(CK_OBJECT_HANDLE* handle, CK_RV rv; CK_ULONG publicKeyLen = 0; CK_ML_DSA_PARAMETER_SET_TYPE param_set = 0; - /* Empty entries for optional label/ID. */ + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE keyTemplate[] = { { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, { CKA_KEY_TYPE, &mldsaKeyType, sizeof(mldsaKeyType) }, @@ -1878,9 +2048,11 @@ static int Pkcs11CreateMldsaPublicKey(CK_OBJECT_HANDLE* handle, { CKA_PARAMETER_SET, ¶m_set, sizeof(param_set) }, { 0, NULL, 0 }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, }; - /* Mandatory entries + 2 optional. */ - CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 2; + /* Mandatory entries + 4 optional. */ + CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 4; if (!key->pubKeySet) { ret = PUBLIC_KEY_E; @@ -1898,6 +2070,11 @@ static int Pkcs11CreateMldsaPublicKey(CK_OBJECT_HANDLE* handle, keyTemplate[keyTmplCnt].ulValueLen = key->idLen; keyTmplCnt++; } + if (session->tokenObjects) { + XMEMCPY(&keyTemplate[keyTmplCnt], pkcs11TokenPubKeyAttrs, + sizeof(pkcs11TokenPubKeyAttrs)); + keyTmplCnt += PKCS11_TOKEN_PUB_KEY_ATTRS; + } if ((key->level == WC_ML_DSA_44) && (mechInfo->ulMinKeySize <= WC_MLDSA_44_PUB_KEY_SIZE) && @@ -1956,7 +2133,7 @@ static int Pkcs11CreateMldsaPrivateKey(CK_OBJECT_HANDLE* privateKey, CK_RV rv; CK_ULONG privateKeyLen = 0; CK_ML_DSA_PARAMETER_SET_TYPE param_set = 0; - /* Empty entries for optional label/ID. */ + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE keyTemplate[] = { { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, { CKA_KEY_TYPE, &mldsaKeyType, sizeof(mldsaKeyType) }, @@ -1964,10 +2141,14 @@ static int Pkcs11CreateMldsaPrivateKey(CK_OBJECT_HANDLE* privateKey, { CKA_VALUE, NULL, 0 }, { CKA_PARAMETER_SET, ¶m_set, sizeof(param_set) }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; - /* Mandatory entries + 2 optional. */ - CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 2; + /* Mandatory entries + 6 optional. */ + CK_ULONG keyTmplCnt = sizeof(keyTemplate) / sizeof(*keyTemplate) - 6; if (key->labelLen > 0) { keyTemplate[keyTmplCnt].type = CKA_LABEL; @@ -1981,6 +2162,11 @@ static int Pkcs11CreateMldsaPrivateKey(CK_OBJECT_HANDLE* privateKey, keyTemplate[keyTmplCnt].ulValueLen = key->idLen; keyTmplCnt++; } + if (session->tokenObjects) { + XMEMCPY(&keyTemplate[keyTmplCnt], pkcs11TokenKeyAttrs, + sizeof(pkcs11TokenKeyAttrs)); + keyTmplCnt += PKCS11_TOKEN_KEY_ATTRS; + } if ((key->level == WC_ML_DSA_44) && (mechInfo->ulMinKeySize <= WC_MLDSA_44_PUB_KEY_SIZE) && @@ -2168,6 +2354,14 @@ static int Pkcs11HmacTypes(int macType, int* mechType, int* keyType) } #endif +#if (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) || defined(HAVE_ECC) || \ + defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_MLKEM) || \ + (!defined(NO_AES) && (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || \ + !defined(NO_HMAC) +static int Pkcs11CheckNewTokenKey(Pkcs11Session* session, CK_KEY_TYPE keyType, + byte* id, int idLen, char* label, int labelLen); +#endif + /** * Store the private key on the token in the session. * @@ -2175,7 +2369,7 @@ static int Pkcs11HmacTypes(int macType, int* mechType, int* keyType) * @param [in] type Key type. * @param [in] clear Clear out the private data from software key. * @param [in] key Key type specific object. - * @return BAD_FUNC_ARG when token is NULL. + * @return BAD_FUNC_ARG when token or key is NULL. * @return BAD_STATE_E when the token has no session from * wc_Pkcs11Token_Open, so the key would go with this call's session. * @return MEMORY_E when a memory allocation fails. @@ -2184,24 +2378,54 @@ static int Pkcs11HmacTypes(int macType, int* mechType, int* keyType) * @return 0 on success. */ int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key) +{ + return wc_Pkcs11StoreKey_ex(token, type, clear, key, 0); +} + +/** + * Store the private key on the token, in the session or persistently. + * + * @param [in] token Token to store private key on. + * @param [in] type Key type. + * @param [in] clear Clear out the private data from software key. + * @param [in] key Key type specific object. + * @param [in] persistent Create token objects (CKA_TOKEN set). + * @return BAD_FUNC_ARG when token or key is NULL, or a persistent key has + * neither id nor label. + * @return BAD_STATE_E when persistent is 0 and the token has no session + * from wc_Pkcs11Token_Open, or a token key of the type already has + * the persistent key's id or label. + * @return MEMORY_E when a memory allocation fails. + * @return NOT_COMPILED_IN when mechanism not available. + * @return WC_HW_E when a PKCS#11 library call fails. + * @return 0 on success. + */ +int wc_Pkcs11StoreKey_ex(Pkcs11Token* token, int type, int clear, void* key, + int persistent) { int ret = 0; Pkcs11Session session; CK_OBJECT_HANDLE privKey = NULL_PTR; - if (token == NULL) + if (token == NULL || key == NULL) ret = BAD_FUNC_ARG; - else if (token->handle == NULL_PTR) + else if (token->handle == NULL_PTR && !persistent) ret = BAD_STATE_E; else ret = Pkcs11OpenSession(token, &session, 1); if (ret == 0) { + session.tokenObjects = (persistent != 0); switch (type) { #if !defined(NO_AES) && defined(HAVE_AESGCM) case PKCS11_KEY_TYPE_AES_GCM: { Aes* aes = (Aes*)key; ret = Pkcs11MechAvail(&session, CKM_AES_GCM, NULL); + if (ret == 0 && persistent) { + ret = Pkcs11CheckNewTokenKey(&session, CKK_AES, + (byte*)aes->id, aes->idLen, aes->label, + aes->labelLen); + } if (ret == 0) { ret = Pkcs11CreateSecretKey(&privKey, &session, CKK_AES, (unsigned char*)aes->devKey, @@ -2220,6 +2444,11 @@ int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key) Aes* aes = (Aes*)key; ret = Pkcs11MechAvail(&session, CKM_AES_CBC, NULL); + if (ret == 0 && persistent) { + ret = Pkcs11CheckNewTokenKey(&session, CKK_AES, + (byte*)aes->id, aes->idLen, aes->label, + aes->labelLen); + } if (ret == 0) { ret = Pkcs11CreateSecretKey(&privKey, &session, CKK_AES, (unsigned char*)aes->devKey, @@ -2245,6 +2474,16 @@ int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key) if (ret == 0) ret = Pkcs11MechAvail(&session, mechType, NULL); + if (ret == 0 && persistent) { + ret = Pkcs11CheckNewTokenKey(&session, (CK_KEY_TYPE)keyType, + (byte*)hmac->id, hmac->idLen, hmac->label, + hmac->labelLen); + } + if (ret == 0 && persistent) { + ret = Pkcs11CheckNewTokenKey(&session, CKK_GENERIC_SECRET, + (byte*)hmac->id, hmac->idLen, hmac->label, + hmac->labelLen); + } if (ret == 0) { ret = Pkcs11CreateSecretKey(&privKey, &session, keyType, (unsigned char*)hmac->keyRaw, @@ -2270,9 +2509,22 @@ int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key) RsaKey* rsaKey = (RsaKey*)key; ret = Pkcs11MechAvail(&session, CKM_RSA_X_509, NULL); + if (ret == 0 && persistent) { + ret = Pkcs11CheckNewTokenKey(&session, CKK_RSA, + (byte*)rsaKey->id, rsaKey->idLen, rsaKey->label, + rsaKey->labelLen); + } if (ret == 0) ret = Pkcs11CreateRsaPrivateKey(&privKey, &session, rsaKey, 1); + if (ret == 0 && persistent) { + CK_OBJECT_HANDLE pubKey = NULL_PTR; + + ret = Pkcs11CreateRsaPublicKey(&pubKey, &session, rsaKey); + if (ret != 0) { + session.func->C_DestroyObject(session.handle, privKey); + } + } if (ret == 0 && clear) { mp_forcezero(&rsaKey->u); mp_forcezero(&rsaKey->dQ); @@ -2289,7 +2541,13 @@ int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key) ecc_key* eccKey = (ecc_key*)key; int usage = 0; - ret = Pkcs11EccKeyUsage(&session, eccKey, &usage); + if (persistent) { + ret = Pkcs11CheckNewTokenKey(&session, CKK_EC, + (byte*)eccKey->id, eccKey->idLen, eccKey->label, + eccKey->labelLen); + } + if (ret == 0) + ret = Pkcs11EccKeyUsage(&session, eccKey, &usage); if (ret == 0) { ret = Pkcs11CreateEccPrivateKey(&privKey, &session, eccKey, usage); @@ -2319,6 +2577,11 @@ int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key) CK_MECHANISM_INFO mechInfo; ret = Pkcs11MechAvail(&session, CKM_ML_KEM, &mechInfo); + if (ret == 0 && persistent) { + ret = Pkcs11CheckNewTokenKey(&session, CKK_ML_KEM, + (byte*)mlkemKey->id, mlkemKey->idLen, mlkemKey->label, + mlkemKey->labelLen); + } if (ret == 0 && ((mlkemKey->flags & MLKEM_FLAG_PRIV_SET) != 0)) { ret = Pkcs11CreateMlKemPrivateKey(&privKey, &session, mlkemKey, &mechInfo); @@ -2349,6 +2612,11 @@ int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key) CK_MECHANISM_INFO mechInfo; ret = Pkcs11MechAvail(&session, CKM_ML_DSA, &mechInfo); + if (ret == 0 && persistent) { + ret = Pkcs11CheckNewTokenKey(&session, CKK_ML_DSA, + (byte*)mldsaKey->id, mldsaKey->idLen, mldsaKey->label, + mldsaKey->labelLen); + } if (ret == 0 && mldsaKey->prvKeySet) { ret = Pkcs11CreateMldsaPrivateKey(&privKey, &session, @@ -2399,7 +2667,8 @@ int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key) #if !defined(NO_RSA) || defined(HAVE_ECC) || (!defined(NO_AES) && \ (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || \ - !defined(NO_HMAC) || !defined(NO_CERTS) + !defined(NO_HMAC) || !defined(NO_CERTS) || \ + defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_MLKEM) /** * Find the PKCS#11 object containing key data using template. @@ -2519,6 +2788,107 @@ static int Pkcs11FindKeyById(CK_OBJECT_HANDLE* key, CK_OBJECT_CLASS keyClass, } #endif +#if !defined(NO_RSA) || defined(HAVE_ECC) || defined(WOLFSSL_HAVE_MLDSA) || \ + defined(WOLFSSL_HAVE_MLKEM) || (!defined(NO_AES) && \ + (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || !defined(NO_HMAC) +/** + * Find key objects by id and label. An empty id or label is not matched. + * + * @param [in] session Session object. + * @param [in] keyClass Key class to match. NULL matches any class. + * @param [in] keyType Key type to match. + * @param [in] onToken Match token objects or session objects. + * @param [in] id Identifier of key. + * @param [in] idLen Length of identifier. + * @param [in] label Label of key. + * @param [in] labelLen Length of label. + * @param [out] handle Handle of first object found. + * @param [out] count Number of objects found, 0 or 1. + * @return WC_HW_E when a PKCS#11 library call fails. + * @return 0 on success. + */ +static int Pkcs11FindKeyObject(Pkcs11Session* session, + CK_OBJECT_CLASS* keyClass, CK_KEY_TYPE keyType, CK_BBOOL onToken, + byte* id, int idLen, char* label, int labelLen, CK_OBJECT_HANDLE* handle, + CK_ULONG* count) +{ + CK_ATTRIBUTE tmpl[5]; + CK_ULONG tmplCnt = 0; + + tmpl[tmplCnt].type = CKA_KEY_TYPE; + tmpl[tmplCnt].pValue = &keyType; + tmpl[tmplCnt].ulValueLen = sizeof(keyType); + tmplCnt++; + tmpl[tmplCnt].type = CKA_TOKEN; + tmpl[tmplCnt].pValue = &onToken; + tmpl[tmplCnt].ulValueLen = sizeof(onToken); + tmplCnt++; + if (idLen > 0) { + tmpl[tmplCnt].type = CKA_ID; + tmpl[tmplCnt].pValue = id; + tmpl[tmplCnt].ulValueLen = (CK_ULONG)idLen; + tmplCnt++; + } + if (labelLen > 0) { + tmpl[tmplCnt].type = CKA_LABEL; + tmpl[tmplCnt].pValue = label; + tmpl[tmplCnt].ulValueLen = (CK_ULONG)labelLen; + tmplCnt++; + } + if (keyClass != NULL) { + tmpl[tmplCnt].type = CKA_CLASS; + tmpl[tmplCnt].pValue = keyClass; + tmpl[tmplCnt].ulValueLen = sizeof(*keyClass); + tmplCnt++; + } + + return Pkcs11FindKeyByTemplate(handle, session, tmpl, tmplCnt, count); +} +#endif + +#if (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) || defined(HAVE_ECC) || \ + defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_MLKEM) || \ + (!defined(NO_AES) && (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || \ + !defined(NO_HMAC) +/** + * Check a key can be created as a new token object. + * Lookups go by label or by id, so neither may be in use by a token key. + * + * @param [in] session Session object. + * @param [in] keyType Key type. + * @param [in] id Identifier of key. + * @param [in] idLen Length of identifier. + * @param [in] label Label of key. + * @param [in] labelLen Length of label. + * @return BAD_FUNC_ARG when the key has neither id nor label. + * @return BAD_STATE_E when a token key of the type has the id or label. + * @return WC_HW_E when a PKCS#11 library call fails. + * @return 0 on success. + */ +static int Pkcs11CheckNewTokenKey(Pkcs11Session* session, CK_KEY_TYPE keyType, + byte* id, int idLen, char* label, int labelLen) +{ + int ret = 0; + CK_ULONG count = 0; + CK_OBJECT_HANDLE handle; + + if (idLen <= 0 && labelLen <= 0) + ret = BAD_FUNC_ARG; + if (ret == 0 && idLen > 0) { + ret = Pkcs11FindKeyObject(session, NULL, keyType, CK_TRUE, id, idLen, + NULL, 0, &handle, &count); + } + if (ret == 0 && count == 0 && labelLen > 0) { + ret = Pkcs11FindKeyObject(session, NULL, keyType, CK_TRUE, NULL, 0, + label, labelLen, &handle, &count); + } + if (ret == 0 && count != 0) + ret = BAD_STATE_E; + + return ret; +} +#endif + #ifndef NO_RSA /** * Find the PKCS#11 object containing the RSA public or private key data with @@ -6770,6 +7140,7 @@ int wc_Pkcs11_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) session.version = token->version; session.handle = (CK_SESSION_HANDLE)(wc_ptr_t)hmac->devCtx; + session.tokenObjects = 0; session.closes = 0; ret = Pkcs11Hmac(&session, info); if (ret != 0 || diff --git a/wolfssl/wolfcrypt/wc_pkcs11.h b/wolfssl/wolfcrypt/wc_pkcs11.h index 0477666edbd..5e943b3c6ed 100644 --- a/wolfssl/wolfcrypt/wc_pkcs11.h +++ b/wolfssl/wolfcrypt/wc_pkcs11.h @@ -70,6 +70,7 @@ typedef struct Pkcs11Session { CK_SESSION_HANDLE handle; /* Handle to active session */ int version; /* Pkcs11InterfaceVersionType */ WC_BITFIELD closes:1; /* Closed after this operation */ + WC_BITFIELD tokenObjects:1; /* Create keys with CKA_TOKEN set */ } Pkcs11Session; /* Types of keys that can be stored. */ @@ -108,6 +109,8 @@ WOLFSSL_API void wc_Pkcs11Token_Close(Pkcs11Token* token); WOLFSSL_API int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key); +WOLFSSL_API int wc_Pkcs11StoreKey_ex(Pkcs11Token* token, int type, int clear, + void* key, int persistent); WOLFSSL_API int wc_Pkcs11_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx); From e69a7f2eeaee1e6e45c867928b6bad81b2ec80aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Thu, 22 Oct 2026 13:09:34 +0200 Subject: [PATCH 2/4] Add wc_Pkcs11PersistKey to keep a session key on the token Keys generated through the PKCS#11 crypto callback, and keys imported with wc_Pkcs11StoreKey, are session objects. Key generation cannot know whether the caller wants to keep the key, so an application decides after the fact, for example once a certificate for the key has been issued. wc_Pkcs11PersistKey copies the session object to a token object with C_CopyObject, which PKCS#11 allows for keys that are not extractable, sets the protecting attributes wc_Pkcs11StoreKey_ex uses and the key's id and label, and destroys the session original. The session object is the handle in devCtx when key generation left one there, otherwise the session object with the key's label and id. RSA, ECC, ML-DSA, ML-KEM, AES and HMAC keys are supported. An HMAC key needs its hash type and is found under its HMAC key type or as a generic secret. For RSA, ECC, ML-DSA and ML-KEM keys that hold their public key, a token public key object is created too, because RSA public key operations, the check-private-key paths and ML-KEM encapsulation look the public key up by id or label for a key loaded by name. As wc_Pkcs11StoreKey_ex does, an ECC public key object is created only when the token supports CKM_ECDSA, and a failure to create it is ignored when the token's private key object cannot sign. For ECC, ML-DSA and ML-KEM, session public key objects with the same names are destroyed, so such a lookup cannot find a stale one. A key without its public part, such as one initialized by id or label only, gets a token copy of the session public key object with its names instead, and the session object is destroyed only after the copy succeeds. An RSA session public key never has a name, since neither wc_Pkcs11StoreKey nor session key generation gives it one. RSA, ECC, ML-DSA and ML-KEM keys then refer to the new private object through devCtx, which the free callback leaves alone. The key needs an id or label that no token key of its type has, and BAD_STATE_E is returned when devCtx already refers to a token object. On failure the token objects created so far are destroyed and the key is left as it was. --- doc/dox_comments/header_files/pkcs11.h | 45 +++ tests/api/test_pkcs11.c | 165 +++++++++- tests/api/test_pkcs11.h | 2 + wolfcrypt/src/wc_pkcs11.c | 423 ++++++++++++++++++++++++- wolfssl/wolfcrypt/wc_pkcs11.h | 1 + 5 files changed, 624 insertions(+), 12 deletions(-) diff --git a/doc/dox_comments/header_files/pkcs11.h b/doc/dox_comments/header_files/pkcs11.h index bd4757cdfab..115350579ba 100644 --- a/doc/dox_comments/header_files/pkcs11.h +++ b/doc/dox_comments/header_files/pkcs11.h @@ -109,6 +109,51 @@ int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, int wc_Pkcs11StoreKey_ex(Pkcs11Token* token, int type, int clear, void* key, int persistent); +/*! + \ingroup PKCS11 + + \brief Makes a private or secret key that lives in a session object + persistent. The session object, from key generation or wc_Pkcs11StoreKey, + is copied with C_CopyObject to a token object with the key's id and label + and then destroyed. RSA, ECC, ML-DSA and ML-KEM keys that hold their + public key also get a token object with the public key, which for ECC, + ML-DSA and ML-KEM replaces any session public key with the same names. An + ECC, ML-DSA or ML-KEM key without its public key gets a token copy of that + session public key instead. As in wc_Pkcs11StoreKey_ex, an ECC public key + is stored only when the token has ECDSA, and is left out when it cannot be + created for a key that cannot sign. RSA, ECC, ML-DSA and ML-KEM keys refer + to the new private object through devCtx. Freeing the key does not remove + token objects. The token session must be read/write and logged in, and + stay open from key generation until this call. + + \return 0 on success. + \return BAD_FUNC_ARG when token or key is NULL, the key has neither an + id nor a label, or an Hmac has no hash type. + \return BAD_STATE_E when the token already holds a key with the same id + or label, or the key already refers to a token object. + \return NOT_COMPILED_IN when the key type is not supported. + \return WC_HW_E when no session object is found or a PKCS#11 call fails. + + \param token Token holding the key. + \param type PKCS11_KEY_TYPE_RSA, PKCS11_KEY_TYPE_EC, PKCS11_KEY_TYPE_MLDSA, + PKCS11_KEY_TYPE_MLKEM, PKCS11_KEY_TYPE_AES_GCM, PKCS11_KEY_TYPE_AES_CBC + or PKCS11_KEY_TYPE_HMAC. + \param key RsaKey, ecc_key, wc_MlDsaKey, MlKemKey, Aes or Hmac with an id + or label. An Hmac needs its hash type (macType), as wc_HmacSetKey sets. + + _Example_ + \code + ecc_key key; + wc_ecc_init_id(&key, id, idLen, NULL, devId); + wc_ecc_make_key_ex(&rng, 32, &key, ECC_SECP256R1); + // use the key, for example to sign a CSR, then keep it + wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key); + \endcode + + \sa wc_Pkcs11StoreKey_ex +*/ +int wc_Pkcs11PersistKey(Pkcs11Token* token, int type, void* key); + /*! \ingroup PKCS11 */ diff --git a/tests/api/test_pkcs11.c b/tests/api/test_pkcs11.c index c065aa7422d..67925ab9958 100644 --- a/tests/api/test_pkcs11.c +++ b/tests/api/test_pkcs11.c @@ -516,6 +516,10 @@ static CK_RV test_pkcs11_store_get_attr(CK_SESSION_HANDLE session, val = (const byte*)&obj->keyType; len = sizeof(obj->keyType); } + else if (tmpl[i].type == CKA_SIGN) { + val = &obj->sign; + len = sizeof(obj->sign); + } else if (tmpl[i].type == CKA_MODULUS && test_pkcs11_fail_modulus) { return TEST_CKR_GENERAL_ERROR; } @@ -886,7 +890,6 @@ int test_wc_Pkcs11_StoreKeyPersistent(void) return EXPECT_RESULT(); } -#if defined(TEST_PKCS11_EC_KEYGEN) && defined(HAVE_ECC_KEY_IMPORT) static CK_OBJECT_HANDLE test_pkcs11_find_obj(CK_OBJECT_CLASS cls, CK_BBOOL onToken) { @@ -899,7 +902,165 @@ static CK_OBJECT_HANDLE test_pkcs11_find_obj(CK_OBJECT_CLASS cls, } return 0; } -#endif + +int test_wc_Pkcs11_PersistKey(void) +{ + static const byte id[] = { 0x0a, 0x0b, 0x0c }; + static CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY; + static CK_KEY_TYPE ecType = CKK_EC; + static CK_BBOOL canSign = CK_TRUE; + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &privClass, sizeof(privClass) }, + { CKA_KEY_TYPE, &ecType, sizeof(ecType) }, + { CKA_ID, (void*)id, sizeof(id) }, + { CKA_SIGN, &canSign, sizeof(canSign) } + }; + Pkcs11Token token; + CK_FUNCTION_LIST func; + CK_OBJECT_HANDLE sessObj = 0; + CK_OBJECT_HANDLE tokObj = 0; + ecc_key key; + ecc_key noId; + ecc_key ref; + WC_RNG rng; + TestPkcs11Obj* obj = NULL; + EXPECT_DECLS; + + test_pkcs11_store_setup(&token, &func); + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&noId, 0, sizeof(noId)); + XMEMSET(&ref, 0, sizeof(ref)); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(test_pkcs11_make_ecc(&key, &rng, id, (int)sizeof(id)), 0); + ExpectIntEQ(wc_ecc_init(&noId), 0); + + ExpectIntEQ(wc_Pkcs11PersistKey(NULL, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &noId), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(WC_HW_E)); + + /* Session object found by id, as left by an import. */ + ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_EC, 0, &key), 0); + sessObj = test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_FALSE); + ExpectIntNE(sessObj, 0); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), 0); + tokObj = test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_TRUE); + ExpectIntNE(tokObj, 0); + ExpectNull(test_pkcs11_obj(sessObj)); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntNE(test_pkcs11_find_obj(CKO_PUBLIC_KEY, CK_TRUE), 0); + ExpectPtrEq(key.devCtx, (void*)(wc_ptr_t)tokObj); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + + ExpectIntEQ(test_pkcs11_free_ecc(&token, &key), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + + /* A reference without the public key keeps the session public key. */ + test_pkcs11_reset(); + ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_EC, 0, &key), 0); + ExpectIntEQ(wc_ecc_init_id(&ref, (unsigned char*)id, (int)sizeof(id), + NULL, INVALID_DEVID), 0); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &ref), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntNE(test_pkcs11_find_obj(CKO_PUBLIC_KEY, CK_TRUE), 0); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + wc_ecc_free(&ref); + + /* Session object in devCtx, as left by keygen; the public key fails. */ + test_pkcs11_reset(); + ExpectIntEQ(test_pkcs11_store_create(0, tmpl, 4, &sessObj), CKR_OK); + key.devCtx = (void*)(wc_ptr_t)sessObj; + test_pkcs11_fail_pub_create = 1; + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(WC_HW_E)); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + ExpectNotNull(test_pkcs11_obj(sessObj)); + ExpectPtrEq(key.devCtx, (void*)(wc_ptr_t)sessObj); + + test_pkcs11_fail_pub_create = 0; + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), 0); + ExpectNull(test_pkcs11_obj(sessObj)); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectPtrEq(key.devCtx, + (void*)(wc_ptr_t)test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_TRUE)); + + /* A key already on the token under other names is not copied again. */ + tokObj = (CK_OBJECT_HANDLE)(wc_ptr_t)key.devCtx; + key.id[0] = 0x0f; + key.idLen = 1; + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectNotNull(test_pkcs11_obj(tokObj)); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + + /* A key that cannot sign does not need its public key on the token. */ + test_pkcs11_reset(); + XMEMCPY(key.id, id, sizeof(id)); + key.idLen = (int)sizeof(id); + ExpectIntEQ(test_pkcs11_store_create(0, tmpl, 3, &sessObj), CKR_OK); + key.devCtx = (void*)(wc_ptr_t)sessObj; + test_pkcs11_fail_pub_create = 1; + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), 0); + test_pkcs11_fail_pub_create = 0; + ExpectIntEQ(test_pkcs11_find_obj(CKO_PUBLIC_KEY, CK_TRUE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + + /* Without ECDSA no public key is stored, as in wc_Pkcs11StoreKey_ex. */ + test_pkcs11_reset(); + ExpectIntEQ(test_pkcs11_store_create(0, tmpl, 4, &sessObj), CKR_OK); + key.devCtx = (void*)(wc_ptr_t)sessObj; + test_pkcs11_no_ecdsa = 1; + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), 0); + test_pkcs11_no_ecdsa = 0; + ExpectIntEQ(test_pkcs11_find_obj(CKO_PUBLIC_KEY, CK_TRUE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + ExpectPtrEq(key.devCtx, + (void*)(wc_ptr_t)test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_TRUE)); + + /* A session object that cannot be destroyed is left to its session. */ + test_pkcs11_reset(); + XMEMCPY(key.id, id, sizeof(id)); + key.idLen = (int)sizeof(id); + ExpectIntEQ(test_pkcs11_store_create(0, tmpl, 3, &sessObj), CKR_OK); + key.devCtx = (void*)(wc_ptr_t)sessObj; + test_pkcs11_fail_destroy = 1; + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), 0); + test_pkcs11_fail_destroy = 0; + ExpectNotNull(test_pkcs11_obj(sessObj)); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectPtrEq(key.devCtx, + (void*)(wc_ptr_t)test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_TRUE)); + + /* The token object gets the key's id and label. */ + test_pkcs11_reset(); + ExpectIntEQ(test_pkcs11_store_create(0, tmpl, 2, &sessObj), CKR_OK); + key.devCtx = (void*)(wc_ptr_t)sessObj; + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), 0); + ExpectNotNull(obj = test_pkcs11_obj(test_pkcs11_find_obj(CKO_PRIVATE_KEY, + CK_TRUE))); + if (obj != NULL) { + ExpectIntEQ(obj->idLen, sizeof(id)); + ExpectBufEQ(obj->id, id, sizeof(id)); + } + + key.devCtx = NULL; + wc_ecc_free(&key); + wc_ecc_free(&noId); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + + return EXPECT_RESULT(); +} #if !defined(NO_RSA) && !defined(NO_PKCS11_RSA) && \ defined(HAVE_WOLF_BIGINT) && \ diff --git a/tests/api/test_pkcs11.h b/tests/api/test_pkcs11.h index f6476f59359..c85871977e1 100644 --- a/tests/api/test_pkcs11.h +++ b/tests/api/test_pkcs11.h @@ -31,6 +31,7 @@ int test_wc_Pkcs11_EcdsaSigDecode(void); int test_wc_Pkcs11_FreeKeepsTokenKey(void); int test_wc_Pkcs11_StoreKeyPersistent(void); +int test_wc_Pkcs11_PersistKey(void); int test_wc_Pkcs11_RsaIdNoLeak(void); int test_wc_Pkcs11_EcKeyUsage(void); int test_wc_Pkcs11_RsaKeyGenError(void); @@ -42,6 +43,7 @@ int test_wc_Pkcs11_KeyGenSessionClosed(void); TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_EcdsaSigDecode), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_FreeKeepsTokenKey), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_StoreKeyPersistent), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_PersistKey), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaIdNoLeak), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_EcKeyUsage), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaKeyGenError), \ diff --git a/wolfcrypt/src/wc_pkcs11.c b/wolfcrypt/src/wc_pkcs11.c index 32b3345c102..7f30879c0ed 100644 --- a/wolfcrypt/src/wc_pkcs11.c +++ b/wolfcrypt/src/wc_pkcs11.c @@ -1256,8 +1256,38 @@ void wc_Pkcs11Token_Close(Pkcs11Token* token) } } -#if (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) || defined(HAVE_ECC) || \ - defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_MLKEM) +#if !defined(NO_RSA) || defined(HAVE_ECC) || defined(WOLFSSL_HAVE_MLDSA) || \ + defined(WOLFSSL_HAVE_MLKEM) || (!defined(NO_AES) && \ + (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || !defined(NO_HMAC) +/** + * Read whether a key object is a token object. + * + * @param [in] session Session object. + * @param [in] handle Handle of key object. + * @param [out] onToken CK_TRUE for a token object. + * @return WC_HW_E when CKA_TOKEN cannot be read. + * @return 0 on success. + */ +static int Pkcs11KeyOnToken(Pkcs11Session* session, CK_OBJECT_HANDLE handle, + CK_BBOOL* onToken) +{ + int ret = 0; + CK_RV rv; + CK_ATTRIBUTE tmpl[] = { + { CKA_TOKEN, onToken, sizeof(*onToken) } + }; + + rv = session->func->C_GetAttributeValue(session->handle, handle, tmpl, 1); + PKCS11_RV("C_GetAttributeValue", rv); + if (rv != CKR_OK || tmpl[0].ulValueLen != sizeof(*onToken)) + ret = WC_HW_E; + + return ret; +} +#endif + +#if !defined(NO_RSA) || defined(HAVE_ECC) || defined(WOLFSSL_HAVE_MLDSA) || \ + defined(WOLFSSL_HAVE_MLKEM) /** * Destroy a key object only when it is a session object. * Token objects, and objects whose CKA_TOKEN cannot be read, are kept. @@ -1268,19 +1298,15 @@ void wc_Pkcs11Token_Close(Pkcs11Token* token) static void Pkcs11DestroySessionKey(Pkcs11Session* session, CK_OBJECT_HANDLE handle) { - CK_RV rv; - CK_BBOOL onToken = CK_TRUE; - CK_ATTRIBUTE tmpl[] = { - { CKA_TOKEN, &onToken, sizeof(onToken) } - }; + CK_RV rv = CKR_OK; + CK_BBOOL onToken = CK_TRUE; - rv = session->func->C_GetAttributeValue(session->handle, handle, tmpl, 1); - PKCS11_RV("C_GetAttributeValue", rv); - if (rv == CKR_OK && tmpl[0].ulValueLen == sizeof(onToken) && + if (Pkcs11KeyOnToken(session, handle, &onToken) == 0 && onToken == CK_FALSE) { rv = session->func->C_DestroyObject(session->handle, handle); PKCS11_RV("C_DestroyObject", rv); } + (void)rv; } #endif @@ -6891,6 +6917,383 @@ static int Pkcs11GetCert(Pkcs11Session* session, wc_CryptoInfo* info) { #endif /* !NO_CERTS */ +#if (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) || defined(HAVE_ECC) || \ + defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_MLKEM) || \ + (!defined(NO_AES) && (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || \ + !defined(NO_HMAC) +/** + * Copy a private or secret key's session object to a new token object. + * + * @param [in] session Session object. + * @param [in] keyClass Key class. + * @param [in] keyType Key type. + * @param [in] devCtx Handle of session object from key, may be NULL. + * @param [in] id Identifier of key. + * @param [in] idLen Length of identifier. + * @param [in] label Label of key. + * @param [in] labelLen Length of label. + * @param [out] sessKey Handle of session object. + * @param [out] tokenKey Handle of new token object. + * @return BAD_FUNC_ARG when the key has neither id nor label. + * @return BAD_STATE_E when the token already holds a key with the id or + * label, or devCtx is a token object. + * @return WC_HW_E when no session object is found or a PKCS#11 call fails. + * @return 0 on success. + */ +static int Pkcs11PersistPrivKey(Pkcs11Session* session, + CK_OBJECT_CLASS* keyClass, CK_KEY_TYPE keyType, void* devCtx, byte* id, + int idLen, char* label, int labelLen, CK_OBJECT_HANDLE* sessKey, + CK_OBJECT_HANDLE* tokenKey) +{ + int ret; + CK_RV rv; + CK_ULONG count = 0; + CK_BBOOL onToken = CK_FALSE; + CK_ATTRIBUTE tmpl[PKCS11_TOKEN_KEY_ATTRS + 2]; + CK_ULONG tmplCnt = PKCS11_TOKEN_KEY_ATTRS; + + ret = Pkcs11CheckNewTokenKey(session, keyType, id, idLen, label, labelLen); + if (ret == 0 && devCtx != NULL) { + *sessKey = (CK_OBJECT_HANDLE)(wc_ptr_t)devCtx; + ret = Pkcs11KeyOnToken(session, *sessKey, &onToken); + if (ret == 0 && onToken != CK_FALSE) + ret = BAD_STATE_E; + } + else if (ret == 0) { + ret = Pkcs11FindKeyObject(session, keyClass, keyType, CK_FALSE, id, + idLen, label, labelLen, sessKey, &count); + if (ret == 0 && count == 0) + ret = WC_HW_E; + } + if (ret == 0) { + XMEMCPY(tmpl, pkcs11TokenKeyAttrs, sizeof(pkcs11TokenKeyAttrs)); + if (idLen > 0) { + tmpl[tmplCnt].type = CKA_ID; + tmpl[tmplCnt].pValue = id; + tmpl[tmplCnt].ulValueLen = (CK_ULONG)idLen; + tmplCnt++; + } + if (labelLen > 0) { + tmpl[tmplCnt].type = CKA_LABEL; + tmpl[tmplCnt].pValue = label; + tmpl[tmplCnt].ulValueLen = (CK_ULONG)labelLen; + tmplCnt++; + } + rv = session->func->C_CopyObject(session->handle, *sessKey, tmpl, + tmplCnt, tokenKey); + PKCS11_RV("C_CopyObject", rv); + if (rv != CKR_OK) + ret = WC_HW_E; + } + + return ret; +} +#endif + +#if defined(HAVE_ECC) || defined(WOLFSSL_HAVE_MLDSA) || \ + defined(WOLFSSL_HAVE_MLKEM) +/** + * Copy the session public key object with a key's id and label to a new + * token object. Nothing is copied when there is no such session object. + * + * @param [in] session Session object. + * @param [in] keyType Key type. + * @param [in] id Identifier of key. + * @param [in] idLen Length of identifier. + * @param [in] label Label of key. + * @param [in] labelLen Length of label. + * @param [out] tokenKey Handle of new token object. + * @return WC_HW_E when a PKCS#11 library call fails. + * @return 0 on success. + */ +static int Pkcs11PersistPubKey(Pkcs11Session* session, CK_KEY_TYPE keyType, + byte* id, int idLen, char* label, int labelLen, + CK_OBJECT_HANDLE* tokenKey) +{ + int ret; + CK_RV rv; + CK_ULONG count = 0; + CK_OBJECT_HANDLE sessKey = NULL_PTR; + + ret = Pkcs11FindKeyObject(session, &pubKeyClass, keyType, CK_FALSE, id, + idLen, label, labelLen, &sessKey, &count); + if (ret == 0 && count != 0) { + rv = session->func->C_CopyObject(session->handle, sessKey, + pkcs11TokenPubKeyAttrs, + PKCS11_TOKEN_PUB_KEY_ATTRS, tokenKey); + PKCS11_RV("C_CopyObject", rv); + if (rv != CKR_OK) + ret = WC_HW_E; + } + + return ret; +} +#endif + +#if !defined(NO_RSA) || defined(HAVE_ECC) || defined(WOLFSSL_HAVE_MLDSA) || \ + defined(WOLFSSL_HAVE_MLKEM) || (!defined(NO_AES) && \ + (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || !defined(NO_HMAC) +/** + * Destroy all key objects of a key type with a key's id and label. + * + * @param [in] session Session object. + * @param [in] keyClass Key class to match. NULL matches any class. + * @param [in] keyType Key type. + * @param [in] onToken Destroy token objects or session objects. + * @param [in] id Identifier of key. + * @param [in] idLen Length of identifier. + * @param [in] label Label of key. + * @param [in] labelLen Length of label. + * @return WC_HW_E when a PKCS#11 library call fails. + * @return 0 on success. + */ +static int Pkcs11DestroyKeyObjects(Pkcs11Session* session, + CK_OBJECT_CLASS* keyClass, CK_KEY_TYPE keyType, CK_BBOOL onToken, + byte* id, int idLen, char* label, int labelLen) +{ + int ret = 0; + CK_RV rv; + CK_ULONG count = 1; + CK_OBJECT_HANDLE handle; + + while (ret == 0 && count != 0) { + ret = Pkcs11FindKeyObject(session, keyClass, keyType, onToken, id, + idLen, label, labelLen, &handle, &count); + if (ret == 0 && count != 0) { + rv = session->func->C_DestroyObject(session->handle, handle); + PKCS11_RV("C_DestroyObject", rv); + if (rv != CKR_OK) + ret = WC_HW_E; + } + } + + return ret; +} +#endif + +/** + * Make a private or secret key held in a session object persistent on the + * token. + * + * @param [in] token Token holding the key. + * @param [in] type PKCS11_KEY_TYPE_RSA, _EC, _MLDSA, _MLKEM, _AES_GCM, + * _AES_CBC or _HMAC. + * @param [in] key Key with an id or label. + * @return BAD_FUNC_ARG when token or key is NULL, the key has neither id + * nor label, or an HMAC key has no hash type. + * @return BAD_STATE_E when the token already holds a key with the id or + * label, or the key already refers to a token object. + * @return NOT_COMPILED_IN when the key type is not supported. + * @return WC_HW_E when a PKCS#11 library call fails. + * @return 0 on success. + */ +int wc_Pkcs11PersistKey(Pkcs11Token* token, int type, void* key) +{ + int ret = 0; + Pkcs11Session session; + CK_OBJECT_HANDLE sessKey = NULL_PTR; + CK_OBJECT_HANDLE privKey = NULL_PTR; + CK_OBJECT_HANDLE pubKey = NULL_PTR; + CK_RV rv = CKR_OK; + + if (token == NULL || key == NULL) + ret = BAD_FUNC_ARG; + if (ret == 0) + ret = Pkcs11OpenSession(token, &session, 1); + if (ret == 0) { + session.tokenObjects = 1; + switch (type) { + #if !defined(NO_AES) && (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC)) + case PKCS11_KEY_TYPE_AES_GCM: + case PKCS11_KEY_TYPE_AES_CBC: { + Aes* aes = (Aes*)key; + + ret = Pkcs11PersistPrivKey(&session, &secretKeyClass, CKK_AES, + NULL, (byte*)aes->id, aes->idLen, + aes->label, aes->labelLen, + &sessKey, &privKey); + break; + } + #endif + #ifndef NO_HMAC + case PKCS11_KEY_TYPE_HMAC: { + Hmac* hmac = (Hmac*)key; + int mechType; + int keyType; + CK_KEY_TYPE otherType = CKK_GENERIC_SECRET; + CK_ULONG count = 0; + + if (hmac->macType == WC_HASH_TYPE_NONE) + ret = BAD_FUNC_ARG; + else + ret = Pkcs11HmacTypes(hmac->macType, &mechType, &keyType); + if (ret == 0) { + ret = Pkcs11FindKeyObject(&session, &secretKeyClass, + (CK_KEY_TYPE)keyType, CK_FALSE, (byte*)hmac->id, + hmac->idLen, hmac->label, hmac->labelLen, &sessKey, + &count); + } + /* wc_Pkcs11StoreKey falls back to a generic secret. */ + if (ret == 0 && count == 0) { + otherType = (CK_KEY_TYPE)keyType; + keyType = (int)CKK_GENERIC_SECRET; + } + if (ret == 0) { + ret = Pkcs11CheckNewTokenKey(&session, otherType, + (byte*)hmac->id, hmac->idLen, hmac->label, + hmac->labelLen); + } + if (ret == 0) { + ret = Pkcs11PersistPrivKey(&session, &secretKeyClass, + (CK_KEY_TYPE)keyType, NULL, (byte*)hmac->id, + hmac->idLen, hmac->label, hmac->labelLen, &sessKey, + &privKey); + } + break; + } + #endif + #if !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN) + case PKCS11_KEY_TYPE_RSA: { + RsaKey* rsaKey = (RsaKey*)key; + + ret = Pkcs11PersistPrivKey(&session, &privKeyClass, CKK_RSA, + rsaKey->devCtx, + rsaKey->id, rsaKey->idLen, + rsaKey->label, rsaKey->labelLen, + &sessKey, &privKey); + if (ret == 0 && !mp_iszero(&rsaKey->e)) { + ret = Pkcs11CreateRsaPublicKey(&pubKey, &session, + rsaKey); + } + if (ret == 0) + rsaKey->devCtx = (void*)(wc_ptr_t)privKey; + break; + } + #endif + #ifdef HAVE_ECC + case PKCS11_KEY_TYPE_EC: { + ecc_key* eccKey = (ecc_key*)key; + CK_BBOOL canSign = CK_TRUE; + CK_ATTRIBUTE signAttr; + + ret = Pkcs11PersistPrivKey(&session, &privKeyClass, CKK_EC, + eccKey->devCtx, + eccKey->id, eccKey->idLen, + eccKey->label, eccKey->labelLen, + &sessKey, &privKey); + if (ret == 0 && !mp_iszero(eccKey->pubkey.x) && + Pkcs11MechAvail(&session, CKM_ECDSA, NULL) == 0) { + ret = Pkcs11CreateEccPublicKey(&pubKey, &session, eccKey, + CKA_VERIFY); + if (ret != 0) { + signAttr.type = CKA_SIGN; + signAttr.pValue = &canSign; + signAttr.ulValueLen = sizeof(canSign); + rv = session.func->C_GetAttributeValue(session.handle, + privKey, &signAttr, 1); + PKCS11_RV("C_GetAttributeValue", rv); + if (rv == CKR_OK && canSign == CK_FALSE) + ret = 0; + } + } + else if (ret == 0 && mp_iszero(eccKey->pubkey.x)) { + ret = Pkcs11PersistPubKey(&session, CKK_EC, eccKey->id, + eccKey->idLen, eccKey->label, eccKey->labelLen, + &pubKey); + } + if (ret == 0) { + eccKey->devCtx = (void*)(wc_ptr_t)privKey; + (void)Pkcs11DestroyKeyObjects(&session, &pubKeyClass, + CKK_EC, CK_FALSE, eccKey->id, eccKey->idLen, + eccKey->label, eccKey->labelLen); + } + break; + } + #endif + #ifdef WOLFSSL_HAVE_MLDSA + case PKCS11_KEY_TYPE_MLDSA: { + wc_MlDsaKey* mldsaKey = (wc_MlDsaKey*)key; + CK_MECHANISM_INFO mechInfo; + + ret = Pkcs11PersistPrivKey(&session, &privKeyClass, + CKK_ML_DSA, mldsaKey->devCtx, + mldsaKey->id, mldsaKey->idLen, + mldsaKey->label, mldsaKey->labelLen, + &sessKey, &privKey); + if (ret == 0 && mldsaKey->pubKeySet) { + ret = Pkcs11MechAvail(&session, CKM_ML_DSA, &mechInfo); + if (ret == 0) { + ret = Pkcs11CreateMldsaPublicKey(&pubKey, &session, + mldsaKey, &mechInfo); + } + } + else if (ret == 0) { + ret = Pkcs11PersistPubKey(&session, CKK_ML_DSA, + mldsaKey->id, mldsaKey->idLen, mldsaKey->label, + mldsaKey->labelLen, &pubKey); + } + if (ret == 0) { + mldsaKey->devCtx = (void*)(wc_ptr_t)privKey; + (void)Pkcs11DestroyKeyObjects(&session, &pubKeyClass, + CKK_ML_DSA, CK_FALSE, mldsaKey->id, mldsaKey->idLen, + mldsaKey->label, mldsaKey->labelLen); + } + break; + } + #endif + #ifdef WOLFSSL_HAVE_MLKEM + case PKCS11_KEY_TYPE_MLKEM: { + MlKemKey* mlkemKey = (MlKemKey*)key; + CK_MECHANISM_INFO mechInfo; + + ret = Pkcs11PersistPrivKey(&session, &privKeyClass, + CKK_ML_KEM, mlkemKey->devCtx, + mlkemKey->id, mlkemKey->idLen, + mlkemKey->label, mlkemKey->labelLen, + &sessKey, &privKey); + if (ret == 0 && (mlkemKey->flags & MLKEM_FLAG_PUB_SET) != 0) { + ret = Pkcs11MechAvail(&session, CKM_ML_KEM, &mechInfo); + if (ret == 0) { + ret = Pkcs11CreateMlKemPublicKey(&pubKey, &session, + mlkemKey, &mechInfo); + } + } + else if (ret == 0) { + ret = Pkcs11PersistPubKey(&session, CKK_ML_KEM, + mlkemKey->id, mlkemKey->idLen, mlkemKey->label, + mlkemKey->labelLen, &pubKey); + } + if (ret == 0) { + mlkemKey->devCtx = (void*)(wc_ptr_t)privKey; + (void)Pkcs11DestroyKeyObjects(&session, &pubKeyClass, + CKK_ML_KEM, CK_FALSE, mlkemKey->id, mlkemKey->idLen, + mlkemKey->label, mlkemKey->labelLen); + } + break; + } + #endif + default: + ret = NOT_COMPILED_IN; + break; + } + + if (ret == 0) { + rv = session.func->C_DestroyObject(session.handle, sessKey); + PKCS11_RV("C_DestroyObject", rv); + } + else if (privKey != NULL_PTR) { + rv = session.func->C_DestroyObject(session.handle, privKey); + PKCS11_RV("C_DestroyObject", rv); + } + Pkcs11CloseSession(token, &session); + } + + (void)pubKey; + (void)rv; + + return ret; +} + /** * Perform a cryptographic operation using PKCS#11 device. * diff --git a/wolfssl/wolfcrypt/wc_pkcs11.h b/wolfssl/wolfcrypt/wc_pkcs11.h index 5e943b3c6ed..edd08639c02 100644 --- a/wolfssl/wolfcrypt/wc_pkcs11.h +++ b/wolfssl/wolfcrypt/wc_pkcs11.h @@ -111,6 +111,7 @@ WOLFSSL_API int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key); WOLFSSL_API int wc_Pkcs11StoreKey_ex(Pkcs11Token* token, int type, int clear, void* key, int persistent); +WOLFSSL_API int wc_Pkcs11PersistKey(Pkcs11Token* token, int type, void* key); WOLFSSL_API int wc_Pkcs11_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx); From 8a448e6ca658507508d1ffe5db35886346974f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Thu, 22 Oct 2026 13:09:34 +0200 Subject: [PATCH 3/4] Add wc_Pkcs11DeleteKey to remove a key from the token Freeing a key leaves its token objects alone, so an application that rotates a persistent key needs a way to remove the old one. wc_Pkcs11DeleteKey destroys every token object of the key type with the key's id and label, which covers the private key and the public key object wc_Pkcs11PersistKey or wc_Pkcs11StoreKey_ex created. Labels need not be unique, so an object must match every name the key has. For RSA, ECC, ML-DSA and ML-KEM keys a session object in devCtx is destroyed too and devCtx is cleared. If a C_DestroyObject fails part way, devCtx is cleared when its object is already gone, so the key is not left with a dead handle. A delete refused before destroying anything keeps devCtx. An HMAC key is removed under its HMAC key type and as a generic secret, and needs its hash type. Nothing to remove is not an error. Private keys that wolfSSL stores on the token carry CKA_PRIVATE, so a session without a user login does not find them, and a delete would remove only the public object and report success. The session state is checked with C_GetSessionInfo first, and BAD_STATE_E is returned unless the user is logged in. The session state values are added to pkcs11.h. The new tests also cover persisting and deleting RSA, ECC, ML-DSA, ML- KEM, AES and HMAC keys, the naming rules across the three functions, and failures of C_CopyObject, C_GetSessionInfo and C_DestroyObject. --- doc/dox_comments/header_files/pkcs11.h | 31 ++ tests/api/test_pkcs11.c | 570 ++++++++++++++++++++++++- tests/api/test_pkcs11.h | 14 +- wolfcrypt/src/wc_pkcs11.c | 204 +++++++++ wolfssl/wolfcrypt/pkcs11.h | 6 + wolfssl/wolfcrypt/wc_pkcs11.h | 1 + 6 files changed, 822 insertions(+), 4 deletions(-) diff --git a/doc/dox_comments/header_files/pkcs11.h b/doc/dox_comments/header_files/pkcs11.h index 115350579ba..6ed406a810b 100644 --- a/doc/dox_comments/header_files/pkcs11.h +++ b/doc/dox_comments/header_files/pkcs11.h @@ -154,6 +154,37 @@ int wc_Pkcs11StoreKey_ex(Pkcs11Token* token, int type, int clear, */ int wc_Pkcs11PersistKey(Pkcs11Token* token, int type, void* key); +/*! + \ingroup PKCS11 + + \brief Removes a key from the token: every token object of the key type + with the key's id and label is destroyed. The user must be logged in to + see private objects. For RSA, ECC, ML-DSA and ML-KEM keys a session + object in devCtx is destroyed as well and devCtx is cleared; when a + PKCS#11 call fails part way, devCtx is kept only while its object still + exists. An HMAC key is removed under its HMAC key type and as a generic + secret. Finding nothing to remove is not an error. A token key with only + the same id or only the same label is kept, and still keeps a new key + from using that id or label. + + \return 0 on success. + \return BAD_FUNC_ARG when token or key is NULL, the key has neither an + id nor a label, or an Hmac has no hash type. + \return BAD_STATE_E when the user is not logged in. + \return NOT_COMPILED_IN when the key type is not supported. + \return WC_HW_E when a PKCS#11 call fails. + + \param token Token holding the key. + \param type PKCS11_KEY_TYPE_RSA, PKCS11_KEY_TYPE_EC, PKCS11_KEY_TYPE_MLDSA, + PKCS11_KEY_TYPE_MLKEM, PKCS11_KEY_TYPE_AES_GCM, PKCS11_KEY_TYPE_AES_CBC + or PKCS11_KEY_TYPE_HMAC. + \param key RsaKey, ecc_key, wc_MlDsaKey, MlKemKey, Aes or Hmac with an id + or label. An Hmac needs its hash type (macType), as wc_HmacSetKey sets. + + \sa wc_Pkcs11PersistKey +*/ +int wc_Pkcs11DeleteKey(Pkcs11Token* token, int type, void* key); + /*! \ingroup PKCS11 */ diff --git a/tests/api/test_pkcs11.c b/tests/api/test_pkcs11.c index 67925ab9958..deb9cfedba8 100644 --- a/tests/api/test_pkcs11.c +++ b/tests/api/test_pkcs11.c @@ -279,6 +279,15 @@ int test_wc_Pkcs11_EcdsaSigDecode(void) defined(HAVE_ECC_KEY_EXPORT) && defined(HAVE_ECC_DHE) #define TEST_PKCS11_EC_KEYGEN #endif +#if defined(WOLFSSL_HAVE_MLDSA) && !defined(NO_PKCS11_MLDSA) && \ + !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && !defined(WOLFSSL_NO_ML_DSA_44) + #define TEST_PKCS11_MLDSA_44 +#endif +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(NO_PKCS11_MLKEM) && \ + !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && !defined(WOLFSSL_NO_ML_KEM) && \ + !defined(WOLFSSL_NO_ML_KEM_768) + #define TEST_PKCS11_MLKEM_768 +#endif #define TEST_PKCS11_MAX_OBJS 8 #define TEST_PKCS11_MAX_ID_LEN 32 @@ -321,6 +330,8 @@ static int test_pkcs11_no_hmac_type = 0; static CK_OBJECT_HANDLE test_pkcs11_sign_key = 0; static int test_pkcs11_fail_copy = 0; static int test_pkcs11_fail_destroy = 0; +static int test_pkcs11_fail_destroy_at = 0; +static int test_pkcs11_fail_session_info = 0; static byte test_pkcs11_pq_pub[2600]; static CK_ULONG test_pkcs11_pq_pub_len = 0; static CK_ULONG test_pkcs11_pq_param = 0; @@ -330,6 +341,7 @@ static int test_pkcs11_session_no_point = 0; static byte test_pkcs11_ec_point[2 + 65]; static TestPkcs11Obj test_pkcs11_last_priv; static const byte test_pkcs11_secret[32] = { 0x5a, 0x5a }; +static CK_STATE test_pkcs11_session_state = CKS_RW_USER_FUNCTIONS; #define TEST_PKCS11_FIND_CLASS 0x01 #define TEST_PKCS11_FIND_TYPE 0x02 @@ -341,6 +353,7 @@ static void test_pkcs11_reset(void) { XMEMSET(test_pkcs11_objs, 0, sizeof(test_pkcs11_objs)); test_pkcs11_destroy_calls = 0; + test_pkcs11_fail_destroy_at = 0; test_pkcs11_fail_pub_create = 0; test_pkcs11_fail_modulus = 0; test_pkcs11_fail_keygen = 0; @@ -482,7 +495,8 @@ static CK_RV test_pkcs11_store_destroy(CK_SESSION_HANDLE session, (void)session; test_pkcs11_destroy_calls++; - if (obj == NULL || test_pkcs11_fail_destroy) + if (obj == NULL || test_pkcs11_fail_destroy || + test_pkcs11_destroy_calls == test_pkcs11_fail_destroy_at) return TEST_CKR_GENERAL_ERROR; obj->used = 0; return CKR_OK; @@ -496,8 +510,11 @@ static CK_RV test_pkcs11_store_get_attr(CK_SESSION_HANDLE session, (void)session; - if (obj == NULL) + if (obj == NULL || (obj->isPrivate && + (test_pkcs11_session_state == CKS_RO_PUBLIC_SESSION || + test_pkcs11_session_state == CKS_RW_PUBLIC_SESSION))) { return TEST_CKR_GENERAL_ERROR; + } for (i = 0; i < cnt; i++) { const byte* val = NULL; CK_ULONG len = 0; @@ -653,6 +670,18 @@ static CK_RV test_pkcs11_store_mech_info(CK_SLOT_ID slotId, return CKR_OK; } +static CK_RV test_pkcs11_store_session_info(CK_SESSION_HANDLE session, + CK_SESSION_INFO_PTR info) +{ + (void)session; + + if (test_pkcs11_fail_session_info) + return TEST_CKR_GENERAL_ERROR; + XMEMSET(info, 0, sizeof(*info)); + info->state = test_pkcs11_session_state; + return CKR_OK; +} + static CK_RV test_pkcs11_store_sign_init(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) { @@ -763,6 +792,7 @@ static void test_pkcs11_store_setup(Pkcs11Token* token, CK_FUNCTION_LIST* func) func->C_GenerateKeyPair = test_pkcs11_store_keygen; func->C_DecryptInit = test_pkcs11_store_decrypt_init; func->C_Decrypt = test_pkcs11_store_decrypt; + func->C_GetSessionInfo = test_pkcs11_store_session_info; func->C_SignInit = test_pkcs11_store_sign_init; func->C_Sign = test_pkcs11_store_sign; func->C_DeriveKey = test_pkcs11_store_derive; @@ -773,9 +803,11 @@ static void test_pkcs11_store_setup(Pkcs11Token* token, CK_FUNCTION_LIST* func) test_pkcs11_no_ecdh = 0; test_pkcs11_no_ecdsa = 0; test_pkcs11_ecdsa_verify_only = 0; + test_pkcs11_session_state = CKS_RW_USER_FUNCTIONS; test_pkcs11_no_hmac_type = 0; test_pkcs11_fail_copy = 0; test_pkcs11_fail_destroy = 0; + test_pkcs11_fail_session_info = 0; test_pkcs11_reset(); } @@ -919,11 +951,11 @@ int test_wc_Pkcs11_PersistKey(void) CK_FUNCTION_LIST func; CK_OBJECT_HANDLE sessObj = 0; CK_OBJECT_HANDLE tokObj = 0; + TestPkcs11Obj* obj = NULL; ecc_key key; ecc_key noId; ecc_key ref; WC_RNG rng; - TestPkcs11Obj* obj = NULL; EXPECT_DECLS; test_pkcs11_store_setup(&token, &func); @@ -1062,6 +1094,93 @@ int test_wc_Pkcs11_PersistKey(void) return EXPECT_RESULT(); } +int test_wc_Pkcs11_DeleteKey(void) +{ + static const byte id[] = { 0x0d, 0x0e }; + static const byte otherId[] = { 0x0f }; + static CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY; + static CK_KEY_TYPE ecType = CKK_EC; + static CK_BBOOL isToken = CK_TRUE; + CK_ATTRIBUTE other[] = { + { CKA_CLASS, &privClass, sizeof(privClass) }, + { CKA_KEY_TYPE, &ecType, sizeof(ecType) }, + { CKA_ID, (void*)otherId, sizeof(otherId) }, + { CKA_TOKEN, &isToken, sizeof(isToken) } + }; + CK_ATTRIBUTE sess[] = { + { CKA_CLASS, &privClass, sizeof(privClass) }, + { CKA_KEY_TYPE, &ecType, sizeof(ecType) }, + { CKA_ID, (void*)id, sizeof(id) } + }; + Pkcs11Token token; + CK_FUNCTION_LIST func; + CK_OBJECT_HANDLE otherObj = 0; + CK_OBJECT_HANDLE sessObj = 0; + ecc_key key; + ecc_key noId; + WC_RNG rng; + EXPECT_DECLS; + + test_pkcs11_store_setup(&token, &func); + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&noId, 0, sizeof(noId)); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(test_pkcs11_make_ecc(&key, &rng, id, (int)sizeof(id)), 0); + ExpectIntEQ(wc_ecc_init(&noId), 0); + + ExpectIntEQ(wc_Pkcs11DeleteKey(NULL, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_EC, &noId), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + ExpectIntEQ(test_pkcs11_store_create(0, other, 4, &otherObj), CKR_OK); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 1), + 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 3); + ExpectIntEQ(test_pkcs11_store_create(0, sess, 3, &sessObj), CKR_OK); + key.devCtx = (void*)(wc_ptr_t)sessObj; + + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_EC, &key), 0); + ExpectNull(key.devCtx); + ExpectNull(test_pkcs11_obj(sessObj)); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + ExpectNotNull(test_pkcs11_obj(otherObj)); + + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_EC, &key), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + + /* Without a user login the private key is hidden from the search. */ + test_pkcs11_reset(); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 1), + 0); + key.devCtx = (void*)(wc_ptr_t)test_pkcs11_find_obj(CKO_PRIVATE_KEY, + CK_TRUE); + test_pkcs11_session_state = CKS_RW_PUBLIC_SESSION; + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectNotNull(key.devCtx); + test_pkcs11_session_state = CKS_RW_USER_FUNCTIONS; + + /* A delete that fails part way drops the object it destroyed. */ + test_pkcs11_reset(); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 1), + 0); + key.devCtx = (void*)(wc_ptr_t)test_pkcs11_find_obj(CKO_PRIVATE_KEY, + CK_TRUE); + test_pkcs11_fail_destroy_at = 2; + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(WC_HW_E)); + ExpectNull(key.devCtx); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + + wc_ecc_free(&key); + wc_ecc_free(&noId); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + + return EXPECT_RESULT(); +} + #if !defined(NO_RSA) && !defined(NO_PKCS11_RSA) && \ defined(HAVE_WOLF_BIGINT) && \ defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY) && \ @@ -1609,6 +1728,333 @@ int test_wc_Pkcs11_KeyGenSessionClosed(void) return EXPECT_RESULT(); } +int test_wc_Pkcs11_RsaPersistDelete(void) +{ + EXPECT_DECLS; +#ifdef TEST_PKCS11_RSA_KEYGEN + static unsigned char id[] = { 0x81 }; + Pkcs11Token token; + CK_FUNCTION_LIST func; + CK_OBJECT_HANDLE sessObj = 0; + RsaKey key; + WC_RNG rng; + + test_pkcs11_store_setup(&token, &func); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_PKCS11_DEVID, + wc_Pkcs11_CryptoDevCb, &token), 0); + ExpectIntEQ(wc_InitRsaKey_Id(&key, id, (int)sizeof(id), NULL, + TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MakeRsaKey(&key, 2048, WC_RSA_EXPONENT, &rng), 0); + if (EXPECT_SUCCESS()) + sessObj = (CK_OBJECT_HANDLE)(wc_ptr_t)key.devCtx; + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 1); + + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_RSA, &key), 0); + ExpectNull(test_pkcs11_obj(sessObj)); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntNE(test_pkcs11_find_obj(CKO_PUBLIC_KEY, CK_TRUE), 0); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectPtrEq(key.devCtx, + (void*)(wc_ptr_t)test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_TRUE)); + + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_RSA, &key), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + ExpectNull(key.devCtx); + ExpectIntEQ(wc_FreeRsaKey(&key), 0); + + wc_CryptoCb_UnRegisterDevice(TEST_PKCS11_DEVID); + DoExpectIntEQ(wc_FreeRng(&rng), 0); +#endif + return EXPECT_RESULT(); +} + +#if !defined(NO_AES) && defined(HAVE_AESGCM) && !defined(NO_PKCS11_AES) && \ + !defined(NO_PKCS11_AESGCM) + #define TEST_PKCS11_PERSIST_AES +#endif +#if !defined(NO_HMAC) && !defined(NO_SHA256) && !defined(NO_PKCS11_HMAC) + #define TEST_PKCS11_PERSIST_HMAC +#endif + +int test_wc_Pkcs11_SecretKeyPersist(void) +{ + EXPECT_DECLS; +#if defined(TEST_PKCS11_PERSIST_AES) || defined(TEST_PKCS11_PERSIST_HMAC) + static unsigned char id[] = { 0x71 }; + static const byte secret[16] = { 0x01 }; + Pkcs11Token token; + CK_FUNCTION_LIST func; +#ifdef TEST_PKCS11_PERSIST_AES + Aes aes; +#endif +#ifdef TEST_PKCS11_PERSIST_HMAC + Hmac hmac; +#endif + + test_pkcs11_store_setup(&token, &func); +#ifdef TEST_PKCS11_PERSIST_AES + ExpectIntEQ(wc_AesInit_Id(&aes, id, (int)sizeof(id), NULL, INVALID_DEVID), + 0); + if (EXPECT_SUCCESS()) { + aes.keylen = sizeof(secret); + XMEMCPY(aes.devKey, secret, sizeof(secret)); + } + ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_AES_GCM, 0, &aes), + 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 1); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_AES_GCM, &aes), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_AES_GCM, &aes), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + wc_AesFree(&aes); +#endif + +#ifdef TEST_PKCS11_PERSIST_HMAC + /* A token without the HMAC key type holds a generic secret. */ + test_pkcs11_reset(); + test_pkcs11_no_hmac_type = 1; + ExpectIntEQ(wc_HmacInit_Id(&hmac, id, (int)sizeof(id), NULL, + INVALID_DEVID), 0); + if (EXPECT_SUCCESS()) { + hmac.macType = WC_SHA256; + hmac.keyRaw = secret; + hmac.keyLen = sizeof(secret); + } + ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_HMAC, 0, &hmac), 0); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_HMAC, &hmac), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_HMAC, 0, &hmac, + 1), WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_HMAC, &hmac), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + test_pkcs11_no_hmac_type = 0; + wc_HmacFree(&hmac); +#endif +#endif + return EXPECT_RESULT(); +} + +int test_wc_Pkcs11_PqcPersistDelete(void) +{ + EXPECT_DECLS; +#if defined(TEST_PKCS11_MLDSA_44) || defined(TEST_PKCS11_MLKEM_768) + static const unsigned char id[] = { 0x91 }; + Pkcs11Token token; + CK_FUNCTION_LIST func; + WC_RNG rng; +#ifdef TEST_PKCS11_MLDSA_44 + wc_MlDsaKey dsa; + wc_MlDsaKey ref; +#endif +#ifdef TEST_PKCS11_MLKEM_768 + MlKemKey kem; +#endif + + test_pkcs11_store_setup(&token, &func); + ExpectIntEQ(wc_InitRng(&rng), 0); + +#ifdef TEST_PKCS11_MLDSA_44 + ExpectIntEQ(wc_MlDsaKey_InitId(&dsa, id, (int)sizeof(id), NULL, + INVALID_DEVID), 0); + ExpectIntEQ(wc_MlDsaKey_SetParams(&dsa, WC_ML_DSA_44), 0); + ExpectIntEQ(wc_MlDsaKey_MakeKey(&dsa, &rng), 0); + ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_MLDSA, 0, &dsa), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 2); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_MLDSA, &dsa), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectPtrEq(dsa.devCtx, + (void*)(wc_ptr_t)test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_TRUE)); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_MLDSA, &dsa), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + ExpectNull(dsa.devCtx); + + /* An import can go to the token directly. */ + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_MLDSA, 0, &dsa, + 1), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_MLDSA, &dsa), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + + /* A reference without the public key keeps the session public key. */ + ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_MLDSA, 0, &dsa), 0); + ExpectIntEQ(wc_MlDsaKey_InitId(&ref, id, (int)sizeof(id), NULL, + INVALID_DEVID), 0); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_MLDSA, &ref), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntNE(test_pkcs11_find_obj(CKO_PUBLIC_KEY, CK_TRUE), 0); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_MLDSA, &ref), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + wc_MlDsaKey_Free(&ref); + wc_MlDsaKey_Free(&dsa); +#endif + +#ifdef TEST_PKCS11_MLKEM_768 + test_pkcs11_reset(); + ExpectIntEQ(wc_MlKemKey_Init_Id(&kem, WC_ML_KEM_768, (unsigned char*)id, + (int)sizeof(id), NULL, INVALID_DEVID), 0); + ExpectIntEQ(wc_MlKemKey_MakeKey(&kem, &rng), 0); + ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_MLKEM, 0, &kem), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 2); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_MLKEM, &kem), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectNotNull(test_pkcs11_obj(test_pkcs11_find_obj(CKO_PUBLIC_KEY, + CK_TRUE))); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_MLKEM, &kem), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + ExpectNull(kem.devCtx); + + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_MLKEM, 0, &kem, + 1), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_MLKEM, &kem), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + wc_MlKemKey_Free(&kem); +#endif + + DoExpectIntEQ(wc_FreeRng(&rng), 0); +#endif + return EXPECT_RESULT(); +} + +int test_wc_Pkcs11_PersistPaths(void) +{ + static const byte id[] = { 0xa1 }; + static const byte secret[16] = { 0x02 }; + Pkcs11Token token; + CK_FUNCTION_LIST func; + CK_OBJECT_HANDLE sessObj = 0; + ecc_key key; + WC_RNG rng; +#if !defined(NO_AES) && defined(HAVE_AES_CBC) && !defined(NO_PKCS11_AES) && \ + !defined(NO_PKCS11_AESCBC) + Aes aes; +#endif +#if !defined(NO_HMAC) && !defined(NO_SHA256) && !defined(NO_PKCS11_HMAC) + Hmac hmac; + TestPkcs11Obj* secObj = NULL; +#endif +#ifdef TEST_PKCS11_RSA_KEYGEN + RsaKey rsa; +#endif + EXPECT_DECLS; + + test_pkcs11_store_setup(&token, &func); + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(test_pkcs11_make_ecc(&key, &rng, id, (int)sizeof(id)), 0); + + /* A failed copy leaves the session key and nothing on the token. */ + ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_EC, 0, &key), 0); + sessObj = test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_FALSE); + test_pkcs11_fail_copy = 1; + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(WC_HW_E)); + test_pkcs11_fail_copy = 0; + ExpectNotNull(test_pkcs11_obj(sessObj)); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + + /* Delete reports a session state or destroy it cannot complete. */ + test_pkcs11_reset(); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 1), + 0); + test_pkcs11_fail_session_info = 1; + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(WC_HW_E)); + test_pkcs11_fail_session_info = 0; + test_pkcs11_fail_destroy = 1; + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_EC, &key), + WC_NO_ERR_TRACE(WC_HW_E)); + test_pkcs11_fail_destroy = 0; + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + +#ifdef TEST_PKCS11_RSA_KEYGEN + test_pkcs11_reset(); + ExpectIntEQ(wc_InitRsaKey_Id(&rsa, (unsigned char*)id, (int)sizeof(id), + NULL, INVALID_DEVID), 0); + ExpectIntEQ(wc_MakeRsaKey(&rsa, 2048, WC_RSA_EXPONENT, &rng), 0); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_RSA, 0, &rsa, 1), + 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntNE(test_pkcs11_find_obj(CKO_PUBLIC_KEY, CK_TRUE), 0); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_RSA, 0, &rsa, 1), + WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectIntEQ(wc_FreeRsaKey(&rsa), 0); +#endif + +#if !defined(NO_AES) && defined(HAVE_AES_CBC) && !defined(NO_PKCS11_AES) && \ + !defined(NO_PKCS11_AESCBC) + test_pkcs11_reset(); + ExpectIntEQ(wc_AesInit_Id(&aes, (unsigned char*)id, (int)sizeof(id), NULL, + INVALID_DEVID), 0); + if (EXPECT_SUCCESS()) { + aes.keylen = sizeof(secret); + XMEMCPY(aes.devKey, secret, sizeof(secret)); + } + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_AES_CBC, 0, &aes, + 1), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_AES_CBC, &aes), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + wc_AesFree(&aes); +#endif + +#if !defined(NO_HMAC) && !defined(NO_SHA256) && !defined(NO_PKCS11_HMAC) + /* An HMAC key stored under its own key type. */ + test_pkcs11_reset(); + ExpectIntEQ(wc_HmacInit_Id(&hmac, (unsigned char*)id, (int)sizeof(id), + NULL, INVALID_DEVID), 0); + if (EXPECT_SUCCESS()) { + hmac.macType = WC_SHA256; + hmac.keyRaw = secret; + hmac.keyLen = sizeof(secret); + } + ExpectIntEQ(wc_Pkcs11StoreKey(&token, PKCS11_KEY_TYPE_HMAC, 0, &hmac), 0); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_HMAC, &hmac), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + ExpectNotNull(secObj = test_pkcs11_obj(test_pkcs11_find_obj( + CKO_SECRET_KEY, CK_TRUE))); + if (secObj != NULL) + ExpectIntEQ(secObj->keyType, CKK_SHA256_HMAC); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_HMAC, &hmac), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + wc_HmacFree(&hmac); + + /* A key named only by id has no hash type to find it by. */ + ExpectIntEQ(wc_HmacInit_Id(&hmac, (unsigned char*)id, (int)sizeof(id), + NULL, INVALID_DEVID), 0); + ExpectIntEQ(wc_Pkcs11PersistKey(&token, PKCS11_KEY_TYPE_HMAC, &hmac), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_HMAC, &hmac), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + wc_HmacFree(&hmac); +#endif + + wc_ecc_free(&key); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + (void)secret; + + return EXPECT_RESULT(); +} + int test_wc_Pkcs11_RsaKeyGenExponent(void) { EXPECT_DECLS; @@ -1726,4 +2172,122 @@ int test_wc_Pkcs11_RsaKeyGenError(void) return EXPECT_RESULT(); } +static int test_pkcs11_add_token_key(const char* label, byte idByte) +{ + static CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY; + static CK_KEY_TYPE ecType = CKK_EC; + static CK_BBOOL isToken = CK_TRUE; + byte id[1]; + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &privClass, sizeof(privClass) }, + { CKA_KEY_TYPE, &ecType, sizeof(ecType) }, + { CKA_TOKEN, &isToken, sizeof(isToken) }, + { CKA_ID, id, sizeof(id) }, + { CKA_LABEL, (void*)label, 0 } + }; + CK_OBJECT_HANDLE h = 0; + + id[0] = idByte; + tmpl[4].ulValueLen = (CK_ULONG)XSTRLEN(label); + if (test_pkcs11_store_create(0, tmpl, 5, &h) != CKR_OK) + return 0; + return (int)h; +} + +static void test_pkcs11_name_key(ecc_key* key, byte idByte, const char* label) +{ + key->id[0] = idByte; + key->idLen = 1; + key->labelLen = (int)XSTRLEN(label); + XMEMCPY(key->label, label, (size_t)key->labelLen); +} + +int test_wc_Pkcs11_KeyNames(void) +{ + static const byte id[] = { 0x31 }; + static byte certId[] = { 0x61 }; + static CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY; + static CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + static CK_KEY_TYPE ecType = CKK_EC; + static CK_BBOOL onToken = CK_TRUE; + CK_ATTRIBUTE sess[] = { + { CKA_CLASS, &privClass, sizeof(privClass) }, + { CKA_KEY_TYPE, &ecType, sizeof(ecType) } + }; + CK_ATTRIBUTE cert[] = { + { CKA_CLASS, &certClass, sizeof(certClass) }, + { CKA_TOKEN, &onToken, sizeof(onToken) }, + { CKA_ID, certId, sizeof(certId) } + }; + Pkcs11Token token; + CK_FUNCTION_LIST func; + CK_OBJECT_HANDLE sessObj = 0; + CK_OBJECT_HANDLE certObj = 0; + ecc_key key; + ecc_key noName; + WC_RNG rng; + int other = 0; + EXPECT_DECLS; + + test_pkcs11_store_setup(&token, &func); + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&noName, 0, sizeof(noName)); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(test_pkcs11_make_ecc(&key, &rng, id, (int)sizeof(id)), 0); + ExpectIntEQ(wc_ecc_init(&noName), 0); + ExpectIntEQ(wc_ecc_make_key(&rng, 32, &noName), 0); + + /* A persistent import needs a name that no token key has. */ + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &noName, + 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 1), + 0); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 1), + WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + + /* Either name in use makes a duplicate. */ + test_pkcs11_reset(); + ExpectIntNE(test_pkcs11_add_token_key("old", 0x41), 0); + test_pkcs11_name_key(&key, 0x41, "new"); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 1), + WC_NO_ERR_TRACE(BAD_STATE_E)); + test_pkcs11_name_key(&key, 0x42, "old"); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 1), + WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + + /* A certificate with the key's id does not block the key. */ + test_pkcs11_reset(); + ExpectIntEQ(test_pkcs11_store_create(0, cert, 3, &certObj), CKR_OK); + test_pkcs11_name_key(&key, 0x61, ""); + ExpectIntEQ(wc_Pkcs11StoreKey_ex(&token, PKCS11_KEY_TYPE_EC, 0, &key, 1), + 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 3); + + /* A delete only removes keys with both the id and the label. */ + test_pkcs11_reset(); + ExpectIntNE(test_pkcs11_add_token_key("shared", 0x51), 0); + ExpectIntNE(other = test_pkcs11_add_token_key("shared", 0x52), 0); + test_pkcs11_name_key(&key, 0x51, "shared"); + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_EC, &key), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 1); + ExpectNotNull(test_pkcs11_obj((CK_OBJECT_HANDLE)other)); + + /* A key without a name is rejected before its session key is touched. */ + ExpectIntEQ(test_pkcs11_store_create(0, sess, 2, &sessObj), CKR_OK); + noName.devCtx = (void*)(wc_ptr_t)sessObj; + ExpectIntEQ(wc_Pkcs11DeleteKey(&token, PKCS11_KEY_TYPE_EC, &noName), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectPtrEq(noName.devCtx, (void*)(wc_ptr_t)sessObj); + ExpectNotNull(test_pkcs11_obj(sessObj)); + noName.devCtx = NULL; + + wc_ecc_free(&key); + wc_ecc_free(&noName); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + + return EXPECT_RESULT(); +} + #endif diff --git a/tests/api/test_pkcs11.h b/tests/api/test_pkcs11.h index c85871977e1..1e2d0e72d47 100644 --- a/tests/api/test_pkcs11.h +++ b/tests/api/test_pkcs11.h @@ -32,24 +32,36 @@ int test_wc_Pkcs11_EcdsaSigDecode(void); int test_wc_Pkcs11_FreeKeepsTokenKey(void); int test_wc_Pkcs11_StoreKeyPersistent(void); int test_wc_Pkcs11_PersistKey(void); +int test_wc_Pkcs11_DeleteKey(void); int test_wc_Pkcs11_RsaIdNoLeak(void); int test_wc_Pkcs11_EcKeyUsage(void); int test_wc_Pkcs11_RsaKeyGenError(void); int test_wc_Pkcs11_RsaKeyGenExponent(void); +int test_wc_Pkcs11_KeyNames(void); int test_wc_Pkcs11_FreeNoObject(void); int test_wc_Pkcs11_KeyGenSessionClosed(void); +int test_wc_Pkcs11_SecretKeyPersist(void); +int test_wc_Pkcs11_RsaPersistDelete(void); +int test_wc_Pkcs11_PqcPersistDelete(void); +int test_wc_Pkcs11_PersistPaths(void); #define TEST_PKCS11_DECLS \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_EcdsaSigDecode), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_FreeKeepsTokenKey), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_StoreKeyPersistent), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_PersistKey), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_DeleteKey), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaIdNoLeak), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_EcKeyUsage), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaKeyGenError), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaKeyGenExponent), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_KeyNames), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_FreeNoObject), \ - TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_KeyGenSessionClosed) + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_KeyGenSessionClosed), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_SecretKeyPersist), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaPersistDelete), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_PqcPersistDelete), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_PersistPaths) #else diff --git a/wolfcrypt/src/wc_pkcs11.c b/wolfcrypt/src/wc_pkcs11.c index 7f30879c0ed..059f273c660 100644 --- a/wolfcrypt/src/wc_pkcs11.c +++ b/wolfcrypt/src/wc_pkcs11.c @@ -7294,6 +7294,210 @@ int wc_Pkcs11PersistKey(Pkcs11Token* token, int type, void* key) return ret; } +#if !defined(NO_RSA) || defined(HAVE_ECC) || defined(WOLFSSL_HAVE_MLDSA) || \ + defined(WOLFSSL_HAVE_MLKEM) || (!defined(NO_AES) && \ + (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || !defined(NO_HMAC) +/** + * Check that the user is logged in, so the session sees private objects. + * + * @param [in] session Session object. + * @return BAD_STATE_E when the user is not logged in. + * @return WC_HW_E when a PKCS#11 library call fails. + * @return 0 on success. + */ +static int Pkcs11CheckUserSession(Pkcs11Session* session) +{ + int ret = 0; + CK_RV rv; + CK_SESSION_INFO info; + + rv = session->func->C_GetSessionInfo(session->handle, &info); + PKCS11_RV("C_GetSessionInfo", rv); + if (rv != CKR_OK) + ret = WC_HW_E; + else if (info.state != CKS_RO_USER_FUNCTIONS && + info.state != CKS_RW_USER_FUNCTIONS) + ret = BAD_STATE_E; + + return ret; +} + +/** + * Destroy all token objects of a key type with the key's id and label. + * + * @param [in] session Session object. + * @param [in] keyType Key type. + * @param [in] id Identifier of key. + * @param [in] idLen Length of identifier. + * @param [in] label Label of key. + * @param [in] labelLen Length of label. + * @return BAD_FUNC_ARG when the key has neither id nor label. + * @return BAD_STATE_E when the user is not logged in. + * @return WC_HW_E when a PKCS#11 library call fails. + * @return 0 on success. + */ +static int Pkcs11DeleteTokenKey(Pkcs11Session* session, CK_KEY_TYPE keyType, + byte* id, int idLen, char* label, int labelLen) +{ + int ret = 0; + + if (idLen <= 0 && labelLen <= 0) + ret = BAD_FUNC_ARG; + if (ret == 0) + ret = Pkcs11CheckUserSession(session); + if (ret == 0) { + ret = Pkcs11DestroyKeyObjects(session, NULL, keyType, CK_TRUE, id, + idLen, label, labelLen); + } + + return ret; +} +#endif + +#if !defined(NO_RSA) || defined(HAVE_ECC) || defined(WOLFSSL_HAVE_MLDSA) || \ + defined(WOLFSSL_HAVE_MLKEM) +/** + * Release the key object a key refers to after deleting its token objects. + * + * @param [in] session Session object. + * @param [in, out] devCtx Device context of the key. + * @param [in] result Result of deleting the token objects. + */ +static void Pkcs11DeleteDevCtx(Pkcs11Session* session, void** devCtx, + int result) +{ + CK_OBJECT_HANDLE handle; + CK_BBOOL onToken; + + if (*devCtx != NULL) { + handle = (CK_OBJECT_HANDLE)(wc_ptr_t)*devCtx; + if (result == 0) { + Pkcs11DestroySessionKey(session, handle); + *devCtx = NULL; + } + else if (result == WC_NO_ERR_TRACE(WC_HW_E) && + Pkcs11KeyOnToken(session, handle, &onToken) != 0) { + *devCtx = NULL; + } + } +} +#endif + +/** + * Remove a key's token objects from the token. + * + * @param [in] token Token holding the key. + * @param [in] type PKCS11_KEY_TYPE_RSA, _EC, _MLDSA, _MLKEM, _AES_GCM, + * _AES_CBC or _HMAC. + * @param [in] key Key with an id or label. + * @return BAD_FUNC_ARG when token or key is NULL, the key has neither id + * nor label, or an HMAC key has no hash type. + * @return BAD_STATE_E when the user is not logged in. + * @return NOT_COMPILED_IN when the key type is not supported. + * @return WC_HW_E when a PKCS#11 library call fails. + * @return 0 on success. + */ +int wc_Pkcs11DeleteKey(Pkcs11Token* token, int type, void* key) +{ + int ret = 0; + Pkcs11Session session; + + if (token == NULL || key == NULL) + ret = BAD_FUNC_ARG; + if (ret == 0) + ret = Pkcs11OpenSession(token, &session, 1); + if (ret == 0) { + switch (type) { + #if !defined(NO_AES) && (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC)) + case PKCS11_KEY_TYPE_AES_GCM: + case PKCS11_KEY_TYPE_AES_CBC: { + Aes* aes = (Aes*)key; + + ret = Pkcs11DeleteTokenKey(&session, CKK_AES, (byte*)aes->id, + aes->idLen, aes->label, + aes->labelLen); + break; + } + #endif + #ifndef NO_HMAC + case PKCS11_KEY_TYPE_HMAC: { + Hmac* hmac = (Hmac*)key; + int mechType; + int keyType; + + if (hmac->macType == WC_HASH_TYPE_NONE) + ret = BAD_FUNC_ARG; + else + ret = Pkcs11HmacTypes(hmac->macType, &mechType, &keyType); + if (ret == 0) { + ret = Pkcs11DeleteTokenKey(&session, (CK_KEY_TYPE)keyType, + (byte*)hmac->id, hmac->idLen, + hmac->label, hmac->labelLen); + } + if (ret == 0) { + ret = Pkcs11DeleteTokenKey(&session, CKK_GENERIC_SECRET, + (byte*)hmac->id, hmac->idLen, + hmac->label, hmac->labelLen); + } + break; + } + #endif + #ifndef NO_RSA + case PKCS11_KEY_TYPE_RSA: { + RsaKey* rsaKey = (RsaKey*)key; + + ret = Pkcs11DeleteTokenKey(&session, CKK_RSA, rsaKey->id, + rsaKey->idLen, rsaKey->label, + rsaKey->labelLen); + Pkcs11DeleteDevCtx(&session, &rsaKey->devCtx, ret); + break; + } + #endif + #ifdef HAVE_ECC + case PKCS11_KEY_TYPE_EC: { + ecc_key* eccKey = (ecc_key*)key; + + ret = Pkcs11DeleteTokenKey(&session, CKK_EC, eccKey->id, + eccKey->idLen, eccKey->label, + eccKey->labelLen); + Pkcs11DeleteDevCtx(&session, &eccKey->devCtx, ret); + break; + } + #endif + #ifdef WOLFSSL_HAVE_MLDSA + case PKCS11_KEY_TYPE_MLDSA: { + wc_MlDsaKey* mldsaKey = (wc_MlDsaKey*)key; + + ret = Pkcs11DeleteTokenKey(&session, CKK_ML_DSA, + mldsaKey->id, mldsaKey->idLen, + mldsaKey->label, + mldsaKey->labelLen); + Pkcs11DeleteDevCtx(&session, &mldsaKey->devCtx, ret); + break; + } + #endif + #ifdef WOLFSSL_HAVE_MLKEM + case PKCS11_KEY_TYPE_MLKEM: { + MlKemKey* mlkemKey = (MlKemKey*)key; + + ret = Pkcs11DeleteTokenKey(&session, CKK_ML_KEM, + mlkemKey->id, mlkemKey->idLen, + mlkemKey->label, + mlkemKey->labelLen); + Pkcs11DeleteDevCtx(&session, &mlkemKey->devCtx, ret); + break; + } + #endif + default: + ret = NOT_COMPILED_IN; + break; + } + Pkcs11CloseSession(token, &session); + } + + return ret; +} + /** * Perform a cryptographic operation using PKCS#11 device. * diff --git a/wolfssl/wolfcrypt/pkcs11.h b/wolfssl/wolfcrypt/pkcs11.h index d77a5ccfc76..0fdde36b1b0 100644 --- a/wolfssl/wolfcrypt/pkcs11.h +++ b/wolfssl/wolfcrypt/pkcs11.h @@ -74,6 +74,12 @@ extern "C" { #define CKF_RW_SESSION 0x00000002UL #define CKF_SERIAL_SESSION 0x00000004UL +#define CKS_RO_PUBLIC_SESSION 0UL +#define CKS_RO_USER_FUNCTIONS 1UL +#define CKS_RW_PUBLIC_SESSION 2UL +#define CKS_RW_USER_FUNCTIONS 3UL +#define CKS_RW_SO_FUNCTIONS 4UL + #define CKO_CERTIFICATE 0x00000001UL #define CKO_PUBLIC_KEY 0x00000002UL #define CKO_PRIVATE_KEY 0x00000003UL diff --git a/wolfssl/wolfcrypt/wc_pkcs11.h b/wolfssl/wolfcrypt/wc_pkcs11.h index edd08639c02..0bba8a712ce 100644 --- a/wolfssl/wolfcrypt/wc_pkcs11.h +++ b/wolfssl/wolfcrypt/wc_pkcs11.h @@ -112,6 +112,7 @@ WOLFSSL_API int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, WOLFSSL_API int wc_Pkcs11StoreKey_ex(Pkcs11Token* token, int type, int clear, void* key, int persistent); WOLFSSL_API int wc_Pkcs11PersistKey(Pkcs11Token* token, int type, void* key); +WOLFSSL_API int wc_Pkcs11DeleteKey(Pkcs11Token* token, int type, void* key); WOLFSSL_API int wc_Pkcs11_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx); From 41fdead0f76814a8492f3da54a73b4bafb11551e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Thu, 22 Oct 2026 13:09:34 +0200 Subject: [PATCH 4/4] Add wc_Pkcs11Token_SetKeyGenPersistent for token object keygen wc_Pkcs11PersistKey makes a generated session key persistent after the fact, but some tokens cannot generate a usable session key at all. NXP's SMW module on the EdgeLock Enclave reports the public point only for token objects and returns an empty CKA_EC_POINT for session keys, and the ELE cannot import a plaintext key, so wolfSSL's ECC key generation failed there. wc_Pkcs11Token_SetKeyGenPersistent sets a keyGenOnToken bit in Pkcs11Token. While it is set, RSA, ECC, ML-DSA and ML-KEM key generation through the crypto callback opens a read/write session and creates a key that has an id or label as a token object, with the protecting attributes of wc_Pkcs11StoreKey_ex. A key without a name, such as a TLS ECDHE ephemeral key, stays a session object. The public key is kept as a token object with the key's names, as wc_Pkcs11PersistKey leaves it. Key generation fails with BAD_STATE_E when a token key of the type already has the id or label. A token key outlives a session that closes after key generation, so key generation without an open token session is allowed for it. The handle from the closed session is not kept, and the key is found later by its id or label. --- doc/dox_comments/header_files/pkcs11.h | 28 +++ tests/api/test_pkcs11.c | 311 ++++++++++++++++++++++++- tests/api/test_pkcs11.h | 6 +- wolfcrypt/src/wc_pkcs11.c | 198 +++++++++++++--- wolfssl/wolfcrypt/wc_pkcs11.h | 5 +- 5 files changed, 515 insertions(+), 33 deletions(-) diff --git a/doc/dox_comments/header_files/pkcs11.h b/doc/dox_comments/header_files/pkcs11.h index 6ed406a810b..ccc97ed5056 100644 --- a/doc/dox_comments/header_files/pkcs11.h +++ b/doc/dox_comments/header_files/pkcs11.h @@ -45,6 +45,34 @@ int wc_Pkcs11Token_Open(Pkcs11Token* token, int readWrite); */ void wc_Pkcs11Token_Close(Pkcs11Token* token); +/*! + \ingroup PKCS11 + + \brief Makes the crypto callback generate RSA, ECC, ML-DSA and ML-KEM + keys that have an id or label as token objects (CKA_TOKEN set). + + Use it for tokens that cannot make a session key persistent later with + wc_Pkcs11PersistKey. The public key is kept as a token object too. Key + generation fails with BAD_STATE_E when a token key of the type already + has the id or label. A key without a name, such as a TLS ephemeral key, + stays a session object, or is generated in software when no session + from wc_Pkcs11Token_Open is open. + + The private key gets CKA_PRIVATE, so the session must be logged in, and + a session from wc_Pkcs11Token_Open must be read/write. Without such a + session a token key is later found by its id or label. + + \return 0 on success. + \return BAD_FUNC_ARG when token is NULL. + + \param token Token used by the crypto callback. + \param persistent Generate token objects when non-zero. + + \sa wc_Pkcs11PersistKey + \sa wc_Pkcs11DeleteKey +*/ +int wc_Pkcs11Token_SetKeyGenPersistent(Pkcs11Token* token, int persistent); + /*! \ingroup PKCS11 diff --git a/tests/api/test_pkcs11.c b/tests/api/test_pkcs11.c index deb9cfedba8..00a644b5339 100644 --- a/tests/api/test_pkcs11.c +++ b/tests/api/test_pkcs11.c @@ -808,6 +808,9 @@ static void test_pkcs11_store_setup(Pkcs11Token* token, CK_FUNCTION_LIST* func) test_pkcs11_fail_copy = 0; test_pkcs11_fail_destroy = 0; test_pkcs11_fail_session_info = 0; + test_pkcs11_sign_key = 0; + test_pkcs11_pq_pub_len = 0; + test_pkcs11_pq_param = 0; test_pkcs11_reset(); } @@ -1638,6 +1641,11 @@ int test_wc_Pkcs11_KeyGenSessionClosed(void) ecc_key key; WC_RNG rng; word32 len = sizeof(test_pkcs11_ec_point) - 2; +#ifdef HAVE_ECC_SIGN + byte hash[32]; + byte sig[ECC_MAX_SIG_SIZE]; + word32 sigLen = sizeof(sig); +#endif #ifdef TEST_PKCS11_RSA_KEYGEN RsaKey rsa; wc_CryptoInfo info; @@ -1646,6 +1654,9 @@ int test_wc_Pkcs11_KeyGenSessionClosed(void) test_pkcs11_store_setup(&token, &func); func.C_OpenSession = test_pkcs11_open_session; func.C_CloseSession = test_pkcs11_close_session; +#ifdef HAVE_ECC_SIGN + XMEMSET(hash, 0x5a, sizeof(hash)); +#endif XMEMSET(&soft, 0, sizeof(soft)); ExpectIntEQ(wc_InitRng(&rng), 0); ExpectIntEQ(test_pkcs11_make_ecc(&soft, &rng, id, (int)sizeof(id)), 0); @@ -1711,8 +1722,29 @@ int test_wc_Pkcs11_KeyGenSessionClosed(void) #endif ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); - /* A session key made in the open session is kept. */ + /* A token key outlives the session and is then found by its id. */ test_pkcs11_reset(); + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 1), 0); + ExpectIntEQ(wc_ecc_init_id(&key, (unsigned char*)id, (int)sizeof(id), + NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_ecc_make_key_ex2(&rng, 32, &key, ECC_SECP256R1, + WC_ECC_FLAG_NONE), 0); + ExpectNull(key.devCtx); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); +#ifdef HAVE_ECC_SIGN + test_pkcs11_sign_key = 0; + ExpectIntEQ(wc_ecc_sign_hash(hash, sizeof(hash), sig, &sigLen, &rng, &key), + 0); +#ifndef WC_PKCS11_FIND_WITH_ID_ONLY + ExpectIntEQ(test_pkcs11_sign_key, + test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_TRUE)); +#endif +#endif + wc_ecc_free(&key); + + /* So does a session key made in the open session. */ + test_pkcs11_reset(); + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 0), 0); token.handle = 1; ExpectIntEQ(wc_ecc_init_id(&key, (unsigned char*)id, (int)sizeof(id), NULL, TEST_PKCS11_DEVID), 0); @@ -1932,6 +1964,159 @@ int test_wc_Pkcs11_PqcPersistDelete(void) return EXPECT_RESULT(); } +int test_wc_Pkcs11_PqcKeyGen(void) +{ + EXPECT_DECLS; +#if defined(TEST_PKCS11_MLDSA_44) || defined(TEST_PKCS11_MLKEM_768) + static const unsigned char id[] = { 0xb1 }; + Pkcs11Token token; + CK_FUNCTION_LIST func; + WC_RNG rng; +#ifdef TEST_PKCS11_MLDSA_44 + wc_MlDsaKey soft; + wc_MlDsaKey dsa; + word32 dsaLen = sizeof(test_pkcs11_pq_pub); +#endif +#ifdef TEST_PKCS11_MLKEM_768 + MlKemKey softKem; + MlKemKey kem; + word32 kemLen = 0; +#endif + + test_pkcs11_store_setup(&token, &func); + func.C_OpenSession = test_pkcs11_open_session; + func.C_CloseSession = test_pkcs11_close_session; + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_PKCS11_DEVID, + wc_Pkcs11_CryptoDevCb, &token), 0); + +#ifdef TEST_PKCS11_MLDSA_44 + ExpectIntEQ(wc_MlDsaKey_Init(&soft, NULL, INVALID_DEVID), 0); + ExpectIntEQ(wc_MlDsaKey_SetParams(&soft, WC_ML_DSA_44), 0); + ExpectIntEQ(wc_MlDsaKey_MakeKey(&soft, &rng), 0); + ExpectIntEQ(wc_MlDsaKey_ExportPubRaw(&soft, test_pkcs11_pq_pub, &dsaLen), + 0); + test_pkcs11_pq_pub_len = dsaLen; + test_pkcs11_pq_param = CKP_ML_DSA_44; + wc_MlDsaKey_Free(&soft); + + /* A named key becomes a token key with its public key. */ + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 1), 0); + ExpectIntEQ(wc_MlDsaKey_InitId(&dsa, id, (int)sizeof(id), NULL, + TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MlDsaKey_SetParams(&dsa, WC_ML_DSA_44), 0); + ExpectIntEQ(wc_MlDsaKey_MakeKey(&dsa, &rng), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectPtrEq(dsa.devCtx, + (void*)(wc_ptr_t)test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_TRUE)); + ExpectIntEQ(wc_MlDsaKey_MakeKey(&dsa, &rng), + WC_NO_ERR_TRACE(BAD_STATE_E)); + dsa.devCtx = NULL; + wc_MlDsaKey_Free(&dsa); + + /* A named session key in a session that closes is refused. */ + test_pkcs11_reset(); + token.handle = 0; + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 0), 0); + ExpectIntEQ(wc_MlDsaKey_InitId(&dsa, id, (int)sizeof(id), NULL, + TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MlDsaKey_SetParams(&dsa, WC_ML_DSA_44), 0); + ExpectIntEQ(wc_MlDsaKey_MakeKey(&dsa, &rng), + WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectNull(dsa.devCtx); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + wc_MlDsaKey_Free(&dsa); + + /* A named token key in a session that closes stays on the token. */ + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 1), 0); + ExpectIntEQ(wc_MlDsaKey_InitId(&dsa, id, (int)sizeof(id), NULL, + TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MlDsaKey_SetParams(&dsa, WC_ML_DSA_44), 0); + ExpectIntEQ(wc_MlDsaKey_MakeKey(&dsa, &rng), 0); + ExpectNull(dsa.devCtx); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + wc_MlDsaKey_Free(&dsa); + test_pkcs11_reset(); + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 0), 0); + + /* A key without a name is generated in software. */ + ExpectIntEQ(wc_MlDsaKey_Init(&dsa, NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MlDsaKey_SetParams(&dsa, WC_ML_DSA_44), 0); + ExpectIntEQ(wc_MlDsaKey_MakeKey(&dsa, &rng), 0); + ExpectNull(dsa.devCtx); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + wc_MlDsaKey_Free(&dsa); + token.handle = 1; +#endif + +#ifdef TEST_PKCS11_MLKEM_768 + test_pkcs11_reset(); + ExpectIntEQ(wc_MlKemKey_Init(&softKem, WC_ML_KEM_768, NULL, + INVALID_DEVID), 0); + ExpectIntEQ(wc_MlKemKey_MakeKey(&softKem, &rng), 0); + ExpectIntEQ(wc_MlKemKey_PublicKeySize(&softKem, &kemLen), 0); + ExpectIntEQ(wc_MlKemKey_EncodePublicKey(&softKem, test_pkcs11_pq_pub, + kemLen), 0); + test_pkcs11_pq_pub_len = kemLen; + test_pkcs11_pq_param = CKP_ML_KEM_768; + wc_MlKemKey_Free(&softKem); + + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 1), 0); + ExpectIntEQ(wc_MlKemKey_Init_Id(&kem, WC_ML_KEM_768, (unsigned char*)id, + (int)sizeof(id), NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MlKemKey_MakeKey(&kem, &rng), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectPtrEq(kem.devCtx, + (void*)(wc_ptr_t)test_pkcs11_find_obj(CKO_PRIVATE_KEY, CK_TRUE)); + ExpectIntEQ(wc_MlKemKey_MakeKey(&kem, &rng), + WC_NO_ERR_TRACE(BAD_STATE_E)); + kem.devCtx = NULL; + wc_MlKemKey_Free(&kem); + + test_pkcs11_reset(); + token.handle = 0; + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 0), 0); + ExpectIntEQ(wc_MlKemKey_Init_Id(&kem, WC_ML_KEM_768, (unsigned char*)id, + (int)sizeof(id), NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MlKemKey_MakeKey(&kem, &rng), + WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectNull(kem.devCtx); + ExpectIntEQ(kem.flags & MLKEM_FLAG_PRIV_SET, 0); + wc_MlKemKey_Free(&kem); + + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 1), 0); + ExpectIntEQ(wc_MlKemKey_Init_Id(&kem, WC_ML_KEM_768, (unsigned char*)id, + (int)sizeof(id), NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MlKemKey_MakeKey(&kem, &rng), 0); + ExpectNull(kem.devCtx); + ExpectIntNE(kem.flags & MLKEM_FLAG_PRIV_SET, 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + wc_MlKemKey_Free(&kem); + test_pkcs11_reset(); + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 0), 0); + + ExpectIntEQ(wc_MlKemKey_Init(&kem, WC_ML_KEM_768, NULL, + TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MlKemKey_MakeKey(&kem, &rng), 0); + ExpectNull(kem.devCtx); + ExpectIntNE(kem.flags & MLKEM_FLAG_PRIV_SET, 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + wc_MlKemKey_Free(&kem); + token.handle = 1; +#endif + + wc_CryptoCb_UnRegisterDevice(TEST_PKCS11_DEVID); + DoExpectIntEQ(wc_FreeRng(&rng), 0); +#endif + return EXPECT_RESULT(); +} + int test_wc_Pkcs11_PersistPaths(void) { static const byte id[] = { 0xa1 }; @@ -2172,6 +2357,130 @@ int test_wc_Pkcs11_RsaKeyGenError(void) return EXPECT_RESULT(); } +int test_wc_Pkcs11_KeyGenPersistent(void) +{ + EXPECT_DECLS; +#ifdef TEST_PKCS11_EC_KEYGEN + static const byte id[] = { 0x21, 0x22 }; + Pkcs11Token token; + CK_FUNCTION_LIST func; + ecc_key soft; + ecc_key key; + WC_RNG rng; + word32 len = sizeof(test_pkcs11_ec_point) - 2; + TestPkcs11Obj* priv = NULL; + TestPkcs11Obj* pub = NULL; +#ifdef TEST_PKCS11_RSA_KEYGEN + RsaKey rsa; +#endif + + test_pkcs11_store_setup(&token, &func); + XMEMSET(&soft, 0, sizeof(soft)); + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(test_pkcs11_make_ecc(&soft, &rng, id, (int)sizeof(id)), 0); + test_pkcs11_ec_point[0] = ASN_OCTET_STRING; + test_pkcs11_ec_point[1] = (byte)len; + ExpectIntEQ(wc_ecc_export_x963(&soft, test_pkcs11_ec_point + 2, &len), 0); + ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_PKCS11_DEVID, + wc_Pkcs11_CryptoDevCb, &token), 0); + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(NULL, 1), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* A token that only reports the public point of token objects. */ + test_pkcs11_session_no_point = 1; + ExpectIntEQ(wc_ecc_init_id(&key, (unsigned char*)id, (int)sizeof(id), + NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_ecc_make_key_ex2(&rng, 32, &key, ECC_SECP256R1, + WC_ECC_FLAG_DEC_SIGN), WC_NO_ERR_TRACE(WC_HW_E)); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 1), 0); + ExpectIntEQ(wc_ecc_make_key_ex2(&rng, 32, &key, ECC_SECP256R1, + WC_ECC_FLAG_DEC_SIGN), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectNotNull(priv = test_pkcs11_obj((CK_OBJECT_HANDLE)(wc_ptr_t) + key.devCtx)); + if (priv != NULL) { + ExpectIntEQ(priv->cls, CKO_PRIVATE_KEY); + ExpectIntEQ(priv->onToken, CK_TRUE); + ExpectIntEQ(priv->idLen, sizeof(id)); + } + ExpectNotNull(pub = test_pkcs11_obj(test_pkcs11_find_obj(CKO_PUBLIC_KEY, + CK_TRUE))); + if (pub != NULL) + ExpectIntEQ(pub->idLen, sizeof(id)); + wc_ecc_free(&key); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + + /* The id is taken now. */ + ExpectIntEQ(wc_ecc_init_id(&key, (unsigned char*)id, (int)sizeof(id), + NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_ecc_make_key_ex2(&rng, 32, &key, ECC_SECP256R1, + WC_ECC_FLAG_DEC_SIGN), WC_NO_ERR_TRACE(BAD_STATE_E)); + wc_ecc_free(&key); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + + /* A key without a name, like a TLS ephemeral key, is a session key. */ + test_pkcs11_session_no_point = 0; + ExpectIntEQ(wc_ecc_init_ex(&key, NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_ecc_make_key_ex2(&rng, 32, &key, ECC_SECP256R1, + WC_ECC_FLAG_DEC_SIGN), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 1); + wc_ecc_free(&key); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + +#ifdef TEST_PKCS11_RSA_KEYGEN + test_pkcs11_reset(); + ExpectIntEQ(wc_InitRsaKey_Id(&rsa, (unsigned char*)id, (int)sizeof(id), + NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MakeRsaKey(&rsa, 2048, WC_RSA_EXPONENT, &rng), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectNotNull(pub = test_pkcs11_obj(test_pkcs11_find_obj(CKO_PUBLIC_KEY, + CK_TRUE))); + if (pub != NULL) + ExpectIntEQ(pub->idLen, sizeof(id)); + ExpectIntEQ(test_pkcs11_token_keys_protected(), 1); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(wc_FreeRsaKey(&rsa), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + + /* A token key outlives a session that closes. */ + test_pkcs11_reset(); + func.C_OpenSession = test_pkcs11_open_session; + func.C_CloseSession = test_pkcs11_close_session; + token.handle = 0; + ExpectIntEQ(wc_InitRsaKey_Id(&rsa, (unsigned char*)id, (int)sizeof(id), + NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_MakeRsaKey(&rsa, 2048, WC_RSA_EXPONENT, &rng), 0); + ExpectNull(rsa.devCtx); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 2); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 0); + ExpectIntEQ(wc_FreeRsaKey(&rsa), 0); + token.handle = 1; +#endif + + test_pkcs11_reset(); + test_pkcs11_session_no_point = 0; + ExpectIntEQ(wc_Pkcs11Token_SetKeyGenPersistent(&token, 0), 0); + ExpectIntEQ(wc_ecc_init_id(&key, (unsigned char*)id, (int)sizeof(id), + NULL, TEST_PKCS11_DEVID), 0); + ExpectIntEQ(wc_ecc_make_key_ex2(&rng, 32, &key, ECC_SECP256R1, + WC_ECC_FLAG_DEC_SIGN), 0); + ExpectIntEQ(test_pkcs11_count(CK_TRUE), 0); + ExpectIntEQ(test_pkcs11_count(CK_FALSE), 1); + wc_ecc_free(&key); + + wc_CryptoCb_UnRegisterDevice(TEST_PKCS11_DEVID); + wc_ecc_free(&soft); + DoExpectIntEQ(wc_FreeRng(&rng), 0); +#endif + return EXPECT_RESULT(); +} + static int test_pkcs11_add_token_key(const char* label, byte idByte) { static CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY; diff --git a/tests/api/test_pkcs11.h b/tests/api/test_pkcs11.h index 1e2d0e72d47..ba1ea743766 100644 --- a/tests/api/test_pkcs11.h +++ b/tests/api/test_pkcs11.h @@ -37,6 +37,7 @@ int test_wc_Pkcs11_RsaIdNoLeak(void); int test_wc_Pkcs11_EcKeyUsage(void); int test_wc_Pkcs11_RsaKeyGenError(void); int test_wc_Pkcs11_RsaKeyGenExponent(void); +int test_wc_Pkcs11_KeyGenPersistent(void); int test_wc_Pkcs11_KeyNames(void); int test_wc_Pkcs11_FreeNoObject(void); int test_wc_Pkcs11_KeyGenSessionClosed(void); @@ -44,6 +45,7 @@ int test_wc_Pkcs11_SecretKeyPersist(void); int test_wc_Pkcs11_RsaPersistDelete(void); int test_wc_Pkcs11_PqcPersistDelete(void); int test_wc_Pkcs11_PersistPaths(void); +int test_wc_Pkcs11_PqcKeyGen(void); #define TEST_PKCS11_DECLS \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_EcdsaSigDecode), \ @@ -55,13 +57,15 @@ int test_wc_Pkcs11_PersistPaths(void); TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_EcKeyUsage), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaKeyGenError), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaKeyGenExponent), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_KeyGenPersistent), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_KeyNames), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_FreeNoObject), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_KeyGenSessionClosed), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_SecretKeyPersist), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaPersistDelete), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_PqcPersistDelete), \ - TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_PersistPaths) + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_PersistPaths), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_PqcKeyGen) #else diff --git a/wolfcrypt/src/wc_pkcs11.c b/wolfcrypt/src/wc_pkcs11.c index 059f273c660..f4c2ff186e8 100644 --- a/wolfcrypt/src/wc_pkcs11.c +++ b/wolfcrypt/src/wc_pkcs11.c @@ -986,6 +986,7 @@ static int Pkcs11Token_Init(Pkcs11Token* token, Pkcs11Dev* dev, int slotId, token->userPin = NULL_PTR; token->userPinSz = 0; token->userPinLogin = 0; + token->keyGenOnToken = 0; token->version = dev->version; } @@ -1199,14 +1200,14 @@ static void Pkcs11CloseSession(Pkcs11Token* token, Pkcs11Session* session) * @return BAD_STATE_E when a key with an id or label would be destroyed * with a session that closes after this operation. * @return CRYPTOCB_UNAVAILABLE when such a key has no id or label. - * @return 0 when the session stays open. + * @return 0 when the session stays open or the key is a token object. */ static int Pkcs11KeyGenSessionCheck(Pkcs11Session* session, int idLen, int labelLen) { int ret = 0; - if (session->closes) + if (session->closes && !session->tokenObjects) ret = (idLen > 0 || labelLen > 0) ? BAD_STATE_E : CRYPTOCB_UNAVAILABLE; return ret; @@ -1256,6 +1257,23 @@ void wc_Pkcs11Token_Close(Pkcs11Token* token) } } +/** + * Set whether RSA, ECC, ML-DSA and ML-KEM key generation creates token objects. + * + * @param [in] token Token object. + * @param [in] persistent Generate token objects when non-zero. + * @return BAD_FUNC_ARG when token is NULL. + * @return 0 on success. + */ +int wc_Pkcs11Token_SetKeyGenPersistent(Pkcs11Token* token, int persistent) +{ + if (token == NULL) + return BAD_FUNC_ARG; + + token->keyGenOnToken = (persistent != 0); + return 0; +} + #if !defined(NO_RSA) || defined(HAVE_ECC) || defined(WOLFSSL_HAVE_MLDSA) || \ defined(WOLFSSL_HAVE_MLKEM) || (!defined(NO_AES) && \ (defined(HAVE_AESGCM) || defined(HAVE_AES_CBC))) || !defined(NO_HMAC) @@ -3637,27 +3655,43 @@ static int Pkcs11RsaKeyGen(Pkcs11Session* session, wc_CryptoInfo* info) CK_OBJECT_HANDLE pubKey = NULL_PTR, privKey = NULL_PTR; CK_MECHANISM mech; CK_BYTE pubExp[sizeof(long)]; + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE pubKeyTmpl[] = { { CKA_MODULUS_BITS, &bits, sizeof(bits) }, { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, - { CKA_PUBLIC_EXPONENT, pubExp, sizeof(pubExp) } + { CKA_PUBLIC_EXPONENT, pubExp, sizeof(pubExp) }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 } }; - CK_ULONG pubTmplCnt = sizeof(pubKeyTmpl)/sizeof(*pubKeyTmpl); - /* Empty entries for optional label/ID. */ + /* 4 mandatory entries + 4 optional. */ + CK_ULONG pubTmplCnt = 4; + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE privKeyTmpl[] = { { CKA_DECRYPT, &ckTrue, sizeof(ckTrue) }, { CKA_SIGN, &ckTrue, sizeof(ckTrue) }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; - /* 2 mandatory entries + 2 optional. */ + /* 2 mandatory entries + 6 optional. */ int privTmplCnt = 2; int i; + if (key->idLen == 0 && key->labelLen == 0) + session->tokenObjects = 0; ret = Pkcs11MechAvail(session, CKM_RSA_PKCS_KEY_PAIR_GEN, NULL); if (ret == 0) ret = Pkcs11KeyGenSessionCheck(session, key->idLen, key->labelLen); + if (ret == 0 && session->tokenObjects) { + ret = Pkcs11CheckNewTokenKey(session, CKK_RSA, key->id, key->idLen, + key->label, key->labelLen); + } if (ret == 0) { WOLFSSL_MSG("PKCS#11: RSA Key Generation Operation"); @@ -3684,6 +3718,26 @@ static int Pkcs11RsaKeyGen(Pkcs11Session* session, wc_CryptoInfo* info) privKeyTmpl[privTmplCnt].ulValueLen = key->idLen; privTmplCnt++; } + if (session->tokenObjects) { + XMEMCPY(&privKeyTmpl[privTmplCnt], pkcs11TokenKeyAttrs, + sizeof(pkcs11TokenKeyAttrs)); + privTmplCnt += PKCS11_TOKEN_KEY_ATTRS; + if (key->labelLen != 0) { + pubKeyTmpl[pubTmplCnt].type = CKA_LABEL; + pubKeyTmpl[pubTmplCnt].pValue = key->label; + pubKeyTmpl[pubTmplCnt].ulValueLen = key->labelLen; + pubTmplCnt++; + } + if (key->idLen != 0) { + pubKeyTmpl[pubTmplCnt].type = CKA_ID; + pubKeyTmpl[pubTmplCnt].pValue = key->id; + pubKeyTmpl[pubTmplCnt].ulValueLen = key->idLen; + pubTmplCnt++; + } + XMEMCPY(&pubKeyTmpl[pubTmplCnt], pkcs11TokenPubKeyAttrs, + sizeof(pkcs11TokenPubKeyAttrs)); + pubTmplCnt += PKCS11_TOKEN_PUB_KEY_ATTRS; + } mech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; mech.ulParameterLen = 0; @@ -3704,9 +3758,9 @@ static int Pkcs11RsaKeyGen(Pkcs11Session* session, wc_CryptoInfo* info) if (ret == 0) ret = Pkcs11GetRsaPublicKey(key, session, pubKey); - if (pubKey != NULL_PTR) + if (pubKey != NULL_PTR && (ret != 0 || !session->tokenObjects)) session->func->C_DestroyObject(session->handle, pubKey); - if (ret == 0 && privKey != NULL_PTR) + if (ret == 0 && privKey != NULL_PTR && !session->closes) key->devCtx = (void*)(wc_ptr_t)privKey; else if (ret != 0 && privKey != NULL_PTR) session->func->C_DestroyObject(session->handle, privKey); @@ -3915,23 +3969,35 @@ static int Pkcs11EcKeyGen(Pkcs11Session* session, wc_CryptoInfo* info) CK_OBJECT_HANDLE pubKey = NULL_PTR, privKey = NULL_PTR; CK_MECHANISM mech; CK_UTF8CHAR params[ECC_MAX_OID_LEN]; + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE pubKeyTmpl[] = { { CKA_EC_PARAMS, params, 0 }, { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, }; int pubTmplCnt = 1; - /* Empty entries for optional label/ID. */ + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE privKeyAttrs[] = { { CKA_DECRYPT, &ckTrue, sizeof(ckTrue) }, { CKA_SIGN, &ckTrue, sizeof(ckTrue) }, { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, { 0, NULL, 0 }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, }; CK_ATTRIBUTE* privKeyTmpl = privKeyAttrs; int privTmplCnt = 0; int usage = 0; + /* An unnamed key, such as a TLS ephemeral key, stays a session object. */ + if (key->idLen == 0 && key->labelLen == 0) + session->tokenObjects = 0; ret = Pkcs11MechAvail(session, CKM_EC_KEY_PAIR_GEN, NULL); if (ret == 0) ret = Pkcs11KeyGenSessionCheck(session, key->idLen, key->labelLen); @@ -3940,6 +4006,10 @@ static int Pkcs11EcKeyGen(Pkcs11Session* session, wc_CryptoInfo* info) ret = Pkcs11EccSetParams(key, pubKeyTmpl, 0); } + if (ret == 0 && session->tokenObjects) { + ret = Pkcs11CheckNewTokenKey(session, CKK_EC, key->id, key->idLen, + key->label, key->labelLen); + } if (ret == 0) { ret = Pkcs11EccKeyUsage(session, key, &usage); if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN) && @@ -3974,6 +4044,26 @@ static int Pkcs11EcKeyGen(Pkcs11Session* session, wc_CryptoInfo* info) privKeyTmpl[privTmplCnt].ulValueLen = key->idLen; privTmplCnt++; } + if (session->tokenObjects) { + XMEMCPY(&privKeyTmpl[privTmplCnt], pkcs11TokenKeyAttrs, + sizeof(pkcs11TokenKeyAttrs)); + privTmplCnt += PKCS11_TOKEN_KEY_ATTRS; + if (key->labelLen != 0) { + pubKeyTmpl[pubTmplCnt].type = CKA_LABEL; + pubKeyTmpl[pubTmplCnt].pValue = key->label; + pubKeyTmpl[pubTmplCnt].ulValueLen = key->labelLen; + pubTmplCnt++; + } + if (key->idLen != 0) { + pubKeyTmpl[pubTmplCnt].type = CKA_ID; + pubKeyTmpl[pubTmplCnt].pValue = key->id; + pubKeyTmpl[pubTmplCnt].ulValueLen = key->idLen; + pubTmplCnt++; + } + XMEMCPY(&pubKeyTmpl[pubTmplCnt], pkcs11TokenPubKeyAttrs, + sizeof(pkcs11TokenPubKeyAttrs)); + pubTmplCnt += PKCS11_TOKEN_PUB_KEY_ATTRS; + } mech.mechanism = CKM_EC_KEY_PAIR_GEN; mech.ulParameterLen = 0; @@ -3995,9 +4085,9 @@ static int Pkcs11EcKeyGen(Pkcs11Session* session, wc_CryptoInfo* info) if (ret == 0) ret = Pkcs11GetEccPublicKey(key, session, pubKey); - if (pubKey != NULL_PTR) + if (pubKey != NULL_PTR && (ret != 0 || !session->tokenObjects)) session->func->C_DestroyObject(session->handle, pubKey); - if (ret == 0 && privKey != NULL_PTR) { + if (ret == 0 && privKey != NULL_PTR && !session->closes) { key->devCtx = (void*)(wc_ptr_t)privKey; } else if (ret != 0 && privKey != NULL_PTR) { @@ -4985,23 +5075,35 @@ static int Pkcs11MlKemKeyGen(Pkcs11Session* session, MlKemKey* key) { CKA_KEY_TYPE, &mlkemKeyType, sizeof(mlkemKeyType) }, { CKA_PARAMETER_SET, ¶m_set, sizeof(param_set) }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; CK_ULONG pubTmplCnt = - sizeof(pubKeyTmpl) / sizeof(*pubKeyTmpl) - 2; + sizeof(pubKeyTmpl) / sizeof(*pubKeyTmpl) - 4; CK_ATTRIBUTE privKeyTmpl[] = { { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, { CKA_DECAPSULATE, &ckTrue, sizeof(ckTrue) }, { CKA_KEY_TYPE, &mlkemKeyType, sizeof(mlkemKeyType) }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; CK_ULONG privTmplCnt = - sizeof(privKeyTmpl) / sizeof(*privKeyTmpl) - 2; + sizeof(privKeyTmpl) / sizeof(*privKeyTmpl) - 6; + if (key->idLen == 0 && key->labelLen == 0) + session->tokenObjects = 0; ret = Pkcs11MechAvail(session, CKM_ML_KEM_KEY_PAIR_GEN, &mechInfo); if (ret == 0) ret = Pkcs11KeyGenSessionCheck(session, key->idLen, key->labelLen); + if (ret == 0 && session->tokenObjects) { + ret = Pkcs11CheckNewTokenKey(session, CKK_ML_KEM, key->id, key->idLen, + key->label, key->labelLen); + } if (ret == 0) { switch (key->type) { #ifndef WOLFSSL_NO_ML_KEM @@ -5057,6 +5159,15 @@ static int Pkcs11MlKemKeyGen(Pkcs11Session* session, MlKemKey* key) pubKeyTmpl[pubTmplCnt].ulValueLen = key->idLen; pubTmplCnt++; } + if (session->tokenObjects) { + XMEMCPY(&privKeyTmpl[privTmplCnt], pkcs11TokenKeyAttrs, + sizeof(pkcs11TokenKeyAttrs)); + privTmplCnt += PKCS11_TOKEN_KEY_ATTRS; + + XMEMCPY(&pubKeyTmpl[pubTmplCnt], pkcs11TokenPubKeyAttrs, + sizeof(pkcs11TokenPubKeyAttrs)); + pubTmplCnt += PKCS11_TOKEN_PUB_KEY_ATTRS; + } mech.mechanism = CKM_ML_KEM_KEY_PAIR_GEN; mech.ulParameterLen = 0; @@ -5077,14 +5188,13 @@ static int Pkcs11MlKemKeyGen(Pkcs11Session* session, MlKemKey* key) ret = Pkcs11GetMlKemPublicKey(key, session, pubKey); } - /* Destroy the public key object, as encapsulation using the public key will - * probably be performed outside of the PKCS#11 token. If not, user can call - * wc_Pkcs11StoreKey() to import the public key to the token. */ - if (pubKey != NULL_PTR) { + /* Encapsulation usually runs in software, so a session public key goes. */ + if (pubKey != NULL_PTR && (ret != 0 || !session->tokenObjects)) { session->func->C_DestroyObject(session->handle, pubKey); } if (ret == 0 && privKey != NULL_PTR) { - key->devCtx = (void*)(wc_ptr_t)privKey; + if (!session->closes) + key->devCtx = (void*)(wc_ptr_t)privKey; key->flags |= MLKEM_FLAG_PRIV_SET; } else if (ret != 0 && privKey != NULL_PTR) { @@ -5551,29 +5661,37 @@ static int Pkcs11MldsaKeyGen(Pkcs11Session* session, wc_MlDsaKey* key) CK_MECHANISM_INFO mechInfo; CK_ML_DSA_PARAMETER_SET_TYPE param_set = 0; - /* Empty entries for optional label/ID. */ + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE pubKeyTmpl[] = { { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, { CKA_KEY_TYPE, &mldsaKeyType, sizeof(mldsaKeyType) }, { CKA_PARAMETER_SET, ¶m_set, sizeof(param_set) }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; - /* Mandatory entries + 2 optional. */ - CK_ULONG pubTmplCnt = sizeof(pubKeyTmpl)/sizeof(*pubKeyTmpl) - 2; + /* Mandatory entries + 4 optional. */ + CK_ULONG pubTmplCnt = sizeof(pubKeyTmpl)/sizeof(*pubKeyTmpl) - 4; - /* Empty entries for optional label/ID. */ + /* Empty entries for optional label/ID/token. */ CK_ATTRIBUTE privKeyTmpl[] = { { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, { CKA_SIGN, &ckTrue, sizeof(ckTrue) }, { CKA_KEY_TYPE, &mldsaKeyType, sizeof(mldsaKeyType) }, { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, { 0, NULL, 0 } }; - /* Mandatory entries + 2 optional. */ - CK_ULONG privTmplCnt = sizeof(privKeyTmpl)/sizeof(*privKeyTmpl) - 2; + /* Mandatory entries + 6 optional. */ + CK_ULONG privTmplCnt = sizeof(privKeyTmpl)/sizeof(*privKeyTmpl) - 6; + if (key->idLen == 0 && key->labelLen == 0) + session->tokenObjects = 0; ret = Pkcs11MechAvail(session, CKM_ML_DSA_KEY_PAIR_GEN, &mechInfo); if (ret == 0) ret = Pkcs11KeyGenSessionCheck(session, key->idLen, key->labelLen); @@ -5597,6 +5715,10 @@ static int Pkcs11MldsaKeyGen(Pkcs11Session* session, wc_MlDsaKey* key) ret = WC_KEY_SIZE_E; } } + if (ret == 0 && session->tokenObjects) { + ret = Pkcs11CheckNewTokenKey(session, CKK_ML_DSA, key->id, key->idLen, + key->label, key->labelLen); + } if (ret == 0) { WOLFSSL_MSG("PKCS#11: ML-DSA Key Generation Operation"); @@ -5622,6 +5744,14 @@ static int Pkcs11MldsaKeyGen(Pkcs11Session* session, wc_MlDsaKey* key) pubKeyTmpl[pubTmplCnt].ulValueLen = key->idLen; pubTmplCnt++; } + if (session->tokenObjects) { + XMEMCPY(&privKeyTmpl[privTmplCnt], pkcs11TokenKeyAttrs, + sizeof(pkcs11TokenKeyAttrs)); + privTmplCnt += PKCS11_TOKEN_KEY_ATTRS; + XMEMCPY(&pubKeyTmpl[pubTmplCnt], pkcs11TokenPubKeyAttrs, + sizeof(pkcs11TokenPubKeyAttrs)); + pubTmplCnt += PKCS11_TOKEN_PUB_KEY_ATTRS; + } mech.mechanism = CKM_ML_DSA_KEY_PAIR_GEN; mech.ulParameterLen = 0; @@ -5643,9 +5773,9 @@ static int Pkcs11MldsaKeyGen(Pkcs11Session* session, wc_MlDsaKey* key) if (ret == 0) ret = Pkcs11GetMldsaPublicKey(key, session, pubKey); - if (pubKey != NULL_PTR) + if (pubKey != NULL_PTR && (ret != 0 || !session->tokenObjects)) session->func->C_DestroyObject(session->handle, pubKey); - if (ret == 0 && privKey != NULL_PTR) { + if (ret == 0 && privKey != NULL_PTR && !session->closes) { key->devCtx = (void*)(wc_ptr_t)privKey; } else if (ret != 0 && privKey != NULL_PTR) @@ -7546,8 +7676,10 @@ int wc_Pkcs11_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) break; #ifdef WOLFSSL_KEY_GEN case WC_PK_TYPE_RSA_KEYGEN: - ret = Pkcs11OpenSession(token, &session, readWrite); + ret = Pkcs11OpenSession(token, &session, + readWrite || token->keyGenOnToken); if (ret == 0) { + session.tokenObjects = token->keyGenOnToken; ret = Pkcs11RsaKeyGen(&session, info); Pkcs11CloseSession(token, &session); } @@ -7571,8 +7703,10 @@ int wc_Pkcs11_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) #ifdef HAVE_ECC #ifndef NO_PKCS11_EC_KEYGEN case WC_PK_TYPE_EC_KEYGEN: - ret = Pkcs11OpenSession(token, &session, readWrite); + ret = Pkcs11OpenSession(token, &session, + readWrite || token->keyGenOnToken); if (ret == 0) { + session.tokenObjects = token->keyGenOnToken; ret = Pkcs11EcKeyGen(&session, info); Pkcs11CloseSession(token, &session); } @@ -7611,8 +7745,10 @@ int wc_Pkcs11_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) #endif #ifdef WOLFSSL_HAVE_MLKEM case WC_PK_TYPE_PQC_KEM_KEYGEN: - ret = Pkcs11OpenSession(token, &session, readWrite); + ret = Pkcs11OpenSession(token, &session, + readWrite || token->keyGenOnToken); if (ret == 0) { + session.tokenObjects = token->keyGenOnToken; ret = Pkcs11PqcKemKeyGen(&session, info); Pkcs11CloseSession(token, &session); } @@ -7634,8 +7770,10 @@ int wc_Pkcs11_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) #endif #if defined(WOLFSSL_HAVE_MLDSA) case WC_PK_TYPE_PQC_SIG_KEYGEN: - ret = Pkcs11OpenSession(token, &session, readWrite); + ret = Pkcs11OpenSession(token, &session, + readWrite || token->keyGenOnToken); if (ret == 0) { + session.tokenObjects = token->keyGenOnToken; ret = Pkcs11PqcSigKeyGen(&session, info); Pkcs11CloseSession(token, &session); } diff --git a/wolfssl/wolfcrypt/wc_pkcs11.h b/wolfssl/wolfcrypt/wc_pkcs11.h index 0bba8a712ce..0e1ba687f61 100644 --- a/wolfssl/wolfcrypt/wc_pkcs11.h +++ b/wolfssl/wolfcrypt/wc_pkcs11.h @@ -60,7 +60,8 @@ typedef struct Pkcs11Token { CK_SESSION_HANDLE handle; /* Handle to active session */ CK_UTF8CHAR_PTR userPin; /* User's PIN to login with */ CK_ULONG userPinSz; /* Size of user's PIN in bytes */ - byte userPinLogin:1; /* Login with User's PIN */ + WC_BITFIELD userPinLogin:1; /* Login with User's PIN */ + WC_BITFIELD keyGenOnToken:1; /* Generate keys as token objects */ int version; /* Pkcs11InterfaceVersionType */ } Pkcs11Token; @@ -106,6 +107,8 @@ WOLFSSL_API int wc_Pkcs11Token_InitName_NoLogin(Pkcs11Token* token, WOLFSSL_API void wc_Pkcs11Token_Final(Pkcs11Token* token); WOLFSSL_API int wc_Pkcs11Token_Open(Pkcs11Token* token, int readWrite); WOLFSSL_API void wc_Pkcs11Token_Close(Pkcs11Token* token); +WOLFSSL_API int wc_Pkcs11Token_SetKeyGenPersistent(Pkcs11Token* token, + int persistent); WOLFSSL_API int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key);