diff --git a/CMakeLists.txt b/CMakeLists.txt index a35e365e017..5c6fba886b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4539,6 +4539,7 @@ if(WOLFSSL_EXAMPLES) tests/api/test_falcon.c tests/api/test_signature.c tests/api/test_lms_xmss.c + tests/api/test_pkcs11.c tests/api/test_dtls.c tests/api/test_dtls13.c tests/api/test_ssl_cert.c diff --git a/tests/api.c b/tests/api.c index fab89229f82..52953727280 100644 --- a/tests/api.c +++ b/tests/api.c @@ -276,6 +276,7 @@ #include #include #include +#include #include #include #include @@ -40011,6 +40012,13 @@ TEST_CASE testCases[] = { /* Signature API */ TEST_SIGNATURE_DECLS, +#if defined(HAVE_PKCS11) && defined(HAVE_ECC) && \ + defined(HAVE_ECC_VERIFY) && !defined(WC_NO_RNG) && \ + !defined(NO_ECC256) && !defined(NO_ECC_SECP) + /* PKCS #11 */ + TEST_PKCS11_DECLS, +#endif + /* ASN */ TEST_ASN_DECLS, diff --git a/tests/api/include.am b/tests/api/include.am index 849414b107c..ffe86c99e89 100644 --- a/tests/api/include.am +++ b/tests/api/include.am @@ -67,6 +67,8 @@ tests_unit_test_SOURCES += tests/api/test_slhdsa.c tests_unit_test_SOURCES += tests/api/test_falcon.c tests_unit_test_SOURCES += tests/api/test_signature.c tests_unit_test_SOURCES += tests/api/test_lms_xmss.c +# PKCS#11 +tests_unit_test_SOURCES += tests/api/test_pkcs11.c # TLS Protocol tests_unit_test_SOURCES += tests/api/test_dtls.c tests_unit_test_SOURCES += tests/api/test_dtls13.c @@ -205,6 +207,7 @@ EXTRA_DIST += tests/api/test_slhdsa.h EXTRA_DIST += tests/api/test_falcon.h EXTRA_DIST += tests/api/test_signature.h EXTRA_DIST += tests/api/test_lms_xmss.h +EXTRA_DIST += tests/api/test_pkcs11.h EXTRA_DIST += tests/api/test_dtls.h EXTRA_DIST += tests/api/test_dtls13.h EXTRA_DIST += tests/api/test_ssl_cert.h diff --git a/tests/api/test_pkcs11.c b/tests/api/test_pkcs11.c new file mode 100644 index 00000000000..eb3b14d4595 --- /dev/null +++ b/tests/api/test_pkcs11.c @@ -0,0 +1,272 @@ +/* test_pkcs11.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL 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. + * + * wolfSSL 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 + */ + +#include + +#include +#include + +#if defined(HAVE_PKCS11) && defined(HAVE_ECC) && \ + defined(HAVE_ECC_VERIFY) && !defined(WC_NO_RNG) && \ + !defined(NO_ECC256) && !defined(NO_ECC_SECP) + +#include +#include +#include + +static int test_pkcs11_verify_init_calls = 0; +static int test_pkcs11_verify_calls = 0; +static const byte* test_pkcs11_expected_sig = NULL; +static word32 test_pkcs11_expected_sig_len = 0; + +static CK_RV test_pkcs11_get_mechanism_info(CK_SLOT_ID slotId, + CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR info) +{ + (void)slotId; + + if (type != CKM_ECDSA || info == NULL) + return CKR_MECHANISM_INVALID; + + XMEMSET(info, 0, sizeof(*info)); + info->flags = CKF_VERIFY; + return CKR_OK; +} + +static CK_RV test_pkcs11_create_object(CK_SESSION_HANDLE session, + CK_ATTRIBUTE_PTR template, CK_ULONG count, CK_OBJECT_HANDLE_PTR object) +{ + (void)session; + (void)template; + (void)count; + + if (object == NULL) + return CKR_MECHANISM_INVALID; + + *object = 1; + return CKR_OK; +} + +static CK_RV test_pkcs11_destroy_object(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object) +{ + (void)session; + (void)object; + + return CKR_OK; +} + +static CK_RV test_pkcs11_verify_init(CK_SESSION_HANDLE session, + CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) +{ + (void)session; + (void)mechanism; + (void)key; + + test_pkcs11_verify_init_calls++; + return CKR_OK; +} + +static CK_RV test_pkcs11_verify(CK_SESSION_HANDLE session, CK_BYTE_PTR data, + CK_ULONG dataLen, CK_BYTE_PTR signature, CK_ULONG signatureLen) +{ + (void)session; + (void)data; + (void)dataLen; + + test_pkcs11_verify_calls++; + if (test_pkcs11_expected_sig == NULL || + signatureLen != test_pkcs11_expected_sig_len || + XMEMCMP(signature, test_pkcs11_expected_sig, signatureLen) != 0) { + return CKR_SIGNATURE_INVALID; + } + + return CKR_OK; +} + +int test_wc_Pkcs11_EcdsaSigDecode(void) +{ + static const byte emptyR[] = { + 0x30, 0x05, 0x02, 0x00, 0x02, 0x01, 0x01 + }; + static const byte emptyS[] = { + 0x30, 0x05, 0x02, 0x01, 0x01, 0x02, 0x00 + }; + static const byte hash[] = { 0x00 }; + byte oversizedR[40]; + byte oversizedS[40]; + byte paddedSig[72]; + byte expectedSig[64]; + Pkcs11Token token; + CK_FUNCTION_LIST func; + wc_CryptoInfo info; + ecc_key key; + WC_RNG rng; + int ret; + int res = 0; + int haveKey = 0; + int haveRng = 0; + EXPECT_DECLS; + + XMEMSET(&token, 0, sizeof(token)); + XMEMSET(&func, 0, sizeof(func)); + XMEMSET(&info, 0, sizeof(info)); + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(oversizedR, 0, sizeof(oversizedR)); + oversizedR[0] = 0x30; + oversizedR[1] = 0x26; + oversizedR[2] = 0x02; + oversizedR[3] = 0x21; + oversizedR[4] = 0x01; + oversizedR[37] = 0x02; + oversizedR[38] = 0x01; + oversizedR[39] = 0x01; + XMEMSET(oversizedS, 0, sizeof(oversizedS)); + oversizedS[0] = 0x30; + oversizedS[1] = 0x26; + oversizedS[2] = 0x02; + oversizedS[3] = 0x01; + oversizedS[4] = 0x01; + oversizedS[5] = 0x02; + oversizedS[6] = 0x21; + oversizedS[7] = 0x01; + XMEMSET(paddedSig, 0, sizeof(paddedSig)); + paddedSig[0] = 0x30; + paddedSig[1] = 0x46; + paddedSig[2] = 0x02; + paddedSig[3] = 0x21; + paddedSig[5] = 0x80; + paddedSig[37] = 0x02; + paddedSig[38] = 0x21; + paddedSig[40] = 0x81; + XMEMSET(expectedSig, 0, sizeof(expectedSig)); + expectedSig[0] = 0x80; + expectedSig[32] = 0x81; + + func.C_GetMechanismInfo = test_pkcs11_get_mechanism_info; + func.C_CreateObject = test_pkcs11_create_object; + func.C_DestroyObject = test_pkcs11_destroy_object; + func.C_VerifyInit = test_pkcs11_verify_init; + func.C_Verify = test_pkcs11_verify; + token.func = &func; + token.slotId = 1; + token.handle = 1; + token.version = WC_PCKS11VERSION_2_40; + + ret = wc_InitRng(&rng); + ExpectIntEQ(ret, 0); + if (ret == 0) + haveRng = 1; + if (ret == 0) + ret = wc_ecc_init(&key); + ExpectIntEQ(ret, 0); + if (ret == 0) + haveKey = 1; + if (ret == 0) + ret = wc_ecc_make_key(&rng, 32, &key); +#ifdef WOLFSSL_ASYNC_CRYPT + if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) + ret = wc_AsyncWait(ret, &key.asyncDev, WC_ASYNC_FLAG_NONE); +#endif + ExpectIntEQ(ret, 0); + + info.algo_type = WC_ALGO_TYPE_PK; + info.pk.type = WC_PK_TYPE_ECDSA_VERIFY; + info.pk.eccverify.hash = hash; + info.pk.eccverify.hashlen = sizeof(hash); + info.pk.eccverify.key = &key; + info.pk.eccverify.res = &res; + test_pkcs11_expected_sig = NULL; + test_pkcs11_expected_sig_len = 0; + + if (ret == 0) { + info.pk.eccverify.sig = emptyR; + info.pk.eccverify.siglen = sizeof(emptyR); + res = 0; + test_pkcs11_verify_init_calls = 0; + test_pkcs11_verify_calls = 0; + ExpectIntEQ(wc_Pkcs11_CryptoDevCb(1, &info, &token), + WC_NO_ERR_TRACE(ASN_PARSE_E)); + ExpectIntEQ(test_pkcs11_verify_init_calls, 0); + ExpectIntEQ(test_pkcs11_verify_calls, 0); + } + + if (ret == 0) { + info.pk.eccverify.sig = emptyS; + info.pk.eccverify.siglen = sizeof(emptyS); + res = 0; + test_pkcs11_verify_init_calls = 0; + test_pkcs11_verify_calls = 0; + ExpectIntEQ(wc_Pkcs11_CryptoDevCb(1, &info, &token), + WC_NO_ERR_TRACE(ASN_PARSE_E)); + ExpectIntEQ(test_pkcs11_verify_init_calls, 0); + ExpectIntEQ(test_pkcs11_verify_calls, 0); + } + + if (ret == 0) { + info.pk.eccverify.sig = oversizedR; + info.pk.eccverify.siglen = sizeof(oversizedR); + res = 0; + test_pkcs11_verify_init_calls = 0; + test_pkcs11_verify_calls = 0; + ExpectIntEQ(wc_Pkcs11_CryptoDevCb(1, &info, &token), + WC_NO_ERR_TRACE(ASN_PARSE_E)); + ExpectIntEQ(test_pkcs11_verify_init_calls, 0); + ExpectIntEQ(test_pkcs11_verify_calls, 0); + } + + if (ret == 0) { + info.pk.eccverify.sig = oversizedS; + info.pk.eccverify.siglen = sizeof(oversizedS); + res = 0; + test_pkcs11_verify_init_calls = 0; + test_pkcs11_verify_calls = 0; + ExpectIntEQ(wc_Pkcs11_CryptoDevCb(1, &info, &token), + WC_NO_ERR_TRACE(ASN_PARSE_E)); + ExpectIntEQ(test_pkcs11_verify_init_calls, 0); + ExpectIntEQ(test_pkcs11_verify_calls, 0); + } + + if (ret == 0) { + info.pk.eccverify.sig = paddedSig; + info.pk.eccverify.siglen = sizeof(paddedSig); + res = 0; + test_pkcs11_verify_init_calls = 0; + test_pkcs11_verify_calls = 0; + test_pkcs11_expected_sig = expectedSig; + test_pkcs11_expected_sig_len = sizeof(expectedSig); + ExpectIntEQ(wc_Pkcs11_CryptoDevCb(1, &info, &token), 0); + ExpectIntEQ(res, 1); + ExpectIntEQ(test_pkcs11_verify_init_calls, 1); + ExpectIntEQ(test_pkcs11_verify_calls, 1); + } + + test_pkcs11_expected_sig = NULL; + test_pkcs11_expected_sig_len = 0; + if (haveKey) + wc_ecc_free(&key); + if (haveRng) + DoExpectIntEQ(wc_FreeRng(&rng), 0); + + return EXPECT_RESULT(); +} + +#endif diff --git a/tests/api/test_pkcs11.h b/tests/api/test_pkcs11.h new file mode 100644 index 00000000000..cadf54266f2 --- /dev/null +++ b/tests/api/test_pkcs11.h @@ -0,0 +1,42 @@ +/* test_pkcs11.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL 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. + * + * wolfSSL 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 + */ + +#ifndef WOLFCRYPT_TEST_PKCS11_H +#define WOLFCRYPT_TEST_PKCS11_H + +#include + +#if defined(HAVE_PKCS11) && defined(HAVE_ECC) && \ + defined(HAVE_ECC_VERIFY) && !defined(WC_NO_RNG) && \ + !defined(NO_ECC256) && !defined(NO_ECC_SECP) + +int test_wc_Pkcs11_EcdsaSigDecode(void); + +#define TEST_PKCS11_DECLS \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_EcdsaSigDecode) + +#else + +#define TEST_PKCS11_DECLS + +#endif + +#endif /* WOLFCRYPT_TEST_PKCS11_H */ diff --git a/wolfcrypt/src/wc_pkcs11.c b/wolfcrypt/src/wc_pkcs11.c index b41a8716316..d0483e837c3 100644 --- a/wolfcrypt/src/wc_pkcs11.c +++ b/wolfcrypt/src/wc_pkcs11.c @@ -3705,8 +3705,8 @@ static int Pkcs11ECDSASig_Decode(const byte* in, word32 inSz, byte* sig, { int ret = 0; word32 i = 0; + word32 len, seqLen = 2; byte tag; - int len, seqLen = 2; /* Make sure zeros in place when decoding short integers. */ XMEMSET(sig, 0, sz * 2); @@ -3733,10 +3733,12 @@ static int Pkcs11ECDSASig_Decode(const byte* in, word32 inSz, byte* sig, ret = ASN_PARSE_E; if (ret == 0 && tag != ASN_INTEGER) ret = ASN_PARSE_E; - if (ret == 0 && (len = in[i++]) > sz + 1) + if (ret == 0 && i >= inSz) + ret = ASN_PARSE_E; + if (ret == 0 && (len = in[i++]) == 0) ret = ASN_PARSE_E; /* Check there is space for INT data */ - if (ret == 0 && i + len > inSz) + if (ret == 0 && (i > inSz || len > inSz - i)) ret = ASN_PARSE_E; if (ret == 0) { /* Skip leading zero */ @@ -3744,23 +3746,29 @@ static int Pkcs11ECDSASig_Decode(const byte* in, word32 inSz, byte* sig, i++; len--; } + if (len > sz) + ret = ASN_PARSE_E; + } + if (ret == 0) { /* Copy r into sig. */ XMEMCPY(sig + sz - len, in + i, len); i += len; } /* Check min data for: INT. */ - if (ret == 0 && i + 2 > inSz) + if (ret == 0 && (i > inSz || inSz - i < 2)) ret = ASN_PARSE_E; /* Check INT */ if (ret == 0 && GetASNTag(in, &i, &tag, inSz) != 0) ret = ASN_PARSE_E; if (ret == 0 && tag != ASN_INTEGER) ret = ASN_PARSE_E; - if (ret == 0 && (len = in[i++]) > sz + 1) + if (ret == 0 && i >= inSz) + ret = ASN_PARSE_E; + if (ret == 0 && (len = in[i++]) == 0) ret = ASN_PARSE_E; /* Check there is space for INT data */ - if (ret == 0 && i + len > inSz) + if (ret == 0 && (i > inSz || len > inSz - i)) ret = ASN_PARSE_E; if (ret == 0) { /* Skip leading zero */ @@ -3768,6 +3776,10 @@ static int Pkcs11ECDSASig_Decode(const byte* in, word32 inSz, byte* sig, i++; len--; } + if (len > sz) + ret = ASN_PARSE_E; + } + if (ret == 0) { /* Copy s into sig. */ XMEMCPY(sig + sz + sz - len, in + i, len); }