feat(examples/security): Add example to demonstrate the usage of custom key storages with PSA

This commit is contained in:
harshal.patil
2026-03-30 14:46:10 +05:30
parent 3c4586abff
commit bac955ae9d
10 changed files with 640 additions and 0 deletions

View File

@@ -52,6 +52,16 @@ examples/security/nvs_encryption_hmac:
depends_filepatterns:
- examples/security/nvs_encryption_hmac/**/*
examples/security/psa_its_custom_backend:
disable:
- if: IDF_TARGET not in ["esp32"]
reason: example is target-agnostic, one chip is enough for CI
depends_components:
- mbedtls
- nvs_flash
depends_filepatterns:
- examples/security/psa_its_custom_backend/**/*
examples/security/security_features_app:
disable:
- if: IDF_TARGET not in ["esp32c3", "esp32s3"]

View File

@@ -0,0 +1,9 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(psa_its_custom_backend)

View File

@@ -0,0 +1,112 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# PSA ITS Custom Storage Backend
## Overview
This example demonstrates the PSA ITS (Internal Trusted Storage) custom storage backend feature, which lets the application route persistent PSA Crypto keys to a user-defined storage implementation based on the key ID range.
By default, all persistent PSA keys are stored in NVS (Non-Volatile Storage) under the framework-owned `psa_its` namespace. With the custom storage backend enabled, a configurable range of key IDs is routed to user-registered callbacks where the application controls where and how those keys are stored.
This example registers a single custom backend that handles one key ID range:
| Key ID Range | Storage Backend | Description |
|---|---|---|
| `0x00000001` - `0x2FFFFFFF` | Default NVS (`psa_its`) | Handled by the framework, no custom code needed |
| `0x30000000` - `0x3FFFFFFF` | Custom NVS namespace (`psa_its_ext`) | Implemented in this example, stores only the raw key bytes — no PSA wrapper, no attributes, no metadata |
Two persistent AES-256 keys are provisioned — one per storage path — and used for AES-CBC encrypt/decrypt to verify correct operation.
### How the custom backend stores keys
PSA Crypto hands the backend a fully-formatted PSA persistent key blob in `set()`. This example unpacks the blob with the upstream helper `psa_parse_key_data_from_storage()` to extract just the inner key bytes and persists *only those bytes* — no PSA wrapper, no attribute trailer, no metadata at all. On `get()`, the backend reads the key bytes back, synthesises a `psa_key_attributes_t` from hardcoded values, and rebuilds the PSA blob with `psa_format_key_data_for_storage()` before returning it to PSA Crypto.
The two upstream helpers (`psa_parse_key_data_from_storage` / `psa_format_key_data_for_storage`) are the canonical way for custom backends to manipulate the PSA blob format without re-implementing it.
This is the minimum on-disk footprint possible — but the trade-off is that the backend commits to a fixed key shape. The attributes it reports on read must match what the application uses when calling `psa_generate_key()` / `psa_import_key()` for keys in the custom UID range. In this example that's AES-256 / CBC / encrypt+decrypt, encoded as a few `#define`s in `custom_nvs_its_backend.c`. A backend that needs to serve heterogeneous keys would persist the attributes too — see the comment block at the top of the backend source for pointers.
> Note: `PSA_STORAGE_FLAG_WRITE_ONCE` is intentionally not tracked in this minimal example.
## How to use the example
### Hardware Required
This example can be executed on any ESP32 development board.
### Configure the project
Set the correct chip target:
```
idf.py set-target <chip_name>
```
The default configuration in `sdkconfig.defaults` enables the custom storage backend with the key ID range `0x30000000` - `0x3FFFFFFF`. These can be adjusted via `idf.py menuconfig`:
- `Component Config` > `mbedTLS` > `Enable custom storage backend for PSA ITS`
- `Component Config` > `mbedTLS` > `Minimum UID for custom backend range`
- `Component Config` > `mbedTLS` > `Maximum UID for custom backend range`
### Build and Flash
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type `Ctrl-]`.)
### Example Output
First boot (keys generated):
```log
I (286) example: === PSA ITS Custom Storage Backend ===
I (306) example: Custom ITS backend registered
I (306) example: --- Provisioning keys ---
I (416) example: [NVS] Generated persistent key 0x1
I (436) example: [CUSTOM] Generated persistent key 0x30000001
I (436) example: --- Testing encryption/decryption ---
I (446) example: [NVS] Encrypt/decrypt OK with key 0x1
I (456) example: [CUSTOM] Encrypt/decrypt OK with key 0x30000001
I (456) example: === All tests passed ===
```
Subsequent boots (keys loaded from storage):
```log
I (286) example: === PSA ITS Custom Storage Backend ===
I (306) example: Custom ITS backend registered
I (306) example: --- Provisioning keys ---
I (306) example: [NVS] Key 0x1 already exists
I (316) example: [CUSTOM] Key 0x30000001 already exists
I (316) example: --- Testing encryption/decryption ---
I (326) example: [NVS] Encrypt/decrypt OK with key 0x1
I (336) example: [CUSTOM] Encrypt/decrypt OK with key 0x30000001
I (336) example: === All tests passed ===
```
## Project Structure
```
main/
app_main.c - Application entry point, registers the custom backend
custom_nvs_its_backend.c/h - Custom NVS namespace storage implementation
partitions_example.csv - Partition table
```
## Adding a New Storage Backend
To replace or extend the custom backend with another storage (e.g., SPIFFS, a secure element, a filesystem on an external chip):
1. Implement the four storage primitives (`set`, `get`, `get_info`, `remove`) so their signatures match `esp_psa_its_custom_ops_t`.
2. Bind them in `app_main.c` when filling the `esp_psa_its_custom_ops_t` instance.
3. Pass that instance to `esp_psa_its_register_custom_backend()`.
The framework imposes no on-disk format on custom backends. A few common patterns:
- **Store the PSA blob verbatim** — minimal code, ~32 bytes of PSA wrapper per entry. Suitable when storage size isn't a concern and the backend should be agnostic to key shape.
- **Unpack-store-repack with hardcoded attributes** (used by this example) — call `psa_parse_key_data_from_storage()` in `set()` to extract just the inner key bytes, persist only those, then call `psa_format_key_data_for_storage()` in `get()` to rebuild the PSA blob using attributes the backend knows in advance. Smallest on-disk footprint; the backend commits to a single key shape.
- **Unpack-store-repack with persisted attributes** — same as above but the backend also stores the attributes (e.g., as a small trailer or sidecar entry). Larger on-disk footprint than hardcoded, but the backend can serve arbitrary key shapes.
If you want to dispatch a single registered custom backend across multiple internal storage targets, do the routing inside your `set`/`get`/`get_info`/`remove` implementations (e.g., switch on `uid` sub-ranges) — the framework only sees one set of callbacks.

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "app_main.c" "custom_nvs_its_backend.c"
INCLUDE_DIRS "."
PRIV_REQUIRES mbedtls nvs_flash)

View File

@@ -0,0 +1,165 @@
/*
* PSA ITS custom storage backend example
*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*
* Demonstrates the PSA ITS custom storage backend feature:
*
* Key IDs 0x00000001 .. 0x2FFFFFFF → default NVS backend (framework)
* Key IDs 0x30000000 .. 0x3FFFFFFF → user-registered custom backend
* (this example: a separate NVS
* namespace "psa_its_ext")
*
* The custom UID range is configured via Kconfig
* (CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_{MIN,MAX}).
*/
#include <stdio.h>
#include <string.h>
#include "esp_err.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "psa/crypto.h"
#include "esp_psa_its.h"
#include "custom_nvs_its_backend.h"
static const char *TAG = "example";
/* ---- Key IDs ---- */
/* Default NVS range (handled by the framework) */
#define NVS_KEY_ID ((psa_key_id_t) 0x00000001)
/* Custom range routed to the registered backend */
#define CUSTOM_KEY_ID ((psa_key_id_t) 0x30000001)
/* ---- Helpers ---- */
static psa_status_t provision_aes_key(psa_key_id_t key_id, const char *label)
{
psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t handle;
psa_status_t status;
status = psa_get_key_attributes(key_id, &attr);
if (status == PSA_SUCCESS) {
ESP_LOGI(TAG, "[%s] Key 0x%lx already exists", label, (unsigned long)key_id);
psa_reset_key_attributes(&attr);
return PSA_SUCCESS;
}
psa_reset_key_attributes(&attr);
psa_set_key_id(&attr, key_id);
psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
psa_set_key_bits(&attr, 256);
psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
psa_set_key_algorithm(&attr, PSA_ALG_CBC_NO_PADDING);
status = psa_generate_key(&attr, &handle);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "[%s] Failed to generate key 0x%lx: %d",
label, (unsigned long)key_id, (int)status);
} else {
ESP_LOGI(TAG, "[%s] Generated persistent key 0x%lx",
label, (unsigned long)key_id);
}
psa_reset_key_attributes(&attr);
return status;
}
static psa_status_t test_encrypt_decrypt(psa_key_id_t key_id, const char *label)
{
const uint8_t plaintext[32] = "Hello PSA ITS custom backend!!";
uint8_t ciphertext[16 + sizeof(plaintext)];
uint8_t decrypted[sizeof(plaintext)];
size_t ciphertext_len = 0;
size_t decrypted_len = 0;
psa_status_t status = psa_cipher_encrypt(key_id, PSA_ALG_CBC_NO_PADDING,
plaintext, sizeof(plaintext),
ciphertext, sizeof(ciphertext),
&ciphertext_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "[%s] Encrypt failed: %d", label, (int)status);
return status;
}
status = psa_cipher_decrypt(key_id, PSA_ALG_CBC_NO_PADDING,
ciphertext, ciphertext_len,
decrypted, sizeof(decrypted),
&decrypted_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "[%s] Decrypt failed: %d", label, (int)status);
return status;
}
if (decrypted_len != sizeof(plaintext) ||
memcmp(decrypted, plaintext, sizeof(plaintext)) != 0) {
ESP_LOGE(TAG, "[%s] Decrypted data mismatch!", label);
return PSA_ERROR_CORRUPTION_DETECTED;
}
ESP_LOGI(TAG, "[%s] Encrypt/decrypt OK with key 0x%lx",
label, (unsigned long)key_id);
return PSA_SUCCESS;
}
/* ---- Application entry ---- */
void app_main(void)
{
psa_status_t status;
ESP_LOGI(TAG, "=== PSA ITS Custom Storage Backend ===");
/* Initialize NVS (used by both the default backend and our custom backend,
* which keep their entries in distinct namespaces). */
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
/* Register the custom backend. The signatures of custom_nvs_its_*
* match esp_psa_its_custom_ops_t exactly, so they can be bound directly. */
static const esp_psa_its_custom_ops_t custom_ops = {
.set = custom_nvs_its_set,
.get = custom_nvs_its_get,
.get_info = custom_nvs_its_get_info,
.remove = custom_nvs_its_remove,
.ctx = NULL,
};
status = esp_psa_its_register_custom_backend(&custom_ops);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed to register custom backend: %d", (int)status);
return;
}
ESP_LOGI(TAG, "Custom ITS backend registered");
/* Provision and exercise one key per storage path. */
ESP_LOGI(TAG, "--- Provisioning keys ---");
if (provision_aes_key(NVS_KEY_ID, "NVS") != PSA_SUCCESS) {
return;
}
if (provision_aes_key(CUSTOM_KEY_ID, "CUSTOM") != PSA_SUCCESS) {
return;
}
ESP_LOGI(TAG, "--- Testing encryption/decryption ---");
if (test_encrypt_decrypt(NVS_KEY_ID, "NVS") != PSA_SUCCESS) {
return;
}
if (test_encrypt_decrypt(CUSTOM_KEY_ID, "CUSTOM") != PSA_SUCCESS) {
return;
}
ESP_LOGI(TAG, "=== All tests passed ===");
}

View File

@@ -0,0 +1,233 @@
/*
* Custom NVS storage implementation for PSA ITS blobs
*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*
* Demonstrates the most stripped-down on-disk format possible: only the raw
* key material is persisted in a separate NVS namespace ("psa_its_ext").
* Nothing else — no PSA wrapper, no attribute trailer, no metadata.
*/
#include <string.h>
#include <stdlib.h>
#include "custom_nvs_its_backend.h"
#include "psa/crypto.h"
#include "esp_psa_key_file.h"
#include "nvs.h"
#define EXT_NVS_NAMESPACE "psa_its_ext"
#define EXT_NVS_KEY_LEN 14
/* Hardcoded attributes for keys served by this backend. They must match
* what the application uses when calling psa_generate_key() / psa_import_key()
* for keys in the custom UID range — PSA Crypto validates the attributes
* we report against the operation being requested. */
#define BACKEND_KEY_LIFETIME PSA_KEY_LIFETIME_PERSISTENT
#define BACKEND_KEY_TYPE PSA_KEY_TYPE_AES
#define BACKEND_KEY_USAGE (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT)
#define BACKEND_KEY_ALG PSA_ALG_CBC_NO_PADDING
static void uid_to_nvs_key(psa_storage_uid_t uid, char *key)
{
static const char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
for (int i = 0; i < 13; i++) {
key[12 - i] = base32[uid & 0x1F];
uid >>= 5;
}
key[13] = '\0';
}
/* Fill psa_key_attributes_t for a stored key whose material is N bytes long.
* Bits is derived from N; everything else is the backend's fixed contract. */
static void fill_hardcoded_attributes(psa_key_attributes_t *attr, size_t key_data_len)
{
psa_set_key_lifetime(attr, BACKEND_KEY_LIFETIME);
psa_set_key_type(attr, BACKEND_KEY_TYPE);
psa_set_key_bits(attr, key_data_len * 8);
psa_set_key_usage_flags(attr, BACKEND_KEY_USAGE);
psa_set_key_algorithm(attr, BACKEND_KEY_ALG);
}
/* ---- Public storage operations ---- */
psa_status_t custom_nvs_its_set(void *ctx, psa_storage_uid_t uid, uint32_t data_length,
const void *p_data, psa_storage_create_flags_t create_flags)
{
(void)ctx;
(void)create_flags; /* WRITE_ONCE not tracked in this minimal example */
/* Unpack the incoming PSA blob — we keep only the inner key material
* and drop the attributes, because we already know what kind of key
* this backend stores. */
psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
const uint8_t *key_data = NULL;
size_t key_data_len = 0;
psa_status_t status = esp_psa_key_file_unpack(p_data, data_length,
&attr, &key_data, &key_data_len);
psa_reset_key_attributes(&attr);
if (status != PSA_SUCCESS) {
return status;
}
nvs_handle_t handle;
char nvs_key[EXT_NVS_KEY_LEN];
uid_to_nvs_key(uid, nvs_key);
esp_err_t err = nvs_open(EXT_NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) {
return PSA_ERROR_STORAGE_FAILURE;
}
err = nvs_set_blob(handle, nvs_key, key_data, key_data_len);
if (err == ESP_OK) {
err = nvs_commit(handle);
}
nvs_close(handle);
return (err == ESP_OK) ? PSA_SUCCESS : PSA_ERROR_STORAGE_FAILURE;
}
psa_status_t custom_nvs_its_get(void *ctx, psa_storage_uid_t uid, uint32_t data_offset,
uint32_t data_length, void *p_data, size_t *p_data_length)
{
(void)ctx;
nvs_handle_t handle;
char nvs_key[EXT_NVS_KEY_LEN];
uid_to_nvs_key(uid, nvs_key);
esp_err_t err = nvs_open(EXT_NVS_NAMESPACE, NVS_READONLY, &handle);
if (err == ESP_ERR_NVS_NOT_FOUND) {
return PSA_ERROR_DOES_NOT_EXIST;
}
if (err != ESP_OK) {
return PSA_ERROR_STORAGE_FAILURE;
}
size_t key_data_len = 0;
err = nvs_get_blob(handle, nvs_key, NULL, &key_data_len);
if (err != ESP_OK) {
nvs_close(handle);
return (err == ESP_ERR_NVS_NOT_FOUND) ? PSA_ERROR_DOES_NOT_EXIST
: PSA_ERROR_STORAGE_FAILURE;
}
uint8_t *key_data = calloc(1, key_data_len);
if (key_data == NULL) {
nvs_close(handle);
return PSA_ERROR_INSUFFICIENT_MEMORY;
}
err = nvs_get_blob(handle, nvs_key, key_data, &key_data_len);
nvs_close(handle);
if (err != ESP_OK) {
free(key_data);
return PSA_ERROR_STORAGE_FAILURE;
}
/* Reconstruct the PSA blob: hardcoded attributes + stored key bytes. */
psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
fill_hardcoded_attributes(&attr, key_data_len);
size_t blob_size = esp_psa_key_file_size(key_data_len);
if (data_offset + data_length < data_offset ||
data_offset + data_length > blob_size) {
free(key_data);
psa_reset_key_attributes(&attr);
return PSA_ERROR_INVALID_ARGUMENT;
}
uint8_t *blob = calloc(1, blob_size);
if (blob == NULL) {
free(key_data);
psa_reset_key_attributes(&attr);
return PSA_ERROR_INSUFFICIENT_MEMORY;
}
size_t written = 0;
psa_status_t status = esp_psa_key_file_pack(&attr, key_data, key_data_len,
blob, blob_size, &written);
if (status != PSA_SUCCESS) {
free(blob);
free(key_data);
psa_reset_key_attributes(&attr);
return status;
}
if (data_length > 0 && p_data != NULL) {
memcpy(p_data, blob + data_offset, data_length);
}
if (p_data_length != NULL) {
*p_data_length = data_length;
}
free(blob);
free(key_data);
psa_reset_key_attributes(&attr);
return PSA_SUCCESS;
}
psa_status_t custom_nvs_its_get_info(void *ctx, psa_storage_uid_t uid,
struct psa_storage_info_t *p_info)
{
(void)ctx;
nvs_handle_t handle;
char nvs_key[EXT_NVS_KEY_LEN];
uid_to_nvs_key(uid, nvs_key);
esp_err_t err = nvs_open(EXT_NVS_NAMESPACE, NVS_READONLY, &handle);
if (err == ESP_ERR_NVS_NOT_FOUND) {
return PSA_ERROR_DOES_NOT_EXIST;
}
if (err != ESP_OK) {
return PSA_ERROR_STORAGE_FAILURE;
}
size_t key_data_len = 0;
err = nvs_get_blob(handle, nvs_key, NULL, &key_data_len);
nvs_close(handle);
if (err == ESP_ERR_NVS_NOT_FOUND) {
return PSA_ERROR_DOES_NOT_EXIST;
}
if (err != ESP_OK) {
return PSA_ERROR_STORAGE_FAILURE;
}
/* PSA expects the size of the synthesized PSA blob, not the on-disk size. */
p_info->size = (uint32_t)esp_psa_key_file_size(key_data_len);
p_info->flags = 0; /* WRITE_ONCE not tracked in this minimal example */
return PSA_SUCCESS;
}
psa_status_t custom_nvs_its_remove(void *ctx, psa_storage_uid_t uid)
{
(void)ctx;
nvs_handle_t handle;
char nvs_key[EXT_NVS_KEY_LEN];
uid_to_nvs_key(uid, nvs_key);
esp_err_t err = nvs_open(EXT_NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err == ESP_ERR_NVS_NOT_FOUND) {
return PSA_ERROR_DOES_NOT_EXIST;
}
if (err != ESP_OK) {
return PSA_ERROR_STORAGE_FAILURE;
}
err = nvs_erase_key(handle, nvs_key);
if (err == ESP_OK) {
err = nvs_commit(handle);
}
nvs_close(handle);
if (err == ESP_ERR_NVS_NOT_FOUND) {
return PSA_ERROR_DOES_NOT_EXIST;
}
return (err == ESP_OK) ? PSA_SUCCESS : PSA_ERROR_STORAGE_FAILURE;
}

View File

@@ -0,0 +1,44 @@
/*
* NVS storage implementation for PSA ITS blobs (separate namespace)
*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*
* Signatures match esp_psa_its_custom_ops_t so these functions can be
* assigned directly to the ops struct in app_main.c.
*/
#pragma once
#include "psa/internal_trusted_storage.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Unpack the incoming PSA blob and persist only the inner key bytes under
* `uid` in the custom NVS namespace. create_flags is ignored — this example
* does not track PSA_STORAGE_FLAG_WRITE_ONCE. */
psa_status_t custom_nvs_its_set(void *ctx, psa_storage_uid_t uid,
uint32_t data_length, const void *p_data,
psa_storage_create_flags_t create_flags);
/* Load the stored key bytes for `uid`, synthesize a PSA blob using hardcoded
* attributes for that UID, and copy `data_length` bytes from `data_offset`
* into `p_data`. */
psa_status_t custom_nvs_its_get(void *ctx, psa_storage_uid_t uid,
uint32_t data_offset, uint32_t data_length,
void *p_data, size_t *p_data_length);
/* Report the size of the synthesized PSA blob for `uid`. flags is always
* reported as 0 since create_flags is not tracked. */
psa_status_t custom_nvs_its_get_info(void *ctx, psa_storage_uid_t uid,
struct psa_storage_info_t *p_info);
/* Remove the key bytes stored under `uid` from the custom NVS namespace. */
psa_status_t custom_nvs_its_remove(void *ctx, psa_storage_uid_t uid);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,

View File

@@ -0,0 +1,50 @@
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
"""Pytest for PSA ITS custom storage backend example."""
import logging
import os
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
@pytest.mark.generic
@idf_parametrize('target', ['esp32'], indirect=['target'])
def test_psa_its_custom_backend(dut: Dut) -> None:
binary_file = os.path.join(dut.app.binary_path, 'psa_its_custom_backend.bin')
bin_size = os.path.getsize(binary_file)
logging.info('psa_its_custom_backend_bin_size: %dKB', bin_size // 1024)
dut.expect(r'example: === PSA ITS Custom Storage Backend ===', timeout=60)
dut.expect(r'example: Custom ITS backend registered', timeout=60)
dut.expect(r'example: --- Provisioning keys ---', timeout=60)
# Default NVS-backed key
match = dut.expect(
r'example: \[NVS\] Generated persistent key 0x1|example: \[NVS\] Key 0x1 already exists',
timeout=60,
)
if b'Generated' in match.group(0):
logging.info('NVS key generated for the first time')
else:
logging.info('NVS key already existed in storage')
# Custom-backend key
match = dut.expect(
r'example: \[CUSTOM\] Generated persistent key 0x30000001|'
r'example: \[CUSTOM\] Key 0x30000001 already exists',
timeout=60,
)
if b'Generated' in match.group(0):
logging.info('Custom-backend key generated for the first time')
else:
logging.info('Custom-backend key already existed in storage')
dut.expect(r'example: --- Testing encryption/decryption ---', timeout=60)
dut.expect(r'example: \[NVS\] Encrypt/decrypt OK with key 0x1', timeout=60)
dut.expect(r'example: \[CUSTOM\] Encrypt/decrypt OK with key 0x30000001', timeout=60)
dut.expect(r'example: === All tests passed ===', timeout=60)

View File

@@ -0,0 +1,9 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
# Enable PSA ITS custom storage backend (Kconfig: MBEDTLS_PSA_ITS_*)
CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND=y
CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MIN=0x30000000
CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MAX=0x3FFFFFFF