From d0c04863443a63d1efb73f03319e80b3172f4ecf Mon Sep 17 00:00:00 2001 From: Takashi Kojo Date: Fri, 4 Sep 2026 17:06:29 +0900 Subject: [PATCH 1/2] PKCS7: decode detached SignedData in d2i_PKCS7 --- doc/dox_comments/header_files/pkcs7.h | 44 ++++++++ src/ssl_p7p12.c | 32 +++++- tests/api/test_ossl_p7p12.c | 141 +++++++++++++++++++++++++- tests/api/test_ossl_p7p12.h | 2 + tests/api/test_pkcs7.c | 87 ++++++++++++++++ tests/api/test_pkcs7.h | 2 + wolfcrypt/src/pkcs7.c | 30 +++++- wolfssl/openssl/pkcs7.h | 6 ++ wolfssl/wolfcrypt/pkcs7.h | 5 +- 9 files changed, 336 insertions(+), 13 deletions(-) diff --git a/doc/dox_comments/header_files/pkcs7.h b/doc/dox_comments/header_files/pkcs7.h index d7f679fafd2..a389a3a514f 100644 --- a/doc/dox_comments/header_files/pkcs7.h +++ b/doc/dox_comments/header_files/pkcs7.h @@ -397,6 +397,50 @@ int wc_PKCS7_EncodeSignedData_ex(wc_PKCS7* pkcs7, const byte* hashBuf, int wc_PKCS7_VerifySignedData(wc_PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz); +/*! + \ingroup PKCS7 + + \brief This function decodes a PKCS7 SignedData message without requiring + its content. Certificates, the signer information and the content type are + parsed and stored in the PKCS7 structure like wc_PKCS7_VerifySignedData() + does. A message with attached content is verified while decoding. A + detached message whose content has not been set is only decoded: + pkcs7->detached is set and the signature is verified later by calling + wc_PKCS7_VerifySignedData() once the content is available. + + \return 0 Returned on successfully decoding the message + \return BAD_FUNC_ARG Returned if pkcs7 is NULL + \return Any error returned by wc_PKCS7_VerifySignedData() on a malformed + message, or on a failed verification of attached content + + \param pkcs7 pointer to the PKCS7 structure in which to store the parsed + certificates and signer information + \param pkiMsg pointer to the buffer containing the signed message to decode + \param pkiMsgSz size of the signed message + + _Example_ + \code + PKCS7 pkcs7; + int ret; + byte pkcs7Buff[] = {}; // the PKCS7 signature + byte content[] = {}; // the detached content + + wc_PKCS7_InitWithCert(&pkcs7, NULL, 0); + ret = wc_PKCS7_DecodeSignedData(&pkcs7, pkcs7Buff, sizeof(pkcs7Buff)); + if (ret == 0 && pkcs7.detached) { + pkcs7.content = content; + pkcs7.contentSz = sizeof(content); + ret = wc_PKCS7_VerifySignedData(&pkcs7, pkcs7Buff, sizeof(pkcs7Buff)); + } + wc_PKCS7_Free(&pkcs7); + \endcode + + \sa wc_PKCS7_VerifySignedData + \sa wc_PKCS7_InitWithCert +*/ +int wc_PKCS7_DecodeSignedData(wc_PKCS7* pkcs7, + byte* pkiMsg, word32 pkiMsgSz); + /*! \ingroup PKCS7 diff --git a/src/ssl_p7p12.c b/src/ssl_p7p12.c index 51054006f41..6ea164b2fdd 100644 --- a/src/ssl_p7p12.c +++ b/src/ssl_p7p12.c @@ -106,7 +106,9 @@ void wolfSSL_PKCS7_SIGNED_free(PKCS7_SIGNED* p7) /** * Convert DER/ASN.1 encoded signedData structure to internal PKCS7 - * structure. Note, does not support detached content. + * structure. An attached signature is verified while decoding. A detached + * signature is only decoded; verify it with wolfSSL_PKCS7_verify() and the + * content. * * p7 - pointer to set to address of newly created PKCS7 structure on return * in - pointer to pointer of DER/ASN.1 data @@ -182,20 +184,44 @@ PKCS7* wolfSSL_d2i_PKCS7_ex(PKCS7** p7, const unsigned char** in, int len, pkcs7 = (WOLFSSL_PKCS7*)wolfSSL_d2i_PKCS7_only(p7, in, len, content, contentSz); if (pkcs7 != NULL) { - if (wc_PKCS7_VerifySignedData(&pkcs7->pkcs7, pkcs7->data, pkcs7->len) + if (wc_PKCS7_DecodeSignedData(&pkcs7->pkcs7, pkcs7->data, pkcs7->len) != 0) { - WOLFSSL_MSG("wc_PKCS7_VerifySignedData failed"); + WOLFSSL_MSG("wc_PKCS7_DecodeSignedData failed"); wolfSSL_PKCS7_free((PKCS7*)pkcs7); if (p7 != NULL) { *p7 = NULL; } return NULL; } + pkcs7->type = SIGNED_DATA; } return (PKCS7*)pkcs7; } +/* Returns 1 if the PKCS7 is a SignedData structure, otherwise 0. */ +int wolfSSL_PKCS7_type_is_signed(PKCS7* pkcs7) +{ + WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7; + + if (p7 == NULL) + return 0; + + return p7->type == SIGNED_DATA; +} + +/* Returns 1 if the decoded SignedData carries no content (detached + * signature), otherwise 0. */ +int wolfSSL_PKCS7_get_detached(PKCS7* pkcs7) +{ + WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7; + + if (p7 == NULL) + return 0; + + return p7->pkcs7.detached != 0; +} + /** * This API was added as a helper function for libest. It diff --git a/tests/api/test_ossl_p7p12.c b/tests/api/test_ossl_p7p12.c index b8a375503f6..a9124315832 100644 --- a/tests/api/test_ossl_p7p12.c +++ b/tests/api/test_ossl_p7p12.c @@ -339,7 +339,7 @@ int test_wolfSSL_PKCS7_sign(void) ExpectIntGT((outLen = i2d_PKCS7(p7, &out)), 0); ExpectNotNull(out); - /* verify with wolfCrypt, d2i_PKCS7 does not support detached content */ + /* verify with wolfCrypt */ ExpectNotNull(p7Ver = wc_PKCS7_New(HEAP_HINT, testDevId)); if (p7Ver != NULL) { p7Ver->content = data; @@ -371,10 +371,16 @@ int test_wolfSSL_PKCS7_sign(void) p7Ver = NULL; #endif /* !NO_PKCS7_STREAM */ - /* verify expected failure (NULL return) from d2i_PKCS7, it does not - * yet support detached content */ + /* d2i_PKCS7 decodes the detached bundle, PKCS7_verify is then given + * the content */ tmpPtr = out; - ExpectNull(p7Ver = d2i_PKCS7(NULL, (const byte**)&tmpPtr, outLen)); + ExpectNotNull(p7Ver = d2i_PKCS7(NULL, (const byte**)&tmpPtr, outLen)); + ExpectIntEQ(PKCS7_is_detached(p7Ver), 1); + BIO_free(inBio); + inBio = NULL; + ExpectNotNull(inBio = BIO_new_mem_buf(data, sizeof(data))); + ExpectIntEQ(PKCS7_verify(p7Ver, NULL, NULL, inBio, NULL, + PKCS7_BINARY | PKCS7_NOVERIFY), 1); PKCS7_free(p7Ver); p7Ver = NULL; @@ -397,7 +403,7 @@ int test_wolfSSL_PKCS7_sign(void) ExpectIntEQ(PKCS7_final(p7, inBio, flags), 1); ExpectIntGT((outLen = i2d_PKCS7(p7, &out)), 0); - /* verify with wolfCrypt, d2i_PKCS7 does not support detached content */ + /* verify with wolfCrypt */ ExpectNotNull(p7Ver = wc_PKCS7_New(HEAP_HINT, testDevId)); if (p7Ver != NULL) { p7Ver->content = data; @@ -675,6 +681,131 @@ int test_wolfSSL_PKCS7_verify_signer_forgery(void) return EXPECT_RESULT(); } +/* d2i_PKCS7() decodes a detached SignedData so that PKCS7_verify() can be + * handed the content afterwards, as with OpenSSL. An attached SignedData is + * still verified while decoding. */ +int test_wolfSSL_PKCS7_d2i_detached(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_ALL) && defined(HAVE_PKCS7) && !defined(NO_BIO) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) + const char* signerCertFile = "./certs/server-cert.pem"; + const char* signerKeyFile = "./certs/server-key.pem"; + const char* caFile = "./certs/ca-cert.pem"; + byte content[] = "Detached content to verify."; + + WOLFSSL_BIO* certBio = NULL; + WOLFSSL_BIO* keyBio = NULL; + WOLFSSL_BIO* caBio = NULL; + WOLFSSL_BIO* signBio = NULL; + WOLFSSL_BIO* inBio = NULL; + X509* signCert = NULL; + EVP_PKEY* signKey = NULL; + X509* caCert = NULL; + X509_STORE* store = NULL; + PKCS7* p7 = NULL; + PKCS7* p7Dec = NULL; + WOLFSSL_STACK* signers = NULL; + byte* der = NULL; + int derSz = 0; + const byte* p = NULL; + int i; + + ExpectNotNull(certBio = BIO_new_file(signerCertFile, "r")); + ExpectNotNull(keyBio = BIO_new_file(signerKeyFile, "r")); + ExpectNotNull(caBio = BIO_new_file(caFile, "r")); + ExpectNotNull(signCert = PEM_read_bio_X509(certBio, NULL, 0, NULL)); + ExpectNotNull(signKey = PEM_read_bio_PrivateKey(keyBio, NULL, 0, NULL)); + ExpectNotNull(caCert = PEM_read_bio_X509(caBio, NULL, 0, NULL)); + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, caCert), 1); + + /* detached signature over 'content' */ + ExpectNotNull(signBio = BIO_new(BIO_s_mem())); + ExpectIntGT(BIO_write(signBio, content, sizeof(content)), 0); + ExpectNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, signBio, + PKCS7_BINARY | PKCS7_DETACHED)); + ExpectIntGT((derSz = i2d_PKCS7(p7, &der)), 0); + ExpectNotNull(der); + + /* decodes without the content */ + p = der; + ExpectNotNull(p7Dec = d2i_PKCS7(NULL, &p, derSz)); + ExpectTrue(p == der + derSz); + ExpectIntEQ(PKCS7_type_is_signed(p7Dec), 1); + ExpectIntEQ(PKCS7_get_detached(p7Dec), 1); + ExpectIntEQ(PKCS7_is_detached(p7Dec), 1); + + /* the embedded signer certificate is available right after decoding */ + ExpectNotNull(signers = PKCS7_get0_signers(p7Dec, NULL, 0)); + ExpectIntEQ(sk_X509_num(signers), 1); + sk_X509_pop_free(signers, NULL); + signers = NULL; + + /* verify with the content supplied through the BIO */ + ExpectNotNull(inBio = BIO_new_mem_buf(content, sizeof(content))); + ExpectIntEQ(PKCS7_verify(p7Dec, NULL, store, inBio, NULL, PKCS7_BINARY), + 1); + BIO_free(inBio); + inBio = NULL; + + /* wrong content still fails */ + ExpectNotNull(inBio = BIO_new_mem_buf("bogus content", 13)); + ExpectIntEQ(PKCS7_verify(p7Dec, NULL, store, inBio, NULL, PKCS7_BINARY), + WC_NO_ERR_TRACE(WOLFSSL_FAILURE)); + BIO_free(inBio); + inBio = NULL; + + PKCS7_free(p7Dec); + p7Dec = NULL; + PKCS7_free(p7); + p7 = NULL; + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + der = NULL; + BIO_free(signBio); + signBio = NULL; + + /* attached signature: verified while decoding, as before */ + ExpectNotNull(signBio = BIO_new(BIO_s_mem())); + ExpectIntGT(BIO_write(signBio, content, sizeof(content)), 0); + ExpectNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, signBio, + PKCS7_BINARY)); + ExpectIntGT((derSz = i2d_PKCS7(p7, &der)), 0); + ExpectNotNull(der); + p = der; + ExpectNotNull(p7Dec = d2i_PKCS7(NULL, &p, derSz)); + ExpectIntEQ(PKCS7_type_is_signed(p7Dec), 1); + ExpectIntEQ(PKCS7_is_detached(p7Dec), 0); + PKCS7_free(p7Dec); + p7Dec = NULL; + + /* tampered attached content is rejected by d2i_PKCS7() */ + if (der != NULL) { + for (i = 0; i + (int)sizeof(content) <= derSz; i++) { + if (XMEMCMP(der + i, content, sizeof(content)) == 0) { + der[i] ^= 0x01; + break; + } + } + ExpectTrue(i + (int)sizeof(content) <= derSz); + } + p = der; + ExpectNull(d2i_PKCS7(NULL, &p, derSz)); + + PKCS7_free(p7); + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + X509_STORE_free(store); + X509_free(caCert); + X509_free(signCert); + EVP_PKEY_free(signKey); + BIO_free(signBio); + BIO_free(certBio); + BIO_free(keyBio); + BIO_free(caBio); +#endif + return EXPECT_RESULT(); +} + /* A degenerate (certs-only) PKCS#7 - one with an empty signerInfos SET and * therefore no signature at all - must NOT be reported as verified, even when * the embedded certificate chains to a trusted CA, and even when diff --git a/tests/api/test_ossl_p7p12.h b/tests/api/test_ossl_p7p12.h index 8ab8fd79530..e83cb21f94f 100644 --- a/tests/api/test_ossl_p7p12.h +++ b/tests/api/test_ossl_p7p12.h @@ -30,6 +30,7 @@ int test_wolfSSL_PKCS7_sign(void); int test_wolfSSL_PKCS7_verify_signer_forgery(void); int test_wolfSSL_PKCS7_verify_sid_binding(void); int test_wolfSSL_PKCS7_verify_degenerate(void); +int test_wolfSSL_PKCS7_d2i_detached(void); int test_wolfSSL_PKCS7_SIGNED_new(void); int test_wolfSSL_PEM_write_bio_PKCS7(void); int test_wolfSSL_PEM_write_bio_encryptedKey(void); @@ -44,6 +45,7 @@ int test_wolfSSL_PKCS12(void); TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_verify_signer_forgery), \ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_verify_sid_binding), \ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_verify_degenerate), \ + TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_d2i_detached), \ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_SIGNED_new), \ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PEM_write_bio_PKCS7), \ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PEM_write_bio_encryptedKey), \ diff --git a/tests/api/test_pkcs7.c b/tests/api/test_pkcs7.c index 982aab81bda..b14bdc22535 100644 --- a/tests/api/test_pkcs7.c +++ b/tests/api/test_pkcs7.c @@ -6915,6 +6915,93 @@ int test_wc_PKCS7_VerifySignedData_TruncCertSetTag(void) * parser can see, and must still succeed after fixing that check to reject * truncated bundles. */ +/* wc_PKCS7_DecodeSignedData() decodes a detached bundle without its content + * and leaves the signature for a later wc_PKCS7_VerifySignedData(). An + * attached bundle is verified while decoding. */ +int test_wc_PKCS7_DecodeSignedData(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) + PKCS7* pkcs7 = NULL; + byte output[6000]; + word32 outputSz = sizeof(output); + byte data[] = "Test data to encode."; + byte badData[] = "This is different content than was signed"; + word32 i; + + /* detached bundle */ + ExpectIntGT((outputSz = (word32)CreatePKCS7SignedData(output, + (int)outputSz, data, (word32)sizeof(data), 0, 1, 0, RSA_TYPE)), 0); + + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0); + ExpectIntEQ(wc_PKCS7_DecodeSignedData(pkcs7, output, outputSz), 0); + if (pkcs7 != NULL) { + ExpectIntEQ(pkcs7->detached, 1); + ExpectNotNull(pkcs7->singleCert); + ExpectIntGT(pkcs7->singleCertSz, 0); + /* nothing was verified */ + ExpectNull(pkcs7->verifyCert); + } + + /* verifying without the content still fails */ + ExpectIntLT(wc_PKCS7_VerifySignedData(pkcs7, output, outputSz), 0); + + /* verify once the content is supplied */ + if (pkcs7 != NULL) { + pkcs7->content = data; + pkcs7->contentSz = (word32)sizeof(data); + } + ExpectIntEQ(wc_PKCS7_VerifySignedData(pkcs7, output, outputSz), 0); + if (pkcs7 != NULL) { + ExpectNotNull(pkcs7->verifyCert); + } + + /* wrong content is rejected */ + if (pkcs7 != NULL) { + pkcs7->content = badData; + pkcs7->contentSz = (word32)sizeof(badData); + } + ExpectIntLT(wc_PKCS7_VerifySignedData(pkcs7, output, outputSz), 0); + + wc_PKCS7_Free(pkcs7); + pkcs7 = NULL; + + /* attached bundle is verified while decoding */ + outputSz = sizeof(output); + ExpectIntGT((outputSz = (word32)CreatePKCS7SignedData(output, + (int)outputSz, data, (word32)sizeof(data), 0, 0, 0, RSA_TYPE)), 0); + + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0); + ExpectIntEQ(wc_PKCS7_DecodeSignedData(pkcs7, output, outputSz), 0); + if (pkcs7 != NULL) { + ExpectIntEQ(pkcs7->detached, 0); + ExpectNotNull(pkcs7->verifyCert); + } + wc_PKCS7_Free(pkcs7); + pkcs7 = NULL; + + /* tampered attached content is rejected */ + for (i = 0; i + sizeof(data) <= outputSz; i++) { + if (XMEMCMP(output + i, data, sizeof(data)) == 0) { + output[i] ^= 0x01; + break; + } + } + ExpectTrue(i + sizeof(data) <= outputSz); + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0); + ExpectIntLT(wc_PKCS7_DecodeSignedData(pkcs7, output, outputSz), 0); + wc_PKCS7_Free(pkcs7); + pkcs7 = NULL; + + ExpectIntEQ(wc_PKCS7_DecodeSignedData(NULL, output, outputSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#endif + return EXPECT_RESULT(); +} + int test_wc_PKCS7_VerifySignedData_DegenerateMinimal(void) { EXPECT_DECLS; diff --git a/tests/api/test_pkcs7.h b/tests/api/test_pkcs7.h index bf66118e08d..61330bec8de 100644 --- a/tests/api/test_pkcs7.h +++ b/tests/api/test_pkcs7.h @@ -82,6 +82,7 @@ int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void); int test_wc_PKCS7_VerifySignedData_TruncEContentTag(void); int test_wc_PKCS7_VerifySignedData_TruncCertSetTag(void); int test_wc_PKCS7_VerifySignedData_DegenerateMinimal(void); +int test_wc_PKCS7_DecodeSignedData(void); int test_wc_PKCS7_VerifySignedData_DegenerateEmptyCertsCrls(void); int test_wc_PKCS7_VerifySignedData_TruncSignerInfosTag(void); int test_wc_PKCS7_VerifySignedData_NoSignerInfosTag(void); @@ -145,6 +146,7 @@ int test_wc_PKCS7_VerifySignedData_NoDigestParams(void); TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_TruncEContentTag), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_TruncCertSetTag), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_DegenerateMinimal), \ + TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_DecodeSignedData), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_DegenerateEmptyCertsCrls), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_TruncSignerInfosTag), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_NoSignerInfosTag), \ diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 6f63ba28354..3dbb4b6079c 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -6888,7 +6888,7 @@ static int wc_PKCS7_HandleOctetStrings(wc_PKCS7* pkcs7, byte* in, word32 inSz, */ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, word32 hashSz, byte* in, word32 inSz, - byte* in2, word32 in2Sz) + byte* in2, word32 in2Sz, int parseOnly) { word32 idx, maxIdx = inSz, outerContentType = 0, contentTypeSz = 0, totalSz = 0; int length = 0, version = 0, ret = 0; @@ -6905,6 +6905,7 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, byte degenerate = 0; byte detached = 0; byte noContent = 0; + byte skipVerify = 0; byte tag = 0; word16 contentIsPkcs7Type = 0; #ifdef ASN_BER_TO_DER @@ -8217,6 +8218,7 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, pkiMsg2Sz = (pkcs7->stream->length > 0)? pkcs7->stream->length: srcSz; degenerate = pkcs7->stream->degenerate; + detached = pkcs7->stream->detached; /* restore content */ content = pkcs7->stream->content; @@ -8226,6 +8228,16 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, ret = wc_PKCS7_ParseSignerInfo(pkcs7, pkiMsg2, pkiMsg2Sz, &idx, degenerate, &signedAttrib, &signedAttribSz); + /* Decode only: a detached bundle without its content cannot be + * verified yet, the caller verifies once the content is known. */ + if (ret == 0) { + pkcs7->detached = (detached != 0); + if (parseOnly && detached && content == NULL && + hashBuf == NULL) { + skipVerify = 1; + } + } + /* parse out the signature if present and verify it */ if (ret == 0 && length > 0 && degenerate == 0) { WOLFSSL_MSG("Parsing signature and verifying"); @@ -8254,7 +8266,7 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, pkcs7->content = content; pkcs7->contentSz = (word32)contentSz; - if (ret == 0) { + if (ret == 0 && !skipVerify) { #if !defined(NO_PKCS7_STREAM) && defined(ASN_BER_TO_DER) byte streamHash[WC_MAX_DIGEST_SIZE]; @@ -8394,12 +8406,22 @@ int wc_PKCS7_VerifySignedData_ex(wc_PKCS7* pkcs7, const byte* hashBuf, word32 pkiMsgFootSz) { return PKCS7_VerifySignedData(pkcs7, hashBuf, hashSz, - pkiMsgHead, pkiMsgHeadSz, pkiMsgFoot, pkiMsgFootSz); + pkiMsgHead, pkiMsgHeadSz, pkiMsgFoot, pkiMsgFootSz, 0); } int wc_PKCS7_VerifySignedData(wc_PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz) { - return PKCS7_VerifySignedData(pkcs7, NULL, 0, pkiMsg, pkiMsgSz, NULL, 0); + return PKCS7_VerifySignedData(pkcs7, NULL, 0, pkiMsg, pkiMsgSz, NULL, 0, 0); +} + +/* Decode SignedData. Attached content is verified; a detached bundle without + * content is only parsed (pkcs7->detached set) for a later verify call. */ +int wc_PKCS7_DecodeSignedData(wc_PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz) +{ + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + return PKCS7_VerifySignedData(pkcs7, NULL, 0, pkiMsg, pkiMsgSz, NULL, 0, 1); } diff --git a/wolfssl/openssl/pkcs7.h b/wolfssl/openssl/pkcs7.h index 54d7b113b0b..c1f45de1d66 100644 --- a/wolfssl/openssl/pkcs7.h +++ b/wolfssl/openssl/pkcs7.h @@ -62,6 +62,8 @@ WOLFSSL_API PKCS7* wolfSSL_d2i_PKCS7(PKCS7** p7, const unsigned char** in, WOLFSSL_LOCAL PKCS7* wolfSSL_d2i_PKCS7_ex(PKCS7** p7, const unsigned char** in, int len, byte* content, word32 contentSz); WOLFSSL_API PKCS7* wolfSSL_d2i_PKCS7_bio(WOLFSSL_BIO* bio, PKCS7** p7); +WOLFSSL_API int wolfSSL_PKCS7_type_is_signed(PKCS7* p7); +WOLFSSL_API int wolfSSL_PKCS7_get_detached(PKCS7* p7); WOLFSSL_API int wolfSSL_i2d_PKCS7_bio(WOLFSSL_BIO *bio, PKCS7 *p7); WOLFSSL_API int wolfSSL_i2d_PKCS7(PKCS7 *p7, unsigned char **out); WOLFSSL_API PKCS7* wolfSSL_PKCS7_sign(WOLFSSL_X509* signer, @@ -88,6 +90,10 @@ WOLFSSL_API int wolfSSL_SMIME_write_PKCS7(WOLFSSL_BIO* out, PKCS7* pkcs7, #define PKCS7_SIGNED_free wolfSSL_PKCS7_SIGNED_free #define d2i_PKCS7 wolfSSL_d2i_PKCS7 #define d2i_PKCS7_bio wolfSSL_d2i_PKCS7_bio +#define PKCS7_type_is_signed wolfSSL_PKCS7_type_is_signed +#define PKCS7_get_detached wolfSSL_PKCS7_get_detached +#define PKCS7_is_detached(p7) (wolfSSL_PKCS7_type_is_signed(p7) && \ + wolfSSL_PKCS7_get_detached(p7)) #define i2d_PKCS7_bio wolfSSL_i2d_PKCS7_bio #define i2d_PKCS7 wolfSSL_i2d_PKCS7 #define PKCS7_sign wolfSSL_PKCS7_sign diff --git a/wolfssl/wolfcrypt/pkcs7.h b/wolfssl/wolfcrypt/pkcs7.h index 6c37344c0b7..d28444282a5 100644 --- a/wolfssl/wolfcrypt/pkcs7.h +++ b/wolfssl/wolfcrypt/pkcs7.h @@ -327,7 +327,8 @@ struct wc_PKCS7 { /* flags - up to 16-bits */ WC_BITFIELD isDynamic:1; WC_BITFIELD noDegenerate:1; /* allow degenerate case in verify function */ - WC_BITFIELD detached:1; /* generate detached SignedData signature bundles */ + WC_BITFIELD detached:1; /* detached SignedData: set for encode, + * reported by decode */ byte contentType[MAX_OID_SZ]; /* custom contentType byte array */ word32 contentTypeSz; /* size of contentType, bytes */ @@ -470,6 +471,8 @@ WOLFSSL_API int wc_PKCS7_VerifySignedData_ex(wc_PKCS7* pkcs7, const byte* hashB word32 hashSz, byte* pkiMsgHead, word32 pkiMsgHeadSz, byte* pkiMsgFoot, word32 pkiMsgFootSz); +WOLFSSL_API int wc_PKCS7_DecodeSignedData(wc_PKCS7* pkcs7, + byte* pkiMsg, word32 pkiMsgSz); WOLFSSL_API int wc_PKCS7_GetSignerSID(wc_PKCS7* pkcs7, byte* out, word32* outSz); From 5fe9895cc23a718b64ffd46d24a2b8ab9e728d92 Mon Sep 17 00:00:00 2001 From: Takashi Kojo Date: Sat, 5 Sep 2026 05:26:20 +0900 Subject: [PATCH 2/2] PKCS7: free contentDynamic when verifying detached content again --- wolfcrypt/src/pkcs7.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 3dbb4b6079c..7ad690c04fa 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -6654,6 +6654,9 @@ static int wc_PKCS7_HandleOctetStrings(wc_PKCS7* pkcs7, byte* in, word32 inSz, XMEMCPY(pkcs7->stream->content, pkcs7->content, pkcs7->contentSz); pkcs7->stream->contentSz = pkcs7->contentSz; } + /* drop the content copy left by a previous verify */ + XFREE(pkcs7->contentDynamic, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->contentDynamic = NULL; return 0; }