feat(runtime/rmi): implement RMI_PDEV_SET_PUBKEY
Validate the public key length and key algorithm based on RMM
specification. Convert the raw public key to mbedtls ecdh or rsa context
based on key algorithm. Set public context in libspdm connection.
Compute the digest of the public key as part of attestation evidence.
This change also adds a patch to libspdm which will set the PUB_KEY to
the spdm connection in provided certificate slot id.
Supports public key from certificate that uses asym algo of type
ECDSA_P256, ECDSA_P384 or RSASSA_3072.
Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
Change-Id: I3d5efa12fe7c267120664ff88e81964db298a9c7
diff --git a/app/device_assignment/el0_app/spdm_requester/CMakeLists.txt b/app/device_assignment/el0_app/spdm_requester/CMakeLists.txt
index 6207365..4a981cc 100644
--- a/app/device_assignment/el0_app/spdm_requester/CMakeLists.txt
+++ b/app/device_assignment/el0_app/spdm_requester/CMakeLists.txt
@@ -27,6 +27,7 @@
"${LIBSPDM_PATCH_DIR}/0001-cryptlib_mbedtls-use-external-Mbed-TLS.patch"
"${LIBSPDM_PATCH_DIR}/0002-fix-libspdm_hmac_new-return-and-LIBSPDM_STATUS_CONST.patch"
"${LIBSPDM_PATCH_DIR}/0003-get_certificate-do-not-store-cert_chain-content.patch"
+ "${LIBSPDM_PATCH_DIR}/0004-add-LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY.patch"
)
Git_Apply_Patches(${LIBSPDM_DIR} "${LIBSPDM_PATCH_FILES}")
diff --git a/app/device_assignment/el0_app/src/dev_assign_el0_app.c b/app/device_assignment/el0_app/src/dev_assign_el0_app.c
index fcd8d7a..ef44b68 100644
--- a/app/device_assignment/el0_app/src/dev_assign_el0_app.c
+++ b/app/device_assignment/el0_app/src/dev_assign_el0_app.c
@@ -341,10 +341,6 @@
rc = cma_spdm_cache_certificate(info, cert_rsp);
if (rc != 0) {
- /* cppcheck-suppress misra-c2012-12.2 */
- /* cppcheck-suppress misra-c2012-10.1 */
- /* coverity[misra_c_2012_rule_10_1_violation:SUPPRESS] */
- /* coverity[misra_c_2012_rule_12_2_violation:SUPPRESS] */
return LIBSPDM_STATUS_RECEIVE_FAIL;
}
}
@@ -422,12 +418,132 @@
void dev_assign_unset_pubkey(struct dev_assign_info *info)
{
+ libspdm_data_parameter_t parameter;
+ void *data_ptr;
+
if ((info->key_sig_algo == RMI_SIGNATURE_ALGORITHM_ECDSA_P256) ||
(info->key_sig_algo == RMI_SIGNATURE_ALGORITHM_ECDSA_P384)) {
mbedtls_ecdh_free(&info->pk_ctx.ecdh);
} else {
mbedtls_rsa_free(&info->pk_ctx.rsa);
}
+
+ /* Set LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY in spdm connection */
+ (void)memset(¶meter, 0, sizeof(parameter));
+ parameter.location = LIBSPDM_DATA_LOCATION_CONNECTION;
+ parameter.additional_data[0] = info->cert_slot_id;
+ data_ptr = NULL;
+ (void)libspdm_set_data(info->libspdm_ctx,
+ LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY,
+ ¶meter, (void *)&data_ptr, sizeof(data_ptr));
+}
+
+/*
+ * Set public key context in libspdm connection
+ */
+static int dev_assign_set_pubkey(uintptr_t heap,
+ unsigned long key_sig_algo)
+{
+ libspdm_data_parameter_t parameter;
+ libspdm_return_t status;
+ struct dev_assign_info *info;
+ void *data_ptr;
+ int rc;
+
+ info = heap_start_to_dev_assign_info(heap);
+
+ struct rmi_public_key_params *params =
+ (struct rmi_public_key_params *)get_shared_mem_start();
+
+ if ((key_sig_algo == RMI_SIGNATURE_ALGORITHM_ECDSA_P256) ||
+ (key_sig_algo == RMI_SIGNATURE_ALGORITHM_ECDSA_P384)) {
+ mbedtls_ecdh_context *ecdh;
+ mbedtls_ecp_keypair kp;
+ mbedtls_ecp_group grp;
+ mbedtls_ecp_point pt;
+
+ ecdh = &info->pk_ctx.ecdh;
+
+ mbedtls_ecdh_init(ecdh);
+ mbedtls_ecp_keypair_init(&kp);
+ mbedtls_ecp_group_init(&grp);
+ mbedtls_ecp_point_init(&pt);
+
+ /* todo: call keypair/group/point_free upon mbedtls_error */
+ if (key_sig_algo == RMI_SIGNATURE_ALGORITHM_ECDSA_P256) {
+ VERBOSE("PDEV_SET_PUBKEY called with ECDSAP256 Algo\n");
+ rc = mbedtls_ecp_group_load(&grp,
+ MBEDTLS_ECP_DP_SECP256R1);
+ } else {
+ VERBOSE("PDEV_SET_PUBKEY called with ECDSAP384 Algo\n");
+ rc = mbedtls_ecp_group_load(&grp,
+ MBEDTLS_ECP_DP_SECP384R1);
+ }
+ if (rc != 0) {
+ goto end_ecdsa;
+ }
+
+ rc = mbedtls_ecp_point_read_binary(&grp, &pt, params->key, params->key_len);
+ if (rc != 0) {
+ goto end_ecdsa;
+ }
+
+ /*
+ * grp.id will be populated as part of read_binary, ignore
+ * coverity uninitialized value
+ */
+ /* coverity[uninit_use_in_call:SUPPRESS] */
+ rc = mbedtls_ecp_set_public_key(grp.id, &kp, &pt);
+ if (rc != 0) {
+ goto end_ecdsa;
+ }
+
+ rc = mbedtls_ecdh_get_params(ecdh, &kp, MBEDTLS_ECDH_OURS);
+ if (rc != 0) {
+ goto end_ecdsa;
+ }
+
+end_ecdsa:
+ mbedtls_ecp_keypair_free(&kp);
+ mbedtls_ecp_group_free(&grp);
+ mbedtls_ecp_point_free(&pt);
+ if (rc != 0) {
+ mbedtls_ecdh_free(ecdh);
+ return DEV_ASSIGN_STATUS_ERROR;
+ }
+ } else if (key_sig_algo == RMI_SIGNATURE_ALGORITHM_RSASSA_3072) {
+ mbedtls_rsa_context *ctx = &info->pk_ctx.rsa;
+
+ mbedtls_rsa_init(ctx);
+
+ /* Public exponent of RSA3072 key is held in metadata */
+ rc = mbedtls_rsa_import_raw(ctx, params->key, params->key_len, NULL, 0, NULL, 0,
+ NULL, 0, params->metadata, params->metadata_len);
+ if (rc != 0) {
+ mbedtls_rsa_free(ctx);
+ return DEV_ASSIGN_STATUS_ERROR;
+ }
+ } else {
+ ERROR("PDEV_SET_PUBKEY: Invalid Signature algorithm: %lu\n", key_sig_algo);
+ return DEV_ASSIGN_STATUS_ERROR;
+ }
+
+ info->key_sig_algo = (uint32_t)key_sig_algo;
+
+ /* Set LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY in spdm connection */
+ (void)memset(¶meter, 0, sizeof(parameter));
+ parameter.location = LIBSPDM_DATA_LOCATION_CONNECTION;
+ parameter.additional_data[0] = info->cert_slot_id;
+ data_ptr = (void *)&info->pk_ctx;
+ status = libspdm_set_data(info->libspdm_ctx,
+ LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY,
+ ¶meter, (void *)&data_ptr, sizeof(data_ptr));
+ if (status != LIBSPDM_STATUS_SUCCESS) {
+ dev_assign_unset_pubkey(info);
+ return DEV_ASSIGN_STATUS_ERROR;
+ }
+
+ return DEV_ASSIGN_STATUS_SUCCESS;
}
/*
@@ -790,6 +906,8 @@
case DEVICE_ASSIGN_APP_FUNC_ID_CONNECT_INIT:
case DEVICE_ASSIGN_APP_FUNC_ID_STOP_CONNECTION:
return dev_assign_communicate_cmd_cmn(func_id, heap);
+ case DEVICE_ASSIGN_APP_FUNC_SET_PUBLIC_KEY:
+ return (unsigned long)dev_assign_set_pubkey(heap, arg_0);
case DEVICE_ASSIGN_APP_FUNC_ID_DEINIT:
return (unsigned long)dev_assign_deinit(heap);
default:
diff --git a/app/device_assignment/rmm_stub/include/dev_assign_app.h b/app/device_assignment/rmm_stub/include/dev_assign_app.h
index 1e0c818..9f6137f 100644
--- a/app/device_assignment/rmm_stub/include/dev_assign_app.h
+++ b/app/device_assignment/rmm_stub/include/dev_assign_app.h
@@ -65,4 +65,16 @@
*/
int dev_assign_abort_app_operation(struct app_data_cfg *app_data);
+/*
+ * Sets public key of the device.
+ * Arguments:
+ * app_data - Pointer to app_data_cfg. This is opaque to caller
+ * pubkey_params - Public key parameters as received from the NS host
+ *
+ * Returns DEV_ASSIGN_STATUS_SUCCESS if setting the public was successful
+ * DEV_ASSIGN_STATUS_ERROR otherwise.
+ */
+int dev_assign_set_public_key(struct app_data_cfg *app_data,
+ struct rmi_public_key_params *pubkey_params);
+
#endif /* DEV_ASSIGN_APP_H */
diff --git a/app/device_assignment/rmm_stub/include/dev_assign_structs.h b/app/device_assignment/rmm_stub/include/dev_assign_structs.h
index 33578a9..b46e162 100644
--- a/app/device_assignment/rmm_stub/include/dev_assign_structs.h
+++ b/app/device_assignment/rmm_stub/include/dev_assign_structs.h
@@ -110,6 +110,16 @@
#define DEVICE_ASSIGN_APP_FUNC_ID_RESUME 10
/*
+ * App function to store a public key in the app's keystore.
+ *
+ * Shared app buf == `struct rmi_public_key_params`
+ *
+ * ret0 == DEV_ASSIGN_STATUS_SUCCESS if the public key is successfully set.
+ * DEV_ASSIGN_STATUS_ERROR if error occurred during key loading.
+ */
+#define DEVICE_ASSIGN_APP_FUNC_SET_PUBLIC_KEY 3
+
+/*
* App function ID to de-initialise. App uses heap available via
* tpidrro_el0.
*
diff --git a/app/device_assignment/rmm_stub/src/dev_assign_app_stub.c b/app/device_assignment/rmm_stub/src/dev_assign_app_stub.c
index 80d9bac..5f3d407 100644
--- a/app/device_assignment/rmm_stub/src/dev_assign_app_stub.c
+++ b/app/device_assignment/rmm_stub/src/dev_assign_app_stub.c
@@ -114,3 +114,17 @@
return DEV_ASSIGN_STATUS_SUCCESS;
}
+
+int dev_assign_set_public_key(struct app_data_cfg *app_data,
+ struct rmi_public_key_params *pubkey_params)
+{
+ int rc;
+
+ app_map_shared_page(app_data);
+ (void)memcpy(app_data->el2_shared_page, (void *)pubkey_params,
+ sizeof(*pubkey_params));
+ rc = (int)app_run(app_data, DEVICE_ASSIGN_APP_FUNC_SET_PUBLIC_KEY,
+ pubkey_params->algo, 0, 0, 0);
+ app_unmap_shared_page(app_data);
+ return rc;
+}
diff --git a/configs/libspdm/0004-add-LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY.patch b/configs/libspdm/0004-add-LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY.patch
new file mode 100644
index 0000000..6faf473
--- /dev/null
+++ b/configs/libspdm/0004-add-LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY.patch
@@ -0,0 +1,54 @@
+From cf5a0e58bf4b3b8375e9d9a39056bd47f19a52c2 Mon Sep 17 00:00:00 2001
+From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
+Date: Fri, 26 Jul 2024 11:34:11 +0100
+Subject: [PATCH 4/5] add LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY
+
+Add LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY to set public key
+context in provided certificate slot id.
+
+Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
+---
+ include/library/spdm_common_lib.h | 1 +
+ library/spdm_common_lib/libspdm_com_context_data.c | 14 ++++++++++++++
+ 2 files changed, 15 insertions(+)
+
+diff --git a/include/library/spdm_common_lib.h b/include/library/spdm_common_lib.h
+index a1fa8cc3..764eeb2e 100644
+--- a/include/library/spdm_common_lib.h
++++ b/include/library/spdm_common_lib.h
+@@ -159,6 +159,7 @@ typedef enum {
+
+ LIBSPDM_DATA_TOTAL_KEY_PAIRS,
+ LIBSPDM_DATA_PEER_USED_CERT_CHAIN_HASH,
++ LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY,
+
+ /* MAX */
+ LIBSPDM_DATA_MAX
+diff --git a/library/spdm_common_lib/libspdm_com_context_data.c b/library/spdm_common_lib/libspdm_com_context_data.c
+index 2307d192..08852240 100644
+--- a/library/spdm_common_lib/libspdm_com_context_data.c
++++ b/library/spdm_common_lib/libspdm_com_context_data.c
+@@ -626,6 +626,20 @@ libspdm_return_t libspdm_set_data(void *spdm_context, libspdm_data_type_t data_t
+ sizeof(context->connection_info.peer_used_cert_chain[slot_id].buffer_hash),
+ data, data_size);
+ break;
++ case LIBSPDM_DATA_PEER_USED_CERT_CHAIN_PUBLIC_KEY:
++ if (parameter->location != LIBSPDM_DATA_LOCATION_CONNECTION) {
++ return LIBSPDM_STATUS_INVALID_PARAMETER;
++ }
++ slot_id = parameter->additional_data[0];
++ if (slot_id >= SPDM_MAX_SLOT_COUNT) {
++ return LIBSPDM_STATUS_INVALID_PARAMETER;
++ }
++ if (data_size != sizeof(void *)) {
++ return LIBSPDM_STATUS_INVALID_PARAMETER;
++ }
++ context->connection_info.peer_used_cert_chain[slot_id].leaf_cert_public_key =
++ *(void **)data;
++ break;
+ case LIBSPDM_DATA_PEER_PUBLIC_KEY:
+ if (parameter->location != LIBSPDM_DATA_LOCATION_LOCAL) {
+ return LIBSPDM_STATUS_INVALID_PARAMETER;
+--
+2.34.1
+
diff --git a/runtime/core/handler.c b/runtime/core/handler.c
index 4076efb..d479f46 100644
--- a/runtime/core/handler.c
+++ b/runtime/core/handler.c
@@ -168,7 +168,7 @@
HANDLER(PDEV_GET_STATE, 1, 1, smc_pdev_get_state, true, true),
HANDLER(PDEV_IDE_RESET, 0, 0, NULL, true, true),
HANDLER(PDEV_NOTIFY, 0, 0, NULL, true, true),
- HANDLER(PDEV_SET_PUBKEY, 4, 0, NULL, true, true),
+ HANDLER(PDEV_SET_PUBKEY, 2, 0, smc_pdev_set_pubkey, true, true),
HANDLER(PDEV_STOP, 1, 0, smc_pdev_stop, true, true),
HANDLER(RTT_AUX_CREATE, 0, 0, NULL, true, true),
HANDLER(RTT_AUX_DESTROY, 0, 0, NULL, true, true),
diff --git a/runtime/include/smc-handler.h b/runtime/include/smc-handler.h
index 0ea36f5..4643da8 100644
--- a/runtime/include/smc-handler.h
+++ b/runtime/include/smc-handler.h
@@ -116,6 +116,9 @@
void smc_pdev_get_state(unsigned long pdev_ptr, struct smc_result *res);
+unsigned long smc_pdev_set_pubkey(unsigned long pdev_ptr,
+ unsigned long pubkey_params_ptr);
+
unsigned long smc_pdev_abort(unsigned long pdev_ptr);
unsigned long smc_pdev_stop(unsigned long pdev_ptr);
diff --git a/runtime/rmi/pdev.c b/runtime/rmi/pdev.c
index 00b001c..c5dde93 100644
--- a/runtime/rmi/pdev.c
+++ b/runtime/rmi/pdev.c
@@ -634,6 +634,136 @@
res->x[0] = RMI_ERROR_INPUT;
}
+static unsigned long get_expected_key_size(unsigned long rmi_key_algo)
+{
+ switch (rmi_key_algo) {
+ case RMI_SIGNATURE_ALGORITHM_ECDSA_P256:
+ return 32;
+ case RMI_SIGNATURE_ALGORITHM_ECDSA_P384:
+ return 48;
+ case RMI_SIGNATURE_ALGORITHM_RSASSA_3072:
+ return 384;
+ default:
+ return 0;
+ }
+}
+
+static bool public_key_sig_algo_valid(unsigned long rmi_key_algo)
+{
+ return (rmi_key_algo == RMI_SIGNATURE_ALGORITHM_ECDSA_P256) ||
+ (rmi_key_algo == RMI_SIGNATURE_ALGORITHM_ECDSA_P384) ||
+ (rmi_key_algo == RMI_SIGNATURE_ALGORITHM_RSASSA_3072);
+}
+
+/*
+ * Provide public key associated with a PDEV.
+ *
+ * pdev_ptr - PA of the PDEV
+ * pubkey_params_ptr - PA of the pubkey parameters
+ */
+unsigned long smc_pdev_set_pubkey(unsigned long pdev_ptr,
+ unsigned long pubkey_params_ptr)
+{
+ struct granule *g_pdev;
+ struct granule *g_pubkey_params;
+ void *aux_mapped_addr;
+ bool ns_access_ok;
+ struct pdev *pd;
+ struct rmi_public_key_params pubkey_params;
+ unsigned long dev_assign_public_key_sig_algo;
+ unsigned long dev_assign_public_key_expected_size;
+ unsigned long smc_rc;
+ int rc;
+
+ if (!is_rmi_feat_da_enabled()) {
+ return SMC_NOT_SUPPORTED;
+ }
+
+ if (!GRANULE_ALIGNED(pdev_ptr) || !GRANULE_ALIGNED(pubkey_params_ptr)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ /* Map and copy public key parameter */
+ g_pubkey_params = find_granule(pubkey_params_ptr);
+ if ((g_pubkey_params == NULL) ||
+ (granule_unlocked_state(g_pubkey_params) != GRANULE_STATE_NS)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ ns_access_ok = ns_buffer_read(SLOT_NS, g_pubkey_params, 0U,
+ sizeof(struct rmi_public_key_params),
+ &pubkey_params);
+ if (!ns_access_ok) {
+ return RMI_ERROR_INPUT;
+ }
+
+ /*
+ * Check key_len and metadata_len with max value.
+ */
+ /* coverity[uninit_use:SUPPRESS] */
+ if ((pubkey_params.key_len > PUBKEY_PARAM_KEY_LEN_MAX) ||
+ (pubkey_params.metadata_len > PUBKEY_PARAM_METADATA_LEN_MAX)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ /* coverity[uninit_use:SUPPRESS] */
+ dev_assign_public_key_sig_algo = pubkey_params.algo;
+ if (!public_key_sig_algo_valid(dev_assign_public_key_sig_algo)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ /*
+ * Validate 'key_len' against expected key length based on algorithm
+ */
+ dev_assign_public_key_expected_size = get_expected_key_size(dev_assign_public_key_sig_algo);
+ assert(dev_assign_public_key_expected_size != 0U);
+ if (pubkey_params.key_len != dev_assign_public_key_expected_size) {
+ return RMI_ERROR_INPUT;
+ }
+
+ /* Lock pdev granule and map it */
+ g_pdev = find_lock_granule(pdev_ptr, GRANULE_STATE_PDEV);
+ if (g_pdev == NULL) {
+ return RMI_ERROR_INPUT;
+ }
+
+ pd = buffer_granule_map(g_pdev, SLOT_PDEV);
+ if (pd == NULL) {
+ granule_unlock(g_pdev);
+ return RMI_ERROR_INPUT;
+ }
+
+ assert(pd->g_pdev == g_pdev);
+
+ if (pd->rmi_state != RMI_PDEV_STATE_NEEDS_KEY) {
+ smc_rc = RMI_ERROR_DEVICE;
+ goto out_pdev_buf_unmap;
+ }
+
+ /* Map PDEV aux granules */
+ aux_mapped_addr = buffer_pdev_aux_granules_map(pd->g_aux, pd->num_aux);
+ assert(aux_mapped_addr != NULL);
+
+ rc = dev_assign_set_public_key(&pd->da_app_data, &pubkey_params);
+ if (rc == DEV_ASSIGN_STATUS_SUCCESS) {
+ pd->dev_comm_state = DEV_COMM_PENDING;
+ pd->rmi_state = RMI_PDEV_STATE_HAS_KEY;
+ smc_rc = RMI_SUCCESS;
+ } else {
+ smc_rc = RMI_ERROR_DEVICE;
+ }
+
+ /* Unmap all PDEV aux granules */
+ buffer_pdev_aux_unmap(aux_mapped_addr, pd->num_aux);
+
+out_pdev_buf_unmap:
+ buffer_unmap(pd);
+
+ granule_unlock(g_pdev);
+
+ return smc_rc;
+}
+
/*
* Stop the PDEV. This sets the PDEV state to STOPPING, and a PDEV communicate
* call can do device specific cleanup like terminating a secure session.