diff --git a/fido2/ctaphid.c b/fido2/ctaphid.c
index f586764..7946db6 100644
--- a/fido2/ctaphid.c
+++ b/fido2/ctaphid.c
@@ -699,6 +699,18 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
+#endif
+#if defined(SOLO_HACKER)
+ case CTAPHID_ENTERBOOT:
+ printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
+ boot_solo_bootloader();
+ ctaphid_write_buffer_init(&wb);
+ wb.cid = cid;
+ wb.cmd = CTAPHID_ENTERBOOT;
+ wb.bcnt = 0;
+ ctaphid_write(&wb, NULL, 0);
+ is_busy = 0;
+ break;
#endif
default:
printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd());
diff --git a/fido2/ctaphid.h b/fido2/ctaphid.h
index 4336062..5683dbf 100644
--- a/fido2/ctaphid.h
+++ b/fido2/ctaphid.h
@@ -40,6 +40,7 @@
// Custom commands between 0x40-0x7f
#define CTAPHID_BOOT (TYPE_INIT | 0x50)
+#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51)
#define ERR_INVALID_CMD 0x01
#define ERR_INVALID_PAR 0x02
diff --git a/fido2/device.h b/fido2/device.h
index 8d78df9..83b7f63 100644
--- a/fido2/device.h
+++ b/fido2/device.h
@@ -94,7 +94,8 @@ void ctap_store_rk(int index,CTAP_residentKey * rk);
void ctap_load_rk(int index,CTAP_residentKey * rk);
void ctap_overwrite_rk(int index,CTAP_residentKey * rk);
-
+// For Solo hacker
+void boot_solo_bootloader();
diff --git a/targets/stm32l442/bootloader/bootloader.c b/targets/stm32l442/bootloader/bootloader.c
index ca9610d..f157ec6 100644
--- a/targets/stm32l442/bootloader/bootloader.c
+++ b/targets/stm32l442/bootloader/bootloader.c
@@ -12,6 +12,7 @@
#include "ctap_errors.h"
#include "log.h"
+
extern uint8_t REBOOT_FLAG;
typedef enum
diff --git a/targets/stm32l442/bootloader/bootloader.h b/targets/stm32l442/bootloader/bootloader.h
index d9b396a..412a5a2 100644
--- a/targets/stm32l442/bootloader/bootloader.h
+++ b/targets/stm32l442/bootloader/bootloader.h
@@ -13,6 +13,9 @@
#define IS_BOOTLOADER 1
+
+#define SOLO_HACKER
+
#define ENABLE_U2F_EXTENSIONS
// #define ENABLE_U2F
diff --git a/targets/stm32l442/bootloader/main.c b/targets/stm32l442/bootloader/main.c
index 0a2e4ea..f5aee0c 100644
--- a/targets/stm32l442/bootloader/main.c
+++ b/targets/stm32l442/bootloader/main.c
@@ -31,6 +31,7 @@
#include "log.h"
#include "ctap.h"
#include "app.h"
+#include "stm32l4xx_ll_rcc.h"
#include "stm32l4xx.h"
@@ -92,6 +93,14 @@ int main(int argc, char * argv[])
}
}
+#ifdef SOLO_HACKER
+ 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())
{
BOOT_boot();
@@ -100,6 +109,7 @@ int main(int argc, char * argv[])
{
printf1(TAG_RED,"Not authorized to boot\r\n");
}
+ start_bootloader:
usbhid_init();
printf1(TAG_GEN,"init usb\n");
diff --git a/targets/stm32l442/lib/stm32l4xx_ll_iwdg.h b/targets/stm32l442/lib/stm32l4xx_ll_iwdg.h
new file mode 100644
index 0000000..6fc7ee2
--- /dev/null
+++ b/targets/stm32l442/lib/stm32l4xx_ll_iwdg.h
@@ -0,0 +1,361 @@
+/**
+ ******************************************************************************
+ * @file stm32l4xx_ll_iwdg.h
+ * @author MCD Application Team
+ * @brief Header file of IWDG LL module.
+ ******************************************************************************
+ * @attention
+ *
+ *
© COPYRIGHT(c) 2017 STMicroelectronics
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32L4xx_LL_IWDG_H
+#define __STM32L4xx_LL_IWDG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32l4xx.h"
+
+/** @addtogroup STM32L4xx_LL_Driver
+ * @{
+ */
+
+#if defined(IWDG)
+
+/** @defgroup IWDG_LL IWDG
+ * @{
+ */
+
+/* Private types -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+
+/* Private constants ---------------------------------------------------------*/
+/** @defgroup IWDG_LL_Private_Constants IWDG Private Constants
+ * @{
+ */
+
+#define LL_IWDG_KEY_RELOAD 0x0000AAAAU /*!< IWDG Reload Counter Enable */
+#define LL_IWDG_KEY_ENABLE 0x0000CCCCU /*!< IWDG Peripheral Enable */
+#define LL_IWDG_KEY_WR_ACCESS_ENABLE 0x00005555U /*!< IWDG KR Write Access Enable */
+#define LL_IWDG_KEY_WR_ACCESS_DISABLE 0x00000000U /*!< IWDG KR Write Access Disable */
+
+/**
+ * @}
+ */
+
+/* Private macros ------------------------------------------------------------*/
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/** @defgroup IWDG_LL_Exported_Constants IWDG Exported Constants
+ * @{
+ */
+
+/** @defgroup IWDG_LL_EC_GET_FLAG Get Flags Defines
+ * @brief Flags defines which can be used with LL_IWDG_ReadReg function
+ * @{
+ */
+#define LL_IWDG_SR_PVU IWDG_SR_PVU /*!< Watchdog prescaler value update */
+#define LL_IWDG_SR_RVU IWDG_SR_RVU /*!< Watchdog counter reload value update */
+#define LL_IWDG_SR_WVU IWDG_SR_WVU /*!< Watchdog counter window value update */
+
+/**
+ * @}
+ */
+
+/** @defgroup IWDG_LL_EC_PRESCALER Prescaler Divider
+ * @{
+ */
+#define LL_IWDG_PRESCALER_4 0x00000000U /*!< Divider by 4 */
+#define LL_IWDG_PRESCALER_8 (IWDG_PR_PR_0) /*!< Divider by 8 */
+#define LL_IWDG_PRESCALER_16 (IWDG_PR_PR_1) /*!< Divider by 16 */
+#define LL_IWDG_PRESCALER_32 (IWDG_PR_PR_1 | IWDG_PR_PR_0) /*!< Divider by 32 */
+#define LL_IWDG_PRESCALER_64 (IWDG_PR_PR_2) /*!< Divider by 64 */
+#define LL_IWDG_PRESCALER_128 (IWDG_PR_PR_2 | IWDG_PR_PR_0) /*!< Divider by 128 */
+#define LL_IWDG_PRESCALER_256 (IWDG_PR_PR_2 | IWDG_PR_PR_1) /*!< Divider by 256 */
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/* Exported macro ------------------------------------------------------------*/
+/** @defgroup IWDG_LL_Exported_Macros IWDG Exported Macros
+ * @{
+ */
+
+/** @defgroup IWDG_LL_EM_WRITE_READ Common Write and read registers Macros
+ * @{
+ */
+
+/**
+ * @brief Write a value in IWDG register
+ * @param __INSTANCE__ IWDG Instance
+ * @param __REG__ Register to be written
+ * @param __VALUE__ Value to be written in the register
+ * @retval None
+ */
+#define LL_IWDG_WriteReg(__INSTANCE__, __REG__, __VALUE__) WRITE_REG(__INSTANCE__->__REG__, (__VALUE__))
+
+/**
+ * @brief Read a value in IWDG register
+ * @param __INSTANCE__ IWDG Instance
+ * @param __REG__ Register to be read
+ * @retval Register value
+ */
+#define LL_IWDG_ReadReg(__INSTANCE__, __REG__) READ_REG(__INSTANCE__->__REG__)
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+
+/* Exported functions --------------------------------------------------------*/
+/** @defgroup IWDG_LL_Exported_Functions IWDG Exported Functions
+ * @{
+ */
+/** @defgroup IWDG_LL_EF_Configuration Configuration
+ * @{
+ */
+
+/**
+ * @brief Start the Independent Watchdog
+ * @note Except if the hardware watchdog option is selected
+ * @rmtoll KR KEY LL_IWDG_Enable
+ * @param IWDGx IWDG Instance
+ * @retval None
+ */
+__STATIC_INLINE void LL_IWDG_Enable(IWDG_TypeDef *IWDGx)
+{
+ WRITE_REG(IWDGx->KR, LL_IWDG_KEY_ENABLE);
+}
+
+/**
+ * @brief Reloads IWDG counter with value defined in the reload register
+ * @rmtoll KR KEY LL_IWDG_ReloadCounter
+ * @param IWDGx IWDG Instance
+ * @retval None
+ */
+__STATIC_INLINE void LL_IWDG_ReloadCounter(IWDG_TypeDef *IWDGx)
+{
+ WRITE_REG(IWDGx->KR, LL_IWDG_KEY_RELOAD);
+}
+
+/**
+ * @brief Enable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers
+ * @rmtoll KR KEY LL_IWDG_EnableWriteAccess
+ * @param IWDGx IWDG Instance
+ * @retval None
+ */
+__STATIC_INLINE void LL_IWDG_EnableWriteAccess(IWDG_TypeDef *IWDGx)
+{
+ WRITE_REG(IWDGx->KR, LL_IWDG_KEY_WR_ACCESS_ENABLE);
+}
+
+/**
+ * @brief Disable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers
+ * @rmtoll KR KEY LL_IWDG_DisableWriteAccess
+ * @param IWDGx IWDG Instance
+ * @retval None
+ */
+__STATIC_INLINE void LL_IWDG_DisableWriteAccess(IWDG_TypeDef *IWDGx)
+{
+ WRITE_REG(IWDGx->KR, LL_IWDG_KEY_WR_ACCESS_DISABLE);
+}
+
+/**
+ * @brief Select the prescaler of the IWDG
+ * @rmtoll PR PR LL_IWDG_SetPrescaler
+ * @param IWDGx IWDG Instance
+ * @param Prescaler This parameter can be one of the following values:
+ * @arg @ref LL_IWDG_PRESCALER_4
+ * @arg @ref LL_IWDG_PRESCALER_8
+ * @arg @ref LL_IWDG_PRESCALER_16
+ * @arg @ref LL_IWDG_PRESCALER_32
+ * @arg @ref LL_IWDG_PRESCALER_64
+ * @arg @ref LL_IWDG_PRESCALER_128
+ * @arg @ref LL_IWDG_PRESCALER_256
+ * @retval None
+ */
+__STATIC_INLINE void LL_IWDG_SetPrescaler(IWDG_TypeDef *IWDGx, uint32_t Prescaler)
+{
+ WRITE_REG(IWDGx->PR, IWDG_PR_PR & Prescaler);
+}
+
+/**
+ * @brief Get the selected prescaler of the IWDG
+ * @rmtoll PR PR LL_IWDG_GetPrescaler
+ * @param IWDGx IWDG Instance
+ * @retval Returned value can be one of the following values:
+ * @arg @ref LL_IWDG_PRESCALER_4
+ * @arg @ref LL_IWDG_PRESCALER_8
+ * @arg @ref LL_IWDG_PRESCALER_16
+ * @arg @ref LL_IWDG_PRESCALER_32
+ * @arg @ref LL_IWDG_PRESCALER_64
+ * @arg @ref LL_IWDG_PRESCALER_128
+ * @arg @ref LL_IWDG_PRESCALER_256
+ */
+__STATIC_INLINE uint32_t LL_IWDG_GetPrescaler(IWDG_TypeDef *IWDGx)
+{
+ return (READ_REG(IWDGx->PR));
+}
+
+/**
+ * @brief Specify the IWDG down-counter reload value
+ * @rmtoll RLR RL LL_IWDG_SetReloadCounter
+ * @param IWDGx IWDG Instance
+ * @param Counter Value between Min_Data=0 and Max_Data=0x0FFF
+ * @retval None
+ */
+__STATIC_INLINE void LL_IWDG_SetReloadCounter(IWDG_TypeDef *IWDGx, uint32_t Counter)
+{
+ WRITE_REG(IWDGx->RLR, IWDG_RLR_RL & Counter);
+}
+
+/**
+ * @brief Get the specified IWDG down-counter reload value
+ * @rmtoll RLR RL LL_IWDG_GetReloadCounter
+ * @param IWDGx IWDG Instance
+ * @retval Value between Min_Data=0 and Max_Data=0x0FFF
+ */
+__STATIC_INLINE uint32_t LL_IWDG_GetReloadCounter(IWDG_TypeDef *IWDGx)
+{
+ return (READ_REG(IWDGx->RLR));
+}
+
+/**
+ * @brief Specify high limit of the window value to be compared to the down-counter.
+ * @rmtoll WINR WIN LL_IWDG_SetWindow
+ * @param IWDGx IWDG Instance
+ * @param Window Value between Min_Data=0 and Max_Data=0x0FFF
+ * @retval None
+ */
+__STATIC_INLINE void LL_IWDG_SetWindow(IWDG_TypeDef *IWDGx, uint32_t Window)
+{
+ WRITE_REG(IWDGx->WINR, IWDG_WINR_WIN & Window);
+}
+
+/**
+ * @brief Get the high limit of the window value specified.
+ * @rmtoll WINR WIN LL_IWDG_GetWindow
+ * @param IWDGx IWDG Instance
+ * @retval Value between Min_Data=0 and Max_Data=0x0FFF
+ */
+__STATIC_INLINE uint32_t LL_IWDG_GetWindow(IWDG_TypeDef *IWDGx)
+{
+ return (READ_REG(IWDGx->WINR));
+}
+
+/**
+ * @}
+ */
+
+/** @defgroup IWDG_LL_EF_FLAG_Management FLAG_Management
+ * @{
+ */
+
+/**
+ * @brief Check if flag Prescaler Value Update is set or not
+ * @rmtoll SR PVU LL_IWDG_IsActiveFlag_PVU
+ * @param IWDGx IWDG Instance
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_IWDG_IsActiveFlag_PVU(IWDG_TypeDef *IWDGx)
+{
+ return (uint32_t)(READ_BIT(IWDGx->SR, IWDG_SR_PVU) == (IWDG_SR_PVU));
+}
+
+/**
+ * @brief Check if flag Reload Value Update is set or not
+ * @rmtoll SR RVU LL_IWDG_IsActiveFlag_RVU
+ * @param IWDGx IWDG Instance
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_IWDG_IsActiveFlag_RVU(IWDG_TypeDef *IWDGx)
+{
+ return (uint32_t)(READ_BIT(IWDGx->SR, IWDG_SR_RVU) == (IWDG_SR_RVU));
+}
+
+/**
+ * @brief Check if flag Window Value Update is set or not
+ * @rmtoll SR WVU LL_IWDG_IsActiveFlag_WVU
+ * @param IWDGx IWDG Instance
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_IWDG_IsActiveFlag_WVU(IWDG_TypeDef *IWDGx)
+{
+ return (uint32_t)(READ_BIT(IWDGx->SR, IWDG_SR_WVU) == (IWDG_SR_WVU));
+}
+
+/**
+ * @brief Check if all flags Prescaler, Reload & Window Value Update are reset or not
+ * @rmtoll SR PVU LL_IWDG_IsReady\n
+ * SR WVU LL_IWDG_IsReady\n
+ * SR RVU LL_IWDG_IsReady
+ * @param IWDGx IWDG Instance
+ * @retval State of bits (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_IWDG_IsReady(IWDG_TypeDef *IWDGx)
+{
+ return (uint32_t)(READ_BIT(IWDGx->SR, IWDG_SR_PVU | IWDG_SR_RVU | IWDG_SR_WVU) == 0U);
+}
+
+/**
+ * @}
+ */
+
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* IWDG) */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32L4xx_LL_IWDG_H */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/targets/stm32l442/src/device.c b/targets/stm32l442/src/device.c
index 80ba742..746ed37 100644
--- a/targets/stm32l442/src/device.c
+++ b/targets/stm32l442/src/device.c
@@ -19,7 +19,7 @@
#include "ctap.h"
#include "crypto.h"
#include "memory_layout.h"
-
+#include "stm32l4xx_ll_iwdg.h"
@@ -393,8 +393,10 @@ led_rgb(0x001040);
delay(50);
+#if SKIP_BUTTON_CHECK_FAST
done:
return 1;
+#endif
fail:
return 0;
@@ -484,7 +486,25 @@ void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
}
}
+void boot_solo_bootloader()
+{
+ LL_IWDG_Enable(IWDG);
+ LL_IWDG_EnableWriteAccess(IWDG);
+
+ LL_IWDG_SetPrescaler(IWDG, LL_IWDG_PRESCALER_4);
+
+ LL_IWDG_SetWindow(IWDG, 4095);
+
+ LL_IWDG_SetReloadCounter(IWDG, 2000); // ~0.25s
+
+ while (LL_IWDG_IsReady(IWDG) != 1)
+ {
+ }
+
+ LL_IWDG_ReloadCounter(IWDG);
+
+}
void _Error_Handler(char *file, int line)
{
diff --git a/tools/programmer.py b/tools/programmer.py
index 81204a7..5b53492 100644
--- a/tools/programmer.py
+++ b/tools/programmer.py
@@ -9,7 +9,7 @@ from binascii import hexlify
from fido2.hid import CtapHidDevice, CTAPHID
from fido2.client import Fido2Client, ClientError
from fido2.ctap import CtapError
-from fido2.ctap1 import CTAP1
+from fido2.ctap1 import CTAP1, ApduError
from fido2.utils import Timeout
from intelhex import IntelHex
@@ -23,7 +23,8 @@ class SoloBootloader:
erase = 0x43
version = 0x44
- HIDCommand = 0x50
+ HIDCommandBoot = 0x50
+ HIDCommandEnterBoot = 0x51
TAG = b'\x8C\x27\x90\xf6'
@@ -72,7 +73,7 @@ class Programmer():
def exchange_hid(self,cmd,addr=0,data=b'A'*16):
req = Programmer.format_request(cmd,addr,data)
- data = self.send_data_hid(SoloBootloader.HIDCommand, req)
+ data = self.send_data_hid(SoloBootloader.HIDCommandBoot, req)
ret = data[0]
if ret != CtapError.ERR.SUCCESS:
@@ -110,9 +111,20 @@ class Programmer():
"""
self.exchange(SoloBootloader.done,0,sig)
+ def enter_solo_bootloader(self,):
+ """
+ If solo is configured as solo hacker or something similar,
+ this command will tell the token to boot directly to the bootloader
+ so it can be reprogrammed
+ """
+ if self.exchange != self.exchange_hid:
+ self.send_data_hid(CTAPHID.INIT, '\x11\x11\x11\x11\x11\x11\x11\x11')
+ self.send_data_hid(SoloBootloader.HIDCommandEnterBoot, '')
+
def program_file(self,name):
- data = json.loads(open(name,'r').read())
+
if name.lower().endswith('.json'):
+ data = json.loads(open(name,'r').read())
fw = base64.b64decode(from_websafe(data['firmware']).encode())
sig = base64.b64decode(from_websafe(data['signature']).encode())
ih = IntelHex()
@@ -126,7 +138,7 @@ class Programmer():
print('Warning, assuming "%s" is an Intel Hex file.' % name)
sig = None
ih = IntelHex()
- ih.fromfile(tmp.name, format='hex')
+ ih.fromfile(name, format='hex')
if self.exchange == self.exchange_hid:
chunk = 2048
@@ -151,12 +163,40 @@ class Programmer():
print('time: %.2f s' % ((t2-t1)/1000.0))
print('Verifying...')
- if sig is not None:
- self.verify_flash(sig)
+ if self.reboot:
+ if sig is not None:
+ self.verify_flash(sig)
+ else:
+ self.verify_flash(b'A'*64)
+
+def attempt_to_find_device(p):
+ found = False
+ for i in range(0,5):
+ try:
+ p.find_device()
+ found = True
+ break
+ except RuntimeError:
+ time.sleep(0.2)
+ return found
+
+def attempt_to_boot_bootloader(p):
+ print('Bootloader not active. Attempting to boot into bootloader mode...')
+ try:
+ p.enter_solo_bootloader()
+ except OSError:
+ pass
+ except CtapError as e:
+ if e.code == CtapError.ERR.INVALID_COMMAND:
+ print('Solo appears to not be a solo hacker. Try holding down the button for 2 while you plug token in.')
+ sys.exit(1)
else:
- self.verify_flash(b'A'*64)
-
-
+ raise(e)
+ print('Solo rebooted. Reconnecting...')
+ time.sleep(.500)
+ if not attempt_to_find_device(p):
+ print('Failed to reconnect!')
+ sys.exit(1)
if __name__ == '__main__':
@@ -166,6 +206,7 @@ if __name__ == '__main__':
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("--enter-bootloader", action="store_true", help = 'Don\'t write anything, try to enter bootloader. Typically only supported by Solo Hacker builds.')
args = parser.parse_args()
print()
@@ -178,7 +219,19 @@ if __name__ == '__main__':
if args.no_reset:
p.set_reboot(False)
- print('version is ', p.version())
+ if args.enter_bootloader:
+ attempt_to_boot_bootloader(p)
+ sys.exit(0)
+
+ try:
+ print('version is ', p.version())
+ except CtapError as e:
+ if e.code == CtapError.ERR.INVALID_COMMAND:
+ attempt_to_boot_bootloader(p)
+ else:
+ raise(e)
+ except ApduError:
+ attempt_to_boot_bootloader(p)
if not args.reset_only:
p.program_file(args.__dict__[''])