mirror of
				https://github.com/cuberite/polarssl.git
				synced 2025-11-03 12:11:27 -05:00 
			
		
		
		
	mbedtls_x509_name allocates memory, which must be freed if there is a subsequent error. Credit to OSS-Fuzz (https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=53811). Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
		
			
				
	
	
		
			728 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			728 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 *  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"
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.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 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_FORMAT, ret );
 | 
						|
    }
 | 
						|
 | 
						|
    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,
 | 
						|
                                        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 ) );
 | 
						|
    }
 | 
						|
 | 
						|
    ret = mbedtls_asn1_get_tag( p, 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;
 | 
						|
 | 
						|
    if( ( ret = mbedtls_asn1_get_tag( p, end, &len1, MBEDTLS_ASN1_CONSTRUCTED
 | 
						|
                    | MBEDTLS_ASN1_CONTEXT_SPECIFIC ) ) != 0 )
 | 
						|
    {
 | 
						|
        if( ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG )
 | 
						|
            return( 0 );
 | 
						|
        else
 | 
						|
            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 );
 | 
						|
    }
 | 
						|
 | 
						|
    *p = start;
 | 
						|
    if( ( ret = mbedtls_x509_crt_parse_der( certs, *p, len1 ) ) < 0 )
 | 
						|
    {
 | 
						|
        return( MBEDTLS_ERR_PKCS7_INVALID_CERT );
 | 
						|
    }
 | 
						|
 | 
						|
    *p = *p + len1;
 | 
						|
 | 
						|
    /*
 | 
						|
     * 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 );
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * 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 )
 | 
						|
{
 | 
						|
    unsigned char *end_signer;
 | 
						|
    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;
 | 
						|
 | 
						|
    /* Parsing IssuerAndSerialNumber */
 | 
						|
    signer->issuer_raw.p = *p;
 | 
						|
 | 
						|
    asn1_ret = mbedtls_asn1_get_tag( p, end_signer, &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_signer, &signer->serial );
 | 
						|
    if( ret != 0 )
 | 
						|
        goto out;
 | 
						|
 | 
						|
    ret = pkcs7_get_digest_algorithm( p, end_signer, &signer->alg_identifier );
 | 
						|
    if( ret != 0 )
 | 
						|
        goto out;
 | 
						|
 | 
						|
    /* Asssume 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 = MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO,
 | 
						|
                                    asn1_ret );
 | 
						|
    else if( ret != 0 )
 | 
						|
        ret = MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO;
 | 
						|
 | 
						|
    return( ret );
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * 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 )
 | 
						|
{
 | 
						|
    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 );
 | 
						|
    if( ret != 0 )
 | 
						|
        goto cleanup;
 | 
						|
    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 );
 | 
						|
        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_set;
 | 
						|
    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 ) );
 | 
						|
    }
 | 
						|
 | 
						|
    end_set = p + len;
 | 
						|
 | 
						|
    /* Get version of signed data */
 | 
						|
    ret = pkcs7_get_version( &p, end_set, &signed_data->version );
 | 
						|
    if( ret != 0 )
 | 
						|
        return( ret );
 | 
						|
 | 
						|
    /* Get digest algorithm */
 | 
						|
    ret = pkcs7_get_digest_algorithm_set( &p, end_set,
 | 
						|
            &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 );
 | 
						|
    }
 | 
						|
 | 
						|
    /* Do not expect any content */
 | 
						|
    ret = pkcs7_get_content_info_type( &p, end_set, &signed_data->content.oid );
 | 
						|
    if( ret != 0 )
 | 
						|
        return( ret );
 | 
						|
 | 
						|
    if( MBEDTLS_OID_CMP( MBEDTLS_OID_PKCS7_DATA, &signed_data->content.oid ) )
 | 
						|
    {
 | 
						|
        return( MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO );
 | 
						|
    }
 | 
						|
 | 
						|
    /* Look for certificates, there may or may not be any */
 | 
						|
    mbedtls_x509_crt_init( &signed_data->certs );
 | 
						|
    ret = pkcs7_get_certificates( &p, end_set, &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_set, &signed_data->signers );
 | 
						|
    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;
 | 
						|
    int isoidset = 0;
 | 
						|
 | 
						|
    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 = pkcs7_get_content_info_type( &p, end, &pkcs7->content_type_oid );
 | 
						|
    if( ret != 0 )
 | 
						|
    {
 | 
						|
        len = buflen;
 | 
						|
        goto try_data;
 | 
						|
    }
 | 
						|
 | 
						|
    if( ! MBEDTLS_OID_CMP( MBEDTLS_OID_PKCS7_DATA, &pkcs7->content_type_oid )
 | 
						|
     || ! MBEDTLS_OID_CMP( MBEDTLS_OID_PKCS7_ENCRYPTED_DATA, &pkcs7->content_type_oid )
 | 
						|
     || ! MBEDTLS_OID_CMP( MBEDTLS_OID_PKCS7_ENVELOPED_DATA, &pkcs7->content_type_oid )
 | 
						|
     || ! MBEDTLS_OID_CMP( MBEDTLS_OID_PKCS7_SIGNED_AND_ENVELOPED_DATA, &pkcs7->content_type_oid )
 | 
						|
     || ! MBEDTLS_OID_CMP( MBEDTLS_OID_PKCS7_DIGESTED_DATA, &pkcs7->content_type_oid )
 | 
						|
     || ! MBEDTLS_OID_CMP( MBEDTLS_OID_PKCS7_ENCRYPTED_DATA, &pkcs7->content_type_oid ) )
 | 
						|
    {
 | 
						|
        ret =  MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE;
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    if( MBEDTLS_OID_CMP( MBEDTLS_OID_PKCS7_SIGNED_DATA, &pkcs7->content_type_oid ) )
 | 
						|
    {
 | 
						|
        ret = MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA;
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    isoidset = 1;
 | 
						|
 | 
						|
    ret = pkcs7_get_next_content_len( &p, end, &len );
 | 
						|
    if( ret != 0 )
 | 
						|
        goto out;
 | 
						|
 | 
						|
try_data:
 | 
						|
    ret = pkcs7_get_signed_data( p, len, &pkcs7->signed_data );
 | 
						|
    if ( ret != 0 )
 | 
						|
        goto out;
 | 
						|
 | 
						|
    if ( !isoidset )
 | 
						|
    {
 | 
						|
        pkcs7->content_type_oid.tag = MBEDTLS_ASN1_OID;
 | 
						|
        pkcs7->content_type_oid.len = MBEDTLS_OID_SIZE( MBEDTLS_OID_PKCS7_SIGNED_DATA );
 | 
						|
        pkcs7->content_type_oid.p = (unsigned char *)MBEDTLS_OID_PKCS7_SIGNED_DATA;
 | 
						|
    }
 | 
						|
 | 
						|
    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 );
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * 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'.
 | 
						|
     *
 | 
						|
     * We could also cache hashes by md, so if there are several sigs all using
 | 
						|
     * the same algo we don't recalculate the hash each time.
 | 
						|
     */
 | 
						|
    for( signer = &pkcs7->signed_data.signers; signer; signer = signer->next )
 | 
						|
    {
 | 
						|
        ret = mbedtls_oid_get_md_alg( &signer->alg_identifier, &md_alg );
 | 
						|
        if( ret != 0 )
 | 
						|
        {
 | 
						|
            ret = MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        md_info = mbedtls_md_info_from_type( md_alg );
 | 
						|
        if( md_info == NULL )
 | 
						|
        {
 | 
						|
            ret = MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        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 )
 | 
						|
        {
 | 
						|
            ret = MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
 | 
						|
            mbedtls_free( hash );
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        ret = mbedtls_pk_verify( &pk_cxt, md_alg, hash,
 | 
						|
                                 mbedtls_md_get_size( md_info ),
 | 
						|
                                 signer->sig.p, signer->sig.len );
 | 
						|
        mbedtls_free( hash );
 | 
						|
        /* END must free hash before jumping out */
 | 
						|
 | 
						|
        if( ret == 0 )
 | 
						|
            break;
 | 
						|
    }
 | 
						|
 | 
						|
    return( ret );
 | 
						|
}
 | 
						|
int mbedtls_pkcs7_signed_data_verify( mbedtls_pkcs7 *pkcs7,
 | 
						|
                                      const mbedtls_x509_crt *cert,
 | 
						|
                                      const unsigned char *data,
 | 
						|
                                      size_t datalen )
 | 
						|
{
 | 
						|
    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 )
 | 
						|
{
 | 
						|
    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
 |