diff --git a/src/crypto.c b/src/crypto.c index db1cd504..e03d5df5 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -480,7 +480,9 @@ static int CheckOpSupportedRv(WP11_Object* obj, CK_ATTRIBUTE_TYPE op, static int CheckOpSupported(WP11_Object* obj, CK_ATTRIBUTE_TYPE op) { - return CheckOpSupportedRv(obj, op, CKR_KEY_TYPE_INCONSISTENT); + /* Usage-attribute denial is CKR_KEY_FUNCTION_NOT_PERMITTED; a key-type + * mismatch is handled separately by the per-mechanism checks. */ + return CheckOpSupportedRv(obj, op, CKR_KEY_FUNCTION_NOT_PERMITTED); } static CK_RV SetInitialStates(WP11_Object* key) @@ -760,6 +762,8 @@ static CK_RV SetAttributeValue(WP11_Session* session, WP11_Object* obj, CK_OBJECT_CLASS objClass; CK_BBOOL getVar; CK_ULONG getVarLen = 1; + byte roCur[sizeof(CK_ULONG)]; + CK_ULONG roCurLen; if (pTemplate == NULL) return CKR_ARGUMENTS_BAD; @@ -965,6 +969,22 @@ static CK_RV SetAttributeValue(WP11_Session* session, WP11_Object* obj, *(CK_BBOOL*)attr->pValue == CK_TRUE) return CKR_ATTRIBUTE_READ_ONLY; } + /* These class/identity and generated-state attributes are read-only + * once the object exists; reject a change. Setting the current value + * is a no-op. */ + if (!newObject && (attr->type == CKA_CLASS || + attr->type == CKA_KEY_TYPE || + attr->type == CKA_LOCAL || + attr->type == CKA_KEY_GEN_MECHANISM || + attr->type == CKA_ALWAYS_SENSITIVE || + attr->type == CKA_NEVER_EXTRACTABLE)) { + roCurLen = sizeof(roCur); + if (WP11_Object_GetAttr(obj, attr->type, roCur, &roCurLen) == 0 && + (attr->pValue == NULL || attr->ulValueLen != roCurLen || + XMEMCMP(attr->pValue, roCur, roCurLen) != 0)) { + return CKR_ATTRIBUTE_READ_ONLY; + } + } ret = WP11_Object_SetAttr(obj, attr->type, (byte*)attr->pValue, attr->ulValueLen); if (ret == MEMORY_E) @@ -1330,6 +1350,7 @@ CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_RV rv; WP11_Session* session; WP11_Object* object; + CK_ATTRIBUTE* classAttr = NULL; WOLFPKCS11_ENTER("C_CreateObject"); #ifdef DEBUG_WOLFPKCS11 @@ -1372,8 +1393,15 @@ CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, } } - /* C_CreateObject requires CKA_CLASS in the template; no API-implied - * class to fall back on. */ + /* C_CreateObject requires CKA_CLASS; there is no API-implied object class + * as there is for derive/unwrap. */ + FindAttributeType(pTemplate, ulCount, CKA_CLASS, &classAttr); + if (classAttr == NULL) { + rv = CKR_TEMPLATE_INCOMPLETE; + WOLFPKCS11_LEAVE("C_CreateObject", rv); + return rv; + } + rv = CheckPrivateLogin(session, pTemplate, ulCount, WP11_NO_IMPLICIT_CLASS); if (rv != CKR_OK) { @@ -4065,10 +4093,18 @@ CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, WP11_Session_SetOpInitialized(session, 0); return CKR_DATA_LEN_RANGE; } - *pulLastPartLen = 15; - if (pLastPart == NULL) + if (pLastPart == NULL) { + /* Size query: report the maximum possible unpadded size. */ + *pulLastPartLen = AES_BLOCK_SIZE - 1; return CKR_OK; + } + /* Pass the caller's buffer capacity so WP11_AesCbcPad_DecryptFinal + * can enforce it. Cap to the block size, which already exceeds the + * 0..15 byte unpadded output, to avoid truncating a large + * CK_ULONG. */ + decPartLen = (*pulLastPartLen < (CK_ULONG)AES_BLOCK_SIZE) ? + (word32)*pulLastPartLen : (word32)AES_BLOCK_SIZE; ret = WP11_AesCbcPad_DecryptFinal(pLastPart, &decPartLen, session); if (ret == BUFFER_E) { /* Report required size; operation stays active for retry. */ @@ -6632,11 +6668,14 @@ CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, } ret = WP11_Object_Find(session, hKey, &obj); - if (ret != CKR_OK) - return ret; + if (ret != 0) { + rv = CKR_OBJECT_HANDLE_INVALID; + WOLFPKCS11_LEAVE("C_VerifyRecoverInit", rv); + return rv; + } if (WP11_Object_GetClass(obj) != CKO_PUBLIC_KEY) { - return CKR_KEY_HANDLE_INVALID; + return CKR_KEY_TYPE_INCONSISTENT; } if (WP11_Object_GetType(obj) != CKK_RSA) { @@ -7480,6 +7519,7 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, WP11_Session* session = NULL; WP11_Object* pub = NULL; WP11_Object* priv = NULL; + CK_ATTRIBUTE* reqAttr = NULL; WOLFPKCS11_ENTER("C_GenerateKeyPair"); #ifdef DEBUG_WOLFPKCS11 @@ -7556,6 +7596,12 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, return CKR_MECHANISM_PARAM_INVALID; } + /* CKA_MODULUS_BITS is required to size the RSA key. */ + FindAttributeType(pPublicKeyTemplate, ulPublicKeyAttributeCount, + CKA_MODULUS_BITS, &reqAttr); + if (reqAttr == NULL) + return CKR_TEMPLATE_INCOMPLETE; + *phPublicKey = *phPrivateKey = CK_INVALID_HANDLE; rv = NewObject(session, CKK_RSA, CKO_PUBLIC_KEY, pPublicKeyTemplate, @@ -7580,6 +7626,12 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, return CKR_MECHANISM_PARAM_INVALID; } + /* CKA_EC_PARAMS carries the curve domain parameters. */ + FindAttributeType(pPublicKeyTemplate, ulPublicKeyAttributeCount, + CKA_EC_PARAMS, &reqAttr); + if (reqAttr == NULL) + return CKR_TEMPLATE_INCOMPLETE; + *phPublicKey = *phPrivateKey = CK_INVALID_HANDLE; rv = NewObject(session, CKK_EC, CKO_PUBLIC_KEY, pPublicKeyTemplate, @@ -7717,6 +7769,16 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, return CKR_MECHANISM_PARAM_INVALID; } + /* DH needs the domain parameters CKA_PRIME and CKA_BASE. */ + FindAttributeType(pPublicKeyTemplate, ulPublicKeyAttributeCount, + CKA_PRIME, &reqAttr); + if (reqAttr == NULL) + return CKR_TEMPLATE_INCOMPLETE; + FindAttributeType(pPublicKeyTemplate, ulPublicKeyAttributeCount, + CKA_BASE, &reqAttr); + if (reqAttr == NULL) + return CKR_TEMPLATE_INCOMPLETE; + *phPublicKey = *phPrivateKey = CK_INVALID_HANDLE; rv = NewObject(session, CKK_DH, CKO_PUBLIC_KEY, pPublicKeyTemplate, @@ -8615,6 +8677,8 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, case CKM_ECDH1_DERIVE: { CK_ECDH1_DERIVE_PARAMS* params; + if (WP11_Object_GetType(obj) != CKK_EC) + return CKR_KEY_TYPE_INCONSISTENT; if (pMechanism->pParameter == NULL) return CKR_MECHANISM_PARAM_INVALID; if (pMechanism->ulParameterLen != sizeof(CK_ECDH1_DERIVE_PARAMS)) @@ -8647,6 +8711,12 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, CK_HKDF_PARAMS_PTR kdfParams; CK_ATTRIBUTE *lenAttr = NULL; + /* Data-derive feeds a CKO_DATA object, which has no key type. + * Only a key base object is checked for an HKDF-compatible type. */ + if (WP11_Object_GetClass(obj) != CKO_DATA && + WP11_Object_GetType(obj) != CKK_HKDF && + WP11_Object_GetType(obj) != CKK_GENERIC_SECRET) + return CKR_KEY_TYPE_INCONSISTENT; if (pMechanism->pParameter == NULL) return CKR_MECHANISM_PARAM_INVALID; if (pMechanism->ulParameterLen != sizeof(CK_HKDF_PARAMS)) @@ -8689,6 +8759,8 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, #endif #ifndef NO_DH case CKM_DH_PKCS_DERIVE: + if (WP11_Object_GetType(obj) != CKK_DH) + return CKR_KEY_TYPE_INCONSISTENT; if (pMechanism->pParameter == NULL) return CKR_MECHANISM_PARAM_INVALID; if (pMechanism->ulParameterLen == 0) @@ -8711,6 +8783,8 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, #ifdef HAVE_AES_CBC case CKM_AES_CBC_ENCRYPT_DATA: { CK_AES_CBC_ENCRYPT_DATA_PARAMS* params; + if (WP11_Object_GetType(obj) != CKK_AES) + return CKR_KEY_TYPE_INCONSISTENT; if (pMechanism->pParameter == NULL) return CKR_MECHANISM_PARAM_INVALID; if (pMechanism->ulParameterLen != @@ -8738,6 +8812,8 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, case CKM_TLS12_KEY_AND_MAC_DERIVE: { CK_TLS12_KEY_MAT_PARAMS* tlsParams = NULL; + if (WP11_Object_GetType(obj) != CKK_GENERIC_SECRET) + return CKR_KEY_TYPE_INCONSISTENT; if (pMechanism->pParameter == NULL) return CKR_MECHANISM_PARAM_INVALID; if (pMechanism->ulParameterLen != @@ -8803,6 +8879,8 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, case CKM_TLS12_MASTER_KEY_DERIVE_DH: { CK_TLS12_MASTER_KEY_DERIVE_PARAMS* prfParams; + if (WP11_Object_GetType(obj) != CKK_GENERIC_SECRET) + return CKR_KEY_TYPE_INCONSISTENT; if (pMechanism->pParameter == NULL) return CKR_MECHANISM_PARAM_INVALID; if (pMechanism->ulParameterLen != diff --git a/src/internal.c b/src/internal.c index b27c56be..1ae6d9a6 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7299,12 +7299,16 @@ int WP11_Slot_SOLogin(WP11_Slot* slot, char* pin, int pinLen) WP11_Lock_LockRO(&slot->lock); if (ret == 0) { - /* Have we already logged in? */ + /* SO already logged in is the same user type; a logged-in USER is a + * different one. */ state = slot->token.loginState; - if (state == WP11_APP_STATE_RW_SO || state == WP11_APP_STATE_RO_USER || - state == WP11_APP_STATE_RW_USER) { + if (state == WP11_APP_STATE_RW_SO) { ret = LOGGED_IN_E; } + else if (state == WP11_APP_STATE_RO_USER || + state == WP11_APP_STATE_RW_USER) { + ret = LOGGED_IN_ANOTHER_E; + } } #ifndef WOLFPKCS11_NO_TIME /* Check for too many fails and timeout. */ @@ -7395,14 +7399,17 @@ int WP11_Slot_UserLogin(WP11_Slot* slot, char* pin, int pinLen) #endif WP11_Lock_LockRW(&slot->lock); - /* Have we already logged in? */ if (ret == 0) { - /* Have we already logged in? */ + /* USER already logged in is the same user type; a logged-in SO is a + * different one. */ state = token->loginState; - if (state == WP11_APP_STATE_RW_SO || state == WP11_APP_STATE_RO_USER || - state == WP11_APP_STATE_RW_USER) { + if (state == WP11_APP_STATE_RO_USER || + state == WP11_APP_STATE_RW_USER) { ret = LOGGED_IN_E; } + else if (state == WP11_APP_STATE_RW_SO) { + ret = LOGGED_IN_ANOTHER_E; + } } #ifndef WOLFPKCS11_NO_TIME /* Check for too many fails */ @@ -8525,6 +8532,10 @@ int WP11_Session_SetGcmParams(WP11_Session* session, unsigned char* iv, * length. Either mismatch is a contract bug. */ if (ret == 0 && (iv == NULL) != (ivSz == 0)) ret = BAD_FUNC_ARG; + /* A NULL AAD pointer must not carry a non-zero length. A non-NULL pointer + * with zero length is a valid no-AAD encoding. */ + if (ret == 0 && aad == NULL && aadLen != 0) + ret = BAD_FUNC_ARG; if (ret == 0) { if (session->mechanism == CKM_AES_GCM && gcm->aad != NULL) { @@ -8535,7 +8546,7 @@ int WP11_Session_SetGcmParams(WP11_Session* session, unsigned char* iv, XMEMCPY(gcm->iv, iv, ivSz); gcm->ivSz = ivSz; gcm->tagBits = tagBits; - if (aad != NULL) { + if (aadLen > 0) { gcm->aad = (unsigned char*)XMALLOC(aadLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (gcm->aad == NULL) @@ -8581,6 +8592,10 @@ int WP11_Session_SetCcmParams(WP11_Session* session, int dataSz, * length. Either mismatch is a contract bug. */ if (ret == 0 && (iv == NULL) != (ivSz == 0)) ret = BAD_FUNC_ARG; + /* A NULL AAD pointer must not carry a non-zero length. A non-NULL pointer + * with zero length is a valid no-AAD encoding. */ + if (ret == 0 && aad == NULL && aadSz != 0) + ret = BAD_FUNC_ARG; if (ret == 0) { if (session->mechanism == CKM_AES_CCM && ccm->aad != NULL) { @@ -8591,7 +8606,7 @@ int WP11_Session_SetCcmParams(WP11_Session* session, int dataSz, if (ivSz > 0) XMEMCPY(ccm->iv, iv, ivSz); ccm->ivSz = ivSz; - if (aad != NULL) { + if (aadSz > 0) { ccm->aad = (unsigned char*)XMALLOC(aadSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (ccm->aad == NULL) { @@ -10307,30 +10322,23 @@ static int GetTrustAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, if (data != NULL) XMEMCPY(data, &object->data.trust.md5Hash, WC_MD5_DIGEST_SIZE); break; + /* GetULong/GetBool size-query and bounds-check *len themselves: they + * set it on a size query or success, and return BUFFER_E (leaving + * *len unchanged) when the buffer is too small. */ case CKA_TRUST_SERVER_AUTH: - *len = sizeof(CK_ULONG); - if (data != NULL) - ret = GetULong(object->data.trust.serverAuth, data, len); + ret = GetULong(object->data.trust.serverAuth, data, len); break; case CKA_TRUST_CLIENT_AUTH: - *len = sizeof(CK_ULONG); - if (data != NULL) - ret = GetULong(object->data.trust.clientAuth, data, len); + ret = GetULong(object->data.trust.clientAuth, data, len); break; case CKA_TRUST_CODE_SIGNING: - *len = sizeof(CK_ULONG); - if (data != NULL) - ret = GetULong(object->data.trust.codeSigning, data, len); + ret = GetULong(object->data.trust.codeSigning, data, len); break; case CKA_TRUST_EMAIL_PROTECTION: - *len = sizeof(CK_ULONG); - if (data != NULL) - ret = GetULong(object->data.trust.emailProtection, data, len); + ret = GetULong(object->data.trust.emailProtection, data, len); break; case CKA_TRUST_STEP_UP_APPROVED: - *len = sizeof(CK_BBOOL); - if (data != NULL) - ret = GetBool(object->data.trust.stepUpApproved, data, len); + ret = GetBool(object->data.trust.stepUpApproved, data, len); break; case CKA_ISSUER: ret = GetData(object->issuer, object->issuerLen, data, len); diff --git a/src/slot.c b/src/slot.c index 6efc8dc9..4764eef4 100644 --- a/src/slot.c +++ b/src/slot.c @@ -1824,7 +1824,10 @@ CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession, * and a read-only session is open. * CKR_USER_PIN_NOT_INITIALIZED when PIN is not initialized for user * type. - * CKR_PIN_INCORRECT when PIN is wrong length or does not verify. + * CKR_USER_ANOTHER_ALREADY_LOGGED_IN when a different user type is + * already logged in. + * CKR_PIN_LEN_RANGE when the PIN length is out of range. + * CKR_PIN_INCORRECT when the PIN does not verify. * CKR_OPERATION_NOT_INITIALIZED when using user type * CKU_CONTEXT_SPECIFIC - user type not supported. * CKR_USER_TYPE_INVALID when other user type is specified. @@ -1862,8 +1865,8 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, return rv; } - if (checkPinLen(ulPinLen) != CKR_OK) { - rv = CKR_PIN_INCORRECT; + rv = checkPinLen(ulPinLen); + if (rv != CKR_OK) { WOLFPKCS11_LEAVE("C_Login", rv); return rv; } @@ -1890,6 +1893,9 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, case LOGGED_IN_E: rv = CKR_USER_ALREADY_LOGGED_IN; break; + case LOGGED_IN_ANOTHER_E: + rv = CKR_USER_ANOTHER_ALREADY_LOGGED_IN; + break; case PIN_NOT_SET_E: rv = CKR_USER_PIN_NOT_INITIALIZED; break; diff --git a/tests/aead_null_aad_test.c b/tests/aead_null_aad_test.c new file mode 100644 index 00000000..6b22f9f2 --- /dev/null +++ b/tests/aead_null_aad_test.c @@ -0,0 +1,199 @@ +/* aead_null_aad_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 + * + * Regression test for issue F-5514: AES-GCM and AES-CCM C_EncryptInit must + * reject a NULL AAD pointer paired with a non-zero AAD length with + * CKR_MECHANISM_PARAM_INVALID. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/aead_null_aad_test" + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; + CK_OBJECT_HANDLE aesKey = CK_INVALID_HANDLE; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_KEY_TYPE aesType = CKK_AES; + CK_BBOOL ckTrue = CK_TRUE; + CK_BBOOL ckFalse = CK_FALSE; + CK_MECHANISM mech; + CK_GCM_PARAMS gcm; + CK_CCM_PARAMS ccm; + byte iv[12]; + byte aad[16]; + byte plain[32], enc[64]; + CK_ULONG encSz; + CK_ATTRIBUTE aesTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &aesType, sizeof(aesType) }, + { CKA_VALUE, aes_128_key, sizeof(aes_128_key) }, + { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + }; + CK_ULONG aesTmplCnt = sizeof(aesTmpl) / sizeof(*aesTmpl); + + XMEMSET(iv, 9, sizeof(iv)); + XMEMSET(aad, 5, sizeof(aad)); + XMEMSET(plain, 7, sizeof(plain)); + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + + rv = funcList->C_CreateObject(session, aesTmpl, aesTmplCnt, &aesKey); + CHECK_RV(rv, "C_CreateObject(AES key)", CKR_OK); + if (rv != CKR_OK) + goto out; + + /* GCM: NULL AAD pointer with a non-zero AAD length must be rejected. + * Skip if AES-GCM is not built in. */ + XMEMSET(&gcm, 0, sizeof(gcm)); + gcm.pIv = iv; + gcm.ulIvLen = sizeof(iv); + gcm.pAAD = NULL; + gcm.ulAADLen = sizeof(aad); + gcm.ulTagBits = 128; + mech.mechanism = CKM_AES_GCM; + mech.pParameter = &gcm; + mech.ulParameterLen = sizeof(gcm); + rv = funcList->C_EncryptInit(session, &mech, aesKey); + if (rv == CKR_MECHANISM_INVALID) { + printf("SKIP: AES-GCM not supported in this build\n"); + test_passed++; + } + else { + CHECK_RV(rv, "C_EncryptInit GCM (NULL AAD, len 16)", + CKR_MECHANISM_PARAM_INVALID); + + /* The canonical no-AAD form (NULL pointer, zero length) is valid. */ + XMEMSET(&gcm, 0, sizeof(gcm)); + gcm.pIv = iv; + gcm.ulIvLen = sizeof(iv); + gcm.pAAD = NULL; + gcm.ulAADLen = 0; + gcm.ulTagBits = 128; + mech.mechanism = CKM_AES_GCM; + mech.pParameter = &gcm; + mech.ulParameterLen = sizeof(gcm); + rv = funcList->C_EncryptInit(session, &mech, aesKey); + CHECK_RV(rv, "C_EncryptInit GCM (NULL AAD, len 0)", CKR_OK); + if (rv == CKR_OK) { + encSz = sizeof(enc); + rv = funcList->C_Encrypt(session, plain, sizeof(plain), enc, + &encSz); + CHECK_RV(rv, "C_Encrypt GCM (no AAD) completes", CKR_OK); + } + + /* A non-NULL AAD pointer with zero length is also a valid no-AAD + * encoding and must be accepted. */ + XMEMSET(&gcm, 0, sizeof(gcm)); + gcm.pIv = iv; + gcm.ulIvLen = sizeof(iv); + gcm.pAAD = aad; + gcm.ulAADLen = 0; + gcm.ulTagBits = 128; + mech.mechanism = CKM_AES_GCM; + mech.pParameter = &gcm; + mech.ulParameterLen = sizeof(gcm); + rv = funcList->C_EncryptInit(session, &mech, aesKey); + CHECK_RV(rv, "C_EncryptInit GCM (non-NULL AAD, len 0)", CKR_OK); + if (rv == CKR_OK) { + encSz = sizeof(enc); + rv = funcList->C_Encrypt(session, plain, sizeof(plain), enc, + &encSz); + CHECK_RV(rv, "C_Encrypt GCM (non-NULL AAD, len 0) completes", + CKR_OK); + } + } + + /* CCM: same mismatch; skip if AES-CCM is not built in. */ + XMEMSET(&ccm, 0, sizeof(ccm)); + ccm.ulDataLen = sizeof(plain); + ccm.pIv = iv; + ccm.ulIvLen = sizeof(iv) - 5; /* 7-byte nonce is valid for CCM */ + ccm.pAAD = NULL; + ccm.ulAADLen = sizeof(aad); + ccm.ulMacLen = 16; + mech.mechanism = CKM_AES_CCM; + mech.pParameter = &ccm; + mech.ulParameterLen = sizeof(ccm); + rv = funcList->C_EncryptInit(session, &mech, aesKey); + if (rv == CKR_MECHANISM_INVALID) { + printf("SKIP: AES-CCM not supported in this build\n"); + test_passed++; + } + else { + CHECK_RV(rv, "C_EncryptInit CCM (NULL AAD, len 16)", + CKR_MECHANISM_PARAM_INVALID); + } + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 AEAD NULL-AAD mismatch test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/create_object_class_test.c b/tests/create_object_class_test.c new file mode 100644 index 00000000..9a62ff80 --- /dev/null +++ b/tests/create_object_class_test.c @@ -0,0 +1,112 @@ +/* create_object_class_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 + * + * Regression test for issues F-5513 and F-5516: a C_CreateObject key template + * that omits CKA_CLASS must be rejected with CKR_TEMPLATE_INCOMPLETE. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/create_object_class_test" + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; + CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_KEY_TYPE genericType = CKK_GENERIC_SECRET; + CK_BBOOL ckFalse = CK_FALSE; + byte keyData[16] = { 0 }; + CK_ATTRIBUTE noClassTmpl[] = { + { CKA_KEY_TYPE, &genericType, sizeof(genericType) }, + { CKA_VALUE, keyData, sizeof(keyData) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + }; + CK_ULONG noClassTmplCnt = sizeof(noClassTmpl) / sizeof(*noClassTmpl); + CK_ATTRIBUTE withClassTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &genericType, sizeof(genericType) }, + { CKA_VALUE, keyData, sizeof(keyData) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + }; + CK_ULONG withClassTmplCnt = sizeof(withClassTmpl) / sizeof(*withClassTmpl); + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + + rv = funcList->C_CreateObject(session, noClassTmpl, noClassTmplCnt, &obj); + CHECK_RV(rv, "C_CreateObject(no CKA_CLASS)", CKR_TEMPLATE_INCOMPLETE); + + rv = funcList->C_CreateObject(session, withClassTmpl, withClassTmplCnt, + &obj); + CHECK_RV(rv, "C_CreateObject(with CKA_CLASS)", CKR_OK); + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 C_CreateObject CKA_CLASS test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/decrypt_final_bufsize_test.c b/tests/decrypt_final_bufsize_test.c new file mode 100644 index 00000000..f02a3018 --- /dev/null +++ b/tests/decrypt_final_bufsize_test.c @@ -0,0 +1,188 @@ +/* decrypt_final_bufsize_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 + * + * Regression test for issue F-6050: the CKM_AES_CBC_PAD branch of + * C_DecryptFinal must report CKR_BUFFER_TOO_SMALL for an undersized output + * buffer instead of overflowing it. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/decrypt_final_bufsize_test" + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; + CK_OBJECT_HANDLE aesKey = CK_INVALID_HANDLE; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_KEY_TYPE aesType = CKK_AES; + CK_BBOOL ckTrue = CK_TRUE; + CK_BBOOL ckFalse = CK_FALSE; + CK_MECHANISM mech; + byte iv[16]; + /* 15-byte plaintext -> one padded block whose final block unpads to 15 + * bytes, the maximum unpadded size. */ + byte plain[15]; + byte enc[32]; + byte upd[32]; + byte lastPart[32]; + CK_ULONG encSz = sizeof(enc); + CK_ULONG updSz; + CK_ULONG lastPartLen; + int i; + CK_ATTRIBUTE aesTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &aesType, sizeof(aesType) }, + { CKA_VALUE, aes_128_key, sizeof(aes_128_key) }, + { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, + { CKA_DECRYPT, &ckTrue, sizeof(ckTrue) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + }; + CK_ULONG aesTmplCnt = sizeof(aesTmpl) / sizeof(*aesTmpl); + + XMEMSET(iv, 9, sizeof(iv)); + XMEMSET(plain, 7, sizeof(plain)); + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + + rv = funcList->C_CreateObject(session, aesTmpl, aesTmplCnt, &aesKey); + CHECK_RV(rv, "C_CreateObject(AES key)", CKR_OK); + if (rv != CKR_OK) + goto out; + + XMEMSET(&mech, 0, sizeof(mech)); + mech.mechanism = CKM_AES_CBC_PAD; + mech.pParameter = iv; + mech.ulParameterLen = sizeof(iv); + + rv = funcList->C_EncryptInit(session, &mech, aesKey); + CHECK_RV(rv, "C_EncryptInit", CKR_OK); + if (rv != CKR_OK) + goto out; + encSz = sizeof(enc); + rv = funcList->C_Encrypt(session, plain, sizeof(plain), enc, &encSz); + CHECK_RV(rv, "C_Encrypt", CKR_OK); + if (rv != CKR_OK) + goto out; + CHECK_TRUE(encSz == 16, "ciphertext is one block"); + + /* Baseline: a full multi-part decrypt with an adequate final buffer + * round-trips the plaintext. */ + rv = funcList->C_DecryptInit(session, &mech, aesKey); + CHECK_RV(rv, "C_DecryptInit (baseline)", CKR_OK); + if (rv != CKR_OK) + goto out; + updSz = sizeof(upd); + rv = funcList->C_DecryptUpdate(session, enc, encSz, upd, &updSz); + CHECK_RV(rv, "C_DecryptUpdate (baseline)", CKR_OK); + if (rv != CKR_OK) + goto out; + CHECK_TRUE(updSz == 0, "DecryptUpdate holds back final block"); + lastPartLen = sizeof(lastPart); + rv = funcList->C_DecryptFinal(session, lastPart, &lastPartLen); + CHECK_RV(rv, "C_DecryptFinal (adequate buffer)", CKR_OK); + CHECK_TRUE(lastPartLen == sizeof(plain) && + XMEMCMP(lastPart, plain, sizeof(plain)) == 0, + "plaintext round-trips with adequate buffer"); + + /* Undersized final buffer: claim 4 bytes and guard the rest with a + * canary. C_DecryptFinal must report CKR_BUFFER_TOO_SMALL without writing + * past the 4 bytes. */ + rv = funcList->C_DecryptInit(session, &mech, aesKey); + CHECK_RV(rv, "C_DecryptInit (undersized)", CKR_OK); + if (rv != CKR_OK) + goto out; + updSz = sizeof(upd); + rv = funcList->C_DecryptUpdate(session, enc, encSz, upd, &updSz); + CHECK_RV(rv, "C_DecryptUpdate (undersized)", CKR_OK); + if (rv != CKR_OK) + goto out; + + /* Size query reports the maximum unpadded size (15). */ + lastPartLen = 0; + rv = funcList->C_DecryptFinal(session, NULL, &lastPartLen); + CHECK_RV(rv, "C_DecryptFinal size query", CKR_OK); + CHECK_TRUE(lastPartLen == 15, "size query reports 15"); + + XMEMSET(lastPart, 0xAB, sizeof(lastPart)); + lastPartLen = 4; + rv = funcList->C_DecryptFinal(session, lastPart, &lastPartLen); + CHECK_RV(rv, "C_DecryptFinal(undersized buffer)", CKR_BUFFER_TOO_SMALL); + + for (i = 4; i < (int)sizeof(lastPart); i++) { + if (lastPart[i] != 0xAB) + break; + } + CHECK_TRUE(i == (int)sizeof(lastPart), + "no write past the caller-declared buffer length"); + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 C_DecryptFinal CBC-PAD buffer-size test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/derive_key_type_test.c b/tests/derive_key_type_test.c new file mode 100644 index 00000000..e7a818fa --- /dev/null +++ b/tests/derive_key_type_test.c @@ -0,0 +1,229 @@ +/* derive_key_type_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 + * + * Regression test for issue F-4065: C_DeriveKey must reject a base key whose + * type does not match the derivation mechanism with + * CKR_KEY_TYPE_INCONSISTENT. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/derive_key_type_test" + +/* Derive with the given mechanism and wrong-typed base key, asserting + * CKR_KEY_TYPE_INCONSISTENT, or skipping if the mechanism is not built in. */ +static void check_wrong_type(CK_SESSION_HANDLE session, CK_MECHANISM* mech, + CK_OBJECT_HANDLE base, const char* op) +{ + CK_RV rv; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_KEY_TYPE genericType = CKK_GENERIC_SECRET; + CK_ULONG valLen = 16; + CK_BBOOL ckFalse = CK_FALSE; + CK_ATTRIBUTE outTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &genericType, sizeof(genericType) }, + { CKA_VALUE_LEN, &valLen, sizeof(valLen) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + }; + CK_OBJECT_HANDLE out = CK_INVALID_HANDLE; + + rv = funcList->C_DeriveKey(session, mech, base, outTmpl, + sizeof(outTmpl) / sizeof(*outTmpl), &out); + if (rv == CKR_MECHANISM_INVALID) { + printf("SKIP: %s (mechanism not supported)\n", op); + test_passed++; + return; + } + CHECK_RV(rv, op, CKR_KEY_TYPE_INCONSISTENT); + if (rv == CKR_OK && out != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, out); +} + +#ifdef WOLFPKCS11_NSS +/* NSS drives the TLS 1.3 key schedule by deriving from a CKO_DATA object. The + * value it sends for that mechanism (PKCS#11 standard 0x402A) is the constant + * wolfPKCS11 names CKM_HKDF_DERIVE, so the CKO_DATA base object reaches the + * key-based derive case and must be accepted, not rejected as a key-type + * mismatch. The CKA_DERIVE usage gate is skipped only in NSS builds, so this + * path is reachable there. */ +static void check_hkdf_data_object(CK_SESSION_HANDLE session) +{ + CK_RV rv; + CK_OBJECT_CLASS dataClass = CKO_DATA; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_KEY_TYPE hkdfType = CKK_HKDF; + CK_BBOOL ckTrue = CK_TRUE; + CK_BBOOL ckFalse = CK_FALSE; + byte value[32]; + CK_ULONG valueLen = sizeof(value); + CK_OBJECT_HANDLE dataObj = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE out = CK_INVALID_HANDLE; + CK_HKDF_PARAMS hkdf; + CK_MECHANISM mech; + CK_ATTRIBUTE dataTmpl[] = { + { CKA_CLASS, &dataClass, sizeof(dataClass) }, + { CKA_VALUE, value, sizeof(value) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + }; + CK_ATTRIBUTE outTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &hkdfType, sizeof(hkdfType) }, + { CKA_VALUE_LEN, &valueLen, sizeof(valueLen) }, + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + }; + + XMEMSET(value, 7, sizeof(value)); + rv = funcList->C_CreateObject(session, dataTmpl, + sizeof(dataTmpl) / sizeof(*dataTmpl), + &dataObj); + CHECK_RV(rv, "C_CreateObject(CKO_DATA)", CKR_OK); + if (rv != CKR_OK) + return; + + XMEMSET(&hkdf, 0, sizeof(hkdf)); + hkdf.bExtract = CK_TRUE; + hkdf.bExpand = CK_TRUE; + hkdf.prfHashMechanism = CKM_SHA256; + hkdf.ulSaltType = CKF_HKDF_SALT_NULL; + hkdf.pInfo = value; + hkdf.ulInfoLen = 8; + mech.mechanism = CKM_HKDF_DERIVE; + mech.pParameter = &hkdf; + mech.ulParameterLen = sizeof(hkdf); + rv = funcList->C_DeriveKey(session, &mech, dataObj, outTmpl, + sizeof(outTmpl) / sizeof(*outTmpl), &out); + if (rv == CKR_MECHANISM_INVALID) { + printf("SKIP: CKM_HKDF_DERIVE not supported in this build\n"); + test_passed++; + return; + } + CHECK_RV(rv, "C_DeriveKey(CKM_HKDF_DERIVE, CKO_DATA base)", CKR_OK); + if (rv == CKR_OK) + funcList->C_DestroyObject(session, out); +} +#endif + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; + CK_OBJECT_HANDLE aesKey = CK_INVALID_HANDLE; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_KEY_TYPE aesType = CKK_AES; + CK_BBOOL ckTrue = CK_TRUE; + CK_BBOOL ckFalse = CK_FALSE; + CK_MECHANISM mech; + byte pub[65]; + byte param[32]; + CK_ECDH1_DERIVE_PARAMS ecdh; + /* AES is the wrong base-key type for these mechanisms; CKA_DERIVE=TRUE so + * the usage gate passes and the per-mechanism type check is exercised. */ + CK_ATTRIBUTE aesTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &aesType, sizeof(aesType) }, + { CKA_VALUE, aes_128_key, sizeof(aes_128_key) }, + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + }; + CK_ULONG aesTmplCnt = sizeof(aesTmpl) / sizeof(*aesTmpl); + + XMEMSET(pub, 4, sizeof(pub)); + XMEMSET(param, 7, sizeof(param)); + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + + rv = funcList->C_CreateObject(session, aesTmpl, aesTmplCnt, &aesKey); + CHECK_RV(rv, "C_CreateObject(AES base key)", CKR_OK); + if (rv != CKR_OK) + goto out; + + /* ECDH derive expects a CKK_EC base key. */ + XMEMSET(&ecdh, 0, sizeof(ecdh)); + ecdh.kdf = CKD_NULL; + ecdh.pPublicData = pub; + ecdh.ulPublicDataLen = sizeof(pub); + mech.mechanism = CKM_ECDH1_DERIVE; + mech.pParameter = &ecdh; + mech.ulParameterLen = sizeof(ecdh); + check_wrong_type(session, &mech, aesKey, "ECDH1 derive with AES base key"); + + /* DH derive expects a CKK_DH base key. */ + mech.mechanism = CKM_DH_PKCS_DERIVE; + mech.pParameter = param; + mech.ulParameterLen = sizeof(param); + check_wrong_type(session, &mech, aesKey, "DH derive with AES base key"); + +#ifdef WOLFPKCS11_NSS + check_hkdf_data_object(session); +#endif + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 C_DeriveKey base-key-type test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/gen_keypair_incomplete_test.c b/tests/gen_keypair_incomplete_test.c new file mode 100644 index 00000000..1d122a41 --- /dev/null +++ b/tests/gen_keypair_incomplete_test.c @@ -0,0 +1,145 @@ +/* gen_keypair_incomplete_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 + * + * Regression test for issue F-5518: C_GenerateKeyPair must reject a public + * template missing a mechanism-required attribute (CKA_MODULUS_BITS for RSA, + * CKA_EC_PARAMS for EC, CKA_PRIME/CKA_BASE for DH) with + * CKR_TEMPLATE_INCOMPLETE. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/gen_keypair_incomplete_test" + +/* Generate a key pair with the given public template. CKA_PRIVATE=FALSE on + * the private template keeps the keys public so no C_Login is needed. The + * mechanism is skipped if it is not built in. */ +static void check_incomplete(CK_SESSION_HANDLE session, CK_MECHANISM_TYPE mech, + CK_ATTRIBUTE* pubTmpl, CK_ULONG pubCnt, + const char* op) +{ + CK_RV rv; + CK_MECHANISM mechanism; + CK_BBOOL ckFalse = CK_FALSE; + CK_ATTRIBUTE privTmpl[] = { + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + }; + CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE; + + XMEMSET(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = mech; + + rv = funcList->C_GenerateKeyPair(session, &mechanism, pubTmpl, pubCnt, + privTmpl, + sizeof(privTmpl) / sizeof(*privTmpl), + &pub, &priv); + if (rv == CKR_MECHANISM_INVALID) { + printf("SKIP: %s (mechanism not supported)\n", op); + test_passed++; + return; + } + CHECK_RV(rv, op, CKR_TEMPLATE_INCOMPLETE); + if (rv == CKR_OK) { + funcList->C_DestroyObject(session, pub); + funcList->C_DestroyObject(session, priv); + } +} + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; + CK_BBOOL ckTrue = CK_TRUE; + CK_ATTRIBUTE rsaPub[] = { /* missing CKA_MODULUS_BITS */ + { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, + { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, + }; + CK_ATTRIBUTE ecPub[] = { /* missing CKA_EC_PARAMS */ + { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, + }; + CK_ATTRIBUTE dhPub[] = { /* missing CKA_PRIME and CKA_BASE */ + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + }; + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + + check_incomplete(session, CKM_RSA_PKCS_KEY_PAIR_GEN, rsaPub, + sizeof(rsaPub) / sizeof(*rsaPub), + "RSA keygen without CKA_MODULUS_BITS"); + check_incomplete(session, CKM_EC_KEY_PAIR_GEN, ecPub, + sizeof(ecPub) / sizeof(*ecPub), + "EC keygen without CKA_EC_PARAMS"); + check_incomplete(session, CKM_DH_PKCS_KEY_PAIR_GEN, dhPub, + sizeof(dhPub) / sizeof(*dhPub), + "DH keygen without CKA_PRIME/CKA_BASE"); + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 C_GenerateKeyPair incomplete-template test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/include.am b/tests/include.am index df786c44..ac9b98a3 100644 --- a/tests/include.am +++ b/tests/include.am @@ -91,6 +91,66 @@ noinst_PROGRAMS += tests/pkcs11_compliance_test tests_pkcs11_compliance_test_SOURCES = tests/pkcs11_compliance_test.c tests_pkcs11_compliance_test_LDADD = +check_PROGRAMS += tests/verify_recover_badkey_test +noinst_PROGRAMS += tests/verify_recover_badkey_test +tests_verify_recover_badkey_test_SOURCES = tests/verify_recover_badkey_test.c +tests_verify_recover_badkey_test_LDADD = + +check_PROGRAMS += tests/login_pin_len_range_test +noinst_PROGRAMS += tests/login_pin_len_range_test +tests_login_pin_len_range_test_SOURCES = tests/login_pin_len_range_test.c +tests_login_pin_len_range_test_LDADD = + +check_PROGRAMS += tests/verify_recover_class_test +noinst_PROGRAMS += tests/verify_recover_class_test +tests_verify_recover_class_test_SOURCES = tests/verify_recover_class_test.c +tests_verify_recover_class_test_LDADD = + +check_PROGRAMS += tests/login_another_user_test +noinst_PROGRAMS += tests/login_another_user_test +tests_login_another_user_test_SOURCES = tests/login_another_user_test.c +tests_login_another_user_test_LDADD = + +check_PROGRAMS += tests/key_function_not_permitted_test +noinst_PROGRAMS += tests/key_function_not_permitted_test +tests_key_function_not_permitted_test_SOURCES = tests/key_function_not_permitted_test.c +tests_key_function_not_permitted_test_LDADD = + +check_PROGRAMS += tests/decrypt_final_bufsize_test +noinst_PROGRAMS += tests/decrypt_final_bufsize_test +tests_decrypt_final_bufsize_test_SOURCES = tests/decrypt_final_bufsize_test.c +tests_decrypt_final_bufsize_test_LDADD = + +check_PROGRAMS += tests/create_object_class_test +noinst_PROGRAMS += tests/create_object_class_test +tests_create_object_class_test_SOURCES = tests/create_object_class_test.c +tests_create_object_class_test_LDADD = + +check_PROGRAMS += tests/aead_null_aad_test +noinst_PROGRAMS += tests/aead_null_aad_test +tests_aead_null_aad_test_SOURCES = tests/aead_null_aad_test.c +tests_aead_null_aad_test_LDADD = + +check_PROGRAMS += tests/gen_keypair_incomplete_test +noinst_PROGRAMS += tests/gen_keypair_incomplete_test +tests_gen_keypair_incomplete_test_SOURCES = tests/gen_keypair_incomplete_test.c +tests_gen_keypair_incomplete_test_LDADD = + +check_PROGRAMS += tests/set_attr_readonly_test +noinst_PROGRAMS += tests/set_attr_readonly_test +tests_set_attr_readonly_test_SOURCES = tests/set_attr_readonly_test.c +tests_set_attr_readonly_test_LDADD = + +check_PROGRAMS += tests/derive_key_type_test +noinst_PROGRAMS += tests/derive_key_type_test +tests_derive_key_type_test_SOURCES = tests/derive_key_type_test.c +tests_derive_key_type_test_LDADD = + +check_PROGRAMS += tests/trust_attr_bufsize_test +noinst_PROGRAMS += tests/trust_attr_bufsize_test +tests_trust_attr_bufsize_test_SOURCES = tests/trust_attr_bufsize_test.c +tests_trust_attr_bufsize_test_LDADD = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -110,6 +170,18 @@ tests_pbkdf2_keygen_attrs_test_LDADD += src/libwolfpkcs11.la tests_pkcs11v3test_LDADD += src/libwolfpkcs11.la tests_rsa_exponent_test_LDADD += src/libwolfpkcs11.la tests_pkcs11_compliance_test_LDADD += src/libwolfpkcs11.la +tests_verify_recover_badkey_test_LDADD += src/libwolfpkcs11.la +tests_login_pin_len_range_test_LDADD += src/libwolfpkcs11.la +tests_verify_recover_class_test_LDADD += src/libwolfpkcs11.la +tests_login_another_user_test_LDADD += src/libwolfpkcs11.la +tests_key_function_not_permitted_test_LDADD += src/libwolfpkcs11.la +tests_decrypt_final_bufsize_test_LDADD += src/libwolfpkcs11.la +tests_create_object_class_test_LDADD += src/libwolfpkcs11.la +tests_aead_null_aad_test_LDADD += src/libwolfpkcs11.la +tests_gen_keypair_incomplete_test_LDADD += src/libwolfpkcs11.la +tests_set_attr_readonly_test_LDADD += src/libwolfpkcs11.la +tests_derive_key_type_test_LDADD += src/libwolfpkcs11.la +tests_trust_attr_bufsize_test_LDADD += src/libwolfpkcs11.la else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -121,8 +193,21 @@ tests_operation_active_test_LDADD += src/libwolfpkcs11.la tests_aes_keygen_attrs_test_LDADD += src/libwolfpkcs11.la tests_pbkdf2_keygen_attrs_test_LDADD += src/libwolfpkcs11.la tests_pkcs11_compliance_test_LDADD += src/libwolfpkcs11.la +tests_verify_recover_badkey_test_LDADD += src/libwolfpkcs11.la +tests_login_pin_len_range_test_LDADD += src/libwolfpkcs11.la +tests_verify_recover_class_test_LDADD += src/libwolfpkcs11.la +tests_login_another_user_test_LDADD += src/libwolfpkcs11.la +tests_key_function_not_permitted_test_LDADD += src/libwolfpkcs11.la +tests_decrypt_final_bufsize_test_LDADD += src/libwolfpkcs11.la +tests_create_object_class_test_LDADD += src/libwolfpkcs11.la +tests_aead_null_aad_test_LDADD += src/libwolfpkcs11.la +tests_gen_keypair_incomplete_test_LDADD += src/libwolfpkcs11.la +tests_set_attr_readonly_test_LDADD += src/libwolfpkcs11.la +tests_derive_key_type_test_LDADD += src/libwolfpkcs11.la +tests_trust_attr_bufsize_test_LDADD += src/libwolfpkcs11.la endif EXTRA_DIST += tests/unit.h \ tests/testdata.h \ + tests/pkcs11_test_util.h \ tests/README.md diff --git a/tests/key_function_not_permitted_test.c b/tests/key_function_not_permitted_test.c new file mode 100644 index 00000000..fe1fdc88 --- /dev/null +++ b/tests/key_function_not_permitted_test.c @@ -0,0 +1,123 @@ +/* key_function_not_permitted_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 + * + * Regression test for issue F-6052: an operation denied by a key's CKA_ + * usage attribute must report CKR_KEY_FUNCTION_NOT_PERMITTED, distinct from + * the key-type mismatch case (CKR_KEY_TYPE_INCONSISTENT). + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/key_function_not_permitted_test" + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; + CK_OBJECT_HANDLE aesKey = CK_INVALID_HANDLE; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_KEY_TYPE aesType = CKK_AES; + CK_BBOOL ckFalse = CK_FALSE; + byte keyData[16] = { 0 }; + byte iv[16] = { 0 }; + CK_MECHANISM mech; + CK_ATTRIBUTE aesTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &aesType, sizeof(aesType) }, + { CKA_VALUE, keyData, sizeof(keyData) }, + /* Correct key type for the mechanism, but usage is denied. */ + { CKA_ENCRYPT, &ckFalse, sizeof(ckFalse) }, + { CKA_DECRYPT, &ckFalse, sizeof(ckFalse) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + }; + CK_ULONG aesTmplCnt = sizeof(aesTmpl) / sizeof(*aesTmpl); + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + + rv = funcList->C_CreateObject(session, aesTmpl, aesTmplCnt, &aesKey); + CHECK_RV(rv, "C_CreateObject(AES, encrypt/decrypt off)", CKR_OK); + if (rv != CKR_OK) + goto out; + + XMEMSET(&mech, 0, sizeof(mech)); + mech.mechanism = CKM_AES_CBC; + mech.pParameter = iv; + mech.ulParameterLen = sizeof(iv); + + rv = funcList->C_EncryptInit(session, &mech, aesKey); + CHECK_RV(rv, "C_EncryptInit(CKA_ENCRYPT=FALSE)", + CKR_KEY_FUNCTION_NOT_PERMITTED); + + rv = funcList->C_DecryptInit(session, &mech, aesKey); + CHECK_RV(rv, "C_DecryptInit(CKA_DECRYPT=FALSE)", + CKR_KEY_FUNCTION_NOT_PERMITTED); + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 key-function-not-permitted test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/login_another_user_test.c b/tests/login_another_user_test.c new file mode 100644 index 00000000..b379efa5 --- /dev/null +++ b/tests/login_another_user_test.c @@ -0,0 +1,167 @@ +/* login_another_user_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 + * + * Regression test for issue F-4527: C_Login must distinguish the + * same-user-type case (CKR_USER_ALREADY_LOGGED_IN) from the different-user- + * type case (CKR_USER_ANOTHER_ALREADY_LOGGED_IN). + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/login_another_user_test" +#define WOLFPKCS11_TOKEN_FILENAME "wp11_token_0000000000000001" + +static byte* soPin = (byte*)"password123456"; +static CK_ULONG soPinLen = 14; +static byte* userPin = (byte*)"wolfpkcs11-test"; +static CK_ULONG userPinLen = 15; + +static void cleanup_store(void) +{ + char filepath[512]; + snprintf(filepath, sizeof(filepath), "%s" PATH_SEP "%s", TEST_DIR, + WOLFPKCS11_TOKEN_FILENAME); + (void)remove(filepath); +} + +static int run_test(void) +{ + CK_RV rv; + CK_C_INITIALIZE_ARGS args; + CK_SLOT_ID slotList[16]; + CK_ULONG slotCount = sizeof(slotList) / sizeof(slotList[0]); + CK_SLOT_ID slot = 0; + CK_SESSION_HANDLE session = 0; + int sessFlags = CKF_SERIAL_SESSION | CKF_RW_SESSION; + /* 32-byte space-padded label as required by C_InitToken. */ + CK_UTF8CHAR label[32]; + + XMEMSET(label, ' ', sizeof(label)); + XMEMCPY(label, "another-user-test", 17); + + /* Start from an uninitialized token. */ + cleanup_store(); + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + XMEMSET(&args, 0, sizeof(args)); + args.flags = CKF_OS_LOCKING_OK; + rv = funcList->C_Initialize(&args); + CHECK_RV(rv, "C_Initialize", CKR_OK); + if (rv != CKR_OK) + goto out; + + rv = funcList->C_GetSlotList(CK_TRUE, slotList, &slotCount); + CHECK_RV(rv, "C_GetSlotList", CKR_OK); + if (rv != CKR_OK || slotCount == 0) + goto out; + slot = slotList[0]; + + /* C_InitToken (sets the SO PIN) requires no open session. */ + rv = funcList->C_InitToken(slot, soPin, soPinLen, label); + CHECK_RV(rv, "C_InitToken", CKR_OK); + if (rv != CKR_OK) + goto out; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", CKR_OK); + if (rv != CKR_OK) + goto out; + + /* SO logged in: a second SO login is the same user type. */ + rv = funcList->C_Login(session, CKU_SO, soPin, soPinLen); + CHECK_RV(rv, "C_Login(SO)", CKR_OK); + + rv = funcList->C_Login(session, CKU_SO, soPin, soPinLen); + CHECK_RV(rv, "C_Login(SO) again -> ALREADY_LOGGED_IN", + CKR_USER_ALREADY_LOGGED_IN); + + rv = funcList->C_Login(session, CKU_USER, userPin, userPinLen); + CHECK_RV(rv, "C_Login(USER) while SO -> ANOTHER_ALREADY_LOGGED_IN", + CKR_USER_ANOTHER_ALREADY_LOGGED_IN); + + /* Set the user PIN (requires SO login) for the second phase. */ + rv = funcList->C_InitPIN(session, userPin, userPinLen); + CHECK_RV(rv, "C_InitPIN", CKR_OK); + + rv = funcList->C_Logout(session); + CHECK_RV(rv, "C_Logout(SO)", CKR_OK); + + /* USER logged in: a second USER login is the same user type. */ + rv = funcList->C_Login(session, CKU_USER, userPin, userPinLen); + CHECK_RV(rv, "C_Login(USER)", CKR_OK); + + rv = funcList->C_Login(session, CKU_USER, userPin, userPinLen); + CHECK_RV(rv, "C_Login(USER) again -> ALREADY_LOGGED_IN", + CKR_USER_ALREADY_LOGGED_IN); + + rv = funcList->C_Login(session, CKU_SO, soPin, soPinLen); + CHECK_RV(rv, "C_Login(SO) while USER -> ANOTHER_ALREADY_LOGGED_IN", + CKR_USER_ANOTHER_ALREADY_LOGGED_IN); + + funcList->C_Logout(session); + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 C_Login another-user test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/login_pin_len_range_test.c b/tests/login_pin_len_range_test.c new file mode 100644 index 00000000..b05b30d3 --- /dev/null +++ b/tests/login_pin_len_range_test.c @@ -0,0 +1,102 @@ +/* login_pin_len_range_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 + * + * Regression test for issue F-3834: a C_Login PIN whose length is out of + * range must be reported as CKR_PIN_LEN_RANGE, matching C_InitToken / + * C_InitPIN / C_SetPIN. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/login_pin_len_range_test" + +/* Longer than WP11_MAX_PIN_LEN (32), so checkPinLen reports CKR_PIN_LEN_RANGE + * regardless of the WP11_MIN_PIN_LEN build setting. */ +static const char* tooLongPin = + "0123456789012345678901234567890123456789"; + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; + CK_ULONG pinLen = (CK_ULONG)XSTRLEN(tooLongPin); + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + + rv = funcList->C_Login(session, CKU_USER, (CK_UTF8CHAR_PTR)tooLongPin, + pinLen); + CHECK_RV(rv, "C_Login(CKU_USER, over-length PIN)", CKR_PIN_LEN_RANGE); + + rv = funcList->C_Login(session, CKU_SO, (CK_UTF8CHAR_PTR)tooLongPin, + pinLen); + CHECK_RV(rv, "C_Login(CKU_SO, over-length PIN)", CKR_PIN_LEN_RANGE); + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 C_Login PIN length range test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/pkcs11_test_util.h b/tests/pkcs11_test_util.h new file mode 100644 index 00000000..a89e3e33 --- /dev/null +++ b/tests/pkcs11_test_util.h @@ -0,0 +1,162 @@ +/* pkcs11_test_util.h + * + * 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 + */ + +/* Shared helpers for the standalone regression tests. Each test is built as + * its own program, so these are file-local (static). Include after + * , (XMEMSET), "testdata.h" + * (WOLFPKCS11_DLL_FILENAME) and, for the dynamic loader path, . + * + * Not every test uses every helper, so the definitions are wrapped in the + * same unused-symbol suppression that testdata.h uses. */ + +#ifndef PKCS11_TEST_UTIL_H +#define PKCS11_TEST_UTIL_H + +#include + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4101 4189 4505) +#elif defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-variable" + #pragma GCC diagnostic ignored "-Wunused-function" +#endif + +static int test_passed = 0; +static int test_failed = 0; + +/* Check a CK_RV against an expected value, counting the result. */ +#define CHECK_RV(rv, op, expected) do { \ + if ((rv) != (expected)) { \ + fprintf(stderr, "FAIL: %s: expected 0x%lx, got 0x%lx\n", op, \ + (unsigned long)(expected), (unsigned long)(rv)); \ + test_failed++; \ + } else { \ + printf("PASS: %s\n", op); \ + test_passed++; \ + } \ +} while (0) + +/* Check an arbitrary condition, counting the result. */ +#define CHECK_TRUE(cond, op) do { \ + if (!(cond)) { \ + fprintf(stderr, "FAIL: %s\n", op); \ + test_failed++; \ + } else { \ + printf("PASS: %s\n", op); \ + test_passed++; \ + } \ +} while (0) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* funcList; + +/* Load the wolfPKCS11 module and populate funcList. */ +static CK_RV pkcs11_load(void) +{ + CK_RV ret; +#ifndef HAVE_PKCS11_STATIC + CK_C_GetFunctionList func; + + dlib = dlopen(WOLFPKCS11_DLL_FILENAME, RTLD_NOW | RTLD_LOCAL); + if (dlib == NULL) { + fprintf(stderr, "dlopen error: %s\n", dlerror()); + return CKR_GENERAL_ERROR; + } + func = (CK_C_GetFunctionList)dlsym(dlib, "C_GetFunctionList"); + if (func == NULL) { + fprintf(stderr, "Failed to get function list function\n"); + dlclose(dlib); + return CKR_GENERAL_ERROR; + } + ret = func(&funcList); + if (ret != CKR_OK) { + dlclose(dlib); + return ret; + } +#else + ret = C_GetFunctionList(&funcList); + if (ret != CKR_OK) + return ret; +#endif + return CKR_OK; +} + +/* Unload the module. */ +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +/* C_Initialize, pick the first slot and open a R/W session on it. */ +static CK_RV pkcs11_open_session(CK_SESSION_HANDLE* session) +{ + CK_RV rv; + CK_C_INITIALIZE_ARGS args; + CK_SLOT_ID slotList[16]; + CK_ULONG slotCount = sizeof(slotList) / sizeof(slotList[0]); + int sessFlags = CKF_SERIAL_SESSION | CKF_RW_SESSION; + + XMEMSET(&args, 0, sizeof(args)); + args.flags = CKF_OS_LOCKING_OK; + rv = funcList->C_Initialize(&args); + if (rv != CKR_OK) + return rv; + + rv = funcList->C_GetSlotList(CK_TRUE, slotList, &slotCount); + if (rv != CKR_OK) + return rv; + if (slotCount == 0) + return CKR_TOKEN_NOT_PRESENT; + + return funcList->C_OpenSession(slotList[0], sessFlags, NULL, NULL, session); +} + +/* Print the pass/fail summary and return the process exit code. */ +static int pkcs11_test_summary(void) +{ + printf("\n=== Test Results ===\n"); + printf("Tests passed: %d\n", test_passed); + printf("Tests failed: %d\n", test_failed); + if (test_failed == 0) + printf("ALL TESTS PASSED!\n"); + else + printf("SOME TESTS FAILED!\n"); + + return (test_failed == 0) ? 0 : 1; +} + +#if defined(_MSC_VER) + #pragma warning(pop) +#elif defined(__GNUC__) + #pragma GCC diagnostic pop +#endif + +#endif /* PKCS11_TEST_UTIL_H */ diff --git a/tests/pkcs11mtt.c b/tests/pkcs11mtt.c index 6ab2808c..fb8901bf 100644 --- a/tests/pkcs11mtt.c +++ b/tests/pkcs11mtt.c @@ -247,14 +247,17 @@ static CK_RV test_object(void* args) CK_ATTRIBUTE empty[] = { }; #endif CK_ATTRIBUTE keyTypeNull[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, NULL, sizeof(CK_KEY_TYPE) } }; CK_ATTRIBUTE keyTypeZeroLen[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, 0, } }; CK_ULONG badKeyType = -1; CK_ATTRIBUTE keyTypeBadValue[] = { - { CKA_KEY_TYPE, &badKeyType, sizeof(&badKeyType) } + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, + { CKA_KEY_TYPE, &badKeyType, sizeof(badKeyType) } }; CK_ATTRIBUTE keyDataNull[] = { { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, @@ -270,10 +273,12 @@ static CK_RV test_object(void* args) { CKA_CLASS, &secretKeyClass, 0, } }; CK_ATTRIBUTE tokenNull[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_TOKEN, NULL, sizeof(CK_BBOOL) }, }; CK_ATTRIBUTE tokenBadLen[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_TOKEN, &ckTrue, 0 }, }; diff --git a/tests/pkcs11test.c b/tests/pkcs11test.c index d583177e..ec39f2a8 100644 --- a/tests/pkcs11test.c +++ b/tests/pkcs11test.c @@ -2000,14 +2000,17 @@ static CK_RV test_object(void* args) CK_ATTRIBUTE empty[] = { }; #endif CK_ATTRIBUTE keyTypeNull[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, NULL, sizeof(CK_KEY_TYPE) } }; CK_ATTRIBUTE keyTypeZeroLen[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, 0, } }; CK_ULONG badKeyType = -1; CK_ATTRIBUTE keyTypeBadValue[] = { - { CKA_KEY_TYPE, &badKeyType, sizeof(&badKeyType) } + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, + { CKA_KEY_TYPE, &badKeyType, sizeof(badKeyType) } }; CK_ATTRIBUTE keyDataNull[] = { { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, @@ -2023,10 +2026,12 @@ static CK_RV test_object(void* args) { CKA_CLASS, &secretKeyClass, 0, } }; CK_ATTRIBUTE tokenNull[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_TOKEN, NULL, sizeof(CK_BBOOL) }, }; CK_ATTRIBUTE tokenBadLen[] = { + { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType) }, { CKA_TOKEN, &ckTrue, 0 }, }; @@ -4893,7 +4898,7 @@ static CK_RV test_encrypt_decrypt_op_not_supported(void* args) CHECK_CKR(ret, "Create AES key with CKA_ENCRYPT=FALSE"); if (ret == CKR_OK) { ret = funcList->C_EncryptInit(session, &mech, key); - CHECK_CKR_FAIL(ret, CKR_KEY_TYPE_INCONSISTENT, + CHECK_CKR_FAIL(ret, CKR_KEY_FUNCTION_NOT_PERMITTED, "EncryptInit should fail with CKA_ENCRYPT=FALSE"); } @@ -4905,7 +4910,7 @@ static CK_RV test_encrypt_decrypt_op_not_supported(void* args) } if (ret == CKR_OK) { ret = funcList->C_DecryptInit(session, &mech, key); - CHECK_CKR_FAIL(ret, CKR_KEY_TYPE_INCONSISTENT, + CHECK_CKR_FAIL(ret, CKR_KEY_FUNCTION_NOT_PERMITTED, "DecryptInit should fail with CKA_DECRYPT=FALSE"); } @@ -5555,7 +5560,7 @@ static CK_RV test_sign_verify_op_not_supported(void* args) CHECK_CKR(ret, "Create generic key with CKA_SIGN=FALSE"); if (ret == CKR_OK) { ret = funcList->C_SignInit(session, &mech, key); - CHECK_CKR_FAIL(ret, CKR_KEY_TYPE_INCONSISTENT, + CHECK_CKR_FAIL(ret, CKR_KEY_FUNCTION_NOT_PERMITTED, "SignInit should fail with CKA_SIGN=FALSE"); } @@ -5568,7 +5573,7 @@ static CK_RV test_sign_verify_op_not_supported(void* args) } if (ret == CKR_OK) { ret = funcList->C_VerifyInit(session, &mech, key); - CHECK_CKR_FAIL(ret, CKR_KEY_TYPE_INCONSISTENT, + CHECK_CKR_FAIL(ret, CKR_KEY_FUNCTION_NOT_PERMITTED, "VerifyInit should fail with CKA_VERIFY=FALSE"); } @@ -5819,7 +5824,7 @@ static CK_RV test_verify_recover_op_not_supported(void* args) CHECK_CKR(ret, "Create RSA pub key with CKA_VERIFY_RECOVER=FALSE"); if (ret == CKR_OK) { ret = funcList->C_VerifyRecoverInit(session, &mech, pubKey); - CHECK_CKR_FAIL(ret, CKR_KEY_TYPE_INCONSISTENT, + CHECK_CKR_FAIL(ret, CKR_KEY_FUNCTION_NOT_PERMITTED, "VerifyRecoverInit should fail with CKA_VERIFY_RECOVER=FALSE"); } @@ -6447,7 +6452,7 @@ static CK_RV test_wrap_unwrap_op_not_supported(void* args) if (ret == CKR_OK) { ret = funcList->C_WrapKey(session, &mech, noWrapKey, key, wrappedKey, &wrappedKeyLen); - CHECK_CKR_FAIL(ret, CKR_KEY_TYPE_INCONSISTENT, + CHECK_CKR_FAIL(ret, CKR_KEY_FUNCTION_NOT_PERMITTED, "WrapKey should fail with CKA_WRAP=FALSE"); } funcList->C_DestroyObject(session, noWrapKey); @@ -6491,7 +6496,7 @@ static CK_RV test_wrap_unwrap_op_not_supported(void* args) ret = funcList->C_UnwrapKey(session, &mech, noWrapKey, wrappedKey, wrappedKeyLen, unwrapResultTmpl, unwrapResultCnt, &unwrapped); - CHECK_CKR_FAIL(ret, CKR_KEY_TYPE_INCONSISTENT, + CHECK_CKR_FAIL(ret, CKR_KEY_FUNCTION_NOT_PERMITTED, "UnwrapKey should fail with CKA_UNWRAP=FALSE"); } funcList->C_DestroyObject(session, noWrapKey); @@ -17656,7 +17661,7 @@ static CK_RV test_destroy_object_not_destroyable(void* args) } /* CKA_DERIVE=CK_FALSE must cause C_DeriveKey to reject the base key - * (CKR_KEY_TYPE_INCONSISTENT via the existing CheckOpSupported pattern). + * (CKR_KEY_FUNCTION_NOT_PERMITTED via the CheckOpSupported pattern). * The check is skipped on WOLFPKCS11_NSS builds for NSS compatibility. */ #if !defined(NO_DH) && !defined(WOLFPKCS11_NSS) static CK_RV test_derive_key_not_allowed(void* args) @@ -17693,7 +17698,7 @@ static CK_RV test_derive_key_not_allowed(void* args) if (ret == CKR_OK) { ret = funcList->C_DeriveKey(session, &mech, base, outTmpl, outTmplCnt, &secret); - CHECK_CKR_FAIL(ret, CKR_KEY_TYPE_INCONSISTENT, + CHECK_CKR_FAIL(ret, CKR_KEY_FUNCTION_NOT_PERMITTED, "DeriveKey rejected when base CKA_DERIVE=FALSE"); if (secret != CK_INVALID_HANDLE) funcList->C_DestroyObject(session, secret); diff --git a/tests/set_attr_readonly_test.c b/tests/set_attr_readonly_test.c new file mode 100644 index 00000000..a8dac52a --- /dev/null +++ b/tests/set_attr_readonly_test.c @@ -0,0 +1,149 @@ +/* set_attr_readonly_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 + * + * Regression test for issue F-5517: C_SetAttributeValue must reject a change + * to a read-only class/identity or generated-state attribute with + * CKR_ATTRIBUTE_READ_ONLY. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/set_attr_readonly_test" + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_OBJECT_CLASS dataClass = CKO_DATA; + CK_KEY_TYPE genericType = CKK_GENERIC_SECRET; + CK_KEY_TYPE aesType = CKK_AES; + CK_BBOOL ckTrue = CK_TRUE; + CK_BBOOL ckFalse = CK_FALSE; + byte keyData[16] = { 0 }; + char label[] = "relabel"; + CK_ATTRIBUTE keyTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &genericType, sizeof(genericType) }, + { CKA_VALUE, keyData, sizeof(keyData) }, + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + /* Non-sensitive and extractable, so CKA_ALWAYS_SENSITIVE and + * CKA_NEVER_EXTRACTABLE are both CK_FALSE after creation. */ + { CKA_SENSITIVE, &ckFalse, sizeof(ckFalse) }, + { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, + }; + CK_ULONG keyTmplCnt = sizeof(keyTmpl) / sizeof(*keyTmpl); + + CK_ATTRIBUTE setClass[] = { { CKA_CLASS, &dataClass, sizeof(dataClass) } }; + CK_ATTRIBUTE setKeyType[] = { { CKA_KEY_TYPE, &aesType, sizeof(aesType) } }; + CK_ATTRIBUTE setAlwaysSensitive[] = + { { CKA_ALWAYS_SENSITIVE, &ckTrue, sizeof(ckTrue) } }; + CK_ATTRIBUTE setNeverExtractable[] = + { { CKA_NEVER_EXTRACTABLE, &ckTrue, sizeof(ckTrue) } }; + CK_ATTRIBUTE setLabel[] = + { { CKA_LABEL, label, (CK_ULONG)(sizeof(label) - 1) } }; + CK_ATTRIBUTE setSameClass[] = + { { CKA_CLASS, &secretClass, sizeof(secretClass) } }; + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + + rv = funcList->C_CreateObject(session, keyTmpl, keyTmplCnt, &key); + CHECK_RV(rv, "C_CreateObject(secret key)", CKR_OK); + if (rv != CKR_OK) + goto out; + + /* Class/identity attributes are read-only after creation. */ + rv = funcList->C_SetAttributeValue(session, key, setClass, 1); + CHECK_RV(rv, "C_SetAttributeValue(CKA_CLASS)", CKR_ATTRIBUTE_READ_ONLY); + + rv = funcList->C_SetAttributeValue(session, key, setKeyType, 1); + CHECK_RV(rv, "C_SetAttributeValue(CKA_KEY_TYPE)", + CKR_ATTRIBUTE_READ_ONLY); + + /* Generated-state attributes are read-only. */ + rv = funcList->C_SetAttributeValue(session, key, setAlwaysSensitive, 1); + CHECK_RV(rv, "C_SetAttributeValue(CKA_ALWAYS_SENSITIVE)", + CKR_ATTRIBUTE_READ_ONLY); + + rv = funcList->C_SetAttributeValue(session, key, setNeverExtractable, 1); + CHECK_RV(rv, "C_SetAttributeValue(CKA_NEVER_EXTRACTABLE)", + CKR_ATTRIBUTE_READ_ONLY); + + /* A genuinely modifiable attribute still succeeds. */ + rv = funcList->C_SetAttributeValue(session, key, setLabel, 1); + CHECK_RV(rv, "C_SetAttributeValue(CKA_LABEL)", CKR_OK); + + /* Setting a read-only attribute to its current value is a no-op. */ + rv = funcList->C_SetAttributeValue(session, key, setSameClass, 1); + CHECK_RV(rv, "C_SetAttributeValue(CKA_CLASS unchanged)", CKR_OK); + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 C_SetAttributeValue read-only test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/trust_attr_bufsize_test.c b/tests/trust_attr_bufsize_test.c new file mode 100644 index 00000000..8b415c74 --- /dev/null +++ b/tests/trust_attr_bufsize_test.c @@ -0,0 +1,173 @@ +/* trust_attr_bufsize_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 + * + * Regression test for issue F-4060: reading an NSS trust ULONG/BOOL attribute + * (CKA_TRUST_*) into an undersized buffer must report CKR_BUFFER_TOO_SMALL + * rather than overflowing it. NSS-only; skipped otherwise. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/trust_attr_bufsize_test" + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; +#ifdef WOLFPKCS11_NSS + CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; + CK_OBJECT_CLASS trustClass = CKO_NSS_TRUST; + CK_BBOOL ckTrue = CK_TRUE; + static byte issuer[] = "CN=Test,O=wolfSSL,C=US"; + static byte serial[] = { 0x02, 0x05, 0x00, 0xC6, 0xA7, 0x91, 0x84 }; + static byte sha1_hash[20] = { 0 }; + static byte md5_hash[16] = { 0 }; + CK_ULONG trustValue = 0xCE534352; + CK_BBOOL stepUp = CK_FALSE; + CK_ATTRIBUTE tmpl[] = { + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_CLASS, &trustClass, sizeof(trustClass) }, + { CKA_ISSUER, issuer, sizeof(issuer) - 1 }, + { CKA_SERIAL_NUMBER, serial, sizeof(serial) }, + { CKA_CERT_SHA1_HASH, sha1_hash, sizeof(sha1_hash) }, + { CKA_CERT_MD5_HASH, md5_hash, sizeof(md5_hash) }, + { CKA_TRUST_SERVER_AUTH, &trustValue, sizeof(trustValue) }, + { CKA_TRUST_CLIENT_AUTH, &trustValue, sizeof(trustValue) }, + { CKA_TRUST_CODE_SIGNING, &trustValue, sizeof(trustValue) }, + { CKA_TRUST_EMAIL_PROTECTION, &trustValue, sizeof(trustValue) }, + { CKA_TRUST_STEP_UP_APPROVED, &stepUp, sizeof(stepUp) }, + }; + CK_ULONG tmplCnt = sizeof(tmpl) / sizeof(*tmpl); + CK_ATTRIBUTE getAttr; + /* Larger than a CK_ULONG so a canary region exists past the claimed + * length on both 32- and 64-bit builds. */ + byte buffer[2 * sizeof(CK_ULONG)]; + int i; +#endif + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + +#ifndef WOLFPKCS11_NSS + /* Trust objects and the CKA_TRUST_* attributes only exist in NSS builds. */ + printf("SKIP: NSS trust objects not supported in this build\n"); + test_passed++; +#else + rv = funcList->C_CreateObject(session, tmpl, tmplCnt, &obj); + CHECK_RV(rv, "C_CreateObject(NSS trust)", CKR_OK); + if (rv != CKR_OK) + goto out; + + /* Size query reports sizeof(CK_ULONG). */ + getAttr.type = CKA_TRUST_SERVER_AUTH; + getAttr.pValue = NULL; + getAttr.ulValueLen = 0; + rv = funcList->C_GetAttributeValue(session, obj, &getAttr, 1); + CHECK_RV(rv, "size query CKA_TRUST_SERVER_AUTH", CKR_OK); + CHECK_TRUE(getAttr.ulValueLen == sizeof(CK_ULONG), + "size query reports sizeof(CK_ULONG)"); + + /* Undersized buffer: claim fewer bytes than a CK_ULONG and guard the rest + * with a canary. Must report CKR_BUFFER_TOO_SMALL without writing past the + * claimed length. */ + XMEMSET(buffer, 0xAB, sizeof(buffer)); + getAttr.type = CKA_TRUST_SERVER_AUTH; + getAttr.pValue = buffer; + getAttr.ulValueLen = sizeof(CK_ULONG) - 1; + rv = funcList->C_GetAttributeValue(session, obj, &getAttr, 1); + CHECK_RV(rv, "undersized CKA_TRUST_SERVER_AUTH", CKR_BUFFER_TOO_SMALL); + for (i = (int)(sizeof(CK_ULONG) - 1); i < (int)sizeof(buffer); i++) { + if (buffer[i] != 0xAB) + break; + } + CHECK_TRUE(i == (int)sizeof(buffer), + "no write past the caller-declared buffer length"); + + /* Same check for the STEP_UP boolean attribute (GetBool path). */ + XMEMSET(buffer, 0xAB, sizeof(buffer)); + getAttr.type = CKA_TRUST_STEP_UP_APPROVED; + getAttr.pValue = buffer; + getAttr.ulValueLen = 0; + rv = funcList->C_GetAttributeValue(session, obj, &getAttr, 1); + CHECK_RV(rv, "undersized CKA_TRUST_STEP_UP_APPROVED", + CKR_BUFFER_TOO_SMALL); + CHECK_TRUE(buffer[0] == 0xAB, "no write into zero-length bool buffer"); + + /* An adequately sized buffer still returns the value. */ + getAttr.type = CKA_TRUST_SERVER_AUTH; + getAttr.pValue = buffer; + getAttr.ulValueLen = sizeof(buffer); + rv = funcList->C_GetAttributeValue(session, obj, &getAttr, 1); + CHECK_RV(rv, "adequate CKA_TRUST_SERVER_AUTH", CKR_OK); + CHECK_TRUE(getAttr.ulValueLen == sizeof(CK_ULONG), + "adequate buffer returns sizeof(CK_ULONG)"); +#endif + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 NSS trust attribute buffer-size test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/verify_recover_badkey_test.c b/tests/verify_recover_badkey_test.c new file mode 100644 index 00000000..952191f6 --- /dev/null +++ b/tests/verify_recover_badkey_test.c @@ -0,0 +1,102 @@ +/* verify_recover_badkey_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 + * + * Regression test for issue F-3833: C_VerifyRecoverInit must report + * CKR_OBJECT_HANDLE_INVALID for a key handle that does not resolve to an + * object, matching the other C_*Init functions. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/verify_recover_badkey_test" + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; + CK_MECHANISM mech; + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + + XMEMSET(&mech, 0, sizeof(mech)); + mech.mechanism = CKM_RSA_PKCS; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + + /* Non-zero handle that does not resolve to any object. */ + rv = funcList->C_VerifyRecoverInit(session, &mech, + (CK_OBJECT_HANDLE)0x7fffffffUL); + CHECK_RV(rv, "C_VerifyRecoverInit(bad handle)", CKR_OBJECT_HANDLE_INVALID); + + rv = funcList->C_VerifyRecoverInit(session, &mech, (CK_OBJECT_HANDLE)0); + CHECK_RV(rv, "C_VerifyRecoverInit(zero handle)", CKR_OBJECT_HANDLE_INVALID); + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 C_VerifyRecoverInit bad-handle test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/tests/verify_recover_class_test.c b/tests/verify_recover_class_test.c new file mode 100644 index 00000000..2685acf9 --- /dev/null +++ b/tests/verify_recover_class_test.c @@ -0,0 +1,117 @@ +/* verify_recover_class_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 + * + * Regression test for issue F-4067: a handle that resolves to a valid object + * of the wrong CKA_CLASS for C_VerifyRecoverInit must report + * CKR_KEY_TYPE_INCONSISTENT. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include "testdata.h" +#include "pkcs11_test_util.h" + +#define TEST_DIR "./store/verify_recover_class_test" + +static int run_test(void) +{ + CK_RV rv; + CK_SESSION_HANDLE session = 0; + CK_MECHANISM mech; + CK_OBJECT_HANDLE secret = CK_INVALID_HANDLE; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_KEY_TYPE genericType = CKK_GENERIC_SECRET; + CK_BBOOL ckTrue = CK_TRUE; + CK_BBOOL ckFalse = CK_FALSE; + byte keyData[16] = { 0 }; + CK_ATTRIBUTE secretTmpl[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &genericType, sizeof(genericType) }, + { CKA_VALUE, keyData, sizeof(keyData) }, + { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, + /* Public so the object is reachable without C_Login. */ + { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, + }; + CK_ULONG secretTmplCnt = sizeof(secretTmpl) / sizeof(*secretTmpl); + + rv = pkcs11_load(); + CHECK_RV(rv, "load library", CKR_OK); + if (rv != CKR_OK) + return -1; + + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); + if (rv != CKR_OK) + goto out; + + rv = funcList->C_CreateObject(session, secretTmpl, secretTmplCnt, &secret); + CHECK_RV(rv, "C_CreateObject(secret key)", CKR_OK); + if (rv != CKR_OK) + goto out; + + XMEMSET(&mech, 0, sizeof(mech)); + mech.mechanism = CKM_RSA_PKCS; + + /* A secret key is the wrong object class for verify-recover. */ + rv = funcList->C_VerifyRecoverInit(session, &mech, secret); + CHECK_RV(rv, "C_VerifyRecoverInit(secret key handle)", + CKR_KEY_TYPE_INCONSISTENT); + +out: + if (session != 0) + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); + pkcs11_unload(); + return 0; +} + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + +#ifndef WOLFPKCS11_NO_ENV + XSETENV("WOLFPKCS11_TOKEN_PATH", TEST_DIR, 1); +#endif + + printf("=== wolfPKCS11 C_VerifyRecoverInit wrong-class test ===\n"); + run_test(); + return pkcs11_test_summary(); +} diff --git a/wolfpkcs11/internal.h b/wolfpkcs11/internal.h index 7e8c56b9..4a6b6342 100644 --- a/wolfpkcs11/internal.h +++ b/wolfpkcs11/internal.h @@ -352,6 +352,7 @@ C_EXTRA_FLAGS="-DWOLFSSL_PUBLIC_MP -DWC_RSA_DIRECT" #define OBJ_COUNT_E -10 #define OBJ_TYPE_E -11 #define PARAM_E -12 +#define LOGGED_IN_ANOTHER_E -13 typedef struct WP11_Object WP11_Object;