# HOST_TEST for espefuse.py using the pytest framework
#
# Supports esp32, esp32s2, esp32s3beta2, esp32s3,
#          esp32c3, esp32h2beta1, esp32c2, esp32c6, esp32p4,
#          esp32c61, esp32c5, esp32c5beta3.
#
# How to use:
#
# Run as HOST_TEST (without a physical connection to a chip):
#  - `pytest test_espefuse.py --chip esp32`
#  - `pytest test_espefuse.py --chip esp32s2`
#
# OR
#
# Run as TEST on FPGA (connection to FPGA with a flashed image):
# required two COM ports
#  - `pytest test_espefuse.py \
#     --chip esp32 --port /dev/ttyUSB0 --reset-port /dev/ttyUSB1`
#
# where  - --port       - a port for espefuse.py operation
#        - --reset-port - a port to clear efuses (connect RTS or DTR ->- J14 pin 39)
#
# Note: For FPGA with ESP32 image, you need to set an env variable ESPTOOL_ENV_FPGA to 1
#       to slow down the connection sequence
#       because of a long delay (~6 seconds) after resetting the FPGA.
#       This is not necessary when using other images than ESP32

import os
import subprocess
import sys
import tempfile
import time

from bitstring import BitStream

# Make command line options --port, --reset-port and --chip available
from conftest import arg_chip, arg_port, arg_reset_port, need_to_install_package_err

TEST_DIR = os.path.abspath(os.path.dirname(__file__))
IMAGES_DIR = os.path.join(TEST_DIR, "images", "efuse")
S_IMAGES_DIR = os.path.join(TEST_DIR, "secure_images")
EFUSE_S_DIR = os.path.join(TEST_DIR, "efuse_scripts")

import pytest

try:
    from espefuse import SUPPORTED_CHIPS
except ImportError:
    need_to_install_package_err()

SUPPORTED_CHIPS = list(SUPPORTED_CHIPS.keys())

import serial

# Set reset_port if --reset-port cmdline option is specified
# This activates testing with real hardware (FPGA)
reset_port = (
    serial.Serial(arg_reset_port, 115200) if arg_reset_port is not None else None
)

if arg_chip not in SUPPORTED_CHIPS:
    pytest.exit(f"{arg_chip} is not a supported target, choose from {SUPPORTED_CHIPS}")
print(f"\nHost tests of espefuse.py for {arg_chip}:")
print("Running espefuse.py tests...")


@pytest.mark.host_test
class EfuseTestCase:
    def setup_method(self):
        if reset_port is None:
            self.efuse_file = tempfile.NamedTemporaryFile(delete=False)
            self.base_cmd = (
                f"{sys.executable} -m espefuse --chip {arg_chip} "
                f"--virt --path-efuse-file {self.efuse_file.name} -d"
            )
            self._set_target_wafer_version()
        else:
            self.base_cmd = (
                f"{sys.executable} -m espefuse --chip {arg_chip} "
                f"--port {arg_port} -d"
            )
            self.reset_efuses()

    def teardown_method(self):
        if reset_port is None:
            self.efuse_file.close()
            os.unlink(self.efuse_file.name)

    def reset_efuses(self):
        # reset and zero efuses
        reset_port.dtr = False
        reset_port.rts = False
        time.sleep(0.05)
        reset_port.dtr = True
        reset_port.rts = True
        time.sleep(0.05)
        reset_port.dtr = False
        reset_port.rts = False

    def get_esptool(self):
        if reset_port is not None:
            import esptool

            esp = esptool.cmds.detect_chip(port=arg_port)
            del esptool
        else:
            import espefuse

            efuse = espefuse.SUPPORTED_CHIPS[arg_chip].efuse_lib
            esp = efuse.EmulateEfuseController(self.efuse_file.name)
            del espefuse
            del efuse
        return esp

    def _set_34_coding_scheme(self):
        self.espefuse_py("burn_efuse CODING_SCHEME 1")

    def _set_none_recovery_coding_scheme(self):
        self.espefuse_py("burn_efuse CODING_SCHEME 3")

    def _set_target_wafer_version(self):
        # ESP32 has to be ECO3 (v3.0) for tests
        if arg_chip == "esp32":
            self.espefuse_py("burn_efuse CHIP_VER_REV1 1 CHIP_VER_REV2 1")

    def check_data_block_in_log(
        self, log, file_path, repeat=1, reverse_order=False, offset=0
    ):
        with open(file_path, "rb") as f:
            data = BitStream("0x00") * offset + BitStream(f)
            blk = data.readlist(f"{data.len // 8}*uint:8")
            blk = blk[::-1] if reverse_order else blk
            hex_blk = " ".join(f"{num:02x}" for num in blk)
            assert repeat == log.count(hex_blk)

    def espefuse_not_virt_py(self, cmd, check_msg=None, ret_code=0):
        full_cmd = " ".join((f"{sys.executable} -m espefuse", cmd))
        return self._run_command(full_cmd, check_msg, ret_code)

    def espefuse_py(self, cmd, do_not_confirm=True, check_msg=None, ret_code=0):
        full_cmd = " ".join(
            [self.base_cmd, "--do-not-confirm" if do_not_confirm else "", cmd]
        )
        output = self._run_command(full_cmd, check_msg, ret_code)
        self._run_command(
            " ".join([self.base_cmd, "check_error"]), "No errors detected", 0
        )
        print(output)
        return output

    def _run_command(self, cmd, check_msg, ret_code):
        try:
            p = subprocess.Popen(
                cmd.split(),
                shell=False,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                universal_newlines=True,
            )
            output, _ = p.communicate()
            returncode = p.returncode
            if check_msg:
                assert check_msg in output
            if returncode:
                print(output)
                print(cmd)
            assert ret_code == returncode
            return output
        except subprocess.CalledProcessError as error:
            print(error)
            raise


class TestReadCommands(EfuseTestCase):
    def test_help(self):
        self.espefuse_not_virt_py("--help", check_msg="usage: __main__.py [-h]")
        self.espefuse_not_virt_py(f"--chip {arg_chip} --help")

    def test_help2(self):
        self.espefuse_not_virt_py("", check_msg="usage: __main__.py [-h]", ret_code=1)

    def test_dump(self):
        self.espefuse_py("dump -h")
        self.espefuse_py("dump")

    def test_dump_format_joint(self):
        tmp_file = tempfile.NamedTemporaryFile(delete=False)
        self.espefuse_py(f"dump --format joint --file_name {tmp_file.name}")

    def test_dump_split_default(self):
        tmp_file = tempfile.NamedTemporaryFile(delete=False)
        self.espefuse_py(f"dump --file_name {tmp_file.name}")

    def test_dump_split(self):
        tmp_file = tempfile.NamedTemporaryFile(delete=False)
        self.espefuse_py(f"dump --format split --file_name {tmp_file.name}")

    def test_summary(self):
        self.espefuse_py("summary -h")
        self.espefuse_py("summary")

    def test_summary_json(self):
        self.espefuse_py("summary --format json")

    def test_summary_filter(self):
        self.espefuse_py("summary MAC")
        self.espefuse_py("summary --format value_only MAC")
        self.espefuse_py(
            "summary --format value_only MAC WR_DIS",
            check_msg="The 'value_only' format can be used exactly for one efuse.",
            ret_code=2,
        )

    @pytest.mark.skipif(
        arg_chip == "esp32p4", reason="No Custom MAC Address defined yet"
    )
    def test_get_custom_mac(self):
        self.espefuse_py("get_custom_mac -h")
        if arg_chip == "esp32":
            right_msg = "Custom MAC Address is not set in the device."
        else:
            right_msg = "Custom MAC Address: 00:00:00:00:00:00 (OK)"
        self.espefuse_py("get_custom_mac", check_msg=right_msg)

    def test_adc_info(self):
        self.espefuse_py("adc_info -h")
        self.espefuse_py("adc_info")

    def test_adc_info_2(self):
        if arg_chip == "esp32":
            self.espefuse_py("burn_efuse BLK3_PART_RESERVE 1")
        elif arg_chip in ["esp32c3", "esp32s3", "esp32s3beta2"]:
            self.espefuse_py("burn_efuse BLK_VERSION_MAJOR 1")
        elif arg_chip in ["esp32c2", "esp32s2", "esp32c6"]:
            self.espefuse_py("burn_efuse BLK_VERSION_MINOR 1")
        elif arg_chip in ["esp32h2", "esp32h2beta1"]:
            self.espefuse_py("burn_efuse BLK_VERSION_MINOR 2")
        self.espefuse_py("adc_info")

    def test_check_error(self):
        self.espefuse_py("check_error -h")
        self.espefuse_py("check_error")
        self.espefuse_py("check_error --recovery")


class TestReadProtectionCommands(EfuseTestCase):
    def test_read_protect_efuse(self):
        self.espefuse_py("read_protect_efuse -h")
        if arg_chip == "esp32":
            cmd = "read_protect_efuse \
                   CODING_SCHEME \
                   MAC_VERSION \
                   BLOCK1 \
                   BLOCK2 \
                   BLOCK3"
            count_protects = 5
        elif arg_chip == "esp32c2":
            cmd = "read_protect_efuse \
                   BLOCK_KEY0_LOW_128"
            count_protects = 1
        else:
            self.espefuse_py(
                "burn_efuse \
                KEY_PURPOSE_0 HMAC_UP \
                KEY_PURPOSE_1 XTS_AES_128_KEY \
                KEY_PURPOSE_2 XTS_AES_128_KEY \
                KEY_PURPOSE_3 HMAC_DOWN_ALL \
                KEY_PURPOSE_4 HMAC_DOWN_JTAG \
                KEY_PURPOSE_5 HMAC_DOWN_DIGITAL_SIGNATURE"
            )
            cmd = "read_protect_efuse \
                   BLOCK_KEY0 \
                   BLOCK_KEY1 \
                   BLOCK_KEY2 \
                   BLOCK_KEY3 \
                   BLOCK_KEY4 \
                   BLOCK_KEY5"
            count_protects = 6
        self.espefuse_py(cmd)
        output = self.espefuse_py(cmd)
        assert count_protects == output.count("is already read protected")

    def test_read_protect_efuse2(self):
        self.espefuse_py("write_protect_efuse RD_DIS")
        if arg_chip == "esp32":
            efuse_name = "CODING_SCHEME"
        elif arg_chip == "esp32c2":
            efuse_name = "BLOCK_KEY0_HI_128"
        else:
            efuse_name = "BLOCK_SYS_DATA2"
        self.espefuse_py(
            f"read_protect_efuse {efuse_name}",
            check_msg="A fatal error occurred: This efuse cannot be read-disabled "
            "due the to RD_DIS field is already write-disabled",
            ret_code=2,
        )

    @pytest.mark.skipif(arg_chip != "esp32", reason="when the purpose of BLOCK2 is set")
    def test_read_protect_efuse3(self):
        self.espefuse_py("burn_efuse ABS_DONE_1 1")
        self.espefuse_py(f"burn_key BLOCK2 {IMAGES_DIR}/256bit")
        self.espefuse_py(
            "read_protect_efuse BLOCK2",
            check_msg="Secure Boot V2 is on (ABS_DONE_1 = True), "
            "BLOCK2 must be readable, stop this operation!",
            ret_code=2,
        )

    def test_read_protect_efuse4(self):
        if arg_chip == "esp32":
            self.espefuse_py(f"burn_key BLOCK2 {IMAGES_DIR}/256bit")
            msg = "must be readable, please stop this operation!"
            self.espefuse_py("read_protect_efuse BLOCK2", check_msg=msg)
        elif arg_chip == "esp32c2":
            self.espefuse_py(
                f"burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit_key SECURE_BOOT_DIGEST"
            )
            self.espefuse_py(
                "read_protect_efuse BLOCK_KEY0",
                check_msg="A fatal error occurred: "
                "BLOCK_KEY0 must be readable, stop this operation!",
                ret_code=2,
            )
        else:
            key1_purpose = (
                "USER"
                if arg_chip in ["esp32p4", "esp32c61", "esp32c5", "esp32c5beta3"]
                else "RESERVED"
            )
            self.espefuse_py(
                f"burn_key BLOCK_KEY0 {IMAGES_DIR}/256bit USER \
                BLOCK_KEY1 {IMAGES_DIR}/256bit {key1_purpose} \
                BLOCK_KEY2 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST0 \
                BLOCK_KEY3 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST1 \
                BLOCK_KEY4 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST2 \
                BLOCK_KEY5 {IMAGES_DIR}/256bit HMAC_UP"
            )
            self.espefuse_py(
                "read_protect_efuse BLOCK_KEY0",
                check_msg="A fatal error occurred: "
                "BLOCK_KEY0 must be readable, stop this operation!",
                ret_code=2,
            )
            self.espefuse_py(
                "read_protect_efuse BLOCK_KEY1",
                check_msg="A fatal error occurred: "
                "BLOCK_KEY1 must be readable, stop this operation!",
                ret_code=2,
            )
            self.espefuse_py(
                "read_protect_efuse BLOCK_KEY2",
                check_msg="A fatal error occurred: "
                "BLOCK_KEY2 must be readable, stop this operation!",
                ret_code=2,
            )
            self.espefuse_py(
                "read_protect_efuse BLOCK_KEY3",
                check_msg="A fatal error occurred: "
                "BLOCK_KEY3 must be readable, stop this operation!",
                ret_code=2,
            )
            self.espefuse_py(
                "read_protect_efuse BLOCK_KEY4",
                check_msg="A fatal error occurred: "
                "BLOCK_KEY4 must be readable, stop this operation!",
                ret_code=2,
            )
            self.espefuse_py("read_protect_efuse BLOCK_KEY5")

    @pytest.mark.skipif(
        arg_chip != "esp32",
        reason="system parameters efuse read-protection is supported only by esp32, "
        "other chips protect whole blocks",
    )
    def test_burn_and_read_protect_efuse(self):
        self.espefuse_py(
            "burn_efuse FLASH_CRYPT_CONFIG 15 RD_DIS 8",
            check_msg="Efuse FLASH_CRYPT_CONFIG is read-protected. "
            "Read back the burn value is not possible.",
        )


class TestWriteProtectionCommands(EfuseTestCase):
    def test_write_protect_efuse(self):
        self.espefuse_py("write_protect_efuse -h")
        if arg_chip == "esp32":
            efuse_lists = """WR_DIS RD_DIS CODING_SCHEME
                           XPD_SDIO_FORCE XPD_SDIO_REG XPD_SDIO_TIEH SPI_PAD_CONFIG_CLK
                           FLASH_CRYPT_CNT UART_DOWNLOAD_DIS FLASH_CRYPT_CONFIG
                           ADC_VREF BLOCK1 BLOCK2 BLOCK3"""
            efuse_lists2 = "WR_DIS RD_DIS"
        elif arg_chip == "esp32c2":
            efuse_lists = """RD_DIS DIS_DOWNLOAD_ICACHE
                           XTS_KEY_LENGTH_256 UART_PRINT_CONTROL"""
            efuse_lists2 = "RD_DIS DIS_DOWNLOAD_ICACHE"
        elif arg_chip == "esp32p4":
            efuse_lists = """RD_DIS KEY_PURPOSE_0 SECURE_BOOT_KEY_REVOKE0
                           SPI_BOOT_CRYPT_CNT"""
            efuse_lists2 = "RD_DIS KEY_PURPOSE_0 KEY_PURPOSE_2"
        else:
            efuse_lists = """RD_DIS DIS_ICACHE DIS_FORCE_DOWNLOAD
                           DIS_DOWNLOAD_MANUAL_ENCRYPT
                           USB_EXCHG_PINS WDT_DELAY_SEL SPI_BOOT_CRYPT_CNT
                           SECURE_BOOT_KEY_REVOKE0 SECURE_BOOT_KEY_REVOKE1
                           SECURE_BOOT_KEY_REVOKE2 KEY_PURPOSE_0 KEY_PURPOSE_1
                           KEY_PURPOSE_2 KEY_PURPOSE_3 KEY_PURPOSE_4 KEY_PURPOSE_5
                           SECURE_BOOT_EN SECURE_BOOT_AGGRESSIVE_REVOKE FLASH_TPUW
                           DIS_DOWNLOAD_MODE
                           ENABLE_SECURITY_DOWNLOAD UART_PRINT_CONTROL
                           MAC
                           BLOCK_USR_DATA BLOCK_KEY0 BLOCK_KEY1
                           BLOCK_KEY2 BLOCK_KEY3 BLOCK_KEY4 BLOCK_KEY5"""
            if arg_chip not in [
                "esp32h2",
                "esp32h2beta1",
                "esp32c6",
                "esp32c61",
                "esp32c5",
                "esp32c5beta3",
            ]:
                efuse_lists += """ DIS_DOWNLOAD_ICACHE
                            SPI_PAD_CONFIG_CLK SPI_PAD_CONFIG_Q
                            SPI_PAD_CONFIG_D SPI_PAD_CONFIG_CS SPI_PAD_CONFIG_HD
                            SPI_PAD_CONFIG_WP SPI_PAD_CONFIG_DQS SPI_PAD_CONFIG_D4
                            SPI_PAD_CONFIG_D5 SPI_PAD_CONFIG_D6 SPI_PAD_CONFIG_D7"""
            efuse_lists2 = "RD_DIS DIS_ICACHE"
        self.espefuse_py(f"write_protect_efuse {efuse_lists}")
        output = self.espefuse_py(f"write_protect_efuse {efuse_lists2}")
        assert output.count("is already write protected") == 2

    def test_write_protect_efuse2(self):
        if arg_chip == "esp32":
            self.espefuse_py("write_protect_efuse WR_DIS")
            self.espefuse_py(
                "write_protect_efuse CODING_SCHEME",
                check_msg="A fatal error occurred: This efuse cannot be write-disabled "
                "due to the WR_DIS field is already write-disabled",
                ret_code=2,
            )


@pytest.mark.skipif(arg_chip == "esp32p4", reason="No Custom MAC Address defined yet")
class TestBurnCustomMacCommands(EfuseTestCase):
    def test_burn_custom_mac(self):
        self.espefuse_py("burn_custom_mac -h")
        cmd = "burn_custom_mac AA:CD:EF:11:22:33"
        mac = "aa:cd:ef:11:22:33"
        if arg_chip == "esp32":
            self.espefuse_py(
                cmd, check_msg=f"Custom MAC Address version 1: {mac} (CRC 0x63 OK)"
            )
        else:
            self.espefuse_py(cmd, check_msg=f"Custom MAC Address: {mac} (OK)")

    def test_burn_custom_mac2(self):
        self.espefuse_py(
            "burn_custom_mac AA:CD:EF:11:22:33:44",
            check_msg="A fatal error occurred: MAC Address needs to be a 6-byte "
            "hexadecimal format separated by colons (:)!",
            ret_code=2,
        )

    def test_burn_custom_mac3(self):
        self.espefuse_py(
            "burn_custom_mac AB:CD:EF:11:22:33",
            check_msg="A fatal error occurred: Custom MAC must be a unicast MAC!",
            ret_code=2,
        )

    @pytest.mark.skipif(
        arg_chip != "esp32", reason="3/4 coding scheme is only in esp32"
    )
    def test_burn_custom_mac_with_34_coding_scheme(self):
        self._set_34_coding_scheme()
        self.espefuse_py("burn_custom_mac -h")
        self.espefuse_py(
            "burn_custom_mac AA:CD:EF:01:02:03",
            check_msg="Custom MAC Address version 1: aa:cd:ef:01:02:03 (CRC 0x56 OK)",
        )
        self.espefuse_py(
            "get_custom_mac",
            check_msg="Custom MAC Address version 1: aa:cd:ef:01:02:03 (CRC 0x56 OK)",
        )

        self.espefuse_py(
            "burn_custom_mac FE:22:33:44:55:66",
            check_msg="New value contains some bits that cannot be cleared "
            "(value will be 0x675745ffeffe)",
            ret_code=2,
        )


@pytest.mark.skipif(
    arg_chip
    not in [
        "esp32",
        "esp32s2",
        "esp32s3",
    ],
    reason=f"{arg_chip} does not support set_flash_voltage",
)
class TestSetFlashVoltageCommands(EfuseTestCase):
    def test_set_flash_voltage_1_8v(self):
        self.espefuse_py("set_flash_voltage -h")
        vdd = "VDD_SDIO" if arg_chip == "esp32" else "VDD_SPI"
        self.espefuse_py(
            "set_flash_voltage 1.8V",
            check_msg=f"Set internal flash voltage regulator ({vdd}) to 1.8V.",
        )
        if arg_chip == "esp32":
            error_msg = "A fatal error occurred: "
            "Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned"
        else:
            error_msg = "A fatal error occurred: "
            "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned"
        self.espefuse_py(
            "set_flash_voltage 3.3V",
            check_msg=f"Enable internal flash voltage regulator ({vdd}) to 3.3V.",
        )
        self.espefuse_py("set_flash_voltage OFF", check_msg=error_msg, ret_code=2)

    def test_set_flash_voltage_3_3v(self):
        vdd = "VDD_SDIO" if arg_chip == "esp32" else "VDD_SPI"
        self.espefuse_py(
            "set_flash_voltage 3.3V",
            check_msg=f"Enable internal flash voltage regulator ({vdd}) to 3.3V.",
        )
        if arg_chip == "esp32":
            error_msg = "A fatal error occurred: "
            "Can't set regulator to 1.8V is XPD_SDIO_TIEH efuse is already burned"
        else:
            error_msg = "A fatal error occurred: "
            "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned"
        self.espefuse_py("set_flash_voltage 1.8V", check_msg=error_msg, ret_code=2)

        if arg_chip == "esp32":
            error_msg = "A fatal error occurred: "
            "Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned"
        else:
            error_msg = "A fatal error occurred: "
            "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned"
        self.espefuse_py("set_flash_voltage OFF", check_msg=error_msg, ret_code=2)

    def test_set_flash_voltage_off(self):
        vdd = "VDD_SDIO" if arg_chip == "esp32" else "VDD_SPI"
        self.espefuse_py(
            "set_flash_voltage OFF",
            check_msg=f"Disable internal flash voltage regulator ({vdd})",
        )
        self.espefuse_py(
            "set_flash_voltage 3.3V",
            check_msg=f"Enable internal flash voltage regulator ({vdd}) to 3.3V.",
        )

    def test_set_flash_voltage_off2(self):
        vdd = "VDD_SDIO" if arg_chip == "esp32" else "VDD_SPI"
        self.espefuse_py(
            "set_flash_voltage OFF",
            check_msg=f"Disable internal flash voltage regulator ({vdd})",
        )
        self.espefuse_py(
            "set_flash_voltage 1.8V",
            check_msg=f"Set internal flash voltage regulator ({vdd}) to 1.8V.",
        )


@pytest.mark.skipif(arg_chip != "esp32c3", reason="Not necessary for all chips")
class TestValueArgForBurnEfuseCommands(EfuseTestCase):
    def test_efuse_is_bool_given_none(self):
        self.espefuse_py("burn_efuse SECURE_BOOT_KEY_REVOKE0")

    def test_efuse_is_bool_given_0(self):
        self.espefuse_py(
            "burn_efuse SECURE_BOOT_KEY_REVOKE0 0",
            check_msg="A fatal error occurred: "
            "New value is not accepted for efuse 'SECURE_BOOT_KEY_REVOKE0' "
            "(will always burn 0->1), given value=0",
            ret_code=2,
        )

    def test_efuse_is_bool_given_2(self):
        self.espefuse_py(
            "burn_efuse SECURE_BOOT_KEY_REVOKE0 2",
            check_msg="A fatal error occurred: "
            "New value is not accepted for efuse 'SECURE_BOOT_KEY_REVOKE0' "
            "(will always burn 0->1), given value=2",
            ret_code=2,
        )

    def test_efuse_is_bytes_ok(self):
        self.espefuse_py(
            "burn_efuse OPTIONAL_UNIQUE_ID 0x12345678123456781234567812345678"
        )

    def test_efuse_is_bytes_given_short_val(self):
        self.espefuse_py(
            "burn_efuse OPTIONAL_UNIQUE_ID 0x1234567812345678",
            check_msg="A fatal error occurred: "
            "The length of efuse 'OPTIONAL_UNIQUE_ID' (128 bits) "
            "(given len of the new value= 64 bits)",
            ret_code=2,
        )

    def test_efuse_is_bytes_given_none(self):
        self.espefuse_py(
            "burn_efuse OPTIONAL_UNIQUE_ID",
            check_msg="A fatal error occurred: "
            "New value required for efuse 'OPTIONAL_UNIQUE_ID' (given None)",
            ret_code=2,
        )

    def test_efuse_is_int_ok(self):
        self.espefuse_py("burn_efuse SPI_PAD_CONFIG_D 7")

    def test_efuse_is_int_given_out_of_range_val(self):
        self.espefuse_py(
            "burn_efuse SPI_PAD_CONFIG_D 200",
            check_msg="A fatal error occurred: "
            "200 is too large an unsigned integer for a bitstring "
            "of length 6. The allowed range is [0, 63].",
            ret_code=2,
        )

    def test_efuse_is_int_given_none(self):
        self.espefuse_py(
            "burn_efuse SPI_PAD_CONFIG_D",
            check_msg="A fatal error occurred: "
            "New value required for efuse 'SPI_PAD_CONFIG_D' (given None)",
            ret_code=2,
        )

    def test_efuse_is_int_given_0(self):
        self.espefuse_py(
            "burn_efuse SPI_PAD_CONFIG_D 0",
            check_msg="A fatal error occurred: "
            "New value should not be 0 for 'SPI_PAD_CONFIG_D' "
            "(given value= 0)",
            ret_code=2,
        )

    def test_efuse_is_bitcount_given_out_of_range_val(self):
        self.espefuse_py(
            "burn_efuse SPI_BOOT_CRYPT_CNT 9",
            check_msg="A fatal error occurred: "
            "9 is too large an unsigned integer for a bitstring "
            "of length 3. The allowed range is [0, 7].",
            ret_code=2,
        )

    def test_efuse_is_bitcount_given_increase_over_max(self):
        self.espefuse_py("burn_efuse SPI_BOOT_CRYPT_CNT")
        self.espefuse_py("burn_efuse SPI_BOOT_CRYPT_CNT")
        self.espefuse_py("burn_efuse SPI_BOOT_CRYPT_CNT")
        self.espefuse_py(
            "burn_efuse SPI_BOOT_CRYPT_CNT",
            check_msg="A fatal error occurred: "
            "15 is too large an unsigned integer for a bitstring "
            "of length 3. The allowed range is [0, 7].",
            ret_code=2,
        )


class TestBurnEfuseCommands(EfuseTestCase):
    @pytest.mark.skipif(
        arg_chip != "esp32",
        reason="IO pins 30 & 31 cannot be set for SPI flash only on esp32",
    )
    def test_set_spi_flash_pin_efuses(self):
        self.espefuse_py(
            "burn_efuse SPI_PAD_CONFIG_HD 30",
            check_msg="A fatal error occurred: "
            "IO pins 30 & 31 cannot be set for SPI flash. 0-29, 32 & 33 only.",
            ret_code=2,
        )
        self.espefuse_py(
            "burn_efuse SPI_PAD_CONFIG_Q 0x23",
            check_msg="A fatal error occurred: "
            "IO pin 35 cannot be set for SPI flash. 0-29, 32 & 33 only.",
            ret_code=2,
        )
        output = self.espefuse_py("burn_efuse SPI_PAD_CONFIG_CS0 33")
        assert "(Override SD_CMD pad (GPIO11/SPICS0)) 0b00000 -> 0b11111" in output
        assert "BURN BLOCK0  - OK (all write block bits are set)" in output

    @pytest.mark.skipif(
        arg_chip == "esp32p4", reason="No Custom MAC Address defined yet"
    )
    def test_burn_mac_custom_efuse(self):
        crc_msg = "(OK)"
        self.espefuse_py("burn_efuse -h")
        if arg_chip == "esp32":
            self.espefuse_py(
                "burn_efuse MAC AA:CD:EF:01:02:03",
                check_msg="Writing Factory MAC address is not supported",
                ret_code=2,
            )
            self.espefuse_py("burn_efuse MAC_VERSION 1")
            crc_msg = "(CRC 0x56 OK)"
        if arg_chip == "esp32c2":
            self.espefuse_py("burn_efuse CUSTOM_MAC_USED 1")
        self.espefuse_py("burn_efuse -h")
        self.espefuse_py(
            "burn_efuse CUSTOM_MAC AB:CD:EF:01:02:03",
            check_msg="A fatal error occurred: Custom MAC must be a unicast MAC!",
            ret_code=2,
        )
        self.espefuse_py("burn_efuse CUSTOM_MAC AA:CD:EF:01:02:03")
        self.espefuse_py("get_custom_mac", check_msg=f"aa:cd:ef:01:02:03 {crc_msg}")

    def test_burn_efuse(self):
        self.espefuse_py("burn_efuse -h")
        if arg_chip == "esp32":
            self.espefuse_py(
                "burn_efuse \
                CHIP_VER_REV2 1 \
                DISABLE_DL_ENCRYPT 1 \
                CONSOLE_DEBUG_DISABLE 1"
            )
            blk1 = "BLOCK1"
            blk2 = "BLOCK2"
        elif arg_chip == "esp32c2":
            self.espefuse_py(
                "burn_efuse \
                XTS_KEY_LENGTH_256 1 \
                UART_PRINT_CONTROL 1 \
                FORCE_SEND_RESUME 1"
            )
            blk1 = "BLOCK_KEY0"
            blk2 = None
        else:
            self.espefuse_py(
                "burn_efuse \
                SECURE_BOOT_EN 1 \
                UART_PRINT_CONTROL 1"
            )
            if arg_chip not in ["esp32c5", "esp32c5beta3", "esp32c61"]:
                # chips having the OPTIONAL_UNIQUE_ID field
                self.espefuse_py(
                    "burn_efuse \
                    OPTIONAL_UNIQUE_ID 0x2328ad5ac9145f698f843a26d6eae168",
                    check_msg="-> 0x2328ad5ac9145f698f843a26d6eae168",
                )
                output = self.espefuse_py("summary -d")
                assert (
                    "read_regs: d6eae168 8f843a26 c9145f69 2328ad5a "
                    "00000000 00000000 00000000 00000000"
                ) in output
                assert "= 68 e1 ea d6 26 3a 84 8f 69 5f 14 c9 5a ad 28 23 R/W" in output
                self.espefuse_py(
                    "burn_bit BLOCK_SYS_DATA 1",
                    check_msg="Burn into BLOCK_SYS_DATA is forbidden "
                    "(RS coding scheme does not allow this).",
                    ret_code=2,
                )
            blk1 = "BLOCK_KEY1"
            blk2 = "BLOCK_KEY2"
        output = self.espefuse_py(
            f"burn_efuse {blk1}"
            + " 0x00010203040506070809111111111111111111111111111111110000112233FF"
        )
        assert (
            "-> 0x00010203040506070809111111111111111111111111111111110000112233ff"
            in output
        )
        output = self.espefuse_py("summary -d")
        assert (
            "read_regs: 112233ff 11110000 11111111 11111111 "
            "11111111 08091111 04050607 00010203"
        ) in output
        assert (
            "= ff 33 22 11 00 00 11 11 11 11 11 11 11 11 11 11 "
            "11 11 11 11 11 11 09 08 07 06 05 04 03 02 01 00 R/W"
        ) in output

        if blk2 is not None:
            output = self.espefuse_py(
                f"burn_efuse {blk2}"
                + " 00010203040506070809111111111111111111111111111111110000112233FF"
            )
            assert (
                "-> 0xff33221100001111111111111111111111111111111109080706050403020100"
                in output
            )
            output = self.espefuse_py("summary -d")
            assert (
                "read_regs: 03020100 07060504 11110908 11111111 "
                "11111111 11111111 00001111 ff332211"
            ) in output
            assert (
                "= 00 01 02 03 04 05 06 07 08 09 11 11 11 11 11 11 "
                "11 11 11 11 11 11 11 11 11 11 00 00 11 22 33 ff R/W"
            ) in output

    @pytest.mark.skipif(
        arg_chip != "esp32", reason="3/4 coding scheme is only in esp32"
    )
    def test_burn_efuse_with_34_coding_scheme(self):
        self._set_34_coding_scheme()
        self.espefuse_py("burn_efuse BLK3_PART_RESERVE 1")
        self.espefuse_py("burn_efuse ADC1_TP_LOW 50")
        self.espefuse_py(
            "burn_efuse ADC1_TP_HIGH 55",
            check_msg="Burn into BLOCK3 is forbidden "
            "(3/4 coding scheme does not allow this)",
            ret_code=2,
        )

    @pytest.mark.skipif(
        arg_chip != "esp32", reason="3/4 coding scheme is only in esp32"
    )
    def test_burn_efuse_with_34_coding_scheme2(self):
        self._set_34_coding_scheme()
        self.espefuse_py("burn_efuse BLK3_PART_RESERVE 1")
        self.espefuse_py(
            "burn_efuse \
            ADC1_TP_LOW 50 \
            ADC1_TP_HIGH 55 \
            ADC2_TP_LOW 40 \
            ADC2_TP_HIGH 45"
        )

    @pytest.mark.skipif(
        arg_chip != "esp32s3",
        reason="Currently S3 only has this efuse incompatibility check",
    )
    def test_burn_efuse_incompatibility_check(self):
        self.espefuse_py(
            "burn_efuse DIS_USB_JTAG 1 DIS_USB_SERIAL_JTAG 1",
            check_msg="Incompatible eFuse settings detected, abort",
            ret_code=2,
        )
        self.espefuse_py("burn_efuse DIS_USB_JTAG 1")
        self.espefuse_py(
            "burn_efuse DIS_USB_SERIAL_JTAG 1",
            check_msg="Incompatible eFuse settings detected, abort",
            ret_code=2,
        )
        self.espefuse_py("burn_efuse DIS_USB_SERIAL_JTAG 1 --force")


class TestBurnKeyCommands(EfuseTestCase):
    @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only")
    def test_burn_key_3_key_blocks(self):
        self.espefuse_py("burn_key -h")
        self.espefuse_py(
            f"burn_key BLOCK1 {IMAGES_DIR}/192bit",
            check_msg="A fatal error occurred: Incorrect key file size 24. "
            "Key file must be 32 bytes (256 bits) of raw binary key data.",
            ret_code=2,
        )
        self.espefuse_py(
            f"burn_key \
            BLOCK1 {IMAGES_DIR}/256bit \
            BLOCK2 {IMAGES_DIR}/256bit_1 \
            BLOCK3 {IMAGES_DIR}/256bit_2 --no-protect-key"
        )
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_1")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_2")

        self.espefuse_py(
            f"burn_key \
            BLOCK1 {IMAGES_DIR}/256bit \
            BLOCK2 {IMAGES_DIR}/256bit_1 \
            BLOCK3 {IMAGES_DIR}/256bit_2"
        )
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_1")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_2")

    @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only")
    def test_burn_key_1_key_block(self):
        self.espefuse_py("burn_key -h")
        self.espefuse_py(
            f"burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit XTS_AES_128_KEY",
            check_msg="A fatal error occurred: Incorrect key file size 16. "
            "Key file must be 32 bytes (256 bits) of raw binary key data.",
            ret_code=2,
        )
        self.espefuse_py(
            f"burn_key BLOCK_KEY0 {IMAGES_DIR}/256bit XTS_AES_128_KEY --no-read-protect"
        )
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit", reverse_order=True)

        self.espefuse_py(f"burn_key BLOCK_KEY0 {IMAGES_DIR}/256bit XTS_AES_128_KEY")
        output = self.espefuse_py("summary -d")
        assert (
            "[3 ] read_regs: 00000000 00000000 00000000 00000000 "
            "00000000 00000000 00000000 00000000"
        ) in output

        assert (
            "= ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? "
            "?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/-"
        ) in output

    @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only")
    def test_burn_key_one_key_block_with_fe_and_sb_keys(self):
        self.espefuse_py("burn_key -h")
        self.espefuse_py(
            f"burn_key BLOCK_KEY0 {IMAGES_DIR}/256bit XTS_AES_128_KEY \
            BLOCK_KEY0 {IMAGES_DIR}/128bit_key SECURE_BOOT_DIGEST",
            check_msg="A fatal error occurred: These keypurposes are incompatible "
            "['XTS_AES_128_KEY', 'SECURE_BOOT_DIGEST']",
            ret_code=2,
        )
        self.espefuse_py(
            f"burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit_key "
            f"XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS "
            f"BLOCK_KEY0 {IMAGES_DIR}/128bit_key SECURE_BOOT_DIGEST --no-read-protect"
        )
        output = self.espefuse_py("summary -d")
        assert (
            "[3 ] read_regs: 0c0d0e0f 08090a0b 04050607 00010203 "
            "03020100 07060504 0b0a0908 0f0e0d0c"
        ) in output

        self.espefuse_py(
            f"burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit_key "
            "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS "
            f"BLOCK_KEY0 {IMAGES_DIR}/128bit_key SECURE_BOOT_DIGEST"
        )
        output = self.espefuse_py("summary -d")
        assert (
            "[3 ] read_regs: 00000000 00000000 00000000 00000000 "
            "03020100 07060504 0b0a0908 0f0e0d0c"
        ) in output

        assert (
            "= ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? "
            "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f -/-"
        ) in output
        assert "= ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/-" in output
        assert "= 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f R/-" in output

    @pytest.mark.skipif(
        arg_chip
        not in [
            "esp32s2",
            "esp32s3",
            "esp32s3beta1",
            "esp32c3",
            "esp32h2beta1",
            "esp32c6",
            "esp32h2",
            "esp32p4",
            "esp32c5",
            "esp32c5beta3",
            "esp32c61",
        ],
        reason="Only chips with 6 keys",
    )
    def test_burn_key_with_6_keys(self):
        cmd = f"burn_key \
               BLOCK_KEY0 {IMAGES_DIR}/256bit   XTS_AES_256_KEY_1 \
               BLOCK_KEY1 {IMAGES_DIR}/256bit_1 XTS_AES_256_KEY_2 \
               BLOCK_KEY2 {IMAGES_DIR}/256bit_2 XTS_AES_128_KEY"
        if arg_chip in [
            "esp32c3",
            "esp32c6",
            "esp32h2",
            "esp32h2beta1",
            "esp32c5",
            "esp32c5beta3",
        ]:
            cmd = cmd.replace("XTS_AES_256_KEY_1", "XTS_AES_128_KEY")
            cmd = cmd.replace("XTS_AES_256_KEY_2", "XTS_AES_128_KEY")
        self.espefuse_py(cmd + " --no-read-protect --no-write-protect")
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit", reverse_order=True)
        self.check_data_block_in_log(
            output, f"{IMAGES_DIR}/256bit_1", reverse_order=True
        )
        self.check_data_block_in_log(
            output, f"{IMAGES_DIR}/256bit_2", reverse_order=True
        )

        self.espefuse_py(cmd)
        output = self.espefuse_py("summary -d")
        assert (
            "[4 ] read_regs: 00000000 00000000 00000000 00000000 "
            "00000000 00000000 00000000 00000000"
        ) in output
        assert (
            "[5 ] read_regs: 00000000 00000000 00000000 00000000 "
            "00000000 00000000 00000000 00000000"
        ) in output
        assert (
            "[6 ] read_regs: 00000000 00000000 00000000 00000000 "
            "00000000 00000000 00000000 00000000"
        ) in output

        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY3 {IMAGES_DIR}/256bit   SECURE_BOOT_DIGEST0 \
            BLOCK_KEY4 {IMAGES_DIR}/256bit_1 SECURE_BOOT_DIGEST1 \
            BLOCK_KEY5 {IMAGES_DIR}/256bit_2 SECURE_BOOT_DIGEST2"
        )
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_1")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_2")

    @pytest.mark.skipif(
        arg_chip != "esp32", reason="3/4 coding scheme is only in esp32"
    )
    def test_burn_key_with_34_coding_scheme(self):
        self._set_34_coding_scheme()
        self.espefuse_py(
            f"burn_key BLOCK1 {IMAGES_DIR}/256bit",
            check_msg="A fatal error occurred: Incorrect key file size 32. "
            "Key file must be 24 bytes (192 bits) of raw binary key data.",
            ret_code=2,
        )
        self.espefuse_py(
            f"burn_key \
            BLOCK1 {IMAGES_DIR}/192bit \
            BLOCK2 {IMAGES_DIR}/192bit_1 \
            BLOCK3 {IMAGES_DIR}/192bit_2 --no-protect-key"
        )
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_1")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_2")

        self.espefuse_py(
            f"burn_key \
            BLOCK1 {IMAGES_DIR}/192bit \
            BLOCK2 {IMAGES_DIR}/192bit_1 \
            BLOCK3 {IMAGES_DIR}/192bit_2"
        )
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_1")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_2")

    @pytest.mark.skipif(
        arg_chip not in ["esp32s2", "esp32s3", "esp32p4", "esp32c61"],
        reason="512 bit keys are only supported on ESP32-S2, S3, P4, C61",
    )
    def test_burn_key_512bit(self):
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY0 {IMAGES_DIR}/256bit_1_256bit_2_combined \
            XTS_AES_256_KEY --no-read-protect --no-write-protect"
        )
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(
            output, f"{IMAGES_DIR}/256bit_1", reverse_order=True
        )
        self.check_data_block_in_log(
            output, f"{IMAGES_DIR}/256bit_2", reverse_order=True
        )

    @pytest.mark.skipif(
        arg_chip not in ["esp32s2", "esp32s3", "esp32p4", "esp32c61"],
        reason="512 bit keys are only supported on ESP32-S2, S3, P4, C61",
    )
    def test_burn_key_512bit_non_consecutive_blocks(self):
        # Burn efuses separately to test different kinds
        # of "key used" detection criteria
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY2 {IMAGES_DIR}/256bit XTS_AES_128_KEY"
        )
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY4 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST0"
        )
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY1 {IMAGES_DIR}/256bit_1_256bit_2_combined \
            XTS_AES_256_KEY --no-read-protect --no-write-protect"
        )
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY5 {IMAGES_DIR}/256bit USER --no-read-protect --no-write-protect"
        )

        # Second half of key should burn to first available key block (BLOCK_KEY5)
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(
            output, f"{IMAGES_DIR}/256bit_1", reverse_order=True
        )
        self.check_data_block_in_log(
            output, f"{IMAGES_DIR}/256bit_2", reverse_order=True
        )

        assert (
            "[5 ] read_regs: bcbd11bf b8b9babb b4b5b6b7 "
            "b0b1b2b3 acadaeaf a8a9aaab a4a5a6a7 11a1a2a3"
        ) in output
        assert (
            "[7 ] read_regs: bcbd22bf b8b9babb b4b5b6b7 "
            "b0b1b2b3 acadaeaf a8a9aaab a4a5a6a7 22a1a2a3"
        ) in output

    @pytest.mark.skipif(
        arg_chip not in ["esp32s2", "esp32s3", "esp32p4", "esp32c61"],
        reason="512 bit keys are only supported on ESP32-S2, S3, P4, C61",
    )
    def test_burn_key_512bit_non_consecutive_blocks_loop_around(self):
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY2 {IMAGES_DIR}/256bit XTS_AES_128_KEY \
            BLOCK_KEY3 {IMAGES_DIR}/256bit USER \
            BLOCK_KEY4 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST0 \
            BLOCK_KEY5 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST1 \
            BLOCK_KEY1 {IMAGES_DIR}/256bit_1_256bit_2_combined \
            XTS_AES_256_KEY --no-read-protect --no-write-protect"
        )

        # Second half of key should burn to first available key block (BLOCK_KEY0)
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(
            output, f"{IMAGES_DIR}/256bit_1", reverse_order=True
        )
        self.check_data_block_in_log(
            output, f"{IMAGES_DIR}/256bit_2", reverse_order=True
        )

        assert (
            "[5 ] read_regs: bcbd11bf b8b9babb b4b5b6b7 b0b1b2b3 "
            "acadaeaf a8a9aaab a4a5a6a7 11a1a2a3"
        ) in output
        assert (
            "[4 ] read_regs: bcbd22bf b8b9babb b4b5b6b7 b0b1b2b3 "
            "acadaeaf a8a9aaab a4a5a6a7 22a1a2a3"
        ) in output

    @pytest.mark.skipif(
        arg_chip not in ["esp32h2", "esp32c5", "esp32c5beta3", "esp32c61", "esp32p4"],
        reason="These chips support ECDSA_KEY",
    )
    def test_burn_key_ecdsa_key(self):
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY0 {S_IMAGES_DIR}/ecdsa192_secure_boot_signing_key_v2.pem \
            ECDSA_KEY \
            BLOCK_KEY1 {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem \
            ECDSA_KEY"
        )
        output = self.espefuse_py("summary -d")
        assert 2 == output.count(
            "= ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? "
            "?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/-"
        )
        assert (
            "[4 ] read_regs: 00000000 00000000 00000000 00000000 "
            "00000000 00000000 00000000 00000000"
        ) in output
        assert (
            "[5 ] read_regs: 00000000 00000000 00000000 00000000 "
            "00000000 00000000 00000000 00000000"
        ) in output

    @pytest.mark.skipif(
        arg_chip not in ["esp32h2", "esp32c5", "esp32c5beta3", "esp32c61", "esp32p4"],
        reason="These chips support ECDSA_KEY",
    )
    def test_burn_key_ecdsa_key_check_byte_order(self):
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY0 {S_IMAGES_DIR}/ecdsa192_secure_boot_signing_key_v2.pem \
            ECDSA_KEY \
            BLOCK_KEY1 {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem \
            ECDSA_KEY \
            --no-read-protect"
        )
        output = self.espefuse_py("summary -d")
        assert (
            "= c8 c4 5d 62 9e 05 05 bd cb 04 a4 7c 06 f5 86 14 "
            "cb 23 81 23 95 b7 71 4f 00 00 00 00 00 00 00 00 R/-"
        ) in output
        assert (
            "= fc 6b ec 75 64 37 7d 3b 88 8d 34 05 ed 91 06 1b "
            "38 c2 50 84 7a 08 9d c3 66 6a 06 90 23 8b 54 b4 R/-"
        ) in output
        assert (
            "[4 ] read_regs: 625dc4c8 bd05059e 7ca404cb 1486f506 "
            "238123cb 4f71b795 00000000 00000000"
        ) in output
        assert (
            "[5 ] read_regs: 75ec6bfc 3b7d3764 05348d88 1b0691ed "
            "8450c238 c39d087a 90066a66 b4548b23"
        ) in output


class TestBurnBlockDataCommands(EfuseTestCase):
    def test_burn_block_data_check_args(self):
        self.espefuse_py("burn_block_data -h")
        blk0 = "BLOCK0"
        blk1 = "BLOCK1"
        self.espefuse_py(
            f"burn_block_data {blk0} {IMAGES_DIR}/224bit {blk1}",
            check_msg="A fatal error occurred: "
            "The number of block_name (2) and datafile (1) should be the same.",
            ret_code=2,
        )

    @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only")
    def test_burn_block_data_with_3_key_blocks(self):
        self.espefuse_py(
            f"burn_block_data \
            BLOCK0 {IMAGES_DIR}/224bit \
            BLOCK3 {IMAGES_DIR}/256bit"
        )
        output = self.espefuse_py("summary -d")
        assert (
            "[3 ] read_regs: a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac "
            "b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc"
        ) in output
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit")

        self.espefuse_py(
            f"burn_block_data \
            BLOCK2 {IMAGES_DIR}/256bit_1"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/256bit_1"
        )

        self.espefuse_py(
            f"burn_block_data \
            BLOCK1 {IMAGES_DIR}/256bit_2"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/256bit_2"
        )

    @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only")
    def test_burn_block_data_with_1_key_block(self):
        self.espefuse_py(
            f"burn_block_data \
            BLOCK0 {IMAGES_DIR}/64bit \
            BLOCK1 {IMAGES_DIR}/96bit \
            BLOCK2 {IMAGES_DIR}/256bit \
            BLOCK3 {IMAGES_DIR}/256bit"
        )
        output = self.espefuse_py("summary -d")
        assert "[0 ] read_regs: 00000001 0000000c" in output
        assert "[1 ] read_regs: 03020100 07060504 000a0908" in output
        assert (
            "[2 ] read_regs: a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac "
            "b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc"
        ) in output
        assert (
            "[3 ] read_regs: a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac "
            "b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc"
        ) in output

    @pytest.mark.skipif(
        arg_chip
        not in [
            "esp32s2",
            "esp32s3",
            "esp32s3beta1",
            "esp32c3",
            "esp32h2beta1",
            "esp32c6",
            "esp32h2",
            "esp32p4",
            "esp32c5",
            "esp32c5beta3",
            "esp32c61",
        ],
        reason="Only chip with 6 keys",
    )
    def test_burn_block_data_with_6_keys(self):
        self.espefuse_py(
            f"burn_block_data \
            BLOCK0 {IMAGES_DIR}/192bit \
            BLOCK3 {IMAGES_DIR}/256bit"
        )
        output = self.espefuse_py("summary -d")
        assert (
            "[0 ] read_regs: 00000000 07060500 00000908 00000000 13000000 00161514"
            in output
        )
        assert (
            "[3 ] read_regs: a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac "
            "b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc"
        ) in output
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit")

        self.espefuse_py(
            f"burn_block_data \
            BLOCK10 {IMAGES_DIR}/256bit_1"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/256bit_1"
        )

        self.espefuse_py(
            f"burn_block_data \
            BLOCK1 {IMAGES_DIR}/192bit \
            BLOCK5 {IMAGES_DIR}/256bit_1 \
            BLOCK6 {IMAGES_DIR}/256bit_2"
        )
        output = self.espefuse_py("summary -d")
        assert (
            "[1 ] read_regs: 00000000 07060500 00000908 00000000 13000000 00161514"
            in output
        )
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_1", 2)
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_2")

    def test_burn_block_data_check_errors(self):
        self.espefuse_py(
            f"burn_block_data \
            BLOCK2 {IMAGES_DIR}/192bit \
            BLOCK2 {IMAGES_DIR}/192bit_1",
            check_msg="A fatal error occurred: Found repeated",
            ret_code=2,
        )
        self.espefuse_py(
            f"burn_block_data \
            BLOCK2 {IMAGES_DIR}/192bit \
            BLOCK3 {IMAGES_DIR}/192bit_1 \
            --offset 4",
            check_msg="A fatal error occurred: "
            "The 'offset' option is not applicable when a few blocks are passed.",
            ret_code=2,
        )
        self.espefuse_py(
            f"burn_block_data BLOCK0 {IMAGES_DIR}/192bit --offset 33",
            check_msg="A fatal error occurred: Invalid offset: the block0 only holds",
            ret_code=2,
        )
        self.espefuse_py(
            f"burn_block_data BLOCK0 {IMAGES_DIR}/256bit --offset 4",
            check_msg="A fatal error occurred: Data does not fit:",
            ret_code=2,
        )

    @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only")
    def test_burn_block_data_with_offset_for_3_key_blocks(self):
        offset = 1
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK0 {IMAGES_DIR}/192bit"
        )

        offset = 4
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK1 {IMAGES_DIR}/192bit_1"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_1", offset=offset
        )

        offset = 6
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK2 {IMAGES_DIR}/192bit_2"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_2", offset=offset
        )

        offset = 8
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK3 {IMAGES_DIR}/192bit_2"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_2", offset=offset
        )

    @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only")
    def test_burn_block_data_with_offset_1_key_block(self):
        offset = 4
        self.espefuse_py(f"burn_block_data --offset {offset} BLOCK1 {IMAGES_DIR}/92bit")
        output = self.espefuse_py("summary -d")
        assert "[1 ] read_regs: 00000000 03020100 00060504" in output

        offset = 6
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK2 {IMAGES_DIR}/192bit_1"
        )
        output = self.espefuse_py("summary -d")
        assert (
            "[2 ] read_regs: 00000000 00110000 05000000 09080706 "
            "0d0c0b0a 11100f0e 15141312 00002116"
        ) in output

        offset = 8
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK3 {IMAGES_DIR}/192bit_2"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_2", offset=offset
        )

    @pytest.mark.skipif(
        arg_chip
        not in [
            "esp32s2",
            "esp32s3",
            "esp32s3beta1",
            "esp32c3",
            "esp32h2beta1",
            "esp32c6",
            "esp32h2",
            "esp32p4",
            "esp32c5",
            "esp32c5beta3",
            "esp32c61",
        ],
        reason="Only chips with 6 keys",
    )
    def test_burn_block_data_with_offset_6_keys(self):
        offset = 4
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK_KEY0 {IMAGES_DIR}/192bit_1"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_1", offset=offset
        )

        offset = 6
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK_KEY1 {IMAGES_DIR}/192bit_2"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_2", offset=offset
        )

        offset = 8
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK_KEY2 {IMAGES_DIR}/192bit_2"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_2", offset=offset
        )

    @pytest.mark.skipif(
        arg_chip != "esp32", reason="3/4 coding scheme is only in esp32"
    )
    def test_burn_block_data_with_34_coding_scheme(self):
        self._set_34_coding_scheme()
        self.espefuse_py(
            f"burn_block_data BLOCK1 {IMAGES_DIR}/256bit",
            check_msg="A fatal error occurred: Data does not fit: "
            "the block1 size is 24 bytes, data file is 32 bytes, offset 0",
            ret_code=2,
        )

        self.espefuse_py(
            f"burn_block_data \
            BLOCK1 {IMAGES_DIR}/192bit \
            BLOCK2 {IMAGES_DIR}/192bit_1 \
            BLOCK3 {IMAGES_DIR}/192bit_2"
        )
        output = self.espefuse_py("summary -d")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_1")
        self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_2")

    @pytest.mark.skipif(
        arg_chip != "esp32", reason="3/4 coding scheme is only in esp32"
    )
    def test_burn_block_data_with_34_coding_scheme_and_offset(self):
        self._set_34_coding_scheme()

        offset = 4
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK1 {IMAGES_DIR}/128bit"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/128bit", offset=offset
        )

        offset = 6
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK2 {IMAGES_DIR}/128bit"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/128bit", offset=offset
        )

        offset = 8
        self.espefuse_py(
            f"burn_block_data --offset {offset} BLOCK3 {IMAGES_DIR}/128bit"
        )
        self.check_data_block_in_log(
            self.espefuse_py("summary -d"), f"{IMAGES_DIR}/128bit", offset=offset
        )


@pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only, supports 2 key blocks")
class TestBurnKeyDigestCommandsEsp32(EfuseTestCase):
    def test_burn_key_digest(self):
        self.espefuse_py("burn_key_digest -h")
        esp = self.get_esptool()
        if esp.get_chip_revision() >= 300:
            self.espefuse_py(
                f"burn_key_digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem"
            )
            output = self.espefuse_py("summary -d")
            assert (
                " = cb 27 91 a3 71 b0 c0 32 2b f7 37 04 78 ba 09 62 "
                "22 4c ab 1c f2 28 78 79 e4 29 67 3e 7d a8 44 63 R/-"
            ) in output
        else:
            self.espefuse_py(
                f"burn_key_digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem",
                check_msg="Incorrect chip revision for Secure boot v2.",
                ret_code=2,
            )

    def test_burn_key_from_digest(self):
        # python espsecure.py digest_rsa_public_key
        # --keyfile test/{S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem
        # -o {S_IMAGES_DIR}/rsa_public_key_digest.bin
        self.espefuse_py(
            f"burn_key \
            BLOCK2 {S_IMAGES_DIR}/rsa_public_key_digest.bin --no-protect-key"
        )
        output = self.espefuse_py("summary -d")
        assert 1 == output.count(
            " = cb 27 91 a3 71 b0 c0 32 2b f7 37 04 78 ba 09 62 "
            "22 4c ab 1c f2 28 78 79 e4 29 67 3e 7d a8 44 63 R/W"
        )

    def test_burn_key_digest_with_34_coding_scheme(self):
        self._set_34_coding_scheme()
        self.espefuse_py(
            f"burn_key_digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem",
            check_msg="burn_key_digest only works with 'None' coding scheme",
            ret_code=2,
        )


@pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only, supports 1 key block")
class TestBurnKeyDigestCommandsEsp32C2(EfuseTestCase):
    def test_burn_key_digest1(self):
        # python espsecure.py generate_signing_key --version 2
        # secure_images/ecdsa192_secure_boot_signing_key_v2.pem --scheme ecdsa192
        self.espefuse_py("burn_key_digest -h")
        self.espefuse_py(
            f"burn_key_digest {S_IMAGES_DIR}/ecdsa192_secure_boot_signing_key_v2.pem"
        )
        output = self.espefuse_py("summary -d")
        assert " = 1e 3d 15 16 96 ca 7f 22 a6 e8 8b d5 27 a0 3b 3b R/-" in output
        assert (
            " = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
            "1e 3d 15 16 96 ca 7f 22 a6 e8 8b d5 27 a0 3b 3b R/-"
        ) in output

    def test_burn_key_digest2(self):
        # python espsecure.py generate_signing_key --version 2
        # secure_images/ecdsa256_secure_boot_signing_key_v2.pem   --scheme ecdsa256
        self.espefuse_py("burn_key_digest -h")
        self.espefuse_py(
            f"burn_key_digest {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem"
        )
        output = self.espefuse_py("summary -d")
        assert " = bf 0f 6a f6 8b d3 6d 8b 53 b3 da a9 33 f6 0a 04 R/-" in output
        assert (
            " = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
            "bf 0f 6a f6 8b d3 6d 8b 53 b3 da a9 33 f6 0a 04 R/-"
        ) in output

    def test_burn_key_from_digest1(self):
        # python espsecure.py digest_sbv2_public_key --keyfile
        # secure_images/ecdsa192_secure_boot_signing_key_v2.pem
        # -o secure_images/ecdsa192_public_key_digest_v2.bin
        self.espefuse_py(
            "burn_key BLOCK_KEY0 "
            f"{S_IMAGES_DIR}/ecdsa192_public_key_digest_v2.bin SECURE_BOOT_DIGEST"
        )
        output = self.espefuse_py("summary -d")
        assert (
            " = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
            "1e 3d 15 16 96 ca 7f 22 a6 e8 8b d5 27 a0 3b 3b R/-"
        ) in output

    def test_burn_key_from_digest2(self):
        # python espsecure.py digest_sbv2_public_key --keyfile
        # secure_images/ecdsa256_secure_boot_signing_key_v2.pem
        # -o secure_images/ecdsa256_public_key_digest_v2.bin
        self.espefuse_py(
            "burn_key BLOCK_KEY0 "
            f"{S_IMAGES_DIR}/ecdsa256_public_key_digest_v2.bin SECURE_BOOT_DIGEST"
        )
        output = self.espefuse_py("summary -d")
        assert (
            " = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
            "bf 0f 6a f6 8b d3 6d 8b 53 b3 da a9 33 f6 0a 04 R/-"
        ) in output


@pytest.mark.skipif(
    arg_chip
    not in [
        "esp32s2",
        "esp32s3",
        "esp32s3beta1",
        "esp32c3",
        "esp32h2beta1",
        "esp32c6",
        "esp32h2",
        "esp32p4",
        "esp32c5",
        "esp32c5beta3",
        "esp32c61",
    ],
    reason="Supports 6 key blocks",
)
class TestBurnKeyDigestCommands(EfuseTestCase):
    def test_burn_key_digest(self):
        self.espefuse_py("burn_key_digest -h")
        self.espefuse_py(
            f"burn_key_digest \
            BLOCK_KEY0 \
            {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem SECURE_BOOT_DIGEST0 \
            BLOCK_KEY1 \
            {S_IMAGES_DIR}/rsa_secure_boot_signing_key2.pem SECURE_BOOT_DIGEST1 \
            BLOCK_KEY2 ",
            check_msg="A fatal error occurred: The number of blocks (3), "
            "datafile (2) and keypurpose (2) should be the same.",
            ret_code=2,
        )
        self.espefuse_py(
            f"burn_key_digest \
            BLOCK_KEY0 \
            {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem SECURE_BOOT_DIGEST0 \
            BLOCK_KEY1 \
            {S_IMAGES_DIR}/rsa_secure_boot_signing_key2.pem SECURE_BOOT_DIGEST1 \
            BLOCK_KEY2 \
            {S_IMAGES_DIR}/rsa_secure_boot_signing_key2.pem SECURE_BOOT_DIGEST2"
        )
        output = self.espefuse_py("summary -d")
        assert 1 == output.count(
            " = cb 27 91 a3 71 b0 c0 32 2b f7 37 04 78 ba 09 62 "
            "22 4c ab 1c f2 28 78 79 e4 29 67 3e 7d a8 44 63 R/-"
        )
        assert 2 == output.count(
            " = 90 1a 74 09 23 8d 52 d4 cb f9 6f 56 3f b3 f4 29 "
            "6d ab d6 6a 33 f5 3b 15 ee cd 8c b3 e7 ec 45 d3 R/-"
        )

    def test_burn_key_from_digest(self):
        #  python espsecure.py digest_rsa_public_key
        # --keyfile test/secure_images/rsa_secure_boot_signing_key.pem
        # -o secure_images/rsa_public_key_digest.bin
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY0 {S_IMAGES_DIR}/rsa_public_key_digest.bin SECURE_BOOT_DIGEST0"
        )
        output = self.espefuse_py("summary -d")
        assert 1 == output.count(
            " = cb 27 91 a3 71 b0 c0 32 2b f7 37 04 78 ba 09 62 "
            "22 4c ab 1c f2 28 78 79 e4 29 67 3e 7d a8 44 63 R/-"
        )

        self.espefuse_py(
            f"burn_key_digest \
            BLOCK_KEY1 \
            {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem SECURE_BOOT_DIGEST1"
        )
        output = self.espefuse_py("summary -d")
        assert 2 == output.count(
            " = cb 27 91 a3 71 b0 c0 32 2b f7 37 04 78 ba 09 62 "
            "22 4c ab 1c f2 28 78 79 e4 29 67 3e 7d a8 44 63 R/-"
        )


class TestBurnBitCommands(EfuseTestCase):
    @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only")
    def test_burn_bit_for_chips_with_3_key_blocks(self):
        self.espefuse_py("burn_bit -h")
        self.espefuse_py("burn_bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255")
        self.espefuse_py(
            "summary",
            check_msg="17 01 01 00 01 00 00 00 01 00 00 00 01 00 00 "
            "00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 80",
        )

        self.espefuse_py(
            "burn_bit BLOCK3 3 5 6 7 9 10 11 12 13 14 15 31 63 95 127 159 191 223 254"
        )
        self.espefuse_py(
            "summary",
            check_msg="ff ff 01 80 01 00 00 80 01 00 00 80 01 "
            "00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01 00 00 c0",
        )

    @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only")
    def test_burn_bit_for_chips_with_1_key_block(self):
        self.espefuse_py("burn_bit -h")
        self.espefuse_py("burn_bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255")
        self.espefuse_py(
            "summary",
            check_msg="17 01 01 00 01 00 00 00 01 00 00 00 01 00 "
            "00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 80",
        )
        self.espefuse_py(
            "burn_bit BLOCK3 100",
            check_msg="Burn into BLOCK_KEY0 is forbidden "
            "(RS coding scheme does not allow this)",
            ret_code=2,
        )

        self.espefuse_py("burn_bit BLOCK0 0 1 2")
        self.espefuse_py("summary", check_msg="[0 ] read_regs: 00000007 00000000")

    @pytest.mark.skipif(
        arg_chip
        not in [
            "esp32s2",
            "esp32s3",
            "esp32s3beta1",
            "esp32c3",
            "esp32h2beta1",
            "esp32c6",
            "esp32h2",
            "esp32p4",
            "esp32c5",
            "esp32c5beta3",
            "esp32c61",
        ],
        reason="Only chip with 6 keys",
    )
    def test_burn_bit_for_chips_with_6_key_blocks(self):
        self.espefuse_py("burn_bit -h")
        self.espefuse_py("burn_bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255")
        self.espefuse_py(
            "summary",
            check_msg="17 01 01 00 01 00 00 00 01 00 00 00 01 00 "
            "00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 80",
        )
        self.espefuse_py(
            "burn_bit BLOCK3 100",
            check_msg="Burn into BLOCK_USR_DATA is forbidden "
            "(RS coding scheme does not allow this)",
            ret_code=2,
        )

        self.espefuse_py("burn_bit BLOCK0 13")
        self.espefuse_py(
            "summary",
            check_msg="[0 ] read_regs: 00002000 00000000 00000000 "
            "00000000 00000000 00000000",
        )

        self.espefuse_py("burn_bit BLOCK0 24")
        self.espefuse_py(
            "summary",
            check_msg="[0 ] read_regs: 01002000 00000000 00000000 "
            "00000000 00000000 00000000",
        )

    @pytest.mark.skipif(
        arg_chip != "esp32", reason="3/4 coding scheme is only in esp32"
    )
    def test_burn_bit_with_34_coding_scheme(self):
        self._set_34_coding_scheme()
        self.espefuse_py("burn_bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 191")
        self.espefuse_py(
            "summary",
            check_msg="17 01 01 00 01 00 00 00 01 00 00 00 01 00 "
            "00 00 01 00 00 00 01 00 00 80",
        )
        self.espefuse_py(
            "burn_bit BLOCK3 17",
            check_msg="Burn into BLOCK3 is forbidden "
            "(3/4 coding scheme does not allow this).",
            ret_code=2,
        )

    @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only")
    def test_burn_bit_with_none_recovery_coding_scheme(self):
        self._set_none_recovery_coding_scheme()
        self.espefuse_py("burn_bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255")
        self.espefuse_py(
            "summary",
            check_msg="17 01 01 00 01 00 00 00 01 00 00 00 01 00 00 "
            "00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 80",
        )


@pytest.mark.skipif(
    arg_chip != "esp32", reason="Tests are only for esp32. (TODO: add for all chips)"
)
class TestByteOrderBurnKeyCommand(EfuseTestCase):
    def test_1_secure_boot_v1(self):
        if arg_chip == "esp32":
            self.espefuse_py(
                f"burn_key \
                flash_encryption {IMAGES_DIR}/256bit \
                secure_boot_v1 {IMAGES_DIR}/256bit_1 --no-protect-key"
            )
            output = self.espefuse_py("summary -d")
            self.check_data_block_in_log(
                output, f"{IMAGES_DIR}/256bit", reverse_order=True
            )
            self.check_data_block_in_log(
                output, f"{IMAGES_DIR}/256bit_1", reverse_order=True
            )

            self.espefuse_py(
                f"burn_key \
                flash_encryption  {IMAGES_DIR}/256bit \
                secure_boot_v1    {IMAGES_DIR}/256bit_1"
            )
            output = self.espefuse_py("summary -d")
            assert (
                "[1 ] read_regs: 00000000 00000000 00000000 00000000 "
                "00000000 00000000 00000000 00000000"
            ) in output
            assert (
                "[2 ] read_regs: 00000000 00000000 00000000 00000000 "
                "00000000 00000000 00000000 00000000"
            ) in output
            assert (
                "[3 ] read_regs: 00000000 00000000 00000000 00000000 "
                "00000000 00000000 00000000 00000000"
            ) in output

    def test_2_secure_boot_v1(self):
        if arg_chip == "esp32":
            self.espefuse_py(
                f"burn_key \
                flash_encryption {IMAGES_DIR}/256bit \
                secure_boot_v2 {IMAGES_DIR}/256bit_1 --no-protect-key"
            )
            output = self.espefuse_py("summary -d")
            self.check_data_block_in_log(
                output, f"{IMAGES_DIR}/256bit", reverse_order=True
            )
            self.check_data_block_in_log(
                output, f"{IMAGES_DIR}/256bit_1", reverse_order=False
            )

            self.espefuse_py(
                f"burn_key \
                flash_encryption {IMAGES_DIR}/256bit \
                secure_boot_v2 {IMAGES_DIR}/256bit_1"
            )
            output = self.espefuse_py("summary -d")
            assert (
                "[1 ] read_regs: 00000000 00000000 00000000 00000000 "
                "00000000 00000000 00000000 00000000"
            ) in output
            self.check_data_block_in_log(
                output, f"{IMAGES_DIR}/256bit_1", reverse_order=False
            )


class TestExecuteScriptsCommands(EfuseTestCase):
    @classmethod
    def setup_class(self):
        # Save the current working directory to be restored later
        self.stored_dir = os.getcwd()

    @classmethod
    def teardown_class(self):
        # Restore the stored working directory
        os.chdir(self.stored_dir)

    @pytest.mark.skipif(
        arg_chip in ["esp32c2", "esp32p4"],
        reason="These chips do not have eFuses used in this test",
    )
    def test_execute_scripts_with_check_that_only_one_burn(self):
        self.espefuse_py("execute_scripts -h")
        name = arg_chip if arg_chip in ["esp32", "esp32c2"] else "esp32xx"
        os.chdir(os.path.join(TEST_DIR, "efuse_scripts", name))
        self.espefuse_py("execute_scripts execute_efuse_script2.py")

    @pytest.mark.skipif(
        arg_chip in ["esp32c2", "esp32p4"],
        reason="These chips do not have eFuses used in this test",
    )
    def test_execute_scripts_with_check(self):
        self.espefuse_py("execute_scripts -h")
        name = arg_chip if arg_chip in ["esp32", "esp32c2"] else "esp32xx"
        os.chdir(os.path.join(TEST_DIR, "efuse_scripts", name))
        self.espefuse_py("execute_scripts execute_efuse_script.py")

    def test_execute_scripts_with_index_and_config(self):
        os.chdir(TEST_DIR)
        if arg_chip in ["esp32", "esp32c2"]:
            cmd = f"execute_scripts {EFUSE_S_DIR}/efuse_burn1.py --index 10 \
            --configfiles {EFUSE_S_DIR}/esp32/config1.json"
        else:
            cmd = f"execute_scripts {EFUSE_S_DIR}/efuse_burn1.py --index 10 \
            --configfiles {EFUSE_S_DIR}/esp32xx/config1.json"
        self.espefuse_py(cmd)
        output = self.espefuse_py("summary -d")
        if arg_chip in ["esp32", "esp32c2"]:
            assert (
                "[3 ] read_regs: e00007ff 00000000 00000000 00000000 "
                "00000000 00000000 00000000 00000000"
            ) in output
        else:
            assert (
                "[8 ] read_regs: e00007ff 00000000 00000000 00000000 "
                "00000000 00000000 00000000 00000000"
            ) in output

    def test_execute_scripts_nesting(self):
        os.chdir(TEST_DIR)
        if arg_chip in ["esp32", "esp32c2"]:
            cmd = f"execute_scripts {EFUSE_S_DIR}/efuse_burn2.py --index 28 \
            --configfiles {EFUSE_S_DIR}/esp32/config2.json"
        else:
            cmd = f"execute_scripts {EFUSE_S_DIR}/efuse_burn2.py --index 28 \
            --configfiles {EFUSE_S_DIR}/esp32xx/config2.json"
        self.espefuse_py(cmd)
        output = self.espefuse_py("summary -d")
        if arg_chip in ["esp32", "esp32c2"]:
            assert (
                "[2 ] read_regs: 10000000 00000000 00000000 00000000 "
                "00000000 00000000 00000000 00000000"
            ) in output
            assert (
                "[3 ] read_regs: ffffffff 00000000 00000000 00000000 "
                "00000000 00000000 00000000 00000000"
            ) in output
        else:
            assert (
                "[7 ] read_regs: 10000000 00000000 00000000 00000000 "
                "00000000 00000000 00000000 00000000"
            ) in output
            assert (
                "[8 ] read_regs: ffffffff 00000000 00000000 00000000 "
                "00000000 00000000 00000000 00000000"
            ) in output


class TestMultipleCommands(EfuseTestCase):
    def test_multiple_cmds_help(self):
        if arg_chip == "esp32c2":
            command1 = (
                f"burn_key_digest {S_IMAGES_DIR}"
                "/ecdsa256_secure_boot_signing_key_v2.pem"
            )
            command2 = (
                f"burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit_key "
                "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS"
            )
        elif arg_chip == "esp32":
            command1 = f"burn_key_digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem"
            command2 = f"burn_key flash_encryption {IMAGES_DIR}/256bit"
        else:
            command1 = f"burn_key_digest BLOCK_KEY0 \
            {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem SECURE_BOOT_DIGEST0"
            command2 = f"burn_key BLOCK_KEY0 \
            {S_IMAGES_DIR}/rsa_public_key_digest.bin SECURE_BOOT_DIGEST0"

        self.espefuse_py(
            f"-h {command1} {command2}",
            check_msg="usage: __main__.py [-h]",
        )

        self.espefuse_py(
            f"{command1} -h {command2}",
            check_msg="usage: __main__.py burn_key_digest [-h]",
        )

        self.espefuse_py(
            f"{command1} {command2} -h",
            check_msg="usage: __main__.py burn_key [-h]",
        )

    @pytest.mark.skipif(
        arg_chip != "esp32c2", reason="For this chip, FE and SB keys go into one BLOCK"
    )
    def test_1_esp32c2(self):
        self.espefuse_py(
            f"burn_key_digest {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem \
            burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit_key \
            XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS --no-read-protect \
            summary"
        )
        output = self.espefuse_py("summary -d")
        assert (
            "[3 ] read_regs: 0c0d0e0f 08090a0b 04050607 00010203 "
            "f66a0fbf 8b6dd38b a9dab353 040af633"
        ) in output
        assert " = 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00 R/-" in output
        assert " = bf 0f 6a f6 8b d3 6d 8b 53 b3 da a9 33 f6 0a 04 R/-" in output

    @pytest.mark.skipif(
        arg_chip != "esp32c2", reason="For this chip, FE and SB keys go into one BLOCK"
    )
    def test_2_esp32c2(self):
        self.espefuse_py(
            f"burn_key_digest {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem \
            burn_key BLOCK_KEY0 \
            {IMAGES_DIR}/128bit_key XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS \
            summary"
        )
        output = self.espefuse_py("summary -d")
        assert (
            "[3 ] read_regs: 00000000 00000000 00000000 00000000 "
            "f66a0fbf 8b6dd38b a9dab353 040af633"
        ) in output
        assert " = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/-" in output
        assert " = bf 0f 6a f6 8b d3 6d 8b 53 b3 da a9 33 f6 0a 04 R/-" in output

    def test_burn_bit(self):
        if arg_chip == "esp32":
            self._set_34_coding_scheme()
        self.espefuse_py(
            "burn_bit BLOCK2 0 1 2 3 \
            burn_bit BLOCK2 4 5 6 7 \
            burn_bit BLOCK2 8 9 10 11 \
            burn_bit BLOCK2 12 13 14 15 \
            summary"
        )
        output = self.espefuse_py("summary -d")
        assert "[2 ] read_regs: 0000ffff 00000000" in output

    def test_not_burn_cmds(self):
        self.espefuse_py(
            "summary \
            dump \
            get_custom_mac \
            adc_info \
            check_error"
        )


@pytest.mark.skipif(
    arg_chip not in ["esp32c3", "esp32c6", "esp32h2", "esp32s3"],
    reason="These chips have a hardware bug that limits the use of the KEY5",
)
class TestKeyPurposes(EfuseTestCase):
    def test_burn_xts_aes_key_purpose(self):
        self.espefuse_py(
            "burn_efuse KEY_PURPOSE_5 XTS_AES_128_KEY",
            check_msg="A fatal error occurred: "
            "KEY_PURPOSE_5 can not have XTS_AES_128_KEY "
            "key due to a hardware bug (please see TRM for more details)",
            ret_code=2,
        )

    @pytest.mark.skipif(
        arg_chip != "esp32h2", reason="esp32h2 can not have ECDSA key in KEY5"
    )
    def test_burn_ecdsa_key_purpose(self):
        self.espefuse_py(
            "burn_efuse KEY_PURPOSE_5 ECDSA_KEY",
            check_msg="A fatal error occurred: "
            "KEY_PURPOSE_5 can not have ECDSA_KEY "
            "key due to a hardware bug (please see TRM for more details)",
            ret_code=2,
        )

    def test_burn_xts_aes_key(self):
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY5 {IMAGES_DIR}/256bit XTS_AES_128_KEY",
            check_msg="A fatal error occurred: "
            "KEY_PURPOSE_5 can not have XTS_AES_128_KEY "
            "key due to a hardware bug (please see TRM for more details)",
            ret_code=2,
        )

    @pytest.mark.skipif(
        arg_chip != "esp32h2", reason="esp32h2 can not have ECDSA key in KEY5"
    )
    def test_burn_ecdsa_key(self):
        self.espefuse_py(
            f"burn_key \
            BLOCK_KEY5 {S_IMAGES_DIR}/ecdsa192_secure_boot_signing_key_v2.pem \
            ECDSA_KEY",
            check_msg="A fatal error occurred: "
            "KEY_PURPOSE_5 can not have ECDSA_KEY "
            "key due to a hardware bug (please see TRM for more details)",
            ret_code=2,
        )


class TestPostponedEfuses(EfuseTestCase):
    def test_postpone_efuses(self):
        if arg_chip == "esp32":
            cmd = f"--postpone \
                    burn_efuse UART_DOWNLOAD_DIS 1 \
                    burn_key BLOCK1 {IMAGES_DIR}/256bit \
                    burn_efuse ABS_DONE_1 1 FLASH_CRYPT_CNT 1"
            num = 1
        else:
            sb_digest_name = (
                "SECURE_BOOT_DIGEST" if arg_chip == "esp32c2" else "SECURE_BOOT_DIGEST0"
            )
            cmd = f"--postpone \
                burn_efuse ENABLE_SECURITY_DOWNLOAD 1 DIS_DOWNLOAD_MODE 1 \
                SECURE_VERSION 1 \
                burn_key BLOCK_KEY0 {IMAGES_DIR}/256bit {sb_digest_name} \
                burn_efuse SPI_BOOT_CRYPT_CNT 1 SECURE_BOOT_EN 1"
            num = 3 if arg_chip == "esp32c2" else 4
        output = self.espefuse_py(cmd)
        assert f"BURN BLOCK{num}  - OK" in output
        assert "BURN BLOCK0  - OK" in output
        assert "Burn postponed efuses from BLOCK0" in output
        assert "BURN BLOCK0  - OK" in output
        assert "Successful" in output


class TestCSVEfuseTable(EfuseTestCase):
    def test_extend_efuse_table_with_csv_file(self):
        csv_file = f"{IMAGES_DIR}/esp_efuse_custom_table.csv"
        output = self.espefuse_py(f" --extend-efuse-table {csv_file} summary")
        assert "MODULE_VERSION (BLOCK3)" in output
        assert "DEVICE_ROLE (BLOCK3)" in output
        assert "SETTING_2 (BLOCK3)" in output
        assert "ID_NUM_0 (BLOCK3)" in output
        assert "ID_NUM_1 (BLOCK3)" in output
        assert "ID_NUM_2 (BLOCK3)" in output
        assert "CUSTOM_SECURE_VERSION (BLOCK3)" in output
        assert "ID_NUMK_0 (BLOCK3)" in output
        assert "ID_NUMK_1 (BLOCK3)" in output

        self.espefuse_py(
            f"--extend-efuse-table {csv_file} burn_efuse \
                         MODULE_VERSION 1 \
                         CUSTOM_SECURE_VERSION 4 \
                         SETTING_1_ALT_NAME 7 \
                         SETTING_2 1 \
                         ID_NUM_0 1 \
                         ID_NUM_1 1 \
                         ID_NUM_2 1 \
                         MY_ID_NUMK_0 1 \
                         MY_ID_NUMK_1 1 \
                         MY_DATA_FIELD1 1"
        )
