Skip to content

Commit 3bb3f93

Browse files
committed
refactor(esp_tee): Update TEE secure storage examples and test-apps
1 parent 41bf07e commit 3bb3f93

File tree

21 files changed

+831
-299
lines changed

21 files changed

+831
-299
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# TEE Secure Storage: Image Generation
2+
3+
4+
## A. Generate Secure Key Blobs
5+
6+
### `esp_tee_sec_stg_keygen` tool
7+
8+
```
9+
$ python esp_tee_sec_stg_keygen.py --help
10+
usage: esp_tee_sec_stg_keygen.py [-h] -k {aes256,ecdsa_p256,ecdsa_p192} -o OUTPUT [-i INPUT] [--write-once]
11+
12+
Generate or import a cryptographic key structure for secure storage
13+
14+
options:
15+
-h, --help show this help message and exit
16+
-k, --key-type {aes256,ecdsa_p256,ecdsa_p192}
17+
key type to be processed
18+
-o, --output OUTPUT output binary file name
19+
-i, --input INPUT input key file (.pem for ecdsa, .bin for aes)
20+
--write-once make key persistent - cannot be modified or deleted once written
21+
```
22+
23+
### ECDSA Keys
24+
25+
```bash
26+
python esp_tee_sec_stg_keygen.py -k ecdsa_p256 -o ecdsa_p256_k0.bin
27+
python esp_tee_sec_stg_keygen.py -k ecdsa_p192 -o ecdsa_p192_k0.bin
28+
```
29+
30+
#### With custom PEM:
31+
32+
```bash
33+
openssl ecparam -name prime256v1 -genkey -noout -out ecdsa_p256.pem
34+
python esp_tee_sec_stg_keygen.py -k ecdsa_p256 -o ecdsa_p256_k1.bin -i ecdsa_p256.pem --write-once
35+
```
36+
37+
### AES-256 Key
38+
39+
```bash
40+
python esp_tee_sec_stg_keygen.py -k aes256 -o aes256_gcm_k0.bin --write-once
41+
```
42+
43+
#### With custom key and IV
44+
45+
```bash
46+
# Generate 32 bytes AES key
47+
openssl rand 32 > aes_key.bin
48+
49+
# Generate 12 bytes IV (optional)
50+
openssl rand 12 >> aes_key.bin
51+
52+
# Generate AES key blob using custom key + IV
53+
python esp_tee_sec_stg_keygen.py -k aes256 -o aes256_gcm_k1.bin -i aes_key.bin
54+
```
55+
56+
## B. NVS Partition Image Generation
57+
58+
### Create the TEE Secure Storage CSV
59+
60+
Prepare a CSV file that defines the data to be used as input for the NVS Partition Generator utility.
61+
62+
```csv
63+
key,type,encoding,value
64+
tee_sec_stg_ns,namespace,,
65+
aes256_key0,file,binary,aes256_gcm_k0.bin
66+
p256_key0,file,binary,ecdsa_p256_k0.bin
67+
p192_key0,file,binary,ecdsa_p192_k0.bin
68+
attest_key0,file,binary,ecdsa_p256_k1.bin
69+
```
70+
> [!IMPORTANT]
71+
> As per the current implementation, all data objects in the TEE Secure Storage are to be stored in the `tee_sec_stg_ns` namespace.
72+
73+
### Generate NVS Encryption Keys
74+
75+
```bash
76+
python nvs_partition_gen.py generate-key --key_protect_hmac --kp_hmac_inputkey hmac_key.bin --keyfile nvs_keys.bin
77+
```
78+
79+
> [!IMPORTANT]
80+
> `hmac_key.bin` must match the HMAC key programmed into the eFuse block specified by `CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`.
81+
82+
### Generate encrypted NVS partition
83+
84+
```bash
85+
python nvs_partition_gen.py encrypt tee_sec_stg_val.csv tee_sec_stg_nvs.bin 0x8000 --inputkey nvs_keys.bin
86+
```
87+
88+
> [!NOTE]
89+
> Replace `0x8000` with the actual size of the TEE Secure Storage partition as configured in your project's partition table.
90+
91+
## (Optional) Decrypt for Verification
92+
93+
```bash
94+
python nvs_partition_gen.py decrypt tee_sec_stg_nvs.bin nvs_keys.bin tee_sec_stg_nvs_decr.bin
95+
```
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#!/usr/bin/env python3
2+
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import argparse
6+
import os
7+
import struct
8+
from enum import Enum
9+
from enum import IntFlag
10+
from typing import Any
11+
from typing import Optional
12+
13+
from cryptography.hazmat.backends import default_backend
14+
from cryptography.hazmat.primitives import serialization
15+
from cryptography.hazmat.primitives.asymmetric import ec
16+
17+
# === Constants ===
18+
SEC_STG_KEY_DATA_SZ = 256
19+
AES_KEY_LEN = 32
20+
AES_DEFAULT_IV_LEN = 16
21+
AES_GCM_IV_LEN = 12
22+
ECDSA_P256_LEN = 32
23+
ECDSA_P192_LEN = 24
24+
25+
26+
# === Key Type Enum ===
27+
class KeyType(Enum):
28+
AES256 = 0
29+
ECDSA_P256 = 1
30+
ECDSA_P192 = 2
31+
32+
33+
# === Bitwise Flags Enum ===
34+
class Flags(IntFlag):
35+
NONE = 0x00000000
36+
WRITE_ONCE = 0x00000001
37+
38+
39+
# === Key Generators ===
40+
41+
42+
def generate_aes256_key(flags: Flags, key_file: Optional[str] = None) -> bytes:
43+
if key_file:
44+
with open(key_file, 'rb') as f:
45+
key_data = f.read()
46+
47+
if len(key_data) < AES_KEY_LEN:
48+
raise ValueError('AES key file must be at least 32 bytes long')
49+
50+
key = key_data[:AES_KEY_LEN]
51+
iv_data = key_data[AES_KEY_LEN:]
52+
53+
iv_len = len(iv_data)
54+
if iv_len == 0:
55+
iv = os.urandom(AES_DEFAULT_IV_LEN)
56+
elif iv_len == AES_GCM_IV_LEN:
57+
iv = iv_data + b'\x00' * (AES_DEFAULT_IV_LEN - AES_GCM_IV_LEN)
58+
elif iv_len == AES_DEFAULT_IV_LEN:
59+
iv = iv_data
60+
else:
61+
raise ValueError('IV length must be exactly 12 or 16 bytes, or omitted to generate one')
62+
else:
63+
key = os.urandom(AES_KEY_LEN)
64+
iv = os.urandom(AES_DEFAULT_IV_LEN)
65+
66+
packed = struct.pack('<II32s16s', KeyType.AES256.value, flags.value, key, iv)
67+
return packed + b'\x00' * (SEC_STG_KEY_DATA_SZ - len(packed))
68+
69+
70+
def generate_ecdsa_key(
71+
curve: ec.EllipticCurve, key_type_enum: KeyType, key_len: int, flags: Flags, pem_file: Optional[str] = None
72+
) -> bytes:
73+
if pem_file:
74+
with open(pem_file, 'rb') as f:
75+
private_key = serialization.load_pem_private_key(f.read(), password=None, backend=default_backend())
76+
if not isinstance(private_key, ec.EllipticCurvePrivateKey):
77+
raise ValueError('Provided PEM does not contain an EC private key')
78+
else:
79+
private_key = ec.generate_private_key(curve, default_backend())
80+
81+
priv = private_key.private_numbers().private_value.to_bytes(key_len, 'big')
82+
pub = private_key.public_key().public_numbers()
83+
x = pub.x.to_bytes(key_len, 'big')
84+
y = pub.y.to_bytes(key_len, 'big')
85+
86+
packed = struct.pack(f'<II{key_len}s{key_len}s{key_len}s', key_type_enum.value, flags.value, priv, x, y)
87+
return packed + b'\x00' * (SEC_STG_KEY_DATA_SZ - len(packed))
88+
89+
90+
def generate_key_data(key_type: KeyType, flags: Flags, input_file: Optional[str]) -> bytes:
91+
if key_type == KeyType.AES256:
92+
return generate_aes256_key(flags, input_file)
93+
elif key_type == KeyType.ECDSA_P256:
94+
return generate_ecdsa_key(ec.SECP256R1(), key_type, ECDSA_P256_LEN, flags, input_file)
95+
elif key_type == KeyType.ECDSA_P192:
96+
return generate_ecdsa_key(ec.SECP192R1(), key_type, ECDSA_P192_LEN, flags, input_file)
97+
else:
98+
raise ValueError(f'Unsupported key type: {key_type}')
99+
100+
101+
# === CLI ===
102+
103+
104+
def parse_args() -> argparse.Namespace:
105+
parser = argparse.ArgumentParser(description='Generate or import a cryptographic key structure for secure storage')
106+
parser.add_argument(
107+
'-k',
108+
'--key-type',
109+
type=str,
110+
choices=[e.name.lower() for e in KeyType],
111+
required=True,
112+
help='key type to be processed',
113+
)
114+
parser.add_argument(
115+
'-o',
116+
'--output',
117+
required=True,
118+
help='output binary file name',
119+
)
120+
parser.add_argument(
121+
'-i',
122+
'--input',
123+
help='input key file (.pem for ecdsa, .bin for aes)',
124+
)
125+
parser.add_argument(
126+
'--write-once',
127+
action='store_true',
128+
help='make key persistent - cannot be modified or deleted once written',
129+
)
130+
return parser.parse_args()
131+
132+
133+
def main() -> None:
134+
args: Any = parse_args()
135+
136+
key_type = KeyType[args.key_type.upper()]
137+
flags = Flags.NONE
138+
if args.write_once:
139+
flags |= Flags.WRITE_ONCE
140+
141+
print(f'[+] Generating key of type: {key_type.name} (value: {key_type.value})')
142+
if args.input:
143+
print(f'[+] Using user-provided key file: {args.input}')
144+
if args.write_once:
145+
print('[+] WRITE_ONCE flag is set')
146+
147+
key_data = generate_key_data(key_type, flags, args.input)
148+
149+
with open(args.output, 'wb') as f:
150+
f.write(key_data)
151+
152+
print(f'[✓] Key written to {args.output}')
153+
154+
155+
if __name__ == '__main__':
156+
main()

components/esp_tee/test_apps/tee_cli_app/README.md

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -78,39 +78,41 @@ get_msg_sha256 "<msg>"
7878
Get the SHA256 digest for the given message
7979
"<msg>" Message for SHA256 digest calculation
8080
81-
tee_sec_stg_gen_key <slot_id> <key_type>
82-
Generate and store a new key of the specified type in the given TEE secure
83-
storage slot
84-
<slot_id> TEE Secure storage slot for storing the key
85-
<key_type> Key type (0: ECDSA_SECP256R1, 1: AES256)
86-
87-
tee_sec_stg_sign <slot_id> <msg_sha256>
88-
Sign a message using the ECDSA keypair stored in the given slot ID and verify
89-
the signature
90-
<slot_id> TEE Secure storage slot storing the ecdsa-secp256r1 keypair
81+
tee_sec_stg_gen_key <key_id> <key_type>
82+
Generate and store a new key of the specified type with the given ID
83+
<key_id> TEE Secure storage key ID
84+
<key_type> Key type (0: AES256, 1: ECDSA_SECP256R1)
85+
86+
tee_sec_stg_sign <key_id> <msg_sha256>
87+
Sign a message using the ECDSA keypair stored with the given key ID and
88+
verify the signature
89+
<key_id> TEE Secure storage key ID
9190
<msg_sha256> SHA256 digest of the message to be signed and verified
9291
93-
tee_sec_stg_encrypt <slot_id> <plaintext>
94-
Encrypt data using AES-GCM with a key from secure storage
95-
<slot_id> TEE Secure storage slot storing the AES key
92+
tee_sec_stg_encrypt <key_id> <plaintext>
93+
Encrypt data using AES-GCM key with the given ID from secure storage
94+
<key_id> TEE Secure storage key ID
9695
<plaintext> Plaintext to be encrypted
9796
98-
tee_sec_stg_decrypt <slot_id> <ciphertext> <tag>
99-
Decrypt data using AES-GCM with a key from secure storage
100-
<slot_id> TEE Secure storage slot storing the AES key
97+
tee_sec_stg_decrypt <key_id> <ciphertext> <tag>
98+
Decrypt data using AES-GCM key with the given ID from secure storage
99+
<key_id> TEE Secure storage key ID
101100
<ciphertext> Ciphertext to be decrypted
102101
<tag> AES-GCM authentication tag
103102
104-
help
105-
Print the list of registered commands
103+
help [<string>] [-v <0|1>]
104+
Print the summary of all registered commands if no arguments are given,
105+
otherwise print summary of given command.
106+
<string> Name of command
107+
-v, --verbose=<0|1> If specified, list console commands with given verbose level
106108
```
107109
108110
## Secure Services
109111
110112
### Attestation
111113
112114
- The `tee_att_info` command provided by the attestation service generates and dumps an Entity Attestation Token (EAT) signed by the TEE.
113-
- The token is signed using the ECDSA key (`secp256r1` curve) stored in the configured slot ID of the TEE Secure Storage.
115+
- The token is signed using the ECDSA key (`secp256r1` curve) stored in the TEE Secure Storage with the configured key ID.
114116
115117
<details>
116118
<summary><b>Sample output:</b> <i>tee_att_info</i></summary>
@@ -128,22 +130,22 @@ I (8180) tee_attest: Attestation token - Data:
128130
### Secure Storage
129131
130132
- The TEE secure storage service provides the following commands:
131-
- `tee_sec_stg_gen_key`: Generate and store a new key (ECDSA or AES) in a specified TEE secure storage slot
132-
- `tee_sec_stg_sign`: Sign a message using an ECDSA `secp256r1` key pair stored in a specified slot and verify the signature
133-
- `tee_sec_stg_encrypt`: Encrypt data with AES256-GCM using the key from the specified slot and outputs the ciphertext and tag
134-
- `tee_sec_stg_decrypt`: Decrypt ciphertext using key from the specified slot and tag for integrity verification
133+
- `tee_sec_stg_gen_key`: Generate and store a new key (ECDSA or AES) in the TEE secure storage with the specified ID
134+
- `tee_sec_stg_sign`: Sign a message using an ECDSA `secp256r1` key pair with the specified ID and verify the signature
135+
- `tee_sec_stg_encrypt`: Encrypt data with AES256-GCM using the key with the specified ID and outputs the ciphertext and tag
136+
- `tee_sec_stg_decrypt`: Decrypt ciphertext using key with the specified ID and tag for integrity verification
135137
- The `get_msg_sha256` command computes the SHA256 hash of a given message, which can be used as input for the `tee_sec_stg_sign` command.
136138
137139
<details>
138140
<summary><b>Sample output:</b> <i>tee_sec_stg_gen_key + get_msg_sha256 + tee_sec_stg_sign</i></summary>
139141
140142
```log
141-
esp32c6> tee_sec_stg_gen_key 7 0
142-
I (2964) tee_sec_stg: Generated ECDSA_SECP256R1 key in slot 7
143+
esp32c6> tee_sec_stg_gen_key ecdsa_p256_k0 1
144+
I (2964) tee_sec_stg: Generated ECDSA_SECP256R1 key with ID ecdsa_p256_k0
143145
esp32c6> get_msg_sha256 "hello world"
144146
I (3984) tee_sec_stg: Message digest (SHA256) -
145147
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
146-
esp32c6> tee_sec_stg_sign 7 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
148+
esp32c6> tee_sec_stg_sign ecdsa_p256_k0 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
147149
I (5384) tee_sec_stg: Generated signature -
148150
944684f6ddcf4c268ac6b65e34ccb8d95bd2849567a87867101bc1f09208f0885d935d7b3ba9d46014f28e4c7c988d68c775431fcb2cb2d4ca5c6862db771088
149151
I (6404) tee_sec_stg: Public key (Uncompressed) -
@@ -157,14 +159,14 @@ I (6444) tee_sec_stg: Signature verified successfully!
157159
<summary><b>Sample output:</b> <i>tee_sec_stg_gen_key + tee_sec_stg_encrypt + tee_sec_stg_decrypt</i></summary>
158160
159161
```log
160-
esp32c6> tee_sec_stg_gen_key 8 1
161-
I (2784) tee_sec_stg: Generated AES256 key in slot 8
162-
esp32c6> tee_sec_stg_encrypt 8 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
162+
esp32c6> tee_sec_stg_gen_key aes256_k0 0
163+
I (2784) tee_sec_stg: Generated AES256 key with ID key0
164+
esp32c6> tee_sec_stg_encrypt aes256_k0 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
163165
I (3084) tee_sec_stg: Ciphertext -
164166
58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1
165167
I (3594) tee_sec_stg: Tag -
166168
caeedb43e08dc3b4e35a58b2412908cc
167-
esp32c6> tee_sec_stg_decrypt 8 58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1 caeedb43e08dc3b4e35a58b2412908cc
169+
esp32c6> tee_sec_stg_decrypt aes256_k0 58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1 caeedb43e08dc3b4e35a58b2412908cc
168170
I (4314) tee_sec_stg: Decrypted plaintext -
169171
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
170172
```

components/esp_tee/test_apps/tee_cli_app/main/app_main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "freertos/FreeRTOS.h"
1919
#include "freertos/task.h"
2020

21+
#include "esp_tee_sec_storage.h"
2122
#include "example_tee_srv.h"
2223

2324
#define PROMPT_STR CONFIG_IDF_TARGET

0 commit comments

Comments
 (0)