From 9fe029b12a0828b247718fc89b08547b1ab916b5 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Sat, 29 Oct 2011 14:22:17 +0000 Subject: [PATCH] sbtools: factor key code, introduce crypto layer, move from open/read/... to fopen/fread/..., add support for encryption/decryption using a device when the key is not known, move sbtoelf to use getopt for command line git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30849 a1c6a512-1295-4272-9138-f99709370657 --- utils/sbtools/Makefile | 22 +++- utils/sbtools/crypto.c | 188 +++++++++++++++++++++++++++ utils/sbtools/crypto.h | 56 ++++++++ utils/sbtools/elftosb.c | 270 ++++++++++++++------------------------- utils/sbtools/misc.c | 174 +++++++++++++++++++++++++ utils/sbtools/misc.h | 48 +++++++ utils/sbtools/sbtoelf.c | 274 +++++++++++++++++++++------------------- 7 files changed, 720 insertions(+), 312 deletions(-) create mode 100644 utils/sbtools/crypto.c create mode 100644 utils/sbtools/misc.c create mode 100644 utils/sbtools/misc.h diff --git a/utils/sbtools/Makefile b/utils/sbtools/Makefile index dc9c0966a7..15d3adb8a1 100644 --- a/utils/sbtools/Makefile +++ b/utils/sbtools/Makefile @@ -1,10 +1,22 @@ +DEFINES=-DCRYPTO_LIBUSB +CC=gcc +LD=gcc +CFLAGS=-g -std=c99 -W -Wall `pkg-config --cflags libusb-1.0` $(DEFINES) +LDFLAGS=`pkg-config --libs libusb-1.0` + all: elftosb sbtoelf -sbtoelf: sbtoelf.c crc.c crypto.h aes128.c sha1.c elf.c sb.h - gcc -g -std=c99 -o $@ -W -Wall $^ +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< -elftosb: elftosb.c crc.c crypto.h aes128.c sha1.c elf.c sb.h dbparser.h dbparser.c - gcc -g -std=c99 -o $@ -W -Wall $^ +sbtoelf: sbtoelf.o crc.o crypto.o aes128.o sha1.o elf.o misc.o + $(LD) $(LDFLAGS) -o $@ $^ + +elftosb: elftosb.o crc.o crypto.o aes128.o sha1.o elf.o dbparser.o misc.o + $(LD) $(LDFLAGS) -o $@ $^ clean: - rm -fr elftosb sbtoelf + rm -fr *.o + +veryclean: + rm -rf sbtoelf elftosb diff --git a/utils/sbtools/crypto.c b/utils/sbtools/crypto.c new file mode 100644 index 0000000000..d4afc6c816 --- /dev/null +++ b/utils/sbtools/crypto.c @@ -0,0 +1,188 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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 "crypto.h" +#include +#include +#ifdef CRYPTO_LIBUSB +#include "libusb.h" +#endif +#include "misc.h" + +static enum crypto_method_t cur_method = CRYPTO_NONE; +static byte key[16]; +static uint16_t usb_vid, usb_pid; + +void crypto_setup(enum crypto_method_t method, void *param) +{ + cur_method = method; + switch(method) + { + case CRYPTO_KEY: + memcpy(key, param, sizeof(key)); + break; + case CRYPTO_USBOTP: + { + uint32_t value = *(uint32_t *)param; + usb_vid = value >> 16; + usb_pid = value & 0xffff; + break; + } + default: + break; + } +} + +int crypto_apply( + byte *in_data, /* Input data */ + byte *out_data, /* Output data (or NULL) */ + int nr_blocks, /* Number of blocks (one block=16 bytes) */ + byte iv[16], /* Key */ + byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ + int encrypt) +{ + if(cur_method == CRYPTO_KEY) + { + cbc_mac(in_data, out_data, nr_blocks, key, iv, out_cbc_mac, encrypt); + return CRYPTO_ERROR_SUCCESS; + } + #ifdef CRYPTO_LIBUSB + else if(cur_method == CRYPTO_USBOTP) + { + if(out_cbc_mac && !encrypt) + memcpy(*out_cbc_mac, in_data + 16 * (nr_blocks - 1), 16); + + libusb_device_handle *handle = NULL; + libusb_context *ctx; + /* init library */ + libusb_init(&ctx); + libusb_set_debug(NULL,3); + /* open device */ + handle = libusb_open_device_with_vid_pid(ctx, usb_vid, usb_pid); + if(handle == NULL) + { + printf("usbotp: cannot open device %04x:%04x\n", usb_vid, usb_pid); + return CRYPTO_ERROR_NODEVICE; + } + /* get device pointer */ + libusb_device *mydev = libusb_get_device(handle); + if(g_debug) + printf("usbotp: device found at %d:%d\n", libusb_get_bus_number(mydev), + libusb_get_device_address(mydev)); + int config_id; + /* explore configuration */ + libusb_get_configuration(handle, &config_id); + struct libusb_config_descriptor *config; + libusb_get_active_config_descriptor(mydev, &config); + + if(g_debug) + { + printf("usbotp: configuration: %d\n", config_id); + printf("usbotp: interfaces: %d\n", config->bNumInterfaces); + } + + const struct libusb_endpoint_descriptor *endp = NULL; + int intf, intf_alt; + for(intf = 0; intf < config->bNumInterfaces; intf++) + for(intf_alt = 0; intf_alt < config->interface[intf].num_altsetting; intf_alt++) + for(int ep = 0; ep < config->interface[intf].altsetting[intf_alt].bNumEndpoints; ep++) + { + endp = &config->interface[intf].altsetting[intf_alt].endpoint[ep]; + if((endp->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_INTERRUPT && + (endp->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) + goto Lfound; + } + libusb_close(handle); + printf("usbotp: No suitable endpoint found\n"); + return CRYPTO_ERROR_BADENDP; + + if(g_debug) + { + printf("usbotp: use interface %d, alt %d\n", intf, intf_alt); + printf("usbotp: use endpoint %d\n", endp->bEndpointAddress); + } + Lfound: + if(libusb_claim_interface(handle, intf) != 0) + { + if(g_debug) + printf("usbotp: claim error\n"); + return CRYPTO_ERROR_CLAIMFAIL; + } + + int buffer_size = 16 + 16 * nr_blocks; + unsigned char *buffer = xmalloc(buffer_size); + memcpy(buffer, iv, 16); + memcpy(buffer + 16, in_data, 16 * nr_blocks); + int ret = libusb_control_transfer(handle, + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE, + 0xaa, encrypt ? 0xeeee : 0xdddd, 0, buffer, buffer_size, 1000); + if(ret < 0) + { + if(g_debug) + printf("usbotp: control transfer failed: %d\n", ret); + libusb_release_interface(handle, intf); + libusb_close(handle); + return CRYPTO_ERROR_DEVREJECT; + } + + int recv_size; + ret = libusb_interrupt_transfer(handle, endp->bEndpointAddress, buffer, + buffer_size, &recv_size, 1000); + libusb_release_interface(handle, intf); + libusb_close(handle); + + if(ret < 0) + { + if(g_debug) + printf("usbotp: interrupt transfer failed: %d\n", ret); + return CRYPTO_ERROR_DEVSILENT; + } + if(recv_size != buffer_size) + { + if(g_debug) + printf("usbotp: device returned %d bytes, expected %d\n", recv_size, + buffer_size); + return CRYPTO_ERROR_DEVERR; + } + + if(out_data) + memcpy(out_data, buffer + 16, 16 * nr_blocks); + if(out_cbc_mac && encrypt) + memcpy(*out_cbc_mac, buffer + buffer_size - 16, 16); + + return CRYPTO_ERROR_SUCCESS; + } + #endif + else + return CRYPTO_ERROR_BADSETUP; +} + +int crypto_cbc( + byte *in_data, /* Input data */ + byte *out_data, /* Output data (or NULL) */ + int nr_blocks, /* Number of blocks (one block=16 bytes) */ + struct crypto_key_t *key, /* Key */ + byte iv[16], /* IV */ + byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ + int encrypt) +{ + crypto_setup(key->method, (void *)key->u.param); + return crypto_apply(in_data, out_data, nr_blocks, iv, out_cbc_mac, encrypt); +} diff --git a/utils/sbtools/crypto.h b/utils/sbtools/crypto.h index 0662ef8587..51f44406db 100644 --- a/utils/sbtools/crypto.h +++ b/utils/sbtools/crypto.h @@ -18,6 +18,9 @@ * KIND, either express or implied. * ****************************************************************************/ +#ifndef __CRYPTO_H__ +#define __CRYPTO_H__ + #include #include #include @@ -39,6 +42,57 @@ void cbc_mac( int encrypt /* 1 to encrypt, 0 to decrypt */ ); +/* crypto.c */ +enum crypto_method_t +{ + CRYPTO_NONE, /* disable */ + CRYPTO_KEY, /* key */ + CRYPTO_USBOTP, /* use usbotp device */ +}; + +/* parameter can be: + * - CRYPTO_KEY: array of 16-bytes (the key) + * - CRYPTO_USBOTP: 32-bit integer: vid << 16 | pid */ +void crypto_setup(enum crypto_method_t method, void *param); + +#define CRYPTO_ERROR_SUCCESS 0 +#define CRYPTO_ERROR_BADSETUP -1 /* bad crypto setup */ +#define CRYPTO_ERROR_NODEVICE -2 /* no device with vid:pid */ +#define CRYPTO_ERROR_BADENDP -3 /* device doesn't have the required endpoints */ +#define CRYPTO_ERROR_CLAIMFAIL -4 /* device interface claim error */ +#define CRYPTO_ERROR_DEVREJECT -5 /* device rejected cypto operation */ +#define CRYPTO_ERROR_DEVSILENT -6 /* device did not notify completion */ +#define CRYPTO_ERROR_DEVERR -7 /* device did something wrong (like return too small buffer) */ +/* return 0 on success, <0 on error */ +int crypto_apply( + byte *in_data, /* Input data */ + byte *out_data, /* Output data (or NULL) */ + int nr_blocks, /* Number of blocks (one block=16 bytes) */ + byte iv[16], /* IV */ + byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ + int encrypt); + +/* all-in-one function */ +struct crypto_key_t +{ + enum crypto_method_t method; + union + { + byte key[16]; + uint32_t vid_pid; + byte param[0]; + }u; +}; + +int crypto_cbc( + byte *in_data, /* Input data */ + byte *out_data, /* Output data (or NULL) */ + int nr_blocks, /* Number of blocks (one block=16 bytes) */ + struct crypto_key_t *key, /* Key */ + byte iv[16], /* IV */ + byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ + int encrypt); + /* crc.c */ uint32_t crc(byte *data, int size); uint32_t crc_continue(uint32_t previous_crc, byte *data, int size); @@ -56,3 +110,5 @@ void sha_1_block(struct sha_1_params_t *params, uint32_t cur_hash[5], byte *data void sha_1_update(struct sha_1_params_t *params, byte *buffer, int size); void sha_1_finish(struct sha_1_params_t *params); void sha_1_output(struct sha_1_params_t *params, byte *out); + +#endif /* __CRYPTO_H__ */ diff --git a/utils/sbtools/elftosb.c b/utils/sbtools/elftosb.c index 17b72417cf..5e65ba3261 100644 --- a/utils/sbtools/elftosb.c +++ b/utils/sbtools/elftosb.c @@ -21,13 +21,8 @@ #define _ISOC99_SOURCE #include -#include -#include -#include #include -#include #include -#include #include #include #include @@ -39,143 +34,18 @@ #include "elf.h" #include "sb.h" #include "dbparser.h" +#include "misc.h" -#define _STR(a) #a -#define STR(a) _STR(a) - -#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) - -bool g_debug = false; char **g_extern; int g_extern_count; #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) -/** - * Misc - */ - -char *s_getenv(const char *name) -{ - char *s = getenv(name); - return s ? s : ""; -} - -void generate_random_data(void *buf, size_t sz) -{ - static int rand_fd = -1; - if(rand_fd == -1) - rand_fd = open("/dev/urandom", O_RDONLY); - if(rand_fd == -1) - bugp("failed to open /dev/urandom"); - if(read(rand_fd, buf, sz) != (ssize_t)sz) - bugp("failed to read /dev/urandom"); -} - -void *xmalloc(size_t s) /* malloc helper, used in elf.c */ -{ - void * r = malloc(s); - if(!r) bugp("malloc"); - return r; -} - -int convxdigit(char digit, byte *val) -{ - if(digit >= '0' && digit <= '9') - { - *val = digit - '0'; - return 0; - } - else if(digit >= 'A' && digit <= 'F') - { - *val = digit - 'A' + 10; - return 0; - } - else if(digit >= 'a' && digit <= 'f') - { - *val = digit - 'a' + 10; - return 0; - } - else - return 1; -} - -/** - * Key file parsing - */ - -typedef byte (*key_array_t)[16]; - -int g_nr_keys; -key_array_t g_key_array; - -static void add_keys(key_array_t ka, int kac) -{ - key_array_t new_ka = xmalloc((g_nr_keys + kac) * 16); - memcpy(new_ka, g_key_array, g_nr_keys * 16); - memcpy(new_ka + g_nr_keys, ka, kac * 16); - free(g_key_array); - g_key_array = new_ka; - g_nr_keys += kac; -} - -static key_array_t read_keys(const char *key_file, int *num_keys) -{ - int size; - struct stat st; - int fd = open(key_file,O_RDONLY); - if(fd == -1) - bugp("opening key file failed"); - if(fstat(fd,&st) == -1) - bugp("key file stat() failed"); - size = st.st_size; - char *buf = xmalloc(size); - if(read(fd, buf, size) != (ssize_t)size) - bugp("reading key file"); - close(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(byte[16]) * *num_keys); - int pos = 0; - for(int i = 0; i < *num_keys; i++) - { - /* skip ws */ - while(pos < size && isspace(buf[pos])) - pos++; - /* enough space ? */ - if((pos + 32) > size) - bugp("invalid key file"); - 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][j] = (a << 4) | b; - } - if(g_debug) - { - printf("Add key: "); - for(int j = 0; j < 16; j++) - printf("%02x", keys[i][j]); - printf("\n"); - } - pos += 32; - } - free(buf); - - return keys; -} +#define crypto_cbc(...) \ + do { int ret = crypto_cbc(__VA_ARGS__); \ + if(ret != CRYPTO_ERROR_SUCCESS) \ + bug("crypto_cbc error: %d\n", ret); \ + }while(0) /** * command file to sb conversion @@ -224,9 +94,9 @@ struct sb_file_t static bool elf_read(void *user, uint32_t addr, void *buf, size_t count) { - if(lseek(*(int *)user, addr, SEEK_SET) == (off_t)-1) + if(fseek((FILE *)user, addr, SEEK_SET) == -1) return false; - return read(*(int *)user, buf, count) == (ssize_t)count; + return fread(buf, 1, count, (FILE *)user) == count; } static void elf_printf(void *user, bool error, const char *fmt, ...) @@ -264,14 +134,14 @@ static void load_elf_by_id(struct cmd_file_t *cmd_file, const char *id) resolve_extern(src); /* load it */ src->type = CMD_SRC_ELF; - int fd = open(src->filename, O_RDONLY); - if(fd < 0) + FILE *fd = fopen(src->filename, "rb"); + if(fd == NULL) bug("cannot open '%s' (id '%s')\n", src->filename, id); if(g_debug) printf("Loading ELF file '%s'...\n", src->filename); elf_init(&src->elf); - src->loaded = elf_read_file(&src->elf, elf_read, elf_printf, &fd); - close(fd); + src->loaded = elf_read_file(&src->elf, elf_read, elf_printf, fd); + fclose(fd); if(!src->loaded) bug("error loading elf file '%s' (id '%s')\n", src->filename, id); elf_translate_addresses(&src->elf); @@ -291,16 +161,17 @@ static void load_bin_by_id(struct cmd_file_t *cmd_file, const char *id) resolve_extern(src); /* load it */ src->type = CMD_SRC_BIN; - int fd = open(src->filename, O_RDONLY); - if(fd < 0) + FILE *fd = fopen(src->filename, "rb"); + if(fd == NULL) bug("cannot open '%s' (id '%s')\n", src->filename, id); if(g_debug) printf("Loading BIN file '%s'...\n", src->filename); - src->bin.size = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); + fseek(fd, 0, SEEK_END); + src->bin.size = ftell(fd); + fseek(fd, 0, SEEK_SET); src->bin.data = xmalloc(src->bin.size); - read(fd, src->bin.data, src->bin.size); - close(fd); + fread(src->bin.data, 1, src->bin.size, fd); + fclose(fd); src->loaded = true; } @@ -767,12 +638,12 @@ void produce_sb_instruction(struct sb_inst_t *inst, static void produce_sb_file(struct sb_file_t *sb, const char *filename) { - int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if(fd < 0) + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) bugp("cannot open output file"); - byte real_key[16]; + 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 */ @@ -782,7 +653,7 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) fill_gaps(sb); compute_sb_offsets(sb); - generate_random_data(real_key, sizeof(real_key)); + generate_random_data(real_key.u.key, 16); /* global SHA-1 */ struct sha_1_params_t file_sha1; @@ -791,13 +662,13 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) struct sb_header_t sb_hdr; produce_sb_header(sb, &sb_hdr); sha_1_update(&file_sha1, (byte *)&sb_hdr, sizeof(sb_hdr)); - write(fd, &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++) - cbc_mac((byte *)&sb_hdr, NULL, sizeof(sb_hdr) / BLOCK_SIZE, g_key_array[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 */ @@ -806,21 +677,21 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) 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)); - write(fd, &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++) - cbc_mac((byte *)&sb_sec_hdr, NULL, sizeof(sb_sec_hdr) / BLOCK_SIZE, - g_key_array[j], cbc_macs[j], &cbc_macs[j], 1); + 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); - cbc_mac(real_key, entry.key, sizeof(real_key) / BLOCK_SIZE, g_key_array[i], + crypto_cbc(real_key.u.key, entry.key, 1, &g_key_array[i], crypto_iv, NULL, 1); - write(fd, &entry, sizeof(entry)); + fwrite(&entry, 1, sizeof(entry), fd); sha_1_update(&file_sha1, (byte *)&entry, sizeof(entry)); } @@ -836,7 +707,7 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) 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[i] = (a << 4) | b; + real_key.u.key[i] = (a << 4) | b; } } if(strlen(s_getenv("SB_OVERRIDE_IV")) != 0) @@ -858,7 +729,7 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) { printf("Real key: "); for(int j = 0; j < 16; j++) - printf("%02x", real_key[j]); + printf("%02x", real_key.u.key[j]); printf("\n"); printf("IV : "); for(int j = 0; j < 16; j++) @@ -872,10 +743,10 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) 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) - cbc_mac((byte *)&tag_cmd, (byte *)&tag_cmd, sizeof(tag_cmd) / BLOCK_SIZE, - real_key, crypto_iv, NULL, 1); + 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)); - write(fd, &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); @@ -888,10 +759,10 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) struct sb_instruction_common_t cmd; produce_sb_instruction(inst, &cmd); if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) - cbc_mac((byte *)&cmd, (byte *)&cmd, sizeof(cmd) / BLOCK_SIZE, - real_key, cur_cbc_mac, &cur_cbc_mac, 1); + 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)); - write(fd, &cmd, sizeof(cmd)); + fwrite(&cmd, 1, sizeof(cmd), fd); } /* data */ if(inst->inst == SB_INST_LOAD || inst->inst == SB_INST_DATA) @@ -901,10 +772,10 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) 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) - cbc_mac(data, data, sz / BLOCK_SIZE, - real_key, cur_cbc_mac, &cur_cbc_mac, 1); + crypto_cbc(data, data, sz / BLOCK_SIZE, + &real_key, cur_cbc_mac, &cur_cbc_mac, 1); sha_1_update(&file_sha1, data, sz); - write(fd, data, sz); + fwrite(data, 1, sz, fd); free(data); } } @@ -915,10 +786,10 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) sha_1_output(&file_sha1, final_sig); generate_random_data(final_sig + 20, 12); if(g_nr_keys > 0) - cbc_mac(final_sig, final_sig, 2, real_key, crypto_iv, NULL, 1); - write(fd, final_sig, 32); + crypto_cbc(final_sig, final_sig, 2, &real_key, crypto_iv, NULL, 1); + fwrite(final_sig, 1, 32, fd); - close(fd); + fclose(fd); } void usage(void) @@ -931,10 +802,16 @@ void usage(void) printf(" -d/--debug\tEnable debug output\n"); printf(" -k \tAdd key file\n"); printf(" -z\t\tAdd zero key\n"); + printf(" --single-key \tAdd single key\n"); + printf(" --usb-otp :\tAdd USB OTP device\n"); exit(1); } -static byte g_zero_key[16] = {0}; +static struct crypto_key_t g_zero_key = +{ + .method = CRYPTO_KEY, + .u.key = {0} +}; int main(int argc, char **argv) { @@ -947,6 +824,8 @@ int main(int argc, char **argv) { {"help", no_argument, 0, '?'}, {"debug", no_argument, 0, 'd'}, + {"single-key", required_argument, 0, 's'}, + {"usb-otp", required_argument, 0, 'u'}, {0, 0, 0, 0} }; @@ -979,6 +858,42 @@ int main(int argc, char **argv) add_keys(&g_zero_key, 1); break; } + case 's': + { + struct crypto_key_t key; + key.method = CRYPTO_KEY; + if(strlen(optarg) != 32) + bug("The key given in argument is invalid"); + for(int i = 0; i < 16; i++) + { + byte a, b; + if(convxdigit(optarg[2 * i], &a) || convxdigit(optarg[2 * i + 1], &b)) + bugp("The key given in argument is invalid\n"); + key.u.key[i] = (a << 4) | b; + } + add_keys(&key, 1); + break; + } + case 'u': + { + int vid, pid; + char *p = strchr(optarg, ':'); + if(p == NULL) + bug("Invalid VID/PID\n"); + + char *end; + vid = strtol(optarg, &end, 16); + if(end != p) + bug("Invalid VID/PID\n"); + pid = strtol(p + 1, &end, 16); + if(end != (optarg + strlen(optarg))) + bug("Invalid VID/PID\n"); + struct crypto_key_t key; + key.method = CRYPTO_USBOTP; + key.u.vid_pid = vid << 16 | pid; + add_keys(&key, 1); + break; + } default: abort(); } @@ -997,9 +912,8 @@ int main(int argc, char **argv) printf("key: %d\n", g_nr_keys); for(int i = 0; i < g_nr_keys; i++) { - for(int j = 0; j < 16; j++) - printf(" %02x", g_key_array[i][j]); - printf("\n"); + printf(" "); + print_key(&g_key_array[i], true); } for(int i = 0; i < g_extern_count; i++) diff --git a/utils/sbtools/misc.c b/utils/sbtools/misc.c new file mode 100644 index 0000000000..09a8919aef --- /dev/null +++ b/utils/sbtools/misc.c @@ -0,0 +1,174 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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 +#include "misc.h" + +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"); + if(rand_fd == NULL) + bugp("failed to open /dev/urandom"); + if(fread(buf, 1, sz, rand_fd) != sz) + bugp("failed to read /dev/urandom"); + fclose(rand_fd); +} + +void *xmalloc(size_t s) +{ + void * r = malloc(s); + if(!r) bugp("malloc"); + return r; +} + +int convxdigit(char digit, byte *val) +{ + if(digit >= '0' && digit <= '9') + { + *val = digit - '0'; + return 0; + } + else if(digit >= 'A' && digit <= 'F') + { + *val = digit - 'A' + 10; + return 0; + } + else if(digit >= 'a' && digit <= 'f') + { + *val = digit - 'a' + 10; + return 0; + } + else + return 1; +} + +/** + * Key file parsing + */ +int g_nr_keys; +key_array_t g_key_array; + +void add_keys(key_array_t ka, int kac) +{ + key_array_t new_ka = xmalloc((g_nr_keys + kac) * sizeof(struct crypto_key_t)); + memcpy(new_ka, g_key_array, g_nr_keys * sizeof(struct crypto_key_t)); + memcpy(new_ka + g_nr_keys, ka, kac * sizeof(struct crypto_key_t)); + free(g_key_array); + g_key_array = new_ka; + g_nr_keys += kac; +} + +key_array_t read_keys(const char *key_file, int *num_keys) +{ + int size; + FILE *fd = fopen(key_file, "r"); + if(fd == NULL) + bugp("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"); + 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++) + { + /* 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; + } + if(g_debug) + { + printf("Add key: "); + for(int j = 0; j < 16; j++) + printf("%02x", keys[i].u.key[j]); + printf("\n"); + } + pos += 32; + } + free(buf); + + return keys; +} + +void print_hex(byte *data, int len, bool newline) +{ + for(int i = 0; i < len; i++) + printf("%02X ", data[i]); + if(newline) + printf("\n"); +} + +void print_key(struct crypto_key_t *key, bool newline) +{ + switch(key->method) + { + case CRYPTO_KEY: + print_hex(key->u.key, 16, false); + break; + case CRYPTO_USBOTP: + printf("USB-OTP(%04x:%04x)", key->u.vid_pid >> 16, key->u.vid_pid & 0xffff); + break; + case CRYPTO_NONE: + printf("none"); + break; + } + if(newline) + printf("\n"); +} diff --git a/utils/sbtools/misc.h b/utils/sbtools/misc.h new file mode 100644 index 0000000000..545285eafc --- /dev/null +++ b/utils/sbtools/misc.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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. + * + ****************************************************************************/ +#ifndef __MISC_H__ +#define __MISC_H__ + +#include +#include "crypto.h" + +#define _STR(a) #a +#define STR(a) _STR(a) + +#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) + +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 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); +void print_key(struct crypto_key_t *key, bool newline); + +#endif /* __MISC_H__ */ diff --git a/utils/sbtools/sbtoelf.c b/utils/sbtools/sbtoelf.c index fb4567bed9..3c1c750582 100644 --- a/utils/sbtools/sbtoelf.c +++ b/utils/sbtools/sbtoelf.c @@ -28,21 +28,19 @@ #define _ISOC99_SOURCE /* snprintf() */ #include -#include -#include -#include #include -#include #include -#include #include #include #include +#include #include +#include #include "crypto.h" #include "elf.h" #include "sb.h" +#include "misc.h" #if 1 /* ANSI colors */ @@ -60,104 +58,24 @@ char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; # define color(a) #endif -#define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0) -#define bugp(a) do { perror("ERROR: "a); exit(1); } while(0) - /* all blocks are sized as a multiple of 0x1ff */ #define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff) /* If you find a firmware that breaks the known format ^^ */ #define assert(a) do { if(!(a)) { fprintf(stderr,"Assertion \"%s\" failed in %s() line %d!\n\nPlease send us your firmware!\n",#a,__func__,__LINE__); exit(1); } } while(0) +#define crypto_cbc(...) \ + do { int ret = crypto_cbc(__VA_ARGS__); \ + if(ret != CRYPTO_ERROR_SUCCESS) \ + bug("crypto_cbc error: %d\n", ret); \ + }while(0) + /* globals */ uint8_t *g_buf; /* file content */ -#define PREFIX_SIZE 128 -char out_prefix[PREFIX_SIZE]; -const char *key_file; - -char *s_getenv(const char *name) -{ - char *s = getenv(name); - return s ? s : ""; -} - -void *xmalloc(size_t s) /* malloc helper, used in elf.c */ -{ - void * r = malloc(s); - if(!r) bugp("malloc"); - return r; -} - -static void print_hex(byte *data, int len, bool newline) -{ - for(int i = 0; i < len; i++) - printf("%02X ", data[i]); - if(newline) - printf("\n"); -} - -static int convxdigit(char digit, byte *val) -{ - if(digit >= '0' && digit <= '9') - { - *val = digit - '0'; - return 0; - } - else if(digit >= 'A' && digit <= 'F') - { - *val = digit - 'A' + 10; - return 0; - } - else if(digit >= 'a' && digit <= 'f') - { - *val = digit - 'a' + 10; - return 0; - } - else - return 1; -} - -typedef byte (*key_array_t)[16]; - -static key_array_t read_keys(int num_keys) -{ - int size; - struct stat st; - int fd = open(key_file,O_RDONLY); - if(fd == -1) - bugp("opening key file failed"); - if(fstat(fd,&st) == -1) - bugp("key file stat() failed"); - size = st.st_size; - char *buf = xmalloc(size); - if(read(fd, buf, size) != (ssize_t)size) - bugp("reading key file"); - close(fd); - - key_array_t keys = xmalloc(sizeof(byte[16]) * num_keys); - int pos = 0; - for(int i = 0; i < num_keys; i++) - { - /* skip ws */ - while(pos < size && isspace(buf[pos])) - pos++; - /* enough space ? */ - if((pos + 32) > size) - bugp("invalid key file (not enough keys)"); - 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][j] = (a << 4) | b; - } - pos += 32; - } - free(buf); - - return keys; -} +char *g_out_prefix; +bool g_debug; +bool g_raw_mode; #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) @@ -195,17 +113,21 @@ static void extract_elf_section(struct elf_params_t *elf, int count, const char static void extract_section(int data_sec, char name[5], byte *buf, int size, const char *indent) { - char filename[PREFIX_SIZE + 32]; - snprintf(filename, sizeof filename, "%s%s.bin", out_prefix, name); - FILE *fd = fopen(filename, "wb"); - if (fd != NULL) { - fwrite(buf, size, 1, fd); - fclose(fd); + char *filename = xmalloc(strlen(g_out_prefix) + strlen(name) + 5); + if(g_out_prefix) + { + sprintf(filename, "%s%s.bin", g_out_prefix, name); + FILE *fd = fopen(filename, "wb"); + if (fd != NULL) + { + fwrite(buf, size, 1, fd); + fclose(fd); + } } if(data_sec) return; - snprintf(filename, sizeof filename, "%s%s", out_prefix, name); + sprintf(filename, "%s%s", g_out_prefix, name); /* elf construction */ struct elf_params_t elf; @@ -484,19 +406,23 @@ static void extract(unsigned long filesize) printf("0x%08x\n", sb_header->first_boot_sec_id); /* encryption cbc-mac */ - key_array_t keys = NULL; /* array of 16-bytes keys */ byte real_key[16]; bool valid_key = false; /* false until a matching key was found */ if(sb_header->nr_keys > 0) { - keys = read_keys(sb_header->nr_keys); + if(sb_header->nr_keys > g_nr_keys) + { + color(GREY); + bug("SB file has %d keys but only %d were specified on command line\n", + sb_header->nr_keys, g_nr_keys); + } color(BLUE); printf("Encryption data\n"); for(int i = 0; i < sb_header->nr_keys; i++) { color(RED); printf(" Key %d: ", i); - print_hex(keys[i], 16, true); + print_key(&g_key_array[i], true); color(GREEN); printf(" CBC-MAC of headers: "); @@ -512,8 +438,8 @@ static void extract(unsigned long filesize) byte computed_cbc_mac[16]; byte zero[16]; memset(zero, 0, 16); - cbc_mac(g_buf, NULL, sb_header->header_size + sb_header->nr_sections, - keys[i], zero, &computed_cbc_mac, 1); + crypto_cbc(g_buf, NULL, sb_header->header_size + sb_header->nr_sections, + &g_key_array[i], zero, &computed_cbc_mac, 1); color(RED); bool ok = memcmp(dict_entry->hdr_cbc_mac, computed_cbc_mac, 16) == 0; if(ok) @@ -533,7 +459,7 @@ static void extract(unsigned long filesize) byte decrypted_key[16]; byte iv[16]; memcpy(iv, g_buf, 16); /* uses the first 16-bytes of SHA-1 sig as IV */ - cbc_mac(dict_entry->key, decrypted_key, 1, keys[i], iv, NULL, 0); + crypto_cbc(dict_entry->key, decrypted_key, 1, &g_key_array[i], iv, NULL, 0); printf(" Decrypted key : "); color(YELLOW); print_hex(decrypted_key, 16, false); @@ -769,37 +695,127 @@ static void extract(unsigned long filesize) printf(" Failed\n"); } -int main(int argc, const char **argv) +void usage(void) { - int fd; - struct stat st; - if(argc != 3 && argc != 4) + printf("Usage: sbtoelf [options] sb-file\n"); + printf("Options:\n"); + printf(" -?/--help\tDisplay this message\n"); + printf(" -o \tSet output prefix\n"); + printf(" -d/--debug\tEnable debug output\n"); + printf(" -k \tAdd key file\n"); + printf(" -z\t\tAdd zero key\n"); + printf(" -r\t\tUse raw command mode\n"); + printf(" --single-key \tAdd single key\n"); + printf(" --usb-otp :\tAdd USB OTP device\n"); + exit(1); +} + +static struct crypto_key_t g_zero_key = +{ + .method = CRYPTO_KEY, + .u.key = {0} +}; + +int main(int argc, char **argv) +{ + while(1) { - printf("Usage: %s []\n",*argv); - printf("To use raw command mode, set environment variable SB_RAW_CMD to YES\n"); - return 1; + static struct option long_options[] = + { + {"help", no_argument, 0, '?'}, + {"debug", no_argument, 0, 'd'}, + {"single-key", required_argument, 0, 's'}, + {"usb-otp", required_argument, 0, 'u'}, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "?do:k:zr", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case -1: + break; + case 'd': + g_debug = true; + break; + case '?': + usage(); + break; + case 'o': + g_out_prefix = optarg; + break; + case 'k': + { + int kac; + key_array_t ka = read_keys(optarg, &kac); + add_keys(ka, kac); + break; + } + case 'z': + { + add_keys(&g_zero_key, 1); + break; + } + case 's': + { + struct crypto_key_t key; + key.method = CRYPTO_KEY; + if(strlen(optarg) != 32) + bug("The key given in argument is invalid"); + for(int i = 0; i < 16; i++) + { + byte a, b; + if(convxdigit(optarg[2 * i], &a) || convxdigit(optarg[2 * i + 1], &b)) + bugp("The key given in argument is invalid\n"); + key.u.key[i] = (a << 4) | b; + } + add_keys(&key, 1); + break; + } + case 'u': + { + int vid, pid; + char *p = strchr(optarg, ':'); + if(p == NULL) + bug("Invalid VID/PID\n"); + + char *end; + vid = strtol(optarg, &end, 16); + if(end != p) + bug("Invalid VID/PID\n"); + pid = strtol(p + 1, &end, 16); + if(end != (optarg + strlen(optarg))) + bug("Invalid VID/PID\n"); + struct crypto_key_t key; + key.method = CRYPTO_USBOTP; + key.u.vid_pid = vid << 16 | pid; + add_keys(&key, 1); + break; + } + default: + abort(); + } } - if(argc == 4) - snprintf(out_prefix, PREFIX_SIZE, "%s", argv[3]); - else - strcpy(out_prefix, ""); + if(argc - optind != 1) + bug("Missing sb file or too many files after options\n"); - if( (fd = open(argv[1], O_RDONLY)) == -1 ) - bugp("opening firmware failed"); + const char *sb_file = argv[optind]; + FILE *fd = fopen(sb_file, "rb"); + if(fd == NULL) + bug("Cannot open input file\n"); + fseek(fd, 0, SEEK_END); + size_t size = ftell(fd); + fseek(fd, 0, SEEK_SET); - key_file = argv[2]; - - if(fstat(fd, &st) == -1) - bugp("firmware stat() failed"); - - g_buf = xmalloc(st.st_size); - if(read(fd, g_buf, st.st_size) != (ssize_t)st.st_size) /* load the whole file into memory */ + g_buf = xmalloc(size); + if(fread(g_buf, 1, size, fd) != size) /* load the whole file into memory */ bugp("reading firmware"); - close(fd); + fclose(fd); - extract(st.st_size); + extract(size); color(OFF);