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] 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;