Skip to content

Commit

Permalink
Add IssueNOCChain and GetNodeId for chip-repl to use (#19890)
Browse files Browse the repository at this point in the history
Add IssueNOCChain and GetNodeId for chip-repl to use 

This is added to allow for manual testing AddNOC and UpdateNOC
using chip-repl.
  • Loading branch information
tehampson authored and pull[bot] committed Jul 18, 2023
1 parent 56de168 commit 2012834
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,27 @@ void DeviceCommissioner::OnDeviceNOCChainGeneration(void * context, CHIP_ERROR s
commissioner->CommissioningStageComplete(status, report);
}

CHIP_ERROR DeviceCommissioner::IssueNOCChain(const ByteSpan & NOCSRElements, NodeId nodeId,
chip::Callback::Callback<OnNOCChainGeneration> * callback)
{
MATTER_TRACE_EVENT_SCOPE("IssueNOCChain", "DeviceCommissioner");
VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE);

ChipLogProgress(Controller, "Getting certificate chain for the device on fabric idx %u",
static_cast<unsigned>(mFabricInfo->GetFabricIndex()));

mOperationalCredentialsDelegate->SetNodeIdForNextNOCRequest(nodeId);

if (mFabricInfo != nullptr)
{
mOperationalCredentialsDelegate->SetFabricIdForNextNOCRequest(mFabricInfo->GetFabricId());
}

// Note: attestationSignature, attestationChallenge, DAC, PAI are not used by existing OperationalCredentialsIssuer.
return mOperationalCredentialsDelegate->GenerateChipNOCChain(NOCSRElements, ByteSpan(), ByteSpan(), ByteSpan(), ByteSpan(),
ByteSpan(), callback);
}

CHIP_ERROR DeviceCommissioner::ProcessCSR(DeviceProxy * proxy, const ByteSpan & NOCSRElements,
const ByteSpan & AttestationSignature, const ByteSpan & dac, const ByteSpan & pai,
const ByteSpan & csrNonce)
Expand Down
6 changes: 6 additions & 0 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,12 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
// Commissioner will establish new device connections after PASE.
OperationalDeviceProxy * GetDeviceSession(const PeerId & peerId) override;

// Issue an NOC chain using the associated OperationalCredentialsDelegate.
// NOTE: This is only valid assuming that `mOperationalCredentialsDelegate` is what is desired
// to issue the NOC chain.
CHIP_ERROR IssueNOCChain(const ByteSpan & NOCSRElements, NodeId nodeId,
chip::Callback::Callback<OnNOCChainGeneration> * callback);

private:
DevicePairingDelegate * mPairingDelegate;

Expand Down
97 changes: 97 additions & 0 deletions src/controller/ExampleOperationalCredentialsIssuer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,103 @@ CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChain(const ByteSpan
return CHIP_NO_ERROR;
}

CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateChipNOCChain(const ByteSpan & csrElements, const ByteSpan & csrNonce,
const ByteSpan & attestationSignature,
const ByteSpan & attestationChallenge, const ByteSpan & DAC,
const ByteSpan & PAI,
Callback::Callback<OnNOCChainGeneration> * onCompletion)
{
VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE);
// At this point, Credential issuer may wish to validate the CSR information
(void) attestationChallenge;
(void) csrNonce;

NodeId assignedId;
if (mNodeIdRequested)
{
assignedId = mNextRequestedNodeId;
mNodeIdRequested = false;
}
else
{
assignedId = mNextAvailableNodeId++;
}

ChipLogProgress(Controller, "Verifying Certificate Signing Request");
TLVReader reader;
reader.Init(csrElements);

if (reader.GetType() == kTLVType_NotSpecified)
{
ReturnErrorOnFailure(reader.Next());
}

VerifyOrReturnError(reader.GetType() == kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE);
VerifyOrReturnError(reader.GetTag() == AnonymousTag(), CHIP_ERROR_UNEXPECTED_TLV_ELEMENT);

TLVType containerType;
ReturnErrorOnFailure(reader.EnterContainer(containerType));
ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, TLV::ContextTag(1)));

ByteSpan csr(reader.GetReadPoint(), reader.GetLength());
reader.ExitContainer(containerType);

P256PublicKey pubkey;
ReturnErrorOnFailure(VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey));

chip::Platform::ScopedMemoryBuffer<uint8_t> noc;
ReturnErrorCodeIf(!noc.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan nocSpan(noc.Get(), kMaxCHIPDERCertLength);

chip::Platform::ScopedMemoryBuffer<uint8_t> icac;
ReturnErrorCodeIf(!icac.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan icacSpan(icac.Get(), kMaxCHIPDERCertLength);

chip::Platform::ScopedMemoryBuffer<uint8_t> rcac;
ReturnErrorCodeIf(!rcac.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan rcacSpan(rcac.Get(), kMaxCHIPDERCertLength);

ReturnErrorOnFailure(
GenerateNOCChainAfterValidation(assignedId, mNextFabricId, chip::kUndefinedCATs, pubkey, rcacSpan, icacSpan, nocSpan));

// TODO(#13825): Should always generate some IPK. Using a temporary fixed value until APIs are plumbed in to set it end-to-end
// TODO: Force callers to set IPK if used before GenerateNOCChain will succeed.
ByteSpan defaultIpkSpan = chip::GroupTesting::DefaultIpkValue::GetDefaultIpk();

// The below static assert validates a key assumption in types used (needed for public API conformance)
static_assert(CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES == kAES_CCM128_Key_Length, "IPK span sizing must match");

// Prepare IPK to be sent back. A more fully-fledged operational credentials delegate
// would obtain a suitable key per fabric.
uint8_t ipkValue[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];
Crypto::AesCcm128KeySpan ipkSpan(ipkValue);

ReturnErrorCodeIf(defaultIpkSpan.size() != sizeof(ipkValue), CHIP_ERROR_INTERNAL);
memcpy(&ipkValue[0], defaultIpkSpan.data(), defaultIpkSpan.size());

chip::Platform::ScopedMemoryBuffer<uint8_t> chipNoc;
ReturnErrorCodeIf(!chipNoc.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan chipNocSpan(chipNoc.Get(), Credentials::kMaxCHIPCertLength);

chip::Platform::ScopedMemoryBuffer<uint8_t> chipIcac;
ReturnErrorCodeIf(!chipIcac.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan chipIcacSpan(chipIcac.Get(), Credentials::kMaxCHIPCertLength);

chip::Platform::ScopedMemoryBuffer<uint8_t> chipRcac;
ReturnErrorCodeIf(!chipRcac.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan chipRcacSpan(chipRcac.Get(), Credentials::kMaxCHIPCertLength);

ReturnErrorOnFailure(ConvertX509CertToChipCert(nocSpan, chipNocSpan));
ReturnErrorOnFailure(ConvertX509CertToChipCert(icacSpan, chipIcacSpan));
ReturnErrorOnFailure(ConvertX509CertToChipCert(rcacSpan, chipRcacSpan));

// Callback onto commissioner.
ChipLogProgress(Controller, "Providing certificate chain to the commissioner");
onCompletion->mCall(onCompletion->mContext, CHIP_NO_ERROR, chipNocSpan, chipIcacSpan, chipRcacSpan, MakeOptional(ipkSpan),
Optional<NodeId>());
return CHIP_NO_ERROR;
}

CHIP_ERROR ExampleOperationalCredentialsIssuer::GetRandomOperationalNodeId(NodeId * aNodeId)
{
for (int i = 0; i < 10; ++i)
Expand Down
4 changes: 4 additions & 0 deletions src/controller/ExampleOperationalCredentialsIssuer.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class DLL_EXPORT ExampleOperationalCredentialsIssuer : public OperationalCredent
const ByteSpan & attestationChallenge, const ByteSpan & DAC, const ByteSpan & PAI,
Callback::Callback<OnNOCChainGeneration> * onCompletion) override;

CHIP_ERROR GenerateChipNOCChain(const ByteSpan & csrElements, const ByteSpan & csrNonce, const ByteSpan & attestationSignature,
const ByteSpan & attestationChallenge, const ByteSpan & DAC, const ByteSpan & PAI,
Callback::Callback<OnNOCChainGeneration> * onCompletion) override;

void SetNodeIdForNextNOCRequest(NodeId nodeId) override
{
mNextRequestedNodeId = nodeId;
Expand Down
28 changes: 28 additions & 0 deletions src/controller/OperationalCredentialsDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,34 @@ class DLL_EXPORT OperationalCredentialsDelegate
const ByteSpan & DAC, const ByteSpan & PAI,
Callback::Callback<OnNOCChainGeneration> * onCompletion) = 0;

/**
* @brief
* This function generates an operational certificate chain for a remote device that is being commissioned.
* The API generates the certificate in CHIP cert form.
*
* The delegate is expected to use the certificate authority whose certificate
* is returned in `GetRootCACertificate()` API call.
*
* The delegate will call `onCompletion` when the NOC certificate chain is ready.
*
* @param[in] csrElements CSR elements as per specifications section 11.18.5.6. NOCSR Elements.
* @param[in] csrNonce CSR nonce as described in 6.4.6.1
* @param[in] attestationSignature Attestation signature as per specifications section 11.22.7.6. CSRResponse Command.
* @param[in] attestationChallenge Attestation challenge as per 11.18.5.7
* @param[in] DAC Device attestation certificate received from the device being commissioned
* @param[in] PAI Product Attestation Intermediate certificate
* @param[in] onCompletion Callback handler to provide generated NOC chain to the caller of GenerateNOCChain()
*
* @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code.
*/
virtual CHIP_ERROR GenerateChipNOCChain(const ByteSpan & csrElements, const ByteSpan & csrNonce,
const ByteSpan & attestationSignature, const ByteSpan & attestationChallenge,
const ByteSpan & DAC, const ByteSpan & PAI,
Callback::Callback<OnNOCChainGeneration> * onCompletion)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}

/**
* This function sets the node ID for which the next NOC Chain would be requested. The node ID is
* provided as a hint, and the delegate implementation may chose to ignore it and pick node ID of
Expand Down
1 change: 1 addition & 0 deletions src/controller/python/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ shared_library("ChipDeviceCtrl") {
if (chip_controller) {
sources += [
"ChipCommissionableNodeController-ScriptBinding.cpp",
"ChipDeviceController-IssueNocChain.cpp",
"ChipDeviceController-ScriptBinding.cpp",
"ChipDeviceController-ScriptDevicePairingDelegate.cpp",
"ChipDeviceController-ScriptDevicePairingDelegate.h",
Expand Down
73 changes: 73 additions & 0 deletions src/controller/python/ChipDeviceController-IssueNocChain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
*
* Copyright (c) 2022 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 <map>
#include <string>

#include <controller/CHIPDeviceController.h>
#include <lib/core/CHIPError.h>
#include <lib/support/Span.h>

typedef void PyObject;

using namespace chip;

extern "C" {

typedef void (*pychip_DeviceController_IssueNOCChainCallbackPythonCallback)(
PyObject * context, ChipError::StorageType status, const uint8_t * noc, size_t nocLen, const uint8_t * icac, size_t icacLen,
const uint8_t * rcac, size_t rcacLen, const uint8_t * ipk, size_t ipkLen, NodeId adminSubject);

static pychip_DeviceController_IssueNOCChainCallbackPythonCallback pychip_DeviceController_IssueNOCChainCallbackPythonCallbackFunct;

void pychip_DeviceController_SetIssueNOCChainCallbackPythonCallback(
pychip_DeviceController_IssueNOCChainCallbackPythonCallback callback)
{
pychip_DeviceController_IssueNOCChainCallbackPythonCallbackFunct = callback;
}

ChipError::StorageType pychip_DeviceController_IssueNOCChain(chip::Controller::DeviceCommissioner * devCtrl,
PyObject * pythonContext, uint8_t * NOCSRElements,
size_t NOCSRElementsLen, NodeId nodeId);
}

void pychip_DeviceController_IssueNOCChainCallback(void * context, CHIP_ERROR status, const ByteSpan & noc, const ByteSpan & icac,
const ByteSpan & rcac, Optional<Crypto::AesCcm128KeySpan> ipk,
Optional<NodeId> adminSubject)
{
if (pychip_DeviceController_IssueNOCChainCallbackPythonCallbackFunct != nullptr)
{
Crypto::AesCcm128KeySpan ipkData;
ipkData = ipk.ValueOr(Crypto::AesCcm128KeySpan());
pychip_DeviceController_IssueNOCChainCallbackPythonCallbackFunct(
context, status.AsInteger(), noc.data(), noc.size(), icac.data(), icac.size(), rcac.data(), rcac.size(), ipkData.data(),
ipk.HasValue() ? ipkData.size() : 0, adminSubject.ValueOr(kUndefinedNodeId));
}
}

ChipError::StorageType pychip_DeviceController_IssueNOCChain(chip::Controller::DeviceCommissioner * devCtrl,
PyObject * pythonContext, uint8_t * NOCSRElements,
size_t NOCSRElementsLen, NodeId nodeId)
{
return devCtrl
->IssueNOCChain(
ByteSpan(NOCSRElements, NOCSRElementsLen), nodeId,
/* Note: Memory leak here. This is a quick and a bit dirty PoC */
new Callback::Callback<Controller::OnNOCChainGeneration>(pychip_DeviceController_IssueNOCChainCallback, pythonContext))
.AsInteger();
}
7 changes: 7 additions & 0 deletions src/controller/python/ChipDeviceController-ScriptBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ ChipError::StorageType pychip_DeviceController_GetAddressAndPort(chip::Controlle
ChipError::StorageType pychip_DeviceController_GetCompressedFabricId(chip::Controller::DeviceCommissioner * devCtrl,
uint64_t * outFabricId);
ChipError::StorageType pychip_DeviceController_GetFabricId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outFabricId);
ChipError::StorageType pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outNodeId);

// Rendezvous
ChipError::StorageType pychip_DeviceController_ConnectBLE(chip::Controller::DeviceCommissioner * devCtrl, uint16_t discriminator,
Expand Down Expand Up @@ -274,6 +275,12 @@ ChipError::StorageType pychip_DeviceController_GetFabricId(chip::Controller::Dev
return CHIP_NO_ERROR.AsInteger();
}

ChipError::StorageType pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommissioner * devCtrl, uint64_t * outNodeId)
{
*outNodeId = devCtrl->GetNodeId();
return CHIP_NO_ERROR.AsInteger();
}

const char * pychip_DeviceController_ErrorToString(ChipError::StorageType err)
{
return chip::ErrorStr(CHIP_ERROR(err));
Expand Down
8 changes: 8 additions & 0 deletions src/controller/python/OpCredsBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ class OperationalCredentialsAdapter : public OperationalCredentialsDelegate
onCompletion);
}

CHIP_ERROR GenerateChipNOCChain(const ByteSpan & csrElements, const ByteSpan & csrNonce, const ByteSpan & attestationSignature,
const ByteSpan & attestationChallenge, const ByteSpan & DAC, const ByteSpan & PAI,
Callback::Callback<OnNOCChainGeneration> * onCompletion) override
{
return mExampleOpCredsIssuer.GenerateChipNOCChain(csrElements, csrNonce, attestationSignature, attestationChallenge, DAC,
PAI, onCompletion);
}

void SetNodeIdForNextNOCRequest(NodeId nodeId) override { mExampleOpCredsIssuer.SetNodeIdForNextNOCRequest(nodeId); }

void SetFabricIdForNextNOCRequest(FabricId fabricId) override { mExampleOpCredsIssuer.SetFabricIdForNextNOCRequest(fabricId); }
Expand Down
Loading

0 comments on commit 2012834

Please sign in to comment.