xref: /aosp_15_r20/external/cronet/net/cookies/cookie_partition_key.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2021 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 #include "net/cookies/cookie_partition_key.h"
6 
7 #include <ostream>
8 #include <tuple>
9 
10 #include "base/feature_list.h"
11 #include "base/logging.h"
12 #include "base/types/optional_util.h"
13 #include "net/base/cronet_buildflags.h"
14 #include "net/cookies/cookie_constants.h"
15 #include "net/cookies/site_for_cookies.h"
16 
17 #if !BUILDFLAG(CRONET_BUILD)
18 #include "mojo/public/cpp/bindings/default_construct_tag.h"
19 #endif
20 
21 namespace net {
22 
23 namespace {
24 
WarnAndCreateUnexpected(const std::string & message)25 base::unexpected<std::string> WarnAndCreateUnexpected(
26     const std::string& message) {
27   DLOG(WARNING) << message;
28   return base::unexpected(message);
29 }
30 
31 }  // namespace
32 
SerializedCookiePartitionKey(const std::string & site,bool has_cross_site_ancestor)33 CookiePartitionKey::SerializedCookiePartitionKey::SerializedCookiePartitionKey(
34     const std::string& site,
35     bool has_cross_site_ancestor)
36     : top_level_site_(site),
37       has_cross_site_ancestor_(has_cross_site_ancestor) {}
38 
39 const std::string&
TopLevelSite() const40 CookiePartitionKey::SerializedCookiePartitionKey::TopLevelSite() const {
41   return top_level_site_;
42 }
43 
44 #if !BUILDFLAG(CRONET_BUILD)
CookiePartitionKey(mojo::DefaultConstruct::Tag)45 CookiePartitionKey::CookiePartitionKey(mojo::DefaultConstruct::Tag) {}
46 #endif
has_cross_site_ancestor() const47 bool CookiePartitionKey::SerializedCookiePartitionKey::has_cross_site_ancestor()
48     const {
49   return has_cross_site_ancestor_;
50 }
51 
52 // static
BoolToAncestorChainBit(bool cross_site)53 CookiePartitionKey::AncestorChainBit CookiePartitionKey::BoolToAncestorChainBit(
54     bool cross_site) {
55   return cross_site ? AncestorChainBit::kCrossSite
56                     : AncestorChainBit::kSameSite;
57 }
58 
CookiePartitionKey(const SchemefulSite & site,std::optional<base::UnguessableToken> nonce,AncestorChainBit ancestor_chain_bit)59 CookiePartitionKey::CookiePartitionKey(
60     const SchemefulSite& site,
61     std::optional<base::UnguessableToken> nonce,
62     AncestorChainBit ancestor_chain_bit)
63     : site_(site), nonce_(nonce), ancestor_chain_bit_(ancestor_chain_bit) {
64   if (nonce.has_value()) {
65     CHECK_EQ(ancestor_chain_bit, AncestorChainBit::kCrossSite);
66   }
67 }
68 
CookiePartitionKey(bool from_script)69 CookiePartitionKey::CookiePartitionKey(bool from_script)
70     : from_script_(from_script) {}
71 
72 CookiePartitionKey::CookiePartitionKey(const CookiePartitionKey& other) =
73     default;
74 
75 CookiePartitionKey::CookiePartitionKey(CookiePartitionKey&& other) = default;
76 
77 CookiePartitionKey& CookiePartitionKey::operator=(
78     const CookiePartitionKey& other) = default;
79 
80 CookiePartitionKey& CookiePartitionKey::operator=(CookiePartitionKey&& other) =
81     default;
82 
83 CookiePartitionKey::~CookiePartitionKey() = default;
84 
operator ==(const CookiePartitionKey & other) const85 bool CookiePartitionKey::operator==(const CookiePartitionKey& other) const {
86   AncestorChainBit this_bit = MaybeAncestorChainBit();
87   AncestorChainBit other_bit = other.MaybeAncestorChainBit();
88 
89   return std::tie(site_, nonce_, this_bit) ==
90          std::tie(other.site_, other.nonce_, other_bit);
91 }
92 
operator !=(const CookiePartitionKey & other) const93 bool CookiePartitionKey::operator!=(const CookiePartitionKey& other) const {
94   return !(*this == other);
95 }
96 
operator <(const CookiePartitionKey & other) const97 bool CookiePartitionKey::operator<(const CookiePartitionKey& other) const {
98   AncestorChainBit this_bit = MaybeAncestorChainBit();
99   AncestorChainBit other_bit = other.MaybeAncestorChainBit();
100   return std::tie(site_, nonce_, this_bit) <
101          std::tie(other.site_, other.nonce_, other_bit);
102 }
103 
104 // static
105 base::expected<CookiePartitionKey::SerializedCookiePartitionKey, std::string>
Serialize(const std::optional<CookiePartitionKey> & in)106 CookiePartitionKey::Serialize(const std::optional<CookiePartitionKey>& in) {
107   if (!in) {
108     return base::ok(
109         SerializedCookiePartitionKey(kEmptyCookiePartitionKey, true));
110   }
111 
112   if (!in->IsSerializeable()) {
113     return WarnAndCreateUnexpected("CookiePartitionKey is not serializeable");
114   }
115 
116   return base::ok(SerializedCookiePartitionKey(
117       in->site_.GetURL().SchemeIsFile() ? in->site_.SerializeFileSiteWithHost()
118                                         : in->site_.Serialize(),
119       in->IsThirdParty()));
120 }
121 
FromNetworkIsolationKey(const NetworkIsolationKey & network_isolation_key,SiteForCookies site_for_cookies,SchemefulSite request_site)122 std::optional<CookiePartitionKey> CookiePartitionKey::FromNetworkIsolationKey(
123     const NetworkIsolationKey& network_isolation_key,
124     SiteForCookies site_for_cookies,
125     SchemefulSite request_site) {
126   const std::optional<base::UnguessableToken>& nonce =
127       network_isolation_key.GetNonce();
128 
129   // Use frame site for nonced partitions. Since the nonce is unique, this
130   // still creates a unique partition key. The reason we use the frame site is
131   // to align CookiePartitionKey's implementation of nonced partitions with
132   // StorageKey's. See https://crbug.com/1440765.
133   const std::optional<SchemefulSite>& partition_key_site =
134       nonce ? network_isolation_key.GetFrameSiteForCookiePartitionKey(
135                   NetworkIsolationKey::CookiePartitionKeyPassKey())
136             : network_isolation_key.GetTopFrameSite();
137   if (!partition_key_site) {
138     return std::nullopt;
139   }
140 
141   const auto ancestor_chain_bit =
142       (nonce || site_for_cookies.IsNull())
143           ? AncestorChainBit::kCrossSite
144           : BoolToAncestorChainBit(
145                 !site_for_cookies.IsFirstParty(request_site.GetURL()));
146 
147   return CookiePartitionKey(*partition_key_site, nonce, ancestor_chain_bit);
148 }
149 
150 // static
FromStorageKeyComponents(const SchemefulSite & site,AncestorChainBit ancestor_chain_bit,const std::optional<base::UnguessableToken> & nonce)151 std::optional<CookiePartitionKey> CookiePartitionKey::FromStorageKeyComponents(
152     const SchemefulSite& site,
153     AncestorChainBit ancestor_chain_bit,
154     const std::optional<base::UnguessableToken>& nonce) {
155   return CookiePartitionKey::FromWire(site, ancestor_chain_bit, nonce);
156 }
157 
158 // static
159 base::expected<std::optional<CookiePartitionKey>, std::string>
FromStorage(const std::string & top_level_site,bool has_cross_site_ancestor)160 CookiePartitionKey::FromStorage(const std::string& top_level_site,
161                                 bool has_cross_site_ancestor) {
162   if (top_level_site == kEmptyCookiePartitionKey) {
163     return base::ok(std::nullopt);
164   }
165 
166   base::expected<CookiePartitionKey, std::string> key = DeserializeInternal(
167       top_level_site, BoolToAncestorChainBit(has_cross_site_ancestor));
168   if (!key.has_value()) {
169     DLOG(WARNING) << key.error();
170   }
171 
172   return key;
173 }
174 
175 // static
176 base::expected<CookiePartitionKey, std::string>
FromUntrustedInput(const std::string & top_level_site,bool has_cross_site_ancestor)177 CookiePartitionKey::FromUntrustedInput(const std::string& top_level_site,
178                                        bool has_cross_site_ancestor) {
179   if (top_level_site.empty()) {
180     return WarnAndCreateUnexpected("top_level_site is unexpectedly empty");
181   }
182 
183   base::expected<CookiePartitionKey, std::string> key = DeserializeInternal(
184       top_level_site, BoolToAncestorChainBit(has_cross_site_ancestor));
185   if (!key.has_value()) {
186     return WarnAndCreateUnexpected(key.error());
187   }
188   return key;
189 }
190 
191 base::expected<CookiePartitionKey, std::string>
DeserializeInternal(const std::string & top_level_site,CookiePartitionKey::AncestorChainBit has_cross_site_ancestor)192 CookiePartitionKey::DeserializeInternal(
193     const std::string& top_level_site,
194     CookiePartitionKey::AncestorChainBit has_cross_site_ancestor) {
195   auto schemeful_site = SchemefulSite::Deserialize(top_level_site);
196   if (schemeful_site.opaque()) {
197     return WarnAndCreateUnexpected(
198         "Cannot deserialize opaque origin to CookiePartitionKey");
199   }
200   return base::ok(CookiePartitionKey(schemeful_site, std::nullopt,
201                                      has_cross_site_ancestor));
202 }
203 
IsSerializeable() const204 bool CookiePartitionKey::IsSerializeable() const {
205   // We should not try to serialize a partition key created by a renderer.
206   DCHECK(!from_script_);
207   return !site_.opaque() && !nonce_.has_value();
208 }
209 
MaybeAncestorChainBit() const210 CookiePartitionKey::AncestorChainBit CookiePartitionKey::MaybeAncestorChainBit()
211     const {
212   return ancestor_chain_enabled_ ? ancestor_chain_bit_
213                                  : AncestorChainBit::kCrossSite;
214 }
215 
operator <<(std::ostream & os,const CookiePartitionKey & cpk)216 std::ostream& operator<<(std::ostream& os, const CookiePartitionKey& cpk) {
217   os << cpk.site();
218   if (cpk.nonce().has_value()) {
219     os << ",nonced";
220   }
221   os << (cpk.IsThirdParty() ? ",cross_site" : ",same_site");
222   return os;
223 }
224 
225 }  // namespace net
226