diff --git a/fido2/ctap.c b/fido2/ctap.c index fa3e1e0..fdf666e 100644 --- a/fido2/ctap.c +++ b/fido2/ctap.c @@ -1775,8 +1775,14 @@ void ctap_load_external_keys(uint8_t * keybytes){ crypto_load_master_secret(STATE.key_space); } +#include "version.h" 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(); authenticator_read_state(&STATE); diff --git a/fido2/version.c b/fido2/version.c new file mode 100644 index 0000000..d8c2252 --- /dev/null +++ b/fido2/version.c @@ -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 +cbor_static_assert(sizeof(version_t) == 4); diff --git a/fido2/version.h b/fido2/version.h index fe5f293..3671c16 100644 --- a/fido2/version.h +++ b/fido2/version.h @@ -17,5 +17,23 @@ #define SOLO_VERSION __STR(SOLO_VERSION_MAJ) "." __STR(SOLO_VERSION_MIN) "." __STR(SOLO_VERSION_PATCH) #endif +#include +#include + +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 diff --git a/targets/stm32l432/Makefile b/targets/stm32l432/Makefile index e7db178..8473b7d 100644 --- a/targets/stm32l432/Makefile +++ b/targets/stm32l432/Makefile @@ -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 -rdu -d all.hex -flashboot: solo.hex bootloader.hex - $(VENV) $(merge_hex) solo.hex bootloader.hex all.hex +flashboot: bootloader.hex STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst diff --git a/targets/stm32l432/bootloader/bootloader.c b/targets/stm32l432/bootloader/bootloader.c index db22f2d..325572e 100644 --- a/targets/stm32l432/bootloader/bootloader.c +++ b/targets/stm32l432/bootloader/bootloader.c @@ -19,6 +19,12 @@ #include "ctap_errors.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; @@ -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() { // 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); 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, ¤t_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 @@ -125,10 +161,7 @@ int bootloader_bridge(int klen, uint8_t * keyh) return CTAP1_ERR_INVALID_LENGTH; } #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" - "\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"; + extern uint8_t *pubkey_boot; const struct uECC_Curve_t * curve = NULL; #endif @@ -165,12 +198,11 @@ int bootloader_bridge(int klen, uint8_t * keyh) } // Do the actual write flash_write((uint32_t)ptr,req->payload, len); - - + last_written_app_address = (uint8_t *)ptr + len - 8 + 4; break; case BootDone: // Writing to flash finished. Request code validation. - printf1(TAG_BOOT, "BootDone: "); + printf1(TAG_BOOT, "BootDone: \r\n"); #ifndef SOLO_HACKER if (len != 64) { @@ -185,17 +217,23 @@ int bootloader_bridge(int klen, uint8_t * keyh) crypto_sha256_final(hash); curve = uECC_secp256r1(); // Verify incoming signature made over the SHA256 hash - if (! uECC_verify(pubkey, - hash, - 32, - req->payload, - curve)) + if ( + !uECC_verify(pubkey_boot, hash, 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; + } #endif // Set the application validated, and mark for reboot. authorize_application(); + REBOOT_FLAG = 1; break; case BootCheck: @@ -218,6 +256,7 @@ int bootloader_bridge(int klen, uint8_t * keyh) break; case BootReboot: printf1(TAG_BOOT, "BootReboot.\r\n"); + printf1(TAG_BOOT, "Application authorized: %d.\r\n", is_authorized_to_boot()); REBOOT_FLAG = 1; break; case BootDisable: diff --git a/targets/stm32l432/bootloader/main.c b/targets/stm32l432/bootloader/main.c index c3bf736..1448ea7 100644 --- a/targets/stm32l432/bootloader/main.c +++ b/targets/stm32l432/bootloader/main.c @@ -138,6 +138,14 @@ int main() 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", ¤t_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*)(¤t_firmware_version) - 16, 32); + while(1) { diff --git a/targets/stm32l432/bootloader/pubkey_bootloader.c b/targets/stm32l432/bootloader/pubkey_bootloader.c new file mode 100644 index 0000000..37e0477 --- /dev/null +++ b/targets/stm32l432/bootloader/pubkey_bootloader.c @@ -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"; diff --git a/targets/stm32l432/bootloader/version_check.c b/targets/stm32l432/bootloader/version_check.c new file mode 100644 index 0000000..f60d4ef --- /dev/null +++ b/targets/stm32l432/bootloader/version_check.c @@ -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); +} diff --git a/targets/stm32l432/build/application.mk b/targets/stm32l432/build/application.mk index cda3519..57c2749 100644 --- a/targets/stm32l432/build/application.mk +++ b/targets/stm32l432/build/application.mk @@ -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/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c SRC += ../../fido2/ctap_parse.c ../../fido2/main.c +SRC += ../../fido2/version.c SRC += ../../fido2/data_migration.c SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c SRC += ../../fido2/extensions/wallet.c @@ -70,6 +71,7 @@ all: $(TARGET).elf %.elf: $(OBJ) $(CC) $^ $(HW) $(LDFLAGS) -o $@ + @echo "Built version: $(VERSION_FLAGS)" %.hex: %.elf $(SZ) $^ diff --git a/targets/stm32l432/build/bootloader.mk b/targets/stm32l432/build/bootloader.mk index 4fa4513..cc61913 100644 --- a/targets/stm32l432/build/bootloader.mk +++ b/targets/stm32l432/build/bootloader.mk @@ -2,6 +2,7 @@ include build/common.mk # ST related 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/fifo.c src/crypto.c src/attestation.c src/sense.c SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c @@ -65,6 +66,7 @@ all: $(TARGET).elf %.elf: $(OBJ) $(CC) $^ $(HW) $(LDFLAGS) -o $@ + arm-none-eabi-size $@ %.hex: %.elf $(CP) -O ihex $^ $(TARGET).hex diff --git a/targets/stm32l432/linker/bootloader_stm32l4xx.ld b/targets/stm32l432/linker/bootloader_stm32l4xx.ld index cccd9c6..8460687 100644 --- a/targets/stm32l432/linker/bootloader_stm32l4xx.ld +++ b/targets/stm32l432/linker/bootloader_stm32l4xx.ld @@ -12,9 +12,17 @@ _estack = 0x2000c000; _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 { flash (rx) : ORIGIN = 0x08000000, LENGTH = 20K + flash_cfg (rx) : ORIGIN = 0x08000000 + 216*1024+8, LENGTH = 2K-8 ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K } @@ -39,6 +47,11 @@ SECTIONS _etext = .; } >flash + .flag2 bootloader_configuration : + { + KEEP(*(.flag2)) ; + } > flash_cfg + _sidata = LOADADDR(.data); .data : diff --git a/targets/stm32l432/linker/bootloader_stm32l4xx_extra.ld b/targets/stm32l432/linker/bootloader_stm32l4xx_extra.ld index 3e1efd7..fbb96da 100644 --- a/targets/stm32l432/linker/bootloader_stm32l4xx_extra.ld +++ b/targets/stm32l432/linker/bootloader_stm32l4xx_extra.ld @@ -12,9 +12,17 @@ _estack = 0x2000c000; _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 { flash (rx) : ORIGIN = 0x08000000, LENGTH = 32K + flash_cfg (rx) : ORIGIN = 0x08000000 + 216*1024+8, LENGTH = 2K-8 ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K } @@ -39,6 +47,11 @@ SECTIONS _etext = .; } >flash + .flag2 bootloader_configuration : + { + KEEP(*(.flag2)) ; + } > flash_cfg + _sidata = LOADADDR(.data); .data : diff --git a/targets/stm32l432/linker/stm32l4xx.ld b/targets/stm32l432/linker/stm32l4xx.ld index 4e43df7..7378208 100644 --- a/targets/stm32l432/linker/stm32l4xx.ld +++ b/targets/stm32l432/linker/stm32l4xx.ld @@ -13,14 +13,21 @@ _estack = 0x2000c000; _MIN_STACK_SIZE = 0x400; /* - Memory layout of device: - 20 KB 198KB-8 38 KB - | bootloader | application | secrets/data | +len | 20 KB/10p| 196KB-8-8/98p | 2kB/1p | 38 KB/19p | +pos | 0->20 KB | 20->216KB-8-8 | 216kB -> 218 kB | 218->256 KB | +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 { - flash (rx) : ORIGIN = 0x08005000, LENGTH = 198K - 8 + flash (rx) : ORIGIN = 0x08000000 + 20K, LENGTH = 216K - 20K - 8 ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K } @@ -56,6 +63,12 @@ SECTIONS _edata = .; } >ram AT> flash + .flag : + { + . = ALIGN(8); + KEEP(*(.flag)) ; + } > flash + .bss : { . = ALIGN(4); diff --git a/targets/stm32l432/linker/stm32l4xx_extra.ld b/targets/stm32l432/linker/stm32l4xx_extra.ld index 6948c90..2496d35 100644 --- a/targets/stm32l432/linker/stm32l4xx_extra.ld +++ b/targets/stm32l432/linker/stm32l4xx_extra.ld @@ -12,9 +12,22 @@ _estack = 0x2000c000; _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 { - flash (rx) : ORIGIN = 0x08008000, LENGTH = 186K - 8 + flash (rx) : ORIGIN = 0x08000000 + 20K + 12K, LENGTH = 216K - 20K - 12K - 8 ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K } @@ -50,6 +63,12 @@ SECTIONS _edata = .; } >ram AT> flash + .flag : + { + . = ALIGN(8); + KEEP(*(.flag)) ; + } > flash + .bss : { . = ALIGN(4); diff --git a/targets/stm32l432/src/memory_layout.h b/targets/stm32l432/src/memory_layout.h index 71178dc..8a66a71 100644 --- a/targets/stm32l432/src/memory_layout.h +++ b/targets/stm32l432/src/memory_layout.h @@ -37,10 +37,33 @@ // End of application code. Leave some extra room for future data storage. // 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) // Bootloader state. #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 +static_assert(sizeof(flash_memory_st) == 256*1024, "Data structure doesn't match flash size"); + + #endif