blob: e2f33d296c01f93e1e7ce29b4963715db5339214 [file] [log] [blame]
/*
* 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 <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>
#include <lib/support/Span.h>
namespace chip {
namespace Crypto {
class OperationalKeystore
{
public:
virtual ~OperationalKeystore() {}
// ==== API designed for commisionables to support fail-safe (although can be used by controllers) ====
/**
* @brief Returns true if a pending operational key exists from a previous
* `NewOpKeypairForFabric` before any `CommitOpKeypairForFabric` or
* `RevertOpKeypairForFabric`. This returns true even if the key is
* NOT ACTIVE (i.e after `NewOpKeypairForFabric` but before
* `ActivateOpKeypairForFabric`).
*/
virtual bool HasPendingOpKeypair() const = 0;
/**
* @brief Returns whether a usable operational key exists for the given fabric.
*
* Returns true even if the key is not persisted, such as if `ActivateOpKeypairForFabric`
* had been successfully called for a given fabric. Only returns true if a key
* is presently usable such that `SignWithOpKeypair` would succeed for the fabric. Therefore
* if there was no previously persisted key and `NewOpKeypairForFabric` had been called
* but not `ActivateOpKeypairForFabric`, there is only an inactive key, and this would return false.
*
* @param fabricIndex - FabricIndex for which availability of keypair will be checked.
* @return true if there an active operational keypair for the given FabricIndex, false otherwise.
*/
virtual bool HasOpKeypairForFabric(FabricIndex fabricIndex) const = 0;
/**
* @brief This initializes a new keypair for the given fabric and generates a CSR for it,
* so that it can be passed in a CSRResponse.
*
* The keypair is temporary and becomes usable for `SignWithOpKeypair` only after either
* `ActivateOpKeypairForFabric` is called. It is destroyed if
* `RevertPendingKeypair` is called before `CommitOpKeypairForFabric`.
* If a pending keypair already existed for the given `fabricIndex`, it is replaced by this call.
*
* Only one pending operational keypair is supported at a time.
*
* @param fabricIndex - FabricIndex for which a new keypair must be made available
* @param outCertificateSigningRequest - Buffer to contain the CSR. Must have size at least `kMIN_CSR_Buffer_Size`.
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCertificateSigningRequest` buffer is too small
* @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized.
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is already a pending keypair for another `fabricIndex` value
* or if fabricIndex is an invalid value.
* @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported
* @retval other CHIP_ERROR value on internal crypto engine errors
*/
virtual CHIP_ERROR NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) = 0;
/**
* @brief Temporarily activates the operational keypair last generated with `NewOpKeypairForFabric`, so
* that `SignWithOpKeypair` starts using it, but only if it matches the public key associated
* with the last NewOpKeypairForFabric.
*
* This is to be used by AddNOC and UpdateNOC so that a prior key generated by NewOpKeypairForFabric
* can be used for CASE while not committing it yet to permanent storage to remain after fail-safe.
*
* @param fabricIndex - FabricIndex for which to activate the keypair, used for security cross-checking
* @param nocPublicKey - Subject public key associated with an incoming NOC
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized.
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no operational keypair for `fabricIndex` from a previous
* matching `NewOpKeypairForFabric`.
* @retval CHIP_ERROR_INVALID_PUBLIC_KEY if `nocPublicKey` does not match the public key associated with the
* key pair from last `NewOpKeypairForFabric`.
* @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported
* @retval other CHIP_ERROR value on internal storage or crypto engine errors
*/
virtual CHIP_ERROR ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey) = 0;
/**
* @brief Permanently commit the operational keypair last generated with `NewOpKeypairForFabric`,
* replacing a prior one previously committed, if any, so that `SignWithOpKeypair` for the
* given `FabricIndex` permanently uses the key that was pending.
*
* This is to be used when CommissioningComplete is successfully received
*
* @param fabricIndex - FabricIndex for which to commit the keypair, used for security cross-checking
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized,
* or ActivateOpKeypairForFabric not yet called
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no pending operational keypair for `fabricIndex`
* @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported
* @retval other CHIP_ERROR value on internal storage or crypto engine errors
*/
virtual CHIP_ERROR CommitOpKeypairForFabric(FabricIndex fabricIndex) = 0;
/**
* @brief Try to read out the permanently committed operational keypair and save it to the buffer.
*
* @param fabricIndex - FabricIndex from which the keypair will be exported
* @param outKeypair - a reference to P256SerializedKeypair object to store the exported key.
* @retval CHIP_ERROR on success.
* @retval CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the key cannot be exported due to security restrictions.
* @retval CHIP_ERROR_NOT_IMPLEMENTED if the exporting is not implemented for the cryptography backend.
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if provided wrong value of `fabricIndex`.
* @retval CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if there is no keypair found for `fabricIndex`.
* @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outKeyPair` buffer is too small to store the read out keypair.
* @retval other CHIP_ERROR value on internal storage or cryptography backend errors.
*/
virtual CHIP_ERROR ExportOpKeypairForFabric(FabricIndex fabricIndex, Crypto::P256SerializedKeypair & outKeypair)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
};
/**
* @brief Migrate the operational keypair from another Operational keystore (`operationalKeystore`) implementation to this one.
*
* This method assumes that the operational key for given `fabricIndex` exists in the `operationalKeystore` storage or it has
* been already migrated to the new Operational Storage. If a key for the given `fabricIndex` does not exist in any of those
* keystores, then the method retuns CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND.
*
* @param fabricIndex - FabricIndex for which to migrate the operational key
* @param operationalKeystore - a reference to the operationalKeystore implementation that may contain saved operational key
* for Fabric
* @retval CHIP_ERROR on success
* @retval CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the key cannot be exported due to security restrictions.
* @retval CHIP_ERROR_NOT_IMPLEMENTED if the exporting is not implemented for the cryptography backend.
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no keypair found for `fabricIndex`.
* @retval CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if there is no keypair found for `fabricIndex`.
* @retval other CHIP_ERROR value on internal storage or crypto engine errors.
*/
virtual CHIP_ERROR MigrateOpKeypairForFabric(FabricIndex fabricIndex, OperationalKeystore & operationalKeystore) const
{
return CHIP_ERROR_NOT_IMPLEMENTED;
};
/**
* @brief Permanently remove the keypair associated with a fabric
*
* This is to be used for fail-safe handling and RemoveFabric. Removes both the
* pending operational keypair for the fabricIndex (if any) and the committed one (if any).
*
* @param fabricIndex - FabricIndex for which to remove the keypair
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized.
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no operational keypair for `fabricIndex`
* @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported
* @retval other CHIP_ERROR value on internal storage errors
*/
virtual CHIP_ERROR RemoveOpKeypairForFabric(FabricIndex fabricIndex) = 0;
/**
* @brief Permanently release the operational keypair last generated with `NewOpKeypairForFabric`,
* such that `SignWithOpKeypair` uses the previously committed key (if one existed).
*
* 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 `CommitOpKeypairForFabric` must succeed to make a new operational
* keypair usable.
*/
virtual void RevertPendingKeypair() = 0;
// ==== Primary operation required: signature
/**
* @brief Whether `SignWithOpKeypair` may be performed in the background.
*
* If true, `CASESession` may attempt to perform `SignWithOpKeypair` in the
* background. In this case, `OperationalKeystore` should protect itself,
* e.g. with a mutex, as the signing could occur at any time during session
* establishment.
*
* @retval true if `SignWithOpKeypair` may be performed in the background
* @retval false if `SignWithOpKeypair` may NOT be performed in the background
*/
virtual bool SupportsSignWithOpKeypairInBackground() const { return false; }
/**
* @brief Sign a message with a fabric's currently-active operational keypair.
*
* If a Keypair was successfully made temporarily active for the given `fabricIndex` with `ActivateOpKeypairForFabric`,
* then that is the keypair whose private key is used. Otherwise, the last committed private key
* is used, if one exists
*
* @param fabricIndex - FabricIndex whose operational keypair will be used to sign the `message`
* @param message - Message to sign with the currently active operational keypair
* @param outSignature - Buffer to contain the signature
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized.
* @retval CHIP_ERROR_INVALID_FABRIC_INDEX if no active key is found for the given `fabricIndex` or if
* `fabricIndex` is invalid.
* @retval other CHIP_ERROR value on internal crypto engine errors
*/
virtual CHIP_ERROR SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message,
Crypto::P256ECDSASignature & outSignature) const = 0;
/**
* @brief Create an ephemeral keypair for use in session establishment.
*
* The caller must Initialize() the P256Keypair if needed. It is not done by this method.
*
* This method MUST ONLY be used for CASESession ephemeral keys.
*
* NOTE: The stack will allocate as many of these as there are CASE sessions which
* can be concurrently in the process of establishment. Implementations must
* support more than one such keypair, or be implemented to match the limitations
* enforced by a given product on its concurrent CASE session establishment limits.
*
* WARNING: The return value MUST be released by `ReleaseEphemeralKeypair`. This is because
* Matter CHIPMem.h does not properly support UniquePtr in a way that would
* safely allow classes derived from Crypto::P256Keypair to be released properly.
*
* @return a pointer to a P256Keypair (or derived class thereof), which may evaluate to nullptr
* if running out of memory.
*/
virtual Crypto::P256Keypair * AllocateEphemeralKeypairForCASE() = 0;
/**
* @brief Release an ephemeral keypair previously provided by `AllocateEphemeralKeypairForCASE()`
*/
virtual void ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) = 0;
};
} // namespace Crypto
} // namespace chip