fix(esp_tee): Prevent IV reuse in the TEE secure storage AES-GCM service

This commit is contained in:
Laukik Hase
2026-02-05 13:45:21 +05:30
parent defd9faf49
commit 7b3e2b82bb
13 changed files with 78 additions and 63 deletions
@@ -97,11 +97,12 @@ tee_sec_stg_encrypt <key_id> <plaintext>
<key_id> TEE Secure storage key ID
<plaintext> Plaintext to be encrypted
tee_sec_stg_decrypt <key_id> <ciphertext> <tag>
tee_sec_stg_decrypt <key_id> <ciphertext> <tag> <iv>
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
<iv> AES-GCM initialization vector
help [<string>] [-v <0|1>]
Print the summary of all registered commands if no arguments are given,
@@ -135,8 +136,8 @@ I (8180) tee_attest: Attestation token - Data:
- The TEE secure storage service provides the following commands:
- `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
- `tee_sec_stg_encrypt`: Encrypt data with AES256-GCM using the key with the specified ID and outputs the ciphertext, tag and the IV used
- `tee_sec_stg_decrypt`: Decrypt ciphertext using key with the specified ID, tag and used IV 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>
@@ -163,14 +164,16 @@ I (6444) tee_sec_stg: Signature verified successfully!
```log
esp32c6> tee_sec_stg_gen_key aes256_k0 0
I (2784) tee_sec_stg: Generated AES256 key with ID key0
I (2784) tee_sec_stg: Generated AES256 key with ID aes256_k0
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 aes256_k0 58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1 caeedb43e08dc3b4e35a58b2412908cc
I (4314) tee_sec_stg: Decrypted plaintext -
40ff09c61af2f94611fb605806489380132b0000f2c63863366aad56ad327e95
I (3084) tee_sec_stg: Tag -
8136e8bfc3c70ca792fa486b3eeca72b
I (3084) tee_sec_stg: IV -
0f202954f1a1a138a2ab8b06
esp32c6> tee_sec_stg_decrypt aes256_k0 40ff09c61af2f94611fb605806489380132b0000f2c63863366aad56ad327e95 8136e8bfc3c70ca792fa486b3eeca72b 0f202954f1a1a138a2ab8b06
I (3594) tee_sec_stg: Decrypted plaintext -
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
```
@@ -33,7 +33,7 @@ static void setup_console(void)
* This can be customized, made dynamic, etc.
*/
repl_config.prompt = PROMPT_STR ">";
repl_config.max_cmdline_length = 128;
repl_config.max_cmdline_length = 256;
/* Register help command */
ESP_ERROR_CHECK(esp_console_register_help_command());
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -416,8 +416,12 @@ static int tee_sec_stg_encrypt(int argc, char **argv)
char tag_hexstr[AES256_GCM_TAG_LEN * 2 + 1];
hexbuf_to_hexstr(tag, sizeof(tag), tag_hexstr, sizeof(tag_hexstr));
char iv_hexstr[AES_GCM_SUPPORTED_IV_LEN * 2 + 1];
hexbuf_to_hexstr(ctx.iv, sizeof(ctx.iv), iv_hexstr, sizeof(iv_hexstr));
ESP_LOGI(TAG, "Ciphertext -\n%s", ciphertext);
ESP_LOGI(TAG, "Tag -\n%s", tag_hexstr);
ESP_LOGI(TAG, "IV -\n%s", iv_hexstr);
free(plaintext_buf);
free(ciphertext_buf);
@@ -448,6 +452,7 @@ static struct {
struct arg_str *key_str_id;
struct arg_str *ciphertext;
struct arg_str *tag;
struct arg_str *iv;
struct arg_end *end;
} tee_sec_stg_decrypt_args;
@@ -466,6 +471,10 @@ static int tee_sec_stg_decrypt(int argc, char **argv)
uint8_t tag[AES256_GCM_TAG_LEN];
hexstr_to_hexbuf(tag_hexstr, strlen(tag_hexstr), tag, sizeof(tag));
const char *iv_hexstr = tee_sec_stg_decrypt_args.iv->sval[0];
uint8_t iv[AES_GCM_SUPPORTED_IV_LEN];
hexstr_to_hexbuf(iv_hexstr, strlen(iv_hexstr), iv, sizeof(iv));
const char *ciphertext = tee_sec_stg_decrypt_args.ciphertext->sval[0];
size_t ciphertext_len = strnlen(ciphertext, MAX_AES_PLAINTEXT_LEN);
if (ciphertext_len == MAX_AES_PLAINTEXT_LEN && ciphertext[MAX_AES_PLAINTEXT_LEN] != '\0') {
@@ -499,6 +508,8 @@ static int tee_sec_stg_decrypt(int argc, char **argv)
.input = (uint8_t *)ciphertext_buf,
.input_len = ciphertext_buf_len
};
/* Copying the IV generated during encryption */
memcpy(ctx.iv, iv, sizeof(iv));
err = esp_tee_sec_storage_aead_decrypt(&ctx, tag, sizeof(tag), plaintext_buf);
if (err != ESP_OK) {
@@ -528,7 +539,8 @@ void register_srv_sec_stg_decrypt(void)
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);
tee_sec_stg_decrypt_args.iv = arg_str1(NULL, NULL, "<iv>", "AES-GCM initialization vector");
tee_sec_stg_decrypt_args.end = arg_end(4);
const esp_console_cmd_t tee_sec_stg = {
.command = "tee_sec_stg_decrypt",
@@ -77,8 +77,9 @@ def test_tee_cli_secure_storage(dut: Dut) -> None:
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()
test_msg_iv = dut.expect(r'IV -\s*([0-9a-fA-F]{24})', timeout=30)[1].decode()
dut.write(f'tee_sec_stg_decrypt {sec_stg_key_ids.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_iv}')
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