mirror of
				https://github.com/Mbed-TLS/mbedtls.git
				synced 2025-10-24 13:32:59 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			774 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			774 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *  Copyright The Mbed TLS Contributors
 | |
|  *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
 | |
|  */
 | |
| #include "common.h"
 | |
| 
 | |
| #include "mbedtls/build_info.h"
 | |
| #if defined(MBEDTLS_PKCS7_C)
 | |
| #include "mbedtls/pkcs7.h"
 | |
| #include "x509_internal.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 =  (size_t) (*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
 |