| /* |
| * |
| * Copyright (c) 2020-2021 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file contains implementation of Device class. The objects of this |
| * class will be used by Controller applications to interact with CHIP |
| * devices. The class provides mechanism to construct, send and receive |
| * messages to and from the corresponding CHIP devices. |
| */ |
| |
| #include <controller/CHIPDevice.h> |
| |
| #include <controller-clusters/zap-generated/CHIPClusters.h> |
| |
| #if CONFIG_DEVICE_LAYER |
| #include <platform/CHIPDeviceLayer.h> |
| #endif |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| #include <lwip/tcp.h> |
| #include <lwip/tcpip.h> |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| #include <app/CommandSender.h> |
| #include <app/ReadPrepareParams.h> |
| #include <app/util/DataModelHandler.h> |
| #include <lib/core/CHIPCore.h> |
| #include <lib/core/CHIPEncoding.h> |
| #include <lib/core/CHIPSafeCasts.h> |
| #include <lib/support/Base64.h> |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/ErrorStr.h> |
| #include <lib/support/PersistentStorageMacros.h> |
| #include <lib/support/SafeInt.h> |
| #include <lib/support/TypeTraits.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <protocols/Protocols.h> |
| #include <system/TLVPacketBufferBackingStore.h> |
| #include <transport/MessageCounter.h> |
| #include <transport/PeerMessageCounter.h> |
| |
| using namespace chip::Inet; |
| using namespace chip::System; |
| using namespace chip::Callback; |
| |
| namespace chip { |
| namespace Controller { |
| CHIP_ERROR Device::LoadSecureSessionParametersIfNeeded(bool & didLoad) |
| { |
| didLoad = false; |
| |
| // If there is no secure connection to the device, try establishing it |
| if (mState != ConnectionState::SecureConnected) |
| { |
| ReturnErrorOnFailure(LoadSecureSessionParameters()); |
| didLoad = true; |
| } |
| else |
| { |
| if (mSecureSession.HasValue()) |
| { |
| Transport::SecureSession * secureSession = mSessionManager->GetSecureSession(mSecureSession.Value()); |
| // Check if the connection state has the correct transport information |
| if (secureSession->GetPeerAddress().GetTransportType() == Transport::Type::kUndefined) |
| { |
| mState = ConnectionState::NotConnected; |
| ReturnErrorOnFailure(LoadSecureSessionParameters()); |
| didLoad = true; |
| } |
| } |
| else |
| { |
| mState = ConnectionState::NotConnected; |
| ReturnErrorOnFailure(LoadSecureSessionParameters()); |
| didLoad = true; |
| } |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::SendCommands(app::CommandSender * commandObj) |
| { |
| bool loadedSecureSession = false; |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession)); |
| VerifyOrReturnError(commandObj != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| return commandObj->SendCommandRequest(mDeviceId, mFabricIndex, mSecureSession); |
| } |
| |
| CHIP_ERROR Device::Serialize(SerializedDevice & output) |
| { |
| SerializableDevice serializable; |
| |
| static_assert(BASE64_ENCODED_LEN(sizeof(serializable)) <= sizeof(output.inner), |
| "Size of serializable should be <= size of output"); |
| |
| CHIP_ZERO_AT(serializable); |
| CHIP_ZERO_AT(output); |
| |
| serializable.mOpsCreds = mPairing; |
| serializable.mDeviceId = Encoding::LittleEndian::HostSwap64(mDeviceId); |
| serializable.mDevicePort = Encoding::LittleEndian::HostSwap16(mDeviceAddress.GetPort()); |
| serializable.mFabricIndex = Encoding::LittleEndian::HostSwap16(mFabricIndex); |
| |
| // The connection state could be null if the device is moving from PASE connection to CASE connection. |
| // The device parameters (e.g. mDeviceOperationalCertProvisioned) are updated during this transition. |
| // The state during this transistion is being persisted so that the next access of the device will |
| // trigger the CASE based secure session. |
| if (mSecureSession.HasValue()) |
| { |
| Transport::SecureSession * secureSession = mSessionManager->GetSecureSession(mSecureSession.Value()); |
| const uint32_t localMessageCounter = secureSession->GetSessionMessageCounter().GetLocalMessageCounter().Value(); |
| const uint32_t peerMessageCounter = secureSession->GetSessionMessageCounter().GetPeerMessageCounter().GetCounter(); |
| |
| serializable.mLocalMessageCounter = Encoding::LittleEndian::HostSwap32(localMessageCounter); |
| serializable.mPeerMessageCounter = Encoding::LittleEndian::HostSwap32(peerMessageCounter); |
| } |
| else |
| { |
| serializable.mLocalMessageCounter = 0; |
| serializable.mPeerMessageCounter = 0; |
| } |
| |
| serializable.mDeviceOperationalCertProvisioned = (mDeviceOperationalCertProvisioned) ? 1 : 0; |
| |
| serializable.mDeviceTransport = to_underlying(mDeviceAddress.GetTransportType()); |
| |
| ReturnErrorOnFailure(Inet::GetInterfaceName(mDeviceAddress.GetInterface(), Uint8::to_char(serializable.mInterfaceName), |
| sizeof(serializable.mInterfaceName))); |
| static_assert(sizeof(serializable.mDeviceAddr) <= INET6_ADDRSTRLEN, "Size of device address must fit within INET6_ADDRSTRLEN"); |
| mDeviceAddress.GetIPAddress().ToString(Uint8::to_char(serializable.mDeviceAddr), sizeof(serializable.mDeviceAddr)); |
| |
| const uint16_t serializedLen = chip::Base64Encode(Uint8::to_const_uchar(reinterpret_cast<uint8_t *>(&serializable)), |
| static_cast<uint16_t>(sizeof(serializable)), Uint8::to_char(output.inner)); |
| VerifyOrReturnError(serializedLen > 0, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(serializedLen < sizeof(output.inner), CHIP_ERROR_INVALID_ARGUMENT); |
| output.inner[serializedLen] = '\0'; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::Deserialize(const SerializedDevice & input) |
| { |
| SerializableDevice serializable; |
| constexpr size_t maxlen = BASE64_ENCODED_LEN(sizeof(serializable)); |
| const size_t len = strnlen(Uint8::to_const_char(&input.inner[0]), maxlen); |
| |
| VerifyOrReturnError(len < sizeof(SerializedDevice), CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(CanCastTo<uint16_t>(len), CHIP_ERROR_INVALID_ARGUMENT); |
| |
| CHIP_ZERO_AT(serializable); |
| const uint16_t deserializedLen = Base64Decode(Uint8::to_const_char(input.inner), static_cast<uint16_t>(len), |
| Uint8::to_uchar(reinterpret_cast<uint8_t *>(&serializable))); |
| |
| VerifyOrReturnError(deserializedLen > 0, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(deserializedLen <= sizeof(serializable), CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // The second parameter to FromString takes the strlen value. We are subtracting 1 |
| // from the sizeof(serializable.mDeviceAddr) to account for null termination, since |
| // strlen doesn't include null character in the size. |
| Inet::IPAddress ipAddress = {}; |
| VerifyOrReturnError( |
| IPAddress::FromString(Uint8::to_const_char(serializable.mDeviceAddr), sizeof(serializable.mDeviceAddr) - 1, ipAddress), |
| CHIP_ERROR_INVALID_ADDRESS); |
| |
| mPairing = serializable.mOpsCreds; |
| mDeviceId = Encoding::LittleEndian::HostSwap64(serializable.mDeviceId); |
| const uint16_t port = Encoding::LittleEndian::HostSwap16(serializable.mDevicePort); |
| const uint16_t index = Encoding::LittleEndian::HostSwap16(serializable.mFabricIndex); |
| mLocalMessageCounter = Encoding::LittleEndian::HostSwap32(serializable.mLocalMessageCounter); |
| mPeerMessageCounter = Encoding::LittleEndian::HostSwap32(serializable.mPeerMessageCounter); |
| |
| VerifyOrReturnError(CanCastTo<FabricIndex>(index), CHIP_ERROR_INVALID_ARGUMENT); |
| mFabricIndex = static_cast<FabricIndex>(index); |
| |
| // TODO - Remove the hack that's incrementing message counter while deserializing device |
| // This hack was added as a quick workaround for TE3 testing. The commissioning code |
| // is closing the exchange after the device has already been serialized and persisted to the storage. |
| // While closing the exchange, the outstanding ack gets sent to the device, thus incrementing |
| // the local message counter. As the device information was stored prior to sending the ack, it now has |
| // the old counter value (which is 1 less than the updated counter). |
| mLocalMessageCounter++; |
| |
| mDeviceOperationalCertProvisioned = (serializable.mDeviceOperationalCertProvisioned != 0); |
| |
| // The InterfaceNameToId() API requires initialization of mInterface, and lock/unlock of |
| // LwIP stack. |
| Inet::InterfaceId interfaceId = INET_NULL_INTERFACEID; |
| if (serializable.mInterfaceName[0] != '\0') |
| { |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| LOCK_TCPIP_CORE(); |
| #endif |
| CHIP_ERROR inetErr = Inet::InterfaceNameToId(Uint8::to_const_char(serializable.mInterfaceName), interfaceId); |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| UNLOCK_TCPIP_CORE(); |
| #endif |
| ReturnErrorOnFailure(inetErr); |
| } |
| |
| static_assert(std::is_same<std::underlying_type<decltype(mDeviceAddress.GetTransportType())>::type, uint8_t>::value, |
| "The underlying type of Transport::Type is not uint8_t."); |
| switch (static_cast<Transport::Type>(serializable.mDeviceTransport)) |
| { |
| case Transport::Type::kUdp: |
| mDeviceAddress = Transport::PeerAddress::UDP(ipAddress, port, interfaceId); |
| break; |
| case Transport::Type::kBle: |
| mDeviceAddress = Transport::PeerAddress::BLE(); |
| break; |
| case Transport::Type::kTcp: |
| case Transport::Type::kUndefined: |
| default: |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::Persist() |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| if (mStorageDelegate != nullptr) |
| { |
| SerializedDevice serialized; |
| ReturnErrorOnFailure(Serialize(serialized)); |
| |
| // TODO: no need to base-64 the serialized values AGAIN |
| PERSISTENT_KEY_OP(GetDeviceId(), kPairedDeviceKeyPrefix, key, |
| error = mStorageDelegate->SyncSetKeyValue(key, serialized.inner, sizeof(serialized.inner))); |
| if (error != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to persist device %" CHIP_ERROR_FORMAT, error.Format()); |
| } |
| } |
| return error; |
| } |
| |
| void Device::OnNewConnection(SessionHandle session) |
| { |
| // Only allow update if the session has been initialized and matches |
| if (mSecureSession.HasValue() && !MatchesSession(session)) |
| { |
| return; |
| } |
| |
| mState = ConnectionState::SecureConnected; |
| mSecureSession.SetValue(session); |
| |
| // Reset the message counters here because this is the first time we get a handle to the secure session. |
| // Since CHIPDevices can be serialized/deserialized in the middle of what is conceptually a single PASE session |
| // we need to restore the session counters along with the session information. |
| Transport::SecureSession * secureSession = mSessionManager->GetSecureSession(mSecureSession.Value()); |
| VerifyOrReturn(secureSession != nullptr); |
| MessageCounter & localCounter = secureSession->GetSessionMessageCounter().GetLocalMessageCounter(); |
| if (localCounter.SetCounter(mLocalMessageCounter) != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Unable to restore local counter to %" PRIu32, mLocalMessageCounter); |
| } |
| Transport::PeerMessageCounter & peerCounter = secureSession->GetSessionMessageCounter().GetPeerMessageCounter(); |
| peerCounter.SetCounter(mPeerMessageCounter); |
| } |
| |
| void Device::OnConnectionExpired(SessionHandle session) |
| { |
| VerifyOrReturn(mSecureSession.HasValue() && mSecureSession.Value() == session, |
| ChipLogDetail(Controller, "Connection expired, but it doesn't match the current session")); |
| mState = ConnectionState::NotConnected; |
| mSecureSession.ClearValue(); |
| } |
| |
| CHIP_ERROR Device::OnMessageReceived(Messaging::ExchangeContext * exchange, const PayloadHeader & payloadHeader, |
| System::PacketBufferHandle && msgBuf) |
| { |
| if (mState == ConnectionState::SecureConnected) |
| { |
| if (mStatusDelegate != nullptr) |
| { |
| mStatusDelegate->OnMessage(std::move(msgBuf)); |
| } |
| else |
| { |
| HandleDataModelMessage(exchange, std::move(msgBuf)); |
| } |
| } |
| return CHIP_NO_ERROR; |
| } |
| |
| void Device::OnResponseTimeout(Messaging::ExchangeContext * ec) {} |
| |
| void Device::OnOpenPairingWindowSuccessResponse(void * context) |
| { |
| ChipLogProgress(Controller, "Successfully opened pairing window on the device"); |
| } |
| |
| void Device::OnOpenPairingWindowFailureResponse(void * context, uint8_t status) |
| { |
| ChipLogError(Controller, "Failed to open pairing window on the device. Status %d", status); |
| } |
| |
| CHIP_ERROR Device::ComputePASEVerifier(uint32_t iterations, uint32_t setupPincode, const ByteSpan & salt, |
| PASEVerifier & outVerifier, uint32_t & outPasscodeId) |
| { |
| ReturnErrorOnFailure(PASESession::GeneratePASEVerifier(outVerifier, iterations, salt, /* useRandomPIN= */ false, setupPincode)); |
| |
| outPasscodeId = mPAKEVerifierID++; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::OpenCommissioningWindow(uint16_t timeout, uint32_t iteration, CommissioningWindowOption option, |
| const ByteSpan & salt, SetupPayload & setupPayload) |
| { |
| constexpr EndpointId kAdministratorCommissioningClusterEndpoint = 0; |
| |
| chip::Controller::AdministratorCommissioningCluster cluster; |
| cluster.Associate(this, kAdministratorCommissioningClusterEndpoint); |
| |
| Callback::Cancelable * successCallback = mOpenPairingSuccessCallback.Cancel(); |
| Callback::Cancelable * failureCallback = mOpenPairingFailureCallback.Cancel(); |
| |
| if (option != CommissioningWindowOption::kOriginalSetupCode) |
| { |
| bool randomSetupPIN = (option == CommissioningWindowOption::kTokenWithRandomPIN); |
| PASEVerifier verifier; |
| |
| ReturnErrorOnFailure( |
| PASESession::GeneratePASEVerifier(verifier, iteration, salt, randomSetupPIN, setupPayload.setUpPINCode)); |
| |
| uint8_t serializedVerifier[2 * kSpake2p_WS_Length]; |
| VerifyOrReturnError(sizeof(serializedVerifier) == sizeof(verifier), CHIP_ERROR_INTERNAL); |
| |
| memcpy(serializedVerifier, verifier.mW0, kSpake2p_WS_Length); |
| memcpy(&serializedVerifier[kSpake2p_WS_Length], verifier.mL, kSpake2p_WS_Length); |
| |
| ReturnErrorOnFailure(cluster.OpenCommissioningWindow(successCallback, failureCallback, timeout, |
| ByteSpan(serializedVerifier, sizeof(serializedVerifier)), |
| setupPayload.discriminator, iteration, salt, mPAKEVerifierID++)); |
| } |
| else |
| { |
| ReturnErrorOnFailure(cluster.OpenBasicCommissioningWindow(successCallback, failureCallback, timeout)); |
| } |
| |
| setupPayload.version = 0; |
| setupPayload.rendezvousInformation = RendezvousInformationFlags(RendezvousInformationFlag::kOnNetwork); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::OpenPairingWindow(uint16_t timeout, CommissioningWindowOption option, SetupPayload & setupPayload) |
| { |
| ByteSpan salt(reinterpret_cast<const uint8_t *>(kSpake2pKeyExchangeSalt), strlen(kSpake2pKeyExchangeSalt)); |
| |
| return OpenCommissioningWindow(timeout, kPBKDFMinimumIterations, option, salt, setupPayload); |
| } |
| |
| void Device::UpdateSession(bool connected) |
| { |
| SessionHandle session = |
| SessionHandle(mDeviceId, mCASESession.GetLocalSessionId(), mCASESession.GetPeerSessionId(), mFabricIndex); |
| if (connected) |
| { |
| OnNewConnection(session); |
| } |
| else |
| { |
| OnConnectionExpired(session); |
| } |
| } |
| |
| CHIP_ERROR Device::CloseSession() |
| { |
| ReturnErrorCodeIf(mState != ConnectionState::SecureConnected, CHIP_ERROR_INCORRECT_STATE); |
| if (mSecureSession.HasValue()) |
| { |
| mSessionManager->ExpirePairing(mSecureSession.Value()); |
| } |
| mState = ConnectionState::NotConnected; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::UpdateAddress(const Transport::PeerAddress & addr) |
| { |
| bool didLoad; |
| |
| mDeviceAddress = addr; |
| |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(didLoad)); |
| |
| if (!mSecureSession.HasValue()) |
| { |
| // Nothing needs to be done here. It's not an error to not have a |
| // secureSession. For one thing, we could have gotten an different |
| // UpdateAddress already and that caused connections to be torn down and |
| // whatnot. |
| return CHIP_NO_ERROR; |
| } |
| |
| Transport::SecureSession * secureSession = mSessionManager->GetSecureSession(mSecureSession.Value()); |
| secureSession->SetPeerAddress(addr); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void Device::Reset() |
| { |
| if (IsActive() && mStorageDelegate != nullptr && mSessionManager != nullptr) |
| { |
| // If a session can be found, persist the device so that we track the newest message counter values |
| |
| if (mSecureSession.HasValue()) |
| { |
| Persist(); |
| } |
| } |
| |
| SetActive(false); |
| mCASESession.Clear(); |
| |
| mState = ConnectionState::NotConnected; |
| mSessionManager = nullptr; |
| mStatusDelegate = nullptr; |
| mInetLayer = nullptr; |
| #if CONFIG_NETWORK_LAYER_BLE |
| mBleLayer = nullptr; |
| #endif |
| if (mExchangeMgr) |
| { |
| // Ensure that any exchange contexts we have open get closed now, |
| // because we don't want them to call back in to us after this |
| // point. |
| mExchangeMgr->CloseAllContextsForDelegate(this); |
| } |
| mExchangeMgr = nullptr; |
| |
| ReleaseDAC(); |
| ReleasePAI(); |
| } |
| |
| CHIP_ERROR Device::LoadSecureSessionParameters() |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| PASESession pairingSession; |
| |
| if (mSessionManager == nullptr || mState == ConnectionState::SecureConnected) |
| { |
| ExitNow(err = CHIP_ERROR_INCORRECT_STATE); |
| } |
| |
| if (mState == ConnectionState::Connecting) |
| { |
| ExitNow(err = CHIP_NO_ERROR); |
| } |
| |
| if (IsOperationalCertProvisioned()) |
| { |
| err = WarmupCASESession(); |
| SuccessOrExit(err); |
| } |
| else |
| { |
| err = pairingSession.FromSerializable(mPairing); |
| SuccessOrExit(err); |
| |
| err = mSessionManager->NewPairing(Optional<Transport::PeerAddress>::Value(mDeviceAddress), mDeviceId, &pairingSession, |
| CryptoContext::SessionRole::kInitiator, mFabricIndex); |
| SuccessOrExit(err); |
| } |
| |
| exit: |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "LoadSecureSessionParameters returning error %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| return err; |
| } |
| |
| bool Device::GetAddress(Inet::IPAddress & addr, uint16_t & port) const |
| { |
| if (mState == ConnectionState::NotConnected) |
| return false; |
| |
| addr = mDeviceAddress.GetIPAddress(); |
| port = mDeviceAddress.GetPort(); |
| return true; |
| } |
| |
| void Device::OperationalCertProvisioned() |
| { |
| VerifyOrReturn(!mDeviceOperationalCertProvisioned, |
| ChipLogDetail(Controller, "Operational certificates already provisioned for this device")); |
| |
| ChipLogDetail(Controller, "Enabling CASE session establishment for the device"); |
| mDeviceOperationalCertProvisioned = true; |
| |
| Persist(); |
| CloseSession(); |
| mState = ConnectionState::NotConnected; |
| } |
| |
| CHIP_ERROR Device::WarmupCASESession() |
| { |
| VerifyOrReturnError(mDeviceOperationalCertProvisioned, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mState == ConnectionState::NotConnected, CHIP_NO_ERROR); |
| |
| // Create a UnauthenticatedSession for CASE pairing. |
| // Don't use mSecureSession here, because mSecureSession is the secure session. |
| Optional<SessionHandle> session = mSessionManager->CreateUnauthenticatedSession(mDeviceAddress); |
| if (!session.HasValue()) |
| { |
| return CHIP_ERROR_NO_MEMORY; |
| } |
| Messaging::ExchangeContext * exchange = mExchangeMgr->NewContext(session.Value(), &mCASESession); |
| VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_INTERNAL); |
| |
| ReturnErrorOnFailure(mCASESession.MessageDispatch().Init(mSessionManager)); |
| |
| uint16_t keyID = 0; |
| ReturnErrorOnFailure(mIDAllocator->Allocate(keyID)); |
| |
| mLocalMessageCounter = 0; |
| mPeerMessageCounter = 0; |
| |
| FabricInfo * fabric = mFabricsTable->FindFabricWithIndex(mFabricIndex); |
| ReturnErrorCodeIf(fabric == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| |
| ReturnErrorOnFailure(mCASESession.EstablishSession(mDeviceAddress, fabric, mDeviceId, keyID, exchange, this)); |
| |
| mState = ConnectionState::Connecting; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void Device::OnSessionEstablishmentError(CHIP_ERROR error) |
| { |
| mState = ConnectionState::NotConnected; |
| mIDAllocator->Free(mCASESession.GetLocalSessionId()); |
| |
| Cancelable ready; |
| mConnectionFailure.DequeueAll(ready); |
| while (ready.mNext != &ready) |
| { |
| Callback::Callback<OnDeviceConnectionFailure> * cb = |
| Callback::Callback<OnDeviceConnectionFailure>::FromCancelable(ready.mNext); |
| |
| cb->Cancel(); |
| cb->mCall(cb->mContext, GetDeviceId(), error); |
| } |
| } |
| |
| void Device::OnSessionEstablished() |
| { |
| CHIP_ERROR err = mSessionManager->NewPairing(Optional<Transport::PeerAddress>::Value(mDeviceAddress), mDeviceId, &mCASESession, |
| CryptoContext::SessionRole::kInitiator, mFabricIndex); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed in setting up CASE secure channel: err %s", ErrorStr(err)); |
| OnSessionEstablishmentError(err); |
| return; |
| } |
| |
| Cancelable ready; |
| mConnectionSuccess.DequeueAll(ready); |
| while (ready.mNext != &ready) |
| { |
| Callback::Callback<OnDeviceConnected> * cb = Callback::Callback<OnDeviceConnected>::FromCancelable(ready.mNext); |
| |
| cb->Cancel(); |
| cb->mCall(cb->mContext, this); |
| } |
| } |
| |
| void Device::ReleaseDAC() |
| { |
| if (mDAC != nullptr) |
| { |
| Platform::MemoryFree(mDAC); |
| } |
| mDACLen = 0; |
| mDAC = nullptr; |
| } |
| |
| CHIP_ERROR Device::SetDAC(const ByteSpan & dac) |
| { |
| if (dac.size() == 0) |
| { |
| ReleaseDAC(); |
| return CHIP_NO_ERROR; |
| } |
| |
| VerifyOrReturnError(dac.size() <= Credentials::kMaxDERCertLength, CHIP_ERROR_INVALID_ARGUMENT); |
| if (mDACLen != 0) |
| { |
| ReleaseDAC(); |
| } |
| |
| VerifyOrReturnError(CanCastTo<uint16_t>(dac.size()), CHIP_ERROR_INVALID_ARGUMENT); |
| if (mDAC == nullptr) |
| { |
| mDAC = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(dac.size())); |
| } |
| VerifyOrReturnError(mDAC != nullptr, CHIP_ERROR_NO_MEMORY); |
| mDACLen = static_cast<uint16_t>(dac.size()); |
| memcpy(mDAC, dac.data(), mDACLen); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void Device::ReleasePAI() |
| { |
| if (mPAI != nullptr) |
| { |
| chip::Platform::MemoryFree(mPAI); |
| } |
| mPAILen = 0; |
| mPAI = nullptr; |
| } |
| |
| CHIP_ERROR Device::SetPAI(const chip::ByteSpan & pai) |
| { |
| if (pai.size() == 0) |
| { |
| ReleasePAI(); |
| return CHIP_NO_ERROR; |
| } |
| |
| VerifyOrReturnError(pai.size() <= Credentials::kMaxDERCertLength, CHIP_ERROR_INVALID_ARGUMENT); |
| if (mPAILen != 0) |
| { |
| ReleasePAI(); |
| } |
| |
| VerifyOrReturnError(CanCastTo<uint16_t>(pai.size()), CHIP_ERROR_INVALID_ARGUMENT); |
| if (mPAI == nullptr) |
| { |
| mPAI = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(pai.size())); |
| } |
| VerifyOrReturnError(mPAI != nullptr, CHIP_ERROR_NO_MEMORY); |
| mPAILen = static_cast<uint16_t>(pai.size()); |
| memcpy(mPAI, pai.data(), mPAILen); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::EstablishConnectivity(Callback::Callback<OnDeviceConnected> * onConnection, |
| Callback::Callback<OnDeviceConnectionFailure> * onFailure) |
| { |
| bool loadedSecureSession = false; |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession)); |
| |
| if (loadedSecureSession) |
| { |
| if (IsOperationalCertProvisioned()) |
| { |
| if (onConnection != nullptr) |
| { |
| mConnectionSuccess.Enqueue(onConnection->Cancel()); |
| } |
| |
| if (onFailure != nullptr) |
| { |
| mConnectionFailure.Enqueue(onFailure->Cancel()); |
| } |
| } |
| else |
| { |
| if (onConnection != nullptr) |
| { |
| onConnection->mCall(onConnection->mContext, this); |
| } |
| } |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void Device::AddResponseHandler(uint8_t seqNum, Callback::Cancelable * onSuccessCallback, Callback::Cancelable * onFailureCallback, |
| app::TLVDataFilter tlvDataFilter) |
| { |
| mCallbacksMgr.AddResponseCallback(mDeviceId, seqNum, onSuccessCallback, onFailureCallback, tlvDataFilter); |
| } |
| |
| void Device::CancelResponseHandler(uint8_t seqNum) |
| { |
| mCallbacksMgr.CancelResponseCallback(mDeviceId, seqNum); |
| } |
| |
| void Device::AddIMResponseHandler(void * commandObj, Callback::Cancelable * onSuccessCallback, |
| Callback::Cancelable * onFailureCallback) |
| { |
| // We are using the pointer to command sender object as the identifier of command transactions. This makes sense as long as |
| // there are only one active command transaction on one command sender object. This is a bit tricky, we try to assume that |
| // chip::NodeId is uint64_t so the pointer can be used as a NodeId for CallbackMgr. |
| static_assert(std::is_same<chip::NodeId, uint64_t>::value, "chip::NodeId is not uint64_t"); |
| chip::NodeId transactionId = reinterpret_cast<chip::NodeId>(commandObj); |
| mCallbacksMgr.AddResponseCallback(transactionId, 0 /* seqNum, always 0 for IM before #6559 */, onSuccessCallback, |
| onFailureCallback); |
| } |
| |
| void Device::CancelIMResponseHandler(void * commandObj) |
| { |
| // We are using the pointer to command sender object as the identifier of command transactions. This makes sense as long as |
| // there are only one active command transaction on one command sender object. This is a bit tricky, we try to assume that |
| // chip::NodeId is uint64_t so the pointer can be used as a NodeId for CallbackMgr. |
| static_assert(std::is_same<chip::NodeId, uint64_t>::value, "chip::NodeId is not uint64_t"); |
| chip::NodeId transactionId = reinterpret_cast<chip::NodeId>(commandObj); |
| mCallbacksMgr.CancelResponseCallback(transactionId, 0 /* seqNum, always 0 for IM before #6559 */); |
| } |
| |
| void Device::AddReportHandler(EndpointId endpoint, ClusterId cluster, AttributeId attribute, |
| Callback::Cancelable * onReportCallback, app::TLVDataFilter tlvDataFilter) |
| { |
| mCallbacksMgr.AddReportCallback(mDeviceId, endpoint, cluster, attribute, onReportCallback, tlvDataFilter); |
| } |
| |
| CHIP_ERROR Device::SendReadAttributeRequest(app::AttributePathParams aPath, Callback::Cancelable * onSuccessCallback, |
| Callback::Cancelable * onFailureCallback, app::TLVDataFilter aTlvDataFilter) |
| { |
| bool loadedSecureSession = false; |
| uint8_t seqNum = GetNextSequenceNumber(); |
| aPath.mNodeId = GetDeviceId(); |
| |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession)); |
| |
| if (onSuccessCallback != nullptr || onFailureCallback != nullptr) |
| { |
| AddResponseHandler(seqNum, onSuccessCallback, onFailureCallback, aTlvDataFilter); |
| } |
| // The application context is used to identify different requests from client application the type of it is intptr_t, here we |
| // use the seqNum. |
| chip::app::ReadPrepareParams readPrepareParams(mSecureSession.Value()); |
| readPrepareParams.mpAttributePathParamsList = &aPath; |
| readPrepareParams.mAttributePathParamsListSize = 1; |
| CHIP_ERROR err = |
| chip::app::InteractionModelEngine::GetInstance()->SendReadRequest(readPrepareParams, seqNum /* application context */); |
| if (err != CHIP_NO_ERROR) |
| { |
| CancelResponseHandler(seqNum); |
| } |
| return err; |
| } |
| |
| CHIP_ERROR Device::SendSubscribeAttributeRequest(app::AttributePathParams aPath, uint16_t mMinIntervalFloorSeconds, |
| uint16_t mMaxIntervalCeilingSeconds, Callback::Cancelable * onSuccessCallback, |
| Callback::Cancelable * onFailureCallback) |
| { |
| bool loadedSecureSession = false; |
| uint8_t seqNum = GetNextSequenceNumber(); |
| aPath.mNodeId = GetDeviceId(); |
| |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession)); |
| |
| app::AttributePathParams * path = mpIMDelegate->AllocateAttributePathParam(1, seqNum); |
| |
| VerifyOrReturnError(path != nullptr, CHIP_ERROR_NO_MEMORY); |
| |
| *path = aPath; |
| |
| // The application context is used to identify different requests from client application the type of it is intptr_t, here we |
| // use the seqNum. |
| VerifyOrReturnError(GetSecureSession().HasValue(), CHIP_ERROR_INCORRECT_STATE); |
| app::ReadPrepareParams params(GetSecureSession().Value()); |
| params.mpAttributePathParamsList = path; |
| params.mAttributePathParamsListSize = 1; |
| params.mMinIntervalFloorSeconds = mMinIntervalFloorSeconds; |
| params.mMaxIntervalCeilingSeconds = mMaxIntervalCeilingSeconds; |
| params.mKeepSubscriptions = false; |
| |
| CHIP_ERROR err = |
| chip::app::InteractionModelEngine::GetInstance()->SendSubscribeRequest(params, seqNum /* application context */); |
| if (err != CHIP_NO_ERROR) |
| { |
| mpIMDelegate->FreeAttributePathParam(seqNum); |
| return err; |
| } |
| |
| if (onSuccessCallback != nullptr || onFailureCallback != nullptr) |
| { |
| AddResponseHandler(seqNum, onSuccessCallback, onFailureCallback); |
| } |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::SendWriteAttributeRequest(app::WriteClientHandle aHandle, Callback::Cancelable * onSuccessCallback, |
| Callback::Cancelable * onFailureCallback) |
| { |
| bool loadedSecureSession = false; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession)); |
| |
| app::WriteClient * writeClient = aHandle.Get(); |
| |
| if (onSuccessCallback != nullptr || onFailureCallback != nullptr) |
| { |
| AddIMResponseHandler(writeClient, onSuccessCallback, onFailureCallback); |
| } |
| if ((err = aHandle.SendWriteRequest(GetDeviceId(), 0, mSecureSession)) != CHIP_NO_ERROR) |
| { |
| CancelIMResponseHandler(writeClient); |
| } |
| return err; |
| } |
| |
| Device::~Device() |
| { |
| if (mExchangeMgr) |
| { |
| // Ensure that any exchange contexts we have open get closed now, |
| // because we don't want them to call back in to us after this |
| // point. |
| mExchangeMgr->CloseAllContextsForDelegate(this); |
| } |
| |
| ReleaseDAC(); |
| ReleasePAI(); |
| } |
| |
| CHIP_ERROR Device::SetNOCCertBufferSize(size_t new_size) |
| { |
| ReturnErrorCodeIf(new_size > sizeof(mNOCCertBuffer), CHIP_ERROR_INVALID_ARGUMENT); |
| mNOCCertBufferSize = new_size; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::SetICACertBufferSize(size_t new_size) |
| { |
| ReturnErrorCodeIf(new_size > sizeof(mICACertBuffer), CHIP_ERROR_INVALID_ARGUMENT); |
| mICACertBufferSize = new_size; |
| return CHIP_NO_ERROR; |
| } |
| |
| } // namespace Controller |
| } // namespace chip |