| /* |
| * |
| * Copyright (c) 2020 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> |
| |
| #if CONFIG_DEVICE_LAYER |
| #include <platform/CHIPDeviceLayer.h> |
| #endif |
| |
| #include <app/InteractionModelEngine.h> |
| #include <app/util/DataModelHandler.h> |
| #include <core/CHIPCore.h> |
| #include <core/CHIPEncoding.h> |
| #include <core/CHIPSafeCasts.h> |
| #include <support/Base64.h> |
| #include <support/CHIPArgParser.hpp> |
| #include <support/CHIPMem.h> |
| #include <support/CodeUtils.h> |
| #include <support/ErrorStr.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 <errno.h> |
| #include <inttypes.h> |
| #include <memory> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <time.h> |
| |
| using namespace chip::Inet; |
| using namespace chip::System; |
| |
| namespace chip { |
| namespace Controller { |
| |
| using namespace chip::Encoding; |
| |
| constexpr const char kPairedDeviceListKeyPrefix[] = "ListPairedDevices"; |
| constexpr const char kPairedDeviceKeyPrefix[] = "PairedDevice"; |
| constexpr const char kNextAvailableKeyID[] = "StartKeyID"; |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| constexpr uint16_t kMdnsPort = 5353; |
| #endif |
| |
| constexpr uint32_t kSessionEstablishmentTimeout = 30 * kMillisecondPerSecond; |
| |
| constexpr uint32_t kMaxCHIPOpCertLength = 600; |
| |
| // This macro generates a key using node ID an key prefix, and performs the given action |
| // on that key. |
| #define PERSISTENT_KEY_OP(node, keyPrefix, key, action) \ |
| do \ |
| { \ |
| constexpr size_t len = std::extent<decltype(keyPrefix)>::value; \ |
| nlSTATIC_ASSERT_PRINT(len > 0, "keyPrefix length must be known at compile time"); \ |
| /* 2 * sizeof(NodeId) to accomodate 2 character for each byte in Node Id */ \ |
| char key[len + 2 * sizeof(NodeId) + 1]; \ |
| nlSTATIC_ASSERT_PRINT(sizeof(node) <= sizeof(uint64_t), "Node ID size is greater than expected"); \ |
| snprintf(key, sizeof(key), "%s%" PRIx64, keyPrefix, node); \ |
| action; \ |
| } while (0) |
| |
| 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) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| Transport::AdminPairingInfo * admin = nullptr; |
| |
| VerifyOrExit(mState == State::NotInitialized, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| if (params.systemLayer != nullptr && params.inetLayer != nullptr) |
| { |
| mSystemLayer = params.systemLayer; |
| mInetLayer = params.inetLayer; |
| } |
| else |
| { |
| #if CONFIG_DEVICE_LAYER |
| err = DeviceLayer::PlatformMgr().InitChipStack(); |
| SuccessOrExit(err); |
| |
| mSystemLayer = &DeviceLayer::SystemLayer; |
| mInetLayer = &DeviceLayer::InetLayer; |
| #endif // CONFIG_DEVICE_LAYER |
| } |
| |
| VerifyOrExit(mSystemLayer != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(mInetLayer != nullptr, err = 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; |
| VerifyOrExit(mBleLayer != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); |
| #endif |
| |
| mTransportMgr = chip::Platform::New<DeviceTransportMgr>(); |
| mSessionMgr = chip::Platform::New<SecureSessionMgr>(); |
| mExchangeMgr = chip::Platform::New<Messaging::ExchangeManager>(); |
| |
| err = 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 |
| ); |
| SuccessOrExit(err); |
| |
| admin = mAdmins.AssignAdminId(mAdminId, localDeviceId); |
| VerifyOrExit(admin != nullptr, err = CHIP_ERROR_NO_MEMORY); |
| |
| err = mSessionMgr->Init(localDeviceId, mSystemLayer, mTransportMgr, &mAdmins); |
| SuccessOrExit(err); |
| |
| err = mExchangeMgr->Init(mSessionMgr); |
| SuccessOrExit(err); |
| |
| err = mExchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::TempZCL::Id, this); |
| SuccessOrExit(err); |
| |
| #if CHIP_ENABLE_INTERACTION_MODEL |
| err = chip::app::InteractionModelEngine::GetInstance()->Init(mExchangeMgr, params.imDelegate); |
| SuccessOrExit(err); |
| #endif |
| |
| mExchangeMgr->SetDelegate(this); |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| if (params.mDeviceAddressUpdateDelegate != nullptr) |
| { |
| err = Mdns::Resolver::Instance().SetResolverDelegate(this); |
| SuccessOrExit(err); |
| |
| mDeviceAddressUpdateDelegate = params.mDeviceAddressUpdateDelegate; |
| } |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| |
| InitDataModelHandler(mExchangeMgr); |
| |
| mState = State::Initialized; |
| mLocalDeviceId = localDeviceId; |
| |
| ReleaseAllDevices(); |
| |
| exit: |
| return err; |
| } |
| |
| CHIP_ERROR DeviceController::Shutdown() |
| { |
| VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); |
| |
| ChipLogDetail(Controller, "Shutting down the controller"); |
| |
| mState = State::NotInitialized; |
| |
| #if CONFIG_DEVICE_LAYER |
| ReturnErrorOnFailure(DeviceLayer::PlatformMgr().Shutdown()); |
| #else |
| mSystemLayer->Shutdown(); |
| mInetLayer->Shutdown(); |
| chip::Platform::Delete(mSystemLayer); |
| chip::Platform::Delete(mInetLayer); |
| #endif // CONFIG_DEVICE_LAYER |
| |
| mSystemLayer = nullptr; |
| mInetLayer = nullptr; |
| mStorageDelegate = 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; |
| } |
| |
| mAdmins.ReleaseAdminId(mAdminId); |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| if (mDeviceAddressUpdateDelegate != nullptr) |
| { |
| Mdns::Resolver::Instance().SetResolverDelegate(nullptr); |
| mDeviceAddressUpdateDelegate = nullptr; |
| } |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| |
| ReleaseAllDevices(); |
| 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, const SerializedDevice & deviceInfo, Device ** out_device) |
| { |
| Device * device = nullptr; |
| |
| VerifyOrReturnError(out_device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| uint16_t index = FindDeviceIndex(deviceId); |
| |
| if (index < kNumMaxActiveDevices) |
| { |
| device = &mActiveDevices[index]; |
| } |
| else |
| { |
| VerifyOrReturnError(mPairedDevices.Contains(deviceId), CHIP_ERROR_NOT_CONNECTED); |
| |
| index = GetInactiveDeviceIndex(); |
| VerifyOrReturnError(index < kNumMaxActiveDevices, CHIP_ERROR_NO_MEMORY); |
| device = &mActiveDevices[index]; |
| |
| CHIP_ERROR err = device->Deserialize(deviceInfo); |
| if (err != CHIP_NO_ERROR) |
| { |
| ReleaseDevice(device); |
| ReturnErrorOnFailure(err); |
| } |
| |
| device->Init(GetControllerDeviceInitParams(), mListenPort, mAdminId); |
| } |
| |
| *out_device = device; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceController::GetDevice(NodeId deviceId, Device ** out_device) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| 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; |
| } |
| |
| CHIP_ERROR DeviceController::UpdateDevice(Device * device, uint64_t fabricId) |
| { |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| ReturnErrorOnFailure(Mdns::Resolver::Instance().StartResolver(mInetLayer, kMdnsPort)); |
| |
| return Mdns::Resolver::Instance().ResolveNodeId(chip::PeerId().SetNodeId(device->GetDeviceId()).SetFabricId(fabricId), |
| chip::Inet::kIPAddressType_Any); |
| #else |
| return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS |
| } |
| |
| void DeviceController::PersistDevice(Device * device) |
| { |
| // mStorageDelegate would not be null for a typical pairing scenario, as Pair() |
| // requires a valid storage delegate. However, test pairing usecase, that's used |
| // mainly by test applications, do not require a storage delegate. This is to |
| // reduce overheads on these tests. |
| // Let's make sure the delegate object is available before calling into it. |
| if (mStorageDelegate != nullptr && mState == State::Initialized) |
| { |
| SerializedDevice serialized; |
| device->Serialize(serialized); |
| |
| // TODO: no need to base-64 the serialized values AGAIN |
| PERSISTENT_KEY_OP(device->GetDeviceId(), kPairedDeviceKeyPrefix, key, |
| mStorageDelegate->SyncSetKeyValue(key, serialized.inner, sizeof(serialized.inner))); |
| } |
| } |
| |
| 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_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK) |
| DeviceLayer::SystemLayer.WakeSelect(); |
| #else |
| ReturnErrorOnFailure(CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); |
| #endif // CONFIG_DEVICE_LAYER && (CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK) |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void 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(); |
| } |
| } |
| |
| 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; |
| char * buffer = nullptr; |
| |
| VerifyOrExit(mStorageDelegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| if (!mPairedDevicesInitialized) |
| { |
| constexpr uint16_t max_size = CHIP_MAX_SERIALIZED_SIZE_U64(kNumMaxPairedDevices); |
| buffer = static_cast<char *>(chip::Platform::MemoryAlloc(max_size)); |
| 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(buffer); |
| 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: %d\n", err); |
| } |
| |
| return err; |
| } |
| |
| CHIP_ERROR DeviceController::SetPairedDeviceList(const char * serialized) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| size_t len = strlen(serialized) + 1; |
| uint16_t lenU16 = static_cast<uint16_t>(len); |
| VerifyOrExit(CanCastTo<uint16_t>(len), err = CHIP_ERROR_INVALID_ARGUMENT); |
| err = mPairedDevices.DeserializeBase64(serialized, lenU16); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to recreate the device list with buffer %s\n", serialized); |
| } |
| else |
| { |
| mPairedDevicesInitialized = true; |
| } |
| |
| return err; |
| } |
| |
| #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 |
| }; |
| } |
| |
| DeviceCommissioner::DeviceCommissioner() |
| { |
| mPairingDelegate = nullptr; |
| mDeviceBeingPaired = kNumMaxActiveDevices; |
| mPairedDevicesUpdated = false; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::Init(NodeId localDeviceId, CommissionerInitParams params) |
| { |
| ReturnErrorOnFailure(DeviceController::Init(localDeviceId, params)); |
| |
| uint16_t size = sizeof(mNextKeyId); |
| CHIP_ERROR error = mStorageDelegate->SyncGetKeyValue(kNextAvailableKeyID, &mNextKeyId, size); |
| if (error || (size != sizeof(mNextKeyId))) |
| { |
| mNextKeyId = 0; |
| } |
| |
| mPairingDelegate = params.pairingDelegate; |
| |
| mOperationalCredentialsDelegate = params.operationalCredentialsDelegate; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::Shutdown() |
| { |
| VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); |
| |
| ChipLogDetail(Controller, "Shutting down the commissioner"); |
| |
| 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; |
| |
| 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_DEVICE_LAYER && CONFIG_NETWORK_LAYER_BLE |
| if (!params.HasBleLayer()) |
| { |
| params.SetPeerAddress(Transport::PeerAddress::BLE()); |
| } |
| peerAddress = Transport::PeerAddress::BLE(); |
| #endif // CONFIG_DEVICE_LAYER && 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 = mPairingSession.Pair(params.GetPeerAddress(), params.GetSetupPINCode(), mNextKeyId++, 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; |
| |
| 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); |
| |
| OnSessionEstablished(); |
| |
| 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; |
| } |
| |
| 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(Ble, "Failed in setting up secure channel: err %s", ErrorStr(err)); |
| OnSessionEstablishmentError(err); |
| return; |
| } |
| |
| ChipLogDetail(Controller, "Remote device completed SPAKE2+ handshake\n"); |
| |
| // TODO: Add code to receive OpCSR from the device, and process the signing request |
| err = SendOperationalCertificateSigningRequestCommand(device->GetDeviceId()); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Ble, "Failed in sending opcsr request command to the device: err %s", ErrorStr(err)); |
| OnSessionEstablishmentError(err); |
| return; |
| } |
| |
| 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); |
| } |
| |
| CHIP_ERROR DeviceCommissioner::SendOperationalCertificateSigningRequestCommand(NodeId remoteDeviceId) |
| { |
| // TODO: Call OperationalCredentials cluster API to send command |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::OnOperationalCertificateSigningRequest(NodeId node, const ByteSpan & csr) |
| { |
| ReturnErrorCodeIf(mOperationalCredentialsDelegate == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| |
| chip::Platform::ScopedMemoryBuffer<uint8_t> opCert; |
| ReturnErrorCodeIf(!opCert.Alloc(kMaxCHIPOpCertLength), CHIP_ERROR_NO_MEMORY); |
| |
| uint32_t opCertLen = 0; |
| ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNodeOperationalCertificate( |
| PeerId().SetNodeId(node), csr, 0, opCert.Get(), kMaxCHIPOpCertLength, opCertLen)); |
| |
| chip::Platform::ScopedMemoryBuffer<uint8_t> signingCert; |
| ReturnErrorCodeIf(!signingCert.Alloc(kMaxCHIPOpCertLength), CHIP_ERROR_NO_MEMORY); |
| |
| uint32_t signingCertLen = 0; |
| CHIP_ERROR err = |
| mOperationalCredentialsDelegate->GetIntermediateCACertificate(0, signingCert.Get(), kMaxCHIPOpCertLength, signingCertLen); |
| if (err == CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED) |
| { |
| // This implies that the commissioner application uses root CA to sign the operational |
| // certificates, and an intermediate CA is not needed. It's not an error condition, so |
| // let's just send operational certificate and root CA certificate to the device. |
| err = CHIP_NO_ERROR; |
| signingCertLen = 0; |
| } |
| ReturnErrorOnFailure(err); |
| |
| ReturnErrorOnFailure( |
| SendOperationalCertificate(node, ByteSpan(opCert.Get(), opCertLen), ByteSpan(signingCert.Get(), signingCertLen))); |
| |
| ReturnErrorOnFailure( |
| mOperationalCredentialsDelegate->GetRootCACertificate(0, signingCert.Get(), kMaxCHIPOpCertLength, signingCertLen)); |
| |
| return SendTrustedRootCertificate(node, ByteSpan(signingCert.Get(), signingCertLen)); |
| } |
| |
| CHIP_ERROR DeviceCommissioner::SendOperationalCertificate(NodeId remoteDeviceId, const ByteSpan & opCertBuf, |
| const ByteSpan & icaCertBuf) |
| { |
| // TODO: Call OperationalCredentials cluster API to add operational credentials on the device |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DeviceCommissioner::SendTrustedRootCertificate(NodeId remoteDeviceId, const ByteSpan & certBuf) |
| { |
| // TODO: Call TrustedRootCertificate cluster API to add root certificate on the device |
| return CHIP_NO_ERROR; |
| } |
| |
| void DeviceCommissioner::PersistDeviceList() |
| { |
| if (mStorageDelegate != nullptr && mPairedDevicesUpdated && mState == State::Initialized) |
| { |
| constexpr uint16_t size = CHIP_MAX_SERIALIZED_SIZE_U64(kNumMaxPairedDevices); |
| char * serialized = static_cast<char *>(chip::Platform::MemoryAlloc(size)); |
| uint16_t requiredSize = size; |
| if (serialized != nullptr) |
| { |
| const char * value = mPairedDevices.SerializeBase64(serialized, requiredSize); |
| if (value != nullptr && requiredSize <= size) |
| { |
| // TODO: no need to base64 again the value |
| PERSISTENT_KEY_OP(static_cast<uint64_t>(0), kPairedDeviceListKeyPrefix, key, |
| mStorageDelegate->SyncSetKeyValue(key, value, requiredSize)); |
| mPairedDevicesUpdated = false; |
| } |
| chip::Platform::MemoryFree(serialized); |
| } |
| } |
| } |
| |
| void DeviceCommissioner::PersistNextKeyId() |
| { |
| if (mStorageDelegate != nullptr && mState == State::Initialized) |
| { |
| mStorageDelegate->SyncSetKeyValue(kNextAvailableKeyID, &mNextKeyId, sizeof(mNextKeyId)); |
| } |
| } |
| |
| 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(); |
| } |
| |
| } // namespace Controller |
| } // namespace chip |