def open_mboot() -> McuBoot: """Open USB communication with RT5xx boot-loader :return: McuBoot instance :raises ConnectionError: if device not connected """ assert not TEST_IMG_CONTENT devs = [] for _ in range( 5 ): # try three times to find USB device (wait until shadows registers are ready) devs = scan_usb("RT5xx") if len(devs) == 1: break sleep(1) if len(devs) != 1: raise ConnectionError( "RT5xx not connected via USB, " "ensure BOOT CONFIG SW7 is ON,OFF,ON and connect USB cable via J38" ) mboot = McuBoot(devs[0], True) mboot.open() assert mboot.is_opened # test connection # blhost -u 0x1FC9,0x20 get-property 1 res = mboot.get_property(PropertyTag.CURRENT_VERSION) assert res is not None return mboot
def burn_srk_fuses(mboot: McuBoot, srk_table: SrkTable, enable_and_close_hab: bool) -> None: """Program SRK fuses into the processor; Not tested on hardware :param mboot: result of `init_flashloader()` :param srk_table: Table of SRK root keys used to provide fuses value :param enable_and_close_hab: optional parameter to enable and close HAB """ assert mboot.get_property(PropertyTag.CURRENT_VERSION) for index, srk_index in enumerate(SRK_FUSES_INDEX): mboot.efuse_program_once(srk_index, srk_table.get_fuse(index)) if enable_and_close_hab: # enable and close HAB mboot.efuse_program_once(ENABLE_HAB_FUSE_INDEX, ENABLE_HAB_FUSE_MASK)
def verify_srk_fuses(mboot: McuBoot, srk_table: SrkTable) -> bool: """Verify fuses in the processor :param mboot: result of `init_flashloader()` :param srk_table: Table of SRK root keys used to provide fuses value :return: True if matches, False if does not match """ assert mboot.get_property(PropertyTag.CURRENT_VERSION) for index, srk_index in enumerate(SRK_FUSES_INDEX): val = mboot.efuse_read_once(srk_index) exp_val = srk_table.get_fuse(index) if val != exp_val: return False return True
def hab_audit_xip_app(cpu_data: CpuData, mboot: McuBoot, read_log_only: bool) -> Optional[bytes]: """Authenticate the application in external FLASH. The function loads application into RAM and invokes its function, that authenticates the application and read the HAB log. Then the HAB log is downloaded and parsed and printed to stdout. :param cpu_data: target cpu data :param mboot: running flashloader :param read_log_only: true to read HAB log without invoking authentication; False to authenticate and read-log It is recommended to call the function firstly with parameter `True` and second time with parameter False to see the difference. :return: bytes contains result of the hab log, otherwise returns None when an error occurred """ # check if the flashloader is running (not None) assert mboot, "Flashloader is not running" # get CPU data dir, hab_audit_base and hab_audit_start assert cpu_data, "Can not read the log, because given cpu data were not provided." cpu_data_bin_dir = cpu_data.bin evk_exec_hab_audit_base = cpu_data.base_address evk_exec_hab_audit_start = cpu_data.start_address # get main directory in absolute format main_dir_absolute = os.path.dirname(__file__) # get hab_audit_executable bin file directory exec_hab_audit_path = os.path.join(os.path.dirname(main_dir_absolute), "data", "cpu_data", cpu_data_bin_dir) if not os.path.isfile(exec_hab_audit_path): print('\nHAB logger not supported for the processor') return None # get executable file, that will be loaded into RAM exec_hab_audit_code = load_binary(exec_hab_audit_path) # find address of the buffer in RAM, where the HAB LOG will be stored log_addr = evk_exec_hab_audit_base + exec_hab_audit_code.find( b'\xA5\x5A\x11\x22\x33\x44\x55\x66') assert log_addr > evk_exec_hab_audit_base # check if the executable binary is in collision with reserved region reserved_regions = mboot.get_property(PropertyTag.RESERVED_REGIONS) # check conflict between hab log address and any of reserved regions # we need 2 values (min and max) - that is why %2 is used assert check_reserved_regions(log_addr, reserved_regions), \ f"Log address is in conflict with reserved regions" assert mboot.write_memory(evk_exec_hab_audit_base, exec_hab_audit_code, 0) assert mboot.call(evk_exec_hab_audit_start | 1, 0 if read_log_only else 1) log = mboot.read_memory(log_addr, 100, 0) return log
class Nitrokey3Bootloader(Nitrokey3Base): """A Nitrokey 3 device running the bootloader.""" def __init__(self, device: RawHid): from . import PID_NITROKEY3_BOOTLOADER, VID_NITROKEY if (device.vid, device.pid) != (VID_NITROKEY, PID_NITROKEY3_BOOTLOADER): raise ValueError("Not a Nitrokey 3 device: expected VID:PID " f"{VID_NITROKEY:x}:{PID_NITROKEY3_BOOTLOADER:x}, " f"got {device.vid:x}:{device.pid:x}") self._path = device.path self.device = McuBoot(device) def __enter__(self) -> "Nitrokey3Bootloader": self.device.open() return self @property def path(self) -> str: if isinstance(self._path, bytes): return self._path.decode("UTF-8") return self._path @property def name(self) -> str: return "Nitrokey 3 Bootloader" @property def status(self) -> Tuple[int, str]: code = self.device.status_code message = StatusCode.desc(code) return (code, message) def close(self) -> None: self.device.close() def reboot(self) -> None: if not self.device.reset(reopen=False): # On Windows, this function returns false even if the reset was successful if platform.system() == "Windows": logger.warning("Failed to reboot Nitrokey 3 bootloader") else: raise Exception("Failed to reboot Nitrokey 3 bootloader") def uuid(self) -> Optional[int]: uuid = self.device.get_property( PropertyTag.UNIQUE_DEVICE_IDENT) # type: ignore[arg-type] if not uuid: raise ValueError("Missing response for UUID property query") if len(uuid) != UUID_LEN: raise ValueError(f"UUID response has invalid length {len(uuid)}") # See GetProperties::device_uuid in the lpc55 crate: # https://github.com/lpc55/lpc55-host/blob/main/src/bootloader/property.rs#L222 wrong_endian = (uuid[3] << 96) + (uuid[2] << 64) + ( uuid[1] << 32) + uuid[0] right_endian = wrong_endian.to_bytes(16, byteorder="little") return int.from_bytes(right_endian, byteorder="big") def update( self, image: bytes, callback: Optional[Callable[[int, int], None]] = None, check_errors: bool = False, ) -> bool: return self.device.receive_sb_file( image, progress_callback=callback, check_errors=check_errors, ) @staticmethod def list() -> List["Nitrokey3Bootloader"]: from . import PID_NITROKEY3_BOOTLOADER, VID_NITROKEY device_filter = USBDeviceFilter( f"0x{VID_NITROKEY:x}:0x{PID_NITROKEY3_BOOTLOADER:x}") devices = [] for device in RawHid.enumerate(device_filter): # TODO: remove assert if https://github.com/NXPmicro/spsdk/issues/32 is fixed assert isinstance(device, RawHid) try: devices.append(Nitrokey3Bootloader(device)) except ValueError: logger.warn( f"Invalid Nitrokey 3 bootloader returned by enumeration: {device}" ) return devices @staticmethod def open(path: str) -> Optional["Nitrokey3Bootloader"]: device_filter = USBDeviceFilter(path) devices = RawHid.enumerate(device_filter) if len(devices) == 0: logger.warn(f"No HID device at {path}") return None if len(devices) > 1: logger.warn(f"Multiple HID devices at {path}: {devices}") return None try: # TODO: remove assert if https://github.com/NXPmicro/spsdk/issues/32 is fixed assert isinstance(devices[0], RawHid) return Nitrokey3Bootloader(devices[0]) except ValueError: logger.warn(f"No Nitrokey 3 bootloader at path {path}", exc_info=sys.exc_info()) return None