diff --git a/wolfcrypt/src/port/nxp/README_SE050.md b/wolfcrypt/src/port/nxp/README_SE050.md index cc04349818..9195a0ec3a 100644 --- a/wolfcrypt/src/port/nxp/README_SE050.md +++ b/wolfcrypt/src/port/nxp/README_SE050.md @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -450,6 +623,150 @@ function `wc_se050_erase_object(int keyId)`. This function is available through ``, 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 @@ -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 ``` - diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index c1e057e563..52721f8b4c 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -37,6 +37,15 @@ #include #include +#ifdef HAVE_HKDF + #include +#endif + +#ifndef WOLFSSL_SE050_NO_ATTEST + #include + #include +#endif + #ifdef NO_INLINE #include #else @@ -52,6 +61,10 @@ #endif #include "ex_sss_boot.h" + #if defined(SSS_HAVE_SE05X_AUTH_PLATFSCP03) && \ + SSS_HAVE_SE05X_AUTH_PLATFSCP03 + #include "ex_sss_auth.h" + #endif #endif #ifdef HAVE_ECC @@ -61,7 +74,8 @@ #define SE050_ECC_DER_MAX 256 #endif #endif -#if !defined(NO_RSA) && !defined(WOLFSSL_SE050_NO_RSA) +#if !defined(NO_RSA) && (!defined(WOLFSSL_SE050_NO_RSA) || \ + !defined(WOLFSSL_SE050_NO_ATTEST)) #include struct RsaKey; #endif @@ -80,10 +94,31 @@ static sss_session_t *cfg_se050_i2c_pi; static sss_key_store_t *gHostKeyStore; static sss_key_store_t *gKeyStore; +#ifdef WOLFSSL_SE050_INIT +static ex_sss_boot_ctx_t gBootCtx; +#endif + +#if defined(WOLFSSL_SE050_INIT) && \ + defined(SSS_HAVE_HOSTCRYPTO_ANY) && SSS_HAVE_HOSTCRYPTO_ANY && \ + defined(SSS_HAVE_SCP_SCP03_SSS) && SSS_HAVE_SCP_SCP03_SSS && \ + defined(SSS_HAVE_SE05X_AUTH_PLATFSCP03) && \ + SSS_HAVE_SE05X_AUTH_PLATFSCP03 + #define SE050_RUNTIME_SCP03 +#endif int wc_se050_set_config(sss_session_t *pSession, sss_key_store_t *pHostKeyStore, sss_key_store_t *pKeyStore) { + int ret; + + if ((pSession == NULL) || (pKeyStore == NULL)) { + return BAD_FUNC_ARG; + } + ret = wolfSSL_CryptHwMutexInit(); + if (ret != 0) { + return ret; + } + WOLFSSL_MSG("Setting SE050 session configuration"); cfg_se050_i2c_pi = pSession; @@ -93,459 +128,1206 @@ int wc_se050_set_config(sss_session_t *pSession, sss_key_store_t *pHostKeyStore, return 0; } -#ifdef WOLFSSL_SE050_INIT -int wc_se050_init(const char* portName) +int wc_se050_get_config(sss_session_t **pSession, + sss_key_store_t **pHostKeyStore, sss_key_store_t **pKeyStore) { - int ret; - sss_status_t status; - static ex_sss_boot_ctx_t pCtx; - - if (portName == NULL) { - portName = SE050_DEFAULT_PORT; + if ((cfg_se050_i2c_pi == NULL) || (gKeyStore == NULL)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - status = ex_sss_boot_open(&pCtx, portName); - if (status == kStatus_SSS_Success) { - ret = wc_se050_set_config(&pCtx.session, - #if SSS_HAVE_HOSTCRYPTO_ANY - &pCtx.host_ks, - #else - NULL, - #endif - &pCtx.ks); - - #ifdef WOLFSSL_SE050_FACTORY_RESET - ex_sss_boot_factory_reset(&pCtx); - #endif + if (pSession != NULL) { + *pSession = cfg_se050_i2c_pi; } - else { - WOLFSSL_MSG("Failed to open SE050 context"); - ret = WC_HW_E; + if (pHostKeyStore != NULL) { + *pHostKeyStore = gHostKeyStore; } - return ret; + if (pKeyStore != NULL) { + *pKeyStore = gKeyStore; + } + + return 0; } -#endif -/** - * Erase and free an object stored in SE050. - * - * keyId ID of object to erase - * - * Returns 0 on success, negative on error. - */ -int wc_se050_erase_object(word32 id) +sss_session_t* wc_se050_get_session(void) { - int ret = 0; - sss_object_t object; - sss_key_store_t host_keystore; - sss_status_t status = kStatus_SSS_Success; + return cfg_se050_i2c_pi; +} -#ifdef SE050_DEBUG - printf("wc_se050_erase_object: id %d\n", id); +pSe05xSession_t wc_se050_get_se05x_session(void) +{ +#if SSS_HAVE_APPLET_SE05X_IOT + if ((cfg_se050_i2c_pi != NULL) && + (cfg_se050_i2c_pi->subsystem == kType_SSS_SE_SE05x)) { + return &((sss_se05x_session_t*)cfg_se050_i2c_pi)->s_ctx; + } #endif + return NULL; +} - if (cfg_se050_i2c_pi == NULL) { +int wc_se050_lock(void) +{ + return wolfSSL_CryptHwMutexLock(); +} + +void wc_se050_unlock(void) +{ + wolfSSL_CryptHwMutexUnLock(); +} + +enum se050_policy_object_type { + SE050_POLICY_OBJECT_ASYM, + SE050_POLICY_OBJECT_FILE +}; + +#define SE050_POLICY_MAX_ENTRIES 3U + +typedef struct se050_policy_set { + sss_policy_u entries[SE050_POLICY_MAX_ENTRIES]; + sss_policy_t policy; +} se050_policy_set; + +#define SE050_POLICY_COMMON_FLAGS (WC_SE050_POLICY_ALLOW_DELETE | \ + WC_SE050_POLICY_ALLOW_WRITE | WC_SE050_POLICY_ALLOW_READ | \ + WC_SE050_POLICY_REQUIRE_SM) +#define SE050_POLICY_ASYM_FLAGS (SE050_POLICY_COMMON_FLAGS | \ + WC_SE050_POLICY_ALLOW_SIGN | WC_SE050_POLICY_ALLOW_VERIFY | \ + WC_SE050_POLICY_ALLOW_ENCRYPT | WC_SE050_POLICY_ALLOW_DECRYPT | \ + WC_SE050_POLICY_ALLOW_KA | WC_SE050_POLICY_ALLOW_KD | \ + WC_SE050_POLICY_ALLOW_GEN | WC_SE050_POLICY_ALLOW_IMPORT_EXPORT | \ + WC_SE050_POLICY_ALLOW_ATTEST) + +static int se050_build_policy_set( + enum se050_policy_object_type objectType, word32 flags, + word32 authObjId, se050_policy_set* policySet) +{ + sss_policy_u* objectPolicy; + sss_policy_u* commonPolicy; + word32 allowedFlags; + word32 count = 0U; + + if (policySet == NULL) { return BAD_FUNC_ARG; } - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + allowedFlags = (objectType == SE050_POLICY_OBJECT_ASYM) ? + SE050_POLICY_ASYM_FLAGS : SE050_POLICY_COMMON_FLAGS; + if ((flags & ~allowedFlags) != 0U) { + return BAD_FUNC_ARG; } - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); - if (status == kStatus_SSS_Success) { - status = sss_key_store_allocate(&host_keystore, - SE050_KEYSTOREID_GENERIC); - } - if (status == kStatus_SSS_Success) { - status = sss_key_object_init(&object, &host_keystore); + XMEMSET(policySet, 0, sizeof(*policySet)); + if (flags == 0U) { + return 0; + } + + objectPolicy = &policySet->entries[count++]; + objectPolicy->auth_obj_id = authObjId; + if (objectType == SE050_POLICY_OBJECT_ASYM) { + objectPolicy->type = KPolicy_Asym_Key; + objectPolicy->policy.asymmkey.can_Sign = + (flags & WC_SE050_POLICY_ALLOW_SIGN) != 0U; + objectPolicy->policy.asymmkey.can_Verify = + (flags & WC_SE050_POLICY_ALLOW_VERIFY) != 0U; + objectPolicy->policy.asymmkey.can_Encrypt = + (flags & WC_SE050_POLICY_ALLOW_ENCRYPT) != 0U; + objectPolicy->policy.asymmkey.can_Decrypt = + (flags & WC_SE050_POLICY_ALLOW_DECRYPT) != 0U; + objectPolicy->policy.asymmkey.can_KA = + (flags & WC_SE050_POLICY_ALLOW_KA) != 0U; + objectPolicy->policy.asymmkey.can_Gen = + (flags & WC_SE050_POLICY_ALLOW_GEN) != 0U; + objectPolicy->policy.asymmkey.can_Import_Export = + (flags & WC_SE050_POLICY_ALLOW_IMPORT_EXPORT) != 0U; + objectPolicy->policy.asymmkey.can_Attest = + (flags & WC_SE050_POLICY_ALLOW_ATTEST) != 0U; + #if !defined(SSS_HAVE_SE05X_VER_GTE_07_02) || \ + !SSS_HAVE_SE05X_VER_GTE_07_02 + objectPolicy->policy.asymmkey.can_Read = + (flags & WC_SE050_POLICY_ALLOW_READ) != 0U; + objectPolicy->policy.asymmkey.can_Write = + (flags & WC_SE050_POLICY_ALLOW_WRITE) != 0U; + objectPolicy->policy.asymmkey.can_KD = + (flags & WC_SE050_POLICY_ALLOW_KD) != 0U; + #else + if ((flags & WC_SE050_POLICY_ALLOW_KD) != 0U) { + sss_policy_u* derivePolicy = &policySet->entries[count++]; + + derivePolicy->type = KPolicy_Sym_Key; + derivePolicy->auth_obj_id = authObjId; + derivePolicy->policy.symmkey.can_HKDF = 1; + } + #endif } - if (status == kStatus_SSS_Success) { - status = sss_key_object_get_handle(&object, id); + else { + objectPolicy->type = KPolicy_File; + objectPolicy->policy.file.can_Read = + (flags & WC_SE050_POLICY_ALLOW_READ) != 0U; + objectPolicy->policy.file.can_Write = + (flags & WC_SE050_POLICY_ALLOW_WRITE) != 0U; + } + + commonPolicy = &policySet->entries[count++]; + commonPolicy->type = KPolicy_Common; + commonPolicy->auth_obj_id = authObjId; + commonPolicy->policy.common.can_Delete = + (flags & WC_SE050_POLICY_ALLOW_DELETE) != 0U; + commonPolicy->policy.common.req_Sm = + (flags & WC_SE050_POLICY_REQUIRE_SM) != 0U; +#if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && \ + SSS_HAVE_SE05X_VER_GTE_07_02 + if (objectType == SE050_POLICY_OBJECT_ASYM) { + commonPolicy->policy.common.can_Read = + (flags & WC_SE050_POLICY_ALLOW_READ) != 0U; + commonPolicy->policy.common.can_Write = + (flags & WC_SE050_POLICY_ALLOW_WRITE) != 0U; } - if (status == kStatus_SSS_Success) { - sss_key_store_erase_key(&host_keystore, &object); - sss_key_object_free(&object); +#endif + + policySet->policy.nPolicies = count; + for (count = 0U; count < policySet->policy.nPolicies; count++) { + policySet->policy.policies[count] = &policySet->entries[count]; } - wolfSSL_CryptHwMutexUnLock(); - if (status != kStatus_SSS_Success) { - ret = WC_HW_E; + return 0; +} + +/* Called only while the shared transport mutex is held. */ +static sss_status_t se050_require_new_object(word32 keyId) +{ + pSe05xSession_t session = wc_se050_get_se05x_session(); + SE05x_Result_t exists = kSE05x_Result_NA; + smStatus_t status; + + if (session == NULL) { + return kStatus_SSS_Fail; } + status = Se05x_API_CheckObjectExists(session, keyId, &exists); + if ((status != SM_OK) || (exists != kSE05x_Result_FAILURE)) { + return kStatus_SSS_Fail; + } + return kStatus_SSS_Success; +} - return ret; +#ifdef WOLFSSL_SE050_INIT +static int se050_boot_context_is_open(void) +{ + return (cfg_se050_i2c_pi != NULL) || + (gBootCtx.session.subsystem != kType_SSS_SubSystem_NONE); } -word32 se050_allocate_key(int keyType) +static sss_key_store_t* se050_boot_host_key_store(void) { - word32 keyId = 0; - static word32 keyId_allocator = SE050_KEYID_START; - switch (keyType) { - case SE050_AES_KEY: - case SE050_ECC_KEY: - case SE050_RSA_KEY: - case SE050_ED25519_KEY: - case SE050_CURVE25519_KEY: - case SE050_ANY_KEY: - keyId = keyId_allocator++; - break; +#if defined(SSS_HAVE_HOSTCRYPTO_ANY) && SSS_HAVE_HOSTCRYPTO_ANY + if (gBootCtx.host_ks.session != NULL) { + return &gBootCtx.host_ks; } -#ifdef SE050_DEBUG - printf("se050_allocate_key: keyId %d\n", keyId); #endif - return keyId; + return NULL; } -#if !defined(WC_NO_RNG) && !defined(WOLFSSL_SE050_NO_TRNG) -int se050_get_random_number(uint32_t count, uint8_t* rand_out) +#ifdef SE050_RUNTIME_SCP03 +static int se050_set_scp03_static_keys(const wc_se050_scp03_keys* keys) { - int ret = 0; + NXSCP03_StaticCtx_t* staticCtx; sss_status_t status; - sss_rng_context_t rng; - -#ifdef SE050_DEBUG - printf("se050_get_random_number: %p (%d)\n", rand_out, count); -#endif - if (cfg_se050_i2c_pi == NULL) { - return WC_HW_E; + if (keys == NULL) { + return BAD_FUNC_ARG; } - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + staticCtx = gBootCtx.se05x_open_ctx.auth.ctx.scp03.pStatic_ctx; + if ((staticCtx == NULL) || (staticCtx->Enc.keyStore == NULL) || + (staticCtx->Mac.keyStore == NULL) || + (staticCtx->Dek.keyStore == NULL)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - status = sss_rng_context_init(&rng, cfg_se050_i2c_pi); + + status = sss_host_key_store_set_key(&gBootCtx.host_ks, &staticCtx->Enc, + keys->enc, sizeof(keys->enc), sizeof(keys->enc) * 8U, NULL, 0); if (status == kStatus_SSS_Success) { - status = sss_rng_get_random(&rng, rand_out, count); + status = sss_host_key_store_set_key(&gBootCtx.host_ks, + &staticCtx->Mac, keys->mac, sizeof(keys->mac), + sizeof(keys->mac) * 8U, NULL, 0); } if (status == kStatus_SSS_Success) { - status = sss_rng_context_free(&rng); + status = sss_host_key_store_set_key(&gBootCtx.host_ks, + &staticCtx->Dek, keys->dek, sizeof(keys->dek), + sizeof(keys->dek) * 8U, NULL, 0); } if (status != kStatus_SSS_Success) { - ret = RNG_FAILURE_E; + return WC_HW_E; } - wolfSSL_CryptHwMutexUnLock(); - - return ret; -} -#endif /* !WC_NO_RNG && !WOLFSSL_SE050_NO_TRNG */ - -#ifdef WOLFSSL_SE050_HASH - -/* Used for sha/sha224/sha384/sha512 */ -int se050_hash_init(SE050_HASH_Context* se050Ctx, void* heap) -{ - se050Ctx->heap = heap; - se050Ctx->len = 0; - se050Ctx->used = 0; - se050Ctx->msg = NULL; + staticCtx->key_len = (int)sizeof(keys->enc); return 0; } -int se050_hash_copy(SE050_HASH_Context* src, SE050_HASH_Context* dst) +#ifdef WOLFSSL_SE050_SCP03_ROTATE +static int se050_set_scp03_dek(const byte* dek, word32 dekSz) { - if (src == NULL || dst == NULL || (src->used != dst->used)) { + NXSCP03_StaticCtx_t* staticCtx; + sss_status_t status; + + if ((dek == NULL) || + (dekSz != sizeof(((wc_se050_scp03_keys*)0)->dek))) { return BAD_FUNC_ARG; } - - if (src->used > 0) { - /* dst->msg points to same buffer as src->msg, needs to be allocated - * and dep copied over instead of plain pointer copy */ - dst->msg = (byte*)XMALLOC(src->used, dst->heap, - DYNAMIC_TYPE_TMP_BUFFER); - if (dst->msg == NULL) { - PRINTF("Tried to allocate %d bytes\n", dst->used); - return MEMORY_E; - } - XMEMSET(dst->msg, 0, dst->used); - XMEMCPY(dst->msg, src->msg, src->used); - dst->used = src->used; - dst->len = src->used; - } else { - dst->msg = NULL; - dst->len = 0; - dst->used = 0; + staticCtx = gBootCtx.se05x_open_ctx.auth.ctx.scp03.pStatic_ctx; + if ((staticCtx == NULL) || (staticCtx->Dek.keyStore == NULL)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - - return 0; + status = sss_host_key_store_set_key(&gBootCtx.host_ks, &staticCtx->Dek, + dek, dekSz, dekSz * 8U, NULL, 0); + return (status == kStatus_SSS_Success) ? 0 : WC_HW_E; } +#endif -int se050_hash_update(SE050_HASH_Context* se050Ctx, const byte* data, word32 len) +static sss_status_t se050_open_scp03(const char* portName, + const wc_se050_scp03_keys* keys, int skipSelectApplet) { - byte* tmp = NULL; - word32 usedSz = 0; + SE_Connect_Ctx_t* connectCtx = &gBootCtx.se05x_open_ctx; + sss_status_t status; + int ret; - if (se050Ctx == NULL || (len > 0 && data == NULL) || (len == 0) || - !WC_SAFE_SUM_WORD32(se050Ctx->used, len, usedSz)) { - return BAD_FUNC_ARG; + status = ex_sss_se05x_prepare_host(&gBootCtx.host_session, + &gBootCtx.host_ks, connectCtx, &gBootCtx.ex_se05x_auth, + kSSS_AuthType_SCP03); + if (status != kStatus_SSS_Success) { + return status; } - if (se050Ctx->len < usedSz) { - if (se050Ctx->msg == NULL) { - se050Ctx->msg = (byte*)XMALLOC(usedSz, - se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); - if (se050Ctx->msg == NULL) { - return MEMORY_E; - } - XMEMSET(se050Ctx->msg, 0, usedSz); - } - else { - tmp = (byte*)XMALLOC(usedSz, se050Ctx->heap, - DYNAMIC_TYPE_TMP_BUFFER); - if (tmp == NULL) { - return MEMORY_E; - } - XMEMSET(tmp, 0, usedSz); - XMEMCPY(tmp, se050Ctx->msg, se050Ctx->used); - XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); - se050Ctx->msg = tmp; - } - se050Ctx->len = usedSz; + /* PUT KEY is a Security Domain operation. NXP's reference rotation + * application opens SCP03 with applet selection skipped, which makes the + * transport select the SSD before INITIALIZE UPDATE. Normal wolfCrypt + * operations must continue to select the IoT applet. */ + connectCtx->skip_select_applet = (skipSelectApplet != 0); + +#if defined(SMCOM_JRCP_V1) + if (ex_sss_boot_isSocketPortName(portName)) { + connectCtx->connType = kType_SE_Conn_Type_JRCP_V1; + connectCtx->portName = portName; + } +#endif +#if defined(SMCOM_JRCP_V2) + if (ex_sss_boot_isSocketPortName(portName)) { + connectCtx->connType = kType_SE_Conn_Type_JRCP_V2; + connectCtx->portName = portName; + } +#endif +#if defined(RJCT_VCOM) + if (ex_sss_boot_isSerialPortName(portName)) { + connectCtx->connType = kType_SE_Conn_Type_VCOM; + connectCtx->portName = portName; } +#endif +#if defined(SCI2C) + #error "SCI2C is not a valid SE05x connection for runtime SCP03 keys" +#endif +#if defined(T1oI2C) + connectCtx->connType = kType_SE_Conn_Type_T1oI2C; + connectCtx->portName = portName; +#endif +#if defined(SMCOM_PCSC) + connectCtx->connType = kType_SE_Conn_Type_PCSC; + connectCtx->portName = portName; +#endif +#if defined(SMCOM_PN7150) + connectCtx->connType = kType_SE_Conn_Type_NFC; + connectCtx->portName = NULL; +#endif +#if !defined(SMCOM_JRCP_V1) && !defined(SMCOM_JRCP_V2) && \ + !defined(RJCT_VCOM) && !defined(T1oI2C) && !defined(SMCOM_PCSC) && \ + !defined(SMCOM_PN7150) + /* wolfSSL's --with-se050 build consumes an already-configured + * middleware and therefore does not inherit its private transport + * define. T=1 over I2C is the port's documented/default transport. */ + connectCtx->connType = kType_SE_Conn_Type_T1oI2C; + connectCtx->portName = portName; +#endif - XMEMCPY(se050Ctx->msg + se050Ctx->used, data, len); - se050Ctx->used += len; + ret = se050_set_scp03_static_keys(keys); + if (ret != 0) { + return kStatus_SSS_Fail; + } - return 0; + return sss_session_open(&gBootCtx.session, kType_SSS_SE_SE05x, 0, + kSSS_ConnectionType_Encrypted, connectCtx); } +#endif /* SE050_RUNTIME_SCP03 */ -int se050_hash_final(SE050_HASH_Context* se050Ctx, byte* hash, size_t digestLen, - sss_algorithm_t algo) +int wc_se050_init(const char* portName) { - int ret; + int ret; sss_status_t status; - sss_digest_t digest_ctx; - const byte* data = se050Ctx->msg; - int size = (se050Ctx->used) / SSS_BLOCK_SIZE; - int leftover = (se050Ctx->used) % SSS_BLOCK_SIZE; - const byte* blocks = data; - if (cfg_se050_i2c_pi == NULL) { - return WC_HW_E; + if (se050_boot_context_is_open()) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + if (portName == NULL) { + portName = SE050_DEFAULT_PORT; } - status = sss_digest_context_init(&digest_ctx, cfg_se050_i2c_pi, algo, - kMode_SSS_Digest); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + status = ex_sss_boot_open(&gBootCtx, portName); if (status == kStatus_SSS_Success) { - status = sss_digest_init(&digest_ctx); + status = ex_sss_key_store_and_object_init(&gBootCtx); } if (status == kStatus_SSS_Success) { - /* used to send chunks of size 512 */ - while (status == kStatus_SSS_Success && size--) { - status = sss_digest_update(&digest_ctx, blocks, SSS_BLOCK_SIZE); - blocks += SSS_BLOCK_SIZE; - } - if (status == kStatus_SSS_Success && leftover) { - status = sss_digest_update(&digest_ctx, blocks, leftover); +#if defined(WOLFSSL_SE050_SCP03_ROTATE) && defined(SE050_RUNTIME_SCP03) + if (gBootCtx.se05x_open_ctx.auth.authType == kSSS_AuthType_SCP03) { + byte defaultDek[] = EX_SSS_AUTH_SE05X_KEY_DEK; + + ret = se050_set_scp03_dek(defaultDek, + (word32)sizeof(defaultDek)); + ForceZero(defaultDek, sizeof(defaultDek)); + if (ret != 0) { + ex_sss_session_close(&gBootCtx); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + return ret; + } } - if (status == kStatus_SSS_Success) { - status = sss_digest_finish(&digest_ctx, hash, &digestLen); +#endif + ret = wc_se050_set_config(&gBootCtx.session, + se050_boot_host_key_store(), &gBootCtx.ks); + if (ret != 0) { + ex_sss_session_close(&gBootCtx); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); } - sss_digest_context_free(&digest_ctx); - } - if (status == kStatus_SSS_Success) { - /* reset state */ - XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); - ret = se050_hash_init(se050Ctx, se050Ctx->heap); - } else { + #ifdef WOLFSSL_SE050_FACTORY_RESET + if (ret == 0) { + ex_sss_boot_factory_reset(&gBootCtx); + } + #endif + } + else { + ex_sss_session_close(&gBootCtx); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + WOLFSSL_MSG("Failed to open SE050 context"); ret = WC_HW_E; } + return ret; +} - wolfSSL_CryptHwMutexUnLock(); +#ifdef SE050_RUNTIME_SCP03 +static int se050_init_scp03_mode(const char* portName, + const wc_se050_scp03_keys* keys, int skipSelectApplet) +{ + sss_status_t status; + int ret; + + if (keys == NULL) { + return BAD_FUNC_ARG; + } + if (se050_boot_context_is_open()) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + if (portName == NULL) { + portName = SE050_DEFAULT_PORT; + } + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + status = se050_open_scp03(portName, keys, skipSelectApplet); + if (status == kStatus_SSS_Success) { + status = ex_sss_key_store_and_object_init(&gBootCtx); + } + if (status == kStatus_SSS_Success) { + ret = wc_se050_set_config(&gBootCtx.session, + se050_boot_host_key_store(), &gBootCtx.ks); + if (ret != 0) { + ex_sss_session_close(&gBootCtx); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + } + } + else { + ex_sss_session_close(&gBootCtx); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + WOLFSSL_MSG("Failed to open SE050 runtime SCP03 context"); + ret = WC_HW_E; + } return ret; } -void se050_hash_free(SE050_HASH_Context* se050Ctx) +int wc_se050_init_ex(const char* portName, const wc_se050_scp03_keys* keys) { - XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); - se050Ctx->msg = NULL; - se050Ctx->len = 0; - se050Ctx->used = 0; + return se050_init_scp03_mode(portName, keys, 0); } +#endif -#endif /* WOLFSSL_SE050_HASH */ +int wc_se050_close(void) +{ + int ret; -#if defined(WOLFSSL_SE050_CRYPT) && !defined(NO_AES) + if (cfg_se050_i2c_pi != &gBootCtx.session) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) { + return ret; + } + ex_sss_session_close(&gBootCtx); + cfg_se050_i2c_pi = NULL; + gHostKeyStore = NULL; + gKeyStore = NULL; + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + wolfSSL_CryptHwMutexUnLock(); + return 0; +} +#endif -int se050_aes_set_key(Aes* aes, const byte* key, word32 keylen, - const byte* iv, int dir) +#ifdef HAVE_HKDF +int wc_se050_scp03_derive_keys_seed(const byte* seed, word32 seedSz, + wc_se050_scp03_keys* derivedOut) { - int ret = 0; - sss_status_t status; - sss_object_t newKey; - sss_key_store_t host_keystore; - word32 keyId; - int keyCreated = 0; + static const byte encInfo[] = "SE050 SCP03 ENC"; + static const byte macInfo[] = "SE050 SCP03 MAC"; + static const byte dekInfo[] = "SE050 SCP03 DEK"; + int ret; - if (cfg_se050_i2c_pi == NULL) { - return WC_HW_E; + if ((seed == NULL) || (seedSz == 0U) || (derivedOut == NULL)) { + return BAD_FUNC_ARG; } - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + XMEMSET(derivedOut, 0, sizeof(*derivedOut)); + ret = wc_HKDF(WC_SHA256, seed, seedSz, NULL, 0, encInfo, + (word32)sizeof(encInfo) - 1U, derivedOut->enc, + sizeof(derivedOut->enc)); + if (ret == 0) { + ret = wc_HKDF(WC_SHA256, seed, seedSz, NULL, 0, macInfo, + (word32)sizeof(macInfo) - 1U, derivedOut->mac, + sizeof(derivedOut->mac)); + } + if (ret == 0) { + ret = wc_HKDF(WC_SHA256, seed, seedSz, NULL, 0, dekInfo, + (word32)sizeof(dekInfo) - 1U, derivedOut->dek, + sizeof(derivedOut->dek)); + } + if (ret != 0) { + ForceZero(derivedOut, sizeof(*derivedOut)); } + return ret; +} +#endif /* HAVE_HKDF */ + +#if defined(WOLFSSL_SE050_SCP03_ROTATE) && \ + defined(SE050_RUNTIME_SCP03) +#define SE050_SCP03_KEY_SZ 16U +#define SE050_SCP03_KCV_SZ 3U +#define SE050_SCP03_KEY_BLOCK_SZ 23U +#define SE050_SCP03_PUT_KEY_SZ \ + (1U + (3U * SE050_SCP03_KEY_BLOCK_SZ)) +#define SE050_SCP03_CMD_CAPACITY \ + (SE050_SCP03_PUT_KEY_SZ + AES_BLOCK_SIZE) + +static int se050_scp03_get_static_key(sss_object_t* object, byte* key, + size_t keySz) +{ + size_t outSz = keySz; + size_t outBits = keySz * 8U; + sss_status_t status; - (void)dir; - (void)iv; + if ((object == NULL) || (object->keyStore == NULL) || (key == NULL)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + status = sss_host_key_store_get_key(&gBootCtx.host_ks, object, key, + &outSz, &outBits); + if ((status != kStatus_SSS_Success) || (outSz != keySz)) { + ForceZero(key, keySz); + return WC_HW_E; + } + return 0; +} - aes->rounds = keylen/4 + 6; +static int se050_scp03_get_static_keys(wc_se050_scp03_keys* keys) +{ + NXSCP03_StaticCtx_t* staticCtx; + int ret; - /* free existing key in slot first before storing new one */ - ret = wc_se050_erase_object(aes->keyId); - if (ret != 0) { - wolfSSL_CryptHwMutexUnLock(); - return ret; + if (keys == NULL) { + return BAD_FUNC_ARG; } - aes->keyIdSet = 0; - - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); - if (status == kStatus_SSS_Success) { - status = sss_key_store_allocate(&host_keystore, SE050_KEYSTOREID_AES); + staticCtx = gBootCtx.se05x_open_ctx.auth.ctx.scp03.pStatic_ctx; + if (staticCtx == NULL) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - if (status == kStatus_SSS_Success) { - status = sss_key_object_init(&newKey, &host_keystore); + ret = se050_scp03_get_static_key(&staticCtx->Enc, keys->enc, + sizeof(keys->enc)); + if (ret == 0) { + ret = se050_scp03_get_static_key(&staticCtx->Mac, keys->mac, + sizeof(keys->mac)); } - if (status == kStatus_SSS_Success) { - keyId = se050_allocate_key(SE050_AES_KEY); - status = sss_key_object_allocate_handle(&newKey, keyId, - kSSS_KeyPart_Default, kSSS_CipherType_AES, keylen, - kKeyObject_Mode_Transient); + if (ret == 0) { + ret = se050_scp03_get_static_key(&staticCtx->Dek, keys->dek, + sizeof(keys->dek)); } - if (status == kStatus_SSS_Success) { - status = sss_key_store_set_key(&host_keystore, &newKey, key, keylen, - keylen * 8, NULL, 0); + if (ret != 0) { + ForceZero(keys, sizeof(*keys)); } + return ret; +} - if (status == kStatus_SSS_Success) { - keyCreated = 1; - aes->keyId = keyId; - aes->keyIdSet = 1; - ret = 0; - } - else { - if (keyCreated) { - sss_key_store_erase_key(&host_keystore, &newKey); - sss_key_object_free(&newKey); +static int se050_scp03_encrypt_block(const byte* key, const byte* in, + byte* out) +{ + Aes aes; + int ret; + /* in/out point into byte-packed APDU command buffers and are not + * guaranteed to be 4-byte aligned. HW-accelerated AES backends (e.g. + * STM32_CRYPTO) cast these pointers to uint32_t* internally, so an + * unaligned buffer here faults. Stage through aligned local buffers. */ + ALIGN16 byte alignedIn[SE050_SCP03_KEY_SZ]; + ALIGN16 byte alignedOut[SE050_SCP03_KEY_SZ]; + + XMEMSET(&aes, 0, sizeof(aes)); +#ifdef WOLFSSL_SE050_CRYPT + /* SCP03 static keys are host secrets. Never route DEK wrapping or KCV + * generation back through the secure element. */ + aes.useSWCrypt = 1; +#endif + XMEMCPY(alignedIn, in, sizeof(alignedIn)); + ret = wc_AesSetKey(&aes, key, SE050_SCP03_KEY_SZ, NULL, AES_ENCRYPTION); + if (ret == 0) { + ret = wc_AesEncryptDirect(&aes, alignedOut, alignedIn); + if (ret == 0) { + XMEMCPY(out, alignedOut, sizeof(alignedOut)); } - ret = WC_HW_E; } + ForceZero(alignedIn, sizeof(alignedIn)); + ForceZero(alignedOut, sizeof(alignedOut)); + wc_AesFree(&aes); + return ret; +} - wolfSSL_CryptHwMutexUnLock(); +static int se050_scp03_make_key_block(const byte* newKey, + const byte* currentDek, byte* block, byte* kcv) +{ + byte checkInput[SE050_SCP03_KEY_SZ]; + int ret; + block[0] = 0x88; /* GlobalPlatform AES key type. */ + block[1] = SE050_SCP03_KEY_SZ + 1U; + block[2] = SE050_SCP03_KEY_SZ; + ret = se050_scp03_encrypt_block(currentDek, newKey, &block[3]); + if (ret == 0) { + XMEMSET(checkInput, 1, sizeof(checkInput)); + ret = se050_scp03_encrypt_block(newKey, checkInput, kcv); + } + if (ret == 0) { + block[3U + SE050_SCP03_KEY_SZ] = SE050_SCP03_KCV_SZ; + XMEMCPY(&block[4U + SE050_SCP03_KEY_SZ], kcv, + SE050_SCP03_KCV_SZ); + } + ForceZero(checkInput, sizeof(checkInput)); return ret; } -int se050_aes_crypt(Aes* aes, const byte* in, byte* out, word32 sz, int dir, - sss_algorithm_t algorithm) +static int se050_scp03_put_keys(const wc_se050_scp03_keys* newKeys, + byte keyVersion, int* keysChanged) { - int ret = 0; - sss_status_t status; - sss_object_t keyObject; - sss_key_store_t host_keystore; + byte cmd[SE050_SCP03_CMD_CAPACITY]; + byte expected[1U + (3U * SE050_SCP03_KCV_SZ)]; + byte response[64]; + byte currentDek[SE050_SCP03_KEY_SZ]; + size_t responseSz = sizeof(response); + size_t currentDekSz = sizeof(currentDek); + size_t currentDekBits = sizeof(currentDek) * 8U; + NXSCP03_StaticCtx_t* staticCtx; + sss_se05x_session_t* session; + tlvHeader_t header = {{0x80, 0xD8, 0, 0x81}}; + sss_status_t status; + smStatus_t smStatus; + word32 i; + int ret; - if (cfg_se050_i2c_pi == NULL) { + if ((newKeys == NULL) || (keysChanged == NULL)) { + return BAD_FUNC_ARG; + } + *keysChanged = 0; + if ((cfg_se050_i2c_pi != &gBootCtx.session) || + (gBootCtx.session.subsystem != kType_SSS_SE_SE05x) || + (gBootCtx.se05x_open_ctx.auth.authType != kSSS_AuthType_SCP03) || + (gBootCtx.se05x_open_ctx.skip_select_applet != 1)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + + staticCtx = gBootCtx.se05x_open_ctx.auth.ctx.scp03.pStatic_ctx; + if ((staticCtx == NULL) || (staticCtx->Dek.keyStore == NULL)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + + status = sss_host_key_store_get_key(&gBootCtx.host_ks, + &staticCtx->Dek, currentDek, ¤tDekSz, ¤tDekBits); + if ((status != kStatus_SSS_Success) || + (currentDekSz != sizeof(currentDek))) { + ForceZero(currentDek, sizeof(currentDek)); return WC_HW_E; } - if (aes->keyIdSet == 0) { - return BAD_FUNC_ARG; + + cmd[0] = keyVersion; + expected[0] = keyVersion; + for (i = 0; i < 3U; i++) { + const byte* key = (i == 0U) ? newKeys->enc : + ((i == 1U) ? newKeys->mac : newKeys->dek); + byte kcv[SE050_SCP03_KEY_SZ]; + + ret = se050_scp03_make_key_block(key, currentDek, + &cmd[1U + (i * SE050_SCP03_KEY_BLOCK_SZ)], kcv); + if (ret != 0) { + ForceZero(kcv, sizeof(kcv)); + ForceZero(currentDek, sizeof(currentDek)); + ForceZero(cmd, sizeof(cmd)); + return ret; + } + XMEMCPY(&expected[1U + (i * SE050_SCP03_KCV_SZ)], kcv, + SE050_SCP03_KCV_SZ); + ForceZero(kcv, sizeof(kcv)); } + ForceZero(currentDek, sizeof(currentDek)); - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + header.hdr[2] = keyVersion; + session = (sss_se05x_session_t*)&gBootCtx.session; + ret = wc_se050_lock(); + if (ret == 0) { + smStatus = DoAPDUTxRx_s_Case4(&session->s_ctx, &header, cmd, + SE050_SCP03_PUT_KEY_SZ, response, &responseSz); + wc_se050_unlock(); + } + else { + smStatus = SM_NOT_OK; } + ForceZero(cmd, sizeof(cmd)); - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); - if (status == kStatus_SSS_Success) { - status = sss_key_store_allocate(&host_keystore, SE050_KEYSTOREID_AES); + if ((ret != 0) || (smStatus != SM_OK) || (responseSz < 2U) || + ((((word32)response[responseSz - 2U] << 8) | + response[responseSz - 1U]) != SM_OK)) { + ForceZero(expected, sizeof(expected)); + ForceZero(response, sizeof(response)); + return WC_HW_E; } - if (status == kStatus_SSS_Success) { - status = sss_key_object_init(&keyObject, &host_keystore); + /* A successful status means the Security Domain has committed the key + * set, even if the response below is malformed or its KCV echo does not + * match. The caller must reconnect with newKeys in that case. */ + *keysChanged = 1; + if (responseSz < (sizeof(expected) + 2U)) { + ForceZero(expected, sizeof(expected)); + ForceZero(response, sizeof(response)); + return WC_HW_E; } - if (status == kStatus_SSS_Success) { - status = sss_key_object_get_handle(&keyObject, aes->keyId); + if (ConstantCompare(response, expected, sizeof(expected)) != 0) { + ForceZero(expected, sizeof(expected)); + ForceZero(response, sizeof(response)); + return AES_GCM_AUTH_E; } + ForceZero(expected, sizeof(expected)); + ForceZero(response, sizeof(response)); - /* The first call to this function needs an initialization call, - * subsequent calls just need to call update */ - if (status == kStatus_SSS_Success && aes->ctxInitDone == 0) { - sss_mode_t mode; + return 0; +} - XMEMSET(&mode, 0, sizeof(mode)); - if (dir == AES_DECRYPTION) - mode = kMode_SSS_Decrypt; - else if (dir == AES_ENCRYPTION) - mode = kMode_SSS_Encrypt; +int wc_se050_scp03_rotate_keys(const wc_se050_scp03_keys* newKeys, + byte keyVersion) +{ + wc_se050_scp03_keys currentKeys; + const wc_se050_scp03_keys* reopenKeys; + const char* portName; + int keysChanged = 0; + int closeRet; + int reopenRet; + int ret; - if (status == kStatus_SSS_Success) { - status = sss_symmetric_context_init(&aes->aes_ctx, - cfg_se050_i2c_pi, &keyObject, algorithm, mode); - } - if (status == kStatus_SSS_Success) { - aes->ctxInitDone = 1; - status = sss_cipher_init(&aes->aes_ctx, (uint8_t*)aes->reg, - sizeof(aes->reg)); - } + if (newKeys == NULL) { + return BAD_FUNC_ARG; } - if (status == kStatus_SSS_Success) { - size_t outSz = (size_t)sz; - status = sss_cipher_update(&aes->aes_ctx, in, sz, out, &outSz); + if ((cfg_se050_i2c_pi != &gBootCtx.session) || + (gBootCtx.session.subsystem != kType_SSS_SE_SE05x) || + (gBootCtx.se05x_open_ctx.auth.authType != kSSS_AuthType_SCP03) || + (gBootCtx.se05x_open_ctx.skip_select_applet != 0)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - ret = (status == kStatus_SSS_Success) ? 0 : WC_HW_E; + portName = gBootCtx.se05x_open_ctx.portName; + ret = se050_scp03_get_static_keys(¤tKeys); + if (ret != 0) { + return ret; + } - wolfSSL_CryptHwMutexUnLock(); + /* Platform SCP03 protects both the IoT applet and its Security Domain, + * but PUT KEY is accepted only by the latter. Reopen against the SSD for + * the update, then always return to a fresh IoT applet session. */ + ret = wc_se050_close(); + if (ret == 0) { + ret = se050_init_scp03_mode(portName, ¤tKeys, 1); + } + if (ret == 0) { + ret = se050_scp03_put_keys(newKeys, keyVersion, &keysChanged); + closeRet = wc_se050_close(); + if (closeRet != 0) { + ret = closeRet; + } + } + if (gBootCtx.session.subsystem == kType_SSS_SubSystem_NONE) { + reopenKeys = (keysChanged != 0) ? newKeys : ¤tKeys; + reopenRet = se050_init_scp03_mode(portName, reopenKeys, 0); + if (reopenRet != 0) { + ret = reopenRet; + } + } + ForceZero(¤tKeys, sizeof(currentKeys)); return ret; } -void se050_aes_free(Aes* aes) +#ifdef HAVE_HKDF +int wc_se050_scp03_rotate_keys_seed(const byte* seed, word32 seedSz, + byte keyVersion, wc_se050_scp03_keys* derivedOut) { - if (aes == NULL) { - return; + wc_se050_scp03_keys keys; + int ret; + + ret = wc_se050_scp03_derive_keys_seed(seed, seedSz, &keys); + if ((ret == 0) && (derivedOut != NULL)) { + XMEMCPY(derivedOut, &keys, sizeof(keys)); + } + if (ret == 0) { + ret = wc_se050_scp03_rotate_keys(&keys, keyVersion); } + ForceZero(&keys, sizeof(keys)); + return ret; +} +#endif /* HAVE_HKDF */ +#endif /* WOLFSSL_SE050_SCP03_ROTATE && SE050_RUNTIME_SCP03 */ - if (aes->ctxInitDone) { - sss_symmetric_context_free(&aes->aes_ctx); +static int se050_erase_object_locked(word32 id) +{ + int ret = 0; + sss_object_t object; + sss_key_store_t host_keystore; + sss_status_t status = kStatus_SSS_Success; - /* sets back to zero to indicate that a free has been called */ - aes->ctxInitDone = 0; + status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, + SE050_KEYSTOREID_GENERIC); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&object, &host_keystore); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_get_handle(&object, id); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_erase_key(&host_keystore, &object); + sss_key_object_free(&object); } - aes->keyId = 0; - aes->keyIdSet = 0; -} + if (status != kStatus_SSS_Success) { + ret = WC_HW_E; + } -#endif /* WOLFSSL_SE050_CRYPT && !NO_AES */ + return ret; +} /** - * Get size of a SE05X secure object at specified object ID. - * - * keystore SE050 keystore associated with object - * keyId SE050 key ID in which object is stored + * Erase and free an object stored in SE050. * - * Size returned depends on object type: - * ECC key: curve size - * RSA/AES/DES/HMAC key: key size - * Binary file: file size + * keyId ID of object to erase * - * Return size or negative on error + * Returns 0 on success, negative on error. */ -static int se050_get_object_size(sss_key_store_t* keystore, word32 keyId) +int wc_se050_erase_object(word32 id) { - uint16_t size = 0; - smStatus_t status = SM_NOT_OK; - sss_se05x_key_store_t* se05x_keystore = NULL; + int ret; - if (keystore == NULL) { - return BAD_FUNC_ARG; - } +#ifdef SE050_DEBUG + printf("wc_se050_erase_object: id %d\n", id); +#endif + + if (cfg_se050_i2c_pi == NULL) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + ret = se050_erase_object_locked(id); + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} + +word32 se050_allocate_key(int keyType) +{ + word32 keyId = 0; + static word32 keyId_allocator = SE050_KEYID_START; + switch (keyType) { + case SE050_AES_KEY: + case SE050_ECC_KEY: + case SE050_RSA_KEY: + case SE050_ED25519_KEY: + case SE050_CURVE25519_KEY: + case SE050_ANY_KEY: + keyId = keyId_allocator++; + break; + } +#ifdef SE050_DEBUG + printf("se050_allocate_key: keyId %d\n", keyId); +#endif + return keyId; +} + +#if !defined(WC_NO_RNG) && !defined(WOLFSSL_SE050_NO_TRNG) +int se050_get_random_number(uint32_t count, uint8_t* rand_out) +{ + int ret = 0; + sss_status_t status; + sss_rng_context_t rng; + +#ifdef SE050_DEBUG + printf("se050_get_random_number: %p (%d)\n", rand_out, count); +#endif + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + status = sss_rng_context_init(&rng, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_rng_get_random(&rng, rand_out, count); + } + if (status == kStatus_SSS_Success) { + status = sss_rng_context_free(&rng); + } + if (status != kStatus_SSS_Success) { + ret = RNG_FAILURE_E; + } + + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} +#endif /* !WC_NO_RNG && !WOLFSSL_SE050_NO_TRNG */ + +#ifdef WOLFSSL_SE050_HASH + +/* Used for sha/sha224/sha384/sha512 */ +int se050_hash_init(SE050_HASH_Context* se050Ctx, void* heap) +{ + se050Ctx->heap = heap; + se050Ctx->len = 0; + se050Ctx->used = 0; + se050Ctx->msg = NULL; + return 0; +} + +int se050_hash_copy(SE050_HASH_Context* src, SE050_HASH_Context* dst) +{ + if (src == NULL || dst == NULL || (src->used != dst->used)) { + return BAD_FUNC_ARG; + } + + if (src->used > 0) { + /* dst->msg points to same buffer as src->msg, needs to be allocated + * and dep copied over instead of plain pointer copy */ + dst->msg = (byte*)XMALLOC(src->used, dst->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (dst->msg == NULL) { + PRINTF("Tried to allocate %d bytes\n", dst->used); + return MEMORY_E; + } + XMEMSET(dst->msg, 0, dst->used); + XMEMCPY(dst->msg, src->msg, src->used); + dst->used = src->used; + dst->len = src->used; + } else { + dst->msg = NULL; + dst->len = 0; + dst->used = 0; + } + + return 0; +} + +int se050_hash_update(SE050_HASH_Context* se050Ctx, const byte* data, word32 len) +{ + byte* tmp = NULL; + word32 usedSz = 0; + + if (se050Ctx == NULL || (len > 0 && data == NULL) || (len == 0) || + !WC_SAFE_SUM_WORD32(se050Ctx->used, len, usedSz)) { + return BAD_FUNC_ARG; + } + + if (se050Ctx->len < usedSz) { + if (se050Ctx->msg == NULL) { + se050Ctx->msg = (byte*)XMALLOC(usedSz, + se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (se050Ctx->msg == NULL) { + return MEMORY_E; + } + XMEMSET(se050Ctx->msg, 0, usedSz); + } + else { + tmp = (byte*)XMALLOC(usedSz, se050Ctx->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (tmp == NULL) { + return MEMORY_E; + } + XMEMSET(tmp, 0, usedSz); + XMEMCPY(tmp, se050Ctx->msg, se050Ctx->used); + XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); + se050Ctx->msg = tmp; + } + se050Ctx->len = usedSz; + } + + XMEMCPY(se050Ctx->msg + se050Ctx->used, data, len); + se050Ctx->used += len; + + return 0; +} + +int se050_hash_final(SE050_HASH_Context* se050Ctx, byte* hash, size_t digestLen, + sss_algorithm_t algo) +{ + int ret; + sss_status_t status; + sss_digest_t digest_ctx; + const byte* data = se050Ctx->msg; + int size = (se050Ctx->used) / SSS_BLOCK_SIZE; + int leftover = (se050Ctx->used) % SSS_BLOCK_SIZE; + const byte* blocks = data; + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = sss_digest_context_init(&digest_ctx, cfg_se050_i2c_pi, algo, + kMode_SSS_Digest); + if (status == kStatus_SSS_Success) { + status = sss_digest_init(&digest_ctx); + } + if (status == kStatus_SSS_Success) { + /* used to send chunks of size 512 */ + while (status == kStatus_SSS_Success && size--) { + status = sss_digest_update(&digest_ctx, blocks, SSS_BLOCK_SIZE); + blocks += SSS_BLOCK_SIZE; + } + if (status == kStatus_SSS_Success && leftover) { + status = sss_digest_update(&digest_ctx, blocks, leftover); + } + if (status == kStatus_SSS_Success) { + status = sss_digest_finish(&digest_ctx, hash, &digestLen); + } + sss_digest_context_free(&digest_ctx); + } + + if (status == kStatus_SSS_Success) { + /* reset state */ + XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); + ret = se050_hash_init(se050Ctx, se050Ctx->heap); + } else { + ret = WC_HW_E; + } + + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} + +void se050_hash_free(SE050_HASH_Context* se050Ctx) +{ + XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); + se050Ctx->msg = NULL; + se050Ctx->len = 0; + se050Ctx->used = 0; +} + +#endif /* WOLFSSL_SE050_HASH */ + +#if defined(WOLFSSL_SE050_CRYPT) && !defined(NO_AES) + +int se050_aes_set_key(Aes* aes, const byte* key, word32 keylen, + const byte* iv, int dir) +{ + int ret = 0; + sss_status_t status; + sss_object_t newKey; + sss_key_store_t host_keystore; + word32 keyId; + int keyCreated = 0; + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + (void)dir; + (void)iv; + + aes->rounds = keylen/4 + 6; + + /* Free an existing key in the slot before storing a replacement. */ + if (aes->keyIdSet != 0U) { + ret = se050_erase_object_locked(aes->keyId); + if (ret != 0) { + wolfSSL_CryptHwMutexUnLock(); + return ret; + } + aes->keyId = 0; + aes->keyIdSet = 0; + } + + status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, SE050_KEYSTOREID_AES); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&newKey, &host_keystore); + } + if (status == kStatus_SSS_Success) { + keyId = se050_allocate_key(SE050_AES_KEY); + status = sss_key_object_allocate_handle(&newKey, keyId, + kSSS_KeyPart_Default, kSSS_CipherType_AES, keylen, + kKeyObject_Mode_Transient); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_set_key(&host_keystore, &newKey, key, keylen, + keylen * 8, NULL, 0); + } + + if (status == kStatus_SSS_Success) { + keyCreated = 1; + aes->keyId = keyId; + aes->keyIdSet = 1; + ret = 0; + } + else { + if (keyCreated) { + sss_key_store_erase_key(&host_keystore, &newKey); + sss_key_object_free(&newKey); + } + ret = WC_HW_E; + } + + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} + +int se050_aes_crypt(Aes* aes, const byte* in, byte* out, word32 sz, int dir, + sss_algorithm_t algorithm) +{ + int ret = 0; + sss_status_t status; + sss_object_t keyObject; + sss_key_store_t host_keystore; + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + if (aes->keyIdSet == 0) { + return BAD_FUNC_ARG; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, SE050_KEYSTOREID_AES); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&keyObject, &host_keystore); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_get_handle(&keyObject, aes->keyId); + } + + /* The first call to this function needs an initialization call, + * subsequent calls just need to call update */ + if (status == kStatus_SSS_Success && aes->ctxInitDone == 0) { + sss_mode_t mode; + + XMEMSET(&mode, 0, sizeof(mode)); + if (dir == AES_DECRYPTION) + mode = kMode_SSS_Decrypt; + else if (dir == AES_ENCRYPTION) + mode = kMode_SSS_Encrypt; + + if (status == kStatus_SSS_Success) { + status = sss_symmetric_context_init(&aes->aes_ctx, + cfg_se050_i2c_pi, &keyObject, algorithm, mode); + } + if (status == kStatus_SSS_Success) { + aes->ctxInitDone = 1; + status = sss_cipher_init(&aes->aes_ctx, (uint8_t*)aes->reg, + sizeof(aes->reg)); + } + } + if (status == kStatus_SSS_Success) { + size_t outSz = (size_t)sz; + status = sss_cipher_update(&aes->aes_ctx, in, sz, out, &outSz); + } + + ret = (status == kStatus_SSS_Success) ? 0 : WC_HW_E; + + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} + +void se050_aes_free(Aes* aes) +{ + if (aes == NULL) { + return; + } + + if (aes->ctxInitDone) { + sss_symmetric_context_free(&aes->aes_ctx); + + /* sets back to zero to indicate that a free has been called */ + aes->ctxInitDone = 0; + } + + aes->keyId = 0; + aes->keyIdSet = 0; +} + +#endif /* WOLFSSL_SE050_CRYPT && !NO_AES */ + +/** + * Get size of a SE05X secure object at specified object ID. + * + * keystore SE050 keystore associated with object + * keyId SE050 key ID in which object is stored + * + * Size returned depends on object type: + * ECC key: curve size + * RSA/AES/DES/HMAC key: key size + * Binary file: file size + * + * Return size or negative on error + */ +static int se050_get_object_size(sss_key_store_t* keystore, word32 keyId) +{ + uint16_t size = 0; + smStatus_t status = SM_NOT_OK; + sss_se05x_key_store_t* se05x_keystore = NULL; + + if (keystore == NULL) { + return BAD_FUNC_ARG; + } se05x_keystore = (sss_se05x_key_store_t*)keystore; status = Se05x_API_ReadSize(&se05x_keystore->session->s_ctx, @@ -554,125 +1336,939 @@ static int se050_get_object_size(sss_key_store_t* keystore, word32 keyId) return WC_HW_E; } - return (int)size; + return (int)size; +} + +/** + * Insert binary object into SE050 as persistent object. + * + * keyId SE050 key ID to store object in + * object binary object data + * objectSz size of binary object, bytes + * + * Returns 0 on success, negative on error + */ +static int se050_insert_binary_object(word32 keyId, const byte* object, + word32 objectSz, const sss_policy_t* policy, int requireNew) +{ + int ret = 0; + sss_object_t newObj; + sss_key_store_t host_keystore; + sss_status_t status = kStatus_SSS_Success; + + if ((cfg_se050_i2c_pi == NULL) || (object == NULL) || (objectSz == 0U)) { + return BAD_FUNC_ARG; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + /* Avoid key ID conflicts with temporary key storage */ + if (keyId >= SE050_KEYID_START) { + wolfSSL_CryptHwMutexUnLock(); + return BAD_FUNC_ARG; + } + + if (requireNew) { + status = se050_require_new_object(keyId); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_context_init(&host_keystore, + cfg_se050_i2c_pi); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&newObj, &host_keystore); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_allocate_handle(&newObj, keyId, + kSSS_KeyPart_Default, kSSS_CipherType_Binary, objectSz, + kKeyObject_Mode_Persistent); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_set_key(&host_keystore, &newObj, object, + objectSz, (objectSz * 8), (void*)policy, 0); + } + wolfSSL_CryptHwMutexUnLock(); + + if (status != kStatus_SSS_Success) { + ret = WC_HW_E; + } + + return ret; +} + +int wc_se050_insert_binary_object_policy(word32 keyId, const byte* object, + word32 objectSz, const sss_policy_t* policy) +{ + return se050_insert_binary_object(keyId, object, objectSz, policy, 0); +} + +int wc_se050_insert_binary_object_ex(word32 keyId, const byte* object, + word32 objectSz, word32 policyFlags, word32 authObjId) +{ + int ret; + const sss_policy_t* policy = NULL; + se050_policy_set policySet; + + ret = se050_build_policy_set(SE050_POLICY_OBJECT_FILE, policyFlags, + authObjId, &policySet); + if (ret != 0) { + return ret; + } + if (policyFlags != 0U) { + policy = &policySet.policy; + } + return se050_insert_binary_object(keyId, object, objectSz, policy, 1); +} + +int wc_se050_insert_binary_object(word32 keyId, const byte* object, + word32 objectSz) +{ + return wc_se050_insert_binary_object_policy(keyId, object, objectSz, + NULL); } /** - * Insert binary object into SE050 as persistent object. + * Get binary object from SE050 from specified key ID. * - * keyId SE050 key ID to store object in - * object binary object data - * objectSz size of binary object, bytes + * keyId SE050 key ID to get binary object from + * out output buffer to place binary object + * outSz size of output buffer on input, size of written object on output * - * Returns 0 on success, negative on error + * Returns 0 on success, LENGTH_ONLY_E if out is NULL with outSz set to + * required buffer size, and other negative on error. */ -int wc_se050_insert_binary_object(word32 keyId, const byte* object, - word32 objectSz) +int wc_se050_get_binary_object(word32 keyId, byte* out, word32* outSz) { int ret = 0; - sss_object_t newObj; + sss_object_t object; sss_key_store_t host_keystore; sss_status_t status = kStatus_SSS_Success; + size_t outBitSz = 0; + + /* If out is NULL, outSz set to required size and LENGTH_ONLY_E returned */ + if (outSz == NULL) { + return BAD_FUNC_ARG; + } + if (cfg_se050_i2c_pi == NULL) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } if (wolfSSL_CryptHwMutexLock() != 0) { return BAD_MUTEX_E; } - /* Avoid key ID conflicts with temporary key storage */ - if (keyId >= SE050_KEYID_START) { + status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&object, &host_keystore); + } + if (status == kStatus_SSS_Success) { + ret = se050_get_object_size(&host_keystore, keyId); + if (ret < 0) { + status = kStatus_SSS_Fail; + } + else { + if (out == NULL) { + *outSz = ret; + wolfSSL_CryptHwMutexUnLock(); + return WC_NO_ERR_TRACE(LENGTH_ONLY_E); + } + if ((word32)ret > *outSz) { + WOLFSSL_MSG("Output buffer not large enough for object"); + wolfSSL_CryptHwMutexUnLock(); + return BAD_LENGTH_E; + } + ret = 0; + } + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_get_handle(&object, keyId); + } + if (status == kStatus_SSS_Success) { + outBitSz = (*outSz) * 8; + status = sss_key_store_get_key(&host_keystore, &object, out, + (size_t*)outSz, &outBitSz); + } + wolfSSL_CryptHwMutexUnLock(); + + if (status != kStatus_SSS_Success) { + ret = WC_HW_E; + } + + return ret; +} + +int wc_se050_get_object_attributes(word32 keyId, byte* attr, word32* attrSz) +{ +#if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && \ + SSS_HAVE_SE05X_VER_GTE_07_02 + pSe05xSession_t session; + smStatus_t status; + size_t size; + + if ((attr == NULL) || (attrSz == NULL) || (*attrSz == 0U)) { + return BAD_FUNC_ARG; + } + if (cfg_se050_i2c_pi == NULL) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + session = wc_se050_get_se05x_session(); + if (session == NULL) { wolfSSL_CryptHwMutexUnLock(); + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + + size = *attrSz; + status = Se05x_API_ReadObjectAttributes(session, keyId, attr, &size); + *attrSz = (word32)size; + wolfSSL_CryptHwMutexUnLock(); + + return (status == SM_OK) ? 0 : WC_HW_E; +#else + (void)keyId; + (void)attr; + (void)attrSz; + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); +#endif +} + +#ifndef WOLFSSL_SE050_NO_ATTEST + +#define SE050_ATTEST_RANDOM_SIZE 16U +#define SE050_ATTR_FIXED_SIZE 14U +#define SE050_ATTR_POLICY_MIN 8U +#define SE050_TLV_OVERHEAD 4U + +static word32 se050_get_u32(const byte* in) +{ + return ((word32)in[0] << 24) | ((word32)in[1] << 16) | + ((word32)in[2] << 8) | (word32)in[3]; +} + +static int se050_attest_algorithm(enum wc_HashType hashAlgo, + word32 cipherType, sss_algorithm_t* algorithm) +{ + int isRsa; + + if (algorithm == NULL) { return BAD_FUNC_ARG; } - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + isRsa = (cipherType == (word32)kSSS_CipherType_RSA) || + (cipherType == (word32)kSSS_CipherType_RSA_CRT); + + if (hashAlgo == WC_HASH_TYPE_SHA) { + *algorithm = isRsa ? kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA1 : + kAlgorithm_SSS_ECDSA_SHA1; + } + else if (hashAlgo == WC_HASH_TYPE_SHA224) { + *algorithm = isRsa ? kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA224 : + kAlgorithm_SSS_ECDSA_SHA224; + } + else if (hashAlgo == WC_HASH_TYPE_SHA256) { + *algorithm = isRsa ? kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA256 : + kAlgorithm_SSS_ECDSA_SHA256; + } + else if (hashAlgo == WC_HASH_TYPE_SHA384) { + *algorithm = isRsa ? kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA384 : + kAlgorithm_SSS_ECDSA_SHA384; + } + else if (hashAlgo == WC_HASH_TYPE_SHA512) { + *algorithm = isRsa ? kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA512 : + kAlgorithm_SSS_ECDSA_SHA512; + } + else { + return BAD_FUNC_ARG; + } + + if (!isRsa && + (cipherType != (word32)kSSS_CipherType_EC_NIST_P) && + (cipherType != (word32)kSSS_CipherType_EC_NIST_K) && + (cipherType != (word32)kSSS_CipherType_EC_BRAINPOOL)) { + return BAD_FUNC_ARG; + } + + return 0; +} + +static void se050_parse_attested_attributes(const byte* attr, word32 attrSz, + wc_se050_attst_result* result) +{ + word32 i; + word32 authId = 0U; + word32 header; + word32 flags = 0U; + word32 entryLen; + int haveAuthId = 0; + + if ((attr == NULL) || (result == NULL) || + (attrSz < (SE050_ATTR_FIXED_SIZE + 1U))) { + return; + } + + i = SE050_ATTR_FIXED_SIZE; + while ((i < attrSz) && (attr[i] >= SE050_ATTR_POLICY_MIN)) { + entryLen = attr[i]; + if ((entryLen > (attrSz - i - 1U)) || (entryLen < 8U)) { + return; + } + if (!haveAuthId) { + authId = se050_get_u32(attr + i + 1U); + haveAuthId = 1; + } + header = se050_get_u32(attr + i + 5U); + if ((header & 0x00040000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_DELETE; + if ((header & 0x00100000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_WRITE; + if ((header & 0x00200000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_READ; + if ((header & 0x10000000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_SIGN; + if ((header & 0x08000000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_VERIFY; + if ((header & 0x02000000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_ENCRYPT; + if ((header & 0x01000000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_DECRYPT; + if ((header & 0x04000000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_KA; + if ((header & 0x00800000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_KD; + if ((header & 0x00080000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_GEN; + if ((header & 0x00001000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_IMPORT_EXPORT; + if ((header & 0x00008000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_ATTEST; + if ((header & 0x00020000U) != 0U) + flags |= WC_SE050_POLICY_REQUIRE_SM; + i += entryLen + 1U; + } + + if (i < attrSz) { + result->origin = attr[i]; + } + result->authObjId = authId; + result->policyFlags = flags; +} + +int wc_se050_attest_object(word32 keyId, word32 attestKeyId, + enum wc_HashType hashAlgo, const byte* random, word32 randomSz, + wc_se050_attst_result* result) +{ + int ret = 0; + int i; + size_t valueSz; + size_t valueBitSz = 0; + sss_algorithm_t algorithm = kAlgorithm_None; + sss_key_store_t keyStore; + sss_object_t object; + sss_object_t attestObject; + sss_se05x_object_t* seObject; + sss_se05x_object_t* seAttestObject; + sss_status_t status = kStatus_SSS_Fail; + + if ((result == NULL) || (keyId == attestKeyId) || (random == NULL) || + (randomSz != SE050_ATTEST_RANDOM_SIZE)) { + return BAD_FUNC_ARG; + } + if (cfg_se050_i2c_pi == NULL) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + + XMEMSET(result, 0, sizeof(*result)); + result->hashAlgo = hashAlgo; + XMEMCPY(result->freshness, random, SE050_ATTEST_RANDOM_SIZE); + for (i = 0; i < SE05X_MAX_ATTST_DATA; i++) { + result->raw.data[i].attributeLen = + sizeof(result->raw.data[i].attribute); + result->raw.data[i].chipIdLen = sizeof(result->raw.data[i].chipId); + result->raw.data[i].signatureLen = + sizeof(result->raw.data[i].signature); + result->raw.data[i].timeStampLen = + sizeof(result->raw.data[i].timeStamp); + #if SSS_HAVE_SE05X_VER_GTE_07_02 + result->raw.data[i].cmdLen = sizeof(result->raw.data[i].cmd); + result->raw.data[i].objSizeLen = + sizeof(result->raw.data[i].objSize); + #else + result->raw.data[i].outrandomLen = + sizeof(result->raw.data[i].outrandom); + #endif + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = sss_key_store_context_init(&keyStore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) + status = sss_key_object_init(&object, &keyStore); + if (status == kStatus_SSS_Success) + status = sss_key_object_get_handle(&object, keyId); + if (status == kStatus_SSS_Success) + status = sss_key_object_init(&attestObject, &keyStore); + if (status == kStatus_SSS_Success) + status = sss_key_object_get_handle(&attestObject, attestKeyId); + + seObject = (sss_se05x_object_t*)&object; + seAttestObject = (sss_se05x_object_t*)&attestObject; if (status == kStatus_SSS_Success) { - status = sss_key_object_init(&newObj, &host_keystore); + ret = se050_attest_algorithm(hashAlgo, seAttestObject->cipherType, + &algorithm); + if (ret != 0) + status = kStatus_SSS_Fail; } if (status == kStatus_SSS_Success) { - status = sss_key_object_allocate_handle(&newObj, keyId, - kSSS_KeyPart_Default, kSSS_CipherType_Binary, objectSz, - kKeyObject_Mode_Persistent); + result->cipherType = seObject->cipherType; + result->objectType = seObject->objectType; + result->curveId = seObject->curve_id; + valueSz = sizeof(result->value); + status = sss_se05x_key_store_get_key_attst( + (sss_se05x_key_store_t*)&keyStore, seObject, result->value, + &valueSz, &valueBitSz, seAttestObject, algorithm, + result->freshness, sizeof(result->freshness), &result->raw); + if (status == kStatus_SSS_Success) { + result->valueSz = (word32)valueSz; + } + } + wolfSSL_CryptHwMutexUnLock(); + + if (status != kStatus_SSS_Success) { + return (ret != 0) ? ret : WC_HW_E; + } + if ((result->raw.valid_number == 0U) || + (result->raw.valid_number > SE05X_MAX_ATTST_DATA)) { + return WC_HW_E; + } + + se050_parse_attested_attributes(result->raw.data[0].attribute, + (word32)result->raw.data[0].attributeLen, result); + return 0; +} + +#if !defined(NO_HASH_WRAPPER) && !defined(NO_SIG_WRAPPER) && \ + !defined(NO_ASN) +static int se050_attested_component(const wc_se050_attst_result* result, + word32 componentIndex, byte* component, word32* componentSz) +{ + if ((result == NULL) || (component == NULL) || (componentSz == NULL) || + (componentIndex >= result->raw.valid_number)) { + return BAD_FUNC_ARG; + } + + if ((result->cipherType == (word32)kSSS_CipherType_RSA) || + (result->cipherType == (word32)kSSS_CipherType_RSA_CRT)) { + #if !defined(NO_RSA) && !defined(WOLFSSL_RSA_VERIFY_ONLY) + int ret; + RsaKey key; + word32 idx = 0; + byte exponent[8]; + word32 exponentSz = sizeof(exponent); + word32 modulusSz = *componentSz; + + ret = wc_InitRsaKey(&key, NULL); + if (ret == 0) + ret = wc_RsaPublicKeyDecode(result->value, &idx, &key, + result->valueSz); + if (ret == 0) { + ret = wc_RsaFlattenPublicKey(&key, exponent, &exponentSz, + component, &modulusSz); + } + if (ret == 0) { + if (componentIndex == 0U) { + *componentSz = modulusSz; + } + else if (*componentSz >= exponentSz) { + XMEMCPY(component, exponent, exponentSz); + *componentSz = exponentSz; + } + else { + ret = BUFFER_E; + } + } + wc_FreeRsaKey(&key); + ForceZero(exponent, sizeof(exponent)); + return ret; + #else + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); + #endif + } + + if ((result->cipherType == (word32)kSSS_CipherType_EC_NIST_P) || + (result->cipherType == (word32)kSSS_CipherType_EC_NIST_K) || + (result->cipherType == (word32)kSSS_CipherType_EC_BRAINPOOL)) { + #ifdef HAVE_ECC + int ret; + ecc_key key; + word32 idx = 0; + + ret = wc_ecc_init(&key); + if (ret == 0) + ret = wc_EccPublicKeyDecode(result->value, &idx, &key, + result->valueSz); + if (ret == 0) + ret = wc_ecc_export_x963(&key, component, componentSz); + wc_ecc_free(&key); + return ret; + #else + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); + #endif + } + + if ((result->cipherType == + (word32)kSSS_CipherType_EC_MONTGOMERY) || + (result->cipherType == + (word32)kSSS_CipherType_EC_TWISTED_ED)) { + word32 i; + word32 rawSz = 32U; + + /* Plug & Trust prepends SubjectPublicKeyInfo DER and reverses these + * little-endian applet values after attestation. The signature is + * over the original applet bytes, so strip DER and undo that reverse. */ + if ((result->cipherType == + (word32)kSSS_CipherType_EC_MONTGOMERY) && + (result->valueSz > 56U)) { + rawSz = 56U; /* X448 */ + } + if ((result->valueSz < rawSz) || (*componentSz < rawSz)) { + return BUFFER_E; + } + for (i = 0U; i < rawSz; i++) { + component[i] = result->value[result->valueSz - 1U - i]; + } + *componentSz = rawSz; + return 0; + } + + if (*componentSz < result->valueSz) { + return BUFFER_E; + } + XMEMCPY(component, result->value, result->valueSz); + *componentSz = result->valueSz; + return 0; +} + +static int se050_attest_append(byte* out, word32 outSz, word32* offset, + const byte* in, word32 inSz) +{ + if ((out == NULL) || (offset == NULL) || + ((in == NULL) && (inSz != 0U)) || (*offset > outSz) || + (inSz > (outSz - *offset))) { + return BUFFER_E; + } + if (inSz != 0U) { + XMEMCPY(out + *offset, in, inSz); + *offset += inSz; + } + return 0; +} + +#if SSS_HAVE_SE05X_VER_GTE_07_02 +static int se050_attest_append_tlv(byte* out, word32 outSz, word32* offset, + byte tag, const byte* value, word32 valueSz) +{ + byte header[SE050_TLV_OVERHEAD]; + int ret; + + if (valueSz > 0xFFFFU) { + return BAD_LENGTH_E; + } + header[0] = tag; + header[1] = 0x82; + header[2] = (byte)(valueSz >> 8); + header[3] = (byte)valueSz; + ret = se050_attest_append(out, outSz, offset, header, sizeof(header)); + if (ret == 0) + ret = se050_attest_append(out, outSz, offset, value, valueSz); + return ret; +} +#endif + +static int se050_build_attestation_data(const wc_se050_attst_result* result, + word32 componentIndex, byte* component, word32 componentSz, + byte* signedData, word32 signedDataSz, word32* used) +{ +#ifdef NO_HASH_WRAPPER + (void)result; + (void)componentIndex; + (void)component; + (void)componentSz; + (void)signedData; + (void)signedDataSz; + (void)used; + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); +#else + int ret = 0; + word32 offset = 0; + const sss_se05x_attst_comp_data_t* data; + + if ((result == NULL) || (signedData == NULL) || (used == NULL) || + (componentIndex >= result->raw.valid_number)) { + return BAD_FUNC_ARG; + } + data = &result->raw.data[componentIndex]; + if ((data->attributeLen > sizeof(data->attribute)) || + (data->chipIdLen > sizeof(data->chipId)) || + (data->timeStampLen > sizeof(data->timeStamp)) || + (data->signatureLen > sizeof(data->signature))) { + return BAD_LENGTH_E; + } +#if SSS_HAVE_SE05X_VER_GTE_07_02 + if ((data->cmdLen > sizeof(data->cmd)) || + (data->objSizeLen > sizeof(data->objSize))) { + return BAD_LENGTH_E; + } +#else + if (data->outrandomLen > sizeof(data->outrandom)) { + return BAD_LENGTH_E; + } +#endif + +#if SSS_HAVE_SE05X_VER_GTE_07_02 + { + int digestSz; + byte commandDigest[WC_MAX_DIGEST_SIZE]; + + digestSz = wc_HashGetDigestSize(result->hashAlgo); + if ((digestSz <= 0) || (data->cmdLen > UINT32_MAX)) { + return BAD_FUNC_ARG; + } + ret = wc_Hash(result->hashAlgo, data->cmd, (word32)data->cmdLen, + commandDigest, (word32)digestSz); + if (ret == 0) + ret = se050_attest_append(signedData, signedDataSz, &offset, + commandDigest, (word32)digestSz); + if ((ret == 0) && (componentSz != 0U)) + ret = se050_attest_append_tlv(signedData, signedDataSz, &offset, + (byte)kSE05x_TAG_1, component, componentSz); + if (ret == 0) + ret = se050_attest_append_tlv(signedData, signedDataSz, &offset, + (byte)kSE05x_TAG_2, data->chipId, (word32)data->chipIdLen); + if (ret == 0) + ret = se050_attest_append_tlv(signedData, signedDataSz, &offset, + (byte)kSE05x_TAG_3, data->attribute, + (word32)data->attributeLen); + if (ret == 0) + ret = se050_attest_append_tlv(signedData, signedDataSz, &offset, + (byte)kSE05x_TAG_4, data->objSize, + (word32)data->objSizeLen); + if (ret == 0) + ret = se050_attest_append_tlv(signedData, signedDataSz, &offset, + (byte)kSE05x_TAG_TIMESTAMP, data->timeStamp.ts, + (word32)data->timeStampLen); + ForceZero(commandDigest, sizeof(commandDigest)); + } +#else + ret = se050_attest_append(signedData, signedDataSz, &offset, component, + componentSz); + if (ret == 0) + ret = se050_attest_append(signedData, signedDataSz, &offset, + data->attribute, (word32)data->attributeLen); + if (ret == 0) + ret = se050_attest_append(signedData, signedDataSz, &offset, + data->timeStamp.ts, (word32)data->timeStampLen); + if (ret == 0) + ret = se050_attest_append(signedData, signedDataSz, &offset, + data->outrandom, (word32)data->outrandomLen); + if (ret == 0) + ret = se050_attest_append(signedData, signedDataSz, &offset, + data->chipId, (word32)data->chipIdLen); +#endif + + if (ret == 0) + *used = offset; + return ret; +#endif +} + +static int se050_verify_attestation_freshness( + const sss_se05x_attst_comp_data_t* data, const byte* expectedRandom, + word32 expectedRandomSz) +{ + if ((data == NULL) || (expectedRandom == NULL) || + (expectedRandomSz != SE050_ATTEST_RANDOM_SIZE)) { + return BAD_FUNC_ARG; + } + +#if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && \ + SSS_HAVE_SE05X_VER_GTE_07_02 + { + word32 offset = 7U; + word32 commandDataSz; + int found = 0; + + if ((data->cmdLen < offset) || (data->cmdLen > sizeof(data->cmd)) || + (data->cmd[0] != (byte)kSE05x_CLA) || + (data->cmd[1] != + (byte)kSE05x_INS_READ_With_Attestation) || + (data->cmd[4] != 0U)) { + return BAD_LENGTH_E; + } + commandDataSz = ((word32)data->cmd[5] << 8) | data->cmd[6]; + if (commandDataSz != ((word32)data->cmdLen - offset)) { + return BAD_LENGTH_E; + } + + while (offset < data->cmdLen) { + byte tag; + byte lengthByte; + word32 valueSz; + + tag = data->cmd[offset++]; + if (offset >= data->cmdLen) { + return BAD_LENGTH_E; + } + lengthByte = data->cmd[offset++]; + if (lengthByte <= 0x7FU) { + valueSz = lengthByte; + } + else if (lengthByte == 0x81U) { + if (offset >= data->cmdLen) { + return BAD_LENGTH_E; + } + valueSz = data->cmd[offset++]; + } + else if (lengthByte == 0x82U) { + if (((word32)data->cmdLen - offset) < 2U) { + return BAD_LENGTH_E; + } + valueSz = ((word32)data->cmd[offset] << 8) | + data->cmd[offset + 1U]; + offset += 2U; + } + else { + return BAD_LENGTH_E; + } + if (valueSz > ((word32)data->cmdLen - offset)) { + return BAD_LENGTH_E; + } + if (tag == (byte)kSE05x_TAG_7) { + if (found || (valueSz != expectedRandomSz)) { + return BAD_LENGTH_E; + } + if (ConstantCompare(data->cmd + offset, expectedRandom, + expectedRandomSz) != 0) { + return WC_NO_ERR_TRACE(SIG_VERIFY_E); + } + found = 1; + } + offset += valueSz; + } + return found ? 0 : BAD_LENGTH_E; } - if (status == kStatus_SSS_Success) { - status = sss_key_store_set_key(&host_keystore, &newObj, object, - objectSz, (objectSz * 8), NULL, 0); +#else + if (data->outrandomLen != expectedRandomSz) { + return BAD_LENGTH_E; } - wolfSSL_CryptHwMutexUnLock(); - - if (status != kStatus_SSS_Success) { - ret = WC_HW_E; + if (ConstantCompare(data->outrandom, expectedRandom, + expectedRandomSz) != 0) { + return WC_NO_ERR_TRACE(SIG_VERIFY_E); } - - return ret; + return 0; +#endif } +#endif /* !NO_HASH_WRAPPER && !NO_SIG_WRAPPER && !NO_ASN */ -/** - * Get binary object from SE050 from specified key ID. - * - * keyId SE050 key ID to get binary object from - * out output buffer to place binary object - * outSz size of output buffer on input, size of written object on output - * - * Returns 0 on success, LENGTH_ONLY_E if out is NULL with outSz set to - * required buffer size, and other negative on error. - */ -int wc_se050_get_binary_object(word32 keyId, byte* out, word32* outSz) +int wc_se050_verify_attestation(const wc_se050_attst_result* result, + const byte* attestPubDer, word32 attestPubDerSz, + const byte* expectedRandom, word32 expectedRandomSz, int* res) { - int ret = 0; - sss_object_t object; - sss_key_store_t host_keystore; - sss_status_t status = kStatus_SSS_Success; - size_t outBitSz = 0; - - /* If out is NULL, outSz set to required size and LENGTH_ONLY_E returned */ - if (outSz == NULL) { +#if defined(NO_HASH_WRAPPER) || defined(NO_SIG_WRAPPER) || defined(NO_ASN) + (void)result; + (void)attestPubDer; + (void)attestPubDerSz; + (void)expectedRandom; + (void)expectedRandomSz; + if (res != NULL) + *res = 0; + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); +#else + int ret = 0; + int keyDecoded = 0; + int sigType = WC_SIGNATURE_TYPE_NONE; + word32 i; + word32 idx; + word32 componentSz; + word32 signedDataSz; + word32 signedDataUsed; + byte* component = NULL; + byte* signedData = NULL; +#if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY) + int eccInit = 0; + ecc_key eccKey; +#endif +#ifndef NO_RSA + int rsaInit = 0; + RsaKey rsaKey; +#endif + void* verifyKey = NULL; + word32 verifyKeySz = 0; + + if ((result == NULL) || (attestPubDer == NULL) || + (attestPubDerSz == 0U) || (expectedRandom == NULL) || + (expectedRandomSz != SE050_ATTEST_RANDOM_SIZE) || + (res == NULL) || + (result->raw.valid_number == 0U) || + (result->raw.valid_number > SE05X_MAX_ATTST_DATA)) { return BAD_FUNC_ARG; } - - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + *res = 0; + if (ConstantCompare(result->freshness, expectedRandom, + expectedRandomSz) != 0) { + return 0; } - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); - if (status == kStatus_SSS_Success) { - status = sss_key_object_init(&object, &host_keystore); +#if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY) + XMEMSET(&eccKey, 0, sizeof(eccKey)); +#endif +#ifndef NO_RSA + XMEMSET(&rsaKey, 0, sizeof(rsaKey)); +#endif +#if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY) + ret = wc_ecc_init(&eccKey); + if (ret == 0) { + eccInit = 1; + idx = 0; + ret = wc_EccPublicKeyDecode(attestPubDer, &idx, &eccKey, + attestPubDerSz); + if (ret == 0) { + keyDecoded = 1; + sigType = WC_SIGNATURE_TYPE_ECC; + verifyKey = &eccKey; + verifyKeySz = sizeof(eccKey); + } } - if (status == kStatus_SSS_Success) { - ret = se050_get_object_size(&host_keystore, keyId); - if (ret < 0) { - status = kStatus_SSS_Fail; +#endif +#ifndef NO_RSA + if (!keyDecoded) { + ret = wc_InitRsaKey(&rsaKey, NULL); + if (ret == 0) { + rsaInit = 1; + idx = 0; + ret = wc_RsaPublicKeyDecode(attestPubDer, &idx, &rsaKey, + attestPubDerSz); + if (ret == 0) { + keyDecoded = 1; + sigType = WC_SIGNATURE_TYPE_RSA_W_ENC; + verifyKey = &rsaKey; + verifyKeySz = sizeof(rsaKey); + } } - else { - if (out == NULL) { - *outSz = ret; - wolfSSL_CryptHwMutexUnLock(); - return WC_NO_ERR_TRACE(LENGTH_ONLY_E); + } +#endif + if (!keyDecoded) { + goto cleanup; + } + + component = (byte*)XMALLOC(WC_SE050_ATTEST_VALUE_MAX, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + signedDataSz = WC_SE050_ATTEST_VALUE_MAX + MAX_POLICY_BUFFER_SIZE + + WC_MAX_DIGEST_SIZE + 128U; + signedData = (byte*)XMALLOC(signedDataSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if ((component == NULL) || (signedData == NULL)) { + ret = MEMORY_E; + goto cleanup; + } + + ret = 0; + for (i = 0; (i < result->raw.valid_number) && (ret == 0); i++) { + ret = se050_verify_attestation_freshness(&result->raw.data[i], + expectedRandom, expectedRandomSz); + if (ret == WC_NO_ERR_TRACE(SIG_VERIFY_E)) { + ret = 0; + goto cleanup; + } + componentSz = WC_SE050_ATTEST_VALUE_MAX; + if (ret == 0) { + ret = se050_attested_component(result, i, component, + &componentSz); + } + if (ret == 0) { + ret = se050_build_attestation_data(result, i, component, + componentSz, signedData, signedDataSz, &signedDataUsed); + } + if (ret == 0) { + if ((result->raw.data[i].signatureLen == 0U) || + (result->raw.data[i].signatureLen > + sizeof(result->raw.data[i].signature))) { + ret = BAD_LENGTH_E; + break; } - if ((word32)ret > *outSz) { - WOLFSSL_MSG("Output buffer not large enough for object"); - wolfSSL_CryptHwMutexUnLock(); - return BAD_LENGTH_E; + ret = wc_SignatureVerify(result->hashAlgo, + (enum wc_SignatureType)sigType, signedData, signedDataUsed, + result->raw.data[i].signature, + (word32)result->raw.data[i].signatureLen, verifyKey, + verifyKeySz); + if (ret == WC_NO_ERR_TRACE(SIG_VERIFY_E)) { + ret = 0; + goto cleanup; } - ret = 0; } } - if (status == kStatus_SSS_Success) { - status = sss_key_object_get_handle(&object, keyId); + if (ret == 0) + *res = 1; + +cleanup: + if (component != NULL) { + ForceZero(component, WC_SE050_ATTEST_VALUE_MAX); + XFREE(component, NULL, DYNAMIC_TYPE_TMP_BUFFER); } - if (status == kStatus_SSS_Success) { - outBitSz = (*outSz) * 8; - status = sss_key_store_get_key(&host_keystore, &object, out, - (size_t*)outSz, &outBitSz); + if (signedData != NULL) { + ForceZero(signedData, signedDataSz); + XFREE(signedData, NULL, DYNAMIC_TYPE_TMP_BUFFER); } - wolfSSL_CryptHwMutexUnLock(); +#if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY) + if (eccInit) + wc_ecc_free(&eccKey); +#endif +#ifndef NO_RSA + if (rsaInit) + wc_FreeRsaKey(&rsaKey); +#endif + return ret; +#endif +} - if (status != kStatus_SSS_Success) { - ret = WC_HW_E; +int wc_se050_validate_provisioned_key(word32 keyId, word32 attestKeyId, + const byte* expectedPubDer, word32 expectedPubDerSz, + const byte* attestPubDer, word32 attestPubDerSz, const byte* random, + word32 randomSz, int* res) +{ + int ret; + int verified = 0; + wc_se050_attst_result result; + + if ((expectedPubDer == NULL) || (expectedPubDerSz == 0U) || + (attestPubDer == NULL) || (attestPubDerSz == 0U) || + (random == NULL) || (randomSz != SE050_ATTEST_RANDOM_SIZE) || + (res == NULL)) { + return BAD_FUNC_ARG; } + *res = 0; + ret = wc_se050_attest_object(keyId, attestKeyId, WC_HASH_TYPE_SHA256, + random, randomSz, &result); + if (ret == 0) + ret = wc_se050_verify_attestation(&result, attestPubDer, + attestPubDerSz, random, randomSz, &verified); + if ((ret == 0) && verified && (result.valueSz == expectedPubDerSz) && + (XMEMCMP(result.value, expectedPubDer, expectedPubDerSz) == 0)) { + *res = 1; + } + ForceZero(&result, sizeof(result)); return ret; } +#endif /* !WOLFSSL_SE050_NO_ATTEST */ + #if !defined(NO_RSA) && !defined(WOLFSSL_SE050_NO_RSA) /** @@ -924,8 +2520,84 @@ int se050_rsa_create_key(struct RsaKey* key, int size, long e) return ret; } +static int se050_rsa_generate_key(word32 keyId, int size, long e, + const sss_policy_t* policy) +{ + sss_status_t status = kStatus_SSS_Success; + sss_object_t keyPair; + sss_key_store_t host_keystore; + int keyObjectInit = 0; + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + if ((keyId >= SE050_KEYID_START) || (size <= 0) || + ((size & 7) != 0) || (e != 65537)) { + return BAD_FUNC_ARG; + } + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = se050_require_new_object(keyId); + if (status == kStatus_SSS_Success) { + status = sss_key_store_context_init(&host_keystore, + cfg_se050_i2c_pi); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, + SE050_KEYSTOREID_RSA); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&keyPair, &host_keystore); + if (status == kStatus_SSS_Success) { + keyObjectInit = 1; + } + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_allocate_handle(&keyPair, keyId, + kSSS_KeyPart_Pair, kSSS_CipherType_RSA, (size / 8), + kKeyObject_Mode_Persistent); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_generate_key(&host_keystore, &keyPair, + size, (void*)policy); + } + + if (keyObjectInit) { + sss_key_object_free(&keyPair); + } + wolfSSL_CryptHwMutexUnLock(); + return (status == kStatus_SSS_Success) ? 0 : WC_HW_E; +} + +int wc_se050_rsa_generate_key_policy(word32 keyId, int size, long e, + const sss_policy_t* policy) +{ + return se050_rsa_generate_key(keyId, size, e, policy); +} + +int wc_se050_rsa_generate_key_ex(word32 keyId, int size, long e, + word32 policyFlags, word32 authObjId) +{ + const sss_policy_t* policy = NULL; + se050_policy_set policySet; + int ret; + + ret = se050_build_policy_set(SE050_POLICY_OBJECT_ASYM, policyFlags, + authObjId, &policySet); + if (ret != 0) { + return ret; + } + if (policyFlags != 0U) { + policy = &policySet.policy; + } + return se050_rsa_generate_key(keyId, size, e, policy); +} + static int se050_rsa_insert_key(word32 keyId, const byte* rsaDer, - word32 rsaDerSize, int keyType) + word32 rsaDerSize, int keyType, const sss_policy_t* policy, + int requireNew) { int ret = 0; int keySize; @@ -936,6 +2608,11 @@ static int se050_rsa_insert_key(word32 keyId, const byte* rsaDer, struct RsaKey key; sss_key_part_t keyPart = kSSS_KeyPart_Pair; + if ((cfg_se050_i2c_pi == NULL) || (rsaDer == NULL) || + (rsaDerSize == 0U)) { + return BAD_FUNC_ARG; + } + if (wolfSSL_CryptHwMutexLock() != 0) { return BAD_MUTEX_E; } @@ -946,6 +2623,10 @@ static int se050_rsa_insert_key(word32 keyId, const byte* rsaDer, return BAD_FUNC_ARG; } + if (requireNew) { + status = se050_require_new_object(keyId); + } + ret = wc_InitRsaKey(&key, NULL); if (ret != 0) { status = kStatus_SSS_Fail; @@ -987,7 +2668,7 @@ static int se050_rsa_insert_key(word32 keyId, const byte* rsaDer, } if (status == kStatus_SSS_Success) { status = sss_key_store_set_key(&host_keystore, &newKey, rsaDer, - rsaDerSize, (keySize * 8), NULL, 0); + rsaDerSize, (keySize * 8), (void*)policy, 0); } wolfSSL_CryptHwMutexUnLock(); @@ -1013,7 +2694,8 @@ static int se050_rsa_insert_key(word32 keyId, const byte* rsaDer, int wc_se050_rsa_insert_private_key(word32 keyId, const byte* rsaDer, word32 rsaDerSize) { - return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PRIVATE); + return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PRIVATE, + NULL, 0); } /** @@ -1028,7 +2710,55 @@ int wc_se050_rsa_insert_private_key(word32 keyId, const byte* rsaDer, int wc_se050_rsa_insert_public_key(word32 keyId, const byte* rsaDer, word32 rsaDerSize) { - return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PUBLIC); + return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PUBLIC, + NULL, 0); +} + +int wc_se050_rsa_insert_private_key_policy(word32 keyId, const byte* rsaDer, + word32 rsaDerSize, const sss_policy_t* policy) +{ + return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PRIVATE, + policy, 0); +} + +int wc_se050_rsa_insert_public_key_policy(word32 keyId, const byte* rsaDer, + word32 rsaDerSize, const sss_policy_t* policy) +{ + return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PUBLIC, + policy, 0); +} + +static int se050_rsa_insert_key_ex(word32 keyId, const byte* rsaDer, + word32 rsaDerSize, int keyType, word32 policyFlags, word32 authObjId) +{ + int ret; + const sss_policy_t* policy = NULL; + se050_policy_set policySet; + + ret = se050_build_policy_set(SE050_POLICY_OBJECT_ASYM, policyFlags, + authObjId, &policySet); + if (ret != 0) { + return ret; + } + if (policyFlags != 0U) { + policy = &policySet.policy; + } + return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, keyType, policy, + 1); +} + +int wc_se050_rsa_insert_private_key_ex(word32 keyId, const byte* rsaDer, + word32 rsaDerSize, word32 policyFlags, word32 authObjId) +{ + return se050_rsa_insert_key_ex(keyId, rsaDer, rsaDerSize, RSA_PRIVATE, + policyFlags, authObjId); +} + +int wc_se050_rsa_insert_public_key_ex(word32 keyId, const byte* rsaDer, + word32 rsaDerSize, word32 policyFlags, word32 authObjId) +{ + return se050_rsa_insert_key_ex(keyId, rsaDer, rsaDerSize, RSA_PUBLIC, + policyFlags, authObjId); } /** @@ -2048,6 +3778,87 @@ static int se050_map_curve(int curve_id, int keySize, return ret; } +static int se050_ecc_generate_key(word32 keyId, int keySize, int curveId, + const sss_policy_t* policy) +{ + sss_status_t status = kStatus_SSS_Success; + sss_object_t keyPair; + sss_key_store_t host_keystore; + sss_cipher_type_t curveType; + int keyObjectInit = 0; + int keySizeBits; + int ret; + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + if ((keyId >= SE050_KEYID_START) || (keySize <= 0)) { + return BAD_FUNC_ARG; + } + ret = se050_map_curve(curveId, keySize, &keySizeBits, &curveType); + if (ret != 0) { + return ret; + } + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = se050_require_new_object(keyId); + if (status == kStatus_SSS_Success) { + status = sss_key_store_context_init(&host_keystore, + cfg_se050_i2c_pi); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, + SE050_KEYSTOREID_ECC); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&keyPair, &host_keystore); + if (status == kStatus_SSS_Success) { + keyObjectInit = 1; + } + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_allocate_handle(&keyPair, keyId, + kSSS_KeyPart_Pair, curveType, keySize, + kKeyObject_Mode_Persistent); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_generate_key(&host_keystore, &keyPair, + keySizeBits, (void*)policy); + } + + if (keyObjectInit) { + sss_key_object_free(&keyPair); + } + wolfSSL_CryptHwMutexUnLock(); + return (status == kStatus_SSS_Success) ? 0 : WC_HW_E; +} + +int wc_se050_ecc_generate_key_policy(word32 keyId, int keySize, int curveId, + const sss_policy_t* policy) +{ + return se050_ecc_generate_key(keyId, keySize, curveId, policy); +} + +int wc_se050_ecc_generate_key_ex(word32 keyId, int keySize, int curveId, + word32 policyFlags, word32 authObjId) +{ + const sss_policy_t* policy = NULL; + se050_policy_set policySet; + int ret; + + ret = se050_build_policy_set(SE050_POLICY_OBJECT_ASYM, policyFlags, + authObjId, &policySet); + if (ret != 0) { + return ret; + } + if (policyFlags != 0U) { + policy = &policySet.policy; + } + return se050_ecc_generate_key(keyId, keySize, curveId, policy); +} + static sss_algorithm_t se050_map_hash_alg(int hashLen) { sss_algorithm_t algorithm = kAlgorithm_None; @@ -2067,7 +3878,8 @@ static sss_algorithm_t se050_map_hash_alg(int hashLen) } static int se050_ecc_insert_key(word32 keyId, const byte* eccDer, - word32 eccDerSize, int keyType) + word32 eccDerSize, int keyType, const sss_policy_t* policy, + int requireNew) { int ret = 0; struct ecc_key key; @@ -2080,6 +3892,11 @@ static int se050_ecc_insert_key(word32 keyId, const byte* eccDer, sss_cipher_type_t curveType = kSSS_CipherType_NONE; sss_key_part_t keyPart = kSSS_KeyPart_Pair; + if ((cfg_se050_i2c_pi == NULL) || (eccDer == NULL) || + (eccDerSize == 0U)) { + return BAD_FUNC_ARG; + } + if (wolfSSL_CryptHwMutexLock() != 0) { return BAD_MUTEX_E; } @@ -2090,6 +3907,10 @@ static int se050_ecc_insert_key(word32 keyId, const byte* eccDer, return BAD_FUNC_ARG; } + if (requireNew) { + status = se050_require_new_object(keyId); + } + ret = wc_ecc_init(&key); if (ret != 0) { status = kStatus_SSS_Fail; @@ -2131,8 +3952,7 @@ static int se050_ecc_insert_key(word32 keyId, const byte* eccDer, } if (status == kStatus_SSS_Success) { status = sss_key_store_set_key(&host_keystore, &newKey, eccDer, - eccDerSize, keySizeBits, - NULL, 0); + eccDerSize, keySizeBits, (void*)policy, 0); } wolfSSL_CryptHwMutexUnLock(); @@ -2157,7 +3977,8 @@ static int se050_ecc_insert_key(word32 keyId, const byte* eccDer, int wc_se050_ecc_insert_public_key(word32 keyId, const byte* eccDer, word32 eccDerSize) { - return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PUBLICKEY); + return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PUBLICKEY, + NULL, 0); } /** @@ -2172,7 +3993,55 @@ int wc_se050_ecc_insert_public_key(word32 keyId, const byte* eccDer, int wc_se050_ecc_insert_private_key(word32 keyId, const byte* eccDer, word32 eccDerSize) { - return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PRIVATEKEY); + return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PRIVATEKEY, + NULL, 0); +} + +int wc_se050_ecc_insert_public_key_policy(word32 keyId, const byte* eccDer, + word32 eccDerSize, const sss_policy_t* policy) +{ + return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PUBLICKEY, + policy, 0); +} + +int wc_se050_ecc_insert_private_key_policy(word32 keyId, const byte* eccDer, + word32 eccDerSize, const sss_policy_t* policy) +{ + return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PRIVATEKEY, + policy, 0); +} + +static int se050_ecc_insert_key_ex(word32 keyId, const byte* eccDer, + word32 eccDerSize, int keyType, word32 policyFlags, word32 authObjId) +{ + int ret; + const sss_policy_t* policy = NULL; + se050_policy_set policySet; + + ret = se050_build_policy_set(SE050_POLICY_OBJECT_ASYM, policyFlags, + authObjId, &policySet); + if (ret != 0) { + return ret; + } + if (policyFlags != 0U) { + policy = &policySet.policy; + } + return se050_ecc_insert_key(keyId, eccDer, eccDerSize, keyType, policy, + 1); +} + +int wc_se050_ecc_insert_public_key_ex(word32 keyId, const byte* eccDer, + word32 eccDerSize, word32 policyFlags, word32 authObjId) +{ + return se050_ecc_insert_key_ex(keyId, eccDer, eccDerSize, ECC_PUBLICKEY, + policyFlags, authObjId); +} + +int wc_se050_ecc_insert_private_key_ex(word32 keyId, const byte* eccDer, + word32 eccDerSize, word32 policyFlags, word32 authObjId) +{ + return se050_ecc_insert_key_ex(keyId, eccDer, eccDerSize, ECC_PRIVATEKEY, + policyFlags, authObjId); } int se050_ecc_sign_hash_ex(const byte* in, word32 inLen, MATH_INT_T* r, MATH_INT_T* s, diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 2e39651674..a3d9e2dd43 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -5627,19 +5627,12 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) #include int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz){ - int ret = 0; - (void)os; if (output == NULL) { return BUFFER_E; } - ret = wolfSSL_CryptHwMutexLock(); - if (ret == 0) { - ret = se050_get_random_number(sz, output); - wolfSSL_CryptHwMutexUnLock(); - } - return ret; + return se050_get_random_number(sz, output); } #elif defined(WOLFSSL_NXP_RNG_1) diff --git a/wolfcrypt/src/wc_port.c b/wolfcrypt/src/wc_port.c index 4d60dcaa81..37db77c016 100644 --- a/wolfcrypt/src/wc_port.c +++ b/wolfcrypt/src/wc_port.c @@ -646,10 +646,15 @@ int wolfCrypt_Init(void) #endif #if defined(WOLFSSL_SE050) && defined(WOLFSSL_SE050_INIT) - ret = wc_se050_init(NULL); - if (ret != 0) { - WOLFSSL_MSG("SE050 init failed"); - WOLFCRYPT_INIT_RAISE_BAD_STATE(); + /* An application may need runtime SCP03 keys to open the SE05x + * before calling wolfCrypt_Init(). Keep that configured session + * instead of trying to replace it with the compiled-in defaults. */ + if (wc_se050_get_session() == NULL) { + ret = wc_se050_init(NULL); + if (ret != 0) { + WOLFSSL_MSG("SE050 init failed"); + WOLFCRYPT_INIT_RAISE_BAD_STATE(); + } } #endif @@ -878,6 +883,16 @@ int wolfCrypt_Cleanup(void) ret = ret2; } #endif + #if defined(WOLFSSL_SE050) && defined(WOLFSSL_SE050_INIT) + if (wc_se050_get_session() != NULL) { + int ret2 = wc_se050_close(); + + /* A session installed with wc_se050_set_config() is owned by the + * application and wc_se050_close() deliberately rejects it. */ + if ((ret == 0) && (ret2 != WC_NO_ERR_TRACE(BAD_STATE_E))) + ret = ret2; + } + #endif #if defined(WOLFSSL_TROPIC01) Tropic01_Deinit(); #endif diff --git a/wolfssl/wolfcrypt/port/nxp/se050_port.h b/wolfssl/wolfcrypt/port/nxp/se050_port.h index ab57a2b3cb..33bc6b96e6 100644 --- a/wolfssl/wolfcrypt/port/nxp/se050_port.h +++ b/wolfssl/wolfcrypt/port/nxp/se050_port.h @@ -26,6 +26,11 @@ #include #include /* for MATH_INT_T */ +#if defined(WOLFSSL_SE050_SCP03_ROTATE) && \ + (defined(NO_AES) || !defined(WOLFSSL_AES_DIRECT)) + #error WOLFSSL_SE050_SCP03_ROTATE requires AES direct support +#endif + #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wundef" @@ -98,6 +103,54 @@ enum SE050KeyType { SE050_CURVE25519_KEY }; +/* SE05x secure object permissions. Attaching any policy makes the applet + * default-deny all permissions that are not explicitly granted. A value of + * zero preserves the applet default policy by attaching no policy. */ +#define WC_SE050_POLICY_ALLOW_DELETE 0x00000001U +#define WC_SE050_POLICY_ALLOW_WRITE 0x00000002U +#define WC_SE050_POLICY_ALLOW_READ 0x00000004U +#define WC_SE050_POLICY_ALLOW_SIGN 0x00000008U +#define WC_SE050_POLICY_ALLOW_VERIFY 0x00000010U +#define WC_SE050_POLICY_ALLOW_ENCRYPT 0x00000020U +#define WC_SE050_POLICY_ALLOW_DECRYPT 0x00000040U +#define WC_SE050_POLICY_ALLOW_KA 0x00000080U +#define WC_SE050_POLICY_ALLOW_KD 0x00000100U +#define WC_SE050_POLICY_ALLOW_GEN 0x00000200U +#define WC_SE050_POLICY_ALLOW_IMPORT_EXPORT 0x00000400U +#define WC_SE050_POLICY_ALLOW_ATTEST 0x00000800U +#define WC_SE050_POLICY_REQUIRE_SM 0x00001000U + +/* Platform SCP03 uses one 128-bit ENC, MAC and data-encryption key. */ +typedef struct wc_se050_scp03_keys { + byte enc[16]; + byte mac[16]; + byte dek[16]; +} wc_se050_scp03_keys; + +#ifndef WC_SE050_ATTEST_VALUE_MAX +#define WC_SE050_ATTEST_VALUE_MAX 1024U +#endif + +#ifndef WOLFSSL_SE050_NO_ATTEST +/* Result of an attested object read. The public object value is returned in + * the same DER form as sss_key_store_get_key(). cipherType, objectType and + * curveId describe the object and are used by the host verifier to recover + * the exact applet response value. */ +typedef struct wc_se050_attst_result { + byte value[WC_SE050_ATTEST_VALUE_MAX]; + word32 valueSz; + byte freshness[16]; + byte origin; + word32 authObjId; + word32 policyFlags; + word32 cipherType; + word32 objectType; + word32 curveId; + enum wc_HashType hashAlgo; + sss_se05x_attst_data_t raw; +} wc_se050_attst_result; +#endif + #ifdef WOLFSSL_SE050_HASH typedef struct { @@ -111,8 +164,56 @@ typedef struct { /* Public Functions */ WOLFSSL_API int wc_se050_set_config(sss_session_t *pSession, sss_key_store_t *pHostKeyStore, sss_key_store_t *pKeyStore); +/** Return the configured SSS session and keystores. Output pointers may be + * NULL. Direct middleware use must be bracketed by wc_se050_lock/unlock. */ +WOLFSSL_API int wc_se050_get_config(sss_session_t **pSession, + sss_key_store_t **pHostKeyStore, sss_key_store_t **pKeyStore); +/** Return the SSS session currently used by the wolfCrypt SE05x port. */ +WOLFSSL_API sss_session_t* wc_se050_get_session(void); +/** Return the low-level SE05x session, or NULL when none is configured. */ +WOLFSSL_API pSe05xSession_t wc_se050_get_se05x_session(void); +/** Acquire/release the shared wolfCrypt hardware transport lock. */ +WOLFSSL_API int wc_se050_lock(void); +WOLFSSL_API void wc_se050_unlock(void); #ifdef WOLFSSL_SE050_INIT WOLFSSL_API int wc_se050_init(const char* portName); +/** Close a session opened by wc_se050_init() or wc_se050_init_ex(). */ +WOLFSSL_API int wc_se050_close(void); +#if defined(SSS_HAVE_HOSTCRYPTO_ANY) && SSS_HAVE_HOSTCRYPTO_ANY && \ + defined(SSS_HAVE_SCP_SCP03_SSS) && SSS_HAVE_SCP_SCP03_SSS && \ + defined(SSS_HAVE_SE05X_AUTH_PLATFSCP03) && \ + SSS_HAVE_SE05X_AUTH_PLATFSCP03 +/** Open Platform SCP03 using caller-supplied 128-bit ENC/MAC/DEK keys. */ +WOLFSSL_API int wc_se050_init_ex(const char* portName, + const wc_se050_scp03_keys* keys); +#endif +#endif + +#ifdef HAVE_HKDF +/** Deterministically derive the Platform SCP03 ENC, MAC and DEK keys from a + * seed. No SE05x session is required, so this can be called before + * wolfCrypt_Init() to recover keys after a power cycle. */ +WOLFSSL_API int wc_se050_scp03_derive_keys_seed(const byte* seed, + word32 seedSz, wc_se050_scp03_keys* derivedOut); +#endif + +#if defined(WOLFSSL_SE050_SCP03_ROTATE) && \ + defined(WOLFSSL_SE050_INIT) && \ + defined(SSS_HAVE_HOSTCRYPTO_ANY) && SSS_HAVE_HOSTCRYPTO_ANY && \ + defined(SSS_HAVE_SCP_SCP03_SSS) && SSS_HAVE_SCP_SCP03_SSS && \ + defined(SSS_HAVE_SE05X_AUTH_PLATFSCP03) && \ + SSS_HAVE_SE05X_AUTH_PLATFSCP03 +/** Destructively replace the Platform SCP03 key set using secured PUT KEY. + * The port temporarily authenticates to the Security Domain, then returns + * with a fresh IoT applet session authenticated by newKeys. */ +WOLFSSL_API int wc_se050_scp03_rotate_keys( + const wc_se050_scp03_keys* newKeys, byte keyVersion); +#ifdef HAVE_HKDF +/** Derive three keys with the documented HKDF-SHA256 construction and rotate. + * Persist the seed before calling. derivedOut may be NULL. */ +WOLFSSL_API int wc_se050_scp03_rotate_keys_seed(const byte* seed, + word32 seedSz, byte keyVersion, wc_se050_scp03_keys* derivedOut); +#endif #endif WOLFSSL_API int wc_se050_erase_object(word32 keyId); @@ -120,16 +221,85 @@ WOLFSSL_API int wc_se050_ecc_insert_public_key(word32 keyId, const byte* eccDer, word32 eccDerSize); WOLFSSL_API int wc_se050_ecc_insert_private_key(word32 keyId, const byte* eccDer, word32 eccDerSize); +/** Insert an ECC public/private key with a flag-based immutable policy. */ +WOLFSSL_API int wc_se050_ecc_insert_public_key_ex(word32 keyId, + const byte* eccDer, word32 eccDerSize, word32 policyFlags, + word32 authObjId); +WOLFSSL_API int wc_se050_ecc_insert_private_key_ex(word32 keyId, + const byte* eccDer, word32 eccDerSize, word32 policyFlags, + word32 authObjId); +WOLFSSL_API int wc_se050_ecc_insert_public_key_policy(word32 keyId, + const byte* eccDer, word32 eccDerSize, const sss_policy_t* policy); +WOLFSSL_API int wc_se050_ecc_insert_private_key_policy(word32 keyId, + const byte* eccDer, word32 eccDerSize, const sss_policy_t* policy); +#ifdef HAVE_ECC +/** Generate a persistent ECC key pair at a caller-selected, unused ID with a + * flag-based immutable policy. keySize is in bytes. */ +WOLFSSL_API int wc_se050_ecc_generate_key_ex(word32 keyId, int keySize, + int curveId, word32 policyFlags, word32 authObjId); +/** Generate a persistent ECC key pair with a raw middleware policy. */ +WOLFSSL_API int wc_se050_ecc_generate_key_policy(word32 keyId, int keySize, + int curveId, const sss_policy_t* policy); +#endif WOLFSSL_API int wc_se050_rsa_insert_public_key(word32 keyId, const byte* rsaDer, word32 rsaDerSize); WOLFSSL_API int wc_se050_rsa_insert_private_key(word32 keyId, const byte* rsaDer, word32 rsaDerSize); +/** Insert an RSA public/private key with a flag-based immutable policy. */ +WOLFSSL_API int wc_se050_rsa_insert_public_key_ex(word32 keyId, + const byte* rsaDer, word32 rsaDerSize, word32 policyFlags, + word32 authObjId); +WOLFSSL_API int wc_se050_rsa_insert_private_key_ex(word32 keyId, + const byte* rsaDer, word32 rsaDerSize, word32 policyFlags, + word32 authObjId); +WOLFSSL_API int wc_se050_rsa_insert_public_key_policy(word32 keyId, + const byte* rsaDer, word32 rsaDerSize, const sss_policy_t* policy); +WOLFSSL_API int wc_se050_rsa_insert_private_key_policy(word32 keyId, + const byte* rsaDer, word32 rsaDerSize, const sss_policy_t* policy); +#if !defined(NO_RSA) && !defined(WOLFSSL_SE050_NO_RSA) +/** Generate a persistent RSA key pair at a caller-selected, unused ID with a + * flag-based immutable policy. size is in bits and e must be 65537. */ +WOLFSSL_API int wc_se050_rsa_generate_key_ex(word32 keyId, int size, long e, + word32 policyFlags, word32 authObjId); +/** Generate a persistent RSA key pair with a raw middleware policy. */ +WOLFSSL_API int wc_se050_rsa_generate_key_policy(word32 keyId, int size, + long e, const sss_policy_t* policy); +#endif WOLFSSL_API int wc_se050_insert_binary_object(word32 keyId, const byte* object, word32 objectSz); +/** Insert a binary object with a flag-based immutable policy. */ +WOLFSSL_API int wc_se050_insert_binary_object_ex(word32 keyId, + const byte* object, word32 objectSz, word32 policyFlags, + word32 authObjId); +WOLFSSL_API int wc_se050_insert_binary_object_policy(word32 keyId, + const byte* object, word32 objectSz, const sss_policy_t* policy); WOLFSSL_API int wc_se050_get_binary_object(word32 keyId, byte* out, word32* outSz); +/** Read raw object attributes, including policy records and origin. */ +WOLFSSL_API int wc_se050_get_object_attributes(word32 keyId, byte* attr, + word32* attrSz); + +#ifndef WOLFSSL_SE050_NO_ATTEST +/** Read and attest an object using a caller-generated 16-byte freshness + * challenge. */ +WOLFSSL_API int wc_se050_attest_object(word32 keyId, word32 attestKeyId, + enum wc_HashType hashAlgo, const byte* random, word32 randomSz, + wc_se050_attst_result* result); +/** Verify all returned attestation components with an ECC/RSA public key and + * require the independently retained 16-byte freshness challenge. */ +WOLFSSL_API int wc_se050_verify_attestation( + const wc_se050_attst_result* result, const byte* attestPubDer, + word32 attestPubDerSz, const byte* expectedRandom, + word32 expectedRandomSz, int* res); +/** Attest a key with a caller-generated 16-byte freshness challenge, verify + * the signature, and compare its public-key DER. */ +WOLFSSL_API int wc_se050_validate_provisioned_key(word32 keyId, + word32 attestKeyId, const byte* expectedPubDer, word32 expectedPubDerSz, + const byte* attestPubDer, word32 attestPubDerSz, const byte* random, + word32 randomSz, int* res); +#endif /* Private Functions */ WOLFSSL_LOCAL word32 se050_allocate_key(int keyType); diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index b111438a36..3872da0c79 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -3290,6 +3290,14 @@ #define WOLFSSL_CRYPT_HW_MUTEX 1 #endif +/* SE05x operations share one session and transport. Enable the wolfCrypt + * hardware mutex by default so the public SE05x lock API and the port's + * internal serialization are effective in normal threaded builds. */ +#if defined(WOLFSSL_SE050) && !defined(SINGLE_THREADED) && \ + !defined(WOLFSSL_CRYPT_HW_MUTEX) + #define WOLFSSL_CRYPT_HW_MUTEX 1 +#endif + #if !defined(XMALLOC_USER) && !defined(MICRIUM_MALLOC) && \ !defined(WOLFSSL_LEANPSK) && !defined(NO_WOLFSSL_MEMORY) && \ !defined(XMALLOC_OVERRIDE)