Skip to content

Commit

Permalink
Define OperationalCredentialsDelegate interface and implement for Dar…
Browse files Browse the repository at this point in the history
…win (#6474)

* Define OperationalCredentialsDelegate interface and implement for Darwin

* Update src/controller/OperationalCredentialsDelegate.h

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Update src/controller/OperationalCredentialsDelegate.h

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Update src/controller/OperationalCredentialsDelegate.h

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* address review comments

* address review comments

* Fix build errors

* Fix build.

* disable use of SecItemAdd for unit tests

* use scoped buffer instead of unique_ptr

* cleanup

Co-authored-by: Justin Wood <woody@apple.com>
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
  • Loading branch information
3 people authored and pull[bot] committed Jul 2, 2021
1 parent 0fb2e70 commit fbee997
Show file tree
Hide file tree
Showing 8 changed files with 520 additions and 4 deletions.
73 changes: 72 additions & 1 deletion src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <support/CodeUtils.h>
#include <support/ErrorStr.h>
#include <support/SafeInt.h>
#include <support/ScopedBuffer.h>
#include <support/TimeUtils.h>
#include <support/logging/CHIPLogging.h>

Expand All @@ -60,6 +61,7 @@

#include <errno.h>
#include <inttypes.h>
#include <memory>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
Expand All @@ -80,7 +82,9 @@ constexpr const char kNextAvailableKeyID[] = "StartKeyID";
constexpr uint16_t kMdnsPort = 5353;
#endif

constexpr const uint32_t kSessionEstablishmentTimeout = 30 * kMillisecondPerSecond;
constexpr uint32_t kSessionEstablishmentTimeout = 30 * kMillisecondPerSecond;

constexpr uint32_t kMaxCHIPOpCertLength = 600;

// This macro generates a key using node ID an key prefix, and performs the given action
// on that key.
Expand Down Expand Up @@ -649,6 +653,8 @@ CHIP_ERROR DeviceCommissioner::Init(NodeId localDeviceId, CommissionerInitParams
}

mPairingDelegate = params.pairingDelegate;

mOperationalCredentialsDelegate = params.operationalCredentialsDelegate;
return CHIP_NO_ERROR;
}

Expand Down Expand Up @@ -915,6 +921,16 @@ void DeviceCommissioner::OnSessionEstablished()
}

ChipLogDetail(Controller, "Remote device completed SPAKE2+ handshake\n");

// TODO: Add code to receive OpCSR from the device, and process the signing request
err = SendOperationalCertificateSigningRequestCommand(device->GetDeviceId());
if (err != CHIP_NO_ERROR)
{
ChipLogError(Ble, "Failed in sending opcsr request command to the device: err %s", ErrorStr(err));
OnSessionEstablishmentError(err);
return;
}

mPairingSession.ToSerializable(device->GetPairing());
mSystemLayer->CancelTimer(OnSessionEstablishmentTimeoutCallback, this);

Expand All @@ -936,6 +952,61 @@ void DeviceCommissioner::OnSessionEstablished()
RendezvousCleanup(CHIP_NO_ERROR);
}

CHIP_ERROR DeviceCommissioner::SendOperationalCertificateSigningRequestCommand(NodeId remoteDeviceId)
{
// TODO: Call OperationalCredentials cluster API to send command
return CHIP_NO_ERROR;
}

CHIP_ERROR DeviceCommissioner::OnOperationalCertificateSigningRequest(NodeId node, const ByteSpan & csr)
{
ReturnErrorCodeIf(mOperationalCredentialsDelegate == nullptr, CHIP_ERROR_INCORRECT_STATE);

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

uint32_t opCertLen = 0;
ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNodeOperationalCertificate(
PeerId().SetNodeId(node), csr, 0, opCert.Get(), kMaxCHIPOpCertLength, opCertLen));

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

uint32_t signingCertLen = 0;
CHIP_ERROR err =
mOperationalCredentialsDelegate->GetIntermediateCACertificate(0, signingCert.Get(), kMaxCHIPOpCertLength, signingCertLen);
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.
err = CHIP_NO_ERROR;
signingCertLen = 0;
}
ReturnErrorOnFailure(err);

ReturnErrorOnFailure(
SendOperationalCertificate(node, ByteSpan(opCert.Get(), opCertLen), ByteSpan(signingCert.Get(), signingCertLen)));

ReturnErrorOnFailure(
mOperationalCredentialsDelegate->GetRootCACertificate(0, signingCert.Get(), kMaxCHIPOpCertLength, signingCertLen));

return SendTrustedRootCertificate(node, ByteSpan(signingCert.Get(), signingCertLen));
}

CHIP_ERROR DeviceCommissioner::SendOperationalCertificate(NodeId remoteDeviceId, const ByteSpan & opCertBuf,
const ByteSpan & icaCertBuf)
{
// TODO: Call OperationalCredentials cluster API to add operational credentials on the device
return CHIP_NO_ERROR;
}

CHIP_ERROR DeviceCommissioner::SendTrustedRootCertificate(NodeId remoteDeviceId, const ByteSpan & certBuf)
{
// TODO: Call TrustedRootCertificate cluster API to add root certificate on the device
return CHIP_NO_ERROR;
}

void DeviceCommissioner::PersistDeviceList()
{
if (mStorageDelegate != nullptr && mPairedDevicesUpdated && mState == State::Initialized)
Expand Down
13 changes: 10 additions & 3 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include <app/InteractionModelDelegate.h>
#include <controller/CHIPDevice.h>
#include <controller/OperationalCredentialsDelegate.h>
#include <core/CHIPCore.h>
#include <core/CHIPPersistentStorageDelegate.h>
#include <core/CHIPTLV.h>
Expand Down Expand Up @@ -118,6 +119,8 @@ class DLL_EXPORT DevicePairingDelegate
struct CommissionerInitParams : public ControllerInitParams
{
DevicePairingDelegate * pairingDelegate = nullptr;

OperationalCredentialsDelegate * operationalCredentialsDelegate = nullptr;
};

/**
Expand Down Expand Up @@ -199,7 +202,7 @@ class DLL_EXPORT DeviceController : public Messaging::ExchangeDelegate,
/**
* @brief
* Allow the CHIP Stack to process any pending events
* This can be called in an event handler loop to tigger callbacks within the CHIP stack
* This can be called in an event handler loop to trigger callbacks within the CHIP stack
* @return CHIP_ERROR The return status
*/
CHIP_ERROR ServiceEventSignal();
Expand Down Expand Up @@ -306,8 +309,6 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, public SessionEst
*/
CHIP_ERROR Init(NodeId localDeviceId, CommissionerInitParams params);

void SetDevicePairingDelegate(DevicePairingDelegate * pairingDelegate) { mPairingDelegate = pairingDelegate; }

CHIP_ERROR Shutdown() override;

// ----- Connection Management -----
Expand Down Expand Up @@ -369,6 +370,7 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, public SessionEst
#endif

private:
OperationalCredentialsDelegate * mOperationalCredentialsDelegate;
DevicePairingDelegate * mPairingDelegate;

/* This field is an index in mActiveDevices list. The object at this index in the list
Expand Down Expand Up @@ -401,6 +403,11 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, public SessionEst

static void OnSessionEstablishmentTimeoutCallback(System::Layer * aLayer, void * aAppState, System::Error aError);

CHIP_ERROR SendOperationalCertificateSigningRequestCommand(NodeId remoteDeviceId);
CHIP_ERROR OnOperationalCertificateSigningRequest(NodeId node, const ByteSpan & csr);
CHIP_ERROR SendOperationalCertificate(NodeId remoteDeviceId, const ByteSpan & opCertBuf, const ByteSpan & icaCertBuf);
CHIP_ERROR SendTrustedRootCertificate(NodeId remoteDeviceId, const ByteSpan & certBuf);

uint16_t mNextKeyId = 0;

PASESession mPairingSession;
Expand Down
101 changes: 101 additions & 0 deletions src/controller/OperationalCredentialsDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
*
* 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 <app/util/basic-types.h>
#include <core/PeerId.h>
#include <crypto/CHIPCryptoPAL.h>
#include <support/DLLUtil.h>
#include <support/Span.h>
#include <transport/raw/MessageHeader.h>

namespace chip {
namespace Controller {

/// Callbacks for CHIP operational credentials generation
class DLL_EXPORT OperationalCredentialsDelegate
{
public:
virtual ~OperationalCredentialsDelegate() {}

/**
* @brief
* This function generates an operational certificate for the given node.
* The API generates the certificate in X.509 DER format.
*
* The delegate is expected to use the certificate authority whose certificate
* is returned in `GetIntermediateCACertificate()` or `GetRootCACertificate()`
* API calls.
*
* @param[in] peerId Node ID and Fabric ID of the target device.
* @param[in] csr Certificate Signing Request from the node in DER format.
* @param[in] serialNumber Serial number to assign to the new certificate.
* @param[in] certBuf The API will fill in the generated cert in this buffer. The buffer is allocated by the caller.
* @param[in] certBufSize The size of certBuf buffer.
* @param[out] outCertLen The size of the actual certificate that was written in the certBuf.
*
* @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code.
*/
virtual CHIP_ERROR GenerateNodeOperationalCertificate(const PeerId & peerId, const ByteSpan & csr, int64_t serialNumber,
uint8_t * certBuf, uint32_t certBufSize, uint32_t & outCertLen) = 0;

/**
* @brief
* This function returns the intermediate certificate authority (ICA) certificate corresponding to the
* provided fabric ID. Intermediate certificate authority is optional. If the controller
* application does not require ICA, this API call will return `CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED`.
*
* The returned certificate is in X.509 DER format.
*
* @param[in] fabricId Fabric ID for which the certificate is being requested.
* @param[in] certBuf The API will fill in the cert in this buffer. The buffer is allocated by the caller.
* @param[in] certBufSize The size of certBuf buffer.
* @param[out] outCertLen The size of the actual certificate that was written in the certBuf.
*
* @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code.
* CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED is not a critical error. It indicates that ICA is not needed.
*/
virtual CHIP_ERROR GetIntermediateCACertificate(FabricId fabricId, uint8_t * certBuf, uint32_t certBufSize,
uint32_t & outCertLen)
{
// By default, let's return CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED status. It'll allow
// commissioner applications to not implement GetIntermediateCACertificate() if they don't require an
// intermediate CA.
return CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED;
}

/**
* @brief
* This function returns the root certificate authority (root CA) certificate corresponding to the
* provided fabric ID.
*
* The returned certificate is in X.509 DER format.
*
* @param[in] fabricId Fabric ID for which the certificate is being requested.
* @param[in] certBuf The API will fill in the cert in this buffer. The buffer is allocated by the caller.
* @param[in] certBufSize The size of certBuf buffer.
* @param[out] outCertLen The size of the actual certificate that was written in the certBuf.
*
* @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code.
*/
virtual CHIP_ERROR GetRootCACertificate(FabricId fabricId, uint8_t * certBuf, uint32_t certBufSize, uint32_t & outCertLen) = 0;
};

} // namespace Controller
} // namespace chip
8 changes: 8 additions & 0 deletions src/darwin/Framework/CHIP.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
1EC4CE6025CC26E900D7304F /* CHIPClientCallbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1EC4CE5C25CC26E900D7304F /* CHIPClientCallbacks.cpp */; };
1EC4CE6225CC271B00D7304F /* af-event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1EC4CE6125CC271B00D7304F /* af-event.cpp */; };
1EC4CE6425CC276600D7304F /* CHIPClustersObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC4CE6325CC276600D7304F /* CHIPClustersObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
2C1B027A2641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2C1B02782641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.mm */; };
2C1B027B2641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2C1B02792641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.h */; };
2C222AD0255C620600E446B9 /* CHIPDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 2C222ACE255C620600E446B9 /* CHIPDevice.h */; settings = {ATTRIBUTES = (Public, ); }; };
2C222AD1255C620600E446B9 /* CHIPDevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2C222ACF255C620600E446B9 /* CHIPDevice.mm */; };
2C222ADF255C811800E446B9 /* CHIPDevice_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 2C222ADE255C811800E446B9 /* CHIPDevice_Internal.h */; };
Expand Down Expand Up @@ -98,6 +100,8 @@
1EC4CE5C25CC26E900D7304F /* CHIPClientCallbacks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CHIPClientCallbacks.cpp; path = gen/CHIPClientCallbacks.cpp; sourceTree = "<group>"; };
1EC4CE6125CC271B00D7304F /* af-event.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "af-event.cpp"; path = "../../../app/util/af-event.cpp"; sourceTree = "<group>"; };
1EC4CE6325CC276600D7304F /* CHIPClustersObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CHIPClustersObjc.h; path = gen/CHIPClustersObjc.h; sourceTree = "<group>"; };
2C1B02782641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPOperationalCredentialsDelegate.mm; sourceTree = "<group>"; };
2C1B02792641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHIPOperationalCredentialsDelegate.h; sourceTree = "<group>"; };
2C222ACE255C620600E446B9 /* CHIPDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHIPDevice.h; sourceTree = "<group>"; };
2C222ACF255C620600E446B9 /* CHIPDevice.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPDevice.mm; sourceTree = "<group>"; };
2C222ADE255C811800E446B9 /* CHIPDevice_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHIPDevice_Internal.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -215,6 +219,8 @@
B202528F2459E34F00F97062 /* CHIP */ = {
isa = PBXGroup;
children = (
2C1B02792641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.h */,
2C1B02782641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.mm */,
1EC4CE5825CC26AB00D7304F /* CHIPGeneratedFiles */,
1EC4CE3525CC259700D7304F /* CHIPApp */,
2C222ADE255C811800E446B9 /* CHIPDevice_Internal.h */,
Expand Down Expand Up @@ -273,6 +279,7 @@
buildActionMask = 2147483647;
files = (
2CB7163B252E8A7B0026E2BB /* CHIPDevicePairingDelegateBridge.h in Headers */,
2C1B027B2641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.h in Headers */,
B289D4212639C0D300D4E314 /* CHIPOnboardingPayloadParser.h in Headers */,
2CB7163F252F731E0026E2BB /* CHIPDevicePairingDelegate.h in Headers */,
2C222AD0255C620600E446B9 /* CHIPDevice.h in Headers */,
Expand Down Expand Up @@ -436,6 +443,7 @@
B289D4222639C0D300D4E314 /* CHIPOnboardingPayloadParser.m in Sources */,
1EC4CE5E25CC26E900D7304F /* call-command-handler.cpp in Sources */,
1EC4CE3B25CC263E00D7304F /* reporting-default-configuration.cpp in Sources */,
2C1B027A2641DB4E00780EF1 /* CHIPOperationalCredentialsDelegate.mm in Sources */,
1EC4CE4F25CC267700D7304F /* process-cluster-message.cpp in Sources */,
1EC4CE3D25CC265200D7304F /* DataModelHandler.cpp in Sources */,
1EC4CE5725CC267700D7304F /* attribute-storage.cpp in Sources */,
Expand Down
14 changes: 14 additions & 0 deletions src/darwin/Framework/CHIP/CHIPDeviceController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#import "CHIPDevice_Internal.h"
#import "CHIPError.h"
#import "CHIPLogging.h"
#import "CHIPOperationalCredentialsDelegate.h"
#import "CHIPPersistentStorageDelegateBridge.h"
#import "CHIPSetupPayload.h"
#import "gen/CHIPClustersObjc.h"
Expand All @@ -34,6 +35,7 @@

static NSString * const kErrorMemoryInit = @"Init Memory failure";
static NSString * const kErrorCommissionerInit = @"Init failure while initializing a commissioner";
static NSString * const kErrorOperationalCredentialsInit = @"Init failure while creating operational credentials delegate";
static NSString * const kErrorPairingInit = @"Init failure while creating a pairing delegate";
static NSString * const kErrorPersistentStorageInit = @"Init failure while creating a persistent storage delegate";
static NSString * const kErrorPairDevice = @"Failure while pairing the device";
Expand All @@ -52,6 +54,7 @@ @interface CHIPDeviceController ()
@property (readonly) chip::Controller::DeviceCommissioner * cppCommissioner;
@property (readonly) CHIPDevicePairingDelegateBridge * pairingDelegateBridge;
@property (readonly) CHIPPersistentStorageDelegateBridge * persistentStorageDelegateBridge;
@property (readonly) CHIPOperationalCredentialsDelegate * operationalCredentialsDelegate;
@property (readonly) chip::NodeId localDeviceId;
@property (readonly) uint16_t listenPort;
@end
Expand Down Expand Up @@ -90,6 +93,11 @@ - (instancetype)init
if ([self checkForInitError:(_persistentStorageDelegateBridge != nullptr) logMsg:kErrorPersistentStorageInit]) {
return nil;
}

_operationalCredentialsDelegate = new CHIPOperationalCredentialsDelegate();
if ([self checkForInitError:(_operationalCredentialsDelegate != nullptr) logMsg:kErrorOperationalCredentialsInit]) {
return nil;
}
}
return self;
}
Expand Down Expand Up @@ -134,6 +142,12 @@ - (BOOL)startup:(_Nullable id<CHIPPersistentStorageDelegate>)storageDelegate
CHIP_ERROR errorCode = CHIP_ERROR_INCORRECT_STATE;

_persistentStorageDelegateBridge->setFrameworkDelegate(storageDelegate);

errorCode = _operationalCredentialsDelegate->init(_persistentStorageDelegateBridge);
if ([self checkForStartError:(CHIP_NO_ERROR == errorCode) logMsg:kErrorOperationalCredentialsInit]) {
return;
}

// initialize NodeID if needed
[self _getControllerNodeId];

Expand Down
Loading

0 comments on commit fbee997

Please sign in to comment.