From beedc248399f0e745a52706ea8469135dd23d05f Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Wed, 5 Dec 2018 00:14:28 -0500 Subject: [PATCH] option to disable solo bootloader --- targets/stm32l442/bootloader/bootloader.c | 50 ++++++++++++++++++++--- targets/stm32l442/bootloader/main.c | 18 ++++---- targets/stm32l442/merge_hex.py | 5 +++ targets/stm32l442/src/device.c | 2 +- targets/stm32l442/src/flash.c | 7 +++- tools/programmer.py | 42 +++++++++++++++---- 6 files changed, 101 insertions(+), 23 deletions(-) diff --git a/targets/stm32l442/bootloader/bootloader.c b/targets/stm32l442/bootloader/bootloader.c index 8239014..e24ef39 100644 --- a/targets/stm32l442/bootloader/bootloader.c +++ b/targets/stm32l442/bootloader/bootloader.c @@ -24,6 +24,7 @@ typedef enum BootVersion = 0x44, BootReboot = 0x45, BootBootloader = 0x46, + BootDisable = 0x47, } BootOperation; @@ -46,12 +47,30 @@ static void erase_application() } } +#define LAST_ADDR (APPLICATION_END_ADDR-2048 + 8) +#define LAST_PAGE (APPLICATION_END_PAGE-1) +static void disable_bootloader() +{ + uint8_t page[PAGE_SIZE]; + memmove(page, (uint8_t*)LAST_ADDR, PAGE_SIZE); + memset(page+PAGE_SIZE -4, 0, 4); + flash_erase_page(LAST_PAGE); + flash_write(LAST_ADDR, page, PAGE_SIZE); +} + static void authorize_application() { - uint32_t zero = 0; - uint32_t * ptr; - ptr = (uint32_t *)AUTH_WORD_ADDR; - flash_write((uint32_t)ptr, (uint8_t *)&zero, 4); + // uint32_t zero = 0; + // uint32_t * ptr; + // ptr = (uint32_t *)AUTH_WORD_ADDR; + // flash_write((uint32_t)ptr, (uint8_t *)&zero, 4); + uint8_t page[PAGE_SIZE]; + if (is_authorized_to_boot()) + return; + memmove(page, (uint8_t*)LAST_ADDR, PAGE_SIZE); + memset(page+PAGE_SIZE -8, 0, 4); + flash_erase_page(LAST_PAGE); + flash_write(LAST_ADDR, page, PAGE_SIZE); } int is_authorized_to_boot() @@ -60,6 +79,12 @@ int is_authorized_to_boot() return *auth == 0; } +int is_bootloader_disabled() +{ + uint32_t * auth = (uint32_t *)(AUTH_WORD_ADDR+4); + return *auth == 0; +} + int bootloader_bridge(int klen, uint8_t * keyh) { static int has_erased = 0; @@ -150,7 +175,22 @@ int bootloader_bridge(int klen, uint8_t * keyh) break; case BootReboot: printf1(TAG_BOOT, "BootReboot.\r\n"); - device_reboot(); + REBOOT_FLAG = 1; + break; + case BootDisable: + printf1(TAG_BOOT, "BootDisable %08lx.\r\n", *(uint32_t *)(AUTH_WORD_ADDR+4)); + if (req->payload[0] == 0xcd && req->payload[1] == 0xde + && req->payload[2] == 0xba && req->payload[3] == 0xaa) + { + disable_bootloader(); + version = 0; + u2f_response_writeback(&version,1); + } + else + { + version = CTAP2_ERR_OPERATION_DENIED; + u2f_response_writeback(&version,1); + } break; #ifdef SOLO_HACKER case BootBootloader: diff --git a/targets/stm32l442/bootloader/main.c b/targets/stm32l442/bootloader/main.c index eb14eb8..977bafe 100644 --- a/targets/stm32l442/bootloader/main.c +++ b/targets/stm32l442/bootloader/main.c @@ -95,22 +95,26 @@ int main(int argc, char * argv[]) } } + #ifdef SOLO_HACKER - stboot_time = millis(); - if ( RCC->CSR & (1<<29) )// check if there was independent watchdog reset + if (!is_bootloader_disabled()) { - RCC->CSR |= (1<<23); // clear reset flags - goto start_bootloader; + stboot_time = millis(); + if ( RCC->CSR & (1<<29) )// check if there was independent watchdog reset + { + RCC->CSR |= (1<<23); // clear reset flags + goto start_bootloader; + } } #endif - if (boot && is_authorized_to_boot()) + if (is_authorized_to_boot() && (boot || is_bootloader_disabled())) { BOOT_boot(); } else { - printf1(TAG_RED,"Not authorized to boot\r\n"); + printf1(TAG_RED,"Not authorized to boot (%08x == %08lx)\r\n", AUTH_WORD_ADDR, *(uint32_t*)AUTH_WORD_ADDR); } start_bootloader: @@ -156,7 +160,7 @@ int main(int argc, char * argv[]) { stboot_time = millis(); } - if ((millis() - stboot_time) > 2000) + if ((millis() - stboot_time) > 5000) { boot_st_bootloader(); } diff --git a/targets/stm32l442/merge_hex.py b/targets/stm32l442/merge_hex.py index 79efedc..0473d8a 100644 --- a/targets/stm32l442/merge_hex.py +++ b/targets/stm32l442/merge_hex.py @@ -25,4 +25,9 @@ first[AUTH_WORD_ADDR+1] = 0 first[AUTH_WORD_ADDR+2] = 0 first[AUTH_WORD_ADDR+3] = 0 +first[AUTH_WORD_ADDR+4] = 0xff +first[AUTH_WORD_ADDR+5] = 0xff +first[AUTH_WORD_ADDR+6] = 0xff +first[AUTH_WORD_ADDR+7] = 0xff + first.tofile(sys.argv[len(sys.argv)-1], format='hex') diff --git a/targets/stm32l442/src/device.c b/targets/stm32l442/src/device.c index d7c0df4..580bc9a 100644 --- a/targets/stm32l442/src/device.c +++ b/targets/stm32l442/src/device.c @@ -170,7 +170,7 @@ void heartbeat() { state = !state; } - if (but) led_rgb((val*b)); + if (but) led_rgb(((val * r)<<8) | ((val*b) << 16) | (val*g)); else led_rgb(((val * g)<<8) | ((val*r) << 16) | (val*b)); } diff --git a/targets/stm32l442/src/flash.c b/targets/stm32l442/src/flash.c index 5d7e463..beaac39 100644 --- a/targets/stm32l442/src/flash.c +++ b/targets/stm32l442/src/flash.c @@ -20,8 +20,13 @@ static void flash_unlock() // Locks flash and turns off DFU void flash_option_bytes_init(int boot_from_dfu) { -#if DEBUG_LEVEL +#ifndef FLASH_ROP +#define FLASH_ROP 0 +#endif +#if FLASH_ROP == 0 uint32_t val = 0xfffff8aa; +#elif FLASH_ROP == 2 + uint32_t val = 0xfffff8cc; #else uint32_t val = 0xfffff8b9; #endif diff --git a/tools/programmer.py b/tools/programmer.py index 915018a..cd67019 100644 --- a/tools/programmer.py +++ b/tools/programmer.py @@ -24,6 +24,7 @@ class SoloBootloader: version = 0x44 reboot = 0x45 st_dfu = 0x46 + disable = 0x47 HIDCommandBoot = 0x50 HIDCommandEnterBoot = 0x51 @@ -136,21 +137,24 @@ class Programmer(): self.send_data_hid(CTAPHID.INIT, '\x11\x11\x11\x11\x11\x11\x11\x11') self.send_data_hid(SoloBootloader.HIDCommandEnterBoot, '') + def is_solo_bootloader(self,): + try: + p.version() + return True + except CtapError as e: + if e.code == CtapError.ERR.INVALID_COMMAND: + pass + else: + raise (e) + return False + def enter_st_dfu(self,): """ If solo is configured as solo hacker or something similar, this command will tell the token to boot directly to the st DFU so it can be reprogrammed. Warning, you could brick your device. """ - soloboot = False - try: - p.version() - soloboot = True - except CtapError as e: - if e.code == CtapError.ERR.INVALID_COMMAND: - pass - else: - raise (e) + soloboot = self.is_solo_bootloader() if soloboot or self.exchange == self.exchange_u2f: req = Programmer.format_request(SoloBootloader.st_dfu) @@ -158,6 +162,21 @@ class Programmer(): else: self.send_only_hid(SoloBootloader.HIDCommandEnterSTBoot, '') + def disable_solo_bootloader(self,): + """ + Disables the Solo bootloader. Only do this if you want to void the possibility + of any updates. + If you've started from a solo hacker, make you you've programmed a final/production build! + """ + ret = self.exchange(SoloBootloader.disable, 0, b'\xcd\xde\xba\xaa') # magic number + if ret[0] != CtapError.ERR.SUCCESS: + print('Failed to disable bootloader') + return False + time.sleep(0.1) + self.exchange(SoloBootloader.reboot) + return True + + def program_file(self,name): if name.lower().endswith('.json'): @@ -246,6 +265,7 @@ if __name__ == '__main__': parser.add_argument("--reboot", action="store_true", help = 'Tell bootloader to reboot.') parser.add_argument("--enter-bootloader", action="store_true", help = 'Don\'t write anything, try to enter bootloader. Typically only supported by Solo Hacker builds.') parser.add_argument("--st-dfu", action="store_true", help = 'Don\'t write anything, try to enter ST DFU. Warning, you could brick your Solo if you overwrite everything. You should reprogram the option bytes just to be safe (boot to Solo bootloader first, then run this command).') + parser.add_argument("--disable", action="store_true", help = 'Disable the Solo bootloader. Cannot be undone. No future updates can be applied.') args = parser.parse_args() print() @@ -271,6 +291,10 @@ if __name__ == '__main__': p.enter_st_dfu() sys.exit(0) + if args.disable: + p.disable_solo_bootloader() + sys.exit(0) + try: print('version is ', p.version()) except CtapError as e: