Squashed commit upgrading to mbedtls-3.4.0
Squash merging branch import/mbedtls-3.4.0
8225713449d3 ("libmbedtls: fix unrecognized compiler option")
f03730842d7b ("core: ltc: configure internal MD5")
2b0d0c50127c ("core: ltc: configure internal SHA-1 and SHA-224")
0e48a6e17630 ("libmedtls: core: update to mbedTLS 3.4.0 API")
049882b143af ("libutee: update to mbedTLS 3.4.0 API")
982307bf6169 ("core: LTC mpi_desc.c: update to mbedTLS 3.4.0 API")
33218e9eff7b ("ta: pkcs11: update to mbedTLS 3.4.0 API")
6956420cc064 ("libmbedtls: fix cipher_wrap.c for NIST AES Key Wrap mode")
ad67ef0b43fd ("libmbedtls: fix cipher_wrap.c for chacha20 and chachapoly")
7300f4d97bbf ("libmbedtls: add fault mitigation in mbedtls_rsa_rsassa_pkcs1_v15_verify()")
cec89b62a86d ("libmbedtls: add fault mitigation in mbedtls_rsa_rsassa_pss_verify_ext()")
e7e048796c44 ("libmbedtls: add SM2 curve")
096beff2cd31 ("libmbedtls: mbedtls_mpi_exp_mod(): optimize mempool usage")
7108668efd3f ("libmbedtls: mbedtls_mpi_exp_mod(): reduce stack usage")
0ba4eb8d0572 ("libmbedtls: mbedtls_mpi_exp_mod() initialize W")
3fd6ecf00382 ("libmbedtls: fix no CRT issue")
d5ea7e9e9aa7 ("libmbedtls: add interfaces in mbedtls for context memory operation")
2b0fb3f1fa3d ("libmedtls: mpi_miller_rabin: increase count limit")
2c3301ab99bb ("libmbedtls: add mbedtls_mpi_init_mempool()")
9a111f0da04b ("libmbedtls: make mbedtls_mpi_mont*() available")
804fe3a374f5 ("mbedtls: configure mbedtls to reach for config")
b28a41531427 ("mbedtls: remove default include/mbedtls/config.h")
dfafe507bbef ("Import mbedtls-3.4.0")
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
Acked-by: Jerome Forissier <jerome.forissier@linaro.org>
Tested-by: Jerome Forissier <jerome.forissier@linaro.org> (vexpress-qemu_armv8a)
diff --git a/lib/libmbedtls/mbedtls/library/pkcs7.c b/lib/libmbedtls/mbedtls/library/pkcs7.c
new file mode 100644
index 0000000..cf05afd
--- /dev/null
+++ b/lib/libmbedtls/mbedtls/library/pkcs7.c
@@ -0,0 +1,785 @@
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common.h"
+
+#include "mbedtls/build_info.h"
+#if defined(MBEDTLS_PKCS7_C)
+#include "mbedtls/pkcs7.h"
+#include "mbedtls/x509.h"
+#include "mbedtls/asn1.h"
+#include "mbedtls/x509_crt.h"
+#include "mbedtls/x509_crl.h"
+#include "mbedtls/oid.h"
+#include "mbedtls/error.h"
+
+#if defined(MBEDTLS_FS_IO)
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#include "mbedtls/platform.h"
+#include "mbedtls/platform_util.h"
+
+#if defined(MBEDTLS_HAVE_TIME)
+#include "mbedtls/platform_time.h"
+#endif
+#if defined(MBEDTLS_HAVE_TIME_DATE)
+#include <time.h>
+#endif
+
+/**
+ * Initializes the mbedtls_pkcs7 structure.
+ */
+void mbedtls_pkcs7_init(mbedtls_pkcs7 *pkcs7)
+{
+ memset(pkcs7, 0, sizeof(*pkcs7));
+}
+
+static int pkcs7_get_next_content_len(unsigned char **p, unsigned char *end,
+ size_t *len)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ ret = mbedtls_asn1_get_tag(p, end, len, MBEDTLS_ASN1_CONSTRUCTED
+ | MBEDTLS_ASN1_CONTEXT_SPECIFIC);
+ if (ret != 0) {
+ ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, ret);
+ } else if ((size_t) (end - *p) != *len) {
+ ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO,
+ MBEDTLS_ERR_ASN1_LENGTH_MISMATCH);
+ }
+
+ return ret;
+}
+
+/**
+ * version Version
+ * Version ::= INTEGER
+ **/
+static int pkcs7_get_version(unsigned char **p, unsigned char *end, int *ver)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ ret = mbedtls_asn1_get_int(p, end, ver);
+ if (ret != 0) {
+ ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_VERSION, ret);
+ }
+
+ /* If version != 1, return invalid version */
+ if (*ver != MBEDTLS_PKCS7_SUPPORTED_VERSION) {
+ ret = MBEDTLS_ERR_PKCS7_INVALID_VERSION;
+ }
+
+ return ret;
+}
+
+/**
+ * ContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * content
+ * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+ **/
+static int pkcs7_get_content_info_type(unsigned char **p, unsigned char *end,
+ unsigned char **seq_end,
+ mbedtls_pkcs7_buf *pkcs7)
+{
+ size_t len = 0;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ unsigned char *start = *p;
+
+ ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED
+ | MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0) {
+ *p = start;
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, ret);
+ }
+ *seq_end = *p + len;
+ ret = mbedtls_asn1_get_tag(p, *seq_end, &len, MBEDTLS_ASN1_OID);
+ if (ret != 0) {
+ *p = start;
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, ret);
+ }
+
+ pkcs7->tag = MBEDTLS_ASN1_OID;
+ pkcs7->len = len;
+ pkcs7->p = *p;
+ *p += len;
+
+ return ret;
+}
+
+/**
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * This is from x509.h
+ **/
+static int pkcs7_get_digest_algorithm(unsigned char **p, unsigned char *end,
+ mbedtls_x509_buf *alg)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ if ((ret = mbedtls_asn1_get_alg_null(p, end, alg)) != 0) {
+ ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_ALG, ret);
+ }
+
+ return ret;
+}
+
+/**
+ * DigestAlgorithmIdentifiers :: SET of DigestAlgorithmIdentifier
+ **/
+static int pkcs7_get_digest_algorithm_set(unsigned char **p,
+ unsigned char *end,
+ mbedtls_x509_buf *alg)
+{
+ size_t len = 0;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED
+ | MBEDTLS_ASN1_SET);
+ if (ret != 0) {
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_ALG, ret);
+ }
+
+ end = *p + len;
+
+ ret = mbedtls_asn1_get_alg_null(p, end, alg);
+ if (ret != 0) {
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_ALG, ret);
+ }
+
+ /** For now, it assumes there is only one digest algorithm specified **/
+ if (*p != end) {
+ return MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE;
+ }
+
+ return 0;
+}
+
+/**
+ * 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)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ size_t len1 = 0;
+ size_t len2 = 0;
+ unsigned char *end_set, *end_cert, *start;
+
+ ret = mbedtls_asn1_get_tag(p, end, &len1, MBEDTLS_ASN1_CONSTRUCTED
+ | MBEDTLS_ASN1_CONTEXT_SPECIFIC);
+ if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) {
+ return 0;
+ }
+ 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) {
+ return MBEDTLS_ERR_PKCS7_INVALID_CERT;
+ }
+
+ *p = end_cert;
+
+ /*
+ * Since in this version we strictly support single certificate, and reaching
+ * here implies we have parsed successfully, we return 1.
+ */
+ return 1;
+}
+
+/**
+ * EncryptedDigest ::= OCTET STRING
+ **/
+static int pkcs7_get_signature(unsigned char **p, unsigned char *end,
+ mbedtls_pkcs7_buf *signature)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ size_t len = 0;
+
+ ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
+ if (ret != 0) {
+ return ret;
+ }
+
+ signature->tag = MBEDTLS_ASN1_OCTET_STRING;
+ signature->len = len;
+ signature->p = *p;
+
+ *p = *p + len;
+
+ return 0;
+}
+
+static void pkcs7_free_signer_info(mbedtls_pkcs7_signer_info *signer)
+{
+ mbedtls_x509_name *name_cur;
+ mbedtls_x509_name *name_prv;
+
+ if (signer == NULL) {
+ return;
+ }
+
+ name_cur = signer->issuer.next;
+ while (name_cur != NULL) {
+ name_prv = name_cur;
+ name_cur = name_cur->next;
+ mbedtls_free(name_prv);
+ }
+ signer->issuer.next = NULL;
+}
+
+/**
+ * SignerInfo ::= SEQUENCE {
+ * version Version;
+ * issuerAndSerialNumber IssuerAndSerialNumber,
+ * digestAlgorithm DigestAlgorithmIdentifier,
+ * authenticatedAttributes
+ * [0] IMPLICIT Attributes OPTIONAL,
+ * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
+ * encryptedDigest EncryptedDigest,
+ * unauthenticatedAttributes
+ * [1] IMPLICIT Attributes OPTIONAL,
+ * Returns 0 if the signerInfo is valid.
+ * Return negative error code for failure.
+ * Structure must not contain vales for authenticatedAttributes
+ * and unauthenticatedAttributes.
+ **/
+static int pkcs7_get_signer_info(unsigned char **p, unsigned char *end,
+ mbedtls_pkcs7_signer_info *signer,
+ mbedtls_x509_buf *alg)
+{
+ unsigned char *end_signer, *end_issuer_and_sn;
+ int asn1_ret = 0, ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ size_t len = 0;
+
+ asn1_ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED
+ | MBEDTLS_ASN1_SEQUENCE);
+ if (asn1_ret != 0) {
+ goto out;
+ }
+
+ end_signer = *p + len;
+
+ ret = pkcs7_get_version(p, end_signer, &signer->version);
+ if (ret != 0) {
+ goto out;
+ }
+
+ asn1_ret = mbedtls_asn1_get_tag(p, end_signer, &len,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+ if (asn1_ret != 0) {
+ goto out;
+ }
+
+ end_issuer_and_sn = *p + len;
+ /* Parsing IssuerAndSerialNumber */
+ signer->issuer_raw.p = *p;
+
+ asn1_ret = mbedtls_asn1_get_tag(p, end_issuer_and_sn, &len,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+ if (asn1_ret != 0) {
+ goto out;
+ }
+
+ ret = mbedtls_x509_get_name(p, *p + len, &signer->issuer);
+ if (ret != 0) {
+ goto out;
+ }
+
+ signer->issuer_raw.len = *p - signer->issuer_raw.p;
+
+ ret = mbedtls_x509_get_serial(p, end_issuer_and_sn, &signer->serial);
+ if (ret != 0) {
+ goto out;
+ }
+
+ /* ensure no extra or missing bytes */
+ if (*p != end_issuer_and_sn) {
+ ret = MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO;
+ goto out;
+ }
+
+ ret = pkcs7_get_digest_algorithm(p, end_signer, &signer->alg_identifier);
+ if (ret != 0) {
+ goto out;
+ }
+
+ /* Check that the digest algorithm used matches the one provided earlier */
+ if (signer->alg_identifier.tag != alg->tag ||
+ signer->alg_identifier.len != alg->len ||
+ memcmp(signer->alg_identifier.p, alg->p, alg->len) != 0) {
+ ret = MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO;
+ goto out;
+ }
+
+ /* Assume authenticatedAttributes is nonexistent */
+ ret = pkcs7_get_digest_algorithm(p, end_signer, &signer->sig_alg_identifier);
+ if (ret != 0) {
+ goto out;
+ }
+
+ ret = pkcs7_get_signature(p, end_signer, &signer->sig);
+ if (ret != 0) {
+ goto out;
+ }
+
+ /* Do not permit any unauthenticated attributes */
+ if (*p != end_signer) {
+ ret = MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO;
+ }
+
+out:
+ if (asn1_ret != 0 || ret != 0) {
+ pkcs7_free_signer_info(signer);
+ ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO,
+ asn1_ret);
+ }
+
+ return ret;
+}
+
+/**
+ * SignerInfos ::= SET of SignerInfo
+ * Return number of signers added to the signed data,
+ * 0 or higher is valid.
+ * Return negative error code for failure.
+ **/
+static int pkcs7_get_signers_info_set(unsigned char **p, unsigned char *end,
+ mbedtls_pkcs7_signer_info *signers_set,
+ mbedtls_x509_buf *digest_alg)
+{
+ unsigned char *end_set;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ int count = 0;
+ size_t len = 0;
+
+ ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED
+ | MBEDTLS_ASN1_SET);
+ if (ret != 0) {
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO, ret);
+ }
+
+ /* Detect zero signers */
+ if (len == 0) {
+ return 0;
+ }
+
+ end_set = *p + len;
+
+ ret = pkcs7_get_signer_info(p, end_set, signers_set, digest_alg);
+ if (ret != 0) {
+ return ret;
+ }
+ count++;
+
+ mbedtls_pkcs7_signer_info *prev = signers_set;
+ while (*p != end_set) {
+ mbedtls_pkcs7_signer_info *signer =
+ mbedtls_calloc(1, sizeof(mbedtls_pkcs7_signer_info));
+ if (!signer) {
+ ret = MBEDTLS_ERR_PKCS7_ALLOC_FAILED;
+ goto cleanup;
+ }
+
+ ret = pkcs7_get_signer_info(p, end_set, signer, digest_alg);
+ if (ret != 0) {
+ mbedtls_free(signer);
+ goto cleanup;
+ }
+ prev->next = signer;
+ prev = signer;
+ count++;
+ }
+
+ return count;
+
+cleanup:
+ pkcs7_free_signer_info(signers_set);
+ mbedtls_pkcs7_signer_info *signer = signers_set->next;
+ while (signer != NULL) {
+ prev = signer;
+ signer = signer->next;
+ pkcs7_free_signer_info(prev);
+ mbedtls_free(prev);
+ }
+ signers_set->next = NULL;
+ return ret;
+}
+
+/**
+ * SignedData ::= SEQUENCE {
+ * version Version,
+ * digestAlgorithms DigestAlgorithmIdentifiers,
+ * contentInfo ContentInfo,
+ * certificates
+ * [0] IMPLICIT ExtendedCertificatesAndCertificates
+ * OPTIONAL,
+ * crls
+ * [0] IMPLICIT CertificateRevocationLists OPTIONAL,
+ * signerInfos SignerInfos }
+ */
+static int pkcs7_get_signed_data(unsigned char *buf, size_t buflen,
+ mbedtls_pkcs7_signed_data *signed_data)
+{
+ unsigned char *p = buf;
+ unsigned char *end = buf + buflen;
+ unsigned char *end_content_info = NULL;
+ size_t len = 0;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ mbedtls_md_type_t md_alg;
+
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED
+ | MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0) {
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, ret);
+ }
+
+ if (p + len != end) {
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT,
+ MBEDTLS_ERR_ASN1_LENGTH_MISMATCH);
+ }
+
+ /* Get version of signed data */
+ ret = pkcs7_get_version(&p, end, &signed_data->version);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Get digest algorithm */
+ ret = pkcs7_get_digest_algorithm_set(&p, end,
+ &signed_data->digest_alg_identifiers);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = mbedtls_oid_get_md_alg(&signed_data->digest_alg_identifiers, &md_alg);
+ if (ret != 0) {
+ return MBEDTLS_ERR_PKCS7_INVALID_ALG;
+ }
+
+ mbedtls_pkcs7_buf content_type;
+ memset(&content_type, 0, sizeof(content_type));
+ ret = pkcs7_get_content_info_type(&p, end, &end_content_info, &content_type);
+ if (ret != 0) {
+ return ret;
+ }
+ if (MBEDTLS_OID_CMP(MBEDTLS_OID_PKCS7_DATA, &content_type)) {
+ return MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO;
+ }
+
+ if (p != end_content_info) {
+ /* Determine if valid content is present */
+ ret = mbedtls_asn1_get_tag(&p,
+ end_content_info,
+ &len,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC);
+ if (ret != 0) {
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, ret);
+ }
+ p += len;
+ if (p != end_content_info) {
+ return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, ret);
+ }
+ /* Valid content is present - this is not supported */
+ return MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE;
+ }
+
+ /* Look for certificates, there may or may not be any */
+ mbedtls_x509_crt_init(&signed_data->certs);
+ 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
+ * signer info.
+ */
+
+ signed_data->no_of_crls = 0;
+
+ /* Get signers info */
+ ret = pkcs7_get_signers_info_set(&p,
+ end,
+ &signed_data->signers,
+ &signed_data->digest_alg_identifiers);
+ if (ret < 0) {
+ return ret;
+ }
+
+ signed_data->no_of_signers = ret;
+
+ /* Don't permit trailing data */
+ if (p != end) {
+ return MBEDTLS_ERR_PKCS7_INVALID_FORMAT;
+ }
+
+ return 0;
+}
+
+int mbedtls_pkcs7_parse_der(mbedtls_pkcs7 *pkcs7, const unsigned char *buf,
+ const size_t buflen)
+{
+ unsigned char *p;
+ unsigned char *end;
+ size_t len = 0;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ if (pkcs7 == NULL) {
+ return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA;
+ }
+
+ /* make an internal copy of the buffer for parsing */
+ pkcs7->raw.p = p = mbedtls_calloc(1, buflen);
+ if (pkcs7->raw.p == NULL) {
+ ret = MBEDTLS_ERR_PKCS7_ALLOC_FAILED;
+ goto out;
+ }
+ memcpy(p, buf, buflen);
+ pkcs7->raw.len = buflen;
+ end = p + buflen;
+
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED
+ | MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0) {
+ ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, ret);
+ goto out;
+ }
+
+ if ((size_t) (end - p) != len) {
+ ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT,
+ MBEDTLS_ERR_ASN1_LENGTH_MISMATCH);
+ goto out;
+ }
+
+ if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID)) != 0) {
+ if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) {
+ goto out;
+ }
+ p = pkcs7->raw.p;
+ len = buflen;
+ goto try_data;
+ }
+
+ if (MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_SIGNED_DATA, p, len)) {
+ /* OID is not MBEDTLS_OID_PKCS7_SIGNED_DATA, which is the only supported feature */
+ if (!MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_DATA, p, len)
+ || !MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_ENCRYPTED_DATA, p, len)
+ || !MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_ENVELOPED_DATA, p, len)
+ || !MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_SIGNED_AND_ENVELOPED_DATA, p, len)
+ || !MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_DIGESTED_DATA, p, len)) {
+ /* OID is valid according to the spec, but unsupported */
+ ret = MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE;
+ } else {
+ /* OID is invalid according to the spec */
+ ret = MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA;
+ }
+ goto out;
+ }
+
+ p += len;
+
+ ret = pkcs7_get_next_content_len(&p, end, &len);
+ if (ret != 0) {
+ goto out;
+ }
+
+ /* ensure no extra/missing data */
+ if (p + len != end) {
+ ret = MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA;
+ goto out;
+ }
+
+try_data:
+ ret = pkcs7_get_signed_data(p, len, &pkcs7->signed_data);
+ if (ret != 0) {
+ goto out;
+ }
+
+ ret = MBEDTLS_PKCS7_SIGNED_DATA;
+
+out:
+ if (ret < 0) {
+ mbedtls_pkcs7_free(pkcs7);
+ }
+
+ 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)
+{
+ 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;
+
+ if (pkcs7->signed_data.no_of_signers == 0) {
+ return MBEDTLS_ERR_PKCS7_INVALID_CERT;
+ }
+
+ if (mbedtls_x509_time_is_past(&cert->valid_to) ||
+ mbedtls_x509_time_is_future(&cert->valid_from)) {
+ return MBEDTLS_ERR_PKCS7_CERT_DATE_INVALID;
+ }
+
+ ret = mbedtls_oid_get_md_alg(&pkcs7->signed_data.digest_alg_identifiers, &md_alg);
+ if (ret != 0) {
+ return ret;
+ }
+
+ md_info = mbedtls_md_info_from_type(md_alg);
+ if (md_info == NULL) {
+ return MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
+ }
+
+ hash = mbedtls_calloc(mbedtls_md_get_size(md_info), 1);
+ if (hash == NULL) {
+ return MBEDTLS_ERR_PKCS7_ALLOC_FAILED;
+ }
+
+ /* BEGIN must free hash before jumping out */
+ if (is_data_hash) {
+ if (datalen != mbedtls_md_get_size(md_info)) {
+ ret = MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
+ } else {
+ memcpy(hash, data, datalen);
+ }
+ } else {
+ ret = mbedtls_md(md_info, data, datalen, hash);
+ }
+ if (ret != 0) {
+ mbedtls_free(hash);
+ return MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
+ }
+
+ /* 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(&pk_cxt, md_alg, hash,
+ mbedtls_md_get_size(md_info),
+ signer->sig.p, signer->sig.len);
+
+ if (ret == 0) {
+ break;
+ }
+ }
+
+ mbedtls_free(hash);
+ /* END must free hash before jumping out */
+ return ret;
+}
+
+int mbedtls_pkcs7_signed_data_verify(mbedtls_pkcs7 *pkcs7,
+ const mbedtls_x509_crt *cert,
+ const unsigned char *data,
+ size_t datalen)
+{
+ if (data == NULL) {
+ return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA;
+ }
+ return mbedtls_pkcs7_data_or_hash_verify(pkcs7, cert, data, datalen, 0);
+}
+
+int mbedtls_pkcs7_signed_hash_verify(mbedtls_pkcs7 *pkcs7,
+ const mbedtls_x509_crt *cert,
+ const unsigned char *hash,
+ size_t hashlen)
+{
+ if (hash == NULL) {
+ return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA;
+ }
+ return mbedtls_pkcs7_data_or_hash_verify(pkcs7, cert, hash, hashlen, 1);
+}
+
+/*
+ * Unallocate all pkcs7 data
+ */
+void mbedtls_pkcs7_free(mbedtls_pkcs7 *pkcs7)
+{
+ mbedtls_pkcs7_signer_info *signer_cur;
+ mbedtls_pkcs7_signer_info *signer_prev;
+
+ if (pkcs7 == NULL || pkcs7->raw.p == NULL) {
+ return;
+ }
+
+ mbedtls_free(pkcs7->raw.p);
+
+ mbedtls_x509_crt_free(&pkcs7->signed_data.certs);
+ mbedtls_x509_crl_free(&pkcs7->signed_data.crl);
+
+ signer_cur = pkcs7->signed_data.signers.next;
+ pkcs7_free_signer_info(&pkcs7->signed_data.signers);
+ while (signer_cur != NULL) {
+ signer_prev = signer_cur;
+ signer_cur = signer_prev->next;
+ pkcs7_free_signer_info(signer_prev);
+ mbedtls_free(signer_prev);
+ }
+
+ pkcs7->raw.p = NULL;
+}
+
+#endif