Beispiel #1
0
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.")
Beispiel #2
0
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'])
Beispiel #3
0
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
Beispiel #4
0
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'])
Beispiel #5
0
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'])
Beispiel #6
0
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
Beispiel #7
0
def mcuboot(device):
    mb = McuBoot(device)
    mb.open()
    yield mb
    mb.close()
Beispiel #8
0
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
Beispiel #9
0
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