From d2a58f3aadf33e11bcbc4743cac65d4464447db8 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Sat, 29 Oct 2011 17:01:47 +0000 Subject: sbtools: move sb file production to its own file with a clean api, factor key reading even more git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30851 a1c6a512-1295-4272-9138-f99709370657 --- utils/sbtools/Makefile | 4 +- utils/sbtools/elftosb.c | 485 +----------------------------------------------- utils/sbtools/misc.c | 127 ++++++++----- utils/sbtools/misc.h | 7 +- utils/sbtools/sb.c | 426 ++++++++++++++++++++++++++++++++++++++++++ utils/sbtools/sb.h | 53 ++++++ utils/sbtools/sbtoelf.c | 8 +- 7 files changed, 574 insertions(+), 536 deletions(-) create mode 100644 utils/sbtools/sb.c (limited to 'utils') diff --git a/utils/sbtools/Makefile b/utils/sbtools/Makefile index 15d3adb8a1..e4db3bf335 100644 --- a/utils/sbtools/Makefile +++ b/utils/sbtools/Makefile @@ -9,10 +9,10 @@ all: elftosb sbtoelf %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< -sbtoelf: sbtoelf.o crc.o crypto.o aes128.o sha1.o elf.o misc.o +sbtoelf: sbtoelf.o crc.o crypto.o aes128.o sha1.o elf.o misc.o sb.o $(LD) $(LDFLAGS) -o $@ $^ -elftosb: elftosb.o crc.o crypto.o aes128.o sha1.o elf.o dbparser.o misc.o +elftosb: elftosb.o crc.o crypto.o aes128.o sha1.o elf.o dbparser.o misc.o sb.o $(LD) $(LDFLAGS) -o $@ $^ clean: diff --git a/utils/sbtools/elftosb.c b/utils/sbtools/elftosb.c index 5e65ba3261..3e217a8979 100644 --- a/utils/sbtools/elftosb.c +++ b/utils/sbtools/elftosb.c @@ -35,6 +35,7 @@ #include "sb.h" #include "dbparser.h" #include "misc.h" +#include "sb.h" char **g_extern; int g_extern_count; @@ -51,47 +52,6 @@ int g_extern_count; * command file to sb conversion */ -#define SB_INST_DATA 0xff - -struct sb_inst_t -{ - uint8_t inst; /* SB_INST_* */ - uint32_t size; - // - void *data; - uint32_t pattern; - uint32_t addr; - // - uint32_t argument; // for call and jump - /* for production use */ - uint32_t padding_size; - uint8_t *padding; -}; - -struct sb_section_t -{ - uint32_t identifier; - bool is_data; - bool is_cleartext; - uint32_t alignment; - // data sections are handled as a single SB_INST_DATA virtual instruction - int nr_insts; - struct sb_inst_t *insts; - /* for production use */ - uint32_t file_offset; /* in blocks */ - uint32_t sec_size; /* in blocks */ -}; - -struct sb_file_t -{ - int nr_sections; - struct sb_section_t *sections; - struct sb_version_t product_ver; - struct sb_version_t component_ver; - /* for production use */ - uint32_t image_size; /* in blocks */ -}; - static bool elf_read(void *user, uint32_t addr, void *buf, size_t count) { if(fseek((FILE *)user, addr, SEEK_SET) == -1) @@ -355,443 +315,6 @@ static struct sb_file_t *apply_cmd_file(struct cmd_file_t *cmd_file) return sb; } -/** - * SB file production - */ - -/* helper function to augment an array, free old array */ -void *augment_array(void *arr, size_t elem_sz, size_t cnt, void *aug, size_t aug_cnt) -{ - void *p = xmalloc(elem_sz * (cnt + aug_cnt)); - memcpy(p, arr, elem_sz * cnt); - memcpy(p + elem_sz * cnt, aug, elem_sz * aug_cnt); - free(arr); - return p; -} - -static void fill_gaps(struct sb_file_t *sb) -{ - for(int i = 0; i < sb->nr_sections; i++) - { - struct sb_section_t *sec = &sb->sections[i]; - for(int j = 0; j < sec->nr_insts; j++) - { - struct sb_inst_t *inst = &sec->insts[j]; - if(inst->inst != SB_INST_LOAD) - continue; - inst->padding_size = ROUND_UP(inst->size, BLOCK_SIZE) - inst->size; - /* emulate elftosb2 behaviour: generate 15 bytes (that's a safe maximum) */ - inst->padding = xmalloc(15); - generate_random_data(inst->padding, 15); - } - } -} - -static void compute_sb_offsets(struct sb_file_t *sb) -{ - sb->image_size = 0; - /* sb header */ - sb->image_size += sizeof(struct sb_header_t) / BLOCK_SIZE; - /* sections headers */ - sb->image_size += sb->nr_sections * sizeof(struct sb_section_header_t) / BLOCK_SIZE; - /* key dictionary */ - sb->image_size += g_nr_keys * sizeof(struct sb_key_dictionary_entry_t) / BLOCK_SIZE; - /* sections */ - for(int i = 0; i < sb->nr_sections; i++) - { - /* each section has a preliminary TAG command */ - sb->image_size += sizeof(struct sb_instruction_tag_t) / BLOCK_SIZE; - /* we might need to pad the section so compute next alignment */ - uint32_t alignment = BLOCK_SIZE; - if((i + 1) < sb->nr_sections) - alignment = sb->sections[i + 1].alignment; - alignment /= BLOCK_SIZE; /* alignment in block sizes */ - - struct sb_section_t *sec = &sb->sections[i]; - - if(g_debug) - { - printf("%s section 0x%08x", sec->is_data ? "Data" : "Boot", - sec->identifier); - if(sec->is_cleartext) - printf(" (cleartext)"); - printf("\n"); - } - - sec->file_offset = sb->image_size; - for(int j = 0; j < sec->nr_insts; j++) - { - struct sb_inst_t *inst = &sec->insts[j]; - if(inst->inst == SB_INST_CALL || inst->inst == SB_INST_JUMP) - { - if(g_debug) - printf(" %s | addr=0x%08x | arg=0x%08x\n", - inst->inst == SB_INST_CALL ? "CALL" : "JUMP", inst->addr, inst->argument); - sb->image_size += sizeof(struct sb_instruction_call_t) / BLOCK_SIZE; - sec->sec_size += sizeof(struct sb_instruction_call_t) / BLOCK_SIZE; - } - else if(inst->inst == SB_INST_FILL) - { - if(g_debug) - printf(" FILL | addr=0x%08x | len=0x%08x | pattern=0x%08x\n", - inst->addr, inst->size, inst->pattern); - sb->image_size += sizeof(struct sb_instruction_fill_t) / BLOCK_SIZE; - sec->sec_size += sizeof(struct sb_instruction_fill_t) / BLOCK_SIZE; - } - else if(inst->inst == SB_INST_LOAD) - { - if(g_debug) - printf(" LOAD | addr=0x%08x | len=0x%08x\n", inst->addr, inst->size); - /* load header */ - sb->image_size += sizeof(struct sb_instruction_load_t) / BLOCK_SIZE; - sec->sec_size += sizeof(struct sb_instruction_load_t) / BLOCK_SIZE; - /* data + alignment */ - sb->image_size += (inst->size + inst->padding_size) / BLOCK_SIZE; - sec->sec_size += (inst->size + inst->padding_size) / BLOCK_SIZE; - } - else if(inst->inst == SB_INST_MODE) - { - if(g_debug) - printf(" MODE | mod=0x%08x", inst->addr); - sb->image_size += sizeof(struct sb_instruction_mode_t) / BLOCK_SIZE; - sec->sec_size += sizeof(struct sb_instruction_mode_t) / BLOCK_SIZE; - } - else if(inst->inst == SB_INST_DATA) - { - if(g_debug) - printf(" DATA | size=0x%08x\n", inst->size); - sb->image_size += ROUND_UP(inst->size, BLOCK_SIZE) / BLOCK_SIZE; - sec->sec_size += ROUND_UP(inst->size, BLOCK_SIZE) / BLOCK_SIZE; - } - else - bug("die on inst %d\n", inst->inst); - } - /* we need to make sure next section starts on the right alignment. - * Since each section starts with a boot tag, we thus need to ensure - * that this sections ends at adress X such that X+BLOCK_SIZE is - * a multiple of the alignment. - * For data sections, we just add random data, otherwise we add nops */ - uint32_t missing_sz = alignment - ((sb->image_size + 1) % alignment); - if(missing_sz != alignment) - { - struct sb_inst_t *aug_insts; - int nr_aug_insts = 0; - - if(sb->sections[i].is_data) - { - nr_aug_insts = 1; - aug_insts = malloc(sizeof(struct sb_inst_t)); - memset(aug_insts, 0, sizeof(struct sb_inst_t)); - aug_insts[0].inst = SB_INST_DATA; - aug_insts[0].size = missing_sz * BLOCK_SIZE; - aug_insts[0].data = xmalloc(missing_sz * BLOCK_SIZE); - generate_random_data(aug_insts[0].data, missing_sz * BLOCK_SIZE); - if(g_debug) - printf(" DATA | size=0x%08x\n", aug_insts[0].size); - } - else - { - nr_aug_insts = missing_sz; - aug_insts = malloc(sizeof(struct sb_inst_t) * nr_aug_insts); - memset(aug_insts, 0, sizeof(struct sb_inst_t) * nr_aug_insts); - for(int j = 0; j < nr_aug_insts; j++) - { - aug_insts[j].inst = SB_INST_NOP; - if(g_debug) - printf(" NOOP\n"); - } - } - - sb->sections[i].insts = augment_array(sb->sections[i].insts, sizeof(struct sb_inst_t), - sb->sections[i].nr_insts, aug_insts, nr_aug_insts); - sb->sections[i].nr_insts += nr_aug_insts; - - /* augment image and section size */ - sb->image_size += missing_sz; - sec->sec_size += missing_sz; - } - } - /* final signature */ - sb->image_size += 2; -} - -static uint64_t generate_timestamp() -{ - struct tm tm_base = {0, 0, 0, 1, 0, 100, 0, 0, 1, 0, NULL}; /* 2000/1/1 0:00:00 */ - time_t t = time(NULL) - mktime(&tm_base); - return (uint64_t)t * 1000000L; -} - -static uint16_t swap16(uint16_t t) -{ - return (t << 8) | (t >> 8); -} - -static void fix_version(struct sb_version_t *ver) -{ - ver->major = swap16(ver->major); - ver->minor = swap16(ver->minor); - ver->revision = swap16(ver->revision); -} - -static void produce_sb_header(struct sb_file_t *sb, struct sb_header_t *sb_hdr) -{ - struct sha_1_params_t sha_1_params; - - sb_hdr->signature[0] = 'S'; - sb_hdr->signature[1] = 'T'; - sb_hdr->signature[2] = 'M'; - sb_hdr->signature[3] = 'P'; - sb_hdr->major_ver = IMAGE_MAJOR_VERSION; - sb_hdr->minor_ver = IMAGE_MINOR_VERSION; - sb_hdr->flags = 0; - sb_hdr->image_size = sb->image_size; - sb_hdr->header_size = sizeof(struct sb_header_t) / BLOCK_SIZE; - sb_hdr->first_boot_sec_id = sb->sections[0].identifier; - sb_hdr->nr_keys = g_nr_keys; - sb_hdr->nr_sections = sb->nr_sections; - sb_hdr->sec_hdr_size = sizeof(struct sb_section_header_t) / BLOCK_SIZE; - sb_hdr->key_dict_off = sb_hdr->header_size + - sb_hdr->sec_hdr_size * sb_hdr->nr_sections; - sb_hdr->first_boot_tag_off = sb_hdr->key_dict_off + - sizeof(struct sb_key_dictionary_entry_t) * sb_hdr->nr_keys / BLOCK_SIZE; - generate_random_data(sb_hdr->rand_pad0, sizeof(sb_hdr->rand_pad0)); - generate_random_data(sb_hdr->rand_pad1, sizeof(sb_hdr->rand_pad1)); - sb_hdr->timestamp = generate_timestamp(); - sb_hdr->product_ver = sb->product_ver; - fix_version(&sb_hdr->product_ver); - sb_hdr->component_ver = sb->component_ver; - fix_version(&sb_hdr->component_ver); - sb_hdr->drive_tag = 0; - - sha_1_init(&sha_1_params); - sha_1_update(&sha_1_params, &sb_hdr->signature[0], - sizeof(struct sb_header_t) - sizeof(sb_hdr->sha1_header)); - sha_1_finish(&sha_1_params); - sha_1_output(&sha_1_params, sb_hdr->sha1_header); -} - -static void produce_sb_section_header(struct sb_section_t *sec, - struct sb_section_header_t *sec_hdr) -{ - sec_hdr->identifier = sec->identifier; - sec_hdr->offset = sec->file_offset; - sec_hdr->size = sec->sec_size; - sec_hdr->flags = (sec->is_data ? 0 : SECTION_BOOTABLE) - | (sec->is_cleartext ? SECTION_CLEARTEXT : 0); -} - -static uint8_t instruction_checksum(struct sb_instruction_header_t *hdr) -{ - uint8_t sum = 90; - byte *ptr = (byte *)hdr; - for(int i = 1; i < 16; i++) - sum += ptr[i]; - return sum; -} - -static void produce_section_tag_cmd(struct sb_section_t *sec, - struct sb_instruction_tag_t *tag, bool is_last) -{ - tag->hdr.opcode = SB_INST_TAG; - tag->hdr.flags = is_last ? SB_INST_LAST_TAG : 0; - tag->identifier = sec->identifier; - tag->len = sec->sec_size; - tag->flags = (sec->is_data ? 0 : SECTION_BOOTABLE) - | (sec->is_cleartext ? SECTION_CLEARTEXT : 0); - tag->hdr.checksum = instruction_checksum(&tag->hdr); -} - -void produce_sb_instruction(struct sb_inst_t *inst, - struct sb_instruction_common_t *cmd) -{ - memset(cmd, 0, sizeof(struct sb_instruction_common_t)); - cmd->hdr.opcode = inst->inst; - switch(inst->inst) - { - case SB_INST_CALL: - case SB_INST_JUMP: - cmd->addr = inst->addr; - cmd->data = inst->argument; - break; - case SB_INST_FILL: - cmd->addr = inst->addr; - cmd->len = inst->size; - cmd->data = inst->pattern; - break; - case SB_INST_LOAD: - cmd->addr = inst->addr; - cmd->len = inst->size; - cmd->data = crc_continue(crc(inst->data, inst->size), - inst->padding, inst->padding_size); - break; - case SB_INST_MODE: - cmd->data = inst->addr; - break; - case SB_INST_NOP: - break; - default: - bug("die\n"); - } - cmd->hdr.checksum = instruction_checksum(&cmd->hdr); -} - -static void produce_sb_file(struct sb_file_t *sb, const char *filename) -{ - FILE *fd = fopen(filename, "wb"); - if(fd == NULL) - bugp("cannot open output file"); - - struct crypto_key_t real_key; - real_key.method = CRYPTO_KEY; - byte crypto_iv[16]; - byte (*cbc_macs)[16] = xmalloc(16 * g_nr_keys); - /* init CBC-MACs */ - for(int i = 0; i < g_nr_keys; i++) - memset(cbc_macs[i], 0, 16); - - fill_gaps(sb); - compute_sb_offsets(sb); - - generate_random_data(real_key.u.key, 16); - - /* global SHA-1 */ - struct sha_1_params_t file_sha1; - sha_1_init(&file_sha1); - /* produce and write header */ - struct sb_header_t sb_hdr; - produce_sb_header(sb, &sb_hdr); - sha_1_update(&file_sha1, (byte *)&sb_hdr, sizeof(sb_hdr)); - fwrite(&sb_hdr, 1, sizeof(sb_hdr), fd); - - memcpy(crypto_iv, &sb_hdr, 16); - - /* update CBC-MACs */ - for(int i = 0; i < g_nr_keys; i++) - crypto_cbc((byte *)&sb_hdr, NULL, sizeof(sb_hdr) / BLOCK_SIZE, &g_key_array[i], - cbc_macs[i], &cbc_macs[i], 1); - - /* produce and write section headers */ - for(int i = 0; i < sb_hdr.nr_sections; i++) - { - struct sb_section_header_t sb_sec_hdr; - produce_sb_section_header(&sb->sections[i], &sb_sec_hdr); - sha_1_update(&file_sha1, (byte *)&sb_sec_hdr, sizeof(sb_sec_hdr)); - fwrite(&sb_sec_hdr, 1, sizeof(sb_sec_hdr), fd); - /* update CBC-MACs */ - for(int j = 0; j < g_nr_keys; j++) - crypto_cbc((byte *)&sb_sec_hdr, NULL, sizeof(sb_sec_hdr) / BLOCK_SIZE, - &g_key_array[j], cbc_macs[j], &cbc_macs[j], 1); - } - /* produce key dictionary */ - for(int i = 0; i < g_nr_keys; i++) - { - struct sb_key_dictionary_entry_t entry; - memcpy(entry.hdr_cbc_mac, cbc_macs[i], 16); - crypto_cbc(real_key.u.key, entry.key, 1, &g_key_array[i], - crypto_iv, NULL, 1); - - fwrite(&entry, 1, sizeof(entry), fd); - sha_1_update(&file_sha1, (byte *)&entry, sizeof(entry)); - } - - /* HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK */ - /* Image crafting, don't use it unless you understand what you do */ - if(strlen(s_getenv("SB_OVERRIDE_REAL_KEY")) != 0) - { - const char *key = s_getenv("SB_OVERRIDE_REAL_KEY"); - if(strlen(key) != 32) - bugp("Cannot override real key: invalid key length\n"); - for(int i = 0; i < 16; i++) - { - byte a, b; - if(convxdigit(key[2 * i], &a) || convxdigit(key[2 * i + 1], &b)) - bugp("Cannot override real key: key should be a 128-bit key written in hexadecimal\n"); - real_key.u.key[i] = (a << 4) | b; - } - } - if(strlen(s_getenv("SB_OVERRIDE_IV")) != 0) - { - const char *iv = s_getenv("SB_OVERRIDE_IV"); - if(strlen(iv) != 32) - bugp("Cannot override iv: invalid key length\n"); - for(int i = 0; i < 16; i++) - { - byte a, b; - if(convxdigit(iv[2 * i], &a) || convxdigit(iv[2 * i + 1], &b)) - bugp("Cannot override iv: key should be a 128-bit key written in hexadecimal\n"); - crypto_iv[i] = (a << 4) | b; - } - - } - /* KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH */ - if(g_debug) - { - printf("Real key: "); - for(int j = 0; j < 16; j++) - printf("%02x", real_key.u.key[j]); - printf("\n"); - printf("IV : "); - for(int j = 0; j < 16; j++) - printf("%02x", crypto_iv[j]); - printf("\n"); - } - /* produce sections data */ - for(int i = 0; i< sb_hdr.nr_sections; i++) - { - /* produce tag command */ - struct sb_instruction_tag_t tag_cmd; - produce_section_tag_cmd(&sb->sections[i], &tag_cmd, (i + 1) == sb_hdr.nr_sections); - if(g_nr_keys > 0) - crypto_cbc((byte *)&tag_cmd, (byte *)&tag_cmd, sizeof(tag_cmd) / BLOCK_SIZE, - &real_key, crypto_iv, NULL, 1); - sha_1_update(&file_sha1, (byte *)&tag_cmd, sizeof(tag_cmd)); - fwrite(&tag_cmd, 1, sizeof(tag_cmd), fd); - /* produce other commands */ - byte cur_cbc_mac[16]; - memcpy(cur_cbc_mac, crypto_iv, 16); - for(int j = 0; j < sb->sections[i].nr_insts; j++) - { - struct sb_inst_t *inst = &sb->sections[i].insts[j]; - /* command */ - if(inst->inst != SB_INST_DATA) - { - struct sb_instruction_common_t cmd; - produce_sb_instruction(inst, &cmd); - if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) - crypto_cbc((byte *)&cmd, (byte *)&cmd, sizeof(cmd) / BLOCK_SIZE, - &real_key, cur_cbc_mac, &cur_cbc_mac, 1); - sha_1_update(&file_sha1, (byte *)&cmd, sizeof(cmd)); - fwrite(&cmd, 1, sizeof(cmd), fd); - } - /* data */ - if(inst->inst == SB_INST_LOAD || inst->inst == SB_INST_DATA) - { - uint32_t sz = inst->size + inst->padding_size; - byte *data = xmalloc(sz); - memcpy(data, inst->data, inst->size); - memcpy(data + inst->size, inst->padding, inst->padding_size); - if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) - crypto_cbc(data, data, sz / BLOCK_SIZE, - &real_key, cur_cbc_mac, &cur_cbc_mac, 1); - sha_1_update(&file_sha1, data, sz); - fwrite(data, 1, sz, fd); - free(data); - } - } - } - /* write file SHA-1 */ - byte final_sig[32]; - sha_1_finish(&file_sha1); - sha_1_output(&file_sha1, final_sig); - generate_random_data(final_sig + 20, 12); - if(g_nr_keys > 0) - crypto_cbc(final_sig, final_sig, 2, &real_key, crypto_iv, NULL, 1); - fwrite(final_sig, 1, 32, fd); - - fclose(fd); -} - void usage(void) { printf("Usage: elftosb [options | file]...\n"); @@ -848,9 +371,7 @@ int main(int argc, char **argv) break; case 'k': { - int kac; - key_array_t ka = read_keys(optarg, &kac); - add_keys(ka, kac); + add_keys_from_file(optarg); break; } case 'z': @@ -922,7 +443,7 @@ int main(int argc, char **argv) struct cmd_file_t *cmd_file = db_parse_file(cmd_filename); struct sb_file_t *sb_file = apply_cmd_file(cmd_file); - produce_sb_file(sb_file, output_filename); + sb_produce_file(sb_file, output_filename); return 0; } diff --git a/utils/sbtools/misc.c b/utils/sbtools/misc.c index 09a8919aef..39934951ae 100644 --- a/utils/sbtools/misc.c +++ b/utils/sbtools/misc.c @@ -29,13 +29,6 @@ bool g_debug = false; /** * Misc */ - -char *s_getenv(const char *name) -{ - char *s = getenv(name); - return s ? s : ""; -} - void generate_random_data(void *buf, size_t sz) { FILE *rand_fd = fopen("/dev/urandom", "rb"); @@ -74,12 +67,73 @@ int convxdigit(char digit, byte *val) return 1; } +/* helper function to augment an array, free old array */ +void *augment_array(void *arr, size_t elem_sz, size_t cnt, void *aug, size_t aug_cnt) +{ + void *p = xmalloc(elem_sz * (cnt + aug_cnt)); + memcpy(p, arr, elem_sz * cnt); + memcpy(p + elem_sz * cnt, aug, elem_sz * aug_cnt); + free(arr); + return p; +} + /** * Key file parsing */ int g_nr_keys; key_array_t g_key_array; +bool parse_key(char **pstr, struct crypto_key_t *key) +{ + char *str = *pstr; + /* ignore spaces */ + while(isspace(*str)) + str++; + /* CRYPTO_KEY: 32 hex characters + * CRYPTO_USBOTP: usbotp(vid:pid) where vid and pid are hex numbers */ + if(isxdigit(str[0])) + { + if(strlen(str) < 32) + return false; + for(int j = 0; j < 16; j++) + { + byte a, b; + if(convxdigit(str[2 * j], &a) || convxdigit(str[2 * j + 1], &b)) + return false; + key->u.key[j] = (a << 4) | b; + } + /* skip key */ + *pstr = str + 32; + key->method = CRYPTO_KEY; + return true; + } + else + { + const char *prefix = "usbotp("; + if(strlen(str) < strlen(prefix)) + return false; + if(strncmp(str, prefix, strlen(prefix)) != 0) + return false; + str += strlen(prefix); + /* vid */ + long vid = strtol(str, &str, 16); + if(vid < 0 || vid > 0xffff) + return false; + if(*str++ != ':') + return false; + /* pid */ + long pid = strtol(str, &str, 16); + if(pid < 0 || pid > 0xffff) + return false; + if(*str++ != ')') + return false; + *pstr = str; + key->method = CRYPTO_USBOTP; + key->u.vid_pid = vid << 16 | pid; + return true; + } +} + void add_keys(key_array_t ka, int kac) { key_array_t new_ka = xmalloc((g_nr_keys + kac) * sizeof(struct crypto_key_t)); @@ -90,61 +144,46 @@ void add_keys(key_array_t ka, int kac) g_nr_keys += kac; } -key_array_t read_keys(const char *key_file, int *num_keys) +void add_keys_from_file(const char *key_file) { int size; FILE *fd = fopen(key_file, "r"); if(fd == NULL) - bugp("opening key file failed"); + bug("opening key file failed"); fseek(fd, 0, SEEK_END); size = ftell(fd); fseek(fd, 0, SEEK_SET); - char *buf = xmalloc(size); - if(fread(buf, size, 1, fd) != (size_t)size) - bugp("reading key file"); + char *buf = xmalloc(size + 1); + if(fread(buf, 1, size, fd) != (size_t)size) + bug("reading key file"); + buf[size] = 0; fclose(fd); if(g_debug) printf("Parsing key file '%s'...\n", key_file); - *num_keys = size ? 1 : 0; - char *ptr = buf; - /* allow trailing newline at the end (but no space after it) */ - while(ptr != buf + size && (ptr + 1) != buf + size) - { - if(*ptr++ == '\n') - (*num_keys)++; - } - - key_array_t keys = xmalloc(sizeof(struct crypto_key_t) * *num_keys); - int pos = 0; - for(int i = 0; i < *num_keys; i++) + char *p = buf; + while(1) { - /* skip ws */ - while(pos < size && isspace(buf[pos])) - pos++; - /* enough space ? */ - if((pos + 32) > size) - bugp("invalid key file"); - keys[i].method = CRYPTO_KEY; - for(int j = 0; j < 16; j++) - { - byte a, b; - if(convxdigit(buf[pos + 2 * j], &a) || convxdigit(buf[pos + 2 * j + 1], &b)) - bugp(" invalid key, it should be a 128-bit key written in hexadecimal\n"); - keys[i].u.key[j] = (a << 4) | b; - } + struct crypto_key_t k; + /* parse key */ + if(!parse_key(&p, &k)) + bug("invalid key file"); if(g_debug) { printf("Add key: "); - for(int j = 0; j < 16; j++) - printf("%02x", keys[i].u.key[j]); - printf("\n"); + print_key(&k, true); } - pos += 32; + add_keys(&k, 1); + /* request at least one space character before next key, or end of file */ + if(*p != 0 && !isspace(*p)) + bug("invalid key file"); + /* skip whitespace */ + while(isspace(*p)) + p++; + if(*p == 0) + break; } free(buf); - - return keys; } void print_hex(byte *data, int len, bool newline) diff --git a/utils/sbtools/misc.h b/utils/sbtools/misc.h index 545285eafc..cc0a3fb5ea 100644 --- a/utils/sbtools/misc.h +++ b/utils/sbtools/misc.h @@ -30,19 +30,22 @@ #define bug(...) do { fprintf(stderr,"["__FILE__":"STR(__LINE__)"]ERROR: "__VA_ARGS__); exit(1); } while(0) #define bugp(a) do { perror("ERROR: "a); exit(1); } while(0) +#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) + extern bool g_debug; typedef struct crypto_key_t *key_array_t; int g_nr_keys; key_array_t g_key_array; -char *s_getenv(const char *name); +void *augment_array(void *arr, size_t elem_sz, size_t cnt, void *aug, size_t aug_cnt); void generate_random_data(void *buf, size_t sz); void *xmalloc(size_t s); int convxdigit(char digit, byte *val); void print_hex(byte *data, int len, bool newline); void add_keys(key_array_t ka, int kac); -key_array_t read_keys(const char *key_file, int *num_keys); +bool parse_key(char **str, struct crypto_key_t *key); +void add_keys_from_file(const char *key_file); void print_key(struct crypto_key_t *key, bool newline); #endif /* __MISC_H__ */ diff --git a/utils/sbtools/sb.c b/utils/sbtools/sb.c new file mode 100644 index 0000000000..3921710a2d --- /dev/null +++ b/utils/sbtools/sb.c @@ -0,0 +1,426 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 Amaury Pouly + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include "misc.h" +#include "crypto.h" +#include "sb.h" + +static void fill_gaps(struct sb_file_t *sb) +{ + for(int i = 0; i < sb->nr_sections; i++) + { + struct sb_section_t *sec = &sb->sections[i]; + for(int j = 0; j < sec->nr_insts; j++) + { + struct sb_inst_t *inst = &sec->insts[j]; + if(inst->inst != SB_INST_LOAD) + continue; + inst->padding_size = ROUND_UP(inst->size, BLOCK_SIZE) - inst->size; + /* emulate elftosb2 behaviour: generate 15 bytes (that's a safe maximum) */ + inst->padding = xmalloc(15); + generate_random_data(inst->padding, 15); + } + } +} + +static void compute_sb_offsets(struct sb_file_t *sb) +{ + sb->image_size = 0; + /* sb header */ + sb->image_size += sizeof(struct sb_header_t) / BLOCK_SIZE; + /* sections headers */ + sb->image_size += sb->nr_sections * sizeof(struct sb_section_header_t) / BLOCK_SIZE; + /* key dictionary */ + sb->image_size += g_nr_keys * sizeof(struct sb_key_dictionary_entry_t) / BLOCK_SIZE; + /* sections */ + for(int i = 0; i < sb->nr_sections; i++) + { + /* each section has a preliminary TAG command */ + sb->image_size += sizeof(struct sb_instruction_tag_t) / BLOCK_SIZE; + /* we might need to pad the section so compute next alignment */ + uint32_t alignment = BLOCK_SIZE; + if((i + 1) < sb->nr_sections) + alignment = sb->sections[i + 1].alignment; + alignment /= BLOCK_SIZE; /* alignment in block sizes */ + + struct sb_section_t *sec = &sb->sections[i]; + + if(g_debug) + { + printf("%s section 0x%08x", sec->is_data ? "Data" : "Boot", + sec->identifier); + if(sec->is_cleartext) + printf(" (cleartext)"); + printf("\n"); + } + + sec->file_offset = sb->image_size; + for(int j = 0; j < sec->nr_insts; j++) + { + struct sb_inst_t *inst = &sec->insts[j]; + if(inst->inst == SB_INST_CALL || inst->inst == SB_INST_JUMP) + { + if(g_debug) + printf(" %s | addr=0x%08x | arg=0x%08x\n", + inst->inst == SB_INST_CALL ? "CALL" : "JUMP", inst->addr, inst->argument); + sb->image_size += sizeof(struct sb_instruction_call_t) / BLOCK_SIZE; + sec->sec_size += sizeof(struct sb_instruction_call_t) / BLOCK_SIZE; + } + else if(inst->inst == SB_INST_FILL) + { + if(g_debug) + printf(" FILL | addr=0x%08x | len=0x%08x | pattern=0x%08x\n", + inst->addr, inst->size, inst->pattern); + sb->image_size += sizeof(struct sb_instruction_fill_t) / BLOCK_SIZE; + sec->sec_size += sizeof(struct sb_instruction_fill_t) / BLOCK_SIZE; + } + else if(inst->inst == SB_INST_LOAD) + { + if(g_debug) + printf(" LOAD | addr=0x%08x | len=0x%08x\n", inst->addr, inst->size); + /* load header */ + sb->image_size += sizeof(struct sb_instruction_load_t) / BLOCK_SIZE; + sec->sec_size += sizeof(struct sb_instruction_load_t) / BLOCK_SIZE; + /* data + alignment */ + sb->image_size += (inst->size + inst->padding_size) / BLOCK_SIZE; + sec->sec_size += (inst->size + inst->padding_size) / BLOCK_SIZE; + } + else if(inst->inst == SB_INST_MODE) + { + if(g_debug) + printf(" MODE | mod=0x%08x", inst->addr); + sb->image_size += sizeof(struct sb_instruction_mode_t) / BLOCK_SIZE; + sec->sec_size += sizeof(struct sb_instruction_mode_t) / BLOCK_SIZE; + } + else if(inst->inst == SB_INST_DATA) + { + if(g_debug) + printf(" DATA | size=0x%08x\n", inst->size); + sb->image_size += ROUND_UP(inst->size, BLOCK_SIZE) / BLOCK_SIZE; + sec->sec_size += ROUND_UP(inst->size, BLOCK_SIZE) / BLOCK_SIZE; + } + else + bug("die on inst %d\n", inst->inst); + } + /* we need to make sure next section starts on the right alignment. + * Since each section starts with a boot tag, we thus need to ensure + * that this sections ends at adress X such that X+BLOCK_SIZE is + * a multiple of the alignment. + * For data sections, we just add random data, otherwise we add nops */ + uint32_t missing_sz = alignment - ((sb->image_size + 1) % alignment); + if(missing_sz != alignment) + { + struct sb_inst_t *aug_insts; + int nr_aug_insts = 0; + + if(sb->sections[i].is_data) + { + nr_aug_insts = 1; + aug_insts = malloc(sizeof(struct sb_inst_t)); + memset(aug_insts, 0, sizeof(struct sb_inst_t)); + aug_insts[0].inst = SB_INST_DATA; + aug_insts[0].size = missing_sz * BLOCK_SIZE; + aug_insts[0].data = xmalloc(missing_sz * BLOCK_SIZE); + generate_random_data(aug_insts[0].data, missing_sz * BLOCK_SIZE); + if(g_debug) + printf(" DATA | size=0x%08x\n", aug_insts[0].size); + } + else + { + nr_aug_insts = missing_sz; + aug_insts = malloc(sizeof(struct sb_inst_t) * nr_aug_insts); + memset(aug_insts, 0, sizeof(struct sb_inst_t) * nr_aug_insts); + for(int j = 0; j < nr_aug_insts; j++) + { + aug_insts[j].inst = SB_INST_NOP; + if(g_debug) + printf(" NOOP\n"); + } + } + + sb->sections[i].insts = augment_array(sb->sections[i].insts, sizeof(struct sb_inst_t), + sb->sections[i].nr_insts, aug_insts, nr_aug_insts); + sb->sections[i].nr_insts += nr_aug_insts; + + /* augment image and section size */ + sb->image_size += missing_sz; + sec->sec_size += missing_sz; + } + } + /* final signature */ + sb->image_size += 2; +} + +static uint64_t generate_timestamp() +{ + struct tm tm_base = {0, 0, 0, 1, 0, 100, 0, 0, 1, 0, NULL}; /* 2000/1/1 0:00:00 */ + time_t t = time(NULL) - mktime(&tm_base); + return (uint64_t)t * 1000000L; +} + +static uint16_t swap16(uint16_t t) +{ + return (t << 8) | (t >> 8); +} + +static void fix_version(struct sb_version_t *ver) +{ + ver->major = swap16(ver->major); + ver->minor = swap16(ver->minor); + ver->revision = swap16(ver->revision); +} + +static void produce_sb_header(struct sb_file_t *sb, struct sb_header_t *sb_hdr) +{ + struct sha_1_params_t sha_1_params; + + sb_hdr->signature[0] = 'S'; + sb_hdr->signature[1] = 'T'; + sb_hdr->signature[2] = 'M'; + sb_hdr->signature[3] = 'P'; + sb_hdr->major_ver = IMAGE_MAJOR_VERSION; + sb_hdr->minor_ver = IMAGE_MINOR_VERSION; + sb_hdr->flags = 0; + sb_hdr->image_size = sb->image_size; + sb_hdr->header_size = sizeof(struct sb_header_t) / BLOCK_SIZE; + sb_hdr->first_boot_sec_id = sb->sections[0].identifier; + sb_hdr->nr_keys = g_nr_keys; + sb_hdr->nr_sections = sb->nr_sections; + sb_hdr->sec_hdr_size = sizeof(struct sb_section_header_t) / BLOCK_SIZE; + sb_hdr->key_dict_off = sb_hdr->header_size + + sb_hdr->sec_hdr_size * sb_hdr->nr_sections; + sb_hdr->first_boot_tag_off = sb_hdr->key_dict_off + + sizeof(struct sb_key_dictionary_entry_t) * sb_hdr->nr_keys / BLOCK_SIZE; + generate_random_data(sb_hdr->rand_pad0, sizeof(sb_hdr->rand_pad0)); + generate_random_data(sb_hdr->rand_pad1, sizeof(sb_hdr->rand_pad1)); + sb_hdr->timestamp = generate_timestamp(); + sb_hdr->product_ver = sb->product_ver; + fix_version(&sb_hdr->product_ver); + sb_hdr->component_ver = sb->component_ver; + fix_version(&sb_hdr->component_ver); + sb_hdr->drive_tag = 0; + + sha_1_init(&sha_1_params); + sha_1_update(&sha_1_params, &sb_hdr->signature[0], + sizeof(struct sb_header_t) - sizeof(sb_hdr->sha1_header)); + sha_1_finish(&sha_1_params); + sha_1_output(&sha_1_params, sb_hdr->sha1_header); +} + +static void produce_sb_section_header(struct sb_section_t *sec, + struct sb_section_header_t *sec_hdr) +{ + sec_hdr->identifier = sec->identifier; + sec_hdr->offset = sec->file_offset; + sec_hdr->size = sec->sec_size; + sec_hdr->flags = (sec->is_data ? 0 : SECTION_BOOTABLE) + | (sec->is_cleartext ? SECTION_CLEARTEXT : 0); +} + +static uint8_t instruction_checksum(struct sb_instruction_header_t *hdr) +{ + uint8_t sum = 90; + byte *ptr = (byte *)hdr; + for(int i = 1; i < 16; i++) + sum += ptr[i]; + return sum; +} + +static void produce_section_tag_cmd(struct sb_section_t *sec, + struct sb_instruction_tag_t *tag, bool is_last) +{ + tag->hdr.opcode = SB_INST_TAG; + tag->hdr.flags = is_last ? SB_INST_LAST_TAG : 0; + tag->identifier = sec->identifier; + tag->len = sec->sec_size; + tag->flags = (sec->is_data ? 0 : SECTION_BOOTABLE) + | (sec->is_cleartext ? SECTION_CLEARTEXT : 0); + tag->hdr.checksum = instruction_checksum(&tag->hdr); +} + +void produce_sb_instruction(struct sb_inst_t *inst, + struct sb_instruction_common_t *cmd) +{ + memset(cmd, 0, sizeof(struct sb_instruction_common_t)); + cmd->hdr.opcode = inst->inst; + switch(inst->inst) + { + case SB_INST_CALL: + case SB_INST_JUMP: + cmd->addr = inst->addr; + cmd->data = inst->argument; + break; + case SB_INST_FILL: + cmd->addr = inst->addr; + cmd->len = inst->size; + cmd->data = inst->pattern; + break; + case SB_INST_LOAD: + cmd->addr = inst->addr; + cmd->len = inst->size; + cmd->data = crc_continue(crc(inst->data, inst->size), + inst->padding, inst->padding_size); + break; + case SB_INST_MODE: + cmd->data = inst->addr; + break; + case SB_INST_NOP: + break; + default: + bug("die\n"); + } + cmd->hdr.checksum = instruction_checksum(&cmd->hdr); +} + +void sb_produce_file(struct sb_file_t *sb, const char *filename) +{ + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) + bugp("cannot open output file"); + + struct crypto_key_t real_key; + real_key.method = CRYPTO_KEY; + byte crypto_iv[16]; + byte (*cbc_macs)[16] = xmalloc(16 * g_nr_keys); + /* init CBC-MACs */ + for(int i = 0; i < g_nr_keys; i++) + memset(cbc_macs[i], 0, 16); + + fill_gaps(sb); + compute_sb_offsets(sb); + + generate_random_data(real_key.u.key, 16); + + /* global SHA-1 */ + struct sha_1_params_t file_sha1; + sha_1_init(&file_sha1); + /* produce and write header */ + struct sb_header_t sb_hdr; + produce_sb_header(sb, &sb_hdr); + sha_1_update(&file_sha1, (byte *)&sb_hdr, sizeof(sb_hdr)); + fwrite(&sb_hdr, 1, sizeof(sb_hdr), fd); + + memcpy(crypto_iv, &sb_hdr, 16); + + /* update CBC-MACs */ + for(int i = 0; i < g_nr_keys; i++) + crypto_cbc((byte *)&sb_hdr, NULL, sizeof(sb_hdr) / BLOCK_SIZE, &g_key_array[i], + cbc_macs[i], &cbc_macs[i], 1); + + /* produce and write section headers */ + for(int i = 0; i < sb_hdr.nr_sections; i++) + { + struct sb_section_header_t sb_sec_hdr; + produce_sb_section_header(&sb->sections[i], &sb_sec_hdr); + sha_1_update(&file_sha1, (byte *)&sb_sec_hdr, sizeof(sb_sec_hdr)); + fwrite(&sb_sec_hdr, 1, sizeof(sb_sec_hdr), fd); + /* update CBC-MACs */ + for(int j = 0; j < g_nr_keys; j++) + crypto_cbc((byte *)&sb_sec_hdr, NULL, sizeof(sb_sec_hdr) / BLOCK_SIZE, + &g_key_array[j], cbc_macs[j], &cbc_macs[j], 1); + } + /* produce key dictionary */ + for(int i = 0; i < g_nr_keys; i++) + { + struct sb_key_dictionary_entry_t entry; + memcpy(entry.hdr_cbc_mac, cbc_macs[i], 16); + crypto_cbc(real_key.u.key, entry.key, 1, &g_key_array[i], + crypto_iv, NULL, 1); + + fwrite(&entry, 1, sizeof(entry), fd); + sha_1_update(&file_sha1, (byte *)&entry, sizeof(entry)); + } + + /* HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK */ + /* Image crafting, don't use it unless you understand what you do */ + if(sb->real_key != NULL) + memcpy(real_key.u.key, *sb->real_key, 16); + if(sb->crypto_iv != NULL) + memcpy(crypto_iv, *sb->crypto_iv, 16); + /* KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH */ + if(g_debug) + { + printf("Real key: "); + for(int j = 0; j < 16; j++) + printf("%02x", real_key.u.key[j]); + printf("\n"); + printf("IV : "); + for(int j = 0; j < 16; j++) + printf("%02x", crypto_iv[j]); + printf("\n"); + } + /* produce sections data */ + for(int i = 0; i< sb_hdr.nr_sections; i++) + { + /* produce tag command */ + struct sb_instruction_tag_t tag_cmd; + produce_section_tag_cmd(&sb->sections[i], &tag_cmd, (i + 1) == sb_hdr.nr_sections); + if(g_nr_keys > 0) + crypto_cbc((byte *)&tag_cmd, (byte *)&tag_cmd, sizeof(tag_cmd) / BLOCK_SIZE, + &real_key, crypto_iv, NULL, 1); + sha_1_update(&file_sha1, (byte *)&tag_cmd, sizeof(tag_cmd)); + fwrite(&tag_cmd, 1, sizeof(tag_cmd), fd); + /* produce other commands */ + byte cur_cbc_mac[16]; + memcpy(cur_cbc_mac, crypto_iv, 16); + for(int j = 0; j < sb->sections[i].nr_insts; j++) + { + struct sb_inst_t *inst = &sb->sections[i].insts[j]; + /* command */ + if(inst->inst != SB_INST_DATA) + { + struct sb_instruction_common_t cmd; + produce_sb_instruction(inst, &cmd); + if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) + crypto_cbc((byte *)&cmd, (byte *)&cmd, sizeof(cmd) / BLOCK_SIZE, + &real_key, cur_cbc_mac, &cur_cbc_mac, 1); + sha_1_update(&file_sha1, (byte *)&cmd, sizeof(cmd)); + fwrite(&cmd, 1, sizeof(cmd), fd); + } + /* data */ + if(inst->inst == SB_INST_LOAD || inst->inst == SB_INST_DATA) + { + uint32_t sz = inst->size + inst->padding_size; + byte *data = xmalloc(sz); + memcpy(data, inst->data, inst->size); + memcpy(data + inst->size, inst->padding, inst->padding_size); + if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) + crypto_cbc(data, data, sz / BLOCK_SIZE, + &real_key, cur_cbc_mac, &cur_cbc_mac, 1); + sha_1_update(&file_sha1, data, sz); + fwrite(data, 1, sz, fd); + free(data); + } + } + } + /* write file SHA-1 */ + byte final_sig[32]; + sha_1_finish(&file_sha1); + sha_1_output(&file_sha1, final_sig); + generate_random_data(final_sig + 20, 12); + if(g_nr_keys > 0) + crypto_cbc(final_sig, final_sig, 2, &real_key, crypto_iv, NULL, 1); + fwrite(final_sig, 1, 32, fd); + + fclose(fd); +} diff --git a/utils/sbtools/sb.h b/utils/sbtools/sb.h index 0a8fad10af..27f5668d2e 100644 --- a/utils/sbtools/sb.h +++ b/utils/sbtools/sb.h @@ -22,6 +22,7 @@ #define __SB_H__ #include +#include #define BLOCK_SIZE 16 @@ -150,4 +151,56 @@ struct sb_instruction_tag_t uint32_t flags; /* section flags */ } __attribute__((packed)); +/******* + * API * + *******/ + +#define SB_INST_DATA 0xff + +struct sb_inst_t +{ + uint8_t inst; /* SB_INST_* */ + uint32_t size; + // + void *data; + uint32_t pattern; + uint32_t addr; + // + uint32_t argument; // for call and jump + /* for production use */ + uint32_t padding_size; + uint8_t *padding; +}; + +struct sb_section_t +{ + uint32_t identifier; + bool is_data; + bool is_cleartext; + uint32_t alignment; + // data sections are handled as one or more SB_INST_DATA virtual instruction + int nr_insts; + struct sb_inst_t *insts; + /* for production use */ + uint32_t file_offset; /* in blocks */ + uint32_t sec_size; /* in blocks */ +}; + +struct sb_file_t +{ + /* override real, otherwise it is randomly generated */ + uint8_t (*real_key)[16]; + /* override crypto IV, use with caution ! Use NULL to generate it */ + uint8_t (*crypto_iv)[16]; + + int nr_sections; + struct sb_section_t *sections; + struct sb_version_t product_ver; + struct sb_version_t component_ver; + /* for production use */ + uint32_t image_size; /* in blocks */ +}; + +void sb_produce_file(struct sb_file_t *sb, const char *filename); + #endif /* __SB_H__ */ diff --git a/utils/sbtools/sbtoelf.c b/utils/sbtools/sbtoelf.c index 3c1c750582..3824ee094e 100644 --- a/utils/sbtools/sbtoelf.c +++ b/utils/sbtools/sbtoelf.c @@ -77,8 +77,6 @@ char *g_out_prefix; bool g_debug; bool g_raw_mode; -#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) - static uint8_t instruction_checksum(struct sb_instruction_header_t *hdr) { uint8_t sum = 90; @@ -495,7 +493,7 @@ static void extract(unsigned long filesize) print_hex(g_buf, 16, true); /* sections */ - if(strcasecmp(s_getenv("SB_RAW_CMD"), "YES") != 0) + if(!g_raw_mode) { color(BLUE); printf("Sections\n"); @@ -747,9 +745,7 @@ int main(int argc, char **argv) break; case 'k': { - int kac; - key_array_t ka = read_keys(optarg, &kac); - add_keys(ka, kac); + add_keys_from_file(optarg); break; } case 'z': -- cgit v1.2.3