blob: 89bd520bac69917b1a5dab161a2f733e6b81e04d [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_CHROMEOS_KCER_KCER_FACTORY_H_
#define CHROME_BROWSER_CHROMEOS_KCER_KCER_FACTORY_H_
#include <utility>
#include <secmodt.h>
#include "base/containers/flat_map.h"
#include "base/no_destructor.h"
#include "chrome/browser/profiles/profile_keyed_service_factory.h"
#include "chromeos/components/kcer/kcer.h"
#include "chromeos/components/kcer/kcer_token.h"
#include "components/keyed_service/core/keyed_service.h"
class Profile;
namespace net {
class NSSCertDatabase;
}
namespace kcer::internal {
class KcerImpl;
} // namespace kcer::internal
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);
// Factory for Kcer instances. At the moment it supports creating instances both
// for Kcer-over-NSS (implementation of the Kcer interface using NSS) and
// Kcer-without-NSS (implementation of the Kcer interface that directly
// communicates with Chaps). This makes the code harder to follow, but the
// general idea is:
// * In all cases KcerFactory owns the pointer to the factory singleton,
// KcerToken-s and the mapping from low level tokens (either NSS slots or Chaps
// tokens) to KcerToken-s.
// * For Kcer-over-NSS in both Ash and Lacros KcerFactory simply fetches the
// existing NSSCertDatabase and builds a Kcer instance using that.
// * For Kcer-without-NSS in Ash some Ash specific code is extracted into
// KcerFactoryAsh because of the build rules. Some more general code is in
// KcerFactory.
// * For Kcer-without-NSS in Lacros some Ash specific code is extracted into
// KcerFactoryLacros because of the build rules. Some more general code is in
// KcerFactory.
class KcerFactory : public ProfileKeyedServiceFactory {
public:
// 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);
// 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 whether HighLevelChapsClient was initialized. The method is mostly
// needed just for testing.
static bool IsHighLevelChapsClientInitialized();
// Should be called on shutdown to avoid dangling pointers.
static void Shutdown();
protected:
struct KcerService : public KeyedService {
explicit KcerService(std::unique_ptr<internal::KcerImpl> kcer_instance);
~KcerService() override;
std::unique_ptr<internal::KcerImpl> kcer;
};
KcerFactory();
~KcerFactory() override;
// A getter for the specializations to access the singleton pointer.
static KcerFactory*& GetGlobalPointer();
// 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(KcerFactory::InitializeOnUIThreadCallback callback,
net::NSSCertDatabase* nss_db)>
GetPrepareTokensForNssOnIOThreadFunctor();
virtual base::WeakPtr<Kcer> GetKcerImpl(Profile* profile);
// Returns true if the Kcer instance for the `context` should be saved into
// kcer::ExtraInstances as "default Kcer".
virtual bool IsPrimaryContext(content::BrowserContext* context) const = 0;
// Initiates the initialization of `kcer_service` - the instance of Kcer for
// the `context`. The implementations should identify correct token ids and
// then call `InitializeKcerInstanceWithoutNss`.
virtual void StartInitializingKcerWithoutNss(
base::WeakPtr<internal::KcerImpl> kcer_service,
content::BrowserContext* context) = 0;
// Initializes `high_level_chaps_client_` when necessary. Returns true if
// `high_level_chaps_client_` is initialized.
virtual bool EnsureHighLevelChapsClientInitialized() = 0;
// Initializes each token for `kcer_service` with `user_token_id` and
// `device_token_id` respectively, initializes `kcer_service` with the tokens.
void InitializeKcerInstanceWithoutNss(
base::WeakPtr<internal::KcerImpl> kcer_service,
std::optional<SessionChapsClient::SlotId> user_token_id,
std::optional<SessionChapsClient::SlotId> device_token_id);
// Initializes a single token.
base::WeakPtr<internal::KcerToken> GetTokenWithoutNss(
std::optional<SessionChapsClient::SlotId> token_id,
Token token_type);
// Returns whether the Kcer-without-NSS experiment is enabled.
bool UseKcerWithoutNss() const;
// 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_;
private:
friend class base::NoDestructor<KcerFactory>;
// 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;
void StartInitializingKcerInstance(
base::WeakPtr<internal::KcerImpl> kcer_service,
content::BrowserContext* context);
void StartInitializingKcerForNss(
base::WeakPtr<internal::KcerImpl> kcer_service,
content::BrowserContext* context);
void InitializeKcerInstanceForNss(
base::WeakPtr<internal::KcerImpl> kcer_service,
base::WeakPtr<internal::KcerToken> user_token,
base::WeakPtr<internal::KcerToken> device_token);
void StartRetrievingContextInfo(content::BrowserContext* context);
void OnContextInfoRetrieved(content::BrowserContext* context,
bool is_default_context,
bool should_have_user_token,
bool should_have_device_token);
// 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_CHROMEOS_KCER_KCER_FACTORY_H_