diff --git a/core/Makefile b/core/Makefile index 058e6c1c9..ccb5a699f 100644 --- a/core/Makefile +++ b/core/Makefile @@ -19,6 +19,7 @@ CROSS_PORT_OPTS ?= PRODUCTION ?= 0 PYOPT ?= 1 BITCOIN_ONLY ?= 0 +RDI ?= 1 STLINK_VER ?= v2 OPENOCD = openocd -f interface/stlink-$(STLINK_VER).cfg -c "transport select hla_swd" -f target/stm32f4x.cfg @@ -134,7 +135,7 @@ build_reflash: ## build reflash firmware + reflash image dd if=build/bootloader/bootloader.bin of=$(REFLASH_BUILD_DIR)/sdimage.bin bs=1 seek=49152 build_firmware: res build_cross ## build firmware with frozen modules - $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" $(FIRMWARE_BUILD_DIR)/firmware.bin + $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" RDI="$(RDI)" $(FIRMWARE_BUILD_DIR)/firmware.bin build_unix: res ## build unix port $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/micropython $(UNIX_PORT_OPTS) BITCOIN_ONLY="$(BITCOIN_ONLY)" diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 39df3241c..ee6fae2ed 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -3,6 +3,7 @@ import os BITCOIN_ONLY = ARGUMENTS.get('BITCOIN_ONLY', '0') +RDI = ARGUMENTS.get('RDI', '1') == '1' EVERYTHING = BITCOIN_ONLY != '1' CCFLAGS_MOD = '' @@ -336,6 +337,12 @@ SOURCE_TREZORHAL = [ 'embed/trezorhal/vectortable.s', ] +if RDI: + SOURCE_TREZORHAL += [ + 'embed/trezorhal/rdi.c', + ] + CPPDEFINES_MOD += ['RDI'] + SOURCE_QSTR = SOURCE_MOD + SOURCE_MICROPYTHON + SOURCE_MICROPYTHON_SPEED env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s -DPYOPT=%s -DBITCOIN_ONLY=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0'), PYOPT, BITCOIN_ONLY)) diff --git a/core/embed/firmware/main.c b/core/embed/firmware/main.c index 0438e1766..96db68131 100644 --- a/core/embed/firmware/main.c +++ b/core/embed/firmware/main.c @@ -40,6 +40,9 @@ #include "display.h" #include "flash.h" #include "mpu.h" +#ifdef RDI +#include "rdi.h" +#endif #include "rng.h" #include "sdcard.h" #include "supervise.h" @@ -48,6 +51,9 @@ int main(void) { // initialize pseudo-random number generator drbg_init(); +#ifdef RDI + rdi_start(); +#endif // reinitialize HAL for Trezor One #if TREZOR_MODEL == 1 diff --git a/core/embed/trezorhal/rdi.c b/core/embed/trezorhal/rdi.c new file mode 100644 index 000000000..9f2d1348e --- /dev/null +++ b/core/embed/trezorhal/rdi.c @@ -0,0 +1,143 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* +Random delay interrupts (RDI) is a contermeasure agains side channel attacks. It +consists of an interrupt handler that is supposed to be called every millisecond +or so. The handler waits for a random number of cpu ticks that is a sample of so +called floating mean distribution. That means that the number is the sum of two +numbers generated uniformly at random in the interval [0, 255]. The first number +is generated freshly for each call of the handler, the other number is supposed +to be refreshed when the device performs an operation that leaks the current +state of the execution flow, such as sending or receiving an usb packet. + +See Differential Power Analysis in the Presence of Hardware Countermeasures by +Christophe Clavier, Jean-Sebastien Coron, Nora Dabbous and Efficient Use of +Random Delays in Embedded Software by Michael Tunstall, Olivier Benoit: +https://link.springer.com/content/pdf/10.1007%2F3-540-44499-8_20.pdf +https://link.springer.com/content/pdf/10.1007%2F978-3-540-72354-7_3.pdf +*/ + +#include "rdi.h" + +#include + +#include "chacha_drbg.h" +#include "common.h" +#include "memzero.h" +#include "rand.h" +#include "secbool.h" + +#define BUFFER_LENGTH 64 +#define RESEED_INTERVAL 65536 + +static CHACHA_DRBG_CTX drbg_ctx; +static uint8_t buffer[BUFFER_LENGTH]; +static size_t buffer_index; +static uint8_t session_delay; +static bool refresh_session_delay; +static secbool rdi_disabled = sectrue; + +static void rdi_reseed(void) { + uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]; + random_buffer(entropy, CHACHA_DRBG_SEED_LENGTH); + chacha_drbg_reseed(&drbg_ctx, entropy); +} + +static void buffer_refill(void) { + chacha_drbg_generate(&drbg_ctx, buffer, BUFFER_LENGTH); +} + +static uint32_t random8(void) { + buffer_index += 1; + if (buffer_index >= BUFFER_LENGTH) { + buffer_refill(); + if (RESEED_INTERVAL != 0 && drbg_ctx.reseed_counter > RESEED_INTERVAL) + rdi_reseed(); + buffer_index = 0; + } + return buffer[buffer_index]; +} + +void rdi_refresh_session_delay(void) { + if (rdi_disabled == secfalse) // if rdi enabled + refresh_session_delay = true; +} + +void rdi_handler(uint32_t uw_tick) { + if (rdi_disabled == secfalse) { // if rdi enabled + if (refresh_session_delay) { + session_delay = random8(); + refresh_session_delay = false; + } + + uint32_t delay = random8() + session_delay; + + // wait (30 + delay) ticks + asm volatile( + "ldr r0, %0;" // r0 = delay + "loop:" + "subs r0, $3;" // r0 -= 3 + "bhs loop;" // if (r0 >= 3): goto loop + // loop (delay // 3) times + // every loop takes 3 ticks + // r0 == (delay % 3) - 3 + "add r0, $3;" // r0 += 3 + // r0 == delay % 3 + "and r0, r0, $3;" // r0 %= 4, make sure that 0 <= r0 < 4 + "ldr r1, =table;" // r1 = &table + "tbb [r1, r0];" // jump 2*r1[r0] bytes forward, that is goto wait_r0 + "base:" + "table:" // table of branch lengths + ".byte (wait_0 - base)/2;" + ".byte (wait_1 - base)/2;" + ".byte (wait_2 - base)/2;" + ".byte (wait_2 - base)/2;" // next instruction must be 2-byte aligned + "wait_2:" + "add r0, $1;" // wait one tick + "wait_1:" + "add r0, $1;" // wait one tick + "wait_0:" + : + : "m"(delay) + : "r0", "r1"); + } else { // if rdi disabled or rdi_disabled corrupted + ensure(rdi_disabled, "Fault detected"); + } +} + +void rdi_start(void) { + if (rdi_disabled == sectrue) { // if rdi disabled + uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]; + random_buffer(entropy, CHACHA_DRBG_SEED_LENGTH); + chacha_drbg_init(&drbg_ctx, entropy); + buffer_refill(); + buffer_index = 0; + refresh_session_delay = true; + rdi_disabled = secfalse; + } +} + +void rdi_stop(void) { + if (rdi_disabled == secfalse) { // if rdi enabled + rdi_disabled = sectrue; + session_delay = 0; + memzero(&drbg_ctx, sizeof(drbg_ctx)); + } +} diff --git a/core/embed/trezorhal/rdi.h b/core/embed/trezorhal/rdi.h new file mode 100644 index 000000000..0b2da982c --- /dev/null +++ b/core/embed/trezorhal/rdi.h @@ -0,0 +1,29 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __TREZORHAL_RDI_H__ +#define __TREZORHAL_RDI_H__ + +#include + +void rdi_start(void); +void rdi_stop(void); +void rdi_refresh_session_delay(void); +void rdi_handler(uint32_t uw_tick); +#endif diff --git a/core/embed/trezorhal/systick.c b/core/embed/trezorhal/systick.c index a4b08e5a5..7320a653f 100644 --- a/core/embed/trezorhal/systick.c +++ b/core/embed/trezorhal/systick.c @@ -48,6 +48,10 @@ #include "irq.h" #include "systick.h" +#ifdef RDI + #include "rdi.h" +#endif + extern __IO uint32_t uwTick; systick_dispatch_t systick_dispatch_table[SYSTICK_DISPATCH_NUM_SLOTS]; @@ -57,6 +61,9 @@ void SysTick_Handler(void) { // 49.71 days = (0xffffffff / (24 * 60 * 60 * 1000)) uint32_t uw_tick = uwTick + 1; uwTick = uw_tick; +#ifdef RDI + rdi_handler(uw_tick); +#endif systick_dispatch_t f = systick_dispatch_table[uw_tick & (SYSTICK_DISPATCH_NUM_SLOTS - 1)]; if (f != NULL) { f(uw_tick); diff --git a/core/embed/trezorhal/usb.c b/core/embed/trezorhal/usb.c index 0183a70ce..7c9519c75 100644 --- a/core/embed/trezorhal/usb.c +++ b/core/embed/trezorhal/usb.c @@ -21,6 +21,7 @@ #include "usb.h" #include "common.h" +#include "rdi.h" #include "usbd_core.h" #define USB_MAX_CONFIG_DESC_SIZE 256 @@ -468,6 +469,9 @@ static uint8_t usb_class_setup(USBD_HandleTypeDef *dev, } static uint8_t usb_class_data_in(USBD_HandleTypeDef *dev, uint8_t ep_num) { +#ifdef RDI + rdi_refresh_session_delay(); +#endif for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) { switch (usb_ifaces[i].type) { case USB_IFACE_TYPE_HID: @@ -490,6 +494,9 @@ static uint8_t usb_class_data_in(USBD_HandleTypeDef *dev, uint8_t ep_num) { } static uint8_t usb_class_data_out(USBD_HandleTypeDef *dev, uint8_t ep_num) { +#ifdef RDI + rdi_refresh_session_delay(); +#endif for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) { switch (usb_ifaces[i].type) { case USB_IFACE_TYPE_HID: