SE Manager#
The Secure Engine (SE) Manager provides thread-safe APIs for the Secure Engine's mailbox interface. Note that PSA Crypto is the recommended device independent crypto API and should be used whenever possible. The SE manager APIs can be used directly for performance or space constrained applications.
Available functionality will vary between devices: device management, such as secure firmware upgrade, secure boot and secure debug implementation, is available on all series 2 devices. Devices with the SE subsystem includes a low level crypto API where the SE Manager will use the SE hardware peripherals to accelerate cryptographic operations. Finally, Vault High devices also include secure key storage functionality, anti-tamper protection, advanced crypto API and attestation.
Note
Below are some of the useful application notes linked with Secure Engine Manager:
Functionality#
The functionality of the SE Manager includes
Core API, inititalizing of SE Manager and SE command context (Core)
Secure key storage (Key handling)
Key wrapping
Storing keys in the SE volatile storage
Using key by reference
Configuration of tamper responses (Utilities)
The available signals include core hard-fault, glitch detectors, PRS, and failed authenticated commands, while the responses vary from triggering an interrupt to the hardware autonomously erasing the one-time-programmable (OTP) memory
Block ciphers (Cipher)
Supports AES-ECB, AES-CBC, AES-CFB128, AES-CFB8, AES-CTR, AES-CCM, AES-GCM, CMAC, HMAC and ChaCha20/Poly1305
The cipher operations can be performed using plaintext keys, wrapped keys or referencing a key in the SE
Streaming operations are supported for AES-GCM and CMAC
Block and streaming hash operations (Hashing)
Supports SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512
True Random Number Generator (Entropy)
Hardware block inside the SE used for generating random numbers. Can be used as a source of entropy, or to securely generate keys inside the SE
Elliptic Curve Signature operation (Signature)
ECDSA and EDDSA
Key agreement (Key derivation)
Perform Elliptic Curve Diffie-Hellman and J-PAKE key agreement operations inside the SE
Key derivation functions (Key derivation)
Perform HKDF and PBKDF2 key derivation functions inside the SE
Device configuration and utilities (Utilities)
Write to user data stored inside the SE
Configuration of debug locks
Configuration of secure boot
Configuration of flash lock
Read SE OTP contents
Read SE firmware version
Read provisioned certificates
Multi-thread safe APIs for MicriumOS and FreeRTOS
Retrieveing attestation tokens (Attestation)
Key Storage and Use of SE Wrapped Keys#
The way keys are stored and operated on depends on the options set in the key descriptor used (sl_se_key_descriptor_t). Each key descriptor is initialized with a storage location, a key type, and length of the key (some key types have a known length, and it is not required to be set). The storage location can either be application memory or inside the SE, for more details, see sl_se_storage_method_t. Depending on the use-case, the key descriptors will also store the pointer to a key and an SE key slot, see sl_se_key_slot_t for the list of available internal SE key slots.
For more information on the key handling APIs see Key handling.
Supported Key Types#
Symmetric keys
AES-128 (16 bytes)
AES-192 (24 bytes)
AES-256 (32 bytes)
ChaCha20 (32 bytes)
Asymmetric keys for ECC
NIST P-192
NIST P-256
NIST P-384
NIST P-521
Curve25519
Curve448
Custom Weierstrass Prime curves are also supported (sl_se_custom_weierstrass_prime_domain_t).
Example Usage of Keys#
#define WRAPPED_KEY_OVERHEAD (12UL + 16UL)
#define AES_256_KEY_SIZE 32UL
uint8_t key_buffer[AES_256_KEY_SIZE];
uint8_t wrapped_key_buffer[AES_256_KEY_SIZE + WRAPPED_KEY_OVERHEAD];
void demo_se_create_key_in_slot(void) {
sl_se_key_descriptor_t new_key = {
.type = SL_SE_KEY_TYPE_AES_256,
.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE,
.storage.method = SL_SE_KEY_STORAGE_INTERNAL_VOLATILE,
.storage.location.slot = SL_SE_KEY_SLOT_VOLATILE_0,
};
sl_se_generate_key(&new_key);
}
void demo_se_create_plaintext_key(void) {
sl_se_key_descriptor_t new_key = {
.type = SL_SE_KEY_TYPE_AES_256,
.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_PLAINTEXT,
};
new_key.storage.location.buffer.pointer = key_buffer;
new_key.storage.location.buffer.size = sizeof(key_buffer);
sl_se_generate_key(&new_key);
}
void demo_se_create_wrapped_key(void) {
sl_se_key_descriptor_t new_wrapped_key = {
.type = SL_SE_KEY_TYPE_AES_256,
.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE,
.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_WRAPPED,
};
new_wrapped_key.storage.location.buffer.pointer = wrapped_key_buffer;
new_wrapped_key.storage.location.buffer.size = sizeof(wrapped_key_buffer);
sl_se_generate_key(&new_wrapped_key);
}
void demo_se_wrapped_key_to_volatile_slot(void) {
sl_se_key_descriptor_t existing_wrapped_key = {
.type = SL_SE_KEY_TYPE_AES_256,
.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE,
.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_WRAPPED,
};
existing_wrapped_key.storage.location.buffer.pointer = wrapped_key_buffer;
existing_wrapped_key.storage.location.buffer.size = sizeof(wrapped_key_buffer);
sl_se_key_descriptor_t key_in_slot = {
.type = SL_SE_KEY_TYPE_AES_256,
.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE,
.storage.method = SL_SE_KEY_STORAGE_INTERNAL_VOLATILE,
.storage.location.slot = SL_SE_KEY_SLOT_VOLATILE_0,
};
sl_se_import_key(&existing_wrapped_key, &key_in_slot);
}
void demo_se_volatile_slot_to_wrapped_key(void) {
sl_se_key_descriptor_t existing_volatile_key = {
.type = SL_SE_KEY_TYPE_AES_256,
.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE,
.storage.method = SL_SE_KEY_STORAGE_INTERNAL_VOLATILE,
.storage.location.slot = SL_SE_KEY_SLOT_VOLATILE_0,
};
sl_se_key_descriptor_t wrapped_key_out = {
.type = SL_SE_KEY_TYPE_AES_256,
.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE,
.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_WRAPPED,
};
wrapped_key_out.storage.location.buffer.pointer = wrapped_key_buffer;
wrapped_key_out.storage.location.buffer.size = sizeof(wrapped_key_buffer);
sl_se_export_key(&existing_volatile_key, &wrapped_key_out);
}
void demo_se_delete_from_volatile_slot(void) {
sl_se_key_descriptor_t existing_volatile_key = {
.type = SL_SE_KEY_TYPE_AES_256,
.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE,
.storage.method = SL_SE_KEY_STORAGE_INTERNAL_VOLATILE,
.storage.location.slot = SL_SE_KEY_SLOT_VOLATILE_0,
};
sl_se_delete_key(&existing_volatile_key);
}
Tamper#
The Secure Engine (SE) tamper module connects a number of hardware and software-driven tamper signals to a set of configurable hardware and software responses. This can be used to program the device to automatically respond to external events that could signal that someone is trying to tamper with the device, and very rapidly remove secrets stored in the SE. The available tamper signals range from signals based on failed authentication and secure boot to specialized glitch detectors. When any of these signals fire, the tamper block can be configured to trigger several different responses, ranging from triggering an interrupt to erasing the one-time-programmable (OTP) memory, removing all SE secrets and resulting in a permanently destroyed device.
A tamper signal can lead to a series of different autonomous responses from the tamper module. These responses are listed in the table below.
Response | Description | |
---|---|---|
0 | Ignore | No action is taken. |
1 | Interrupt | The SETAMPERHOST interrupt on the host is triggered. |
2 | Filter | A counter in the tamper filter is increased. |
4 | Reset | The device is reset. |
7 | Erase OTP | Erases the OTP configuration of the device. |
These responses are cumulative, meaning that if a filter response is triggered, an interrupt will also be triggered. For a full overview of the tamper signals, see sl_se_manager_defines.h.
The tamper configuration is one-time-programmable, and is done using the initialise OTP command to the SE (see sl_se_init_otp). This means that tamper settings must be written together with secure boot settings, and are immutable after they are written. After tamper has been initialized, it is possible to temporarily disable one or several tamper signals using an authenticated command, similar to secure debug unlock. This is only possible if the debug public key has been installed on the device. It is only possible to disable the customer enabled response. The default response to a signal cannot be disabled.
Tamper is configured by providing the following:
Setting | Description |
---|---|
Tamper response levels | A response level for each tamper signal. It is not possible to degrade the default response level of a tamper signal, so if a response is set to a lower level than the default response level listed in the table in the Signals section, this won't have any effect. |
Filter settings | The tamper filter counter has two settings:
These options can be set to the values given in the tables in the Response Filter section. Please see the examples section for a suggested use of the tamper filter signal. |
Flags | The tamper flags is used to configure two options:
|
Reset threshold | The number of consecutive tamper resets before the the part enters debug mode. If the threshold is set to 0, the part will never enter the debug mode due to tamper reset. |
Example Usage#
The glitch detectors can see spurious activations, and should typically not be used to directly drive a serious tamper response. Instead they should feed their signals into a tamper interrupt (to handle the response logic on the M33), or into the tamper filter counter, which can be used to activate a high level response if a number of incidents occur in a short time window. The time period and counter threshold must be tuned to the use case. In the following example the device will erase OTP and become inoperable if 4 glitch signals is seen in a 1 minute time period.
Since you can only configure tamper once for each device, please make sure that this is the configuration you actually want before you execute this example on actual device.
sl_se_otp_init_t otp_settings_init = SL_SE_OTP_INIT_DEFAULT;
// Configure tamper levels
otp_settings_init.tamper_levels[SL_SE_TAMPER_SIGNAL_FILTER] = SL_SE_TAMPER_LEVEL_PERMANENTLY_ERASE_OTP;
otp_settings_init.tamper_levels[SL_SE_TAMPER_SIGNAL_VGLITCHFALLING] = SL_SE_TAMPER_LEVEL_FILTER;
otp_settings_init.tamper_levels[SL_SE_TAMPER_SIGNAL_VGLITCHRISING] = SL_SE_TAMPER_LEVEL_FILTER;
otp_settings_init.tamper_levels[SL_SE_TAMPER_SIGNAL_DGLITCH] = SL_SE_TAMPER_LEVEL_FILTER;
// Configure tamper filter options
otp_settings_init.tamper_filter_period = SL_SE_TAMPER_FILTER_PERIOD_1MIN;
otp_settings_init.tamper_filter_threshold = SL_SE_TAMPER_FILTER_THRESHOLD_4;
// Commit OTP settings. This command is only available once!
sl_se_init_otp(&otp_settings_init);
RTOS Mode and Multi-Thread Safety#
Note
The SE Manager API is multi-thread safe, but does not support preemption. This means the API cannot be called from ISR or critical/atomic sections when running in an RTOS thread. When using the SE Manager API in a bare-metal application, it is the application developer's responsibility to not call the SE Manager APIs when another operation is in progress.
The SE Manager supports multi-thread safe APIs for MicriumOS and FreeRTOS interfacing with CMSIS RTOS2 APIs.
In the cases where Micrium OS or FreeRTOS are included in the project (RTOS-mode), the SE Manager will be configured with threading and yield support. Configure sl_se_command_context_t with sl_se_set_yield to yield the CPU core when the SE Manager is waiting for the Secure Engine to complete a mailbox command.
For threading support the SE Manager applies an SE lock mechanism to protect the Secure Engine Mailbox interface from being accessed by more than one thread, ensuring multi-thread safety. For yielding the CPU core while waiting for the SE, the SE Manager APIs that invoke SE mailbox commands will wait on a semaphore which is signaled in the ISR that handles the SE mailbox completion interrupt. Hence other threads may run on the CPU core while the SE is processing the mailbox command.