Skip to content

Commit

Permalink
Group Data Provider updated to newest Group Key Management spec. (#12840
Browse files Browse the repository at this point in the history
)

* Group Data Provider updated to support the new Group Key Management attributes.

* Group Data Provider update: Review comments applied.
  • Loading branch information
rcasallas-silabs authored and pull[bot] committed Feb 25, 2022
1 parent 5fee2a9 commit 9905228
Show file tree
Hide file tree
Showing 8 changed files with 1,701 additions and 2,509 deletions.
269 changes: 104 additions & 165 deletions src/app/clusters/groups-server/groups-server.cpp

Large diffs are not rendered by default.

196 changes: 84 additions & 112 deletions src/credentials/GroupDataProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,34 +30,24 @@ namespace Credentials {
class GroupDataProvider
{
public:
// An EpochKey is a single key usable to determine an operational group key
struct EpochKey
{
static constexpr size_t kLengthBytes = Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES;
// Validity start time in microseconds since 2000-01-01T00:00:00 UTC ("the Epoch")
uint64_t start_time;
// Actual key bits. Depending on context, it may be a raw epoch key (as seen within `SetKeySet` calls)
// or it may be the derived operational group key (as seen in any other usage).
uint8_t key[kLengthBytes];
};
static constexpr uint16_t kMaxGroupsPerFabric = CHIP_CONFIG_MAX_GROUPS_PER_FABRIC;
static constexpr uint16_t kMaxGroupKeysPerFabric = CHIP_CONFIG_MAX_GROUP_KEYS_PER_FABRIC;

// A GroupMapping maps a controlling GroupId to a given EndpointId. There may be
// multiple GroupMapping having the same `group` value, but each with different
// `endpoint` value.
struct GroupMapping
struct GroupInfo
{
static constexpr size_t kGroupNameMax = CHIP_CONFIG_MAX_GROUP_NAME_LENGTH;

// The endpoint to which a GroupId is mapped.
EndpointId endpoint = kInvalidEndpointId;
// The GroupId, which, when received in a message will map to the `endpoint`.
GroupId group = kUndefinedGroupId;
// Group name
// Identifies group within the scope of the given Fabric
chip::GroupId group_id = kUndefinedGroupId;
// Lastest group name written for a given GroupId on any Endpoint via the Groups cluster
char name[kGroupNameMax + 1] = { 0 };

GroupMapping() = default;
GroupMapping(EndpointId eid, GroupId gid) : GroupMapping(eid, gid, nullptr) {}
GroupMapping(EndpointId eid, GroupId gid, const char * groupName) : endpoint(eid), group(gid)
GroupInfo() { SetName(nullptr); }
GroupInfo(const char * groupName) { SetName(groupName); }
GroupInfo(const CharSpan & groupName) { SetName(groupName); }
GroupInfo(chip::GroupId id, const char * groupName) : group_id(id) { SetName(groupName); }
GroupInfo(chip::GroupId id, const CharSpan & groupName) : group_id(id) { SetName(groupName); }
void SetName(const char * groupName)
{
if (nullptr == groupName)
{
Expand All @@ -70,7 +60,7 @@ class GroupDataProvider
name[size] = 0;
}
}
GroupMapping(EndpointId eid, GroupId gid, const CharSpan & groupName) : endpoint(eid), group(gid)
void SetName(const CharSpan & groupName)
{
if (nullptr == groupName.data())
{
Expand All @@ -83,42 +73,57 @@ class GroupDataProvider
name[size] = 0;
}
}
bool operator==(const GroupMapping & other)
bool operator==(const GroupInfo & other)
{
return (this->endpoint == other.endpoint) && (this->group == other.group) &&
strncmp(this->name, other.name, kGroupNameMax);
return (this->group_id == other.group_id) && !strncmp(this->name, other.name, kGroupNameMax);
}
};

// A group state maps the group key set to use for encryption/decryption for a given group ID.
struct GroupState
struct GroupKey
{
GroupState() = default;
GroupState(chip::FabricIndex fabric, chip::GroupId group_id, uint16_t key_set) :
fabric_index(fabric), group(group_id), keyset_id(key_set)
{}
// Fabric Index associated with the group state entry's fabric scoping
chip::FabricIndex fabric_index = kUndefinedFabricIndex;
// Identifies the group within the scope of the given fabric
chip::GroupId group = kUndefinedGroupId;
// References the set of group keys that generate operationa group keys for use with the given group
uint16_t keyset_id = 0;
bool operator==(const GroupState & other)
GroupKey() = default;
GroupKey(chip::GroupId group, chip::KeysetId keyset) : group_id(group), keyset_id(keyset) {}
// Identifies group within the scope of the given Fabric
chip::GroupId group_id = kUndefinedGroupId;
// Set of group keys that generate operational group keys for use with this group
chip::KeysetId keyset_id = 0;
bool operator==(const GroupKey & other) { return this->group_id == other.group_id && this->keyset_id == other.keyset_id; }
};

struct GroupEndpoint
{
GroupEndpoint() = default;
GroupEndpoint(chip::GroupId group, chip::EndpointId endpoint) : group_id(group), endpoint_id(endpoint) {}
// Identifies group within the scope of the given Fabric
chip::GroupId group_id = kUndefinedGroupId;
// Endpoint on the Node to which messages to this group may be forwarded
chip::EndpointId endpoint_id = kInvalidEndpointId;

bool operator==(const GroupEndpoint & other)
{
return this->fabric_index == other.fabric_index && this->group == other.group && this->keyset_id == other.keyset_id;
return this->group_id == other.group_id && this->endpoint_id == other.endpoint_id;
}
};

// An EpochKey is a single key usable to determine an operational group key
struct EpochKey
{
static constexpr size_t kLengthBytes = Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES;
// Validity start time in microseconds since 2000-01-01T00:00:00 UTC ("the Epoch")
uint64_t start_time;
// Actual key bits. Depending on context, it may be a raw epoch key (as seen within `SetKeySet` calls)
// or it may be the derived operational group key (as seen in any other usage).
uint8_t key[kLengthBytes];
};

// A operational group key set, usable by many GroupState mappings
struct KeySet
{
using SecurityPolicy = chip::app::Clusters::GroupKeyManagement::GroupKeySecurityPolicy;

KeySet() = default;
KeySet(uint16_t id) : keyset_id(id) {}
KeySet(uint16_t id, SecurityPolicy policy_id, uint8_t num_keys) : keyset_id(id), policy(policy_id), num_keys_used(num_keys)
{}
KeySet(SecurityPolicy policy_id, uint8_t num_keys) : keyset_id(0), policy(policy_id), num_keys_used(num_keys) {}

// The actual keys for the group key set
EpochKey epoch_keys[3];
Expand All @@ -131,11 +136,8 @@ class GroupDataProvider

bool operator==(const KeySet & other)
{
if (this->policy == other.policy && this->num_keys_used == other.num_keys_used)
{
return !memcmp(this->epoch_keys, other.epoch_keys, this->num_keys_used * sizeof(EpochKey));
}
return false;
VerifyOrReturnError(this->policy == other.policy && this->num_keys_used == other.num_keys_used, false);
return !memcmp(this->epoch_keys, other.epoch_keys, this->num_keys_used * sizeof(EpochKey));
}
};

Expand Down Expand Up @@ -167,33 +169,10 @@ class GroupDataProvider
Iterator() = default;
};

using GroupMappingIterator = Iterator<GroupMapping>;
using GroupStateIterator = Iterator<GroupState>;
using KeySetIterator = Iterator<KeySet>;

/**
* Interface for a listener of changes in any Group configuration. Necessary
* to implement attribute subscription for Group Key Management cluster, and
* to react to configuration changes that may impact in-progress functional work.
*/
class GroupListener
{
public:
virtual ~GroupListener() = default;
/**
* Listener callback invoked when a GroupState entry is mutated or added.
*
* @param[in] old_state GroupState reflecting the previous entry. Set to nullptr on appends.
* @param[in] new_state GroupState reflecting the updated/new entry.
*/
virtual void OnGroupStateChanged(const GroupState * old_state, const GroupState * new_state) = 0;
/**
* Listener callback invoked when a GroupState entry is removed from the Groups list.
*
* @param[in] removed_state Copy of GroupState that was just removed. Index included is no longer accessible.
*/
virtual void OnGroupStateRemoved(const GroupState * removed_state) = 0;
};
using GroupInfoIterator = Iterator<GroupInfo>;
using GroupKeyIterator = Iterator<GroupKey>;
using EndpointIterator = Iterator<GroupEndpoint>;
using KeySetIterator = Iterator<KeySet>;

GroupDataProvider() = default;
virtual ~GroupDataProvider() = default;
Expand All @@ -207,68 +186,68 @@ class GroupDataProvider
* initialization. Must be called once before any other API succeeds.
*
* @retval #CHIP_ERROR_INCORRECT_STATE if called when already initialized.
* @retval #CHIP_NO_ERROR on success.
* @retval #CHIP_NO_ERROR on success
*/
virtual CHIP_ERROR Init() = 0;
virtual void Finish() = 0;

//
// Group Mappings
// Group Table
//

virtual bool HasGroupNamesSupport() = 0;
virtual bool GroupMappingExists(chip::FabricIndex fabric_index, const GroupMapping & mapping) = 0;
virtual CHIP_ERROR AddGroupMapping(chip::FabricIndex fabric_index, const GroupMapping & mapping) = 0;
virtual CHIP_ERROR RemoveGroupMapping(chip::FabricIndex fabric_index, const GroupMapping & mapping) = 0;
virtual CHIP_ERROR RemoveAllGroupMappings(chip::FabricIndex fabric_index, EndpointId endpoint) = 0;
// By id
virtual CHIP_ERROR SetGroupInfo(chip::FabricIndex fabric_index, const GroupInfo & info) = 0;
virtual CHIP_ERROR GetGroupInfo(chip::FabricIndex fabric_index, chip::GroupId group_id, GroupInfo & info) = 0;
// By index
virtual CHIP_ERROR SetGroupInfoAt(chip::FabricIndex fabric_index, size_t index, const GroupInfo & info) = 0;
virtual CHIP_ERROR GetGroupInfoAt(chip::FabricIndex fabric_index, size_t index, GroupInfo & info) = 0;
virtual CHIP_ERROR RemoveGroupInfoAt(chip::FabricIndex fabric_index, size_t index) = 0;
// Endpoints
virtual bool HasEndpoint(chip::FabricIndex fabric_index, chip::GroupId group_id, chip::EndpointId endpoint_id) = 0;
virtual CHIP_ERROR AddEndpoint(chip::FabricIndex fabric_index, chip::GroupId group_id, chip::EndpointId endpoint_id) = 0;
virtual CHIP_ERROR RemoveEndpoint(chip::FabricIndex fabric_index, chip::GroupId group_id, chip::EndpointId endpoint_id) = 0;
virtual CHIP_ERROR RemoveEndpoint(chip::FabricIndex fabric_index, chip::EndpointId endpoint_id) = 0;
// Iterators
/**
* Creates an iterator that may be used to obtain the groups associated with the given fabric.
* Creates an iterator that may be used to obtain the list of groups associated with the given fabric.
* The number of concurrent instances of this iterator is limited. In order to release the allocated memory,
* the iterator's Release() method must be called after the iteration is finished.
* @retval An instance of GroupMappingIterator on success
* @retval An instance of EndpointIterator on success
* @retval nullptr if no iterator instances are available.
*/
virtual GroupMappingIterator * IterateGroupMappings(chip::FabricIndex fabric_index) = 0;
virtual GroupInfoIterator * IterateGroupInfo(chip::FabricIndex fabric_index) = 0;
/**
* Creates an iterator that may be used to obtain the groups associated with the given fabric and endpoint.
* Creates an iterator that may be used to obtain the list of (group, endpoint) pairs associated with the given fabric.
* The number of concurrent instances of this iterator is limited. In order to release the allocated memory,
* the iterator's Release() method must be called after the iteration is finished.
* @retval An instance of GroupMappingIterator on success
* @retval An instance of EndpointIterator on success
* @retval nullptr if no iterator instances are available.
*/
virtual GroupMappingIterator * IterateGroupMappings(chip::FabricIndex fabric_index, EndpointId endpoint) = 0;
virtual EndpointIterator * IterateEndpoints(chip::FabricIndex fabric_index) = 0;

//
// Group States
// Group-Key map
//

virtual CHIP_ERROR SetGroupState(size_t state_index, const GroupState & state) = 0;
virtual CHIP_ERROR GetGroupState(size_t state_index, GroupState & state) = 0;
virtual CHIP_ERROR RemoveGroupState(size_t state_index) = 0;
virtual CHIP_ERROR SetGroupKeyAt(chip::FabricIndex fabric_index, size_t index, const GroupKey & info) = 0;
virtual CHIP_ERROR GetGroupKeyAt(chip::FabricIndex fabric_index, size_t index, GroupKey & info) = 0;
virtual CHIP_ERROR RemoveGroupKeyAt(chip::FabricIndex fabric_index, size_t index) = 0;
/**
* Creates an iterator that may be used to obtain the list of group states.
* Creates an iterator that may be used to obtain the list of (group, keyset) pairs associated with the given fabric.
* The number of concurrent instances of this iterator is limited. In order to release the allocated memory,
* the iterator's Release() method must be called after the iteration is finished.
* @retval An instance of GroupStateIterator on success
* @retval An instance of GroupKeyIterator on success
* @retval nullptr if no iterator instances are available.
*/
virtual GroupStateIterator * IterateGroupStates() = 0;
/**
* Creates an iterator that may be used to obtain the list of group states associated with the given fabric.
* The number of concurrent instances of this iterator is limited. In order to release the allocated memory,
* the iterator's Release() method must be called after the iteration is finished.
* @retval An instance of GroupStateIterator on success
* @retval nullptr if no iterator instances are available.
*/
virtual GroupStateIterator * IterateGroupStates(chip::FabricIndex fabric_index) = 0;
virtual GroupKeyIterator * IterateGroupKey(chip::FabricIndex fabric_index) = 0;

//
// Key Sets
//

virtual CHIP_ERROR SetKeySet(chip::FabricIndex fabric_index, uint16_t keyset_id, const KeySet & keys) = 0;
virtual CHIP_ERROR GetKeySet(chip::FabricIndex fabric_index, uint16_t keyset_id, KeySet & keys) = 0;
virtual CHIP_ERROR RemoveKeySet(chip::FabricIndex fabric_index, uint16_t keyset_id) = 0;
virtual CHIP_ERROR SetKeySet(chip::FabricIndex fabric_index, const KeySet & keys) = 0;
virtual CHIP_ERROR GetKeySet(chip::FabricIndex fabric_index, chip::KeysetId keyset_id, KeySet & keys) = 0;
virtual CHIP_ERROR RemoveKeySet(chip::FabricIndex fabric_index, chip::KeysetId keyset_id) = 0;
/**
* Creates an iterator that may be used to obtain the list of key sets associated with the given fabric.
* The number of concurrent instances of this iterator is limited. In order to release the allocated memory,
Expand All @@ -283,13 +262,6 @@ class GroupDataProvider

// General
virtual CHIP_ERROR Decrypt(PacketHeader packetHeader, PayloadHeader & payloadHeader, System::PacketBufferHandle & msg) = 0;

// Listener
void SetListener(GroupListener * listener) { mListener = listener; };
void RemoveListener() { mListener = nullptr; };

protected:
GroupListener * mListener = nullptr;
};

/**
Expand Down
Loading

0 comments on commit 9905228

Please sign in to comment.