arch/arm64/imx9: add key store, signing and persistence to the ELE

The EdgeLock Enclave offers a key store the mailbox driver did not
reach. A key generated in there is permitted one algorithm and one
usage, and export can be withheld, so the private half has no command
that returns it.

Adds the session, key store and key management services, key generation,
signing by handle, and the storage exchange that makes a key store
outlive a boot. Storage runs the other way round from every other
command: the enclave asks the host to write its key store down and to
give it back, and those requests arrive while a command of this side's
is still outstanding, so the reply tag is what tells them apart.

Two things a port has to know and neither reference nor header says.
Key store commands carry a trailing crc, the exclusive or of every word
including the header, without which the enclave answers rating 0xb9. And
a persistent key lifetime is a statement of intent: the strict flag on
key generation is what writes the key to the store, and without it a
store exported around the key comes back without it.

Every mailbox wait is bounded. An enclave that stops answering must not
take the calling thread with it, and a reply buffer is a kilobyte, which
does not belong on the stack of whatever task asked for a signature.

Tested on an i.MX93: a P-256 key generated in the enclave, signing a
digest whose signature verifies against the returned public half on a
host, and still doing so after the board has been powered off.

Signed-off-by: Royyan Zahir <royzah@gmail.com>
This commit is contained in:
Royyan Zahir 2026-09-24 15:56:44 +04:00 • committed by Xiang Xiao
parent 86da4d11ea
commit 68dd87f4df
3 changed files with 1466 additions and 24 deletions

View file

@ -57,6 +57,64 @@
#define ELE_VERIFY_IMAGE_REQ 0x88
#define ELE_COMMIT_REQ 0xa8
/* Key store services. These answer to ELE_VERSION_FW, not ELE_VERSION, and
* every one of them is a session the caller has to close.
*/
#define ELE_SAB_INIT_REQ 0x17
#define ELE_SESSION_OPEN_REQ 0x10
#define ELE_SESSION_CLOSE_REQ 0x11
#define ELE_KEY_STORE_OPEN_REQ 0x30
#define ELE_KEY_STORE_CLOSE_REQ 0x31
#define ELE_KEY_MGMT_OPEN_REQ 0x40
#define ELE_KEY_MGMT_CLOSE_REQ 0x41
#define ELE_GENERATE_KEY_REQ 0x42
#define ELE_DELETE_KEY_REQ 0x4e
#define ELE_SIG_GEN_OPEN_REQ 0x70
#define ELE_STORAGE_OPEN_REQ 0xe0
#define ELE_STORAGE_CLOSE_REQ 0xe1
#define ELE_STORAGE_IMPORT_REQ 0xe2
#define ELE_STORAGE_EXPORT_START 0xe3
#define ELE_STORAGE_EXPORT_FINISH 0xe4
#define ELE_STORAGE_CHUNK_EXPORT 0xe5
#define ELE_STORAGE_CHUNK_GET 0xe6
#define ELE_STORAGE_CHUNK_GET_DONE 0xe7
#define ELE_SIG_GEN_CLOSE_REQ 0x71
#define ELE_SIGNATURE_GEN_REQ 0x72
#define ELE_KEY_STORE_FLAG_CREATE 0x01
/* Key attributes, from NXP's key management API. SECP_R1 covers the NIST
* curves; the enclave offers no Edwards or Montgomery curve.
*/
#define ELE_KEY_TYPE_ECC_PAIR_SECP_R1 0x7112
#define ELE_KEY_USAGE_SIGN_HASH 0x00001000
#define ELE_KEY_USAGE_VERIFY_HASH 0x00002000
#define ELE_KEY_LIFETIME_PERSISTENT 0x00000001
#define ELE_KEY_LIFECYCLE_OPEN 0x01
#define ELE_KEY_LIFECYCLE_CLOSED 0x02
#define ELE_ALGO_ECDSA_SHA256 0x06000609
#define ELE_KEY_GROUP_PERSISTENT 1
/* Without the strict flag a generated key lives in the enclave's own memory
* and is never written to the key store, so it does not survive the store
* being closed, let alone a reboot.
*/
#define ELE_KEY_FLAG_STRICT 0x80
/* The input to a signature is a message the enclave hashes itself, or a
* digest the caller hashed. A CSR is signed over a digest. Bit 0 selects the
* message, not the digest, which the part settles: setting it produced a
* signature over sha256 of the digest that was passed in.
*/
#define ELE_SIG_FLAG_INPUT_DIGEST 0x00
#define ELE_SIG_FLAG_INPUT_MESSAGE 0x01
#define ELE_KEY_STORE_FLAG_SYNC 0x80
#define ELE_KEY_STORE_FLAG_MONINC 0x20
/* Messaging Unit registers. */
#define ELE_MU_TCR (IMX9_S3MUA_BASE + 0x120)

File diff suppressed because it is too large Load diff

View file

@ -29,6 +29,7 @@
#include "hardware/imx9_ele.h"
#include <sys/types.h>
#include <stdbool.h>
#include <stdint.h>
/****************************************************************************
@ -235,6 +236,19 @@ int imx9_ele_verify_image(uint32_t img_id, uint32_t *response);
int imx9_ele_start_rng(void);
/****************************************************************************
* Name: imx9_ele_lock / imx9_ele_unlock
*
* Description:
* Serialise use of the ELE mailbox, which every command shares. Recursive.
* imx9_ele_get_random() takes it itself; any other caller holds it across
* each command, or across a whole session that must not be interleaved.
*
****************************************************************************/
void imx9_ele_lock(void);
void imx9_ele_unlock(void);
/****************************************************************************
* Name: imx9_ele_get_trng_state
*
@ -287,4 +301,145 @@ int imx9_ele_get_random(void *buf, size_t len);
****************************************************************************/
int imx9_ele_commit(uint32_t info, uint32_t *response);
/****************************************************************************
* Name: imx9_ele_session_open / imx9_ele_session_close
*
* Description:
* Open and close an ELE session. The key store services hang off one, and
* the enclave holds the session until it is closed.
*
* Returned Value:
* Zero (OK) is returned for success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int imx9_ele_sab_init(uint32_t *rsp);
int imx9_ele_session_open(uint32_t *session);
int imx9_ele_session_open_rsp(uint32_t *session, uint32_t *rsp);
int imx9_ele_session_close(uint32_t session);
/****************************************************************************
* Name: imx9_ele_key_store_open / imx9_ele_key_store_close
*
* Description:
* Open a key store on an ELE session, creating it if asked. A key
* generated into a store has no command that returns its private half.
*
* Returned Value:
* Zero (OK) is returned for success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int imx9_ele_key_store_open(uint32_t session, uint32_t id, uint32_t nonce,
uint8_t flags, uint32_t *store);
int imx9_ele_key_store_open_rsp(uint32_t session, uint32_t id,
uint32_t nonce, uint8_t flags,
uint32_t *store, uint32_t *rsp);
int imx9_ele_key_store_close(uint32_t store);
/****************************************************************************
* Name: imx9_ele_key_mgmt_open / close, imx9_ele_generate_key
*
* Description:
* Generate a key pair inside the enclave. The public half is returned; the
* private half stays in the key store with no command that returns it.
*
* Returned Value:
* Zero (OK) is returned for success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int imx9_ele_key_mgmt_open(uint32_t store, uint32_t *mgmt, uint32_t *rsp);
int imx9_ele_key_mgmt_close(uint32_t mgmt);
int imx9_ele_generate_key(uint32_t mgmt, uint16_t key_type,
uint16_t key_bits, uint32_t algo,
uint32_t lifecycle,
void *pubkey, size_t pubkey_len,
uint32_t *key_id, uint32_t *rsp);
/****************************************************************************
* Name: imx9_ele_sig_gen_open / close, imx9_ele_sign
*
* Description:
* Sign with a key held in the key store. The key is named by identifier,
* never handed over, so this is the only way to use it.
*
* Returned Value:
* Zero (OK) is returned for success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int imx9_ele_sig_gen_open(uint32_t store, uint32_t *svc, uint32_t *rsp);
int imx9_ele_sig_gen_close(uint32_t svc);
int imx9_ele_sign(uint32_t svc, uint32_t key_id, uint32_t algo, bool digest,
void *in, size_t inlen, void *out, size_t outlen,
uint32_t *rsp);
/****************************************************************************
* Name: imx9_ele_poll_msg
*
* Description:
* Receive a message the enclave sent on its own initiative.
*
* Returned Value:
* Zero (OK) on success, -ETIMEDOUT if nothing arrived.
*
****************************************************************************/
int imx9_ele_poll_msg(struct ele_msg *msg_ptr, uint32_t timeout_us);
/****************************************************************************
* Name: imx9_ele_storage_open / close
*
* Description:
* Open a storage session, without which the enclave will not sync a key
* store.
*
* Returned Value:
* Zero (OK) is returned for success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int imx9_ele_storage_open(uint32_t session, uint32_t *storage,
uint32_t *rsp);
int imx9_ele_storage_close(uint32_t storage);
/****************************************************************************
* Name: imx9_ele_blob_get / imx9_ele_blob_put
*
* Description:
* The key store the enclave asked to have persisted, a slot at a time.
* Where it is kept is not this driver's business.
*
* Returned Value:
* imx9_ele_blob_get() returns the size of that slot, zero if it holds
* nothing. imx9_ele_blob_put() returns zero (OK), or a negated errno.
*
****************************************************************************/
uint32_t imx9_ele_blob_get(unsigned slot, uint32_t *id, uint32_t *id_ext,
const void **blob);
int imx9_ele_blob_put(uint32_t id, uint32_t id_ext, const void *blob,
uint32_t len);
/****************************************************************************
* Name: imx9_ele_storage_master_import
*
* Description:
* Give the enclave back the master blob of a key store it exported.
* Without it the open that follows answers UNKNOWN_ID.
*
* Returned Value:
* Zero (OK), -ENOENT if no master blob is held, or a negated errno.
*
****************************************************************************/
int imx9_ele_storage_master_import(uint32_t storage, uint32_t *rsp);
#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_ELE_H */