0

Starting Device Bound Session Credentials (DBSC) integration

For more information on DBSC see https://github.com/WICG/dbsc.

- Create a feature flag for DBSC, off by default
- Add a mojom::NetworkContextParamsPtr param for dbsc, only set to true
in the main network context
- The DBSC service is attached to the URLRequestContext
- Add a DBSC service if the feature flag is enabled and param is true,
otherwise the service returns null
- If the headers contains a valid DBSC registration, and dbsc is
enabled, ask the service to create the session

Bug: 41495201
Change-Id: Ic2737f3d8144893ff39d16af89264c0868c2ec7d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5274486
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Reviewed-by: Adam Rice <ricea@chromium.org>
Reviewed-by: Lily Chen <chlily@chromium.org>
Commit-Queue: Kristian Monsen <kristianm@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1274553}
This commit is contained in:
Kristian Monsen
2024-03-18 23:32:00 +00:00
committed by Chromium LUCI CQ
parent c2dd699e7c
commit d56563ce41
18 changed files with 332 additions and 1 deletions

@@ -1236,6 +1236,9 @@ void ProfileNetworkContextService::ConfigureNetworkContextParamsInternal(
network_context_params->enable_ip_protection =
ipp_config_provider->IsIpProtectionEnabled();
}
network_context_params->device_bound_sessions_enabled =
base::FeatureList::IsEnabled(net::features::kDeviceBoundSessions);
}
base::FilePath ProfileNetworkContextService::GetPartitionPath(

@@ -1106,6 +1106,8 @@ component("net") {
sources += [
"device_bound_sessions/bound_session_registration_fetcher_param.cc",
"device_bound_sessions/bound_session_registration_fetcher_param.h",
"device_bound_sessions/device_bound_session_service.cc",
"device_bound_sessions/device_bound_session_service.h",
]
}
@@ -3205,7 +3207,11 @@ test("net_unittests") {
}
if (enable_device_bound_sessions) {
sources += [ "device_bound_sessions/bound_session_registration_fetcher_param_unittest.cc" ]
sources += [
"device_bound_sessions/bound_session_registration_fetcher_param_unittest.cc",
"device_bound_sessions/test_util.cc",
"device_bound_sessions/test_util.h",
]
}
}

@@ -534,4 +534,8 @@ BASE_FEATURE(kReduceIPAddressChangeNotification,
base::FEATURE_ENABLED_BY_DEFAULT);
#endif // BUILDFLAG(IS_MAC)
BASE_FEATURE(kDeviceBoundSessions,
"DeviceBoundSessions",
base::FEATURE_DISABLED_BY_DEFAULT);
} // namespace net::features

@@ -521,6 +521,10 @@ NET_EXPORT BASE_DECLARE_FEATURE(kTruncateBodyToContentLength);
NET_EXPORT BASE_DECLARE_FEATURE(kReduceIPAddressChangeNotification);
#endif // BUILDFLAG(IS_MAC)
// This feature will enable the Device Bound Session Credentials protocol to let
// the server assert sessions (and cookies) are bound to a specific device.
NET_EXPORT BASE_DECLARE_FEATURE(kDeviceBoundSessions);
} // namespace net::features
#endif // NET_BASE_FEATURES_H_

@@ -0,0 +1,27 @@
// 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.
#include "net/device_bound_sessions/device_bound_session_service.h"
#include <memory>
#include "base/memory/ptr_util.h"
#include "net/base/features.h"
namespace net {
namespace {
class DeviceBoundSessionServiceImpl : public DeviceBoundSessionService {
public:
// TODO(kristianm): Implement RegisterBoundSession
void RegisterBoundSession(const BoundSessionRegistrationFetcherParam&
registration_params) override {}
};
} // namespace
std::unique_ptr<DeviceBoundSessionService> DeviceBoundSessionService::Create() {
return std::make_unique<DeviceBoundSessionServiceImpl>();
}
} // namespace net

@@ -0,0 +1,29 @@
// 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 NET_DEVICE_BOUND_SESSIONS_DEVICE_BOUND_SESSION_SERVICE_H_
#define NET_DEVICE_BOUND_SESSIONS_DEVICE_BOUND_SESSION_SERVICE_H_
#include <memory>
#include "net/base/net_export.h"
#include "net/device_bound_sessions/bound_session_registration_fetcher_param.h"
namespace net {
// Main class for Device Bound Session Credentials (DBSC).
// Full information can be found at https://github.com/WICG/dbsc
class NET_EXPORT DeviceBoundSessionService {
public:
static std::unique_ptr<DeviceBoundSessionService> Create();
virtual void RegisterBoundSession(
const BoundSessionRegistrationFetcherParam& registration_params) = 0;
virtual ~DeviceBoundSessionService() = default;
};
} // namespace net
#endif // NET_DEVICE_BOUND_SESSIONS_DEVICE_BOUND_SESSION_SERVICE_H_

@@ -0,0 +1,12 @@
// 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.
#include "net/device_bound_sessions/test_util.h"
namespace net {
DeviceBoundSessionServiceMock::DeviceBoundSessionServiceMock() = default;
DeviceBoundSessionServiceMock::~DeviceBoundSessionServiceMock() = default;
} // namespace net

@@ -0,0 +1,27 @@
// 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 NET_DEVICE_BOUND_SESSIONS_TEST_UTIL_H_
#define NET_DEVICE_BOUND_SESSIONS_TEST_UTIL_H_
#include "net/device_bound_sessions/bound_session_registration_fetcher_param.h"
#include "net/device_bound_sessions/device_bound_session_service.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace net {
class DeviceBoundSessionServiceMock : public DeviceBoundSessionService {
public:
DeviceBoundSessionServiceMock();
~DeviceBoundSessionServiceMock() override;
MOCK_METHOD(void,
RegisterBoundSession,
(const BoundSessionRegistrationFetcherParam& registration_params),
(override));
};
} // namespace net
#endif // NET_DEVICE_BOUND_SESSIONS_TEST_UTIL_H_

@@ -47,6 +47,10 @@
#include "net/reporting/reporting_service.h"
#endif // BUILDFLAG(ENABLE_REPORTING)
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
#include "net/device_bound_sessions/device_bound_session_service.h"
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
namespace net {
URLRequestContext::URLRequestContext(
@@ -253,4 +257,11 @@ void URLRequestContext::set_transport_security_persister(
transport_security_persister_ = std::move(transport_security_persister);
}
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
void URLRequestContext::set_device_bound_session_service(
std::unique_ptr<DeviceBoundSessionService> device_bound_session_service) {
device_bound_session_service_ = std::move(device_bound_session_service);
}
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
} // namespace net

@@ -61,6 +61,10 @@ class PersistentReportingAndNelStore;
class ReportingService;
#endif // BUILDFLAG(ENABLE_REPORTING)
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
class DeviceBoundSessionService;
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
// Class that provides application-specific context for URLRequest
// instances. May only be created by URLRequestContextBuilder.
// Owns most of its member variables, except a few that may be shared
@@ -207,6 +211,13 @@ class NET_EXPORT URLRequestContext final {
}
#endif // BUILDFLAG(ENABLE_REPORTING)
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
// May return nullptr if the feature is disabled.
DeviceBoundSessionService* device_bound_session_service() const {
return device_bound_session_service_.get();
}
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
bool enable_brotli() const { return enable_brotli_; }
bool enable_zstd() const { return enable_zstd_; }
@@ -301,6 +312,10 @@ class NET_EXPORT URLRequestContext final {
std::unique_ptr<TransportSecurityPersister> transport_security_persister);
raw_ptr<NetLog> net_log_ = nullptr;
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
void set_device_bound_session_service(
std::unique_ptr<DeviceBoundSessionService> device_bound_session_service);
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
std::unique_ptr<HostResolver> host_resolver_;
std::unique_ptr<CertVerifier> cert_verifier_;
@@ -348,6 +363,10 @@ class NET_EXPORT URLRequestContext final {
std::unique_ptr<std::set<raw_ptr<const URLRequest, SetExperimental>>>
url_requests_;
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
std::unique_ptr<DeviceBoundSessionService> device_bound_session_service_;
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
// Enables Brotli Content-Encoding support.
bool enable_brotli_ = false;
// Enables Zstd Content-Encoding support.

@@ -63,6 +63,10 @@
#include "base/android/build_info.h"
#endif // BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
#include "net/device_bound_sessions/device_bound_session_service.h"
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
namespace net {
URLRequestContextBuilder::HttpCacheParams::HttpCacheParams() = default;
@@ -243,6 +247,13 @@ void URLRequestContextBuilder::SetCreateHttpTransactionFactoryCallback(
std::move(create_http_network_transaction_factory);
}
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
void URLRequestContextBuilder::set_device_bound_session_service(
std::unique_ptr<DeviceBoundSessionService> device_bound_session_service) {
device_bound_session_service_ = std::move(device_bound_session_service);
}
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
void URLRequestContextBuilder::BindToNetwork(
handles::NetworkHandle network,
std::optional<HostResolver::ManagerOptions> options) {
@@ -492,6 +503,11 @@ std::unique_ptr<URLRequestContext> URLRequestContextBuilder::Build() {
}
#endif // BUILDFLAG(ENABLE_REPORTING)
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
context->set_device_bound_session_service(
std::move(device_bound_session_service_));
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
HttpNetworkSessionContext network_session_context;
// Unlike the other fields of HttpNetworkSession::Context,
// |client_socket_factory| is not mirrored in URLRequestContext.

@@ -65,6 +65,10 @@ struct ReportingPolicy;
class PersistentReportingAndNelStore;
#endif // BUILDFLAG(ENABLE_REPORTING)
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
class DeviceBoundSessionService;
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
// A URLRequestContextBuilder creates a single URLRequestContext. It provides
// methods to manage various URLRequestContext components which should be called
// before creating the Context. Once configuration is complete, calling Build()
@@ -345,6 +349,11 @@ class NET_EXPORT URLRequestContextBuilder {
cookie_deprecation_label_ = label;
}
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
void set_device_bound_session_service(
std::unique_ptr<DeviceBoundSessionService> device_bound_session_service);
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
// Binds the context to `network`. All requests scheduled through the context
// built by this builder will be sent using `network`. Requests will fail if
// `network` disconnects. `options` allows to specify the ManagerOptions that
@@ -451,6 +460,9 @@ class NET_EXPORT URLRequestContextBuilder {
std::unique_ptr<HttpServerProperties> http_server_properties_;
std::map<std::string, std::unique_ptr<URLRequestJobFactory::ProtocolHandler>>
protocol_handlers_;
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
std::unique_ptr<DeviceBoundSessionService> device_bound_session_service_;
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
raw_ptr<ClientSocketFactory> client_socket_factory_raw_ = nullptr;
};

@@ -103,6 +103,11 @@
#include "net/android/network_library.h"
#endif
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
#include "net/device_bound_sessions/bound_session_registration_fetcher_param.h"
#include "net/device_bound_sessions/device_bound_session_service.h"
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
namespace {
// These values are persisted to logs. Entries should not be renumbered and
@@ -523,6 +528,9 @@ void URLRequestHttpJob::NotifyHeadersComplete() {
}
ProcessStrictTransportSecurityHeader();
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
ProcessDeviceBoundSessionsHeader();
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
// Clear |set_cookie_access_result_list_| after any processing in case
// SaveCookiesAndNotifyHeadersComplete is called again.
@@ -1055,6 +1063,19 @@ void URLRequestHttpJob::OnSetCookieResult(const CookieOptions& options,
NotifyHeadersComplete();
}
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
void URLRequestHttpJob::ProcessDeviceBoundSessionsHeader() {
std::vector<BoundSessionRegistrationFetcherParam> params =
BoundSessionRegistrationFetcherParam::CreateIfValid(request_->url(),
GetResponseHeaders());
if (auto* service = request_->context()->device_bound_session_service()) {
for (const auto& param : params) {
service->RegisterBoundSession(param);
}
}
}
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
void URLRequestHttpJob::ProcessStrictTransportSecurityHeader() {
DCHECK(response_info_);
TransportSecurityState* security_state =

@@ -125,6 +125,11 @@ class NET_EXPORT_PRIVATE URLRequestHttpJob : public URLRequestJob {
CookieAccessResultList& excluded_cookies) const;
void SaveCookiesAndNotifyHeadersComplete(int result);
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
// Process the DBSC header, if one exists.
void ProcessDeviceBoundSessionsHeader();
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
// Processes the Strict-Transport-Security header, if one exists.
void ProcessStrictTransportSecurityHeader();

@@ -68,6 +68,11 @@
#include "net/android/net_test_support_jni/AndroidNetworkLibraryTestUtil_jni.h"
#endif
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
#include "net/device_bound_sessions/device_bound_session_service.h"
#include "net/device_bound_sessions/test_util.h"
#endif
using net::test::IsError;
using net::test::IsOk;
@@ -1170,6 +1175,86 @@ TEST_F(URLRequestHttpJobTest, ShouldBypassHSTS) {
}
}
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
class URLRequestHttpJobWithMockSocketsDeviceBoundSessionServiceTest
: public TestWithTaskEnvironment {
protected:
URLRequestHttpJobWithMockSocketsDeviceBoundSessionServiceTest() {
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->set_client_socket_factory_for_testing(&socket_factory_);
context_builder->set_device_bound_session_service(
std::make_unique<testing::StrictMock<DeviceBoundSessionServiceMock>>());
context_ = context_builder->Build();
request_ = context_->CreateRequest(GURL("http://www.example.com"),
DEFAULT_PRIORITY, &delegate_,
TRAFFIC_ANNOTATION_FOR_TESTS);
}
DeviceBoundSessionServiceMock& GetMockService() {
return *static_cast<DeviceBoundSessionServiceMock*>(
context_->device_bound_session_service());
}
MockClientSocketFactory socket_factory_;
std::unique_ptr<URLRequestContext> context_;
TestDelegate delegate_;
std::unique_ptr<URLRequest> request_;
};
TEST_F(URLRequestHttpJobWithMockSocketsDeviceBoundSessionServiceTest,
ShouldRespondToDeviceBoundSessionHeader) {
const MockWrite writes[] = {
MockWrite("GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: en-us,fr\r\n\r\n")};
const MockRead reads[] = {
MockRead(
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Sec-Session-Registration: \"new\";challenge=:Y29kZWQ=:;es256\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
request_->Start();
EXPECT_CALL(GetMockService(), RegisterBoundSession).Times(1);
delegate_.RunUntilComplete();
EXPECT_THAT(delegate_.request_status(), IsOk());
}
TEST_F(URLRequestHttpJobWithMockSocketsDeviceBoundSessionServiceTest,
ShouldNotRespondWithoutDeviceBoundSessionHeader) {
const MockWrite writes[] = {
MockWrite("GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: en-us,fr\r\n\r\n")};
const MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
request_->Start();
EXPECT_CALL(GetMockService(), RegisterBoundSession).Times(0);
delegate_.RunUntilComplete();
EXPECT_THAT(delegate_.request_status(), IsOk());
}
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
namespace {
std::unique_ptr<test_server::HttpResponse> HandleRequest(
const std::string_view& content,

@@ -189,6 +189,10 @@
#include "base/android/application_status_listener.h"
#endif // BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
#include "net/device_bound_sessions/device_bound_session_service.h"
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
namespace network {
namespace {
@@ -2681,6 +2685,13 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext(
builder.set_cookie_deprecation_label(*params_->cookie_deprecation_label);
}
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
if (params_->device_bound_sessions_enabled) {
builder.set_device_bound_session_service(
net::DeviceBoundSessionService::Create());
}
#endif
if (on_url_request_context_builder_configured) {
std::move(on_url_request_context_builder_configured).Run(&builder);
}

@@ -799,6 +799,42 @@ TEST_F(NetworkContextTest, EnableReportingWithStore) {
->store());
}
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
TEST_F(NetworkContextTest, DeviceBoundSessionsDefaultParam) {
mojom::NetworkContextParamsPtr context_params =
CreateNetworkContextParamsForTesting();
std::unique_ptr<NetworkContext> network_context =
CreateContextWithParams(std::move(context_params));
EXPECT_FALSE(
network_context->url_request_context()->device_bound_session_service());
}
TEST_F(NetworkContextTest, DeviceBoundSessionsEnableParam) {
mojom::NetworkContextParamsPtr context_params =
CreateNetworkContextParamsForTesting();
context_params->device_bound_sessions_enabled = true;
std::unique_ptr<NetworkContext> network_context =
CreateContextWithParams(std::move(context_params));
EXPECT_TRUE(
network_context->url_request_context()->device_bound_session_service());
}
TEST_F(NetworkContextTest, DeviceBoundSessionsDisableParam) {
mojom::NetworkContextParamsPtr context_params =
CreateNetworkContextParamsForTesting();
context_params->device_bound_sessions_enabled = false;
std::unique_ptr<NetworkContext> network_context =
CreateContextWithParams(std::move(context_params));
EXPECT_FALSE(
network_context->url_request_context()->device_bound_session_service());
}
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
TEST_F(NetworkContextTest, DisableNetworkErrorLogging) {
base::test::ScopedFeatureList scoped_feature_list_;
scoped_feature_list_.InitAndDisableFeature(features::kNetworkErrorLogging);

@@ -598,6 +598,9 @@ struct NetworkContextParams {
// `cookie_encryption_provider` is called to obtain a valid set of keys for
// cookie encryption.
pending_remote<CookieEncryptionProvider>? cookie_encryption_provider;
// Enables Device Bound Session Credential for this network context.
bool device_bound_sessions_enabled = false;
};
struct NetworkConditions {