blob: 852d17c6a8b0a41e33df96fe40133b7dee61e841 [file] [log] [blame]
/*
* Copyright (c) 2020-2024 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 "PairingCommand.h"
#include "platform/PlatformManager.h"
#include "RpcClientProcessor.h"
#include "joint_fabric_service/joint_fabric_service.rpc.pb.h"
#include <commands/common/DeviceScanner.h>
#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/support/logging/CHIPLogging.h>
#include <protocols/secure_channel/PASESession.h>
#include <setup_payload/ManualSetupPayloadParser.h>
#include <setup_payload/QRCodeSetupPayloadParser.h>
#include <string>
using namespace ::chip;
using namespace ::chip::Controller;
CHIP_ERROR PairingCommand::RunCommand()
{
CurrentCommissioner().RegisterPairingDelegate(this);
/* TODO: if JFA is onboarded get the administrator CAT initial version from JF_DS@GroupList (through RPC)
* https://github.com/project-chip/connectedhomeip/issues/39443
*/
chip::CASEAuthTag administratorCAT = GetAdminCATWithVersion(CHIP_CONFIG_ADMINISTRATOR_CAT_INITIAL_VERSION);
NodeId administratorCaseAdminSubject = NodeIdFromCASEAuthTag(administratorCAT);
/* TODO: if JFA is onboarded get the Anchor CAT initial version from JF_DS@GroupList (through RPC)
* https://github.com/project-chip/connectedhomeip/issues/39443
*/
chip::CASEAuthTag anchorCAT = GetAnchorCATWithVersion(CHIP_CONFIG_ANCHOR_CAT_INITIAL_VERSION);
NodeId anchorCaseAdminSubject = NodeIdFromCASEAuthTag(anchorCAT);
if (mAnchorNodeId == chip::kUndefinedNodeId)
{
if (!mAnchor.ValueOr(false))
{
ChipLogError(JointFabric, "Please first commission the Anchor Administrator: add `--anchor true` parameter");
return CHIP_ERROR_NOT_CONNECTED;
}
else
{
if (mExecuteJCM.ValueOr(false))
{
ChipLogError(JointFabric, "--anchor and --execute-jcm options are not allowed simultaneously!");
return CHIP_ERROR_BAD_REQUEST;
}
// JFA will be issued a NOC with Anchor CAT and Administrator CAT
mCASEAuthTags = MakeOptional(std::vector<uint32_t>{ administratorCAT, anchorCAT });
}
}
else if (mAnchor.ValueOr(false))
{
ChipLogError(JointFabric, "Anchor Administrator already commissioned as Node ID: " ChipLogFormatX64,
ChipLogValueX64(mAnchorNodeId));
return CHIP_ERROR_BAD_REQUEST;
}
else
{
mSkipCommissioningComplete = MakeOptional(true);
}
mDeviceIsICD = false;
// Clear the CATs in OperationalCredentialsIssuer
mCredIssuerCmds->SetCredentialIssuerCATValues(kUndefinedCATs);
// All the AddNOC commands invoked by JFC will have
// the value below for the CaseAdminSubject field
if (mExecuteJCM.ValueOr(false))
{
// JFA-B will be issued a NOC with Administrator CAT
mCASEAuthTags = MakeOptional(std::vector<uint32_t>{ administratorCAT });
(static_cast<ExampleCredentialIssuerCommands *>(mCredIssuerCmds))
->SetCredentialIssuerCaseAdminSubject(anchorCaseAdminSubject);
}
else
{
(static_cast<ExampleCredentialIssuerCommands *>(mCredIssuerCmds))
->SetCredentialIssuerCaseAdminSubject(administratorCaseAdminSubject);
}
if (mCASEAuthTags.HasValue() && mCASEAuthTags.Value().size() <= kMaxSubjectCATAttributeCount)
{
CATValues cats = kUndefinedCATs;
for (size_t index = 0; index < mCASEAuthTags.Value().size(); ++index)
{
cats.values[index] = mCASEAuthTags.Value()[index];
}
if (cats.AreValid())
{
mCredIssuerCmds->SetCredentialIssuerCATValues(cats);
}
}
return RunInternal(mNodeId);
}
CHIP_ERROR PairingCommand::RunInternal(NodeId remoteId)
{
CHIP_ERROR err = CHIP_NO_ERROR;
switch (mPairingMode)
{
case PairingMode::None:
err = Unpair(remoteId);
break;
case PairingMode::Code:
#if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
chip::DeviceLayer::ConnectivityMgr().WiFiPafSetApFreq(
mApFreqStr.HasValue() ? static_cast<uint16_t>(std::stol(mApFreqStr.Value())) : 0);
#endif
err = PairWithCode(remoteId);
break;
case PairingMode::CodePaseOnly:
err = PaseWithCode(remoteId);
break;
case PairingMode::Ble:
err = Pair(remoteId, PeerAddress::BLE());
break;
case PairingMode::Nfc:
if (mDiscriminator.has_value())
{
err = Pair(remoteId, PeerAddress::NFC(mDiscriminator.value()));
}
else
{
// Discriminator is mandatory
err = CHIP_ERROR_MESSAGE_INCOMPLETE;
}
break;
case PairingMode::OnNetwork:
err = PairWithMdns(remoteId);
break;
case PairingMode::SoftAP:
err = Pair(remoteId, PeerAddress::UDP(mRemoteAddr.address, mRemotePort, mRemoteAddr.interfaceId));
break;
#if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
case PairingMode::WiFiPAF:
chip::DeviceLayer::ConnectivityMgr().WiFiPafSetApFreq(
mApFreqStr.HasValue() ? static_cast<uint16_t>(std::stol(mApFreqStr.Value())) : 0);
err = Pair(remoteId, PeerAddress::WiFiPAF(remoteId));
break;
#endif
case PairingMode::AlreadyDiscovered:
err = Pair(remoteId, PeerAddress::UDP(mRemoteAddr.address, mRemotePort, mRemoteAddr.interfaceId));
break;
case PairingMode::AlreadyDiscoveredByIndex:
err = PairWithMdnsOrBleByIndex(remoteId, mIndex);
break;
case PairingMode::AlreadyDiscoveredByIndexWithCode:
err = PairWithMdnsOrBleByIndexWithCode(remoteId, mIndex);
break;
}
return err;
}
CommissioningParameters PairingCommand::GetCommissioningParameters()
{
auto params = CommissioningParameters();
params.SetSkipCommissioningComplete(mSkipCommissioningComplete.ValueOr(false));
params.SetExecuteJCM(mExecuteJCM.ValueOr(false));
if (mBypassAttestationVerifier.ValueOr(false))
{
params.SetDeviceAttestationDelegate(this);
}
switch (mNetworkType)
{
case PairingNetworkType::WiFi:
params.SetWiFiCredentials(Controller::WiFiCredentials(mSSID, mPassword));
break;
case PairingNetworkType::Thread:
params.SetThreadOperationalDataset(mOperationalDataset);
break;
case PairingNetworkType::WiFiOrThread:
params.SetWiFiCredentials(Controller::WiFiCredentials(mSSID, mPassword));
params.SetThreadOperationalDataset(mOperationalDataset);
break;
case PairingNetworkType::None:
break;
}
if (mCountryCode.HasValue())
{
params.SetCountryCode(CharSpan::fromCharString(mCountryCode.Value()));
}
// mTCAcknowledgements and mTCAcknowledgementVersion are optional, but related. When one is missing, default the value to 0, to
// increase the test tools ability to test the applications.
if (mTCAcknowledgements.HasValue() || mTCAcknowledgementVersion.HasValue())
{
TermsAndConditionsAcknowledgement termsAndConditionsAcknowledgement = {
.acceptedTermsAndConditions = mTCAcknowledgements.ValueOr(0),
.acceptedTermsAndConditionsVersion = mTCAcknowledgementVersion.ValueOr(0),
};
params.SetTermsAndConditionsAcknowledgement(termsAndConditionsAcknowledgement);
}
// mTimeZoneList is an optional argument managed by TypedComplexArgument mComplex_TimeZones.
// Since optional Complex arguments are not currently supported via the <chip::Optional> class,
// we will use mTimeZoneList.data() value to determine if the argument was provided.
if (mTimeZoneList.data())
{
params.SetTimeZone(mTimeZoneList);
}
// miDSTOffsetList is an optional argument managed by TypedComplexArgument mComplex_DSTOffsets.
// Since optional Complex arguments are not currently supported via the <chip::Optional> class,
// we will use mTimeZoneList.data() value to determine if the argument was provided.
if (mDSTOffsetList.data())
{
params.SetDSTOffsets(mDSTOffsetList);
}
if (mICDRegistration.ValueOr(false))
{
params.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete);
if (!mICDSymmetricKey.HasValue())
{
Crypto::DRBG_get_bytes(mRandomGeneratedICDSymmetricKey, sizeof(mRandomGeneratedICDSymmetricKey));
mICDSymmetricKey.SetValue(ByteSpan(mRandomGeneratedICDSymmetricKey));
}
if (!mICDCheckInNodeId.HasValue())
{
mICDCheckInNodeId.SetValue(CurrentCommissioner().GetNodeId());
}
if (!mICDMonitoredSubject.HasValue())
{
mICDMonitoredSubject.SetValue(mICDCheckInNodeId.Value());
}
if (!mICDClientType.HasValue())
{
mICDClientType.SetValue(app::Clusters::IcdManagement::ClientTypeEnum::kPermanent);
}
// These Optionals must have values now.
// The commissioner will verify these values.
params.SetICDSymmetricKey(mICDSymmetricKey.Value());
if (mICDStayActiveDurationMsec.HasValue())
{
params.SetICDStayActiveDurationMsec(mICDStayActiveDurationMsec.Value());
}
params.SetICDCheckInNodeId(mICDCheckInNodeId.Value());
params.SetICDMonitoredSubject(mICDMonitoredSubject.Value());
params.SetICDClientType(mICDClientType.Value());
}
return params;
}
CHIP_ERROR PairingCommand::PaseWithCode(NodeId remoteId)
{
auto discoveryType = DiscoveryType::kAll;
if (mUseOnlyOnNetworkDiscovery.ValueOr(false))
{
discoveryType = DiscoveryType::kDiscoveryNetworkOnly;
}
if (mDiscoverOnce.ValueOr(false))
{
discoveryType = DiscoveryType::kDiscoveryNetworkOnlyWithoutPASEAutoRetry;
}
return CurrentCommissioner().EstablishPASEConnection(remoteId, mOnboardingPayload, discoveryType);
}
CHIP_ERROR PairingCommand::PairWithCode(NodeId remoteId)
{
CommissioningParameters commissioningParams = GetCommissioningParameters();
// If no network discovery behavior and no network credentials are provided, assume that the pairing command is trying to pair
// with an on-network device.
if (!mUseOnlyOnNetworkDiscovery.HasValue())
{
auto threadCredentials = commissioningParams.GetThreadOperationalDataset();
auto wiFiCredentials = commissioningParams.GetWiFiCredentials();
mUseOnlyOnNetworkDiscovery.SetValue(!threadCredentials.HasValue() && !wiFiCredentials.HasValue());
}
auto discoveryType = DiscoveryType::kAll;
if (mUseOnlyOnNetworkDiscovery.ValueOr(false))
{
discoveryType = DiscoveryType::kDiscoveryNetworkOnly;
}
if (mDiscoverOnce.ValueOr(false))
{
discoveryType = DiscoveryType::kDiscoveryNetworkOnlyWithoutPASEAutoRetry;
}
return CurrentCommissioner().PairDevice(remoteId, mOnboardingPayload, commissioningParams, discoveryType);
}
CHIP_ERROR PairingCommand::Pair(NodeId remoteId, PeerAddress address)
{
VerifyOrDieWithMsg(mSetupPINCode.has_value(), chipTool, "Using mSetupPINCode in a mode when we have not gotten one");
auto params = RendezvousParameters().SetSetupPINCode(mSetupPINCode.value()).SetPeerAddress(address);
if (mDiscriminator.has_value())
{
params.SetDiscriminator(mDiscriminator.value());
}
CHIP_ERROR err = CHIP_NO_ERROR;
if (mPaseOnly.ValueOr(false))
{
err = CurrentCommissioner().EstablishPASEConnection(remoteId, params);
}
else
{
auto commissioningParams = GetCommissioningParameters();
err = CurrentCommissioner().PairDevice(remoteId, params, commissioningParams);
}
return err;
}
CHIP_ERROR PairingCommand::PairWithMdnsOrBleByIndex(NodeId remoteId, uint16_t index)
{
#if CHIP_DEVICE_LAYER_TARGET_DARWIN
VerifyOrReturnError(IsInteractive(), CHIP_ERROR_INCORRECT_STATE);
VerifyOrDieWithMsg(mSetupPINCode.has_value(), chipTool, "Using mSetupPINCode in a mode when we have not gotten one");
RendezvousParameters params;
ReturnErrorOnFailure(GetDeviceScanner().Get(index, params));
params.SetSetupPINCode(mSetupPINCode.value());
CHIP_ERROR err = CHIP_NO_ERROR;
if (mPaseOnly.ValueOr(false))
{
err = CurrentCommissioner().EstablishPASEConnection(remoteId, params);
}
else
{
auto commissioningParams = GetCommissioningParameters();
err = CurrentCommissioner().PairDevice(remoteId, params, commissioningParams);
}
return err;
#else
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN
}
CHIP_ERROR PairingCommand::PairWithMdnsOrBleByIndexWithCode(NodeId remoteId, uint16_t index)
{
// We might or might not have a setup code. We don't know yet, but if we
// do, we'll emplace it at that point.
mSetupPINCode.reset();
#if CHIP_DEVICE_LAYER_TARGET_DARWIN
VerifyOrReturnError(IsInteractive(), CHIP_ERROR_INCORRECT_STATE);
Dnssd::CommonResolutionData resolutionData;
auto err = GetDeviceScanner().Get(index, resolutionData);
if (CHIP_ERROR_NOT_FOUND == err)
{
// There is no device with this index that has some resolution data. This could simply
// be because the device is a ble device. In this case let's fall back to looking for
// a device with this index and some RendezvousParameters.
SetupPayload payload;
bool isQRCode = strncmp(mOnboardingPayload, kQRCodePrefix, strlen(kQRCodePrefix)) == 0;
if (isQRCode)
{
ReturnErrorOnFailure(QRCodeSetupPayloadParser(mOnboardingPayload).populatePayload(payload));
VerifyOrReturnError(payload.isValidQRCodePayload(), CHIP_ERROR_INVALID_ARGUMENT);
}
else
{
ReturnErrorOnFailure(ManualSetupPayloadParser(mOnboardingPayload).populatePayload(payload));
VerifyOrReturnError(payload.isValidManualCode(), CHIP_ERROR_INVALID_ARGUMENT);
}
mSetupPINCode.emplace(payload.setUpPINCode);
return PairWithMdnsOrBleByIndex(remoteId, index);
}
err = CHIP_NO_ERROR;
if (mPaseOnly.ValueOr(false))
{
err = CurrentCommissioner().EstablishPASEConnection(remoteId, mOnboardingPayload, DiscoveryType::kDiscoveryNetworkOnly,
MakeOptional(resolutionData));
}
else
{
auto commissioningParams = GetCommissioningParameters();
err = CurrentCommissioner().PairDevice(remoteId, mOnboardingPayload, commissioningParams,
DiscoveryType::kDiscoveryNetworkOnly, MakeOptional(resolutionData));
}
return err;
#else
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN
}
CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId)
{
Dnssd::DiscoveryFilter filter(mFilterType);
switch (mFilterType)
{
case Dnssd::DiscoveryFilterType::kNone:
break;
case Dnssd::DiscoveryFilterType::kShortDiscriminator:
case Dnssd::DiscoveryFilterType::kLongDiscriminator:
case Dnssd::DiscoveryFilterType::kCompressedFabricId:
case Dnssd::DiscoveryFilterType::kVendorId:
case Dnssd::DiscoveryFilterType::kDeviceType:
filter.code = mDiscoveryFilterCode;
break;
case Dnssd::DiscoveryFilterType::kCommissioningMode:
break;
case Dnssd::DiscoveryFilterType::kCommissioner:
filter.code = 1;
break;
case Dnssd::DiscoveryFilterType::kInstanceName:
filter.code = 0;
filter.instanceName = mDiscoveryFilterInstanceName;
break;
}
CurrentCommissioner().RegisterDeviceDiscoveryDelegate(this);
return CurrentCommissioner().DiscoverCommissionableNodes(filter);
}
CHIP_ERROR PairingCommand::Unpair(NodeId remoteId)
{
mCurrentFabricRemover = Platform::MakeUnique<Controller::CurrentFabricRemover>(&CurrentCommissioner());
return mCurrentFabricRemover->RemoveCurrentFabric(remoteId, &mCurrentFabricRemoveCallback);
}
void PairingCommand::OnStatusUpdate(DevicePairingDelegate::Status status)
{
switch (status)
{
case DevicePairingDelegate::Status::SecurePairingSuccess:
ChipLogProgress(chipTool, "Secure Pairing Success");
ChipLogProgress(chipTool, "CASE establishment successful");
break;
case DevicePairingDelegate::Status::SecurePairingFailed:
ChipLogError(chipTool, "Secure Pairing Failed");
SetCommandExitStatus(CHIP_ERROR_INCORRECT_STATE);
break;
}
}
void PairingCommand::OnPairingComplete(CHIP_ERROR err)
{
if (err == CHIP_NO_ERROR)
{
ChipLogProgress(chipTool, "Pairing Success");
ChipLogProgress(chipTool, "PASE establishment successful");
if (mPairingMode == PairingMode::CodePaseOnly || mPaseOnly.ValueOr(false))
{
SetCommandExitStatus(err);
}
}
else
{
ChipLogProgress(chipTool, "Pairing Failure: %s", ErrorStr(err));
}
if (err != CHIP_NO_ERROR)
{
SetCommandExitStatus(err);
}
}
void PairingCommand::OnPairingDeleted(CHIP_ERROR err)
{
if (err == CHIP_NO_ERROR)
{
ChipLogProgress(chipTool, "Pairing Deleted Success");
}
else
{
ChipLogProgress(chipTool, "Pairing Deleted Failure: %s", ErrorStr(err));
}
SetCommandExitStatus(err);
}
namespace {
// Constants
constexpr uint32_t kRpcTimeoutMs = 1000;
constexpr uint32_t kDefaultChannelId = 1;
::pw_rpc::nanopb::JointFabric::Client rpcClient(chip::rpc::client::GetDefaultRpcClient(), kDefaultChannelId);
std::mutex responseMutex;
std::condition_variable responseCv;
bool responseReceived = false;
CHIP_ERROR responseError = CHIP_NO_ERROR;
// By passing the `call` parameter into WaitForResponse we are explicitly trying to insure the caller takes into consideration that
// the lifetime of the `call` object when calling WaitForResponse
template <typename CallType>
CHIP_ERROR WaitForResponse(CallType & call)
{
std::unique_lock<std::mutex> lock(responseMutex);
responseReceived = false;
responseError = CHIP_NO_ERROR;
if (responseCv.wait_for(lock, std::chrono::milliseconds(kRpcTimeoutMs), [] { return responseReceived; }))
{
return responseError;
}
else
{
return CHIP_ERROR_TIMEOUT;
}
}
// Callback function to be called when the RPC response is received
void OnOwnershipTransferDone(const _pw_protobuf_Empty & response, ::pw::Status status)
{
std::lock_guard<std::mutex> lock(responseMutex);
responseReceived = true;
responseError = status.ok() ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL;
responseCv.notify_one();
if (status.ok())
{
ChipLogProgress(JointFabric, "OnOwnershipTransferDone RPC call succeeded!");
}
else
{
ChipLogProgress(JointFabric, "OnOwnershipTransferDone RPC call failed with status: %d\n", status.code());
}
}
} // namespace
void PairingCommand::OnCommissioningComplete(NodeId nodeId, const Optional<Crypto::P256PublicKey> & trustedIcacPublicKeyB,
CHIP_ERROR err)
{
if (err == CHIP_NO_ERROR)
{
if (!mSkipCommissioningComplete.ValueOr(false))
{
ChipLogProgress(JointFabric, "Anchor Administrator commissioned with sucess");
mAnchorNodeId = nodeId;
}
else
{
OwnershipContext request;
memset(&request, 0, sizeof(request));
request.node_id = nodeId;
request.jcm = false;
if (mExecuteJCM.ValueOr(false))
{
request.jcm = true;
if (trustedIcacPublicKeyB.HasValue())
{
memcpy(request.trustedIcacPublicKeyB.bytes, trustedIcacPublicKeyB.Value().ConstBytes(),
Crypto::kP256_PublicKey_Length);
request.trustedIcacPublicKeyB.size = Crypto::kP256_PublicKey_Length;
for (size_t i = 0; i < Crypto::kP256_PublicKey_Length; ++i)
{
ChipLogProgress(JointFabric, "trustedIcacPublicKeyB[%li] = %02X", i,
request.trustedIcacPublicKeyB.bytes[i]);
}
}
else
{
SetCommandExitStatus(CHIP_ERROR_INVALID_ARGUMENT);
ChipLogError(chipTool, "JCM requested but peer Admin ICAC not found");
return;
}
}
auto call = rpcClient.TransferOwnership(request, OnOwnershipTransferDone);
if (!call.active())
{
ChipLogError(JointFabric, "RPC: OwnershipTransfer Call Error");
// The RPC call was not sent. This could occur due to, for example, an invalid channel ID. Handle if necessary.
}
err = WaitForResponse(call);
if (err != CHIP_NO_ERROR)
{
ChipLogError(JointFabric, "RPC: OwnershipTransfer Timeout Error");
}
}
}
else
{
// When ICD device commissioning fails, the ICDClientInfo stored in OnICDRegistrationComplete needs to be removed.
if (mDeviceIsICD)
{
CHIP_ERROR deleteEntryError =
CHIPCommand::sICDClientStorage.DeleteEntry(ScopedNodeId(mNodeId, CurrentCommissioner().GetFabricIndex()));
if (deleteEntryError != CHIP_NO_ERROR)
{
ChipLogError(chipTool, "Failed to delete ICD entry: %s", ErrorStr(err));
}
}
ChipLogProgress(chipTool, "Device commissioning Failure: %s", ErrorStr(err));
}
SetCommandExitStatus(err);
}
void PairingCommand::OnReadCommissioningInfo(const Controller::ReadCommissioningInfo & info)
{
ChipLogProgress(AppServer, "OnReadCommissioningInfo - vendorId=0x%04X productId=0x%04X", info.basic.vendorId,
info.basic.productId);
// The string in CharSpan received from the device is not null-terminated, we use std::string here for coping and
// appending a numm-terminator at the end of the string.
std::string userActiveModeTriggerInstruction;
// Note: the callback doesn't own the buffer, should make a copy if it will be used it later.
if (info.icd.userActiveModeTriggerInstruction.size() != 0)
{
userActiveModeTriggerInstruction =
std::string(info.icd.userActiveModeTriggerInstruction.data(), info.icd.userActiveModeTriggerInstruction.size());
}
if (info.icd.userActiveModeTriggerHint.HasAny())
{
ChipLogProgress(AppServer, "OnReadCommissioningInfo - LIT UserActiveModeTriggerHint=0x%08x",
info.icd.userActiveModeTriggerHint.Raw());
ChipLogProgress(AppServer, "OnReadCommissioningInfo - LIT UserActiveModeTriggerInstruction=%s",
userActiveModeTriggerInstruction.c_str());
}
ChipLogProgress(AppServer, "OnReadCommissioningInfo ICD - IdleModeDuration=%u activeModeDuration=%u activeModeThreshold=%u",
info.icd.idleModeDuration, info.icd.activeModeDuration, info.icd.activeModeThreshold);
}
void PairingCommand::OnICDRegistrationComplete(ScopedNodeId nodeId, uint32_t icdCounter)
{
char icdSymmetricKeyHex[Crypto::kAES_CCM128_Key_Length * 2 + 1];
Encoding::BytesToHex(mICDSymmetricKey.Value().data(), mICDSymmetricKey.Value().size(), icdSymmetricKeyHex,
sizeof(icdSymmetricKeyHex), Encoding::HexFlags::kNullTerminate);
app::ICDClientInfo clientInfo;
clientInfo.check_in_node = ScopedNodeId(mICDCheckInNodeId.Value(), nodeId.GetFabricIndex());
clientInfo.peer_node = nodeId;
clientInfo.monitored_subject = mICDMonitoredSubject.Value();
clientInfo.start_icd_counter = icdCounter;
CHIP_ERROR err = CHIPCommand::sICDClientStorage.SetKey(clientInfo, mICDSymmetricKey.Value());
if (err == CHIP_NO_ERROR)
{
err = CHIPCommand::sICDClientStorage.StoreEntry(clientInfo);
}
if (err != CHIP_NO_ERROR)
{
CHIPCommand::sICDClientStorage.RemoveKey(clientInfo);
ChipLogError(chipTool, "Failed to persist symmetric key for " ChipLogFormatX64 ": %s", ChipLogValueX64(nodeId.GetNodeId()),
err.AsString());
SetCommandExitStatus(err);
return;
}
mDeviceIsICD = true;
ChipLogProgress(chipTool, "Saved ICD Symmetric key for " ChipLogFormatX64, ChipLogValueX64(nodeId.GetNodeId()));
ChipLogProgress(chipTool,
"ICD Registration Complete for device " ChipLogFormatX64 " / Check-In NodeID: " ChipLogFormatX64
" / Monitored Subject: " ChipLogFormatX64 " / Symmetric Key: %s / ICDCounter %u",
ChipLogValueX64(nodeId.GetNodeId()), ChipLogValueX64(mICDCheckInNodeId.Value()),
ChipLogValueX64(mICDMonitoredSubject.Value()), icdSymmetricKeyHex, icdCounter);
}
void PairingCommand::OnICDStayActiveComplete(ScopedNodeId deviceId, uint32_t promisedActiveDuration)
{
ChipLogProgress(chipTool, "ICD Stay Active Complete for device " ChipLogFormatX64 " / promisedActiveDuration: %u",
ChipLogValueX64(deviceId.GetNodeId()), promisedActiveDuration);
}
void PairingCommand::OnDiscoveredDevice(const Dnssd::CommissionNodeData & nodeData)
{
// Ignore nodes with closed commissioning window
VerifyOrReturn(nodeData.commissioningMode != 0);
auto & resolutionData = nodeData;
const uint16_t port = resolutionData.port;
char buf[Inet::IPAddress::kMaxStringLength];
resolutionData.ipAddress[0].ToString(buf);
ChipLogProgress(chipTool, "Discovered Device: %s:%u", buf, port);
// Stop Mdns discovery.
auto err = CurrentCommissioner().StopCommissionableDiscovery();
// Some platforms does not implement a mechanism to stop mdns browse, so
// we just ignore CHIP_ERROR_NOT_IMPLEMENTED instead of bailing out.
if (CHIP_NO_ERROR != err && CHIP_ERROR_NOT_IMPLEMENTED != err)
{
SetCommandExitStatus(err);
return;
}
CurrentCommissioner().RegisterDeviceDiscoveryDelegate(nullptr);
auto interfaceId = resolutionData.ipAddress[0].IsIPv6LinkLocal() ? resolutionData.interfaceId : Inet::InterfaceId::Null();
auto peerAddress = PeerAddress::UDP(resolutionData.ipAddress[0], port, interfaceId);
err = Pair(mNodeId, peerAddress);
if (CHIP_NO_ERROR != err)
{
SetCommandExitStatus(err);
}
}
void PairingCommand::OnCurrentFabricRemove(void * context, NodeId nodeId, CHIP_ERROR err)
{
PairingCommand * command = reinterpret_cast<PairingCommand *>(context);
VerifyOrReturn(command != nullptr, ChipLogError(chipTool, "OnCurrentFabricRemove: context is null"));
if (err == CHIP_NO_ERROR)
{
ChipLogProgress(chipTool, "Device unpair completed with success: " ChipLogFormatX64, ChipLogValueX64(nodeId));
}
else
{
ChipLogProgress(chipTool, "Device unpair Failure: " ChipLogFormatX64 " %s", ChipLogValueX64(nodeId), ErrorStr(err));
}
command->SetCommandExitStatus(err);
}
Optional<uint16_t> PairingCommand::FailSafeExpiryTimeoutSecs() const
{
// We don't need to set additional failsafe timeout as we don't ask the final user if he wants to continue
return Optional<uint16_t>();
}
void PairingCommand::OnDeviceAttestationCompleted(Controller::DeviceCommissioner * deviceCommissioner, DeviceProxy * device,
const Credentials::DeviceAttestationVerifier::AttestationDeviceInfo & info,
Credentials::AttestationVerificationResult attestationResult)
{
// Bypass attestation verification, continue with success
auto err = deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(
device, Credentials::AttestationVerificationResult::kSuccess);
if (CHIP_NO_ERROR != err)
{
SetCommandExitStatus(err);
}
}