Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
 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
Ejemplo n.º 4
0
    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.")
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
 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()
Ejemplo n.º 8
0
 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
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
    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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
    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")
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
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)
Ejemplo n.º 15
0
    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
Ejemplo n.º 16
0
    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:])
Ejemplo n.º 17
0
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
Ejemplo n.º 18
0
    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:])
Ejemplo n.º 19
0
    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
Ejemplo n.º 20
0
    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
Ejemplo n.º 21
0
 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")
Ejemplo n.º 22
0
    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)
Ejemplo n.º 23
0
    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
Ejemplo n.º 24
0
    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")
Ejemplo n.º 25
0
 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
Ejemplo n.º 26
0
    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")
Ejemplo n.º 27
0
    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)
Ejemplo n.º 28
0
    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.")
Ejemplo n.º 29
0
    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)
Ejemplo n.º 30
0
    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)