blob: 356cd5a8b963e5b02c19f13b9e71492129dc8905 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2018 The Chromium Authors
Tsuyoshi Horocdbb4902018-04-12 06:09:142// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/web_package/signed_exchange_utils.h"
6
Md Hasibul Hasana963a9342024-04-03 10:15:147#include <string_view>
8
Kunihiko Sakamoto0cc93712019-01-11 06:26:539#include "base/command_line.h"
Tsuyoshi Horo46f5fff2018-05-10 12:33:3510#include "base/feature_list.h"
Kunihiko Sakamoto6405d0af2021-11-18 00:46:3711#include "base/metrics/histogram_functions.h"
Kunihiko Sakamotob5c94d902018-09-04 04:09:0212#include "base/strings/string_util.h"
Hans Wennborg5ffd1392019-10-16 11:00:0213#include "base/strings/stringprintf.h"
Tsuyoshi Horo46f5fff2018-05-10 12:33:3514#include "base/time/time.h"
Tsuyoshi Horocdbb4902018-04-12 06:09:1415#include "base/trace_event/trace_event.h"
Tsuyoshi Horo154dafc2018-11-06 09:32:2816#include "content/browser/loader/download_utils_impl.h"
Tsuyoshi Horo4801e762018-04-25 07:36:5717#include "content/browser/web_package/signed_exchange_devtools_proxy.h"
Tsuyoshi Horob40c7c32018-05-31 07:32:4518#include "content/browser/web_package/signed_exchange_error.h"
Kunihiko Sakamotoe6aa22e2018-06-15 03:26:5519#include "content/browser/web_package/signed_exchange_request_handler.h"
Arthur Sonzognibdeca8e2023-09-11 08:32:1220#include "content/common/features.h"
Kunihiko Sakamotof586da62019-03-28 03:03:0421#include "content/public/browser/content_browser_client.h"
22#include "content/public/common/content_client.h"
Kunihiko Sakamoto0cc93712019-01-11 06:26:5323#include "content/public/common/content_switches.h"
Kunihiko Sakamotob5c94d902018-09-04 04:09:0224#include "net/http/http_util.h"
Matt Menke930e44d2020-07-16 02:26:4425#include "net/url_request/redirect_info.h"
Tsuyoshi Horo4f5ce9012019-02-27 01:04:4526#include "services/network/public/cpp/features.h"
Sigurd Schneidere3d43c6b2021-07-27 13:35:0127#include "services/network/public/cpp/resource_request.h"
Lucas Furukawa Gadanid661c0d2019-12-02 19:58:1628#include "services/network/public/mojom/url_response_head.mojom.h"
Tsuyoshi Horocdbb4902018-04-12 06:09:1429
30namespace content {
31namespace signed_exchange_utils {
32
Tsuyoshi Horoaf9059532019-08-29 15:27:0233namespace {
Kunihiko Sakamoto6405d0af2021-11-18 00:46:3734constexpr char kLoadResultHistogram[] = "SignedExchange.LoadResult2";
Arthur Sonzognic686e8f2024-01-11 08:36:3735std::optional<base::Time> g_verification_time_for_testing;
Tsuyoshi Horoaf9059532019-08-29 15:27:0236} // namespace
37
Kunihiko Sakamoto6405d0af2021-11-18 00:46:3738void RecordLoadResultHistogram(SignedExchangeLoadResult result) {
39 base::UmaHistogramEnumeration(kLoadResultHistogram, result);
40}
41
Tsuyoshi Horo6361cb02018-06-04 04:36:0242void ReportErrorAndTraceEvent(
Tsuyoshi Horob40c7c32018-05-31 07:32:4543 SignedExchangeDevToolsProxy* devtools_proxy,
Tsuyoshi Horob40c7c32018-05-31 07:32:4544 const std::string& error_message,
Arthur Sonzognic686e8f2024-01-11 08:36:3745 std::optional<SignedExchangeError::FieldIndexPair> error_field) {
Tsuyoshi Horo6361cb02018-06-04 04:36:0246 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("loading"),
47 "SignedExchangeError", TRACE_EVENT_SCOPE_THREAD, "error",
48 error_message);
Tsuyoshi Horo4801e762018-04-25 07:36:5749 if (devtools_proxy)
Tsuyoshi Horob40c7c32018-05-31 07:32:4550 devtools_proxy->ReportError(error_message, std::move(error_field));
Tsuyoshi Horocdbb4902018-04-12 06:09:1451}
52
Clark DuVallab63d142019-07-23 04:24:3653bool IsSignedExchangeHandlingEnabled(BrowserContext* context) {
Andrew Williams38351472024-07-24 16:47:3054 return GetContentClient()->browser()->AllowSignedExchange(context);
Kunihiko Sakamoto0cc93712019-01-11 06:26:5355}
56
Tsuyoshi Horo4f5ce9012019-02-27 01:04:4557bool IsSignedExchangeReportingForDistributorsEnabled() {
Dave Tapuskab325d752024-04-23 01:34:1458 return base::FeatureList::IsEnabled(network::features::kReporting);
Tsuyoshi Horo4f5ce9012019-02-27 01:04:4559}
60
Tsuyoshi Horo46f5fff2018-05-10 12:33:3561bool ShouldHandleAsSignedHTTPExchange(
62 const GURL& request_url,
Lucas Furukawa Gadanid661c0d2019-12-02 19:58:1663 const network::mojom::URLResponseHead& head) {
Tsuyoshi Horo46f5fff2018-05-10 12:33:3564 // Currently we don't support the signed exchange which is returned from a
65 // service worker.
Alison Gale770f3fc2024-04-27 00:39:5866 // TODO(crbug.com/40558902): Decide whether we should support it or not.
Tsuyoshi Horo46f5fff2018-05-10 12:33:3567 if (head.was_fetched_via_service_worker)
68 return false;
Kunihiko Sakamotoe6aa22e2018-06-15 03:26:5569 if (!SignedExchangeRequestHandler::IsSupportedMimeType(head.mime_type))
Tsuyoshi Horo46f5fff2018-05-10 12:33:3570 return false;
Tsuyoshi Horo73af1622019-02-28 05:34:0471 // Do not handle responses without HttpResponseHeaders.
72 // (Example: data:application/signed-exchange,)
73 if (!head.headers.get())
74 return false;
Josh Simmons05aa52f2023-08-30 22:45:1875 if (download_utils::MustDownload(/*browser_context=*/nullptr, request_url,
76 head.headers.get(), head.mime_type)) {
Tsuyoshi Horo154dafc2018-11-06 09:32:2877 return false;
78 }
Kunihiko Sakamotof586da62019-03-28 03:03:0479 return true;
Tsuyoshi Horo46f5fff2018-05-10 12:33:3580}
81
Arthur Sonzognic686e8f2024-01-11 08:36:3782std::optional<SignedExchangeVersion> GetSignedExchangeVersion(
Matt Menke16ba4572024-10-04 04:24:1883 std::string_view content_type) {
Kunihiko Sakamotob5c94d902018-09-04 04:09:0284 // https://wicg.github.io/webpackage/loading.html#signed-exchange-version
85 // Step 1. Let mimeType be the supplied MIME type of response. [spec text]
86 // |content_type| is the supplied MIME type.
87 // Step 2. If mimeType is undefined, return undefined. [spec text]
88 // Step 3. If mimeType's essence is not "application/signed-exchange", return
89 // undefined. [spec text]
90 const std::string::size_type semicolon = content_type.find(';');
91 const std::string essence = base::ToLowerASCII(base::TrimWhitespaceASCII(
92 content_type.substr(0, semicolon), base::TRIM_ALL));
93 if (essence != "application/signed-exchange")
Arthur Sonzognic686e8f2024-01-11 08:36:3794 return std::nullopt;
Kunihiko Sakamotob5c94d902018-09-04 04:09:0295
96 // Step 4.Let params be mimeType's parameters. [spec text]
97 std::map<std::string, std::string> params;
Md Hasibul Hasana963a9342024-04-03 10:15:1498 if (semicolon != std::string_view::npos) {
Kunihiko Sakamotob5c94d902018-09-04 04:09:0299 net::HttpUtil::NameValuePairsIterator parser(
Matt Menke16ba4572024-10-04 04:24:18100 content_type.substr(semicolon + 1), ';');
Kunihiko Sakamotob5c94d902018-09-04 04:09:02101 while (parser.GetNext()) {
Matt Menke300c55162024-10-02 15:09:47102 params[base::ToLowerASCII(parser.name())] = parser.value();
Kunihiko Sakamotob5c94d902018-09-04 04:09:02103 }
104 if (!parser.valid())
Arthur Sonzognic686e8f2024-01-11 08:36:37105 return std::nullopt;
Kunihiko Sakamotob5c94d902018-09-04 04:09:02106 }
107 // Step 5. If params["v"] exists, return it. Otherwise, return undefined.
108 // [spec text]
109 auto iter = params.find("v");
110 if (iter != params.end()) {
Kouhei Ueno9007cf22019-01-09 08:23:24111 if (iter->second == "b3")
Arthur Sonzognic686e8f2024-01-11 08:36:37112 return std::make_optional(SignedExchangeVersion::kB3);
113 return std::make_optional(SignedExchangeVersion::kUnknown);
Kunihiko Sakamotob5c94d902018-09-04 04:09:02114 }
Arthur Sonzognic686e8f2024-01-11 08:36:37115 return std::nullopt;
Kunihiko Sakamotob5c94d902018-09-04 04:09:02116}
117
Tsuyoshi Horo06eb28f2019-02-21 13:52:24118SignedExchangeLoadResult GetLoadResultFromSignatureVerifierResult(
119 SignedExchangeSignatureVerifier::Result verify_result) {
120 switch (verify_result) {
121 case SignedExchangeSignatureVerifier::Result::kSuccess:
122 return SignedExchangeLoadResult::kSuccess;
123 case SignedExchangeSignatureVerifier::Result::kErrCertificateSHA256Mismatch:
124 // "Handling the certificate reference
125 // ...
126 // - If the SHA-256 hash of chain’s leaf's certificate is not equal to
127 // certSha256, return "signature_verification_error"." [spec text]
128 return SignedExchangeLoadResult::kSignatureVerificationError;
129 case SignedExchangeSignatureVerifier::Result::
130 kErrSignatureVerificationFailed:
131 // "Validating a signature
132 // ...
133 // - If parsedSignature’s signature is not a valid signature of message
134 // by publicKey using the ecdsa_secp256r1_sha256 algorithm, return
135 // invalid." [spec text]
136 //
137 // "Parsing signed exchanges
138 // - ...
139 // - If parsedSignature is not valid for headerBytes and
140 // requestUrlBytes, and signed exchange version version, return
141 // "signature_verification_error"." [spec text]
142 return SignedExchangeLoadResult::kSignatureVerificationError;
Tsuyoshi Horo06eb28f2019-02-21 13:52:24143 case SignedExchangeSignatureVerifier::Result::kErrUnsupportedCertType:
144 // "Validating a signature
145 // ...
146 // - If parsedSignature’s signature is not a valid signature of message
147 // by publicKey using the ecdsa_secp256r1_sha256 algorithm, return
148 // invalid." [spec text]
149 //
150 // "Parsing signed exchanges
151 // - ...
152 // - If parsedSignature is not valid for headerBytes and
153 // requestUrlBytes, and signed exchange version version, return
154 // "signature_verification_error"." [spec text]
155 return SignedExchangeLoadResult::kSignatureVerificationError;
156 case SignedExchangeSignatureVerifier::Result::kErrValidityPeriodTooLong:
157 // "Cross-origin trust
158 // ...
159 // - If signature’s expiration time is more than 604800 seconds (7 days)
160 // after signature’s date, return "untrusted"." [spec text]
161 //
162 // "Parsing signed exchanges
163 // - ...
164 // - If parsedSignature does not establish cross-origin trust for
165 // parsedExchange, return "cert_verification_error"." [spec text]
166 return SignedExchangeLoadResult::kCertVerificationError;
167 case SignedExchangeSignatureVerifier::Result::kErrFutureDate:
168 case SignedExchangeSignatureVerifier::Result::kErrExpired:
169 // "Validating a signature
170 // ...
171 // - If the UA’s estimate of the current time is more than clockSkew
172 // before signature’s date, return "untrusted".
173 // - If the UA’s estimate of the current time is after signature’s
174 // expiration time, return "untrusted"." [spec text]
175 //
176 // "Parsing signed exchanges
177 // - ...
178 // - If parsedSignature is not valid for headerBytes and
179 // requestUrlBytes, and signed exchange version version, return
180 // "signature_verification_error"." [spec text]
181 return SignedExchangeLoadResult::kSignatureVerificationError;
182
183 // Deprecated error results.
184 case SignedExchangeSignatureVerifier::Result::kErrNoCertificate_deprecated:
185 case SignedExchangeSignatureVerifier::Result::
186 kErrNoCertificateSHA256_deprecated:
187 case SignedExchangeSignatureVerifier::Result::
188 kErrInvalidSignatureFormat_deprecated:
189 case SignedExchangeSignatureVerifier::Result::
Kunihiko Sakamoto44cd9a82019-02-26 05:47:06190 kErrInvalidSignatureIntegrity_deprecated:
191 case SignedExchangeSignatureVerifier::Result::
Tsuyoshi Horo06eb28f2019-02-21 13:52:24192 kErrInvalidTimestamp_deprecated:
Peter Boströmfc7ddc182024-10-31 19:37:21193 NOTREACHED();
Tsuyoshi Horo06eb28f2019-02-21 13:52:24194 }
195
Peter Boströmfc7ddc182024-10-31 19:37:21196 NOTREACHED();
Tsuyoshi Horo06eb28f2019-02-21 13:52:24197}
198
Tsuyoshi Horod5eb7612019-05-09 08:59:46199net::RedirectInfo CreateRedirectInfo(
200 const GURL& new_url,
201 const network::ResourceRequest& outer_request,
Lucas Furukawa Gadani62fcfa92019-12-05 15:38:41202 const network::mojom::URLResponseHead& outer_response,
Tsuyoshi Horod5eb7612019-05-09 08:59:46203 bool is_fallback_redirect) {
204 // https://wicg.github.io/webpackage/loading.html#mp-http-fetch
205 // Step 3. Set actualResponse's status to 303. [spec text]
206 return net::RedirectInfo::ComputeRedirectInfo(
Dominic Farolino28e21212019-12-03 17:38:07207 "GET", outer_request.url, outer_request.site_for_cookies,
Tsuyoshi Horod5eb7612019-05-09 08:59:46208 outer_request.update_first_party_url_on_redirect
Matt Menke930e44d2020-07-16 02:26:44209 ? net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT
210 : net::RedirectInfo::FirstPartyURLPolicy::NEVER_CHANGE_URL,
Kelvin Jiangbadc7042025-06-27 01:34:27211 outer_request.referrer_policy, outer_request.referrer.spec(),
212 outer_request.request_initiator, 303, new_url,
Tsuyoshi Horod5eb7612019-05-09 08:59:46213 net::RedirectUtil::GetReferrerPolicyHeader(outer_response.headers.get()),
214 false /* insecure_scheme_was_upgraded */, true /* copy_fragment */,
215 is_fallback_redirect);
216}
217
Lucas Furukawa Gadani62fcfa92019-12-05 15:38:41218network::mojom::URLResponseHeadPtr CreateRedirectResponseHead(
219 const network::mojom::URLResponseHead& outer_response,
Tsuyoshi Horod5eb7612019-05-09 08:59:46220 bool is_fallback_redirect) {
Lucas Furukawa Gadani62fcfa92019-12-05 15:38:41221 auto response_head = network::mojom::URLResponseHead::New();
222 response_head->encoded_data_length = 0;
Tsuyoshi Horod5eb7612019-05-09 08:59:46223 std::string buf;
224 std::string link_header;
225 if (!is_fallback_redirect &&
Tsuyoshi Horod5eb7612019-05-09 08:59:46226 outer_response.headers) {
Chris Fredricksonc0314cc2024-10-18 14:48:51227 link_header = outer_response.headers->GetNormalizedHeader("link").value_or(
228 std::string());
Tsuyoshi Horod5eb7612019-05-09 08:59:46229 }
230 if (link_header.empty()) {
231 buf = base::StringPrintf("HTTP/1.1 %d %s\r\n", 303, "See Other");
232 } else {
Tsuyoshi Horod5eb7612019-05-09 08:59:46233 buf = base::StringPrintf(
234 "HTTP/1.1 %d %s\r\n"
235 "link: %s\r\n",
236 303, "See Other", link_header.c_str());
237 }
Lucas Furukawa Gadani62fcfa92019-12-05 15:38:41238 response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
Tsuyoshi Horod5eb7612019-05-09 08:59:46239 net::HttpUtil::AssembleRawHeaders(buf));
Lucas Furukawa Gadani62fcfa92019-12-05 15:38:41240 response_head->encoded_data_length = 0;
241 response_head->request_start = outer_response.request_start;
242 response_head->response_start = outer_response.response_start;
243 response_head->request_time = outer_response.request_time;
244 response_head->response_time = outer_response.response_time;
245 response_head->load_timing = outer_response.load_timing;
Tsuyoshi Horod5eb7612019-05-09 08:59:46246 return response_head;
247}
248
John Abd-El-Malekd96edf32019-07-29 22:04:52249int MakeRequestID() {
250 // Request ID for browser initiated requests. request_ids generated by
251 // child processes are counted up from 0, while browser created requests
252 // start at -2 and go down from there. (We need to start at -2 because -1 is
253 // used as a special value all over the resource_dispatcher_host for
254 // uninitialized variables.) This way, we no longer have the unlikely (but
255 // observed in the real world!) event where we have two requests with the same
256 // request_id_.
Avi Drissmanded77172021-07-02 18:23:00257 static std::atomic_int request_id(-1);
John Abd-El-Malekd96edf32019-07-29 22:04:52258
Avi Drissmanded77172021-07-02 18:23:00259 return --request_id;
John Abd-El-Malekd96edf32019-07-29 22:04:52260}
261
Tsuyoshi Horoaf9059532019-08-29 15:27:02262base::Time GetVerificationTime() {
263 if (g_verification_time_for_testing)
264 return *g_verification_time_for_testing;
265 return base::Time::Now();
266}
267
268void SetVerificationTimeForTesting(
Arthur Sonzognic686e8f2024-01-11 08:36:37269 std::optional<base::Time> verification_time_for_testing) {
Tsuyoshi Horoaf9059532019-08-29 15:27:02270 g_verification_time_for_testing = verification_time_for_testing;
271}
272
Kunihiko Sakamoto6405d0af2021-11-18 00:46:37273bool IsCookielessOnlyExchange(const net::HttpResponseHeaders& inner_headers) {
Matt Menke16ba4572024-10-04 04:24:18274 std::optional<std::string_view> value;
Kunihiko Sakamoto6405d0af2021-11-18 00:46:37275 size_t iter = 0;
Matt Menke16ba4572024-10-04 04:24:18276 while ((value = inner_headers.EnumerateHeader(&iter, "Vary"))) {
277 if (base::EqualsCaseInsensitiveASCII(*value, "cookie")) {
Kunihiko Sakamoto6405d0af2021-11-18 00:46:37278 return true;
Matt Menke16ba4572024-10-04 04:24:18279 }
Kunihiko Sakamoto6405d0af2021-11-18 00:46:37280 }
281 return false;
282}
283
Tsuyoshi Horocdbb4902018-04-12 06:09:14284} // namespace signed_exchange_utils
285} // namespace content