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

Introduce fail-safe compliant Operational Cert storage #19643

Merged
merged 3 commits into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions src/credentials/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ static_library("credentials") {
"GroupDataProviderImpl.cpp",
"LastKnownGoodTime.cpp",
"LastKnownGoodTime.h",
"OperationalCertificateStore.h",
"PersistentStorageOpCertStore.cpp",
"PersistentStorageOpCertStore.h",
"attestation_verifier/DeviceAttestationDelegate.h",
"attestation_verifier/DeviceAttestationVerifier.cpp",
"attestation_verifier/DeviceAttestationVerifier.h",
Expand Down
248 changes: 248 additions & 0 deletions src/credentials/OperationalCertificateStore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/*
* 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.
*/

#pragma once

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

namespace chip {
namespace Credentials {

class OperationalCertificateStore
{
public:
enum class CertChainElement : uint8_t
{
kRcac = 0,
kIcac = 1,
kNoc = 2
};

virtual ~OperationalCertificateStore() {}

// ==== API designed for commisionables to support fail-safe (although can be used by controllers) ====

/**
* @brief Returns true if a pending root certificate exists and is active from a previous
* `AddNewTrustedRootCertForFabric`.
*/
virtual bool HasPendingRootCert() const = 0;

/**
* @brief Returns true if a pending operational certificate chain exists and is active from a previous
* `AddNewOpCertsForFabric` or `UpdateOpCertsForFabric`.
*/
virtual bool HasPendingNocChain() const = 0;

/**
* @brief Returns whether a usable operational certificates chain exists for the given fabric.
*
* Returns true even if the certificates are not persisted yet. Only returns true if a certificate
* is presently usable such that `GetCertificate` would succeed for the fabric.
*
* @param fabricIndex - FabricIndex for which availability of certificate will be checked.
* @param element - Element of the certificate chain whose presence needs to be checked
* @return true if there an active obtainable operational certificate of the given type for the given FabricIndex,
* false otherwise.
*/
virtual bool HasCertificateForFabric(FabricIndex fabricIndex, CertChainElement element) const = 0;

/**
* @brief Add and temporarily activate a new Trusted Root Certificate to storage for the given fabric
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
*
* The certificate is temporary until committed or reverted.
* The certificate is committed to storage on `CommitOpCertsForFabric`.
* The certificate is destroyed if `RevertPendingOpCerts` is called before `CommitOpCertsForFabric`.
*
* Only one pending trusted root certificate is supported at a time and it is illegal
* to call this method if there is already a persisted root certificate for the given
* fabric.
*
* Uniqueness constraints for roots (see AddTrustedRootCertificate command in spec) is not
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* enforced by this method and must be done as a more holistic check elsewhere. Cryptographic
* signature verification or path validation is not enforced by this method.
*
* If `UpdateOpCertsForFabric` had been called before this method, this method will return
* CHIP_ERROR_INCORRECT_STATE since it is illegal to update trusted roots when updating an
* existing NOC chain.
*
* @param fabricIndex - FabricIndex for which a new trusted root certificate should be added
* @param rcac - Buffer containing the root certificate to add.
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary root cert
* @retval CHIP_ERROR_INVALID_ARGUMENT if the certificate is empty or too large
* @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method
* is called after `UpdateOpCertsForFabric`, or if there was
* already a pending or persisted root certificate for the given `fabricIndex`.
* @retval other CHIP_ERROR value on internal errors
*/
virtual CHIP_ERROR AddNewTrustedRootCertForFabric(FabricIndex fabricIndex, const ByteSpan & rcac) = 0;

/**
* @brief Add and temporarily activate an operational certificate chain for the given fabric.
*
* The certificate chain is temporary until committed or reverted.
* The certificate chain is committed to storage on `CommitOpCertsForFabric`.
* The certificate chain is destroyed if `RevertPendingOpCerts` is called before `CommitOpCertsForFabric`.
*
* Only one pending operational certificate chain is supported at a time and it is illegal
* to call this method if there is already a persisted certificate chain for the given
* fabric.
*
* Cryptographic signature verification or path validation is not enforced by this method.
*
* If `UpdateOpCertsForFabric` had been called before this method, this method will return
* CHIP_ERROR_INCORRECT_STATE since it is illegal to add a certificate chain after
* updating an existing NOC updating prior to a commit.
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
*
* If `AddNewTrustedRootCertForFabric` had not been called before this method, this method will
* return CHIP_ERROR_INCORRECT_STATE since it is illegal in this implementation to store an
* NOC chain without associated root.
*
* NOTE: The Matter spec allows AddNOC without AddTrustedRootCertificate if the NOC
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. Our AddNOC impl right now definitely does not handle this right. Is there an issue tracking that?

* chains to an existing root, to support root reuse. In this implementation, we expect each
* fabric to store the root with the rest of the chain. Because of this, callers must ensure
* that if an AddNOC command is done and no trusted root was added, that the requisite existing
* root be "copied over" to match.
*
* @param fabricIndex - FabricIndex for which to add a new operational certificate chain
* @param noc - Buffer containing the NOC certificate to add
* @param icac - Buffer containing the ICAC certificate to add. If no ICAC is needed, `icac.empty()` must be true.
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `noc` and `icac` cert copies
* @retval CHIP_ERROR_INVALID_ARGUMENT if either the noc or icac are invalid sizes
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if `fabricIndex` mismatches the one from previous successful
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* `AddNewTrustedRootCertForFabric`.
* @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method
* is called after `UpdateOpCertsForFabric`, or if there was
* already a pending or persisted operational cert chain for the given `fabricIndex`,
* or if AddNewTrustedRootCertForFabric had not yet been called for the given `fabricIndex`.
* @retval other CHIP_ERROR value on internal errors
*/
virtual CHIP_ERROR AddNewOpCertsForFabric(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac) = 0;

/**
* @brief Update and temporarily activate an existing operational certificate chain for the given fabric.
*
* The certificate chain is temporary until committed or reverted.
* The certificate chain is committed to storage on `CommitOpCertsForFabric`.
* The certificate chain is reverted to prior storage if `RevertPendingOpCerts` is called
* before `CommitOpCertsForFabric`.
*
* Only one pending operational certificate chain is supported at a time and it is illegal
* to call this method if there was not already a persisted certificate chain for the given
* fabric.
*
* Cryptographic signature verification or path validation is not enforced by this method.
*
* If `AddNewOpCertsForFabric` had been called before this method, this method will return
* CHIP_ERROR_INCORRECT_STATE since it is illegal to update a certificate chain after
* adding an existing NOC updating prior to a commit.
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
*
* If there is no existing persisted trusted root certificate for the given fabricIndex, this
* method will method will eturn CHIP_ERROR_INCORRECT_STATE since it is illegal in this
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* implementation to store an NOC chain without associated root.
*
* @param fabricIndex - FabricIndex for which to add a new operational certificate chain
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* @param noc - Buffer containing the NOC certificate to update
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* @param icac - Buffer containing the ICAC certificate to update. If no ICAC is needed, `icac.empty()` must be true.
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `noc` and `icac` cert copies
* @retval CHIP_ERROR_INVALID_ARGUMENT if either the noc or icac are invalid sizes
* @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method
* is called after `AddNewOpCertsForFabric`, or if there was
* already a pending cert chain for the given `fabricIndex`,
* or if there is no associated persisted root for for the given `fabricIndex`.
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* @retval other CHIP_ERROR value on internal errors
*/
virtual CHIP_ERROR UpdateOpCertsForFabric(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac) = 0;

/**
* @brief Permanently commit the certificate chain last setup via successful calls to
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* legal combinations of `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
* `UpdateOpCertsForFabric`, replacing previously committed data, if any.
*
* This is to be used when CommissioningComplete is successfully received
*
* @param fabricIndex - FabricIndex for which to commit the certificate chain, used for security cross-checking
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized,
* or if not valid pending state is available.
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there are no pending certificate chain for `fabricIndex`
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* @retval other CHIP_ERROR value on internal storage errors
*/
virtual CHIP_ERROR CommitOpCertsForFabric(FabricIndex fabricIndex) = 0;

/**
* @brief Permanently remove the certificate chain associated with a fabric.
*
* This is to be used for fail-safe handling and RemoveFabric. Removes both the
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* pending operational cert chain elements for the fabricIndex (if any) and the committed
* ones (if any).
*
* @param fabricIndex - FabricIndex for which to remove the operational cert chain
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized.
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there was not operational certificate data at all for `fabricIndex`
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* @retval other CHIP_ERROR value on internal storage errors
*/
virtual CHIP_ERROR RemoveOpCertsForFabric(FabricIndex fabricIndex) = 0;

/**
* @brief Permanently release the operational certificate chain made via successful calls to
* legal combinations of `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
* `UpdateOpCertsForFabric`, if any.
*
* This is to be used when a fail-safe expires prior to CommissioningComplete.
*
* This method cannot error-out and must always succeed, even on a no-op. This should
* be safe to do given that `CommitOpCertsForFabric` must succeed to make an operation
* certificate chain usable.
*/
virtual void RevertPendingOpCerts() = 0;

/**
* @brief Get the operational certificate element requested, giving the pending data or committed
* data depending on prior `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
* `UpdateOpCertsForFabric` calls.
*
* On success, the `outCertificate` span is resized to the size of the actual certificate read-back.
*
* @param fabricIndex - fabricIndex for which to get the certificate
* @param element - which element of the cerficate chain to get
* @param outCertificate - buffer to contain the certificate obtained from persistent or temporary storage
*
* @retval CHIP_NO_ERROR on success.
* @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCertificate` does not fit the certificate.
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
* @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized.
* @retval CHIP_ERROR_NOT_FOUND if the element cannot be found.
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if the fabricIndex is invalid.
* @retval other CHIP_ERROR value on internal storage errors.
*/
virtual CHIP_ERROR GetCertificate(FabricIndex fabricIndex, CertChainElement element,
MutableByteSpan & outCertificate) const = 0;
};

} // namespace Credentials
} // namespace chip
Loading