diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt
index 2588c27c0..2aab9ab9b 100644
--- a/extras/test/CMakeLists.txt
+++ b/extras/test/CMakeLists.txt
@@ -36,6 +36,8 @@ set(TEST_SRCS
src/test_CloudSchedule.cpp
src/test_decode.cpp
src/test_encode.cpp
+ src/test_command_decode.cpp
+ src/test_command_encode.cpp
src/test_publishEvery.cpp
src/test_publishOnChange.cpp
src/test_publishOnChangeRateLimit.cpp
@@ -55,6 +57,9 @@ set(TEST_DUT_SRCS
../../src/property/PropertyContainer.cpp
../../src/cbor/CBORDecoder.cpp
../../src/cbor/CBOREncoder.cpp
+ ../../src/cbor/MessageDecoder.cpp
+ ../../src/cbor/MessageEncoder.cpp
+ ../../src/cbor/CBOR.cpp
../../src/cbor/lib/tinycbor/src/cborencoder.c
../../src/cbor/lib/tinycbor/src/cborencoder_close_container_checked.c
../../src/cbor/lib/tinycbor/src/cborerrorstrings.c
diff --git a/extras/test/src/test_command_decode.cpp b/extras/test/src/test_command_decode.cpp
new file mode 100644
index 000000000..dfae8290a
--- /dev/null
+++ b/extras/test/src/test_command_decode.cpp
@@ -0,0 +1,177 @@
+/*
+ Copyright (c) 2019 Arduino. All rights reserved.
+*/
+
+/**************************************************************************************
+ INCLUDE
+ **************************************************************************************/
+
+#include
+#include
+
+#include
+
+#include
+#include
+
+/**************************************************************************************
+ TEST CODE
+ **************************************************************************************/
+
+SCENARIO("Test the decoding of command messages") {
+ /************************************************************************************/
+
+ // FIXME
+ // WHEN("Decode the ThingBeginCmd message")
+ // {
+ // CommandDown command;
+
+ // /*
+
+ // DA 00010300 # tag(66560)
+ // 81 # array(1)
+ // 78 24 # text(36)
+ // 65343439346435352D383732612D346664322D393634362D393266383739343933393463 # "e4494d55-872a-4fd2-9646-92f87949394c"
+
+ // */
+
+ // uint8_t const payload[] = {0xDA, 0x00, 0x01, 0x03, 0x00, 0x81, 0x78, 0x24, 0x65, 0x34, 0x34, 0x39, 0x34, 0x64, 0x35, 0x35, 0x2D, 0x38, 0x37, 0x32, 0x61, 0x2D, 0x34, 0x66, 0x64, 0x32, 0x2D, 0x39, 0x36, 0x34, 0x36, 0x2D, 0x39, 0x32, 0x66, 0x38, 0x37, 0x39, 0x34, 0x39, 0x33, 0x39, 0x34, 0x63};
+
+ // size_t payload_length = sizeof(payload) / sizeof(uint8_t);
+
+ // CBORMessageDecoder decoder;
+ // Decoder::Status err = decoder.decode((Message*)&command, payload, payload_length);
+
+ // const char *thingIdToMatch = "e4494d55-872a-4fd2-9646-92f87949394c";
+
+ // THEN("The decode is successful") {
+ // REQUIRE(err == Decoder::Status::Complete);
+ // REQUIRE(strcmp(command.thingBeginCmd.params.thing_id, thingIdToMatch) == 0);
+ // REQUIRE(command.c.id == ThingBeginCmdId);
+ // }
+ // }
+
+ /************************************************************************************/
+
+ WHEN("Decode the SetTimezoneCommand message")
+ {
+ CommandDown command;
+
+ /*
+ DA 00010764 # tag(67840)
+ 82 # array(2)
+ 1A 65DCB821 # unsigned(1708963873)
+ 1A 78ACA191 # unsigned(2024579473)
+ */
+
+ uint8_t const payload[] = {0xDA, 0x00, 0x01, 0x09, 0x00, 0x82, 0x1A, 0x65, 0xDC, 0xB8, 0x21, 0x1A, 0x78, 0xAC, 0xA1, 0x91};
+
+ size_t payload_length = sizeof(payload) / sizeof(uint8_t);
+ CBORMessageDecoder decoder;
+ Decoder::Status err = decoder.decode((Message*)&command, payload, payload_length);
+
+ THEN("The decode is successful") {
+ REQUIRE(err == Decoder::Status::Complete);
+ REQUIRE(command.timezoneCommandDown.params.offset == (uint32_t)1708963873);
+ REQUIRE(command.timezoneCommandDown.params.until == (uint32_t)2024579473);
+ REQUIRE(command.c.id == TimezoneCommandDownId);
+ }
+ }
+
+ /************************************************************************************/
+
+ WHEN("Decode the LastValuesUpdateCmd message")
+ {
+ CommandDown command;
+
+ /*
+ DA 00010600 # tag(67072)
+ 81 # array(1)
+ 4D # bytes(13)
+ 00010203040506070809101112 # "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\u0010\u0011\u0012"
+
+ */
+
+ uint8_t const payload[] = {0xDA, 0x00, 0x01, 0x06, 0x00, 0x81, 0x4D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12};
+
+ size_t payload_length = sizeof(payload) / sizeof(uint8_t);
+ CBORMessageDecoder decoder;
+ Decoder::Status err = decoder.decode((Message*)&command, payload, payload_length);
+
+ THEN("The decode is successful") {
+ REQUIRE(err == Decoder::Status::Complete);
+ REQUIRE(command.lastValuesUpdateCmd.params.length == 13);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[0] == (uint8_t)0x00);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[1] == (uint8_t)0x01);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[2] == (uint8_t)0x02);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[3] == (uint8_t)0x03);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[4] == (uint8_t)0x04);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[5] == (uint8_t)0x05);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[6] == (uint8_t)0x06);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[7] == (uint8_t)0x07);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[8] == (uint8_t)0x08);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[9] == (uint8_t)0x09);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[10] == (uint8_t)0x10);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[11] == (uint8_t)0x11);
+ REQUIRE(command.lastValuesUpdateCmd.params.last_values[12] == (uint8_t)0x12);
+ REQUIRE(command.c.id == LastValuesUpdateCmdId);
+ }
+ free(command.lastValuesUpdateCmd.params.last_values);
+ }
+
+ /************************************************************************************/
+
+ // FIXME
+ // WHEN("Decode the OtaUpdateCmdDown message")
+ // {
+ // CommandDown command;
+
+ // /*
+ // DA 00010100 # tag(65792)
+ // 84 # array(4)
+ // 50 # text(12)
+ // 000102030405060708090A0B0C0D0E0F
+ // 75 # text(21)
+ // 383736313233383736313233383736313233313233 # "876123876123876123123"
+ // 53 # bytes(19)
+ // 33303034616162323735313164623332313264 # "3004aab27511db3212d"
+ // 50 # bytes(16)
+ // 6A6B6173646B6A686173646B6A687868 # "jkasdkjhasdkjhxh"
+
+ // */
+
+ // uint8_t const payload[] = {0xDA, 0x00, 0x01, 0x01, 0x00, 0x84, 0x6C, 0x6F, 0x74, 0x61, 0x2D, 0x69, 0x64, 0x2D, 0x31, 0x32, 0x33, 0x34, 0x35, 0x75, 0x38, 0x37, 0x36, 0x31, 0x32, 0x33, 0x38, 0x37, 0x36, 0x31, 0x32, 0x33, 0x38, 0x37, 0x36, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x53, 0x33, 0x30, 0x30, 0x34, 0x61, 0x61, 0x62, 0x32, 0x37, 0x35, 0x31, 0x31, 0x64, 0x62, 0x33, 0x32, 0x31, 0x32, 0x64, 0x50, 0x6A, 0x6B, 0x61, 0x73, 0x64, 0x6B, 0x6A, 0x68, 0x61, 0x73, 0x64, 0x6B, 0x6A, 0x68, 0x78, 0x68};
+
+ // size_t payload_length = sizeof(payload) / sizeof(uint8_t);
+ // CBORMessageDecoder decoder;
+ // Decoder::Status err = decoder.decode((Message*)&command, payload, payload_length);
+
+ // uint8_t otaIdToMatch[ID_SIZE] = {};
+ // const char *urlToMatch = "876123876123876123123";
+
+ // THEN("The decode is successful") {
+ // REQUIRE(err == Decoder::Status::Complete);
+ // REQUIRE(memcmp(command.otaUpdateCmdDown.params.id, otaIdToMatch, ID_SIZE) == 0);
+ // REQUIRE(strcmp(command.otaUpdateCmdDown.params.url, urlToMatch) == 0);
+ // // Initial SHA256 check
+ // REQUIRE(command.otaUpdateCmdDown.params.initialSha256[0] == (uint8_t)0x33);
+ // REQUIRE(command.otaUpdateCmdDown.params.initialSha256[1] == (uint8_t)0x30);
+ // REQUIRE(command.otaUpdateCmdDown.params.initialSha256[2] == (uint8_t)0x30);
+ // REQUIRE(command.otaUpdateCmdDown.params.initialSha256[3] == (uint8_t)0x34);
+ // REQUIRE(command.otaUpdateCmdDown.params.initialSha256[4] == (uint8_t)0x61);
+ // REQUIRE(command.otaUpdateCmdDown.params.initialSha256[5] == (uint8_t)0x61);
+ // REQUIRE(command.otaUpdateCmdDown.params.initialSha256[6] == (uint8_t)0x62);
+ // REQUIRE(command.otaUpdateCmdDown.params.initialSha256[7] == (uint8_t)0x32);
+
+ // // Final SHA256 check
+ // REQUIRE(command.otaUpdateCmdDown.params.finalSha256[0] == (uint8_t)0x6A);
+ // REQUIRE(command.otaUpdateCmdDown.params.finalSha256[1] == (uint8_t)0x6B);
+ // REQUIRE(command.otaUpdateCmdDown.params.finalSha256[2] == (uint8_t)0x61);
+ // REQUIRE(command.otaUpdateCmdDown.params.finalSha256[3] == (uint8_t)0x73);
+ // REQUIRE(command.otaUpdateCmdDown.params.finalSha256[4] == (uint8_t)0x64);
+ // REQUIRE(command.otaUpdateCmdDown.params.finalSha256[5] == (uint8_t)0x6B);
+
+ // REQUIRE(command.c.id == OtaUpdateCmdDownId);
+ // }
+ // }
+}
diff --git a/extras/test/src/test_command_encode.cpp b/extras/test/src/test_command_encode.cpp
new file mode 100644
index 000000000..05ad70ca2
--- /dev/null
+++ b/extras/test/src/test_command_encode.cpp
@@ -0,0 +1,215 @@
+/*
+ Copyright (c) 2019 Arduino. All rights reserved.
+*/
+
+/**************************************************************************************
+ INCLUDE
+ **************************************************************************************/
+
+#include
+
+#include
+
+#include
+#include
+
+/**************************************************************************************
+ TEST CODE
+ **************************************************************************************/
+
+SCENARIO("Test the encoding of command messages") {
+ /************************************************************************************/
+
+ WHEN("Encode the OtaBeginUp message")
+ {
+ OtaBeginUp command;
+ uint8_t sha[SHA256_SIZE] = {0x01, 0x02, 0x03, 0x04};
+ memcpy(command.params.sha, sha, SHA256_SIZE);
+
+ command.c.id = CommandId::OtaBeginUpId;
+
+ uint8_t buffer[512];
+ size_t bytes_encoded = sizeof(buffer);
+
+ CBORMessageEncoder encoder;
+ Encoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded);
+
+ uint8_t expected_result[] = {
+ 0xda, 0x00, 0x01, 0x00, 0x00, 0x81, 0x58, 0x20, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ // Test the encoding is
+ // DA 00010000 # tag(65536)
+ // 81 # array(1)
+ // 58 20 # bytes(32)
+ // 01020304
+ THEN("The encoding is successful") {
+ REQUIRE(err == Encoder::Status::Complete);
+ REQUIRE(bytes_encoded == sizeof(expected_result));
+ REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0);
+ }
+ }
+
+
+ /************************************************************************************/
+
+ WHEN("Encode the ThingBeginCmd message")
+ {
+ ThingBeginCmd command;
+ String thing_id = "thing_id";
+ strcpy(command.params.thing_id, thing_id.c_str());
+
+ command.c.id = CommandId::ThingBeginCmdId;
+
+ uint8_t buffer[512];
+ size_t bytes_encoded = sizeof(buffer);
+
+ CBORMessageEncoder encoder;
+ Encoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded);
+
+ uint8_t expected_result[] = {
+ 0xda, 0x00, 0x01, 0x03, 0x00, 0x81, 0x68, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x64
+ };
+
+ // Test the encoding is
+ // DA 00010300 # tag(66304)
+ // 81 # array(1)
+ // 68 # text(8)
+ // 7468696E675F6964 # "thing_id"
+
+ THEN("The encoding is successful") {
+ REQUIRE(err == Encoder::Status::Complete);
+ REQUIRE(bytes_encoded == sizeof(expected_result));
+ REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0);
+ }
+ }
+
+ /************************************************************************************/
+
+ WHEN("Encode the LastValuesBeginCmd message")
+ {
+ LastValuesBeginCmd command;
+ command.c.id = CommandId::LastValuesBeginCmdId;
+
+ uint8_t buffer[512];
+ size_t bytes_encoded = sizeof(buffer);
+
+ CBORMessageEncoder encoder;
+ Encoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded);
+
+ uint8_t expected_result[] = {
+ 0xda, 0x00, 0x01, 0x05, 0x00, 0x80
+ };
+
+ // Test the encoding is
+ // DA 00010500 # tag(66816)
+ // 80 # array(0)
+ THEN("The encoding is successful") {
+ REQUIRE(err == Encoder::Status::Complete);
+ REQUIRE(bytes_encoded == sizeof(expected_result));
+ REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0);
+ }
+ }
+
+ /************************************************************************************/
+
+ WHEN("Encode the DeviceBeginCmd message")
+ {
+ DeviceBeginCmd command;
+ String lib_version = "2.0.0";
+ strcpy(command.params.lib_version, lib_version.c_str());
+
+ command.c.id = CommandId::DeviceBeginCmdId;
+
+ uint8_t buffer[512];
+ size_t bytes_encoded = sizeof(buffer);
+
+ CBORMessageEncoder encoder;
+ Encoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded);
+
+ uint8_t expected_result[] = {
+ 0xda, 0x00, 0x01, 0x07, 0x00, 0x81, 0x65, 0x32, 0x2e, 0x30, 0x2e, 0x30
+ };
+
+ // Test the encoding is
+ // DA 00010700 # tag(67328)
+ // 81 # array(1)
+ // 65 # text(5)
+ // 322E302E30 # "2.0.0"
+ THEN("The encoding is successful") {
+ REQUIRE(err == Encoder::Status::Complete);
+ REQUIRE(bytes_encoded == sizeof(expected_result));
+ REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0);
+ }
+ }
+
+ /************************************************************************************/
+
+ WHEN("Encode the OtaProgressCmdUp message")
+ {
+ OtaProgressCmdUp command;
+ command.params.time = 2;
+
+ uint8_t id[ID_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+ memcpy(command.params.id, id, ID_SIZE);
+ command.params.state = 1;
+ command.params.state_data = -1;
+ command.params.time = 100;
+
+ command.c.id = CommandId::OtaProgressCmdUpId;
+
+ uint8_t buffer[512];
+ size_t bytes_encoded = sizeof(buffer);
+
+ CBORMessageEncoder encoder;
+ Encoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded);
+
+ uint8_t expected_result[] = {
+ 0xda, 0x00, 0x01, 0x02, 0x00, 0x84, 0x50, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xe1, 0x20, 0x18, 0x64
+ };
+
+ // Test the encoding is
+ // DA 00010200 # tag(66048)
+ // 84 # array(4)
+ // 50 # bytes(16)
+ // 000102030405060708090A0B0C0D0E0F # "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f"
+ // E1 # primitive(1)
+ // 20 # negative(0)
+ // 18 64 # unsigned(100)
+ THEN("The encoding is successful") {
+ REQUIRE(err == Encoder::Status::Complete);
+ REQUIRE(bytes_encoded == sizeof(expected_result));
+ REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0);
+ }
+ }
+
+ /************************************************************************************/
+
+ WHEN("Encode the TimezoneCommandUp message")
+ {
+ TimezoneCommandUp command;
+ command.c.id = CommandId::TimezoneCommandUpId;
+
+ uint8_t buffer[512];
+ size_t bytes_encoded = sizeof(buffer);
+
+ CBORMessageEncoder encoder;
+ Encoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded);
+
+ uint8_t expected_result[] = {
+ 0xda, 0x00, 0x01, 0x08, 0x00, 0x80
+ };
+
+ // Test the encoding is
+ // DA 00010800 # tag(67584)
+ // 80 # array(0)
+ THEN("The encoding is successful") {
+ REQUIRE(err == Encoder::Status::Complete);
+ REQUIRE(bytes_encoded == sizeof(expected_result));
+ REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0);
+ }
+ }
+}
diff --git a/src/AIoTC_Config.h b/src/AIoTC_Config.h
index f4fbd2a6a..652c37176 100644
--- a/src/AIoTC_Config.h
+++ b/src/AIoTC_Config.h
@@ -115,9 +115,14 @@
#endif
#if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA)
+ #define BEAR_SSL_CLIENT_IBUF_SIZE (16384 + 325) // Allows download from storage API
#define BOARD_STM32H7
#endif
+#if defined(ARDUINO_NANO_RP2040_CONNECT)
+ #define BEAR_SSL_CLIENT_IBUF_SIZE (16384 + 325) // Allows download from storage API
+#endif
+
#if defined(ARDUINO_EDGE_CONTROL)
#define BOARD_HAS_SECRET_KEY
#define HAS_TCP
diff --git a/src/ArduinoIoTCloudDevice.cpp b/src/ArduinoIoTCloudDevice.cpp
index 86569e8b6..3e2510154 100644
--- a/src/ArduinoIoTCloudDevice.cpp
+++ b/src/ArduinoIoTCloudDevice.cpp
@@ -109,12 +109,12 @@ ArduinoCloudDevice::State ArduinoCloudDevice::handleInit() {
ArduinoCloudDevice::State ArduinoCloudDevice::handleSendCapabilities() {
/* Sends device capabilities message */
- Message message = { DeviceBeginCmdId };
- deliver(&message);
+ DeviceBeginCmd deviceBegin = { DeviceBeginCmdId, AIOT_CONFIG_LIB_VERSION };
+ deliver(reinterpret_cast(&deviceBegin));
/* Subscribe to device topic to request */
- message = { ThingBeginCmdId };
- deliver(&message);
+ ThingBeginCmd thingBegin = { ThingBeginCmdId };
+ deliver(reinterpret_cast(&thingBegin));
/* No device configuration received. Wait: 4s -> 8s -> 16s -> 32s -> 32s ...*/
_attachAttempt.retry();
diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp
index e9ccd4a94..521a92d40 100644
--- a/src/ArduinoIoTCloudTCP.cpp
+++ b/src/ArduinoIoTCloudTCP.cpp
@@ -43,6 +43,7 @@
#include
#include "cbor/CBOREncoder.h"
#include "utility/watchdog/Watchdog.h"
+#include
/******************************************************************************
LOCAL MODULE FUNCTIONS
@@ -62,7 +63,6 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP()
, _connection_attempt(0,0)
, _message_stream(std::bind(&ArduinoIoTCloudTCP::sendMessage, this, std::placeholders::_1))
, _thing(&_message_stream)
-, _thing_id_property{nullptr}
, _device(&_message_stream)
, _mqtt_data_buf{0}
, _mqtt_data_len{0}
@@ -76,8 +76,8 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP()
, _mqttClient{nullptr}
, _deviceTopicOut("")
, _deviceTopicIn("")
-, _shadowTopicOut("")
-, _shadowTopicIn("")
+, _messageTopicOut("")
+, _messageTopicIn("")
, _dataTopicOut("")
, _dataTopicIn("")
#if OTA_ENABLED
@@ -181,6 +181,7 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress,
_mqttClient.setUsernamePassword(getDeviceId(), _password);
}
#endif
+
_mqttClient.onMessage(ArduinoIoTCloudTCP::onMessage);
_mqttClient.setKeepAliveInterval(30 * 1000);
_mqttClient.setConnectionTimeout(1500);
@@ -188,17 +189,14 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress,
_deviceTopicOut = getTopic_deviceout();
_deviceTopicIn = getTopic_devicein();
-
- Property* p;
- p = new CloudWrapperString(_lib_version);
- addPropertyToContainer(_device.getPropertyContainer(), *p, "LIB_VERSION", Permission::Read, -1);
- p = new CloudWrapperString(_thing_id);
- _thing_id_property = &addPropertyToContainer(_device.getPropertyContainer(), *p, "thing_id", Permission::ReadWrite, -1).writeOnDemand();
+ _messageTopicIn = getTopic_messagein();
+ _messageTopicOut = getTopic_messageout();
_thing.begin();
_device.begin();
#if OTA_ENABLED
+ Property* p;
p = new CloudWrapperBool(_ota_cap);
addPropertyToContainer(_device.getPropertyContainer(), *p, "OTA_CAP", Permission::Read, -1);
p = new CloudWrapperInt(_ota_error);
@@ -322,9 +320,16 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConnectMqttBroker()
{
if (_mqttClient.connect(_brokerAddress.c_str(), _brokerPort))
{
- DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s connected to %s:%d", __FUNCTION__, _brokerAddress.c_str(), _brokerPort);
+ /* Subscribe to message topic to receive commands */
+ _mqttClient.subscribe(_messageTopicIn);
+
+ /* Temoporarly subsribe to device topic to receive OTA properties */
+ _mqttClient.subscribe(_deviceTopicIn);
+
/* Reconfigure timers for next state */
_connection_attempt.begin(AIOT_CONFIG_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms, AIOT_CONFIG_MAX_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms);
+
+ DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s connected to %s:%d", __FUNCTION__, _brokerAddress.c_str(), _brokerPort);
return State::Connected;
}
@@ -341,6 +346,7 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected()
{
if (!_mqttClient.connected() || !_thing.connected() || !_device.connected())
{
+ Serial.println("State::Disconnect");
return State::Disconnect;
}
@@ -439,28 +445,6 @@ void ArduinoIoTCloudTCP::handleMessage(int length)
/* Topic for OTA properties and device configuration */
if (_deviceTopicIn == topic) {
CBORDecoder::decode(_device.getPropertyContainer(), (uint8_t*)bytes, length);
-
- /* Temporary check to avoid flooding device state machine with usless messages */
- if (_thing_id_property->isDifferentFromCloud()) {
- _thing_id_property->fromCloudToLocal();
-
- Message message;
- /* If we are attached we need first to detach */
- if (_device.isAttached()) {
- detachThing();
- message = { DeviceDetachedCmdId };
- }
- /* If received thing id is valid attach to the new thing */
- if (_thing_id.length()) {
- attachThing();
- message = { DeviceAttachedCmdId };
- } else {
- /* Send message to device state machine to inform we have received a null thing-id */
- _thing_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
- message = { DeviceRegisteredCmdId };
- }
- _device.handleMessage(&message);
- }
}
/* Topic for user input data */
@@ -468,41 +452,97 @@ void ArduinoIoTCloudTCP::handleMessage(int length)
CBORDecoder::decode(_thing.getPropertyContainer(), (uint8_t*)bytes, length);
}
- /* Topic for sync Thing last values on connect */
- if (_shadowTopicIn == topic) {
- DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] last values received", __FUNCTION__, millis());
- /* Decode last values property array */
- CBORDecoder::decode(_thing.getPropertyContainer(), (uint8_t*)bytes, length, true);
- /* Unlock thing state machine waiting last values */
- Message message = { LastValuesUpdateCmdId };
- _thing.handleMessage(&message);
- /* Call ArduinoIoTCloud sync user callback*/
- execCloudEventCallback(ArduinoIoTCloudEvent::SYNC);
+ /* Topic for device commands */
+ if (_messageTopicIn == topic) {
+ CommandDown command;
+ DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] received %d bytes", __FUNCTION__, millis(), length);
+ CBORMessageDecoder decoder;
+
+ size_t buffer_length = length;
+ if (decoder.decode((Message*)&command, bytes, buffer_length) != Decoder::Status::Error) {
+ DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] received command id %d", __FUNCTION__, millis(), command.c.id);
+ switch (command.c.id)
+ {
+ case CommandId::ThingUpdateCmdId:
+ {
+ DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] device configuration received", __FUNCTION__, millis());
+ if ( _thing_id != String(command.thingBeginCmd.params.thing_id)) {
+ _thing_id = String(command.thingBeginCmd.params.thing_id);
+ Message message;
+ /* If we are attached we need first to detach */
+ if (_device.isAttached()) {
+ detachThing();
+ message = { DeviceDetachedCmdId };
+ }
+ /* If received thing id is valid attach to the new thing */
+ if (_thing_id.length()) {
+ attachThing();
+ message = { DeviceAttachedCmdId };
+ } else {
+ /* Send message to device state machine to inform we have received a null thing-id */
+ _thing_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
+ message = { DeviceRegisteredCmdId };
+ }
+ _device.handleMessage(&message);
+ }
+ }
+ break;
+
+ case CommandId::LastValuesUpdateCmdId:
+ {
+ DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] last values received", __FUNCTION__, millis());
+ CBORDecoder::decode(_thing.getPropertyContainer(),
+ (uint8_t*)command.lastValuesUpdateCmd.params.last_values,
+ command.lastValuesUpdateCmd.params.length, true);
+ _thing.handleMessage((Message*)&command);
+ execCloudEventCallback(ArduinoIoTCloudEvent::SYNC);
+
+ /*
+ * NOTE: in this current version properties are not properly integrated with the new paradigm of
+ * modeling the messages with C structs. The current CBOR library allocates an array in the heap
+ * thus we need to delete it after decoding it with the old CBORDecoder
+ */
+ free(command.lastValuesUpdateCmd.params.last_values);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
}
}
void ArduinoIoTCloudTCP::sendMessage(Message * msg)
{
- switch (msg->id)
- {
- case DeviceBeginCmdId:
- sendDevicePropertiesToCloud();
- break;
+ uint8_t data[MQTT_TRANSMIT_BUFFER_SIZE];
+ size_t bytes_encoded = sizeof(data);
+ CBORMessageEncoder encoder;
- case ThingBeginCmdId:
- requestThingId();
- break;
+ switch (msg->id) {
+ case PropertiesUpdateCmdId:
+ return sendPropertyContainerToCloud(_dataTopicOut,
+ _thing.getPropertyContainer(),
+ _thing.getPropertyContainerIndex());
+ break;
- case LastValuesBeginCmdId:
- requestLastValue();
- break;
+#if OTA_ENABLED
+ case DeviceBeginCmdId:
+ sendDevicePropertyToCloud("OTA_CAP");
+ sendDevicePropertyToCloud("OTA_ERROR");
+ sendDevicePropertyToCloud("OTA_SHA256");
+ break;
+#endif
- case PropertiesUpdateCmdId:
- sendThingPropertiesToCloud();
- break;
+ default:
+ break;
+ }
- default:
- break;
+ if (encoder.encode(msg, data, bytes_encoded) == Encoder::Status::Complete &&
+ bytes_encoded > 0) {
+ write(_messageTopicOut, data, bytes_encoded);
+ } else {
+ DEBUG_ERROR("error encoding %d", msg->id);
}
}
@@ -526,29 +566,6 @@ void ArduinoIoTCloudTCP::sendPropertyContainerToCloud(String const topic, Proper
}
}
-void ArduinoIoTCloudTCP::sendThingPropertiesToCloud()
-{
- sendPropertyContainerToCloud(_dataTopicOut, _thing.getPropertyContainer(), _thing.getPropertyContainerIndex());
-}
-
-void ArduinoIoTCloudTCP::sendDevicePropertiesToCloud()
-{
- PropertyContainer ro_device_property_container;
- unsigned int last_device_property_index = 0;
-
- std::list ro_device_property_list {"LIB_VERSION", "OTA_CAP", "OTA_ERROR", "OTA_SHA256"};
- std::for_each(ro_device_property_list.begin(),
- ro_device_property_list.end(),
- [this, &ro_device_property_container ] (String const & name)
- {
- Property* p = getProperty(this->_device.getPropertyContainer(), name);
- if(p != nullptr)
- addPropertyToContainer(ro_device_property_container, *p, p->name(), p->isWriteableByCloud() ? Permission::ReadWrite : Permission::Read);
- }
- );
- sendPropertyContainerToCloud(_deviceTopicOut, ro_device_property_container, last_device_property_index);
-}
-
#if OTA_ENABLED
void ArduinoIoTCloudTCP::sendDevicePropertyToCloud(String const name)
{
@@ -564,26 +581,6 @@ void ArduinoIoTCloudTCP::sendDevicePropertyToCloud(String const name)
}
#endif
-void ArduinoIoTCloudTCP::requestLastValue()
-{
- // Send the getLastValues CBOR message to the cloud
- // [{0: "r:m", 3: "getLastValues"}] = 81 A2 00 63 72 3A 6D 03 6D 67 65 74 4C 61 73 74 56 61 6C 75 65 73
- // Use http://cbor.me to easily generate CBOR encoding
- const uint8_t CBOR_REQUEST_LAST_VALUE_MSG[] = { 0x81, 0xA2, 0x00, 0x63, 0x72, 0x3A, 0x6D, 0x03, 0x6D, 0x67, 0x65, 0x74, 0x4C, 0x61, 0x73, 0x74, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x73 };
- write(_shadowTopicOut, CBOR_REQUEST_LAST_VALUE_MSG, sizeof(CBOR_REQUEST_LAST_VALUE_MSG));
-}
-
-void ArduinoIoTCloudTCP::requestThingId()
-{
- if (!_mqttClient.subscribe(_deviceTopicIn))
- {
- /* If device_id is wrong the board can't connect to the broker so this condition
- * should never happen.
- */
- DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not subscribe to %s", __FUNCTION__, _deviceTopicIn.c_str());
- }
-}
-
void ArduinoIoTCloudTCP::attachThing()
{
@@ -595,14 +592,6 @@ void ArduinoIoTCloudTCP::attachThing()
return;
}
- _shadowTopicIn = getTopic_shadowin();
- _shadowTopicOut = getTopic_shadowout();
- if (!_mqttClient.subscribe(_shadowTopicIn)) {
- DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not subscribe to %s", __FUNCTION__, _shadowTopicIn.c_str());
- DEBUG_ERROR("Check your thing configuration, and press the reset button on your board.");
- return;
- }
-
DEBUG_INFO("Connected to Arduino IoT Cloud");
DEBUG_INFO("Thing ID: %s", getThingId().c_str());
execCloudEventCallback(ArduinoIoTCloudEvent::CONNECT);
@@ -615,11 +604,6 @@ void ArduinoIoTCloudTCP::detachThing()
return;
}
- if (!_mqttClient.unsubscribe(_shadowTopicIn)) {
- DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not unsubscribe from %s", __FUNCTION__, _shadowTopicIn.c_str());
- return;
- }
-
DEBUG_INFO("Disconnected from Arduino IoT Cloud");
execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT);
}
diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h
index 1c13592df..ef1a93105 100644
--- a/src/ArduinoIoTCloudTCP.h
+++ b/src/ArduinoIoTCloudTCP.h
@@ -56,6 +56,9 @@
#include
#endif
+#include "cbor/MessageDecoder.h"
+#include "cbor/MessageEncoder.h"
+
/******************************************************************************
CONSTANTS
******************************************************************************/
@@ -125,7 +128,6 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
TimedAttempt _connection_attempt;
MessageStream _message_stream;
ArduinoCloudThing _thing;
- Property * _thing_id_property;
ArduinoCloudDevice _device;
String _brokerAddress;
@@ -165,8 +167,8 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
String _deviceTopicOut;
String _deviceTopicIn;
- String _shadowTopicOut;
- String _shadowTopicIn;
+ String _messageTopicOut;
+ String _messageTopicIn;
String _dataTopicOut;
String _dataTopicIn;
@@ -182,8 +184,10 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
inline String getTopic_deviceout() { return String("/a/d/" + getDeviceId() + "/e/o");}
inline String getTopic_devicein () { return String("/a/d/" + getDeviceId() + "/e/i");}
- inline String getTopic_shadowout() { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/shadow/o"); }
- inline String getTopic_shadowin () { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/shadow/i"); }
+
+ inline String getTopic_messageout() { return String("/a/d/" + getDeviceId() + "/c/up");}
+ inline String getTopic_messagein () { return String("/a/d/" + getDeviceId() + "/c/dw");}
+
inline String getTopic_dataout () { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/e/o"); }
inline String getTopic_datain () { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/e/i"); }
@@ -197,10 +201,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
void handleMessage(int length);
void sendMessage(Message * msg);
void sendPropertyContainerToCloud(String const topic, PropertyContainer & property_container, unsigned int & current_property_index);
- void sendThingPropertiesToCloud();
- void sendDevicePropertiesToCloud();
- void requestLastValue();
- void requestThingId();
+
void attachThing();
void detachThing();
int write(String const topic, byte const data[], int const length);
@@ -208,7 +209,6 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
#if OTA_ENABLED
void sendDevicePropertyToCloud(String const name);
#endif
-
};
/******************************************************************************
diff --git a/src/cbor/CBOR.cpp b/src/cbor/CBOR.cpp
new file mode 100644
index 000000000..3765969f3
--- /dev/null
+++ b/src/cbor/CBOR.cpp
@@ -0,0 +1,56 @@
+#include "CBOR.h"
+
+CommandId toCommandId(CBORCommandTag tag) {
+ switch(tag) {
+ case CBORCommandTag::CBOROtaBeginUp:
+ return CommandId::OtaBeginUpId;
+ case CBORCommandTag::CBORThingBeginCmd:
+ return CommandId::ThingBeginCmdId;
+ case CBORCommandTag::CBORLastValuesBeginCmd:
+ return CommandId::LastValuesBeginCmdId;
+ case CBORCommandTag::CBORDeviceBeginCmd:
+ return CommandId::DeviceBeginCmdId;
+ case CBORCommandTag::CBOROtaProgressCmdUp:
+ return CommandId::OtaProgressCmdUpId;
+ case CBORCommandTag::CBORTimezoneCommandUp:
+ return CommandId::TimezoneCommandUpId;
+ case CBORCommandTag::CBOROtaUpdateCmdDown:
+ return CommandId::OtaUpdateCmdDownId;
+ case CBORCommandTag::CBORThingUpdateCmd:
+ return CommandId::ThingUpdateCmdId;
+ case CBORCommandTag::CBORLastValuesUpdate:
+ return CommandId::LastValuesUpdateCmdId;
+ case CBORCommandTag::CBORTimezoneCommandDown:
+ return CommandId::TimezoneCommandDownId;
+ default:
+ return CommandId::UnknownCmdId;
+ }
+}
+
+
+CBORCommandTag toCBORCommandTag(CommandId id) {
+ switch(id) {
+ case CommandId::OtaBeginUpId:
+ return CBORCommandTag::CBOROtaBeginUp;
+ case CommandId::ThingBeginCmdId:
+ return CBORCommandTag::CBORThingBeginCmd;
+ case CommandId::LastValuesBeginCmdId:
+ return CBORCommandTag::CBORLastValuesBeginCmd;
+ case CommandId::DeviceBeginCmdId:
+ return CBORCommandTag::CBORDeviceBeginCmd;
+ case CommandId::OtaProgressCmdUpId:
+ return CBORCommandTag::CBOROtaProgressCmdUp;
+ case CommandId::TimezoneCommandUpId:
+ return CBORCommandTag::CBORTimezoneCommandUp;
+ case CommandId::OtaUpdateCmdDownId:
+ return CBORCommandTag::CBOROtaUpdateCmdDown;
+ case CommandId::ThingUpdateCmdId:
+ return CBORCommandTag::CBORThingUpdateCmd;
+ case CommandId::LastValuesUpdateCmdId:
+ return CBORCommandTag::CBORLastValuesUpdate;
+ case CommandId::TimezoneCommandDownId:
+ return CBORCommandTag::CBORTimezoneCommandDown;
+ default:
+ return CBORCommandTag::CBORUnknownCmdTag;
+ }
+}
\ No newline at end of file
diff --git a/src/cbor/CBOR.h b/src/cbor/CBOR.h
new file mode 100644
index 000000000..538926d51
--- /dev/null
+++ b/src/cbor/CBOR.h
@@ -0,0 +1,27 @@
+#pragma once
+#include
+
+enum CBORCommandTag: uint64_t {
+ // Commands UP
+ CBOROtaBeginUp = 0x010000,
+ CBORThingBeginCmd = 0x010300,
+ CBORLastValuesBeginCmd = 0x010500,
+ CBORDeviceBeginCmd = 0x010700,
+ CBOROtaProgressCmdUp = 0x010200,
+ CBORTimezoneCommandUp = 0x010800,
+
+ // Commands DOWN
+ CBOROtaUpdateCmdDown = 0x010100,
+ CBORThingUpdateCmd = 0x010400,
+ CBORLastValuesUpdate = 0x010600,
+ CBORTimezoneCommandDown = 0x010900,
+
+ // Unknown Command Tag https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
+ CBORUnknownCmdTag16b = 0xffff, // invalid tag
+ CBORUnknownCmdTag32b = 0xffffffff, // invalid tag
+ CBORUnknownCmdTag64b = 0xffffffffffffffff, // invalid tag
+ CBORUnknownCmdTag = CBORUnknownCmdTag32b
+};
+
+CommandId toCommandId(CBORCommandTag tag);
+CBORCommandTag toCBORCommandTag(CommandId id);
\ No newline at end of file
diff --git a/src/cbor/MessageDecoder.cpp b/src/cbor/MessageDecoder.cpp
new file mode 100644
index 000000000..ea455cb06
--- /dev/null
+++ b/src/cbor/MessageDecoder.cpp
@@ -0,0 +1,247 @@
+//
+// This file is part of CBORDecoder
+//
+// Copyright 2019 ARDUINO SA (http://www.arduino.cc/)
+//
+// This software is released under the GNU General Public License version 3,
+// which covers the main part of CBORDecoder.
+// The terms of this license can be found at:
+// https://www.gnu.org/licenses/gpl-3.0.en.html
+//
+// You can be released from the requirements of the above licenses by purchasing
+// a commercial license. Buying such a license is mandatory if you want to modify or
+// otherwise use the software for commercial activities involving the Arduino
+// software without disclosing the source code of your own applications. To purchase
+// a commercial license, send an email to license@arduino.cc.
+//
+
+/******************************************************************************
+ INCLUDE
+ ******************************************************************************/
+
+#include
+
+#undef max
+#undef min
+#include
+
+#include "MessageDecoder.h"
+#include
+
+/******************************************************************************
+ PUBLIC MEMBER FUNCTIONS
+ ******************************************************************************/
+
+Decoder::Status CBORMessageDecoder::decode(Message * message, uint8_t const * const payload, size_t& length)
+{
+ CborValue main_iter, array_iter;
+ CborTag tag;
+ CborParser parser;
+
+ if (cbor_parser_init(payload, length, 0, &parser, &main_iter) != CborNoError)
+ return Decoder::Status::Error;
+
+ if (main_iter.type != CborTagType)
+ return Decoder::Status::Error;
+
+ if (cbor_value_get_tag(&main_iter, &tag) == CborNoError) {
+ message->id = toCommandId(CBORCommandTag(tag));
+ }
+
+ if (cbor_value_advance(&main_iter) != CborNoError) {
+ return Decoder::Status::Error;
+ }
+
+ ArrayParserState current_state = ArrayParserState::EnterArray,
+ next_state = ArrayParserState::Error;
+
+ while (current_state != ArrayParserState::Complete) {
+ switch (current_state) {
+ case ArrayParserState::EnterArray : next_state = handle_EnterArray(&main_iter, &array_iter); break;
+ case ArrayParserState::ParseParam : next_state = handle_Param(&array_iter, message); break;
+ case ArrayParserState::LeaveArray : next_state = handle_LeaveArray(&main_iter, &array_iter); break;
+ case ArrayParserState::Complete : return Decoder::Status::Complete;
+ case ArrayParserState::MessageNotSupported : return Decoder::Status::Error;
+ case ArrayParserState::Error : return Decoder::Status::Error;
+ }
+
+ current_state = next_state;
+ }
+
+ return Decoder::Status::Complete;
+}
+
+/******************************************************************************
+ PRIVATE MEMBER FUNCTIONS
+ ******************************************************************************/
+
+bool copyCBORStringToArray(CborValue * param, char * dest, size_t dest_size) {
+ if (!cbor_value_is_text_string(param)) {
+ return false;
+ }
+
+ // NOTE: keep in mind that _cbor_value_copy_string tries to put a \0 at the end of the string
+ if(_cbor_value_copy_string(param, dest, &dest_size, NULL) != CborNoError) {
+ return false;
+ }
+
+ return true;
+}
+
+// FIXME dest_size should be also returned, the copied byte array can have a different size from the starting one
+// for the time being we need this on SHA256 only
+bool copyCBORByteToArray(CborValue * param, uint8_t * dest, size_t dest_size) {
+ if (!cbor_value_is_byte_string(param)) {
+ return false;
+ }
+
+ // NOTE: keep in mind that _cbor_value_copy_string tries to put a \0 at the end of the string
+ if(_cbor_value_copy_string(param, dest, &dest_size, NULL) != CborNoError) {
+
+ return false;
+ }
+
+ return true;
+}
+
+CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_EnterArray(CborValue * main_iter, CborValue * array_iter) {
+ ArrayParserState next_state = ArrayParserState::Error;
+ if (cbor_value_get_type(main_iter) == CborArrayType) {
+ if (cbor_value_enter_container(main_iter, array_iter) == CborNoError) {
+ next_state = ArrayParserState::ParseParam;
+ }
+ }
+
+ return next_state;
+}
+
+CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_LeaveArray(CborValue * main_iter, CborValue * array_iter) {
+ // Advance to the next parameter (the last one in the array)
+ if (cbor_value_advance(array_iter) != CborNoError) {
+ return ArrayParserState::Error;
+ }
+ // Leave the array
+ if (cbor_value_leave_container(main_iter, array_iter) != CborNoError) {
+ return ArrayParserState::Error;
+ }
+ return ArrayParserState::Complete;
+}
+
+/******************************************************************************
+ MESSAGE DECODE FUNCTIONS
+ ******************************************************************************/
+
+CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeThingBeginCmd(CborValue * param, Message * message) {
+ ThingBeginCmd * thingCommand = (ThingBeginCmd *) message;
+
+ // Message is composed of a single parameter, a string (thing_id)
+ if (!copyCBORStringToArray(param, thingCommand->params.thing_id, sizeof(thingCommand->params.thing_id))) {
+ return ArrayParserState::Error;
+ }
+
+ return ArrayParserState::LeaveArray;
+}
+
+CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeTimezoneCommandDown(CborValue * param, Message * message) {
+ TimezoneCommandDown * setTz = (TimezoneCommandDown *) message;
+
+ // Message is composed of 2 parameters, offset 32-bit signed integer and until 32-bit unsigned integer
+ // Get offset
+ if (cbor_value_is_integer(param)) {
+ int64_t val = 0;
+ if (cbor_value_get_int64(param, &val) == CborNoError) {
+ setTz->params.offset = static_cast(val);
+ }
+ }
+
+ // Next
+ if (cbor_value_advance(param) != CborNoError) {
+ return ArrayParserState::Error;
+ }
+
+ // Get until
+ if (cbor_value_is_integer(param)) {
+ uint64_t val = 0;
+ if (cbor_value_get_uint64(param, &val) == CborNoError) {
+ setTz->params.until = static_cast(val);
+ }
+ }
+
+ return ArrayParserState::LeaveArray;
+}
+
+CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeLastValuesUpdateCmd(CborValue * param, Message * message) {
+ LastValuesUpdateCmd * setLv = (LastValuesUpdateCmd *) message;
+
+ // Message is composed by a single parameter, a variable length byte array.
+ if (cbor_value_is_byte_string(param)) {
+ // Cortex M0 is not able to assign a value to pointed memory that is not 32bit aligned
+ // we use a support variable to cope with that
+ size_t s;
+ if (cbor_value_dup_byte_string(param, &setLv->params.last_values, &s, NULL) != CborNoError) {
+ return ArrayParserState::Error;
+ }
+
+ setLv->params.length = s;
+ }
+
+ return ArrayParserState::LeaveArray;
+}
+
+CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeOtaUpdateCmdDown(CborValue * param, Message * message) {
+ OtaUpdateCmdDown * ota = (OtaUpdateCmdDown *) message;
+
+ // Message is composed 4 parameters: id, url, initialSha, finalSha
+ if (!copyCBORByteToArray(param, ota->params.id, sizeof(ota->params.id))) {
+ return ArrayParserState::Error;
+ }
+
+ if (cbor_value_advance(param) != CborNoError) {
+ return ArrayParserState::Error;
+ }
+
+ if (!copyCBORStringToArray(param, ota->params.url, sizeof(ota->params.url))) {
+ return ArrayParserState::Error;
+ }
+
+ if (cbor_value_advance(param) != CborNoError) {
+ return ArrayParserState::Error;
+ }
+
+ if (!copyCBORByteToArray(param, ota->params.initialSha256, sizeof(ota->params.initialSha256))) {
+ return ArrayParserState::Error;
+ }
+
+ if (cbor_value_advance(param) != CborNoError) {
+ return ArrayParserState::Error;
+ }
+
+ if (!copyCBORByteToArray(param, ota->params.finalSha256, sizeof(ota->params.finalSha256))) {
+ return ArrayParserState::Error;
+ }
+
+ return ArrayParserState::LeaveArray;
+}
+
+CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_Param(CborValue * param, Message * message) {
+
+ switch (message->id)
+ {
+ case CommandId::ThingUpdateCmdId:
+ return CBORMessageDecoder::decodeThingBeginCmd(param, message);
+
+ case CommandId::TimezoneCommandDownId:
+ return CBORMessageDecoder::decodeTimezoneCommandDown(param, message);
+
+ case CommandId::LastValuesUpdateCmdId:
+ return CBORMessageDecoder::decodeLastValuesUpdateCmd(param, message);
+
+ case CommandId::OtaUpdateCmdDownId:
+ return CBORMessageDecoder::decodeOtaUpdateCmdDown(param, message);
+
+ default:
+ return ArrayParserState::MessageNotSupported;
+ }
+
+ return ArrayParserState::LeaveArray;
+}
diff --git a/src/cbor/MessageDecoder.h b/src/cbor/MessageDecoder.h
new file mode 100644
index 000000000..e8ba099a4
--- /dev/null
+++ b/src/cbor/MessageDecoder.h
@@ -0,0 +1,84 @@
+//
+// This file is part of ArduinoCloudThing
+//
+// Copyright 2019 ARDUINO SA (http://www.arduino.cc/)
+//
+// This software is released under the GNU General Public License version 3,
+// which covers the main part of ArduinoCloudThing.
+// The terms of this license can be found at:
+// https://www.gnu.org/licenses/gpl-3.0.en.html
+//
+// You can be released from the requirements of the above licenses by purchasing
+// a commercial license. Buying such a license is mandatory if you want to modify or
+// otherwise use the software for commercial activities involving the Arduino
+// software without disclosing the source code of your own applications. To purchase
+// a commercial license, send an email to license@arduino.cc.
+//
+
+#ifndef ARDUINO_CBOR_MESSAGE_DECODER_H_
+#define ARDUINO_CBOR_MESSAGE_DECODER_H_
+
+/******************************************************************************
+ INCLUDE
+ ******************************************************************************/
+
+#include
+
+#undef max
+#undef min
+#include
+
+#include "CBOR.h"
+#include "../interfaces/Decoder.h"
+#include "lib/tinycbor/cbor-lib.h"
+
+/******************************************************************************
+ CLASS DECLARATION
+ ******************************************************************************/
+
+class CBORMessageDecoder: public Decoder
+{
+public:
+ CBORMessageDecoder() { }
+ CBORMessageDecoder(CBORMessageDecoder const &) { }
+
+ /* decode a CBOR payload received from the cloud */
+ Decoder::Status decode(Message * msg, uint8_t const * const payload, size_t& length);
+
+ void setMessage(__attribute__((unused))Message* msg) {} // FIXME not implemented yet
+ Decoder::Status feed(__attribute__((unused)) uint8_t* buf, __attribute__((unused)) size_t &len) { return Decoder::Status::Error; } // FIXME not implemented yet
+
+private:
+
+ enum class DecoderState {
+ Success,
+ MessageNotSupported,
+ MalformedMessage,
+ Error
+ };
+
+ enum class ArrayParserState {
+ EnterArray,
+ ParseParam,
+ LeaveArray,
+ Complete,
+ Error,
+ MessageNotSupported
+ };
+
+ ArrayParserState handle_EnterArray(CborValue * main_iter, CborValue * array_iter);
+ ArrayParserState handle_Param(CborValue * param, Message * message);
+ ArrayParserState handle_LeaveArray(CborValue * main_iter, CborValue * array_iter);
+
+ bool ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val);
+ double convertCborHalfFloatToDouble(uint16_t const half_val);
+
+ // Message specific decoders
+ ArrayParserState decodeThingBeginCmd(CborValue * param, Message * message);
+ ArrayParserState decodeTimezoneCommandDown(CborValue * param, Message * message);
+ ArrayParserState decodeLastValuesUpdateCmd(CborValue * param, Message * message);
+ ArrayParserState decodeOtaUpdateCmdDown(CborValue * param, Message * message);
+
+};
+
+#endif /* ARDUINO_CBOR_MESSAGE_DECODER_H_ */
diff --git a/src/cbor/MessageEncoder.cpp b/src/cbor/MessageEncoder.cpp
new file mode 100644
index 000000000..e60f15079
--- /dev/null
+++ b/src/cbor/MessageEncoder.cpp
@@ -0,0 +1,190 @@
+/*
+ This file is part of ArduinoIoTCloud.
+
+ Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
+
+ This software is released under the GNU General Public License version 3,
+ which covers the main part of arduino-cli.
+ The terms of this license can be found at:
+ https://www.gnu.org/licenses/gpl-3.0.en.html
+
+ You can be released from the requirements of the above licenses by purchasing
+ a commercial license. Buying such a license is mandatory if you want to modify or
+ otherwise use the software for commercial activities involving the Arduino
+ software without disclosing the source code of your own applications. To purchase
+ a commercial license, send an email to license@arduino.cc.
+*/
+
+/******************************************************************************
+ * INCLUDE
+ ******************************************************************************/
+
+#include "CBOREncoder.h"
+
+#undef max
+#undef min
+#include
+#include
+
+#include "lib/tinycbor/cbor-lib.h"
+#include "MessageEncoder.h"
+
+/******************************************************************************
+ * PUBLIC MEMBER FUNCTIONS
+ ******************************************************************************/
+
+Encoder::Status CBORMessageEncoder::encode(Message * message, uint8_t * data, size_t& len)
+{
+ EncoderState current_state = EncoderState::EncodeTag,
+ next_state = EncoderState::Error;
+
+ CborEncoder encoder;
+ CborEncoder arrayEncoder;
+
+ cbor_encoder_init(&encoder, data, len, 0);
+
+ while (current_state != EncoderState::Complete) {
+
+ switch (current_state) {
+ case EncoderState::EncodeTag : next_state = handle_EncodeTag(&encoder, message); break;
+ case EncoderState::EncodeArray : next_state = handle_EncodeArray(&encoder, &arrayEncoder, message); break;
+ case EncoderState::EncodeParam : next_state = handle_EncodeParam(&arrayEncoder, message); break;
+ case EncoderState::CloseArray : next_state = handle_CloseArray(&encoder, &arrayEncoder); break;
+ case EncoderState::Complete : /* Nothing to do */ break;
+ case EncoderState::MessageNotSupported :
+ case EncoderState::Error : return Encoder::Status::Error;
+ }
+
+ current_state = next_state;
+ }
+
+ len = cbor_encoder_get_buffer_size(&encoder, data);
+
+ return Encoder::Status::Complete;
+}
+
+/******************************************************************************
+ PRIVATE MEMBER FUNCTIONS
+ ******************************************************************************/
+
+CBORMessageEncoder::EncoderState CBORMessageEncoder::handle_EncodeTag(CborEncoder * encoder, Message * message)
+{
+ CborTag commandTag = toCBORCommandTag(message->id);
+ if (commandTag == CBORCommandTag::CBORUnknownCmdTag16b ||
+ commandTag == CBORCommandTag::CBORUnknownCmdTag32b ||
+ commandTag == CBORCommandTag::CBORUnknownCmdTag64b ||
+ cbor_encode_tag(encoder, commandTag) != CborNoError) {
+ return EncoderState::Error;
+ }
+
+ return EncoderState::EncodeArray;
+}
+
+CBORMessageEncoder::EncoderState CBORMessageEncoder::handle_EncodeArray(CborEncoder * encoder, CborEncoder * array_encoder, Message * message)
+{
+ // Set array size based on the message id
+ size_t array_size = 0;
+ switch (message->id)
+ {
+ case CommandId::OtaBeginUpId:
+ array_size = 1;
+ break;
+ case CommandId::ThingBeginCmdId:
+ array_size = 1;
+ break;
+ case CommandId::DeviceBeginCmdId:
+ array_size = 1;
+ break;
+ case CommandId::LastValuesBeginCmdId:
+ break;
+ case CommandId::OtaProgressCmdUpId:
+ array_size = 4;
+ break;
+ case CommandId::TimezoneCommandUpId:
+ break;
+ default:
+ return EncoderState::MessageNotSupported;
+ }
+
+ // Start an array with fixed width based on message type
+ if (cbor_encoder_create_array(encoder, array_encoder, array_size) != CborNoError){
+ return EncoderState::Error;
+ }
+
+ return EncoderState::EncodeParam;
+}
+
+CBORMessageEncoder::EncoderState CBORMessageEncoder::handle_EncodeParam(CborEncoder * array_encoder, Message * message)
+{
+ CborError error = CborNoError;
+ switch (message->id)
+ {
+ case CommandId::OtaBeginUpId:
+ error = CBORMessageEncoder::encodeOtaBeginUp(array_encoder, message);
+ break;
+ case CommandId::ThingBeginCmdId:
+ error = CBORMessageEncoder::encodeThingBeginCmd(array_encoder, message);
+ break;
+ case CommandId::DeviceBeginCmdId:
+ error = CBORMessageEncoder::encodeDeviceBeginCmd(array_encoder, message);
+ break;
+ case CommandId::LastValuesBeginCmdId:
+ break;
+ case CommandId::OtaProgressCmdUpId:
+ error = CBORMessageEncoder::encodeOtaProgressCmdUp(array_encoder, message);
+ break;
+ case CommandId::TimezoneCommandUpId:
+ break;
+ default:
+ return EncoderState::MessageNotSupported;
+ }
+
+ if (error != CborNoError) {
+ return EncoderState::Error;
+ }
+
+ return EncoderState::CloseArray;
+}
+
+CBORMessageEncoder::EncoderState CBORMessageEncoder::handle_CloseArray(CborEncoder * encoder, CborEncoder * array_encoder)
+{
+
+ if (cbor_encoder_close_container(encoder, array_encoder) != CborNoError) {
+ return EncoderState::Error;
+ }
+
+ return EncoderState::Complete;
+}
+
+// Message specific encoders
+CborError CBORMessageEncoder::encodeOtaBeginUp(CborEncoder * array_encoder, Message * message)
+{
+ OtaBeginUp * otaBeginUp = (OtaBeginUp *) message;
+ CHECK_CBOR(cbor_encode_byte_string(array_encoder, otaBeginUp->params.sha, SHA256_SIZE));
+ return CborNoError;
+}
+
+CborError CBORMessageEncoder::encodeThingBeginCmd(CborEncoder * array_encoder, Message * message)
+{
+ ThingBeginCmd * thingBeginCmd = (ThingBeginCmd *) message;
+ CHECK_CBOR(cbor_encode_text_stringz(array_encoder, thingBeginCmd->params.thing_id));
+ return CborNoError;
+}
+
+CborError CBORMessageEncoder::encodeDeviceBeginCmd(CborEncoder * array_encoder, Message * message)
+{
+ DeviceBeginCmd * deviceBeginCmd = (DeviceBeginCmd *) message;
+ CHECK_CBOR(cbor_encode_text_stringz(array_encoder, deviceBeginCmd->params.lib_version));
+ return CborNoError;
+}
+
+CborError CBORMessageEncoder::encodeOtaProgressCmdUp(CborEncoder * array_encoder, Message * message)
+{
+ OtaProgressCmdUp * ota = (OtaProgressCmdUp *)message;
+ CHECK_CBOR(cbor_encode_byte_string(array_encoder, ota->params.id, ID_SIZE));
+ CHECK_CBOR(cbor_encode_simple_value(array_encoder, ota->params.state));
+ CHECK_CBOR(cbor_encode_int(array_encoder, ota->params.state_data));
+ CHECK_CBOR(cbor_encode_uint(array_encoder, ota->params.time));
+ return CborNoError;
+}
+
diff --git a/src/cbor/MessageEncoder.h b/src/cbor/MessageEncoder.h
new file mode 100644
index 000000000..6d39e1681
--- /dev/null
+++ b/src/cbor/MessageEncoder.h
@@ -0,0 +1,74 @@
+/*
+ This file is part of ArduinoIoTCloud.
+
+ Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
+
+ This software is released under the GNU General Public License version 3,
+ which covers the main part of arduino-cli.
+ The terms of this license can be found at:
+ https://www.gnu.org/licenses/gpl-3.0.en.html
+
+ You can be released from the requirements of the above licenses by purchasing
+ a commercial license. Buying such a license is mandatory if you want to modify or
+ otherwise use the software for commercial activities involving the Arduino
+ software without disclosing the source code of your own applications. To purchase
+ a commercial license, send an email to license@arduino.cc.
+*/
+
+#ifndef ARDUINO_CBOR_MESSAGE_ENCODER_H_
+#define ARDUINO_CBOR_MESSAGE_ENCODER_H_
+
+/******************************************************************************
+ * INCLUDE
+ ******************************************************************************/
+
+#include
+
+#undef max
+#undef min
+#include
+
+#include "CBOR.h"
+#include "../interfaces/Encoder.h"
+#include "lib/tinycbor/cbor-lib.h"
+
+/******************************************************************************
+ * CLASS DECLARATION
+ ******************************************************************************/
+
+class CBORMessageEncoder: public Encoder
+{
+
+public:
+ CBORMessageEncoder() { }
+ CBORMessageEncoder(CborEncoder const &) { }
+ Encoder::Status encode(Message * message, uint8_t * data, size_t& len);
+
+ void setMessage(__attribute__((unused)) Message* msg) {} // FIXME not implemented yet
+ Encoder::Status encode(__attribute__((unused)) uint8_t* buf, __attribute__((unused)) size_t& len) { return Encoder::Status::Error; } // FIXME not implemented yet
+private:
+
+ enum class EncoderState
+ {
+ EncodeTag,
+ EncodeArray,
+ EncodeParam,
+ CloseArray,
+ MessageNotSupported,
+ Complete,
+ Error
+ };
+
+ EncoderState handle_EncodeTag(CborEncoder * encoder, Message * message);
+ EncoderState handle_EncodeArray(CborEncoder * encoder, CborEncoder * array_encoder, Message * message);
+ EncoderState handle_EncodeParam(CborEncoder * array_encoder, Message * message);
+ EncoderState handle_CloseArray(CborEncoder * encoder, CborEncoder * array_encoder);
+
+ // Message specific encoders
+ CborError encodeThingBeginCmd(CborEncoder * array_encoder, Message * message);
+ CborError encodeOtaBeginUp(CborEncoder * array_encoder, Message * message);
+ CborError encodeDeviceBeginCmd(CborEncoder * array_encoder, Message * message);
+ CborError encodeOtaProgressCmdUp(CborEncoder * array_encoder, Message * message);
+};
+
+#endif /* ARDUINO_CBOR_MESSAGE_ENCODER_H_ */
diff --git a/src/interfaces/Decoder.h b/src/interfaces/Decoder.h
new file mode 100644
index 000000000..ee913414a
--- /dev/null
+++ b/src/interfaces/Decoder.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of the ArduinoIoTCloud library.
+
+ Copyright (c) 2024 Arduino SA
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#pragma once
+#include
+#include
+
+// using decoderFactory=std::function;
+
+class Decoder {
+public:
+ enum Status: uint8_t {
+ Complete,
+ InProgress,
+ Error
+ };
+
+ /**
+ * Decode a buffer into a provided message structure
+ * @param msg: the message structure that is going to be filled with data provided in the buffer
+ * @param buf: the incoming buffer that needs to be decoded
+ * @param len: the length of the incoming buffer, value will be updated with the used len of the buffer
+ * @return SUCCESS: if the message is decoded correctly
+ * ERROR: if the message wasn't decoded correctly
+ */
+ virtual Status decode(Message* msg, const uint8_t* const buf, size_t &len) = 0;
+
+ /**
+ * sets the preallocated empty message structure that needs to be decoded, before iterativley decoding it
+ * @param Message the message that needs to be decoded, msg should be big enough to accommodate all the required fields
+ */
+ virtual void setMessage(Message* msg) = 0;
+
+ /**
+ * after having the Message struct set call this function to decode he chunks of buffers provided to this call
+ * @param buffer the buffer the message will be encoded into
+ * @param len the length of the provided buffer, value will be updated with the used len of the buffer
+ * @return SUCCESS: the message is completely encoded
+ * IN_PROGRESS: the message is encoded correctly so far, provide another buffer to continue
+ * ERROR: error during the encoding of the message
+ */
+ virtual Status feed(uint8_t* buf, size_t &len) = 0;
+};
diff --git a/src/interfaces/Encoder.h b/src/interfaces/Encoder.h
new file mode 100644
index 000000000..baeb75225
--- /dev/null
+++ b/src/interfaces/Encoder.h
@@ -0,0 +1,51 @@
+/*
+ This file is part of the ArduinoIoTCloud library.
+
+ Copyright (c) 2024 Arduino SA
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#pragma once
+#include
+#include
+
+// using encoderFactory=std::function;
+
+class Encoder {
+public:
+ enum Status: uint8_t {
+ Complete,
+ InProgress,
+ Error
+ };
+
+ /**
+ * Encode a message into a buffer in a single shot
+ * @param msg: the message that needs to be encoded
+ * @param buf: the buffer the message will be encoded into
+ * @param len: the length of the provided buffer, value will be updated with the consumed len of the buffer
+ * @return SUCCESS: if the message is encoded correctly
+ * ERROR: error during the encoding of the message
+ */
+ virtual Status encode(Message* msg, uint8_t* buf, size_t& len) = 0;
+
+ /**
+ * sets the message that needs to be encoded, before iterativley encoding it
+ * @param Message the message that needs to be encoded
+ */
+ virtual void setMessage(Message* msg) = 0;
+
+ /**
+ * after having the Message struct set call this function to encode the message into a buffer
+ * this action is performed incrementally into chunks of buffers
+ * @param buf: the buffer the message will be encoded into
+ * @param len: the length of the provided buffer, value will be updated with the consumed len of the buffer
+ * @return SUCCESS: the message is completely encoded
+ * IN_PROGRESS: the message is encoded correctly so far, provide another buffer to continue
+ * ERROR: error during the encoding of the message
+ */
+ virtual Status encode(uint8_t* buf, size_t& len) = 0;
+};
\ No newline at end of file
diff --git a/src/message/Commands.h b/src/message/Commands.h
index 0f9cdac20..36ded0dc8 100644
--- a/src/message/Commands.h
+++ b/src/message/Commands.h
@@ -18,10 +18,20 @@
#include
/******************************************************************************
- * TYPEDEF
+ * DEFINE
******************************************************************************/
-enum CommandId : uint16_t {
+#define THING_ID_SIZE 37
+#define SHA256_SIZE 32
+#define URL_SIZE 256
+#define ID_SIZE 16
+#define MAX_LIB_VERSION_SIZE 10
+
+/******************************************************************************
+ TYPEDEF
+ ******************************************************************************/
+
+enum CommandId: uint32_t {
/* Device commands */
DeviceBeginCmdId,
@@ -39,6 +49,15 @@ enum CommandId : uint16_t {
/* Generic commands */
ResetCmdId,
+ /* OTA commands */
+ OtaBeginUpId,
+ OtaProgressCmdUpId,
+ OtaUpdateCmdDownId,
+
+ /* Timezone commands */
+ TimezoneCommandUpId,
+ TimezoneCommandDownId,
+
/* Unknown command id */
UnknownCmdId
};
@@ -48,3 +67,76 @@ struct Command {
};
typedef Command Message;
+
+struct LastValuesBeginCmd {
+ Command c;
+};
+
+struct OtaBeginUp {
+ Command c;
+ struct {
+ uint8_t sha [SHA256_SIZE];
+ } params;
+};
+
+struct DeviceBeginCmd {
+ Command c;
+ struct {
+ char lib_version[MAX_LIB_VERSION_SIZE];
+ } params;
+};
+
+struct OtaProgressCmdUp {
+ Command c;
+ struct {
+ uint8_t id[ID_SIZE];
+ uint8_t state;
+ int32_t state_data;
+ uint64_t time;
+ } params;
+};
+
+struct TimezoneCommandUp {
+ Command c;
+};
+
+struct OtaUpdateCmdDown {
+ Command c;
+ struct {
+ uint8_t id[ID_SIZE];
+ char url[URL_SIZE];
+ uint8_t initialSha256[SHA256_SIZE];
+ uint8_t finalSha256[SHA256_SIZE];
+ } params;
+};
+
+struct ThingBeginCmd {
+ Command c;
+ struct {
+ char thing_id[THING_ID_SIZE];
+ } params;
+};
+
+struct LastValuesUpdateCmd {
+ Command c;
+ struct {
+ uint8_t * last_values;
+ size_t length;
+ } params;
+};
+
+struct TimezoneCommandDown {
+ Command c;
+ struct {
+ int32_t offset;
+ uint32_t until;
+ } params;
+};
+
+union CommandDown {
+ struct Command c;
+ struct OtaUpdateCmdDown otaUpdateCmdDown;
+ struct ThingBeginCmd thingBeginCmd;
+ struct LastValuesUpdateCmd lastValuesUpdateCmd;
+ struct TimezoneCommandDown timezoneCommandDown;
+};
diff --git a/src/tls/AIoTCUPCert.h b/src/tls/AIoTCUPCert.h
index 330b8cb78..1e942bcb8 100644
--- a/src/tls/AIoTCUPCert.h
+++ b/src/tls/AIoTCUPCert.h
@@ -38,11 +38,12 @@
* https://www.amazontrust.com/repository/AmazonRootCA4.pem
* https://www.amazontrust.com/repository/SFSRootCAG2.pem
* https://certs.secureserver.net/repository/sf-class2-root.crt
+ * https://iot.arduino.cc
*/
static const unsigned char x509_crt_bundle[] = {
- 0x00, 0x06, 0x00, 0x3b, 0x01, 0x26, 0x30, 0x39, 0x31, 0x0b, 0x30, 0x09,
+ 0x00, 0x07, 0x00, 0x3b, 0x01, 0x26, 0x30, 0x39, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0f, 0x30,
0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x06, 0x41, 0x6d, 0x61, 0x7a,
0x6f, 0x6e, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
@@ -151,80 +152,93 @@ static const unsigned char x509_crt_bundle[] = {
0xe0, 0xe3, 0xbd, 0x5f, 0x84, 0x62, 0xf3, 0x70, 0x64, 0x33, 0xa0, 0xcb,
0x24, 0x2f, 0x70, 0xba, 0x88, 0xa1, 0x2a, 0xa0, 0x75, 0xf8, 0x81, 0xae,
0x62, 0x06, 0xc4, 0x81, 0xdb, 0x39, 0x6e, 0x29, 0xb0, 0x1e, 0xfa, 0x2e,
- 0x5c, 0x00, 0x6a, 0x01, 0x24, 0x30, 0x68, 0x31, 0x0b, 0x30, 0x09, 0x06,
- 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x25, 0x30, 0x23,
- 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66,
- 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c,
- 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
- 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x29, 0x53, 0x74,
- 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x43, 0x6c, 0x61, 0x73,
- 0x73, 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
- 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x20, 0x30, 0x0d, 0x06, 0x09, 0x2a,
- 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
- 0x01, 0x0d, 0x00, 0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00,
- 0xb7, 0x32, 0xc8, 0xfe, 0xe9, 0x71, 0xa6, 0x04, 0x85, 0xad, 0x0c, 0x11,
- 0x64, 0xdf, 0xce, 0x4d, 0xef, 0xc8, 0x03, 0x18, 0x87, 0x3f, 0xa1, 0xab,
- 0xfb, 0x3c, 0xa6, 0x9f, 0xf0, 0xc3, 0xa1, 0xda, 0xd4, 0xd8, 0x6e, 0x2b,
- 0x53, 0x90, 0xfb, 0x24, 0xa4, 0x3e, 0x84, 0xf0, 0x9e, 0xe8, 0x5f, 0xec,
- 0xe5, 0x27, 0x44, 0xf5, 0x28, 0xa6, 0x3f, 0x7b, 0xde, 0xe0, 0x2a, 0xf0,
- 0xc8, 0xaf, 0x53, 0x2f, 0x9e, 0xca, 0x05, 0x01, 0x93, 0x1e, 0x8f, 0x66,
- 0x1c, 0x39, 0xa7, 0x4d, 0xfa, 0x5a, 0xb6, 0x73, 0x04, 0x25, 0x66, 0xeb,
- 0x77, 0x7f, 0xe7, 0x59, 0xc6, 0x4a, 0x99, 0x25, 0x14, 0x54, 0xeb, 0x26,
- 0xc7, 0xf3, 0x7f, 0x19, 0xd5, 0x30, 0x70, 0x8f, 0xaf, 0xb0, 0x46, 0x2a,
- 0xff, 0xad, 0xeb, 0x29, 0xed, 0xd7, 0x9f, 0xaa, 0x04, 0x87, 0xa3, 0xd4,
- 0xf9, 0x89, 0xa5, 0x34, 0x5f, 0xdb, 0x43, 0x91, 0x82, 0x36, 0xd9, 0x66,
- 0x3c, 0xb1, 0xb8, 0xb9, 0x82, 0xfd, 0x9c, 0x3a, 0x3e, 0x10, 0xc8, 0x3b,
- 0xef, 0x06, 0x65, 0x66, 0x7a, 0x9b, 0x19, 0x18, 0x3d, 0xff, 0x71, 0x51,
- 0x3c, 0x30, 0x2e, 0x5f, 0xbe, 0x3d, 0x77, 0x73, 0xb2, 0x5d, 0x06, 0x6c,
- 0xc3, 0x23, 0x56, 0x9a, 0x2b, 0x85, 0x26, 0x92, 0x1c, 0xa7, 0x02, 0xb3,
- 0xe4, 0x3f, 0x0d, 0xaf, 0x08, 0x79, 0x82, 0xb8, 0x36, 0x3d, 0xea, 0x9c,
- 0xd3, 0x35, 0xb3, 0xbc, 0x69, 0xca, 0xf5, 0xcc, 0x9d, 0xe8, 0xfd, 0x64,
- 0x8d, 0x17, 0x80, 0x33, 0x6e, 0x5e, 0x4a, 0x5d, 0x99, 0xc9, 0x1e, 0x87,
- 0xb4, 0x9d, 0x1a, 0xc0, 0xd5, 0x6e, 0x13, 0x35, 0x23, 0x5e, 0xdf, 0x9b,
- 0x5f, 0x3d, 0xef, 0xd6, 0xf7, 0x76, 0xc2, 0xea, 0x3e, 0xbb, 0x78, 0x0d,
- 0x1c, 0x42, 0x67, 0x6b, 0x04, 0xd8, 0xf8, 0xd6, 0xda, 0x6f, 0x8b, 0xf2,
- 0x44, 0xa0, 0x01, 0xab, 0x02, 0x01, 0x03, 0x00, 0x9b, 0x01, 0x26, 0x30,
- 0x81, 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
- 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08,
- 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30,
- 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74,
- 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03,
+ 0x5c, 0x00, 0x47, 0x00, 0x5b, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x41, 0x72, 0x64, 0x75, 0x69,
+ 0x6e, 0x6f, 0x20, 0x4c, 0x4c, 0x43, 0x20, 0x55, 0x53, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x02, 0x49, 0x54, 0x31, 0x10,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x07, 0x41, 0x72, 0x64,
+ 0x75, 0x69, 0x6e, 0x6f, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
+ 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+ 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x6d, 0x77, 0x6c, 0x5a, 0xcf,
+ 0x61, 0x1c, 0x7d, 0x44, 0x98, 0x51, 0xf2, 0x5e, 0xe1, 0x02, 0x40, 0x77,
+ 0xb7, 0x9c, 0xbd, 0x49, 0xa2, 0xa3, 0x8c, 0x4e, 0xab, 0x5e, 0x98, 0xac,
+ 0x82, 0xfc, 0x69, 0x5b, 0x44, 0x22, 0x77, 0xb4, 0x4d, 0x2e, 0x8e, 0xdf,
+ 0x2a, 0x71, 0xc1, 0x39, 0x6c, 0xd6, 0x39, 0x14, 0xbd, 0xd9, 0x6b, 0x18,
+ 0x4b, 0x4b, 0xec, 0xb3, 0xd5, 0xee, 0x42, 0x89, 0x89, 0x55, 0x22, 0x00,
+ 0x6a, 0x01, 0x24, 0x30, 0x68, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65,
0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67,
- 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x3b, 0x30,
- 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x32, 0x53, 0x74, 0x61, 0x72,
- 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
- 0x65, 0x73, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74,
- 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
- 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82,
- 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
- 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd5, 0x0c, 0x3a, 0xc4, 0x2a,
- 0xf9, 0x4e, 0xe2, 0xf5, 0xbe, 0x19, 0x97, 0x5f, 0x8e, 0x88, 0x53, 0xb1,
- 0x1f, 0x3f, 0xcb, 0xcf, 0x9f, 0x20, 0x13, 0x6d, 0x29, 0x3a, 0xc8, 0x0f,
- 0x7d, 0x3c, 0xf7, 0x6b, 0x76, 0x38, 0x63, 0xd9, 0x36, 0x60, 0xa8, 0x9b,
- 0x5e, 0x5c, 0x00, 0x80, 0xb2, 0x2f, 0x59, 0x7f, 0xf6, 0x87, 0xf9, 0x25,
- 0x43, 0x86, 0xe7, 0x69, 0x1b, 0x52, 0x9a, 0x90, 0xe1, 0x71, 0xe3, 0xd8,
- 0x2d, 0x0d, 0x4e, 0x6f, 0xf6, 0xc8, 0x49, 0xd9, 0xb6, 0xf3, 0x1a, 0x56,
- 0xae, 0x2b, 0xb6, 0x74, 0x14, 0xeb, 0xcf, 0xfb, 0x26, 0xe3, 0x1a, 0xba,
- 0x1d, 0x96, 0x2e, 0x6a, 0x3b, 0x58, 0x94, 0x89, 0x47, 0x56, 0xff, 0x25,
- 0xa0, 0x93, 0x70, 0x53, 0x83, 0xda, 0x84, 0x74, 0x14, 0xc3, 0x67, 0x9e,
- 0x04, 0x68, 0x3a, 0xdf, 0x8e, 0x40, 0x5a, 0x1d, 0x4a, 0x4e, 0xcf, 0x43,
- 0x91, 0x3b, 0xe7, 0x56, 0xd6, 0x00, 0x70, 0xcb, 0x52, 0xee, 0x7b, 0x7d,
- 0xae, 0x3a, 0xe7, 0xbc, 0x31, 0xf9, 0x45, 0xf6, 0xc2, 0x60, 0xcf, 0x13,
- 0x59, 0x02, 0x2b, 0x80, 0xcc, 0x34, 0x47, 0xdf, 0xb9, 0xde, 0x90, 0x65,
- 0x6d, 0x02, 0xcf, 0x2c, 0x91, 0xa6, 0xa6, 0xe7, 0xde, 0x85, 0x18, 0x49,
- 0x7c, 0x66, 0x4e, 0xa3, 0x3a, 0x6d, 0xa9, 0xb5, 0xee, 0x34, 0x2e, 0xba,
- 0x0d, 0x03, 0xb8, 0x33, 0xdf, 0x47, 0xeb, 0xb1, 0x6b, 0x8d, 0x25, 0xd9,
- 0x9b, 0xce, 0x81, 0xd1, 0x45, 0x46, 0x32, 0x96, 0x70, 0x87, 0xde, 0x02,
- 0x0e, 0x49, 0x43, 0x85, 0xb6, 0x6c, 0x73, 0xbb, 0x64, 0xea, 0x61, 0x41,
- 0xac, 0xc9, 0xd4, 0x54, 0xdf, 0x87, 0x2f, 0xc7, 0x22, 0xb2, 0x26, 0xcc,
- 0x9f, 0x59, 0x54, 0x68, 0x9f, 0xfc, 0xbe, 0x2a, 0x2f, 0xc4, 0x55, 0x1c,
- 0x75, 0x40, 0x60, 0x17, 0x85, 0x02, 0x55, 0x39, 0x8b, 0x7f, 0x05, 0x02,
- 0x03, 0x01, 0x00, 0x01
+ 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30,
+ 0x30, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x29, 0x53, 0x74, 0x61, 0x72,
+ 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x30, 0x82, 0x01, 0x20, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0d,
+ 0x00, 0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb7, 0x32,
+ 0xc8, 0xfe, 0xe9, 0x71, 0xa6, 0x04, 0x85, 0xad, 0x0c, 0x11, 0x64, 0xdf,
+ 0xce, 0x4d, 0xef, 0xc8, 0x03, 0x18, 0x87, 0x3f, 0xa1, 0xab, 0xfb, 0x3c,
+ 0xa6, 0x9f, 0xf0, 0xc3, 0xa1, 0xda, 0xd4, 0xd8, 0x6e, 0x2b, 0x53, 0x90,
+ 0xfb, 0x24, 0xa4, 0x3e, 0x84, 0xf0, 0x9e, 0xe8, 0x5f, 0xec, 0xe5, 0x27,
+ 0x44, 0xf5, 0x28, 0xa6, 0x3f, 0x7b, 0xde, 0xe0, 0x2a, 0xf0, 0xc8, 0xaf,
+ 0x53, 0x2f, 0x9e, 0xca, 0x05, 0x01, 0x93, 0x1e, 0x8f, 0x66, 0x1c, 0x39,
+ 0xa7, 0x4d, 0xfa, 0x5a, 0xb6, 0x73, 0x04, 0x25, 0x66, 0xeb, 0x77, 0x7f,
+ 0xe7, 0x59, 0xc6, 0x4a, 0x99, 0x25, 0x14, 0x54, 0xeb, 0x26, 0xc7, 0xf3,
+ 0x7f, 0x19, 0xd5, 0x30, 0x70, 0x8f, 0xaf, 0xb0, 0x46, 0x2a, 0xff, 0xad,
+ 0xeb, 0x29, 0xed, 0xd7, 0x9f, 0xaa, 0x04, 0x87, 0xa3, 0xd4, 0xf9, 0x89,
+ 0xa5, 0x34, 0x5f, 0xdb, 0x43, 0x91, 0x82, 0x36, 0xd9, 0x66, 0x3c, 0xb1,
+ 0xb8, 0xb9, 0x82, 0xfd, 0x9c, 0x3a, 0x3e, 0x10, 0xc8, 0x3b, 0xef, 0x06,
+ 0x65, 0x66, 0x7a, 0x9b, 0x19, 0x18, 0x3d, 0xff, 0x71, 0x51, 0x3c, 0x30,
+ 0x2e, 0x5f, 0xbe, 0x3d, 0x77, 0x73, 0xb2, 0x5d, 0x06, 0x6c, 0xc3, 0x23,
+ 0x56, 0x9a, 0x2b, 0x85, 0x26, 0x92, 0x1c, 0xa7, 0x02, 0xb3, 0xe4, 0x3f,
+ 0x0d, 0xaf, 0x08, 0x79, 0x82, 0xb8, 0x36, 0x3d, 0xea, 0x9c, 0xd3, 0x35,
+ 0xb3, 0xbc, 0x69, 0xca, 0xf5, 0xcc, 0x9d, 0xe8, 0xfd, 0x64, 0x8d, 0x17,
+ 0x80, 0x33, 0x6e, 0x5e, 0x4a, 0x5d, 0x99, 0xc9, 0x1e, 0x87, 0xb4, 0x9d,
+ 0x1a, 0xc0, 0xd5, 0x6e, 0x13, 0x35, 0x23, 0x5e, 0xdf, 0x9b, 0x5f, 0x3d,
+ 0xef, 0xd6, 0xf7, 0x76, 0xc2, 0xea, 0x3e, 0xbb, 0x78, 0x0d, 0x1c, 0x42,
+ 0x67, 0x6b, 0x04, 0xd8, 0xf8, 0xd6, 0xda, 0x6f, 0x8b, 0xf2, 0x44, 0xa0,
+ 0x01, 0xab, 0x02, 0x01, 0x03, 0x00, 0x9b, 0x01, 0x26, 0x30, 0x81, 0x98,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07,
+ 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73,
+ 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64,
+ 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65,
+ 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x3b, 0x30, 0x39, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x32, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69,
+ 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
+ 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xd5, 0x0c, 0x3a, 0xc4, 0x2a, 0xf9, 0x4e,
+ 0xe2, 0xf5, 0xbe, 0x19, 0x97, 0x5f, 0x8e, 0x88, 0x53, 0xb1, 0x1f, 0x3f,
+ 0xcb, 0xcf, 0x9f, 0x20, 0x13, 0x6d, 0x29, 0x3a, 0xc8, 0x0f, 0x7d, 0x3c,
+ 0xf7, 0x6b, 0x76, 0x38, 0x63, 0xd9, 0x36, 0x60, 0xa8, 0x9b, 0x5e, 0x5c,
+ 0x00, 0x80, 0xb2, 0x2f, 0x59, 0x7f, 0xf6, 0x87, 0xf9, 0x25, 0x43, 0x86,
+ 0xe7, 0x69, 0x1b, 0x52, 0x9a, 0x90, 0xe1, 0x71, 0xe3, 0xd8, 0x2d, 0x0d,
+ 0x4e, 0x6f, 0xf6, 0xc8, 0x49, 0xd9, 0xb6, 0xf3, 0x1a, 0x56, 0xae, 0x2b,
+ 0xb6, 0x74, 0x14, 0xeb, 0xcf, 0xfb, 0x26, 0xe3, 0x1a, 0xba, 0x1d, 0x96,
+ 0x2e, 0x6a, 0x3b, 0x58, 0x94, 0x89, 0x47, 0x56, 0xff, 0x25, 0xa0, 0x93,
+ 0x70, 0x53, 0x83, 0xda, 0x84, 0x74, 0x14, 0xc3, 0x67, 0x9e, 0x04, 0x68,
+ 0x3a, 0xdf, 0x8e, 0x40, 0x5a, 0x1d, 0x4a, 0x4e, 0xcf, 0x43, 0x91, 0x3b,
+ 0xe7, 0x56, 0xd6, 0x00, 0x70, 0xcb, 0x52, 0xee, 0x7b, 0x7d, 0xae, 0x3a,
+ 0xe7, 0xbc, 0x31, 0xf9, 0x45, 0xf6, 0xc2, 0x60, 0xcf, 0x13, 0x59, 0x02,
+ 0x2b, 0x80, 0xcc, 0x34, 0x47, 0xdf, 0xb9, 0xde, 0x90, 0x65, 0x6d, 0x02,
+ 0xcf, 0x2c, 0x91, 0xa6, 0xa6, 0xe7, 0xde, 0x85, 0x18, 0x49, 0x7c, 0x66,
+ 0x4e, 0xa3, 0x3a, 0x6d, 0xa9, 0xb5, 0xee, 0x34, 0x2e, 0xba, 0x0d, 0x03,
+ 0xb8, 0x33, 0xdf, 0x47, 0xeb, 0xb1, 0x6b, 0x8d, 0x25, 0xd9, 0x9b, 0xce,
+ 0x81, 0xd1, 0x45, 0x46, 0x32, 0x96, 0x70, 0x87, 0xde, 0x02, 0x0e, 0x49,
+ 0x43, 0x85, 0xb6, 0x6c, 0x73, 0xbb, 0x64, 0xea, 0x61, 0x41, 0xac, 0xc9,
+ 0xd4, 0x54, 0xdf, 0x87, 0x2f, 0xc7, 0x22, 0xb2, 0x26, 0xcc, 0x9f, 0x59,
+ 0x54, 0x68, 0x9f, 0xfc, 0xbe, 0x2a, 0x2f, 0xc4, 0x55, 0x1c, 0x75, 0x40,
+ 0x60, 0x17, 0x85, 0x02, 0x55, 0x39, 0x8b, 0x7f, 0x05, 0x02, 0x03, 0x01,
+ 0x00, 0x01
};
-unsigned int x509_crt_bundle_len = 2164;
#elif defined (ARDUINO_EDGE_CONTROL)
/*
diff --git a/src/tls/BearSSLClient.cpp b/src/tls/BearSSLClient.cpp
index cdc58794f..74181ff3f 100644
--- a/src/tls/BearSSLClient.cpp
+++ b/src/tls/BearSSLClient.cpp
@@ -34,11 +34,19 @@
#include "BearSSLClient.h"
-extern "C" void aiotc_client_profile_init(br_ssl_client_context *cc, br_x509_minimal_context *xc, const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num);
-
-
-bool BearSSLClient::_sslio_closing = false;
+BearSSLClient::BearSSLClient() :
+ _noSNI(false),
+ _get_time_func(nullptr),
+ _sslio_closing(false)
+{
+ _ecKey.curve = 0;
+ _ecKey.x = NULL;
+ _ecKey.xlen = 0;
+ _ecCert.data = NULL;
+ _ecCert.data_len = 0;
+ _ecCertDynamic = false;
+}
BearSSLClient::BearSSLClient(Client* client, const br_x509_trust_anchor* myTAs, int myNumTAs, GetTimeCallbackFunc func) :
_client(client),
@@ -160,7 +168,7 @@ void BearSSLClient::stop()
{
if (_client->connected()) {
if ((br_ssl_engine_current_state(&_sc.eng) & BR_SSL_CLOSED) == 0) {
- BearSSLClient::_sslio_closing = true;
+ _sslio_closing = true;
br_sslio_close(&_ioc);
}
@@ -185,7 +193,7 @@ uint8_t BearSSLClient::connected()
BearSSLClient::operator bool()
{
- return (*_client);
+ return (*_client);
}
void BearSSLClient::setInsecure(SNI insecure)
@@ -266,8 +274,8 @@ int BearSSLClient::connectSSL(const char* host)
/* Ensure this flag is cleared so we don't terminate a just starting connection. */
_sslio_closing = false;
- // initialize client context with all necessary algorithms and hardcoded trust anchors.
- aiotc_client_profile_init(&_sc, &_xc, _TAs, _numTAs);
+ // initialize client context with enabled algorithms and trust anchors
+ _br_ssl_client_init_function(&_sc, &_xc, _TAs, _numTAs);
br_ssl_engine_set_buffers_bidi(&_sc.eng, _ibuf, sizeof(_ibuf), _obuf, sizeof(_obuf));
@@ -278,7 +286,7 @@ int BearSSLClient::connectSSL(const char* host)
// ECC508 random success, add custom ECDSA vfry and EC sign
br_ssl_engine_set_ecdsa(&_sc.eng, eccX08_vrfy_asn1);
br_x509_minimal_set_ecdsa(&_xc, br_ssl_engine_get_ec(&_sc.eng), br_ssl_engine_get_ecdsa(&_sc.eng));
-
+
// enable client auth using the ECCX08
if (_ecCert.data_len && _ecKey.xlen) {
br_ssl_client_set_single_ec(&_sc, &_ecCert, 1, &_ecKey, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, BR_KEYTYPE_EC, br_ec_get_default(), eccX08_sign_asn1);
@@ -302,7 +310,7 @@ int BearSSLClient::connectSSL(const char* host)
br_x509_minimal_set_time(&_xc, days, sec);
// use our own socket I/O operations
- br_sslio_init(&_ioc, &_sc.eng, BearSSLClient::clientRead, _client, BearSSLClient::clientWrite, _client);
+ br_sslio_init(&_ioc, &_sc.eng, BearSSLClient::clientRead, this, BearSSLClient::clientWrite, this);
br_sslio_flush(&_ioc);
@@ -323,12 +331,13 @@ int BearSSLClient::connectSSL(const char* host)
int BearSSLClient::clientRead(void *ctx, unsigned char *buf, size_t len)
{
- if (BearSSLClient::_sslio_closing) {
+ BearSSLClient* bc = (BearSSLClient*)ctx;
+ Client* c = bc->_client;
+
+ if(bc->_sslio_closing) {
return -1;
}
- Client* c = (Client*)ctx;
-
if (!c->connected()) {
return -1;
}
@@ -341,7 +350,7 @@ int BearSSLClient::clientRead(void *ctx, unsigned char *buf, size_t len)
#ifdef DEBUGSERIAL
DEBUGSERIAL.print("BearSSLClient::clientRead - ");
DEBUGSERIAL.print(result);
- DEBUGSERIAL.print(" - ");
+ DEBUGSERIAL.print(" - ");
for (size_t i = 0; i < result; i++) {
byte b = buf[i];
@@ -358,12 +367,13 @@ int BearSSLClient::clientRead(void *ctx, unsigned char *buf, size_t len)
int BearSSLClient::clientWrite(void *ctx, const unsigned char *buf, size_t len)
{
- if (BearSSLClient::_sslio_closing) {
+ BearSSLClient* bc = (BearSSLClient*)ctx;
+ Client* c = bc->_client;
+
+ if(bc->_sslio_closing) {
return -1;
}
- Client* c = (Client*)ctx;
-
#ifdef DEBUGSERIAL
DEBUGSERIAL.print("BearSSLClient::clientWrite - ");
DEBUGSERIAL.print(len);
diff --git a/src/tls/BearSSLClient.h b/src/tls/BearSSLClient.h
index 457ef92f0..32fb18727 100644
--- a/src/tls/BearSSLClient.h
+++ b/src/tls/BearSSLClient.h
@@ -48,11 +48,14 @@ class BearSSLClient : public Client {
public:
BearSSLClient(Client* client, const br_x509_trust_anchor* myTAs, int myNumTAs, GetTimeCallbackFunc func);
+ BearSSLClient();
virtual ~BearSSLClient();
inline void setClient(Client& client) { _client = &client; }
-
+ inline void setProfile(void(*client_init_function)(br_ssl_client_context *cc, br_x509_minimal_context *xc, const br_x509_trust_anchor *trust_anchors, size_t trustrust_anchorst_anchors_num)) { _br_ssl_client_init_function = client_init_function; }
+ inline void setTrustAnchors(const br_x509_trust_anchor* myTAs, int myNumTAs) { _TAs = myTAs; _numTAs = myNumTAs; }
+ inline void onGetTime(GetTimeCallbackFunc callback) { _get_time_func = callback;}
virtual int connect(IPAddress ip, uint16_t port);
virtual int connect(const char* host, uint16_t port);
@@ -97,12 +100,19 @@ class BearSSLClient : public Client {
br_x509_certificate _ecCert;
bool _ecCertDynamic;
- static bool _sslio_closing;
+ /* FIXME By introducing _sslio_closing we are overriding the correct behaviour of SSL protocol
+ * where the client is require to correctly close the ssl session. In the way we use it
+ * we are blocking bearssl from sending any data on the underlying level, this fix requires
+ * further investigation in the bearssl code
+ */
+ bool _sslio_closing;
br_ssl_client_context _sc;
br_x509_minimal_context _xc;
unsigned char _ibuf[BEAR_SSL_CLIENT_IBUF_SIZE];
unsigned char _obuf[BEAR_SSL_CLIENT_OBUF_SIZE];
br_sslio_context _ioc;
+
+ void (*_br_ssl_client_init_function)(br_ssl_client_context *cc, br_x509_minimal_context *xc, const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num);
};
#endif /* #ifdef BOARD_HAS_ECCX08 */
diff --git a/src/tls/utility/TLSClientMqtt.cpp b/src/tls/utility/TLSClientMqtt.cpp
new file mode 100644
index 000000000..d5f5830d1
--- /dev/null
+++ b/src/tls/utility/TLSClientMqtt.cpp
@@ -0,0 +1,61 @@
+/*
+ This file is part of the ArduinoIoTCloud library.
+
+ Copyright (c) 2024 Arduino SA
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#include
+
+#ifdef HAS_TCP
+
+#include "TLSClientMqtt.h"
+
+#if defined(BOARD_HAS_SECRET_KEY)
+ #include "tls/AIoTCUPCert.h"
+#endif
+
+#if defined(BOARD_HAS_SE050) || defined(BOARD_HAS_SOFTSE)
+ #include "tls/AIoTCSSCert.h"
+#endif
+
+#ifdef BOARD_HAS_ECCX08
+ #include "tls/BearSSLTrustAnchors.h"
+ extern "C" {
+ void aiotc_client_profile_init(br_ssl_client_context *cc,
+ br_x509_minimal_context *xc,
+ const br_x509_trust_anchor *trust_anchors,
+ size_t trust_anchors_num);
+ unsigned long getTime();
+ }
+#endif
+
+void TLSClientMqtt::begin(ConnectionHandler & connection) {
+
+#if defined(BOARD_HAS_OFFLOADED_ECCX08)
+
+#elif defined(BOARD_HAS_ECCX08)
+ setClient(connection.getClient());
+ setProfile(aiotc_client_profile_init);
+ setTrustAnchors(ArduinoIoTCloudTrustAnchor, ArduinoIoTCloudTrustAnchor_NUM);
+ onGetTime(getTime);
+#elif defined(ARDUINO_PORTENTA_C33)
+ setClient(connection.getClient());
+ setCACert(AIoTSSCert);
+#elif defined(ARDUINO_NICLA_VISION)
+ appendCustomCACert(AIoTSSCert);
+#elif defined(ARDUINO_EDGE_CONTROL)
+ appendCustomCACert(AIoTUPCert);
+#elif defined(ARDUINO_UNOR4_WIFI)
+
+#elif defined(ARDUINO_ARCH_ESP32)
+ setCACertBundle(x509_crt_bundle);
+#elif defined(ARDUINO_ARCH_ESP8266)
+ setInsecure();
+#endif
+}
+
+#endif
diff --git a/src/tls/utility/TLSClientMqtt.h b/src/tls/utility/TLSClientMqtt.h
new file mode 100644
index 000000000..837e76dec
--- /dev/null
+++ b/src/tls/utility/TLSClientMqtt.h
@@ -0,0 +1,69 @@
+/*
+ This file is part of the ArduinoIoTCloud library.
+
+ Copyright (c) 2024 Arduino SA
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#pragma once
+
+#include
+#include
+
+#if defined(BOARD_HAS_OFFLOADED_ECCX08)
+ /*
+ * Arduino MKR WiFi1010 - WiFi
+ * Arduino NANO 33 IoT - WiFi
+ */
+ #include "WiFiSSLClient.h"
+ class TLSClientMqtt : public WiFiBearSSLClient {
+#elif defined(BOARD_HAS_ECCX08)
+ /*
+ * Arduino MKR GSM 1400
+ * Arduino MKR NB 1500
+ * Arduino Portenta H7
+ * Arduino Giga R1
+ * OPTA
+ */
+ #include
+ class TLSClientMqtt : public BearSSLClient {
+#elif defined(ARDUINO_PORTENTA_C33)
+ /*
+ * Arduino Portenta C33
+ */
+ #include
+ class TLSClientMqtt : public SSLClient {
+#elif defined(ARDUINO_NICLA_VISION)
+ /*
+ * Arduino Nicla Vision
+ */
+ #include
+ class TLSClientMqtt : public WiFiSSLSE050Client {
+#elif defined(ARDUINO_EDGE_CONTROL)
+ /*
+ * Arduino Edge Control
+ */
+ #include
+ class TLSClientMqtt : public GSMSSLClient {
+#elif defined(ARDUINO_UNOR4_WIFI)
+ /*
+ * Arduino UNO R4 WiFi
+ */
+ #include
+ class TLSClientMqtt : public WiFiSSLClient {
+#elif defined(BOARD_ESP)
+ /*
+ * ESP32*
+ * ESP82*
+ */
+ #include
+ class TLSClientMqtt : public WiFiClientSecure {
+#endif
+
+public:
+ void begin(ConnectionHandler & connection);
+
+};
diff --git a/src/tls/utility/TLSClientOta.cpp b/src/tls/utility/TLSClientOta.cpp
new file mode 100644
index 000000000..f020a2ac4
--- /dev/null
+++ b/src/tls/utility/TLSClientOta.cpp
@@ -0,0 +1,60 @@
+/*
+ This file is part of the ArduinoIoTCloud library.
+
+ Copyright (c) 2024 Arduino SA
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#include
+
+#if defined(HAS_TCP) && OTA_ENABLED
+
+#include "TLSClientOta.h"
+
+#if defined(BOARD_HAS_SECRET_KEY)
+ #include "tls/AIoTCUPCert.h"
+#endif
+
+#if defined(BOARD_HAS_SE050) || defined(BOARD_HAS_SOFTSE)
+ #include "tls/AIoTCSSCert.h"
+#endif
+
+#ifdef BOARD_HAS_ECCX08
+ #include "tls/BearSSLTrustAnchors.h"
+ extern "C" {
+ void aiotc_client_profile_init(br_ssl_client_context *cc,
+ br_x509_minimal_context *xc,
+ const br_x509_trust_anchor *trust_anchors,
+ size_t trust_anchors_num);
+ unsigned long getTime();
+ }
+#endif
+
+void TLSClientOta::begin(ConnectionHandler &connection) {
+#if defined(BOARD_HAS_OFFLOADED_ECCX08)
+
+#elif defined(BOARD_HAS_ECCX08)
+ setClient(*getNewClient(connection.getInterface()));
+ setProfile(aiotc_client_profile_init);
+ setTrustAnchors(ArduinoIoTCloudTrustAnchor, ArduinoIoTCloudTrustAnchor_NUM);
+ onGetTime(getTime);
+#elif defined(ARDUINO_PORTENTA_C33)
+ setClient(*getNewClient(connection.getInterface()));
+ setCACert(AIoTSSCert);
+#elif defined(ARDUINO_NICLA_VISION)
+ appendCustomCACert(AIoTSSCert);
+#elif defined(ARDUINO_EDGE_CONTROL)
+ appendCustomCACert(AIoTUPCert);
+#elif defined(ARDUINO_UNOR4_WIFI)
+
+#elif defined(ARDUINO_ARCH_ESP32)
+ setCACertBundle(x509_crt_bundle);
+#elif defined(ARDUINO_ARCH_ESP8266)
+ setInsecure();
+#endif
+}
+
+#endif
diff --git a/src/tls/utility/TLSClientOta.h b/src/tls/utility/TLSClientOta.h
new file mode 100644
index 000000000..3e76433ab
--- /dev/null
+++ b/src/tls/utility/TLSClientOta.h
@@ -0,0 +1,96 @@
+/*
+ This file is part of the ArduinoIoTCloud library.
+
+ Copyright (c) 2024 Arduino SA
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#pragma once
+
+#include
+#include
+
+#if defined(BOARD_HAS_OFFLOADED_ECCX08)
+ /*
+ * Arduino MKR WiFi1010 - WiFi
+ * Arduino NANO 33 IoT - WiFi
+ */
+ #include "WiFiSSLClient.h"
+ class TLSClientOta : public WiFiBearSSLClient {
+#elif defined(BOARD_HAS_ECCX08)
+ /*
+ * Arduino MKR GSM 1400
+ * Arduino MKR NB 1500
+ * Arduino Portenta H7
+ * Arduino Giga R1
+ * OPTA
+ */
+ #include
+ class TLSClientOta : public BearSSLClient {
+#elif defined(ARDUINO_PORTENTA_C33)
+ /*
+ * Arduino Portenta C33
+ */
+ #include
+ class TLSClientOta : public SSLClient {
+#elif defined(ARDUINO_NICLA_VISION)
+ /*
+ * Arduino Nicla Vision
+ */
+ #include
+ class TLSClientOta : public WiFiSSLSE050Client {
+#elif defined(ARDUINO_EDGE_CONTROL)
+ /*
+ * Arduino Edge Control
+ */
+ #include
+ class TLSClientOta : public GSMSSLClient {
+#elif defined(ARDUINO_UNOR4_WIFI)
+ /*
+ * Arduino UNO R4 WiFi
+ */
+ #include
+ class TLSClientOta : public WiFiSSLClient {
+#elif defined(BOARD_ESP)
+ /*
+ * ESP32*
+ * ESP82*
+ */
+ #include
+ class TLSClientOta : public WiFiClientSecure {
+#endif
+
+public:
+ void begin(ConnectionHandler & connection);
+
+private:
+ inline Client* getNewClient(NetworkAdapter net) {
+ switch(net) {
+#ifdef BOARD_HAS_WIFI
+ case NetworkAdapter::WIFI:
+ return new WiFiClient();
+#endif // BOARD_HAS_WIFI
+#ifdef BOARD_HAS_ETHERNET
+ case NetworkAdapter::ETHERNET:
+ return new EthernetClient();
+#endif // BOARD_HAS_ETHERNET
+#ifdef BOARD_HAS_NB
+ case NetworkAdapter::NB:
+ return new NBClient();
+#endif // BOARD_HAS_NB
+#ifdef BOARD_HAS_GSM
+ case NetworkAdapter::GSM:
+ return new GSMClient();
+#endif // BOARD_HAS_GSM
+#ifdef BOARD_HAS_CATM1_NBIOT
+ case NetworkAdapter::CATM1:
+ return new GSMClient();
+#endif // BOARD_HAS_CATM1_NBIOT
+ default:
+ return nullptr;
+ }
+ }
+};