Skip to content

Commit

Permalink
RPC: Add RPC to set temporary spake values (#19411)
Browse files Browse the repository at this point in the history
* RPC: Add RPC to set temporary spake values

Add new RPCs to the Device RPC service which allows setting and
getting the verifier, salt, and iteration count.

When setting these values the current commissionable data provider
gets wrapped by a new one which returns the set values for anything
which has been set, and returns the original values for everything
else.

These values do not persist across reboot.

* remove else
  • Loading branch information
rgoliver authored and pull[bot] committed Jun 14, 2022
1 parent b798bf4 commit 2184561
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 2 deletions.
2 changes: 2 additions & 0 deletions examples/common/pigweed/protos/device_service.options
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ chip.rpc.DeviceInfo.serial_number max_size:32 // length defined in chip spec 8.
chip.rpc.DeviceState.fabric_info max_count:2
chip.rpc.PairingInfo.qr_code max_size:256
chip.rpc.PairingInfo.qr_code_url max_size:256
chip.rpc.SpakeInfo.verifier max_size:97 // kSpake2p_VerifierSerialized_Length
chip.rpc.SpakeInfo.salt max_size:32 // kSpake2p_Max_PBKDF_Salt_Length
8 changes: 8 additions & 0 deletions examples/common/pigweed/protos/device_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ message PairingInfo {
string qr_code_url = 4;
}

message SpakeInfo {
optional bytes verifier = 1;
optional bytes salt = 2;
optional uint32 iteration_count = 3;
}

// type lengths defined in chip spec 8.2.3.1
message DeviceInfo {
uint32 vendor_id = 1;
Expand Down Expand Up @@ -43,4 +49,6 @@ service Device {
rpc SetPairingState(PairingState) returns (pw.protobuf.Empty){}
rpc GetPairingState(pw.protobuf.Empty) returns (PairingState){}
rpc SetPairingInfo(PairingInfo) returns (pw.protobuf.Empty){}
rpc GetSpakeInfo(pw.protobuf.Empty) returns (SpakeInfo){}
rpc SetSpakeInfo(SpakeInfo) returns (pw.protobuf.Empty){}
}
224 changes: 222 additions & 2 deletions examples/common/pigweed/rpc_services/Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#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"
Expand All @@ -35,6 +36,175 @@
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:
Expand Down Expand Up @@ -174,15 +344,65 @@ class Device : public pw_rpc::nanopb::Device::Service<Device>
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));
}
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 (DeviceLayer::GetCommissionableDataProvider()->SetSetupPasscode(request.code) != CHIP_NO_ERROR ||
DeviceLayer::GetCommissionableDataProvider()->SetSetupDiscriminator(request.discriminator) != CHIP_NO_ERROR)
if (mCommissionableDataProvider.SetSetupPasscode(request.code) != CHIP_NO_ERROR ||
mCommissionableDataProvider.SetSetupDiscriminator(request.discriminator) != CHIP_NO_ERROR)
{
return pw::Status::Unknown();
}
return pw::OkStatus();
}

private:
Internal::CommissionableDataProviderRpcWrapper mCommissionableDataProvider;
};

} // namespace rpc
Expand Down

0 comments on commit 2184561

Please sign in to comment.