diff --git a/arch/arm/crypto/sha1_glue.c b/arch/arm/crypto/sha1_glue.c index 176aa9e..57cd9d1 100644 --- a/arch/arm/crypto/sha1_glue.c +++ b/arch/arm/crypto/sha1_glue.c @@ -119,6 +119,7 @@ .name = "sha1", .driver_name = "sha1-asm", .priority = 150, + .algo = HASH_ALGO_SHA1, }, .init = sha1_init, diff --git a/arch/arm/crypto/sha256_glue.c b/arch/arm/crypto/sha256_glue.c index f8086f6..e649609 100644 --- a/arch/arm/crypto/sha256_glue.c +++ b/arch/arm/crypto/sha256_glue.c @@ -173,6 +173,7 @@ .name = "sha224", .driver_name = "sha224-asm", .priority = 150, + .algo = HASH_ALGO_SHA224, }, .length = SHA224_DIGEST_SIZE, @@ -195,6 +196,7 @@ .name = "sha256", .driver_name = "sha256-asm", .priority = 150, + .algo = HASH_ALGO_SHA256, }, .length = SHA256_DIGEST_SIZE, diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index 7bb9b43..1913d5f 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -86,9 +86,11 @@ } } - ret = bootm_load_initrd(data, initrd_start); - if (ret) - return ret; + if (bootm_has_initrd(data)) { + ret = bootm_load_initrd(data, initrd_start); + if (ret) + return ret; + } if (data->initrd_res) { initrd_start = data->initrd_res->start; @@ -110,6 +112,9 @@ printf("...\n"); } + if (data->dryrun) + return 0; + start_linux((void *)kernel, swap, initrd_start, initrd_size, data->oftree); restart_machine(); @@ -135,7 +140,7 @@ * relocate itself before decompression. */ load_address = mem_start + PAGE_ALIGN( - uimage_get_size(data->os, data->os_num) * 4); + bootm_get_os_size(data) * 4); if (bootm_verbose(data)) printf("no OS load address, defaulting to 0x%08lx\n", load_address); @@ -223,6 +228,7 @@ ret = -EINVAL; goto err_free; } + free(oftree); } else { data->oftree = oftree; } @@ -354,37 +360,9 @@ .filetype = filetype_arm_zimage, }; -static int do_bootm_barebox(struct image_data *data) -{ - void *barebox; - - barebox = read_file(data->os_file, NULL); - if (!barebox) - return -EINVAL; - - if (IS_ENABLED(CONFIG_OFTREE) && data->of_root_node) { - data->oftree = of_get_fixed_tree(data->of_root_node); - fdt_add_reserve_map(data->oftree); - of_print_cmdline(data->of_root_node); - if (bootm_verbose(data) > 1) - of_print_nodes(data->of_root_node, 0); - } - - if (bootm_verbose(data)) { - printf("\nStarting barebox at 0x%p", barebox); - if (data->oftree) - printf(", oftree at 0x%p", data->oftree); - printf("...\n"); - } - - start_linux(barebox, 0, 0, 0, data->oftree); - - restart_machine(); -} - static struct image_handler barebox_handler = { .name = "ARM barebox", - .bootm = do_bootm_barebox, + .bootm = do_bootm_linux, .filetype = filetype_arm_barebox, }; @@ -553,6 +531,12 @@ BAREBOX_MAGICVAR(aimage_noverwrite_tags, "Disable overwrite of the tags addr with the one present in aimage"); #endif +static struct image_handler arm_fit_handler = { + .name = "FIT image", + .bootm = do_bootm_linux, + .filetype = filetype_oftree, +}; + static struct binfmt_hook binfmt_aimage_hook = { .type = filetype_aimage, .exec = "bootm", @@ -578,6 +562,8 @@ register_image_handler(&aimage_handler); binfmt_register(&binfmt_aimage_hook); } + if (IS_BUILTIN(CONFIG_CMD_BOOTM_FITIMAGE)) + register_image_handler(&arm_fit_handler); binfmt_register(&binfmt_arm_zimage_hook); binfmt_register(&binfmt_barebox_hook); diff --git a/arch/arm/mach-omap/omap_generic.c b/arch/arm/mach-omap/omap_generic.c index 4e26c6b..2221acf 100644 --- a/arch/arm/mach-omap/omap_generic.c +++ b/arch/arm/mach-omap/omap_generic.c @@ -79,6 +79,11 @@ if (!barebox) return -EINVAL; + if (data->dryrun) { + free(barebox) + return 0; + } + omap_start_barebox(barebox); } diff --git a/arch/blackfin/lib/blackfin_linux.c b/arch/blackfin/lib/blackfin_linux.c index 2561a7e..da2f78b 100644 --- a/arch/blackfin/lib/blackfin_linux.c +++ b/arch/blackfin/lib/blackfin_linux.c @@ -50,6 +50,9 @@ appl = (void *)(idata->os_address + idata->os_entry); printf("Starting Kernel at 0x%p\n", appl); + if (idata->dryrun) + return 0; + icache_disable(); strncpy(cmdlinedest, cmdline, 0x1000); diff --git a/arch/efi/efi/efi-image.c b/arch/efi/efi/efi-image.c index b6437f4..c78043b 100644 --- a/arch/efi/efi/efi-image.c +++ b/arch/efi/efi/efi-image.c @@ -190,7 +190,7 @@ static int do_bootm_efi(struct image_data *data) { void *tmp; - void *initrd; + void *initrd = NULL; size_t size; efi_handle_t handle; int ret; @@ -244,6 +244,13 @@ printf("...\n"); } + if (data->dryrun) { + BS->unload_image(handle); + free(boot_header); + free(initrd); + return 0; + } + efi_set_variable_usec("LoaderTimeExecUSec", &efi_systemd_vendor_guid, get_time_ns()/1000); diff --git a/arch/mips/lib/bootm.c b/arch/mips/lib/bootm.c index 84f72f5..ce1521f 100644 --- a/arch/mips/lib/bootm.c +++ b/arch/mips/lib/bootm.c @@ -17,6 +17,11 @@ if (!barebox) return -EINVAL; + if (data->dryrun) { + free(barebox) + return 0; + } + shutdown_barebox(); barebox(); diff --git a/arch/nios2/lib/bootm.c b/arch/nios2/lib/bootm.c index 77da119..231568f 100644 --- a/arch/nios2/lib/bootm.c +++ b/arch/nios2/lib/bootm.c @@ -42,6 +42,9 @@ if (ret) return ret; + if (data->dryrun) + return 0; + kernel = (void *)(idata->os_address + idata->os_entry); /* kernel parameters passing diff --git a/arch/ppc/lib/ppclinux.c b/arch/ppc/lib/ppclinux.c index 409c0cf..a36682c 100644 --- a/arch/ppc/lib/ppclinux.c +++ b/arch/ppc/lib/ppclinux.c @@ -60,6 +60,9 @@ return -EINVAL; } + if (data->dryrun) + return 0; + /* Relocate the device tree if outside the initial * Linux mapped TLB. */ diff --git a/commands/Kconfig b/commands/Kconfig index 1743670..b8f37c1 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -418,6 +418,28 @@ help Support using Android Images. +config CMD_BOOTM_FITIMAGE + bool + prompt "FIT image support" + select FITIMAGE + depends on CMD_BOOTM && ARM + help + Support using FIT Images. Have a look at the u-boot source + tree in the "doc/uImage.FIT" folder for more information: + http://git.denx.de/?p=u-boot.git;a=tree;f=doc/uImage.FIT + +config CMD_BOOTM_FITIMAGE_SIGNATURE + bool + prompt "support verifying signed FIT images" + depends on CMD_BOOTM_FITIMAGE + select FITIMAGE_SIGNATURE + help + Support verifying signed FIT images. This requires FIT images + as described in: + http://git.denx.de/?p=u-boot.git;a=blob;f=doc/uImage.FIT/signature.txt + Additionally the barebox device tree needs a /signature node with the + public key with which the image has been signed. + config CMD_BOOTU tristate default y diff --git a/commands/boot.c b/commands/boot.c index fd58824..f403010 100644 --- a/commands/boot.c +++ b/commands/boot.c @@ -42,13 +42,9 @@ static int boot_script(char *path) { int ret; - struct bootm_data data = { - .os_address = UIMAGE_SOME_ADDRESS, - .initrd_address = UIMAGE_SOME_ADDRESS, - }; + struct bootm_data data = {}; globalvar_set_match("linux.bootargs.dyn.", ""); - globalvar_set_match("bootm.", ""); ret = run_command(path); if (ret) { @@ -56,15 +52,9 @@ goto out; } - data.initrd_address = UIMAGE_INVALID_ADDRESS; - data.os_address = UIMAGE_SOME_ADDRESS; - data.oftree_file = getenv_nonempty("global.bootm.oftree"); - data.os_file = getenv_nonempty("global.bootm.image"); - getenv_ul("global.bootm.image.loadaddr", &data.os_address); - getenv_ul("global.bootm.initrd.loadaddr", &data.initrd_address); - data.initrd_file = getenv_nonempty("global.bootm.initrd"); data.verbose = verbose; data.dryrun = dryrun; + bootm_data_init_defaults(&data); ret = bootm_boot(&data); if (ret) diff --git a/commands/bootm.c b/commands/bootm.c index 75849a1..7a19fa2 100644 --- a/commands/bootm.c +++ b/commands/bootm.c @@ -46,7 +46,7 @@ #include #include -#define BOOTM_OPTS_COMMON "ca:e:vo:fd" +#define BOOTM_OPTS_COMMON "sca:e:vo:fd" #ifdef CONFIG_CMD_BOOTM_INITRD #define BOOTM_OPTS BOOTM_OPTS_COMMON "L:r:" @@ -59,31 +59,24 @@ int opt; struct bootm_data data = {}; int ret = 1; - const char *oftree = NULL, *initrd_file = NULL, *os_file = NULL; - data.initrd_address = UIMAGE_INVALID_ADDRESS; - data.os_address = UIMAGE_SOME_ADDRESS; - data.verify = 0; - data.verbose = 0; - - oftree = getenv("global.bootm.oftree"); - os_file = getenv("global.bootm.image"); - getenv_ul("global.bootm.image.loadaddr", &data.os_address); - getenv_ul("global.bootm.initrd.loadaddr", &data.initrd_address); - if (IS_ENABLED(CONFIG_CMD_BOOTM_INITRD)) - initrd_file = getenv("global.bootm.initrd"); + bootm_data_init_defaults(&data); while ((opt = getopt(argc, argv, BOOTM_OPTS)) > 0) { switch(opt) { case 'c': - data.verify = 1; + if (data.verify < BOOTM_VERIFY_HASH) + data.verify = BOOTM_VERIFY_HASH; + break; + case 's': + data.verify = BOOTM_VERIFY_SIGNATURE; break; #ifdef CONFIG_CMD_BOOTM_INITRD case 'L': data.initrd_address = simple_strtoul(optarg, NULL, 0); break; case 'r': - initrd_file = optarg; + data.initrd_file = optarg; break; #endif case 'a': @@ -96,7 +89,7 @@ data.verbose++; break; case 'o': - oftree = optarg; + data.oftree_file = optarg; break; case 'f': data.force = 1; @@ -110,39 +103,27 @@ } if (optind != argc) - os_file = argv[optind]; + data.os_file = argv[optind]; - if (!os_file || !*os_file) { + if (!data.os_file) { printf("no boot image given\n"); goto err_out; } - if (initrd_file && !*initrd_file) - initrd_file = NULL; - - if (oftree && !*oftree) - oftree = NULL; - - data.os_file = os_file; - data.oftree_file = oftree; - data.initrd_file = initrd_file; - ret = bootm_boot(&data); if (ret) { printf("handler failed with: %s\n", strerror(-ret)); goto err_out; } - if (data.dryrun) - printf("Dryrun. Aborted\n"); - err_out: return ret ? 1 : 0; } BAREBOX_CMD_HELP_START(bootm) BAREBOX_CMD_HELP_TEXT("Options:") -BAREBOX_CMD_HELP_OPT ("-c\t", "crc check uImage data") +BAREBOX_CMD_HELP_OPT ("-c\t", "hash check image integrity") +BAREBOX_CMD_HELP_OPT ("-s\t", "check signature of image") BAREBOX_CMD_HELP_OPT ("-d\t", "dry run: check data, but do not run") BAREBOX_CMD_HELP_OPT ("-f\t", "load images even if type is undetectable") #ifdef CONFIG_CMD_BOOTM_INITRD @@ -184,6 +165,7 @@ BAREBOX_MAGICVAR_NAMED(global_bootm_initrd, global.bootm.initrd, "bootm default initrd"); BAREBOX_MAGICVAR_NAMED(global_bootm_initrd_loadaddr, global.bootm.initrd.loadaddr, "bootm default initrd loadaddr"); BAREBOX_MAGICVAR_NAMED(global_bootm_oftree, global.bootm.oftree, "bootm default oftree"); +BAREBOX_MAGICVAR_NAMED(global_bootm_verify, global.bootm.verify, "bootm default verify level"); static struct binfmt_hook binfmt_uimage_hook = { .type = filetype_uimage, diff --git a/common/Kconfig b/common/Kconfig index 8e79509..345de50 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -66,6 +66,15 @@ select CRC32 bool +config FITIMAGE + bool + select OFTREE + select DIGEST + +config FITIMAGE_SIGNATURE + select CRYPTO_RSA + bool + config LOGBUF bool diff --git a/common/Makefile b/common/Makefile index 56e6bec..ffaf8e7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_SHELL_SIMPLE) += parser.o obj-$(CONFIG_STATE) += state.o obj-$(CONFIG_UIMAGE) += image.o uimage.o +obj-$(CONFIG_FITIMAGE) += image-fit.o obj-$(CONFIG_MENUTREE) += menutree.o obj-$(CONFIG_EFI_GUID) += efi-guid.o obj-$(CONFIG_EFI_DEVICEPATH) += efi-devicepath.o diff --git a/common/bootm.c b/common/bootm.c index 08125e7..d8acff8 100644 --- a/common/bootm.c +++ b/common/bootm.c @@ -17,8 +17,10 @@ #include #include #include +#include #include #include +#include static LIST_HEAD(handler_list); @@ -46,6 +48,31 @@ return NULL; } +void bootm_data_init_defaults(struct bootm_data *data) +{ + data->initrd_address = UIMAGE_INVALID_ADDRESS; + data->os_address = UIMAGE_SOME_ADDRESS; + data->oftree_file = getenv_nonempty("global.bootm.oftree"); + data->os_file = getenv_nonempty("global.bootm.image"); + getenv_ul("global.bootm.image.loadaddr", &data->os_address); + getenv_ul("global.bootm.initrd.loadaddr", &data->initrd_address); + data->initrd_file = getenv_nonempty("global.bootm.initrd"); + data->verify = bootm_get_verify_mode(); +} + +static enum bootm_verify bootm_verify_mode = BOOTM_VERIFY_HASH; + +enum bootm_verify bootm_get_verify_mode(void) +{ + return bootm_verify_mode; +} + +static const char * const bootm_verify_names[] = { + [BOOTM_VERIFY_NONE] = "none", + [BOOTM_VERIFY_HASH] = "hash", + [BOOTM_VERIFY_SIGNATURE] = "signature", +}; + /* * bootm_load_os() - load OS to RAM * @@ -66,9 +93,24 @@ if (load_address == UIMAGE_INVALID_ADDRESS) return -EINVAL; + if (data->os_fit) { + data->os_res = request_sdram_region("kernel", + load_address, + data->os_fit->kernel_size); + if (!data->os_res) + return -ENOMEM; + memcpy((void *)load_address, data->os_fit->kernel, + data->os_fit->kernel_size); + return 0; + } + if (data->os) { + int num; + + num = simple_strtoul(data->os_part, NULL, 0); + data->os_res = uimage_load_to_sdram(data->os, - data->os_num, load_address); + num, load_address); if (!data->os_res) return -ENOMEM; @@ -86,6 +128,44 @@ return -EINVAL; } +bool bootm_has_initrd(struct image_data *data) +{ + if (!IS_ENABLED(CONFIG_CMD_BOOTM_INITRD)) + return false; + + if (data->os_fit && data->os_fit->initrd) + return true; + + if (data->initrd_file) + return true; + + return false; +} + +static int bootm_open_initrd_uimage(struct image_data *data) +{ + int ret; + + if (strcmp(data->os_file, data->initrd_file)) { + data->initrd = uimage_open(data->initrd_file); + if (!data->initrd) + return -EINVAL; + + if (bootm_get_verify_mode() > BOOTM_VERIFY_NONE) { + ret = uimage_verify(data->initrd); + if (ret) { + printf("Checking data crc failed with %s\n", + strerror(-ret)); + } + } + uimage_print_contents(data->initrd); + } else { + data->initrd = data->os; + } + + return 0; +} + /* * bootm_load_initrd() - load initrd to RAM * @@ -101,24 +181,112 @@ */ int bootm_load_initrd(struct image_data *data, unsigned long load_address) { + enum filetype type; + int ret; + + if (!IS_ENABLED(CONFIG_CMD_BOOTM_INITRD)) + return -ENOSYS; + + if (!bootm_has_initrd(data)) + return -EINVAL; + if (data->initrd_res) return 0; - if (data->initrd) { - data->initrd_res = uimage_load_to_sdram(data->initrd, - data->initrd_num, load_address); + if (data->os_fit && data->os_fit->initrd) { + data->initrd_res = request_sdram_region("initrd", + load_address, + data->os_fit->initrd_size); if (!data->initrd_res) return -ENOMEM; - - return 0; + memcpy((void *)load_address, data->os_fit->initrd, + data->os_fit->initrd_size); + printf("Loaded initrd from FIT image\n"); + goto done1; } - if (data->initrd_file) { - data->initrd_res = file_to_sdram(data->initrd_file, load_address); + type = file_name_detect_type(data->initrd_file); + + if ((int)type < 0) { + printf("could not open %s: %s\n", data->initrd_file, + strerror(-type)); + return (int)type; + } + + if (type == filetype_uimage) { + int num; + ret = bootm_open_initrd_uimage(data); + if (ret) { + printf("loading initrd failed with %s\n", + strerror(-ret)); + return ret; + } + + num = simple_strtoul(data->initrd_part, NULL, 0); + + data->initrd_res = uimage_load_to_sdram(data->initrd, + num, load_address); if (!data->initrd_res) return -ENOMEM; - return 0; + goto done; + } + + data->initrd_res = file_to_sdram(data->initrd_file, load_address); + if (!data->initrd_res) + return -ENOMEM; + +done: + + printf("Loaded initrd %s '%s'", file_type_to_string(type), + data->initrd_file); + if (type == filetype_uimage && data->initrd->header.ih_type == IH_TYPE_MULTI) + printf(", multifile image %s", data->initrd_part); + printf("\n"); +done1: + printf("initrd is at " PRINTF_CONVERSION_RESOURCE "-" PRINTF_CONVERSION_RESOURCE "\n", + data->initrd_res->start, + data->initrd_res->end); + + return 0; +} + +static int bootm_open_oftree_uimage(struct image_data *data, size_t *size, + struct fdt_header **fdt) +{ + enum filetype ft; + const char *oftree = data->oftree_file; + int num = simple_strtoul(data->oftree_part, NULL, 0); + struct uimage_handle *of_handle; + int release = 0; + + printf("Loading devicetree from '%s'@%d\n", oftree, num); + + if (!IS_ENABLED(CONFIG_CMD_BOOTM_OFTREE_UIMAGE)) + return -EINVAL; + + if (!strcmp(data->os_file, oftree)) { + of_handle = data->os; + } else if (!strcmp(data->initrd_file, oftree)) { + of_handle = data->initrd; + } else { + of_handle = uimage_open(oftree); + if (!of_handle) + return -ENODEV; + uimage_print_contents(of_handle); + release = 1; + } + + *fdt = uimage_load_to_buf(of_handle, num, size); + + if (release) + uimage_close(of_handle); + + ft = file_detect_type(*fdt, *size); + if (ft != filetype_oftree) { + printf("%s is not an oftree but %s\n", + data->oftree_file, file_type_to_string(ft)); + return -EINVAL; } return 0; @@ -137,8 +305,10 @@ */ int bootm_load_devicetree(struct image_data *data, unsigned long load_address) { + enum filetype type; int fdt_size; struct fdt_header *oftree; + int ret; if (data->oftree) return 0; @@ -146,8 +316,51 @@ if (!IS_ENABLED(CONFIG_OFTREE)) return 0; - if (!data->of_root_node) - return 0; + if (data->os_fit && data->os_fit->oftree) { + data->of_root_node = of_unflatten_dtb(data->os_fit->oftree); + } else if (data->oftree_file) { + size_t size; + + type = file_name_detect_type(data->oftree_file); + + if ((int)type < 0) { + printf("could not open %s: %s\n", data->oftree_file, + strerror(-type)); + return (int)type; + } + + switch (type) { + case filetype_uimage: + ret = bootm_open_oftree_uimage(data, &size, &oftree); + break; + case filetype_oftree: + ret = read_file_2(data->oftree_file, &size, (void *)&oftree, + FILESIZE_MAX); + break; + default: + return -EINVAL; + } + + if (ret) + return ret; + + data->of_root_node = of_unflatten_dtb(oftree); + + free(oftree); + + if (!data->of_root_node) { + pr_err("unable to unflatten devicetree\n"); + return -EINVAL; + } + + } else { + data->of_root_node = of_get_root_node(); + if (!data->of_root_node) + return 0; + + if (bootm_verbose(data) > 1 && data->of_root_node) + printf("using internal devicetree\n"); + } if (data->initrd_res) { of_add_initrd(data->of_root_node, data->initrd_res->start, @@ -185,6 +398,27 @@ return 0; } +int bootm_get_os_size(struct image_data *data) +{ + int ret; + + if (data->os) + return uimage_get_size(data->os, + simple_strtoul(data->os_part, NULL, 0)); + if (data->os_fit) + return data->os_fit->kernel_size; + + if (data->os_file) { + struct stat s; + ret = stat(data->os_file, &s); + if (ret) + return ret; + return s.st_size; + } + + return -EINVAL; +} + static int bootm_open_os_uimage(struct image_data *data) { int ret; @@ -193,7 +427,7 @@ if (!data->os) return -EINVAL; - if (data->verify) { + if (bootm_get_verify_mode() > BOOTM_VERIFY_NONE) { ret = uimage_verify(data->os); if (ret) { printf("Checking data crc failed with %s\n", @@ -208,7 +442,6 @@ if (data->os->header.ih_arch != IH_ARCH) { printf("Unsupported Architecture 0x%x\n", data->os->header.ih_arch); - uimage_close(data->os); return -EINVAL; } @@ -218,111 +451,6 @@ return 0; } -static int bootm_open_initrd_uimage(struct image_data *data) -{ - int ret; - - if (!data->initrd_file) - return 0; - - if (strcmp(data->os_file, data->initrd_file)) { - data->initrd = uimage_open(data->initrd_file); - if (!data->initrd) - return -EINVAL; - - if (data->verify) { - ret = uimage_verify(data->initrd); - if (ret) { - printf("Checking data crc failed with %s\n", - strerror(-ret)); - } - } - uimage_print_contents(data->initrd); - } else { - data->initrd = data->os; - } - - return 0; -} - -static int bootm_open_oftree_uimage(struct image_data *data) -{ - struct fdt_header *fdt; - enum filetype ft; - const char *oftree = data->oftree_file; - int num = data->oftree_num; - struct uimage_handle *of_handle; - int release = 0; - size_t size; - - printf("Loading devicetree from '%s'@%d\n", oftree, num); - - if (IS_ENABLED(CONFIG_CMD_BOOTM_OFTREE_UIMAGE)) { - if (!strcmp(data->os_file, oftree)) { - of_handle = data->os; - } else if (!strcmp(data->initrd_file, oftree)) { - of_handle = data->initrd; - } else { - of_handle = uimage_open(oftree); - if (!of_handle) - return -ENODEV; - uimage_print_contents(of_handle); - release = 1; - } - - fdt = uimage_load_to_buf(of_handle, num, &size); - - if (release) - uimage_close(of_handle); - - ft = file_detect_type(fdt, size); - if (ft != filetype_oftree) { - printf("%s is not an oftree but %s\n", - data->oftree_file, file_type_to_string(ft)); - return -EINVAL; - } - - data->of_root_node = of_unflatten_dtb(fdt); - if (!data->of_root_node) { - pr_err("unable to unflatten devicetree\n"); - free(fdt); - return -EINVAL; - } - - free(fdt); - - return 0; - } else { - return -EINVAL; - } -} - -static int bootm_open_oftree(struct image_data *data) -{ - struct fdt_header *fdt; - const char *oftree = data->oftree_file; - size_t size; - - printf("Loading devicetree from '%s'\n", oftree); - - fdt = read_file(oftree, &size); - if (!fdt) { - perror("open"); - return -ENODEV; - } - - data->of_root_node = of_unflatten_dtb(fdt); - if (!data->of_root_node) { - pr_err("unable to unflatten devicetree\n"); - free(fdt); - return -EINVAL; - } - - free(fdt); - - return 0; -} - static void bootm_print_info(struct image_data *data) { if (data->os_res) @@ -331,45 +459,29 @@ data->os_res->end); else printf("OS image not yet relocated\n"); - - if (data->initrd_file) { - enum filetype initrd_type = file_name_detect_type(data->initrd_file); - - printf("Loading initrd %s '%s'", - file_type_to_string(initrd_type), - data->initrd_file); - if (initrd_type == filetype_uimage && - data->initrd->header.ih_type == IH_TYPE_MULTI) - printf(", multifile image %d", data->initrd_num); - printf("\n"); - if (data->initrd_res) - printf("initrd is at " PRINTF_CONVERSION_RESOURCE "-" PRINTF_CONVERSION_RESOURCE "\n", - data->initrd_res->start, - data->initrd_res->end); - else - printf("initrd image not yet relocated\n"); - } } -static char *bootm_image_name_and_no(const char *name, int *no) +static int bootm_image_name_and_part(const char *name, char **filename, char **part) { char *at, *ret; if (!name || !*name) - return NULL; - - *no = 0; + return -EINVAL; ret = xstrdup(name); + + *filename = ret; + *part = NULL; + at = strchr(ret, '@'); if (!at) - return ret; + return 0; *at++ = 0; - *no = simple_strtoul(at, NULL, 10); + *part = at; - return ret; + return 0; } /* @@ -380,8 +492,7 @@ struct image_data *data; struct image_handler *handler; int ret; - enum filetype os_type, initrd_type = filetype_unknown; - enum filetype oftree_type = filetype_unknown; + enum filetype os_type; if (!bootm_data->os_file) { printf("no image given\n"); @@ -390,9 +501,9 @@ data = xzalloc(sizeof(*data)); - data->os_file = bootm_image_name_and_no(bootm_data->os_file, &data->os_num); - data->oftree_file = bootm_image_name_and_no(bootm_data->oftree_file, &data->oftree_num); - data->initrd_file = bootm_image_name_and_no(bootm_data->initrd_file, &data->initrd_num); + bootm_image_name_and_part(bootm_data->os_file, &data->os_file, &data->os_part); + bootm_image_name_and_part(bootm_data->oftree_file, &data->oftree_part, &data->os_part); + bootm_image_name_and_part(bootm_data->initrd_file, &data->initrd_part, &data->os_part); data->verbose = bootm_data->verbose; data->verify = bootm_data->verify; data->force = bootm_data->force; @@ -415,70 +526,36 @@ goto err_out; } - if (IS_ENABLED(CONFIG_CMD_BOOTM_INITRD) && data->initrd_file) { - initrd_type = file_name_detect_type(data->initrd_file); + if (IS_ENABLED(CONFIG_FITIMAGE) && os_type == filetype_oftree) { + struct fit_handle *fit; - if ((int)initrd_type < 0) { - printf("could not open %s: %s\n", data->initrd_file, - strerror(-initrd_type)); - ret = (int)initrd_type; + fit = fit_open(data->os_file, data->os_part, data->verbose, data->verify); + if (IS_ERR(fit)) { + printf("Loading FIT image %s failed with: %s\n", data->os_file, + strerrorp(fit)); + ret = PTR_ERR(fit); goto err_out; } - } - if (IS_ENABLED(CONFIG_OFTREE) && data->oftree_file) { - oftree_type = file_name_detect_type(data->oftree_file); - - if ((int)oftree_type < 0) { - printf("could not open %s: %s\n", data->oftree_file, - strerror(-oftree_type)); - ret = (int) oftree_type; - goto err_out; - } + data->os_fit = fit; } if (os_type == filetype_uimage) { ret = bootm_open_os_uimage(data); if (ret) { - printf("Loading OS image failed with %s\n", + printf("Loading OS image failed with: %s\n", strerror(-ret)); goto err_out; } } - if (IS_ENABLED(CONFIG_CMD_BOOTM_INITRD) && data->initrd_file) { - if (initrd_type == filetype_uimage) { - ret = bootm_open_initrd_uimage(data); - if (ret) { - printf("loading initrd failed with %s\n", - strerror(-ret)); - goto err_out; - } - } - } - printf("\nLoading %s '%s'", file_type_to_string(os_type), data->os_file); if (os_type == filetype_uimage && data->os->header.ih_type == IH_TYPE_MULTI) - printf(", multifile image %d", data->os_num); + printf(", multifile image %s", data->os_part); printf("\n"); - if (IS_ENABLED(CONFIG_OFTREE)) { - if (data->oftree_file) { - if (oftree_type == filetype_uimage) - ret = bootm_open_oftree_uimage(data); - if (oftree_type == filetype_oftree) - ret = bootm_open_oftree(data); - if (ret) - goto err_out; - } else { - data->of_root_node = of_get_root_node(); - if (bootm_verbose(data) > 1 && data->of_root_node) - printf("using internal devicetree\n"); - } - } - if (data->os_address == UIMAGE_SOME_ADDRESS) data->os_address = UIMAGE_INVALID_ADDRESS; @@ -497,10 +574,10 @@ printf("Passing control to %s handler\n", handler->name); } + ret = handler->bootm(data); if (data->dryrun) - ret = 0; - else - ret = handler->bootm(data); + printf("Dryrun. Aborted\n"); + err_out: if (data->os_res) release_sdram_region(data->os_res); @@ -512,6 +589,8 @@ uimage_close(data->initrd); if (data->os) uimage_close(data->os); + if (IS_ENABLED(CONFIG_FITIMAGE) && data->os_fit) + fit_close(data->os_fit); if (data->of_root_node && data->of_root_node != of_get_root_node()) of_delete_node(data->of_root_node); @@ -532,6 +611,8 @@ globalvar_add_simple("bootm.initrd", NULL); globalvar_add_simple("bootm.initrd.loadaddr", NULL); } + globalvar_add_simple_enum("bootm.verify", (unsigned int *)&bootm_verify_mode, + bootm_verify_names, ARRAY_SIZE(bootm_verify_names)); return 0; } diff --git a/common/image-fit.c b/common/image-fit.c new file mode 100644 index 0000000..9ce1116 --- /dev/null +++ b/common/image-fit.c @@ -0,0 +1,581 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright (C) Jan Lübbe, 2014 + */ +#define pr_fmt(fmt) "FIT: " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FDT_MAX_DEPTH 32 +#define FDT_MAX_PATH_LEN 200 + +#define CHECK_LEVEL_NONE 0 +#define CHECK_LEVEL_HASH 1 +#define CHECK_LEVEL_SIG 2 +#define CHECK_LEVEL_MAX 3 + +static uint32_t dt_struct_advance(struct fdt_header *f, uint32_t dt, int size) +{ + dt += size; + dt = ALIGN(dt, 4); + + if (dt > f->off_dt_struct + f->size_dt_struct) + return 0; + + return dt; +} + +static char *dt_string(struct fdt_header *f, char *strstart, uint32_t ofs) +{ + if (ofs > f->size_dt_strings) + return NULL; + else + return strstart + ofs; +} + +static int of_read_string_list(struct device_node *np, const char *name, struct string_list *sl) +{ + struct property *prop; + const char *s; + + of_property_for_each_string(np, name, prop, s) { + string_list_add(sl, s); + } + + return prop ? 0 : -EINVAL; +} + +static int fit_digest(void *fit, struct digest *digest, + struct string_list *inc_nodes, struct string_list *exc_props, + uint32_t hashed_strings_start, uint32_t hashed_strings_size) +{ + struct fdt_header *fdt = fit; + uint32_t dt_struct; + void *dt_strings; + struct fdt_header f = {}; + int stack[FDT_MAX_DEPTH]; + char path[FDT_MAX_PATH_LEN]; + char *end; + uint32_t tag; + int start = -1; + int depth = -1; + int want = 0; + + f.totalsize = fdt32_to_cpu(fdt->totalsize); + f.off_dt_struct = fdt32_to_cpu(fdt->off_dt_struct); + f.size_dt_struct = fdt32_to_cpu(fdt->size_dt_struct); + f.off_dt_strings = fdt32_to_cpu(fdt->off_dt_strings); + f.size_dt_strings = fdt32_to_cpu(fdt->size_dt_strings); + + if (hashed_strings_start > f.size_dt_strings || + hashed_strings_size > f.size_dt_strings || + hashed_strings_start + hashed_strings_size > f.size_dt_strings) { + pr_err("%s: hashed-strings too large\n", __func__); + return -EINVAL; + } + + dt_struct = f.off_dt_struct; + dt_strings = (void *)fdt + f.off_dt_strings; + + end = path; + *end = '\0'; + + do { + const struct fdt_property *fdt_prop; + const struct fdt_node_header *fnh; + const char *name; + int include = 0; + int stop_at = 0; + int offset = dt_struct; + int maxlen, len; + + tag = be32_to_cpu(*(uint32_t *)(fit + dt_struct)); + + switch (tag) { + case FDT_BEGIN_NODE: + fnh = fit + dt_struct; + name = fnh->name; + maxlen = (unsigned long)fdt + f.off_dt_struct + + f.size_dt_struct - (unsigned long)name; + + len = strnlen(name, maxlen + 1); + if (len > maxlen) + return -ESPIPE; + + dt_struct = dt_struct_advance(&f, dt_struct, + sizeof(struct fdt_node_header) + len + 1); + + depth++; + if (depth == FDT_MAX_DEPTH) + return -ESPIPE; + if (end - path + 2 + len >= FDT_MAX_PATH_LEN) + return -ESPIPE; + if (end != path + 1) + *end++ = '/'; + strcpy(end, name); + end += len; + stack[depth] = want; + if (want == 1) + stop_at = offset; + if (string_list_contains(inc_nodes, path)) + want = 2; + else if (want) + want--; + else + stop_at = offset; + include = want; + + break; + + case FDT_END_NODE: + dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE); + + include = want; + want = stack[depth--]; + while (end > path && *--end != '/') + ; + *end = '\0'; + + break; + + case FDT_PROP: + fdt_prop = fit + dt_struct; + len = fdt32_to_cpu(fdt_prop->len); + + name = dt_string(&f, dt_strings, fdt32_to_cpu(fdt_prop->nameoff)); + if (!name) + return -ESPIPE; + + dt_struct = dt_struct_advance(&f, dt_struct, + sizeof(struct fdt_property) + len); + + include = want >= 2; + stop_at = offset; + if (string_list_contains(exc_props, name)) + include = 0; + + break; + + case FDT_NOP: + dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE); + + include = want >= 2; + stop_at = offset; + + break; + + case FDT_END: + dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE); + + include = 1; + + break; + + default: + pr_err("%s: Unknown tag 0x%08X\n", __func__, tag); + return -EINVAL; + } + + if (!dt_struct) + return -ESPIPE; + + pr_debug("%s: include %d, want %d, offset 0x%x, len 0x%x\n", + path, include, want, offset, dt_struct-offset); + + if (include && start == -1) + start = offset; + + if (!include && start != -1) { + pr_debug("region: 0x%p+0x%x\n", fit + start, offset - start); + digest_update(digest, fit + start, offset - start); + start = -1; + } + } while (tag != FDT_END); + + pr_debug("region: 0x%p+0x%x\n", fit + start, dt_struct - start); + digest_update(digest, fit + start, dt_struct - start); + + pr_debug("strings: 0x%p+0x%x\n", dt_strings+hashed_strings_start, hashed_strings_size); + digest_update(digest, dt_strings + hashed_strings_start, hashed_strings_size); + + return 0; +} + +/* + * The consistency of the FTD structure was already checked by of_unflatten_dtb() + */ +static int fit_verify_signature(struct device_node *sig_node, void *fit) +{ + uint32_t hashed_strings_start, hashed_strings_size; + struct string_list inc_nodes, exc_props; + struct rsa_public_key key = {}; + struct digest *digest; + int sig_len; + const char *algo_name, *key_name, *sig_value; + char *key_path; + struct device_node *key_node; + enum hash_algo algo; + void *hash; + int ret; + + if (of_property_read_string(sig_node, "algo", &algo_name)) { + pr_err("algo not found\n"); + ret = -EINVAL; + goto out; + } + if (strcmp(algo_name, "sha1,rsa2048") == 0) { + algo = HASH_ALGO_SHA1; + } else if (strcmp(algo_name, "sha256,rsa4096") == 0) { + algo = HASH_ALGO_SHA256; + } else { + pr_err("unknown algo %s\n", algo_name); + ret = -EINVAL; + goto out; + } + digest = digest_alloc_by_algo(algo); + if (!digest) { + pr_err("unsupported algo %s\n", algo_name); + ret = -EINVAL; + goto out; + } + + sig_value = of_get_property(sig_node, "value", &sig_len); + if (!sig_value) { + pr_err("signature value not found in %s\n", sig_node->full_name); + ret = -EINVAL; + goto out_free_digest; + } + + if (of_property_read_string(sig_node, "key-name-hint", &key_name)) { + pr_err("key name not found in %s\n", sig_node->full_name); + ret = -EINVAL; + goto out_free_digest; + } + key_path = xasprintf("/signature/key-%s", key_name); + key_node = of_find_node_by_path(key_path); + free(key_path); + if (!key_node) { + pr_info("failed to find key node\n"); + ret = -ENOENT; + goto out_free_digest; + } + + ret = rsa_of_read_key(key_node, &key); + if (ret) { + pr_info("failed to read key in %s\n", key_node->full_name); + ret = -ENOENT; + goto out_free_digest; + } + + if (of_property_read_u32_index(sig_node, "hashed-strings", 0, &hashed_strings_start)) { + pr_err("hashed-strings start not found in %s\n", sig_node->full_name); + ret = -EINVAL; + goto out_free_digest; + } + if (of_property_read_u32_index(sig_node, "hashed-strings", 1, &hashed_strings_size)) { + pr_err("hashed-strings size not found in %s\n", sig_node->full_name); + ret = -EINVAL; + goto out_free_digest; + } + + string_list_init(&inc_nodes); + string_list_init(&exc_props); + + if (of_read_string_list(sig_node, "hashed-nodes", &inc_nodes)) { + pr_err("hashed-nodes property not found in %s\n", sig_node->full_name); + ret = -EINVAL; + goto out_sl; + } + + string_list_add(&exc_props, "data"); + + digest_init(digest); + ret = fit_digest(fit, digest, &inc_nodes, &exc_props, hashed_strings_start, hashed_strings_size); + hash = xzalloc(digest_length(digest)); + digest_final(digest, hash); + + ret = rsa_verify(&key, sig_value, sig_len, hash, algo); + if (ret) { + pr_info("image signature BAD\n"); + ret = -EBADMSG; + } else { + pr_info("image signature OK\n"); + ret = 0; + } + + free(hash); + out_sl: + string_list_free(&inc_nodes); + string_list_free(&exc_props); + out_free_digest: + digest_free(digest); + out: + return ret; +} + +static int fit_verify_hash(struct device_node *hash, const void *data, int data_len) +{ + struct digest *d; + const char *algo; + const char *value_read; + char *value_calc; + int hash_len, ret; + + value_read = of_get_property(hash, "value", &hash_len); + if (!value_read) { + pr_err("%s: \"value\" property not found\n", hash->full_name); + return -EINVAL; + } + + if (of_property_read_string(hash, "algo", &algo)) { + pr_err("%s: \"algo\" property not found\n", hash->full_name); + return -EINVAL; + } + + d = digest_alloc(algo); + if (!d) { + pr_err("%s: unsupported algo %s\n", hash->full_name, algo); + return -EINVAL; + } + + if (hash_len != digest_length(d)) { + pr_err("%s: invalid hash length %d\n", hash->full_name, hash_len); + ret = -EINVAL; + goto err_digest_free; + } + + value_calc = xmalloc(hash_len); + + digest_init(d); + digest_update(d, data, data_len); + digest_final(d, value_calc); + + if (memcmp(value_read, value_calc, hash_len)) { + pr_info("%s: hash BAD\n", hash->full_name); + ret = -EBADMSG; + } else { + pr_info("%s: hash OK\n", hash->full_name); + ret = 0; + } + + free(value_calc); + +err_digest_free: + digest_free(d); + + return ret; +} + +static int fit_open_image(struct fit_handle *handle, const char *unit, const void **outdata, + unsigned long *outsize) +{ + struct device_node *image = NULL, *hash; + const char *type = NULL, *desc= "(no description)"; + const void *data; + int data_len; + int ret = 0; + + image = of_get_child_by_name(handle->root, "images"); + if (!image) + return -ENOENT; + + image = of_get_child_by_name(image, unit); + if (!image) + return -ENOENT; + + of_property_read_string(image, "description", &desc); + pr_info("image '%s': '%s'\n", unit, desc); + + of_property_read_string(image, "type", &type); + if (!type) { + pr_err("No \"type\" property found in %s\n", image->full_name); + return -EINVAL; + } + + data = of_get_property(image, "data", &data_len); + if (!data) { + pr_err("data not found\n"); + return -EINVAL; + } + + if (handle->verify > BOOTM_VERIFY_NONE) { + ret = -EINVAL; + for_each_child_of_node(image, hash) { + if (handle->verbose) + of_print_nodes(hash, 0); + ret = fit_verify_hash(hash, data, data_len); + if (ret < 0) + return ret; + } + } + if (ret < 0) + return ret; + + *outdata = data; + *outsize = data_len; + + return 0; +} + +static int fit_open_configuration(struct fit_handle *handle, const char *name) +{ + struct device_node *conf_node = NULL, *sig_node; + const char *unit, *desc = "(no description)"; + int ret; + + conf_node = of_get_child_by_name(handle->root, "configurations"); + if (!conf_node) + return -ENOENT; + + if (name) { + unit = name; + } else if (of_property_read_string(conf_node, "default", &unit)) { + unit = "conf@1"; + } + + conf_node = of_get_child_by_name(conf_node, unit); + if (!conf_node) { + pr_err("configuration '%s' not found\n", unit); + return -ENOENT; + } + + of_property_read_string(conf_node, "description", &desc); + pr_info("configuration '%s': %s\n", unit, desc); + + if (IS_ENABLED(CONFIG_FITIMAGE_SIGNATURE) && + handle->verify == BOOTM_VERIFY_SIGNATURE) { + ret = -EINVAL; + for_each_child_of_node(conf_node, sig_node) { + if (handle->verbose) + of_print_nodes(sig_node, 0); + ret = fit_verify_signature(sig_node, handle->fit); + if (ret < 0) + return ret; + } + + if (ret < 0) + return ret; + } + + if (of_property_read_string(conf_node, "kernel", &unit) == 0) { + ret = fit_open_image(handle, unit, &handle->kernel, &handle->kernel_size); + if (ret) + return ret; + } else { + return -ENOENT; + } + + if (of_property_read_string(conf_node, "fdt", &unit) == 0) { + ret = fit_open_image(handle, unit, &handle->oftree, &handle->oftree_size); + if (ret) + return ret; + } + + if (of_property_read_string(conf_node, "ramdisk", &unit) == 0) { + ret = fit_open_image(handle, unit, &handle->initrd, &handle->initrd_size); + if (ret) + return ret; + } + + return 0; +} + +struct fit_handle *fit_open(const char *filename, const char *config, bool verbose, + enum bootm_verify verify) +{ + struct fit_handle *handle = NULL; + const char *desc = "(no description)"; + int ret; + + handle = xzalloc(sizeof(struct fit_handle)); + + handle->verbose = verbose; + + ret = read_file_2(filename, &handle->size, &handle->fit, FILESIZE_MAX); + if (ret) { + pr_err("unable to read %s: %s\n", filename, strerror(-ret)); + goto err; + } + + handle->root = of_unflatten_dtb(handle->fit); + if (IS_ERR(handle->root)) { + ret = PTR_ERR(handle->root); + goto err; + } + + handle->verify = verify; + + of_property_read_string(handle->root, "description", &desc); + pr_info("'%s': %s\n", filename, desc); + + ret = fit_open_configuration(handle, config); + if (ret) + goto err; + + return handle; + err: + if (handle->root) + of_delete_node(handle->root); + free(handle->fit); + free(handle); + + return ERR_PTR(ret); +} + +void fit_close(struct fit_handle *handle) +{ + if (handle->root) + of_delete_node(handle->root); + if (handle->fit) + free(handle->fit); + free(handle); +} + +#ifdef CONFIG_SANDBOX +static int do_bootm_sandbox_fit(struct image_data *data) +{ + struct fit_handle *handle; + handle = fit_open(data->os_file, data->os_part, data->verbose); + if (handle) + fit_close(handle); + return 0; +} + +static struct image_handler sandbox_fit_handler = { + .name = "FIT image", + .bootm = do_bootm_sandbox_fit, + .filetype = filetype_oftree, +}; + +static int sandbox_fit_register(void) +{ + return register_image_handler(&sandbox_fit_handler); +} +late_initcall(sandbox_fit_register); +#endif diff --git a/crypto/Kconfig b/crypto/Kconfig index 41145a3..ca6f03a 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -87,6 +87,9 @@ select DIGEST_SHA1_GENERIC bool +config CRYPTO_RSA + bool + config CRYPTO_KEYSTORE bool "Keystore" help diff --git a/crypto/Makefile b/crypto/Makefile index c6d1778..b4fb3d8 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -11,4 +11,5 @@ obj-$(CONFIG_DIGEST_SHA512_GENERIC) += sha4.o obj-$(CONFIG_CRYPTO_PBKDF2) += pbkdf2.o +obj-$(CONFIG_CRYPTO_RSA) += rsa.o obj-$(CONFIG_CRYPTO_KEYSTORE) += keystore.o diff --git a/crypto/digest.c b/crypto/digest.c index a90e4ff..46600f2 100644 --- a/crypto/digest.c +++ b/crypto/digest.c @@ -116,7 +116,27 @@ list_for_each_entry(tmp, &digests, list) { if (strcmp(tmp->base.name, name) != 0) continue; - + + if (tmp->base.priority <= priority) + continue; + + d = tmp; + priority = tmp->base.priority; + } + + return d; +} + +static struct digest_algo *digest_algo_get_by_algo(enum hash_algo algo) +{ + struct digest_algo *d = NULL; + struct digest_algo *tmp; + int priority = -1; + + list_for_each_entry(tmp, &digests, list) { + if (tmp->base.algo != algo) + continue; + if (tmp->base.priority <= priority) continue; @@ -160,6 +180,27 @@ } EXPORT_SYMBOL_GPL(digest_alloc); +struct digest *digest_alloc_by_algo(enum hash_algo hash_algo) +{ + struct digest *d; + struct digest_algo *algo; + + algo = digest_algo_get_by_algo(hash_algo); + if (!algo) + return NULL; + + d = xzalloc(sizeof(*d)); + d->algo = algo; + d->ctx = xzalloc(algo->ctx_length); + if (d->algo->alloc(d)) { + digest_free(d); + return NULL; + } + + return d; +} +EXPORT_SYMBOL_GPL(digest_alloc_by_algo); + void digest_free(struct digest *d) { if (!d) diff --git a/crypto/md5.c b/crypto/md5.c index 23892ba..f8f52bf 100644 --- a/crypto/md5.c +++ b/crypto/md5.c @@ -293,6 +293,7 @@ .name = "md5", .driver_name = "md5-generic", .priority = 0, + .algo = HASH_ALGO_MD5, }, .init = digest_md5_init, .update = digest_md5_update, diff --git a/crypto/rsa.c b/crypto/rsa.c new file mode 100644 index 0000000..3caee3c --- /dev/null +++ b/crypto/rsa.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UINT64_MULT32(v, multby) (((uint64_t)(v)) * ((uint32_t)(multby))) + +#define get_unaligned_be32(a) fdt32_to_cpu(*(uint32_t *)a) +#define put_unaligned_be32(a, b) (*(uint32_t *)(b) = cpu_to_fdt32(a)) + +/* Default public exponent for backward compatibility */ +#define RSA_DEFAULT_PUBEXP 65537 + +/* This is the minimum/maximum key size we support, in bits */ +#define RSA_MIN_KEY_BITS 1024 +#define RSA_MAX_KEY_BITS 4096 + +/** + * subtract_modulus() - subtract modulus from the given value + * + * @key: Key containing modulus to subtract + * @num: Number to subtract modulus from, as little endian word array + */ +static void subtract_modulus(const struct rsa_public_key *key, uint32_t num[]) +{ + int64_t acc = 0; + uint i; + + for (i = 0; i < key->len; i++) { + acc += (uint64_t)num[i] - key->modulus[i]; + num[i] = (uint32_t)acc; + acc >>= 32; + } +} + +/** + * greater_equal_modulus() - check if a value is >= modulus + * + * @key: Key containing modulus to check + * @num: Number to check against modulus, as little endian word array + * @return 0 if num < modulus, 1 if num >= modulus + */ +static int greater_equal_modulus(const struct rsa_public_key *key, + uint32_t num[]) +{ + int i; + + for (i = (int)key->len - 1; i >= 0; i--) { + if (num[i] < key->modulus[i]) + return 0; + if (num[i] > key->modulus[i]) + return 1; + } + + return 1; /* equal */ +} + +/** + * montgomery_mul_add_step() - Perform montgomery multiply-add step + * + * Operation: montgomery result[] += a * b[] / n0inv % modulus + * + * @key: RSA key + * @result: Place to put result, as little endian word array + * @a: Multiplier + * @b: Multiplicand, as little endian word array + */ +static void montgomery_mul_add_step(const struct rsa_public_key *key, + uint32_t result[], const uint32_t a, const uint32_t b[]) +{ + uint64_t acc_a, acc_b; + uint32_t d0; + uint i; + + acc_a = (uint64_t)a * b[0] + result[0]; + d0 = (uint32_t)acc_a * key->n0inv; + acc_b = (uint64_t)d0 * key->modulus[0] + (uint32_t)acc_a; + for (i = 1; i < key->len; i++) { + acc_a = (acc_a >> 32) + (uint64_t)a * b[i] + result[i]; + acc_b = (acc_b >> 32) + (uint64_t)d0 * key->modulus[i] + + (uint32_t)acc_a; + result[i - 1] = (uint32_t)acc_b; + } + + acc_a = (acc_a >> 32) + (acc_b >> 32); + + result[i - 1] = (uint32_t)acc_a; + + if (acc_a >> 32) + subtract_modulus(key, result); +} + +/** + * montgomery_mul() - Perform montgomery mutitply + * + * Operation: montgomery result[] = a[] * b[] / n0inv % modulus + * + * @key: RSA key + * @result: Place to put result, as little endian word array + * @a: Multiplier, as little endian word array + * @b: Multiplicand, as little endian word array + */ +static void montgomery_mul(const struct rsa_public_key *key, + uint32_t result[], uint32_t a[], const uint32_t b[]) +{ + uint i; + + for (i = 0; i < key->len; ++i) + result[i] = 0; + for (i = 0; i < key->len; ++i) + montgomery_mul_add_step(key, result, a[i], b); +} + +/** + * num_pub_exponent_bits() - Number of bits in the public exponent + * + * @key: RSA key + * @num_bits: Storage for the number of public exponent bits + */ +static int num_public_exponent_bits(const struct rsa_public_key *key, + int *num_bits) +{ + uint64_t exponent; + int exponent_bits; + const uint max_bits = (sizeof(exponent) * 8); + + exponent = key->exponent; + exponent_bits = 0; + + if (!exponent) { + *num_bits = exponent_bits; + return 0; + } + + for (exponent_bits = 1; exponent_bits < max_bits + 1; ++exponent_bits) + if (!(exponent >>= 1)) { + *num_bits = exponent_bits; + return 0; + } + + return -EINVAL; +} + +/** + * is_public_exponent_bit_set() - Check if a bit in the public exponent is set + * + * @key: RSA key + * @pos: The bit position to check + */ +static int is_public_exponent_bit_set(const struct rsa_public_key *key, + int pos) +{ + return key->exponent & (1ULL << pos); +} + +/** + * pow_mod() - in-place public exponentiation + * + * @key: RSA key + * @inout: Big-endian word array containing value and result + */ +static int pow_mod(const struct rsa_public_key *key, void *__inout) +{ + uint32_t *inout = __inout; + uint32_t *result, *ptr; + uint i; + int j, k; + uint32_t val[RSA_MAX_KEY_BITS / 32], acc[RSA_MAX_KEY_BITS / 32], tmp[RSA_MAX_KEY_BITS / 32]; + uint32_t a_scaled[RSA_MAX_KEY_BITS / 32]; + + /* Sanity check for stack size - key->len is in 32-bit words */ + if (key->len > RSA_MAX_KEY_BITS / 32) { + debug("RSA key words %u exceeds maximum %d\n", key->len, + RSA_MAX_KEY_BITS / 32); + return -EINVAL; + } + + result = tmp; /* Re-use location. */ + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0, ptr = inout + key->len - 1; i < key->len; i++, ptr--) + val[i] = get_unaligned_be32(ptr); + + if (0 != num_public_exponent_bits(key, &k)) + return -EINVAL; + + if (k < 2) { + debug("Public exponent is too short (%d bits, minimum 2)\n", + k); + return -EINVAL; + } + + if (!is_public_exponent_bit_set(key, 0)) { + debug("LSB of RSA public exponent must be set.\n"); + return -EINVAL; + } + + /* the bit at e[k-1] is 1 by definition, so start with: C := M */ + montgomery_mul(key, acc, val, key->rr); /* acc = a * RR / R mod n */ + /* retain scaled version for intermediate use */ + memcpy(a_scaled, acc, key->len * sizeof(a_scaled[0])); + + for (j = k - 2; j > 0; --j) { + montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */ + + if (is_public_exponent_bit_set(key, j)) { + /* acc = tmp * val / R mod n */ + montgomery_mul(key, acc, tmp, a_scaled); + } else { + /* e[j] == 0, copy tmp back to acc for next operation */ + memcpy(acc, tmp, key->len * sizeof(acc[0])); + } + } + + /* the bit at e[0] is always 1 */ + montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */ + montgomery_mul(key, acc, tmp, val); /* acc = tmp * a / R mod M */ + memcpy(result, acc, key->len * sizeof(result[0])); + + /* Make sure result < mod; result is at most 1x mod too large. */ + if (greater_equal_modulus(key, result)) + subtract_modulus(key, result); + + /* Convert to bigendian byte array */ + for (i = key->len - 1, ptr = inout; (int)i >= 0; i--, ptr++) + put_unaligned_be32(result[i], ptr); + return 0; +} + +/* + * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2]. + */ +static const u8 RSA_digest_info_MD5[] = { + 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* OID */ + 0x05, 0x00, 0x04, 0x10 +}; + +static const u8 RSA_digest_info_SHA1[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2B, 0x0E, 0x03, 0x02, 0x1A, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 RSA_digest_info_RIPE_MD_160[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2B, 0x24, 0x03, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 RSA_digest_info_SHA224[] = { + 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, + 0x05, 0x00, 0x04, 0x1C +}; + +static const u8 RSA_digest_info_SHA256[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x20 +}; + +static const u8 RSA_digest_info_SHA384[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, + 0x05, 0x00, 0x04, 0x30 +}; + +static const u8 RSA_digest_info_SHA512[] = { + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x05, 0x00, 0x04, 0x40 +}; + +static const struct { + const u8 *data; + size_t size; +} RSA_ASN1_templates[] = { +#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) } + [HASH_ALGO_MD5] = _(MD5), + [HASH_ALGO_SHA1] = _(SHA1), + [HASH_ALGO_RIPE_MD_160] = _(RIPE_MD_160), + [HASH_ALGO_SHA256] = _(SHA256), + [HASH_ALGO_SHA384] = _(SHA384), + [HASH_ALGO_SHA512] = _(SHA512), + [HASH_ALGO_SHA224] = _(SHA224), +#undef _ +}; + +int rsa_verify(const struct rsa_public_key *key, const uint8_t *sig, + const uint32_t sig_len, const uint8_t *hash, + enum hash_algo algo) +{ + int ret = 0; + uint8_t buf[RSA_MAX_SIG_BITS / 8]; + int i; + unsigned PS_end, T_offset; + const u8 *asn1_template = RSA_ASN1_templates[algo].data; + size_t asn1_size = RSA_ASN1_templates[algo].size; + struct digest *d = digest_alloc_by_algo(algo); + + if (!d) + return -EOPNOTSUPP; + + if (sig_len != (key->len * sizeof(uint32_t))) { + debug("Signature is of incorrect length %d, should be %d\n", sig_len, + key->len * sizeof(uint32_t)); + ret = -EINVAL; + goto out_free_digest; + } + + /* Sanity check for stack size */ + if (sig_len > RSA_MAX_SIG_BITS / 8) { + debug("Signature length %u exceeds maximum %d\n", sig_len, + RSA_MAX_SIG_BITS / 8); + ret = -EINVAL; + goto out_free_digest; + } + + memcpy(buf, sig, sig_len); + + ret = pow_mod(key, buf); + if (ret) + goto out_free_digest; + + T_offset = sig_len - (asn1_size + digest_length(d)); + + PS_end = T_offset - 1; + if (buf[PS_end] != 0x00) { + pr_err(" = -EBADMSG [EM[T-1] == %02u]", buf[PS_end]); + ret = -EBADMSG; + goto out_free_digest; + } + + for (i = 2; i < PS_end; i++) { + if (buf[i] != 0xff) { + pr_err(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, buf[i]); + ret = -EBADMSG; + goto out_free_digest; + } + } + + if (memcmp(asn1_template, buf + T_offset, asn1_size) != 0) { + pr_err(" = -EBADMSG [EM[T] ASN.1 mismatch]"); + ret = -EBADMSG; + goto out_free_digest; + } + + if (memcmp(hash, buf + T_offset + asn1_size, digest_length(d)) != 0) { + pr_err(" = -EKEYREJECTED [EM[T] hash mismatch]"); + ret = -EKEYREJECTED; + goto out_free_digest; + } + + out_free_digest: + digest_free(d); + + return ret; +} + +static void rsa_convert_big_endian(uint32_t *dst, const uint32_t *src, int len) +{ + int i; + + for (i = 0; i < len; i++) + dst[i] = fdt32_to_cpu(src[len - 1 - i]); +} + +int rsa_of_read_key(struct device_node *node, struct rsa_public_key *key) +{ + const void *modulus, *rr; + const uint64_t *public_exponent; + int length; + + of_property_read_u32(node, "rsa,num-bits", &key->len); + of_property_read_u32(node, "rsa,n0-inverse", &key->n0inv); + + public_exponent = of_get_property(node, "rsa,exponent", &length); + if (!public_exponent || length < sizeof(*public_exponent)) + key->exponent = RSA_DEFAULT_PUBEXP; + else + key->exponent = fdt64_to_cpu(*public_exponent); + + modulus = of_get_property(node, "rsa,modulus", NULL); + rr = of_get_property(node, "rsa,r-squared", NULL); + + if (!key->len || !modulus || !rr) { + debug("%s: Missing RSA key info", __func__); + return -EFAULT; + } + + /* Sanity check for stack size */ + if (key->len > RSA_MAX_KEY_BITS || key->len < RSA_MIN_KEY_BITS) { + debug("RSA key bits %u outside allowed range %d..%d\n", + key->len, RSA_MIN_KEY_BITS, RSA_MAX_KEY_BITS); + return -EFAULT; + } + + key->len /= sizeof(uint32_t) * 8; + + key->modulus = xzalloc(RSA_MAX_KEY_BITS / 8); + key->rr = xzalloc(RSA_MAX_KEY_BITS / 8); + + rsa_convert_big_endian(key->modulus, modulus, key->len); + rsa_convert_big_endian(key->rr, rr, key->len); + + return 0; +} diff --git a/crypto/sha1.c b/crypto/sha1.c index a3de271..cbde4d2 100644 --- a/crypto/sha1.c +++ b/crypto/sha1.c @@ -287,6 +287,7 @@ .name = "sha1", .driver_name = "sha1-generic", .priority = 0, + .algo = HASH_ALGO_SHA1, }, .init = sha1_init, diff --git a/crypto/sha2.c b/crypto/sha2.c index 6ac5527..df566c8 100644 --- a/crypto/sha2.c +++ b/crypto/sha2.c @@ -327,6 +327,7 @@ .name = "sha224", .driver_name = "sha224-generic", .priority = 0, + .algo = HASH_ALGO_SHA224, }, .init = sha224_init, @@ -352,6 +353,7 @@ .name = "sha256", .driver_name = "sha256-generic", .priority = 0, + .algo = HASH_ALGO_SHA256, }, .init = sha256_init, diff --git a/crypto/sha4.c b/crypto/sha4.c index 187a91e..4ce37b7 100644 --- a/crypto/sha4.c +++ b/crypto/sha4.c @@ -246,6 +246,7 @@ .name = "sha384", .driver_name = "sha384-generic", .priority = 0, + .algo = HASH_ALGO_SHA384, }, .init = sha384_init, @@ -272,6 +273,7 @@ .name = "sha512", .driver_name = "sha512-generic", .priority = 0, + .algo = HASH_ALGO_SHA512, }, .init = sha512_init, diff --git a/include/asm-generic/errno.h b/include/asm-generic/errno.h index 6072f7b..7d99a95 100644 --- a/include/asm-generic/errno.h +++ b/include/asm-generic/errno.h @@ -126,6 +126,11 @@ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ +#define ECANCELED 125 /* Operation Canceled */ +#define ENOKEY 126 /* Required key not available */ +#define EKEYEXPIRED 127 /* Key has expired */ +#define EKEYREVOKED 128 /* Key has been revoked */ +#define EKEYREJECTED 129 /* Key was rejected by service */ /* Should never be seen by user programs */ #define ERESTARTSYS 512 diff --git a/include/boot.h b/include/boot.h index bdd5477..0198cc8 100644 --- a/include/boot.h +++ b/include/boot.h @@ -7,12 +7,18 @@ #include #include +enum bootm_verify { + BOOTM_VERIFY_NONE, + BOOTM_VERIFY_HASH, + BOOTM_VERIFY_SIGNATURE, +}; + struct bootm_data { const char *os_file; const char *initrd_file; const char *oftree_file; int verbose; - bool verify; + enum bootm_verify verify; bool force; bool dryrun; unsigned long initrd_address; @@ -28,7 +34,11 @@ /* if os is an uImage this will be provided */ struct uimage_handle *os; - int os_num; + + /* if os is a FIT image this will be provided */ + struct fit_handle *os_fit; + + char *os_part; /* otherwise only the filename will be provided */ char *os_file; @@ -49,7 +59,7 @@ /* if initrd is an uImage this will be provided */ struct uimage_handle *initrd; - int initrd_num; + char *initrd_part; /* otherwise only the filename will be provided */ char *initrd_file; @@ -57,13 +67,13 @@ unsigned long initrd_address; char *oftree_file; - int oftree_num; + char *oftree_part; struct device_node *of_root_node; struct fdt_header *oftree; struct resource *oftree_res; - int verify; + enum bootm_verify verify; int verbose; int force; int dryrun; @@ -109,9 +119,17 @@ } #endif +void bootm_data_init_defaults(struct bootm_data *data); + int bootm_load_os(struct image_data *data, unsigned long load_address); + +bool bootm_has_initrd(struct image_data *data); int bootm_load_initrd(struct image_data *data, unsigned long load_address); + int bootm_load_devicetree(struct image_data *data, unsigned long load_address); +int bootm_get_os_size(struct image_data *data); + +enum bootm_verify bootm_get_verify_mode(void); #define UIMAGE_SOME_ADDRESS (UIMAGE_INVALID_ADDRESS - 1) diff --git a/include/digest.h b/include/digest.h index 3a9d305..fe30cc2 100644 --- a/include/digest.h +++ b/include/digest.h @@ -23,12 +23,34 @@ struct digest; +enum hash_algo { + HASH_ALGO_MD4, + HASH_ALGO_MD5, + HASH_ALGO_SHA1, + HASH_ALGO_RIPE_MD_160, + HASH_ALGO_SHA224, + HASH_ALGO_SHA256, + HASH_ALGO_SHA384, + HASH_ALGO_SHA512, + HASH_ALGO_RIPE_MD_128, + HASH_ALGO_RIPE_MD_256, + HASH_ALGO_RIPE_MD_320, + HASH_ALGO_WP_256, + HASH_ALGO_WP_384, + HASH_ALGO_WP_512, + HASH_ALGO_TGR_128, + HASH_ALGO_TGR_160, + HASH_ALGO_TGR_192, + HASH_ALGO__LAST +}; + struct crypto_alg { char *name; char *driver_name; int priority; #define DIGEST_ALGO_NEED_KEY (1 << 0) unsigned int flags; + enum hash_algo algo; }; struct digest_algo { @@ -65,6 +87,7 @@ void digest_algo_prints(const char *prefix); struct digest *digest_alloc(const char *name); +struct digest *digest_alloc_by_algo(enum hash_algo); void digest_free(struct digest *d); int digest_file_window(struct digest *d, const char *filename, diff --git a/include/image-fit.h b/include/image-fit.h new file mode 100644 index 0000000..c9d6911 --- /dev/null +++ b/include/image-fit.h @@ -0,0 +1,45 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright (C) Jan Lübbe, 2014 + */ + +#ifndef __IMAGE_FIT_H__ +#define __IMAGE_FIT_H__ + +#include +#include + +struct fit_handle { + void *fit; + size_t size; + + bool verbose; + enum bootm_verify verify; + + struct device_node *root; + + const void *kernel; + unsigned long kernel_size; + const void *oftree; + unsigned long oftree_size; + const void *initrd; + unsigned long initrd_size; +}; + +struct fit_handle *fit_open(const char *filename, const char *config, bool verbose, + enum bootm_verify verify); +void fit_close(struct fit_handle *handle); + +#endif /* __IMAGE_FIT_H__ */ diff --git a/include/rsa.h b/include/rsa.h new file mode 100644 index 0000000..feb8c31 --- /dev/null +++ b/include/rsa.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _RSA_H +#define _RSA_H + +#include +#include + +/** + * struct rsa_public_key - holder for a public key + * + * An RSA public key consists of a modulus (typically called N), the inverse + * and R^2, where R is 2^(# key bits). + */ + +struct rsa_public_key { + uint len; /* len of modulus[] in number of uint32_t */ + uint32_t n0inv; /* -1 / modulus[0] mod 2^32 */ + uint32_t *modulus; /* modulus as little endian array */ + uint32_t *rr; /* R^2 as little endian array */ + uint64_t exponent; /* public exponent */ +}; + +/** + * rsa_verify() - Verify a signature against some data + * + * Verify a RSA PKCS1.5 signature against an expected hash. + * + * @info: Specifies key and FIT information + * @data: Pointer to the input data + * @data_len: Data length + * @sig: Signature + * @sig_len: Number of bytes in signature + * @return 0 if verified, -ve on error + */ +int rsa_verify(const struct rsa_public_key *key, const uint8_t *sig, + const uint32_t sig_len, const uint8_t *hash, + enum hash_algo algo); + +/* This is the maximum signature length that we support, in bits */ +#define RSA_MAX_SIG_BITS 4096 + +int rsa_of_read_key(struct device_node *node, struct rsa_public_key *key); + +#endif