leo hendrickson / Mbed OS example-Ethernet-mbed-Cloud-connect
Revision:
0:8f0bb79ddd48
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/simple-mbed-cloud-client/mbed-cloud-client/source/ConnectorClient.cpp	Tue May 04 08:55:12 2021 +0000
@@ -0,0 +1,1397 @@
+// ----------------------------------------------------------------------------
+// Copyright 2016-2017 ARM Ltd.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------
+
+// fixup the compilation on ARMCC for PRId32
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include "include/ConnectorClient.h"
+#include "include/CloudClientStorage.h"
+#include "include/CertificateParser.h"
+
+#ifndef MBED_CLIENT_DISABLE_EST_FEATURE
+#include "include/EstClient.h"
+#endif // !MBED_CLIENT_DISABLE_EST_FEATURE
+
+#include "MbedCloudClient.h"
+#include "mbed-client/m2mconstants.h"
+#include "mbed-client/m2minterfacefactory.h"
+#include "mbed-client/m2mdevice.h"
+#include "mbed-client/m2mconstants.h"
+#include "mbed-trace/mbed_trace.h"
+#include "factory_configurator_client.h"
+#include "key_config_manager.h"
+#include "mbed-client/uriqueryparser.h"
+#include "randLIB.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "ns_hal_init.h"
+
+#ifdef MBED_CONF_MBED_CLIENT_EVENT_LOOP_SIZE
+#define MBED_CLIENT_EVENT_LOOP_SIZE MBED_CONF_MBED_CLIENT_EVENT_LOOP_SIZE
+#else
+#define MBED_CLIENT_EVENT_LOOP_SIZE 1024
+#endif
+
+#define TRACE_GROUP "mClt"
+
+#define INTERNAL_ENDPOINT_PARAM     "&iep="
+#define DEFAULT_ENDPOINT            "endpoint"
+#define INTERFACE_ERROR             "Client interface is not created. Restart"
+#define CREDENTIAL_ERROR            "Failed to read credentials from storage"
+#define DEVICE_NOT_PROVISIONED      "Device not provisioned"
+#define CONNECTOR_ERROR_NO_MEMORY   "Not enough memory to store LWM2M credentials"
+#define CONNECTOR_BOOTSTRAP_AGAIN   "Re-bootstrapping"
+
+#ifndef MBED_CLIENT_DISABLE_EST_FEATURE
+#define ERROR_EST_ENROLLMENT_REQUEST_FAILED   "EST enrollment request failed"
+#define LWM2M_CSR_SUBJECT_FORMAT              "L=%s,OU=%s,CN=%s"
+#endif // !MBED_CLIENT_DISABLE_EST_FEATURE
+
+#define MAX_REBOOTSTRAP_TIMEOUT 21600 // 6 hours
+
+// XXX: nothing here yet
+class EventData {
+
+};
+
+static int read_callback_helper(const char *key, void *buffer, size_t *buffer_len)
+{
+    size_t cert_size = 0;
+    if (strcmp(key, g_fcc_lwm2m_device_private_key_name) == 0 ||
+        strcmp(key, g_fcc_bootstrap_device_private_key_name) == 0) {
+        if (ccs_item_size(key, buffer_len, CCS_PRIVATE_KEY_ITEM) != CCS_STATUS_SUCCESS) {
+            *buffer_len = 0;
+            return CCS_STATUS_ERROR;
+        }
+
+        if (ccs_get_item(key, (uint8_t*)buffer, *buffer_len, &cert_size, CCS_PRIVATE_KEY_ITEM) != CCS_STATUS_SUCCESS) {
+            *buffer_len = 0;
+            return CCS_STATUS_ERROR;
+        }
+    } else {
+        if (ccs_item_size(key, buffer_len, CCS_CERTIFICATE_ITEM) != CCS_STATUS_SUCCESS) {
+            *buffer_len = 0;
+            return CCS_STATUS_ERROR;
+        }
+
+        if (ccs_get_item(key, (uint8_t*)buffer, *buffer_len, &cert_size, CCS_CERTIFICATE_ITEM) != CCS_STATUS_SUCCESS) {
+            *buffer_len = 0;
+            return CCS_STATUS_ERROR;
+        }
+    }
+
+    *buffer_len = cert_size;
+
+    return CCS_STATUS_SUCCESS;
+}
+
+static bool write_security_object_data_to_kcm(const M2MResourceBase& resource, const uint8_t *buffer, const size_t buffer_size, void */*client_args*/)
+{
+    ccs_status_e status = CCS_STATUS_ERROR;
+    uint32_t resource_id = resource.name_id();
+    uint16_t object_instance_id = resource.object_instance_id();
+
+    switch (resource_id) {
+        case M2MSecurity::PublicKey:
+            if (object_instance_id == M2MSecurity::Bootstrap) {
+                ccs_delete_item(g_fcc_bootstrap_device_certificate_name, CCS_CERTIFICATE_ITEM);
+                status = ccs_set_item(g_fcc_bootstrap_device_certificate_name, buffer, buffer_size, CCS_CERTIFICATE_ITEM);
+            } else {
+                ccs_delete_item(g_fcc_lwm2m_device_certificate_name, CCS_CERTIFICATE_ITEM);
+                status = ccs_set_item(g_fcc_lwm2m_device_certificate_name, buffer, buffer_size, CCS_CERTIFICATE_ITEM);
+            }
+            break;
+
+        case M2MSecurity::ServerPublicKey:
+            if (object_instance_id == M2MSecurity::Bootstrap) {
+                ccs_delete_item(g_fcc_bootstrap_server_ca_certificate_name, CCS_CERTIFICATE_ITEM);
+                status = ccs_set_item(g_fcc_bootstrap_server_ca_certificate_name, buffer, buffer_size, CCS_CERTIFICATE_ITEM);
+            } else {
+                ccs_delete_item(g_fcc_lwm2m_server_ca_certificate_name, CCS_CERTIFICATE_ITEM);
+                status = ccs_set_item(g_fcc_lwm2m_server_ca_certificate_name, buffer, buffer_size, CCS_CERTIFICATE_ITEM);
+            }
+            break;
+
+        case M2MSecurity::Secretkey:
+            if (object_instance_id == M2MSecurity::Bootstrap) {
+                ccs_delete_item(g_fcc_bootstrap_device_private_key_name, CCS_PRIVATE_KEY_ITEM);
+                status = ccs_set_item(g_fcc_bootstrap_device_private_key_name, buffer, buffer_size, CCS_PRIVATE_KEY_ITEM);
+            } else {
+                ccs_delete_item(g_fcc_lwm2m_device_private_key_name, CCS_PRIVATE_KEY_ITEM);
+                status = ccs_set_item(g_fcc_lwm2m_device_private_key_name, buffer, buffer_size, CCS_PRIVATE_KEY_ITEM);
+            }
+            break;
+
+        default:
+            break;
+    }
+
+    return (status == CCS_STATUS_SUCCESS) ? true : false;
+}
+
+static int read_security_object_data_from_kcm(const M2MResourceBase& resource, void *buffer, size_t *buffer_len, void */*client_args*/)
+{
+    uint32_t resource_id = resource.name_id();
+    uint16_t object_instance_id = resource.object_instance_id();
+    switch (resource_id) {
+        case M2MSecurity::PublicKey:
+            if (object_instance_id == M2MSecurity::Bootstrap) {
+                return read_callback_helper(g_fcc_bootstrap_device_certificate_name, buffer, buffer_len);
+            } else {
+                return read_callback_helper(g_fcc_lwm2m_device_certificate_name, buffer, buffer_len);
+            }
+
+        case M2MSecurity::ServerPublicKey:
+            if (object_instance_id == M2MSecurity::Bootstrap) {
+                return read_callback_helper(g_fcc_bootstrap_server_ca_certificate_name, buffer, buffer_len);
+            } else {
+                return read_callback_helper(g_fcc_lwm2m_server_ca_certificate_name, buffer, buffer_len);
+            }
+
+        case M2MSecurity::Secretkey:
+            if (object_instance_id == M2MSecurity::Bootstrap) {
+                return read_callback_helper(g_fcc_bootstrap_device_private_key_name, buffer, buffer_len);
+            } else {
+                return read_callback_helper(g_fcc_lwm2m_device_private_key_name, buffer, buffer_len);
+            }
+
+        default:
+            break;
+    }
+
+    return CCS_STATUS_ERROR;
+}
+
+static int open_certificate_chain_callback(const M2MResourceBase& resource, void */*buffer*/, size_t *chain_size, void *client_args)
+{
+    void *handle = NULL;
+    uint16_t object_instance_id = resource.object_instance_id();
+    ConnectorClient *client = (ConnectorClient*)client_args;
+    if (object_instance_id == M2MSecurity::Bootstrap) {
+        handle = ccs_open_certificate_chain(g_fcc_bootstrap_device_certificate_name, chain_size);
+        client->set_certificate_chain_handle(handle);
+    } else {
+        handle = ccs_open_certificate_chain(g_fcc_lwm2m_device_certificate_name, chain_size);
+        client->set_certificate_chain_handle(handle);
+    }
+
+    return (handle) ? CCS_STATUS_SUCCESS : CCS_STATUS_ERROR;
+}
+
+static int read_certificate_chain_callback(const M2MResourceBase& /*resource*/, void *buffer, size_t *buffer_len, void *client_args)
+{
+    ConnectorClient *client = (ConnectorClient*) client_args;
+    ccs_status_e status = CCS_STATUS_ERROR;
+    if (client->certificate_chain_handle()) {
+        status = ccs_get_next_cert_chain(client->certificate_chain_handle(), buffer, buffer_len);
+    }
+
+    return status;
+}
+
+static int close_certificate_chain_callback(const M2MResourceBase& /*resource*/, void */*buffer*/, size_t *, void *client_args)
+{
+    ccs_status_e status = CCS_STATUS_ERROR;
+    ConnectorClient *client = (ConnectorClient*) client_args;
+    if (client->certificate_chain_handle()) {
+        status = ccs_close_certificate_chain(client->certificate_chain_handle());
+        client->set_certificate_chain_handle(NULL);
+    }
+    return status;
+}
+
+ConnectorClient::ConnectorClient(ConnectorClientCallback* callback)
+: _callback(callback),
+  _current_state(State_Bootstrap_Start),
+  _event_generated(false), _state_engine_running(false),
+  _setup_complete(false),
+  _interface(NULL), _security(NULL),
+  _endpoint_info(M2MSecurity::Certificate), _client_objs(NULL),
+  _rebootstrap_timer(*this), _bootstrap_security_instance(1),
+  _lwm2m_security_instance(0), _certificate_chain_handle(NULL)
+#ifndef MBED_CLIENT_DISABLE_EST_FEATURE
+  ,_est_client(*this)
+#endif // !MBED_CLIENT_DISABLE_EST_FEATURE
+
+{
+    assert(_callback != NULL);
+
+    _rebootstrap_time = randLIB_get_random_in_range(1, 10);
+}
+
+
+ConnectorClient::~ConnectorClient()
+{
+    uninitialize_storage();
+    M2MDevice::delete_instance();
+    M2MSecurity::delete_instance();
+    delete _interface;
+}
+
+bool ConnectorClient::setup()
+{
+    // the setup() may be called again after connection has been closed so let's avoid leaks
+    if (_setup_complete == false) {
+
+        // The ns_hal_init() needs to be called by someone before create_interface(),
+        // as it will also initialize the tasklet.
+        ns_hal_init(NULL, MBED_CLIENT_EVENT_LOOP_SIZE, NULL, NULL);
+
+        // Create the lwm2m server security object we need always
+        M2MSecurity *security = M2MInterfaceFactory::create_security(M2MSecurity::M2MServer);
+        M2MInterface *interface = M2MInterfaceFactory::create_interface(*this,
+                                                          DEFAULT_ENDPOINT,                     // endpoint name string
+                                                          MBED_CLOUD_CLIENT_ENDPOINT_TYPE,      // endpoint type string
+                                                          MBED_CLOUD_CLIENT_LIFETIME,           // lifetime
+                                                          MBED_CLOUD_CLIENT_LISTEN_PORT,        // listen port
+                                                          _endpoint_info.account_id,            // domain string
+                                                          transport_mode(),                     // binding mode
+                                                          M2MInterface::LwIP_IPv4);             // network stack
+
+        if ((security == NULL) || (interface == NULL)) {
+            M2MSecurity::delete_instance();
+            delete interface;
+        } else {
+            if (initialize_storage() == CCS_STATUS_SUCCESS) {
+                _security = security;
+                _interface = interface;
+                _setup_complete = true;
+            }
+        }
+    }
+
+    return _setup_complete;
+}
+
+#ifndef MBED_CLIENT_DISABLE_EST_FEATURE
+const EstClient &ConnectorClient::est_client()
+{
+    return _est_client;
+}
+#endif // !MBED_CLIENT_DISABLE_EST_FEATURE
+
+void ConnectorClient::start_bootstrap()
+{
+    tr_debug("ConnectorClient::start_bootstrap()");
+
+    assert(_callback != NULL);
+    assert(_setup_complete);
+
+    init_security_object();
+    // Stop rebootstrap timer if it was running
+    _rebootstrap_timer.stop_timer();
+
+    if (create_bootstrap_object()) {
+        _interface->update_endpoint(_endpoint_info.endpoint_name);
+        _interface->update_domain(_endpoint_info.account_id);
+        internal_event(State_Bootstrap_Start);
+    } else {
+        tr_error("ConnectorClient::start_bootstrap() - bootstrap object fail");
+    }
+    state_engine();
+}
+
+void ConnectorClient::start_registration(M2MBaseList* client_objs)
+{
+    tr_debug("ConnectorClient::start_registration()");
+
+    assert(_callback != NULL);
+    assert(_setup_complete);
+
+    init_security_object();
+    _client_objs = client_objs;
+
+    // XXX: actually this call should be external_event() to match the pattern used in other m2m classes
+    if (create_register_object()) {
+        if(_security->get_security_instance_id(M2MSecurity::M2MServer) >= 0) {
+            _interface->update_endpoint(_endpoint_info.endpoint_name);
+            _interface->update_domain(_endpoint_info.account_id);
+            internal_event(State_Registration_Start);
+        } else {
+            tr_error("ConnectorClient::start_registration(): failed to create objs");
+            _callback->connector_error(M2MInterface::InvalidParameters, INTERFACE_ERROR);
+        }
+    } else {
+        tr_error("ConnectorClient::start_registration - failed to read credentials");
+        _callback->connector_error((M2MInterface::Error)MbedCloudClient::ConnectorFailedToReadCredentials, CREDENTIAL_ERROR);
+    }
+    state_engine();
+}
+
+M2MInterface * ConnectorClient::m2m_interface()
+{
+    return _interface;
+}
+
+void ConnectorClient::update_registration()
+{
+    if(_interface && _security && _security->get_security_instance_id(M2MSecurity::M2MServer) >= 0) {
+        if (_client_objs != NULL) {
+            _interface->update_registration(_security, *_client_objs);
+        }
+        else {
+            _interface->update_registration(_security);
+        }
+    }
+}
+
+// generates an internal event. called from within a state
+// function to transition to a new state
+void ConnectorClient::internal_event(StartupSubStateRegistration new_state)
+{
+    tr_debug("ConnectorClient::internal_event: state: %d -> %d", _current_state, new_state);
+    _event_generated = true;
+    _current_state = new_state;
+
+    // Avoid recursive chain which eats too much of stack
+    if (!_state_engine_running) {
+        state_engine();
+    }
+}
+
+// the state engine executes the state machine states
+void ConnectorClient::state_engine(void)
+{
+    tr_debug("ConnectorClient::state_engine");
+
+    // this simple flagging gets rid of recursive calls to this method
+    _state_engine_running = true;
+
+    // while events are being generated keep executing states
+    while (_event_generated) {
+        _event_generated = false;  // event used up, reset flag
+
+        state_function(_current_state);
+    }
+
+    _state_engine_running = false;
+}
+
+void ConnectorClient::state_function(StartupSubStateRegistration current_state)
+{
+    switch (current_state) {
+        case State_Bootstrap_Start:
+            state_bootstrap_start();
+            break;
+        case State_Bootstrap_Started:
+            state_bootstrap_started();
+            break;
+        case State_Bootstrap_Success:
+            state_bootstrap_success();
+            break;
+        case State_Bootstrap_Failure:
+            state_bootstrap_failure();
+            break;
+#ifndef MBED_CLIENT_DISABLE_EST_FEATURE
+        case State_EST_Start:
+            state_est_start();
+            break;
+        case State_EST_Started:
+            state_est_started();
+            break;
+        case State_EST_Success:
+            state_est_success();
+            break;
+        case State_EST_Failure:
+            state_est_failure();
+            break;
+#endif // !MBED_CLIENT_DISABLE_EST_FEATURE
+        case State_Registration_Start:
+            state_registration_start();
+            break;
+        case State_Registration_Started:
+            state_registration_started();
+            break;
+        case State_Registration_Success:
+            state_registration_success();
+            break;
+        case State_Registration_Failure:
+            state_registration_failure();
+            break;
+        case State_Unregistered:
+            state_unregistered();
+            break;
+        default:
+            break;
+    }
+}
+
+/*
+*  Creates register server object with mbed device server address and other parameters
+*  required for client to connect to mbed device server.
+*/
+bool ConnectorClient::create_register_object()
+{
+    tr_debug("ConnectorClient::create_register_object()");
+    int32_t m2m_id = _security->get_security_instance_id(M2MSecurity::M2MServer);
+    if (m2m_id == -1) {
+        init_security_object();
+    }
+
+    m2m_id = _security->get_security_instance_id(M2MSecurity::M2MServer);
+    if (m2m_id == -1) {
+        tr_error("ConnectorClient::create_register_object() - failed to read security object!");
+        return false;
+    }
+
+    // Allocate scratch buffer, this will be used to copy parameters from storage to security object
+    const int max_size = MAX_CERTIFICATE_SIZE;
+    uint8_t *buffer = (uint8_t*)malloc(max_size);
+    size_t real_size = 0;
+    bool success = false;
+
+    if (_security->set_resource_value(M2MSecurity::BootstrapServer, M2MSecurity::M2MServer, m2m_id)) {
+        success = true;
+    }
+
+    // Add ResourceID's and values to the security ObjectID/ObjectInstance
+    if (success) {
+        success = false;
+        if (_security->set_resource_value(M2MSecurity::SecurityMode, _endpoint_info.mode, m2m_id)) {
+            success = true;
+        }
+    }
+
+    if (success && buffer == NULL) {
+        success = false;
+    }
+
+    // Endpoint
+    if (success) {
+        success = false;
+        char device_id[64];
+
+        size_t cert_size = max_size;
+        uint8_t certificate[MAX_CERTIFICATE_SIZE];
+        uint8_t *certificate_ptr = (uint8_t*)&certificate;
+
+        // TODO! Update to use chain api
+        if (_security->resource_value_buffer(M2MSecurity::PublicKey, certificate_ptr, m2m_id, &cert_size) == 0) {
+            real_size = cert_size;
+            if (extract_field_from_certificate((uint8_t*)certificate, real_size, "CN", device_id)) {
+                tr_info("ConnectorClient::create_register_object - CN - endpoint_name : %s", device_id);
+                _endpoint_info.endpoint_name = String(device_id);
+                success = true;
+            } else {
+                tr_error("KEY_ENDPOINT_NAME failed.");
+            }
+        }
+    }
+
+    // Connector URL
+    if (success) {
+        success = false;
+        if (ccs_get_item(g_fcc_lwm2m_server_uri_name, buffer, max_size, &real_size, CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+            tr_info("ConnectorClient::create_register_object - M2MServerUri %.*s", (int)real_size, buffer);
+            if (_security->set_resource_value(M2MSecurity::M2MServerUri, buffer, (uint32_t)real_size, m2m_id)) {
+                success = true;
+            }
+#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION
+            _endpoint_info.lwm2m_server_uri = _security->resource_value_string(M2MSecurity::M2MServerUri, m2m_id);
+#endif
+        }
+        else {
+            tr_error("KEY_CONNECTOR_URL failed.");
+        }
+    }
+
+    // Try to get internal endpoint name
+    if (success) {
+        if (ccs_get_item(KEY_INTERNAL_ENDPOINT, buffer, max_size, &real_size, CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+            _endpoint_info.internal_endpoint_name = String((const char*)buffer, real_size);
+            tr_info("ConnectorClient::create_register_object - internal endpoint name : %s", _endpoint_info.internal_endpoint_name.c_str());
+        }
+        else {
+            tr_debug("KEY_INTERNAL_ENDPOINT failed.");
+        }
+    }
+
+    if (success) {
+        success = false;
+        if (ccs_get_item(KEY_ACCOUNT_ID, buffer, max_size, &real_size, CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+            tr_info("ConnectorClient::create_register_object - AccountId %.*s", (int)real_size, buffer);
+            _endpoint_info.account_id = String((const char*)buffer, real_size);
+            success = true;
+        } else {
+            if (ccs_get_item(g_fcc_lwm2m_server_uri_name, buffer, max_size, &real_size, CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+                String address((const char*)buffer, real_size);
+                if (address.size() > 0) {
+                    const char *aid = NULL;
+                    const int aid_size = parse_query_parameter_value_from_uri((const char*)address.c_str(), QUERY_PARAM_AID, &aid);
+                    if (aid_size > 0) {
+                        _endpoint_info.account_id.clear();
+                        _endpoint_info.account_id.append_raw(aid, aid_size);
+                        if (ccs_set_item(KEY_ACCOUNT_ID,
+                                (const uint8_t*)_endpoint_info.account_id.c_str(),
+                                (size_t)_endpoint_info.account_id.size(), CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+                            success = true;
+                            tr_info("ConnectorClient::create_register_object - aid from uri %s", _endpoint_info.account_id.c_str());
+                        } else {
+                            tr_error("ConnectorClient::create_register_object - failed to store aid");
+                        }
+
+                    }
+                }
+            }
+        }
+    }
+
+    free(buffer);
+
+    return success;
+}
+
+/*
+*  Creates bootstrap server object with bootstrap server address and other parameters
+*  required for connecting to mbed Cloud bootstrap server.
+*/
+bool ConnectorClient::create_bootstrap_object()
+{
+    tr_debug("ConnectorClient::create_bootstrap_object");
+    bool success = false;
+
+    // Check if bootstrap credentials are already stored in KCM
+    if (bootstrap_credentials_stored_in_kcm() && _security) {
+        int32_t bs_id = _security->get_security_instance_id(M2MSecurity::Bootstrap);
+        if (_security->set_resource_value(M2MSecurity::SecurityMode, M2MSecurity::Certificate, bs_id)) {
+            success = true;
+        }
+
+        tr_info("ConnectorClient::create_bootstrap_object - bs_id = %" PRId32, bs_id);
+        tr_info("ConnectorClient::create_bootstrap_object - use credentials from storage");
+
+        // Allocate scratch buffer, this will be used to copy parameters from storage to security object
+        size_t real_size = 0;
+        const int max_size = MAX_CERTIFICATE_SIZE;
+        uint8_t *buffer = NULL;
+        if (success) {
+            success = false;
+            buffer = (uint8_t*)malloc(max_size);
+            if (buffer != NULL) {
+                success = true;
+            }
+        }
+
+        // Read internal endpoint name if it exists, we need to append
+        // it to bootstrap uri if device already bootstrapped
+        uint8_t *iep = NULL;
+        if (success && ccs_get_string_item(KEY_INTERNAL_ENDPOINT, buffer, max_size, CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+            iep = (uint8_t*)malloc(strlen((const char*)buffer) + strlen(INTERNAL_ENDPOINT_PARAM) + 1);
+            if (iep != NULL) {
+                strcpy((char*)iep, INTERNAL_ENDPOINT_PARAM);
+                strcat((char*)iep, (const char*)buffer);
+                tr_info("ConnectorClient::create_bootstrap_object - iep: %s", buffer);
+            }
+            //TODO: Should handle error if iep exists but allocation fails?
+        }
+
+        // Bootstrap URI
+        if (success) {
+            success = false;
+            if (ccs_get_string_item(g_fcc_bootstrap_server_uri_name, buffer, max_size, CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+                real_size = strlen((const char*)buffer);
+
+                // Append iep if we 1. have it 2. it doesn't already exist in uri 3. it fits
+                if (iep &&
+                    strstr((const char*)buffer, (const char*)iep) == NULL &&
+                    (real_size + strlen((const char*)iep) + 1) <= max_size) {
+                    strcat((char*)buffer, (const char*)iep);
+                    real_size += strlen((const char*)iep) + 1;
+                }
+
+                tr_info("ConnectorClient::create_bootstrap_object - M2MServerUri %.*s", (int)real_size, buffer);
+                if (_security->set_resource_value(M2MSecurity::M2MServerUri, buffer, real_size, bs_id)) {
+                    success = true;
+                }
+            }
+        }
+
+        free(iep);
+
+        // Endpoint
+        if (success) {
+            success = false;
+            if (ccs_get_item(g_fcc_endpoint_parameter_name, buffer, max_size, &real_size, CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+                success = true;
+                _endpoint_info.endpoint_name = String((const char*)buffer, real_size);
+                tr_info("ConnectorClient::create_bootstrap_object - Endpoint %s", _endpoint_info.endpoint_name.c_str());
+            }
+        }
+
+        // Account ID, not mandatory
+        if (success) {
+            if (ccs_get_item(KEY_ACCOUNT_ID, buffer, max_size, &real_size, CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+                _endpoint_info.account_id = String((const char*)buffer, real_size);
+                tr_info("ConnectorClient::create_bootstrap_object - AccountId %s", _endpoint_info.account_id.c_str());
+            }
+        }
+
+        free(buffer);
+
+        if (!success) {
+            tr_error("ConnectorClient::create_bootstrap_object - Failed to read credentials");
+            _callback->connector_error((M2MInterface::Error)MbedCloudClient::ConnectorFailedToReadCredentials,CREDENTIAL_ERROR);
+            _security->remove_object_instance(bs_id);
+        }
+    } else {
+        success = true;
+        tr_info("ConnectorClient::create_bootstrap_object - bootstrap object already done");
+    }
+
+
+    return success;
+}
+
+void ConnectorClient::state_bootstrap_start()
+{
+    tr_info("ConnectorClient::state_bootstrap_start()");
+    assert(_interface != NULL);
+    assert(_security != NULL);
+
+    _interface->bootstrap(_security);
+
+    internal_event(State_Bootstrap_Started);
+}
+
+void ConnectorClient::state_bootstrap_started()
+{
+    // this state may be useful only for verifying the callbacks?
+}
+
+void ConnectorClient::state_bootstrap_success()
+{
+    assert(_callback != NULL);
+    // Parse internal endpoint name from mDS cert
+    _callback->registration_process_result(State_Bootstrap_Success);
+}
+
+void ConnectorClient::state_bootstrap_failure()
+{
+    assert(_callback != NULL);
+    // maybe some additional canceling and/or leanup is needed here?
+    _callback->registration_process_result(State_Bootstrap_Failure);
+}
+
+#ifndef MBED_CLIENT_DISABLE_EST_FEATURE
+void ConnectorClient::state_est_start()
+{
+    // - Generate CSR from data during bootstrap phase
+    // - Call EST enrollment API from InterfaceImpl
+
+    // Update the internal endpoint name and account id to endpoint info structure
+    // as we get those during bootstrap phase
+    _endpoint_info.internal_endpoint_name = _interface->internal_endpoint_name();
+
+    int32_t m2m_id = _security->get_security_instance_id(M2MSecurity::M2MServer);
+    int32_t sec_mode = M2MSecurity::SecurityNotSet;
+    if (m2m_id >= 0) {
+        sec_mode = _security->resource_value_int(M2MSecurity::SecurityMode, m2m_id);
+
+        // We need to parse account id from lwm2m server uri query if it is not yet
+        // set in endpoint info structure
+        if (_endpoint_info.account_id.length() <= 0) {
+            String address = _security->resource_value_string(M2MSecurity::M2MServerUri, m2m_id);
+            tr_debug("ConnectorClient::state_est_start - address: %s", address.c_str());
+            if (address.size() > 0) {
+                const char *aid = NULL;
+                const int aid_size = parse_query_parameter_value_from_uri((const char*)address.c_str(), QUERY_PARAM_AID, &aid);
+                if (aid_size > 0) {
+                    _endpoint_info.account_id.append_raw(aid, aid_size);
+                }
+            }
+        }
+    }
+
+    tr_debug("ConnectorClient::state_est_start - security instance id: %" PRId32, m2m_id);
+    tr_debug("ConnectorClient::state_est_start - ep: %s", _endpoint_info.internal_endpoint_name.c_str());
+    tr_debug("ConnectorClient::state_est_start - iep: %s", _endpoint_info.endpoint_name.c_str());
+    tr_debug("ConnectorClient::state_est_start - aid: %s", _endpoint_info.account_id.c_str());
+
+    // Check EST required parameters are in place
+    if (m2m_id < 0 ||
+        _endpoint_info.endpoint_name.length() <= 0 ||
+        _endpoint_info.internal_endpoint_name.length() <= 0 ||
+        _endpoint_info.account_id.length() <= 0) {
+        tr_error("ConnectorClient::state_est_start - Missing parameters for EST enrollment!");
+        internal_event(State_EST_Failure);
+        return;
+    }
+
+    uint32_t is_bs_server = _security->resource_value_int(M2MSecurity::BootstrapServer, m2m_id);
+    size_t public_key_size = MAX_CERTIFICATE_SIZE;
+    size_t server_key_size = MAX_CERTIFICATE_SIZE;
+    size_t private_key_size = MAX_CERTIFICATE_SIZE;
+
+    // Temp buffer for storing CSR and certificates
+    uint8_t *buffer = (uint8_t*)malloc(MAX_CERTIFICATE_SIZE);
+    size_t real_size = 0;
+    if (buffer == NULL) {
+        tr_error("ConnectorClient::state_est_start - Allocating temp buffer failed!");
+        internal_event(State_EST_Failure);
+        return;
+    }
+    uint8_t *buffer_ptr = buffer;
+
+    // TODO! Update to use chain api
+    if (_security->resource_value_buffer(M2MSecurity::PublicKey, buffer_ptr, m2m_id, &public_key_size) != 0) {
+        public_key_size = 0;
+    }
+    if (_security->resource_value_buffer(M2MSecurity::ServerPublicKey, buffer_ptr, m2m_id, &server_key_size) != 0) {
+        server_key_size = 0;
+    }
+    if (_security->resource_value_buffer(M2MSecurity::Secretkey, buffer_ptr, m2m_id, &private_key_size) != 0) {
+        private_key_size = 0;
+    }
+
+    tr_info("est check - is bs server /0/1: %" PRIu32, is_bs_server);
+    tr_info("est check - Security Mode /0/2: %" PRIu32, sec_mode);
+    tr_info("est check - Public key size /0/3: %lu", (unsigned long)public_key_size);
+    tr_info("est check - Server Public key size /0/4: %lu", (unsigned long)server_key_size);
+    tr_info("est check - Secret key size /0/5: %lu", (unsigned long)private_key_size);
+
+    // Configure CSR params
+    kcm_csr_params_s csr_params;
+    int subject_size = snprintf(NULL, 0, LWM2M_CSR_SUBJECT_FORMAT,
+                                _endpoint_info.internal_endpoint_name.c_str(),
+                                _endpoint_info.account_id.c_str(),
+                                _endpoint_info.endpoint_name.c_str());
+    if (subject_size <= 0) {
+        tr_error("ConnectorClient::state_est_start - CSR Subject formatting failed!");
+        free(buffer);
+        internal_event(State_EST_Failure);
+        return;
+    }
+
+    // For null-terminator
+    subject_size++;
+
+    csr_params.subject = (char*)malloc(subject_size);
+    if (csr_params.subject == NULL) {
+        tr_error("ConnectorClient::state_est_start - CSR Subject formatting failed!");
+        free(buffer);
+        internal_event(State_EST_Failure);
+        return;
+    }
+
+    snprintf(csr_params.subject, subject_size, LWM2M_CSR_SUBJECT_FORMAT,
+             _endpoint_info.internal_endpoint_name.c_str(),
+             _endpoint_info.account_id.c_str(),
+             _endpoint_info.endpoint_name.c_str());
+
+    csr_params.md_type = KCM_MD_SHA256;
+    csr_params.key_usage = KCM_CSR_KU_NONE;
+    csr_params.ext_key_usage = KCM_CSR_EXT_KU_NONE;
+
+    // Delete existing keys
+    ccs_delete_item(g_fcc_lwm2m_device_certificate_name, CCS_CERTIFICATE_ITEM);
+    ccs_delete_item(g_fcc_lwm2m_device_private_key_name, CCS_PRIVATE_KEY_ITEM);
+
+    kcm_status_e status = kcm_generate_keys_and_csr(KCM_SCHEME_EC_SECP256R1,
+                                                    (const uint8_t*)g_fcc_lwm2m_device_private_key_name,
+                                                    strlen(g_fcc_lwm2m_device_private_key_name),
+                                                    NULL,
+                                                    0,
+                                                    false,
+                                                    &csr_params,
+                                                    buffer,
+                                                    MAX_CERTIFICATE_SIZE,
+                                                    &real_size,
+                                                    NULL);
+
+    free(csr_params.subject);
+
+    if (status != KCM_STATUS_SUCCESS) {
+        tr_error("ConnectorClient::state_est_start - Generating keys and csr failed!");
+        free(buffer);
+        internal_event(State_EST_Failure);
+        return;
+    }
+
+    // Update state and start the enrollment by sending the enroll request
+    internal_event(State_EST_Started);
+    est_status_e est_status = _est_client.est_request_enrollment(NULL,
+                                                                 0,
+                                                                 buffer,
+                                                                 real_size,
+                                                                 ConnectorClient::est_enrollment_result,
+                                                                 this);
+
+    if (est_status != EST_STATUS_SUCCESS) {
+        tr_error("ConnectorClient::state_est_start - EST enrollment failed with error %d", (int)est_status);
+        internal_event(State_EST_Failure);
+    }
+
+    free(buffer);
+}
+
+void ConnectorClient::state_est_started()
+{
+}
+
+void ConnectorClient::state_est_success()
+{
+    tr_info("ConnectorClient::state_est_success()");
+    _interface->finish_bootstrap();
+}
+
+void ConnectorClient::state_est_failure()
+{
+    tr_info("ConnectorClient::state_est_failure()");
+    internal_event(State_Bootstrap_Failure);
+    //Failed to store credentials, bootstrap failed
+    _callback->connector_error(M2MInterface::ESTEnrollmentFailed, ERROR_EST_ENROLLMENT_REQUEST_FAILED); // Translated to error code ConnectMemoryConnectFail
+}
+#endif /* !MBED_CLIENT_DISABLE_EST_FEATURE */
+
+void ConnectorClient::state_registration_start()
+{
+    tr_info("ConnectorClient::state_registration_start()");
+    assert(_interface != NULL);
+    assert(_security != NULL);
+    _interface->register_object(_security, *_client_objs);
+    internal_event(State_Registration_Started);
+}
+
+void ConnectorClient::state_registration_started()
+{
+    // this state may be useful only for verifying the callbacks?
+}
+
+void ConnectorClient::state_registration_success()
+{
+    assert(_callback != NULL);
+    _endpoint_info.internal_endpoint_name = _interface->internal_endpoint_name();
+
+    //The endpoint is maximum 32 character long, we put bigger buffer for future extensions
+    const int max_size = 64;
+    uint8_t *buffer = NULL;
+    buffer = (uint8_t*)malloc(max_size);
+
+    if(!buffer) {
+         _callback->registration_process_result(State_Registration_Failure);
+         return;
+    }
+    bool no_param_update = true;
+
+    if(ccs_get_string_item(KEY_INTERNAL_ENDPOINT, buffer, max_size, CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+        if (strcmp((const char*)buffer, _endpoint_info.internal_endpoint_name.c_str()) != 0) {
+            // Update is required as the stored KCM entry is different than _endpoint_info.internal_endpoint_name.
+            no_param_update = false;
+        }
+    }
+    free(buffer);
+
+    // Update INTERNAL_ENDPOINT setting only if there is no such entry or the value is not matching the
+    // _endpoint_info.internal_endpoint_name.
+    if(!no_param_update) {
+        ccs_delete_item(KEY_INTERNAL_ENDPOINT, CCS_CONFIG_ITEM);
+        ccs_set_item(KEY_INTERNAL_ENDPOINT, (const uint8_t*)_endpoint_info.internal_endpoint_name.c_str(),
+                             (size_t)_endpoint_info.internal_endpoint_name.size(),
+                             CCS_CONFIG_ITEM);
+    }
+
+    _rebootstrap_time = randLIB_get_random_in_range(1, 10);
+    _callback->registration_process_result(State_Registration_Success);
+}
+
+void ConnectorClient::state_registration_failure()
+{
+    assert(_callback != NULL);
+    // maybe some additional canceling and/or leanup is needed here?
+    _callback->registration_process_result(State_Registration_Failure);
+}
+
+void ConnectorClient::state_unregistered()
+{
+    assert(_callback != NULL);
+    _callback->registration_process_result(State_Unregistered);
+}
+
+void ConnectorClient::bootstrap_data_ready(M2MSecurity *security_object)
+{
+    tr_info("ConnectorClient::bootstrap_data_ready");
+    if(security_object) {
+        // Update bootstrap credentials (we could skip this if we knew whether they were updated)
+        // This will also update the address in case of first to claim
+        ccs_status_e status = set_bootstrap_credentials(security_object);
+        if (status != CCS_STATUS_SUCCESS) {
+            // TODO: what now?
+            tr_error("ConnectorClient::bootstrap_data_ready - could not store bootstrap credentials");
+        }
+
+        // Clear the first to claim flag if it's active
+        if (is_first_to_claim()) {
+            status = clear_first_to_claim();
+            if (status != CCS_STATUS_SUCCESS) {
+                // TODO: what now?
+                tr_error("ConnectorClient::bootstrap_data_ready - couldn't clear first to claim flag!");
+            }
+        }
+
+        // Bootstrap might delete m2mserver security object instance completely to force bootstrap
+        // with new credentials, in that case delete the stored lwm2m credentials as well and re-bootstrap
+        if (security_object->get_security_instance_id(M2MSecurity::M2MServer) == -1) {
+            bootstrap_again();
+            return;
+        }
+        // Bootstrap wrote M2MServer credentials, store them and also update first to claim status if it's configured
+        else {
+#ifndef MBED_CLIENT_DISABLE_EST_FEATURE
+            int32_t m2m_id = _security->get_security_instance_id(M2MSecurity::M2MServer);
+            if (m2m_id >= 0 &&
+                _security->resource_value_int(M2MSecurity::SecurityMode, m2m_id) == M2MSecurity::EST) {
+                // If EST is supported, continue to EST state to start EST enrollment
+                tr_info("ConnectorClient::bootstrap_data_ready() - Continue to EST enrollment");
+                internal_event(State_EST_Start);
+                return;
+            }
+#endif // MBED_CLIENT_DISABLE_EST_FEATURE
+            // Security mode was not EST, in that case just store the received credentials
+            tr_info("ConnectorClient::bootstrap_data_ready() - Storing lwm2m credentials");
+            status = set_connector_credentials(security_object);
+        }
+
+        if (status != CCS_STATUS_SUCCESS) {
+            internal_event(State_Bootstrap_Failure);
+            //Failed to store credentials, bootstrap failed
+            _callback->connector_error(M2MInterface::MemoryFail, CONNECTOR_ERROR_NO_MEMORY); // Translated to error code ConnectMemoryConnectFail
+            return;
+        } else {
+            tr_info("ConnectorClient::bootstrap_data_ready - set_credentials status %d", status);
+        }
+    }
+}
+
+void ConnectorClient::bootstrap_done(M2MSecurity *security_object)
+{
+    tr_info("ConnectorClient::bootstrap_done");
+    internal_event(State_Bootstrap_Success);
+}
+
+void ConnectorClient::object_registered(M2MSecurity *security_object, const M2MServer &server_object)
+{
+    internal_event(State_Registration_Success);
+}
+
+void ConnectorClient::object_unregistered(M2MSecurity *server_object)
+{
+    internal_event(State_Unregistered);
+}
+
+void ConnectorClient::registration_updated(M2MSecurity *security_object, const M2MServer & server_object)
+{
+    _callback->registration_process_result(State_Registration_Updated);
+}
+
+void ConnectorClient::error(M2MInterface::Error error)
+{
+    tr_error("ConnectorClient::error() - error: %d", error);
+    assert(_callback != NULL);
+
+    if (_current_state >= State_Registration_Start &&
+        use_bootstrap() &&
+        (error == M2MInterface::SecureConnectionFailed ||
+         error == M2MInterface::InvalidParameters)) {
+        tr_info("ConnectorClient::error() - Error during lwm2m registration");
+
+        // Delete the lwm2m security instance
+        int32_t id = _security->get_security_instance_id(M2MSecurity::M2MServer);
+        if (id >= 0) {
+            _security->remove_object_instance(id);
+        }
+
+        bootstrap_again();
+    }
+    else {
+        _callback->connector_error(error, _interface->error_description());
+    }
+}
+
+void ConnectorClient::value_updated(M2MBase *base, M2MBase::BaseType type)
+{
+    assert(_callback != NULL);
+    _callback->value_updated(base, type);
+}
+
+bool ConnectorClient::connector_credentials_available()
+{
+    tr_debug("ConnectorClient::connector_credentials_available");
+    if (ccs_check_item(g_fcc_lwm2m_server_uri_name, CCS_CONFIG_ITEM) != CCS_STATUS_SUCCESS) {
+        return false;
+    }
+    return true;
+}
+
+bool ConnectorClient::use_bootstrap()
+{
+    tr_debug("ConnectorClient::use_bootstrap");
+    size_t real_size = 0;
+    uint8_t data[CONFIG_BOOLEAN_ITEM_SIZE] = {0};
+    uint32_t value = 0;
+    ccs_status_e status = ccs_get_item(g_fcc_use_bootstrap_parameter_name, data, CONFIG_BOOLEAN_ITEM_SIZE, &real_size, CCS_CONFIG_ITEM);
+    if (status == CCS_STATUS_SUCCESS) {
+        memcpy(&value, data, CONFIG_BOOLEAN_ITEM_SIZE);
+        // Return true if use_bootstrap is set
+        if (value == 1) {
+            return true;
+        }
+    }
+    return false;
+}
+
+
+bool ConnectorClient::get_key(const char *key, const char *endpoint, char *&key_name)
+{
+    if(key_name) {
+        free(key_name);
+        key_name = NULL;
+    }
+
+    key_name = (char*)malloc(strlen(key)+strlen(endpoint)+1);
+    if(key_name) {
+        strcpy(key_name, key);
+        strcat(key_name, endpoint);
+        tr_debug("key %s", key_name);
+        return true;
+    }
+    return false;
+}
+
+ccs_status_e ConnectorClient::set_connector_credentials(M2MSecurity *security)
+{
+    tr_debug("ConnectorClient::set_connector_credentials");
+    ccs_status_e status = CCS_STATUS_ERROR;
+
+    int32_t m2m_id = security->get_security_instance_id(M2MSecurity::M2MServer);
+    if (m2m_id == -1) {
+        return status;
+    }
+
+    size_t buffer_size = MAX_CERTIFICATE_SIZE;
+    uint8_t public_key[MAX_CERTIFICATE_SIZE];
+    uint8_t *public_key_ptr = (uint8_t*)&public_key;
+
+    // TODO! Update to use chain api
+    if (security->resource_value_buffer(M2MSecurity::PublicKey, public_key_ptr, m2m_id, &buffer_size) != 0) {
+        return status;
+    }
+
+    char device_id[64];
+    memset(device_id, 0, 64);
+    if (extract_field_from_certificate(public_key, buffer_size, "L", device_id)) {
+        tr_info("ConnectorClient::set_connector_credentials - L internal_endpoint_name : %s", device_id);
+        _endpoint_info.internal_endpoint_name = String(device_id);
+        ccs_delete_item(KEY_INTERNAL_ENDPOINT, CCS_CONFIG_ITEM);
+        status = ccs_set_item(KEY_INTERNAL_ENDPOINT,(uint8_t*)device_id, strlen(device_id), CCS_CONFIG_ITEM);
+    }
+
+    memset(device_id, 0, 64);
+    if (extract_field_from_certificate(public_key, buffer_size, "CN", device_id)) {
+        tr_info("ConnectorClient::set_connector_credentials - CN endpoint_name : %s", device_id);
+        _endpoint_info.endpoint_name = String(device_id);
+    }
+
+    if(status == CCS_STATUS_SUCCESS) {
+        ccs_delete_item(KEY_ACCOUNT_ID, CCS_CONFIG_ITEM);
+        // AccountID optional so don't fail if unable to store
+        ccs_set_item(KEY_ACCOUNT_ID,
+                     (const uint8_t*)_endpoint_info.account_id.c_str(),
+                     (size_t)_endpoint_info.account_id.size(),
+                     CCS_CONFIG_ITEM);
+    }
+
+    if (status == CCS_STATUS_SUCCESS) {
+#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION
+        _endpoint_info.lwm2m_server_uri = security->resource_value_string(M2MSecurity::M2MServerUri, m2m_id);
+#endif
+        ccs_delete_item(g_fcc_lwm2m_server_uri_name, CCS_CONFIG_ITEM);
+        status = ccs_set_item(g_fcc_lwm2m_server_uri_name,
+                              (const uint8_t*)security->resource_value_string(M2MSecurity::M2MServerUri, m2m_id).c_str(),
+                              (size_t)security->resource_value_string(M2MSecurity::M2MServerUri, m2m_id).size(),
+                              CCS_CONFIG_ITEM);
+    }
+
+    M2MDevice *device = M2MInterfaceFactory::create_device();
+    if (status == CCS_STATUS_SUCCESS && device) {
+        String temp = "";
+        uint32_t currenttime = (uint32_t)device->resource_value_int(M2MDevice::CurrentTime, 0);
+        uint8_t data[4];
+        memcpy(data, &currenttime, 4);
+        ccs_delete_item(g_fcc_current_time_parameter_name, CCS_CONFIG_ITEM);
+        ccs_set_item(g_fcc_current_time_parameter_name, data, 4, CCS_CONFIG_ITEM);
+
+        temp = device->resource_value_string(M2MDevice::Timezone, 0);
+        if (temp.size() > 0) {
+            ccs_delete_item(g_fcc_device_time_zone_parameter_name, CCS_CONFIG_ITEM);
+            ccs_set_item(g_fcc_device_time_zone_parameter_name, (const uint8_t*)temp.c_str(), temp.size(), CCS_CONFIG_ITEM);
+        }
+
+        temp = device->resource_value_string(M2MDevice::UTCOffset, 0);
+        if (temp.size() > 0) {
+            ccs_delete_item(g_fcc_offset_from_utc_parameter_name, CCS_CONFIG_ITEM);
+            ccs_set_item(g_fcc_offset_from_utc_parameter_name, (const uint8_t*)temp.c_str(), temp.size(), CCS_CONFIG_ITEM);
+        }
+
+        status = CCS_STATUS_SUCCESS;
+    }
+    else {
+        tr_debug("No device object to store!");
+    }
+
+    return status;
+}
+
+ccs_status_e ConnectorClient::set_bootstrap_credentials(M2MSecurity *security)
+{
+    tr_debug("ConnectorClient::set_bootstrap_credentials");
+    ccs_status_e status = CCS_STATUS_ERROR;
+
+    size_t uri_size = MAX_CERTIFICATE_SIZE;
+    uint8_t bootstrap_uri[MAX_CERTIFICATE_SIZE];
+    uint8_t *bootstrap_uri_ptr = (uint8_t*)&bootstrap_uri;
+
+    int32_t bs_id = security->get_security_instance_id(M2MSecurity::Bootstrap);
+    if (bs_id == -1) {
+        return status;
+    }
+
+    String bs_uri = security->resource_value_string(M2MSecurity::M2MServerUri, bs_id);
+    if (bs_uri.size() <= 0) {
+        return status;
+    }
+
+    // Update BS uri only if it has changed.
+    // If uri is missing or some other error while reading the item --> return error.
+    if (ccs_get_string_item(g_fcc_bootstrap_server_uri_name, bootstrap_uri_ptr, uri_size, CCS_CONFIG_ITEM) == CCS_STATUS_SUCCESS) {
+        if (strcmp((const char*)bootstrap_uri_ptr, bs_uri.c_str()) != 0) {
+            tr_info("ConnectorClient::set_bootstrap_credentials - update uri from: %s to: %s", (char*)bootstrap_uri_ptr, bs_uri.c_str());
+            ccs_delete_item(g_fcc_bootstrap_server_uri_name, CCS_CONFIG_ITEM);
+            status = ccs_set_item(g_fcc_bootstrap_server_uri_name,
+                              (const uint8_t*)bs_uri.c_str(),
+                              strlen(bs_uri.c_str()),
+                              CCS_CONFIG_ITEM);
+        } else {
+            status = CCS_STATUS_SUCCESS;
+        }
+    }
+
+    return status;
+}
+
+ccs_status_e ConnectorClient::clear_first_to_claim()
+{
+    tr_debug("ConnectorClient::clear_first_to_claim");
+    return ccs_delete_item(KEY_FIRST_TO_CLAIM, CCS_CONFIG_ITEM);
+}
+
+
+const ConnectorClientEndpointInfo *ConnectorClient::endpoint_info() const
+{
+    return &_endpoint_info;
+}
+
+bool ConnectorClient::bootstrap_credentials_stored_in_kcm()
+{
+    size_t real_size = 0;
+    ccs_status_e success = ccs_item_size(g_fcc_bootstrap_server_uri_name, &real_size, CCS_CONFIG_ITEM);
+    // Return true if bootstrap uri exists in KCM
+    if ((success == CCS_STATUS_SUCCESS) && real_size > 0) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool ConnectorClient::is_first_to_claim()
+{
+    size_t real_size = 0;
+    uint8_t data[CONFIG_BOOLEAN_ITEM_SIZE] = {0};
+    uint32_t value = 0;
+    ccs_status_e status = ccs_get_item(KEY_FIRST_TO_CLAIM, data, CONFIG_BOOLEAN_ITEM_SIZE, &real_size, CCS_CONFIG_ITEM);
+    if (status == CCS_STATUS_SUCCESS) {
+        memcpy(&value, data, CONFIG_BOOLEAN_ITEM_SIZE);
+        // Return true if first to claim is set
+        if (value == 1) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void ConnectorClient::timer_expired(M2MTimerObserver::Type type)
+{
+    if (type == M2MTimerObserver::BootstrapFlowTimer) {
+        start_bootstrap();
+    }
+}
+
+#ifndef MBED_CLIENT_DISABLE_EST_FEATURE
+void ConnectorClient::est_enrollment_result(est_enrollment_result_e result,
+                                            cert_chain_context_s *cert_chain,
+                                            void *context)
+{
+    tr_debug("ConnectorClient::est_enrollment_result - %s", result == EST_ENROLLMENT_SUCCESS ? "successful" : "failed");
+    if (result == EST_ENROLLMENT_SUCCESS) {
+        tr_debug("ConnectorClient::est_enrollment_result - PublicKey size %d", cert_chain->chain_length);
+    }
+
+    ConnectorClient *cc = static_cast<ConnectorClient*>(context);
+    assert(cc);
+    assert(cc->_security != NULL);
+
+    int32_t m2m_id = cc->_security->get_security_instance_id(M2MSecurity::M2MServer);
+    StartupSubStateRegistration state = State_EST_Failure;
+
+    if (result == EST_ENROLLMENT_SUCCESS &&
+        m2m_id >= 0 &&
+        cert_chain != NULL &&
+        cert_chain->chain_length > 0)
+    {
+
+        kcm_cert_chain_handle chain_handle;
+        kcm_status_e status = kcm_cert_chain_create(&chain_handle,
+                                       (const uint8_t*)g_fcc_lwm2m_device_certificate_name,
+                                       strlen(g_fcc_lwm2m_device_certificate_name),
+                                       cert_chain->chain_length,
+                                       false);
+        tr_debug("Cert chain create %d", status);
+        if (status == KCM_STATUS_SUCCESS) {
+            cert_context_s *cert = cert_chain->certs;
+            while (cert != NULL) {
+
+                // Store certificate
+                status = kcm_cert_chain_add_next(chain_handle, cert->cert, cert->cert_length);
+                if (status != KCM_STATUS_SUCCESS) {
+                    break;
+                }
+
+                cert = cert->next;
+            }
+
+            status = kcm_cert_chain_close(chain_handle);
+            if (status == KCM_STATUS_SUCCESS) {
+                tr_info("ConnectorClient::est_enrollment_result() - Certificates stored successfully");
+                tr_info("ConnectorClient::est_enrollment_result() - Storing lwm2m credentials");
+                if (cc->set_connector_credentials(cc->_security) == CCS_STATUS_SUCCESS) {
+                    state = State_EST_Success;
+                }
+            }
+            else {
+                tr_error("ConnectorClient::est_enrollment_result - storing certificate chain failed!");
+            }
+        }
+    }
+
+    // Finally free the certificate chain context
+    EstClient::free_cert_chain_context(cert_chain);
+
+    cc->internal_event(state);
+}
+#endif /* !MBED_CLIENT_DISABLE_EST_FEATURE */
+
+
+M2MInterface::BindingMode ConnectorClient::transport_mode()
+{
+#ifdef MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP
+    return M2MInterface::UDP;
+#elif defined MBED_CLOUD_CLIENT_TRANSPORT_MODE_TCP
+    return M2MInterface::TCP;
+#elif defined MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE
+    return M2MInterface::UDP_QUEUE;
+#elif defined MBED_CLOUD_CLIENT_TRANSPORT_MODE_TCP_QUEUE
+    return M2MInterface::TCP_QUEUE;
+#else
+    return M2MInterface::UDP;
+#endif
+}
+
+void ConnectorClient::init_security_object()
+{
+    if (_security) {
+        for (int i = 0; i <= M2MSecurity::Bootstrap; i++) {
+            // _security->create_object_instance() returns NULL if object already exists
+            if (_security->create_object_instance((M2MSecurity::ServerType)i)) {
+                M2MResource* res = _security->get_resource(M2MSecurity::ServerPublicKey, i);
+                if (res) {
+                    res->set_resource_read_callback(read_security_object_data_from_kcm, this);
+                    res->set_resource_write_callback(write_security_object_data_to_kcm, this);
+                }
+
+                res = _security->get_resource(M2MSecurity::PublicKey, i);
+                if (res) {
+                    res->set_resource_read_callback(read_security_object_data_from_kcm, this);
+                    res->set_resource_write_callback(write_security_object_data_to_kcm, this);
+                }
+
+                res = _security->get_resource(M2MSecurity::Secretkey, i);
+                if (res) {
+                    res->set_resource_read_callback(read_security_object_data_from_kcm, this);
+                    res->set_resource_write_callback(write_security_object_data_to_kcm, this);
+                }
+
+                res = _security->get_resource(M2MSecurity::OpenCertificateChain, i);
+                if (res) {
+                    res->set_resource_read_callback(open_certificate_chain_callback, this);
+                }
+
+                res = _security->get_resource(M2MSecurity::ReadDeviceCertificateChain, i);
+                if (res) {
+                    res->set_resource_read_callback(read_certificate_chain_callback, this);
+                }
+
+                res = _security->get_resource(M2MSecurity::CloseCertificateChain, i);
+                if (res) {
+                    res->set_resource_read_callback(close_certificate_chain_callback, this);
+                }
+            }
+        }
+    }
+}
+
+void *ConnectorClient::certificate_chain_handle() const
+{
+    return _certificate_chain_handle;
+}
+
+void ConnectorClient::set_certificate_chain_handle(void *cert_handle)
+{
+    _certificate_chain_handle = cert_handle;
+}
+
+void ConnectorClient::bootstrap_again()
+{
+    // delete the old connector credentials
+    ccs_delete_item(g_fcc_lwm2m_server_uri_name, CCS_CONFIG_ITEM);
+    ccs_delete_item(g_fcc_lwm2m_server_ca_certificate_name, CCS_CERTIFICATE_ITEM);
+    ccs_delete_item(g_fcc_lwm2m_device_certificate_name, CCS_CERTIFICATE_ITEM);
+    ccs_delete_item(g_fcc_lwm2m_device_private_key_name, CCS_PRIVATE_KEY_ITEM);
+
+    tr_error("ConnectorClient::bootstrap_again in %d seconds", _rebootstrap_time);
+
+    _rebootstrap_timer.start_timer(_rebootstrap_time * 1000, M2MTimerObserver::BootstrapFlowTimer, true);
+    _rebootstrap_time *= 2;
+    if (_rebootstrap_time > MAX_REBOOTSTRAP_TIMEOUT) {
+        _rebootstrap_time = MAX_REBOOTSTRAP_TIMEOUT;
+    }
+
+    _callback->connector_error(M2MInterface::SecureConnectionFailed, CONNECTOR_BOOTSTRAP_AGAIN);
+}