Commit f7702193 authored by Tsuyoshi Horo's avatar Tsuyoshi Horo Committed by Commit Bot

Verify the certificate using CertVerifier for Signed Exchange


Bug: 803774
Change-Id: I9c888a507913c54e11f3f6b3014001586151eed9
Reviewed-on: https://chromium-review.googlesource.com/920803
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
Reviewed-by: default avatarRyan Sleevi <rsleevi@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Cr-Commit-Position: refs/heads/master@{#541432}
parent b142aad1
......@@ -339,7 +339,8 @@ class NavigationURLLoaderNetworkService::URLLoaderRequestController
resource_context_, url_request_context_getter),
base::BindRepeating(
&URLLoaderRequestController::CreateURLLoaderThrottles,
base::Unretained(this))));
base::Unretained(this)),
url_request_context_getter));
}
// The ResourceDispatcherHostImpl can be null in unit tests.
......@@ -482,7 +483,8 @@ class NavigationURLLoaderNetworkService::URLLoaderRequestController
default_url_loader_factory_getter_->GetNetworkFactory(),
base::BindRepeating(
&URLLoaderRequestController::CreateURLLoaderThrottles,
base::Unretained(this))));
base::Unretained(this)),
url_request_context_getter));
}
Restart();
......
......@@ -5,20 +5,28 @@
#include "content/browser/web_package/signed_exchange_handler.h"
#include "base/feature_list.h"
#include "base/strings/string_number_conversions.h"
#include "components/cbor/cbor_reader.h"
#include "content/browser/loader/merkle_integrity_source_stream.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_header_parser.h"
#include "content/browser/web_package/signed_exchange_signature_verifier.h"
#include "content/public/common/content_features.h"
#include "content/public/common/shared_url_loader_factory.h"
#include "content/public/common/url_loader_throttle.h"
#include "mojo/public/cpp/system/string_data_pipe_producer.h"
#include "net/base/io_buffer.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/x509_certificate.h"
#include "net/filter/source_stream.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "net/ssl/ssl_config.h"
#include "net/ssl/ssl_config_service.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/resource_response.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
......@@ -31,6 +39,8 @@ constexpr size_t kBufferSizeForRead = 65536;
constexpr char kMiHeader[] = "MI";
net::CertVerifier* g_cert_verifier_for_testing = nullptr;
cbor::CBORValue BytestringFromString(base::StringPiece in_string) {
return cbor::CBORValue(
std::vector<uint8_t>(in_string.begin(), in_string.end()));
......@@ -65,17 +75,28 @@ class BufferSourceStream : public net::SourceStream {
} // namespace
// static
void SignedExchangeHandler::SetCertVerifierForTesting(
net::CertVerifier* cert_verifier) {
g_cert_verifier_for_testing = cert_verifier;
}
SignedExchangeHandler::SignedExchangeHandler(
std::unique_ptr<net::SourceStream> body,
ExchangeHeadersCallback headers_callback,
url::Origin request_initiator,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter)
URLLoaderThrottlesGetter url_loader_throttles_getter,
scoped_refptr<net::URLRequestContextGetter> request_context_getter)
: headers_callback_(std::move(headers_callback)),
source_(std::move(body)),
request_initiator_(std::move(request_initiator)),
url_loader_factory_(std::move(url_loader_factory)),
url_loader_throttles_getter_(std::move(url_loader_throttles_getter)),
request_context_getter_(std::move(request_context_getter)),
net_log_(net::NetLogWithSource::Make(
request_context_getter_->GetURLRequestContext()->net_log(),
net::NetLogSourceType::CERT_VERIFIER_JOB)),
weak_factory_(this) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
......@@ -114,6 +135,8 @@ void SignedExchangeHandler::DidRead(bool completed_syncly, int result) {
return;
}
// TODO(https://crbug.com/815019): This logic can cause browser-side DoS. We
// MUST fix it before shipping.
original_body_string_.append(read_buf_->data(), result);
if (completed_syncly) {
......@@ -209,8 +232,18 @@ bool SignedExchangeHandler::RunHeadersCallback() {
}
base::StringPiece status_code_str =
status_iter->second.GetBytestringAsString();
int status_code;
if (!base::StringToInt(status_code_str, &status_code)) {
DVLOG(1) << "Invalid status code " << status_code_str;
return false;
}
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 ");
status_code_str.AppendToString(&fake_header_str);
......@@ -225,13 +258,18 @@ bool SignedExchangeHandler::RunHeadersCallback() {
if (name == kMethodKey)
continue;
// TODO(https://crbug.com/803774): Stop going through
// net::HttpResponseHeaders but just use
// SignedExchangeSignatureVerifier::Input.
// TODO(https://crbug.com/803774): Check that name and value don't contain
// special characters using IsValidHeaderName and IsValidHeaderValue.
name.AppendToString(&fake_header_str);
fake_header_str.append(": ");
value.AppendToString(&fake_header_str);
fake_header_str.append("\r\n");
if (std::string(name) == "signature") {
if (name == "signature")
signatures = SignedExchangeHeaderParser::ParseSignature(value);
}
verifier_input->response_headers[name.as_string()] = value.as_string();
}
fake_header_str.append("\r\n");
response_head_.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
......@@ -256,15 +294,26 @@ bool SignedExchangeHandler::RunHeadersCallback() {
if (!signatures || signatures->empty())
return false;
verifier_input->method = request_method_;
verifier_input->url = request_url_.spec();
verifier_input->response_code = status_code;
verifier_input->signature = (*signatures)[0];
// 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|
// may be empty.
DCHECK(cert_url.is_valid());
DCHECK(url_loader_factory_);
DCHECK(url_loader_throttles_getter_);
std::vector<std::unique_ptr<URLLoaderThrottle>> throttles =
std::move(url_loader_throttles_getter_).Run();
cert_fetcher_ = SignedExchangeCertFetcher::CreateAndStart(
std::move(url_loader_factory_), std::move(throttles),
GURL((*signatures)[0].cert_url), std::move(request_initiator_), false,
std::move(url_loader_factory_), std::move(throttles), cert_url,
std::move(request_initiator_), false,
base::BindOnce(&SignedExchangeHandler::OnCertReceived,
base::Unretained(this)));
base::Unretained(this), std::move(verifier_input)));
return true;
}
......@@ -276,17 +325,73 @@ void SignedExchangeHandler::RunErrorCallback(net::Error error) {
}
void SignedExchangeHandler::OnCertReceived(
std::unique_ptr<SignedExchangeSignatureVerifier::Input> verifier_input,
scoped_refptr<net::X509Certificate> cert) {
if (!cert) {
DVLOG(1) << "Fetching certificate error";
RunErrorCallback(net::ERR_FAILED);
return;
}
// TODO(https://crbug.com/803774): Verify the certificate and generate a
// SSLInfo.
verifier_input->certificate = cert;
if (SignedExchangeSignatureVerifier::Verify(*verifier_input) !=
SignedExchangeSignatureVerifier::Result::kSuccess) {
RunErrorCallback(net::ERR_FAILED);
return;
}
net::URLRequestContext* request_context =
request_context_getter_->GetURLRequestContext();
if (!request_context) {
RunErrorCallback(net::ERR_CONTEXT_SHUT_DOWN);
return;
}
unverified_cert_ = cert;
net::SSLConfig config;
request_context->ssl_config_service()->GetSSLConfig(&config);
net::CertVerifier* cert_verifier = g_cert_verifier_for_testing
? g_cert_verifier_for_testing
: request_context->cert_verifier();
// TODO(https://crbug.com/815024): Get the OCSP response from the
// “status_request” extension of the main-certificate, and check the lifetime
// (nextUpdate - thisUpdate) is less than 7 days.
int result = cert_verifier->Verify(
net::CertVerifier::RequestParams(
unverified_cert_, request_url_.host(), config.GetCertVerifyFlags(),
std::string() /* ocsp_response */, net::CertificateList()),
net::SSLConfigService::GetCRLSet().get(), &cert_verify_result_,
base::BindRepeating(&SignedExchangeHandler::OnCertVerifyComplete,
base::Unretained(this)),
&cert_verifier_request_, net_log_);
// TODO(https://crbug.com/803774): Avoid these recursive patterns by using
// explicit state machines (eg: DoLoop() in //net).
if (result != net::ERR_IO_PENDING)
OnCertVerifyComplete(result);
}
void SignedExchangeHandler::OnCertVerifyComplete(int result) {
if (result != net::OK) {
DVLOG(1) << "Certificate verification error: " << result;
RunErrorCallback(static_cast<net::Error>(result));
return;
}
net::SSLInfo ssl_info;
ssl_info.cert = cert_verify_result_.verified_cert;
ssl_info.unverified_cert = unverified_cert_;
ssl_info.cert_status = cert_verify_result_.cert_status;
ssl_info.is_issued_by_known_root =
cert_verify_result_.is_issued_by_known_root;
ssl_info.public_key_hashes = cert_verify_result_.public_key_hashes;
ssl_info.ocsp_result = cert_verify_result_.ocsp_result;
ssl_info.is_fatal_cert_error =
net::IsCertStatusError(ssl_info.cert_status) &&
!net::IsCertStatusMinorError(ssl_info.cert_status);
// TODO(https://crbug.com/815025): Verify the Certificate Transparency status.
std::move(headers_callback_)
.Run(net::OK, request_url_, request_method_, response_head_,
std::move(mi_stream_), base::Optional<net::SSLInfo>());
std::move(mi_stream_), ssl_info);
}
} // namespace content
......@@ -9,18 +9,26 @@
#include "base/callback.h"
#include "base/optional.h"
#include "content/browser/web_package/signed_exchange_signature_verifier.h"
#include "content/common/content_export.h"
#include "content/public/common/shared_url_loader_factory.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "net/base/completion_callback.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/cert_verify_result.h"
#include "net/log/net_log_with_source.h"
#include "net/ssl/ssl_info.h"
#include "services/network/public/cpp/resource_response.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace net {
class CertVerifier;
class CertVerifyResult;
class SourceStream;
}
class URLRequestContextGetter;
class X509Certificate;
} // namespace net
namespace content {
......@@ -29,7 +37,7 @@ class SignedExchangeCertFetcher;
class URLLoaderThrottle;
class MerkleIntegritySourceStream;
// IMPORTANT: Currenly SignedExchangeHandler doesn't implement any verifying
// IMPORTANT: Currenly SignedExchangeHandler partially implements the verifying
// logic.
// TODO(https://crbug.com/803774): Implement verifying logic.
class CONTENT_EXPORT SignedExchangeHandler {
......@@ -46,6 +54,10 @@ class CONTENT_EXPORT SignedExchangeHandler {
using URLLoaderThrottlesGetter = base::RepeatingCallback<
std::vector<std::unique_ptr<content::URLLoaderThrottle>>()>;
// TODO(https://crbug.com/817187): Find a more sophisticated way to use a
// MockCertVerifier in browser tests instead of using the static method.
static void SetCertVerifierForTesting(net::CertVerifier* cert_verifier);
// Once constructed |this| starts reading the |body| and parses the response
// as a signed HTTP exchange. The response body of the exchange can be read
// from |payload_stream| passed to |headers_callback|. |url_loader_factory|
......@@ -56,7 +68,8 @@ class CONTENT_EXPORT SignedExchangeHandler {
ExchangeHeadersCallback headers_callback,
url::Origin request_initiator,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter);
URLLoaderThrottlesGetter url_loader_throttles_getter,
scoped_refptr<net::URLRequestContextGetter> request_context_getter);
~SignedExchangeHandler();
protected:
......@@ -68,7 +81,10 @@ class CONTENT_EXPORT SignedExchangeHandler {
bool RunHeadersCallback();
void RunErrorCallback(net::Error);
void OnCertReceived(scoped_refptr<net::X509Certificate> cert);
void OnCertReceived(
std::unique_ptr<SignedExchangeSignatureVerifier::Input> verifier_input,
scoped_refptr<net::X509Certificate> cert);
void OnCertVerifyComplete(int result);
// Signed exchange contents.
GURL request_url_;
......@@ -94,6 +110,19 @@ class CONTENT_EXPORT SignedExchangeHandler {
std::unique_ptr<SignedExchangeCertFetcher> cert_fetcher_;
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
scoped_refptr<net::X509Certificate> unverified_cert_;
// CertVerifyResult must be freed after the Request has been destructed.
// So |cert_verify_result_| must be written before |cert_verifier_request_|.
net::CertVerifyResult cert_verify_result_;
std::unique_ptr<net::CertVerifier::Request> cert_verifier_request_;
// TODO(https://crbug.com/767450): figure out what we should do for NetLog
// with Network Service.
net::NetLogWithSource net_log_;
base::WeakPtrFactory<SignedExchangeHandler> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(SignedExchangeHandler);
......
......@@ -9,6 +9,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "crypto/sha2.h"
namespace content {
......@@ -213,12 +214,42 @@ SignedExchangeHeaderParser::ParseSignature(base::StringPiece signature_str) {
Signature& sig = signatures.back();
sig.label = value.label;
sig.sig = value.params["sig"];
if (sig.sig.empty()) {
DVLOG(1) << "ParseSignature: 'sig' parameter is not set";
return base::nullopt;
}
sig.integrity = value.params["integrity"];
sig.cert_url = value.params["certUrl"];
sig.cert_sha256 = value.params["certSha256"];
sig.ed25519_key = value.params["ed25519Key"];
sig.validity_url = value.params["validityUrl"];
if (sig.integrity.empty()) {
DVLOG(1) << "ParseSignature: 'integrity' parameter is not set";
return base::nullopt;
}
sig.cert_url = GURL(value.params["certUrl"]);
if (!sig.cert_url.is_valid()) {
// TODO(https://crbug.com/819467) : When we will support "ed25519Key", the
// params may not have "certUrl".
DVLOG(1) << "ParseSignature: 'certUrl' parameter is not a valid URL: "
<< value.params["certUrl"];
return base::nullopt;
}
const std::string cert_sha256_string = value.params["certSha256"];
if (cert_sha256_string.size() != crypto::kSHA256Length) {
// TODO(https://crbug.com/819467) : When we will support "ed25519Key", the
// params may not have "certSha256".
DVLOG(1) << "ParseSignature: 'certSha256' parameter is not a valid "
"SHA-256 digest.";
return base::nullopt;
}
net::SHA256HashValue cert_sha256;
memcpy(&cert_sha256.data, cert_sha256_string.data(), crypto::kSHA256Length);
sig.cert_sha256 = std::move(cert_sha256);
// TODO(https://crbug.com/819467): Support ed25519key.
// sig.ed25519_key = value.params["ed25519Key"];
sig.validity_url = GURL(value.params["validityUrl"]);
if (!sig.validity_url.is_valid()) {
DVLOG(1) << "ParseSignature: 'validityUrl' parameter is not a valid URL: "
<< value.params["validityUrl"];
return base::nullopt;
}
if (!base::StringToUint64(value.params["date"], &sig.date)) {
DVLOG(1) << "ParseSignature: 'date' parameter is not a number: "
<< sig.date;
......@@ -229,18 +260,6 @@ SignedExchangeHeaderParser::ParseSignature(base::StringPiece signature_str) {
<< sig.expires;
return base::nullopt;
}
bool has_cert = !sig.cert_url.empty() && !sig.cert_sha256.empty();
bool has_ed25519_key = !sig.ed25519_key.empty();
if (sig.sig.empty() || sig.integrity.empty() || sig.validity_url.empty() ||
(!has_cert && !has_ed25519_key)) {
DVLOG(1) << "ParseSignature: incomplete signature";
return base::nullopt;
}
if (has_cert && has_ed25519_key) {
DVLOG(1) << "ParseSignature: signature has both certUrl and ed25519Key";
return base::nullopt;
}
}
return signatures;
}
......
......@@ -8,10 +8,13 @@
#include <stdint.h>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "content/common/content_export.h"
#include "net/base/hash_value.h"
#include "url/gurl.h"
namespace content {
......@@ -27,10 +30,11 @@ class CONTENT_EXPORT SignedExchangeHeaderParser {
std::string label;
std::string sig;
std::string integrity;
std::string cert_url;
std::string cert_sha256;
std::string ed25519_key;
std::string validity_url;
GURL cert_url;
base::Optional<net::SHA256HashValue> cert_sha256;
// TODO(https://crbug.com/819467): Support ed25519key.
// std::string ed25519_key;
GURL validity_url;
uint64_t date;
uint64_t expires;
};
......
......@@ -55,13 +55,15 @@ TEST_F(SignedExchangeHeaderParserTest, ParseSignature) {
" certUrl=\"https://example.com/oldcerts\";"
" certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;"
" date=1511128380; expires=1511733180,"
"srisig;"
" sig=*lGZVaJJM5f2oGczFlLmBdKTDL+QADza4BgeO494ggACYJOvrof6uh5OJCcwKrk7DK+"
"LBch0jssDYPp5CLc1SDA;"
"sig2;"
" sig=*MEQCIGjZRqTRf9iKNkGFyzRMTFgwf/BrY2ZNIP/dykhUV0aYAiBTXg+8wujoT4n/W+"
"cNgb7pGqQvIUGYZ8u8HZJ5YH26Qg;"
" integrity=\"mi\";"
" validityUrl=\"https://example.com/resource.validity.1511128380\";"
" ed25519Key=*zsSevyFsxyZHiUluVBDd4eypdRLTqyWRVOJuuKUz+A8;"
" certUrl=\"https://example.com/newcerts\";"
" certSha256=*J/lEm9kNRODdCmINbvitpvdYKNQ+YgBj99DlYp4fEXw;"
" date=1511128380; expires=1511733180";
const uint8_t decoded_sig1[] = {
0x30, 0x45, 0x02, 0x21, 0x00, 0xd7, 0x94, 0x8d, 0xa0, 0x37, 0x74, 0x4d,
0x06, 0x58, 0x05, 0x8a, 0xe4, 0x4d, 0x16, 0x96, 0x57, 0x70, 0x32, 0x1a,
......@@ -69,24 +71,24 @@ TEST_F(SignedExchangeHeaderParserTest, ParseSignature) {
0x2c, 0x02, 0x20, 0x6b, 0xd0, 0xec, 0x54, 0xe3, 0x0c, 0xfa, 0x0e, 0x58,
0xa7, 0x01, 0x01, 0x74, 0x65, 0xb7, 0xb1, 0x2f, 0x9b, 0xbe, 0x79, 0x80,
0x24, 0x98, 0x92, 0x33, 0x08, 0x6e, 0x05, 0xda, 0xa9, 0xe5, 0x46};
const uint8_t decoded_cert_sha256[] = {
0x5b, 0xbb, 0x81, 0xf7, 0xaf, 0x5d, 0x15, 0x6d, 0xcc, 0x6f, 0x96,
0x5e, 0x7c, 0xf4, 0xbd, 0x4e, 0xae, 0x59, 0x6c, 0x7e, 0x62, 0x4a,
0x63, 0x88, 0x2e, 0x98, 0xef, 0xda, 0xa1, 0x00, 0xae, 0x62};
const net::SHA256HashValue decoded_cert_sha256_1 = {
{0x5b, 0xbb, 0x81, 0xf7, 0xaf, 0x5d, 0x15, 0x6d, 0xcc, 0x6f, 0x96,
0x5e, 0x7c, 0xf4, 0xbd, 0x4e, 0xae, 0x59, 0x6c, 0x7e, 0x62, 0x4a,
0x63, 0x88, 0x2e, 0x98, 0xef, 0xda, 0xa1, 0x00, 0xae, 0x62}};
const uint8_t decoded_sig2[] = {
0x94, 0x66, 0x55, 0x68, 0x92, 0x4c, 0xe5, 0xfd, 0xa8, 0x19, 0xcc,
0xc5, 0x94, 0xb9, 0x81, 0x74, 0xa4, 0xc3, 0x2f, 0xe4, 0x00, 0x0f,
0x36, 0xb8, 0x06, 0x07, 0x8e, 0xe3, 0xde, 0x20, 0x80, 0x00, 0x98,
0x24, 0xeb, 0xeb, 0xa1, 0xfe, 0xae, 0x87, 0x93, 0x89, 0x09, 0xcc,
0x0a, 0xae, 0x4e, 0xc3, 0x2b, 0xe2, 0xc1, 0x72, 0x1d, 0x23, 0xb2,
0xc0, 0xd8, 0x3e, 0x9e, 0x42, 0x2d, 0xcd, 0x52, 0x0c};
const uint8_t decoded_ed25519_key[] = {
0xce, 0xc4, 0x9e, 0xbf, 0x21, 0x6c, 0xc7, 0x26, 0x47, 0x89, 0x49,
0x6e, 0x54, 0x10, 0xdd, 0xe1, 0xec, 0xa9, 0x75, 0x12, 0xd3, 0xab,
0x25, 0x91, 0x54, 0xe2, 0x6e, 0xb8, 0xa5, 0x33, 0xf8, 0x0f};
0x30, 0x44, 0x02, 0x20, 0x68, 0xd9, 0x46, 0xa4, 0xd1, 0x7f, 0xd8, 0x8a,
0x36, 0x41, 0x85, 0xcb, 0x34, 0x4c, 0x4c, 0x58, 0x30, 0x7f, 0xf0, 0x6b,
0x63, 0x66, 0x4d, 0x20, 0xff, 0xdd, 0xca, 0x48, 0x54, 0x57, 0x46, 0x98,
0x02, 0x20, 0x53, 0x5e, 0x0f, 0xbc, 0xc2, 0xe8, 0xe8, 0x4f, 0x89, 0xff,
0x5b, 0xe7, 0x0d, 0x81, 0xbe, 0xe9, 0x1a, 0xa4, 0x2f, 0x21, 0x41, 0x98,
0x67, 0xcb, 0xbc, 0x1d, 0x92, 0x79, 0x60, 0x7d, 0xba, 0x42};
const net::SHA256HashValue decoded_cert_sha256_2 = {
{0x27, 0xf9, 0x44, 0x9b, 0xd9, 0x0d, 0x44, 0xe0, 0xdd, 0x0a, 0x62,
0x0d, 0x6e, 0xf8, 0xad, 0xa6, 0xf7, 0x58, 0x28, 0xd4, 0x3e, 0x62,
0x00, 0x63, 0xf7, 0xd0, 0xe5, 0x62, 0x9e, 0x1f, 0x11, 0x7c}};
auto signatures = SignedExchangeHeaderParser::ParseSignature(hdr_string);
EXPECT_TRUE(signatures.has_value());
ASSERT_TRUE(signatures.has_value());
ASSERT_EQ(signatures->size(), 2u);
EXPECT_EQ(signatures->at(0).label, "sig1");
......@@ -97,22 +99,19 @@ TEST_F(SignedExchangeHeaderParserTest, ParseSignature) {
EXPECT_EQ(signatures->at(0).validity_url,
"https://example.com/resource.validity.1511128380");
EXPECT_EQ(signatures->at(0).cert_url, "https://example.com/oldcerts");
EXPECT_EQ(signatures->at(0).cert_sha256,
std::string(reinterpret_cast<const char*>(decoded_cert_sha256),
sizeof(decoded_cert_sha256)));
EXPECT_EQ(signatures->at(0).cert_sha256, decoded_cert_sha256_1);
EXPECT_EQ(signatures->at(0).date, 1511128380ul);
EXPECT_EQ(signatures->at(0).expires, 1511733180ul);
EXPECT_EQ(signatures->at(1).label, "srisig");
EXPECT_EQ(signatures->at(1).label, "sig2");
EXPECT_EQ(signatures->at(1).sig,
std::string(reinterpret_cast<const char*>(decoded_sig2),
sizeof(decoded_sig2)));
EXPECT_EQ(signatures->at(1).integrity, "mi");
EXPECT_EQ(signatures->at(1).validity_url,
"https://example.com/resource.validity.1511128380");
EXPECT_EQ(signatures->at(1).ed25519_key,
std::string(reinterpret_cast<const char*>(decoded_ed25519_key),
sizeof(decoded_ed25519_key)));
EXPECT_EQ(signatures->at(1).cert_url, "https://example.com/newcerts");
EXPECT_EQ(signatures->at(1).cert_sha256, decoded_cert_sha256_2);
EXPECT_EQ(signatures->at(1).date, 1511128380ul);
EXPECT_EQ(signatures->at(1).expires, 1511733180ul);
}
......@@ -131,7 +130,7 @@ TEST_F(SignedExchangeHeaderParserTest, IncompleteSignature) {
EXPECT_FALSE(signatures.has_value());
}
TEST_F(SignedExchangeHeaderParserTest, HasBothCertAndEd25519Key) {
TEST_F(SignedExchangeHeaderParserTest, DuplicatedParam) {
const char hdr_string[] =
"sig1;"
" sig=*MEUCIQDXlI2gN3RNBlgFiuRNFpZXcDIaUpX6HIEwcZEc0cZYLAIga9DsVOMM+"
......@@ -139,21 +138,34 @@ TEST_F(SignedExchangeHeaderParserTest, HasBothCertAndEd25519Key) {
" integrity=\"mi\";"
" validityUrl=\"https://example.com/resource.validity.1511128380\";"
" certUrl=\"https://example.com/oldcerts\";"
" certUrl=\"https://example.com/oldcerts\";"
" certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;"
" ed25519Key=*zsSevyFsxyZHiUluVBDd4eypdRLTqyWRVOJuuKUz+A8;"
" date=1511128380; expires=1511733180";
auto signatures = SignedExchangeHeaderParser::ParseSignature(hdr_string);
EXPECT_FALSE(signatures.has_value());
}
TEST_F(SignedExchangeHeaderParserTest, DuplicatedParam) {
TEST_F(SignedExchangeHeaderParserTest, InvalidCertURL) {
const char hdr_string[] =
"sig1;"
" sig=*MEUCIQDXlI2gN3RNBlgFiuRNFpZXcDIaUpX6HIEwcZEc0cZYLAIga9DsVOMM+"
"g5YpwEBdGW3sS+bvnmAJJiSMwhuBdqp5UY;"
" integrity=\"mi\";"
" validityUrl=\"https://example.com/resource.validity.1511128380\";"
" certUrl=\"https://example.com/oldcerts\";"
" certUrl=\"https:://example.com/oldcerts\";"
" certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;"
" date=1511128380; expires=1511733180";
auto signatures = SignedExchangeHeaderParser::ParseSignature(hdr_string);
EXPECT_FALSE(signatures.has_value());
}
TEST_F(SignedExchangeHeaderParserTest, InvalidValidityUrl) {
const char hdr_string[] =
"sig1;"
" sig=*MEUCIQDXlI2gN3RNBlgFiuRNFpZXcDIaUpX6HIEwcZEc0cZYLAIga9DsVOMM+"
"g5YpwEBdGW3sS+bvnmAJJiSMwhuBdqp5UY;"
" integrity=\"mi\";"
" validityUrl=\"https:://example.com/resource.validity.1511128380\";"
" certUrl=\"https://example.com/oldcerts\";"
" certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;"
" date=1511128380; expires=1511733180";
......@@ -161,4 +173,18 @@ TEST_F(SignedExchangeHeaderParserTest, DuplicatedParam) {
EXPECT_FALSE(signatures.has_value());
}
TEST_F(SignedExchangeHeaderParserTest, InvalidCertSHA256) {
const char hdr_string[] =
"sig1;"
" sig=*MEUCIQDXlI2gN3RNBlgFiuRNFpZXcDIaUpX6HIEwcZEc0cZYLAIga9DsVOMM+"
"g5YpwEBdGW3sS+bvnmAJJiSMwhuBdqp5UY;"
" integrity=\"mi\";"
" validityUrl=\"https://example.com/resource.validity.1511128380\";"
" certUrl=\"https://example.com/oldcerts\";"
" certSha256=*W7uB969dFW3Mb5ZefPS9;"
" date=1511128380; expires=1511733180";
auto signatures = SignedExchangeHeaderParser::ParseSignature(hdr_string);
EXPECT_FALSE(signatures.has_value());
}
} // namespace content
......@@ -116,15 +116,19 @@ base::Optional<cbor::CBORValue> GenerateSignedMessageCBOR(
cbor::CBORValue::MapValue map;
// 11.4.1. "If certSha256 is set: The text string "certSha256" to the byte
// string value of certSha256." [spec text]
if (!input.signature.cert_sha256.empty()) {
map.insert_or_assign(cbor::CBORValue(kCertSha256Key),
cbor::CBORValue(input.signature.cert_sha256,
cbor::CBORValue::Type::BYTE_STRING));
if (input.signature.cert_sha256.has_value()) {
map.insert_or_assign(
cbor::CBORValue(kCertSha256Key),
cbor::CBORValue(
base::StringPiece(reinterpret_cast<const char*>(
input.signature.cert_sha256->data),
sizeof(input.signature.cert_sha256->data)),
cbor::CBORValue::Type::BYTE_STRING));
}
// 11.4.2. "The text string "validityUrl" to the byte string value of
// validityUrl." [spec text]
map.insert_or_assign(cbor::CBORValue(kValidityUrlKey),
cbor::CBORValue(input.signature.validity_url,
cbor::CBORValue(input.signature.validity_url.spec(),
cbor::CBORValue::Type::BYTE_STRING));
// 11.4.3. "The text string "date" to the integer value of date." [spec text]
if (!base::IsValueInRangeForNumericType<int64_t>(input.signature.date))
......@@ -214,14 +218,30 @@ SignedExchangeSignatureVerifier::Input::Input(const Input&) = default;
SignedExchangeSignatureVerifier::Input::~Input() = default;
bool SignedExchangeSignatureVerifier::Verify(const Input& input) {
// TODO(crbug.com/803774): Verify input.signature.certSha256 against
// input.certificate.
SignedExchangeSignatureVerifier::Result SignedExchangeSignatureVerifier::Verify(
const Input& input) {
if (!input.certificate) {
DVLOG(1) << "No certificate set.";
return Result::kErrNoCertificate;
}
if (!input.signature.cert_sha256.has_value()) {
DVLOG(1) << "No certSha256 set.";
return Result::kErrNoCertificateSHA256;
}
// The main-certificate is the first certificate in certificate-chain.
if (*input.signature.cert_sha256 !=
net::X509Certificate::CalculateFingerprint256(
input.certificate->cert_buffer())) {
DVLOG(1) << "certSha256 mismatch.";
return Result::kErrCertificateSHA256Mismatch;
}
auto message = GenerateSignedMessage(input);
if (!message) {
DVLOG(1) << "Failed to reconstruct signed message.";
return false;
return Result::kErrInvalidSignatureFormat;
}
const std::string& sig = input.signature.sig;
......@@ -230,18 +250,18 @@ bool SignedExchangeSignatureVerifier::Verify(const Input& input) {
sig.size()),
*message, input.certificate)) {
DVLOG(1) << "Failed to verify signature \"sig\".";
return false;
return Result::kErrSignatureVerificationFailed;
}
if (!base::EqualsCaseInsensitiveASCII(input.signature.integrity, "mi")) {
DVLOG(1)
<< "The current implemention only supports \"mi\" integrity scheme.";
return false;
return Result::kErrInvalidSignatureIntegrity;
}
// TODO(crbug.com/803774): Verify input.signature.{date,expires}.
return true;
return Result::kSuccess;
}
base::Optional<std::vector<uint8_t>>
......
......@@ -44,7 +44,17 @@ class CONTENT_EXPORT SignedExchangeSignatureVerifier final {
scoped_refptr<net::X509Certificate> certificate;
};
static bool Verify(const Input& input);
enum class Result {
kSuccess,
kErrNoCertificate,
kErrNoCertificateSHA256,
kErrCertificateSHA256Mismatch,
kErrInvalidSignatureFormat,
kErrSignatureVerificationFailed,
kErrInvalidSignatureIntegrity
};
static Result Verify(const Input& input);
static base::Optional<std::vector<uint8_t>> EncodeCanonicalExchangeHeaders(
const Input& input);
......
......@@ -128,15 +128,26 @@ TEST(SignedExchangeSignatureVerifier, Verify) {
input.signature = (*signature)[0];
input.certificate = certlist[0];
EXPECT_TRUE(SignedExchangeSignatureVerifier::Verify(input));
EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kSuccess,
SignedExchangeSignatureVerifier::Verify(input));
SignedExchangeSignatureVerifier::Input corrupted_input(input);
corrupted_input.url = "https://example.com/bad.html";
EXPECT_FALSE(SignedExchangeSignatureVerifier::Verify(corrupted_input));
EXPECT_EQ(
SignedExchangeSignatureVerifier::Result::kErrSignatureVerificationFailed,
SignedExchangeSignatureVerifier::Verify(corrupted_input));
SignedExchangeSignatureVerifier::Input badsig_input(input);
badsig_input.signature.sig[0]++;
EXPECT_FALSE(SignedExchangeSignatureVerifier::Verify(badsig_input));
EXPECT_EQ(
SignedExchangeSignatureVerifier::Result::kErrSignatureVerificationFailed,
SignedExchangeSignatureVerifier::Verify(badsig_input));
SignedExchangeSignatureVerifier::Input badsigsha256_input(input);
badsigsha256_input.signature.cert_sha256->data[0]++;
EXPECT_EQ(
SignedExchangeSignatureVerifier::Result::kErrCertificateSHA256Mismatch,
SignedExchangeSignatureVerifier::Verify(badsigsha256_input));
}
} // namespace
......
......@@ -14,7 +14,6 @@ class URLRequestContextGetter;
namespace content {
class URLRequestContextGetter;
class ResourceContext;
// A URLLoaderFactory used for fetching certificate of signed HTTP exchange
......
......@@ -15,6 +15,7 @@
#include "content/public/common/shared_url_loader_factory.h"
#include "net/cert/cert_status_flags.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
......@@ -79,7 +80,8 @@ WebPackageLoader::WebPackageLoader(
url::Origin request_initiator,
uint32_t url_loader_options,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter)
URLLoaderThrottlesGetter url_loader_throttles_getter,
scoped_refptr<net::URLRequestContextGetter> request_context_getter)
: original_response_timing_info_(
std::make_unique<ResponseTimingInfo>(original_response)),
forwarding_client_(std::move(forwarding_client)),
......@@ -88,6 +90,7 @@ WebPackageLoader::WebPackageLoader(
url_loader_options_(url_loader_options),
url_loader_factory_(std::move(url_loader_factory)),
url_loader_throttles_getter_(std::move(url_loader_throttles_getter)),
request_context_getter_(std::move(request_context_getter)),
weak_factory_(this) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
url_loader_.Bind(std::move(endpoints->url_loader));
......@@ -171,7 +174,8 @@ void WebPackageLoader::OnStartLoadingResponseBody(
base::BindOnce(&WebPackageLoader::OnHTTPExchangeFound,
weak_factory_.GetWeakPtr()),
std::move(request_initiator_), std::move(url_loader_factory_),
std::move(url_loader_throttles_getter_));
std::move(url_loader_throttles_getter_),
std::move(request_context_getter_));
}
void WebPackageLoader::OnComplete(
......
......@@ -17,6 +17,7 @@
namespace net {
class SourceStream;
class URLRequestContextGetter;
} // namespace net
namespace content {
......@@ -38,13 +39,15 @@ class WebPackageLoader final : public network::mojom::URLLoaderClient,
using URLLoaderThrottlesGetter = base::RepeatingCallback<
std::vector<std::unique_ptr<content::URLLoaderThrottle>>()>;
WebPackageLoader(const network::ResourceResponseHead& original_response,
network::mojom::URLLoaderClientPtr forwarding_client,
network::mojom::URLLoaderClientEndpointsPtr endpoints,
url::Origin request_initiator,
uint32_t url_loader_options,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter);
WebPackageLoader(
const network::ResourceResponseHead& original_response,
network::mojom::URLLoaderClientPtr forwarding_client,
network::mojom::URLLoaderClientEndpointsPtr endpoints,
url::Origin request_initiator,
uint32_t url_loader_options,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter,
scoped_refptr<net::URLRequestContextGetter> request_context_getter);
~WebPackageLoader() override;
// network::mojom::URLLoaderClient implementation
......@@ -123,6 +126,7 @@ class WebPackageLoader final : public network::mojom::URLLoaderClient,
const uint32_t url_loader_options_;
scoped_refptr<SharedURLLoaderFactory> url_loader_factory_;
URLLoaderThrottlesGetter url_loader_throttles_getter_;
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
base::Optional<net::SSLInfo> ssl_info_;
......
......@@ -56,7 +56,8 @@ WebPackagePrefetchHandler::WebPackagePrefetchHandler(
web_package_loader_ = std::make_unique<WebPackageLoader>(
response, std::move(client), std::move(endpoints),
std::move(request_initiator), network::mojom::kURLLoadOptionNone,
std::move(url_loader_factory), loader_throttles_getter);
std::move(url_loader_factory), loader_throttles_getter,
request_context_getter);
}
WebPackagePrefetchHandler::~WebPackagePrefetchHandler() = default;
......
......@@ -14,6 +14,7 @@
#include "content/public/common/shared_url_loader_factory.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/public/cpp/resource_response.h"
#include "services/network/public/mojom/url_loader.mojom.h"
......@@ -30,11 +31,13 @@ WebPackageRequestHandler::WebPackageRequestHandler(
url::Origin request_initiator,
uint32_t url_loader_options,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter)
URLLoaderThrottlesGetter url_loader_throttles_getter,
scoped_refptr<net::URLRequestContextGetter> request_context_getter)
: request_initiator_(std::move(request_initiator)),
url_loader_options_(url_loader_options),
url_loader_factory_(url_loader_factory),
url_loader_throttles_getter_(std::move(url_loader_throttles_getter)),
request_context_getter_(std::move(request_context_getter)),
weak_factory_(this) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
}
......@@ -79,7 +82,8 @@ bool WebPackageRequestHandler::MaybeCreateLoaderForResponse(
web_package_loader_ = std::make_unique<WebPackageLoader>(
response, std::move(client), url_loader->Unbind(),
std::move(request_initiator_), url_loader_options_,
std::move(url_loader_factory_), std::move(url_loader_throttles_getter_));
std::move(url_loader_factory_), std::move(url_loader_throttles_getter_),
std::move(request_context_getter_));
return true;
}
......
......@@ -10,6 +10,10 @@
#include "content/public/common/resource_type.h"
#include "url/origin.h"
namespace net {
class URLRequestContextGetter;
} // namespace net
namespace content {
class SharedURLLoaderFactory;
......@@ -27,7 +31,8 @@ class WebPackageRequestHandler final : public URLLoaderRequestHandler {
url::Origin request_initiator,
uint32_t url_loader_options,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter);
URLLoaderThrottlesGetter url_loader_throttles_getter,
scoped_refptr<net::URLRequestContextGetter> request_context_getter);
~WebPackageRequestHandler() override;
// URLLoaderRequestHandler implementation
......@@ -53,6 +58,7 @@ class WebPackageRequestHandler final : public URLLoaderRequestHandler {
const uint32_t url_loader_options_;
scoped_refptr<SharedURLLoaderFactory> url_loader_factory_;
URLLoaderThrottlesGetter url_loader_throttles_getter_;
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
base::WeakPtrFactory<WebPackageRequestHandler> weak_factory_;
......
......@@ -7,9 +7,11 @@
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "content/browser/web_package/signed_exchange_handler.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_features.h"
......@@ -20,6 +22,9 @@
#include "content/public/test/test_navigation_throttle.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/shell/browser/shell.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "net/test/url_request/url_request_mock_http_job.h"
#include "net/url_request/url_request_filter.h"
......@@ -64,10 +69,12 @@ class WebPackageRequestHandlerBrowserTest
: public ContentBrowserTest,
public testing::WithParamInterface<bool> {
public:
WebPackageRequestHandlerBrowserTest() = default;
WebPackageRequestHandlerBrowserTest()
: mock_cert_verifier_(std::make_unique<net::MockCertVerifier>()){};
~WebPackageRequestHandlerBrowserTest() = default;
void SetUp() override {
SignedExchangeHandler::SetCertVerifierForTesting(mock_cert_verifier_.get());
if (is_network_service_enabled()) {
feature_list_.InitWithFeatures(
{features::kSignedHTTPExchange, network::features::kNetworkService},
......@@ -78,9 +85,20 @@ class WebPackageRequestHandlerBrowserTest
ContentBrowserTest::SetUp();
}
void TearDownOnMainThread() override { interceptor_.reset(); }
void TearDownOnMainThread() override {
interceptor_.reset();
SignedExchangeHandler::SetCertVerifierForTesting(nullptr);
}
protected:
static scoped_refptr<net::X509Certificate> LoadCertificate(
const std::string& cert_file) {
base::ScopedAllowBlockingForTesting allow_io;
return net::CreateCertificateChainFromFile(
net::GetTestCertsDirectory(), cert_file,
net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
}
void InstallUrlInterceptor(const GURL& url, const std::string& data_path) {
if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
if (!interceptor_) {
......@@ -97,6 +115,8 @@ class WebPackageRequestHandlerBrowserTest
}
}
std::unique_ptr<net::MockCertVerifier> mock_cert_verifier_;
private:
static std::string ReadFile(const std::string& data_path) {
base::ScopedAllowBlockingForTesting allow_io;
......@@ -153,6 +173,16 @@ IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, Simple) {
GURL("https://cert.example.org/cert.msg"),
"content/test/data/htxg/wildcard_example.org.public.pem.msg");
// Make the MockCertVerifier treat the certificate "wildcard.pem" as valid for
// "*.example.org".
scoped_refptr<net::X509Certificate> original_cert =
LoadCertificate("wildcard.pem");
net::CertVerifyResult dummy_result;
dummy_result.verified_cert = original_cert;
dummy_result.cert_status = net::OK;
mock_cert_verifier_->AddResultForCertAndHost(original_cert, "*.example.org",
dummy_result, net::OK);
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/htxg/test.example.org_test.htxg");
......@@ -160,6 +190,23 @@ IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, Simple) {
TitleWatcher title_watcher(shell()->web_contents(), title);
NavigateToURL(shell(), url);
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
NavigationEntry* entry =
shell()->web_contents()->GetController().GetVisibleEntry();
EXPECT_TRUE(entry->GetSSL().initialized);
EXPECT_FALSE(!!(entry->GetSSL().content_status &
SSLStatus::DISPLAYED_INSECURE_CONTENT));
ASSERT_TRUE(entry->GetSSL().certificate);
// "wildcard_example.org.public.pem.msg" is generated from "wildcard.pem". So
// the SHA256 of the certificates must match.
const net::SHA256HashValue fingerprint =
net::X509Certificate::CalculateFingerprint256(
entry->GetSSL().certificate->cert_buffer());
const net::SHA256HashValue original_fingerprint =
net::X509Certificate::CalculateFingerprint256(
original_cert->cert_buffer());
EXPECT_EQ(original_fingerprint, fingerprint);
}
IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, CertNotFound) {
......@@ -178,6 +225,60 @@ IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, CertNotFound) {
EXPECT_EQ(content::PAGE_TYPE_ERROR, entry->GetPageType());
}
IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest,
CertSha256Mismatch) {
// The certificate is for "127.0.0.1". And the SHA 256 hash of the certificate
// is different from the certSha256 of the signature in the htxg file. So the
// certification verification must fail.
InstallUrlInterceptor(GURL("https://cert.example.org/cert.msg"),
"content/test/data/htxg/127.0.0.1.public.pem.msg");
// Set the default result of MockCertVerifier to OK, to check that the
// verification of SignedExchange must fail even if the certificate is valid.
mock_cert_verifier_->set_default_result(net::OK);
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/htxg/test.example.org_test.htxg");
NavigationFailureObserver failure_observer(shell()->web_contents());
NavigateToURL(shell(), url);
EXPECT_TRUE(failure_observer.did_fail());
NavigationEntry* entry =
shell()->web_contents()->GetController().GetVisibleEntry();
EXPECT_EQ(content::PAGE_TYPE_ERROR, entry->GetPageType());
}
IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, VerifyCertFailure) {
// The certificate is for "*.example.com". But the request URL of the htxg
// file is "https://test.example.com/test/". So the certification verification
// must fail.
InstallUrlInterceptor(
GURL("https://cert.example.org/cert.msg"),
"content/test/data/htxg/wildcard_example.org.public.pem.msg");
// Make the MockCertVerifier treat the certificate "wildcard.pem" as valid for
// "*.example.org".
scoped_refptr<net::X509Certificate> original_cert =
LoadCertificate("wildcard.pem");
net::CertVerifyResult dummy_result;
dummy_result.verified_cert = original_cert;
dummy_result.cert_status = net::OK;
mock_cert_verifier_->AddResultForCertAndHost(original_cert, "*.example.org",
dummy_result, net::OK);
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL(
"/htxg/test.example.com_invalid_test.htxg");
NavigationFailureObserver failure_observer(shell()->web_contents());
NavigateToURL(shell(), url);
EXPECT_TRUE(failure_observer.did_fail());
NavigationEntry* entry =
shell()->web_contents()->GetController().GetVisibleEntry();
EXPECT_EQ(content::PAGE_TYPE_ERROR, entry->GetPageType());
}
INSTANTIATE_TEST_CASE_P(WebPackageRequestHandlerBrowserTest,
WebPackageRequestHandlerBrowserTest,
testing::Bool());
......
......@@ -21,10 +21,18 @@ sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \
../../../../net/data/ssl/certificates/wildcard.pem \
> /tmp/wildcard_example.org.public.pem
# Generate the certificate message file.
# Generate the certificate message file of "*.example.org".
gen-certurl \
/tmp/wildcard_example.org.public.pem > wildcard_example.org.public.pem.msg
# Get the public key of "127.0.0.1".
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \
../../../../net/data/ssl/certificates/ok_cert.pem \
> /tmp/127.0.0.1.public.pem
# Generate the certificate message file of"127.0.0.1".
gen-certurl /tmp/127.0.0.1.public.pem > 127.0.0.1.public.pem.msg
# Generate the signed exchange file.
gen-signedexchange \
-uri https://test.example.org/test/ \
......@@ -35,4 +43,16 @@ gen-signedexchange \
-validityUrl https://cert.example.org/resource.validity.msg \
-privateKey /tmp/wildcard_example.org.private.pem \
-o test.example.org_test.htxg \
-miRecordSize=100
-miRecordSize 100
# Generate the signed exchange file with invalid URL.
gen-signedexchange \
-uri https://test.example.com/test/ \
-status 200 \
-content test.html \
-certificate /tmp/wildcard_example.org.public.pem \
-certUrl https://cert.example.org/cert.msg \
-validityUrl https://cert.example.org/resource.validity.msg \
-privateKey /tmp/wildcard_example.org.private.pem \
-o test.example.com_invalid_test.htxg \
-miRecordSize 100
HTTP/1.1 200 OK
Content-Type: application/http-exchange+cbor
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