feat(lib/t_cose): EL3 based crypto
Add a t_cose crypto adapter that is used by the RMM to hook crypto and
redirect it to the EL3. By default, the RMM uses the PSA crypto adapter,
but in order to support delegated attestation where the RMM does not
have access to the attestation private key, we need the ability to
redirect signing requests to the EL3 instead of PSA crypto. EL3 may then
handle the request itself or forward to an agent like HES.
During the redirect, the adapter queues attestation requests into a
global EL3 queue via registered callback during library initialization.
The main idea is to patch the psa crypto adapter in t_cose so that it
forwards the sign call(s) if ATTEST_EL3_TOKEN_SIGN is defined.
As not all the functions that use the crypto_context are redirected (and
even if they were, new functions can be added by upstream t_cose) this
patch need to make sure that casting the crypto_context to
t_cose_psa_crypto_context still works. So t_cose_rmm_el3_ctx is updated
to have t_cose_psa_crypto_context as its first member.
Change-Id: Ie9fefdfa3297e05406d7c06f82d9550c5c2d2474
Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
Signed-off-by: Raghu Krishnamurthy <raghupathyk@nvidia.com>
diff --git a/cmake/CommonConfigs.cmake b/cmake/CommonConfigs.cmake
index 2d57c8c..569f8d3 100644
--- a/cmake/CommonConfigs.cmake
+++ b/cmake/CommonConfigs.cmake
@@ -65,6 +65,13 @@
TYPE BOOL
DEFAULT OFF)
+arm_config_option(
+ NAME ATTEST_EL3_TOKEN_SIGN
+ HELP "Use EL3 service to sign realm attestation token."
+ TYPE BOOL
+ DEFAULT OFF
+ ADVANCED)
+
#
# Introduce a pseudo-library purely for applying flags to RMM's libraries.
# This is applied to any targets created after this point.
diff --git a/configs/t_cose/0001-Add-compile-time-option-for-el3-signing.patch b/configs/t_cose/0001-Add-compile-time-option-for-el3-signing.patch
new file mode 100644
index 0000000..f362a08
--- /dev/null
+++ b/configs/t_cose/0001-Add-compile-time-option-for-el3-signing.patch
@@ -0,0 +1,114 @@
+From 73fefd8abb9281bfadb259cd837a826816fa3744 Mon Sep 17 00:00:00 2001
+From: Mate Toth-Pal <mate.toth-pal@arm.com>
+Date: Wed, 3 Jul 2024 11:22:06 +0200
+Subject: [PATCH] Add compile time option for el3 based signing
+
+Update signing related functions to call hes variants if
+ATTEST_EL3_TOKEN_SIGN is defined.
+---
+ crypto_adapters/t_cose_psa_crypto.c | 14 +++++++++++++-
+ crypto_adapters/t_cose_psa_crypto.h | 28 ++++++++++++++++++++++++++--
+ 2 files changed, 39 insertions(+), 3 deletions(-)
+
+diff --git a/crypto_adapters/t_cose_psa_crypto.c b/crypto_adapters/t_cose_psa_crypto.c
+index 04235f1..b648358 100644
+--- a/crypto_adapters/t_cose_psa_crypto.c
++++ b/crypto_adapters/t_cose_psa_crypto.c
+@@ -119,7 +119,7 @@ bool t_cose_crypto_is_algorithm_supported(int32_t cose_algorithm_id)
+ *
+ * \return The PSA algorithm ID or 0 if this doesn't map the COSE ID.
+ */
+-static psa_algorithm_t cose_alg_id_to_psa_alg_id(int32_t cose_alg_id)
++psa_algorithm_t cose_alg_id_to_psa_alg_id(int32_t cose_alg_id)
+ {
+ /* The #ifdefs save a little code when algorithms are disabled */
+ switch(cose_alg_id) {
+@@ -226,6 +226,11 @@ t_cose_crypto_sign(int32_t cose_algorithm_id,
+ struct q_useful_buf signature_buffer,
+ struct q_useful_buf_c *signature)
+ {
++#if ATTEST_EL3_TOKEN_SIGN
++ return t_cose_crypto_el3_token_sign(
++ cose_algorithm_id, signing_key, crypto_context,
++ hash_to_sign, signature_buffer, signature);
++#else
+ enum t_cose_err_t return_value;
+ psa_status_t psa_result;
+ psa_algorithm_t psa_alg_id;
+@@ -264,6 +269,7 @@ t_cose_crypto_sign(int32_t cose_algorithm_id,
+
+ Done:
+ return return_value;
++#endif
+ }
+
+
+@@ -280,6 +286,11 @@ t_cose_crypto_sign_restart(bool started,
+ struct q_useful_buf signature_buffer,
+ struct q_useful_buf_c *signature)
+ {
++#if ATTEST_EL3_TOKEN_SIGN
++ return t_cose_crypto_el3_token_sign_restart(
++ started, cose_algorithm_id, signing_key, crypto_context,
++ hash_to_sign, signature_buffer, signature);
++#else
+ enum t_cose_err_t return_value;
+ psa_status_t psa_result;
+ psa_algorithm_t psa_alg_id;
+@@ -333,6 +344,7 @@ t_cose_crypto_sign_restart(bool started,
+
+ Done:
+ return return_value;
++#endif
+ }
+ #endif /* PSA_CRYPTO_HAS_RESTARTABLE_SIGNING */
+
+diff --git a/crypto_adapters/t_cose_psa_crypto.h b/crypto_adapters/t_cose_psa_crypto.h
+index bf4963c..51473fe 100644
+--- a/crypto_adapters/t_cose_psa_crypto.h
++++ b/crypto_adapters/t_cose_psa_crypto.h
+@@ -14,14 +14,42 @@
+
+ #include <psa/crypto.h>
+
++/*
++ * t_cose will not call into MbedTLS when ATTEST_EL3_TOKEN_SIGN build
++ * option is defined. Hence ignore the MbedTLS version check.
++ */
+ #define PSA_CRYPTO_HAS_RESTARTABLE_SIGNING \
+- ((MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR >= 4) || \
+- MBEDTLS_VERSION_MAJOR > 3)
++ (((MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR >= 4) || \
++ MBEDTLS_VERSION_MAJOR > 3) || ATTEST_EL3_TOKEN_SIGN)
+
+ #if PSA_CRYPTO_HAS_RESTARTABLE_SIGNING
+ struct t_cose_psa_crypto_context {
+ psa_sign_hash_interruptible_operation_t operation;
+ };
++
++psa_algorithm_t cose_alg_id_to_psa_alg_id(int32_t cose_alg_id);
++
++#if ATTEST_EL3_TOKEN_SIGN
++enum t_cose_err_t
++t_cose_crypto_el3_token_sign_restart(bool started,
++ int32_t cose_algorithm_id,
++ struct t_cose_key signing_key,
++ void *crypto_context,
++ struct q_useful_buf_c hash_to_sign,
++ struct q_useful_buf signature_buffer,
++ struct q_useful_buf_c *signature);
++#endif /* ATTEST_EL3_TOKEN_SIGN */
++
+ #endif /* PSA_CRYPTO_HAS_RESTARTABLE_SIGNING */
+
++#if ATTEST_EL3_TOKEN_SIGN
++enum t_cose_err_t t_cose_crypto_el3_token_sign(int32_t cose_algorithm_id,
++ struct t_cose_key signing_key,
++ void *crypto_context,
++ struct q_useful_buf_c hash_to_sign,
++ struct q_useful_buf signature_buffer,
++ struct q_useful_buf_c *signature);
++#endif /* ATTEST_EL3_TOKEN_SIGN */
++
++
+ #endif /* t_cose_psa_crypto_h */
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index 42c798d..929b1f6 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -293,6 +293,7 @@
RMM_CCA_DA ,ON | OFF ,OFF ,"Enable Device Assignment support in RMM (experimental)"
ATTEST_PLAT_TOKEN_SIZE , ,0x1000 ,"Maximum size in bytes expected for the Attestation platform token"
PLAT_ARM_MAX_DRAM_BANKS , ,2 ,"Maximum number of DRAM banks allowed in Arm platform layer"
+ ATTEST_EL3_TOKEN_SIGN ,ON|OFF ,OFF ,"Use EL3 service to sign realm attestation token."
.. _llvm_build:
diff --git a/lib/t_cose/CMakeLists.txt b/lib/t_cose/CMakeLists.txt
index 302385f..f947bd4 100644
--- a/lib/t_cose/CMakeLists.txt
+++ b/lib/t_cose/CMakeLists.txt
@@ -16,7 +16,8 @@
# Patch t_cose
set(T_COSE_PATCH_DIR "${RMM_SOURCE_DIR}/configs/t_cose")
set(T_COSE_PATCH_FILES
- "${T_COSE_PATCH_DIR}/0001-Add-t_cose_key_encode-API.patch")
+ "${T_COSE_PATCH_DIR}/0001-Add-t_cose_key_encode-API.patch"
+ "${T_COSE_PATCH_DIR}/0001-Add-compile-time-option-for-el3-signing.patch")
Git_Apply_Patches(${T_COSE_SOURCE_DIR} "${T_COSE_PATCH_FILES}")
@@ -29,6 +30,8 @@
"-Wno-unused-variable")
target_compile_definitions(t_cose
+ PUBLIC
+ "ATTEST_EL3_TOKEN_SIGN=$<IF:$<BOOL:${ATTEST_EL3_TOKEN_SIGN}>,1,0>"
PRIVATE
"T_COSE_USE_PSA_CRYPTO=1"
"T_COSE_DISABLE_HPKE=1"
@@ -48,6 +51,7 @@
target_include_directories(t_cose
PUBLIC
+ "include"
"${T_COSE_SOURCE_DIR}/inc"
"${T_COSE_SOURCE_DIR}/crypto_adapters"
PRIVATE
@@ -63,3 +67,18 @@
"${T_COSE_SOURCE_DIR}/src/t_cose_util.c"
"${T_COSE_SOURCE_DIR}/src/t_cose_key.c"
)
+
+if (ATTEST_EL3_TOKEN_SIGN)
+ target_compile_definitions(t_cose
+ PRIVATE
+ "T_COSE_DISABLE_COSE_SIGN=1")
+
+ target_sources(t_cose
+ PRIVATE
+ "src/t_cose_el3_token_sign.c")
+
+ target_link_libraries(t_cose
+ PRIVATE
+ rmm-lib-arch
+ rmm-lib-debug)
+endif()
diff --git a/lib/t_cose/include/t_cose_el3_token_sign.h b/lib/t_cose/include/t_cose_el3_token_sign.h
new file mode 100644
index 0000000..b99334d
--- /dev/null
+++ b/lib/t_cose/include/t_cose_el3_token_sign.h
@@ -0,0 +1,68 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef T_COSE_EL3_TOKEN_SIGN_H
+#define T_COSE_EL3_TOKEN_SIGN_H
+
+#include <spinlock.h>
+#include <t_cose_psa_crypto.h>
+
+struct t_cose_el3_token_sign_ctx {
+
+ /*
+ * Some of the operations in the crypto adaption layer might use the
+ * crypto context. Make sure that casting this context to
+ * `struct t_cose_psa_crypto_context` by them is safe.
+ */
+ struct t_cose_psa_crypto_context psa_crypto_context;
+
+ spinlock_t lock;
+ struct {
+ /* cppcheck-suppress misra-c2012-6.1 */
+ bool is_req_queued : 1U;
+ /* cppcheck-suppress misra-c2012-6.1 */
+ bool is_sig_valid : 1U;
+ /* cppcheck-suppress misra-c2012-6.1 */
+ bool is_el3_err : 1U;
+ psa_algorithm_t sign_alg_id;
+ uintptr_t cookie;
+ uint64_t req_ticket;
+ size_t sig_len;
+ void *sig_buffer;
+ size_t hash_len;
+ const void *hash_buffer;
+ } state;
+};
+COMPILER_ASSERT_NO_CBMC(U(offsetof(struct t_cose_el3_token_sign_ctx, psa_crypto_context)) == 0U);
+
+/*
+ * Callback function for the t_cose lib to be able to enqueue a signing request.
+ * The function is expected to return true if the request was successfully queued
+ * and false otherwise.
+ * The t_cose adaptation will call this function with the lock held for el3_ctx. Since
+ * the lock is held, it imposes lock ordering constraints on the call back.
+ *
+ * The callback returns 0 on success, -EAGAIN to try queuing again or other negative
+ * error codes for any other error.
+ */
+typedef int (*t_cose_crypto_el3_enqueue_t)(struct t_cose_el3_token_sign_ctx *el3_ctx,
+ uint64_t *ticket);
+
+/*
+ * Initialize enqueue callback with the library. This is expected to be called
+ * once during boot.
+ */
+void t_cose_crypto_el3_enqueue_cb_init(t_cose_crypto_el3_enqueue_t enqueue);
+
+/*
+ * Initialize the el3 token signing context and associate with the
+ * granule address of the REC it is associated with. The library user
+ * must call this function when the context is first used or being
+ * reused for a new signing operation.
+ */
+void t_cose_crypto_el3_ctx_init(struct t_cose_el3_token_sign_ctx *el3_ctx,
+ uintptr_t cookie);
+
+#endif /* T_COSE_EL3_TOKEN_SIGN_H */
diff --git a/lib/t_cose/src/t_cose_el3_token_sign.c b/lib/t_cose/src/t_cose_el3_token_sign.c
new file mode 100644
index 0000000..816ff18
--- /dev/null
+++ b/lib/t_cose/src/t_cose_el3_token_sign.c
@@ -0,0 +1,162 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+/*
+ * This file contains alternative implementations of the signing related
+ * functions defined in the t_cose crypto adaption layer.
+ * Instead of calling the PSA crypto interface, these implementations use EL3
+ * services to perform the signing operation.
+ *
+ * These functions are called from the patched
+ * ext/t_cose/crypto_adapters/t_cose_psa_crypto.c file.
+ *
+ * For signing, we only use restartable signing since it allows returning
+ * to the caller without completing signing, which is required for
+ * offloading signing operations to EL3 services, asynchronously.
+ */
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <t_cose_crypto.h> /* The interface this implements */
+#include <t_cose_el3_token_sign.h>
+
+static t_cose_crypto_el3_enqueue_t t_cose_crypto_el3_enqueue;
+static bool cb_initialized;
+
+static void
+t_cose_crypto_init_el3_ctx_crypto(struct t_cose_el3_token_sign_ctx *el3_ctx_locked,
+ psa_algorithm_t psa_algorithm_id,
+ struct q_useful_buf_c hash_to_sign,
+ struct q_useful_buf signature_buffer)
+{
+ /* Assumes lock is held for context */
+ el3_ctx_locked->state.sign_alg_id = psa_algorithm_id;
+ el3_ctx_locked->state.sig_len = signature_buffer.len;
+ el3_ctx_locked->state.sig_buffer = signature_buffer.ptr;
+ el3_ctx_locked->state.hash_len = hash_to_sign.len;
+ el3_ctx_locked->state.hash_buffer = hash_to_sign.ptr;
+}
+
+void t_cose_crypto_el3_enqueue_cb_init(t_cose_crypto_el3_enqueue_t enqueue)
+{
+ t_cose_crypto_el3_enqueue = enqueue;
+ cb_initialized = true;
+}
+
+void t_cose_crypto_el3_ctx_init(struct t_cose_el3_token_sign_ctx *el3_ctx,
+ uintptr_t cookie)
+{
+ assert(cb_initialized);
+
+ spinlock_acquire(&el3_ctx->lock);
+ (void)memset(&el3_ctx->state, 0, sizeof(el3_ctx->state));
+ el3_ctx->state.cookie = cookie;
+ spinlock_release(&el3_ctx->lock);
+}
+
+/*
+ * See documentation in t_cose_crypto.h
+ */
+enum t_cose_err_t t_cose_crypto_el3_token_sign_restart(
+ bool started, int32_t cose_algorithm_id, struct t_cose_key signing_key,
+ void *crypto_context, struct q_useful_buf_c hash_to_sign,
+ struct q_useful_buf signature_buffer, struct q_useful_buf_c *signature)
+{
+ enum t_cose_err_t return_value;
+ struct t_cose_el3_token_sign_ctx *el3_crypto_context;
+ psa_algorithm_t psa_alg_id;
+
+ (void)signing_key;
+
+ assert(cb_initialized);
+
+ psa_alg_id = cose_alg_id_to_psa_alg_id(cose_algorithm_id);
+ if (!PSA_ALG_IS_ECDSA(psa_alg_id)) {
+ return_value = T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
+ goto done;
+ }
+
+ if (!crypto_context) {
+ return_value = T_COSE_ERR_FAIL;
+ goto done;
+ }
+
+ el3_crypto_context = (struct t_cose_el3_token_sign_ctx *)crypto_context;
+
+ /*
+ * Since the response corresponding to this REC can be updated by
+ * another REC we need to protect the below from concurrent access.
+ */
+ spinlock_acquire(&el3_crypto_context->lock);
+ if (!started) {
+ t_cose_crypto_init_el3_ctx_crypto(el3_crypto_context,
+ psa_alg_id,
+ hash_to_sign,
+ signature_buffer);
+ }
+
+ if (!el3_crypto_context->state.is_req_queued) {
+ int ret;
+ /*
+ * The callback is called with the lock held for the context
+ * and imposes lock ordering constraints on the call back to
+ * ensure there are no dead locks.
+ * lock ordering: el3_crypto_context->lock -> any lock held
+ * by the callback.
+ */
+ ret = t_cose_crypto_el3_enqueue(el3_crypto_context,
+ &el3_crypto_context->state.req_ticket);
+ if (ret == 0) {
+ el3_crypto_context->state.is_req_queued = true;
+ } else if (ret != -EAGAIN) {
+ return_value = T_COSE_ERR_FAIL;
+ ERROR("%s:%d: EL3 Token Sign queuing failed.\n",
+ __func__, __LINE__);
+ goto release;
+ }
+ }
+
+ /*
+ * Responses for this request are pulled outside the current function
+ */
+ if (el3_crypto_context->state.is_el3_err) {
+ return_value = T_COSE_ERR_FAIL;
+ ERROR("%s:%d: EL3 Token Sign failed when pulling response\n",
+ __func__, __LINE__);
+ goto release;
+ }
+
+ return_value = T_COSE_ERR_SIG_IN_PROGRESS;
+
+ if (el3_crypto_context->state.is_sig_valid) {
+ assert(el3_crypto_context->state.is_req_queued == true);
+ assert(el3_crypto_context->state.sig_len <= signature_buffer.len);
+ signature->ptr = signature_buffer.ptr;
+ signature->len = el3_crypto_context->state.sig_len;
+ return_value = T_COSE_SUCCESS;
+ }
+
+release:
+ spinlock_release(&el3_crypto_context->lock);
+done:
+ return return_value;
+}
+
+enum t_cose_err_t t_cose_crypto_el3_token_sign(int32_t cose_algorithm_id,
+ struct t_cose_key signing_key,
+ void *crypto_context,
+ struct q_useful_buf_c hash_to_sign,
+ struct q_useful_buf signature_buffer,
+ struct q_useful_buf_c *signature)
+{
+ (void)cose_algorithm_id;
+ (void)signing_key;
+ (void)crypto_context;
+ (void)hash_to_sign;
+ (void)signature_buffer;
+ (void)signature;
+
+ return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
+}