[PATCH 7/8] efi_loader: signature: rework for intermediate certificates support

Heinrich Schuchardt xypron.glpk at gmx.de
Tue Jun 9 18:34:32 CEST 2020


On 6/9/20 7:14 AM, AKASHI Takahiro wrote:
> In this commit, efi_signature_verify(with_sigdb) will be re-implemented
> using pcks7_verify_one() in order to support certificates chain, where
> the signer's certificate will be signed by an intermediate CA (certificate
> authority) and the latter's certificate will also be signed by another CA
> and so on.
>
> What we need to do here is to search for certificates in a signature,
> build up a chain of certificates and verify one by one. pkcs7_verify_one()
> handles most of these steps except the last one.
>
> pkcs7_verify_one() returns, if succeeded, the last certificate to verify,
> which can be either a self-signed one or one that should be signed by one
> of certificates in "db". Re-worked efi_signature_verify() will take care
> of this step.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi at linaro.org>
> ---
>  include/efi_loader.h              |   8 +-
>  lib/efi_loader/Kconfig            |   1 +
>  lib/efi_loader/efi_image_loader.c |   2 +-
>  lib/efi_loader/efi_signature.c    | 385 ++++++++++++++----------------
>  lib/efi_loader/efi_variable.c     |   5 +-
>  5 files changed, 188 insertions(+), 213 deletions(-)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index a95f9b339027..086528c305ef 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -767,10 +767,10 @@ bool efi_signature_lookup_digest(struct efi_image_regions *regs,
>  bool efi_signature_verify_one(struct efi_image_regions *regs,
>  			      struct pkcs7_message *msg,
>  			      struct efi_signature_store *db);
> -bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs,
> -				     struct pkcs7_message *msg,
> -				     struct efi_signature_store *db,
> -				     struct efi_signature_store *dbx);
> +bool efi_signature_verify(struct efi_image_regions *regs,
> +			  struct pkcs7_message *msg,
> +			  struct efi_signature_store *db,
> +			  struct efi_signature_store *dbx);
>  bool efi_signature_check_signers(struct pkcs7_message *msg,
>  				 struct efi_signature_store *dbx);
>
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index aad37b715505..5fc178180721 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -157,6 +157,7 @@ config EFI_SECURE_BOOT
>  	select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
>  	select X509_CERTIFICATE_PARSER
>  	select PKCS7_MESSAGE_PARSER
> +	select PKCS7_VERIFY
>  	default n
>  	help
>  	  Select this option to enable EFI secure boot support.
> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> index 584a841116df..029bb035e5ce 100644
> --- a/lib/efi_loader/efi_image_loader.c
> +++ b/lib/efi_loader/efi_image_loader.c
> @@ -642,7 +642,7 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
>  		}
>
>  		/* try white-list */
> -		if (efi_signature_verify_with_sigdb(regs, msg, db, dbx))
> +		if (efi_signature_verify(regs, msg, db, dbx))
>  			continue;
>
>  		debug("Signature was not verified by \"db\"\n");
> diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c
> index a87cbe520f56..55d584b20ac0 100644
> --- a/lib/efi_loader/efi_signature.c
> +++ b/lib/efi_loader/efi_signature.c
> @@ -10,7 +10,9 @@
>  #include <image.h>
>  #include <hexdump.h>
>  #include <malloc.h>
> +#include <crypto/pkcs7.h>
>  #include <crypto/pkcs7_parser.h>
> +#include <crypto/public_key.h>
>  #include <crypto/x509_parser.h>

This line is missing in my efi-2020-10 branch.
This include is already included via pkcs7_parser.h.
Nothing to worry about. Just in case you resubmit, please, rebase.

Best regards

Heinrich

>  #include <linux/compat.h>
>  #include <linux/oid_registry.h>
> @@ -61,143 +63,6 @@ static bool efi_hash_regions(struct image_region *regs, int count,
>  	return true;
>  }
>
> -/**
> - * efi_hash_msg_content - calculate a hash value of contentInfo
> - * @msg:	Signature
> - * @hash:	Pointer to a pointer to buffer holding a hash value
> - * @size:	Size of buffer to be returned
> - *
> - * Calculate a sha256 value of contentInfo in @msg and return a value in @hash.
> - *
> - * Return:	true on success, false on error
> - */
> -static bool efi_hash_msg_content(struct pkcs7_message *msg, void **hash,
> -				 size_t *size)
> -{
> -	struct image_region regtmp;
> -
> -	regtmp.data = msg->data;
> -	regtmp.size = msg->data_len;
> -
> -	return efi_hash_regions(&regtmp, 1, hash, size);
> -}
> -
> -/**
> - * efi_signature_verify - verify a signature with a certificate
> - * @regs:		List of regions to be authenticated
> - * @signed_info:	Pointer to PKCS7's signed_info
> - * @cert:		x509 certificate
> - *
> - * Signature pointed to by @signed_info against image pointed to by @regs
> - * is verified by a certificate pointed to by @cert.
> - * @signed_info holds a signature, including a message digest which is to be
> - * compared with a hash value calculated from @regs.
> - *
> - * Return:	true if signature is verified, false if not
> - */
> -static bool efi_signature_verify(struct efi_image_regions *regs,
> -				 struct pkcs7_message *msg,
> -				 struct pkcs7_signed_info *ps_info,
> -				 struct x509_certificate *cert)
> -{
> -	struct image_sign_info info;
> -	struct image_region regtmp[2];
> -	void *hash;
> -	size_t size;
> -	char c;
> -	bool verified;
> -
> -	EFI_PRINT("%s: Enter, %p, %p, %p(issuer: %s, subject: %s)\n", __func__,
> -		  regs, ps_info, cert, cert->issuer, cert->subject);
> -
> -	verified = false;
> -
> -	memset(&info, '\0', sizeof(info));
> -	info.padding = image_get_padding_algo("pkcs-1.5");
> -	/*
> -	 * Note: image_get_[checksum|crypto]_algo takes an string
> -	 * argument like "<checksum>,<crypto>"
> -	 * TODO: support other hash algorithms
> -	 */
> -	if (!strcmp(ps_info->sig->hash_algo, "sha1")) {
> -		info.checksum = image_get_checksum_algo("sha1,rsa2048");
> -		info.name = "sha1,rsa2048";
> -	} else if (!strcmp(ps_info->sig->hash_algo, "sha256")) {
> -		info.checksum = image_get_checksum_algo("sha256,rsa2048");
> -		info.name = "sha256,rsa2048";
> -	} else {
> -		EFI_PRINT("unknown msg digest algo: %s\n",
> -			  ps_info->sig->hash_algo);
> -		goto out;
> -	}
> -	info.crypto = image_get_crypto_algo(info.name);
> -
> -	info.key = cert->pub->key;
> -	info.keylen = cert->pub->keylen;
> -
> -	/* verify signature */
> -	EFI_PRINT("%s: crypto: %s, signature len:%x\n", __func__,
> -		  info.name, ps_info->sig->s_size);
> -	if (ps_info->aa_set & (1UL << sinfo_has_message_digest)) {
> -		EFI_PRINT("%s: RSA verify authentication attribute\n",
> -			  __func__);
> -		/*
> -		 * NOTE: This path will be executed only for
> -		 * PE image authentication
> -		 */
> -
> -		/* check if hash matches digest first */
> -		EFI_PRINT("checking msg digest first, len:0x%x\n",
> -			  ps_info->msgdigest_len);
> -
> -#ifdef DEBUG
> -		EFI_PRINT("hash in database:\n");
> -		print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
> -			       ps_info->msgdigest, ps_info->msgdigest_len,
> -			       false);
> -#endif
> -		/* against contentInfo first */
> -		hash = NULL;
> -		if ((msg->data && efi_hash_msg_content(msg, &hash, &size)) ||
> -				/* for signed image */
> -		    efi_hash_regions(regs->reg, regs->num, &hash, &size)) {
> -				/* for authenticated variable */
> -			if (ps_info->msgdigest_len != size ||
> -			    memcmp(hash, ps_info->msgdigest, size)) {
> -				EFI_PRINT("Digest doesn't match\n");
> -				free(hash);
> -				goto out;
> -			}
> -
> -			free(hash);
> -		} else {
> -			EFI_PRINT("Digesting image failed\n");
> -			goto out;
> -		}
> -
> -		/* against digest */
> -		c = 0x31;
> -		regtmp[0].data = &c;
> -		regtmp[0].size = 1;
> -		regtmp[1].data = ps_info->authattrs;
> -		regtmp[1].size = ps_info->authattrs_len;
> -
> -		if (!rsa_verify(&info, regtmp, 2,
> -				ps_info->sig->s, ps_info->sig->s_size))
> -			verified = true;
> -	} else {
> -		EFI_PRINT("%s: RSA verify content data\n", __func__);
> -		/* against all data */
> -		if (!rsa_verify(&info, regs->reg, regs->num,
> -				ps_info->sig->s, ps_info->sig->s_size))
> -			verified = true;
> -	}
> -
> -out:
> -	EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified);
> -	return verified;
> -}
> -
>  /**
>   * efi_signature_lookup_digest - search for an image's digest in sigdb
>   * @regs:	List of regions to be authenticated
> @@ -261,61 +126,127 @@ out:
>  }
>
>  /**
> - * efi_signature_verify_with_list - verify a signature with signature list
> - * @regs:		List of regions to be authenticated
> - * @msg:		Signature
> - * @signed_info:	Pointer to PKCS7's signed_info
> - * @siglist:		Signature list for certificates
> - * @valid_cert:		x509 certificate that verifies this signature
> + * efi_lookup_certificate - find a certificate within db
> + * @msg:	Signature
> + * @db:		Signature database
>   *
> - * Signature pointed to by @signed_info against image pointed to by @regs
> - * is verified by signature list pointed to by @siglist.
> - * Signature database is a simple concatenation of one or more
> - * signature list(s).
> + * Search signature database pointed to by @db and find a certificate
> + * pointed to by @cert.
>   *
> - * Return:	true if signature is verified, false if not
> + * Return:	true if found, false otherwise.
>   */
> -static
> -bool efi_signature_verify_with_list(struct efi_image_regions *regs,
> -				    struct pkcs7_message *msg,
> -				    struct pkcs7_signed_info *signed_info,
> -				    struct efi_signature_store *siglist,
> -				    struct x509_certificate **valid_cert)
> +static bool efi_lookup_certificate(struct x509_certificate *cert,
> +				   struct efi_signature_store *db)
>  {
> -	struct x509_certificate *cert;
> +	struct efi_signature_store *siglist;
>  	struct efi_sig_data *sig_data;
> -	bool verified = false;
> +	struct image_region reg[1];
> +	void *hash = NULL, *hash_tmp = NULL;
> +	size_t size = 0;
> +	bool found = false;
>
> -	EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__,
> -		  regs, signed_info, siglist, valid_cert);
> +	EFI_PRINT("%s: Enter, %p, %p\n", __func__, cert, db);
>
> -	if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509)) {
> -		EFI_PRINT("Signature type is not supported: %pUl\n",
> -			  &siglist->sig_type);
> +	if (!cert || !db || !db->sig_data_list)
>  		goto out;
> -	}
>
> -	/* go through the list */
> -	for (sig_data = siglist->sig_data_list; sig_data;
> -	     sig_data = sig_data->next) {
> -		/* TODO: support owner check based on policy */
> +	/*
> +	 * TODO: identify a certificate using sha256 digest
> +	 * Is there any better way?
> +	 */
> +	/* calculate hash of TBSCertificate */
> +	reg[0].data = cert->tbs;
> +	reg[0].size = cert->tbs_size;
> +	if (!efi_hash_regions(reg, 1, &hash, &size))
> +		goto out;
>
> -		cert = x509_cert_parse(sig_data->data, sig_data->size);
> -		if (IS_ERR(cert)) {
> -			EFI_PRINT("Parsing x509 certificate failed\n");
> -			goto out;
> +	EFI_PRINT("%s: searching for %s\n", __func__, cert->subject);
> +	for (siglist = db; siglist; siglist = siglist->next) {
> +		/* only with x509 certificate */
> +		if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509))
> +			continue;
> +
> +		for (sig_data = siglist->sig_data_list; sig_data;
> +		     sig_data = sig_data->next) {
> +			struct x509_certificate *cert_tmp;
> +
> +			cert_tmp = x509_cert_parse(sig_data->data,
> +						   sig_data->size);
> +			if (!cert)
> +				continue;
> +
> +			reg[0].data = cert_tmp->tbs;
> +			reg[0].size = cert_tmp->tbs_size;
> +			if (!efi_hash_regions(reg, 1, &hash_tmp, NULL))
> +				goto out;
> +
> +			x509_free_certificate(cert_tmp);
> +
> +			if (!memcmp(hash, hash_tmp, size)) {
> +				found = true;
> +				goto out;
> +			}
>  		}
> +	}
> +out:
> +	free(hash);
> +	free(hash_tmp);
>
> -		verified = efi_signature_verify(regs, msg, signed_info, cert);
> +	EFI_PRINT("%s: Exit, found: %d\n", __func__, found);
> +	return found;
> +}
>
> -		if (verified) {
> -			if (valid_cert)
> -				*valid_cert = cert;
> -			else
> -				x509_free_certificate(cert);
> -			break;
> +/**
> + * efi_verify_certificate - verify certificate's signature with database
> + * @signer:	Certificate
> + * @db:		Signature database
> + * @root:	Certificate to verify @signer
> + *
> + * Determine if certificate pointed to by @signer may be verified
> + * by one of certificates in signature database pointed to by @db.
> + *
> + * Return:	true if certificate is verified, false otherwise.
> + */
> +static bool efi_verify_certificate(struct x509_certificate *signer,
> +				   struct efi_signature_store *db,
> +				   struct x509_certificate **root)
> +{
> +	struct efi_signature_store *siglist;
> +	struct efi_sig_data *sig_data;
> +	struct x509_certificate *cert;
> +	bool verified = false;
> +	int ret;
> +
> +	EFI_PRINT("%s: Enter, %p, %p\n", __func__, signer, db);
> +
> +	if (!signer || !db || !db->sig_data_list)
> +		goto out;
> +
> +	for (siglist = db; siglist; siglist = siglist->next) {
> +		/* only with x509 certificate */
> +		if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509))
> +			continue;
> +
> +		for (sig_data = siglist->sig_data_list; sig_data;
> +		     sig_data = sig_data->next) {
> +			cert = x509_cert_parse(sig_data->data, sig_data->size);
> +			if (!cert) {
> +				EFI_PRINT("Cannot parse x509 certificate\n");
> +				continue;
> +			}
> +
> +			ret = public_key_verify_signature(cert->pub,
> +							  signer->sig);
> +			if (!ret) {
> +				verified = true;
> +				if (root)
> +					*root = cert;
> +				else
> +					x509_free_certificate(cert);
> +				goto out;
> +			}
> +			x509_free_certificate(cert);
>  		}
> -		x509_free_certificate(cert);
>  	}
>
>  out:
> @@ -423,9 +354,9 @@ bool efi_signature_verify_one(struct efi_image_regions *regs,
>  			      struct efi_signature_store *db)
>  {
>  	struct pkcs7_signed_info *sinfo;
> -	struct efi_signature_store *siglist;
> -	struct x509_certificate *cert;
> +	struct x509_certificate *signer;
>  	bool verified = false;
> +	int ret;
>
>  	EFI_PRINT("%s: Enter, %p, %p, %p\n", __func__, regs, msg, db);
>
> @@ -440,13 +371,29 @@ bool efi_signature_verify_one(struct efi_image_regions *regs,
>  		EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n",
>  			  sinfo->sig->hash_algo, sinfo->sig->pkey_algo);
>
> -		for (siglist = db; siglist; siglist = siglist->next)
> -			if (efi_signature_verify_with_list(regs, msg, sinfo,
> -							   siglist, &cert)) {
> +		EFI_PRINT("Verifying certificate chain\n");
> +		signer = NULL;
> +		ret = pkcs7_verify_one(msg, sinfo, &signer);
> +		if (ret == -ENOPKG)
> +			continue;
> +
> +		if (ret < 0 || !signer)
> +			goto out;
> +
> +		if (sinfo->blacklisted)
> +			continue;
> +
> +		EFI_PRINT("Verifying last certificate in chain\n");
> +		if (signer->self_signed) {
> +			if (efi_lookup_certificate(signer, db)) {
>  				verified = true;
>  				goto out;
>  			}
> -		EFI_PRINT("Valid certificate not in \"db\"\n");
> +		} else if (efi_verify_certificate(signer, db, NULL)) {
> +			verified = true;
> +			goto out;
> +		}
> +		EFI_PRINT("Valid certificate not in db\n");
>  	}
>
>  out:
> @@ -454,8 +401,8 @@ out:
>  	return verified;
>  }
>
> -/**
> - * efi_signature_verify_with_sigdb - verify signatures with db and dbx
> +/*
> + * efi_signature_verify - verify signatures with db and dbx
>   * @regs:	List of regions to be authenticated
>   * @msg:	Signature
>   * @db:		Signature database for trusted certificates
> @@ -466,43 +413,71 @@ out:
>   *
>   * Return:	true if verification for all signatures passed, false otherwise
>   */
> -bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs,
> -				     struct pkcs7_message *msg,
> -				     struct efi_signature_store *db,
> -				     struct efi_signature_store *dbx)
> +bool efi_signature_verify(struct efi_image_regions *regs,
> +			  struct pkcs7_message *msg,
> +			  struct efi_signature_store *db,
> +			  struct efi_signature_store *dbx)
>  {
> -	struct pkcs7_signed_info *info;
> -	struct efi_signature_store *siglist;
> -	struct x509_certificate *cert;
> +	struct pkcs7_signed_info *sinfo;
> +	struct x509_certificate *signer, *root;
>  	bool verified = false;
> +	int ret;
>
>  	EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, dbx);
>
>  	if (!regs || !msg || !db || !db->sig_data_list)
>  		goto out;
>
> -	for (info = msg->signed_infos; info; info = info->next) {
> +	for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) {
>  		EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n",
> -			  info->sig->hash_algo, info->sig->pkey_algo);
> +			  sinfo->sig->hash_algo, sinfo->sig->pkey_algo);
>
> -		for (siglist = db; siglist; siglist = siglist->next) {
> -			if (efi_signature_verify_with_list(regs, msg, info,
> -							   siglist, &cert))
> -				break;
> -		}
> -		if (!siglist) {
> -			EFI_PRINT("Valid certificate not in \"db\"\n");
> +		/*
> +		 * only for authenticated variable.
> +		 *
> +		 * If this function is called for image,
> +		 * hash calculation will be done in
> +		 * pkcs7_verify_one().
> +		 */
> +		if (!msg->data &&
> +		    !efi_hash_regions(regs->reg, regs->num,
> +				      (void **)&sinfo->sig->digest, NULL)) {
> +			EFI_PRINT("Digesting an image failed\n");
>  			goto out;
>  		}
>
> -		if (!dbx || efi_signature_check_revocation(info, cert, dbx))
> +		EFI_PRINT("Verifying certificate chain\n");
> +		signer = NULL;
> +		ret = pkcs7_verify_one(msg, sinfo, &signer);
> +		if (ret == -ENOPKG)
>  			continue;
>
> -		EFI_PRINT("Certificate in \"dbx\"\n");
> +		if (ret < 0 || !signer)
> +			goto out;
> +
> +		if (sinfo->blacklisted)
> +			goto out;
> +
> +		EFI_PRINT("Verifying last certificate in chain\n");
> +		if (signer->self_signed) {
> +			if (efi_lookup_certificate(signer, db))
> +				if (efi_signature_check_revocation(sinfo,
> +								   signer, dbx))
> +					continue;
> +		} else if (efi_verify_certificate(signer, db, &root)) {
> +			bool check;
> +
> +			check = efi_signature_check_revocation(sinfo, root,
> +							       dbx);
> +			x509_free_certificate(root);
> +			if (check)
> +				continue;
> +		}
> +
> +		EFI_PRINT("Certificate chain didn't reach trusted CA\n");
>  		goto out;
>  	}
>  	verified = true;
> -
>  out:
>  	EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified);
>  	return verified;
> diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
> index 90d740fbda2c..a2f3020c1847 100644
> --- a/lib/efi_loader/efi_variable.c
> +++ b/lib/efi_loader/efi_variable.c
> @@ -558,12 +558,11 @@ static efi_status_t efi_variable_authenticate(u16 *variable,
>  	}
>
>  	/* verify signature */
> -	if (efi_signature_verify_with_sigdb(regs, var_sig, truststore, NULL)) {
> +	if (efi_signature_verify(regs, var_sig, truststore, NULL)) {
>  		EFI_PRINT("Verified\n");
>  	} else {
>  		if (truststore2 &&
> -		    efi_signature_verify_with_sigdb(regs, var_sig,
> -						    truststore2, NULL)) {
> +		    efi_signature_verify(regs, var_sig, truststore2, NULL)) {
>  			EFI_PRINT("Verified\n");
>  		} else {
>  			EFI_PRINT("Verifying variable's signature failed\n");
>



More information about the U-Boot mailing list