Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
320 changes: 318 additions & 2 deletions wolfcrypt/src/port/nxp/README_SE050.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ wolfSSL uses the "EdgeLock SE05x Plug & Trust Middleware" to interface with
SE050. This can be downloaded from the NXP website [here](https://www.nxp.com/products/security-and-authentication/authentication/edgelock-se050-plug-trust-secure-element-family-enhanced-iot-security-with-high-flexibility:SE050#design-resources).
An free NXP account is required to download the middleware.

wolfSSL last tested with SE05x middleware version 04.02.00.
wolfSSL last tested with SE05x middleware version 04.07.01.

Instructions for building will vary on target platform and host operating
system. A Raspberry Pi with an NXP EdgeLock SE050 Development Kit can be used
Expand Down Expand Up @@ -246,6 +246,19 @@ defined, wolfCrypt will instead fall back to using `/dev/random` and
Disables using the SE050 for RSA, useful for the SE050E which does not have
RSA support.

**`WOLFSSL_SE050_NO_ATTEST`**

Removes the SE05x object-attestation and host-verification helpers. Define this
for small builds that do not provision or validate attested objects.

**`WOLFSSL_SE050_SCP03_ROTATE`**

Enables the destructive Platform SCP03 key-rotation APIs. This is deliberately
opt-in. The middleware must enable Platform SCP03 and HostCrypto, and wolfSSL
must be built with `WOLFSSL_SE050_INIT` and AES direct support
(`WOLFSSL_AES_DIRECT`). The seed-based helper additionally requires HKDF
(`--enable-hkdf` / `HAVE_HKDF`).

**`WOLFSSL_SE050_NO_ECDHE`**

Disables offloading ECDH key generation and shared secret operations to the
Expand Down Expand Up @@ -355,6 +368,36 @@ wolfSSL_Debugging_ON();
wolfCrypt_Cleanup();
```

An application using runtime Platform-SCP03 keys may need to authenticate to
the SE05x before global wolfCrypt initialization. In that case,
`wolfCrypt_Init()` detects and retains the already configured session instead
of attempting a second connection with the middleware's compiled-in keys.
`wolfCrypt_Cleanup()` closes sessions owned by wolfSSL, including sessions
opened by `wc_se050_init_ex()` before global initialization. For example:

```c
wc_se050_scp03_keys activeKeys;
int ret;

/* Recover activeKeys from protected storage, or derive them from a protected
* seed as shown in the Platform SCP03 section below. */
ret = wc_se050_init_ex(NULL, &activeKeys);
/* Securely erase the temporary activeKeys copy here. */
if (ret == 0)
ret = wolfCrypt_Init();
if (ret != 0) {
(void)wc_se050_close();
return ret;
}

/* Use wolfCrypt and the authenticated SE05x session. */

return wolfCrypt_Cleanup();
```

Sessions supplied by `wc_se050_set_config()` remain caller-owned and are not
closed by `wolfCrypt_Cleanup()`.

If `WOLFSSL_SE050_INIT` has not been defined when compiling wolfSSL, the
following API can be called after wolfSSL library initialization to pass the
correct pre-initialized `sss_session_t` and `sss_key_store_t` structure
Expand All @@ -369,6 +412,39 @@ int wc_se050_set_config(
sss_key_store_t *pKeyStore);
```

### Accessing the wolfSSL SE05x Session

Applications that need an SSS operation not wrapped by wolfSSL can retrieve
the configured objects with:

```c
sss_session_t* wc_se050_get_session(void);
pSe05xSession_t wc_se050_get_se05x_session(void);
int wc_se050_get_config(sss_session_t** session,
sss_key_store_t** hostKeyStore, sss_key_store_t** keyStore);
```

When `WOLFSSL_SE050_INIT` is enabled, `wc_se050_close()` safely closes a
session opened by `wc_se050_init()` or `wc_se050_init_ex()` and clears the
configured pointers. It returns `BAD_STATE_E` when wolfSSL does not own the
active session. A second initialization attempt while any SE05x session is
configured also returns `BAD_STATE_E` instead of replacing or leaking it.

The SE05x transport is shared with wolfCrypt. Threaded SE05x builds enable the
wolfCrypt hardware mutex by default. Every direct middleware call
through one of these pointers must be serialized with the same lock:

```c
int ret = wc_se050_lock();
if (ret == 0) {
/* Direct SSS or Se05x_API_* call. */
wc_se050_unlock();
}
```

Do not call another `wc_se050_*` or wolfCrypt hardware operation while holding
this lock; those functions acquire it internally.

### wolfSSL SE050 Key Generation

wolfSSL includes APIs for key generation when `WOLFSSL_KEY_GEN` has been
Expand Down Expand Up @@ -416,6 +492,103 @@ These APIs will all return 0 on success or a negative error code on failure.
The input to all these functions is a DER-encoded key and the size of that DER
array in bytes.

### Provisioning and Generating Objects with Policies

The `_ex` insertion variants accept permission flags and an authentication
object ID. They are available for ECC public/private keys, RSA public/private
keys, and binary objects. For example:

```c
int ret = wc_se050_ecc_insert_private_key_ex(keyId, der, derSz,
WC_SE050_POLICY_ALLOW_READ |
WC_SE050_POLICY_ALLOW_SIGN |
WC_SE050_POLICY_ALLOW_ATTEST,
0); /* auth object 0 grants the permissions to every authenticated user */
```

Available flags are `WC_SE050_POLICY_ALLOW_DELETE`, `ALLOW_WRITE`,
`ALLOW_READ`, `ALLOW_SIGN`, `ALLOW_VERIFY`, `ALLOW_ENCRYPT`, `ALLOW_DECRYPT`,
`ALLOW_KA`, `ALLOW_KD`, `ALLOW_GEN`, `ALLOW_IMPORT_EXPORT`, `ALLOW_ATTEST`,
and `REQUIRE_SM`. Flags that do not apply to the object type are rejected.
`ALLOW_KD` grants HKDF on applet 7.2 and the general KDF permission on older
applets. The flag front end writes one applet policy record so common and
key-specific permissions remain combined across middleware versions, then
uses the normal middleware object writer (including binary chunking, RSA
component sequencing, and EC curve creation).
The corresponding `_policy` variants accept a complete middleware
`sss_policy_t` when the flag front end is not expressive enough.

Applications can also generate persistent ECC and RSA key pairs entirely
inside the SE05x while attaching the policy at creation time:

```c
int wc_se050_ecc_generate_key_ex(word32 keyId, int keySize,
int curveId, word32 policyFlags, word32 authObjId);
int wc_se050_ecc_generate_key_policy(word32 keyId, int keySize,
int curveId, const sss_policy_t* policy);

int wc_se050_rsa_generate_key_ex(word32 keyId, int size, long e,
word32 policyFlags, word32 authObjId);
int wc_se050_rsa_generate_key_policy(word32 keyId, int size, long e,
const sss_policy_t* policy);
```

For ECC, `keySize` is in bytes and `curveId` is a wolfCrypt curve ID such as
`ECC_SECP256R1`. For RSA, `size` is in bits and the SE05x requires `e` to be
65537. Generation requires an unused provisioning ID below
`SE050_KEYID_START`; it never replaces an existing object. The private key is
generated on-chip and is not returned to the host.

For example, generate a persistent P-256 signing key and then bind a wolfCrypt
key structure to it:

```c
ecc_key key;
word32 keyId = 0x20;
int ret;

ret = wc_se050_ecc_generate_key_ex(keyId, 32, ECC_SECP256R1,
WC_SE050_POLICY_ALLOW_DELETE |
WC_SE050_POLICY_ALLOW_READ |
WC_SE050_POLICY_ALLOW_SIGN |
WC_SE050_POLICY_ALLOW_VERIFY, 0);
if (ret == 0)
ret = wc_ecc_init(&key);
if (ret == 0)
ret = wc_ecc_use_key_id(&key, keyId, 0);

/* Use key, then call wc_ecc_free(&key). The SE05x object remains persistent. */
```

Include `ALLOW_READ` when the application will bind the object with
`wc_ecc_use_key_id()` or `wc_RsaUseKeyId()`, because those functions read the
public component. Include `ALLOW_DELETE` only when the provisioning lifecycle
must permit deletion. `ALLOW_GEN` grants the applet's regenerate permission,
but these wolfSSL helpers still require a new ID to prevent an accidental
replacement; use the direct middleware under `wc_se050_lock()` for an
intentional in-place regeneration allowed by a custom lifecycle.

A zero flag value attaches no policy and preserves the old applet-default
behavior. Any nonzero policy is default-deny: every permission not granted is
denied. Policies are immutable after object creation; replacing one requires
deleting and recreating the object. Every `_ex` insertion, including a zero
flag value, requires an unused object ID and refuses to overwrite an existing
object. In particular:

- Omitting `ALLOW_WRITE` prevents replacement of the value.
- Omitting `ALLOW_DELETE` makes normal deletion fail. The object remains until
an applet factory reset. Test no-delete policies on a development part first,
because an incorrect policy can permanently consume NV storage.
- `ALLOW_READ` does not make AES/symmetric secret values readable; SE05x
applets reject those reads regardless of policy.

On applet 7.2 and later, `wc_se050_get_object_attributes()` returns the raw
applet attribute bytes,
including policy entries and origin, so provisioning code can verify what was
stored. It returns `NOT_COMPILED_IN` with older middleware configurations.
`wc_se050_erase_object()` returns `WC_HW_E` when deletion is denied;
the failure is the expected result for a no-delete object.

### wolfSSL SE050 Certificate Insertion and Retrieval

Applications can insert or retrieve certificates or binary data into an SE050
Expand Down Expand Up @@ -450,6 +623,150 @@ function `wc_se050_erase_object(int keyId)`. This function is available through
`<wolfssl/wolfcrypt/port/nxp/se050_port.h>`, and should be passed the key ID
to be deleted.

### Platform SCP03 Runtime Keys and Rotation

For a middleware build configured with Platform SCP03, `wc_se050_init_ex()`
opens the wolfSSL-owned session using caller-supplied 16-byte ENC, MAC, and DEK
keys instead of the middleware's compiled defaults:

```c
wc_se050_scp03_keys keys;
/* Load all three fields from protected, durable storage. */
int ret = wc_se050_init_ex(NULL, &keys);
```

The port uses the configured middleware transport when its transport macro is
visible. Otherwise it uses T=1 over I2C, which is the documented/default SE05x
port configuration.

When the middleware HostCrypto backend uses wolfSSL, the SCP03 handshake needs
entropy before the authenticated SE05x session exists. Configure an independent
host entropy source and define `WOLFSSL_SE050_NO_TRNG` so the handshake does not
try to bootstrap itself from the SE05x TRNG.

When `HAVE_HKDF` is enabled, the active keys can be regenerated
deterministically from a protected seed without an open SE05x session:

```c
int wc_se050_scp03_derive_keys_seed(const byte* seed, word32 seedSz,
wc_se050_scp03_keys* derivedOut);
```

This is the normal consecutive-power-cycle flow for seed-provisioned keys:

```c
#define SCP03_SEED_SIZE 32

wc_se050_scp03_keys activeKeys;
byte seed[SCP03_SEED_SIZE];
int ret;

/* Load the same protected seed that was committed before key rotation. */
ret = load_protected_scp03_seed(seed, sizeof(seed));
if (ret == 0)
ret = wc_se050_scp03_derive_keys_seed(seed, sizeof(seed), &activeKeys);
/* Securely erase seed here. */
if (ret == 0)
ret = wc_se050_init_ex(NULL, &activeKeys);
/* Securely erase activeKeys here. */
if (ret == 0)
ret = wolfCrypt_Init();
if (ret != 0) {
(void)wc_se050_close();
return ret;
}

/* Use wolfCrypt and SE05x. */

return wolfCrypt_Cleanup();
```

The derive-only API does not change the SE05x and does not require
`WOLFSSL_SE050_SCP03_ROTATE`. It uses HKDF-SHA256 with an empty salt and the
three info strings listed below. The same seed therefore regenerates the same
ENC, MAC, and DEK values after every power cycle. The key-version byte is part
of the destructive PUT KEY operation, not this derivation or session setup.

Key rotation is only compiled when `WOLFSSL_SE050_SCP03_ROTATE` is defined:

```c
int wc_se050_scp03_rotate_keys(const wc_se050_scp03_keys* newKeys,
byte keyVersion);
int wc_se050_scp03_rotate_keys_seed(const byte* seed, word32 seedSz,
byte keyVersion, wc_se050_scp03_keys* derivedOut);
```

The direct API wraps all three new keys with the current DEK, closes the IoT
applet session, authenticates to the Supplementary Security Domain, sends one
secured GlobalPlatform PUT KEY command, and verifies the three returned KCVs.
It then returns with a fresh IoT applet session authenticated by the new keys.
The host AES block operation stages its input and output through 16-byte-aligned
buffers, which is required by strict-alignment hardware AES backends such as
STM32H7.
`WC_HW_E` means the PUT KEY command was rejected; `AES_GCM_AUTH_E` means the
returned KCVs did not match. The seed API uses this exact interoperable KDF:

- HKDF-SHA256 with an empty salt and `seed` as IKM.
- Three 16-byte outputs using info strings `SE050 SCP03 ENC`,
`SE050 SCP03 MAC`, and `SE050 SCP03 DEK` respectively.

SCP03 key loss makes the part inaccessible through Platform SCP03. Persist
directly supplied keys before calling the direct API. For the seed API, persist
the seed before calling; the derive-only API can recover the same key set on
every subsequent boot, so storing the derived keys is optional. After a
successful rotation, verify an operation on the new session before retiring
the old provisioning record. `wc_se050_close()` closes that new session
normally. Never test rotation on a production part.

### Object Attestation and Provisioning Validation

`wc_se050_attest_object()` performs an attested read using an
application-provisioned attestation key. The attestation key's object policy
must grant
`WC_SE050_POLICY_ALLOW_ATTEST`. The caller must supply an independently
generated 16-byte freshness challenge. Generate and retain that challenge
outside the SE05x so a response cannot select its own freshness value.

```c
wc_se050_attst_result result;
byte freshness[16];
int valid;

ret = application_get_host_random(freshness, sizeof(freshness));
if (ret == 0)
ret = wc_se050_attest_object(keyId, attestKeyId, WC_HASH_TYPE_SHA256,
freshness, sizeof(freshness), &result);
if (ret == 0)
ret = wc_se050_verify_attestation(&result, attestPublicDer,
attestPublicDerSz, freshness, sizeof(freshness), &valid);
```

The verifier supports the pre-7.2 and 7.2+ signed-data formats and ECC or RSA
attestation public keys, including X25519 and Ed25519 object values. It requires
the independently retained 16-byte challenge and compares it with both the
host-side result metadata and every signed attestation component; a recorded
response therefore cannot be accepted for a new challenge. `result` contains
the returned object value, the challenge used for the request, parsed
origin/authentication ID/policy flags, object metadata, and the raw middleware
attestation records for remote verification. A valid signature proves the
response was signed by the corresponding attestation key; it does not establish
trust in that key. The application must validate the attestation key's
certificate/provisioning chain separately.

`wc_se050_validate_provisioned_key()` combines a SHA-256 attested read,
signature verification, and exact DER public-key comparison. It takes the
same caller-generated 16-byte freshness challenge:

```c
ret = wc_se050_validate_provisioned_key(keyId, attestKeyId,
expectedPublicDer, expectedPublicDerSz, attestPublicDer,
attestPublicDerSz, freshness, sizeof(freshness), &valid);
```

The attestation key and certificate chain are application-provisioned;
reserved NXP credentials are variant-specific and are not selected
automatically.

### wolfSSL SE050 Factory Reset

If wolfSSL is compiled with `WOLFSSL_SE050_FACTORY_RESET` defined, when
Expand Down Expand Up @@ -618,4 +935,3 @@ Once the build has finished, the `wolfcrypt_test` executable can be run with:
$ cd /home/pi/se_mw/simw-top_build/raspbian_native_se050_t1oi2c/bin
$ ./wolfcrypt_test
```

Loading
Loading