blob: 758ecc9c4542595be85842a2612bbd10ca14bc33 [file] [log] [blame]
/*
*
* 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 <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;
Spake2pVerifierSerialized mVerifierBuf;
std::optional<ByteSpan> mVerifierOverride;
uint8_t mSaltBuf[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 pw_protobuf_Empty & 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)
{
return pw::Status::Internal();
}
// 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::CommissioningWindowStatus::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::CommissioningWindowStatus::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