Skip to content

Commit

Permalink
Support async NOC generation via op cred delegate API (#7863)
Browse files Browse the repository at this point in the history
* Support async NOC generation via op cred delegate API

* address review comments

* remove nodeId from NOC callback

* fix Android build

* Update src/controller/CHIPDeviceController.cpp

Co-authored-by: Michael Spang <mcspang@gmail.com>

* Update src/controller/CHIPDeviceController.cpp

Co-authored-by: Michael Spang <mcspang@gmail.com>

Co-authored-by: Michael Spang <mcspang@gmail.com>
  • Loading branch information
2 people authored and pull[bot] committed Aug 23, 2021
1 parent fb87ac1 commit 1072030
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 100 deletions.
130 changes: 88 additions & 42 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,9 @@ using namespace chip::Encoding;

constexpr uint32_t kSessionEstablishmentTimeout = 30 * kMillisecondPerSecond;

// TODO - Reduce memory requirement for generating x509 certificates
// As per specifications (section 6.3.7. Trusted Root CA Certificates), DER certs
// should require 600 bytes. Currently, due to ASN writer overheads, a larger buffer
// is needed, even though the generated certificate fits in 600 bytes limit.
constexpr uint32_t kMaxCHIPDERCertLength = 1024;
constexpr uint32_t kMaxCHIPCSRLength = 1024;
constexpr uint32_t kMaxCHIPCSRLength = 1024;

DeviceController::DeviceController()
DeviceController::DeviceController() : mLocalNOCCallback(OnLocalNOCGenerated, this)
{
mState = State::NotInitialized;
mSessionMgr = nullptr;
Expand Down Expand Up @@ -215,46 +210,67 @@ CHIP_ERROR DeviceController::Init(NodeId localDeviceId, ControllerInitParams par
return CHIP_NO_ERROR;
}

CHIP_ERROR DeviceController::GenerateOperationalCertificates(const ByteSpan & CSR, NodeId deviceId, MutableByteSpan & cert)
CHIP_ERROR DeviceController::GenerateOperationalCertificates(const ByteSpan & noc, MutableByteSpan & cert)
{
// This code requires about 2K RAM to generate the certificates.
// This code requires about 1K RAM to generate the certificates.
// The code would run as part of commissioner applications, so RAM requirements should be fine.
// Need to analyze if this requirement could be better managed by using static memory pools.
chip::Platform::ScopedMemoryBuffer<uint8_t> noc;
ReturnErrorCodeIf(!noc.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);

uint32_t nocLen = 0;
ChipLogProgress(Controller, "Generating operational certificate for device " ChipLogFormatX64, ChipLogValueX64(deviceId));
ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNodeOperationalCertificate(
PeerId().SetNodeId(deviceId), CSR, 1, noc.Get(), kMaxCHIPDERCertLength, nocLen));

ReturnErrorCodeIf(nocLen == 0, CHIP_ERROR_CERT_NOT_FOUND);

chip::Platform::ScopedMemoryBuffer<uint8_t> ica;
ReturnErrorCodeIf(!ica.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);

uint32_t icaLen = 0;
MutableByteSpan icaCertSpan(ica.Get(), kMaxCHIPDERCertLength);

ChipLogProgress(Controller, "Getting intermediate CA certificate from the issuer");
CHIP_ERROR err = mOperationalCredentialsDelegate->GetIntermediateCACertificate(0, ica.Get(), kMaxCHIPDERCertLength, icaLen);
CHIP_ERROR err = mOperationalCredentialsDelegate->GetIntermediateCACertificate(0, icaCertSpan);
ChipLogProgress(Controller, "GetIntermediateCACertificate returned %" PRId32, err);
if (err == CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED)
{
// This implies that the commissioner application uses root CA to sign the operational
// certificates, and an intermediate CA is not needed. It's not an error condition, so
// let's just send operational certificate and root CA certificate to the device.
icaLen = 0;
icaCertSpan.reduce_size(0);
ChipLogProgress(Controller, "Intermediate CA is not needed");
}
else if (err != CHIP_NO_ERROR)
{
return err;
}

ReturnErrorOnFailure(ConvertX509CertsToChipCertArray(ByteSpan(noc.Get(), nocLen), ByteSpan(ica.Get(), icaLen), cert));
ReturnErrorOnFailure(ConvertX509CertsToChipCertArray(noc, icaCertSpan, cert));

return CHIP_NO_ERROR;
}

void DeviceController::OnLocalNOCGenerated(void * context, const ByteSpan & noc)
{
CHIP_ERROR err = CHIP_NO_ERROR;

DeviceController * controller = static_cast<DeviceController *>(context);

Transport::AdminPairingInfo * const admin = controller->mAdmins.FindAdminWithId(controller->mAdminId);

uint32_t chipCertAllocatedLen = kMaxCHIPCertLength * 2;
chip::Platform::ScopedMemoryBuffer<uint8_t> chipCert;

VerifyOrExit(admin != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(chipCert.Alloc(chipCertAllocatedLen), err = CHIP_ERROR_NO_MEMORY);

{
MutableByteSpan chipCertSpan(chipCert.Get(), chipCertAllocatedLen);
err = controller->GenerateOperationalCertificates(noc, chipCertSpan);
SuccessOrExit(err);

err = admin->SetOperationalCert(chipCertSpan);
SuccessOrExit(err);
}

exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed in generating local operational credentials. Error %s", ErrorStr(err));
}
}

CHIP_ERROR DeviceController::LoadLocalCredentials(Transport::AdminPairingInfo * admin)
{
ChipLogProgress(Controller, "Getting operational keys");
Expand All @@ -274,12 +290,11 @@ CHIP_ERROR DeviceController::LoadLocalCredentials(Transport::AdminPairingInfo *
ChipLogProgress(Controller, "Getting root certificate for the controller from the issuer");
chip::Platform::ScopedMemoryBuffer<uint8_t> rootCert;
ReturnErrorCodeIf(!rootCert.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
uint32_t rootCertLen = 0;

ReturnErrorOnFailure(
mOperationalCredentialsDelegate->GetRootCACertificate(0, rootCert.Get(), kMaxCHIPDERCertLength, rootCertLen));
ReturnErrorOnFailure(
ConvertX509CertToChipCert(rootCert.Get(), rootCertLen, chipCert.Get(), chipCertAllocatedLen, chipCertLen));
MutableByteSpan rootCertSpan(rootCert.Get(), kMaxCHIPDERCertLength);
ReturnErrorOnFailure(mOperationalCredentialsDelegate->GetRootCACertificate(0, rootCertSpan));
VerifyOrReturnError(CanCastTo<uint32_t>(rootCertSpan.size()), CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(ConvertX509CertToChipCert(rootCertSpan.data(), static_cast<uint32_t>(rootCertSpan.size()),
chipCert.Get(), chipCertAllocatedLen, chipCertLen));

ReturnErrorOnFailure(admin->SetRootCert(ByteSpan(chipCert.Get(), chipCertLen)));
}
Expand All @@ -295,9 +310,8 @@ CHIP_ERROR DeviceController::LoadLocalCredentials(Transport::AdminPairingInfo *
// TODO - Match the generated cert against CSR and operational keypair
// Make sure it chains back to the trusted root.
ChipLogProgress(Controller, "Generating operational certificate for the controller");
MutableByteSpan chipCertSpan(chipCert.Get(), chipCertAllocatedLen);
ReturnErrorOnFailure(GenerateOperationalCertificates(ByteSpan(CSR.Get(), csrLength), mLocalDeviceId, chipCertSpan));
ReturnErrorOnFailure(admin->SetOperationalCert(chipCertSpan));
ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNodeOperationalCertificate(
Optional<NodeId>(mLocalDeviceId), 0, ByteSpan(CSR.Get(), csrLength), ByteSpan(), &mLocalNOCCallback));
}

ReturnErrorOnFailure(mAdmins.Store(admin->GetAdminId()));
Expand Down Expand Up @@ -802,7 +816,7 @@ DeviceCommissioner::DeviceCommissioner() :
mOpCertResponseCallback(OnOperationalCertificateAddResponse, this), mRootCertResponseCallback(OnRootCertSuccessResponse, this),
mOnCSRFailureCallback(OnCSRFailureResponse, this), mOnCertFailureCallback(OnAddOpCertFailureResponse, this),
mOnRootCertFailureCallback(OnRootCertFailureResponse, this), mOnDeviceConnectedCallback(OnDeviceConnectedFn, this),
mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this)
mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this), mDeviceNOCCallback(OnDeviceNOCGenerated, this)
{
mPairingDelegate = nullptr;
mDeviceBeingPaired = kNumMaxActiveDevices;
Expand Down Expand Up @@ -1217,6 +1231,43 @@ void DeviceCommissioner::OnOperationalCertificateSigningRequest(void * context,
}
}

void DeviceCommissioner::OnDeviceNOCGenerated(void * context, const ByteSpan & noc)
{
CHIP_ERROR err = CHIP_NO_ERROR;

DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context);

// The operational certificate array can contain upto 2 certificates (NOC, and ICAC)
// The memory is allocated to account for both these certificates.
uint32_t chipCertAllocatedLen = kMaxCHIPCertLength * 2;
chip::Platform::ScopedMemoryBuffer<uint8_t> chipCert;

Device * device = nullptr;
VerifyOrExit(commissioner->mState == State::Initialized, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(commissioner->mDeviceBeingPaired < kNumMaxActiveDevices, err = CHIP_ERROR_INCORRECT_STATE);

device = &commissioner->mActiveDevices[commissioner->mDeviceBeingPaired];

VerifyOrExit(chipCert.Alloc(chipCertAllocatedLen), err = CHIP_ERROR_NO_MEMORY);

{
MutableByteSpan chipCertSpan(chipCert.Get(), chipCertAllocatedLen);
err = commissioner->GenerateOperationalCertificates(noc, chipCertSpan);
SuccessOrExit(err);

ChipLogProgress(Controller, "Sending operational certificate to the device");
err = commissioner->SendOperationalCertificate(device, chipCertSpan);
SuccessOrExit(err);
}

exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed in generating device's operational credentials. Error %s", ErrorStr(err));
commissioner->OnSessionEstablishmentError(err);
}
}

CHIP_ERROR DeviceCommissioner::ProcessOpCSR(const ByteSpan & CSR, const ByteSpan & CSRNonce, const ByteSpan & VendorReserved1,
const ByteSpan & VendorReserved2, const ByteSpan & VendorReserved3,
const ByteSpan & Signature)
Expand All @@ -1226,9 +1277,6 @@ CHIP_ERROR DeviceCommissioner::ProcessOpCSR(const ByteSpan & CSR, const ByteSpan

Device * device = &mActiveDevices[mDeviceBeingPaired];

// TODO: Verify the OpCSR signature using pubkey from DAC
// This will be done when device attestation is implemented.

// Verify that Nonce matches with what we sent
const ByteSpan nonce = device->GetCSRNonce();
VerifyOrReturnError(CSRNonce.size() == nonce.size(), CHIP_ERROR_INVALID_ARGUMENT);
Expand All @@ -1237,12 +1285,10 @@ CHIP_ERROR DeviceCommissioner::ProcessOpCSR(const ByteSpan & CSR, const ByteSpan
chip::Platform::ScopedMemoryBuffer<uint8_t> chipOpCert;
ReturnErrorCodeIf(!chipOpCert.Alloc(kMaxCHIPCertLength * 2), CHIP_ERROR_NO_MEMORY);

MutableByteSpan chipCertSpan(chipOpCert.Get(), kMaxCHIPCertLength * 2);

ReturnErrorOnFailure(GenerateOperationalCertificates(CSR, device->GetDeviceId(), chipCertSpan));

ChipLogProgress(Controller, "Sending operational certificate to the device");
ReturnErrorOnFailure(SendOperationalCertificate(device, chipCertSpan));
// TODO: Send DAC as input parameter to GenerateNodeOperationalCertificate()
// This will be done when device attestation is implemented.
ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNodeOperationalCertificate(
Optional<NodeId>(device->GetDeviceId()), 0, CSR, ByteSpan(), &mDeviceNOCCallback));

return CHIP_NO_ERROR;
}
Expand Down
9 changes: 8 additions & 1 deletion src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ class DLL_EXPORT DeviceController : public Messaging::ExchangeDelegate,
// array can contain up to two certificates (node operational certificate, and ICA certificate).
// If the certificate issuer doesn't require an ICA (i.e. NOC is signed by the root CA), the array
// will have only one certificate (node operational certificate).
CHIP_ERROR GenerateOperationalCertificates(const ByteSpan & CSR, NodeId deviceId, MutableByteSpan & cert);
CHIP_ERROR GenerateOperationalCertificates(const ByteSpan & noc, MutableByteSpan & cert);

private:
//////////// ExchangeDelegate Implementation ///////////////
Expand All @@ -367,6 +367,9 @@ class DLL_EXPORT DeviceController : public Messaging::ExchangeDelegate,
void ReleaseAllDevices();

CHIP_ERROR LoadLocalCredentials(Transport::AdminPairingInfo * admin);

static void OnLocalNOCGenerated(void * context, const ByteSpan & noc);
Callback::Callback<NOCGenerated> mLocalNOCCallback;
};

/**
Expand Down Expand Up @@ -602,6 +605,8 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, public SessionEst
static void OnDeviceConnectedFn(void * context, Device * device);
static void OnDeviceConnectionFailureFn(void * context, NodeId deviceId, CHIP_ERROR error);

static void OnDeviceNOCGenerated(void * context, const ByteSpan & noc);

/**
* @brief
* This function processes the CSR sent by the device.
Expand Down Expand Up @@ -633,6 +638,8 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, public SessionEst
Callback::Callback<OnDeviceConnected> mOnDeviceConnectedCallback;
Callback::Callback<OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;

Callback::Callback<NOCGenerated> mDeviceNOCCallback;

PASESession mPairingSession;
};

Expand Down
45 changes: 35 additions & 10 deletions src/controller/ExampleOperationalCredentialsIssuer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <credentials/CHIPCert.h>
#include <support/CHIPMem.h>
#include <support/ScopedBuffer.h>

namespace chip {
namespace Controller {
Expand Down Expand Up @@ -62,26 +63,50 @@ CHIP_ERROR ExampleOperationalCredentialsIssuer::Initialize(PersistentStorageDele
return CHIP_NO_ERROR;
}

CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNodeOperationalCertificate(const PeerId & peerId, const ByteSpan & csr,
int64_t serialNumber, uint8_t * certBuf,
uint32_t certBufSize, uint32_t & outCertLen)
CHIP_ERROR
ExampleOperationalCredentialsIssuer::GenerateNodeOperationalCertificate(const Optional<NodeId> & nodeId, FabricId fabricId,
const ByteSpan & csr, const ByteSpan & DAC,
Callback::Callback<NOCGenerated> * onNOCGenerated)
{
VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE);
X509CertRequestParams request = { serialNumber, mIssuerId, mNow, mNow + mValidity, true, peerId.GetFabricId(),
true, peerId.GetNodeId() };
NodeId assignedId;
if (nodeId.HasValue())
{
assignedId = nodeId.Value();
}
else
{
assignedId = mNextAvailableNodeId++;
}

X509CertRequestParams request = { 1, mIssuerId, mNow, mNow + mValidity, true, fabricId, true, assignedId };

P256PublicKey pubkey;
ReturnErrorOnFailure(VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey));
return NewNodeOperationalX509Cert(request, CertificateIssuerLevel::kIssuerIsRootCA, pubkey, mIssuer, certBuf, certBufSize,
outCertLen);

chip::Platform::ScopedMemoryBuffer<uint8_t> noc;
ReturnErrorCodeIf(!noc.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
uint32_t nocLen = 0;

ReturnErrorOnFailure(NewNodeOperationalX509Cert(request, CertificateIssuerLevel::kIssuerIsRootCA, pubkey, mIssuer, noc.Get(),
kMaxCHIPDERCertLength, nocLen));

onNOCGenerated->mCall(onNOCGenerated->mContext, ByteSpan(noc.Get(), nocLen));

return CHIP_NO_ERROR;
}

CHIP_ERROR ExampleOperationalCredentialsIssuer::GetRootCACertificate(FabricId fabricId, uint8_t * certBuf, uint32_t certBufSize,
uint32_t & outCertLen)
CHIP_ERROR ExampleOperationalCredentialsIssuer::GetRootCACertificate(FabricId fabricId, MutableByteSpan & outCert)
{
VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE);
X509CertRequestParams request = { 0, mIssuerId, mNow, mNow + mValidity, true, fabricId, false, 0 };
return NewRootX509Cert(request, mIssuer, certBuf, certBufSize, outCertLen);

size_t outCertSize = (outCert.size() > UINT32_MAX) ? UINT32_MAX : outCert.size();
uint32_t outCertLen = 0;
ReturnErrorOnFailure(NewRootX509Cert(request, mIssuer, outCert.data(), static_cast<uint32_t>(outCertSize), outCertLen));
outCert.reduce_size(outCertLen);

return CHIP_NO_ERROR;
}

} // namespace Controller
Expand Down
8 changes: 5 additions & 3 deletions src/controller/ExampleOperationalCredentialsIssuer.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ class DLL_EXPORT ExampleOperationalCredentialsIssuer : public OperationalCredent
public:
virtual ~ExampleOperationalCredentialsIssuer() {}

CHIP_ERROR GenerateNodeOperationalCertificate(const PeerId & peerId, const ByteSpan & csr, int64_t serialNumber,
uint8_t * certBuf, uint32_t certBufSize, uint32_t & outCertLen) override;
CHIP_ERROR GenerateNodeOperationalCertificate(const Optional<NodeId> & nodeId, FabricId fabricId, const ByteSpan & csr,
const ByteSpan & DAC, Callback::Callback<NOCGenerated> * onNOCGenerated) override;

CHIP_ERROR GetRootCACertificate(FabricId fabricId, uint8_t * certBuf, uint32_t certBufSize, uint32_t & outCertLen) override;
CHIP_ERROR GetRootCACertificate(FabricId fabricId, MutableByteSpan & outCert) override;

/**
* @brief Initialize the issuer with the keypair in the storage.
Expand Down Expand Up @@ -75,6 +75,8 @@ class DLL_EXPORT ExampleOperationalCredentialsIssuer : public OperationalCredent

// By default, let's set validity to 10 years
uint32_t mValidity = 365 * 24 * 60 * 60 * 10;

NodeId mNextAvailableNodeId = 1;
};

} // namespace Controller
Expand Down
Loading

0 comments on commit 1072030

Please sign in to comment.