| /* |
| * |
| * Copyright (c) 2020-2021 Project CHIP Authors |
| * Copyright (c) 2013-2017 Nest Labs, Inc. |
| * 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. |
| */ |
| |
| /** |
| * @file |
| * Implementation of CHIP Device Controller, a common class |
| * that implements discovery, pairing and provisioning of CHIP |
| * devices. |
| * |
| */ |
| |
| #ifndef __STDC_LIMIT_MACROS |
| #define __STDC_LIMIT_MACROS |
| #endif |
| #ifndef __STDC_FORMAT_MACROS |
| #define __STDC_FORMAT_MACROS |
| #endif |
| |
| // module header, comes first |
| #include <controller/CHIPDeviceController.h> |
| |
| #include <app/common/gen/enums.h> |
| #include <controller/data_model/gen/CHIPClusters.h> |
| |
| #if CONFIG_DEVICE_LAYER |
| #include <platform/CHIPDeviceLayer.h> |
| #include <platform/ConfigurationManager.h> |
| #endif |
| |
| #include <app/InteractionModelEngine.h> |
| #include <app/util/DataModelHandler.h> |
| #include <core/CHIPCore.h> |
| #include <core/CHIPEncoding.h> |
| #include <core/CHIPSafeCasts.h> |
| #include <credentials/CHIPCert.h> |
| #include <messaging/ExchangeContext.h> |
| #include <protocols/secure_channel/MessageCounterManager.h> |
| #include <setup_payload/QRCodeSetupPayloadParser.h> |
| #include <support/Base64.h> |
| #include <support/CHIPArgParser.hpp> |
| #include <support/CHIPMem.h> |
| #include <support/CodeUtils.h> |
| #include <support/ErrorStr.h> |
| #include <support/PersistentStorageMacros.h> |
| #include <support/SafeInt.h> |
| #include <support/ScopedBuffer.h> |
| #include <support/TimeUtils.h> |
| #include <support/logging/CHIPLogging.h> |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| #include <ble/BleLayer.h> |
| #include <transport/raw/BLE.h> |
| #endif |
| |
| #include <app/util/af-enums.h> |
| |
| #include <errno.h> |
| #include <inttypes.h> |
| #include <memory> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <time.h> |
| |
| using namespace chip::Inet; |
| using namespace chip::System; |
| using namespace chip::Credentials; |
| |
| // For some applications those does not implement IMDelegate, the DeviceControllerInteractionModelDelegate will dispatch the |
| // response to IMDefaultResponseCallback CHIPClientCallbacks, for the applications those implemented IMDelegate, this function will |
| // not be used. |
| bool __attribute__((weak)) IMDefaultResponseCallback(const chip::app::Command * commandObj, EmberAfStatus status) |
| { |
| return false; |
| } |
| |
| namespace chip { |
| namespace Controller { |
| |
| using namespace chip::Encoding; |
| |
| constexpr uint32_t kSessionEstablishmentTimeout = 30 * kMillisecondPerSecond; |
| |
| // 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() |
| { |
| mState = State::NotInitialized; |
| mSessionMgr = nullptr; |
| mExchangeMgr = nullptr; |
| mLocalDeviceId = 0; |
| mStorageDelegate = nullptr; |
| mPairedDevicesInitialized = false; |
| mListenPort = CHIP_PORT; |
| } |
| |
| CHIP_ERROR DeviceController::Init(NodeId localDeviceId, ControllerInitParams params) |
| { |
| VerifyOrReturnError(mState == State::NotInitialized, CHIP_ERROR_INCORRECT_STATE); |
| |
| if (params.systemLayer != nullptr && params.inetLayer != nullptr) |
| { |
| mSystemLayer = params.systemLayer; |
| mInetLayer = params.inetLayer; |
| } |
| else |
| { |
| #if CONFIG_DEVICE_LAYER |
| ReturnErrorOnFailure(DeviceLayer::PlatformMgr().InitChipStack()); |
| |
| mSystemLayer = &DeviceLayer::SystemLayer; |
| mInetLayer = &DeviceLayer::InetLayer; |
| #endif // CONFIG_DEVICE_LAYER |
| } |
| |
| VerifyOrReturnError(mSystemLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(mInetLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| mStorageDelegate = params.storageDelegate; |
| #if CONFIG_NETWORK_LAYER_BLE |
| #if CONFIG_DEVICE_LAYER |
| if (params.bleLayer == nullptr) |
| { |
| params.bleLayer = DeviceLayer::ConnectivityMgr().GetBleLayer(); |
| } |
| #endif // CONFIG_DEVICE_LAYER |
| mBleLayer = params.bleLayer; |
| VerifyOrReturnError(mBleLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| #endif |
| |
| mTransportMgr = chip::Platform::New<DeviceTransportMgr>(); |
| mSessionMgr = chip::Platform::New<SecureSessionMgr>(); |
| mExchangeMgr = chip::Platform::New<Messaging::ExchangeManager>(); |
| mMessageCounterManager = chip::Platform::New<secure_channel::MessageCounterManager>(); |
| |
| ReturnErrorOnFailure(mTransportMgr->Init( |
| Transport::UdpListenParameters(mInetLayer).SetAddressType(Inet::kIPAddressType_IPv6).SetListenPort(mListenPort) |
| #if INET_CONFIG_ENABLE_IPV4 |
| , |
| Transport::UdpListenParameters(mInetLayer).SetAddressType(Inet::kIPAddressType_IPv4).SetListenPort(mListenPort) |
| #endif |
| #if CONFIG_NETWORK_LAYER_BLE |
| , |
| Transport::BleListenParameters(mBleLayer) |
| #endif |
| )); |
| |
| ReturnErrorOnFailure(mAdmins.Init(mStorageDelegate)); |
| |
| Transport::AdminPairingInfo * const admin = mAdmins.AssignAdminId(mAdminId, localDeviceId); |
| VerifyOrReturnError(admin != nullptr, CHIP_ERROR_NO_MEMORY); |
| |
| ReturnErrorOnFailure(mAdmins.LoadFromStorage(mAdminId)); |
| |
| ReturnErrorOnFailure(mSessionMgr->Init(localDeviceId, mSystemLayer, mTransportMgr, &mAdmins, mMessageCounterManager)); |
| |
| ReturnErrorOnFailure(mExchangeMgr->Init(mSessionMgr)); |
| |
| ReturnErrorOnFailure(mMessageCounterManager->Init(mExchangeMgr)); |
| |
| ReturnErrorOnFailure(mExchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::TempZCL::Id, this)); |
| |
| if (params.imDelegate != nullptr) |
| { |
| ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->Init(mExchangeMgr, params.imDelegate)); |
| } |
| else |
| { |
| mDefaultIMDelegate = chip::Platform::New<DeviceControllerInteractionModelDelegate>(); |
| ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->Init(mExchangeMgr, mDefaultIMDelegate)); |
| } |
| |
| mExchangeMgr->SetDelegate(this); |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| ReturnErrorOnFailure(Mdns::Resolver::Instance().SetResolverDelegate(this)); |
| |
| RegisterDeviceAddressUpdateDelegate(params.mDeviceAddressUpdateDelegate); |
| |
| Mdns::Resolver::Instance().StartResolver(mInetLayer, kMdnsPort); |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| |
| InitDataModelHandler(mExchangeMgr); |
| |
| mState = State::Initialized; |
| mLocalDeviceId = localDeviceId; |
| |
| VerifyOrReturnError(params.operationalCredentialsDelegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| mOperationalCredentialsDelegate = params.operationalCredentialsDelegate; |
| |
| ReturnErrorOnFailure(LoadLocalCredentials(admin)); |
| |
| ReleaseAllDevices(); |
| |
| 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"); |
| Crypto::P256Keypair * keypair = admin->GetOperationalKey(); |
| |
| ReturnErrorCodeIf(keypair == nullptr, CHIP_ERROR_NO_MEMORY); |
| |
| if (!admin->AreCredentialsAvailable()) |
| { |
| chip::Platform::ScopedMemoryBuffer<uint8_t> chipCert; |
| uint32_t chipCertAllocatedLen = kMaxCHIPCertLength * 2; |
| ReturnErrorCodeIf(!chipCert.Alloc(chipCertAllocatedLen), CHIP_ERROR_NO_MEMORY); |
| uint32_t chipCertLen = 0; |
| |
| // 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; |
| |
| ReturnErrorOnFailure( |
| mOperationalCredentialsDelegate->GetRootCACertificate(0, rootCert.Get(), kMaxCHIPDERCertLength, rootCertLen)); |
| ReturnErrorOnFailure( |
| ConvertX509CertToChipCert(rootCert.Get(), rootCertLen, chipCert.Get(), chipCertAllocatedLen, chipCertLen)); |
| |
| ReturnErrorOnFailure(admin->SetRootCert(ByteSpan(chipCert.Get(), 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())); |
| } |
| |
| ChipLogProgress(Controller, "Generating credentials"); |
| ReturnErrorOnFailure(admin->GetCredentials(mCredentials, mCertificates, mRootKeyId)); |
| |
| ChipLogProgress(Controller, "Loaded credentials successfully"); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceController::Shutdown() |
| { |
| VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); |
| |
| ChipLogDetail(Controller, "Shutting down the controller"); |
| |
| #if CONFIG_DEVICE_LAYER |
| // |
| // We can safely call PlatformMgr().Shutdown(), which like DeviceController::Shutdown(), |
| // expects to be called with external thread synchronization and will not try to acquire the |
| // stack lock. |
| // |
| // Actually stopping the event queue is a separable call that applications will have to sequence. |
| // Consumers are expected to call PlaformMgr().StopEventLoopTask() before calling |
| // DeviceController::Shutdown() in the CONFIG_DEVICE_LAYER configuration |
| // |
| ReturnErrorOnFailure(DeviceLayer::PlatformMgr().Shutdown()); |
| #else |
| VerifyOrReturnError(mInetLayer->Shutdown() == INET_NO_ERROR, CHIP_ERROR_INTERNAL); |
| VerifyOrReturnError(mSystemLayer->Shutdown() == CHIP_SYSTEM_NO_ERROR, CHIP_ERROR_INTERNAL); |
| chip::Platform::Delete(mInetLayer); |
| chip::Platform::Delete(mSystemLayer); |
| #endif // CONFIG_DEVICE_LAYER |
| |
| mSystemLayer = nullptr; |
| mInetLayer = nullptr; |
| |
| mState = State::NotInitialized; |
| |
| // Shut down the interaction model before we try shuttting down the exchange |
| // manager. |
| app::InteractionModelEngine::GetInstance()->Shutdown(); |
| |
| // TODO(#6668): Some exchange has leak, shutting down ExchangeManager will cause a assert fail. |
| // if (mExchangeMgr != nullptr) |
| // { |
| // mExchangeMgr->Shutdown(); |
| // } |
| if (mSessionMgr != nullptr) |
| { |
| mSessionMgr->Shutdown(); |
| } |
| |
| mStorageDelegate = nullptr; |
| |
| ReleaseAllDevices(); |
| |
| if (mMessageCounterManager != nullptr) |
| { |
| chip::Platform::Delete(mMessageCounterManager); |
| mMessageCounterManager = nullptr; |
| } |
| |
| if (mExchangeMgr != nullptr) |
| { |
| chip::Platform::Delete(mExchangeMgr); |
| mExchangeMgr = nullptr; |
| } |
| |
| if (mSessionMgr != nullptr) |
| { |
| chip::Platform::Delete(mSessionMgr); |
| mSessionMgr = nullptr; |
| } |
| |
| if (mTransportMgr != nullptr) |
| { |
| chip::Platform::Delete(mTransportMgr); |
| mTransportMgr = nullptr; |
| } |
| |
| if (mDefaultIMDelegate != nullptr) |
| { |
| chip::Platform::Delete(mDefaultIMDelegate); |
| mDefaultIMDelegate = nullptr; |
| } |
| |
| mAdmins.ReleaseAdminId(mAdminId); |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| Mdns::Resolver::Instance().SetResolverDelegate(nullptr); |
| mDeviceAddressUpdateDelegate = nullptr; |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceController::SetUdpListenPort(uint16_t listenPort) |
| { |
| if (mState == State::Initialized) |
| { |
| return CHIP_ERROR_INCORRECT_STATE; |
| } |
| |
| mListenPort = listenPort; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceController::GetDevice(NodeId deviceId, Device ** out_device) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| Device * device = nullptr; |
| uint16_t index = 0; |
| |
| VerifyOrExit(out_device != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); |
| index = FindDeviceIndex(deviceId); |
| |
| if (index < kNumMaxActiveDevices) |
| { |
| device = &mActiveDevices[index]; |
| } |
| else |
| { |
| err = InitializePairedDeviceList(); |
| SuccessOrExit(err); |
| |
| VerifyOrExit(mPairedDevices.Contains(deviceId), err = CHIP_ERROR_NOT_CONNECTED); |
| |
| index = GetInactiveDeviceIndex(); |
| VerifyOrExit(index < kNumMaxActiveDevices, err = CHIP_ERROR_NO_MEMORY); |
| device = &mActiveDevices[index]; |
| |
| { |
| SerializedDevice deviceInfo; |
| uint16_t size = sizeof(deviceInfo.inner); |
| |
| PERSISTENT_KEY_OP(deviceId, kPairedDeviceKeyPrefix, key, |
| err = mStorageDelegate->SyncGetKeyValue(key, deviceInfo.inner, size)); |
| SuccessOrExit(err); |
| VerifyOrExit(size <= sizeof(deviceInfo.inner), err = CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR); |
| |
| err = device->Deserialize(deviceInfo); |
| VerifyOrExit(err == CHIP_NO_ERROR, ReleaseDevice(device)); |
| |
| device->Init(GetControllerDeviceInitParams(), mListenPort, mAdminId); |
| } |
| } |
| |
| *out_device = device; |
| |
| exit: |
| if (err != CHIP_NO_ERROR && device != nullptr) |
| { |
| ReleaseDevice(device); |
| } |
| return err; |
| } |
| |
| bool DeviceController::DoesDevicePairingExist(const PeerId & deviceId) |
| { |
| 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(deviceId).SetFabricId(fabricId), |
| chip::Inet::kIPAddressType_Any); |
| #else |
| return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| } |
| |
| void DeviceController::PersistDevice(Device * device) |
| { |
| if (mState == State::Initialized) |
| { |
| device->Persist(); |
| } |
| else |
| { |
| ChipLogError(Controller, "Failed to persist device. Controller not initialized."); |
| } |
| } |
| |
| CHIP_ERROR DeviceController::ServiceEvents() |
| { |
| VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); |
| |
| #if CONFIG_DEVICE_LAYER |
| ReturnErrorOnFailure(DeviceLayer::PlatformMgr().StartEventLoopTask()); |
| #endif // CONFIG_DEVICE_LAYER |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceController::ServiceEventSignal() |
| { |
| VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); |
| |
| #if CONFIG_DEVICE_LAYER && CHIP_SYSTEM_CONFIG_USE_IO_THREAD |
| DeviceLayer::SystemLayer.WakeIOThread(); |
| #else |
| ReturnErrorOnFailure(CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); |
| #endif // CONFIG_DEVICE_LAYER && CHIP_SYSTEM_CONFIG_USE_IO_THREAD |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceController::GetFabricId(uint64_t & fabricId) |
| { |
| Transport::AdminPairingInfo * admin = mAdmins.FindAdminWithId(mAdminId); |
| VerifyOrReturnError(admin != nullptr, CHIP_ERROR_INCORRECT_STATE); |
| |
| fabricId = admin->GetFabricId(); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceController::OnMessageReceived(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, |
| const PayloadHeader & payloadHeader, System::PacketBufferHandle && msgBuf) |
| { |
| uint16_t index; |
| bool needClose = true; |
| |
| VerifyOrExit(mState == State::Initialized, ChipLogError(Controller, "OnMessageReceived was called in incorrect state")); |
| |
| VerifyOrExit(packetHeader.GetSourceNodeId().HasValue(), |
| ChipLogError(Controller, "OnMessageReceived was called for unknown source node")); |
| |
| index = FindDeviceIndex(packetHeader.GetSourceNodeId().Value()); |
| VerifyOrExit(index < kNumMaxActiveDevices, ChipLogError(Controller, "OnMessageReceived was called for unknown device object")); |
| |
| needClose = false; // Device will handle it |
| mActiveDevices[index].OnMessageReceived(ec, packetHeader, payloadHeader, std::move(msgBuf)); |
| |
| exit: |
| if (needClose) |
| { |
| ec->Close(); |
| } |
| return CHIP_NO_ERROR; |
| } |
| |
| void DeviceController::OnResponseTimeout(Messaging::ExchangeContext * ec) |
| { |
| ChipLogProgress(Controller, "Time out! failed to receive response from Exchange: %p", ec); |
| } |
| |
| void DeviceController::OnNewConnection(SecureSessionHandle session, Messaging::ExchangeManager * mgr) |
| { |
| VerifyOrReturn(mState == State::Initialized, ChipLogError(Controller, "OnNewConnection was called in incorrect state")); |
| |
| uint16_t index = FindDeviceIndex(mgr->GetSessionMgr()->GetPeerConnectionState(session)->GetPeerNodeId()); |
| VerifyOrReturn(index < kNumMaxActiveDevices, |
| ChipLogDetail(Controller, "OnNewConnection was called for unknown device, ignoring it.")); |
| |
| mActiveDevices[index].OnNewConnection(session); |
| } |
| |
| void DeviceController::OnConnectionExpired(SecureSessionHandle session, Messaging::ExchangeManager * mgr) |
| { |
| VerifyOrReturn(mState == State::Initialized, ChipLogError(Controller, "OnConnectionExpired was called in incorrect state")); |
| |
| uint16_t index = FindDeviceIndex(session); |
| VerifyOrReturn(index < kNumMaxActiveDevices, |
| ChipLogDetail(Controller, "OnConnectionExpired was called for unknown device, ignoring it.")); |
| |
| mActiveDevices[index].OnConnectionExpired(session); |
| } |
| |
| uint16_t DeviceController::GetInactiveDeviceIndex() |
| { |
| uint16_t i = 0; |
| while (i < kNumMaxActiveDevices && mActiveDevices[i].IsActive()) |
| i++; |
| if (i < kNumMaxActiveDevices) |
| { |
| mActiveDevices[i].SetActive(true); |
| } |
| |
| return i; |
| } |
| |
| void DeviceController::ReleaseDevice(Device * device) |
| { |
| device->Reset(); |
| } |
| |
| void DeviceController::ReleaseDevice(uint16_t index) |
| { |
| if (index < kNumMaxActiveDevices) |
| { |
| ReleaseDevice(&mActiveDevices[index]); |
| } |
| } |
| |
| void DeviceController::ReleaseDeviceById(NodeId remoteDeviceId) |
| { |
| for (uint16_t i = 0; i < kNumMaxActiveDevices; i++) |
| { |
| if (mActiveDevices[i].GetDeviceId() == remoteDeviceId) |
| { |
| ReleaseDevice(&mActiveDevices[i]); |
| } |
| } |
| } |
| |
| void DeviceController::ReleaseAllDevices() |
| { |
| for (uint16_t i = 0; i < kNumMaxActiveDevices; i++) |
| { |
| ReleaseDevice(&mActiveDevices[i]); |
| } |
| } |
| |
| uint16_t DeviceController::FindDeviceIndex(SecureSessionHandle session) |
| { |
| uint16_t i = 0; |
| while (i < kNumMaxActiveDevices) |
| { |
| if (mActiveDevices[i].IsActive() && mActiveDevices[i].IsSecureConnected() && mActiveDevices[i].MatchesSession(session)) |
| { |
| return i; |
| } |
| i++; |
| } |
| return i; |
| } |
| |
| uint16_t DeviceController::FindDeviceIndex(NodeId id) |
| { |
| uint16_t i = 0; |
| while (i < kNumMaxActiveDevices) |
| { |
| if (mActiveDevices[i].IsActive() && mActiveDevices[i].GetDeviceId() == id) |
| { |
| return i; |
| } |
| i++; |
| } |
| return i; |
| } |
| |
| CHIP_ERROR DeviceController::InitializePairedDeviceList() |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| uint8_t * buffer = nullptr; |
| |
| VerifyOrExit(mStorageDelegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| if (!mPairedDevicesInitialized) |
| { |
| constexpr uint16_t max_size = sizeof(uint64_t) * kNumMaxPairedDevices; |
| buffer = static_cast<uint8_t *>(chip::Platform::MemoryCalloc(max_size, 1)); |
| uint16_t size = max_size; |
| |
| VerifyOrExit(buffer != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| CHIP_ERROR lookupError = CHIP_NO_ERROR; |
| PERSISTENT_KEY_OP(static_cast<uint64_t>(0), kPairedDeviceListKeyPrefix, key, |
| lookupError = mStorageDelegate->SyncGetKeyValue(key, buffer, size)); |
| |
| // It's ok to not have an entry for the Paired Device list. We treat it the same as having an empty list. |
| if (lookupError != CHIP_ERROR_KEY_NOT_FOUND) |
| { |
| VerifyOrExit(size <= max_size, err = CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR); |
| err = SetPairedDeviceList(ByteSpan(buffer, size)); |
| SuccessOrExit(err); |
| } |
| } |
| |
| exit: |
| if (buffer != nullptr) |
| { |
| chip::Platform::MemoryFree(buffer); |
| } |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to initialize the device list with error: %" PRId32, err); |
| } |
| |
| return err; |
| } |
| |
| CHIP_ERROR DeviceController::SetPairedDeviceList(ByteSpan serialized) |
| { |
| CHIP_ERROR err = mPairedDevices.Deserialize(serialized); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to recreate the device list with buffer %.*s\n", static_cast<int>(serialized.size()), |
| serialized.data()); |
| } |
| else |
| { |
| mPairedDevicesInitialized = true; |
| } |
| |
| return err; |
| } |
| |
| void DeviceController::PersistNextKeyId() |
| { |
| if (mStorageDelegate != nullptr && mState == State::Initialized) |
| { |
| uint16_t nextKeyID = mIDAllocator.Peek(); |
| mStorageDelegate->SyncSetKeyValue(kNextAvailableKeyID, &nextKeyID, sizeof(nextKeyID)); |
| } |
| } |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| void DeviceController::OnNodeIdResolved(const chip::Mdns::ResolvedNodeData & nodeData) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| Device * device = nullptr; |
| |
| err = GetDevice(nodeData.mPeerId.GetNodeId(), &device); |
| SuccessOrExit(err); |
| |
| err = device->UpdateAddress(Transport::PeerAddress::UDP(nodeData.mAddress, nodeData.mPort, nodeData.mInterfaceId)); |
| SuccessOrExit(err); |
| |
| PersistDevice(device); |
| |
| exit: |
| |
| if (mDeviceAddressUpdateDelegate != nullptr) |
| { |
| mDeviceAddressUpdateDelegate->OnAddressUpdateComplete(nodeData.mPeerId.GetNodeId(), err); |
| } |
| return; |
| }; |
| |
| void DeviceController::OnNodeIdResolutionFailed(const chip::PeerId & peer, CHIP_ERROR error) |
| { |
| ChipLogError(Controller, "Error resolving node id: %s", ErrorStr(error)); |
| |
| if (mDeviceAddressUpdateDelegate != nullptr) |
| { |
| mDeviceAddressUpdateDelegate->OnAddressUpdateComplete(peer.GetNodeId(), error); |
| } |
| }; |
| |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| |
| ControllerDeviceInitParams DeviceController::GetControllerDeviceInitParams() |
| { |
| return ControllerDeviceInitParams{ |
| .transportMgr = mTransportMgr, |
| .sessionMgr = mSessionMgr, |
| .exchangeMgr = mExchangeMgr, |
| .inetLayer = mInetLayer, |
| .storageDelegate = mStorageDelegate, |
| .credentials = &mCredentials, |
| .idAllocator = &mIDAllocator, |
| }; |
| } |
| |
| DeviceCommissioner::DeviceCommissioner() : |
| mSuccess(BasicSuccess, this), mFailure(BasicFailure, this), |
| mOpCSRResponseCallback(OnOperationalCertificateSigningRequest, this), |
| mOpCertResponseCallback(OnOperationalCertificateAddResponse, this), mRootCertResponseCallback(OnRootCertSuccessResponse, this), |
| mOnCSRFailureCallback(OnCSRFailureResponse, this), mOnCertFailureCallback(OnAddOpCertFailureResponse, this), |
| mOnRootCertFailureCallback(OnRootCertFailureResponse, this), mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), |
| mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this) |
| { |
| mPairingDelegate = nullptr; |
| mDeviceBeingPaired = kNumMaxActiveDevices; |
| mPairedDevicesUpdated = false; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::Init(NodeId localDeviceId, CommissionerInitParams params) |
| { |
| ReturnErrorOnFailure(DeviceController::Init(localDeviceId, params)); |
| |
| uint16_t nextKeyID = 0; |
| uint16_t size = sizeof(nextKeyID); |
| CHIP_ERROR error = mStorageDelegate->SyncGetKeyValue(kNextAvailableKeyID, &nextKeyID, size); |
| if (error || (size != sizeof(nextKeyID))) |
| { |
| nextKeyID = 0; |
| } |
| ReturnErrorOnFailure(mIDAllocator.ReserveUpTo(nextKeyID)); |
| mPairingDelegate = params.pairingDelegate; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::Shutdown() |
| { |
| VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); |
| |
| ChipLogDetail(Controller, "Shutting down the commissioner"); |
| |
| mPairingSession.Clear(); |
| |
| PersistDeviceList(); |
| |
| DeviceController::Shutdown(); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, RendezvousParameters & params) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| Device * device = nullptr; |
| Transport::PeerAddress peerAddress = Transport::PeerAddress::UDP(Inet::IPAddress::Any); |
| |
| Messaging::ExchangeContext * exchangeCtxt = nullptr; |
| |
| uint16_t keyID = 0; |
| |
| Transport::AdminPairingInfo * admin = mAdmins.FindAdminWithId(mAdminId); |
| |
| VerifyOrExit(remoteDeviceId != kAnyNodeId && remoteDeviceId != kUndefinedNodeId, err = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(mState == State::Initialized, err = CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrExit(mDeviceBeingPaired == kNumMaxActiveDevices, err = CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrExit(admin != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| err = InitializePairedDeviceList(); |
| SuccessOrExit(err); |
| |
| params.SetAdvertisementDelegate(&mRendezvousAdvDelegate); |
| |
| // TODO: We need to specify the peer address for BLE transport in bindings. |
| if (params.GetPeerAddress().GetTransportType() == Transport::Type::kBle || |
| params.GetPeerAddress().GetTransportType() == Transport::Type::kUndefined) |
| { |
| #if CONFIG_NETWORK_LAYER_BLE |
| if (!params.HasBleLayer()) |
| { |
| params.SetPeerAddress(Transport::PeerAddress::BLE()); |
| } |
| peerAddress = Transport::PeerAddress::BLE(); |
| #endif // CONFIG_NETWORK_LAYER_BLE |
| } |
| else if (params.GetPeerAddress().GetTransportType() == Transport::Type::kTcp || |
| params.GetPeerAddress().GetTransportType() == Transport::Type::kUdp) |
| { |
| peerAddress = Transport::PeerAddress::UDP(params.GetPeerAddress().GetIPAddress(), params.GetPeerAddress().GetPort(), |
| params.GetPeerAddress().GetInterface()); |
| } |
| |
| mDeviceBeingPaired = GetInactiveDeviceIndex(); |
| VerifyOrExit(mDeviceBeingPaired < kNumMaxActiveDevices, err = CHIP_ERROR_NO_MEMORY); |
| device = &mActiveDevices[mDeviceBeingPaired]; |
| |
| mIsIPRendezvous = (params.GetPeerAddress().GetTransportType() != Transport::Type::kBle); |
| |
| err = mPairingSession.MessageDispatch().Init(mTransportMgr); |
| SuccessOrExit(err); |
| mPairingSession.MessageDispatch().SetPeerAddress(params.GetPeerAddress()); |
| |
| device->Init(GetControllerDeviceInitParams(), mListenPort, remoteDeviceId, peerAddress, admin->GetAdminId()); |
| |
| mSystemLayer->StartTimer(kSessionEstablishmentTimeout, OnSessionEstablishmentTimeoutCallback, this); |
| if (params.GetPeerAddress().GetTransportType() != Transport::Type::kBle) |
| { |
| device->SetAddress(params.GetPeerAddress().GetIPAddress()); |
| } |
| #if CONFIG_NETWORK_LAYER_BLE |
| else |
| { |
| if (params.HasConnectionObject()) |
| { |
| SuccessOrExit(err = mBleLayer->NewBleConnectionByObject(params.GetConnectionObject())); |
| } |
| else if (params.HasDiscriminator()) |
| { |
| SuccessOrExit(err = mBleLayer->NewBleConnectionByDiscriminator(params.GetDiscriminator())); |
| } |
| else |
| { |
| ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| } |
| #endif |
| exchangeCtxt = mExchangeMgr->NewContext(SecureSessionHandle(), &mPairingSession); |
| VerifyOrExit(exchangeCtxt != nullptr, err = CHIP_ERROR_INTERNAL); |
| |
| 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(); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| // Delete the current rendezvous session only if a device is not currently being paired. |
| if (mDeviceBeingPaired == kNumMaxActiveDevices) |
| { |
| FreeRendezvousSession(); |
| } |
| |
| if (device != nullptr) |
| { |
| ReleaseDevice(device); |
| mDeviceBeingPaired = kNumMaxActiveDevices; |
| } |
| } |
| |
| return err; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::PairTestDeviceWithoutSecurity(NodeId remoteDeviceId, const Transport::PeerAddress & peerAddress, |
| SerializedDevice & serialized) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| Device * device = nullptr; |
| |
| SecurePairingUsingTestSecret * testSecurePairingSecret = nullptr; |
| |
| // Check that the caller has provided an IP address (instead of a BLE peer address) |
| VerifyOrExit(peerAddress.GetTransportType() == Transport::Type::kUdp, err = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(remoteDeviceId != kUndefinedNodeId && remoteDeviceId != kAnyNodeId, err = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| VerifyOrExit(mState == State::Initialized, err = CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrExit(mDeviceBeingPaired == kNumMaxActiveDevices, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| testSecurePairingSecret = chip::Platform::New<SecurePairingUsingTestSecret>(); |
| VerifyOrExit(testSecurePairingSecret != nullptr, err = CHIP_ERROR_NO_MEMORY); |
| |
| mDeviceBeingPaired = GetInactiveDeviceIndex(); |
| VerifyOrExit(mDeviceBeingPaired < kNumMaxActiveDevices, err = CHIP_ERROR_NO_MEMORY); |
| device = &mActiveDevices[mDeviceBeingPaired]; |
| |
| testSecurePairingSecret->ToSerializable(device->GetPairing()); |
| |
| device->Init(GetControllerDeviceInitParams(), mListenPort, remoteDeviceId, peerAddress, mAdminId); |
| |
| device->Serialize(serialized); |
| |
| err = mSessionMgr->NewPairing(Optional<Transport::PeerAddress>::Value(peerAddress), device->GetDeviceId(), |
| testSecurePairingSecret, SecureSession::SessionRole::kInitiator, mAdminId, nullptr); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed in setting up secure channel: err %s", ErrorStr(err)); |
| OnSessionEstablishmentError(err); |
| } |
| SuccessOrExit(err); |
| |
| mPairedDevices.Insert(device->GetDeviceId()); |
| mPairedDevicesUpdated = true; |
| |
| // Note - This assumes storage is synchronous, the device must be in storage before we can cleanup |
| // the rendezvous session and mark pairing success |
| PersistDevice(device); |
| // Also persist the device list at this time |
| // This makes sure that a newly added device is immediately available |
| PersistDeviceList(); |
| |
| if (mPairingDelegate != nullptr) |
| { |
| mPairingDelegate->OnStatusUpdate(DevicePairingDelegate::SecurePairingSuccess); |
| } |
| |
| RendezvousCleanup(CHIP_NO_ERROR); |
| |
| exit: |
| if (testSecurePairingSecret != nullptr) |
| { |
| chip::Platform::Delete(testSecurePairingSecret); |
| } |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| if (device != nullptr) |
| { |
| ReleaseDevice(device); |
| mDeviceBeingPaired = kNumMaxActiveDevices; |
| } |
| } |
| |
| return err; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::StopPairing(NodeId remoteDeviceId) |
| { |
| VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mDeviceBeingPaired < kNumMaxActiveDevices, CHIP_ERROR_INCORRECT_STATE); |
| |
| Device * device = &mActiveDevices[mDeviceBeingPaired]; |
| VerifyOrReturnError(device->GetDeviceId() == remoteDeviceId, CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR); |
| |
| FreeRendezvousSession(); |
| |
| ReleaseDevice(device); |
| mDeviceBeingPaired = kNumMaxActiveDevices; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::UnpairDevice(NodeId remoteDeviceId) |
| { |
| // TODO: Send unpairing message to the remote device. |
| |
| VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); |
| |
| if (mDeviceBeingPaired < kNumMaxActiveDevices) |
| { |
| Device * device = &mActiveDevices[mDeviceBeingPaired]; |
| if (device->GetDeviceId() == remoteDeviceId) |
| { |
| FreeRendezvousSession(); |
| } |
| } |
| |
| if (mStorageDelegate != nullptr) |
| { |
| PERSISTENT_KEY_OP(remoteDeviceId, kPairedDeviceKeyPrefix, key, mStorageDelegate->SyncDeleteKeyValue(key)); |
| } |
| |
| mPairedDevices.Remove(remoteDeviceId); |
| mPairedDevicesUpdated = true; |
| ReleaseDeviceById(remoteDeviceId); |
| |
| 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(); |
| } |
| |
| void DeviceCommissioner::RendezvousCleanup(CHIP_ERROR status) |
| { |
| mRendezvousAdvDelegate.StopAdvertisement(); |
| mRendezvousAdvDelegate.RendezvousComplete(); |
| |
| FreeRendezvousSession(); |
| |
| // TODO: make mStorageDelegate mandatory once all controller applications implement the interface. |
| if (mDeviceBeingPaired != kNumMaxActiveDevices && mStorageDelegate != nullptr) |
| { |
| // Let's release the device that's being paired. |
| // If pairing was successful, its information is |
| // already persisted. The application will use GetDevice() |
| // method to get access to the device, which will fetch |
| // the device information from the persistent storage. |
| DeviceController::ReleaseDevice(mDeviceBeingPaired); |
| } |
| |
| mDeviceBeingPaired = kNumMaxActiveDevices; |
| |
| if (mPairingDelegate != nullptr) |
| { |
| mPairingDelegate->OnPairingComplete(status); |
| } |
| } |
| |
| void DeviceCommissioner::OnSessionEstablishmentError(CHIP_ERROR err) |
| { |
| mSystemLayer->CancelTimer(OnSessionEstablishmentTimeoutCallback, this); |
| |
| if (mPairingDelegate != nullptr) |
| { |
| mPairingDelegate->OnStatusUpdate(DevicePairingDelegate::SecurePairingFailed); |
| } |
| |
| RendezvousCleanup(err); |
| } |
| |
| void DeviceCommissioner::OnSessionEstablished() |
| { |
| VerifyOrReturn(mDeviceBeingPaired < kNumMaxActiveDevices, OnSessionEstablishmentError(CHIP_ERROR_INVALID_DEVICE_DESCRIPTOR)); |
| |
| Device * device = &mActiveDevices[mDeviceBeingPaired]; |
| |
| mPairingSession.PeerConnection().SetPeerNodeId(device->GetDeviceId()); |
| |
| CHIP_ERROR err = |
| mSessionMgr->NewPairing(Optional<Transport::PeerAddress>::Value(mPairingSession.PeerConnection().GetPeerAddress()), |
| mPairingSession.PeerConnection().GetPeerNodeId(), &mPairingSession, |
| SecureSession::SessionRole::kInitiator, mAdminId, nullptr); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed in setting up secure channel: err %s", ErrorStr(err)); |
| OnSessionEstablishmentError(err); |
| return; |
| } |
| |
| 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. |
| #if CONFIG_USE_CLUSTERS_FOR_IP_COMMISSIONING |
| bool sendOperationalCertsImmediately = !mIsIPRendezvous; |
| #else |
| bool sendOperationalCertsImmediately = true; |
| #endif |
| |
| if (sendOperationalCertsImmediately) |
| { |
| err = SendTrustedRootCertificate(device); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Ble, "Failed in sending 'add trusted root' command to the device: err %s", ErrorStr(err)); |
| OnSessionEstablishmentError(err); |
| return; |
| } |
| } |
| else |
| { |
| AdvanceCommissioningStage(CHIP_NO_ERROR); |
| } |
| } |
| |
| CHIP_ERROR DeviceCommissioner::SendOperationalCertificateSigningRequestCommand(Device * device) |
| { |
| ChipLogDetail(Controller, "Sending OpCSR request to %p device", device); |
| VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| chip::Controller::OperationalCredentialsCluster cluster; |
| cluster.Associate(device, 0); |
| |
| Callback::Cancelable * successCallback = mOpCSRResponseCallback.Cancel(); |
| Callback::Cancelable * failureCallback = mOnCSRFailureCallback.Cancel(); |
| |
| ReturnErrorOnFailure(device->GenerateCSRNonce()); |
| ReturnErrorOnFailure(cluster.OpCSRRequest(successCallback, failureCallback, device->GetCSRNonce())); |
| ChipLogDetail(Controller, "Sent OpCSR request, waiting for the CSR"); |
| return CHIP_NO_ERROR; |
| } |
| |
| void DeviceCommissioner::OnCSRFailureResponse(void * context, uint8_t status) |
| { |
| ChipLogProgress(Controller, "Device failed to receive the CSR request Response: 0x%02x", status); |
| DeviceCommissioner * commissioner = reinterpret_cast<DeviceCommissioner *>(context); |
| commissioner->mOpCSRResponseCallback.Cancel(); |
| commissioner->mOnCSRFailureCallback.Cancel(); |
| // TODO: Map error status to correct error code |
| commissioner->OnSessionEstablishmentError(CHIP_ERROR_INTERNAL); |
| } |
| |
| void DeviceCommissioner::OnOperationalCertificateSigningRequest(void * context, ByteSpan CSR, ByteSpan CSRNonce, |
| ByteSpan VendorReserved1, ByteSpan VendorReserved2, |
| ByteSpan VendorReserved3, ByteSpan Signature) |
| { |
| ChipLogProgress(Controller, "Received certificate signing request from the device"); |
| DeviceCommissioner * commissioner = reinterpret_cast<DeviceCommissioner *>(context); |
| |
| commissioner->mOpCSRResponseCallback.Cancel(); |
| commissioner->mOnCSRFailureCallback.Cancel(); |
| |
| if (commissioner->ProcessOpCSR(CSR, CSRNonce, VendorReserved1, VendorReserved2, VendorReserved3, Signature) != CHIP_NO_ERROR) |
| { |
| // Handle error, and notify session failure to the commissioner application. |
| ChipLogError(Controller, "Failed to process the certificate signing request"); |
| // TODO: Map error status to correct error code |
| commissioner->OnSessionEstablishmentError(CHIP_ERROR_INTERNAL); |
| } |
| } |
| |
| CHIP_ERROR DeviceCommissioner::ProcessOpCSR(const ByteSpan & CSR, const ByteSpan & CSRNonce, const ByteSpan & VendorReserved1, |
| const ByteSpan & VendorReserved2, const ByteSpan & VendorReserved3, |
| const ByteSpan & Signature) |
| { |
| VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mDeviceBeingPaired < kNumMaxActiveDevices, CHIP_ERROR_INCORRECT_STATE); |
| |
| Device * device = &mActiveDevices[mDeviceBeingPaired]; |
| |
| // TODO: Verify the OpCSR signature using pubkey from DAC |
| // This will be done when device attestation is implemented. |
| |
| // Verify that Nonce matches with what we sent |
| const ByteSpan nonce = device->GetCSRNonce(); |
| 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> chipOpCert; |
| ReturnErrorCodeIf(!chipOpCert.Alloc(kMaxCHIPCertLength * 2), CHIP_ERROR_NO_MEMORY); |
| |
| 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; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::SendOperationalCertificate(Device * device, const ByteSpan & opCertBuf) |
| { |
| VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| chip::Controller::OperationalCredentialsCluster cluster; |
| cluster.Associate(device, 0); |
| |
| Callback::Cancelable * successCallback = mOpCertResponseCallback.Cancel(); |
| Callback::Cancelable * failureCallback = mOnCertFailureCallback.Cancel(); |
| |
| ReturnErrorOnFailure(cluster.AddOpCert(successCallback, failureCallback, opCertBuf, ByteSpan(nullptr, 0), mLocalDeviceId, 0)); |
| |
| ChipLogProgress(Controller, "Sent operational certificate to the device"); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void DeviceCommissioner::OnAddOpCertFailureResponse(void * context, uint8_t status) |
| { |
| ChipLogProgress(Controller, "Device failed to receive the operational certificate Response: 0x%02x", status); |
| DeviceCommissioner * commissioner = reinterpret_cast<DeviceCommissioner *>(context); |
| commissioner->mOpCSRResponseCallback.Cancel(); |
| commissioner->mOnCertFailureCallback.Cancel(); |
| // TODO: Map error status to correct error code |
| commissioner->OnSessionEstablishmentError(CHIP_ERROR_INTERNAL); |
| } |
| |
| void DeviceCommissioner::OnOperationalCertificateAddResponse(void * context, uint8_t StatusCode, uint64_t FabricIndex, |
| uint8_t * DebugText) |
| { |
| ChipLogProgress(Controller, "Device confirmed that it has received the operational certificate"); |
| DeviceCommissioner * commissioner = reinterpret_cast<DeviceCommissioner *>(context); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| Device * device = nullptr; |
| |
| VerifyOrExit(commissioner->mState == State::Initialized, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| commissioner->mOpCSRResponseCallback.Cancel(); |
| commissioner->mOnCertFailureCallback.Cancel(); |
| |
| VerifyOrExit(commissioner->mDeviceBeingPaired < kNumMaxActiveDevices, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| device = &commissioner->mActiveDevices[commissioner->mDeviceBeingPaired]; |
| |
| err = commissioner->OnOperationalCredentialsProvisioningCompletion(device); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| commissioner->OnSessionEstablishmentError(err); |
| } |
| } |
| |
| CHIP_ERROR DeviceCommissioner::SendTrustedRootCertificate(Device * device) |
| { |
| VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| Transport::AdminPairingInfo * admin = mAdmins.FindAdminWithId(mAdminId); |
| VerifyOrReturnError(admin != nullptr, CHIP_ERROR_INCORRECT_STATE); |
| |
| uint16_t rootCertLen = 0; |
| const uint8_t * rootCert = admin->GetTrustedRoot(rootCertLen); |
| VerifyOrReturnError(rootCert != nullptr, CHIP_ERROR_INCORRECT_STATE); |
| |
| ChipLogProgress(Controller, "Sending root certificate to the device"); |
| |
| chip::Controller::OperationalCredentialsCluster cluster; |
| cluster.Associate(device, 0); |
| |
| Callback::Cancelable * successCallback = mRootCertResponseCallback.Cancel(); |
| Callback::Cancelable * failureCallback = mOnRootCertFailureCallback.Cancel(); |
| |
| ReturnErrorOnFailure(cluster.AddTrustedRootCertificate(successCallback, failureCallback, ByteSpan(rootCert, rootCertLen))); |
| |
| ChipLogProgress(Controller, "Sent root certificate to the device"); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void DeviceCommissioner::OnRootCertSuccessResponse(void * context) |
| { |
| ChipLogProgress(Controller, "Device confirmed that it has received the root certificate"); |
| DeviceCommissioner * commissioner = reinterpret_cast<DeviceCommissioner *>(context); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| Device * device = nullptr; |
| |
| VerifyOrExit(commissioner->mState == State::Initialized, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| commissioner->mRootCertResponseCallback.Cancel(); |
| commissioner->mOnRootCertFailureCallback.Cancel(); |
| |
| VerifyOrExit(commissioner->mDeviceBeingPaired < kNumMaxActiveDevices, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| device = &commissioner->mActiveDevices[commissioner->mDeviceBeingPaired]; |
| |
| err = commissioner->SendOperationalCertificateSigningRequestCommand(device); |
| SuccessOrExit(err); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| commissioner->OnSessionEstablishmentError(err); |
| } |
| } |
| |
| void DeviceCommissioner::OnRootCertFailureResponse(void * context, uint8_t status) |
| { |
| ChipLogProgress(Controller, "Device failed to receive the root certificate Response: 0x%02x", status); |
| DeviceCommissioner * commissioner = reinterpret_cast<DeviceCommissioner *>(context); |
| commissioner->mRootCertResponseCallback.Cancel(); |
| commissioner->mOnRootCertFailureCallback.Cancel(); |
| // TODO: Map error status to correct error code |
| commissioner->OnSessionEstablishmentError(CHIP_ERROR_INTERNAL); |
| } |
| |
| CHIP_ERROR DeviceCommissioner::OnOperationalCredentialsProvisioningCompletion(Device * device) |
| { |
| ChipLogProgress(Controller, "Operational credentials provisioned on device %p", device); |
| VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| #if CONFIG_USE_CLUSTERS_FOR_IP_COMMISSIONING |
| if (mIsIPRendezvous) |
| { |
| AdvanceCommissioningStage(CHIP_NO_ERROR); |
| } |
| else |
| #endif |
| { |
| mPairingSession.ToSerializable(device->GetPairing()); |
| mSystemLayer->CancelTimer(OnSessionEstablishmentTimeoutCallback, this); |
| |
| mPairedDevices.Insert(device->GetDeviceId()); |
| mPairedDevicesUpdated = true; |
| |
| // Note - This assumes storage is synchronous, the device must be in storage before we can cleanup |
| // the rendezvous session and mark pairing success |
| PersistDevice(device); |
| // Also persist the device list at this time |
| // This makes sure that a newly added device is immediately available |
| PersistDeviceList(); |
| if (mPairingDelegate != nullptr) |
| { |
| mPairingDelegate->OnStatusUpdate(DevicePairingDelegate::SecurePairingSuccess); |
| } |
| RendezvousCleanup(CHIP_NO_ERROR); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void DeviceCommissioner::PersistDeviceList() |
| { |
| if (mStorageDelegate != nullptr && mPairedDevicesUpdated && mState == State::Initialized) |
| { |
| mPairedDevices.Serialize([&](ByteSpan data) -> CHIP_ERROR { |
| VerifyOrReturnError(data.size() <= UINT16_MAX, CHIP_ERROR_INVALID_ARGUMENT); |
| PERSISTENT_KEY_OP(static_cast<uint64_t>(0), kPairedDeviceListKeyPrefix, key, |
| mStorageDelegate->SyncSetKeyValue(key, data.data(), static_cast<uint16_t>(data.size()))); |
| mPairedDevicesUpdated = false; |
| return CHIP_NO_ERROR; |
| }); |
| } |
| } |
| |
| void DeviceCommissioner::ReleaseDevice(Device * device) |
| { |
| PersistDeviceList(); |
| DeviceController::ReleaseDevice(device); |
| } |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| CHIP_ERROR DeviceCommissioner::CloseBleConnection() |
| { |
| // It is fine since we can only commission one device at the same time. |
| // We should be able to distinguish different BLE connections if we want |
| // to commission multiple devices at the same time over BLE. |
| return mBleLayer->CloseAllBleConnections(); |
| } |
| #endif |
| |
| void DeviceCommissioner::OnSessionEstablishmentTimeout() |
| { |
| VerifyOrReturn(mState == State::Initialized); |
| VerifyOrReturn(mDeviceBeingPaired < kNumMaxActiveDevices); |
| |
| Device * device = &mActiveDevices[mDeviceBeingPaired]; |
| StopPairing(device->GetDeviceId()); |
| |
| if (mPairingDelegate != nullptr) |
| { |
| mPairingDelegate->OnPairingComplete(CHIP_ERROR_TIMEOUT); |
| } |
| } |
| |
| void DeviceCommissioner::OnSessionEstablishmentTimeoutCallback(System::Layer * aLayer, void * aAppState, System::Error aError) |
| { |
| reinterpret_cast<DeviceCommissioner *>(aAppState)->OnSessionEstablishmentTimeout(); |
| } |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| CHIP_ERROR DeviceCommissioner::DiscoverCommissionableNodes(Mdns::DiscoveryFilter filter) |
| { |
| ReturnErrorOnFailure(SetUpNodeDiscovery()); |
| return chip::Mdns::Resolver::Instance().FindCommissionableNodes(filter); |
| } |
| |
| const Mdns::DiscoveredNodeData * DeviceCommissioner::GetDiscoveredDevice(int idx) |
| { |
| return GetDiscoveredNode(idx); |
| } |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| |
| CHIP_ERROR DeviceControllerInteractionModelDelegate::CommandResponseStatus( |
| const app::CommandSender * apCommandSender, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, |
| const uint32_t aProtocolId, const uint16_t aProtocolCode, chip::EndpointId aEndpointId, const chip::ClusterId aClusterId, |
| chip::CommandId aCommandId, uint8_t aCommandIndex) |
| { |
| // Generally IM has more detailed errors than ember library, here we always use the, the actual handling of the |
| // commands should implement full IMDelegate. |
| // #6308 By implement app side IM delegate, we should be able to accept detailed error codes. |
| // Note: The IMDefaultResponseCallback is a bridge to the old CallbackMgr before IM is landed, so it still accepts EmberAfStatus |
| // instead of IM status code. |
| IMDefaultResponseCallback(apCommandSender, |
| (aProtocolCode == 0 && aGeneralCode == Protocols::SecureChannel::GeneralStatusCode::kSuccess) |
| ? EMBER_ZCL_STATUS_SUCCESS |
| : EMBER_ZCL_STATUS_FAILURE); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceControllerInteractionModelDelegate::CommandResponseProtocolError(const app::CommandSender * apCommandSender, |
| uint8_t aCommandIndex) |
| { |
| // Generally IM has more detailed errors than ember library, here we always use EMBER_ZCL_STATUS_FAILURE before #6308 is landed |
| // and the app can take care of these error codes, the actual handling of the commands should implement full IMDelegate. |
| // #6308: By implement app side IM delegate, we should be able to accept detailed error codes. |
| // Note: The IMDefaultResponseCallback is a bridge to the old CallbackMgr before IM is landed, so it still accepts EmberAfStatus |
| // instead of IM status code. |
| IMDefaultResponseCallback(apCommandSender, EMBER_ZCL_STATUS_FAILURE); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceControllerInteractionModelDelegate::CommandResponseError(const app::CommandSender * apCommandSender, |
| CHIP_ERROR aError) |
| { |
| // Generally IM has more detailed errors than ember library, here we always use EMBER_ZCL_STATUS_FAILURE before #6308 is landed |
| // and the app can take care of these error codes, the actual handling of the commands should implement full IMDelegate. |
| // #6308: By implement app side IM delegate, we should be able to accept detailed error codes. |
| // Note: The IMDefaultResponseCallback is a bridge to the old CallbackMgr before IM is landed, so it still accepts EmberAfStatus |
| // instead of IM status code. |
| IMDefaultResponseCallback(apCommandSender, EMBER_ZCL_STATUS_FAILURE); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceControllerInteractionModelDelegate::CommandResponseProcessed(const app::CommandSender * apCommandSender) |
| { |
| // No thing is needed in this case. The success callback is called in CommandResponseStatus, and failure callback is called in |
| // CommandResponseStatus, CommandResponseProtocolError and CommandResponseError. |
| return CHIP_NO_ERROR; |
| } |
| void BasicSuccess(void * context, uint16_t val) |
| { |
| ChipLogProgress(Controller, "Received success response 0x%x\n", val); |
| DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context); |
| commissioner->AdvanceCommissioningStage(CHIP_NO_ERROR); |
| } |
| |
| void BasicFailure(void * context, uint8_t status) |
| { |
| ChipLogProgress(Controller, "Received failure response %d\n", (int) status); |
| DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context); |
| commissioner->OnSessionEstablishmentError(static_cast<CHIP_ERROR>(status)); |
| } |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| void DeviceCommissioner::OnNodeIdResolved(const chip::Mdns::ResolvedNodeData & nodeData) |
| { |
| if (mDeviceBeingPaired < kNumMaxActiveDevices) |
| { |
| Device * device = &mActiveDevices[mDeviceBeingPaired]; |
| if (device->GetDeviceId() == nodeData.mPeerId.GetNodeId() && mCommissioningStage == CommissioningStage::kFindOperational) |
| { |
| AdvanceCommissioningStage(CHIP_NO_ERROR); |
| } |
| } |
| DeviceController::OnNodeIdResolved(nodeData); |
| OperationalDiscoveryComplete(nodeData.mPeerId.GetNodeId()); |
| } |
| |
| void DeviceCommissioner::OnNodeIdResolutionFailed(const chip::PeerId & peer, CHIP_ERROR error) |
| { |
| if (mDeviceBeingPaired < kNumMaxActiveDevices) |
| { |
| Device * device = &mActiveDevices[mDeviceBeingPaired]; |
| if (device->GetDeviceId() == peer.GetNodeId() && mCommissioningStage == CommissioningStage::kFindOperational) |
| { |
| OnSessionEstablishmentError(error); |
| } |
| } |
| DeviceController::OnNodeIdResolutionFailed(peer, error); |
| } |
| |
| #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) |
| { |
| case CommissioningStage::kSecurePairing: |
| return CommissioningStage::kArmFailsafe; |
| case CommissioningStage::kArmFailsafe: |
| return CommissioningStage::kConfigRegulatory; |
| case CommissioningStage::kConfigRegulatory: |
| return CommissioningStage::kCheckCertificates; |
| case CommissioningStage::kCheckCertificates: |
| return CommissioningStage::kNetworkEnable; // TODO : for softAP, this needs to be network setup |
| case CommissioningStage::kNetworkEnable: |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| return CommissioningStage::kFindOperational; // TODO : once case is working, need to add stages to find and reconnect |
| // here. |
| #else |
| return CommissioningStage::kSendComplete; |
| #endif |
| case CommissioningStage::kFindOperational: |
| return CommissioningStage::kSendComplete; |
| case CommissioningStage::kSendComplete: |
| return CommissioningStage::kCleanup; |
| |
| // Currently unimplemented. |
| case CommissioningStage::kConfigACL: |
| case CommissioningStage::kNetworkSetup: |
| case CommissioningStage::kScanNetworks: |
| return CommissioningStage::kError; |
| // Neither of these have a next stage so return kError; |
| case CommissioningStage::kCleanup: |
| case CommissioningStage::kError: |
| return CommissioningStage::kError; |
| } |
| return CommissioningStage::kError; |
| } |
| |
| void DeviceCommissioner::AdvanceCommissioningStage(CHIP_ERROR err) |
| { |
| // For now, we ignore errors coming in from the device since not all commissioning clusters are implemented on the device |
| // side. |
| CommissioningStage nextStage = GetNextCommissioningStage(); |
| if (nextStage == CommissioningStage::kError) |
| { |
| return; |
| } |
| |
| if (!mIsIPRendezvous) |
| { |
| return; |
| } |
| Device * device = nullptr; |
| if (mDeviceBeingPaired >= kNumMaxActiveDevices) |
| { |
| return; |
| } |
| |
| device = &mActiveDevices[mDeviceBeingPaired]; |
| |
| // TODO(cecille): We probably want something better than this for breadcrumbs. |
| uint64_t breadcrumb = static_cast<uint64_t>(nextStage); |
| |
| // TODO(cecille): This should be customized per command. |
| constexpr uint32_t kCommandTimeoutMs = 3000; |
| |
| switch (nextStage) |
| { |
| case CommissioningStage::kArmFailsafe: { |
| // TODO(cecille): This is NOT the right way to do this - we should consider attaching an im delegate per command or |
| // something. Per exchange context? |
| ChipLogProgress(Controller, "Arming failsafe"); |
| // TODO(cecille): Find a way to enumerate the clusters here. |
| GeneralCommissioningCluster genCom; |
| uint16_t commissioningExpirySeconds = 5; |
| // TODO: should get the endpoint information from the descriptor cluster. |
| genCom.Associate(device, 0); |
| genCom.ArmFailSafe(mSuccess.Cancel(), mFailure.Cancel(), commissioningExpirySeconds, breadcrumb, kCommandTimeoutMs); |
| } |
| break; |
| case CommissioningStage::kConfigRegulatory: { |
| // To set during config phase: |
| // UTC time |
| // time zone |
| // dst offset |
| // Regulatory config |
| // TODO(cecille): Set time as well once the time cluster is implemented |
| // TODO(cecille): Worthwhile to keep this around as part of the class? |
| // TODO(cecille): Where is the country config actually set? |
| ChipLogProgress(Controller, "Setting Regulatory Config"); |
| uint32_t regulatoryLocation = EMBER_ZCL_REGULATORY_LOCATION_TYPE_OUTDOOR; |
| #if CONFIG_DEVICE_LAYER |
| CHIP_ERROR status = DeviceLayer::ConfigurationMgr().GetRegulatoryLocation(regulatoryLocation); |
| #else |
| CHIP_ERROR status = CHIP_ERROR_NOT_IMPLEMENTED; |
| #endif |
| if (status != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Unable to find regulatory location, defaulting to outdoor"); |
| } |
| |
| static constexpr size_t kMaxCountryCodeSize = 3; |
| char countryCodeStr[kMaxCountryCodeSize] = "WW"; |
| size_t actualCountryCodeSize = 2; |
| |
| #if CONFIG_DEVICE_LAYER |
| status = DeviceLayer::ConfigurationMgr().GetCountryCode(countryCodeStr, kMaxCountryCodeSize, actualCountryCodeSize); |
| #else |
| status = CHIP_ERROR_NOT_IMPLEMENTED; |
| #endif |
| if (status != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Unable to find country code, defaulting to WW"); |
| } |
| chip::ByteSpan countryCode(reinterpret_cast<uint8_t *>(countryCodeStr), actualCountryCodeSize); |
| |
| GeneralCommissioningCluster genCom; |
| genCom.Associate(device, 0); |
| genCom.SetRegulatoryConfig(mSuccess.Cancel(), mFailure.Cancel(), static_cast<uint8_t>(regulatoryLocation), countryCode, |
| breadcrumb, kCommandTimeoutMs); |
| } |
| break; |
| case CommissioningStage::kCheckCertificates: { |
| ChipLogProgress(Controller, "Exchanging certificates"); |
| // TODO(cecille): Once this is implemented through the clusters, it should be moved to the proper stage and the callback |
| // should advance the commissioning stage |
| CHIP_ERROR status = SendTrustedRootCertificate(device); |
| if (status != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed in sending 'add trusted root' command to the device: err %s", ErrorStr(err)); |
| OnSessionEstablishmentError(err); |
| return; |
| } |
| } |
| break; |
| // TODO: Right now, these stages are not implemented as a separate stage because they are no-ops. |
| // Once these are implemented through the clusters, these should be moved into their separate stages and the callbacks |
| // should advance the commissioning stage. |
| case CommissioningStage::kConfigACL: |
| case CommissioningStage::kNetworkSetup: |
| case CommissioningStage::kScanNetworks: |
| // TODO: Implement |
| break; |
| case CommissioningStage::kNetworkEnable: { |
| ChipLogProgress(Controller, "Enabling Network"); |
| // TODO: For ethernet, we actually need a scan stage to get the ethernet netif name. Right now, default to using a magic |
| // value to enable without checks. |
| NetworkCommissioningCluster netCom; |
| // TODO: should get the endpoint information from the descriptor cluster. |
| netCom.Associate(device, 0); |
| // TODO: Once network credential sending is implemented, attempting to set wifi credential on an ethernet only device |
| // will cause an error to be sent back. At that point, we should scan and we shoud see the proper ethernet network ID |
| // returned in the scan results. For now, we use magic. |
| char magicNetworkEnableCode[] = "ETH0"; |
| netCom.EnableNetwork(mSuccess.Cancel(), mFailure.Cancel(), |
| ByteSpan(reinterpret_cast<uint8_t *>(&magicNetworkEnableCode), sizeof(magicNetworkEnableCode)), |
| breadcrumb, kCommandTimeoutMs); |
| } |
| break; |
| case CommissioningStage::kFindOperational: { |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| ChipLogProgress(Controller, "Finding node on operational network"); |
| Mdns::Resolver::Instance().ResolveNodeId(PeerId().SetFabricId(0).SetNodeId(device->GetDeviceId()), |
| Inet::IPAddressType::kIPAddressType_Any); |
| #endif |
| } |
| break; |
| case CommissioningStage::kSendComplete: { |
| // TODO this is actualy not correct - we must reconnect over CASE to send this command. |
| ChipLogProgress(Controller, "Calling commissioning complete"); |
| GeneralCommissioningCluster genCom; |
| genCom.Associate(device, 0); |
| genCom.CommissioningComplete(mSuccess.Cancel(), mFailure.Cancel()); |
| } |
| break; |
| case CommissioningStage::kCleanup: |
| ChipLogProgress(Controller, "Rendezvous cleanup"); |
| mPairingSession.ToSerializable(device->GetPairing()); |
| mSystemLayer->CancelTimer(OnSessionEstablishmentTimeoutCallback, this); |
| |
| mPairedDevices.Insert(device->GetDeviceId()); |
| mPairedDevicesUpdated = true; |
| |
| // Note - This assumes storage is synchronous, the device must be in storage before we can cleanup |
| // the rendezvous session and mark pairing success |
| PersistDevice(device); |
| // Also persist the device list at this time |
| // This makes sure that a newly added device is immediately available |
| PersistDeviceList(); |
| if (mPairingDelegate != nullptr) |
| { |
| mPairingDelegate->OnStatusUpdate(DevicePairingDelegate::SecurePairingSuccess); |
| } |
| RendezvousCleanup(CHIP_NO_ERROR); |
| break; |
| case CommissioningStage::kSecurePairing: |
| case CommissioningStage::kError: |
| break; |
| } |
| mCommissioningStage = nextStage; |
| } |
| |
| } // namespace Controller |
| } // namespace chip |
| |
| namespace chip { |
| namespace Platform { |
| namespace PersistedStorage { |
| |
| /* |
| * Dummy implementations of PersistedStorage platform methods. These aren't |
| * used in the context of the Device Controller, but are required to satisfy |
| * the linker. |
| */ |
| |
| CHIP_ERROR Read(const char * aKey, uint32_t & aValue) |
| { |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Write(const char * aKey, uint32_t aValue) |
| { |
| return CHIP_NO_ERROR; |
| } |
| |
| } // namespace PersistedStorage |
| } // namespace Platform |
| } // namespace chip |