Commit 4c759a7f authored by Kunihiko Sakamoto's avatar Kunihiko Sakamoto Committed by Commit Bot

Introduce SignedExchangeCertificateChain

In preparation to add support for the new cert format, this patch
introduces SignedExchangeCertificateChain class that contains all
information of a certificate chain. Certificate parsing code in
SignedExchangeCertFetcher is moved to this new class.

Pure refactoring, no behavior change.

Bug: 815024,815025
Change-Id: Iacb592279c9fef7afb40cb303ef81eebb4be34a3
Reviewed-on: https://chromium-review.googlesource.com/1002339Reviewed-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@{#549400}
parent 2bdfe08c
......@@ -1661,6 +1661,8 @@ jumbo_source_set("browser") {
"web_package/signed_exchange_cert_fetcher.h",
"web_package/signed_exchange_cert_fetcher_factory.cc",
"web_package/signed_exchange_cert_fetcher_factory.h",
"web_package/signed_exchange_certificate_chain.cc",
"web_package/signed_exchange_certificate_chain.h",
"web_package/signed_exchange_consts.h",
"web_package/signed_exchange_handler.cc",
"web_package/signed_exchange_handler.h",
......
......@@ -54,33 +54,6 @@ const net::NetworkTrafficAnnotationTag kCertFetcherTrafficAnnotation =
"type of request."
)");
bool ConsumeByte(base::StringPiece* data, uint8_t* out) {
if (data->empty())
return false;
*out = (*data)[0];
data->remove_prefix(1);
return true;
}
bool Consume2Bytes(base::StringPiece* data, uint16_t* out) {
if (data->size() < 2)
return false;
*out = (static_cast<uint8_t>((*data)[0]) << 8) |
static_cast<uint8_t>((*data)[1]);
data->remove_prefix(2);
return true;
}
bool Consume3Bytes(base::StringPiece* data, uint32_t* out) {
if (data->size() < 3)
return false;
*out = (static_cast<uint8_t>((*data)[0]) << 16) |
(static_cast<uint8_t>((*data)[1]) << 8) |
static_cast<uint8_t>((*data)[2]);
data->remove_prefix(3);
return true;
}
} // namespace
// static
......@@ -102,61 +75,6 @@ SignedExchangeCertFetcher::CreateAndStart(
return cert_fetcher;
}
// static
base::Optional<std::vector<base::StringPiece>>
SignedExchangeCertFetcher::GetCertChainFromMessage(base::StringPiece message) {
uint8_t cert_request_context_size = 0;
if (!ConsumeByte(&message, &cert_request_context_size)) {
DVLOG(1) << "Can't read certificate request request context size.";
return base::nullopt;
}
if (cert_request_context_size != 0) {
DVLOG(1) << "Invalid certificate request context size: "
<< static_cast<int>(cert_request_context_size);
return base::nullopt;
}
uint32_t cert_list_size = 0;
if (!Consume3Bytes(&message, &cert_list_size)) {
DVLOG(1) << "Can't read certificate list size.";
return base::nullopt;
}
if (cert_list_size != message.length()) {
DVLOG(1) << "Certificate list size error: cert_list_size=" << cert_list_size
<< " remaining=" << message.length();
return base::nullopt;
}
std::vector<base::StringPiece> certs;
while (!message.empty()) {
uint32_t cert_data_size = 0;
if (!Consume3Bytes(&message, &cert_data_size)) {
DVLOG(1) << "Can't read certificate data size.";
return base::nullopt;
}
if (message.length() < cert_data_size) {
DVLOG(1) << "Certificate data size error: cert_data_size="
<< cert_data_size << " remaining=" << message.length();
return base::nullopt;
}
certs.emplace_back(message.substr(0, cert_data_size));
message.remove_prefix(cert_data_size);
uint16_t extensions_size = 0;
if (!Consume2Bytes(&message, &extensions_size)) {
DVLOG(1) << "Can't read extensions size.";
return base::nullopt;
}
if (message.length() < extensions_size) {
DVLOG(1) << "Extensions size error: extensions_size=" << extensions_size
<< " remaining=" << message.length();
return base::nullopt;
}
message.remove_prefix(extensions_size);
}
return certs;
}
SignedExchangeCertFetcher::SignedExchangeCertFetcher(
scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory,
std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
......@@ -202,7 +120,7 @@ void SignedExchangeCertFetcher::Abort() {
body_.reset();
handle_watcher_ = nullptr;
body_string_.clear();
std::move(callback_).Run(scoped_refptr<net::X509Certificate>());
std::move(callback_).Run(nullptr);
}
void SignedExchangeCertFetcher::OnHandleReady(MojoResult result) {
......@@ -239,20 +157,18 @@ void SignedExchangeCertFetcher::OnDataComplete() {
url_loader_ = nullptr;
body_.reset();
handle_watcher_ = nullptr;
base::Optional<std::vector<base::StringPiece>> der_certs =
GetCertChainFromMessage(body_string_);
if (!der_certs) {
body_string_.clear();
std::move(callback_).Run(scoped_refptr<net::X509Certificate>());
std::unique_ptr<SignedExchangeCertificateChain> cert_chain =
SignedExchangeCertificateChain::Parse(body_string_);
body_string_.clear();
if (!cert_chain) {
std::move(callback_).Run(nullptr);
TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("loading"),
"SignedExchangeCertFetcher::OnDataComplete", "error",
"Failed to get certificate chain from message.");
return;
}
scoped_refptr<net::X509Certificate> cert =
net::X509Certificate::CreateFromDERCertChain(*der_certs);
body_string_.clear();
std::move(callback_).Run(std::move(cert));
std::move(callback_).Run(std::move(cert_chain));
TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("loading"),
"SignedExchangeCertFetcher::OnDataComplete");
}
......
......@@ -12,15 +12,11 @@
#include "base/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/strings/string_piece_forward.h"
#include "content/browser/web_package/signed_exchange_certificate_chain.h"
#include "content/common/content_export.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "url/origin.h"
namespace net {
class X509Certificate;
} // namespace net
namespace network {
class SharedURLLoaderFactory;
} // namespace network
......@@ -38,7 +34,7 @@ class CONTENT_EXPORT SignedExchangeCertFetcher
: public network::mojom::URLLoaderClient {
public:
using CertificateCallback =
base::OnceCallback<void(scoped_refptr<net::X509Certificate>)>;
base::OnceCallback<void(std::unique_ptr<SignedExchangeCertificateChain>)>;
// Starts fetching the certificate using a ThrottlingURLLoader created with
// the |shared_url_loader_factory| and the |throttles|. The |callback| will
......@@ -56,11 +52,6 @@ class CONTENT_EXPORT SignedExchangeCertFetcher
bool force_fetch,
CertificateCallback callback);
// Parses a TLS 1.3 Certificate message containing X.509v3 certificates and
// returns a vector of cert_data. Returns nullopt when failed to parse.
static base::Optional<std::vector<base::StringPiece>> GetCertChainFromMessage(
base::StringPiece message);
~SignedExchangeCertFetcher() override;
private:
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/web_package/signed_exchange_certificate_chain.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/web_package/signed_exchange_consts.h"
#include "content/public/common/content_switches.h"
#include "net/cert/x509_certificate.h"
namespace content {
namespace {
bool ConsumeByte(base::StringPiece* data, uint8_t* out) {
if (data->empty())
return false;
*out = (*data)[0];
data->remove_prefix(1);
return true;
}
bool Consume2Bytes(base::StringPiece* data, uint16_t* out) {
if (data->size() < 2)
return false;
*out = (static_cast<uint8_t>((*data)[0]) << 8) |
static_cast<uint8_t>((*data)[1]);
data->remove_prefix(2);
return true;
}
bool Consume3Bytes(base::StringPiece* data, uint32_t* out) {
if (data->size() < 3)
return false;
*out = (static_cast<uint8_t>((*data)[0]) << 16) |
(static_cast<uint8_t>((*data)[1]) << 8) |
static_cast<uint8_t>((*data)[2]);
data->remove_prefix(3);
return true;
}
} // namespace
// static
std::unique_ptr<SignedExchangeCertificateChain>
SignedExchangeCertificateChain::Parse(base::StringPiece cert_response_body) {
base::Optional<std::vector<base::StringPiece>> der_certs =
GetCertChainFromMessage(cert_response_body);
if (!der_certs)
return nullptr;
scoped_refptr<net::X509Certificate> cert =
net::X509Certificate::CreateFromDERCertChain(*der_certs);
if (!cert)
return nullptr;
return base::WrapUnique(new SignedExchangeCertificateChain(cert));
}
// static
base::Optional<std::vector<base::StringPiece>>
SignedExchangeCertificateChain::GetCertChainFromMessage(
base::StringPiece message) {
uint8_t cert_request_context_size = 0;
if (!ConsumeByte(&message, &cert_request_context_size)) {
DVLOG(1) << "Can't read certificate request request context size.";
return base::nullopt;
}
if (cert_request_context_size != 0) {
DVLOG(1) << "Invalid certificate request context size: "
<< static_cast<int>(cert_request_context_size);
return base::nullopt;
}
uint32_t cert_list_size = 0;
if (!Consume3Bytes(&message, &cert_list_size)) {
DVLOG(1) << "Can't read certificate list size.";
return base::nullopt;
}
if (cert_list_size != message.length()) {
DVLOG(1) << "Certificate list size error: cert_list_size=" << cert_list_size
<< " remaining=" << message.length();
return base::nullopt;
}
std::vector<base::StringPiece> certs;
while (!message.empty()) {
uint32_t cert_data_size = 0;
if (!Consume3Bytes(&message, &cert_data_size)) {
DVLOG(1) << "Can't read certificate data size.";
return base::nullopt;
}
if (message.length() < cert_data_size) {
DVLOG(1) << "Certificate data size error: cert_data_size="
<< cert_data_size << " remaining=" << message.length();
return base::nullopt;
}
certs.emplace_back(message.substr(0, cert_data_size));
message.remove_prefix(cert_data_size);
uint16_t extensions_size = 0;
if (!Consume2Bytes(&message, &extensions_size)) {
DVLOG(1) << "Can't read extensions size.";
return base::nullopt;
}
if (message.length() < extensions_size) {
DVLOG(1) << "Extensions size error: extensions_size=" << extensions_size
<< " remaining=" << message.length();
return base::nullopt;
}
message.remove_prefix(extensions_size);
}
return certs;
}
SignedExchangeCertificateChain::SignedExchangeCertificateChain(
scoped_refptr<net::X509Certificate> cert)
: cert_(cert) {
DCHECK(cert);
}
SignedExchangeCertificateChain::~SignedExchangeCertificateChain() = default;
} // namespace content
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_CERTIFICATE_CHAIN_H_
#define CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_CERTIFICATE_CHAIN_H_
#include <memory>
#include "base/containers/span.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/strings/string_piece_forward.h"
#include "content/common/content_export.h"
namespace net {
class X509Certificate;
} // namespace net
namespace content {
// SignedExchangeCertificateChain contains all information in signed exchange
// certificate chain.
// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#cert-chain-format
class CONTENT_EXPORT SignedExchangeCertificateChain {
public:
static std::unique_ptr<SignedExchangeCertificateChain> Parse(
base::StringPiece cert_response_body);
// Parses a TLS 1.3 Certificate message containing X.509v3 certificates and
// returns a vector of cert_data. Returns nullopt when failed to parse.
static base::Optional<std::vector<base::StringPiece>> GetCertChainFromMessage(
base::StringPiece message);
~SignedExchangeCertificateChain();
const scoped_refptr<net::X509Certificate>& cert() const { return cert_; }
private:
explicit SignedExchangeCertificateChain(
scoped_refptr<net::X509Certificate> cert);
scoped_refptr<net::X509Certificate> cert_;
};
} // namespace content
#endif // CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_CERTIFICATE_CHAIN_H_
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/web_package/signed_exchange_cert_fetcher.h" // nogncheck
#include "content/browser/web_package/signed_exchange_certificate_chain.h" // nogncheck
#include "base/strings/string_piece.h"
......@@ -10,7 +10,7 @@ namespace content {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
base::StringPiece input(reinterpret_cast<const char*>(data), size);
SignedExchangeCertFetcher::GetCertChainFromMessage(input);
SignedExchangeCertificateChain::GetCertChainFromMessage(input);
return 0;
}
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/web_package/signed_exchange_certificate_chain.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
base::Optional<std::vector<base::StringPiece>> GetCertChain(
const uint8_t* input,
size_t input_size) {
return SignedExchangeCertificateChain::GetCertChainFromMessage(
base::StringPiece(reinterpret_cast<const char*>(input), input_size));
}
} // namespace
TEST(SignedExchangeCertificateParseTest, OneCert) {
const uint8_t input[] = {
// clang-format off
0x00, // request context size
0x00, 0x00, 0x07, // certificate list size
0x00, 0x00, 0x02, // cert data size
0x11, 0x22, // cert data
0x00, 0x00, // extensions size
// clang-format on
};
base::Optional<std::vector<base::StringPiece>> certs =
GetCertChain(input, arraysize(input));
ASSERT_TRUE(certs);
ASSERT_EQ(1u, certs->size());
const uint8_t kExpected[] = {
// clang-format off
0x11, 0x22, // cert data
// clang-format on
};
EXPECT_THAT((*certs)[0],
testing::ElementsAreArray(kExpected, arraysize(kExpected)));
}
TEST(SignedExchangeCertificateParseTest, OneCertWithExtension) {
const uint8_t input[] = {
// clang-format off
0x00, // request context size
0x00, 0x00, 0x0A, // certificate list size
0x00, 0x00, 0x02, // cert data size
0x11, 0x22, // cert data
0x00, 0x03, // extensions size
0xE1, 0xE2, 0xE3, // extensions data
// clang-format on
};
base::Optional<std::vector<base::StringPiece>> certs =
GetCertChain(input, arraysize(input));
ASSERT_TRUE(certs);
ASSERT_EQ(1u, certs->size());
const uint8_t kExpected[] = {
// clang-format off
0x11, 0x22, // cert data
// clang-format on
};
EXPECT_THAT((*certs)[0],
testing::ElementsAreArray(kExpected, arraysize(kExpected)));
}
TEST(SignedExchangeCertificateParseTest, TwoCerts) {
const uint8_t input[] = {
// clang-format off
0x00, // request context size
0x00, 0x01, 0x13, // certificate list size
0x00, 0x01, 0x04, // cert data size
// cert data
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x00, // extensions size
0x00, 0x00, 0x05, // cert data size
0x33, 0x44, 0x55, 0x66, 0x77, // cert data
0x00, 0x00, // extensions size
// clang-format on
};
const uint8_t kExpected1[] = {
// clang-format off
// cert data
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
// clang-format on
};
const uint8_t kExpected2[] = {
// clang-format off
0x33, 0x44, 0x55, 0x66, 0x77, // cert data
// clang-format on
};
base::Optional<std::vector<base::StringPiece>> certs =
GetCertChain(input, sizeof(input));
ASSERT_TRUE(certs);
ASSERT_EQ(2u, certs->size());
EXPECT_THAT((*certs)[0],
testing::ElementsAreArray(kExpected1, arraysize(kExpected1)));
EXPECT_THAT((*certs)[1],
testing::ElementsAreArray(kExpected2, arraysize(kExpected2)));
}
TEST(SignedExchangeCertificateParseTest, Empty) {
EXPECT_FALSE(GetCertChain(nullptr, 0));
}
TEST(SignedExchangeCertificateParseTest, InvalidRequestContextSize) {
const uint8_t input[] = {
// clang-format off
0x01, // request context size: must be zero
0x20, // request context
// clang-format on
};
EXPECT_FALSE(GetCertChain(input, arraysize(input)));
}
TEST(SignedExchangeCertificateParseTest, CanNotReadCertListSize1) {
const uint8_t input[] = {
// clang-format off
0x00, // request context size
0x01, // certificate list size: must be 3 bytes
// clang-format on
};
EXPECT_FALSE(GetCertChain(input, arraysize(input)));
}
TEST(SignedExchangeCertificateParseTest, CanNotReadCertListSize2) {
const uint8_t input[] = {
// clang-format off
0x00, // request context size
0x00, 0x01, // certificate list size: must be 3 bytes
// clang-format on
};
EXPECT_FALSE(GetCertChain(input, arraysize(input)));
}
TEST(SignedExchangeCertificateParseTest, CertListSizeError) {
const uint8_t input[] = {
// clang-format off
0x00, // request context size
0x00, 0x01, 0x01, // certificate list size: 257 (This must be 7)
0x00, 0x00, 0x02, // cert data size
0x11, 0x22, // cert data
0x00, 0x00, // extensions size
// clang-format on
};
EXPECT_FALSE(GetCertChain(input, arraysize(input)));
}
TEST(SignedExchangeCertificateParseTest, CanNotReadCertDataSize) {
const uint8_t input[] = {
// clang-format off
0x00, // request context size
0x00, 0x00, 0x02, // certificate list size
0x00, 0x01, // cert data size: must be 3 bytes
// clang-format on
};
EXPECT_FALSE(GetCertChain(input, arraysize(input)));
}
TEST(SignedExchangeCertificateParseTest, CertDataSizeError) {
const uint8_t input[] = {
// clang-format off
0x00, // request context size
0x00, 0x00, 0x04, // certificate list size
0x00, 0x00, 0x02, // cert data size
0x11, // cert data: Need 2 bytes
// clang-format on
};
EXPECT_FALSE(GetCertChain(input, arraysize(input)));
}
TEST(SignedExchangeCertificateParseTest, CanNotReadExtensionsSize) {
const uint8_t input[] = {
// clang-format off
0x00, // request context size
0x00, 0x00, 0x06, // certificate list size
0x00, 0x00, 0x02, // cert data size
0x11, 0x22, // cert data
0x00, // extensions size : must be 2 bytes
// clang-format on
};
EXPECT_FALSE(GetCertChain(input, arraysize(input)));
}
TEST(SignedExchangeCertificateParseTest, ExtensionsSizeError) {
const uint8_t input[] = {
// clang-format off
0x00, // request context size
0x00, 0x00, 0x07, // certificate list size
0x00, 0x00, 0x02, // cert data size
0x11, 0x22, // cert data
0x00, 0x01, // extensions size
// clang-format on
};
EXPECT_FALSE(GetCertChain(input, arraysize(input)));
}
} // namespace content
......@@ -9,6 +9,7 @@
#include "base/trace_event/trace_event.h"
#include "content/browser/loader/merkle_integrity_source_stream.h"
#include "content/browser/web_package/signed_exchange_cert_fetcher_factory.h"
#include "content/browser/web_package/signed_exchange_certificate_chain.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_signature_verifier.h"
......@@ -250,11 +251,11 @@ void SignedExchangeHandler::RunErrorCallback(net::Error error) {
}
void SignedExchangeHandler::OnCertReceived(
scoped_refptr<net::X509Certificate> cert) {
std::unique_ptr<SignedExchangeCertificateChain> cert_chain) {
TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"),
"SignedExchangeHandler::OnCertReceived");
DCHECK_EQ(state_, State::kFetchingCertificate);
if (!cert) {
if (!cert_chain) {
RunErrorCallback(net::ERR_FAILED);
TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("loading"),
"SignedExchangeHandler::OnCertReceived", "error",
......@@ -262,7 +263,7 @@ void SignedExchangeHandler::OnCertReceived(
return;
}
if (SignedExchangeSignatureVerifier::Verify(*header_, cert,
if (SignedExchangeSignatureVerifier::Verify(*header_, cert_chain->cert(),
GetVerificationTime()) !=
SignedExchangeSignatureVerifier::Result::kSuccess) {
RunErrorCallback(net::ERR_FAILED);
......@@ -281,7 +282,7 @@ void SignedExchangeHandler::OnCertReceived(
return;
}
unverified_cert_ = cert;
unverified_cert_chain_ = std::move(cert_chain);
net::SSLConfig config;
request_context->ssl_config_service()->GetSSLConfig(&config);
......@@ -294,7 +295,7 @@ void SignedExchangeHandler::OnCertReceived(
// (nextUpdate - thisUpdate) is less than 7 days.
int result = cert_verifier->Verify(
net::CertVerifier::RequestParams(
unverified_cert_, header_->request_url().host(),
unverified_cert_chain_->cert(), header_->request_url().host(),
config.GetCertVerifyFlags(), std::string() /* ocsp_response */,
net::CertificateList()),
net::SSLConfigService::GetCRLSet().get(), &cert_verify_result_,
......@@ -350,7 +351,7 @@ void SignedExchangeHandler::OnCertVerifyComplete(int result) {
net::SSLInfo ssl_info;
ssl_info.cert = cert_verify_result_.verified_cert;
ssl_info.unverified_cert = unverified_cert_;
ssl_info.unverified_cert = unverified_cert_chain_->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;
......
......@@ -27,7 +27,6 @@ class CertVerifyResult;
class DrainableIOBuffer;
class SourceStream;
class URLRequestContextGetter;
class X509Certificate;
} // namespace net
namespace network {
......@@ -38,6 +37,7 @@ namespace content {
class SignedExchangeCertFetcher;
class SignedExchangeCertFetcherFactory;
class SignedExchangeCertificateChain;
// IMPORTANT: Currenly SignedExchangeHandler partially implements the verifying
// logic.
......@@ -90,7 +90,7 @@ class CONTENT_EXPORT SignedExchangeHandler {
void RunErrorCallback(net::Error);
void OnCertReceived(
scoped_refptr<net::X509Certificate> cert);
std::unique_ptr<SignedExchangeCertificateChain> cert_chain);
void OnCertVerifyComplete(int result);
ExchangeHeadersCallback headers_callback_;
......@@ -110,7 +110,7 @@ class CONTENT_EXPORT SignedExchangeHandler {
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
scoped_refptr<net::X509Certificate> unverified_cert_;
std::unique_ptr<SignedExchangeCertificateChain> unverified_cert_chain_;
// CertVerifyResult must be freed after the Request has been destructed.
// So |cert_verify_result_| must be written before |cert_verifier_request_|.
......
......@@ -61,18 +61,11 @@ class MockSignedExchangeCertFetcherFactory
SignedExchangeCertFetcher::CertificateCallback callback) override {
EXPECT_EQ(cert_url, expected_cert_url_);
scoped_refptr<net::X509Certificate> cert;
base::Optional<std::vector<base::StringPiece>> der_certs =
SignedExchangeCertFetcher::GetCertChainFromMessage(cert_str_);
EXPECT_TRUE(der_certs);
if (der_certs) {
cert = net::X509Certificate::CreateFromDERCertChain(*der_certs);
EXPECT_TRUE(cert);
}
auto cert_chain = SignedExchangeCertificateChain::Parse(cert_str_);
EXPECT_TRUE(cert_chain);
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), cert));
FROM_HERE, base::BindOnce(std::move(callback), std::move(cert_chain)));
return nullptr;
}
......
......@@ -1501,6 +1501,7 @@ test("content_unittests") {
"../browser/web_contents/web_drag_dest_mac_unittest.mm",
"../browser/web_contents/web_drag_source_mac_unittest.mm",
"../browser/web_package/signed_exchange_cert_fetcher_unittest.cc",
"../browser/web_package/signed_exchange_certificate_chain_unittest.cc",
"../browser/web_package/signed_exchange_handler_unittest.cc",
"../browser/web_package/signed_exchange_header_parser_unittest.cc",
"../browser/web_package/signed_exchange_header_unittest.cc",
......
......@@ -122,14 +122,14 @@ fuzzer_test("signed_exchange_header_parser_fuzzer") {
"//content/test/data/fuzzer_corpus/signed_exchange_header_parser_data/"
}
fuzzer_test("signed_exchange_cert_fetcher_parser_fuzzer") {
fuzzer_test("signed_exchange_certificate_chain_fuzzer") {
sources = [
"signed_exchange_cert_fetcher_parser_fuzzer.cc",
"../../browser/web_package/signed_exchange_certificate_chain_fuzzer.cc",
]
deps = [
"//content/test:test_support",
]
seed_corpus = "//content/test/data/fuzzer_corpus/signed_exchange_cert_fetcher_parser_data/"
seed_corpus = "//content/test/data/fuzzer_corpus/signed_exchange_certificate_chain_data/"
}
fuzzer_test("appcache_manifest_parser_fuzzer") {
......
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