diff --git a/examples/chip-tool/BUILD.gn b/examples/chip-tool/BUILD.gn index 990b3947c7fa9f..a255731bd8c1b3 100644 --- a/examples/chip-tool/BUILD.gn +++ b/examples/chip-tool/BUILD.gn @@ -29,7 +29,19 @@ declare_args() { config_pair_with_random_id = true } -executable("chip-tool") { +config("config") { + include_dirs = [ + ".", + "${chip_root}/zzz_generated/chip-tool", + ] + + defines = [ + "CONFIG_USE_SEPARATE_EVENTLOOP=${config_use_separate_eventloop}", + "CONFIG_PAIR_WITH_RANDOM_ID=${config_pair_with_random_id}", + ] +} + +static_library("chip-tool-utils") { sources = [ "commands/clusters/ModelCommand.cpp", "commands/common/CHIPCommand.cpp", @@ -45,15 +57,29 @@ executable("chip-tool") { "commands/reporting/ReportingCommand.cpp", "commands/tests/TestCommand.cpp", "config/PersistentStorage.cpp", - "main.cpp", ] - defines = [ - "CONFIG_USE_SEPARATE_EVENTLOOP=${config_use_separate_eventloop}", - "CONFIG_PAIR_WITH_RANDOM_ID=${config_pair_with_random_id}", + deps = [ + "${chip_root}/src/controller/data_model", + "${chip_root}/src/lib", + "${chip_root}/src/platform", + "${chip_root}/third_party/inipp", + ] + + cflags = [ "-Wconversion" ] + + public_configs = [ ":config" ] + + output_dir = root_out_dir +} + +executable("chip-tool") { + sources = [ + "main.cpp", ] deps = [ + ":chip-tool-utils", "${chip_root}/src/controller/data_model", "${chip_root}/src/lib", "${chip_root}/src/platform", @@ -62,10 +88,7 @@ executable("chip-tool") { cflags = [ "-Wconversion" ] - include_dirs = [ - ".", - "${chip_root}/zzz_generated/chip-tool", - ] + public_configs = [ ":config" ] output_dir = root_out_dir } diff --git a/examples/chip-tool/commands/common/Command.h b/examples/chip-tool/commands/common/Command.h index aa8bd0a656f6a0..d43da608accc3f 100644 --- a/examples/chip-tool/commands/common/Command.h +++ b/examples/chip-tool/commands/common/Command.h @@ -83,6 +83,20 @@ class Command ::chip::Inet::InterfaceId interfaceId; }; + /** + * @brief + * Encapsulates key objects in the CHIP stack that need continued + * access, so wrapping it in here makes it nice and compactly encapsulated. + */ + struct ExecutionContext + { + ChipDeviceCommissioner * commissioner; + chip::Controller::OperationalCredentialsDelegate * opCredsIssuer; + PersistentStorage * storage; + chip::NodeId localId; + chip::NodeId remoteId; + }; + Command(const char * commandName) : mName(commandName) {} virtual ~Command() {} diff --git a/examples/chip-tool/commands/common/Commands.cpp b/examples/chip-tool/commands/common/Commands.cpp index 18550d724853e7..6d0ba7e265c1dc 100644 --- a/examples/chip-tool/commands/common/Commands.cpp +++ b/examples/chip-tool/commands/common/Commands.cpp @@ -23,6 +23,11 @@ #include #include +#if CONFIG_DEVICE_LAYER +#include +#endif + +#include #include #include @@ -45,9 +50,68 @@ int Commands::Run(int argc, char ** argv) VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init Storage failure: %s", chip::ErrorStr(err))); chip::Logging::SetLogFilter(mStorage.GetLoggingLevel()); + localId = mStorage.GetLocalNodeId(); + remoteId = mStorage.GetRemoteNodeId(); + + ChipLogProgress(Controller, "Read local id 0x" ChipLogFormatX64 ", remote id 0x" ChipLogFormatX64, ChipLogValueX64(localId), + ChipLogValueX64(remoteId)); + + factoryInitParams.storageDelegate = &mStorage; + factoryInitParams.listenPort = mStorage.GetListenPort(); + + err = InitializeCredentialsIssuer(mStorage); + VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Operational Cred Issuer: %s", chip::ErrorStr(err))); + + commissionerParams.operationalCredentialsDelegate = GetCredentialIssuer(); + + VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Commissioner: %s", chip::ErrorStr(err))); + + err = SetupDeviceAttestation(); + VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Device Attestation Setup: %s", chip::ErrorStr(err))); + + VerifyOrExit(rcac.Alloc(chip::Controller::kMaxCHIPDERCertLength), err = CHIP_ERROR_NO_MEMORY); + VerifyOrExit(noc.Alloc(chip::Controller::kMaxCHIPDERCertLength), err = CHIP_ERROR_NO_MEMORY); + VerifyOrExit(icac.Alloc(chip::Controller::kMaxCHIPDERCertLength), err = 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; + SuccessOrExit(err = 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. + err = GenerateControllerNOCChain(localId, 0, ephemeralKey, rcacSpan, icacSpan, nocSpan); + SuccessOrExit(err); + + commissionerParams.ephemeralKeypair = &ephemeralKey; + commissionerParams.controllerRCAC = rcacSpan; + commissionerParams.controllerICAC = icacSpan; + commissionerParams.controllerNOC = nocSpan; + + // init the factory, then setup the Controller + err = DeviceControllerFactory::GetInstance().Init(factoryInitParams); + VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Controller Factory failed to initialize")); + err = DeviceControllerFactory::GetInstance().SetupCommissioner(commissionerParams, mController); + VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Commissioner: %s", chip::ErrorStr(err))); + } - err = RunCommand(argc, argv); - VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(chipTool, "Run command failure: %s", chip::ErrorStr(err))); +#if CONFIG_USE_SEPARATE_EVENTLOOP + // ServiceEvents() calls StartEventLoopTask(), which is paired with the + // StopEventLoopTask() below. + err = DeviceControllerFactory::GetInstance().ServiceEvents(); + VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Run Loop: %s", chip::ErrorStr(err))); +#endif // CONFIG_USE_SEPARATE_EVENTLOOP + + err = RunCommand(localId, remoteId, argc, argv, &command); + SuccessOrExit(err); + +#if !CONFIG_USE_SEPARATE_EVENTLOOP + chip::DeviceLayer::PlatformMgr().RunEventLoop(); +#endif // !CONFIG_USE_SEPARATE_EVENTLOOP exit: return (err == CHIP_NO_ERROR) ? EXIT_SUCCESS : EXIT_FAILURE; @@ -114,7 +178,46 @@ CHIP_ERROR Commands::RunCommand(int argc, char ** argv) return CHIP_ERROR_INVALID_ARGUMENT; } - return command->Run(); + { + Command::ExecutionContext execContext; + + execContext.commissioner = &mController; + execContext.opCredsIssuer = GetCredentialIssuer(); + execContext.storage = &mStorage; + execContext.localId = localId; + execContext.remoteId = remoteId; + + command->SetExecutionContext(execContext); + *ranCommand = command; + + // + // Set this to true first BEFORE we send commands to ensure we don't end + // up in a situation where the response comes back faster than we can + // set the variable to true, which will cause it to block indefinitely. + // + command->UpdateWaitForResponse(true); +#if CONFIG_USE_SEPARATE_EVENTLOOP + chip::DeviceLayer::PlatformMgr().ScheduleWork(RunQueuedCommand, reinterpret_cast(command)); + command->WaitForResponse(command->GetWaitDurationInSeconds()); +#else // CONFIG_USE_SEPARATE_EVENTLOOP + err = command->Run(); + SuccessOrExit(err); + command->ScheduleWaitForResponse(command->GetWaitDurationInSeconds()); +#endif // CONFIG_USE_SEPARATE_EVENTLOOP + } + +exit: + return err; +} + +void Commands::RunQueuedCommand(intptr_t commandArg) +{ + auto * command = reinterpret_cast(commandArg); + CHIP_ERROR err = command->Run(); + if (err != CHIP_NO_ERROR) + { + command->SetCommandExitStatus(err); + } } std::map::iterator Commands::GetCluster(std::string clusterName) diff --git a/examples/chip-tool/commands/common/Commands.h b/examples/chip-tool/commands/common/Commands.h index cc1d027ba987b7..f9f8095b014d47 100644 --- a/examples/chip-tool/commands/common/Commands.h +++ b/examples/chip-tool/commands/common/Commands.h @@ -26,11 +26,15 @@ class Commands { public: + using NodeId = ::chip::NodeId; + using FabricId = ::chip::FabricId; using CommandsVector = ::std::vector>; void Register(const char * clusterName, commands_list commandsList); int Run(int argc, char ** argv); + virtual ~Commands() {} + private: CHIP_ERROR RunCommand(int argc, char ** argv); std::map::iterator GetCluster(std::string clusterName); @@ -43,6 +47,14 @@ class Commands void ShowClusterAttributes(std::string executable, std::string clusterName, std::string commandName, CommandsVector & commands); void ShowCommand(std::string executable, std::string clusterName, Command * command); + virtual CHIP_ERROR InitializeCredentialsIssuer(chip::PersistentStorageDelegate & storage) = 0; + virtual CHIP_ERROR SetupDeviceAttestation() = 0; + virtual chip::Controller::OperationalCredentialsDelegate * GetCredentialIssuer() = 0; + virtual CHIP_ERROR GenerateControllerNOCChain(NodeId nodeId, FabricId fabricId, chip::Crypto::P256Keypair & keypair, + chip::MutableByteSpan & rcac, chip::MutableByteSpan & icac, + chip::MutableByteSpan & noc) = 0; + std::map mClusters; + chip::Controller::DeviceCommissioner mController; PersistentStorage mStorage; }; diff --git a/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h b/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h new file mode 100644 index 00000000000000..7e80264ed0d86f --- /dev/null +++ b/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h @@ -0,0 +1,50 @@ +/* + * 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. + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +class ExampleCredentialIssuerCommands : public Commands +{ +private: + CHIP_ERROR InitializeCredentialsIssuer(chip::PersistentStorageDelegate & storage) override + { + return mOpCredsIssuer.Initialize(storage); + } + CHIP_ERROR SetupDeviceAttestation() override + { + chip::Credentials::SetDeviceAttestationCredentialsProvider(chip::Credentials::Examples::GetExampleDACProvider()); + chip::Credentials::SetDeviceAttestationVerifier(chip::Credentials::Examples::GetExampleDACVerifier()); + return CHIP_NO_ERROR; + } + chip::Controller::OperationalCredentialsDelegate * GetCredentialIssuer() override { return &mOpCredsIssuer; } + CHIP_ERROR GenerateControllerNOCChain(NodeId nodeId, FabricId fabricId, chip::Crypto::P256Keypair & keypair, + chip::MutableByteSpan & rcac, chip::MutableByteSpan & icac, + chip::MutableByteSpan & noc) override + { + return mOpCredsIssuer.GenerateNOCChainAfterValidation(nodeId, fabricId, keypair.Pubkey(), rcac, icac, noc); + } + + chip::Controller::ExampleOperationalCredentialsIssuer mOpCredsIssuer; +}; diff --git a/examples/chip-tool/main.cpp b/examples/chip-tool/main.cpp index c767020c72d160..19e068b6239d24 100644 --- a/examples/chip-tool/main.cpp +++ b/examples/chip-tool/main.cpp @@ -16,7 +16,7 @@ * */ -#include "commands/common/Commands.h" +#include "commands/example/ExampleCredentialIssuerCommands.h" #include "commands/discover/Commands.h" #include "commands/pairing/Commands.h" @@ -31,7 +31,7 @@ // ================================================================================ int main(int argc, char * argv[]) { - Commands commands; + ExampleCredentialIssuerCommands commands; registerCommandsDiscover(commands); registerCommandsPayload(commands); registerCommandsPairing(commands); diff --git a/src/controller/CHIPDevice.h b/src/controller/CHIPDevice.h index 91a20aa27e339d..6696f96267c41a 100644 --- a/src/controller/CHIPDevice.h +++ b/src/controller/CHIPDevice.h @@ -398,6 +398,7 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta } ByteSpan GetCSRNonce() const { return ByteSpan(mCSRNonce, sizeof(mCSRNonce)); } + MutableByteSpan GetCSRNonce() { return MutableByteSpan(mCSRNonce, sizeof(mCSRNonce)); } CHIP_ERROR SetAttestationNonce(ByteSpan attestationNonce) { diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 14492862e051ff..55e12c403ba380 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -1274,6 +1274,8 @@ CHIP_ERROR DeviceCommissioner::SendOperationalCertificateSigningRequestCommand(D Callback::Cancelable * successCallback = mOpCSRResponseCallback.Cancel(); Callback::Cancelable * failureCallback = mOnCSRFailureCallback.Cancel(); + MutableByteSpan csrNonce = device->GetCSRNonce(); + ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNOCSR(csrNonce)); ReturnErrorOnFailure(cluster.OpCSRRequest(successCallback, failureCallback, device->GetCSRNonce())); ChipLogDetail(Controller, "Sent OpCSR request, waiting for the CSR"); @@ -1379,7 +1381,7 @@ CHIP_ERROR DeviceCommissioner::ProcessOpCSR(const ByteSpan & NOCSRElements, cons mOperationalCredentialsDelegate->SetNodeIdForNextNOCRequest(device->GetDeviceId()); mOperationalCredentialsDelegate->SetFabricIdForNextNOCRequest(0); - return mOperationalCredentialsDelegate->GenerateNOCChain(NOCSRElements, AttestationSignature, ByteSpan(), ByteSpan(), + return mOperationalCredentialsDelegate->GenerateNOCChain(NOCSRElements, AttestationSignature, device->GetDAC(), ByteSpan(), ByteSpan(), &mDeviceNOCChainCallback); } diff --git a/src/controller/OperationalCredentialsDelegate.h b/src/controller/OperationalCredentialsDelegate.h index fed605aab208b4..b8353313fdc71a 100644 --- a/src/controller/OperationalCredentialsDelegate.h +++ b/src/controller/OperationalCredentialsDelegate.h @@ -76,6 +76,12 @@ class DLL_EXPORT OperationalCredentialsDelegate * fabric ID. */ virtual void SetFabricIdForNextNOCRequest(FabricId fabricId) {} + + virtual CHIP_ERROR GenerateNOCSR(MutableByteSpan & csrNonce) + { + ReturnErrorOnFailure(Crypto::DRBG_get_bytes(csrNonce.data(), csrNonce.size())); + return CHIP_NO_ERROR; + } }; } // namespace Controller