xref: /aosp_15_r20/external/cronet/net/base/network_anonymization_key.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2022 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 #include "net/base/network_anonymization_key.h"
5 
6 #include <atomic>
7 #include <optional>
8 
9 #include "base/feature_list.h"
10 #include "base/unguessable_token.h"
11 #include "base/values.h"
12 #include "net/base/features.h"
13 #include "net/base/net_export.h"
14 #include "net/base/network_isolation_key.h"
15 #include "net/base/schemeful_site.h"
16 #include "net/cookies/site_for_cookies.h"
17 
18 namespace net {
19 
20 namespace {
21 
22 // True if network state partitioning should be enabled regardless of feature
23 // settings.
24 bool g_partition_by_default = false;
25 
26 // True if NAK::IsPartitioningEnabled has been called, and the value of
27 // `g_partition_by_default` cannot be changed.
28 ABSL_CONST_INIT std::atomic<bool> g_partition_by_default_locked = false;
29 
30 }  // namespace
31 
NetworkAnonymizationKey(const SchemefulSite & top_frame_site,bool is_cross_site,std::optional<base::UnguessableToken> nonce)32 NetworkAnonymizationKey::NetworkAnonymizationKey(
33     const SchemefulSite& top_frame_site,
34     bool is_cross_site,
35     std::optional<base::UnguessableToken> nonce)
36     : top_frame_site_(top_frame_site),
37       is_cross_site_(is_cross_site),
38       nonce_(nonce) {
39   DCHECK(top_frame_site_.has_value());
40 }
41 
CreateFromFrameSite(const SchemefulSite & top_frame_site,const SchemefulSite & frame_site,std::optional<base::UnguessableToken> nonce)42 NetworkAnonymizationKey NetworkAnonymizationKey::CreateFromFrameSite(
43     const SchemefulSite& top_frame_site,
44     const SchemefulSite& frame_site,
45     std::optional<base::UnguessableToken> nonce) {
46   bool is_cross_site = top_frame_site != frame_site;
47   return NetworkAnonymizationKey(top_frame_site, is_cross_site, nonce);
48 }
49 
CreateFromNetworkIsolationKey(const net::NetworkIsolationKey & network_isolation_key)50 NetworkAnonymizationKey NetworkAnonymizationKey::CreateFromNetworkIsolationKey(
51     const net::NetworkIsolationKey& network_isolation_key) {
52   // We cannot create a valid NetworkAnonymizationKey from a NetworkIsolationKey
53   // that is not fully populated.
54   if (!network_isolation_key.IsFullyPopulated()) {
55     return NetworkAnonymizationKey();
56   }
57 
58   return CreateFromFrameSite(
59       network_isolation_key.GetTopFrameSite().value(),
60       network_isolation_key
61           .GetFrameSiteForNetworkAnonymizationKey(
62               NetworkIsolationKey::NetworkAnonymizationKeyPassKey())
63           .value(),
64       network_isolation_key.GetNonce());
65 }
66 
NetworkAnonymizationKey()67 NetworkAnonymizationKey::NetworkAnonymizationKey()
68     : top_frame_site_(std::nullopt),
69       is_cross_site_(false),
70       nonce_(std::nullopt) {}
71 
72 NetworkAnonymizationKey::NetworkAnonymizationKey(
73     const NetworkAnonymizationKey& network_anonymization_key) = default;
74 
75 NetworkAnonymizationKey::NetworkAnonymizationKey(
76     NetworkAnonymizationKey&& network_anonymization_key) = default;
77 
78 NetworkAnonymizationKey::~NetworkAnonymizationKey() = default;
79 
80 NetworkAnonymizationKey& NetworkAnonymizationKey::operator=(
81     const NetworkAnonymizationKey& network_anonymization_key) = default;
82 
83 NetworkAnonymizationKey& NetworkAnonymizationKey::operator=(
84     NetworkAnonymizationKey&& network_anonymization_key) = default;
85 
CreateTransient()86 NetworkAnonymizationKey NetworkAnonymizationKey::CreateTransient() {
87   SchemefulSite site_with_opaque_origin;
88   return NetworkAnonymizationKey(site_with_opaque_origin, false);
89 }
90 
ToDebugString() const91 std::string NetworkAnonymizationKey::ToDebugString() const {
92   if (!IsFullyPopulated()) {
93     return "null";
94   }
95 
96   std::string str = GetSiteDebugString(top_frame_site_);
97   str += IsCrossSite() ? " cross_site" : " same_site";
98 
99   // Currently, if the NAK has a nonce it will be marked transient. For debug
100   // purposes we will print the value but if called via
101   // `NetworkAnonymizationKey::ToString` we will have already returned "".
102   if (nonce_.has_value()) {
103     str += " (with nonce " + nonce_->ToString() + ")";
104   }
105 
106   return str;
107 }
108 
IsEmpty() const109 bool NetworkAnonymizationKey::IsEmpty() const {
110   return !top_frame_site_.has_value();
111 }
112 
IsFullyPopulated() const113 bool NetworkAnonymizationKey::IsFullyPopulated() const {
114   return top_frame_site_.has_value();
115 }
116 
IsTransient() const117 bool NetworkAnonymizationKey::IsTransient() const {
118   if (!IsFullyPopulated())
119     return true;
120 
121   return top_frame_site_->opaque() || nonce_.has_value();
122 }
123 
ToValue(base::Value * out_value) const124 bool NetworkAnonymizationKey::ToValue(base::Value* out_value) const {
125   if (IsEmpty()) {
126     *out_value = base::Value(base::Value::Type::LIST);
127     return true;
128   }
129 
130   if (IsTransient())
131     return false;
132 
133   std::optional<std::string> top_frame_value =
134       SerializeSiteWithNonce(*top_frame_site_);
135   if (!top_frame_value)
136     return false;
137   base::Value::List list;
138   list.Append(std::move(top_frame_value).value());
139 
140   list.Append(IsCrossSite());
141 
142   *out_value = base::Value(std::move(list));
143   return true;
144 }
145 
FromValue(const base::Value & value,NetworkAnonymizationKey * network_anonymization_key)146 bool NetworkAnonymizationKey::FromValue(
147     const base::Value& value,
148     NetworkAnonymizationKey* network_anonymization_key) {
149   if (!value.is_list()) {
150     return false;
151   }
152 
153   const base::Value::List& list = value.GetList();
154   if (list.empty()) {
155     *network_anonymization_key = NetworkAnonymizationKey();
156     return true;
157   }
158 
159   // Check the format.
160   if (list.size() != 2 || !list[0].is_string() || !list[1].is_bool()) {
161     return false;
162   }
163 
164   // Check top_level_site is valid for any key scheme
165   std::optional<SchemefulSite> top_frame_site =
166       SchemefulSite::DeserializeWithNonce(
167           base::PassKey<NetworkAnonymizationKey>(), list[0].GetString());
168   if (!top_frame_site) {
169     return false;
170   }
171 
172   bool is_cross_site = list[1].GetBool();
173 
174   *network_anonymization_key =
175       NetworkAnonymizationKey(top_frame_site.value(), is_cross_site);
176   return true;
177 }
178 
GetSiteDebugString(const std::optional<SchemefulSite> & site) const179 std::string NetworkAnonymizationKey::GetSiteDebugString(
180     const std::optional<SchemefulSite>& site) const {
181   return site ? site->GetDebugString() : "null";
182 }
183 
SerializeSiteWithNonce(const SchemefulSite & site)184 std::optional<std::string> NetworkAnonymizationKey::SerializeSiteWithNonce(
185     const SchemefulSite& site) {
186   return *(const_cast<SchemefulSite&>(site).SerializeWithNonce(
187       base::PassKey<NetworkAnonymizationKey>()));
188 }
189 
190 // static
IsPartitioningEnabled()191 bool NetworkAnonymizationKey::IsPartitioningEnabled() {
192   g_partition_by_default_locked.store(true, std::memory_order_relaxed);
193   return g_partition_by_default ||
194          base::FeatureList::IsEnabled(
195              features::kSplitHostCacheByNetworkIsolationKey) ||
196          base::FeatureList::IsEnabled(
197              features::kPartitionConnectionsByNetworkIsolationKey) ||
198          base::FeatureList::IsEnabled(
199              features::kPartitionHttpServerPropertiesByNetworkIsolationKey) ||
200          base::FeatureList::IsEnabled(
201              features::kPartitionSSLSessionsByNetworkIsolationKey) ||
202          base::FeatureList::IsEnabled(
203              features::kPartitionNelAndReportingByNetworkIsolationKey);
204 }
205 
206 // static
PartitionByDefault()207 void NetworkAnonymizationKey::PartitionByDefault() {
208   DCHECK(!g_partition_by_default_locked.load(std::memory_order_relaxed));
209   // Only set the global if none of the relevant features are overridden.
210   if (!base::FeatureList::GetInstance()->IsFeatureOverridden(
211           "SplitHostCacheByNetworkIsolationKey") &&
212       !base::FeatureList::GetInstance()->IsFeatureOverridden(
213           "PartitionConnectionsByNetworkIsolationKey") &&
214       !base::FeatureList::GetInstance()->IsFeatureOverridden(
215           "PartitionHttpServerPropertiesByNetworkIsolationKey") &&
216       !base::FeatureList::GetInstance()->IsFeatureOverridden(
217           "PartitionSSLSessionsByNetworkIsolationKey") &&
218       !base::FeatureList::GetInstance()->IsFeatureOverridden(
219           "PartitionNelAndReportingByNetworkIsolationKey")) {
220     g_partition_by_default = true;
221   }
222 }
223 
224 // static
ClearGlobalsForTesting()225 void NetworkAnonymizationKey::ClearGlobalsForTesting() {
226   g_partition_by_default = false;
227   g_partition_by_default_locked.store(false);
228 }
229 
230 }  // namespace net
231