Skip to content

Commit

Permalink
Enable CASE session establishment (project-chip#7666)
Browse files Browse the repository at this point in the history
* Enable CASE session establishment

* persist counter if device is connected

* enable CASE sessions for chip-tool

* address review comments

* add tests for SessionIDAllocator

* address review comments

* address review comments

* add ReserveUpTo

* release resouces on cleanup, and reduce message timeout

* release resources on cleanup and init.

* Remove sleep from ModelCommand

* some cleanup
  • Loading branch information
pan-apple authored and Nikita committed Sep 23, 2021
1 parent e6b8774 commit 3a98ff8
Show file tree
Hide file tree
Showing 30 changed files with 912 additions and 281 deletions.
25 changes: 20 additions & 5 deletions examples/chip-tool/commands/clusters/ModelCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,10 @@ CHIP_ERROR ModelCommand::Run(NodeId localId, NodeId remoteId)
{
chip::DeviceLayer::StackLock lock;

err = GetExecContext()->commissioner->GetDevice(remoteId, &mDevice);
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(chipTool, "Init failure! No pairing for device: %" PRIu64, localId));

err = SendCommand(mDevice, mEndPointId);
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(chipTool, "Failed to send message: %s", ErrorStr(err)));
err = GetExecContext()->commissioner->GetConnectedDevice(remoteId, &mOnDeviceConnectedCallback,
&mOnDeviceConnectionFailureCallback);
VerifyOrExit(err == CHIP_NO_ERROR,
ChipLogError(chipTool, "Failed in initiating connection to the device: %" PRIu64 ", error %d", remoteId, err));
}

WaitForResponse(kWaitDurationInSeconds);
Expand All @@ -65,3 +64,19 @@ CHIP_ERROR ModelCommand::Run(NodeId localId, NodeId remoteId)
exit:
return err;
}

void ModelCommand::OnDeviceConnectedFn(void * context, chip::Controller::Device * device)
{
ModelCommand * command = reinterpret_cast<ModelCommand *>(context);
VerifyOrReturn(command != nullptr,
ChipLogError(chipTool, "Device connected, but cannot send the command, as the context is null"));
command->SendCommand(device, command->mEndPointId);
}

void ModelCommand::OnDeviceConnectionFailureFn(void * context, NodeId deviceId, CHIP_ERROR error)
{
ModelCommand * command = reinterpret_cast<ModelCommand *>(context);
ChipLogError(chipTool, "Failed in connecting to the device %" PRIu64 ". Error %d", deviceId, error);
VerifyOrReturn(command != nullptr, ChipLogError(chipTool, "ModelCommand context is null"));
command->SetCommandExitStatus(false);
}
12 changes: 10 additions & 2 deletions examples/chip-tool/commands/clusters/ModelCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@
class ModelCommand : public Command
{
public:
ModelCommand(const char * commandName) : Command(commandName) {}
ModelCommand(const char * commandName) :
Command(commandName), mOnDeviceConnectedCallback(OnDeviceConnectedFn, this),
mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this)
{}

void AddArguments() { AddArgument("endpoint-id", CHIP_ZCL_ENDPOINT_MIN, CHIP_ZCL_ENDPOINT_MAX, &mEndPointId); }

Expand All @@ -41,6 +44,11 @@ class ModelCommand : public Command
virtual CHIP_ERROR SendCommand(ChipDevice * device, uint8_t endPointId) = 0;

private:
ChipDevice * mDevice;
uint8_t mEndPointId;

static void OnDeviceConnectedFn(void * context, chip::Controller::Device * device);
static void OnDeviceConnectionFailureFn(void * context, NodeId deviceId, CHIP_ERROR error);

chip::Callback::Callback<chip::Controller::OnDeviceConnected> mOnDeviceConnectedCallback;
chip::Callback::Callback<chip::Controller::OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;
};
4 changes: 1 addition & 3 deletions examples/chip-tool/commands/discover/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,8 @@ class Update : public DiscoverCommand
/////////// DiscoverCommand Interface /////////
CHIP_ERROR RunCommand(NodeId remoteId, uint64_t fabricId) override
{
ChipDevice * device;
ReturnErrorOnFailure(GetExecContext()->commissioner->GetDevice(remoteId, &device));
ChipLogProgress(chipTool, "Mdns: Updating NodeId: %" PRIx64 " FabricId: %" PRIx64 " ...", remoteId, fabricId);
return GetExecContext()->commissioner->UpdateDevice(device, fabricId);
return GetExecContext()->commissioner->UpdateDevice(remoteId, fabricId);
}

/////////// DeviceAddressUpdateDelegate Interface /////////
Expand Down
24 changes: 21 additions & 3 deletions examples/chip-tool/commands/pairing/PairingCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,20 @@ void PairingCommand::OnPairingDeleted(CHIP_ERROR err)
SetCommandExitStatus(err == CHIP_NO_ERROR);
}

void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err)
{
if (err == CHIP_NO_ERROR)
{
ChipLogProgress(chipTool, "Device commissioning completed with success");
}
else
{
ChipLogProgress(chipTool, "Device commissioning Failure: %s", ErrorStr(err));
}

SetCommandExitStatus(err == CHIP_NO_ERROR);
}

CHIP_ERROR PairingCommand::SetupNetwork()
{

Expand Down Expand Up @@ -316,13 +330,17 @@ void PairingCommand::OnEnableNetworkResponse(void * context, uint8_t errorCode,

CHIP_ERROR PairingCommand::UpdateNetworkAddress()
{
ReturnErrorOnFailure(GetExecContext()->commissioner->GetDevice(mRemoteId, &mDevice));
ChipLogProgress(chipTool, "Mdns: Updating NodeId: %" PRIx64 " FabricId: %" PRIx64 " ...", mRemoteId, mFabricId);
return GetExecContext()->commissioner->UpdateDevice(mDevice, mFabricId);
return GetExecContext()->commissioner->UpdateDevice(mRemoteId, mFabricId);
}

void PairingCommand::OnAddressUpdateComplete(NodeId nodeId, CHIP_ERROR err)
{
ChipLogProgress(chipTool, "OnAddressUpdateComplete: %s", ErrorStr(err));
SetCommandExitStatus(CHIP_NO_ERROR == err);
if (err != CHIP_NO_ERROR)
{
// Set exit status only if the address update failed.
// Otherwise wait for OnCommissioningComplete() callback.
SetCommandExitStatus(false);
}
}
1 change: 1 addition & 0 deletions examples/chip-tool/commands/pairing/PairingCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class PairingCommand : public Command,
void OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status) override;
void OnPairingComplete(CHIP_ERROR error) override;
void OnPairingDeleted(CHIP_ERROR error) override;
void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error) override;

/////////// DeviceAddressUpdateDelegate Interface /////////
void OnAddressUpdateComplete(NodeId nodeId, CHIP_ERROR error) override;
Expand Down
15 changes: 12 additions & 3 deletions src/app/server/RendezvousServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,18 @@ CHIP_ERROR RendezvousServer::WaitForPairing(const RendezvousParameters & params,
ReturnErrorOnFailure(mExchangeManager->RegisterUnsolicitedMessageHandlerForType(
Protocols::SecureChannel::MsgType::PBKDFParamRequest, &mPairingSession));

uint16_t keyID = 0;
ReturnErrorOnFailure(mIDAllocator->Allocate(keyID));

if (params.HasPASEVerifier())
{
ReturnErrorOnFailure(mPairingSession.WaitForPairing(params.GetPASEVerifier(), mNextKeyId++, this));
ReturnErrorOnFailure(mPairingSession.WaitForPairing(params.GetPASEVerifier(), keyID, this));
}
else
{
ReturnErrorOnFailure(mPairingSession.WaitForPairing(params.GetSetupPINCode(), kSpake2p_Iteration_Count,
reinterpret_cast<const unsigned char *>(kSpake2pKeyExchangeSalt),
strlen(kSpake2pKeyExchangeSalt), mNextKeyId++, this));
strlen(kSpake2pKeyExchangeSalt), keyID, this));
}

ReturnErrorOnFailure(mPairingSession.MessageDispatch().Init(transportMgr));
Expand Down Expand Up @@ -181,6 +184,12 @@ void RendezvousServer::OnSessionEstablished()
VerifyOrReturn(connection.StoreIntoKVS(*mStorage) == CHIP_NO_ERROR,
ChipLogError(AppServer, "Failed to store the connection state"));

mStorage->SyncSetKeyValue(kStorablePeerConnectionCountKey, &mNextKeyId, sizeof(mNextKeyId));
// The Peek() is used to find the smallest key ID that's not been assigned to any session.
// This value is persisted, and on reboot, it is used to revive any previously
// active secure sessions.
// We support one active PASE session at any time. So the key ID should not be updated
// in another thread, while we retrieve it here.
uint16_t keyID = mIDAllocator->Peek();
mStorage->SyncSetKeyValue(kStorablePeerConnectionCountKey, &keyID, sizeof(keyID));
}
} // namespace chip
16 changes: 11 additions & 5 deletions src/app/server/RendezvousServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <messaging/ExchangeMgr.h>
#include <platform/CHIPDeviceLayer.h>
#include <protocols/secure_channel/RendezvousParameters.h>
#include <protocols/secure_channel/SessionIDAllocator.h>

namespace chip {

Expand All @@ -31,11 +32,17 @@ class RendezvousServer : public SessionEstablishmentDelegate
CHIP_ERROR WaitForPairing(const RendezvousParameters & params, Messaging::ExchangeManager * exchangeManager,
TransportMgrBase * transportMgr, SecureSessionMgr * sessionMgr, Transport::AdminPairingInfo * admin);

CHIP_ERROR Init(AppDelegate * delegate, PersistentStorageDelegate * storage)
CHIP_ERROR Init(AppDelegate * delegate, PersistentStorageDelegate * storage, SessionIDAllocator * idAllocator)
{
VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
mStorage = storage;

VerifyOrReturnError(idAllocator != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
mIDAllocator = idAllocator;

// The caller may chose to not provide a delegate object. The RendezvousServer checks for null delegate before calling
// its methods.
mDelegate = delegate;
mStorage = storage;
return CHIP_NO_ERROR;
}

Expand All @@ -45,8 +52,6 @@ class RendezvousServer : public SessionEstablishmentDelegate

void Cleanup();

uint16_t GetNextKeyId() const { return mNextKeyId; }
void SetNextKeyId(uint16_t id) { mNextKeyId = id; }
void OnPlatformEvent(const DeviceLayer::ChipDeviceEvent * event);

private:
Expand All @@ -55,11 +60,12 @@ class RendezvousServer : public SessionEstablishmentDelegate
Messaging::ExchangeManager * mExchangeManager = nullptr;

PASESession mPairingSession;
uint16_t mNextKeyId = 0;
SecureSessionMgr * mSessionMgr = nullptr;

Transport::AdminPairingInfo * mAdmin = nullptr;

SessionIDAllocator * mIDAllocator = nullptr;

const RendezvousAdvertisementDelegate * mAdvDelegate;

bool HasAdvertisementDelegate() const { return mAdvDelegate != nullptr; }
Expand Down
28 changes: 18 additions & 10 deletions src/app/server/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class ServerStorageDelegate : public PersistentStorageDelegate
};

ServerStorageDelegate gServerStorage;
SessionIDAllocator gSessionIDAllocator;

CHIP_ERROR PersistAdminPairingToKVS(AdminPairingInfo * admin, AdminId nextAvailableId)
{
Expand Down Expand Up @@ -149,7 +150,7 @@ void EraseAllAdminPairingsUpTo(AdminId nextAvailableId)
}
}

static CHIP_ERROR RestoreAllSessionsFromKVS(SecureSessionMgr & sessionMgr, RendezvousServer & server)
static CHIP_ERROR RestoreAllSessionsFromKVS(SecureSessionMgr & sessionMgr)
{
uint16_t nextSessionKeyId = 0;
// It's not an error if the key doesn't exist. Just return right away.
Expand All @@ -170,16 +171,22 @@ static CHIP_ERROR RestoreAllSessionsFromKVS(SecureSessionMgr & sessionMgr, Rende

ChipLogProgress(AppServer, "Fetched the session information: from 0x" ChipLogFormatX64,
ChipLogValueX64(session->PeerConnection().GetPeerNodeId()));
sessionMgr.NewPairing(Optional<Transport::PeerAddress>::Value(session->PeerConnection().GetPeerAddress()),
session->PeerConnection().GetPeerNodeId(), session, SecureSession::SessionRole::kResponder,
connection.GetAdminId(), nullptr);
if (gSessionIDAllocator.Reserve(keyId) == CHIP_NO_ERROR)
{
sessionMgr.NewPairing(Optional<Transport::PeerAddress>::Value(session->PeerConnection().GetPeerAddress()),
session->PeerConnection().GetPeerNodeId(), session, SecureSession::SessionRole::kResponder,
connection.GetAdminId(), nullptr);
}
else
{
ChipLogProgress(AppServer, "Session Key ID %" PRIu16 " cannot be used. Skipping over this session", keyId);
}
session->Clear();
}
}

chip::Platform::Delete(session);

server.SetNextKeyId(nextSessionKeyId);
return CHIP_NO_ERROR;
}

Expand All @@ -189,6 +196,7 @@ void EraseAllSessionsUpTo(uint16_t nextSessionKeyId)

for (uint16_t keyId = 0; keyId < nextSessionKeyId; keyId++)
{
gSessionIDAllocator.Free(keyId);
StorablePeerConnection::DeleteFromKVS(gServerStorage, keyId);
}
}
Expand Down Expand Up @@ -427,9 +435,8 @@ CHIP_ERROR OpenDefaultPairingWindow(ResetAdmins resetAdmins, chip::PairingWindow

if (resetAdmins == ResetAdmins::kYes)
{
uint16_t nextKeyId = gRendezvousServer.GetNextKeyId();
EraseAllAdminPairingsUpTo(gNextAvailableAdminId);
EraseAllSessionsUpTo(nextKeyId);
EraseAllSessionsUpTo(gSessionIDAllocator.Peek());
// Only resetting gNextAvailableAdminId at reboot otherwise previously paired device with adminID 0
// can continue sending messages to accessory as next available admin will also be 0.
// This logic is not up to spec, will be implemented up to spec once AddOptCert is implemented.
Expand Down Expand Up @@ -462,7 +469,7 @@ void InitServer(AppDelegate * delegate)
PersistedStorage::KeyValueStoreMgrImpl().Init("/tmp/chip_server_kvs");
#endif

err = gRendezvousServer.Init(delegate, &gServerStorage);
err = gRendezvousServer.Init(delegate, &gServerStorage, &gSessionIDAllocator);
SuccessOrExit(err);

gAdvDelegate.SetDelegate(delegate);
Expand Down Expand Up @@ -517,7 +524,7 @@ void InitServer(AppDelegate * delegate)
VerifyOrExit(CHIP_NO_ERROR == RestoreAllAdminPairingsFromKVS(gAdminPairings, gNextAvailableAdminId),
ChipLogError(AppServer, "Could not restore admin table"));

VerifyOrExit(CHIP_NO_ERROR == RestoreAllSessionsFromKVS(gSessions, gRendezvousServer),
VerifyOrExit(CHIP_NO_ERROR == RestoreAllSessionsFromKVS(gSessions),
ChipLogError(AppServer, "Could not restore previous sessions"));
}
else
Expand All @@ -542,7 +549,8 @@ void InitServer(AppDelegate * delegate)
err = gExchangeMgr.RegisterUnsolicitedMessageHandlerForProtocol(Protocols::ServiceProvisioning::Id, &gCallbacks);
VerifyOrExit(err == CHIP_NO_ERROR, err = CHIP_ERROR_NO_UNSOLICITED_MESSAGE_HANDLER);

err = gCASEServer.ListenForSessionEstablishment(&gExchangeMgr, &gTransports, &gSessions, &GetGlobalAdminPairingTable());
err = gCASEServer.ListenForSessionEstablishment(&gExchangeMgr, &gTransports, &gSessions, &GetGlobalAdminPairingTable(),
&gSessionIDAllocator);
SuccessOrExit(err);

exit:
Expand Down
Loading

0 comments on commit 3a98ff8

Please sign in to comment.