def read_memory(ctx: click.Context, address: int, byte_count: int, out_file: click.File, memory_id: int, use_hexdump: bool) -> None: """Read memory. \b ADDRESS - starting address BYTE_COUNT - number of bytes to read FILE - store result into this file, if not specified use stdout MEMORY_ID - id of memory to read from (default: 0) """ with McuBoot(ctx.obj['interface']) as mboot: response = mboot.read_memory(address, byte_count, memory_id) assert response, "Error reading memory" if out_file: out_file.write(response) # type: ignore else: click.echo(format_raw_data(response, use_hexdump=use_hexdump)) display_output([len(response)], mboot.status_code, ctx.obj['use_json'], f"Read {len(response)} of {byte_count} bytes.")
def set_user_key(ctx: click.Context, key_type: int, file_and_size: str) -> None: """Send the user key specified by <type> to bootloader. \b TYPE - Type of user key FILE - Binary file containing user key plaintext SIZE - If not specified, the entire <file> will be sent. Otherwise, only send the first <size> bytes. The valid options of <type> and corresponding <size> are documented in the target's Reference Manual or User Manual. """ file_path, size = parse_file_and_size(file_and_size) with open(file_path, 'rb') as key_file: key_data = key_file.read(size) with McuBoot(ctx.obj['interface']) as mboot: response = mboot.kp_set_user_key(key_type=key_type, key_data=key_data) # type: ignore if not response: raise SPSDKError("Error sending key to the device") display_output([], mboot.status_code, ctx.obj['use_json'])
def generate_key_blob(ctx: click.Context, dek_file: click.File, blob_file: click.File, key_sel: str) -> None: """Generate the Key Blob for a given DEK. \b DEK_FILE - the file with the binary DEK key BLOB_FILE - the generated file with binary key blob KEY_SEL - select the BKEK used to wrap the BK and generate the blob. For devices with SNVS, valid options of [key_sel] are 0, 1 or OTPMK: OTPMK from FUSE or OTP(default), 2 or ZMK: ZMK from SNVS, 3 or CMK: CMK from SNVS, For devices without SNVS, this option will be ignored. """ with McuBoot(ctx.obj['interface']) as mboot: data = dek_file.read() # type: ignore key_sel_int = int(key_sel) if key_sel.isnumeric() else GenerateKeyBlobSelect.get(key_sel) assert isinstance(key_sel_int, int) write_response = mboot.generate_key_blob(data, key_sel=key_sel_int) display_output( [mboot.status_code, len(write_response)] if write_response else None, mboot.status_code, ctx.obj['use_json'] ) if write_response: blob_file.write(write_response) # type: ignore
def enroll(ctx: click.Context) -> None: """Key provisioning enroll.""" with McuBoot(ctx.obj['interface']) as mboot: response = mboot.kp_enroll() display_output([], mboot.status_code, ctx.obj['use_json'])
def reset(ctx: click.Context) -> None: """Reset the device.""" with McuBoot(ctx.obj['interface']) as mboot: mboot.reset(reopen=False) display_output([], mboot.status_code, ctx.obj['use_json'])
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 :raises SPSDKError: When flashloader is not running :raises SPSDKError: When given cpu data were not provided :raises SPSDKError: When there is invalid address :raises SPSDKError: When Log address is in conflict with reserved regions :raises SPSDKError: When write memory failed :raises SPSDKError: When call failed """ # check if the flashloader is running (not None) if not mboot: raise SPSDKError("Flashloader is not running") # get CPU data dir, hab_audit_base and hab_audit_start if not cpu_data: raise SPSDKError( "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") if log_addr <= evk_exec_hab_audit_base: raise SPSDKError("Invalid address") # 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 if not check_reserved_regions(log_addr, reserved_regions): raise SPSDKError("Log address is in conflict with reserved regions") if not mboot.write_memory(evk_exec_hab_audit_base, exec_hab_audit_code, 0): raise SPSDKError("Write memory failed") if not mboot.call(evk_exec_hab_audit_start | 1, 0 if read_log_only else 1): raise SPSDKError("Call failed") log = mboot.read_memory(log_addr, 100, 0) return log
def mcuboot(device): mb = McuBoot(device) mb.open() yield mb mb.close()
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
def init_flashloader(cpu_params: CpuParams) -> McuBoot: """Load an execute flash-loader binary in i.MX-RT The function signs the flashloader if needed (if HAB enabled) :param cpu_params: processor specific parameters of the test :return: McuBoot instance to communicate with flash-loader :raises ConnectionError: if connection cannot be established """ devs = mboot_scan_usb(cpu_params.com_processor_name ) # check whether flashloader is already running if len(devs) == 0: # if flash-loader not running yet, it must be downloaded to RAM and launched flshldr_img = BootImgRT.parse( load_binary(cpu_params.data_dir, "ivt_flashloader.bin")) devs = sdp_scan_usb(cpu_params.com_processor_name) if len(devs) != 1: raise ConnectionError("Cannot connect to ROM bootloader") with SDP(devs[0], cmd_exception=True) as sd: assert sd.is_opened try: sd.read(INT_RAM_ADDR_CODE, 4) # dummy read to receive response except SdpCommandError: # there is an exception if HAB is locked, cause read not supported pass if (sd.status_code == StatusCode.HAB_IS_LOCKED) and (sd.hab_status == ResponseValue.LOCKED): auth_flshldr_img = BootImgRT(flshldr_img.address, BootImgRT.IVT_OFFSET_OTHER) _to_authenticated_image( cpu_params, auth_flshldr_img, flshldr_img.app.data, 0, flshldr_img.ivt.app_address, ) # entry addr cannot be detected from img else: auth_flshldr_img = flshldr_img assert sd.write_file(auth_flshldr_img.address, auth_flshldr_img.export()) try: assert sd.jump_and_run(auth_flshldr_img.address + auth_flshldr_img.ivt_offset) except SdpCommandError: pass # SDP may return an exception if HAB locked for _ in range(10): # wait 10 sec until flash-loader is inited sleep(1) # Scan for MCU-BOOT device devs = mboot_scan_usb(cpu_params.com_processor_name) if len(devs) == 1: break if len(devs) != 1: raise ConnectionError("Cannot connect to Flash-Loader") result = McuBoot(devs[0], cmd_exception=True) result.open() assert result.is_opened result.reopen = False # reopen not supported for RT1050??? return result