From b4aa79da87e8463acf5877c77616199cb0ba41d7 Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Mon, 22 Jun 2026 00:01:50 -0500 Subject: [PATCH 1/9] pkcs7: Parse all certificates embedded in PKCS #7 SignedData The PKCS7 parser is extended to parse multiple certificates from the certificate set, instead of assuming a single signer certificate. Signed-off-by: Sridhar Markonda --- include/mbedtls/pkcs7.h | 11 ++- library/pkcs7.c | 170 ++++++++++++++++++++++++++++------------ 2 files changed, 130 insertions(+), 51 deletions(-) diff --git a/include/mbedtls/pkcs7.h b/include/mbedtls/pkcs7.h index aa9764e19a..96ca302b20 100644 --- a/include/mbedtls/pkcs7.h +++ b/include/mbedtls/pkcs7.h @@ -121,6 +121,15 @@ typedef struct mbedtls_pkcs7_signer_info { } mbedtls_pkcs7_signer_info; +/** + * Structure for holding a certificate in a PKCS #7 SignedData + */ +typedef struct mbedtls_pkcs7_cert { + mbedtls_x509_crt MBEDTLS_PRIVATE(cert); + struct mbedtls_pkcs7_cert *MBEDTLS_PRIVATE(next); +} +mbedtls_pkcs7_cert; + /** * Structure holding the signed data section */ @@ -128,7 +137,7 @@ typedef struct mbedtls_pkcs7_signed_data { int MBEDTLS_PRIVATE(version); mbedtls_pkcs7_buf MBEDTLS_PRIVATE(digest_alg_identifiers); int MBEDTLS_PRIVATE(no_of_certs); - mbedtls_x509_crt MBEDTLS_PRIVATE(certs); + mbedtls_pkcs7_cert *MBEDTLS_PRIVATE(certs); int MBEDTLS_PRIVATE(no_of_crls); mbedtls_x509_crl MBEDTLS_PRIVATE(crl); int MBEDTLS_PRIVATE(no_of_signers); diff --git a/library/pkcs7.c b/library/pkcs7.c index 4e3b554f0e..bbd71842ca 100644 --- a/library/pkcs7.c +++ b/library/pkcs7.c @@ -20,6 +20,7 @@ #include "mbedtls/platform.h" #include "mbedtls/platform_util.h" +#include #if defined(MBEDTLS_HAVE_TIME) #include "mbedtls/platform_time.h" @@ -158,62 +159,121 @@ static int pkcs7_get_digest_algorithm_set(unsigned char **p, } /** - * certificates :: SET OF ExtendedCertificateOrCertificate, - * ExtendedCertificateOrCertificate ::= CHOICE { - * certificate Certificate -- x509, - * extendedCertificate[0] IMPLICIT ExtendedCertificate } - * Return number of certificates added to the signed data, - * 0 or higher is valid. - * Return negative error code for failure. - **/ -static int pkcs7_get_certificates(unsigned char **p, unsigned char *end, - mbedtls_x509_crt *certs) + * \brief Parse and add a certificate to the certificate list. + * + * \details Decodes a DER-encoded certificate and adds it to the + * certificate list. + * + * \param der_cert DER-encoded certificate. + * \param der_len Length of the DER-encoded certificate. + * \param certs Certificate list to which the parsed certificate + * is added. + * + * \return 0 on success, or a negative error code on failure. + */ +static int pkcs7_parse_cert_der(const unsigned char *der_cert, size_t der_len, + mbedtls_pkcs7_cert **certs) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - size_t len1 = 0; - size_t len2 = 0; - unsigned char *end_set, *end_cert, *start; + mbedtls_pkcs7_cert *pcert; - ret = mbedtls_asn1_get_tag(p, end, &len1, MBEDTLS_ASN1_CONSTRUCTED - | MBEDTLS_ASN1_CONTEXT_SPECIFIC); - if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { - return 0; + pcert = mbedtls_calloc(1, sizeof(mbedtls_pkcs7_cert)); + if (pcert == NULL) { + return MBEDTLS_ERR_PKCS7_ALLOC_FAILED; } + + mbedtls_x509_crt_init(&pcert->cert); + + ret = mbedtls_x509_crt_parse_der(&pcert->cert, der_cert, der_len); if (ret != 0) { - return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, ret); - } - start = *p; - end_set = *p + len1; - - ret = mbedtls_asn1_get_tag(p, end_set, &len2, MBEDTLS_ASN1_CONSTRUCTED - | MBEDTLS_ASN1_SEQUENCE); - if (ret != 0) { - return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CERT, ret); - } - - end_cert = *p + len2; - - /* - * This is to verify that there is only one signer certificate. It seems it is - * not easy to differentiate between the chain vs different signer's certificate. - * So, we support only the root certificate and the single signer. - * The behaviour would be improved with addition of multiple signer support. - */ - if (end_cert != end_set) { - return MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE; - } - - if ((ret = mbedtls_x509_crt_parse_der(certs, start, len1)) < 0) { + mbedtls_free(pcert); return MBEDTLS_ERR_PKCS7_INVALID_CERT; } - *p = end_cert; + if (*certs == NULL) { + *certs = pcert; + } else { + pcert->next = *certs; + *certs = pcert; + } - /* - * Since in this version we strictly support single certificate, and reaching - * here implies we have parsed successfully, we return 1. - */ - return 1; + return 0; +} + +/** + * \brief Parse certificates from PKCS#7 SignedData. + * + * \details Parses the SET OF ExtendedCertificateOrCertificate and + * stores them in a linked list of mbedtls_pkcs7_cert nodes. + * + * certificates :: SET OF ExtendedCertificateOrCertificate, + * ExtendedCertificateOrCertificate ::= CHOICE { + * certificate Certificate -- x509, + * extendedCertificate[0] IMPLICIT ExtendedCertificate + * } + * + * \param p Input pointer to ASN.1 buffer. + * \param end End of input buffer. + * \param certs Certificate list. + * + * \return Number of certificates parsed (>= 0) on success, + * or a negative error code on failure. + */ +static int pkcs7_get_certificates(unsigned char **p, unsigned char *end, + mbedtls_pkcs7_cert **certs) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t set_len = 0; + int count = 0; + unsigned char *end_set; + + ret = mbedtls_asn1_get_tag(p, end, &set_len, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_CONTEXT_SPECIFIC); + if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + return 0; + } else if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, ret); + } + + end_set = *p + set_len; + + while (*p < end_set && count < INT_MAX) { + unsigned char *elem_start = *p; + unsigned char *q = *p; + size_t cert_len = 0; + size_t der_len; + + ret = mbedtls_asn1_get_tag(&q, end_set, &cert_len, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CERT, + ret); + } + + if (cert_len > (size_t) (end_set - q)) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + der_len = (size_t) ((q + cert_len) - elem_start); + + ret = pkcs7_parse_cert_der(elem_start, der_len, certs); + if (ret != 0) { + return ret; + } + + *p = q + cert_len; + count++; + } + + if (*p != end_set) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return count; } /** @@ -514,14 +574,13 @@ static int pkcs7_get_signed_data(unsigned char *buf, size_t buflen, } /* Look for certificates, there may or may not be any */ - mbedtls_x509_crt_init(&signed_data->certs); + signed_data->certs = NULL; ret = pkcs7_get_certificates(&p, end, &signed_data->certs); if (ret < 0) { return ret; } signed_data->no_of_certs = ret; - /* * Currently CRLs are not supported. If CRL exist, the parsing will fail * at next step of getting signers info and return error as invalid @@ -759,6 +818,8 @@ void mbedtls_pkcs7_free(mbedtls_pkcs7 *pkcs7) { mbedtls_pkcs7_signer_info *signer_cur; mbedtls_pkcs7_signer_info *signer_prev; + mbedtls_pkcs7_cert *cert_cur; + mbedtls_pkcs7_cert *cert_prev; if (pkcs7 == NULL || pkcs7->raw.p == NULL) { return; @@ -766,7 +827,16 @@ void mbedtls_pkcs7_free(mbedtls_pkcs7 *pkcs7) mbedtls_free(pkcs7->raw.p); - mbedtls_x509_crt_free(&pkcs7->signed_data.certs); + cert_cur = pkcs7->signed_data.certs; + while (cert_cur != NULL) { + cert_prev = cert_cur; + cert_cur = cert_cur->next; + mbedtls_x509_crt_free(&cert_prev->cert); + mbedtls_free(cert_prev); + } + + pkcs7->signed_data.certs = NULL; + mbedtls_x509_crl_free(&pkcs7->signed_data.crl); signer_cur = pkcs7->signed_data.signers.next; From 31cd11e04b4f46cc184c47737554c0daf5250082 Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Fri, 28 Aug 2026 01:14:20 -0500 Subject: [PATCH 2/9] tests: Allow PKCS #7 parsing with multiple certificates The PKCS#7 parser supports multiple embedded certificates. Update the test expectation to treat SignedData with multiple certificates as valid instead of returning MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE. Signed-off-by: Sridhar Markonda --- tests/suites/test_suite_pkcs7.data | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/suites/test_suite_pkcs7.data b/tests/suites/test_suite_pkcs7.data index 4aff54c1cc..7d0a0710f4 100644 --- a/tests/suites/test_suite_pkcs7.data +++ b/tests/suites/test_suite_pkcs7.data @@ -22,9 +22,9 @@ PKCS7 Signed Data Parse reused object from multiple signers to one signer depends_on:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY pkcs7_parse_reuse:"../framework/data_files/pkcs7_data_multiple_signed.der":"../framework/data_files/pkcs7_data_cert_signed_sha256.der":MBEDTLS_PKCS7_SIGNED_DATA -PKCS7 Signed Data Parse Fail with multiple certs #4 +PKCS7 Signed Data Parse Pass with multiple certs #4 depends_on:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY -pkcs7_parse:"../framework/data_files/pkcs7_data_multiple_certs_signed.der":MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE +pkcs7_parse:"../framework/data_files/pkcs7_data_multiple_certs_signed.der":MBEDTLS_PKCS7_SIGNED_DATA PKCS7 Signed Data Parse Fail with corrupted cert #5.0 depends_on:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY From 89cc0b2cb4bb4f4f23388eca5e5bdd9c1f2fe0a4 Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Tue, 1 Sep 2026 13:30:17 -0500 Subject: [PATCH 3/9] pkcs7: Add certificate chain verification and extended APIs Extend PKCS#7 SignedData verification to support building and verifying the certificate chain of the signer. Add extended signature verification APIs that use caller-supplied trusted certificates and optionally embedded leaf certificates from the PKCS#7 structure. When embedded leaf certificates are used, their certificate chain is validated against the provided trusted CAs. Signed-off-by: Sridhar Markonda --- include/mbedtls/pkcs7.h | 76 ++++++- library/pkcs7.c | 425 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 469 insertions(+), 32 deletions(-) diff --git a/include/mbedtls/pkcs7.h b/include/mbedtls/pkcs7.h index 96ca302b20..9d89158bc2 100644 --- a/include/mbedtls/pkcs7.h +++ b/include/mbedtls/pkcs7.h @@ -63,6 +63,8 @@ #define MBEDTLS_ERR_PKCS7_ALLOC_FAILED PSA_ERROR_INSUFFICIENT_MEMORY /**< Allocation of memory failed. */ #define MBEDTLS_ERR_PKCS7_VERIFY_FAIL PSA_ERROR_INVALID_SIGNATURE /**< Verification Failed */ #define MBEDTLS_ERR_PKCS7_CERT_DATE_INVALID -0x5880 /**< The PKCS #7 date issued/expired dates are invalid */ +#define MBEDTLS_ERR_PKCS7_CERT_VERIFY_FAILED -0x5900 /**< The PKCS #7 signer certificate verification failed. */ +#define MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND -0x5980 /**< No certificate matching the PKCS#7 signer information was found. */ /* \} name */ /** @@ -208,7 +210,7 @@ int mbedtls_pkcs7_parse_der(mbedtls_pkcs7 *pkcs7, const unsigned char *buf, * * \return 0 if the signature verifies, or a negative error code on failure. */ -int mbedtls_pkcs7_signed_data_verify(mbedtls_pkcs7 *pkcs7, +int mbedtls_pkcs7_signed_data_verify(const mbedtls_pkcs7 *pkcs7, const mbedtls_x509_crt *cert, const unsigned char *data, size_t datalen); @@ -240,10 +242,80 @@ int mbedtls_pkcs7_signed_data_verify(mbedtls_pkcs7 *pkcs7, * * \return 0 if the signature verifies, or a negative error code on failure. */ -int mbedtls_pkcs7_signed_hash_verify(mbedtls_pkcs7 *pkcs7, +int mbedtls_pkcs7_signed_hash_verify(const mbedtls_pkcs7 *pkcs7, const mbedtls_x509_crt *cert, const unsigned char *hash, size_t hashlen); +/** + * \brief Verify a PKCS #7 SignedData signature over plain data. + * + * For each signer in the PKCS#7 structure, this function verifies + * the signature over the supplied data using the digest algorithm + * specified by the signer and a matching leaf certificate. + * Verification succeeds if at least one signer signature is valid. + * + * The verification uses certificates provided by the caller and, + * optionally, certificates embedded within the PKCS#7 SignedData + * structure. When certificates from the PKCS#7 structure are used, + * the signer certificate chain is validated against the supplied + * trusted certificates. + * + * \param pkcs7 PKCS#7 SignedData structure containing signatures. + * \param trust_certs Certificate list containing trusted leaf and CA certificates. + * CA certificates are used for certificate chain verification and + * leaf certificates are used for signature verification. + * \param data Plain data on which signature has to be verified. + * \param datalen Length of the \p data. + * \param use_pkcs7_leaf Flag indicating whether to use the leaf certificate embedded + * in the PKCS#7 structure: set to \c 1 to enforce use of the PKCS#7 + * leaf certificate, or \c 0 to use only caller-supplied certificates. + * + * \note This function internally calculates the hash on the supplied + * plain data for signature verification. + * + * \return 0 on success, or a negative error code on failure. + */ +int mbedtls_pkcs7_signed_data_verify_ext(const mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *trust_certs, + const unsigned char *data, + const size_t datalen, + const int use_pkcs7_leaf); + +/** + * \brief Verify PKCS #7 SignedData signature over a supplied hash. + * + * For each signer in the PKCS#7 structure, this function verifies + * the signature over the supplied message hash using the digest + * algorithm specified by the signer and a matching leaf certificate. + * Verification succeeds if at least one signer signature is valid. + * + * The verification uses certificates provided by the caller and, + * optionally, certificates embedded within the PKCS#7 SignedData + * structure. When certificates from the PKCS#7 structure are used, + * the signer certificate chain is validated against the supplied + * trusted certificates. + * + * \param pkcs7 PKCS #7 SignedData structure containing signatures. + * \param trust_certs Certificate list containing trusted leaf and CA certificates. + * CA certificates are used for certificate chain verification and + * leaf certificates are used for signature verification. + * \param hash Hash of the plain data on which signature has to be verified. + * \param hashlen Length of the \p hash. + * \param use_pkcs7_leaf Flag indicating whether to use the leaf certificate embedded in + * the PKCS#7 structure: set to \c 1 to enforce use of the PKCS#7 + * leaf certificate, or \c 0 to use only caller-supplied certificates. + * + * \note This function differs from mbedtls_pkcs7_signed_data_verify_ext() + * in that the hash of the input data is provided directly by caller. + * + * \return 0 on success, or a negative error code on failure. + */ +int mbedtls_pkcs7_signed_hash_verify_ext(const mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *trust_certs, + const unsigned char *hash, + const size_t hashlen, + const int use_pkcs7_leaf); + /** * \brief Unallocate all PKCS #7 data and zeroize the memory. * It doesn't free \p pkcs7 itself. This should be done by the caller. diff --git a/library/pkcs7.c b/library/pkcs7.c index bbd71842ca..7f58569e4d 100644 --- a/library/pkcs7.c +++ b/library/pkcs7.c @@ -697,20 +697,258 @@ out: return ret; } -static int mbedtls_pkcs7_data_or_hash_verify(mbedtls_pkcs7 *pkcs7, - const mbedtls_x509_crt *cert, - const unsigned char *data, - size_t datalen, - const int is_data_hash) +/** + * \brief Decide whether a certificate should be classified as a CA. + * Policy: + * - If BasicConstraints present and CA=TRUE => CA + * - If KeyUsage is present, require keyCertSign for CA + * - If BasicConstraints absent => NOT a CA (classify as leaf) + * - If BasicConstraints is present and CA=FALSE => not a CA (leaf) + * + * \param crt Certificate to classify. + * + * \return 1 if the certificate is classified as CA, 0 otherwise. + */ +static int pkcs7_is_ca_cert(const mbedtls_x509_crt *crt) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - unsigned char *hash; - mbedtls_pk_context pk_cxt = cert->pk; - const mbedtls_md_info_t *md_info; - mbedtls_md_type_t md_alg; - mbedtls_pkcs7_signer_info *signer; + /* BasicConstraints: is CA? */ + ret = mbedtls_x509_crt_get_ca_istrue(crt); + if (ret == 1) { + /* CA=TRUE; enforce keyCertSign only if KU exists */ + ret = mbedtls_x509_crt_check_key_usage(crt, MBEDTLS_X509_KU_KEY_CERT_SIGN); + return (ret == 0) ? 1 : 0; + } else { + /* ret == 0: BC present, CA=FALSE. ret < 0: BasicConstraints absent) */ + return 0; + } +} - if (pkcs7->signed_data.no_of_signers == 0) { +/** + * \brief Check if the given CA certificate is a possible issuer of + * the provided certificate. + * \details This function attempts to match the issuer of \p cert with + * the subject of \p ca using the following order: + * 1. If both AKI keyIdentifier and SKI are present, compare them. + * 2. If AKI authorityCertIssuer and authorityCertSerialNumber are + * present, compare against the CA's subject DN and serial. + * 3. Fallback to comparing issuer and subject Distinguished Name (DN). + * + * \param cert The certificate whose issuer is to be found. + * \param ca Candidate CA certificate. + * + * \return 0 if \p ca is a potential issuer of \p cert, -1 otherwise. + */ +static int pkcs7_is_issuer_match(const mbedtls_x509_crt *cert, + const mbedtls_x509_crt *ca) +{ + if (cert->authority_key_id.keyIdentifier.len != 0 && + ca->subject_key_id.len != 0) { + + if (cert->authority_key_id.keyIdentifier.len == ca->subject_key_id.len && + memcmp(cert->authority_key_id.keyIdentifier.p, ca->subject_key_id.p, + ca->subject_key_id.len) == 0) { + return 0; + } + + return -1; + } + + if (cert->authority_key_id.authorityCertIssuer.buf.len != 0 && + cert->authority_key_id.authorityCertSerialNumber.len != 0) { + + if (cert->authority_key_id.authorityCertIssuer.buf.len == ca->subject_raw.len && + memcmp(cert->authority_key_id.authorityCertIssuer.buf.p, + ca->subject_raw.p, ca->subject_raw.len) == 0 && + cert->authority_key_id.authorityCertSerialNumber.len == ca->serial.len && + memcmp(cert->authority_key_id.authorityCertSerialNumber.p, + ca->serial.p, ca->serial.len) == 0) { + return 0; + } + + return -1; + } + + if (cert->issuer_raw.len == ca->subject_raw.len && + memcmp(cert->issuer_raw.p, ca->subject_raw.p, + cert->issuer_raw.len) == 0) { + return 0; + } + + return -1; +} + +/** + * \brief Check if a certificate already exist in the chain + * by comparing TBSCertificate content. + * + * \param chain The certificate chain to search + * \param cert The certificate to look for + * + * \return 1 if certificate exists in chain + * 0 if certificate does not exist + */ +static int pkcs7_is_duplicate_cert(const mbedtls_x509_crt *chain, + const mbedtls_x509_crt *cert) +{ + const mbedtls_x509_crt *cur; + + for (cur = chain; cur != NULL; cur = cur->next) { + if (cur->tbs.len != cert->tbs.len) { + continue; + } + + if (memcmp(cur->tbs.p, cert->tbs.p, cert->tbs.len) == 0) { + return 1; + } + } + + return 0; +} + +/** + * \brief Build CA certificate chain from embedded PKCS#7 certs. + * + * \param pkcs7_certs Embedded certificate list (mbedtls_pkcs7_cert). + * \param leaf_cert Leaf certificate to build chain for. + * \param ca_chain On success, pointer to CA chain (caller must free). + * + * \return 0 on success, or a negative error code on failure. + */ +static int pkcs7_build_ca_chain(const mbedtls_pkcs7_cert *pkcs7_certs, + const mbedtls_x509_crt *leaf_cert, + mbedtls_x509_crt **ca_chain) +{ + const mbedtls_x509_crt *current = leaf_cert; + const mbedtls_pkcs7_cert *embedded_cert; + mbedtls_x509_crt *chain = NULL; + int ret; + size_t depth = 0; + + *ca_chain = NULL; + + while (depth < MBEDTLS_X509_MAX_INTERMEDIATE_CA && current != NULL) { + int found = 0; + + for (embedded_cert = pkcs7_certs; embedded_cert != NULL; + embedded_cert = embedded_cert->next) { + const mbedtls_x509_crt *issuer = &embedded_cert->cert; + + if (issuer->raw.p == NULL || issuer->raw.len == 0) { + continue; + } + + if (issuer == current) { + continue; + } + + if (pkcs7_is_ca_cert(issuer) != 1) { + continue; + } + + if (pkcs7_is_issuer_match(current, issuer) == 0) { + if (pkcs7_is_duplicate_cert(chain, issuer)) { + continue; + } + + if (chain == NULL) { + chain = mbedtls_calloc(1, sizeof(mbedtls_x509_crt)); + if (chain == NULL) { + ret = MBEDTLS_ERR_PKCS7_ALLOC_FAILED; + goto cleanup; + } + mbedtls_x509_crt_init(chain); + } + + ret = mbedtls_x509_crt_parse_der(chain, issuer->raw.p, issuer->raw.len); + if (ret != 0) { + goto cleanup; + } + + current = issuer; + found = 1; + depth++; + break; + } + } + + if (!found) { + break; + } + } + + *ca_chain = chain; + return 0; + +cleanup: + if (chain != NULL) { + mbedtls_x509_crt_free(chain); + mbedtls_free(chain); + } + return ret; +} + +/** + * \brief Verify certificate chain against trusted anchors. + * + * \details Builds CA chain from embedded PKCS#7 certs and verifies + * the complete chain against trust anchors. + * + * \note mbedtls_x509_crt_verify() accepts mixed list of trusted + * CAs and trusted leaf certs. + * + * \param pkcs7_certs Embedded certificate list + * \param leaf_cert Leaf certificate to verify. + * \param trust_anchors Trust anchors: root CAs and/or trusted leaf certs. + * + * \return 0 on success, or a negative error code on failure. + */ +static int pkcs7_verify_chain(const mbedtls_pkcs7_cert *pkcs7_certs, + mbedtls_x509_crt *leaf_cert, + const mbedtls_x509_crt *trust_anchors) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_x509_crt *ca_chain = NULL; + uint32_t flags = 0; + + ret = pkcs7_build_ca_chain(pkcs7_certs, leaf_cert, &ca_chain); + if (ret != 0) { + return ret; + } + + leaf_cert->next = ca_chain; + ret = mbedtls_x509_crt_verify(leaf_cert, (mbedtls_x509_crt *) trust_anchors, + NULL, NULL, &flags, NULL, NULL); + + leaf_cert->next = NULL; + + if (ca_chain != NULL) { + mbedtls_x509_crt_free(ca_chain); + mbedtls_free(ca_chain); + } + + if (ret != 0 || flags != 0) { + return MBEDTLS_ERR_PKCS7_CERT_VERIFY_FAILED; + } + + return 0; +} + +/** + * \brief Check if certificate matches signer info. + * + * \details Validates certificate date, matches serial/issuer, + * and checks key usage for digital signature. + * + * \param cert Certificate to check. + * \param signer Signer info to match against. + * + * \return 0 if match, or a negative error code otherwise. + */ +static int pkcs7_check_cert_with_signer(const mbedtls_x509_crt *cert, + const mbedtls_pkcs7_signer_info *signer) +{ + if (cert->raw.p == NULL || cert->raw.len == 0) { return MBEDTLS_ERR_PKCS7_INVALID_CERT; } @@ -719,6 +957,96 @@ static int mbedtls_pkcs7_data_or_hash_verify(mbedtls_pkcs7 *pkcs7, return MBEDTLS_ERR_PKCS7_CERT_DATE_INVALID; } + if (cert->serial.len != signer->serial.len || + memcmp(cert->serial.p, signer->serial.p, signer->serial.len) != 0) { + return MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND; + } + + if (cert->issuer_raw.len != signer->issuer_raw.len || + memcmp(cert->issuer_raw.p, signer->issuer_raw.p, + signer->issuer_raw.len) != 0) { + return MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND; + } + + if (mbedtls_x509_crt_check_key_usage(cert, + MBEDTLS_X509_KU_DIGITAL_SIGNATURE) != 0) { + return MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND; + } + + return 0; +} + +/** + * \brief Find matching leaf certificate. + * + * \details Searches for certificate matching signer info. + * Can search in embedded PKCS#7 certs or external chain. + * + * \param pkcs7 PKCS#7 structure (for embedded certs). + * \param certs External certificate chain. + * \param signer Signer info to match against. + * \param use_pkcs7_leaf Flag: 1=search embedded, 0=search external. + * \param leaf_cert On success, pointer to matching certificate. + * + * \return 0 on success, or a negative error code on failure. + */ +static int pkcs7_get_leaf_cert(const mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *certs, + const mbedtls_pkcs7_signer_info *signer, + const int use_pkcs7_leaf, + mbedtls_x509_crt **leaf_cert) +{ + int ret = MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND; + + if (use_pkcs7_leaf == 1) { + const mbedtls_pkcs7_cert *embedded_cert = pkcs7->signed_data.certs; + + while (embedded_cert != NULL) { + const mbedtls_x509_crt *cert = &embedded_cert->cert; + + ret = pkcs7_check_cert_with_signer(cert, signer); + if (ret == 0) { + *leaf_cert = (mbedtls_x509_crt *) cert; + return 0; + } + + embedded_cert = embedded_cert->next; + } + } else { + const mbedtls_x509_crt *cert = certs; + + while (cert != NULL) { + ret = pkcs7_check_cert_with_signer(cert, signer); + if (ret == 0) { + *leaf_cert = (mbedtls_x509_crt *) cert; + return 0; + } + + cert = cert->next; + } + } + + return ret; +} + +static int mbedtls_pkcs7_data_or_hash_verify(const mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *cert, + const unsigned char *data, + size_t datalen, + const int is_data_hash, + const int use_pkcs7_leaf) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *hash = NULL; + const mbedtls_md_info_t *md_info; + mbedtls_md_type_t md_alg; + const mbedtls_pkcs7_signer_info *signer; + mbedtls_x509_crt *leaf_cert = NULL; + + if (pkcs7->signed_data.no_of_signers == 0) { + return MBEDTLS_ERR_PKCS7_INVALID_CERT; + } + ret = mbedtls_x509_oid_get_md_alg(&pkcs7->signed_data.digest_alg_identifiers, &md_alg); if (ret != 0) { return ret; @@ -764,21 +1092,26 @@ static int mbedtls_pkcs7_data_or_hash_verify(mbedtls_pkcs7 *pkcs7, /* assume failure */ ret = MBEDTLS_ERR_PKCS7_VERIFY_FAIL; - /* - * Potential TODOs - * Currently we iterate over all signers and return success if any of them - * verify. - * - * However, we could make this better by checking against the certificate's - * identification and SignerIdentifier fields first. That would also allow - * us to distinguish between 'no signature for key' and 'signature for key - * failed to validate'. - */ for (signer = &pkcs7->signed_data.signers; signer; signer = signer->next) { - ret = mbedtls_pk_verify_ext(cert->sig_pk, &pk_cxt, md_alg, hash, - mbedtls_md_get_size(md_info), - signer->sig.p, signer->sig.len); + if (signer->sig.p == NULL || signer->sig.len == 0) { + continue; + } + ret = pkcs7_get_leaf_cert(pkcs7, cert, signer, use_pkcs7_leaf, &leaf_cert); + if (ret != 0) { + continue; + } + + if (use_pkcs7_leaf == 1) { + ret = pkcs7_verify_chain(pkcs7->signed_data.certs, leaf_cert, cert); + if (ret != 0) { + continue; + } + } + + ret = mbedtls_pk_verify_ext(leaf_cert->sig_pk, &leaf_cert->pk, md_alg, + hash, mbedtls_md_get_size(md_info), + signer->sig.p, signer->sig.len); if (ret == 0) { break; } @@ -789,26 +1122,58 @@ static int mbedtls_pkcs7_data_or_hash_verify(mbedtls_pkcs7 *pkcs7, return ret; } -int mbedtls_pkcs7_signed_data_verify(mbedtls_pkcs7 *pkcs7, +int mbedtls_pkcs7_signed_data_verify(const mbedtls_pkcs7 *pkcs7, const mbedtls_x509_crt *cert, const unsigned char *data, size_t datalen) { - if (data == NULL) { + if (data == NULL || pkcs7 == NULL || cert == NULL) { return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; } - return mbedtls_pkcs7_data_or_hash_verify(pkcs7, cert, data, datalen, 0); + + return mbedtls_pkcs7_data_or_hash_verify(pkcs7, cert, data, datalen, 0, 0); } -int mbedtls_pkcs7_signed_hash_verify(mbedtls_pkcs7 *pkcs7, +int mbedtls_pkcs7_signed_hash_verify(const mbedtls_pkcs7 *pkcs7, const mbedtls_x509_crt *cert, const unsigned char *hash, size_t hashlen) { - if (hash == NULL) { + if (hash == NULL || pkcs7 == NULL || cert == NULL) { return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; } - return mbedtls_pkcs7_data_or_hash_verify(pkcs7, cert, hash, hashlen, 1); + + return mbedtls_pkcs7_data_or_hash_verify(pkcs7, cert, hash, hashlen, 1, 0); +} + +int mbedtls_pkcs7_signed_data_verify_ext(const mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *trust_certs, + const unsigned char *data, + const size_t datalen, + const int use_pkcs7_leaf) +{ + if (data == NULL || pkcs7 == NULL || trust_certs == NULL || + (use_pkcs7_leaf != 0 && use_pkcs7_leaf != 1)) { + return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; + } + + return mbedtls_pkcs7_data_or_hash_verify(pkcs7, trust_certs, data, datalen, + 0, use_pkcs7_leaf); +} + +int mbedtls_pkcs7_signed_hash_verify_ext(const mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *trust_certs, + const unsigned char *hash, + const size_t hashlen, + const int use_pkcs7_leaf) +{ + if (hash == NULL || pkcs7 == NULL || trust_certs == NULL || + (use_pkcs7_leaf != 0 && use_pkcs7_leaf != 1)) { + return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; + } + + return mbedtls_pkcs7_data_or_hash_verify(pkcs7, trust_certs, hash, hashlen, + 1, use_pkcs7_leaf); } /* From 33e538b546eec676d2ad231369b7f143ca730556 Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Mon, 17 Aug 2026 01:36:32 -0500 Subject: [PATCH 4/9] tests: Update PKCS#7 verification tests and add tests for extended APIs Update expected return values of existing PKCS#7 verification tests to match the new signer certificate error reporting. Add test cases and test functions for the extended PKCS#7 signature verification APIs, including embedded signer certificates, certificate chain validation, and multiple signer scenarios. Signed-off-by: Sridhar Markonda --- tests/suites/test_suite_pkcs7.data | 96 +++++++++++++++++++++++++- tests/suites/test_suite_pkcs7.function | 91 ++++++++++++++++++++++++ 2 files changed, 185 insertions(+), 2 deletions(-) diff --git a/tests/suites/test_suite_pkcs7.data b/tests/suites/test_suite_pkcs7.data index 7d0a0710f4..74d1947e37 100644 --- a/tests/suites/test_suite_pkcs7.data +++ b/tests/suites/test_suite_pkcs7.data @@ -84,7 +84,7 @@ pkcs7_verify:"../framework/data_files/pkcs7_zerolendata_detached.der":"../framew PKCS7 Signed Data Verification Fail zero-len data depends_on:PSA_WANT_ALG_SHA_1:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY -pkcs7_verify:"../framework/data_files/pkcs7_zerolendata_detached.der":"../framework/data_files/pkcs7-rsa-sha256-2.der":"../framework/data_files/pkcs7_zerolendata.bin":0:MBEDTLS_ERR_RSA_VERIFY_FAILED +pkcs7_verify:"../framework/data_files/pkcs7_zerolendata_detached.der":"../framework/data_files/pkcs7-rsa-sha256-2.der":"../framework/data_files/pkcs7_zerolendata.bin":0:MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND PKCS7 Signed Data Verification Pass SHA256 #9 depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY @@ -112,7 +112,7 @@ pkcs7_verify:"../framework/data_files/pkcs7_data_cert_signed_sha512.der":"../fra PKCS7 Signed Data Verification Fail because of different certificate #12 depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY -pkcs7_verify:"../framework/data_files/pkcs7_data_cert_signed_sha256.der":"../framework/data_files/pkcs7-rsa-sha256-2.der":"../framework/data_files/pkcs7_data.bin":0:MBEDTLS_ERR_RSA_VERIFY_FAILED +pkcs7_verify:"../framework/data_files/pkcs7_data_cert_signed_sha256.der":"../framework/data_files/pkcs7-rsa-sha256-2.der":"../framework/data_files/pkcs7_data.bin":0:MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND PKCS7 Signed Data Verification Fail because of different data hash #13 depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY @@ -3271,3 +3271,95 @@ pkcs7_asn1_fail:"3082049d06092a864886f70d010702a082048e3082048a020101310f300d060 PKCS7 Parse Failure Invalid ASN1: Change length from 256 to 257 #794.0 depends_on:PSA_WANT_ALG_SHA_256 pkcs7_asn1_fail:"3082049d06092a864886f70d010702a082048e3082048a020101310f300d06096086480165030402010500300b06092a864886f70d0107013182046530820173020101304c3034310b3009060355040613024e4c310e300c060355040a0c05504b4353373115301306035504030c0c504b4353372043657274203102147bdeddd2444cd1cdfe5c41a8102c89b7df2e6cbf300d06096086480165030402010500300d06092a864886f70d0101010500048201005becd87195c1deff90c24c91269b55b3f069bc225c326c314c1a51786ffe14c830be4e4bc73cba36c97677b44168279be91e7cdf7c19386ae21862719d13a3a0fff0803d460962f2cda8371484873252c3d7054db8143e2b081a3816ed0804ca5099ae5fece83d5c2c3783b1988b4b46dc94e55587a107ea1546bf22d28a097f652a4066dc2965269069af2f5176bb8ce9ca6d11f96757f03204f756703587d00ad424796c92fc7aeb6f494431999eda30990e4f5773632ed258fe0276673599da6fce35cdad7726a0bb024cad996b88e0cb98854ceb5c0b6ec748d9f9ce6a6cd437858bacb814618a272ff3a415c6e07f3db0988777fdec845a97bf7d102dd030820173020101304c3034310b3009060355040613024e4c310e300c060355040a0c05504b4353373115301306035504030c0c504b435337204365727420320214564d554025af599e327079b2720ef00df37cfb7c300d06096086480165030402010500300d06092a864886f70d010101050004820100046098dc5f2b99e3bc2ed6642d7f0a61efabc15c38c76333bccceeee369a4f4a5bdda8ea59264124e429f08632683ddba17bbf262d82b6befa3b4e1480376a8c7f3c7d7e2a6779bbf18262aa857de8c20a12b6a2ce8e4d7f31dd1b195392cf6185afce34afe05896057b36e9b171b6c67ed7c2286cbfa83ff8a167c7bc7109aee7d7e1909bd384fbfc61938efca51c8993c71db317730f16e7e776867ae4fdc562d6f15de585e7b94ef496a3676367a2cbaab7b636648b0076bad5cdf1e09740ec6a451bff534efa21509f3ba77b8101b91ea7e55ddbfe1401517067d7b01a2a0879f0885c894a87cfb68c4d7ee890604881aeb97c22d1f337ef923e035a640030820173020101304c3034310b3009060355040613024e4c310e300c060355040a0c05504b4353373115301306035504030c0c504b43533720436572742033021462477298759e8e04718ae563155f3523cfb84cdb300d06096086480165030402010500300d06092a864886f70d0101010500048201016d2cbb37fd12957e5a7480ce0435e8ef6077def08ff270844de2516a4742f211d8c74690bc0948e08a296abba9285f6bb305b95b7f9408ce44e83880c719444672ec99746ba78cc4bde475319d01bde77c49d58d16d3b2e91c2e61e8303ca3d7eda3797fd8b35ca72bb14b15a24b56bb70dc13484565808110ff5db58c56f7a0435607e3c352fbf4f55106f2980fd3cd42397dbf7137ee7f1e32cd80a3b7ab12b46c169220278d7717b78a3c45d6395a130a8b58841cbc290e4f817de3a4fe16ecd077ca33a0aa25e235cc7f6655a80ba8aeec87905bb2a75459b7bcc5a133f2993493b41ffcf63cd7a391e3e6f78094ca442c80144403cb0a5f00bfc61ae880" + +PKCS7 Signed Data Verify Pass: zero-len data #795.0 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_zerolendata_detached-2.der":"../framework/data_files/pkcs7-rsa-sha256-leaf-1.der":"../framework/data_files/pkcs7_zerolendata.bin":0:0:0 + +PKCS7 Signed Data Verify Fail: zero-len data #795.1 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_zerolendata_detached-2.der":"../framework/data_files/pkcs7-rsa-sha256-leaf-3.der":"../framework/data_files/pkcs7_zerolendata.bin":0:0:MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND + +PKCS7 Signed Data Verify Pass: Embedded leaf & Intermediate and Trusted Root cert #796.0 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256-2.der":"../framework/data_files/pkcs7-rsa-sha256-root-ca-1.pem":"../framework/data_files/pkcs7_data.bin":0:1:0 + +PKCS7 Signed Data Verify Fail: Embedded leaf & Intermediate cert, chain check #796.1 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256-2.der":"NULL":"../framework/data_files/pkcs7_data.bin":0:1:MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA + +PKCS7 Signed Data Verify Fail: Invalid Root CA, chain check #796.2 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY:MBEDTLS_PEM_PARSE_C +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256-2.der":"../framework/data_files/pkcs7-rsa-sha256-root-ca-2.pem":"../framework/data_files/pkcs7_data.bin":0:1:MBEDTLS_ERR_PKCS7_CERT_VERIFY_FAILED + +PKCS7 Signed Data Verify Fail: Embedded leaf signer cert #797.0 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256.der":"NULL":"../framework/data_files/pkcs7_data.bin":0:0:MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA + +PKCS7 Signed Data Verify Pass: Trusted CA cert, chain check #797.1 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256.der":"../framework/data_files/pkcs7-rsa-sha256-int-ca-1.der":"../framework/data_files/pkcs7_data.bin":0:1:0 + +PKCS7 Signed Data Verify Fail: Invalid CA cert, chain check #797.2 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256.der":"../framework/data_files/pkcs7-rsa-sha256-int-ca-3.der":"../framework/data_files/pkcs7_data.bin":0:1:MBEDTLS_ERR_PKCS7_CERT_VERIFY_FAILED + +PKCS7 signed data verify Fail: NULL PKCS7 input #798.0 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"NULL":"../framework/data_files/pkcs7-rsa-sha256-1.pem":"../framework/data_files/pkcs7_data.bin":0:1:MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA + +PKCS7 signed data verify Fail: data is NULL #798.1 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256.der":"NULL":"NULL":0:1:MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA + +PKCS7 signed data verify Fail: Invalid chain_check input #798.2 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256.der":"NULL":"../framework/data_files/pkcs7_data.bin":0:2:MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA + +PKCS7 Signed Data Verify Pass: Trusted signer cert #799.0 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_without_cert.der":"../framework/data_files/pkcs7-rsa-sha256-leaf-1.der":"../framework/data_files/pkcs7_data.bin":0:0:0 + +PKCS7 Signed Data Verify Fail: Trusted signer cert chain, chain check #799.1 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY:MBEDTLS_PEM_PARSE_C +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_without_cert.der":"../framework/data_files/pkcs7-rsa-sha256-leaf-1.der":"../framework/data_files/pkcs7_data.bin":0:1:MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND + +PKCS7 Signed Data Verify Pass: Multiple signers, chain check #800.0 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256-3.der":"../framework/data_files/pkcs7-rsa-sha256-ca-chain.pem":"../framework/data_files/pkcs7_data.bin":0:1:0 + +PKCS7 Signed Data Verify Pass: Multiple signers,Trusted Root cert, chain check #800.1 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_multi_cert_sha256.der":"../framework/data_files/pkcs7-rsa-sha256-root-ca-1.pem":"../framework/data_files/pkcs7_data.bin":0:1:0 + +PKCS7 Signed Data Verify Pass: Multiple signers, chain check #800.2 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256-3.der":"../framework/data_files/pkcs7-rsa-sha256-int-ca-2.der":"../framework/data_files/pkcs7_data.bin":0:1:0 + +PKCS7 Signed Data Verify Fail: Multiple signers, Invalid CA cert #800.3 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256-3.der":"../framework/data_files/pkcs7-rsa-sha256-int-ca-3.der":"../framework/data_files/pkcs7_data.bin":0:1:MBEDTLS_ERR_PKCS7_CERT_VERIFY_FAILED + +PKCS7 Signed Data Hash Verify Pass: Trusted CA chain, chain check #801.0 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256.der":"../framework/data_files/pkcs7-rsa-sha256-ca-chain.pem":"../framework/data_files/pkcs7_data.bin":MBEDTLS_MD_SHA256:1:0 + +PKCS7 Signed Data Hash Verify Fail: Invalid data input, chain check #801.1 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256.der":"../framework/data_files/pkcs7-rsa-sha256-ca-chain.pem":"../framework/data_files/pkcs7_data_1.bin":MBEDTLS_MD_SHA256:1:MBEDTLS_ERR_PKCS7_VERIFY_FAIL + +PKCS7 Signed Data Hash Verify Fail: Different Hash Algorithm #801.2 +depends_on:PSA_WANT_ALG_SHA_256:PSA_WANT_ALG_SHA_512:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256.der":"../framework/data_files/pkcs7-rsa-sha256-ca-chain.pem":"../framework/data_files/pkcs7_data.bin":MBEDTLS_MD_SHA512:1:MBEDTLS_ERR_PKCS7_VERIFY_FAIL + +PKCS7 Signed Data Verify Fail: Expired Certificate #802 +depends_on:PSA_WANT_ALG_SHA_256:MBEDTLS_HAVE_TIME_DATE +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_without_cert.der":"../framework/data_files/pkcs7-rsa-expired-2.der":"../framework/data_files/pkcs7_data.bin":0:0:MBEDTLS_ERR_PKCS7_CERT_DATE_INVALID + +PKCS7 Signed Data Verify Fail: Invalid Certificate KeyUsage #803 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_data_invalid_KU_cert.der":"../framework/data_files/pkcs7-rsa-sha256-invalid-leaf.der":"../framework/data_files/pkcs7_data.bin":0:0:MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND + +PKCS7 Signed Data Verify Pass: Root-Signed Leaf Certificate, chain check #804 +depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY +pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256-4.der":"../framework/data_files/pkcs7-rsa-sha256-root-ca-1.pem":"../framework/data_files/pkcs7_data.bin":0:1:0 diff --git a/tests/suites/test_suite_pkcs7.function b/tests/suites/test_suite_pkcs7.function index 3bd7595b96..e5d2b694c7 100644 --- a/tests/suites/test_suite_pkcs7.function +++ b/tests/suites/test_suite_pkcs7.function @@ -227,3 +227,94 @@ exit: USE_PSA_DONE(); } /* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_FS_IO:MBEDTLS_X509_CRT_PARSE_C:PSA_HAVE_ALG_SOME_RSA_VERIFY */ +void pkcs7_verify_ext(char *pkcs7_file, char *certs_file, char *input_file, + int do_hash_alg, int chain_check, int expect_ret) +{ + unsigned char *pkcs7_buf = NULL; + unsigned char *data = NULL; + unsigned char *hash = NULL; + size_t pkcs7_len = 0; + size_t datalen = 0; + size_t hashlen = 0; + int ret; + FILE *file = NULL; + struct stat st; + const mbedtls_md_info_t *md_info = NULL; + + mbedtls_pkcs7 pkcs7; + mbedtls_x509_crt certs; + + USE_PSA_INIT(); + + mbedtls_pkcs7_init(&pkcs7); + mbedtls_x509_crt_init(&certs); + + /* Load PKCS7 */ + if (strcmp(pkcs7_file, "NULL") != 0) { + ret = mbedtls_pk_load_file(pkcs7_file, &pkcs7_buf, &pkcs7_len); + TEST_EQUAL(ret, 0); + + ret = mbedtls_pkcs7_parse_der(&pkcs7, pkcs7_buf, pkcs7_len); + TEST_EQUAL(ret, MBEDTLS_PKCS7_SIGNED_DATA); + } + + /* Load certs */ + if (strcmp(certs_file, "NULL") != 0) { + ret = mbedtls_x509_crt_parse_file(&certs, certs_file); + TEST_EQUAL(ret, 0); + } + + /* Load data */ + if (strcmp(input_file, "NULL") != 0) { + ret = stat(input_file, &st); + TEST_EQUAL(ret, 0); + + datalen = st.st_size; + TEST_CALLOC(data, datalen == 0 ? 1 : datalen); + + file = fopen(input_file, "rb"); + TEST_ASSERT(file != NULL); + TEST_EQUAL(fread(data, 1, datalen, file), datalen); + fclose(file); + file = NULL; + } + + if (do_hash_alg) { + md_info = mbedtls_md_info_from_type((mbedtls_md_type_t) do_hash_alg); + TEST_ASSERT(md_info != NULL); + + hashlen = mbedtls_md_get_size(md_info); + TEST_CALLOC(hash, hashlen); + + if (data != NULL && datalen != 0) { + ret = mbedtls_md(md_info, data, datalen, hash); + TEST_EQUAL(ret, 0); + } + + ret = mbedtls_pkcs7_signed_hash_verify_ext( + strcmp(pkcs7_file, "NULL") ? &pkcs7 : NULL, + strcmp(certs_file, "NULL") ? &certs : NULL, hash, hashlen, + chain_check); + } else { + ret = mbedtls_pkcs7_signed_data_verify_ext( + strcmp(pkcs7_file, "NULL") ? &pkcs7 : NULL, + strcmp(certs_file, "NULL") ? &certs : NULL, data, datalen, + chain_check); + } + + TEST_EQUAL(ret, expect_ret); + +exit: + if (file != NULL) { + fclose(file); + } + mbedtls_free(hash); + mbedtls_free(data); + mbedtls_x509_crt_free(&certs); + mbedtls_pkcs7_free(&pkcs7); + mbedtls_free(pkcs7_buf); + USE_PSA_DONE(); +} +/* END_CASE */ From 402e80b575fc4767644847936c7d1ba7243762a6 Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Thu, 27 Aug 2026 11:13:09 -0500 Subject: [PATCH 5/9] pkcs7: add APIs for embedded certificate handling Add APIs to retrieve the number of certificates embedded in a PKCS#7 SignedData structure and to extract the embedded certificates into an mbedtls_x509_crt chain owned by the caller. Signed-off-by: Sridhar Markonda --- include/mbedtls/pkcs7.h | 30 ++++++++++++++++++++++++++ library/pkcs7.c | 48 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/include/mbedtls/pkcs7.h b/include/mbedtls/pkcs7.h index 9d89158bc2..29cdcdfe68 100644 --- a/include/mbedtls/pkcs7.h +++ b/include/mbedtls/pkcs7.h @@ -316,6 +316,36 @@ int mbedtls_pkcs7_signed_hash_verify_ext(const mbedtls_pkcs7 *pkcs7, const size_t hashlen, const int use_pkcs7_leaf); +/** + * \brief Return the number of certificates embedded in a PKCS #7 SignedData + * structure. + * + * \param pkcs7 PKCS #7 structure containing signature and certificates. + * + * \return Number of embedded certificates (>= 0), or + * #MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA if \p pkcs7 is NULL. + */ +int mbedtls_pkcs7_get_cert_count(const mbedtls_pkcs7 *pkcs7); + +/** + * \brief Extract embedded certificates from PKCS#7 SignedData. + * + * \details Creates a new certificate chain by parsing the DER-encoded + * certificates embedded in the PKCS#7 structure. The caller + * is responsible for freeing the returned certificate chain. + * + * \param pkcs7 PKCS#7 structure containing embedded certificates. + * \param out_certs On success, pointer to newly allocated certificate chain. + * Caller must free with mbedtls_x509_crt_free() and mbedtls_free(). + * + * \return 0 on success, + * #MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA if \p pkcs7 is NULL, + * \p out_certs is NULL, or no certificates are embedded, + * or a negative error code on failure. + */ +int mbedtls_pkcs7_get_certs(const mbedtls_pkcs7 *pkcs7, + mbedtls_x509_crt **out_certs); + /** * \brief Unallocate all PKCS #7 data and zeroize the memory. * It doesn't free \p pkcs7 itself. This should be done by the caller. diff --git a/library/pkcs7.c b/library/pkcs7.c index 7f58569e4d..9a4254c43e 100644 --- a/library/pkcs7.c +++ b/library/pkcs7.c @@ -1176,6 +1176,54 @@ int mbedtls_pkcs7_signed_hash_verify_ext(const mbedtls_pkcs7 *pkcs7, 1, use_pkcs7_leaf); } +int mbedtls_pkcs7_get_cert_count(const mbedtls_pkcs7 *pkcs7) +{ + if (pkcs7 == NULL) { + return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; + } + + return pkcs7->signed_data.no_of_certs; +} + +int mbedtls_pkcs7_get_certs(const mbedtls_pkcs7 *pkcs7, + mbedtls_x509_crt **out_certs) +{ + const mbedtls_pkcs7_cert *embedded_cert; + mbedtls_x509_crt *cert_chain = NULL; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (pkcs7 == NULL || out_certs == NULL) { + return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; + } + + *out_certs = NULL; + if (pkcs7->signed_data.no_of_certs == 0) { + return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; + } + + cert_chain = (mbedtls_x509_crt *) mbedtls_calloc(1, sizeof(mbedtls_x509_crt)); + if (cert_chain == NULL) { + return MBEDTLS_ERR_PKCS7_ALLOC_FAILED; + } + + mbedtls_x509_crt_init(cert_chain); + + for (embedded_cert = pkcs7->signed_data.certs; embedded_cert != NULL; + embedded_cert = embedded_cert->next) { + ret = mbedtls_x509_crt_parse_der(cert_chain, embedded_cert->cert.raw.p, + embedded_cert->cert.raw.len); + if (ret != 0) { + mbedtls_x509_crt_free(cert_chain); + mbedtls_free(cert_chain); + return ret; + } + } + + *out_certs = cert_chain; + + return 0; +} + /* * Unallocate all pkcs7 data */ From cdb531ffe3f17de7887b51a93c88daca62f33bd7 Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Thu, 27 Aug 2026 11:28:25 -0500 Subject: [PATCH 6/9] tests: Add tests for PKCS#7 embedded certificate APIs The new tests validate certificate count retrieval and embedded certificate extraction. Signed-off-by: Sridhar Markonda --- tests/suites/test_suite_pkcs7.data | 28 +++++++++ tests/suites/test_suite_pkcs7.function | 82 ++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/tests/suites/test_suite_pkcs7.data b/tests/suites/test_suite_pkcs7.data index 74d1947e37..2717133b37 100644 --- a/tests/suites/test_suite_pkcs7.data +++ b/tests/suites/test_suite_pkcs7.data @@ -3363,3 +3363,31 @@ pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_data_invalid_KU_cert.der" PKCS7 Signed Data Verify Pass: Root-Signed Leaf Certificate, chain check #804 depends_on:PSA_WANT_ALG_SHA_256:PSA_HAVE_ALG_RSA_PKCS1V15_VERIFY pkcs7_verify_ext:"../framework/data_files/pkcs7_signed_cert_sha256-4.der":"../framework/data_files/pkcs7-rsa-sha256-root-ca-1.pem":"../framework/data_files/pkcs7_data.bin":0:1:0 + +PKCS7 get embedded certs: SignedData with cert #805.0 +depends_on:MBEDTLS_FS_IO:MBEDTLS_X509_CRT_PARSE_C +pkcs7_get_certs:"../framework/data_files/pkcs7_signed_cert_sha256.der":0 + +PKCS7 get embedded certs: SignedData with multiple certs #805.1 +depends_on:MBEDTLS_FS_IO:MBEDTLS_X509_CRT_PARSE_C +pkcs7_get_certs:"../framework/data_files/pkcs7_signed_multi_cert_sha256.der":0 + +PKCS7 get embedded certs: SignedData without certs #805.2 +depends_on:MBEDTLS_FS_IO:MBEDTLS_X509_CRT_PARSE_C +pkcs7_get_certs:"../framework/data_files/pkcs7_signed_without_cert.der":MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA + +PKCS7 embedded cert count: SignedData with certificates #806.0 +depends_on:MBEDTLS_FS_IO +pkcs7_get_cert_count:"../framework/data_files/pkcs7_signed_cert_sha256.der":1 + +PKCS7 embedded cert count: SignedData without certificates #806.1 +depends_on:MBEDTLS_FS_IO +pkcs7_get_cert_count:"../framework/data_files/pkcs7_signed_without_cert.der":0 + +PKCS7 embedded cert count: SignedData with multiple certs #806.2 +depends_on:MBEDTLS_FS_IO +pkcs7_get_cert_count:"../framework/data_files/pkcs7_signed_multi_cert_sha256.der":4 + +PKCS7 embedded cert count: NULL pkcs7 returns error #806.3 +depends_on:MBEDTLS_FS_IO +pkcs7_get_cert_count:"NULL":MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA diff --git a/tests/suites/test_suite_pkcs7.function b/tests/suites/test_suite_pkcs7.function index e5d2b694c7..3e319e4f7b 100644 --- a/tests/suites/test_suite_pkcs7.function +++ b/tests/suites/test_suite_pkcs7.function @@ -318,3 +318,85 @@ exit: USE_PSA_DONE(); } /* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_FS_IO:MBEDTLS_X509_CRT_PARSE_C */ +void pkcs7_get_certs(char *pkcs7_file, int expected_ret) +{ + unsigned char *pkcs7_buf = NULL; + size_t buflen; + int ret; + char info[512]; + + mbedtls_pkcs7 pkcs7; + mbedtls_x509_crt *certs = NULL; + + USE_PSA_INIT(); + + mbedtls_pkcs7_init(&pkcs7); + + ret = mbedtls_pk_load_file(pkcs7_file, &pkcs7_buf, &buflen); + TEST_EQUAL(ret, 0); + + ret = mbedtls_pkcs7_parse_der(&pkcs7, pkcs7_buf, buflen); + TEST_EQUAL(ret, MBEDTLS_PKCS7_SIGNED_DATA); + + ret = mbedtls_pkcs7_get_certs(&pkcs7, &certs); + TEST_EQUAL(ret, expected_ret); + + if (expected_ret == 0) { + TEST_ASSERT(certs != NULL); + + /* deep copy check */ + mbedtls_pkcs7_free(&pkcs7); + mbedtls_x509_crt_info(info, sizeof(info), " ", certs); + TEST_ASSERT(strlen(info) > 0); + + mbedtls_x509_crt_free(certs); + mbedtls_free(certs); + certs = NULL; + } else { + TEST_EQUAL(certs, NULL); + } + +exit: + if (certs != NULL) { + mbedtls_x509_crt_free(certs); + mbedtls_free(certs); + } + mbedtls_free(pkcs7_buf); + mbedtls_pkcs7_free(&pkcs7); + USE_PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_FS_IO:MBEDTLS_PKCS7_C */ +void pkcs7_get_cert_count(char *pkcs7_file, int expected) +{ + int ret; + mbedtls_pkcs7 pkcs7; + unsigned char *buf = NULL; + size_t len = 0; + + USE_PSA_INIT(); + + mbedtls_pkcs7_init(&pkcs7); + + if (strcmp(pkcs7_file, "NULL") != 0) { + ret = mbedtls_pk_load_file(pkcs7_file, &buf, &len); + TEST_EQUAL(ret, 0); + + ret = mbedtls_pkcs7_parse_der(&pkcs7, buf, len); + TEST_EQUAL(ret, MBEDTLS_PKCS7_SIGNED_DATA); + } + + ret = mbedtls_pkcs7_get_cert_count( + (strcmp(pkcs7_file, "NULL") != 0) ? &pkcs7 : NULL); + + TEST_EQUAL(ret, expected); + +exit: + mbedtls_pkcs7_free(&pkcs7); + mbedtls_free(buf); + USE_PSA_DONE(); +} +/* END_CASE */ From a5ef02924f7f46632041abdf61b2c71f9f0bf67c Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Fri, 28 Aug 2026 09:41:30 -0500 Subject: [PATCH 7/9] Changelog: add PKCS#7 API and feature entries Signed-off-by: Sridhar Markonda --- ChangeLog | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/ChangeLog b/ChangeLog index 76d65c6ac2..729026f979 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,33 @@ Mbed TLS ChangeLog (Sorted per branch, date) += Mbed TLS x.x.x branch released xxxx-xx-xx + +API changes + * Add the public type mbedtls_pkcs7_cert and the error codes + MBEDTLS_ERR_PKCS7_CERT_VERIFY_FAILED and + MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND. + + * Add the APIs mbedtls_pkcs7_signed_data_verify_ext(), + mbedtls_pkcs7_signed_hash_verify_ext(), + mbedtls_pkcs7_get_cert_count(), and mbedtls_pkcs7_get_certs(). + + * Update mbedtls_pkcs7_signed_data_verify() and + mbedtls_pkcs7_signed_hash_verify() to accept a const + mbedtls_pkcs7 pointer. + + * mbedtls_pkcs7_signed_data_verify() and mbedtls_pkcs7_signed_hash_verify() + now returns MBEDTLS_ERR_PKCS7_SIGNER_CERT_NOT_FOUND when the supplied + certificate does not match the signer certificate. + +Features + * Add support for multiple certificates embedded in PKCS #7 + SignedData structures. + + * Add support for PKCS #7 signature verification using either signer + certificates embedded in the PKCS #7 structure or certificates + supplied by the application. Certificate chain validation for + PKCS #7 embedded signer certificates is also supported. + = Mbed TLS 4.2.0 branch released 2026-07-07 Features From abd4720cfe81040a74a5a15bcbfef26c5cb85b09 Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Thu, 3 Sep 2026 03:46:40 -0500 Subject: [PATCH 8/9] tests: fix pointer comparison in PKCS7 tests Replace TEST_EQUAL with TEST_ASSERT for pointer comparison to avoid -Werror=pointer-to-int-cast failures on 32-bit targets. Fixes MinGW CI failure. Signed-off-by: Sridhar Markonda --- tests/suites/test_suite_pkcs7.function | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/suites/test_suite_pkcs7.function b/tests/suites/test_suite_pkcs7.function index 3e319e4f7b..b66a54ae7c 100644 --- a/tests/suites/test_suite_pkcs7.function +++ b/tests/suites/test_suite_pkcs7.function @@ -355,7 +355,7 @@ void pkcs7_get_certs(char *pkcs7_file, int expected_ret) mbedtls_free(certs); certs = NULL; } else { - TEST_EQUAL(certs, NULL); + TEST_ASSERT(certs == NULL); } exit: From e1c2286142e179d786af37083b82ead11fc7d9a3 Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Thu, 3 Sep 2026 03:50:47 -0500 Subject: [PATCH 9/9] pkcs7: fix indentation Signed-off-by: Sridhar Markonda --- include/mbedtls/pkcs7.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mbedtls/pkcs7.h b/include/mbedtls/pkcs7.h index 29cdcdfe68..2875636039 100644 --- a/include/mbedtls/pkcs7.h +++ b/include/mbedtls/pkcs7.h @@ -344,7 +344,7 @@ int mbedtls_pkcs7_get_cert_count(const mbedtls_pkcs7 *pkcs7); * or a negative error code on failure. */ int mbedtls_pkcs7_get_certs(const mbedtls_pkcs7 *pkcs7, - mbedtls_x509_crt **out_certs); + mbedtls_x509_crt **out_certs); /** * \brief Unallocate all PKCS #7 data and zeroize the memory.