From 2cd940571822a406d14b0ae5efd9002647b705b7 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 15:31:58 +0000 Subject: [PATCH 01/19] Fix C_VerifyRecoverInit invalid-handle return code (F-3833) WP11_Object_Find returns BAD_FUNC_ARG when the key handle does not match any object. C_VerifyRecoverInit forwarded that raw negative wolfCrypt code directly, casting it to CK_RV and yielding an undefined return value. Return CKR_OBJECT_HANDLE_INVALID instead, matching the other C_*Init functions and the PKCS#11 specification. Add tests/verify_recover_badkey_test.c regression test. --- src/crypto.c | 7 +- tests/include.am | 7 ++ tests/verify_recover_badkey_test.c | 192 +++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 tests/verify_recover_badkey_test.c diff --git a/src/crypto.c b/src/crypto.c index db1cd504..7a2ce5c0 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -6632,8 +6632,11 @@ 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; diff --git a/tests/include.am b/tests/include.am index df786c44..d2ef4709 100644 --- a/tests/include.am +++ b/tests/include.am @@ -91,6 +91,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -110,6 +115,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -121,6 +127,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ diff --git a/tests/verify_recover_badkey_test.c b/tests/verify_recover_badkey_test.c new file mode 100644 index 00000000..caf92850 --- /dev/null +++ b/tests/verify_recover_badkey_test.c @@ -0,0 +1,192 @@ +/* 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 used to forward the + * raw wolfCrypt error (BAD_FUNC_ARG) returned by WP11_Object_Find when the + * supplied key handle did not match any object, casting a negative int to + * CK_RV. The spec-defined code for a non-resolvable handle is + * CKR_OBJECT_HANDLE_INVALID, matching every other C_*Init function. + */ + +#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" + +#define TEST_DIR "./store/verify_recover_badkey_test" + +static int test_passed = 0; +static int test_failed = 0; + +#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) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* 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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +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; + CK_MECHANISM mech; + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", 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. Must report + * CKR_OBJECT_HANDLE_INVALID rather than a raw negative wolfCrypt code. */ + rv = funcList->C_VerifyRecoverInit(session, &mech, + (CK_OBJECT_HANDLE)0x7fffffffUL); + CHECK_RV(rv, "C_VerifyRecoverInit(bad handle)", CKR_OBJECT_HANDLE_INVALID); + + /* Sanity: the zero handle path already reports the same code. */ + 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(); + + 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; +} From 781e1aede63b63c70c3370d9ca5847a4e6d15c4a Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 15:34:58 +0000 Subject: [PATCH 02/19] Propagate CKR_PIN_LEN_RANGE from C_Login (F-3834) C_Login discarded the checkPinLen result and substituted CKR_PIN_INCORRECT for an out-of-range PIN length. Propagate the checkPinLen return value so CKR_PIN_LEN_RANGE reaches the caller, matching C_InitToken, C_InitPIN and C_SetPIN. Add tests/login_pin_len_range_test.c regression test. --- src/slot.c | 4 +- tests/include.am | 7 ++ tests/login_pin_len_range_test.c | 191 +++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 tests/login_pin_len_range_test.c diff --git a/src/slot.c b/src/slot.c index 6efc8dc9..5d55c605 100644 --- a/src/slot.c +++ b/src/slot.c @@ -1862,8 +1862,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; } diff --git a/tests/include.am b/tests/include.am index d2ef4709..d66757e6 100644 --- a/tests/include.am +++ b/tests/include.am @@ -96,6 +96,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -116,6 +121,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -128,6 +134,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ diff --git a/tests/login_pin_len_range_test.c b/tests/login_pin_len_range_test.c new file mode 100644 index 00000000..9671dcfd --- /dev/null +++ b/tests/login_pin_len_range_test.c @@ -0,0 +1,191 @@ +/* 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. C_Login used to discard the + * CKR_PIN_LEN_RANGE result of checkPinLen and substitute CKR_PIN_INCORRECT. + * A 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" + +#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 test_passed = 0; +static int test_failed = 0; + +#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) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* 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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +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; + CK_ULONG pinLen = (CK_ULONG)XSTRLEN(tooLongPin); + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", CKR_OK); + if (rv != CKR_OK) + goto out; + + /* Out-of-range PIN length must surface as CKR_PIN_LEN_RANGE, not + * CKR_PIN_INCORRECT. Tested for both user types. */ + 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(); + + 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; +} From 93578aa0814ee26f5539a99604365dd1bdbf42c9 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 15:38:44 +0000 Subject: [PATCH 03/19] Return CKR_KEY_TYPE_INCONSISTENT for wrong-class key in C_VerifyRecoverInit (F-4067) When C_VerifyRecoverInit resolved a handle to a valid object whose CKA_CLASS was not CKO_PUBLIC_KEY it returned CKR_KEY_HANDLE_INVALID, which the spec reserves for handles that do not refer to keys. Return CKR_KEY_TYPE_INCONSISTENT for a key of the wrong object class. Add tests/verify_recover_class_test.c regression test. --- src/crypto.c | 2 +- tests/include.am | 7 + tests/verify_recover_class_test.c | 207 ++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 tests/verify_recover_class_test.c diff --git a/src/crypto.c b/src/crypto.c index 7a2ce5c0..39820b08 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -6639,7 +6639,7 @@ CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, } 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) { diff --git a/tests/include.am b/tests/include.am index d66757e6..420e63ea 100644 --- a/tests/include.am +++ b/tests/include.am @@ -101,6 +101,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -122,6 +127,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -135,6 +141,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ diff --git a/tests/verify_recover_class_test.c b/tests/verify_recover_class_test.c new file mode 100644 index 00000000..4f0e19ca --- /dev/null +++ b/tests/verify_recover_class_test.c @@ -0,0 +1,207 @@ +/* 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. When C_VerifyRecoverInit resolves a + * handle to a valid object whose CKA_CLASS is not CKO_PUBLIC_KEY it used to + * return CKR_KEY_HANDLE_INVALID (reserved for handles that do not refer to + * keys). A key of the wrong object class 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" + +#define TEST_DIR "./store/verify_recover_class_test" + +static int test_passed = 0; +static int test_failed = 0; + +#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) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* 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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +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; + 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 object so it 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; + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", 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; + + /* The handle resolves to a secret key, not a public key. Wrong object + * class for verify-recover must report CKR_KEY_TYPE_INCONSISTENT, not + * CKR_KEY_HANDLE_INVALID. */ + 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(); + + 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; +} From 65819e471417fb21745d529e57fd1f375b0c3442 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 15:42:13 +0000 Subject: [PATCH 04/19] Distinguish CKR_USER_ANOTHER_ALREADY_LOGGED_IN in C_Login (F-4527) WP11_Slot_SOLogin and WP11_Slot_UserLogin returned LOGGED_IN_E for any already-logged-in state, so C_Login always reported CKR_USER_ALREADY_LOGGED_IN. Add LOGGED_IN_ANOTHER_E for the case where a different user type is logged in and map it to CKR_USER_ANOTHER_ALREADY_LOGGED_IN, keeping CKR_USER_ALREADY_LOGGED_IN for the same user type. Add tests/login_another_user_test.c regression test. --- src/internal.c | 20 ++- src/slot.c | 3 + tests/include.am | 7 + tests/login_another_user_test.c | 239 ++++++++++++++++++++++++++++++++ wolfpkcs11/internal.h | 2 + 5 files changed, 265 insertions(+), 6 deletions(-) create mode 100644 tests/login_another_user_test.c diff --git a/src/internal.c b/src/internal.c index b27c56be..9664b4cc 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? */ + /* Have we already logged in? Distinguish the same user type (SO) + * from a different one (USER) for the caller's return code. */ 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. */ @@ -7397,12 +7401,16 @@ int WP11_Slot_UserLogin(WP11_Slot* slot, char* pin, int pinLen) WP11_Lock_LockRW(&slot->lock); /* Have we already logged in? */ if (ret == 0) { - /* Have we already logged in? */ + /* Have we already logged in? Distinguish the same user type (USER) + * from a different one (SO) for the caller's return code. */ 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 */ diff --git a/src/slot.c b/src/slot.c index 5d55c605..77cfd188 100644 --- a/src/slot.c +++ b/src/slot.c @@ -1890,6 +1890,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/include.am b/tests/include.am index 420e63ea..5b7a1ae8 100644 --- a/tests/include.am +++ b/tests/include.am @@ -106,6 +106,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -128,6 +133,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -142,6 +148,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ diff --git a/tests/login_another_user_test.c b/tests/login_another_user_test.c new file mode 100644 index 00000000..56a6bd75 --- /dev/null +++ b/tests/login_another_user_test.c @@ -0,0 +1,239 @@ +/* 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 conflated the two + * already-logged-in cases: logging in while the *same* user type is logged + * in must return CKR_USER_ALREADY_LOGGED_IN, while logging in while a + * *different* user type is logged in must return + * CKR_USER_ANOTHER_ALREADY_LOGGED_IN. Pre-fix every case returned + * CKR_USER_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" + +#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 int test_passed = 0; +static int test_failed = 0; + +#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) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* 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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +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); + + 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]; + + /* Initialize the token (sets the SO PIN) before opening a 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 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); + + /* USER login while SO is logged in is a *different* type. */ + 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) so phase B can log in as user. */ + 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 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); + + /* SO login while USER is logged in is a *different* type. */ + 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(); + + 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; +} diff --git a/wolfpkcs11/internal.h b/wolfpkcs11/internal.h index 7e8c56b9..057da55c 100644 --- a/wolfpkcs11/internal.h +++ b/wolfpkcs11/internal.h @@ -352,6 +352,8 @@ C_EXTRA_FLAGS="-DWOLFSSL_PUBLIC_MP -DWC_RSA_DIRECT" #define OBJ_COUNT_E -10 #define OBJ_TYPE_E -11 #define PARAM_E -12 +/* A different user type is already logged in. */ +#define LOGGED_IN_ANOTHER_E -13 typedef struct WP11_Object WP11_Object; From ed77edf999aaf90aad0d74c93a61aa332ba3f481 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 15:49:34 +0000 Subject: [PATCH 05/19] Return CKR_KEY_FUNCTION_NOT_PERMITTED for usage-denied keys (F-6052) CheckOpSupported returned CKR_KEY_TYPE_INCONSISTENT when a key's CKA_ usage attribute was CK_FALSE. PKCS#11 specifies CKR_KEY_FUNCTION_NOT_PERMITTED for that case and reserves CKR_KEY_TYPE_INCONSISTENT for a key type that does not match the mechanism (handled by the per-mechanism checks). This affects C_EncryptInit, C_DecryptInit, C_SignInit, C_VerifyInit, C_VerifyRecoverInit, C_WrapKey, C_UnwrapKey and C_DeriveKey. Update the existing pkcs11test.c usage-denied assertions, which encoded the old code, and add tests/key_function_not_permitted_test.c. --- src/crypto.c | 6 +- tests/include.am | 7 + tests/key_function_not_permitted_test.c | 214 ++++++++++++++++++++++++ tests/pkcs11test.c | 18 +- 4 files changed, 235 insertions(+), 10 deletions(-) create mode 100644 tests/key_function_not_permitted_test.c diff --git a/src/crypto.c b/src/crypto.c index 39820b08..7b776327 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -480,7 +480,11 @@ 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); + /* A key whose CKA_ usage attribute does not allow the operation is + * reported as CKR_KEY_FUNCTION_NOT_PERMITTED. CKR_KEY_TYPE_INCONSISTENT + * is reserved for a key type that does not match the mechanism, which the + * per-mechanism checks handle. */ + return CheckOpSupportedRv(obj, op, CKR_KEY_FUNCTION_NOT_PERMITTED); } static CK_RV SetInitialStates(WP11_Object* key) diff --git a/tests/include.am b/tests/include.am index 5b7a1ae8..929e7540 100644 --- a/tests/include.am +++ b/tests/include.am @@ -111,6 +111,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -134,6 +139,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -149,6 +155,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ diff --git a/tests/key_function_not_permitted_test.c b/tests/key_function_not_permitted_test.c new file mode 100644 index 00000000..85c5f541 --- /dev/null +++ b/tests/key_function_not_permitted_test.c @@ -0,0 +1,214 @@ +/* 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. CheckOpSupported denied an operation + * whose CKA_ usage attribute was CK_FALSE with CKR_KEY_TYPE_INCONSISTENT. + * PKCS#11 mandates CKR_KEY_FUNCTION_NOT_PERMITTED for a key whose usage + * attribute does not allow the operation; CKR_KEY_TYPE_INCONSISTENT is for a + * key type that does not match the mechanism. + */ + +#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" + +#define TEST_DIR "./store/key_function_not_permitted_test" + +static int test_passed = 0; +static int test_failed = 0; + +#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) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* 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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +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; + 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) }, + /* Public object so it is reachable without C_Login. */ + { 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; + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", 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); + + /* Correct key type, but CKA_ENCRYPT is CK_FALSE: the operation is not + * permitted, which is distinct from a key-type mismatch. */ + 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(); + + 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; +} diff --git a/tests/pkcs11test.c b/tests/pkcs11test.c index d583177e..cef9887e 100644 --- a/tests/pkcs11test.c +++ b/tests/pkcs11test.c @@ -4893,7 +4893,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 +4905,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 +5555,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 +5568,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 +5819,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 +6447,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 +6491,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 +17656,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 +17693,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); From 9db4559cb4f60c5eb3152a2ae3d0a03efe68e369 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 16:09:23 +0000 Subject: [PATCH 06/19] Enforce output buffer size in C_DecryptFinal CBC-PAD branch (F-6050) The CKM_AES_CBC_PAD branch of C_DecryptFinal passed WP11_AesCbcPad_DecryptFinal the buffered block length (always 16) as the output capacity instead of the caller's *pulLastPartLen, so the helper's BUFFER_E guard could never fire and an undersized caller buffer was overflowed by up to 15 bytes. Pass the caller's actual capacity, like the single-shot C_Decrypt path, so a too-small buffer yields CKR_BUFFER_TOO_SMALL. The size query now reports AES_BLOCK_SIZE - 1. Add tests/decrypt_final_bufsize_test.c regression test. --- src/crypto.c | 14 +- tests/decrypt_final_bufsize_test.c | 289 +++++++++++++++++++++++++++++ tests/include.am | 7 + 3 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 tests/decrypt_final_bufsize_test.c diff --git a/src/crypto.c b/src/crypto.c index 7b776327..4cb7a5b9 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -4069,10 +4069,20 @@ 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 (not the buffered block + * length, which is always 16) so WP11_AesCbcPad_DecryptFinal + * validates the write against it and returns BUFFER_E rather than + * overflowing a too-small buffer, mirroring the single-shot + * C_Decrypt path. Cap to the block size, which already exceeds the + * 0..15 byte 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. */ diff --git a/tests/decrypt_final_bufsize_test.c b/tests/decrypt_final_bufsize_test.c new file mode 100644 index 00000000..f40646a2 --- /dev/null +++ b/tests/decrypt_final_bufsize_test.c @@ -0,0 +1,289 @@ +/* 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 handed WP11_AesCbcPad_DecryptFinal the buffered block length + * (16) instead of the caller's output-buffer capacity, so the helper's + * BUFFER_E guard could never fire and an undersized caller buffer was + * overflowed by up to 15 bytes. C_DecryptFinal must return + * CKR_BUFFER_TOO_SMALL for a too-small buffer like the GCM/CTS branches. + */ + +#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" + +#define TEST_DIR "./store/decrypt_final_bufsize_test" + +static int test_passed = 0; +static int test_failed = 0; + +#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) + +#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; + +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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +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; + 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; the held-back final block + * unpads to 15 bytes, the maximum, maximising the overflow window. */ + 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; + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", 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); + + /* Encrypt: 15 bytes -> 16 bytes ciphertext. */ + 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 complete multi-part decrypt with an adequate final buffer + * still round-trips the plaintext after the fix. */ + 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"); + + /* Fresh decrypt that supplies an undersized final buffer: claim only 4 + * bytes of capacity and guard the rest with a canary. C_DecryptFinal must + * return CKR_BUFFER_TOO_SMALL without writing past the 4 bytes. Pre-fix it + * wrote up to 15 bytes and returned CKR_OK, overflowing the buffer. */ + 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(); + + 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; +} diff --git a/tests/include.am b/tests/include.am index 929e7540..6b208b9c 100644 --- a/tests/include.am +++ b/tests/include.am @@ -116,6 +116,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -140,6 +145,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -156,6 +162,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ From df872702b132b6bb339dd153e1f4834a59d660a3 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 16:17:39 +0000 Subject: [PATCH 07/19] Require CKA_CLASS in C_CreateObject templates (F-5513, F-5516) CreateObject initialised objectClass to the (CK_OBJECT_CLASS)-1 sentinel and only updated it when CKA_CLASS was present. A key template that omitted CKA_CLASS fell into the key-object path and created an object whose CKA_CLASS was the invalid sentinel value. Reject a C_CreateObject template without CKA_CLASS with CKR_TEMPLATE_INCOMPLETE. Existing negative tests that omitted CKA_CLASS relied on falling through to the key/token attribute checks; give them a valid CKA_CLASS so they still exercise those checks. Add tests/create_object_class_test.c regression test. --- src/crypto.c | 16 ++- tests/create_object_class_test.c | 208 +++++++++++++++++++++++++++++++ tests/include.am | 7 ++ tests/pkcs11mtt.c | 5 + tests/pkcs11test.c | 5 + 5 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 tests/create_object_class_test.c diff --git a/src/crypto.c b/src/crypto.c index 4cb7a5b9..9b7f9935 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -1376,8 +1376,20 @@ 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 in the template; there is no + * API-implied object class as there is for derive/unwrap. Reject an + * incomplete template rather than creating an object whose class is the + * uninitialized sentinel value. */ + { + CK_ATTRIBUTE* classAttr = NULL; + 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) { diff --git a/tests/create_object_class_test.c b/tests/create_object_class_test.c new file mode 100644 index 00000000..55b680b0 --- /dev/null +++ b/tests/create_object_class_test.c @@ -0,0 +1,208 @@ +/* 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. CreateObject left objectClass + * at the (CK_OBJECT_CLASS)-1 sentinel when CKA_CLASS was omitted and then + * treated the template as a key object, creating an object whose CKA_CLASS is + * not a valid PKCS#11 class. A complete key template that omits only + * 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" + +#define TEST_DIR "./store/create_object_class_test" + +static int test_passed = 0; +static int test_failed = 0; + +#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) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* 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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +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; + 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 }; + + /* Complete key material, but no CKA_CLASS. Pre-fix this created an + * object with an invalid (sentinel) class. */ + 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); + + /* Same template with CKA_CLASS supplied: must succeed. */ + 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; + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", CKR_OK); + if (rv != CKR_OK) + goto out; + + /* Key template missing only CKA_CLASS must be rejected. */ + rv = funcList->C_CreateObject(session, noClassTmpl, noClassTmplCnt, &obj); + CHECK_RV(rv, "C_CreateObject(no CKA_CLASS)", CKR_TEMPLATE_INCOMPLETE); + + /* The same template with CKA_CLASS supplied still succeeds. */ + 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(); + + 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; +} diff --git a/tests/include.am b/tests/include.am index 6b208b9c..77aee11b 100644 --- a/tests/include.am +++ b/tests/include.am @@ -121,6 +121,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -146,6 +151,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -163,6 +169,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ diff --git a/tests/pkcs11mtt.c b/tests/pkcs11mtt.c index 6ab2808c..99bef0ae 100644 --- a/tests/pkcs11mtt.c +++ b/tests/pkcs11mtt.c @@ -247,13 +247,16 @@ 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_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &badKeyType, sizeof(&badKeyType) } }; CK_ATTRIBUTE keyDataNull[] = { @@ -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 cef9887e..5b5ae352 100644 --- a/tests/pkcs11test.c +++ b/tests/pkcs11test.c @@ -2000,13 +2000,16 @@ 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_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, { CKA_KEY_TYPE, &badKeyType, sizeof(&badKeyType) } }; CK_ATTRIBUTE keyDataNull[] = { @@ -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 }, }; From 953867006bb0d8ca01ffb52b348f188c11c44103 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 16:22:29 +0000 Subject: [PATCH 08/19] Reject mismatched AAD pointer/length for AES-GCM and AES-CCM (F-5514) WP11_Session_SetGcmParams and WP11_Session_SetCcmParams validated the IV pointer/length agreement but not the AAD. With aad == NULL and aadLen > 0 they returned success and stored a zero-length AAD, so C_EncryptInit / C_DecryptInit accepted malformed parameters. Apply the same pointer/length agreement check to the AAD, which the callers map to CKR_MECHANISM_PARAM_INVALID. Add tests/aead_null_aad_test.c regression test. --- src/internal.c | 10 ++ tests/aead_null_aad_test.c | 259 +++++++++++++++++++++++++++++++++++++ tests/include.am | 7 + 3 files changed, 276 insertions(+) create mode 100644 tests/aead_null_aad_test.c diff --git a/src/internal.c b/src/internal.c index 9664b4cc..ee202cf7 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8533,6 +8533,11 @@ 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; + /* The AAD pointer and length must agree by the same rule, so a NULL AAD + * with a non-zero length is rejected rather than silently treated as + * empty AAD. */ + if (ret == 0 && (aad == NULL) != (aadLen == 0)) + ret = BAD_FUNC_ARG; if (ret == 0) { if (session->mechanism == CKM_AES_GCM && gcm->aad != NULL) { @@ -8589,6 +8594,11 @@ 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; + /* The AAD pointer and length must agree by the same rule, so a NULL AAD + * with a non-zero length is rejected rather than silently treated as + * empty AAD. */ + if (ret == 0 && (aad == NULL) != (aadSz == 0)) + ret = BAD_FUNC_ARG; if (ret == 0) { if (session->mechanism == CKM_AES_CCM && ccm->aad != NULL) { diff --git a/tests/aead_null_aad_test.c b/tests/aead_null_aad_test.c new file mode 100644 index 00000000..6fd765e9 --- /dev/null +++ b/tests/aead_null_aad_test.c @@ -0,0 +1,259 @@ +/* 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. WP11_Session_SetGcmParams and + * WP11_Session_SetCcmParams validated the IV pointer/length agreement but not + * the AAD pointer/length agreement, so C_EncryptInit with pAAD == NULL and + * ulAADLen > 0 was silently accepted. A mismatched AAD pointer/length must be + * rejected 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" + +#define TEST_DIR "./store/aead_null_aad_test" + +static int test_passed = 0; +static int test_failed = 0; + +#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) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* 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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +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; + 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; + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", 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; + + /* AES-GCM: NULL AAD pointer with a non-zero AAD length is a contract + * violation and must be rejected. (Failed init leaves no active op.) */ + 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); + CHECK_RV(rv, "C_EncryptInit GCM (NULL AAD, len 16)", + CKR_MECHANISM_PARAM_INVALID); + + /* AES-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); + } + + /* The canonical no-AAD form (NULL pointer, zero length) is still 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); + } + +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(); + + 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; +} diff --git a/tests/include.am b/tests/include.am index 77aee11b..95dbd926 100644 --- a/tests/include.am +++ b/tests/include.am @@ -126,6 +126,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -152,6 +157,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -170,6 +176,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ From c4f3033ba4103ec8c899da09f187fdc71b60526d Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 16:26:27 +0000 Subject: [PATCH 09/19] Validate required public-key attributes in C_GenerateKeyPair (F-5518) C_GenerateKeyPair built the key objects and invoked the mechanism generator without first checking the mechanism's required public-key template attributes. A missing CKA_MODULUS_BITS surfaced as CKR_FUNCTION_FAILED, and a missing CKA_EC_PARAMS dereferenced a NULL domain-parameter pointer. Require CKA_MODULUS_BITS for RSA, CKA_EC_PARAMS for EC and CKA_PRIME/CKA_BASE for DH, returning CKR_TEMPLATE_INCOMPLETE. Add tests/gen_keypair_incomplete_test.c regression test. --- src/crypto.c | 35 ++++ tests/gen_keypair_incomplete_test.c | 237 ++++++++++++++++++++++++++++ tests/include.am | 7 + 3 files changed, 279 insertions(+) create mode 100644 tests/gen_keypair_incomplete_test.c diff --git a/src/crypto.c b/src/crypto.c index 9b7f9935..0690a97b 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -7585,6 +7585,16 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, return CKR_MECHANISM_PARAM_INVALID; } + /* CKA_MODULUS_BITS is required to size the RSA key. */ + { + CK_ATTRIBUTE* reqAttr = NULL; + 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, @@ -7609,6 +7619,17 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, return CKR_MECHANISM_PARAM_INVALID; } + /* CKA_EC_PARAMS carries the curve; without it there are no domain + * parameters to generate against. */ + { + CK_ATTRIBUTE* reqAttr = NULL; + 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, @@ -7746,6 +7767,20 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, return CKR_MECHANISM_PARAM_INVALID; } + /* DH needs the domain parameters CKA_PRIME and CKA_BASE. */ + { + CK_ATTRIBUTE* primeAttr = NULL; + CK_ATTRIBUTE* baseAttr = NULL; + FindAttributeType(pPublicKeyTemplate, + ulPublicKeyAttributeCount, CKA_PRIME, + &primeAttr); + FindAttributeType(pPublicKeyTemplate, + ulPublicKeyAttributeCount, CKA_BASE, + &baseAttr); + if (primeAttr == NULL || baseAttr == NULL) + return CKR_TEMPLATE_INCOMPLETE; + } + *phPublicKey = *phPrivateKey = CK_INVALID_HANDLE; rv = NewObject(session, CKK_DH, CKO_PUBLIC_KEY, pPublicKeyTemplate, diff --git a/tests/gen_keypair_incomplete_test.c b/tests/gen_keypair_incomplete_test.c new file mode 100644 index 00000000..1e8ae2c2 --- /dev/null +++ b/tests/gen_keypair_incomplete_test.c @@ -0,0 +1,237 @@ +/* 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 built the key objects + * and ran the mechanism generator without first checking the mechanism's + * required public-key template attributes, so a missing CKA_MODULUS_BITS + * (RSA), CKA_EC_PARAMS (EC) or CKA_PRIME/CKA_BASE (DH) surfaced as + * CKR_FUNCTION_FAILED instead of 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" + +#define TEST_DIR "./store/gen_keypair_incomplete_test" + +static int test_passed = 0; +static int test_failed = 0; + +#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) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* 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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +/* Generate a key pair with the supplied public template and report the rv. + * A CK_FALSE CKA_PRIVATE on the private template keeps the objects public so + * the call does not require C_Login. The result must be CKR_TEMPLATE_INCOMPLETE + * when the public template is missing a mechanism-required attribute, or + * CKR_MECHANISM_INVALID if the mechanism is not built in (treated as skip). */ +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_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; + CK_BBOOL ckTrue = CK_TRUE; + /* RSA public template missing CKA_MODULUS_BITS. */ + CK_ATTRIBUTE rsaPub[] = { + { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, + { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, + }; + /* EC public template missing CKA_EC_PARAMS. */ + CK_ATTRIBUTE ecPub[] = { + { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, + }; + /* DH public template missing CKA_PRIME and CKA_BASE. */ + CK_ATTRIBUTE dhPub[] = { + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, + }; + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", 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(); + + 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; +} diff --git a/tests/include.am b/tests/include.am index 95dbd926..7ad3f426 100644 --- a/tests/include.am +++ b/tests/include.am @@ -131,6 +131,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -158,6 +163,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -177,6 +183,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ From 9cf2dd57c65053002a83ee73064b3537b081313a Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 16:32:05 +0000 Subject: [PATCH 10/19] Reject changes to read-only attributes in C_SetAttributeValue (F-5517) SetAttributeValue forwarded every template attribute to WP11_Object_SetAttr, which accepted updates to attributes PKCS#11 marks read-only once the object exists: CKA_CLASS, CKA_KEY_TYPE, CKA_LOCAL, CKA_KEY_GEN_MECHANISM, CKA_ALWAYS_SENSITIVE and CKA_NEVER_EXTRACTABLE. Reject an attempt to change any of them with CKR_ATTRIBUTE_READ_ONLY. A set to the current value remains a no-op so C_CopyObject, which funnels through the same helper, is unaffected. Add tests/set_attr_readonly_test.c regression test. --- src/crypto.c | 24 ++++ tests/include.am | 7 + tests/set_attr_readonly_test.c | 238 +++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 tests/set_attr_readonly_test.c diff --git a/src/crypto.c b/src/crypto.c index 0690a97b..e8557ff8 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -969,6 +969,30 @@ static CK_RV SetAttributeValue(WP11_Session* session, WP11_Object* obj, *(CK_BBOOL*)attr->pValue == CK_TRUE) return CKR_ATTRIBUTE_READ_ONLY; } + /* PKCS#11 marks these class/identity and generated-state attributes + * read-only once the object exists. Reject an attempt to change them + * (via C_SetAttributeValue or C_CopyObject); setting the current value + * is a harmless no-op. Not applied during object creation. */ + if (!newObject) { + static const CK_ATTRIBUTE_TYPE readOnlyAttrs[] = { + CKA_CLASS, CKA_KEY_TYPE, CKA_LOCAL, CKA_KEY_GEN_MECHANISM, + CKA_ALWAYS_SENSITIVE, CKA_NEVER_EXTRACTABLE + }; + unsigned int k; + for (k = 0; k < sizeof(readOnlyAttrs) / sizeof(readOnlyAttrs[0]); + k++) { + byte cur[sizeof(CK_ULONG)]; + CK_ULONG curLen = sizeof(cur); + if (attr->type != readOnlyAttrs[k]) + continue; + if (WP11_Object_GetAttr(obj, attr->type, cur, &curLen) == 0 && + (attr->pValue == NULL || attr->ulValueLen != curLen || + XMEMCMP(attr->pValue, cur, curLen) != 0)) { + return CKR_ATTRIBUTE_READ_ONLY; + } + break; + } + } ret = WP11_Object_SetAttr(obj, attr->type, (byte*)attr->pValue, attr->ulValueLen); if (ret == MEMORY_E) diff --git a/tests/include.am b/tests/include.am index 7ad3f426..8aca1198 100644 --- a/tests/include.am +++ b/tests/include.am @@ -136,6 +136,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -164,6 +169,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -184,6 +190,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ diff --git a/tests/set_attr_readonly_test.c b/tests/set_attr_readonly_test.c new file mode 100644 index 00000000..d7613cd4 --- /dev/null +++ b/tests/set_attr_readonly_test.c @@ -0,0 +1,238 @@ +/* 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. SetAttributeValue forwarded every + * template attribute to WP11_Object_SetAttr, which accepted updates to + * read-only class/identity and generated-state attributes (CKA_CLASS, + * CKA_KEY_TYPE, CKA_ALWAYS_SENSITIVE, CKA_NEVER_EXTRACTABLE, ...). Changing + * those via C_SetAttributeValue must fail 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" + +#define TEST_DIR "./store/set_attr_readonly_test" + +static int test_passed = 0; +static int test_failed = 0; + +#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) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* 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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +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; + 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, extractable: 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; + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", 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; + + /* Changing class/identity attributes must be rejected. */ + 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); + + /* Changing generated-state attributes must be rejected. */ + 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, not a + * change, so it is allowed (this is what C_CopyObject relies on). */ + 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(); + + 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; +} From 73d170f544f7522a5b8933a55ec4e531f966397c Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 16:36:55 +0000 Subject: [PATCH 11/19] Validate base key type against derivation mechanism in C_DeriveKey (F-4065) C_DeriveKey passed the base key straight to the mechanism-specific derive routine without checking that its key type matched the mechanism, so a wrong base key type failed deep in the crypto (CKR_FUNCTION_FAILED, or a NULL dereference) instead of the spec-mandated CKR_KEY_TYPE_INCONSISTENT. Require CKK_EC for CKM_ECDH1_DERIVE, CKK_DH for CKM_DH_PKCS_DERIVE, CKK_AES for CKM_AES_CBC_ENCRYPT_DATA, CKK_HKDF/CKK_GENERIC_SECRET for the HKDF mechanisms and CKK_GENERIC_SECRET for the TLS 1.2 mechanisms. Add tests/derive_key_type_test.c regression test. --- src/crypto.c | 13 ++ tests/derive_key_type_test.c | 250 +++++++++++++++++++++++++++++++++++ tests/include.am | 7 + 3 files changed, 270 insertions(+) create mode 100644 tests/derive_key_type_test.c diff --git a/src/crypto.c b/src/crypto.c index e8557ff8..69bcc43e 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -8703,6 +8703,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)) @@ -8735,6 +8737,9 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, CK_HKDF_PARAMS_PTR kdfParams; CK_ATTRIBUTE *lenAttr = NULL; + if (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)) @@ -8777,6 +8782,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) @@ -8799,6 +8806,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 != @@ -8826,6 +8835,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 != @@ -8891,6 +8902,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/tests/derive_key_type_test.c b/tests/derive_key_type_test.c new file mode 100644 index 00000000..8c5be751 --- /dev/null +++ b/tests/derive_key_type_test.c @@ -0,0 +1,250 @@ +/* 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 passed the base key to the + * mechanism-specific derive routine without checking that its key type + * matched the mechanism, so a wrong base key type failed deep in the crypto + * with CKR_FUNCTION_FAILED instead of the spec-mandated + * 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" + +#define TEST_DIR "./store/derive_key_type_test" + +static int test_passed = 0; +static int test_failed = 0; + +#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) + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* 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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +/* Derive with the given mechanism using the wrong-typed base key and assert + * CKR_KEY_TYPE_INCONSISTENT, or skip 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); +} + +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; + 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; + /* An AES key is the wrong base-key type for every asymmetric/KDF derive + * mechanism below. CKA_DERIVE=TRUE so the usage gate is passed and the + * per-mechanism type check is the thing under test. */ + 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; + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", 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"); + +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(); + + 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; +} diff --git a/tests/include.am b/tests/include.am index 8aca1198..dfa3b9bf 100644 --- a/tests/include.am +++ b/tests/include.am @@ -141,6 +141,11 @@ 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 = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -170,6 +175,7 @@ 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 else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la @@ -191,6 +197,7 @@ 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 endif EXTRA_DIST += tests/unit.h \ From ff063dd6193b38e3409c41bd704b56c04b460af6 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 17 Jun 2026 16:43:53 +0000 Subject: [PATCH 12/19] Enforce caller buffer size for NSS trust ULONG/BOOL attributes (F-4060) GetTrustAttr set *len to sizeof(CK_ULONG)/sizeof(CK_BBOOL) before calling GetULong/GetBool for the CKA_TRUST_* attributes, defeating those helpers' *len < dataLen buffer-too-small check and overflowing an undersized caller buffer. Let GetULong/GetBool handle the size query and the buffer-too-small case themselves, matching the canonical pattern. Add tests/trust_attr_bufsize_test.c regression test (NSS-only; verified against an --enable-nss build, skipped otherwise). --- src/internal.c | 24 ++- tests/include.am | 7 + tests/trust_attr_bufsize_test.c | 269 ++++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+), 15 deletions(-) create mode 100644 tests/trust_attr_bufsize_test.c diff --git a/src/internal.c b/src/internal.c index ee202cf7..1c078432 100644 --- a/src/internal.c +++ b/src/internal.c @@ -10325,30 +10325,24 @@ 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 handle the size query (data == NULL) and the + * buffer-too-small case themselves. Do not pre-set *len, which would + * defeat their *len < dataLen check and overflow an undersized + * caller buffer. */ 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/tests/include.am b/tests/include.am index dfa3b9bf..0ef4a10b 100644 --- a/tests/include.am +++ b/tests/include.am @@ -146,6 +146,11 @@ 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 @@ -176,6 +181,7 @@ 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 @@ -198,6 +204,7 @@ 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 \ diff --git a/tests/trust_attr_bufsize_test.c b/tests/trust_attr_bufsize_test.c new file mode 100644 index 00000000..19fe0b48 --- /dev/null +++ b/tests/trust_attr_bufsize_test.c @@ -0,0 +1,269 @@ +/* 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. GetTrustAttr overwrote the caller's *len + * with sizeof(CK_ULONG)/sizeof(CK_BBOOL) before delegating to GetULong/GetBool, + * defeating their buffer-too-small check and overflowing an undersized caller + * buffer for the NSS trust ULONG/BOOL attributes. C_GetAttributeValue must + * report CKR_BUFFER_TOO_SMALL instead. 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" + +#define TEST_DIR "./store/trust_attr_bufsize_test" + +static int test_passed = 0; +static int test_failed = 0; + +#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) + +#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; + +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; +} + +static void pkcs11_unload(void) +{ +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + funcList = NULL; +} + +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; +#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; + byte buffer[sizeof(CK_ULONG)]; + int i; +#endif + + 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]; + + rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_RV(rv, "C_OpenSession", 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 only 4 bytes and guard the rest. Must report + * CKR_BUFFER_TOO_SMALL without writing past the 4 bytes. Pre-fix this + * wrote sizeof(CK_ULONG) bytes, overflowing the buffer. */ + XMEMSET(buffer, 0xAB, sizeof(buffer)); + getAttr.type = CKA_TRUST_SERVER_AUTH; + getAttr.pValue = buffer; + getAttr.ulValueLen = 4; + rv = funcList->C_GetAttributeValue(session, obj, &getAttr, 1); + CHECK_RV(rv, "undersized CKA_TRUST_SERVER_AUTH", CKR_BUFFER_TOO_SMALL); + for (i = 4; i < (int)sizeof(buffer); i++) { + if (buffer[i] != 0xAB) + break; + } + CHECK_TRUE(i == (int)sizeof(buffer), + "no write past the caller-declared buffer length"); + + /* The STEP_UP boolean attribute has the same flaw via GetBool. */ + 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(); + + 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; +} From 9e5e453a0590473f1dc066252543ae313c4e23df Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 18 Jun 2026 14:52:21 +0000 Subject: [PATCH 13/19] Factor shared test boilerplate into pkcs11_test_util.h The F-* regression tests each duplicated the module load/unload, the funcList/dlib globals, the CHECK_RV/CHECK_TRUE macros and the pass/fail summary. Move them to tests/pkcs11_test_util.h along with a pkcs11_open_session() helper for the common C_Initialize + slot + R/W session setup, and use them from the tests. --- tests/aead_null_aad_test.c | 107 ++-------------- tests/create_object_class_test.c | 108 +--------------- tests/decrypt_final_bufsize_test.c | 129 +++---------------- tests/derive_key_type_test.c | 109 ++-------------- tests/gen_keypair_incomplete_test.c | 120 +++--------------- tests/include.am | 1 + tests/key_function_not_permitted_test.c | 105 ++-------------- tests/login_another_user_test.c | 92 ++------------ tests/login_pin_len_range_test.c | 103 ++------------- tests/pkcs11_test_util.h | 160 ++++++++++++++++++++++++ tests/set_attr_readonly_test.c | 111 ++-------------- tests/trust_attr_bufsize_test.c | 119 ++---------------- tests/verify_recover_badkey_test.c | 106 ++-------------- tests/verify_recover_class_test.c | 106 ++-------------- 14 files changed, 275 insertions(+), 1201 deletions(-) create mode 100644 tests/pkcs11_test_util.h diff --git a/tests/aead_null_aad_test.c b/tests/aead_null_aad_test.c index 6fd765e9..d5b22d16 100644 --- a/tests/aead_null_aad_test.c +++ b/tests/aead_null_aad_test.c @@ -18,11 +18,9 @@ * 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. WP11_Session_SetGcmParams and - * WP11_Session_SetCcmParams validated the IV pointer/length agreement but not - * the AAD pointer/length agreement, so C_EncryptInit with pAAD == NULL and - * ulAADLen > 0 was silently accepted. A mismatched AAD pointer/length must be - * rejected with CKR_MECHANISM_PARAM_INVALID. + * 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 @@ -48,78 +46,14 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/aead_null_aad_test" -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#ifndef HAVE_PKCS11_STATIC -static void* dlib; -#endif -static CK_FUNCTION_LIST* 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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - 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; CK_OBJECT_HANDLE aesKey = CK_INVALID_HANDLE; CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; CK_KEY_TYPE aesType = CKK_AES; @@ -150,21 +84,8 @@ static int run_test(void) 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); if (rv != CKR_OK) goto out; @@ -173,8 +94,7 @@ static int run_test(void) if (rv != CKR_OK) goto out; - /* AES-GCM: NULL AAD pointer with a non-zero AAD length is a contract - * violation and must be rejected. (Failed init leaves no active op.) */ + /* GCM: NULL AAD pointer with a non-zero AAD length must be rejected. */ XMEMSET(&gcm, 0, sizeof(gcm)); gcm.pIv = iv; gcm.ulIvLen = sizeof(iv); @@ -188,7 +108,7 @@ static int run_test(void) CHECK_RV(rv, "C_EncryptInit GCM (NULL AAD, len 16)", CKR_MECHANISM_PARAM_INVALID); - /* AES-CCM: same mismatch. Skip if AES-CCM is not built in. */ + /* CCM: same mismatch; skip if AES-CCM is not built in. */ XMEMSET(&ccm, 0, sizeof(ccm)); ccm.ulDataLen = sizeof(plain); ccm.pIv = iv; @@ -246,14 +166,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 AEAD NULL-AAD mismatch test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/create_object_class_test.c b/tests/create_object_class_test.c index 55b680b0..9a62ff80 100644 --- a/tests/create_object_class_test.c +++ b/tests/create_object_class_test.c @@ -18,11 +18,8 @@ * 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. CreateObject left objectClass - * at the (CK_OBJECT_CLASS)-1 sentinel when CKA_CLASS was omitted and then - * treated the template as a key object, creating an object whose CKA_CLASS is - * not a valid PKCS#11 class. A complete key template that omits only - * CKA_CLASS must be rejected with CKR_TEMPLATE_INCOMPLETE. + * 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 @@ -48,94 +45,25 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/create_object_class_test" -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#ifndef HAVE_PKCS11_STATIC -static void* dlib; -#endif -static CK_FUNCTION_LIST* 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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - 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; 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 }; - - /* Complete key material, but no CKA_CLASS. Pre-fix this created an - * object with an invalid (sentinel) class. */ 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); - - /* Same template with CKA_CLASS supplied: must succeed. */ CK_ATTRIBUTE withClassTmpl[] = { { CKA_CLASS, &secretClass, sizeof(secretClass) }, { CKA_KEY_TYPE, &genericType, sizeof(genericType) }, @@ -149,29 +77,14 @@ static int run_test(void) 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); if (rv != CKR_OK) goto out; - /* Key template missing only CKA_CLASS must be rejected. */ rv = funcList->C_CreateObject(session, noClassTmpl, noClassTmplCnt, &obj); CHECK_RV(rv, "C_CreateObject(no CKA_CLASS)", CKR_TEMPLATE_INCOMPLETE); - /* The same template with CKA_CLASS supplied still succeeds. */ rv = funcList->C_CreateObject(session, withClassTmpl, withClassTmplCnt, &obj); CHECK_RV(rv, "C_CreateObject(with CKA_CLASS)", CKR_OK); @@ -195,14 +108,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 C_CreateObject CKA_CLASS test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/decrypt_final_bufsize_test.c b/tests/decrypt_final_bufsize_test.c index f40646a2..f02a3018 100644 --- a/tests/decrypt_final_bufsize_test.c +++ b/tests/decrypt_final_bufsize_test.c @@ -18,12 +18,9 @@ * 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 handed WP11_AesCbcPad_DecryptFinal the buffered block length - * (16) instead of the caller's output-buffer capacity, so the helper's - * BUFFER_E guard could never fire and an undersized caller buffer was - * overflowed by up to 15 bytes. C_DecryptFinal must return - * CKR_BUFFER_TOO_SMALL for a too-small buffer like the GCM/CTS branches. + * 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 @@ -49,88 +46,14 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/decrypt_final_bufsize_test" -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#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; - -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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - 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; CK_OBJECT_HANDLE aesKey = CK_INVALID_HANDLE; CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; CK_KEY_TYPE aesType = CKK_AES; @@ -138,8 +61,8 @@ static int run_test(void) CK_BBOOL ckFalse = CK_FALSE; CK_MECHANISM mech; byte iv[16]; - /* 15-byte plaintext -> one padded block; the held-back final block - * unpads to 15 bytes, the maximum, maximising the overflow window. */ + /* 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]; @@ -166,21 +89,8 @@ static int run_test(void) 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); if (rv != CKR_OK) goto out; @@ -194,7 +104,6 @@ static int run_test(void) mech.pParameter = iv; mech.ulParameterLen = sizeof(iv); - /* Encrypt: 15 bytes -> 16 bytes ciphertext. */ rv = funcList->C_EncryptInit(session, &mech, aesKey); CHECK_RV(rv, "C_EncryptInit", CKR_OK); if (rv != CKR_OK) @@ -206,8 +115,8 @@ static int run_test(void) goto out; CHECK_TRUE(encSz == 16, "ciphertext is one block"); - /* Baseline: a complete multi-part decrypt with an adequate final buffer - * still round-trips the plaintext after the fix. */ + /* 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) @@ -225,10 +134,9 @@ static int run_test(void) XMEMCMP(lastPart, plain, sizeof(plain)) == 0, "plaintext round-trips with adequate buffer"); - /* Fresh decrypt that supplies an undersized final buffer: claim only 4 - * bytes of capacity and guard the rest with a canary. C_DecryptFinal must - * return CKR_BUFFER_TOO_SMALL without writing past the 4 bytes. Pre-fix it - * wrote up to 15 bytes and returned CKR_OK, overflowing the 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) @@ -276,14 +184,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 C_DecryptFinal CBC-PAD buffer-size test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/derive_key_type_test.c b/tests/derive_key_type_test.c index 8c5be751..bb5f7635 100644 --- a/tests/derive_key_type_test.c +++ b/tests/derive_key_type_test.c @@ -18,10 +18,8 @@ * 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 passed the base key to the - * mechanism-specific derive routine without checking that its key type - * matched the mechanism, so a wrong base key type failed deep in the crypto - * with CKR_FUNCTION_FAILED instead of the spec-mandated + * 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. */ @@ -48,71 +46,12 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/derive_key_type_test" -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#ifndef HAVE_PKCS11_STATIC -static void* dlib; -#endif -static CK_FUNCTION_LIST* 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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - -/* Derive with the given mechanism using the wrong-typed base key and assert - * CKR_KEY_TYPE_INCONSISTENT, or skip if the mechanism is not built in. */ +/* 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) { @@ -144,12 +83,7 @@ static void check_wrong_type(CK_SESSION_HANDLE session, CK_MECHANISM* mech, 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; CK_OBJECT_HANDLE aesKey = CK_INVALID_HANDLE; CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; CK_KEY_TYPE aesType = CKK_AES; @@ -159,9 +93,8 @@ static int run_test(void) byte pub[65]; byte param[32]; CK_ECDH1_DERIVE_PARAMS ecdh; - /* An AES key is the wrong base-key type for every asymmetric/KDF derive - * mechanism below. CKA_DERIVE=TRUE so the usage gate is passed and the - * per-mechanism type check is the thing under test. */ + /* 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) }, @@ -179,21 +112,8 @@ static int run_test(void) 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); if (rv != CKR_OK) goto out; @@ -237,14 +157,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 C_DeriveKey base-key-type test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/gen_keypair_incomplete_test.c b/tests/gen_keypair_incomplete_test.c index 1e8ae2c2..1d122a41 100644 --- a/tests/gen_keypair_incomplete_test.c +++ b/tests/gen_keypair_incomplete_test.c @@ -18,11 +18,10 @@ * 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 built the key objects - * and ran the mechanism generator without first checking the mechanism's - * required public-key template attributes, so a missing CKA_MODULUS_BITS - * (RSA), CKA_EC_PARAMS (EC) or CKA_PRIME/CKA_BASE (DH) surfaced as - * CKR_FUNCTION_FAILED instead of CKR_TEMPLATE_INCOMPLETE. + * 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 @@ -48,74 +47,13 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/gen_keypair_incomplete_test" -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#ifndef HAVE_PKCS11_STATIC -static void* dlib; -#endif -static CK_FUNCTION_LIST* 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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - -/* Generate a key pair with the supplied public template and report the rv. - * A CK_FALSE CKA_PRIVATE on the private template keeps the objects public so - * the call does not require C_Login. The result must be CKR_TEMPLATE_INCOMPLETE - * when the public template is missing a mechanism-required attribute, or - * CKR_MECHANISM_INVALID if the mechanism is not built in (treated as skip). */ +/* 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) @@ -151,24 +89,16 @@ static void check_incomplete(CK_SESSION_HANDLE session, CK_MECHANISM_TYPE mech, 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; CK_BBOOL ckTrue = CK_TRUE; - /* RSA public template missing CKA_MODULUS_BITS. */ - CK_ATTRIBUTE rsaPub[] = { + CK_ATTRIBUTE rsaPub[] = { /* missing CKA_MODULUS_BITS */ { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, }; - /* EC public template missing CKA_EC_PARAMS. */ - CK_ATTRIBUTE ecPub[] = { + CK_ATTRIBUTE ecPub[] = { /* missing CKA_EC_PARAMS */ { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, }; - /* DH public template missing CKA_PRIME and CKA_BASE. */ - CK_ATTRIBUTE dhPub[] = { + CK_ATTRIBUTE dhPub[] = { /* missing CKA_PRIME and CKA_BASE */ { CKA_DERIVE, &ckTrue, sizeof(ckTrue) }, }; @@ -177,21 +107,8 @@ static int run_test(void) 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); if (rv != CKR_OK) goto out; @@ -224,14 +141,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 C_GenerateKeyPair incomplete-template test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/include.am b/tests/include.am index 0ef4a10b..ac9b98a3 100644 --- a/tests/include.am +++ b/tests/include.am @@ -209,4 +209,5 @@ 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 index 85c5f541..fe1fdc88 100644 --- a/tests/key_function_not_permitted_test.c +++ b/tests/key_function_not_permitted_test.c @@ -18,11 +18,9 @@ * 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. CheckOpSupported denied an operation - * whose CKA_ usage attribute was CK_FALSE with CKR_KEY_TYPE_INCONSISTENT. - * PKCS#11 mandates CKR_KEY_FUNCTION_NOT_PERMITTED for a key whose usage - * attribute does not allow the operation; CKR_KEY_TYPE_INCONSISTENT is for a - * key type that does not match the mechanism. + * 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 @@ -48,78 +46,14 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/key_function_not_permitted_test" -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#ifndef HAVE_PKCS11_STATIC -static void* dlib; -#endif -static CK_FUNCTION_LIST* 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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - 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; CK_OBJECT_HANDLE aesKey = CK_INVALID_HANDLE; CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; CK_KEY_TYPE aesType = CKK_AES; @@ -134,7 +68,6 @@ static int run_test(void) /* Correct key type for the mechanism, but usage is denied. */ { CKA_ENCRYPT, &ckFalse, sizeof(ckFalse) }, { CKA_DECRYPT, &ckFalse, sizeof(ckFalse) }, - /* Public object so it is reachable without C_Login. */ { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, }; CK_ULONG aesTmplCnt = sizeof(aesTmpl) / sizeof(*aesTmpl); @@ -144,21 +77,8 @@ static int run_test(void) 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); if (rv != CKR_OK) goto out; @@ -172,8 +92,6 @@ static int run_test(void) mech.pParameter = iv; mech.ulParameterLen = sizeof(iv); - /* Correct key type, but CKA_ENCRYPT is CK_FALSE: the operation is not - * permitted, which is distinct from a key-type mismatch. */ rv = funcList->C_EncryptInit(session, &mech, aesKey); CHECK_RV(rv, "C_EncryptInit(CKA_ENCRYPT=FALSE)", CKR_KEY_FUNCTION_NOT_PERMITTED); @@ -201,14 +119,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 key-function-not-permitted test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/login_another_user_test.c b/tests/login_another_user_test.c index 56a6bd75..b379efa5 100644 --- a/tests/login_another_user_test.c +++ b/tests/login_another_user_test.c @@ -18,12 +18,9 @@ * 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 conflated the two - * already-logged-in cases: logging in while the *same* user type is logged - * in must return CKR_USER_ALREADY_LOGGED_IN, while logging in while a - * *different* user type is logged in must return - * CKR_USER_ANOTHER_ALREADY_LOGGED_IN. Pre-fix every case returned - * CKR_USER_ALREADY_LOGGED_IN. + * 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 @@ -49,6 +46,7 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/login_another_user_test" #define WOLFPKCS11_TOKEN_FILENAME "wp11_token_0000000000000001" @@ -58,66 +56,6 @@ static CK_ULONG soPinLen = 14; static byte* userPin = (byte*)"wolfpkcs11-test"; static CK_ULONG userPinLen = 15; -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#ifndef HAVE_PKCS11_STATIC -static void* dlib; -#endif -static CK_FUNCTION_LIST* 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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - static void cleanup_store(void) { char filepath[512]; @@ -141,6 +79,7 @@ static int run_test(void) XMEMSET(label, ' ', sizeof(label)); XMEMCPY(label, "another-user-test", 17); + /* Start from an uninitialized token. */ cleanup_store(); rv = pkcs11_load(); @@ -161,7 +100,7 @@ static int run_test(void) goto out; slot = slotList[0]; - /* Initialize the token (sets the SO PIN) before opening a session. */ + /* 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) @@ -172,7 +111,7 @@ static int run_test(void) if (rv != CKR_OK) goto out; - /* --- SO logged in: a second SO login is the same type --- */ + /* 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); @@ -180,19 +119,18 @@ static int run_test(void) CHECK_RV(rv, "C_Login(SO) again -> ALREADY_LOGGED_IN", CKR_USER_ALREADY_LOGGED_IN); - /* USER login while SO is logged in is a *different* type. */ 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) so phase B can log in as user. */ + /* 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 type --- */ + /* 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); @@ -200,7 +138,6 @@ static int run_test(void) CHECK_RV(rv, "C_Login(USER) again -> ALREADY_LOGGED_IN", CKR_USER_ALREADY_LOGGED_IN); - /* SO login while USER is logged in is a *different* type. */ 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); @@ -226,14 +163,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 C_Login another-user test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/login_pin_len_range_test.c b/tests/login_pin_len_range_test.c index 9671dcfd..b05b30d3 100644 --- a/tests/login_pin_len_range_test.c +++ b/tests/login_pin_len_range_test.c @@ -18,10 +18,9 @@ * 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. C_Login used to discard the - * CKR_PIN_LEN_RANGE result of checkPinLen and substitute CKR_PIN_INCORRECT. - * A PIN whose length is out of range must be reported as CKR_PIN_LEN_RANGE, - * matching C_InitToken / C_InitPIN / C_SetPIN. + * 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 @@ -47,6 +46,7 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/login_pin_len_range_test" @@ -55,75 +55,10 @@ static const char* tooLongPin = "0123456789012345678901234567890123456789"; -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#ifndef HAVE_PKCS11_STATIC -static void* dlib; -#endif -static CK_FUNCTION_LIST* 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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - 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; CK_ULONG pinLen = (CK_ULONG)XSTRLEN(tooLongPin); rv = pkcs11_load(); @@ -131,26 +66,11 @@ static int run_test(void) 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); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); - if (rv != CKR_OK) - goto out; - - /* Out-of-range PIN length must surface as CKR_PIN_LEN_RANGE, not - * CKR_PIN_INCORRECT. Tested for both user types. */ 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); @@ -178,14 +98,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 C_Login PIN length range test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/pkcs11_test_util.h b/tests/pkcs11_test_util.h new file mode 100644 index 00000000..2f992146 --- /dev/null +++ b/tests/pkcs11_test_util.h @@ -0,0 +1,160 @@ +/* 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 + +#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/set_attr_readonly_test.c b/tests/set_attr_readonly_test.c index d7613cd4..a8dac52a 100644 --- a/tests/set_attr_readonly_test.c +++ b/tests/set_attr_readonly_test.c @@ -18,11 +18,9 @@ * 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. SetAttributeValue forwarded every - * template attribute to WP11_Object_SetAttr, which accepted updates to - * read-only class/identity and generated-state attributes (CKA_CLASS, - * CKA_KEY_TYPE, CKA_ALWAYS_SENSITIVE, CKA_NEVER_EXTRACTABLE, ...). Changing - * those via C_SetAttributeValue must fail with CKR_ATTRIBUTE_READ_ONLY. + * 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 @@ -48,78 +46,14 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/set_attr_readonly_test" -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#ifndef HAVE_PKCS11_STATIC -static void* dlib; -#endif -static CK_FUNCTION_LIST* 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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - 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; CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; CK_OBJECT_CLASS dataClass = CKO_DATA; @@ -134,7 +68,7 @@ static int run_test(void) { CKA_KEY_TYPE, &genericType, sizeof(genericType) }, { CKA_VALUE, keyData, sizeof(keyData) }, { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, - /* Non-sensitive, extractable: CKA_ALWAYS_SENSITIVE and + /* 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) }, @@ -157,21 +91,8 @@ static int run_test(void) 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); if (rv != CKR_OK) goto out; @@ -180,7 +101,7 @@ static int run_test(void) if (rv != CKR_OK) goto out; - /* Changing class/identity attributes must be rejected. */ + /* 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); @@ -188,7 +109,7 @@ static int run_test(void) CHECK_RV(rv, "C_SetAttributeValue(CKA_KEY_TYPE)", CKR_ATTRIBUTE_READ_ONLY); - /* Changing generated-state attributes must be rejected. */ + /* 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); @@ -201,8 +122,7 @@ static int run_test(void) 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, not a - * change, so it is allowed (this is what C_CopyObject relies on). */ + /* 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); @@ -225,14 +145,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 C_SetAttributeValue read-only test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/trust_attr_bufsize_test.c b/tests/trust_attr_bufsize_test.c index 19fe0b48..0516751c 100644 --- a/tests/trust_attr_bufsize_test.c +++ b/tests/trust_attr_bufsize_test.c @@ -18,11 +18,9 @@ * 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. GetTrustAttr overwrote the caller's *len - * with sizeof(CK_ULONG)/sizeof(CK_BBOOL) before delegating to GetULong/GetBool, - * defeating their buffer-too-small check and overflowing an undersized caller - * buffer for the NSS trust ULONG/BOOL attributes. C_GetAttributeValue must - * report CKR_BUFFER_TOO_SMALL instead. NSS-only; skipped otherwise. + * 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 @@ -48,88 +46,14 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/trust_attr_bufsize_test" -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#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; - -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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - 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; #ifdef WOLFPKCS11_NSS CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; CK_OBJECT_CLASS trustClass = CKO_NSS_TRUST; @@ -164,21 +88,8 @@ static int run_test(void) 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); if (rv != CKR_OK) goto out; @@ -201,9 +112,8 @@ static int run_test(void) CHECK_TRUE(getAttr.ulValueLen == sizeof(CK_ULONG), "size query reports sizeof(CK_ULONG)"); - /* Undersized buffer: claim only 4 bytes and guard the rest. Must report - * CKR_BUFFER_TOO_SMALL without writing past the 4 bytes. Pre-fix this - * wrote sizeof(CK_ULONG) bytes, overflowing the buffer. */ + /* Undersized buffer: claim 4 bytes, guard the rest with a canary. Must + * report CKR_BUFFER_TOO_SMALL without writing past the 4 bytes. */ XMEMSET(buffer, 0xAB, sizeof(buffer)); getAttr.type = CKA_TRUST_SERVER_AUTH; getAttr.pValue = buffer; @@ -217,7 +127,7 @@ static int run_test(void) CHECK_TRUE(i == (int)sizeof(buffer), "no write past the caller-declared buffer length"); - /* The STEP_UP boolean attribute has the same flaw via GetBool. */ + /* 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; @@ -256,14 +166,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 NSS trust attribute buffer-size test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/verify_recover_badkey_test.c b/tests/verify_recover_badkey_test.c index caf92850..952191f6 100644 --- a/tests/verify_recover_badkey_test.c +++ b/tests/verify_recover_badkey_test.c @@ -18,11 +18,9 @@ * 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 used to forward the - * raw wolfCrypt error (BAD_FUNC_ARG) returned by WP11_Object_Find when the - * supplied key handle did not match any object, casting a negative int to - * CK_RV. The spec-defined code for a non-resolvable handle is - * CKR_OBJECT_HANDLE_INVALID, matching every other C_*Init function. + * 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 @@ -48,78 +46,14 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/verify_recover_badkey_test" -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#ifndef HAVE_PKCS11_STATIC -static void* dlib; -#endif -static CK_FUNCTION_LIST* 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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - 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; CK_MECHANISM mech; rv = pkcs11_load(); @@ -127,21 +61,8 @@ static int run_test(void) 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); if (rv != CKR_OK) goto out; @@ -150,13 +71,11 @@ static int run_test(void) mech.pParameter = NULL; mech.ulParameterLen = 0; - /* Non-zero handle that does not resolve to any object. Must report - * CKR_OBJECT_HANDLE_INVALID rather than a raw negative wolfCrypt code. */ + /* 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); - /* Sanity: the zero handle path already reports the same code. */ rv = funcList->C_VerifyRecoverInit(session, &mech, (CK_OBJECT_HANDLE)0); CHECK_RV(rv, "C_VerifyRecoverInit(zero handle)", CKR_OBJECT_HANDLE_INVALID); @@ -179,14 +98,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 C_VerifyRecoverInit bad-handle test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } diff --git a/tests/verify_recover_class_test.c b/tests/verify_recover_class_test.c index 4f0e19ca..2685acf9 100644 --- a/tests/verify_recover_class_test.c +++ b/tests/verify_recover_class_test.c @@ -18,10 +18,8 @@ * 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. When C_VerifyRecoverInit resolves a - * handle to a valid object whose CKA_CLASS is not CKO_PUBLIC_KEY it used to - * return CKR_KEY_HANDLE_INVALID (reserved for handles that do not refer to - * keys). A key of the wrong object class must report + * 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. */ @@ -48,78 +46,14 @@ #endif #include "testdata.h" +#include "pkcs11_test_util.h" #define TEST_DIR "./store/verify_recover_class_test" -static int test_passed = 0; -static int test_failed = 0; - -#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) - -#ifndef HAVE_PKCS11_STATIC -static void* dlib; -#endif -static CK_FUNCTION_LIST* 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; -} - -static void pkcs11_unload(void) -{ -#ifndef HAVE_PKCS11_STATIC - if (dlib != NULL) { - dlclose(dlib); - dlib = NULL; - } -#endif - funcList = NULL; -} - 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; CK_MECHANISM mech; CK_OBJECT_HANDLE secret = CK_INVALID_HANDLE; CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; @@ -132,7 +66,7 @@ static int run_test(void) { CKA_KEY_TYPE, &genericType, sizeof(genericType) }, { CKA_VALUE, keyData, sizeof(keyData) }, { CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue) }, - /* Public object so it is reachable without C_Login. */ + /* Public so the object is reachable without C_Login. */ { CKA_PRIVATE, &ckFalse, sizeof(ckFalse) }, }; CK_ULONG secretTmplCnt = sizeof(secretTmpl) / sizeof(*secretTmpl); @@ -142,21 +76,8 @@ static int run_test(void) 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]; - - rv = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); - CHECK_RV(rv, "C_OpenSession", CKR_OK); + rv = pkcs11_open_session(&session); + CHECK_RV(rv, "open session", CKR_OK); if (rv != CKR_OK) goto out; @@ -168,9 +89,7 @@ static int run_test(void) XMEMSET(&mech, 0, sizeof(mech)); mech.mechanism = CKM_RSA_PKCS; - /* The handle resolves to a secret key, not a public key. Wrong object - * class for verify-recover must report CKR_KEY_TYPE_INCONSISTENT, not - * CKR_KEY_HANDLE_INVALID. */ + /* 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); @@ -194,14 +113,5 @@ int main(int argc, char* argv[]) printf("=== wolfPKCS11 C_VerifyRecoverInit wrong-class test ===\n"); run_test(); - - 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; + return pkcs11_test_summary(); } From 4c26ff64960ddd9b334da4be4c8b8e9514daa3d6 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 18 Jun 2026 14:58:43 +0000 Subject: [PATCH 14/19] Tidy F-* fix comments and hoist local declarations Drop comments that narrated the previous (buggy) behaviour or merely restated the code, keeping only the high-level/spec rationale. Hoist the single-use block-scoped declarations added by the fixes (classAttr in C_CreateObject, reqAttr in C_GenerateKeyPair, the read-only-attribute scratch buffer in SetAttributeValue) to the start of their functions. --- src/crypto.c | 122 +++++++++++++++++------------------------- src/internal.c | 23 +++----- wolfpkcs11/internal.h | 1 - 3 files changed, 56 insertions(+), 90 deletions(-) diff --git a/src/crypto.c b/src/crypto.c index 69bcc43e..3c92263c 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -480,10 +480,8 @@ static int CheckOpSupportedRv(WP11_Object* obj, CK_ATTRIBUTE_TYPE op, static int CheckOpSupported(WP11_Object* obj, CK_ATTRIBUTE_TYPE op) { - /* A key whose CKA_ usage attribute does not allow the operation is - * reported as CKR_KEY_FUNCTION_NOT_PERMITTED. CKR_KEY_TYPE_INCONSISTENT - * is reserved for a key type that does not match the mechanism, which the - * per-mechanism checks handle. */ + /* 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); } @@ -764,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; @@ -969,28 +969,20 @@ static CK_RV SetAttributeValue(WP11_Session* session, WP11_Object* obj, *(CK_BBOOL*)attr->pValue == CK_TRUE) return CKR_ATTRIBUTE_READ_ONLY; } - /* PKCS#11 marks these class/identity and generated-state attributes - * read-only once the object exists. Reject an attempt to change them - * (via C_SetAttributeValue or C_CopyObject); setting the current value - * is a harmless no-op. Not applied during object creation. */ - if (!newObject) { - static const CK_ATTRIBUTE_TYPE readOnlyAttrs[] = { - CKA_CLASS, CKA_KEY_TYPE, CKA_LOCAL, CKA_KEY_GEN_MECHANISM, - CKA_ALWAYS_SENSITIVE, CKA_NEVER_EXTRACTABLE - }; - unsigned int k; - for (k = 0; k < sizeof(readOnlyAttrs) / sizeof(readOnlyAttrs[0]); - k++) { - byte cur[sizeof(CK_ULONG)]; - CK_ULONG curLen = sizeof(cur); - if (attr->type != readOnlyAttrs[k]) - continue; - if (WP11_Object_GetAttr(obj, attr->type, cur, &curLen) == 0 && - (attr->pValue == NULL || attr->ulValueLen != curLen || - XMEMCMP(attr->pValue, cur, curLen) != 0)) { - return CKR_ATTRIBUTE_READ_ONLY; - } - break; + /* 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, @@ -1358,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 @@ -1400,18 +1393,13 @@ CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, } } - /* C_CreateObject requires CKA_CLASS in the template; there is no - * API-implied object class as there is for derive/unwrap. Reject an - * incomplete template rather than creating an object whose class is the - * uninitialized sentinel value. */ - { - CK_ATTRIBUTE* classAttr = NULL; - FindAttributeType(pTemplate, ulCount, CKA_CLASS, &classAttr); - if (classAttr == NULL) { - rv = CKR_TEMPLATE_INCOMPLETE; - WOLFPKCS11_LEAVE("C_CreateObject", rv); - return rv; - } + /* 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, @@ -4111,12 +4099,10 @@ CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, return CKR_OK; } - /* Pass the caller's buffer capacity (not the buffered block - * length, which is always 16) so WP11_AesCbcPad_DecryptFinal - * validates the write against it and returns BUFFER_E rather than - * overflowing a too-small buffer, mirroring the single-shot - * C_Decrypt path. Cap to the block size, which already exceeds the - * 0..15 byte output, to avoid truncating a large CK_ULONG. */ + /* 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); @@ -7533,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 @@ -7610,14 +7597,10 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, } /* CKA_MODULUS_BITS is required to size the RSA key. */ - { - CK_ATTRIBUTE* reqAttr = NULL; - FindAttributeType(pPublicKeyTemplate, - ulPublicKeyAttributeCount, CKA_MODULUS_BITS, - &reqAttr); - if (reqAttr == NULL) - return CKR_TEMPLATE_INCOMPLETE; - } + FindAttributeType(pPublicKeyTemplate, ulPublicKeyAttributeCount, + CKA_MODULUS_BITS, &reqAttr); + if (reqAttr == NULL) + return CKR_TEMPLATE_INCOMPLETE; *phPublicKey = *phPrivateKey = CK_INVALID_HANDLE; @@ -7643,16 +7626,11 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, return CKR_MECHANISM_PARAM_INVALID; } - /* CKA_EC_PARAMS carries the curve; without it there are no domain - * parameters to generate against. */ - { - CK_ATTRIBUTE* reqAttr = NULL; - FindAttributeType(pPublicKeyTemplate, - ulPublicKeyAttributeCount, CKA_EC_PARAMS, - &reqAttr); - if (reqAttr == NULL) - return CKR_TEMPLATE_INCOMPLETE; - } + /* 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; @@ -7792,18 +7770,14 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, } /* DH needs the domain parameters CKA_PRIME and CKA_BASE. */ - { - CK_ATTRIBUTE* primeAttr = NULL; - CK_ATTRIBUTE* baseAttr = NULL; - FindAttributeType(pPublicKeyTemplate, - ulPublicKeyAttributeCount, CKA_PRIME, - &primeAttr); - FindAttributeType(pPublicKeyTemplate, - ulPublicKeyAttributeCount, CKA_BASE, - &baseAttr); - if (primeAttr == NULL || baseAttr == NULL) - return CKR_TEMPLATE_INCOMPLETE; - } + 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; diff --git a/src/internal.c b/src/internal.c index 1c078432..dc05e1c4 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7299,8 +7299,8 @@ int WP11_Slot_SOLogin(WP11_Slot* slot, char* pin, int pinLen) WP11_Lock_LockRO(&slot->lock); if (ret == 0) { - /* Have we already logged in? Distinguish the same user type (SO) - * from a different one (USER) for the caller's return code. */ + /* 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) { ret = LOGGED_IN_E; @@ -7399,10 +7399,9 @@ 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? Distinguish the same user type (USER) - * from a different one (SO) for the caller's return code. */ + /* 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_RO_USER || state == WP11_APP_STATE_RW_USER) { @@ -8533,9 +8532,7 @@ 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; - /* The AAD pointer and length must agree by the same rule, so a NULL AAD - * with a non-zero length is rejected rather than silently treated as - * empty AAD. */ + /* The AAD pointer and length must agree by the same rule. */ if (ret == 0 && (aad == NULL) != (aadLen == 0)) ret = BAD_FUNC_ARG; @@ -8594,9 +8591,7 @@ 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; - /* The AAD pointer and length must agree by the same rule, so a NULL AAD - * with a non-zero length is rejected rather than silently treated as - * empty AAD. */ + /* The AAD pointer and length must agree by the same rule. */ if (ret == 0 && (aad == NULL) != (aadSz == 0)) ret = BAD_FUNC_ARG; @@ -10325,10 +10320,8 @@ 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 handle the size query (data == NULL) and the - * buffer-too-small case themselves. Do not pre-set *len, which would - * defeat their *len < dataLen check and overflow an undersized - * caller buffer. */ + /* GetULong/GetBool handle the size query and buffer-too-small case; + * do not pre-set *len. */ case CKA_TRUST_SERVER_AUTH: ret = GetULong(object->data.trust.serverAuth, data, len); break; diff --git a/wolfpkcs11/internal.h b/wolfpkcs11/internal.h index 057da55c..4a6b6342 100644 --- a/wolfpkcs11/internal.h +++ b/wolfpkcs11/internal.h @@ -352,7 +352,6 @@ C_EXTRA_FLAGS="-DWOLFSSL_PUBLIC_MP -DWC_RSA_DIRECT" #define OBJ_COUNT_E -10 #define OBJ_TYPE_E -11 #define PARAM_E -12 -/* A different user type is already logged in. */ #define LOGGED_IN_ANOTHER_E -13 From ff73a34da1e718d61a59aa663d9f6d1ce7716d4c Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 18 Jun 2026 15:21:29 +0000 Subject: [PATCH 15/19] Fix CI: HKDF_DATA data-object derive and AES-GCM-disabled test C_DeriveKey applied the HKDF base key-type check to CKM_HKDF_DATA, which derives from a CKO_DATA object rather than a key, so NSS's TLS 1.3 key schedule failed with CKR_KEY_TYPE_INCONSISTENT (test-nss-curl: 'the key does not support the requested operation'). Restrict the key-type check to CKM_HKDF_DERIVE. Add an NSS-only regression test deriving via CKM_HKDF_DATA from a CKO_DATA object. aead_null_aad_test asserted CKR_MECHANISM_PARAM_INVALID for AES-GCM, but the --disable-aesgcm build (no_aesgcm CI job) returns CKR_MECHANISM_INVALID; skip the GCM checks when the mechanism is unavailable, as already done for CCM. --- src/crypto.c | 5 ++- tests/aead_null_aad_test.c | 50 +++++++++++++++------------ tests/derive_key_type_test.c | 66 ++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 22 deletions(-) diff --git a/src/crypto.c b/src/crypto.c index 3c92263c..78a91e11 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -8711,7 +8711,10 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, CK_HKDF_PARAMS_PTR kdfParams; CK_ATTRIBUTE *lenAttr = NULL; - if (WP11_Object_GetType(obj) != CKK_HKDF && + /* CKM_HKDF_DATA derives from a CKO_DATA object, so only the + * key-based CKM_HKDF_DERIVE has a base key type to check. */ + if (pMechanism->mechanism == CKM_HKDF_DERIVE && + WP11_Object_GetType(obj) != CKK_HKDF && WP11_Object_GetType(obj) != CKK_GENERIC_SECRET) return CKR_KEY_TYPE_INCONSISTENT; if (pMechanism->pParameter == NULL) diff --git a/tests/aead_null_aad_test.c b/tests/aead_null_aad_test.c index d5b22d16..720ac6a9 100644 --- a/tests/aead_null_aad_test.c +++ b/tests/aead_null_aad_test.c @@ -94,7 +94,8 @@ static int run_test(void) if (rv != CKR_OK) goto out; - /* GCM: NULL AAD pointer with a non-zero AAD length must be rejected. */ + /* 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); @@ -105,8 +106,33 @@ static int run_test(void) mech.pParameter = &gcm; mech.ulParameterLen = sizeof(gcm); rv = funcList->C_EncryptInit(session, &mech, aesKey); - CHECK_RV(rv, "C_EncryptInit GCM (NULL AAD, len 16)", - CKR_MECHANISM_PARAM_INVALID); + 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); + } + } /* CCM: same mismatch; skip if AES-CCM is not built in. */ XMEMSET(&ccm, 0, sizeof(ccm)); @@ -129,24 +155,6 @@ static int run_test(void) CKR_MECHANISM_PARAM_INVALID); } - /* The canonical no-AAD form (NULL pointer, zero length) is still 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); - } - out: if (session != 0) funcList->C_CloseSession(session); diff --git a/tests/derive_key_type_test.c b/tests/derive_key_type_test.c index bb5f7635..c1079048 100644 --- a/tests/derive_key_type_test.c +++ b/tests/derive_key_type_test.c @@ -80,6 +80,68 @@ static void check_wrong_type(CK_SESSION_HANDLE session, CK_MECHANISM* mech, funcList->C_DestroyObject(session, out); } +#ifdef WOLFPKCS11_NSS +/* CKM_HKDF_DATA derives from a CKO_DATA object (the TLS 1.3 key schedule NSS + * drives). That base object 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_DATA; + 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_DATA not supported in this build\n"); + test_passed++; + return; + } + CHECK_RV(rv, "C_DeriveKey(CKM_HKDF_DATA, CKO_DATA base)", CKR_OK); + if (rv == CKR_OK) + funcList->C_DestroyObject(session, out); +} +#endif + static int run_test(void) { CK_RV rv; @@ -138,6 +200,10 @@ static int run_test(void) 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); From 43008cbb2c677bdfc782cfa03b19998435425b9e Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 18 Jun 2026 15:27:42 +0000 Subject: [PATCH 16/19] Address Copilot review on PR #194 - keyTypeBadValue used sizeof(&badKeyType) (pointer size) for the CKA_KEY_TYPE attribute length, wrong on LLP64; use sizeof(badKeyType). - trust_attr_bufsize_test: size the canary buffer larger than a CK_ULONG and claim sizeof(CK_ULONG)-1 so the undersized-buffer check is genuinely undersized and the canary region exists on 32- and 64-bit builds. --- tests/pkcs11mtt.c | 2 +- tests/pkcs11test.c | 2 +- tests/trust_attr_bufsize_test.c | 13 ++++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/pkcs11mtt.c b/tests/pkcs11mtt.c index 99bef0ae..fb8901bf 100644 --- a/tests/pkcs11mtt.c +++ b/tests/pkcs11mtt.c @@ -257,7 +257,7 @@ static CK_RV test_object(void* args) CK_ULONG badKeyType = -1; CK_ATTRIBUTE keyTypeBadValue[] = { { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, - { CKA_KEY_TYPE, &badKeyType, sizeof(&badKeyType) } + { CKA_KEY_TYPE, &badKeyType, sizeof(badKeyType) } }; CK_ATTRIBUTE keyDataNull[] = { { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, diff --git a/tests/pkcs11test.c b/tests/pkcs11test.c index 5b5ae352..ec39f2a8 100644 --- a/tests/pkcs11test.c +++ b/tests/pkcs11test.c @@ -2010,7 +2010,7 @@ static CK_RV test_object(void* args) CK_ULONG badKeyType = -1; CK_ATTRIBUTE keyTypeBadValue[] = { { CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass) }, - { CKA_KEY_TYPE, &badKeyType, sizeof(&badKeyType) } + { CKA_KEY_TYPE, &badKeyType, sizeof(badKeyType) } }; CK_ATTRIBUTE keyDataNull[] = { { CKA_CLASS, &privKeyClass, sizeof(privKeyClass) }, diff --git a/tests/trust_attr_bufsize_test.c b/tests/trust_attr_bufsize_test.c index 0516751c..8b415c74 100644 --- a/tests/trust_attr_bufsize_test.c +++ b/tests/trust_attr_bufsize_test.c @@ -79,7 +79,9 @@ static int run_test(void) }; CK_ULONG tmplCnt = sizeof(tmpl) / sizeof(*tmpl); CK_ATTRIBUTE getAttr; - byte buffer[sizeof(CK_ULONG)]; + /* 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 @@ -112,15 +114,16 @@ static int run_test(void) CHECK_TRUE(getAttr.ulValueLen == sizeof(CK_ULONG), "size query reports sizeof(CK_ULONG)"); - /* Undersized buffer: claim 4 bytes, guard the rest with a canary. Must - * report CKR_BUFFER_TOO_SMALL without writing past the 4 bytes. */ + /* 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 = 4; + 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 = 4; i < (int)sizeof(buffer); i++) { + for (i = (int)(sizeof(CK_ULONG) - 1); i < (int)sizeof(buffer); i++) { if (buffer[i] != 0xAB) break; } From ca9519720f82b1cd2feb769af96a3ba4a475278f Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 18 Jun 2026 17:28:16 +0000 Subject: [PATCH 17/19] Fix CI: accept CKO_DATA base object for HKDF derive (F-4065) NSS drives the TLS 1.3 key schedule with a data-based HKDF derive on a CKO_DATA base object. NSS sends the PKCS#11 standard mechanism value 0x402A, which wolfPKCS11's headers name CKM_HKDF_DERIVE, so that call reaches the key-based derive case. The previous guard keyed off the mechanism value and wrongly rejected the CKO_DATA base object with CKR_KEY_TYPE_INCONSISTENT, breaking the NSS handshake (test-nss-curl, test-firefox). Gate the base key-type check on the object class instead: a CKO_DATA object has no key type and is always a valid HKDF data input; only key base objects are checked for an HKDF-compatible type. The regression test now uses the mechanism value NSS actually sends so it exercises the failing path. --- src/crypto.c | 6 +++--- tests/derive_key_type_test.c | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/crypto.c b/src/crypto.c index 78a91e11..e03d5df5 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -8711,9 +8711,9 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, CK_HKDF_PARAMS_PTR kdfParams; CK_ATTRIBUTE *lenAttr = NULL; - /* CKM_HKDF_DATA derives from a CKO_DATA object, so only the - * key-based CKM_HKDF_DERIVE has a base key type to check. */ - if (pMechanism->mechanism == CKM_HKDF_DERIVE && + /* 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; diff --git a/tests/derive_key_type_test.c b/tests/derive_key_type_test.c index c1079048..e7a818fa 100644 --- a/tests/derive_key_type_test.c +++ b/tests/derive_key_type_test.c @@ -81,8 +81,10 @@ static void check_wrong_type(CK_SESSION_HANDLE session, CK_MECHANISM* mech, } #ifdef WOLFPKCS11_NSS -/* CKM_HKDF_DATA derives from a CKO_DATA object (the TLS 1.3 key schedule NSS - * drives). That base object must be accepted, not rejected as a key-type +/* 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) @@ -126,17 +128,17 @@ static void check_hkdf_data_object(CK_SESSION_HANDLE session) hkdf.ulSaltType = CKF_HKDF_SALT_NULL; hkdf.pInfo = value; hkdf.ulInfoLen = 8; - mech.mechanism = CKM_HKDF_DATA; + 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_DATA not supported in this build\n"); + printf("SKIP: CKM_HKDF_DERIVE not supported in this build\n"); test_passed++; return; } - CHECK_RV(rv, "C_DeriveKey(CKM_HKDF_DATA, CKO_DATA base)", CKR_OK); + CHECK_RV(rv, "C_DeriveKey(CKM_HKDF_DERIVE, CKO_DATA base)", CKR_OK); if (rv == CKR_OK) funcList->C_DestroyObject(session, out); } From 4159dca610025b3d0c5073b7bf3e9356a1c4dce0 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 18 Jun 2026 17:48:48 +0000 Subject: [PATCH 18/19] Address Copilot review on PR #194: AAD zero-length and C_Login docs - AES-GCM/CCM SetParams: only reject a NULL AAD pointer paired with a non-zero length. A non-NULL pointer with zero length is a valid no-AAD encoding; rejecting it broke callers that pass an unused buffer with a zero length. Guard the AAD copy on a positive length so the no-AAD case never allocates. Regression test now asserts non-NULL + zero length is accepted. - C_Login doc comment: reflect CKR_PIN_LEN_RANGE for out-of-range PIN length (now propagated from checkPinLen) and document the CKR_USER_ANOTHER_ALREADY_LOGGED_IN path. --- src/internal.c | 14 ++++++++------ src/slot.c | 5 ++++- tests/aead_null_aad_test.c | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/internal.c b/src/internal.c index dc05e1c4..f2957bca 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8532,8 +8532,9 @@ 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; - /* The AAD pointer and length must agree by the same rule. */ - if (ret == 0 && (aad == NULL) != (aadLen == 0)) + /* 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) { @@ -8545,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) @@ -8591,8 +8592,9 @@ 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; - /* The AAD pointer and length must agree by the same rule. */ - if (ret == 0 && (aad == NULL) != (aadSz == 0)) + /* 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) { @@ -8604,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) { diff --git a/src/slot.c b/src/slot.c index 77cfd188..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. diff --git a/tests/aead_null_aad_test.c b/tests/aead_null_aad_test.c index 720ac6a9..6b22f9f2 100644 --- a/tests/aead_null_aad_test.c +++ b/tests/aead_null_aad_test.c @@ -132,6 +132,27 @@ static int run_test(void) &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. */ From 05689dcfe5f18425a8691f6183986f42e97910f7 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 18 Jun 2026 18:09:20 +0000 Subject: [PATCH 19/19] Address Copilot review on PR #194: trust-attr comment and test header - GetTrustAttr: correct the comment over the CKA_TRUST_* cases. GetULong/ GetBool set *len on a size query or success and return BUFFER_E without touching *len when the buffer is too small; the old wording implied they also reported the size on the too-small path. - pkcs11_test_util.h: include so the helper header is self-contained and not dependent on include order. --- src/internal.c | 5 +++-- tests/pkcs11_test_util.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/internal.c b/src/internal.c index f2957bca..1ae6d9a6 100644 --- a/src/internal.c +++ b/src/internal.c @@ -10322,8 +10322,9 @@ 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 handle the size query and buffer-too-small case; - * do not pre-set *len. */ + /* 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: ret = GetULong(object->data.trust.serverAuth, data, len); break; diff --git a/tests/pkcs11_test_util.h b/tests/pkcs11_test_util.h index 2f992146..a89e3e33 100644 --- a/tests/pkcs11_test_util.h +++ b/tests/pkcs11_test_util.h @@ -30,6 +30,8 @@ #ifndef PKCS11_TEST_UTIL_H #define PKCS11_TEST_UTIL_H +#include + #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4101 4189 4505)