blob: 3ededbcffe7cb4c2f82257a5e1a0edad25fadb4e [file] [log] [blame]
#include "ICDManagementServer.h"
#include <app/icd/ICDNotifier.h>
using namespace chip;
using namespace chip::Protocols;
namespace chip {
ICDManagementServer ICDManagementServer::mInstance;
Status ICDManagementServer::RegisterClient(PersistentStorageDelegate & storage, FabricIndex fabric_index, chip::NodeId node_id,
uint64_t monitored_subject, chip::ByteSpan key,
Optional<chip::ByteSpan> verification_key, bool is_admin)
{
bool isFirstEntryForFabric = false;
ICDMonitoringTable table(storage, fabric_index, GetClientsSupportedPerFabric(), mSymmetricKeystore);
// Get current entry, if exists
ICDMonitoringEntry entry(mSymmetricKeystore);
CHIP_ERROR err = table.Find(node_id, entry);
if (CHIP_NO_ERROR == err)
{
// Existing entry: Validate Key if, and only if, the ISD has NOT administrator permissions
if (!is_admin)
{
VerifyOrReturnError(verification_key.HasValue(), InteractionModel::Status::Failure);
VerifyOrReturnError(entry.IsKeyEquivalent(verification_key.Value()), InteractionModel::Status::Failure);
}
}
else if (CHIP_ERROR_NOT_FOUND == err)
{
// New entry
VerifyOrReturnError(entry.index < table.Limit(), InteractionModel::Status::ResourceExhausted);
// Check if it's going to be the first entry for fabric
isFirstEntryForFabric = table.IsEmpty();
}
else
{
// Error
return InteractionModel::Status::Failure;
}
// Save
entry.checkInNodeID = node_id;
entry.monitoredSubject = monitored_subject;
if (entry.keyHandleValid)
{
entry.DeleteKey();
}
err = entry.SetKey(key);
VerifyOrReturnError(CHIP_ERROR_INVALID_ARGUMENT != err, InteractionModel::Status::ConstraintError);
VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure);
err = table.Set(entry.index, entry);
// Delete key upon failure to prevent key storage leakage.
if (err != CHIP_NO_ERROR)
{
entry.DeleteKey();
}
VerifyOrReturnError(CHIP_ERROR_INVALID_ARGUMENT != err, InteractionModel::Status::ConstraintError);
VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure);
if (isFirstEntryForFabric)
{
// Notify subscribers that the first entry for the fabric was successfully added
TriggerICDMTableUpdatedEvent();
}
return InteractionModel::Status::Success;
}
Status ICDManagementServer::UnregisterClient(PersistentStorageDelegate & storage, FabricIndex fabric_index, chip::NodeId node_id,
Optional<chip::ByteSpan> verificationKey, bool is_admin)
{
ICDMonitoringTable table(storage, fabric_index, GetClientsSupportedPerFabric(), mSymmetricKeystore);
// Get current entry, if exists
ICDMonitoringEntry entry(mSymmetricKeystore);
CHIP_ERROR err = table.Find(node_id, entry);
VerifyOrReturnError(CHIP_ERROR_NOT_FOUND != err, InteractionModel::Status::NotFound);
VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure);
// Existing entry: Validate Key if, and only if, the ISD has NOT administrator permissions
if (!is_admin)
{
VerifyOrReturnError(verificationKey.HasValue(), InteractionModel::Status::Failure);
VerifyOrReturnError(entry.IsKeyEquivalent(verificationKey.Value()), InteractionModel::Status::Failure);
}
err = table.Remove(entry.index);
VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure);
if (table.IsEmpty())
{
TriggerICDMTableUpdatedEvent();
}
return InteractionModel::Status::Success;
}
Status ICDManagementServer::StayActiveRequest(FabricIndex fabric_index)
{
// TODO: Implementent stay awake logic for end device
// https://github.com/project-chip/connectedhomeip/issues/24259
app::ICDNotifier::GetInstance().BroadcastICDManagementEvent(app::ICDListener::ICDManagementEvents::kStayActiveRequestReceived);
return InteractionModel::Status::UnsupportedCommand;
}
void ICDManagementServer::TriggerICDMTableUpdatedEvent()
{
app::ICDNotifier::GetInstance().BroadcastICDManagementEvent(app::ICDListener::ICDManagementEvents::kTableUpdated);
}
} // namespace chip