Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow external ownership of CHIPDeviceController operational key #18482

Merged
merged 6 commits into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ CHIP_ERROR DeviceController::Init(ControllerInitParams params)
mVendorId = params.controllerVendorId;
if (params.operationalKeypair != nullptr || !params.controllerNOC.empty() || !params.controllerRCAC.empty())
{
ReturnErrorOnFailure(ProcessControllerNOCChain(params));
ReturnErrorOnFailure(InitControllerNOCChain(params));

if (params.enableServerInteractions)
{
Expand All @@ -144,15 +144,23 @@ CHIP_ERROR DeviceController::Init(ControllerInitParams params)
return CHIP_NO_ERROR;
}

CHIP_ERROR DeviceController::ProcessControllerNOCChain(const ControllerInitParams & params)
CHIP_ERROR DeviceController::InitControllerNOCChain(const ControllerInitParams & params)
{
FabricInfo newFabric;
constexpr uint32_t chipCertAllocatedLen = kMaxCHIPCertLength;
chip::Platform::ScopedMemoryBuffer<uint8_t> chipCert;
Credentials::P256PublicKeySpan rootPublicKey;
FabricId fabricId;

ReturnErrorOnFailure(newFabric.SetOperationalKeypair(params.operationalKeypair));
if (params.hasExternallyOwnedOperationalKeypair)
{
ReturnErrorOnFailure(newFabric.SetExternallyOwnedOperationalKeypair(params.operationalKeypair));
}
else
{
ReturnErrorOnFailure(newFabric.SetOperationalKeypair(params.operationalKeypair));
}

newFabric.SetVendorId(params.controllerVendorId);

ReturnErrorCodeIf(!chipCert.Alloc(chipCertAllocatedLen), CHIP_ERROR_NO_MEMORY);
Expand Down
22 changes: 20 additions & 2 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ struct ControllerInitParams
controllerNOC. It's used by controller to establish CASE sessions with devices */
Crypto::P256Keypair * operationalKeypair = nullptr;

/**
* Controls whether or not the operationalKeypair should be owned by the caller.
* By default, this is false, but if the keypair cannot be serialized, then
* setting this to true will allow you to manage this keypair's lifecycle.
*/
bool hasExternallyOwnedOperationalKeypair = false;

/* The following certificates must be in x509 DER format */
ByteSpan controllerNOC;
ByteSpan controllerICAC;
Expand Down Expand Up @@ -240,6 +247,19 @@ class DLL_EXPORT DeviceController : public SessionRecoveryDelegate, public Abstr
*/
CHIP_ERROR DisconnectDevice(NodeId nodeId);

/**
* @brief
* Reconfigures a new set of operational credentials to be used with this
* controller given ControllerInitParams state.
*
* WARNING: This is a low-level method that should only be called directly
* if you know exactly how this will interact with controller state,
* since there are several integrations that do this call for you.
* It can be used for fine-grained dependency injection of a controller's
* NOC and operational keypair.
*/
CHIP_ERROR InitControllerNOCChain(const ControllerInitParams & params);
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved

protected:
enum class State
{
Expand Down Expand Up @@ -275,8 +295,6 @@ class DLL_EXPORT DeviceController : public SessionRecoveryDelegate, public Abstr

private:
void ReleaseOperationalDevice(OperationalDeviceProxy * device);

CHIP_ERROR ProcessControllerNOCChain(const ControllerInitParams & params);
};

#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY
Expand Down
87 changes: 57 additions & 30 deletions src/credentials/FabricTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,20 +94,6 @@ CHIP_ERROR FabricInfo::CommitToStorage(PersistentStorageDelegate * storage)
storage->SyncSetKeyValue(keyAlloc.FabricNOC(mFabricIndex), mNOCCert.data(), static_cast<uint16_t>(mNOCCert.size())));

{
Crypto::P256SerializedKeypair serializedOpKey;
if (mOperationalKey != nullptr)
{
ReturnErrorOnFailure(mOperationalKey->Serialize(serializedOpKey));
}
else
{
// Could we just not store it instead? What would deserialize need
// to do then?
P256Keypair keypair;
ReturnErrorOnFailure(keypair.Initialize());
ReturnErrorOnFailure(keypair.Serialize(serializedOpKey));
}

uint8_t buf[OpKeyTLVMaxSize()];
TLV::TLVWriter writer;
writer.Init(buf);
Expand All @@ -117,7 +103,14 @@ CHIP_ERROR FabricInfo::CommitToStorage(PersistentStorageDelegate * storage)

ReturnErrorOnFailure(writer.Put(kOpKeyVersionTag, kOpKeyVersion));

ReturnErrorOnFailure(writer.Put(kOpKeyDataTag, ByteSpan(serializedOpKey.Bytes(), serializedOpKey.Length())));
// If key storage is externally managed, key is not stored here,
// and when loading is done later, it will be ignored.
if (!mHasExternallyOwnedOperationalKey && (mOperationalKey != nullptr))
{
Crypto::P256SerializedKeypair serializedOpKey;
ReturnErrorOnFailure(mOperationalKey->Serialize(serializedOpKey));
ReturnErrorOnFailure(writer.Put(kOpKeyDataTag, ByteSpan(serializedOpKey.Bytes(), serializedOpKey.Length())));
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
}

ReturnErrorOnFailure(writer.EndContainer(outerType));

Expand Down Expand Up @@ -213,27 +206,36 @@ CHIP_ERROR FabricInfo::LoadFromStorage(PersistentStorageDelegate * storage)
ReturnErrorOnFailure(reader.Get(opKeyVersion));
VerifyOrReturnError(opKeyVersion == kOpKeyVersion, CHIP_ERROR_VERSION_MISMATCH);

ReturnErrorOnFailure(reader.Next(kOpKeyDataTag));
ByteSpan keyData;
ReturnErrorOnFailure(reader.GetByteView(keyData));
CHIP_ERROR err = reader.Next(kOpKeyDataTag);
if (err == CHIP_NO_ERROR)
{
ByteSpan keyData;
ReturnErrorOnFailure(reader.GetByteView(keyData));

// Unfortunately, we have to copy the data into a P256SerializedKeypair.
Crypto::P256SerializedKeypair serializedOpKey;
VerifyOrReturnError(keyData.size() <= serializedOpKey.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL);
// Unfortunately, we have to copy the data into a P256SerializedKeypair.
Crypto::P256SerializedKeypair serializedOpKey;
VerifyOrReturnError(keyData.size() <= serializedOpKey.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL);

memcpy(serializedOpKey.Bytes(), keyData.data(), keyData.size());
serializedOpKey.SetLength(keyData.size());
memcpy(serializedOpKey.Bytes(), keyData.data(), keyData.size());
serializedOpKey.SetLength(keyData.size());

if (mOperationalKey == nullptr)
{
if (mOperationalKey == nullptr)
{
#ifdef ENABLE_HSM_CASE_OPS_KEY
mOperationalKey = chip::Platform::New<P256KeypairHSM>();
mOperationalKey = chip::Platform::New<P256KeypairHSM>();
#else
mOperationalKey = chip::Platform::New<P256Keypair>();
mOperationalKey = chip::Platform::New<P256Keypair>();
#endif
}
VerifyOrReturnError(mOperationalKey != nullptr, CHIP_ERROR_NO_MEMORY);
ReturnErrorOnFailure(mOperationalKey->Deserialize(serializedOpKey));
}
else
{
// Key was absent: set mOperationalKey to null, for another caller to set
// it. This may happen if externally owned.
mOperationalKey = nullptr;
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
}
VerifyOrReturnError(mOperationalKey != nullptr, CHIP_ERROR_NO_MEMORY);
ReturnErrorOnFailure(mOperationalKey->Deserialize(serializedOpKey));

ReturnErrorOnFailure(reader.ExitContainer(containerType));
ReturnErrorOnFailure(reader.VerifyEndOfContainer());
Expand Down Expand Up @@ -321,6 +323,9 @@ CHIP_ERROR FabricInfo::DeleteFromStorage(PersistentStorageDelegate * storage, Fa
CHIP_ERROR FabricInfo::SetOperationalKeypair(const P256Keypair * keyPair)
{
VerifyOrReturnError(keyPair != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

mHasExternallyOwnedOperationalKey = false;

P256SerializedKeypair serialized;
ReturnErrorOnFailure(keyPair->Serialize(serialized));
if (mOperationalKey == nullptr)
Expand All @@ -335,6 +340,20 @@ CHIP_ERROR FabricInfo::SetOperationalKeypair(const P256Keypair * keyPair)
return mOperationalKey->Deserialize(serialized);
}

CHIP_ERROR FabricInfo::SetExternallyOwnedOperationalKeypair(P256Keypair * keyPair)
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
{
VerifyOrReturnError(keyPair != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
if (!mHasExternallyOwnedOperationalKey && mOperationalKey != nullptr)
{
chip::Platform::Delete(mOperationalKey);
mOperationalKey = nullptr;
}

mHasExternallyOwnedOperationalKey = true;
mOperationalKey = keyPair;
return CHIP_NO_ERROR;
}

void FabricInfo::ReleaseCert(MutableByteSpan & cert)
{
if (cert.data() != nullptr)
Expand Down Expand Up @@ -565,7 +584,15 @@ CHIP_ERROR FabricInfo::SetFabricInfo(FabricInfo & newFabric)
ReturnErrorOnFailure(VerifyCredentials(newFabric.mNOCCert, newFabric.mICACert, newFabric.mRootCert, validContext, operationalId,
fabricId, pubkey));

SetOperationalKeypair(newFabric.GetOperationalKey());
if (newFabric.mHasExternallyOwnedOperationalKey)
{
ReturnErrorOnFailure(SetExternallyOwnedOperationalKeypair(newFabric.GetOperationalKey()));
}
else
{
ReturnErrorOnFailure(SetOperationalKeypair(newFabric.GetOperationalKey()));
}

SetRootCert(newFabric.mRootCert);
mOperationalId = operationalId;
mFabricId = fabricId;
Expand Down
33 changes: 30 additions & 3 deletions src/credentials/FabricTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class DLL_EXPORT FabricInfo

~FabricInfo()
{
if (mOperationalKey != nullptr)
if (!mHasExternallyOwnedOperationalKey && mOperationalKey != nullptr)
{
chip::Platform::Delete(mOperationalKey);
}
Expand Down Expand Up @@ -123,8 +123,33 @@ class DLL_EXPORT FabricInfo
}
return mOperationalKey;
}

tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
/**
* Sets the P256Keypair used for this fabric. This will make a copy of the keypair
* via the P256Keypair::Serialize and P256Keypair::Deserialize methods.
*
* The keyPair argument is safe to deallocate once this method returns.
*
* If your P256Keypair does not support serialization, use the
* `SetExternallyOwnedOperationalKeypair` method instead.
*/
CHIP_ERROR SetOperationalKeypair(const Crypto::P256Keypair * keyPair);

/**
* Sets the P256Keypair used for this fabric, delegating ownership of the
* key to the caller. The P256Keypair provided here must be freed later by
* the caller of this method if it was allocated dynamically.
*
* This should be used if your P256Keypair does not support serialization
* and deserialization (e.g. your private key is held in a secure element
* and cannot be accessed directly), or if you back your operational
* private keys by external implementation of the cryptographic interfaces.
*
* To have the ownership of the key managed for you, use
* SetOperationalKeypair instead.
*/
CHIP_ERROR SetExternallyOwnedOperationalKeypair(Crypto::P256Keypair * keyPair);

// TODO - Update these APIs to take ownership of the buffer, instead of copying
// internally.
// TODO - Optimize persistent storage of NOC and Root Cert in FabricInfo.
Expand Down Expand Up @@ -186,11 +211,12 @@ class DLL_EXPORT FabricInfo
mVendorId = VendorId::NotSpecified;
mFabricLabel[0] = '\0';

if (mOperationalKey != nullptr)
if (mHasExternallyOwnedOperationalKey && mOperationalKey != nullptr)
{
chip::Platform::Delete(mOperationalKey);
mOperationalKey = nullptr;
}
mOperationalKey = nullptr;

ReleaseOperationalCerts();
mFabricIndex = kUndefinedFabricIndex;
}
Expand Down Expand Up @@ -229,6 +255,7 @@ class DLL_EXPORT FabricInfo
#else
mutable Crypto::P256Keypair * mOperationalKey = nullptr;
#endif
bool mHasExternallyOwnedOperationalKey = false;

MutableByteSpan mRootCert;
MutableByteSpan mICACert;
Expand Down