LCOV - code coverage report
Current view: top level - credentials - OperationalCertificateStore.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 1 1 100.0 %
Date: 2024-02-15 08:20:41 Functions: 1 2 50.0 %

          Line data    Source code
       1             : /*
       2             :  *    Copyright (c) 2022 Project CHIP Authors
       3             :  *    All rights reserved.
       4             :  *
       5             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6             :  *    you may not use this file except in compliance with the License.
       7             :  *    You may obtain a copy of the License at
       8             :  *
       9             :  *        http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  *    Unless required by applicable law or agreed to in writing, software
      12             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  *    See the License for the specific language governing permissions and
      15             :  *    limitations under the License.
      16             :  */
      17             : 
      18             : #pragma once
      19             : 
      20             : #include <lib/core/CHIPError.h>
      21             : #include <lib/core/DataModelTypes.h>
      22             : #include <lib/support/Span.h>
      23             : 
      24             : namespace chip {
      25             : namespace Credentials {
      26             : 
      27             : class OperationalCertificateStore
      28             : {
      29             : public:
      30             :     enum class CertChainElement : uint8_t
      31             :     {
      32             :         kRcac = 0,
      33             :         kIcac = 1,
      34             :         kNoc  = 2
      35             :     };
      36             : 
      37           1 :     virtual ~OperationalCertificateStore() {}
      38             : 
      39             :     // ==== API designed for commisionables to support fail-safe (although can be used by controllers) ====
      40             : 
      41             :     /**
      42             :      * @brief Returns true if a pending root certificate exists and is active from a previous
      43             :      *        `AddNewTrustedRootCertForFabric`.
      44             :      */
      45             :     virtual bool HasPendingRootCert() const = 0;
      46             : 
      47             :     /**
      48             :      * @brief Returns true if a pending operational certificate chain exists and is active from a previous
      49             :      *        `AddNewOpCertsForFabric` or `UpdateOpCertsForFabric`.
      50             :      */
      51             :     virtual bool HasPendingNocChain() const = 0;
      52             : 
      53             :     /**
      54             :      * @brief Returns whether a usable operational certificates chain exists for the given fabric.
      55             :      *
      56             :      * Returns true even if the certificates are not persisted yet. Only returns true if a certificate
      57             :      * is presently usable such that `GetCertificate` would succeed for the fabric.
      58             :      *
      59             :      * @param fabricIndex - FabricIndex for which availability of certificate will be checked.
      60             :      * @param element - Element of the certificate chain whose presence needs to be checked
      61             :      * @return true if there an active obtainable operational certificate of the given type for the given FabricIndex,
      62             :      *         false otherwise.
      63             :      */
      64             :     virtual bool HasCertificateForFabric(FabricIndex fabricIndex, CertChainElement element) const = 0;
      65             : 
      66             :     /**
      67             :      * @brief Add and temporarily activate a new Trusted Root Certificate for the given fabric
      68             :      *
      69             :      * The certificate is temporary until committed or reverted.
      70             :      * The certificate is committed to storage only on `CommitOpCertsForFabric`.
      71             :      * The certificate is destroyed if `RevertPendingOpCerts` is called before `CommitOpCertsForFabric`.
      72             :      *
      73             :      * Only one pending trusted root certificate is supported at a time and it is illegal
      74             :      * to call this method if there is already a persisted root certificate for the given
      75             :      * fabric.
      76             :      *
      77             :      * Uniqueness constraints for roots (see AddTrustedRootCertificate command in spec) are not
      78             :      * enforced by this method and must be done as a more holistic check elsewhere. Cryptographic
      79             :      * signature verification or path validation are not enforced by this method.
      80             :      *
      81             :      * If `UpdateOpCertsForFabric` had been called before this method, this method will return
      82             :      * CHIP_ERROR_INCORRECT_STATE since it is illegal to update trusted roots when updating an
      83             :      * existing NOC chain.
      84             :      *
      85             :      * @param fabricIndex - FabricIndex for which a new trusted root certificate should be added
      86             :      * @param rcac - Buffer containing the root certificate to add.
      87             :      *
      88             :      * @retval CHIP_NO_ERROR on success
      89             :      * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary root cert
      90             :      * @retval CHIP_ERROR_INVALID_ARGUMENT if the certificate is empty or too large
      91             :      * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method
      92             :      *                                    is called after `UpdateOpCertsForFabric`, or if there was
      93             :      *                                    already a pending or persisted root certificate for the given `fabricIndex`.
      94             :      * @retval other CHIP_ERROR value on internal errors
      95             :      */
      96             :     virtual CHIP_ERROR AddNewTrustedRootCertForFabric(FabricIndex fabricIndex, const ByteSpan & rcac) = 0;
      97             : 
      98             :     /**
      99             :      * @brief Add and temporarily activate an operational certificate chain for the given fabric.
     100             :      *
     101             :      * The certificate chain is temporary until committed or reverted.
     102             :      * The certificate chain is committed to storage on `CommitOpCertsForFabric`.
     103             :      * The certificate chain is destroyed if `RevertPendingOpCerts` is called before `CommitOpCertsForFabric`.
     104             :      *
     105             :      * Only one pending operational certificate chain is supported at a time and it is illegal
     106             :      * to call this method if there is already a persisted certificate chain for the given
     107             :      * fabric.
     108             :      *
     109             :      * Cryptographic signature verification or path validation are not enforced by this method.
     110             :      *
     111             :      * If `UpdateOpCertsForFabric` had been called before this method, this method will return
     112             :      * CHIP_ERROR_INCORRECT_STATE since it is illegal to add a certificate chain after
     113             :      * updating an existing NOC and before committing or reverting the update.
     114             :      *
     115             :      * If `AddNewTrustedRootCertForFabric` had not been called before this method, this method will
     116             :      * return CHIP_ERROR_INCORRECT_STATE since it is illegal in this implementation to store an
     117             :      * NOC chain without associated root.
     118             :      *
     119             :      * NOTE: The Matter spec allows AddNOC without AddTrustedRootCertificate if the NOC
     120             :      * chains to an existing root, to support root reuse. In this implementation, we expect each
     121             :      * fabric to store the root with the rest of the chain. Because of this, callers must ensure
     122             :      * that if an AddNOC command is done and no trusted root was added, that the requisite existing
     123             :      * root be "copied over" to match.
     124             :      *
     125             :      * @param fabricIndex - FabricIndex for which to add a new operational certificate chain
     126             :      * @param noc - Buffer containing the NOC certificate to add
     127             :      * @param icac - Buffer containing the ICAC certificate to add. If no ICAC is needed, `icac.empty()` must be true.
     128             :      *
     129             :      * @retval CHIP_NO_ERROR on success
     130             :      * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `noc` and `icac` cert copies
     131             :      * @retval CHIP_ERROR_INVALID_ARGUMENT if either the noc or icac are invalid sizes
     132             :      * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if `fabricIndex` mismatches the one from a previous successful
     133             :      *                                         `AddNewTrustedRootCertForFabric`.
     134             :      * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method
     135             :      *                                    is called after `UpdateOpCertsForFabric`, or if there was
     136             :      *                                    already a pending or persisted operational cert chain for the given `fabricIndex`,
     137             :      *                                    or if AddNewTrustedRootCertForFabric had not yet been called for the given `fabricIndex`.
     138             :      * @retval other CHIP_ERROR value on internal errors
     139             :      */
     140             :     virtual CHIP_ERROR AddNewOpCertsForFabric(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac) = 0;
     141             : 
     142             :     /**
     143             :      * @brief Update and temporarily activate an existing operational certificate chain for the given fabric.
     144             :      *
     145             :      * The certificate chain is temporary until committed or reverted.
     146             :      * The certificate chain is committed to storage on `CommitOpCertsForFabric`.
     147             :      * The certificate chain is reverted to prior storage if `RevertPendingOpCerts` is called
     148             :      * before `CommitOpCertsForFabric`.
     149             :      *
     150             :      * Only one pending operational certificate chain is supported at a time and it is illegal
     151             :      * to call this method if there was not already a persisted certificate chain for the given
     152             :      * fabric.
     153             :      *
     154             :      * Cryptographic signature verification or path validation are not enforced by this method.
     155             :      *
     156             :      * If `AddNewOpCertsForFabric` had been called before this method, this method will return
     157             :      * CHIP_ERROR_INCORRECT_STATE since it is illegal to update a certificate chain after
     158             :      * adding an existing NOC and before committing or reverting the addition.
     159             :      *
     160             :      * If there is no existing persisted trusted root certificate and NOC chain for the given
     161             :      * fabricIndex, this method will return CHIP_ERROR_INCORRECT_STATE since it is
     162             :      * illegal in this implementation to store an NOC chain without associated root, and it is illegal
     163             :      * to update an opcert for a fabric not already configured.
     164             :      *
     165             :      * @param fabricIndex - FabricIndex for which to update the operational certificate chain
     166             :      * @param noc - Buffer containing the new NOC certificate to use
     167             :      * @param icac - Buffer containing the ICAC certificate to use. If no ICAC is needed, `icac.empty()` must be true.
     168             :      *
     169             :      * @retval CHIP_NO_ERROR on success
     170             :      * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `noc` and `icac` cert copies
     171             :      * @retval CHIP_ERROR_INVALID_ARGUMENT if either the noc or icac are invalid sizes
     172             :      * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method
     173             :      *                                    is called after `AddNewOpCertsForFabric`, or if there was
     174             :      *                                    already a pending cert chain for the given `fabricIndex`, or if there are
     175             :      *                                    no associated persisted root and NOC chain for for the given `fabricIndex`.
     176             :      * @retval other CHIP_ERROR value on internal errors
     177             :      */
     178             :     virtual CHIP_ERROR UpdateOpCertsForFabric(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac) = 0;
     179             : 
     180             :     /**
     181             :      * @brief Permanently commit the certificate chain last configured via successful calls to
     182             :      *        legal combinations of `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
     183             :      *        `UpdateOpCertsForFabric`, replacing previously committed data, if any.
     184             :      *
     185             :      * This is to be used when CommissioningComplete is successfully received
     186             :      *
     187             :      * @param fabricIndex - FabricIndex for which to commit the certificate chain, used for security cross-checking
     188             :      *
     189             :      * @retval CHIP_NO_ERROR on success
     190             :      * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized,
     191             :      *                                    or if no valid pending state is available.
     192             :      * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no pending certificate chain for `fabricIndex`
     193             :      * @retval other CHIP_ERROR value on internal storage errors
     194             :      */
     195             :     virtual CHIP_ERROR CommitOpCertsForFabric(FabricIndex fabricIndex) = 0;
     196             : 
     197             :     /**
     198             :      * @brief Permanently remove the certificate chain associated with a fabric.
     199             :      *
     200             :      * This is to be used for RemoveFabric. Removes both the pending operational cert chain
     201             :      * elements for the fabricIndex (if any) and the committed ones (if any).
     202             :      *
     203             :      * @param fabricIndex - FabricIndex for which to remove the operational cert chain
     204             :      *
     205             :      * @retval CHIP_NO_ERROR on success
     206             :      * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized.
     207             :      * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there was no operational certificate data at all for `fabricIndex`
     208             :      * @retval other CHIP_ERROR value on internal storage errors
     209             :      */
     210             :     virtual CHIP_ERROR RemoveOpCertsForFabric(FabricIndex fabricIndex) = 0;
     211             : 
     212             :     /**
     213             :      * @brief Permanently release the operational certificate chain made via successful calls to
     214             :      *        legal combinations of `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
     215             :      *        `UpdateOpCertsForFabric`, if any.
     216             :      *
     217             :      * This is to be used when a fail-safe expires prior to CommissioningComplete.
     218             :      *
     219             :      * This method cannot error-out and must always succeed, even on a no-op. This should
     220             :      * be safe to do given that `CommitOpCertsForFabric` must succeed to make an operation
     221             :      * certificate chain usable.
     222             :      */
     223             :     virtual void RevertPendingOpCerts() = 0;
     224             : 
     225             :     /**
     226             :      * @brief Same as RevertPendingOpCerts(), but leaves pending Trusted Root certs if they had
     227             :      *        been added. This is is an operation to support the complex error handling of
     228             :      *        AddNOC, where we don't want to have "sticking" ICAC/NOC after validation
     229             :      *        problems, but don't want to lose the RCAC given in an AddTrustedRootCertificate
     230             :      *        command.
     231             :      */
     232             :     virtual void RevertPendingOpCertsExceptRoot() = 0;
     233             : 
     234             :     /**
     235             :      * @brief Get the operational certificate element requested, giving the pending data or committed
     236             :      *        data depending on prior `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
     237             :      *        `UpdateOpCertsForFabric` calls.
     238             :      *
     239             :      * On success, the `outCertificate` span is resized to the size of the actual certificate read-back.
     240             :      *
     241             :      * @param fabricIndex - fabricIndex for which to get the certificate
     242             :      * @param element - which element of the cerficate chain to get
     243             :      * @param outCertificate - buffer to contain the certificate obtained from persistent or temporary storage
     244             :      *
     245             :      * @retval CHIP_NO_ERROR on success.
     246             :      * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCertificate` is too small to fit the certificate found.
     247             :      * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized.
     248             :      * @retval CHIP_ERROR_NOT_FOUND if the element cannot be found.
     249             :      * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if the fabricIndex is invalid.
     250             :      * @retval other CHIP_ERROR value on internal storage errors.
     251             :      */
     252             :     virtual CHIP_ERROR GetCertificate(FabricIndex fabricIndex, CertChainElement element,
     253             :                                       MutableByteSpan & outCertificate) const = 0;
     254             : };
     255             : 
     256             : /**
     257             :  * @brief RAII class to operate on an OperationalCertificateStore with auto-revert if not committed.
     258             :  *
     259             :  * Use as:
     260             :  *
     261             :  * CHIP_ERROR FunctionWillReturnWithPendingReverted(....)
     262             :  * {
     263             :  *     OpCertStoreTransaction transaction(opCertStore);
     264             :  *
     265             :  *     ReturnErrorOnFailure(transaction->AddNewTrustedRootCertForFabric(...));
     266             :  *     ReturnErrorOnFailure(transaction->AddNewOpCertsForFabric(...));
     267             :  *     ReturnErrorOnFailure(transaction->CommitOpCertsForFabric(...));
     268             :  *
     269             :  *     return CHIP_NO_ERROR;
     270             :  * }
     271             :  */
     272             : class OpCertStoreTransaction
     273             : {
     274             : public:
     275             :     explicit OpCertStoreTransaction(OperationalCertificateStore & store) : mStore(store) {}
     276             :     ~OpCertStoreTransaction()
     277             :     {
     278             :         // This is a no-op if CommitOpCertsForFabric had been called on the store
     279             :         mStore.RevertPendingOpCerts();
     280             :     }
     281             : 
     282             :     // Non-copyable
     283             :     OpCertStoreTransaction(OpCertStoreTransaction const &) = delete;
     284             :     void operator=(OpCertStoreTransaction const &)         = delete;
     285             : 
     286             :     OperationalCertificateStore * operator->() { return &mStore; }
     287             : 
     288             : private:
     289             :     OperationalCertificateStore & mStore;
     290             : };
     291             : 
     292             : } // namespace Credentials
     293             : } // namespace chip

Generated by: LCOV version 1.14