Hello Krystian Hebel,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/coreboot/+/43403
to review the following change.
Change subject: util/cbfstool/ibpmtool: Add a new tool to assemble Boot Guard manifests ......................................................................
util/cbfstool/ibpmtool: Add a new tool to assemble Boot Guard manifests
OpenSSL functions tested on coreboot-sdk 1.52. The tool can create Boot Guard Manifests during build time. Takes the FIT entries as input to create IBB hash and IBB segments in the manifests. Later the entries are removed when manifests are ready.
Signed-off-by: Michał Żygowski michal.zygowski@3mdeb.com Signed-off-by: Krystian Hebel krystian.hebel@3mdeb.com Change-Id: Iddb50760b2ecdf06a58e371836fa440f45015e0f --- M .gitignore M Makefile.inc M util/cbfstool/Makefile M util/cbfstool/Makefile.inc A util/cbfstool/bpm.c A util/cbfstool/bpm.h M util/cbfstool/fit.c M util/cbfstool/fit.h M util/cbfstool/ifittool.c A util/cbfstool/libcrypto-compat.c A util/cbfstool/libcrypto-compat.h 11 files changed, 1,508 insertions(+), 78 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/03/43403/1
diff --git a/.gitignore b/.gitignore index ed66776..48d06d7 100644 --- a/.gitignore +++ b/.gitignore @@ -95,6 +95,8 @@ util/cbfstool/fmaptool util/cbfstool/ifwitool util/cbfstool/rmodtool +util/cbfstool/ifittool +util/cbfstool/ibpmtool util/cbmem/.dependencies util/cbmem/cbmem util/dumpmmcr/dumpmmcr diff --git a/Makefile.inc b/Makefile.inc index 912dd42..0701ddc 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -531,6 +531,7 @@ RMODTOOL:=$(objutil)/cbfstool/rmodtool IFWITOOL:=$(objutil)/cbfstool/ifwitool IFITTOOL:=$(objutil)/cbfstool/ifittool +IBPMTOOL:=$(objutil)/cbfstool/ibpmtool AMDCOMPRESS:=$(objutil)/cbfstool/amdcompress
$(obj)/cbfstool: $(CBFSTOOL) @@ -548,6 +549,9 @@ $(obj)/ifittool: $(IFITTOOL) cp $< $@
+$(obj)/ibpmtool: $(IBPMTOOL) + cp $< $@ + $(obj)/amdcompress: $(AMDCOMPRESS) cp $< $@
@@ -1109,7 +1113,7 @@ RAMSTAGE= endif
-$(obj)/coreboot.rom: $(obj)/coreboot.pre $(RAMSTAGE) $(CBFSTOOL) $$(INTERMEDIATE) +$(obj)/coreboot.rom: $(obj)/coreboot.pre $(RAMSTAGE) $(CBFSTOOL) $(IBPMTOOL) $$(INTERMEDIATE)
@printf " CBFS $(subst $(obj)/,,$(@))\n" # The full ROM may be larger than the CBFS part, so create an empty diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile index ad8f9a8..704e3e2 100644 --- a/util/cbfstool/Makefile +++ b/util/cbfstool/Makefile @@ -13,7 +13,7 @@ VBOOT_HOST_BUILD ?= $(abspath $(objutil)/vboot_lib)
.PHONY: all -all: cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool +all: cbfstool ifittool fmaptool rmodtool ifwitool ibpmtool cbfs-compression-tool
cbfstool: $(objutil)/cbfstool/cbfstool
@@ -25,9 +25,11 @@
ifittool: $(objutil)/cbfstool/ifittool
+ibpmtool: $(objutil)/cbfstool/ibpmtool + cbfs-compression-tool: $(objutil)/cbfstool/cbfs-compression-tool
-.PHONY: clean cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool +.PHONY: clean cbfstool ifittool fmaptool rmodtool ifwitool ibpmtool cbfs-compression-tool clean: $(RM) fmd_parser.c fmd_parser.h fmd_scanner.c fmd_scanner.h $(RM) $(objutil)/cbfstool/cbfstool $(cbfsobj) @@ -35,6 +37,7 @@ $(RM) $(objutil)/cbfstool/rmodtool $(rmodobj) $(RM) $(objutil)/cbfstool/ifwitool $(ifwiobj) $(RM) $(objutil)/cbfstool/ifittool $(ifitobj) + $(RM) $(objutil)/cbfstool/ibpmtool $(ibpmobj) $(RM) $(objutil)/cbfstool/cbfs-compression-tool $(cbfscompobj) $(RM) -r $(VBOOT_HOST_BUILD)
@@ -56,6 +59,7 @@ $(INSTALL) rmodtool $(DESTDIR)$(BINDIR) $(INSTALL) ifwitool $(DESTDIR)$(BINDIR) $(INSTALL) ifittool $(DESTDIR)$(BINDIR) + $(INSTALL) ibpmtool $(DESTDIR)$(BINDIR) $(INSTALL) cbfs-compression-tool $(DESTDIR)$(BINDIR)
ifneq ($(V),1) diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc index bc2ff48..d2ca906 100644 --- a/util/cbfstool/Makefile.inc +++ b/util/cbfstool/Makefile.inc @@ -83,6 +83,22 @@ # compression algorithms ifitobj += $(compressionobj)
+ibpmobj := +ibpmobj += bpm.o +ibpmobj += common.o +ibpmobj += fit.o +ibpmobj += xdr.o +ibpmobj += cbfs_image.o +ibpmobj += cbfs-mkpayload.o +ibpmobj += elfheaders.o +ibpmobj += rmodule.o +ibpmobj += $(compressionobj) +ibpmobj += partitioned_file.o +ibpmobj += fmap.o +ibpmobj += kv_pair.o +ibpmobj += valstr.o +ibpmobj += libcrypto-compat.o +
cbfscompobj := cbfscompobj += $(compressionobj) @@ -189,6 +205,11 @@ printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(ifitobj)) $(VBOOT_HOSTLIB)
+# -lssl and -lcrypto must be at the end of command line, not in $(TOOLLDFLAGS) +$(objutil)/cbfstool/ibpmtool: $(addprefix $(objutil)/cbfstool/,$(ibpmobj)) + printf " HOSTCC $(subst $(objutil)/,,$(@)) (link) $(TOOLLDFLAGS)\n" + $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(ibpmobj)) $(VBOOT_HOSTLIB) -lcrypto -lssl + $(objutil)/cbfstool/cbfs-compression-tool: $(addprefix $(objutil)/cbfstool/,$(cbfscompobj)) printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(cbfscompobj)) diff --git a/util/cbfstool/bpm.c b/util/cbfstool/bpm.c new file mode 100644 index 0000000..0c906c4 --- /dev/null +++ b/util/cbfstool/bpm.c @@ -0,0 +1,812 @@ +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> + +#include <openssl/pem.h> +#include "libcrypto-compat.h" + +#include <vb2_sha.h> + +#include "bpm.h" +#include "fit.h" + +static struct vb2_digest_context ctx; +partitioned_file_t *image_file; + +/* + * Boot Guard uses versions as defined below - most of them uses 0x10, only + * hdr_struct_version is 0x01. There are also version fields reserved for + * Platform Manufacturer, those can be changed with no impact on BtG operation. + * + * CBnT uses 0x21 for bpm.header.struct_version and km.struct_version, + * 0x20 for all other fields versions, including bpm.header.hdr_struct_version. + * + * The layouts of structures with different versions are not compatible, except + * for IDs and version fields. + */ +#define BPM_STRUCT_VERSION 0x10 + +static struct bpm { + bpm_header_t header; + ibb_element_t ibb_element; + //pm_s_t pm_s; // Platform Manufacturer structure, optional + bpms_t signature; +} __packed bpm = { + // Commented lines are minimal set of fields that must be filled by the code + .header.structure_id = "__ACBP__", + .header.struct_version = BPM_STRUCT_VERSION, + .header.hdr_struct_version = 1, + .header.acmsvn_auth = 2, + .header.nem_data_stack = 0x100, + + .ibb_element.structure_id = "__IBBS__", + .ibb_element.struct_version = BPM_STRUCT_VERSION, + .ibb_element.pbet_value = 5, + .ibb_element.post_ibb_hash.hash_alg = ALG_NULL, + /* + * Value in the following field is the entry point in case of ACM success. + * It doesn't have to be 0xfffffff0, but it must be in the last 64 bytes of + * 32b address space. On ACM failure, 0xfffffff0 is taken. + * Set to 0xfffffff0 as coreboot doesn't support other values. + */ + .ibb_element.ibb_entry_point = 0xfffffff0, + + .ibb_element.digest.hash_alg = ALG_SHA256, + .ibb_element.digest.size = SHA256_DIGEST_SIZE, + // .ibb_element.digest.buf = ... + + // .ibb_element.ibb_seg_count = ... + // .ibb_element.ibb_seg = ... + + .signature.structure_id = "__PMSG__", + .signature.struct_version = BPM_STRUCT_VERSION, + + .signature.key_signature.version = BPM_STRUCT_VERSION, + .signature.key_signature.key_alg = ALG_RSA, + .signature.key_signature.key.version = BPM_STRUCT_VERSION, + .signature.key_signature.key.key_size = BPM_KEY_SIZE_BITS, + .signature.key_signature.key.exponent = 0x10001, + // .signature.key_signature.key.modulus = ... + + .signature.key_signature.sig_scheme = ALG_RSASSA, + .signature.key_signature.sig_structure.version = BPM_STRUCT_VERSION, + .signature.key_signature.sig_structure.key_size = BPM_KEY_SIZE_BITS, + .signature.key_signature.sig_structure.hash_alg = ALG_SHA256, + // .signature.key_signature.sig_structure.rsa_sig = ... +}; + +static struct km { + char structure_id[8]; // "__KEYM__" + uint8_t struct_version; + uint8_t km_version; + uint8_t kmsvn; + uint8_t km_id; + hash_s bp_key; + key_sig_s signature; +} __packed km = { + .structure_id = "__KEYM__", + .struct_version = BPM_STRUCT_VERSION, + // .km_id = ... + + .bp_key.hash_alg = ALG_SHA256, + .bp_key.size = SHA256_DIGEST_SIZE, + // .bp_key.buf = ... + + .signature.version = BPM_STRUCT_VERSION, + .signature.key_alg = ALG_RSA, + .signature.key.version = BPM_STRUCT_VERSION, + .signature.key.key_size = BPM_KEY_SIZE_BITS, + .signature.key.exponent = 0x10001, + // .signature.key.modulus = ... + + .signature.sig_scheme = ALG_RSASSA, + .signature.sig_structure.version = BPM_STRUCT_VERSION, + .signature.sig_structure.key_size = BPM_KEY_SIZE_BITS, + .signature.sig_structure.hash_alg = ALG_SHA256, + // .signature.sig_structure.rsa_sig = ... +}; + +static struct { + const char *file; + const char *region; + int fit_entries; + const char *km_file; + const char *km_key; + int km_id; + const char *bpm_file; + const char *bpm_key; + int validate_hash; + int dump_manifests; + int dump_ibb; +} params = { + .region = "COREBOOT", + .km_file = "km.bin", + .bpm_file = "bpm.bin" +}; + +static void add_ibb_segment(uint32_t base, uint32_t size, int hashed) +{ + if (bpm.ibb_element.ibb_seg_count >= 8) { + ERROR("Can't add IBB segment, too many segments!\n"); + return; + } + + bpm.ibb_element.segment[bpm.ibb_element.ibb_seg_count].base = base; + bpm.ibb_element.segment[bpm.ibb_element.ibb_seg_count].size = size; + bpm.ibb_element.segment[bpm.ibb_element.ibb_seg_count].flags = !hashed; + + bpm.ibb_element.ibb_seg_count++; +} + +static void sort_and_hash_segments(struct buffer *buf) +{ + int swapped; + ibb_seg tmp; + ibb_seg *tab = bpm.ibb_element.segment; + + // Bubble sort entries + for (int j = 0; j < bpm.ibb_element.ibb_seg_count - 1; j++) { + swapped = 0; + for (int i = 0; i < bpm.ibb_element.ibb_seg_count - j - 1; i++) { + if (tab[i].base < tab[i + 1].base) + continue; + memcpy(&tmp, &tab[i], sizeof(tmp)); + memcpy(&tab[i], &tab[i + 1], sizeof(tab[i])); + memcpy(&tab[i + 1], &tmp, sizeof(tab[i + 1])); + swapped = 1; + } + if (!swapped) + break; + } + + // Hash sorted entries in order + vb2_digest_init(&ctx, VB2_HASH_SHA256); + + if (params.dump_ibb) + LOG("\nIBB segments\tBase\t\tSize\n"); + for (int i = 0; i < bpm.ibb_element.ibb_seg_count; i++) { + if (params.dump_ibb) + LOG("\t\t0x%08x\t0x%08x\n", tab[i].base, tab[i].size); + char *data = buf->data + (tab[i].base - (0x100000000 - buf->size)); + vb2_digest_extend(&ctx, (unsigned char *)data, tab[i].size); + } + + vb2_digest_finalize(&ctx, bpm.ibb_element.digest.buf, SHA256_DIGEST_SIZE); +} + +static void fill_ibb_element(struct buffer *image_region, int max_entries) +{ + struct fit_table *fit = fit_get_table(image_region, + convert_to_from_top_aligned, + 0); + if (!fit) + ERROR("FIT not found\n"); + + /* + * There is no requirement in the documentation that FIT must be + * 64B aligned. However in order to cut out FIT from bootblock, it is + * aligned to 64B when Boot Guard is enabled. IBB segments can be only + * multiple of 64B in size. + */ + uint32_t fit_base = (char*)fit - image_region->data + 0x100000000 - image_region->size; + DEBUG("FIT base 0x%x\n", fit_base); + if (fit_base & 0x3f) + ERROR("FIT is not 64B-aligned\n"); + + /* + * There is explicit alignment in fit.S, so there can be no IBB code anyway. + * It will however result in different region sizes than obtained with Intel + * tool. This in turn changes SHA256 of IBB, even if all of the files that + * are parts of IBB are the same. + * + * Header is not counted in CPU_INTEL_NUM_FIT_ENTRIES, hence max_entries+1. + */ + uint32_t fit_limit = align_up(fit_base + (max_entries + 1) * 16, 64); + DEBUG("FIT limit (aligned) 0x%x\n", fit_limit); + + for (unsigned i = 1; i < fit->header.size_reserved; i++) { + if ((fit->entries[i].type_checksum_valid & 0x7f) != FIT_TYPE_BIOS_STARTUP) + continue; + + uint64_t start = fit->entries[i].address; + uint64_t size = fit->entries[i].size_reserved * 16; + + fit_delete_entry(fit, i--); + + // No overlap, just add entry and continue + if ((start >= fit_limit) || (start + size <= fit_base)) { + add_ibb_segment(start, size, 1); + continue; + } + + // Entry begins below FIT, add lower part + if (start < fit_base) { + add_ibb_segment(start, fit_base - start, 1); + } + + // Entry ends above FIT+alignment, add upper part + if (start + size > fit_limit) { + add_ibb_segment(fit_limit, start + size - fit_limit, 1); + } + } + + sort_and_hash_segments(image_region); +} + +/* + * Returns new address of Boot Policy Manifest Signature Element. + */ +static bpms_t *fill_gaps(void) +{ + char *dst = (char*)&bpm.ibb_element.segment[bpm.ibb_element.ibb_seg_count]; + char *src = (char*)&bpm.ibb_element.segment[8]; + int size = (char*)(&bpm + 1) - src; + + memmove(dst, src, size); + + // TODO: if Platform Manufacturer struct is used it also has to be shrunk + + return (bpms_t*)dst; +} + + +static void hexdump_ident(uint8_t *p, int size, int ident) +{ + for (int i = 0; i < size; i++) { + if ((i%16) == 0) LOG("\n%*c", ident, ' '); + LOG("%.2X ", p[i]); + } + LOG("\n"); +} + +static void dump_bpm(void) +{ + // Assumes no pm_s in BPM + bpms_t *sig = (bpms_t*)&bpm.ibb_element.segment[bpm.ibb_element.ibb_seg_count]; + + LOG("BPM fields from '%s':\n", params.bpm_file); + LOG("Header:\n"); + LOG(" structure_id: %8.8s\n", bpm.header.structure_id); + LOG(" struct_version: 0x%x\n", bpm.header.struct_version); + LOG(" hdr_struct_version: 0x%x\n", bpm.header.hdr_struct_version); + LOG(" pmbpm_version: 0x%x\n", bpm.header.pmbpm_version); + LOG(" bpsvn: 0x%x\n", bpm.header.bpsvn); + LOG(" acmsvn_auth: 0x%x\n", bpm.header.acmsvn_auth); + LOG(" nem_data_stack: 0x%x\n", bpm.header.nem_data_stack); + + LOG("\nIBB element:\n"); + LOG(" structure_id: %8.8s\n", bpm.ibb_element.structure_id); + LOG(" struct_version: 0x%x\n", bpm.ibb_element.struct_version); + LOG(" pbet_value: 0x%x\n", bpm.ibb_element.pbet_value); + LOG(" flags: 0x%x\n", bpm.ibb_element.flags); + LOG(" ibb_mchbar: 0x%lx\n", bpm.ibb_element.ibb_mchbar); + LOG(" vtd_bar: 0x%lx\n", bpm.ibb_element.vtd_bar); + LOG(" pmrl_base: 0x%x\n", bpm.ibb_element.pmrl_base); + LOG(" pmrl_limit: 0x%x\n", bpm.ibb_element.pmrl_limit); + LOG(" ibb_entry_point: 0x%x\n", bpm.ibb_element.ibb_entry_point); + LOG(" ibb_seg_count: 0x%x\n", bpm.ibb_element.ibb_seg_count); + + LOG(" Post IBB hash:\n"); + LOG(" hash_alg: 0x%x\n", bpm.ibb_element.post_ibb_hash.hash_alg); + LOG(" size: 0x%x\n", bpm.ibb_element.post_ibb_hash.size); + LOG(" buf:"); + hexdump_ident(bpm.ibb_element.post_ibb_hash.buf, SHA256_DIGEST_SIZE, 25); + + LOG(" Digest:\n"); + LOG(" hash_alg: 0x%x\n", bpm.ibb_element.digest.hash_alg); + LOG(" size: 0x%x\n", bpm.ibb_element.digest.size); + LOG(" buf:"); + hexdump_ident(bpm.ibb_element.digest.buf, SHA256_DIGEST_SIZE, 25); + + for (int i = 0; i < bpm.ibb_element.ibb_seg_count; i++) { + LOG(" IBB segment %d:\n", i); + LOG(" flags: 0x%x\n", bpm.ibb_element.segment[i].flags); + LOG(" base: 0x%x\n", bpm.ibb_element.segment[i].base); + LOG(" size: 0x%x\n", bpm.ibb_element.segment[i].size); + } + + LOG("\nSignature:\n"); + LOG(" structure_id: %8.8s\n", sig->structure_id); + LOG(" struct_version: 0x%x\n", sig->struct_version); + + LOG(" Key signature:\n"); + LOG(" version: 0x%x\n", sig->key_signature.version); + LOG(" key_alg: 0x%x\n", sig->key_signature.key_alg); + LOG(" Key:\n"); + LOG(" version: 0x%x\n", sig->key_signature.key.version); + LOG(" key_size: 0x%x\n", sig->key_signature.key.key_size); + LOG(" exponent: 0x%x\n", sig->key_signature.key.exponent); + LOG(" modulus:"); + hexdump_ident(sig->key_signature.key.modulus, BPM_KEY_SIZE_BYTES, 25); + LOG(" Signature:\n"); + LOG(" sig_scheme: 0x%x\n", sig->key_signature.sig_scheme); + LOG(" version: 0x%x\n", sig->key_signature.sig_structure.version); + LOG(" key_size: 0x%x\n", sig->key_signature.sig_structure.key_size); + LOG(" hash_alg: 0x%x\n", sig->key_signature.sig_structure.hash_alg); + LOG(" signature:"); + hexdump_ident(sig->key_signature.sig_structure.rsa_sig, BPM_KEY_SIZE_BYTES, 25); +} + +static void dump_km(void) +{ + uint8_t dig[SHA256_DIGEST_SIZE]; + + LOG("KM fields from '%s':\n", params.km_file); + LOG("Header:\n"); + LOG(" structure_id: %8.8s\n", km.structure_id); + LOG(" struct_version: 0x%x\n", km.struct_version); + LOG(" kmsvn: 0x%x\n", km.kmsvn); + LOG(" km_id: 0x%x\n", km.km_id); + + LOG(" Boot Policy key:\n"); + LOG(" hash_alg: 0x%x\n", km.bp_key.hash_alg); + LOG(" size: 0x%x\n", km.bp_key.size); + LOG(" buf:"); + hexdump_ident(km.bp_key.buf, SHA256_DIGEST_SIZE, 25); + + LOG(" Key signature:\n"); + LOG(" version: 0x%x\n", km.signature.version); + LOG(" key_alg: 0x%x\n", km.signature.key_alg); + LOG(" Key:\n"); + LOG(" version: 0x%x\n", km.signature.key.version); + LOG(" key_size: 0x%x\n", km.signature.key.key_size); + LOG(" exponent: 0x%x\n", km.signature.key.exponent); + LOG(" modulus:"); + hexdump_ident(km.signature.key.modulus, BPM_KEY_SIZE_BYTES, 25); + LOG(" Signature:\n"); + LOG(" sig_scheme: 0x%x\n", km.signature.sig_scheme); + LOG(" version: 0x%x\n", km.signature.sig_structure.version); + LOG(" key_size: 0x%x\n", km.signature.sig_structure.key_size); + LOG(" hash_alg: 0x%x\n", km.signature.sig_structure.hash_alg); + LOG(" signature:"); + hexdump_ident(km.signature.sig_structure.rsa_sig, BPM_KEY_SIZE_BYTES, 25); + + vb2_digest_buffer(km.signature.key.modulus, + sizeof(km.signature.key.modulus), VB2_HASH_SHA256, + dig, SHA256_DIGEST_SIZE); + + LOG("FYI: Hash of the KM public key (modulus only):\n"); + hexdump_ident(dig, SHA256_DIGEST_SIZE, 8); +} + +/* + * This function overwrites 'km' and 'bpm' structures. Do not call it unless + * they are no longer needed, i.e. call only after BPM and KM were written to + * file or if they are not written at all ('-H' or '-D' without '-B' or '-K'). + */ +static int validate_hash(void) +{ + FILE *file = NULL; + unsigned char dig[SHA256_DIGEST_SIZE]; + + // In case fread fails + memset(&bpm, 0, sizeof(bpm)); + memset(&km, 0, sizeof(km)); + + file = fopen(params.bpm_file, "rb"); + if (file == NULL) { + ERROR("Cannot open file %s\n", params.bpm_file); + return 1; + } + if (!fread(&bpm, sizeof(bpm), 1, file) && !feof(file)) { + ERROR("Error reading file %s\n", params.bpm_file); + return 1; + } + fclose(file); + + if (bpm.ibb_element.ibb_seg_count < 1 || bpm.ibb_element.ibb_seg_count > 8) { + ERROR("Bad IBB segments count, '%s' is malformed\n", params.bpm_file); + return 1; + } + + if (params.dump_manifests) + dump_bpm(); + + file = fopen(params.km_file, "rb"); + if (file == NULL) { + ERROR("Cannot open file %s\n", params.km_file); + return 1; + } + if (!fread(&km, sizeof(km), 1, file) && !feof(file)){ + ERROR("Error reading file %s\n", params.km_file); + return 1; + } + fclose(file); + + if (params.dump_manifests) + dump_km(); + + if (params.validate_hash) { + // Assumes no pm_s in BPM + bpms_t *sig = (bpms_t*)&bpm.ibb_element.segment[bpm.ibb_element.ibb_seg_count]; + + vb2_digest_buffer(sig->key_signature.key.modulus, BPM_KEY_SIZE_BYTES, + VB2_HASH_SHA256, dig, SHA256_DIGEST_SIZE); + + if (memcmp(dig, km.bp_key.buf, SHA256_DIGEST_SIZE)) { + ERROR("Hash of key used for signing BPM **does not match** value in KM\n"); + return 1; + } + LOG("Hash of key used for signing BPM matches value in KM\n"); + } + + return 0; +} + +static const char *optstring = "hHdDvf:r:e:k:K:i:b:B:n:p:P:s:S:A:t:"; +static struct option long_options[] = { + {"file", required_argument, 0, 'f' }, + {"region", required_argument, 0, 'r' }, + {"fit-entries", required_argument, 0, 'e' }, + {"km-file", required_argument, 0, 'k' }, + {"oem-root-key", required_argument, 0, 'K' }, + {"kmid", required_argument, 0, 'i' }, + {"bpm-file", required_argument, 0, 'b' }, + {"bpm-key", required_argument, 0, 'B' }, + {"nem-data-stack", required_argument, 0, 'n' }, + {"pmbpm-version", required_argument, 0, 'p' }, + {"pmkm-version", required_argument, 0, 'P' }, + {"pbet-value", required_argument, 0, 't' }, + {"bpsvn", required_argument, 0, 's' }, + {"kmsvn", required_argument, 0, 'S' }, + {"acmsvn-auth", required_argument, 0, 'A' }, + {"dump-ibb", no_argument, 0, 'd' }, + {"dump-manifests", no_argument, 0, 'D' }, + {"verbose", no_argument, 0, 'v' }, + {"help", no_argument, 0, 'h' }, + {"hash-check", no_argument, 0, 'H' }, + {NULL, 0, 0, 0 } +}; + +static void usage(const char *name) +{ + printf( + "\nibpmtool: utility for creating Boot Policy Manifests and Key manifests\n\n" + "USAGE: %s [OPTIONS] <-f|--file name> <-e|--fit-entries size>\n" + "\t<-B|--bpm-key bpm_key> [<-b|--bpm-file bpm>]\n" + "\t[<-i|--key-id id> <-K|--oem-root-key km_key>] [<-k|--km-file km>]\n" + "\t[-r|--region fmap_region] [-n|--nem-data-stack num_pages]\n" + "\t[-p|--pmbpm-version oem_ver] [-P|--pmkm-version oem_ver]\n" + "\t[-t|--pbet-value seconds] [-s|--bpsvn bpm_revoc_val]\n" + "\t[-S|--kmsvn km_revoc_val] [-A|--acmsvn-auth acm_revoc_val]\n\n" + "OPTIONS:\n" + "\t-h|--help Display this text\n" + "\t-v|--verbose Be verbose (implies '-d')\n" + "\t-H|--hash-check Validate BPM key hash against KM. Can be used with\n" + "\t or without normally required options '-f', '-e'\n" + "\t and '-B'\n" + "\t-d|--dump-ibb Dump IBB regions\n" + "\t-D|--dump-manifests Dump BPM and KM fields from files specified by '-b'\n" + "\t and '-k' or their default values ("bpm.bin" and\n" + "\t "km.bin", respectively)\n" + "REQUIRED ARGUMENTS:\n" + "\t-f|--file The file containing the CBFS\n" + "\t-e|--fit-entries The number of possible FIT entries, not counting\n" + "\t FIT header\n" + "\t-B|--bpm-key Private key used for signing BPM\n" + "OPTIONAL ARGUMENTS:\n" + "\t-b|--bpm-file Output file for BPM, default "bpm.bin"\n" + "\t-K|--oem-root-key Private key used for signing KM\n" + "\t-i|--kmid Key Manifest ID (1-15), must match the one in ME\n" + "\t-k|--km-file Output file for KM, default "km.bin"\n" + "\t-r|--region The FMAP region to operate on, default "COREBOOT"\n" + "\t-n|--nem-data-stack Number of pages for NEM (CAR) data, default 0x160\n" + "\t-p|--pmbpm-version Platform Manufacturer's BPM version number\n" + "\t-P|--pmkm-version Platform Manufacturer's KM version number\n" + "\t-t|--pbet-value Time, in seconds, allowed for BIOS execution before\n" + "\t writing to BOOT_GUARD_PBEC MSR. Minimum of 6, value\n" + "\t of 0 means unlimited. Default 10\n" + "\t-s|--bpsvn Boot Policy revocation value. Anti-rollback feature\n" + "\t that doesn't allow revoked BPMs to be used. This\n" + "\t value is saved in FPF and saturates at 0xf, use\n" + "\t with care\n" + "\t-S|--kmsvn The same as above, but for KM\n" + "\t-A|--acmsvn-auth ACM revocation value. Similar to the above, but the\n" + "\t revocation process is more complicated. Production\n" + "\t ACMs begin with 2, this is the default value. Do\n" + "\t not change it unless you know what you're doing\n\n" + "Either both '-K' and '-i' options must be used or none of them. If they are not\n" + "used, Key Manifest file won't be generated.\n" + , name); +} + +static int read_args(int argc, char *argv[]) +{ + int ret = 0; + + while (1) { + int optindex = 0; + + char c = getopt_long(argc, argv, optstring, long_options, &optindex); + + if (c == -1) + break; + + switch (c) { + case 'h': + return 1; + case 'H': + params.validate_hash = 1; + break; + case 'v': + params.dump_ibb = 1; + verbose = 2; + break; + case 'd': + params.dump_ibb = 1; + break; + case 'D': + params.dump_manifests = 1; + break; + case 'f': + params.file = optarg; + break; + case 'r': + params.region = optarg; + break; + case 'e': + params.fit_entries = atoi(optarg); + break; + case 'n': + bpm.header.nem_data_stack = atoi(optarg); + break; + case 's': + bpm.header.bpsvn = atoi(optarg); + if (bpm.header.bpsvn > 0xf) { + ret = 1; + ERROR("Bad BPSVN value (%d)\n", bpm.header.bpsvn); + } + break; + case 'S': + km.kmsvn = atoi(optarg); + if (km.kmsvn > 0xf) { + ret = 1; + ERROR("Bad KMSVN value (%d)\n", km.kmsvn); + } + break; + case 'A': + bpm.header.acmsvn_auth = atoi(optarg); + if (bpm.header.acmsvn_auth > 0xf) { + ret = 1; + ERROR("Bad ACMSVN_Auth value (%d)\n", bpm.header.acmsvn_auth); + } + break; + case 't': + // Actual time is 5 + pbet_value seconds + if (atoi(optarg) == 0) { + bpm.ibb_element.pbet_value = 0; + break; + } + if (atoi(optarg) <= 5) { + ret = 1; + ERROR("Too low PBET value\n"); + break; + } + bpm.ibb_element.pbet_value = atoi(optarg) - 5; + break; + case 'p': + bpm.header.pmbpm_version = atoi(optarg); + break; + case 'P': + km.km_version = atoi(optarg); + break; + case 'k': + params.km_file = optarg; + break; + case 'K': + params.km_key = optarg; + break; + case 'i': + params.km_id = atoi(optarg); + break; + case 'b': + params.bpm_file = optarg; + break; + case 'B': + params.bpm_key = optarg; + break; + default: + ERROR("Unknown option '%c'\n", c); + break; + } + } + + if (params.file == NULL && params.validate_hash == 0 && params.dump_manifests == 0) { + ERROR("Required '--file' argument missing\n"); + ret = 1; + } + + if (params.fit_entries == 0 && params.validate_hash == 0 && params.dump_manifests == 0) { + ERROR("Required '--fit-entries' argument missing\n"); + ret = 1; + } + + if (params.bpm_key == NULL && params.validate_hash == 0 && params.dump_manifests == 0) { + ERROR("Required '--bpm-key' argument missing\n"); + ret = 1; + } + + if (params.km_key == NULL && params.km_id != 0) { + ERROR("Got '--key-id', but no '--oem-root-key'\n"); + ret = 1; + } + + if (params.km_key != NULL && (params.km_id < 1 || params.km_id > 15)) { + ERROR("Wrong '--key-id'\n"); + ret = 1; + } + + return ret; +} + +static void dump_args(void) +{ + DEBUG("Arguments:\n"); + DEBUG("\tfile: %s\n", params.file ?: "(null)"); + DEBUG("\tregion: %s\n", params.region ?: "(null)"); + DEBUG("\tfit-entries: %d\n", params.fit_entries); + DEBUG("\tkm_file: %s\n", params.km_file ?: "(null)"); + DEBUG("\tkm_key: %s\n", params.km_key ?: "(null)"); + DEBUG("\tkm_id: %d\n", params.km_id); + DEBUG("\tbpm_file: %s\n", params.bpm_file ?: "(null)"); + DEBUG("\tbpm_key: %s\n\n", params.bpm_key ?: "(null)"); + DEBUG("\tvalidate_hash: %d\n\n", params.validate_hash); + DEBUG("\tdump_manifests: %d\n\n", params.dump_manifests); +} + +int main(int argc, char *argv[]) +{ + FILE *file = NULL; + RSA* privkey = NULL; + const BIGNUM *n, *e; + struct buffer image_region; + + if (read_args(argc, argv)) { + dump_args(); + usage(argv[0]); + return 1; + } + + dump_args(); + + if ((params.validate_hash || params.dump_manifests) && + (!params.file || !params.fit_entries || !params.bpm_key)) { + INFO("'-H' and/or '-D' used while required arguments are not set, not " + "generating manifest files\n"); + return validate_hash(); + } + + image_file = partitioned_file_reopen(params.file, 1); + if (image_file == NULL) { + ERROR("Cannot open file %s\n", params.file); + return 1; + } + + if (!partitioned_file_read_region(&image_region, image_file, params.region)) { + partitioned_file_close(image_file); + ERROR("No '%s' region in %s\n", params.region, params.file); + return 1; + } + + // Fill IBB structures and extend their hashes + DEBUG("Adding IBB structures\n"); + fill_ibb_element(&image_region, params.fit_entries); + + // Function above modified FIT, write it back to file + if (!partitioned_file_write_region(image_file, &image_region)) { + ERROR("Failed to write changes to %s\n", params.file); + partitioned_file_close(image_file); + return 1; + } + partitioned_file_close(image_file); + + bpms_t *new_sig = fill_gaps(); + + DEBUG("Reading BPM private key\n"); + file = fopen(params.bpm_key, "rb"); + if (file == NULL) { + ERROR("Cannot open file %s\n", params.bpm_key); + return 1; + } + + if (!(privkey = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL))) { + ERROR("Couldn't read private key from file %s\n", params.bpm_key); + fclose(file); + return 1; + } + + fclose(file); + + RSA_get0_key(privkey, &n, &e, NULL); + if (BN_bn2lebinpad(n, new_sig->key_signature.key.modulus, + BPM_KEY_SIZE_BYTES) != + BPM_KEY_SIZE_BYTES) { + ERROR("Bad length of modulus\n"); + return 1; + } + + // Calculate SHA256 of BPM fields and sign it + DEBUG("Signing BPM\n"); + unsigned int size = (char*)(new_sig) - (char*)(&bpm); + unsigned char dig[SHA256_DIGEST_SIZE]; + + vb2_digest_buffer((unsigned char*)&bpm, size, + VB2_HASH_SHA256, dig, SHA256_DIGEST_SIZE); + + RSA_sign(NID_sha256, dig, SHA256_DIGEST_SIZE, + new_sig->key_signature.sig_structure.rsa_sig, &size, privkey); + + if (size != BPM_KEY_SIZE_BYTES) { + ERROR("Bad key size: 0x%x\n", size); + } + + // Write BPM to file + size = (char *)(new_sig) - (char*)(&bpm) + sizeof(bpms_t); + file = fopen(params.bpm_file, "wb"); + fwrite(&bpm, size, 1, file); + fclose(file); + + DEBUG("Boot Policy Manifest saved to %s\n", params.bpm_file); + + // Boot Policy Manifest is ready, now prepare Key Manifest, if required + if (params.km_key == NULL) { + DEBUG("OEM root key not provided, skipping Key Manifest generation\n"); + if (params.validate_hash || params.dump_manifests) + return validate_hash(); + return 0; + } + + DEBUG("Building Key Manifest\n"); + km.km_id = params.km_id; + // SHA256 of public part (modulus) of key used for signing BPM + vb2_digest_buffer(new_sig->key_signature.key.modulus, BPM_KEY_SIZE_BYTES, + VB2_HASH_SHA256, km.bp_key.buf, SHA256_DIGEST_SIZE); + + DEBUG("Reading OEM root private key\n"); + file = fopen(params.km_key, "rb"); + if (file == NULL) { + ERROR("Cannot open file %s\n", params.km_key); + return 1; + } + + if (!(privkey = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL))) { + ERROR("Couldn't read private key from file %s\n", params.km_key); + fclose(file); + return 1; + } + + fclose(file); + + RSA_get0_key(privkey, &n, &e, NULL); + if (BN_bn2lebinpad(n, km.signature.key.modulus, BPM_KEY_SIZE_BYTES) + != BPM_KEY_SIZE_BYTES) { + ERROR("Bad length of modulus\n"); + return 1; + } + + // Calculate SHA256 of KM fields and sign it + DEBUG("Signing KM\n"); + vb2_digest_buffer((unsigned char*)&km, sizeof(km) - sizeof(km.signature), + VB2_HASH_SHA256, dig, SHA256_DIGEST_SIZE); + + RSA_sign(NID_sha256, dig, SHA256_DIGEST_SIZE, + km.signature.sig_structure.rsa_sig, &size, privkey); + + if (size != BPM_KEY_SIZE_BYTES) { + ERROR("Bad key size: 0x%x\n", size); + } + + file = fopen(params.km_file, "wb"); + fwrite(&km, sizeof(km), 1, file); + fclose(file); + + DEBUG("Key Manifest saved to %s\n", params.km_file); + + if (params.validate_hash || params.dump_manifests) + return validate_hash(); + + return 0; +} diff --git a/util/cbfstool/bpm.h b/util/cbfstool/bpm.h new file mode 100644 index 0000000..6a1cf68 --- /dev/null +++ b/util/cbfstool/bpm.h @@ -0,0 +1,99 @@ +// SHA1 is not supported for Verified Boot +#define ALG_SHA1 0x04 +#define ALG_SHA256 0x0B +// NULL seems to use the same size as SHA256 +#define ALG_NULL 0x10 + +#define SHA1_DIGEST_SIZE 0x14 +#define SHA256_DIGEST_SIZE 0x20 + +#define ALG_RSA 0x01 +#define ALG_RSASSA 0x14 + +#define BPM_KEY_SIZE_BITS 2048 +#define BPM_KEY_SIZE_BYTES (BPM_KEY_SIZE_BITS / 8) + +typedef struct { + uint16_t hash_alg; + uint16_t size; + uint8_t buf[SHA256_DIGEST_SIZE]; +} __packed hash_s; + +typedef struct { + uint8_t version; + uint16_t key_size; + uint32_t exponent; + uint8_t modulus[BPM_KEY_SIZE_BYTES]; // little endian +} __packed rsa_pk_s; + +typedef struct { + uint8_t version; + uint16_t key_size; + uint16_t hash_alg; + uint8_t rsa_sig[BPM_KEY_SIZE_BYTES]; // little endian +} __packed rsassa_sig_s; + +typedef struct { + uint8_t version; + uint16_t key_alg; + rsa_pk_s key; + uint16_t sig_scheme; + rsassa_sig_s sig_structure; +} __packed key_sig_s; + +typedef struct { + char structure_id[8]; // "__ACBP__" + uint8_t struct_version; + uint8_t hdr_struct_version; + uint8_t pmbpm_version; + uint8_t bpsvn; + uint8_t acmsvn_auth; + uint8_t reserved; + uint16_t nem_data_stack; // in 4K pages +} __packed bpm_header_t; + +#define IBB_SEGMENT_HASHED 0 +#define IBB_SEGMENT_NONHASHED 1 +typedef struct { + uint16_t reserved; + uint16_t flags; + uint32_t base; + uint32_t size; +} __packed ibb_seg; + +#define ENABLE_VTD 0x01 +#define INITIALI_MEASURE_LOC3 0x02 +#define AUTHORITY_MEASURE 0x04 +#define TPM_FAILURE_ACTION 0x08 +#define TOP_SWAP_SUPPORTED 0x10 +typedef struct { + char structure_id[8]; // "__IBBS__" + uint8_t struct_version; + uint8_t reserved[2]; + uint8_t pbet_value; + uint32_t flags; + uint64_t ibb_mchbar; + uint64_t vtd_bar; + uint32_t pmrl_base; + uint32_t pmrl_limit; + uint64_t reserved1[2]; + hash_s post_ibb_hash; + uint32_t ibb_entry_point; + hash_s digest; + uint8_t ibb_seg_count; + ibb_seg segment[8]; // ibb_seg_count, min 1, max 8 +// There may be less than 8, the gap will be filled later in the code +} __packed ibb_element_t; + +typedef struct { + char structure_id[8]; // "__PMDA__" + uint8_t struct_version; + uint16_t pm_data_size; + uint8_t pm_data[512]; // max 512, must be multiple of 4 bytes +} __packed pm_s_t; + +typedef struct { + char structure_id[8]; // "__PMSG__" + uint8_t struct_version; + key_sig_s key_signature; +} __packed bpms_t; diff --git a/util/cbfstool/fit.c b/util/cbfstool/fit.c index e7aa8d7..fb619a5 100644 --- a/util/cbfstool/fit.c +++ b/util/cbfstool/fit.c @@ -21,43 +21,6 @@
#define FIT_SIZE_ALIGNMENT 16
-struct fit_entry { - /** - * Address is the base address of the firmware component - * must be aligned on 16 byte boundary - */ - uint64_t address; - /** - * Size is the span of the component in multiple of 16 bytes - * Bits [24:31] are reserved and must be set to 0 - */ - uint32_t size_reserved; - /** - * Component's version number in binary coded decimal (BCD) format. - * For the FIT header entry, the value in this field will indicate the - * revision number of the FIT data structure. The upper byte of the - * revision field indicates the major revision and the lower byte - * indicates the minor revision. - */ - uint16_t version; - /** - * FIT types 0x00 to 0x7F - * Bit 7 (C_V) indicates whether component has valid checksum. - */ - uint8_t type_checksum_valid; - /** - * Component's checksum. The modulo sum of all the bytes in the - * component and the value in this field (Chksum) must add up to zero. - * This field is only valid if the C_V flag is non-zero. - */ - uint8_t checksum; -} __packed; - -struct fit_table { - struct fit_entry header; - struct fit_entry entries[]; -} __packed; - struct microcode_header { uint32_t version; uint32_t revision; diff --git a/util/cbfstool/fit.h b/util/cbfstool/fit.h index d900cf5..4177370 100644 --- a/util/cbfstool/fit.h +++ b/util/cbfstool/fit.h @@ -4,6 +4,7 @@ #ifndef __CBFSTOOL_FIT_H #define __CBFSTOOL_FIT_H
+#include "partitioned_file.h" #include "cbfs_image.h" #include "common.h"
@@ -27,6 +28,43 @@ FIT_TYPE_UNUSED = 127, };
+struct fit_entry { + /** + * Address is the base address of the firmware component + * must be aligned on 16 byte boundary + */ + uint64_t address; + /** + * Size is the span of the component in multiple of 16 bytes + * Bits [24:31] are reserved and must be set to 0 + */ + uint32_t size_reserved; + /** + * Component's version number in binary coded decimal (BCD) format. + * For the FIT header entry, the value in this field will indicate the + * revision number of the FIT data structure. The upper byte of the + * revision field indicates the major revision and the lower byte + * indicates the minor revision. + */ + uint16_t version; + /** + * FIT types 0x00 to 0x7F + * Bit 7 (C_V) indicates whether component has valid checksum. + */ + uint8_t type_checksum_valid; + /** + * Component's checksum. The modulo sum of all the bytes in the + * component and the value in this field (Chksum) must add up to zero. + * This field is only valid if the C_V flag is non-zero. + */ + uint8_t checksum; +} __packed; + +struct fit_table { + struct fit_entry header; + struct fit_entry entries[]; +} __packed; + /* * Converts between offsets from the start of the specified image region and * "top-aligned" offsets from the top of the entire flash image. Should work in @@ -36,7 +74,43 @@ typedef unsigned (*fit_offset_converter_t)(const struct buffer *region, unsigned offset);
-struct fit_table; + +extern partitioned_file_t *image_file; +/* + * Converts between offsets from the start of the specified image region and + * "top-aligned" offsets from the top of the entire boot media. See comment + * below for convert_to_from_top_aligned() about forming addresses. + */ +static inline unsigned int convert_to_from_absolute_top_aligned( + const struct buffer *region, unsigned int offset) +{ + assert(region); + + size_t image_size = partitioned_file_total_size(image_file); + + return image_size - region->offset - offset; +} + +/* + * Converts between offsets from the start of the specified image region and + * "top-aligned" offsets from the top of the image region. Works in either + * direction: pass in one type of offset and receive the other type. + * N.B. A top-aligned offset is always a positive number, and should not be + * confused with a top-aligned *address*, which is its arithmetic inverse. */ +static inline unsigned int convert_to_from_top_aligned(const struct buffer *region, + unsigned int offset) +{ + assert(region); + + /* Cover the situation where a negative base address is given by the + * user. Callers of this function negate it, so it'll be a positive + * number smaller than the region. + */ + if ((offset > 0) && (offset < region->size)) + return region->size - offset; + + return convert_to_from_absolute_top_aligned(region, offset); +}
struct fit_table *fit_get_table(struct buffer *bootblock, fit_offset_converter_t offset_fn, diff --git a/util/cbfstool/ifittool.c b/util/cbfstool/ifittool.c index 4d85229..1b0f286 100644 --- a/util/cbfstool/ifittool.c +++ b/util/cbfstool/ifittool.c @@ -9,7 +9,6 @@
#include "common.h" #include "cbfs_image.h" -#include "partitioned_file.h" #include "fit.h"
/* Global variables */ @@ -77,42 +76,6 @@ }
/* - * Converts between offsets from the start of the specified image region and - * "top-aligned" offsets from the top of the entire boot media. See comment - * below for convert_to_from_top_aligned() about forming addresses. - */ -static unsigned int convert_to_from_absolute_top_aligned( - const struct buffer *region, unsigned int offset) -{ - assert(region); - - size_t image_size = partitioned_file_total_size(image_file); - - return image_size - region->offset - offset; -} - -/* - * Converts between offsets from the start of the specified image region and - * "top-aligned" offsets from the top of the image region. Works in either - * direction: pass in one type of offset and receive the other type. - * N.B. A top-aligned offset is always a positive number, and should not be - * confused with a top-aligned *address*, which is its arithmetic inverse. */ -static unsigned int convert_to_from_top_aligned(const struct buffer *region, - unsigned int offset) -{ - assert(region); - - /* Cover the situation where a negative base address is given by the - * user. Callers of this function negate it, so it'll be a positive - * number smaller than the region. - */ - if ((offset > 0) && (offset < region->size)) - return region->size - offset; - - return convert_to_from_absolute_top_aligned(region, offset); -} - -/* * Get a pointer from an offset. This function assumes the ROM is located * in the host address space at [4G - romsize -> 4G). It also assume all * pointers have values within this address range. diff --git a/util/cbfstool/libcrypto-compat.c b/util/cbfstool/libcrypto-compat.c new file mode 100644 index 0000000..f34b5c3 --- /dev/null +++ b/util/cbfstool/libcrypto-compat.c @@ -0,0 +1,428 @@ +/* + * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "libcrypto-compat.h" + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +#include <string.h> +#include <stdint.h> +#include <openssl/engine.h> + +static void *OPENSSL_zalloc(size_t num) +{ + void *ret = OPENSSL_malloc(num); + + if (ret != NULL) + memset(ret, 0, num); + return ret; +} + +int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +{ + /* If the fields n and e in r are NULL, the corresponding input + * parameters MUST be non-NULL for n and e. d may be + * left NULL (in case only the public key is used). + */ + if ((r->n == NULL && n == NULL) + || (r->e == NULL && e == NULL)) + return 0; + + if (n != NULL) { + BN_free(r->n); + r->n = n; + } + if (e != NULL) { + BN_free(r->e); + r->e = e; + } + if (d != NULL) { + BN_free(r->d); + r->d = d; + } + + return 1; +} + +int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) +{ + /* If the fields p and q in r are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((r->p == NULL && p == NULL) + || (r->q == NULL && q == NULL)) + return 0; + + if (p != NULL) { + BN_free(r->p); + r->p = p; + } + if (q != NULL) { + BN_free(r->q); + r->q = q; + } + + return 1; +} + +int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) +{ + /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((r->dmp1 == NULL && dmp1 == NULL) + || (r->dmq1 == NULL && dmq1 == NULL) + || (r->iqmp == NULL && iqmp == NULL)) + return 0; + + if (dmp1 != NULL) { + BN_free(r->dmp1); + r->dmp1 = dmp1; + } + if (dmq1 != NULL) { + BN_free(r->dmq1); + r->dmq1 = dmq1; + } + if (iqmp != NULL) { + BN_free(r->iqmp); + r->iqmp = iqmp; + } + + return 1; +} + +void RSA_get0_key(const RSA *r, + const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) +{ + if (n != NULL) + *n = r->n; + if (e != NULL) + *e = r->e; + if (d != NULL) + *d = r->d; +} + +void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) +{ + if (p != NULL) + *p = r->p; + if (q != NULL) + *q = r->q; +} + +void RSA_get0_crt_params(const RSA *r, + const BIGNUM **dmp1, const BIGNUM **dmq1, + const BIGNUM **iqmp) +{ + if (dmp1 != NULL) + *dmp1 = r->dmp1; + if (dmq1 != NULL) + *dmq1 = r->dmq1; + if (iqmp != NULL) + *iqmp = r->iqmp; +} + +void DSA_get0_pqg(const DSA *d, + const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +{ + if (p != NULL) + *p = d->p; + if (q != NULL) + *q = d->q; + if (g != NULL) + *g = d->g; +} + +int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + /* If the fields p, q and g in d are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((d->p == NULL && p == NULL) + || (d->q == NULL && q == NULL) + || (d->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free(d->p); + d->p = p; + } + if (q != NULL) { + BN_free(d->q); + d->q = q; + } + if (g != NULL) { + BN_free(d->g); + d->g = g; + } + + return 1; +} + +void DSA_get0_key(const DSA *d, + const BIGNUM **pub_key, const BIGNUM **priv_key) +{ + if (pub_key != NULL) + *pub_key = d->pub_key; + if (priv_key != NULL) + *priv_key = d->priv_key; +} + +int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) +{ + /* If the field pub_key in d is NULL, the corresponding input + * parameters MUST be non-NULL. The priv_key field may + * be left NULL. + */ + if (d->pub_key == NULL && pub_key == NULL) + return 0; + + if (pub_key != NULL) { + BN_free(d->pub_key); + d->pub_key = pub_key; + } + if (priv_key != NULL) { + BN_free(d->priv_key); + d->priv_key = priv_key; + } + + return 1; +} + +void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) +{ + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} + +int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + if (r == NULL || s == NULL) + return 0; + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + return 1; +} + +void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) +{ + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} + +int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + if (r == NULL || s == NULL) + return 0; + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + return 1; +} + +void DH_get0_pqg(const DH *dh, + const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +{ + if (p != NULL) + *p = dh->p; + if (q != NULL) + *q = dh->q; + if (g != NULL) + *g = dh->g; +} + +int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + /* If the fields p and g in d are NULL, the corresponding input + * parameters MUST be non-NULL. q may remain NULL. + */ + if ((dh->p == NULL && p == NULL) + || (dh->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free(dh->p); + dh->p = p; + } + if (q != NULL) { + BN_free(dh->q); + dh->q = q; + } + if (g != NULL) { + BN_free(dh->g); + dh->g = g; + } + + if (q != NULL) { + dh->length = BN_num_bits(q); + } + + return 1; +} + +void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) +{ + if (pub_key != NULL) + *pub_key = dh->pub_key; + if (priv_key != NULL) + *priv_key = dh->priv_key; +} + +int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) +{ + /* If the field pub_key in dh is NULL, the corresponding input + * parameters MUST be non-NULL. The priv_key field may + * be left NULL. + */ + if (dh->pub_key == NULL && pub_key == NULL) + return 0; + + if (pub_key != NULL) { + BN_free(dh->pub_key); + dh->pub_key = pub_key; + } + if (priv_key != NULL) { + BN_free(dh->priv_key); + dh->priv_key = priv_key; + } + + return 1; +} + +int DH_set_length(DH *dh, long length) +{ + dh->length = length; + return 1; +} + +const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx) +{ + return ctx->iv; +} + +unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx) +{ + return ctx->iv; +} + +EVP_MD_CTX *EVP_MD_CTX_new(void) +{ + return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); +} + +void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + EVP_MD_CTX_cleanup(ctx); + OPENSSL_free(ctx); +} + +RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth) +{ + RSA_METHOD *ret; + + ret = OPENSSL_malloc(sizeof(RSA_METHOD)); + + if (ret != NULL) { + memcpy(ret, meth, sizeof(*meth)); + ret->name = OPENSSL_strdup(meth->name); + if (ret->name == NULL) { + OPENSSL_free(ret); + return NULL; + } + } + + return ret; +} + +int RSA_meth_set1_name(RSA_METHOD *meth, const char *name) +{ + char *tmpname; + + tmpname = OPENSSL_strdup(name); + if (tmpname == NULL) { + return 0; + } + + OPENSSL_free((void *)(uintptr_t)meth->name); + meth->name = tmpname; + + return 1; +} + +int RSA_meth_set_priv_enc(RSA_METHOD *meth, + int (*priv_enc) (int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)) +{ + meth->rsa_priv_enc = priv_enc; + return 1; +} + +int RSA_meth_set_priv_dec(RSA_METHOD *meth, + int (*priv_dec) (int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)) +{ + meth->rsa_priv_dec = priv_dec; + return 1; +} + +int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish) (RSA *rsa)) +{ + meth->finish = finish; + return 1; +} + +void RSA_meth_free(RSA_METHOD *meth) +{ + if (meth != NULL) { + OPENSSL_free((void *)(uintptr_t)meth->name); + OPENSSL_free(meth); + } +} + +int RSA_bits(const RSA *r) +{ + return (BN_num_bits(r->n)); +} + +RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey) +{ + if (pkey->type != EVP_PKEY_RSA) { + return NULL; + } + return pkey->pkey.rsa; +} + +int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen) +{ + if (tolen < BN_num_bytes(a)) + return -1; + + memset(to, 0, tolen); + + int size = BN_bn2bin(a, to); + + for (int i = 0; i < size/2; i++) { + unsigned char tmp = to[i]; + to[i] = to[size - i - 1]; + to[size - i - 1] = tmp; + } + + return size; +} + +#endif /* OPENSSL_VERSION_NUMBER */ diff --git a/util/cbfstool/libcrypto-compat.h b/util/cbfstool/libcrypto-compat.h new file mode 100644 index 0000000..18b54c8 --- /dev/null +++ b/util/cbfstool/libcrypto-compat.h @@ -0,0 +1,60 @@ +#ifndef LIBCRYPTO_COMPAT_H +#define LIBCRYPTO_COMPAT_H + +#include <openssl/opensslv.h> +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +#include <openssl/rsa.h> +#include <openssl/dsa.h> +#include <openssl/ecdsa.h> +#include <openssl/dh.h> +#include <openssl/evp.h> + +int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); +int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q); +int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp); +void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d); +void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); +void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp); + +void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); +int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); +void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key); +int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); + +void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps); +int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s); + +void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps); +int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s); + +void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); +int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); +void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key); +int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key); +int DH_set_length(DH *dh, long length); + +const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx); +unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx); +EVP_MD_CTX *EVP_MD_CTX_new(void); +void EVP_MD_CTX_free(EVP_MD_CTX *ctx); +#define EVP_CIPHER_impl_ctx_size(e) e->ctx_size +#define EVP_CIPHER_CTX_get_cipher_data(ctx) ctx->cipher_data + +RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth); +int RSA_meth_set1_name(RSA_METHOD *meth, const char *name); +#define RSA_meth_get_finish(meth) meth->finish +int RSA_meth_set_priv_enc(RSA_METHOD *meth, int (*priv_enc) (int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)); +int RSA_meth_set_priv_dec(RSA_METHOD *meth, int (*priv_dec) (int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)); +int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish) (RSA *rsa)); +void RSA_meth_free(RSA_METHOD *meth); + +int RSA_bits(const RSA *r); + +RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey); + +int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen); + +#endif /* OPENSSL_VERSION_NUMBER */ + +#endif /* LIBCRYPTO_COMPAT_H */