From f5370f686b1e35276903980c1dc9d7994fc3e67d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Fri, 30 Oct 2026 03:41:11 +0100 Subject: [PATCH 1/2] Add CKM_CONCATENATE_BASE_AND_KEY, _BASE_AND_DATA and _DATA_AND_BASE Implement the PKCS#11 miscellaneous concatenation derive mechanisms (v3.1/v3.2 section 6.43.3 to 6.43.5). They let a hybrid key agreement such as ETSI TS 103 744 build PSK || Z_ECDH || Z_MLKEM as a sensitive, non-extractable secret and feed it to CKM_HKDF_DERIVE without exporting the parts. A CKA_VALUE_LEN shorter than the concatenation keeps the leading bytes, matching NSS and SoftHSM, and the data may then be longer than the largest key. The shorter length may not end inside the bytes of a sensitive or non-extractable input key: a caller could otherwise derive keys one byte longer at a time, use each to sign or encrypt, and find every key byte in 256 tries. Dropping trailing data, or all of a key, stays allowed. Without CKA_VALUE_LEN the key takes the summed length, as in NSS softoken; NSS asks for a CKK_HKDF key this way when it combines the secrets of its hybrid TLS groups. An AES key has no single well-defined length, so it needs CKA_VALUE_LEN and otherwise gets CKR_TEMPLATE_INCOMPLETE. CKA_KEY_TYPE stays required, as for the other derive mechanisms. For CKM_CONCATENATE_BASE_AND_KEY the second key must be a secret key with CKA_DERIVE set, the derived key is sensitive if either input is and non-extractable if either input is, and CKA_ALWAYS_SENSITIVE / CKA_NEVER_EXTRACTABLE need both inputs to have them. A CKM_CONCATENATE_DATA_AND_BASE result truncated to the caller's data holds no key bytes, so it gets neither flag. CKA_WRAP_WITH_TRUSTED is set when either input has it, so a copy cannot be wrapped with an untrusted key. A token key whose value is not decoded, before login or after logout, gives CKR_USER_NOT_LOGGED_IN instead of being concatenated as empty or zeroed bytes. An AES key wrapped by the STM32U5 DHUK gives CKR_KEY_FUNCTION_NOT_PERMITTED, as its stored value is not the key. The concatenation output contains the input key bytes, so these three mechanisms inherit CKA_SENSITIVE and CKA_EXTRACTABLE even in builds with WOLFPKCS11_LEGACY_DERIVE_NO_INHERIT (enabled for NSS); otherwise concatenating a sensitive key with empty data would give a readable copy. Under WP11_NSS_PERMISSIVE_KEY_DEFAULTS the second key skips the CKA_DERIVE check, as the base key does. Related derive changes: - Drop WP11_DERIVE_SECRET_KEY. Concatenation needs no crypto, so the secret-key derive path is always built. - For the other mechanisms, an AES key derived without CKA_VALUE_LEN takes the whole derived length. When that is not 16, 24 or 32 bytes, return CKR_TEMPLATE_INCOMPLETE before creating the object instead of failing in the key setter with CKR_FUNCTION_FAILED. A template that has CKA_VALUE_LEN keeps the error object creation returns for it. - Use a WP11_MAX_SYM_KEY_SZ of 256 bytes in ML-KEM builds without DH. The ECC-only value of 66 bytes could not hold a 96-byte PSK || ECDH || ML-KEM input. Fixes #264. --- src/crypto.c | 254 ++++++++-- src/internal.c | 123 +++++ src/slot.c | 11 + tests/pkcs11str.c | 98 ++++ tests/pkcs11test.c | 1032 +++++++++++++++++++++++++++++++++++++++++ tests/pkcs11v3test.c | 254 ++++++++++ wolfpkcs11/internal.h | 6 + wolfpkcs11/pkcs11.h | 9 + 8 files changed, 1754 insertions(+), 33 deletions(-) diff --git a/src/crypto.c b/src/crypto.c index 35b7d0f1..5e762ddf 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -54,12 +54,6 @@ #define PRF_KEY_SIZE 48 -/* Derive mechanisms that build their output into a secret key object. */ -#if defined(HAVE_ECC) || !defined(NO_DH) || defined(WOLFPKCS11_HKDF) || \ - (!defined(NO_AES) && defined(HAVE_AES_CBC)) || defined(WOLFSSL_HAVE_PRF) - #define WP11_DERIVE_SECRET_KEY -#endif - /* Check that a CK_ULONG value fits in word32 with room for overhead such as * authentication tags, key wrap blocks, or padding. On LP64 platforms CK_ULONG * is 64-bit but wolfCrypt functions use word32/int for lengths. */ @@ -641,7 +635,6 @@ static CK_RV SetInitialStates(WP11_Object* key) return rv; } -#ifdef WP11_DERIVE_SECRET_KEY /* PKCS#11 derive rules: the historical flags also need the base key's. */ static CK_RV SetDerivedStates(WP11_Object* key, CK_BBOOL baseAlwaysSensitive, CK_BBOOL baseNeverExtractable) @@ -670,7 +663,6 @@ static CK_RV SetDerivedStates(WP11_Object* key, CK_BBOOL baseAlwaysSensitive, } return rv; } -#endif static CK_RV TemplateHasAttribute(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) @@ -1050,6 +1042,14 @@ static CK_RV CheckAttributeUpdate(WP11_Session* session, WP11_Object* obj, return CKR_OK; } +static CK_RV UndecodedKeyRv(WP11_Session* session) +{ + /* Logged in yet undecoded: the stored key failed to verify. */ + if (WP11_Slot_IsUserLoggedIn(WP11_Session_GetSlot(session))) + return CKR_FUNCTION_FAILED; + return CKR_USER_NOT_LOGGED_IN; +} + /** * Set the values of the attributes into the object. * @@ -1345,12 +1345,8 @@ static CK_RV SetAttributeValue(WP11_Session* session, WP11_Object* obj, return CKR_ATTRIBUTE_VALUE_INVALID; else if (ret == BUFFER_E) return CKR_BUFFER_TOO_SMALL; - else if (ret == BAD_STATE_E) { - /* Logged in yet undecoded: the stored key failed to verify. */ - if (WP11_Slot_IsUserLoggedIn(WP11_Session_GetSlot(session))) - return CKR_FUNCTION_FAILED; - return CKR_USER_NOT_LOGGED_IN; - } + else if (ret == BAD_STATE_E) + return UndecodedKeyRv(session); else if (ret != 0) return CKR_FUNCTION_FAILED; } @@ -10149,7 +10145,6 @@ CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession, return rv; } -#ifdef WP11_DERIVE_SECRET_KEY /** * Determine the key length of the object. * @@ -10199,7 +10194,6 @@ static int SymmKeyLen(WP11_Object* obj, word32 len, CK_ULONG* symmKeyLen) return ret; } -#endif #ifdef WOLFSSL_HAVE_PRF static int SetKeyExtract(WP11_Session* session, byte* ptr, CK_ULONG length, @@ -10352,6 +10346,36 @@ static int Tls12_Extract_Keys(WP11_Session* session, } #endif +static CK_RV ConcatKeyRv(WP11_Session* session, int ret) +{ + if (ret == 0) + return CKR_OK; + if (ret == BAD_STATE_E) + return UndecodedKeyRv(session); + if (ret == NOT_AVAILABLE_E) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + return CKR_FUNCTION_FAILED; +} + +/* A result that ends inside a protected key lets each of its bytes be found + * by brute force, so it must hold all or none of such a key. */ +static int ConcatCutsProtectedKey(WP11_Object* key, CK_ULONG off, + CK_ULONG len, CK_ULONG reqLen) +{ + CK_BBOOL sensitive = CK_TRUE; + CK_BBOOL extractable = CK_FALSE; + CK_ULONG bLen = sizeof(CK_BBOOL); + + if (reqLen <= off || reqLen >= off + len) + return 0; + if (WP11_Object_GetAttr(key, CKA_SENSITIVE, &sensitive, &bLen) != 0) + sensitive = CK_TRUE; + bLen = sizeof(CK_BBOOL); + if (WP11_Object_GetAttr(key, CKA_EXTRACTABLE, &extractable, &bLen) != 0) + extractable = CK_FALSE; + return sensitive == CK_TRUE || extractable == CK_FALSE; +} + /** * Generate a symmetric key into a new key object. * @@ -10373,6 +10397,14 @@ static int Tls12_Extract_Keys(WP11_Session* session, * CKR_FUNCTION_FAILED when setting an attribute fails. * CKR_MECHANISM_INVALID when the mechanism is not supported with this * type of operation. + * CKR_KEY_HANDLE_INVALID when the other key handle is not valid. + * CKR_KEY_TYPE_INCONSISTENT when a concatenated key is not a secret + * key. + * CKR_KEY_FUNCTION_NOT_PERMITTED when a key may not be derived from. + * CKR_KEY_SIZE_RANGE when the derived key is too long. + * CKR_TEMPLATE_INCOMPLETE when an AES key has no CKA_VALUE_LEN and + * is concatenated, or the derived length is not an AES key size. + * CKR_USER_NOT_LOGGED_IN when a token key value is not decoded. * CKR_OK on success. */ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, @@ -10386,25 +10418,31 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, CK_RV rv = CKR_OK; WP11_Session* session; WP11_Object* obj = NULL; -#ifdef WP11_DERIVE_SECRET_KEY + WP11_Object* otherKey = NULL; byte* derivedKey = NULL; word32 keyLen; CK_ULONG symmKeyLen; unsigned char* secretKeyData[2] = { NULL, NULL }; CK_ULONG secretKeyLen[2] = { 0, 0 }; -#ifndef WOLFPKCS11_LEGACY_DERIVE_NO_INHERIT /* F-4533: protection attributes inherited from the base key. */ CK_BBOOL baseSensitive = CK_FALSE; CK_BBOOL baseExtractable = CK_TRUE; CK_ULONG bLen; +#ifdef WOLFPKCS11_LEGACY_DERIVE_NO_INHERIT + int inherit = 0; +#else + int inherit = 1; #endif -#endif -#ifdef WP11_DERIVE_SECRET_KEY + int dataOnly = 0; + CK_BBOOL wrapTrusted = CK_FALSE; CK_BBOOL baseAlwaysSensitive = CK_FALSE; CK_BBOOL baseNeverExtractable = CK_FALSE; + CK_BBOOL otherState; CK_ULONG histLen; CK_ATTRIBUTE* valueLenAttr = NULL; CK_ULONG reqValueLen = 0; +#if !defined(NO_AES) && !defined(WOLFPKCS11_NSS) + CK_ATTRIBUTE* keyTypeAttr = NULL; #endif #ifndef NO_DH CK_ULONG dhPrimeLen = 0; @@ -10804,24 +10842,143 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, } #endif #endif + case CKM_CONCATENATE_BASE_AND_KEY: + case CKM_CONCATENATE_BASE_AND_DATA: + case CKM_CONCATENATE_DATA_AND_BASE: { + CK_KEY_DERIVATION_STRING_DATA* strData = NULL; + CK_ATTRIBUTE* lenAttr = NULL; + CK_ATTRIBUTE* typeAttr = NULL; + CK_ULONG baseLen = 0; + CK_ULONG addLen = 0; + CK_ULONG totalLen; + CK_ULONG reqLen; + word32 keySz; + + if (WP11_Object_GetClass(obj) != CKO_SECRET_KEY || + !WP11_KeyType_IsSecret(WP11_Object_GetType(obj))) + return CKR_KEY_TYPE_INCONSISTENT; + if (pMechanism->pParameter == NULL) + return CKR_MECHANISM_PARAM_INVALID; + if (pMechanism->mechanism == CKM_CONCATENATE_BASE_AND_KEY) { + if (pMechanism->ulParameterLen != sizeof(CK_OBJECT_HANDLE)) + return CKR_MECHANISM_PARAM_INVALID; + if (WP11_Object_Find(session, + *(CK_OBJECT_HANDLE*)pMechanism->pParameter, + &otherKey) != 0) + return CKR_KEY_HANDLE_INVALID; + if (WP11_Object_GetClass(otherKey) != CKO_SECRET_KEY || + !WP11_KeyType_IsSecret(WP11_Object_GetType(otherKey))) + return CKR_KEY_TYPE_INCONSISTENT; +#ifndef WP11_NSS_PERMISSIVE_KEY_DEFAULTS + ret = CheckOpSupported(otherKey, CKA_DERIVE); + if (ret != CKR_OK) + return ret; +#endif + ret = WP11_Concat_KeyLen(otherKey, &keySz); + if (ret != 0) + return ConcatKeyRv(session, ret); + addLen = keySz; + } + else { + if (pMechanism->ulParameterLen != + sizeof(CK_KEY_DERIVATION_STRING_DATA)) + return CKR_MECHANISM_PARAM_INVALID; + strData = (CK_KEY_DERIVATION_STRING_DATA*) + pMechanism->pParameter; + if (strData->pData == NULL && strData->ulLen > 0) + return CKR_MECHANISM_PARAM_INVALID; + if (strData->ulLen > + (CK_ULONG)WOLFSSL_MAX_32BIT - WP11_MAX_SYM_KEY_SZ) + return CKR_KEY_SIZE_RANGE; + addLen = strData->ulLen; + } + ret = WP11_Concat_KeyLen(obj, &keySz); + if (ret != 0) + return ConcatKeyRv(session, ret); + baseLen = keySz; + totalLen = baseLen + addLen; + reqLen = totalLen; + + FindAttributeType(pTemplate, ulAttributeCount, CKA_VALUE_LEN, + &lenAttr); + if (lenAttr != NULL) { + if (lenAttr->pValue == NULL || + lenAttr->ulValueLen != sizeof(CK_ULONG)) + return CKR_ATTRIBUTE_VALUE_INVALID; + XMEMCPY(&reqLen, lenAttr->pValue, sizeof(CK_ULONG)); + if (reqLen == 0 || reqLen > totalLen) + return CKR_ATTRIBUTE_VALUE_INVALID; + if (ConcatCutsProtectedKey(obj, (pMechanism->mechanism == + CKM_CONCATENATE_DATA_AND_BASE) ? addLen : 0, baseLen, + reqLen) || (otherKey != NULL && + ConcatCutsProtectedKey(otherKey, baseLen, addLen, + reqLen))) + return CKR_ATTRIBUTE_VALUE_INVALID; + } + else { + FindAttributeType(pTemplate, ulAttributeCount, CKA_KEY_TYPE, + &typeAttr); + if (typeAttr != NULL && + *(CK_KEY_TYPE*)typeAttr->pValue == CKK_AES) + return CKR_TEMPLATE_INCOMPLETE; + } + if (reqLen == 0 || reqLen > WP11_MAX_SYM_KEY_SZ) + return CKR_KEY_SIZE_RANGE; + inherit = 1; + dataOnly = pMechanism->mechanism == CKM_CONCATENATE_DATA_AND_BASE && + reqLen <= addLen; + bLen = sizeof(CK_BBOOL); + if (WP11_Object_GetAttr(obj, CKA_WRAP_WITH_TRUSTED, &wrapTrusted, + &bLen) != 0) + wrapTrusted = CK_FALSE; + bLen = sizeof(CK_BBOOL); + if (otherKey != NULL && WP11_Object_GetAttr(otherKey, + CKA_WRAP_WITH_TRUSTED, &otherState, &bLen) == 0 && + otherState == CK_TRUE) + wrapTrusted = CK_TRUE; + + keyLen = (word32)reqLen; + derivedKey = (byte*)XMALLOC(keyLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (derivedKey == NULL) + return CKR_DEVICE_MEMORY; + XMEMSET(derivedKey, 0, keyLen); + + ret = WP11_Concat_Derive(obj, otherKey, + (strData != NULL) ? strData->pData : NULL, + (strData != NULL) ? (word32)strData->ulLen : 0, + pMechanism->mechanism == CKM_CONCATENATE_DATA_AND_BASE, + derivedKey, keyLen); + rv = ConcatKeyRv(session, ret); + break; + } default: (void)ulAttributeCount; return CKR_MECHANISM_INVALID; } -#ifdef WP11_DERIVE_SECRET_KEY if ((ret == 0) && (derivedKey != NULL)) { -#ifndef WOLFPKCS11_LEGACY_DERIVE_NO_INHERIT /* F-4533: read the base key's protection bits while `obj' still * refers to it, before CreateObject reuses `obj' for the new key. */ - bLen = sizeof(CK_BBOOL); - if (WP11_Object_GetAttr(obj, CKA_SENSITIVE, &baseSensitive, &bLen) != 0) - baseSensitive = CK_FALSE; - bLen = sizeof(CK_BBOOL); - if (WP11_Object_GetAttr(obj, CKA_EXTRACTABLE, &baseExtractable, - &bLen) != 0) - baseExtractable = CK_TRUE; -#endif + if (inherit) { + bLen = sizeof(CK_BBOOL); + if (WP11_Object_GetAttr(obj, CKA_SENSITIVE, &baseSensitive, + &bLen) != 0) + baseSensitive = CK_FALSE; + bLen = sizeof(CK_BBOOL); + if (WP11_Object_GetAttr(obj, CKA_EXTRACTABLE, &baseExtractable, + &bLen) != 0) + baseExtractable = CK_TRUE; + bLen = sizeof(CK_BBOOL); + if (otherKey != NULL && WP11_Object_GetAttr(otherKey, + CKA_SENSITIVE, &otherState, &bLen) == 0 && + otherState == CK_TRUE) + baseSensitive = CK_TRUE; + bLen = sizeof(CK_BBOOL); + if (otherKey != NULL && WP11_Object_GetAttr(otherKey, + CKA_EXTRACTABLE, &otherState, &bLen) == 0 && + otherState == CK_FALSE) + baseExtractable = CK_FALSE; + } histLen = sizeof(CK_BBOOL); if (WP11_Object_GetAttr(obj, CKA_ALWAYS_SENSITIVE, &baseAlwaysSensitive, &histLen) != 0) @@ -10830,6 +10987,22 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, if (WP11_Object_GetAttr(obj, CKA_NEVER_EXTRACTABLE, &baseNeverExtractable, &histLen) != 0) baseNeverExtractable = CK_FALSE; + if (otherKey != NULL) { + histLen = sizeof(CK_BBOOL); + if (WP11_Object_GetAttr(otherKey, CKA_ALWAYS_SENSITIVE, + &otherState, &histLen) != 0 || + otherState != CK_TRUE) + baseAlwaysSensitive = CK_FALSE; + histLen = sizeof(CK_BBOOL); + if (WP11_Object_GetAttr(otherKey, CKA_NEVER_EXTRACTABLE, + &otherState, &histLen) != 0 || + otherState != CK_TRUE) + baseNeverExtractable = CK_FALSE; + } + if (dataOnly) { + baseAlwaysSensitive = CK_FALSE; + baseNeverExtractable = CK_FALSE; + } FindAttributeType(pTemplate, ulAttributeCount, CKA_VALUE_LEN, &valueLenAttr); if ((valueLenAttr != NULL) && (valueLenAttr->pValue != NULL) && @@ -10848,13 +11021,24 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, #endif if (reqValueLen > keyLen) rv = CKR_ATTRIBUTE_VALUE_INVALID; +#if !defined(NO_AES) && !defined(WOLFPKCS11_NSS) + if (rv == CKR_OK && valueLenAttr == NULL) { + FindAttributeType(pTemplate, ulAttributeCount, CKA_KEY_TYPE, + &keyTypeAttr); + if (keyTypeAttr != NULL && + *(CK_KEY_TYPE*)keyTypeAttr->pValue == CKK_AES && + keyLen != AES_128_KEY_SIZE && keyLen != AES_192_KEY_SIZE && + keyLen != AES_256_KEY_SIZE) { + rv = CKR_TEMPLATE_INCOMPLETE; + } + } +#endif if (rv == CKR_OK) { rv = CreateObject(session, pTemplate, ulAttributeCount, CKO_SECRET_KEY, &obj); } if (rv == CKR_OK) { /* obj now refers to the newly created derived key. */ -#ifndef WOLFPKCS11_LEGACY_DERIVE_NO_INHERIT /* F-4533: PKCS#11 v3.0 5.5.5 - a derived key must be at least as * protected as its base key. Force the inherited attributes after * the caller template has been applied so a weaker template @@ -10871,9 +11055,14 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, sizeof(bbFalse)) != 0) rv = CKR_FUNCTION_FAILED; } + if (rv == CKR_OK && wrapTrusted == CK_TRUE) { + CK_BBOOL bbTrue = CK_TRUE; + if (WP11_Object_SetAttr(obj, CKA_WRAP_WITH_TRUSTED, &bbTrue, + sizeof(bbTrue)) != 0) + rv = CKR_FUNCTION_FAILED; + } if (rv != CKR_OK) WP11_Object_Free(obj); -#endif } if (rv == CKR_OK) { ret = SymmKeyLen(obj, keyLen, &symmKeyLen); @@ -10912,7 +11101,6 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, wc_ForceZero(derivedKey, keyLen); XFREE(derivedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); } -#endif return rv; } diff --git a/src/internal.c b/src/internal.c index 8efcee80..9d04984d 100644 --- a/src/internal.c +++ b/src/internal.c @@ -19044,6 +19044,129 @@ int WP11_KDF_Derive(WP11_Session* session, CK_HKDF_PARAMS_PTR params, #endif +/* Call with the object lock held when the key is on the token. */ +static int wp11_Concat_KeyState(WP11_Object* key) +{ + int ret = 0; + + if (key->objClass != CKO_SECRET_KEY || key->data.symmKey == NULL) + ret = BAD_FUNC_ARG; +#ifndef WOLFPKCS11_NO_STORE + else if (key->encoded || key->undecoded) + ret = BAD_STATE_E; +#endif +#ifdef WOLFSSL_STM32U5_DHUK + else if (key->dhukIvLen > 0) + ret = NOT_AVAILABLE_E; +#endif + + return ret; +} + +/** + * Get the length of a secret key's value for concatenation. + * + * @param key [in] Secret key object. + * @param len [out] Length of the key value in bytes. + * @return BAD_FUNC_ARG when the object is not a secret key. + * BAD_STATE_E when the key value is not decoded. + * NOT_AVAILABLE_E when the key value is wrapped by hardware. + * 0 on success. + */ +int WP11_Concat_KeyLen(WP11_Object* key, word32* len) +{ + int ret; + + if (key == NULL || len == NULL) + return BAD_FUNC_ARG; + + if (key->onToken) + WP11_Lock_LockRO(key->lock); + ret = wp11_Concat_KeyState(key); + if (ret == 0) + *len = key->data.symmKey->len; + if (key->onToken) + WP11_Lock_UnlockRO(key->lock); + + return ret; +} + +/* Append up to outLen leading bytes of a secret key's value to out. */ +static int wp11_Concat_Key(WP11_Object* key, byte* out, word32 outLen, + word32* used) +{ + int ret; + word32 len; + + if (key->onToken) + WP11_Lock_LockRO(key->lock); + ret = wp11_Concat_KeyState(key); + if (ret == 0) { + len = key->data.symmKey->len; + if (len > outLen) + len = outLen; + if (len > 0) + XMEMCPY(out, key->data.symmKey->data, len); + *used += len; + } + if (key->onToken) + WP11_Lock_UnlockRO(key->lock); + + return ret; +} + +/** + * Concatenate a secret key's value with another key's value or with data. + * + * @param base [in] Base secret key object. + * @param other [in] Key appended to the base key. NULL to use data. + * @param data [in] Data to concatenate with the base key. + * @param dataLen [in] Length of data in bytes. + * @param dataFirst [in] Data goes before the base key value when set. + * @param out [out] Buffer to hold the leading outLen bytes. + * @param outLen [in] Number of bytes to output. + * @return BAD_FUNC_ARG when an object is not a secret key or the + * concatenation is shorter than outLen. + * BAD_STATE_E when a key value is not decoded. + * NOT_AVAILABLE_E when a key value is wrapped by hardware. + * 0 on success. + */ +int WP11_Concat_Derive(WP11_Object* base, WP11_Object* other, + const byte* data, word32 dataLen, int dataFirst, + byte* out, word32 outLen) +{ + int ret; + word32 used = 0; + word32 len; + + if (base == NULL || out == NULL || + (other == NULL && data == NULL && dataLen > 0)) + return BAD_FUNC_ARG; + + if (other == NULL && dataFirst) { + len = (dataLen < outLen) ? dataLen : outLen; + if (len > 0) + XMEMCPY(out, data, len); + used = len; + } + ret = wp11_Concat_Key(base, out + used, outLen - used, &used); + if (ret == 0 && other != NULL) { + ret = wp11_Concat_Key(other, out + used, outLen - used, &used); + } + else if (ret == 0 && !dataFirst) { + len = outLen - used; + if (dataLen < len) + len = dataLen; + if (len > 0) + XMEMCPY(out + used, data, len); + used += len; + } + + if (ret == 0 && used != outLen) + ret = BAD_FUNC_ARG; + return ret; +} + #ifndef NO_DH /** * Generate an DH key pair. diff --git a/src/slot.c b/src/slot.c index 0ba5b84d..5816a361 100644 --- a/src/slot.c +++ b/src/slot.c @@ -497,6 +497,9 @@ static CK_MECHANISM_TYPE mechanismList[] = { CKM_TLS_MAC, #endif CKM_GENERIC_SECRET_KEY_GEN, + CKM_CONCATENATE_BASE_AND_KEY, + CKM_CONCATENATE_BASE_AND_DATA, + CKM_CONCATENATE_DATA_AND_BASE, #ifndef NO_HMAC CKM_PKCS5_PBKD2 #endif @@ -909,6 +912,9 @@ static CK_MECHANISM_INFO sha3MechInfo = { static CK_MECHANISM_INFO genSecKeyGenMechInfo = { 8, WP11_MAX_SYM_KEY_SZ * 8, CKF_GENERATE }; +static CK_MECHANISM_INFO concatMechInfo = { + 8, WP11_MAX_SYM_KEY_SZ * 8, CKF_DERIVE +}; #ifndef NO_HMAC static CK_MECHANISM_INFO pkcs5Pbkdf2MechInfo = { @@ -1293,6 +1299,11 @@ CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, XMEMCPY(pInfo, &genSecKeyGenMechInfo, sizeof(CK_MECHANISM_INFO)); break; + case CKM_CONCATENATE_BASE_AND_KEY: + case CKM_CONCATENATE_BASE_AND_DATA: + case CKM_CONCATENATE_DATA_AND_BASE: + XMEMCPY(pInfo, &concatMechInfo, sizeof(CK_MECHANISM_INFO)); + break; #ifndef NO_HMAC case CKM_PKCS5_PBKD2: XMEMCPY(pInfo, &pkcs5Pbkdf2MechInfo, diff --git a/tests/pkcs11str.c b/tests/pkcs11str.c index 8fe2de79..29b9dc1d 100644 --- a/tests/pkcs11str.c +++ b/tests/pkcs11str.c @@ -961,6 +961,88 @@ static CK_RV find_mlkem_pub_key(CK_SESSION_HANDLE session, } #endif /* WOLFPKCS11_MLKEM */ +#ifndef WOLFPKCS11_NO_STORE +static unsigned char concatKeyId[] = "123concatkey"; + +static CK_RV create_concat_key(CK_SESSION_HANDLE session) +{ + CK_RV ret; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_BBOOL ckFalse = CK_FALSE; + unsigned char value[16] = { 0 }; + CK_OBJECT_HANDLE key; + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &keyClass, sizeof(keyClass) }, + { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + { CKA_ID, concatKeyId, sizeof(concatKeyId) - 1 }, + { CKA_VALUE, value, sizeof(value) }, + }; + + ret = funcList->C_CreateObject(session, tmpl, + sizeof(tmpl) / sizeof(*tmpl), &key); + CHECK_CKR(ret, "Create public secret key"); + + return ret; +} + +static CK_RV concat_before_login(void) +{ + CK_RV ret; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_BBOOL ckFalse = CK_FALSE; + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE derived = CK_INVALID_HANDLE; + CK_ULONG count = 0; + unsigned char data[4] = { 0xd0, 0xd1, 0xd2, 0xd3 }; + CK_KEY_DERIVATION_STRING_DATA params = { data, sizeof(data) }; + CK_MECHANISM mech = { CKM_CONCATENATE_BASE_AND_DATA, ¶ms, + sizeof(params) }; + CK_ATTRIBUTE findTmpl[] = { + { CKA_CLASS, &keyClass, sizeof(keyClass) }, + { CKA_ID, concatKeyId, sizeof(concatKeyId) - 1 }, + }; + CK_ULONG valueLen = 16 + sizeof(data); + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &keyClass, sizeof(keyClass) }, + { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + { CKA_VALUE_LEN, &valueLen, sizeof(valueLen) }, + }; + + ret = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, + NULL, NULL, &session); + CHECK_CKR(ret, "Open session before login"); + if (ret == CKR_OK) { + ret = funcList->C_FindObjectsInit(session, findTmpl, + sizeof(findTmpl) / sizeof(*findTmpl)); + CHECK_CKR(ret, "Find public secret key init"); + } + if (ret == CKR_OK) { + ret = funcList->C_FindObjects(session, &key, 1, &count); + CHECK_CKR(ret, "Find public secret key"); + funcList->C_FindObjectsFinal(session); + } + if (ret == CKR_OK && count != 1) { + ret = -1; + CHECK_CKR(ret, "Find public secret key count"); + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, key, tmpl, + sizeof(tmpl) / sizeof(*tmpl), &derived); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "Concatenate undecoded key before login"); + } + if (session != CK_INVALID_HANDLE) + funcList->C_CloseSession(session); + + return ret; +} +#endif + static CK_RV pkcs11_test(int slotId, int setPin, int closeDl) { CK_RV ret; @@ -1131,6 +1213,15 @@ static CK_RV pkcs11_test(int slotId, int setPin, int closeDl) printf("Done\n"); } } +#endif +#ifndef WOLFPKCS11_NO_STORE + if (ret == CKR_OK && userPinLen != 0) { + printf("Create public secret key ... "); + ret = create_concat_key(session); + if (ret == CKR_OK) { + printf("Done\n"); + } + } #endif pkcs11_close_session(session); } @@ -1155,6 +1246,13 @@ static CK_RV pkcs11_test(int slotId, int setPin, int closeDl) printf("Done\n"); } } + if (ret == CKR_OK && userPinLen != 0) { + printf("Concatenate before login ... "); + ret = concat_before_login(); + if (ret == CKR_OK) { + printf("Done\n"); + } + } #endif if (ret == CKR_OK) { inited = 1; diff --git a/tests/pkcs11test.c b/tests/pkcs11test.c index c1e2d532..472f9751 100644 --- a/tests/pkcs11test.c +++ b/tests/pkcs11test.c @@ -15997,6 +15997,1019 @@ static CK_RV test_hkdf_derive_implied_class(void* args) } #endif +static CK_RV concat_create_key(CK_SESSION_HANDLE session, CK_BYTE* value, + CK_ULONG len, CK_BBOOL sensitive, + CK_BBOOL extractable, CK_BBOOL derive, + CK_BBOOL token, CK_OBJECT_HANDLE* key) +{ + CK_ATTRIBUTE tmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_TOKEN, &token, sizeof(token)}, + {CKA_SENSITIVE, &sensitive, sizeof(sensitive)}, + {CKA_EXTRACTABLE, &extractable, sizeof(extractable)}, + {CKA_DERIVE, &derive, sizeof(derive)}, + {CKA_SIGN, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE, value, len}, + }; + + return funcList->C_CreateObject(session, tmpl, + sizeof(tmpl) / sizeof(*tmpl), key); +} + +static CK_RV concat_derive(CK_SESSION_HANDLE session, CK_MECHANISM* mech, + CK_OBJECT_HANDLE base, CK_KEY_TYPE keyType, + CK_ULONG valueLen, CK_OBJECT_HANDLE* key) +{ + CK_ATTRIBUTE tmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &keyType, sizeof(keyType)}, + {CKA_SENSITIVE, &ckFalse, sizeof(ckFalse)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_SIGN, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE_LEN, &valueLen, sizeof(valueLen)}, + }; + CK_ULONG cnt = sizeof(tmpl) / sizeof(*tmpl); + + if (valueLen == 0) + cnt--; + *key = CK_INVALID_HANDLE; + return funcList->C_DeriveKey(session, mech, base, tmpl, cnt, key); +} + +static CK_RV concat_check_value(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE key, CK_BYTE* exp, + CK_ULONG expLen) +{ + CK_RV ret; + CK_BYTE value[64]; + CK_ATTRIBUTE attr = {CKA_VALUE, value, sizeof(value)}; + + ret = funcList->C_GetAttributeValue(session, key, &attr, 1); + CHECK_CKR(ret, "Get concatenated value"); + if (ret == CKR_OK && (attr.ulValueLen != expLen || + XMEMCMP(value, exp, expLen) != 0)) { + ret = CKR_DATA_INVALID; + CHECK_CKR(ret, "Concatenated value"); + } + funcList->C_DestroyObject(session, key); + + return ret; +} + +static CK_RV concat_check_bools(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE key, CK_BBOOL sensitive, + CK_BBOOL extractable, + CK_BBOOL alwaysSensitive, + CK_BBOOL neverExtractable) +{ + CK_RV ret; + CK_BBOOL got[4]; + CK_ATTRIBUTE attrs[] = { + {CKA_SENSITIVE, &got[0], sizeof(CK_BBOOL)}, + {CKA_EXTRACTABLE, &got[1], sizeof(CK_BBOOL)}, + {CKA_ALWAYS_SENSITIVE, &got[2], sizeof(CK_BBOOL)}, + {CKA_NEVER_EXTRACTABLE, &got[3], sizeof(CK_BBOOL)}, + }; + + ret = funcList->C_GetAttributeValue(session, key, attrs, 4); + CHECK_CKR(ret, "Get derived key protection"); + if (ret == CKR_OK && (got[0] != sensitive || got[1] != extractable || + got[2] != alwaysSensitive || got[3] != neverExtractable)) { + ret = CKR_DATA_INVALID; + CHECK_CKR(ret, "Derived key protection"); + } + funcList->C_DestroyObject(session, key); + + return ret; +} + +static CK_RV test_concatenate_base_and_key(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_BYTE a[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + CK_BYTE b[8] = { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7 }; + CK_BYTE ab[24]; + CK_BYTE aa[32]; + CK_OBJECT_HANDLE hA = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hB = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hOther = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hKey; + CK_MECHANISM mech = { CKM_CONCATENATE_BASE_AND_KEY, &hOther, + sizeof(hOther) }; + + XMEMCPY(ab, a, sizeof(a)); + XMEMCPY(ab + sizeof(a), b, sizeof(b)); + XMEMCPY(aa, a, sizeof(a)); + XMEMCPY(aa + sizeof(a), a, sizeof(a)); + + ret = concat_create_key(session, a, sizeof(a), CK_FALSE, CK_TRUE, CK_TRUE, + CK_FALSE, &hA); + CHECK_CKR(ret, "Create base key"); + if (ret == CKR_OK) { + ret = concat_create_key(session, b, sizeof(b), CK_FALSE, CK_TRUE, + CK_TRUE, CK_TRUE, &hB); + CHECK_CKR(ret, "Create token key"); + } + if (ret == CKR_OK) { + hOther = hB; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR(ret, "Concatenate base and key"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, ab, sizeof(ab)); + if (ret == CKR_OK) { + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 20, &hKey); + CHECK_CKR(ret, "Concatenate base and key, shorter length"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, ab, 20); + if (ret == CKR_OK) { + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, + sizeof(ab) + 1, &hKey); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, + "Concatenate base and key, length too long"); + } + if (ret == CKR_OK) { + hOther = hA; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR(ret, "Concatenate base key with itself"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, aa, sizeof(aa)); +#ifndef NO_AES + if (ret == CKR_OK) { + hOther = hB; + ret = concat_derive(session, &mech, hA, CKK_AES, 0, &hKey); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCOMPLETE, + "Concatenate to AES key without length"); + } + if (ret == CKR_OK) { + ret = concat_derive(session, &mech, hA, CKK_AES, sizeof(ab), &hKey); + CHECK_CKR(ret, "Concatenate to AES-192 key"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, ab, sizeof(ab)); +#endif + + if (hB != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hB); + if (hA != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hA); + + return ret; +} + +static CK_RV test_concatenate_data(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_BYTE a[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; + CK_BYTE d[4] = { 0xd0, 0xd1, 0xd2, 0xd3 }; + CK_BYTE ad[12] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0xd0, 0xd1, 0xd2, 0xd3 + }; + CK_BYTE da[12] = { + 0xd0, 0xd1, 0xd2, 0xd3, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef + }; + CK_OBJECT_HANDLE hA = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hKey; + CK_KEY_DERIVATION_STRING_DATA params = { d, sizeof(d) }; + CK_MECHANISM mech = { CKM_CONCATENATE_BASE_AND_DATA, ¶ms, + sizeof(params) }; +#ifndef NO_AES + CK_BYTE aes[16] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + CK_BYTE aesD[20]; + CK_OBJECT_HANDLE hAes = CK_INVALID_HANDLE; + CK_ATTRIBUTE aesTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType)}, + {CKA_SENSITIVE, &ckFalse, sizeof(ckFalse)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE, aes, sizeof(aes)}, + }; + + XMEMCPY(aesD, aes, sizeof(aes)); + XMEMCPY(aesD + sizeof(aes), d, sizeof(d)); +#endif + + ret = concat_create_key(session, a, sizeof(a), CK_FALSE, CK_TRUE, CK_TRUE, + CK_FALSE, &hA); + CHECK_CKR(ret, "Create base key"); + if (ret == CKR_OK) { + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR(ret, "Concatenate base and data"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, ad, sizeof(ad)); + if (ret == CKR_OK) { + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 10, &hKey); + CHECK_CKR(ret, "Concatenate base and data, shorter length"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, ad, 10); + if (ret == CKR_OK) { + mech.mechanism = CKM_CONCATENATE_DATA_AND_BASE; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR(ret, "Concatenate data and base"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, da, sizeof(da)); +#ifdef WOLFPKCS11_HKDF + if (ret == CKR_OK) { + ret = concat_derive(session, &mech, hA, CKK_HKDF, 0, &hKey); + CHECK_CKR(ret, "Concatenate data and base to HKDF key"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, da, sizeof(da)); +#endif + if (ret == CKR_OK) { + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 6, &hKey); + CHECK_CKR(ret, "Concatenate data and base, shorter length"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, da, 6); + if (ret == CKR_OK) { + params.ulLen = 0; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR(ret, "Concatenate empty data and base"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, a, sizeof(a)); +#ifndef NO_AES + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, aesTmpl, + sizeof(aesTmpl) / sizeof(*aesTmpl), + &hAes); + CHECK_CKR(ret, "Create AES base key"); + } + if (ret == CKR_OK) { + mech.mechanism = CKM_CONCATENATE_BASE_AND_DATA; + params.ulLen = sizeof(d); + ret = concat_derive(session, &mech, hAes, CKK_GENERIC_SECRET, 0, + &hKey); + CHECK_CKR(ret, "Concatenate AES base key and data"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, aesD, sizeof(aesD)); + if (hAes != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hAes); +#endif + + if (hA != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hA); + + return ret; +} + +static CK_RV test_concatenate_protection(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_BYTE a[16] = { 0 }; + CK_ULONG genLen = 16; + CK_OBJECT_HANDLE hA = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hG1 = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hG2 = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hOther = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hKey; + CK_MECHANISM genMech = { CKM_GENERIC_SECRET_KEY_GEN, NULL, 0 }; + CK_MECHANISM mech = { CKM_CONCATENATE_BASE_AND_KEY, &hOther, + sizeof(hOther) }; + CK_KEY_DERIVATION_STRING_DATA params = { a, sizeof(a) }; + CK_MECHANISM dataMech = { CKM_CONCATENATE_BASE_AND_DATA, ¶ms, + sizeof(params) }; + CK_ATTRIBUTE genTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_SENSITIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_EXTRACTABLE, &ckFalse, sizeof(ckFalse)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE_LEN, &genLen, sizeof(genLen)}, + }; + CK_ULONG genCnt = sizeof(genTmpl) / sizeof(*genTmpl); + + ret = concat_create_key(session, a, sizeof(a), CK_FALSE, CK_TRUE, CK_TRUE, + CK_FALSE, &hA); + CHECK_CKR(ret, "Create plain key"); + if (ret == CKR_OK) { + ret = funcList->C_GenerateKey(session, &genMech, genTmpl, genCnt, + &hG1); + CHECK_CKR(ret, "Generate protected key 1"); + } + if (ret == CKR_OK) { + ret = funcList->C_GenerateKey(session, &genMech, genTmpl, genCnt, + &hG2); + CHECK_CKR(ret, "Generate protected key 2"); + } + if (ret == CKR_OK) { + hOther = hG1; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR(ret, "Concatenate plain base and protected key"); + } + if (ret == CKR_OK) { + ret = concat_check_bools(session, hKey, CK_TRUE, CK_FALSE, CK_FALSE, + CK_FALSE); + } + if (ret == CKR_OK) { + hOther = hA; + ret = concat_derive(session, &mech, hG1, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR(ret, "Concatenate protected base and plain key"); + } + if (ret == CKR_OK) { + ret = concat_check_bools(session, hKey, CK_TRUE, CK_FALSE, CK_FALSE, + CK_FALSE); + } + if (ret == CKR_OK) { + hOther = hG2; + ret = concat_derive(session, &mech, hG1, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR(ret, "Concatenate two protected keys"); + } + if (ret == CKR_OK) { + ret = concat_check_bools(session, hKey, CK_TRUE, CK_FALSE, CK_TRUE, + CK_TRUE); + } + if (ret == CKR_OK) { + ret = concat_derive(session, &dataMech, hG1, CKK_GENERIC_SECRET, 0, + &hKey); + CHECK_CKR(ret, "Concatenate protected base and data"); + } + if (ret == CKR_OK) { + ret = concat_check_bools(session, hKey, CK_TRUE, CK_FALSE, CK_TRUE, + CK_TRUE); + } + if (ret == CKR_OK) { + dataMech.mechanism = CKM_CONCATENATE_DATA_AND_BASE; + ret = concat_derive(session, &dataMech, hG1, CKK_GENERIC_SECRET, + sizeof(a), &hKey); + CHECK_CKR(ret, "Concatenate data and protected base, data only"); + } + if (ret == CKR_OK) { + ret = concat_check_bools(session, hKey, CK_TRUE, CK_FALSE, CK_FALSE, + CK_FALSE); + } + if (ret == CKR_OK) { + ret = concat_derive(session, &dataMech, hG1, CKK_GENERIC_SECRET, + sizeof(a) + 1, &hKey); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, + "Concatenate data and one protected base byte"); + } + if (ret == CKR_OK) { + dataMech.mechanism = CKM_CONCATENATE_BASE_AND_DATA; + ret = concat_derive(session, &dataMech, hG1, CKK_GENERIC_SECRET, + genLen - 1, &hKey); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, + "Concatenate truncated protected base and data"); + } + if (ret == CKR_OK) { + hOther = hG1; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, + sizeof(a) + 1, &hKey); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, + "Concatenate plain base and one protected key byte"); + } + if (ret == CKR_OK) { + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, + sizeof(a) - 1, &hKey); + CHECK_CKR(ret, "Concatenate truncated plain base, protected key " + "dropped"); + } + if (ret == CKR_OK) { + ret = concat_check_bools(session, hKey, CK_TRUE, CK_FALSE, CK_FALSE, + CK_FALSE); + } + + if (hG2 != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hG2); + if (hG1 != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hG1); + if (hA != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hA); + + return ret; +} + +static CK_RV test_concatenate_errors(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_BYTE a[16] = { 0 }; + CK_OBJECT_HANDLE hA = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hNoDerive = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hData = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hOther = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hKey; + CK_MECHANISM mech = { CKM_CONCATENATE_BASE_AND_KEY, &hOther, + sizeof(hOther) }; + CK_KEY_DERIVATION_STRING_DATA params = { NULL, 4 }; + CK_MECHANISM dataMech = { CKM_CONCATENATE_DATA_AND_BASE, ¶ms, + sizeof(params) }; + CK_MECHANISM_INFO info; + CK_ATTRIBUTE dataTmpl[] = { + {CKA_CLASS, &dataClass, sizeof(dataClass)}, + {CKA_VALUE, a, sizeof(a)}, + }; + CK_ULONG valueLen = 0; + CK_ATTRIBUTE lenTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_VALUE_LEN, &valueLen, sizeof(valueLen)}, + }; + CK_ULONG lenCnt = sizeof(lenTmpl) / sizeof(*lenTmpl); + CK_BYTE* big = NULL; + CK_ULONG maxLen = 0; + CK_OBJECT_HANDLE hBig = CK_INVALID_HANDLE; +#ifdef HAVE_ECC + CK_OBJECT_HANDLE hEcPub = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hEcPriv = CK_INVALID_HANDLE; +#endif +#ifndef NO_AES + CK_BYTE zero[24] = { 0 }; +#endif + + ret = funcList->C_GetMechanismInfo(slot, CKM_CONCATENATE_BASE_AND_KEY, + &info); + CHECK_CKR(ret, "Concatenate mechanism info"); + if (ret == CKR_OK && ((info.flags & CKF_DERIVE) == 0 || + info.ulMinKeySize != 8 || info.ulMaxKeySize % 8 != 0 || + info.ulMaxKeySize < 64 * 8)) { + ret = CKR_MECHANISM_INVALID; + CHECK_CKR(ret, "Concatenate mechanism info values"); + } + if (ret == CKR_OK) { + maxLen = info.ulMaxKeySize / 8; + big = (CK_BYTE*)XMALLOC(maxLen + 1, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (big == NULL) + ret = CKR_DEVICE_MEMORY; + CHECK_CKR(ret, "Allocate maximum length data"); + } + if (ret == CKR_OK) + XMEMSET(big, 0x5a, maxLen + 1); + if (ret == CKR_OK) { + ret = concat_create_key(session, a, sizeof(a), CK_FALSE, CK_TRUE, + CK_TRUE, CK_FALSE, &hA); + CHECK_CKR(ret, "Create base key"); + } + if (ret == CKR_OK) { + ret = concat_create_key(session, a, sizeof(a), CK_FALSE, CK_TRUE, + CK_FALSE, CK_FALSE, &hNoDerive); + CHECK_CKR(ret, "Create key without derive"); + } + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, dataTmpl, + sizeof(dataTmpl) / sizeof(*dataTmpl), + &hData); + CHECK_CKR(ret, "Create data object"); + } + if (ret == CKR_OK) { + hOther = hA + 1000; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR_FAIL(ret, CKR_KEY_HANDLE_INVALID, "Unknown other key"); + } + if (ret == CKR_OK) { + hOther = hData; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR_FAIL(ret, CKR_KEY_TYPE_INCONSISTENT, "Other key is data"); + } +#ifdef HAVE_ECC + if (ret == CKR_OK) { + ret = gen_ec_keys(session, ecc_p256_params, sizeof(ecc_p256_params), + &hEcPub, &hEcPriv, NULL, 0, NULL, 0, 0); + } + if (ret == CKR_OK) { + hOther = hA; + ret = concat_derive(session, &mech, hEcPriv, CKK_GENERIC_SECRET, 0, + &hKey); + CHECK_CKR_FAIL(ret, CKR_KEY_TYPE_INCONSISTENT, "Base key is EC"); + } +#endif + if (ret == CKR_OK) { + hOther = hA; + mech.pParameter = NULL; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, "NULL parameter"); + mech.pParameter = &hOther; + } + if (ret == CKR_OK) { + valueLen = 0; + ret = funcList->C_DeriveKey(session, &mech, hA, lenTmpl, lenCnt, + &hKey); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, "Zero length"); + } + if (ret == CKR_OK) { + valueLen = 16; + lenTmpl[2].ulValueLen = sizeof(valueLen) - 1; + ret = funcList->C_DeriveKey(session, &mech, hA, lenTmpl, lenCnt, + &hKey); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, "Malformed length"); + lenTmpl[2].ulValueLen = sizeof(valueLen); + } +#if !defined(WOLFPKCS11_NSS) && !defined(WP11_NSS_PERMISSIVE_KEY_DEFAULTS) + if (ret == CKR_OK) { + hOther = hNoDerive; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR_FAIL(ret, CKR_KEY_FUNCTION_NOT_PERMITTED, + "Other key without derive"); + } +#endif + if (ret == CKR_OK) { + hOther = hA; + mech.ulParameterLen = sizeof(hOther) - 1; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, + "Bad other key parameter length"); + mech.ulParameterLen = sizeof(hOther); + } + if (ret == CKR_OK) { + ret = concat_derive(session, &dataMech, hA, CKK_GENERIC_SECRET, 0, + &hKey); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, "NULL data"); + } + if (ret == CKR_OK) { + params.pData = a; + dataMech.ulParameterLen = sizeof(params) - 1; + ret = concat_derive(session, &dataMech, hA, CKK_GENERIC_SECRET, 0, + &hKey); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, + "Bad data parameter length"); + dataMech.ulParameterLen = sizeof(params); + } + if (ret == CKR_OK) { + params.pData = big; + params.ulLen = (CK_ULONG)-1; + ret = concat_derive(session, &dataMech, hA, CKK_GENERIC_SECRET, 16, + &hKey); + CHECK_CKR_FAIL(ret, CKR_KEY_SIZE_RANGE, "Data length overflow"); + } + if (ret == CKR_OK) { + params.ulLen = maxLen + 1; + ret = concat_derive(session, &dataMech, hA, CKK_GENERIC_SECRET, 16, + &hKey); + CHECK_CKR(ret, "Data longer than a key, truncated"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, big, 16); + if (ret == CKR_OK) { + ret = concat_create_key(session, big, maxLen / 2 + 1, CK_FALSE, + CK_TRUE, CK_TRUE, CK_FALSE, &hBig); + CHECK_CKR(ret, "Create half maximum length key"); + } + if (ret == CKR_OK) { + hOther = hBig; + ret = concat_derive(session, &mech, hBig, CKK_GENERIC_SECRET, 0, + &hKey); + CHECK_CKR_FAIL(ret, CKR_KEY_SIZE_RANGE, "Two keys too long"); + } + if (ret == CKR_OK) { + params.ulLen = maxLen; + ret = concat_derive(session, &dataMech, hA, CKK_GENERIC_SECRET, 0, + &hKey); + CHECK_CKR_FAIL(ret, CKR_KEY_SIZE_RANGE, "Concatenation too long"); + } + if (ret == CKR_OK) { + ret = concat_derive(session, &dataMech, hA, CKK_GENERIC_SECRET, 32, + &hKey); + CHECK_CKR(ret, "Concatenation too long, truncated"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, big, 32); + params.pData = a; + params.ulLen = sizeof(a); +#ifndef NO_AES + if (ret == CKR_OK) { + params.ulLen = 4; + ret = concat_derive(session, &dataMech, hA, CKK_AES, 0, &hKey); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCOMPLETE, + "AES key without length from 20 bytes"); + } +#ifndef WOLFPKCS11_NSS + if (ret == CKR_OK) { + ret = concat_derive(session, &dataMech, hA, CKK_AES, 20, &hKey); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, + "AES key of 20 bytes"); + } +#endif + params.ulLen = sizeof(a); + if (ret == CKR_OK) { + ret = concat_derive(session, &dataMech, hA, CKK_AES, 24, &hKey); + CHECK_CKR(ret, "AES-192 key from 32 bytes"); + } + if (ret == CKR_OK) + ret = concat_check_value(session, hKey, zero, sizeof(zero)); +#endif + +#ifdef HAVE_ECC + if (hEcPriv != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hEcPriv); + if (hEcPub != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hEcPub); +#endif + if (hBig != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hBig); + XFREE(big, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (hData != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hData); + if (hNoDerive != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hNoDerive); + if (hA != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hA); + + return ret; +} + +static CK_RV test_concatenate_wrap_with_trusted(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_BYTE a[16] = { 0 }; + CK_BBOOL trusted; + CK_OBJECT_HANDLE hA = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hT = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hOther = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hKey; + CK_MECHANISM mech = { CKM_CONCATENATE_BASE_AND_KEY, &hOther, + sizeof(hOther) }; + CK_KEY_DERIVATION_STRING_DATA params = { NULL, 0 }; + CK_MECHANISM dataMech = { CKM_CONCATENATE_BASE_AND_DATA, ¶ms, + sizeof(params) }; + CK_ATTRIBUTE tmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_WRAP_WITH_TRUSTED, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE, a, sizeof(a)}, + }; + CK_ATTRIBUTE attr = { CKA_WRAP_WITH_TRUSTED, &trusted, sizeof(trusted) }; + + ret = concat_create_key(session, a, sizeof(a), CK_FALSE, CK_TRUE, CK_TRUE, + CK_FALSE, &hA); + CHECK_CKR(ret, "Create plain key"); + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, tmpl, + sizeof(tmpl) / sizeof(*tmpl), &hT); + CHECK_CKR(ret, "Create wrap with trusted key"); + } + if (ret == CKR_OK) { + ret = concat_derive(session, &dataMech, hT, CKK_GENERIC_SECRET, 0, + &hKey); + CHECK_CKR(ret, "Copy wrap with trusted key"); + } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, hKey, &attr, 1); + CHECK_CKR(ret, "Get copy wrap with trusted"); + funcList->C_DestroyObject(session, hKey); + } + CHECK_COND(ret != CKR_OK || trusted == CK_TRUE, ret, + "Copy keeps wrap with trusted"); + if (ret == CKR_OK) { + hOther = hT; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR(ret, "Concatenate plain key and wrap with trusted key"); + } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, hKey, &attr, 1); + CHECK_CKR(ret, "Get concatenation wrap with trusted"); + funcList->C_DestroyObject(session, hKey); + } + CHECK_COND(ret != CKR_OK || trusted == CK_TRUE, ret, + "Concatenation keeps wrap with trusted of other key"); + if (ret == CKR_OK) { + hOther = hA; + ret = concat_derive(session, &mech, hA, CKK_GENERIC_SECRET, 0, &hKey); + CHECK_CKR(ret, "Concatenate plain keys"); + } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, hKey, &attr, 1); + CHECK_CKR(ret, "Get plain concatenation wrap with trusted"); + funcList->C_DestroyObject(session, hKey); + } + CHECK_COND(ret != CKR_OK || trusted == CK_FALSE, ret, + "Plain concatenation has no wrap with trusted"); + + if (hT != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hT); + if (hA != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hA); + + return ret; +} + +#ifndef WOLFPKCS11_NO_STORE +static CK_RV test_concatenate_logged_out(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_RV loginRet; + CK_BYTE a[16] = { 0 }; + CK_BYTE d[4] = { 0xd0, 0xd1, 0xd2, 0xd3 }; + CK_OBJECT_HANDLE hA = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hS = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; + CK_KEY_DERIVATION_STRING_DATA params = { d, sizeof(d) }; + CK_MECHANISM mech = { CKM_CONCATENATE_BASE_AND_DATA, ¶ms, + sizeof(params) }; + CK_MECHANISM keyMech = { CKM_CONCATENATE_BASE_AND_KEY, &hA, sizeof(hA) }; + CK_ATTRIBUTE sessTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_PRIVATE, &ckFalse, sizeof(ckFalse)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE, d, sizeof(d)}, + }; + CK_ATTRIBUTE keyTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_TOKEN, &ckTrue, sizeof(ckTrue)}, + {CKA_PRIVATE, &ckFalse, sizeof(ckFalse)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE, a, sizeof(a)}, + }; + CK_ATTRIBUTE tmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_PRIVATE, &ckFalse, sizeof(ckFalse)}, + }; + + if (userPinLen == 0) + return CKR_SKIPPED; + + ret = funcList->C_CreateObject(session, keyTmpl, + sizeof(keyTmpl) / sizeof(*keyTmpl), &hA); + CHECK_CKR(ret, "Create public token key"); + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, sessTmpl, + sizeof(sessTmpl) / sizeof(*sessTmpl), + &hS); + CHECK_CKR(ret, "Create public session key"); + } + if (ret == CKR_OK) { + ret = funcList->C_Logout(session); + CHECK_CKR(ret, "Logout"); + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hA, tmpl, + sizeof(tmpl) / sizeof(*tmpl), &hKey); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "Concatenate encoded key after logout"); + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &keyMech, hS, tmpl, + sizeof(tmpl) / sizeof(*tmpl), &hKey); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "Concatenate encoded other key after logout"); + } + loginRet = funcList->C_Login(session, CKU_USER, userPin, userPinLen); + CHECK_CKR(loginRet, "Login again"); + if (ret == CKR_OK) + ret = loginRet; + } + + if (hKey != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hKey); + if (hS != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hS); + if (hA != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hA); + + return ret; +} +#endif + +#if defined(HAVE_ECC) && (defined(HAVE_ALL_CURVES) || defined(HAVE_ECC384)) && \ + !defined(NO_AES) && !defined(WOLFPKCS11_NSS) +static CK_RV test_ecdh_derive_aes_no_value_len(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_BYTE p384Params[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 }; + CK_OBJECT_HANDLE hPub = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPriv = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; + CK_BYTE point[128]; + CK_ATTRIBUTE pointAttr = { CKA_EC_POINT, point, sizeof(point) }; + CK_ECDH1_DERIVE_PARAMS params; + CK_MECHANISM mech = { CKM_ECDH1_DERIVE, ¶ms, sizeof(params) }; + CK_ULONG valueLen = 32; + CK_ATTRIBUTE tmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType)}, + {CKA_VALUE_LEN, &valueLen, sizeof(valueLen)}, + }; + CK_ULONG cnt = sizeof(tmpl) / sizeof(*tmpl); + + ret = gen_ec_keys(session, p384Params, sizeof(p384Params), &hPub, &hPriv, + NULL, 0, NULL, 0, 0); + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, hPub, &pointAttr, 1); + CHECK_CKR(ret, "Get P-384 point"); + } + if (ret == CKR_OK) { + params.kdf = CKD_NULL; + params.pSharedData = NULL; + params.ulSharedDataLen = 0; + params.pPublicData = point; + params.ulPublicDataLen = pointAttr.ulValueLen; + ret = funcList->C_DeriveKey(session, &mech, hPriv, tmpl, cnt - 1, + &hKey); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCOMPLETE, + "AES key without length from P-384 secret"); + } + if (ret == CKR_OK) { + tmpl[2].ulValueLen = sizeof(valueLen) - 1; + ret = funcList->C_DeriveKey(session, &mech, hPriv, tmpl, cnt, &hKey); + CHECK_CKR_FAIL(ret, CKR_BUFFER_TOO_SMALL, + "AES key with short length from P-384 secret"); + tmpl[2].ulValueLen = sizeof(valueLen); + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hPriv, tmpl, cnt, &hKey); + CHECK_CKR(ret, "AES-256 key from P-384 secret"); + } + + if (hKey != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hKey); + if (hPriv != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hPriv); + if (hPub != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hPub); + + return ret; +} +#endif + +#if defined(WOLFPKCS11_HKDF) && defined(WOLFSSL_SHA384) && !defined(NO_AES) && \ + !defined(WOLFPKCS11_NSS) +static CK_RV test_hkdf_derive_aes_no_value_len(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_BYTE ikm[32] = { 0 }; + CK_BYTE salt[16] = { 0 }; + CK_OBJECT_HANDLE hBase = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; + CK_HKDF_PARAMS params = { + CK_TRUE, CK_FALSE, CKM_SHA384_HMAC, CKF_HKDF_SALT_DATA, + salt, sizeof(salt), CK_INVALID_HANDLE, NULL, 0 + }; + CK_MECHANISM mech = { CKM_HKDF_DERIVE, ¶ms, sizeof(params) }; + CK_ATTRIBUTE tmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType)}, + }; + CK_ULONG cnt = sizeof(tmpl) / sizeof(*tmpl); + + ret = concat_create_key(session, ikm, sizeof(ikm), CK_FALSE, CK_TRUE, + CK_TRUE, CK_FALSE, &hBase); + CHECK_CKR(ret, "Create HKDF base key"); + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hBase, tmpl, cnt, &hKey); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCOMPLETE, + "AES key without length from SHA-384 extract"); + } + if (ret == CKR_OK) { + params.prfHashMechanism = CKM_SHA256_HMAC; + ret = funcList->C_DeriveKey(session, &mech, hBase, tmpl, cnt, &hKey); + CHECK_CKR(ret, "AES-256 key from SHA-256 extract"); + } + + if (hKey != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hKey); + if (hBase != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hBase); + + return ret; +} +#endif + +#if defined(WOLFPKCS11_HKDF) && !defined(NO_HMAC) +static CK_RV test_concatenate_hybrid_hkdf(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_BYTE zEcdh[32] = { 0 }; + CK_BYTE zMlkem[32]; + CK_BYTE salt[16]; + CK_BYTE info[10]; + CK_BYTE msg[16]; + CK_BYTE mac[32]; + CK_ULONG macLen = sizeof(mac); + CK_BYTE expMac[32] = { + 0x71, 0xe9, 0x0f, 0x7e, 0xea, 0xec, 0x0f, 0x44, + 0x08, 0x5b, 0x7d, 0x7e, 0xed, 0x2a, 0x38, 0x39, + 0xf6, 0x5b, 0xb9, 0xd1, 0x7d, 0x3a, 0x8e, 0x0a, + 0x4c, 0xbd, 0x84, 0xcf, 0x3b, 0xac, 0xb0, 0x7c + }; + CK_ULONG okmLen = 32; + CK_OBJECT_HANDLE hEcdh = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hMlkem = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hIkm = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hOkm = CK_INVALID_HANDLE; + CK_MECHANISM concatMech = { CKM_CONCATENATE_BASE_AND_KEY, &hMlkem, + sizeof(hMlkem) }; + CK_HKDF_PARAMS hkdfParams = { + CK_TRUE, CK_TRUE, CKM_SHA256_HMAC, CKF_HKDF_SALT_DATA, + salt, sizeof(salt), CK_INVALID_HANDLE, info, sizeof(info) + }; + CK_MECHANISM hkdfMech = { CKM_HKDF_DERIVE, &hkdfParams, + sizeof(hkdfParams) }; + CK_MECHANISM macMech = { CKM_SHA256_HMAC, NULL, 0 }; + CK_ATTRIBUTE ikmTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_SENSITIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_EXTRACTABLE, &ckFalse, sizeof(ckFalse)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)}, + }; + CK_ATTRIBUTE okmTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_SIGN, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE_LEN, &okmLen, sizeof(okmLen)}, + }; +#if !defined(WOLFPKCS11_LEGACY_DERIVE_NO_INHERIT) && !defined(WOLFPKCS11_NSS) + CK_BBOOL got[2]; + CK_ATTRIBUTE protAttrs[] = { + {CKA_SENSITIVE, &got[0], sizeof(CK_BBOOL)}, + {CKA_EXTRACTABLE, &got[1], sizeof(CK_BBOOL)}, + }; +#endif + CK_ULONG i; + + for (i = 0; i < sizeof(zMlkem); i++) + zMlkem[i] = (CK_BYTE)(i + 1); + for (i = 0; i < sizeof(salt); i++) + salt[i] = (CK_BYTE)(0x10 + i); + for (i = 0; i < sizeof(info); i++) + info[i] = (CK_BYTE)(0xf0 + i); + for (i = 0; i < sizeof(msg); i++) + msg[i] = (CK_BYTE)(0x40 + i); + + ret = concat_create_key(session, zEcdh, sizeof(zEcdh), CK_TRUE, CK_FALSE, + CK_TRUE, CK_FALSE, &hEcdh); + CHECK_CKR(ret, "Create ECDH secret"); + if (ret == CKR_OK) { + ret = concat_create_key(session, zMlkem, sizeof(zMlkem), CK_TRUE, + CK_FALSE, CK_TRUE, CK_FALSE, &hMlkem); + CHECK_CKR(ret, "Create ML-KEM secret"); + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &concatMech, hEcdh, ikmTmpl, + sizeof(ikmTmpl) / sizeof(*ikmTmpl), &hIkm); + CHECK_CKR(ret, "Concatenate hybrid secrets"); + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &hkdfMech, hIkm, okmTmpl, + sizeof(okmTmpl) / sizeof(*okmTmpl), &hOkm); + CHECK_CKR(ret, "HKDF from concatenated secret"); + } +#if !defined(WOLFPKCS11_LEGACY_DERIVE_NO_INHERIT) && !defined(WOLFPKCS11_NSS) + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, hOkm, protAttrs, 2); + CHECK_CKR(ret, "Get HKDF key protection"); + } + if (ret == CKR_OK && (got[0] != CK_TRUE || got[1] != CK_FALSE)) { + ret = CKR_DATA_INVALID; + CHECK_CKR(ret, "HKDF key inherits protection"); + } +#endif + if (ret == CKR_OK) { + ret = funcList->C_SignInit(session, &macMech, hOkm); + CHECK_CKR(ret, "HMAC sign init"); + } + if (ret == CKR_OK) { + ret = funcList->C_Sign(session, msg, sizeof(msg), mac, &macLen); + CHECK_CKR(ret, "HMAC sign"); + } + if (ret == CKR_OK && (macLen != sizeof(expMac) || + XMEMCMP(mac, expMac, sizeof(expMac)) != 0)) { + ret = CKR_DATA_INVALID; + CHECK_CKR(ret, "HMAC with hybrid HKDF key"); + } + + if (hOkm != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hOkm); + if (hIkm != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hIkm); + if (hMlkem != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hMlkem); + if (hEcdh != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hEcdh); + + return ret; +} +#endif + #ifndef NO_SHA static CK_RV test_x509_check_value(void* args) { @@ -19236,6 +20249,25 @@ static TEST_FUNC testFunc[] = { PKCS11TEST_FUNC_SESS_DECL(test_hkdf_gen_key), PKCS11TEST_FUNC_SESS_DECL(test_hkdf_derive_expand_null_value_len), PKCS11TEST_FUNC_SESS_DECL(test_hkdf_derive_implied_class), +#endif + PKCS11TEST_FUNC_SESS_DECL(test_concatenate_base_and_key), + PKCS11TEST_FUNC_SESS_DECL(test_concatenate_data), + PKCS11TEST_FUNC_SESS_DECL(test_concatenate_protection), + PKCS11TEST_FUNC_SESS_DECL(test_concatenate_errors), + PKCS11TEST_FUNC_SESS_DECL(test_concatenate_wrap_with_trusted), +#ifndef WOLFPKCS11_NO_STORE + PKCS11TEST_FUNC_SESS_DECL(test_concatenate_logged_out), +#endif +#if defined(HAVE_ECC) && (defined(HAVE_ALL_CURVES) || defined(HAVE_ECC384)) && \ + !defined(NO_AES) && !defined(WOLFPKCS11_NSS) + PKCS11TEST_FUNC_SESS_DECL(test_ecdh_derive_aes_no_value_len), +#endif +#if defined(WOLFPKCS11_HKDF) && defined(WOLFSSL_SHA384) && !defined(NO_AES) && \ + !defined(WOLFPKCS11_NSS) + PKCS11TEST_FUNC_SESS_DECL(test_hkdf_derive_aes_no_value_len), +#endif +#if defined(WOLFPKCS11_HKDF) && !defined(NO_HMAC) + PKCS11TEST_FUNC_SESS_DECL(test_concatenate_hybrid_hkdf), #endif #ifdef WOLFSSL_HAVE_PRF #ifndef NO_MD5 diff --git a/tests/pkcs11v3test.c b/tests/pkcs11v3test.c index 3b3857a6..9a563482 100644 --- a/tests/pkcs11v3test.c +++ b/tests/pkcs11v3test.c @@ -2423,6 +2423,257 @@ static CK_RV test_copy_object_mlkem_key(void* args) return ret; } + +#if defined(HAVE_ECC) && defined(WOLFPKCS11_HKDF) && !defined(NO_HMAC) +static CK_OBJECT_CLASS hybridSecretClass = CKO_SECRET_KEY; +static CK_KEY_TYPE hybridGenericType = CKK_GENERIC_SECRET; +static CK_BBOOL hybridFalse = CK_FALSE; + +static CK_RV hybrid_ec_pair(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE* priv, + CK_BYTE* point, CK_ULONG* pointLen) +{ + CK_RV ret; + CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE; + CK_MECHANISM mech = { CKM_EC_KEY_PAIR_GEN, NULL, 0 }; + CK_ATTRIBUTE pubTmpl[] = { + { CKA_EC_PARAMS, ecc_p256_params, sizeof(ecc_p256_params) }, + }; + CK_ATTRIBUTE privTmpl[] = { + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + }; + CK_ATTRIBUTE pointTmpl = { CKA_EC_POINT, point, *pointLen }; + + ret = funcList->C_GenerateKeyPair(session, &mech, pubTmpl, 1, privTmpl, 1, + &pub, priv); + CHECK_CKR(ret, "Generate EC key pair"); + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, pub, &pointTmpl, 1); + CHECK_CKR(ret, "Get EC point"); + } + if (pub != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, pub); + /* ECDH takes the raw point, so drop the DER OCTET STRING header. */ + if (ret == CKR_OK) { + CHECK_COND(pointTmpl.ulValueLen > 2 && point[0] == 0x04 && + point[1] == pointTmpl.ulValueLen - 2, ret, + "EC point is a DER OCTET STRING"); + } + if (ret == CKR_OK) { + *pointLen = pointTmpl.ulValueLen - 2; + XMEMMOVE(point, point + 2, *pointLen); + } + + return ret; +} + +static CK_RV hybrid_concat(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE base, + CK_OBJECT_HANDLE other, CK_OBJECT_HANDLE* key) +{ + CK_RV ret; + CK_MECHANISM mech = { CKM_CONCATENATE_BASE_AND_KEY, &other, + sizeof(other) }; + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &hybridSecretClass, sizeof(hybridSecretClass) }, + { CKA_KEY_TYPE, &hybridGenericType, sizeof(hybridGenericType) }, + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + }; + + ret = funcList->C_DeriveKey(session, &mech, base, tmpl, + sizeof(tmpl) / sizeof(*tmpl), key); + CHECK_CKR(ret, "Concatenate base and key"); + + return ret; +} + +static CK_RV hybrid_party_mac(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE psk, + CK_OBJECT_HANDLE ecPriv, CK_BYTE* peerPoint, + CK_ULONG peerPointLen, CK_OBJECT_HANDLE kemSecret, + CK_BYTE* mac, CK_ULONG* macLen) +{ + CK_RV ret; + CK_OBJECT_HANDLE zEcdh = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE pskEcdh = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE ikm = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE okm = CK_INVALID_HANDLE; + CK_ULONG ecdhLen = 32; + CK_ULONG okmLen = 32; + CK_ULONG ikmLen = 0; + CK_BYTE value[96]; + CK_BYTE salt[16] = { 0x73, 0x61, 0x6c, 0x74 }; + CK_BYTE info[8] = { 0x69, 0x6e, 0x66, 0x6f }; + CK_BYTE msg[16] = { 0x6d, 0x73, 0x67 }; + CK_ECDH1_DERIVE_PARAMS ecdh; + CK_MECHANISM ecdhMech = { CKM_ECDH1_DERIVE, &ecdh, sizeof(ecdh) }; + CK_HKDF_PARAMS hkdf; + CK_MECHANISM hkdfMech = { CKM_HKDF_DERIVE, &hkdf, sizeof(hkdf) }; + CK_MECHANISM macMech = { CKM_SHA256_HMAC, NULL, 0 }; + CK_ATTRIBUTE ecdhTmpl[] = { + { CKA_CLASS, &hybridSecretClass, sizeof(hybridSecretClass) }, + { CKA_KEY_TYPE, &hybridGenericType, sizeof(hybridGenericType) }, + { CKA_VALUE_LEN, &ecdhLen, sizeof(ecdhLen) }, + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + { CKA_SENSITIVE, &ckTrue, sizeof(ckTrue) }, + { CKA_EXTRACTABLE, &hybridFalse, sizeof(hybridFalse) }, + }; + CK_ATTRIBUTE okmTmpl[] = { + { CKA_CLASS, &hybridSecretClass, sizeof(hybridSecretClass) }, + { CKA_KEY_TYPE, &hybridGenericType, sizeof(hybridGenericType) }, + { CKA_VALUE_LEN, &okmLen, sizeof(okmLen) }, + { CKA_SIGN, &ckTrue, sizeof(ckTrue) }, + }; + CK_ATTRIBUTE lenTmpl = { CKA_VALUE_LEN, &ikmLen, sizeof(ikmLen) }; + CK_ATTRIBUTE valueTmpl = { CKA_VALUE, value, sizeof(value) }; + + XMEMSET(&ecdh, 0, sizeof(ecdh)); + ecdh.kdf = CKD_NULL; + ecdh.pPublicData = peerPoint; + ecdh.ulPublicDataLen = peerPointLen; + XMEMSET(&hkdf, 0, sizeof(hkdf)); + hkdf.bExtract = CK_TRUE; + hkdf.bExpand = CK_TRUE; + hkdf.prfHashMechanism = CKM_SHA256_HMAC; + hkdf.ulSaltType = CKF_HKDF_SALT_DATA; + hkdf.pSalt = salt; + hkdf.ulSaltLen = sizeof(salt); + hkdf.pInfo = info; + hkdf.ulInfoLen = sizeof(info); + + ret = funcList->C_DeriveKey(session, &ecdhMech, ecPriv, ecdhTmpl, + sizeof(ecdhTmpl) / sizeof(*ecdhTmpl), &zEcdh); + CHECK_CKR(ret, "ECDH derive"); + if (ret == CKR_OK) + ret = hybrid_concat(session, psk, zEcdh, &pskEcdh); + if (zEcdh != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, zEcdh); + if (ret == CKR_OK) + ret = hybrid_concat(session, pskEcdh, kemSecret, &ikm); + if (pskEcdh != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, pskEcdh); + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, ikm, &lenTmpl, 1); + CHECK_CKR(ret, "Get hybrid IKM length"); + } + CHECK_COND(ret != CKR_OK || ikmLen == 96, ret, "Hybrid IKM is 96 bytes"); + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, ikm, &valueTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "Hybrid IKM value is not readable"); + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &hkdfMech, ikm, okmTmpl, + sizeof(okmTmpl) / sizeof(*okmTmpl), &okm); + CHECK_CKR(ret, "HKDF from hybrid IKM"); + } + if (ikm != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, ikm); + if (ret == CKR_OK) { + ret = funcList->C_SignInit(session, &macMech, okm); + CHECK_CKR(ret, "HMAC sign init with OKM"); + } + if (ret == CKR_OK) { + ret = funcList->C_Sign(session, msg, sizeof(msg), mac, macLen); + CHECK_CKR(ret, "HMAC sign with OKM"); + } + + if (okm != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, okm); + + return ret; +} + +static CK_RV test_concatenate_hybrid_agreement(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_FUNCTION_LIST_3_2* funcListExt = (CK_FUNCTION_LIST_3_2*)funcList; + CK_OBJECT_HANDLE psk = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE ecPrivA = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE ecPrivB = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE kemPub = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE kemPriv = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE ssA = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE ssB = CK_INVALID_HANDLE; + CK_BYTE pointA[80]; + CK_BYTE pointB[80]; + CK_ULONG pointALen = sizeof(pointA); + CK_ULONG pointBLen = sizeof(pointB); + CK_BYTE ct[WC_ML_KEM_512_CIPHER_TEXT_SIZE]; + CK_ULONG ctLen = sizeof(ct); + CK_BYTE pskValue[32]; + CK_BYTE macA[32]; + CK_BYTE macB[32]; + CK_ULONG macALen = sizeof(macA); + CK_ULONG macBLen = sizeof(macB); + CK_MECHANISM kemMech = { CKM_ML_KEM, NULL, 0 }; + CK_ATTRIBUTE pskTmpl[] = { + { CKA_CLASS, &hybridSecretClass, sizeof(hybridSecretClass) }, + { CKA_KEY_TYPE, &hybridGenericType, sizeof(hybridGenericType) }, + { CKA_VALUE, pskValue, sizeof(pskValue) }, + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + { CKA_SENSITIVE, &ckTrue, sizeof(ckTrue) }, + { CKA_EXTRACTABLE, &hybridFalse, sizeof(hybridFalse) }, + }; + CK_ATTRIBUTE ssTmpl[] = { + { CKA_CLASS, &hybridSecretClass, sizeof(hybridSecretClass) }, + { CKA_KEY_TYPE, &hybridGenericType, sizeof(hybridGenericType) }, + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + { CKA_SENSITIVE, &ckTrue, sizeof(ckTrue) }, + { CKA_EXTRACTABLE, &hybridFalse, sizeof(hybridFalse) }, + }; + CK_ULONG ssCnt = sizeof(ssTmpl) / sizeof(*ssTmpl); + + XMEMSET(pskValue, 0x5a, sizeof(pskValue)); + ret = funcList->C_CreateObject(session, pskTmpl, + sizeof(pskTmpl) / sizeof(*pskTmpl), &psk); + CHECK_CKR(ret, "Create PSK"); + if (ret == CKR_OK) + ret = hybrid_ec_pair(session, &ecPrivA, pointA, &pointALen); + if (ret == CKR_OK) + ret = hybrid_ec_pair(session, &ecPrivB, pointB, &pointBLen); + if (ret == CKR_OK) { + ret = gen_mlkem_keys(session, CKP_ML_KEM_512, &kemPub, &kemPriv, NULL, + 0, NULL, 0, 0); + } + if (ret == CKR_OK) { + ret = funcListExt->C_EncapsulateKey(session, &kemMech, kemPub, ssTmpl, + ssCnt, ct, &ctLen, &ssA); + CHECK_CKR(ret, "ML-KEM encapsulate"); + } + if (ret == CKR_OK) { + ret = funcListExt->C_DecapsulateKey(session, &kemMech, kemPriv, ssTmpl, + ssCnt, ct, ctLen, &ssB); + CHECK_CKR(ret, "ML-KEM decapsulate"); + } + if (kemPriv != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, kemPriv); + if (kemPub != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, kemPub); + if (ret == CKR_OK) { + ret = hybrid_party_mac(session, psk, ecPrivA, pointB, pointBLen, ssA, + macA, &macALen); + } + if (ret == CKR_OK) { + ret = hybrid_party_mac(session, psk, ecPrivB, pointA, pointALen, ssB, + macB, &macBLen); + } + CHECK_COND(ret != CKR_OK || (macALen == macBLen && + XMEMCMP(macA, macB, macALen) == 0), ret, + "Both parties derive the same key"); + + if (ssB != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, ssB); + if (ssA != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, ssA); + if (ecPrivB != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, ecPrivB); + if (ecPrivA != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, ecPrivA); + if (psk != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, psk); + + return ret; +} +#endif #endif /* WOLFPKCS11_MLKEM */ #endif /* WOLFPKCS11_PKCS11_V3_2 */ @@ -4040,6 +4291,9 @@ static TEST_FUNC testFunc[] = { PKCS11TEST_FUNC_SESS_DECL(test_mlkem_initial_states), PKCS11TEST_FUNC_SESS_DECL(test_mlkem_encap_decap_not_permitted), PKCS11TEST_FUNC_SESS_DECL(test_copy_object_mlkem_key), +#if defined(HAVE_ECC) && defined(WOLFPKCS11_HKDF) && !defined(NO_HMAC) + PKCS11TEST_FUNC_SESS_DECL(test_concatenate_hybrid_agreement), +#endif #endif #ifdef WOLFPKCS11_LMS PKCS11TEST_FUNC_SESS_DECL(test_hss_verify_kat), diff --git a/wolfpkcs11/internal.h b/wolfpkcs11/internal.h index ca3e6c2f..1c269517 100644 --- a/wolfpkcs11/internal.h +++ b/wolfpkcs11/internal.h @@ -353,6 +353,8 @@ C_EXTRA_FLAGS="-DWOLFSSL_PUBLIC_MP -DWC_RSA_DIRECT" #define WP11_MAX_SYM_KEY_SZ (2048) #elif !defined(NO_DH) #define WP11_MAX_SYM_KEY_SZ (4096/8) +#elif defined(WOLFPKCS11_MLKEM) +#define WP11_MAX_SYM_KEY_SZ 256 #elif defined(HAVE_ECC) #define WP11_MAX_SYM_KEY_SZ ((521+7)/8) #else @@ -720,6 +722,10 @@ WP11_LOCAL int WP11_GenerateRandomKey(WP11_Object* secret, WP11_Slot* slot, WP11_LOCAL int WP11_KDF_Derive(WP11_Session* session, CK_HKDF_PARAMS_PTR params, unsigned char* key, word32* keyLen, WP11_Object* priv); +WP11_LOCAL int WP11_Concat_KeyLen(WP11_Object* key, word32* len); +WP11_LOCAL int WP11_Concat_Derive(WP11_Object* base, WP11_Object* other, + const byte* data, word32 dataLen, + int dataFirst, byte* out, word32 outLen); WP11_LOCAL int WP11_Tls12_Master_Key_Derive(CK_SSL3_RANDOM_DATA* random, CK_MECHANISM_TYPE mech, const char* label, diff --git a/wolfpkcs11/pkcs11.h b/wolfpkcs11/pkcs11.h index 94f3c389..10d72dc6 100644 --- a/wolfpkcs11/pkcs11.h +++ b/wolfpkcs11/pkcs11.h @@ -341,6 +341,9 @@ extern "C" { #define CKM_SHA3_512 0x000002D0UL #define CKM_SHA3_512_HMAC 0x000002D1UL #define CKM_GENERIC_SECRET_KEY_GEN 0x00000350UL +#define CKM_CONCATENATE_BASE_AND_KEY 0x00000360UL +#define CKM_CONCATENATE_BASE_AND_DATA 0x00000362UL +#define CKM_CONCATENATE_DATA_AND_BASE 0x00000363UL #define CKM_SSL3_MASTER_KEY_DERIVE 0x00000371UL #define CKM_TLS_PRF 0x00000378UL #define CKM_PKCS5_PBKD2 0x000003B0UL @@ -714,6 +717,12 @@ typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS { } CK_AES_CBC_ENCRYPT_DATA_PARAMS; typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS* CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR; +typedef struct CK_KEY_DERIVATION_STRING_DATA { + CK_BYTE_PTR pData; + CK_ULONG ulLen; +} CK_KEY_DERIVATION_STRING_DATA; +typedef CK_KEY_DERIVATION_STRING_DATA* CK_KEY_DERIVATION_STRING_DATA_PTR; + typedef CK_ULONG CK_MAC_GENERAL_PARAMS; typedef CK_MAC_GENERAL_PARAMS* CK_MAC_GENERAL_PARAMS_PTR; From fe70490f283cc186543365a2e95e84da23a3aab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Fri, 30 Oct 2026 17:24:28 +0100 Subject: [PATCH 2/2] Refuse to use token keys whose value is not decoded Encrypted token objects (RSA, EC, DH, ML-DSA and ML-KEM private keys, AES, generic secret and HKDF keys) are encoded at logout: the key value in memory is zeroed and only the encrypted copy is kept. Objects loaded from the store stay undecoded until the user logs in. A token key with CKA_PRIVATE false is still visible in a public session in both states, and every operation except concatenation used it as is. HMAC sign and verify, AES encrypt and decrypt, ECDSA sign, C_WrapKey, HKDF and C_DigestKey all returned CKR_OK while working with an all-zero key. Check the key state before using a key: - CheckOpSupported, so every operation init and derive base key. The check runs after the usage flag check, so a key without the flag still gives CKR_KEY_FUNCTION_NOT_PERMITTED. - The key being wrapped in C_WrapKey. - The HKDF salt key, when it is a secret key. - C_DigestKey, under the object lock. The digest operation is aborted on failure. - The source object of C_CopyObject. The copy kept the encoded key data but could never decode it. An undecoded key gives CKR_USER_NOT_LOGGED_IN when no user is logged in, and CKR_FUNCTION_FAILED when the user is logged in, as the stored key then failed to decrypt. The concatenate mechanisms now share the same state check. C_GetAttributeValue returned the zeroed CKA_VALUE of a non-sensitive, extractable key with CKR_OK. While a key is encoded or undecoded, each attribute held in its encrypted record now gives CKR_ATTRIBUTE_SENSITIVE: CKA_VALUE and the check value of a secret key, CKA_VALUE of a DH or EC private key, the modulus, modulus bits and public exponent of an RSA private key, the EC parameters and EC point of an EC private key, and the parameter set of an ML-DSA or ML-KEM private key. Some of these survived logout, depending on what the wolfCrypt free functions keep, but none exist for a key that was loaded and never decoded, so a key would answer differently before the first login than after a logout. Reading the EC parameters of such a key dereferenced a NULL curve. CKA_VALUE_LEN, which follows from the stored record length, and the DH domain parameters, which are stored in the clear, stay readable. The key length is now set when a secret key is loaded, as it read 0 until the first login. Objects that are not encrypted, such as public keys, were also decoded only at login. Before the first login a public key had no key data, and reading CKA_EC_POINT of an EC public key dereferenced a NULL curve. These objects are now decoded when the token is loaded, and login decodes them again only when that failed, so a damaged record still fails the login. An operation started before C_Logout kept running afterwards with the scrubbed key. AES-GCM and TLS MAC used the zero key and returned CKR_OK. Logout now drops the session references to a key it scrubbed, in the same way C_DestroyObject does for a destroyed key. Asymmetric operations, AES-GCM, AES-CCM, AES-ECB, TLS MAC and the NSS TLS PRF read the key while they run, so their reference is dropped. The owning session then releases the operation state on its next call and returns CKR_OPERATION_NOT_INITIALIZED, or starts the new operation it was asked for. AES-CBC, HMAC and the other operations that copy the key at init keep running with the right key, as after a destroy, since wolfCrypt's PKCS#11 layer destroys the key before it finishes a multi-part HMAC. A digest started after an operation that used the key keeps running. An init that runs at the same time as C_Logout can still pick up the key before it is scrubbed. Closing that needs a state check where each key is read and is left for a follow-up. Destroy used to free another session's RSA-OAEP label and reset its operation state from the destroying thread. The owning thread could then free the label a second time at the end of a decrypt, and hash contexts were dropped without being freed. Only the owning session changes its operation state now. A failed RSA-OAEP encrypt or decrypt left its label allocated, and it leaked once the session moved on to another mechanism. The label is now freed whatever the result. test_wrap_key_wrap_with_trusted created its CKA_TRUSTED wrapping key as a token object in an SO session. Such a key is encrypted under the user token key, which an SO login never sets, so it never decodes for the user and the test wrapped with a zero key. The test now wraps with a public session object, which is not encrypted, and checks that the SO token key fails with CKR_FUNCTION_FAILED rather than being used. SO created token keys need a separate fix. --- src/crypto.c | 53 ++- src/internal.c | 349 +++++++++----- tests/pkcs11str.c | 205 +++++++- tests/pkcs11test.c | 1052 ++++++++++++++++++++++++++++++++++++++++- tests/pkcs11v3test.c | 128 +++++ wolfpkcs11/internal.h | 2 + 6 files changed, 1658 insertions(+), 131 deletions(-) diff --git a/src/crypto.c b/src/crypto.c index 5e762ddf..c6ff2a32 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -574,6 +574,21 @@ static CK_RV CheckAttributes(CK_ATTRIBUTE* pTemplate, CK_ULONG ulCount, int set) return CKR_OK; } +static CK_RV UndecodedKeyRv(WP11_Slot* slot) +{ + /* A key still undecoded while logged in failed to verify. */ + if (WP11_Slot_IsUserLoggedIn(slot)) + return CKR_FUNCTION_FAILED; + return CKR_USER_NOT_LOGGED_IN; +} + +static CK_RV CheckKeyDecoded(WP11_Object* obj) +{ + if (WP11_Object_KeyState(obj) != 0) + return UndecodedKeyRv(WP11_Object_GetSlot(obj)); + return CKR_OK; +} + /* A certificate's type shares the key type field, so only key objects may be * used as keys. A data object may seed a derivation. */ static int CheckKeyObject(WP11_Object* obj, CK_ATTRIBUTE_TYPE op) @@ -603,7 +618,7 @@ static int CheckOpSupportedRv(WP11_Object* obj, CK_ATTRIBUTE_TYPE op, WOLFPKCS11_MSG("Operation not supported by object"); return (int)errRv; } - return CKR_OK; + return (int)CheckKeyDecoded(obj); } static int CheckOpSupported(WP11_Object* obj, CK_ATTRIBUTE_TYPE op) @@ -1042,14 +1057,6 @@ static CK_RV CheckAttributeUpdate(WP11_Session* session, WP11_Object* obj, return CKR_OK; } -static CK_RV UndecodedKeyRv(WP11_Session* session) -{ - /* Logged in yet undecoded: the stored key failed to verify. */ - if (WP11_Slot_IsUserLoggedIn(WP11_Session_GetSlot(session))) - return CKR_FUNCTION_FAILED; - return CKR_USER_NOT_LOGGED_IN; -} - /** * Set the values of the attributes into the object. * @@ -1346,7 +1353,7 @@ static CK_RV SetAttributeValue(WP11_Session* session, WP11_Object* obj, else if (ret == BUFFER_E) return CKR_BUFFER_TOO_SMALL; else if (ret == BAD_STATE_E) - return UndecodedKeyRv(session); + return UndecodedKeyRv(WP11_Session_GetSlot(session)); else if (ret != 0) return CKR_FUNCTION_FAILED; } @@ -2115,6 +2122,11 @@ static CK_RV wp11_C_CopyObject(CK_SESSION_HANDLE hSession, WOLFPKCS11_LEAVE("C_CopyObject", rv); return rv; } + rv = CheckKeyDecoded(obj); + if (rv != CKR_OK) { + WOLFPKCS11_LEAVE("C_CopyObject", rv); + return rv; + } /* Creating a private object requires an authenticated user session. The * copy template can set CKA_PRIVATE=CK_TRUE, so gate it like @@ -5525,6 +5537,10 @@ static CK_RV wp11_C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) ret = WP11_Digest_Key(obj, session); + if (ret == BAD_STATE_E) { + WP11_Session_AbortOp(session); + return UndecodedKeyRv(WP11_Session_GetSlot(session)); + } if (ret < 0) { WP11_Session_AbortOp(session); return CKR_FUNCTION_FAILED; @@ -9644,6 +9660,9 @@ static CK_RV wp11_C_WrapKey(CK_SESSION_HANDLE hSession, if (rv != CKR_OK) { return rv; } + rv = CheckKeyDecoded(key); + if (rv != CKR_OK) + return rv; switch (keyType) { #if !defined(NO_RSA) && !defined(WOLFPKCS11_NO_STORE) @@ -10351,7 +10370,7 @@ static CK_RV ConcatKeyRv(WP11_Session* session, int ret) if (ret == 0) return CKR_OK; if (ret == BAD_STATE_E) - return UndecodedKeyRv(session); + return UndecodedKeyRv(WP11_Session_GetSlot(session)); if (ret == NOT_AVAILABLE_E) return CKR_KEY_FUNCTION_NOT_PERMITTED; return CKR_FUNCTION_FAILED; @@ -10508,6 +10527,8 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, ret = CheckOpSupported(obj, CKA_DERIVE); #else ret = CheckKeyObject(obj, CKA_DERIVE); + if (ret == CKR_OK) + ret = (int)CheckKeyDecoded(obj); #endif if (ret != CKR_OK) return ret; @@ -10562,6 +10583,7 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, case CKM_HKDF_DATA: { CK_HKDF_PARAMS_PTR kdfParams; CK_ATTRIBUTE *lenAttr = NULL; + WP11_Object* saltObj = NULL; /* Data-derive feeds a CKO_DATA object, which has no key type. * Only a key base object is checked for an HKDF-compatible type. */ @@ -10583,6 +10605,15 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, if (kdfParams->bExpand && !CK_ULONG_FITS_WORD32(kdfParams->ulInfoLen)) return CKR_MECHANISM_PARAM_INVALID; + if (kdfParams->bExtract && + kdfParams->ulSaltType == CKF_HKDF_SALT_KEY && + WP11_Object_Find(session, kdfParams->hSaltKey, + &saltObj) == 0 && + WP11_Object_GetClass(saltObj) == CKO_SECRET_KEY) { + rv = CheckKeyDecoded(saltObj); + if (rv != CKR_OK) + return rv; + } FindAttributeType(pTemplate, ulAttributeCount, CKA_VALUE_LEN, &lenAttr); diff --git a/src/internal.c b/src/internal.c index 9d04984d..57715039 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7795,37 +7795,50 @@ static int XmssObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, #endif /* WOLFPKCS11_XMSS */ /** - * Decode the symmetric key - requires decryption. + * Get the length of the secret in a stored symmetric key record. * - * @param [in, out] object Symmetric key object. + * @param [in] object Symmetric key object. + * @param [out] len Length of the secret in bytes. * @return 0 on success. - * @return -ve on failure. + * @return BAD_FUNC_ARG when the record is shorter than the tag. + * @return BUFFER_E when the secret does not fit the key buffer. */ -static int wp11_Object_Decode_SymmKey(WP11_Object* object) +static int wp11_Object_SymmKeyLen(WP11_Object* object, word32* len) { - int ret = 0; - /* keyDataLen is signed and covers the encrypted payload plus the trailing * AES_BLOCK_SIZE tag. Reject lengths below the tag so the payload length * below cannot go negative when used as an unsigned length. A length of * exactly AES_BLOCK_SIZE is a valid zero-byte secret: the encoder writes * symmKey->len + AES_BLOCK_SIZE, so a zero-length key must still load. */ if (object->keyDataLen < AES_BLOCK_SIZE) - ret = BAD_FUNC_ARG; - if (ret == 0 && - (word32)(object->keyDataLen - AES_BLOCK_SIZE) > WP11_MAX_SYM_KEY_SZ) - ret = BUFFER_E; + return BAD_FUNC_ARG; + if ((word32)(object->keyDataLen - AES_BLOCK_SIZE) > WP11_MAX_SYM_KEY_SZ) + return BUFFER_E; + *len = (word32)(object->keyDataLen - AES_BLOCK_SIZE); + return 0; +} + +/** + * Decode the symmetric key - requires decryption. + * + * @param [in, out] object Symmetric key object. + * @return 0 on success. + * @return -ve on failure. + */ +static int wp11_Object_Decode_SymmKey(WP11_Object* object) +{ + int ret; + word32 len = 0; + + ret = wp11_Object_SymmKeyLen(object, &len); if (ret == 0) { ret = wp11_Object_DecryptKeyData(object, object->data.symmKey->data, - object->keyData, - object->keyDataLen - AES_BLOCK_SIZE); - if (ret != 0) { - wc_ForceZero(object->data.symmKey->data, - (word32)(object->keyDataLen - AES_BLOCK_SIZE)); - } + object->keyData, (int)len); + if (ret != 0) + wc_ForceZero(object->data.symmKey->data, len); } if (ret == 0) - object->data.symmKey->len = object->keyDataLen - AES_BLOCK_SIZE; + object->data.symmKey->len = len; object->encoded = (ret != 0); return ret; @@ -7876,6 +7889,7 @@ static int wp11_Object_Load_SymmKey(WP11_Object* object, int tokenId, int objId) { int ret; void* storage = NULL; + word32 len = 0; /* Open access to symmetric key. */ ret = wp11_storage_open_readonly(WOLFPKCS11_STORE_SYMMKEY, tokenId, objId, @@ -7886,6 +7900,9 @@ static int wp11_Object_Load_SymmKey(WP11_Object* object, int tokenId, int objId) &object->keyDataLen); wp11_storage_close(storage); } + /* CKA_VALUE_LEN is readable before the key is decoded. */ + if (ret == 0 && wp11_Object_SymmKeyLen(object, &len) == 0) + object->data.symmKey->len = len; return ret; } @@ -9359,6 +9376,10 @@ static int wp11_Token_Load(WP11_Slot* slot, int tokenId, WP11_Token* token) for (i = token->objCnt - 1; (ret == 0) && (i >= 0); i--) { /* Load the objects. */ ret = wp11_Object_Load(object, tokenId, i); + /* Objects not under the token key are usable before any login. */ + if (ret == 0 && !wp11_Object_IsEncrypted(object) && + wp11_Object_Decode(object) != 0) + object->undecoded = 1; /* Keep new token object ids above the stored ones, in case the * counter was not stored or was not usable. */ if (ret == 0 && (int)OBJ_HANDLE_OBJ_ID(object->handle) >= @@ -9381,7 +9402,8 @@ static int wp11_Token_Load(WP11_Slot* slot, int tokenId, WP11_Token* token) token->key, sizeof(token->key), slot); object = token->object; while (ret == 0 && object != NULL) { - ret = wp11_Object_Decode(object); + if (wp11_Object_IsEncrypted(object) || object->undecoded) + ret = wp11_Object_Decode(object); object = object->next; } #endif @@ -10215,6 +10237,9 @@ int WP11_Slot_OpenSession(WP11_Slot* slot, unsigned long flags, void* app, } static void wp11_Slot_Logout(WP11_Slot* slot); +#ifndef WOLFPKCS11_NO_STORE +static void wp11_Slot_ClearKeyOps(WP11_Slot* slot, WP11_Object* object); +#endif /** * Close a session associated with a slot. @@ -11166,7 +11191,8 @@ int WP11_Slot_UserLogin(WP11_Slot* slot, char* pin, int pinLen) XMEMCPY(token->key, d->key, sizeof(token->key)); object = token->object; while (ret == 0 && object != NULL) { - ret = wp11_Object_Decode(object); + if (wp11_Object_IsEncrypted(object) || object->undecoded) + ret = wp11_Object_Decode(object); if (ret != 0) failed = object; object = object->next; @@ -11694,6 +11720,11 @@ static void wp11_Slot_Logout(WP11_Slot* slot) /* Zero token key only on user logout — SO logout must preserve it * for subsequent object encryption (e.g., empty-PIN flow). */ wc_ForceZero(slot->token.key, sizeof(slot->token.key)); + for (object = slot->token.object; object != NULL; + object = object->next) { + if (object->encoded) + wp11_Slot_ClearKeyOps(slot, object); + } } #endif #ifndef WOLFPKCS11_NSS @@ -12065,7 +12096,11 @@ static int wp11_init_get_op_category(int init) static int wp11_mech_reads_key(CK_MECHANISM_TYPE mechanism) { return mechanism == CKM_AES_GCM || mechanism == CKM_AES_CCM || - mechanism == CKM_AES_ECB; + mechanism == CKM_AES_ECB || mechanism == CKM_TLS_MAC +#ifdef WOLFPKCS11_NSS + || mechanism == CKM_NSS_TLS_PRF_GENERAL_SHA256 +#endif + ; } /** @@ -12088,9 +12123,34 @@ int WP11_Session_IsOpCategoryInit(WP11_Session* session, int opCategory) return wp11_init_get_op_category(currentInit) == opCategory; } +/** + * Release the operation state of a session whose key was dropped by a destroy + * or a logout. + * + * @param session [in] Session object. + * @return Current object of the session, NULL when there is none. + */ +static WP11_Object* wp11_Session_ReleaseStaleOp(WP11_Session* session) +{ + WP11_Object* curr; + int op; + int abortOp; + + WP11_Lock_LockRO(&session->slot->lock); + curr = session->curr; + op = session->init & ~WP11_INIT_DIGEST_MASK; + abortOp = (curr == NULL && op != 0 && op != WP11_INIT_DIGEST); + WP11_Lock_UnlockRO(&session->slot->lock); + if (abortOp) + WP11_Session_AbortOp(session); + + return curr; +} + /** * Check whether the session has an active operation of the given category. * Per PKCS#11 spec, only operations of the same type block re-initialization. + * An operation whose key was dropped is released first, whatever its category. * * @param session [in] Session object. * @param opCategory [in] Operation category (WP11_OP_*). @@ -12099,10 +12159,7 @@ int WP11_Session_IsOpCategoryInit(WP11_Session* session, int opCategory) */ int WP11_Session_IsOpCategoryActive(WP11_Session* session, int opCategory) { - /* The key of this operation was destroyed so it can no longer run. */ - if (session->curr == NULL && wp11_mech_reads_key(session->mechanism)) - return 0; - + (void)wp11_Session_ReleaseStaleOp(session); return WP11_Session_IsOpCategoryInit(session, opCategory); } @@ -13475,24 +13532,40 @@ WP11_API int WP11_Test_SetObjectClass(CK_SESSION_HANDLE hSession, #endif /* DEBUG_WOLFPKCS11 */ /** - * Drop any session reference to an object that is about to be freed. + * Drop the session's reference to its operation key unless the operation + * copied the key at init. Caller holds the slot lock for writing. * - * The active object pointer (session->curr) is a raw pointer cached when an - * operation is initialized. A token object can be the active object of more - * than one session. Since this reaches into other sessions, session->curr is - * only accessed under the slot lock (see WP11_Session_GetObject()). - * - * Symmetric operations (AES, HMAC/generic-secret, HKDF) copy the key into the - * operation context during their *Init and run to completion without referring - * to the object again, so a caller may legitimately destroy the key before - * finishing the operation (wolfCrypt's PKCS#11 layer does exactly this for - * multi-part HMAC). Those operations are left untouched. AES-GCM, AES-CCM and - * AES-ECB read the key object when data is processed, so only their reference - * is dropped; the owning session releases the operation state itself. + * @param session [in] Session whose operation uses the key. + * @param symmetric [in] Whether the key is a secret key. + */ +static void wp11_Session_ClearActive(WP11_Session* session, int symmetric) +{ + if (!symmetric || wp11_mech_reads_key(session->mechanism)) + session->curr = NULL; +} + +#ifndef WOLFPKCS11_NO_STORE +/** + * Clear the running operations that use a key whose value logout scrubbed. + * Caller holds the slot lock for writing. * - * Asymmetric operations dereference the key object while running, so for those - * key types drop the active reference in every session that holds it and reset - * the operation state, ensuring a later step cannot use freed memory. + * @param slot [in] Slot whose sessions are scanned. + * @param object [in] Token object that is now encoded. + */ +static void wp11_Slot_ClearKeyOps(WP11_Slot* slot, WP11_Object* object) +{ + WP11_Session* sess; + int symmetric = WP11_KeyType_IsSecret(object->type); + + for (sess = slot->session; sess != NULL; sess = sess->next) { + if (sess->curr == object) + wp11_Session_ClearActive(sess, symmetric); + } +} +#endif + +/** + * Drop any session reference to an object that is about to be freed. * * @param slot [in] Slot whose sessions are scanned. * @param object [in] Object being destroyed. @@ -13500,37 +13573,17 @@ WP11_API int WP11_Test_SetObjectClass(CK_SESSION_HANDLE hSession, void WP11_Slot_ClearActiveObject(WP11_Slot* slot, WP11_Object* object) { WP11_Session* curr; - CK_KEY_TYPE keyType; int symmetric; if (slot == NULL || object == NULL) return; - keyType = WP11_Object_GetType(object); - symmetric = WP11_KeyType_IsSecret(keyType); + symmetric = WP11_KeyType_IsSecret(WP11_Object_GetType(object)); WP11_Lock_LockRW(&slot->lock); for (curr = slot->session; curr != NULL; curr = curr->next) { - if (curr->curr == object && symmetric) { - if (wp11_mech_reads_key(curr->mechanism)) - curr->curr = NULL; - } - else if (curr->curr == object) { -#if !defined(NO_RSA) && !defined(WC_NO_RSA_OAEP) - /* Release the operation-owned RSA-OAEP label before dropping the - * operation state. Otherwise a later init for a different mechanism - * overwrites the params union and the label can no longer be - * freed. */ - if (curr->mechanism == CKM_RSA_PKCS_OAEP && - curr->params.oaep.label != NULL) { - XFREE(curr->params.oaep.label, NULL, DYNAMIC_TYPE_TMP_BUFFER); - curr->params.oaep.label = NULL; - curr->params.oaep.labelSz = 0; - } -#endif - curr->curr = NULL; - curr->init = 0; - } + if (curr->curr == object) + wp11_Session_ClearActive(curr, symmetric); } WP11_Lock_UnlockRW(&slot->lock); } @@ -13546,18 +13599,7 @@ void WP11_Slot_ClearActiveObject(WP11_Slot* slot, WP11_Object* object) */ void WP11_Session_GetObject(WP11_Session* session, WP11_Object** object) { - WP11_Object* curr; - int abortOp; - - WP11_Lock_LockRO(&session->slot->lock); - curr = session->curr; - /* The key was destroyed: release the state its operation still holds. */ - abortOp = (curr == NULL && wp11_mech_reads_key(session->mechanism) && - (session->init & ~WP11_INIT_DIGEST_MASK) != 0); - WP11_Lock_UnlockRO(&session->slot->lock); - if (abortOp) - WP11_Session_AbortOp(session); - *object = curr; + *object = wp11_Session_ReleaseStaleOp(session); } /** @@ -14064,6 +14106,49 @@ CK_KEY_TYPE WP11_Object_GetType(WP11_Object* object) return object->type; } +/** + * Get the slot the object belongs to. + * + * @param object [in] Object object. + * @return Slot of the object. + */ +WP11_Slot* WP11_Object_GetSlot(WP11_Object* object) +{ + return object->slot; +} + +/* Call with the object lock held when the object is on the token. */ +static int wp11_Object_KeyStateLocked(WP11_Object* object) +{ +#ifndef WOLFPKCS11_NO_STORE + if (object->encoded || object->undecoded) + return BAD_STATE_E; +#else + (void)object; +#endif + return 0; +} + +/** + * Check that the key value of an object is decoded and can be used. + * + * @param object [in] Object object. + * @return BAD_STATE_E when the key value is not decoded. + * 0 otherwise. + */ +int WP11_Object_KeyState(WP11_Object* object) +{ + int ret; + + if (object->onToken) + WP11_Lock_LockRO(object->lock); + ret = wp11_Object_KeyStateLocked(object); + if (object->onToken) + WP11_Lock_UnlockRO(object->lock); + + return ret; +} + /** * Get the object's devId. * @@ -15399,13 +15484,19 @@ static int RsaObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, int ret = 0; int noPriv = (((object->opFlag & WP11_FLAG_SENSITIVE) != 0) || ((object->opFlag & WP11_FLAG_EXTRACTABLE) == 0)); + int noKey = (wp11_Object_KeyStateLocked(object) != 0); if (mp_iszero(&object->data.rsaKey->d)) noPriv = 1; switch (type) { case CKA_MODULUS: - ret = GetMPIData(&object->data.rsaKey->n, data, len); + if (noKey) { + *len = CK_UNAVAILABLE_INFORMATION; + ret = CKR_ATTRIBUTE_SENSITIVE; + } + else + ret = GetMPIData(&object->data.rsaKey->n, data, len); break; case CKA_PRIVATE_EXPONENT: if (noPriv) { @@ -15456,10 +15547,21 @@ static int RsaObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, ret = GetMPIData(&object->data.rsaKey->u, data, len); break; case CKA_PUBLIC_EXPONENT: - ret = GetMPIData(&object->data.rsaKey->e, data, len); + if (noKey) { + *len = CK_UNAVAILABLE_INFORMATION; + ret = CKR_ATTRIBUTE_SENSITIVE; + } + else + ret = GetMPIData(&object->data.rsaKey->e, data, len); break; case CKA_MODULUS_BITS: - ret = GetULong(mp_count_bits(&object->data.rsaKey->n), data, len); + if (noKey) { + *len = CK_UNAVAILABLE_INFORMATION; + ret = CKR_ATTRIBUTE_SENSITIVE; + } + else + ret = GetULong(mp_count_bits(&object->data.rsaKey->n), data, + len); break; case CKA_WRAP_TEMPLATE: case CKA_UNWRAP_TEMPLATE: @@ -15587,6 +15689,7 @@ static int EcObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, int noPriv = (((object->opFlag & WP11_FLAG_SENSITIVE) != 0) || ((object->opFlag & WP11_FLAG_EXTRACTABLE) == 0)); int noPub = 0; + int noKey = (wp11_Object_KeyStateLocked(object) != 0); if (object->data.ecKey->type == ECC_PUBLICKEY) noPriv = 1; @@ -15595,10 +15698,15 @@ static int EcObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, switch (type) { case CKA_EC_PARAMS: - ret = GetEcParams(object->data.ecKey, data, len); + if (noKey) { + *len = CK_UNAVAILABLE_INFORMATION; + ret = CKR_ATTRIBUTE_SENSITIVE; + } + else + ret = GetEcParams(object->data.ecKey, data, len); break; case CKA_VALUE: - if (noPriv) { + if (noPriv || noKey) { *len = CK_UNAVAILABLE_INFORMATION; ret = CKR_ATTRIBUTE_SENSITIVE; } @@ -15614,6 +15722,10 @@ static int EcObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, *len = CK_UNAVAILABLE_INFORMATION; ret = NOT_AVAILABLE_E; } + else if (noKey) { + *len = CK_UNAVAILABLE_INFORMATION; + ret = CKR_ATTRIBUTE_SENSITIVE; + } else ret = GetEcPoint(object->data.ecKey, data, len); break; @@ -15747,6 +15859,7 @@ static int MldsaObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, int noPriv = (((object->opFlag & WP11_FLAG_SENSITIVE) != 0) || ((object->opFlag & WP11_FLAG_EXTRACTABLE) == 0)); int noPub = 0; + int noKey = (wp11_Object_KeyStateLocked(object) != 0); if (!object->data.mldsaKey->prvKeySet) noPriv = 1; @@ -15755,7 +15868,12 @@ static int MldsaObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, switch (type) { case CKA_PARAMETER_SET: - ret = GetMldsaParams(object->data.mldsaKey, data, len); + if (noKey) { + *len = CK_UNAVAILABLE_INFORMATION; + ret = CKR_ATTRIBUTE_SENSITIVE; + } + else + ret = GetMldsaParams(object->data.mldsaKey, data, len); break; case CKA_SEED: *len = CK_UNAVAILABLE_INFORMATION; @@ -15763,7 +15881,7 @@ static int MldsaObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, break; case CKA_VALUE: if (object->objClass == CKO_PRIVATE_KEY) { - if (noPriv) { + if (noPriv || noKey) { *len = CK_UNAVAILABLE_INFORMATION; ret = CKR_ATTRIBUTE_SENSITIVE; } @@ -15819,7 +15937,8 @@ static int DhObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, /* Public key held in key when object class is CKO_PUBLIC_KEY and * private key when object class is CKO_PRIVATE_KEY. */ - if (object->objClass != CKO_PRIVATE_KEY || !noPriv) { + if (object->objClass != CKO_PRIVATE_KEY || + (!noPriv && wp11_Object_KeyStateLocked(object) == 0)) { ret = GetData(object->data.dhKey->key, object->data.dhKey->len, data, len); } @@ -15937,6 +16056,7 @@ static int MlKemObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, int noPriv = (((object->opFlag & WP11_FLAG_SENSITIVE) != 0) || ((object->opFlag & WP11_FLAG_EXTRACTABLE) == 0)); int noPub = 0; + int noKey = (wp11_Object_KeyStateLocked(object) != 0); if (!(object->data.mlKemKey->flags & MLKEM_FLAG_PRIV_SET)) noPriv = 1; @@ -15945,7 +16065,12 @@ static int MlKemObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, switch (type) { case CKA_PARAMETER_SET: - ret = GetMlKemParams(object->data.mlKemKey, data, len); + if (noKey) { + *len = CK_UNAVAILABLE_INFORMATION; + ret = CKR_ATTRIBUTE_SENSITIVE; + } + else + ret = GetMlKemParams(object->data.mlKemKey, data, len); break; case CKA_SEED: *len = CK_UNAVAILABLE_INFORMATION; @@ -15953,7 +16078,7 @@ static int MlKemObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, break; case CKA_VALUE: if (object->objClass == CKO_PRIVATE_KEY) { - if (noPriv) { + if (noPriv || noKey) { *len = CK_UNAVAILABLE_INFORMATION; ret = CKR_ATTRIBUTE_SENSITIVE; } @@ -16023,7 +16148,7 @@ static int SecretObject_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, switch (type) { case CKA_VALUE: - if (noPriv) { + if (noPriv || wp11_Object_KeyStateLocked(object) != 0) { *len = CK_UNAVAILABLE_INFORMATION; ret = CKR_ATTRIBUTE_SENSITIVE; } @@ -16332,6 +16457,11 @@ int WP11_Object_GetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, byte* data, ret = NOT_AVAILABLE_E; #endif } + else if (object->objClass == CKO_SECRET_KEY && + wp11_Object_KeyStateLocked(object) != 0) { + *len = CK_UNAVAILABLE_INFORMATION; + ret = CKR_ATTRIBUTE_SENSITIVE; + } else if (object->objClass == CKO_SECRET_KEY) ret = GetSecretKeyCheckValue(object, data, len); else @@ -16582,7 +16712,7 @@ static int wp11_Object_PolicyChanged(WP11_Object* object, (object->objClass != prevClass || ((object->opFlag ^ prevFlag) & ~(word32)WP11_FLAG_POLICY_EXCLUDED) != 0)) { - if (object->encoded || object->undecoded) { + if (wp11_Object_KeyStateLocked(object) != 0) { object->objClass = prevClass; object->opFlag = prevFlag; ret = BAD_STATE_E; @@ -17640,11 +17770,11 @@ int WP11_RsaOaep_PublicEncrypt(unsigned char* in, word32 inLen, if (ret >= 0) { *outLen = ret; ret = 0; - - if (oaep->label != NULL) { - XFREE(oaep->label, NULL, DYNAMIC_TYPE_TMP_BUFFER); - oaep->label = NULL; - } + } + /* The operation ends whatever the result. */ + if (oaep->label != NULL) { + XFREE(oaep->label, NULL, DYNAMIC_TYPE_TMP_BUFFER); + oaep->label = NULL; } return ret; @@ -17713,11 +17843,9 @@ int WP11_RsaOaep_PrivateDecrypt(unsigned char* in, word32 inLen, if (priv->onToken) WP11_Lock_UnlockRW(priv->lock); - if (ret == 0) { - if (oaep->label != NULL) { - XFREE(oaep->label, NULL, DYNAMIC_TYPE_TMP_BUFFER); - oaep->label = NULL; - } + if (oaep->label != NULL) { + XFREE(oaep->label, NULL, DYNAMIC_TYPE_TMP_BUFFER); + oaep->label = NULL; } (void)slot; @@ -19001,6 +19129,8 @@ int WP11_KDF_Derive(WP11_Session* session, CK_HKDF_PARAMS_PTR params, !WP11_KeyType_IsSecret(saltKey->type) || saltKey->data.symmKey == NULL) return CKR_MECHANISM_PARAM_INVALID; + if (WP11_Object_KeyState(saltKey) != 0) + return BAD_STATE_E; salt = saltKey->data.symmKey->data; saltLen = saltKey->data.symmKey->len; break; @@ -19047,16 +19177,14 @@ int WP11_KDF_Derive(WP11_Session* session, CK_HKDF_PARAMS_PTR params, /* Call with the object lock held when the key is on the token. */ static int wp11_Concat_KeyState(WP11_Object* key) { - int ret = 0; + int ret; if (key->objClass != CKO_SECRET_KEY || key->data.symmKey == NULL) ret = BAD_FUNC_ARG; -#ifndef WOLFPKCS11_NO_STORE - else if (key->encoded || key->undecoded) - ret = BAD_STATE_E; -#endif + else + ret = wp11_Object_KeyStateLocked(key); #ifdef WOLFSSL_STM32U5_DHUK - else if (key->dhukIvLen > 0) + if (ret == 0 && key->dhukIvLen > 0) ret = NOT_AVAILABLE_E; #endif @@ -21608,6 +21736,7 @@ int WP11_Digest_Update(unsigned char* data, word32 dataLen, * @param key [in] Key to be digested. * @param session [in] Session object with the Digest object. * @return CKR_KEY_INDIGESTIBLE when the key is not a secret key value. +* BAD_STATE_E when the key value is not decoded. * -ve on failure. * 0 on success. */ @@ -21621,18 +21750,20 @@ int WP11_Digest_Key(WP11_Object* key, WP11_Session* session) !WP11_KeyType_IsSecret(key->type) || key->data.symmKey == NULL) { return (int)CKR_KEY_INDIGESTIBLE; } - if (key->encoded) - return BAD_FUNC_ARG; if (key->onToken) { WP11_Lock_LockRO(key->lock); } - ret = wc_HashUpdate(&digest->hash, digest->hashType, - key->data.symmKey->data, key->data.symmKey->len); + ret = wp11_Object_KeyStateLocked(key); + if (ret == 0) { + ret = wc_HashUpdate(&digest->hash, digest->hashType, + key->data.symmKey->data, key->data.symmKey->len); + /* A hash context holding key bytes must not leave the token. */ + if (ret == 0) + digest->keyDigested = 1; + } if (key->onToken) { WP11_Lock_UnlockRO(key->lock); } - /* The hash context now holds key bytes: it must not leave the token. */ - digest->keyDigested = 1; #else (void) session; (void) key; diff --git a/tests/pkcs11str.c b/tests/pkcs11str.c index 29b9dc1d..b0b5d74d 100644 --- a/tests/pkcs11str.c +++ b/tests/pkcs11str.c @@ -962,9 +962,9 @@ static CK_RV find_mlkem_pub_key(CK_SESSION_HANDLE session, #endif /* WOLFPKCS11_MLKEM */ #ifndef WOLFPKCS11_NO_STORE -static unsigned char concatKeyId[] = "123concatkey"; +static unsigned char secretKeyId[] = "123secretkey"; -static CK_RV create_concat_key(CK_SESSION_HANDLE session) +static CK_RV create_public_secret_key(CK_SESSION_HANDLE session) { CK_RV ret; CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; @@ -977,7 +977,8 @@ static CK_RV create_concat_key(CK_SESSION_HANDLE session) { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, - { CKA_ID, concatKeyId, sizeof(concatKeyId) - 1 }, + { CKA_SIGN, &ckTrue, sizeof(ckTrue) }, + { CKA_ID, secretKeyId, sizeof(secretKeyId) - 1 }, { CKA_VALUE, value, sizeof(value) }, }; @@ -988,7 +989,102 @@ static CK_RV create_concat_key(CK_SESSION_HANDLE session) return ret; } -static CK_RV concat_before_login(void) +#if defined(HAVE_ECC) || !defined(NO_DH) +#ifdef HAVE_ECC +static unsigned char ecPublicId[] = "123ecpublic"; +#endif +#ifndef NO_DH +static unsigned char dhPublicId[] = "123dhpublic"; +#endif + +static CK_RV create_public_asym_keys(CK_SESSION_HANDLE session) +{ + CK_RV ret = CKR_OK; + CK_BBOOL ckFalse = CK_FALSE; + CK_OBJECT_HANDLE key; +#ifdef HAVE_ECC + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_KEY_TYPE, &eccKeyType, sizeof(eccKeyType) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + { CKA_EC_PARAMS, ecc_p256_params, sizeof(ecc_p256_params) }, + { CKA_VALUE, ecc_p256_priv, sizeof(ecc_p256_priv) }, + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_ID, ecPublicId, sizeof(ecPublicId) - 1 }, + }; + CK_ATTRIBUTE pubTmpl[] = { + { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, + { CKA_KEY_TYPE, &eccKeyType, sizeof(eccKeyType) }, + { CKA_EC_PARAMS, ecc_p256_params, sizeof(ecc_p256_params) }, + { CKA_EC_POINT, ecc_p256_pub, sizeof(ecc_p256_pub) }, + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_ID, ecPublicId, sizeof(ecPublicId) - 1 }, + }; +#endif +#ifndef NO_DH + CK_ATTRIBUTE dhTmpl[] = { + { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_KEY_TYPE, &dhKeyType, sizeof(dhKeyType) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + { CKA_PRIME, dh_ffdhe2048_p, sizeof(dh_ffdhe2048_p) }, + { CKA_BASE, dh_ffdhe2048_g, sizeof(dh_ffdhe2048_g) }, + { CKA_VALUE, dh_2048_priv, sizeof(dh_2048_priv) }, + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_ID, dhPublicId, sizeof(dhPublicId) - 1 }, + }; +#endif + +#ifdef HAVE_ECC + ret = funcList->C_CreateObject(session, tmpl, + sizeof(tmpl) / sizeof(*tmpl), &key); + CHECK_CKR(ret, "Create public EC private key"); + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, pubTmpl, + sizeof(pubTmpl) / sizeof(*pubTmpl), + &key); + CHECK_CKR(ret, "Create EC public key"); + } +#endif +#ifndef NO_DH + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, dhTmpl, + sizeof(dhTmpl) / sizeof(*dhTmpl), &key); + CHECK_CKR(ret, "Create public DH private key"); + } +#endif + + return ret; +} + +static CK_RV find_public_object(CK_SESSION_HANDLE session, + CK_OBJECT_CLASS objClass, unsigned char* id, + CK_ULONG idLen, CK_OBJECT_HANDLE* obj) +{ + CK_RV ret; + CK_ULONG count = 0; + CK_ATTRIBUTE findTmpl[] = { + { CKA_CLASS, &objClass, sizeof(objClass) }, + { CKA_ID, id, idLen }, + }; + + ret = funcList->C_FindObjectsInit(session, findTmpl, + sizeof(findTmpl) / sizeof(*findTmpl)); + CHECK_CKR(ret, "Find public object init"); + if (ret == CKR_OK) { + ret = funcList->C_FindObjects(session, obj, 1, &count); + CHECK_CKR(ret, "Find public object"); + funcList->C_FindObjectsFinal(session); + } + if (ret == CKR_OK && count != 1) { + ret = -1; + CHECK_CKR(ret, "Find public object count"); + } + + return ret; +} +#endif + +static CK_RV undecoded_key_before_login(void) { CK_RV ret; CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; @@ -1001,11 +1097,31 @@ static CK_RV concat_before_login(void) CK_KEY_DERIVATION_STRING_DATA params = { data, sizeof(data) }; CK_MECHANISM mech = { CKM_CONCATENATE_BASE_AND_DATA, ¶ms, sizeof(params) }; +#ifndef NO_HMAC + CK_MECHANISM macMech = { CKM_SHA256_HMAC, NULL, 0 }; +#endif CK_ATTRIBUTE findTmpl[] = { { CKA_CLASS, &keyClass, sizeof(keyClass) }, - { CKA_ID, concatKeyId, sizeof(concatKeyId) - 1 }, + { CKA_ID, secretKeyId, sizeof(secretKeyId) - 1 }, }; +#ifdef HAVE_ECC + CK_OBJECT_HANDLE ecKey = CK_INVALID_HANDLE; + unsigned char ecParams[16]; + CK_ATTRIBUTE paramsTmpl = { CKA_EC_PARAMS, ecParams, sizeof(ecParams) }; + CK_OBJECT_HANDLE ecPub = CK_INVALID_HANDLE; + unsigned char ecPoint[sizeof(ecc_p256_pub)]; + CK_ATTRIBUTE pointTmpl = { CKA_EC_POINT, ecPoint, sizeof(ecPoint) }; +#endif +#ifndef NO_DH + CK_OBJECT_HANDLE dhKey = CK_INVALID_HANDLE; + unsigned char prime[sizeof(dh_ffdhe2048_p)]; + unsigned char dhValue[sizeof(dh_2048_priv)]; + CK_ATTRIBUTE primeTmpl = { CKA_PRIME, prime, sizeof(prime) }; + CK_ATTRIBUTE dhValueTmpl = { CKA_VALUE, dhValue, sizeof(dhValue) }; +#endif CK_ULONG valueLen = 16 + sizeof(data); + CK_ULONG keyLen = 0; + CK_ATTRIBUTE lenTmpl = { CKA_VALUE_LEN, &keyLen, sizeof(keyLen) }; CK_ATTRIBUTE tmpl[] = { { CKA_CLASS, &keyClass, sizeof(keyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, @@ -1030,12 +1146,76 @@ static CK_RV concat_before_login(void) ret = -1; CHECK_CKR(ret, "Find public secret key count"); } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, key, &lenTmpl, 1); + CHECK_CKR(ret, "Key length of undecoded key before login"); + } + CHECK_COND(ret != CKR_OK || keyLen == 16, ret, + "Key length value before login"); if (ret == CKR_OK) { ret = funcList->C_DeriveKey(session, &mech, key, tmpl, sizeof(tmpl) / sizeof(*tmpl), &derived); CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, "Concatenate undecoded key before login"); } +#ifndef NO_HMAC + if (ret == CKR_OK) { + ret = funcList->C_SignInit(session, &macMech, key); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "HMAC sign with undecoded key before login"); + } +#endif +#ifdef HAVE_ECC + if (ret == CKR_OK) { + ret = find_public_object(session, CKO_PRIVATE_KEY, ecPublicId, + sizeof(ecPublicId) - 1, &ecKey); + } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, ecKey, ¶msTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "EC parameters of undecoded key before login"); + } + if (ret == CKR_OK) { + ret = find_public_object(session, CKO_PUBLIC_KEY, ecPublicId, + sizeof(ecPublicId) - 1, &ecPub); + } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, ecPub, &pointTmpl, 1); + CHECK_CKR(ret, "EC point of public key before login"); + } + CHECK_COND(ret != CKR_OK || + (pointTmpl.ulValueLen == sizeof(ecc_p256_pub) && + XMEMCMP(ecPoint, ecc_p256_pub, sizeof(ecPoint)) == 0), ret, + "EC point value before login"); + if (ret == CKR_OK) { + paramsTmpl.ulValueLen = sizeof(ecParams); + ret = funcList->C_GetAttributeValue(session, ecPub, ¶msTmpl, 1); + CHECK_CKR(ret, "EC parameters of public key before login"); + } + CHECK_COND(ret != CKR_OK || + (paramsTmpl.ulValueLen == sizeof(ecc_p256_params) && + XMEMCMP(ecParams, ecc_p256_params, + sizeof(ecc_p256_params)) == 0), ret, + "EC parameters value before login"); +#endif +#ifndef NO_DH + if (ret == CKR_OK) { + ret = find_public_object(session, CKO_PRIVATE_KEY, dhPublicId, + sizeof(dhPublicId) - 1, &dhKey); + } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, dhKey, &primeTmpl, 1); + CHECK_CKR(ret, "DH prime of undecoded key before login"); + } + CHECK_COND(ret != CKR_OK || (primeTmpl.ulValueLen == sizeof(prime) && + XMEMCMP(prime, dh_ffdhe2048_p, sizeof(prime)) == 0), ret, + "DH prime value before login"); + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, dhKey, &dhValueTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "DH value of undecoded key before login"); + } +#endif if (session != CK_INVALID_HANDLE) funcList->C_CloseSession(session); @@ -1217,11 +1397,20 @@ static CK_RV pkcs11_test(int slotId, int setPin, int closeDl) #ifndef WOLFPKCS11_NO_STORE if (ret == CKR_OK && userPinLen != 0) { printf("Create public secret key ... "); - ret = create_concat_key(session); + ret = create_public_secret_key(session); + if (ret == CKR_OK) { + printf("Done\n"); + } + } +#if defined(HAVE_ECC) || !defined(NO_DH) + if (ret == CKR_OK && userPinLen != 0) { + printf("Create public asymmetric keys ... "); + ret = create_public_asym_keys(session); if (ret == CKR_OK) { printf("Done\n"); } } +#endif #endif pkcs11_close_session(session); } @@ -1247,8 +1436,8 @@ static CK_RV pkcs11_test(int slotId, int setPin, int closeDl) } } if (ret == CKR_OK && userPinLen != 0) { - printf("Concatenate before login ... "); - ret = concat_before_login(); + printf("Use undecoded key before login ... "); + ret = undecoded_key_before_login(); if (ret == CKR_OK) { printf("Done\n"); } diff --git a/tests/pkcs11test.c b/tests/pkcs11test.c index 472f9751..f37c3c41 100644 --- a/tests/pkcs11test.c +++ b/tests/pkcs11test.c @@ -6705,10 +6705,12 @@ static CK_RV test_wrap_key_wrap_with_trusted(void* args) CK_MECHANISM mech = { CKM_AES_KEY_WRAP, NULL, 0 }; CK_OBJECT_HANDLE untrustedKey = CK_INVALID_HANDLE; CK_OBJECT_HANDLE trustedKey = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE trustedTokenKey = CK_INVALID_HANDLE; CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; byte wrappedKey[40], keyData[16]; CK_ULONG wrappedKeyLen; CK_BBOOL ckTrusted = CK_TRUE; + CK_BBOOL onToken = CK_FALSE; CK_ATTRIBUTE untrustedWrapTmpl[] = { { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType) }, @@ -6718,14 +6720,14 @@ static CK_RV test_wrap_key_wrap_with_trusted(void* args) CK_ULONG untrustedWrapTmplCnt = sizeof(untrustedWrapTmpl) / sizeof(*untrustedWrapTmpl); /* CKA_TRUSTED may only be set by the SO, so the trusted wrapping key is - * provisioned as a public token object under an SO session. */ + * provisioned as a public session object under an SO session. */ CK_ATTRIBUTE trustedWrapTmpl[] = { { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType) }, { CKA_VALUE, aes_128_key, sizeof(aes_128_key) }, { CKA_WRAP, &ckTrue, sizeof(ckTrue) }, { CKA_TRUSTED, &ckTrusted, sizeof(CK_BBOOL) }, - { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_TOKEN, &onToken, sizeof(onToken) }, { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, }; CK_ULONG trustedWrapTmplCnt = sizeof(trustedWrapTmpl) / @@ -6743,7 +6745,7 @@ static CK_RV test_wrap_key_wrap_with_trusted(void* args) /* Provision the trusted wrapping key as the SO. The token has a single * login state, so log the harness user out, log in as SO to create the - * public token key, then restore the user session. */ + * public session key, then restore the user session. */ ret = funcList->C_Logout(session); CHECK_CKR(ret, "Logout user before SO provisioning"); if (ret == CKR_OK) { @@ -6755,6 +6757,12 @@ static CK_RV test_wrap_key_wrap_with_trusted(void* args) trustedWrapTmplCnt, &trustedKey); CHECK_CKR(ret, "Create trusted AES wrapping key (SO)"); } + if (ret == CKR_OK) { + onToken = CK_TRUE; + ret = funcList->C_CreateObject(session, trustedWrapTmpl, + trustedWrapTmplCnt, &trustedTokenKey); + CHECK_CKR(ret, "Create trusted AES wrapping token key (SO)"); + } if (ret == CKR_OK) { ret = funcList->C_Logout(session); CHECK_CKR(ret, "Logout SO after provisioning"); @@ -6799,9 +6807,69 @@ static CK_RV test_wrap_key_wrap_with_trusted(void* args) wrappedKey, &wrappedKeyLen); CHECK_CKR(ret, "Wrap CKA_WRAP_WITH_TRUSTED key with trusted wrapping key"); } + /* A token key the SO created does not decode for the user. */ + if (ret == CKR_OK) { + wrappedKeyLen = sizeof(wrappedKey); + ret = funcList->C_WrapKey(session, &mech, trustedTokenKey, key, + wrappedKey, &wrappedKeyLen); + CHECK_CKR_FAIL(ret, CKR_FUNCTION_FAILED, + "Wrap with trusted token key created by SO"); + } +#ifndef NO_SHA256 + if (ret == CKR_OK) { + CK_MECHANISM digestMech = { CKM_SHA256, NULL, 0 }; + + ret = funcList->C_DigestInit(session, &digestMech); + CHECK_CKR(ret, "Digest init for token key created by SO"); + if (ret == CKR_OK) { + ret = funcList->C_DigestKey(session, trustedTokenKey); + CHECK_CKR_FAIL(ret, CKR_FUNCTION_FAILED, + "Digest token key created by SO"); + } + } +#endif +#ifdef WOLFPKCS11_HKDF + if (ret == CKR_OK) { + CK_OBJECT_HANDLE base = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE derived = CK_INVALID_HANDLE; + CK_ULONG outLen = 32; + CK_HKDF_PARAMS hkdf; + CK_MECHANISM hkdfMech = { CKM_HKDF_DERIVE, &hkdf, sizeof(hkdf) }; + CK_ATTRIBUTE baseTmpl[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, + { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, + { CKA_VALUE, keyData, sizeof(keyData) }, + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + }; + CK_ATTRIBUTE outTmpl[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, + { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, + { CKA_VALUE_LEN, &outLen, sizeof(outLen) }, + }; + + XMEMSET(&hkdf, 0, sizeof(hkdf)); + hkdf.bExtract = CK_TRUE; + hkdf.prfHashMechanism = CKM_SHA256_HMAC; + hkdf.ulSaltType = CKF_HKDF_SALT_KEY; + hkdf.hSaltKey = trustedTokenKey; + ret = funcList->C_CreateObject(session, baseTmpl, + sizeof(baseTmpl) / sizeof(*baseTmpl), + &base); + CHECK_CKR(ret, "Create HKDF base key"); + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &hkdfMech, base, outTmpl, + sizeof(outTmpl) / sizeof(*outTmpl), + &derived); + CHECK_CKR_FAIL(ret, CKR_FUNCTION_FAILED, + "HKDF with salt token key created by SO"); + funcList->C_DestroyObject(session, base); + } + } +#endif funcList->C_DestroyObject(session, untrustedKey); funcList->C_DestroyObject(session, trustedKey); + funcList->C_DestroyObject(session, trustedTokenKey); funcList->C_DestroyObject(session, key); return ret; @@ -16786,6 +16854,974 @@ static CK_RV test_concatenate_logged_out(void* args) } #endif +#if defined(HAVE_ECC) && !defined(NO_SHA256) +static CK_RV stale_key_digest(CK_SESSION_HANDLE session, CK_BBOOL token) +{ + CK_RV ret; + CK_RV loginRet; + CK_OBJECT_HANDLE hEcPub = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hEcPriv = CK_INVALID_HANDLE; + CK_MECHANISM mech = { CKM_EC_KEY_PAIR_GEN, NULL, 0 }; + CK_BYTE data[32]; + CK_BYTE sig[128]; + CK_ULONG sigLen; + CK_BYTE hash[WC_SHA256_DIGEST_SIZE]; + CK_ULONG hashLen; + CK_ATTRIBUTE ecPubTmpl[] = { + {CKA_EC_PARAMS, ecc_p256_params, sizeof(ecc_p256_params)}, + {CKA_VERIFY, &ckTrue, sizeof(ckTrue)}, + }; + CK_ATTRIBUTE ecPrivTmpl[] = { + {CKA_SIGN, &ckTrue, sizeof(ckTrue)}, + {CKA_TOKEN, &token, sizeof(token)}, + }; + + memset(data, 9, sizeof(data)); + + ret = funcList->C_GenerateKeyPair(session, &mech, ecPubTmpl, + sizeof(ecPubTmpl) / sizeof(*ecPubTmpl), ecPrivTmpl, + sizeof(ecPrivTmpl) / sizeof(*ecPrivTmpl), &hEcPub, &hEcPriv); + CHECK_CKR(ret, "Generate EC key pair"); + mech.mechanism = CKM_ECDSA; + if (ret == CKR_OK) { + ret = funcList->C_SignInit(session, &mech, hEcPriv); + CHECK_CKR(ret, "ECDSA sign init"); + } + if (ret == CKR_OK) { + sigLen = sizeof(sig); + ret = funcList->C_Sign(session, data, sizeof(data), sig, &sigLen); + CHECK_CKR(ret, "ECDSA sign"); + } + mech.mechanism = CKM_SHA256; + if (ret == CKR_OK) { + ret = funcList->C_DigestInit(session, &mech); + CHECK_CKR(ret, "Digest init"); + } + if (ret == CKR_OK) { + ret = funcList->C_DigestUpdate(session, data, sizeof(data)); + CHECK_CKR(ret, "Digest update"); + } + if (ret == CKR_OK && token) { + ret = funcList->C_Logout(session); + CHECK_CKR(ret, "Logout during digest"); + if (ret == CKR_OK) { + ret = funcList->C_DigestInit(session, &mech); + CHECK_CKR_FAIL(ret, CKR_OPERATION_ACTIVE, + "Digest init again after logout"); + if (ret == CKR_OK) { + hashLen = sizeof(hash); + ret = funcList->C_DigestFinal(session, hash, &hashLen); + CHECK_CKR(ret, "Digest final after logout"); + } + loginRet = funcList->C_Login(session, CKU_USER, userPin, + userPinLen); + CHECK_CKR(loginRet, "Login again"); + if (ret == CKR_OK) + ret = loginRet; + } + } + else if (ret == CKR_OK) { + ret = funcList->C_DestroyObject(session, hEcPriv); + CHECK_CKR(ret, "Destroy key of completed sign during digest"); + hEcPriv = CK_INVALID_HANDLE; + if (ret == CKR_OK) { + ret = funcList->C_DigestInit(session, &mech); + CHECK_CKR_FAIL(ret, CKR_OPERATION_ACTIVE, + "Digest init again after destroy"); + } + if (ret == CKR_OK) { + hashLen = sizeof(hash); + ret = funcList->C_DigestFinal(session, hash, &hashLen); + CHECK_CKR(ret, "Digest final after destroy"); + } + } + + if (hEcPriv != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hEcPriv); + if (hEcPub != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hEcPub); + + return ret; +} + +static CK_RV test_completed_op_stale_key(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + + ret = stale_key_digest(session, CK_FALSE); +#ifndef WOLFPKCS11_NO_STORE + if (ret == CKR_OK && userPinLen != 0) + ret = stale_key_digest(session, CK_TRUE); +#endif + + return ret; +} +#endif + +#if !defined(NO_RSA) && !defined(WC_NO_RSA_OAEP) && !defined(NO_SHA256) +static CK_ATTRIBUTE oaepRsaTmpl[] = { + {CKA_CLASS, &privKeyClass, sizeof(privKeyClass)}, + {CKA_KEY_TYPE, &rsaKeyType, sizeof(rsaKeyType)}, + {CKA_DECRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_MODULUS, rsa_2048_modulus, sizeof(rsa_2048_modulus)}, + {CKA_PRIVATE_EXPONENT, rsa_2048_priv_exp, sizeof(rsa_2048_priv_exp)}, + {CKA_PRIME_1, rsa_2048_p, sizeof(rsa_2048_p)}, + {CKA_PRIME_2, rsa_2048_q, sizeof(rsa_2048_q)}, + {CKA_EXPONENT_1, rsa_2048_dP, sizeof(rsa_2048_dP)}, + {CKA_EXPONENT_2, rsa_2048_dQ, sizeof(rsa_2048_dQ)}, + {CKA_COEFFICIENT, rsa_2048_u, sizeof(rsa_2048_u)}, + {CKA_PUBLIC_EXPONENT, rsa_2048_pub_exp, sizeof(rsa_2048_pub_exp)}, + {CKA_TOKEN, &ckTrue, sizeof(ckTrue)}, +}; +#define OAEP_RSA_TMPL_CNT (sizeof(oaepRsaTmpl) / sizeof(*oaepRsaTmpl)) + +static CK_RV test_oaep_failed_decrypt(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_OBJECT_HANDLE hRsa = CK_INVALID_HANDLE; + CK_BYTE label[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + CK_BYTE enc[256]; + CK_BYTE dec[256]; + CK_ULONG decLen = sizeof(dec); + CK_RSA_PKCS_OAEP_PARAMS oaep; + CK_MECHANISM mech = { CKM_RSA_PKCS_OAEP, &oaep, sizeof(oaep) }; + CK_MECHANISM pkcsMech = { CKM_RSA_PKCS, NULL, 0 }; + + XMEMSET(enc, 0, sizeof(enc)); + oaep.hashAlg = CKM_SHA256; + oaep.mgf = CKG_MGF1_SHA256; + oaep.source = CKZ_DATA_SPECIFIED; + oaep.pSourceData = label; + oaep.ulSourceDataLen = sizeof(label); + + ret = funcList->C_CreateObject(session, oaepRsaTmpl, OAEP_RSA_TMPL_CNT - 1, + &hRsa); + CHECK_CKR(ret, "Create session RSA private key"); + if (ret == CKR_OK) { + ret = funcList->C_DecryptInit(session, &mech, hRsa); + CHECK_CKR(ret, "RSA-OAEP decrypt init"); + } + if (ret == CKR_OK) { + ret = funcList->C_Decrypt(session, enc, sizeof(enc), dec, &decLen); + CHECK_CKR_FAIL(ret, CKR_ENCRYPTED_DATA_INVALID, + "RSA-OAEP decrypt of invalid data"); + } + if (ret == CKR_OK) { + ret = funcList->C_DecryptInit(session, &pkcsMech, hRsa); + CHECK_CKR(ret, "RSA decrypt init after failed RSA-OAEP decrypt"); + } + if (ret == CKR_OK) { + decLen = sizeof(dec); + ret = funcList->C_Decrypt(session, enc, sizeof(enc), dec, &decLen); + CHECK_CKR_FAIL(ret, CKR_ENCRYPTED_DATA_INVALID, + "RSA decrypt of invalid data"); + } + + if (hRsa != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hRsa); + + return ret; +} + +static CK_RV test_oaep_key_destroyed(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_SESSION_HANDLE other = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hRsa = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hRsaSess = CK_INVALID_HANDLE; + CK_BYTE label[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + CK_BYTE enc[256]; + CK_BYTE dec[256]; + CK_ULONG decLen = sizeof(dec); + CK_RSA_PKCS_OAEP_PARAMS oaep; + CK_MECHANISM mech = { CKM_RSA_PKCS_OAEP, &oaep, sizeof(oaep) }; + + XMEMSET(enc, 0, sizeof(enc)); + oaep.hashAlg = CKM_SHA256; + oaep.mgf = CKG_MGF1_SHA256; + oaep.source = CKZ_DATA_SPECIFIED; + oaep.pSourceData = label; + oaep.ulSourceDataLen = sizeof(label); + + ret = funcList->C_CreateObject(session, oaepRsaTmpl, OAEP_RSA_TMPL_CNT, + &hRsa); + CHECK_CKR(ret, "Create token RSA private key"); + if (ret == CKR_OK) { + ret = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, + NULL, NULL, &other); + CHECK_CKR(ret, "Open second session"); + } + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(other, oaepRsaTmpl, + OAEP_RSA_TMPL_CNT - 1, &hRsaSess); + CHECK_CKR(ret, "Create session RSA private key"); + } + if (ret == CKR_OK) { + ret = funcList->C_DecryptInit(other, &mech, hRsa); + CHECK_CKR(ret, "RSA-OAEP decrypt init"); + } + if (ret == CKR_OK) { + ret = funcList->C_DestroyObject(session, hRsa); + CHECK_CKR(ret, "Destroy key from another session"); + if (ret == CKR_OK) + hRsa = CK_INVALID_HANDLE; + } + if (ret == CKR_OK) { + ret = funcList->C_Decrypt(other, enc, sizeof(enc), dec, &decLen); + CHECK_CKR_FAIL(ret, CKR_OPERATION_NOT_INITIALIZED, + "RSA-OAEP decrypt after key destroyed"); + } + if (ret == CKR_OK) { + ret = funcList->C_DecryptInit(other, &mech, hRsaSess); + CHECK_CKR(ret, "RSA-OAEP decrypt init after key destroyed"); + } + + if (other != CK_INVALID_HANDLE) + funcList->C_CloseSession(other); + if (hRsa != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hRsa); + + return ret; +} +#endif + +#ifndef WOLFPKCS11_NO_STORE +static CK_RV test_key_use_logged_out(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_RV loginRet; + int loggedOut = 0; + CK_BYTE macKey[32]; + CK_OBJECT_HANDLE hTok = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hSess = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hAes = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hAesSess = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hEcPub = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hEcPriv = CK_INVALID_HANDLE; +#if !defined(NO_RSA) && !defined(WC_NO_RSA_OAEP) && !defined(NO_SHA256) + CK_OBJECT_HANDLE hRsa = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hRsaSess = CK_INVALID_HANDLE; + CK_SESSION_HANDLE oaepSession = CK_INVALID_HANDLE; + CK_BYTE label[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + CK_RSA_PKCS_OAEP_PARAMS oaep; + CK_ATTRIBUTE_TYPE rsaAttrs[] = { + CKA_MODULUS, CKA_PUBLIC_EXPONENT, CKA_MODULUS_BITS + }; + int i; + CK_ATTRIBUTE rsaTmpl[] = { + {CKA_CLASS, &privKeyClass, sizeof(privKeyClass)}, + {CKA_KEY_TYPE, &rsaKeyType, sizeof(rsaKeyType)}, + {CKA_PRIVATE, &ckFalse, sizeof(ckFalse)}, + {CKA_DECRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_MODULUS, rsa_2048_modulus, sizeof(rsa_2048_modulus)}, + {CKA_PRIVATE_EXPONENT, rsa_2048_priv_exp, sizeof(rsa_2048_priv_exp)}, + {CKA_PRIME_1, rsa_2048_p, sizeof(rsa_2048_p)}, + {CKA_PRIME_2, rsa_2048_q, sizeof(rsa_2048_q)}, + {CKA_EXPONENT_1, rsa_2048_dP, sizeof(rsa_2048_dP)}, + {CKA_EXPONENT_2, rsa_2048_dQ, sizeof(rsa_2048_dQ)}, + {CKA_COEFFICIENT, rsa_2048_u, sizeof(rsa_2048_u)}, + {CKA_PUBLIC_EXPONENT, rsa_2048_pub_exp, sizeof(rsa_2048_pub_exp)}, + {CKA_TOKEN, &ckTrue, sizeof(ckTrue)}, + }; + CK_ULONG rsaCnt = sizeof(rsaTmpl) / sizeof(*rsaTmpl); +#endif +#ifndef NO_DH + CK_OBJECT_HANDLE hDh = CK_INVALID_HANDLE; + CK_BYTE prime[sizeof(dh_ffdhe2048_p)]; + CK_ATTRIBUTE primeTmpl = { CKA_PRIME, prime, sizeof(prime) }; + CK_ATTRIBUTE dhTmpl[] = { + {CKA_CLASS, &privKeyClass, sizeof(privKeyClass)}, + {CKA_KEY_TYPE, &dhKeyType, sizeof(dhKeyType)}, + {CKA_PRIVATE, &ckFalse, sizeof(ckFalse)}, + {CKA_SENSITIVE, &ckFalse, sizeof(ckFalse)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_PRIME, dh_ffdhe2048_p, sizeof(dh_ffdhe2048_p)}, + {CKA_BASE, dh_ffdhe2048_g, sizeof(dh_ffdhe2048_g)}, + {CKA_VALUE, dh_2048_priv, sizeof(dh_2048_priv)}, + {CKA_TOKEN, &ckTrue, sizeof(ckTrue)}, + }; +#endif + CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hCopy = CK_INVALID_HANDLE; + CK_MECHANISM mech; + CK_BYTE value[32]; + CK_ATTRIBUTE getTmpl = { CKA_VALUE, value, sizeof(value) }; + CK_ULONG valueLen = 0; + CK_ATTRIBUTE lenTmpl = { CKA_VALUE_LEN, &valueLen, sizeof(valueLen) }; +#if defined(HAVE_ECC) || defined(HAVE_AES_CBC) || defined(HAVE_AESGCM) || \ + defined(WOLFSSL_HAVE_PRF) || defined(HAVE_AESECB) + CK_BYTE in[16] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; + CK_BYTE out[128]; + CK_ULONG outSz; +#endif +#ifndef NO_HMAC + CK_BYTE data[16] = { 0 }; + CK_BYTE mac[WC_SHA256_DIGEST_SIZE]; + CK_BYTE macSess[WC_SHA256_DIGEST_SIZE]; + CK_ULONG macLen; +#endif +#ifdef HAVE_AES_CBC + CK_BYTE iv[16] = { 0 }; + CK_BYTE cbcOut[16]; + CK_BYTE cbcSessOut[16]; + CK_SESSION_HANDLE cbcSession = CK_INVALID_HANDLE; +#endif +#ifdef HAVE_AESECB + CK_SESSION_HANDLE ecbSession = CK_INVALID_HANDLE; +#endif +#ifdef HAVE_AESGCM + CK_BYTE gcmIv[12] = { 0 }; + CK_GCM_PARAMS gcm; + CK_SESSION_HANDLE gcmSession = CK_INVALID_HANDLE; +#endif +#if defined(WOLFSSL_HAVE_PRF) && !defined(NO_SHA256) + CK_TLS_MAC_PARAMS tlsMac; + CK_SESSION_HANDLE tlsSession = CK_INVALID_HANDLE; +#endif +#if defined(WOLFPKCS11_NSS) && defined(WOLFSSL_HAVE_PRF) + CK_SESSION_HANDLE prfSession = CK_INVALID_HANDLE; +#endif +#ifdef HAVE_AES_KEYWRAP + CK_BYTE wrapped[40]; + CK_ULONG wrappedLen; + CK_ULONG unwrapLen = 16; + CK_ATTRIBUTE unwrapTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_VALUE_LEN, &unwrapLen, sizeof(unwrapLen)}, + }; +#endif +#if !defined(NO_DH) || defined(HAVE_ECC) + CK_ULONG deriveLen = 32; + CK_ATTRIBUTE deriveTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_VALUE_LEN, &deriveLen, sizeof(deriveLen)}, + }; +#endif +#ifndef NO_DH + CK_BYTE dhPeer[sizeof(dh_ffdhe2048_p)]; +#endif +#ifdef HAVE_ECC + CK_ECDH1_DERIVE_PARAMS ecdh; +#endif +#ifdef WOLFPKCS11_HKDF + CK_HKDF_PARAMS hkdf; + CK_ULONG outLen = 32; + CK_ATTRIBUTE outTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_PRIVATE, &ckFalse, sizeof(ckFalse)}, + {CKA_VALUE_LEN, &outLen, sizeof(outLen)}, + }; +#endif +#ifndef NO_SHA256 + CK_BYTE hash[WC_SHA256_DIGEST_SIZE]; + CK_ULONG hashLen; +#endif + CK_ATTRIBUTE tokTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_PRIVATE, &ckFalse, sizeof(ckFalse)}, + {CKA_SIGN, &ckTrue, sizeof(ckTrue)}, + {CKA_VERIFY, &ckTrue, sizeof(ckTrue)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE, macKey, sizeof(macKey)}, + {CKA_TOKEN, &ckTrue, sizeof(ckTrue)}, + }; + CK_ULONG tokCnt = sizeof(tokTmpl) / sizeof(*tokTmpl); + CK_ATTRIBUTE aesTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType)}, + {CKA_PRIVATE, &ckFalse, sizeof(ckFalse)}, + {CKA_ENCRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_DECRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_WRAP, &ckTrue, sizeof(ckTrue)}, + {CKA_UNWRAP, &ckTrue, sizeof(ckTrue)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_SENSITIVE, &ckFalse, sizeof(ckFalse)}, + {CKA_VALUE, aes_128_key, sizeof(aes_128_key)}, + {CKA_TOKEN, &ckTrue, sizeof(ckTrue)}, + }; + CK_ULONG aesCnt = sizeof(aesTmpl) / sizeof(*aesTmpl); +#ifdef HAVE_ECC + CK_ATTRIBUTE ecPubTmpl[] = { + {CKA_EC_PARAMS, ecc_p256_params, sizeof(ecc_p256_params)}, + {CKA_VERIFY, &ckTrue, sizeof(ckTrue)}, + }; + CK_ATTRIBUTE ecPrivTmpl[] = { + {CKA_SIGN, &ckTrue, sizeof(ckTrue)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)}, + {CKA_PRIVATE, &ckFalse, sizeof(ckFalse)}, + {CKA_SENSITIVE, &ckFalse, sizeof(ckFalse)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_TOKEN, &ckTrue, sizeof(ckTrue)}, + }; +#endif + + if (userPinLen == 0) + return CKR_SKIPPED; + + memset(macKey, 0x5a, sizeof(macKey)); + + ret = funcList->C_CreateObject(session, tokTmpl, tokCnt, &hTok); + CHECK_CKR(ret, "Create public token secret key"); + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, tokTmpl, tokCnt - 1, &hSess); + CHECK_CKR(ret, "Create public session secret key"); + } + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, aesTmpl, aesCnt, &hAes); + CHECK_CKR(ret, "Create public token AES key"); + } + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, aesTmpl, aesCnt - 1, + &hAesSess); + CHECK_CKR(ret, "Create public session AES key"); + } +#if !defined(NO_RSA) && !defined(WC_NO_RSA_OAEP) && !defined(NO_SHA256) + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, rsaTmpl, rsaCnt, &hRsa); + CHECK_CKR(ret, "Create public token RSA private key"); + } +#endif +#ifndef NO_DH + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, dhTmpl, + sizeof(dhTmpl) / sizeof(*dhTmpl), &hDh); + CHECK_CKR(ret, "Create public token DH private key"); + } +#endif +#ifdef HAVE_ECC + if (ret == CKR_OK) { + mech.mechanism = CKM_EC_KEY_PAIR_GEN; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + ret = funcList->C_GenerateKeyPair(session, &mech, ecPubTmpl, + sizeof(ecPubTmpl) / sizeof(*ecPubTmpl), ecPrivTmpl, + sizeof(ecPrivTmpl) / sizeof(*ecPrivTmpl), &hEcPub, &hEcPriv); + CHECK_CKR(ret, "Generate public token EC private key"); + } + mech.mechanism = CKM_ECDSA; + if (ret == CKR_OK) { + ret = funcList->C_SignInit(session, &mech, hEcPriv); + CHECK_CKR(ret, "ECDSA sign init before logout"); + } +#endif +#ifdef HAVE_AES_CBC + mech.mechanism = CKM_AES_CBC; + mech.pParameter = iv; + mech.ulParameterLen = sizeof(iv); + if (ret == CKR_OK) { + ret = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, + NULL, NULL, &cbcSession); + CHECK_CKR(ret, "Open session for AES-CBC"); + } + if (ret == CKR_OK) { + ret = funcList->C_EncryptInit(cbcSession, &mech, hAes); + CHECK_CKR(ret, "AES-CBC encrypt init before logout"); + } +#endif +#ifdef HAVE_AESGCM + XMEMSET(&gcm, 0, sizeof(gcm)); + gcm.pIv = gcmIv; + gcm.ulIvLen = sizeof(gcmIv); + gcm.ulTagBits = 128; + mech.mechanism = CKM_AES_GCM; + mech.pParameter = &gcm; + mech.ulParameterLen = sizeof(gcm); + if (ret == CKR_OK) { + ret = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, + NULL, NULL, &gcmSession); + CHECK_CKR(ret, "Open session for AES-GCM"); + } + if (ret == CKR_OK) { + ret = funcList->C_EncryptInit(gcmSession, &mech, hAes); + CHECK_CKR(ret, "AES-GCM encrypt init before logout"); + } +#endif +#ifdef HAVE_AESECB + mech.mechanism = CKM_AES_ECB; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + if (ret == CKR_OK) { + ret = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, + NULL, NULL, &ecbSession); + CHECK_CKR(ret, "Open session for AES-ECB"); + } + if (ret == CKR_OK) { + ret = funcList->C_EncryptInit(ecbSession, &mech, hAes); + CHECK_CKR(ret, "AES-ECB encrypt init before logout"); + } +#endif +#if defined(WOLFSSL_HAVE_PRF) && !defined(NO_SHA256) + tlsMac.prfHashMechanism = CKM_SHA256; + tlsMac.ulMacLength = 12; + tlsMac.ulServerOrClient = 1; + mech.mechanism = CKM_TLS_MAC; + mech.pParameter = &tlsMac; + mech.ulParameterLen = sizeof(tlsMac); + if (ret == CKR_OK) { + ret = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, + NULL, NULL, &tlsSession); + CHECK_CKR(ret, "Open session for TLS MAC"); + } + if (ret == CKR_OK) { + ret = funcList->C_SignInit(tlsSession, &mech, hTok); + CHECK_CKR(ret, "TLS MAC sign init before logout"); + } +#endif +#if !defined(NO_RSA) && !defined(WC_NO_RSA_OAEP) && !defined(NO_SHA256) + oaep.hashAlg = CKM_SHA256; + oaep.mgf = CKG_MGF1_SHA256; + oaep.source = CKZ_DATA_SPECIFIED; + oaep.pSourceData = label; + oaep.ulSourceDataLen = sizeof(label); + mech.mechanism = CKM_RSA_PKCS_OAEP; + mech.pParameter = &oaep; + mech.ulParameterLen = sizeof(oaep); + if (ret == CKR_OK) { + ret = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, + NULL, NULL, &oaepSession); + CHECK_CKR(ret, "Open session for RSA-OAEP"); + } + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(oaepSession, rsaTmpl, rsaCnt - 1, + &hRsaSess); + CHECK_CKR(ret, "Create public session RSA private key"); + } + if (ret == CKR_OK) { + ret = funcList->C_DecryptInit(oaepSession, &mech, hRsa); + CHECK_CKR(ret, "RSA-OAEP decrypt init before logout"); + } +#endif +#if defined(WOLFPKCS11_NSS) && defined(WOLFSSL_HAVE_PRF) + mech.mechanism = CKM_NSS_TLS_PRF_GENERAL_SHA256; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + if (ret == CKR_OK) { + ret = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, + NULL, NULL, &prfSession); + CHECK_CKR(ret, "Open session for NSS TLS PRF"); + } + if (ret == CKR_OK) { + ret = funcList->C_SignInit(prfSession, &mech, hTok); + CHECK_CKR(ret, "NSS TLS PRF sign init before logout"); + } +#endif + if (ret == CKR_OK) { + ret = funcList->C_Logout(session); + CHECK_CKR(ret, "Logout"); + loggedOut = (ret == CKR_OK); + } +#ifdef HAVE_ECC + if (ret == CKR_OK) { + outSz = sizeof(out); + ret = funcList->C_Sign(session, in, sizeof(in), out, &outSz); + CHECK_CKR_FAIL(ret, CKR_OPERATION_NOT_INITIALIZED, + "ECDSA sign started before logout"); + } +#endif +#ifdef HAVE_AES_CBC + if (ret == CKR_OK) { + outSz = sizeof(cbcOut); + ret = funcList->C_Encrypt(cbcSession, in, sizeof(in), cbcOut, &outSz); + CHECK_CKR(ret, "AES-CBC encrypt started before logout"); + } +#endif +#ifdef HAVE_AESGCM + if (ret == CKR_OK) { + outSz = sizeof(out); + ret = funcList->C_Encrypt(gcmSession, in, sizeof(in), out, &outSz); + CHECK_CKR_FAIL(ret, CKR_OPERATION_NOT_INITIALIZED, + "AES-GCM encrypt started before logout"); + } +#endif +#ifdef HAVE_AESECB + if (ret == CKR_OK) { + outSz = sizeof(out); + ret = funcList->C_Encrypt(ecbSession, in, sizeof(in), out, &outSz); + CHECK_CKR_FAIL(ret, CKR_OPERATION_NOT_INITIALIZED, + "AES-ECB encrypt started before logout"); + } +#endif +#if defined(WOLFSSL_HAVE_PRF) && !defined(NO_SHA256) + if (ret == CKR_OK) { + outSz = sizeof(out); + ret = funcList->C_Sign(tlsSession, in, sizeof(in), out, &outSz); + CHECK_CKR_FAIL(ret, CKR_OPERATION_NOT_INITIALIZED, + "TLS MAC sign started before logout"); + } +#endif +#if defined(WOLFPKCS11_NSS) && defined(WOLFSSL_HAVE_PRF) + if (ret == CKR_OK) { + outSz = sizeof(out); + ret = funcList->C_Sign(prfSession, in, sizeof(in), out, &outSz); + CHECK_CKR_FAIL(ret, CKR_OPERATION_NOT_INITIALIZED, + "NSS TLS PRF sign started before logout"); + } +#endif +#if !defined(NO_RSA) && !defined(WC_NO_RSA_OAEP) && !defined(NO_SHA256) + mech.mechanism = CKM_RSA_PKCS_OAEP; + mech.pParameter = &oaep; + mech.ulParameterLen = sizeof(oaep); + if (ret == CKR_OK) { + ret = funcList->C_DecryptInit(oaepSession, &mech, hRsaSess); + CHECK_CKR(ret, "RSA-OAEP decrypt init after logout"); + } + for (i = 0; ret == CKR_OK && i < (int)(sizeof(rsaAttrs) / + sizeof(*rsaAttrs)); i++) { + getTmpl.type = rsaAttrs[i]; + getTmpl.ulValueLen = sizeof(value); + ret = funcList->C_GetAttributeValue(session, hRsa, &getTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "Get RSA key attribute after logout"); + } +#endif + if (ret == CKR_OK) { + getTmpl.type = CKA_VALUE; + getTmpl.ulValueLen = sizeof(value); + ret = funcList->C_GetAttributeValue(session, hAes, &getTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "Get token key value after logout"); + } + CHECK_COND(ret != CKR_OK || + getTmpl.ulValueLen == CK_UNAVAILABLE_INFORMATION, ret, + "Token key value unavailable after logout"); + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, hAes, &lenTmpl, 1); + CHECK_CKR(ret, "Get token key length after logout"); + } + CHECK_COND(ret != CKR_OK || valueLen == sizeof(aes_128_key), ret, + "Token key length after logout"); + if (ret == CKR_OK) { + getTmpl.type = CKA_MODULUS; + getTmpl.ulValueLen = sizeof(value); + ret = funcList->C_GetAttributeValue(session, hAes, &getTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_TYPE_INVALID, + "Get modulus of token AES key after logout"); + } +#ifdef HAVE_ECC + if (ret == CKR_OK) { + getTmpl.type = CKA_EC_PARAMS; + getTmpl.ulValueLen = sizeof(value); + ret = funcList->C_GetAttributeValue(session, hEcPriv, &getTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "Get EC parameters of token private key after logout"); + } + if (ret == CKR_OK) { + getTmpl.type = CKA_VALUE; + getTmpl.ulValueLen = sizeof(value); + ret = funcList->C_GetAttributeValue(session, hEcPriv, &getTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "Get value of token EC private key after logout"); + } + if (ret == CKR_OK) { + getTmpl.type = CKA_EC_POINT; + getTmpl.ulValueLen = sizeof(value); + ret = funcList->C_GetAttributeValue(session, hEcPriv, &getTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "Get EC point of token private key after logout"); + } + if (ret == CKR_OK) { + getTmpl.type = CKA_MODULUS; + getTmpl.ulValueLen = sizeof(value); + ret = funcList->C_GetAttributeValue(session, hEcPriv, &getTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_TYPE_INVALID, + "Get modulus of token EC private key after logout"); + } +#endif +#ifndef NO_DH + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, hDh, &primeTmpl, 1); + CHECK_CKR(ret, "Get DH prime after logout"); + } + CHECK_COND(ret != CKR_OK || (primeTmpl.ulValueLen == sizeof(prime) && + XMEMCMP(prime, dh_ffdhe2048_p, sizeof(prime)) == 0), ret, + "DH prime after logout"); + if (ret == CKR_OK) { + getTmpl.type = CKA_VALUE; + getTmpl.ulValueLen = sizeof(value); + ret = funcList->C_GetAttributeValue(session, hDh, &getTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "Get DH private value after logout"); + } +#endif +#ifndef NO_SHA + if (ret == CKR_OK) { + getTmpl.type = CKA_CHECK_VALUE; + getTmpl.ulValueLen = sizeof(value); + ret = funcList->C_GetAttributeValue(session, hTok, &getTmpl, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "Get token key check value after logout"); + } +#endif + if (ret == CKR_OK) { + ret = funcList->C_CopyObject(session, hAes, NULL, 0, &hCopy); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "Copy token key after logout"); + } + + mech.pParameter = NULL; + mech.ulParameterLen = 0; +#ifndef NO_HMAC + mech.mechanism = CKM_SHA256_HMAC; + if (ret == CKR_OK) { + ret = funcList->C_SignInit(session, &mech, hTok); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, "HMAC sign after logout"); + } + if (ret == CKR_OK) { + ret = funcList->C_VerifyInit(session, &mech, hTok); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "HMAC verify after logout"); + } +#endif +#ifdef HAVE_AES_CBC + mech.mechanism = CKM_AES_CBC; + mech.pParameter = iv; + mech.ulParameterLen = sizeof(iv); + if (ret == CKR_OK) { + ret = funcList->C_EncryptInit(session, &mech, hAes); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "AES encrypt after logout"); + } + if (ret == CKR_OK) { + ret = funcList->C_DecryptInit(session, &mech, hAes); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "AES decrypt after logout"); + } + mech.pParameter = NULL; + mech.ulParameterLen = 0; +#endif +#ifdef HAVE_AES_KEYWRAP + mech.mechanism = CKM_AES_KEY_WRAP; + if (ret == CKR_OK) { + wrappedLen = sizeof(wrapped); + ret = funcList->C_WrapKey(session, &mech, hAes, hAesSess, wrapped, + &wrappedLen); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "Wrap with token key after logout"); + } + if (ret == CKR_OK) { + wrappedLen = sizeof(wrapped); + ret = funcList->C_WrapKey(session, &mech, hAesSess, hAes, wrapped, + &wrappedLen); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "Wrap token key after logout"); + } + if (ret == CKR_OK) { + XMEMSET(wrapped, 0, sizeof(wrapped)); + ret = funcList->C_UnwrapKey(session, &mech, hAes, wrapped, 24, + unwrapTmpl, + sizeof(unwrapTmpl) / sizeof(*unwrapTmpl), + &hKey); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "Unwrap with token key after logout"); + } +#endif +#ifdef WOLFPKCS11_HKDF + XMEMSET(&hkdf, 0, sizeof(hkdf)); + hkdf.bExtract = CK_TRUE; + hkdf.prfHashMechanism = CKM_SHA256_HMAC; + hkdf.ulSaltType = CKF_HKDF_SALT_NULL; + mech.mechanism = CKM_HKDF_DERIVE; + mech.pParameter = &hkdf; + mech.ulParameterLen = sizeof(hkdf); + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hTok, outTmpl, + sizeof(outTmpl) / sizeof(*outTmpl), &hKey); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "HKDF from token key after logout"); + } + if (ret == CKR_OK) { + hkdf.ulSaltType = CKF_HKDF_SALT_KEY; + hkdf.hSaltKey = hTok; + ret = funcList->C_DeriveKey(session, &mech, hSess, outTmpl, + sizeof(outTmpl) / sizeof(*outTmpl), &hKey); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "HKDF with token salt key after logout"); + } +#ifdef HAVE_ECC + if (ret == CKR_OK) { + hkdf.hSaltKey = hEcPriv; + ret = funcList->C_DeriveKey(session, &mech, hSess, outTmpl, + sizeof(outTmpl) / sizeof(*outTmpl), &hKey); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, + "HKDF with private key salt after logout"); + } +#endif + mech.pParameter = NULL; + mech.ulParameterLen = 0; +#endif +#ifdef HAVE_ECC + mech.mechanism = CKM_ECDSA; + if (ret == CKR_OK) { + ret = funcList->C_SignInit(session, &mech, hEcPriv); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "ECDSA sign after logout"); + } + XMEMSET(&ecdh, 0, sizeof(ecdh)); + ecdh.kdf = CKD_NULL; + ecdh.pPublicData = ecc_p256_point; + ecdh.ulPublicDataLen = sizeof(ecc_p256_point); + mech.mechanism = CKM_ECDH1_DERIVE; + mech.pParameter = &ecdh; + mech.ulParameterLen = sizeof(ecdh); + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hEcPriv, deriveTmpl, + sizeof(deriveTmpl) / sizeof(*deriveTmpl), + &hKey); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, "ECDH after logout"); + } + mech.pParameter = NULL; + mech.ulParameterLen = 0; +#endif +#ifndef NO_DH + XMEMSET(dhPeer, 0x07, sizeof(dhPeer)); + mech.mechanism = CKM_DH_PKCS_DERIVE; + mech.pParameter = dhPeer; + mech.ulParameterLen = sizeof(dhPeer); + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hDh, deriveTmpl, + sizeof(deriveTmpl) / sizeof(*deriveTmpl), + &hKey); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, "DH derive after logout"); + } + mech.pParameter = NULL; + mech.ulParameterLen = 0; +#endif +#ifndef NO_HMAC + mech.mechanism = CKM_SHA256_HMAC; + if (ret == CKR_OK) { + ret = funcList->C_SignInit(session, &mech, hAes); + CHECK_CKR_FAIL(ret, CKR_KEY_FUNCTION_NOT_PERMITTED, + "Sign with token key lacking CKA_SIGN after logout"); + } +#endif +#ifndef NO_SHA256 + mech.mechanism = CKM_SHA256; + if (ret == CKR_OK) { + ret = funcList->C_DigestInit(session, &mech); + CHECK_CKR(ret, "Digest init after logout"); + } + if (ret == CKR_OK) { + ret = funcList->C_DigestKey(session, hTok); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "Digest token key after logout"); + } + if (ret == CKR_OK) { + ret = funcList->C_DigestInit(session, &mech); + CHECK_CKR(ret, "Digest init after failed digest key"); + } + if (ret == CKR_OK) { + hashLen = sizeof(hash); + ret = funcList->C_DigestFinal(session, hash, &hashLen); + CHECK_CKR(ret, "Digest final"); + } +#endif + + if (loggedOut) { + loginRet = funcList->C_Login(session, CKU_USER, userPin, userPinLen); + CHECK_CKR(loginRet, "Login again"); + if (ret == CKR_OK) + ret = loginRet; + } + +#ifndef NO_HMAC + mech.mechanism = CKM_SHA256_HMAC; + if (ret == CKR_OK) { + ret = funcList->C_SignInit(session, &mech, hTok); + CHECK_CKR(ret, "HMAC sign init after login"); + } + if (ret == CKR_OK) { + macLen = sizeof(mac); + ret = funcList->C_Sign(session, data, sizeof(data), mac, &macLen); + CHECK_CKR(ret, "HMAC sign with token key"); + } + if (ret == CKR_OK) { + ret = funcList->C_SignInit(session, &mech, hSess); + CHECK_CKR(ret, "HMAC sign init with session key"); + } + if (ret == CKR_OK) { + macLen = sizeof(macSess); + ret = funcList->C_Sign(session, data, sizeof(data), macSess, &macLen); + CHECK_CKR(ret, "HMAC sign with session key"); + } + CHECK_COND(ret != CKR_OK || XMEMCMP(mac, macSess, sizeof(mac)) == 0, ret, + "Token key decoded after login"); +#endif +#ifdef HAVE_AES_CBC + mech.mechanism = CKM_AES_CBC; + mech.pParameter = iv; + mech.ulParameterLen = sizeof(iv); + if (ret == CKR_OK) { + ret = funcList->C_EncryptInit(session, &mech, hAesSess); + CHECK_CKR(ret, "AES-CBC encrypt init with session key"); + } + if (ret == CKR_OK) { + outSz = sizeof(cbcSessOut); + ret = funcList->C_Encrypt(session, in, sizeof(in), cbcSessOut, &outSz); + CHECK_CKR(ret, "AES-CBC encrypt with session key"); + } + CHECK_COND(ret != CKR_OK || + XMEMCMP(cbcOut, cbcSessOut, sizeof(cbcOut)) == 0, ret, + "AES-CBC across logout used the key value"); + if (cbcSession != CK_INVALID_HANDLE) + funcList->C_CloseSession(cbcSession); +#endif +#ifdef HAVE_AESGCM + if (gcmSession != CK_INVALID_HANDLE) + funcList->C_CloseSession(gcmSession); +#endif +#ifdef HAVE_AESECB + if (ecbSession != CK_INVALID_HANDLE) + funcList->C_CloseSession(ecbSession); +#endif +#if defined(WOLFSSL_HAVE_PRF) && !defined(NO_SHA256) + if (tlsSession != CK_INVALID_HANDLE) + funcList->C_CloseSession(tlsSession); +#endif +#if !defined(NO_RSA) && !defined(WC_NO_RSA_OAEP) && !defined(NO_SHA256) + if (oaepSession != CK_INVALID_HANDLE) + funcList->C_CloseSession(oaepSession); + if (hRsa != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hRsa); +#endif +#if defined(WOLFPKCS11_NSS) && defined(WOLFSSL_HAVE_PRF) + if (prfSession != CK_INVALID_HANDLE) + funcList->C_CloseSession(prfSession); +#endif + + if (hCopy != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hCopy); + if (hKey != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hKey); +#ifndef NO_DH + if (hDh != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hDh); +#endif + if (hEcPriv != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hEcPriv); + if (hEcPub != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hEcPub); + if (hAesSess != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hAesSess); + if (hAes != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hAes); + if (hSess != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hSess); + if (hTok != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hTok); + + return ret; +} +#endif + #if defined(HAVE_ECC) && (defined(HAVE_ALL_CURVES) || defined(HAVE_ECC384)) && \ !defined(NO_AES) && !defined(WOLFPKCS11_NSS) static CK_RV test_ecdh_derive_aes_no_value_len(void* args) @@ -20258,6 +21294,16 @@ static TEST_FUNC testFunc[] = { #ifndef WOLFPKCS11_NO_STORE PKCS11TEST_FUNC_SESS_DECL(test_concatenate_logged_out), #endif +#ifndef WOLFPKCS11_NO_STORE + PKCS11TEST_FUNC_SESS_DECL(test_key_use_logged_out), +#endif +#if defined(HAVE_ECC) && !defined(NO_SHA256) + PKCS11TEST_FUNC_SESS_DECL(test_completed_op_stale_key), +#endif +#if !defined(NO_RSA) && !defined(WC_NO_RSA_OAEP) && !defined(NO_SHA256) + PKCS11TEST_FUNC_SESS_DECL(test_oaep_failed_decrypt), + PKCS11TEST_FUNC_SESS_DECL(test_oaep_key_destroyed), +#endif #if defined(HAVE_ECC) && (defined(HAVE_ALL_CURVES) || defined(HAVE_ECC384)) && \ !defined(NO_AES) && !defined(WOLFPKCS11_NSS) PKCS11TEST_FUNC_SESS_DECL(test_ecdh_derive_aes_no_value_len), diff --git a/tests/pkcs11v3test.c b/tests/pkcs11v3test.c index 9a563482..aa86b541 100644 --- a/tests/pkcs11v3test.c +++ b/tests/pkcs11v3test.c @@ -4256,6 +4256,128 @@ static CK_RV test_xmss_paramset_setattr_rejected(void* args) #endif /* WOLFPKCS11_XMSS */ +#if defined(WOLFPKCS11_PKCS11_V3_2) && !defined(WOLFPKCS11_NO_STORE) && \ + (defined(WOLFPKCS11_MLDSA) || defined(WOLFPKCS11_MLKEM)) +static CK_RV pqc_attrs_logged_out(CK_SESSION_HANDLE session, + CK_MECHANISM_TYPE genMech, CK_ULONG paramSet, + CK_ATTRIBUTE_TYPE useAttr, + CK_MECHANISM_TYPE useMech) +{ + CK_RV ret; + CK_RV loginRet; + CK_BBOOL ckFalse = CK_FALSE; + CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE; + CK_MECHANISM mech = { genMech, NULL, 0 }; + CK_MECHANISM opMech = { useMech, NULL, 0 }; + CK_ULONG gotSet = 0; + CK_BYTE value[64]; + CK_ATTRIBUTE setAttr = { CKA_PARAMETER_SET, &gotSet, sizeof(gotSet) }; + CK_ATTRIBUTE valueAttr = { CKA_VALUE, value, sizeof(value) }; + CK_ATTRIBUTE pubTmpl[] = { + { CKA_PARAMETER_SET, ¶mSet, sizeof(paramSet) }, + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + }; + CK_ATTRIBUTE privTmpl[] = { + { useAttr, &ckTrue, sizeof(ckTrue) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + { CKA_SENSITIVE, &ckFalse, sizeof(ckFalse) }, + { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + }; + + if (userPinLen == 0) + return CKR_SKIPPED; + + ret = funcList->C_GenerateKeyPair(session, &mech, pubTmpl, + sizeof(pubTmpl) / sizeof(*pubTmpl), + privTmpl, + sizeof(privTmpl) / sizeof(*privTmpl), + &pub, &priv); + CHECK_CKR(ret, "Generate public token key pair"); + if (ret == CKR_OK) { + ret = funcList->C_Logout(session); + CHECK_CKR(ret, "Logout"); + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, priv, &valueAttr, 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "Private key value after logout"); + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, priv, &setAttr, + 1); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_SENSITIVE, + "Private key parameter set after logout"); + } + if (ret == CKR_OK) { + setAttr.ulValueLen = sizeof(gotSet); + ret = funcList->C_GetAttributeValue(session, pub, &setAttr, 1); + CHECK_CKR(ret, "Public key parameter set after logout"); + CHECK_COND(ret != CKR_OK || gotSet == paramSet, ret, + "Public key parameter set value after logout"); + } + if (ret == CKR_OK && useAttr == CKA_SIGN) { + ret = funcList->C_SignInit(session, &opMech, priv); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "Sign with private key after logout"); + } +#ifdef WOLFPKCS11_MLKEM + if (ret == CKR_OK && useAttr == CKA_DECAPSULATE) { + CK_FUNCTION_LIST_3_2* funcListExt = + (CK_FUNCTION_LIST_3_2*)funcList; + CK_OBJECT_HANDLE secret = CK_INVALID_HANDLE; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_KEY_TYPE genericType = CKK_GENERIC_SECRET; + CK_BYTE ct[WC_ML_KEM_512_CIPHER_TEXT_SIZE]; + CK_ATTRIBUTE secretTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &genericType, sizeof(genericType) }, + }; + + XMEMSET(ct, 0, sizeof(ct)); + ret = funcListExt->C_DecapsulateKey(session, &opMech, priv, + secretTmpl, sizeof(secretTmpl) / sizeof(*secretTmpl), + ct, sizeof(ct), &secret); + CHECK_CKR_FAIL(ret, CKR_USER_NOT_LOGGED_IN, + "Decapsulate with private key after logout"); + } +#endif + loginRet = funcList->C_Login(session, CKU_USER, userPin, + userPinLen); + CHECK_CKR(loginRet, "Login again"); + if (ret == CKR_OK) + ret = loginRet; + } + } + if (priv != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, priv); + if (pub != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, pub); + + return ret; +} + +#ifdef WOLFPKCS11_MLDSA +static CK_RV test_mldsa_attrs_logged_out(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + + return pqc_attrs_logged_out(session, CKM_ML_DSA_KEY_PAIR_GEN, + CKP_ML_DSA_44, CKA_SIGN, CKM_ML_DSA); +} +#endif + +#ifdef WOLFPKCS11_MLKEM +static CK_RV test_mlkem_attrs_logged_out(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + + return pqc_attrs_logged_out(session, CKM_ML_KEM_KEY_PAIR_GEN, + CKP_ML_KEM_512, CKA_DECAPSULATE, + CKM_ML_KEM); +} +#endif +#endif + static TEST_FUNC testFunc[] = { PKCS11TEST_FUNC_NO_INIT_DECL(test_get_interface_list), PKCS11TEST_FUNC_NO_INIT_DECL(test_get_interface), @@ -4272,6 +4394,9 @@ static TEST_FUNC testFunc[] = { PKCS11TEST_FUNC_SESS_DECL(test_mldsa_fixed_keys_seed), PKCS11TEST_FUNC_SESS_DECL(test_mldsa_fixed_keys_both), PKCS11TEST_FUNC_SESS_DECL(test_copy_object_mldsa_key), +#ifndef WOLFPKCS11_NO_STORE + PKCS11TEST_FUNC_SESS_DECL(test_mldsa_attrs_logged_out), +#endif #endif #ifdef WOLFPKCS11_MLKEM PKCS11TEST_FUNC_SESS_DECL(test_mlkem_gen_keys), @@ -4294,6 +4419,9 @@ static TEST_FUNC testFunc[] = { #if defined(HAVE_ECC) && defined(WOLFPKCS11_HKDF) && !defined(NO_HMAC) PKCS11TEST_FUNC_SESS_DECL(test_concatenate_hybrid_agreement), #endif +#ifndef WOLFPKCS11_NO_STORE + PKCS11TEST_FUNC_SESS_DECL(test_mlkem_attrs_logged_out), +#endif #endif #ifdef WOLFPKCS11_LMS PKCS11TEST_FUNC_SESS_DECL(test_hss_verify_kat), diff --git a/wolfpkcs11/internal.h b/wolfpkcs11/internal.h index 1c269517..44ac67a2 100644 --- a/wolfpkcs11/internal.h +++ b/wolfpkcs11/internal.h @@ -574,6 +574,8 @@ WP11_LOCAL int WP11_Object_OnToken(WP11_Object* object); WP11_LOCAL void WP11_Object_Persist(WP11_Object* object); WP11_LOCAL int WP11_KeyType_IsSecret(CK_KEY_TYPE type); WP11_LOCAL CK_KEY_TYPE WP11_Object_GetType(WP11_Object* object); +WP11_LOCAL WP11_Slot* WP11_Object_GetSlot(WP11_Object* object); +WP11_LOCAL int WP11_Object_KeyState(WP11_Object* object); WP11_LOCAL CK_ULONG WP11_Object_GetDevId(WP11_Object* object); WP11_LOCAL int WP11_Object_SetRsaKey(WP11_Object* object, unsigned char** data,