mirror of
https://github.com/espressif/esptool.git
synced 2025-10-19 02:43:00 +08:00
feat(espefuse): Adds support for burning 512-bit keys for C5
This commit is contained in:

committed by
Roland Dobai

parent
605c80b846
commit
468de5ce57
@@ -63,9 +63,9 @@ Optional arguments:
|
||||
|
||||
- USER.
|
||||
- RESERVED.
|
||||
:esp32s2 or esp32s3: - XTS_AES_256_KEY_1. The first 256 bits of 512bit flash encryption key.
|
||||
:esp32s2 or esp32s3: - XTS_AES_256_KEY_2. The second 256 bits of 512bit flash encryption key.
|
||||
:esp32h2: - ECDSA_KEY. It can be ECDSA private keys based on NIST192p or NIST256p curve. The private key is extracted from the given file and written into a eFuse block with write and read protection enabled. This private key shall be used by ECDSA accelerator for the signing purpose.
|
||||
:esp32c5 or esp32c61 or esp32p4 or esp32s2 or esp32s3: - XTS_AES_256_KEY_1. The first 256 bits of 512bit flash encryption key.
|
||||
:esp32c5 or esp32c61 or esp32p4 or esp32s2 or esp32s3: - XTS_AES_256_KEY_2. The second 256 bits of 512bit flash encryption key.
|
||||
::esp32c5 or esp32c61 or esp32h2 or esp32h21 or esp32h4 or esp32p4: - ECDSA_KEY. It can be ECDSA private keys based on NIST192p or NIST256p curve. The private key is extracted from the given file and written into a eFuse block with write and read protection enabled. This private key shall be used by ECDSA accelerator for the signing purpose.
|
||||
- XTS_AES_128_KEY. 256 bit flash encryption key.
|
||||
- HMAC_DOWN_ALL.
|
||||
- HMAC_DOWN_JTAG.
|
||||
@@ -74,7 +74,11 @@ Optional arguments:
|
||||
- SECURE_BOOT_DIGEST0. 1 secure boot key.
|
||||
- SECURE_BOOT_DIGEST1. 2 secure boot key.
|
||||
- SECURE_BOOT_DIGEST2. 3 secure boot key.
|
||||
:esp32s2 or esp32s3: - XTS_AES_256_KEY. This is a virtual key purpose for flash encryption key. This allows you to write a whole 512-bit key into two blocks with ``XTS_AES_256_KEY_1`` and ``XTS_AES_256_KEY_2`` purposes without splitting the key file.
|
||||
:esp32c5 or esp32c61 or esp32p4 or esp32s2 or esp32s3: - XTS_AES_256_KEY. This is a virtual key purpose for flash encryption key. This allows you to write a whole 512-bit key into two blocks with ``XTS_AES_256_KEY_1`` and ``XTS_AES_256_KEY_2`` purposes without splitting the key file.
|
||||
:esp32c5: - XTS_AES_256_PSRAM_KEY. This is a virtual key purpose for psram encryption key. This allows you to write a whole 512-bit key into two blocks with ``XTS_AES_256_PSRAM_KEY_1`` and ``XTS_AES_256_PSRAM_KEY_2`` purposes without splitting the key file.
|
||||
:esp32c5: - XTS_AES_256_PSRAM_KEY_1. The first 256 bits of 512bit psram encryption key.
|
||||
:esp32c5: - XTS_AES_256_PSRAM_KEY_2. The second 256 bits of 512bit psram encryption key.
|
||||
:esp32c5 or esp32h4 or esp32p4: - KM_INIT_KEY. This is a key that is used for the generation of AES/ECDSA keys by the key manager.
|
||||
|
||||
.. only:: esp32h2
|
||||
|
||||
|
@@ -491,13 +491,17 @@ class BaseCommands:
|
||||
block_names: list[str],
|
||||
datafiles: list[BinaryIO],
|
||||
keypurposes: list[str],
|
||||
base_keypurpose: str = "XTS_AES_256_KEY",
|
||||
) -> tuple[list[str], list[BinaryIO], list[str]]:
|
||||
"""Helper method to split 512-bit key into two 256-bit keys"""
|
||||
if base_keypurpose not in keypurposes:
|
||||
return block_names, datafiles, keypurposes
|
||||
|
||||
keypurpose_list = list(keypurposes)
|
||||
datafile_list = list(datafiles)
|
||||
block_name_list = list(block_names)
|
||||
|
||||
i = keypurpose_list.index("XTS_AES_256_KEY")
|
||||
i = keypurpose_list.index(base_keypurpose)
|
||||
block_name = block_name_list[i]
|
||||
|
||||
block_num = self.efuses.get_index_block_by_name(block_name)
|
||||
@@ -506,19 +510,19 @@ class BaseCommands:
|
||||
data = datafile_list[i].read()
|
||||
if len(data) != 64:
|
||||
raise esptool.FatalError(
|
||||
"Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes"
|
||||
% len(data)
|
||||
f"Incorrect key file size {len(data)}, {base_keypurpose} "
|
||||
"should be 64 bytes"
|
||||
)
|
||||
|
||||
key_block_2 = self._get_next_key_block(block, block_name_list)
|
||||
if not key_block_2:
|
||||
raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks")
|
||||
raise esptool.FatalError(f"{base_keypurpose} requires two free keyblocks")
|
||||
|
||||
keypurpose_list.append("XTS_AES_256_KEY_1")
|
||||
keypurpose_list.append(f"{base_keypurpose}_1")
|
||||
datafile_list.append(io.BytesIO(data[:32]))
|
||||
block_name_list.append(block_name)
|
||||
|
||||
keypurpose_list.append("XTS_AES_256_KEY_2")
|
||||
keypurpose_list.append(f"{base_keypurpose}_2")
|
||||
datafile_list.append(io.BytesIO(data[32:]))
|
||||
block_name_list.append(key_block_2.name)
|
||||
|
||||
|
@@ -451,7 +451,7 @@ class EfuseKeyPurposeField(EfuseField):
|
||||
("KM_INIT_KEY", 12, None, None, "need_rd_protect"), # init key that is used for the generation of AES/ECDSA key
|
||||
("XTS_AES_256_PSRAM_KEY_1", 13, None, "Reverse", "need_rd_protect"), # XTS_AES_256_PSRAM_KEY_1 (PSRAM encryption)
|
||||
("XTS_AES_256_PSRAM_KEY_2", 14, None, "Reverse", "need_rd_protect"), # XTS_AES_256_PSRAM_KEY_1 (PSRAM encryption)
|
||||
# ("XTS_AES_256_PSRAM_KEY", -2, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_PSRAM_KEY_1 and XTS_AES_256_PSRAM_KEY_1
|
||||
("XTS_AES_256_PSRAM_KEY", -2, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_PSRAM_KEY_1 and XTS_AES_256_PSRAM_KEY_1
|
||||
("XTS_AES_128_PSRAM_KEY", 15, None, "Reverse", "need_rd_protect"), # XTS_AES_128_PSRAM_KEY (PSRAM encryption)
|
||||
("ECDSA_KEY_P192", 16, None, "Reverse", "need_rd_protect"), # ECDSA key P192
|
||||
("ECDSA_KEY_P384_L", 17, None, "Reverse", "need_rd_protect"), # ECDSA key P384 low
|
||||
|
@@ -155,6 +155,15 @@ class ESP32C5Commands(BaseCommands):
|
||||
keypurpose_list,
|
||||
)
|
||||
|
||||
if "XTS_AES_256_PSRAM_KEY" in keypurpose_list:
|
||||
# XTS_AES_256_PSRAM_KEY -> XTS_AES_256_PSRAM_KEY_1 and XTS_AES_256_PSRAM_KEY_2
|
||||
block_name_list, datafile_list, keypurpose_list = self._split_512_bit_key(
|
||||
block_name_list,
|
||||
datafile_list, # type: ignore
|
||||
keypurpose_list,
|
||||
"XTS_AES_256_PSRAM_KEY",
|
||||
)
|
||||
|
||||
util.check_duplicate_name_in_list(block_name_list)
|
||||
if len(block_name_list) != len(datafile_list) or len(block_name_list) != len(
|
||||
keypurpose_list
|
||||
|
@@ -68,6 +68,35 @@ ESPEFUSE_MODNAME = "python -m espefuse"
|
||||
EMPTY_BLOCK = " ".join(["00"] * 32) # "00 00 ... 00"
|
||||
|
||||
|
||||
class Command:
|
||||
def __init__(self, chip: str, cmd: str):
|
||||
self.chip = chip
|
||||
self.cmd = cmd
|
||||
|
||||
def supports(self, search_item: str) -> bool:
|
||||
"""Check if the command output contains the given key."""
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"espefuse",
|
||||
"--virt",
|
||||
"--chip",
|
||||
self.chip,
|
||||
self.cmd,
|
||||
"--help",
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
)
|
||||
return search_item in result.stdout
|
||||
|
||||
def does_not_support(self, search_item: str) -> bool:
|
||||
"""Check if the command output does not contain the given key."""
|
||||
return not self.supports(search_item)
|
||||
|
||||
|
||||
@pytest.mark.host_test
|
||||
class EfuseTestCase:
|
||||
def setup_method(self):
|
||||
@@ -955,17 +984,7 @@ class TestBurnKeyCommands(EfuseTestCase):
|
||||
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",
|
||||
"esp32c3",
|
||||
"esp32c6",
|
||||
"esp32h2",
|
||||
"esp32p4",
|
||||
"esp32c5",
|
||||
"esp32c61",
|
||||
],
|
||||
Command(arg_chip, "burn-key").does_not_support("BLOCK_KEY5"),
|
||||
reason="Only chips with 6 keys",
|
||||
)
|
||||
def test_burn_key_with_6_keys(self):
|
||||
@@ -973,12 +992,7 @@ class TestBurnKeyCommands(EfuseTestCase):
|
||||
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",
|
||||
"esp32c5",
|
||||
]:
|
||||
if Command(arg_chip, "burn-key").does_not_support("XTS_AES_256_KEY"):
|
||||
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")
|
||||
@@ -1051,8 +1065,8 @@ class TestBurnKeyCommands(EfuseTestCase):
|
||||
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",
|
||||
Command(arg_chip, "burn-key").does_not_support("XTS_AES_256_KEY"),
|
||||
reason="512 bit keys not supported on this chip",
|
||||
)
|
||||
def test_burn_key_512bit(self):
|
||||
self.espefuse_py(
|
||||
@@ -1069,8 +1083,8 @@ class TestBurnKeyCommands(EfuseTestCase):
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip not in ["esp32s2", "esp32s3", "esp32p4", "esp32c61"],
|
||||
reason="512 bit keys are only supported on ESP32-S2, S3, P4, C61",
|
||||
Command(arg_chip, "burn-key").does_not_support("XTS_AES_256_KEY"),
|
||||
reason="512 bit keys not supported on this chip",
|
||||
)
|
||||
def test_burn_key_512bit_non_consecutive_blocks(self):
|
||||
# Burn efuses separately to test different kinds
|
||||
@@ -1112,8 +1126,8 @@ class TestBurnKeyCommands(EfuseTestCase):
|
||||
) 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",
|
||||
Command(arg_chip, "burn-key").does_not_support("XTS_AES_256_KEY"),
|
||||
reason="512 bit keys not supported on this chip",
|
||||
)
|
||||
def test_burn_key_512bit_non_consecutive_blocks_loop_around(self):
|
||||
self.espefuse_py(
|
||||
@@ -1145,8 +1159,39 @@ class TestBurnKeyCommands(EfuseTestCase):
|
||||
) in output
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip not in ["esp32h2", "esp32c5", "esp32c61", "esp32p4"],
|
||||
reason="These chips support ECDSA_KEY",
|
||||
Command(arg_chip, "burn-key").does_not_support("XTS_AES_256_PSRAM_KEY"),
|
||||
reason="512 bit keys not supported on this chip",
|
||||
)
|
||||
def test_burn_key_512bit_for_flash_and_psram(self):
|
||||
self.espefuse_py(
|
||||
f"burn-key \
|
||||
BLOCK_KEY0 {IMAGES_DIR}/256bit_1_256bit_2_combined \
|
||||
XTS_AES_256_KEY \
|
||||
BLOCK_KEY2 {IMAGES_DIR}/256bit_1_256bit_2_combined \
|
||||
XTS_AES_256_PSRAM_KEY --no-read-protect --no-write-protect"
|
||||
)
|
||||
output = self.espefuse_py("-d summary")
|
||||
assert (
|
||||
"[4 ] read_regs: bcbd11bf b8b9babb b4b5b6b7 b0b1b2b3 "
|
||||
"acadaeaf a8a9aaab a4a5a6a7 11a1a2a3"
|
||||
) in output
|
||||
assert (
|
||||
"[5 ] read_regs: bcbd22bf b8b9babb b4b5b6b7 b0b1b2b3 "
|
||||
"acadaeaf a8a9aaab a4a5a6a7 22a1a2a3"
|
||||
) in output
|
||||
|
||||
assert (
|
||||
"[6 ] 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(
|
||||
Command(arg_chip, "burn-key").does_not_support("ECDSA_KEY"),
|
||||
reason="ECDSA_KEY not supported on this chip",
|
||||
)
|
||||
def test_burn_key_ecdsa_key(self):
|
||||
self.espefuse_py(
|
||||
@@ -1171,8 +1216,8 @@ class TestBurnKeyCommands(EfuseTestCase):
|
||||
) in output
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip not in ["esp32h2", "esp32c5", "esp32c61", "esp32p4"],
|
||||
reason="These chips support ECDSA_KEY",
|
||||
Command(arg_chip, "burn-key").does_not_support("ECDSA_KEY"),
|
||||
reason="ECDSA_KEY not supported on this chip",
|
||||
)
|
||||
def test_burn_key_ecdsa_key_check_byte_order(self):
|
||||
self.espefuse_py(
|
||||
@@ -1265,18 +1310,8 @@ class TestBurnBlockDataCommands(EfuseTestCase):
|
||||
) in output
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip
|
||||
not in [
|
||||
"esp32s2",
|
||||
"esp32s3",
|
||||
"esp32c3",
|
||||
"esp32c6",
|
||||
"esp32h2",
|
||||
"esp32p4",
|
||||
"esp32c5",
|
||||
"esp32c61",
|
||||
],
|
||||
reason="Only chip with 6 keys",
|
||||
Command(arg_chip, "burn-key").does_not_support("BLOCK_KEY5"),
|
||||
reason="Only chips with 6 keys",
|
||||
)
|
||||
def test_burn_block_data_with_6_keys(self):
|
||||
self.espefuse_py(
|
||||
@@ -1405,17 +1440,7 @@ class TestBurnBlockDataCommands(EfuseTestCase):
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip
|
||||
not in [
|
||||
"esp32s2",
|
||||
"esp32s3",
|
||||
"esp32c3",
|
||||
"esp32c6",
|
||||
"esp32h2",
|
||||
"esp32p4",
|
||||
"esp32c5",
|
||||
"esp32c61",
|
||||
],
|
||||
Command(arg_chip, "burn-key").does_not_support("BLOCK_KEY5"),
|
||||
reason="Only chips with 6 keys",
|
||||
)
|
||||
def test_burn_block_data_with_offset_6_keys(self):
|
||||
@@ -1601,18 +1626,8 @@ class TestBurnKeyDigestCommandsEsp32C2(EfuseTestCase):
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip
|
||||
not in [
|
||||
"esp32s2",
|
||||
"esp32s3",
|
||||
"esp32c3",
|
||||
"esp32c6",
|
||||
"esp32h2",
|
||||
"esp32p4",
|
||||
"esp32c5",
|
||||
"esp32c61",
|
||||
],
|
||||
reason="Supports 6 key blocks",
|
||||
Command(arg_chip, "burn-key").does_not_support("BLOCK_KEY5"),
|
||||
reason="Only chips with 6 keys",
|
||||
)
|
||||
class TestBurnKeyDigestCommands(EfuseTestCase):
|
||||
def test_burn_key_digest(self):
|
||||
@@ -1712,18 +1727,8 @@ class TestBurnBitCommands(EfuseTestCase):
|
||||
self.espefuse_py("summary", check_msg="[0 ] read_regs: 00000007 00000000")
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip
|
||||
not in [
|
||||
"esp32s2",
|
||||
"esp32s3",
|
||||
"esp32c3",
|
||||
"esp32c6",
|
||||
"esp32h2",
|
||||
"esp32p4",
|
||||
"esp32c5",
|
||||
"esp32c61",
|
||||
],
|
||||
reason="Only chip with 6 keys",
|
||||
Command(arg_chip, "burn-key").does_not_support("BLOCK_KEY5"),
|
||||
reason="Only chips with 6 keys",
|
||||
)
|
||||
def test_burn_bit_for_chips_with_6_key_blocks(self):
|
||||
self.espefuse_py("burn-bit -h")
|
||||
@@ -2021,7 +2026,10 @@ class TestMultipleCommands(EfuseTestCase):
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip != "esp32c2", reason="For this chip, FE and SB keys go into one BLOCK"
|
||||
Command(arg_chip, "burn-key").does_not_support(
|
||||
"XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS"
|
||||
),
|
||||
reason="This chip does not support both FE and SB keys going into one BLOCK",
|
||||
)
|
||||
def test_1_esp32c2(self):
|
||||
self.espefuse_py(
|
||||
@@ -2039,7 +2047,10 @@ class TestMultipleCommands(EfuseTestCase):
|
||||
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"
|
||||
Command(arg_chip, "burn-key").does_not_support(
|
||||
"XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS"
|
||||
),
|
||||
reason="This chip does not support both FE and SB keys going into one BLOCK",
|
||||
)
|
||||
def test_2_esp32c2(self):
|
||||
self.espefuse_py(
|
||||
@@ -2102,8 +2113,9 @@ class TestMultipleCommands(EfuseTestCase):
|
||||
|
||||
|
||||
@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",
|
||||
Command(arg_chip, "burn-key").does_not_support("BLOCK_KEY5"),
|
||||
reason="This test is only relevant for chips affected by "
|
||||
"the BLOCK_KEY5 hardware bug",
|
||||
)
|
||||
class TestKeyPurposes(EfuseTestCase):
|
||||
def test_burn_xts_aes_key_purpose(self):
|
||||
|
Reference in New Issue
Block a user