xref: /aosp_15_r20/external/cronet/net/base/network_isolation_key.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef NET_BASE_NETWORK_ISOLATION_KEY_H_
6 #define NET_BASE_NETWORK_ISOLATION_KEY_H_
7 
8 #include <optional>
9 #include <string>
10 
11 #include "base/types/pass_key.h"
12 #include "base/unguessable_token.h"
13 #include "net/base/net_export.h"
14 #include "net/base/schemeful_site.h"
15 
16 namespace url {
17 class Origin;
18 }
19 
20 namespace network::mojom {
21 class NonEmptyNetworkIsolationKeyDataView;
22 }
23 
24 namespace net {
25 class CookiePartitionKey;
26 class NetworkAnonymizationKey;
27 }
28 
29 namespace net {
30 
31 // Key used to isolate shared network stack resources used by requests based on
32 // the context on which they were made.
33 class NET_EXPORT NetworkIsolationKey {
34  public:
35   // Full constructor.  When a request is initiated by the top frame, it must
36   // also populate the |frame_site| parameter when calling this constructor.
37   NetworkIsolationKey(
38       const SchemefulSite& top_frame_site,
39       const SchemefulSite& frame_site,
40       const std::optional<base::UnguessableToken>& nonce = std::nullopt);
41 
42   // Alternative constructor that takes ownership of arguments, to save copies.
43   NetworkIsolationKey(
44       SchemefulSite&& top_frame_site,
45       SchemefulSite&& frame_site,
46       std::optional<base::UnguessableToken>&& nonce = std::nullopt);
47 
48   // Legacy constructor.
49   // TODO(https://crbug.com/1145294):  Remove this in favor of above
50   // constructor.
51   NetworkIsolationKey(const url::Origin& top_frame_origin,
52                       const url::Origin& frame_origin);
53 
54   // Construct an empty key.
55   NetworkIsolationKey();
56 
57   NetworkIsolationKey(const NetworkIsolationKey& network_isolation_key);
58   NetworkIsolationKey(NetworkIsolationKey&& network_isolation_key);
59 
60   ~NetworkIsolationKey();
61 
62   NetworkIsolationKey& operator=(
63       const NetworkIsolationKey& network_isolation_key);
64   NetworkIsolationKey& operator=(NetworkIsolationKey&& network_isolation_key);
65 
66   // Creates a transient non-empty NetworkIsolationKey by creating an opaque
67   // origin. This prevents the NetworkIsolationKey from sharing data with other
68   // NetworkIsolationKeys. Data for transient NetworkIsolationKeys is not
69   // persisted to disk.
70   static NetworkIsolationKey CreateTransientForTesting();
71 
72   // Creates a new key using |top_frame_site_| and |new_frame_site|.
73   NetworkIsolationKey CreateWithNewFrameSite(
74       const SchemefulSite& new_frame_site) const;
75 
76   // Compare keys for equality, true if all enabled fields are equal.
77   bool operator==(const NetworkIsolationKey& other) const {
78     switch (GetMode()) {
79       case Mode::kFrameSiteWithSharedOpaqueEnabled:
80         if ((frame_site_ && frame_site_->opaque()) &&
81             (other.frame_site_ && other.frame_site_->opaque())) {
82           return std::tie(top_frame_site_, nonce_) ==
83                  std::tie(other.top_frame_site_, other.nonce_);
84         }
85         [[fallthrough]];
86       case Mode::kFrameSiteEnabled:
87         return std::tie(top_frame_site_, frame_site_, nonce_) ==
88                std::tie(other.top_frame_site_, other.frame_site_, other.nonce_);
89       case Mode::kCrossSiteFlagEnabled:
90         return std::tie(top_frame_site_, is_cross_site_, nonce_) ==
91                std::tie(other.top_frame_site_, other.is_cross_site_,
92                         other.nonce_);
93     }
94   }
95 
96   // Compare keys for inequality, true if any enabled field varies.
97   bool operator!=(const NetworkIsolationKey& other) const {
98     return !(*this == other);
99   }
100 
101   // Provide an ordering for keys based on all enabled fields.
102   bool operator<(const NetworkIsolationKey& other) const {
103     switch (GetMode()) {
104       case Mode::kFrameSiteWithSharedOpaqueEnabled:
105         if ((frame_site_ && frame_site_->opaque()) &&
106             (other.frame_site_ && other.frame_site_->opaque())) {
107           return std::tie(top_frame_site_, nonce_) <
108                  std::tie(other.top_frame_site_, other.nonce_);
109         }
110         [[fallthrough]];
111       case Mode::kFrameSiteEnabled:
112         return std::tie(top_frame_site_, frame_site_, nonce_) <
113                std::tie(other.top_frame_site_, other.frame_site_, other.nonce_);
114       case Mode::kCrossSiteFlagEnabled:
115         return std::tie(top_frame_site_, is_cross_site_, nonce_) <
116                std::tie(other.top_frame_site_, other.is_cross_site_,
117                         other.nonce_);
118     }
119   }
120 
121   // Returns the string representation of the key for use in string-keyed disk
122   // cache. This is the string representation of each piece of the key separated
123   // by spaces. Returns nullopt if the network isolation key is transient, in
124   // which case, nothing should typically be saved to disk using the key.
125   std::optional<std::string> ToCacheKeyString() const;
126 
127   // Returns string for debugging. Difference from ToString() is that transient
128   // entries may be distinguishable from each other.
129   std::string ToDebugString() const;
130 
131   // Returns true if all parts of the key are non-empty.
132   bool IsFullyPopulated() const;
133 
134   // Returns true if this key's lifetime is short-lived, or if
135   // IsFullyPopulated() returns true. It may not make sense to persist state to
136   // disk related to it (e.g., disk cache).
137   bool IsTransient() const;
138 
139   // Getters for the top frame and frame sites. These accessors are primarily
140   // intended for IPC calls, and to be able to create an IsolationInfo from a
141   // NetworkIsolationKey.
GetTopFrameSite()142   const std::optional<SchemefulSite>& GetTopFrameSite() const {
143     return top_frame_site_;
144   }
145 
146   enum class Mode {
147     // This scheme indicates that "triple-key" NetworkIsolationKeys are used to
148     // partition the HTTP cache. This key will have the following properties:
149     // `top_frame_site` -> the schemeful site of the top level page.
150     // `frame_site ` -> the schemeful site of the frame.
151     // `is_cross_site` -> std::nullopt.
152     kFrameSiteEnabled,
153     // This scheme indicates that "2.5-key" NetworkIsolationKeys are used to
154     // partition the HTTP cache. This key will have the following properties:
155     // `top_frame_site_` -> the schemeful site of the top level page.
156     // `frame_site_` -> should only be accessed for serialization or building
157     // nonced CookiePartitionKeys.
158     // `is_cross_site_` -> a boolean indicating whether the frame site is
159     // schemefully cross-site from the top-level site.
160     kCrossSiteFlagEnabled,
161     // This scheme indicates that "triple-key" NetworkIsolationKeys are used to
162     // partition the HTTP cache except when the frame site has an opaque origin.
163     // In that case, a fixed value will be used instead of the frame site such
164     // that all opaque origin frames under a given top-level site share a cache
165     // partition (unless the NIK is explicitly provided a nonce).
166     kFrameSiteWithSharedOpaqueEnabled,
167   };
168 
169   // Returns the cache key scheme currently in use.
170   static Mode GetMode();
171 
172   // Do not use outside of testing. Returns the `frame_site_`.
GetFrameSiteForTesting()173   const std::optional<SchemefulSite> GetFrameSiteForTesting() const {
174     if (GetMode() == Mode::kFrameSiteEnabled ||
175         GetMode() == Mode::kFrameSiteWithSharedOpaqueEnabled) {
176       return frame_site_;
177     }
178     return std::nullopt;
179   }
180 
181   // Do not use outside of testing. Returns `is_cross_site_`.
GetIsCrossSiteForTesting()182   const std::optional<bool> GetIsCrossSiteForTesting() const {
183     if (GetMode() == Mode::kCrossSiteFlagEnabled) {
184       return is_cross_site_;
185     }
186     return std::nullopt;
187   }
188 
189   // When serializing a NIK for sending via mojo we want to access the frame
190   // site directly. We don't want to expose this broadly, though, hence the
191   // passkey.
192   using SerializationPassKey = base::PassKey<struct mojo::StructTraits<
193       network::mojom::NonEmptyNetworkIsolationKeyDataView,
194       NetworkIsolationKey>>;
GetFrameSiteForSerialization(SerializationPassKey)195   const std::optional<SchemefulSite>& GetFrameSiteForSerialization(
196       SerializationPassKey) const {
197     CHECK(!IsEmpty());
198     return frame_site_;
199   }
200   // We also need to access the frame site directly when constructing
201   // CookiePartitionKey for nonced partitions. We also use a passkey for this
202   // case.
203   using CookiePartitionKeyPassKey = base::PassKey<CookiePartitionKey>;
GetFrameSiteForCookiePartitionKey(CookiePartitionKeyPassKey)204   const std::optional<SchemefulSite>& GetFrameSiteForCookiePartitionKey(
205       CookiePartitionKeyPassKey) const {
206     CHECK(!IsEmpty());
207     return frame_site_;
208   }
209   // Same as above but for constructing a `NetworkAnonymizationKey()` from this
210   // NIK.
211   using NetworkAnonymizationKeyPassKey = base::PassKey<NetworkAnonymizationKey>;
GetFrameSiteForNetworkAnonymizationKey(NetworkAnonymizationKeyPassKey)212   const std::optional<SchemefulSite>& GetFrameSiteForNetworkAnonymizationKey(
213       NetworkAnonymizationKeyPassKey) const {
214     CHECK(!IsEmpty());
215     return frame_site_;
216   }
217 
218   // Getter for the nonce.
GetNonce()219   const std::optional<base::UnguessableToken>& GetNonce() const {
220     return nonce_;
221   }
222 
223   // Returns true if all parts of the key are empty.
224   bool IsEmpty() const;
225 
226  private:
227   // Whether this key has opaque origins or a nonce.
228   bool IsOpaque() const;
229 
230   // The origin/etld+1 of the top frame of the page making the request.
231   std::optional<SchemefulSite> top_frame_site_;
232 
233   // The origin/etld+1 of the frame that initiates the request.
234   std::optional<SchemefulSite> frame_site_;
235 
236   // A boolean indicating whether the frame origin is cross-site from the
237   // top-level origin. This will be used for experiments to determine the
238   // the difference in performance between partitioning the HTTP cache using the
239   // top-level origin and frame origin ("triple-keying") vs. the top-level
240   // origin and an is-cross-site bit ("2.5-keying") like the
241   // `NetworkAnonymizationKey` uses for network state partitioning. This will be
242   // std::nullopt when `GetMode()` returns `Mode::kFrameSiteEnabled` or
243   // `Mode::kFrameSiteWithSharedOpaqueEnabled`, or for an empty
244   // `NetworkIsolationKey`.
245   std::optional<bool> is_cross_site_;
246 
247   // Having a nonce is a way to force a transient opaque `NetworkIsolationKey`
248   // for non-opaque origins.
249   std::optional<base::UnguessableToken> nonce_;
250 };
251 
252 }  // namespace net
253 
254 #endif  // NET_BASE_NETWORK_ISOLATION_KEY_H_
255