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
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 @@
     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 @@
     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 @@
 {
     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 @@
 
     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 @@
 /**
  * \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_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 @@
 
     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 @@
 
     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 @@
     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 @@
         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 @@
 
 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 @@
 		"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 @@
 		"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 */