
EFI measures the secure boot state including DB etc in PCR7. When using FIT signatures we can measure the known RSA keys instead. Short of a DER encoder we can pick the ssh way of serializing RSA keys. Signed-off-by: Ludwig Nussel <ludwig.nussel@siemens.com> --- boot/Kconfig | 8 +++++ boot/bootm.c | 84 +++++++++++++++++++++++++++++++++++++++----- boot/image-fit-sig.c | 3 ++ include/spbuf.h | 28 +++++++++++++++ lib/Makefile | 2 ++ lib/spbuf.c | 64 +++++++++++++++++++++++++++++++++ 6 files changed, 180 insertions(+), 9 deletions(-) create mode 100644 include/spbuf.h create mode 100644 lib/spbuf.c diff --git a/boot/Kconfig b/boot/Kconfig index 2ff6f003738..b2f16ab6b27 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -1051,6 +1051,14 @@ if MEASURED_BOOT through system resets and are the first stage bootloader, then this option should be enabled to ignore any existing data in the event log memory region. + +if FIT_SIGNATURE + config MEASURE_TRUSTED_KEYS + bool "Measure FIT signature keys instead of kernel and initrd" + help + Measure the trusted keys used for signature verification + of fit images instead of kernel, initrd etc checksums +endif # FIT_SIGNATURE endif # MEASURED_BOOT config SYS_BOOTM_LEN diff --git a/boot/bootm.c b/boot/bootm.c index 33ff67aa53d..a99be45ffaa 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -28,6 +28,13 @@ #if defined(CONFIG_CMD_USB) #include <usb.h> #endif +#if defined(CONFIG_MEASURE_TRUSTED_KEYS) +#include <string.h> +#include <spbuf.h> +#ifdef LOG_DEBUG +#include <u-boot/md5.h> +#endif +#endif #else #include "mkimage.h" #endif @@ -908,6 +915,61 @@ int bootm_process_cmdline_env(int flags) return 0; } +static int bootm_measure_keys(struct udevice *dev, const void *key_blob) +{ + int ret = 0; +#if defined(CONFIG_MEASURE_TRUSTED_KEYS) + int noffset; + int sig_node; + + log_info("Measuring keys\n"); + + sig_node = fdt_subnode_offset(key_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found: %s\n", __func__, + fdt_strerror(sig_node)); + return 0; + } + + fdt_for_each_subnode(noffset, key_blob, sig_node) { + const char *keyname = fit_get_name(key_blob, noffset, NULL); + + log_info(" %s\n", keyname); + + u32 nlen = 0; + u32 elen = 0; + const u8 *n = fdt_getprop(key_blob, noffset, "rsa,modulus", &nlen); + const u8 *e = fdt_getprop(key_blob, noffset, "rsa,exponent", &elen); + /* ssh-keygen style digest. dt has + * properties big endian, just as we + * need it for this purpose + */ + if (!n || !e) { + log_err(" Missing modulus and/or exponent\n"); + continue; + } + u8 *p = NULL, *buf = NULL; + + p = spbuf_put_rsa_pubkey(buf, n, nlen, e, elen); + buf = calloc(p - buf, 1); + p = spbuf_put_rsa_pubkey(buf, n, nlen, e, elen); + +#ifdef LOG_DEBUG + char fpr[MD5_SUM_LEN] = {0}; + + md5_wd(buf, p - buf, fpr, CHUNKSZ_MD5); + log_info(" %*pX\n", (int)sizeof(fpr), fpr); +#endif + int r = tcg2_measure_data(dev, NULL, 7, p - buf, + buf, EV_COMPACT_HASH, + strlen(keyname) + 1, (u8 *)keyname); + if (r) + ret = 1; + } +#endif + return ret; +} + int bootm_measure(struct bootm_headers *images) { int ret = 0; @@ -919,7 +981,6 @@ int bootm_measure(struct bootm_headers *images) return ret; if (IS_ENABLED(CONFIG_MEASURED_BOOT)) { - struct tcg2_event_log elog; struct udevice *dev; void *initrd_buf; void *image_buf; @@ -927,15 +988,19 @@ int bootm_measure(struct bootm_headers *images) u32 rd_len; bool ign; - elog.log_size = 0; ign = IS_ENABLED(CONFIG_MEASURE_IGNORE_LOG); - ret = tcg2_measurement_init(&dev, &elog, ign); - if (ret) + ret = tcg2_measurement_init(&dev, NULL, ign); + if (ret && ret != -EEXIST) return ret; + if (CONFIG_IS_ENABLED(MEASURE_TRUSTED_KEYS)) { + ret = bootm_measure_keys(dev, gd_fdt_blob()); + goto measurement_done; + } + image_buf = map_sysmem(images->os.image_start, images->os.image_len); - ret = tcg2_measure_data(dev, &elog, 8, images->os.image_len, + ret = tcg2_measure_data(dev, NULL, 8, images->os.image_len, image_buf, EV_COMPACT_HASH, strlen("linux") + 1, (u8 *)"linux"); if (ret) @@ -943,14 +1008,14 @@ int bootm_measure(struct bootm_headers *images) rd_len = images->rd_end - images->rd_start; initrd_buf = map_sysmem(images->rd_start, rd_len); - ret = tcg2_measure_data(dev, &elog, 9, rd_len, initrd_buf, + ret = tcg2_measure_data(dev, NULL, 9, rd_len, initrd_buf, EV_COMPACT_HASH, strlen("initrd") + 1, (u8 *)"initrd"); if (ret) goto unmap_initrd; if (IS_ENABLED(CONFIG_MEASURE_DEVICETREE)) { - ret = tcg2_measure_data(dev, &elog, 1, images->ft_len, + ret = tcg2_measure_data(dev, NULL, 1, images->ft_len, (u8 *)images->ft_addr, EV_TABLE_OF_DEVICES, strlen("dts") + 1, @@ -962,7 +1027,7 @@ int bootm_measure(struct bootm_headers *images) s = env_get("bootargs"); if (!s) s = ""; - ret = tcg2_measure_data(dev, &elog, 1, strlen(s) + 1, (u8 *)s, + ret = tcg2_measure_data(dev, NULL, 1, strlen(s) + 1, (u8 *)s, EV_PLATFORM_CONFIG_FLAGS, strlen(s) + 1, (u8 *)s); @@ -971,7 +1036,8 @@ unmap_initrd: unmap_image: unmap_sysmem(image_buf); - tcg2_measurement_term(dev, &elog, ret != 0); +measurement_done: + tcg2_measurement_term(dev, NULL, ret != 0); } return ret; diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c index f23e9d5d0b0..550c45edbaa 100644 --- a/boot/image-fit-sig.c +++ b/boot/image-fit-sig.c @@ -10,6 +10,9 @@ #include <log.h> #include <malloc.h> #include <asm/global_data.h> +#include <linux/sizes.h> +#include <tpm-v2.h> +#include <tpm_tcg2.h> DECLARE_GLOBAL_DATA_PTR; #endif /* !USE_HOSTCC*/ #include <fdt_region.h> diff --git a/include/spbuf.h b/include/spbuf.h new file mode 100644 index 00000000000..0b58f0d5daa --- /dev/null +++ b/include/spbuf.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * very basic serialization of data using ssh protocol (rfc4251) + * style buffers. Ie numbers are stored big endian. Variable data is + * prefixed with a 32bit length field. + * + * The functions can be called with a NULL argument as buffer to + * only have the required length calculated. + * + * Copyright (c) 2025 Siemens AG + */ + +#ifndef _SPBUF_H +#define _SPBUF_H + +#include <string.h> + +#ifdef USE_HOSTCC +typedef uint8_t u8; +typedef uint32_t u32; +#endif + +u8 *spbuf_put_u32(u8 *buf, u32 num); +u8 *spbuf_put_string(u8 *buf, const char *str); +u8 *spbuf_put_bignum(u8 *buf, const u8 *bn, u32 len); +u8 *spbuf_put_rsa_pubkey(u8 *buf, const u8 *n, u32 nlen, const u8 *e, u32 elen); + +#endif diff --git a/lib/Makefile b/lib/Makefile index 2643bfc867c..1f57a6093f2 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -161,6 +161,8 @@ obj-$(CONFIG_$(PHASE_)SEMIHOSTING) += semihosting.o obj-$(CONFIG_UTHREAD) += uthread.o +obj-$(CONFIG_MEASURE_TRUSTED_KEYS) += spbuf.o + # # Build a fast OID lookup registry from include/linux/oid_registry.h # diff --git a/lib/spbuf.c b/lib/spbuf.c new file mode 100644 index 00000000000..5ff5030db83 --- /dev/null +++ b/lib/spbuf.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Siemens AG + */ + +#include <spbuf.h> + +u8 *spbuf_put_u32(u8 *buf, u32 num) +{ + if (buf) { + buf[0] = (num >> 24) & 0xff; + buf[1] = (num >> 16) & 0xff; + buf[2] = (num >> 8) & 0xff; + buf[3] = num & 0xff; + } + return buf + 4; +} + +u8 *spbuf_put_string(u8 *buf, const char *str) +{ + u32 len = strlen(str); + u8 *p = spbuf_put_u32(buf, len); + + if (buf) + memcpy(p, str, len); + return p + len; +} + +u8 *spbuf_put_bignum(u8 *buf, const u8 *bn, u32 len) +{ + u8 *p; + int prepend = 0; + /* skip leading zeroes */ + while (!*bn && len) { + ++bn; + --len; + } + if (len && *bn & 0x80) + prepend = 1; + p = spbuf_put_u32(buf, len + prepend); + if (prepend) { + if (buf) + *p = 0; + ++p; + } + if (len && buf) + memcpy(p, bn, len); + + return p + len; +} + +u8 *spbuf_put_rsa_pubkey(u8 *buf, const u8 *n, u32 nlen, const u8 *e, u32 elen) +{ + u8 *p, *len = NULL; + + p = spbuf_put_string(buf, "ssh-rsa"); + if (buf) buf = p; else len = p; + p = spbuf_put_bignum(buf, e, elen); + if (buf) buf = p; else len += (ptrdiff_t)p; + p = spbuf_put_bignum(buf, n, nlen); + if (buf) buf = p; else len += (ptrdiff_t)p; + + return buf ?: len; +} -- 2.34.1