Merge pull request #238 from Nitrokey/bootloader-downgrade-protection

Bootloader downgrade protection
This commit is contained in:
Conor Patrick 2019-10-08 13:53:57 -04:00 committed by GitHub
commit 9158453830
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 204 additions and 22 deletions

View File

@ -1775,8 +1775,14 @@ void ctap_load_external_keys(uint8_t * keybytes){
crypto_load_master_secret(STATE.key_space); crypto_load_master_secret(STATE.key_space);
} }
#include "version.h"
void ctap_init() void ctap_init()
{ {
printf1(TAG_ERR,"Current firmware version address: %p\r\n", &firmware_version);
printf1(TAG_ERR,"Current firmware version: %d.%d.%d.%d (%02x.%02x.%02x.%02x)\r\n",
firmware_version.major, firmware_version.minor, firmware_version.patch, firmware_version.reserved,
firmware_version.major, firmware_version.minor, firmware_version.patch, firmware_version.reserved
);
crypto_ecc256_init(); crypto_ecc256_init();
authenticator_read_state(&STATE); authenticator_read_state(&STATE);

13
fido2/version.c Normal file
View File

@ -0,0 +1,13 @@
#include "version.h"
const version_t firmware_version __attribute__ ((section (".flag"))) __attribute__ ((__used__)) = {
.major = SOLO_VERSION_MAJ,
.minor = SOLO_VERSION_MIN,
.patch = SOLO_VERSION_PATCH,
.reserved = 0
};
// from tinycbor, for a quick static_assert
#include <compilersupport_p.h>
cbor_static_assert(sizeof(version_t) == 4);

View File

@ -17,5 +17,23 @@
#define SOLO_VERSION __STR(SOLO_VERSION_MAJ) "." __STR(SOLO_VERSION_MIN) "." __STR(SOLO_VERSION_PATCH) #define SOLO_VERSION __STR(SOLO_VERSION_MAJ) "." __STR(SOLO_VERSION_MIN) "." __STR(SOLO_VERSION_PATCH)
#endif #endif
#include <stdint.h>
#include <stdbool.h>
typedef struct {
union{
uint32_t raw;
struct {
uint8_t major;
uint8_t minor;
uint8_t patch;
uint8_t reserved;
};
};
} version_t;
bool is_newer(const version_t* const newer, const version_t* const older);
const version_t firmware_version ;
#endif #endif

View File

@ -90,8 +90,7 @@ flash_dfu: solo.hex bootloader.hex
# STM32_Programmer_CLI -c port=usb1 -halt -e all --readunprotect # STM32_Programmer_CLI -c port=usb1 -halt -e all --readunprotect
STM32_Programmer_CLI -c port=usb1 -halt -rdu -d all.hex STM32_Programmer_CLI -c port=usb1 -halt -rdu -d all.hex
flashboot: solo.hex bootloader.hex flashboot: bootloader.hex
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst

View File

@ -19,6 +19,12 @@
#include "ctap_errors.h" #include "ctap_errors.h"
#include "log.h" #include "log.h"
volatile version_t current_firmware_version __attribute__ ((section (".flag2"))) __attribute__ ((__used__)) = {
.major = SOLO_VERSION_MAJ,
.minor = SOLO_VERSION_MIN,
.patch = SOLO_VERSION_PATCH,
.reserved = 0
};
extern uint8_t REBOOT_FLAG; extern uint8_t REBOOT_FLAG;
@ -56,8 +62,6 @@ static void erase_application()
} }
} }
#define LAST_ADDR (APPLICATION_END_ADDR-2048 + 8)
#define LAST_PAGE (APPLICATION_END_PAGE-1)
static void disable_bootloader() static void disable_bootloader()
{ {
// Clear last 4 bytes of the last application page-1, which is 108th // Clear last 4 bytes of the last application page-1, which is 108th
@ -102,6 +106,38 @@ int is_bootloader_disabled()
uint32_t * auth = (uint32_t *)(AUTH_WORD_ADDR+4); uint32_t * auth = (uint32_t *)(AUTH_WORD_ADDR+4);
return *auth == 0; return *auth == 0;
} }
uint8_t * last_written_app_address;
#include "version.h"
bool is_firmware_version_newer_or_equal()
{
printf1(TAG_BOOT,"Current firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n",
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved,
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved
);
volatile version_t * new_version = ((volatile version_t *) last_written_app_address);
printf1(TAG_BOOT,"Uploaded firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n",
new_version->major, new_version->minor, new_version->patch, new_version->reserved,
new_version->major, new_version->minor, new_version->patch, new_version->reserved
);
const bool allowed = is_newer(new_version, &current_firmware_version) || current_firmware_version.raw == 0xFFFFFFFF;
if (allowed){
printf1(TAG_BOOT, "Update allowed, setting new firmware version as current.\r\n");
// current_firmware_version.raw = new_version.raw;
uint8_t page[PAGE_SIZE];
memmove(page, (uint8_t*)BOOT_VERSION_ADDR, PAGE_SIZE);
memmove(page, new_version, 4);
printf1(TAG_BOOT, "Writing\r\n");
flash_erase_page(BOOT_VERSION_PAGE);
flash_write(BOOT_VERSION_ADDR, page, PAGE_SIZE);
printf1(TAG_BOOT, "Finish\r\n");
} else {
printf1(TAG_BOOT, "Firmware older - update not allowed.\r\n");
}
return allowed;
}
/** /**
* Execute bootloader commands * Execute bootloader commands
@ -125,10 +161,7 @@ int bootloader_bridge(int klen, uint8_t * keyh)
return CTAP1_ERR_INVALID_LENGTH; return CTAP1_ERR_INVALID_LENGTH;
} }
#ifndef SOLO_HACKER #ifndef SOLO_HACKER
uint8_t * pubkey = (uint8_t*)"\xd2\xa4\x2f\x8f\xb2\x31\x1c\xc1\xf7\x0c\x7e\x64\x32\xfb\xbb\xb4\xa3\xdd\x32\x20" extern uint8_t *pubkey_boot;
"\x0f\x1b\x88\x9c\xda\x62\xc2\x83\x25\x93\xdd\xb8\x75\x9d\xf9\x86\xee\x03\x6c\xce"
"\x34\x47\x71\x36\xb3\xb2\xad\x6d\x12\xb7\xbe\x49\x3e\x20\xa4\x61\xac\xc7\x71\xc7"
"\x1f\xa8\x14\xf2";
const struct uECC_Curve_t * curve = NULL; const struct uECC_Curve_t * curve = NULL;
#endif #endif
@ -165,12 +198,11 @@ int bootloader_bridge(int klen, uint8_t * keyh)
} }
// Do the actual write // Do the actual write
flash_write((uint32_t)ptr,req->payload, len); flash_write((uint32_t)ptr,req->payload, len);
last_written_app_address = (uint8_t *)ptr + len - 8 + 4;
break; break;
case BootDone: case BootDone:
// Writing to flash finished. Request code validation. // Writing to flash finished. Request code validation.
printf1(TAG_BOOT, "BootDone: "); printf1(TAG_BOOT, "BootDone: \r\n");
#ifndef SOLO_HACKER #ifndef SOLO_HACKER
if (len != 64) if (len != 64)
{ {
@ -185,17 +217,23 @@ int bootloader_bridge(int klen, uint8_t * keyh)
crypto_sha256_final(hash); crypto_sha256_final(hash);
curve = uECC_secp256r1(); curve = uECC_secp256r1();
// Verify incoming signature made over the SHA256 hash // Verify incoming signature made over the SHA256 hash
if (! uECC_verify(pubkey, if (
hash, !uECC_verify(pubkey_boot, hash, 32, req->payload, curve)
32, )
req->payload,
curve))
{ {
printf1(TAG_BOOT, "Signature invalid\r\n");
return CTAP2_ERR_OPERATION_DENIED;
}
if (!is_firmware_version_newer_or_equal()){
printf1(TAG_BOOT, "Firmware older - update not allowed.\r\n");
printf1(TAG_BOOT, "Rebooting...\r\n");
REBOOT_FLAG = 1;
return CTAP2_ERR_OPERATION_DENIED; return CTAP2_ERR_OPERATION_DENIED;
} }
#endif #endif
// Set the application validated, and mark for reboot. // Set the application validated, and mark for reboot.
authorize_application(); authorize_application();
REBOOT_FLAG = 1; REBOOT_FLAG = 1;
break; break;
case BootCheck: case BootCheck:
@ -218,6 +256,7 @@ int bootloader_bridge(int klen, uint8_t * keyh)
break; break;
case BootReboot: case BootReboot:
printf1(TAG_BOOT, "BootReboot.\r\n"); printf1(TAG_BOOT, "BootReboot.\r\n");
printf1(TAG_BOOT, "Application authorized: %d.\r\n", is_authorized_to_boot());
REBOOT_FLAG = 1; REBOOT_FLAG = 1;
break; break;
case BootDisable: case BootDisable:

View File

@ -138,6 +138,14 @@ int main()
printf1(TAG_GEN,"recv'ing hid msg \n"); printf1(TAG_GEN,"recv'ing hid msg \n");
extern volatile version_t current_firmware_version;
printf1(TAG_BOOT,"Current firmware version address: %p\r\n", &current_firmware_version);
printf1(TAG_BOOT,"Current firmware version: %d.%d.%d.%d (%02x.%02x.%02x.%02x)\r\n",
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved,
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved
);
dump_hex1(TAG_BOOT, (uint8_t*)(&current_firmware_version) - 16, 32);
while(1) while(1)
{ {

View File

@ -0,0 +1,6 @@
#include "stdint.h"
uint8_t * pubkey_boot = (uint8_t*)"\xd2\xa4\x2f\x8f\xb2\x31\x1c\xc1\xf7\x0c\x7e\x64\x32\xfb\xbb\xb4\xa3\xdd\x32\x20"
"\x0f\x1b\x88\x9c\xda\x62\xc2\x83\x25\x93\xdd\xb8\x75\x9d\xf9\x86\xee\x03\x6c\xce"
"\x34\x47\x71\x36\xb3\xb2\xad\x6d\x12\xb7\xbe\x49\x3e\x20\xa4\x61\xac\xc7\x71\xc7"
"\x1f\xa8\x14\xf2";

View File

@ -0,0 +1,8 @@
#include "version.h"
// FIXME test version check function
bool is_newer(const version_t* const newer, const version_t* const older){
return (newer->major > older->major) ||
(newer->major == older->major && newer->minor > older->minor) ||
(newer->major == older->major && newer->minor == older->minor && newer->patch >= older->patch);
}

View File

@ -10,6 +10,7 @@ SRC += $(DRIVER_LIBS) $(USB_LIB)
SRC += ../../fido2/apdu.c ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c SRC += ../../fido2/apdu.c ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
SRC += ../../fido2/version.c
SRC += ../../fido2/data_migration.c SRC += ../../fido2/data_migration.c
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
SRC += ../../fido2/extensions/wallet.c SRC += ../../fido2/extensions/wallet.c
@ -70,6 +71,7 @@ all: $(TARGET).elf
%.elf: $(OBJ) %.elf: $(OBJ)
$(CC) $^ $(HW) $(LDFLAGS) -o $@ $(CC) $^ $(HW) $(LDFLAGS) -o $@
@echo "Built version: $(VERSION_FLAGS)"
%.hex: %.elf %.hex: %.elf
$(SZ) $^ $(SZ) $^

View File

@ -2,6 +2,7 @@ include build/common.mk
# ST related # ST related
SRC = bootloader/main.c bootloader/bootloader.c SRC = bootloader/main.c bootloader/bootloader.c
SRC += bootloader/pubkey_bootloader.c bootloader/version_check.c
SRC += src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c SRC += src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
SRC += src/fifo.c src/crypto.c src/attestation.c src/sense.c SRC += src/fifo.c src/crypto.c src/attestation.c src/sense.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
@ -65,6 +66,7 @@ all: $(TARGET).elf
%.elf: $(OBJ) %.elf: $(OBJ)
$(CC) $^ $(HW) $(LDFLAGS) -o $@ $(CC) $^ $(HW) $(LDFLAGS) -o $@
arm-none-eabi-size $@
%.hex: %.elf %.hex: %.elf
$(CP) -O ihex $^ $(TARGET).hex $(CP) -O ihex $^ $(TARGET).hex

View File

@ -12,9 +12,17 @@ _estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400; _MIN_STACK_SIZE = 0x400;
/*
flash_cfg is for storing bootloader data, like last used firmware version.
bootloader_configuration should be equal to (APPLICATION_END_PAGE) page address, from targets/stm32l432/src/memory_layout.h:30; and equal to flash_cfg origin
*/
bootloader_configuration = 0x08000000 + 216*1024+8;
MEMORY MEMORY
{ {
flash (rx) : ORIGIN = 0x08000000, LENGTH = 20K flash (rx) : ORIGIN = 0x08000000, LENGTH = 20K
flash_cfg (rx) : ORIGIN = 0x08000000 + 216*1024+8, LENGTH = 2K-8
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
} }
@ -39,6 +47,11 @@ SECTIONS
_etext = .; _etext = .;
} >flash } >flash
.flag2 bootloader_configuration :
{
KEEP(*(.flag2)) ;
} > flash_cfg
_sidata = LOADADDR(.data); _sidata = LOADADDR(.data);
.data : .data :

View File

@ -12,9 +12,17 @@ _estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400; _MIN_STACK_SIZE = 0x400;
/*
flash_cfg is for storing bootloader data, like last used firmware version.
bootloader_configuration should be equal to (APPLICATION_END_PAGE) page address, from targets/stm32l432/src/memory_layout.h:30; and equal to flash_cfg origin
*/
bootloader_configuration = 0x08000000 + 216*1024+8;
MEMORY MEMORY
{ {
flash (rx) : ORIGIN = 0x08000000, LENGTH = 32K flash (rx) : ORIGIN = 0x08000000, LENGTH = 32K
flash_cfg (rx) : ORIGIN = 0x08000000 + 216*1024+8, LENGTH = 2K-8
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
} }
@ -39,6 +47,11 @@ SECTIONS
_etext = .; _etext = .;
} >flash } >flash
.flag2 bootloader_configuration :
{
KEEP(*(.flag2)) ;
} > flash_cfg
_sidata = LOADADDR(.data); _sidata = LOADADDR(.data);
.data : .data :

View File

@ -13,14 +13,21 @@ _estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400; _MIN_STACK_SIZE = 0x400;
/* /*
Memory layout of device: len | 20 KB/10p| 196KB-8-8/98p | 2kB/1p | 38 KB/19p |
20 KB 198KB-8 38 KB pos | 0->20 KB | 20->216KB-8-8 | 216kB -> 218 kB | 218->256 KB |
| bootloader | application | secrets/data | posp | 0-10 | 10-113 | 113-114 | 113-128 |
desc | bootloader | application | bootloader data | secrets/data |
Last 8 bytes in application space are occupied by bootloader flags - app
authorization and bootloader activation flag.
*/ */
/* Current firmware version number is concatenated to the firmware code - see .flag marker */
/* flash length is (APPLICATION_END_PAGE-20*1024), where 20K is bootloader */
MEMORY MEMORY
{ {
flash (rx) : ORIGIN = 0x08005000, LENGTH = 198K - 8 flash (rx) : ORIGIN = 0x08000000 + 20K, LENGTH = 216K - 20K - 8
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
} }
@ -56,6 +63,12 @@ SECTIONS
_edata = .; _edata = .;
} >ram AT> flash } >ram AT> flash
.flag :
{
. = ALIGN(8);
KEEP(*(.flag)) ;
} > flash
.bss : .bss :
{ {
. = ALIGN(4); . = ALIGN(4);

View File

@ -12,9 +12,22 @@ _estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400; _MIN_STACK_SIZE = 0x400;
/*
len | 32 KB/16p| 184KB-8-8/92p | 2kB/1p | 38 KB/19p |
pos | 0->32 KB | 32->216KB-8-8 | 216kB -> 218 kB | 218->256 KB |
posp | 0-16 | 16-113 | 113-114 | 113-128 |
desc | bootloader | application | bootloader data | secrets/data |
Last 8 bytes in application space are occupied by bootloader flags - app
authorization and bootloader activation flag.
*/
/* Current firmware version number is concatenated to the firmware code - see .flag marker */
/* flash length is (APPLICATION_END_PAGE-20*1024), where 20K is bootloader */
MEMORY MEMORY
{ {
flash (rx) : ORIGIN = 0x08008000, LENGTH = 186K - 8 flash (rx) : ORIGIN = 0x08000000 + 20K + 12K, LENGTH = 216K - 20K - 12K - 8
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
} }
@ -50,6 +63,12 @@ SECTIONS
_edata = .; _edata = .;
} >ram AT> flash } >ram AT> flash
.flag :
{
. = ALIGN(8);
KEEP(*(.flag)) ;
} > flash
.bss : .bss :
{ {
. = ALIGN(4); . = ALIGN(4);

View File

@ -37,10 +37,33 @@
// End of application code. Leave some extra room for future data storage. // End of application code. Leave some extra room for future data storage.
// NOT included in application // NOT included in application
#define APPLICATION_END_PAGE ((PAGES - 19)) #define APPLICATION_END_PAGE ((PAGES - 20))
#define APPLICATION_END_ADDR ((0x08000000 + ((APPLICATION_END_PAGE)*PAGE_SIZE))-8) #define APPLICATION_END_ADDR ((0x08000000 + ((APPLICATION_END_PAGE)*PAGE_SIZE))-8)
// Bootloader state. // Bootloader state.
#define AUTH_WORD_ADDR (APPLICATION_END_ADDR) #define AUTH_WORD_ADDR (APPLICATION_END_ADDR)
#define LAST_ADDR (APPLICATION_END_ADDR-2048 + 8)
#define BOOT_VERSION_PAGE (APPLICATION_END_PAGE)
#define BOOT_VERSION_ADDR (0x08000000 + BOOT_VERSION_PAGE*FLASH_PAGE_SIZE + 8)
#define LAST_PAGE (APPLICATION_END_PAGE-1)
struct flash_memory_st{
uint8_t bootloader[APPLICATION_START_PAGE*2*1024];
uint8_t application[(APPLICATION_END_PAGE-APPLICATION_START_PAGE)*2*1024-8];
uint8_t auth_word[4];
uint8_t bootloader_disabled[4];
// place for more user data
uint8_t _reserved_application_end_mark[8];
uint8_t bootloader_data[2*1024-8];
uint8_t user_data[38*1024];
} __attribute__((packed));
typedef struct flash_memory_st flash_memory_st;
#include <assert.h>
static_assert(sizeof(flash_memory_st) == 256*1024, "Data structure doesn't match flash size");
#endif #endif