blob: 7784c50e8e3ce1af9247ff57a7aadce1676ec6d5 [file] [log] [blame]
// Copyright 2019 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_BASE_NETWORK_ISOLATION_KEY_H_
#define NET_BASE_NETWORK_ISOLATION_KEY_H_
#include <string>
#include "base/types/pass_key.h"
#include "base/unguessable_token.h"
#include "net/base/net_export.h"
#include "net/base/schemeful_site.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace url {
class Origin;
}
namespace network::mojom {
class NonEmptyNetworkIsolationKeyDataView;
}
namespace net {
class CookiePartitionKey;
class NetworkAnonymizationKey;
}
namespace net {
// Key used to isolate shared network stack resources used by requests based on
// the context on which they were made.
class NET_EXPORT NetworkIsolationKey {
public:
// Full constructor. When a request is initiated by the top frame, it must
// also populate the |frame_site| parameter when calling this constructor.
NetworkIsolationKey(
const SchemefulSite& top_frame_site,
const SchemefulSite& frame_site,
const absl::optional<base::UnguessableToken>& nonce = absl::nullopt);
// Alternative constructor that takes ownership of arguments, to save copies.
NetworkIsolationKey(
SchemefulSite&& top_frame_site,
SchemefulSite&& frame_site,
absl::optional<base::UnguessableToken>&& nonce = absl::nullopt);
// Legacy constructor.
// TODO(https://crbug.com/1145294): Remove this in favor of above
// constructor.
NetworkIsolationKey(const url::Origin& top_frame_origin,
const url::Origin& frame_origin);
// Construct an empty key.
NetworkIsolationKey();
NetworkIsolationKey(const NetworkIsolationKey& network_isolation_key);
NetworkIsolationKey(NetworkIsolationKey&& network_isolation_key);
~NetworkIsolationKey();
NetworkIsolationKey& operator=(
const NetworkIsolationKey& network_isolation_key);
NetworkIsolationKey& operator=(NetworkIsolationKey&& network_isolation_key);
// Creates a transient non-empty NetworkIsolationKey by creating an opaque
// origin. This prevents the NetworkIsolationKey from sharing data with other
// NetworkIsolationKeys. Data for transient NetworkIsolationKeys is not
// persisted to disk.
static NetworkIsolationKey CreateTransientForTesting();
// Creates a new key using |top_frame_site_| and |new_frame_site|.
NetworkIsolationKey CreateWithNewFrameSite(
const SchemefulSite& new_frame_site) const;
// Compare keys for equality, true if all enabled fields are equal.
bool operator==(const NetworkIsolationKey& other) const {
switch (GetMode()) {
case Mode::kFrameSiteWithSharedOpaqueEnabled:
if ((frame_site_ && frame_site_->opaque()) &&
(other.frame_site_ && other.frame_site_->opaque())) {
return std::tie(top_frame_site_, nonce_) ==
std::tie(other.top_frame_site_, other.nonce_);
}
[[fallthrough]];
case Mode::kFrameSiteEnabled:
return std::tie(top_frame_site_, frame_site_, nonce_) ==
std::tie(other.top_frame_site_, other.frame_site_, other.nonce_);
case Mode::kCrossSiteFlagEnabled:
return std::tie(top_frame_site_, is_cross_site_, nonce_) ==
std::tie(other.top_frame_site_, other.is_cross_site_,
other.nonce_);
}
}
// Compare keys for inequality, true if any enabled field varies.
bool operator!=(const NetworkIsolationKey& other) const {
return !(*this == other);
}
// Provide an ordering for keys based on all enabled fields.
bool operator<(const NetworkIsolationKey& other) const {
switch (GetMode()) {
case Mode::kFrameSiteWithSharedOpaqueEnabled:
if ((frame_site_ && frame_site_->opaque()) &&
(other.frame_site_ && other.frame_site_->opaque())) {
return std::tie(top_frame_site_, nonce_) <
std::tie(other.top_frame_site_, other.nonce_);
}
[[fallthrough]];
case Mode::kFrameSiteEnabled:
return std::tie(top_frame_site_, frame_site_, nonce_) <
std::tie(other.top_frame_site_, other.frame_site_, other.nonce_);
case Mode::kCrossSiteFlagEnabled:
return std::tie(top_frame_site_, is_cross_site_, nonce_) <
std::tie(other.top_frame_site_, other.is_cross_site_,
other.nonce_);
}
}
// Returns the string representation of the key for use in string-keyed disk
// cache. This is the string representation of each piece of the key separated
// by spaces. Returns nullopt if the network isolation key is transient, in
// which case, nothing should typically be saved to disk using the key.
absl::optional<std::string> ToCacheKeyString() const;
// Returns string for debugging. Difference from ToString() is that transient
// entries may be distinguishable from each other.
std::string ToDebugString() const;
// Returns true if all parts of the key are non-empty.
bool IsFullyPopulated() const;
// Returns true if this key's lifetime is short-lived, or if
// IsFullyPopulated() returns true. It may not make sense to persist state to
// disk related to it (e.g., disk cache).
bool IsTransient() const;
// Getters for the top frame and frame sites. These accessors are primarily
// intended for IPC calls, and to be able to create an IsolationInfo from a
// NetworkIsolationKey.
const absl::optional<SchemefulSite>& GetTopFrameSite() const {
return top_frame_site_;
}
enum class Mode {
// This scheme indicates that "triple-key" NetworkIsolationKeys are used to
// partition the HTTP cache. This key will have the following properties:
// `top_frame_site` -> the schemeful site of the top level page.
// `frame_site ` -> the schemeful site of the frame.
// `is_cross_site` -> absl::nullopt.
kFrameSiteEnabled,
// This scheme indicates that "2.5-key" NetworkIsolationKeys are used to
// partition the HTTP cache. This key will have the following properties:
// `top_frame_site_` -> the schemeful site of the top level page.
// `frame_site_` -> should only be accessed for serialization or building
// nonced CookiePartitionKeys.
// `is_cross_site_` -> a boolean indicating whether the frame site is
// schemefully cross-site from the top-level site.
kCrossSiteFlagEnabled,
// This scheme indicates that "triple-key" NetworkIsolationKeys are used to
// partition the HTTP cache except when the frame site has an opaque origin.
// In that case, a fixed value will be used instead of the frame site such
// that all opaque origin frames under a given top-level site share a cache
// partition (unless the NIK is explicitly provided a nonce).
kFrameSiteWithSharedOpaqueEnabled,
};
// Returns the cache key scheme currently in use.
static Mode GetMode();
// Do not use outside of testing. Returns the `frame_site_`.
const absl::optional<SchemefulSite> GetFrameSiteForTesting() const {
if (GetMode() == Mode::kFrameSiteEnabled ||
GetMode() == Mode::kFrameSiteWithSharedOpaqueEnabled) {
return frame_site_;
}
return absl::nullopt;
}
// Do not use outside of testing. Returns `is_cross_site_`.
const absl::optional<bool> GetIsCrossSiteForTesting() const {
if (GetMode() == Mode::kCrossSiteFlagEnabled) {
return is_cross_site_;
}
return absl::nullopt;
}
// When serializing a NIK for sending via mojo we want to access the frame
// site directly. We don't want to expose this broadly, though, hence the
// passkey.
using SerializationPassKey = base::PassKey<struct mojo::StructTraits<
network::mojom::NonEmptyNetworkIsolationKeyDataView,
NetworkIsolationKey>>;
const absl::optional<SchemefulSite>& GetFrameSiteForSerialization(
SerializationPassKey) const {
CHECK(!IsEmpty());
return frame_site_;
}
// We also need to access the frame site directly when constructing
// CookiePartitionKey for nonced partitions. We also use a passkey for this
// case.
using CookiePartitionKeyPassKey = base::PassKey<CookiePartitionKey>;
const absl::optional<SchemefulSite>& GetFrameSiteForCookiePartitionKey(
CookiePartitionKeyPassKey) const {
CHECK(!IsEmpty());
return frame_site_;
}
// Same as above but for constructing a `NetworkAnonymizationKey()` from this
// NIK.
using NetworkAnonymizationKeyPassKey = base::PassKey<NetworkAnonymizationKey>;
const absl::optional<SchemefulSite>& GetFrameSiteForNetworkAnonymizationKey(
NetworkAnonymizationKeyPassKey) const {
CHECK(!IsEmpty());
return frame_site_;
}
// Getter for the nonce.
const absl::optional<base::UnguessableToken>& GetNonce() const {
return nonce_;
}
// Returns true if all parts of the key are empty.
bool IsEmpty() const;
private:
// Whether this key has opaque origins or a nonce.
bool IsOpaque() const;
// The origin/etld+1 of the top frame of the page making the request.
absl::optional<SchemefulSite> top_frame_site_;
// The origin/etld+1 of the frame that initiates the request.
absl::optional<SchemefulSite> frame_site_;
// A boolean indicating whether the frame origin is cross-site from the
// top-level origin. This will be used for experiments to determine the
// the difference in performance between partitioning the HTTP cache using the
// top-level origin and frame origin ("triple-keying") vs. the top-level
// origin and an is-cross-site bit ("2.5-keying") like the
// `NetworkAnonymizationKey` uses for network state partitioning. This will be
// absl::nullopt when `GetMode()` returns `Mode::kFrameSiteEnabled` or
// `Mode::kFrameSiteWithSharedOpaqueEnabled`, or for an empty
// `NetworkIsolationKey`.
absl::optional<bool> is_cross_site_;
// Having a nonce is a way to force a transient opaque `NetworkIsolationKey`
// for non-opaque origins.
absl::optional<base::UnguessableToken> nonce_;
};
} // namespace net
#endif // NET_BASE_NETWORK_ISOLATION_KEY_H_