| // Copyright 2023 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CRYPTOHOME_AUTH_BLOCKS_GENERIC_H_ |
| #define CRYPTOHOME_AUTH_BLOCKS_GENERIC_H_ |
| |
| #include <memory> |
| #include <optional> |
| #include <tuple> |
| #include <utility> |
| |
| #include <libhwsec/frontend/cryptohome/frontend.h> |
| #include <libhwsec/frontend/pinweaver_manager/frontend.h> |
| #include <libhwsec/frontend/recovery_crypto/frontend.h> |
| #include <libhwsec-foundation/status/status_chain.h> |
| #include <libstorage/platform/platform.h> |
| |
| #include "cryptohome/auth_blocks/auth_block.h" |
| #include "cryptohome/auth_blocks/auth_block_type.h" |
| #include "cryptohome/auth_blocks/challenge_credential_auth_block.h" |
| #include "cryptohome/auth_blocks/cryptohome_recovery_auth_block.h" |
| #include "cryptohome/auth_blocks/double_wrapped_compat_auth_block.h" |
| #include "cryptohome/auth_blocks/fingerprint_auth_block.h" |
| #include "cryptohome/auth_blocks/pin_weaver_auth_block.h" |
| #include "cryptohome/auth_blocks/scrypt_auth_block.h" |
| #include "cryptohome/auth_blocks/tpm_bound_to_pcr_auth_block.h" |
| #include "cryptohome/auth_blocks/tpm_ecc_auth_block.h" |
| #include "cryptohome/auth_blocks/tpm_not_bound_to_pcr_auth_block.h" |
| #include "cryptohome/challenge_credentials/challenge_credentials_helper.h" |
| #include "cryptohome/crypto.h" |
| #include "cryptohome/cryptohome_keys_manager.h" |
| #include "cryptohome/error/cryptohome_crypto_error.h" |
| #include "cryptohome/error/locations.h" |
| #include "cryptohome/features.h" |
| #include "cryptohome/flatbuffer_schemas/auth_block_state.h" |
| #include "cryptohome/key_challenge_service_factory.h" |
| #include "cryptohome/key_objects.h" |
| #include "cryptohome/util/async_init.h" |
| |
| namespace cryptohome { |
| |
| // To be supported by this generic API, an AuthBlock class must implement a |
| // specific static API. This is the GenericAuthBlock concept. |
| // |
| // The generic auth block type must: |
| // - Have a static constexpr member kType of AuthBlockType |
| // - Have a StateType alias specifying the AuthBlockState type |
| // - Have a static function IsSupported() that returns CryptoStatus |
| // - Have a static function New() that returns unique_ptr<AuthBlock> |
| |
| // Provide a collection of functions that delegates the actual operations to the |
| // appropriate auth block implementation, based on an AuthBlockType parameter. |
| // |
| // These operations are generally implemented by going through the list of all |
| // AuthBlock classes, finding one with a matching kType and then calling the |
| // static operation for that type. Thus for each public function there are |
| // usually two private functions, one handling the generic case and one handling |
| // the base case when no such class is found. |
| // |
| // The generic function option does not hold any internal state of its own but |
| // it does have pointers to all the standard "global" interfaces that the |
| // various AuthBlock static functions take as parameters. |
| class GenericAuthBlockFunctions { |
| private: |
| // This special template type is a way for us to "pass" a set of types to a |
| // function. The actual type itself is empty and doesn't have any particular |
| // value besides having a parameter pack of types attached to it. |
| template <typename... Types> |
| struct TypeContainer {}; |
| |
| // A type container with all of the auth block types that support generic |
| // functions. Usually used as the initial TypeContainer parameter for all of |
| // the variadic functions. |
| using AllBlockTypes = TypeContainer<PinWeaverAuthBlock, |
| ChallengeCredentialAuthBlock, |
| DoubleWrappedCompatAuthBlock, |
| TpmBoundToPcrAuthBlock, |
| TpmNotBoundToPcrAuthBlock, |
| ScryptAuthBlock, |
| CryptohomeRecoveryAuthBlock, |
| TpmEccAuthBlock, |
| FingerprintAuthBlock>; |
| |
| // Call the given function pointer with all of the parameters in |params| |
| // but stripped down to the subset of parameters that the function pointer |
| // accepts. |
| template <typename T, typename Ret, typename... Args> |
| static Ret CallWithParameters(Ret (*func)(Args...), const T& params) { |
| return std::apply(func, std::tie(std::get<Args>(params)...)); |
| } |
| |
| // Thunk from generic to type-specific IsSupported. |
| template <typename T, typename... Rest> |
| CryptoStatus IsSupportedImpl(AuthBlockType auth_block_type, |
| TypeContainer<T, Rest...>) { |
| if (T::kType == auth_block_type) { |
| return CallWithParameters(&T::IsSupported, parameters_); |
| } |
| return IsSupportedImpl(auth_block_type, TypeContainer<Rest...>()); |
| } |
| CryptoStatus IsSupportedImpl(AuthBlockType auth_block_type, TypeContainer<>) { |
| return hwsec_foundation::status::MakeStatus<error::CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC(kLocGenericAuthBlockIsSupportedNotFound), |
| error::ErrorActionSet( |
| {error::PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO); |
| } |
| |
| // Thunk from generic to type-specific New. |
| template <typename T, typename... Rest> |
| std::unique_ptr<AuthBlock> NewImpl(AuthBlockType auth_block_type, |
| const AuthInput& auth_input, |
| TypeContainer<T, Rest...>) { |
| if (T::kType == auth_block_type) { |
| return CallWithParameters( |
| &T::New, std::tuple_cat(std::tie(auth_input), parameters_)); |
| } |
| return NewImpl(auth_block_type, auth_input, TypeContainer<Rest...>()); |
| } |
| std::unique_ptr<AuthBlock> NewImpl(AuthBlockType auth_block_type, |
| const AuthInput& auth_input, |
| TypeContainer<>) { |
| return nullptr; |
| } |
| |
| // Underlying implementation of GetAuthBlockTypeFromState. Looks for an auth |
| // block implementation whose StateType matches the state stored in the auth |
| // block state variant. |
| template <typename T, typename... Rest> |
| std::optional<AuthBlockType> GetAuthBlockTypeFromStateImpl( |
| const AuthBlockState& auth_block_state, TypeContainer<T, Rest...>) { |
| if (std::holds_alternative<typename T::StateType>(auth_block_state.state)) { |
| return T::kType; |
| } |
| return GetAuthBlockTypeFromStateImpl(auth_block_state, |
| TypeContainer<Rest...>()); |
| } |
| std::optional<AuthBlockType> GetAuthBlockTypeFromStateImpl( |
| const AuthBlockState& auth_block_state, TypeContainer<>) { |
| return std::nullopt; |
| } |
| |
| public: |
| GenericAuthBlockFunctions( |
| libstorage::Platform* platform, |
| AsyncInitFeatures* features, |
| AsyncInitPtr<ChallengeCredentialsHelper> challenge_credentials_helper, |
| KeyChallengeServiceFactory* key_challenge_service_factory, |
| AsyncInitPtr<BiometricsAuthBlockService> bio_service, |
| Crypto* crypto) |
| : parameters_(std::forward_as_tuple(*platform, |
| *features, |
| challenge_credentials_helper, |
| key_challenge_service_factory, |
| std::move(bio_service), |
| *crypto, |
| *crypto->GetHwsec(), |
| *crypto->GetRecoveryCrypto(), |
| *crypto->GetPinWeaverManager(), |
| *crypto->cryptohome_keys_manager())) { |
| } |
| |
| // Returns success if this auth block type is supported on the current |
| // hardware and software environment. |
| CryptoStatus IsSupported(AuthBlockType auth_block_type) { |
| return IsSupportedImpl(auth_block_type, AllBlockTypes()); |
| } |
| |
| // Returns either a constructed AuthBlock or null, if the construction is |
| // unable to successfully proceed. |
| std::unique_ptr<AuthBlock> GetAuthBlockWithType(AuthBlockType auth_block_type, |
| const AuthInput& auth_input) { |
| return NewImpl(auth_block_type, auth_input, AllBlockTypes()); |
| } |
| |
| // Generic implementation of AuthBlockUtility::GetAuthBlockTypeFromState. |
| std::optional<AuthBlockType> GetAuthBlockTypeFromState( |
| const AuthBlockState& auth_block_state) { |
| return GetAuthBlockTypeFromStateImpl(auth_block_state, AllBlockTypes()); |
| } |
| |
| private: |
| // Copies of any |parameters_| entries which don't live anywhere else with a |
| // longer lifetime than |this|. This shouldn't be necessary for most |
| // parameters, which are "global" interfaces owned by AuthBlockUtility, or the |
| // larger Cryptohome service as a whole. |
| base::RepeatingCallback<BiometricsAuthBlockService*()> bio_service_getter_; |
| |
| // References or pointers to all of the parameters values that we support, |
| // stored as a tuple. We keep this in a tuple because it makes it much simpler |
| // to automatically reduce to the subset of arguments that the underlying |
| // AuthBlock functions require. |
| // |
| // References should be used whenever the parameter MUST always be valid, and |
| // can never be null. If it's possible for the parameter to be null then you |
| // must instead use a pointer, and auth block functions that consume it MUST |
| // check it for null and gracefully (i.e. no CHECK) fail. |
| std::tuple<libstorage::Platform&, |
| AsyncInitFeatures&, |
| AsyncInitPtr<ChallengeCredentialsHelper>, |
| KeyChallengeServiceFactory*, |
| AsyncInitPtr<BiometricsAuthBlockService>, |
| Crypto&, |
| const hwsec::CryptohomeFrontend&, |
| const hwsec::RecoveryCryptoFrontend&, |
| const hwsec::PinWeaverManagerFrontend&, |
| CryptohomeKeysManager&> |
| parameters_; |
| }; |
| |
| } // namespace cryptohome |
| |
| #endif // CRYPTOHOME_AUTH_BLOCKS_GENERIC_H_ |