From d7e2a8a3ba17feaf02b9fb7d9bf10ec32bf4c968 Mon Sep 17 00:00:00 2001 From: Tobias Deiminger Date: Sat, 28 Mar 2026 10:18:15 +0100 Subject: [PATCH 1/3] tests: Add PKCS#7 verification interoperability test Intention is to verify PKCS#7 / CMS signatures produced by third party tools. The optional test is enabled by providing a comma separated list of external DER files that have previously been signed with a third party tool to configure --with-pkcs7-test-signed-data. Usage: openssl cms -sign -in <(echo -n "openssl cms interop test message") \ -signer certs/client-cert.pem -inkey certs/client-key.pem \ -md sha256 -nodetach -outform DER -out /tmp/cms.der openssl smime -sign -in <(echo -n "openssl smime interop test message") \ -signer certs/client-cert.pem -inkey certs/client-key.pem \ -md sha256 -binary -nodetach -outform DER -out /tmp/smime.der ./configure --enable-pkcs7 --enable-certext --enable-examples \ --with-pkcs7-test-signed-data=/tmp/cms.der,/tmp/smime.der make -j$(nproc) tests/unit.test -test_wc_PKCS7_VerifySignedData_interop --- configure.ac | 11 +++++++ tests/api/test_pkcs7.c | 68 ++++++++++++++++++++++++++++++++++++++++++ tests/api/test_pkcs7.h | 9 ++++++ 3 files changed, 88 insertions(+) diff --git a/configure.ac b/configure.ac index c4948ba406..fd0666a472 100644 --- a/configure.ac +++ b/configure.ac @@ -10480,6 +10480,17 @@ if test "x$ENABLED_OLD_EXTDATA_FMT" = "xyes"; then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_OLD_EXTDATA_FMT" fi +# Optional PKCS#7 SignedData interoperability test +AC_ARG_WITH([pkcs7-test-signed-data], + [AS_HELP_STRING([--with-pkcs7-test-signed-data=FILE[,FILE,...]], + [External test data for optional PKCS#7 interoperability test])], + [ + AC_DEFINE([HAVE_PKCS7_TEST_SIGNED_DATA_FILES], [1], + [Define to enable PKCS#7 SignedData interoperability test]) + AC_DEFINE_UNQUOTED([PKCS7_TEST_SIGNED_DATA_FILES], ["$withval"], + [Comma-separated list of DER-encoded CMS SignedData files for interop test]) + ]) + # determine if we have key validation mechanism if test "x$ENABLED_ECC" != "xno" || test "x$ENABLED_RSA" = "xyes" then diff --git a/tests/api/test_pkcs7.c b/tests/api/test_pkcs7.c index 3c8b59fb8e..ff5355e635 100644 --- a/tests/api/test_pkcs7.c +++ b/tests/api/test_pkcs7.c @@ -5000,3 +5000,71 @@ int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void) #endif /* HAVE_PKCS7 && !NO_PKCS7_STREAM */ return EXPECT_RESULT(); } + +#if defined(HAVE_PKCS7) && defined(HAVE_PKCS7_TEST_SIGNED_DATA_FILES) && \ + !defined(NO_FILESYSTEM) +static int pkcs7_verify_one_signed_data_der_file(const char* path) +{ + EXPECT_DECLS; + XFILE f = XBADFILE; + long signedDataSz; + byte* signedData = NULL; + PKCS7* pkcs7 = NULL; + + ExpectTrue((f = XFOPEN(path, "rb")) != XBADFILE); + ExpectIntEQ(XFSEEK(f, 0, XSEEK_END), 0); + ExpectIntGT(signedDataSz = XFTELL(f), 0); + ExpectIntEQ(XFSEEK(f, 0, XSEEK_SET), 0); + + ExpectNotNull(signedData = (byte*)XMALLOC((word32)signedDataSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectIntEQ((long)XFREAD(signedData, 1, (word32)signedDataSz, f), + signedDataSz); + if (f != XBADFILE) + XFCLOSE(f); + + ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID)); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0); + + /* test signature verification and message content */ + ExpectIntEQ(wc_PKCS7_VerifySignedData(pkcs7, + signedData, + (word32)signedDataSz), 0); + ExpectNotNull(pkcs7->contentDynamic); + + XFREE(signedData, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wc_PKCS7_Free(pkcs7); + return EXPECT_RESULT(); +} + +/* Verify CMS SignedData DER files created with third party tools. + * + * This test verifies signatures created with external tools can be verified by + * WolfSSL. Standards allow some variance when creating CMS signatures. The + * test is intended to catch incompatibility from subtle differences in + * implementation. + * + * DER files are supplied at configure time: + * configure --with-pkcs7-test-signed-data=openssl_cms.der,openssl_smime.der + */ +int test_wc_PKCS7_VerifySignedData_interop(void) +{ + EXPECT_DECLS; + char pathsTokenBuf[FOURK_BUF]; + char* nextPath; + char* tokState = NULL; + + ExpectTrue(XSTRLEN(PKCS7_TEST_SIGNED_DATA_FILES) < sizeof(pathsTokenBuf)); + XSTRNCPY(pathsTokenBuf, PKCS7_TEST_SIGNED_DATA_FILES, sizeof(pathsTokenBuf)); + + nextPath = XSTRTOK(pathsTokenBuf, ",", &tokState); + while (nextPath != NULL) { + Expect(pkcs7_verify_one_signed_data_der_file(nextPath) == TEST_SUCCESS, + ("%s signature verification succeeds", nextPath), + ("%s signature verification failed", nextPath)); + nextPath = XSTRTOK(NULL, ",", &tokState); + } + return EXPECT_RESULT(); +} +#endif /* HAVE_PKCS7 && HAVE_PKCS7_TEST_SIGNED_DATA_FILES && !NO_FILESYSTEM */ + diff --git a/tests/api/test_pkcs7.h b/tests/api/test_pkcs7.h index 084744d43c..3c1a19f07f 100644 --- a/tests/api/test_pkcs7.h +++ b/tests/api/test_pkcs7.h @@ -59,6 +59,14 @@ int test_wc_PKCS7_DecodeCompressedData(void); int test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients(void); int test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq(void); int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void); +#if defined(HAVE_PKCS7) && defined(HAVE_PKCS7_TEST_SIGNED_DATA_FILES) && \ + !defined(NO_FILESYSTEM) +int test_wc_PKCS7_VerifySignedData_interop(void); +#define TEST_PKCS7_INTEROP_VERIFY_SD_DECL \ + TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_interop), +#else +#define TEST_PKCS7_INTEROP_VERIFY_SD_DECL +#endif #define TEST_PKCS7_DECLS \ @@ -94,6 +102,7 @@ int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void); TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_BER), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_NoDefaultSignedAttribs), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq), \ + TEST_PKCS7_INTEROP_VERIFY_SD_DECL \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_IndefLenOOB) #define TEST_PKCS7_ENCRYPTED_DATA_DECLS \ From dc7bf76d199e7b87f02a6fff42f0f05f5efb25d1 Mon Sep 17 00:00:00 2001 From: Tobias Deiminger Date: Sun, 29 Mar 2026 16:58:58 +0200 Subject: [PATCH 2/3] .github: Test PKCS7 interoperability for OpenSSL and GnuTLS --- .github/workflows/pkcs7-interop.yml | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/pkcs7-interop.yml diff --git a/.github/workflows/pkcs7-interop.yml b/.github/workflows/pkcs7-interop.yml new file mode 100644 index 0000000000..c14a862cb2 --- /dev/null +++ b/.github/workflows/pkcs7-interop.yml @@ -0,0 +1,55 @@ +name: PKCS7 Interoperability + +# START OF COMMON SECTION +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +# END OF COMMON SECTION + +jobs: + pkcs7_interop: + name: PKCS7 interop (OpenSSL, GnuTLS) + if: github.repository_owner == 'wolfssl' + runs-on: ubuntu-24.04 + timeout-minutes: 14 + steps: + - uses: actions/checkout@v4 + + - name: Install 3rd party PKCS#7 tools + run: sudo apt-get install -y openssl gnutls-bin + + - name: Sign with 3rd party PKCS#7 tools + run: | + echo -n "pkcs7 interop test" > $RUNNER_TEMP/content.bin + openssl req -x509 -newkey rsa:2048 -keyout $RUNNER_TEMP/key.pem \ + -out $RUNNER_TEMP/cert.pem -days 1 -nodes \ + -subj "/CN=wolfssl-pkcs7-interop-test" + openssl cms -sign -binary -nodetach \ + -in $RUNNER_TEMP/content.bin \ + -signer $RUNNER_TEMP/cert.pem -inkey $RUNNER_TEMP/key.pem \ + -md sha256 -outform DER -out $RUNNER_TEMP/openssl_cms.der + openssl smime -sign -binary -nodetach \ + -in $RUNNER_TEMP/content.bin \ + -signer $RUNNER_TEMP/cert.pem -inkey $RUNNER_TEMP/key.pem \ + -md sha256 -outform DER -out $RUNNER_TEMP/openssl_smime.der + certtool --p7-sign \ + --infile $RUNNER_TEMP/content.bin \ + --load-certificate $RUNNER_TEMP/cert.pem \ + --load-privkey $RUNNER_TEMP/key.pem \ + --outder --outfile $RUNNER_TEMP/gnutls.der + + - name: Build wolfSSL + run: | + ./autogen.sh + ./configure --enable-pkcs7 --enable-certext --enable-examples \ + --with-pkcs7-test-signed-data=$RUNNER_TEMP/openssl_cms.der,$RUNNER_TEMP/openssl_smime.der,$RUNNER_TEMP/gnutls.der + make -j$(nproc) + + - name: Run PKCS7 interop test + run: tests/unit.test -test_wc_PKCS7_VerifySignedData_interop From 7bae3087d0911e08235808d7d37add622ee76eab Mon Sep 17 00:00:00 2001 From: Tobias Deiminger Date: Fri, 27 Mar 2026 21:48:36 +0100 Subject: [PATCH 3/3] wolfcrypt/src/pkcs7.c: Fix PKCS#7 verification for digestAlgorithm.parameters = NULL RFC 8017 hardcodes DER serialization samples of DigestInfo, where the parameter part is always NULL (05 00) for known hash algorithm [1]. This value does thus *not* depend on SignerInfo.digestAlgorithm.parameters. Starting with 75c303055 ("Add option for absent hash params in PKCS7"), wolfSSL wrongly assumed and implemented such a dependency. This non-conformance caused an interoperability bug with OpenSSL: A signature created with openssl cms could not be verified in WolfSSL. OpenSSL correctly leaves SignerInfo.digestAlgorithm.parameters absent and adds explicit NULL to DigestInfo. WolfSSL saw the absence and wrongly inferred DigestInfo would also have no explicit NULL - but it has - leading to size mismatch. Fix it by constructing the expected DigestInfo always with NULL (05 00). 4f21117f3 ("tests: Add PKCS#7 verification interoperability test") and 8d8170ea2 (".github: Test PKCS7 interoperability for OpenSSL and GnuTLS") can be used to reproduce the bug and to demonstrate this commit fixes it. [1] https://www.rfc-editor.org/rfc/rfc8017#section-9.2 --- wolfcrypt/src/pkcs7.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index c46885b168..9080417f3a 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -4779,9 +4779,7 @@ static int wc_PKCS7_BuildSignedDataDigest(wc_PKCS7* pkcs7, byte* signedAttrib, } } - /* Set algoID, match whatever was input to match either NULL or absent */ - algoIdSz = SetAlgoIDEx(pkcs7->hashOID, algoId, oidHashType, - 0, pkcs7->hashParamsAbsent); + algoIdSz = SetAlgoID(pkcs7->hashOID, algoId, oidHashType, 0); digestStrSz = SetOctetString(hashSz, digestStr); digestInfoSeqSz = SetSequence(algoIdSz + digestStrSz + hashSz,