[PATCH v2 8/8] tpm: allow the user to select the compiled algorithms
Ilias Apalodimas
ilias.apalodimas at linaro.org
Mon Jun 24 06:43:39 CEST 2024
On Mon, 24 Jun 2024 at 00:52, Simon Glass <sjg at chromium.org> wrote:
>
> 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
I'll fix the spelling errors before merging
> >
> > 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)
The name and length are. TPM2_ALG_SHAXXX is what the device returns
though when probed for available algorithms and since those values are
not maskable, TCG defined its masks .... TCG2_BOOT_HASH_ALG_SHA512
etc.
This is unfortunate but I can't immediately think of an elegant way to
combine those.
>
> > 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
Yes, I was toying around with the idea of creating a mask with the
supported algorithms. We could even reuse the masking the TCG defines.
There's nothing special to this function tbh, so we can move it in
common/hash.c instead as well. But I'll pick this as-is for now and
plan for a wider refactoring of all the hashing support we have. There
are a lot of places we redefine those and we shouldn't.
Thanks
/Ilias
>
> > +
> > +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