firmware/stm32/bootloader/dispatch.c
2020-11-18 14:19:14 -05:00

768 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
*
* dispatch.c
*
* This code runs in an area of flash protected from viewing. It has limited entry
* point (via a special callgate) and checks state carefully before running other stuff.
*
*/
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include "basics.h"
#include "misc.h"
#include "sha256.h"
#include "version.h"
#include "clocks.h"
#include "oled.h"
#include "delay.h"
#include "rng.h"
#include "gpio.h"
#include "ae.h"
#include "pins.h"
#include "verify.h"
#include "storage.h"
#include "sflash.h"
#include "dispatch.h"
#include "constant_time.h"
#include "assets/screens.h"
#include "stm32l4xx_hal.h"
// This magic value indicates we should go direct into DFU on this reboot.
// Arbitrary SRAM1 location, random magic values. Also what screen to show.
static const char *REBOOT_TO_DFU = "Boot2DFU";
typedef struct {
char magic[8];
const uint8_t *screen;
} dfu_flag_t;
#define dfu_flag ((dfu_flag_t *)0x20008000)
// reboot_seed_setup()
//
// We need to know when we are rebooted, so write some noise
// into SRAM and lock its value. Not secrets. One page = 1k bytes here.
//
void
reboot_seed_setup(void)
{
extern uint8_t reboot_seed_base[1024]; // see link-script.ld
// lots of manual memory alloc here...
uint8_t *reboot_seed = &reboot_seed_base[0]; // 32 bytes
coldcardFirmwareHeader_t *hdr_copy = (void *)&reboot_seed_base[32];
uint32_t *boot_flags = (uint32_t *)RAM_BOOT_FLAGS;
// can only do this once, and might be done already
if(SYSCFG->SWPR != (1<<31)) {
ASSERT(((uint32_t)reboot_seed) == 0x10007c00);
ASSERT(((uint32_t)hdr_copy) == RAM_HEADER_BASE);
// populate seed w/ noise
memset(reboot_seed, 0x55, 1024);
rng_buffer(reboot_seed, 32);
// preserve a copy of the verified FW header
memcpy(hdr_copy, FW_HDR, sizeof(coldcardFirmwareHeader_t));
// document how we booted.
uint32_t fl = 0;
if(!flash_is_security_level2()) {
fl |= RBF_FACTORY_MODE;
}
if(sf_completed_upgrade == SF_COMPLETED_UPGRADE) {
fl |= RBF_FRESH_VERSION;
}
*boot_flags = fl;
// lock it (top most page = 1k bytes)
SYSCFG->SWPR = (1<<31);
}
}
// memset4()
//
static inline void
memset4(uint32_t *dest, uint32_t value, uint32_t byte_len)
{
for(; byte_len; byte_len-=4, dest++) {
*dest = value;
}
}
// wipe_all_sram()
//
static void
wipe_all_sram(void)
{
const uint32_t noise = 0xdeadbeef;
// wipe all of SRAM (except our own memory, which was already wiped)
memset4((void *)SRAM1_BASE, noise, SRAM1_SIZE_MAX);
memset4((void *)SRAM2_BASE, noise, SRAM2_SIZE - BL_SRAM_SIZE);
}
// system_startup()
//
// Called only on system boot.
//
void
system_startup(void)
{
// configure clocks first
clocks_setup();
#if RELEASE
// security check: should we be in protected mode? Was there some UV-C bitrot perhaps?
if(!check_all_ones(rom_secrets->bag_number, sizeof(rom_secrets->bag_number))
&& !flash_is_security_level2()
) {
// yikes. recovery: do lockdown... we should be/(thought we were) locked already
flash_lockdown_hard(OB_RDP_LEVEL_2);
}
#endif
// workaround to get into DFU from micropython
// LATER: none of this is useful with RDP=2
if(memcmp(dfu_flag->magic, REBOOT_TO_DFU, sizeof(dfu_flag->magic)) == 0) {
dfu_flag->magic[0] = 0;
// still see a flash here, but that's proof it works.
oled_setup();
oled_show(dfu_flag->screen);
enter_dfu();
// NOT-REACHED
}
// clear and setup OLED display
oled_setup();
oled_show_progress(screen_verify, 0);
// won't always need it, but enable RNG anyway
rng_setup();
// wipe all of SRAM (except our own memory, which was already wiped)
wipe_all_sram();
// config pins
gpio_setup();
ae_setup();
ae_set_gpio(0); // not checking return on purpose
// protect our flash, and/or check it's protected
// - and pick pairing secret if we don't already have one
// - may also do one-time setup of 508a
// - note: ae_setup must already be called, since it can talk to that
flash_setup();
// escape into DFU
if(dfu_button_pressed()) dfu_by_request();
// maybe upgrade to a firmware image found in sflash
sf_firmware_upgrade();
// SLOW part: check firmware is legit; else enter DFU
// - may die due to downgrade attack or unsigned/badly signed image
verify_firmware();
// .. for slow people, check again; last chance
if(dfu_button_pressed()) dfu_by_request();
// track reboots, capture firmware hdr used
// - must be near end of boot process, ie: here.
reboot_seed_setup();
// load a blank screen, so that if the firmware crashes, we are showing
// something reasonable and not misleading.
oled_show(screen_blankish);
}
// fatal_error(const char *msg)
//
void
fatal_error(const char *msgvoid)
{
oled_setup();
oled_show(screen_fatal);
// Maybe should do a reset after a delay, like with
// the watchdog timer or something.
LOCKUP_FOREVER();
}
// fatal_mitm()
//
void
fatal_mitm(void)
{
oled_setup();
oled_show(screen_mitm);
#ifdef RELEASE
wipe_all_sram();
#endif
LOCKUP_FOREVER();
}
// dfu_by_request()
//
void
dfu_by_request(void)
{
if(flash_is_security_level2()) {
// cannot get into DFU when secure
// so do nothing
return;
}
oled_show(screen_dfu);
enter_dfu();
}
// enter_dfu()
//
void __attribute__((noreturn))
enter_dfu(void)
{
const uint32_t noise = 0xDeadBeef;
// clear the green light, if set
ae_setup();
ae_set_gpio(0);
// Reset huge parts of the chip
__HAL_RCC_APB1_FORCE_RESET();
__HAL_RCC_APB1_RELEASE_RESET();
__HAL_RCC_APB2_FORCE_RESET();
__HAL_RCC_APB2_RELEASE_RESET();
__HAL_RCC_AHB1_FORCE_RESET();
__HAL_RCC_AHB1_RELEASE_RESET();
#if 0
// But not this; it borks things.
__HAL_RCC_AHB2_FORCE_RESET();
__HAL_RCC_AHB2_RELEASE_RESET();
#endif
__HAL_RCC_AHB3_FORCE_RESET();
__HAL_RCC_AHB3_RELEASE_RESET();
__HAL_FIREWALL_PREARM_ENABLE();
// Wipe all of memory SRAM, just in case
// there is some way to trick us into DFU
// after sensitive content in place.
memset4((void *)SRAM1_BASE, noise, SRAM1_SIZE_MAX);
memset4((void *)SRAM2_BASE, noise, SRAM2_SIZE - 1024); // avoid seed area
if(flash_is_security_level2()) {
// cannot do DFU in RDP=2, so just die. Helps to preserve screen
LOCKUP_FOREVER();
}
// Reset clocks.
HAL_RCC_DeInit();
// move system ROM into 0x0
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
// simulate a reset vector
__ASM volatile ("movs r0, #0\n"
"ldr r3, [r0, #0]\n"
"msr msp, r3\n"
"ldr r3, [r0, #4]\n"
"blx r3"
: : : "r0", "r3", "sp");
// NOT-REACHED.
__builtin_unreachable();
}
// good_addr()
//
static int
good_addr(const uint8_t *b, int minlen, int len, bool readonly)
{
uint32_t x = (uint32_t)b;
if(minlen) {
if(!b) return EFAULT; // gave no buffer
if(len < minlen) return ERANGE; // too small
}
if((x >= SRAM1_BASE) && ((x-SRAM1_BASE) < SRAM1_SIZE_MAX)) {
// inside SRAM1, okay
return 0;
}
if(!readonly) {
return EPERM;
}
if((x >= FIRMWARE_START) && (x - FIRMWARE_START) < FW_MAX_LENGTH) {
// inside flash of main firmware (happens for QSTR's)
return 0;
}
return EACCES;
}
/*
The callgate into the firewall...
From the reference manual:
The “call gate” is composed of 3 words located on the first three
32-bit addresses of the base address of the code segment and of the
Volatile data segment if it is declared as not shared (VDS = 0) and
executable (VDE = 1).
1st word: Dummy 32-bit words always closed in order to protect
the “call gate” opening from an access due to a prefetch buffer.
2nd and 3rd words: 2 specific 32-bit words called “call gate” and always opened.
We are assuming the caller gives us a working C runtime: stack, arguments in
registers and so on.
*/
// firewall_dispatch()
//
// A C-runtime compatible env. is running, so do some work.
//
__attribute__ ((used))
int
firewall_dispatch(int method_num, uint8_t *buf_io, int len_in,
uint32_t arg2, uint32_t incoming_sp, uint32_t incoming_lr)
{
// from linker, offset of firewall entry
extern uint32_t firewall_starts;
int rv = 0;
#if 0
// TODO: re-enable this; causing crash now.
// in case the caller didn't already, but would just lead to a crash anyway
__disable_irq();
#endif
// "1=any code executed outside the protected segment will close the Firewall"
// "0=.. will reset the processor"
__HAL_FIREWALL_PREARM_DISABLE();
// Important:
// - range check pointers so we aren't tricked into revealing our secrets
// - check buf_io points to main SRAM, and not into us!
// - range check len_in tightly
// - calling convention only gives me enough for 4 args to this function, so
// using read/write in place.
// - use arg2 use when a simple number is needed; never a pointer!
// - mpy may provide a pointer to flash if we give it a qstr or small value, and if
// we're reading only, that's fine.
if(len_in > 1024) { // arbitrary max, increase as needed
rv = ERANGE;
goto fail;
}
// Use these macros
#define REQUIRE_IN_ONLY(x) if((rv = good_addr(buf_io, (x), len_in, true))) { goto fail; }
#define REQUIRE_OUT(x) if((rv = good_addr(buf_io, (x), len_in, false))) { goto fail; }
switch(method_num) {
case 0: {
REQUIRE_OUT(64);
// Return my version string
memset(buf_io, 0, len_in);
strlcpy((char *)buf_io, version_string, len_in);
rv = strlen(version_string);
break;
}
case 1: {
// Perform SHA256 over ourselves, with 32-bits of salt, to imply we
// haven't stored valid responses.
REQUIRE_OUT(32);
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx, (void *)&arg2, 4);
sha256_update(&ctx, (void *)BL_FLASH_BASE, BL_FLASH_SIZE);
sha256_final(&ctx, buf_io);
break;
}
case 2: {
const uint8_t *scr;
bool secure = flash_is_security_level2();
// Go into DFU mode. It's a one-way trip.
// Also used to show some "fatal" screens w/ memory wipe.
switch(arg2) {
default:
case 0:
// enter DFU for firmware upgrades
if(secure) {
// we cannot support DFU in secure mode anymore
rv = EPERM;
goto fail;
}
scr = screen_dfu;
break;
case 1:
// in case some way for Micropython to detect it.
scr = screen_downgrade;
break;
case 2:
scr = screen_blankish;
break;
case 3:
scr = screen_brick;
secure = true; // no point going into DFU, if even possible
break;
}
oled_setup();
oled_show(scr);
wipe_all_sram();
if(secure) {
// just die with that message shown; can't start DFU
LOCKUP_FOREVER();
} else {
// Cannot just call enter_dfu() because it doesn't work well
// once Micropython has configured so much stuff in the chip.
// Leave a reminder to ourselves
memcpy(dfu_flag->magic, REBOOT_TO_DFU, sizeof(dfu_flag->magic));
dfu_flag->screen = scr;
// reset system
NVIC_SystemReset();
// NOT-REACHED
}
break;
}
case 3:
// logout: wipe all of memory and lock up. Must powercycle to recover.
switch(arg2) {
case 0:
case 2:
oled_show(screen_logout);
break;
case 1:
// leave screen untouched
break;
}
wipe_all_sram();
if(arg2 == 2) {
// need some time to show OLED contents
delay_ms(100);
// reboot so we can "login" again
NVIC_SystemReset();
// NOT-REACHED (but ok if it does)
}
// wait for an interrupt which will never happen (ie. sleep)
LOCKUP_FOREVER()
break;
case 4:
// attempt to control the GPIO (won't work for 1)
ae_setup();
ae_keep_alive();
switch(arg2) {
default:
case 0: // read state
rv = ae_get_gpio();
break;
case 1: // clear it (can work anytime)
rv = ae_set_gpio(0);
break;
case 2: // set it (will always fail)
rv = ae_set_gpio(1);
break;
case 3: { // do a verify and see if it maybe goes green
uint8_t fw_digest[32], world_digest[32];
// takes time, shows progress bar
checksum_flash(fw_digest, world_digest);
rv = ae_set_gpio_secure(world_digest);
oled_show(screen_blankish);
break;
}
}
break;
case 5:
// Are we a brick?
// if the pairing secret doesn't work anymore, that
// means we've been bricked.
// TODO: also report hardware issue, and non-configured states
ae_setup();
rv = (ae_pair_unlock() != 0);
break;
case 6:
// Do we have a ATECC608a and all that implies?
// NOTE: this number was unused in V1 bootroms, so return ENOENT
#if FOR_608
rv = 0;
#else
rv = ENOENT;
#endif
break;
case 12:
// read the DFU button (used for selftest at least)
REQUIRE_OUT(1);
gpio_setup();
buf_io[0] = dfu_button_pressed();
break;
case 15: {
// Read a dataslot directly. Will fail on
// encrypted slots.
if(len_in != 4 && len_in != 32 && len_in != 72) {
rv = ERANGE;
} else {
REQUIRE_OUT(4);
ae_setup();
if(ae_read_data_slot(arg2 & 0xf, buf_io, len_in)) {
rv = EIO;
}
}
break;
}
case 16: {
// Provide the 2 words for anti-phishing.
REQUIRE_OUT(MAX_PIN_LEN);
// arg2: length of pin.
if((arg2 < 1) || (arg2 > MAX_PIN_LEN)) {
rv = ERANGE;
} else {
if(pin_prefix_words((char *)buf_io, arg2, (uint32_t *)buf_io)) {
rv = EIO;
}
}
break;
}
case 17:
// test rng
REQUIRE_OUT(32);
memset(buf_io, 0x55, 32); // to help show errors
rng_buffer(buf_io, 32);
break;
case 18: {
// Try login w/ PIN.
REQUIRE_OUT(PIN_ATTEMPT_SIZE_V2);
pinAttempt_t *args = (pinAttempt_t *)buf_io;
switch(arg2) {
case 0:
rv = pin_setup_attempt(args);
break;
case 1:
rv = pin_delay(args);
break;
case 2:
rv = pin_login_attempt(args);
break;
case 3:
rv = pin_change(args);
break;
case 4:
rv = pin_fetch_secret(args);
break;
case 5:
rv = pin_firmware_greenlight(args);
break;
case 6: // new for v2
rv = pin_long_secret(args);
break;
default:
rv = ENOENT;
break;
}
break;
}
case 19: { // bag number stuff
switch(arg2) {
case 0:
// read out number
REQUIRE_OUT(32);
memcpy(buf_io, rom_secrets->bag_number, 32);
break;
case 1:
// set the bag number, and (should) do lock down
REQUIRE_IN_ONLY(32);
flash_save_bag_number(buf_io);
break;
case 100:
flash_lockdown_hard(OB_RDP_LEVEL_0); // wipes contents of flash (1->0)
break;
case 101:
flash_lockdown_hard(OB_RDP_LEVEL_1); // Can only do 0->1 (experiments)
break;
case 102:
// production units will be:
flash_lockdown_hard(OB_RDP_LEVEL_2); // No change possible after this.
break;
default:
rv = ENOENT;
break;
}
break;
}
case 20:
// Read out entire config dataspace
REQUIRE_OUT(128);
ae_setup();
rv = ae_config_read(buf_io);
if(rv) {
rv = EIO;
}
break;
case 21:
// read OTP / downgrade protection
switch(arg2) {
case 0:
REQUIRE_OUT(8);
get_min_version(buf_io);
break;
case 1:
REQUIRE_IN_ONLY(8);
rv = check_is_downgrade(buf_io, NULL);
break;
case 2:
REQUIRE_IN_ONLY(8);
if(buf_io[0] < 0x10 || buf_io[0] >= 0x40) {
// bad data
rv = ERANGE;
} if(check_is_downgrade(buf_io, NULL)) {
// already at a higher version?
rv = EAGAIN;
} else {
uint8_t min[8];
get_min_version(min);
if(memcmp(min, buf_io, 8) == 0) {
// dupe
rv = EAGAIN;
} else {
// save it, but might be "full" already
if(record_highwater_version(buf_io)) {
rv = ENOMEM;
}
}
}
break;
case 3:
// read raw counter0 value (max is 0x1fffff)
REQUIRE_OUT(4);
ae_setup();
rv = ae_get_counter((uint32_t *)buf_io, 0) ? EIO: 0;
break;
default:
rv = ENOENT;
break;
}
break;
case -1:
// System startup code. Cannot be reached by any code (that hopes to run
// again) except our reset stub.
if(incoming_lr <= BL_FLASH_BASE || incoming_lr >= (uint32_t)&firewall_starts) {
fatal_error("LR");
} else {
system_startup();
}
break;
default:
rv = ENOENT;
break;
}
#undef REQUIRE_IN_ONLY
#undef REQUIRE_OUT
fail:
// Precaution: we don't want to leave ATECC508A authorized for any specific keys,
// perhaps due to an error path we didn't see. Always reset the chip.
ae_reset_chip();
// Unlikely it matters, but clear flash memory cache.
__HAL_FLASH_DATA_CACHE_DISABLE();
__HAL_FLASH_DATA_CACHE_RESET();
__HAL_FLASH_DATA_CACHE_ENABLE();
// .. and instruction memory (flash cache too?)
__HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
__HAL_FLASH_INSTRUCTION_CACHE_RESET();
__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
// authorize return from firewall into user's code
__HAL_FIREWALL_PREARM_ENABLE();
return rv;
}
// HAL support garbage
const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
const uint32_t MSIRangeTable[12] = {100000, 200000, 400000, 800000, 1000000, 2000000, \
4000000, 8000000, 16000000, 24000000, 32000000, 48000000};
uint32_t SystemCoreClock;
// TODO: cleanup HAL stuff to not use this
uint32_t HAL_GetTick(void) { return 53; }
// EOF