[PATCH v2 8/8] tpm: allow the user to select the compiled algorithms

Simon Glass sjg at chromium.org
Sun Jun 23 23:52:14 CEST 2024


Hi Ilias,

On Sun, 23 Jun 2024 at 05:49, Ilias Apalodimas
<ilias.apalodimas at linaro.org> wrote:
>
> Simon reports that after enabling all algorithms on the TPM some boards
> fail since they don't have enough storage to accommodate the ~5KB growth.
>
> The choice of hash algorithms is determined by the platform and the TPM
> configuration. Failing to cap a PCR in a bank which the platform left
> active is a security vulnerability. It might allow  unsealing of secrets
> if an attacker can replay a good set of measurements into an unused bank.
>
> If MEASURED_BOOT or EFI_TCG2_PROTOCOL is enabled our Kconfig will enable
> all supported hashing algorithms. We still want to allow users to add a
> TPM and not enable measured boot via EFI or bootm though and at the same
> time, control the compiled algorithms for size reasons.
>
> So let's add a function tpm2_allow_extend() which checks the TPM active
> PCRs banks against the one U-Boot was compiled with. We only allow
> extending PCRs if the algorithms selected during build match the TPM
> configuration.
>
> It's worth noting that this is only added for TPM2.0, since TPM1.2 is
> lacking a lot of code at the moment to read the available PCR banks.
> We unconditionally enable SHA1 when a TPM is selected, which is the only
> hashing algorithm v1.2 supports.
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
> ---
>  boot/Kconfig     |  4 ++++
>  include/tpm-v2.h | 59 +++++++++++++++++++++++++++++++++++-------------
>  lib/Kconfig      |  6 ++---
>  lib/tpm-v2.c     | 40 +++++++++++++++++++++++++++++---
>  4 files changed, 87 insertions(+), 22 deletions(-)

Reviewed-by: Simon Glass <sjg at chromium.org>
Tested-by: Simon Glass <sjg at chromium.org>  # chromebook-link

nits / thoughts for the future below
>
> diff --git a/boot/Kconfig b/boot/Kconfig
> index 6f3096c15a6f..b061891e109c 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -734,6 +734,10 @@ config LEGACY_IMAGE_FORMAT
>  config MEASURED_BOOT
>         bool "Measure boot images and configuration when booting without EFI"
>         depends on HASH && TPM_V2
> +       select SHA1
> +       select SHA256
> +       select SHA384
> +       select SHA512
>         help
>           This option enables measurement of the boot process when booting
>           without UEFI . Measurement involves creating cryptographic hashes
> diff --git a/include/tpm-v2.h b/include/tpm-v2.h
> index aedf2c0f4f5c..4fd19c52fd70 100644
> --- a/include/tpm-v2.h
> +++ b/include/tpm-v2.h
> @@ -278,48 +278,40 @@ struct digest_info {
>  #define TCG2_BOOT_HASH_ALG_SM3_256 0x00000010
>
>  static const struct digest_info hash_algo_list[] = {
> +#if IS_ENABLED(CONFIG_SHA1)
>         {
>                 "sha1",
>                 TPM2_ALG_SHA1,
>                 TCG2_BOOT_HASH_ALG_SHA1,
>                 TPM2_SHA1_DIGEST_SIZE,
>         },
> +#endif
> +#if IS_ENABLED(CONFIG_SHA256)
>         {
>                 "sha256",
>                 TPM2_ALG_SHA256,
>                 TCG2_BOOT_HASH_ALG_SHA256,
>                 TPM2_SHA256_DIGEST_SIZE,
>         },
> +#endif
> +#if IS_ENABLED(CONFIG_SHA384)
>         {
>                 "sha384",
>                 TPM2_ALG_SHA384,
>                 TCG2_BOOT_HASH_ALG_SHA384,
>                 TPM2_SHA384_DIGEST_SIZE,
>         },
> +#endif
> +#if IS_ENABLED(CONFIG_SHA512)
>         {
>                 "sha512",

This is a duplicate of common/hash.c (we can worry about all this later)

>                 TPM2_ALG_SHA512,
>                 TCG2_BOOT_HASH_ALG_SHA512,
>                 TPM2_SHA512_DIGEST_SIZE,

so is this

>         },
> +#endif
>  };
>
> -static inline u16 tpm2_algorithm_to_len(enum tpm2_algorithms a)
> -{
> -       switch (a) {
> -       case TPM2_ALG_SHA1:
> -               return TPM2_SHA1_DIGEST_SIZE;
> -       case TPM2_ALG_SHA256:
> -               return TPM2_SHA256_DIGEST_SIZE;
> -       case TPM2_ALG_SHA384:
> -               return TPM2_SHA384_DIGEST_SIZE;
> -       case TPM2_ALG_SHA512:
> -               return TPM2_SHA512_DIGEST_SIZE;
> -       default:
> -               return 0;
> -       }
> -}
> -
>  /* NV index attributes */
>  enum tpm_index_attrs {
>         TPMA_NV_PPWRITE         = 1UL << 0,
> @@ -712,6 +704,41 @@ enum tpm2_algorithms tpm2_name_to_algorithm(const char *name);
>   */
>  const char *tpm2_algorithm_name(enum tpm2_algorithms);
>
> +/**
> + * tpm2_algorithm_to_len() - Return an algorithm length for supported algorithm id
> + *
> + * @algorithm_id: algorithm defined in enum tpm2_algorithms
> + * Return: len or 0 if not supported
> + */
> +u16 tpm2_algorithm_to_len(enum tpm2_algorithms algo);
> +
> +/*
> + * When measured boot is enabled via EFI or bootX commands all the algorithms
> + * above are selected by our Kconfigs. Due to U-Boots nature of being small there

U-Boot's

> + * are cases where we need some functionality from the TPM -- e.g storage or RNG
> + * but we don't want to support measurements.
> + *
> + * The choice of hash algorithms are determined by the platform and the TPM
> + * configuration. Failing to cap a PCR in a bank which the platform left
> + * active is a security vulnerability. It permits the unsealing of secrets
> + * if an attacker can replay a good set of measurements into an unused bank.
> + *
> + * On top of that a previous stage bootloader (e.g TF-A), migh pass an eventlog

might

> + * since it doesn't have a TPM driver, which U-Boot needs to replace. The algorit h

algorithm

> + * choice is a compile time option in that case and we need to make sure we conform.
> + *
> + * Add a variable here that sums the supported algorithms U-Boot was compiled
> + * with so we can refuse to do measurements if we don't support all of them
> + */
> +
> +/**
> + * tpm2_allow_extend() - Check if extending PCRs is allowed and safe
> + *
> + * @dev: TPM device
> + * Return: true if allowed
> + */
> +bool tpm2_allow_extend(struct udevice *dev);
> +
>  /**
>   * tpm2_is_active_pcr() - check the pcr_select. If at least one of the PCRs
>   *                       supports the algorithm add it on the active ones
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 189e6eb31aa1..b3baa4b85b07 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -439,9 +439,6 @@ config TPM
>         depends on DM
>         imply DM_RNG
>         select SHA1
> -       select SHA256
> -       select SHA384
> -       select SHA512
>         help
>           This enables support for TPMs which can be used to provide security
>           features for your board. The TPM can be connected via LPC or I2C
> @@ -449,6 +446,9 @@ config TPM
>           command to interactive the TPM. Driver model support is provided
>           for the low-level TPM interface, but only one TPM is supported at
>           a time by the TPM library.
> +         For size reasons only SHA1 is selected which is supported on TPM1.2.
> +         If you want a fully functional TPM enable all hashing algorithms.
> +         If you enabled measured boot all hashing algorithms are selected.
>
>  config SPL_TPM
>         bool "Trusted Platform Module (TPM) Support in SPL"
> diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c
> index 36aace03cf4e..59e6cbafafaa 100644
> --- a/lib/tpm-v2.c
> +++ b/lib/tpm-v2.c
> @@ -196,6 +196,11 @@ u32 tpm2_pcr_extend(struct udevice *dev, u32 index, u32 algorithm,
>
>         if (!digest)
>                 return -EINVAL;
> +
> +       if (!tpm2_allow_extend(dev)) {
> +               log_err("Cannot extend PCRs if all the TPM enabled algorithms are not supported\n");
> +               return -EINVAL;
> +       }
>         /*
>          * Fill the command structure starting from the first buffer:
>          *     - the digest
> @@ -409,11 +414,10 @@ int tpm2_get_pcr_info(struct udevice *dev, struct tpml_pcr_selection *pcrs)
>
>         pcrs->count = get_unaligned_be32(response);
>         /*
> -        * We only support 5 algorithms for now so check against that
> +        * We only support 4 algorithms for now so check against that
>          * instead of TPM2_NUM_PCR_BANKS
>          */
> -       if (pcrs->count > ARRAY_SIZE(hash_algo_list) ||
> -           pcrs->count < 1) {
> +       if (pcrs->count > 4 || pcrs->count < 1) {
>                 printf("%s: too many pcrs: %u\n", __func__, pcrs->count);
>                 return -EMSGSIZE;
>         }
> @@ -880,3 +884,33 @@ const char *tpm2_algorithm_name(enum tpm2_algorithms algo)
>         return "";
>  }
>
> +u16 tpm2_algorithm_to_len(enum tpm2_algorithms algo)

nit: return uint? There is no need to mask the register here

> +{
> +       size_t i;
> +
> +       for (i = 0; i < ARRAY_SIZE(hash_algo_list); ++i) {
> +               if (hash_algo_list[i].hash_alg == algo)
> +                       return hash_algo_list[i].hash_len;
> +       }
> +
> +       return 0;
> +}

At some point we could have a HASH_ALGOT variable in hash.h which
encodes the hash algorithm, then some of this will be common

> +
> +bool tpm2_allow_extend(struct udevice *dev)
> +{
> +       struct tpml_pcr_selection pcrs;
> +       size_t i;
> +       int rc;
> +
> +       rc = tpm2_get_pcr_info(dev, &pcrs);
> +       if (rc)
> +               return false;
> +
> +       for (i = 0; i < pcrs.count; i++) {
> +               if (tpm2_is_active_pcr(&pcrs.selection[i]) &&
> +                   !tpm2_algorithm_to_len(pcrs.selection[i].hash))
> +                       return false;
> +       }
> +
> +       return true;
> +}
> --
> 2.45.2
>

REgards,
Simon


More information about the U-Boot mailing list