refactor(esp_tee): Update TEE secure storage examples and test-apps

This commit is contained in:
Laukik Hase
2025-03-28 16:44:05 +05:30
parent 41bf07e6ce
commit 3bb3f9362e
21 changed files with 839 additions and 307 deletions

View File

@@ -0,0 +1,95 @@
# TEE Secure Storage: Image Generation
## A. Generate Secure Key Blobs
### `esp_tee_sec_stg_keygen` tool
```
$ python esp_tee_sec_stg_keygen.py --help
usage: esp_tee_sec_stg_keygen.py [-h] -k {aes256,ecdsa_p256,ecdsa_p192} -o OUTPUT [-i INPUT] [--write-once]
Generate or import a cryptographic key structure for secure storage
options:
-h, --help show this help message and exit
-k, --key-type {aes256,ecdsa_p256,ecdsa_p192}
key type to be processed
-o, --output OUTPUT output binary file name
-i, --input INPUT input key file (.pem for ecdsa, .bin for aes)
--write-once make key persistent - cannot be modified or deleted once written
```
### ECDSA Keys
```bash
python esp_tee_sec_stg_keygen.py -k ecdsa_p256 -o ecdsa_p256_k0.bin
python esp_tee_sec_stg_keygen.py -k ecdsa_p192 -o ecdsa_p192_k0.bin
```
#### With custom PEM:
```bash
openssl ecparam -name prime256v1 -genkey -noout -out ecdsa_p256.pem
python esp_tee_sec_stg_keygen.py -k ecdsa_p256 -o ecdsa_p256_k1.bin -i ecdsa_p256.pem --write-once
```
### AES-256 Key
```bash
python esp_tee_sec_stg_keygen.py -k aes256 -o aes256_gcm_k0.bin --write-once
```
#### With custom key and IV
```bash
# Generate 32 bytes AES key
openssl rand 32 > aes_key.bin
# Generate 12 bytes IV (optional)
openssl rand 12 >> aes_key.bin
# Generate AES key blob using custom key + IV
python esp_tee_sec_stg_keygen.py -k aes256 -o aes256_gcm_k1.bin -i aes_key.bin
```
## B. NVS Partition Image Generation
### Create the TEE Secure Storage CSV
Prepare a CSV file that defines the data to be used as input for the NVS Partition Generator utility.
```csv
key,type,encoding,value
tee_sec_stg_ns,namespace,,
aes256_key0,file,binary,aes256_gcm_k0.bin
p256_key0,file,binary,ecdsa_p256_k0.bin
p192_key0,file,binary,ecdsa_p192_k0.bin
attest_key0,file,binary,ecdsa_p256_k1.bin
```
> [!IMPORTANT]
> As per the current implementation, all data objects in the TEE Secure Storage are to be stored in the `tee_sec_stg_ns` namespace.
### Generate NVS Encryption Keys
```bash
python nvs_partition_gen.py generate-key --key_protect_hmac --kp_hmac_inputkey hmac_key.bin --keyfile nvs_keys.bin
```
> [!IMPORTANT]
> `hmac_key.bin` must match the HMAC key programmed into the eFuse block specified by `CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`.
### Generate encrypted NVS partition
```bash
python nvs_partition_gen.py encrypt tee_sec_stg_val.csv tee_sec_stg_nvs.bin 0x8000 --inputkey nvs_keys.bin
```
> [!NOTE]
> Replace `0x8000` with the actual size of the TEE Secure Storage partition as configured in your project's partition table.
## (Optional) Decrypt for Verification
```bash
python nvs_partition_gen.py decrypt tee_sec_stg_nvs.bin nvs_keys.bin tee_sec_stg_nvs_decr.bin
```

View File

@@ -0,0 +1,156 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import argparse
import os
import struct
from enum import Enum
from enum import IntFlag
from typing import Any
from typing import Optional
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
# === Constants ===
SEC_STG_KEY_DATA_SZ = 256
AES_KEY_LEN = 32
AES_DEFAULT_IV_LEN = 16
AES_GCM_IV_LEN = 12
ECDSA_P256_LEN = 32
ECDSA_P192_LEN = 24
# === Key Type Enum ===
class KeyType(Enum):
AES256 = 0
ECDSA_P256 = 1
ECDSA_P192 = 2
# === Bitwise Flags Enum ===
class Flags(IntFlag):
NONE = 0x00000000
WRITE_ONCE = 0x00000001
# === Key Generators ===
def generate_aes256_key(flags: Flags, key_file: Optional[str] = None) -> bytes:
if key_file:
with open(key_file, 'rb') as f:
key_data = f.read()
if len(key_data) < AES_KEY_LEN:
raise ValueError('AES key file must be at least 32 bytes long')
key = key_data[:AES_KEY_LEN]
iv_data = key_data[AES_KEY_LEN:]
iv_len = len(iv_data)
if iv_len == 0:
iv = os.urandom(AES_DEFAULT_IV_LEN)
elif iv_len == AES_GCM_IV_LEN:
iv = iv_data + b'\x00' * (AES_DEFAULT_IV_LEN - AES_GCM_IV_LEN)
elif iv_len == AES_DEFAULT_IV_LEN:
iv = iv_data
else:
raise ValueError('IV length must be exactly 12 or 16 bytes, or omitted to generate one')
else:
key = os.urandom(AES_KEY_LEN)
iv = os.urandom(AES_DEFAULT_IV_LEN)
packed = struct.pack('<II32s16s', KeyType.AES256.value, flags.value, key, iv)
return packed + b'\x00' * (SEC_STG_KEY_DATA_SZ - len(packed))
def generate_ecdsa_key(
curve: ec.EllipticCurve, key_type_enum: KeyType, key_len: int, flags: Flags, pem_file: Optional[str] = None
) -> bytes:
if pem_file:
with open(pem_file, 'rb') as f:
private_key = serialization.load_pem_private_key(f.read(), password=None, backend=default_backend())
if not isinstance(private_key, ec.EllipticCurvePrivateKey):
raise ValueError('Provided PEM does not contain an EC private key')
else:
private_key = ec.generate_private_key(curve, default_backend())
priv = private_key.private_numbers().private_value.to_bytes(key_len, 'big')
pub = private_key.public_key().public_numbers()
x = pub.x.to_bytes(key_len, 'big')
y = pub.y.to_bytes(key_len, 'big')
packed = struct.pack(f'<II{key_len}s{key_len}s{key_len}s', key_type_enum.value, flags.value, priv, x, y)
return packed + b'\x00' * (SEC_STG_KEY_DATA_SZ - len(packed))
def generate_key_data(key_type: KeyType, flags: Flags, input_file: Optional[str]) -> bytes:
if key_type == KeyType.AES256:
return generate_aes256_key(flags, input_file)
elif key_type == KeyType.ECDSA_P256:
return generate_ecdsa_key(ec.SECP256R1(), key_type, ECDSA_P256_LEN, flags, input_file)
elif key_type == KeyType.ECDSA_P192:
return generate_ecdsa_key(ec.SECP192R1(), key_type, ECDSA_P192_LEN, flags, input_file)
else:
raise ValueError(f'Unsupported key type: {key_type}')
# === CLI ===
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description='Generate or import a cryptographic key structure for secure storage')
parser.add_argument(
'-k',
'--key-type',
type=str,
choices=[e.name.lower() for e in KeyType],
required=True,
help='key type to be processed',
)
parser.add_argument(
'-o',
'--output',
required=True,
help='output binary file name',
)
parser.add_argument(
'-i',
'--input',
help='input key file (.pem for ecdsa, .bin for aes)',
)
parser.add_argument(
'--write-once',
action='store_true',
help='make key persistent - cannot be modified or deleted once written',
)
return parser.parse_args()
def main() -> None:
args: Any = parse_args()
key_type = KeyType[args.key_type.upper()]
flags = Flags.NONE
if args.write_once:
flags |= Flags.WRITE_ONCE
print(f'[+] Generating key of type: {key_type.name} (value: {key_type.value})')
if args.input:
print(f'[+] Using user-provided key file: {args.input}')
if args.write_once:
print('[+] WRITE_ONCE flag is set')
key_data = generate_key_data(key_type, flags, args.input)
with open(args.output, 'wb') as f:
f.write(key_data)
print(f'[✓] Key written to {args.output}')
if __name__ == '__main__':
main()

View File

@@ -78,31 +78,33 @@ get_msg_sha256 "<msg>"
Get the SHA256 digest for the given message
"<msg>" Message for SHA256 digest calculation
tee_sec_stg_gen_key <slot_id> <key_type>
Generate and store a new key of the specified type in the given TEE secure
storage slot
<slot_id> TEE Secure storage slot for storing the key
<key_type> Key type (0: ECDSA_SECP256R1, 1: AES256)
tee_sec_stg_gen_key <key_id> <key_type>
Generate and store a new key of the specified type with the given ID
<key_id> TEE Secure storage key ID
<key_type> Key type (0: AES256, 1: ECDSA_SECP256R1)
tee_sec_stg_sign <slot_id> <msg_sha256>
Sign a message using the ECDSA keypair stored in the given slot ID and verify
the signature
<slot_id> TEE Secure storage slot storing the ecdsa-secp256r1 keypair
tee_sec_stg_sign <key_id> <msg_sha256>
Sign a message using the ECDSA keypair stored with the given key ID and
verify the signature
<key_id> TEE Secure storage key ID
<msg_sha256> SHA256 digest of the message to be signed and verified
tee_sec_stg_encrypt <slot_id> <plaintext>
Encrypt data using AES-GCM with a key from secure storage
<slot_id> TEE Secure storage slot storing the AES key
tee_sec_stg_encrypt <key_id> <plaintext>
Encrypt data using AES-GCM key with the given ID from secure storage
<key_id> TEE Secure storage key ID
<plaintext> Plaintext to be encrypted
tee_sec_stg_decrypt <slot_id> <ciphertext> <tag>
Decrypt data using AES-GCM with a key from secure storage
<slot_id> TEE Secure storage slot storing the AES key
tee_sec_stg_decrypt <key_id> <ciphertext> <tag>
Decrypt data using AES-GCM key with the given ID from secure storage
<key_id> TEE Secure storage key ID
<ciphertext> Ciphertext to be decrypted
<tag> AES-GCM authentication tag
help
Print the list of registered commands
help [<string>] [-v <0|1>]
Print the summary of all registered commands if no arguments are given,
otherwise print summary of given command.
<string> Name of command
-v, --verbose=<0|1> If specified, list console commands with given verbose level
```
## Secure Services
@@ -110,7 +112,7 @@ help
### Attestation
- The `tee_att_info` command provided by the attestation service generates and dumps an Entity Attestation Token (EAT) signed by the TEE.
- The token is signed using the ECDSA key (`secp256r1` curve) stored in the configured slot ID of the TEE Secure Storage.
- The token is signed using the ECDSA key (`secp256r1` curve) stored in the TEE Secure Storage with the configured key ID.
<details>
<summary><b>Sample output:</b> <i>tee_att_info</i></summary>
@@ -128,22 +130,22 @@ I (8180) tee_attest: Attestation token - Data:
### Secure Storage
- The TEE secure storage service provides the following commands:
- `tee_sec_stg_gen_key`: Generate and store a new key (ECDSA or AES) in a specified TEE secure storage slot
- `tee_sec_stg_sign`: Sign a message using an ECDSA `secp256r1` key pair stored in a specified slot and verify the signature
- `tee_sec_stg_encrypt`: Encrypt data with AES256-GCM using the key from the specified slot and outputs the ciphertext and tag
- `tee_sec_stg_decrypt`: Decrypt ciphertext using key from the specified slot and tag for integrity verification
- `tee_sec_stg_gen_key`: Generate and store a new key (ECDSA or AES) in the TEE secure storage with the specified ID
- `tee_sec_stg_sign`: Sign a message using an ECDSA `secp256r1` key pair with the specified ID and verify the signature
- `tee_sec_stg_encrypt`: Encrypt data with AES256-GCM using the key with the specified ID and outputs the ciphertext and tag
- `tee_sec_stg_decrypt`: Decrypt ciphertext using key with the specified ID and tag for integrity verification
- 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.
<details>
<summary><b>Sample output:</b> <i>tee_sec_stg_gen_key + get_msg_sha256 + tee_sec_stg_sign</i></summary>
```log
esp32c6> tee_sec_stg_gen_key 7 0
I (2964) tee_sec_stg: Generated ECDSA_SECP256R1 key in slot 7
esp32c6> tee_sec_stg_gen_key ecdsa_p256_k0 1
I (2964) tee_sec_stg: Generated ECDSA_SECP256R1 key with ID ecdsa_p256_k0
esp32c6> get_msg_sha256 "hello world"
I (3984) tee_sec_stg: Message digest (SHA256) -
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
esp32c6> tee_sec_stg_sign 7 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
esp32c6> tee_sec_stg_sign ecdsa_p256_k0 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
I (5384) tee_sec_stg: Generated signature -
944684f6ddcf4c268ac6b65e34ccb8d95bd2849567a87867101bc1f09208f0885d935d7b3ba9d46014f28e4c7c988d68c775431fcb2cb2d4ca5c6862db771088
I (6404) tee_sec_stg: Public key (Uncompressed) -
@@ -157,14 +159,14 @@ I (6444) tee_sec_stg: Signature verified successfully!
<summary><b>Sample output:</b> <i>tee_sec_stg_gen_key + tee_sec_stg_encrypt + tee_sec_stg_decrypt</i></summary>
```log
esp32c6> tee_sec_stg_gen_key 8 1
I (2784) tee_sec_stg: Generated AES256 key in slot 8
esp32c6> tee_sec_stg_encrypt 8 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
esp32c6> tee_sec_stg_gen_key aes256_k0 0
I (2784) tee_sec_stg: Generated AES256 key with ID key0
esp32c6> tee_sec_stg_encrypt aes256_k0 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
I (3084) tee_sec_stg: Ciphertext -
58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1
I (3594) tee_sec_stg: Tag -
caeedb43e08dc3b4e35a58b2412908cc
esp32c6> tee_sec_stg_decrypt 8 58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1 caeedb43e08dc3b4e35a58b2412908cc
esp32c6> tee_sec_stg_decrypt aes256_k0 58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1 caeedb43e08dc3b4e35a58b2412908cc
I (4314) tee_sec_stg: Decrypted plaintext -
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
```

View File

@@ -18,6 +18,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_tee_sec_storage.h"
#include "example_tee_srv.h"
#define PROMPT_STR CONFIG_IDF_TARGET

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -79,7 +79,7 @@ static esp_err_t hexbuf_to_hexstr(const void *hexbuf, size_t hexbuf_sz, char *he
return ESP_OK;
}
static esp_err_t verify_ecdsa_secp256r1_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_pubkey_t *pubkey, const esp_tee_sec_storage_sign_t *sign)
static esp_err_t verify_ecdsa_secp256r1_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_ecdsa_pubkey_t *pubkey, const esp_tee_sec_storage_ecdsa_sign_t *sign)
{
if (pubkey == NULL || digest == NULL || sign == NULL) {
return ESP_ERR_INVALID_ARG;
@@ -197,7 +197,7 @@ void register_cmd_msg_sha256(void)
}
static struct {
struct arg_int *slot_id;
struct arg_str *key_str_id;
struct arg_int *key_type;
struct arg_end *end;
} tee_sec_stg_gen_key_args;
@@ -211,29 +211,27 @@ static int tee_sec_stg_gen_key(int argc, char **argv)
}
esp_err_t err = ESP_FAIL;
uint16_t slot_id = (uint16_t)tee_sec_stg_gen_key_args.slot_id->ival[0];
esp_tee_sec_storage_type_t key_type = (esp_tee_sec_storage_type_t)tee_sec_stg_gen_key_args.key_type->ival[0];
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
esp_tee_sec_storage_key_cfg_t cfg = {
.id = (const char *)tee_sec_stg_gen_key_args.key_str_id->sval[0],
.type = (esp_tee_sec_storage_type_t)tee_sec_stg_gen_key_args.key_type->ival[0]
};
err = esp_tee_sec_storage_clear_key(cfg.id);
if (err != ESP_OK && err != ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to clear key %d!", cfg.id);
goto exit;
}
err = esp_tee_sec_storage_clear_slot(slot_id);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to clear slot %d!", slot_id);
goto exit;
}
err = esp_tee_sec_storage_gen_key(slot_id, key_type);
err = esp_tee_sec_storage_gen_key(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate key!");
goto exit;
}
ESP_LOGI(TAG, "Generated %s key in slot %d",
(key_type == ESP_SEC_STG_KEY_ECDSA_SECP256R1) ? "ECDSA_SECP256R1" : "AES256",
slot_id);
ESP_LOGI(TAG, "Generated %s key with ID %s",
(cfg.type == ESP_SEC_STG_KEY_ECDSA_SECP256R1) ? "ECDSA_SECP256R1" : "AES256",
cfg.id);
exit:
return err;
@@ -241,13 +239,13 @@ exit:
void register_srv_sec_stg_gen_key(void)
{
tee_sec_stg_gen_key_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot for storing the key");
tee_sec_stg_gen_key_args.key_type = arg_int1(NULL, NULL, "<key_type>", "Key type (0: ECDSA_SECP256R1, 1: AES256)");
tee_sec_stg_gen_key_args.key_str_id = arg_str1(NULL, NULL, "<key_id>", "TEE Secure storage key ID");
tee_sec_stg_gen_key_args.key_type = arg_int1(NULL, NULL, "<key_type>", "Key type (0: AES256, 1: ECDSA_SECP256R1)");
tee_sec_stg_gen_key_args.end = arg_end(2);
const esp_console_cmd_t tee_sec_stg = {
.command = "tee_sec_stg_gen_key",
.help = "Generate and store a new key of the specified type in the given TEE secure storage slot",
.help = "Generate and store a new key of the specified type with the given ID",
.hint = NULL,
.func = &tee_sec_stg_gen_key,
.argtable = &tee_sec_stg_gen_key_args,
@@ -257,7 +255,7 @@ void register_srv_sec_stg_gen_key(void)
}
static struct {
struct arg_int *slot_id;
struct arg_str *key_str_id;
struct arg_str *msg_sha256;
struct arg_end *end;
} tee_sec_stg_sign_args;
@@ -280,16 +278,13 @@ static int tee_sec_stg_sign(int argc, char **argv)
uint8_t digest[SHA256_DIGEST_SZ] = {};
hexstr_to_hexbuf(msg_sha256, msg_sha256_len, digest, sizeof(digest));
esp_err_t err = ESP_FAIL;
uint16_t slot_id = (uint16_t)tee_sec_stg_sign_args.slot_id->ival[0];
esp_tee_sec_storage_key_cfg_t cfg = {
.id = (const char *)tee_sec_stg_sign_args.key_str_id->sval[0],
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1
};
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
goto exit;
}
esp_tee_sec_storage_sign_t sign = {};
err = esp_tee_sec_storage_get_signature(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1, digest, sizeof(digest), &sign);
esp_tee_sec_storage_ecdsa_sign_t sign = {};
esp_err_t err = esp_tee_sec_storage_ecdsa_sign(&cfg, digest, sizeof(digest), &sign);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate signature!");
goto exit;
@@ -306,8 +301,8 @@ static int tee_sec_stg_sign(int argc, char **argv)
ESP_LOGI(TAG, "Generated signature -\n%s", sign_hexstr);
free(sign_hexstr);
esp_tee_sec_storage_pubkey_t pubkey = {};
err = esp_tee_sec_storage_get_pubkey(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1, &pubkey);
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
err = esp_tee_sec_storage_ecdsa_get_pubkey(&cfg, &pubkey);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch public-key!");
goto exit;
@@ -338,13 +333,13 @@ exit:
void register_srv_sec_stg_sign(void)
{
tee_sec_stg_sign_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot storing the ecdsa-secp256r1 keypair");
tee_sec_stg_sign_args.key_str_id = arg_str1(NULL, NULL, "<key_id>", "TEE Secure storage key ID");
tee_sec_stg_sign_args.msg_sha256 = arg_str1(NULL, NULL, "<msg_sha256>", "SHA256 digest of the message to be signed and verified");
tee_sec_stg_sign_args.end = arg_end(2);
const esp_console_cmd_t tee_sec_stg = {
.command = "tee_sec_stg_sign",
.help = "Sign a message using the ECDSA keypair stored in the given slot ID and verify the signature",
.help = "Sign a message using the ECDSA keypair stored with the given key ID and verify the signature",
.hint = NULL,
.func = &tee_sec_stg_sign,
.argtable = &tee_sec_stg_sign_args,
@@ -354,7 +349,7 @@ void register_srv_sec_stg_sign(void)
}
static struct {
struct arg_int *slot_id;
struct arg_str *key_str_id;
struct arg_str *plaintext;
struct arg_end *end;
} tee_sec_stg_encrypt_args;
@@ -369,7 +364,7 @@ static int tee_sec_stg_encrypt(int argc, char **argv)
esp_err_t err = ESP_FAIL;
uint8_t tag[AES256_GCM_TAG_LEN];
uint16_t slot_id = (uint16_t)tee_sec_stg_encrypt_args.slot_id->ival[0];
const char *key_id = (const char *)tee_sec_stg_encrypt_args.key_str_id->sval[0];
const char *plaintext = tee_sec_stg_encrypt_args.plaintext->sval[0];
size_t plaintext_len = strnlen(plaintext, MAX_AES_PLAINTEXT_LEN);
@@ -399,13 +394,13 @@ static int tee_sec_stg_encrypt(int argc, char **argv)
goto exit;
}
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
goto exit;
}
esp_tee_sec_storage_aead_ctx_t ctx = {
.key_id = key_id,
.input = (uint8_t *)plaintext_buf,
.input_len = plaintext_buf_len
};
err = esp_tee_sec_storage_encrypt(slot_id, (uint8_t *)plaintext_buf, plaintext_buf_len,
NULL, 0, tag, sizeof(tag), ciphertext_buf);
err = esp_tee_sec_storage_aead_encrypt(&ctx, tag, sizeof(tag), ciphertext_buf);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to encrypt data: %s", esp_err_to_name(err));
goto exit;
@@ -434,13 +429,13 @@ exit:
void register_srv_sec_stg_encrypt(void)
{
tee_sec_stg_encrypt_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot storing the AES key");
tee_sec_stg_encrypt_args.key_str_id = arg_str1(NULL, NULL, "<key_id>", "TEE Secure storage key ID");
tee_sec_stg_encrypt_args.plaintext = arg_str1(NULL, NULL, "<plaintext>", "Plaintext to be encrypted");
tee_sec_stg_encrypt_args.end = arg_end(2);
const esp_console_cmd_t tee_sec_stg = {
.command = "tee_sec_stg_encrypt",
.help = "Encrypt data using AES-GCM with a key from secure storage",
.help = "Encrypt data using AES-GCM key with the given ID from secure storage",
.hint = NULL,
.func = &tee_sec_stg_encrypt,
.argtable = &tee_sec_stg_encrypt_args,
@@ -450,7 +445,7 @@ void register_srv_sec_stg_encrypt(void)
}
static struct {
struct arg_int *slot_id;
struct arg_str *key_str_id;
struct arg_str *ciphertext;
struct arg_str *tag;
struct arg_end *end;
@@ -465,7 +460,7 @@ static int tee_sec_stg_decrypt(int argc, char **argv)
}
esp_err_t err = ESP_FAIL;
uint16_t slot_id = (uint16_t)tee_sec_stg_decrypt_args.slot_id->ival[0];
const char *key_id = (const char *)tee_sec_stg_decrypt_args.key_str_id->sval[0];
const char *tag_hexstr = tee_sec_stg_decrypt_args.tag->sval[0];
uint8_t tag[AES256_GCM_TAG_LEN];
@@ -499,13 +494,13 @@ static int tee_sec_stg_decrypt(int argc, char **argv)
goto exit;
}
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
goto exit;
}
esp_tee_sec_storage_aead_ctx_t ctx = {
.key_id = key_id,
.input = (uint8_t *)ciphertext_buf,
.input_len = ciphertext_buf_len
};
err = esp_tee_sec_storage_decrypt(slot_id, ciphertext_buf, ciphertext_buf_len,
NULL, 0, tag, sizeof(tag), plaintext_buf);
err = esp_tee_sec_storage_aead_decrypt(&ctx, tag, sizeof(tag), plaintext_buf);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to decrypt data: %s", esp_err_to_name(err));
goto exit;
@@ -530,14 +525,14 @@ exit:
void register_srv_sec_stg_decrypt(void)
{
tee_sec_stg_decrypt_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot storing the AES key");
tee_sec_stg_decrypt_args.key_str_id = arg_str1(NULL, NULL, "<key_id>", "TEE Secure storage key ID");
tee_sec_stg_decrypt_args.ciphertext = arg_str1(NULL, NULL, "<ciphertext>", "Ciphertext to be decrypted");
tee_sec_stg_decrypt_args.tag = arg_str1(NULL, NULL, "<tag>", "AES-GCM authentication tag");
tee_sec_stg_decrypt_args.end = arg_end(3);
const esp_console_cmd_t tee_sec_stg = {
.command = "tee_sec_stg_decrypt",
.help = "Decrypt data using AES-GCM with a key from secure storage",
.help = "Decrypt data using AES-GCM key with the given ID from secure storage",
.hint = NULL,
.func = &tee_sec_stg_decrypt,
.argtable = &tee_sec_stg_decrypt_args,

View File

@@ -53,13 +53,14 @@ def test_tee_cli_secure_storage(dut: Dut) -> None:
time.sleep(1)
# Test out the TEE secure storage workflow - Message signing and verification
iterations = 3
sec_stg_slots = {0: 0, 1: 14, 2: 7}
for i in range(iterations):
dut.write(f'tee_sec_stg_gen_key {sec_stg_slots.get(i)} 0')
dut.expect(r'Generated ECDSA_SECP256R1 key in slot (\d+)', timeout=30)
sec_stg_key_ids = {0: 'key0', 1: 'key1', 2: 'key2'}
iterations = len(sec_stg_key_ids)
dut.write(f'tee_sec_stg_sign {sec_stg_slots.get(i)} {test_msg_hash}')
for i in range(iterations):
dut.write(f'tee_sec_stg_gen_key {sec_stg_key_ids.get(i)} 1')
dut.expect(r'Generated ECDSA_SECP256R1 key with ID (\S+)', timeout=30)
dut.write(f'tee_sec_stg_sign {sec_stg_key_ids.get(i)} {test_msg_hash}')
test_msg_sign = dut.expect(r'Generated signature -\s*([0-9a-fA-F]{128})', timeout=30)[1].decode()
test_msg_pubkey = dut.expect(r'Public key \(Uncompressed\) -\s*([0-9a-fA-F]{130})', timeout=30)[1].decode()
dut.expect('Signature verified successfully', timeout=30)
@@ -69,16 +70,15 @@ def test_tee_cli_secure_storage(dut: Dut) -> None:
time.sleep(1)
# Test out the TEE secure storage workflow - Encryption and decryption
sec_stg_slots = {0: 1, 1: 14, 2: 9}
for i in range(iterations):
dut.write(f'tee_sec_stg_gen_key {sec_stg_slots.get(i)} 1')
dut.expect(r'Generated AES256 key in slot (\d+)', timeout=30)
dut.write(f'tee_sec_stg_gen_key {sec_stg_key_ids.get(i)} 0')
dut.expect(r'Generated AES256 key with ID (\S+)', timeout=30)
dut.write(f'tee_sec_stg_encrypt {sec_stg_slots.get(i)} {test_msg_hash}')
dut.write(f'tee_sec_stg_encrypt {sec_stg_key_ids.get(i)} {test_msg_hash}')
test_msg_cipher = dut.expect(r'Ciphertext -\s*([0-9a-fA-F]{64})', timeout=30)[1].decode()
test_msg_tag = dut.expect(r'Tag -\s*([0-9a-fA-F]{32})', timeout=30)[1].decode()
dut.write(f'tee_sec_stg_decrypt {sec_stg_slots.get(i)} {test_msg_cipher} {test_msg_tag}')
dut.write(f'tee_sec_stg_decrypt {sec_stg_key_ids.get(i)} {test_msg_cipher} {test_msg_tag}')
test_msg_decipher = dut.expect(r'Decrypted plaintext -\s*([0-9a-fA-F]{64})', timeout=30)[1].decode()
assert test_msg_decipher == test_msg_hash
@@ -133,9 +133,9 @@ def test_tee_cli_attestation(dut: Dut) -> None:
dut.expect('ESP-TEE: Secure services demonstration', timeout=30)
time.sleep(1)
att_key_slot = dut.app.sdkconfig.get('SECURE_TEE_ATT_KEY_SLOT_ID')
dut.write(f'tee_sec_stg_gen_key {att_key_slot} 0')
dut.expect(r'Generated ECDSA_SECP256R1 key in slot (\d+)', timeout=30)
att_key_id = dut.app.sdkconfig.get('SECURE_TEE_ATT_KEY_STR_ID')
dut.write(f'tee_sec_stg_gen_key {att_key_id} 1')
dut.expect(r'Generated ECDSA_SECP256R1 key with ID (\S+)', timeout=30)
# Get the Entity Attestation token from TEE and verify its signature
dut.write('tee_att_info')

View File

@@ -1,6 +1,6 @@
# Minimal TEE configuration
CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE=y
CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK=9
CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID=5
# Reducing TEE DRAM and STACK sizes
# 21KB

View File

@@ -12,4 +12,4 @@ CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=y
# TEE Secure Storage: Release mode
CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE=y
CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK=9
CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID=5

View File

@@ -42,7 +42,7 @@ uint32_t _ss_add_in_loop(uint32_t a, uint32_t b, uint32_t iter)
for (int i = 0; i < iter; i++) {
a += b;
esp_rom_delay_us(1000000);
ESP_LOGD(TAG, "[mode: %d] val: %d", esp_cpu_get_curr_privilege_level(), a);
esp_rom_printf("[mode: %d] val: %d\n", esp_cpu_get_curr_privilege_level(), a);
}
return a;
}

View File

@@ -1,9 +1,17 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
# pylint: disable=W0621 # redefined-outer-name
import base64
import csv
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
from typing import Any
from typing import Dict
from typing import List
import espsecure
import esptool
@@ -13,6 +21,7 @@ from _pytest.monkeypatch import MonkeyPatch
from pytest_embedded_idf.serial import IdfSerial
from pytest_embedded_serial_esp.serial import EspSerial
# fmt: off
esp_tee_empty_bin = {
'esp32c6': [
0xE9, 0x04, 0x02, 0x10, 0x00, 0x00, 0x80, 0x40, 0xEE, 0x00, 0x00, 0x00,
@@ -56,6 +65,7 @@ esp_tee_empty_bin = {
0xFC, 0x74, 0xB2, 0xB9, 0x34, 0x72, 0xC3, 0x00
]
}
# fmt: on
# This is a custom IdfSerial class to support custom functionality
@@ -70,10 +80,12 @@ class TEESerial(IdfSerial):
@EspSerial.use_esptool()
def bootloader_force_flash_if_req(self) -> None:
# Forcefully flash the bootloader only if security features are enabled
if any((
self.app.sdkconfig.get('SECURE_BOOT', True),
self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED', True),
)):
if any(
(
self.app.sdkconfig.get('SECURE_BOOT', True),
self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED', True),
)
):
offs = int(self.app.sdkconfig.get('BOOTLOADER_OFFSET_IN_FLASH', 0))
bootloader_path = os.path.join(self.app.binary_path, 'bootloader', 'bootloader.bin')
encrypt = '--encrypt' if self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED') else ''
@@ -81,7 +93,7 @@ class TEESerial(IdfSerial):
esptool.main(
f'--no-stub write_flash {offs} {bootloader_path} --force {encrypt} --flash_size {flash_size}'.split(),
esp=self.esp
esp=self.esp,
)
@EspSerial.use_esptool()
@@ -97,20 +109,30 @@ class TEESerial(IdfSerial):
temp_file.flush()
esptool.main(
f'--no-stub write_flash {offs} {temp_file.name} --flash_size {flash_size}'.split(),
esp=self.esp
f'--no-stub write_flash {offs} {temp_file.name} --flash_size {flash_size}'.split(), esp=self.esp
)
else:
self.erase_partition(partition)
@EspSerial.use_esptool()
def copy_test_tee_img(self, partition: str, is_rollback: bool = False) -> None:
def custom_write_partition(self, partition: str, bin_path: str, encrypt: bool = False) -> None:
offs = self.app.partition_table[partition]['offset']
no_stub = '--no-stub' if self.app.sdkconfig.get('SECURE_ENABLE_SECURE_ROM_DL_MODE') else ''
encrypt = '--encrypt' if self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED') else ''
flash_size = self._get_flash_size()
flash_file = bin_path
args = f'{no_stub} write_flash {offs} {flash_file}'.split()
if encrypt:
args.append('--encrypt')
args += f'--flash_size {flash_size}'.split()
esptool.main(args, esp=self.esp)
@EspSerial.use_esptool()
def copy_test_tee_img(self, partition: str, is_rollback: bool = False) -> None:
flash_file = os.path.join(self.app.binary_path, 'esp_tee', 'esp_tee.bin')
encrypt = self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED', False)
if is_rollback:
datafile = 'esp_tee_empty.bin'
@@ -125,22 +147,27 @@ class TEESerial(IdfSerial):
if self.app.sdkconfig.get('SECURE_BOOT'):
keyfile = self.app.sdkconfig.get('SECURE_BOOT_SIGNING_KEY')
# Signing the image with espsecure
espsecure.main(
f'sign_data --version 2 --append_signatures --keyfile {keyfile} --output {datafile_signed} {datafile}'.split()
[
'sign_data',
'--version',
'2',
'--append_signatures',
'--keyfile',
keyfile,
'--output',
datafile_signed,
datafile,
]
)
flash_file = datafile_signed
esptool.main(
f'{no_stub} write_flash {offs} {flash_file} {encrypt} --flash_size {flash_size}'.split(),
esp=self.esp
)
self.custom_write_partition(partition, flash_file, encrypt=encrypt)
if is_rollback:
if os.path.exists(datafile):
os.remove(datafile)
if os.path.exists(datafile_signed):
os.remove(datafile_signed)
for file in [datafile, datafile_signed]:
if os.path.exists(file):
os.remove(file)
@EspSerial.use_esptool()
def custom_flash(self) -> None:
@@ -165,6 +192,114 @@ class TEESerial(IdfSerial):
self.flash()
self.custom_erase_partition('secure_storage')
KEY_DEFS: List[Dict[str, Any]] = [
{'key': 'aes256_key0', 'type': 'aes256', 'input': None, 'write_once': True},
{
'key': 'aes256_key1',
'type': 'aes256',
'input': 'aes256_key.bin',
'write_once': False,
'b64': 'qZxftt2T8mOpLxALIfsDqI65srqPxrJtCVnDU8wrKXbFCJekDRzXqINlU5s=',
},
{'key': 'p256_key0', 'type': 'ecdsa_p256', 'input': None, 'write_once': False},
{
'key': 'attest_key',
'type': 'ecdsa_p256',
'input': 'ecdsa_p256_key.pem',
'write_once': True,
'b64': (
'LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlNU1VpUktHaVZjSTIvbUZFekI3eXRIOVJj'
'd0wyUThkNDhONHNFUHFYc0RvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkYxYXRZQUxrdnB4cCt4N3c1dmVPQ1Vj'
'RUhFRTY5azkvcFB5eFdTbEZkbW5wMnBmbVJpZwp5NnRTMDNaM2tnN2hYcitTQmNLbmRCV2RlZW81Vm9XV29nPT0K'
'LS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo='
),
},
]
NVS_KEYS_B64 = 'MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzPMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzA=='
TMP_DIR = Path('tmp')
NVS_KEYS_PATH = TMP_DIR / 'nvs_keys.bin'
NVS_CSV_PATH = TMP_DIR / 'tee_sec_stg_val.csv'
NVS_BIN_PATH = TMP_DIR / 'tee_sec_stg_nvs.bin'
def run_command(self, command: List[str]) -> None:
try:
subprocess.check_call(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError:
print(f'Command failed: {" ".join(command)}')
raise
def write_keys_to_file(self, b64_data: str, path: Path) -> None:
path.write_bytes(base64.b64decode(b64_data))
def create_tee_sec_stg_csv(self, tmp_dir: Path) -> Path:
csv_path = self.NVS_CSV_PATH
rows: List[List[str]] = [
['key', 'type', 'encoding', 'value'],
['tee_sec_stg_ns', 'namespace', '', ''],
]
rows += [[entry['key'], 'file', 'binary', str(tmp_dir / f'{entry["key"]}.bin')] for entry in self.KEY_DEFS]
with csv_path.open('w', newline='') as f:
csv.writer(f).writerows(rows)
return csv_path
def custom_flash_with_host_gen_sec_stg_img(self) -> None:
tmp_dir = self.TMP_DIR
tmp_dir.mkdir(parents=True, exist_ok=True)
for entry in self.KEY_DEFS:
if entry['input']:
input_path = tmp_dir / entry['input']
self.write_keys_to_file(entry['b64'], input_path)
entry['input'] = str(input_path)
self.write_keys_to_file(self.NVS_KEYS_B64, self.NVS_KEYS_PATH)
idf_path = Path(os.environ['IDF_PATH'])
ESP_TEE_SEC_STG_KEYGEN = os.path.join(
idf_path, 'components', 'esp_tee', 'scripts', 'esp_tee_sec_stg_keygen', 'esp_tee_sec_stg_keygen.py'
)
NVS_PARTITION_GEN = os.path.join(
idf_path, 'components', 'nvs_flash', 'nvs_partition_generator', 'nvs_partition_gen.py'
)
cmds = [
[sys.executable, ESP_TEE_SEC_STG_KEYGEN, '-k', entry['type'], '-o', str(tmp_dir / f'{entry["key"]}.bin')]
+ (['-i', entry['input']] if entry['input'] else [])
+ (['--write-once'] if entry['write_once'] else [])
for entry in self.KEY_DEFS
]
csv_path = self.create_tee_sec_stg_csv(tmp_dir)
nvs_bin = self.NVS_BIN_PATH
nvs_keys = self.NVS_KEYS_PATH
size = self.app.partition_table['secure_storage']['size']
cmds.append(
[
sys.executable,
NVS_PARTITION_GEN,
'encrypt',
str(csv_path),
str(nvs_bin),
str(size),
'--inputkey',
str(nvs_keys),
]
)
try:
for cmd in cmds:
self.run_command(cmd)
self.bootloader_force_flash_if_req()
self.flash()
self.custom_erase_partition('secure_storage')
self.custom_write_partition('secure_storage', nvs_bin)
finally:
shutil.rmtree(tmp_dir)
@pytest.fixture(scope='module')
def monkeypatch_module(request: FixtureRequest) -> MonkeyPatch:

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -29,14 +29,14 @@
#define ECDSA_SECP256R1_KEY_LEN (32)
#define ESP_ATT_TK_BUF_SIZE (1792)
#define ESP_ATT_TK_PSA_CERT_REF ("0716053550477-10100")
#define ESP_ATT_TK_PSA_CERT_REF ("0632793520245-10010")
#define ESP_ATT_TK_NONCE (0xABCD1234)
#define ESP_ATT_TK_CLIENT_ID (0x0FACADE0)
static const char *TAG = "test_esp_tee_att";
extern int verify_ecdsa_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_pubkey_t *pubkey, const esp_tee_sec_storage_sign_t *sign, bool is_crv_p192);
extern int verify_ecdsa_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_ecdsa_pubkey_t *pubkey, const esp_tee_sec_storage_ecdsa_sign_t *sign, bool is_crv_p192);
static uint8_t hexchar_to_byte(char hex)
{
@@ -72,7 +72,7 @@ static void hexstr_to_bytes(const char *hex_str, uint8_t **hex_buf, size_t *buf_
}
}
static int decompress_ecdsa_pubkey(const mbedtls_ecp_group *grp, const unsigned char *input, size_t ilen, esp_tee_sec_storage_pubkey_t *pubkey)
static int decompress_ecdsa_pubkey(const mbedtls_ecp_group *grp, const unsigned char *input, size_t ilen, esp_tee_sec_storage_ecdsa_pubkey_t *pubkey)
{
int ret = -1;
mbedtls_mpi r, x, n;
@@ -186,7 +186,7 @@ static void prehash_token_data(const char *token_json, uint8_t *digest, size_t l
cJSON_Delete(root);
}
static void fetch_pubkey(const char *token_json, esp_tee_sec_storage_pubkey_t *pubkey_ctx)
static void fetch_pubkey(const char *token_json, esp_tee_sec_storage_ecdsa_pubkey_t *pubkey_ctx)
{
TEST_ASSERT_NOT_NULL(token_json);
TEST_ASSERT_NOT_NULL(pubkey_ctx);
@@ -218,7 +218,7 @@ static void fetch_pubkey(const char *token_json, esp_tee_sec_storage_pubkey_t *p
cJSON_Delete(root);
}
static void fetch_signature(const char *token_json, esp_tee_sec_storage_sign_t *sign_ctx)
static void fetch_signature(const char *token_json, esp_tee_sec_storage_ecdsa_sign_t *sign_ctx)
{
TEST_ASSERT_NOT_NULL(token_json);
TEST_ASSERT_NOT_NULL(sign_ctx);
@@ -267,11 +267,11 @@ TEST_CASE("Test TEE Attestation - Generate and verify the EAT", "[attestation]")
prehash_token_data((const char *)token_buf, digest, sizeof(digest));
// Fetching and decompressing the public key
esp_tee_sec_storage_pubkey_t pubkey_ctx = {};
esp_tee_sec_storage_ecdsa_pubkey_t pubkey_ctx = {};
fetch_pubkey((const char *)token_buf, &pubkey_ctx);
// Fetching the signature components
esp_tee_sec_storage_sign_t sign_ctx = {};
esp_tee_sec_storage_ecdsa_sign_t sign_ctx = {};
fetch_signature((const char *)token_buf, &sign_ctx);
// Verifying the generated token

View File

@@ -27,6 +27,8 @@
#define TEST_PART_LABEL "custom"
#define TEST_BUF_SZ 256
#define ESP_TEE_SEC_STG_PART_LABEL "secure_storage"
static const char *TAG = "test_esp_tee_flash_prot";
static void set_boot_count_in_nvs(uint8_t boot_count)
@@ -93,7 +95,7 @@ static void test_esp_partition_mmap_api(void)
TEST_FAIL_MESSAGE("System fault should have been generated");
break;
case 5:
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL);
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, ESP_TEE_SEC_STG_PART_LABEL);
TEST_ASSERT_NOT_NULL(part);
TEST_ESP_OK(esp_partition_mmap(part, 0, part->size, ESP_PARTITION_MMAP_DATA, &outptr, &out_handle));
ESP_LOG_BUFFER_HEXDUMP(TAG, outptr, 0x20, ESP_LOG_INFO);
@@ -149,7 +151,7 @@ static void test_esp_partition_api(void)
test_esp_partition_api_w(part);
break;
case 4:
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL);
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, ESP_TEE_SEC_STG_PART_LABEL);
test_esp_partition_api_w(part);
break;
case 5:
@@ -255,7 +257,7 @@ static void test_esp_flash_api(void)
test_esp_flash_api_r(part->address);
break;
case 4:
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL);
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, ESP_TEE_SEC_STG_PART_LABEL);
TEST_ASSERT_NOT_NULL(part);
test_esp_flash_api_e(part->address);
break;
@@ -327,7 +329,7 @@ static void test_esp_rom_spiflash_api(void)
TEST_FAIL_MESSAGE("System fault should have been generated");
break;
case 4:
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL);
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, ESP_TEE_SEC_STG_PART_LABEL);
TEST_ASSERT_NOT_NULL(part);
test_esp_rom_spiflash_api_e(part->address);
TEST_FAIL_MESSAGE("System fault should have been generated");

View File

@@ -30,7 +30,7 @@ extern uint32_t _instruction_reserved_start;
#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE
#define TEST_APM_EFUSE_PROT_REG \
(EFUSE_RD_KEY0_DATA0_REG + (((CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK) - 4) * 0x20))
(EFUSE_RD_KEY0_DATA0_REG + (CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID * 0x20))
#else
#define TEST_APM_EFUSE_PROT_REG EFUSE_RD_KEY5_DATA0_REG
#endif

View File

@@ -17,9 +17,12 @@
#include "esp_tee.h"
#include "esp_tee_sec_storage.h"
#include "secure_service_num.h"
#if CONFIG_SECURE_TEE_ATTESTATION
#include "esp_tee_attestation.h"
#endif
#include "esp_random.h"
#include "nvs.h"
#include "unity.h"
#include "sdkconfig.h"
@@ -31,9 +34,14 @@
#define ECDSA_SECP256R1_KEY_LEN (32)
#define ECDSA_SECP192R1_KEY_LEN (24)
#define ESP_ATT_TK_BUF_SIZE (1792)
#define ESP_ATT_TK_PSA_CERT_REF ("0632793520245-10010")
#define MAX_SEC_STG_ITER (16)
static const char *TAG = "test_esp_tee_sec_storage";
int verify_ecdsa_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_pubkey_t *pubkey, const esp_tee_sec_storage_sign_t *sign, bool is_crv_p192)
int verify_ecdsa_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_ecdsa_pubkey_t *pubkey, const esp_tee_sec_storage_ecdsa_sign_t *sign, bool is_crv_p192)
{
#if !CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
TEST_ASSERT_FALSE(is_crv_p192);
@@ -73,7 +81,7 @@ int verify_ecdsa_sign(const uint8_t *digest, size_t len, const esp_tee_sec_stora
return 0;
}
TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp256r1) with all key-slot IDs", "[secure_storage]")
TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp256r1)", "[sec_storage]")
{
const size_t buf_sz = 16 * 1024 + 6; // NOTE: Not an exact multiple of SHA block size
unsigned char *message = heap_caps_malloc(buf_sz, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
@@ -85,27 +93,34 @@ TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp256r1) with all key-
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256(message, buf_sz, msg_digest, false));
free(message);
TEST_ESP_OK(esp_tee_sec_storage_init());
esp_tee_sec_storage_key_cfg_t key_cfg = {
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1
};
for (uint16_t slot_id = 0; slot_id <= MAX_SEC_STG_SLOT_ID; slot_id++) {
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1));
for (unsigned int i = 0; i < MAX_SEC_STG_ITER; i++) {
char key_id[32];
int ret = snprintf(key_id, sizeof(key_id), "ecdsa_key_%u", i);
TEST_ASSERT_TRUE(ret > 0 && ret < sizeof(key_id));
esp_tee_sec_storage_sign_t sign = {};
TEST_ESP_OK(esp_tee_sec_storage_get_signature(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1, msg_digest, sizeof(msg_digest), &sign));
key_cfg.id = key_id;
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_get_pubkey(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1, &pubkey));
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_sign(&key_cfg, msg_digest, sizeof(msg_digest), &sign));
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, &pubkey));
TEST_ESP_OK(verify_ecdsa_sign(msg_digest, sizeof(msg_digest), &pubkey, &sign, false));
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
}
}
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp192r1) with all key-slot IDs", "[secure_storage]")
TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp192r1)", "[sec_storage]")
{
const size_t buf_sz = 16 * 1024 + 6; // NOTE: Not an exact multiple of SHA block size
unsigned char *message = heap_caps_malloc(buf_sz, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
@@ -117,27 +132,34 @@ TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp192r1) with all key-
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256(message, buf_sz, msg_digest, false));
free(message);
TEST_ESP_OK(esp_tee_sec_storage_init());
esp_tee_sec_storage_key_cfg_t key_cfg = {
.type = ESP_SEC_STG_KEY_ECDSA_SECP192R1
};
for (uint16_t slot_id = 0; slot_id <= MAX_SEC_STG_SLOT_ID; slot_id++) {
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP192R1));
for (unsigned int i = 0; i < MAX_SEC_STG_ITER; i++) {
char key_id[32];
int ret = snprintf(key_id, sizeof(key_id), "ecdsa_key_%u", i);
TEST_ASSERT_TRUE(ret > 0 && ret < sizeof(key_id));
esp_tee_sec_storage_sign_t sign = {};
TEST_ESP_OK(esp_tee_sec_storage_get_signature(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP192R1, msg_digest, sizeof(msg_digest), &sign));
key_cfg.id = key_id;
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_get_pubkey(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP192R1, &pubkey));
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_sign(&key_cfg, msg_digest, sizeof(msg_digest), &sign));
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, &pubkey));
TEST_ESP_OK(verify_ecdsa_sign(msg_digest, sizeof(msg_digest), &pubkey, &sign, true));
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
}
}
#endif
TEST_CASE("Test TEE Secure Storage - Encrypt-decrypt (aes256_gcm) with all key-slot IDs", "[secure_storage]")
TEST_CASE("Test TEE Secure Storage - Encrypt-decrypt (aes256_gcm)", "[sec_storage]")
{
const uint8_t SZ = 100;
uint8_t *plaintext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
@@ -154,21 +176,37 @@ TEST_CASE("Test TEE Secure Storage - Encrypt-decrypt (aes256_gcm) with all key-s
uint8_t aad[16];
memset(aad, 0xA5, sizeof(aad));
TEST_ESP_OK(esp_tee_sec_storage_init());
esp_tee_sec_storage_key_cfg_t key_cfg = {
.type = ESP_SEC_STG_KEY_AES256
};
for (uint16_t slot_id = 0; slot_id <= MAX_SEC_STG_SLOT_ID; slot_id++) {
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256));
for (unsigned int i = 0; i < MAX_SEC_STG_ITER; i++) {
char test_key_id[32];
int ret = snprintf(test_key_id, sizeof(test_key_id), "aes_key_%u", i);
TEST_ASSERT_TRUE(ret > 0 && ret < sizeof(test_key_id));
TEST_ESP_OK(esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad),
tag, sizeof(tag), ciphertext));
TEST_ESP_OK(esp_tee_sec_storage_decrypt(slot_id, ciphertext, SZ, aad, sizeof(aad),
tag, sizeof(tag), decryptedtext));
key_cfg.id = test_key_id;
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_aead_ctx_t aead_ctx = {
.key_id = test_key_id,
.aad = aad,
.aad_len = sizeof(aad),
};
aead_ctx.input = plaintext;
aead_ctx.input_len = SZ;
TEST_ESP_OK(esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), ciphertext));
aead_ctx.input = ciphertext;
aead_ctx.input_len = SZ;
TEST_ESP_OK(esp_tee_sec_storage_aead_decrypt(&aead_ctx, tag, sizeof(tag), decryptedtext));
TEST_ASSERT_EQUAL_HEX8_ARRAY(plaintext, decryptedtext, SZ);
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
}
free(plaintext);
@@ -176,7 +214,7 @@ TEST_CASE("Test TEE Secure Storage - Encrypt-decrypt (aes256_gcm) with all key-s
free(decryptedtext);
}
TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys", "[secure_storage]")
TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys", "[sec_storage]")
{
// Setup for ECDSA
const uint8_t SZ = 100;
@@ -187,6 +225,12 @@ TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys",
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256(message, SZ, msg_digest, false));
free(message);
const char *key_id = "key_id_misc";
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = key_id,
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1
};
// Setup for AES
uint8_t *plaintext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(plaintext);
@@ -197,127 +241,195 @@ TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys",
uint8_t aad[16];
memset(aad, 0xA5, sizeof(aad));
TEST_ESP_OK(esp_tee_sec_storage_init());
esp_tee_sec_storage_aead_ctx_t aead_ctx = {
.key_id = key_id,
.aad = aad,
.aad_len = sizeof(aad),
.input = plaintext,
.input_len = SZ
};
// Test ECDSA key with AES operation
const uint16_t slot_id = 7;
ESP_LOGI(TAG, "Slot ID: %u - Trying AES operation with ECDSA key...", slot_id);
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
ESP_LOGI(TAG, "Key ID: %s - Trying AES operation with ECDSA key...", key_cfg.id);
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
ESP_LOGW(TAG, "err: %x", err);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), ciphertext));
esp_tee_sec_storage_type_t key_type = ESP_SEC_STG_KEY_ECDSA_SECP256R1;
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, key_type));
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad),
tag, sizeof(tag), ciphertext));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
// Test AES key with ECDSA operation
ESP_LOGI(TAG, "Slot ID: %u - Trying ECDSA operation with AES key...", slot_id);
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256));
ESP_LOGI(TAG, "Key ID: %s - Trying ECDSA operation with AES key...", key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
key_cfg.type = ESP_SEC_STG_KEY_AES256;
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_sign_t sign = {};
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_get_signature(slot_id, key_type, msg_digest, sizeof(msg_digest), &sign));
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id));
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ESP_ERR(ESP_FAIL, esp_tee_sec_storage_ecdsa_sign(&key_cfg, msg_digest, sizeof(msg_digest), &sign));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
// Test with non-existent data
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad),
tag, sizeof(tag), ciphertext));
TEST_ESP_ERR(ESP_ERR_NVS_NOT_FOUND, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), ciphertext));
free(plaintext);
free(ciphertext);
}
TEST_CASE("Test TEE Secure Storage - Invalid key-slot IDs", "[secure_storage]")
TEST_CASE("Test TEE Secure Storage - Null Pointer and Zero Length", "[sec_storage]")
{
TEST_ESP_OK(esp_tee_sec_storage_init());
uint16_t slot_id = MAX_SEC_STG_SLOT_ID + 1;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1));
slot_id = MIN_SEC_STG_SLOT_ID - 1;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256));
}
TEST_CASE("Test TEE Secure Storage - Exhaust all key-slots", "[secure_storage]")
{
TEST_ESP_OK(esp_tee_sec_storage_init());
esp_err_t err = ESP_FAIL;
uint16_t slot_id = MIN_SEC_STG_SLOT_ID;
while (1) {
esp_tee_sec_storage_clear_slot(slot_id);
err = esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256);
if (err != ESP_OK) {
break;
}
TEST_ASSERT_FALSE(esp_tee_sec_storage_is_slot_empty(slot_id));
slot_id++;
}
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, err);
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(MAX_SEC_STG_SLOT_ID, ESP_SEC_STG_KEY_AES256));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(MIN_SEC_STG_SLOT_ID, ESP_SEC_STG_KEY_AES256));
uint16_t mid_slot = (MIN_SEC_STG_SLOT_ID + MAX_SEC_STG_SLOT_ID) / 2;
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(mid_slot, ESP_SEC_STG_KEY_AES256));
}
TEST_CASE("Test TEE Secure Storage - Null Pointer and Zero Length", "[secure_storage]")
{
TEST_ESP_OK(esp_tee_sec_storage_init());
const uint16_t slot_id = 7;
const char *key_id = "key_id_misc";
uint8_t data[31], tag[12];
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256));
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = key_id,
.type = ESP_SEC_STG_KEY_AES256
};
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_encrypt(slot_id, NULL, sizeof(data), NULL, 0, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_encrypt(slot_id, data, 0, NULL, 0, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_encrypt(slot_id, data, sizeof(data), NULL, 0, NULL, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_encrypt(slot_id, data, sizeof(data), NULL, 0, tag, 0, data));
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_decrypt(slot_id, NULL, sizeof(data), NULL, 0, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_decrypt(slot_id, data, 0, NULL, 0, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_decrypt(slot_id, data, sizeof(data), NULL, 0, NULL, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_decrypt(slot_id, data, sizeof(data), NULL, 0, tag, 0, data));
esp_tee_sec_storage_aead_ctx_t aead_ctx = {
.key_id = key_cfg.id,
.input = data,
.input_len = sizeof(data),
};
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_aead_encrypt(&aead_ctx, NULL, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, 0, data));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_aead_decrypt(&aead_ctx, NULL, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_aead_decrypt(&aead_ctx, tag, 0, data));
esp_tee_sec_storage_type_t key_type = ESP_SEC_STG_KEY_ECDSA_SECP256R1;
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, key_type));
aead_ctx.input = NULL;
aead_ctx.input_len = sizeof(data);
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_aead_decrypt(&aead_ctx, tag, sizeof(tag), data));
esp_tee_sec_storage_sign_t sign = {};
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_signature(slot_id, key_type, NULL, sizeof(data), &sign));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_get_signature(slot_id, key_type, data, 0, &sign));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_signature(slot_id, key_type, data, sizeof(data), NULL));
aead_ctx.input = data;
aead_ctx.input_len = 0;
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_aead_decrypt(&aead_ctx, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_pubkey(slot_id, key_type, NULL));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_id));
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
key_cfg.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1;
err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_sign(&key_cfg, NULL, sizeof(data), &sign));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_sign(&key_cfg, data, 0, &sign));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_sign(&key_cfg, data, sizeof(data), NULL));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, NULL));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
}
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C6)
TEST_CASE("Test TEE Secure Storage - Corruption from non-secure world", "[secure_storage_neg]")
TEST_CASE("Test TEE Secure Storage - WRITE_ONCE keys", "[sec_storage]")
{
const esp_partition_t *tee_sec_stg = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL);
TEST_ASSERT_NOT_NULL(tee_sec_stg);
const char *key_id = "key_id_test_wo";
uint8_t buf_w[128];
memset(buf_w, 0xA5, sizeof(buf_w));
TEST_ESP_OK(esp_partition_write(tee_sec_stg, 0x00, buf_w, sizeof(buf_w)));
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = key_id,
.type = ESP_SEC_STG_KEY_AES256,
.flags = SEC_STORAGE_FLAG_WRITE_ONCE,
};
uint8_t buf_r[128];
memset(buf_r, 0x00, sizeof(buf_r));
TEST_ESP_OK(esp_partition_read(tee_sec_stg, 0x00, buf_r, sizeof(buf_r)));
ESP_LOG_BUFFER_HEXDUMP(TAG, buf_r, sizeof(buf_r), ESP_LOG_INFO);
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_FAIL_MESSAGE("APM violation interrupt should have been generated");
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(&key_cfg));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_clear_key(key_cfg.id));
}
static void test_aead_encrypt_decrypt(const char *key_id, const uint8_t *input, size_t len)
{
uint8_t *ciphertext = heap_caps_malloc(len, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(ciphertext);
uint8_t *decrypted = heap_caps_malloc(len, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(decrypted);
uint8_t tag[12];
uint8_t aad[16];
esp_fill_random(aad, sizeof(aad));
esp_tee_sec_storage_aead_ctx_t ctx = {
.key_id = key_id,
.aad = aad,
.aad_len = sizeof(aad),
};
ctx.input = input;
ctx.input_len = len;
TEST_ESP_OK(esp_tee_sec_storage_aead_encrypt(&ctx, tag, sizeof(tag), ciphertext));
ctx.input = ciphertext;
ctx.input_len = len;
TEST_ESP_OK(esp_tee_sec_storage_aead_decrypt(&ctx, tag, sizeof(tag), decrypted));
TEST_ASSERT_EQUAL_HEX8_ARRAY(input, decrypted, len);
free(ciphertext);
free(decrypted);
}
TEST_CASE("Test TEE Secure Storage - Host-generated keys", "[sec_storage_host_keygen]")
{
const char *aes_key_id0 = "aes256_key0";
const char *aes_key_id1 = "aes256_key1";
const char *ecdsa_key_id0 = "p256_key0";
const char *attest_key_id = "attest_key";
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_clear_key(aes_key_id0));
const size_t SZ = 100;
uint8_t *plaintext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(plaintext);
esp_fill_random(plaintext, SZ);
test_aead_encrypt_decrypt(aes_key_id0, plaintext, SZ);
test_aead_encrypt_decrypt(aes_key_id1, plaintext, SZ);
free(plaintext);
TEST_ESP_OK(esp_tee_sec_storage_clear_key(aes_key_id1));
uint8_t msg_digest[SHA256_DIGEST_SZ];
esp_fill_random(msg_digest, sizeof(msg_digest));
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = ecdsa_key_id0,
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1,
};
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_sign(&key_cfg, msg_digest, sizeof(msg_digest), &sign));
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, &pubkey));
TEST_ESP_OK(verify_ecdsa_sign(msg_digest, sizeof(msg_digest), &pubkey, &sign, false));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(ecdsa_key_id0));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_clear_key(attest_key_id));
#if CONFIG_SECURE_TEE_ATTESTATION
uint8_t *token_buf = heap_caps_calloc(ESP_ATT_TK_BUF_SIZE, sizeof(uint8_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(token_buf);
uint32_t token_len = 0;
TEST_ESP_OK(esp_tee_att_generate_token(0xA1B2C3D4, 0x0FACADE0, (const char *)ESP_ATT_TK_PSA_CERT_REF,
token_buf, ESP_ATT_TK_BUF_SIZE, &token_len));
free(token_buf);
#endif
}
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
static const uint8_t sha[] = {
@@ -327,11 +439,8 @@ static const uint8_t sha[] = {
0x91, 0xbe, 0x58, 0x10, 0xfe, 0x80, 0x65, 0x6e
};
static void test_ecdsa_sign(mbedtls_ecp_group_id gid, const uint8_t *hash, int slot_id)
static void test_ecdsa_sign(mbedtls_ecp_group_id gid)
{
TEST_ESP_OK(esp_tee_sec_storage_init());
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
bool is_crv_p192 = false;
esp_tee_sec_storage_type_t key_type = ESP_SEC_STG_KEY_ECDSA_SECP256R1;
size_t key_len = ECDSA_SECP256R1_KEY_LEN;
@@ -342,10 +451,20 @@ static void test_ecdsa_sign(mbedtls_ecp_group_id gid, const uint8_t *hash, int s
key_len = ECDSA_SECP192R1_KEY_LEN;
}
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, key_type));
const char *key_id = "ecdsa_k0";
esp_tee_sec_storage_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_get_pubkey(slot_id, key_type, &pubkey));
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = key_id,
.type = key_type
};
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, &pubkey));
mbedtls_mpi r, s;
mbedtls_mpi_init(&r);
@@ -360,7 +479,7 @@ static void test_ecdsa_sign(mbedtls_ecp_group_id gid, const uint8_t *hash, int s
esp_ecdsa_pk_conf_t conf = {
.grp_id = gid,
.tee_slot_id = slot_id,
.tee_key_id = key_id,
.load_pubkey = true,
.use_tee_sec_stg_key = true,
};
@@ -371,7 +490,7 @@ static void test_ecdsa_sign(mbedtls_ecp_group_id gid, const uint8_t *hash, int s
TEST_ASSERT_MBEDTLS_OK(mbedtls_ecdsa_sign(&ecdsa_context.MBEDTLS_PRIVATE(grp), &r, &s, &key_mpi, sha, SHA256_DIGEST_SZ, NULL, NULL));
esp_tee_sec_storage_sign_t sign = {};
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_write_binary(&r, sign.sign_r, key_len));
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_write_binary(&s, sign.sign_s, key_len));
@@ -381,13 +500,15 @@ static void test_ecdsa_sign(mbedtls_ecp_group_id gid, const uint8_t *hash, int s
mbedtls_ecdsa_free(&ecdsa_context);
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
}
TEST_CASE("Test TEE Secure Storage - mbedtls ECDSA signing", "[mbedtls]")
{
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP256R1, sha, MIN_SEC_STG_SLOT_ID);
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP256R1);
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP192R1, sha, MAX_SEC_STG_SLOT_ID);
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP192R1);
#endif
}
#endif

View File

@@ -1,7 +0,0 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
tee, app, tee_0, , 192K,
secure_storage, data, tee_sec_stg, , 64K,
factory, app, factory, , 512K,
nvs, data, nvs, , 24K,
custom, data, , , 1M
1 # ESP-IDF Partition Table
2 # Name, Type, SubType, Offset, Size, Flags
3 tee, app, tee_0, , 192K,
4 secure_storage, data, tee_sec_stg, , 64K,
5 factory, app, factory, , 512K,
6 nvs, data, nvs, , 24K,
7 custom, data, , , 1M

View File

@@ -3,7 +3,7 @@
tee_0, app, tee_0, , 192K,
tee_1, app, tee_1, , 192K,
tee_otadata, data, tee_ota, , 8K,
secure_storage, data, tee_sec_stg, , 56K,
secure_storage, data, nvs, , 56K,
ota_0, app, ota_0, , 512K,
ota_1, app, ota_1, , 512K,
otadata, data, ota, , 8K,
1 # ESP-IDF Partition Table
3 tee_0, app, tee_0, , 192K,
4 tee_1, app, tee_1, , 192K,
5 tee_otadata, data, tee_ota, , 8K,
6 secure_storage, data, tee_sec_stg, , 56K, secure_storage, data, nvs, , 56K,
7 ota_0, app, ota_0, , 512K,
8 ota_1, app, ota_1, , 512K,
9 otadata, data, ota, , 8K,

View File

@@ -442,7 +442,18 @@ def test_esp_tee_secure_storage(dut: IdfDut) -> None:
# Flash image and erase the secure_storage partition
dut.serial.custom_flash_with_empty_sec_stg()
dut.run_all_single_board_cases(group='secure_storage')
dut.run_all_single_board_cases(group='sec_storage')
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
def test_esp_tee_secure_storage_with_host_img(dut: IdfDut) -> None:
# Flash image and write the secure_storage partition with host-generated keys
dut.serial.custom_flash_with_host_gen_sec_stg_img()
dut.run_all_single_board_cases(group='sec_storage_host_keygen')
# ---------------- TEE Attestation tests ----------------

View File

@@ -0,0 +1,5 @@
# Decreasing Bootloader log verbosity
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
# Disabling ATTESTATION
CONFIG_SECURE_TEE_ATTESTATION=n

View File

@@ -7,10 +7,8 @@ CONFIG_SECURE_ENABLE_TEE=y
CONFIG_SECURE_TEE_DEBUG_MODE=y
CONFIG_SECURE_TEE_TEST_MODE=y
# Custom partition table
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
# Setting partition table
CONFIG_PARTITION_TABLE_SINGLE_APP_TEE=y
# TEE IRAM size
CONFIG_SECURE_TEE_IRAM_SIZE=0x8400

View File

@@ -1,11 +1,10 @@
menu "Example Configuration"
config EXAMPLE_TEE_SEC_STG_SLOT_ID
int "TEE: Secure Storage keypair slot ID"
default 0
range 0 15
config EXAMPLE_TEE_SEC_STG_KEY_STR_ID
string "TEE: Secure Storage Key ID"
default "key_id_0"
help
This configuration sets the slot ID from the TEE secure storage
This configuration sets the key string identifier from the TEE secure storage
storing the ECDSA keypair for executing sign/verify operations
from the TEE side

View File

@@ -1,7 +1,7 @@
/*
* TEE Secure Storage example
*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -21,20 +21,21 @@
#include "esp_tee_sec_storage.h"
#include "secure_service_num.h"
#include "sdkconfig.h"
#define SHA256_DIGEST_SZ (32)
#define ECDSA_SECP256R1_KEY_LEN (32)
#define AES256_GCM_TAG_LEN (16)
#define AES256_GCM_AAD_LEN (16)
#define KEY_SLOT_ID (CONFIG_EXAMPLE_TEE_SEC_STG_SLOT_ID)
#define KEY_STR_ID (CONFIG_EXAMPLE_TEE_SEC_STG_KEY_STR_ID)
#define MAX_AES_PLAINTEXT_LEN (128)
static const char *message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
static const char *TAG = "example_tee_sec_stg";
static esp_err_t verify_ecdsa_secp256r1_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_pubkey_t *pubkey, const esp_tee_sec_storage_sign_t *sign)
static esp_err_t verify_ecdsa_secp256r1_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_ecdsa_pubkey_t *pubkey, const esp_tee_sec_storage_ecdsa_sign_t *sign)
{
if (pubkey == NULL || digest == NULL || sign == NULL) {
return ESP_ERR_INVALID_ARG;
@@ -112,20 +113,25 @@ static void example_tee_sec_stg_sign_verify(void *pvParameter)
goto exit;
}
esp_err_t err = esp_tee_sec_storage_clear_slot(KEY_SLOT_ID);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to clear slot %d!", KEY_SLOT_ID);
esp_tee_sec_storage_key_cfg_t cfg = {
.id = (const char *)(KEY_STR_ID),
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1
};
esp_err_t err = esp_tee_sec_storage_clear_key(cfg.id);
if (err != ESP_OK && err != ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to clear key %d!", cfg.id);
goto exit;
}
err = esp_tee_sec_storage_gen_key(KEY_SLOT_ID, ESP_SEC_STG_KEY_ECDSA_SECP256R1);
err = esp_tee_sec_storage_gen_key(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate keypair!");
goto exit;
}
esp_tee_sec_storage_sign_t sign = {};
err = esp_tee_sec_storage_get_signature(KEY_SLOT_ID, ESP_SEC_STG_KEY_ECDSA_SECP256R1, msg_digest, sizeof(msg_digest), &sign);
esp_tee_sec_storage_ecdsa_sign_t sign = {};
err = esp_tee_sec_storage_ecdsa_sign(&cfg, msg_digest, sizeof(msg_digest), &sign);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate signature!");
goto exit;
@@ -133,8 +139,8 @@ static void example_tee_sec_stg_sign_verify(void *pvParameter)
ESP_LOG_BUFFER_HEX("Signature", &sign, sizeof(sign));
esp_tee_sec_storage_pubkey_t pubkey = {};
err = esp_tee_sec_storage_get_pubkey(KEY_SLOT_ID, ESP_SEC_STG_KEY_ECDSA_SECP256R1, &pubkey);
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
err = esp_tee_sec_storage_ecdsa_get_pubkey(&cfg, &pubkey);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch public-key!");
goto exit;
@@ -175,19 +181,32 @@ static void example_tee_sec_stg_encrypt_decrypt(void *pvParameter)
goto exit;
}
err = esp_tee_sec_storage_clear_slot(KEY_SLOT_ID);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to clear slot %d!", KEY_SLOT_ID);
esp_tee_sec_storage_key_cfg_t cfg = {
.id = (const char *)(KEY_STR_ID),
.type = ESP_SEC_STG_KEY_AES256
};
err = esp_tee_sec_storage_clear_key(cfg.id);
if (err != ESP_OK && err != ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to clear key %d!", cfg.id);
goto exit;
}
err = esp_tee_sec_storage_gen_key(KEY_SLOT_ID, ESP_SEC_STG_KEY_AES256);
err = esp_tee_sec_storage_gen_key(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate key!");
goto exit;
}
err = esp_tee_sec_storage_encrypt(KEY_SLOT_ID, (uint8_t *)plaintext, plaintext_len, aad_buf, sizeof(aad_buf), tag, sizeof(tag), ciphertext);
esp_tee_sec_storage_aead_ctx_t ctx = {
.key_id = cfg.id,
.aad = aad_buf,
.aad_len = sizeof(aad_buf),
};
ctx.input = (const uint8_t *)plaintext;
ctx.input_len = plaintext_len;
err = esp_tee_sec_storage_aead_encrypt(&ctx, tag, sizeof(tag), ciphertext);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to encrypt data!");
goto exit;
@@ -195,7 +214,9 @@ static void example_tee_sec_stg_encrypt_decrypt(void *pvParameter)
ESP_LOG_BUFFER_HEX("Encrypted data", ciphertext, plaintext_len);
err = esp_tee_sec_storage_decrypt(KEY_SLOT_ID, (uint8_t *)ciphertext, plaintext_len, aad_buf, sizeof(aad_buf), tag, sizeof(tag), ciphertext);
ctx.input = (const uint8_t *)ciphertext;
ctx.input_len = plaintext_len;
err = esp_tee_sec_storage_aead_decrypt(&ctx, tag, sizeof(tag), ciphertext);
if (err != ESP_OK || memcmp(ciphertext, plaintext, plaintext_len) != 0) {
ESP_LOGE(TAG, "Encryption verification failed!");
err = ESP_FAIL;
@@ -216,8 +237,6 @@ void app_main(void)
{
ESP_LOGI(TAG, "TEE Secure Storage");
ESP_ERROR_CHECK(esp_tee_sec_storage_init());
xTaskCreate(example_tee_sec_stg_sign_verify, "tee_sec_stg_sign_verify", 4096, (void *)message, 5, NULL);
xTaskCreate(example_tee_sec_stg_encrypt_decrypt, "tee_sec_stg_encrypt_decrypt", 4096, (void *)message, 5, NULL);
}