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/session_challenge_param.h" 6 7 #include "base/ranges/algorithm.h" 8 #include "net/http/http_response_headers.h" 9 #include "url/gurl.h" 10 11 namespace { 12 // Sec-Session-Challenge header defined in 13 // https://github.com/WICG/dbsc/blob/main/README.md#high-level-overview 14 constexpr char kSessionChallengeHeaderName[] = "Sec-Session-Challenge"; 15 constexpr char kSessionIdKey[] = "id"; 16 } // namespace 17 18 namespace net::device_bound_sessions { 19 20 SessionChallengeParam::SessionChallengeParam( 21 SessionChallengeParam&& other) noexcept = default; 22 23 SessionChallengeParam& SessionChallengeParam::operator=( 24 SessionChallengeParam&& other) noexcept = default; 25 26 SessionChallengeParam::~SessionChallengeParam() = default; 27 SessionChallengeParam(std::optional<std::string> session_id,std::string challenge)28SessionChallengeParam::SessionChallengeParam( 29 std::optional<std::string> session_id, 30 std::string challenge) 31 : session_id_(std::move(session_id)), challenge_(std::move(challenge)) {} 32 33 // static ParseItem(const structured_headers::ParameterizedMember & session_challenge)34std::optional<SessionChallengeParam> SessionChallengeParam::ParseItem( 35 const structured_headers::ParameterizedMember& session_challenge) { 36 if (session_challenge.member_is_inner_list || 37 session_challenge.member.empty()) { 38 return std::nullopt; 39 } 40 41 const structured_headers::Item& item = session_challenge.member[0].item; 42 if (!item.is_string()) { 43 return std::nullopt; 44 } 45 46 std::string challenge(item.GetString()); 47 if (challenge.empty()) { 48 return std::nullopt; 49 } 50 51 std::optional<std::string> session_id; 52 if (auto it = base::ranges::find( 53 session_challenge.params, kSessionIdKey, 54 &std::pair<std::string, structured_headers::Item>::first); 55 it != session_challenge.params.end()) { 56 const auto& param = it->second; 57 if (!param.is_string()) { 58 return std::nullopt; 59 } 60 61 auto id = param.GetString(); 62 if (!id.empty()) { 63 session_id = std::move(id); 64 } 65 } 66 67 return SessionChallengeParam(std::move(session_id), std::move(challenge)); 68 } 69 70 // static CreateIfValid(const GURL & request_url,const net::HttpResponseHeaders * headers)71std::vector<SessionChallengeParam> SessionChallengeParam::CreateIfValid( 72 const GURL& request_url, 73 const net::HttpResponseHeaders* headers) { 74 std::vector<SessionChallengeParam> params; 75 if (!request_url.is_valid()) { 76 return params; 77 } 78 79 if (!headers) { 80 return params; 81 } 82 std::optional<std::string> header_value = 83 headers->GetNormalizedHeader(kSessionChallengeHeaderName); 84 if (!header_value) { 85 return params; 86 } 87 88 std::optional<structured_headers::List> list = 89 structured_headers::ParseList(*header_value); 90 91 if (!list) { 92 return params; 93 } 94 95 for (const auto& session_challenge : *list) { 96 std::optional<SessionChallengeParam> param = ParseItem(session_challenge); 97 if (param) { 98 params.push_back(std::move(*param)); 99 } 100 } 101 102 return params; 103 } 104 105 } // namespace net::device_bound_sessions 106