Skip to content

Commit

Permalink
[Yaml] Add a test adding a new fabric from an existing fabric (#25969)
Browse files Browse the repository at this point in the history
* [chip-tool] Add GetCommissionerRootCertificate command

[chip-tool] Add IssueNocChain command

* Add src/app/tests/suites/Test_AddNewFabricFromExistingFabric.yaml test working with the chip-tool python yaml runner

---------

Co-authored-by: Andrei Litvin <[email protected]>
  • Loading branch information
2 people authored and pull[bot] committed Dec 22, 2023
1 parent 75818dd commit 2027330
Show file tree
Hide file tree
Showing 15 changed files with 483 additions and 16 deletions.
1 change: 1 addition & 0 deletions examples/chip-tool/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ static_library("chip-tool-utils") {
"commands/pairing/OpenCommissioningWindowCommand.cpp",
"commands/pairing/OpenCommissioningWindowCommand.h",
"commands/pairing/PairingCommand.cpp",
"commands/pairing/ToTLVCert.cpp",
"commands/payload/AdditionalDataParseCommand.cpp",
"commands/payload/SetupPayloadGenerateCommand.cpp",
"commands/payload/SetupPayloadParseCommand.cpp",
Expand Down
37 changes: 25 additions & 12 deletions examples/chip-tool/commands/common/CHIPCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,22 @@ CHIP_ERROR CHIPCommand::GetIdentityNodeId(std::string identity, chip::NodeId * n
return CHIP_NO_ERROR;
}

CHIP_ERROR CHIPCommand::GetIdentityRootCertificate(std::string identity, chip::ByteSpan & span)
{
if (identity == kIdentityNull)
{
return CHIP_ERROR_NOT_FOUND;
}

chip::NodeId nodeId;
VerifyOrDie(GetIdentityNodeId(identity, &nodeId) == CHIP_NO_ERROR);
CommissionerIdentity lookupKey{ identity, nodeId };
auto item = mCommissioners.find(lookupKey);

span = chip::ByteSpan(item->first.mRCAC, item->first.mRCACLen);
return CHIP_NO_ERROR;
}

chip::FabricId CHIPCommand::CurrentCommissionerId()
{
chip::FabricId id;
Expand Down Expand Up @@ -388,21 +404,13 @@ void CHIPCommand::ShutdownCommissioner(const CommissionerIdentity & key)
mCommissioners[key].get()->Shutdown();
}

CHIP_ERROR CHIPCommand::InitializeCommissioner(const CommissionerIdentity & identity, chip::FabricId fabricId)
CHIP_ERROR CHIPCommand::InitializeCommissioner(CommissionerIdentity & identity, chip::FabricId fabricId)
{
chip::Platform::ScopedMemoryBuffer<uint8_t> noc;
chip::Platform::ScopedMemoryBuffer<uint8_t> icac;
chip::Platform::ScopedMemoryBuffer<uint8_t> rcac;

std::unique_ptr<ChipDeviceCommissioner> commissioner = std::make_unique<ChipDeviceCommissioner>();
chip::Controller::SetupParams commissionerParams;

ReturnLogErrorOnFailure(mCredIssuerCmds->SetupDeviceAttestation(commissionerParams, sTrustStore));

VerifyOrReturnError(noc.Alloc(chip::Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
VerifyOrReturnError(icac.Alloc(chip::Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
VerifyOrReturnError(rcac.Alloc(chip::Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);

chip::Crypto::P256Keypair ephemeralKey;

if (fabricId != chip::kUndefinedFabricId)
Expand All @@ -420,15 +428,20 @@ CHIP_ERROR CHIPCommand::InitializeCommissioner(const CommissionerIdentity & iden

ReturnLogErrorOnFailure(mCredIssuerCmds->InitializeCredentialsIssuer(mCommissionerStorage));

chip::MutableByteSpan nocSpan(noc.Get(), chip::Controller::kMaxCHIPDERCertLength);
chip::MutableByteSpan icacSpan(icac.Get(), chip::Controller::kMaxCHIPDERCertLength);
chip::MutableByteSpan rcacSpan(rcac.Get(), chip::Controller::kMaxCHIPDERCertLength);
chip::MutableByteSpan nocSpan(identity.mNOC);
chip::MutableByteSpan icacSpan(identity.mICAC);
chip::MutableByteSpan rcacSpan(identity.mRCAC);

ReturnLogErrorOnFailure(ephemeralKey.Initialize(chip::Crypto::ECPKeyTarget::ECDSA));

ReturnLogErrorOnFailure(mCredIssuerCmds->GenerateControllerNOCChain(identity.mLocalNodeId, fabricId,
mCommissionerStorage.GetCommissionerCATs(),
ephemeralKey, rcacSpan, icacSpan, nocSpan));

identity.mRCACLen = rcacSpan.size();
identity.mICACLen = icacSpan.size();
identity.mNOCLen = nocSpan.size();

commissionerParams.operationalKeypair = &ephemeralKey;
commissionerParams.controllerRCAC = rcacSpan;
commissionerParams.controllerICAC = icacSpan;
Expand Down
10 changes: 9 additions & 1 deletion examples/chip-tool/commands/common/CHIPCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ class CHIPCommand : public Command

std::string GetIdentity();
CHIP_ERROR GetIdentityNodeId(std::string identity, chip::NodeId * nodeId);
CHIP_ERROR GetIdentityRootCertificate(std::string identity, chip::ByteSpan & span);
void SetIdentity(const char * name);

// This method returns the commissioner instance to be used for running the command.
Expand All @@ -179,12 +180,19 @@ class CHIPCommand : public Command
}
std::string mName;
chip::NodeId mLocalNodeId;
uint8_t mRCAC[chip::Controller::kMaxCHIPDERCertLength] = {};
uint8_t mICAC[chip::Controller::kMaxCHIPDERCertLength] = {};
uint8_t mNOC[chip::Controller::kMaxCHIPDERCertLength] = {};

size_t mRCACLen;
size_t mICACLen;
size_t mNOCLen;
};

// InitializeCommissioner uses various members, so can't be static. This is
// obviously a little odd, since the commissioners are then shared across
// multiple commands in interactive mode...
CHIP_ERROR InitializeCommissioner(const CommissionerIdentity & identity, chip::FabricId fabricId);
CHIP_ERROR InitializeCommissioner(CommissionerIdentity & identity, chip::FabricId fabricId);
void ShutdownCommissioner(const CommissionerIdentity & key);
chip::FabricId CurrentCommissionerId();

Expand Down
32 changes: 32 additions & 0 deletions examples/chip-tool/commands/common/RemoteDataModelLogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ constexpr const char * kErrorIdKey = "error";
constexpr const char * kClusterErrorIdKey = "clusterError";
constexpr const char * kValueKey = "value";
constexpr const char * kNodeIdKey = "nodeId";
constexpr const char * kNOCKey = "NOC";
constexpr const char * kICACKey = "ICAC";
constexpr const char * kRCACKey = "RCAC";
constexpr const char * kIPKKey = "IPK";

namespace {
RemoteDataModelLoggerDelegate * gDelegate;
Expand All @@ -53,6 +57,7 @@ CHIP_ERROR LogError(Json::Value & value, const chip::app::StatusIB & status)
auto valueStr = chip::JsonToString(value);
return gDelegate->LogJSON(valueStr.c_str());
}

} // namespace

namespace RemoteDataModelLogger {
Expand Down Expand Up @@ -165,6 +170,33 @@ CHIP_ERROR LogGetCommissionerNodeId(chip::NodeId value)
return gDelegate->LogJSON(valueStr.c_str());
}

CHIP_ERROR LogGetCommissionerRootCertificate(const char * value)
{
VerifyOrReturnError(gDelegate != nullptr, CHIP_NO_ERROR);

Json::Value rootValue;
rootValue[kValueKey] = Json::Value();
rootValue[kValueKey][kRCACKey] = value;

auto valueStr = chip::JsonToString(rootValue);
return gDelegate->LogJSON(valueStr.c_str());
}

CHIP_ERROR LogIssueNOCChain(const char * noc, const char * icac, const char * rcac, const char * ipk)
{
VerifyOrReturnError(gDelegate != nullptr, CHIP_NO_ERROR);

Json::Value rootValue;
rootValue[kValueKey] = Json::Value();
rootValue[kValueKey][kNOCKey] = noc;
rootValue[kValueKey][kICACKey] = icac;
rootValue[kValueKey][kRCACKey] = rcac;
rootValue[kValueKey][kIPKKey] = ipk;

auto valueStr = chip::JsonToString(rootValue);
return gDelegate->LogJSON(valueStr.c_str());
}

CHIP_ERROR LogDiscoveredNodeData(const chip::Dnssd::DiscoveredNodeData & nodeData)
{
VerifyOrReturnError(gDelegate != nullptr, CHIP_NO_ERROR);
Expand Down
3 changes: 3 additions & 0 deletions examples/chip-tool/commands/common/RemoteDataModelLogger.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <app/ConcreteCommandPath.h>
#include <app/EventHeader.h>
#include <app/MessageDef/StatusIB.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/dnssd/Resolver.h>

class RemoteDataModelLoggerDelegate
Expand All @@ -40,6 +41,8 @@ CHIP_ERROR LogEventAsJSON(const chip::app::EventHeader & header, chip::TLV::TLVR
CHIP_ERROR LogErrorAsJSON(const chip::app::EventHeader & header, const chip::app::StatusIB & status);
CHIP_ERROR LogErrorAsJSON(const CHIP_ERROR & error);
CHIP_ERROR LogGetCommissionerNodeId(chip::NodeId value);
CHIP_ERROR LogGetCommissionerRootCertificate(const char * value);
CHIP_ERROR LogIssueNOCChain(const char * noc, const char * icac, const char * rcac, const char * ipk);
CHIP_ERROR LogDiscoveredNodeData(const chip::Dnssd::DiscoveredNodeData & nodeData);
void SetDelegate(RemoteDataModelLoggerDelegate * delegate);
}; // namespace RemoteDataModelLogger
4 changes: 4 additions & 0 deletions examples/chip-tool/commands/pairing/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "commands/common/Commands.h"
#include "commands/pairing/CloseSessionCommand.h"
#include "commands/pairing/GetCommissionerNodeIdCommand.h"
#include "commands/pairing/GetCommissionerRootCertificateCommand.h"
#include "commands/pairing/IssueNOCChainCommand.h"
#include "commands/pairing/OpenCommissioningWindowCommand.h"
#include "commands/pairing/PairingCommand.h"

Expand Down Expand Up @@ -241,6 +243,8 @@ void registerCommandsPairing(Commands & commands, CredentialIssuerCommands * cre
make_unique<OpenCommissioningWindowCommand>(credsIssuerConfig),
make_unique<CloseSessionCommand>(credsIssuerConfig),
make_unique<GetCommissionerNodeIdCommand>(credsIssuerConfig),
make_unique<GetCommissionerRootCertificateCommand>(credsIssuerConfig),
make_unique<IssueNOCChainCommand>(credsIssuerConfig),
};

commands.Register(clusterName, clusterCommands);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2023 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 "../common/CHIPCommand.h"

#include "ToTLVCert.h"

class GetCommissionerRootCertificateCommand : public CHIPCommand
{
public:
GetCommissionerRootCertificateCommand(CredentialIssuerCommands * credIssuerCommands) :
CHIPCommand("get-commissioner-root-certificate", credIssuerCommands,
"Returns a base64-encoded RCAC prefixed with: 'base64:'")
{}

/////////// CHIPCommand Interface /////////
CHIP_ERROR RunCommand() override
{
chip::ByteSpan span;
ReturnErrorOnFailure(GetIdentityRootCertificate(GetIdentity(), span));

std::string rcac;
ReturnErrorOnFailure(ToTLVCert(span, rcac));
ChipLogProgress(chipTool, "RCAC: %s", rcac.c_str());

ReturnErrorOnFailure(RemoteDataModelLogger::LogGetCommissionerRootCertificate(rcac.c_str()));

SetCommandExitStatus(CHIP_NO_ERROR);
return CHIP_NO_ERROR;
}

chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(10); }
};
86 changes: 86 additions & 0 deletions examples/chip-tool/commands/pairing/IssueNOCChainCommand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2023 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 "../common/CHIPCommand.h"

#include "ToTLVCert.h"

class IssueNOCChainCommand : public CHIPCommand
{
public:
IssueNOCChainCommand(CredentialIssuerCommands * credIssuerCommands) :
CHIPCommand("issue-noc-chain", credIssuerCommands,
"Returns a base64-encoded NOC, ICAC, RCAC, and IPK prefixed with: 'base64:'"),
mDeviceNOCChainCallback(OnDeviceNOCChainGeneration, this)
{
AddArgument("elements", &mNOCSRElements, "NOCSRElements encoded in hexadecimal");
AddArgument("node-id", 0, UINT64_MAX, &mNodeId, "The target node id");
}

/////////// CHIPCommand Interface /////////
CHIP_ERROR RunCommand() override
{
auto & commissioner = CurrentCommissioner();
ReturnErrorOnFailure(commissioner.IssueNOCChain(mNOCSRElements, mNodeId, &mDeviceNOCChainCallback));
return CHIP_NO_ERROR;
}

chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(10); }

static void OnDeviceNOCChainGeneration(void * context, CHIP_ERROR status, const chip::ByteSpan & noc,
const chip::ByteSpan & icac, const chip::ByteSpan & rcac,
chip::Optional<chip::IdentityProtectionKeySpan> ipk,
chip::Optional<chip::NodeId> adminSubject)
{
auto command = static_cast<IssueNOCChainCommand *>(context);

auto err = status;
VerifyOrReturn(CHIP_NO_ERROR == err, command->SetCommandExitStatus(err));

std::string nocStr;
err = ToTLVCert(noc, nocStr);
VerifyOrReturn(CHIP_NO_ERROR == err, command->SetCommandExitStatus(err));
ChipLogProgress(chipTool, "NOC: %s", nocStr.c_str());

std::string icacStr;
err = ToTLVCert(icac, icacStr);
VerifyOrReturn(CHIP_NO_ERROR == err, command->SetCommandExitStatus(err));
ChipLogProgress(chipTool, "ICAC: %s", icacStr.c_str());

std::string rcacStr;
err = ToTLVCert(rcac, rcacStr);
VerifyOrReturn(CHIP_NO_ERROR == err, command->SetCommandExitStatus(err));
ChipLogProgress(chipTool, "RCAC: %s", rcacStr.c_str());

auto ipkValue = ipk.ValueOr(chip::Crypto::IdentityProtectionKeySpan());
std::string ipkStr;
err = ToBase64(ipkValue, ipkStr);
VerifyOrReturn(CHIP_NO_ERROR == err, command->SetCommandExitStatus(err));
ChipLogProgress(chipTool, "IPK: %s", ipkStr.c_str());

err = RemoteDataModelLogger::LogIssueNOCChain(nocStr.c_str(), icacStr.c_str(), rcacStr.c_str(), ipkStr.c_str());
command->SetCommandExitStatus(err);
}

private:
chip::Callback::Callback<chip::Controller::OnNOCChainGeneration> mDeviceNOCChainCallback;
chip::ByteSpan mNOCSRElements;
chip::NodeId mNodeId;
};
52 changes: 52 additions & 0 deletions examples/chip-tool/commands/pairing/ToTLVCert.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2023 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.
*
*/

#include "ToTLVCert.h"

#include <credentials/CHIPCert.h>
#include <lib/support/Base64.h>

constexpr const char kBase64Header[] = "base64:";
constexpr size_t kBase64HeaderLen = ArraySize(kBase64Header) - 1;

CHIP_ERROR ToBase64(const chip::ByteSpan & input, std::string & outputAsPrefixedBase64)
{
chip::Platform::ScopedMemoryBuffer<char> base64String;
base64String.Alloc(kBase64HeaderLen + BASE64_ENCODED_LEN(input.size()) + 1);
VerifyOrReturnError(base64String.Get() != nullptr, CHIP_ERROR_NO_MEMORY);

auto encodedLen = chip::Base64Encode(input.data(), static_cast<uint16_t>(input.size()), base64String.Get() + kBase64HeaderLen);
if (encodedLen)
{
memcpy(base64String.Get(), kBase64Header, kBase64HeaderLen);
encodedLen = static_cast<uint16_t>(encodedLen + kBase64HeaderLen);
}
base64String.Get()[encodedLen] = '\0';
outputAsPrefixedBase64 = std::string(base64String.Get(), encodedLen);

return CHIP_NO_ERROR;
}

CHIP_ERROR ToTLVCert(const chip::ByteSpan & derEncodedCertificate, std::string & tlvCertAsPrefixedBase64)
{
uint8_t chipCertBuffer[chip::Credentials::kMaxCHIPCertLength];
chip::MutableByteSpan chipCertBytes(chipCertBuffer);
ReturnErrorOnFailure(chip::Credentials::ConvertX509CertToChipCert(derEncodedCertificate, chipCertBytes));
ReturnErrorOnFailure(ToBase64(chipCertBytes, tlvCertAsPrefixedBase64));
return CHIP_NO_ERROR;
}
Loading

0 comments on commit 2027330

Please sign in to comment.