// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/certificate_provider/certificate_provider_service_factory.h"
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/no_destructor.h"
#include "base/values.h"
#include "chrome/browser/certificate_provider/certificate_provider_service.h"
#include "chrome/common/extensions/api/certificate_provider.h"
#include "extensions/browser/event_listener_map.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/event_router_factory.h"
#include "extensions/browser/extension_event_histogram_value.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_factory.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/common/extension.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/ssl/ssl_private_key.h"
#include "third_party/boringssl/src/include/openssl/digest.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
namespace chromeos {
namespace {
namespace api_cp = extensions::api::certificate_provider;
class DefaultDelegate : public CertificateProviderService::Delegate,
public extensions::EventRouter::Observer,
public extensions::ExtensionRegistryObserver {
public:
// |event_router| may be null in tests.
DefaultDelegate(CertificateProviderService* service,
extensions::ExtensionRegistry* registry,
extensions::EventRouter* event_router);
DefaultDelegate(const DefaultDelegate&) = delete;
DefaultDelegate& operator=(const DefaultDelegate&) = delete;
~DefaultDelegate() override;
// CertificateProviderService::Delegate:
std::vector<std::string> CertificateProviderExtensions() override;
void BroadcastCertificateRequest(int request_id) override;
bool DispatchSignRequestToExtension(
const std::string& extension_id,
int request_id,
uint16_t algorithm,
const scoped_refptr<net::X509Certificate>& certificate,
base::span<const uint8_t> input) override;
// extensions::EventRouter::Observer:
void OnListenerAdded(const extensions::EventListenerInfo& details) override {}
void OnListenerRemoved(const extensions::EventListenerInfo& details) override;
// extensions::ExtensionRegistryObserver:
void OnExtensionUnloaded(content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) override;
private:
// Returns extension IDs that currently have event listeners for the given
// event,
base::flat_set<std::string> GetSubscribedExtensions(
const std::string& event_name);
const raw_ptr<CertificateProviderService> service_;
const raw_ptr<extensions::ExtensionRegistry> registry_;
const raw_ptr<extensions::EventRouter> event_router_;
};
// Constructs the "onCertificatesUpdateRequested" event.
std::unique_ptr<extensions::Event> BuildOnCertificatesUpdateRequestedEvent(
int request_id) {
api_cp::CertificatesUpdateRequest certificates_update_request;
certificates_update_request.certificates_request_id = request_id;
base::Value::List event_args;
event_args.Append(certificates_update_request.ToValue());
return std::make_unique<extensions::Event>(
extensions::events::CERTIFICATEPROVIDER_ON_CERTIFICATES_UPDATE_REQUESTED,
api_cp::OnCertificatesUpdateRequested::kEventName, std::move(event_args));
}
// Constructs the legacy "onCertificatesRequested" event.
std::unique_ptr<extensions::Event> BuildOnCertificatesRequestedEvent(
int request_id) {
base::Value::List event_args;
event_args.Append(request_id);
return std::make_unique<extensions::Event>(
extensions::events::CERTIFICATEPROVIDER_ON_CERTIFICATES_REQUESTED,
api_cp::OnCertificatesRequested::kEventName, std::move(event_args));
}
// Constructs the "onSignatureRequested" event.
std::unique_ptr<extensions::Event> BuildOnSignatureRequestedEvent(
int request_id,
uint16_t algorithm,
const net::X509Certificate& certificate,
base::span<const uint8_t> input) {
api_cp::SignatureRequest request;
request.sign_request_id = request_id;
switch (algorithm) {
case SSL_SIGN_RSA_PKCS1_SHA1:
request.algorithm = api_cp::Algorithm::kRsassaPkcs1V1_5Sha1;
break;
case SSL_SIGN_RSA_PKCS1_SHA256:
request.algorithm = api_cp::Algorithm::kRsassaPkcs1V1_5Sha256;
break;
case SSL_SIGN_RSA_PKCS1_SHA384:
request.algorithm = api_cp::Algorithm::kRsassaPkcs1V1_5Sha384;
break;
case SSL_SIGN_RSA_PKCS1_SHA512:
request.algorithm = api_cp::Algorithm::kRsassaPkcs1V1_5Sha512;
break;
case SSL_SIGN_RSA_PSS_RSAE_SHA256:
request.algorithm = api_cp::Algorithm::kRsassaPssSha256;
break;
case SSL_SIGN_RSA_PSS_RSAE_SHA384:
request.algorithm = api_cp::Algorithm::kRsassaPssSha384;
break;
case SSL_SIGN_RSA_PSS_RSAE_SHA512:
request.algorithm = api_cp::Algorithm::kRsassaPssSha512;
break;
default:
LOG(ERROR) << "Unknown signature algorithm";
return nullptr;
}
request.input.assign(input.begin(), input.end());
std::string_view cert_der =
net::x509_util::CryptoBufferAsStringPiece(certificate.cert_buffer());
request.certificate.assign(cert_der.begin(), cert_der.end());
base::Value::List event_args;
event_args.Append(request.ToValue());
return std::make_unique<extensions::Event>(
extensions::events::CERTIFICATEPROVIDER_ON_SIGNATURE_REQUESTED,
api_cp::OnSignatureRequested::kEventName, std::move(event_args));
}
// Constructs the legacy "onSignDigestRequested" event.
std::unique_ptr<extensions::Event> BuildOnSignDigestRequestedEvent(
int request_id,
uint16_t algorithm,
const net::X509Certificate& certificate,
base::span<const uint8_t> input) {
api_cp::SignRequest request;
request.sign_request_id = request_id;
switch (algorithm) {
case SSL_SIGN_RSA_PKCS1_SHA1:
request.hash = api_cp::Hash::kSha1;
break;
case SSL_SIGN_RSA_PKCS1_SHA256:
request.hash = api_cp::Hash::kSha256;
break;
case SSL_SIGN_RSA_PKCS1_SHA384:
request.hash = api_cp::Hash::kSha384;
break;
case SSL_SIGN_RSA_PKCS1_SHA512:
request.hash = api_cp::Hash::kSha512;
break;
default:
LOG(ERROR) << "Unknown signature algorithm";
return nullptr;
}
std::string_view cert_der =
net::x509_util::CryptoBufferAsStringPiece(certificate.cert_buffer());
request.certificate.assign(cert_der.begin(), cert_der.end());
// The extension expects the input to be hashed ahead of time.
request.digest.resize(EVP_MAX_MD_SIZE);
const EVP_MD* md = SSL_get_signature_algorithm_digest(algorithm);
unsigned digest_len;
if (!md || !EVP_Digest(input.data(), input.size(), request.digest.data(),
&digest_len, md, /*ENGINE *impl=*/nullptr)) {
return nullptr;
}
request.digest.resize(digest_len);
base::Value::List event_args;
event_args.Append(request_id);
event_args.Append(request.ToValue());
return std::make_unique<extensions::Event>(
extensions::events::CERTIFICATEPROVIDER_ON_SIGN_DIGEST_REQUESTED,
api_cp::OnSignDigestRequested::kEventName, std::move(event_args));
}
DefaultDelegate::DefaultDelegate(CertificateProviderService* service,
extensions::ExtensionRegistry* registry,
extensions::EventRouter* event_router)
: service_(service), registry_(registry), event_router_(event_router) {
DCHECK(service_);
registry_->AddObserver(this);
event_router_->RegisterObserver(
this, api_cp::OnCertificatesUpdateRequested::kEventName);
event_router_->RegisterObserver(this,
api_cp::OnCertificatesRequested::kEventName);
}
DefaultDelegate::~DefaultDelegate() {
event_router_->UnregisterObserver(this);
registry_->RemoveObserver(this);
}
std::vector<std::string> DefaultDelegate::CertificateProviderExtensions() {
base::flat_set<std::string> ids = GetSubscribedExtensions(
api_cp::OnCertificatesUpdateRequested::kEventName);
const base::flat_set<std::string> legacy_ids =
GetSubscribedExtensions(api_cp::OnCertificatesRequested::kEventName);
ids.insert(legacy_ids.begin(), legacy_ids.end());
return std::vector<std::string>(ids.begin(), ids.end());
}
void DefaultDelegate::BroadcastCertificateRequest(int request_id) {
// First, broadcast the event to the extensions that use the up-to-date
// version of the API.
const auto up_to_date_api_extension_ids = GetSubscribedExtensions(
api_cp::OnCertificatesUpdateRequested::kEventName);
for (const std::string& extension_id : up_to_date_api_extension_ids) {
event_router_->DispatchEventToExtension(
extension_id, BuildOnCertificatesUpdateRequestedEvent(request_id));
}
// Second, broadcast the event to the extensions that only listen for the
// legacy event.
for (const std::string& extension_id :
GetSubscribedExtensions(api_cp::OnCertificatesRequested::kEventName)) {
if (up_to_date_api_extension_ids.contains(extension_id))
continue;
event_router_->DispatchEventToExtension(
extension_id, BuildOnCertificatesRequestedEvent(request_id));
}
}
bool DefaultDelegate::DispatchSignRequestToExtension(
const std::string& extension_id,
int request_id,
uint16_t algorithm,
const scoped_refptr<net::X509Certificate>& certificate,
base::span<const uint8_t> input) {
DCHECK(certificate);
std::unique_ptr<extensions::Event> event;
// Send the up-to-date version of the event, and fall back to the legacy event
// if the extension is only listening for that one.
if (event_router_->ExtensionHasEventListener(
extension_id, api_cp::OnSignatureRequested::kEventName)) {
event = BuildOnSignatureRequestedEvent(request_id, algorithm, *certificate,
input);
} else if (event_router_->ExtensionHasEventListener(
extension_id, api_cp::OnSignDigestRequested::kEventName)) {
event = BuildOnSignDigestRequestedEvent(request_id, algorithm, *certificate,
input);
}
if (!event)
return false;
event_router_->DispatchEventToExtension(extension_id, std::move(event));
return true;
}
void DefaultDelegate::OnListenerRemoved(
const extensions::EventListenerInfo& details) {
if (!event_router_->ExtensionHasEventListener(
details.extension_id,
api_cp::OnCertificatesUpdateRequested::kEventName) &&
!event_router_->ExtensionHasEventListener(
details.extension_id, api_cp::OnCertificatesRequested::kEventName)) {
service_->OnExtensionUnregistered(details.extension_id);
}
}
void DefaultDelegate::OnExtensionUnloaded(
content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) {
service_->OnExtensionUnloaded(extension->id());
}
base::flat_set<std::string> DefaultDelegate::GetSubscribedExtensions(
const std::string& event_name) {
std::vector<std::string> ids;
for (const std::unique_ptr<extensions::EventListener>& listener :
event_router_->listeners().GetEventListenersByName(event_name)) {
ids.push_back(listener->extension_id());
}
return base::flat_set<std::string>(ids.begin(), ids.end());
}
} // namespace
// static
CertificateProviderService*
CertificateProviderServiceFactory::GetForBrowserContext(
content::BrowserContext* context) {
return static_cast<CertificateProviderService*>(
GetInstance()->GetServiceForBrowserContext(context, true));
}
// static
CertificateProviderServiceFactory*
CertificateProviderServiceFactory::GetInstance() {
static base::NoDestructor<CertificateProviderServiceFactory> instance;
return instance.get();
}
CertificateProviderServiceFactory::CertificateProviderServiceFactory()
: ProfileKeyedServiceFactory(
"CertificateProviderService",
ProfileSelections::Builder()
.WithRegular(ProfileSelection::kRedirectedToOriginal)
// TODO(crbug.com/40257657): Check if this service is needed in
// Guest mode.
.WithGuest(ProfileSelection::kRedirectedToOriginal)
// TODO(crbug.com/41488885): Check if this service is needed for
// Ash Internals.
.WithAshInternals(ProfileSelection::kRedirectedToOriginal)
.Build()) {
DependsOn(extensions::EventRouterFactory::GetInstance());
DependsOn(extensions::ExtensionRegistryFactory::GetInstance());
}
bool CertificateProviderServiceFactory::ServiceIsNULLWhileTesting() const {
return true;
}
KeyedService* CertificateProviderServiceFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
CertificateProviderService* const service = new CertificateProviderService();
service->SetDelegate(std::make_unique<DefaultDelegate>(
service,
extensions::ExtensionRegistryFactory::GetForBrowserContext(context),
extensions::EventRouterFactory::GetForBrowserContext(context)));
return service;
}
} // namespace chromeos