blob: 7237f8758747a1977c5c5acf3dc99b40d16f1fcb [file] [log] [blame]
// 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/format_macros.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h"
#include "components/cbor/reader.h"
#include "content/browser/web_package/signed_exchange_consts.h"
#include "content/browser/web_package/signed_exchange_utils.h"
#include "content/public/browser/content_browser_client.h"
#include "crypto/sha2.h"
#include "net/cert/asn1_util.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "services/network/public/cpp/network_switches.h"
namespace content {
namespace {
// https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html#cert-chain-format
std::unique_ptr<SignedExchangeCertificateChain> ParseCertChain(
base::span<const uint8_t> message,
SignedExchangeDevToolsProxy* devtools_proxy) {
cbor::Reader::DecoderError error;
base::Optional<cbor::Value> value = cbor::Reader::Read(message, &error);
if (!value.has_value()) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy,
base::StringPrintf("Failed to decode Value. CBOR error: %s",
cbor::Reader::ErrorCodeToString(error)));
return nullptr;
}
if (!value->is_array()) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy,
base::StringPrintf(
"Expected top-level Value to be an array. Actual type: %d",
static_cast<int>(value->type())));
return nullptr;
}
const cbor::Value::ArrayValue& top_level_array = value->GetArray();
// Expect at least 2 elements (magic string and main certificate).
if (top_level_array.size() < 2) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy,
base::StringPrintf(
"Expected top-level array to have at least 2 elements."
"Actual element count: %" PRIuS,
top_level_array.size()));
return nullptr;
}
if (!top_level_array[0].is_string() ||
top_level_array[0].GetString() != kCertChainCborMagic) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy,
"First element of cert chain CBOR does not match the magic string.");
return nullptr;
}
std::vector<base::StringPiece> der_certs;
der_certs.reserve(top_level_array.size() - 1);
std::string ocsp;
std::string sct;
for (size_t i = 1; i < top_level_array.size(); i++) {
if (!top_level_array[i].is_map()) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy,
base::StringPrintf(
"Expected certificate map, got non-map type at index %zu."
" Actual type: %d",
i, static_cast<int>(top_level_array[i].type())));
return nullptr;
}
const cbor::Value::MapValue& cert_map = top_level_array[i].GetMap();
// Step 1. Each cert value MUST be a DER-encoded X.509v3 certificate
// ([RFC5280]). Other key/value pairs in the same array item define
// properties of this certificate. [spec text]
auto cert_iter = cert_map.find(cbor::Value(kCertKey));
if (cert_iter == cert_map.end() || !cert_iter->second.is_bytestring()) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy,
base::StringPrintf(
"cert is not found or not a bytestring, at index %zu.", i));
return nullptr;
}
der_certs.push_back(cert_iter->second.GetBytestringAsString());
auto ocsp_iter = cert_map.find(cbor::Value(kOcspKey));
if (i == 1) {
// Step 2. The first certificate’s ocsp value MUST be a complete,
// DER-encoded OCSP response for that certificate (using the ASN.1 type
// OCSPResponse defined in [RFC6960]). ... [spec text]
if (ocsp_iter == cert_map.end() || !ocsp_iter->second.is_bytestring()) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy,
"ocsp is not a bytestring, or not found in the first cert map.");
return nullptr;
}
ocsp = ocsp_iter->second.GetBytestringAsString().as_string();
if (ocsp.empty()) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy, "ocsp must not be empty.");
return nullptr;
}
} else if (ocsp_iter != cert_map.end()) {
// Step 2. ... Subsequent certificates MUST NOT have an ocsp value. [spec
// text]
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy,
base::StringPrintf(
"ocsp value found in a subsequent cert map, at index %zu.", i));
return nullptr;
}
// Step 3. Each certificate’s sct value if any MUST be a
// SignedCertificateTimestampList for that certificate as defined by Section
// 3.3 of [RFC6962]. [spec text]
//
// We use SCTs only of the main certificate.
if (i == 1) {
auto sct_iter = cert_map.find(cbor::Value(kSctKey));
if (sct_iter != cert_map.end()) {
if (!sct_iter->second.is_bytestring()) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy, "sct is not a bytestring.");
return nullptr;
}
sct = sct_iter->second.GetBytestringAsString().as_string();
if (sct.empty()) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy, "sct must not be empty.");
return nullptr;
}
}
}
}
scoped_refptr<net::X509Certificate> cert =
net::X509Certificate::CreateFromDERCertChain(der_certs);
if (!cert) {
signed_exchange_utils::ReportErrorAndTraceEvent(
devtools_proxy, "X509Certificate::CreateFromDERCertChain failed.");
return nullptr;
}
return base::WrapUnique(new SignedExchangeCertificateChain(cert, ocsp, sct));
}
} // namespace
// static
std::unique_ptr<SignedExchangeCertificateChain>
SignedExchangeCertificateChain::Parse(
base::span<const uint8_t> cert_response_body,
SignedExchangeDevToolsProxy* devtools_proxy) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"),
"SignedExchangeCertificateChain::Parse");
return ParseCertChain(cert_response_body, devtools_proxy);
}
SignedExchangeCertificateChain::SignedExchangeCertificateChain(
scoped_refptr<net::X509Certificate> cert,
const std::string& ocsp,
const std::string& sct)
: cert_(cert), ocsp_(ocsp), sct_(sct) {
DCHECK(cert);
}
SignedExchangeCertificateChain::~SignedExchangeCertificateChain() = default;
bool SignedExchangeCertificateChain::ShouldIgnoreErrors() const {
return IgnoreErrorsSPKIList::ShouldIgnoreErrors(cert_);
}
std::unique_ptr<SignedExchangeCertificateChain::IgnoreErrorsSPKIList>&
SignedExchangeCertificateChain::IgnoreErrorsSPKIList::GetInstance() {
static base::NoDestructor<std::unique_ptr<IgnoreErrorsSPKIList>> instance(
std::make_unique<IgnoreErrorsSPKIList>(
*base::CommandLine::ForCurrentProcess()));
return *instance;
}
std::unique_ptr<SignedExchangeCertificateChain::IgnoreErrorsSPKIList>
SignedExchangeCertificateChain::IgnoreErrorsSPKIList::SetInstanceForTesting(
std::unique_ptr<IgnoreErrorsSPKIList> p) {
GetInstance().swap(p);
return p;
}
SignedExchangeCertificateChain::IgnoreErrorsSPKIList::IgnoreErrorsSPKIList(
const std::string& spki_list) {
Parse(spki_list);
}
SignedExchangeCertificateChain::IgnoreErrorsSPKIList::IgnoreErrorsSPKIList(
const base::CommandLine& command_line) {
if (!GetContentClient()->browser()->CanIgnoreCertificateErrorIfNeeded())
return;
Parse(command_line.GetSwitchValueASCII(
network::switches::kIgnoreCertificateErrorsSPKIList));
}
void SignedExchangeCertificateChain::IgnoreErrorsSPKIList::Parse(
const std::string& spki_list) {
hash_set_ =
network::IgnoreErrorsCertVerifier::MakeWhitelist(base::SplitString(
spki_list, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL));
}
SignedExchangeCertificateChain::IgnoreErrorsSPKIList::~IgnoreErrorsSPKIList() =
default;
// static
bool SignedExchangeCertificateChain::IgnoreErrorsSPKIList::ShouldIgnoreErrors(
scoped_refptr<net::X509Certificate> certificate) {
return GetInstance()->ShouldIgnoreErrorsInternal(certificate);
}
bool SignedExchangeCertificateChain::IgnoreErrorsSPKIList::
ShouldIgnoreErrorsInternal(
scoped_refptr<net::X509Certificate> certificate) {
if (hash_set_.empty())
return false;
base::StringPiece spki;
if (!net::asn1::ExtractSPKIFromDERCert(
net::x509_util::CryptoBufferAsStringPiece(certificate->cert_buffer()),
&spki)) {
return false;
}
net::SHA256HashValue hash;
crypto::SHA256HashString(spki, &hash, sizeof(net::SHA256HashValue));
return hash_set_.find(hash) != hash_set_.end();
}
} // namespace content