mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
Merge branch 'refactor/normalize_tee_pycli_script_output' into 'master'
refactor(esp_tee): Normalize the `esp_tee` python tools CLI output Closes IDF-15329 See merge request espressif/esp-idf!49274
This commit is contained in:
@@ -124,18 +124,26 @@ if(CONFIG_SECURE_ENABLE_TEE AND NOT esp_tee_build)
|
|||||||
list(APPEND exclude_srv "attestation")
|
list(APPEND exclude_srv "attestation")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# The parser uses OptionEatAll, so each flag consumes all following values and
|
||||||
|
# the CMake lists can be passed directly. --exclude behaves like nargs='+', so
|
||||||
|
# omit it entirely when empty (a bare --exclude would error / swallow --wrap).
|
||||||
|
set(exclude_args)
|
||||||
|
if(exclude_srv)
|
||||||
|
set(exclude_args "--exclude" ${exclude_srv})
|
||||||
|
endif()
|
||||||
|
|
||||||
idf_build_get_property(secure_service_headers_dir SECURE_SERVICE_HEADERS_DIR)
|
idf_build_get_property(secure_service_headers_dir SECURE_SERVICE_HEADERS_DIR)
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND python ${secure_service_yml_parser_py}
|
COMMAND python ${secure_service_yml_parser_py}
|
||||||
"--sec_srv" ${secure_service_yml}
|
"--sec_srv" ${secure_service_yml}
|
||||||
"--exclude" ${exclude_srv}
|
${exclude_args}
|
||||||
WORKING_DIRECTORY ${secure_service_headers_dir}
|
WORKING_DIRECTORY ${secure_service_headers_dir}
|
||||||
)
|
)
|
||||||
|
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND python ${secure_service_yml_parser_py}
|
COMMAND python ${secure_service_yml_parser_py}
|
||||||
"--sec_srv" ${secure_service_yml}
|
"--sec_srv" ${secure_service_yml}
|
||||||
"--exclude" ${exclude_srv} "--wrap"
|
${exclude_args} "--wrap"
|
||||||
OUTPUT_VARIABLE wrap_list
|
OUTPUT_VARIABLE wrap_list
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
|||||||
@@ -7,17 +7,18 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
$ python esp_tee_sec_stg_keygen.py --help
|
$ python esp_tee_sec_stg_keygen.py --help
|
||||||
usage: esp_tee_sec_stg_keygen.py [-h] -k {aes256,ecdsa_p256,ecdsa_p384} -o OUTPUT [-i INPUT] [--write-once]
|
|
||||||
|
|
||||||
Generate or import a cryptographic key structure for secure storage
|
Usage: esp_tee_sec_stg_keygen.py [OPTIONS]
|
||||||
|
|
||||||
options:
|
Generate or import a cryptographic key structure for secure storage.
|
||||||
-h, --help show this help message and exit
|
|
||||||
-k, --key-type {aes256,ecdsa_p256,ecdsa_p384}
|
Options:
|
||||||
key type to be processed
|
-k, --key-type [aes256|ecdsa_p256|ecdsa_p384]
|
||||||
-o, --output OUTPUT output binary file name
|
key type to be processed [required]
|
||||||
-i, --input INPUT input key file (.pem for ecdsa, .bin for aes)
|
-o, --output TEXT output binary file name [required]
|
||||||
|
-i, --input TEXT input key file (.pem for ecdsa, .bin for aes)
|
||||||
--write-once make key persistent - cannot be modified or deleted once written
|
--write-once make key persistent - cannot be modified or deleted once written
|
||||||
|
-h, --help Show this message and exit.
|
||||||
```
|
```
|
||||||
|
|
||||||
### ECDSA Keys
|
### ECDSA Keys
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
# SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from enum import IntFlag
|
from enum import IntFlag
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
import rich_click as click
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
from cryptography.hazmat.primitives import serialization
|
from cryptography.hazmat.primitives import serialization
|
||||||
from cryptography.hazmat.primitives.asymmetric import ec
|
from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
|
from esp_pylib.logger import log
|
||||||
|
|
||||||
# === Constants ===
|
# === Constants ===
|
||||||
SEC_STG_KEY_DATA_SZ = 256
|
SEC_STG_KEY_DATA_SZ = 256
|
||||||
@@ -86,56 +86,63 @@ def generate_key_data(key_type: KeyType, flags: Flags, input_file: str | None) -
|
|||||||
# === CLI ===
|
# === CLI ===
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
@click.command(context_settings=dict(help_option_names=['-h', '--help']))
|
||||||
parser = argparse.ArgumentParser(description='Generate or import a cryptographic key structure for secure storage')
|
@click.option(
|
||||||
parser.add_argument(
|
'-k',
|
||||||
'-k',
|
'--key-type',
|
||||||
'--key-type',
|
'key_type',
|
||||||
type=str,
|
type=click.Choice([e.name.lower() for e in KeyType], case_sensitive=False),
|
||||||
choices=[e.name.lower() for e in KeyType],
|
required=True,
|
||||||
required=True,
|
help='key type to be processed',
|
||||||
help='key type to be processed',
|
)
|
||||||
)
|
@click.option(
|
||||||
parser.add_argument(
|
'-o',
|
||||||
'-o',
|
'--output',
|
||||||
'--output',
|
required=True,
|
||||||
required=True,
|
help='output binary file name',
|
||||||
help='output binary file name',
|
)
|
||||||
)
|
@click.option(
|
||||||
parser.add_argument(
|
'-i',
|
||||||
'-i',
|
'--input',
|
||||||
'--input',
|
'input_file',
|
||||||
help='input key file (.pem for ecdsa, .bin for aes)',
|
default=None,
|
||||||
)
|
help='input key file (.pem for ecdsa, .bin for aes)',
|
||||||
parser.add_argument(
|
)
|
||||||
'--write-once',
|
@click.option(
|
||||||
action='store_true',
|
'--write-once',
|
||||||
help='make key persistent - cannot be modified or deleted once written',
|
is_flag=True,
|
||||||
)
|
default=False,
|
||||||
return parser.parse_args()
|
help='make key persistent - cannot be modified or deleted once written',
|
||||||
|
)
|
||||||
|
def main(key_type: str, output: str, input_file: str | None, write_once: bool) -> None:
|
||||||
def main() -> None:
|
"""Generate or import a cryptographic key structure for secure storage."""
|
||||||
args: Any = parse_args()
|
selected_type = KeyType[key_type.upper()]
|
||||||
|
|
||||||
key_type = KeyType[args.key_type.upper()]
|
|
||||||
flags = Flags.NONE
|
flags = Flags.NONE
|
||||||
if args.write_once:
|
if write_once:
|
||||||
flags |= Flags.WRITE_ONCE
|
flags |= Flags.WRITE_ONCE
|
||||||
|
|
||||||
print(f'[+] Generating key of type: {key_type.name} (value: {key_type.value})')
|
log.print(
|
||||||
if args.input:
|
f'[+] Generating key of type: {selected_type.name} (value: {selected_type.value})',
|
||||||
print(f'[+] Using user-provided key file: {args.input}')
|
markup=False,
|
||||||
if args.write_once:
|
soft_wrap=True,
|
||||||
print('[+] WRITE_ONCE flag is set')
|
)
|
||||||
|
if input_file:
|
||||||
|
log.print(f'[+] Using user-provided key file: {input_file}', markup=False, soft_wrap=True)
|
||||||
|
if write_once:
|
||||||
|
log.print('[+] WRITE_ONCE flag is set', markup=False, soft_wrap=True)
|
||||||
|
|
||||||
key_data = generate_key_data(key_type, flags, args.input)
|
try:
|
||||||
|
key_data = generate_key_data(selected_type, flags, input_file)
|
||||||
|
with open(output, 'wb') as f:
|
||||||
|
f.write(key_data)
|
||||||
|
except (ValueError, OSError) as e:
|
||||||
|
raise click.ClickException(str(e))
|
||||||
|
|
||||||
with open(args.output, 'wb') as f:
|
log.print(f'[✓] Key written to {output}', markup=False, soft_wrap=True)
|
||||||
f.write(key_data)
|
|
||||||
|
|
||||||
print(f'[✓] Key written to {args.output}')
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
from esp_pylib.excepthook import install_exception_reporting
|
||||||
|
|
||||||
|
install_exception_reporting()
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
import argparse
|
|
||||||
from typing import List
|
|
||||||
from typing import Set
|
|
||||||
from typing import Tuple
|
|
||||||
|
|
||||||
|
import rich_click as click
|
||||||
import yaml
|
import yaml
|
||||||
|
from esp_pylib.cli_options import OptionEatAll
|
||||||
|
|
||||||
SEC_SRV_TABLE_SPLIT_ID = 30
|
SEC_SRV_TABLE_SPLIT_ID = 30
|
||||||
OUTPUT_HEADERS = [
|
OUTPUT_HEADERS = [
|
||||||
@@ -16,11 +14,11 @@ OUTPUT_HEADERS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def parse_services(yml_files: List[str], excluded_fam: Set[str]) -> List[Tuple[int, str, int]]:
|
def parse_services(yml_files: list[str], excluded_fam: set[str]) -> list[tuple[int, str, int]]:
|
||||||
services, service_ids = [], set()
|
services, service_ids = [], set()
|
||||||
|
|
||||||
for yml_file in yml_files:
|
for yml_file in yml_files:
|
||||||
with open(yml_file, 'r') as f:
|
with open(yml_file) as f:
|
||||||
data = yaml.safe_load(f)
|
data = yaml.safe_load(f)
|
||||||
for family in data.get('secure_services', []):
|
for family in data.get('secure_services', []):
|
||||||
family_name = family.get('family', '')
|
family_name = family.get('family', '')
|
||||||
@@ -36,8 +34,8 @@ def parse_services(yml_files: List[str], excluded_fam: Set[str]) -> List[Tuple[i
|
|||||||
return sorted(services, key=lambda x: x[0])
|
return sorted(services, key=lambda x: x[0])
|
||||||
|
|
||||||
|
|
||||||
def generate_num_header(services: List[Tuple[int, str, int]], output_file: str) -> None:
|
def generate_num_header(services: list[tuple[int, str, int]], output_file: str) -> None:
|
||||||
header = '''/**
|
header = """/**
|
||||||
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
|
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -47,21 +45,21 @@ def generate_num_header(services: List[Tuple[int, str, int]], output_file: str)
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
'''
|
"""
|
||||||
body = '\n'.join(f'#define SS_{name.upper()}\t{nr}' for nr, name, _ in services)
|
body = '\n'.join(f'#define SS_{name.upper()}\t{nr}' for nr, name, _ in services)
|
||||||
footer = f'\n#define MAX_SECURE_SERVICES_ID\t{services[-1][0] + 1 if services else 0}\n'
|
footer = f'\n#define MAX_SECURE_SERVICES_ID\t{services[-1][0] + 1 if services else 0}\n'
|
||||||
footer += f'#define SECURE_SERVICES_NUM\t{len(services)}\n\n'
|
footer += f'#define SECURE_SERVICES_NUM\t{len(services)}\n\n'
|
||||||
footer += f'#define SECURE_SERVICES_SPLIT_ID\t{SEC_SRV_TABLE_SPLIT_ID}\n\n'
|
footer += f'#define SECURE_SERVICES_SPLIT_ID\t{SEC_SRV_TABLE_SPLIT_ID}\n\n'
|
||||||
footer += '''typedef void (*secure_service_t)(void);
|
footer += """typedef void (*secure_service_t)(void);
|
||||||
typedef struct { secure_service_t func; int nargs; } secure_service_entry_t;
|
typedef struct { secure_service_t func; int nargs; } secure_service_entry_t;
|
||||||
'''
|
"""
|
||||||
footer += '\n#ifdef __cplusplus\n}\n#endif\n'
|
footer += '\n#ifdef __cplusplus\n}\n#endif\n'
|
||||||
with open(output_file, 'w') as f:
|
with open(output_file, 'w') as f:
|
||||||
f.write(header + body + footer)
|
f.write(header + body + footer)
|
||||||
|
|
||||||
|
|
||||||
def generate_dec_header(services: List[Tuple[int, str, int]], output_file: str) -> None:
|
def generate_dec_header(services: list[tuple[int, str, int]], output_file: str) -> None:
|
||||||
header = '''/**
|
header = """/**
|
||||||
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
|
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -70,20 +68,20 @@ def generate_dec_header(services: List[Tuple[int, str, int]], output_file: str)
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
'''
|
"""
|
||||||
body = '\n'.join(f'void _ss_{name}(void);' for _, name, _ in services)
|
body = '\n'.join(f'void _ss_{name}(void);' for _, name, _ in services)
|
||||||
footer = '\n#ifdef __cplusplus\n}\n#endif\n'
|
footer = '\n#ifdef __cplusplus\n}\n#endif\n'
|
||||||
with open(output_file, 'w') as f:
|
with open(output_file, 'w') as f:
|
||||||
f.write(header + body + footer)
|
f.write(header + body + footer)
|
||||||
|
|
||||||
|
|
||||||
def generate_table_split(services: List[Tuple[int, str, int]], output_file_1: str, output_file_2: str) -> None:
|
def generate_table_split(services: list[tuple[int, str, int]], output_file_1: str, output_file_2: str) -> None:
|
||||||
header = '''/**
|
header = """/**
|
||||||
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
|
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
'''
|
"""
|
||||||
services_1 = [service for service in services if service[0] < SEC_SRV_TABLE_SPLIT_ID]
|
services_1 = [service for service in services if service[0] < SEC_SRV_TABLE_SPLIT_ID]
|
||||||
services_2 = [service for service in services if service[0] >= SEC_SRV_TABLE_SPLIT_ID]
|
services_2 = [service for service in services if service[0] >= SEC_SRV_TABLE_SPLIT_ID]
|
||||||
|
|
||||||
@@ -95,10 +93,10 @@ def generate_table_split(services: List[Tuple[int, str, int]], output_file_1: st
|
|||||||
f2.write(header + body_2)
|
f2.write(header + body_2)
|
||||||
|
|
||||||
|
|
||||||
def generate_wrap_list(yml_files: List[str], excluded_fam: Set[str]) -> None:
|
def generate_wrap_list(yml_files: list[str], excluded_fam: set[str]) -> None:
|
||||||
wrap_list: list[str] = []
|
wrap_list: list[str] = []
|
||||||
for yml_file in yml_files:
|
for yml_file in yml_files:
|
||||||
with open(yml_file, 'r') as f:
|
with open(yml_file) as f:
|
||||||
data = yaml.safe_load(f)
|
data = yaml.safe_load(f)
|
||||||
wrap_list.extend(
|
wrap_list.extend(
|
||||||
f'-Wl,--wrap={entry["function"]}'
|
f'-Wl,--wrap={entry["function"]}'
|
||||||
@@ -109,23 +107,44 @@ def generate_wrap_list(yml_files: List[str], excluded_fam: Set[str]) -> None:
|
|||||||
print(' '.join(wrap_list), end='')
|
print(' '.join(wrap_list), end='')
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
@click.command(context_settings=dict(help_option_names=['-h', '--help']))
|
||||||
parser = argparse.ArgumentParser(description='Generate secure service outputs from YAML table')
|
@click.option('--wrap', is_flag=True, default=False, help='Generate linker wrap options')
|
||||||
parser.add_argument('--wrap', action='store_true', help='Generate linker wrap options')
|
@click.option(
|
||||||
parser.add_argument('-s', '--sec_srv', nargs='+', required=True, help='Secure service table(s) in YAML')
|
'-s',
|
||||||
parser.add_argument('--exclude', nargs='*', default=[], help='List of API families to exclude from the output')
|
'--sec_srv',
|
||||||
|
'sec_srv',
|
||||||
|
multiple=True,
|
||||||
|
required=True,
|
||||||
|
type=str,
|
||||||
|
cls=OptionEatAll,
|
||||||
|
help='Secure service table(s) in YAML (space- or flag-separated)',
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'--exclude',
|
||||||
|
multiple=True,
|
||||||
|
type=str,
|
||||||
|
cls=OptionEatAll,
|
||||||
|
help='API families to exclude from the output (space- or flag-separated)',
|
||||||
|
)
|
||||||
|
def main(wrap: bool, sec_srv: tuple[str, ...], exclude: tuple[str, ...]) -> None:
|
||||||
|
"""Generate secure service outputs from YAML table."""
|
||||||
|
yml_files = list(sec_srv)
|
||||||
|
excluded_fam = set(exclude)
|
||||||
|
|
||||||
args = parser.parse_args()
|
try:
|
||||||
excluded_fam = set(args.exclude)
|
if wrap:
|
||||||
|
generate_wrap_list(yml_files, excluded_fam)
|
||||||
if args.wrap:
|
else:
|
||||||
generate_wrap_list(args.sec_srv, excluded_fam)
|
services = parse_services(yml_files, excluded_fam)
|
||||||
else:
|
generate_num_header(services, OUTPUT_HEADERS[0])
|
||||||
services = parse_services(args.sec_srv, excluded_fam)
|
generate_dec_header(services, OUTPUT_HEADERS[1])
|
||||||
generate_num_header(services, OUTPUT_HEADERS[0])
|
generate_table_split(services, OUTPUT_HEADERS[2], OUTPUT_HEADERS[3])
|
||||||
generate_dec_header(services, OUTPUT_HEADERS[1])
|
except (ValueError, OSError, yaml.YAMLError) as e:
|
||||||
generate_table_split(services, OUTPUT_HEADERS[2], OUTPUT_HEADERS[3])
|
raise click.ClickException(str(e))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
from esp_pylib.excepthook import install_exception_reporting
|
||||||
|
|
||||||
|
install_exception_reporting()
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user