aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Hall <julian.hall@arm.com>2021-05-17 16:34:48 +0100
committerGyorgy Szing <Gyorgy.Szing@arm.com>2021-07-05 12:43:51 +0200
commit482fd2fa697c8e3695a8b33e85de55fa33c2fd8e (patch)
tree7a393e1249b8b0ad1375d157235a6be66f32f88d
parent700aa36408ceec03311bd7aa097c93d220371304 (diff)
downloadtrusted-services-482fd2fa697c8e3695a8b33e85de55fa33c2fd8e.tar.gz
Add provisioning support to attestation service provider
Adds operations to enable the IAK to be provisioned into a device during manufacture. Two provisioning strategies are supported, self-generate IAK on first run and import externally generated IAK. Test cases run through each provisioning flow. Signed-off-by: Julian Hall <julian.hall@arm.com> Change-Id: I6708b064b31ef7749d5ecac24c86af6411cdc7c2
-rw-r--r--components/service/attestation/client/provision/attest_provision_client.c148
-rw-r--r--components/service/attestation/client/provision/attest_provision_client.h49
-rw-r--r--components/service/attestation/client/provision/component.cmake13
-rw-r--r--components/service/attestation/include/provision/attest_provision.h70
-rw-r--r--components/service/attestation/key_mngr/attest_key_mngr.c77
-rw-r--r--components/service/attestation/key_mngr/attest_key_mngr.h20
-rw-r--r--components/service/attestation/provider/attest_provider.c81
-rw-r--r--components/service/attestation/provider/serializer/attest_provider_serializer.h8
-rw-r--r--components/service/attestation/provider/serializer/packed-c/packedc_attest_provider_serializer.c55
-rw-r--r--components/service/attestation/test/service/attestation_provisioning_tests.cpp124
-rw-r--r--components/service/attestation/test/service/component.cmake1
-rw-r--r--deployments/component-test/component-test.cmake1
-rw-r--r--deployments/ts-service-test/linux-pc/CMakeLists.txt1
-rw-r--r--protocols/service/attestation/packed-c/export_iak_public_key.h30
-rw-r--r--protocols/service/attestation/packed-c/get_token.h7
-rw-r--r--protocols/service/attestation/packed-c/get_token_size.h7
-rw-r--r--protocols/service/attestation/packed-c/import_iak.h25
-rw-r--r--protocols/service/attestation/packed-c/opcodes.h4
18 files changed, 706 insertions, 15 deletions
diff --git a/components/service/attestation/client/provision/attest_provision_client.c b/components/service/attestation/client/provision/attest_provision_client.c
new file mode 100644
index 0000000..9209cc1
--- /dev/null
+++ b/components/service/attestation/client/provision/attest_provision_client.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include "attest_provision_client.h"
+#include <common/tlv/tlv.h>
+#include <provision/attest_provision.h>
+#include <protocols/service/attestation/packed-c/export_iak_public_key.h>
+#include <protocols/service/attestation/packed-c/import_iak.h>
+#include <protocols/service/attestation/packed-c/opcodes.h>
+#include <protocols/rpc/common/packed-c/status.h>
+
+/**
+ * @brief The singleton attest_provision_client instance
+ *
+ * The attest provison C API assumes a single backend service provider. This
+ * structure defines the state used by the attest_provision_client that communicates
+ * with a remote provider using the provided rpc caller.
+ */
+static struct attest_provision_client
+{
+ struct rpc_caller *caller;
+ int rpc_status;
+} instance;
+
+
+psa_status_t attest_provision_client_init(struct rpc_caller *caller)
+{
+ instance.caller = caller;
+ instance.rpc_status = TS_RPC_CALL_ACCEPTED;
+
+ return PSA_SUCCESS;
+}
+
+void attest_provision_client_deinit(void)
+{
+ instance.caller = NULL;
+}
+
+int attest_provision_client_rpc_status(void)
+{
+ return instance.rpc_status;
+}
+
+psa_status_t attest_provision_export_iak_public_key(
+ uint8_t *data,
+ size_t data_size,
+ size_t *data_length)
+{
+ psa_status_t psa_status = PSA_ERROR_GENERIC_ERROR;
+
+ *data_length = 0; /* For failure case */
+
+ rpc_call_handle call_handle;
+ uint8_t *req_buf;
+
+ call_handle = rpc_caller_begin(instance.caller, &req_buf, 0);
+
+ if (call_handle) {
+
+ uint8_t *resp_buf;
+ size_t resp_len;
+ int opstatus;
+
+ instance.rpc_status = rpc_caller_invoke(instance.caller, call_handle,
+ TS_ATTESTATION_OPCODE_EXPORT_IAK_PUBLIC_KEY, &opstatus, &resp_buf, &resp_len);
+
+ if (instance.rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+ psa_status = opstatus;
+
+ if (psa_status == PSA_SUCCESS) {
+
+ struct tlv_const_iterator resp_iter;
+ struct tlv_record decoded_record;
+ tlv_const_iterator_begin(&resp_iter, resp_buf, resp_len);
+
+ if (tlv_find_decode(&resp_iter,
+ TS_ATTESTATION_EXPORT_IAK_PUBLIC_KEY_OUT_TAG_DATA, &decoded_record)) {
+
+ if (decoded_record.length <= data_size) {
+
+ memcpy(data, decoded_record.value, decoded_record.length);
+ *data_length = decoded_record.length;
+ }
+ else {
+ /* Provided buffer is too small */
+ psa_status = PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+ }
+ else {
+ /* Mandatory response parameter missing */
+ psa_status = PSA_ERROR_GENERIC_ERROR;
+ }
+ }
+ }
+
+ rpc_caller_end(instance.caller, call_handle);
+ }
+
+ return psa_status;
+}
+
+psa_status_t attest_provision_import_iak(
+ const uint8_t *data,
+ size_t data_length)
+{
+ psa_status_t psa_status = PSA_ERROR_GENERIC_ERROR;
+ size_t req_len = tlv_required_space(data_length);
+
+ struct tlv_record key_record;
+ key_record.tag = TS_ATTESTATION_IMPORT_IAK_IN_TAG_DATA;
+ key_record.length = data_length;
+ key_record.value = data;
+
+ rpc_call_handle call_handle;
+ uint8_t *req_buf;
+
+ call_handle = rpc_caller_begin(instance.caller, &req_buf, req_len);
+
+ if (call_handle) {
+
+ uint8_t *resp_buf;
+ size_t resp_len;
+ int opstatus;
+ struct tlv_iterator req_iter;
+
+ tlv_iterator_begin(&req_iter, req_buf, req_len);
+ tlv_encode(&req_iter, &key_record);
+
+ instance.rpc_status = rpc_caller_invoke(instance.caller, call_handle,
+ TS_ATTESTATION_OPCODE_IMPORT_IAK, &opstatus, &resp_buf, &resp_len);
+
+ if (instance.rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+ psa_status = opstatus;
+ }
+
+ rpc_caller_end(instance.caller, call_handle);
+ }
+
+ return psa_status;
+
+}
diff --git a/components/service/attestation/client/provision/attest_provision_client.h b/components/service/attestation/client/provision/attest_provision_client.h
new file mode 100644
index 0000000..73ddbf7
--- /dev/null
+++ b/components/service/attestation/client/provision/attest_provision_client.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ATTEST_PROVISION_CLIENT_H
+#define ATTEST_PROVISION_CLIENT_H
+
+#include <psa/error.h>
+#include <rpc_caller.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Initialises the singleton attestion provisioning client
+ *
+ * Implements the provisioning client API defined in attest_provision.h
+ *
+ * @param[in] rpc_caller RPC caller instance
+ *
+ * @return A status indicating the success/failure of the operation
+ */
+psa_status_t attest_provision_client_init(struct rpc_caller *caller);
+
+/**
+ * @brief De-initialises the singleton attestion provisioning client
+ *
+ */
+void attest_provision_client_deinit(void);
+
+/**
+ * @brief Return the most recent RPC status
+ *
+ * May be used to obtain information about an RPC error that resulted
+ * in an API operation failure
+ *
+ * @return Most recent RPC operation status
+ */
+int attest_provision_client_rpc_status(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ATTEST_PROVISION_CLIENT_H */
diff --git a/components/service/attestation/client/provision/component.cmake b/components/service/attestation/client/provision/component.cmake
new file mode 100644
index 0000000..3f02418
--- /dev/null
+++ b/components/service/attestation/client/provision/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/attest_provision_client.c"
+ )
diff --git a/components/service/attestation/include/provision/attest_provision.h b/components/service/attestation/include/provision/attest_provision.h
new file mode 100644
index 0000000..d8355f0
--- /dev/null
+++ b/components/service/attestation/include/provision/attest_provision.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <psa/error.h>
+
+#ifndef ATTEST_PROVISION_H
+#define ATTEST_PROVISION_H
+
+/**
+ * A provisioning client API for perfoming one-off provisioning
+ * operations related to the attestation service. This API will typically
+ * be used by a special factory application during device manufacture.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Export IAK public key
+ *
+ * Used to retrieve the IAK public key that corresponds to the key-pair
+ * that was generated or provisioned for the device. The public key
+ * may be used by a remote verifier as an identifier for the device.
+ *
+ * \param[out] data Buffer where the key data is to be written.
+ * \param data_size Size of the \p data buffer in bytes.
+ * \param[out] data_length On success, the number of bytes
+ * that make up the key data.
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t attest_provision_export_iak_public_key(
+ uint8_t *data,
+ size_t data_size,
+ size_t *data_length);
+
+/**
+ * \brief Import IAK
+ *
+ * Used during device manufacture to provision the IAK. Two IAK
+ * provisioning strategies are supported 1) Externally generated
+ * key-pair that is provisioned using this interface. 2) Self
+ * generated where the IAK is generated by the device autonomously.
+ * If a key is to be imported, the operation must be performed before
+ * any other operation related to the attestation service. This
+ * operation may only be performed once for a device. An attempt
+ * to repeat the operation will be rejected.
+ *
+ * \param[in] data Buffer containing the key data.
+ * \param[in] data_length Size of the \p data buffer in bytes.
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t attest_provision_import_iak(
+ const uint8_t *data,
+ size_t data_length);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ATTEST_PROVISION_H */
diff --git a/components/service/attestation/key_mngr/attest_key_mngr.c b/components/service/attestation/key_mngr/attest_key_mngr.c
index a4d8ec0..783d08f 100644
--- a/components/service/attestation/key_mngr/attest_key_mngr.c
+++ b/components/service/attestation/key_mngr/attest_key_mngr.c
@@ -18,6 +18,29 @@ static struct attest_key_mngr
psa_key_handle_t iak_handle;
} instance;
+/* Local defines */
+#define IAK_KEY_BITS (256)
+
+/**
+ * \brief Set the IAK key attributes
+ *
+ * \param[out] attribute Key attributes object to set
+ * \param[in] key_id The IAK key id or zero for volatile key
+ */
+static void set_iak_attributes(psa_key_attributes_t *attributes, psa_key_id_t key_id)
+{
+ if (key_id)
+ psa_set_key_id(attributes, key_id);
+ else
+ psa_set_key_lifetime(attributes, PSA_KEY_LIFETIME_VOLATILE);
+
+ psa_set_key_usage_flags(attributes, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH);
+
+ psa_set_key_algorithm(attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
+ psa_set_key_type(attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_CURVE_SECP256R1));
+ psa_set_key_bits(attributes, IAK_KEY_BITS);
+}
+
/**
* \brief Generates the IAK
*
@@ -34,17 +57,7 @@ static psa_status_t generate_iak(psa_key_id_t key_id, psa_key_handle_t *iak_hand
psa_status_t status;
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
- if (key_id)
- psa_set_key_id(&attributes, key_id);
- else
- psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
-
- psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH);
-
- psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
- psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_CURVE_SECP256R1));
- psa_set_key_bits(&attributes, 256);
-
+ set_iak_attributes(&attributes, key_id);
status = psa_generate_key(&attributes, iak_handle);
psa_reset_key_attributes(&attributes);
@@ -63,6 +76,7 @@ void attest_key_mngr_deinit(void)
{
if (instance.is_iak_open && !instance.iak_id) {
+ /* Clean-up if IAK is volatile */
psa_destroy_key(instance.iak_handle);
instance.is_iak_open = false;
}
@@ -116,3 +130,44 @@ psa_status_t attest_key_mngr_export_iak_public_key(uint8_t *data,
return status;
}
+
+size_t attest_key_mngr_max_iak_key_size(void)
+{
+ return PSA_KEY_EXPORT_MAX_SIZE(
+ PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_CURVE_SECP256R1)),
+ IAK_KEY_BITS);
+}
+
+psa_status_t attest_key_mngr_import_iak(const uint8_t *data, size_t data_length)
+{
+ psa_status_t status = PSA_ERROR_NOT_PERMITTED;
+
+ if (!instance.is_iak_open) {
+
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ set_iak_attributes(&attributes, instance.iak_id);
+
+ if (instance.iak_id) {
+
+ /* A valid key id has been specified so only allow import
+ * if no persistent key for the key id exists.
+ */
+ if (psa_open_key(instance.iak_id, &instance.iak_handle) == PSA_ERROR_DOES_NOT_EXIST) {
+
+ /* Allow persistent key to be provisioned */
+ status = psa_import_key(&attributes, data, data_length, &instance.iak_handle);
+ }
+ }
+ else {
+
+ /* It's a volatile key so allow a once-per-boot import */
+ status = psa_import_key(&attributes, data, data_length, &instance.iak_handle);
+ }
+
+ psa_reset_key_attributes(&attributes);
+
+ instance.is_iak_open = (status == PSA_SUCCESS);
+ }
+
+ return status;
+}
diff --git a/components/service/attestation/key_mngr/attest_key_mngr.h b/components/service/attestation/key_mngr/attest_key_mngr.h
index eaf46fc..f61619a 100644
--- a/components/service/attestation/key_mngr/attest_key_mngr.h
+++ b/components/service/attestation/key_mngr/attest_key_mngr.h
@@ -46,6 +46,9 @@ void attest_key_mngr_deinit(void);
/**
* \brief Get the IAK key handle
*
+ * If an IAK doesn't exist, one will be generated. This supports the
+ * generate-on-first-run strategy.
+ *
* \param[out] iak_handle The returned key handle
* \return Status
*/
@@ -63,6 +66,23 @@ psa_status_t attest_key_mngr_get_iak_handle(psa_key_handle_t *iak_handle);
psa_status_t attest_key_mngr_export_iak_public_key(uint8_t *data,
size_t data_size, size_t *data_length);
+/**
+ * \brief Return maximum size of an IAK key-pair
+ *
+ * \return Maximum size
+ */
+size_t attest_key_mngr_max_iak_key_size(void);
+
+/**
+ * \brief Import the IAK key-pair
+ *
+ * \param[in] data The key data
+ * \param[out] data_length Length in bytes of the key-pair
+ *
+ * \return Status
+ */
+psa_status_t attest_key_mngr_import_iak(const uint8_t *data, size_t data_length);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/components/service/attestation/provider/attest_provider.c b/components/service/attestation/provider/attest_provider.c
index 1e12717..460a6d0 100644
--- a/components/service/attestation/provider/attest_provider.c
+++ b/components/service/attestation/provider/attest_provider.c
@@ -15,11 +15,15 @@
/* Service request handlers */
static rpc_status_t get_token_handler(void *context, struct call_req* req);
static rpc_status_t get_token_size_handler(void *context, struct call_req* req);
+static rpc_status_t export_iak_public_key_handler(void *context, struct call_req* req);
+static rpc_status_t import_iak_handler(void *context, struct call_req* req);
/* Handler mapping table for service */
static const struct service_handler handler_table[] = {
- {TS_ATTESTATION_OPCODE_GET_TOKEN, get_token_handler},
- {TS_ATTESTATION_OPCODE_GET_TOKEN_SIZE, get_token_size_handler}
+ {TS_ATTESTATION_OPCODE_GET_TOKEN, get_token_handler},
+ {TS_ATTESTATION_OPCODE_GET_TOKEN_SIZE, get_token_size_handler},
+ {TS_ATTESTATION_OPCODE_EXPORT_IAK_PUBLIC_KEY, export_iak_public_key_handler},
+ {TS_ATTESTATION_OPCODE_IMPORT_IAK, import_iak_handler}
};
struct rpc_interface *attest_provider_init(struct attest_provider *context, psa_key_id_t iak_id)
@@ -155,3 +159,76 @@ static rpc_status_t get_token_size_handler(void *context, struct call_req* req)
return rpc_status;
}
+
+static rpc_status_t export_iak_public_key_handler(void *context, struct call_req* req)
+{
+ rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+ struct call_param_buf *req_buf = call_req_get_req_buf(req);
+ const struct attest_provider_serializer *serializer = get_attest_serializer(context, req);
+
+ if (serializer) {
+
+ size_t max_key_size = attest_key_mngr_max_iak_key_size();
+
+ uint8_t *key_buffer = malloc(max_key_size);
+
+ if (key_buffer) {
+
+ int opstatus;
+ size_t export_size;
+ opstatus =
+ attest_key_mngr_export_iak_public_key(key_buffer, max_key_size, &export_size);
+
+ if (opstatus == PSA_SUCCESS) {
+
+ struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+ rpc_status =
+ serializer->serialize_export_iak_public_key_resp(resp_buf,
+ key_buffer, export_size);
+ }
+
+ free(key_buffer);
+ call_req_set_opstatus(req, opstatus);
+ }
+ else {
+ /* Failed to allocate key buffer */
+ rpc_status = TS_RPC_ERROR_RESOURCE_FAILURE;
+ }
+ }
+
+ return rpc_status;
+}
+
+static rpc_status_t import_iak_handler(void *context, struct call_req* req)
+{
+ rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+ struct call_param_buf *req_buf = call_req_get_req_buf(req);
+ const struct attest_provider_serializer *serializer = get_attest_serializer(context, req);
+
+ if (serializer) {
+
+ size_t key_data_len = attest_key_mngr_max_iak_key_size();
+ uint8_t *key_buffer = malloc(key_data_len);
+
+ if (key_buffer) {
+
+ rpc_status =
+ serializer->deserialize_import_iak_req(req_buf, key_buffer, &key_data_len);
+
+ if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+ int opstatus;
+ opstatus = attest_key_mngr_import_iak(key_buffer, key_data_len);
+ call_req_set_opstatus(req, opstatus);
+ }
+
+ free(key_buffer);
+ }
+ else {
+
+ rpc_status = TS_RPC_ERROR_RESOURCE_FAILURE;
+ }
+ }
+
+ return rpc_status;
+}
diff --git a/components/service/attestation/provider/serializer/attest_provider_serializer.h b/components/service/attestation/provider/serializer/attest_provider_serializer.h
index 5334cba..1c05ab0 100644
--- a/components/service/attestation/provider/serializer/attest_provider_serializer.h
+++ b/components/service/attestation/provider/serializer/attest_provider_serializer.h
@@ -33,6 +33,14 @@ struct attest_provider_serializer {
rpc_status_t (*serialize_get_token_size_resp)(struct call_param_buf *resp_buf,
size_t token_size);
+
+ /* Operation: export_iak_public_key */
+ rpc_status_t (*serialize_export_iak_public_key_resp)(struct call_param_buf *resp_buf,
+ const uint8_t *data, size_t data_len);
+
+ /* Operation: import_iak */
+ rpc_status_t (*deserialize_import_iak_req)(const struct call_param_buf *req_buf,
+ uint8_t *data, size_t *data_len);
};
#endif /* ATTEST_PROVIDER_SERIALIZER_H */
diff --git a/components/service/attestation/provider/serializer/packed-c/packedc_attest_provider_serializer.c b/components/service/attestation/provider/serializer/packed-c/packedc_attest_provider_serializer.c
index c2e5d56..65e76f3 100644
--- a/components/service/attestation/provider/serializer/packed-c/packedc_attest_provider_serializer.c
+++ b/components/service/attestation/provider/serializer/packed-c/packedc_attest_provider_serializer.c
@@ -8,6 +8,8 @@
#include <protocols/rpc/common/packed-c/status.h>
#include <protocols/service/attestation/packed-c/get_token.h>
#include <protocols/service/attestation/packed-c/get_token_size.h>
+#include <protocols/service/attestation/packed-c/export_iak_public_key.h>
+#include <protocols/service/attestation/packed-c/import_iak.h>
#include "packedc_attest_provider_serializer.h"
@@ -94,6 +96,55 @@ static rpc_status_t serialize_get_token_size_resp(struct call_param_buf *resp_bu
return rpc_status;
}
+/* Operation: export_iak_public_key */
+static rpc_status_t serialize_export_iak_public_key_resp(struct call_param_buf *resp_buf,
+ const uint8_t *data, size_t data_len)
+{
+ rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+ struct tlv_iterator resp_iter;
+
+ struct tlv_record key_record;
+ key_record.tag = TS_ATTESTATION_EXPORT_IAK_PUBLIC_KEY_OUT_TAG_DATA;
+ key_record.length = data_len;
+ key_record.value = data;
+
+ tlv_iterator_begin(&resp_iter, resp_buf->data, resp_buf->size);
+
+ if (tlv_encode(&resp_iter, &key_record)) {
+
+ resp_buf->data_len = tlv_required_space(data_len);
+ rpc_status = TS_RPC_CALL_ACCEPTED;
+ }
+
+ return rpc_status;
+}
+
+/* Operation: import_iak */
+static rpc_status_t deserialize_import_iak_req(const struct call_param_buf *req_buf,
+ uint8_t *data, size_t *data_len)
+{
+ rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+ struct tlv_const_iterator req_iter;
+ struct tlv_record decoded_record;
+ size_t out_buf_size = *data_len;
+
+ *data_len = 0;
+
+ tlv_const_iterator_begin(&req_iter, (uint8_t*)req_buf->data, req_buf->data_len);
+
+ if (tlv_find_decode(&req_iter, TS_ATTESTATION_IMPORT_IAK_IN_TAG_DATA, &decoded_record)) {
+
+ if (decoded_record.length <= out_buf_size) {
+
+ memcpy(data, decoded_record.value, decoded_record.length);
+ *data_len = decoded_record.length;
+ rpc_status = TS_RPC_CALL_ACCEPTED;
+ }
+ }
+
+ return rpc_status;
+}
+
/* Singleton method to provide access to the serializer instance */
const struct attest_provider_serializer *packedc_attest_provider_serializer_instance(void)
{
@@ -101,7 +152,9 @@ const struct attest_provider_serializer *packedc_attest_provider_serializer_inst
deserialize_get_token_req,
serialize_get_token_resp,
deserialize_get_token_size_req,
- serialize_get_token_size_resp
+ serialize_get_token_size_resp,
+ serialize_export_iak_public_key_resp,
+ deserialize_import_iak_req
};
return &instance;
diff --git a/components/service/attestation/test/service/attestation_provisioning_tests.cpp b/components/service/attestation/test/service/attestation_provisioning_tests.cpp
new file mode 100644
index 0000000..de447cf
--- /dev/null
+++ b/components/service/attestation/test/service/attestation_provisioning_tests.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <limits.h>
+#include <string.h>
+#include <service/attestation/client/provision/attest_provision_client.h>
+#include <protocols/rpc/common/packed-c/encoding.h>
+#include <service_locator.h>
+#include <provision/attest_provision.h>
+#include <CppUTest/TestHarness.h>
+
+/*
+ * Service-level provisioning tests for the attestation service.
+ */
+TEST_GROUP(AttestationProvisioningTests)
+{
+ void setup()
+ {
+ struct rpc_caller *caller;
+ int status;
+
+ m_rpc_session_handle = NULL;
+ m_attest_service_context = NULL;
+
+ service_locator_init();
+
+ m_attest_service_context =
+ service_locator_query("sn:trustedfirmware.org:attestation:0", &status);
+ CHECK_TRUE(m_attest_service_context);
+
+ m_rpc_session_handle =
+ service_context_open(m_attest_service_context, TS_RPC_ENCODING_PACKED_C, &caller);
+ CHECK_TRUE(m_rpc_session_handle);
+
+ attest_provision_client_init(caller);
+ }
+
+ void teardown()
+ {
+ attest_provision_client_deinit();
+
+ service_context_close(m_attest_service_context, m_rpc_session_handle);
+ m_rpc_session_handle = NULL;
+
+ service_context_relinquish(m_attest_service_context);
+ m_attest_service_context = NULL;
+ }
+
+ rpc_session_handle m_rpc_session_handle;
+ struct service_context *m_attest_service_context;
+};
+
+/* Reference IAK private key to provision into the key-store. The public
+ * key is generated deterministically from the private key.
+ */
+static const uint8_t ref_iak_priv_key[] =
+{
+ 0xf1, 0xb7, 0x14, 0x23, 0x43, 0x40, 0x2f, 0x3b, 0x5d, 0xe7, 0x31, 0x5e, 0xa8,
+ 0x94, 0xf9, 0xda, 0x5c, 0xf5, 0x03, 0xff, 0x79, 0x38, 0xa3, 0x7c, 0xa1, 0x4e,
+ 0xb0, 0x32, 0x86, 0x98, 0x84, 0x50
+};
+
+TEST(AttestationProvisioningTests, selfGeneratedIak)
+{
+ /* Verify that the provisioning flow where the device self-generates an
+ * IAK on first run works as expected. Because no IAK exists at test entry,
+ * the export IAK public key operation should trigger generation of a key.
+ */
+ psa_status_t status;
+ uint8_t iak_pub_key_buf[100];
+ size_t iak_pub_key_len = 0;
+
+ status = attest_provision_export_iak_public_key(iak_pub_key_buf,
+ sizeof(iak_pub_key_buf), &iak_pub_key_len);
+
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ CHECK_TRUE(iak_pub_key_len);
+
+ /* On repeating the export, expect the same initial key value to
+ * be returned.
+ */
+ uint8_t second_iak_pub_key_buf[100];
+ size_t second_iak_pub_key_len = 0;
+
+ status = attest_provision_export_iak_public_key(second_iak_pub_key_buf,
+ sizeof(second_iak_pub_key_buf), &second_iak_pub_key_len);
+
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(iak_pub_key_len, second_iak_pub_key_len);
+ MEMCMP_EQUAL(iak_pub_key_buf, second_iak_pub_key_buf, iak_pub_key_len);
+
+ /* Attempting to import an IAK should be forbidden because one
+ * has already been self-generated.
+ */
+ status = attest_provision_import_iak(ref_iak_priv_key, sizeof(ref_iak_priv_key));
+
+ LONGS_EQUAL(PSA_ERROR_NOT_PERMITTED, status);
+}
+
+TEST(AttestationProvisioningTests, provisionedIak)
+{
+ /* Verify that the provisioning flow where an IAK is generated externally
+ * and imported during manufacture.
+ */
+ psa_status_t status;
+
+ status = attest_provision_import_iak(ref_iak_priv_key, sizeof(ref_iak_priv_key));
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ /* Attempting to import again should be forbidden */
+ status = attest_provision_import_iak(ref_iak_priv_key, sizeof(ref_iak_priv_key));
+ LONGS_EQUAL(PSA_ERROR_NOT_PERMITTED, status);
+
+ /* Check that the IAK public key can be exported */
+ uint8_t iak_pub_key_buf[100];
+ size_t iak_pub_key_len = 0;
+
+ status = attest_provision_export_iak_public_key(iak_pub_key_buf,
+ sizeof(iak_pub_key_buf), &iak_pub_key_len);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+}
diff --git a/components/service/attestation/test/service/component.cmake b/components/service/attestation/test/service/component.cmake
index a07e402..20dcfef 100644
--- a/components/service/attestation/test/service/component.cmake
+++ b/components/service/attestation/test/service/component.cmake
@@ -10,4 +10,5 @@ endif()
target_sources(${TGT} PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/attestation_service_tests.cpp"
+ "${CMAKE_CURRENT_LIST_DIR}/attestation_provisioning_tests.cpp"
)
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index bb52dd5..a4ee13e 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -53,6 +53,7 @@ add_components(
"components/service/attestation/provider"
"components/service/attestation/provider/serializer/packed-c"
"components/service/attestation/client/psa"
+ "components/service/attestation/client/provision"
"components/service/attestation/test/common"
"components/service/attestation/test/component"
"components/service/attestation/test/service"
diff --git a/deployments/ts-service-test/linux-pc/CMakeLists.txt b/deployments/ts-service-test/linux-pc/CMakeLists.txt
index ac4c237..798b8cb 100644
--- a/deployments/ts-service-test/linux-pc/CMakeLists.txt
+++ b/deployments/ts-service-test/linux-pc/CMakeLists.txt
@@ -82,6 +82,7 @@ add_components(
"components/service/test_runner/test/service"
"components/service/attestation/include"
"components/service/attestation/client/psa"
+ "components/service/attestation/client/provision"
"components/service/attestation/test/service"
)
diff --git a/protocols/service/attestation/packed-c/export_iak_public_key.h b/protocols/service/attestation/packed-c/export_iak_public_key.h
new file mode 100644
index 0000000..4ae553d
--- /dev/null
+++ b/protocols/service/attestation/packed-c/export_iak_public_key.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TS_ATTESTATION_EXPORT_IAK_PUBLIC_KEY_H
+#define TS_ATTESTATION_EXPORT_IAK_PUBLIC_KEY_H
+
+/**
+ * Parameter definitions for the EXPORT_IAK_PUBLIC_KEY operation.
+ *
+ * Can be used during device provisioning to retrieve an
+ * imported or generated IAK public key. The IAK public key
+ * may be used by a verifier as the identity for the device.
+ * This operation supports the provisioning flow where the
+ * IAK public key is read and stored in a central database.
+ * Note that exporting the IAK public key from a device that
+ * doesn't hold an IAK will trigger generation of a fresh
+ * IAK using the device's TRNG.
+ */
+
+/* Variable length output parameter tags */
+enum
+{
+ /* TLV tag to identify the IAK public key data parameter
+ */
+ TS_ATTESTATION_EXPORT_IAK_PUBLIC_KEY_OUT_TAG_DATA = 1
+};
+
+#endif /* TS_ATTESTATION_EXPORT_IAK_PUBLIC_KEY_H */
diff --git a/protocols/service/attestation/packed-c/get_token.h b/protocols/service/attestation/packed-c/get_token.h
index e02ed1e..bf16adc 100644
--- a/protocols/service/attestation/packed-c/get_token.h
+++ b/protocols/service/attestation/packed-c/get_token.h
@@ -6,6 +6,13 @@
#ifndef TS_ATTESTATION_GET_TOKEN_H
#define TS_ATTESTATION_GET_TOKEN_H
+/**
+ * Parameter definitions for the GET_TOKEN operation.
+ *
+ * Fetches a signed attestation token to allow device
+ * state to be remotely verified.
+ */
+
/* Variable length input parameter tags */
enum
{
diff --git a/protocols/service/attestation/packed-c/get_token_size.h b/protocols/service/attestation/packed-c/get_token_size.h
index c342541..c5716da 100644
--- a/protocols/service/attestation/packed-c/get_token_size.h
+++ b/protocols/service/attestation/packed-c/get_token_size.h
@@ -8,6 +8,13 @@
#include <stdint.h>
+/**
+ * Parameter definitions for the GET_TOKEN_SIZE operation.
+ *
+ * Returns the expected size of an attestation token when
+ * a challenge of the specified length is presented.
+ */
+
/* Mandatory fixed sized input parameters */
struct __attribute__ ((__packed__)) ts_attestation_get_token_size_in
{
diff --git a/protocols/service/attestation/packed-c/import_iak.h b/protocols/service/attestation/packed-c/import_iak.h
new file mode 100644
index 0000000..0fafd66
--- /dev/null
+++ b/protocols/service/attestation/packed-c/import_iak.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TS_ATTESTATION_IMPORT_IAK_H
+#define TS_ATTESTATION_IMPORT_IAK_H
+
+/**
+ * Parameter definitions for the IMPORT_IAK operation.
+ *
+ * Used during device provisioning to load an externally
+ * generated IAK key-pair into a device. An attempt to
+ * import an IAK when an IAK already exists will be rejected.
+ */
+
+/* Variable length input parameter tags */
+enum
+{
+ /* TLV tag to identify the IAK key-pair data parameter
+ */
+ TS_ATTESTATION_IMPORT_IAK_IN_TAG_DATA = 1,
+};
+
+#endif /* TS_ATTESTATION_IMPORT_IAK_H */
diff --git a/protocols/service/attestation/packed-c/opcodes.h b/protocols/service/attestation/packed-c/opcodes.h
index 62a7617..3484e1b 100644
--- a/protocols/service/attestation/packed-c/opcodes.h
+++ b/protocols/service/attestation/packed-c/opcodes.h
@@ -9,8 +9,10 @@
/* C/C++ definition of attestation service opcodes
*/
-#define TS_ATTESTATION_OPCODE_NOP (0x0000)
+
#define TS_ATTESTATION_OPCODE_GET_TOKEN (0x0001)
#define TS_ATTESTATION_OPCODE_GET_TOKEN_SIZE (0x0002)
+#define TS_ATTESTATION_OPCODE_EXPORT_IAK_PUBLIC_KEY (0x0003)
+#define TS_ATTESTATION_OPCODE_IMPORT_IAK (0x0004)
#endif /* TS_ATTESTATION_OPCODES_H */