Package: abootimg Version: 0.6-1 Severity: wishlist Tags: upstream patch abootimg currently fills header.id (unique image ID) with zero bytes in its output, with no way to do otherwise.
The Android mkbootimg tool uses the (raw binary) SHA-1 of various bits of the image, zero-padded at the end, as the image's unique ID. I am not aware of any devices that require this, but you never know. Rockchip devices like the RK3188 have their own variant image format in which the end of the header is also included in the SHA-1, resulting in a different ID. These devices' bootloader *does* verify the hash, so an ordinary mkbootimg or abootimg will not produce a bootable kernel. This modified mkbootimg produces that SHA1, for instance: https://github.com/naobsd/cm_system_core/tree/ics-rockchip-naobsd/mkbootimg I attach a rather untested patch. I'll test it on a Rockchip device soon, but with a suitable bootimg.cfg and "--id rockchip", it does produce the same image as the modified mkbootimg. The patch does not include the Debian packaging bits to add a gcrypt dependency. It would be fine to swap gcrypt for some other GPL-compatible crypto library (libnettle, libtomcrypt, etc.) or even a copy/pasted SHA1 implementation if desired, but gcrypt is Priority: standard so it seemed good enough :-) Regards, S -- System Information: Debian Release: jessie/sid APT prefers proposed-updates APT policy: (500, 'proposed-updates'), (500, 'unstable'), (500, 'testing'), (500, 'stable'), (1, 'experimental') Architecture: amd64 (x86_64) Foreign Architectures: i386 Kernel: Linux 3.10-3-amd64 (SMP w/4 CPU cores) Locale: LANG=en_GB.utf8, LC_CTYPE=en_GB.utf8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash
>From 39133030f13386e4ad9a40866f4ee530475e46b0 Mon Sep 17 00:00:00 2001 From: Simon McVittie <[email protected]> Date: Mon, 7 Oct 2013 19:45:32 +0100 Subject: [PATCH] Add support for various forms of the ID parameter The Android mkbootimg tool uses the (raw binary) SHA-1 of various bits of the image, zero-padded at the end, as the image's unique ID. I am not aware of any devices that require this, but you never know. Rockchip devices like the RK3188 have their own variant image format in which the end of the header is also included in the SHA-1, resulting in a different ID. These devices' bootloader *does* verify the hash, so an ordinary mkbootimg or abootimg will not produce a bootable kernel. This modified mkbootimg produces that SHA1, for instance: https://github.com/naobsd/cm_system_core/tree/ics-rockchip-naobsd/mkbootimg This does add a dependency, by requiring a SHA1 implementation. I used gcrypt here, since it's reasonably small and part of Debian 'standard' as of wheezy; any other SHA1 implementation with support for incremental updates would do. I also included support for arbitrary human-readable or binary image IDs, since that was rather easier. --- Makefile | 4 +- abootimg.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 148 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index ed39f9b..d0d89af 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ -CPPFLAGS=-DHAS_BLKID +CPPFLAGS=-DHAS_BLKID -DHAS_GCRYPT CFLAGS=-O3 -Wall -LDLIBS=-lblkid +LDLIBS=-lblkid -lgcrypt all: abootimg diff --git a/abootimg.c b/abootimg.c index a55c5c8..6a1cc7e 100644 --- a/abootimg.c +++ b/abootimg.c @@ -16,7 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - +#include <assert.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -51,6 +51,10 @@ #include <blkid/blkid.h> #endif +#ifdef HAS_GCRYPT +#include <gcrypt.h> +#endif + #include "version.h" #include "bootimg.h" @@ -64,6 +68,13 @@ enum command { create }; +enum id_mode { + id_mode_none, + id_mode_sha1, + id_mode_rockchip, + id_mode_string, + id_mode_hex +}; typedef struct { @@ -75,6 +86,8 @@ typedef struct char* kernel_fname; char* ramdisk_fname; char* second_fname; + enum id_mode id_mode; + char* id_arg; FILE* stream; @@ -152,7 +165,7 @@ void print_usage(void) " - ramdisk image (default name initrd.img)\n" " - second stage image (default name stage2.img)\n" "\n" - " abootimg -u <bootimg> [-c \"param=value\"] [-f <bootimg.cfg>] [-k <kernel>] [-r <ramdisk>] [-s <secondstage>]\n" + " abootimg -u <bootimg> [-c \"param=value\"] [-f <bootimg.cfg>] [-k <kernel>] [-r <ramdisk>] [-s <secondstage>] [--id sha1|rockchip|string:<...>|hex:<...>]\n" "\n" " update a current boot image with objects given in command line\n" " - header informations given in arguments (several can be provided)\n" @@ -161,9 +174,17 @@ void print_usage(void) " - ramdisk image\n" " - second stage image\n" "\n" + " The image ID will be set according to the id parameter:\n" + " - sha1: SHA1 of kernel image and size, ramdisk image and size,\n" + " second stage image and size (as written by Android mkbootimg)\n" + " - rockchip: SHA1 of the same things as sha1, plus the rest of\n" + " the header (required by the Rockchip boot loader)\n" + " - string:myimagename: any user-supplied text up to 32 bytes\n" + " - hex:0123456789abcdef...: any user-supplied binary up to 32 bytes\n" + "\n" " bootimg has to be valid Android Boot Image, or the update will abort.\n" "\n" - " abootimg --create <bootimg> [-c \"param=value\"] [-f <bootimg.cfg>] -k <kernel> -r <ramdisk> [-s <secondstage>]\n" + " abootimg --create <bootimg> [-c \"param=value\"] [-f <bootimg.cfg>] -k <kernel> -r <ramdisk> [-s <secondstage>] [--id <...>]\n" "\n" " create a new image from scratch.\n" " if the boot image file is a block device, sanity check will be performed to avoid overwriting a existing\n" @@ -266,6 +287,24 @@ enum command parse_args(int argc, char** argv, t_abootimg* img) return none; img->second_fname = argv[i]; } + else if (!strcmp(argv[i], "--id")) { + if (++i >= argc) + return none; + if (!strcmp(argv[i], "sha1")) + img->id_mode = id_mode_sha1; + else if (!strcmp(argv[i], "rockchip")) + img->id_mode = id_mode_rockchip; + else if (!strncmp(argv[i], "string:", strlen("string:"))) { + img->id_mode = id_mode_string; + img->id_arg = argv[i] + strlen("string:"); + } + else if (!strncmp(argv[i], "hex:", strlen("hex:"))) { + img->id_mode = id_mode_hex; + img->id_arg = argv[i] + strlen("hex:"); + } + else + return none; + } else return none; } @@ -628,7 +667,109 @@ void update_images(t_abootimg *img) abort_printf("%s: updated is too big for the Boot Image (%u vs %u bytes)\n", img->fname, total_size, img->size); } +#ifdef HAS_GCRYPT +void hash_object (gcry_md_hd_t hd, char *bytes, unsigned size) +{ + /* FIXME: this assumes a little-endian host platform with 4-byte int, + * matching the target platform */ + assert(sizeof(size) == 4); + assert(htole32(0x12345678) == 0x12345678); + gcry_md_write(hd, bytes, size); + gcry_md_write(hd, &size, sizeof(size)); +} +#endif + +void update_id(t_abootimg *img) +{ + switch (img->id_mode) { + case id_mode_none: + break; + + case id_mode_sha1: + case id_mode_rockchip: +#ifdef HAS_GCRYPT + { + gcry_error_t err; + gcry_md_hd_t hd; + size_t len = gcry_md_get_algo_dlen(GCRY_MD_SHA1); + + err = gcry_md_open(&hd, GCRY_MD_SHA1, 0); + + if (err) { + abort_printf("%s\n", gcry_strerror(err)); + } + + hash_object(hd, img->kernel, img->header.kernel_size); + hash_object(hd, img->ramdisk, img->header.ramdisk_size); + hash_object(hd, img->second, img->header.second_size); + + if (img->id_mode == id_mode_rockchip) { + /* Rockchip images hash the rest of the header, too */ + gcry_md_write(hd, &img->header.tags_addr, sizeof(img->header.tags_addr)); + gcry_md_write(hd, &img->header.page_size, sizeof(img->header.page_size)); + gcry_md_write(hd, &img->header.unused, sizeof(img->header.unused)); + gcry_md_write(hd, &img->header.name, sizeof(img->header.name)); + gcry_md_write(hd, &img->header.cmdline, sizeof(img->header.cmdline)); + } + + /* Truncate the SHA1 to fit */ + memcpy((char *)&img->header.id, gcry_md_read(hd, GCRY_MD_SHA1), len > sizeof(img->header.id) ? sizeof(img->header.id) : len); + gcry_md_close(hd); + } +#else + abort_printf("abootimg was compiled without SHA-1 support\n"); +#endif + break; + + case id_mode_string: + memset(img->header.id, 0, sizeof(img->header.id)); + strncpy((char *)&img->header.id, img->id_arg, sizeof(img->header.id)); + break; + + case id_mode_hex: + { + unsigned char buf[sizeof(img->header.id)] = { 0 }; + unsigned i; + + for (i = 0; i < sizeof(img->header.id); i++) { + const char *p = img->id_arg + (i * 2); + + if (p[0] == '\0') + break; + + if (p[1] == '\0') + abort_printf("odd number of hex digits given\n"); + + if (p[0] >= '0' && p[0] <= '9') + buf[i] = p[0] - '0'; + else if (p[0] >= 'A' && p[0] <= 'F') + buf[i] = (p[0] - 'A' + 10); + else if (p[0] >= 'a' && p[0] <= 'f') + buf[i] = (p[0] - 'a' + 10); + else + abort_printf("'%c' is not a hex digit\n", p[0]); + + buf[i] <<= 4; + + if (p[1] >= '0' && p[1] <= '9') + buf[i] |= (p[1] - '0'); + else if (p[1] >= 'A' && p[1] <= 'F') + buf[i] |= (p[1] - 'A' + 10); + else if (p[1] >= 'a' && p[1] <= 'f') + buf[i] |= (p[1] - 'a' + 10); + else + abort_printf("'%c' is not a hex digit\n", p[1]); + } + + memcpy(img->header.id, buf, sizeof(img->header.id)); + } + break; + + default: + abort_printf("unexpected --id mode\n"); + } +} void write_bootimg(t_abootimg* img) { @@ -928,6 +1069,7 @@ int main(int argc, char** argv) read_header(bootimg); update_header(bootimg); update_images(bootimg); + update_id(bootimg); write_bootimg(bootimg); break; @@ -940,6 +1082,7 @@ int main(int argc, char** argv) open_bootimg(bootimg, "w"); update_header(bootimg); update_images(bootimg); + update_id(bootimg); if (check_boot_img_header(bootimg)) abort_printf("%s: Sanity cheks failed", bootimg->fname); write_bootimg(bootimg); -- 1.8.4.rc3

