diff --git a/.github/workflows/sanitizer-tests.yml b/.github/workflows/sanitizer-tests.yml index a4e81919..67e1c689 100644 --- a/.github/workflows/sanitizer-tests.yml +++ b/.github/workflows/sanitizer-tests.yml @@ -18,7 +18,7 @@ jobs: sanitizer: [asan, ubsan] config: - name: "Standard Build" - configure_flags: "" + configure_flags: "--enable-aeskeywrap" - name: "NSS Build" configure_flags: "--enable-nss" - name: "TPM Build" diff --git a/src/crypto.c b/src/crypto.c index 0326e02c..b12aebd2 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -7864,10 +7864,18 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, FindAttributeType(pTemplate, ulAttributeCount, CKA_VALUE_LEN, &lenAttr); if (kdfParams->bExpand) { - if (!lenAttr) { - return CKR_MECHANISM_PARAM_INVALID; + CK_ULONG reqLen; + if (!lenAttr || !lenAttr->pValue) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + if (lenAttr->ulValueLen != sizeof(CK_ULONG)) { + return CKR_ATTRIBUTE_VALUE_INVALID; } - keyLen = *(word32*)lenAttr->pValue; + XMEMCPY(&reqLen, lenAttr->pValue, sizeof(CK_ULONG)); + if (reqLen == 0 || reqLen > (CK_ULONG)0xFFFFFFFF) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + keyLen = (word32)reqLen; } else { keyLen = WC_MAX_DIGEST_SIZE; @@ -7944,14 +7952,26 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, if (tlsParams->pReturnedKeyMaterial == NULL) return CKR_MECHANISM_PARAM_INVALID; - keyLen = (word32)(2 * tlsParams->ulMacSizeInBits) + - (word32)(2 * tlsParams->ulKeySizeInBits) + - (word32)(2 * tlsParams->ulIVSizeInBits); - if (keyLen == 0) - return CKR_MECHANISM_PARAM_INVALID; - if ((keyLen % 8) != 0) - return CKR_MECHANISM_PARAM_INVALID; - keyLen /= 8; + { + CK_ULONG totalBits; + /* Validate individual fields won't overflow when doubled */ + if (tlsParams->ulMacSizeInBits > ((CK_ULONG)0xFFFFFFFF / 2) || + tlsParams->ulKeySizeInBits > ((CK_ULONG)0xFFFFFFFF / 2) || + tlsParams->ulIVSizeInBits > ((CK_ULONG)0xFFFFFFFF / 2)) { + return CKR_MECHANISM_PARAM_INVALID; + } + totalBits = (2 * tlsParams->ulMacSizeInBits) + + (2 * tlsParams->ulKeySizeInBits) + + (2 * tlsParams->ulIVSizeInBits); + if (totalBits == 0) + return CKR_MECHANISM_PARAM_INVALID; + if ((totalBits % 8) != 0) + return CKR_MECHANISM_PARAM_INVALID; + totalBits /= 8; + if (totalBits > (CK_ULONG)0xFFFFFFFF) + return CKR_MECHANISM_PARAM_INVALID; + keyLen = (word32)totalBits; + } derivedKey = (byte*)XMALLOC(keyLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (derivedKey == NULL) @@ -8081,7 +8101,7 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, } } - if (rv == CKR_OK) { + if ((rv == CKR_OK) && (derivedKey != NULL)) { rv = SetInitialStates(obj); } diff --git a/src/internal.c b/src/internal.c index 883796c0..dc9a9877 100644 --- a/src/internal.c +++ b/src/internal.c @@ -945,6 +945,13 @@ static void wp11_Session_Final(WP11_Session* session) session->init = 0; } #endif +#ifdef HAVE_AES_KEYWRAP + if ((session->mechanism == CKM_AES_KEY_WRAP || + session->mechanism == CKM_AES_KEY_WRAP_PAD) && session->init) { + wc_AesFree(&session->params.kw.aes); + session->init = 0; + } +#endif #ifdef HAVE_AESCCM if (session->mechanism == CKM_AES_CCM) { if (session->params.ccm.aad != NULL) { @@ -2554,7 +2561,7 @@ static long GetRsaExponentValue(unsigned char* eData, word32 eSz) long e = 0; /* Convert big-endian data into number. */ - for (i = eSz - 1; i >= 0; i--) { + for (i = 0; i < (int)eSz; i++) { e <<= 8; e |= eData[i]; } @@ -8281,6 +8288,7 @@ void WP11_Object_Free(WP11_Object* object) #ifndef NO_DH if (object->type == CKK_DH && object->data.dhKey != NULL) { wc_FreeDhKey(&object->data.dhKey->params); + wc_ForceZero(object->data.dhKey->key, object->data.dhKey->len); XFREE(object->data.dhKey, NULL, DYNAMIC_TYPE_DH); object->data.dhKey = NULL; } @@ -8745,6 +8753,7 @@ int WP11_Object_SetMldsaKey(WP11_Object* object, unsigned char** data, ret = BAD_FUNC_ARG; } } + wc_ForceZero(expandedKey, expandedKeyLen); XFREE(expandedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); } } @@ -8831,7 +8840,7 @@ int WP11_Object_SetSecretKey(WP11_Object* object, unsigned char** data, key = object->data.symmKey; key->len = 0; - XMEMSET(key->data, 0, sizeof(key->data)); + wc_ForceZero(key->data, sizeof(key->data)); /* First item is the key's length. */ if (ret == 0 && data[0] != NULL && len[0] != (int)sizeof(CK_ULONG)) @@ -13655,6 +13664,7 @@ int WP11_AesKeyWrap_Encrypt(unsigned char* plain, word32 plainSz, ret = wc_AesKeyWrap_ex(&wrap->aes, plain, plainSz, enc, *encSz, wrap->ivSz != 0 ? wrap->iv : NULL); + wc_AesFree(&wrap->aes); session->init = 0; if (ret < 0) return ret; @@ -13670,6 +13680,7 @@ int WP11_AesKeyWrap_Decrypt(unsigned char* enc, word32 encSz, ret = wc_AesKeyUnWrap_ex(&wrap->aes, enc, encSz, dec, *decSz, wrap->ivSz != 0 ? wrap->iv : NULL); + wc_AesFree(&wrap->aes); session->init = 0; if (ret < 0) return ret; diff --git a/tests/include.am b/tests/include.am index 725c474c..14e81752 100644 --- a/tests/include.am +++ b/tests/include.am @@ -61,6 +61,11 @@ noinst_PROGRAMS += tests/pkcs11v3test tests_pkcs11v3test_SOURCES = tests/pkcs11v3test.c tests_pkcs11v3test_LDADD = +check_PROGRAMS += tests/rsa_exponent_test +noinst_PROGRAMS += tests/rsa_exponent_test +tests_rsa_exponent_test_SOURCES = tests/rsa_exponent_test.c +tests_rsa_exponent_test_LDADD = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -74,6 +79,7 @@ tests_find_objects_null_template_test_LDADD += src/libwolfpkcs11.la tests_aes_cbc_pad_padding_test_LDADD += src/libwolfpkcs11.la tests_ecb_check_value_error_test_LDADD += src/libwolfpkcs11.la tests_pkcs11v3test_LDADD += src/libwolfpkcs11.la +tests_rsa_exponent_test_LDADD += src/libwolfpkcs11.la else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la tests_empty_pin_store_test_LDADD += src/libwolfpkcs11.la diff --git a/tests/pkcs11test.c b/tests/pkcs11test.c index 17a3e62f..c0f2c976 100644 --- a/tests/pkcs11test.c +++ b/tests/pkcs11test.c @@ -14827,6 +14827,112 @@ static CK_RV test_hkdf_gen_key(void* args) return ret; } + +/* Regression test: HKDF expand with NULL pValue in CKA_VALUE_LEN + * previously crashed (Issue #1315: lenAttr->pValue was dereferenced + * without NULL check). + */ +static CK_RV test_hkdf_derive_expand_null_value_len(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret; + CK_OBJECT_HANDLE hBaseKey = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPrk = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hExpandKey = CK_INVALID_HANDLE; + + CK_BYTE ikm[] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }; + CK_ULONG ikm_len = sizeof(ikm); + CK_BYTE salt[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + CK_BYTE info[] = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9 }; + CK_ULONG prk_len = 32; + + /* Create IKM as a secret key object */ + CK_ATTRIBUTE templateSecret[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_TOKEN, &ckFalse, sizeof(ckFalse)}, + {CKA_PRIVATE, &ckTrue, sizeof(ckTrue)}, + {CKA_SENSITIVE, &ckFalse, sizeof(ckFalse)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE, ikm, ikm_len}, + {CKA_VALUE_LEN, &ikm_len, sizeof(ikm_len)}, + {CKA_DERIVE, &ckTrue, sizeof(ckTrue)} + }; + CK_ULONG templateSecretCount = + sizeof(templateSecret) / sizeof(templateSecret[0]); + + /* Extract params */ + CK_HKDF_PARAMS paramsExtract = { + CK_TRUE, CK_FALSE, CKM_SHA256_HMAC, + CKF_HKDF_SALT_DATA, salt, sizeof(salt), + CK_INVALID_HANDLE, NULL_PTR, 0 + }; + CK_MECHANISM mechExtract = + { CKM_HKDF_DERIVE, ¶msExtract, sizeof(paramsExtract) }; + CK_ATTRIBUTE templateExtract[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_SENSITIVE, &ckFalse, sizeof(ckFalse)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE_LEN, &prk_len, sizeof(prk_len)} + }; + CK_ULONG templateExtractCount = + sizeof(templateExtract) / sizeof(templateExtract[0]); + + /* Expand params - expand only */ + CK_HKDF_PARAMS paramsExpand = { + CK_FALSE, CK_TRUE, CKM_SHA256_HMAC, + CKF_HKDF_SALT_NULL, NULL_PTR, 0, + CK_INVALID_HANDLE, info, sizeof(info) + }; + CK_MECHANISM mechExpand = + { CKM_HKDF_DERIVE, ¶msExpand, sizeof(paramsExpand) }; + + /* Expand template with NULL pValue for CKA_VALUE_LEN. + * This triggers the NULL dereference at crypto.c:7870. + */ + CK_ATTRIBUTE templateExpand[] = { + {CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)}, + {CKA_KEY_TYPE, &genericKeyType, sizeof(genericKeyType)}, + {CKA_SENSITIVE, &ckFalse, sizeof(ckFalse)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_VALUE_LEN, NULL_PTR, 0} + }; + CK_ULONG templateExpandCount = + sizeof(templateExpand) / sizeof(templateExpand[0]); + + /* Step 1: Create base key */ + ret = funcList->C_CreateObject(session, templateSecret, + templateSecretCount, &hBaseKey); + CHECK_CKR(ret, "Create IKM object"); + + /* Step 2: Extract to get PRK */ + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mechExtract, hBaseKey, + templateExtract, templateExtractCount, &hPrk); + CHECK_CKR(ret, "HKDF extract"); + } + + /* Step 3: Expand with NULL CKA_VALUE_LEN pValue - previously crashed */ + if (ret == CKR_OK) { + ret = funcList->C_DeriveKey(session, &mechExpand, hPrk, + templateExpand, templateExpandCount, &hExpandKey); + /* Before the fix, this line is never reached (NULL deref crash). + * After the fix, expect an error return. */ + CHECK_CKR_FAIL(ret, CKR_ATTRIBUTE_VALUE_INVALID, + "HKDF expand with NULL CKA_VALUE_LEN pValue"); + } + + return ret; +} #endif #ifndef NO_SHA @@ -16400,6 +16506,7 @@ static TEST_FUNC testFunc[] = { PKCS11TEST_FUNC_SESS_DECL(test_hkdf_derive_expand_with_extract_null_salt), PKCS11TEST_FUNC_SESS_DECL(test_hkdf_derive_extract_with_expand_salt_key), PKCS11TEST_FUNC_SESS_DECL(test_hkdf_gen_key), + PKCS11TEST_FUNC_SESS_DECL(test_hkdf_derive_expand_null_value_len), #endif #ifdef WOLFSSL_HAVE_PRF #ifndef NO_MD5 diff --git a/tests/rsa_exponent_test.c b/tests/rsa_exponent_test.c new file mode 100644 index 00000000..d3edc510 --- /dev/null +++ b/tests/rsa_exponent_test.c @@ -0,0 +1,548 @@ +/* rsa_exponent_test.c + * + * Copyright (C) 2006-2025 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 + * + * Test for RSA exponent byte-order handling in key generation. + * Verifies that big-endian CKA_PUBLIC_EXPONENT values are correctly + * interpreted during C_GenerateKeyPair, especially for non-palindromic + * exponents where a byte-reversal bug would produce the wrong value. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#if !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN) + +#undef HAVE_ECC +#define NO_AES +#define NO_DH +#include "testdata.h" + +/* Minimal unit test macros */ +#define CHECK_COND(cond, ret, msg) \ + do { \ + if (!(cond)) { \ + fprintf(stderr, "\n%s:%d - %s - FAIL\n", \ + __FILE__, __LINE__, msg); \ + ret = -1; \ + } \ + } \ + while (0) +#define CHECK_CKR(rv, msg) \ + do { \ + if (rv != CKR_OK) { \ + fprintf(stderr, "\n%s:%d - %s: %lx - FAIL\n", \ + __FILE__, __LINE__, msg, rv); \ + } \ + } \ + while (0) +#define CHECK_CKR_FAIL(rv, exp, msg) \ + do { \ + if (rv != exp) { \ + fprintf(stderr, "\n%s:%d - %s RETURNED %lx - FAIL\n", \ + __FILE__, __LINE__, msg, rv); \ + if (rv == CKR_OK) \ + rv = -1; \ + } \ + else \ + rv = CKR_OK; \ + } \ + while (0) + +static int verbose = 0; + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* funcList; +static CK_SLOT_ID slot = 0; +static const char* tokenName = "wolfpkcs11"; +static byte* soPin = (byte*)"password123456"; +static int soPinLen = 14; +static byte* userPin = (byte*)"wolfpkcs11-test"; +static int userPinLen = 15; + +static CK_BBOOL ckTrue = CK_TRUE; + +/* Standard palindromic exponent: 65537 = 0x010001 */ +static unsigned char exp_65537[] = { 0x01, 0x00, 0x01 }; + +/* Non-palindromic exponent: 65539 = 0x010003 + * If byte-order is reversed, this becomes 0x030001 = 196611. + * The byte-reversal bug in GetRsaExponentValue would cause the wrong + * exponent to be used during key generation, leading to a mismatch + * between the public key's exponent (from the template) and the + * private key's actual exponent (from the buggy conversion). + */ +static unsigned char exp_65539[] = { 0x01, 0x00, 0x03 }; + +static unsigned char testPlaintext[32] = { + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, + 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79 +}; + +static CK_RV pkcs11_init(void) +{ + CK_RV ret; + CK_C_INITIALIZE_ARGS args; + CK_INFO info; + CK_SLOT_ID slotList[16]; + CK_ULONG slotCount = sizeof(slotList) / sizeof(slotList[0]); + +#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 -1; + } + + func = (CK_C_GetFunctionList)dlsym(dlib, "C_GetFunctionList"); + if (func == NULL) { + fprintf(stderr, "Failed to get function list function\n"); + dlclose(dlib); + return -1; + } + + ret = func(&funcList); + if (ret != CKR_OK) { + fprintf(stderr, "Failed to get function list: %lx\n", ret); + dlclose(dlib); + return ret; + } +#else + ret = C_GetFunctionList(&funcList); + if (ret != CKR_OK) { + fprintf(stderr, "Failed to get function list: %lx\n", ret); + return ret; + } +#endif + + XMEMSET(&args, 0, sizeof(args)); + args.flags = CKF_OS_LOCKING_OK; + ret = funcList->C_Initialize(&args); + CHECK_CKR(ret, "Initialize"); + + if (ret == CKR_OK) { + ret = funcList->C_GetInfo(&info); + CHECK_CKR(ret, "Get Info"); + } + + if (ret == CKR_OK) { + ret = funcList->C_GetSlotList(CK_TRUE, slotList, &slotCount); + CHECK_CKR(ret, "Get Slot List"); + } + + if (ret == CKR_OK && slotCount > 0) { + slot = slotList[0]; + } + else if (ret == CKR_OK) { + fprintf(stderr, "No slots available\n"); + ret = CKR_GENERAL_ERROR; + } + + return ret; +} + +static CK_RV pkcs11_final(void) +{ + funcList->C_Finalize(NULL); +#ifndef HAVE_PKCS11_STATIC + if (dlib) { + dlclose(dlib); + dlib = NULL; + } +#endif + return CKR_OK; +} + +static CK_RV pkcs11_init_token(void) +{ + CK_RV ret; + unsigned char label[32]; + + XMEMSET(label, ' ', sizeof(label)); + XMEMCPY(label, tokenName, XSTRLEN(tokenName)); + + ret = funcList->C_InitToken(slot, soPin, soPinLen, label); + CHECK_CKR(ret, "Init Token"); + + return ret; +} + +static CK_RV pkcs11_set_user_pin(void) +{ + CK_RV ret; + CK_SESSION_HANDLE session; + int sessFlags = CKF_SERIAL_SESSION | CKF_RW_SESSION; + + ret = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, &session); + CHECK_CKR(ret, "Open Session for PIN setup"); + + if (ret == CKR_OK) { + ret = funcList->C_Login(session, CKU_SO, soPin, soPinLen); + CHECK_CKR(ret, "Login as SO"); + + if (ret == CKR_OK) { + ret = funcList->C_InitPIN(session, userPin, userPinLen); + CHECK_CKR(ret, "Set User PIN - Init PIN"); + } + + funcList->C_Logout(session); + funcList->C_CloseSession(session); + } + + return ret; +} + +static CK_RV pkcs11_open_session(CK_SESSION_HANDLE* session) +{ + CK_RV ret; + int sessFlags = CKF_SERIAL_SESSION | CKF_RW_SESSION; + + ret = funcList->C_OpenSession(slot, sessFlags, NULL, NULL, session); + CHECK_CKR(ret, "Open Session"); + + if (ret == CKR_OK) { + ret = funcList->C_Login(*session, CKU_USER, userPin, userPinLen); + CHECK_CKR(ret, "Login"); + } + + return ret; +} + +static CK_RV pkcs11_close_session(CK_SESSION_HANDLE session) +{ + CK_RV ret; + + ret = funcList->C_Logout(session); + CHECK_CKR(ret, "Logout"); + + if (ret == CKR_OK) { + ret = funcList->C_CloseSession(session); + CHECK_CKR(ret, "Close Session"); + } + + return ret; +} + +/* Generate an RSA key pair with the given public exponent, then verify + * the exponent is preserved and the key pair works for encrypt/decrypt. + * + * Returns CKR_OK on success (key works correctly). + * Returns non-CKR_OK if any step fails. + */ +static CK_RV generate_and_test_rsa_exponent(CK_SESSION_HANDLE session, + unsigned char* pubExp, + CK_ULONG pubExpLen, + const char* testName) +{ + CK_RV ret; + CK_OBJECT_HANDLE pubKey = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE privKey = CK_INVALID_HANDLE; + CK_ULONG bits = 2048; + CK_MECHANISM mech; + unsigned char keyId[] = { 0xEE, 0x01 }; + unsigned char readExpBuf[8]; + CK_ULONG readExpLen = sizeof(readExpBuf); + unsigned char encrypted[256]; + unsigned char decrypted[256]; + CK_ULONG encLen = sizeof(encrypted); + CK_ULONG decLen = sizeof(decrypted); + CK_MECHANISM encMech; + + CK_ATTRIBUTE pubKeyTmpl[] = { + { CKA_MODULUS_BITS, &bits, sizeof(bits) }, + { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, + { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, + { CKA_PUBLIC_EXPONENT, pubExp, pubExpLen }, + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_ID, keyId, sizeof(keyId) } + }; + int pubTmplCnt = sizeof(pubKeyTmpl) / sizeof(*pubKeyTmpl); + CK_ATTRIBUTE privKeyTmpl[] = { + { CKA_DECRYPT, &ckTrue, sizeof(ckTrue) }, + { CKA_SIGN, &ckTrue, sizeof(ckTrue) }, + { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, + { CKA_ID, keyId, sizeof(keyId) } + }; + int privTmplCnt = sizeof(privKeyTmpl) / sizeof(*privKeyTmpl); + + CK_ATTRIBUTE getExpTmpl[] = { + { CKA_PUBLIC_EXPONENT, readExpBuf, readExpLen } + }; + + printf(" %s: Generating RSA key pair with exponent 0x", testName); + { + CK_ULONG j; + for (j = 0; j < pubExpLen; j++) + printf("%02x", pubExp[j]); + } + printf("...\n"); + + /* Step 1: Generate key pair */ + mech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + mech.ulParameterLen = 0; + mech.pParameter = NULL; + + ret = funcList->C_GenerateKeyPair(session, &mech, pubKeyTmpl, + pubTmplCnt, privKeyTmpl, privTmplCnt, &pubKey, &privKey); + CHECK_CKR(ret, "RSA Generate Key Pair"); + if (ret != CKR_OK) { + fprintf(stderr, " %s: Key generation failed\n", testName); + return ret; + } + + /* Step 2: Read back the public exponent from the generated public key */ + getExpTmpl[0].ulValueLen = sizeof(readExpBuf); + ret = funcList->C_GetAttributeValue(session, pubKey, getExpTmpl, 1); + CHECK_CKR(ret, "Get Public Exponent"); + if (ret != CKR_OK) { + fprintf(stderr, " %s: Failed to read back public exponent\n", + testName); + goto cleanup; + } + + readExpLen = getExpTmpl[0].ulValueLen; + + /* Verify the exponent matches what we requested */ + if (readExpLen != pubExpLen || + XMEMCMP(readExpBuf, pubExp, pubExpLen) != 0) { + fprintf(stderr, " %s: Public exponent mismatch!\n", testName); + fprintf(stderr, " Expected: 0x"); + { + CK_ULONG j; + for (j = 0; j < pubExpLen; j++) + fprintf(stderr, "%02x", pubExp[j]); + } + fprintf(stderr, "\n Got: 0x"); + { + CK_ULONG j; + for (j = 0; j < readExpLen; j++) + fprintf(stderr, "%02x", readExpBuf[j]); + } + fprintf(stderr, "\n"); + ret = CKR_GENERAL_ERROR; + goto cleanup; + } + + printf(" %s: Public exponent read back correctly\n", testName); + + /* Step 3: Encrypt with public key */ + encMech.mechanism = CKM_RSA_PKCS; + encMech.pParameter = NULL; + encMech.ulParameterLen = 0; + + ret = funcList->C_EncryptInit(session, &encMech, pubKey); + CHECK_CKR(ret, "RSA Encrypt Init"); + if (ret != CKR_OK) { + fprintf(stderr, " %s: Encrypt init failed\n", testName); + goto cleanup; + } + + ret = funcList->C_Encrypt(session, testPlaintext, sizeof(testPlaintext), + encrypted, &encLen); + CHECK_CKR(ret, "RSA Encrypt"); + if (ret != CKR_OK) { + fprintf(stderr, " %s: Encrypt failed\n", testName); + goto cleanup; + } + + /* Step 4: Decrypt with private key */ + ret = funcList->C_DecryptInit(session, &encMech, privKey); + CHECK_CKR(ret, "RSA Decrypt Init"); + if (ret != CKR_OK) { + fprintf(stderr, " %s: Decrypt init failed\n", testName); + goto cleanup; + } + + ret = funcList->C_Decrypt(session, encrypted, encLen, + decrypted, &decLen); + CHECK_CKR(ret, "RSA Decrypt"); + if (ret != CKR_OK) { + fprintf(stderr, " %s: Decrypt failed\n", testName); + goto cleanup; + } + + /* Step 5: Verify plaintext roundtrip */ + if (decLen != sizeof(testPlaintext) || + XMEMCMP(decrypted, testPlaintext, decLen) != 0) { + fprintf(stderr, " %s: Decrypted data doesn't match original!\n", + testName); + fprintf(stderr, " Original length: %lu, decrypted length: %lu\n", + (unsigned long)sizeof(testPlaintext), (unsigned long)decLen); + ret = CKR_GENERAL_ERROR; + goto cleanup; + } + + printf(" %s: Encrypt/decrypt roundtrip PASSED\n", testName); + +cleanup: + if (pubKey != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, pubKey); + if (privKey != CK_INVALID_HANDLE) + funcList->C_DestroyObject(session, privKey); + + /* Suppress unused variable warnings from testdata.h */ + (void)rsa_2048_u; + (void)rsa_2048_dQ; + (void)rsa_2048_dP; + (void)rsa_2048_q; + (void)rsa_2048_p; + (void)rsa_2048_priv_exp; + (void)rsa_2048_modulus; + (void)rsa_2048_pub_exp; + + return ret; +} + +static CK_RV rsa_exponent_test(void) +{ + CK_RV ret; + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + + printf("RSA Exponent Byte-Order Test\n"); + printf("============================\n"); + + /* Initialize PKCS#11 */ + ret = pkcs11_init(); + if (ret != CKR_OK) { + fprintf(stderr, "Failed to initialize PKCS#11\n"); + return ret; + } + + ret = pkcs11_init_token(); + if (ret != CKR_OK) { + fprintf(stderr, "Failed to initialize token\n"); + goto cleanup; + } + + ret = pkcs11_set_user_pin(); + if (ret != CKR_OK) { + fprintf(stderr, "Failed to set user PIN\n"); + goto cleanup; + } + + ret = pkcs11_open_session(&session); + if (ret != CKR_OK) { + fprintf(stderr, "Failed to open session\n"); + goto cleanup; + } + + /* Test 1: Palindromic exponent 65537 (0x010001) - control test. + * This should always pass regardless of byte-order handling because + * the big-endian byte sequence {0x01, 0x00, 0x01} reads the same + * forwards and backwards. + */ + printf("\nTest 1: Palindromic exponent (65537 = 0x010001)\n"); + ret = generate_and_test_rsa_exponent(session, exp_65537, + sizeof(exp_65537), + "palindromic-65537"); + if (ret != CKR_OK) { + fprintf(stderr, "FAIL: Palindromic exponent test failed\n"); + goto cleanup; + } + printf(" PASSED\n"); + + /* Test 2: Non-palindromic exponent 65539 (0x010003) - bug trigger. + * If GetRsaExponentValue reads bytes in little-endian order (the bug), + * it will interpret {0x01, 0x00, 0x03} as 0x030001 = 196611 instead + * of the correct 0x010003 = 65539. The key will be generated with + * exponent 196611 but the public key object retains exponent 65539, + * causing encrypt/decrypt to fail. + */ + printf("\nTest 2: Non-palindromic exponent (65539 = 0x010003)\n"); + ret = generate_and_test_rsa_exponent(session, exp_65539, + sizeof(exp_65539), + "non-palindromic-65539"); + if (ret != CKR_OK) { + fprintf(stderr, "FAIL: Non-palindromic exponent test failed\n"); + fprintf(stderr, " This is the expected failure for bug #1311:\n"); + fprintf(stderr, " GetRsaExponentValue reads big-endian data in " + "little-endian order.\n"); + fprintf(stderr, " Exponent 65539 (0x010003) was likely interpreted " + "as 196611 (0x030001).\n"); + goto cleanup; + } + printf(" PASSED\n"); + +cleanup: + if (session != CK_INVALID_HANDLE) + pkcs11_close_session(session); + pkcs11_final(); + return ret; +} + +#endif /* !NO_RSA && WOLFSSL_KEY_GEN */ + +int main(int argc, char* argv[]) +{ +#if !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN) + CK_RV ret; + +#ifndef WOLFPKCS11_NO_ENV + if (!XGETENV("WOLFPKCS11_TOKEN_PATH")) { + XSETENV("WOLFPKCS11_TOKEN_PATH", "./store/rsa_exp_test", 1); + } +#endif + + if (argc > 1 && strcmp(argv[1], "-v") == 0) { + verbose = 1; + printf("Verbose mode enabled.\n"); + } + + printf("wolfPKCS11 RSA Exponent Byte-Order Test\n"); + printf("========================================\n\n"); + + ret = rsa_exponent_test(); + if (ret == CKR_OK) { + printf("\nAll tests passed!\n"); + return 0; + } + else { + printf("\nTest failed with error: %lx\n", ret); + return 1; + } +#else + (void)argc; + (void)argv; + printf("RSA or KeyGen not compiled in, skipping.\n"); + return 77; +#endif +}