def load_from_config( cls, config: Dict[str, Any] ) -> Union["CmdLoad", "CmdLoadHashLocking", "CmdLoadCmac"]: """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. :raises SPSDKError: Invalid configuration field. """ authentication = config.get("authentication") address = value_to_int(config["address"], 0) memory_id = value_to_int(config.get("memoryId", "0"), 0) if authentication == "hashlocking": data = load_binary(config["file"]) return CmdLoadHashLocking.load_from_config(config) # Backward compatibility if authentication == "cmac": data = load_binary(config["file"]) return CmdLoadCmac.load_from_config(config) # Backward compatibility # general non-authenticated load command if config.get("file"): data = load_binary(config["file"]) return CmdLoad(address=address, data=data, memory_id=memory_id) if config.get("values"): values = [value_to_int(s, 0) for s in config["values"].split(",")] data = pack(f"<{len(values)}L", *values) return CmdLoad(address=address, data=data, memory_id=memory_id) raise SPSDKError(f"Unsupported LOAD command args: {config}")
def from_xml_element(cls, xml_element: ET.Element) -> "RegsRegister": """Initialization register by XML ET element. :param xml_element: Input XML subelement with register data. :return: The instance of this class. """ name = xml_element.attrib[ "name"] if "name" in xml_element.attrib else "N/A" offset = value_to_int(xml_element.attrib["offset"] ) if "offset" in xml_element.attrib else 0 width = value_to_int(xml_element.attrib["width"] ) if "width" in xml_element.attrib else 0 descr = xml_element.attrib[ "description"] if "description" in xml_element.attrib else "N/A" reverse = (xml_element.attrib["reversed"] if "reversed" in xml_element.attrib else "False") == "True" access = xml_element.attrib[ "access"] if "access" in xml_element.attrib else "N/A" reg = cls(name, offset, width, descr, reverse, access) if xml_element.text: xml_bitfields = xml_element.findall("bit_field") xml_bitfields.extend(xml_element.findall("reserved_bit_field")) xml_bitfields_len = len(xml_bitfields) for xml_bitfield in xml_bitfields: bitfield = RegsBitField.from_xml_element(xml_bitfield, reg) if xml_bitfields_len == 1 and bitfield.width == reg.width: if len(reg.description) < len(bitfield.description): reg.description = bitfield.description reg.access = bitfield.access else: if reg.access == "N/A": reg.access = "Bitfields depended" reg.add_bitfield(bitfield) return reg
def from_xml_element(cls, xml_element: ET.Element, parent: "RegsRegister") -> "RegsBitField": """Initialization register by XML ET element. :param xml_element: Input XML subelement with register data. :param parent: Reference to parent RegsRegister object. :return: The instance of this class. """ name = xml_element.attrib[ "name"] if "name" in xml_element.attrib else "N/A" offset = value_to_int(xml_element.attrib["offset"] ) if "offset" in xml_element.attrib else 0 width = value_to_int(xml_element.attrib["width"] ) if "width" in xml_element.attrib else 0 descr = xml_element.attrib[ "description"] if "description" in xml_element.attrib else "N/A" access = xml_element.attrib[ "access"] if "access" in xml_element.attrib else "R/W" reset_value = (value_to_int(xml_element.attrib["reset_value"]) if "reset_value" in xml_element.attrib else 0) hidden = False if xml_element.tag == "bit_field" else True bitfield = cls(parent, name, offset, width, descr, reset_value, access, hidden) if xml_element.text: xml_enums = xml_element.findall("bit_field_value") for xml_enum in xml_enums: bitfield.add_enum(RegsEnum.from_xml_element(xml_enum, width)) return bitfield
def test_value_to_int(value, res, exc): """Test of value_to_int function""" if not exc: assert res == value_to_int(value) else: with pytest.raises(SPSDKError): value_to_int(value)
def load_from_config(cls, config: Dict[str, Any]) -> "SecureBinary31": """Creates instantion of SecureBinary31 from configuration. :param config: Input standard configuration. :return: Instantion of Secure Binary V3.1 class """ container_keyblob_enc_key_path = config.get( "containerKeyBlobEncryptionKey") is_nxp_container = config.get("isNxpContainer", False) description = config.get("description") kdk_access_rights = value_to_int(config.get("kdkAccessRights", 0)) container_configuration_word = value_to_int( config.get("containerConfigurationWord", 0)) firmware_version = value_to_int(config.get("firmwareVersion", 1)) commands = config["commands"] is_encrypted = config.get("isEncrypted", True) timestamp = config.get("timestamp") if timestamp: # re-format it timestamp = value_to_int(timestamp) cert_block = CertBlockV31.from_config(config) # if use_isk is set, we use for signing the ISK certificate instead of root signing_key_path = (config.get("signingCertificatePrivateKeyFile") if cert_block.isk_certificate else config.get("mainRootCertPrivateKeyFile")) curve_name = (config.get("iskCertificateEllipticCurve") if cert_block.isk_certificate else config.get("rootCertificateEllipticCurve")) assert curve_name and isinstance(curve_name, str) assert signing_key_path signing_key = load_binary( signing_key_path) if signing_key_path else None assert signing_key pck = None if is_encrypted: assert container_keyblob_enc_key_path pck = bytes.fromhex(load_text(container_keyblob_enc_key_path)) # Create SB3 object sb3 = SecureBinary31( pck=pck, cert_block=cert_block, curve_name=curve_name, kdk_access_rights=kdk_access_rights, firmware_version=firmware_version, description=description, is_nxp_container=is_nxp_container, flags=container_configuration_word, signing_key=signing_key, timestamp=timestamp, is_encrypted=is_encrypted, ) # Add commands into the SB3 object sb3.sb_commands.load_from_config(commands) return sb3
def load_from_config(cls, config: Dict[str, Any]) -> "CmdProgFuses": """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. """ address = value_to_int(config["address"], 0) fuses = [value_to_int(fuse, 0) for fuse in config["values"].split(",")] data = pack(f"<{len(fuses)}L", *fuses) return CmdProgFuses(address=address, data=data)
def load_from_config(cls, config: Dict[str, Any]) -> "CmdErase": """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. """ address = value_to_int(config["address"], 0) length = value_to_int(config["size"], 0) memory_id = value_to_int(config.get("memoryId", "0"), 0) return CmdErase(address=address, length=length, memory_id=memory_id)
def load_from_config(cls, config: Dict[str, Any]) -> "CmdConfigureMemory": """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. """ memory_id = value_to_int(config["memoryId"], 0) return CmdConfigureMemory( address=value_to_int(config["configAddress"], 0), memory_id=memory_id )
def load_from_config(cls, config: Dict[str, Any]) -> "CmdFillMemory": """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. """ address = value_to_int(config["address"], 0) length = value_to_int(config["size"], 0) pattern = value_to_int(config["pattern"], 0) return CmdFillMemory(address=address, length=length, pattern=pattern)
def load_from_config(cls, config: Dict[str, Any]) -> "CmdLoadHashLocking": """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. :raises SPSDKError: Invalid configuration field. """ address = value_to_int(config["address"], 0) memory_id = value_to_int(config.get("memoryId", "0"), 0) data = load_binary(config["file"]) return CmdLoadHashLocking(address=address, data=data, memory_id=memory_id)
def _is_number(param: Any) -> bool: """Checks whether the input represents a number. :param param: Input to analyze :raises SPSDKError: Input doesn't represent a number :return: True if input represents a number """ try: value_to_int(param) return True except SPSDKError: return False
def mix_load_from_config(self, config: Dict[str, Any]) -> None: """Load configuration from dictionary. :param config: Dictionary with configuration fields. """ super().mix_load_from_config(config) self.firmware_version = value_to_int(config.get("firmwareVersion", 0)) sign_hash_len_raw = config.get("manifestSigningHashLength", None) sign_hash_len = value_to_int( sign_hash_len_raw) if sign_hash_len_raw else None self.manifest = MasterBootImageManifest(self.firmware_version, self.tz, sign_hash_len=sign_hash_len)
def load_registers_from_xml(self, xml: str, filter_reg: List[str] = None, grouped_regs: List[dict] = None) -> None: """Function loads the registers from the given XML. :param xml: Input XML data in string format. :param filter_reg: List of register names that should be filtered out. :param grouped_regs: List of register prefixes names to be grouped int one. :raises SPSDKRegsError: XML parse problem occuress. """ def is_reg_in_group(reg: str) -> Union[dict, None]: """Help function to recognize if the register should be part of group.""" if grouped_regs: for group in grouped_regs: if reg.startswith(group["name"]): return group return None try: xml_elements = ET.parse(xml) except ET.ParseError as exc: raise SPSDKRegsError(f"Cannot Parse XML data: {str(exc)}") from exc xml_registers = xml_elements.findall("register") xml_registers = self._filter_by_names(xml_registers, filter_reg or []) # Load all registers into the class for xml_reg in xml_registers: group = is_reg_in_group(xml_reg.attrib["name"]) if group: try: group_reg = self.find_reg(group["name"]) except SPSDKRegsErrorRegisterNotFound: group_reg = RegsRegister( name=group["name"], offset=value_to_int(group.get("offset", 0)), width=value_to_int(group.get("width", 0)), description=group.get( "description", f"Group of {group['name']} registers."), reverse=value_to_bool(group.get("reverse", False)), access=group.get("access", None), config_as_hexstring=group.get("config_as_hexstring", False), ) self.add_register(group_reg) group_reg.add_group_reg(RegsRegister.from_xml_element(xml_reg)) else: self.add_register(RegsRegister.from_xml_element(xml_reg))
def from_xml_element(cls, xml_element: ET.Element, maxwidth: int = 0) -> "RegsEnum": """Initialization Enum by XML ET element. :param xml_element: Input XML subelement with enumeration data. :param maxwidth: The maximal width of bitfield for this enum (used for formating). :return: The instance of this class. :raises SPSDKRegsError: Error during enum XML parsing. """ name = xml_element.attrib[ "name"] if "name" in xml_element.attrib else "N/A" if "value" not in xml_element.attrib: raise SPSDKRegsError(f"Missing Enum Value Key for {name}.") raw_val = xml_element.attrib["value"] try: value = value_to_int(raw_val) except (TypeError, ValueError, SPSDKError) as exc: raise SPSDKRegsError(f"Invalid Enum Value: {raw_val}") from exc descr = xml_element.attrib[ "description"] if "description" in xml_element.attrib else "N/A" return cls(name, value, descr, maxwidth)
def set_value(self, val: Any, raw: bool = False) -> None: """Set the new value of register. :param val: The new value to set. :param raw: Do not use any modification hooks. :raises SPSDKError: When invalid values is loaded into register """ try: value = value_to_int(val) if not raw and len(self._set_value_hooks) > 0: for hook in self._set_value_hooks: value = hook[0](value, hook[1]) self._value = value if self.has_group_registers(): # Update also values in sub registers subreg_width = self.sub_regs[0].width for index, sub_reg in enumerate(self.sub_regs, start=1): # sub_reg.set_value((value >> (index * subreg_width)) & ((1 << subreg_width) - 1)) sub_reg.set_value((value >> (self.width - index * subreg_width)) & ((1 << subreg_width) - 1)) except SPSDKError: logger.error(f"Loaded invalid value {str(val)}") raise SPSDKError(f"Loaded invalid value {str(val)}")
def load_yml_config( self, yml_data: Any, exclude_regs: List[str] = None, exclude_fields: Dict[str, Dict[str, str]] = None, ) -> None: """The function loads the configuration from YML file. :param yml_data: The YAML commented data with register values. :param exclude_regs: List of excluded registers :param exclude_fields: Dictionary with lists of excluded bitfields """ for reg_name in yml_data.keys(): reg_dict = yml_data[reg_name] register = self.find_reg(reg_name, include_group_regs=True) if "value" in reg_dict.keys(): raw_val = reg_dict["value"] val = int( raw_val, 16 ) if register.config_as_hexstring else value_to_int(raw_val) register.set_value(val, True) elif "bitfields" in reg_dict.keys(): for bitfield_name in reg_dict["bitfields"]: bitfield_val = reg_dict["bitfields"][bitfield_name] bitfield = register.find_bitfield(bitfield_name) if (exclude_fields and reg_name in exclude_fields.keys() and bitfield_name in exclude_fields[reg_name]): continue bitfield.set_enum_value(bitfield_val, True) else: logger.error(f"There are no data for {reg_name} register.") logger.debug( f"The register {reg_name} has been loaded from configuration.")
def __init__( self, parent: "RegsRegister", name: str, offset: int, width: int, description: str = None, reset_val: Any = "0", access: str = "RW", hidden: bool = False, ) -> None: """Constructor of RegsBitField class. Used to store bitfield information. :param parent: Parent register of bitfield. :param name: Name of bitfield. :param offset: Bit offset of bitfield. :param width: Bit width of bitfield. :param description: Text description of bitfield. :param reset_val: Reset value of bitfield. :param access: Access type of bitfield. :param hidden: The bitfield will be hidden from standard searches. """ self.parent = parent self.name = name or "N/A" self.offset = offset self.width = width self.description = description or "N/A" self.reset_value = value_to_int(reset_val, 0) self.access = access self.hidden = hidden self._enums: List[RegsEnum] = [] self._update_reset_value() self.set_value(self.reset_value, raw=True)
def mix_load_from_config(self, config: Dict[str, Any]) -> None: """Load configuration from dictionary. :param config: Dictionary with configuration fields. """ value = config.get("outputImageExecutionAddress") assert value is not None self.load_address = value_to_int(value)
def load_from_config(cls, config: Dict[str, Any]) -> "CmdCall": """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. """ address = value_to_int(config["address"], 0) return CmdCall(address=address)
def load_from_config(cls, config: Dict[str, Any]) -> "CmdCopy": """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. """ address = value_to_int(config["addressFrom"], 0) length = value_to_int(config["size"], 0) destination_address = value_to_int(config["addressTo"], 0) memory_id_from = value_to_int(config["memoryIdFrom"], 0) memory_id_to = value_to_int(config["memoryIdTo"], 0) return CmdCopy( address=address, length=length, destination_address=destination_address, memory_id_from=memory_id_from, memory_id_to=memory_id_to, )
def load_from_config(cls, config: Dict[str, Any]) -> "CmdProgIfr": """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. """ address = value_to_int(config["address"], 0) data = load_binary(config["file"]) return CmdProgIfr(address=address, data=data)
def load_from_config(cls, config: Dict[str, Any]) -> "CmdFwVersionCheck": """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. """ value = value_to_int(config["value"], 0) counter_id_str = config["counterId"] counter_id = CmdFwVersionCheck.CounterID[counter_id_str] return CmdFwVersionCheck(value=value, counter_id=counter_id)
def _get_seal_count(self) -> int: """Function returns seal count for the device. :return: Count of seals fields. :raises SPSDKError: When 'seal_count' in database.json can not be found """ count = self.config.get_seal_count(self.device) if not count: raise SPSDKError("Can't find 'seal_count' in database.json") return value_to_int(count)
def load_from_config(cls, config: Dict[str, Any]) -> "CmdLoadKeyBlob": """Load configuration from dictionary. :param config: Dictionary with configuration fields. :return: Command object loaded from configration. """ data = load_binary(config["file"]) offset = value_to_int(config["offset"], 0) key_wrap_name = config["wrappingKeyId"] key_wrap_id = CmdLoadKeyBlob.KeyWraps[key_wrap_name] return CmdLoadKeyBlob(offset=offset, data=data, key_wrap_id=key_wrap_id)
def parse_property_tag(property_tag: str) -> int: """Convert the property as name or stringified number into integer. :param property_tag: Name or number of the property tag :return: Property integer tag """ try: value = value_to_int(property_tag) return value if value in PROPERTIES_NAMES.values() else 0xFF except SPSDKError: return PROPERTIES_NAMES.get(property_tag, 0xFF)
def _parse_key_type(user_input: str, collection: Any, default: int = None) -> int: try: return value_to_int(user_input) except SPSDKError: key_type = user_input.upper() key_type_int = collection.get(key_type, default) if key_type_int is None: raise SPSDKError( # pylint: disable=raise-missing-from f"Unable to find '{user_input}' in '{collection.__name__}'") return key_type_int
def from_config(cls, config: Dict[str, Any]) -> "CertBlockV31": """Creates instantion of CertBlockV31 from configuration. :param config: Input standard configuration. :return: Instantion of CertBlockV3.1 :raises SPSDKError: If found gap in certificates from config file. """ root_certificates_loaded: List[Optional[str]] = [ config.get(f"rootCertificate{idx}File") for idx in range(4) ] # filter out None and empty values root_certificates = list(filter(None, root_certificates_loaded)) for org, filtered in zip(root_certificates_loaded, root_certificates): if org != filtered: raise SPSDKError( "There are gaps in rootCertificateXFile definition") main_root_cert_id = config.get("mainRootCertId", 0) main_root_private_key_file = config.get("mainRootCertPrivateKeyFile") use_isk = config.get("useIsk", False) isk_certificate = config.get("signingCertificateFile") isk_constraint = value_to_int( config.get("signingCertificateConstraint", "0")) isk_sign_data_path = config.get("signCertData") root_certs = [ misc.load_binary(cert_file) for cert_file in root_certificates ] user_data = None isk_private_key = None isk_cert = None if use_isk: assert isk_certificate and main_root_private_key_file if isk_sign_data_path: user_data = misc.load_binary(isk_sign_data_path) isk_private_key = misc.load_binary(main_root_private_key_file) isk_cert = misc.load_binary(isk_certificate) cert_block = CertBlockV31( root_certs=root_certs, used_root_cert=main_root_cert_id, user_data=user_data, constraints=isk_constraint, isk_cert=isk_cert, ca_flag=not use_isk, isk_private_key=isk_private_key, ) return cert_block
def _custom_export(self) -> bytes: if self.presets is None: raise SPSDKError("Preset data not present") if self.customs is None: raise SPSDKError("Data not present") logger.info(f"{len(self.presets)} registers loaded from defaults") logger.debug(self.presets) logger.info(f"{len(self.customs)} modifications provided") logger.debug(self.customs) data = self.presets data.update(self.customs) registers = [value_to_int(item) for item in data.values()] # transform data into binary format (little endian, 32b per register) return struct.pack(f"<{len(registers)}I", *registers)
def set_value(self, new_val: Any, raw: bool = False) -> None: """Updates the value of the bitfield. :param new_val: New value of bitfield. :param raw: If set, no automatic modification of value is applied. :raises SPSDKError: The input value is out of range. """ new_val_int = value_to_int(new_val) if new_val_int > 1 << self.width: raise SPSDKError("The input value is out of bitfield range") reg_val = self.parent.get_value() mask = ((1 << self.width) - 1) << self.offset reg_val = reg_val & ~mask value = (new_val_int << self.offset) & mask reg_val = reg_val | value self.parent.set_value(reg_val, raw)
def set_enum_value(self, new_val: str, raw: bool = False) -> None: """Updates the value of the bitfield by its enum value. :param new_val: New enum value of bitfield. :param raw: If set, no automatic modification of value is applied. :raises SPSDKRegsErrorEnumNotFound: Input value cannot be decoded. """ try: val_int = self.get_enum_constant(new_val) except SPSDKRegsErrorEnumNotFound as exc: # Try to decode standard input try: val_int = value_to_int(new_val) except TypeError: raise exc self.set_value(val_int, raw)