• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 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/device_bound_sessions/registration_fetcher_param.h"
6 
7 #include <vector>
8 
9 #include "base/base64url.h"
10 #include "base/logging.h"
11 #include "base/strings/escape.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "net/base/schemeful_site.h"
15 #include "net/http/structured_headers.h"
16 
17 namespace {
18 // TODO(kristianm): See if these can be used with
19 // services/network/sec_header_helpers.cc
20 constexpr char kRegistrationHeaderName[] = "Sec-Session-Registration";
21 constexpr char kChallengeParamKey[] = "challenge";
22 constexpr char kPathParamKey[] = "path";
23 constexpr char kAuthCodeParamKey[] = "authorization";
24 
25 constexpr char kES256[] = "ES256";
26 constexpr char kRS256[] = "RS256";
27 
AlgoFromString(const std::string_view & algo)28 std::optional<crypto::SignatureVerifier::SignatureAlgorithm> AlgoFromString(
29     const std::string_view& algo) {
30   if (algo == kES256) {
31     return crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256;
32   }
33 
34   if (algo == kRS256) {
35     return crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256;
36   }
37 
38   return std::nullopt;
39 }
40 }  // namespace
41 
42 namespace net::device_bound_sessions {
43 
44 RegistrationFetcherParam::RegistrationFetcherParam(
45     RegistrationFetcherParam&& other) = default;
46 
47 RegistrationFetcherParam& RegistrationFetcherParam::operator=(
48     RegistrationFetcherParam&& other) noexcept = default;
49 
50 RegistrationFetcherParam::~RegistrationFetcherParam() = default;
51 
RegistrationFetcherParam(GURL registration_endpoint,std::vector<crypto::SignatureVerifier::SignatureAlgorithm> supported_algos,std::string challenge,std::optional<std::string> authorization)52 RegistrationFetcherParam::RegistrationFetcherParam(
53     GURL registration_endpoint,
54     std::vector<crypto::SignatureVerifier::SignatureAlgorithm> supported_algos,
55     std::string challenge,
56     std::optional<std::string> authorization)
57     : registration_endpoint_(std::move(registration_endpoint)),
58       supported_algos_(std::move(supported_algos)),
59       challenge_(std::move(challenge)),
60       authorization_(std::move(authorization)) {}
61 
ParseItem(const GURL & request_url,const structured_headers::ParameterizedMember & session_registration)62 std::optional<RegistrationFetcherParam> RegistrationFetcherParam::ParseItem(
63     const GURL& request_url,
64     const structured_headers::ParameterizedMember& session_registration) {
65   std::vector<crypto::SignatureVerifier::SignatureAlgorithm> supported_algos;
66   for (const auto& algo_token : session_registration.member) {
67     if (algo_token.item.is_token()) {
68       std::optional<crypto::SignatureVerifier::SignatureAlgorithm> algo =
69           AlgoFromString(algo_token.item.GetString());
70       if (algo) {
71         supported_algos.push_back(*algo);
72       };
73     }
74   }
75   if (supported_algos.empty()) {
76     return std::nullopt;
77   }
78 
79   GURL registration_endpoint;
80   std::string challenge;
81   std::optional<std::string> authorization;
82   for (const auto& [key, value] : session_registration.params) {
83     // The keys for the parameters are unique and must be lower case.
84     // Quiche (https://quiche.googlesource.com/quiche), used here,
85     // will currently pick the last if there is more than one.
86     if (key == kPathParamKey) {
87       if (!value.is_string()) {
88         continue;
89       }
90       std::string path = value.GetString();
91       // TODO(kristianm): Update this as same site requirements are solidified
92       std::string unescaped = base::UnescapeURLComponent(
93           path,
94           base::UnescapeRule::PATH_SEPARATORS |
95               base::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
96       GURL candidate_endpoint = request_url.Resolve(unescaped);
97       if (candidate_endpoint.is_valid() &&
98           net::SchemefulSite(candidate_endpoint) ==
99               net::SchemefulSite(request_url)) {
100         registration_endpoint = std::move(candidate_endpoint);
101       }
102     } else if (key == kChallengeParamKey && value.is_string()) {
103       challenge = value.GetString();
104     } else if (key == kAuthCodeParamKey && value.is_string()) {
105       authorization = value.GetString();
106     }
107 
108     // Other params are ignored
109   }
110 
111   if (!registration_endpoint.is_valid() || challenge.empty()) {
112     return std::nullopt;
113   }
114 
115   return RegistrationFetcherParam(
116       std::move(registration_endpoint), std::move(supported_algos),
117       std::move(challenge), std::move(authorization));
118 }
119 
CreateIfValid(const GURL & request_url,const net::HttpResponseHeaders * headers)120 std::vector<RegistrationFetcherParam> RegistrationFetcherParam::CreateIfValid(
121     const GURL& request_url,
122     const net::HttpResponseHeaders* headers) {
123   std::vector<RegistrationFetcherParam> params;
124   if (!request_url.is_valid()) {
125     return params;
126   }
127 
128   if (!headers) {
129     return params;
130   }
131   std::optional<std::string> header_value =
132       headers->GetNormalizedHeader(kRegistrationHeaderName);
133   if (!header_value) {
134     return params;
135   }
136 
137   std::optional<structured_headers::List> list =
138       structured_headers::ParseList(*header_value);
139   if (!list || list->empty()) {
140     return params;
141   }
142 
143   for (const auto& item : *list) {
144     if (item.member_is_inner_list) {
145       std::optional<RegistrationFetcherParam> fetcher_param =
146           ParseItem(request_url, item);
147       if (fetcher_param) {
148         params.push_back(std::move(*fetcher_param));
149       }
150     }
151   }
152 
153   return params;
154 }
155 
156 // static
CreateInstanceForTesting(GURL registration_endpoint,std::vector<crypto::SignatureVerifier::SignatureAlgorithm> supported_algos,std::string challenge,std::optional<std::string> authorization)157 RegistrationFetcherParam RegistrationFetcherParam::CreateInstanceForTesting(
158     GURL registration_endpoint,
159     std::vector<crypto::SignatureVerifier::SignatureAlgorithm> supported_algos,
160     std::string challenge,
161     std::optional<std::string> authorization) {
162   return RegistrationFetcherParam(
163       std::move(registration_endpoint), std::move(supported_algos),
164       std::move(challenge), std::move(authorization));
165 }
166 
167 }  // namespace net::device_bound_sessions
168