Skip to content

Commit

Permalink
Merge branch 'rainmaker-write-node-id' into 'main'
Browse files Browse the repository at this point in the history
rainmaker: Cluster revision update

See merge request app-frameworks/esp-matter!508
  • Loading branch information
shahpiyushv committed Oct 11, 2023
2 parents 63e79f0 + 462e58a commit 79dfcc7
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 48 deletions.
175 changes: 127 additions & 48 deletions components/esp_matter_rainmaker/esp_matter_rainmaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,53 @@
#include <esp_matter_rainmaker.h>
#include <esp_rmaker_core.h>
#include <esp_rmaker_user_mapping.h>
#include <app/util/attribute-storage.h>
#include <app/AttributeAccessInterface.h>

#define ESP_MATTER_RAINMAKER_COMMAND_LIMIT 5 /* This command can be called 5 times per reboot */
#define ESP_MATTER_RAINMAKER_MAX_DATA_LEN 40
#define ESP_MATTER_RAINMAKER_MAX_SIGN_DATA_LEN 40
#define ESP_MATTER_RAINMAKER_MAX_NODE_ID_LEN 40
#define ESP_MATTER_RAINMAKER_MAX_CHALLENGE_LEN 150
#define ESP_MATTER_RAINMAKER_COMMAND_LIMIT 5 /* This command can be called 5 times per reboot */
#define ESP_MATTER_RAINMAKER_MAX_DATA_LEN 40
#define ESP_MATTER_RAINMAKER_MAX_CHALLENGE_LEN 40
#define ESP_MATTER_RAINMAKER_MAX_NODE_ID_LEN 40
#define ESP_MATTER_RAINMAKER_MAX_CHALLENGE_RESPONSE_LEN 150

using namespace chip::app;
using namespace chip::app::Clusters;

#define RAINMAKER_CLUSTER_REVISION 2

static const char *TAG = "esp_matter_rainmaker";

namespace esp_matter {

// Rainmaker cluster data model definition
namespace cluster {
namespace rainmaker {
static constexpr chip::EndpointId endpoint_id = 0x00000000; /* Same as root node endpoint. This will always be
endpoint_id 0. */
static constexpr chip::ClusterId Id = 0x131BFC00; /* 0x131B == manufacturer code.
0xFCOO == start of manufacturer specific cluster_id */

namespace attribute {

namespace status {
static constexpr chip::AttributeId Id = 0x00000000;
} /* status */
namespace node_id {

namespace rmaker_node_id {
static constexpr chip::AttributeId Id = 0x00000001;
} /* node_id */
namespace challenge {
} /* rmaker_node_id */

// This attribute is of access type read-only.
namespace challenge_response {
static constexpr chip::AttributeId Id = 0x00000002;
} /* challenge_response */

// This attribute is of access type write.
// When a client writes to this attribute, it signs the incoming payload and
// subsequently stores that signature in the challenge_response attribute.
namespace challenge {
static constexpr chip::AttributeId Id = 0x00000003;
} /* challenge */

} /* attribute */

namespace command {
Expand Down Expand Up @@ -128,24 +145,24 @@ static esp_err_t status_attribute_update(bool status)
return attribute::update(endpoint_id, cluster_id, attribute_id, &val);
}

static esp_err_t node_id_attribute_update(char *node_id)
static esp_err_t rmaker_node_id_attribute_update(char *rmaker_node_id)
{
if (!node_id) {
if (!rmaker_node_id) {
return ESP_ERR_INVALID_ARG;
}
uint16_t endpoint_id = cluster::rainmaker::endpoint_id;
uint32_t cluster_id = cluster::rainmaker::Id;
uint32_t attribute_id = cluster::rainmaker::attribute::node_id::Id;
esp_matter_attr_val_t val = esp_matter_char_str(node_id, strlen(node_id));
uint32_t attribute_id = cluster::rainmaker::attribute::rmaker_node_id::Id;
esp_matter_attr_val_t val = esp_matter_char_str(rmaker_node_id, strlen(rmaker_node_id));
return attribute::update(endpoint_id, cluster_id, attribute_id, &val);
}

static esp_err_t challenge_attribute_update(char *challenge)
static esp_err_t challenge_response_attribute_update(char *challenge_response)
{
uint16_t endpoint_id = cluster::rainmaker::endpoint_id;
uint32_t cluster_id = cluster::rainmaker::Id;
uint32_t attribute_id = cluster::rainmaker::attribute::challenge::Id;
esp_matter_attr_val_t val = esp_matter_char_str(challenge, strlen(challenge));
uint32_t attribute_id = cluster::rainmaker::attribute::challenge_response::Id;
esp_matter_attr_val_t val = esp_matter_char_str(challenge_response, strlen(challenge_response));
return attribute::update(endpoint_id, cluster_id, attribute_id, &val);
}

Expand Down Expand Up @@ -187,7 +204,7 @@ static esp_err_t command_callback(const ConcreteCommandPath &command_path, TLVRe

/* Parse the tlv data */
chip::CharSpan config_value;
chip::app::DataModel::Decode(tlv_data, config_value);
DataModel::Decode(tlv_data, config_value);
const char *data = config_value.data();
int size = config_value.size();
if (!data || size <= 0) {
Expand Down Expand Up @@ -235,6 +252,34 @@ static esp_err_t command_callback(const ConcreteCommandPath &command_path, TLVRe
return ESP_OK;
}

static esp_err_t sign_and_update_challenge_response(chip::CharSpan challenge_span)
{
if (challenge_span.size() > ESP_MATTER_RAINMAKER_MAX_CHALLENGE_LEN) {
return ESP_ERR_INVALID_ARG;
}

/* Copy the data. This is done to make the strings NULL terminated. */
char challenge[ESP_MATTER_RAINMAKER_MAX_CHALLENGE_LEN] = {0};

uint8_t bytes_to_copy = std::min(sizeof(challenge), challenge_span.size());
strncpy(challenge, challenge_span.data(), bytes_to_copy);
challenge[bytes_to_copy] = 0;
ESP_LOGI(TAG, "challenge: %s", challenge);

// sign the data here
char *challenge_response = NULL;
size_t outlen = 0;

esp_err_t err = esp_rmaker_node_auth_sign_msg((void *)challenge, challenge_span.size(), (void **)&challenge_response, &outlen);
if (err != ESP_OK) {
return err;
}

err = challenge_response_attribute_update(challenge_response);
free(challenge_response);
return err;
}

static esp_err_t sign_data_command_callback(const ConcreteCommandPath &command_path, TLVReader &tlv_data, void *opaque_ptr)
{
/* Get ids */
Expand All @@ -256,35 +301,21 @@ static esp_err_t sign_data_command_callback(const ConcreteCommandPath &command_p
}
command_count--;

// In an invoke interaction, the payload should be enclosed within an anonymous struct,
// and each argument must be specified using a context-specific tag.
// The following code parses the data without context-specific tags, which is incorrect and needs correction.
// TODO: CON-820

/* Parse the tlv data */
chip::CharSpan config_value;
chip::app::DataModel::Decode(tlv_data, config_value);
DataModel::Decode(tlv_data, config_value);
if (!config_value.data() || config_value.size() <= 0) {
ESP_LOGE(TAG, "Command data not found or was not decoded correctly. The expected data is a string or the"
"format is \"<data>\"");
return ESP_FAIL;
}

/* Copy the data. This is done to make the strings NULL terminated. */
char challenge[ESP_MATTER_RAINMAKER_MAX_SIGN_DATA_LEN] = {0};
strncpy(challenge, config_value.data(), config_value.size());
challenge[config_value.size()] = '\0';
ESP_LOGI(TAG, "challenge: %s", challenge);

// sign the data here
char *challenge_response = NULL;
size_t outlen = 0;
esp_err_t err = esp_rmaker_node_auth_sign_msg((void *)challenge, config_value.size(), (void **)&challenge_response, &outlen);

// Return if challenge response is NULL
if (err != ESP_OK) {
return err;
}

challenge_attribute_update(challenge_response);
free(challenge_response);

return err;
return sign_and_update_challenge_response(config_value);
}

static esp_err_t custom_cluster_create()
Expand All @@ -295,22 +326,27 @@ static esp_err_t custom_cluster_create()

/* Create custom rainmaker cluster */
cluster_t *cluster = esp_matter::cluster::create(endpoint, cluster::rainmaker::Id, CLUSTER_FLAG_SERVER);
attribute::create(cluster, Globals::Attributes::ClusterRevision::Id, ATTRIBUTE_FLAG_NONE, esp_matter_uint16(1));
attribute::create(cluster, Globals::Attributes::ClusterRevision::Id, ATTRIBUTE_FLAG_NONE, esp_matter_uint16(RAINMAKER_CLUSTER_REVISION));

/* Create custom status attribute */
/* Update the value of the attribute after esp_rmaker_node_init() is done */
attribute::create(cluster, cluster::rainmaker::attribute::status::Id, ATTRIBUTE_FLAG_NONE, esp_matter_bool(false));

/* Create custom node_id attribute */
/* Create custom rmaker_node_id attribute */
/* Update the value of the attribute after esp_rmaker_node_init() is done */
char node_id[ESP_MATTER_RAINMAKER_MAX_NODE_ID_LEN] = {0};
attribute::create(cluster, cluster::rainmaker::attribute::node_id::Id, ATTRIBUTE_FLAG_NONE,
esp_matter_char_str(node_id, sizeof(node_id)));
char rmaker_node_id[ESP_MATTER_RAINMAKER_MAX_NODE_ID_LEN] = {0};
attribute::create(cluster, cluster::rainmaker::attribute::rmaker_node_id::Id, ATTRIBUTE_FLAG_NONE,
esp_matter_char_str(rmaker_node_id, sizeof(rmaker_node_id)));

/* Create custom challenge attribute */
/* Create custom challenge_response attribute */
/* Update the value of the attribute after sign_data command is called */
char challenge_response[ESP_MATTER_RAINMAKER_MAX_CHALLENGE_RESPONSE_LEN] = {0};
attribute::create(cluster, cluster::rainmaker::attribute::challenge_response::Id, ATTRIBUTE_FLAG_NONE,
esp_matter_char_str(challenge_response, sizeof(challenge_response)));

/* Create custom challenge attribute */
char challenge[ESP_MATTER_RAINMAKER_MAX_CHALLENGE_LEN] = {0};
attribute::create(cluster, cluster::rainmaker::attribute::challenge::Id, ATTRIBUTE_FLAG_NONE,
attribute::create(cluster, cluster::rainmaker::attribute::challenge::Id, ATTRIBUTE_FLAG_WRITABLE,
esp_matter_char_str(challenge, sizeof(challenge)));

/* Create custom configuration command */
Expand All @@ -324,13 +360,56 @@ static esp_err_t custom_cluster_create()
return ESP_OK;
}

class RainmakerAttrAccess : public AttributeAccessInterface
{
public:
// Register for the Rainmaker cluster on endpoint 0.
RainmakerAttrAccess() : AttributeAccessInterface(chip::Optional<chip::EndpointId>(cluster::rainmaker::endpoint_id),
cluster::rainmaker::Id) {}

CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override
{
return CHIP_NO_ERROR;
}

CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override
{
if (aPath == ConcreteDataAttributePath(cluster::rainmaker::endpoint_id, cluster::rainmaker::Id, cluster::rainmaker::attribute::challenge::Id))
{
chip::CharSpan challenge;
CHIP_ERROR c_err = aDecoder.Decode(challenge);
if (c_err != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "Failed to decode challenge, err:%" CHIP_ERROR_FORMAT, c_err.Format());
return c_err;
}

return (ESP_OK == sign_and_update_challenge_response(challenge)) ? CHIP_NO_ERROR : CHIP_ERROR_INCORRECT_STATE;
}
else
{
return CHIP_NO_ERROR;
}
}
};

RainmakerAttrAccess gAttrAccess;

esp_err_t init()
{
/* Add custom rainmaker cluster */
#if CONFIG_ENABLE_CHIP_SHELL
register_commands();
#endif
return custom_cluster_create();
esp_err_t err = custom_cluster_create();
if (ESP_OK != err) {
ESP_LOGE(TAG, "Failed to create rainmaker cluster");
return err;
}

registerAttributeAccessOverride(&gAttrAccess);
return ESP_OK;

}

esp_err_t start()
Expand All @@ -346,8 +425,8 @@ esp_err_t start()
esp_event_handler_register(RMAKER_EVENT, RMAKER_EVENT_USER_NODE_MAPPING_RESET,
&user_node_association_event_handler, NULL);

/* Update node_id */
return node_id_attribute_update(esp_rmaker_get_node_id());
/* Update rmaker_node_id */
return rmaker_node_id_attribute_update(esp_rmaker_get_node_id());
}

} /* rainmaker */
Expand Down
46 changes: 46 additions & 0 deletions components/esp_matter_rainmaker/rainmaker.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0"?>
<!--
Copyright (c) 2023 Project CHIP Authors
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.
-->
<configurator>
<domain name="CHIP"/>
<cluster>

<name>Rainmaker</name>
<domain>CHIP</domain>
<code>0x131BFC00</code>
<define>RAINMAKER_CLUSTER</define>

<description>Attributes and commands for rainmaker cluster.</description>

<globalAttribute side="either" code="0xFFFD" value="2"/>

<attribute side="server" code="0x0000" define="STATUS" type="boolean" default="0">Status</attribute>
<attribute side="server" code="0x0001" define="RMAKER_NODE_ID" type="char_string" length="40">RmakerNodeId</attribute>
<attribute side="server" code="0x0002" define="CHALLENGE_RESPONSE" type="char_string" length="150">ChallengeResponse</attribute>
<attribute side="server" code="0x0003" define="CHALLENGE" type="char_string" length="40" writable="true">Challenge</attribute>

<command source="client" code="0x00" name="Configuration" optional="false">
<description>Configuration command.</description>
<arg name="ConfigurationArg" type="char_string"/>
</command>

<command source="client" code="0x01" name="SignData" optional="false">
<description>SignData command.</description>
<arg name="SignDataArg" type="char_string"/>
</command>

</cluster>
</configurator>

0 comments on commit 79dfcc7

Please sign in to comment.