diff --git a/src/crypto.c b/src/crypto.c index 27b664e0..35b7d0f1 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -580,12 +580,29 @@ static CK_RV CheckAttributes(CK_ATTRIBUTE* pTemplate, CK_ULONG ulCount, int set) 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) +{ + CK_OBJECT_CLASS objClass = WP11_Object_GetClass(obj); + + if (objClass != CKO_PUBLIC_KEY && objClass != CKO_PRIVATE_KEY && + objClass != CKO_SECRET_KEY && + (objClass != CKO_DATA || op != CKA_DERIVE)) { + return (int)CKR_KEY_HANDLE_INVALID; + } + return CKR_OK; +} + static int CheckOpSupportedRv(WP11_Object* obj, CK_ATTRIBUTE_TYPE op, CK_RV errRv) { CK_BBOOL haveOp = 0; CK_ULONG dataLen = sizeof(haveOp); - int ret = WP11_Object_GetAttr(obj, op, &haveOp, &dataLen); + int ret = CheckKeyObject(obj, op); + if (ret != CKR_OK) + return ret; + ret = WP11_Object_GetAttr(obj, op, &haveOp, &dataLen); if (ret != 0) return ret; if (!haveOp) { @@ -1577,10 +1594,14 @@ static CK_RV AddRSAPrivateKeyObject(WP11_Session* session, /** * Create an object in the session or on the token associated with the session. * - * @param hSession [in] Handle of session. - * @param pTemplate [in] Template of attributes for object. - * @param ulCount [in] Number of attribute triplets in template. - * @param object [out] New Object object. + * @param hSession [in] Handle of session. + * @param pTemplate [in] Template of attributes for object. + * @param ulCount [in] Number of attribute triplets in template. + * @param impliedClass [in] Class the API creates when the template has no + * CKA_CLASS, or WP11_NO_IMPLICIT_CLASS. A caller + * passing a class checks the template first with + * CheckSecretKeyTemplate. + * @param object [out] New Object object. * @return CKR_CRYPTOKI_NOT_INITIALIZED when library not initialized. * CKR_SESSION_HANDLE_INVALID when session handle is not valid. * CKR_ARGUMENTS_BAD when pTemplate or phObject is NULL. @@ -1593,11 +1614,12 @@ static CK_RV AddRSAPrivateKeyObject(WP11_Session* session, * CKR_OK on success. */ static CK_RV CreateObject(WP11_Session* session, CK_ATTRIBUTE_PTR pTemplate, - CK_ULONG ulCount, WP11_Object** object) + CK_ULONG ulCount, CK_OBJECT_CLASS impliedClass, + WP11_Object** object) { CK_RV rv; CK_ULONG objType = -1; - CK_OBJECT_CLASS objectClass = -1; + CK_OBJECT_CLASS objectClass = impliedClass; CK_ATTRIBUTE* attr; FindAttributeType(pTemplate, ulCount, CKA_CLASS, &attr); @@ -1692,6 +1714,12 @@ static CK_RV CreateObject(WP11_Session* session, CK_ATTRIBUTE_PTR pTemplate, default: return CKR_ATTRIBUTE_VALUE_INVALID; } + + /* Secret key types and the secret key class only go together. */ + if ((objectClass == CKO_SECRET_KEY) != + (WP11_KeyType_IsSecret(objType) != 0)) { + return CKR_TEMPLATE_INCONSISTENT; + } } rv = NewObject(session, objType, objectClass, pTemplate, ulCount, object); @@ -1699,6 +1727,40 @@ static CK_RV CreateObject(WP11_Session* session, CK_ATTRIBUTE_PTR pTemplate, return rv; } +/* Validate the template for a secret key an operation creates, before any + * key material is computed for it. */ +static CK_RV CheckSecretKeyTemplate(CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount) +{ + CK_RV rv = CKR_OK; + CK_ATTRIBUTE* attr; + + FindAttributeType(pTemplate, ulCount, CKA_CLASS, &attr); + if (attr != NULL) { + if (attr->pValue == NULL || + attr->ulValueLen != sizeof(CK_OBJECT_CLASS)) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + } + else if (*(CK_OBJECT_CLASS*)attr->pValue != CKO_SECRET_KEY) { + rv = CKR_TEMPLATE_INCONSISTENT; + } + } + if (rv == CKR_OK) { + FindAttributeType(pTemplate, ulCount, CKA_KEY_TYPE, &attr); + if (attr == NULL) + rv = CKR_TEMPLATE_INCOMPLETE; + else if (attr->pValue == NULL || + attr->ulValueLen != sizeof(CK_KEY_TYPE)) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + } + else if (!WP11_KeyType_IsSecret(*(CK_KEY_TYPE*)attr->pValue)) { + rv = CKR_TEMPLATE_INCONSISTENT; + } + } + + return rv; +} + /* Find an attribute that must be supplied with a non-empty value. */ static CK_RV RequireAttribute(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_ATTRIBUTE_TYPE type) @@ -1928,7 +1990,8 @@ static CK_RV wp11_C_CreateObject(CK_SESSION_HANDLE hSession, return rv; } - rv = CreateObject(session, pTemplate, ulCount, &object); + rv = CreateObject(session, pTemplate, ulCount, WP11_NO_IMPLICIT_CLASS, + &object); if (rv != CKR_OK) { WOLFPKCS11_LEAVE("C_CreateObject", rv); return rv; @@ -9556,6 +9619,8 @@ static CK_RV wp11_C_WrapKey(CK_SESSION_HANDLE hSession, return CKR_WRAPPING_KEY_HANDLE_INVALID; ret = CheckOpSupported(wrappingKey, CKA_WRAP); + if (ret == CKR_KEY_HANDLE_INVALID) + return CKR_WRAPPING_KEY_HANDLE_INVALID; if (ret != CKR_OK) return ret; @@ -9882,6 +9947,8 @@ static CK_RV wp11_C_UnwrapKey(CK_SESSION_HANDLE hSession, return CKR_UNWRAPPING_KEY_HANDLE_INVALID; ret = CheckOpSupported(unwrappingKey, CKA_UNWRAP); + if (ret == CKR_KEY_HANDLE_INVALID) + return CKR_UNWRAPPING_KEY_HANDLE_INVALID; if (ret != CKR_OK) return ret; @@ -10027,7 +10094,8 @@ static CK_RV wp11_C_UnwrapKey(CK_SESSION_HANDLE hSession, CK_ULONG keyDataLens[2] = { sizeof(CK_ULONG), ulUnwrappedLen }; *phKey = CK_INVALID_HANDLE; - rv = CreateObject(session, pTemplate, ulAttributeCount, &keyObj); + rv = CreateObject(session, pTemplate, ulAttributeCount, + WP11_NO_IMPLICIT_CLASS, &keyObj); if (rv == CKR_OK) { if (WP11_Object_SetSecretKey(keyObj, keyData, keyDataLens) != 0) rv = CKR_FUNCTION_FAILED; @@ -10149,9 +10217,10 @@ static int SetKeyExtract(WP11_Session* session, byte* ptr, CK_ULONG length, unsigned char* secretKeyData[2] = { NULL, NULL }; CK_ULONG secretKeyLen[2] = { 0, 0 }; - ret = (int)CreateObject(session, pTemplate, ulAttributeCount, &secret); + ret = (int)CreateObject(session, pTemplate, ulAttributeCount, + CKO_SECRET_KEY, &secret); if (ret != 0) - return CKR_OBJECT_HANDLE_INVALID; + return ret; ret = SymmKeyLen(secret, (word32)length, &symmKeyLen); if (ret == 0) { @@ -10399,12 +10468,16 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, * behavior (WP11_NSS_PERMISSIVE_KEY_DEFAULTS is auto-enabled for NSS). */ #ifndef WP11_NSS_PERMISSIVE_KEY_DEFAULTS ret = CheckOpSupported(obj, CKA_DERIVE); +#else + ret = CheckKeyObject(obj, CKA_DERIVE); +#endif if (ret != CKR_OK) return ret; -#endif rv = CheckPrivateLogin(session, pTemplate, ulAttributeCount, CKO_SECRET_KEY); + if (rv == CKR_OK) + rv = CheckSecretKeyTemplate(pTemplate, ulAttributeCount); if (rv != CKR_OK) return rv; @@ -10649,7 +10722,10 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, XFREE(derivedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); derivedKey = NULL; - if (ret != 0) + /* Key extraction reports a CK_RV, the derive a wolfCrypt error. */ + if (ret > 0) + rv = (CK_RV)ret; + else if (ret != 0) rv = CKR_FUNCTION_FAILED; break; } @@ -10772,8 +10848,10 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession, #endif if (reqValueLen > keyLen) rv = CKR_ATTRIBUTE_VALUE_INVALID; - if (rv == CKR_OK) - rv = CreateObject(session, pTemplate, ulAttributeCount, &obj); + 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 @@ -11416,6 +11494,8 @@ static CK_RV wp11_C_EncapsulateKey(CK_SESSION_HANDLE hSession, /* Refuse before the size query or any ciphertext is written. */ rv = CheckPrivateLogin(session, pTemplate, ulAttributeCount, CKO_SECRET_KEY); + if (rv == CKR_OK) + rv = CheckSecretKeyTemplate(pTemplate, ulAttributeCount); if (rv != CKR_OK) return rv; @@ -11443,7 +11523,8 @@ static CK_RV wp11_C_EncapsulateKey(CK_SESSION_HANDLE hSession, return CKR_OK; if (rv == CKR_OK) { - rv = CreateObject(session, pTemplate, ulAttributeCount, &secretObj); + rv = CreateObject(session, pTemplate, ulAttributeCount, + CKO_SECRET_KEY, &secretObj); if (rv == CKR_OK) { secretKeyData[1] = derivedKey; secretKeyLen[1] = keyLen; @@ -11559,6 +11640,8 @@ static CK_RV wp11_C_DecapsulateKey(CK_SESSION_HANDLE hSession, /* Refuse before the private key is used. */ rv = CheckPrivateLogin(session, pTemplate, ulAttributeCount, CKO_SECRET_KEY); + if (rv == CKR_OK) + rv = CheckSecretKeyTemplate(pTemplate, ulAttributeCount); if (rv != CKR_OK) return rv; @@ -11582,7 +11665,8 @@ static CK_RV wp11_C_DecapsulateKey(CK_SESSION_HANDLE hSession, } if (rv == CKR_OK) { - rv = CreateObject(session, pTemplate, ulAttributeCount, &secretObj); + rv = CreateObject(session, pTemplate, ulAttributeCount, + CKO_SECRET_KEY, &secretObj); if (rv == CKR_OK) { secretKeyData[1] = derivedKey; secretKeyLen[1] = keyLen; diff --git a/src/internal.c b/src/internal.c index 32c00efb..cdee03ec 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1915,10 +1915,10 @@ static int wolfPKCS11_Store_Name(int type, CK_ULONG id1, CK_ULONG id2, char* nam { int ret = 0; const char* str = NULL; - /* Reserve enough space in the final filename for suffixes such as - * "/wp11_rsakey_priv_%016lx_%016lx" (47 chars worst-case). - */ - enum { WP11_STORE_SUFFIX_RESERVE = 48 }; + const char* kind = NULL; + /* XSNPRINTF is unbounded in some builds, so this must cover the longest + * name below: "/wp11_mldsakey_priv_" and two 16 digit ids. */ + enum { WP11_STORE_SUFFIX_RESERVE = 53 }; #ifndef WOLFPKCS11_NO_ENV char homePath[256]; #endif @@ -1995,7 +1995,6 @@ static int wolfPKCS11_Store_Name(int type, CK_ULONG id1, CK_ULONG id2, char* nam } #endif - /* 47 is maximum number of character to a filename and path separator. */ if (str == NULL) { return -1; } @@ -2009,91 +2008,98 @@ static int wolfPKCS11_Store_Name(int type, CK_ULONG id1, CK_ULONG id2, char* nam /* Set different filename for each type of data and different ids. */ switch (type) { case WOLFPKCS11_STORE_TOKEN: - ret = XSNPRINTF(name, nameLen, "%s/wp11_token_%016lx", str, id1); + kind = "token"; break; case WOLFPKCS11_STORE_OBJECT: - ret = XSNPRINTF(name, nameLen, "%s/wp11_obj_%016lx_%016lx", str, id1, - id2); + kind = "obj"; break; case WOLFPKCS11_STORE_SYMMKEY: - ret = XSNPRINTF(name, nameLen, "%s/wp11_symmkey_%016lx_%016lx", str, - id1, id2); + kind = "symmkey"; break; case WOLFPKCS11_STORE_RSAKEY_PRIV: - ret = XSNPRINTF(name, nameLen, "%s/wp11_rsakey_priv_%016lx_%016lx", - str, id1, id2); + kind = "rsakey_priv"; break; case WOLFPKCS11_STORE_RSAKEY_PUB: - ret = XSNPRINTF(name, nameLen, "%s/wp11_rsakey_pub_%016lx_%016lx", - str, id1, id2); + kind = "rsakey_pub"; break; case WOLFPKCS11_STORE_ECCKEY_PRIV: - ret = XSNPRINTF(name, nameLen, "%s/wp11_ecckey_priv_%016lx_%016lx", - str, id1, id2); + kind = "ecckey_priv"; break; case WOLFPKCS11_STORE_ECCKEY_PUB: - ret = XSNPRINTF(name, nameLen, "%s/wp11_ecckey_pub_%016lx_%016lx", - str, id1, id2); + kind = "ecckey_pub"; break; case WOLFPKCS11_STORE_DHKEY_PRIV: - ret = XSNPRINTF(name, nameLen, "%s/wp11_dhkey_priv_%016lx_%016lx", - str, id1, id2); + kind = "dhkey_priv"; break; case WOLFPKCS11_STORE_DHKEY_PUB: - ret = XSNPRINTF(name, nameLen, "%s/wp11_dhkey_pub_%016lx_%016lx", - str, id1, id2); + kind = "dhkey_pub"; break; case WOLFPKCS11_STORE_CERT: - ret = XSNPRINTF(name, nameLen, "%s/wp11_cert_%016lx_%016lx", - str, id1, id2); + kind = "cert"; break; case WOLFPKCS11_STORE_TRUST: - ret = XSNPRINTF(name, nameLen, "%s/wp11_trust_%016lx_%016lx", - str, id1, id2); + kind = "trust"; break; case WOLFPKCS11_STORE_DATA: - ret = XSNPRINTF(name, nameLen, "%s/wp11_data_%016lx_%016lx", - str, id1, id2); + kind = "data"; break; case WOLFPKCS11_STORE_MLDSAKEY_PRIV: - ret = XSNPRINTF(name, nameLen, "%s/wp11_mldsakey_priv_%016lx_%016lx", - str, id1, id2); + kind = "mldsakey_priv"; break; case WOLFPKCS11_STORE_MLDSAKEY_PUB: - ret = XSNPRINTF(name, nameLen, "%s/wp11_mldsakey_pub_%016lx_%016lx", - str, id1, id2); + kind = "mldsakey_pub"; break; #ifdef WOLFPKCS11_MLKEM case WOLFPKCS11_STORE_MLKEMKEY_PRIV: - ret = XSNPRINTF(name, nameLen, "%s/wp11_mlkemkey_priv_%016lx_%016lx", - str, id1, id2); + kind = "mlkemkey_priv"; break; case WOLFPKCS11_STORE_MLKEMKEY_PUB: - ret = XSNPRINTF(name, nameLen, "%s/wp11_mlkemkey_pub_%016lx_%016lx", - str, id1, id2); + kind = "mlkemkey_pub"; break; #endif #ifdef WOLFPKCS11_LMS case WOLFPKCS11_STORE_HSSKEY_PUB: - ret = XSNPRINTF(name, nameLen, "%s/wp11_hsskey_pub_%016lx_%016lx", - str, id1, id2); + kind = "hsskey_pub"; break; #endif #ifdef WOLFPKCS11_XMSS case WOLFPKCS11_STORE_XMSSKEY_PUB: - ret = XSNPRINTF(name, nameLen, "%s/wp11_xmsskey_pub_%016lx_%016lx", - str, id1, id2); + kind = "xmsskey_pub"; break; #endif default: - ret = -1; - break; + return -1; + } + + if (type == WOLFPKCS11_STORE_TOKEN) { + ret = XSNPRINTF(name, nameLen, "%s/wp11_%s_%016lx", str, kind, id1); + } + else { + ret = XSNPRINTF(name, nameLen, "%s/wp11_%s_%016lx_%016lx", str, kind, + id1, id2); } return ret; } +#ifdef DEBUG_WOLFPKCS11 +/** + * Test hook: build the store file name for a store type under the current + * token path. + * + * @param type [in] Store type. + * @return Length of the name on success. + * -1 when no name can be built for the type. + */ +WP11_API int WP11_Test_StoreName(int type) +{ + char name[WP11_STORE_MAX_PATH]; + + return wolfPKCS11_Store_Name(type, 1, 1, name, (int)sizeof(name)); +} +#endif + /* One token store pass commits as a unit: staged records, then a marker. */ #define WP11_STORE_BATCH #define WP11_STORE_STAGED_MAX (WP11_STORE_MAX_PATH + 8) @@ -2300,6 +2306,7 @@ static int wolfPKCS11_StoreBatchApply(const char* listName, int mode) XFILE list; XFILE staged; size_t len; + size_t stagedLen; size_t i; const char* base; char line[WP11_STORE_MAX_PATH + 2]; @@ -2331,11 +2338,12 @@ static int wolfPKCS11_StoreBatchApply(const char* listName, int mode) } len = (size_t)XSNPRINTF(finalName, sizeof(finalName), "%s/%s", storeBatch.dir, base); + stagedLen = sizeof(stagedName); if (len < sizeof(finalName)) { - len = (size_t)XSNPRINTF(stagedName, sizeof(stagedName), "%s.new", - finalName); + stagedLen = (size_t)XSNPRINTF(stagedName, sizeof(stagedName), + "%s.new", finalName); } - if (len >= sizeof(finalName) || len >= sizeof(stagedName)) { + if (len >= sizeof(finalName) || stagedLen >= sizeof(stagedName)) { if (mode != WP11_BATCH_DISCARD) ret = READ_ONLY_E; continue; @@ -8373,8 +8381,17 @@ static int wp11_Object_Decode(WP11_Object* object) } } - if (ret == 0) + if (ret == 0) { object->undecoded = 0; + /* Older releases stored derived secret keys with no class. Adopt the + * secret key class and rebind the key data to it straight away. */ + if (object->objClass == (CK_OBJECT_CLASS)-1 && + WP11_KeyType_IsSecret(object->type)) { + object->objClass = CKO_SECRET_KEY; + if (wp11_Object_Encode(object, 0) != 0) + object->objClass = (CK_OBJECT_CLASS)-1; + } + } /* Authentication failure means this object isn't for this user. */ if (ret == AES_GCM_AUTH_E) ret = 0; @@ -13402,6 +13419,41 @@ WP11_API int WP11_Test_SetNextObjId(CK_SLOT_ID slotId, int onToken, return 0; } + +#ifndef WOLFPKCS11_NO_STORE +/** + * Test hook: give an object a class and bind its key data to that class, as + * older releases stored derived secret keys that had no class. + * + * @param hSession [in] Session handle. + * @param hObject [in] Object handle. + * @param objClass [in] Class for the object. + * @return BAD_FUNC_ARG when the session or object is not found. + * Other -ve when the key data cannot be bound. + * 0 on success. + */ +WP11_API int WP11_Test_SetObjectClass(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hObject, CK_OBJECT_CLASS objClass) +{ + int ret; + WP11_Session* session; + WP11_Object* object; + + if (WP11_Session_Get(hSession, &session) != 0 || + WP11_Object_Find(session, hObject, &object) != 0) { + return BAD_FUNC_ARG; + } + + if (object->onToken) + WP11_Lock_LockRW(object->lock); + object->objClass = objClass; + ret = wp11_Object_Encode(object, 0); + if (object->onToken) + WP11_Lock_UnlockRW(object->lock); + + return ret; +} +#endif #endif /* DEBUG_WOLFPKCS11 */ /** @@ -13437,8 +13489,7 @@ void WP11_Slot_ClearActiveObject(WP11_Slot* slot, WP11_Object* object) return; keyType = WP11_Object_GetType(object); - symmetric = (keyType == CKK_AES || keyType == CKK_GENERIC_SECRET || - keyType == CKK_HKDF); + symmetric = WP11_KeyType_IsSecret(keyType); WP11_Lock_LockRW(&slot->lock); for (curr = slot->session; curr != NULL; curr = curr->next) { @@ -13880,8 +13931,8 @@ void WP11_Object_Free(WP11_Object* object) object->data.xmssKey = NULL; } #endif - if ((object->type == CKK_AES || object->type == CKK_GENERIC_SECRET || - object->type == CKK_HKDF) && object->data.symmKey != NULL) { + if (WP11_KeyType_IsSecret(object->type) && + object->data.symmKey != NULL) { word32 zeroLen = object->data.symmKey->len; /* Clamp to the fixed buffer as a backstop against a length * recorded larger than the buffer. */ @@ -13971,6 +14022,19 @@ void WP11_Object_Persist(WP11_Object* object) #endif } +/** + * Check whether a key type is a secret key type, holding its value in + * symmKey. + * + * @param type [in] Key type. + * @return 1 when the key type is a secret key type. + * 0 otherwise. + */ +int WP11_KeyType_IsSecret(CK_KEY_TYPE type) +{ + return type == CKK_AES || type == CKK_GENERIC_SECRET || type == CKK_HKDF; +} + /** * Get the object's type - for example the key type. * @@ -14642,8 +14706,9 @@ int WP11_Object_SetMlKemKey(WP11_Object* object, unsigned char** data, * @param object [in] Object object. * @param data [in] Array of byte arrays. * @param len [in] Array of lengths of byte arrays. - * @return BAD_FUNC_ARG when key length data is not size of CK_ULONG or - * key length is not a valid size for the key type. + * @return BAD_FUNC_ARG when the object is not a secret key with secret key + * storage, key length data is not size of CK_ULONG or key length is + * not a valid size for the key type. * BUFFER_E when key data length is less than key length data value. * Other -ve on failure. * 0 on success. @@ -14656,6 +14721,13 @@ int WP11_Object_SetSecretKey(WP11_Object* object, unsigned char** data, CK_ULONG keyLen = 0; word32 newLen = 0; + /* Other classes and key types use the storage union differently. */ + if (object->objClass != CKO_SECRET_KEY || + !WP11_KeyType_IsSecret(object->type) || + object->data.symmKey == NULL) { + return BAD_FUNC_ARG; + } + if (object->onToken) WP11_Lock_LockRW(object->lock); @@ -15893,8 +15965,7 @@ int WP11_Generic_SerializeKey(WP11_Object* object, byte* output, word32* poutsz) if (object == NULL || poutsz == NULL) return PARAM_E; - if (object->type != CKK_AES && object->type != CKK_GENERIC_SECRET - && object->type != CKK_HKDF) + if (!WP11_KeyType_IsSecret(object->type)) return OBJ_TYPE_E; if (object->objClass != CKO_SECRET_KEY) @@ -18909,11 +18980,7 @@ int WP11_KDF_Derive(WP11_Session* session, CK_HKDF_PARAMS_PTR params, return CKR_OBJECT_HANDLE_INVALID; /* Only these secret key types hold their value in symmKey. */ if (saltKey->objClass != CKO_SECRET_KEY || - (saltKey->type != CKK_GENERIC_SECRET && -#ifndef NO_AES - saltKey->type != CKK_AES && -#endif - saltKey->type != CKK_HKDF) || + !WP11_KeyType_IsSecret(saltKey->type) || saltKey->data.symmKey == NULL) return CKR_MECHANISM_PARAM_INVALID; salt = saltKey->data.symmKey->data; @@ -21409,9 +21476,8 @@ int WP11_Digest_Key(WP11_Object* key, WP11_Session* session) #ifndef WOLFPKCS11_NO_STORE WP11_Digest* digest = &session->params.digest; - if (key->objClass != CKO_SECRET_KEY || (key->type != CKK_AES && - key->type != CKK_GENERIC_SECRET && key->type != CKK_HKDF) || - key->data.symmKey == NULL) { + if (key->objClass != CKO_SECRET_KEY || + !WP11_KeyType_IsSecret(key->type) || key->data.symmKey == NULL) { return (int)CKR_KEY_INDIGESTIBLE; } if (key->encoded) diff --git a/src/slot.c b/src/slot.c index ebf95c58..0ba5b84d 100644 --- a/src/slot.c +++ b/src/slot.c @@ -623,42 +623,45 @@ static CK_MECHANISM_INFO shaRsaPkcsMechInfo = { }; #endif #ifdef HAVE_ECC +/* Only named prime curves with uncompressed points are supported. */ +#define WP11_EC_MECH_FLAGS \ + (CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS) /* Info on EC key generation mechanism. */ static CK_MECHANISM_INFO ecKgMechInfo = { - 256, 521, CKF_GENERATE_KEY_PAIR + 256, 521, CKF_GENERATE_KEY_PAIR | WP11_EC_MECH_FLAGS }; /* Info on ECDSA mechanism. */ static CK_MECHANISM_INFO ecdsaMechInfo = { - 256, 521, CKF_SIGN | CKF_VERIFY + 256, 521, CKF_SIGN | CKF_VERIFY | WP11_EC_MECH_FLAGS }; #ifndef NO_SHA static CK_MECHANISM_INFO ecdsaSha1MechInfo = { - 256, 521, CKF_SIGN | CKF_VERIFY + 256, 521, CKF_SIGN | CKF_VERIFY | WP11_EC_MECH_FLAGS }; #endif #ifdef WOLFSSL_SHA224 static CK_MECHANISM_INFO ecdsaSha224MechInfo = { - 256, 521, CKF_SIGN | CKF_VERIFY + 256, 521, CKF_SIGN | CKF_VERIFY | WP11_EC_MECH_FLAGS }; #endif #ifndef NO_SHA256 static CK_MECHANISM_INFO ecdsaSha256MechInfo = { - 256, 521, CKF_SIGN | CKF_VERIFY + 256, 521, CKF_SIGN | CKF_VERIFY | WP11_EC_MECH_FLAGS }; #endif #ifdef WOLFSSL_SHA384 static CK_MECHANISM_INFO ecdsaSha384MechInfo = { - 256, 521, CKF_SIGN | CKF_VERIFY + 256, 521, CKF_SIGN | CKF_VERIFY | WP11_EC_MECH_FLAGS }; #endif #ifdef WOLFSSL_SHA512 static CK_MECHANISM_INFO ecdsaSha512MechInfo = { - 256, 521, CKF_SIGN | CKF_VERIFY + 256, 521, CKF_SIGN | CKF_VERIFY | WP11_EC_MECH_FLAGS }; #endif /* Info on ECDH mechanism. */ static CK_MECHANISM_INFO ecdhMechInfo = { - 256, 521, CKF_DERIVE + 256, 521, CKF_DERIVE | WP11_EC_MECH_FLAGS }; #endif #ifdef WOLFPKCS11_MLDSA diff --git a/tests/include.am b/tests/include.am index 6227e4f4..21098b8f 100644 --- a/tests/include.am +++ b/tests/include.am @@ -395,6 +395,10 @@ noinst_PROGRAMS += tests/secret_scrub_test tests_secret_scrub_test_SOURCES = tests/secret_scrub_test.c tests_secret_scrub_test_LDADD = +noinst_PROGRAMS += tests/legacy_class_load_test +tests_legacy_class_load_test_SOURCES = tests/legacy_class_load_test.c +tests_legacy_class_load_test_LDADD = + noinst_PROGRAMS += tests/unwrap_keygen_test tests_unwrap_keygen_test_SOURCES = tests/unwrap_keygen_test.c tests_unwrap_keygen_test_LDADD = @@ -416,6 +420,7 @@ check_PROGRAMS += tests/object_store_decode_test check_PROGRAMS += tests/mlkem_secret_scrub_test check_PROGRAMS += tests/token_reset_fail_test check_PROGRAMS += tests/unwrap_keygen_test +check_PROGRAMS += tests/legacy_class_load_test endif if BUILD_STATIC @@ -500,6 +505,7 @@ tests_encrypt_decrypt_params_test_LDADD += src/libwolfpkcs11.la tests_sign_verify_mac_test_LDADD += src/libwolfpkcs11.la tests_digest_length_test_LDADD += src/libwolfpkcs11.la tests_secret_scrub_test_LDADD += src/libwolfpkcs11.la +tests_legacy_class_load_test_LDADD += src/libwolfpkcs11.la tests_unwrap_keygen_test_LDADD += src/libwolfpkcs11.la tests_token_partial_store_test_LDADD += src/libwolfpkcs11.la else @@ -576,6 +582,7 @@ tests_encrypt_decrypt_params_test_LDADD += src/libwolfpkcs11.la tests_sign_verify_mac_test_LDADD += src/libwolfpkcs11.la tests_digest_length_test_LDADD += src/libwolfpkcs11.la tests_secret_scrub_test_LDADD += src/libwolfpkcs11.la +tests_legacy_class_load_test_LDADD += src/libwolfpkcs11.la tests_unwrap_keygen_test_LDADD += src/libwolfpkcs11.la tests_token_partial_store_test_LDADD += src/libwolfpkcs11.la endif diff --git a/tests/info_mech_table_test.c b/tests/info_mech_table_test.c index 9a44cda3..634b78aa 100644 --- a/tests/info_mech_table_test.c +++ b/tests/info_mech_table_test.c @@ -317,6 +317,40 @@ static void test_listed_keygen_supported(CK_SLOT_ID slot, } } +#ifdef HAVE_ECC +/* EC mechanisms report the prime field curves, named curve parameters and + * uncompressed points that the token supports, and nothing else. */ +static void test_ec_mech_flags(CK_SLOT_ID slot) +{ + static const CK_MECHANISM_TYPE mechs[] = { + CKM_EC_KEY_PAIR_GEN, CKM_ECDSA, CKM_ECDSA_SHA1, CKM_ECDSA_SHA224, + CKM_ECDSA_SHA256, CKM_ECDSA_SHA384, CKM_ECDSA_SHA512, CKM_ECDH1_DERIVE + }; + const CK_FLAGS want = CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS; + const CK_FLAGS unsupported = CKF_EC_F_2M | CKF_EC_ECPARAMETERS | + CKF_EC_COMPRESS; + CK_MECHANISM_INFO info; + CK_RV rv; + size_t i; + char msg[96]; + + for (i = 0; i < sizeof(mechs) / sizeof(mechs[0]); i++) { + rv = funcList->C_GetMechanismInfo(slot, mechs[i], &info); + if (rv == CKR_MECHANISM_INVALID) + continue; + snprintf(msg, sizeof(msg), "C_GetMechanismInfo(0x%lx)", + (unsigned long)mechs[i]); + CHECK_RV(rv, msg, CKR_OK); + if (rv != CKR_OK) + continue; + snprintf(msg, sizeof(msg), "EC capability flags of 0x%lx", + (unsigned long)mechs[i]); + CHECK_TRUE((info.flags & want) == want && + (info.flags & unsupported) == 0, msg); + } +} +#endif + #ifdef ALLOC_HOOKS /* A session is reported open only when a usable handle was returned. */ static void test_open_session_alloc_failure(CK_SLOT_ID slot) @@ -964,6 +998,9 @@ static int run_test(void) if (rv == CKR_OK) { test_generic_secret_size(slotList[0], session); test_listed_keygen_supported(slotList[0], session); +#ifdef HAVE_ECC + test_ec_mech_flags(slotList[0]); +#endif #ifdef ALLOC_HOOKS test_open_session_alloc_failure(slotList[0]); #endif diff --git a/tests/legacy_class_load_test.c b/tests/legacy_class_load_test.c new file mode 100644 index 00000000..04320e17 --- /dev/null +++ b/tests/legacy_class_load_test.c @@ -0,0 +1,402 @@ +/* legacy_class_load_test.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPKCS11. + * + * wolfPKCS11 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPKCS11 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * Older releases stored secret keys derived from a template without CKA_CLASS + * with no object class. A token holding such keys must load them as secret + * keys that still work, and keep them loadable if they cannot be rebound to + * the secret key class straight away. The DEBUG_WOLFPKCS11-only hooks build + * those stored keys and check that an unknown store type gets no file name. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/legacy_class_load_test" + +#if defined(DEBUG_WOLFPKCS11) && !defined(WOLFPKCS11_NO_STORE) && \ + !defined(NO_AES) && defined(HAVE_AES_CBC) + +/* DEBUG_WOLFPKCS11-only test hooks exported by libwolfpkcs11, with C linkage + * as in wolfpkcs11/internal.h. */ +#ifdef __cplusplus +extern "C" { +#endif +extern int WP11_Test_SetObjectClass(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hObject, CK_OBJECT_CLASS objClass); +#if !defined(WOLFPKCS11_CUSTOM_STORE) && !defined(WOLFPKCS11_TPM_STORE) +extern int WP11_Test_StoreName(int type); +#endif +#ifdef __cplusplus +} +#endif + +#if defined(USE_WOLFSSL_MEMORY) && !defined(NO_WOLFSSL_MEMORY) && \ + !defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_DEBUG_MEMORY) + #define ALLOC_HOOKS +#endif + +/* The generic key re-encodes into a buffer of this length plus a tag, a size + * nothing else allocates while logging in. */ +#define ODD_KEY_LEN 91 + +#ifdef ALLOC_HOOKS +static size_t failSize = 0; + +static void* hook_malloc(size_t sz) +{ + if (failSize != 0 && sz == failSize) { + failSize = 0; + return NULL; + } + return malloc(sz); +} + +static void hook_free(void* ptr) +{ + free(ptr); +} + +static void* hook_realloc(void* ptr, size_t sz) +{ + return realloc(ptr, sz); +} +#endif + +static CK_SLOT_ID slot = 0; +static const char* soPin = "password123456"; +static const char* userPin = "wolfpkcs11-test"; +static const char tokenLabel[] = "legacy-class-load"; +static const char aesLabel[] = "legacy-aes"; +static const char genLabel[] = "legacy-generic"; + +static CK_RV lib_init(void) +{ + CK_C_INITIALIZE_ARGS args; + CK_SLOT_ID slotList[16]; + CK_ULONG slotCount = sizeof(slotList) / sizeof(slotList[0]); + CK_RV rv; + + XMEMSET(&args, 0, sizeof(args)); + args.flags = CKF_OS_LOCKING_OK; + rv = funcList->C_Initialize(&args); + if (rv == CKR_OK) + rv = funcList->C_GetSlotList(CK_TRUE, slotList, &slotCount); + if (rv == CKR_OK && slotCount == 0) + rv = CKR_TOKEN_NOT_PRESENT; + if (rv == CKR_OK) + slot = slotList[0]; + return rv; +} + +static CK_RV token_setup(void) +{ + CK_SESSION_HANDLE soSession = 0; + unsigned char label[32]; + CK_RV rv; + + XMEMSET(label, ' ', sizeof(label)); + XMEMCPY(label, tokenLabel, XSTRLEN(tokenLabel)); + rv = funcList->C_InitToken(slot, (CK_UTF8CHAR_PTR)soPin, + (CK_ULONG)XSTRLEN(soPin), label); + if (rv == CKR_OK) { + rv = funcList->C_OpenSession(slot, + CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &soSession); + } + if (rv == CKR_OK) { + rv = funcList->C_Login(soSession, CKU_SO, (CK_UTF8CHAR_PTR)soPin, + (CK_ULONG)XSTRLEN(soPin)); + } + if (rv == CKR_OK) { + rv = funcList->C_InitPIN(soSession, (CK_UTF8CHAR_PTR)userPin, + (CK_ULONG)XSTRLEN(userPin)); + funcList->C_Logout(soSession); + } + if (soSession != 0) + funcList->C_CloseSession(soSession); + return rv; +} + +static CK_RV user_login(CK_SESSION_HANDLE session) +{ + return funcList->C_Login(session, CKU_USER, (CK_UTF8CHAR_PTR)userPin, + (CK_ULONG)XSTRLEN(userPin)); +} + +static CK_RV open_user_session(CK_SESSION_HANDLE* session) +{ + CK_RV rv; + + rv = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, + NULL, NULL, session); + if (rv == CKR_OK) + rv = user_login(*session); + return rv; +} + +static CK_RV create_token_key(CK_SESSION_HANDLE session, CK_KEY_TYPE keyType, + const unsigned char* value, CK_ULONG len, + const char* label, CK_OBJECT_HANDLE* key) +{ + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_BBOOL ckTrue = CK_TRUE; + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + { CKA_VALUE, (void*)value, len }, + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, + { CKA_LABEL, (void*)label, (CK_ULONG)XSTRLEN(label) }, + }; + + *key = CK_INVALID_HANDLE; + return funcList->C_CreateObject(session, tmpl, + sizeof(tmpl) / sizeof(*tmpl), key); +} + +static CK_OBJECT_HANDLE find_key(CK_SESSION_HANDLE session, const char* label) +{ + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + CK_ULONG count = 0; + CK_ATTRIBUTE tmpl[] = { + { CKA_LABEL, (void*)label, (CK_ULONG)XSTRLEN(label) }, + }; + + if (funcList->C_FindObjectsInit(session, tmpl, 1) == CKR_OK) { + if (funcList->C_FindObjects(session, &key, 1, &count) != CKR_OK || + count != 1) { + key = CK_INVALID_HANDLE; + } + funcList->C_FindObjectsFinal(session); + } + return key; +} + +static CK_OBJECT_CLASS key_class(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE key) +{ + CK_OBJECT_CLASS objClass = CKO_DATA; + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &objClass, sizeof(objClass) }, + }; + + if (funcList->C_GetAttributeValue(session, key, tmpl, 1) != CKR_OK) + objClass = CKO_DATA; + return objClass; +} + +static CK_RV encrypt_block(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, + unsigned char* out) +{ + unsigned char iv[16]; + unsigned char plain[16]; + CK_ULONG outLen = 16; + CK_MECHANISM mech = { CKM_AES_CBC, iv, sizeof(iv) }; + CK_RV rv; + + XMEMSET(iv, 0, sizeof(iv)); + XMEMSET(plain, 0x42, sizeof(plain)); + rv = funcList->C_EncryptInit(session, &mech, key); + if (rv == CKR_OK) + rv = funcList->C_Encrypt(session, plain, sizeof(plain), out, &outLen); + return rv; +} + +static void run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE aesKey = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE genKey = CK_INVALID_HANDLE; + unsigned char aesValue[16]; + unsigned char genValue[ODD_KEY_LEN]; + unsigned char expect[16]; + unsigned char got[16]; + + XMEMSET(aesValue, 0x5a, sizeof(aesValue)); + XMEMSET(genValue, 0x3c, sizeof(genValue)); + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return; + + /* Store an AES and a generic secret key with no class. */ + rv = lib_init(); + CHECK_RV(rv, "C_Initialize", CKR_OK); + if (rv == CKR_OK) { + rv = token_setup(); + CHECK_RV(rv, "token setup", CKR_OK); + } + if (rv == CKR_OK) { + rv = open_user_session(&session); + CHECK_RV(rv, "user session", CKR_OK); + } + if (rv == CKR_OK) { + rv = create_token_key(session, CKK_AES, aesValue, sizeof(aesValue), + aesLabel, &aesKey); + CHECK_RV(rv, "create AES token key", CKR_OK); + } + if (rv == CKR_OK) { + rv = create_token_key(session, CKK_GENERIC_SECRET, genValue, + sizeof(genValue), genLabel, &genKey); + CHECK_RV(rv, "create generic token key", CKR_OK); + } + if (rv == CKR_OK) { + rv = encrypt_block(session, aesKey, expect); + CHECK_RV(rv, "encrypt with AES key", CKR_OK); + } + if (rv == CKR_OK) { + CHECK_TRUE(WP11_Test_SetObjectClass(session, aesKey, + (CK_OBJECT_CLASS)-1) == 0, "store AES key with no class"); + CHECK_TRUE(WP11_Test_SetObjectClass(session, genKey, + (CK_OBJECT_CLASS)-1) == 0, + "store generic key with no class"); + CHECK_TRUE(key_class(session, aesKey) == (CK_OBJECT_CLASS)-1, + "AES key has no class"); + CHECK_TRUE(WP11_Test_SetObjectClass(session, CK_INVALID_HANDLE, + (CK_OBJECT_CLASS)-1) != 0, "no class for a bad handle"); + } + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + + /* Load the token, failing the generic key's rebind once. */ + rv = lib_init(); + CHECK_RV(rv, "C_Initialize to load keys with no class", CKR_OK); +#ifdef ALLOC_HOOKS + failSize = ODD_KEY_LEN + 16; +#endif + if (rv == CKR_OK) { + rv = open_user_session(&session); + CHECK_RV(rv, "user session with keys with no class", CKR_OK); + } +#ifdef ALLOC_HOOKS + CHECK_TRUE(failSize == 0, "generic key rebind was attempted"); + failSize = 0; +#endif + if (rv == CKR_OK) { + aesKey = find_key(session, aesLabel); + genKey = find_key(session, genLabel); + CHECK_TRUE(key_class(session, aesKey) == CKO_SECRET_KEY, + "AES key loads as a secret key"); +#ifdef ALLOC_HOOKS + CHECK_TRUE(key_class(session, genKey) == (CK_OBJECT_CLASS)-1, + "generic key keeps its stored class when not rebound"); +#endif + XMEMSET(got, 0, sizeof(got)); + rv = encrypt_block(session, aesKey, got); + CHECK_RV(rv, "encrypt with loaded AES key", CKR_OK); + CHECK_TRUE(XMEMCMP(got, expect, sizeof(got)) == 0, + "loaded AES key gives the same ciphertext"); + } + if (rv == CKR_OK) { + funcList->C_Logout(session); + rv = user_login(session); + CHECK_RV(rv, "log in again", CKR_OK); + } + if (rv == CKR_OK) { + CHECK_TRUE(key_class(session, genKey) == CKO_SECRET_KEY, + "generic key is a secret key after logging in again"); + } + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + + /* The secret key class was stored with the keys. */ + rv = lib_init(); + CHECK_RV(rv, "C_Initialize to load rebound keys", CKR_OK); + if (rv == CKR_OK) { + rv = open_user_session(&session); + CHECK_RV(rv, "user session with rebound keys", CKR_OK); + } + if (rv == CKR_OK) { + aesKey = find_key(session, aesLabel); + genKey = find_key(session, genLabel); + CHECK_TRUE(key_class(session, aesKey) == CKO_SECRET_KEY && + key_class(session, genKey) == CKO_SECRET_KEY, + "rebound keys load as secret keys"); + XMEMSET(got, 0, sizeof(got)); + rv = encrypt_block(session, aesKey, got); + CHECK_RV(rv, "encrypt with rebound AES key", CKR_OK); + CHECK_TRUE(XMEMCMP(got, expect, sizeof(got)) == 0, + "rebound AES key gives the same ciphertext"); + } +#if !defined(WOLFPKCS11_CUSTOM_STORE) && !defined(WOLFPKCS11_TPM_STORE) + CHECK_TRUE(WP11_Test_StoreName(WOLFPKCS11_STORE_TOKEN) > 0, + "token store file name"); + CHECK_TRUE(WP11_Test_StoreName(0x7f) == -1, + "no store file name for an unknown type"); +#endif + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + + pkcs11_unload(); +} + +#endif + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#if !defined(DEBUG_WOLFPKCS11) || defined(WOLFPKCS11_NO_STORE) || \ + defined(NO_AES) || !defined(HAVE_AES_CBC) + printf("Debug hooks or stored AES keys not available, skipping\n"); + return 77; /* automake: test skipped */ +#else +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif +#ifdef ALLOC_HOOKS + if (wolfSSL_SetAllocators(hook_malloc, hook_free, hook_realloc) != 0) { + printf("FAIL: wolfSSL_SetAllocators\n"); + return 1; + } +#endif + + printf("=== wolfPKCS11 legacy class load test ===\n"); + run_test(); + return pkcs11_test_summary(); +#endif +} diff --git a/tests/pkcs11_compliance_test.c b/tests/pkcs11_compliance_test.c index 5c1d41ce..57cc80cb 100644 --- a/tests/pkcs11_compliance_test.c +++ b/tests/pkcs11_compliance_test.c @@ -1279,7 +1279,6 @@ static void test_2370_private_default(void) CK_BYTE userPin[] = "wolfpkcs11-test"; CK_UTF8CHAR label[32]; CK_OBJECT_CLASS secretKeyClass = CKO_SECRET_KEY; - CK_OBJECT_CLASS pubKeyClass = CKO_PUBLIC_KEY; CK_KEY_TYPE aesKeyType = CKK_AES; CK_ULONG aesKeyLen = 16; CK_MECHANISM aesMech; @@ -1292,15 +1291,17 @@ static void test_2370_private_default(void) CK_ATTRIBUTE getPrivTmpl[] = { { CKA_PRIVATE, &gotPrivate, sizeof(gotPrivate) }, }; +#ifndef NO_RSA /* CKO_PUBLIC_KEY default is CKA_PRIVATE=CK_FALSE per spec. */ - CK_KEY_TYPE genericKeyType = CKK_GENERIC_SECRET; - CK_BBOOL ckTrue = CK_TRUE; + CK_OBJECT_CLASS pubKeyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE rsaKeyType = CKK_RSA; CK_ATTRIBUTE pubTmpl[] = { - { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, - { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, - { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, - { CKA_VALUE, aes_128_key, sizeof(aes_128_key) }, + { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, + { CKA_KEY_TYPE, &rsaKeyType, sizeof(rsaKeyType) }, + { CKA_MODULUS, rsa_2048_modulus, sizeof(rsa_2048_modulus) }, + { CKA_PUBLIC_EXPONENT, rsa_2048_pub_exp, sizeof(rsa_2048_pub_exp) }, }; +#endif (void)pubKey; (void)privKey; @@ -1374,6 +1375,7 @@ static void test_2370_private_default(void) } } +#ifndef NO_RSA /* Public-key default must remain CK_FALSE so the public arm of a key * pair stays accessible from public sessions. Use C_CreateObject so we * don't need a key-pair-generation mechanism enabled. */ @@ -1391,6 +1393,7 @@ static void test_2370_private_default(void) "public-key default CKA_PRIVATE is CK_FALSE"); } } +#endif out: if (slotList != NULL) diff --git a/tests/pkcs11mtt.c b/tests/pkcs11mtt.c index 78c60c95..d9572747 100644 --- a/tests/pkcs11mtt.c +++ b/tests/pkcs11mtt.c @@ -262,7 +262,7 @@ static CK_RV test_object(void* args) { CKA_KEY_TYPE, &badKeyType, sizeof(badKeyType) } }; CK_ATTRIBUTE keyDataNull[] = { - { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_VALUE, NULL, sizeof(keyData) }, }; @@ -514,7 +514,7 @@ static CK_RV test_attribute(void* args) CK_BBOOL badBool = 2; CK_DATE date = { { 0, }, { 0, }, { 0, } }; CK_ATTRIBUTE tmpl[] = { - { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, { CKA_SENSITIVE, &ckFalse, sizeof(ckFalse) }, @@ -686,7 +686,7 @@ static CK_RV test_attribute_types(void* args) CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; static byte keyData[] = { 0x00 }; CK_ATTRIBUTE tmpl[] = { - { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_VALUE, keyData, sizeof(keyData) }, }; @@ -918,7 +918,7 @@ static CK_RV test_find_objects(void* args) static byte keyData[] = { 0x00 }; static byte id[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; CK_ATTRIBUTE tmpl[] = { - { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_VALUE, keyData, sizeof(keyData) }, { CKA_ID, id, sizeof(id) }, diff --git a/tests/pkcs11test.c b/tests/pkcs11test.c index df606840..c1e2d532 100644 --- a/tests/pkcs11test.c +++ b/tests/pkcs11test.c @@ -2049,7 +2049,7 @@ static CK_RV test_object(void* args) { CKA_KEY_TYPE, &badKeyType, sizeof(badKeyType) } }; CK_ATTRIBUTE keyDataNull[] = { - { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_VALUE, NULL, sizeof(keyData) }, }; @@ -3236,7 +3236,7 @@ static CK_RV test_cross_session_object(void* args) CK_ULONG size; static byte keyData[] = { 0x00 }; CK_ATTRIBUTE tmpl[] = { - { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_VALUE, keyData, sizeof(keyData) }, { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, @@ -3282,7 +3282,7 @@ static CK_RV test_attribute(void* args) CK_BBOOL badBool = 2; CK_DATE date = { { 0, }, { 0, }, { 0, } }; CK_ATTRIBUTE tmpl[] = { - { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_SENSITIVE, &ckFalse, sizeof(ckFalse) }, { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, @@ -3454,7 +3454,7 @@ static CK_RV test_attribute_types(void* args) CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; static byte keyData[] = { 0x00 }; CK_ATTRIBUTE tmpl[] = { - { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_VALUE, keyData, sizeof(keyData) }, { CKA_SUBJECT, NULL, 0 }, @@ -3650,25 +3650,27 @@ static CK_RV test_attribute_get(void* args) int i = 0; CK_BBOOL falseBool = 0; CK_ATTRIBUTE createTmpl[] = { - { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, + { CKA_SENSITIVE, &ckFalse, sizeof(ckFalse) }, { CKA_VALUE, keyData, sizeof(keyData) }, { CKA_ID, id, sizeof(id) }, { CKA_PRIVATE, &falseBool, sizeof(falseBool) } }; CK_ULONG tmplCnt = sizeof(createTmpl) / sizeof(*createTmpl); CK_ATTRIBUTE createTmpl2[] = { - { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_EXTRACTABLE, &ckFalse, sizeof(ckFalse) }, + { CKA_SENSITIVE, &ckFalse, sizeof(ckFalse) }, { CKA_VALUE, keyData, sizeof(keyData) }, { CKA_ID, id, sizeof(id) }, { CKA_PRIVATE, &ckTrue, sizeof(ckTrue) } }; CK_ULONG tmplCnt2 = sizeof(createTmpl2) / sizeof(*createTmpl2); CK_ATTRIBUTE createTmpl3[] = { - { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_SENSITIVE, &ckTrue, sizeof(ckTrue) }, { CKA_VALUE, keyData, sizeof(keyData) }, @@ -3716,8 +3718,8 @@ static CK_RV test_attribute_get(void* args) } if (ret == CKR_OK) { - if (getTmpl[0].ulValueLen != sizeof(pubKeyClass) || - (*((CK_OBJECT_CLASS *)(getTmpl[0].pValue))) != pubKeyClass) { + if (getTmpl[0].ulValueLen != sizeof(secretKeyClass) || + (*((CK_OBJECT_CLASS *)(getTmpl[0].pValue))) != secretKeyClass) { ret = -1; CHECK_CKR(ret, "CKA_CLASS value match creation params"); } @@ -3803,7 +3805,7 @@ static CK_RV test_extractable_set_false_to_true(void* args) CK_BBOOL val; /* Object created with CKA_EXTRACTABLE = FALSE */ CK_ATTRIBUTE createTmplFalse[] = { - { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_EXTRACTABLE, &ckFalse, sizeof(ckFalse) }, { CKA_VALUE, keyData, sizeof(keyData) }, @@ -3813,7 +3815,7 @@ static CK_RV test_extractable_set_false_to_true(void* args) sizeof(createTmplFalse) / sizeof(*createTmplFalse); /* Object created with CKA_EXTRACTABLE = TRUE */ CK_ATTRIBUTE createTmplTrue[] = { - { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, { CKA_VALUE, keyData, sizeof(keyData) }, @@ -4635,7 +4637,7 @@ static CK_RV test_find_objects(void* args) CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; static byte keyData[] = { 0x00 }; CK_ATTRIBUTE tmpl[] = { - { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_VALUE, keyData, sizeof(keyData) }, }; @@ -15840,6 +15842,159 @@ static CK_RV test_hkdf_derive_expand_null_value_len(void* args) return ret; } + +/* A derived key is a secret key with secret key defaults when the template + * omits CKA_CLASS, and a template naming another class is rejected. */ +static CK_RV test_hkdf_derive_implied_class(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_OBJECT_HANDLE hBaseKey = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; + CK_BYTE ikm[32]; + CK_ULONG keyLen = 32; + CK_OBJECT_CLASS keyClass = CKO_DATA; + CK_BBOOL encrypt = CK_FALSE; + CK_ATTRIBUTE baseTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_VALUE, ikm, sizeof(ikm)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)} + }; + CK_HKDF_PARAMS params = { + CK_TRUE, CK_FALSE, CKM_SHA256_HMAC, + CKF_HKDF_SALT_NULL, NULL_PTR, 0, + CK_INVALID_HANDLE, NULL_PTR, 0 + }; + CK_MECHANISM mech = { CKM_HKDF_DERIVE, ¶ms, sizeof(params) }; + CK_ATTRIBUTE noClassTmpl[] = { + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_VALUE_LEN, &keyLen, sizeof(keyLen)} + }; + CK_ATTRIBUTE dataClassTmpl[] = { + {CKA_CLASS, &dataClass, sizeof(dataClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_VALUE_LEN, &keyLen, sizeof(keyLen)} + }; + CK_ATTRIBUTE getTmpl[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_ENCRYPT, &encrypt, sizeof(encrypt)} + }; + CK_ATTRIBUTE nullClassTmpl[] = { + {CKA_CLASS, NULL_PTR, sizeof(CK_OBJECT_CLASS)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_VALUE_LEN, &keyLen, sizeof(keyLen)} + }; + CK_ATTRIBUTE noTypeTmpl[] = { + {CKA_VALUE_LEN, &keyLen, sizeof(keyLen)} + }; + CK_ATTRIBUTE shortTypeTmpl[] = { + {CKA_KEY_TYPE, &genericKeyType, 1}, + {CKA_VALUE_LEN, &keyLen, sizeof(keyLen)} + }; + CK_OBJECT_HANDLE hDataBase = CK_INVALID_HANDLE; + CK_ATTRIBUTE dataBaseTmpl[] = { + {CKA_CLASS, &dataClass, sizeof(dataClass)}, + {CKA_VALUE, ikm, sizeof(ikm)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)} + }; + + XMEMSET(ikm, 0x0b, sizeof(ikm)); + + ret = funcList->C_CreateObject(session, baseTmpl, + sizeof(baseTmpl) / sizeof(*baseTmpl), &hBaseKey); + CHECK_CKR(ret, "Create HKDF base key"); + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hBaseKey, noClassTmpl, + sizeof(noClassTmpl) / sizeof(*noClassTmpl), &hKey); + CHECK_CKR(ret, "HKDF derive without CKA_CLASS"); + } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, hKey, getTmpl, + sizeof(getTmpl) / sizeof(*getTmpl)); + CHECK_CKR(ret, "Get derived key class"); + } + if (ret == CKR_OK && keyClass != CKO_SECRET_KEY) { + ret = -1; + CHECK_CKR(ret, "Derived key class is CKO_SECRET_KEY"); + } + if (ret == CKR_OK && encrypt != CK_TRUE) { + ret = -1; + CHECK_CKR(ret, "Derived key has secret key defaults"); + } + if (hKey != CK_INVALID_HANDLE) { + funcList->C_DestroyObject(session, hKey); + hKey = CK_INVALID_HANDLE; + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hBaseKey, dataClassTmpl, + sizeof(dataClassTmpl) / sizeof(*dataClassTmpl), &hKey); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCONSISTENT, + "HKDF derive with a non-secret class"); + } + + mech.mechanism = CKM_HKDF_DATA; + if (ret == CKR_OK) { + keyClass = CKO_DATA; + ret = funcList->C_DeriveKey(session, &mech, hBaseKey, noClassTmpl, + sizeof(noClassTmpl) / sizeof(*noClassTmpl), &hKey); + CHECK_CKR(ret, "HKDF data derive without CKA_CLASS"); + } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, hKey, getTmpl, 1); + CHECK_CKR(ret, "Get HKDF data derived key class"); + } + if (ret == CKR_OK) { + CHECK_COND(keyClass == CKO_SECRET_KEY, ret, + "HKDF data derived key class is CKO_SECRET_KEY"); + } + if (hKey != CK_INVALID_HANDLE) { + funcList->C_DestroyObject(session, hKey); + hKey = CK_INVALID_HANDLE; + } + + /* A malformed class or key type is refused before deriving. */ + mech.mechanism = CKM_HKDF_DERIVE; + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hBaseKey, nullClassTmpl, + sizeof(nullClassTmpl) / sizeof(*nullClassTmpl), &hKey); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, + "HKDF derive with an empty CKA_CLASS"); + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hBaseKey, noTypeTmpl, + sizeof(noTypeTmpl) / sizeof(*noTypeTmpl), &hKey); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCOMPLETE, + "HKDF derive without CKA_KEY_TYPE"); + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hBaseKey, shortTypeTmpl, + sizeof(shortTypeTmpl) / sizeof(*shortTypeTmpl), &hKey); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, + "HKDF derive with a short CKA_KEY_TYPE"); + } + + /* A data object may seed a derivation. */ + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, dataBaseTmpl, + sizeof(dataBaseTmpl) / sizeof(*dataBaseTmpl), &hDataBase); + CHECK_CKR(ret, "Create HKDF data object base"); + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, hDataBase, noClassTmpl, + sizeof(noClassTmpl) / sizeof(*noClassTmpl), &hKey); + CHECK_CKR(ret, "HKDF derive from a data object"); + } + + if (hKey != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hKey); + if (hDataBase != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hDataBase); + if (hBaseKey != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, hBaseKey); + + return ret; +} #endif #ifndef NO_SHA @@ -16366,6 +16521,124 @@ static CK_RV test_derive_tls12_key_and_mac(void *args) { return ret; } +/* TLS key material is always secret keys, whether or not the template names + * the class, and a template naming another class is refused. */ +static CK_RV test_derive_tls12_key_and_mac_class(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_OBJECT_HANDLE premaster = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hDerivedKey = CK_INVALID_HANDLE; + CK_BYTE secret[48]; + CK_BYTE clientRandom[32]; + CK_BYTE serverRandom[32]; + CK_BYTE clientIV[16]; + CK_BYTE serverIV[16]; + CK_OBJECT_CLASS macClass = CKO_DATA; + CK_OBJECT_CLASS keyClass = CKO_DATA; + CK_ATTRIBUTE premasterTmpl[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_VALUE, secret, sizeof(secret)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)} + }; + CK_SSL3_KEY_MAT_OUT keyMat = { + CK_INVALID_HANDLE, CK_INVALID_HANDLE, + CK_INVALID_HANDLE, CK_INVALID_HANDLE, + clientIV, serverIV + }; + CK_TLS12_KEY_MAT_PARAMS params = { + 32 * 8, 16 * 8, 16 * 8, CK_FALSE, + { clientRandom, sizeof(clientRandom), + serverRandom, sizeof(serverRandom) }, + &keyMat, CKM_SHA256 + }; + CK_MECHANISM mech = { + CKM_TLS12_KEY_AND_MAC_DERIVE, ¶ms, sizeof(params) + }; + CK_ATTRIBUTE noClassTmpl[] = { + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_SENSITIVE, &ckFalse, sizeof(ckFalse)}, + }; + CK_ATTRIBUTE dataClassTmpl[] = { + {CKA_CLASS, &dataClass, sizeof(dataClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + }; + CK_BBOOL badBool = 2; + CK_ATTRIBUTE badBoolTmpl[] = { + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_SENSITIVE, &badBool, sizeof(badBool)}, + }; + CK_ATTRIBUTE macGetTmpl[] = { + {CKA_CLASS, &macClass, sizeof(macClass)}, + }; + CK_ATTRIBUTE keyGetTmpl[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + }; + + XMEMSET(secret, 0x11, sizeof(secret)); + XMEMSET(clientRandom, 0x22, sizeof(clientRandom)); + XMEMSET(serverRandom, 0x33, sizeof(serverRandom)); + + ret = funcList->C_CreateObject(session, premasterTmpl, + sizeof(premasterTmpl) / sizeof(*premasterTmpl), &premaster); + CHECK_CKR(ret, "Create TLS premaster"); + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, premaster, noClassTmpl, + sizeof(noClassTmpl) / sizeof(*noClassTmpl), &hDerivedKey); + CHECK_CKR(ret, "TLS key and MAC derive without CKA_CLASS"); + } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, keyMat.hClientMacSecret, + macGetTmpl, 1); + CHECK_CKR(ret, "Get MAC key class"); + } + if (ret == CKR_OK) { + ret = funcList->C_GetAttributeValue(session, keyMat.hClientKey, + keyGetTmpl, 1); + CHECK_CKR(ret, "Get cipher key class"); + } + if (ret == CKR_OK) { + CHECK_COND(macClass == CKO_SECRET_KEY && keyClass == CKO_SECRET_KEY, + ret, "TLS key material is secret keys"); + } + if (keyMat.hClientMacSecret != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, keyMat.hClientMacSecret); + if (keyMat.hServerMacSecret != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, keyMat.hServerMacSecret); + if (keyMat.hClientKey != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, keyMat.hClientKey); + if (keyMat.hServerKey != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, keyMat.hServerKey); + keyMat.hClientMacSecret = CK_INVALID_HANDLE; + keyMat.hServerMacSecret = CK_INVALID_HANDLE; + keyMat.hClientKey = CK_INVALID_HANDLE; + keyMat.hServerKey = CK_INVALID_HANDLE; + + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, premaster, dataClassTmpl, + sizeof(dataClassTmpl) / sizeof(*dataClassTmpl), &hDerivedKey); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCONSISTENT, + "TLS key and MAC derive to a data object"); + } + if (ret == CKR_OK) { + CHECK_COND(keyMat.hClientMacSecret == CK_INVALID_HANDLE && + keyMat.hClientKey == CK_INVALID_HANDLE, ret, + "No TLS key material for a refused template"); + } + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mech, premaster, badBoolTmpl, + sizeof(badBoolTmpl) / sizeof(*badBoolTmpl), &hDerivedKey); + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, + "TLS key and MAC derive with a bad attribute value"); + } + + if (premaster != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, premaster); + + return ret; +} + static CK_RV test_derive_tls12_master_key_dh(void* args) { CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; CK_RV ret; @@ -17626,6 +17899,156 @@ static CK_RV test_get_attr_value_key_type_on_non_key(void* args) return ret; } +/* A certificate is not a key, whatever usage attributes it carries. */ +static CK_RV test_crypto_init_certificate(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_OBJECT_HANDLE x509Cert = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE wtlsCert = CK_INVALID_HANDLE; + CK_CERTIFICATE_TYPE x509 = CKC_X_509; + CK_CERTIFICATE_TYPE wtls = CKC_WTLS; + CK_BYTE certData[] = { 0x30, 0x82, 0x01, 0x00 }; + CK_ATTRIBUTE x509Tmpl[] = { + { CKA_CLASS, &certificateClass, sizeof(certificateClass) }, + { CKA_CERTIFICATE_TYPE, &x509, sizeof(x509) }, + { CKA_VALUE, certData, sizeof(certData) }, + { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, + { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, + { CKA_WRAP, &ckTrue, sizeof(ckTrue) }, + { CKA_UNWRAP, &ckTrue, sizeof(ckTrue) }, + }; + CK_ATTRIBUTE wtlsTmpl[] = { + { CKA_CLASS, &certificateClass, sizeof(certificateClass) }, + { CKA_CERTIFICATE_TYPE, &wtls, sizeof(wtls) }, + { CKA_VALUE, certData, sizeof(certData) }, + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + }; + CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 }; + CK_OBJECT_HANDLE toWrap = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE unwrapped = CK_INVALID_HANDLE; + CK_BYTE wrapped[64]; + CK_ULONG wrappedLen = sizeof(wrapped); + CK_ATTRIBUTE toWrapTmpl[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, + { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, + { CKA_VALUE, certData, sizeof(certData) }, + { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, + }; + CK_ATTRIBUTE unwrapTmpl[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, + { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, + }; +#ifndef NO_DH + CK_OBJECT_HANDLE secret = CK_INVALID_HANDLE; + CK_ULONG secLen = 32; + CK_ATTRIBUTE secTmpl[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, + { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, + { CKA_VALUE_LEN, &secLen, sizeof(secLen) }, + }; +#endif + + ret = funcList->C_CreateObject(session, x509Tmpl, + sizeof(x509Tmpl) / sizeof(*x509Tmpl), &x509Cert); + CHECK_CKR(ret, "Create X.509 certificate with usage attributes"); + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, wtlsTmpl, + sizeof(wtlsTmpl) / sizeof(*wtlsTmpl), &wtlsCert); + CHECK_CKR(ret, "Create WTLS certificate with usage attributes"); + } + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, toWrapTmpl, + sizeof(toWrapTmpl) / sizeof(*toWrapTmpl), &toWrap); + CHECK_CKR(ret, "Create key to wrap"); + } + if (ret == CKR_OK) { + ret = funcList->C_WrapKey(session, &mech, x509Cert, toWrap, wrapped, + &wrappedLen); + CHECK_CKR_FAIL(ret, CKR_WRAPPING_KEY_HANDLE_INVALID, + "Wrap with a certificate"); + } + if (ret == CKR_OK) { + XMEMSET(wrapped, 0, sizeof(wrapped)); + ret = funcList->C_UnwrapKey(session, &mech, x509Cert, wrapped, + sizeof(wrapped), unwrapTmpl, + sizeof(unwrapTmpl) / sizeof(*unwrapTmpl), &unwrapped); + CHECK_CKR_FAIL(ret, CKR_UNWRAPPING_KEY_HANDLE_INVALID, + "Unwrap with a certificate"); + if (unwrapped != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, unwrapped); + } +#ifndef NO_RSA + if (ret == CKR_OK) { + ret = funcList->C_EncryptInit(session, &mech, x509Cert); + CHECK_CKR_FAIL(ret, CKR_KEY_HANDLE_INVALID, + "Encrypt init with a certificate"); + } + if (ret == CKR_OK) { + ret = funcList->C_VerifyInit(session, &mech, x509Cert); + CHECK_CKR_FAIL(ret, CKR_KEY_HANDLE_INVALID, + "Verify init with a certificate"); + } +#endif +#ifndef NO_DH + if (ret == CKR_OK) { + mech.mechanism = CKM_DH_PKCS_DERIVE; + mech.pParameter = dh_2048_peer; + mech.ulParameterLen = sizeof(dh_2048_peer); + ret = funcList->C_DeriveKey(session, &mech, wtlsCert, secTmpl, + sizeof(secTmpl) / sizeof(*secTmpl), &secret); + CHECK_CKR_FAIL(ret, CKR_KEY_HANDLE_INVALID, + "Derive with a certificate"); + if (secret != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, secret); + } +#endif + + if (toWrap != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, toWrap); + if (wtlsCert != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, wtlsCert); + if (x509Cert != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, x509Cert); + + return ret; +} + +/* Secret key types only make secret keys, and secret keys only take secret + * key types. */ +static CK_RV test_create_key_class_type_match(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret = CKR_OK; + CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; + CK_BYTE value[16]; + CK_ATTRIBUTE pubSecretTmpl[] = { + { CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass) }, + { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, + { CKA_VALUE, value, sizeof(value) }, + }; + CK_ATTRIBUTE privSecretTmpl[] = { + { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, + { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, + { CKA_VALUE, value, sizeof(value) }, + }; + + XMEMSET(value, 0x5a, sizeof(value)); + + ret = funcList->C_CreateObject(session, pubSecretTmpl, + sizeof(pubSecretTmpl) / sizeof(*pubSecretTmpl), &obj); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCONSISTENT, + "Create public key of a secret key type"); + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, privSecretTmpl, + sizeof(privSecretTmpl) / sizeof(*privSecretTmpl), &obj); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCONSISTENT, + "Create private key of a secret key type"); + } + + return ret; +} + #ifndef WOLFPKCS11_NSS /* Creating, copying, destroying, and setting attributes on session objects * should be allowed in read-only sessions per the PKCS#11 spec. Only token @@ -18577,6 +19000,8 @@ static TEST_FUNC testFunc[] = { PKCS11TEST_FUNC_SESS_DECL(test_get_attr_value_all_processed), PKCS11TEST_FUNC_SESS_DECL(test_get_attr_value_cert_type_on_key), PKCS11TEST_FUNC_SESS_DECL(test_get_attr_value_key_type_on_non_key), + PKCS11TEST_FUNC_SESS_DECL(test_crypto_init_certificate), + PKCS11TEST_FUNC_SESS_DECL(test_create_key_class_type_match), PKCS11TEST_FUNC_SESS_DECL(test_find_objects), PKCS11TEST_FUNC_SESS_DECL(test_find_objects_many), PKCS11TEST_FUNC_SESS_DECL(test_private_object_access), @@ -18810,6 +19235,7 @@ static TEST_FUNC testFunc[] = { PKCS11TEST_FUNC_SESS_DECL(test_hkdf_derive_extract_with_expand_salt_key), 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 #ifdef WOLFSSL_HAVE_PRF #ifndef NO_MD5 @@ -18834,6 +19260,7 @@ static TEST_FUNC testFunc[] = { #ifdef WOLFSSL_HAVE_PRF PKCS11TEST_FUNC_SESS_DECL(test_derive_tls12_master_key_dh), PKCS11TEST_FUNC_SESS_DECL(test_derive_tls12_key_and_mac), + PKCS11TEST_FUNC_SESS_DECL(test_derive_tls12_key_and_mac_class), PKCS11TEST_FUNC_SESS_DECL(test_derive_tls12_master_key), #ifdef WOLFPKCS11_NSS PKCS11TEST_FUNC_SESS_DECL(test_nss_trust_object), diff --git a/tests/pkcs11v3test.c b/tests/pkcs11v3test.c index a0bd054e..3b3857a6 100644 --- a/tests/pkcs11v3test.c +++ b/tests/pkcs11v3test.c @@ -1965,6 +1965,111 @@ static CK_RV test_mlkem_bad_mech_params(void* args) return ret; } +/* The shared secret object must be a secret key of a secret key type. */ +static CK_RV test_mlkem_secret_template_type(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret = CKR_OK; + CK_FUNCTION_LIST_3_2* funcListExt = (CK_FUNCTION_LIST_3_2*)funcList; + CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE secret = CK_INVALID_HANDLE; + CK_MECHANISM mech = { CKM_ML_KEM, NULL, 0 }; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_OBJECT_CLASS dataClass = CKO_DATA; + CK_KEY_TYPE genericKeyType = CKK_GENERIC_SECRET; + CK_ATTRIBUTE goodTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, + }; + CK_ATTRIBUTE wrongTypeTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &mlkemKeyType, sizeof(mlkemKeyType) }, + }; + CK_ATTRIBUTE noClassTmpl[] = { + { CKA_KEY_TYPE, &mlkemKeyType, sizeof(mlkemKeyType) }, + }; + CK_ATTRIBUTE dataTmpl[] = { + { CKA_CLASS, &dataClass, sizeof(dataClass) }, + }; + CK_BYTE* ciphertext = NULL; + CK_ULONG ctLen = 0; + + ret = gen_mlkem_keys(session, CKP_ML_KEM_512, &pub, &priv, NULL, 0, + NULL, 0, 0); + if (ret == CKR_OK) { + ret = funcListExt->C_EncapsulateKey(session, &mech, pub, dataTmpl, + 1, NULL, &ctLen, &secret); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCONSISTENT, + "ML-KEM Encapsulate size query to a data object"); + } + if (ret == CKR_OK) { + ret = funcListExt->C_EncapsulateKey(session, &mech, pub, goodTmpl, + 2, NULL, &ctLen, &secret); + CHECK_CKR(ret, "ML-KEM Encapsulate size query"); + } + if (ret == CKR_OK) { + ciphertext = (CK_BYTE*)malloc(ctLen); + if (ciphertext == NULL) + ret = CKR_HOST_MEMORY; + } + if (ret == CKR_OK) { + XMEMSET(ciphertext, 0xa5, ctLen); + ret = funcListExt->C_EncapsulateKey(session, &mech, pub, wrongTypeTmpl, + 2, ciphertext, &ctLen, &secret); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCONSISTENT, + "ML-KEM Encapsulate to a non-secret key type"); + } + if (ret == CKR_OK) { + CHECK_COND(ciphertext[0] == 0xa5 && ciphertext[ctLen - 1] == 0xa5, + ret, "No ciphertext for a refused template"); + } + if (ret == CKR_OK) { + ret = funcListExt->C_EncapsulateKey(session, &mech, pub, noClassTmpl, + 1, ciphertext, &ctLen, &secret); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCONSISTENT, + "ML-KEM Encapsulate to a non-secret type, no class"); + } + if (ret == CKR_OK) { + ret = funcListExt->C_EncapsulateKey(session, &mech, pub, dataTmpl, + 1, ciphertext, &ctLen, &secret); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCONSISTENT, + "ML-KEM Encapsulate to a data object"); + } + if (ret == CKR_OK) { + ret = funcListExt->C_EncapsulateKey(session, &mech, pub, goodTmpl, + 2, ciphertext, &ctLen, &secret); + CHECK_CKR(ret, "ML-KEM Encapsulate to a generic secret"); + } + if (secret != CK_INVALID_HANDLE) { + funcList->C_DestroyObject(session, secret); + secret = CK_INVALID_HANDLE; + } + if (ret == CKR_OK) { + ret = funcListExt->C_DecapsulateKey(session, &mech, priv, wrongTypeTmpl, + 2, ciphertext, ctLen, &secret); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCONSISTENT, + "ML-KEM Decapsulate to a non-secret key type"); + } + if (ret == CKR_OK) { + ret = funcListExt->C_DecapsulateKey(session, &mech, priv, dataTmpl, + 1, ciphertext, ctLen, &secret); + CHECK_CKR_FAIL(ret, CKR_TEMPLATE_INCONSISTENT, + "ML-KEM Decapsulate to a data object"); + } + + if (ciphertext != NULL) + free(ciphertext); + if (secret != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, secret); + if (priv != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, priv); + if (pub != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, pub); + + return ret; +} + static CK_RV test_mlkem_key_validation(void* args) { CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; @@ -3930,6 +4035,7 @@ static TEST_FUNC testFunc[] = { PKCS11TEST_FUNC_SESS_DECL(test_mlkem_seed_invalid), PKCS11TEST_FUNC_SESS_DECL(test_mlkem_encap_decap_fail), PKCS11TEST_FUNC_SESS_DECL(test_mlkem_bad_mech_params), + PKCS11TEST_FUNC_SESS_DECL(test_mlkem_secret_template_type), PKCS11TEST_FUNC_SESS_DECL(test_mlkem_key_validation), PKCS11TEST_FUNC_SESS_DECL(test_mlkem_initial_states), PKCS11TEST_FUNC_SESS_DECL(test_mlkem_encap_decap_not_permitted), diff --git a/tests/token_path_test.c b/tests/token_path_test.c index e2545f4c..63b8e1ea 100644 --- a/tests/token_path_test.c +++ b/tests/token_path_test.c @@ -440,6 +440,85 @@ static int test_missing_storage_path(void) return 0; } +#if defined(HAVE_ECC) && !defined(WOLFPKCS11_NSS) && \ + !defined(WOLFPKCS11_NO_ENV) +static void fill_token_dir(char* dir, size_t sz) +{ + size_t len = (size_t)snprintf(dir, sz, "./store/long_path_"); + + memset(dir + len, 'x', sz - 1 - len); + dir[sz - 1] = '\0'; +} + +/* With 120 byte store names, a token directory that leaves room for every + * store file name works fully and one byte longer is refused up front. */ +static int test_long_token_path(void) +{ + CK_RV ret; + int result = 0; + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE ecType = CKK_EC; + CK_BBOOL ckTrue = CK_TRUE; + CK_ATTRIBUTE privTmpl[] = { + { CKA_CLASS, &privClass, sizeof(privClass) }, + { CKA_KEY_TYPE, &ecType, sizeof(ecType) }, + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_EC_PARAMS, ecc_p256_params, sizeof(ecc_p256_params) }, + { CKA_VALUE, ecc_p256_priv, sizeof(ecc_p256_priv) }, + }; + char fitDir[67]; + char longDir[68]; + + fill_token_dir(fitDir, sizeof(fitDir)); + fill_token_dir(longDir, sizeof(longDir)); + MKDIR(fitDir); + MKDIR(longDir); + cleanup_test_files(fitDir); + cleanup_test_files(longDir); + + setenv("WOLFPKCS11_TOKEN_PATH", fitDir, 1); + ret = create_test_token("Testing the longest token path", 1); + if (ret == CKR_OK) { + ret = funcList->C_OpenSession(slot, + CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session); + } + if (ret == CKR_OK) + ret = funcList->C_Login(session, CKU_USER, userPin, userPinLen); + if (ret == CKR_OK) { + ret = funcList->C_CreateObject(session, privTmpl, + sizeof(privTmpl) / sizeof(*privTmpl), &key); + } + if (ret == CKR_OK) + ret = funcList->C_DestroyObject(session, key); + if (session != CK_INVALID_HANDLE) { + funcList->C_Logout(session); + funcList->C_CloseSession(session); + } + if (ret == CKR_OK) { + printf("PASS: Token key stored under the longest token path\n"); + test_passed++; + } + else { + printf("FAIL: Token key under the longest token path: 0x%lx\n", + (unsigned long)ret); + test_failed++; + result = -1; + } + cleanup_test_files(fitDir); + + setenv("WOLFPKCS11_TOKEN_PATH", longDir, 1); + ret = create_test_token("Testing a token path one byte too long", 0); + if (ret == CKR_OK) + result = -1; + cleanup_test_files(longDir); + + unsetenv("WOLFPKCS11_TOKEN_PATH"); + return result; +} +#endif + static void print_results(void) { printf("\n=== Test Results ===\n"); @@ -549,6 +628,13 @@ int main(int argc, char* argv[]) result = 1; } +#if defined(HAVE_ECC) && !defined(WOLFPKCS11_NSS) && \ + !defined(WOLFPKCS11_NO_ENV) + if (test_long_token_path() != 0) { + result = 1; + } +#endif + /* Clean up */ #ifndef WOLFPKCS11_NO_ENV setenv("WOLFPKCS11_TOKEN_PATH", INIT_FINAL_TEST_DIR, 1); diff --git a/wolfpkcs11/internal.h b/wolfpkcs11/internal.h index 00780b4c..ca3e6c2f 100644 --- a/wolfpkcs11/internal.h +++ b/wolfpkcs11/internal.h @@ -538,6 +538,13 @@ WP11_API int WP11_Test_RemoveObjectStale(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE liveHandle, CK_OBJECT_HANDLE staleHandle); WP11_API int WP11_Test_SetNextObjId(CK_SLOT_ID slotId, int onToken, int nextObjId); +#ifndef WOLFPKCS11_NO_STORE +WP11_API int WP11_Test_SetObjectClass(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hObject, CK_OBJECT_CLASS objClass); +#if !defined(WOLFPKCS11_CUSTOM_STORE) && !defined(WOLFPKCS11_TPM_STORE) +WP11_API int WP11_Test_StoreName(int type); +#endif +#endif #endif WP11_LOCAL int WP11_Object_HandleOnToken(CK_OBJECT_HANDLE handle); WP11_LOCAL void WP11_Slot_ClearActiveObject(WP11_Slot* slot, @@ -563,6 +570,7 @@ WP11_LOCAL int WP11_Object_Copy(WP11_Object *src, WP11_Object *dest); WP11_LOCAL CK_OBJECT_HANDLE WP11_Object_GetHandle(WP11_Object* object); 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 CK_ULONG WP11_Object_GetDevId(WP11_Object* object);