Commit a68c90ee authored by Kunihiko Sakamoto's avatar Kunihiko Sakamoto Committed by Commit Bot

Get rid of SignedExchangeSignatureVerifier::Input

After this patch:
- SignedExchangeHeader parses the Signature: header
- SignedExchangeSignatureVerifier takes a SignedExchangeHeader instead
  of custom input parameter struct
- SignedExchangeHandler holds SignedExchangeHeader as a member

Bug: 803774
Change-Id: I842f211360c93b11b52a495f6b0c6fa5b664c197
Reviewed-on: https://chromium-review.googlesource.com/954904
Commit-Queue: Kunihiko Sakamoto <ksakamoto@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542424}
parent fdb1d592
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include "content/browser/web_package/signed_exchange_cert_fetcher.h" #include "content/browser/web_package/signed_exchange_cert_fetcher.h"
#include "content/browser/web_package/signed_exchange_consts.h" #include "content/browser/web_package/signed_exchange_consts.h"
#include "content/browser/web_package/signed_exchange_header.h" #include "content/browser/web_package/signed_exchange_header.h"
#include "content/browser/web_package/signed_exchange_header_parser.h"
#include "content/browser/web_package/signed_exchange_signature_verifier.h" #include "content/browser/web_package/signed_exchange_signature_verifier.h"
#include "content/public/common/content_features.h" #include "content/public/common/content_features.h"
#include "content/public/common/shared_url_loader_factory.h" #include "content/public/common/shared_url_loader_factory.h"
...@@ -161,32 +160,23 @@ bool SignedExchangeHandler::ParseHeadersLength() { ...@@ -161,32 +160,23 @@ bool SignedExchangeHandler::ParseHeadersLength() {
bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() { bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() {
DCHECK_EQ(state_, State::kReadingHeaders); DCHECK_EQ(state_, State::kReadingHeaders);
base::Optional<SignedExchangeHeader> header = header_ = SignedExchangeHeader::Parse(base::make_span(
SignedExchangeHeader::Parse(base::make_span( reinterpret_cast<uint8_t*>(header_buf_->data()), headers_length_));
reinterpret_cast<uint8_t*>(header_buf_->data()), headers_length_));
header_read_buf_ = nullptr; header_read_buf_ = nullptr;
header_buf_ = nullptr; header_buf_ = nullptr;
if (!header) { if (!header_) {
DVLOG(1) << "Failed to parse CBOR header"; DVLOG(1) << "Failed to parse CBOR header";
return false; return false;
} }
request_url_ = header->request_url(); // TODO(https://crbug.com/803774): implement the following logic in
request_method_ = header->request_method(); // SignedExchangeHeaderParser.
base::Optional<std::vector<SignedExchangeHeaderParser::Signature>> signatures;
// TODO(https://crbug.com/803774): Rename
// SignedExchangeSignatureVerifier::Input to SignedExchangeSignatureHeader and
// implement the following logic in SignedExchangeHeaderParser.
auto verifier_input =
std::make_unique<SignedExchangeSignatureVerifier::Input>();
std::string fake_header_str("HTTP/1.1 "); std::string fake_header_str("HTTP/1.1 ");
fake_header_str.append(base::NumberToString(header->response_code())); fake_header_str.append(base::NumberToString(header_->response_code()));
fake_header_str.append(" "); fake_header_str.append(" ");
fake_header_str.append(net::GetHttpReasonPhrase(header->response_code())); fake_header_str.append(net::GetHttpReasonPhrase(header_->response_code()));
fake_header_str.append(" \r\n"); fake_header_str.append(" \r\n");
for (const auto& it : header->response_headers()) { for (const auto& it : header_->response_headers()) {
if (!net::HttpUtil::IsValidHeaderName(it.first)) { if (!net::HttpUtil::IsValidHeaderName(it.first)) {
DVLOG(1) << "Invalid header name"; DVLOG(1) << "Invalid header name";
return false; return false;
...@@ -199,9 +189,6 @@ bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() { ...@@ -199,9 +189,6 @@ bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() {
fake_header_str.append(": "); fake_header_str.append(": ");
fake_header_str.append(it.second); fake_header_str.append(it.second);
fake_header_str.append("\r\n"); fake_header_str.append("\r\n");
if (it.first == "signature") {
signatures = SignedExchangeHeaderParser::ParseSignature(it.second);
}
} }
fake_header_str.append("\r\n"); fake_header_str.append("\r\n");
response_head_.headers = base::MakeRefCounted<net::HttpResponseHeaders>( response_head_.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
...@@ -220,17 +207,7 @@ bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() { ...@@ -220,17 +207,7 @@ bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() {
mi_stream_ = std::make_unique<MerkleIntegritySourceStream>( mi_stream_ = std::make_unique<MerkleIntegritySourceStream>(
mi_header_value, std::move(source_)); mi_header_value, std::move(source_));
if (!signatures || signatures->empty()) const GURL cert_url = header_->signature().cert_url;
return false;
verifier_input->method = request_method_;
verifier_input->url = request_url_.spec();
verifier_input->response_code = header->response_code();
verifier_input->signature = (*signatures)[0];
verifier_input->response_headers = std::move(header->response_headers());
// Copy |cert_url| to keep after |verifier_input| is passed to base::BindOnce.
const GURL cert_url = verifier_input->signature.cert_url;
// TODO(https://crbug.com/819467): When we will support ed25519Key, |cert_url| // TODO(https://crbug.com/819467): When we will support ed25519Key, |cert_url|
// may be empty. // may be empty.
DCHECK(cert_url.is_valid()); DCHECK(cert_url.is_valid());
...@@ -243,7 +220,7 @@ bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() { ...@@ -243,7 +220,7 @@ bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() {
std::move(url_loader_factory_), std::move(throttles), cert_url, std::move(url_loader_factory_), std::move(throttles), cert_url,
std::move(request_initiator_), false, std::move(request_initiator_), false,
base::BindOnce(&SignedExchangeHandler::OnCertReceived, base::BindOnce(&SignedExchangeHandler::OnCertReceived,
base::Unretained(this), std::move(verifier_input))); base::Unretained(this)));
state_ = State::kFetchingCertificate; state_ = State::kFetchingCertificate;
return true; return true;
...@@ -258,7 +235,6 @@ void SignedExchangeHandler::RunErrorCallback(net::Error error) { ...@@ -258,7 +235,6 @@ void SignedExchangeHandler::RunErrorCallback(net::Error error) {
} }
void SignedExchangeHandler::OnCertReceived( void SignedExchangeHandler::OnCertReceived(
std::unique_ptr<SignedExchangeSignatureVerifier::Input> verifier_input,
scoped_refptr<net::X509Certificate> cert) { scoped_refptr<net::X509Certificate> cert) {
DCHECK_EQ(state_, State::kFetchingCertificate); DCHECK_EQ(state_, State::kFetchingCertificate);
if (!cert) { if (!cert) {
...@@ -266,8 +242,7 @@ void SignedExchangeHandler::OnCertReceived( ...@@ -266,8 +242,7 @@ void SignedExchangeHandler::OnCertReceived(
RunErrorCallback(net::ERR_FAILED); RunErrorCallback(net::ERR_FAILED);
return; return;
} }
verifier_input->certificate = cert; if (SignedExchangeSignatureVerifier::Verify(*header_, cert) !=
if (SignedExchangeSignatureVerifier::Verify(*verifier_input) !=
SignedExchangeSignatureVerifier::Result::kSuccess) { SignedExchangeSignatureVerifier::Result::kSuccess) {
RunErrorCallback(net::ERR_FAILED); RunErrorCallback(net::ERR_FAILED);
return; return;
...@@ -292,8 +267,9 @@ void SignedExchangeHandler::OnCertReceived( ...@@ -292,8 +267,9 @@ void SignedExchangeHandler::OnCertReceived(
// (nextUpdate - thisUpdate) is less than 7 days. // (nextUpdate - thisUpdate) is less than 7 days.
int result = cert_verifier->Verify( int result = cert_verifier->Verify(
net::CertVerifier::RequestParams( net::CertVerifier::RequestParams(
unverified_cert_, request_url_.host(), config.GetCertVerifyFlags(), unverified_cert_, header_->request_url().host(),
std::string() /* ocsp_response */, net::CertificateList()), config.GetCertVerifyFlags(), std::string() /* ocsp_response */,
net::CertificateList()),
net::SSLConfigService::GetCRLSet().get(), &cert_verify_result_, net::SSLConfigService::GetCRLSet().get(), &cert_verify_result_,
base::BindRepeating(&SignedExchangeHandler::OnCertVerifyComplete, base::BindRepeating(&SignedExchangeHandler::OnCertVerifyComplete,
base::Unretained(this)), base::Unretained(this)),
...@@ -324,8 +300,8 @@ void SignedExchangeHandler::OnCertVerifyComplete(int result) { ...@@ -324,8 +300,8 @@ void SignedExchangeHandler::OnCertVerifyComplete(int result) {
!net::IsCertStatusMinorError(ssl_info.cert_status); !net::IsCertStatusMinorError(ssl_info.cert_status);
// TODO(https://crbug.com/815025): Verify the Certificate Transparency status. // TODO(https://crbug.com/815025): Verify the Certificate Transparency status.
std::move(headers_callback_) std::move(headers_callback_)
.Run(net::OK, request_url_, request_method_, response_head_, .Run(net::OK, header_->request_url(), header_->request_method(),
std::move(mi_stream_), ssl_info); response_head_, std::move(mi_stream_), ssl_info);
state_ = State::kHeadersCallbackCalled; state_ = State::kHeadersCallbackCalled;
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/optional.h" #include "base/optional.h"
#include "content/browser/web_package/signed_exchange_signature_verifier.h" #include "content/browser/web_package/signed_exchange_header.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/common/shared_url_loader_factory.h" #include "content/public/common/shared_url_loader_factory.h"
#include "mojo/public/cpp/system/data_pipe.h" #include "mojo/public/cpp/system/data_pipe.h"
...@@ -92,13 +92,10 @@ class CONTENT_EXPORT SignedExchangeHandler { ...@@ -92,13 +92,10 @@ class CONTENT_EXPORT SignedExchangeHandler {
void RunErrorCallback(net::Error); void RunErrorCallback(net::Error);
void OnCertReceived( void OnCertReceived(
std::unique_ptr<SignedExchangeSignatureVerifier::Input> verifier_input,
scoped_refptr<net::X509Certificate> cert); scoped_refptr<net::X509Certificate> cert);
void OnCertVerifyComplete(int result); void OnCertVerifyComplete(int result);
// Signed exchange contents. // Signed exchange contents.
GURL request_url_;
std::string request_method_;
network::ResourceResponseHead response_head_; network::ResourceResponseHead response_head_;
ExchangeHeadersCallback headers_callback_; ExchangeHeadersCallback headers_callback_;
...@@ -111,6 +108,7 @@ class CONTENT_EXPORT SignedExchangeHandler { ...@@ -111,6 +108,7 @@ class CONTENT_EXPORT SignedExchangeHandler {
scoped_refptr<net::DrainableIOBuffer> header_read_buf_; scoped_refptr<net::DrainableIOBuffer> header_read_buf_;
size_t headers_length_ = 0; size_t headers_length_ = 0;
base::Optional<SignedExchangeHeader> header_;
std::unique_ptr<MerkleIntegritySourceStream> mi_stream_; std::unique_ptr<MerkleIntegritySourceStream> mi_stream_;
// Used to create |cert_fetcher_|. // Used to create |cert_fetcher_|.
......
...@@ -127,6 +127,18 @@ base::Optional<SignedExchangeHeader> SignedExchangeHeader::Parse( ...@@ -127,6 +127,18 @@ base::Optional<SignedExchangeHeader> SignedExchangeHeader::Parse(
!ParseResponseMap(top_level_array[1], &ret)) !ParseResponseMap(top_level_array[1], &ret))
return base::nullopt; return base::nullopt;
auto signature_iter = ret.response_headers_.find("signature");
if (signature_iter == ret.response_headers_.end())
return base::nullopt;
base::Optional<std::vector<SignedExchangeHeaderParser::Signature>>
signatures =
SignedExchangeHeaderParser::ParseSignature(signature_iter->second);
if (!signatures || signatures->empty())
return base::nullopt;
ret.signature_ = (*signatures)[0];
return std::move(ret); return std::move(ret);
} }
...@@ -135,6 +147,8 @@ SignedExchangeHeader::SignedExchangeHeader(const SignedExchangeHeader&) = ...@@ -135,6 +147,8 @@ SignedExchangeHeader::SignedExchangeHeader(const SignedExchangeHeader&) =
default; default;
SignedExchangeHeader::SignedExchangeHeader(SignedExchangeHeader&&) = default; SignedExchangeHeader::SignedExchangeHeader(SignedExchangeHeader&&) = default;
SignedExchangeHeader::~SignedExchangeHeader() = default; SignedExchangeHeader::~SignedExchangeHeader() = default;
SignedExchangeHeader& SignedExchangeHeader::operator=(SignedExchangeHeader&&) =
default;
void SignedExchangeHeader::AddResponseHeader(base::StringPiece name, void SignedExchangeHeader::AddResponseHeader(base::StringPiece name,
base::StringPiece value) { base::StringPiece value) {
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/containers/span.h" #include "base/containers/span.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "content/browser/web_package/signed_exchange_header_parser.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "net/http/http_status_code.h" #include "net/http/http_status_code.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -49,6 +50,7 @@ class CONTENT_EXPORT SignedExchangeHeader { ...@@ -49,6 +50,7 @@ class CONTENT_EXPORT SignedExchangeHeader {
SignedExchangeHeader(); SignedExchangeHeader();
SignedExchangeHeader(const SignedExchangeHeader&); SignedExchangeHeader(const SignedExchangeHeader&);
SignedExchangeHeader(SignedExchangeHeader&&); SignedExchangeHeader(SignedExchangeHeader&&);
SignedExchangeHeader& operator=(SignedExchangeHeader&&);
~SignedExchangeHeader(); ~SignedExchangeHeader();
void AddResponseHeader(base::StringPiece name, base::StringPiece value); void AddResponseHeader(base::StringPiece name, base::StringPiece value);
...@@ -68,12 +70,21 @@ class CONTENT_EXPORT SignedExchangeHeader { ...@@ -68,12 +70,21 @@ class CONTENT_EXPORT SignedExchangeHeader {
return response_headers_; return response_headers_;
} }
const SignedExchangeHeaderParser::Signature& signature() const {
return signature_;
}
void SetSignatureForTesting(
const SignedExchangeHeaderParser::Signature& sig) {
signature_ = sig;
}
private: private:
GURL request_url_; GURL request_url_;
std::string request_method_; std::string request_method_;
net::HttpStatusCode response_code_; net::HttpStatusCode response_code_;
std::map<std::string, std::string> response_headers_; std::map<std::string, std::string> response_headers_;
SignedExchangeHeaderParser::Signature signature_;
}; };
} // namespace content } // namespace content
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "components/cbor/cbor_values.h" #include "components/cbor/cbor_values.h"
#include "components/cbor/cbor_writer.h" #include "components/cbor/cbor_writer.h"
#include "content/browser/web_package/signed_exchange_consts.h" #include "content/browser/web_package/signed_exchange_consts.h"
#include "content/browser/web_package/signed_exchange_header.h"
#include "content/browser/web_package/signed_exchange_header_parser.h" #include "content/browser/web_package/signed_exchange_header_parser.h"
#include "crypto/signature_verifier.h" #include "crypto/signature_verifier.h"
#include "net/cert/asn1_util.h" #include "net/cert/asn1_util.h"
...@@ -35,21 +36,23 @@ constexpr uint8_t kMessageHeader[] = ...@@ -35,21 +36,23 @@ constexpr uint8_t kMessageHeader[] =
"HTTP Exchange"; "HTTP Exchange";
base::Optional<cbor::CBORValue> GenerateCanonicalRequestCBOR( base::Optional<cbor::CBORValue> GenerateCanonicalRequestCBOR(
const SignedExchangeSignatureVerifier::Input& input) { const SignedExchangeHeader& header) {
cbor::CBORValue::MapValue map; cbor::CBORValue::MapValue map;
map.insert_or_assign( map.insert_or_assign(
cbor::CBORValue(kMethodKey, cbor::CBORValue::Type::BYTE_STRING), cbor::CBORValue(kMethodKey, cbor::CBORValue::Type::BYTE_STRING),
cbor::CBORValue(input.method, cbor::CBORValue::Type::BYTE_STRING)); cbor::CBORValue(header.request_method(),
cbor::CBORValue::Type::BYTE_STRING));
map.insert_or_assign( map.insert_or_assign(
cbor::CBORValue(kUrlKey, cbor::CBORValue::Type::BYTE_STRING), cbor::CBORValue(kUrlKey, cbor::CBORValue::Type::BYTE_STRING),
cbor::CBORValue(input.url, cbor::CBORValue::Type::BYTE_STRING)); cbor::CBORValue(header.request_url().spec(),
cbor::CBORValue::Type::BYTE_STRING));
return cbor::CBORValue(map); return cbor::CBORValue(map);
} }
base::Optional<cbor::CBORValue> GenerateCanonicalResponseCBOR( base::Optional<cbor::CBORValue> GenerateCanonicalResponseCBOR(
const SignedExchangeSignatureVerifier::Input& input) { const SignedExchangeHeader& header) {
const auto& headers = input.response_headers; const auto& headers = header.response_headers();
auto it = headers.find(kSignedHeadersName); auto it = headers.find(kSignedHeadersName);
if (it == headers.end()) { if (it == headers.end()) {
...@@ -64,7 +67,7 @@ base::Optional<cbor::CBORValue> GenerateCanonicalResponseCBOR( ...@@ -64,7 +67,7 @@ base::Optional<cbor::CBORValue> GenerateCanonicalResponseCBOR(
return base::nullopt; return base::nullopt;
cbor::CBORValue::MapValue map; cbor::CBORValue::MapValue map;
std::string response_code_str = base::NumberToString(input.response_code); std::string response_code_str = base::NumberToString(header.response_code());
map.insert_or_assign( map.insert_or_assign(
cbor::CBORValue(kStatusKey, cbor::CBORValue::Type::BYTE_STRING), cbor::CBORValue(kStatusKey, cbor::CBORValue::Type::BYTE_STRING),
cbor::CBORValue(response_code_str, cbor::CBORValue::Type::BYTE_STRING)); cbor::CBORValue(response_code_str, cbor::CBORValue::Type::BYTE_STRING));
...@@ -85,14 +88,14 @@ base::Optional<cbor::CBORValue> GenerateCanonicalResponseCBOR( ...@@ -85,14 +88,14 @@ base::Optional<cbor::CBORValue> GenerateCanonicalResponseCBOR(
return cbor::CBORValue(map); return cbor::CBORValue(map);
} }
// Generate CBORValue from |input| as specified in: // Generate CBORValue from |header| as specified in:
// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#rfc.section.3.4 // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#rfc.section.3.4
base::Optional<cbor::CBORValue> GenerateCanonicalExchangeHeadersCBOR( base::Optional<cbor::CBORValue> GenerateCanonicalExchangeHeadersCBOR(
const SignedExchangeSignatureVerifier::Input& input) { const SignedExchangeHeader& header) {
auto req_val = GenerateCanonicalRequestCBOR(input); auto req_val = GenerateCanonicalRequestCBOR(header);
if (!req_val) if (!req_val)
return base::nullopt; return base::nullopt;
auto res_val = GenerateCanonicalResponseCBOR(input); auto res_val = GenerateCanonicalResponseCBOR(header);
if (!res_val) if (!res_val)
return base::nullopt; return base::nullopt;
...@@ -106,8 +109,8 @@ base::Optional<cbor::CBORValue> GenerateCanonicalExchangeHeadersCBOR( ...@@ -106,8 +109,8 @@ base::Optional<cbor::CBORValue> GenerateCanonicalExchangeHeadersCBOR(
// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#rfc.section.3.6 // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#rfc.section.3.6
// Step 11.4. // Step 11.4.
base::Optional<cbor::CBORValue> GenerateSignedMessageCBOR( base::Optional<cbor::CBORValue> GenerateSignedMessageCBOR(
const SignedExchangeSignatureVerifier::Input& input) { const SignedExchangeHeader& header) {
auto headers_val = GenerateCanonicalExchangeHeadersCBOR(input); auto headers_val = GenerateCanonicalExchangeHeadersCBOR(header);
if (!headers_val) if (!headers_val)
return base::nullopt; return base::nullopt;
...@@ -116,35 +119,35 @@ base::Optional<cbor::CBORValue> GenerateSignedMessageCBOR( ...@@ -116,35 +119,35 @@ base::Optional<cbor::CBORValue> GenerateSignedMessageCBOR(
cbor::CBORValue::MapValue map; cbor::CBORValue::MapValue map;
// 11.4.1. "If certSha256 is set: The text string "certSha256" to the byte // 11.4.1. "If certSha256 is set: The text string "certSha256" to the byte
// string value of certSha256." [spec text] // string value of certSha256." [spec text]
if (input.signature.cert_sha256.has_value()) { if (header.signature().cert_sha256.has_value()) {
map.insert_or_assign( map.insert_or_assign(
cbor::CBORValue(kCertSha256Key), cbor::CBORValue(kCertSha256Key),
cbor::CBORValue( cbor::CBORValue(
base::StringPiece(reinterpret_cast<const char*>( base::StringPiece(reinterpret_cast<const char*>(
input.signature.cert_sha256->data), header.signature().cert_sha256->data),
sizeof(input.signature.cert_sha256->data)), sizeof(header.signature().cert_sha256->data)),
cbor::CBORValue::Type::BYTE_STRING)); cbor::CBORValue::Type::BYTE_STRING));
} }
// 11.4.2. "The text string "validityUrl" to the byte string value of // 11.4.2. "The text string "validityUrl" to the byte string value of
// validityUrl." [spec text] // validityUrl." [spec text]
map.insert_or_assign(cbor::CBORValue(kValidityUrlKey), map.insert_or_assign(cbor::CBORValue(kValidityUrlKey),
cbor::CBORValue(input.signature.validity_url.spec(), cbor::CBORValue(header.signature().validity_url.spec(),
cbor::CBORValue::Type::BYTE_STRING)); cbor::CBORValue::Type::BYTE_STRING));
// 11.4.3. "The text string "date" to the integer value of date." [spec text] // 11.4.3. "The text string "date" to the integer value of date." [spec text]
if (!base::IsValueInRangeForNumericType<int64_t>(input.signature.date)) if (!base::IsValueInRangeForNumericType<int64_t>(header.signature().date))
return base::nullopt; return base::nullopt;
map.insert_or_assign( map.insert_or_assign(
cbor::CBORValue(kDateKey), cbor::CBORValue(kDateKey),
cbor::CBORValue(base::checked_cast<int64_t>(input.signature.date))); cbor::CBORValue(base::checked_cast<int64_t>(header.signature().date)));
// 11.4.4. "The text string "expires" to the integer value of expires." // 11.4.4. "The text string "expires" to the integer value of expires."
// [spec text] // [spec text]
if (!base::IsValueInRangeForNumericType<int64_t>(input.signature.expires)) if (!base::IsValueInRangeForNumericType<int64_t>(header.signature().expires))
return base::nullopt; return base::nullopt;
map.insert_or_assign( map.insert_or_assign(
cbor::CBORValue(kExpiresKey), cbor::CBORValue(kExpiresKey),
cbor::CBORValue(base::checked_cast<int64_t>(input.signature.expires))); cbor::CBORValue(base::checked_cast<int64_t>(header.signature().expires)));
// 11.4.5. "The text string "headers" to the CBOR representation // 11.4.5. "The text string "headers" to the CBOR representation
// (Section 3.4) of exchange's headers." [spec text] // (Section 3.4) of exchange's headers." [spec text]
map.insert_or_assign(cbor::CBORValue(kHeadersKey), std::move(*headers_val)); map.insert_or_assign(cbor::CBORValue(kHeadersKey), std::move(*headers_val));
...@@ -187,9 +190,9 @@ bool VerifySignature(base::span<const uint8_t> sig, ...@@ -187,9 +190,9 @@ bool VerifySignature(base::span<const uint8_t> sig,
} }
base::Optional<std::vector<uint8_t>> GenerateSignedMessage( base::Optional<std::vector<uint8_t>> GenerateSignedMessage(
const SignedExchangeSignatureVerifier::Input& input) { const SignedExchangeHeader& header) {
// GenerateSignedMessageCBOR corresponds to Step 11.4. // GenerateSignedMessageCBOR corresponds to Step 11.4.
base::Optional<cbor::CBORValue> cbor_val = GenerateSignedMessageCBOR(input); base::Optional<cbor::CBORValue> cbor_val = GenerateSignedMessageCBOR(header);
if (!cbor_val) if (!cbor_val)
return base::nullopt; return base::nullopt;
base::Optional<std::vector<uint8_t>> cbor_message = base::Optional<std::vector<uint8_t>> cbor_message =
...@@ -212,63 +215,58 @@ base::Optional<std::vector<uint8_t>> GenerateSignedMessage( ...@@ -212,63 +215,58 @@ base::Optional<std::vector<uint8_t>> GenerateSignedMessage(
} // namespace } // namespace
SignedExchangeSignatureVerifier::Input::Input() = default;
SignedExchangeSignatureVerifier::Input::Input(const Input&) = default;
SignedExchangeSignatureVerifier::Input::~Input() = default;
SignedExchangeSignatureVerifier::Result SignedExchangeSignatureVerifier::Verify( SignedExchangeSignatureVerifier::Result SignedExchangeSignatureVerifier::Verify(
const Input& input) { const SignedExchangeHeader& header,
if (!input.certificate) { scoped_refptr<net::X509Certificate> certificate) {
if (!certificate) {
DVLOG(1) << "No certificate set."; DVLOG(1) << "No certificate set.";
return Result::kErrNoCertificate; return Result::kErrNoCertificate;
} }
if (!input.signature.cert_sha256.has_value()) { if (!header.signature().cert_sha256.has_value()) {
DVLOG(1) << "No certSha256 set."; DVLOG(1) << "No certSha256 set.";
return Result::kErrNoCertificateSHA256; return Result::kErrNoCertificateSHA256;
} }
// The main-certificate is the first certificate in certificate-chain. // The main-certificate is the first certificate in certificate-chain.
if (*input.signature.cert_sha256 != if (*header.signature().cert_sha256 !=
net::X509Certificate::CalculateFingerprint256( net::X509Certificate::CalculateFingerprint256(
input.certificate->cert_buffer())) { certificate->cert_buffer())) {
DVLOG(1) << "certSha256 mismatch."; DVLOG(1) << "certSha256 mismatch.";
return Result::kErrCertificateSHA256Mismatch; return Result::kErrCertificateSHA256Mismatch;
} }
auto message = GenerateSignedMessage(input); auto message = GenerateSignedMessage(header);
if (!message) { if (!message) {
DVLOG(1) << "Failed to reconstruct signed message."; DVLOG(1) << "Failed to reconstruct signed message.";
return Result::kErrInvalidSignatureFormat; return Result::kErrInvalidSignatureFormat;
} }
const std::string& sig = input.signature.sig; const std::string& sig = header.signature().sig;
if (!VerifySignature( if (!VerifySignature(
base::make_span(reinterpret_cast<const uint8_t*>(sig.data()), base::make_span(reinterpret_cast<const uint8_t*>(sig.data()),
sig.size()), sig.size()),
*message, input.certificate)) { *message, certificate)) {
DVLOG(1) << "Failed to verify signature \"sig\"."; DVLOG(1) << "Failed to verify signature \"sig\".";
return Result::kErrSignatureVerificationFailed; return Result::kErrSignatureVerificationFailed;
} }
if (!base::EqualsCaseInsensitiveASCII(input.signature.integrity, "mi")) { if (!base::EqualsCaseInsensitiveASCII(header.signature().integrity, "mi")) {
DVLOG(1) DVLOG(1)
<< "The current implemention only supports \"mi\" integrity scheme."; << "The current implemention only supports \"mi\" integrity scheme.";
return Result::kErrInvalidSignatureIntegrity; return Result::kErrInvalidSignatureIntegrity;
} }
// TODO(crbug.com/803774): Verify input.signature.{date,expires}. // TODO(crbug.com/803774): Verify header.signature().{date,expires}.
return Result::kSuccess; return Result::kSuccess;
} }
base::Optional<std::vector<uint8_t>> base::Optional<std::vector<uint8_t>>
SignedExchangeSignatureVerifier::EncodeCanonicalExchangeHeaders( SignedExchangeSignatureVerifier::EncodeCanonicalExchangeHeaders(
const SignedExchangeSignatureVerifier::Input& input) { const SignedExchangeHeader& header) {
base::Optional<cbor::CBORValue> cbor_val = base::Optional<cbor::CBORValue> cbor_val =
GenerateCanonicalExchangeHeadersCBOR(input); GenerateCanonicalExchangeHeadersCBOR(header);
if (!cbor_val) if (!cbor_val)
return base::nullopt; return base::nullopt;
......
...@@ -11,12 +11,13 @@ ...@@ -11,12 +11,13 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/optional.h" #include "base/optional.h"
#include "content/browser/web_package/signed_exchange_header_parser.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "net/cert/x509_certificate.h" #include "net/cert/x509_certificate.h"
namespace content { namespace content {
class SignedExchangeHeader;
// SignedExchangeSignatureVerifier verifies the signature of the given // SignedExchangeSignatureVerifier verifies the signature of the given
// signed exchange. This is done by reconstructing the signed message // signed exchange. This is done by reconstructing the signed message
// and verifying the cryptographic signature enclosed in "Signature" response // and verifying the cryptographic signature enclosed in "Signature" response
...@@ -29,21 +30,6 @@ namespace content { ...@@ -29,21 +30,6 @@ namespace content {
// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#rfc.section.3.6 // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#rfc.section.3.6
class CONTENT_EXPORT SignedExchangeSignatureVerifier final { class CONTENT_EXPORT SignedExchangeSignatureVerifier final {
public: public:
struct CONTENT_EXPORT Input {
public:
Input();
Input(const Input&);
~Input();
std::string method;
std::string url;
int response_code;
std::map<std::string, std::string> response_headers;
SignedExchangeHeaderParser::Signature signature;
scoped_refptr<net::X509Certificate> certificate;
};
enum class Result { enum class Result {
kSuccess, kSuccess,
kErrNoCertificate, kErrNoCertificate,
...@@ -54,10 +40,11 @@ class CONTENT_EXPORT SignedExchangeSignatureVerifier final { ...@@ -54,10 +40,11 @@ class CONTENT_EXPORT SignedExchangeSignatureVerifier final {
kErrInvalidSignatureIntegrity kErrInvalidSignatureIntegrity
}; };
static Result Verify(const Input& input); static Result Verify(const SignedExchangeHeader& header,
scoped_refptr<net::X509Certificate> certificate);
static base::Optional<std::vector<uint8_t>> EncodeCanonicalExchangeHeaders( static base::Optional<std::vector<uint8_t>> EncodeCanonicalExchangeHeaders(
const Input& input); const SignedExchangeHeader& header);
}; };
} // namespace content } // namespace content
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "content/browser/web_package/signed_exchange_signature_verifier.h" #include "content/browser/web_package/signed_exchange_signature_verifier.h"
#include "content/browser/web_package/signed_exchange_header.h"
#include "content/browser/web_package/signed_exchange_header_parser.h" #include "content/browser/web_package/signed_exchange_header_parser.h"
#include "net/cert/x509_certificate.h" #include "net/cert/x509_certificate.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
...@@ -13,20 +14,18 @@ namespace content { ...@@ -13,20 +14,18 @@ namespace content {
namespace { namespace {
TEST(SignedExchangeSignatureVerifier, EncodeCanonicalExchangeHeaders) { TEST(SignedExchangeSignatureVerifier, EncodeCanonicalExchangeHeaders) {
SignedExchangeSignatureVerifier::Input input; SignedExchangeHeader header;
input.method = "GET"; header.set_request_method("GET");
input.url = "https://example.com/index.html"; header.set_request_url(GURL("https://example.com/index.html"));
input.response_code = 200; header.set_response_code(net::HTTP_OK);
input.response_headers.insert( header.AddResponseHeader("content-type", "text/html; charset=utf-8");
std::make_pair("content-type", "text/html; charset=utf-8")); header.AddResponseHeader("content-encoding", "mi-sha256");
input.response_headers.insert( header.AddResponseHeader("unsigned-header", "foobar");
std::make_pair("content-encoding", "mi-sha256")); header.AddResponseHeader("signed-headers",
input.response_headers.insert(std::make_pair("unsigned-header", "foobar")); "\"content-type\", \"content-encoding\"");
input.response_headers.insert(std::make_pair(
"signed-headers", "\"content-type\", \"content-encoding\""));
base::Optional<std::vector<uint8_t>> encoded = base::Optional<std::vector<uint8_t>> encoded =
SignedExchangeSignatureVerifier::EncodeCanonicalExchangeHeaders(input); SignedExchangeSignatureVerifier::EncodeCanonicalExchangeHeaders(header);
ASSERT_TRUE(encoded.has_value()); ASSERT_TRUE(encoded.has_value());
static const uint8_t kExpected[] = { static const uint8_t kExpected[] = {
...@@ -113,41 +112,45 @@ TEST(SignedExchangeSignatureVerifier, Verify) { ...@@ -113,41 +112,45 @@ TEST(SignedExchangeSignatureVerifier, Verify) {
kCertPEM, arraysize(kCertPEM), net::X509Certificate::FORMAT_AUTO); kCertPEM, arraysize(kCertPEM), net::X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1u, certlist.size()); ASSERT_EQ(1u, certlist.size());
SignedExchangeSignatureVerifier::Input input; SignedExchangeHeader header;
input.method = "GET"; header.set_request_method("GET");
input.url = "https://example.com/index.html"; header.set_request_url(GURL("https://example.com/index.html"));
input.response_code = 200; header.set_response_code(net::HTTP_OK);
input.response_headers.insert( header.AddResponseHeader("content-type", "text/html; charset=utf-8");
std::make_pair("content-type", "text/html; charset=utf-8")); header.AddResponseHeader("content-encoding", "mi-sha256");
input.response_headers.insert( header.AddResponseHeader(
std::make_pair("content-encoding", "mi-sha256")); "mi", "mi-sha256=4ld4G-h-sQSoLBD39ndIO15O_82NXSzq9UMFEYI02JQ");
input.response_headers.insert(std::make_pair( header.AddResponseHeader("signed-headers",
"mi", "mi-sha256=4ld4G-h-sQSoLBD39ndIO15O_82NXSzq9UMFEYI02JQ")); "\"content-type\", \"content-encoding\", \"mi\"");
input.response_headers.insert(std::make_pair( header.SetSignatureForTesting((*signature)[0]);
"signed-headers", "\"content-type\", \"content-encoding\", \"mi\""));
input.signature = (*signature)[0]; auto certificate = certlist[0];
input.certificate = certlist[0];
EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kSuccess, EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kSuccess,
SignedExchangeSignatureVerifier::Verify(input)); SignedExchangeSignatureVerifier::Verify(header, certificate));
SignedExchangeSignatureVerifier::Input corrupted_input(input); SignedExchangeHeader corrupted_header(header);
corrupted_input.url = "https://example.com/bad.html"; corrupted_header.set_request_url(GURL("https://example.com/bad.html"));
EXPECT_EQ( EXPECT_EQ(
SignedExchangeSignatureVerifier::Result::kErrSignatureVerificationFailed, SignedExchangeSignatureVerifier::Result::kErrSignatureVerificationFailed,
SignedExchangeSignatureVerifier::Verify(corrupted_input)); SignedExchangeSignatureVerifier::Verify(corrupted_header, certificate));
SignedExchangeSignatureVerifier::Input badsig_input(input); SignedExchangeHeader badsig_header(header);
badsig_input.signature.sig[0]++; SignedExchangeHeaderParser::Signature badsig = header.signature();
badsig.sig[0]++;
badsig_header.SetSignatureForTesting(badsig);
EXPECT_EQ( EXPECT_EQ(
SignedExchangeSignatureVerifier::Result::kErrSignatureVerificationFailed, SignedExchangeSignatureVerifier::Result::kErrSignatureVerificationFailed,
SignedExchangeSignatureVerifier::Verify(badsig_input)); SignedExchangeSignatureVerifier::Verify(badsig_header, certificate));
SignedExchangeSignatureVerifier::Input badsigsha256_input(input); SignedExchangeHeader badsigsha256_header(header);
badsigsha256_input.signature.cert_sha256->data[0]++; SignedExchangeHeaderParser::Signature badsigsha256 = header.signature();
badsigsha256.cert_sha256->data[0]++;
badsigsha256_header.SetSignatureForTesting(badsigsha256);
EXPECT_EQ( EXPECT_EQ(
SignedExchangeSignatureVerifier::Result::kErrCertificateSHA256Mismatch, SignedExchangeSignatureVerifier::Result::kErrCertificateSHA256Mismatch,
SignedExchangeSignatureVerifier::Verify(badsigsha256_input)); SignedExchangeSignatureVerifier::Verify(badsigsha256_header,
certificate));
} }
} // namespace } // namespace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment