blob: 47c3c29b2a9a095c0902aedb2d637fb47a272f58 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ASH_KCER_KCER_FACTORY_ASH_H_
#define CHROME_BROWSER_ASH_KCER_KCER_FACTORY_ASH_H_
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "chrome/browser/profiles/profile_keyed_service_factory.h"
#include "chromeos/ash/components/kcer/chaps/session_chaps_client.h"
#include "chromeos/ash/components/kcer/kcer.h"
#include "chromeos/ash/components/kcer/kcer_token.h"
#include "chromeos/ash/components/tpm/tpm_token_info_getter.h"
class Profile;
namespace net {
class NSSCertDatabase;
}
namespace kcer::internal {
class KcerImpl;
} // namespace kcer::internal
namespace user_prefs {
class PrefRegistrySyncable;
}
namespace kcer {
// Feature flag to toggle between Kcer-over-NSS and Kcer-without-NSS. If
// disabled, Kcer will use the implementation that relies on NSS. If enabled,
// Kcer will talk directly with Chaps without using NSS.
BASE_DECLARE_FEATURE(kKcerWithoutNss);
class KcerFactoryAsh : public ProfileKeyedServiceFactory {
public:
// Public for the implementation of this class.
using UniqueSlotId = std::pair<SECMODModuleID, CK_SLOT_ID>;
using KcerTokenMapNss =
base::flat_map<UniqueSlotId, std::unique_ptr<internal::KcerToken>>;
using KcerTokenMapWithoutNss =
base::flat_map<SessionChapsClient::SlotId,
std::unique_ptr<internal::KcerToken>>;
using InitializeOnUIThreadCallback =
base::OnceCallback<void(base::WeakPtr<internal::KcerToken>,
base::WeakPtr<internal::KcerToken>)>;
// Returns a Kcer instance for the `profile`. The lifetime of the instance is
// bound to the `profile`. If the pointer is cached, it needs to be checked
// before it's used.
static base::WeakPtr<Kcer> GetKcer(Profile* profile);
static KcerFactoryAsh* GetInstance();
// Public for Pkcs12Migrator unit tests.
struct KcerService : public KeyedService {
explicit KcerService(std::unique_ptr<internal::KcerImpl> kcer_instance);
~KcerService() override;
std::unique_ptr<internal::KcerImpl> kcer;
};
// Returns whether HighLevelChapsClient was initialized. The method is mostly
// needed just for testing.
static bool IsHighLevelChapsClientInitialized();
// Creates an entry in user preferences in Ash that a PKCS#12 file was
// dual-written. This will be used in case a rollback for the related
// experiment is needed.
static void RecordPkcs12CertDualWritten();
// Clears cached NSS state. Useful for unittests using Kcer with a test NSS
// database, the test should clear the Kcer state when finished to avoid
// leaking into the next test. This must be called on the IO thread.
static void ClearNssTokenMapForTesting();
private:
friend base::NoDestructor<KcerFactoryAsh>;
KcerFactoryAsh();
~KcerFactoryAsh() override;
// BrowserContextKeyedServiceFactory:
// The services need to be created immediately after the browser context
// because the instance for the main profile will also be exposed as the
// default one and it won't be able to be created on demand from there.
bool ServiceIsCreatedWithBrowserContext() const override;
// Creates a new service, which is not fully initialized yet, but can already
// be used as if it's initialized. Posts a task to finish the initialization.
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
// Implements BrowserContextKeyedServiceFactory.
void RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) override;
// Returns whether the Kcer-without-NSS experiment is enabled.
bool UseKcerWithoutNss() const;
void Initialize();
void StartInitializingKcerInstance(
base::WeakPtr<internal::KcerImpl> kcer_service,
content::BrowserContext* context);
void StartInitializingKcerWithoutNss(
base::WeakPtr<internal::KcerImpl> kcer_service,
content::BrowserContext* context);
void GetDeviceTokenInfo(base::WeakPtr<internal::KcerImpl> kcer_service,
AccountId account_id);
void GetUserTokenInfo(
base::WeakPtr<internal::KcerImpl> kcer_service,
AccountId account_id,
std::unique_ptr<ash::TPMTokenInfoGetter> scoped_device_token_info_getter,
std::optional<user_data_auth::TpmTokenInfo> device_token_info);
void GotAllTokenInfos(
base::WeakPtr<internal::KcerImpl> kcer_service,
std::optional<user_data_auth::TpmTokenInfo> device_token_info,
std::unique_ptr<ash::TPMTokenInfoGetter> scoped_user_token_info_getter,
std::optional<user_data_auth::TpmTokenInfo> user_token_info);
base::WeakPtr<internal::KcerToken> GetTokenWithoutNss(
std::optional<SessionChapsClient::SlotId> token_id,
Token token_type);
bool EnsureHighLevelChapsClientInitialized();
void InitializeKcerInstanceWithoutNss(
base::WeakPtr<internal::KcerImpl> kcer_service,
std::optional<SessionChapsClient::SlotId> user_token_id,
std::optional<SessionChapsClient::SlotId> device_token_id);
void StartInitializingKcerForNss(
base::WeakPtr<internal::KcerImpl> kcer_service,
content::BrowserContext* context);
// Returns a functor that will create KcerTokens for each slot from `nss_db`
// (if necessary) and return weak pointers to them to the provided `callback`.
// The functor should be run on the IO thread, the callback will be run on the
// UI thread.
base::OnceCallback<void(InitializeOnUIThreadCallback callback,
net::NSSCertDatabase* nss_db)>
GetPrepareTokensForNssOnIOThreadFunctor();
void InitializeKcerInstanceForNss(
base::WeakPtr<internal::KcerImpl> kcer_service,
base::WeakPtr<internal::KcerToken> user_token,
base::WeakPtr<internal::KcerToken> device_token);
void StartInitializingDeviceKcerWithoutNss();
void InitializeDeviceKcerWithoutNss(
std::unique_ptr<ash::TPMTokenInfoGetter> scoped_device_token_info_getter,
std::optional<user_data_auth::TpmTokenInfo> device_token_info);
void StartInitializingDeviceKcerForNss();
void InitializeDeviceKcerForNss(
base::WeakPtr<internal::KcerToken> /*user_token*/,
base::WeakPtr<internal::KcerToken> device_token);
base::WeakPtr<Kcer> GetKcerImpl(Profile* profile);
void RecordPkcs12CertDualWrittenImpl();
void ClearNssTokenMapForTestingImpl();
// Used by `high_level_chaps_client_` and must outlive it.
std::unique_ptr<SessionChapsClient> session_chaps_client_;
// Used by tokens in `chaps_tokens_ui_` (to communicate with Chaps) and must
// outlive them.
std::unique_ptr<HighLevelChapsClient> high_level_chaps_client_;
// Stores the mapping between NSS slots and KcerToken-s. Each private or
// system slot is mapped into a KcerToken, so a Kcer instance equivalent to a
// given NSSCertDatabase can be created. Public slots are ignored because
// there should be no keys or client certificates there by the time Kcer is
// launched.
// The map is created on the UI thread, only used on the IO thread and is
// never destroyed (as a part of NoDestructor<> factory).
KcerTokenMapNss nss_tokens_io_;
// Stores the mapping between Chaps tokens and KcerToken-s. The map is created
// on the UI thread, only used on the UI thread and is never destroyed (as a
// part of NoDestructor<> factory).
// Only one token map is used for each Chromium launch, which is controlled by
// an experiment.
KcerTokenMapWithoutNss chaps_tokens_ui_;
};
} // namespace kcer
#endif // CHROME_BROWSER_ASH_KCER_KCER_FACTORY_ASH_H_