def sign_data_blob(self, data_to_sign: bytes, key: bytes) -> bytes: """Get HSM encryption sign for data blob. :param data_to_sign: Input data to sign. :param key: FW signing key (MFWISK). :raises SPSDKError: In case of any vulnerability. :return: Data blob signature (64 bytes). """ if not self.mboot.write_memory(self.DEVBUFF_BASE0, key): raise SPSDKError("Cannot write signing key into device.") if not self.mboot.write_memory(self.DEVBUFF_BASE1, data_to_sign): raise SPSDKError("Cannot write Data to sign into device.") hsm_gen_key_res = self.mboot.tp_hsm_enc_sign( self.DEVBUFF_BASE0, len(key), self.DEVBUFF_BASE1, len(data_to_sign), self.DEVBUFF_BASE2, self.DEFBUFF_SB3_SIGNATURE_SIZE, ) if hsm_gen_key_res != self.DEFBUFF_SB3_SIGNATURE_SIZE: raise SPSDKError("HSM signing command failed.") signature = self.mboot.read_memory( self.DEVBUFF_BASE2, self.DEFBUFF_SB3_SIGNATURE_SIZE, ) if not signature: raise SPSDKError("Cannot read generated signature from device.") self.store_temp_res("SB3_sign.bin", signature, "to_merge") return signature
def align_block(data: Union[bytes, bytearray], alignment: int = 4, padding: int = 0) -> bytes: """Align binary data block length to specified boundary by adding padding bytes to the end. :param data: to be aligned :param alignment: boundary alignment (typically 2, 4, 16, 64 or 256 boundary) :param padding: byte to be added, use -1 to fill with random data :return: aligned block :raises SPSDKError: When there is wrong alignment """ assert isinstance(data, (bytes, bytearray)) if alignment < 0: raise SPSDKError("Wrong alignment") if padding < -1 or padding > 255: raise SPSDKError("Wrong padding") current_size = len(data) num_padding = align(current_size, alignment) - current_size if not num_padding: return bytes(data) if padding == -1: # pylint: disable=import-outside-toplevel from spsdk.utils.crypto.common import crypto_backend return bytes(data + crypto_backend().random_bytes(num_padding)) return bytes(data + bytes([padding]) * num_padding)
def _get_signature(self) -> bytes: if not self.sig_provider: raise SPSDKError("Signature provider is not set") signature = self.sig_provider.sign(self._get_data_for_signature()) if not signature: raise SPSDKError("Signature is not present") return signature
def validate(self) -> None: """Validate the settings of class members. :raises SPSDKError: Invalid configuration of SB3.1 header blob class members. """ if self.flags is None: raise SPSDKError("Invalid SB3.1 header flags.") if self.block_count is None or self.block_count < 0: raise SPSDKError("Invalid SB3.1 header block count.") if self.curve_name is None or self.curve_name not in [ "secp256r1", "secp384r1" ]: raise SPSDKError("Invalid SB3.1 header curve name.") if self.block_size is None or self.block_size != self.calculate_block_size( ): raise SPSDKError("Invalid SB3.1 header block size.") if self.image_type is None or self.image_type not in [6, 7]: raise SPSDKError("Invalid SB3.1 header image type.") if self.firmware_version is None: raise SPSDKError("Invalid SB3.1 header firmware version.") if self.timestamp is None: raise SPSDKError("Invalid SB3.1 header timestamp.") if self.image_total_length is None or self.image_total_length < self.HEADER_SIZE: raise SPSDKError("Invalid SB3.1 header image total length.") if self.cert_block_offset is None: raise SPSDKError( "Invalid SB3.1 header certification block offset.") if self.description is None or len(self.description) != 16: raise SPSDKError("Invalid SB3.1 header image description.")
def _validate_keyblob(keyblobs: List, keyblob_id: Number) -> Optional[Dict]: """Checks, whether a keyblob is valid. Parser returns a list of dicts which contains keyblob definitions. These definitions should contain a 'start', 'end', 'key' & 'counter' keys with appropriate values. To be able to create a keyblob, we need these for values. Otherwise we throw an exception that the keyblob is invalid. :param keyblobs: list of dicts defining keyblobs :param keyblob_id: id of keyblob we want to check :raises SPSDKError: If the keyblob definition is empty :raises SPSDKError: If the kyeblob definition is missing one key :return: keyblob If exists and is valid, None otherwise """ for kb in keyblobs: if keyblob_id == kb["keyblob_id"]: kb_content = kb["keyblob_content"] if len(kb_content) == 0: raise SPSDKError(f"Keyblob {keyblob_id} definition is empty!") for key in ["start", "end", "key", "counter"]: if key not in kb_content[0]: raise SPSDKError(f"Keyblob {keyblob_id} is missing '{key}' definition!") return kb else: continue return None
def parse_image_file(file_path: str) -> List[SegmentInfo]: """Parse image. :param file_path: path, where the image is stored :raises SPSDKError: When elf/axf files are used :raises SPSDKError: When binary file is used :raises SPSDKError: When unsupported file is used :return: SegmentInfo object """ with open(file_path, "rb") as f: data = f.read(4) if data == b"\x7fELF": raise SPSDKError("Elf file is not supported") try: binfile = bincopy.BinFile(file_path) return [ SegmentInfo(start=segment.address, length=len(segment.data), data_bin=segment.data) for segment in binfile.segments ] except UnicodeDecodeError as e: raise SPSDKError( "Error: please use write-memory command for binary file downloading." ) from e except Exception as e: raise SPSDKError("Error loading file") from e
def __init__( self, root_certs: Union[Sequence[ECC.EccKey], Sequence[bytes]], ca_flag: bool, version: str = "2.1", used_root_cert: int = 0, constraints: int = 0, isk_private_key: Union[ECC.EccKey, bytes] = None, isk_cert: Union[ECC.EccKey, bytes] = None, user_data: bytes = None, ) -> None: """The Constructor for Certificate block.""" # workaround for base MasterBootImage self.signature_size = 0 self.header = CertificateBlockHeader(version) self.root_key_record = RootKeyRecord( ca_flag=ca_flag, used_root_cert=used_root_cert, root_certs=root_certs ) self.isk_certificate = None if not ca_flag: if not isk_private_key: raise SPSDKError("ISK private key is not set.") if not isk_cert: raise SPSDKError("ISK certificate is not set.") self.isk_certificate = IskCertificate( constraints=constraints, isk_private_key=isk_private_key, isk_cert=isk_cert, user_data=user_data, ) self.expected_size = self._calculate_expected_size()
def export(self) -> bytes: """Serialize Certificate Block V2 object.""" # At least one certificate must be used if not self._cert: raise SPSDKError("At least one certificate must be used") # The hast of root key certificate must be in RKHT if self.rkh_index is None: raise SPSDKError("The HASH of used Root Key must be in RKHT") # CA: Using a single certificate is allowed. In this case, the sole certificate must be self-signed and must not # be a CA. If multiple certificates are used, the root must be self-signed and all but the last must be CAs. if self._cert[-1].ca: raise SPSDKError("The last chain certificate must not be CA") if not all(cert.ca for cert in self._cert[:-1]): raise SPSDKError("All certificates except the last chain certificate must be CA") # Export data = self.header.export() for cert in self._cert: data += pack("<I", cert.raw_size) data += cert.export() for key in self._root_key_hashes: data += bytes(key) data = misc.align_block(data, self.alignment) if len(data) != self.raw_size: raise SPSDKError("Invalid length of data") return data
def wrap_key(self, user_pck: bytes) -> bytes: """Wrap the user PCK key. :param user_pck: User PCK key :raises SPSDKError: In case of any vulnerability. :return: Wrapped user PCK by RFC3396. """ if not self.mboot.write_memory(self.DEVBUFF_BASE0, user_pck): raise SPSDKError("Cannot write USER_PCK into device.") hsm_store_key_res = self.mboot.tp_hsm_store_key( TrustProvKeyType.CKDFK, 0x01, self.DEVBUFF_BASE0, self.DEVBUFF_USER_PCK_KEY_SIZE, self.DEVBUFF_BASE1, self.DEVBUFF_SIZE, ) if not hsm_store_key_res: raise SPSDKError("HSM Store Key command failed.") if hsm_store_key_res[1] != self.DEVBUFF_WRAPPED_USER_PCK_KEY_SIZE: raise SPSDKError("HSM Store Key command has invalid results.") wrapped_user_pck = self.mboot.read_memory( self.DEVBUFF_BASE1, self.DEVBUFF_WRAPPED_USER_PCK_KEY_SIZE, ) if not wrapped_user_pck: raise SPSDKError("Cannot read WRAPPED USER PCK from device.") self.store_temp_res("CUST_MK_SK.bin", wrapped_user_pck) return wrapped_user_pck
def plain_data(self) -> bytes: """Plain data for selected key range. :return: key blob exported into binary form (serialization) :raises SPSDKError: Invalid value of zero fill parameter :raises SPSDKError: Invalid value crc :raises SPSDKError: Invalid length binary data """ result = bytes() result += self.key result += self.ctr_init_vector result += pack("<I", self.start_addr) end_addr_with_flags = (self.end_addr & ~self._KEY_FLAG_MASK) | self.key_flags result += pack("<I", end_addr_with_flags) # zero fill if self.zero_fill: if len(self.zero_fill) != 4: raise SPSDKError("Invalid value") result += self.zero_fill else: result += crypto_backend().random_bytes(4) # CRC is not used, use random value if self.crc_fill: if len(self.crc_fill) != 4: raise SPSDKError("Invalid value crc") result += self.crc_fill else: result += crypto_backend().random_bytes(4) result += bytes([0] * 8) # expanded_wrap_data result += bytes([0] * 16) # unused filler if len(result) != 64: raise SPSDKError("Invalid length binary data") return result
def enable_debug(probe: DebugProbe, ap_mem: int = 0) -> bool: """Function that enables debug access ports on devices with debug mailbox. :param probe: Initialized debug probe. :param ap_mem: Index of Debug access port for memory interface. :return: True if debug port is enabled, False otherwise :raises SPSDKError: Unlock method failed. """ debug_enabled = False try: logger.debug("step 3: Check if AHB is enabled") if not test_ahb_access(probe, ap_mem): logger.debug("Locked Device. Launching unlock sequence.") # Start debug mailbox system StartDebugSession(dm=DebugMailbox(debug_probe=probe)).run() # Recheck the AHB access if test_ahb_access(probe, ap_mem): logger.debug("Access granted") debug_enabled = True else: logger.debug("Enable debug operation failed!") else: logger.debug("Unlocked Device") debug_enabled = True except AttributeError as exc: raise SPSDKError(f"Invalid input parameters({str(exc)})") from exc except SPSDKDebugProbeError as exc: raise SPSDKError(f"Can't unlock device ({str(exc)})") from exc return debug_enabled
def __init__( self, dek: Optional[bytes] = None, mac: Optional[bytes] = None, nonce: Optional[bytes] = None, timestamp: Optional[datetime] = None, ): """Initialize SBV2xAdvancedParams. :param dek: DEK key :param mac: MAC key :param nonce: nonce :param timestamp: fixed timestamp for the header; use None to use current date/time :raises SPSDKError: Invalid dek or mac :raises SPSDKError: Invalid length of nonce """ self._dek: bytes = dek if dek else crypto_backend().random_bytes(32) self._mac: bytes = mac if mac else crypto_backend().random_bytes(32) self._nonce: bytes = nonce if nonce else SBV2xAdvancedParams._create_nonce( ) if timestamp is None: timestamp = datetime.now() self._timestamp = datetime.fromtimestamp(int(timestamp.timestamp())) if len(self._dek) != 32 and len(self._mac) != 32: raise SPSDKError("Invalid dek or mac") if len(self._nonce) != 16: raise SPSDKError("Invalid length of nonce")
def read_raw_data( stream: Union[io.BufferedReader, io.BytesIO], length: int, index: int = None, no_seek: bool = False, ) -> bytes: """Read raw data.""" if index is not None: if index < 0: raise SPSDKError(f" Index must be non-negative, found {index}") if index != stream.tell(): stream.seek(index) if length < 0: raise SPSDKError(f" Length must be non-negative, found {length}") try: data = stream.read(length) except Exception as exc: raise StreamReadFailed(f" stream.read() failed, requested {length} bytes") from exc if len(data) != length: raise NotEnoughBytesException( f" Could not read enough bytes, expected {length}, found {len(data)}" ) if no_seek: stream.seek(-length, SEEK_CUR) return data
def parse_hex_data(hex_data: str) -> bytes: """Parse hex-data into bytes. :param hex_data: input hex-data, e.g: {{1122}}, {{11 22}} :raises SPSDKError: Failure to parse given input :return: data parsed from input """ hex_data = hex_data.replace(' ', '') if not hex_data.startswith('{{') or not hex_data.endswith('}}'): raise SPSDKError( "Incorrectly formated hex-data: Need to start with {{ and end with }}" ) hex_data = hex_data.replace('{{', '').replace('}}', '') if len(hex_data) % 2: raise SPSDKError( "Incorrectly formated hex-data: Need to have even number of characters" ) if not re.fullmatch(r"[0-9a-fA-F]*", hex_data): raise SPSDKError("Incorrect hex-data: Need to have valid hex string") str_parts = [hex_data[i:i + 2] for i in range(0, len(hex_data), 2)] byte_pieces = [int(part, 16) for part in str_parts] result = bytes(byte_pieces) if not result: raise SPSDKError("Incorrect hex-data: Unable to get any data") return bytes(byte_pieces)
def add_certificate(self, cert: Union[bytes, Certificate]) -> None: """Add certificate. First call adds root certificate. Additional calls add chain certificates. :param cert: The certificate itself in DER format :raises SPSDKError: If certificate cannot be added """ if isinstance(cert, bytes): cert_obj = Certificate(cert) elif isinstance(cert, Certificate): cert_obj = cert else: raise SPSDKError("Invalid parameter type (cert)") if cert_obj.version != "v3": raise SPSDKError("Expected certificate v3 but received: " + cert_obj.version) if self._cert: # chain certificate? last_cert = self._cert[-1] # verify that it is signed by parent key if not cert_obj.verify(last_cert.public_key_modulus, last_cert.public_key_exponent): raise SPSDKError("Chain certificate cannot be verified using parent public key") else: # root certificate if cert_obj.self_signed == "no": raise SPSDKError("Root certificate must be self-signed") self._cert.append(cert_obj) self._header.cert_count += 1 self._header.cert_table_length += cert_obj.raw_size + 4
def aes_key_unwrap(self, kek: bytes, wrapped_key: bytes) -> bytes: """Unwraps a key using a key-encrypting key (KEK). :param kek: The key-encrypting key :param wrapped_key: Encrypted data :return: Un-wrapped key :raises SPSDKError: Invalid length of kek or key_to_wrap """ if len(kek) not in (16, 24, 32): raise SPSDKError("The wrapping key must be a valid AES key length") if len(wrapped_key) < 24: raise SPSDKError("Must be at least 24 bytes") if len(wrapped_key) % 8 != 0: raise SPSDKError("The wrapped key must be a multiple of 8 bytes") # default iv iv = 0xA6A6A6A6A6A6A6A6 n = len(wrapped_key) // 8 - 1 # NOTE: R[0] is never accessed, left in for consistency with RFC indices r = [b""] + [wrapped_key[i * 8:i * 8 + 8] for i in range(1, n + 1)] a = unpack_from(">Q", wrapped_key[:8])[0] aes = AES.new(kek, AES.MODE_ECB) for j in range(5, -1, -1): # counting down for i in range(n, 0, -1): # (n, n-1, ..., 1) b = aes.decrypt(pack(">Q", a ^ (n * j + i)) + r[i]) a = unpack_from(">Q", b[:8])[0] r[i] = b[8:] if a != iv: raise SPSDKError( f"Integrity Check Failed: {a:016X} (expected {iv:016X})") return b"".join(r[1:])
def load_binary_image(*path_segments: str) -> BinFile: r"""Load binary data file. :param \*path_segments: list that consists of: - absolute path - optional sub-directory (any number) - file name including file extension All the fields together represents absolute path to the file :raises SPSDKError: The binary file cannot be loaded. :return: Binary data represented in BinFile class. """ path = os.path.join(*path_segments) path = path.replace("\\", "/") try: with open(path, "rb") as f: data = f.read(4) except Exception as e: raise SPSDKError(f"Error loading file: {str(e)}") from e if data == b"\x7fELF": raise SPSDKError("Elf file is not supported") binfile = BinFile() try: binfile.add_file(path) except UnicodeDecodeError as e: binfile.add_binary_file(path) except Exception as e: raise SPSDKError(f"Error loading file: {str(e)}") from e return binfile
def aes_key_wrap(self, kek: bytes, key_to_wrap: bytes) -> bytes: """Wraps a key using a key-encrypting key (KEK). :param kek: The key-encrypting key :param key_to_wrap: Plain data :return: Wrapped key :raises SPSDKError: Invalid length of kek or key_to_wrap """ if len(kek) not in (16, 24, 32): raise SPSDKError("The wrapping key must be a valid AES key length") if len(key_to_wrap) < 16: raise SPSDKError("The key to wrap must be at least 16 bytes") if len(key_to_wrap) % 8 != 0: raise SPSDKError("The key to wrap must be a multiple of 8 bytes") iv = 0xA6A6A6A6A6A6A6A6 n = len(key_to_wrap) // 8 r = [b""] + [key_to_wrap[i * 8:i * 8 + 8] for i in range(0, n)] a = iv aes = AES.new(kek, AES.MODE_ECB) for j in range(6): for i in range(1, n + 1): b = aes.encrypt(pack(">Q", a) + r[i]) a = unpack_from(">Q", b[:8])[0] ^ (n * j + i) r[i] = b[8:] return pack(">Q", a) + b"".join(r[1:])
def encrypt_block(self, key: bytes, start_addr: int, data: bytes) -> bytes: """Encrypt block located in any FAC region. :param key: user for encryption :param start_addr: start address of the data :param data: binary block to be encrypted; the block size must be BEE_ENCR_BLOCK_SIZE :return: encrypted block if it is inside any FAC region; untouched block if it is not in any FAC region :raises SPSDKError: When incorrect length of binary block :raises SPSDKError: When encryption mode different from AES/CTR provided :raises SPSDKError: When invalid length of key :raises SPSDKError: When invalid range of region """ if len(data) != BEE_ENCR_BLOCK_SIZE: raise SPSDKError( "Incorrect length of binary block to be encrypted") if self._start_addr <= start_addr < self._end_addr: if self.mode != BeeProtectRegionBlockAesMode.CTR: raise SPSDKError("only AES/CTR encryption mode supported now") if len(key) != 16: raise SPSDKError("Invalid length of key") for fac in self.fac_regions: if fac.start_addr <= start_addr < fac.end_addr: if start_addr + len(data) > fac.end_addr: raise SPSDKError("Invalid range of region") cntr_key = Counter( self.counter, ctr_value=start_addr >> 4, ctr_byteorder_encoding="big", ) return crypto_backend().aes_ctr_encrypt( key, data, cntr_key.value) return data
def run(self, params: List[int] = None) -> List[Any]: """Run DebugMailboxCommand.""" paramslen = len(params) if params else 0 if paramslen != self.paramlen: raise SPSDKError( "Provided parameters length is not equal to command parameters length!" ) req = self.id | (self.paramlen << 16) logger.debug(f"<- spin_write: {format_value(req, 32)}") self.dm.spin_write(self.dm.registers["REQUEST"]["address"], req) # Wait 30ms to allow reset of internal logic of debug mailbox time.sleep(0.03) if params: for i in range(paramslen): ret = self.dm.spin_read(self.dm.registers["RETURN"]["address"]) logger.debug(f"-> spin_read: {format_value(ret, 32)}") if (ret & 0xFFFF) != 0xA5A5: raise SPSDKError("Device did not send correct ACK answer!") if ((ret >> 16) & 0xFFFF) != (self.paramlen - i): raise SPSDKError( "Device expects parameters of different length we can provide!" ) logger.debug(f"<- spin_write: {format_value(params[i], 32)}") self.dm.spin_write(self.dm.registers["REQUEST"]["address"], params[i]) ret = self.dm.spin_read(self.dm.registers["RETURN"]["address"]) logger.debug(f"-> spin_read: {format_value(ret, 32)}") resplen = (ret >> 16) & 0x7FFF status = ret & 0xFFFF if resplen != self.resplen: # MSB is used to show it is the new protocol -> 0x7FFF raise SPSDKError( "Device wants to send us different size than expected!") if status != 0: raise SPSDKError(f"Status code is not success: {ret & 0xFFFF} !") # do not send ack, in case no data follows if resplen == 0: return [] # ack the response ack = 0xA5A5 | (self.resplen << 16) logger.debug(f"<- spin_write: {format_value(ack, 32)}") self.dm.spin_write(self.dm.registers["REQUEST"]["address"], ack) response = [] for i in range(self.resplen): ret = self.dm.spin_read(self.dm.registers["RETURN"]["address"]) logger.debug(f"-> spin_read: {format_value(ret, 32)}") response.append(ret) ack = 0xA5A5 | ((self.resplen - i - 1) << 16) logger.debug(f"<- spin_write: {format_value(ack, 32)}") self.dm.spin_write(self.dm.registers["REQUEST"]["address"], ack) return response
def validate(self) -> None: """Validates the configuration of the instance.""" if (self.start_addr & _ENCR_BLOCK_ADDR_MASK != 0) and (self.length & _ENCR_BLOCK_ADDR_MASK != 0): raise SPSDKError("Invalid configuration of the instance") if self.protected_level < 0 or self.protected_level > 3: raise SPSDKError("Invalid protected level") if self.start_addr < 0 or self.end_addr > 0xFFFFFFFF or self.start_addr >= self.end_addr: raise SPSDKError("Invalid start/end address")
def export(self, kek: Union[bytes, str], iv: bytes = bytes([0xA6] * 8)) -> bytes: """Creates key wrap for the key blob. :param kek: key to encode; 16 bytes long :param iv: counter initialization vector; 8 bytes; optional, OTFAD uses empty init value :return: Serialized key blob :raises SPSDKError: If any parameter is not valid :raises SPSDKError: If length of kek is not valid :raises SPSDKError: If length of data is not valid """ if isinstance(kek, str): kek = bytes.fromhex(kek) if len(kek) != 16: raise SPSDKError("Invalid length of kek") if len(iv) != self._EXPORT_CTR_IV_SIZE: raise SPSDKError("Invalid length of initialization vector") n = self._EXPORT_NBLOCKS_5 plaintext = self.plain_data() # input data to be encrypted if len(plaintext) < n * 8: raise SPSDKError("Invalid length of data to be encrypted") # step 1: initialize the byte - sized data variables # set a = iv # for i = 1 to n # r[i] = plain_data[i] a = iv # 64-bit integrity check register r = bytearray(8) + bytearray( plaintext[0:8 * n]) # 8-bit array of 64-bit registers # step 2: calculate intermediate values # for j = 0 to 5 # for i = 1 to n # encr = AES(K, A | R[i]) # a = MSB(64, encr) ^ (n*j)+i # r[i] = LSB(64, B) for j in range(0, 6): for i in range(1, n + 1): in_data = a + r[8 * i:8 * i + 8] # 128-bit temporary plaintext input vector aes = AES.new(kek, AES.MODE_ECB) encr = aes.encrypt(in_data) xor = encr[7] ^ ((n * j) + i) a = bytes(encr[:7]) + bytes([xor]) r[8 * i:8 * i + 8] = encr[8:] # step 3: output the results # set result[0] = A # for i = 1 to n # result[i] = r[i] result = a + r[8:8 * n + 8] return align_block(result, self._EXPORT_KEY_BLOB_SIZE, padding=0) # align to 64 bytes (0 padding)
def get_yaml_config(self, data: CM, indent: int = 0) -> CM: """Return YAML configuration In PfrConfiguration format. :param data: The registers settings data. :param indent: YAML start indent. :return: YAML PFR configuration in commented map(ordered dict). :raises SPSDKError: When there is no device found :raises SPSDKError: When there is no type found """ if not self.device: raise SPSDKError("Device not found") if not self.type: raise SPSDKError("Type not found") res_data = CM() res_data.yaml_set_start_comment( f"NXP {self.device} PFR {self.type} configuration", indent=indent) description = CM() description.insert(1, "device", self.device, comment="The NXP device name.") description.insert(2, "revision", self.revision, comment="The NXP device revision.") description.insert(3, "type", self.type.upper(), comment="The PFR type (CMPA, CFPA).") description.insert(4, "version", spsdk_version, comment="The SPSDK tool version.") description.insert(5, "author", spsdk_author, comment="The author of the configuration.") description.insert(6, "release", spsdk_release, comment="The SPSDK release.") res_data.insert( 1, "description", description, comment=f"The PFR {self.type} configuration description.", ) res_data.insert( 2, "settings", data, comment=f"The PFR {self.type} registers configuration.") return res_data
def validate(self) -> None: """Validates settings of the instance. :raises SPSDKError: If invalid length of kib key :raises SPSDKError: If invalid length of kib iv """ if len(self.kib_key) != self._KEY_LEN: raise SPSDKError("Invalid length of kib key") if len(self.kib_iv) != self._KEY_LEN: raise SPSDKError("Invalid length of kib iv")
def sign(self) -> None: """Sign the DC data using SignatureProvider.""" if not self.signature_provider: raise SPSDKError("Debug Credential Signature provider is not set") signature = self.signature_provider.sign(self._get_data_to_sign()) if not signature: raise SPSDKError( "Debug Credential Signature provider didn't return any signature" ) self.signature = signature
def mix_validate(self) -> None: """Validate the setting of image. :raises SPSDKError: The configuration of Certificate v3.1 is invalid. """ if not self.cert_block: raise SPSDKError("Certification block is missing") if not self.signature_provider: raise SPSDKError("Signature provider is missing")
def set_config(self, config: PfrConfiguration, raw: bool = False) -> None: """Apply configuration from file. :param config: PFR configuration. :param raw: When set all (included computed fields) configuration will be applied. :raises SPSDKError: When device is not provided. :raises SPSDKError: When revision is not provided. :raises SPSDKPfrConfigError: Invalid config file. """ if not self.device: raise SPSDKError("No device provided") if not self.revision: raise SPSDKError("No revision provided") if config.device != self.device: raise SPSDKPfrConfigError( f"Invalid device in configuration. {self.device} != {config.device}" ) if not config.revision or config.revision in ("latest", ""): config.revision = self.config.get_latest_revision(self.device) logger.warning( f"The configuration file doesn't contains silicon revision, \ the latest: '{config.revision}' has been used.") if config.revision != self.revision: raise SPSDKPfrConfigError( f"Invalid revision in configuration. {self.revision} != {config.revision}" ) if config.type and config.type.upper() != self.__class__.__name__: raise SPSDKPfrConfigError( f"Invalid configuration type. {self.__class__.__name__} != {config.type}" ) if not config.settings: raise SPSDKPfrConfigError("Missing configuration of PFR fields!") computed_regs = [] computed_regs.extend(self.config.get_ignored_registers(self.device)) if not raw: computed_regs.extend( self.config.get_computed_registers(self.device)) computed_fields = None if raw else self.config.get_computed_fields( self.device) self.registers.load_yml_config(config.settings, computed_regs, computed_fields) if not raw: # # Just update only configured registers exclude_hooks = [] if not self.config.get_value("mandatory_computed_regs", self.device): exclude_hooks.extend( list( set(self.registers.get_reg_names()) - set(config.settings.keys()))) self.registers.run_hooks(exclude_hooks)
def mix_validate(self) -> None: """Validate the setting of image. raise SPSDKError: Invalid HW key enabled member type. """ if not self.ctr_init_vector: raise SPSDKError( "Initial vector for encryption counter MUST exists.") if len(self.ctr_init_vector) != self._CTR_INIT_VECTOR_SIZE: raise SPSDKError( "Invalid size of Initial vector for encryption counter.")
def __init__(self, device: str = None, revision: str = None, user_config: PfrConfiguration = None) -> None: """Initialize an instance. :param device: device to use, list of supported devices is available via 'devices' method :param revision: silicon revision, if not specified, the latest is being used :param user_config: PfrConfiguration with user configuration to use with initialization :raises SPSDKError: When no device is provided :raises SPSDKError: When no device is not supported :raises SPSDKError: When there is invalid revision """ if not (device or user_config): raise SPSDKError("No device provided") self.config = self._load_config() # either 'device' or 'user_config' IS defined! Mypy doesn't understand the check above self.device = device or user_config.device # type: ignore if self.device not in self.config.get_devices(): raise SPSDKError(f"Device '{self.device}' is not supported") self.revision = revision or (user_config.revision if user_config else "latest") if not self.revision or self.revision == "latest": self.revision = self.config.get_latest_revision(self.device) logger.warning( f"The silicon revision is not specified, the latest: '{self.revision}' has been used." ) if self.revision not in self.config.get_revisions(self.device): raise SPSDKError( f"Invalid revision '{self.revision}' for '{self.device}'") self.registers = Registers(self.device) self.registers.load_registers_from_xml( xml=self.config.get_data_file(self.device, self.revision), filter_reg=self.config.get_ignored_registers(self.device), grouped_regs=self.config.get_grouped_registers(self.device), ) # Set the computed field handler for reg, fields in self.config.get_computed_fields( self.device).items(): reg_obj = self.registers.find_reg(reg) reg_obj.add_setvalue_hook(self.reg_computed_fields_handler, fields) self.user_config = PfrConfiguration( config=user_config, device=self.device, revision=self.revision, cfg_type=self.__class__.__name__, ) if self.user_config.settings: self.set_config(self.user_config, raw=False)
def encrypt_image(self, base_address: int, data: bytes, byte_swap: bool) -> bytes: """Encrypt specified data. :param base_address: of the data in target memory; must be >= self.start_addr :param data: to be encrypted (e.g. plain image); base_address + len(data) must be <= self.end_addr :param byte_swap: this probably depends on the flash device, how bytes are organized there True should be used for FLASH on EVK RT6xx; False for FLASH on EVK RT5xx :return: encrypted data :raises SPSDKError: If start_addr or end_addr does not match with base_address (+ data length) :raises SPSDKError: If start address is not valid """ if base_address % 16 != 0: raise SPSDKError("Invalid start address" ) # Start address has to be 16 byte aligned data = align_block(data, self._IMAGE_ALIGNMENT) # align data length data_len = len(data) # check start and end addresses if not self.matches_range(base_address, base_address + data_len - 1): raise SPSDKError( f"Image address range is not within key blob: {hex(self.start_addr)}-{hex(self.end_addr)}" ) result = bytes() counter = Counter(self._get_ctr_nonce(), ctr_value=base_address, ctr_byteorder_encoding="big") for index in range(0, data_len, 16): # prepare data in byte order if byte_swap: # swap 8 bytes + swap 8 bytes data_2_encr = ( data[-data_len + index + 7:-data_len + index - 1:-1] + data[-data_len + index + 15:-data_len + index + 7:-1]) else: data_2_encr = data[index:index + 16] # encrypt encr_data = crypto_backend().aes_ctr_encrypt( self.key, data_2_encr, counter.value) # fix byte order in result if byte_swap: result += encr_data[-9:-17:-1] + encr_data[ -1:-9:-1] # swap 8 bytes + swap 8 bytes else: result += encr_data # update counter for encryption counter.increment(16) if len(result) != data_len: raise SPSDKError("Invalid length of encrypted data") return bytes(result)