Untitled

 avatar
user_5822903
plain_text
2 months ago
16 kB
5
Indexable
"""

/*********************************************************************************/



Copyright © 2025 Volvo Car Corporation. All rights reserved.



NOTICE:
This file contains material that is confidential and confidential to Volvo Cars and/or
other developers. No license is granted under any intellectual or industrial property
rights of Volvo Cars except as may be provided in an agreement with Volvo Cars.
Any unauthorized copying or distribution of content from this file is prohibited.



/*********************************************************************************/

reqprod: 398360
version: 4
title: Format of the Verification Block Table
purpose: >
    Define the format of the verification block table

description: >
    The verification block table, encoded with big endian, shall have the following format:
    Verification Block Table Format Identifier. The verification block will start with most
    significant byte of this format identifier. It defines the version of the verification
    block table, mainly intended for tool parsing and format checks in the ECU, i.e. it shall not
    be used to configure e.g. the actual algorithm to be used by the bootloader. Size 16 bits.
    Number of data blocks. This shall be identical to the number of data blocks included in the
    software part. The data block containing the verification block table itself shall not be
    included. Size 16 bits.
    StartAddress [size 32 bits], Length [size 32 bits], Hash value. Every data blocks shall be
    defined, except the block table itself. The size of the hash is dependent of the hash function
    used and is specified elsewhere. The Length is the unprocessed length (see other requirements
    for more details).
    The data blocks shall be sorted according to the order the data blocks are being programmed to
    the ECU.
    Example.
    SHA-256 hash function is used, i.e. each hash value is 256 bits.
    Number of data blocks are two, i.e. the total length of the verification block table, in octets,
    will be (16+16+2x(256+32+32))/8=84 octets.
    The data shown belongs to the verification block. The Verification block starts at address
    0x0007FF00, i.e. it is the third data block in the figure:
    0000
    0002
    00060000 00000013 D78ECD62C62CF1F575D7DEC6D2FD57DA066554FD42EEE5C867E70083BC942262
    00060100 0001EF00 E6027B3F959FAF231B382FFDC64EB23825E6B7CCEA47F5196D02CA2AFDF44188
    The format identifier is 0x0000
    Number of data blocks are two (0x0002)
    Data block 1: Start address 0x00060000 and length 0x00000013. Hash 0xD78ECD...
    Data block 2: Start address 0x00060100 and length 0x0001EF00. Hash 0xE6027B...

details: >
    Verify first 6 bytes of VBT block(manipulated format identifier, data blocks, start address)
    and also verify software download (SWDL) of ESS VBF with manipulated format identifier
    present in verification block table fails.
"""

import logging
from glob import glob
from hilding.dut import Dut
from hilding.dut import DutTestError
from supportfunctions.support_lzma import LzmaEncoder
from supportfunctions.support_lzss import LzssEncoder
from supportfunctions.support_sec_acc import SupportSecurityAccess

LZMA = LzmaEncoder()
LZSS = LzssEncoder()
SSA = SupportSecurityAccess()


def verify_correct_vbt_start_address(dut, vbf_header, vbf_data, vbf_block_details, vbf_offset):
    """
    Validate the start address and data format identifier of VBT with VBF header values
    Args:
        dut (Dut): An instance of Dut
        vbf_header (dict): VBF file header
        vbf_data (bytes): VBF data 
        vbf_block_details (list): VBF block data, VBF block details
        vbf_offset (int): VBF offset
    Returns:
        bool: Returns true when respective VBT data block condition is matched
    """
    # Get start address from vbf_header
    for key in vbf_header.keys():
        if key == 'erase':
            vbfhdblock= vbf_header['erase']
            vbfhdblock_list= list(vbfhdblock)
    vbf_offset = dut.SSBL.block_data_extract(vbf_data, vbf_offset)

    # VBT data block
    dut.SSBL.vbf_header_convert(vbf_header)
    vbt = vbf_block_details[-1]['vbf_block_data']

    # Initialize decompressed data
    decompr_data = b""
    vbfhdblock_list = []
    # Retrieve decompressed data
    if vbf_header['data_format_identifier'] == 0: # format '0x00':
        decompr_data = vbt
    elif vbf_header['data_format_identifier'] == 16: # format '0x10':
        decompr_data = LZSS.decode_barray(vbt)
    elif vbf_header['data_format_identifier'] == 32: # format '0x20':
        decompr_data = LZMA.decode_barray(vbt)
    else:
        logging.warning("Unknown compression format for data format identifier: "
                        "%0.02X", vbf_header['data_format_identifier'])

    # Convert decompressed VBT data to list of hex
    vbt_hex = decompr_data.hex()
    vbt_list = list(vbt_hex)

    # Validate verification block table(start address) and VBF header values
    if vbfhdblock_list[15:19] == vbt_list[4:8]:
        logging.info("Successfully verified VBT start address and format identifier as expected")
        return True

    logging.error("Test Failed: Received incorrect VBT start address %s ", vbt_list[4:8])
    return False


def manipulated_vbt(vbf_block_details, vbf_header):
    """
    Manipulating the first 6 bytes of verification block table
    Args:
        vbf_block_details (list): VBF block data, VBF block details
        vbf_header (dict): VBF file header
    Returns:
        vbf_block_details(list): Manipulated verification block table i.e last VBF block data
    """
    # Getting VBT
    vbt = vbf_block_details[-1]['vbf_block_data']
    decompr_data = b""
    # Retrieve decompressed data
    if vbf_header['data_format_identifier'] == 0: # format '0x00':
        decompr_data = vbt
    elif vbf_header['data_format_identifier'] == 16: # format '0x10':
        logging.info("Compression method 1: LZSS")
        decompr_data = LZSS.decode_barray(vbt)
    elif vbf_header['data_format_identifier'] == 32: # format '0x20':
        logging.info("Compression method 2: LZMA")
        decompr_data = LZMA.decode_barray(vbt)
    else:
        logging.warning("Unknown compression format for data format identifier: "
                        "%0.02X", vbf_header['data_format_identifier'])

    if decompr_data:
        vbt_hex = str(SSA.sa_key_calculated_distort(decompr_data))
        vbf_block_details[-1]['vbf_block_data'] = bytearray(vbt_hex, 'utf-8')

    return vbf_block_details


def block_wise_software_download_ess(dut, vbf_header, vbf_blocks_details, vbf_offset):
    """
    Blockwise software download(SWDL) for ESS VBF file
    Args:
        dut (Dut): An instance of Dut
        vbf_header (dict): VBF file header
        vbf_blocks_details (list): VBF block and VBF block data
        vbf_offset
    Returns:
        (bool): Returns true when software download of ESS VBF file is successful
    """
    # Software download(SWDL) for blocks of ESS VBF file

    for block_id, block_details in enumerate(vbf_blocks_details):
        logging.info("SWDL on %s of ESS VBF", block_id+1)
        result = dut.SSBL.transfer_data_block(dut, vbf_header, block_details["vbf_block_data"],
                                                vbf_offset)
        if not result:
            logging.error("Test Failed: Block %s failed", block_id+1)
            return False

    logging.info("Software download for ESS VBF completed")
    return True


def extract_vbf_details_vbt(dut, vbf_data, vbf_offset):
    """
    Extracting VBF parameters from ESS VBFs file
    Args:
        dut (Dut): An instance of Dut
        vbf_data (bytes): VBF data
        vbf_offset (int): VBF offset
    Returns:
        vbf_blocks_details (list): VBF block and VBF block data
    """
    vbf_blocks_details = []

    # Extract first VBF block
    vbf_offset, vbf_block, vbf_block_data = dut.SSBL.block_data_extract(vbf_data, vbf_offset)
    vbf_blocks_details.append({'vbf_block': vbf_block, 'vbf_block_data': vbf_block_data})

    # Extract remaining VBF blocks
    while vbf_block['StartAddress'] != 0:
        vbf_offset, vbf_block, vbf_block_data = dut.SSBL.block_data_extract(vbf_data, vbf_offset)

        # Break if no more blocks found
        if vbf_block['StartAddress'] != 0:

            # Preparing VBF blocks details list
            vbf_blocks_details.append({'vbf_block': vbf_block, 'vbf_block_data': vbf_block_data})

    return vbf_blocks_details


def extract_vbt(dut, vbf_data, vbf_offset):
    """
    Extracting VBF data block from VBF parameters
    Args:
        dut (Dut): An instance of Dut
        vbf_data (str): VBF data block
        vbf_offset (int): VBF offset
    Returns:
        data_block (str): VBT data in hex
    """
    data_block = ''

    # To extract VBT data, we need to extract last block of VBF file
    # So we are iterating through the loop and extracting every block
    # And in the last iteration of the loop, we receive VBT data block.
    while True:
        vbf_offset, vbf_block, vbf_block_data = dut.SSBL.block_data_extract(vbf_data, vbf_offset)
        if vbf_block['StartAddress'] == 0:
            logging.debug("No more blocks")
            break
        data_block = LZMA.decode_barray(vbf_block_data)

    return data_block.hex()


def software_download_manipulated_ess_vbt(dut):
    """
    Software download(SWDL) for SBL and manipulated ESS VBFs file type
    Args:
        dut (Dut): An instance of Dut
    Returns:
        (bool): Returns true when software download of ESS VBF file with manipulated VBT is failed
    """
    # Activate SBL
    sbl_result = dut.SSBL.sbl_activation(dut, sa_keys=dut.conf.default_rig_config)
    logging.info("Software download (downloading and activating sbl) completed."
                 " Result: %s", sbl_result)

    if sbl_result is False:
        logging.error("Test Failed: Aborting software download due to problems when "
                      "activating SBL")
        return False

    # Download ESS
    _, vbf_header, vbf_data, vbf_offset = dut.SSBL.read_vbf_file(dut.SSBL.get_ess_filename())

    # Convert VBF header so values can be used directly
    dut.SSBL.vbf_header_convert(vbf_header)

    # Erase memory
    result = dut.SSBL.flash_erase(dut, vbf_header, stepno=2)
    # Manipulating the VBT
    vbf_blocks_details = manipulated_vbt(extract_vbf_details_vbt(dut, vbf_data, vbf_offset),
                                                vbf_header)
    swdl_result = result and block_wise_software_download_ess(dut, vbf_header, vbf_blocks_details,
                                                                vbf_offset)
    if not swdl_result:
        logging.info("SWDL ESS VBF file fails with manipulated VBT as expected")
        return True

    logging.error("Test Failed: Unexpected result for ESS VBF file")
    return False


def extract_vbf_params(dut, vbf_path):
    """
    Extracting VBF parameters from VBF file
    Args:
        dut (Dut): An instance of Dut
        vbf_path (dict): VBF file path
    Returns:
        vbf_params (dict): VBF data, VBF root hash and file name
    """
    vbf_params = {'vbf_data': '',
                  'vbf_root_hash': '',
                  'file_name': ''}

    _, vbf_header, vbf_data, vbf_offset = dut.SSBL.read_vbf_file(vbf_path)
    vbt = extract_vbt(dut, vbf_data, vbf_offset)
    vbf_params["vbf_root_hash"] = vbf_header['verification_block_root_hash']

    # Used [2:] to remove '0x' from the hex string
    vbf_params["vbf_data"] = (vbf_header["verification_block_start"][2:] +
                              vbf_header["verification_block_length"][2:] + vbt)
    vbf_params["file_name"] = vbf_path.split('/')[-1]

    return vbf_params


def step_1(dut: Dut):
    """
    action: Validate the format identifier and start address of VBT blocks with VBF header
    expected_result: Format identifier and start address of VBT blocks should match with
                     VBF header                   
    """
    # pylint: disable=unused-argument
    result = dut.SSBL.get_vbf_files()
    _, vbf_header, vbf_data, vbf_offset = dut.SSBL.read_vbf_file(dut.SSBL.get_ess_filename())

    # Extract VBF blocks
    vbf_blocks_details = extract_vbf_details_vbt(dut, vbf_data, vbf_offset)
    # Verifying the VBT format identifier
    vbt_result = verify_correct_vbt_start_address(dut, vbf_header, vbf_data,
                                               vbf_blocks_details, vbf_offset)
    if result and vbt_result:
        logging.info("Successfully validated format identifier and start address of VBT blocks"
                     " with VBF header as expected")
        return True

    logging.error("Test Failed: VBT verification failed for format identifier and start address")
    return False


def step_2(dut: Dut):
    """
    action: Calculate root hash from VBF data and compare with the value present in VBF header
    expected_result: Calculated root hash value and root hash value present in VBF header should
                     be equal
    """
    vbf_params_list =[]
    rig_vbf_path = dut.conf.rig.vbf_path
    vbf_paths = glob(str(rig_vbf_path) + "/*.vbf")

    if len(vbf_paths) == 0:
        logging.error("Test Failed: No VBF file found in path: %s", rig_vbf_path)
        return False

    for path in vbf_paths:
        vbf_params_list.append(extract_vbf_params(dut, path))
    result = dut.SSBL.compare_root_hash(vbf_params_list)
    if result:
        logging.info("Calculated root hash value and the root hash value present in "
                     "VBF are equal as expected")
        return True

    logging.error("Test Failed: Calculated root hash value and the root hash value present in VBF "
                  "header are not equal")
    return False


def step_3(dut: Dut):
    """
    action: Verify software download(SWDL) request for VBF file of ESS software part type
            with manipulated format identifier, data blocks, start address
            of verification block table.
    expected_result: Software download(SWDL) should fail for manipulated VBT of ESS VBF file
    """
    # Software download with SBL and manipulated ESS VBT
    result = software_download_manipulated_ess_vbt(dut)
    if result:
        logging.info("ESS software download fails with manipulated format identifier, data blocks,"
                     " start address of VBT as expected ")
        return True

    logging.error("Test Failed: ESS software download with manipulated VBT is successful")
    return False


def run():
    """
    Verify first 6 bytes of VBT block(manipulated format identifier, data blocks, start address)
    and also verify software download (SWDL) of ESS VBF with manipulated format identifier
    present in verification block table fails.
    """
    dut = Dut()

    start_time = dut.start()
    result = False
    result_step = False

    try:
        dut.precondition(timeout=300)

        result_step = dut.step(step_1, purpose="Validate the format identifier and start address "
                                               "of VBT blocks with VBF header")
        if result_step:
            result_step = dut.step(step_2, purpose="Calculate root hash from VBF data and "
                                                   "compare with the value present in VBF header")
        if result_step:
            result_step = dut.step(step_3, purpose="Verify Software download(SWDL) request for "
                                                   "VBF file of ESS software part type with "
                                                   "manipulated format identifier, data blocks, "
                                                   "start address of verification block table")
        result = result_step

    except DutTestError as error:
        logging.error("Test Failed: %s", error)
    finally:
        dut.postcondition(start_time, result)


if __name__ == '__main__':
    run()
Editor is loading...
Leave a Comment