diff --git a/fido2/ctaphid.c b/fido2/ctaphid.c index 7946db6..99573df 100644 --- a/fido2/ctaphid.c +++ b/fido2/ctaphid.c @@ -711,7 +711,12 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw) ctaphid_write(&wb, NULL, 0); is_busy = 0; break; + case CTAPHID_ENTERSTBOOT: + printf1(TAG_HID,"CTAPHID_ENTERBOOT\n"); + boot_st_bootloader(); + break; #endif + default: printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd()); ctaphid_send_error(cid, CTAP1_ERR_INVALID_COMMAND); diff --git a/fido2/ctaphid.h b/fido2/ctaphid.h index 5683dbf..0815ef6 100644 --- a/fido2/ctaphid.h +++ b/fido2/ctaphid.h @@ -39,8 +39,9 @@ #define CTAPHID_KEEPALIVE (TYPE_INIT | 0x3b) // Custom commands between 0x40-0x7f -#define CTAPHID_BOOT (TYPE_INIT | 0x50) -#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51) +#define CTAPHID_BOOT (TYPE_INIT | 0x50) +#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51) +#define CTAPHID_ENTERSTBOOT (TYPE_INIT | 0x52) #define ERR_INVALID_CMD 0x01 #define ERR_INVALID_PAR 0x02 diff --git a/fido2/device.h b/fido2/device.h index 83b7f63..c6b63f5 100644 --- a/fido2/device.h +++ b/fido2/device.h @@ -96,7 +96,7 @@ void ctap_overwrite_rk(int index,CTAP_residentKey * rk); // For Solo hacker void boot_solo_bootloader(); - +void boot_st_bootloader(); diff --git a/targets/stm32l442/bootloader/bootloader.c b/targets/stm32l442/bootloader/bootloader.c index f157ec6..41ef628 100644 --- a/targets/stm32l442/bootloader/bootloader.c +++ b/targets/stm32l442/bootloader/bootloader.c @@ -22,6 +22,8 @@ typedef enum BootCheck = 0x42, BootErase = 0x43, BootVersion = 0x44, + BootReboot = 0x45, + BootBootloader = 0x46, } BootOperation; @@ -143,6 +145,17 @@ int bootloader_bridge(int klen, uint8_t * keyh) u2f_response_writeback(&version,1); return 0; break; + case BootReboot: + printf1(TAG_BOOT, "BootReboot.\r\n"); + device_reboot(); + break; +#ifndef SOLO_HACKER + case BootBootloader: + printf1(TAG_BOOT, "BootBootloader.\r\n"); + flash_option_bytes_init(1); + boot_st_bootloader(); + break; +#endif default: return CTAP1_ERR_INVALID_COMMAND; } diff --git a/targets/stm32l442/bootloader/main.c b/targets/stm32l442/bootloader/main.c index f5aee0c..77c797c 100644 --- a/targets/stm32l442/bootloader/main.c +++ b/targets/stm32l442/bootloader/main.c @@ -56,6 +56,7 @@ int main(int argc, char * argv[]) { uint8_t hidmsg[64]; uint32_t t1 = 0; + uint32_t stboot_time = 0; uint32_t boot = 1; set_logging_mask( @@ -94,6 +95,7 @@ int main(int argc, char * argv[]) } #ifdef SOLO_HACKER + stboot_time = millis(); if ( RCC->CSR & (1<<29) )// check if there was independent watchdog reset { RCC->CSR |= (1<<23); // clear reset flags @@ -147,6 +149,17 @@ int main(int argc, char * argv[]) delay(250); device_reboot(); } +#ifdef SOLO_HACKER + // Boot ST bootloader if button is held for 2s + if (!device_is_button_pressed()) + { + stboot_time = millis(); + } + if ((millis() - stboot_time) > 2000) + { + boot_st_bootloader(); + } +#endif } // Should never get here diff --git a/targets/stm32l442/src/device.c b/targets/stm32l442/src/device.c index 746ed37..f9c3c71 100644 --- a/targets/stm32l442/src/device.c +++ b/targets/stm32l442/src/device.c @@ -486,6 +486,18 @@ void ctap_overwrite_rk(int index,CTAP_residentKey * rk) } } +void boot_st_bootloader() +{ + __disable_irq(); + + __set_MSP(*((uint32_t *)0x1fff0000)); + + ((void (*)(void)) (*((uint32_t *)0x1fff0004)))(); + + while(1) + ; +} + void boot_solo_bootloader() { LL_IWDG_Enable(IWDG); diff --git a/tools/programmer.py b/tools/programmer.py index 5b53492..2611d0b 100644 --- a/tools/programmer.py +++ b/tools/programmer.py @@ -22,9 +22,12 @@ class SoloBootloader: check = 0x42 erase = 0x43 version = 0x44 + reboot = 0x45 + st_dfu = 0x46 HIDCommandBoot = 0x50 HIDCommandEnterBoot = 0x51 + HIDCommandEnterSTBoot = 0x52 TAG = b'\x8C\x27\x90\xf6' @@ -45,6 +48,13 @@ class Programmer(): """ option to reboot after programming """ self.reboot = val + def reboot(self,val): + """ option to reboot after programming """ + try: + self.exchange(SoloBootloader.reboot) + except OSError: + pass + def find_device(self,): dev = next(CtapHidDevice.list_devices(), None) if not dev: @@ -64,6 +74,11 @@ class Programmer(): return cmd + addr[:3] + SoloBootloader.TAG + length + data + def send_only_hid(self, cmd, data): + if type(data) != type(b''): + data = struct.pack('%dB' % len(data), *[ord(x) for x in data]) + self.dev._dev.InternalSend(0x80 | cmd, bytearray(data)) + def send_data_hid(self, cmd, data): if type(data) != type(b''): data = struct.pack('%dB' % len(data), *[ord(x) for x in data]) @@ -121,6 +136,18 @@ class Programmer(): self.send_data_hid(CTAPHID.INIT, '\x11\x11\x11\x11\x11\x11\x11\x11') self.send_data_hid(SoloBootloader.HIDCommandEnterBoot, '') + 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. + """ + if self.exchange == self.exchange_hid: + self.send_only_hid(SoloBootloader.HIDCommandEnterSTBoot, '') + else: + req = Programmer.format_request(SoloBootloader.st_dfu) + self.send_only_hid(SoloBootloader.HIDCommandBoot, req) + def program_file(self,name): if name.lower().endswith('.json'): @@ -201,12 +228,14 @@ def attempt_to_boot_bootloader(p): if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument("", help = 'firmware file. Either a JSON or hex file. JSON file contains signature while hex does not.') + parser.add_argument("[firmware]", nargs='?', default='', help = 'firmware file. Either a JSON or hex file. JSON file contains signature while hex does not.') parser.add_argument("--use-hid", action="store_true", help = 'Programs using custom HID command (default). Quicker than using U2F authenticate which is what a browser has to use.') parser.add_argument("--use-u2f", action="store_true", help = 'Programs using U2F authenticate. This is what a web application will use.') parser.add_argument("--no-reset", action="store_true", help = 'Don\'t reset after writing firmware. Stay in bootloader mode.') parser.add_argument("--reset-only", action="store_true", help = 'Don\'t write anything, try to boot without a signature.') + 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. Make sure to reprogram the option bytes just to be safe.') args = parser.parse_args() print() @@ -223,6 +252,15 @@ if __name__ == '__main__': attempt_to_boot_bootloader(p) sys.exit(0) + if args.reboot: + p.reboot() + sys.exit(0) + + if args.st_dfu: + print('Sending command to boot into ST DFU...') + p.enter_st_dfu() + sys.exit(0) + try: print('version is ', p.version()) except CtapError as e: @@ -234,6 +272,11 @@ if __name__ == '__main__': attempt_to_boot_bootloader(p) if not args.reset_only: - p.program_file(args.__dict__['']) + fw = args.__dict__['[firmware]'] + if fw == '': + print('Need to supply firmware filename.') + args.print_help() + sys.exit(1) + p.program_file(fw) else: p.exchange(SoloBootloader.done,0,b'A'*64)