| /* |
| * |
| * Copyright (c) 2021 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <platform/CHIPDeviceConfig.h> |
| #include <platform/CommissionableDataProvider.h> |
| |
| #include "app/clusters/ota-requestor/OTARequestorInterface.h" |
| #include "app/server/CommissioningWindowManager.h" |
| #include "app/server/OnboardingCodesUtil.h" |
| #include "app/server/Server.h" |
| #include "credentials/FabricTable.h" |
| #include "device_service/device_service.rpc.pb.h" |
| #include "platform/CommissionableDataProvider.h" |
| #include "platform/ConfigurationManager.h" |
| #include "platform/DiagnosticDataProvider.h" |
| #include "platform/PlatformManager.h" |
| #include <crypto/CHIPCryptoPAL.h> |
| #include <platform/DeviceInstanceInfoProvider.h> |
| #include <setup_payload/QRCodeSetupPayloadGenerator.h> |
| |
| namespace chip { |
| namespace rpc { |
| |
| namespace Internal { |
| // This class supports changing the commissionable data provider values at |
| // runtime using the RPCs. |
| // This class is LazyInit after getting or setting any of it's values. After |
| // this the class wraps the original CommissionableDataProvider, returning the |
| // original values for anything which has not been overwritten. |
| // |
| // NOTE: Values written do not persist across a reboot. |
| class CommissionableDataProviderRpcWrapper : public DeviceLayer::CommissionableDataProvider |
| { |
| public: |
| CHIP_ERROR GetSetupPasscode(uint32_t & setupPasscode) override |
| { |
| LazyInit(); |
| if (mPasscodeOverride.has_value()) |
| { |
| setupPasscode = mPasscodeOverride.value(); |
| return CHIP_NO_ERROR; |
| } |
| if (mCommissionableDataProvider) |
| { |
| return mCommissionableDataProvider->GetSetupPasscode(setupPasscode); |
| } |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| // NOTE: Changing the passcode will not change the verifier or anything else |
| // this just changes the value returned from GetSetupPasscode. |
| // Using this is completely optional, and only really useful for test |
| // automation which can read the configured passcode for commissioning |
| // after it is changed. |
| CHIP_ERROR SetSetupPasscode(uint32_t setupPasscode) override |
| { |
| LazyInit(); |
| mPasscodeOverride = setupPasscode; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR GetSetupDiscriminator(uint16_t & setupDiscriminator) override |
| { |
| LazyInit(); |
| if (mDiscriminatorOverride.has_value()) |
| { |
| setupDiscriminator = mDiscriminatorOverride.value(); |
| return CHIP_NO_ERROR; |
| } |
| if (mCommissionableDataProvider) |
| { |
| return mCommissionableDataProvider->GetSetupDiscriminator(setupDiscriminator); |
| } |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| CHIP_ERROR SetSetupDiscriminator(uint16_t setupDiscriminator) override |
| { |
| LazyInit(); |
| mDiscriminatorOverride = setupDiscriminator; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR GetSpake2pIterationCount(uint32_t & iterationCount) override |
| { |
| LazyInit(); |
| if (mIterationCountOverride.has_value()) |
| { |
| iterationCount = mIterationCountOverride.value(); |
| return CHIP_NO_ERROR; |
| } |
| if (mCommissionableDataProvider) |
| { |
| return mCommissionableDataProvider->GetSpake2pIterationCount(iterationCount); |
| } |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| CHIP_ERROR SetSpake2pIterationCount(uint32_t iterationCount) |
| { |
| LazyInit(); |
| mIterationCountOverride = iterationCount; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR GetSpake2pSalt(MutableByteSpan & saltBuf) override |
| { |
| LazyInit(); |
| if (mSaltOverride.has_value()) |
| { |
| if (mSaltOverride.value().size() > saltBuf.size()) |
| { |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| } |
| std::copy(mSaltOverride.value().begin(), mSaltOverride.value().end(), saltBuf.begin()); |
| saltBuf.reduce_size(mSaltOverride.value().size()); |
| return CHIP_NO_ERROR; |
| } |
| if (mCommissionableDataProvider) |
| { |
| return mCommissionableDataProvider->GetSpake2pSalt(saltBuf); |
| } |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| CHIP_ERROR SetSpake2pSalt(ByteSpan saltBuf) |
| { |
| LazyInit(); |
| if (sizeof(mSaltBuf) < saltBuf.size()) |
| { |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| } |
| std::copy(saltBuf.begin(), saltBuf.end(), mSaltBuf); |
| mSaltOverride = ByteSpan(mSaltBuf, saltBuf.size()); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR GetSpake2pVerifier(MutableByteSpan & verifierBuf, size_t & outVerifierLen) override |
| { |
| LazyInit(); |
| if (mVerifierOverride.has_value()) |
| { |
| outVerifierLen = mVerifierOverride.value().size(); |
| if (mVerifierOverride.value().size() > verifierBuf.size()) |
| { |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| } |
| std::copy(mVerifierOverride.value().begin(), mVerifierOverride.value().end(), verifierBuf.begin()); |
| verifierBuf.reduce_size(mVerifierOverride.value().size()); |
| return CHIP_NO_ERROR; |
| } |
| if (mCommissionableDataProvider) |
| { |
| return mCommissionableDataProvider->GetSpake2pVerifier(verifierBuf, outVerifierLen); |
| } |
| |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| CHIP_ERROR SetSpake2pVerifier(ByteSpan verifierBuf) |
| { |
| LazyInit(); |
| if (sizeof(mVerifierBuf) < verifierBuf.size()) |
| { |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| } |
| std::copy(verifierBuf.begin(), verifierBuf.end(), mVerifierBuf); |
| mVerifierOverride = ByteSpan(mVerifierBuf, verifierBuf.size()); |
| return CHIP_NO_ERROR; |
| } |
| |
| private: |
| std::optional<uint16_t> mDiscriminatorOverride; |
| std::optional<uint32_t> mPasscodeOverride; |
| Crypto::Spake2pVerifierSerialized mVerifierBuf; |
| std::optional<ByteSpan> mVerifierOverride; |
| uint8_t mSaltBuf[Crypto::kSpake2p_Max_PBKDF_Salt_Length]; |
| std::optional<ByteSpan> mSaltOverride; |
| std::optional<uint32_t> mIterationCountOverride; |
| DeviceLayer::CommissionableDataProvider * mCommissionableDataProvider = nullptr; |
| |
| void LazyInit() |
| { |
| if (!mCommissionableDataProvider) |
| { |
| mCommissionableDataProvider = DeviceLayer::GetCommissionableDataProvider(); |
| DeviceLayer::SetCommissionableDataProvider(this); |
| } |
| } |
| }; |
| } // namespace Internal |
| |
| class Device : public pw_rpc::nanopb::Device::Service<Device> |
| { |
| public: |
| virtual ~Device() = default; |
| |
| virtual pw::Status FactoryReset(const pw_protobuf_Empty & request, pw_protobuf_Empty & response) |
| { |
| DeviceLayer::ConfigurationMgr().InitiateFactoryReset(); |
| return pw::OkStatus(); |
| } |
| |
| virtual pw::Status Reboot(const chip_rpc_RebootRequest & request, pw_protobuf_Empty & response) |
| { |
| return pw::Status::Unimplemented(); |
| } |
| |
| virtual pw::Status TriggerOta(const pw_protobuf_Empty & request, pw_protobuf_Empty & response) |
| { |
| #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR |
| chip::DeviceLayer::PlatformMgr().ScheduleWork( |
| [](intptr_t) { |
| chip::OTARequestorInterface * requestor = chip::GetRequestorInstance(); |
| if (requestor == nullptr) |
| { |
| ChipLogError(SoftwareUpdate, "Can't get the CASESessionManager"); |
| } |
| else |
| { |
| requestor->TriggerImmediateQuery(); |
| } |
| }, |
| reinterpret_cast<intptr_t>(nullptr)); |
| return pw::OkStatus(); |
| #else // CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR |
| ChipLogError(AppServer, "Trigger OTA requested, but OTA requestor not compiled in."); |
| return pw::Status::Unimplemented(); |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR |
| } |
| |
| virtual pw::Status SetOtaMetadataForProvider(const chip_rpc_MetadataForProvider & request, pw_protobuf_Empty & response) |
| { |
| #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR |
| chip::OTARequestorInterface * requestor = chip::GetRequestorInstance(); |
| if (requestor == nullptr) |
| { |
| ChipLogError(SoftwareUpdate, "Can't get the CASESessionManager"); |
| return pw::Status::Unavailable(); |
| } |
| else if (sizeof(metadataForProviderBuffer) < request.tlv.size) |
| { |
| return pw::Status::ResourceExhausted(); |
| } |
| memcpy(metadataForProviderBuffer, request.tlv.bytes, request.tlv.size); |
| DeviceLayer::StackLock lock; |
| requestor->SetMetadataForProvider(chip::ByteSpan(metadataForProviderBuffer, request.tlv.size)); |
| return pw::OkStatus(); |
| #else // CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR |
| ChipLogError(AppServer, "OTA set metadata for provider requested, but OTA requestor not compiled in."); |
| return pw::Status::Unimplemented(); |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR |
| } |
| |
| virtual pw::Status SetPairingState(const chip_rpc_PairingState & request, pw_protobuf_Empty & response) |
| { |
| if (request.pairing_enabled && !chip::Server::GetInstance().GetCommissioningWindowManager().IsCommissioningWindowOpen()) |
| { |
| DeviceLayer::StackLock lock; |
| chip::ChipError err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow(); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(NotSpecified, "RPC SetPairingState failed to open commissioning window: %" CHIP_ERROR_FORMAT, |
| err.Format()); |
| return pw::Status::Internal(); |
| } |
| } |
| else if (!request.pairing_enabled && |
| chip::Server::GetInstance().GetCommissioningWindowManager().IsCommissioningWindowOpen()) |
| { |
| DeviceLayer::StackLock lock; |
| chip::Server::GetInstance().GetCommissioningWindowManager().CloseCommissioningWindow(); |
| } |
| return pw::OkStatus(); |
| } |
| |
| virtual pw::Status GetPairingState(const pw_protobuf_Empty & request, chip_rpc_PairingState & response) |
| { |
| response.pairing_enabled = DeviceLayer::ConnectivityMgr().IsBLEAdvertisingEnabled(); |
| return pw::OkStatus(); |
| } |
| |
| virtual pw::Status GetDeviceState(const pw_protobuf_Empty & request, chip_rpc_DeviceState & response) |
| { |
| uint64_t time_since_boot_sec; |
| DeviceLayer::GetDiagnosticDataProvider().GetUpTime(time_since_boot_sec); |
| response.time_since_boot_millis = time_since_boot_sec * 1000; |
| size_t count = 0; |
| DeviceLayer::StackLock lock; |
| for (const FabricInfo & fabricInfo : Server::GetInstance().GetFabricTable()) |
| { |
| if (count < ArraySize(response.fabric_info)) |
| { |
| response.fabric_info[count].fabric_id = fabricInfo.GetFabricId(); |
| response.fabric_info[count].node_id = fabricInfo.GetPeerId().GetNodeId(); |
| count++; |
| } |
| } |
| response.fabric_info_count = count; |
| return pw::OkStatus(); |
| } |
| |
| virtual pw::Status GetDeviceInfo(const pw_protobuf_Empty & request, chip_rpc_DeviceInfo & response) |
| { |
| |
| uint16_t vendor_id; |
| if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(vendor_id) == CHIP_NO_ERROR) |
| { |
| response.vendor_id = static_cast<uint32_t>(vendor_id); |
| } |
| else |
| { |
| return pw::Status::Internal(); |
| } |
| |
| uint16_t product_id; |
| if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetProductId(product_id) == CHIP_NO_ERROR) |
| { |
| response.product_id = static_cast<uint32_t>(product_id); |
| } |
| else |
| { |
| return pw::Status::Internal(); |
| } |
| |
| uint32_t software_version; |
| if (DeviceLayer::ConfigurationMgr().GetSoftwareVersion(software_version) == CHIP_NO_ERROR) |
| { |
| response.software_version = software_version; |
| } |
| else |
| { |
| return pw::Status::Internal(); |
| } |
| |
| if (DeviceLayer::ConfigurationMgr().GetSoftwareVersionString(response.software_version_string, |
| sizeof(response.software_version_string)) != CHIP_NO_ERROR) |
| { |
| return pw::Status::Internal(); |
| } |
| |
| uint32_t code; |
| if (DeviceLayer::GetCommissionableDataProvider()->GetSetupPasscode(code) == CHIP_NO_ERROR) |
| { |
| response.pairing_info.code = code; |
| response.has_pairing_info = true; |
| } |
| |
| uint16_t discriminator; |
| if (DeviceLayer::GetCommissionableDataProvider()->GetSetupDiscriminator(discriminator) == CHIP_NO_ERROR) |
| { |
| response.pairing_info.discriminator = static_cast<uint32_t>(discriminator); |
| response.has_pairing_info = true; |
| } |
| |
| if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetSerialNumber(response.serial_number, sizeof(response.serial_number)) != |
| CHIP_NO_ERROR) |
| { |
| response.serial_number[0] = '\0'; // optional serial field not set. |
| } |
| |
| // Create buffer for QR code that can fit max size and null terminator. |
| char qrCodeBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1]; |
| chip::MutableCharSpan qrCodeText(qrCodeBuffer); |
| if (GetQRCode(qrCodeText, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)) == CHIP_NO_ERROR) |
| { |
| snprintf(response.pairing_info.qr_code, sizeof(response.pairing_info.qr_code), "%s", qrCodeText.data()); |
| GetQRCodeUrl(response.pairing_info.qr_code_url, sizeof(response.pairing_info.qr_code_url), qrCodeText); |
| response.has_pairing_info = true; |
| } |
| |
| return pw::OkStatus(); |
| } |
| |
| virtual pw::Status GetSpakeInfo(const pw_protobuf_Empty & request, chip_rpc_SpakeInfo & response) |
| { |
| size_t serializedVerifierLen = 0; |
| MutableByteSpan verifierSpan{ response.verifier.bytes }; |
| if (DeviceLayer::GetCommissionableDataProvider()->GetSpake2pVerifier(verifierSpan, serializedVerifierLen) == CHIP_NO_ERROR) |
| { |
| response.verifier.size = verifierSpan.size(); |
| response.has_verifier = true; |
| } |
| |
| MutableByteSpan saltSpan{ response.salt.bytes }; |
| if (DeviceLayer::GetCommissionableDataProvider()->GetSpake2pSalt(saltSpan) == CHIP_NO_ERROR) |
| { |
| response.salt.size = saltSpan.size(); |
| response.has_salt = true; |
| } |
| |
| if (DeviceLayer::GetCommissionableDataProvider()->GetSpake2pIterationCount(response.iteration_count) == CHIP_NO_ERROR) |
| { |
| response.has_iteration_count = true; |
| } |
| |
| return pw::OkStatus(); |
| } |
| |
| virtual pw::Status SetSpakeInfo(const chip_rpc_SpakeInfo & request, pw_protobuf_Empty & response) |
| { |
| if (request.has_salt) |
| { |
| mCommissionableDataProvider.SetSpake2pSalt(ByteSpan(request.salt.bytes, request.salt.size)); |
| } |
| if (request.has_iteration_count) |
| { |
| mCommissionableDataProvider.SetSpake2pIterationCount(request.iteration_count); |
| } |
| if (request.has_verifier) |
| { |
| mCommissionableDataProvider.SetSpake2pVerifier(ByteSpan(request.verifier.bytes, request.verifier.size)); |
| } |
| |
| if (Server::GetInstance().GetCommissioningWindowManager().IsCommissioningWindowOpen() && |
| Server::GetInstance().GetCommissioningWindowManager().CommissioningWindowStatusForCluster() != |
| app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kEnhancedWindowOpen) |
| { |
| // Cache values before closing to restore them after restart. |
| app::DataModel::Nullable<VendorId> vendorId = Server::GetInstance().GetCommissioningWindowManager().GetOpenerVendorId(); |
| app::DataModel::Nullable<FabricIndex> fabricIndex = |
| Server::GetInstance().GetCommissioningWindowManager().GetOpenerFabricIndex(); |
| |
| // Restart commissioning window to recache the spakeInfo values: |
| { |
| DeviceLayer::StackLock lock; |
| Server::GetInstance().GetCommissioningWindowManager().CloseCommissioningWindow(); |
| } |
| // Let other tasks possibly work since Commissioning window close/open are "heavy" |
| if (Server::GetInstance().GetCommissioningWindowManager().CommissioningWindowStatusForCluster() != |
| app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen && |
| !vendorId.IsNull() && !fabricIndex.IsNull()) |
| { |
| DeviceLayer::StackLock lock; |
| System::Clock::Seconds16 commissioningTimeout = |
| System::Clock::Seconds16(CHIP_DEVICE_CONFIG_DISCOVERY_TIMEOUT_SECS); // Use default for timeout for now. |
| Server::GetInstance() |
| .GetCommissioningWindowManager() |
| .OpenBasicCommissioningWindowForAdministratorCommissioningCluster(commissioningTimeout, fabricIndex.Value(), |
| vendorId.Value()); |
| } |
| else |
| { |
| DeviceLayer::StackLock lock; |
| Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow(); |
| } |
| } |
| |
| return pw::OkStatus(); |
| } |
| |
| // NOTE: Changing the passcode will not change the verifier or anything else |
| // this just changes the value returned from GetSetupPasscode. |
| // Using this is completely optional, and only really useful for test |
| // automation which can read the configured passcode for commissioning |
| // after it is changed. |
| virtual pw::Status SetPairingInfo(const chip_rpc_PairingInfo & request, pw_protobuf_Empty & response) |
| { |
| if (mCommissionableDataProvider.SetSetupPasscode(request.code) != CHIP_NO_ERROR || |
| mCommissionableDataProvider.SetSetupDiscriminator(request.discriminator) != CHIP_NO_ERROR) |
| { |
| return pw::Status::Unknown(); |
| } |
| return pw::OkStatus(); |
| } |
| |
| private: |
| #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR |
| static constexpr size_t kMaxMetadataForProviderLength = 512; // length defined in chip spec 11.20.6.7 |
| uint8_t metadataForProviderBuffer[kMaxMetadataForProviderLength]; |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR |
| Internal::CommissionableDataProviderRpcWrapper mCommissionableDataProvider; |
| }; |
| |
| } // namespace rpc |
| } // namespace chip |