diff --git a/.mbedignore b/.mbedignore
index 7f81d54..cbe5036 100644
--- a/.mbedignore
+++ b/.mbedignore
@@ -2,3 +2,5 @@ __*/*
pal-platform/*
storage-selector/littlefs/*
delta-tool/*
+venv/*
+utils/*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ece62b8..d3a6188 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,14 @@
# Changelog for Pelion Device Management Client example application
+## Release 4.12.0 (01.03.2022)
+
+- Updated to Mbed OS 6.15.1.
+- [Linux] Add a new `define_lwm2m_compliant.txt` that enables communication with a LwM2M compliant service / interoperability (IoP) testing.
+- [Mbed OS] Add a new `mbed_app_lwm2m_compliant.json` that enables communication with a LwM2M compliant service / interoperability (IOP) testing with FRDM K64F device.
+
## Release 4.11.2 (01.12.2021)
-Updated to Mbed OS 6.15.0.
+- Updated to Mbed OS 6.15.0.
## Release 4.11.1 (11.10.2021)
diff --git a/README.md b/README.md
index 4c7b19f..5f7b4cb 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,179 @@
-The full documentation for this example is [available on our documentation site](https://www.pelion.com/docs/device-management/current/connecting/device-management-client-tutorials.html).
+# Device Management Client reference application for connectivity
-You can report concerns about the documentation or this SW as issues to [this GitHub repository](https://github.com/PelionIoT/mbed-cloud-client-example/issues).
+The [`mbed-cloud-client-example`](https://github.com/PelionIoT/mbed-cloud-client-example) is a reference application that uses [Pelion Device Management Client library](https://github.com/PelionIoT/mbed-cloud-client) and demonstrates how to build a connectivity application.
+
+## Device Management Client connection tutorial
+
+This tutorial builds and flashes a Device Management Client application using either Linux (running on a PC) or Mbed OS.
+The application can then connect to a standard OMA Lightweight M2M server.
+The application uses developer mode that relies on a developer certificate, which you add to your software binary to allow test devices to connect to the server.
+In the production, you should use the factory flow.
+
+### Linux
+
+#### Requirements
+
+This requires a Linux PC (64-bit Ubuntu/XUbuntu OS desktop environment).
+See also the [Mbed CLI instructions](https://os.mbed.com/docs/mbed-os/latest/tools/developing-mbed-cli.html).
+
+#### Connecting the device
+
+1. Open a terminal, and clone the example repository to a convenient location on your development environment:
+
+ ```
+ git clone https://github.com/PelionIoT/mbed-cloud-client-example
+ cd mbed-cloud-client-example
+ ```
+
+ **Note:** If you want to speed up `mbed deploy`, you can remove components that are unnecessary for Linux, such as `mbed-os.lib` and the `drivers/` folder.
+
+2. Deploy the example repository:
+
+ ```
+ mbed deploy
+ ```
+
+3. [Create a developer certificate](#create-developer-cert).
+
+4. Copy the `mbed_cloud_dev_credentials.c` file to the root folder of the example.
+
+5. Deploy Linux dependencies:
+
+ ```
+ python pal-platform/pal-platform.py deploy --target=x86_x64_NativeLinux_mbedtls generate
+ cd __x86_x64_NativeLinux_mbedtls
+ ```
+ **Note: python2 is needed for the above command**
+
+6. Generate `cmake` files based on your configuration and build profile (**Release** or **Debug**):
+
+ - For the **Release** profile:
+ ```
+ cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=./../pal-platform/Toolchain/GCC/GCC.cmake -DEXTERNAL_DEFINE_FILE=./../define_lwm2m_compliant.txt
+ ```
+
+ - For the **Debug** profile:
+ ```
+ cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=./../pal-platform/Toolchain/GCC/GCC.cmake -DEXTERNAL_DEFINE_FILE=./../define_lwm2m_compliant.txt
+ ```
+ - If you want your application to bypass the Bootstrap server and work directly with LwM2M server, please add `DISABLE_BOOTSTRAP` cmake flag:
+ ```
+ cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DDISABLE_BOOTSTRAP=ON -DCMAKE_TOOLCHAIN_FILE=./../pal-platform/Toolchain/GCC/GCC.cmake -DEXTERNAL_DEFINE_FILE=./../define_lwm2m_compliant.txt
+ ```
+
+7. Compile the application:
+
+ ```
+ make mbedCloudClientExample.elf
+ ```
+
+8. The build creates binaries under `mbed-cloud-client-example/__x86_x64_NativeLinux_mbedtls`. In both cases, there are subdirectories `Debug` and `Release` respectively created for the two profiles.
+
+9. Run the application (at the respective path, see above):
+
+ ```
+ ./mbedCloudClientExample.elf
+ ```
+
+ You should see a message when the device connects to LwM2M server:
+
+ ```
+ Client registered
+ Endpoint Name:
+ Device Id:
+ ```
+10. If you want to run the application with a clean storage, you can remove the `pal` folder that is created in the location where you run your application.
+
+
+### Mbed OS
+
+#### Prerequisites
+
+To work with the Device Management Client example application, you need:
+
+* A supported board with a network connection and an SD card attached. Currently FRDM K64F and NUCLEO F429ZI boards are supported.
+* [Serial connection](https://os.mbed.com/docs/latest/tutorials/serial-comm.html) to your device with open terminal connection (baud rate 115200, 8N1).
+* [Arm Mbed CLI](https://os.mbed.com/docs/mbed-os/latest/tools/index.html) installed. See [installation instructions](https://os.mbed.com/docs/latest/tools/installation-and-setup.html).
+ * Make sure that all the Python components are in par with the `pip` package [requirements.txt](https://github.com/PelionIoT/mbed-os/blob/master/requirements.txt) list from Mbed OS.
+* Updated [DAPLink](https://github.com/ARMmbed/DAPLink/releases) software (version 250 or later), if your board uses DAPLink.
+
+#### Connecting the device
+
+1. Clone the embedded application's GitHub repository to your local computer and navigate to the new folder:
+
+ ```
+ mbed import https://github.com/PelionIoT/mbed-cloud-client-example
+ cd mbed-cloud-client-example
+ ```
+
+2. Configure Mbed CLI to use your board:
+
+ ```
+ mbed target
+ mbed toolchain GCC_ARM
+ ```
+
+3. [Create a developer certificate](#create-developer-cert).
+
+4. Copy the `mbed_cloud_dev_credentials.c` file to the root folder of the example application.
+
+5. Configure the example application:
+ 1. If you want your application to bypass the Bootstrap server and work directly with LwM2M server,
+ please set the `disable-bootstrap-feature` feature to `true` in [mbed_app_lwm2m_compliant.json](https://github.com/PelionIoT/mbed-cloud-client-example/blob/master/mbed_app_lwm2m_compliant.json#L21).
+
+ ```
+ mbed-client.disable-bootstrap-feature: true
+ ```
+
+ 2. Currently, the application will always start with a clean storage.
+ If you want to avoid this, remove the `RESET_STORAGE` from the `"target.macros_add"` line in the `mbed_app_lwm2m_compliant.json`:
+
+ ```
+ "target.macros_add" : ["LWM2M_COMPLIANT","DISABLE_SERVER_CERT_VERIFY"],
+ ```
+
+7. Compile the example application:
+
+ ```
+ mbed compile --app-config mbed_app_lwm2m_compliant.json
+ ```
+ For more information about Mbed CLI parameters, please see the [Mbed OS documentation site](https://os.mbed.com/docs/mbed-os/latest/build-tools/mbed-cli-1.html).
+
+8. Flash the binary to the device
+ 1. Connect the device to your computer over USB. It's listed as a mass storage device.
+ 2. Drag and drop `mbed-cloud-client-example.bin` to the device, or alternatively add the `-f` flag to the build command (if your device is connected to the build machine). This flashes the binary to the device. You should see the LED blink rapidly; wait for it to stop.
+
+9. Press the **Reset** button to restart the device and reset the terminal.
+10. When the client has successfully connected, the terminal shows:
+
+ ```
+ Client registered
+ Endpoint Name:
+ Device ID:
+ ```
+
+Create a developer certificate
+
+1. Download the server CA certificate from the LwM2M service you want to connect to and copy it to the scripts' folder:
+
+ ```
+ cp utils/server_ca_cert.der
+ ```
+
+2. Run the python script `cert_convert.py` to generate a `mbed_cloud_dev_credentials.c` file.
+
+ ```
+ cd utils
+ python cert_convert.py --endpoint --uri --use-ca
+ ```
+
+ The script will do the following steps:
+ 1. Generate a root CA key and certificate on the first time the script is running. All CA outputs are stored in `CA` folder.
+ 1. Generate a private key and a certificate signed by this CA.
+ 1. Convert the private key, certificate and the server certificate to a C file.
+ 1. All non CA outputs are stored in a folder named `YOUR_ENDPOINT_NAME`.
+
+
+
+
diff --git a/define_lwm2m_compliant.txt b/define_lwm2m_compliant.txt
new file mode 100644
index 0000000..3e5d7ea
--- /dev/null
+++ b/define_lwm2m_compliant.txt
@@ -0,0 +1,70 @@
+cmake_policy(SET CMP0005 OLD) # definitions escaped explicitly (default)
+cmake_policy(SET CMP0011 OLD) # policy flow old way (default)
+
+if (NOT (${OS_BRAND} MATCHES "Linux"))
+ message(FATAL_ERROR "define.txt to be used only with Linux builds.")
+endif()
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../)
+
+add_definitions(-DMBED_CLOUD_CLIENT_USER_CONFIG_FILE="\\"mbed_cloud_client_user_config.h"\\")
+add_definitions(-DPAL_USER_DEFINED_CONFIGURATION="\\"sotp_fs_config_linux_lwm2m_compliant.h"\\")
+add_definitions(-DTARGET_LIKE_POSIX)
+add_definitions(-DPAL_DTLS_PEER_MIN_TIMEOUT=5000)
+
+# More ciphers
+add_definitions(-DPAL_MAX_ALLOWED_CIPHER_SUITES=4)
+
+# enable fota
+add_definitions(-DMBED_CLOUD_CLIENT_FOTA_ENABLE=1)
+add_definitions(-DMBED_CLOUD_CLIENT_FOTA_LINUX_SINGLE_MAIN_FILE=1)
+
+# Set to 1 to enable tracing
+add_definitions(-DMBED_CONF_MBED_TRACE_ENABLE=0)
+
+add_definitions(-DMBED_CONF_APP_DEVELOPER_MODE=1)
+
+add_definitions(-DPLATFORM_ENABLE_BUTTON=1)
+add_definitions(-DPLATFORM_ENABLE_LED=1)
+
+add_definitions(-DMBED_CONF_MBED_CLOUD_CLIENT_DISABLE_CERTIFICATE_ENROLLMENT)
+add_definitions(-DLWM2M_COMPLIANT)
+add_definitions(-DDISABLE_SERVER_CERT_VERIFY)
+
+add_definitions(-DFOTA_DEFAULT_APP_IFS=1)
+add_definitions(-DTARGET_LIKE_LINUX=1)
+add_definitions(-DFOTA_CUSTOM_PLATFORM=1)
+add_definitions(-DMBED_CLOUD_CLIENT_FOTA_SUB_COMPONENT_SUPPORT=1)
+
+if(RESET_STORAGE)
+ add_definitions(-DRESET_STORAGE)
+endif(RESET_STORAGE)
+
+SET(PAL_TLS_BSP_DIR ${NEW_CMAKE_SOURCE_DIR}/mbed-cloud-client/mbed-client-pal/Configs/${TLS_LIBRARY})
+
+if (${TLS_LIBRARY} MATCHES mbedTLS)
+ add_definitions(-DMBEDTLS_CONFIG_FILE="\\"${PAL_TLS_BSP_DIR}/mbedTLSConfig_Linux_LWM2M_Compliant.h"\\")
+endif()
+
+if(PAL_SIMULATOR_FILE_SYSTEM_OVER_RAM)
+ message(WARNING "You are using simulation of File System over RAM")
+ add_definitions(-DPAL_SIMULATOR_FILE_SYSTEM_OVER_RAM=${PAL_SIMULATOR_FILE_SYSTEM_OVER_RAM})
+endif(PAL_SIMULATOR_FILE_SYSTEM_OVER_RAM)
+
+# This definition controls application automatic reboot if network errors exceed certain limit.
+# Disabled when set to 0.
+add_definitions(-DMAX_ERROR_COUNT=0)
+
+if(ENABLE_DEVICE_SENTRY)
+ add_definitions(-DMBED_CONF_MBED_CLOUD_CLIENT_ENABLE_DEVICE_SENTRY)
+ message("Enable Device Sentry feature")
+ add_definitions(-DMBED_CONF_APP_ENABLE_DS_CUSTOM_METRICS_EXAMPLE)
+ message("Enable Device Sentry example application")
+endif(ENABLE_DEVICE_SENTRY)
+
+# Enable FOTA Update
+option(FOTA_ENABLE "Enable FOTA client module" ON)
+
+if(DISABLE_BOOTSTRAP)
+ add_definitions(-DMBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE)
+endif()
diff --git a/mbed-cloud-client.lib b/mbed-cloud-client.lib
index 4868938..777d48f 100644
--- a/mbed-cloud-client.lib
+++ b/mbed-cloud-client.lib
@@ -1 +1 @@
-https://github.com/PelionIoT/mbed-cloud-client/#08d12fb1878c795a7a05770beee485e054bf23b0
+https://github.com/PelionIoT/mbed-cloud-client/#54282bc27ed9c404524e15a0de93dc9a59516f79
diff --git a/mbed-os.lib b/mbed-os.lib
index 5d6d1e1..7655422 100644
--- a/mbed-os.lib
+++ b/mbed-os.lib
@@ -1 +1 @@
-https://github.com/ARMmbed/mbed-os/#4cfbea43cabe86bc3ed7a5287cd464be7a218938
+https://github.com/ARMmbed/mbed-os/#2eb06e76208588afc6cb7580a8dd64c5429a10ce
diff --git a/mbed_app_lwm2m_compliant.json b/mbed_app_lwm2m_compliant.json
new file mode 100644
index 0000000..aeadccf
--- /dev/null
+++ b/mbed_app_lwm2m_compliant.json
@@ -0,0 +1,93 @@
+{
+ "target_overrides": {
+ "*": {
+ "target.features_add" : ["BOOTLOADER", "STORAGE"],
+ "target.c_lib" : "std",
+ "platform.stdio-baud-rate" : 115200,
+ "platform.stdio-convert-newlines" : true,
+ "platform.stdio-buffered-serial" : true,
+ "platform.stdio-flush-at-exit" : true,
+ "rtos.main-thread-stack-size" : 5120,
+ "events.shared-stacksize" : 2048,
+ "events.shared-eventsize" : 2048,
+ "update-client.storage-locations" : 1,
+ "mbed-trace.enable" : 0,
+ "nsapi.default-wifi-security" : "WPA_WPA2",
+ "nsapi.default-wifi-ssid" : "\"SSID\"",
+ "nsapi.default-wifi-password" : "\"Password\"",
+ "target.macros_add" : ["LWM2M_COMPLIANT","DISABLE_SERVER_CERT_VERIFY","RESET_STORAGE"],
+ "client_app.pal-user-defined-configuration" : "\"pal_config_MbedOS_LWM2M_Compliant.h\"",
+ "lwip.mem-size" : 18432,
+ "mbed-client.disable-bootstrap-feature" : null,
+ "mbed-client.max-certificate-size" : 2048
+ },
+ "K64F": {
+ "target.network-default-interface-type" : "ETHERNET",
+ "target.bootloader_img" : "tools/mbed-bootloader-k64f-block_device-kvstore-v4.1.0.bin",
+ "target.header_offset" : "0xa000",
+ "target.app_offset" : "0xa400",
+ "target.components_add" : ["SD"],
+ "update-client.bootloader-details" : "0x00007188",
+ "update-client.application-details" : "(40*1024)",
+ "update-client.storage-address" : "(1024*1024*64)",
+ "update-client.storage-size" : "((MBED_ROM_START + MBED_ROM_SIZE - APPLICATION_ADDR) * MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS)",
+ "mbed-cloud-client.update-storage" : "ARM_UCP_FLASHIAP_BLOCKDEVICE",
+ "storage_filesystem.internal_base_address" : "(32*1024)",
+ "storage_filesystem.rbp_internal_size" : "(8*1024)",
+ "storage_filesystem.external_base_address" : "(0x0)",
+ "storage_filesystem.external_size" : "(1024*1024*64)",
+ "storage.storage_type" : "FILESYSTEM",
+ "storage_filesystem.filesystem" : "LITTLE",
+ "storage_filesystem.blockdevice" : "SD"
+ },
+ "NUCLEO_F429ZI": {
+ "target.network-default-interface-type" : "ETHERNET",
+ "target.bootloader_img" : "tools/mbed-bootloader-nucleo_f429zi-internal_flash-no_rot-v4.1.0.bin",
+ "target.header_offset" : "0x8000",
+ "target.app_offset" : "0x8400",
+ "target.restrict_size" : "0xF7C00",
+ "update-client.bootloader-details" : "0x080078CC",
+ "update-client.application-details" : "(MBED_ROM_START + MBED_BOOTLOADER_SIZE)",
+ "update-client.storage-address" : "(MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_BASE_ADDRESS+MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+ "update-client.storage-size" : "(1024*1024-MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+ "update-client.storage-page" : 1,
+ "mbed-cloud-client.update-storage" : "ARM_UCP_FLASHIAP",
+ "storage_tdb_internal.internal_base_address": "(MBED_ROM_START+1024*1024)",
+ "storage_tdb_internal.internal_size" : "(128*1024)",
+ "storage.storage_type" : "TDB_INTERNAL"
+ }
+ },
+ "config": {
+ "developer-mode": {
+ "help" : "Enable Developer mode to skip Factory enrollment",
+ "options" : [null, 1],
+ "value" : 1
+ },
+ "button-pinname": {
+ "help" : "PinName for button.",
+ "value" : "BUTTON1"
+ },
+ "led-pinname" : {
+ "help" : "PinName for led, which is attached to led blink resource.",
+ "value" : "LED1"
+ },
+ "bootloader-size": {
+ "help" : "Helper macro to enable calculation of rom regions. target.header_offset and target.app_offset still needs to be calculated manually, though.",
+ "value" : "(32*1024)",
+ "macro_name": "MBED_BOOTLOADER_SIZE"
+ },
+ "mbed-trace-max-level": {
+ "help" : "Max trace level. Must be one of the following: [TRACE_LEVEL_DEBUG, TRACE_LEVEL_INFO, TRACE_LEVEL_WARN, TRACE_LEVEL_ERROR, TRACE_LEVEL_CMD]",
+ "macro_name": "MBED_TRACE_MAX_LEVEL",
+ "value" : "TRACE_LEVEL_DEBUG"
+ },
+ "mbed-cloud-client-update-buffer-size": {
+ "value" : null
+ },
+ "enable-ds-custom-metrics-example": {
+ "help" : "Enable Device Sentry custom metrics example applicaton",
+ "options" : [null, 1],
+ "value" : null
+ }
+ }
+}
diff --git a/mbed_cloud_client_user_config.h b/mbed_cloud_client_user_config.h
index e72f367..152d898 100644
--- a/mbed_cloud_client_user_config.h
+++ b/mbed_cloud_client_user_config.h
@@ -24,7 +24,12 @@
#define MBED_CLOUD_CLIENT_ENDPOINT_TYPE "default"
#endif
-#define MBED_CLOUD_CLIENT_LIFETIME 86400
+#define MBED_CLOUD_CLIENT_LIFETIME (3*60)
+
+#ifdef LWM2M_COMPLIANT
+#define MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP
+#define MBED_CONF_MBED_CLIENT_MAX_CERTIFICATE_SIZE 2048
+#endif
#if !defined(MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP) && !defined(MBED_CLOUD_CLIENT_TRANSPORT_MODE_TCP) && !defined(MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE)
#define MBED_CLOUD_CLIENT_TRANSPORT_MODE_TCP
diff --git a/pal_config_MbedOS_LWM2M_Compliant.h b/pal_config_MbedOS_LWM2M_Compliant.h
new file mode 100644
index 0000000..b6d324e
--- /dev/null
+++ b/pal_config_MbedOS_LWM2M_Compliant.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018-2022 ARM Limited. All rights reserved.
+ * 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.
+ */
+
+#ifndef PAL_CONFIG_MBEDOS
+#define PAL_CONFIG_MBEDOS
+
+#define PAL_USE_HW_ROT 0
+#define PAL_USE_HW_RTC 0
+#define PAL_USE_HW_TRNG 1
+#define PAL_SIMULATOR_FLASH_OVER_FILE_SYSTEM 0
+#define PAL_USE_INTERNAL_FLASH 0
+#define PAL_USE_SECURE_TIME 0
+#define PAL_USE_SSL_SESSION_RESUME 1
+#define PAL_MAX_FRAG_LEN 4
+
+
+#include "mbedOS_SST.h"
+
+
+#endif //PAL_CONFIG_MBEDOS
diff --git a/sotp_fs_config_linux_lwm2m_compliant.h b/sotp_fs_config_linux_lwm2m_compliant.h
new file mode 100644
index 0000000..9e12672
--- /dev/null
+++ b/sotp_fs_config_linux_lwm2m_compliant.h
@@ -0,0 +1,12 @@
+#ifndef PAL_HEADER_SOTP_FS_LINUX_COIOTE
+#define PAL_HEADER_SOTP_FS_LINUX_COIOTE
+
+#define PAL_USE_HW_ROT 0
+#define PAL_USE_HW_RTC 0
+#define PAL_USE_HW_TRNG 1
+#define PAL_SIMULATOR_FLASH_OVER_FILE_SYSTEM 1
+#define PAL_USE_SECURE_TIME 0
+
+#include "Linux_default.h"
+
+#endif //PAL_HEADER_SOTP_FS_LINUX_COIOTE
diff --git a/utils/cert_convert.py b/utils/cert_convert.py
new file mode 100755
index 0000000..0fde4d2
--- /dev/null
+++ b/utils/cert_convert.py
@@ -0,0 +1,545 @@
+#!/usr/bin/env python3
+
+# ----------------------------------------------------------------------------
+# Copyright 2022 Pelion
+#
+# 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.
+# ----------------------------------------------------------------------------
+
+"""Script to convert DER files into C arrays."""
+
+import argparse
+import binascii
+import pathlib
+import sys
+
+from helpers import ExecuteHelper
+from helpers import _str_to_resolved_path
+
+dummy_accountID = "123456"
+bs_public_cert_file = "bs_cert.der"
+bs_private_key_file = "bs_key.der"
+csr_file = "csr.pem"
+keys_file = "keys.pem"
+lwm2m_private_key_file = None
+lwm2m_public_cert_file = None
+output_file = "mbed_cloud_dev_credentials.c"
+private_key_file = "cprik.der"
+public_cert_file = "cert.der"
+root_cert_pem_file = "CA/root_cert.pem"
+root_cert_der_file = "CA/root_cert.der"
+root_keys_file = "CA/root_keys.pem"
+server_cert_file = "server_ca_cert.der"
+
+
+def _parse_args():
+ # Parse command line
+ parser = argparse.ArgumentParser(
+ description="Convert DER into c file", add_help=False
+ )
+
+ required = parser.add_argument_group("required arguments")
+ optional = parser.add_argument_group("optional arguments")
+
+ required.add_argument(
+ "--endpoint",
+ help="endpoint name",
+ required=True,
+ )
+ required.add_argument(
+ "--uri",
+ help="The URI of the bootstrap or device managment service",
+ required=True,
+ )
+ optional.add_argument(
+ "--server-cert",
+ help="Server CA cert",
+ default=server_cert_file,
+ )
+ optional.add_argument(
+ "--use-bs",
+ action="store_true",
+ help="Use BS certificate",
+ default=False,
+ required=False,
+ )
+ optional.add_argument(
+ "--use-ca",
+ action="store_true",
+ help="Use CA certificate",
+ default=False,
+ required=False,
+ )
+ optional.add_argument(
+ "-v",
+ "--verbose",
+ action="store_true",
+ help="Print verbose output",
+ default=False,
+ required=False,
+ )
+ optional.add_argument(
+ "-h", "--help", action="help", help="Show this help message and exit."
+ )
+ args = parser.parse_args()
+
+ return args
+
+
+def _sanity_check_files(args):
+ # Sanity check files exist or not.
+ status = True
+
+ # Expect the server_certificate to exist.
+ if not pathlib.Path(server_cert_file).is_file():
+ print("ERROR - file {} not found.".format(args.server_cert))
+ status = False
+
+ return status
+
+
+def _generate_ca_keys(endpoint, use_bs, verbose):
+
+ helper = ExecuteHelper(verbose)
+
+ status = True
+ if (
+ pathlib.Path(root_cert_pem_file).exists()
+ and pathlib.Path(root_keys_file).exists()
+ ):
+ print("Using the existing CA instead of generating a new one.")
+ else:
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "ecparam",
+ "-out",
+ root_keys_file,
+ "-name",
+ "prime256v1",
+ "-genkey",
+ ]
+ )[0]
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "req",
+ "-x509",
+ "-new",
+ "-key",
+ root_keys_file,
+ "-sha256",
+ "-days",
+ "36500",
+ "-subj",
+ "/CN=ROOT_CA",
+ "-outform",
+ "PEM",
+ "-out",
+ root_cert_pem_file,
+ ]
+ )[0]
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "x509",
+ "-inform",
+ "PEM",
+ "-in",
+ root_cert_pem_file,
+ "-outform",
+ "DER",
+ "-out",
+ root_cert_der_file,
+ ]
+ )[0]
+
+ # Now the CA is in place, create the certificates using it.
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "ecparam",
+ "-out",
+ keys_file,
+ "-name",
+ "prime256v1",
+ "-genkey",
+ ]
+ )[0]
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "pkcs8",
+ "-topk8",
+ "-inform",
+ "PEM",
+ "-outform",
+ "DER",
+ "-in",
+ keys_file,
+ "-out",
+ private_key_file,
+ "-nocrypt",
+ ]
+ )[0]
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "req",
+ "-new",
+ "-key",
+ keys_file,
+ "-out",
+ csr_file,
+ "-subj",
+ "/CN={}".format(endpoint),
+ ]
+ )[0]
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "x509",
+ "-req",
+ "-in",
+ csr_file,
+ "-CA",
+ root_cert_pem_file,
+ "-CAkey",
+ root_keys_file,
+ "-CAcreateserial",
+ "-days",
+ "36500",
+ "-outform",
+ "DER",
+ "-out",
+ public_cert_file,
+ ]
+ )[0]
+
+ # If the use_bs flag is set the generate a set of LwM2M server files.
+ if use_bs:
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "ecparam",
+ "-out",
+ keys_file,
+ "-name",
+ "prime256v1",
+ "-genkey",
+ ]
+ )[0]
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "pkcs8",
+ "-topk8",
+ "-inform",
+ "PEM",
+ "-outform",
+ "DER",
+ "-in",
+ keys_file,
+ "-out",
+ lwm2m_private_key_file,
+ "-nocrypt",
+ ]
+ )[0]
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "req",
+ "-new",
+ "-key",
+ keys_file,
+ "-out",
+ csr_file,
+ "-subj",
+ "/CN={}".format(endpoint),
+ ]
+ )[0]
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "x509",
+ "-req",
+ "-in",
+ csr_file,
+ "-CA",
+ root_cert_pem_file,
+ "-CAkey",
+ root_keys_file,
+ "-CAcreateserial",
+ "-days",
+ "36500",
+ "-outform",
+ "DER",
+ "-out",
+ lwm2m_public_cert_file,
+ ]
+ )[0]
+ return status
+
+
+def _generate_non_ca_keys(endpoint, verbose):
+
+ helper = ExecuteHelper(verbose)
+
+ status = True
+
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "ecparam",
+ "-out",
+ keys_file,
+ "-name",
+ "prime256v1",
+ "-genkey",
+ ]
+ )[0]
+
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "pkcs8",
+ "-topk8",
+ "-inform",
+ "PEM",
+ "-outform",
+ "DER",
+ "-in",
+ keys_file,
+ "-out",
+ private_key_file,
+ "-nocrypt",
+ ]
+ )[0]
+ status = status and helper.execute_command(
+ [
+ "openssl",
+ "req",
+ "-x509",
+ "-new",
+ "-key",
+ keys_file,
+ "-sha256",
+ "-days",
+ "36500",
+ "-subj",
+ "/CN={}".format(endpoint),
+ "-outform",
+ "DER",
+ "-out",
+ public_cert_file,
+ ]
+ )[0]
+
+ return status
+
+
+def _process_data(infile, outfile, name, verbose):
+ # Process data
+ if verbose:
+ print("\tAdding {} into the credentials file.".format(infile))
+ outfile.write("const uint8_t {}[] =\n".format(name))
+ outfile.write("{\n")
+ with open(infile, "rb") as dataFile:
+ while True:
+ hexdata = dataFile.read(16).hex()
+ if len(hexdata) == 0:
+ break
+ hexlist = map("".join, zip(hexdata[::2], hexdata[1::2]))
+ outfile.write(" ")
+ for item in hexlist:
+ outfile.write("0x{}, ".format(item))
+ outfile.write("\n")
+ outfile.write("};\n")
+
+ outfile.write("const uint32_t {0}_SIZE = sizeof({0});\n\n".format(name))
+
+
+def _create_output_folders(endpoint):
+ status = True
+
+ # Create the CA folder if we don't already have one.
+ try:
+ pathlib.Path("CA").mkdir(parents=True, exist_ok=True)
+ except FileExistsError:
+ print("ERROR - could not create folder {}.".format("CA"))
+ status = False
+
+ try:
+ pathlib.Path(endpoint).mkdir(parents=True, exist_ok=True)
+ except FileExistsError:
+ print("ERROR - could not create folder {}.".format(endpoint))
+ status = False
+ return status
+
+
+def _resolve_file_paths(args):
+
+ # Convert all the file names to full paths.
+ global csr_file
+ global keys_file
+ global output_file
+ global private_key_file
+ global public_cert_file
+ global root_cert_der_file
+ global root_cert_pem_file
+ global root_keys_file
+ global server_cert_file
+
+ global lwm2m_private_key_file
+ global lwm2m_public_cert_file
+
+ csr_file = _str_to_resolved_path(
+ pathlib.Path(args.endpoint).joinpath(csr_file)
+ )
+ keys_file = _str_to_resolved_path(
+ pathlib.Path(args.endpoint).joinpath(keys_file)
+ )
+ output_file = _str_to_resolved_path(
+ pathlib.Path(args.endpoint).joinpath(output_file)
+ )
+
+ root_cert_der_file = _str_to_resolved_path(root_cert_der_file)
+ root_cert_pem_file = _str_to_resolved_path(root_cert_pem_file)
+ root_keys_file = _str_to_resolved_path(root_keys_file)
+ server_cert_file = _str_to_resolved_path(args.server_cert)
+
+ # If the use_bs flag is set then we need to generate 2 sets of files.
+ # Set the filenames to reflect this.
+
+ if args.use_ca and args.use_bs:
+ lwm2m_private_key_file = _str_to_resolved_path(
+ pathlib.Path(args.endpoint).joinpath(private_key_file)
+ )
+ lwm2m_public_cert_file = _str_to_resolved_path(
+ pathlib.Path(args.endpoint).joinpath(public_cert_file)
+ )
+ private_key_file = _str_to_resolved_path(
+ pathlib.Path(args.endpoint).joinpath(bs_private_key_file)
+ )
+ public_cert_file = _str_to_resolved_path(
+ pathlib.Path(args.endpoint).joinpath(bs_public_cert_file)
+ )
+ else:
+ private_key_file = _str_to_resolved_path(
+ pathlib.Path(args.endpoint).joinpath(private_key_file)
+ )
+ public_cert_file = _str_to_resolved_path(
+ pathlib.Path(args.endpoint).joinpath(public_cert_file)
+ )
+
+
+def main():
+ """Perform the main execution."""
+ args = _parse_args()
+
+ if not _create_output_folders(args.endpoint):
+ sys.exit(2)
+
+ _resolve_file_paths(args)
+
+ if _sanity_check_files(args) is False:
+ sys.exit(2)
+
+ if args.use_ca:
+ status = _generate_ca_keys(args.endpoint, args.use_bs, args.verbose)
+ else:
+ status = _generate_non_ca_keys(args.endpoint, args.verbose)
+
+ if status is False:
+ print(
+ "ERROR - certificate generation failed. "
+ "Run with --verbose for more details."
+ )
+ sys.exit(1)
+
+ # All the variables etc in the output file have the same prefix. Doing it
+ # like this keeps the line lengths below 80 characters.
+ prefix = "MBED_CLOUD_DEV_"
+
+ if args.verbose:
+ print("\nGenerating {}.".format(output_file))
+
+ with open(output_file, "w") as output_data:
+ output_data.write("#ifndef __{}CREDENTIALS_H__\n".format(prefix))
+ output_data.write("#define __{}CREDENTIALS_H__\n\n".format(prefix))
+ output_data.write("#include \n\n")
+
+ output_data.write(
+ 'const char {}BOOTSTRAP_ENDPOINT_NAME[] = "{}";\n'.format(
+ prefix, args.endpoint
+ )
+ )
+ output_data.write(
+ 'const char {}ACCOUNT_ID[] = "{}";\n'.format(
+ prefix, dummy_accountID
+ )
+ )
+ output_data.write(
+ 'const char {}BOOTSTRAP_SERVER_URI[] = "{}";\n\n'.format(
+ prefix, args.uri
+ )
+ )
+
+ _process_data(
+ private_key_file,
+ output_data,
+ "{}BOOTSTRAP_DEVICE_PRIVATE_KEY".format(prefix),
+ args.verbose,
+ )
+ _process_data(
+ public_cert_file,
+ output_data,
+ "{}BOOTSTRAP_DEVICE_CERTIFICATE".format(prefix),
+ args.verbose,
+ )
+ _process_data(
+ server_cert_file,
+ output_data,
+ "{}BOOTSTRAP_SERVER_ROOT_CA_CERTIFICATE".format(prefix),
+ args.verbose,
+ )
+
+ output_data.write(
+ 'const char {}MANUFACTURER[] = "dev_manufacturer";\n'.format(
+ prefix
+ )
+ )
+ output_data.write(
+ 'const char {}MODEL_NUMBER[] = "dev_model_num";\n'.format(prefix)
+ )
+ output_data.write(
+ 'const char {}SERIAL_NUMBER[] = "0";\n'.format(prefix)
+ )
+ output_data.write(
+ 'const char {}DEVICE_TYPE[] = "dev_device_type";\n'.format(prefix)
+ )
+ output_data.write(
+ 'const char {}HARDWARE_VERSION[] = "'
+ 'dev_hardware_version";\n'.format(prefix)
+ )
+ output_data.write(
+ "const uint32_t {}MEMORY_TOTAL_KB = 0;\n\n".format(prefix)
+ )
+
+ output_data.write("#endif //__{}CREDENTIALS_H__\n".format(prefix))
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/utils/helpers.py b/utils/helpers.py
new file mode 100644
index 0000000..e375df3
--- /dev/null
+++ b/utils/helpers.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022 Pelion Limited and Contributors. All rights reserved.
+#
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+"""
+Helpers module.
+
+This module contains helper functions and classes
+"""
+
+import pathlib
+import subprocess
+
+
+def _str_to_resolved_path(path_str):
+ """
+ Convert a string to a resolved Path object.
+
+ Args:
+ * path_str (str): string to convert to a Path object.
+
+ """
+ return pathlib.Path(path_str).resolve(strict=False)
+
+
+class ExecuteHelper:
+ """Class to provide a wrapper for executing commands as a subprocess."""
+
+ verbose = False
+
+ def __init__(self, verbose=False):
+ """Initialise the class."""
+ ExecuteHelper.verbose = verbose
+
+ @staticmethod
+ def _print(data):
+ if ExecuteHelper.verbose:
+ print(data)
+
+ @staticmethod
+ def _print_command(data):
+ if ExecuteHelper.verbose:
+ for item in data:
+ print("{} ".format(item), end="")
+ print("")
+
+ @staticmethod
+ def execute_command(command, timeout=None):
+ """Execute the provided command list.
+
+ Executes the command and returns the error code, stdout and stderr.
+ """
+ ExecuteHelper._print_command(command)
+ p = subprocess.Popen(
+ command,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ bufsize=-1,
+ universal_newlines=True,
+ )
+ try:
+ output, error = p.communicate(timeout=timeout)
+ except subprocess.TimeoutExpired:
+ ExecuteHelper._print("Timed out after {}s".format(timeout))
+ p.kill()
+ output, error = p.communicate()
+
+ if p.returncode:
+ ExecuteHelper._print("error:")
+ ExecuteHelper._print(error)
+ ExecuteHelper._print("returnCode:")
+ ExecuteHelper._print(p.returncode)
+
+ return (p.returncode == 0), output, error
diff --git a/west.yml b/west.yml
index 287cc3d..0db8bff 100644
--- a/west.yml
+++ b/west.yml
@@ -10,7 +10,7 @@ manifest:
- name: pelion-dm
repo-path: mbed-cloud-client
remote: PelionIoT
- revision: 08d12fb1878c795a7a05770beee485e054bf23b0
+ revision: 54282bc27ed9c404524e15a0de93dc9a59516f79
path: modules/lib/pelion-dm
- name: zephyr
remote: zephyrproject-rtos