Commit 081d150e authored by Kunihiko Sakamoto's avatar Kunihiko Sakamoto Committed by Commit Bot

Add OCSP check for Signed Exchange

After this patch, SignedExchangeHandler starts accepting
"application/signed-exchange;v=b1" content-type in addition to v=b0.
(But Accept-Header still advertises only v=b0.)

For b1 signed exchanges, OCSP response from cert chain is passed to
CertVerifier::Verify(), and signed exchange without valid OCSP response
is rejected.

For now, b1 has minimal test coverage, but when we drop b0 support after
M68 branch cut, we'll be able to convert existing tests to b1.

This also makes IgnoreErrorsCertVerifier set OCSP results if request has
a non-empty ocsp response. This allows LayoutTests to work.

Bug: 815024
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_mojo
Change-Id: I611d68c1d4f26b1f97ea81f8f9f9b89ba3ad0d84
Reviewed-on: https://chromium-review.googlesource.com/1060933Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Reviewed-by: default avatarRyan Sleevi <rsleevi@chromium.org>
Reviewed-by: default avatarTsuyoshi Horo <horo@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Commit-Queue: Kunihiko Sakamoto <ksakamoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561709}
parent 2778edc4
...@@ -89,10 +89,10 @@ SignedExchangeHandler::SignedExchangeHandler( ...@@ -89,10 +89,10 @@ SignedExchangeHandler::SignedExchangeHandler(
TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"),
"SignedExchangeHandler::SignedExchangeHandler"); "SignedExchangeHandler::SignedExchangeHandler");
// Currently, only 'v=b0' is supported.
if (!SignedExchangeHeaderParser::GetVersionParamFromContentType(content_type, if (!SignedExchangeHeaderParser::GetVersionParamFromContentType(content_type,
&version_) || &version_) ||
version_ != SignedExchangeVersion::kB0) { (version_ != SignedExchangeVersion::kB0 &&
version_ != SignedExchangeVersion::kB1)) {
base::SequencedTaskRunnerHandle::Get()->PostTask( base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&SignedExchangeHandler::RunErrorCallback, FROM_HERE, base::BindOnce(&SignedExchangeHandler::RunErrorCallback,
weak_factory_.GetWeakPtr(), weak_factory_.GetWeakPtr(),
...@@ -101,7 +101,7 @@ SignedExchangeHandler::SignedExchangeHandler( ...@@ -101,7 +101,7 @@ SignedExchangeHandler::SignedExchangeHandler(
devtools_proxy_.get(), "SignedExchangeHandler::SignedExchangeHandler", devtools_proxy_.get(), "SignedExchangeHandler::SignedExchangeHandler",
base::StringPrintf("Unsupported version of the content type. Currentry " base::StringPrintf("Unsupported version of the content type. Currentry "
"content type must be " "content type must be "
"\"application/signed-exchange;v=b0\". But the " "\"application/signed-exchange;v={b0,b1}\". But the "
"response content type was \"%s\"", "response content type was \"%s\"",
content_type.c_str())); content_type.c_str()));
return; return;
...@@ -305,13 +305,10 @@ void SignedExchangeHandler::OnCertReceived( ...@@ -305,13 +305,10 @@ void SignedExchangeHandler::OnCertReceived(
net::CertVerifier* cert_verifier = g_cert_verifier_for_testing net::CertVerifier* cert_verifier = g_cert_verifier_for_testing
? g_cert_verifier_for_testing ? g_cert_verifier_for_testing
: request_context->cert_verifier(); : 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( int result = cert_verifier->Verify(
net::CertVerifier::RequestParams( net::CertVerifier::RequestParams(
unverified_cert_chain_->cert(), header_->request_url().host(), unverified_cert_chain_->cert(), header_->request_url().host(),
config.GetCertVerifyFlags(), std::string() /* ocsp_response */, config.GetCertVerifyFlags(), unverified_cert_chain_->ocsp(),
net::CertificateList()), net::CertificateList()),
net::SSLConfigService::GetCRLSet().get(), &cert_verify_result_, net::SSLConfigService::GetCRLSet().get(), &cert_verify_result_,
base::BindRepeating(&SignedExchangeHandler::OnCertVerifyComplete, base::BindRepeating(&SignedExchangeHandler::OnCertVerifyComplete,
...@@ -326,6 +323,27 @@ void SignedExchangeHandler::OnCertReceived( ...@@ -326,6 +323,27 @@ void SignedExchangeHandler::OnCertReceived(
"SignedExchangeHandler::OnCertReceived"); "SignedExchangeHandler::OnCertReceived");
} }
bool SignedExchangeHandler::CheckOCSPStatus(
const net::OCSPVerifyResult& ocsp_result) {
// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#cross-origin-trust
// Step 6.3 Validate that main-certificate has an ocsp property (Section 3.3)
// with a valid OCSP response whose lifetime (nextUpdate - thisUpdate) is less
// than 7 days ([RFC6960]). [spec text]
//
// OCSP verification is done in CertVerifier::Verify(), so we just check the
// result here.
// The b0 implementation checkpoint has no OCSP check.
if (version_ == SignedExchangeVersion::kB0)
return true;
if (ocsp_result.response_status != net::OCSPVerifyResult::PROVIDED ||
ocsp_result.revocation_status != net::OCSPRevocationStatus::GOOD)
return false;
return true;
}
void SignedExchangeHandler::OnCertVerifyComplete(int result) { void SignedExchangeHandler::OnCertVerifyComplete(int result) {
TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"),
"SignedExchangeHandler::OnCertVerifyComplete"); "SignedExchangeHandler::OnCertVerifyComplete");
...@@ -339,6 +357,17 @@ void SignedExchangeHandler::OnCertVerifyComplete(int result) { ...@@ -339,6 +357,17 @@ void SignedExchangeHandler::OnCertVerifyComplete(int result) {
return; return;
} }
if (!CheckOCSPStatus(cert_verify_result_.ocsp_result)) {
signed_exchange_utils::ReportErrorAndEndTraceEvent(
devtools_proxy_.get(), "SignedExchangeHandler::OnCertVerifyComplete",
base::StringPrintf(
"OCSP check failed. response status: %d, revocation status: %d",
cert_verify_result_.ocsp_result.response_status,
cert_verify_result_.ocsp_result.revocation_status));
RunErrorCallback(static_cast<net::Error>(net::ERR_FAILED));
return;
}
network::ResourceResponseHead response_head; network::ResourceResponseHead response_head;
response_head.headers = header_->BuildHttpResponseHeaders(); response_head.headers = header_->BuildHttpResponseHeaders();
response_head.headers->GetMimeTypeAndCharset(&response_head.mime_type, response_head.headers->GetMimeTypeAndCharset(&response_head.mime_type,
......
...@@ -28,6 +28,7 @@ class CertVerifyResult; ...@@ -28,6 +28,7 @@ class CertVerifyResult;
class DrainableIOBuffer; class DrainableIOBuffer;
class SourceStream; class SourceStream;
class URLRequestContextGetter; class URLRequestContextGetter;
struct OCSPVerifyResult;
} // namespace net } // namespace net
namespace network { namespace network {
...@@ -95,6 +96,7 @@ class CONTENT_EXPORT SignedExchangeHandler { ...@@ -95,6 +96,7 @@ class CONTENT_EXPORT SignedExchangeHandler {
void OnCertReceived( void OnCertReceived(
std::unique_ptr<SignedExchangeCertificateChain> cert_chain); std::unique_ptr<SignedExchangeCertificateChain> cert_chain);
void OnCertVerifyComplete(int result); void OnCertVerifyComplete(int result);
bool CheckOCSPStatus(const net::OCSPVerifyResult& ocsp_result);
ExchangeHeadersCallback headers_callback_; ExchangeHeadersCallback headers_callback_;
base::Optional<SignedExchangeVersion> version_; base::Optional<SignedExchangeVersion> version_;
......
...@@ -122,6 +122,12 @@ int IgnoreErrorsCertVerifier::Verify(const RequestParams& params, ...@@ -122,6 +122,12 @@ int IgnoreErrorsCertVerifier::Verify(const RequestParams& params,
std::transform(spki_fingerprints.begin(), spki_fingerprints.end(), std::transform(spki_fingerprints.begin(), spki_fingerprints.end(),
std::back_inserter(verify_result->public_key_hashes), std::back_inserter(verify_result->public_key_hashes),
[](const SHA256HashValue& v) { return HashValue(v); }); [](const SHA256HashValue& v) { return HashValue(v); });
if (!params.ocsp_response().empty()) {
verify_result->ocsp_result.response_status =
net::OCSPVerifyResult::PROVIDED;
verify_result->ocsp_result.revocation_status =
net::OCSPRevocationStatus::GOOD;
}
return net::OK; return net::OK;
} }
......
<!DOCTYPE html>
<title>Location of SignedHTTPExchange</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="./resources/htxg-util.js"></script>
<body>
<script>
promise_test(async (t) => {
await waitUntilDidFinishLoadForFrame;
// The timestamp of the test .sxg file is "May 15 2018 00:00 UTC" and valid
// until "May 22 2018 00:00 UTC".
await setSignedExchangeVerificationTime(new Date("May 15 2018 00:01 UTC"));
const event = await new Promise(async (resolve, reject) => {
// We can't catch the network error on iframe. So we use the timer.
t.step_timeout(() => reject('timeout'), 1000);
const frame =
await withIframe('resources/htxg-location.sxg', 'htxg_iframe');
const channel = new MessageChannel();
channel.port1.onmessage = resolve;
frame.contentWindow.postMessage(
{port: channel.port2}, '*', [channel.port2]);
});
assert_equals(event.data.location, 'https://www.127.0.0.1/test.html');
}, 'Location of SignedHTTPExchange');
</script>
</body>
...@@ -18,7 +18,7 @@ go get github.com/nyaxt/webpackage/go/signedexchange/cmd/gen-signedexchange ...@@ -18,7 +18,7 @@ go get github.com/nyaxt/webpackage/go/signedexchange/cmd/gen-signedexchange
# Generate the certificate message file of "127.0.0.1.pem". # Generate the certificate message file of "127.0.0.1.pem".
gen-certurl \ gen-certurl \
../../../../../../../blink/tools/blinkpy/thirdparty/wpt/certs/127.0.0.1.pem \ ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.pem \
> 127.0.0.1.pem.msg > 127.0.0.1.pem.msg
# Generate the signed exchange file. # Generate the signed exchange file.
...@@ -29,7 +29,7 @@ gen-signedexchange \ ...@@ -29,7 +29,7 @@ gen-signedexchange \
-certificate ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.pem \ -certificate ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.pem \
-certUrl http://localhost:8000/loading/htxg/resources/127.0.0.1.pem.msg \ -certUrl http://localhost:8000/loading/htxg/resources/127.0.0.1.pem.msg \
-validityUrl http://localhost:8000/loading/htxg/resources/resource.validity.msg \ -validityUrl http://localhost:8000/loading/htxg/resources/resource.validity.msg \
-privateKey ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.key\ -privateKey ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.key \
-date 2018-04-01T00:00:00Z \ -date 2018-04-01T00:00:00Z \
-expire 168h \ -expire 168h \
-o htxg-location.htxg \ -o htxg-location.htxg \
...@@ -43,9 +43,32 @@ gen-signedexchange \ ...@@ -43,9 +43,32 @@ gen-signedexchange \
-certificate ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.pem \ -certificate ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.pem \
-certUrl http://localhost:8000/loading/htxg/resources/not_found_cert.pem.msg \ -certUrl http://localhost:8000/loading/htxg/resources/not_found_cert.pem.msg \
-validityUrl http://localhost:8000/loading/htxg/resources/not_found_cert.validity.msg \ -validityUrl http://localhost:8000/loading/htxg/resources/not_found_cert.validity.msg \
-privateKey ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.key\ -privateKey ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.key \
-date 2018-04-01T00:00:00Z \ -date 2018-04-01T00:00:00Z \
-expire 168h \ -expire 168h \
-o htxg-cert-not-found.htxg \ -o htxg-cert-not-found.htxg \
-miRecordSize 100 -miRecordSize 100
# Install gen-certurl command from the original WICG repository [1].
# (Note: this overwrites gen-certurl fetched from [2] in the above.)
go get github.com/WICG/webpackage/go/signedexchange/cmd/gen-certurl
# Generate the certificate chain CBOR of "127.0.0.1.pem".
gen-certurl \
-pem ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.pem \
> 127.0.0.1.pem.cbor
# Generate the b1 version of signed exchange file.
gen-signedexchange \
-uri https://www.127.0.0.1/test.html \
-status 200 \
-content htxg-location.html \
-certificate ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.pem \
-certUrl http://localhost:8000/loading/htxg/resources/127.0.0.1.pem.cbor \
-validityUrl http://localhost:8000/loading/htxg/resources/resource.validity.msg \
-privateKey ../../../../../../../blink/tools/blinkpy/third_party/wpt/certs/127.0.0.1.key \
-date 2018-05-15T00:00:00Z \
-expire 168h \
-o htxg-location.sxg \
-miRecordSize 100
``` ```
main frame - didStartProvisionalLoadForFrame
main frame - didCommitLoadForFrame
main frame - didReceiveTitle: Location of SignedHTTPExchange
main frame - didFinishDocumentLoadForFrame
main frame - didHandleOnloadEventsForFrame
main frame - didFinishLoadForFrame
frame "htxg_iframe" - didReceiveTitle:
frame "htxg_iframe" - didStartProvisionalLoadForFrame
frame "htxg_iframe" - didFailProvisionalLoadWithError
This is a testharness.js-based test.
FAIL Location of SignedHTTPExchange promise_test: Unhandled rejection with value: "timeout"
Harness: the test ran to completion.
main frame - didStartProvisionalLoadForFrame
main frame - didCommitLoadForFrame
main frame - didReceiveTitle: Location of SignedHTTPExchange
main frame - didFinishDocumentLoadForFrame
main frame - didHandleOnloadEventsForFrame
main frame - didFinishLoadForFrame
frame "htxg_iframe" - didReceiveTitle:
frame "htxg_iframe" - didStartProvisionalLoadForFrame
frame "htxg_iframe" - didFailProvisionalLoadWithError
This is a testharness.js-based test.
FAIL Location of SignedHTTPExchange promise_test: Unhandled rejection with value: "timeout"
Harness: the test ran to completion.
...@@ -85,6 +85,7 @@ application/sgml ...@@ -85,6 +85,7 @@ application/sgml
application/sgml-open-catalog application/sgml-open-catalog
application/sieve application/sieve
application/signed-exchange;v=b0 htxg application/signed-exchange;v=b0 htxg
application/signed-exchange;v=b1 sxg
application/slate application/slate
application/smil smi smil application/smil smi smil
application/srgs gram application/srgs gram
......
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