Skip to content

Commit

Permalink
Allow for split commissioner / admin topology
Browse files Browse the repository at this point in the history
It is possible to implement Matter commissioning such that a commissioner
node is not on any fabric, and delegates operational communication and
invocation of the CommissioningComplete command to a different
administrator node.

In this configuration, the commissioner only communicates by PASE and may
not ever have received operational credentials.

To support such a configuration, this commit amends the commissioner
to accept initialization to the kUndefinedFabricIndex.  In this
configuration, the commissioner does not bootstrap itself with any
credentials, and is capable of conducting the commissioning procedure
until these are needed.  When it is found that credentials aren't
available, the commissioner gracefully discontinues commissioning.

Because CASESessionManager is using fabric association for operational
discovery, discontinuation of the commissioning sequence occurs at
attempted operational discovery.

Fixes #13501
  • Loading branch information
msandstedt committed Jan 25, 2022
1 parent 5c6492a commit c34cf0b
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 45 deletions.
71 changes: 44 additions & 27 deletions examples/chip-tool/commands/common/CHIPCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@

using DeviceControllerFactory = chip::Controller::DeviceControllerFactory;

constexpr chip::FabricId kIdentityAlphaFabricId = 1;
constexpr chip::FabricId kIdentityBetaFabricId = 2;
constexpr chip::FabricId kIdentityGammaFabricId = 3;
constexpr chip::FabricIndex kIdentityNullFabricId = chip::kUndefinedFabricIndex;
constexpr chip::FabricIndex kIdentityAlphaFabricId = 1;
constexpr chip::FabricIndex kIdentityBetaFabricId = 2;
constexpr chip::FabricIndex kIdentityGammaFabricId = 3;

CHIP_ERROR CHIPCommand::Run()
{
Expand All @@ -51,6 +52,7 @@ CHIP_ERROR CHIPCommand::Run()
factoryInitParams.listenPort = static_cast<uint16_t>(mDefaultStorage.GetListenPort() + CurrentCommissionerIndex());
ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().Init(factoryInitParams));

ReturnLogErrorOnFailure(InitializeCommissioner(kIdentityNull, kIdentityNullFabricId));
ReturnLogErrorOnFailure(InitializeCommissioner(kIdentityAlpha, kIdentityAlphaFabricId));
ReturnLogErrorOnFailure(InitializeCommissioner(kIdentityBeta, kIdentityBetaFabricId));
ReturnLogErrorOnFailure(InitializeCommissioner(kIdentityGamma, kIdentityGammaFabricId));
Expand All @@ -65,6 +67,7 @@ CHIP_ERROR CHIPCommand::Run()
// since the CHIP thread and event queue have been stopped, preventing any thread
// races.
//
ReturnLogErrorOnFailure(ShutdownCommissioner(kIdentityNull));
ReturnLogErrorOnFailure(ShutdownCommissioner(kIdentityAlpha));
ReturnLogErrorOnFailure(ShutdownCommissioner(kIdentityBeta));
ReturnLogErrorOnFailure(ShutdownCommissioner(kIdentityGamma));
Expand Down Expand Up @@ -99,7 +102,8 @@ void CHIPCommand::StopTracing()
void CHIPCommand::SetIdentity(const char * identity)
{
std::string name = std::string(identity);
if (name.compare(kIdentityAlpha) != 0 && name.compare(kIdentityBeta) != 0 && name.compare(kIdentityGamma) != 0)
if (name.compare(kIdentityAlpha) != 0 && name.compare(kIdentityBeta) != 0 && name.compare(kIdentityGamma) != 0 &&
name.compare(kIdentityNull))
{
ChipLogError(chipTool, "Unknown commissioner name: %s. Supported names are [%s, %s, %s]", name.c_str(), kIdentityAlpha,
kIdentityBeta, kIdentityGamma);
Expand All @@ -112,7 +116,8 @@ void CHIPCommand::SetIdentity(const char * identity)
std::string CHIPCommand::GetIdentity()
{
std::string name = mCommissionerName.HasValue() ? mCommissionerName.Value() : kIdentityAlpha;
if (name.compare(kIdentityAlpha) != 0 && name.compare(kIdentityBeta) != 0 && name.compare(kIdentityGamma) != 0)
if (name.compare(kIdentityAlpha) != 0 && name.compare(kIdentityBeta) != 0 && name.compare(kIdentityGamma) != 0 &&
name.compare(kIdentityNull))
{
ChipLogError(chipTool, "Unknown commissioner name: %s. Supported names are [%s, %s, %s]", name.c_str(), kIdentityAlpha,
kIdentityBeta, kIdentityGamma);
Expand All @@ -122,9 +127,9 @@ std::string CHIPCommand::GetIdentity()
return name;
}

uint16_t CHIPCommand::CurrentCommissionerIndex()
chip::FabricIndex CHIPCommand::CurrentCommissionerIndex()
{
uint16_t index = 0;
chip::FabricIndex index;

std::string name = GetIdentity();
if (name.compare(kIdentityAlpha) == 0)
Expand All @@ -139,9 +144,16 @@ uint16_t CHIPCommand::CurrentCommissionerIndex()
{
index = kIdentityGammaFabricId;
}
else if (name.compare(kIdentityNull) == 0)
{
index = kIdentityNullFabricId;
}
else
{
VerifyOrDieWithMsg(false, chipTool, "Unknown commissioner name: %s. Supported names are [%s, %s, %s]", name.c_str(),
kIdentityAlpha, kIdentityBeta, kIdentityGamma);
}

VerifyOrDieWithMsg(index != 0, chipTool, "Unknown commissioner name: %s. Supported names are [%s, %s, %s]", name.c_str(),
kIdentityAlpha, kIdentityBeta, kIdentityGamma);
return index;
}

Expand All @@ -156,7 +168,7 @@ CHIP_ERROR CHIPCommand::ShutdownCommissioner(std::string key)
return mCommissioners[key].get()->Shutdown();
}

CHIP_ERROR CHIPCommand::InitializeCommissioner(std::string key, chip::FabricId fabricId)
CHIP_ERROR CHIPCommand::InitializeCommissioner(std::string key, chip::FabricIndex fabricIndex)
{
chip::Platform::ScopedMemoryBuffer<uint8_t> noc;
chip::Platform::ScopedMemoryBuffer<uint8_t> icac;
Expand All @@ -172,28 +184,33 @@ CHIP_ERROR CHIPCommand::InitializeCommissioner(std::string key, chip::FabricId f
VerifyOrReturnError(icac.Alloc(chip::Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
VerifyOrReturnError(rcac.Alloc(chip::Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);

chip::MutableByteSpan nocSpan(noc.Get(), chip::Controller::kMaxCHIPDERCertLength);
chip::MutableByteSpan icacSpan(icac.Get(), chip::Controller::kMaxCHIPDERCertLength);
chip::MutableByteSpan rcacSpan(rcac.Get(), chip::Controller::kMaxCHIPDERCertLength);

chip::Crypto::P256Keypair ephemeralKey;
ReturnLogErrorOnFailure(ephemeralKey.Initialize());

// TODO - OpCreds should only be generated for pairing command
// store the credentials in persistent storage, and
// generate when not available in the storage.
ReturnLogErrorOnFailure(mCommissionerStorage.Init(key.c_str()));
ReturnLogErrorOnFailure(mCredIssuerCmds->InitializeCredentialsIssuer(mCommissionerStorage));
ReturnLogErrorOnFailure(mCredIssuerCmds->GenerateControllerNOCChain(mCommissionerStorage.GetLocalNodeId(), fabricId,
ephemeralKey, rcacSpan, icacSpan, nocSpan));
if (fabricIndex != chip::kUndefinedFabricIndex)
{

// TODO - OpCreds should only be generated for pairing command
// store the credentials in persistent storage, and
// generate when not available in the storage.
ReturnLogErrorOnFailure(mCommissionerStorage.Init(key.c_str()));
ReturnLogErrorOnFailure(mCredIssuerCmds->InitializeCredentialsIssuer(mCommissionerStorage));

chip::MutableByteSpan nocSpan(noc.Get(), chip::Controller::kMaxCHIPDERCertLength);
chip::MutableByteSpan icacSpan(icac.Get(), chip::Controller::kMaxCHIPDERCertLength);
chip::MutableByteSpan rcacSpan(rcac.Get(), chip::Controller::kMaxCHIPDERCertLength);

ReturnLogErrorOnFailure(ephemeralKey.Initialize());
ReturnLogErrorOnFailure(mCredIssuerCmds->GenerateControllerNOCChain(mCommissionerStorage.GetLocalNodeId(), fabricIndex,
ephemeralKey, rcacSpan, icacSpan, nocSpan));
commissionerParams.ephemeralKeypair = &ephemeralKey;
commissionerParams.controllerRCAC = rcacSpan;
commissionerParams.controllerICAC = icacSpan;
commissionerParams.controllerNOC = nocSpan;
}

commissionerParams.storageDelegate = &mCommissionerStorage;
commissionerParams.fabricIndex = static_cast<chip::FabricIndex>(fabricId);
commissionerParams.fabricIndex = fabricIndex;
commissionerParams.operationalCredentialsDelegate = mCredIssuerCmds->GetCredentialIssuer();
commissionerParams.ephemeralKeypair = &ephemeralKey;
commissionerParams.controllerRCAC = rcacSpan;
commissionerParams.controllerICAC = icacSpan;
commissionerParams.controllerNOC = nocSpan;
commissionerParams.controllerVendorId = chip::VendorId::TestVendor1;

ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().SetupCommissioner(commissionerParams, *(commissioner.get())));
Expand Down
14 changes: 12 additions & 2 deletions examples/chip-tool/commands/common/CHIPCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ class PersistentStorage;
constexpr const char kIdentityAlpha[] = "alpha";
constexpr const char kIdentityBeta[] = "beta";
constexpr const char kIdentityGamma[] = "gamma";
// The null fabric commissioner is a commissioner that isn't on a fabric.
// This is a legal configuration in which the commissioner delegates
// operational communication and invocation of the commssioning complete
// command to a separate on-fabric administrator node.
//
// The null-fabric-commissioner identity is provided here to demonstrate the
// commissioner portion of such an architecture. The null-fabric-commissioner
// can carry a commissioning flow up until the point of operational channel
// (CASE) communcation.
constexpr const char kIdentityNull[] = "null-fabric-commissioner";

class CHIPCommand : public Command
{
Expand Down Expand Up @@ -96,9 +106,9 @@ class CHIPCommand : public Command
ChipDeviceCommissioner & CurrentCommissioner();

private:
CHIP_ERROR InitializeCommissioner(std::string key, chip::FabricId fabricId);
CHIP_ERROR InitializeCommissioner(std::string key, chip::FabricIndex fabricIndex);
CHIP_ERROR ShutdownCommissioner(std::string key);
uint16_t CurrentCommissionerIndex();
chip::FabricIndex CurrentCommissionerIndex();
std::map<std::string, std::unique_ptr<ChipDeviceCommissioner>> mCommissioners;
chip::Optional<char *> mCommissionerName;

Expand Down
1 change: 1 addition & 0 deletions src/app/CASESessionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ void CASESessionManager::ReleaseSession(PeerId peerId)

CHIP_ERROR CASESessionManager::ResolveDeviceAddress(FabricInfo * fabric, NodeId nodeId)
{
VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_INCORRECT_STATE);
return mConfig.dnsResolver->ResolveNodeId(fabric->GetPeerIdForNode(nodeId), Inet::IPAddressType::kAny,
Dnssd::Resolver::CacheBypass::On);
}
Expand Down
39 changes: 25 additions & 14 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,13 @@ CHIP_ERROR DeviceController::Init(ControllerInitParams params)
mOperationalCredentialsDelegate = params.operationalCredentialsDelegate;

mFabricIndex = params.fabricIndex;
ReturnErrorOnFailure(ProcessControllerNOCChain(params));

mFabricInfo = params.systemState->Fabrics()->FindFabricWithIndex(mFabricIndex);
VerifyOrReturnError(mFabricInfo != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
mVendorId = params.controllerVendorId;
if (mFabricIndex != kUndefinedFabricIndex)
{
ReturnErrorOnFailure(ProcessControllerNOCChain(params));
mFabricInfo = params.systemState->Fabrics()->FindFabricWithIndex(mFabricIndex);
VerifyOrReturnError(mFabricInfo != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
}

DeviceProxyInitParams deviceInitParams = {
.sessionManager = params.systemState->SessionMgr(),
Expand Down Expand Up @@ -215,8 +218,6 @@ CHIP_ERROR DeviceController::ProcessControllerNOCChain(const ControllerInitParam

ReturnErrorOnFailure(fabric->SetFabricInfo(newFabric));
mLocalId = fabric->GetPeerId();
mVendorId = fabric->GetVendorId();

mFabricId = fabric->GetFabricId();

ChipLogProgress(Controller, "Joined the fabric at index %d. Compressed fabric ID is: 0x" ChipLogFormatX64, mFabricIndex,
Expand Down Expand Up @@ -273,7 +274,11 @@ void DeviceController::OnFirstMessageDeliveryFailed(const SessionHandle & sessio
VerifyOrReturn(mState == State::Initialized,
ChipLogError(Controller, "OnFirstMessageDeliveryFailed was called in incorrect state"));
VerifyOrReturn(session->GetSessionType() == Transport::Session::SessionType::kSecure);
UpdateDevice(session->AsSecureSession()->GetPeerNodeId());
CHIP_ERROR err = UpdateDevice(session->AsSecureSession()->GetPeerNodeId());
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "OnFirstMessageDeliveryFailed was called, but UpdateDevice did not succeed (%s)", ErrorStr(err));
}
}

CHIP_ERROR DeviceController::InitializePairedDeviceList()
Expand Down Expand Up @@ -773,11 +778,8 @@ CHIP_ERROR DeviceCommissioner::EstablishPASEConnection(NodeId remoteDeviceId, Re

uint16_t keyID = 0;

FabricInfo * fabric = mSystemState->Fabrics()->FindFabricWithIndex(mFabricIndex);

VerifyOrExit(mState == State::Initialized, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(mDeviceBeingCommissioned == nullptr, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(fabric != nullptr, err = CHIP_ERROR_INCORRECT_STATE);

err = InitializePairedDeviceList();
SuccessOrExit(err);
Expand Down Expand Up @@ -808,7 +810,7 @@ CHIP_ERROR DeviceCommissioner::EstablishPASEConnection(NodeId remoteDeviceId, Re

mIsIPRendezvous = (params.GetPeerAddress().GetTransportType() != Transport::Type::kBle);

device->Init(GetControllerDeviceInitParams(), remoteDeviceId, peerAddress, fabric->GetFabricIndex());
device->Init(GetControllerDeviceInitParams(), remoteDeviceId, peerAddress, mFabricIndex);

if (params.GetPeerAddress().GetTransportType() != Transport::Type::kBle)
{
Expand Down Expand Up @@ -1214,8 +1216,11 @@ CHIP_ERROR DeviceCommissioner::ProcessOpCSR(DeviceProxy * proxy, const ByteSpan

mOperationalCredentialsDelegate->SetNodeIdForNextNOCRequest(proxy->GetDeviceId());

FabricInfo * fabric = mSystemState->Fabrics()->FindFabricWithIndex(mFabricIndex);
mOperationalCredentialsDelegate->SetFabricIdForNextNOCRequest(fabric->GetFabricId());
if (mFabricIndex != kUndefinedFabricIndex)
{
FabricInfo * fabric = mSystemState->Fabrics()->FindFabricWithIndex(mFabricIndex);
mOperationalCredentialsDelegate->SetFabricIdForNextNOCRequest(fabric->GetFabricId());
}

return mOperationalCredentialsDelegate->GenerateNOCChain(NOCSRElements, AttestationSignature, dac, ByteSpan(), ByteSpan(),
&mDeviceNOCChainCallback);
Expand Down Expand Up @@ -1797,7 +1802,13 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
}
break;
case CommissioningStage::kFindOperational: {
UpdateDevice(proxy->GetDeviceId());
CHIP_ERROR err = UpdateDevice(proxy->GetDeviceId());
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Unable to proceed to operational discovery\n");
CommissioningStageComplete(err);
return;
}
}
break;
case CommissioningStage::kSendComplete: {
Expand Down
3 changes: 2 additions & 1 deletion src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ struct ControllerInitParams

uint16_t controllerVendorId;

/* Set fabricIndex to kUndefinedFabricIndex to defeat self commissioning. */
FabricIndex fabricIndex = kMinValidFabricIndex;
FabricId fabricId = kUndefinedFabricId;
};
Expand Down Expand Up @@ -367,7 +368,7 @@ class DLL_EXPORT DeviceController : public SessionRecoveryDelegate,

void PersistNextKeyId();

FabricIndex mFabricIndex = kMinValidFabricIndex;
FabricIndex mFabricIndex = kUndefinedFabricIndex;

OperationalCredentialsDelegate * mOperationalCredentialsDelegate;

Expand Down
2 changes: 1 addition & 1 deletion src/controller/CHIPDeviceControllerFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ struct SetupParams
ByteSpan controllerICAC;
ByteSpan controllerRCAC;

FabricIndex fabricIndex = kMinValidFabricIndex;
FabricIndex fabricIndex = kUndefinedFabricIndex;
FabricId fabricId = kUndefinedFabricId;

uint16_t controllerVendorId;
Expand Down

0 comments on commit c34cf0b

Please sign in to comment.