diff options
author | Julian Hall <julian.hall@arm.com> | 2021-05-17 16:34:48 +0100 |
---|---|---|
committer | Gyorgy Szing <Gyorgy.Szing@arm.com> | 2021-07-05 12:43:51 +0200 |
commit | 482fd2fa697c8e3695a8b33e85de55fa33c2fd8e (patch) | |
tree | 7a393e1249b8b0ad1375d157235a6be66f32f88d | |
parent | 700aa36408ceec03311bd7aa097c93d220371304 (diff) | |
download | trusted-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
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 000000000..9209cc1ba --- /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 000000000..73ddbf72e --- /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 000000000..3f02418b2 --- /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 000000000..d8355f0a2 --- /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 a4d8ec0d9..783d08fd9 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 eaf46fcd9..f61619a39 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 1e1271761..460a6d065 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 5334cba5c..1c05ab07e 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 c2e5d56f3..65e76f304 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 000000000..de447cf61 --- /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 a07e402be..20dcfefd6 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 bb52dd5c1..a4ee13ed7 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 ac4c237de..798b8cbe4 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 000000000..4ae553d9b --- /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 e02ed1ed3..bf16adc77 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 c34254181..c5716da31 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 000000000..0fafd668d --- /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 62a7617e6..3484e1bb5 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 */ |