Enable CASE session establishment (#7666)
* 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
diff --git a/examples/chip-tool/commands/clusters/ModelCommand.cpp b/examples/chip-tool/commands/clusters/ModelCommand.cpp
index 6323045..f6ecc96 100644
--- a/examples/chip-tool/commands/clusters/ModelCommand.cpp
+++ b/examples/chip-tool/commands/clusters/ModelCommand.cpp
@@ -51,11 +51,10 @@
{
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);
@@ -65,3 +64,19 @@
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);
+}
diff --git a/examples/chip-tool/commands/clusters/ModelCommand.h b/examples/chip-tool/commands/clusters/ModelCommand.h
index f35f162..3583a3f 100644
--- a/examples/chip-tool/commands/clusters/ModelCommand.h
+++ b/examples/chip-tool/commands/clusters/ModelCommand.h
@@ -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); }
@@ -41,6 +44,11 @@
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;
};
diff --git a/examples/chip-tool/commands/discover/Commands.h b/examples/chip-tool/commands/discover/Commands.h
index 9b81043..ed743f9 100644
--- a/examples/chip-tool/commands/discover/Commands.h
+++ b/examples/chip-tool/commands/discover/Commands.h
@@ -66,10 +66,8 @@
/////////// 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 /////////
diff --git a/examples/chip-tool/commands/pairing/PairingCommand.cpp b/examples/chip-tool/commands/pairing/PairingCommand.cpp
index 21fc0cc..cc2c664 100644
--- a/examples/chip-tool/commands/pairing/PairingCommand.cpp
+++ b/examples/chip-tool/commands/pairing/PairingCommand.cpp
@@ -162,6 +162,20 @@
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()
{
@@ -316,13 +330,17 @@
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);
+ }
}
diff --git a/examples/chip-tool/commands/pairing/PairingCommand.h b/examples/chip-tool/commands/pairing/PairingCommand.h
index 7fdc137..abb33af 100644
--- a/examples/chip-tool/commands/pairing/PairingCommand.h
+++ b/examples/chip-tool/commands/pairing/PairingCommand.h
@@ -103,6 +103,7 @@
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;
diff --git a/src/app/server/RendezvousServer.cpp b/src/app/server/RendezvousServer.cpp
index 3055f24..25aa9b2 100644
--- a/src/app/server/RendezvousServer.cpp
+++ b/src/app/server/RendezvousServer.cpp
@@ -100,15 +100,18 @@
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));
@@ -181,6 +184,12 @@
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
diff --git a/src/app/server/RendezvousServer.h b/src/app/server/RendezvousServer.h
index 4ab2c21..845dcd5 100644
--- a/src/app/server/RendezvousServer.h
+++ b/src/app/server/RendezvousServer.h
@@ -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 {
@@ -31,11 +32,17 @@
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;
}
@@ -45,8 +52,6 @@
void Cleanup();
- uint16_t GetNextKeyId() const { return mNextKeyId; }
- void SetNextKeyId(uint16_t id) { mNextKeyId = id; }
void OnPlatformEvent(const DeviceLayer::ChipDeviceEvent * event);
private:
@@ -55,11 +60,12 @@
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; }
diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp
index 4198882..b1beb04 100644
--- a/src/app/server/Server.cpp
+++ b/src/app/server/Server.cpp
@@ -98,6 +98,7 @@
};
ServerStorageDelegate gServerStorage;
+SessionIDAllocator gSessionIDAllocator;
CHIP_ERROR PersistAdminPairingToKVS(AdminPairingInfo * admin, AdminId nextAvailableId)
{
@@ -149,7 +150,7 @@
}
}
-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.
@@ -170,16 +171,22 @@
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;
}
@@ -189,6 +196,7 @@
for (uint16_t keyId = 0; keyId < nextSessionKeyId; keyId++)
{
+ gSessionIDAllocator.Free(keyId);
StorablePeerConnection::DeleteFromKVS(gServerStorage, keyId);
}
}
@@ -427,9 +435,8 @@
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.
@@ -462,7 +469,7 @@
PersistedStorage::KeyValueStoreMgrImpl().Init("/tmp/chip_server_kvs");
#endif
- err = gRendezvousServer.Init(delegate, &gServerStorage);
+ err = gRendezvousServer.Init(delegate, &gServerStorage, &gSessionIDAllocator);
SuccessOrExit(err);
gAdvDelegate.SetDelegate(delegate);
@@ -517,7 +524,7 @@
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
@@ -542,7 +549,8 @@
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:
diff --git a/src/controller/CHIPDevice.cpp b/src/controller/CHIPDevice.cpp
index e4f5e28..b08d9d1 100644
--- a/src/controller/CHIPDevice.cpp
+++ b/src/controller/CHIPDevice.cpp
@@ -125,8 +125,7 @@
}
else
{
- Transport::PeerConnectionState * connectionState = nullptr;
- connectionState = mSessionManager->GetPeerConnectionState(mSecureSession);
+ Transport::PeerConnectionState * connectionState = mSessionManager->GetPeerConnectionState(mSecureSession);
// Check if the connection state has the correct transport information
if (connectionState == nullptr || connectionState->GetPeerAddress().GetTransportType() == Transport::Type::kUndefined ||
@@ -165,15 +164,26 @@
serializable.mAdminId = Encoding::LittleEndian::HostSwap16(mAdminId);
Transport::PeerConnectionState * connectionState = mSessionManager->GetPeerConnectionState(mSecureSession);
- VerifyOrReturnError(connectionState != nullptr, CHIP_ERROR_INCORRECT_STATE);
- const uint32_t localMessageCounter = connectionState->GetSessionMessageCounter().GetLocalMessageCounter().Value();
- const uint32_t peerMessageCounter = connectionState->GetSessionMessageCounter().GetPeerMessageCounter().GetCounter();
- serializable.mLocalMessageCounter = Encoding::LittleEndian::HostSwap32(localMessageCounter);
- serializable.mPeerMessageCounter = Encoding::LittleEndian::HostSwap32(peerMessageCounter);
+ // The connection state could be null if the device is moving from PASE connection to CASE connection.
+ // The device parameters (e.g. mDeviceOperationalCertProvisioned) are updated during this transition.
+ // The state during this transistion is being persisted so that the next access of the device will
+ // trigger the CASE based secure session.
+ if (connectionState != nullptr)
+ {
+ const uint32_t localMessageCounter = connectionState->GetSessionMessageCounter().GetLocalMessageCounter().Value();
+ const uint32_t peerMessageCounter = connectionState->GetSessionMessageCounter().GetPeerMessageCounter().GetCounter();
- serializable.mCASESessionKeyId = Encoding::LittleEndian::HostSwap16(mCASESessionKeyId);
- serializable.mDeviceProvisioningComplete = (mDeviceProvisioningComplete) ? 1 : 0;
+ serializable.mLocalMessageCounter = Encoding::LittleEndian::HostSwap32(localMessageCounter);
+ serializable.mPeerMessageCounter = Encoding::LittleEndian::HostSwap32(peerMessageCounter);
+ }
+ else
+ {
+ serializable.mLocalMessageCounter = 0;
+ serializable.mPeerMessageCounter = 0;
+ }
+
+ serializable.mDeviceOperationalCertProvisioned = (mDeviceOperationalCertProvisioned) ? 1 : 0;
static_assert(std::is_same<std::underlying_type<decltype(mDeviceAddress.GetTransportType())>::type, uint8_t>::value,
"The underlying type of Transport::Type is not uint8_t.");
@@ -232,8 +242,7 @@
// the old counter value (which is 1 less than the updated counter).
mLocalMessageCounter++;
- mCASESessionKeyId = Encoding::LittleEndian::HostSwap16(serializable.mCASESessionKeyId);
- mDeviceProvisioningComplete = (serializable.mDeviceProvisioningComplete != 0);
+ mDeviceOperationalCertProvisioned = (serializable.mDeviceOperationalCertProvisioned != 0);
// The InterfaceNameToId() API requires initialization of mInterface, and lock/unlock of
// LwIP stack.
@@ -309,6 +318,8 @@
void Device::OnConnectionExpired(SecureSessionHandle session)
{
+ VerifyOrReturn(session == mSecureSession,
+ ChipLogDetail(Controller, "Connection expired, but it doesn't match the current session"));
mState = ConnectionState::NotConnected;
mSecureSession = SecureSessionHandle{};
}
@@ -426,8 +437,10 @@
ExitNow(err = CHIP_ERROR_INCORRECT_STATE);
}
- err = pairingSession.FromSerializable(mPairing);
- SuccessOrExit(err);
+ if (mState == ConnectionState::Connecting)
+ {
+ ExitNow(err = CHIP_NO_ERROR);
+ }
if (resetNeeded == ResetTransport::kYes)
{
@@ -445,16 +458,20 @@
SuccessOrExit(err);
}
- err = mSessionManager->NewPairing(Optional<Transport::PeerAddress>::Value(mDeviceAddress), mDeviceId, &pairingSession,
- SecureSession::SessionRole::kInitiator, mAdminId);
- SuccessOrExit(err);
+ if (IsOperationalCertProvisioned())
+ {
+ err = WarmupCASESession();
+ SuccessOrExit(err);
+ }
+ else
+ {
+ err = pairingSession.FromSerializable(mPairing);
+ SuccessOrExit(err);
- // TODO - Enable CASE Session setup before message is sent to a fully provisioned device
- // if (IsProvisioningComplete())
- // {
- // err = EstablishCASESession();
- // SuccessOrExit(err);
- // }
+ err = mSessionManager->NewPairing(Optional<Transport::PeerAddress>::Value(mDeviceAddress), mDeviceId, &pairingSession,
+ SecureSession::SessionRole::kInitiator, mAdminId);
+ SuccessOrExit(err);
+ }
exit:
@@ -475,35 +492,118 @@
return true;
}
-CHIP_ERROR Device::EstablishCASESession()
+void Device::OperationalCertProvisioned()
{
+ VerifyOrReturn(!mDeviceOperationalCertProvisioned,
+ ChipLogDetail(Controller, "Operational certificates already provisioned for this device"));
+
+ ChipLogDetail(Controller, "Enabling CASE session establishment for the device");
+ mDeviceOperationalCertProvisioned = true;
+
+ Persist();
+
+ if (mState == ConnectionState::SecureConnected)
+ {
+ mSessionManager->ExpirePairing(mSecureSession);
+ mState = ConnectionState::NotConnected;
+ }
+}
+
+CHIP_ERROR Device::WarmupCASESession()
+{
+ VerifyOrReturnError(mDeviceOperationalCertProvisioned, CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrReturnError(mState == ConnectionState::NotConnected, CHIP_NO_ERROR);
+
Messaging::ExchangeContext * exchange = mExchangeMgr->NewContext(SecureSessionHandle(), &mCASESession);
VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_INTERNAL);
ReturnErrorOnFailure(mCASESession.MessageDispatch().Init(mSessionManager->GetTransportManager()));
mCASESession.MessageDispatch().SetPeerAddress(mDeviceAddress);
- ReturnErrorOnFailure(mCASESession.EstablishSession(mDeviceAddress, mCredentials, mDeviceId, 0, exchange, this));
+ uint16_t keyID = 0;
+ ReturnErrorOnFailure(mIDAllocator->Allocate(keyID));
+
+ mLocalMessageCounter = 0;
+ mPeerMessageCounter = 0;
+
+ ReturnErrorOnFailure(mCASESession.EstablishSession(mDeviceAddress, mCredentials, mDeviceId, keyID, exchange, this));
+
+ mState = ConnectionState::Connecting;
return CHIP_NO_ERROR;
}
-void Device::OnSessionEstablishmentError(CHIP_ERROR error) {}
+void Device::OnSessionEstablishmentError(CHIP_ERROR error)
+{
+ mState = ConnectionState::NotConnected;
+ mIDAllocator->Free(mCASESession.GetLocalKeyId());
+
+ Cancelable ready;
+ mConnectionFailure.DequeueAll(ready);
+ while (ready.mNext != &ready)
+ {
+ Callback::Callback<OnDeviceConnectionFailure> * cb =
+ Callback::Callback<OnDeviceConnectionFailure>::FromCancelable(ready.mNext);
+
+ cb->Cancel();
+ cb->mCall(cb->mContext, GetDeviceId(), error);
+ }
+}
void Device::OnSessionEstablished()
{
mCASESession.PeerConnection().SetPeerNodeId(mDeviceId);
- // TODO - Enable keys derived from CASE Session
- // CHIP_ERROR err = mSessionManager->NewPairing(Optional<Transport::PeerAddress>::Value(mDeviceAddress), mDeviceId,
- // &mCASESession,
- // SecureSession::SessionRole::kInitiator, mAdminId, nullptr);
- // if (err != CHIP_NO_ERROR)
- // {
- // ChipLogError(Controller, "Failed in setting up CASE secure channel: err %s", ErrorStr(err));
- // OnSessionEstablishmentError(err);
- // return;
- // }
+ CHIP_ERROR err = mSessionManager->NewPairing(Optional<Transport::PeerAddress>::Value(mDeviceAddress), mDeviceId, &mCASESession,
+ SecureSession::SessionRole::kInitiator, mAdminId, nullptr);
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Controller, "Failed in setting up CASE secure channel: err %s", ErrorStr(err));
+ OnSessionEstablishmentError(err);
+ return;
+ }
+
+ Cancelable ready;
+ mConnectionSuccess.DequeueAll(ready);
+ while (ready.mNext != &ready)
+ {
+ Callback::Callback<OnDeviceConnected> * cb = Callback::Callback<OnDeviceConnected>::FromCancelable(ready.mNext);
+
+ cb->Cancel();
+ cb->mCall(cb->mContext, this);
+ }
+}
+
+CHIP_ERROR Device::EstablishConnectivity(Callback::Callback<OnDeviceConnected> * onConnection,
+ Callback::Callback<OnDeviceConnectionFailure> * onFailure)
+{
+ bool loadedSecureSession = false;
+ ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession));
+
+ if (loadedSecureSession)
+ {
+ if (IsOperationalCertProvisioned())
+ {
+ if (onConnection != nullptr)
+ {
+ mConnectionSuccess.Enqueue(onConnection->Cancel());
+ }
+
+ if (onFailure != nullptr)
+ {
+ mConnectionFailure.Enqueue(onFailure->Cancel());
+ }
+ }
+ else
+ {
+ if (onConnection != nullptr)
+ {
+ onConnection->mCall(onConnection->mContext, this);
+ }
+ }
+ }
+
+ return CHIP_NO_ERROR;
}
void Device::AddResponseHandler(uint8_t seqNum, Callback::Cancelable * onSuccessCallback, Callback::Cancelable * onFailureCallback)
diff --git a/src/controller/CHIPDevice.h b/src/controller/CHIPDevice.h
index 9d683a7..e5f8eda 100644
--- a/src/controller/CHIPDevice.h
+++ b/src/controller/CHIPDevice.h
@@ -38,6 +38,7 @@
#include <messaging/ExchangeMgr.h>
#include <protocols/secure_channel/CASESession.h>
#include <protocols/secure_channel/PASESession.h>
+#include <protocols/secure_channel/SessionIDAllocator.h>
#include <setup_payload/SetupPayload.h>
#include <support/Base64.h>
#include <support/DLLUtil.h>
@@ -80,11 +81,17 @@
Inet::InetLayer * inetLayer = nullptr;
PersistentStorageDelegate * storageDelegate = nullptr;
Credentials::OperationalCredentialSet * credentials = nullptr;
+ SessionIDAllocator * idAllocator = nullptr;
#if CONFIG_NETWORK_LAYER_BLE
Ble::BleLayer * bleLayer = nullptr;
#endif
};
+class Device;
+
+typedef void (*OnDeviceConnected)(void * context, Device * device);
+typedef void (*OnDeviceConnectionFailure)(void * context, NodeId deviceId, CHIP_ERROR error);
+
class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEstablishmentDelegate
{
public:
@@ -175,6 +182,7 @@
mAdminId = admin;
mStorageDelegate = params.storageDelegate;
mCredentials = params.credentials;
+ mIDAllocator = params.idAllocator;
#if CONFIG_NETWORK_LAYER_BLE
mBleLayer = params.bleLayer;
#endif
@@ -311,6 +319,8 @@
bool IsSecureConnected() const { return IsActive() && mState == ConnectionState::SecureConnected; }
+ bool IsSessionSetupInProgress() const { return IsActive() && mState == ConnectionState::Connecting; }
+
void Reset();
NodeId GetDeviceId() const { return mDeviceId; }
@@ -334,12 +344,8 @@
Callback::Cancelable * onFailureCallback);
void CancelIMResponseHandler(app::Command * commandObj);
- void ProvisioningComplete(uint16_t caseKeyId)
- {
- mDeviceProvisioningComplete = true;
- mCASESessionKeyId = caseKeyId;
- }
- bool IsProvisioningComplete() const { return mDeviceProvisioningComplete; }
+ void OperationalCertProvisioned();
+ bool IsOperationalCertProvisioned() const { return mDeviceOperationalCertProvisioned; }
//////////// SessionEstablishmentDelegate Implementation ///////////////
void OnSessionEstablishmentError(CHIP_ERROR error) override;
@@ -351,6 +357,23 @@
ByteSpan GetCSRNonce() const { return ByteSpan(mCSRNonce, sizeof(mCSRNonce)); }
+ /*
+ * This function can be called to establish a secure session with the device.
+ *
+ * If the device doesn't have operational credentials, and is under commissioning process,
+ * PASE keys will be used for secure session.
+ *
+ * If the device has been commissioned and has operational credentials, CASE session
+ * setup will be triggered.
+ *
+ * On establishing the session, the callback function `onConnection` will be called. If the
+ * session setup fails, `onFailure` will be called.
+ *
+ * If the session already exists, `onConnection` will be called immediately.
+ */
+ CHIP_ERROR EstablishConnectivity(Callback::Callback<OnDeviceConnected> * onConnection,
+ Callback::Callback<OnDeviceConnectionFailure> * onFailure);
+
private:
enum class ConnectionState
{
@@ -419,22 +442,31 @@
*/
CHIP_ERROR LoadSecureSessionParametersIfNeeded(bool & didLoad);
- CHIP_ERROR EstablishCASESession();
+ /**
+ * This function triggers CASE session setup if the device has been provisioned with
+ * operational credentials, and there is no currently active session.
+ */
+
+ CHIP_ERROR WarmupCASESession();
uint16_t mListenPort;
Transport::AdminId mAdminId = Transport::kUndefinedAdminId;
- bool mDeviceProvisioningComplete = false;
+ bool mDeviceOperationalCertProvisioned = false;
CASESession mCASESession;
- uint16_t mCASESessionKeyId = 0;
Credentials::OperationalCredentialSet * mCredentials = nullptr;
PersistentStorageDelegate * mStorageDelegate = nullptr;
uint8_t mCSRNonce[kOpCSRNonceLength];
+
+ SessionIDAllocator * mIDAllocator = nullptr;
+
+ Callback::CallbackDeque mConnectionSuccess;
+ Callback::CallbackDeque mConnectionFailure;
};
/**
@@ -483,11 +515,10 @@
PASESessionSerializable mOpsCreds;
uint64_t mDeviceId; /* This field is serialized in LittleEndian byte order */
uint8_t mDeviceAddr[INET6_ADDRSTRLEN];
- uint16_t mDevicePort; /* This field is serialized in LittleEndian byte order */
- uint16_t mAdminId; /* This field is serialized in LittleEndian byte order */
- uint16_t mCASESessionKeyId; /* This field is serialized in LittleEndian byte order */
+ uint16_t mDevicePort; /* This field is serialized in LittleEndian byte order */
+ uint16_t mAdminId; /* This field is serialized in LittleEndian byte order */
uint8_t mDeviceTransport;
- uint8_t mDeviceProvisioningComplete;
+ uint8_t mDeviceOperationalCertProvisioned;
uint8_t mInterfaceName[kMaxInterfaceName];
uint32_t mLocalMessageCounter; /* This field is serialized in LittleEndian byte order */
uint32_t mPeerMessageCounter; /* This field is serialized in LittleEndian byte order */
diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp
index 64b4a7f..8fb1b76 100644
--- a/src/controller/CHIPDeviceController.cpp
+++ b/src/controller/CHIPDeviceController.cpp
@@ -96,8 +96,12 @@
constexpr uint32_t kSessionEstablishmentTimeout = 30 * kMillisecondPerSecond;
-constexpr uint32_t kMaxCHIPOpCertLength = 1024;
-constexpr uint32_t kMaxCHIPCSRLength = 1024;
+// TODO - Reduce memory requirement for generating x509 certificates
+// As per specifications (section 6.3.7. Trusted Root CA Certificates), DER certs
+// should require 600 bytes. Currently, due to ASN writer overheads, a larger buffer
+// is needed, even though the generated certificate fits in 600 bytes limit.
+constexpr uint32_t kMaxCHIPDERCertLength = 1024;
+constexpr uint32_t kMaxCHIPCSRLength = 1024;
DeviceController::DeviceController()
{
@@ -211,6 +215,46 @@
return CHIP_NO_ERROR;
}
+CHIP_ERROR DeviceController::GenerateOperationalCertificates(const ByteSpan & CSR, NodeId deviceId, MutableByteSpan & cert)
+{
+ // This code requires about 2K RAM to generate the certificates.
+ // The code would run as part of commissioner applications, so RAM requirements should be fine.
+ // Need to analyze if this requirement could be better managed by using static memory pools.
+ chip::Platform::ScopedMemoryBuffer<uint8_t> noc;
+ ReturnErrorCodeIf(!noc.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
+
+ uint32_t nocLen = 0;
+ ChipLogProgress(Controller, "Generating operational certificate for device " ChipLogFormatX64, ChipLogValueX64(deviceId));
+ ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNodeOperationalCertificate(
+ PeerId().SetNodeId(deviceId), CSR, 1, noc.Get(), kMaxCHIPDERCertLength, nocLen));
+
+ ReturnErrorCodeIf(nocLen == 0, CHIP_ERROR_CERT_NOT_FOUND);
+
+ chip::Platform::ScopedMemoryBuffer<uint8_t> ica;
+ ReturnErrorCodeIf(!ica.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
+
+ uint32_t icaLen = 0;
+ ChipLogProgress(Controller, "Getting intermediate CA certificate from the issuer");
+ CHIP_ERROR err = mOperationalCredentialsDelegate->GetIntermediateCACertificate(0, ica.Get(), kMaxCHIPDERCertLength, icaLen);
+ ChipLogProgress(Controller, "GetIntermediateCACertificate returned %" PRId32, err);
+ if (err == CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED)
+ {
+ // This implies that the commissioner application uses root CA to sign the operational
+ // certificates, and an intermediate CA is not needed. It's not an error condition, so
+ // let's just send operational certificate and root CA certificate to the device.
+ icaLen = 0;
+ ChipLogProgress(Controller, "Intermediate CA is not needed");
+ }
+ else if (err != CHIP_NO_ERROR)
+ {
+ return err;
+ }
+
+ ReturnErrorOnFailure(ConvertX509CertsToChipCertArray(ByteSpan(noc.Get(), nocLen), ByteSpan(ica.Get(), icaLen), cert));
+
+ return CHIP_NO_ERROR;
+}
+
CHIP_ERROR DeviceController::LoadLocalCredentials(Transport::AdminPairingInfo * admin)
{
ChipLogProgress(Controller, "Getting operational keys");
@@ -220,38 +264,41 @@
if (!admin->AreCredentialsAvailable())
{
- chip::Platform::ScopedMemoryBuffer<uint8_t> buffer1;
- ReturnErrorCodeIf(!buffer1.Alloc(kMaxCHIPCSRLength), CHIP_ERROR_NO_MEMORY);
-
- chip::Platform::ScopedMemoryBuffer<uint8_t> buffer2;
- ReturnErrorCodeIf(!buffer2.Alloc(kMaxCHIPOpCertLength), CHIP_ERROR_NO_MEMORY);
-
- uint8_t * CSR = buffer1.Get();
- size_t csrLength = kMaxCHIPCSRLength;
- ReturnErrorOnFailure(keypair->NewCertificateSigningRequest(CSR, csrLength));
-
- uint8_t * cert = buffer2.Get();
- uint32_t certLen = 0;
-
- // TODO - Match the generated cert against CSR and operational keypair
- // Make sure it chains back to the trusted root.
- ChipLogProgress(Controller, "Generating operational certificate for the controller");
- ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNodeOperationalCertificate(
- PeerId().SetNodeId(mLocalDeviceId), ByteSpan(CSR, csrLength), 1, cert, kMaxCHIPOpCertLength, certLen));
-
- uint8_t * chipCert = buffer1.Get();
+ chip::Platform::ScopedMemoryBuffer<uint8_t> chipCert;
+ uint32_t chipCertAllocatedLen = kMaxCHIPCertLength * 2;
+ ReturnErrorCodeIf(!chipCert.Alloc(chipCertAllocatedLen), CHIP_ERROR_NO_MEMORY);
uint32_t chipCertLen = 0;
- ReturnErrorOnFailure(ConvertX509CertToChipCert(cert, certLen, chipCert, kMaxCHIPOpCertLength, chipCertLen));
- ReturnErrorOnFailure(admin->SetOperationalCert(ByteSpan(chipCert, chipCertLen)));
+ // Get root CA certificate
+ {
+ ChipLogProgress(Controller, "Getting root certificate for the controller from the issuer");
+ chip::Platform::ScopedMemoryBuffer<uint8_t> rootCert;
+ ReturnErrorCodeIf(!rootCert.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
+ uint32_t rootCertLen = 0;
- ChipLogProgress(Controller, "Getting root certificate for the controller from the issuer");
- ReturnErrorOnFailure(mOperationalCredentialsDelegate->GetRootCACertificate(0, cert, kMaxCHIPOpCertLength, certLen));
+ ReturnErrorOnFailure(
+ mOperationalCredentialsDelegate->GetRootCACertificate(0, rootCert.Get(), kMaxCHIPDERCertLength, rootCertLen));
+ ReturnErrorOnFailure(
+ ConvertX509CertToChipCert(rootCert.Get(), rootCertLen, chipCert.Get(), chipCertAllocatedLen, chipCertLen));
- chipCertLen = 0;
- ReturnErrorOnFailure(ConvertX509CertToChipCert(cert, certLen, chipCert, kMaxCHIPOpCertLength, chipCertLen));
+ ReturnErrorOnFailure(admin->SetRootCert(ByteSpan(chipCert.Get(), chipCertLen)));
+ }
- ReturnErrorOnFailure(admin->SetRootCert(ByteSpan(chipCert, chipCertLen)));
+ // Generate Operational Certificates (NOC and ICAC)
+ {
+ chip::Platform::ScopedMemoryBuffer<uint8_t> CSR;
+ size_t csrLength = kMaxCHIPCSRLength;
+ ReturnErrorCodeIf(!CSR.Alloc(csrLength), CHIP_ERROR_NO_MEMORY);
+
+ ReturnErrorOnFailure(keypair->NewCertificateSigningRequest(CSR.Get(), csrLength));
+
+ // TODO - Match the generated cert against CSR and operational keypair
+ // Make sure it chains back to the trusted root.
+ ChipLogProgress(Controller, "Generating operational certificate for the controller");
+ MutableByteSpan chipCertSpan(chipCert.Get(), chipCertAllocatedLen);
+ ReturnErrorOnFailure(GenerateOperationalCertificates(ByteSpan(CSR.Get(), csrLength), mLocalDeviceId, chipCertSpan));
+ ReturnErrorOnFailure(admin->SetOperationalCert(chipCertSpan));
+ }
ReturnErrorOnFailure(mAdmins.Store(admin->GetAdminId()));
}
@@ -361,39 +408,6 @@
return CHIP_NO_ERROR;
}
-CHIP_ERROR DeviceController::GetDevice(NodeId deviceId, const SerializedDevice & deviceInfo, Device ** out_device)
-{
- Device * device = nullptr;
-
- VerifyOrReturnError(out_device != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
- uint16_t index = FindDeviceIndex(deviceId);
-
- if (index < kNumMaxActiveDevices)
- {
- device = &mActiveDevices[index];
- }
- else
- {
- VerifyOrReturnError(mPairedDevices.Contains(deviceId), CHIP_ERROR_NOT_CONNECTED);
-
- index = GetInactiveDeviceIndex();
- VerifyOrReturnError(index < kNumMaxActiveDevices, CHIP_ERROR_NO_MEMORY);
- device = &mActiveDevices[index];
-
- CHIP_ERROR err = device->Deserialize(deviceInfo);
- if (err != CHIP_NO_ERROR)
- {
- ReleaseDevice(device);
- ReturnErrorOnFailure(err);
- }
-
- device->Init(GetControllerDeviceInitParams(), mListenPort, mAdminId);
- }
-
- *out_device = device;
- return CHIP_NO_ERROR;
-}
-
CHIP_ERROR DeviceController::GetDevice(NodeId deviceId, Device ** out_device)
{
CHIP_ERROR err = CHIP_NO_ERROR;
@@ -444,14 +458,47 @@
return err;
}
-CHIP_ERROR DeviceController::UpdateDevice(Device * device, uint64_t fabricId)
+bool DeviceController::DoesDevicePairingExist(const PeerId & deviceId)
{
- // TODO - Detect when the device is fully provisioned, instead of relying on UpdateDevice()
- device->ProvisioningComplete(mNextKeyId++);
- PersistDevice(device);
- PersistNextKeyId();
+ if (InitializePairedDeviceList() == CHIP_NO_ERROR)
+ {
+ return mPairedDevices.Contains(deviceId.GetNodeId());
+ }
+
+ return false;
+}
+
+CHIP_ERROR DeviceController::GetConnectedDevice(NodeId deviceId, Callback::Callback<OnDeviceConnected> * onConnection,
+ Callback::Callback<OnDeviceConnectionFailure> * onFailure)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ Device * device = nullptr;
+
+ err = GetDevice(deviceId, &device);
+ SuccessOrExit(err);
+
+ if (device->IsSecureConnected())
+ {
+ onConnection->mCall(onConnection->mContext, device);
+ return CHIP_NO_ERROR;
+ }
+
+ err = device->EstablishConnectivity(onConnection, onFailure);
+ SuccessOrExit(err);
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ onFailure->mCall(onFailure->mContext, deviceId, err);
+ }
+
+ return err;
+}
+
+CHIP_ERROR DeviceController::UpdateDevice(NodeId deviceId, uint64_t fabricId)
+{
#if CHIP_DEVICE_CONFIG_ENABLE_MDNS
- return Mdns::Resolver::Instance().ResolveNodeId(chip::PeerId().SetNodeId(device->GetDeviceId()).SetFabricId(fabricId),
+ return Mdns::Resolver::Instance().ResolveNodeId(chip::PeerId().SetNodeId(deviceId).SetFabricId(fabricId),
chip::Inet::kIPAddressType_Any);
#else
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
@@ -690,7 +737,8 @@
{
if (mStorageDelegate != nullptr && mState == State::Initialized)
{
- mStorageDelegate->SyncSetKeyValue(kNextAvailableKeyID, &mNextKeyId, sizeof(mNextKeyId));
+ uint16_t nextKeyID = mIDAllocator.Peek();
+ mStorageDelegate->SyncSetKeyValue(kNextAvailableKeyID, &nextKeyID, sizeof(nextKeyID));
}
}
@@ -738,6 +786,7 @@
.inetLayer = mInetLayer,
.storageDelegate = mStorageDelegate,
.credentials = &mCredentials,
+ .idAllocator = &mIDAllocator,
};
}
@@ -746,7 +795,8 @@
mOpCSRResponseCallback(OnOperationalCertificateSigningRequest, this),
mOpCertResponseCallback(OnOperationalCertificateAddResponse, this), mRootCertResponseCallback(OnRootCertSuccessResponse, this),
mOnCSRFailureCallback(OnCSRFailureResponse, this), mOnCertFailureCallback(OnAddOpCertFailureResponse, this),
- mOnRootCertFailureCallback(OnRootCertFailureResponse, this)
+ mOnRootCertFailureCallback(OnRootCertFailureResponse, this), mOnDeviceConnectedCallback(OnDeviceConnectedFn, this),
+ mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this)
{
mPairingDelegate = nullptr;
mDeviceBeingPaired = kNumMaxActiveDevices;
@@ -757,12 +807,14 @@
{
ReturnErrorOnFailure(DeviceController::Init(localDeviceId, params));
- uint16_t size = sizeof(mNextKeyId);
- CHIP_ERROR error = mStorageDelegate->SyncGetKeyValue(kNextAvailableKeyID, &mNextKeyId, size);
- if (error || (size != sizeof(mNextKeyId)))
+ uint16_t nextKeyID = 0;
+ uint16_t size = sizeof(nextKeyID);
+ CHIP_ERROR error = mStorageDelegate->SyncGetKeyValue(kNextAvailableKeyID, &nextKeyID, size);
+ if (error || (size != sizeof(nextKeyID)))
{
- mNextKeyId = 0;
+ nextKeyID = 0;
}
+ ReturnErrorOnFailure(mIDAllocator.ReserveUpTo(nextKeyID));
mPairingDelegate = params.pairingDelegate;
return CHIP_NO_ERROR;
@@ -790,6 +842,8 @@
Messaging::ExchangeContext * exchangeCtxt = nullptr;
+ uint16_t keyID = 0;
+
Transport::AdminPairingInfo * admin = mAdmins.FindAdminWithId(mAdminId);
VerifyOrExit(remoteDeviceId != kAnyNodeId && remoteDeviceId != kUndefinedNodeId, err = CHIP_ERROR_INVALID_ARGUMENT);
@@ -858,7 +912,10 @@
exchangeCtxt = mExchangeMgr->NewContext(SecureSessionHandle(), &mPairingSession);
VerifyOrExit(exchangeCtxt != nullptr, err = CHIP_ERROR_INTERNAL);
- err = mPairingSession.Pair(params.GetPeerAddress(), params.GetSetupPINCode(), mNextKeyId++, exchangeCtxt, this);
+ err = mIDAllocator.Allocate(keyID);
+ SuccessOrExit(err);
+
+ err = mPairingSession.Pair(params.GetPeerAddress(), params.GetSetupPINCode(), keyID, exchangeCtxt, this);
// Immediately persist the updted mNextKeyID value
// TODO maybe remove FreeRendezvousSession() since mNextKeyID is always persisted immediately
PersistNextKeyId();
@@ -996,6 +1053,20 @@
return CHIP_NO_ERROR;
}
+CHIP_ERROR DeviceCommissioner::OperationalDiscoveryComplete(NodeId remoteDeviceId)
+{
+ ChipLogProgress(Controller, "OperationalDiscoveryComplete for device ID %" PRIu64, remoteDeviceId);
+ VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE);
+
+ Device * device = nullptr;
+ ReturnErrorOnFailure(GetDevice(remoteDeviceId, &device));
+ device->OperationalCertProvisioned();
+ PersistDevice(device);
+ PersistNextKeyId();
+
+ return GetConnectedDevice(remoteDeviceId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
+}
+
void DeviceCommissioner::FreeRendezvousSession()
{
PersistNextKeyId();
@@ -1058,7 +1129,7 @@
return;
}
- ChipLogDetail(Controller, "Remote device completed SPAKE2+ handshake\n");
+ ChipLogDetail(Controller, "Remote device completed SPAKE2+ handshake");
// TODO: Add code to receive OpCSR from the device, and process the signing request
// For IP rendezvous, this is sent as part of the state machine.
@@ -1146,43 +1217,15 @@
VerifyOrReturnError(CSRNonce.size() == nonce.size(), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(memcmp(CSRNonce.data(), nonce.data(), CSRNonce.size()) == 0, CHIP_ERROR_INVALID_ARGUMENT);
- chip::Platform::ScopedMemoryBuffer<uint8_t> noc;
- ReturnErrorCodeIf(!noc.Alloc(kMaxCHIPOpCertLength), CHIP_ERROR_NO_MEMORY);
-
- uint32_t nocLen = 0;
- ChipLogProgress(Controller, "Generating operational certificate for device " ChipLogFormatX64,
- ChipLogValueX64(device->GetDeviceId()));
- ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNodeOperationalCertificate(
- PeerId().SetNodeId(device->GetDeviceId()), CSR, 1, noc.Get(), kMaxCHIPOpCertLength, nocLen));
-
- ReturnErrorCodeIf(nocLen == 0, CHIP_ERROR_CERT_NOT_FOUND);
-
- chip::Platform::ScopedMemoryBuffer<uint8_t> ica;
- ReturnErrorCodeIf(!ica.Alloc(kMaxCHIPOpCertLength), CHIP_ERROR_NO_MEMORY);
-
- uint32_t icaLen = 0;
- ChipLogProgress(Controller, "Getting intermediate CA certificate from the issuer");
- CHIP_ERROR err = mOperationalCredentialsDelegate->GetIntermediateCACertificate(0, ica.Get(), kMaxCHIPOpCertLength, icaLen);
- ChipLogProgress(Controller, "GetIntermediateCACertificate returned %" PRId32, err);
- if (err == CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED)
- {
- // This implies that the commissioner application uses root CA to sign the operational
- // certificates, and an intermediate CA is not needed. It's not an error condition, so
- // let's just send operational certificate and root CA certificate to the device.
- err = CHIP_NO_ERROR;
- icaLen = 0;
- ChipLogProgress(Controller, "Intermediate CA is not needed");
- }
- ReturnErrorOnFailure(err);
-
chip::Platform::ScopedMemoryBuffer<uint8_t> chipOpCert;
- ReturnErrorCodeIf(!chipOpCert.Alloc(kMaxCHIPOpCertLength * 2), CHIP_ERROR_NO_MEMORY);
- uint32_t chipOpCertLen = 0;
- ReturnErrorOnFailure(ConvertX509CertsToChipCertArray(ByteSpan(noc.Get(), nocLen), ByteSpan(ica.Get(), icaLen), chipOpCert.Get(),
- kMaxCHIPOpCertLength * 2, chipOpCertLen));
+ ReturnErrorCodeIf(!chipOpCert.Alloc(kMaxCHIPCertLength * 2), CHIP_ERROR_NO_MEMORY);
- ChipLogProgress(Controller, "Sending operational certificate to the device. Op Cert Len %" PRIu32, chipOpCertLen);
- ReturnErrorOnFailure(SendOperationalCertificate(device, ByteSpan(chipOpCert.Get(), chipOpCertLen)));
+ MutableByteSpan chipCertSpan(chipOpCert.Get(), kMaxCHIPCertLength * 2);
+
+ ReturnErrorOnFailure(GenerateOperationalCertificates(CSR, device->GetDeviceId(), chipCertSpan));
+
+ ChipLogProgress(Controller, "Sending operational certificate to the device");
+ ReturnErrorOnFailure(SendOperationalCertificate(device, chipCertSpan));
return CHIP_NO_ERROR;
}
@@ -1489,6 +1532,7 @@
}
}
DeviceController::OnNodeIdResolved(nodeData);
+ OperationalDiscoveryComplete(nodeData.mPeerId.GetNodeId());
}
void DeviceCommissioner::OnNodeIdResolutionFailed(const chip::PeerId & peer, CHIP_ERROR error)
@@ -1506,6 +1550,26 @@
#endif
+void DeviceCommissioner::OnDeviceConnectedFn(void * context, Device * device)
+{
+ DeviceCommissioner * commissioner = reinterpret_cast<DeviceCommissioner *>(context);
+ VerifyOrReturn(commissioner != nullptr, ChipLogProgress(Controller, "Device connected callback with null context. Ignoring"));
+ VerifyOrReturn(commissioner->mPairingDelegate != nullptr,
+ ChipLogProgress(Controller, "Device connected callback with null pairing delegate. Ignoring"));
+ commissioner->mPairingDelegate->OnCommissioningComplete(device->GetDeviceId(), CHIP_NO_ERROR);
+}
+
+void DeviceCommissioner::OnDeviceConnectionFailureFn(void * context, NodeId deviceId, CHIP_ERROR error)
+{
+ DeviceCommissioner * commissioner = reinterpret_cast<DeviceCommissioner *>(context);
+ ChipLogProgress(Controller, "Device connection failed. Error %s", ErrorStr(error));
+ VerifyOrReturn(commissioner != nullptr,
+ ChipLogProgress(Controller, "Device connection failure callback with null context. Ignoring"));
+ VerifyOrReturn(commissioner->mPairingDelegate != nullptr,
+ ChipLogProgress(Controller, "Device connection failure callback with null pairing delegate. Ignoring"));
+ commissioner->mPairingDelegate->OnCommissioningComplete(deviceId, error);
+}
+
CommissioningStage DeviceCommissioner::GetNextCommissioningStage()
{
switch (mCommissioningStage)
diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h
index c57caa7..30273a5 100644
--- a/src/controller/CHIPDeviceController.h
+++ b/src/controller/CHIPDeviceController.h
@@ -143,6 +143,11 @@
* @param error Error cause, if any
*/
virtual void OnPairingDeleted(CHIP_ERROR error) {}
+
+ /**
+ * Called when the commissioning process is complete (with success or error)
+ */
+ virtual void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error) {}
};
struct CommissionerInitParams : public ControllerInitParams
@@ -204,21 +209,6 @@
/**
* @brief
- * This function deserializes the provided deviceInfo object, and initializes and outputs the
- * corresponding Device object. The lifetime of the output object is tied to that of the DeviceController
- * object. The caller must not use the Device object If they free the DeviceController object, or
- * after they call ReleaseDevice() on the returned device object.
- *
- * @param[in] deviceId Node ID for the CHIP device
- * @param[in] deviceInfo Serialized device info for the device
- * @param[out] device The output device object
- *
- * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code.
- */
- CHIP_ERROR GetDevice(NodeId deviceId, const SerializedDevice & deviceInfo, Device ** device);
-
- /**
- * @brief
* This function is similar to the other GetDevice object, except it reads the serialized object from
* the persistent storage.
*
@@ -230,16 +220,30 @@
CHIP_ERROR GetDevice(NodeId deviceId, Device ** device);
/**
+ * This function returns true if the device corresponding to `deviceId` has previously been commissioned
+ * on the fabric.
+ */
+ bool DoesDevicePairingExist(const PeerId & deviceId);
+
+ /**
+ * This function finds the device corresponding to deviceId, and establishes a secure connection with it.
+ * Once the connection is successfully establishes (or if it's already connected), it calls `onConnectedDevice`
+ * callback. If it fails to establish the connection, it calls `onError` callback.
+ */
+ CHIP_ERROR GetConnectedDevice(NodeId deviceId, Callback::Callback<OnDeviceConnected> * onConnection,
+ Callback::Callback<OnDeviceConnectionFailure> * onFailure);
+
+ /**
* @brief
* This function update the device informations asynchronously using mdns.
* If new device informations has been found, it will be persisted.
*
- * @param[in] device The input device object to update
+ * @param[in] deviceId Node ID for the CHIP devicex
* @param[in] fabricId The fabricId used for mdns resolution
*
* @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code.
*/
- CHIP_ERROR UpdateDevice(Device * device, uint64_t fabricId);
+ CHIP_ERROR UpdateDevice(NodeId deviceId, uint64_t fabricId);
void PersistDevice(Device * device);
@@ -334,7 +338,7 @@
Credentials::OperationalCredentialSet mCredentials;
Credentials::CertificateKeyId mRootKeyId;
- uint16_t mNextKeyId = 0;
+ SessionIDAllocator mIDAllocator;
#if CHIP_DEVICE_CONFIG_ENABLE_MDNS
//////////// ResolverDelegate Implementation ///////////////
@@ -343,6 +347,13 @@
Mdns::DiscoveredNodeData * GetDiscoveredNodes() override { return mCommissionableNodes; }
#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS
+ // This function uses `OperationalCredentialsDelegate` to generate the operational certificates
+ // for the given device. The output is a TLV encoded array of compressed CHIP certificates. The
+ // array can contain up to two certificates (node operational certificate, and ICA certificate).
+ // If the certificate issuer doesn't require an ICA (i.e. NOC is signed by the root CA), the array
+ // will have only one certificate (node operational certificate).
+ CHIP_ERROR GenerateOperationalCertificates(const ByteSpan & CSR, NodeId deviceId, MutableByteSpan & cert);
+
private:
//////////// ExchangeDelegate Implementation ///////////////
void OnMessageReceived(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
@@ -445,6 +456,16 @@
*/
CHIP_ERROR UnpairDevice(NodeId remoteDeviceId);
+ /**
+ * This function call indicates that the device has been provisioned with operational
+ * credentials, and is reachable on operational network. At this point, the device is
+ * available for CASE session establishment.
+ *
+ * The function updates the state of device proxy object such that all subsequent messages
+ * will use secure session established via CASE handshake.
+ */
+ CHIP_ERROR OperationalDiscoveryComplete(NodeId remoteDeviceId);
+
//////////// SessionEstablishmentDelegate Implementation ///////////////
void OnSessionEstablishmentError(CHIP_ERROR error) override;
void OnSessionEstablished() override;
@@ -584,6 +605,9 @@
/* Callback called when adding root cert to device results in failure */
static void OnRootCertFailureResponse(void * context, uint8_t status);
+ static void OnDeviceConnectedFn(void * context, Device * device);
+ static void OnDeviceConnectionFailureFn(void * context, NodeId deviceId, CHIP_ERROR error);
+
/**
* @brief
* This function processes the CSR sent by the device.
@@ -612,6 +636,9 @@
Callback::Callback<DefaultFailureCallback> mOnCertFailureCallback;
Callback::Callback<DefaultFailureCallback> mOnRootCertFailureCallback;
+ Callback::Callback<OnDeviceConnected> mOnDeviceConnectedCallback;
+ Callback::Callback<OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;
+
PASESession mPairingSession;
};
diff --git a/src/controller/ExampleOperationalCredentialsIssuer.cpp b/src/controller/ExampleOperationalCredentialsIssuer.cpp
index 84bf3b6..c007e1a 100644
--- a/src/controller/ExampleOperationalCredentialsIssuer.cpp
+++ b/src/controller/ExampleOperationalCredentialsIssuer.cpp
@@ -18,6 +18,7 @@
#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <credentials/CHIPCert.h>
+#include <support/CHIPMem.h>
namespace chip {
namespace Controller {
@@ -29,6 +30,16 @@
CHIP_ERROR ExampleOperationalCredentialsIssuer::Initialize(PersistentStorageDelegate & storage)
{
+ using namespace ASN1;
+ ASN1UniversalTime effectiveTime;
+
+ // Initializing the default start validity to start of 2021. The default validity duration is 10 years.
+ CHIP_ZERO_AT(effectiveTime);
+ effectiveTime.Year = 2021;
+ effectiveTime.Month = 1;
+ effectiveTime.Day = 1;
+ ReturnErrorOnFailure(ASN1ToChipEpochTime(effectiveTime, mNow));
+
Crypto::P256SerializedKeypair serializedKey;
uint16_t keySize = static_cast<uint16_t>(sizeof(serializedKey));
diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h
index 0dfaa4e..abf7a17 100755
--- a/src/credentials/CHIPCert.h
+++ b/src/credentials/CHIPCert.h
@@ -44,6 +44,9 @@
static constexpr uint32_t kChip64bitAttrUTF8Length = 16;
static constexpr uint16_t kX509NoWellDefinedExpirationDateYear = 9999;
+// As per specifications (section 6.3.7. Trusted Root CA Certificates)
+static constexpr uint32_t kMaxCHIPCertLength = 400;
+
/** Data Element Tags for the CHIP Certificate
*/
enum
@@ -642,16 +645,13 @@
*
* The API enforces that the NOC is issued by ICA (if ICA is provided).
*
- * @param x509NOC Node operational credentials certificate in X.509 DER encoding.
- * @param x509ICAC Intermediate CA certificate in X.509 DER encoding.
- * @param chipCertArrayBuf Buffer to store converted certificates in CHIP format.
- * @param chipCertArrayBufSize The size of the buffer to store converted certificates.
- * @param chipCertBufLen The length of the converted certificates.
+ * @param x509NOC Node operational credentials certificate in X.509 DER encoding.
+ * @param x509ICAC Intermediate CA certificate in X.509 DER encoding.
+ * @param chipCertArray Buffer to store converted certificates in CHIP format.
*
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
-CHIP_ERROR ConvertX509CertsToChipCertArray(const ByteSpan & x509NOC, const ByteSpan & x509ICAC, uint8_t * chipCertArrayBuf,
- uint32_t chipCertArrayBufSize, uint32_t & chipCertBufLen);
+CHIP_ERROR ConvertX509CertsToChipCertArray(const ByteSpan & x509NOC, const ByteSpan & x509ICAC, MutableByteSpan & chipCertArray);
/**
* @brief Convert CHIP certificate to the standard X.509 DER encoded certificate.
diff --git a/src/credentials/CHIPCertFromX509.cpp b/src/credentials/CHIPCertFromX509.cpp
index 6cd0e6e..f2f9fa1 100644
--- a/src/credentials/CHIPCertFromX509.cpp
+++ b/src/credentials/CHIPCertFromX509.cpp
@@ -728,14 +728,17 @@
return err;
}
-CHIP_ERROR ConvertX509CertsToChipCertArray(const ByteSpan & x509NOC, const ByteSpan & x509ICAC, uint8_t * chipCertArrayBuf,
- uint32_t chipCertArrayBufSize, uint32_t & chipCertBufLen)
+CHIP_ERROR ConvertX509CertsToChipCertArray(const ByteSpan & x509NOC, const ByteSpan & x509ICAC, MutableByteSpan & chipCertArray)
{
// NOC is mandatory
VerifyOrReturnError(x509NOC.size() > 0, CHIP_ERROR_INVALID_ARGUMENT);
TLVWriter writer;
- writer.Init(chipCertArrayBuf, chipCertArrayBufSize);
+
+ // We can still generate the certificate if the output chip cert buffer is bigger than UINT32_MAX,
+ // since generated cert needs less space than UINT32_MAX.
+ uint32_t chipCertBufLen = (chipCertArray.size() > UINT32_MAX) ? UINT32_MAX : static_cast<uint32_t>(chipCertArray.size());
+ writer.Init(chipCertArray.data(), chipCertBufLen);
TLVType outerContainer;
ReturnErrorOnFailure(writer.StartContainer(AnonymousTag, kTLVType_Array, outerContainer));
@@ -760,7 +763,8 @@
ReturnErrorOnFailure(writer.EndContainer(outerContainer));
ReturnErrorOnFailure(writer.Finalize());
- chipCertBufLen = writer.GetLengthWritten();
+ ReturnErrorCodeIf(writer.GetLengthWritten() > chipCertBufLen, CHIP_ERROR_INTERNAL);
+ chipCertArray.reduce_size(writer.GetLengthWritten());
return CHIP_NO_ERROR;
}
diff --git a/src/credentials/tests/TestChipCert.cpp b/src/credentials/tests/TestChipCert.cpp
index a404afa..544936f 100644
--- a/src/credentials/tests/TestChipCert.cpp
+++ b/src/credentials/tests/TestChipCert.cpp
@@ -1009,20 +1009,22 @@
sizeof(noc_cert), noc_len) == CHIP_NO_ERROR);
uint8_t outCertBuf[kTestCertBufSize * 2];
- uint32_t outCertLen;
+ MutableByteSpan outCert(outCertBuf, sizeof(outCertBuf));
NL_TEST_ASSERT(inSuite,
- ConvertX509CertsToChipCertArray(ByteSpan(noc_cert, noc_len), ByteSpan(ica_cert, ica_len), outCertBuf,
- sizeof(outCertBuf), outCertLen) == CHIP_NO_ERROR);
+ ConvertX509CertsToChipCertArray(ByteSpan(noc_cert, noc_len), ByteSpan(ica_cert, ica_len), outCert) ==
+ CHIP_NO_ERROR);
+ NL_TEST_ASSERT(inSuite, outCert.size() <= sizeof(outCertBuf));
ChipCertificateSet certSet;
NL_TEST_ASSERT(inSuite, certSet.Init(3, kTestCertBufSize * 3) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite,
- certSet.LoadCerts(outCertBuf, outCertLen, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)) ==
- CHIP_NO_ERROR);
+ certSet.LoadCerts(outCert.data(), static_cast<uint32_t>(outCert.size()),
+ BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)) == CHIP_NO_ERROR);
static uint8_t rootCertBuf[kTestCertBufSize];
+ uint32_t outCertLen;
NL_TEST_ASSERT(inSuite,
ConvertX509CertToChipCert(root_cert, root_len, rootCertBuf, sizeof(rootCertBuf), outCertLen) == CHIP_NO_ERROR);
NL_TEST_ASSERT(
@@ -1071,16 +1073,17 @@
uint8_t outCertBuf[kTestCertBufSize * 2];
uint32_t outCertLen;
+ MutableByteSpan outCert(outCertBuf, sizeof(outCertBuf));
NL_TEST_ASSERT(inSuite,
- ConvertX509CertsToChipCertArray(ByteSpan(noc_cert, noc_len), ByteSpan(nullptr, 0), outCertBuf,
- sizeof(outCertBuf), outCertLen) == CHIP_NO_ERROR);
+ ConvertX509CertsToChipCertArray(ByteSpan(noc_cert, noc_len), ByteSpan(nullptr, 0), outCert) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT(inSuite, outCert.size() <= sizeof(outCertBuf));
ChipCertificateSet certSet;
NL_TEST_ASSERT(inSuite, certSet.Init(3, kTestCertBufSize * 3) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite,
- certSet.LoadCerts(outCertBuf, outCertLen, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)) ==
- CHIP_NO_ERROR);
+ certSet.LoadCerts(outCert.data(), static_cast<uint32_t>(outCert.size()),
+ BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)) == CHIP_NO_ERROR);
static uint8_t rootCertBuf[kTestCertBufSize];
@@ -1142,16 +1145,16 @@
sizeof(noc_cert), noc_len) == CHIP_NO_ERROR);
uint8_t outCertBuf[kTestCertBufSize * 2];
- uint32_t outCertLen;
+ MutableByteSpan outCert(outCertBuf, sizeof(outCertBuf));
// Test that NOC is mandatory
NL_TEST_ASSERT(inSuite,
- ConvertX509CertsToChipCertArray(ByteSpan(nullptr, 0), ByteSpan(ica_cert, ica_len), outCertBuf,
- sizeof(outCertBuf), outCertLen) == CHIP_ERROR_INVALID_ARGUMENT);
+ ConvertX509CertsToChipCertArray(ByteSpan(nullptr, 0), ByteSpan(ica_cert, ica_len), outCert) ==
+ CHIP_ERROR_INVALID_ARGUMENT);
// Test that NOC issuer must match ICA
NL_TEST_ASSERT(inSuite,
- ConvertX509CertsToChipCertArray(ByteSpan(noc_cert, noc_len), ByteSpan(root_cert, root_len), outCertBuf,
- sizeof(outCertBuf), outCertLen) == CHIP_ERROR_INVALID_ARGUMENT);
+ ConvertX509CertsToChipCertArray(ByteSpan(noc_cert, noc_len), ByteSpan(root_cert, root_len), outCert) ==
+ CHIP_ERROR_INVALID_ARGUMENT);
X509CertRequestParams ica_params_wrong_fabric = { 1234, 0xabcdabcd, 631161876, 729942000, true, 0x9999, false, 0 };
@@ -1160,8 +1163,8 @@
ica_len) == CHIP_NO_ERROR);
// Test that NOC fabric must match ICA fabric
NL_TEST_ASSERT(inSuite,
- ConvertX509CertsToChipCertArray(ByteSpan(noc_cert, noc_len), ByteSpan(ica_cert, ica_len), outCertBuf,
- sizeof(outCertBuf), outCertLen) == CHIP_ERROR_INVALID_ARGUMENT);
+ ConvertX509CertsToChipCertArray(ByteSpan(noc_cert, noc_len), ByteSpan(ica_cert, ica_len), outCert) ==
+ CHIP_ERROR_INVALID_ARGUMENT);
}
/**
diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController.mm b/src/darwin/Framework/CHIP/CHIPDeviceController.mm
index 47d96c7..0d92975 100644
--- a/src/darwin/Framework/CHIP/CHIPDeviceController.mm
+++ b/src/darwin/Framework/CHIP/CHIPDeviceController.mm
@@ -383,17 +383,10 @@
return;
}
dispatch_sync(_chipWorkQueue, ^{
- chip::Controller::Device * device = nullptr;
-
if ([self isRunning]) {
- errorCode = self.cppCommissioner->GetDevice(deviceID, &device);
+ errorCode = self.cppCommissioner->UpdateDevice(deviceID, fabricId);
+ CHIP_LOG_ERROR("Update device address returned: %d", errorCode);
}
-
- if (errorCode != CHIP_NO_ERROR) {
- return;
- }
-
- errorCode = self.cppCommissioner->UpdateDevice(device, fabricId);
});
}
diff --git a/src/protocols/secure_channel/BUILD.gn b/src/protocols/secure_channel/BUILD.gn
index e499545..525406a 100644
--- a/src/protocols/secure_channel/BUILD.gn
+++ b/src/protocols/secure_channel/BUILD.gn
@@ -14,6 +14,8 @@
"SessionEstablishmentDelegate.h",
"SessionEstablishmentExchangeDispatch.cpp",
"SessionEstablishmentExchangeDispatch.h",
+ "SessionIDAllocator.cpp",
+ "SessionIDAllocator.h",
"StatusReport.cpp",
"StatusReport.h",
]
diff --git a/src/protocols/secure_channel/CASEServer.cpp b/src/protocols/secure_channel/CASEServer.cpp
index 25adca9..74c6d09 100644
--- a/src/protocols/secure_channel/CASEServer.cpp
+++ b/src/protocols/secure_channel/CASEServer.cpp
@@ -30,7 +30,8 @@
namespace chip {
CHIP_ERROR CASEServer::ListenForSessionEstablishment(Messaging::ExchangeManager * exchangeManager, TransportMgrBase * transportMgr,
- SecureSessionMgr * sessionMgr, Transport::AdminPairingTable * admins)
+ SecureSessionMgr * sessionMgr, Transport::AdminPairingTable * admins,
+ SessionIDAllocator * idAllocator)
{
VerifyOrReturnError(transportMgr != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(exchangeManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
@@ -40,6 +41,7 @@
mSessionMgr = sessionMgr;
mAdmins = admins;
mExchangeManager = exchangeManager;
+ mIDAllocator = idAllocator;
ReturnErrorOnFailure(mPairingSession.MessageDispatch().Init(transportMgr));
@@ -53,6 +55,8 @@
{
ReturnErrorCodeIf(ec == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+ Cleanup();
+
// TODO - Use PK of the root CA for the initiator to figure out the admin.
mAdminId = ec->GetSecureSession().GetAdminId();
@@ -60,15 +64,21 @@
// ReturnErrorCodeIf(mAdminId == Transport::kUndefinedAdminId, CHIP_ERROR_INVALID_ARGUMENT);
mAdminId = 0;
- mAdmins->LoadFromStorage(mAdminId);
-
Transport::AdminPairingInfo * admin = mAdmins->FindAdminWithId(mAdminId);
+
+ if (admin == nullptr)
+ {
+ ReturnErrorOnFailure(mAdmins->LoadFromStorage(mAdminId));
+ admin = mAdmins->FindAdminWithId(mAdminId);
+ }
ReturnErrorCodeIf(admin == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(admin->GetCredentials(mCredentials, mCertificates, mRootKeyId));
+ ReturnErrorOnFailure(mIDAllocator->Allocate(mSessionKeyId));
+
// Setup CASE state machine using the credentials for the current admin.
- ReturnErrorOnFailure(mPairingSession.ListenForSessionEstablishment(&mCredentials, mNextKeyId++, this));
+ ReturnErrorOnFailure(mPairingSession.ListenForSessionEstablishment(&mCredentials, mSessionKeyId, this));
// Hand over the exchange context to the CASE session.
ec->SetDelegate(&mPairingSession);
@@ -85,39 +95,39 @@
mPairingSession.OnMessageReceived(ec, packetHeader, payloadHeader, std::move(payload));
// TODO - Enable multiple concurrent CASE session establishment
- // This will prevent CASEServer to process another CASE session establishment request until the current
- // one completes (successfully or failed)
- mExchangeManager->UnregisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::CASE_SigmaR1);
}
void CASEServer::Cleanup()
{
// Let's re-register for CASE SigmaR1 message, so that the next CASE session setup request can be processed.
- mExchangeManager->RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::CASE_SigmaR1, this);
mAdminId = Transport::kUndefinedAdminId;
mCredentials.Release();
+ mCertificates.Release();
+ mPairingSession.Clear();
}
void CASEServer::OnSessionEstablishmentError(CHIP_ERROR err)
{
ChipLogProgress(Inet, "CASE Session establishment failed: %s", ErrorStr(err));
+ mIDAllocator->Free(mSessionKeyId);
Cleanup();
}
void CASEServer::OnSessionEstablished()
{
ChipLogProgress(Inet, "CASE Session established. Setting up the secure channel.");
- // TODO - enable use of secure session established via CASE
- // CHIP_ERROR err =
- // mSessionMgr->NewPairing(Optional<Transport::PeerAddress>::Value(mPairingSession.PeerConnection().GetPeerAddress()),
- // mPairingSession.PeerConnection().GetPeerNodeId(), &mPairingSession,
- // SecureSession::SessionRole::kResponder, mAdminId, nullptr);
- // if (err != CHIP_NO_ERROR)
- // {
- // ChipLogError(Inet, "Failed in setting up secure channel: err %s", ErrorStr(err));
- // OnSessionEstablishmentError(err);
- // return;
- // }
+ mSessionMgr->ExpireAllPairings(mPairingSession.PeerConnection().GetPeerNodeId(), mAdminId);
+
+ CHIP_ERROR err =
+ mSessionMgr->NewPairing(Optional<Transport::PeerAddress>::Value(mPairingSession.PeerConnection().GetPeerAddress()),
+ mPairingSession.PeerConnection().GetPeerNodeId(), &mPairingSession,
+ SecureSession::SessionRole::kResponder, mAdminId, nullptr);
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Inet, "Failed in setting up secure channel: err %s", ErrorStr(err));
+ OnSessionEstablishmentError(err);
+ return;
+ }
ChipLogProgress(Inet, "CASE secure channel is available now.");
Cleanup();
diff --git a/src/protocols/secure_channel/CASEServer.h b/src/protocols/secure_channel/CASEServer.h
index 5a976b7..54c1f5d 100644
--- a/src/protocols/secure_channel/CASEServer.h
+++ b/src/protocols/secure_channel/CASEServer.h
@@ -20,6 +20,7 @@
#include <messaging/ExchangeDelegate.h>
#include <messaging/ExchangeMgr.h>
#include <protocols/secure_channel/CASESession.h>
+#include <protocols/secure_channel/SessionIDAllocator.h>
namespace chip {
@@ -38,7 +39,8 @@
}
CHIP_ERROR ListenForSessionEstablishment(Messaging::ExchangeManager * exchangeManager, TransportMgrBase * transportMgr,
- SecureSessionMgr * sessionMgr, Transport::AdminPairingTable * admins);
+ SecureSessionMgr * sessionMgr, Transport::AdminPairingTable * admins,
+ SessionIDAllocator * idAllocator);
//////////// SessionEstablishmentDelegate Implementation ///////////////
void OnSessionEstablishmentError(CHIP_ERROR error) override;
@@ -58,7 +60,7 @@
Messaging::ExchangeManager * mExchangeManager = nullptr;
CASESession mPairingSession;
- uint16_t mNextKeyId = 0;
+ uint16_t mSessionKeyId = 0;
SecureSessionMgr * mSessionMgr = nullptr;
Transport::AdminId mAdminId = Transport::kUndefinedAdminId;
@@ -70,6 +72,8 @@
CHIP_ERROR InitCASEHandshake(Messaging::ExchangeContext * ec);
+ SessionIDAllocator * mIDAllocator = nullptr;
+
void Cleanup();
};
diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp
index 99c38f1..be3bf16 100644
--- a/src/protocols/secure_channel/CASESession.cpp
+++ b/src/protocols/secure_channel/CASESession.cpp
@@ -67,10 +67,10 @@
using HKDF_sha_crypto = HKDF_sha;
#endif
-// Wait at most 30 seconds for the response from the peer.
+// Wait at most 10 seconds for the response from the peer.
// This timeout value assumes the underlying transport is reliable.
// The session establishment fails if the response is not received within timeout window.
-static constexpr ExchangeContext::Timeout kSigma_Response_Timeout = 30000;
+static constexpr ExchangeContext::Timeout kSigma_Response_Timeout = 10000;
CASESession::CASESession()
{
@@ -969,6 +969,11 @@
if (mOpCredSet->IsTrustedRootIn(trustedRoot[i]))
{
+ if (mTrustedRootId.mId != nullptr)
+ {
+ chip::Platform::MemoryFree(const_cast<uint8_t *>(mTrustedRootId.mId));
+ mTrustedRootId.mId = nullptr;
+ }
mTrustedRootId.mId = reinterpret_cast<const uint8_t *>(chip::Platform::MemoryAlloc(kTrustedRootIdSize));
VerifyOrReturnError(mTrustedRootId.mId != nullptr, CHIP_ERROR_NO_MEMORY);
@@ -1021,30 +1026,34 @@
CHIP_ERROR CASESession::Validate_and_RetrieveResponderID(const uint8_t ** msgIterator, P256PublicKey & responderID,
const uint8_t ** responderOpCert, uint16_t & responderOpCertLen)
{
- ChipCertificateData chipCertData;
ChipCertificateData * resultCert = nullptr;
+ ChipCertificateSet certSet;
+ // Certificate set can contain up to 3 certs (NOC, ICA cert, and Root CA cert)
+ ReturnErrorOnFailure(certSet.Init(3, kMaxCHIPCertLength * 3));
+
responderOpCertLen = chip::Encoding::LittleEndian::Read16(*msgIterator);
*responderOpCert = *msgIterator;
*msgIterator += responderOpCertLen;
Encoding::LittleEndian::BufferWriter bbuf(responderID, responderID.Length());
- ReturnErrorOnFailure(DecodeChipCert(*responderOpCert, responderOpCertLen, chipCertData));
+ ReturnErrorOnFailure(
+ certSet.LoadCerts(*responderOpCert, responderOpCertLen, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
- bbuf.Put(chipCertData.mPublicKey, chipCertData.mPublicKeyLen);
+ bbuf.Put(certSet.GetCertSet()[0].mPublicKey, certSet.GetCertSet()[0].mPublicKeyLen);
VerifyOrReturnError(bbuf.Fit(), CHIP_ERROR_NO_MEMORY);
// Validate responder identity located in msg_r2_encrypted
ReturnErrorOnFailure(
mOpCredSet->FindCertSet(mTrustedRootId)
- ->LoadCert(*responderOpCert, responderOpCertLen, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
+ ->LoadCerts(*responderOpCert, responderOpCertLen, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
ReturnErrorOnFailure(SetEffectiveTime());
// Locate the subject DN and key id that will be used as input the FindValidCert() method.
{
- const ChipDN & subjectDN = chipCertData.mSubjectDN;
- const CertificateKeyId & subjectKeyId = chipCertData.mSubjectKeyId;
+ const ChipDN & subjectDN = certSet.GetCertSet()[0].mSubjectDN;
+ const CertificateKeyId & subjectKeyId = certSet.GetCertSet()[0].mSubjectKeyId;
ReturnErrorOnFailure(mOpCredSet->FindValidCert(mTrustedRootId, subjectDN, subjectKeyId, mValidContext, resultCert));
}
diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h
index 41b38e6..67e9bf7 100644
--- a/src/protocols/secure_channel/CASESession.h
+++ b/src/protocols/secure_channel/CASESession.h
@@ -188,6 +188,10 @@
return &mMessageDispatch;
}
+ /** @brief This function zeroes out and resets the memory used by the object.
+ **/
+ void Clear();
+
private:
enum SigmaErrorType : uint8_t
{
@@ -235,8 +239,6 @@
// TODO: Remove this and replace with system method to retrieve current time
CHIP_ERROR SetEffectiveTime(void);
- void Clear();
-
CHIP_ERROR ValidateReceivedMessage(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader,
const PayloadHeader & payloadHeader, System::PacketBufferHandle & msg);
diff --git a/src/protocols/secure_channel/SessionIDAllocator.cpp b/src/protocols/secure_channel/SessionIDAllocator.cpp
new file mode 100644
index 0000000..eadb736
--- /dev/null
+++ b/src/protocols/secure_channel/SessionIDAllocator.cpp
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#include <protocols/secure_channel/SessionIDAllocator.h>
+
+#include <support/CodeUtils.h>
+
+namespace chip {
+
+CHIP_ERROR SessionIDAllocator::Allocate(uint16_t & id)
+{
+ VerifyOrReturnError(mNextAvailable < kMaxSessionID, CHIP_ERROR_NO_MEMORY);
+ id = mNextAvailable;
+
+ // TODO - Update SessionID allocator to use freed session IDs
+ mNextAvailable++;
+
+ return CHIP_NO_ERROR;
+}
+
+void SessionIDAllocator::Free(uint16_t id)
+{
+ if (mNextAvailable > 0 && (mNextAvailable - 1) == id)
+ {
+ mNextAvailable--;
+ }
+}
+
+CHIP_ERROR SessionIDAllocator::Reserve(uint16_t id)
+{
+ VerifyOrReturnError(id < kMaxSessionID, CHIP_ERROR_NO_MEMORY);
+ if (id >= mNextAvailable)
+ {
+ mNextAvailable = id;
+ mNextAvailable++;
+ }
+
+ // TODO - Check if ID is already allocated in SessionIDAllocator::Reserve()
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR SessionIDAllocator::ReserveUpTo(uint16_t id)
+{
+ VerifyOrReturnError(id < kMaxSessionID, CHIP_ERROR_NO_MEMORY);
+ if (id >= mNextAvailable)
+ {
+ mNextAvailable = id;
+ mNextAvailable++;
+ }
+
+ // TODO - Update ReserveUpTo to mark all IDs in use
+ // Current SessionIDAllocator only tracks the smallest unused session ID.
+ // If/when we change it to track all in use IDs, we should also update ReserveUpTo
+ // to reserve all individual session IDs, instead of just setting the mNextAvailable.
+
+ return CHIP_NO_ERROR;
+}
+
+uint16_t SessionIDAllocator::Peek()
+{
+ return mNextAvailable;
+}
+
+} // namespace chip
diff --git a/src/protocols/secure_channel/SessionIDAllocator.h b/src/protocols/secure_channel/SessionIDAllocator.h
new file mode 100644
index 0000000..577d6d3
--- /dev/null
+++ b/src/protocols/secure_channel/SessionIDAllocator.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#pragma once
+
+#include <core/CHIPError.h>
+
+namespace chip {
+
+class SessionIDAllocator
+{
+public:
+ SessionIDAllocator() {}
+ ~SessionIDAllocator() {}
+
+ CHIP_ERROR Allocate(uint16_t & id);
+ void Free(uint16_t id);
+ CHIP_ERROR Reserve(uint16_t id);
+ CHIP_ERROR ReserveUpTo(uint16_t id);
+ uint16_t Peek();
+
+private:
+ // Session ID is a 15 bit value (16th bit indicates unicast/group key)
+ static constexpr uint16_t kMaxSessionID = (1 << 15) - 1;
+ uint16_t mNextAvailable = 0;
+};
+
+} // namespace chip
diff --git a/src/protocols/secure_channel/tests/BUILD.gn b/src/protocols/secure_channel/tests/BUILD.gn
index 6bbcfb3..a48aa9a 100644
--- a/src/protocols/secure_channel/tests/BUILD.gn
+++ b/src/protocols/secure_channel/tests/BUILD.gn
@@ -12,6 +12,7 @@
"TestCASESession.cpp",
"TestMessageCounterManager.cpp",
"TestPASESession.cpp",
+ "TestSessionIDAllocator.cpp",
"TestStatusReport.cpp",
]
diff --git a/src/protocols/secure_channel/tests/TestSessionIDAllocator.cpp b/src/protocols/secure_channel/tests/TestSessionIDAllocator.cpp
new file mode 100644
index 0000000..be12be9
--- /dev/null
+++ b/src/protocols/secure_channel/tests/TestSessionIDAllocator.cpp
@@ -0,0 +1,157 @@
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#include <protocols/secure_channel/SessionIDAllocator.h>
+#include <support/CHIPMem.h>
+#include <support/UnitTestRegistration.h>
+
+#include <nlunit-test.h>
+
+using namespace chip;
+
+void TestSessionIDAllocator_Allocate(nlTestSuite * inSuite, void * inContext)
+{
+ SessionIDAllocator allocator;
+
+ NL_TEST_ASSERT(inSuite, allocator.Peek() == 0);
+
+ uint16_t id;
+
+ for (uint16_t i = 0; i < 16; i++)
+ {
+ CHIP_ERROR err = allocator.Allocate(id);
+ NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
+ NL_TEST_ASSERT(inSuite, id == i);
+ NL_TEST_ASSERT(inSuite, allocator.Peek() == i + 1);
+ }
+}
+
+void TestSessionIDAllocator_Free(nlTestSuite * inSuite, void * inContext)
+{
+ SessionIDAllocator allocator;
+
+ NL_TEST_ASSERT(inSuite, allocator.Peek() == 0);
+
+ uint16_t id;
+
+ for (uint16_t i = 0; i < 16; i++)
+ {
+ CHIP_ERROR err = allocator.Allocate(id);
+ NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
+ NL_TEST_ASSERT(inSuite, id == i);
+ NL_TEST_ASSERT(inSuite, allocator.Peek() == i + 1);
+ }
+
+ // Free an intermediate ID
+ allocator.Free(10);
+ NL_TEST_ASSERT(inSuite, allocator.Peek() == 16);
+
+ // Free the last allocated ID
+ allocator.Free(15);
+ NL_TEST_ASSERT(inSuite, allocator.Peek() == 15);
+
+ // Free some random unallocated ID
+ allocator.Free(100);
+ NL_TEST_ASSERT(inSuite, allocator.Peek() == 15);
+}
+
+void TestSessionIDAllocator_Reserve(nlTestSuite * inSuite, void * inContext)
+{
+ SessionIDAllocator allocator;
+
+ uint16_t id;
+
+ for (uint16_t i = 0; i < 16; i++)
+ {
+ CHIP_ERROR err = allocator.Allocate(id);
+ NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
+ NL_TEST_ASSERT(inSuite, id == i);
+ NL_TEST_ASSERT(inSuite, allocator.Peek() == i + 1);
+ }
+
+ allocator.Reserve(100);
+ NL_TEST_ASSERT(inSuite, allocator.Peek() == 101);
+}
+
+void TestSessionIDAllocator_ReserveUpTo(nlTestSuite * inSuite, void * inContext)
+{
+ SessionIDAllocator allocator;
+
+ allocator.ReserveUpTo(100);
+ NL_TEST_ASSERT(inSuite, allocator.Peek() == 101);
+}
+
+// Test Suite
+
+/**
+ * Test Suite that lists all the test functions.
+ */
+// clang-format off
+static const nlTest sTests[] =
+{
+ NL_TEST_DEF("SessionIDAllocator_Allocate", TestSessionIDAllocator_Allocate),
+ NL_TEST_DEF("SessionIDAllocator_Free", TestSessionIDAllocator_Free),
+ NL_TEST_DEF("SessionIDAllocator_Reserve", TestSessionIDAllocator_Reserve),
+ NL_TEST_DEF("SessionIDAllocator_ReserveUpTo", TestSessionIDAllocator_ReserveUpTo),
+
+ NL_TEST_SENTINEL()
+};
+// clang-format on
+
+/**
+ * Set up the test suite.
+ */
+static int TestSetup(void * inContext)
+{
+ CHIP_ERROR error = chip::Platform::MemoryInit();
+ if (error != CHIP_NO_ERROR)
+ return FAILURE;
+ return SUCCESS;
+}
+
+/**
+ * Tear down the test suite.
+ */
+static int TestTeardown(void * inContext)
+{
+ chip::Platform::MemoryShutdown();
+ return SUCCESS;
+}
+
+// clang-format off
+static nlTestSuite sSuite =
+{
+ "Test-CHIP-SessionIDAllocator",
+ &sTests[0],
+ TestSetup,
+ TestTeardown,
+};
+// clang-format on
+
+/**
+ * Main
+ */
+int TestSessionIDAllocator()
+{
+ // Run test suit against one context
+ nlTestRunner(&sSuite, nullptr);
+
+ return (nlTestRunnerStats(&sSuite));
+}
+
+CHIP_REGISTER_TEST_SUITE(TestSessionIDAllocator)
diff --git a/src/transport/AdminPairingTable.cpp b/src/transport/AdminPairingTable.cpp
index 3ff3946..ceb554f 100644
--- a/src/transport/AdminPairingTable.cpp
+++ b/src/transport/AdminPairingTable.cpp
@@ -213,7 +213,7 @@
return CHIP_NO_ERROR;
}
- VerifyOrReturnError(cert.size() <= kMaxChipCertSize, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(cert.size() <= kMaxCHIPCertLength, CHIP_ERROR_INVALID_ARGUMENT);
if (mRootCertLen != 0 && mRootCertAllocatedLen < cert.size())
{
ReleaseRootCert();
@@ -252,7 +252,7 @@
}
// There could be two certs in the set -> ICA and NOC
- VerifyOrReturnError(cert.size() <= kMaxChipCertSize * 2, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(cert.size() <= kMaxCHIPCertLength * 2, CHIP_ERROR_INVALID_ARGUMENT);
if (mOpCertLen != 0 && mOpCertAllocatedLen < cert.size())
{
ReleaseOperationalCert();
@@ -275,7 +275,7 @@
CertificateKeyId & rootKeyId)
{
constexpr uint8_t kMaxNumCertsInOpCreds = 3;
- ReturnErrorOnFailure(certificates.Init(kMaxNumCertsInOpCreds, kMaxChipCertSize * kMaxNumCertsInOpCreds));
+ ReturnErrorOnFailure(certificates.Init(kMaxNumCertsInOpCreds, kMaxCHIPCertLength * kMaxNumCertsInOpCreds));
ReturnErrorOnFailure(
certificates.LoadCert(mRootCert, mRootCertLen,
diff --git a/src/transport/AdminPairingTable.h b/src/transport/AdminPairingTable.h
index b25079d..1b2126c 100644
--- a/src/transport/AdminPairingTable.h
+++ b/src/transport/AdminPairingTable.h
@@ -43,8 +43,6 @@
constexpr char kAdminTableKeyPrefix[] = "CHIPAdmin";
constexpr char kAdminTableCountKey[] = "CHIPAdminNextId";
-constexpr uint16_t kMaxChipCertSize = 600;
-
struct AccessControlList
{
uint32_t placeholder;
@@ -193,9 +191,9 @@
uint16_t mOpCertLen; /* This field is serialized in LittleEndian byte order */
Crypto::P256SerializedKeypair mOperationalKey;
- uint8_t mRootCert[kMaxChipCertSize];
+ uint8_t mRootCert[Credentials::kMaxCHIPCertLength];
// The operationa credentials set can have up to two certs -> ICAC and NOC
- uint8_t mOperationalCert[kMaxChipCertSize * 2];
+ uint8_t mOperationalCert[Credentials::kMaxCHIPCertLength * 2];
char mFabricLabel[kFabricLabelMaxLengthInBytes + 1] = { '\0' };
};
};
diff --git a/src/transport/SecureSessionMgr.cpp b/src/transport/SecureSessionMgr.cpp
index 162efd3..b13e236 100644
--- a/src/transport/SecureSessionMgr.cpp
+++ b/src/transport/SecureSessionMgr.cpp
@@ -214,6 +214,34 @@
return err;
}
+void SecureSessionMgr::ExpirePairing(SecureSessionHandle session)
+{
+ PeerConnectionState * state = GetPeerConnectionState(session);
+ if (state != nullptr)
+ {
+ mPeerConnections.MarkConnectionExpired(
+ state, [this](const Transport::PeerConnectionState & state1) { HandleConnectionExpired(state1); });
+ }
+}
+
+void SecureSessionMgr::ExpireAllPairings(NodeId peerNodeId, Transport::AdminId admin)
+{
+ PeerConnectionState * state = mPeerConnections.FindPeerConnectionState(peerNodeId, nullptr);
+ while (state != nullptr)
+ {
+ if (admin == state->GetAdminId())
+ {
+ mPeerConnections.MarkConnectionExpired(
+ state, [this](const Transport::PeerConnectionState & state1) { HandleConnectionExpired(state1); });
+ state = mPeerConnections.FindPeerConnectionState(peerNodeId, nullptr);
+ }
+ else
+ {
+ state = mPeerConnections.FindPeerConnectionState(peerNodeId, state);
+ }
+ }
+}
+
CHIP_ERROR SecureSessionMgr::NewPairing(const Optional<Transport::PeerAddress> & peerAddr, NodeId peerNodeId,
PairingSession * pairing, SecureSession::SessionRole direction, Transport::AdminId admin,
Transport::Base * transport)
diff --git a/src/transport/SecureSessionMgr.h b/src/transport/SecureSessionMgr.h
index acdb01c..ac29c1b 100644
--- a/src/transport/SecureSessionMgr.h
+++ b/src/transport/SecureSessionMgr.h
@@ -220,6 +220,9 @@
CHIP_ERROR NewPairing(const Optional<Transport::PeerAddress> & peerAddr, NodeId peerNodeId, PairingSession * pairing,
SecureSession::SessionRole direction, Transport::AdminId admin, Transport::Base * transport = nullptr);
+ void ExpirePairing(SecureSessionHandle session);
+ void ExpireAllPairings(NodeId peerNodeId, Transport::AdminId admin);
+
/**
* @brief
* Return the System Layer pointer used by current SecureSessionMgr.