diff --git ./src/Kconfig ./src/Kconfig index 49f8672..4ca811d 100644 --- ./src/Kconfig +++ ./src/Kconfig @@ -259,6 +259,21 @@ config BOOTSPLASH_FILE The path and filename of the file to use as graphical bootsplash screen. The file format has to be jpg. +config MEASURED_BOOT + bool "Enable TPM measured boot" + default n + select TPM + depends on MAINBOARD_HAS_LPC_TPM + depends on !VBOOT + help + Enable this option to measure the bootblock, romstage and + CBFS files into TPM PCRs. This does not verify these values + (that is the job of something like vboot), but makes it possible + for the payload to validate the boot path and allow something + like Heads to attest to the user that the system is likely safe. + + You probably want to say N. + endmenu menu "Mainboard" diff --git ./src/include/program_loading.h ./src/include/program_loading.h index 416e2e9..40486cd 100644 --- ./src/include/program_loading.h +++ ./src/include/program_loading.h @@ -24,6 +24,8 @@ enum { /* Last segment of program. Can be used to take different actions for * cache maintenance of a program load. */ SEG_FINAL = 1 << 0, + /* Indicate that the program segment should not be measured */ + SEG_NO_MEASURE = 1 << 1, }; enum prog_type { diff --git ./src/include/sha1.h ./src/include/sha1.h new file mode 100644 index 0000000..e7e28e6 --- /dev/null +++ ./src/include/sha1.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* SHA-1 functions */ + +#ifndef _sha1_h_ +#define _sha1_h_ + +#include +#include + +#define SHA1_DIGEST_SIZE 20 +#define SHA1_BLOCK_SIZE 64 + +/* SHA-1 context */ +struct sha1_ctx { + uint32_t count; + uint32_t state[5]; + union { + uint8_t b[SHA1_BLOCK_SIZE]; + uint32_t w[DIV_ROUND_UP(SHA1_BLOCK_SIZE, sizeof(uint32_t))]; + } buf; +}; + +void sha1_init(struct sha1_ctx *ctx); +void sha1_update(struct sha1_ctx *ctx, const uint8_t *data, uint32_t len); +uint8_t *sha1_final(struct sha1_ctx *ctx); + +#endif /* _sha1_h_ */ diff --git ./src/include/tpm_lite/tlcl.h ./src/include/tpm_lite/tlcl.h index 8dd5d80..15fbebf 100644 --- ./src/include/tpm_lite/tlcl.h +++ ./src/include/tpm_lite/tlcl.h @@ -147,6 +147,11 @@ uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest, uint8_t *out_digest); /** + * Perform a SHA1 hash on a region and extend a PCR with the hash. + */ +uint32_t tlcl_measure(int pcr_num, const void * start, size_t len); + +/** * Get the entire set of permanent flags. */ uint32_t tlcl_get_permanent_flags(TPM_PERMANENT_FLAGS *pflags); diff --git ./src/lib/Makefile.inc ./src/lib/Makefile.inc index 8f92b29..1822daf 100644 --- ./src/lib/Makefile.inc +++ ./src/lib/Makefile.inc @@ -54,8 +54,13 @@ verstage-$(CONFIG_TPM) += tlcl.c verstage-$(CONFIG_TPM2) += tpm2_marshaling.c verstage-$(CONFIG_TPM2) += tpm2_tlcl.c -ifeq ($(CONFIG_VBOOT_SEPARATE_VERSTAGE),y) +# Add the TPM support into the ROM stage for measuring the bootblock romstage-$(CONFIG_TPM) += tlcl.c +romstage-$(CONFIG_TPM) += sha1.c +ramstage-$(CONFIG_TPM) += tlcl.c +ramstage-$(CONFIG_TPM) += sha1.c + +ifeq ($(CONFIG_VBOOT_SEPARATE_VERSTAGE),y) romstage-$(CONFIG_TPM2) += tpm2_marshaling.c romstage-$(CONFIG_TPM2) += tpm2_tlcl.c endif # CONFIG_VBOOT_SEPARATE_VERSTAGE diff --git ./src/lib/cbfs.c ./src/lib/cbfs.c index 11bce2c..8428245 100644 --- ./src/lib/cbfs.c +++ ./src/lib/cbfs.c @@ -69,7 +69,11 @@ void *cbfs_boot_map_with_leak(const char *name, uint32_t type, size_t *size) if (size != NULL) *size = fsize; - return rdev_mmap(&fh.data, 0, fsize); + void * buffer = rdev_mmap(&fh.data, 0, fsize); + + prog_segment_loaded((uintptr_t)buffer, fsize, 0); + + return buffer; } int cbfs_locate_file_in_region(struct cbfsf *fh, const char *region_name, @@ -97,7 +101,8 @@ size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset, return 0; if (rdev_readat(rdev, buffer, offset, in_size) != in_size) return 0; - return in_size; + out_size = in_size; + break; case CBFS_COMPRESS_LZ4: if ((ENV_BOOTBLOCK || ENV_VERSTAGE) && @@ -115,7 +120,7 @@ size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset, timestamp_add_now(TS_START_ULZ4F); out_size = ulz4fn(compr_start, in_size, buffer, buffer_size); timestamp_add_now(TS_END_ULZ4F); - return out_size; + break; case CBFS_COMPRESS_LZMA: if (ENV_BOOTBLOCK || ENV_VERSTAGE) @@ -134,11 +139,15 @@ size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset, rdev_munmap(rdev, map); - return out_size; + break; default: return 0; } + + prog_segment_loaded((uintptr_t)buffer, out_size, 0); + + return out_size; } static inline int tohex4(unsigned int c) diff --git ./src/lib/hardwaremain.c ./src/lib/hardwaremain.c index a56d68e..8c13f5f 100644 --- ./src/lib/hardwaremain.c +++ ./src/lib/hardwaremain.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #if CONFIG_HAVE_ACPI_RESUME #include @@ -544,3 +545,13 @@ void boot_state_current_unblock(void) { boot_state_unblock(current_phase.state_id, current_phase.seq); } + +// ramstage measurements go into PCR3 if we are doing measured boot +void platform_segment_loaded(uintptr_t start, size_t size, int flags) +{ + if (IS_ENABLED(CONFIG_MEASURED_BOOT) && !(flags & SEG_NO_MEASURE)) + { + tlcl_measure(3, (const void*) start, size); + } +} + diff --git ./src/lib/rmodule.c ./src/lib/rmodule.c index a3a74ac..bb99e5f 100644 --- ./src/lib/rmodule.c +++ ./src/lib/rmodule.c @@ -198,7 +198,7 @@ int rmodule_load(void *base, struct rmodule *module) rmodule_clear_bss(module); prog_segment_loaded((uintptr_t)module->location, - rmodule_memory_size(module), SEG_FINAL); + rmodule_memory_size(module), SEG_FINAL | SEG_NO_MEASURE); return 0; } diff --git ./src/lib/sha1.c ./src/lib/sha1.c new file mode 100644 index 0000000..506907f --- /dev/null +++ ./src/lib/sha1.c @@ -0,0 +1,175 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * SHA-1 implementation largely based on libmincrypt in the the Android + * Open Source Project (platorm/system/core.git/libmincrypt/sha.c + */ + +#include "sha1.h" +#include + +static uint32_t ror27(uint32_t val) +{ + return (val >> 27) | (val << 5); +} +static uint32_t ror2(uint32_t val) +{ + return (val >> 2) | (val << 30); +} +static uint32_t ror31(uint32_t val) +{ + return (val >> 31) | (val << 1); +} + +static void sha1_transform(struct sha1_ctx *ctx) +{ + uint32_t W[80]; + register uint32_t A, B, C, D, E; + int t; + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define SHA_F1(A, B, C, D, E, t) \ + E += ror27(A) + \ + (W[t] = __builtin_bswap32(ctx->buf.w[t])) + \ + (D^(B&(C^D))) + 0x5A827999; \ + B = ror2(B); + + for (t = 0; t < 15; t += 5) { + SHA_F1(A, B, C, D, E, t + 0); + SHA_F1(E, A, B, C, D, t + 1); + SHA_F1(D, E, A, B, C, t + 2); + SHA_F1(C, D, E, A, B, t + 3); + SHA_F1(B, C, D, E, A, t + 4); + } + SHA_F1(A, B, C, D, E, t + 0); /* 16th one, t == 15 */ + +#undef SHA_F1 + +#define SHA_F1(A, B, C, D, E, t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (D^(B&(C^D))) + 0x5A827999; \ + B = ror2(B); + + SHA_F1(E, A, B, C, D, t + 1); + SHA_F1(D, E, A, B, C, t + 2); + SHA_F1(C, D, E, A, B, t + 3); + SHA_F1(B, C, D, E, A, t + 4); + +#undef SHA_F1 + +#define SHA_F2(A, B, C, D, E, t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (B^C^D) + 0x6ED9EBA1; \ + B = ror2(B); + + for (t = 20; t < 40; t += 5) { + SHA_F2(A, B, C, D, E, t + 0); + SHA_F2(E, A, B, C, D, t + 1); + SHA_F2(D, E, A, B, C, t + 2); + SHA_F2(C, D, E, A, B, t + 3); + SHA_F2(B, C, D, E, A, t + 4); + } + +#undef SHA_F2 + +#define SHA_F3(A, B, C, D, E, t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + ((B&C)|(D&(B|C))) + 0x8F1BBCDC; \ + B = ror2(B); + + for (; t < 60; t += 5) { + SHA_F3(A, B, C, D, E, t + 0); + SHA_F3(E, A, B, C, D, t + 1); + SHA_F3(D, E, A, B, C, t + 2); + SHA_F3(C, D, E, A, B, t + 3); + SHA_F3(B, C, D, E, A, t + 4); + } + +#undef SHA_F3 + +#define SHA_F4(A, B, C, D, E, t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (B^C^D) + 0xCA62C1D6; \ + B = ror2(B); + + for (; t < 80; t += 5) { + SHA_F4(A, B, C, D, E, t + 0); + SHA_F4(E, A, B, C, D, t + 1); + SHA_F4(D, E, A, B, C, t + 2); + SHA_F4(C, D, E, A, B, t + 3); + SHA_F4(B, C, D, E, A, t + 4); + } + +#undef SHA_F4 + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +void sha1_update(struct sha1_ctx *ctx, const uint8_t *data, uint32_t len) +{ + int i = ctx->count % sizeof(ctx->buf); + const uint8_t *p = (const uint8_t *)data; + + ctx->count += len; + + while (len > sizeof(ctx->buf) - i) { + memcpy(&ctx->buf.b[i], p, sizeof(ctx->buf) - i); + len -= sizeof(ctx->buf) - i; + p += sizeof(ctx->buf) - i; + sha1_transform(ctx); + i = 0; + } + + while (len--) { + ctx->buf.b[i++] = *p++; + if (i == sizeof(ctx->buf)) { + sha1_transform(ctx); + i = 0; + } + } +} + + +uint8_t *sha1_final(struct sha1_ctx *ctx) +{ + uint32_t cnt = ctx->count * 8; + int i; + + sha1_update(ctx, (uint8_t *)"\x80", 1); + while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) + sha1_update(ctx, (uint8_t *)"\0", 1); + + for (i = 0; i < 8; ++i) { + uint8_t tmp = cnt >> ((7 - i) * 8); + sha1_update(ctx, &tmp, 1); + } + + for (i = 0; i < 5; i++) + ctx->buf.w[i] = __builtin_bswap32(ctx->state[i]); + + return ctx->buf.b; +} + +void sha1_init(struct sha1_ctx *ctx) +{ + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; + ctx->count = 0; +} diff --git ./src/lib/tlcl.c ./src/lib/tlcl.c index 49854cb..32eb128 100644 --- ./src/lib/tlcl.c +++ ./src/lib/tlcl.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "tlcl_internal.h" #include "tlcl_structures.h" @@ -351,3 +352,23 @@ uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest, kPcrDigestLength); return result; } + + +uint32_t tlcl_measure(int pcr_num, const void * start, size_t len) +{ + VBDEBUG("TPM: pcr %d measure %p @ %zu: ", pcr_num, start, len); + + struct sha1_ctx sha; + sha1_init(&sha); + sha1_update(&sha, start, len); + + const uint8_t * hash = sha1_final(&sha); + for(unsigned i = 0 ; i < SHA1_DIGEST_SIZE ; i++) + VBDEBUG("%02x", hash[i]); + VBDEBUG("\n"); + + //hexdump(start, 128); + + return tlcl_extend(pcr_num, hash, NULL); +} + diff --git ./src/northbridge/intel/sandybridge/romstage.c ./src/northbridge/intel/sandybridge/romstage.c index 738e285..c7c0c62 100644 --- ./src/northbridge/intel/sandybridge/romstage.c +++ ./src/northbridge/intel/sandybridge/romstage.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include "southbridge/intel/bd82x6x/pch.h" #include @@ -72,6 +74,18 @@ void mainboard_romstage_entry(unsigned long bist) /* Initialize superio */ mainboard_config_superio(); + if (IS_ENABLED(CONFIG_MEASURED_BOOT) && IS_ENABLED(CONFIG_LPC_TPM)) { + // we don't know if we are coming out of a resume + // at this point, but want to setup the tpm ASAP + init_tpm(0); + const void * const bootblock = (const void*) 0xFFFFF800; + const unsigned bootblock_size = 0x800; + tlcl_measure(0, bootblock, bootblock_size); + + extern char _romstage, _eromstage; + tlcl_measure(1, &_romstage, &_eromstage - &_romstage); + } + /* USB is initialized in MRC if MRC is used. */ if (CONFIG_USE_NATIVE_RAMINIT) { early_usb_init(mainboard_usb_ports); @@ -116,9 +130,23 @@ void mainboard_romstage_entry(unsigned long bist) northbridge_romstage_finalize(s3resume); - if (IS_ENABLED(CONFIG_LPC_TPM)) { + // the normal TPM init happens here, if we haven't already + // set it up as part of the measured boot. + if (!IS_ENABLED(CONFIG_MEASURED_BOOT) && IS_ENABLED(CONFIG_LPC_TPM)) { init_tpm(s3resume); } + printk(BIOS_DEBUG, "%s: romstage complete\n", __FILE__); + post_code(0x3f); } + + +void platform_segment_loaded(uintptr_t start, size_t size, int flags) +{ + if (IS_ENABLED(CONFIG_MEASURED_BOOT) && !(flags & SEG_NO_MEASURE)) + { + tlcl_measure(2, (const void*) start, size); + } +} + diff --git ./util/cbfstool/lz4/lib/lz4frame.c ./util/cbfstool/lz4/lib/lz4frame.c index e5458bb..58fb68f 100644 --- ./util/cbfstool/lz4/lib/lz4frame.c +++ ./util/cbfstool/lz4/lib/lz4frame.c @@ -1091,7 +1091,7 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext, dctxPtr->tmpInTarget = minFHSize; /* minimum to attempt decode */ dctxPtr->dStage = dstage_storeHeader; } - + /* Falls through. */ case dstage_storeHeader: { size_t sizeToCopy = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;