imxtools/sbtools: rework cryptography

It was a mess, a mix of crypto_* and cbc_mac calls. I made everything call crypto
functions, and also separate key setup from cryptographic operations, this will
be useful to speed up the code in the upcoming commits. Drop support for "usbotp"
key, since the crypto code for that was never mainlined and we can always get the
keys from a device as long as we have code execution (using the DCP debug registers).

Change-Id: I7aa24d12207ffb744225d1b9cc7cb1dc7281dd22
This commit is contained in:
Amaury Pouly 2017-01-01 20:48:05 +01:00
parent cb8a98e365
commit 2b20026dd7
10 changed files with 172 additions and 319 deletions

View file

@ -14,7 +14,7 @@ CFLAGS += -std=gnu99 -g -O3
OUTPUT = mkimxboot OUTPUT = mkimxboot
# inputs for lib # inputs for lib
IMXTOOLS_SOURCES = misc.c sb.c crypto.c crc.c aes128.c sha1.c elf.c IMXTOOLS_SOURCES = misc.c sb.c crypto.cpp crc.c aes128.c sha1.c elf.c
LIBSOURCES := dualboot.c mkimxboot.c md5.c \ LIBSOURCES := dualboot.c mkimxboot.c md5.c \
$(addprefix $(IMXTOOLS_DIR),$(IMXTOOLS_SOURCES)) $(addprefix $(IMXTOOLS_DIR),$(IMXTOOLS_SOURCES))
# inputs for binary only # inputs for binary only

View file

@ -1,7 +1,9 @@
DEFINES=-DCRYPTO_LIBUSB DEFINES=
CC=gcc CC=gcc
LD=gcc CXX=g++
CFLAGS=-O3 -g -std=c99 -W -Wall `pkg-config --cflags libusb-1.0` $(DEFINES) LD=g++
CFLAGS=-O3 -g -std=c99 -Wall `pkg-config --cflags libusb-1.0` $(DEFINES)
CXXFLAGS=-O3 -g -Wall $(DEFINES)
LDFLAGS=`pkg-config --libs libusb-1.0` LDFLAGS=`pkg-config --libs libusb-1.0`
BINS=elftosb sbtoelf sbloader rsrctool elftosb1 BINS=elftosb sbtoelf sbloader rsrctool elftosb1
@ -10,6 +12,9 @@ all: $(BINS)
%.o: %.c %.o: %.c
$(CC) $(CFLAGS) -c -o $@ $< $(CC) $(CFLAGS) -c -o $@ $<
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
sbtoelf: sbtoelf.o crc.o crypto.o aes128.o sha1.o xorcrypt.o dbparser.o elf.o misc.o sb.o sb1.o sbtoelf: sbtoelf.o crc.o crypto.o aes128.o sha1.o xorcrypt.o dbparser.o elf.o misc.o sb.o sb1.o
$(LD) -o $@ $^ $(LDFLAGS) $(LD) -o $@ $^ $(LDFLAGS)

View file

@ -1,188 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

@ -0,0 +1,55 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2016 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 "misc.h"
static enum crypto_method_t g_cur_method = CRYPTO_NONE;
static byte g_key[16];
int crypto_setup(struct crypto_key_t *key)
{
g_cur_method = key->method;
switch(g_cur_method)
{
case CRYPTO_KEY:
memcpy(g_key, key->u.key, 16);
return CRYPTO_ERROR_SUCCESS;
default:
return CRYPTO_ERROR_BADSETUP;
}
}
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) */
bool encrypt)
{
if(g_cur_method == CRYPTO_KEY)
{
cbc_mac(in_data, out_data, nr_blocks, g_key, iv, out_cbc_mac, encrypt);
return CRYPTO_ERROR_SUCCESS;
}
else
return CRYPTO_ERROR_BADSETUP;
}

View file

@ -24,6 +24,11 @@
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef uint8_t byte; typedef uint8_t byte;
@ -48,32 +53,8 @@ enum crypto_method_t
CRYPTO_NONE, /* disable */ CRYPTO_NONE, /* disable */
CRYPTO_KEY, /* key */ CRYPTO_KEY, /* key */
CRYPTO_XOR_KEY, /* XOR key */ CRYPTO_XOR_KEY, /* XOR 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) */
#define CRYPTO_NUM_ERRORS 8
/* 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);
union xorcrypt_key_t union xorcrypt_key_t
{ {
uint8_t key[64]; uint8_t key[64];
@ -88,19 +69,25 @@ struct crypto_key_t
{ {
byte key[16]; byte key[16];
union xorcrypt_key_t xor_key[2]; union xorcrypt_key_t xor_key[2];
uint32_t vid_pid;
byte param[0];
}u; }u;
}; };
int crypto_cbc( #define CRYPTO_ERROR_SUCCESS 0
#define CRYPTO_ERROR_BADSETUP -1
/* parameter can be:
* - CRYPTO_KEY: array of 16-bytes (the key)
* return 0 on success, <0 on error */
int crypto_setup(struct crypto_key_t *key);
/* return 0 on success, <0 on error */
int crypto_apply(
byte *in_data, /* Input data */ byte *in_data, /* Input data */
byte *out_data, /* Output data (or NULL) */ byte *out_data, /* Output data (or NULL) */
int nr_blocks, /* Number of blocks (one block=16 bytes) */ int nr_blocks, /* Number of blocks (one block=16 bytes) */
struct crypto_key_t *key, /* Key */
byte iv[16], /* IV */ byte iv[16], /* IV */
byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */
int encrypt); bool encrypt);
/* crc.c */ /* crc.c */
uint32_t crc(byte *data, int size); uint32_t crc(byte *data, int size);
@ -127,4 +114,8 @@ uint32_t xor_encrypt(union xorcrypt_key_t keys[2], void *data, int size);
uint32_t xor_decrypt(union xorcrypt_key_t keys[2], void *data, int size); uint32_t xor_decrypt(union xorcrypt_key_t keys[2], void *data, int size);
void xor_generate_key(uint32_t laserfuse[3], union xorcrypt_key_t key[2]); void xor_generate_key(uint32_t laserfuse[3], union xorcrypt_key_t key[2]);
#ifdef __cplusplus
}
#endif
#endif /* __CRYPTO_H__ */ #endif /* __CRYPTO_H__ */

View file

@ -118,7 +118,6 @@ bool parse_key(char **pstr, struct crypto_key_t *key)
while(isspace(*str)) while(isspace(*str))
str++; str++;
/* CRYPTO_KEY: 32 hex characters /* CRYPTO_KEY: 32 hex characters
* CRYPTO_USBOTP: usbotp(vid:pid) where vid and pid are hex numbers
* CRYPTO_XOR_KEY: 256 hex characters */ * CRYPTO_XOR_KEY: 256 hex characters */
if(isxdigit(str[0]) && strlen(str) >= 256 && isxdigit(str[32])) if(isxdigit(str[0]) && strlen(str) >= 256 && isxdigit(str[32]))
{ {
@ -151,30 +150,7 @@ bool parse_key(char **pstr, struct crypto_key_t *key)
return true; return true;
} }
else else
{ return false;
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) void add_keys(key_array_t ka, int kac)
@ -278,9 +254,6 @@ void print_key(void *user, misc_printf_t printf, struct crypto_key_t *key, bool
case CRYPTO_KEY: case CRYPTO_KEY:
print_hex(user, printf, key->u.key, 16, false); print_hex(user, printf, key->u.key, 16, false);
break; break;
case CRYPTO_USBOTP:
printf(user, "USB-OTP(%04x:%04x)", key->u.vid_pid >> 16, key->u.vid_pid & 0xffff);
break;
case CRYPTO_NONE: case CRYPTO_NONE:
printf(user, "none"); printf(user, "none");
break; break;

View file

@ -73,8 +73,7 @@ enum rsrc_error_t
RSRC_FORMAT_ERROR = -5, RSRC_FORMAT_ERROR = -5,
RSRC_CHECKSUM_ERROR = -6, RSRC_CHECKSUM_ERROR = -6,
RSRC_NO_VALID_KEY = -7, RSRC_NO_VALID_KEY = -7,
RSRC_FIRST_CRYPTO_ERROR = -8, RSRC_CRYPTO_ERROR = -8,
RSRC_LAST_CRYPTO_ERROR = RSRC_FIRST_CRYPTO_ERROR - CRYPTO_NUM_ERRORS,
}; };
enum rsrc_error_t rsrc_write_file(struct rsrc_file_t *rsrc, const char *filename); enum rsrc_error_t rsrc_write_file(struct rsrc_file_t *rsrc, const char *filename);

View file

@ -322,6 +322,12 @@ enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *
byte *buf = xmalloc(sb_hdr.image_size * BLOCK_SIZE); byte *buf = xmalloc(sb_hdr.image_size * BLOCK_SIZE);
byte *buf_p = buf; byte *buf_p = buf;
#define write(p, sz) do { memcpy(buf_p, p, sz); buf_p += sz; } while(0) #define write(p, sz) do { memcpy(buf_p, p, sz); buf_p += sz; } while(0)
#define check_crypto(expr) \
do { int err = expr; \
if(err != CRYPTO_ERROR_SUCCESS) { \
free(cbc_macs); \
cprintf(u, true, GREY, "Crypto error: %d\n", err); \
return SB_CRYPTO_ERROR; } } while(0)
sha_1_update(&file_sha1, (byte *)&sb_hdr, sizeof(sb_hdr)); sha_1_update(&file_sha1, (byte *)&sb_hdr, sizeof(sb_hdr));
write(&sb_hdr, sizeof(sb_hdr)); write(&sb_hdr, sizeof(sb_hdr));
@ -330,8 +336,11 @@ enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *
/* update CBC-MACs */ /* update CBC-MACs */
for(int i = 0; i < g_nr_keys; i++) 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); check_crypto(crypto_setup(&g_key_array[i]));
check_crypto(crypto_apply((byte *)&sb_hdr, NULL, sizeof(sb_hdr) / BLOCK_SIZE,
cbc_macs[i], &cbc_macs[i], true));
}
/* produce and write section headers */ /* produce and write section headers */
for(int i = 0; i < sb_hdr.nr_sections; i++) for(int i = 0; i < sb_hdr.nr_sections; i++)
@ -342,23 +351,23 @@ enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *
write(&sb_sec_hdr, sizeof(sb_sec_hdr)); write(&sb_sec_hdr, sizeof(sb_sec_hdr));
/* update CBC-MACs */ /* update CBC-MACs */
for(int j = 0; j < g_nr_keys; j++) 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); check_crypto(crypto_setup(&g_key_array[j]));
check_crypto(crypto_apply((byte *)&sb_sec_hdr, NULL,
sizeof(sb_sec_hdr) / BLOCK_SIZE, cbc_macs[j], &cbc_macs[j], true));
}
} }
/* produce key dictionary */ /* produce key dictionary */
for(int i = 0; i < g_nr_keys; i++) for(int i = 0; i < g_nr_keys; i++)
{ {
struct sb_key_dictionary_entry_t entry; struct sb_key_dictionary_entry_t entry;
memcpy(entry.hdr_cbc_mac, cbc_macs[i], 16); memcpy(entry.hdr_cbc_mac, cbc_macs[i], 16);
crypto_cbc(real_key.u.key, entry.key, 1, &g_key_array[i], check_crypto(crypto_setup(&g_key_array[i]));
crypto_iv, NULL, 1); check_crypto(crypto_apply(real_key.u.key, entry.key, 1, crypto_iv, NULL, true));
write(&entry, sizeof(entry)); write(&entry, sizeof(entry));
sha_1_update(&file_sha1, (byte *)&entry, sizeof(entry)); sha_1_update(&file_sha1, (byte *)&entry, sizeof(entry));
} }
free(cbc_macs);
/* HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK */ /* 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 */ /* Image crafting, don't use it unless you understand what you do */
if(sb->override_real_key) if(sb->override_real_key)
@ -388,6 +397,8 @@ enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *
write(data, init_gap); write(data, init_gap);
free(data); free(data);
} }
/* setup real key */
check_crypto(crypto_setup(&real_key));
/* produce sections data */ /* produce sections data */
for(int i = 0; i< sb_hdr.nr_sections; i++) for(int i = 0; i< sb_hdr.nr_sections; i++)
{ {
@ -395,8 +406,10 @@ enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *
struct sb_instruction_tag_t tag_cmd; struct sb_instruction_tag_t tag_cmd;
produce_section_tag_cmd(&sb->sections[i], &tag_cmd, (i + 1) == sb_hdr.nr_sections); produce_section_tag_cmd(&sb->sections[i], &tag_cmd, (i + 1) == sb_hdr.nr_sections);
if(g_nr_keys > 0) if(g_nr_keys > 0)
crypto_cbc((byte *)&tag_cmd, (byte *)&tag_cmd, sizeof(tag_cmd) / BLOCK_SIZE, {
&real_key, crypto_iv, NULL, 1); check_crypto(crypto_apply((byte *)&tag_cmd, (byte *)&tag_cmd,
sizeof(tag_cmd) / BLOCK_SIZE, crypto_iv, NULL, true));
}
sha_1_update(&file_sha1, (byte *)&tag_cmd, sizeof(tag_cmd)); sha_1_update(&file_sha1, (byte *)&tag_cmd, sizeof(tag_cmd));
write(&tag_cmd, sizeof(tag_cmd)); write(&tag_cmd, sizeof(tag_cmd));
/* produce other commands */ /* produce other commands */
@ -411,8 +424,10 @@ enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *
struct sb_instruction_common_t cmd; struct sb_instruction_common_t cmd;
produce_sb_instruction(inst, &cmd, u, cprintf); produce_sb_instruction(inst, &cmd, u, cprintf);
if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) 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); check_crypto(crypto_apply((byte *)&cmd, (byte *)&cmd,
sizeof(cmd) / BLOCK_SIZE, cur_cbc_mac, &cur_cbc_mac, true));
}
sha_1_update(&file_sha1, (byte *)&cmd, sizeof(cmd)); sha_1_update(&file_sha1, (byte *)&cmd, sizeof(cmd));
write(&cmd, sizeof(cmd)); write(&cmd, sizeof(cmd));
} }
@ -424,8 +439,10 @@ enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *
memcpy(data, inst->data, inst->size); memcpy(data, inst->data, inst->size);
memcpy(data + inst->size, inst->padding, inst->padding_size); memcpy(data + inst->size, inst->padding, inst->padding_size);
if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) 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); check_crypto(crypto_apply(data, data, sz / BLOCK_SIZE,
cur_cbc_mac, &cur_cbc_mac, true));
}
sha_1_update(&file_sha1, data, sz); sha_1_update(&file_sha1, data, sz);
write(data, sz); write(data, sz);
free(data); free(data);
@ -450,8 +467,10 @@ enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *
cmd.hdr.opcode = SB_INST_NOP; cmd.hdr.opcode = SB_INST_NOP;
cmd.hdr.checksum = instruction_checksum(&cmd.hdr); cmd.hdr.checksum = instruction_checksum(&cmd.hdr);
if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) 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); check_crypto(crypto_apply((byte *)&cmd, (byte *)&cmd,
sizeof(cmd) / BLOCK_SIZE, cur_cbc_mac, &cur_cbc_mac, true));
}
sha_1_update(&file_sha1, (byte *)&cmd, sizeof(cmd)); sha_1_update(&file_sha1, (byte *)&cmd, sizeof(cmd));
write(&cmd, sizeof(cmd)); write(&cmd, sizeof(cmd));
} }
@ -463,28 +482,34 @@ enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *
sha_1_output(&file_sha1, final_sig); sha_1_output(&file_sha1, final_sig);
generate_random_data(final_sig + 20, 12); generate_random_data(final_sig + 20, 12);
if(g_nr_keys > 0) if(g_nr_keys > 0)
crypto_cbc(final_sig, final_sig, 2, &real_key, crypto_iv, NULL, 1); check_crypto(crypto_apply(final_sig, final_sig, 2, crypto_iv, NULL, true));
write(final_sig, 32); write(final_sig, 32);
free(cbc_macs);
if(buf_p - buf != sb_hdr.image_size * BLOCK_SIZE) if(buf_p - buf != sb_hdr.image_size * BLOCK_SIZE)
{ {
printf(GREY, "[ERROR][INTERNAL] SB image buffer was not entirely filled !\n"); free(buf);
printf(GREY, "[ERROR][INTERNAL] expected %u blocks, got %u\n", printf(GREY, "Internal error: SB image buffer was not entirely filled !\n");
printf(GREY, "Internal error: expected %u blocks, got %u\n",
(buf_p - buf) / BLOCK_SIZE, sb_hdr.image_size); (buf_p - buf) / BLOCK_SIZE, sb_hdr.image_size);
cprintf(u, true, GREY, "Internal error\n");
return SB_ERROR; return SB_ERROR;
} }
FILE *fd = fopen(filename, "wb"); FILE *fd = fopen(filename, "wb");
if(fd == NULL) if(fd == NULL)
return SB_OPEN_ERROR; return SB_OPEN_ERROR;
if(fwrite(buf, sb_hdr.image_size * BLOCK_SIZE, 1, fd) != 1) int cnt = fwrite(buf, sb_hdr.image_size * BLOCK_SIZE, 1, fd);
{ if(cnt != 1)
free(buf); printf(GREY, "Write error: %m\n");
return SB_WRITE_ERROR;
}
fclose(fd);
free(buf); free(buf);
fclose(fd);
if(cnt != 1)
return SB_WRITE_ERROR;
return SB_SUCCESS; return SB_SUCCESS;
#undef check_crypto
#undef printf #undef printf
} }
@ -712,22 +737,28 @@ static void sb_printer(void *user, const char *fmt, ...)
} }
struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, void *u, struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, void *u,
generic_printf_t cprintf, enum sb_error_t *err) generic_printf_t cprintf, enum sb_error_t *out_err)
{ {
struct sb_file_t *sb_file = NULL; struct sb_file_t *sb_file = NULL;
uint8_t *buf = _buf; uint8_t *buf = _buf;
#define printf(c, ...) cprintf(u, false, c, __VA_ARGS__) #define printf(c, ...) cprintf(u, false, c, __VA_ARGS__)
#define fatal(e, ...) \ #define fatal(e, ...) \
do { if(err) *err = e; \ do { if(out_err) *out_err = e; \
cprintf(u, true, GREY, __VA_ARGS__); \ cprintf(u, true, GREY, __VA_ARGS__); \
free(cbcmacs); \
sb_free(sb_file); \ sb_free(sb_file); \
return NULL; } while(0) return NULL; } while(0)
struct printer_t printer = {.user = u, .cprintf = cprintf, .color = OFF, .error = false }; struct printer_t printer = {.user = u, .cprintf = cprintf, .color = OFF, .error = false };
#define print_hex(c, p, len, nl) \ #define print_hex(c, p, len, nl) \
do { printer.color = c; print_hex(&printer, sb_printer, p, len, nl); } while(0) do { printer.color = c; print_hex(&printer, sb_printer, p, len, nl); } while(0)
#define check_crypto(expr) \
do { int err = expr; \
if(err != CRYPTO_ERROR_SUCCESS) \
fatal(SB_CRYPTO_ERROR, "Crypto error: %d\n", err); } while(0)
struct sha_1_params_t sha_1_params; struct sha_1_params_t sha_1_params;
byte (*cbcmacs)[16] = xmalloc(16 * g_nr_keys);
sb_file = xmalloc(sizeof(struct sb_file_t)); sb_file = xmalloc(sizeof(struct sb_file_t));
memset(sb_file, 0, sizeof(struct sb_file_t)); memset(sb_file, 0, sizeof(struct sb_file_t));
struct sb_header_t *sb_header = (struct sb_header_t *)buf; struct sb_header_t *sb_header = (struct sb_header_t *)buf;
@ -826,12 +857,12 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
printf(YELLOW, "0x%08x\n", sb_header->first_boot_sec_id); printf(YELLOW, "0x%08x\n", sb_header->first_boot_sec_id);
/* encryption cbc-mac */ /* encryption cbc-mac */
byte real_key[16]; struct crypto_key_t real_key;
real_key.method = CRYPTO_KEY;
bool valid_key = false; /* false until a matching key was found */ bool valid_key = false; /* false until a matching key was found */
if(sb_header->nr_keys > 0) if(sb_header->nr_keys > 0)
{ {
byte (*cbcmacs)[16] = xmalloc(16 * g_nr_keys);
printf(BLUE, "Encryption keys\n"); printf(BLUE, "Encryption keys\n");
for(int i = 0; i < g_nr_keys; i++) for(int i = 0; i < g_nr_keys; i++)
{ {
@ -843,13 +874,9 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
/* check it */ /* check it */
byte zero[16]; byte zero[16];
memset(zero, 0, 16); memset(zero, 0, 16);
int ret = crypto_cbc(buf, NULL, sb_header->header_size + sb_header->nr_sections, check_crypto(crypto_setup(&g_key_array[i]));
&g_key_array[i], zero, &cbcmacs[i], 1); check_crypto(crypto_apply(buf, NULL, sb_header->header_size +
if(ret != CRYPTO_ERROR_SUCCESS) sb_header->nr_sections, zero, &cbcmacs[i], true));
{
free(cbcmacs);
fatal(SB_FIRST_CRYPTO_ERROR + ret, "Crypto error: %d", ret);
}
print_hex(YELLOW, cbcmacs[i], 16, true); print_hex(YELLOW, cbcmacs[i], 16, true);
} }
@ -878,24 +905,20 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
byte decrypted_key[16]; byte decrypted_key[16];
byte iv[16]; byte iv[16];
memcpy(iv, buf, 16); /* uses the first 16-bytes of SHA-1 sig as IV */ memcpy(iv, buf, 16); /* uses the first 16-bytes of SHA-1 sig as IV */
int ret = crypto_cbc(dict_entry->key, decrypted_key, 1, &g_key_array[idx], iv, NULL, 0); check_crypto(crypto_setup(&g_key_array[idx]));
if(ret != CRYPTO_ERROR_SUCCESS) check_crypto(crypto_apply(dict_entry->key, decrypted_key, 1, iv, NULL, false));
{
free(cbcmacs);
fatal(SB_FIRST_CRYPTO_ERROR + ret, "Crypto error: %d\n", ret);
}
printf(GREEN, " Decrypted key: "); printf(GREEN, " Decrypted key: ");
print_hex(YELLOW, decrypted_key, 16, false); print_hex(YELLOW, decrypted_key, 16, false);
if(valid_key) if(valid_key)
{ {
if(memcmp(real_key, decrypted_key, 16) == 0) if(memcmp(real_key.u.key, decrypted_key, 16) == 0)
printf(RED, " Cross-Check Ok"); printf(RED, " Cross-Check Ok");
else else
printf(RED, " Cross-Check Failed"); printf(RED, " Cross-Check Failed");
} }
else else
{ {
memcpy(real_key, decrypted_key, 16); memcpy(real_key.u.key, decrypted_key, 16);
valid_key = true; valid_key = true;
} }
printf(OFF, "\n"); printf(OFF, "\n");
@ -904,8 +927,6 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
printf(RED, " Don't Match\n"); printf(RED, " Don't Match\n");
} }
free(cbcmacs);
if(!valid_key) if(!valid_key)
{ {
if(g_force) if(g_force)
@ -916,11 +937,9 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
if(getenv("SB_REAL_KEY") != 0) if(getenv("SB_REAL_KEY") != 0)
{ {
struct crypto_key_t k;
char *env = getenv("SB_REAL_KEY"); char *env = getenv("SB_REAL_KEY");
if(!parse_key(&env, &k) || *env) if(!parse_key(&env, &real_key) || *env)
fatal(SB_ERROR, "Invalid SB_REAL_KEY\n"); fatal(SB_ERROR, "Invalid SB_REAL_KEY\n");
memcpy(real_key, k.u.key, 16);
/* assume the key is valid */ /* assume the key is valid */
if(valid_key) if(valid_key)
printf(GREY, " Overriding real key\n"); printf(GREY, " Overriding real key\n");
@ -931,16 +950,17 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
printf(RED, " Summary:\n"); printf(RED, " Summary:\n");
printf(GREEN, " Real key: "); printf(GREEN, " Real key: ");
print_hex(YELLOW, real_key, 16, true); print_hex(YELLOW, real_key.u.key, 16, true);
printf(GREEN, " IV : "); printf(GREEN, " IV : ");
print_hex(YELLOW, buf, 16, true); print_hex(YELLOW, buf, 16, true);
memcpy(sb_file->real_key, real_key, 16); memcpy(sb_file->real_key, real_key.u.key, 16);
memcpy(sb_file->crypto_iv, buf, 16); memcpy(sb_file->crypto_iv, buf, 16);
/* setup real key if needed */
check_crypto(crypto_setup(&real_key));
} }
else else
valid_key = true; valid_key = true;
/* sections */ /* sections */
if(!(flags & SB_RAW_MODE)) if(!(flags & SB_RAW_MODE))
{ {
@ -986,12 +1006,13 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
/* save it */ /* save it */
byte *sec = xmalloc(size); byte *sec = xmalloc(size);
if(encrypted) if(encrypted)
cbc_mac(buf + pos, sec, size / BLOCK_SIZE, real_key, buf, NULL, 0); check_crypto(crypto_apply(buf + pos, sec, size / BLOCK_SIZE, buf, NULL, false));
else else
memcpy(sec, buf + pos, size); memcpy(sec, buf + pos, size);
struct sb_section_t *s = read_section(data_sec, sec_hdr->identifier, struct sb_section_t *s = read_section(data_sec, sec_hdr->identifier,
sec, size, " ", u, cprintf, err); sec, size, " ", u, cprintf, out_err);
free(sec);
if(s) if(s)
{ {
s->other_flags = sec_hdr->flags & ~SECTION_STD_MASK; s->other_flags = sec_hdr->flags & ~SECTION_STD_MASK;
@ -1001,9 +1022,7 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
free(s); free(s);
} }
else else
fatal(*err, "Error reading section\n"); fatal(*out_err, "Error reading section\n");
free(sec);
} }
} }
else if(valid_key) else if(valid_key)
@ -1019,7 +1038,7 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
memcpy(iv, buf, 16); memcpy(iv, buf, 16);
byte cmd[BLOCK_SIZE]; byte cmd[BLOCK_SIZE];
if(sb_header->nr_keys > 0) if(sb_header->nr_keys > 0)
cbc_mac(buf + offset, cmd, 1, real_key, iv, &iv, 0); check_crypto(crypto_apply(buf + offset, cmd, 1, iv, &iv, false));
else else
memcpy(cmd, buf + offset, BLOCK_SIZE); memcpy(cmd, buf + offset, BLOCK_SIZE);
struct sb_instruction_header_t *hdr = (struct sb_instruction_header_t *)cmd; struct sb_instruction_header_t *hdr = (struct sb_instruction_header_t *)cmd;
@ -1077,12 +1096,13 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
/* save it */ /* save it */
byte *sec = xmalloc(size); byte *sec = xmalloc(size);
if(encrypted) if(encrypted)
cbc_mac(buf + pos, sec, size / BLOCK_SIZE, real_key, buf, NULL, 0); check_crypto(crypto_apply(buf + pos, sec, size / BLOCK_SIZE, buf, NULL, false));
else else
memcpy(sec, buf + pos, size); memcpy(sec, buf + pos, size);
struct sb_section_t *s = read_section(data_sec, tag->identifier, struct sb_section_t *s = read_section(data_sec, tag->identifier,
sec, size, " ", u, cprintf, err); sec, size, " ", u, cprintf, out_err);
free(sec);
if(s) if(s)
{ {
s->other_flags = tag->flags & ~SECTION_STD_MASK; s->other_flags = tag->flags & ~SECTION_STD_MASK;
@ -1094,8 +1114,7 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
free(s); free(s);
} }
else else
fatal(*err, "Error reading section\n"); fatal(*out_err, "Error reading section\n");
free(sec);
/* last one ? */ /* last one ? */
if(tag->hdr.flags & SB_INST_LAST_TAG) if(tag->hdr.flags & SB_INST_LAST_TAG)
@ -1126,7 +1145,7 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
printf(OFF, " "); printf(OFF, " ");
print_hex(YELLOW, encrypted_block + 16, 16, true); print_hex(YELLOW, encrypted_block + 16, 16, true);
/* decrypt it */ /* decrypt it */
cbc_mac(encrypted_block, decrypted_block, 2, real_key, buf, NULL, 0); check_crypto(crypto_apply(encrypted_block, decrypted_block, 2, buf, NULL, false));
} }
else else
memcpy(decrypted_block, &buf[filesize - 32], 32); memcpy(decrypted_block, &buf[filesize - 32], 32);
@ -1153,6 +1172,7 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, unsigned flags, vo
fatal(SB_CHECKSUM_ERROR, "File SHA-1 error\n"); fatal(SB_CHECKSUM_ERROR, "File SHA-1 error\n");
} }
free(cbcmacs);
return sb_file; return sb_file;
#undef printf #undef printf
#undef fatal #undef fatal

View file

@ -232,8 +232,7 @@ enum sb_error_t
SB_FORMAT_ERROR = -5, SB_FORMAT_ERROR = -5,
SB_CHECKSUM_ERROR = -6, SB_CHECKSUM_ERROR = -6,
SB_NO_VALID_KEY = -7, SB_NO_VALID_KEY = -7,
SB_FIRST_CRYPTO_ERROR = -8, SB_CRYPTO_ERROR = -8,
SB_LAST_CRYPTO_ERROR = SB_FIRST_CRYPTO_ERROR - CRYPTO_NUM_ERRORS,
}; };
#define SB_RAW_MODE (1 << 0) /* read image in raw mode (aka bootloader-like) */ #define SB_RAW_MODE (1 << 0) /* read image in raw mode (aka bootloader-like) */

View file

@ -139,8 +139,7 @@ enum sb1_error_t
SB1_FORMAT_ERROR = -5, SB1_FORMAT_ERROR = -5,
SB1_CHECKSUM_ERROR = -6, SB1_CHECKSUM_ERROR = -6,
SB1_NO_VALID_KEY = -7, SB1_NO_VALID_KEY = -7,
SB1_FIRST_CRYPTO_ERROR = -8, SB1_CRYPTO_ERROR = -8,
SB1_LAST_CRYPTO_ERROR = SB1_FIRST_CRYPTO_ERROR - CRYPTO_NUM_ERRORS,
}; };
enum sb1_error_t sb1_write_file(struct sb1_file_t *sb, const char *filename); enum sb1_error_t sb1_write_file(struct sb1_file_t *sb, const char *filename);