1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Amaury Pouly 2011-10-29 14:22:17 +00:00
parent b7547e5868
commit 9fe029b12a
7 changed files with 720 additions and 312 deletions

View file

@ -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

188
utils/sbtools/crypto.c Normal file
View file

@ -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 <stdio.h>
#include <stdbool.h>
#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);
}

View file

@ -18,6 +18,9 @@
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __CRYPTO_H__
#define __CRYPTO_H__
#include <stdio.h>
#include <stdint.h>
#include <string.h>
@ -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__ */

View file

@ -21,13 +21,8 @@
#define _ISOC99_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
@ -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 <file>\tAdd key file\n");
printf(" -z\t\tAdd zero key\n");
printf(" --single-key <key>\tAdd single key\n");
printf(" --usb-otp <vid>:<pid>\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++)

174
utils/sbtools/misc.c Normal file
View file

@ -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 <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#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");
}

48
utils/sbtools/misc.h Normal file
View file

@ -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 <stdbool.h>
#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__ */

View file

@ -28,21 +28,19 @@
#define _ISOC99_SOURCE /* snprintf() */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <stdarg.h>
#include <strings.h>
#include <getopt.h>
#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 <file>\tSet output prefix\n");
printf(" -d/--debug\tEnable debug output\n");
printf(" -k <file>\tAdd key file\n");
printf(" -z\t\tAdd zero key\n");
printf(" -r\t\tUse raw command mode\n");
printf(" --single-key <key>\tAdd single key\n");
printf(" --usb-otp <vid>:<pid>\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 <firmware> <key file> [<out prefix>]\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);