-
Notifications
You must be signed in to change notification settings - Fork 873
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Brave Services Key V2 (uplift to 1.62.x) (#21688)
Add support for Brave Services Key V2 (#21542) * Add support for Brave Services Key V2 And use for AI chat * Refactor * Move logic to brave_service_keys * Generalize logic such that callers can sign over multiple headers * Separate digest header generation * Rename AI_CHAT_SERVICE_KEY -> SERVICE_KEY_AI_CHAT * Separate SERVICE_KEY_AI_CHAT from signing logic * * Switch from base::span<> to const std::vector<>& * Move unused header to .cc file * Break apart functions and add more unit tests * Update GetAuthorizationHeaders * Pass the URL, HTTP method, full list of headers, and a list of headers to actually be signed to GetAuthorizationHeaders * Instead of using std::vector<std::pair<std::string, std::string>> for the list of headers, instead use base::flat_map<std::string, std::string> since that matches the headers passed to the APIRequestHelper * Enforce header ordering specified by headers_to_sign * Generate (request-target) header if supplied, and add test from spec * Pass url and method to CreateSignatureString This way, (request-target) can be generated inside there and thus be unit tested. Adjust unit tests. * Add VLOG(1) when header to sign does not exist Also DCHECK(false) for good measure. * Add SERVICE_KEY_AI_CHAT and KEY_ID to config.js This way they can be sourced from .env. * Update tests * Link to specific section test vectors are from * Remove the "(created)" header from headers_to_sign (it's not included in the test vector) * Use //crypto instad of //crypto:crypto in components/brave_service_keys/BUILD.gn * Use constexpr for http method constant * Don't use a reference to the digest header * Use NOTREACHED_NORETURN() instead of DCHECK and VLOG(1) * Use CHECK for url in GetAuthorizationHeader Brave Server URLs should always be defined * Uncomment base/flat_map.h include in unittest * Add comment explaining KEY_ID * Add comments explaining functions in service_key_utils * Add is_official_build check for service_key_ai_chat * Update header constants * Use existing constants for kDigest and kAuthorization * Change kRequestTarget to kRequestTargetHeader * Rename service_key_utils.* -> brave_service_key_utils.* * nit: use base::StrCat and .append() * Make headers a const& in CreateSignatureString * Rename KEY_ID -> BRAVE_SERVICES_KEY_ID * Fix formatting of string * Rename SERVICE_KEY_AI_CHAT-> SERVICE_KEY_AICHAT * Apply Jenkinsfile patch * Revert "Apply Jenkinsfile patch" This reverts commit 513bfda.
- Loading branch information
Showing
9 changed files
with
369 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Copyright (c) 2024 The Brave Authors. All rights reserved. | ||
# This Source Code Form is subject to the terms of the Mozilla Public | ||
# License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||
# You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
import("//brave/build/config.gni") | ||
import("//build/buildflag_header.gni") | ||
import("//testing/test.gni") | ||
|
||
declare_args() { | ||
# BRAVE_SERVICES_KEY_ID = TARGET_OS + '-' + CV_MAJOR + '-' + RELEASE_CHANNEL | ||
# It is used (in combination with a secret seed) to generate service keys | ||
# for each service, OS, chrominum version, release channel | ||
# combination. | ||
brave_services_key_id = "" | ||
} | ||
|
||
if (is_official_build) { | ||
assert(brave_services_key_id != "") | ||
} | ||
|
||
buildflag_header("buildflags") { | ||
header = "buildflags.h" | ||
flags = [ "BRAVE_SERVICES_KEY_ID=\"$brave_services_key_id\"" ] | ||
} | ||
|
||
static_library("brave_service_keys") { | ||
sources = [ | ||
"brave_service_key_utils.cc", | ||
"brave_service_key_utils.h", | ||
] | ||
|
||
deps = [ | ||
":buildflags", | ||
"//base", | ||
"//crypto", | ||
"//net", | ||
"//url:url", | ||
] | ||
} | ||
source_set("unit_tests") { | ||
testonly = true | ||
sources = [ "brave_service_key_utils_unittest.cc" ] | ||
|
||
deps = [ | ||
":brave_service_keys", | ||
":buildflags", | ||
"//base", | ||
"//testing/gtest", | ||
] | ||
} |
110 changes: 110 additions & 0 deletions
110
components/brave_service_keys/brave_service_key_utils.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// Copyright (c) 2024 The Brave Authors. All rights reserved. | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||
// You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
#include "brave/components/brave_service_keys/brave_service_key_utils.h" | ||
|
||
#include <vector> | ||
|
||
#include "base/base64.h" | ||
#include "base/notreached.h" | ||
#include "base/strings/strcat.h" | ||
#include "base/strings/string_util.h" | ||
#include "brave/components/brave_service_keys/buildflags.h" | ||
#include "crypto/hmac.h" | ||
#include "crypto/sha2.h" | ||
#include "net/http/http_auth_scheme.h" | ||
#include "net/http/http_request_headers.h" | ||
|
||
namespace brave_service_keys { | ||
|
||
namespace { | ||
|
||
constexpr char kRequestTargetHeader[] = "(request-target)"; | ||
|
||
} // namespace | ||
|
||
std::pair<std::string, std::string> GetDigestHeader( | ||
const std::string& payload) { | ||
const std::string value = base::StrCat( | ||
{"SHA-256=", base::Base64Encode(crypto::SHA256HashString(payload))}); | ||
return std::make_pair(net::kDigestAuthScheme, value); | ||
} | ||
|
||
std::pair<std::string, std::string> CreateSignatureString( | ||
const base::flat_map<std::string, std::string>& headers, | ||
const GURL& url, | ||
const std::string& method, | ||
const std::vector<std::string>& headers_to_sign) { | ||
std::string header_names; | ||
std::string signature_string; | ||
|
||
for (const auto& header_to_sign : headers_to_sign) { | ||
// Prepend some padding / newlines if this isn't the first | ||
// header to sign | ||
if (!header_names.empty()) { | ||
header_names.append(" "); | ||
signature_string.append("\n"); | ||
} | ||
header_names.append(header_to_sign); | ||
|
||
// Handle the special case header (request-target) by constructing | ||
// the value instead of getting it from headers. | ||
if (header_to_sign == kRequestTargetHeader) { | ||
signature_string.append( | ||
base::StrCat({kRequestTargetHeader, ": ", base::ToLowerASCII(method), | ||
" ", url.PathForRequest()})); | ||
continue; | ||
} | ||
|
||
// For all the headers to sign, we expect their values to be be in the | ||
// headers flat_map and use the value there to add to the signature string. | ||
auto header = headers.find(header_to_sign); | ||
if (header == headers.end()) { | ||
NOTREACHED_NORETURN() | ||
<< "Can't sign over non-existent header " << header_to_sign; | ||
} | ||
signature_string.append( | ||
base::StrCat({header_to_sign, ": ", header->second})); | ||
} | ||
|
||
return std::make_pair(header_names, signature_string); | ||
} | ||
|
||
std::optional<std::pair<std::string, std::string>> GetAuthorizationHeader( | ||
const std::string& service_key, | ||
const base::flat_map<std::string, std::string>& headers, | ||
const GURL& url, | ||
const std::string& method, | ||
const std::vector<std::string>& headers_to_sign) { | ||
CHECK(url.is_valid()); | ||
auto [header_names, signature_string] = | ||
CreateSignatureString(headers, url, method, headers_to_sign); | ||
|
||
// Create the signature using the service_key. | ||
crypto::HMAC hmac(crypto::HMAC::SHA256); | ||
const size_t signature_digest_length = hmac.DigestLength(); | ||
std::vector<uint8_t> signature_digest(signature_digest_length); | ||
const bool success = hmac.Init(service_key) && | ||
hmac.Sign(signature_string, &signature_digest[0], | ||
signature_digest.size()); | ||
if (!success) { | ||
return std::nullopt; | ||
} | ||
|
||
// Create the authorization header. | ||
std::string signature_digest_base64; | ||
base::Base64Encode( | ||
std::string(signature_digest.begin(), signature_digest.end()), | ||
&signature_digest_base64); | ||
|
||
const std::string value = | ||
base::StrCat({"Signature keyId=\"", BUILDFLAG(BRAVE_SERVICES_KEY_ID), | ||
"\",algorithm=\"hs2019\",headers=\"", header_names, | ||
"\",signature=\"", signature_digest_base64, "\""}); | ||
|
||
return std::make_pair(net::HttpRequestHeaders::kAuthorization, value); | ||
} | ||
|
||
} // namespace brave_service_keys |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// Copyright (c) 2024 The Brave Authors. All rights reserved. | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||
// You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
#ifndef BRAVE_COMPONENTS_BRAVE_SERVICE_KEYS_BRAVE_SERVICE_KEY_UTILS_H_ | ||
#define BRAVE_COMPONENTS_BRAVE_SERVICE_KEYS_BRAVE_SERVICE_KEY_UTILS_H_ | ||
|
||
#include <optional> | ||
#include <string> | ||
#include <utility> | ||
#include <vector> | ||
|
||
#include "base/containers/flat_map.h" | ||
#include "url/gurl.h" | ||
|
||
namespace brave_service_keys { | ||
|
||
// Calculates the SHA-256 hash of the supplied payload and returns a pair | ||
// comprising of the digest header field, and header value in the format | ||
// "SHA-256=<base64_encoded_hash>". | ||
std::pair<std::string, std::string> GetDigestHeader(const std::string& payload); | ||
|
||
// Generates the the string to be signed over and included in the authorization | ||
// header. See | ||
// https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-08#section-2.3:w | ||
std::pair<std::string, std::string> CreateSignatureString( | ||
const base::flat_map<std::string, std::string>& headers, | ||
const GURL& url, | ||
const std::string& method, | ||
const std::vector<std::string>& headers_to_sign); | ||
|
||
// Generates an authorization header field and value pair using the provided | ||
// service key to sign over specified headers. | ||
std::optional<std::pair<std::string, std::string>> GetAuthorizationHeader( | ||
const std::string& service_key, | ||
const base::flat_map<std::string, std::string>& headers, | ||
const GURL& url, | ||
const std::string& method, | ||
const std::vector<std::string>& headers_to_sign); | ||
|
||
} // namespace brave_service_keys | ||
|
||
#endif // BRAVE_COMPONENTS_BRAVE_SERVICE_KEYS_BRAVE_SERVICE_KEY_UTILS_H_ |
Oops, something went wrong.