forked from espressif/esp-idf
feat(security): update idf.py extensions to support security feature application
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@@ -290,7 +290,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
|||||||
format: str,
|
format: str,
|
||||||
md5_disable: str,
|
md5_disable: str,
|
||||||
flash_offset: str,
|
flash_offset: str,
|
||||||
fill_flash_size: str) -> None:
|
fill_flash_size: str,
|
||||||
|
merge_args: str) -> None:
|
||||||
ensure_build_directory(args, ctx.info_name)
|
ensure_build_directory(args, ctx.info_name)
|
||||||
project_desc = _get_project_desc(ctx, args)
|
project_desc = _get_project_desc(ctx, args)
|
||||||
merge_bin_args = [PYTHON, '-m', 'esptool']
|
merge_bin_args = [PYTHON, '-m', 'esptool']
|
||||||
@@ -320,7 +321,10 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
|||||||
yellow_print('idf.py merge-bin: --fill-flash-size is only valid for RAW format, option will be ignored.')
|
yellow_print('idf.py merge-bin: --fill-flash-size is only valid for RAW format, option will be ignored.')
|
||||||
else:
|
else:
|
||||||
merge_bin_args += ['--fill-flash-size', fill_flash_size]
|
merge_bin_args += ['--fill-flash-size', fill_flash_size]
|
||||||
merge_bin_args += ['@flash_args']
|
if not merge_args:
|
||||||
|
merge_bin_args += ['@flash_args']
|
||||||
|
else:
|
||||||
|
merge_bin_args += ['@{}'.format(merge_args)]
|
||||||
print(f'Merged binary {output} will be created in the build directory...')
|
print(f'Merged binary {output} will be created in the build directory...')
|
||||||
RunTool('merge_bin', merge_bin_args, args.build_dir, build_dir=args.build_dir, hints=not args.no_hints)()
|
RunTool('merge_bin', merge_bin_args, args.build_dir, build_dir=args.build_dir, hints=not args.no_hints)()
|
||||||
|
|
||||||
@@ -424,6 +428,15 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
|||||||
generate_signing_key_args += [extra_args['keyfile']]
|
generate_signing_key_args += [extra_args['keyfile']]
|
||||||
RunTool('espsecure', generate_signing_key_args, args.project_dir)()
|
RunTool('espsecure', generate_signing_key_args, args.project_dir)()
|
||||||
|
|
||||||
|
def secure_generate_key_digest(action: str, ctx: click.core.Context, args: PropertyDict, keyfile:str, output:str, **extra_args: str) -> None:
|
||||||
|
ensure_build_directory(args, ctx.info_name)
|
||||||
|
generate_key_digest_args = [PYTHON, '-m', 'espsecure', 'digest_sbv2_public_key']
|
||||||
|
if keyfile:
|
||||||
|
generate_key_digest_args += ['--keyfile', keyfile]
|
||||||
|
if output:
|
||||||
|
generate_key_digest_args += ['--output', output]
|
||||||
|
RunTool('espsecure', generate_key_digest_args, args.project_dir)()
|
||||||
|
|
||||||
def secure_sign_data(action: str,
|
def secure_sign_data(action: str,
|
||||||
ctx: click.core.Context,
|
ctx: click.core.Context,
|
||||||
args: PropertyDict,
|
args: PropertyDict,
|
||||||
@@ -468,6 +481,35 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
|||||||
verify_signature_args += [extra_args['datafile']]
|
verify_signature_args += [extra_args['datafile']]
|
||||||
RunTool('espsecure', verify_signature_args, args.build_dir)()
|
RunTool('espsecure', verify_signature_args, args.build_dir)()
|
||||||
|
|
||||||
|
def secure_generate_nvs_partition_key(action: str,
|
||||||
|
ctx: click.core.Context,
|
||||||
|
args: PropertyDict,
|
||||||
|
encryption_scheme: str,
|
||||||
|
keyfile: str,
|
||||||
|
hmac_keyfile: str,
|
||||||
|
**extra_args: str) -> None:
|
||||||
|
ensure_build_directory(args, ctx.info_name)
|
||||||
|
generate_nvs_partition_key_args = [PYTHON, '-m', 'esp_idf_nvs_partition_gen', 'generate-key']
|
||||||
|
if encryption_scheme == 'HMAC':
|
||||||
|
generate_nvs_partition_key_args += ['--key_protect_hmac']
|
||||||
|
generate_nvs_partition_key_args += ['--kp_hmac_keygen']
|
||||||
|
generate_nvs_partition_key_args += ['--kp_hmac_keyfile', hmac_keyfile]
|
||||||
|
generate_nvs_partition_key_args += ['--keyfile', keyfile]
|
||||||
|
|
||||||
|
RunTool('espsecure', generate_nvs_partition_key_args, args.project_dir)()
|
||||||
|
|
||||||
|
def secure_encrypt_nvs_partition(action: str, ctx: click.core.Context, args: PropertyDict, keyfile: str, **extra_args: str) -> None:
|
||||||
|
ensure_build_directory(args, ctx.info_name)
|
||||||
|
encrypt_nvs_partition_args = [PYTHON, '-m', 'esp_idf_nvs_partition_gen', 'encrypt']
|
||||||
|
encrypt_nvs_partition_args += ['--inputkey', keyfile]
|
||||||
|
if extra_args['input_file']:
|
||||||
|
encrypt_nvs_partition_args += [extra_args['input_file']]
|
||||||
|
if extra_args['output_file']:
|
||||||
|
encrypt_nvs_partition_args += [extra_args['output_file']]
|
||||||
|
if extra_args['partition_size']:
|
||||||
|
encrypt_nvs_partition_args += [extra_args['partition_size']]
|
||||||
|
RunTool('espsecure', encrypt_nvs_partition_args, args.project_dir)()
|
||||||
|
|
||||||
def _parse_efuse_args(ctx: click.core.Context, args: PropertyDict, extra_args: Dict) -> List:
|
def _parse_efuse_args(ctx: click.core.Context, args: PropertyDict, extra_args: Dict) -> List:
|
||||||
efuse_args = []
|
efuse_args = []
|
||||||
if args.port:
|
if args.port:
|
||||||
@@ -639,6 +681,13 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
|||||||
'help': ('[ONLY RAW] If set, the final binary file will be padded with FF bytes up to this flash size.'),
|
'help': ('[ONLY RAW] If set, the final binary file will be padded with FF bytes up to this flash size.'),
|
||||||
'type': click.Choice(['256KB', '512KB', '1MB', '2MB', '4MB', '8MB', '16MB', '32MB', '64MB', '128MB']),
|
'type': click.Choice(['256KB', '512KB', '1MB', '2MB', '4MB', '8MB', '16MB', '32MB', '64MB', '128MB']),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'names': ['--merge-args'],
|
||||||
|
'help': (
|
||||||
|
'Filepath to specify which binaries should be merged with their respective addresses. '
|
||||||
|
'The file format should be the same as flash_args.'
|
||||||
|
),
|
||||||
|
}
|
||||||
],
|
],
|
||||||
'dependencies': ['all'], # all = build
|
'dependencies': ['all'], # all = build
|
||||||
},
|
},
|
||||||
@@ -781,6 +830,20 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
'secure-generate-key-digest': {
|
||||||
|
'callback': secure_generate_key_digest,
|
||||||
|
'help': ('Generate a digest of a puiblic key file for use with secure boot.'),
|
||||||
|
'options': [
|
||||||
|
{
|
||||||
|
'names': ['--keyfile', '-k'],
|
||||||
|
'help': ('Public key file for digest generation.'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'names': ['--output', '-o'],
|
||||||
|
'help': ('Output file for key digest.'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
'secure-sign-data': {
|
'secure-sign-data': {
|
||||||
'callback': secure_sign_data,
|
'callback': secure_sign_data,
|
||||||
'help': ('Sign a data file for use with secure boot. Signing algorithm is deterministic ECDSA w/ SHA-512 (V1) or either RSA-'
|
'help': ('Sign a data file for use with secure boot. Signing algorithm is deterministic ECDSA w/ SHA-512 (V1) or either RSA-'
|
||||||
@@ -851,6 +914,51 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
'secure-generate-nvs-partition-key': {
|
||||||
|
'callback': secure_generate_nvs_partition_key,
|
||||||
|
'help': 'Generate a key for NVS partition encryption.',
|
||||||
|
'options': [
|
||||||
|
{
|
||||||
|
'names': ['--keyfile', '-k'],
|
||||||
|
'help': 'File to store the generated key.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'names': ['--encryption-scheme', '-s'],
|
||||||
|
'help': 'Encryption scheme to use.',
|
||||||
|
'type': click.Choice(['HMAC', 'Flash']),
|
||||||
|
'default': 'HMAC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'names': ['--hmac-keyfile', '-l'],
|
||||||
|
'help': 'File to store the generated HMAC key.',
|
||||||
|
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'secure-encrypt-nvs-partition': {
|
||||||
|
'callback': secure_encrypt_nvs_partition,
|
||||||
|
'help': 'Encrypt the NVS partition.',
|
||||||
|
'options': [
|
||||||
|
{
|
||||||
|
'names': ['--keyfile', '-k'],
|
||||||
|
'help': 'File with NVS partition key.',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'arguments': [
|
||||||
|
{
|
||||||
|
'names': ['input_file'],
|
||||||
|
'nargs': 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'names': ['output_file'],
|
||||||
|
'nargs': 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'names': ['partition_size'],
|
||||||
|
'nargs': 1,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
'efuse-burn': {
|
'efuse-burn': {
|
||||||
'callback': efuse_burn,
|
'callback': efuse_burn,
|
||||||
'help': 'Burn the eFuse with the specified name.',
|
'help': 'Burn the eFuse with the specified name.',
|
||||||
@@ -878,8 +986,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
|||||||
'names': ['--force-write-always'],
|
'names': ['--force-write-always'],
|
||||||
'is_flag': True,
|
'is_flag': True,
|
||||||
'help': (
|
'help': (
|
||||||
"Write the eFuse even if it looks like it's already been written, or is write protected."
|
'Write the eFuse even if it looks like it\'s already been written, or is write protected.'
|
||||||
"Note that this option can't disable write protection, or clear any bit which has already been set."
|
'Note that this option can\'t disable write protection, or clear any bit which has already been set.'
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -977,7 +1085,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
|||||||
'Baud rate for monitor. '
|
'Baud rate for monitor. '
|
||||||
'If this option is not provided IDF_MONITOR_BAUD and MONITORBAUD '
|
'If this option is not provided IDF_MONITOR_BAUD and MONITORBAUD '
|
||||||
'environment variables, global baud rate and project_description.json in build directory '
|
'environment variables, global baud rate and project_description.json in build directory '
|
||||||
"(generated by CMake from project's sdkconfig) "
|
'(generated by CMake from project\'s sdkconfig) '
|
||||||
'will be checked for default value.'
|
'will be checked for default value.'
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
#
|
#
|
||||||
# SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@@ -409,6 +409,7 @@ class TestSecureCommands(TestWrapperCommands):
|
|||||||
subprocess.run([sys.executable, idf_py_path, 'build'], stdout=subprocess.DEVNULL)
|
subprocess.run([sys.executable, idf_py_path, 'build'], stdout=subprocess.DEVNULL)
|
||||||
cls.flash_encryption_key = 'test_key.bin'
|
cls.flash_encryption_key = 'test_key.bin'
|
||||||
cls.signing_key = 'test_signing_key.pem'
|
cls.signing_key = 'test_signing_key.pem'
|
||||||
|
cls.nvs_partition_key = 'nvs_partition_key.bin'
|
||||||
|
|
||||||
def secure_generate_flash_encryption_key(self):
|
def secure_generate_flash_encryption_key(self):
|
||||||
generate_key_command = [sys.executable, idf_py_path, 'secure-generate-flash-encryption-key', self.flash_encryption_key]
|
generate_key_command = [sys.executable, idf_py_path, 'secure-generate-flash-encryption-key', self.flash_encryption_key]
|
||||||
@@ -449,19 +450,6 @@ class TestSecureCommands(TestWrapperCommands):
|
|||||||
self.assertIn('Using 256-bit key', output)
|
self.assertIn('Using 256-bit key', output)
|
||||||
self.assertIn('Done', output)
|
self.assertIn('Done', output)
|
||||||
|
|
||||||
def secure_generate_signing_key(self):
|
|
||||||
generate_key_command = [sys.executable,
|
|
||||||
idf_py_path,
|
|
||||||
'secure-generate-signing-key',
|
|
||||||
'--version',
|
|
||||||
'2',
|
|
||||||
'--scheme',
|
|
||||||
'rsa3072',
|
|
||||||
self.signing_key]
|
|
||||||
output = self.call_command(generate_key_command)
|
|
||||||
self.assertIn(f'RSA 3072 private key in PEM format written to {self.signing_key}', output)
|
|
||||||
self.assertIn('Done', output)
|
|
||||||
|
|
||||||
def secure_sign_data(self):
|
def secure_sign_data(self):
|
||||||
self.secure_generate_signing_key()
|
self.secure_generate_signing_key()
|
||||||
sign_command = [sys.executable,
|
sign_command = [sys.executable,
|
||||||
@@ -490,6 +478,43 @@ class TestSecureCommands(TestWrapperCommands):
|
|||||||
output = self.call_command(sign_command)
|
output = self.call_command(sign_command)
|
||||||
self.assertIn('verification successful', output)
|
self.assertIn('verification successful', output)
|
||||||
|
|
||||||
|
def secure_generate_signing_key(self):
|
||||||
|
generate_key_command = [sys.executable,
|
||||||
|
idf_py_path,
|
||||||
|
'secure-generate-signing-key',
|
||||||
|
'--version',
|
||||||
|
'2',
|
||||||
|
'--scheme',
|
||||||
|
'rsa3072',
|
||||||
|
self.signing_key]
|
||||||
|
output = self.call_command(generate_key_command)
|
||||||
|
self.assertIn(f'RSA 3072 private key in PEM format written to {self.signing_key}', output)
|
||||||
|
|
||||||
|
def test_secure_generate_key_digest(self):
|
||||||
|
self.secure_generate_signing_key()
|
||||||
|
digest_command = [sys.executable,
|
||||||
|
idf_py_path,
|
||||||
|
'secure-generate-key-digest',
|
||||||
|
'--keyfile',
|
||||||
|
f'{self.signing_key}',
|
||||||
|
'--output',
|
||||||
|
'key_digest.bin']
|
||||||
|
output = self.call_command(digest_command)
|
||||||
|
self.assertIn(f'Writing the public key digest of {self.signing_key} to key_digest.bin', output)
|
||||||
|
|
||||||
|
def test_secure_generate_nvs_partition_key(self):
|
||||||
|
generate_key_command = [sys.executable,
|
||||||
|
idf_py_path,
|
||||||
|
'secure-generate-nvs-partition-key',
|
||||||
|
'--keyfile',
|
||||||
|
f'{self.nvs_partition_key}',
|
||||||
|
'--encryption-scheme',
|
||||||
|
'HMAC',
|
||||||
|
'--hmac-keyfile',
|
||||||
|
'nvs_partition_key.bin']
|
||||||
|
output = self.call_command(generate_key_command)
|
||||||
|
self.assertIn('Created encryption keys:', output)
|
||||||
|
|
||||||
|
|
||||||
class TestMergeBinCommands(TestWrapperCommands):
|
class TestMergeBinCommands(TestWrapperCommands):
|
||||||
"""
|
"""
|
||||||
|
Reference in New Issue
Block a user