summaryrefslogtreecommitdiff
path: root/chromium/net/cert/pki
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2022-09-29 16:16:15 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2022-11-09 10:04:06 +0000
commita95a7417ad456115a1ef2da4bb8320531c0821f1 (patch)
treeedcd59279e486d2fd4a8f88a7ed025bcf925c6e6 /chromium/net/cert/pki
parent33fc33aa94d4add0878ec30dc818e34e1dd3cc2a (diff)
downloadqtwebengine-chromium-a95a7417ad456115a1ef2da4bb8320531c0821f1.tar.gz
BASELINE: Update Chromium to 106.0.5249.126
Change-Id: Ib0bb21c437a7d1686e21c33f2d329f2ac425b7ab Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/438936 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/cert/pki')
-rw-r--r--chromium/net/cert/pki/README.md11
-rw-r--r--chromium/net/cert/pki/cert_error_id.cc14
-rw-r--r--chromium/net/cert/pki/cert_error_id.h37
-rw-r--r--chromium/net/cert/pki/cert_error_params.cc140
-rw-r--r--chromium/net/cert/pki/cert_error_params.h67
-rw-r--r--chromium/net/cert/pki/cert_errors.cc201
-rw-r--r--chromium/net/cert/pki/cert_errors.h168
-rw-r--r--chromium/net/cert/pki/cert_issuer_source.h67
-rw-r--r--chromium/net/cert/pki/cert_issuer_source_static.cc36
-rw-r--r--chromium/net/cert/pki/cert_issuer_source_static.h50
-rw-r--r--chromium/net/cert/pki/cert_issuer_source_static_unittest.cc37
-rw-r--r--chromium/net/cert/pki/cert_issuer_source_sync_unittest.h216
-rw-r--r--chromium/net/cert/pki/certificate_policies.cc372
-rw-r--r--chromium/net/cert/pki/certificate_policies.h135
-rw-r--r--chromium/net/cert/pki/certificate_policies_unittest.cc313
-rw-r--r--chromium/net/cert/pki/common_cert_errors.cc66
-rw-r--r--chromium/net/cert/pki/common_cert_errors.h145
-rw-r--r--chromium/net/cert/pki/crl.cc623
-rw-r--r--chromium/net/cert/pki/crl.h224
-rw-r--r--chromium/net/cert/pki/extended_key_usage.cc40
-rw-r--r--chromium/net/cert/pki/extended_key_usage.h88
-rw-r--r--chromium/net/cert/pki/extended_key_usage_unittest.cc166
-rw-r--r--chromium/net/cert/pki/general_names.cc236
-rw-r--r--chromium/net/cert/pki/general_names.h124
-rw-r--r--chromium/net/cert/pki/name_constraints.cc428
-rw-r--r--chromium/net/cert/pki/name_constraints.h100
-rw-r--r--chromium/net/cert/pki/name_constraints_unittest.cc1221
-rw-r--r--chromium/net/cert/pki/nist_pkits_unittest.cc100
-rw-r--r--chromium/net/cert/pki/nist_pkits_unittest.h157
-rw-r--r--chromium/net/cert/pki/ocsp.cc1037
-rw-r--r--chromium/net/cert/pki/ocsp.h328
-rw-r--r--chromium/net/cert/pki/ocsp_parse_ocsp_cert_id_fuzzer.cc17
-rw-r--r--chromium/net/cert/pki/ocsp_parse_ocsp_response_data_fuzzer.cc17
-rw-r--r--chromium/net/cert/pki/ocsp_parse_ocsp_response_fuzzer.cc17
-rw-r--r--chromium/net/cert/pki/ocsp_parse_ocsp_single_response_fuzzer.cc17
-rw-r--r--chromium/net/cert/pki/ocsp_unittest.cc239
-rw-r--r--chromium/net/cert/pki/parse_certificate.cc955
-rw-r--r--chromium/net/cert/pki/parse_certificate.h621
-rw-r--r--chromium/net/cert/pki/parse_certificate_fuzzer.cc24
-rw-r--r--chromium/net/cert/pki/parse_certificate_unittest.cc1176
-rw-r--r--chromium/net/cert/pki/parse_name.cc222
-rw-r--r--chromium/net/cert/pki/parse_name.h157
-rw-r--r--chromium/net/cert/pki/parse_name_unittest.cc361
-rw-r--r--chromium/net/cert/pki/parsed_certificate.cc302
-rw-r--r--chromium/net/cert/pki/parsed_certificate.h335
-rw-r--r--chromium/net/cert/pki/parsed_certificate_unittest.cc586
-rw-r--r--chromium/net/cert/pki/path_builder.cc851
-rw-r--r--chromium/net/cert/pki/path_builder.h247
-rw-r--r--chromium/net/cert/pki/path_builder_pkits_unittest.cc300
-rw-r--r--chromium/net/cert/pki/path_builder_unittest.cc2555
-rw-r--r--chromium/net/cert/pki/path_builder_verify_certificate_chain_unittest.cc67
-rw-r--r--chromium/net/cert/pki/revocation_util.cc38
-rw-r--r--chromium/net/cert/pki/revocation_util.h33
-rw-r--r--chromium/net/cert/pki/signature_algorithm.cc487
-rw-r--r--chromium/net/cert/pki/signature_algorithm.h95
-rw-r--r--chromium/net/cert/pki/signature_algorithm_unittest.cc1468
-rw-r--r--chromium/net/cert/pki/simple_path_builder_delegate.cc126
-rw-r--r--chromium/net/cert/pki/simple_path_builder_delegate.h65
-rw-r--r--chromium/net/cert/pki/simple_path_builder_delegate_unittest.cc109
-rw-r--r--chromium/net/cert/pki/test_helpers.cc377
-rw-r--r--chromium/net/cert/pki/test_helpers.h155
-rw-r--r--chromium/net/cert/pki/trust_store.cc93
-rw-r--r--chromium/net/cert/pki/trust_store.h90
-rw-r--r--chromium/net/cert/pki/trust_store_collection.cc46
-rw-r--r--chromium/net/cert/pki/trust_store_collection.h44
-rw-r--r--chromium/net/cert/pki/trust_store_collection_unittest.cc182
-rw-r--r--chromium/net/cert/pki/trust_store_in_memory.cc92
-rw-r--r--chromium/net/cert/pki/trust_store_in_memory.h91
-rw-r--r--chromium/net/cert/pki/verify_certificate_chain.cc1357
-rw-r--r--chromium/net/cert/pki/verify_certificate_chain.h248
-rw-r--r--chromium/net/cert/pki/verify_certificate_chain_pkits_unittest.cc129
-rw-r--r--chromium/net/cert/pki/verify_certificate_chain_typed_unittest.h228
-rw-r--r--chromium/net/cert/pki/verify_certificate_chain_unittest.cc42
-rw-r--r--chromium/net/cert/pki/verify_name_match.cc418
-rw-r--r--chromium/net/cert/pki/verify_name_match.h57
-rw-r--r--chromium/net/cert/pki/verify_name_match_fuzzer.cc34
-rw-r--r--chromium/net/cert/pki/verify_name_match_normalizename_fuzzer.cc26
-rw-r--r--chromium/net/cert/pki/verify_name_match_unittest.cc612
-rw-r--r--chromium/net/cert/pki/verify_name_match_verifynameinsubtree_fuzzer.cc35
-rw-r--r--chromium/net/cert/pki/verify_signed_data.cc217
-rw-r--r--chromium/net/cert/pki/verify_signed_data.h49
-rw-r--r--chromium/net/cert/pki/verify_signed_data_unittest.cc190
82 files changed, 23246 insertions, 0 deletions
diff --git a/chromium/net/cert/pki/README.md b/chromium/net/cert/pki/README.md
new file mode 100644
index 00000000000..9e936b61b99
--- /dev/null
+++ b/chromium/net/cert/pki/README.md
@@ -0,0 +1,11 @@
+# Web PKI Certificate path building and verification
+
+This directory contains the core internal code used for path building
+and verifying certificates. This is existing temporarily in this
+directory while changes are made to remove chromium specific
+dependencies. This code will be moving to it's own separate library
+in boringssl (Issue 1322914).
+
+Please do not depend on this directory continuing to exist, and please
+try to avoid adding dependencies on anything in this directory from
+outside of //net/cert.
diff --git a/chromium/net/cert/pki/cert_error_id.cc b/chromium/net/cert/pki/cert_error_id.cc
new file mode 100644
index 00000000000..793b92ffb2c
--- /dev/null
+++ b/chromium/net/cert/pki/cert_error_id.cc
@@ -0,0 +1,14 @@
+// Copyright 2016 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 "net/cert/pki/cert_error_id.h"
+
+namespace net {
+
+const char* CertErrorIdToDebugString(CertErrorId id) {
+ // The CertErrorId is simply a pointer for a C-string literal.
+ return reinterpret_cast<const char*>(id);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/cert_error_id.h b/chromium/net/cert/pki/cert_error_id.h
new file mode 100644
index 00000000000..1c0e4ec947b
--- /dev/null
+++ b/chromium/net/cert/pki/cert_error_id.h
@@ -0,0 +1,37 @@
+// Copyright 2016 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 NET_CERT_PKI_CERT_ERROR_ID_H_
+#define NET_CERT_PKI_CERT_ERROR_ID_H_
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Each "class" of certificate error/warning has its own unique ID. This is
+// essentially like an error code, however the value is not stable. Under the
+// hood these IDs are pointers and use the process's address space to ensure
+// uniqueness.
+//
+// Equality of CertErrorId can be done using the == operator.
+//
+// To define new error IDs use the macro DEFINE_CERT_ERROR_ID().
+using CertErrorId = const void*;
+
+// DEFINE_CERT_ERROR_ID() creates a CertErrorId given a non-null C-string
+// literal. The string should be a textual name for the error which will appear
+// when pretty-printing errors for debugging. It should be ASCII.
+//
+// TODO(crbug.com/634443): Implement this -- add magic to ensure that storage
+// of identical strings isn't pool.
+#define DEFINE_CERT_ERROR_ID(name, c_str_literal) \
+ const CertErrorId name = c_str_literal
+
+// Returns a debug string for a CertErrorId. In practice this returns the
+// string literal given to DEFINE_CERT_ERROR_ID(), which is human-readable.
+NET_EXPORT const char* CertErrorIdToDebugString(CertErrorId id);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_CERT_ERROR_ID_H_
diff --git a/chromium/net/cert/pki/cert_error_params.cc b/chromium/net/cert/pki/cert_error_params.cc
new file mode 100644
index 00000000000..0d4f2b61d83
--- /dev/null
+++ b/chromium/net/cert/pki/cert_error_params.cc
@@ -0,0 +1,140 @@
+// Copyright 2016 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 "net/cert/pki/cert_error_params.h"
+
+#include <memory>
+
+#include "base/check.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/der/input.h"
+
+namespace net {
+
+namespace {
+
+// Parameters subclass for describing (and pretty-printing) 1 or 2 DER
+// blobs. It makes a copy of the der::Inputs.
+class CertErrorParams2Der : public CertErrorParams {
+ public:
+ CertErrorParams2Der(const char* name1,
+ const der::Input& der1,
+ const char* name2,
+ const der::Input& der2)
+ : name1_(name1),
+ der1_(der1.AsString()),
+ name2_(name2),
+ der2_(der2.AsString()) {}
+
+ CertErrorParams2Der(const CertErrorParams2Der&) = delete;
+ CertErrorParams2Der& operator=(const CertErrorParams2Der&) = delete;
+
+ std::string ToDebugString() const override {
+ std::string result;
+ AppendDer(name1_, der1_, &result);
+ if (name2_) {
+ result += "\n";
+ AppendDer(name2_, der2_, &result);
+ }
+ return result;
+ }
+
+ private:
+ static void AppendDer(const char* name,
+ const std::string& der,
+ std::string* out) {
+ *out += name;
+ *out += ": " + base::HexEncode(der.data(), der.size());
+ }
+
+ const char* name1_;
+ std::string der1_;
+
+ const char* name2_;
+ std::string der2_;
+};
+
+// Parameters subclass for describing (and pretty-printing) a single size_t.
+class CertErrorParams1SizeT : public CertErrorParams {
+ public:
+ CertErrorParams1SizeT(const char* name, size_t value)
+ : name_(name), value_(value) {}
+
+ CertErrorParams1SizeT(const CertErrorParams1SizeT&) = delete;
+ CertErrorParams1SizeT& operator=(const CertErrorParams1SizeT&) = delete;
+
+ std::string ToDebugString() const override {
+ return name_ + std::string(": ") + base::NumberToString(value_);
+ }
+
+ private:
+ const char* name_;
+ size_t value_;
+};
+
+// Parameters subclass for describing (and pretty-printing) two size_t
+// values.
+class CertErrorParams2SizeT : public CertErrorParams {
+ public:
+ CertErrorParams2SizeT(const char* name1,
+ size_t value1,
+ const char* name2,
+ size_t value2)
+ : name1_(name1), value1_(value1), name2_(name2), value2_(value2) {}
+
+ CertErrorParams2SizeT(const CertErrorParams2SizeT&) = delete;
+ CertErrorParams2SizeT& operator=(const CertErrorParams2SizeT&) = delete;
+
+ std::string ToDebugString() const override {
+ return name1_ + std::string(": ") + base::NumberToString(value1_) + "\n" +
+ name2_ + std::string(": ") + base::NumberToString(value2_);
+ }
+
+ private:
+ const char* name1_;
+ size_t value1_;
+ const char* name2_;
+ size_t value2_;
+};
+
+} // namespace
+
+CertErrorParams::CertErrorParams() = default;
+CertErrorParams::~CertErrorParams() = default;
+
+std::unique_ptr<CertErrorParams> CreateCertErrorParams1Der(
+ const char* name,
+ const der::Input& der) {
+ DCHECK(name);
+ return std::make_unique<CertErrorParams2Der>(name, der, nullptr,
+ der::Input());
+}
+
+std::unique_ptr<CertErrorParams> CreateCertErrorParams2Der(
+ const char* name1,
+ const der::Input& der1,
+ const char* name2,
+ const der::Input& der2) {
+ DCHECK(name1);
+ DCHECK(name2);
+ return std::make_unique<CertErrorParams2Der>(name1, der1, name2, der2);
+}
+
+std::unique_ptr<CertErrorParams> CreateCertErrorParams1SizeT(const char* name,
+ size_t value) {
+ DCHECK(name);
+ return std::make_unique<CertErrorParams1SizeT>(name, value);
+}
+
+NET_EXPORT std::unique_ptr<CertErrorParams> CreateCertErrorParams2SizeT(
+ const char* name1,
+ size_t value1,
+ const char* name2,
+ size_t value2) {
+ DCHECK(name1);
+ DCHECK(name2);
+ return std::make_unique<CertErrorParams2SizeT>(name1, value1, name2, value2);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/cert_error_params.h b/chromium/net/cert/pki/cert_error_params.h
new file mode 100644
index 00000000000..b00d0f2e8a4
--- /dev/null
+++ b/chromium/net/cert/pki/cert_error_params.h
@@ -0,0 +1,67 @@
+// Copyright 2016 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 NET_CERT_PKI_CERT_ERROR_PARAMS_H_
+#define NET_CERT_PKI_CERT_ERROR_PARAMS_H_
+
+#include <memory>
+#include <string>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+namespace der {
+class Input;
+}
+
+// CertErrorParams is a base class for describing extra parameters attached to
+// a CertErrorNode.
+//
+// An example use for parameters is to identify the OID for an unconsumed
+// critical extension. This parameter could then be pretty printed when
+// diagnosing the error.
+class NET_EXPORT CertErrorParams {
+ public:
+ CertErrorParams();
+
+ CertErrorParams(const CertErrorParams&) = delete;
+ CertErrorParams& operator=(const CertErrorParams&) = delete;
+
+ virtual ~CertErrorParams();
+
+ // Creates a representation of this parameter as a string, which may be
+ // used for pretty printing the error.
+ virtual std::string ToDebugString() const = 0;
+};
+
+// Creates a parameter object that holds a copy of |der|, and names it |name|
+// in debug string outputs.
+NET_EXPORT std::unique_ptr<CertErrorParams> CreateCertErrorParams1Der(
+ const char* name,
+ const der::Input& der);
+
+// Same as CreateCertErrorParams1Der() but has a second DER blob.
+NET_EXPORT std::unique_ptr<CertErrorParams> CreateCertErrorParams2Der(
+ const char* name1,
+ const der::Input& der1,
+ const char* name2,
+ const der::Input& der2);
+
+// Creates a parameter object that holds a single size_t value. |name| is used
+// when pretty-printing the parameters.
+NET_EXPORT std::unique_ptr<CertErrorParams> CreateCertErrorParams1SizeT(
+ const char* name,
+ size_t value);
+
+// Same as CreateCertErrorParams1SizeT() but has a second size_t.
+NET_EXPORT std::unique_ptr<CertErrorParams> CreateCertErrorParams2SizeT(
+ const char* name1,
+ size_t value1,
+ const char* name2,
+ size_t value2);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_CERT_ERROR_PARAMS_H_
diff --git a/chromium/net/cert/pki/cert_errors.cc b/chromium/net/cert/pki/cert_errors.cc
new file mode 100644
index 00000000000..833fb1d3638
--- /dev/null
+++ b/chromium/net/cert/pki/cert_errors.cc
@@ -0,0 +1,201 @@
+// Copyright 2016 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 "net/cert/pki/cert_errors.h"
+
+#include "net/cert/pki/cert_error_params.h"
+#include "net/cert/pki/parse_name.h"
+#include "net/cert/pki/parsed_certificate.h"
+
+#include <sstream>
+
+namespace net {
+
+namespace {
+
+void AppendLinesWithIndentation(const std::string& text,
+ const std::string& indentation,
+ std::string* out) {
+ std::istringstream stream(text);
+ for (std::string line; std::getline(stream, line, '\n');) {
+ out->append(indentation);
+ out->append(line);
+ out->append("\n");
+ }
+}
+
+} // namespace
+
+CertError::CertError() = default;
+
+CertError::CertError(Severity severity,
+ CertErrorId id,
+ std::unique_ptr<CertErrorParams> params)
+ : severity(severity), id(id), params(std::move(params)) {}
+
+CertError::CertError(CertError&& other) = default;
+
+CertError& CertError::operator=(CertError&&) = default;
+
+CertError::~CertError() = default;
+
+std::string CertError::ToDebugString() const {
+ std::string result;
+ switch (severity) {
+ case SEVERITY_WARNING:
+ result += "WARNING: ";
+ break;
+ case SEVERITY_HIGH:
+ result += "ERROR: ";
+ break;
+ }
+ result += CertErrorIdToDebugString(id);
+ result += +"\n";
+
+ if (params)
+ AppendLinesWithIndentation(params->ToDebugString(), " ", &result);
+
+ return result;
+}
+
+CertErrors::CertErrors() = default;
+CertErrors::CertErrors(CertErrors&& other) = default;
+CertErrors& CertErrors::operator=(CertErrors&&) = default;
+CertErrors::~CertErrors() = default;
+
+void CertErrors::Add(CertError::Severity severity,
+ CertErrorId id,
+ std::unique_ptr<CertErrorParams> params) {
+ nodes_.emplace_back(severity, id, std::move(params));
+}
+
+void CertErrors::AddError(CertErrorId id,
+ std::unique_ptr<CertErrorParams> params) {
+ Add(CertError::SEVERITY_HIGH, id, std::move(params));
+}
+
+void CertErrors::AddError(CertErrorId id) {
+ AddError(id, nullptr);
+}
+
+void CertErrors::AddWarning(CertErrorId id,
+ std::unique_ptr<CertErrorParams> params) {
+ Add(CertError::SEVERITY_WARNING, id, std::move(params));
+}
+
+void CertErrors::AddWarning(CertErrorId id) {
+ AddWarning(id, nullptr);
+}
+
+std::string CertErrors::ToDebugString() const {
+ std::string result;
+ for (const CertError& node : nodes_)
+ result += node.ToDebugString();
+
+ return result;
+}
+
+bool CertErrors::ContainsError(CertErrorId id) const {
+ for (const CertError& node : nodes_) {
+ if (node.id == id)
+ return true;
+ }
+ return false;
+}
+
+bool CertErrors::ContainsAnyErrorWithSeverity(
+ CertError::Severity severity) const {
+ for (const CertError& node : nodes_) {
+ if (node.severity == severity)
+ return true;
+ }
+ return false;
+}
+
+CertPathErrors::CertPathErrors() = default;
+
+CertPathErrors::CertPathErrors(CertPathErrors&& other) = default;
+CertPathErrors& CertPathErrors::operator=(CertPathErrors&&) = default;
+
+CertPathErrors::~CertPathErrors() = default;
+
+CertErrors* CertPathErrors::GetErrorsForCert(size_t cert_index) {
+ if (cert_index >= cert_errors_.size())
+ cert_errors_.resize(cert_index + 1);
+ return &cert_errors_[cert_index];
+}
+
+const CertErrors* CertPathErrors::GetErrorsForCert(size_t cert_index) const {
+ if (cert_index >= cert_errors_.size())
+ return nullptr;
+ return &cert_errors_[cert_index];
+}
+
+CertErrors* CertPathErrors::GetOtherErrors() {
+ return &other_errors_;
+}
+
+bool CertPathErrors::ContainsError(CertErrorId id) const {
+ for (const CertErrors& errors : cert_errors_) {
+ if (errors.ContainsError(id))
+ return true;
+ }
+
+ if (other_errors_.ContainsError(id))
+ return true;
+
+ return false;
+}
+
+bool CertPathErrors::ContainsAnyErrorWithSeverity(
+ CertError::Severity severity) const {
+ for (const CertErrors& errors : cert_errors_) {
+ if (errors.ContainsAnyErrorWithSeverity(severity))
+ return true;
+ }
+
+ if (other_errors_.ContainsAnyErrorWithSeverity(severity))
+ return true;
+
+ return false;
+}
+
+std::string CertPathErrors::ToDebugString(
+ const ParsedCertificateList& certs) const {
+ std::ostringstream result;
+
+ for (size_t i = 0; i < cert_errors_.size(); ++i) {
+ // Pretty print the current CertErrors. If there were no errors/warnings,
+ // then continue.
+ const CertErrors& errors = cert_errors_[i];
+ std::string cert_errors_string = errors.ToDebugString();
+ if (cert_errors_string.empty())
+ continue;
+
+ // Add a header that identifies which certificate this CertErrors pertains
+ // to.
+ std::string cert_name_debug_str;
+ if (i < certs.size() && certs[i]) {
+ RDNSequence subject;
+ if (ParseName(certs[i]->tbs().subject_tlv, &subject) &&
+ ConvertToRFC2253(subject, &cert_name_debug_str)) {
+ cert_name_debug_str = " (" + cert_name_debug_str + ")";
+ }
+ }
+ result << "----- Certificate i=" << i << cert_name_debug_str << " -----\n";
+ result << cert_errors_string << "\n";
+ }
+
+ // Print any other errors that aren't associated with a particular certificate
+ // in the chain.
+ std::string other_errors = other_errors_.ToDebugString();
+ if (!other_errors.empty()) {
+ result << "----- Other errors (not certificate specific) -----\n";
+ result << other_errors << "\n";
+ }
+
+ return result.str();
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/cert_errors.h b/chromium/net/cert/pki/cert_errors.h
new file mode 100644
index 00000000000..98f635da34b
--- /dev/null
+++ b/chromium/net/cert/pki/cert_errors.h
@@ -0,0 +1,168 @@
+// Copyright 2016 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.
+
+// ----------------------------
+// Overview of error design
+// ----------------------------
+//
+// Certificate path building/validation/parsing may emit a sequence of errors
+// and warnings.
+//
+// Each individual error/warning entry (CertError) is comprised of:
+//
+// * A unique identifier.
+//
+// This serves similarly to an error code, and is used to query if a
+// particular error/warning occurred.
+//
+// * [optional] A parameters object.
+//
+// Nodes may attach a heap-allocated subclass of CertErrorParams to carry
+// extra information that is used when reporting the error. For instance
+// a parsing error may describe where in the DER the failure happened, or
+// what the unexpected value was.
+//
+// A collection of errors is represented by the CertErrors object. This may be
+// used to group errors that have a common context, such as all the
+// errors/warnings that apply to a specific certificate.
+//
+// Lastly, CertPathErrors composes multiple CertErrors -- one for each
+// certificate in the verified chain.
+//
+// ----------------------------
+// Defining new errors
+// ----------------------------
+//
+// The error IDs are extensible and do not need to be centrally defined.
+//
+// To define a new error use the macro DEFINE_CERT_ERROR_ID() in a .cc file.
+// If consumers are to be able to query for this error then the symbol should
+// also be exposed in a header file.
+//
+// Error IDs are in truth string literals, whose pointer value will be unique
+// per process.
+
+#ifndef NET_CERT_PKI_CERT_ERRORS_H_
+#define NET_CERT_PKI_CERT_ERRORS_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/cert_error_id.h"
+#include "net/cert/pki/parsed_certificate.h"
+
+namespace net {
+
+class CertErrorParams;
+
+// CertError represents either an error or a warning.
+struct NET_EXPORT CertError {
+ enum Severity {
+ SEVERITY_HIGH,
+ SEVERITY_WARNING,
+ };
+
+ CertError();
+ CertError(Severity severity,
+ CertErrorId id,
+ std::unique_ptr<CertErrorParams> params);
+ CertError(CertError&& other);
+ CertError& operator=(CertError&&);
+ ~CertError();
+
+ // Pretty-prints the error and its parameters.
+ std::string ToDebugString() const;
+
+ Severity severity;
+ CertErrorId id;
+ std::unique_ptr<CertErrorParams> params;
+};
+
+// CertErrors is a collection of CertError, along with convenience methods to
+// add and inspect errors.
+class NET_EXPORT CertErrors {
+ public:
+ CertErrors();
+ CertErrors(CertErrors&& other);
+ CertErrors& operator=(CertErrors&&);
+ ~CertErrors();
+
+ // Adds an error/warning. |params| may be null.
+ void Add(CertError::Severity severity,
+ CertErrorId id,
+ std::unique_ptr<CertErrorParams> params);
+
+ // Adds a high severity error.
+ void AddError(CertErrorId id, std::unique_ptr<CertErrorParams> params);
+ void AddError(CertErrorId id);
+
+ // Adds a low severity error.
+ void AddWarning(CertErrorId id, std::unique_ptr<CertErrorParams> params);
+ void AddWarning(CertErrorId id);
+
+ // Dumps a textual representation of the errors for debugging purposes.
+ std::string ToDebugString() const;
+
+ // Returns true if the error |id| was added to this CertErrors (of any
+ // severity).
+ bool ContainsError(CertErrorId id) const;
+
+ // Returns true if this contains any errors of the given severity level.
+ bool ContainsAnyErrorWithSeverity(CertError::Severity severity) const;
+
+ private:
+ std::vector<CertError> nodes_;
+};
+
+// CertPathErrors is a collection of CertErrors, to group errors into different
+// buckets for different certificates. The "index" should correspond with that
+// of the certificate relative to its chain.
+class NET_EXPORT CertPathErrors {
+ public:
+ CertPathErrors();
+ CertPathErrors(CertPathErrors&& other);
+ CertPathErrors& operator=(CertPathErrors&&);
+ ~CertPathErrors();
+
+ // Gets a bucket to put errors in for |cert_index|. This will lookup and
+ // return the existing error bucket if one exists, or create a new one for the
+ // specified index. It is expected that |cert_index| is the corresponding
+ // index in a certificate chain (with 0 being the target).
+ CertErrors* GetErrorsForCert(size_t cert_index);
+
+ // Const version of the above, with the difference that if there is no
+ // existing bucket for |cert_index| returns nullptr rather than lazyily
+ // creating one.
+ const CertErrors* GetErrorsForCert(size_t cert_index) const;
+
+ // Returns a bucket to put errors that are not associated with a particular
+ // certificate.
+ CertErrors* GetOtherErrors();
+
+ // Returns true if CertPathErrors contains the specified error (of any
+ // severity).
+ bool ContainsError(CertErrorId id) const;
+
+ // Returns true if this contains any errors of the given severity level.
+ bool ContainsAnyErrorWithSeverity(CertError::Severity severity) const;
+
+ // Shortcut for ContainsAnyErrorWithSeverity(CertError::SEVERITY_HIGH).
+ bool ContainsHighSeverityErrors() const {
+ return ContainsAnyErrorWithSeverity(CertError::SEVERITY_HIGH);
+ }
+
+ // Pretty-prints all the errors in the CertPathErrors. If there were no
+ // errors/warnings, returns an empty string.
+ std::string ToDebugString(const ParsedCertificateList& certs) const;
+
+ private:
+ std::vector<CertErrors> cert_errors_;
+ CertErrors other_errors_;
+};
+
+} // namespace net
+
+#endif // NET_CERT_PKI_CERT_ERRORS_H_
diff --git a/chromium/net/cert/pki/cert_issuer_source.h b/chromium/net/cert/pki/cert_issuer_source.h
new file mode 100644
index 00000000000..1568cd058f3
--- /dev/null
+++ b/chromium/net/cert/pki/cert_issuer_source.h
@@ -0,0 +1,67 @@
+// Copyright 2016 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 NET_CERT_PKI_CERT_ISSUER_SOURCE_H_
+#define NET_CERT_PKI_CERT_ISSUER_SOURCE_H_
+
+#include <memory>
+#include <vector>
+
+#include "net/base/net_export.h"
+#include "net/cert/pki/parsed_certificate.h"
+
+namespace net {
+
+// Interface for looking up issuers of a certificate during path building.
+// Provides a synchronous and asynchronous method for retrieving issuers, so the
+// path builder can try to complete synchronously first. The caller is expected
+// to call SyncGetIssuersOf first, see if it can make progress with those
+// results, and if not, then fall back to calling AsyncGetIssuersOf.
+// An implementations may choose to return results from either one of the Get
+// methods, or from both.
+class NET_EXPORT CertIssuerSource {
+ public:
+ class NET_EXPORT Request {
+ public:
+ Request() = default;
+
+ Request(const Request&) = delete;
+ Request& operator=(const Request&) = delete;
+
+ // Destruction of the Request cancels it.
+ virtual ~Request() = default;
+
+ // Retrieves issuers and appends them to |issuers|.
+ //
+ // GetNext should be called again to retrieve any remaining issuers.
+ //
+ // If no issuers are left then |issuers| will not be modified. This
+ // indicates that the issuers have been exhausted and GetNext() should
+ // not be called again.
+ virtual void GetNext(ParsedCertificateList* issuers) = 0;
+ };
+
+ virtual ~CertIssuerSource() = default;
+
+ // Finds certificates whose Subject matches |cert|'s Issuer.
+ // Matches are appended to |issuers|. Any existing contents of |issuers| will
+ // not be modified. If the implementation does not support synchronous
+ // lookups, or if there are no matches, |issuers| is not modified.
+ virtual void SyncGetIssuersOf(const ParsedCertificate* cert,
+ ParsedCertificateList* issuers) = 0;
+
+ // Finds certificates whose Subject matches |cert|'s Issuer.
+ // If the implementation does not support asynchronous lookups or can
+ // determine synchronously that it would return no results, |*out_req|
+ // will be set to nullptr.
+ //
+ // Otherwise a request is started and saved to |out_req|. The results can be
+ // read through the Request interface.
+ virtual void AsyncGetIssuersOf(const ParsedCertificate* cert,
+ std::unique_ptr<Request>* out_req) = 0;
+};
+
+} // namespace net
+
+#endif // NET_CERT_PKI_CERT_ISSUER_SOURCE_H_
diff --git a/chromium/net/cert/pki/cert_issuer_source_static.cc b/chromium/net/cert/pki/cert_issuer_source_static.cc
new file mode 100644
index 00000000000..c41aede9d6f
--- /dev/null
+++ b/chromium/net/cert/pki/cert_issuer_source_static.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 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 "net/cert/pki/cert_issuer_source_static.h"
+
+namespace net {
+
+CertIssuerSourceStatic::CertIssuerSourceStatic() = default;
+CertIssuerSourceStatic::~CertIssuerSourceStatic() = default;
+
+void CertIssuerSourceStatic::AddCert(scoped_refptr<ParsedCertificate> cert) {
+ intermediates_.insert(std::make_pair(
+ cert->normalized_subject().AsStringPiece(), std::move(cert)));
+}
+
+void CertIssuerSourceStatic::Clear() {
+ intermediates_.clear();
+}
+
+void CertIssuerSourceStatic::SyncGetIssuersOf(const ParsedCertificate* cert,
+ ParsedCertificateList* issuers) {
+ auto range =
+ intermediates_.equal_range(cert->normalized_issuer().AsStringPiece());
+ for (auto it = range.first; it != range.second; ++it)
+ issuers->push_back(it->second);
+}
+
+void CertIssuerSourceStatic::AsyncGetIssuersOf(
+ const ParsedCertificate* cert,
+ std::unique_ptr<Request>* out_req) {
+ // CertIssuerSourceStatic never returns asynchronous results.
+ out_req->reset();
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/cert_issuer_source_static.h b/chromium/net/cert/pki/cert_issuer_source_static.h
new file mode 100644
index 00000000000..c3be882d023
--- /dev/null
+++ b/chromium/net/cert/pki/cert_issuer_source_static.h
@@ -0,0 +1,50 @@
+// Copyright 2016 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 NET_CERT_PKI_CERT_ISSUER_SOURCE_STATIC_H_
+#define NET_CERT_PKI_CERT_ISSUER_SOURCE_STATIC_H_
+
+#include <unordered_map>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/cert_issuer_source.h"
+
+namespace net {
+
+// Synchronously returns issuers from a pre-supplied set.
+class NET_EXPORT CertIssuerSourceStatic : public CertIssuerSource {
+ public:
+ CertIssuerSourceStatic();
+
+ CertIssuerSourceStatic(const CertIssuerSourceStatic&) = delete;
+ CertIssuerSourceStatic& operator=(const CertIssuerSourceStatic&) = delete;
+
+ ~CertIssuerSourceStatic() override;
+
+ // Adds |cert| to the set of certificates that this CertIssuerSource will
+ // provide.
+ void AddCert(scoped_refptr<ParsedCertificate> cert);
+
+ // Clears the set of certificates.
+ void Clear();
+
+ // CertIssuerSource implementation:
+ void SyncGetIssuersOf(const ParsedCertificate* cert,
+ ParsedCertificateList* issuers) override;
+ void AsyncGetIssuersOf(const ParsedCertificate* cert,
+ std::unique_ptr<Request>* out_req) override;
+
+ private:
+ // The certificates that the CertIssuerSourceStatic can return, keyed on the
+ // normalized subject value.
+ std::unordered_multimap<base::StringPiece,
+ scoped_refptr<ParsedCertificate>,
+ base::StringPieceHash>
+ intermediates_;
+};
+
+} // namespace net
+
+#endif // NET_CERT_PKI_CERT_ISSUER_SOURCE_STATIC_H_
diff --git a/chromium/net/cert/pki/cert_issuer_source_static_unittest.cc b/chromium/net/cert/pki/cert_issuer_source_static_unittest.cc
new file mode 100644
index 00000000000..02727cc6724
--- /dev/null
+++ b/chromium/net/cert/pki/cert_issuer_source_static_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright 2016 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 "net/cert/pki/cert_issuer_source_static.h"
+
+#include "net/cert/pki/cert_issuer_source_sync_unittest.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+class CertIssuerSourceStaticTestDelegate {
+ public:
+ void AddCert(scoped_refptr<ParsedCertificate> cert) {
+ source_.AddCert(std::move(cert));
+ }
+
+ CertIssuerSource& source() { return source_; }
+
+ protected:
+ CertIssuerSourceStatic source_;
+};
+
+INSTANTIATE_TYPED_TEST_SUITE_P(CertIssuerSourceStaticTest,
+ CertIssuerSourceSyncTest,
+ CertIssuerSourceStaticTestDelegate);
+
+INSTANTIATE_TYPED_TEST_SUITE_P(CertIssuerSourceStaticNormalizationTest,
+ CertIssuerSourceSyncNormalizationTest,
+ CertIssuerSourceStaticTestDelegate);
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/pki/cert_issuer_source_sync_unittest.h b/chromium/net/cert/pki/cert_issuer_source_sync_unittest.h
new file mode 100644
index 00000000000..e3f165036db
--- /dev/null
+++ b/chromium/net/cert/pki/cert_issuer_source_sync_unittest.h
@@ -0,0 +1,216 @@
+// Copyright 2016 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 NET_CERT_PKI_CERT_ISSUER_SOURCE_SYNC_UNITTEST_H_
+#define NET_CERT_PKI_CERT_ISSUER_SOURCE_SYNC_UNITTEST_H_
+
+#include <algorithm>
+
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/cert_issuer_source.h"
+#include "net/cert/pki/test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+
+namespace net {
+
+namespace {
+
+::testing::AssertionResult ReadTestPem(const std::string& file_name,
+ const std::string& block_name,
+ std::string* result) {
+ const PemBlockMapping mappings[] = {
+ {block_name.c_str(), result},
+ };
+
+ return ReadTestDataFromPemFile(file_name, mappings);
+}
+
+::testing::AssertionResult ReadTestCert(
+ const std::string& file_name,
+ scoped_refptr<ParsedCertificate>* result) {
+ std::string der;
+ ::testing::AssertionResult r =
+ ReadTestPem("net/data/cert_issuer_source_static_unittest/" + file_name,
+ "CERTIFICATE", &der);
+ if (!r)
+ return r;
+ CertErrors errors;
+ *result = ParsedCertificate::Create(
+ bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(
+ reinterpret_cast<const uint8_t*>(der.data()), der.size(), nullptr)),
+ {}, &errors);
+ if (!*result) {
+ return ::testing::AssertionFailure()
+ << "ParsedCertificate::Create() failed:\n"
+ << errors.ToDebugString();
+ }
+ return ::testing::AssertionSuccess();
+}
+
+} // namespace
+
+template <typename TestDelegate>
+class CertIssuerSourceSyncTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ ASSERT_TRUE(ReadTestCert("root.pem", &root_));
+ ASSERT_TRUE(ReadTestCert("i1_1.pem", &i1_1_));
+ ASSERT_TRUE(ReadTestCert("i1_2.pem", &i1_2_));
+ ASSERT_TRUE(ReadTestCert("i2.pem", &i2_));
+ ASSERT_TRUE(ReadTestCert("i3_1.pem", &i3_1_));
+ ASSERT_TRUE(ReadTestCert("i3_2.pem", &i3_2_));
+ ASSERT_TRUE(ReadTestCert("c1.pem", &c1_));
+ ASSERT_TRUE(ReadTestCert("c2.pem", &c2_));
+ ASSERT_TRUE(ReadTestCert("d.pem", &d_));
+ ASSERT_TRUE(ReadTestCert("e1.pem", &e1_));
+ ASSERT_TRUE(ReadTestCert("e2.pem", &e2_));
+ }
+
+ void AddCert(scoped_refptr<ParsedCertificate> cert) {
+ delegate_.AddCert(std::move(cert));
+ }
+
+ void AddAllCerts() {
+ AddCert(root_);
+ AddCert(i1_1_);
+ AddCert(i1_2_);
+ AddCert(i2_);
+ AddCert(i3_1_);
+ AddCert(i3_2_);
+ AddCert(c1_);
+ AddCert(c2_);
+ AddCert(d_);
+ AddCert(e1_);
+ AddCert(e2_);
+ }
+
+ CertIssuerSource& source() { return delegate_.source(); }
+
+ protected:
+ bool IssuersMatch(scoped_refptr<ParsedCertificate> cert,
+ ParsedCertificateList expected_matches) {
+ ParsedCertificateList matches;
+ source().SyncGetIssuersOf(cert.get(), &matches);
+
+ std::vector<der::Input> der_result_matches;
+ for (const auto& it : matches)
+ der_result_matches.push_back(it->der_cert());
+ std::sort(der_result_matches.begin(), der_result_matches.end());
+
+ std::vector<der::Input> der_expected_matches;
+ for (const auto& it : expected_matches)
+ der_expected_matches.push_back(it->der_cert());
+ std::sort(der_expected_matches.begin(), der_expected_matches.end());
+
+ if (der_expected_matches == der_result_matches)
+ return true;
+
+ // Print some extra information for debugging.
+ EXPECT_EQ(der_expected_matches, der_result_matches);
+ return false;
+ }
+
+ TestDelegate delegate_;
+ scoped_refptr<ParsedCertificate> root_;
+ scoped_refptr<ParsedCertificate> i1_1_;
+ scoped_refptr<ParsedCertificate> i1_2_;
+ scoped_refptr<ParsedCertificate> i2_;
+ scoped_refptr<ParsedCertificate> i3_1_;
+ scoped_refptr<ParsedCertificate> i3_2_;
+ scoped_refptr<ParsedCertificate> c1_;
+ scoped_refptr<ParsedCertificate> c2_;
+ scoped_refptr<ParsedCertificate> d_;
+ scoped_refptr<ParsedCertificate> e1_;
+ scoped_refptr<ParsedCertificate> e2_;
+};
+
+TYPED_TEST_SUITE_P(CertIssuerSourceSyncTest);
+
+TYPED_TEST_P(CertIssuerSourceSyncTest, NoMatch) {
+ this->AddCert(this->root_);
+
+ EXPECT_TRUE(this->IssuersMatch(this->c1_, ParsedCertificateList()));
+}
+
+TYPED_TEST_P(CertIssuerSourceSyncTest, OneMatch) {
+ this->AddAllCerts();
+
+ EXPECT_TRUE(this->IssuersMatch(this->i1_1_, {this->root_}));
+ EXPECT_TRUE(this->IssuersMatch(this->d_, {this->i2_}));
+}
+
+TYPED_TEST_P(CertIssuerSourceSyncTest, MultipleMatches) {
+ this->AddAllCerts();
+
+ EXPECT_TRUE(this->IssuersMatch(this->e1_, {this->i3_1_, this->i3_2_}));
+ EXPECT_TRUE(this->IssuersMatch(this->e2_, {this->i3_1_, this->i3_2_}));
+}
+
+// Searching for the issuer of a self-issued cert returns the same cert if it
+// happens to be in the CertIssuerSourceStatic.
+// Conceptually this makes sense, though probably not very useful in practice.
+// Doesn't hurt anything though.
+TYPED_TEST_P(CertIssuerSourceSyncTest, SelfIssued) {
+ this->AddAllCerts();
+
+ EXPECT_TRUE(this->IssuersMatch(this->root_, {this->root_}));
+}
+
+// CertIssuerSourceStatic never returns results asynchronously.
+TYPED_TEST_P(CertIssuerSourceSyncTest, IsNotAsync) {
+ this->AddCert(this->i1_1_);
+ std::unique_ptr<CertIssuerSource::Request> request;
+ this->source().AsyncGetIssuersOf(this->c1_.get(), &request);
+ EXPECT_EQ(nullptr, request);
+}
+
+// These are all the tests that should have the same result with or without
+// normalization.
+REGISTER_TYPED_TEST_SUITE_P(CertIssuerSourceSyncTest,
+ NoMatch,
+ OneMatch,
+ MultipleMatches,
+ SelfIssued,
+ IsNotAsync);
+
+template <typename TestDelegate>
+class CertIssuerSourceSyncNormalizationTest
+ : public CertIssuerSourceSyncTest<TestDelegate> {};
+TYPED_TEST_SUITE_P(CertIssuerSourceSyncNormalizationTest);
+
+TYPED_TEST_P(CertIssuerSourceSyncNormalizationTest,
+ MultipleMatchesAfterNormalization) {
+ this->AddAllCerts();
+
+ EXPECT_TRUE(this->IssuersMatch(this->c1_, {this->i1_1_, this->i1_2_}));
+ EXPECT_TRUE(this->IssuersMatch(this->c2_, {this->i1_1_, this->i1_2_}));
+}
+
+// These tests require (utf8) normalization.
+REGISTER_TYPED_TEST_SUITE_P(CertIssuerSourceSyncNormalizationTest,
+ MultipleMatchesAfterNormalization);
+
+template <typename TestDelegate>
+class CertIssuerSourceSyncNotNormalizedTest
+ : public CertIssuerSourceSyncTest<TestDelegate> {};
+TYPED_TEST_SUITE_P(CertIssuerSourceSyncNotNormalizedTest);
+
+TYPED_TEST_P(CertIssuerSourceSyncNotNormalizedTest,
+ OneMatchWithoutNormalization) {
+ this->AddAllCerts();
+
+ // Without normalization c1 and c2 should at least be able to find their
+ // exact matching issuer. (c1 should match i1_1, and c2 should match i1_2.)
+ EXPECT_TRUE(this->IssuersMatch(this->c1_, {this->i1_1_}));
+ EXPECT_TRUE(this->IssuersMatch(this->c2_, {this->i1_2_}));
+}
+
+// These tests are for implementations which do not do utf8 normalization.
+REGISTER_TYPED_TEST_SUITE_P(CertIssuerSourceSyncNotNormalizedTest,
+ OneMatchWithoutNormalization);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_CERT_ISSUER_SOURCE_SYNC_UNITTEST_H_
diff --git a/chromium/net/cert/pki/certificate_policies.cc b/chromium/net/cert/pki/certificate_policies.cc
new file mode 100644
index 00000000000..e7a3c17e435
--- /dev/null
+++ b/chromium/net/cert/pki/certificate_policies.cc
@@ -0,0 +1,372 @@
+// Copyright 2015 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 <algorithm>
+
+#include "net/cert/pki/certificate_policies.h"
+
+#include "net/cert/pki/cert_error_params.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "net/der/parser.h"
+#include "net/der/tag.h"
+
+namespace net {
+
+namespace {
+
+// ---------------------------------------------------------------
+// Errors
+// ---------------------------------------------------------------
+
+DEFINE_CERT_ERROR_ID(kPolicyQualifiersEmptySequence,
+ "The policy qualifiers SEQUENCE is empty");
+DEFINE_CERT_ERROR_ID(kUnknownPolicyQualifierOid,
+ "Unknown policy qualifier OID (not CPS or User Notice)");
+DEFINE_CERT_ERROR_ID(kPoliciesEmptySequence, "Policies is an empty SEQUENCE");
+DEFINE_CERT_ERROR_ID(kPoliciesDuplicateOid, "Policies contains duplicate OIDs");
+DEFINE_CERT_ERROR_ID(kPolicyInformationTrailingData,
+ "PolicyInformation has trailing data");
+DEFINE_CERT_ERROR_ID(kFailedParsingPolicyQualifiers,
+ "Failed parsing policy qualifiers");
+DEFINE_CERT_ERROR_ID(kMissingQualifier,
+ "PolicyQualifierInfo is missing qualifier");
+DEFINE_CERT_ERROR_ID(kPolicyQualifierInfoTrailingData,
+ "PolicyQualifierInfo has trailing data");
+
+// Minimally parse policyQualifiers, storing in |policy_qualifiers| if non-null.
+// If a policy qualifier other than User Notice/CPS is present, parsing
+// will fail if |restrict_to_known_qualifiers| was set to true.
+bool ParsePolicyQualifiers(bool restrict_to_known_qualifiers,
+ der::Parser* policy_qualifiers_sequence_parser,
+ std::vector<PolicyQualifierInfo>* policy_qualifiers,
+ CertErrors* errors) {
+ DCHECK(errors);
+
+ // If it is present, the policyQualifiers sequence should have at least 1
+ // element.
+ //
+ // policyQualifiers SEQUENCE SIZE (1..MAX) OF
+ // PolicyQualifierInfo OPTIONAL }
+ if (!policy_qualifiers_sequence_parser->HasMore()) {
+ errors->AddError(kPolicyQualifiersEmptySequence);
+ return false;
+ }
+ while (policy_qualifiers_sequence_parser->HasMore()) {
+ // PolicyQualifierInfo ::= SEQUENCE {
+ der::Parser policy_information_parser;
+ if (!policy_qualifiers_sequence_parser->ReadSequence(
+ &policy_information_parser)) {
+ return false;
+ }
+ // policyQualifierId PolicyQualifierId,
+ der::Input qualifier_oid;
+ if (!policy_information_parser.ReadTag(der::kOid, &qualifier_oid))
+ return false;
+ if (restrict_to_known_qualifiers &&
+ qualifier_oid != der::Input(kCpsPointerId) &&
+ qualifier_oid != der::Input(kUserNoticeId)) {
+ errors->AddError(kUnknownPolicyQualifierOid,
+ CreateCertErrorParams1Der("oid", qualifier_oid));
+ return false;
+ }
+ // qualifier ANY DEFINED BY policyQualifierId }
+ der::Input qualifier_tlv;
+ if (!policy_information_parser.ReadRawTLV(&qualifier_tlv)) {
+ errors->AddError(kMissingQualifier);
+ return false;
+ }
+ // Should not have trailing data after qualifier.
+ if (policy_information_parser.HasMore()) {
+ errors->AddError(kPolicyQualifierInfoTrailingData);
+ return false;
+ }
+
+ if (policy_qualifiers)
+ policy_qualifiers->push_back({qualifier_oid, qualifier_tlv});
+ }
+ return true;
+}
+
+// RFC 5280 section 4.2.1.4. Certificate Policies:
+//
+// certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
+//
+// PolicyInformation ::= SEQUENCE {
+// policyIdentifier CertPolicyId,
+// policyQualifiers SEQUENCE SIZE (1..MAX) OF
+// PolicyQualifierInfo OPTIONAL }
+//
+// CertPolicyId ::= OBJECT IDENTIFIER
+//
+// PolicyQualifierInfo ::= SEQUENCE {
+// policyQualifierId PolicyQualifierId,
+// qualifier ANY DEFINED BY policyQualifierId }
+//
+// PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
+//
+// Qualifier ::= CHOICE {
+// cPSuri CPSuri,
+// userNotice UserNotice }
+//
+// CPSuri ::= IA5String
+//
+// UserNotice ::= SEQUENCE {
+// noticeRef NoticeReference OPTIONAL,
+// explicitText DisplayText OPTIONAL }
+//
+// NoticeReference ::= SEQUENCE {
+// organization DisplayText,
+// noticeNumbers SEQUENCE OF INTEGER }
+//
+// DisplayText ::= CHOICE {
+// ia5String IA5String (SIZE (1..200)),
+// visibleString VisibleString (SIZE (1..200)),
+// bmpString BMPString (SIZE (1..200)),
+// utf8String UTF8String (SIZE (1..200)) }
+bool ParseCertificatePoliciesExtensionImpl(
+ const der::Input& extension_value,
+ bool fail_parsing_unknown_qualifier_oids,
+ std::vector<der::Input>* policy_oids,
+ std::vector<PolicyInformation>* policy_informations,
+ CertErrors* errors) {
+ DCHECK(policy_oids);
+ DCHECK(errors);
+ // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
+ der::Parser extension_parser(extension_value);
+ der::Parser policies_sequence_parser;
+ if (!extension_parser.ReadSequence(&policies_sequence_parser))
+ return false;
+ // Should not have trailing data after certificatePolicies sequence.
+ if (extension_parser.HasMore())
+ return false;
+ // The certificatePolicies sequence should have at least 1 element.
+ if (!policies_sequence_parser.HasMore()) {
+ errors->AddError(kPoliciesEmptySequence);
+ return false;
+ }
+
+ policy_oids->clear();
+ if (policy_informations)
+ policy_informations->clear();
+
+ while (policies_sequence_parser.HasMore()) {
+ // PolicyInformation ::= SEQUENCE {
+ der::Parser policy_information_parser;
+ if (!policies_sequence_parser.ReadSequence(&policy_information_parser))
+ return false;
+ // policyIdentifier CertPolicyId,
+ der::Input policy_oid;
+ if (!policy_information_parser.ReadTag(der::kOid, &policy_oid))
+ return false;
+
+ policy_oids->push_back(policy_oid);
+
+ std::vector<PolicyQualifierInfo>* policy_qualifiers = nullptr;
+ if (policy_informations) {
+ policy_informations->emplace_back();
+ policy_informations->back().policy_oid = policy_oid;
+ policy_qualifiers = &policy_informations->back().policy_qualifiers;
+ }
+
+ if (!policy_information_parser.HasMore())
+ continue;
+
+ // policyQualifiers SEQUENCE SIZE (1..MAX) OF
+ // PolicyQualifierInfo OPTIONAL }
+ der::Parser policy_qualifiers_sequence_parser;
+ if (!policy_information_parser.ReadSequence(
+ &policy_qualifiers_sequence_parser)) {
+ return false;
+ }
+ // Should not have trailing data after policyQualifiers sequence.
+ if (policy_information_parser.HasMore()) {
+ errors->AddError(kPolicyInformationTrailingData);
+ return false;
+ }
+
+ // RFC 5280 section 4.2.1.4: When qualifiers are used with the special
+ // policy anyPolicy, they MUST be limited to the qualifiers identified in
+ // this section.
+ if (!ParsePolicyQualifiers(fail_parsing_unknown_qualifier_oids ||
+ policy_oid == der::Input(kAnyPolicyOid),
+ &policy_qualifiers_sequence_parser,
+ policy_qualifiers, errors)) {
+ errors->AddError(kFailedParsingPolicyQualifiers);
+ return false;
+ }
+ }
+
+ // RFC 5280 section 4.2.1.4: A certificate policy OID MUST NOT appear more
+ // than once in a certificate policies extension.
+ std::sort(policy_oids->begin(), policy_oids->end());
+ auto dupe_policy_iter =
+ std::adjacent_find(policy_oids->begin(), policy_oids->end());
+ if (dupe_policy_iter != policy_oids->end()) {
+ errors->AddError(kPoliciesDuplicateOid,
+ CreateCertErrorParams1Der("oid", *dupe_policy_iter));
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+PolicyInformation::PolicyInformation() = default;
+PolicyInformation::~PolicyInformation() = default;
+PolicyInformation::PolicyInformation(const PolicyInformation&) = default;
+PolicyInformation::PolicyInformation(PolicyInformation&&) = default;
+
+bool ParseCertificatePoliciesExtension(const der::Input& extension_value,
+ std::vector<PolicyInformation>* policies,
+ CertErrors* errors) {
+ std::vector<der::Input> unused_policy_oids;
+ return ParseCertificatePoliciesExtensionImpl(
+ extension_value, /*fail_parsing_unknown_qualifier_oids=*/false,
+ &unused_policy_oids, policies, errors);
+}
+
+bool ParseCertificatePoliciesExtensionOids(
+ const der::Input& extension_value,
+ bool fail_parsing_unknown_qualifier_oids,
+ std::vector<der::Input>* policy_oids,
+ CertErrors* errors) {
+ return ParseCertificatePoliciesExtensionImpl(
+ extension_value, fail_parsing_unknown_qualifier_oids, policy_oids,
+ nullptr, errors);
+}
+
+// From RFC 5280:
+//
+// PolicyConstraints ::= SEQUENCE {
+// requireExplicitPolicy [0] SkipCerts OPTIONAL,
+// inhibitPolicyMapping [1] SkipCerts OPTIONAL }
+//
+// SkipCerts ::= INTEGER (0..MAX)
+bool ParsePolicyConstraints(const der::Input& policy_constraints_tlv,
+ ParsedPolicyConstraints* out) {
+ der::Parser parser(policy_constraints_tlv);
+
+ // PolicyConstraints ::= SEQUENCE {
+ der::Parser sequence_parser;
+ if (!parser.ReadSequence(&sequence_parser))
+ return false;
+
+ // RFC 5280 prohibits CAs from issuing PolicyConstraints as an empty sequence:
+ //
+ // Conforming CAs MUST NOT issue certificates where policy constraints
+ // is an empty sequence. That is, either the inhibitPolicyMapping field
+ // or the requireExplicitPolicy field MUST be present. The behavior of
+ // clients that encounter an empty policy constraints field is not
+ // addressed in this profile.
+ if (!sequence_parser.HasMore())
+ return false;
+
+ absl::optional<der::Input> require_value;
+ if (!sequence_parser.ReadOptionalTag(der::ContextSpecificPrimitive(0),
+ &require_value)) {
+ return false;
+ }
+
+ if (require_value) {
+ uint8_t require_explicit_policy;
+ if (!ParseUint8(require_value.value(), &require_explicit_policy)) {
+ // TODO(eroman): Surface reason for failure if length was longer than
+ // uint8.
+ return false;
+ }
+ out->require_explicit_policy = require_explicit_policy;
+ }
+
+ absl::optional<der::Input> inhibit_value;
+ if (!sequence_parser.ReadOptionalTag(der::ContextSpecificPrimitive(1),
+ &inhibit_value)) {
+ return false;
+ }
+
+ if (inhibit_value) {
+ uint8_t inhibit_policy_mapping;
+ if (!ParseUint8(inhibit_value.value(), &inhibit_policy_mapping)) {
+ // TODO(eroman): Surface reason for failure if length was longer than
+ // uint8.
+ return false;
+ }
+ out->inhibit_policy_mapping = inhibit_policy_mapping;
+ }
+
+ // There should be no remaining data.
+ if (sequence_parser.HasMore() || parser.HasMore())
+ return false;
+
+ return true;
+}
+
+// From RFC 5280:
+//
+// InhibitAnyPolicy ::= SkipCerts
+//
+// SkipCerts ::= INTEGER (0..MAX)
+bool ParseInhibitAnyPolicy(const der::Input& inhibit_any_policy_tlv,
+ uint8_t* num_certs) {
+ der::Parser parser(inhibit_any_policy_tlv);
+
+ // TODO(eroman): Surface reason for failure if length was longer than uint8.
+ if (!parser.ReadUint8(num_certs))
+ return false;
+
+ // There should be no remaining data.
+ if (parser.HasMore())
+ return false;
+
+ return true;
+}
+
+// From RFC 5280:
+//
+// PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
+// issuerDomainPolicy CertPolicyId,
+// subjectDomainPolicy CertPolicyId }
+bool ParsePolicyMappings(const der::Input& policy_mappings_tlv,
+ std::vector<ParsedPolicyMapping>* mappings) {
+ mappings->clear();
+
+ der::Parser parser(policy_mappings_tlv);
+
+ // PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
+ der::Parser sequence_parser;
+ if (!parser.ReadSequence(&sequence_parser))
+ return false;
+
+ // Must be at least 1 mapping.
+ if (!sequence_parser.HasMore())
+ return false;
+
+ while (sequence_parser.HasMore()) {
+ der::Parser mapping_parser;
+ if (!sequence_parser.ReadSequence(&mapping_parser))
+ return false;
+
+ ParsedPolicyMapping mapping;
+ if (!mapping_parser.ReadTag(der::kOid, &mapping.issuer_domain_policy))
+ return false;
+ if (!mapping_parser.ReadTag(der::kOid, &mapping.subject_domain_policy))
+ return false;
+
+ // There shouldn't be extra unconsumed data.
+ if (mapping_parser.HasMore())
+ return false;
+
+ mappings->push_back(mapping);
+ }
+
+ // There shouldn't be extra unconsumed data.
+ if (parser.HasMore())
+ return false;
+
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/certificate_policies.h b/chromium/net/cert/pki/certificate_policies.h
new file mode 100644
index 00000000000..182bf9a82f5
--- /dev/null
+++ b/chromium/net/cert/pki/certificate_policies.h
@@ -0,0 +1,135 @@
+// Copyright 2015 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 NET_CERT_PKI_CERTIFICATE_POLICIES_H_
+#define NET_CERT_PKI_CERTIFICATE_POLICIES_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "net/base/net_export.h"
+#include "net/der/input.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+
+class CertErrors;
+
+// Returns the DER-encoded OID, without tag or length, of the anyPolicy
+// certificate policy defined in RFC 5280 section 4.2.1.4.
+inline constexpr uint8_t kAnyPolicyOid[] = {0x55, 0x1D, 0x20, 0x00};
+
+// From RFC 5280:
+//
+// id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 }
+//
+// In dotted notation: 2.5.29.54
+inline constexpr uint8_t kInhibitAnyPolicyOid[] = {0x55, 0x1d, 0x36};
+
+// From RFC 5280:
+//
+// id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
+//
+// In dotted notation: 2.5.29.33
+inline constexpr uint8_t kPolicyMappingsOid[] = {0x55, 0x1d, 0x21};
+
+// -- policyQualifierIds for Internet policy qualifiers
+//
+// id-qt OBJECT IDENTIFIER ::= { id-pkix 2 }
+// id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 }
+//
+// In dotted decimal form: 1.3.6.1.5.5.7.2.1
+inline constexpr uint8_t kCpsPointerId[] = {0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01};
+
+// id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 }
+//
+// In dotted decimal form: 1.3.6.1.5.5.7.2.2
+inline constexpr uint8_t kUserNoticeId[] = {0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x02};
+
+struct PolicyQualifierInfo {
+ der::Input qualifier_oid;
+ der::Input qualifier;
+};
+
+struct NET_EXPORT PolicyInformation {
+ PolicyInformation();
+ ~PolicyInformation();
+ PolicyInformation(const PolicyInformation&);
+ PolicyInformation(PolicyInformation&&);
+
+ der::Input policy_oid;
+ std::vector<PolicyQualifierInfo> policy_qualifiers;
+};
+
+// Parses a certificatePolicies extension and stores the policy information
+// |*policies|, in the order presented in |extension_value|.
+//
+// Returns true on success. On failure returns false and may add errors to
+// |errors|, which must be non-null.
+//
+// The values in |policies| are only valid as long as |extension_value| is (as
+// it references data).
+NET_EXPORT bool ParseCertificatePoliciesExtension(
+ const der::Input& extension_value,
+ std::vector<PolicyInformation>* policies,
+ CertErrors* errors);
+
+// Parses a certificatePolicies extension and stores the policy OIDs in
+// |*policy_oids|, in sorted order.
+//
+// If policyQualifiers for User Notice or CPS are present then they are
+// ignored (RFC 5280 section 4.2.1.4 says "optional qualifiers, which MAY
+// be present, are not expected to change the definition of the policy."
+//
+// If a policy qualifier other than User Notice/CPS is present, parsing
+// will fail if |fail_parsing_unknown_qualifier_oids| was set to true,
+// otherwise the unrecognized qualifiers wil be skipped and not parsed
+// any further.
+//
+// Returns true on success. On failure returns false and may add errors to
+// |errors|, which must be non-null.
+//
+// The values in |policy_oids| are only valid as long as |extension_value| is
+// (as it references data).
+NET_EXPORT bool ParseCertificatePoliciesExtensionOids(
+ const der::Input& extension_value,
+ bool fail_parsing_unknown_qualifier_oids,
+ std::vector<der::Input>* policy_oids,
+ CertErrors* errors);
+
+struct ParsedPolicyConstraints {
+ absl::optional<uint8_t> require_explicit_policy;
+
+ absl::optional<uint8_t> inhibit_policy_mapping;
+};
+
+// Parses a PolicyConstraints SEQUENCE as defined by RFC 5280. Returns true on
+// success, and sets |out|.
+[[nodiscard]] NET_EXPORT bool ParsePolicyConstraints(
+ const der::Input& policy_constraints_tlv,
+ ParsedPolicyConstraints* out);
+
+// Parses an InhibitAnyPolicy as defined by RFC 5280. Returns true on success,
+// and sets |num_certs|.
+[[nodiscard]] NET_EXPORT bool ParseInhibitAnyPolicy(
+ const der::Input& inhibit_any_policy_tlv,
+ uint8_t* num_certs);
+
+struct ParsedPolicyMapping {
+ der::Input issuer_domain_policy;
+ der::Input subject_domain_policy;
+};
+
+// Parses a PolicyMappings SEQUENCE as defined by RFC 5280. Returns true on
+// success, and sets |mappings|.
+[[nodiscard]] NET_EXPORT bool ParsePolicyMappings(
+ const der::Input& policy_mappings_tlv,
+ std::vector<ParsedPolicyMapping>* mappings);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_CERTIFICATE_POLICIES_H_
diff --git a/chromium/net/cert/pki/certificate_policies_unittest.cc b/chromium/net/cert/pki/certificate_policies_unittest.cc
new file mode 100644
index 00000000000..b38aff49a73
--- /dev/null
+++ b/chromium/net/cert/pki/certificate_policies_unittest.cc
@@ -0,0 +1,313 @@
+// Copyright 2015 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 "net/cert/pki/certificate_policies.h"
+
+#include "net/cert/pki/test_helpers.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+::testing::AssertionResult LoadTestData(const std::string& name,
+ std::string* result) {
+ std::string path = "net/data/certificate_policies_unittest/" + name;
+
+ const PemBlockMapping mappings[] = {
+ {"CERTIFICATE POLICIES", result},
+ };
+
+ return ReadTestDataFromPemFile(path, mappings);
+}
+
+const uint8_t policy_1_2_3_der[] = {0x2A, 0x03};
+const uint8_t policy_1_2_4_der[] = {0x2A, 0x04};
+
+class ParseCertificatePoliciesExtensionOidsTest
+ : public testing::TestWithParam<bool> {
+ protected:
+ bool fail_parsing_unknown_qualifier_oids() const { return GetParam(); }
+};
+
+// Run the tests with all possible values for
+// |fail_parsing_unknown_qualifier_oids|.
+INSTANTIATE_TEST_SUITE_P(All,
+ ParseCertificatePoliciesExtensionOidsTest,
+ testing::Bool());
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest, InvalidEmpty) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("invalid-empty.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_FALSE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest, InvalidIdentifierNotOid) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("invalid-policy_identifier_not_oid.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_FALSE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest, AnyPolicy) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("anypolicy.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_TRUE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+ ASSERT_EQ(1U, policies.size());
+ EXPECT_EQ(der::Input(kAnyPolicyOid), policies[0]);
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest, AnyPolicyWithQualifier) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("anypolicy_with_qualifier.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_TRUE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+ ASSERT_EQ(1U, policies.size());
+ EXPECT_EQ(der::Input(kAnyPolicyOid), policies[0]);
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest,
+ InvalidAnyPolicyWithCustomQualifier) {
+ std::string der;
+ ASSERT_TRUE(
+ LoadTestData("invalid-anypolicy_with_custom_qualifier.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_FALSE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest, OnePolicy) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("policy_1_2_3.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_TRUE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+ ASSERT_EQ(1U, policies.size());
+ EXPECT_EQ(der::Input(policy_1_2_3_der), policies[0]);
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest, OnePolicyWithQualifier) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("policy_1_2_3_with_qualifier.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_TRUE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+ ASSERT_EQ(1U, policies.size());
+ EXPECT_EQ(der::Input(policy_1_2_3_der), policies[0]);
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest,
+ OnePolicyWithCustomQualifier) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("policy_1_2_3_with_custom_qualifier.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ bool result = ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors);
+
+ if (fail_parsing_unknown_qualifier_oids()) {
+ EXPECT_FALSE(result);
+ } else {
+ EXPECT_TRUE(result);
+ ASSERT_EQ(1U, policies.size());
+ EXPECT_EQ(der::Input(policy_1_2_3_der), policies[0]);
+ }
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest,
+ InvalidPolicyWithDuplicatePolicyOid) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("invalid-policy_1_2_3_dupe.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_FALSE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest,
+ InvalidPolicyWithEmptyQualifiersSequence) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData(
+ "invalid-policy_1_2_3_with_empty_qualifiers_sequence.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_FALSE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest,
+ InvalidPolicyInformationHasUnconsumedData) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData(
+ "invalid-policy_1_2_3_policyinformation_unconsumed_data.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_FALSE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest,
+ InvalidPolicyQualifierInfoHasUnconsumedData) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData(
+ "invalid-policy_1_2_3_policyqualifierinfo_unconsumed_data.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_FALSE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest, TwoPolicies) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("policy_1_2_3_and_1_2_4.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_TRUE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+ ASSERT_EQ(2U, policies.size());
+ EXPECT_EQ(der::Input(policy_1_2_3_der), policies[0]);
+ EXPECT_EQ(der::Input(policy_1_2_4_der), policies[1]);
+}
+
+TEST_P(ParseCertificatePoliciesExtensionOidsTest, TwoPoliciesWithQualifiers) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("policy_1_2_3_and_1_2_4_with_qualifiers.pem", &der));
+ std::vector<der::Input> policies;
+ CertErrors errors;
+ EXPECT_TRUE(ParseCertificatePoliciesExtensionOids(
+ der::Input(&der), fail_parsing_unknown_qualifier_oids(), &policies,
+ &errors));
+ ASSERT_EQ(2U, policies.size());
+ EXPECT_EQ(der::Input(policy_1_2_3_der), policies[0]);
+ EXPECT_EQ(der::Input(policy_1_2_4_der), policies[1]);
+}
+
+TEST(ParseCertificatePoliciesExtensionTest, InvalidEmpty) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("invalid-empty.pem", &der));
+ std::vector<PolicyInformation> policies;
+ CertErrors errors;
+ EXPECT_FALSE(
+ ParseCertificatePoliciesExtension(der::Input(&der), &policies, &errors));
+}
+
+TEST(ParseCertificatePoliciesExtensionTest,
+ InvalidPolicyWithDuplicatePolicyOid) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("invalid-policy_1_2_3_dupe.pem", &der));
+ std::vector<PolicyInformation> policies;
+ CertErrors errors;
+ EXPECT_FALSE(
+ ParseCertificatePoliciesExtension(der::Input(&der), &policies, &errors));
+}
+
+TEST(ParseCertificatePoliciesExtensionTest, OnePolicyWithCustomQualifier) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("policy_1_2_3_with_custom_qualifier.pem", &der));
+ std::vector<PolicyInformation> policies;
+ CertErrors errors;
+ EXPECT_TRUE(
+ ParseCertificatePoliciesExtension(der::Input(&der), &policies, &errors));
+ ASSERT_EQ(1U, policies.size());
+ PolicyInformation& policy = policies[0];
+ EXPECT_EQ(der::Input(policy_1_2_3_der), policy.policy_oid);
+
+ ASSERT_EQ(1U, policy.policy_qualifiers.size());
+ PolicyQualifierInfo& qualifier = policy.policy_qualifiers[0];
+ // 1.2.3.4
+ const uint8_t kExpectedQualifierOid[] = {0x2a, 0x03, 0x04};
+ EXPECT_EQ(der::Input(kExpectedQualifierOid), qualifier.qualifier_oid);
+ // UTF8String { "hi" }
+ const uint8_t kExpectedQualifier[] = {0x0c, 0x02, 0x68, 0x69};
+ EXPECT_EQ(der::Input(kExpectedQualifier), qualifier.qualifier);
+}
+
+TEST(ParseCertificatePoliciesExtensionTest, TwoPolicies) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("policy_1_2_3_and_1_2_4.pem", &der));
+ std::vector<PolicyInformation> policies;
+ CertErrors errors;
+ EXPECT_TRUE(
+ ParseCertificatePoliciesExtension(der::Input(&der), &policies, &errors));
+ ASSERT_EQ(2U, policies.size());
+ {
+ PolicyInformation& policy = policies[0];
+ EXPECT_EQ(der::Input(policy_1_2_3_der), policy.policy_oid);
+ EXPECT_EQ(0U, policy.policy_qualifiers.size());
+ }
+ {
+ PolicyInformation& policy = policies[1];
+ EXPECT_EQ(der::Input(policy_1_2_4_der), policy.policy_oid);
+ EXPECT_EQ(0U, policy.policy_qualifiers.size());
+ }
+}
+
+TEST(ParseCertificatePoliciesExtensionTest, TwoPoliciesWithQualifiers) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("policy_1_2_3_and_1_2_4_with_qualifiers.pem", &der));
+ std::vector<PolicyInformation> policies;
+ CertErrors errors;
+ EXPECT_TRUE(
+ ParseCertificatePoliciesExtension(der::Input(&der), &policies, &errors));
+ ASSERT_EQ(2U, policies.size());
+ {
+ PolicyInformation& policy = policies[0];
+ EXPECT_EQ(der::Input(policy_1_2_3_der), policy.policy_oid);
+ ASSERT_EQ(1U, policy.policy_qualifiers.size());
+ PolicyQualifierInfo& qualifier = policy.policy_qualifiers[0];
+ EXPECT_EQ(der::Input(kCpsPointerId), qualifier.qualifier_oid);
+ // IA5String { "https://example.com/1_2_3" }
+ const uint8_t kExpectedQualifier[] = {
+ 0x16, 0x19, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x5f, 0x32, 0x5f, 0x33};
+ EXPECT_EQ(der::Input(kExpectedQualifier), qualifier.qualifier);
+ }
+ {
+ PolicyInformation& policy = policies[1];
+ EXPECT_EQ(der::Input(policy_1_2_4_der), policy.policy_oid);
+ ASSERT_EQ(1U, policy.policy_qualifiers.size());
+ PolicyQualifierInfo& qualifier = policy.policy_qualifiers[0];
+ EXPECT_EQ(der::Input(kCpsPointerId), qualifier.qualifier_oid);
+ // IA5String { "http://example.com/1_2_4" }
+ const uint8_t kExpectedQualifier[] = {
+ 0x16, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x31, 0x5f, 0x32, 0x5f, 0x34};
+ EXPECT_EQ(der::Input(kExpectedQualifier), qualifier.qualifier);
+ }
+}
+
+// NOTE: The tests for ParseInhibitAnyPolicy() are part of
+// parsed_certificate_unittest.cc
+
+} // namespace
+} // namespace net
diff --git a/chromium/net/cert/pki/common_cert_errors.cc b/chromium/net/cert/pki/common_cert_errors.cc
new file mode 100644
index 00000000000..d282999c472
--- /dev/null
+++ b/chromium/net/cert/pki/common_cert_errors.cc
@@ -0,0 +1,66 @@
+// Copyright 2017 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 "net/cert/pki/common_cert_errors.h"
+
+namespace net::cert_errors {
+
+DEFINE_CERT_ERROR_ID(kInternalError, "Internal error");
+DEFINE_CERT_ERROR_ID(kValidityFailedNotAfter, "Time is after notAfter");
+DEFINE_CERT_ERROR_ID(kValidityFailedNotBefore, "Time is before notBefore");
+DEFINE_CERT_ERROR_ID(kDistrustedByTrustStore, "Distrusted by trust store");
+
+DEFINE_CERT_ERROR_ID(
+ kSignatureAlgorithmMismatch,
+ "Certificate.signatureAlgorithm != TBSCertificate.signature");
+
+DEFINE_CERT_ERROR_ID(kChainIsEmpty, "Chain is empty");
+DEFINE_CERT_ERROR_ID(kChainIsLength1, "Cannot verify a chain of length 1");
+DEFINE_CERT_ERROR_ID(kUnconsumedCriticalExtension,
+ "Unconsumed critical extension");
+DEFINE_CERT_ERROR_ID(
+ kTargetCertInconsistentCaBits,
+ "Target certificate looks like a CA but does not set all CA properties");
+DEFINE_CERT_ERROR_ID(kKeyCertSignBitNotSet, "keyCertSign bit is not set");
+DEFINE_CERT_ERROR_ID(kMaxPathLengthViolated, "max_path_length reached");
+DEFINE_CERT_ERROR_ID(kBasicConstraintsIndicatesNotCa,
+ "Basic Constraints indicates not a CA");
+DEFINE_CERT_ERROR_ID(kMissingBasicConstraints,
+ "Does not have Basic Constraints");
+DEFINE_CERT_ERROR_ID(kNotPermittedByNameConstraints,
+ "Not permitted by name constraints");
+DEFINE_CERT_ERROR_ID(kTooManyNameConstraintChecks,
+ "Too many name constraints checks");
+DEFINE_CERT_ERROR_ID(kSubjectDoesNotMatchIssuer,
+ "subject does not match issuer");
+DEFINE_CERT_ERROR_ID(kVerifySignedDataFailed, "VerifySignedData failed");
+DEFINE_CERT_ERROR_ID(kSignatureAlgorithmsDifferentEncoding,
+ "Certificate.signatureAlgorithm is encoded differently "
+ "than TBSCertificate.signature");
+DEFINE_CERT_ERROR_ID(kEkuLacksServerAuth,
+ "The extended key usage does not include server auth");
+DEFINE_CERT_ERROR_ID(kEkuLacksServerAuthButHasGatedCrypto,
+ "The extended key usage does not include server auth but "
+ "instead includes Netscape Server Gated Crypto");
+DEFINE_CERT_ERROR_ID(kEkuLacksClientAuth,
+ "The extended key usage does not include client auth");
+DEFINE_CERT_ERROR_ID(kCertIsNotTrustAnchor,
+ "Certificate is not a trust anchor");
+DEFINE_CERT_ERROR_ID(kNoValidPolicy, "No valid policy");
+DEFINE_CERT_ERROR_ID(kPolicyMappingAnyPolicy,
+ "PolicyMappings must not map anyPolicy");
+DEFINE_CERT_ERROR_ID(kFailedParsingSpki, "Couldn't parse SubjectPublicKeyInfo");
+DEFINE_CERT_ERROR_ID(kUnacceptableSignatureAlgorithm,
+ "Unacceptable signature algorithm");
+DEFINE_CERT_ERROR_ID(kUnacceptablePublicKey, "Unacceptable public key");
+DEFINE_CERT_ERROR_ID(kCertificateRevoked, "Certificate is revoked");
+DEFINE_CERT_ERROR_ID(kNoRevocationMechanism,
+ "Certificate lacks a revocation mechanism");
+DEFINE_CERT_ERROR_ID(kUnableToCheckRevocation, "Unable to check revocation");
+DEFINE_CERT_ERROR_ID(kNoIssuersFound, "No matching issuer found");
+DEFINE_CERT_ERROR_ID(kDeadlineExceeded, "Deadline exceeded");
+DEFINE_CERT_ERROR_ID(kIterationLimitExceeded, "Iteration limit exceeded");
+DEFINE_CERT_ERROR_ID(kDepthLimitExceeded, "Depth limit exceeded");
+
+} // namespace net::cert_errors
diff --git a/chromium/net/cert/pki/common_cert_errors.h b/chromium/net/cert/pki/common_cert_errors.h
new file mode 100644
index 00000000000..2819671f4c9
--- /dev/null
+++ b/chromium/net/cert/pki/common_cert_errors.h
@@ -0,0 +1,145 @@
+// Copyright 2017 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 NET_CERT_PKI_COMMON_CERT_ERRORS_H_
+#define NET_CERT_PKI_COMMON_CERT_ERRORS_H_
+
+#include "net/base/net_export.h"
+#include "net/cert/pki/cert_errors.h"
+
+// This file contains the set of "default" certificate errors (those
+// defined by the core verification/path building code).
+//
+// Errors may be defined for other domains.
+namespace net::cert_errors {
+
+// An internal error occurred which prevented path building or verification
+// from finishing.
+NET_EXPORT extern const CertErrorId kInternalError;
+
+// The verification time is after the certificate's notAfter time.
+NET_EXPORT extern const CertErrorId kValidityFailedNotAfter;
+
+// The verification time is before the certificate's notBefore time.
+NET_EXPORT extern const CertErrorId kValidityFailedNotBefore;
+
+// The certificate is actively distrusted by the trust store (this is separate
+// from other revocation mechanisms).
+NET_EXPORT extern const CertErrorId kDistrustedByTrustStore;
+
+// The certificate disagrees on what the signature algorithm was
+// (Certificate.signatureAlgorithm != TBSCertificate.signature).
+NET_EXPORT extern const CertErrorId kSignatureAlgorithmMismatch;
+
+// Certificate verification was called with an empty chain.
+NET_EXPORT extern const CertErrorId kChainIsEmpty;
+
+// Certificate verification was called with a chain of length 1, which is not
+// supported (i.e. the target certificate cannot also be a trusted
+// certificate). See https://crbug.com/814994.
+NET_EXPORT extern const CertErrorId kChainIsLength1;
+
+// The certificate contains an unknown extension which is marked as critical.
+NET_EXPORT extern const CertErrorId kUnconsumedCriticalExtension;
+
+// The target certificate appears to be a CA (has Basic Constraints CA=true),
+// however does not have a keyUsage consistent with being a CA (keyCertSign).
+NET_EXPORT extern const CertErrorId kTargetCertInconsistentCaBits;
+
+// The certificate is being used to sign other certificates, however the
+// keyCertSign KeyUsage was not set.
+NET_EXPORT extern const CertErrorId kKeyCertSignBitNotSet;
+
+// The chain violates the max_path_length from BasicConstraints.
+NET_EXPORT extern const CertErrorId kMaxPathLengthViolated;
+
+// The certificate being used to sign other certificates has a
+// BasicConstraints extension, however it sets CA=false
+NET_EXPORT extern const CertErrorId kBasicConstraintsIndicatesNotCa;
+
+// The certificate being used to sign other certificates does not include a
+// BasicConstraints extension.
+NET_EXPORT extern const CertErrorId kMissingBasicConstraints;
+
+// The certificate has a subject or subjectAltName that violates an issuer's
+// name constraints.
+NET_EXPORT extern const CertErrorId kNotPermittedByNameConstraints;
+
+// The chain has an excessive number of names and/or name constraints.
+NET_EXPORT extern const CertErrorId kTooManyNameConstraintChecks;
+
+// The certificate's issuer field does not match the subject of its alleged
+// issuer.
+NET_EXPORT extern const CertErrorId kSubjectDoesNotMatchIssuer;
+
+// Failed to verify the certificate's signature using its issuer's public key.
+NET_EXPORT extern const CertErrorId kVerifySignedDataFailed;
+
+// The certificate encodes its signature differently between
+// Certificate.algorithm and TBSCertificate.signature, but it appears
+// to be the same algorithm.
+NET_EXPORT extern const CertErrorId kSignatureAlgorithmsDifferentEncoding;
+
+// The certificate verification is being done for serverAuth, however the
+// certificate lacks serverAuth in its ExtendedKeyUsages.
+NET_EXPORT extern const CertErrorId kEkuLacksServerAuth;
+
+// The certificate verification is being done for clientAuth, however the
+// certificate lacks clientAuth in its ExtendedKeyUsages.
+NET_EXPORT extern const CertErrorId kEkuLacksClientAuth;
+
+// The root certificate in a chain is not trusted.
+NET_EXPORT extern const CertErrorId kCertIsNotTrustAnchor;
+
+// The chain is not valid for any policy, and an explicit policy was required.
+// (Either because the relying party requested it during verificaiton, or it was
+// requrested by a PolicyConstraints extension).
+NET_EXPORT extern const CertErrorId kNoValidPolicy;
+
+// The certificate is trying to map to, or from, anyPolicy.
+NET_EXPORT extern const CertErrorId kPolicyMappingAnyPolicy;
+
+// The public key in this certificate could not be parsed.
+NET_EXPORT extern const CertErrorId kFailedParsingSpki;
+
+// The certificate's signature algorithm (used to verify its
+// signature) is not acceptable by the consumer. What constitutes as
+// "acceptable" is determined by the verification delegate.
+NET_EXPORT extern const CertErrorId kUnacceptableSignatureAlgorithm;
+
+// The certificate's public key is not acceptable by the consumer.
+// What constitutes as "acceptable" is determined by the verification delegate.
+NET_EXPORT extern const CertErrorId kUnacceptablePublicKey;
+
+// The certificate's EKU is missing serverAuth. However Netscape Server Gated
+// Crypto is present instead.
+NET_EXPORT extern const CertErrorId kEkuLacksServerAuthButHasGatedCrypto;
+
+// The certificate has been revoked.
+NET_EXPORT extern const CertErrorId kCertificateRevoked;
+
+// The certificate lacks a recognized revocation mechanism (i.e. OCSP/CRL).
+// Emitted as an error when revocation checking expects certificates to have
+// such info.
+NET_EXPORT extern const CertErrorId kNoRevocationMechanism;
+
+// The certificate had a revocation mechanism, but when used it was unable to
+// affirmatively say whether the certificate was unrevoked.
+NET_EXPORT extern const CertErrorId kUnableToCheckRevocation;
+
+// Path building was unable to find any issuers for the certificate.
+NET_EXPORT extern const CertErrorId kNoIssuersFound;
+
+// Deadline was reached during path building.
+NET_EXPORT extern const CertErrorId kDeadlineExceeded;
+
+// Iteration limit was reached during path building.
+NET_EXPORT extern const CertErrorId kIterationLimitExceeded;
+
+// Depth limit was reached during path building.
+NET_EXPORT extern const CertErrorId kDepthLimitExceeded;
+
+} // namespace net::cert_errors
+
+#endif // NET_CERT_PKI_COMMON_CERT_ERRORS_H_
diff --git a/chromium/net/cert/pki/crl.cc b/chromium/net/cert/pki/crl.cc
new file mode 100644
index 00000000000..c3a0c9dc5fa
--- /dev/null
+++ b/chromium/net/cert/pki/crl.cc
@@ -0,0 +1,623 @@
+// Copyright 2019 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 "net/cert/pki/crl.h"
+
+#include "base/stl_util.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/revocation_util.h"
+#include "net/cert/pki/signature_algorithm.h"
+#include "net/cert/pki/verify_name_match.h"
+#include "net/cert/pki/verify_signed_data.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "net/der/parser.h"
+#include "net/der/tag.h"
+
+namespace net {
+
+namespace {
+
+// id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 }
+// In dotted notation: 2.5.29.28
+inline constexpr uint8_t kIssuingDistributionPointOid[] = {0x55, 0x1d, 0x1c};
+
+[[nodiscard]] bool NormalizeNameTLV(const der::Input& name_tlv,
+ std::string* out_normalized_name) {
+ der::Parser parser(name_tlv);
+ der::Input name_rdn;
+ net::CertErrors unused_errors;
+ return parser.ReadTag(der::kSequence, &name_rdn) &&
+ NormalizeName(name_rdn, out_normalized_name, &unused_errors) &&
+ !parser.HasMore();
+}
+
+bool ContainsExactMatchingName(std::vector<base::StringPiece> a,
+ std::vector<base::StringPiece> b) {
+ std::sort(a.begin(), a.end());
+ std::sort(b.begin(), b.end());
+ return !base::STLSetIntersection<std::vector<base::StringPiece>>(a, b)
+ .empty();
+}
+
+} // namespace
+
+bool ParseCrlCertificateList(const der::Input& crl_tlv,
+ der::Input* out_tbs_cert_list_tlv,
+ der::Input* out_signature_algorithm_tlv,
+ der::BitString* out_signature_value) {
+ der::Parser parser(crl_tlv);
+
+ // CertificateList ::= SEQUENCE {
+ der::Parser certificate_list_parser;
+ if (!parser.ReadSequence(&certificate_list_parser))
+ return false;
+
+ // tbsCertList TBSCertList
+ if (!certificate_list_parser.ReadRawTLV(out_tbs_cert_list_tlv))
+ return false;
+
+ // signatureAlgorithm AlgorithmIdentifier,
+ if (!certificate_list_parser.ReadRawTLV(out_signature_algorithm_tlv))
+ return false;
+
+ // signatureValue BIT STRING }
+ absl::optional<der::BitString> signature_value =
+ certificate_list_parser.ReadBitString();
+ if (!signature_value)
+ return false;
+ *out_signature_value = signature_value.value();
+
+ // There isn't an extension point at the end of CertificateList.
+ if (certificate_list_parser.HasMore())
+ return false;
+
+ // By definition the input was a single CertificateList, so there shouldn't be
+ // unconsumed data.
+ if (parser.HasMore())
+ return false;
+
+ return true;
+}
+
+bool ParseCrlTbsCertList(const der::Input& tbs_tlv, ParsedCrlTbsCertList* out) {
+ der::Parser parser(tbs_tlv);
+
+ // TBSCertList ::= SEQUENCE {
+ der::Parser tbs_parser;
+ if (!parser.ReadSequence(&tbs_parser))
+ return false;
+
+ // version Version OPTIONAL,
+ // -- if present, MUST be v2
+ absl::optional<der::Input> version_der;
+ if (!tbs_parser.ReadOptionalTag(der::kInteger, &version_der))
+ return false;
+ if (version_der.has_value()) {
+ uint64_t version64;
+ if (!der::ParseUint64(*version_der, &version64))
+ return false;
+ // If version is present, it MUST be v2(1).
+ if (version64 != 1)
+ return false;
+ out->version = CrlVersion::V2;
+ } else {
+ // Uh, RFC 5280 doesn't actually say it anywhere, but presumably if version
+ // is not specified, it is V1.
+ out->version = CrlVersion::V1;
+ }
+
+ // signature AlgorithmIdentifier,
+ if (!tbs_parser.ReadRawTLV(&out->signature_algorithm_tlv))
+ return false;
+
+ // issuer Name,
+ if (!tbs_parser.ReadRawTLV(&out->issuer_tlv))
+ return false;
+
+ // thisUpdate Time,
+ if (!ReadUTCOrGeneralizedTime(&tbs_parser, &out->this_update))
+ return false;
+
+ // nextUpdate Time OPTIONAL,
+ der::Tag maybe_next_update_tag;
+ der::Input unused_next_update_input;
+ if (tbs_parser.PeekTagAndValue(&maybe_next_update_tag,
+ &unused_next_update_input) &&
+ (maybe_next_update_tag == der::kUtcTime ||
+ maybe_next_update_tag == der::kGeneralizedTime)) {
+ der::GeneralizedTime next_update_time;
+ if (!ReadUTCOrGeneralizedTime(&tbs_parser, &next_update_time))
+ return false;
+ out->next_update = next_update_time;
+ } else {
+ out->next_update = absl::nullopt;
+ }
+
+ // revokedCertificates SEQUENCE OF SEQUENCE { ... } OPTIONAL,
+ der::Input unused_revoked_certificates;
+ der::Tag maybe_revoked_certifigates_tag;
+ if (tbs_parser.PeekTagAndValue(&maybe_revoked_certifigates_tag,
+ &unused_revoked_certificates) &&
+ maybe_revoked_certifigates_tag == der::kSequence) {
+ der::Input revoked_certificates_tlv;
+ if (!tbs_parser.ReadRawTLV(&revoked_certificates_tlv))
+ return false;
+ out->revoked_certificates_tlv = revoked_certificates_tlv;
+ } else {
+ out->revoked_certificates_tlv = absl::nullopt;
+ }
+
+ // crlExtensions [0] EXPLICIT Extensions OPTIONAL
+ // -- if present, version MUST be v2
+ if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
+ &out->crl_extensions_tlv)) {
+ return false;
+ }
+ if (out->crl_extensions_tlv.has_value()) {
+ if (out->version != CrlVersion::V2)
+ return false;
+ }
+
+ if (tbs_parser.HasMore()) {
+ // Invalid or extraneous elements.
+ return false;
+ }
+
+ // By definition the input was a single sequence, so there shouldn't be
+ // unconsumed data.
+ if (parser.HasMore())
+ return false;
+
+ return true;
+}
+
+bool ParseIssuingDistributionPoint(
+ const der::Input& extension_value,
+ std::unique_ptr<GeneralNames>* out_distribution_point_names,
+ ContainedCertsType* out_only_contains_cert_type) {
+ der::Parser idp_extension_value_parser(extension_value);
+ // IssuingDistributionPoint ::= SEQUENCE {
+ der::Parser idp_parser;
+ if (!idp_extension_value_parser.ReadSequence(&idp_parser))
+ return false;
+
+ // 5.2.5. Conforming CRLs issuers MUST NOT issue CRLs where the DER
+ // encoding of the issuing distribution point extension is an empty
+ // sequence.
+ if (!idp_parser.HasMore())
+ return false;
+
+ // distributionPoint [0] DistributionPointName OPTIONAL,
+ absl::optional<der::Input> distribution_point;
+ if (!idp_parser.ReadOptionalTag(
+ der::kTagContextSpecific | der::kTagConstructed | 0,
+ &distribution_point)) {
+ return false;
+ }
+
+ if (distribution_point.has_value()) {
+ // DistributionPointName ::= CHOICE {
+ der::Parser dp_name_parser(*distribution_point);
+ // fullName [0] GeneralNames,
+ // nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
+ absl::optional<der::Input> der_full_name;
+ if (!dp_name_parser.ReadOptionalTag(
+ der::kTagContextSpecific | der::kTagConstructed | 0,
+ &der_full_name)) {
+ return false;
+ }
+ if (!der_full_name) {
+ // Only fullName is supported.
+ return false;
+ }
+ CertErrors errors;
+ *out_distribution_point_names =
+ GeneralNames::CreateFromValue(*der_full_name, &errors);
+ if (!*out_distribution_point_names)
+ return false;
+
+ if (dp_name_parser.HasMore()) {
+ // CHOICE represents a single value.
+ return false;
+ }
+ }
+
+ *out_only_contains_cert_type = ContainedCertsType::ANY_CERTS;
+
+ // onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE,
+ absl::optional<der::Input> only_contains_user_certs;
+ if (!idp_parser.ReadOptionalTag(der::kTagContextSpecific | 1,
+ &only_contains_user_certs)) {
+ return false;
+ }
+ if (only_contains_user_certs.has_value()) {
+ bool bool_value;
+ if (!der::ParseBool(*only_contains_user_certs, &bool_value))
+ return false;
+ if (!bool_value)
+ return false; // DER-encoding requires DEFAULT values be omitted.
+ *out_only_contains_cert_type = ContainedCertsType::USER_CERTS;
+ }
+
+ // onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE,
+ absl::optional<der::Input> only_contains_ca_certs;
+ if (!idp_parser.ReadOptionalTag(der::kTagContextSpecific | 2,
+ &only_contains_ca_certs)) {
+ return false;
+ }
+ if (only_contains_ca_certs.has_value()) {
+ bool bool_value;
+ if (!der::ParseBool(*only_contains_ca_certs, &bool_value))
+ return false;
+ if (!bool_value)
+ return false; // DER-encoding requires DEFAULT values be omitted.
+ if (*out_only_contains_cert_type != ContainedCertsType::ANY_CERTS) {
+ // 5.2.5. at most one of onlyContainsUserCerts, onlyContainsCACerts,
+ // and onlyContainsAttributeCerts may be set to TRUE.
+ return false;
+ }
+ *out_only_contains_cert_type = ContainedCertsType::CA_CERTS;
+ }
+
+ // onlySomeReasons [3] ReasonFlags OPTIONAL,
+ // indirectCRL [4] BOOLEAN DEFAULT FALSE,
+ // onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
+ // onlySomeReasons, indirectCRL, and onlyContainsAttributeCerts are not
+ // supported, fail parsing if they are present.
+ if (idp_parser.HasMore())
+ return false;
+
+ return true;
+}
+
+CRLRevocationStatus GetCRLStatusForCert(
+ const der::Input& cert_serial,
+ CrlVersion crl_version,
+ const absl::optional<der::Input>& revoked_certificates_tlv) {
+ if (!revoked_certificates_tlv.has_value()) {
+ // RFC 5280 Section 5.1.2.6: "When there are no revoked certificates, the
+ // revoked certificates list MUST be absent."
+ // No covered certificates are revoked, therefore the cert is good.
+ return CRLRevocationStatus::GOOD;
+ }
+
+ der::Parser parser(*revoked_certificates_tlv);
+
+ // revokedCertificates SEQUENCE OF SEQUENCE {
+ der::Parser revoked_certificates_parser;
+ if (!parser.ReadSequence(&revoked_certificates_parser))
+ return CRLRevocationStatus::UNKNOWN;
+
+ // RFC 5280 Section 5.1.2.6: "When there are no revoked certificates, the
+ // revoked certificates list MUST be absent."
+ if (!revoked_certificates_parser.HasMore())
+ return CRLRevocationStatus::UNKNOWN;
+
+ // By definition the input was a single Extensions sequence, so there
+ // shouldn't be unconsumed data.
+ if (parser.HasMore())
+ return CRLRevocationStatus::UNKNOWN;
+
+ bool found_matching_serial = false;
+
+ while (revoked_certificates_parser.HasMore()) {
+ // revokedCertificates SEQUENCE OF SEQUENCE {
+ der::Parser crl_entry_parser;
+ if (!revoked_certificates_parser.ReadSequence(&crl_entry_parser))
+ return CRLRevocationStatus::UNKNOWN;
+
+ der::Input revoked_cert_serial_number;
+ // userCertificate CertificateSerialNumber,
+ if (!crl_entry_parser.ReadTag(der::kInteger, &revoked_cert_serial_number))
+ return CRLRevocationStatus::UNKNOWN;
+
+ // revocationDate Time,
+ der::GeneralizedTime unused_revocation_date;
+ if (!ReadUTCOrGeneralizedTime(&crl_entry_parser, &unused_revocation_date))
+ return CRLRevocationStatus::UNKNOWN;
+
+ // crlEntryExtensions Extensions OPTIONAL
+ if (crl_entry_parser.HasMore()) {
+ // -- if present, version MUST be v2
+ if (crl_version != CrlVersion::V2)
+ return CRLRevocationStatus::UNKNOWN;
+
+ der::Input crl_entry_extensions_tlv;
+ if (!crl_entry_parser.ReadRawTLV(&crl_entry_extensions_tlv))
+ return CRLRevocationStatus::UNKNOWN;
+
+ std::map<der::Input, ParsedExtension> extensions;
+ if (!ParseExtensions(crl_entry_extensions_tlv, &extensions))
+ return CRLRevocationStatus::UNKNOWN;
+
+ // RFC 5280 Section 5.3: "If a CRL contains a critical CRL entry
+ // extension that the application cannot process, then the application
+ // MUST NOT use that CRL to determine the status of any certificates."
+ for (const auto& ext : extensions) {
+ if (ext.second.critical)
+ return CRLRevocationStatus::UNKNOWN;
+ }
+ }
+
+ if (crl_entry_parser.HasMore())
+ return CRLRevocationStatus::UNKNOWN;
+
+ if (revoked_cert_serial_number == cert_serial) {
+ // Cert is revoked, but can't return yet since there might be critical
+ // extensions on later entries that would prevent use of this CRL.
+ found_matching_serial = true;
+ }
+ }
+
+ if (found_matching_serial)
+ return CRLRevocationStatus::REVOKED;
+
+ // |cert| is not present in the revokedCertificates list.
+ return CRLRevocationStatus::GOOD;
+}
+
+ParsedCrlTbsCertList::ParsedCrlTbsCertList() = default;
+ParsedCrlTbsCertList::~ParsedCrlTbsCertList() = default;
+
+CRLRevocationStatus CheckCRL(base::StringPiece raw_crl,
+ const ParsedCertificateList& valid_chain,
+ size_t target_cert_index,
+ const ParsedDistributionPoint& cert_dp,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age) {
+ DCHECK_LT(target_cert_index, valid_chain.size());
+
+ if (cert_dp.reasons) {
+ // Reason codes are not supported. If the distribution point contains a
+ // subset of reasons then skip it. We aren't interested in subsets of CRLs
+ // and the RFC states that there MUST be a CRL that covers all reasons.
+ return CRLRevocationStatus::UNKNOWN;
+ }
+ if (cert_dp.crl_issuer) {
+ // Indirect CRLs are not supported.
+ return CRLRevocationStatus::UNKNOWN;
+ }
+
+ const ParsedCertificate* target_cert = valid_chain[target_cert_index].get();
+
+ // 6.3.3 (a) Update the local CRL cache by obtaining a complete CRL, a
+ // delta CRL, or both, as required.
+ //
+ // This implementation only supports complete CRLs and takes the CRL as
+ // input, it is up to the caller to provide an up to date CRL.
+
+ der::Input tbs_cert_list_tlv;
+ der::Input signature_algorithm_tlv;
+ der::BitString signature_value;
+ if (!ParseCrlCertificateList(der::Input(raw_crl), &tbs_cert_list_tlv,
+ &signature_algorithm_tlv, &signature_value)) {
+ return CRLRevocationStatus::UNKNOWN;
+ }
+
+ ParsedCrlTbsCertList tbs_cert_list;
+ if (!ParseCrlTbsCertList(tbs_cert_list_tlv, &tbs_cert_list))
+ return CRLRevocationStatus::UNKNOWN;
+
+ // 5.1.1.2 signatureAlgorithm
+ //
+ // TODO(https://crbug.com/749276): Check the signature algorithm against
+ // policy.
+ absl::optional<SignatureAlgorithm> signature_algorithm =
+ ParseSignatureAlgorithm(signature_algorithm_tlv,
+ /*errors=*/nullptr);
+ if (!signature_algorithm) {
+ return CRLRevocationStatus::UNKNOWN;
+ }
+
+ // This field MUST contain the same algorithm identifier as the
+ // signature field in the sequence tbsCertList (Section 5.1.2.2).
+ absl::optional<SignatureAlgorithm> tbs_alg =
+ ParseSignatureAlgorithm(tbs_cert_list.signature_algorithm_tlv,
+ /*errors=*/nullptr);
+ if (!tbs_alg || *signature_algorithm != *tbs_alg) {
+ return CRLRevocationStatus::UNKNOWN;
+ }
+
+ // Check CRL dates. Roughly corresponds to 6.3.3 (a) (1) but does not attempt
+ // to update the CRL if it is out of date.
+ if (!CheckRevocationDateValid(
+ tbs_cert_list.this_update,
+ base::OptionalOrNullptr(tbs_cert_list.next_update), verify_time,
+ max_age)) {
+ return CRLRevocationStatus::UNKNOWN;
+ }
+
+ // 6.3.3 (a) (2) is skipped: This implementation does not support delta CRLs.
+
+ // 6.3.3 (b) Verify the issuer and scope of the complete CRL as follows:
+ // 6.3.3 (b) (1) If the DP includes cRLIssuer, then verify that the issuer
+ // field in the complete CRL matches cRLIssuer in the DP and
+ // that the complete CRL contains an issuing distribution
+ // point extension with the indirectCRL boolean asserted.
+ //
+ // Nothing is done here since distribution points with crlIssuer were skipped
+ // above.
+
+ // 6.3.3 (b) (1) Otherwise, verify that the CRL issuer matches the
+ // certificate issuer.
+ //
+ // Normalization for the name comparison is used although the RFC is not
+ // clear on this. There are several places that explicitly are called out as
+ // requiring identical encodings:
+ //
+ // 4.2.1.13. CRL Distribution Points (cert extension) says the DP cRLIssuer
+ // field MUST be exactly the same as the encoding in issuer field of the
+ // CRL.
+ //
+ // 5.2.5. Issuing Distribution Point (crl extension)
+ // The identical encoding MUST be used in the distributionPoint fields
+ // of the certificate and the CRL.
+ //
+ // 5.3.3. Certificate Issuer (crl entry extension) also says "The encoding of
+ // the DN MUST be identical to the encoding used in the certificate"
+ //
+ // But 6.3.3 (b) (1) just says "matches". Also NIST PKITS includes at least
+ // one test that requires normalization here.
+ // TODO(https://crbug.com/749276): could do exact comparison first and only
+ // fall back to normalizing if that fails.
+ std::string normalized_crl_issuer;
+ if (!NormalizeNameTLV(tbs_cert_list.issuer_tlv, &normalized_crl_issuer))
+ return CRLRevocationStatus::UNKNOWN;
+ if (der::Input(&normalized_crl_issuer) != target_cert->normalized_issuer())
+ return CRLRevocationStatus::UNKNOWN;
+
+ if (tbs_cert_list.crl_extensions_tlv.has_value()) {
+ std::map<der::Input, ParsedExtension> extensions;
+ if (!ParseExtensions(*tbs_cert_list.crl_extensions_tlv, &extensions))
+ return CRLRevocationStatus::UNKNOWN;
+
+ // 6.3.3 (b) (2) If the complete CRL includes an issuing distribution point
+ // (IDP) CRL extension, check the following:
+ ParsedExtension idp_extension;
+ if (ConsumeExtension(der::Input(kIssuingDistributionPointOid), &extensions,
+ &idp_extension)) {
+ std::unique_ptr<GeneralNames> distribution_point_names;
+ ContainedCertsType only_contains_cert_type;
+ if (!ParseIssuingDistributionPoint(idp_extension.value,
+ &distribution_point_names,
+ &only_contains_cert_type)) {
+ return CRLRevocationStatus::UNKNOWN;
+ }
+
+ if (distribution_point_names) {
+ // 6.3.3. (b) (2) (i) If the distribution point name is present in the
+ // IDP CRL extension and the distribution field is
+ // present in the DP, then verify that one of the
+ // names in the IDP matches one of the names in the
+ // DP.
+ // 5.2.5. The identical encoding MUST be used in the distributionPoint
+ // fields of the certificate and the CRL.
+ // TODO(https://crbug.com/749276): Check other name types?
+ if (!cert_dp.distribution_point_fullname ||
+ !ContainsExactMatchingName(
+ cert_dp.distribution_point_fullname
+ ->uniform_resource_identifiers,
+ distribution_point_names->uniform_resource_identifiers)) {
+ return CRLRevocationStatus::UNKNOWN;
+ }
+
+ // 6.3.3. (b) (2) (i) If the distribution point name is present in the
+ // IDP CRL extension and the distribution field is
+ // omitted from the DP, then verify that one of the
+ // names in the IDP matches one of the names in the
+ // cRLIssuer field of the DP.
+ // Indirect CRLs are not supported, if indirectCRL was specified,
+ // ParseIssuingDistributionPoint would already have failed.
+ }
+
+ switch (only_contains_cert_type) {
+ case ContainedCertsType::USER_CERTS:
+ // 6.3.3. (b) (2) (ii) If the onlyContainsUserCerts boolean is
+ // asserted in the IDP CRL extension, verify
+ // that the certificate does not include the
+ // basic constraints extension with the cA
+ // boolean asserted.
+ // 5.2.5. If either onlyContainsUserCerts or onlyContainsCACerts is
+ // set to TRUE, then the scope of the CRL MUST NOT include any
+ // version 1 or version 2 certificates.
+ if ((target_cert->has_basic_constraints() &&
+ target_cert->basic_constraints().is_ca) ||
+ target_cert->tbs().version == CertificateVersion::V1 ||
+ target_cert->tbs().version == CertificateVersion::V2) {
+ return CRLRevocationStatus::UNKNOWN;
+ }
+ break;
+
+ case ContainedCertsType::CA_CERTS:
+ // 6.3.3. (b) (2) (iii) If the onlyContainsCACerts boolean is asserted
+ // in the IDP CRL extension, verify that the
+ // certificate includes the basic constraints
+ // extension with the cA boolean asserted.
+ // The version check is not done here, as the basicConstraints
+ // extension is required, and could not be present unless it is a V3
+ // certificate.
+ if (!target_cert->has_basic_constraints() ||
+ !target_cert->basic_constraints().is_ca) {
+ return CRLRevocationStatus::UNKNOWN;
+ }
+ break;
+
+ case ContainedCertsType::ANY_CERTS:
+ // (iv) Verify that the onlyContainsAttributeCerts
+ // boolean is not asserted.
+ // If onlyContainsAttributeCerts was present,
+ // ParseIssuingDistributionPoint would already have failed.
+ break;
+ }
+ }
+
+ for (const auto& ext : extensions) {
+ // Fail if any unhandled critical CRL extensions are present.
+ if (ext.second.critical)
+ return CRLRevocationStatus::UNKNOWN;
+ }
+ }
+
+ // 6.3.3 (c-e) skipped: delta CRLs and reason codes are not supported.
+
+ // This implementation only supports direct CRLs where the CRL was signed by
+ // one of the certs in its validated issuer chain. This allows handling some
+ // cases of key rollover without requiring additional CRL issuer cert
+ // discovery & path building.
+ // TODO(https://crbug.com/749276): should this loop start at
+ // |target_cert_index|? There doesn't seem to be anything in the specs that
+ // precludes a CRL signed by a self-issued cert from covering itself. On the
+ // other hand it seems like a pretty weird thing to allow and causes NIST
+ // PKITS 4.5.3 to pass when it seems like it would not be intended to (since
+ // issuingDistributionPoint CRL extension is not handled).
+ for (size_t i = target_cert_index + 1; i < valid_chain.size(); ++i) {
+ const ParsedCertificate* issuer_cert = valid_chain[i].get();
+
+ // 6.3.3 (f) Obtain and validate the certification path for the issuer of
+ // the complete CRL. The trust anchor for the certification
+ // path MUST be the same as the trust anchor used to validate
+ // the target certificate.
+ //
+ // As the |issuer_cert| is from the already validated chain, it is already
+ // known to chain to the same trust anchor as the target certificate.
+ if (der::Input(&normalized_crl_issuer) != issuer_cert->normalized_subject())
+ continue;
+
+ // 6.3.3 (f) If a key usage extension is present in the CRL issuer's
+ // certificate, verify that the cRLSign bit is set.
+ if (issuer_cert->has_key_usage() &&
+ !issuer_cert->key_usage().AssertsBit(KEY_USAGE_BIT_CRL_SIGN)) {
+ continue;
+ }
+
+ // 6.3.3 (g) Validate the signature on the complete CRL using the public
+ // key validated in step (f).
+ if (!VerifySignedData(*signature_algorithm, tbs_cert_list_tlv,
+ signature_value, issuer_cert->tbs().spki_tlv)) {
+ continue;
+ }
+
+ // 6.3.3 (h,i) skipped. This implementation does not support delta CRLs.
+
+ // 6.3.3 (j) If (cert_status is UNREVOKED), then search for the
+ // certificate on the complete CRL. If an entry is found that
+ // matches the certificate issuer and serial number as described
+ // in Section 5.3.3, then set the cert_status variable to the
+ // indicated reason as described in step (i).
+ //
+ // CRL is valid and covers |target_cert|, check if |target_cert| is present
+ // in the revokedCertificates sequence.
+ return GetCRLStatusForCert(target_cert->tbs().serial_number,
+ tbs_cert_list.version,
+ tbs_cert_list.revoked_certificates_tlv);
+
+ // 6.3.3 (k,l) skipped. This implementation does not support reason codes.
+ }
+
+ // Did not find the issuer & signer of |raw_crl| in |valid_chain|.
+ return CRLRevocationStatus::UNKNOWN;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/crl.h b/chromium/net/cert/pki/crl.h
new file mode 100644
index 00000000000..e6add49add4
--- /dev/null
+++ b/chromium/net/cert/pki/crl.h
@@ -0,0 +1,224 @@
+// Copyright 2019 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 NET_CERT_PKI_CRL_H_
+#define NET_CERT_PKI_CRL_H_
+
+#include "base/strings/string_piece_forward.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/general_names.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+
+struct ParsedCrlTbsCertList;
+struct ParsedDistributionPoint;
+
+// TODO(https://crbug.com/749276): This is the same enum with the same meaning
+// as OCSPRevocationStatus, maybe they should be merged?
+enum class CRLRevocationStatus {
+ GOOD = 0,
+ REVOKED = 1,
+ UNKNOWN = 2,
+ MAX_VALUE = UNKNOWN
+};
+
+// Parses a DER-encoded CRL "CertificateList" as specified by RFC 5280 Section
+// 5.1. Returns true on success and sets the results in the |out_*| parameters.
+// The contents of the output data is not validated.
+//
+// Note that on success the out parameters alias data from the input |crl_tlv|.
+// Hence the output values are only valid as long as |crl_tlv| remains valid.
+//
+// On failure the out parameters have an undefined state. Some of them may have
+// been updated during parsing, whereas others may not have been changed.
+//
+// CertificateList ::= SEQUENCE {
+// tbsCertList TBSCertList,
+// signatureAlgorithm AlgorithmIdentifier,
+// signatureValue BIT STRING }
+[[nodiscard]] NET_EXPORT_PRIVATE bool ParseCrlCertificateList(
+ const der::Input& crl_tlv,
+ der::Input* out_tbs_cert_list_tlv,
+ der::Input* out_signature_algorithm_tlv,
+ der::BitString* out_signature_value);
+
+// Parses a DER-encoded "TBSCertList" as specified by RFC 5280 Section 5.1.
+// Returns true on success and sets the results in |out|.
+//
+// Note that on success |out| aliases data from the input |tbs_tlv|.
+// Hence the fields of the ParsedCrlTbsCertList are only valid as long as
+// |tbs_tlv| remains valid.
+//
+// On failure |out| has an undefined state. Some of its fields may have been
+// updated during parsing, whereas others may not have been changed.
+//
+// Refer to the per-field documentation of ParsedCrlTbsCertList for details on
+// what validity checks parsing performs.
+//
+// TBSCertList ::= SEQUENCE {
+// version Version OPTIONAL,
+// -- if present, MUST be v2
+// signature AlgorithmIdentifier,
+// issuer Name,
+// thisUpdate Time,
+// nextUpdate Time OPTIONAL,
+// revokedCertificates SEQUENCE OF SEQUENCE {
+// userCertificate CertificateSerialNumber,
+// revocationDate Time,
+// crlEntryExtensions Extensions OPTIONAL
+// -- if present, version MUST be v2
+// } OPTIONAL,
+// crlExtensions [0] EXPLICIT Extensions OPTIONAL
+// -- if present, version MUST be v2
+// }
+[[nodiscard]] NET_EXPORT_PRIVATE bool ParseCrlTbsCertList(
+ const der::Input& tbs_tlv,
+ ParsedCrlTbsCertList* out);
+
+// Represents a CRL "Version" from RFC 5280. TBSCertList reuses the same
+// Version definition from TBSCertificate, however only v1(not present) and
+// v2(1) are valid values, so a unique enum is used to avoid confusion.
+enum class CrlVersion {
+ V1,
+ V2,
+};
+
+// Corresponds with "TBSCertList" from RFC 5280 Section 5.1:
+struct NET_EXPORT_PRIVATE ParsedCrlTbsCertList {
+ ParsedCrlTbsCertList();
+ ~ParsedCrlTbsCertList();
+
+ // version Version OPTIONAL,
+ // -- if present, MUST be v2
+ //
+ // Parsing guarantees that the version is one of v1 or v2.
+ CrlVersion version = CrlVersion::V1;
+
+ // signature AlgorithmIdentifier,
+ //
+ // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+ // guarantees are made regarding the value of this SEQUENCE.
+ //
+ // This can be further parsed using SignatureValue::Create().
+ der::Input signature_algorithm_tlv;
+
+ // issuer Name,
+ //
+ // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+ // guarantees are made regarding the value of this SEQUENCE.
+ der::Input issuer_tlv;
+
+ // thisUpdate Time,
+ // nextUpdate Time OPTIONAL,
+ //
+ // Parsing guarantees that thisUpdate and nextUpdate(if present) are valid
+ // DER-encoded dates, however it DOES NOT guarantee anything about their
+ // values. For instance notAfter could be before notBefore, or the dates
+ // could indicate an expired CRL.
+ der::GeneralizedTime this_update;
+ absl::optional<der::GeneralizedTime> next_update;
+
+ // revokedCertificates SEQUENCE OF SEQUENCE {
+ // userCertificate CertificateSerialNumber,
+ // revocationDate Time,
+ // crlEntryExtensions Extensions OPTIONAL
+ // -- if present, version MUST be v2
+ // } OPTIONAL,
+ //
+ // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+ // guarantees are made regarding the value of this SEQUENCE.
+ absl::optional<der::Input> revoked_certificates_tlv;
+
+ // crlExtensions [0] EXPLICIT Extensions OPTIONAL
+ // -- if present, version MUST be v2
+ //
+ // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+ // guarantees are made regarding the value of this SEQUENCE. (Note that the
+ // EXPLICIT outer tag is stripped.)
+ //
+ // Parsing guarantees that if extensions is present the version is v2.
+ absl::optional<der::Input> crl_extensions_tlv;
+};
+
+// Represents the IssuingDistributionPoint certificate type constraints:
+enum class ContainedCertsType {
+ // Neither onlyContainsUserCerts or onlyContainsCACerts was present.
+ ANY_CERTS,
+ // onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE,
+ USER_CERTS,
+ // onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE,
+ CA_CERTS,
+};
+
+// Parses a DER-encoded IssuingDistributionPoint extension value.
+// Returns true on success and sets the results in the |out_*| parameters.
+//
+// If the IssuingDistributionPoint contains a distributionPoint fullName field,
+// |out_distribution_point_names| will contain the parsed representation.
+// If the distributionPoint type is nameRelativeToCRLIssuer, parsing will fail.
+//
+// |out_only_contains_cert_type| will contain the logical representation of the
+// onlyContainsUserCerts and onlyContainsCACerts fields (or their absence).
+//
+// indirectCRL and onlyContainsAttributeCerts are not supported and parsing will
+// fail if they are present.
+//
+// Note that on success |out_distribution_point_names| aliases data from the
+// input |extension_value|.
+//
+// On failure the |out_*| parameters have undefined state.
+//
+// IssuingDistributionPoint ::= SEQUENCE {
+// distributionPoint [0] DistributionPointName OPTIONAL,
+// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE,
+// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE,
+// onlySomeReasons [3] ReasonFlags OPTIONAL,
+// indirectCRL [4] BOOLEAN DEFAULT FALSE,
+// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
+[[nodiscard]] NET_EXPORT_PRIVATE bool ParseIssuingDistributionPoint(
+ const der::Input& extension_value,
+ std::unique_ptr<GeneralNames>* out_distribution_point_names,
+ ContainedCertsType* out_only_contains_cert_type);
+
+NET_EXPORT_PRIVATE CRLRevocationStatus
+GetCRLStatusForCert(const der::Input& cert_serial,
+ CrlVersion crl_version,
+ const absl::optional<der::Input>& revoked_certificates_tlv);
+
+// Checks the revocation status of the certificate |cert| by using the
+// DER-encoded |raw_crl|. |cert| must already have passed certificate path
+// validation.
+//
+// Returns GOOD if the CRL indicates the certificate is not revoked,
+// REVOKED if it indicates it is revoked, or UNKNOWN for all other cases.
+//
+// * |raw_crl|: A DER encoded CRL CertificateList.
+// * |valid_chain|: The validated certificate chain containing the target cert.
+// * |target_cert_index|: The index into |valid_chain| of the certificate being
+// checked for revocation.
+// * |cert_dp|: The distribution point from the target certificate's CRL
+// distribution points extension that |raw_crl| corresponds to. If
+// |raw_crl| was not specified in a distribution point, the caller must
+// synthesize a ParsedDistributionPoint object as specified by RFC 5280
+// 6.3.3.
+// * |verify_time|: The time to use when checking revocation status.
+// * |max_age|: The maximum age for a CRL, implemented as time since
+// the |thisUpdate| field in the CRL TBSCertList. Responses older than
+// |max_age| will be considered invalid.
+[[nodiscard]] NET_EXPORT CRLRevocationStatus
+CheckCRL(base::StringPiece raw_crl,
+ const ParsedCertificateList& valid_chain,
+ size_t target_cert_index,
+ const ParsedDistributionPoint& cert_dp,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_CRL_H_
diff --git a/chromium/net/cert/pki/extended_key_usage.cc b/chromium/net/cert/pki/extended_key_usage.cc
new file mode 100644
index 00000000000..e4e97b30175
--- /dev/null
+++ b/chromium/net/cert/pki/extended_key_usage.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 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 "net/cert/pki/extended_key_usage.h"
+
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "net/der/tag.h"
+
+namespace net {
+
+bool ParseEKUExtension(const der::Input& extension_value,
+ std::vector<der::Input>* eku_oids) {
+ der::Parser extension_parser(extension_value);
+ der::Parser sequence_parser;
+ if (!extension_parser.ReadSequence(&sequence_parser))
+ return false;
+
+ // Section 4.2.1.12 of RFC 5280 defines ExtKeyUsageSyntax as:
+ // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ //
+ // Therefore, the sequence must contain at least one KeyPurposeId.
+ if (!sequence_parser.HasMore())
+ return false;
+ while (sequence_parser.HasMore()) {
+ der::Input eku_oid;
+ if (!sequence_parser.ReadTag(der::kOid, &eku_oid))
+ // The SEQUENCE OF must contain only KeyPurposeIds (OIDs).
+ return false;
+ eku_oids->push_back(eku_oid);
+ }
+ if (extension_parser.HasMore())
+ // The extension value must follow ExtKeyUsageSyntax - there is no way that
+ // it could be extended to allow for something after the SEQUENCE OF.
+ return false;
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/extended_key_usage.h b/chromium/net/cert/pki/extended_key_usage.h
new file mode 100644
index 00000000000..f2ce9eb3e36
--- /dev/null
+++ b/chromium/net/cert/pki/extended_key_usage.h
@@ -0,0 +1,88 @@
+// Copyright 2015 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 NET_CERT_PKI_EXTENDED_KEY_USAGE_H_
+#define NET_CERT_PKI_EXTENDED_KEY_USAGE_H_
+
+#include <vector>
+
+#include "net/base/net_export.h"
+#include "net/der/input.h"
+
+namespace net {
+
+// The arc for the anyExtendedKeyUsage OID is found under the id-ce arc,
+// defined in section 4.2.1 of RFC 5280:
+// id-ce OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 29 }
+//
+// From RFC 5280 section 4.2.1.12:
+// id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
+// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
+// In dotted notation: 2.5.29.37.0
+inline constexpr uint8_t kAnyEKU[] = {0x55, 0x1d, 0x25, 0x00};
+
+// All other key usage purposes defined in RFC 5280 are found in the id-kp
+// arc, defined in section 4.2.1.12 as:
+// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
+//
+// With id-pkix defined in RFC 5280 section 4.2.2 as:
+// id-pkix OBJECT IDENTIFIER ::=
+// { iso(1) identified-organization(3) dod(6) internet(1)
+// security(5) mechanisms(5) pkix(7) }
+//
+// From RFC 5280 section 4.2.1.12:
+// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
+// In dotted notation: 1.3.6.1.5.5.7.3.1
+inline constexpr uint8_t kServerAuth[] = {0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x01};
+
+// From RFC 5280 section 4.2.1.12:
+// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
+// In dotted notation: 1.3.6.1.5.5.7.3.2
+inline constexpr uint8_t kClientAuth[] = {0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x02};
+
+// From RFC 5280 section 4.2.1.12:
+// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
+// In dotted notation: 1.3.6.1.5.5.7.3.3
+inline constexpr uint8_t kCodeSigning[] = {0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x03};
+
+// From RFC 5280 section 4.2.1.12:
+// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
+// In dotted notation: 1.3.6.1.5.5.7.3.4
+inline constexpr uint8_t kEmailProtection[] = {0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x04};
+
+// From RFC 5280 section 4.2.1.12:
+// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
+// In dotted notation: 1.3.6.1.5.5.7.3.8
+inline constexpr uint8_t kTimeStamping[] = {0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x08};
+
+// From RFC 5280 section 4.2.1.12:
+// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
+// In dotted notation: 1.3.6.1.5.5.7.3.9
+inline constexpr uint8_t kOCSPSigning[] = {0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x09};
+
+// Netscape Server Gated Crypto (2.16.840.1.113730.4.1) is a deprecated OID
+// which in some situations is considered equivalent to the serverAuth key
+// purpose.
+inline constexpr uint8_t kNetscapeServerGatedCrypto[] = {
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01};
+
+// Parses |extension_value|, which contains the extnValue field of an X.509v3
+// Extended Key Usage extension, and populates |eku_oids| with the list of
+// DER-encoded OID values (that is, without tag and length). Returns false if
+// |extension_value| is improperly encoded.
+//
+// Note: The returned OIDs are only as valid as long as the data pointed to by
+// |extension_value| is valid.
+NET_EXPORT bool ParseEKUExtension(const der::Input& extension_value,
+ std::vector<der::Input>* eku_oids);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_EXTENDED_KEY_USAGE_H_
diff --git a/chromium/net/cert/pki/extended_key_usage_unittest.cc b/chromium/net/cert/pki/extended_key_usage_unittest.cc
new file mode 100644
index 00000000000..f98ad799882
--- /dev/null
+++ b/chromium/net/cert/pki/extended_key_usage_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright 2015 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 <algorithm>
+
+#include "net/cert/pki/extended_key_usage.h"
+#include "net/der/input.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+// Helper method to check if an EKU is present in a std::vector of EKUs.
+bool HasEKU(const std::vector<der::Input>& list, const der::Input& eku) {
+ for (const auto& oid : list) {
+ if (oid == eku)
+ return true;
+ }
+ return false;
+}
+
+// Check that we can read multiple EKUs from an extension.
+TEST(ExtendedKeyUsageTest, ParseEKUExtension) {
+ // clang-format off
+ const uint8_t raw_extension_value[] = {
+ 0x30, 0x14, // SEQUENCE (20 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, // 1.3.6.1.5.5.7.3.1
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02 // 1.3.6.1.5.5.7.3.2
+ // end of SEQUENCE
+ };
+ // clang-format on
+ der::Input extension_value(raw_extension_value);
+
+ std::vector<der::Input> ekus;
+ EXPECT_TRUE(ParseEKUExtension(extension_value, &ekus));
+
+ EXPECT_EQ(2u, ekus.size());
+ EXPECT_TRUE(HasEKU(ekus, der::Input(kServerAuth)));
+ EXPECT_TRUE(HasEKU(ekus, der::Input(kClientAuth)));
+}
+
+// Check that an extension with the same OID present multiple times doesn't
+// cause an error.
+TEST(ExtendedKeyUsageTest, RepeatedOid) {
+ // clang-format off
+ const uint8_t extension_bytes[] = {
+ 0x30, 0x14, // SEQUENCE (20 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, // 1.3.6.1.5.5.7.3.1
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01 // 1.3.6.1.5.5.7.3.1
+ };
+ // clang-format on
+ der::Input extension(extension_bytes);
+
+ std::vector<der::Input> ekus;
+ EXPECT_TRUE(ParseEKUExtension(extension, &ekus));
+ EXPECT_EQ(2u, ekus.size());
+ for (const auto& eku : ekus) {
+ EXPECT_EQ(der::Input(kServerAuth), eku);
+ }
+}
+
+// Check that parsing an EKU extension which contains a private OID doesn't
+// cause an error.
+TEST(ExtendedKeyUsageTest, ParseEKUExtensionGracefullyHandlesPrivateOids) {
+ // clang-format off
+ const uint8_t extension_bytes[] = {
+ 0x30, 0x13, // SEQUENCE (19 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, // 1.3.6.1.5.5.7.3.1
+ 0x06, 0x07, // OBJECT IDENTIFIER (7 bytes)
+ 0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79 // 1.3.6.1.4.1.11129
+ };
+ // clang-format on
+ der::Input extension(extension_bytes);
+
+ std::vector<der::Input> ekus;
+ EXPECT_TRUE(ParseEKUExtension(extension, &ekus));
+ EXPECT_EQ(2u, ekus.size());
+ EXPECT_TRUE(HasEKU(ekus, der::Input(kServerAuth)));
+
+ const uint8_t google_oid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79};
+ der::Input google(google_oid);
+ EXPECT_TRUE(HasEKU(ekus, google));
+}
+
+// Test a variety of bad inputs.
+
+// If the extension value has data following the sequence of oids, parsing it
+// should fail.
+TEST(ExtendedKeyUsageTest, ExtraData) {
+ // clang-format off
+ const uint8_t extra_data[] = {
+ 0x30, 0x14, // SEQUENCE (20 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, // 1.3.6.1.5.5.7.3.1
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, // 1.3.6.1.5.5.7.3.2
+ // end of SEQUENCE
+ 0x02, 0x01, // INTEGER (1 byte)
+ 0x01 // 1
+ };
+ // clang-format on
+
+ std::vector<der::Input> ekus;
+ EXPECT_FALSE(ParseEKUExtension(der::Input(extra_data), &ekus));
+}
+
+// Check that ParseEKUExtension only accepts a sequence containing only oids.
+// This test case has an integer in the sequence (which should fail). A key
+// difference between this test case and ExtendedKeyUsageTest.ExtraData is where
+// the sequence ends - in this test case the integer is still part of the
+// sequence, while in ExtendedKeyUsageTest.ExtraData the integer is after the
+// sequence.
+TEST(ExtendedKeyUsageTest, NotAnOid) {
+ // clang-format off
+ const uint8_t not_an_oid[] = {
+ 0x30, 0x0d, // SEQUENCE (13 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, // 1.3.6.1.5.5.7.3.1
+ 0x02, 0x01, // INTEGER (1 byte)
+ 0x01 // 1
+ // end of SEQUENCE
+ };
+ // clang-format on
+
+ std::vector<der::Input> ekus;
+ EXPECT_FALSE(ParseEKUExtension(der::Input(not_an_oid), &ekus));
+}
+
+// Checks that the list of oids passed to ParseEKUExtension are in a sequence,
+// instead of one or more oid tag-length-values concatenated together.
+TEST(ExtendedKeyUsageTest, NotASequence) {
+ // clang-format off
+ const uint8_t not_a_sequence[] = {
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01 // 1.3.6.1.5.5.7.3.1
+ };
+ // clang-format on
+
+ std::vector<der::Input> ekus;
+ EXPECT_FALSE(ParseEKUExtension(der::Input(not_a_sequence), &ekus));
+}
+
+// A sequence passed into ParseEKUExtension must have at least one oid in it.
+TEST(ExtendedKeyUsageTest, EmptySequence) {
+ const uint8_t empty_sequence[] = {0x30, 0x00}; // SEQUENCE (0 bytes)
+
+ std::vector<der::Input> ekus;
+ EXPECT_FALSE(ParseEKUExtension(der::Input(empty_sequence), &ekus));
+}
+
+// The extension value must not be empty.
+TEST(ExtendedKeyUsageTest, EmptyExtension) {
+ std::vector<der::Input> ekus;
+ EXPECT_FALSE(ParseEKUExtension(der::Input(), &ekus));
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/pki/general_names.cc b/chromium/net/cert/pki/general_names.cc
new file mode 100644
index 00000000000..0a598dd24fe
--- /dev/null
+++ b/chromium/net/cert/pki/general_names.cc
@@ -0,0 +1,236 @@
+// Copyright 2017 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 "net/cert/pki/general_names.h"
+
+#include "base/check_op.h"
+#include "base/strings/string_util.h"
+#include "net/cert/pki/cert_error_params.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "net/der/tag.h"
+
+namespace net {
+
+DEFINE_CERT_ERROR_ID(kFailedParsingGeneralName, "Failed parsing GeneralName");
+
+namespace {
+
+DEFINE_CERT_ERROR_ID(kRFC822NameNotAscii, "rfc822Name is not ASCII");
+DEFINE_CERT_ERROR_ID(kDnsNameNotAscii, "dNSName is not ASCII");
+DEFINE_CERT_ERROR_ID(kURINotAscii, "uniformResourceIdentifier is not ASCII");
+DEFINE_CERT_ERROR_ID(kFailedParsingIp, "Failed parsing iPAddress");
+DEFINE_CERT_ERROR_ID(kUnknownGeneralNameType, "Unknown GeneralName type");
+DEFINE_CERT_ERROR_ID(kFailedReadingGeneralNames,
+ "Failed reading GeneralNames SEQUENCE");
+DEFINE_CERT_ERROR_ID(kGeneralNamesTrailingData,
+ "GeneralNames contains trailing data after the sequence");
+DEFINE_CERT_ERROR_ID(kGeneralNamesEmpty,
+ "GeneralNames is a sequence of 0 elements");
+DEFINE_CERT_ERROR_ID(kFailedReadingGeneralName,
+ "Failed reading GeneralName TLV");
+
+// Return true if the bitmask |mask| contains only zeros after the first
+// |prefix_length| bits.
+bool IsSuffixZero(const IPAddressBytes& mask, unsigned prefix_length) {
+ size_t zero_bits = mask.size() * CHAR_BIT - prefix_length;
+ size_t zero_bytes = zero_bits / CHAR_BIT;
+ std::vector<uint8_t> zeros(zero_bytes, 0);
+ if (memcmp(zeros.data(), mask.data() + mask.size() - zero_bytes, zero_bytes))
+ return false;
+ size_t leftover_bits = zero_bits % CHAR_BIT;
+ if (leftover_bits) {
+ uint8_t b = mask[mask.size() - zero_bytes - 1];
+ for (size_t i = 0; i < leftover_bits; ++i) {
+ if (b & (1 << i))
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+GeneralNames::GeneralNames() = default;
+
+GeneralNames::~GeneralNames() = default;
+
+// static
+std::unique_ptr<GeneralNames> GeneralNames::Create(
+ const der::Input& general_names_tlv,
+ CertErrors* errors) {
+ DCHECK(errors);
+
+ // RFC 5280 section 4.2.1.6:
+ // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+ der::Parser parser(general_names_tlv);
+ der::Input sequence_value;
+ if (!parser.ReadTag(der::kSequence, &sequence_value)) {
+ errors->AddError(kFailedReadingGeneralNames);
+ return nullptr;
+ }
+ // Should not have trailing data after GeneralNames sequence.
+ if (parser.HasMore()) {
+ errors->AddError(kGeneralNamesTrailingData);
+ return nullptr;
+ }
+ return CreateFromValue(sequence_value, errors);
+}
+
+// static
+std::unique_ptr<GeneralNames> GeneralNames::CreateFromValue(
+ const der::Input& general_names_value,
+ CertErrors* errors) {
+ DCHECK(errors);
+
+ auto general_names = std::make_unique<GeneralNames>();
+
+ der::Parser sequence_parser(general_names_value);
+ // The GeneralNames sequence should have at least 1 element.
+ if (!sequence_parser.HasMore()) {
+ errors->AddError(kGeneralNamesEmpty);
+ return nullptr;
+ }
+
+ while (sequence_parser.HasMore()) {
+ der::Input raw_general_name;
+ if (!sequence_parser.ReadRawTLV(&raw_general_name)) {
+ errors->AddError(kFailedReadingGeneralName);
+ return nullptr;
+ }
+
+ if (!ParseGeneralName(raw_general_name, IP_ADDRESS_ONLY,
+ general_names.get(), errors)) {
+ errors->AddError(kFailedParsingGeneralName);
+ return nullptr;
+ }
+ }
+
+ return general_names;
+}
+
+[[nodiscard]] bool ParseGeneralName(
+ const der::Input& input,
+ GeneralNames::ParseGeneralNameIPAddressType ip_address_type,
+ GeneralNames* subtrees,
+ CertErrors* errors) {
+ DCHECK(errors);
+ der::Parser parser(input);
+ der::Tag tag;
+ der::Input value;
+ if (!parser.ReadTagAndValue(&tag, &value))
+ return false;
+ GeneralNameTypes name_type = GENERAL_NAME_NONE;
+ if (tag == der::ContextSpecificConstructed(0)) {
+ // otherName [0] OtherName,
+ name_type = GENERAL_NAME_OTHER_NAME;
+ subtrees->other_names.push_back(value);
+ } else if (tag == der::ContextSpecificPrimitive(1)) {
+ // rfc822Name [1] IA5String,
+ name_type = GENERAL_NAME_RFC822_NAME;
+ const base::StringPiece s = value.AsStringPiece();
+ if (!base::IsStringASCII(s)) {
+ errors->AddError(kRFC822NameNotAscii);
+ return false;
+ }
+ subtrees->rfc822_names.push_back(s);
+ } else if (tag == der::ContextSpecificPrimitive(2)) {
+ // dNSName [2] IA5String,
+ name_type = GENERAL_NAME_DNS_NAME;
+ const base::StringPiece s = value.AsStringPiece();
+ if (!base::IsStringASCII(s)) {
+ errors->AddError(kDnsNameNotAscii);
+ return false;
+ }
+ subtrees->dns_names.push_back(s);
+ } else if (tag == der::ContextSpecificConstructed(3)) {
+ // x400Address [3] ORAddress,
+ name_type = GENERAL_NAME_X400_ADDRESS;
+ subtrees->x400_addresses.push_back(value);
+ } else if (tag == der::ContextSpecificConstructed(4)) {
+ // directoryName [4] Name,
+ name_type = GENERAL_NAME_DIRECTORY_NAME;
+ // Name is a CHOICE { rdnSequence RDNSequence }, therefore the SEQUENCE
+ // tag is explicit. Remove it, since the name matching functions expect
+ // only the value portion.
+ der::Parser name_parser(value);
+ der::Input name_value;
+ if (!name_parser.ReadTag(der::kSequence, &name_value) || parser.HasMore())
+ return false;
+ subtrees->directory_names.push_back(name_value);
+ } else if (tag == der::ContextSpecificConstructed(5)) {
+ // ediPartyName [5] EDIPartyName,
+ name_type = GENERAL_NAME_EDI_PARTY_NAME;
+ subtrees->edi_party_names.push_back(value);
+ } else if (tag == der::ContextSpecificPrimitive(6)) {
+ // uniformResourceIdentifier [6] IA5String,
+ name_type = GENERAL_NAME_UNIFORM_RESOURCE_IDENTIFIER;
+ const base::StringPiece s = value.AsStringPiece();
+ if (!base::IsStringASCII(s)) {
+ errors->AddError(kURINotAscii);
+ return false;
+ }
+ subtrees->uniform_resource_identifiers.push_back(s);
+ } else if (tag == der::ContextSpecificPrimitive(7)) {
+ // iPAddress [7] OCTET STRING,
+ name_type = GENERAL_NAME_IP_ADDRESS;
+ if (ip_address_type == GeneralNames::IP_ADDRESS_ONLY) {
+ // RFC 5280 section 4.2.1.6:
+ // When the subjectAltName extension contains an iPAddress, the address
+ // MUST be stored in the octet string in "network byte order", as
+ // specified in [RFC791]. The least significant bit (LSB) of each octet
+ // is the LSB of the corresponding byte in the network address. For IP
+ // version 4, as specified in [RFC791], the octet string MUST contain
+ // exactly four octets. For IP version 6, as specified in [RFC2460],
+ // the octet string MUST contain exactly sixteen octets.
+ if ((value.Length() != IPAddress::kIPv4AddressSize &&
+ value.Length() != IPAddress::kIPv6AddressSize)) {
+ errors->AddError(kFailedParsingIp);
+ return false;
+ }
+ subtrees->ip_addresses.emplace_back(value.UnsafeData(), value.Length());
+ } else {
+ DCHECK_EQ(ip_address_type, GeneralNames::IP_ADDRESS_AND_NETMASK);
+ // RFC 5280 section 4.2.1.10:
+ // The syntax of iPAddress MUST be as described in Section 4.2.1.6 with
+ // the following additions specifically for name constraints. For IPv4
+ // addresses, the iPAddress field of GeneralName MUST contain eight (8)
+ // octets, encoded in the style of RFC 4632 (CIDR) to represent an
+ // address range [RFC4632]. For IPv6 addresses, the iPAddress field
+ // MUST contain 32 octets similarly encoded. For example, a name
+ // constraint for "class C" subnet 192.0.2.0 is represented as the
+ // octets C0 00 02 00 FF FF FF 00, representing the CIDR notation
+ // 192.0.2.0/24 (mask 255.255.255.0).
+ if (value.Length() != IPAddress::kIPv4AddressSize * 2 &&
+ value.Length() != IPAddress::kIPv6AddressSize * 2) {
+ errors->AddError(kFailedParsingIp);
+ return false;
+ }
+ const IPAddress mask(value.UnsafeData() + value.Length() / 2,
+ value.Length() / 2);
+ const unsigned mask_prefix_length = MaskPrefixLength(mask);
+ if (!IsSuffixZero(mask.bytes(), mask_prefix_length)) {
+ errors->AddError(kFailedParsingIp);
+ return false;
+ }
+ subtrees->ip_address_ranges.emplace_back(
+ IPAddress(value.UnsafeData(), value.Length() / 2),
+ mask_prefix_length);
+ }
+ } else if (tag == der::ContextSpecificPrimitive(8)) {
+ // registeredID [8] OBJECT IDENTIFIER }
+ name_type = GENERAL_NAME_REGISTERED_ID;
+ subtrees->registered_ids.push_back(value);
+ } else {
+ errors->AddError(kUnknownGeneralNameType,
+ CreateCertErrorParams1SizeT("tag", tag));
+ return false;
+ }
+ DCHECK_NE(GENERAL_NAME_NONE, name_type);
+ subtrees->present_name_types |= name_type;
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/general_names.h b/chromium/net/cert/pki/general_names.h
new file mode 100644
index 00000000000..0bacddfe98e
--- /dev/null
+++ b/chromium/net/cert/pki/general_names.h
@@ -0,0 +1,124 @@
+// Copyright 2017 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 NET_CERT_PKI_GENERAL_NAMES_H_
+#define NET_CERT_PKI_GENERAL_NAMES_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/strings/string_piece_forward.h"
+#include "net/base/ip_address.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/cert_error_id.h"
+
+namespace net {
+
+class CertErrors;
+
+NET_EXPORT extern const CertErrorId kFailedParsingGeneralName;
+
+namespace der {
+class Input;
+} // namespace der
+
+// Bitfield values for the GeneralName types defined in RFC 5280. The ordering
+// and exact values are not important, but match the order from the RFC for
+// convenience.
+enum GeneralNameTypes {
+ GENERAL_NAME_NONE = 0,
+ GENERAL_NAME_OTHER_NAME = 1 << 0,
+ GENERAL_NAME_RFC822_NAME = 1 << 1,
+ GENERAL_NAME_DNS_NAME = 1 << 2,
+ GENERAL_NAME_X400_ADDRESS = 1 << 3,
+ GENERAL_NAME_DIRECTORY_NAME = 1 << 4,
+ GENERAL_NAME_EDI_PARTY_NAME = 1 << 5,
+ GENERAL_NAME_UNIFORM_RESOURCE_IDENTIFIER = 1 << 6,
+ GENERAL_NAME_IP_ADDRESS = 1 << 7,
+ GENERAL_NAME_REGISTERED_ID = 1 << 8,
+ GENERAL_NAME_ALL_TYPES = (1 << 9) - 1,
+};
+
+// Represents a GeneralNames structure. When processing GeneralNames, it is
+// often necessary to know which types of names were present, and to check
+// all the names of a certain type. Therefore, a bitfield of all the name
+// types is kept, and the names are split into members for each type.
+struct NET_EXPORT GeneralNames {
+ // Controls parsing of iPAddress names in ParseGeneralName.
+ // IP_ADDRESS_ONLY parses the iPAddress names as a 4 or 16 byte IP address.
+ // IP_ADDRESS_AND_NETMASK parses the iPAddress names as 8 or 32 bytes
+ // containing an IP address followed by a netmask.
+ enum ParseGeneralNameIPAddressType {
+ IP_ADDRESS_ONLY,
+ IP_ADDRESS_AND_NETMASK,
+ };
+
+ GeneralNames();
+ ~GeneralNames();
+
+ // Create a GeneralNames object representing the DER-encoded
+ // |general_names_tlv|. The returned object may reference data from
+ // |general_names_tlv|, so is only valid as long as |general_names_tlv| is.
+ // Returns nullptr on failure, and may fill |errors| with
+ // additional information. |errors| must be non-null.
+ static std::unique_ptr<GeneralNames> Create(
+ const der::Input& general_names_tlv,
+ CertErrors* errors);
+
+ // As above, but takes the GeneralNames sequence value, without the tag and
+ // length.
+ static std::unique_ptr<GeneralNames> CreateFromValue(
+ const der::Input& general_names_value,
+ CertErrors* errors);
+
+ // DER-encoded OtherName values.
+ std::vector<der::Input> other_names;
+
+ // ASCII rfc822names.
+ std::vector<base::StringPiece> rfc822_names;
+
+ // ASCII hostnames.
+ std::vector<base::StringPiece> dns_names;
+
+ // DER-encoded ORAddress values.
+ std::vector<der::Input> x400_addresses;
+
+ // DER-encoded Name values (not including the Sequence tag).
+ std::vector<der::Input> directory_names;
+
+ // DER-encoded EDIPartyName values.
+ std::vector<der::Input> edi_party_names;
+
+ // ASCII URIs.
+ std::vector<base::StringPiece> uniform_resource_identifiers;
+
+ // iPAddresses as sequences of octets in network byte order. This will be
+ // populated if the GeneralNames represents a Subject Alternative Name.
+ std::vector<IPAddress> ip_addresses;
+
+ // iPAddress ranges, as <IP, prefix length> pairs. This will be populated
+ // if the GeneralNames represents a Name Constraints.
+ std::vector<std::pair<IPAddress, unsigned>> ip_address_ranges;
+
+ // DER-encoded OBJECT IDENTIFIERs.
+ std::vector<der::Input> registered_ids;
+
+ // Which name types were present, as a bitfield of GeneralNameTypes.
+ int present_name_types = GENERAL_NAME_NONE;
+};
+
+// Parses a GeneralName value and adds it to |subtrees|.
+// |ip_address_type| specifies how to parse iPAddress names.
+// Returns false on failure, and may fill |errors| with additional information.
+// |errors| must be non-null.
+// TODO(mattm): should this be a method on GeneralNames?
+[[nodiscard]] NET_EXPORT bool ParseGeneralName(
+ const der::Input& input,
+ GeneralNames::ParseGeneralNameIPAddressType ip_address_type,
+ GeneralNames* subtrees,
+ CertErrors* errors);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_GENERAL_NAMES_H_
diff --git a/chromium/net/cert/pki/name_constraints.cc b/chromium/net/cert/pki/name_constraints.cc
new file mode 100644
index 00000000000..b66abdbef6c
--- /dev/null
+++ b/chromium/net/cert/pki/name_constraints.cc
@@ -0,0 +1,428 @@
+// Copyright 2015 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 "net/cert/pki/name_constraints.h"
+
+#include <limits.h>
+
+#include <memory>
+
+#include "base/check.h"
+#include "base/numerics/clamped_math.h"
+#include "base/strings/string_util.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/common_cert_errors.h"
+#include "net/cert/pki/verify_name_match.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "net/der/tag.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+
+namespace {
+
+// The name types of GeneralName that are fully supported in name constraints.
+//
+// (The other types will have the minimal checking described by RFC 5280
+// section 4.2.1.10: If a name constraints extension that is marked as critical
+// imposes constraints on a particular name form, and an instance of
+// that name form appears in the subject field or subjectAltName
+// extension of a subsequent certificate, then the application MUST
+// either process the constraint or reject the certificate.)
+const int kSupportedNameTypes = GENERAL_NAME_DNS_NAME |
+ GENERAL_NAME_DIRECTORY_NAME |
+ GENERAL_NAME_IP_ADDRESS;
+
+// Controls wildcard handling of DNSNameMatches.
+// If WildcardMatchType is WILDCARD_PARTIAL_MATCH "*.bar.com" is considered to
+// match the constraint "foo.bar.com". If it is WILDCARD_FULL_MATCH, "*.bar.com"
+// will match "bar.com" but not "foo.bar.com".
+enum WildcardMatchType { WILDCARD_PARTIAL_MATCH, WILDCARD_FULL_MATCH };
+
+// Returns true if |name| falls in the subtree defined by |dns_constraint|.
+// RFC 5280 section 4.2.1.10:
+// DNS name restrictions are expressed as host.example.com. Any DNS
+// name that can be constructed by simply adding zero or more labels
+// to the left-hand side of the name satisfies the name constraint. For
+// example, www.host.example.com would satisfy the constraint but
+// host1.example.com would not.
+//
+// |wildcard_matching| controls handling of wildcard names (|name| starts with
+// "*."). Wildcard handling is not specified by RFC 5280, but certificate
+// verification allows it, name constraints must check it similarly.
+bool DNSNameMatches(base::StringPiece name,
+ base::StringPiece dns_constraint,
+ WildcardMatchType wildcard_matching) {
+ // Everything matches the empty DNS name constraint.
+ if (dns_constraint.empty())
+ return true;
+
+ // Normalize absolute DNS names by removing the trailing dot, if any.
+ if (!name.empty() && *name.rbegin() == '.')
+ name.remove_suffix(1);
+ if (!dns_constraint.empty() && *dns_constraint.rbegin() == '.')
+ dns_constraint.remove_suffix(1);
+
+ // Wildcard partial-match handling ("*.bar.com" matching name constraint
+ // "foo.bar.com"). This only handles the case where the the dnsname and the
+ // constraint match after removing the leftmost label, otherwise it is handled
+ // by falling through to the check of whether the dnsname is fully within or
+ // fully outside of the constraint.
+ if (wildcard_matching == WILDCARD_PARTIAL_MATCH && name.size() > 2 &&
+ name[0] == '*' && name[1] == '.') {
+ size_t dns_constraint_dot_pos = dns_constraint.find('.');
+ if (dns_constraint_dot_pos != std::string::npos) {
+ base::StringPiece dns_constraint_domain =
+ dns_constraint.substr(dns_constraint_dot_pos + 1);
+ base::StringPiece wildcard_domain = name.substr(2);
+ if (base::EqualsCaseInsensitiveASCII(wildcard_domain,
+ dns_constraint_domain)) {
+ return true;
+ }
+ }
+ }
+
+ if (!base::EndsWith(name, dns_constraint,
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ return false;
+ }
+ // Exact match.
+ if (name.size() == dns_constraint.size())
+ return true;
+ // If dNSName constraint starts with a dot, only subdomains should match.
+ // (e.g., "foo.bar.com" matches constraint ".bar.com", but "bar.com" doesn't.)
+ // RFC 5280 is ambiguous, but this matches the behavior of other platforms.
+ if (!dns_constraint.empty() && dns_constraint[0] == '.')
+ dns_constraint.remove_prefix(1);
+ // Subtree match.
+ if (name.size() > dns_constraint.size() &&
+ name[name.size() - dns_constraint.size() - 1] == '.') {
+ return true;
+ }
+ // Trailing text matches, but not in a subtree (e.g., "foobar.com" is not a
+ // match for "bar.com").
+ return false;
+}
+
+// Parses a GeneralSubtrees |value| and store the contents in |subtrees|.
+// The individual values stored into |subtrees| are not validated by this
+// function.
+// NOTE: |subtrees| is not pre-initialized by the function(it is expected to be
+// a default initialized object), and it will be modified regardless of the
+// return value.
+[[nodiscard]] bool ParseGeneralSubtrees(const der::Input& value,
+ GeneralNames* subtrees,
+ CertErrors* errors) {
+ DCHECK(errors);
+
+ // GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
+ //
+ // GeneralSubtree ::= SEQUENCE {
+ // base GeneralName,
+ // minimum [0] BaseDistance DEFAULT 0,
+ // maximum [1] BaseDistance OPTIONAL }
+ //
+ // BaseDistance ::= INTEGER (0..MAX)
+ der::Parser sequence_parser(value);
+ // The GeneralSubtrees sequence should have at least 1 element.
+ if (!sequence_parser.HasMore())
+ return false;
+ while (sequence_parser.HasMore()) {
+ der::Parser subtree_sequence;
+ if (!sequence_parser.ReadSequence(&subtree_sequence))
+ return false;
+
+ der::Input raw_general_name;
+ if (!subtree_sequence.ReadRawTLV(&raw_general_name))
+ return false;
+
+ if (!ParseGeneralName(raw_general_name,
+ GeneralNames::IP_ADDRESS_AND_NETMASK, subtrees,
+ errors)) {
+ errors->AddError(kFailedParsingGeneralName);
+ return false;
+ }
+
+ // RFC 5280 section 4.2.1.10:
+ // Within this profile, the minimum and maximum fields are not used with any
+ // name forms, thus, the minimum MUST be zero, and maximum MUST be absent.
+ // However, if an application encounters a critical name constraints
+ // extension that specifies other values for minimum or maximum for a name
+ // form that appears in a subsequent certificate, the application MUST
+ // either process these fields or reject the certificate.
+
+ // Note that technically failing here isn't required: rather only need to
+ // fail if a name of this type actually appears in a subsequent cert and
+ // this extension was marked critical. However the minimum and maximum
+ // fields appear uncommon enough that implementing that isn't useful.
+ if (subtree_sequence.HasMore())
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+NameConstraints::~NameConstraints() = default;
+
+// static
+std::unique_ptr<NameConstraints> NameConstraints::Create(
+ const der::Input& extension_value,
+ bool is_critical,
+ CertErrors* errors) {
+ DCHECK(errors);
+
+ auto name_constraints = std::make_unique<NameConstraints>();
+ if (!name_constraints->Parse(extension_value, is_critical, errors))
+ return nullptr;
+ return name_constraints;
+}
+
+bool NameConstraints::Parse(const der::Input& extension_value,
+ bool is_critical,
+ CertErrors* errors) {
+ DCHECK(errors);
+
+ der::Parser extension_parser(extension_value);
+ der::Parser sequence_parser;
+
+ // NameConstraints ::= SEQUENCE {
+ // permittedSubtrees [0] GeneralSubtrees OPTIONAL,
+ // excludedSubtrees [1] GeneralSubtrees OPTIONAL }
+ if (!extension_parser.ReadSequence(&sequence_parser))
+ return false;
+ if (extension_parser.HasMore())
+ return false;
+
+ absl::optional<der::Input> permitted_subtrees_value;
+ if (!sequence_parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
+ &permitted_subtrees_value)) {
+ return false;
+ }
+ if (permitted_subtrees_value &&
+ !ParseGeneralSubtrees(permitted_subtrees_value.value(),
+ &permitted_subtrees_, errors)) {
+ return false;
+ }
+ constrained_name_types_ |=
+ permitted_subtrees_.present_name_types &
+ (is_critical ? GENERAL_NAME_ALL_TYPES : kSupportedNameTypes);
+
+ absl::optional<der::Input> excluded_subtrees_value;
+ if (!sequence_parser.ReadOptionalTag(der::ContextSpecificConstructed(1),
+ &excluded_subtrees_value)) {
+ return false;
+ }
+ if (excluded_subtrees_value &&
+ !ParseGeneralSubtrees(excluded_subtrees_value.value(),
+ &excluded_subtrees_, errors)) {
+ return false;
+ }
+ constrained_name_types_ |=
+ excluded_subtrees_.present_name_types &
+ (is_critical ? GENERAL_NAME_ALL_TYPES : kSupportedNameTypes);
+
+ // RFC 5280 section 4.2.1.10:
+ // Conforming CAs MUST NOT issue certificates where name constraints is an
+ // empty sequence. That is, either the permittedSubtrees field or the
+ // excludedSubtrees MUST be present.
+ if (!permitted_subtrees_value && !excluded_subtrees_value)
+ return false;
+
+ if (sequence_parser.HasMore())
+ return false;
+
+ return true;
+}
+
+void NameConstraints::IsPermittedCert(const der::Input& subject_rdn_sequence,
+ const GeneralNames* subject_alt_names,
+ CertErrors* errors) const {
+ // Checking NameConstraints is O(number_of_names * number_of_constraints).
+ // Impose a hard limit to mitigate the use of name constraints as a DoS
+ // mechanism.
+ const size_t kMaxChecks = 1048576; // 1 << 20
+ base::ClampedNumeric<size_t> check_count = 0;
+
+ if (subject_alt_names) {
+ check_count +=
+ base::ClampMul(subject_alt_names->dns_names.size(),
+ base::ClampAdd(excluded_subtrees_.dns_names.size(),
+ permitted_subtrees_.dns_names.size()));
+ check_count += base::ClampMul(
+ subject_alt_names->directory_names.size(),
+ base::ClampAdd(excluded_subtrees_.directory_names.size(),
+ permitted_subtrees_.directory_names.size()));
+ check_count += base::ClampMul(
+ subject_alt_names->ip_addresses.size(),
+ base::ClampAdd(excluded_subtrees_.ip_address_ranges.size(),
+ permitted_subtrees_.ip_address_ranges.size()));
+ }
+
+ if (!(subject_alt_names && subject_rdn_sequence.Length() == 0)) {
+ check_count += base::ClampAdd(excluded_subtrees_.directory_names.size(),
+ permitted_subtrees_.directory_names.size());
+ }
+
+ if (check_count > kMaxChecks) {
+ errors->AddError(cert_errors::kTooManyNameConstraintChecks);
+ return;
+ }
+
+ // Subject Alternative Name handling:
+ //
+ // RFC 5280 section 4.2.1.6:
+ // id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 }
+ //
+ // SubjectAltName ::= GeneralNames
+ //
+ // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+
+ if (subject_alt_names) {
+ // Check unsupported name types:
+ // constrained_name_types() for the unsupported types will only be true if
+ // that type of name was present in a name constraint that was marked
+ // critical.
+ //
+ // RFC 5280 section 4.2.1.10:
+ // If a name constraints extension that is marked as critical
+ // imposes constraints on a particular name form, and an instance of
+ // that name form appears in the subject field or subjectAltName
+ // extension of a subsequent certificate, then the application MUST
+ // either process the constraint or reject the certificate.
+ if (constrained_name_types() & subject_alt_names->present_name_types &
+ ~kSupportedNameTypes) {
+ errors->AddError(cert_errors::kNotPermittedByNameConstraints);
+ return;
+ }
+
+ // Check supported name types:
+ for (const auto& dns_name : subject_alt_names->dns_names) {
+ if (!IsPermittedDNSName(dns_name)) {
+ errors->AddError(cert_errors::kNotPermittedByNameConstraints);
+ return;
+ }
+ }
+
+ for (const auto& directory_name : subject_alt_names->directory_names) {
+ if (!IsPermittedDirectoryName(directory_name)) {
+ errors->AddError(cert_errors::kNotPermittedByNameConstraints);
+ return;
+ }
+ }
+
+ for (const auto& ip_address : subject_alt_names->ip_addresses) {
+ if (!IsPermittedIP(ip_address)) {
+ errors->AddError(cert_errors::kNotPermittedByNameConstraints);
+ return;
+ }
+ }
+ }
+
+ // Subject handling:
+
+ // RFC 5280 section 4.2.1.10:
+ // Legacy implementations exist where an electronic mail address is embedded
+ // in the subject distinguished name in an attribute of type emailAddress
+ // (Section 4.1.2.6). When constraints are imposed on the rfc822Name name
+ // form, but the certificate does not include a subject alternative name, the
+ // rfc822Name constraint MUST be applied to the attribute of type emailAddress
+ // in the subject distinguished name.
+ if (!subject_alt_names &&
+ (constrained_name_types() & GENERAL_NAME_RFC822_NAME)) {
+ bool contained_email_address = false;
+ if (!NameContainsEmailAddress(subject_rdn_sequence,
+ &contained_email_address)) {
+ errors->AddError(cert_errors::kNotPermittedByNameConstraints);
+ return;
+ }
+ if (contained_email_address) {
+ errors->AddError(cert_errors::kNotPermittedByNameConstraints);
+ return;
+ }
+ }
+
+ // RFC 5280 4.1.2.6:
+ // If subject naming information is present only in the subjectAltName
+ // extension (e.g., a key bound only to an email address or URI), then the
+ // subject name MUST be an empty sequence and the subjectAltName extension
+ // MUST be critical.
+ // This code assumes that criticality condition is checked by the caller, and
+ // therefore only needs to avoid the IsPermittedDirectoryName check against an
+ // empty subject in such a case.
+ if (subject_alt_names && subject_rdn_sequence.Length() == 0)
+ return;
+
+ if (!IsPermittedDirectoryName(subject_rdn_sequence)) {
+ errors->AddError(cert_errors::kNotPermittedByNameConstraints);
+ return;
+ }
+}
+
+bool NameConstraints::IsPermittedDNSName(base::StringPiece name) const {
+ for (const auto& excluded_name : excluded_subtrees_.dns_names) {
+ // When matching wildcard hosts against excluded subtrees, consider it a
+ // match if the constraint would match any expansion of the wildcard. Eg,
+ // *.bar.com should match a constraint of foo.bar.com.
+ if (DNSNameMatches(name, excluded_name, WILDCARD_PARTIAL_MATCH))
+ return false;
+ }
+
+ // If permitted subtrees are not constrained, any name that is not excluded is
+ // allowed.
+ if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_DNS_NAME))
+ return true;
+
+ for (const auto& permitted_name : permitted_subtrees_.dns_names) {
+ // When matching wildcard hosts against permitted subtrees, consider it a
+ // match only if the constraint would match all expansions of the wildcard.
+ // Eg, *.bar.com should match a constraint of bar.com, but not foo.bar.com.
+ if (DNSNameMatches(name, permitted_name, WILDCARD_FULL_MATCH))
+ return true;
+ }
+
+ return false;
+}
+
+bool NameConstraints::IsPermittedDirectoryName(
+ const der::Input& name_rdn_sequence) const {
+ for (const auto& excluded_name : excluded_subtrees_.directory_names) {
+ if (VerifyNameInSubtree(name_rdn_sequence, excluded_name))
+ return false;
+ }
+
+ // If permitted subtrees are not constrained, any name that is not excluded is
+ // allowed.
+ if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_DIRECTORY_NAME))
+ return true;
+
+ for (const auto& permitted_name : permitted_subtrees_.directory_names) {
+ if (VerifyNameInSubtree(name_rdn_sequence, permitted_name))
+ return true;
+ }
+
+ return false;
+}
+
+bool NameConstraints::IsPermittedIP(const IPAddress& ip) const {
+ for (const auto& excluded_ip : excluded_subtrees_.ip_address_ranges) {
+ if (IPAddressMatchesPrefix(ip, excluded_ip.first, excluded_ip.second))
+ return false;
+ }
+
+ // If permitted subtrees are not constrained, any name that is not excluded is
+ // allowed.
+ if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_IP_ADDRESS))
+ return true;
+
+ for (const auto& permitted_ip : permitted_subtrees_.ip_address_ranges) {
+ if (IPAddressMatchesPrefix(ip, permitted_ip.first, permitted_ip.second))
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/name_constraints.h b/chromium/net/cert/pki/name_constraints.h
new file mode 100644
index 00000000000..0fe0452da51
--- /dev/null
+++ b/chromium/net/cert/pki/name_constraints.h
@@ -0,0 +1,100 @@
+// Copyright 2015 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 NET_CERT_PKI_NAME_CONSTRAINTS_H_
+#define NET_CERT_PKI_NAME_CONSTRAINTS_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/strings/string_piece_forward.h"
+#include "net/base/ip_address.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/general_names.h"
+
+namespace net {
+
+class CertErrors;
+
+namespace der {
+class Input;
+} // namespace der
+
+// Parses a NameConstraints extension value and allows testing whether names are
+// allowed under those constraints as defined by RFC 5280 section 4.2.1.10.
+class NET_EXPORT NameConstraints {
+ public:
+ ~NameConstraints();
+
+ // Parses a DER-encoded NameConstraints extension and initializes this object.
+ // |extension_value| should be the extnValue from the extension (not including
+ // the OCTET STRING tag). |is_critical| should be true if the extension was
+ // marked critical. Returns nullptr if parsing the the extension failed.
+ // The object may reference data from |extension_value|, so is only valid as
+ // long as |extension_value| is.
+ static std::unique_ptr<NameConstraints> Create(
+ const der::Input& extension_value,
+ bool is_critical,
+ CertErrors* errors);
+
+ // Tests if a certificate is allowed by the name constraints.
+ // |subject_rdn_sequence| should be the DER-encoded value of the subject's
+ // RDNSequence (not including Sequence tag), and may be an empty ASN.1
+ // sequence. |subject_alt_names| should be the parsed representation of the
+ // subjectAltName extension or nullptr if the extension was not present.
+ // If the certificate is not allowed, an error will be added to |errors|.
+ // Note that this method does not check hostname or IP address in commonName,
+ // which is deprecated (crbug.com/308330).
+ void IsPermittedCert(const der::Input& subject_rdn_sequence,
+ const GeneralNames* subject_alt_names,
+ CertErrors* errors) const;
+
+ // Returns true if the ASCII hostname |name| is permitted.
+ // |name| may be a wildcard hostname (starts with "*."). Eg, "*.bar.com"
+ // would not be permitted if "bar.com" is permitted and "foo.bar.com" is
+ // excluded, while "*.baz.com" would only be permitted if "baz.com" is
+ // permitted.
+ bool IsPermittedDNSName(base::StringPiece name) const;
+
+ // Returns true if the directoryName |name_rdn_sequence| is permitted.
+ // |name_rdn_sequence| should be the DER-encoded RDNSequence value (not
+ // including the Sequence tag.)
+ bool IsPermittedDirectoryName(const der::Input& name_rdn_sequence) const;
+
+ // Returns true if the iPAddress |ip| is permitted.
+ bool IsPermittedIP(const IPAddress& ip) const;
+
+ // Returns a bitfield of GeneralNameTypes of all the types constrained by this
+ // NameConstraints. Name types that aren't supported will only be present if
+ // the name constraint they appeared in was marked critical.
+ //
+ // RFC 5280 section 4.2.1.10 says:
+ // Applications conforming to this profile MUST be able to process name
+ // constraints that are imposed on the directoryName name form and SHOULD be
+ // able to process name constraints that are imposed on the rfc822Name,
+ // uniformResourceIdentifier, dNSName, and iPAddress name forms.
+ // If a name constraints extension that is marked as critical
+ // imposes constraints on a particular name form, and an instance of
+ // that name form appears in the subject field or subjectAltName
+ // extension of a subsequent certificate, then the application MUST
+ // either process the constraint or reject the certificate.
+ int constrained_name_types() const { return constrained_name_types_; }
+
+ const GeneralNames& permitted_subtrees() const { return permitted_subtrees_; }
+ const GeneralNames& excluded_subtrees() const { return excluded_subtrees_; }
+
+ private:
+ [[nodiscard]] bool Parse(const der::Input& extension_value,
+ bool is_critical,
+ CertErrors* errors);
+
+ GeneralNames permitted_subtrees_;
+ GeneralNames excluded_subtrees_;
+ int constrained_name_types_ = GENERAL_NAME_NONE;
+};
+
+} // namespace net
+
+#endif // NET_CERT_PKI_NAME_CONSTRAINTS_H_
diff --git a/chromium/net/cert/pki/name_constraints_unittest.cc b/chromium/net/cert/pki/name_constraints_unittest.cc
new file mode 100644
index 00000000000..32a97af4f4b
--- /dev/null
+++ b/chromium/net/cert/pki/name_constraints_unittest.cc
@@ -0,0 +1,1221 @@
+// Copyright 2015 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 "net/cert/pki/name_constraints.h"
+
+#include <memory>
+
+#include "net/base/ip_address.h"
+#include "net/cert/pki/common_cert_errors.h"
+#include "net/cert/pki/test_helpers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+::testing::AssertionResult LoadTestData(const char* token,
+ const std::string& basename,
+ std::string* result) {
+ std::string path = "net/data/name_constraints_unittest/" + basename;
+
+ const PemBlockMapping mappings[] = {
+ {token, result},
+ };
+
+ return ReadTestDataFromPemFile(path, mappings);
+}
+
+::testing::AssertionResult LoadTestName(const std::string& basename,
+ std::string* result) {
+ return LoadTestData("NAME", basename, result);
+}
+
+::testing::AssertionResult LoadTestNameConstraint(const std::string& basename,
+ std::string* result) {
+ return LoadTestData("NAME CONSTRAINTS", basename, result);
+}
+
+::testing::AssertionResult LoadTestSubjectAltNameData(
+ const std::string& basename,
+ std::string* result) {
+ return LoadTestData("SUBJECT ALTERNATIVE NAME", basename, result);
+}
+
+::testing::AssertionResult LoadTestSubjectAltName(
+ const std::string& basename,
+ std::unique_ptr<GeneralNames>* result,
+ std::string* result_der) {
+ ::testing::AssertionResult load_result =
+ LoadTestSubjectAltNameData(basename, result_der);
+ if (!load_result)
+ return load_result;
+ CertErrors errors;
+ *result = GeneralNames::Create(der::Input(result_der), &errors);
+ if (!*result)
+ return ::testing::AssertionFailure() << "Create failed";
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult IsPermittedCert(
+ const NameConstraints* name_constraints,
+ const der::Input& subject_rdn_sequence,
+ const GeneralNames* subject_alt_names) {
+ CertErrors errors;
+ name_constraints->IsPermittedCert(subject_rdn_sequence, subject_alt_names,
+ &errors);
+ if (!errors.ContainsAnyErrorWithSeverity(CertError::SEVERITY_HIGH))
+ return ::testing::AssertionSuccess();
+ if (!errors.ContainsError(cert_errors::kNotPermittedByNameConstraints))
+ ADD_FAILURE() << "unexpected error " << errors.ToDebugString();
+ return ::testing::AssertionFailure();
+}
+
+} // namespace
+
+class ParseNameConstraints
+ : public ::testing::TestWithParam<::testing::tuple<bool>> {
+ public:
+ bool is_critical() const { return ::testing::get<0>(GetParam()); }
+};
+
+// Run the tests with the name constraints marked critical and non-critical. For
+// supported name types, the results should be the same for both.
+INSTANTIATE_TEST_SUITE_P(InstantiationName,
+ ParseNameConstraints,
+ ::testing::Values(true, false));
+
+TEST_P(ParseNameConstraints, DNSNames) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("dnsname.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("permitted.example.com"));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("permitted.example.com."));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("a.permitted.example.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("apermitted.example.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("apermitted.example.com."));
+ EXPECT_TRUE(
+ name_constraints->IsPermittedDNSName("alsopermitted.example.com"));
+ EXPECT_FALSE(
+ name_constraints->IsPermittedDNSName("excluded.permitted.example.com"));
+ EXPECT_FALSE(
+ name_constraints->IsPermittedDNSName("a.excluded.permitted.example.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName(
+ "stillnotpermitted.excluded.permitted.example.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName(
+ "a.stillnotpermitted.excluded.permitted.example.com"));
+ EXPECT_FALSE(
+ name_constraints->IsPermittedDNSName("extraneousexclusion.example.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName(
+ "a.extraneousexclusion.example.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("other.example.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("other.com"));
+
+ // Wildcard names:
+ // Pattern could match excluded.permitted.example.com, thus should not be
+ // allowed.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("*.permitted.example.com"));
+ // Entirely within excluded name, obviously not allowed.
+ EXPECT_FALSE(
+ name_constraints->IsPermittedDNSName("*.excluded.permitted.example.com"));
+ // Within permitted.example.com and cannot match any exclusion, thus these are
+ // allowed.
+ EXPECT_TRUE(
+ name_constraints->IsPermittedDNSName("*.foo.permitted.example.com"));
+ EXPECT_TRUE(
+ name_constraints->IsPermittedDNSName("*.alsopermitted.example.com"));
+ // Matches permitted.example2.com, but also matches other .example2.com names
+ // which are not in either permitted or excluded, so not allowed.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("*.example2.com"));
+ // Partial wildcards are not supported, so these name are permitted even if
+ // it seems like they shouldn't be. It's fine, since certificate verification
+ // won't treat them as wildcard names either.
+ EXPECT_TRUE(
+ name_constraints->IsPermittedDNSName("*xcluded.permitted.example.com"));
+ EXPECT_TRUE(
+ name_constraints->IsPermittedDNSName("exclude*.permitted.example.com"));
+ EXPECT_TRUE(
+ name_constraints->IsPermittedDNSName("excl*ded.permitted.example.com"));
+ // Garbage wildcard data.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("*."));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("*.*"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName(".*"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("*"));
+ // Matches SAN with trailing dot.
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("permitted.example3.com"));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("permitted.example3.com."));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("a.permitted.example3.com"));
+ EXPECT_TRUE(
+ name_constraints->IsPermittedDNSName("a.permitted.example3.com."));
+
+ EXPECT_EQ(GENERAL_NAME_DNS_NAME, name_constraints->constrained_name_types());
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-permitted.pem", &san, &san_der));
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+
+ ASSERT_TRUE(
+ LoadTestSubjectAltName("san-excluded-dnsname.pem", &san, &san_der));
+ EXPECT_FALSE(
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+
+ ASSERT_TRUE(
+ LoadTestSubjectAltName("san-excluded-directoryname.pem", &san, &san_der));
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+
+ ASSERT_TRUE(
+ LoadTestSubjectAltName("san-excluded-ipaddress.pem", &san, &san_der));
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints,
+ DNSNamesWithMultipleLevelsBetweenExcludedAndPermitted) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("dnsname2.pem", &a));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // Matches permitted exactly.
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("com"));
+ // Contained within permitted and doesn't match excluded (foo.bar.com).
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("bar.com"));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("baz.bar.com"));
+ // Matches excluded exactly.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("foo.bar.com"));
+ // Contained within excluded.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("baz.foo.bar.com"));
+
+ // Cannot match anything within excluded.
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("*.baz.bar.com"));
+ // Wildcard hostnames only match a single label, so cannot match excluded
+ // which has two labels before .com.
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("*.com"));
+
+ // Partial match of foo.bar.com.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("*.bar.com"));
+ // All expansions of wildcard are within excluded.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("*.foo.bar.com"));
+}
+
+TEST_P(ParseNameConstraints, DNSNamesPermittedWithLeadingDot) {
+ std::string a;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("dnsname-permitted_with_leading_dot.pem", &a));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // A permitted dNSName constraint of ".bar.com" should only match subdomains
+ // of .bar.com, but not bar.com itself.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("bar.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("foobar.com"));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("foo.bar.com"));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("*.bar.com"));
+}
+
+TEST_P(ParseNameConstraints, DNSNamesExcludedWithLeadingDot) {
+ std::string a;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("dnsname-excluded_with_leading_dot.pem", &a));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // An excluded dNSName constraint of ".bar.com" should only match subdomains
+ // of .bar.com, but not bar.com itself.
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("com"));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("bar.com"));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("foobar.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("foo.bar.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("*.bar.com"));
+}
+
+TEST_P(ParseNameConstraints, DNSNamesPermittedTwoDot) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("dnsname-permitted_two_dot.pem", &a));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // A dNSName constraint of ".." isn't meaningful. Shouldn't match anything.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("com."));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("foo.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("*.com"));
+}
+
+TEST_P(ParseNameConstraints, DNSNamesExcludeOnly) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("dnsname-excluded.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // Only "excluded.permitted.example.com" is excluded, and since permitted is
+ // empty, any dNSName outside that is allowed.
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName(""));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("foo.com"));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("permitted.example.com"));
+ EXPECT_FALSE(
+ name_constraints->IsPermittedDNSName("excluded.permitted.example.com"));
+ EXPECT_FALSE(
+ name_constraints->IsPermittedDNSName("a.excluded.permitted.example.com"));
+}
+
+TEST_P(ParseNameConstraints, DNSNamesExcludeAll) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("dnsname-excludeall.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // "permitted.example.com" is in the permitted section, but since "" is
+ // excluded, nothing is permitted.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName(""));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("foo.com"));
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("permitted.example.com"));
+ EXPECT_FALSE(
+ name_constraints->IsPermittedDNSName("foo.permitted.example.com"));
+}
+
+TEST_P(ParseNameConstraints, DNSNamesExcludeDot) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("dnsname-exclude_dot.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // "." is excluded, which should match nothing.
+ EXPECT_FALSE(name_constraints->IsPermittedDNSName("foo.com"));
+ EXPECT_TRUE(name_constraints->IsPermittedDNSName("permitted.example.com"));
+ EXPECT_TRUE(
+ name_constraints->IsPermittedDNSName("foo.permitted.example.com"));
+}
+
+TEST_P(ParseNameConstraints, DNSNamesFailOnInvalidIA5String) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("dnsname.pem", &a));
+
+ size_t replace_location = a.find("permitted.example2.com");
+ ASSERT_NE(std::string::npos, replace_location);
+ a.replace(replace_location, 1, 1, -1);
+
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, DirectoryNames) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint("directoryname.pem", &constraints_der));
+
+ std::string name_us;
+ ASSERT_TRUE(LoadTestName("name-us.pem", &name_us));
+ std::string name_us_ca;
+ ASSERT_TRUE(LoadTestName("name-us-california.pem", &name_us_ca));
+ std::string name_us_ca_mountain_view;
+ ASSERT_TRUE(LoadTestName("name-us-california-mountain_view.pem",
+ &name_us_ca_mountain_view));
+ std::string name_us_az;
+ ASSERT_TRUE(LoadTestName("name-us-arizona.pem", &name_us_az));
+ std::string name_jp;
+ ASSERT_TRUE(LoadTestName("name-jp.pem", &name_jp));
+ std::string name_jp_tokyo;
+ ASSERT_TRUE(LoadTestName("name-jp-tokyo.pem", &name_jp_tokyo));
+ std::string name_de;
+ ASSERT_TRUE(LoadTestName("name-de.pem", &name_de));
+ std::string name_ca;
+ ASSERT_TRUE(LoadTestName("name-ca.pem", &name_ca));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // Not in any permitted subtree.
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_ca)));
+ // Within the permitted C=US subtree.
+ EXPECT_TRUE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_us)));
+ // Within the permitted C=US subtree.
+ EXPECT_TRUE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_us_az)));
+ // Within the permitted C=US subtree, however the excluded C=US,ST=California
+ // subtree takes priority.
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_us_ca)));
+ // Within the permitted C=US subtree as well as the permitted
+ // C=US,ST=California,L=Mountain View subtree, however the excluded
+ // C=US,ST=California subtree still takes priority.
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_us_ca_mountain_view)));
+ // Not in any permitted subtree, and also inside the extraneous excluded C=DE
+ // subtree.
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_de)));
+ // Not in any permitted subtree.
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_jp)));
+ // Within the permitted C=JP,ST=Tokyo subtree.
+ EXPECT_TRUE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_jp_tokyo)));
+
+ EXPECT_EQ(GENERAL_NAME_DIRECTORY_NAME,
+ name_constraints->constrained_name_types());
+
+ // Within the permitted C=US subtree.
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us),
+ nullptr /* subject_alt_names */));
+ // Within the permitted C=US subtree, however the excluded C=US,ST=California
+ // subtree takes priority.
+ EXPECT_FALSE(IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us_ca),
+ nullptr /* subject_alt_names */));
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-permitted.pem", &san, &san_der));
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+
+ ASSERT_TRUE(
+ LoadTestSubjectAltName("san-excluded-dnsname.pem", &san, &san_der));
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+
+ ASSERT_TRUE(
+ LoadTestSubjectAltName("san-excluded-directoryname.pem", &san, &san_der));
+ EXPECT_FALSE(
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+
+ ASSERT_TRUE(
+ LoadTestSubjectAltName("san-excluded-ipaddress.pem", &san, &san_der));
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, DirectoryNamesExcludeOnly) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("directoryname-excluded.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ std::string name_empty;
+ ASSERT_TRUE(LoadTestName("name-empty.pem", &name_empty));
+ std::string name_us;
+ ASSERT_TRUE(LoadTestName("name-us.pem", &name_us));
+ std::string name_us_ca;
+ ASSERT_TRUE(LoadTestName("name-us-california.pem", &name_us_ca));
+ std::string name_us_ca_mountain_view;
+ ASSERT_TRUE(LoadTestName("name-us-california-mountain_view.pem",
+ &name_us_ca_mountain_view));
+
+ // Only "C=US,ST=California" is excluded, and since permitted is empty,
+ // any directoryName outside that is allowed.
+ EXPECT_TRUE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_empty)));
+ EXPECT_TRUE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_us)));
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_us_ca)));
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_us_ca_mountain_view)));
+}
+
+TEST_P(ParseNameConstraints, DirectoryNamesExcludeAll) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("directoryname-excludeall.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ std::string name_empty;
+ ASSERT_TRUE(LoadTestName("name-empty.pem", &name_empty));
+ std::string name_us;
+ ASSERT_TRUE(LoadTestName("name-us.pem", &name_us));
+ std::string name_us_ca;
+ ASSERT_TRUE(LoadTestName("name-us-california.pem", &name_us_ca));
+ std::string name_us_ca_mountain_view;
+ ASSERT_TRUE(LoadTestName("name-us-california-mountain_view.pem",
+ &name_us_ca_mountain_view));
+ std::string name_jp;
+ ASSERT_TRUE(LoadTestName("name-jp.pem", &name_jp));
+
+ // "C=US" is in the permitted section, but since an empty
+ // directoryName is excluded, nothing is permitted.
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_empty)));
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_us)));
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_us_ca)));
+ EXPECT_FALSE(name_constraints->IsPermittedDirectoryName(
+ SequenceValueFromString(&name_jp)));
+}
+
+TEST_P(ParseNameConstraints, IPAdresses) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("ipaddress.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // IPv4 tests:
+
+ // Not in any permitted range.
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 169, 0, 1)));
+
+ // Within the permitted 192.168.0.0/255.255.0.0 range.
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress(192, 168, 0, 1)));
+
+ // Within the permitted 192.168.0.0/255.255.0.0 range, however the
+ // excluded 192.168.5.0/255.255.255.0 takes priority.
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 168, 5, 1)));
+
+ // Within the permitted 192.168.0.0/255.255.0.0 range as well as the
+ // permitted 192.168.5.32/255.255.255.224 range, however the excluded
+ // 192.168.5.0/255.255.255.0 still takes priority.
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 168, 5, 33)));
+
+ // Not in any permitted range. (Just outside the
+ // 192.167.5.32/255.255.255.224 range.)
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 167, 5, 31)));
+
+ // Within the permitted 192.167.5.32/255.255.255.224 range.
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress(192, 167, 5, 32)));
+
+ // Within the permitted 192.167.5.32/255.255.255.224 range.
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress(192, 167, 5, 63)));
+
+ // Not in any permitted range. (Just outside the
+ // 192.167.5.32/255.255.255.224 range.)
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 167, 5, 64)));
+
+ // Not in any permitted range, and also inside the extraneous excluded
+ // 192.166.5.32/255.255.255.224 range.
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 166, 5, 32)));
+
+ // IPv6 tests:
+
+ // Not in any permitted range.
+ EXPECT_FALSE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 0, 0, 0, 1)));
+
+ // Within the permitted
+ // 102:304:506:708:90a:b0c::/ffff:ffff:ffff:ffff:ffff:ffff:: range.
+ EXPECT_TRUE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 1)));
+
+ // Within the permitted
+ // 102:304:506:708:90a:b0c::/ffff:ffff:ffff:ffff:ffff:ffff:: range, however
+ // the excluded
+ // 102:304:506:708:90a:b0c:500:0/ffff:ffff:ffff:ffff:ffff:ffff:ff00:0 takes
+ // priority.
+ EXPECT_FALSE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 5, 0, 0, 1)));
+
+ // Within the permitted
+ // 102:304:506:708:90a:b0c::/ffff:ffff:ffff:ffff:ffff:ffff:: range as well
+ // as the permitted
+ // 102:304:506:708:90a:b0c:520:0/ffff:ffff:ffff:ffff:ffff:ffff:ff60:0,
+ // however the excluded
+ // 102:304:506:708:90a:b0c:500:0/ffff:ffff:ffff:ffff:ffff:ffff:ff00:0 takes
+ // priority.
+ EXPECT_FALSE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 5, 33, 0, 1)));
+
+ // Not in any permitted range. (Just outside the
+ // 102:304:506:708:90a:b0b:520:0/ffff:ffff:ffff:ffff:ffff:ffff:ff60:0
+ // range.)
+ EXPECT_FALSE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 5, 31, 255, 255)));
+
+ // Within the permitted
+ // 102:304:506:708:90a:b0b:520:0/ffff:ffff:ffff:ffff:ffff:ffff:ff60:0 range.
+ EXPECT_TRUE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 5, 32, 0, 0)));
+
+ // Within the permitted
+ // 102:304:506:708:90a:b0b:520:0/ffff:ffff:ffff:ffff:ffff:ffff:ff60:0 range.
+ EXPECT_TRUE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 5, 63, 255, 255)));
+
+ // Not in any permitted range. (Just outside the
+ // 102:304:506:708:90a:b0b:520:0/ffff:ffff:ffff:ffff:ffff:ffff:ff60:0
+ // range.)
+ EXPECT_FALSE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 5, 64, 0, 0)));
+
+ // Not in any permitted range, and also inside the extraneous excluded
+ // 102:304:506:708:90a:b0a:520:0/ffff:ffff:ffff:ffff:ffff:ffff:ff60:0 range.
+ EXPECT_FALSE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 10, 5, 33, 0, 1)));
+
+ EXPECT_EQ(GENERAL_NAME_IP_ADDRESS,
+ name_constraints->constrained_name_types());
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-permitted.pem", &san, &san_der));
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+
+ ASSERT_TRUE(
+ LoadTestSubjectAltName("san-excluded-dnsname.pem", &san, &san_der));
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+
+ ASSERT_TRUE(
+ LoadTestSubjectAltName("san-excluded-directoryname.pem", &san, &san_der));
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+
+ ASSERT_TRUE(
+ LoadTestSubjectAltName("san-excluded-ipaddress.pem", &san, &san_der));
+ EXPECT_FALSE(
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, IPAdressesExcludeOnly) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("ipaddress-excluded.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // Only 192.168.5.0/255.255.255.0 is excluded, and since permitted is empty,
+ // any iPAddress outside that is allowed.
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress(192, 168, 0, 1)));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 168, 5, 1)));
+ EXPECT_TRUE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 0, 0, 0, 1)));
+}
+
+TEST_P(ParseNameConstraints, IPAdressesExcludeAll) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("ipaddress-excludeall.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ // 192.168.0.0/255.255.0.0 and
+ // 102:304:506:708:90a:b0c::/ffff:ffff:ffff:ffff:ffff:ffff:: are permitted,
+ // but since 0.0.0.0/0 and ::/0 are excluded nothing is permitted.
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 168, 0, 1)));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(1, 1, 1, 1)));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(
+ IPAddress(2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(
+ IPAddress(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 0, 0, 0, 1)));
+}
+
+TEST_P(ParseNameConstraints, IPAdressesNetmaskPermitSingleHost) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("ipaddress-permit_singlehost.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress::IPv4AllZeros()));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 168, 1, 1)));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 168, 1, 2)));
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress(192, 168, 1, 3)));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 168, 1, 4)));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(255, 255, 255, 255)));
+}
+
+TEST_P(ParseNameConstraints, IPAdressesNetmaskPermitPrefixLen31) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("ipaddress-permit_prefix31.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress::IPv4AllZeros()));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 168, 1, 1)));
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress(192, 168, 1, 2)));
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress(192, 168, 1, 3)));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 168, 1, 4)));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(192, 168, 1, 5)));
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress(255, 255, 255, 255)));
+}
+
+TEST_P(ParseNameConstraints, IPAdressesNetmaskPermitPrefixLen1) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("ipaddress-permit_prefix1.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ EXPECT_FALSE(name_constraints->IsPermittedIP(IPAddress::IPv4AllZeros()));
+ EXPECT_FALSE(
+ name_constraints->IsPermittedIP(IPAddress(0x7F, 0xFF, 0xFF, 0xFF)));
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress(0x80, 0, 0, 0)));
+ EXPECT_TRUE(
+ name_constraints->IsPermittedIP(IPAddress(0xFF, 0xFF, 0xFF, 0xFF)));
+}
+
+TEST_P(ParseNameConstraints, IPAdressesNetmaskPermitAll) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("ipaddress-permit_all.pem", &a));
+
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(
+ NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress::IPv4AllZeros()));
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress(192, 168, 1, 1)));
+ EXPECT_TRUE(name_constraints->IsPermittedIP(IPAddress(255, 255, 255, 255)));
+}
+
+TEST_P(ParseNameConstraints, IPAdressesFailOnInvalidAddr) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint("ipaddress-invalid_addr.pem", &a));
+
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, IPAdressesFailOnInvalidMaskNotContiguous) {
+ std::string a;
+ ASSERT_TRUE(LoadTestNameConstraint(
+ "ipaddress-invalid_mask_not_contiguous_1.pem", &a));
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+
+ ASSERT_TRUE(LoadTestNameConstraint(
+ "ipaddress-invalid_mask_not_contiguous_2.pem", &a));
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+
+ ASSERT_TRUE(LoadTestNameConstraint(
+ "ipaddress-invalid_mask_not_contiguous_3.pem", &a));
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+
+ ASSERT_TRUE(LoadTestNameConstraint(
+ "ipaddress-invalid_mask_not_contiguous_4.pem", &a));
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&a), is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, OtherNamesInPermitted) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("othername-permitted.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_OTHER_NAME,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-othername.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, OtherNamesInExcluded) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("othername-excluded.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_OTHER_NAME,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-othername.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, Rfc822NamesInPermitted) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("rfc822name-permitted.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_RFC822_NAME,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-rfc822name.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, Rfc822NamesInExcluded) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("rfc822name-excluded.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_RFC822_NAME,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-rfc822name.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, X400AddresssInPermitted) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("x400address-permitted.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_X400_ADDRESS,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-x400address.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, X400AddresssInExcluded) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("x400address-excluded.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_X400_ADDRESS,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-x400address.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, EdiPartyNamesInPermitted) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("edipartyname-permitted.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_EDI_PARTY_NAME,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-edipartyname.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, EdiPartyNamesInExcluded) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("edipartyname-excluded.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_EDI_PARTY_NAME,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-edipartyname.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, URIsInPermitted) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint("uri-permitted.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_UNIFORM_RESOURCE_IDENTIFIER,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-uri.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, URIsInExcluded) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint("uri-excluded.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_UNIFORM_RESOURCE_IDENTIFIER,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-uri.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, RegisteredIDsInPermitted) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("registeredid-permitted.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_REGISTERED_ID,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-registeredid.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints, RegisteredIDsInExcluded) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("registeredid-excluded.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ if (is_critical()) {
+ EXPECT_EQ(GENERAL_NAME_REGISTERED_ID,
+ name_constraints->constrained_name_types());
+ } else {
+ EXPECT_EQ(0, name_constraints->constrained_name_types());
+ }
+
+ std::string san_der;
+ std::unique_ptr<GeneralNames> san;
+ ASSERT_TRUE(LoadTestSubjectAltName("san-registeredid.pem", &san, &san_der));
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(), der::Input(), san.get()));
+}
+
+TEST_P(ParseNameConstraints,
+ failsOnGeneralSubtreeWithMinimumZeroEncodedUnnecessarily) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("dnsname-with_min_0.pem", &constraints_der));
+ // The value should not be in the DER encoding if it is the default. But this
+ // could be changed to allowed if there are buggy encoders out there that
+ // include it anyway.
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&constraints_der),
+ is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, FailsOnGeneralSubtreeWithMinimum) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("dnsname-with_min_1.pem", &constraints_der));
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&constraints_der),
+ is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints,
+ failsOnGeneralSubtreeWithMinimumZeroEncodedUnnecessarilyAndMaximum) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint("dnsname-with_min_0_and_max.pem",
+ &constraints_der));
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&constraints_der),
+ is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, FailsOnGeneralSubtreeWithMinimumAndMaximum) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint("dnsname-with_min_1_and_max.pem",
+ &constraints_der));
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&constraints_der),
+ is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, FailsOnGeneralSubtreeWithMaximum) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint("dnsname-with_max.pem", &constraints_der));
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&constraints_der),
+ is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, FailsOnEmptyExtensionValue) {
+ std::string constraints_der = "";
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&constraints_der),
+ is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, FailsOnNoPermittedAndExcluded) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("invalid-no_subtrees.pem", &constraints_der));
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&constraints_der),
+ is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, FailsOnEmptyPermitted) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint("invalid-empty_permitted_subtree.pem",
+ &constraints_der));
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&constraints_der),
+ is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, FailsOnEmptyExcluded) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint("invalid-empty_excluded_subtree.pem",
+ &constraints_der));
+ CertErrors errors;
+ EXPECT_FALSE(NameConstraints::Create(der::Input(&constraints_der),
+ is_critical(), &errors));
+}
+
+TEST_P(ParseNameConstraints, IsPermittedCertSubjectEmailAddressIsOk) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint("directoryname.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ std::string name_us_arizona_email;
+ ASSERT_TRUE(
+ LoadTestName("name-us-arizona-email.pem", &name_us_arizona_email));
+
+ // Name constraints don't contain rfc822Name, so emailAddress in subject is
+ // allowed regardless.
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us_arizona_email),
+ nullptr /* subject_alt_names */));
+}
+
+TEST_P(ParseNameConstraints, IsPermittedCertSubjectEmailAddressIsNotOk) {
+ std::string constraints_der;
+ ASSERT_TRUE(
+ LoadTestNameConstraint("rfc822name-permitted.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ std::string name_us_arizona_email;
+ ASSERT_TRUE(
+ LoadTestName("name-us-arizona-email.pem", &name_us_arizona_email));
+
+ // Name constraints contain rfc822Name, so emailAddress in subject is not
+ // allowed if the constraints were critical.
+ EXPECT_EQ(!is_critical(),
+ IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us_arizona_email),
+ nullptr /* subject_alt_names */));
+}
+
+// Hostname in commonName is not allowed (crbug.com/308330), so these are tests
+// are not particularly interesting, just verifying that the commonName is
+// ignored for dNSName constraints.
+TEST_P(ParseNameConstraints, IsPermittedCertSubjectDnsNames) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint("directoryname_and_dnsname.pem",
+ &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ std::string name_us_az_foocom;
+ ASSERT_TRUE(LoadTestName("name-us-arizona-foo.com.pem", &name_us_az_foocom));
+ // The subject is within permitted directoryName constraints, so permitted.
+ // (The commonName hostname is not within permitted dNSName constraints, so
+ // this would not be permitted if hostnames in commonName were checked.)
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us_az_foocom),
+ nullptr /* subject_alt_names */));
+
+ std::string name_us_az_permitted;
+ ASSERT_TRUE(LoadTestName("name-us-arizona-permitted.example.com.pem",
+ &name_us_az_permitted));
+ // The subject is in permitted directoryName and the commonName is within
+ // permitted dNSName constraints, so this should be permitted regardless if
+ // hostnames in commonName are checked or not.
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us_az_permitted),
+ nullptr /* subject_alt_names */));
+
+ std::string name_us_ca_permitted;
+ ASSERT_TRUE(LoadTestName("name-us-california-permitted.example.com.pem",
+ &name_us_ca_permitted));
+ // The subject is within the excluded C=US,ST=California directoryName, so
+ // this should not be allowed, regardless of checking the
+ // permitted.example.com in commonName.
+ EXPECT_FALSE(IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us_ca_permitted),
+ nullptr /* subject_alt_names */));
+}
+
+// IP addresses in commonName are not allowed (crbug.com/308330), so these are
+// tests are not particularly interesting, just verifying that the commonName is
+// ignored for iPAddress constraints.
+TEST_P(ParseNameConstraints, IsPermittedCertSubjectIpAddresses) {
+ std::string constraints_der;
+ ASSERT_TRUE(LoadTestNameConstraint(
+ "directoryname_and_dnsname_and_ipaddress.pem", &constraints_der));
+ CertErrors errors;
+ std::unique_ptr<NameConstraints> name_constraints(NameConstraints::Create(
+ der::Input(&constraints_der), is_critical(), &errors));
+ ASSERT_TRUE(name_constraints);
+
+ std::string name_us_az_1_1_1_1;
+ ASSERT_TRUE(LoadTestName("name-us-arizona-1.1.1.1.pem", &name_us_az_1_1_1_1));
+ // The subject is within permitted directoryName constraints, so permitted.
+ // (The commonName IP address is not within permitted iPAddresses constraints,
+ // so this would not be permitted if IP addresses in commonName were checked.)
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us_az_1_1_1_1),
+ nullptr /* subject_alt_names */));
+
+ std::string name_us_az_192_168_1_1;
+ ASSERT_TRUE(
+ LoadTestName("name-us-arizona-192.168.1.1.pem", &name_us_az_192_168_1_1));
+ // The subject is in permitted directoryName and the commonName is within
+ // permitted iPAddress constraints, so this should be permitted regardless if
+ // IP addresses in commonName are checked or not.
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us_az_192_168_1_1),
+ nullptr /* subject_alt_names */));
+
+ std::string name_us_ca_192_168_1_1;
+ ASSERT_TRUE(LoadTestName("name-us-california-192.168.1.1.pem",
+ &name_us_ca_192_168_1_1));
+ // The subject is within the excluded C=US,ST=California directoryName, so
+ // this should not be allowed, regardless of checking the
+ // IP address in commonName.
+ EXPECT_FALSE(IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us_ca_192_168_1_1),
+ nullptr /* subject_alt_names */));
+
+ std::string name_us_az_ipv6;
+ ASSERT_TRUE(LoadTestName("name-us-arizona-ipv6.pem", &name_us_az_ipv6));
+ // The subject is within permitted directoryName constraints, so permitted.
+ // (The commonName is an ipv6 address which wasn't supported in the past, but
+ // since commonName checking is ignored entirely, this is permitted.)
+ EXPECT_TRUE(IsPermittedCert(name_constraints.get(),
+ SequenceValueFromString(&name_us_az_ipv6),
+ nullptr /* subject_alt_names */));
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/nist_pkits_unittest.cc b/chromium/net/cert/pki/nist_pkits_unittest.cc
new file mode 100644
index 00000000000..f2309349fba
--- /dev/null
+++ b/chromium/net/cert/pki/nist_pkits_unittest.cc
@@ -0,0 +1,100 @@
+// Copyright 2017 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 "net/cert/pki/nist_pkits_unittest.h"
+
+#include "net/cert/pki/certificate_policies.h"
+
+#include <sstream>
+
+namespace net {
+
+namespace {
+
+// 2.16.840.1.101.3.2.1.48.1
+const uint8_t kTestPolicy1[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x02, 0x01, 0x30, 0x01};
+
+// 2.16.840.1.101.3.2.1.48.2
+const uint8_t kTestPolicy2[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x02, 0x01, 0x30, 0x02};
+
+// 2.16.840.1.101.3.2.1.48.3
+const uint8_t kTestPolicy3[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x02, 0x01, 0x30, 0x03};
+
+// 2.16.840.1.101.3.2.1.48.6
+const uint8_t kTestPolicy6[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x02, 0x01, 0x30, 0x06};
+
+void SetPolicySetFromString(const char* const policy_names,
+ std::set<der::Input>* out) {
+ out->clear();
+ std::istringstream stream(policy_names);
+ for (std::string line; std::getline(stream, line, ',');) {
+ size_t start = line.find_first_not_of(" \n\t\r\f\v");
+ if (start == std::string::npos) {
+ continue;
+ }
+ size_t end = line.find_last_not_of(" \n\t\r\f\v");
+ if (end == std::string::npos) {
+ continue;
+ }
+ std::string policy_name = line.substr(start, end + 1);
+ if (policy_name.empty()) {
+ continue;
+ }
+
+ if (policy_name == "anyPolicy") {
+ out->insert(der::Input(kAnyPolicyOid));
+ } else if (policy_name == "NIST-test-policy-1") {
+ out->insert(der::Input(kTestPolicy1));
+ } else if (policy_name == "NIST-test-policy-2") {
+ out->insert(der::Input(kTestPolicy2));
+ } else if (policy_name == "NIST-test-policy-3") {
+ out->insert(der::Input(kTestPolicy3));
+ } else if (policy_name == "NIST-test-policy-6") {
+ out->insert(der::Input(kTestPolicy6));
+ } else {
+ ADD_FAILURE() << "Unknown policy name: " << policy_name;
+ }
+ }
+}
+
+} // namespace
+
+PkitsTestInfo::PkitsTestInfo() {
+ SetInitialPolicySet("anyPolicy");
+ SetUserConstrainedPolicySet("NIST-test-policy-1");
+}
+
+PkitsTestInfo::PkitsTestInfo(const PkitsTestInfo& other) = default;
+
+PkitsTestInfo::~PkitsTestInfo() = default;
+
+void PkitsTestInfo::SetInitialExplicitPolicy(bool b) {
+ initial_explicit_policy =
+ b ? InitialExplicitPolicy::kTrue : InitialExplicitPolicy::kFalse;
+}
+
+void PkitsTestInfo::SetInitialPolicyMappingInhibit(bool b) {
+ initial_policy_mapping_inhibit = b ? InitialPolicyMappingInhibit::kTrue
+ : InitialPolicyMappingInhibit::kFalse;
+}
+
+void PkitsTestInfo::SetInitialInhibitAnyPolicy(bool b) {
+ initial_inhibit_any_policy =
+ b ? InitialAnyPolicyInhibit::kTrue : InitialAnyPolicyInhibit::kFalse;
+}
+
+void PkitsTestInfo::SetInitialPolicySet(const char* const policy_names) {
+ SetPolicySetFromString(policy_names, &initial_policy_set);
+}
+
+void PkitsTestInfo::SetUserConstrainedPolicySet(
+ const char* const policy_names) {
+ SetPolicySetFromString(policy_names, &user_constrained_policy_set);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/nist_pkits_unittest.h b/chromium/net/cert/pki/nist_pkits_unittest.h
new file mode 100644
index 00000000000..bf4d16485c9
--- /dev/null
+++ b/chromium/net/cert/pki/nist_pkits_unittest.h
@@ -0,0 +1,157 @@
+// Copyright 2016 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 NET_CERT_PKI_NIST_PKITS_UNITTEST_H_
+#define NET_CERT_PKI_NIST_PKITS_UNITTEST_H_
+
+#include <set>
+
+#include "net/cert/pki/test_helpers.h"
+#include "net/der/parse_values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+// Describes the inputs and outputs (other than the certificates) for
+// the PKITS tests.
+struct PkitsTestInfo {
+ // Default construction results in the "default settings".
+ PkitsTestInfo();
+ PkitsTestInfo(const PkitsTestInfo& other);
+ ~PkitsTestInfo();
+
+ // Sets |initial_policy_set| to the specified policies. The
+ // policies are described as comma-separated symbolic strings like
+ // "anyPolicy" and "NIST-test-policy-1".
+ //
+ // If this isn't called, the default is "anyPolicy".
+ void SetInitialPolicySet(const char* const policy_names);
+
+ // Sets |user_constrained_policy_set| to the specified policies. The
+ // policies are described as comma-separated symbolic strings like
+ // "anyPolicy" and "NIST-test-policy-1".
+ //
+ // If this isn't called, the default is "NIST-test-policy-1".
+ void SetUserConstrainedPolicySet(const char* const policy_names);
+
+ void SetInitialExplicitPolicy(bool b);
+ void SetInitialPolicyMappingInhibit(bool b);
+ void SetInitialInhibitAnyPolicy(bool b);
+
+ // ----------------
+ // Info
+ // ----------------
+
+ // The PKITS test number. For example, "4.1.1".
+ const char* test_number = nullptr;
+
+ // ----------------
+ // Inputs
+ // ----------------
+
+ // A set of policy OIDs to use for "initial-policy-set".
+ std::set<der::Input> initial_policy_set;
+
+ // The value of "initial-explicit-policy".
+ InitialExplicitPolicy initial_explicit_policy = InitialExplicitPolicy::kFalse;
+
+ // The value of "initial-policy-mapping-inhibit".
+ InitialPolicyMappingInhibit initial_policy_mapping_inhibit =
+ InitialPolicyMappingInhibit::kFalse;
+
+ // The value of "initial-inhibit-any-policy".
+ InitialAnyPolicyInhibit initial_inhibit_any_policy =
+ InitialAnyPolicyInhibit::kFalse;
+
+ // This is the time when PKITS was published.
+ der::GeneralizedTime time = {2011, 4, 15, 0, 0, 0};
+
+ // ----------------
+ // Expected outputs
+ // ----------------
+
+ // Whether path validation should succeed.
+ bool should_validate = false;
+
+ std::set<der::Input> user_constrained_policy_set;
+};
+
+// Parameterized test class for PKITS tests.
+// The instantiating code should define a PkitsTestDelegate with an appropriate
+// static RunTest method, and then INSTANTIATE_TYPED_TEST_SUITE_P for each
+// testcase (each TYPED_TEST_SUITE_P in pkits_testcases-inl.h).
+template <typename PkitsTestDelegate>
+class PkitsTest : public ::testing::Test {
+ public:
+ template <size_t num_certs, size_t num_crls>
+ void RunTest(const char* const (&cert_names)[num_certs],
+ const char* const (&crl_names)[num_crls],
+ const PkitsTestInfo& info) {
+ std::vector<std::string> cert_ders;
+ for (const std::string& s : cert_names)
+ cert_ders.push_back(net::ReadTestFileToString(
+ "net/third_party/nist-pkits/certs/" + s + ".crt"));
+ std::vector<std::string> crl_ders;
+ for (const std::string& s : crl_names)
+ crl_ders.push_back(net::ReadTestFileToString(
+ "net/third_party/nist-pkits/crls/" + s + ".crl"));
+
+ base::StringPiece test_number = info.test_number;
+
+ // Some of the PKITS tests are intentionally given different expectations
+ // from PKITS.pdf.
+ //
+ // Empty user_constrained_policy_set due to short-circuit on invalid
+ // signatures:
+ //
+ // 4.1.2 - Invalid CA Signature Test2
+ // 4.1.3 - Invalid EE Signature Test3
+ // 4.1.6 - Invalid DSA Signature Test6
+ //
+ // Expected to fail because DSA signatures are not supported:
+ //
+ // 4.1.4 - Valid DSA Signatures Test4
+ // 4.1.5 - Valid DSA Parameter Inheritance Test5
+ //
+ // Expected to fail because Name constraints on rfc822Names are not
+ // supported:
+ //
+ // 4.13.21 - Valid RFC822 nameConstraints Test21
+ // 4.13.23 - Valid RFC822 nameConstraints Test23
+ // 4.13.25 - Valid RFC822 nameConstraints Test25
+ // 4.13.27 - Valid DN and RFC822 nameConstraints Test27
+ //
+ // Expected to fail because Name constraints on
+ // uniformResourceIdentifiers are not supported:
+ //
+ // 4.13.34 - Valid URI nameConstraints Test34
+ // 4.13.36 - Valid URI nameConstraints Test36
+ if (test_number == "4.1.2" || test_number == "4.1.3" ||
+ test_number == "4.1.6") {
+ PkitsTestInfo modified_info = info;
+ modified_info.user_constrained_policy_set = {};
+ PkitsTestDelegate::RunTest(cert_ders, crl_ders, modified_info);
+ } else if (test_number == "4.1.4" || test_number == "4.1.5") {
+ PkitsTestInfo modified_info = info;
+ modified_info.user_constrained_policy_set = {};
+ modified_info.should_validate = false;
+ PkitsTestDelegate::RunTest(cert_ders, crl_ders, modified_info);
+ } else if (test_number == "4.13.21" || test_number == "4.13.23" ||
+ test_number == "4.13.25" || test_number == "4.13.27" ||
+ test_number == "4.13.34" || test_number == "4.13.36") {
+ PkitsTestInfo modified_info = info;
+ modified_info.should_validate = false;
+ PkitsTestDelegate::RunTest(cert_ders, crl_ders, modified_info);
+ } else {
+ PkitsTestDelegate::RunTest(cert_ders, crl_ders, info);
+ }
+ }
+};
+
+// Inline the generated test code:
+#include "net/third_party/nist-pkits/pkits_testcases-inl.h"
+
+} // namespace net
+
+#endif // NET_CERT_PKI_NIST_PKITS_UNITTEST_H_
diff --git a/chromium/net/cert/pki/ocsp.cc b/chromium/net/cert/pki/ocsp.cc
new file mode 100644
index 00000000000..46fd72f7109
--- /dev/null
+++ b/chromium/net/cert/pki/ocsp.cc
@@ -0,0 +1,1037 @@
+// Copyright 2016 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 "net/cert/pki/ocsp.h"
+
+#include <algorithm>
+
+#include "base/base64.h"
+#include "base/strings/string_util.h"
+#include "base/time/time.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/extended_key_usage.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/cert/pki/revocation_util.h"
+#include "net/cert/pki/verify_name_match.h"
+#include "net/cert/pki/verify_signed_data.h"
+#include "net/cert/x509_util.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "third_party/boringssl/src/include/openssl/digest.h"
+#include "third_party/boringssl/src/include/openssl/mem.h"
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "url/gurl.h"
+
+namespace net {
+
+OCSPCertID::OCSPCertID() = default;
+OCSPCertID::~OCSPCertID() = default;
+
+OCSPSingleResponse::OCSPSingleResponse() = default;
+OCSPSingleResponse::~OCSPSingleResponse() = default;
+
+OCSPResponseData::OCSPResponseData() = default;
+OCSPResponseData::~OCSPResponseData() = default;
+
+OCSPResponse::OCSPResponse() = default;
+OCSPResponse::~OCSPResponse() = default;
+
+// CertID ::= SEQUENCE {
+// hashAlgorithm AlgorithmIdentifier,
+// issuerNameHash OCTET STRING, -- Hash of issuer's DN
+// issuerKeyHash OCTET STRING, -- Hash of issuer's public key
+// serialNumber CertificateSerialNumber
+// }
+bool ParseOCSPCertID(const der::Input& raw_tlv, OCSPCertID* out) {
+ der::Parser outer_parser(raw_tlv);
+ der::Parser parser;
+ if (!outer_parser.ReadSequence(&parser))
+ return false;
+ if (outer_parser.HasMore())
+ return false;
+
+ der::Input sigalg_tlv;
+ if (!parser.ReadRawTLV(&sigalg_tlv))
+ return false;
+ if (!ParseHashAlgorithm(sigalg_tlv, &(out->hash_algorithm)))
+ return false;
+ if (!parser.ReadTag(der::kOctetString, &(out->issuer_name_hash)))
+ return false;
+ if (!parser.ReadTag(der::kOctetString, &(out->issuer_key_hash)))
+ return false;
+ if (!parser.ReadTag(der::kInteger, &(out->serial_number)))
+ return false;
+ CertErrors errors;
+ if (!VerifySerialNumber(out->serial_number, false /*warnings_only*/, &errors))
+ return false;
+
+ return !parser.HasMore();
+}
+
+namespace {
+
+// Parses |raw_tlv| to extract an OCSP RevokedInfo (RFC 6960) and stores the
+// result in the OCSPCertStatus |out|. Returns whether the parsing was
+// successful.
+//
+// RevokedInfo ::= SEQUENCE {
+// revocationTime GeneralizedTime,
+// revocationReason [0] EXPLICIT CRLReason OPTIONAL
+// }
+bool ParseRevokedInfo(const der::Input& raw_tlv, OCSPCertStatus* out) {
+ der::Parser parser(raw_tlv);
+ if (!parser.ReadGeneralizedTime(&(out->revocation_time)))
+ return false;
+
+ der::Input reason_input;
+ if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &reason_input,
+ &(out->has_reason))) {
+ return false;
+ }
+ if (out->has_reason) {
+ der::Parser reason_parser(reason_input);
+ der::Input reason_value_input;
+ uint8_t reason_value;
+ if (!reason_parser.ReadTag(der::kEnumerated, &reason_value_input))
+ return false;
+ if (!der::ParseUint8(reason_value_input, &reason_value))
+ return false;
+ if (reason_value >
+ static_cast<uint8_t>(OCSPCertStatus::RevocationReason::LAST)) {
+ return false;
+ }
+ out->revocation_reason =
+ static_cast<OCSPCertStatus::RevocationReason>(reason_value);
+ if (out->revocation_reason == OCSPCertStatus::RevocationReason::UNUSED)
+ return false;
+ if (reason_parser.HasMore())
+ return false;
+ }
+ return !parser.HasMore();
+}
+
+// Parses |raw_tlv| to extract an OCSP CertStatus (RFC 6960) and stores the
+// result in the OCSPCertStatus |out|. Returns whether the parsing was
+// successful.
+//
+// CertStatus ::= CHOICE {
+// good [0] IMPLICIT NULL,
+// revoked [1] IMPLICIT RevokedInfo,
+// unknown [2] IMPLICIT UnknownInfo
+// }
+//
+// UnknownInfo ::= NULL
+bool ParseCertStatus(const der::Input& raw_tlv, OCSPCertStatus* out) {
+ der::Parser parser(raw_tlv);
+ der::Tag status_tag;
+ der::Input status;
+ if (!parser.ReadTagAndValue(&status_tag, &status))
+ return false;
+
+ out->has_reason = false;
+ if (status_tag == der::ContextSpecificPrimitive(0)) {
+ out->status = OCSPRevocationStatus::GOOD;
+ } else if (status_tag == der::ContextSpecificConstructed(1)) {
+ out->status = OCSPRevocationStatus::REVOKED;
+ if (!ParseRevokedInfo(status, out))
+ return false;
+ } else if (status_tag == der::ContextSpecificPrimitive(2)) {
+ out->status = OCSPRevocationStatus::UNKNOWN;
+ } else {
+ return false;
+ }
+
+ return !parser.HasMore();
+}
+
+// Writes the hash of |value| as an OCTET STRING to |cbb|, using |hash_type| as
+// the algorithm. Returns true on success.
+bool AppendHashAsOctetString(const EVP_MD* hash_type,
+ CBB* cbb,
+ const der::Input& value) {
+ CBB octet_string;
+ unsigned hash_len;
+ uint8_t hash_buffer[EVP_MAX_MD_SIZE];
+
+ return CBB_add_asn1(cbb, &octet_string, CBS_ASN1_OCTETSTRING) &&
+ EVP_Digest(value.UnsafeData(), value.Length(), hash_buffer, &hash_len,
+ hash_type, nullptr) &&
+ CBB_add_bytes(&octet_string, hash_buffer, hash_len) && CBB_flush(cbb);
+}
+
+} // namespace
+
+// SingleResponse ::= SEQUENCE {
+// certID CertID,
+// certStatus CertStatus,
+// thisUpdate GeneralizedTime,
+// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
+// singleExtensions [1] EXPLICIT Extensions OPTIONAL
+// }
+bool ParseOCSPSingleResponse(const der::Input& raw_tlv,
+ OCSPSingleResponse* out) {
+ der::Parser outer_parser(raw_tlv);
+ der::Parser parser;
+ if (!outer_parser.ReadSequence(&parser))
+ return false;
+ if (outer_parser.HasMore())
+ return false;
+
+ if (!parser.ReadRawTLV(&(out->cert_id_tlv)))
+ return false;
+ der::Input status_tlv;
+ if (!parser.ReadRawTLV(&status_tlv))
+ return false;
+ if (!ParseCertStatus(status_tlv, &(out->cert_status)))
+ return false;
+ if (!parser.ReadGeneralizedTime(&(out->this_update)))
+ return false;
+
+ der::Input next_update_input;
+ if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
+ &next_update_input, &(out->has_next_update))) {
+ return false;
+ }
+ if (out->has_next_update) {
+ der::Parser next_update_parser(next_update_input);
+ if (!next_update_parser.ReadGeneralizedTime(&(out->next_update)))
+ return false;
+ if (next_update_parser.HasMore())
+ return false;
+ }
+
+ if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(1),
+ &(out->extensions), &(out->has_extensions))) {
+ return false;
+ }
+
+ return !parser.HasMore();
+}
+
+namespace {
+
+// Parses |raw_tlv| to extract a ResponderID (RFC 6960) and stores the
+// result in the ResponderID |out|. Returns whether the parsing was successful.
+//
+// ResponderID ::= CHOICE {
+// byName [1] Name,
+// byKey [2] KeyHash
+// }
+bool ParseResponderID(const der::Input& raw_tlv,
+ OCSPResponseData::ResponderID* out) {
+ der::Parser parser(raw_tlv);
+ der::Tag id_tag;
+ der::Input id_input;
+ if (!parser.ReadTagAndValue(&id_tag, &id_input))
+ return false;
+
+ if (id_tag == der::ContextSpecificConstructed(1)) {
+ out->type = OCSPResponseData::ResponderType::NAME;
+ out->name = id_input;
+ } else if (id_tag == der::ContextSpecificConstructed(2)) {
+ der::Parser key_parser(id_input);
+ der::Input key_hash;
+ if (!key_parser.ReadTag(der::kOctetString, &key_hash))
+ return false;
+ if (key_parser.HasMore())
+ return false;
+ if (key_hash.Length() != SHA_DIGEST_LENGTH)
+ return false;
+
+ out->type = OCSPResponseData::ResponderType::KEY_HASH;
+ out->key_hash = key_hash;
+ } else {
+ return false;
+ }
+ return !parser.HasMore();
+}
+
+} // namespace
+
+// ResponseData ::= SEQUENCE {
+// version [0] EXPLICIT Version DEFAULT v1,
+// responderID ResponderID,
+// producedAt GeneralizedTime,
+// responses SEQUENCE OF SingleResponse,
+// responseExtensions [1] EXPLICIT Extensions OPTIONAL
+// }
+bool ParseOCSPResponseData(const der::Input& raw_tlv, OCSPResponseData* out) {
+ der::Parser outer_parser(raw_tlv);
+ der::Parser parser;
+ if (!outer_parser.ReadSequence(&parser))
+ return false;
+ if (outer_parser.HasMore())
+ return false;
+
+ der::Input version_input;
+ bool version_present;
+ if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
+ &version_input, &version_present)) {
+ return false;
+ }
+
+ // For compatibilty, we ignore the restriction from X.690 Section 11.5 that
+ // DEFAULT values should be omitted for values equal to the default value.
+ // TODO: Add warning about non-strict parsing.
+ if (version_present) {
+ der::Parser version_parser(version_input);
+ if (!version_parser.ReadUint8(&(out->version)))
+ return false;
+ if (version_parser.HasMore())
+ return false;
+ } else {
+ out->version = 0;
+ }
+
+ if (out->version != 0)
+ return false;
+
+ der::Input responder_input;
+ if (!parser.ReadRawTLV(&responder_input))
+ return false;
+ if (!ParseResponderID(responder_input, &(out->responder_id)))
+ return false;
+ if (!parser.ReadGeneralizedTime(&(out->produced_at)))
+ return false;
+
+ der::Parser responses_parser;
+ if (!parser.ReadSequence(&responses_parser))
+ return false;
+ out->responses.clear();
+ while (responses_parser.HasMore()) {
+ der::Input single_response;
+ if (!responses_parser.ReadRawTLV(&single_response))
+ return false;
+ out->responses.push_back(single_response);
+ }
+
+ if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(1),
+ &(out->extensions), &(out->has_extensions))) {
+ return false;
+ }
+
+ return !parser.HasMore();
+}
+
+namespace {
+
+// Parses |raw_tlv| to extract a BasicOCSPResponse (RFC 6960) and stores the
+// result in the OCSPResponse |out|. Returns whether the parsing was
+// successful.
+//
+// BasicOCSPResponse ::= SEQUENCE {
+// tbsResponseData ResponseData,
+// signatureAlgorithm AlgorithmIdentifier,
+// signature BIT STRING,
+// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL
+// }
+bool ParseBasicOCSPResponse(const der::Input& raw_tlv, OCSPResponse* out) {
+ der::Parser outer_parser(raw_tlv);
+ der::Parser parser;
+ if (!outer_parser.ReadSequence(&parser))
+ return false;
+ if (outer_parser.HasMore())
+ return false;
+
+ if (!parser.ReadRawTLV(&(out->data)))
+ return false;
+ der::Input sigalg_tlv;
+ if (!parser.ReadRawTLV(&sigalg_tlv))
+ return false;
+ // TODO(crbug.com/634443): Propagate the errors.
+ absl::optional<SignatureAlgorithm> sigalg =
+ ParseSignatureAlgorithm(sigalg_tlv, /*errors=*/nullptr);
+ if (!sigalg)
+ return false;
+ out->signature_algorithm = sigalg.value();
+ absl::optional<der::BitString> signature = parser.ReadBitString();
+ if (!signature)
+ return false;
+ out->signature = signature.value();
+ der::Input certs_input;
+ if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &certs_input,
+ &(out->has_certs))) {
+ return false;
+ }
+
+ out->certs.clear();
+ if (out->has_certs) {
+ der::Parser certs_seq_parser(certs_input);
+ der::Parser certs_parser;
+ if (!certs_seq_parser.ReadSequence(&certs_parser))
+ return false;
+ if (certs_seq_parser.HasMore())
+ return false;
+ while (certs_parser.HasMore()) {
+ der::Input cert_tlv;
+ if (!certs_parser.ReadRawTLV(&cert_tlv))
+ return false;
+ out->certs.push_back(cert_tlv);
+ }
+ }
+
+ return !parser.HasMore();
+}
+
+} // namespace
+
+// OCSPResponse ::= SEQUENCE {
+// responseStatus OCSPResponseStatus,
+// responseBytes [0] EXPLICIT ResponseBytes OPTIONAL
+// }
+//
+// ResponseBytes ::= SEQUENCE {
+// responseType OBJECT IDENTIFIER,
+// response OCTET STRING
+// }
+bool ParseOCSPResponse(const der::Input& raw_tlv, OCSPResponse* out) {
+ der::Parser outer_parser(raw_tlv);
+ der::Parser parser;
+ if (!outer_parser.ReadSequence(&parser))
+ return false;
+ if (outer_parser.HasMore())
+ return false;
+
+ der::Input response_status_input;
+ uint8_t response_status;
+ if (!parser.ReadTag(der::kEnumerated, &response_status_input))
+ return false;
+ if (!der::ParseUint8(response_status_input, &response_status))
+ return false;
+ if (response_status >
+ static_cast<uint8_t>(OCSPResponse::ResponseStatus::LAST)) {
+ return false;
+ }
+ out->status = static_cast<OCSPResponse::ResponseStatus>(response_status);
+ if (out->status == OCSPResponse::ResponseStatus::UNUSED)
+ return false;
+
+ if (out->status == OCSPResponse::ResponseStatus::SUCCESSFUL) {
+ der::Parser outer_bytes_parser;
+ der::Parser bytes_parser;
+ if (!parser.ReadConstructed(der::ContextSpecificConstructed(0),
+ &outer_bytes_parser)) {
+ return false;
+ }
+ if (!outer_bytes_parser.ReadSequence(&bytes_parser))
+ return false;
+ if (outer_bytes_parser.HasMore())
+ return false;
+
+ der::Input type_oid;
+ if (!bytes_parser.ReadTag(der::kOid, &type_oid))
+ return false;
+ if (type_oid != der::Input(kBasicOCSPResponseOid))
+ return false;
+
+ // As per RFC 6960 Section 4.2.1, the value of |response| SHALL be the DER
+ // encoding of BasicOCSPResponse.
+ der::Input response;
+ if (!bytes_parser.ReadTag(der::kOctetString, &response))
+ return false;
+ if (!ParseBasicOCSPResponse(response, out))
+ return false;
+ if (bytes_parser.HasMore())
+ return false;
+ }
+
+ return !parser.HasMore();
+}
+
+namespace {
+
+// Checks that the |type| hash of |value| is equal to |hash|
+bool VerifyHash(const EVP_MD* type,
+ const der::Input& hash,
+ const der::Input& value) {
+ unsigned value_hash_len;
+ uint8_t value_hash[EVP_MAX_MD_SIZE];
+ if (!EVP_Digest(value.UnsafeData(), value.Length(), value_hash,
+ &value_hash_len, type, nullptr)) {
+ return false;
+ }
+
+ return hash == der::Input(value_hash, value_hash_len);
+}
+
+// Extracts the bytes of the SubjectPublicKey bit string given an SPKI. That is
+// to say, the value of subjectPublicKey without the leading unused bit
+// count octet.
+//
+// Returns true on success and fills |*spk_tlv| with the result.
+//
+// SubjectPublicKeyInfo ::= SEQUENCE {
+// algorithm AlgorithmIdentifier,
+// subjectPublicKey BIT STRING
+// }
+bool GetSubjectPublicKeyBytes(const der::Input& spki_tlv, der::Input* spk_tlv) {
+ base::StringPiece spk_strpiece;
+ if (!asn1::ExtractSubjectPublicKeyFromSPKI(spki_tlv.AsStringPiece(),
+ &spk_strpiece)) {
+ return false;
+ }
+
+ // ExtractSubjectPublicKeyFromSPKI() includes the unused bit count. For this
+ // application, the unused bit count must be zero, and is not included in the
+ // result.
+ if (!base::StartsWith(spk_strpiece, "\0"))
+ return false;
+ spk_strpiece.remove_prefix(1);
+
+ *spk_tlv = der::Input(spk_strpiece);
+ return true;
+}
+
+// Checks the OCSPCertID |id| identifies |certificate|.
+bool CheckCertIDMatchesCertificate(
+ const OCSPCertID& id,
+ const ParsedCertificate* certificate,
+ const ParsedCertificate* issuer_certificate) {
+ const EVP_MD* type = nullptr;
+ switch (id.hash_algorithm) {
+ case DigestAlgorithm::Md2:
+ case DigestAlgorithm::Md4:
+ case DigestAlgorithm::Md5:
+ // Unsupported.
+ return false;
+ case DigestAlgorithm::Sha1:
+ type = EVP_sha1();
+ break;
+ case DigestAlgorithm::Sha256:
+ type = EVP_sha256();
+ break;
+ case DigestAlgorithm::Sha384:
+ type = EVP_sha384();
+ break;
+ case DigestAlgorithm::Sha512:
+ type = EVP_sha512();
+ break;
+ }
+
+ if (!VerifyHash(type, id.issuer_name_hash, certificate->tbs().issuer_tlv))
+ return false;
+
+ der::Input key_tlv;
+ if (!GetSubjectPublicKeyBytes(issuer_certificate->tbs().spki_tlv, &key_tlv))
+ return false;
+
+ if (!VerifyHash(type, id.issuer_key_hash, key_tlv))
+ return false;
+
+ return id.serial_number == certificate->tbs().serial_number;
+}
+
+// TODO(eroman): Revisit how certificate parsing is used by this file. Ideally
+// would either pass in the parsed bits, or have a better abstraction for lazily
+// parsing.
+scoped_refptr<ParsedCertificate> OCSPParseCertificate(base::StringPiece der) {
+ ParseCertificateOptions parse_options;
+ parse_options.allow_invalid_serial_numbers = true;
+
+ // TODO(eroman): Swallows the parsing errors. However uses a permissive
+ // parsing model.
+ CertErrors errors;
+ return ParsedCertificate::Create(x509_util::CreateCryptoBuffer(der), {},
+ &errors);
+}
+
+// Checks that the ResponderID |id| matches the certificate |cert| either
+// by verifying the name matches that of the certificate or that the hash
+// matches the certificate's public key hash (RFC 6960, 4.2.2.3).
+[[nodiscard]] bool CheckResponderIDMatchesCertificate(
+ const OCSPResponseData::ResponderID& id,
+ const ParsedCertificate* cert) {
+ switch (id.type) {
+ case OCSPResponseData::ResponderType::NAME: {
+ der::Input name_rdn;
+ der::Input cert_rdn;
+ if (!der::Parser(id.name).ReadTag(der::kSequence, &name_rdn) ||
+ !der::Parser(cert->tbs().subject_tlv)
+ .ReadTag(der::kSequence, &cert_rdn))
+ return false;
+ return VerifyNameMatch(name_rdn, cert_rdn);
+ }
+ case OCSPResponseData::ResponderType::KEY_HASH: {
+ der::Input key;
+ if (!GetSubjectPublicKeyBytes(cert->tbs().spki_tlv, &key))
+ return false;
+ return VerifyHash(EVP_sha1(), id.key_hash, key);
+ }
+ }
+
+ return false;
+}
+
+// Verifies that |responder_certificate| has been authority for OCSP signing,
+// delegated to it by |issuer_certificate|.
+//
+// TODO(eroman): No revocation checks are done (see id-pkix-ocsp-nocheck in the
+// spec). extension).
+//
+// TODO(eroman): Not all properties of the certificate are verified, only the
+// signature and EKU. Can full RFC 5280 validation be used, or are there
+// compatibility concerns?
+[[nodiscard]] bool VerifyAuthorizedResponderCert(
+ const ParsedCertificate* responder_certificate,
+ const ParsedCertificate* issuer_certificate) {
+ // The Authorized Responder must be directly signed by the issuer of the
+ // certificate being checked.
+ // TODO(eroman): Must check the signature algorithm against policy.
+ if (!VerifySignedData(responder_certificate->signature_algorithm(),
+ responder_certificate->tbs_certificate_tlv(),
+ responder_certificate->signature_value(),
+ issuer_certificate->tbs().spki_tlv)) {
+ return false;
+ }
+
+ // The Authorized Responder must include the value id-kp-OCSPSigning as
+ // part of the extended key usage extension.
+ if (!responder_certificate->has_extended_key_usage())
+ return false;
+ const std::vector<der::Input>& ekus =
+ responder_certificate->extended_key_usage();
+ if (std::find(ekus.begin(), ekus.end(), der::Input(kOCSPSigning)) ==
+ ekus.end()) {
+ return false;
+ }
+
+ return true;
+}
+
+[[nodiscard]] bool VerifyOCSPResponseSignatureGivenCert(
+ const OCSPResponse& response,
+ const ParsedCertificate* cert) {
+ // TODO(eroman): Must check the signature algorithm against policy.
+ return VerifySignedData(response.signature_algorithm, response.data,
+ response.signature, cert->tbs().spki_tlv);
+}
+
+// Verifies that the OCSP response has a valid signature using
+// |issuer_certificate|, or an authorized responder issued by
+// |issuer_certificate| for OCSP signing.
+[[nodiscard]] bool VerifyOCSPResponseSignature(
+ const OCSPResponse& response,
+ const OCSPResponseData& response_data,
+ const ParsedCertificate* issuer_certificate) {
+ // In order to verify the OCSP signature, a valid responder matching the OCSP
+ // Responder ID must be located (RFC 6960, 4.2.2.2). The responder is allowed
+ // to be either the certificate issuer or a delegated authority directly
+ // signed by the issuer.
+ if (CheckResponderIDMatchesCertificate(response_data.responder_id,
+ issuer_certificate) &&
+ VerifyOCSPResponseSignatureGivenCert(response, issuer_certificate)) {
+ return true;
+ }
+
+ // Otherwise search through the provided certificates for the Authorized
+ // Responder. Want a certificate that:
+ // (1) Matches the OCSP Responder ID.
+ // (2) Has been given authority for OCSP signing by |issuer_certificate|.
+ // (3) Has signed the OCSP response using its public key.
+ for (const auto& responder_cert_tlv : response.certs) {
+ scoped_refptr<ParsedCertificate> cur_responder_certificate =
+ OCSPParseCertificate(responder_cert_tlv.AsStringPiece());
+
+ // If failed parsing the certificate, keep looking.
+ if (!cur_responder_certificate)
+ continue;
+
+ // If the certificate doesn't match the OCSP's responder ID, keep looking.
+ if (!CheckResponderIDMatchesCertificate(response_data.responder_id,
+ cur_responder_certificate.get())) {
+ continue;
+ }
+
+ // If the certificate isn't a valid Authorized Responder certificate, keep
+ // looking.
+ if (!VerifyAuthorizedResponderCert(cur_responder_certificate.get(),
+ issuer_certificate)) {
+ continue;
+ }
+
+ // If the certificate signed this OCSP response, have found a match.
+ // Otherwise keep looking.
+ if (VerifyOCSPResponseSignatureGivenCert(response,
+ cur_responder_certificate.get())) {
+ return true;
+ }
+ }
+
+ // Failed to confirm the validity of the OCSP signature using any of the
+ // candidate certificates.
+ return false;
+}
+
+// Parse ResponseData and return false if any unhandled critical extensions are
+// found. No known critical ResponseData extensions exist.
+bool ParseOCSPResponseDataExtensions(
+ const der::Input& response_extensions,
+ OCSPVerifyResult::ResponseStatus* response_details) {
+ std::map<der::Input, ParsedExtension> extensions;
+ if (!ParseExtensions(response_extensions, &extensions)) {
+ *response_details = OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR;
+ return false;
+ }
+
+ for (const auto& ext : extensions) {
+ // TODO: handle ResponseData extensions
+
+ if (ext.second.critical) {
+ *response_details = OCSPVerifyResult::UNHANDLED_CRITICAL_EXTENSION;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Parse SingleResponse and return false if any unhandled critical extensions
+// (other than the CT extension) are found. The CT-SCT extension is not required
+// to be marked critical, but since it is handled by Chrome, we will overlook
+// the flag setting.
+bool ParseOCSPSingleResponseExtensions(
+ const der::Input& single_extensions,
+ OCSPVerifyResult::ResponseStatus* response_details) {
+ std::map<der::Input, ParsedExtension> extensions;
+ if (!ParseExtensions(single_extensions, &extensions)) {
+ *response_details = OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR;
+ return false;
+ }
+
+ // The wire form of the OID 1.3.6.1.4.1.11129.2.4.5 - OCSP SingleExtension for
+ // X.509v3 Certificate Transparency Signed Certificate Timestamp List, see
+ // Section 3.3 of RFC6962.
+ const uint8_t ct_ocsp_ext_oid[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
+ 0xD6, 0x79, 0x02, 0x04, 0x05};
+ der::Input ct_ext_oid(ct_ocsp_ext_oid);
+
+ for (const auto& ext : extensions) {
+ // The CT OCSP extension is handled in ct::ExtractSCTListFromOCSPResponse
+ if (ext.second.oid == ct_ext_oid)
+ continue;
+
+ // TODO: handle SingleResponse extensions
+
+ if (ext.second.critical) {
+ *response_details = OCSPVerifyResult::UNHANDLED_CRITICAL_EXTENSION;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Loops through the OCSPSingleResponses to find the best match for |cert|.
+OCSPRevocationStatus GetRevocationStatusForCert(
+ const OCSPResponseData& response_data,
+ const ParsedCertificate* cert,
+ const ParsedCertificate* issuer_certificate,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age,
+ OCSPVerifyResult::ResponseStatus* response_details) {
+ OCSPRevocationStatus result = OCSPRevocationStatus::UNKNOWN;
+ *response_details = OCSPVerifyResult::NO_MATCHING_RESPONSE;
+
+ for (const auto& single_response_der : response_data.responses) {
+ // In the common case, there should only be one SingleResponse in the
+ // ResponseData (matching the certificate requested and used on this
+ // connection). However, it is possible for the OCSP responder to provide
+ // multiple responses for multiple certificates. Look through all the
+ // provided SingleResponses, and check to see if any match the
+ // certificate. A SingleResponse matches a certificate if it has the same
+ // serial number, issuer name (hash), and issuer public key (hash).
+ OCSPSingleResponse single_response;
+ if (!ParseOCSPSingleResponse(single_response_der, &single_response))
+ return OCSPRevocationStatus::UNKNOWN;
+
+ // Reject unhandled critical extensions in SingleResponse
+ if (single_response.has_extensions &&
+ !ParseOCSPSingleResponseExtensions(single_response.extensions,
+ response_details)) {
+ return OCSPRevocationStatus::UNKNOWN;
+ }
+
+ OCSPCertID cert_id;
+ if (!ParseOCSPCertID(single_response.cert_id_tlv, &cert_id))
+ return OCSPRevocationStatus::UNKNOWN;
+ if (!CheckCertIDMatchesCertificate(cert_id, cert, issuer_certificate))
+ continue;
+
+ // The SingleResponse matches the certificate, but may be out of date. Out
+ // of date responses are noted seperate from responses with mismatched
+ // serial numbers. If an OCSP responder provides both an up to date
+ // response and an expired response, the up to date response takes
+ // precedence (PROVIDED > INVALID_DATE).
+ if (!CheckRevocationDateValid(single_response.this_update,
+ single_response.has_next_update
+ ? &single_response.next_update
+ : nullptr,
+ verify_time, max_age)) {
+ if (*response_details != OCSPVerifyResult::PROVIDED)
+ *response_details = OCSPVerifyResult::INVALID_DATE;
+ continue;
+ }
+
+ // In the case with multiple matching and up to date responses, keep only
+ // the strictest status (REVOKED > UNKNOWN > GOOD).
+ if (*response_details != OCSPVerifyResult::PROVIDED ||
+ result == OCSPRevocationStatus::GOOD ||
+ single_response.cert_status.status == OCSPRevocationStatus::REVOKED) {
+ result = single_response.cert_status.status;
+ }
+ *response_details = OCSPVerifyResult::PROVIDED;
+ }
+
+ return result;
+}
+
+OCSPRevocationStatus CheckOCSP(
+ base::StringPiece raw_response,
+ base::StringPiece certificate_der,
+ const ParsedCertificate* certificate,
+ base::StringPiece issuer_certificate_der,
+ const ParsedCertificate* issuer_certificate,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age,
+ OCSPVerifyResult::ResponseStatus* response_details) {
+ *response_details = OCSPVerifyResult::NOT_CHECKED;
+
+ if (raw_response.empty()) {
+ *response_details = OCSPVerifyResult::MISSING;
+ return OCSPRevocationStatus::UNKNOWN;
+ }
+
+ der::Input response_der(raw_response);
+ OCSPResponse response;
+ if (!ParseOCSPResponse(response_der, &response)) {
+ *response_details = OCSPVerifyResult::PARSE_RESPONSE_ERROR;
+ return OCSPRevocationStatus::UNKNOWN;
+ }
+
+ // RFC 6960 defines all responses |response_status| != SUCCESSFUL as error
+ // responses. No revocation information is provided on error responses, and
+ // the OCSPResponseData structure is not set.
+ if (response.status != OCSPResponse::ResponseStatus::SUCCESSFUL) {
+ *response_details = OCSPVerifyResult::ERROR_RESPONSE;
+ return OCSPRevocationStatus::UNKNOWN;
+ }
+
+ // Actual revocation information is contained within the BasicOCSPResponse as
+ // a ResponseData structure. The BasicOCSPResponse was parsed above, and
+ // contains an unparsed ResponseData. From RFC 6960:
+ //
+ // BasicOCSPResponse ::= SEQUENCE {
+ // tbsResponseData ResponseData,
+ // signatureAlgorithm AlgorithmIdentifier,
+ // signature BIT STRING,
+ // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ //
+ // ResponseData ::= SEQUENCE {
+ // version [0] EXPLICIT Version DEFAULT v1,
+ // responderID ResponderID,
+ // producedAt GeneralizedTime,
+ // responses SEQUENCE OF SingleResponse,
+ // responseExtensions [1] EXPLICIT Extensions OPTIONAL }
+ OCSPResponseData response_data;
+ if (!ParseOCSPResponseData(response.data, &response_data)) {
+ *response_details = OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR;
+ return OCSPRevocationStatus::UNKNOWN;
+ }
+
+ // Process the OCSP ResponseData extensions. In particular, must reject if
+ // there are any critical extensions that are not understood.
+ if (response_data.has_extensions &&
+ !ParseOCSPResponseDataExtensions(response_data.extensions,
+ response_details)) {
+ return OCSPRevocationStatus::UNKNOWN;
+ }
+
+ scoped_refptr<ParsedCertificate> parsed_certificate;
+ scoped_refptr<ParsedCertificate> parsed_issuer_certificate;
+ if (!certificate) {
+ parsed_certificate = OCSPParseCertificate(certificate_der);
+ certificate = parsed_certificate.get();
+ }
+ if (!issuer_certificate) {
+ parsed_issuer_certificate = OCSPParseCertificate(issuer_certificate_der);
+ issuer_certificate = parsed_issuer_certificate.get();
+ }
+
+ if (!certificate || !issuer_certificate) {
+ *response_details = OCSPVerifyResult::NOT_CHECKED;
+ return OCSPRevocationStatus::UNKNOWN;
+ }
+
+ // If producedAt is outside of the certificate validity period, reject the
+ // response.
+ if (response_data.produced_at < certificate->tbs().validity_not_before ||
+ response_data.produced_at > certificate->tbs().validity_not_after) {
+ *response_details = OCSPVerifyResult::BAD_PRODUCED_AT;
+ return OCSPRevocationStatus::UNKNOWN;
+ }
+
+ // Look through all of the OCSPSingleResponses for a match (based on CertID
+ // and time).
+ OCSPRevocationStatus status =
+ GetRevocationStatusForCert(response_data, certificate, issuer_certificate,
+ verify_time, max_age, response_details);
+
+ // Check that the OCSP response has a valid signature. It must either be
+ // signed directly by the issuing certificate, or a valid authorized
+ // responder.
+ if (!VerifyOCSPResponseSignature(response, response_data,
+ issuer_certificate)) {
+ return OCSPRevocationStatus::UNKNOWN;
+ }
+
+ return status;
+}
+
+} // namespace
+
+OCSPRevocationStatus CheckOCSP(
+ base::StringPiece raw_response,
+ base::StringPiece certificate_der,
+ base::StringPiece issuer_certificate_der,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age,
+ OCSPVerifyResult::ResponseStatus* response_details) {
+ return CheckOCSP(raw_response, certificate_der, nullptr,
+ issuer_certificate_der, nullptr, verify_time, max_age,
+ response_details);
+}
+
+OCSPRevocationStatus CheckOCSP(
+ base::StringPiece raw_response,
+ const ParsedCertificate* certificate,
+ const ParsedCertificate* issuer_certificate,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age,
+ OCSPVerifyResult::ResponseStatus* response_details) {
+ return CheckOCSP(raw_response, base::StringPiece(), certificate,
+ base::StringPiece(), issuer_certificate, verify_time,
+ max_age, response_details);
+}
+
+bool CreateOCSPRequest(const ParsedCertificate* cert,
+ const ParsedCertificate* issuer,
+ std::vector<uint8_t>* request_der) {
+ request_der->clear();
+
+ bssl::ScopedCBB cbb;
+
+ // This initial buffer size is big enough for 20 octet long serial numbers
+ // (upper bound from RFC 5280) and then a handful of extra bytes. This
+ // number doesn't matter for correctness.
+ const size_t kInitialBufferSize = 100;
+
+ if (!CBB_init(cbb.get(), kInitialBufferSize))
+ return false;
+
+ // OCSPRequest ::= SEQUENCE {
+ // tbsRequest TBSRequest,
+ // optionalSignature [0] EXPLICIT Signature OPTIONAL }
+ //
+ // TBSRequest ::= SEQUENCE {
+ // version [0] EXPLICIT Version DEFAULT v1,
+ // requestorName [1] EXPLICIT GeneralName OPTIONAL,
+ // requestList SEQUENCE OF Request,
+ // requestExtensions [2] EXPLICIT Extensions OPTIONAL }
+ CBB ocsp_request;
+ if (!CBB_add_asn1(cbb.get(), &ocsp_request, CBS_ASN1_SEQUENCE))
+ return false;
+
+ CBB tbs_request;
+ if (!CBB_add_asn1(&ocsp_request, &tbs_request, CBS_ASN1_SEQUENCE))
+ return false;
+
+ // "version", "requestorName", and "requestExtensions" are omitted.
+
+ CBB request_list;
+ if (!CBB_add_asn1(&tbs_request, &request_list, CBS_ASN1_SEQUENCE))
+ return false;
+
+ CBB request;
+ if (!CBB_add_asn1(&request_list, &request, CBS_ASN1_SEQUENCE))
+ return false;
+
+ // Request ::= SEQUENCE {
+ // reqCert CertID,
+ // singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
+ CBB req_cert;
+ if (!CBB_add_asn1(&request, &req_cert, CBS_ASN1_SEQUENCE))
+ return false;
+
+ // CertID ::= SEQUENCE {
+ // hashAlgorithm AlgorithmIdentifier,
+ // issuerNameHash OCTET STRING, -- Hash of issuer's DN
+ // issuerKeyHash OCTET STRING, -- Hash of issuer's public key
+ // serialNumber CertificateSerialNumber }
+
+ // TODO(eroman): Don't use SHA1.
+ const EVP_MD* md = EVP_sha1();
+ if (!EVP_marshal_digest_algorithm(&req_cert, md))
+ return false;
+
+ AppendHashAsOctetString(md, &req_cert, issuer->tbs().subject_tlv);
+
+ der::Input key_tlv;
+ if (!GetSubjectPublicKeyBytes(issuer->tbs().spki_tlv, &key_tlv))
+ return false;
+ AppendHashAsOctetString(md, &req_cert, key_tlv);
+
+ CBB serial_number;
+ if (!CBB_add_asn1(&req_cert, &serial_number, CBS_ASN1_INTEGER))
+ return false;
+ if (!CBB_add_bytes(&serial_number, cert->tbs().serial_number.UnsafeData(),
+ cert->tbs().serial_number.Length())) {
+ return false;
+ }
+
+ uint8_t* result_bytes;
+ size_t result_bytes_length;
+ if (!CBB_finish(cbb.get(), &result_bytes, &result_bytes_length))
+ return false;
+ bssl::UniquePtr<uint8_t> delete_tbs_cert_bytes(result_bytes);
+
+ request_der->assign(result_bytes, result_bytes + result_bytes_length);
+ return true;
+}
+
+// From RFC 2560 section A.1.1:
+//
+// An OCSP request using the GET method is constructed as follows:
+//
+// GET {url}/{url-encoding of base-64 encoding of the DER encoding of
+// the OCSPRequest}
+GURL CreateOCSPGetURL(const ParsedCertificate* cert,
+ const ParsedCertificate* issuer,
+ base::StringPiece ocsp_responder_url) {
+ std::vector<uint8_t> ocsp_request_der;
+ if (!CreateOCSPRequest(cert, issuer, &ocsp_request_der)) {
+ // Unexpected (means BoringSSL failed an operation).
+ return GURL();
+ }
+
+ // Base64 encode the request data.
+ std::string b64_encoded;
+ base::Base64Encode(
+ base::StringPiece(reinterpret_cast<const char*>(ocsp_request_der.data()),
+ ocsp_request_der.size()),
+ &b64_encoded);
+
+ // In theory +, /, and = are valid in paths and don't need to be escaped.
+ // However from the example in RFC 5019 section 5 it is clear that the intent
+ // is to escape non-alphanumeric characters (the example conclusively escapes
+ // '/' and '=', but doesn't clarify '+').
+ base::ReplaceSubstringsAfterOffset(&b64_encoded, 0, "+", "%2B");
+ base::ReplaceSubstringsAfterOffset(&b64_encoded, 0, "/", "%2F");
+ base::ReplaceSubstringsAfterOffset(&b64_encoded, 0, "=", "%3D");
+
+ // No attempt is made to collapse double slashes for URLs that end in slash,
+ // since the spec doesn't do that.
+ return GURL(std::string(ocsp_responder_url) + "/" + b64_encoded);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/ocsp.h b/chromium/net/cert/pki/ocsp.h
new file mode 100644
index 00000000000..6a2a5e5b7d3
--- /dev/null
+++ b/chromium/net/cert/pki/ocsp.h
@@ -0,0 +1,328 @@
+// Copyright 2016 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 NET_CERT_PKI_OCSP_H_
+#define NET_CERT_PKI_OCSP_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/strings/string_piece_forward.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+#include "net/cert/ocsp_revocation_status.h"
+#include "net/cert/ocsp_verify_result.h"
+#include "net/cert/pki/parse_certificate.h"
+#include "net/cert/pki/signature_algorithm.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "net/der/parser.h"
+#include "net/der/tag.h"
+
+class GURL;
+
+namespace base {
+class Time;
+class TimeDelta;
+} // namespace base
+
+namespace net {
+
+class ParsedCertificate;
+
+// OCSPCertID contains a representation of a DER-encoded RFC 6960 "CertID".
+//
+// CertID ::= SEQUENCE {
+// hashAlgorithm AlgorithmIdentifier,
+// issuerNameHash OCTET STRING, -- Hash of issuer's DN
+// issuerKeyHash OCTET STRING, -- Hash of issuer's public key
+// serialNumber CertificateSerialNumber
+// }
+struct NET_EXPORT_PRIVATE OCSPCertID {
+ OCSPCertID();
+ ~OCSPCertID();
+
+ DigestAlgorithm hash_algorithm;
+ der::Input issuer_name_hash;
+ der::Input issuer_key_hash;
+ der::Input serial_number;
+};
+
+// OCSPCertStatus contains a representation of a DER-encoded RFC 6960
+// "CertStatus". |revocation_time| and |has_reason| are only valid when
+// |status| is REVOKED. |revocation_reason| is only valid when |has_reason| is
+// true.
+//
+// CertStatus ::= CHOICE {
+// good [0] IMPLICIT NULL,
+// revoked [1] IMPLICIT RevokedInfo,
+// unknown [2] IMPLICIT UnknownInfo
+// }
+//
+// RevokedInfo ::= SEQUENCE {
+// revocationTime GeneralizedTime,
+// revocationReason [0] EXPLICIT CRLReason OPTIONAL
+// }
+//
+// UnknownInfo ::= NULL
+//
+// CRLReason ::= ENUMERATED {
+// unspecified (0),
+// keyCompromise (1),
+// cACompromise (2),
+// affiliationChanged (3),
+// superseded (4),
+// cessationOfOperation (5),
+// certificateHold (6),
+// -- value 7 is not used
+// removeFromCRL (8),
+// privilegeWithdrawn (9),
+// aACompromise (10)
+// }
+// (from RFC 5280)
+struct OCSPCertStatus {
+ // Correspond to the values of CRLReason
+ enum class RevocationReason {
+ UNSPECIFIED = 0,
+ KEY_COMPROMISE = 1,
+ CA_COMPROMISE = 2,
+ AFFILIATION_CHANGED = 3,
+ SUPERSEDED = 4,
+ CESSATION_OF_OPERATION = 5,
+ CERTIFICATE_HOLD = 6,
+ UNUSED = 7,
+ REMOVE_FROM_CRL = 8,
+ PRIVILEGE_WITHDRAWN = 9,
+ AA_COMPROMISE = 10,
+
+ LAST = AA_COMPROMISE,
+ };
+
+ OCSPRevocationStatus status;
+ der::GeneralizedTime revocation_time;
+ bool has_reason;
+ RevocationReason revocation_reason;
+};
+
+// OCSPSingleResponse contains a representation of a DER-encoded RFC 6960
+// "SingleResponse". The |cert_id_tlv| and |extensions| fields are pointers to
+// the original object and are only valid as long as it is alive. They also
+// aren't verified until they are parsed. |next_update| is only valid if
+// |has_next_update| is true and |extensions| is only valid if |has_extensions|
+// is true.
+//
+// SingleResponse ::= SEQUENCE {
+// certID CertID,
+// certStatus CertStatus,
+// thisUpdate GeneralizedTime,
+// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
+// singleExtensions [1] EXPLICIT Extensions OPTIONAL
+// }
+struct NET_EXPORT OCSPSingleResponse {
+ OCSPSingleResponse();
+ ~OCSPSingleResponse();
+
+ der::Input cert_id_tlv;
+ OCSPCertStatus cert_status;
+ der::GeneralizedTime this_update;
+ bool has_next_update;
+ der::GeneralizedTime next_update;
+ bool has_extensions;
+ der::Input extensions;
+};
+
+// OCSPResponseData contains a representation of a DER-encoded RFC 6960
+// "ResponseData". The |responses| and |extensions| fields are pointers to the
+// original object and are only valid as long as it is alive. They also aren't
+// verified until they are parsed into OCSPSingleResponse and ParsedExtensions.
+// |extensions| is only valid if |has_extensions| is true.
+//
+// ResponseData ::= SEQUENCE {
+// version [0] EXPLICIT Version DEFAULT v1,
+// responderID ResponderID,
+// producedAt GeneralizedTime,
+// responses SEQUENCE OF SingleResponse,
+// responseExtensions [1] EXPLICIT Extensions OPTIONAL
+// }
+struct NET_EXPORT OCSPResponseData {
+ enum class ResponderType { NAME, KEY_HASH };
+
+ struct ResponderID {
+ ResponderType type;
+ der::Input name;
+ der::Input key_hash;
+ };
+
+ OCSPResponseData();
+ ~OCSPResponseData();
+
+ uint8_t version;
+ OCSPResponseData::ResponderID responder_id;
+ der::GeneralizedTime produced_at;
+ std::vector<der::Input> responses;
+ bool has_extensions;
+ der::Input extensions;
+};
+
+// OCSPResponse contains a representation of a DER-encoded RFC 6960
+// "OCSPResponse" and the corresponding "BasicOCSPResponse". The |data| field
+// is a pointer to the original object and are only valid as long is it is
+// alive. The |data| field isn't verified until it is parsed into an
+// OCSPResponseData. |data|, |signature_algorithm|, |signature|, and
+// |has_certs| is only valid if |status| is SUCCESSFUL. |certs| is only valid
+// if |has_certs| is true.
+//
+// OCSPResponse ::= SEQUENCE {
+// responseStatus OCSPResponseStatus,
+// responseBytes [0] EXPLICIT ResponseBytes OPTIONAL
+// }
+//
+// ResponseBytes ::= SEQUENCE {
+// responseType OBJECT IDENTIFIER,
+// response OCTET STRING
+// }
+//
+// BasicOCSPResponse ::= SEQUENCE {
+// tbsResponseData ResponseData,
+// signatureAlgorithm AlgorithmIdentifier,
+// signature BIT STRING,
+// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL
+// }
+//
+// OCSPResponseStatus ::= ENUMERATED {
+// successful (0), -- Response has valid confirmations
+// malformedRequest (1), -- Illegal confirmation request
+// internalError (2), -- Internal error in issuer
+// tryLater (3), -- Try again later
+// -- (4) is not used
+// sigRequired (5), -- Must sign the request
+// unauthorized (6) -- Request unauthorized
+// }
+struct NET_EXPORT OCSPResponse {
+ // Correspond to the values of OCSPResponseStatus
+ enum class ResponseStatus {
+ SUCCESSFUL = 0,
+ MALFORMED_REQUEST = 1,
+ INTERNAL_ERROR = 2,
+ TRY_LATER = 3,
+ UNUSED = 4,
+ SIG_REQUIRED = 5,
+ UNAUTHORIZED = 6,
+
+ LAST = UNAUTHORIZED,
+ };
+
+ OCSPResponse();
+ ~OCSPResponse();
+
+ ResponseStatus status;
+ der::Input data;
+ SignatureAlgorithm signature_algorithm;
+ der::BitString signature;
+ bool has_certs;
+ std::vector<der::Input> certs;
+};
+
+// From RFC 6960:
+//
+// id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
+// id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
+//
+// In dotted notation: 1.3.6.1.5.5.7.48.1.1
+inline constexpr uint8_t kBasicOCSPResponseOid[] = {
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01};
+
+// Parses a DER-encoded OCSP "CertID" as specified by RFC 6960. Returns true on
+// success and sets the results in |out|.
+//
+// On failure |out| has an undefined state. Some of its fields may have been
+// updated during parsing, whereas others may not have been changed.
+NET_EXPORT_PRIVATE bool ParseOCSPCertID(const der::Input& raw_tlv,
+ OCSPCertID* out);
+
+// Parses a DER-encoded OCSP "SingleResponse" as specified by RFC 6960. Returns
+// true on success and sets the results in |out|. The resulting |out|
+// references data from |raw_tlv| and is only valid for the lifetime of
+// |raw_tlv|.
+//
+// On failure |out| has an undefined state. Some of its fields may have been
+// updated during parsing, whereas others may not have been changed.
+NET_EXPORT_PRIVATE bool ParseOCSPSingleResponse(const der::Input& raw_tlv,
+ OCSPSingleResponse* out);
+
+// Parses a DER-encoded OCSP "ResponseData" as specified by RFC 6960. Returns
+// true on success and sets the results in |out|. The resulting |out|
+// references data from |raw_tlv| and is only valid for the lifetime of
+// |raw_tlv|.
+//
+// On failure |out| has an undefined state. Some of its fields may have been
+// updated during parsing, whereas others may not have been changed.
+NET_EXPORT_PRIVATE bool ParseOCSPResponseData(const der::Input& raw_tlv,
+ OCSPResponseData* out);
+
+// Parses a DER-encoded "OCSPResponse" as specified by RFC 6960. Returns true
+// on success and sets the results in |out|. The resulting |out|
+// references data from |raw_tlv| and is only valid for the lifetime of
+// |raw_tlv|.
+//
+// On failure |out| has an undefined state. Some of its fields may have been
+// updated during parsing, whereas others may not have been changed.
+NET_EXPORT_PRIVATE bool ParseOCSPResponse(const der::Input& raw_tlv,
+ OCSPResponse* out);
+
+// Checks the revocation status of the certificate |certificate_der| by using
+// the DER-encoded |raw_response|.
+//
+// Returns GOOD if the OCSP response indicates the certificate is not revoked,
+// REVOKED if it indicates it is revoked, or UNKNOWN for all other cases.
+//
+// * |raw_response|: A DER encoded OCSPResponse.
+// * |certificate_der|: The certificate being checked for revocation.
+// * |issuer_certificate_der|: The certificate that signed |certificate_der|.
+// The caller must have already performed path verification.
+// * |verify_time|: The time to use when checking revocation status.
+// * |max_age|: The maximum age for an OCSP response, implemented as time since
+// the |this_update| field in OCSPSingleResponse. Responses older than
+// |max_age| will be considered invalid.
+// * |response_details|: Additional details about failures.
+[[nodiscard]] NET_EXPORT OCSPRevocationStatus
+CheckOCSP(base::StringPiece raw_response,
+ base::StringPiece certificate_der,
+ base::StringPiece issuer_certificate_der,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age,
+ OCSPVerifyResult::ResponseStatus* response_details);
+
+// Checks the revocation status of |certificate| by using the DER-encoded
+// |raw_response|.
+//
+// Arguments are the same as above, except that it takes already parsed
+// instances of the certificate and issuer certificate.
+[[nodiscard]] NET_EXPORT OCSPRevocationStatus
+CheckOCSP(base::StringPiece raw_response,
+ const ParsedCertificate* certificate,
+ const ParsedCertificate* issuer_certificate,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age,
+ OCSPVerifyResult::ResponseStatus* response_details);
+
+// Creates a DER-encoded OCSPRequest for |cert|. The request is fairly basic:
+// * No signature
+// * No requestorName
+// * No extensions
+// * Uses SHA1 for all hashes.
+//
+// Returns true on success and fills |request_der| with the resulting bytes.
+NET_EXPORT bool CreateOCSPRequest(const ParsedCertificate* cert,
+ const ParsedCertificate* issuer,
+ std::vector<uint8_t>* request_der);
+
+// Creates a URL to issue a GET request for OCSP information for |cert|.
+NET_EXPORT GURL CreateOCSPGetURL(const ParsedCertificate* cert,
+ const ParsedCertificate* issuer,
+ base::StringPiece ocsp_responder_url);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_OCSP_H_
diff --git a/chromium/net/cert/pki/ocsp_parse_ocsp_cert_id_fuzzer.cc b/chromium/net/cert/pki/ocsp_parse_ocsp_cert_id_fuzzer.cc
new file mode 100644
index 00000000000..1d23453d0b5
--- /dev/null
+++ b/chromium/net/cert/pki/ocsp_parse_ocsp_cert_id_fuzzer.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 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 <stddef.h>
+#include <stdint.h>
+
+#include "net/cert/pki/ocsp.h"
+#include "net/der/input.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ net::der::Input cert_id_der(data, size);
+ net::OCSPCertID cert_id;
+ net::ParseOCSPCertID(cert_id_der, &cert_id);
+
+ return 0;
+}
diff --git a/chromium/net/cert/pki/ocsp_parse_ocsp_response_data_fuzzer.cc b/chromium/net/cert/pki/ocsp_parse_ocsp_response_data_fuzzer.cc
new file mode 100644
index 00000000000..d312f0fae1b
--- /dev/null
+++ b/chromium/net/cert/pki/ocsp_parse_ocsp_response_data_fuzzer.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 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 <stddef.h>
+#include <stdint.h>
+
+#include "net/cert/pki/ocsp.h"
+#include "net/der/input.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ net::der::Input response_data_der(data, size);
+ net::OCSPResponseData response_data;
+ net::ParseOCSPResponseData(response_data_der, &response_data);
+
+ return 0;
+}
diff --git a/chromium/net/cert/pki/ocsp_parse_ocsp_response_fuzzer.cc b/chromium/net/cert/pki/ocsp_parse_ocsp_response_fuzzer.cc
new file mode 100644
index 00000000000..f3673aeec7a
--- /dev/null
+++ b/chromium/net/cert/pki/ocsp_parse_ocsp_response_fuzzer.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 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 <stddef.h>
+#include <stdint.h>
+
+#include "net/cert/pki/ocsp.h"
+#include "net/der/input.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ net::der::Input response_der(data, size);
+ net::OCSPResponse response;
+ net::ParseOCSPResponse(response_der, &response);
+
+ return 0;
+}
diff --git a/chromium/net/cert/pki/ocsp_parse_ocsp_single_response_fuzzer.cc b/chromium/net/cert/pki/ocsp_parse_ocsp_single_response_fuzzer.cc
new file mode 100644
index 00000000000..872e2680a4e
--- /dev/null
+++ b/chromium/net/cert/pki/ocsp_parse_ocsp_single_response_fuzzer.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 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 <stddef.h>
+#include <stdint.h>
+
+#include "net/cert/pki/ocsp.h"
+#include "net/der/input.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ net::der::Input single_response_der(data, size);
+ net::OCSPSingleResponse single_response;
+ net::ParseOCSPSingleResponse(single_response_der, &single_response);
+
+ return 0;
+}
diff --git a/chromium/net/cert/pki/ocsp_unittest.cc b/chromium/net/cert/pki/ocsp_unittest.cc
new file mode 100644
index 00000000000..6b3ae13a68d
--- /dev/null
+++ b/chromium/net/cert/pki/ocsp_unittest.cc
@@ -0,0 +1,239 @@
+// Copyright 2016 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 "net/cert/pki/ocsp.h"
+
+#include "base/base64.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "net/cert/pki/test_helpers.h"
+#include "net/der/encode_values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+#include "url/gurl.h"
+
+namespace net {
+
+namespace {
+
+const base::TimeDelta kOCSPAgeOneWeek = base::Days(7);
+
+std::string GetFilePath(const std::string& file_name) {
+ return std::string("net/data/ocsp_unittest/") + file_name;
+}
+
+scoped_refptr<ParsedCertificate> ParseCertificate(base::StringPiece data) {
+ CertErrors errors;
+ return ParsedCertificate::Create(
+ bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(
+ reinterpret_cast<const uint8_t*>(data.data()), data.size(), nullptr)),
+ {}, &errors);
+}
+
+struct TestParams {
+ const char* file_name;
+ OCSPRevocationStatus expected_revocation_status;
+ OCSPVerifyResult::ResponseStatus expected_response_status;
+};
+
+class CheckOCSPTest : public ::testing::TestWithParam<TestParams> {};
+
+const TestParams kTestParams[] = {
+ {"good_response.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"good_response_sha256.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"no_response.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::NO_MATCHING_RESPONSE},
+
+ {"malformed_request.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::ERROR_RESPONSE},
+
+ {"bad_status.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::PARSE_RESPONSE_ERROR},
+
+ {"bad_ocsp_type.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::PARSE_RESPONSE_ERROR},
+
+ {"bad_signature.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::PROVIDED},
+
+ {"ocsp_sign_direct.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"ocsp_sign_indirect.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"ocsp_sign_indirect_missing.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::PROVIDED},
+
+ {"ocsp_sign_bad_indirect.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::PROVIDED},
+
+ {"ocsp_extra_certs.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"has_version.pem", OCSPRevocationStatus::GOOD, OCSPVerifyResult::PROVIDED},
+
+ {"responder_name.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"responder_id.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"has_extension.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"good_response_next_update.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"revoke_response.pem", OCSPRevocationStatus::REVOKED,
+ OCSPVerifyResult::PROVIDED},
+
+ {"revoke_response_reason.pem", OCSPRevocationStatus::REVOKED,
+ OCSPVerifyResult::PROVIDED},
+
+ {"unknown_response.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::PROVIDED},
+
+ {"multiple_response.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::PROVIDED},
+
+ {"other_response.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::NO_MATCHING_RESPONSE},
+
+ {"has_single_extension.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"has_critical_single_extension.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::UNHANDLED_CRITICAL_EXTENSION},
+
+ {"has_critical_response_extension.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::UNHANDLED_CRITICAL_EXTENSION},
+
+ {"has_critical_ct_extension.pem", OCSPRevocationStatus::GOOD,
+ OCSPVerifyResult::PROVIDED},
+
+ {"missing_response.pem", OCSPRevocationStatus::UNKNOWN,
+ OCSPVerifyResult::NO_MATCHING_RESPONSE},
+};
+
+// Parameterised test name generator for tests depending on RenderTextBackend.
+struct PrintTestName {
+ std::string operator()(const testing::TestParamInfo<TestParams>& info) const {
+ base::StringPiece name(info.param.file_name);
+ // Strip ".pem" from the end as GTest names cannot contain period.
+ name.remove_suffix(4);
+ return std::string(name);
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ CheckOCSPTest,
+ ::testing::ValuesIn(kTestParams),
+ PrintTestName());
+
+TEST_P(CheckOCSPTest, FromFile) {
+ const TestParams& params = GetParam();
+
+ std::string ocsp_data;
+ std::string ca_data;
+ std::string cert_data;
+ std::string request_data;
+ const PemBlockMapping mappings[] = {
+ {"OCSP RESPONSE", &ocsp_data},
+ {"CA CERTIFICATE", &ca_data},
+ {"CERTIFICATE", &cert_data},
+ {"OCSP REQUEST", &request_data},
+ };
+
+ ASSERT_TRUE(ReadTestDataFromPemFile(GetFilePath(params.file_name), mappings));
+
+ // Mar 5 00:00:00 2017 GMT
+ base::Time kVerifyTime = base::Time::UnixEpoch() + base::Seconds(1488672000);
+
+ // Test that CheckOCSP() works.
+ OCSPVerifyResult::ResponseStatus response_status;
+ OCSPRevocationStatus revocation_status =
+ CheckOCSP(ocsp_data, cert_data, ca_data, kVerifyTime, kOCSPAgeOneWeek,
+ &response_status);
+
+ EXPECT_EQ(params.expected_revocation_status, revocation_status);
+ EXPECT_EQ(params.expected_response_status, response_status);
+
+ // Check that CreateOCSPRequest() works.
+ scoped_refptr<ParsedCertificate> cert = ParseCertificate(cert_data);
+ ASSERT_TRUE(cert);
+
+ scoped_refptr<ParsedCertificate> issuer = ParseCertificate(ca_data);
+ ASSERT_TRUE(issuer);
+
+ std::vector<uint8_t> encoded_request;
+ ASSERT_TRUE(CreateOCSPRequest(cert.get(), issuer.get(), &encoded_request));
+
+ EXPECT_EQ(der::Input(encoded_request.data(), encoded_request.size()),
+ der::Input(&request_data));
+}
+
+base::StringPiece kGetURLTestParams[] = {
+ "http://www.example.com/",
+ "http://www.example.com/path/",
+ "http://www.example.com/path",
+ "http://www.example.com/path?query"
+ "http://user:pass@www.example.com/path?query",
+};
+
+class CreateOCSPGetURLTest
+ : public ::testing::TestWithParam<base::StringPiece> {};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ CreateOCSPGetURLTest,
+ ::testing::ValuesIn(kGetURLTestParams));
+
+TEST_P(CreateOCSPGetURLTest, Basic) {
+ std::string ca_data;
+ std::string cert_data;
+ std::string request_data;
+ const PemBlockMapping mappings[] = {
+ {"CA CERTIFICATE", &ca_data},
+ {"CERTIFICATE", &cert_data},
+ {"OCSP REQUEST", &request_data},
+ };
+
+ // Load one of the test files. (Doesn't really matter which one as
+ // constructing the DER is tested elsewhere).
+ ASSERT_TRUE(
+ ReadTestDataFromPemFile(GetFilePath("good_response.pem"), mappings));
+
+ scoped_refptr<ParsedCertificate> cert = ParseCertificate(cert_data);
+ ASSERT_TRUE(cert);
+
+ scoped_refptr<ParsedCertificate> issuer = ParseCertificate(ca_data);
+ ASSERT_TRUE(issuer);
+
+ GURL url = CreateOCSPGetURL(cert.get(), issuer.get(), GetParam());
+
+ // Try to extract the encoded data and compare against |request_data|.
+ //
+ // A known answer output test would be better as this just reverses the logic
+ // from the implementaiton file.
+ std::string b64 = url.spec().substr(GetParam().size() + 1);
+
+ // Hex un-escape the data.
+ base::ReplaceSubstringsAfterOffset(&b64, 0, "%2B", "+");
+ base::ReplaceSubstringsAfterOffset(&b64, 0, "%2F", "/");
+ base::ReplaceSubstringsAfterOffset(&b64, 0, "%3D", "=");
+
+ // Base64 decode the data.
+ std::string decoded;
+ ASSERT_TRUE(base::Base64Decode(b64, &decoded));
+
+ EXPECT_EQ(request_data, decoded);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/pki/parse_certificate.cc b/chromium/net/cert/pki/parse_certificate.cc
new file mode 100644
index 00000000000..d206ec897e6
--- /dev/null
+++ b/chromium/net/cert/pki/parse_certificate.cc
@@ -0,0 +1,955 @@
+// Copyright 2015 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 "net/cert/pki/parse_certificate.h"
+
+#include <utility>
+
+#include "base/strings/string_util.h"
+#include "net/cert/pki/cert_error_params.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/general_names.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "net/der/parser.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+
+namespace {
+
+DEFINE_CERT_ERROR_ID(kCertificateNotSequence,
+ "Failed parsing Certificate SEQUENCE");
+DEFINE_CERT_ERROR_ID(kUnconsumedDataInsideCertificateSequence,
+ "Unconsumed data inside Certificate SEQUENCE");
+DEFINE_CERT_ERROR_ID(kUnconsumedDataAfterCertificateSequence,
+ "Unconsumed data after Certificate SEQUENCE");
+DEFINE_CERT_ERROR_ID(kTbsCertificateNotSequence,
+ "Couldn't read tbsCertificate as SEQUENCE");
+DEFINE_CERT_ERROR_ID(
+ kSignatureAlgorithmNotSequence,
+ "Couldn't read Certificate.signatureAlgorithm as SEQUENCE");
+DEFINE_CERT_ERROR_ID(kSignatureValueNotBitString,
+ "Couldn't read Certificate.signatureValue as BIT STRING");
+DEFINE_CERT_ERROR_ID(kUnconsumedDataInsideTbsCertificateSequence,
+ "Unconsumed data inside TBSCertificate");
+DEFINE_CERT_ERROR_ID(kTbsNotSequence, "Failed parsing TBSCertificate SEQUENCE");
+DEFINE_CERT_ERROR_ID(kFailedReadingVersion, "Failed reading version");
+DEFINE_CERT_ERROR_ID(kFailedParsingVersion, "Failed parsing version");
+DEFINE_CERT_ERROR_ID(kVersionExplicitlyV1,
+ "Version explicitly V1 (should be omitted)");
+DEFINE_CERT_ERROR_ID(kFailedReadingSerialNumber, "Failed reading serialNumber");
+DEFINE_CERT_ERROR_ID(kFailedReadingSignatureValue, "Failed reading signature");
+DEFINE_CERT_ERROR_ID(kFailedReadingIssuer, "Failed reading issuer");
+DEFINE_CERT_ERROR_ID(kFailedReadingValidity, "Failed reading validity");
+DEFINE_CERT_ERROR_ID(kFailedParsingValidity, "Failed parsing validity");
+DEFINE_CERT_ERROR_ID(kFailedReadingSubject, "Failed reading subject");
+DEFINE_CERT_ERROR_ID(kFailedReadingSpki, "Failed reading subjectPublicKeyInfo");
+DEFINE_CERT_ERROR_ID(kFailedReadingIssuerUniqueId,
+ "Failed reading issuerUniqueId");
+DEFINE_CERT_ERROR_ID(kFailedParsingIssuerUniqueId,
+ "Failed parsing issuerUniqueId");
+DEFINE_CERT_ERROR_ID(
+ kIssuerUniqueIdNotExpected,
+ "Unexpected issuerUniqueId (must be V2 or V3 certificate)");
+DEFINE_CERT_ERROR_ID(kFailedReadingSubjectUniqueId,
+ "Failed reading subjectUniqueId");
+DEFINE_CERT_ERROR_ID(kFailedParsingSubjectUniqueId,
+ "Failed parsing subjectUniqueId");
+DEFINE_CERT_ERROR_ID(
+ kSubjectUniqueIdNotExpected,
+ "Unexpected subjectUniqueId (must be V2 or V3 certificate)");
+DEFINE_CERT_ERROR_ID(kFailedReadingExtensions,
+ "Failed reading extensions SEQUENCE");
+DEFINE_CERT_ERROR_ID(kUnexpectedExtensions,
+ "Unexpected extensions (must be V3 certificate)");
+DEFINE_CERT_ERROR_ID(kSerialNumberIsNegative, "Serial number is negative");
+DEFINE_CERT_ERROR_ID(kSerialNumberIsZero, "Serial number is zero");
+DEFINE_CERT_ERROR_ID(kSerialNumberLengthOver20,
+ "Serial number is longer than 20 octets");
+DEFINE_CERT_ERROR_ID(kSerialNumberNotValidInteger,
+ "Serial number is not a valid INTEGER");
+
+// Returns true if |input| is a SEQUENCE and nothing else.
+[[nodiscard]] bool IsSequenceTLV(const der::Input& input) {
+ der::Parser parser(input);
+ der::Parser unused_sequence_parser;
+ if (!parser.ReadSequence(&unused_sequence_parser))
+ return false;
+ // Should by a single SEQUENCE by definition of the function.
+ return !parser.HasMore();
+}
+
+// Reads a SEQUENCE from |parser| and writes the full tag-length-value into
+// |out|. On failure |parser| may or may not have been advanced.
+[[nodiscard]] bool ReadSequenceTLV(der::Parser* parser, der::Input* out) {
+ return parser->ReadRawTLV(out) && IsSequenceTLV(*out);
+}
+
+// Parses a Version according to RFC 5280:
+//
+// Version ::= INTEGER { v1(0), v2(1), v3(2) }
+//
+// No value other that v1, v2, or v3 is allowed (and if given will fail). RFC
+// 5280 minimally requires the handling of v3 (and overwhelmingly these are the
+// certificate versions in use today):
+//
+// Implementations SHOULD be prepared to accept any version certificate.
+// At a minimum, conforming implementations MUST recognize version 3
+// certificates.
+[[nodiscard]] bool ParseVersion(const der::Input& in,
+ CertificateVersion* version) {
+ der::Parser parser(in);
+ uint64_t version64;
+ if (!parser.ReadUint64(&version64))
+ return false;
+
+ switch (version64) {
+ case 0:
+ *version = CertificateVersion::V1;
+ break;
+ case 1:
+ *version = CertificateVersion::V2;
+ break;
+ case 2:
+ *version = CertificateVersion::V3;
+ break;
+ default:
+ // Don't allow any other version identifier.
+ return false;
+ }
+
+ // By definition the input to this function was a single INTEGER, so there
+ // shouldn't be anything else after it.
+ return !parser.HasMore();
+}
+
+// Returns true if every bit in |bits| is zero (including empty).
+[[nodiscard]] bool BitStringIsAllZeros(const der::BitString& bits) {
+ // Note that it is OK to read from the unused bits, since BitString parsing
+ // guarantees they are all zero.
+ for (size_t i = 0; i < bits.bytes().Length(); ++i) {
+ if (bits.bytes().UnsafeData()[i] != 0)
+ return false;
+ }
+ return true;
+}
+
+// Parses a DistributionPointName.
+//
+// From RFC 5280:
+//
+// DistributionPointName ::= CHOICE {
+// fullName [0] GeneralNames,
+// nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
+bool ParseDistributionPointName(const der::Input& dp_name,
+ ParsedDistributionPoint* distribution_point) {
+ der::Parser parser(dp_name);
+ absl::optional<der::Input> der_full_name;
+ if (!parser.ReadOptionalTag(
+ der::kTagContextSpecific | der::kTagConstructed | 0,
+ &der_full_name)) {
+ return false;
+ }
+ if (der_full_name) {
+ // TODO(mattm): surface the CertErrors.
+ CertErrors errors;
+ distribution_point->distribution_point_fullname =
+ GeneralNames::CreateFromValue(*der_full_name, &errors);
+ if (!distribution_point->distribution_point_fullname)
+ return false;
+ return !parser.HasMore();
+ }
+
+ if (!parser.ReadOptionalTag(
+ der::kTagContextSpecific | der::kTagConstructed | 1,
+ &distribution_point
+ ->distribution_point_name_relative_to_crl_issuer)) {
+ return false;
+ }
+ if (distribution_point->distribution_point_name_relative_to_crl_issuer) {
+ return !parser.HasMore();
+ }
+
+ // The CHOICE must contain either fullName or nameRelativeToCRLIssuer.
+ return false;
+}
+
+// RFC 5280, section 4.2.1.13.
+//
+// DistributionPoint ::= SEQUENCE {
+// distributionPoint [0] DistributionPointName OPTIONAL,
+// reasons [1] ReasonFlags OPTIONAL,
+// cRLIssuer [2] GeneralNames OPTIONAL }
+bool ParseAndAddDistributionPoint(
+ der::Parser* parser,
+ std::vector<ParsedDistributionPoint>* distribution_points) {
+ ParsedDistributionPoint distribution_point;
+
+ // DistributionPoint ::= SEQUENCE {
+ der::Parser distrib_point_parser;
+ if (!parser->ReadSequence(&distrib_point_parser))
+ return false;
+
+ // distributionPoint [0] DistributionPointName OPTIONAL,
+ absl::optional<der::Input> distribution_point_name;
+ if (!distrib_point_parser.ReadOptionalTag(
+ der::kTagContextSpecific | der::kTagConstructed | 0,
+ &distribution_point_name)) {
+ return false;
+ }
+
+ if (distribution_point_name &&
+ !ParseDistributionPointName(*distribution_point_name,
+ &distribution_point)) {
+ return false;
+ }
+
+ // reasons [1] ReasonFlags OPTIONAL,
+ if (!distrib_point_parser.ReadOptionalTag(der::kTagContextSpecific | 1,
+ &distribution_point.reasons)) {
+ return false;
+ }
+
+ // cRLIssuer [2] GeneralNames OPTIONAL }
+ if (!distrib_point_parser.ReadOptionalTag(
+ der::kTagContextSpecific | der::kTagConstructed | 2,
+ &distribution_point.crl_issuer)) {
+ return false;
+ }
+ // TODO(eroman): Parse "cRLIssuer"?
+
+ // RFC 5280, section 4.2.1.13:
+ // either distributionPoint or cRLIssuer MUST be present.
+ if (!distribution_point_name && !distribution_point.crl_issuer)
+ return false;
+
+ if (distrib_point_parser.HasMore())
+ return false;
+
+ distribution_points->push_back(std::move(distribution_point));
+ return true;
+}
+
+} // namespace
+
+ParsedTbsCertificate::ParsedTbsCertificate() = default;
+
+ParsedTbsCertificate::ParsedTbsCertificate(ParsedTbsCertificate&& other) =
+ default;
+
+ParsedTbsCertificate::~ParsedTbsCertificate() = default;
+
+bool VerifySerialNumber(const der::Input& value,
+ bool warnings_only,
+ CertErrors* errors) {
+ // If |warnings_only| was set to true, the exact same errors will be logged,
+ // only they will be logged with a lower severity (warning rather than error).
+ CertError::Severity error_severity =
+ warnings_only ? CertError::SEVERITY_WARNING : CertError::SEVERITY_HIGH;
+
+ bool negative;
+ if (!der::IsValidInteger(value, &negative)) {
+ errors->Add(error_severity, kSerialNumberNotValidInteger, nullptr);
+ return false;
+ }
+
+ // RFC 5280 section 4.1.2.2:
+ //
+ // Note: Non-conforming CAs may issue certificates with serial numbers
+ // that are negative or zero. Certificate users SHOULD be prepared to
+ // gracefully handle such certificates.
+ if (negative)
+ errors->AddWarning(kSerialNumberIsNegative);
+ if (value.Length() == 1 && value.UnsafeData()[0] == 0)
+ errors->AddWarning(kSerialNumberIsZero);
+
+ // RFC 5280 section 4.1.2.2:
+ //
+ // Certificate users MUST be able to handle serialNumber values up to 20
+ // octets. Conforming CAs MUST NOT use serialNumber values longer than 20
+ // octets.
+ if (value.Length() > 20) {
+ errors->Add(error_severity, kSerialNumberLengthOver20,
+ CreateCertErrorParams1SizeT("length", value.Length()));
+ return false;
+ }
+
+ return true;
+}
+
+bool ReadUTCOrGeneralizedTime(der::Parser* parser, der::GeneralizedTime* out) {
+ der::Input value;
+ der::Tag tag;
+
+ if (!parser->ReadTagAndValue(&tag, &value))
+ return false;
+
+ if (tag == der::kUtcTime)
+ return der::ParseUTCTime(value, out);
+
+ if (tag == der::kGeneralizedTime)
+ return der::ParseGeneralizedTime(value, out);
+
+ // Unrecognized tag.
+ return false;
+}
+
+bool ParseValidity(const der::Input& validity_tlv,
+ der::GeneralizedTime* not_before,
+ der::GeneralizedTime* not_after) {
+ der::Parser parser(validity_tlv);
+
+ // Validity ::= SEQUENCE {
+ der::Parser validity_parser;
+ if (!parser.ReadSequence(&validity_parser))
+ return false;
+
+ // notBefore Time,
+ if (!ReadUTCOrGeneralizedTime(&validity_parser, not_before))
+ return false;
+
+ // notAfter Time }
+ if (!ReadUTCOrGeneralizedTime(&validity_parser, not_after))
+ return false;
+
+ // By definition the input was a single Validity sequence, so there shouldn't
+ // be unconsumed data.
+ if (parser.HasMore())
+ return false;
+
+ // The Validity type does not have an extension point.
+ if (validity_parser.HasMore())
+ return false;
+
+ // Note that RFC 5280 doesn't require notBefore to be <=
+ // notAfter, so that will not be considered a "parsing" error here. Instead it
+ // will be considered an expired certificate later when testing against the
+ // current timestamp.
+ return true;
+}
+
+bool ParseCertificate(const der::Input& certificate_tlv,
+ der::Input* out_tbs_certificate_tlv,
+ der::Input* out_signature_algorithm_tlv,
+ der::BitString* out_signature_value,
+ CertErrors* out_errors) {
+ // |out_errors| is optional. But ensure it is non-null for the remainder of
+ // this function.
+ CertErrors unused_errors;
+ if (!out_errors)
+ out_errors = &unused_errors;
+
+ der::Parser parser(certificate_tlv);
+
+ // Certificate ::= SEQUENCE {
+ der::Parser certificate_parser;
+ if (!parser.ReadSequence(&certificate_parser)) {
+ out_errors->AddError(kCertificateNotSequence);
+ return false;
+ }
+
+ // tbsCertificate TBSCertificate,
+ if (!ReadSequenceTLV(&certificate_parser, out_tbs_certificate_tlv)) {
+ out_errors->AddError(kTbsCertificateNotSequence);
+ return false;
+ }
+
+ // signatureAlgorithm AlgorithmIdentifier,
+ if (!ReadSequenceTLV(&certificate_parser, out_signature_algorithm_tlv)) {
+ out_errors->AddError(kSignatureAlgorithmNotSequence);
+ return false;
+ }
+
+ // signatureValue BIT STRING }
+ absl::optional<der::BitString> signature_value =
+ certificate_parser.ReadBitString();
+ if (!signature_value) {
+ out_errors->AddError(kSignatureValueNotBitString);
+ return false;
+ }
+ *out_signature_value = signature_value.value();
+
+ // There isn't an extension point at the end of Certificate.
+ if (certificate_parser.HasMore()) {
+ out_errors->AddError(kUnconsumedDataInsideCertificateSequence);
+ return false;
+ }
+
+ // By definition the input was a single Certificate, so there shouldn't be
+ // unconsumed data.
+ if (parser.HasMore()) {
+ out_errors->AddError(kUnconsumedDataAfterCertificateSequence);
+ return false;
+ }
+
+ return true;
+}
+
+// From RFC 5280 section 4.1:
+//
+// TBSCertificate ::= SEQUENCE {
+// version [0] EXPLICIT Version DEFAULT v1,
+// serialNumber CertificateSerialNumber,
+// signature AlgorithmIdentifier,
+// issuer Name,
+// validity Validity,
+// subject Name,
+// subjectPublicKeyInfo SubjectPublicKeyInfo,
+// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
+// -- If present, version MUST be v2 or v3
+// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
+// -- If present, version MUST be v2 or v3
+// extensions [3] EXPLICIT Extensions OPTIONAL
+// -- If present, version MUST be v3
+// }
+bool ParseTbsCertificate(const der::Input& tbs_tlv,
+ const ParseCertificateOptions& options,
+ ParsedTbsCertificate* out,
+ CertErrors* errors) {
+ // The rest of this function assumes that |errors| is non-null.
+ CertErrors unused_errors;
+ if (!errors)
+ errors = &unused_errors;
+
+ // TODO(crbug.com/634443): Add useful error information to |errors|.
+
+ der::Parser parser(tbs_tlv);
+
+ // TBSCertificate ::= SEQUENCE {
+ der::Parser tbs_parser;
+ if (!parser.ReadSequence(&tbs_parser)) {
+ errors->AddError(kTbsNotSequence);
+ return false;
+ }
+
+ // version [0] EXPLICIT Version DEFAULT v1,
+ absl::optional<der::Input> version;
+ if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
+ &version)) {
+ errors->AddError(kFailedReadingVersion);
+ return false;
+ }
+ if (version) {
+ if (!ParseVersion(version.value(), &out->version)) {
+ errors->AddError(kFailedParsingVersion);
+ return false;
+ }
+ if (out->version == CertificateVersion::V1) {
+ errors->AddError(kVersionExplicitlyV1);
+ // The correct way to specify v1 is to omit the version field since v1 is
+ // the DEFAULT.
+ return false;
+ }
+ } else {
+ out->version = CertificateVersion::V1;
+ }
+
+ // serialNumber CertificateSerialNumber,
+ if (!tbs_parser.ReadTag(der::kInteger, &out->serial_number)) {
+ errors->AddError(kFailedReadingSerialNumber);
+ return false;
+ }
+ if (!VerifySerialNumber(out->serial_number,
+ options.allow_invalid_serial_numbers, errors)) {
+ // Invalid serial numbers are only considered fatal failures if
+ // |!allow_invalid_serial_numbers|.
+ if (!options.allow_invalid_serial_numbers)
+ return false;
+ }
+
+ // signature AlgorithmIdentifier,
+ if (!ReadSequenceTLV(&tbs_parser, &out->signature_algorithm_tlv)) {
+ errors->AddError(kFailedReadingSignatureValue);
+ return false;
+ }
+
+ // issuer Name,
+ if (!ReadSequenceTLV(&tbs_parser, &out->issuer_tlv)) {
+ errors->AddError(kFailedReadingIssuer);
+ return false;
+ }
+
+ // validity Validity,
+ der::Input validity_tlv;
+ if (!tbs_parser.ReadRawTLV(&validity_tlv)) {
+ errors->AddError(kFailedReadingValidity);
+ return false;
+ }
+ if (!ParseValidity(validity_tlv, &out->validity_not_before,
+ &out->validity_not_after)) {
+ errors->AddError(kFailedParsingValidity);
+ return false;
+ }
+
+ // subject Name,
+ if (!ReadSequenceTLV(&tbs_parser, &out->subject_tlv)) {
+ errors->AddError(kFailedReadingSubject);
+ return false;
+ }
+
+ // subjectPublicKeyInfo SubjectPublicKeyInfo,
+ if (!ReadSequenceTLV(&tbs_parser, &out->spki_tlv)) {
+ errors->AddError(kFailedReadingSpki);
+ return false;
+ }
+
+ // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
+ // -- If present, version MUST be v2 or v3
+ absl::optional<der::Input> issuer_unique_id;
+ if (!tbs_parser.ReadOptionalTag(der::ContextSpecificPrimitive(1),
+ &issuer_unique_id)) {
+ errors->AddError(kFailedReadingIssuerUniqueId);
+ return false;
+ }
+ if (issuer_unique_id) {
+ out->issuer_unique_id = der::ParseBitString(issuer_unique_id.value());
+ if (!out->issuer_unique_id) {
+ errors->AddError(kFailedParsingIssuerUniqueId);
+ return false;
+ }
+ if (out->version != CertificateVersion::V2 &&
+ out->version != CertificateVersion::V3) {
+ errors->AddError(kIssuerUniqueIdNotExpected);
+ return false;
+ }
+ }
+
+ // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
+ // -- If present, version MUST be v2 or v3
+ absl::optional<der::Input> subject_unique_id;
+ if (!tbs_parser.ReadOptionalTag(der::ContextSpecificPrimitive(2),
+ &subject_unique_id)) {
+ errors->AddError(kFailedReadingSubjectUniqueId);
+ return false;
+ }
+ if (subject_unique_id) {
+ out->subject_unique_id = der::ParseBitString(subject_unique_id.value());
+ if (!out->subject_unique_id) {
+ errors->AddError(kFailedParsingSubjectUniqueId);
+ return false;
+ }
+ if (out->version != CertificateVersion::V2 &&
+ out->version != CertificateVersion::V3) {
+ errors->AddError(kSubjectUniqueIdNotExpected);
+ return false;
+ }
+ }
+
+ // extensions [3] EXPLICIT Extensions OPTIONAL
+ // -- If present, version MUST be v3
+ if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(3),
+ &out->extensions_tlv)) {
+ errors->AddError(kFailedReadingExtensions);
+ return false;
+ }
+ if (out->extensions_tlv) {
+ // extensions_tlv must be a single element. Also check that it is a
+ // SEQUENCE.
+ if (!IsSequenceTLV(out->extensions_tlv.value())) {
+ errors->AddError(kFailedReadingExtensions);
+ return false;
+ }
+ if (out->version != CertificateVersion::V3) {
+ errors->AddError(kUnexpectedExtensions);
+ return false;
+ }
+ }
+
+ // Note that there IS an extension point at the end of TBSCertificate
+ // (according to RFC 5912), so from that interpretation, unconsumed data would
+ // be allowed in |tbs_parser|.
+ //
+ // However because only v1, v2, and v3 certificates are supported by the
+ // parsing, there shouldn't be any subsequent data in those versions, so
+ // reject.
+ if (tbs_parser.HasMore()) {
+ errors->AddError(kUnconsumedDataInsideTbsCertificateSequence);
+ return false;
+ }
+
+ // By definition the input was a single TBSCertificate, so there shouldn't be
+ // unconsumed data.
+ if (parser.HasMore())
+ return false;
+
+ return true;
+}
+
+// From RFC 5280:
+//
+// Extension ::= SEQUENCE {
+// extnID OBJECT IDENTIFIER,
+// critical BOOLEAN DEFAULT FALSE,
+// extnValue OCTET STRING
+// -- contains the DER encoding of an ASN.1 value
+// -- corresponding to the extension type identified
+// -- by extnID
+// }
+bool ParseExtension(const der::Input& extension_tlv, ParsedExtension* out) {
+ der::Parser parser(extension_tlv);
+
+ // Extension ::= SEQUENCE {
+ der::Parser extension_parser;
+ if (!parser.ReadSequence(&extension_parser))
+ return false;
+
+ // extnID OBJECT IDENTIFIER,
+ if (!extension_parser.ReadTag(der::kOid, &out->oid))
+ return false;
+
+ // critical BOOLEAN DEFAULT FALSE,
+ out->critical = false;
+ bool has_critical;
+ der::Input critical;
+ if (!extension_parser.ReadOptionalTag(der::kBool, &critical, &has_critical))
+ return false;
+ if (has_critical) {
+ if (!der::ParseBool(critical, &out->critical))
+ return false;
+ if (!out->critical)
+ return false; // DER-encoding requires DEFAULT values be omitted.
+ }
+
+ // extnValue OCTET STRING
+ if (!extension_parser.ReadTag(der::kOctetString, &out->value))
+ return false;
+
+ // The Extension type does not have an extension point (everything goes in
+ // extnValue).
+ if (extension_parser.HasMore())
+ return false;
+
+ // By definition the input was a single Extension sequence, so there shouldn't
+ // be unconsumed data.
+ if (parser.HasMore())
+ return false;
+
+ return true;
+}
+
+NET_EXPORT bool ParseExtensions(
+ const der::Input& extensions_tlv,
+ std::map<der::Input, ParsedExtension>* extensions) {
+ der::Parser parser(extensions_tlv);
+
+ // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ der::Parser extensions_parser;
+ if (!parser.ReadSequence(&extensions_parser))
+ return false;
+
+ // The Extensions SEQUENCE must contains at least 1 element (otherwise it
+ // should have been omitted).
+ if (!extensions_parser.HasMore())
+ return false;
+
+ extensions->clear();
+
+ while (extensions_parser.HasMore()) {
+ ParsedExtension extension;
+
+ der::Input extension_tlv;
+ if (!extensions_parser.ReadRawTLV(&extension_tlv))
+ return false;
+
+ if (!ParseExtension(extension_tlv, &extension))
+ return false;
+
+ bool is_duplicate =
+ !extensions->insert(std::make_pair(extension.oid, extension)).second;
+
+ // RFC 5280 says that an extension should not appear more than once.
+ if (is_duplicate)
+ return false;
+ }
+
+ // By definition the input was a single Extensions sequence, so there
+ // shouldn't be unconsumed data.
+ if (parser.HasMore())
+ return false;
+
+ return true;
+}
+
+NET_EXPORT bool ConsumeExtension(
+ const der::Input& oid,
+ std::map<der::Input, ParsedExtension>* unconsumed_extensions,
+ ParsedExtension* extension) {
+ auto it = unconsumed_extensions->find(oid);
+ if (it == unconsumed_extensions->end())
+ return false;
+
+ *extension = it->second;
+ unconsumed_extensions->erase(it);
+ return true;
+}
+
+bool ParseBasicConstraints(const der::Input& basic_constraints_tlv,
+ ParsedBasicConstraints* out) {
+ der::Parser parser(basic_constraints_tlv);
+
+ // BasicConstraints ::= SEQUENCE {
+ der::Parser sequence_parser;
+ if (!parser.ReadSequence(&sequence_parser))
+ return false;
+
+ // cA BOOLEAN DEFAULT FALSE,
+ out->is_ca = false;
+ bool has_ca;
+ der::Input ca;
+ if (!sequence_parser.ReadOptionalTag(der::kBool, &ca, &has_ca))
+ return false;
+ if (has_ca) {
+ if (!der::ParseBool(ca, &out->is_ca))
+ return false;
+ // TODO(eroman): Should reject if CA was set to false, since
+ // DER-encoding requires DEFAULT values be omitted. In
+ // practice however there are a lot of certificates that use
+ // the broken encoding.
+ }
+
+ // pathLenConstraint INTEGER (0..MAX) OPTIONAL }
+ der::Input encoded_path_len;
+ if (!sequence_parser.ReadOptionalTag(der::kInteger, &encoded_path_len,
+ &out->has_path_len)) {
+ return false;
+ }
+ if (out->has_path_len) {
+ // TODO(eroman): Surface reason for failure if length was longer than uint8.
+ if (!der::ParseUint8(encoded_path_len, &out->path_len))
+ return false;
+ } else {
+ // Default initialize to 0 as a precaution.
+ out->path_len = 0;
+ }
+
+ // There shouldn't be any unconsumed data in the extension.
+ if (sequence_parser.HasMore())
+ return false;
+
+ // By definition the input was a single BasicConstraints sequence, so there
+ // shouldn't be unconsumed data.
+ if (parser.HasMore())
+ return false;
+
+ return true;
+}
+
+// TODO(crbug.com/1314019): return absl::optional<BitString> when converting
+// has_key_usage_ and key_usage_ into single absl::optional field.
+bool ParseKeyUsage(const der::Input& key_usage_tlv, der::BitString* key_usage) {
+ der::Parser parser(key_usage_tlv);
+ absl::optional<der::BitString> key_usage_internal = parser.ReadBitString();
+ if (!key_usage_internal)
+ return false;
+
+ // By definition the input was a single BIT STRING.
+ if (parser.HasMore())
+ return false;
+
+ // RFC 5280 section 4.2.1.3:
+ //
+ // When the keyUsage extension appears in a certificate, at least
+ // one of the bits MUST be set to 1.
+ if (BitStringIsAllZeros(key_usage_internal.value()))
+ return false;
+
+ *key_usage = key_usage_internal.value();
+ return true;
+}
+
+bool ParseAuthorityInfoAccess(
+ const der::Input& authority_info_access_tlv,
+ std::vector<AuthorityInfoAccessDescription>* out_access_descriptions) {
+ der::Parser parser(authority_info_access_tlv);
+
+ out_access_descriptions->clear();
+
+ // AuthorityInfoAccessSyntax ::=
+ // SEQUENCE SIZE (1..MAX) OF AccessDescription
+ der::Parser sequence_parser;
+ if (!parser.ReadSequence(&sequence_parser))
+ return false;
+ if (!sequence_parser.HasMore())
+ return false;
+
+ while (sequence_parser.HasMore()) {
+ AuthorityInfoAccessDescription access_description;
+
+ // AccessDescription ::= SEQUENCE {
+ der::Parser access_description_sequence_parser;
+ if (!sequence_parser.ReadSequence(&access_description_sequence_parser))
+ return false;
+
+ // accessMethod OBJECT IDENTIFIER,
+ if (!access_description_sequence_parser.ReadTag(
+ der::kOid, &access_description.access_method_oid)) {
+ return false;
+ }
+
+ // accessLocation GeneralName }
+ if (!access_description_sequence_parser.ReadRawTLV(
+ &access_description.access_location)) {
+ return false;
+ }
+
+ if (access_description_sequence_parser.HasMore())
+ return false;
+
+ out_access_descriptions->push_back(access_description);
+ }
+
+ return true;
+}
+
+bool ParseAuthorityInfoAccessURIs(
+ const der::Input& authority_info_access_tlv,
+ std::vector<base::StringPiece>* out_ca_issuers_uris,
+ std::vector<base::StringPiece>* out_ocsp_uris) {
+ std::vector<AuthorityInfoAccessDescription> access_descriptions;
+ if (!ParseAuthorityInfoAccess(authority_info_access_tlv,
+ &access_descriptions)) {
+ return false;
+ }
+
+ for (const auto& access_description : access_descriptions) {
+ der::Parser access_location_parser(access_description.access_location);
+ der::Tag access_location_tag;
+ der::Input access_location_value;
+ if (!access_location_parser.ReadTagAndValue(&access_location_tag,
+ &access_location_value)) {
+ return false;
+ }
+
+ // GeneralName ::= CHOICE {
+ if (access_location_tag == der::ContextSpecificPrimitive(6)) {
+ // uniformResourceIdentifier [6] IA5String,
+ base::StringPiece uri = access_location_value.AsStringPiece();
+ if (!base::IsStringASCII(uri))
+ return false;
+
+ if (access_description.access_method_oid == der::Input(kAdCaIssuersOid))
+ out_ca_issuers_uris->push_back(uri);
+ else if (access_description.access_method_oid == der::Input(kAdOcspOid))
+ out_ocsp_uris->push_back(uri);
+ }
+ }
+ return true;
+}
+
+ParsedDistributionPoint::ParsedDistributionPoint() = default;
+ParsedDistributionPoint::ParsedDistributionPoint(
+ ParsedDistributionPoint&& other) = default;
+ParsedDistributionPoint::~ParsedDistributionPoint() = default;
+
+bool ParseCrlDistributionPoints(
+ const der::Input& extension_value,
+ std::vector<ParsedDistributionPoint>* distribution_points) {
+ distribution_points->clear();
+
+ // RFC 5280, section 4.2.1.13.
+ //
+ // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
+ der::Parser extension_value_parser(extension_value);
+ der::Parser distribution_points_parser;
+ if (!extension_value_parser.ReadSequence(&distribution_points_parser))
+ return false;
+ if (extension_value_parser.HasMore())
+ return false;
+
+ // Sequence must have a minimum of 1 item.
+ if (!distribution_points_parser.HasMore())
+ return false;
+
+ while (distribution_points_parser.HasMore()) {
+ if (!ParseAndAddDistributionPoint(&distribution_points_parser,
+ distribution_points))
+ return false;
+ }
+
+ return true;
+}
+
+ParsedAuthorityKeyIdentifier::ParsedAuthorityKeyIdentifier() = default;
+ParsedAuthorityKeyIdentifier::~ParsedAuthorityKeyIdentifier() = default;
+ParsedAuthorityKeyIdentifier::ParsedAuthorityKeyIdentifier(
+ ParsedAuthorityKeyIdentifier&& other) = default;
+ParsedAuthorityKeyIdentifier& ParsedAuthorityKeyIdentifier::operator=(
+ ParsedAuthorityKeyIdentifier&& other) = default;
+
+bool ParseAuthorityKeyIdentifier(
+ const der::Input& extension_value,
+ ParsedAuthorityKeyIdentifier* authority_key_identifier) {
+ // RFC 5280, section 4.2.1.1.
+ // AuthorityKeyIdentifier ::= SEQUENCE {
+ // keyIdentifier [0] KeyIdentifier OPTIONAL,
+ // authorityCertIssuer [1] GeneralNames OPTIONAL,
+ // authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
+ //
+ // KeyIdentifier ::= OCTET STRING
+
+ der::Parser extension_value_parser(extension_value);
+ der::Parser aki_parser;
+ if (!extension_value_parser.ReadSequence(&aki_parser))
+ return false;
+ if (extension_value_parser.HasMore())
+ return false;
+
+ // TODO(mattm): Should having an empty AuthorityKeyIdentifier SEQUENCE be an
+ // error? RFC 5280 doesn't explicitly say it.
+
+ // keyIdentifier [0] KeyIdentifier OPTIONAL,
+ if (!aki_parser.ReadOptionalTag(der::ContextSpecificPrimitive(0),
+ &authority_key_identifier->key_identifier)) {
+ return false;
+ }
+
+ // authorityCertIssuer [1] GeneralNames OPTIONAL,
+ if (!aki_parser.ReadOptionalTag(
+ der::ContextSpecificConstructed(1),
+ &authority_key_identifier->authority_cert_issuer)) {
+ return false;
+ }
+
+ // authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
+ if (!aki_parser.ReadOptionalTag(
+ der::ContextSpecificPrimitive(2),
+ &authority_key_identifier->authority_cert_serial_number)) {
+ return false;
+ }
+
+ // -- authorityCertIssuer and authorityCertSerialNumber MUST both
+ // -- be present or both be absent
+ if (authority_key_identifier->authority_cert_issuer.has_value() !=
+ authority_key_identifier->authority_cert_serial_number.has_value()) {
+ return false;
+ }
+
+ // There shouldn't be any unconsumed data in the AuthorityKeyIdentifier
+ // SEQUENCE.
+ if (aki_parser.HasMore())
+ return false;
+
+ return true;
+}
+
+bool ParseSubjectKeyIdentifier(const der::Input& extension_value,
+ der::Input* subject_key_identifier) {
+ // SubjectKeyIdentifier ::= KeyIdentifier
+ //
+ // KeyIdentifier ::= OCTET STRING
+ der::Parser extension_value_parser(extension_value);
+ if (!extension_value_parser.ReadTag(der::kOctetString,
+ subject_key_identifier)) {
+ return false;
+ }
+
+ // There shouldn't be any unconsumed data in the extension SEQUENCE.
+ if (extension_value_parser.HasMore())
+ return false;
+
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/parse_certificate.h b/chromium/net/cert/pki/parse_certificate.h
new file mode 100644
index 00000000000..d71dda139b5
--- /dev/null
+++ b/chromium/net/cert/pki/parse_certificate.h
@@ -0,0 +1,621 @@
+// Copyright 2015 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 NET_CERT_PKI_PARSE_CERTIFICATE_H_
+#define NET_CERT_PKI_PARSE_CERTIFICATE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "net/base/net_export.h"
+#include "net/cert/pki/general_names.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+
+namespace der {
+class Parser;
+}
+
+class CertErrors;
+struct ParsedTbsCertificate;
+
+// Returns true if the given serial number (CertificateSerialNumber in RFC 5280)
+// is valid:
+//
+// CertificateSerialNumber ::= INTEGER
+//
+// The input to this function is the (unverified) value octets of the INTEGER.
+// This function will verify that:
+//
+// * The octets are a valid DER-encoding of an INTEGER (for instance, minimal
+// encoding length).
+//
+// * No more than 20 octets are used.
+//
+// Note that it DOES NOT reject non-positive values (zero or negative).
+//
+// For reference, here is what RFC 5280 section 4.1.2.2 says:
+//
+// Given the uniqueness requirements above, serial numbers can be
+// expected to contain long integers. Certificate users MUST be able to
+// handle serialNumber values up to 20 octets. Conforming CAs MUST NOT
+// use serialNumber values longer than 20 octets.
+//
+// Note: Non-conforming CAs may issue certificates with serial numbers
+// that are negative or zero. Certificate users SHOULD be prepared to
+// gracefully handle such certificates.
+//
+// |errors| must be a non-null destination for any errors/warnings. If
+// |warnings_only| is set to true, then what would ordinarily be errors are
+// instead added as warnings.
+[[nodiscard]] NET_EXPORT bool VerifySerialNumber(const der::Input& value,
+ bool warnings_only,
+ CertErrors* errors);
+
+// Consumes a "Time" value (as defined by RFC 5280) from |parser|. On success
+// writes the result to |*out| and returns true. On failure no guarantees are
+// made about the state of |parser|.
+//
+// From RFC 5280:
+//
+// Time ::= CHOICE {
+// utcTime UTCTime,
+// generalTime GeneralizedTime }
+[[nodiscard]] NET_EXPORT bool ReadUTCOrGeneralizedTime(
+ der::Parser* parser,
+ der::GeneralizedTime* out);
+
+// Parses a DER-encoded "Validity" as specified by RFC 5280. Returns true on
+// success and sets the results in |not_before| and |not_after|:
+//
+// Validity ::= SEQUENCE {
+// notBefore Time,
+// notAfter Time }
+//
+// Note that upon success it is NOT guaranteed that |*not_before <= *not_after|.
+[[nodiscard]] NET_EXPORT bool ParseValidity(const der::Input& validity_tlv,
+ der::GeneralizedTime* not_before,
+ der::GeneralizedTime* not_after);
+
+struct NET_EXPORT ParseCertificateOptions {
+ // If set to true, then parsing will skip checks on the certificate's serial
+ // number. The only requirement will be that the serial number is an INTEGER,
+ // however it is not required to be a valid DER-encoding (i.e. minimal
+ // encoding), nor is it required to be constrained to any particular length.
+ bool allow_invalid_serial_numbers = false;
+};
+
+// Parses a DER-encoded "Certificate" as specified by RFC 5280. Returns true on
+// success and sets the results in the |out_*| parameters. On both the failure
+// and success case, if |out_errors| was non-null it may contain extra error
+// information.
+//
+// Note that on success the out parameters alias data from the input
+// |certificate_tlv|. Hence the output values are only valid as long as
+// |certificate_tlv| remains valid.
+//
+// On failure the out parameters have an undefined state, except for
+// out_errors. Some of them may have been updated during parsing, whereas
+// others may not have been changed.
+//
+// The out parameters represent each field of the Certificate SEQUENCE:
+// Certificate ::= SEQUENCE {
+//
+// The |out_tbs_certificate_tlv| parameter corresponds with "tbsCertificate"
+// from RFC 5280:
+// tbsCertificate TBSCertificate,
+//
+// This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+// guarantees are made regarding the value of this SEQUENCE.
+// This can be further parsed using ParseTbsCertificate().
+//
+// The |out_signature_algorithm_tlv| parameter corresponds with
+// "signatureAlgorithm" from RFC 5280:
+// signatureAlgorithm AlgorithmIdentifier,
+//
+// This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+// guarantees are made regarding the value of this SEQUENCE.
+// This can be further parsed using SignatureValue::Create().
+//
+// The |out_signature_value| parameter corresponds with "signatureValue" from
+// RFC 5280:
+// signatureValue BIT STRING }
+//
+// Parsing guarantees that this is a valid BIT STRING.
+[[nodiscard]] NET_EXPORT bool ParseCertificate(
+ const der::Input& certificate_tlv,
+ der::Input* out_tbs_certificate_tlv,
+ der::Input* out_signature_algorithm_tlv,
+ der::BitString* out_signature_value,
+ CertErrors* out_errors);
+
+// Parses a DER-encoded "TBSCertificate" as specified by RFC 5280. Returns true
+// on success and sets the results in |out|. Certain invalid inputs may
+// be accepted based on the provided |options|.
+//
+// If |errors| was non-null then any warnings/errors that occur during parsing
+// are added to it.
+//
+// Note that on success |out| aliases data from the input |tbs_tlv|.
+// Hence the fields of the ParsedTbsCertificate are only valid as long as
+// |tbs_tlv| remains valid.
+//
+// On failure |out| has an undefined state. Some of its fields may have been
+// updated during parsing, whereas others may not have been changed.
+//
+// Refer to the per-field documentation of ParsedTbsCertificate for details on
+// what validity checks parsing performs.
+//
+// TBSCertificate ::= SEQUENCE {
+// version [0] EXPLICIT Version DEFAULT v1,
+// serialNumber CertificateSerialNumber,
+// signature AlgorithmIdentifier,
+// issuer Name,
+// validity Validity,
+// subject Name,
+// subjectPublicKeyInfo SubjectPublicKeyInfo,
+// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
+// -- If present, version MUST be v2 or v3
+// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
+// -- If present, version MUST be v2 or v3
+// extensions [3] EXPLICIT Extensions OPTIONAL
+// -- If present, version MUST be v3
+// }
+[[nodiscard]] NET_EXPORT bool ParseTbsCertificate(
+ const der::Input& tbs_tlv,
+ const ParseCertificateOptions& options,
+ ParsedTbsCertificate* out,
+ CertErrors* errors);
+
+// Represents a "Version" from RFC 5280:
+// Version ::= INTEGER { v1(0), v2(1), v3(2) }
+enum class CertificateVersion {
+ V1,
+ V2,
+ V3,
+};
+
+// ParsedTbsCertificate contains pointers to the main fields of a DER-encoded
+// RFC 5280 "TBSCertificate".
+//
+// ParsedTbsCertificate is expected to be filled by ParseTbsCertificate(), so
+// subsequent field descriptions are in terms of what ParseTbsCertificate()
+// sets.
+struct NET_EXPORT ParsedTbsCertificate {
+ ParsedTbsCertificate();
+ ParsedTbsCertificate(ParsedTbsCertificate&& other);
+ ParsedTbsCertificate& operator=(ParsedTbsCertificate&& other) = default;
+ ~ParsedTbsCertificate();
+
+ // Corresponds with "version" from RFC 5280:
+ // version [0] EXPLICIT Version DEFAULT v1,
+ //
+ // Parsing guarantees that the version is one of v1, v2, or v3.
+ CertificateVersion version = CertificateVersion::V1;
+
+ // Corresponds with "serialNumber" from RFC 5280:
+ // serialNumber CertificateSerialNumber,
+ //
+ // This field specifically contains the content bytes of the INTEGER. So for
+ // instance if the serial number was 1000 then this would contain bytes
+ // {0x03, 0xE8}.
+ //
+ // The serial number may or may not be a valid DER-encoded INTEGER:
+ //
+ // If the option |allow_invalid_serial_numbers=true| was used during
+ // parsing, then nothing further can be assumed about these bytes.
+ //
+ // Otherwise if |allow_invalid_serial_numbers=false| then in addition
+ // to being a valid DER-encoded INTEGER, parsing guarantees that
+ // the serial number is at most 20 bytes long. Parsing does NOT guarantee
+ // that the integer is positive (might be zero or negative).
+ der::Input serial_number;
+
+ // Corresponds with "signatureAlgorithm" from RFC 5280:
+ // signatureAlgorithm AlgorithmIdentifier,
+ //
+ // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+ // guarantees are made regarding the value of this SEQUENCE.
+ //
+ // This can be further parsed using SignatureValue::Create().
+ der::Input signature_algorithm_tlv;
+
+ // Corresponds with "issuer" from RFC 5280:
+ // issuer Name,
+ //
+ // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+ // guarantees are made regarding the value of this SEQUENCE.
+ der::Input issuer_tlv;
+
+ // Corresponds with "validity" from RFC 5280:
+ // validity Validity,
+ //
+ // Where Validity is defined as:
+ //
+ // Validity ::= SEQUENCE {
+ // notBefore Time,
+ // notAfter Time }
+ //
+ // Parsing guarantees that notBefore (validity_not_before) and notAfter
+ // (validity_not_after) are valid DER-encoded dates, however it DOES NOT
+ // gurantee anything about their values. For instance notAfter could be
+ // before notBefore, or the dates could indicate an expired certificate.
+ // Consumers are responsible for testing expiration.
+ der::GeneralizedTime validity_not_before;
+ der::GeneralizedTime validity_not_after;
+
+ // Corresponds with "subject" from RFC 5280:
+ // subject Name,
+ //
+ // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+ // guarantees are made regarding the value of this SEQUENCE.
+ der::Input subject_tlv;
+
+ // Corresponds with "subjectPublicKeyInfo" from RFC 5280:
+ // subjectPublicKeyInfo SubjectPublicKeyInfo,
+ //
+ // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+ // guarantees are made regarding the value of this SEQUENCE.
+ der::Input spki_tlv;
+
+ // Corresponds with "issuerUniqueID" from RFC 5280:
+ // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
+ // -- If present, version MUST be v2 or v3
+ //
+ // Parsing guarantees that if issuer_unique_id is present it is a valid BIT
+ // STRING, and that the version is either v2 or v3
+ absl::optional<der::BitString> issuer_unique_id;
+
+ // Corresponds with "subjectUniqueID" from RFC 5280:
+ // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
+ // -- If present, version MUST be v2 or v3
+ //
+ // Parsing guarantees that if subject_unique_id is present it is a valid BIT
+ // STRING, and that the version is either v2 or v3
+ absl::optional<der::BitString> subject_unique_id;
+
+ // Corresponds with "extensions" from RFC 5280:
+ // extensions [3] EXPLICIT Extensions OPTIONAL
+ // -- If present, version MUST be v3
+ //
+ //
+ // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No
+ // guarantees are made regarding the value of this SEQUENCE. (Note that the
+ // EXPLICIT outer tag is stripped.)
+ //
+ // Parsing guarantees that if extensions is present the version is v3.
+ absl::optional<der::Input> extensions_tlv;
+};
+
+// ParsedExtension represents a parsed "Extension" from RFC 5280. It contains
+// der:Inputs which are not owned so the associated data must be kept alive.
+//
+// Extension ::= SEQUENCE {
+// extnID OBJECT IDENTIFIER,
+// critical BOOLEAN DEFAULT FALSE,
+// extnValue OCTET STRING
+// -- contains the DER encoding of an ASN.1 value
+// -- corresponding to the extension type identified
+// -- by extnID
+// }
+struct NET_EXPORT ParsedExtension {
+ der::Input oid;
+ // |value| will contain the contents of the OCTET STRING. For instance for
+ // basicConstraints it will be the TLV for a SEQUENCE.
+ der::Input value;
+ bool critical = false;
+};
+
+// Parses a DER-encoded "Extension" as specified by RFC 5280. Returns true on
+// success and sets the results in |out|.
+//
+// Note that on success |out| aliases data from the input |extension_tlv|.
+// Hence the fields of the ParsedExtension are only valid as long as
+// |extension_tlv| remains valid.
+//
+// On failure |out| has an undefined state. Some of its fields may have been
+// updated during parsing, whereas others may not have been changed.
+[[nodiscard]] NET_EXPORT bool ParseExtension(const der::Input& extension_tlv,
+ ParsedExtension* out);
+
+// From RFC 5280:
+//
+// id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 }
+//
+// In dotted notation: 2.5.29.14
+inline constexpr uint8_t kSubjectKeyIdentifierOid[] = {0x55, 0x1d, 0x0e};
+
+// From RFC 5280:
+//
+// id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
+//
+// In dotted notation: 2.5.29.15
+inline constexpr uint8_t kKeyUsageOid[] = {0x55, 0x1d, 0x0f};
+
+// From RFC 5280:
+//
+// id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 }
+//
+// In dotted notation: 2.5.29.17
+inline constexpr uint8_t kSubjectAltNameOid[] = {0x55, 0x1d, 0x11};
+
+// From RFC 5280:
+//
+// id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
+//
+// In dotted notation: 2.5.29.19
+inline constexpr uint8_t kBasicConstraintsOid[] = {0x55, 0x1d, 0x13};
+
+// From RFC 5280:
+//
+// id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
+//
+// In dotted notation: 2.5.29.30
+inline constexpr uint8_t kNameConstraintsOid[] = {0x55, 0x1d, 0x1e};
+
+// From RFC 5280:
+//
+// id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 }
+//
+// In dotted notation: 2.5.29.32
+inline constexpr uint8_t kCertificatePoliciesOid[] = {0x55, 0x1d, 0x20};
+
+// From RFC 5280:
+//
+// id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
+//
+// In dotted notation: 2.5.29.35
+inline constexpr uint8_t kAuthorityKeyIdentifierOid[] = {0x55, 0x1d, 0x23};
+
+// From RFC 5280:
+//
+// id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 }
+//
+// In dotted notation: 2.5.29.36
+inline constexpr uint8_t kPolicyConstraintsOid[] = {0x55, 0x1d, 0x24};
+
+// From RFC 5280:
+//
+// id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
+//
+// In dotted notation: 2.5.29.37
+inline constexpr uint8_t kExtKeyUsageOid[] = {0x55, 0x1d, 0x25};
+
+// From RFC 5280:
+//
+// id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
+//
+// In dotted notation: 1.3.6.1.5.5.7.1.1
+inline constexpr uint8_t kAuthorityInfoAccessOid[] = {0x2B, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01};
+
+// From RFC 5280:
+//
+// id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
+//
+// In dotted notation: 1.3.6.1.5.5.7.48.2
+inline constexpr uint8_t kAdCaIssuersOid[] = {0x2B, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x02};
+
+// From RFC 5280:
+//
+// id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
+//
+// In dotted notation: 1.3.6.1.5.5.7.48.1
+inline constexpr uint8_t kAdOcspOid[] = {0x2B, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01};
+
+// From RFC 5280:
+//
+// id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 }
+//
+// In dotted notation: 2.5.29.31
+inline constexpr uint8_t kCrlDistributionPointsOid[] = {0x55, 0x1d, 0x1f};
+
+// Parses the Extensions sequence as defined by RFC 5280. Extensions are added
+// to the map |extensions| keyed by the OID. Parsing guarantees that each OID
+// is unique. Note that certificate verification must consume each extension
+// marked as critical.
+//
+// Returns true on success and fills |extensions|. The output will reference
+// bytes in |extensions_tlv|, so that data must be kept alive.
+// On failure |extensions| may be partially written to and should not be used.
+[[nodiscard]] NET_EXPORT bool ParseExtensions(
+ const der::Input& extensions_tlv,
+ std::map<der::Input, ParsedExtension>* extensions);
+
+// Removes the extension with OID |oid| from |unconsumed_extensions| and fills
+// |extension| with the matching extension value. If there was no extension
+// matching |oid| then returns |false|.
+[[nodiscard]] NET_EXPORT bool ConsumeExtension(
+ const der::Input& oid,
+ std::map<der::Input, ParsedExtension>* unconsumed_extensions,
+ ParsedExtension* extension);
+
+struct ParsedBasicConstraints {
+ bool is_ca = false;
+ bool has_path_len = false;
+ uint8_t path_len = 0;
+};
+
+// Parses the BasicConstraints extension as defined by RFC 5280:
+//
+// BasicConstraints ::= SEQUENCE {
+// cA BOOLEAN DEFAULT FALSE,
+// pathLenConstraint INTEGER (0..MAX) OPTIONAL }
+//
+// The maximum allowed value of pathLenConstraints will be whatever can fit
+// into a uint8_t.
+[[nodiscard]] NET_EXPORT bool ParseBasicConstraints(
+ const der::Input& basic_constraints_tlv,
+ ParsedBasicConstraints* out);
+
+// KeyUsageBit contains the index for a particular key usage. The index is
+// measured from the most significant bit of a bit string.
+//
+// From RFC 5280 section 4.2.1.3:
+//
+// KeyUsage ::= BIT STRING {
+// digitalSignature (0),
+// nonRepudiation (1), -- recent editions of X.509 have
+// -- renamed this bit to contentCommitment
+// keyEncipherment (2),
+// dataEncipherment (3),
+// keyAgreement (4),
+// keyCertSign (5),
+// cRLSign (6),
+// encipherOnly (7),
+// decipherOnly (8) }
+enum KeyUsageBit {
+ KEY_USAGE_BIT_DIGITAL_SIGNATURE = 0,
+ KEY_USAGE_BIT_NON_REPUDIATION = 1,
+ KEY_USAGE_BIT_KEY_ENCIPHERMENT = 2,
+ KEY_USAGE_BIT_DATA_ENCIPHERMENT = 3,
+ KEY_USAGE_BIT_KEY_AGREEMENT = 4,
+ KEY_USAGE_BIT_KEY_CERT_SIGN = 5,
+ KEY_USAGE_BIT_CRL_SIGN = 6,
+ KEY_USAGE_BIT_ENCIPHER_ONLY = 7,
+ KEY_USAGE_BIT_DECIPHER_ONLY = 8,
+};
+
+// Parses the KeyUsage extension as defined by RFC 5280. Returns true on
+// success, and |key_usage| will alias data in |key_usage_tlv|. On failure
+// returns false, and |key_usage| may have been modified.
+//
+// In addition to validating that key_usage_tlv is a BIT STRING, this does
+// additional KeyUsage specific validations such as requiring at least 1 bit to
+// be set.
+//
+// To test if a particular key usage is set, call, e.g.:
+// key_usage->AssertsBit(KEY_USAGE_BIT_DIGITAL_SIGNATURE);
+[[nodiscard]] NET_EXPORT bool ParseKeyUsage(const der::Input& key_usage_tlv,
+ der::BitString* key_usage);
+
+struct AuthorityInfoAccessDescription {
+ // The accessMethod DER OID value.
+ der::Input access_method_oid;
+ // The accessLocation DER TLV.
+ der::Input access_location;
+};
+// Parses the Authority Information Access extension defined by RFC 5280.
+// Returns true on success, and |out_access_descriptions| will alias data
+// in |authority_info_access_tlv|.On failure returns false, and
+// out_access_descriptions may have been partially filled.
+//
+// No validation is performed on the contents of the
+// AuthorityInfoAccessDescription fields.
+[[nodiscard]] NET_EXPORT bool ParseAuthorityInfoAccess(
+ const der::Input& authority_info_access_tlv,
+ std::vector<AuthorityInfoAccessDescription>* out_access_descriptions);
+
+// Parses the Authority Information Access extension defined by RFC 5280,
+// extracting the caIssuers URIs and OCSP URIs.
+//
+// Returns true on success, and |out_ca_issuers_uris| and |out_ocsp_uris| will
+// alias data in |authority_info_access_tlv|. On failure returns false, and
+// |out_ca_issuers_uris| and |out_ocsp_uris| may have been partially filled.
+//
+// |out_ca_issuers_uris| is filled with the accessLocations of type
+// uniformResourceIdentifier for the accessMethod id-ad-caIssuers.
+// |out_ocsp_uris| is filled with the accessLocations of type
+// uniformResourceIdentifier for the accessMethod id-ad-ocsp.
+//
+// The values in |out_ca_issuers_uris| and |out_ocsp_uris| are checked to be
+// IA5String (ASCII strings), but no other validation is performed on them.
+//
+// accessMethods other than id-ad-caIssuers and id-ad-ocsp are silently ignored.
+// accessLocation types other than uniformResourceIdentifier are silently
+// ignored.
+[[nodiscard]] NET_EXPORT bool ParseAuthorityInfoAccessURIs(
+ const der::Input& authority_info_access_tlv,
+ std::vector<base::StringPiece>* out_ca_issuers_uris,
+ std::vector<base::StringPiece>* out_ocsp_uris);
+
+// ParsedDistributionPoint represents a parsed DistributionPoint from RFC 5280.
+//
+// DistributionPoint ::= SEQUENCE {
+// distributionPoint [0] DistributionPointName OPTIONAL,
+// reasons [1] ReasonFlags OPTIONAL,
+// cRLIssuer [2] GeneralNames OPTIONAL }
+struct NET_EXPORT ParsedDistributionPoint {
+ ParsedDistributionPoint();
+ ParsedDistributionPoint(ParsedDistributionPoint&& other);
+ ~ParsedDistributionPoint();
+
+ // The parsed fullName, if distributionPoint was present and was a fullName.
+ std::unique_ptr<GeneralNames> distribution_point_fullname;
+
+ // If present, the DER encoded value of the nameRelativeToCRLIssuer field.
+ // This should be a RelativeDistinguishedName, but the parser does not
+ // validate it.
+ absl::optional<der::Input> distribution_point_name_relative_to_crl_issuer;
+
+ // If present, the DER encoded value of the reasons field. This should be a
+ // ReasonFlags bitString, but the parser does not validate it.
+ absl::optional<der::Input> reasons;
+
+ // If present, the DER encoded value of the cRLIssuer field. This should be a
+ // GeneralNames, but the parser does not validate it.
+ absl::optional<der::Input> crl_issuer;
+};
+
+// Parses the value of a CRL Distribution Points extension (sequence of
+// DistributionPoint). Return true on success, and fills |distribution_points|
+// with values that reference data in |distribution_points_tlv|.
+[[nodiscard]] NET_EXPORT bool ParseCrlDistributionPoints(
+ const der::Input& distribution_points_tlv,
+ std::vector<ParsedDistributionPoint>* distribution_points);
+
+// Represents the AuthorityKeyIdentifier extension defined by RFC 5280 section
+// 4.2.1.1.
+//
+// AuthorityKeyIdentifier ::= SEQUENCE {
+// keyIdentifier [0] KeyIdentifier OPTIONAL,
+// authorityCertIssuer [1] GeneralNames OPTIONAL,
+// authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
+//
+// KeyIdentifier ::= OCTET STRING
+struct NET_EXPORT ParsedAuthorityKeyIdentifier {
+ ParsedAuthorityKeyIdentifier();
+ ~ParsedAuthorityKeyIdentifier();
+ ParsedAuthorityKeyIdentifier(ParsedAuthorityKeyIdentifier&& other);
+ ParsedAuthorityKeyIdentifier& operator=(ParsedAuthorityKeyIdentifier&& other);
+
+ // The keyIdentifier, which is an OCTET STRING.
+ absl::optional<der::Input> key_identifier;
+
+ // The authorityCertIssuer, which should be a GeneralNames, but this is not
+ // enforced by ParseAuthorityKeyIdentifier.
+ absl::optional<der::Input> authority_cert_issuer;
+
+ // The DER authorityCertSerialNumber, which should be a
+ // CertificateSerialNumber (an INTEGER) but this is not enforced by
+ // ParseAuthorityKeyIdentifier.
+ absl::optional<der::Input> authority_cert_serial_number;
+};
+
+// Parses the value of an authorityKeyIdentifier extension. Returns true on
+// success and fills |authority_key_identifier| with values that reference data
+// in |extension_value|. On failure the state of |authority_key_identifier| is
+// not guaranteed.
+[[nodiscard]] NET_EXPORT bool ParseAuthorityKeyIdentifier(
+ const der::Input& extension_value,
+ ParsedAuthorityKeyIdentifier* authority_key_identifier);
+
+// Parses the value of a subjectKeyIdentifier extension. Returns true on
+// success and |subject_key_identifier| references data in |extension_value|.
+// On failure the state of |subject_key_identifier| is not guaranteed.
+[[nodiscard]] NET_EXPORT bool ParseSubjectKeyIdentifier(
+ const der::Input& extension_value,
+ der::Input* subject_key_identifier);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_PARSE_CERTIFICATE_H_
diff --git a/chromium/net/cert/pki/parse_certificate_fuzzer.cc b/chromium/net/cert/pki/parse_certificate_fuzzer.cc
new file mode 100644
index 00000000000..b73eb018a24
--- /dev/null
+++ b/chromium/net/cert/pki/parse_certificate_fuzzer.cc
@@ -0,0 +1,24 @@
+// Copyright 2016 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 <stddef.h>
+#include <stdint.h>
+
+#include "base/check_op.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/cert/x509_util.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ net::CertErrors errors;
+ scoped_refptr<net::ParsedCertificate> cert = net::ParsedCertificate::Create(
+ net::x509_util::CreateCryptoBuffer(base::make_span(data, size)), {},
+ &errors);
+
+ // Severe errors must be provided iff the parsing failed.
+ CHECK_EQ(errors.ContainsAnyErrorWithSeverity(net::CertError::SEVERITY_HIGH),
+ cert == nullptr);
+
+ return 0;
+}
diff --git a/chromium/net/cert/pki/parse_certificate_unittest.cc b/chromium/net/cert/pki/parse_certificate_unittest.cc
new file mode 100644
index 00000000000..7f5c48efe3e
--- /dev/null
+++ b/chromium/net/cert/pki/parse_certificate_unittest.cc
@@ -0,0 +1,1176 @@
+// Copyright 2015 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 "net/cert/pki/parse_certificate.h"
+
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/general_names.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/cert/pki/test_helpers.h"
+#include "net/der/input.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+
+namespace net {
+
+namespace {
+
+// Pretty-prints a GeneralizedTime as a human-readable string for use in test
+// expectations (it is more readable to specify the expected results as a
+// string).
+std::string ToString(const der::GeneralizedTime& time) {
+ std::ostringstream pretty_time;
+ pretty_time << "year=" << int{time.year} << ", month=" << int{time.month}
+ << ", day=" << int{time.day} << ", hours=" << int{time.hours}
+ << ", minutes=" << int{time.minutes}
+ << ", seconds=" << int{time.seconds};
+ return pretty_time.str();
+}
+
+std::string GetFilePath(const std::string& file_name) {
+ return std::string("net/data/parse_certificate_unittest/") + file_name;
+}
+
+// Loads certificate data and expectations from the PEM file |file_name|.
+// Verifies that parsing the Certificate matches expectations:
+// * If expected to fail, emits the expected errors
+// * If expected to succeeds, the parsed fields match expectations
+void RunCertificateTest(const std::string& file_name) {
+ std::string data;
+ std::string expected_errors;
+ std::string expected_tbs_certificate;
+ std::string expected_signature_algorithm;
+ std::string expected_signature;
+
+ // Read the certificate data and test expectations from a single PEM file.
+ const PemBlockMapping mappings[] = {
+ {"CERTIFICATE", &data},
+ {"ERRORS", &expected_errors, true /*optional*/},
+ {"SIGNATURE", &expected_signature, true /*optional*/},
+ {"SIGNATURE ALGORITHM", &expected_signature_algorithm, true /*optional*/},
+ {"TBS CERTIFICATE", &expected_tbs_certificate, true /*optional*/},
+ };
+ std::string test_file_path = GetFilePath(file_name);
+ ASSERT_TRUE(ReadTestDataFromPemFile(test_file_path, mappings));
+
+ // Note that empty expected_errors doesn't necessarily mean success.
+ bool expected_result = !expected_tbs_certificate.empty();
+
+ // Parsing the certificate.
+ der::Input tbs_certificate_tlv;
+ der::Input signature_algorithm_tlv;
+ der::BitString signature_value;
+ CertErrors errors;
+ bool actual_result =
+ ParseCertificate(der::Input(&data), &tbs_certificate_tlv,
+ &signature_algorithm_tlv, &signature_value, &errors);
+
+ EXPECT_EQ(expected_result, actual_result);
+ VerifyCertErrors(expected_errors, errors, test_file_path);
+
+ // Ensure that the parsed certificate matches expectations.
+ if (expected_result && actual_result) {
+ EXPECT_EQ(0, signature_value.unused_bits());
+ EXPECT_EQ(der::Input(&expected_signature), signature_value.bytes());
+ EXPECT_EQ(der::Input(&expected_signature_algorithm),
+ signature_algorithm_tlv);
+ EXPECT_EQ(der::Input(&expected_tbs_certificate), tbs_certificate_tlv);
+ }
+}
+
+// Tests parsing a Certificate.
+TEST(ParseCertificateTest, Version3) {
+ RunCertificateTest("cert_version3.pem");
+}
+
+// Tests parsing a simplified Certificate-like structure (the sub-fields for
+// algorithm and tbsCertificate are not actually valid, but ParseCertificate()
+// doesn't check them)
+TEST(ParseCertificateTest, Skeleton) {
+ RunCertificateTest("cert_skeleton.pem");
+}
+
+// Tests parsing a Certificate that is not a sequence fails.
+TEST(ParseCertificateTest, NotSequence) {
+ RunCertificateTest("cert_not_sequence.pem");
+}
+
+// Tests that uncomsumed data is not allowed after the main SEQUENCE.
+TEST(ParseCertificateTest, DataAfterSignature) {
+ RunCertificateTest("cert_data_after_signature.pem");
+}
+
+// Tests that parsing fails if the signature BIT STRING is missing.
+TEST(ParseCertificateTest, MissingSignature) {
+ RunCertificateTest("cert_missing_signature.pem");
+}
+
+// Tests that parsing fails if the signature is present but not a BIT STRING.
+TEST(ParseCertificateTest, SignatureNotBitString) {
+ RunCertificateTest("cert_signature_not_bit_string.pem");
+}
+
+// Tests that parsing fails if the main SEQUENCE is empty (missing all the
+// fields).
+TEST(ParseCertificateTest, EmptySequence) {
+ RunCertificateTest("cert_empty_sequence.pem");
+}
+
+// Tests what happens when the signature algorithm is present, but has the wrong
+// tag.
+TEST(ParseCertificateTest, AlgorithmNotSequence) {
+ RunCertificateTest("cert_algorithm_not_sequence.pem");
+}
+
+// Loads tbsCertificate data and expectations from the PEM file |file_name|.
+// Verifies that parsing the TBSCertificate succeeds, and each parsed field
+// matches the expectations.
+//
+// TODO(eroman): Get rid of the |expected_version| parameter -- this should be
+// encoded in the test expectations file.
+void RunTbsCertificateTestGivenVersion(const std::string& file_name,
+ CertificateVersion expected_version) {
+ std::string data;
+ std::string expected_serial_number;
+ std::string expected_signature_algorithm;
+ std::string expected_issuer;
+ std::string expected_validity_not_before;
+ std::string expected_validity_not_after;
+ std::string expected_subject;
+ std::string expected_spki;
+ std::string expected_issuer_unique_id;
+ std::string expected_subject_unique_id;
+ std::string expected_extensions;
+ std::string expected_errors;
+
+ // Read the certificate data and test expectations from a single PEM file.
+ const PemBlockMapping mappings[] = {
+ {"TBS CERTIFICATE", &data},
+ {"SIGNATURE ALGORITHM", &expected_signature_algorithm, true},
+ {"SERIAL NUMBER", &expected_serial_number, true},
+ {"ISSUER", &expected_issuer, true},
+ {"VALIDITY NOTBEFORE", &expected_validity_not_before, true},
+ {"VALIDITY NOTAFTER", &expected_validity_not_after, true},
+ {"SUBJECT", &expected_subject, true},
+ {"SPKI", &expected_spki, true},
+ {"ISSUER UNIQUE ID", &expected_issuer_unique_id, true},
+ {"SUBJECT UNIQUE ID", &expected_subject_unique_id, true},
+ {"EXTENSIONS", &expected_extensions, true},
+ {"ERRORS", &expected_errors, true},
+ };
+ std::string test_file_path = GetFilePath(file_name);
+ ASSERT_TRUE(ReadTestDataFromPemFile(test_file_path, mappings));
+
+ bool expected_result = !expected_spki.empty();
+
+ ParsedTbsCertificate parsed;
+ CertErrors errors;
+ bool actual_result =
+ ParseTbsCertificate(der::Input(&data), {}, &parsed, &errors);
+
+ EXPECT_EQ(expected_result, actual_result);
+ VerifyCertErrors(expected_errors, errors, test_file_path);
+
+ if (!expected_result || !actual_result)
+ return;
+
+ // Ensure that the ParsedTbsCertificate matches expectations.
+ EXPECT_EQ(expected_version, parsed.version);
+
+ EXPECT_EQ(der::Input(&expected_serial_number), parsed.serial_number);
+ EXPECT_EQ(der::Input(&expected_signature_algorithm),
+ parsed.signature_algorithm_tlv);
+
+ EXPECT_EQ(der::Input(&expected_issuer), parsed.issuer_tlv);
+
+ // In the test expectations PEM file, validity is described as a
+ // textual string of the parsed value (rather than as DER).
+ EXPECT_EQ(expected_validity_not_before, ToString(parsed.validity_not_before));
+ EXPECT_EQ(expected_validity_not_after, ToString(parsed.validity_not_after));
+
+ EXPECT_EQ(der::Input(&expected_subject), parsed.subject_tlv);
+ EXPECT_EQ(der::Input(&expected_spki), parsed.spki_tlv);
+
+ EXPECT_EQ(!expected_issuer_unique_id.empty(),
+ parsed.issuer_unique_id.has_value());
+ if (parsed.issuer_unique_id.has_value()) {
+ EXPECT_EQ(der::Input(&expected_issuer_unique_id),
+ parsed.issuer_unique_id->bytes());
+ }
+ EXPECT_EQ(!expected_subject_unique_id.empty(),
+ parsed.subject_unique_id.has_value());
+ if (parsed.subject_unique_id.has_value()) {
+ EXPECT_EQ(der::Input(&expected_subject_unique_id),
+ parsed.subject_unique_id->bytes());
+ }
+
+ EXPECT_EQ(!expected_extensions.empty(), parsed.extensions_tlv.has_value());
+ if (parsed.extensions_tlv) {
+ EXPECT_EQ(der::Input(&expected_extensions), parsed.extensions_tlv.value());
+ }
+}
+
+void RunTbsCertificateTest(const std::string& file_name) {
+ RunTbsCertificateTestGivenVersion(file_name, CertificateVersion::V3);
+}
+
+// Tests parsing a TBSCertificate for v3 that contains no optional fields.
+TEST(ParseTbsCertificateTest, Version3NoOptionals) {
+ RunTbsCertificateTest("tbs_v3_no_optionals.pem");
+}
+
+// Tests parsing a TBSCertificate for v3 that contains extensions.
+TEST(ParseTbsCertificateTest, Version3WithExtensions) {
+ RunTbsCertificateTest("tbs_v3_extensions.pem");
+}
+
+// Tests parsing a TBSCertificate which lacks a version number (causing it to
+// default to v1).
+TEST(ParseTbsCertificateTest, Version1) {
+ RunTbsCertificateTestGivenVersion("tbs_v1.pem", CertificateVersion::V1);
+}
+
+// The version was set to v1 explicitly rather than omitting the version field.
+TEST(ParseTbsCertificateTest, ExplicitVersion1) {
+ RunTbsCertificateTest("tbs_explicit_v1.pem");
+}
+
+// Extensions are not defined in version 1.
+TEST(ParseTbsCertificateTest, Version1WithExtensions) {
+ RunTbsCertificateTest("tbs_v1_extensions.pem");
+}
+
+// Extensions are not defined in version 2.
+TEST(ParseTbsCertificateTest, Version2WithExtensions) {
+ RunTbsCertificateTest("tbs_v2_extensions.pem");
+}
+
+// A boring version 2 certificate with none of the optional fields.
+TEST(ParseTbsCertificateTest, Version2NoOptionals) {
+ RunTbsCertificateTestGivenVersion("tbs_v2_no_optionals.pem",
+ CertificateVersion::V2);
+}
+
+// A version 2 certificate with an issuer unique ID field.
+TEST(ParseTbsCertificateTest, Version2IssuerUniqueId) {
+ RunTbsCertificateTestGivenVersion("tbs_v2_issuer_unique_id.pem",
+ CertificateVersion::V2);
+}
+
+// A version 2 certificate with both a issuer and subject unique ID field.
+TEST(ParseTbsCertificateTest, Version2IssuerAndSubjectUniqueId) {
+ RunTbsCertificateTestGivenVersion("tbs_v2_issuer_and_subject_unique_id.pem",
+ CertificateVersion::V2);
+}
+
+// A version 3 certificate with all of the optional fields (issuer unique id,
+// subject unique id, and extensions).
+TEST(ParseTbsCertificateTest, Version3AllOptionals) {
+ RunTbsCertificateTest("tbs_v3_all_optionals.pem");
+}
+
+// The version was set to v4, which is unrecognized.
+TEST(ParseTbsCertificateTest, Version4) {
+ RunTbsCertificateTest("tbs_v4.pem");
+}
+
+// Tests that extraneous data after extensions in a v3 is rejected.
+TEST(ParseTbsCertificateTest, Version3DataAfterExtensions) {
+ RunTbsCertificateTest("tbs_v3_data_after_extensions.pem");
+}
+
+// Tests using a real-world certificate (whereas the other tests are fabricated
+// (and in fact invalid) data.
+TEST(ParseTbsCertificateTest, Version3Real) {
+ RunTbsCertificateTest("tbs_v3_real.pem");
+}
+
+// Parses a TBSCertificate whose "validity" field expresses both notBefore
+// and notAfter using UTCTime.
+TEST(ParseTbsCertificateTest, ValidityBothUtcTime) {
+ RunTbsCertificateTest("tbs_validity_both_utc_time.pem");
+}
+
+// Parses a TBSCertificate whose "validity" field expresses both notBefore
+// and notAfter using GeneralizedTime.
+TEST(ParseTbsCertificateTest, ValidityBothGeneralizedTime) {
+ RunTbsCertificateTest("tbs_validity_both_generalized_time.pem");
+}
+
+// Parses a TBSCertificate whose "validity" field expresses notBefore using
+// UTCTime and notAfter using GeneralizedTime.
+TEST(ParseTbsCertificateTest, ValidityUTCTimeAndGeneralizedTime) {
+ RunTbsCertificateTest("tbs_validity_utc_time_and_generalized_time.pem");
+}
+
+// Parses a TBSCertificate whose validity" field expresses notBefore using
+// GeneralizedTime and notAfter using UTCTime. Also of interest, notBefore >
+// notAfter. Parsing will succeed, however no time can satisfy this constraint.
+TEST(ParseTbsCertificateTest, ValidityGeneralizedTimeAndUTCTime) {
+ RunTbsCertificateTest("tbs_validity_generalized_time_and_utc_time.pem");
+}
+
+// Parses a TBSCertificate whose "validity" field does not strictly follow
+// the DER rules (and fails to be parsed).
+TEST(ParseTbsCertificateTest, ValidityRelaxed) {
+ RunTbsCertificateTest("tbs_validity_relaxed.pem");
+}
+
+// Parses a KeyUsage with a single 0 bit.
+TEST(ParseKeyUsageTest, OneBitAllZeros) {
+ const uint8_t der[] = {
+ 0x03, 0x02, // BIT STRING
+ 0x07, // Number of unused bits
+ 0x00, // bits
+ };
+
+ der::BitString key_usage;
+ ASSERT_FALSE(ParseKeyUsage(der::Input(der), &key_usage));
+}
+
+// Parses a KeyUsage with 32 bits that are all 0.
+TEST(ParseKeyUsageTest, 32BitsAllZeros) {
+ const uint8_t der[] = {
+ 0x03, 0x05, // BIT STRING
+ 0x00, // Number of unused bits
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ der::BitString key_usage;
+ ASSERT_FALSE(ParseKeyUsage(der::Input(der), &key_usage));
+}
+
+// Parses a KeyUsage with 32 bits, one of which is 1 (but not in recognized
+// set).
+TEST(ParseKeyUsageTest, 32BitsOneSet) {
+ const uint8_t der[] = {
+ 0x03, 0x05, // BIT STRING
+ 0x00, // Number of unused bits
+ 0x00, 0x00, 0x00, 0x02,
+ };
+
+ der::BitString key_usage;
+ ASSERT_TRUE(ParseKeyUsage(der::Input(der), &key_usage));
+
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_DIGITAL_SIGNATURE));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_NON_REPUDIATION));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_KEY_ENCIPHERMENT));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_DATA_ENCIPHERMENT));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_KEY_AGREEMENT));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_CRL_SIGN));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_ENCIPHER_ONLY));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_DECIPHER_ONLY));
+}
+
+// Parses a KeyUsage containing bit string 101.
+TEST(ParseKeyUsageTest, ThreeBits) {
+ const uint8_t der[] = {
+ 0x03, 0x02, // BIT STRING
+ 0x05, // Number of unused bits
+ 0xA0, // bits
+ };
+
+ der::BitString key_usage;
+ ASSERT_TRUE(ParseKeyUsage(der::Input(der), &key_usage));
+
+ EXPECT_TRUE(key_usage.AssertsBit(KEY_USAGE_BIT_DIGITAL_SIGNATURE));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_NON_REPUDIATION));
+ EXPECT_TRUE(key_usage.AssertsBit(KEY_USAGE_BIT_KEY_ENCIPHERMENT));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_DATA_ENCIPHERMENT));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_KEY_AGREEMENT));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_CRL_SIGN));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_ENCIPHER_ONLY));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_DECIPHER_ONLY));
+}
+
+// Parses a KeyUsage containing DECIPHER_ONLY, which is the
+// only bit that doesn't fit in the first byte.
+TEST(ParseKeyUsageTest, DecipherOnly) {
+ const uint8_t der[] = {
+ 0x03, 0x03, // BIT STRING
+ 0x07, // Number of unused bits
+ 0x00, 0x80, // bits
+ };
+
+ der::BitString key_usage;
+ ASSERT_TRUE(ParseKeyUsage(der::Input(der), &key_usage));
+
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_DIGITAL_SIGNATURE));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_NON_REPUDIATION));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_KEY_ENCIPHERMENT));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_DATA_ENCIPHERMENT));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_KEY_AGREEMENT));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_CRL_SIGN));
+ EXPECT_FALSE(key_usage.AssertsBit(KEY_USAGE_BIT_ENCIPHER_ONLY));
+ EXPECT_TRUE(key_usage.AssertsBit(KEY_USAGE_BIT_DECIPHER_ONLY));
+}
+
+// Parses an empty KeyUsage.
+TEST(ParseKeyUsageTest, Empty) {
+ const uint8_t der[] = {
+ 0x03, 0x01, // BIT STRING
+ 0x00, // Number of unused bits
+ };
+
+ der::BitString key_usage;
+ ASSERT_FALSE(ParseKeyUsage(der::Input(der), &key_usage));
+}
+
+TEST(ParseAuthorityInfoAccess, BasicTests) {
+ // SEQUENCE {
+ // SEQUENCE {
+ // # ocsp with directoryName
+ // OBJECT_IDENTIFIER { 1.3.6.1.5.5.7.48.1 }
+ // [4] {
+ // SEQUENCE {
+ // SET {
+ // SEQUENCE {
+ // # commonName
+ // OBJECT_IDENTIFIER { 2.5.4.3 }
+ // PrintableString { "ocsp" }
+ // }
+ // }
+ // }
+ // }
+ // }
+ // SEQUENCE {
+ // # caIssuers with directoryName
+ // OBJECT_IDENTIFIER { 1.3.6.1.5.5.7.48.2 }
+ // [4] {
+ // SEQUENCE {
+ // SET {
+ // SEQUENCE {
+ // # commonName
+ // OBJECT_IDENTIFIER { 2.5.4.3 }
+ // PrintableString { "ca issuer" }
+ // }
+ // }
+ // }
+ // }
+ // }
+ // SEQUENCE {
+ // # non-standard method with URI
+ // OBJECT_IDENTIFIER { 1.3.6.1.5.5.7.48.3 }
+ // [6 PRIMITIVE] { "http://nonstandard.example.com" }
+ // }
+ // SEQUENCE {
+ // # ocsp with URI
+ // OBJECT_IDENTIFIER { 1.3.6.1.5.5.7.48.1 }
+ // [6 PRIMITIVE] { "http://ocsp.example.com" }
+ // }
+ // SEQUENCE {
+ // # caIssuers with URI
+ // OBJECT_IDENTIFIER { 1.3.6.1.5.5.7.48.2 }
+ // [6 PRIMITIVE] { "http://www.example.com/issuer.crt" }
+ // }
+ // }
+ const uint8_t der[] = {
+ 0x30, 0x81, 0xc3, 0x30, 0x1d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0xa4, 0x11, 0x30, 0x0f, 0x31, 0x0d, 0x30, 0x0b, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x04, 0x6f, 0x63, 0x73, 0x70, 0x30, 0x22,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0xa4, 0x16,
+ 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x09, 0x63, 0x61, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x30, 0x2a,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x03, 0x86, 0x1e,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x6f, 0x6e, 0x73, 0x74,
+ 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70,
+ 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x2d, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x21, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x73, 0x73, 0x75,
+ 0x65, 0x72, 0x2e, 0x63, 0x72, 0x74};
+
+ std::vector<AuthorityInfoAccessDescription> access_descriptions;
+ ASSERT_TRUE(ParseAuthorityInfoAccess(der::Input(der), &access_descriptions));
+ ASSERT_EQ(5u, access_descriptions.size());
+ {
+ const auto& desc = access_descriptions[0];
+ EXPECT_EQ(der::Input(kAdOcspOid), desc.access_method_oid);
+ const uint8_t location_der[] = {0xa4, 0x11, 0x30, 0x0f, 0x31, 0x0d, 0x30,
+ 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x04, 0x6f, 0x63, 0x73, 0x70};
+ EXPECT_EQ(der::Input(location_der), desc.access_location);
+ }
+ {
+ const auto& desc = access_descriptions[1];
+ EXPECT_EQ(der::Input(kAdCaIssuersOid), desc.access_method_oid);
+ const uint8_t location_der[] = {
+ 0xa4, 0x16, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x09, 0x63, 0x61, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72};
+ EXPECT_EQ(der::Input(location_der), desc.access_location);
+ }
+ {
+ const auto& desc = access_descriptions[2];
+ const uint8_t method_oid[] = {0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x03};
+ EXPECT_EQ(der::Input(method_oid), desc.access_method_oid);
+ const uint8_t location_der[] = {
+ 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x6f,
+ 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x2e, 0x65,
+ 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d};
+ EXPECT_EQ(der::Input(location_der), desc.access_location);
+ }
+ {
+ const auto& desc = access_descriptions[3];
+ EXPECT_EQ(der::Input(kAdOcspOid), desc.access_method_oid);
+ const uint8_t location_der[] = {0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d};
+ EXPECT_EQ(der::Input(location_der), desc.access_location);
+ }
+ {
+ const auto& desc = access_descriptions[4];
+ EXPECT_EQ(der::Input(kAdCaIssuersOid), desc.access_method_oid);
+ const uint8_t location_der[] = {
+ 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x2e, 0x63, 0x72, 0x74};
+ EXPECT_EQ(der::Input(location_der), desc.access_location);
+ }
+
+ std::vector<base::StringPiece> ca_issuers_uris, ocsp_uris;
+ ASSERT_TRUE(ParseAuthorityInfoAccessURIs(der::Input(der), &ca_issuers_uris,
+ &ocsp_uris));
+ ASSERT_EQ(1u, ca_issuers_uris.size());
+ EXPECT_EQ("http://www.example.com/issuer.crt", ca_issuers_uris.front());
+ ASSERT_EQ(1u, ocsp_uris.size());
+ EXPECT_EQ("http://ocsp.example.com", ocsp_uris.front());
+}
+
+TEST(ParseAuthorityInfoAccess, NoOcspOrCaIssuersURIs) {
+ // SEQUENCE {
+ // SEQUENCE {
+ // # non-standard method with directoryName
+ // OBJECT_IDENTIFIER { 1.2.3 }
+ // [4] {
+ // SEQUENCE {
+ // SET {
+ // SEQUENCE {
+ // # commonName
+ // OBJECT_IDENTIFIER { 2.5.4.3 }
+ // PrintableString { "foo" }
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ const uint8_t der[] = {0x30, 0x18, 0x30, 0x16, 0x06, 0x02, 0x2a, 0x03, 0xa4,
+ 0x10, 0x30, 0x0e, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x03, 0x66, 0x6f, 0x6f};
+
+ std::vector<AuthorityInfoAccessDescription> access_descriptions;
+ ASSERT_TRUE(ParseAuthorityInfoAccess(der::Input(der), &access_descriptions));
+ ASSERT_EQ(1u, access_descriptions.size());
+ const auto& desc = access_descriptions[0];
+ const uint8_t method_oid[] = {0x2a, 0x03};
+ EXPECT_EQ(der::Input(method_oid), desc.access_method_oid);
+ const uint8_t location_der[] = {0xa4, 0x10, 0x30, 0x0e, 0x31, 0x0c,
+ 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x03, 0x66, 0x6f, 0x6f};
+ EXPECT_EQ(der::Input(location_der), desc.access_location);
+
+ std::vector<base::StringPiece> ca_issuers_uris, ocsp_uris;
+ // ParseAuthorityInfoAccessURIs should still return success since it was a
+ // valid AuthorityInfoAccess extension, even though it did not contain any
+ // elements we care about, and both output vectors should be empty.
+ ASSERT_TRUE(ParseAuthorityInfoAccessURIs(der::Input(der), &ca_issuers_uris,
+ &ocsp_uris));
+ EXPECT_EQ(0u, ca_issuers_uris.size());
+ EXPECT_EQ(0u, ocsp_uris.size());
+}
+
+TEST(ParseAuthorityInfoAccess, IncompleteAccessDescription) {
+ // SEQUENCE {
+ // # first entry is ok
+ // SEQUENCE {
+ // OBJECT_IDENTIFIER { 1.3.6.1.5.5.7.48.1 }
+ // [6 PRIMITIVE] { "http://ocsp.example.com" }
+ // }
+ // # second is missing accessLocation field
+ // SEQUENCE {
+ // OBJECT_IDENTIFIER { 1.3.6.1.5.5.7.48.2 }
+ // }
+ // }
+ const uint8_t der[] = {0x30, 0x31, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70,
+ 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x02};
+
+ std::vector<AuthorityInfoAccessDescription> access_descriptions;
+ EXPECT_FALSE(ParseAuthorityInfoAccess(der::Input(der), &access_descriptions));
+
+ std::vector<base::StringPiece> ca_issuers_uris, ocsp_uris;
+ EXPECT_FALSE(ParseAuthorityInfoAccessURIs(der::Input(der), &ca_issuers_uris,
+ &ocsp_uris));
+}
+
+TEST(ParseAuthorityInfoAccess, ExtraDataInAccessDescription) {
+ // SEQUENCE {
+ // SEQUENCE {
+ // OBJECT_IDENTIFIER { 1.3.6.1.5.5.7.48.1 }
+ // [6 PRIMITIVE] { "http://ocsp.example.com" }
+ // # invalid, AccessDescription only has 2 fields
+ // PrintableString { "henlo" }
+ // }
+ // }
+ const uint8_t der[] = {
+ 0x30, 0x2c, 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f,
+ 0x63, 0x73, 0x70, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x13, 0x05, 0x68, 0x65, 0x6e, 0x6c, 0x6f};
+
+ std::vector<AuthorityInfoAccessDescription> access_descriptions;
+ EXPECT_FALSE(ParseAuthorityInfoAccess(der::Input(der), &access_descriptions));
+
+ std::vector<base::StringPiece> ca_issuers_uris, ocsp_uris;
+ EXPECT_FALSE(ParseAuthorityInfoAccessURIs(der::Input(der), &ca_issuers_uris,
+ &ocsp_uris));
+}
+
+TEST(ParseAuthorityInfoAccess, EmptySequence) {
+ // SEQUENCE { }
+ const uint8_t der[] = {0x30, 0x00};
+
+ std::vector<AuthorityInfoAccessDescription> access_descriptions;
+ EXPECT_FALSE(ParseAuthorityInfoAccess(der::Input(der), &access_descriptions));
+
+ std::vector<base::StringPiece> ca_issuers_uris, ocsp_uris;
+ EXPECT_FALSE(ParseAuthorityInfoAccessURIs(der::Input(der), &ca_issuers_uris,
+ &ocsp_uris));
+}
+
+// Test fixture for testing ParseCrlDistributionPoints.
+//
+// Test data is encoded in certificate files. This fixture is responsible for
+// reading and parsing the certificates to get at the extension under test.
+class ParseCrlDistributionPointsTest : public ::testing::Test {
+ public:
+ protected:
+ bool GetCrlDps(const char* file_name,
+ std::vector<ParsedDistributionPoint>* dps) {
+ std::string cert_bytes;
+ // Read the test certificate file.
+ const PemBlockMapping mappings[] = {
+ {"CERTIFICATE", &cert_bytes},
+ };
+ std::string test_file_path = GetFilePath(file_name);
+ EXPECT_TRUE(ReadTestDataFromPemFile(test_file_path, mappings));
+
+ // Extract the CRLDP from the test Certificate.
+ CertErrors errors;
+ scoped_refptr<ParsedCertificate> cert = ParsedCertificate::Create(
+ bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(
+ reinterpret_cast<const uint8_t*>(cert_bytes.data()),
+ cert_bytes.size(), nullptr)),
+ {}, &errors);
+
+ if (!cert)
+ return false;
+
+ auto it = cert->extensions().find(der::Input(kCrlDistributionPointsOid));
+ if (it == cert->extensions().end())
+ return false;
+
+ der::Input crl_dp_tlv = it->second.value;
+
+ // Keep the certificate data alive, since this function will return
+ // der::Inputs that reference it. Run the function under test (for parsing
+ //
+ // TODO(eroman): The use of ParsedCertificate in this test should be removed
+ // in lieu of lazy parsing.
+ keep_alive_certs_.push_back(cert);
+
+ return ParseCrlDistributionPoints(crl_dp_tlv, dps);
+ }
+
+ private:
+ ParsedCertificateList keep_alive_certs_;
+};
+
+TEST_F(ParseCrlDistributionPointsTest, OneUriNoIssuer) {
+ std::vector<ParsedDistributionPoint> dps;
+ ASSERT_TRUE(GetCrlDps("crldp_1uri_noissuer.pem", &dps));
+
+ ASSERT_EQ(1u, dps.size());
+ const ParsedDistributionPoint& dp1 = dps.front();
+
+ ASSERT_TRUE(dp1.distribution_point_fullname);
+ const GeneralNames& fullname = *dp1.distribution_point_fullname;
+ EXPECT_EQ(GENERAL_NAME_UNIFORM_RESOURCE_IDENTIFIER,
+ fullname.present_name_types);
+ ASSERT_EQ(1u, fullname.uniform_resource_identifiers.size());
+ EXPECT_EQ(fullname.uniform_resource_identifiers.front(),
+ std::string("http://www.example.com/foo.crl"));
+
+ EXPECT_FALSE(dp1.distribution_point_name_relative_to_crl_issuer);
+ EXPECT_FALSE(dp1.reasons);
+ EXPECT_FALSE(dp1.crl_issuer);
+}
+
+TEST_F(ParseCrlDistributionPointsTest, ThreeUrisNoIssuer) {
+ std::vector<ParsedDistributionPoint> dps;
+ ASSERT_TRUE(GetCrlDps("crldp_3uri_noissuer.pem", &dps));
+
+ ASSERT_EQ(1u, dps.size());
+ const ParsedDistributionPoint& dp1 = dps.front();
+
+ ASSERT_TRUE(dp1.distribution_point_fullname);
+ const GeneralNames& fullname = *dp1.distribution_point_fullname;
+ EXPECT_EQ(GENERAL_NAME_UNIFORM_RESOURCE_IDENTIFIER,
+ fullname.present_name_types);
+ ASSERT_EQ(3u, fullname.uniform_resource_identifiers.size());
+ EXPECT_EQ(fullname.uniform_resource_identifiers[0],
+ std::string("http://www.example.com/foo1.crl"));
+ EXPECT_EQ(fullname.uniform_resource_identifiers[1],
+ std::string("http://www.example.com/blah.crl"));
+ EXPECT_EQ(fullname.uniform_resource_identifiers[2],
+ std::string("not-even-a-url"));
+
+ EXPECT_FALSE(dp1.distribution_point_name_relative_to_crl_issuer);
+ EXPECT_FALSE(dp1.reasons);
+ EXPECT_FALSE(dp1.crl_issuer);
+}
+
+TEST_F(ParseCrlDistributionPointsTest, CrlIssuerAsDirname) {
+ std::vector<ParsedDistributionPoint> dps;
+ ASSERT_TRUE(GetCrlDps("crldp_issuer_as_dirname.pem", &dps));
+
+ ASSERT_EQ(1u, dps.size());
+ const ParsedDistributionPoint& dp1 = dps.front();
+ ASSERT_TRUE(dp1.distribution_point_fullname);
+ const GeneralNames& fullname = *dp1.distribution_point_fullname;
+ EXPECT_EQ(GENERAL_NAME_DIRECTORY_NAME, fullname.present_name_types);
+ // Generated by `ascii2der | xxd -i` from the Name value in
+ // crldp_issuer_as_dirname.pem.
+ const uint8_t kExpectedName[] = {
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x16,
+ 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x32, 0x30, 0x31, 0x31, 0x31, 0x22,
+ 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x19, 0x69, 0x6e, 0x64,
+ 0x69, 0x72, 0x65, 0x63, 0x74, 0x43, 0x52, 0x4c, 0x20, 0x43, 0x41, 0x33,
+ 0x20, 0x63, 0x52, 0x4c, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72, 0x31, 0x29,
+ 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x69, 0x6e, 0x64,
+ 0x69, 0x72, 0x65, 0x63, 0x74, 0x20, 0x43, 0x52, 0x4c, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x43, 0x52,
+ 0x4c, 0x20, 0x43, 0x41, 0x33};
+ ASSERT_EQ(1u, fullname.directory_names.size());
+ EXPECT_EQ(der::Input(kExpectedName), fullname.directory_names[0]);
+
+ EXPECT_FALSE(dp1.distribution_point_name_relative_to_crl_issuer);
+ EXPECT_FALSE(dp1.reasons);
+
+ ASSERT_TRUE(dp1.crl_issuer);
+ // Generated by `ascii2der | xxd -i` from the cRLIssuer value in
+ // crldp_issuer_as_dirname.pem.
+ const uint8_t kExpectedCrlIssuer[] = {
+ 0xa4, 0x54, 0x30, 0x52, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1f, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x16, 0x54, 0x65, 0x73, 0x74, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+ 0x73, 0x20, 0x32, 0x30, 0x31, 0x31, 0x31, 0x22, 0x30, 0x20, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x19, 0x69, 0x6e, 0x64, 0x69, 0x72,
+ 0x65, 0x63, 0x74, 0x43, 0x52, 0x4c, 0x20, 0x43, 0x41, 0x33, 0x20,
+ 0x63, 0x52, 0x4c, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72};
+ EXPECT_EQ(der::Input(kExpectedCrlIssuer), dp1.crl_issuer);
+}
+
+TEST_F(ParseCrlDistributionPointsTest, FullnameAsDirname) {
+ std::vector<ParsedDistributionPoint> dps;
+ ASSERT_TRUE(GetCrlDps("crldp_full_name_as_dirname.pem", &dps));
+
+ ASSERT_EQ(1u, dps.size());
+ const ParsedDistributionPoint& dp1 = dps.front();
+
+ ASSERT_TRUE(dp1.distribution_point_fullname);
+ const GeneralNames& fullname = *dp1.distribution_point_fullname;
+ EXPECT_EQ(GENERAL_NAME_DIRECTORY_NAME, fullname.present_name_types);
+ // Generated by `ascii2der | xxd -i` from the Name value in
+ // crldp_full_name_as_dirname.pem.
+ const uint8_t kExpectedName[] = {
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x16,
+ 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x32, 0x30, 0x31, 0x31, 0x31, 0x45,
+ 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x53, 0x65, 0x6c,
+ 0x66, 0x2d, 0x49, 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, 0x43, 0x65, 0x72,
+ 0x74, 0x20, 0x44, 0x50, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x42, 0x61, 0x73,
+ 0x69, 0x63, 0x20, 0x53, 0x65, 0x6c, 0x66, 0x2d, 0x49, 0x73, 0x73, 0x75,
+ 0x65, 0x64, 0x20, 0x43, 0x52, 0x4c, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69,
+ 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x41};
+ ASSERT_EQ(1u, fullname.directory_names.size());
+ EXPECT_EQ(der::Input(kExpectedName), fullname.directory_names[0]);
+
+ EXPECT_FALSE(dp1.distribution_point_name_relative_to_crl_issuer);
+ EXPECT_FALSE(dp1.reasons);
+ EXPECT_FALSE(dp1.crl_issuer);
+}
+
+TEST_F(ParseCrlDistributionPointsTest, RelativeNameAndReasonsAndMultipleDPs) {
+ // SEQUENCE {
+ // SEQUENCE {
+ // # distributionPoint
+ // [0] {
+ // # nameRelativeToCRLIssuer
+ // [1] {
+ // SET {
+ // SEQUENCE {
+ // # commonName
+ // OBJECT_IDENTIFIER { 2.5.4.3 }
+ // PrintableString { "CRL1" }
+ // }
+ // }
+ // }
+ // }
+ // # reasons
+ // [1 PRIMITIVE] { b`011` }
+ // }
+ // SEQUENCE {
+ // # distributionPoint
+ // [0] {
+ // # fullName
+ // [0] {
+ // [4] {
+ // SEQUENCE {
+ // SET {
+ // SEQUENCE {
+ // # commonName
+ // OBJECT_IDENTIFIER { 2.5.4.3 }
+ // PrintableString { "CRL2" }
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ // # reasons
+ // [1 PRIMITIVE] { b`100111111` }
+ // }
+ // }
+ const uint8_t kInputDer[] = {
+ 0x30, 0x37, 0x30, 0x17, 0xa0, 0x11, 0xa1, 0x0f, 0x31, 0x0d, 0x30, 0x0b,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x04, 0x43, 0x52, 0x4c, 0x31, 0x81,
+ 0x02, 0x05, 0x60, 0x30, 0x1c, 0xa0, 0x15, 0xa0, 0x13, 0xa4, 0x11, 0x30,
+ 0x0f, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x04,
+ 0x43, 0x52, 0x4c, 0x32, 0x81, 0x03, 0x07, 0x9f, 0x80};
+
+ std::vector<ParsedDistributionPoint> dps;
+ ASSERT_TRUE(ParseCrlDistributionPoints(der::Input(kInputDer), &dps));
+ ASSERT_EQ(2u, dps.size());
+ {
+ const ParsedDistributionPoint& dp = dps[0];
+ EXPECT_FALSE(dp.distribution_point_fullname);
+
+ ASSERT_TRUE(dp.distribution_point_name_relative_to_crl_issuer);
+ // SET {
+ // SEQUENCE {
+ // # commonName
+ // OBJECT_IDENTIFIER { 2.5.4.3 }
+ // PrintableString { "CRL1" }
+ // }
+ // }
+ const uint8_t kExpectedRDN[] = {0x31, 0x0d, 0x30, 0x0b, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x04, 0x43, 0x52, 0x4c, 0x31};
+ EXPECT_EQ(der::Input(kExpectedRDN),
+ *dp.distribution_point_name_relative_to_crl_issuer);
+
+ ASSERT_TRUE(dp.reasons);
+ const uint8_t kExpectedReasons[] = {0x05, 0x60};
+ EXPECT_EQ(der::Input(kExpectedReasons), *dp.reasons);
+
+ EXPECT_FALSE(dp.crl_issuer);
+ }
+ {
+ const ParsedDistributionPoint& dp = dps[1];
+ ASSERT_TRUE(dp.distribution_point_fullname);
+ const GeneralNames& fullname = *dp.distribution_point_fullname;
+ EXPECT_EQ(GENERAL_NAME_DIRECTORY_NAME, fullname.present_name_types);
+ // SET {
+ // SEQUENCE {
+ // # commonName
+ // OBJECT_IDENTIFIER { 2.5.4.3 }
+ // PrintableString { "CRL2" }
+ // }
+ // }
+ const uint8_t kExpectedName[] = {0x31, 0x0d, 0x30, 0x0b, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x04, 0x43, 0x52, 0x4c, 0x32};
+ ASSERT_EQ(1u, fullname.directory_names.size());
+ EXPECT_EQ(der::Input(kExpectedName), fullname.directory_names[0]);
+
+ EXPECT_FALSE(dp.distribution_point_name_relative_to_crl_issuer);
+
+ ASSERT_TRUE(dp.reasons);
+ const uint8_t kExpectedReasons[] = {0x07, 0x9f, 0x80};
+ EXPECT_EQ(der::Input(kExpectedReasons), *dp.reasons);
+
+ EXPECT_FALSE(dp.crl_issuer);
+ }
+}
+
+TEST_F(ParseCrlDistributionPointsTest, NoDistributionPointName) {
+ // SEQUENCE {
+ // SEQUENCE {
+ // # cRLIssuer
+ // [2] {
+ // [4] {
+ // SEQUENCE {
+ // SET {
+ // SEQUENCE {
+ // # organizationUnitName
+ // OBJECT_IDENTIFIER { 2.5.4.11 }
+ // PrintableString { "crl issuer" }
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ const uint8_t kInputDer[] = {0x30, 0x1d, 0x30, 0x1b, 0xa2, 0x19, 0xa4, 0x17,
+ 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x63, 0x72, 0x6c,
+ 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72};
+
+ std::vector<ParsedDistributionPoint> dps;
+ ASSERT_TRUE(ParseCrlDistributionPoints(der::Input(kInputDer), &dps));
+ ASSERT_EQ(1u, dps.size());
+ const ParsedDistributionPoint& dp = dps[0];
+ EXPECT_FALSE(dp.distribution_point_fullname);
+
+ EXPECT_FALSE(dp.distribution_point_name_relative_to_crl_issuer);
+
+ EXPECT_FALSE(dp.reasons);
+
+ ASSERT_TRUE(dp.crl_issuer);
+ const uint8_t kExpectedDer[] = {0xa4, 0x17, 0x30, 0x15, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x0a, 0x63, 0x72, 0x6c, 0x20, 0x69, 0x73,
+ 0x73, 0x75, 0x65, 0x72};
+ EXPECT_EQ(der::Input(kExpectedDer), *dp.crl_issuer);
+}
+
+TEST_F(ParseCrlDistributionPointsTest, OnlyReasons) {
+ // SEQUENCE {
+ // SEQUENCE {
+ // # reasons
+ // [1 PRIMITIVE] { b`011` }
+ // }
+ // }
+ const uint8_t kInputDer[] = {0x30, 0x06, 0x30, 0x04, 0x81, 0x02, 0x05, 0x60};
+
+ std::vector<ParsedDistributionPoint> dps;
+ EXPECT_FALSE(ParseCrlDistributionPoints(der::Input(kInputDer), &dps));
+}
+
+TEST_F(ParseCrlDistributionPointsTest, EmptyDistributionPoint) {
+ // SEQUENCE {
+ // SEQUENCE {
+ // }
+ // }
+ const uint8_t kInputDer[] = {0x30, 0x02, 0x30, 0x00};
+
+ std::vector<ParsedDistributionPoint> dps;
+ EXPECT_FALSE(ParseCrlDistributionPoints(der::Input(kInputDer), &dps));
+}
+
+TEST_F(ParseCrlDistributionPointsTest, EmptyDistributionPoints) {
+ // SEQUENCE { }
+ const uint8_t kInputDer[] = {0x30, 0x00};
+
+ std::vector<ParsedDistributionPoint> dps;
+ EXPECT_FALSE(ParseCrlDistributionPoints(der::Input(kInputDer), &dps));
+}
+
+bool ParseAuthorityKeyIdentifierTestData(
+ const char* file_name,
+ std::string* backing_bytes,
+ ParsedAuthorityKeyIdentifier* authority_key_identifier) {
+ // Read the test file.
+ const PemBlockMapping mappings[] = {
+ {"AUTHORITY_KEY_IDENTIFIER", backing_bytes},
+ };
+ std::string test_file_path =
+ std::string(
+ "net/data/parse_certificate_unittest/authority_key_identifier/") +
+ file_name;
+ EXPECT_TRUE(ReadTestDataFromPemFile(test_file_path, mappings));
+
+ return ParseAuthorityKeyIdentifier(der::Input(backing_bytes),
+ authority_key_identifier);
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, EmptyInput) {
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ EXPECT_FALSE(
+ ParseAuthorityKeyIdentifier(der::Input(), &authority_key_identifier));
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, EmptySequence) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ // TODO(mattm): should this be an error? RFC 5280 doesn't explicitly say it.
+ ASSERT_TRUE(ParseAuthorityKeyIdentifierTestData(
+ "empty_sequence.pem", &backing_bytes, &authority_key_identifier));
+
+ EXPECT_FALSE(authority_key_identifier.key_identifier);
+ EXPECT_FALSE(authority_key_identifier.authority_cert_issuer);
+ EXPECT_FALSE(authority_key_identifier.authority_cert_serial_number);
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, KeyIdentifier) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ ASSERT_TRUE(ParseAuthorityKeyIdentifierTestData(
+ "key_identifier.pem", &backing_bytes, &authority_key_identifier));
+
+ ASSERT_TRUE(authority_key_identifier.key_identifier);
+ const uint8_t kExpectedValue[] = {0xDE, 0xAD, 0xB0, 0x0F};
+ EXPECT_EQ(der::Input(kExpectedValue),
+ authority_key_identifier.key_identifier);
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, IssuerAndSerial) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ ASSERT_TRUE(ParseAuthorityKeyIdentifierTestData(
+ "issuer_and_serial.pem", &backing_bytes, &authority_key_identifier));
+
+ EXPECT_FALSE(authority_key_identifier.key_identifier);
+
+ ASSERT_TRUE(authority_key_identifier.authority_cert_issuer);
+ const uint8_t kExpectedIssuer[] = {0xa4, 0x11, 0x30, 0x0f, 0x31, 0x0d, 0x30,
+ 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x04, 0x52, 0x6f, 0x6f, 0x74};
+ EXPECT_EQ(der::Input(kExpectedIssuer),
+ authority_key_identifier.authority_cert_issuer);
+
+ ASSERT_TRUE(authority_key_identifier.authority_cert_serial_number);
+ const uint8_t kExpectedSerial[] = {0x27, 0x4F};
+ EXPECT_EQ(der::Input(kExpectedSerial),
+ authority_key_identifier.authority_cert_serial_number);
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, KeyIdentifierAndIssuerAndSerial) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ ASSERT_TRUE(ParseAuthorityKeyIdentifierTestData(
+ "key_identifier_and_issuer_and_serial.pem", &backing_bytes,
+ &authority_key_identifier));
+
+ ASSERT_TRUE(authority_key_identifier.key_identifier);
+ const uint8_t kExpectedValue[] = {0xDE, 0xAD, 0xB0, 0x0F};
+ EXPECT_EQ(der::Input(kExpectedValue),
+ authority_key_identifier.key_identifier);
+
+ ASSERT_TRUE(authority_key_identifier.authority_cert_issuer);
+ const uint8_t kExpectedIssuer[] = {0xa4, 0x11, 0x30, 0x0f, 0x31, 0x0d, 0x30,
+ 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x04, 0x52, 0x6f, 0x6f, 0x74};
+ EXPECT_EQ(der::Input(kExpectedIssuer),
+ authority_key_identifier.authority_cert_issuer);
+
+ ASSERT_TRUE(authority_key_identifier.authority_cert_serial_number);
+ const uint8_t kExpectedSerial[] = {0x27, 0x4F};
+ EXPECT_EQ(der::Input(kExpectedSerial),
+ authority_key_identifier.authority_cert_serial_number);
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, IssuerOnly) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ EXPECT_FALSE(ParseAuthorityKeyIdentifierTestData(
+ "issuer_only.pem", &backing_bytes, &authority_key_identifier));
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, SerialOnly) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ EXPECT_FALSE(ParseAuthorityKeyIdentifierTestData(
+ "serial_only.pem", &backing_bytes, &authority_key_identifier));
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, InvalidContents) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ EXPECT_FALSE(ParseAuthorityKeyIdentifierTestData(
+ "invalid_contents.pem", &backing_bytes, &authority_key_identifier));
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, InvalidKeyIdentifier) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ EXPECT_FALSE(ParseAuthorityKeyIdentifierTestData(
+ "invalid_key_identifier.pem", &backing_bytes, &authority_key_identifier));
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, InvalidIssuer) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ EXPECT_FALSE(ParseAuthorityKeyIdentifierTestData(
+ "invalid_issuer.pem", &backing_bytes, &authority_key_identifier));
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, InvalidSerial) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ EXPECT_FALSE(ParseAuthorityKeyIdentifierTestData(
+ "invalid_serial.pem", &backing_bytes, &authority_key_identifier));
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, ExtraContentsAfterIssuerAndSerial) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ EXPECT_FALSE(ParseAuthorityKeyIdentifierTestData(
+ "extra_contents_after_issuer_and_serial.pem", &backing_bytes,
+ &authority_key_identifier));
+}
+
+TEST(ParseAuthorityKeyIdentifierTest, ExtraContentsAfterExtensionSequence) {
+ std::string backing_bytes;
+ ParsedAuthorityKeyIdentifier authority_key_identifier;
+ EXPECT_FALSE(ParseAuthorityKeyIdentifierTestData(
+ "extra_contents_after_extension_sequence.pem", &backing_bytes,
+ &authority_key_identifier));
+}
+
+TEST(ParseSubjectKeyIdentifierTest, EmptyInput) {
+ der::Input subject_key_identifier;
+ EXPECT_FALSE(
+ ParseSubjectKeyIdentifier(der::Input(), &subject_key_identifier));
+}
+
+TEST(ParseSubjectKeyIdentifierTest, Valid) {
+ // OCTET_STRING {`abcd`}
+ const uint8_t kInput[] = {0x04, 0x02, 0xab, 0xcd};
+ const uint8_t kExpected[] = {0xab, 0xcd};
+ der::Input subject_key_identifier;
+ EXPECT_TRUE(
+ ParseSubjectKeyIdentifier(der::Input(kInput), &subject_key_identifier));
+ EXPECT_EQ(der::Input(kExpected), subject_key_identifier);
+}
+
+TEST(ParseSubjectKeyIdentifierTest, ExtraData) {
+ // OCTET_STRING {`abcd`}
+ // NULL
+ const uint8_t kInput[] = {0x04, 0x02, 0xab, 0xcd, 0x05};
+ der::Input subject_key_identifier;
+ EXPECT_FALSE(
+ ParseSubjectKeyIdentifier(der::Input(kInput), &subject_key_identifier));
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/pki/parse_name.cc b/chromium/net/cert/pki/parse_name.cc
new file mode 100644
index 00000000000..5cd4516890c
--- /dev/null
+++ b/chromium/net/cert/pki/parse_name.cc
@@ -0,0 +1,222 @@
+// Copyright 2016 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 "net/cert/pki/parse_name.h"
+
+#include "base/check_op.h"
+#include "base/notreached.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/der/parse_values.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "third_party/boringssl/src/include/openssl/mem.h"
+
+namespace net {
+
+namespace {
+
+// Returns a string containing the dotted numeric form of |oid|, or an empty
+// string on error.
+std::string OidToString(der::Input oid) {
+ CBS cbs;
+ CBS_init(&cbs, oid.UnsafeData(), oid.Length());
+ bssl::UniquePtr<char> text(CBS_asn1_oid_to_text(&cbs));
+ if (!text)
+ return std::string();
+ return text.get();
+}
+
+} // namespace
+
+bool X509NameAttribute::ValueAsString(std::string* out) const {
+ switch (value_tag) {
+ case der::kTeletexString:
+ return der::ParseTeletexStringAsLatin1(value, out);
+ case der::kIA5String:
+ return der::ParseIA5String(value, out);
+ case der::kPrintableString:
+ return der::ParsePrintableString(value, out);
+ case der::kUtf8String:
+ *out = value.AsString();
+ return true;
+ case der::kUniversalString:
+ return der::ParseUniversalString(value, out);
+ case der::kBmpString:
+ return der::ParseBmpString(value, out);
+ default:
+ return false;
+ }
+}
+
+bool X509NameAttribute::ValueAsStringWithUnsafeOptions(
+ PrintableStringHandling printable_string_handling,
+ std::string* out) const {
+ if (printable_string_handling == PrintableStringHandling::kAsUTF8Hack &&
+ value_tag == der::kPrintableString) {
+ *out = value.AsString();
+ return true;
+ }
+ return ValueAsString(out);
+}
+
+bool X509NameAttribute::ValueAsStringUnsafe(std::string* out) const {
+ switch (value_tag) {
+ case der::kIA5String:
+ case der::kPrintableString:
+ case der::kTeletexString:
+ case der::kUtf8String:
+ *out = value.AsString();
+ return true;
+ case der::kUniversalString:
+ return der::ParseUniversalString(value, out);
+ case der::kBmpString:
+ return der::ParseBmpString(value, out);
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool X509NameAttribute::AsRFC2253String(std::string* out) const {
+ std::string type_string;
+ std::string value_string;
+ // TODO(mattm): Add streetAddress and domainComponent here?
+ if (type == der::Input(kTypeCommonNameOid)) {
+ type_string = "CN";
+ } else if (type == der::Input(kTypeSurnameOid)) {
+ type_string = "SN";
+ } else if (type == der::Input(kTypeCountryNameOid)) {
+ type_string = "C";
+ } else if (type == der::Input(kTypeLocalityNameOid)) {
+ type_string = "L";
+ } else if (type == der::Input(kTypeStateOrProvinceNameOid)) {
+ type_string = "ST";
+ } else if (type == der::Input(kTypeOrganizationNameOid)) {
+ type_string = "O";
+ } else if (type == der::Input(kTypeOrganizationUnitNameOid)) {
+ type_string = "OU";
+ } else if (type == der::Input(kTypeGivenNameOid)) {
+ type_string = "givenName";
+ } else if (type == der::Input(kTypeEmailAddressOid)) {
+ type_string = "emailAddress";
+ } else {
+ type_string = OidToString(type);
+ if (type_string.empty())
+ return false;
+ value_string = "#" + base::HexEncode(value.UnsafeData(), value.Length());
+ }
+
+ if (value_string.empty()) {
+ std::string unescaped;
+ if (!ValueAsStringUnsafe(&unescaped))
+ return false;
+
+ bool nonprintable = false;
+ for (unsigned int i = 0; i < unescaped.length(); ++i) {
+ unsigned char c = static_cast<unsigned char>(unescaped[i]);
+ if (i == 0 && c == '#') {
+ value_string += "\\#";
+ } else if (i == 0 && c == ' ') {
+ value_string += "\\ ";
+ } else if (i == unescaped.length() - 1 && c == ' ') {
+ value_string += "\\ ";
+ } else if (c == ',' || c == '+' || c == '"' || c == '\\' || c == '<' ||
+ c == '>' || c == ';') {
+ value_string += "\\";
+ value_string += c;
+ } else if (c < 32 || c > 126) {
+ nonprintable = true;
+ std::string h;
+ h += c;
+ value_string += "\\" + base::HexEncode(h.data(), h.length());
+ } else {
+ value_string += c;
+ }
+ }
+
+ // If we have non-printable characters in a TeletexString, we hex encode
+ // since we don't handle Teletex control codes.
+ if (nonprintable && value_tag == der::kTeletexString)
+ value_string = "#" + base::HexEncode(value.UnsafeData(), value.Length());
+ }
+
+ *out = type_string + "=" + value_string;
+ return true;
+}
+
+bool ReadRdn(der::Parser* parser, RelativeDistinguishedName* out) {
+ while (parser->HasMore()) {
+ der::Parser attr_type_and_value;
+ if (!parser->ReadSequence(&attr_type_and_value))
+ return false;
+ // Read the attribute type, which must be an OBJECT IDENTIFIER.
+ der::Input type;
+ if (!attr_type_and_value.ReadTag(der::kOid, &type))
+ return false;
+
+ // Read the attribute value.
+ der::Tag tag;
+ der::Input value;
+ if (!attr_type_and_value.ReadTagAndValue(&tag, &value))
+ return false;
+
+ // There should be no more elements in the sequence after reading the
+ // attribute type and value.
+ if (attr_type_and_value.HasMore())
+ return false;
+
+ out->push_back(X509NameAttribute(type, tag, value));
+ }
+
+ // RFC 5280 section 4.1.2.4
+ // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
+ return out->size() != 0;
+}
+
+bool ParseName(const der::Input& name_tlv, RDNSequence* out) {
+ der::Parser name_parser(name_tlv);
+ der::Input name_value;
+ if (!name_parser.ReadTag(der::kSequence, &name_value))
+ return false;
+ return ParseNameValue(name_value, out);
+}
+
+bool ParseNameValue(const der::Input& name_value, RDNSequence* out) {
+ der::Parser rdn_sequence_parser(name_value);
+ while (rdn_sequence_parser.HasMore()) {
+ der::Parser rdn_parser;
+ if (!rdn_sequence_parser.ReadConstructed(der::kSet, &rdn_parser))
+ return false;
+ RelativeDistinguishedName type_and_values;
+ if (!ReadRdn(&rdn_parser, &type_and_values))
+ return false;
+ out->push_back(type_and_values);
+ }
+
+ return true;
+}
+
+bool ConvertToRFC2253(const RDNSequence& rdn_sequence, std::string* out) {
+ std::string rdns_string;
+ size_t size = rdn_sequence.size();
+ for (size_t i = 0; i < size; ++i) {
+ RelativeDistinguishedName rdn = rdn_sequence[size - i - 1];
+ std::string rdn_string;
+ for (const auto& atv : rdn) {
+ if (!rdn_string.empty())
+ rdn_string += "+";
+ std::string atv_string;
+ if (!atv.AsRFC2253String(&atv_string))
+ return false;
+ rdn_string += atv_string;
+ }
+ if (!rdns_string.empty())
+ rdns_string += ",";
+ rdns_string += rdn_string;
+ }
+
+ *out = rdns_string;
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/parse_name.h b/chromium/net/cert/pki/parse_name.h
new file mode 100644
index 00000000000..e44833a9b30
--- /dev/null
+++ b/chromium/net/cert/pki/parse_name.h
@@ -0,0 +1,157 @@
+// Copyright 2016 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 NET_CERT_PKI_PARSE_NAME_H_
+#define NET_CERT_PKI_PARSE_NAME_H_
+
+#include <vector>
+
+#include "net/base/net_export.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "net/der/tag.h"
+
+namespace net {
+
+// id-at-commonName: 2.5.4.3 (RFC 5280)
+inline constexpr uint8_t kTypeCommonNameOid[] = {0x55, 0x04, 0x03};
+// id-at-surname: 2.5.4.4 (RFC 5280)
+inline constexpr uint8_t kTypeSurnameOid[] = {0x55, 0x04, 0x04};
+// id-at-serialNumber: 2.5.4.5 (RFC 5280)
+inline constexpr uint8_t kTypeSerialNumberOid[] = {0x55, 0x04, 0x05};
+// id-at-countryName: 2.5.4.6 (RFC 5280)
+inline constexpr uint8_t kTypeCountryNameOid[] = {0x55, 0x04, 0x06};
+// id-at-localityName: 2.5.4.7 (RFC 5280)
+inline constexpr uint8_t kTypeLocalityNameOid[] = {0x55, 0x04, 0x07};
+// id-at-stateOrProvinceName: 2.5.4.8 (RFC 5280)
+inline constexpr uint8_t kTypeStateOrProvinceNameOid[] = {0x55, 0x04, 0x08};
+// street (streetAddress): 2.5.4.9 (RFC 4519)
+inline constexpr uint8_t kTypeStreetAddressOid[] = {0x55, 0x04, 0x09};
+// id-at-organizationName: 2.5.4.10 (RFC 5280)
+inline constexpr uint8_t kTypeOrganizationNameOid[] = {0x55, 0x04, 0x0a};
+// id-at-organizationalUnitName: 2.5.4.11 (RFC 5280)
+inline constexpr uint8_t kTypeOrganizationUnitNameOid[] = {0x55, 0x04, 0x0b};
+// id-at-title: 2.5.4.12 (RFC 5280)
+inline constexpr uint8_t kTypeTitleOid[] = {0x55, 0x04, 0x0c};
+// id-at-name: 2.5.4.41 (RFC 5280)
+inline constexpr uint8_t kTypeNameOid[] = {0x55, 0x04, 0x29};
+// id-at-givenName: 2.5.4.42 (RFC 5280)
+inline constexpr uint8_t kTypeGivenNameOid[] = {0x55, 0x04, 0x2a};
+// id-at-initials: 2.5.4.43 (RFC 5280)
+inline constexpr uint8_t kTypeInitialsOid[] = {0x55, 0x04, 0x2b};
+// id-at-generationQualifier: 2.5.4.44 (RFC 5280)
+inline constexpr uint8_t kTypeGenerationQualifierOid[] = {0x55, 0x04, 0x2c};
+// dc (domainComponent): 0.9.2342.19200300.100.1.25 (RFC 4519)
+inline constexpr uint8_t kTypeDomainComponentOid[] = {
+ 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19};
+// RFC 5280 section A.1:
+//
+// pkcs-9 OBJECT IDENTIFIER ::=
+// { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
+//
+// id-emailAddress AttributeType ::= { pkcs-9 1 }
+//
+// In dotted form: 1.2.840.113549.1.9.1
+inline constexpr uint8_t kTypeEmailAddressOid[] = {0x2A, 0x86, 0x48, 0x86, 0xF7,
+ 0x0D, 0x01, 0x09, 0x01};
+
+// X509NameAttribute contains a representation of a DER-encoded RFC 2253
+// "AttributeTypeAndValue".
+//
+// AttributeTypeAndValue ::= SEQUENCE {
+// type AttributeType,
+// value AttributeValue
+// }
+struct NET_EXPORT X509NameAttribute {
+ X509NameAttribute(der::Input in_type,
+ der::Tag in_value_tag,
+ der::Input in_value)
+ : type(in_type), value_tag(in_value_tag), value(in_value) {}
+
+ // Configures handling of PrintableString in the attribute value. Do
+ // not use non-default handling without consulting //net owners. With
+ // kAsUTF8Hack, PrintableStrings are interpreted as UTF-8 strings.
+ enum class PrintableStringHandling { kDefault, kAsUTF8Hack };
+
+ // Attempts to convert the value represented by this struct into a
+ // UTF-8 string and store it in |out|, returning whether the conversion
+ // was successful.
+ [[nodiscard]] bool ValueAsString(std::string* out) const;
+
+ // Attempts to convert the value represented by this struct into a
+ // UTF-8 string and store it in |out|, returning whether the conversion
+ // was successful. Allows configuring some non-standard string handling
+ // options.
+ //
+ // Do not use without consulting //net owners.
+ [[nodiscard]] bool ValueAsStringWithUnsafeOptions(
+ PrintableStringHandling printable_string_handling,
+ std::string* out) const;
+
+ // Attempts to convert the value represented by this struct into a
+ // std::string and store it in |out|, returning whether the conversion was
+ // successful. Due to some encodings being incompatible, the caller must
+ // verify the attribute |value_tag|.
+ //
+ // Note: Don't use this function unless you know what you're doing. Use
+ // ValueAsString instead.
+ //
+ // Note: The conversion doesn't verify that the value corresponds to the
+ // ASN.1 definition of the value type.
+ [[nodiscard]] bool ValueAsStringUnsafe(std::string* out) const;
+
+ // Formats the NameAttribute per RFC2253 into an ASCII string and stores
+ // the result in |out|, returning whether the conversion was successful.
+ [[nodiscard]] bool AsRFC2253String(std::string* out) const;
+
+ der::Input type;
+ der::Tag value_tag;
+ der::Input value;
+};
+
+typedef std::vector<X509NameAttribute> RelativeDistinguishedName;
+typedef std::vector<RelativeDistinguishedName> RDNSequence;
+
+// Parses all the ASN.1 AttributeTypeAndValue elements in |parser| and stores
+// each as an AttributeTypeAndValue object in |out|.
+//
+// AttributeTypeAndValue is defined in RFC 5280 section 4.1.2.4:
+//
+// AttributeTypeAndValue ::= SEQUENCE {
+// type AttributeType,
+// value AttributeValue }
+//
+// AttributeType ::= OBJECT IDENTIFIER
+//
+// AttributeValue ::= ANY -- DEFINED BY AttributeType
+//
+// DirectoryString ::= CHOICE {
+// teletexString TeletexString (SIZE (1..MAX)),
+// printableString PrintableString (SIZE (1..MAX)),
+// universalString UniversalString (SIZE (1..MAX)),
+// utf8String UTF8String (SIZE (1..MAX)),
+// bmpString BMPString (SIZE (1..MAX)) }
+//
+// The type of the component AttributeValue is determined by the AttributeType;
+// in general it will be a DirectoryString.
+[[nodiscard]] NET_EXPORT bool ReadRdn(der::Parser* parser,
+ RelativeDistinguishedName* out);
+
+// Parses a DER-encoded "Name" as specified by 5280. Returns true on success
+// and sets the results in |out|.
+[[nodiscard]] NET_EXPORT bool ParseName(const der::Input& name_tlv,
+ RDNSequence* out);
+// Parses a DER-encoded "Name" value (without the sequence tag & length) as
+// specified by 5280. Returns true on success and sets the results in |out|.
+[[nodiscard]] NET_EXPORT bool ParseNameValue(const der::Input& name_value,
+ RDNSequence* out);
+
+// Formats a RDNSequence |rdn_sequence| per RFC2253 as an ASCII string and
+// stores the result into |out|, and returns whether the conversion was
+// successful.
+[[nodiscard]] NET_EXPORT bool ConvertToRFC2253(const RDNSequence& rdn_sequence,
+ std::string* out);
+} // namespace net
+
+#endif // NET_CERT_PKI_PARSE_NAME_H_
diff --git a/chromium/net/cert/pki/parse_name_unittest.cc b/chromium/net/cert/pki/parse_name_unittest.cc
new file mode 100644
index 00000000000..3e29b808c4e
--- /dev/null
+++ b/chromium/net/cert/pki/parse_name_unittest.cc
@@ -0,0 +1,361 @@
+// Copyright 2016 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 "net/cert/pki/parse_name.h"
+
+#include "net/cert/pki/test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+// Loads test data from file. The filename is constructed from the parameters:
+// |prefix| describes the type of data being tested, e.g. "ascii",
+// "unicode_bmp", "unicode_supplementary", and "invalid".
+// |value_type| indicates what ASN.1 type is used to encode the data.
+// |suffix| indicates any additional modifications, such as caseswapping,
+// whitespace adding, etc.
+::testing::AssertionResult LoadTestData(const std::string& prefix,
+ const std::string& value_type,
+ const std::string& suffix,
+ std::string* result) {
+ std::string path = "net/data/verify_name_match_unittest/names/" + prefix +
+ "-" + value_type + "-" + suffix + ".pem";
+
+ const PemBlockMapping mappings[] = {
+ {"NAME", result},
+ };
+
+ return ReadTestDataFromPemFile(path, mappings);
+}
+
+} // anonymous namespace
+
+TEST(ParseNameTest, IA5SafeStringValue) {
+ const uint8_t der[] = {
+ 0x46, 0x6f, 0x6f, 0x20, 0x62, 0x61, 0x72,
+ };
+ X509NameAttribute value(der::Input(), der::kIA5String, der::Input(der));
+ std::string result_unsafe;
+ ASSERT_TRUE(value.ValueAsStringUnsafe(&result_unsafe));
+ ASSERT_EQ("Foo bar", result_unsafe);
+ std::string result;
+ ASSERT_TRUE(value.ValueAsString(&result));
+ ASSERT_EQ("Foo bar", result);
+}
+
+TEST(ParseNameTest, IA5UnsafeStringValue) {
+ const uint8_t der[] = {
+ 0x46, 0x6f, 0xFF, 0x20, 0x62, 0x61, 0x72,
+ };
+ X509NameAttribute value(der::Input(), der::kIA5String, der::Input(der));
+ std::string result_unsafe;
+ ASSERT_TRUE(value.ValueAsStringUnsafe(&result_unsafe));
+ ASSERT_EQ("Fo\377 bar", result_unsafe);
+ std::string result;
+ ASSERT_FALSE(value.ValueAsString(&result));
+}
+
+TEST(ParseNameTest, PrintableSafeStringValue) {
+ const uint8_t der[] = {
+ 0x46, 0x6f, 0x6f, 0x20, 0x62, 0x61, 0x72,
+ };
+ X509NameAttribute value(der::Input(), der::kPrintableString, der::Input(der));
+ std::string result_unsafe;
+ ASSERT_TRUE(value.ValueAsStringUnsafe(&result_unsafe));
+ ASSERT_EQ("Foo bar", result_unsafe);
+ std::string result;
+ ASSERT_TRUE(value.ValueAsString(&result));
+ ASSERT_EQ("Foo bar", result);
+}
+
+TEST(ParseNameTest, PrintableUnsafeStringValue) {
+ const uint8_t der[] = {
+ 0x46, 0x6f, 0x5f, 0x20, 0x62, 0x61, 0x72,
+ };
+ X509NameAttribute value(der::Input(), der::kPrintableString, der::Input(der));
+ std::string result_unsafe;
+ ASSERT_TRUE(value.ValueAsStringUnsafe(&result_unsafe));
+ ASSERT_EQ("Fo_ bar", result_unsafe);
+ std::string result;
+ ASSERT_FALSE(value.ValueAsString(&result));
+}
+
+TEST(ParseNameTest, PrintableStringUnsafeOptions) {
+ const uint8_t der[] = {
+ 0x46, 0x6f, 0x5f, 0x20, 0x62, 0x61, 0x72,
+ };
+ X509NameAttribute value(der::Input(), der::kPrintableString, der::Input(der));
+ std::string result;
+ ASSERT_FALSE(value.ValueAsStringWithUnsafeOptions(
+ X509NameAttribute::PrintableStringHandling::kDefault, &result));
+ ASSERT_TRUE(value.ValueAsStringWithUnsafeOptions(
+ X509NameAttribute::PrintableStringHandling::kAsUTF8Hack, &result));
+ ASSERT_EQ("Fo_ bar", result);
+}
+
+TEST(ParseNameTest, TeletexSafeStringValue) {
+ const uint8_t der[] = {
+ 0x46, 0x6f, 0x6f, 0x20, 0x62, 0x61, 0x72,
+ };
+ X509NameAttribute value(der::Input(), der::kTeletexString, der::Input(der));
+ std::string result_unsafe;
+ ASSERT_TRUE(value.ValueAsStringUnsafe(&result_unsafe));
+ ASSERT_EQ("Foo bar", result_unsafe);
+ std::string result;
+ ASSERT_TRUE(value.ValueAsString(&result));
+ ASSERT_EQ("Foo bar", result);
+}
+
+TEST(ParseNameTest, TeletexLatin1StringValue) {
+ const uint8_t der[] = {
+ 0x46, 0x6f, 0xd6, 0x20, 0x62, 0x61, 0x72,
+ };
+ X509NameAttribute value(der::Input(), der::kTeletexString, der::Input(der));
+ std::string result_unsafe;
+ ASSERT_TRUE(value.ValueAsStringUnsafe(&result_unsafe));
+ ASSERT_EQ("Fo\xd6 bar", result_unsafe);
+ std::string result;
+ ASSERT_TRUE(value.ValueAsString(&result));
+ ASSERT_EQ("FoÖ bar", result);
+}
+
+TEST(ParseNameTest, ConvertBmpString) {
+ const uint8_t der[] = {
+ 0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x61, 0x00, 0x72,
+ };
+ X509NameAttribute value(der::Input(), der::kBmpString, der::Input(der));
+ std::string result_unsafe;
+ ASSERT_TRUE(value.ValueAsStringUnsafe(&result_unsafe));
+ ASSERT_EQ("foobar", result_unsafe);
+ std::string result;
+ ASSERT_TRUE(value.ValueAsString(&result));
+ ASSERT_EQ("foobar", result);
+}
+
+// BmpString must encode characters in pairs of 2 bytes.
+TEST(ParseNameTest, ConvertInvalidBmpString) {
+ const uint8_t der[] = {0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x72};
+ X509NameAttribute value(der::Input(), der::kBmpString, der::Input(der));
+ std::string result;
+ ASSERT_FALSE(value.ValueAsStringUnsafe(&result));
+ ASSERT_FALSE(value.ValueAsString(&result));
+}
+
+TEST(ParseNameTest, ConvertUniversalString) {
+ const uint8_t der[] = {0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x6f,
+ 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x62,
+ 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x72};
+ X509NameAttribute value(der::Input(), der::kUniversalString, der::Input(der));
+ std::string result_unsafe;
+ ASSERT_TRUE(value.ValueAsStringUnsafe(&result_unsafe));
+ ASSERT_EQ("foobar", result_unsafe);
+ std::string result;
+ ASSERT_TRUE(value.ValueAsString(&result));
+ ASSERT_EQ("foobar", result);
+}
+
+// UniversalString must encode characters in pairs of 4 bytes.
+TEST(ParseNameTest, ConvertInvalidUniversalString) {
+ const uint8_t der[] = {0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72};
+ X509NameAttribute value(der::Input(), der::kUniversalString, der::Input(der));
+ std::string result;
+ ASSERT_FALSE(value.ValueAsStringUnsafe(&result));
+ ASSERT_FALSE(value.ValueAsString(&result));
+}
+
+TEST(ParseNameTest, EmptyName) {
+ const uint8_t der[] = {0x30, 0x00};
+ der::Input rdn(der);
+ RDNSequence atv;
+ ASSERT_TRUE(ParseName(rdn, &atv));
+ ASSERT_EQ(0u, atv.size());
+}
+
+TEST(ParseNameTest, ValidName) {
+ const uint8_t der[] = {0x30, 0x3c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x14, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b, 0x47,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x0e, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+ 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41};
+ der::Input rdn(der);
+ RDNSequence atv;
+ ASSERT_TRUE(ParseName(rdn, &atv));
+ ASSERT_EQ(3u, atv.size());
+ ASSERT_EQ(1u, atv[0].size());
+ ASSERT_EQ(der::Input(kTypeCountryNameOid), atv[0][0].type);
+ ASSERT_EQ("US", atv[0][0].value.AsString());
+ ASSERT_EQ(1u, atv[1].size());
+ ASSERT_EQ(der::Input(kTypeOrganizationNameOid), atv[1][0].type);
+ ASSERT_EQ("Google Inc.", atv[1][0].value.AsString());
+ ASSERT_EQ(1u, atv[2].size());
+ ASSERT_EQ(der::Input(kTypeCommonNameOid), atv[2][0].type);
+ ASSERT_EQ("Google Test CA", atv[2][0].value.AsString());
+}
+
+TEST(ParseNameTest, InvalidNameExtraData) {
+ std::string invalid;
+ ASSERT_TRUE(
+ LoadTestData("invalid", "AttributeTypeAndValue", "extradata", &invalid));
+ RDNSequence atv;
+ ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv));
+}
+
+TEST(ParseNameTest, InvalidNameEmpty) {
+ std::string invalid;
+ ASSERT_TRUE(
+ LoadTestData("invalid", "AttributeTypeAndValue", "empty", &invalid));
+ RDNSequence atv;
+ ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv));
+}
+
+TEST(ParseNameTest, InvalidNameBadType) {
+ std::string invalid;
+ ASSERT_TRUE(LoadTestData("invalid", "AttributeTypeAndValue",
+ "badAttributeType", &invalid));
+ RDNSequence atv;
+ ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv));
+}
+
+TEST(ParseNameTest, InvalidNameNotSequence) {
+ std::string invalid;
+ ASSERT_TRUE(LoadTestData("invalid", "AttributeTypeAndValue", "setNotSequence",
+ &invalid));
+ RDNSequence atv;
+ ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv));
+}
+
+TEST(ParseNameTest, InvalidNameNotSet) {
+ std::string invalid;
+ ASSERT_TRUE(LoadTestData("invalid", "RDN", "sequenceInsteadOfSet", &invalid));
+ RDNSequence atv;
+ ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv));
+}
+
+TEST(ParseNameTest, InvalidNameEmptyRdn) {
+ std::string invalid;
+ ASSERT_TRUE(LoadTestData("invalid", "RDN", "empty", &invalid));
+ RDNSequence atv;
+ ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv));
+}
+
+TEST(ParseNameTest, RFC2253FormatBasic) {
+ const uint8_t der[] = {0x30, 0x3b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x16, 0x30,
+ 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x49,
+ 0x73, 0x6f, 0x64, 0x65, 0x20, 0x4c, 0x69, 0x6d, 0x69,
+ 0x74, 0x65, 0x64, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x0b, 0x53, 0x74, 0x65, 0x76,
+ 0x65, 0x20, 0x4b, 0x69, 0x6c, 0x6c, 0x65};
+ der::Input rdn_input(der);
+ RDNSequence rdn;
+ ASSERT_TRUE(ParseName(rdn_input, &rdn));
+ std::string output;
+ ASSERT_TRUE(ConvertToRFC2253(rdn, &output));
+ ASSERT_EQ("CN=Steve Kille,O=Isode Limited,C=GB", output);
+}
+
+TEST(ParseNameTest, RFC2253FormatMultiRDN) {
+ const uint8_t der[] = {
+ 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0b, 0x57, 0x69, 0x64, 0x67, 0x65, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x1f, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x05,
+ 0x53, 0x61, 0x6c, 0x65, 0x73, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x08, 0x4a, 0x2e, 0x20, 0x53, 0x6d, 0x69, 0x74, 0x68};
+ der::Input rdn_input(der);
+ RDNSequence rdn;
+ ASSERT_TRUE(ParseName(rdn_input, &rdn));
+ std::string output;
+ ASSERT_TRUE(ConvertToRFC2253(rdn, &output));
+ ASSERT_EQ("OU=Sales+CN=J. Smith,O=Widget Inc.,C=US", output);
+}
+
+TEST(ParseNameTest, RFC2253FormatQuoted) {
+ const uint8_t der[] = {
+ 0x30, 0x40, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x47, 0x42, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x15, 0x53, 0x75, 0x65, 0x2c, 0x20, 0x47, 0x72,
+ 0x61, 0x62, 0x62, 0x69, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x52,
+ 0x75, 0x6e, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x08, 0x4c, 0x2e, 0x20, 0x45, 0x61, 0x67, 0x6c, 0x65};
+ der::Input rdn_input(der);
+ RDNSequence rdn;
+ ASSERT_TRUE(ParseName(rdn_input, &rdn));
+ std::string output;
+ ASSERT_TRUE(ConvertToRFC2253(rdn, &output));
+ ASSERT_EQ("CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB", output);
+}
+
+TEST(ParseNameTest, RFC2253FormatNonPrintable) {
+ const uint8_t der[] = {0x30, 0x33, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x0d, 0x30,
+ 0x0b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x04, 0x54,
+ 0x65, 0x73, 0x74, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x0c, 0x42, 0x65, 0x66, 0x6f,
+ 0x72, 0x65, 0x0d, 0x41, 0x66, 0x74, 0x65, 0x72};
+ der::Input rdn_input(der);
+ RDNSequence rdn;
+ ASSERT_TRUE(ParseName(rdn_input, &rdn));
+ std::string output;
+ ASSERT_TRUE(ConvertToRFC2253(rdn, &output));
+ ASSERT_EQ("CN=Before\\0DAfter,O=Test,C=GB", output);
+}
+
+TEST(ParseNameTest, RFC2253FormatUnknownOid) {
+ const uint8_t der[] = {0x30, 0x30, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x0d, 0x30,
+ 0x0b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x04, 0x54,
+ 0x65, 0x73, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x8b, 0x3a, 0x00, 0x13,
+ 0x04, 0x04, 0x02, 0x48, 0x69};
+ der::Input rdn_input(der);
+ RDNSequence rdn;
+ ASSERT_TRUE(ParseName(rdn_input, &rdn));
+ std::string output;
+ ASSERT_TRUE(ConvertToRFC2253(rdn, &output));
+ ASSERT_EQ("1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB", output);
+}
+
+TEST(ParseNameTest, RFC2253FormatLargeOid) {
+ const uint8_t der[] = {0x30, 0x16, 0x31, 0x14, 0x30, 0x12, 0x06, 0x0a,
+ 0x81, 0x0d, 0x06, 0x01, 0x99, 0x21, 0x01, 0x8b,
+ 0x3a, 0x00, 0x13, 0x04, 0x74, 0x65, 0x73, 0x74};
+ der::Input rdn_input(der);
+ RDNSequence rdn;
+ ASSERT_TRUE(ParseName(rdn_input, &rdn));
+ std::string output;
+ ASSERT_TRUE(ConvertToRFC2253(rdn, &output));
+ ASSERT_EQ("2.61.6.1.3233.1.1466.0=#74657374", output);
+}
+
+TEST(ParseNameTest, RFC2253FormatInvalidOid) {
+ // Same DER as RFC2253FormatLargeOid but with the last byte of the OID
+ // replaced with 0x80, which ends the OID with a truncated multi-byte
+ // component.
+ const uint8_t der[] = {0x30, 0x16, 0x31, 0x14, 0x30, 0x12, 0x06, 0x0a,
+ 0x81, 0x0d, 0x06, 0x01, 0x99, 0x21, 0x01, 0x8b,
+ 0x3a, 0x80, 0x13, 0x04, 0x74, 0x65, 0x73, 0x74};
+ der::Input rdn_input(der);
+ RDNSequence rdn;
+ ASSERT_TRUE(ParseName(rdn_input, &rdn));
+ std::string output;
+ EXPECT_FALSE(ConvertToRFC2253(rdn, &output));
+}
+
+TEST(ParseNameTest, RFC2253FormatUTF8) {
+ const uint8_t der[] = {0x30, 0x12, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x04, 0x13, 0x07, 0x4c,
+ 0x75, 0xc4, 0x8d, 0x69, 0xc4, 0x87};
+ der::Input rdn_input(der);
+ RDNSequence rdn;
+ ASSERT_TRUE(ParseName(rdn_input, &rdn));
+ std::string output;
+ ASSERT_TRUE(ConvertToRFC2253(rdn, &output));
+ ASSERT_EQ("SN=Lu\\C4\\8Di\\C4\\87", output);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/parsed_certificate.cc b/chromium/net/cert/pki/parsed_certificate.cc
new file mode 100644
index 00000000000..a1268a127b6
--- /dev/null
+++ b/chromium/net/cert/pki/parsed_certificate.cc
@@ -0,0 +1,302 @@
+// Copyright 2016 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 "net/cert/pki/parsed_certificate.h"
+
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/certificate_policies.h"
+#include "net/cert/pki/extended_key_usage.h"
+#include "net/cert/pki/name_constraints.h"
+#include "net/cert/pki/signature_algorithm.h"
+#include "net/cert/pki/verify_name_match.h"
+#include "net/der/parser.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+
+namespace net {
+
+namespace {
+
+DEFINE_CERT_ERROR_ID(kFailedParsingCertificate, "Failed parsing Certificate");
+DEFINE_CERT_ERROR_ID(kFailedParsingTbsCertificate,
+ "Failed parsing TBSCertificate");
+DEFINE_CERT_ERROR_ID(kFailedParsingSignatureAlgorithm,
+ "Failed parsing SignatureAlgorithm");
+DEFINE_CERT_ERROR_ID(kFailedReadingIssuerOrSubject,
+ "Failed reading issuer or subject");
+DEFINE_CERT_ERROR_ID(kFailedNormalizingSubject, "Failed normalizing subject");
+DEFINE_CERT_ERROR_ID(kFailedNormalizingIssuer, "Failed normalizing issuer");
+DEFINE_CERT_ERROR_ID(kFailedParsingExtensions, "Failed parsing extensions");
+DEFINE_CERT_ERROR_ID(kFailedParsingBasicConstraints,
+ "Failed parsing basic constraints");
+DEFINE_CERT_ERROR_ID(kFailedParsingKeyUsage, "Failed parsing key usage");
+DEFINE_CERT_ERROR_ID(kFailedParsingEku, "Failed parsing extended key usage");
+DEFINE_CERT_ERROR_ID(kFailedParsingSubjectAltName,
+ "Failed parsing subjectAltName");
+DEFINE_CERT_ERROR_ID(kSubjectAltNameNotCritical,
+ "Empty subject and subjectAltName is not critical");
+DEFINE_CERT_ERROR_ID(kFailedParsingNameConstraints,
+ "Failed parsing name constraints");
+DEFINE_CERT_ERROR_ID(kFailedParsingAia, "Failed parsing authority info access");
+DEFINE_CERT_ERROR_ID(kFailedParsingPolicies,
+ "Failed parsing certificate policies");
+DEFINE_CERT_ERROR_ID(kFailedParsingPolicyConstraints,
+ "Failed parsing policy constraints");
+DEFINE_CERT_ERROR_ID(kFailedParsingPolicyMappings,
+ "Failed parsing policy mappings");
+DEFINE_CERT_ERROR_ID(kFailedParsingInhibitAnyPolicy,
+ "Failed parsing inhibit any policy");
+DEFINE_CERT_ERROR_ID(kFailedParsingAuthorityKeyIdentifier,
+ "Failed parsing authority key identifier");
+DEFINE_CERT_ERROR_ID(kFailedParsingSubjectKeyIdentifier,
+ "Failed parsing subject key identifier");
+
+[[nodiscard]] bool GetSequenceValue(const der::Input& tlv, der::Input* value) {
+ der::Parser parser(tlv);
+ return parser.ReadTag(der::kSequence, value) && !parser.HasMore();
+}
+
+} // namespace
+
+bool ParsedCertificate::GetExtension(const der::Input& extension_oid,
+ ParsedExtension* parsed_extension) const {
+ if (!tbs_.extensions_tlv)
+ return false;
+
+ auto it = extensions_.find(extension_oid);
+ if (it == extensions_.end()) {
+ *parsed_extension = ParsedExtension();
+ return false;
+ }
+
+ *parsed_extension = it->second;
+ return true;
+}
+
+ParsedCertificate::ParsedCertificate() = default;
+ParsedCertificate::~ParsedCertificate() = default;
+
+// static
+scoped_refptr<ParsedCertificate> ParsedCertificate::Create(
+ bssl::UniquePtr<CRYPTO_BUFFER> backing_data,
+ const ParseCertificateOptions& options,
+ CertErrors* errors) {
+ // |errors| is an optional parameter, but to keep the code simpler, use a
+ // dummy object when one wasn't provided.
+ CertErrors unused_errors;
+ if (!errors)
+ errors = &unused_errors;
+
+ auto result = base::WrapRefCounted(new ParsedCertificate);
+ result->cert_data_ = std::move(backing_data);
+ result->cert_ = der::Input(CRYPTO_BUFFER_data(result->cert_data_.get()),
+ CRYPTO_BUFFER_len(result->cert_data_.get()));
+
+ if (!ParseCertificate(result->cert_, &result->tbs_certificate_tlv_,
+ &result->signature_algorithm_tlv_,
+ &result->signature_value_, errors)) {
+ errors->AddError(kFailedParsingCertificate);
+ return nullptr;
+ }
+
+ if (!ParseTbsCertificate(result->tbs_certificate_tlv_, options, &result->tbs_,
+ errors)) {
+ errors->AddError(kFailedParsingTbsCertificate);
+ return nullptr;
+ }
+
+ // Attempt to parse the signature algorithm contained in the Certificate.
+ absl::optional<SignatureAlgorithm> sigalg =
+ ParseSignatureAlgorithm(result->signature_algorithm_tlv_, errors);
+ if (!sigalg) {
+ errors->AddError(kFailedParsingSignatureAlgorithm);
+ return nullptr;
+ }
+ result->signature_algorithm_ = *sigalg;
+
+ der::Input subject_value;
+ if (!GetSequenceValue(result->tbs_.subject_tlv, &subject_value)) {
+ errors->AddError(kFailedReadingIssuerOrSubject);
+ return nullptr;
+ }
+ if (!NormalizeName(subject_value, &result->normalized_subject_, errors)) {
+ errors->AddError(kFailedNormalizingSubject);
+ return nullptr;
+ }
+ der::Input issuer_value;
+ if (!GetSequenceValue(result->tbs_.issuer_tlv, &issuer_value)) {
+ errors->AddError(kFailedReadingIssuerOrSubject);
+ return nullptr;
+ }
+ if (!NormalizeName(issuer_value, &result->normalized_issuer_, errors)) {
+ errors->AddError(kFailedNormalizingIssuer);
+ return nullptr;
+ }
+
+ // Parse the standard X.509 extensions.
+ if (result->tbs_.extensions_tlv) {
+ // ParseExtensions() ensures there are no duplicates, and maps the (unique)
+ // OID to the extension value.
+ if (!ParseExtensions(result->tbs_.extensions_tlv.value(),
+ &result->extensions_)) {
+ errors->AddError(kFailedParsingExtensions);
+ return nullptr;
+ }
+
+ ParsedExtension extension;
+
+ // Basic constraints.
+ if (result->GetExtension(der::Input(kBasicConstraintsOid), &extension)) {
+ result->has_basic_constraints_ = true;
+ if (!ParseBasicConstraints(extension.value,
+ &result->basic_constraints_)) {
+ errors->AddError(kFailedParsingBasicConstraints);
+ return nullptr;
+ }
+ }
+
+ // Key Usage.
+ if (result->GetExtension(der::Input(kKeyUsageOid), &extension)) {
+ result->has_key_usage_ = true;
+ if (!ParseKeyUsage(extension.value, &result->key_usage_)) {
+ errors->AddError(kFailedParsingKeyUsage);
+ return nullptr;
+ }
+ }
+
+ // Extended Key Usage.
+ if (result->GetExtension(der::Input(kExtKeyUsageOid), &extension)) {
+ result->has_extended_key_usage_ = true;
+ if (!ParseEKUExtension(extension.value, &result->extended_key_usage_)) {
+ errors->AddError(kFailedParsingEku);
+ return nullptr;
+ }
+ }
+
+ // Subject alternative name.
+ if (result->GetExtension(der::Input(kSubjectAltNameOid),
+ &result->subject_alt_names_extension_)) {
+ // RFC 5280 section 4.2.1.6:
+ // SubjectAltName ::= GeneralNames
+ result->subject_alt_names_ = GeneralNames::Create(
+ result->subject_alt_names_extension_.value, errors);
+ if (!result->subject_alt_names_) {
+ errors->AddError(kFailedParsingSubjectAltName);
+ return nullptr;
+ }
+ // RFC 5280 section 4.1.2.6:
+ // If subject naming information is present only in the subjectAltName
+ // extension (e.g., a key bound only to an email address or URI), then the
+ // subject name MUST be an empty sequence and the subjectAltName extension
+ // MUST be critical.
+ if (subject_value.Length() == 0 &&
+ !result->subject_alt_names_extension_.critical) {
+ errors->AddError(kSubjectAltNameNotCritical);
+ return nullptr;
+ }
+ }
+
+ // Name constraints.
+ if (result->GetExtension(der::Input(kNameConstraintsOid), &extension)) {
+ result->name_constraints_ =
+ NameConstraints::Create(extension.value, extension.critical, errors);
+ if (!result->name_constraints_) {
+ errors->AddError(kFailedParsingNameConstraints);
+ return nullptr;
+ }
+ }
+
+ // Authority information access.
+ if (result->GetExtension(der::Input(kAuthorityInfoAccessOid),
+ &result->authority_info_access_extension_)) {
+ result->has_authority_info_access_ = true;
+ if (!ParseAuthorityInfoAccessURIs(
+ result->authority_info_access_extension_.value,
+ &result->ca_issuers_uris_, &result->ocsp_uris_)) {
+ errors->AddError(kFailedParsingAia);
+ return nullptr;
+ }
+ }
+
+ // Policies.
+ if (result->GetExtension(der::Input(kCertificatePoliciesOid), &extension)) {
+ result->has_policy_oids_ = true;
+ if (!ParseCertificatePoliciesExtensionOids(
+ extension.value, false /*fail_parsing_unknown_qualifier_oids*/,
+ &result->policy_oids_, errors)) {
+ errors->AddError(kFailedParsingPolicies);
+ return nullptr;
+ }
+ }
+
+ // Policy constraints.
+ if (result->GetExtension(der::Input(kPolicyConstraintsOid), &extension)) {
+ result->has_policy_constraints_ = true;
+ if (!ParsePolicyConstraints(extension.value,
+ &result->policy_constraints_)) {
+ errors->AddError(kFailedParsingPolicyConstraints);
+ return nullptr;
+ }
+ }
+
+ // Policy mappings.
+ if (result->GetExtension(der::Input(kPolicyMappingsOid), &extension)) {
+ result->has_policy_mappings_ = true;
+ if (!ParsePolicyMappings(extension.value, &result->policy_mappings_)) {
+ errors->AddError(kFailedParsingPolicyMappings);
+ return nullptr;
+ }
+ }
+
+ // Inhibit Any Policy.
+ if (result->GetExtension(der::Input(kInhibitAnyPolicyOid), &extension)) {
+ result->has_inhibit_any_policy_ = true;
+ if (!ParseInhibitAnyPolicy(extension.value,
+ &result->inhibit_any_policy_)) {
+ errors->AddError(kFailedParsingInhibitAnyPolicy);
+ return nullptr;
+ }
+ }
+
+ // Subject Key Identifier.
+ if (result->GetExtension(der::Input(kSubjectKeyIdentifierOid),
+ &extension)) {
+ result->subject_key_identifier_ = absl::make_optional<der::Input>();
+ if (!ParseSubjectKeyIdentifier(
+ extension.value, &result->subject_key_identifier_.value())) {
+ errors->AddError(kFailedParsingSubjectKeyIdentifier);
+ return nullptr;
+ }
+ }
+
+ // Authority Key Identifier.
+ if (result->GetExtension(der::Input(kAuthorityKeyIdentifierOid),
+ &extension)) {
+ result->authority_key_identifier_ =
+ absl::make_optional<ParsedAuthorityKeyIdentifier>();
+ if (!ParseAuthorityKeyIdentifier(
+ extension.value, &result->authority_key_identifier_.value())) {
+ errors->AddError(kFailedParsingAuthorityKeyIdentifier);
+ return nullptr;
+ }
+ }
+ }
+
+ return result;
+}
+
+// static
+bool ParsedCertificate::CreateAndAddToVector(
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_data,
+ const ParseCertificateOptions& options,
+ std::vector<scoped_refptr<net::ParsedCertificate>>* chain,
+ CertErrors* errors) {
+ scoped_refptr<ParsedCertificate> cert(
+ Create(std::move(cert_data), options, errors));
+ if (!cert)
+ return false;
+ chain->push_back(std::move(cert));
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/parsed_certificate.h b/chromium/net/cert/pki/parsed_certificate.h
new file mode 100644
index 00000000000..d02c4bf5129
--- /dev/null
+++ b/chromium/net/cert/pki/parsed_certificate.h
@@ -0,0 +1,335 @@
+
+// Copyright 2016 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 NET_CERT_PKI_PARSED_CERTIFICATE_H_
+#define NET_CERT_PKI_PARSED_CERTIFICATE_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/check.h"
+#include "base/memory/ref_counted.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/certificate_policies.h"
+#include "net/cert/pki/parse_certificate.h"
+#include "net/cert/pki/signature_algorithm.h"
+#include "net/der/input.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/boringssl/src/include/openssl/base.h"
+
+namespace net {
+
+struct GeneralNames;
+class NameConstraints;
+class ParsedCertificate;
+class CertErrors;
+
+using ParsedCertificateList = std::vector<scoped_refptr<ParsedCertificate>>;
+
+// Represents an X.509 certificate, including Certificate, TBSCertificate, and
+// standard extensions.
+// Creating a ParsedCertificate does not completely parse and validate the
+// certificate data. Presence of a member in this class implies the DER was
+// parsed successfully to that level, but does not imply the contents of that
+// member are valid, unless otherwise specified. See the documentation for each
+// member or the documentation of the type it returns.
+class NET_EXPORT ParsedCertificate
+ : public base::RefCountedThreadSafe<ParsedCertificate> {
+ public:
+ // Map from OID to ParsedExtension.
+ using ExtensionsMap = std::map<der::Input, ParsedExtension>;
+
+ // Creates a ParsedCertificate given a DER-encoded Certificate. Returns
+ // nullptr on failure. Failure will occur if the standard certificate fields
+ // and supported extensions cannot be parsed.
+ // On either success or failure, if |errors| is non-null it may have error
+ // information added to it.
+ static scoped_refptr<ParsedCertificate> Create(
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_data,
+ const ParseCertificateOptions& options,
+ CertErrors* errors);
+
+ // Creates a ParsedCertificate by copying the provided |data|, and appends it
+ // to |chain|. Returns true if the certificate was successfully parsed and
+ // added. If false is return, |chain| is unmodified.
+ //
+ // On either success or failure, if |errors| is non-null it may have error
+ // information added to it.
+ static bool CreateAndAddToVector(
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_data,
+ const ParseCertificateOptions& options,
+ std::vector<scoped_refptr<net::ParsedCertificate>>* chain,
+ CertErrors* errors);
+
+ ParsedCertificate(const ParsedCertificate&) = delete;
+ ParsedCertificate& operator=(const ParsedCertificate&) = delete;
+
+ // Returns the DER-encoded certificate data for this cert.
+ const der::Input& der_cert() const { return cert_; }
+
+ // Returns the CRYPTO_BUFFER backing this object.
+ CRYPTO_BUFFER* cert_buffer() const { return cert_data_.get(); }
+
+ // Accessors for raw fields of the Certificate.
+ const der::Input& tbs_certificate_tlv() const { return tbs_certificate_tlv_; }
+
+ const der::Input& signature_algorithm_tlv() const {
+ return signature_algorithm_tlv_;
+ }
+
+ const der::BitString& signature_value() const { return signature_value_; }
+
+ // Accessor for struct containing raw fields of the TbsCertificate.
+ const ParsedTbsCertificate& tbs() const { return tbs_; }
+
+ // Returns the signatureAlgorithm of the Certificate (not the tbsCertificate).
+ SignatureAlgorithm signature_algorithm() const {
+ return signature_algorithm_;
+ }
+
+ // Returns the DER-encoded raw subject value (including the outer sequence
+ // tag). This is guaranteed to be valid DER, though the contents of unhandled
+ // string types are treated as raw bytes.
+ der::Input subject_tlv() const { return tbs_.subject_tlv; }
+ // Returns the DER-encoded normalized subject value (not including outer
+ // Sequence tag). This is guaranteed to be valid DER, though the contents of
+ // unhandled string types are treated as raw bytes.
+ der::Input normalized_subject() const {
+ return der::Input(&normalized_subject_);
+ }
+ // Returns the DER-encoded raw issuer value (including the outer sequence
+ // tag). This is guaranteed to be valid DER, though the contents of unhandled
+ // string types are treated as raw bytes.
+ der::Input issuer_tlv() const { return tbs_.issuer_tlv; }
+ // Returns the DER-encoded normalized issuer value (not including outer
+ // Sequence tag). This is guaranteed to be valid DER, though the contents of
+ // unhandled string types are treated as raw bytes.
+ der::Input normalized_issuer() const {
+ return der::Input(&normalized_issuer_);
+ }
+
+ // Returns true if the certificate has a BasicConstraints extension.
+ bool has_basic_constraints() const { return has_basic_constraints_; }
+
+ // Returns the ParsedBasicConstraints struct. Caller must check
+ // has_basic_constraints() before accessing this.
+ const ParsedBasicConstraints& basic_constraints() const {
+ DCHECK(has_basic_constraints_);
+ return basic_constraints_;
+ }
+
+ // Returns true if the certificate has a KeyUsage extension.
+ bool has_key_usage() const { return has_key_usage_; }
+
+ // Returns the KeyUsage BitString. Caller must check
+ // has_key_usage() before accessing this.
+ const der::BitString& key_usage() const {
+ DCHECK(has_key_usage_);
+ return key_usage_;
+ }
+
+ // Returns true if the certificate has a ExtendedKeyUsage extension.
+ bool has_extended_key_usage() const { return has_extended_key_usage_; }
+
+ // Returns the ExtendedKeyUsage key purpose OIDs. Caller must check
+ // has_extended_key_usage() before accessing this.
+ const std::vector<der::Input>& extended_key_usage() const {
+ DCHECK(has_extended_key_usage_);
+ return extended_key_usage_;
+ }
+
+ // Returns true if the certificate has a SubjectAltName extension.
+ bool has_subject_alt_names() const { return subject_alt_names_ != nullptr; }
+
+ // Returns the ParsedExtension struct for the SubjectAltName extension.
+ // If the cert did not have a SubjectAltName extension, this will be a
+ // default-initialized ParsedExtension struct.
+ const ParsedExtension& subject_alt_names_extension() const {
+ return subject_alt_names_extension_;
+ }
+
+ // Returns the GeneralNames class parsed from SubjectAltName extension, or
+ // nullptr if no SubjectAltName extension was present.
+ const GeneralNames* subject_alt_names() const {
+ return subject_alt_names_.get();
+ }
+
+ // Returns true if the certificate has a NameConstraints extension.
+ bool has_name_constraints() const { return name_constraints_ != nullptr; }
+
+ // Returns the parsed NameConstraints extension. Must not be called if
+ // has_name_constraints() is false.
+ const NameConstraints& name_constraints() const {
+ DCHECK(name_constraints_);
+ return *name_constraints_;
+ }
+
+ // Returns true if the certificate has an AuthorityInfoAccess extension.
+ bool has_authority_info_access() const { return has_authority_info_access_; }
+
+ // Returns the ParsedExtension struct for the AuthorityInfoAccess extension.
+ const ParsedExtension& authority_info_access_extension() const {
+ return authority_info_access_extension_;
+ }
+
+ // Returns any caIssuers URIs from the AuthorityInfoAccess extension.
+ const std::vector<base::StringPiece>& ca_issuers_uris() const {
+ return ca_issuers_uris_;
+ }
+
+ // Returns any OCSP URIs from the AuthorityInfoAccess extension.
+ const std::vector<base::StringPiece>& ocsp_uris() const { return ocsp_uris_; }
+
+ // Returns true if the certificate has a Policies extension.
+ bool has_policy_oids() const { return has_policy_oids_; }
+
+ // Returns the policy OIDs. Caller must check has_policy_oids() before
+ // accessing this.
+ const std::vector<der::Input>& policy_oids() const {
+ DCHECK(has_policy_oids());
+ return policy_oids_;
+ }
+
+ // Returns true if the certificate has a PolicyConstraints extension.
+ bool has_policy_constraints() const { return has_policy_constraints_; }
+
+ // Returns the ParsedPolicyConstraints struct. Caller must check
+ // has_policy_constraints() before accessing this.
+ const ParsedPolicyConstraints& policy_constraints() const {
+ DCHECK(has_policy_constraints_);
+ return policy_constraints_;
+ }
+
+ // Returns true if the certificate has a PolicyMappings extension.
+ bool has_policy_mappings() const { return has_policy_mappings_; }
+
+ // Returns the PolicyMappings extension. Caller must check
+ // has_policy_mappings() before accessing this.
+ const std::vector<ParsedPolicyMapping>& policy_mappings() const {
+ DCHECK(has_policy_mappings_);
+ return policy_mappings_;
+ }
+
+ // Returns true if the certificate has a InhibitAnyPolicy extension.
+ bool has_inhibit_any_policy() const { return has_inhibit_any_policy_; }
+
+ // Returns the Inhibit Any Policy extension. Caller must check
+ // has_inhibit_any_policy() before accessing this.
+ uint8_t inhibit_any_policy() const {
+ DCHECK(has_inhibit_any_policy_);
+ return inhibit_any_policy_;
+ }
+
+ // Returns the AuthorityKeyIdentifier extension, or nullopt if there wasn't
+ // one.
+ const absl::optional<ParsedAuthorityKeyIdentifier>& authority_key_identifier()
+ const {
+ return authority_key_identifier_;
+ }
+
+ // Returns the SubjectKeyIdentifier extension, or nullopt if there wasn't
+ // one.
+ const absl::optional<der::Input>& subject_key_identifier() const {
+ return subject_key_identifier_;
+ }
+
+ // Returns a map of all the extensions in the certificate.
+ const ExtensionsMap& extensions() const { return extensions_; }
+
+ // Gets the value for extension matching |extension_oid|. Returns false if the
+ // extension is not present.
+ bool GetExtension(const der::Input& extension_oid,
+ ParsedExtension* parsed_extension) const;
+
+ private:
+ friend class base::RefCountedThreadSafe<ParsedCertificate>;
+ ParsedCertificate();
+ ~ParsedCertificate();
+
+ // The backing store for the certificate data.
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_data_;
+
+ // Points to the raw certificate DER.
+ der::Input cert_;
+
+ der::Input tbs_certificate_tlv_;
+ der::Input signature_algorithm_tlv_;
+ der::BitString signature_value_;
+ ParsedTbsCertificate tbs_;
+
+ // The signatureAlgorithm from the Certificate.
+ //
+ // TODO(crbug.com/1321688): This class requires that we recognize the
+ // signature algorithm, but there are some self-signed root certificates with
+ // weak signature algorithms like MD2. We never verify those signatures, but
+ // this means we must include MD2, etc., in the `SignatureAlgorithm` enum.
+ // Instead, make this an `absl::optional<SignatureAlgorithm>` and make the
+ // call sites handle recognized and unrecognized algorithms.
+ SignatureAlgorithm signature_algorithm_;
+
+ // Normalized DER-encoded Subject (not including outer Sequence tag).
+ std::string normalized_subject_;
+ // Normalized DER-encoded Issuer (not including outer Sequence tag).
+ std::string normalized_issuer_;
+
+ // BasicConstraints extension.
+ bool has_basic_constraints_ = false;
+ ParsedBasicConstraints basic_constraints_;
+
+ // KeyUsage extension.
+ bool has_key_usage_ = false;
+ der::BitString key_usage_;
+
+ // ExtendedKeyUsage extension.
+ bool has_extended_key_usage_ = false;
+ std::vector<der::Input> extended_key_usage_;
+
+ // Raw SubjectAltName extension.
+ ParsedExtension subject_alt_names_extension_;
+ // Parsed SubjectAltName extension.
+ std::unique_ptr<GeneralNames> subject_alt_names_;
+
+ // NameConstraints extension.
+ std::unique_ptr<NameConstraints> name_constraints_;
+
+ // AuthorityInfoAccess extension.
+ bool has_authority_info_access_ = false;
+ ParsedExtension authority_info_access_extension_;
+ // CaIssuers and Ocsp URIs parsed from the AuthorityInfoAccess extension. Note
+ // that the AuthorityInfoAccess may have contained other AccessDescriptions
+ // which are not represented here.
+ std::vector<base::StringPiece> ca_issuers_uris_;
+ std::vector<base::StringPiece> ocsp_uris_;
+
+ // Policies extension.
+ bool has_policy_oids_ = false;
+ std::vector<der::Input> policy_oids_;
+
+ // Policy constraints extension.
+ bool has_policy_constraints_ = false;
+ ParsedPolicyConstraints policy_constraints_;
+
+ // Policy mappings extension.
+ bool has_policy_mappings_ = false;
+ std::vector<ParsedPolicyMapping> policy_mappings_;
+
+ // Inhibit Any Policy extension.
+ bool has_inhibit_any_policy_ = false;
+ uint8_t inhibit_any_policy_;
+
+ // AuthorityKeyIdentifier extension.
+ absl::optional<ParsedAuthorityKeyIdentifier> authority_key_identifier_;
+
+ // SubjectKeyIdentifier extension.
+ absl::optional<der::Input> subject_key_identifier_;
+
+ // All of the extensions.
+ ExtensionsMap extensions_;
+};
+
+} // namespace net
+
+#endif // NET_CERT_PKI_PARSED_CERTIFICATE_H_
diff --git a/chromium/net/cert/pki/parsed_certificate_unittest.cc b/chromium/net/cert/pki/parsed_certificate_unittest.cc
new file mode 100644
index 00000000000..b33520910b3
--- /dev/null
+++ b/chromium/net/cert/pki/parsed_certificate_unittest.cc
@@ -0,0 +1,586 @@
+// Copyright 2017 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 "net/cert/pki/parsed_certificate.h"
+
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/parse_certificate.h"
+#include "net/cert/pki/test_helpers.h"
+#include "net/der/input.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+
+// TODO(eroman): Add tests for parsing of policy mappings.
+
+namespace net {
+
+namespace {
+
+std::string GetFilePath(const std::string& file_name) {
+ return std::string("net/data/parse_certificate_unittest/") + file_name;
+}
+
+// Reads and parses a certificate from the PEM file |file_name|.
+//
+// Returns nullptr if the certificate parsing failed, and verifies that any
+// errors match the ERRORS block in the .pem file.
+scoped_refptr<ParsedCertificate> ParseCertificateFromFile(
+ const std::string& file_name,
+ const ParseCertificateOptions& options) {
+ std::string data;
+ std::string expected_errors;
+
+ // Read the certificate data and error expectations from a single PEM file.
+ const PemBlockMapping mappings[] = {
+ {"CERTIFICATE", &data},
+ {"ERRORS", &expected_errors, true /*optional*/},
+ };
+ std::string test_file_path = GetFilePath(file_name);
+ EXPECT_TRUE(ReadTestDataFromPemFile(test_file_path, mappings));
+
+ CertErrors errors;
+ scoped_refptr<ParsedCertificate> cert = ParsedCertificate::Create(
+ bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(
+ reinterpret_cast<const uint8_t*>(data.data()), data.size(), nullptr)),
+ options, &errors);
+
+ // The errors are baselined for |!allow_invalid_serial_numbers|. So if
+ // requesting a non-default option skip the error checks.
+ // TODO(eroman): This is ugly.
+ if (!options.allow_invalid_serial_numbers)
+ VerifyCertErrors(expected_errors, errors, test_file_path);
+
+ // Every parse failure being tested should emit error information.
+ if (!cert)
+ EXPECT_FALSE(errors.ToDebugString().empty());
+
+ return cert;
+}
+
+der::Input DavidBenOid() {
+ // This OID corresponds with
+ // 1.2.840.113554.4.1.72585.0 (https://davidben.net/oid)
+ static const uint8_t kOid[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12,
+ 0x04, 0x01, 0x84, 0xb7, 0x09, 0x00};
+ return der::Input(kOid);
+}
+
+// Parses an Extension whose critical field is true (255).
+TEST(ParsedCertificateTest, ExtensionCritical) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("extension_critical.pem", {});
+ ASSERT_TRUE(cert);
+
+ const uint8_t kExpectedValue[] = {0x30, 0x00};
+
+ ParsedExtension extension;
+ ASSERT_TRUE(cert->GetExtension(DavidBenOid(), &extension));
+
+ EXPECT_TRUE(extension.critical);
+ EXPECT_EQ(DavidBenOid(), extension.oid);
+ EXPECT_EQ(der::Input(kExpectedValue), extension.value);
+}
+
+// Parses an Extension whose critical field is false (omitted).
+TEST(ParsedCertificateTest, ExtensionNotCritical) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("extension_not_critical.pem", {});
+ ASSERT_TRUE(cert);
+
+ const uint8_t kExpectedValue[] = {0x30, 0x00};
+
+ ParsedExtension extension;
+ ASSERT_TRUE(cert->GetExtension(DavidBenOid(), &extension));
+
+ EXPECT_FALSE(extension.critical);
+ EXPECT_EQ(DavidBenOid(), extension.oid);
+ EXPECT_EQ(der::Input(kExpectedValue), extension.value);
+}
+
+// Parses an Extension whose critical field is 0. This is in one sense FALSE,
+// however because critical has DEFAULT of false this is in fact invalid
+// DER-encoding.
+TEST(ParsedCertificateTest, ExtensionCritical0) {
+ ASSERT_FALSE(ParseCertificateFromFile("extension_critical_0.pem", {}));
+}
+
+// Parses an Extension whose critical field is 3. Under DER-encoding BOOLEAN
+// values must an octet of either all zero bits, or all 1 bits, so this is not
+// valid.
+TEST(ParsedCertificateTest, ExtensionCritical3) {
+ ASSERT_FALSE(ParseCertificateFromFile("extension_critical_3.pem", {}));
+}
+
+// Parses an Extensions that is an empty sequence.
+TEST(ParsedCertificateTest, ExtensionsEmptySequence) {
+ ASSERT_FALSE(ParseCertificateFromFile("extensions_empty_sequence.pem", {}));
+}
+
+// Parses an Extensions that is not a sequence.
+TEST(ParsedCertificateTest, ExtensionsNotSequence) {
+ ASSERT_FALSE(ParseCertificateFromFile("extensions_not_sequence.pem", {}));
+}
+
+// Parses an Extensions that has data after the sequence.
+TEST(ParsedCertificateTest, ExtensionsDataAfterSequence) {
+ ASSERT_FALSE(
+ ParseCertificateFromFile("extensions_data_after_sequence.pem", {}));
+}
+
+// Parses an Extensions that contains duplicated key usages.
+TEST(ParsedCertificateTest, ExtensionsDuplicateKeyUsage) {
+ ASSERT_FALSE(
+ ParseCertificateFromFile("extensions_duplicate_key_usage.pem", {}));
+}
+
+// Parses a certificate with a bad key usage extension (BIT STRING with zero
+// elements).
+TEST(ParsedCertificateTest, BadKeyUsage) {
+ ASSERT_FALSE(ParseCertificateFromFile("bad_key_usage.pem", {}));
+}
+
+// Parses a certificate that has a PolicyQualifierInfo that is missing the
+// qualifier field.
+TEST(ParsedCertificateTest, BadPolicyQualifiers) {
+ ASSERT_FALSE(ParseCertificateFromFile("bad_policy_qualifiers.pem", {}));
+}
+
+// Parses a certificate that uses an unknown signature algorithm OID (00).
+TEST(ParsedCertificateTest, BadSignatureAlgorithmOid) {
+ ASSERT_FALSE(ParseCertificateFromFile("bad_signature_algorithm_oid.pem", {}));
+}
+
+// The validity encodes time as UTCTime but following the BER rules rather than
+// DER rules (i.e. YYMMDDHHMMZ instead of YYMMDDHHMMSSZ).
+TEST(ParsedCertificateTest, BadValidity) {
+ ASSERT_FALSE(ParseCertificateFromFile("bad_validity.pem", {}));
+}
+
+// The signature algorithm contains an unexpected parameters field.
+TEST(ParsedCertificateTest, FailedSignatureAlgorithm) {
+ ASSERT_FALSE(ParseCertificateFromFile("failed_signature_algorithm.pem", {}));
+}
+
+TEST(ParsedCertificateTest, IssuerBadPrintableString) {
+ ASSERT_FALSE(ParseCertificateFromFile("issuer_bad_printable_string.pem", {}));
+}
+
+TEST(ParsedCertificateTest, NameConstraintsBadIp) {
+ ASSERT_FALSE(ParseCertificateFromFile("name_constraints_bad_ip.pem", {}));
+}
+
+TEST(ParsedCertificateTest, PolicyQualifiersEmptySequence) {
+ ASSERT_FALSE(
+ ParseCertificateFromFile("policy_qualifiers_empty_sequence.pem", {}));
+}
+
+TEST(ParsedCertificateTest, SubjectBlankSubjectAltNameNotCritical) {
+ ASSERT_FALSE(ParseCertificateFromFile(
+ "subject_blank_subjectaltname_not_critical.pem", {}));
+}
+
+TEST(ParsedCertificateTest, SubjectNotAscii) {
+ ASSERT_FALSE(ParseCertificateFromFile("subject_not_ascii.pem", {}));
+}
+
+TEST(ParsedCertificateTest, SubjectNotPrintableString) {
+ ASSERT_FALSE(
+ ParseCertificateFromFile("subject_not_printable_string.pem", {}));
+}
+
+TEST(ParsedCertificateTest, SubjectAltNameBadIp) {
+ ASSERT_FALSE(ParseCertificateFromFile("subjectaltname_bad_ip.pem", {}));
+}
+
+TEST(ParsedCertificateTest, SubjectAltNameDnsNotAscii) {
+ ASSERT_FALSE(
+ ParseCertificateFromFile("subjectaltname_dns_not_ascii.pem", {}));
+}
+
+TEST(ParsedCertificateTest, SubjectAltNameGeneralNamesEmptySequence) {
+ ASSERT_FALSE(ParseCertificateFromFile(
+ "subjectaltname_general_names_empty_sequence.pem", {}));
+}
+
+TEST(ParsedCertificateTest, SubjectAltNameTrailingData) {
+ ASSERT_FALSE(
+ ParseCertificateFromFile("subjectaltname_trailing_data.pem", {}));
+}
+
+TEST(ParsedCertificateTest, V1ExplicitVersion) {
+ ASSERT_FALSE(ParseCertificateFromFile("v1_explicit_version.pem", {}));
+}
+
+// Parses an Extensions that contains an extended key usages.
+TEST(ParsedCertificateTest, ExtendedKeyUsage) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("extended_key_usage.pem", {});
+ ASSERT_TRUE(cert);
+
+ ASSERT_EQ(4u, cert->extensions().size());
+
+ ParsedExtension extension;
+ ASSERT_TRUE(cert->GetExtension(der::Input(kExtKeyUsageOid), &extension));
+
+ EXPECT_FALSE(extension.critical);
+ EXPECT_EQ(45u, extension.value.Length());
+
+ EXPECT_TRUE(cert->has_extended_key_usage());
+ EXPECT_EQ(4u, cert->extended_key_usage().size());
+}
+
+// Parses an Extensions that contains a key usage.
+TEST(ParsedCertificateTest, KeyUsage) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("key_usage.pem", {});
+ ASSERT_TRUE(cert);
+
+ ASSERT_TRUE(cert->has_key_usage());
+
+ EXPECT_EQ(5u, cert->key_usage().unused_bits());
+ const uint8_t kExpectedBytes[] = {0xA0};
+ EXPECT_EQ(der::Input(kExpectedBytes), cert->key_usage().bytes());
+
+ EXPECT_TRUE(cert->key_usage().AssertsBit(0));
+ EXPECT_FALSE(cert->key_usage().AssertsBit(1));
+ EXPECT_TRUE(cert->key_usage().AssertsBit(2));
+}
+
+// Parses an Extensions that contains a policies extension.
+TEST(ParsedCertificateTest, Policies) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("policies.pem", {});
+ ASSERT_TRUE(cert);
+
+ ASSERT_EQ(4u, cert->extensions().size());
+
+ ParsedExtension extension;
+ ASSERT_TRUE(
+ cert->GetExtension(der::Input(kCertificatePoliciesOid), &extension));
+
+ EXPECT_FALSE(extension.critical);
+ EXPECT_EQ(95u, extension.value.Length());
+
+ EXPECT_TRUE(cert->has_policy_oids());
+ EXPECT_EQ(2u, cert->policy_oids().size());
+}
+
+// Parses an Extensions that contains a subjectaltname extension.
+TEST(ParsedCertificateTest, SubjectAltName) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("subject_alt_name.pem", {});
+ ASSERT_TRUE(cert);
+
+ ASSERT_TRUE(cert->has_subject_alt_names());
+}
+
+// Parses an Extensions that contains multiple extensions, sourced from a
+// real-world certificate.
+TEST(ParsedCertificateTest, ExtensionsReal) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("extensions_real.pem", {});
+ ASSERT_TRUE(cert);
+
+ ASSERT_EQ(7u, cert->extensions().size());
+
+ EXPECT_TRUE(cert->has_key_usage());
+ EXPECT_TRUE(cert->has_basic_constraints());
+ EXPECT_TRUE(cert->has_authority_info_access());
+ EXPECT_TRUE(cert->has_policy_oids());
+
+ ASSERT_TRUE(cert->authority_key_identifier());
+ ASSERT_TRUE(cert->authority_key_identifier()->key_identifier);
+ EXPECT_FALSE(cert->authority_key_identifier()->authority_cert_issuer);
+ EXPECT_FALSE(cert->authority_key_identifier()->authority_cert_serial_number);
+ const uint8_t expected_authority_key_identifier[] = {
+ 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64,
+ 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e,
+ };
+ EXPECT_EQ(der::Input(expected_authority_key_identifier),
+ cert->authority_key_identifier()->key_identifier);
+
+ ASSERT_TRUE(cert->subject_key_identifier());
+ const uint8_t expected_subject_key_identifier[] = {
+ 0x4a, 0xdd, 0x06, 0x16, 0x1b, 0xbc, 0xf6, 0x68, 0xb5, 0x76,
+ 0xf5, 0x81, 0xb6, 0xbb, 0x62, 0x1a, 0xba, 0x5a, 0x81, 0x2f};
+ EXPECT_EQ(der::Input(expected_subject_key_identifier),
+ cert->subject_key_identifier());
+
+ ParsedExtension extension;
+ ASSERT_TRUE(
+ cert->GetExtension(der::Input(kCertificatePoliciesOid), &extension));
+
+ EXPECT_FALSE(extension.critical);
+ EXPECT_EQ(16u, extension.value.Length());
+
+ // TODO(eroman): Verify the other extensions' values.
+}
+
+// Parses a BasicConstraints with no CA or pathlen.
+TEST(ParsedCertificateTest, BasicConstraintsNotCa) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("basic_constraints_not_ca.pem", {});
+ ASSERT_TRUE(cert);
+
+ EXPECT_TRUE(cert->has_basic_constraints());
+ EXPECT_FALSE(cert->basic_constraints().is_ca);
+ EXPECT_FALSE(cert->basic_constraints().has_path_len);
+}
+
+// Parses a BasicConstraints with CA but no pathlen.
+TEST(ParsedCertificateTest, BasicConstraintsCaNoPath) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("basic_constraints_ca_no_path.pem", {});
+ ASSERT_TRUE(cert);
+
+ EXPECT_TRUE(cert->has_basic_constraints());
+ EXPECT_TRUE(cert->basic_constraints().is_ca);
+ EXPECT_FALSE(cert->basic_constraints().has_path_len);
+}
+
+// Parses a BasicConstraints with CA and pathlen of 9.
+TEST(ParsedCertificateTest, BasicConstraintsCaPath9) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("basic_constraints_ca_path_9.pem", {});
+ ASSERT_TRUE(cert);
+
+ EXPECT_TRUE(cert->has_basic_constraints());
+ EXPECT_TRUE(cert->basic_constraints().is_ca);
+ EXPECT_TRUE(cert->basic_constraints().has_path_len);
+ EXPECT_EQ(9u, cert->basic_constraints().path_len);
+}
+
+// Parses a BasicConstraints with CA and pathlen of 255 (largest allowed size).
+TEST(ParsedCertificateTest, BasicConstraintsPathlen255) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("basic_constraints_pathlen_255.pem", {});
+ ASSERT_TRUE(cert);
+
+ EXPECT_TRUE(cert->has_basic_constraints());
+ EXPECT_TRUE(cert->basic_constraints().is_ca);
+ EXPECT_TRUE(cert->basic_constraints().has_path_len);
+ EXPECT_EQ(255, cert->basic_constraints().path_len);
+}
+
+// Parses a BasicConstraints with CA and pathlen of 256 (too large).
+TEST(ParsedCertificateTest, BasicConstraintsPathlen256) {
+ ASSERT_FALSE(
+ ParseCertificateFromFile("basic_constraints_pathlen_256.pem", {}));
+}
+
+// Parses a BasicConstraints with CA and a negative pathlen.
+TEST(ParsedCertificateTest, BasicConstraintsNegativePath) {
+ ASSERT_FALSE(
+ ParseCertificateFromFile("basic_constraints_negative_path.pem", {}));
+}
+
+// Parses a BasicConstraints with CA and pathlen that is very large (and
+// couldn't fit in a 64-bit integer).
+TEST(ParsedCertificateTest, BasicConstraintsPathTooLarge) {
+ ASSERT_FALSE(
+ ParseCertificateFromFile("basic_constraints_path_too_large.pem", {}));
+}
+
+// Parses a BasicConstraints with CA explicitly set to false. This violates
+// DER-encoding rules, however is commonly used, so it is accepted.
+TEST(ParsedCertificateTest, BasicConstraintsCaFalse) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("basic_constraints_ca_false.pem", {});
+ ASSERT_TRUE(cert);
+
+ EXPECT_TRUE(cert->has_basic_constraints());
+ EXPECT_FALSE(cert->basic_constraints().is_ca);
+ EXPECT_FALSE(cert->basic_constraints().has_path_len);
+}
+
+// Parses a BasicConstraints with CA set to true and an unexpected NULL at
+// the end.
+TEST(ParsedCertificateTest, BasicConstraintsUnconsumedData) {
+ ASSERT_FALSE(
+ ParseCertificateFromFile("basic_constraints_unconsumed_data.pem", {}));
+}
+
+// Parses a BasicConstraints with CA omitted (false), but with a pathlen of 1.
+// This is valid DER for the ASN.1, however is not valid when interpreting the
+// BasicConstraints at a higher level.
+TEST(ParsedCertificateTest, BasicConstraintsPathLenButNotCa) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("basic_constraints_pathlen_not_ca.pem", {});
+ ASSERT_TRUE(cert);
+
+ EXPECT_TRUE(cert->has_basic_constraints());
+ EXPECT_FALSE(cert->basic_constraints().is_ca);
+ EXPECT_TRUE(cert->basic_constraints().has_path_len);
+ EXPECT_EQ(1u, cert->basic_constraints().path_len);
+}
+
+// Tests parsing a certificate that contains a policyConstraints
+// extension having requireExplicitPolicy:3.
+TEST(ParsedCertificateTest, PolicyConstraintsRequire) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("policy_constraints_require.pem", {});
+ ASSERT_TRUE(cert);
+
+ EXPECT_TRUE(cert->has_policy_constraints());
+ EXPECT_TRUE(cert->policy_constraints().require_explicit_policy.has_value());
+ EXPECT_EQ(3, cert->policy_constraints().require_explicit_policy.value());
+ EXPECT_FALSE(cert->policy_constraints().inhibit_policy_mapping.has_value());
+}
+
+// Tests parsing a certificate that contains a policyConstraints
+// extension having inhibitPolicyMapping:1.
+TEST(ParsedCertificateTest, PolicyConstraintsInhibit) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("policy_constraints_inhibit.pem", {});
+ ASSERT_TRUE(cert);
+
+ EXPECT_TRUE(cert->has_policy_constraints());
+ EXPECT_FALSE(cert->policy_constraints().require_explicit_policy.has_value());
+ EXPECT_TRUE(cert->policy_constraints().inhibit_policy_mapping.has_value());
+ EXPECT_EQ(1, cert->policy_constraints().inhibit_policy_mapping.value());
+}
+
+// Tests parsing a certificate that contains a policyConstraints
+// extension having requireExplicitPolicy:5,inhibitPolicyMapping:2.
+TEST(ParsedCertificateTest, PolicyConstraintsInhibitRequire) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("policy_constraints_inhibit_require.pem", {});
+ ASSERT_TRUE(cert);
+
+ EXPECT_TRUE(cert->has_policy_constraints());
+ EXPECT_TRUE(cert->policy_constraints().require_explicit_policy.has_value());
+ EXPECT_EQ(5, cert->policy_constraints().require_explicit_policy.value());
+ EXPECT_TRUE(cert->policy_constraints().inhibit_policy_mapping.has_value());
+ EXPECT_EQ(2, cert->policy_constraints().inhibit_policy_mapping.value());
+}
+
+// Tests parsing a certificate that has a policyConstraints
+// extension with an empty sequence.
+TEST(ParsedCertificateTest, PolicyConstraintsEmpty) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("policy_constraints_empty.pem", {});
+ ASSERT_FALSE(cert);
+}
+
+// Tests a certificate with a serial number with a leading 0 padding byte in
+// the encoding since it is not negative.
+TEST(ParsedCertificateTest, SerialNumberZeroPadded) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("serial_zero_padded.pem", {});
+ ASSERT_TRUE(cert);
+
+ static const uint8_t expected_serial[3] = {0x00, 0x80, 0x01};
+ EXPECT_EQ(der::Input(expected_serial), cert->tbs().serial_number);
+}
+
+// Tests a serial number where the MSB is >= 0x80, causing the encoded
+// length to be 21 bytes long. This is an error, as RFC 5280 specifies a
+// maximum of 20 bytes.
+TEST(ParsedCertificateTest, SerialNumberZeroPadded21BytesLong) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("serial_zero_padded_21_bytes.pem", {});
+ ASSERT_FALSE(cert);
+
+ // Try again with allow_invalid_serial_numbers=true. Parsing should succeed.
+ ParseCertificateOptions options;
+ options.allow_invalid_serial_numbers = true;
+ cert = ParseCertificateFromFile("serial_zero_padded_21_bytes.pem", options);
+ ASSERT_TRUE(cert);
+
+ static const uint8_t expected_serial[21] = {
+ 0x00, 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13};
+ EXPECT_EQ(der::Input(expected_serial), cert->tbs().serial_number);
+}
+
+// Tests a serial number which is negative. CAs are not supposed to include
+// negative serial numbers, however RFC 5280 expects consumers to deal with it
+// anyway.
+TEST(ParsedCertificateTest, SerialNumberNegative) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("serial_negative.pem", {});
+ ASSERT_TRUE(cert);
+
+ static const uint8_t expected_serial[2] = {0x80, 0x01};
+ EXPECT_EQ(der::Input(expected_serial), cert->tbs().serial_number);
+}
+
+// Tests a serial number which is very long. RFC 5280 specifies a maximum of 20
+// bytes.
+TEST(ParsedCertificateTest, SerialNumber37BytesLong) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("serial_37_bytes.pem", {});
+ ASSERT_FALSE(cert);
+
+ // Try again with allow_invalid_serial_numbers=true. Parsing should succeed.
+ ParseCertificateOptions options;
+ options.allow_invalid_serial_numbers = true;
+ cert = ParseCertificateFromFile("serial_37_bytes.pem", options);
+ ASSERT_TRUE(cert);
+
+ static const uint8_t expected_serial[37] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+ 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25};
+ EXPECT_EQ(der::Input(expected_serial), cert->tbs().serial_number);
+}
+
+// Tests a serial number which is zero. RFC 5280 says they should be positive,
+// however also recommends supporting non-positive ones, so parsing here
+// is expected to succeed.
+TEST(ParsedCertificateTest, SerialNumberZero) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("serial_zero.pem", {});
+ ASSERT_TRUE(cert);
+
+ static const uint8_t expected_serial[] = {0x00};
+ EXPECT_EQ(der::Input(expected_serial), cert->tbs().serial_number);
+}
+
+// Tests a serial number which not a number (NULL).
+TEST(ParsedCertificateTest, SerialNotNumber) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("serial_not_number.pem", {});
+ ASSERT_FALSE(cert);
+}
+
+// Tests a serial number which uses a non-minimal INTEGER encoding
+TEST(ParsedCertificateTest, SerialNotMinimal) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("serial_not_minimal.pem", {});
+ ASSERT_FALSE(cert);
+}
+
+// Tests parsing a certificate that has an inhibitAnyPolicy extension.
+TEST(ParsedCertificateTest, InhibitAnyPolicy) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("inhibit_any_policy.pem", {});
+ ASSERT_TRUE(cert);
+
+ ParsedExtension extension;
+ ASSERT_TRUE(cert->GetExtension(der::Input(kInhibitAnyPolicyOid), &extension));
+
+ uint8_t skip_count;
+ ASSERT_TRUE(ParseInhibitAnyPolicy(extension.value, &skip_count));
+ EXPECT_EQ(3, skip_count);
+}
+
+// Tests a subjectKeyIdentifier that is not an OCTET_STRING.
+TEST(ParsedCertificateTest, SubjectKeyIdentifierNotOctetString) {
+ scoped_refptr<ParsedCertificate> cert = ParseCertificateFromFile(
+ "subject_key_identifier_not_octet_string.pem", {});
+ ASSERT_FALSE(cert);
+}
+
+// Tests an authorityKeyIdentifier that is not a SEQUENCE.
+TEST(ParsedCertificateTest, AuthourityKeyIdentifierNotSequence) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromFile("authority_key_identifier_not_sequence.pem", {});
+ ASSERT_FALSE(cert);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/pki/path_builder.cc b/chromium/net/cert/pki/path_builder.cc
new file mode 100644
index 00000000000..cdb9ede48dd
--- /dev/null
+++ b/chromium/net/cert/pki/path_builder.cc
@@ -0,0 +1,851 @@
+// Copyright 2016 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 "net/cert/pki/path_builder.h"
+
+#include <memory>
+#include <set>
+#include <unordered_set>
+
+#include "base/logging.h"
+#include "base/memory/raw_ptr.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
+#include "base/strings/string_number_conversions.h"
+#include "crypto/sha2.h"
+#include "net/base/net_errors.h"
+#include "net/cert/pki/cert_issuer_source.h"
+#include "net/cert/pki/certificate_policies.h"
+#include "net/cert/pki/common_cert_errors.h"
+#include "net/cert/pki/parse_certificate.h"
+#include "net/cert/pki/parse_name.h" // For CertDebugString.
+#include "net/cert/pki/trust_store.h"
+#include "net/cert/pki/verify_certificate_chain.h"
+#include "net/cert/pki/verify_name_match.h"
+#include "net/der/parser.h"
+#include "net/der/tag.h"
+
+namespace net {
+
+namespace {
+
+using CertIssuerSources = std::vector<CertIssuerSource*>;
+
+// Returns a hex-encoded sha256 of the DER-encoding of |cert|.
+std::string FingerPrintParsedCertificate(const net::ParsedCertificate* cert) {
+ std::string hash = crypto::SHA256HashString(cert->der_cert().AsStringPiece());
+ return base::HexEncode(hash.data(), hash.size());
+}
+
+// TODO(mattm): decide how much debug logging to keep.
+std::string CertDebugString(const ParsedCertificate* cert) {
+ RDNSequence subject;
+ std::string subject_str;
+ if (!ParseName(cert->tbs().subject_tlv, &subject) ||
+ !ConvertToRFC2253(subject, &subject_str))
+ subject_str = "???";
+
+ return FingerPrintParsedCertificate(cert) + " " + subject_str;
+}
+
+std::string PathDebugString(const ParsedCertificateList& certs) {
+ std::string s;
+ for (const auto& cert : certs) {
+ if (!s.empty())
+ s += "\n";
+ s += " " + CertDebugString(cert.get());
+ }
+ return s;
+}
+
+void RecordIterationCountHistogram(uint32_t iteration_count) {
+ base::UmaHistogramCounts10000("Net.CertVerifier.PathBuilderIterationCount",
+ iteration_count);
+}
+
+// This structure describes a certificate and its trust level. Note that |cert|
+// may be null to indicate an "empty" entry.
+struct IssuerEntry {
+ scoped_refptr<ParsedCertificate> cert;
+ CertificateTrust trust;
+ int trust_and_key_id_match_ordering;
+};
+
+enum KeyIdentifierMatch {
+ // |target| has a keyIdentifier and it matches |issuer|'s
+ // subjectKeyIdentifier.
+ kMatch = 0,
+ // |target| does not have authorityKeyIdentifier or |issuer| does not have
+ // subjectKeyIdentifier.
+ kNoData = 1,
+ // |target|'s authorityKeyIdentifier does not match |issuer|.
+ kMismatch = 2,
+};
+
+// Returns an integer that represents the relative ordering of |issuer| for
+// prioritizing certificates in path building based on |issuer|'s
+// subjectKeyIdentifier and |target|'s authorityKeyIdentifier. Lower return
+// values indicate higer priority.
+KeyIdentifierMatch CalculateKeyIdentifierMatch(
+ const ParsedCertificate* target,
+ const ParsedCertificate* issuer) {
+ if (!target->authority_key_identifier())
+ return kNoData;
+
+ // TODO(crbug.com/635205): If issuer does not have a subjectKeyIdentifier,
+ // could try synthesizing one using the standard SHA-1 method. Ideally in a
+ // way where any issuers that do have a matching subjectKeyIdentifier could
+ // be tried first before doing the extra work.
+ if (target->authority_key_identifier()->key_identifier &&
+ issuer->subject_key_identifier()) {
+ if (target->authority_key_identifier()->key_identifier !=
+ issuer->subject_key_identifier().value()) {
+ return kMismatch;
+ }
+ return kMatch;
+ }
+
+ return kNoData;
+}
+
+// Returns an integer that represents the relative ordering of |issuer| based
+// on |issuer_trust| and authorityKeyIdentifier matching for prioritizing
+// certificates in path building. Lower return values indicate higer priority.
+int TrustAndKeyIdentifierMatchToOrder(const ParsedCertificate* target,
+ const ParsedCertificate* issuer,
+ const CertificateTrust& issuer_trust) {
+ enum {
+ kTrustedAndKeyIdMatch = 0,
+ kTrustedAndKeyIdNoData = 1,
+ kKeyIdMatch = 2,
+ kKeyIdNoData = 3,
+ kTrustedAndKeyIdMismatch = 4,
+ kKeyIdMismatch = 5,
+ kDistrustedAndKeyIdMatch = 6,
+ kDistrustedAndKeyIdNoData = 7,
+ kDistrustedAndKeyIdMismatch = 8,
+ };
+
+ KeyIdentifierMatch key_id_match = CalculateKeyIdentifierMatch(target, issuer);
+ switch (issuer_trust.type) {
+ case CertificateTrustType::TRUSTED_ANCHOR:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
+ switch (key_id_match) {
+ case kMatch:
+ return kTrustedAndKeyIdMatch;
+ case kNoData:
+ return kTrustedAndKeyIdNoData;
+ case kMismatch:
+ return kTrustedAndKeyIdMismatch;
+ }
+ case CertificateTrustType::UNSPECIFIED:
+ switch (key_id_match) {
+ case kMatch:
+ return kKeyIdMatch;
+ case kNoData:
+ return kKeyIdNoData;
+ case kMismatch:
+ return kKeyIdMismatch;
+ }
+ case CertificateTrustType::DISTRUSTED:
+ switch (key_id_match) {
+ case kMatch:
+ return kDistrustedAndKeyIdMatch;
+ case kNoData:
+ return kDistrustedAndKeyIdNoData;
+ case kMismatch:
+ return kDistrustedAndKeyIdMismatch;
+ }
+ }
+}
+
+// CertIssuersIter iterates through the intermediates from |cert_issuer_sources|
+// which may be issuers of |cert|.
+class CertIssuersIter {
+ public:
+ // Constructs the CertIssuersIter. |*cert_issuer_sources|, |*trust_store|,
+ // and |*debug_data| must be valid for the lifetime of the CertIssuersIter.
+ CertIssuersIter(scoped_refptr<ParsedCertificate> cert,
+ CertIssuerSources* cert_issuer_sources,
+ const TrustStore* trust_store,
+ base::SupportsUserData* debug_data);
+
+ CertIssuersIter(const CertIssuersIter&) = delete;
+ CertIssuersIter& operator=(const CertIssuersIter&) = delete;
+
+ // Gets the next candidate issuer, or clears |*out| when all issuers have been
+ // exhausted.
+ void GetNextIssuer(IssuerEntry* out);
+
+ // Returns true if candidate issuers were found for |cert_|.
+ bool had_non_skipped_issuers() const {
+ return issuers_.size() > skipped_issuer_count_;
+ }
+
+ void increment_skipped_issuer_count() { skipped_issuer_count_++; }
+
+ // Returns the |cert| for which issuers are being retrieved.
+ const ParsedCertificate* cert() const { return cert_.get(); }
+ scoped_refptr<ParsedCertificate> reference_cert() const { return cert_; }
+
+ private:
+ void AddIssuers(ParsedCertificateList issuers);
+ void DoAsyncIssuerQuery();
+
+ // Returns true if |issuers_| contains unconsumed certificates.
+ bool HasCurrentIssuer() const { return cur_issuer_ < issuers_.size(); }
+
+ // Sorts the remaining entries in |issuers_| in the preferred order to
+ // explore. Does not change the ordering for indices before cur_issuer_.
+ void SortRemainingIssuers();
+
+ scoped_refptr<ParsedCertificate> cert_;
+ raw_ptr<CertIssuerSources> cert_issuer_sources_;
+ raw_ptr<const TrustStore> trust_store_;
+
+ // The list of issuers for |cert_|. This is added to incrementally (first
+ // synchronous results, then possibly multiple times as asynchronous results
+ // arrive.) The issuers may be re-sorted each time new issuers are added, but
+ // only the results from |cur_| onwards should be sorted, since the earlier
+ // results were already returned.
+ // Elements should not be removed from |issuers_| once added, since
+ // |present_issuers_| will point to data owned by the certs.
+ std::vector<IssuerEntry> issuers_;
+ // The index of the next cert in |issuers_| to return.
+ size_t cur_issuer_ = 0;
+ // The number of issuers that were skipped due to the loop checker.
+ size_t skipped_issuer_count_ = 0;
+ // Set to true whenever new issuers are appended at the end, to indicate the
+ // ordering needs to be checked.
+ bool issuers_needs_sort_ = false;
+
+ // Set of DER-encoded values for the certs in |issuers_|. Used to prevent
+ // duplicates. This is based on the full DER of the cert to allow different
+ // versions of the same certificate to be tried in different candidate paths.
+ // This points to data owned by |issuers_|.
+ std::unordered_set<base::StringPiece, base::StringPieceHash> present_issuers_;
+
+ // Tracks which requests have been made yet.
+ bool did_initial_query_ = false;
+ bool did_async_issuer_query_ = false;
+ // Index into pending_async_requests_ that is the next one to process.
+ size_t cur_async_request_ = 0;
+ // Owns the Request objects for any asynchronous requests so that they will be
+ // cancelled if CertIssuersIter is destroyed.
+ std::vector<std::unique_ptr<CertIssuerSource::Request>>
+ pending_async_requests_;
+
+ raw_ptr<base::SupportsUserData> debug_data_;
+};
+
+CertIssuersIter::CertIssuersIter(scoped_refptr<ParsedCertificate> in_cert,
+ CertIssuerSources* cert_issuer_sources,
+ const TrustStore* trust_store,
+ base::SupportsUserData* debug_data)
+ : cert_(in_cert),
+ cert_issuer_sources_(cert_issuer_sources),
+ trust_store_(trust_store),
+ debug_data_(debug_data) {
+ DVLOG(2) << "CertIssuersIter created for " << CertDebugString(cert());
+}
+
+void CertIssuersIter::GetNextIssuer(IssuerEntry* out) {
+ if (!did_initial_query_) {
+ did_initial_query_ = true;
+ for (auto* cert_issuer_source : *cert_issuer_sources_) {
+ ParsedCertificateList new_issuers;
+ cert_issuer_source->SyncGetIssuersOf(cert(), &new_issuers);
+ AddIssuers(std::move(new_issuers));
+ }
+ }
+
+ // If there aren't any issuers, block until async results are ready.
+ if (!HasCurrentIssuer()) {
+ if (!did_async_issuer_query_) {
+ // Now issue request(s) for async ones (AIA, etc).
+ DoAsyncIssuerQuery();
+ }
+
+ // TODO(eroman): Rather than blocking on the async requests in FIFO order,
+ // consume in the order they become ready.
+ while (!HasCurrentIssuer() &&
+ cur_async_request_ < pending_async_requests_.size()) {
+ ParsedCertificateList new_issuers;
+ pending_async_requests_[cur_async_request_]->GetNext(&new_issuers);
+ if (new_issuers.empty()) {
+ // Request is exhausted, no more results pending from that
+ // CertIssuerSource.
+ pending_async_requests_[cur_async_request_++].reset();
+ } else {
+ AddIssuers(std::move(new_issuers));
+ }
+ }
+ }
+
+ if (HasCurrentIssuer()) {
+ SortRemainingIssuers();
+
+ DVLOG(2) << "CertIssuersIter returning issuer " << cur_issuer_ << " of "
+ << issuers_.size() << " for " << CertDebugString(cert());
+ // Still have issuers that haven't been returned yet, return the highest
+ // priority one (head of remaining list). A reference to the returned issuer
+ // is retained, since |present_issuers_| points to data owned by it.
+ *out = issuers_[cur_issuer_++];
+ return;
+ }
+
+ DVLOG(2) << "CertIssuersIter reached the end of all available issuers for "
+ << CertDebugString(cert());
+ // Reached the end of all available issuers.
+ *out = IssuerEntry();
+}
+
+void CertIssuersIter::AddIssuers(ParsedCertificateList new_issuers) {
+ for (scoped_refptr<ParsedCertificate>& issuer : new_issuers) {
+ if (present_issuers_.find(issuer->der_cert().AsStringPiece()) !=
+ present_issuers_.end())
+ continue;
+ present_issuers_.insert(issuer->der_cert().AsStringPiece());
+
+ // Look up the trust for this issuer.
+ IssuerEntry entry;
+ entry.cert = std::move(issuer);
+ entry.trust = trust_store_->GetTrust(entry.cert.get(), debug_data_);
+ entry.trust_and_key_id_match_ordering = TrustAndKeyIdentifierMatchToOrder(
+ cert(), entry.cert.get(), entry.trust);
+
+ issuers_.push_back(std::move(entry));
+ issuers_needs_sort_ = true;
+ }
+}
+
+void CertIssuersIter::DoAsyncIssuerQuery() {
+ DCHECK(!did_async_issuer_query_);
+ did_async_issuer_query_ = true;
+ cur_async_request_ = 0;
+ for (auto* cert_issuer_source : *cert_issuer_sources_) {
+ std::unique_ptr<CertIssuerSource::Request> request;
+ cert_issuer_source->AsyncGetIssuersOf(cert(), &request);
+ if (request) {
+ DVLOG(1) << "AsyncGetIssuersOf pending for " << CertDebugString(cert());
+ pending_async_requests_.push_back(std::move(request));
+ }
+ }
+}
+
+void CertIssuersIter::SortRemainingIssuers() {
+ if (!issuers_needs_sort_)
+ return;
+
+ std::stable_sort(
+ issuers_.begin() + cur_issuer_, issuers_.end(),
+ [](const IssuerEntry& issuer1, const IssuerEntry& issuer2) {
+ // TODO(crbug.com/635205): Add other prioritization hints. (See big list
+ // of possible sorting hints in RFC 4158.)
+ const bool issuer1_self_issued = issuer1.cert->normalized_subject() ==
+ issuer1.cert->normalized_issuer();
+ const bool issuer2_self_issued = issuer2.cert->normalized_subject() ==
+ issuer2.cert->normalized_issuer();
+ return std::tie(issuer1.trust_and_key_id_match_ordering,
+ issuer2_self_issued,
+ // Newer(larger) notBefore & notAfter dates are
+ // preferred, hence |issuer2| is on the LHS of
+ // the comparison and |issuer1| on the RHS.
+ issuer2.cert->tbs().validity_not_before,
+ issuer2.cert->tbs().validity_not_after) <
+ std::tie(issuer2.trust_and_key_id_match_ordering,
+ issuer1_self_issued,
+ issuer1.cert->tbs().validity_not_before,
+ issuer1.cert->tbs().validity_not_after);
+ });
+
+ issuers_needs_sort_ = false;
+}
+
+// CertIssuerIterPath tracks which certs are present in the path and prevents
+// paths from being built which repeat any certs (including different versions
+// of the same cert, based on Subject+SubjectAltName+SPKI).
+// (RFC 5280 forbids duplicate certificates per section 6.1, and RFC 4158
+// further recommends disallowing the same Subject+SubjectAltName+SPKI in
+// section 2.4.2.)
+class CertIssuerIterPath {
+ public:
+ // Returns true if |cert| is already present in the path.
+ bool IsPresent(const ParsedCertificate* cert) const {
+ return present_certs_.find(GetKey(cert)) != present_certs_.end();
+ }
+
+ // Appends |cert_issuers_iter| to the path. The cert referred to by
+ // |cert_issuers_iter| must not be present in the path already.
+ void Append(std::unique_ptr<CertIssuersIter> cert_issuers_iter) {
+ bool added =
+ present_certs_.insert(GetKey(cert_issuers_iter->cert())).second;
+ DCHECK(added);
+ cur_path_.push_back(std::move(cert_issuers_iter));
+ }
+
+ // Pops the last CertIssuersIter off the path.
+ void Pop() {
+ size_t num_erased = present_certs_.erase(GetKey(cur_path_.back()->cert()));
+ DCHECK_EQ(num_erased, 1U);
+ cur_path_.pop_back();
+ }
+
+ // Copies the ParsedCertificate elements of the current path to |*out_path|.
+ void CopyPath(ParsedCertificateList* out_path) {
+ out_path->clear();
+ for (const auto& node : cur_path_)
+ out_path->push_back(node->reference_cert());
+ }
+
+ // Returns true if the path is empty.
+ bool Empty() const { return cur_path_.empty(); }
+
+ // Returns the last CertIssuersIter in the path.
+ CertIssuersIter* back() { return cur_path_.back().get(); }
+
+ // Returns the length of the path.
+ size_t Length() const { return cur_path_.size(); }
+
+ std::string PathDebugString() {
+ std::string s;
+ for (const auto& node : cur_path_) {
+ if (!s.empty())
+ s += "\n";
+ s += " " + CertDebugString(node->cert());
+ }
+ return s;
+ }
+
+ private:
+ using Key =
+ std::tuple<base::StringPiece, base::StringPiece, base::StringPiece>;
+
+ static Key GetKey(const ParsedCertificate* cert) {
+ // TODO(mattm): ideally this would use a normalized version of
+ // SubjectAltName, but it's not that important just for LoopChecker.
+ //
+ // Note that subject_alt_names_extension().value will be empty if the cert
+ // had no SubjectAltName extension, so there is no need for a condition on
+ // has_subject_alt_names().
+ return Key(cert->normalized_subject().AsStringPiece(),
+ cert->subject_alt_names_extension().value.AsStringPiece(),
+ cert->tbs().spki_tlv.AsStringPiece());
+ }
+
+ std::vector<std::unique_ptr<CertIssuersIter>> cur_path_;
+
+ // This refers to data owned by |cur_path_|.
+ // TODO(mattm): use unordered_set. Requires making a hash function for Key.
+ std::set<Key> present_certs_;
+};
+
+} // namespace
+
+const ParsedCertificate* CertPathBuilderResultPath::GetTrustedCert() const {
+ if (certs.empty())
+ return nullptr;
+
+ switch (last_cert_trust.type) {
+ case CertificateTrustType::TRUSTED_ANCHOR:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
+ return certs.back().get();
+ case CertificateTrustType::UNSPECIFIED:
+ case CertificateTrustType::DISTRUSTED:
+ return nullptr;
+ }
+
+ NOTREACHED();
+ return nullptr;
+}
+
+// CertPathIter generates possible paths from |cert| to a trust anchor in
+// |trust_store|, using intermediates from the |cert_issuer_source| objects if
+// necessary.
+class CertPathIter {
+ public:
+ CertPathIter(scoped_refptr<ParsedCertificate> cert,
+ const TrustStore* trust_store,
+ base::SupportsUserData* debug_data);
+
+ CertPathIter(const CertPathIter&) = delete;
+ CertPathIter& operator=(const CertPathIter&) = delete;
+
+ // Adds a CertIssuerSource to provide intermediates for use in path building.
+ // The |*cert_issuer_source| must remain valid for the lifetime of the
+ // CertPathIter.
+ void AddCertIssuerSource(CertIssuerSource* cert_issuer_source);
+
+ // Gets the next candidate path, and fills it into |out_certs| and
+ // |out_last_cert_trust|. Note that the returned path is unverified and must
+ // still be run through a chain validator. If a candidate path could not be
+ // built, a partial path will be returned and |out_errors| will have an error
+ // added.
+ // If the return value is true, GetNextPath may be called again to backtrack
+ // and continue path building. Once all paths have been exhausted returns
+ // false. If deadline or iteration limit is exceeded, sets |out_certs| to the
+ // current path being explored and returns false.
+ bool GetNextPath(ParsedCertificateList* out_certs,
+ CertificateTrust* out_last_cert_trust,
+ CertPathErrors* out_errors,
+ const base::TimeTicks deadline,
+ uint32_t* iteration_count,
+ const uint32_t max_iteration_count,
+ const uint32_t max_path_building_depth);
+
+ private:
+ // Stores the next candidate issuer, until it is used during the
+ // STATE_GET_NEXT_ISSUER_COMPLETE step.
+ IssuerEntry next_issuer_;
+ // The current path being explored, made up of CertIssuerIters. Each node
+ // keeps track of the state of searching for issuers of that cert, so that
+ // when backtracking it can resume the search where it left off.
+ CertIssuerIterPath cur_path_;
+ // The CertIssuerSources for retrieving candidate issuers.
+ CertIssuerSources cert_issuer_sources_;
+ // The TrustStore for checking if a path ends in a trust anchor.
+ raw_ptr<const TrustStore> trust_store_;
+
+ raw_ptr<base::SupportsUserData> debug_data_;
+};
+
+CertPathIter::CertPathIter(scoped_refptr<ParsedCertificate> cert,
+ const TrustStore* trust_store,
+ base::SupportsUserData* debug_data)
+ : trust_store_(trust_store), debug_data_(debug_data) {
+ // Initialize |next_issuer_| to the target certificate.
+ next_issuer_.cert = std::move(cert);
+ next_issuer_.trust =
+ trust_store_->GetTrust(next_issuer_.cert.get(), debug_data_);
+}
+
+void CertPathIter::AddCertIssuerSource(CertIssuerSource* cert_issuer_source) {
+ cert_issuer_sources_.push_back(cert_issuer_source);
+}
+
+bool CertPathIter::GetNextPath(ParsedCertificateList* out_certs,
+ CertificateTrust* out_last_cert_trust,
+ CertPathErrors* out_errors,
+ const base::TimeTicks deadline,
+ uint32_t* iteration_count,
+ const uint32_t max_iteration_count,
+ const uint32_t max_path_building_depth) {
+ out_certs->clear();
+ *out_last_cert_trust = CertificateTrust::ForUnspecified();
+
+ while (true) {
+ if (!deadline.is_null() && base::TimeTicks::Now() > deadline) {
+ if (cur_path_.Empty()) {
+ // If the deadline is already expired before the first call to
+ // GetNextPath, cur_path_ will be empty. Return the leaf cert in that
+ // case.
+ if (next_issuer_.cert)
+ out_certs->push_back(next_issuer_.cert);
+ } else {
+ cur_path_.CopyPath(out_certs);
+ }
+ out_errors->GetOtherErrors()->AddError(cert_errors::kDeadlineExceeded);
+ return false;
+ }
+
+ // We are not done yet, so if the current path is at the depth limit then
+ // we must backtrack to find an acceptable solution.
+ if (max_path_building_depth > 0 &&
+ cur_path_.Length() >= max_path_building_depth) {
+ cur_path_.CopyPath(out_certs);
+ out_errors->GetOtherErrors()->AddError(cert_errors::kDepthLimitExceeded);
+ DVLOG(1) << "CertPathIter reached depth limit. Returning partial path "
+ "and backtracking:\n"
+ << PathDebugString(*out_certs);
+ cur_path_.Pop();
+ return true;
+ }
+
+ if (!next_issuer_.cert) {
+ if (cur_path_.Empty()) {
+ DVLOG(1) << "CertPathIter exhausted all paths...";
+ return false;
+ }
+
+ (*iteration_count)++;
+ if (max_iteration_count > 0 && *iteration_count > max_iteration_count) {
+ cur_path_.CopyPath(out_certs);
+ out_errors->GetOtherErrors()->AddError(
+ cert_errors::kIterationLimitExceeded);
+ return false;
+ }
+
+ cur_path_.back()->GetNextIssuer(&next_issuer_);
+ if (!next_issuer_.cert) {
+ if (!cur_path_.back()->had_non_skipped_issuers()) {
+ // If the end of a path was reached without finding an anchor, return
+ // the partial path before backtracking.
+ cur_path_.CopyPath(out_certs);
+ out_errors->GetErrorsForCert(out_certs->size() - 1)
+ ->AddError(cert_errors::kNoIssuersFound);
+ DVLOG(1) << "CertPathIter returning partial path and backtracking:\n"
+ << PathDebugString(*out_certs);
+ cur_path_.Pop();
+ return true;
+ } else {
+ // No more issuers for current chain, go back up and see if there are
+ // any more for the previous cert.
+ DVLOG(1) << "CertPathIter backtracking...";
+ cur_path_.Pop();
+ continue;
+ }
+ }
+ }
+
+ // If the cert is trusted but is the leaf, treat it as having unspecified
+ // trust. This may allow a successful path to be built to a different root
+ // (or to the same cert if it's self-signed).
+ switch (next_issuer_.trust.type) {
+ case CertificateTrustType::TRUSTED_ANCHOR:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
+ if (cur_path_.Empty()) {
+ DVLOG(1) << "Leaf is a trust anchor, considering as UNSPECIFIED";
+ next_issuer_.trust = CertificateTrust::ForUnspecified();
+ }
+ break;
+ case CertificateTrustType::DISTRUSTED:
+ case CertificateTrustType::UNSPECIFIED:
+ // No override necessary.
+ break;
+ }
+
+ switch (next_issuer_.trust.type) {
+ // If the trust for this issuer is "known" (either because it is
+ // distrusted, or because it is trusted) then stop building and return the
+ // path.
+ case CertificateTrustType::DISTRUSTED:
+ case CertificateTrustType::TRUSTED_ANCHOR:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS: {
+ // If the issuer has a known trust level, can stop building the path.
+ DVLOG(2) << "CertPathIter got anchor: "
+ << CertDebugString(next_issuer_.cert.get());
+ cur_path_.CopyPath(out_certs);
+ out_certs->push_back(std::move(next_issuer_.cert));
+ DVLOG(1) << "CertPathIter returning path:\n"
+ << PathDebugString(*out_certs);
+ *out_last_cert_trust = next_issuer_.trust;
+ next_issuer_ = IssuerEntry();
+ return true;
+ }
+ case CertificateTrustType::UNSPECIFIED: {
+ // Skip this cert if it is already in the chain.
+ if (cur_path_.IsPresent(next_issuer_.cert.get())) {
+ cur_path_.back()->increment_skipped_issuer_count();
+ DVLOG(1) << "CertPathIter skipping dupe cert: "
+ << CertDebugString(next_issuer_.cert.get());
+ next_issuer_ = IssuerEntry();
+ continue;
+ }
+
+ cur_path_.Append(std::make_unique<CertIssuersIter>(
+ std::move(next_issuer_.cert), &cert_issuer_sources_, trust_store_,
+ debug_data_));
+ next_issuer_ = IssuerEntry();
+ DVLOG(1) << "CertPathIter cur_path_ =\n" << cur_path_.PathDebugString();
+ // Continue descending the tree.
+ continue;
+ }
+ }
+ }
+}
+
+CertPathBuilderResultPath::CertPathBuilderResultPath() = default;
+CertPathBuilderResultPath::~CertPathBuilderResultPath() = default;
+
+bool CertPathBuilderResultPath::IsValid() const {
+ return GetTrustedCert() && !errors.ContainsHighSeverityErrors();
+}
+
+CertPathBuilder::Result::Result() = default;
+CertPathBuilder::Result::Result(Result&&) = default;
+CertPathBuilder::Result::~Result() = default;
+CertPathBuilder::Result& CertPathBuilder::Result::operator=(Result&&) = default;
+
+bool CertPathBuilder::Result::HasValidPath() const {
+ return GetBestValidPath() != nullptr;
+}
+
+bool CertPathBuilder::Result::AnyPathContainsError(CertErrorId error_id) const {
+ for (const auto& path : paths) {
+ if (path->errors.ContainsError(error_id))
+ return true;
+ }
+
+ return false;
+}
+
+const CertPathBuilderResultPath* CertPathBuilder::Result::GetBestValidPath()
+ const {
+ const CertPathBuilderResultPath* result_path = GetBestPathPossiblyInvalid();
+
+ if (result_path && result_path->IsValid())
+ return result_path;
+
+ return nullptr;
+}
+
+const CertPathBuilderResultPath*
+CertPathBuilder::Result::GetBestPathPossiblyInvalid() const {
+ DCHECK((paths.empty() && best_result_index == 0) ||
+ best_result_index < paths.size());
+
+ if (best_result_index >= paths.size())
+ return nullptr;
+
+ return paths[best_result_index].get();
+}
+
+CertPathBuilder::CertPathBuilder(
+ scoped_refptr<ParsedCertificate> cert,
+ TrustStore* trust_store,
+ CertPathBuilderDelegate* delegate,
+ const der::GeneralizedTime& time,
+ KeyPurpose key_purpose,
+ InitialExplicitPolicy initial_explicit_policy,
+ const std::set<der::Input>& user_initial_policy_set,
+ InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
+ InitialAnyPolicyInhibit initial_any_policy_inhibit)
+ : cert_path_iter_(
+ std::make_unique<CertPathIter>(std::move(cert),
+ trust_store,
+ /*debug_data=*/&out_result_)),
+ delegate_(delegate),
+ time_(time),
+ key_purpose_(key_purpose),
+ initial_explicit_policy_(initial_explicit_policy),
+ user_initial_policy_set_(user_initial_policy_set),
+ initial_policy_mapping_inhibit_(initial_policy_mapping_inhibit),
+ initial_any_policy_inhibit_(initial_any_policy_inhibit) {
+ DCHECK(delegate);
+ // The TrustStore also implements the CertIssuerSource interface.
+ AddCertIssuerSource(trust_store);
+}
+
+CertPathBuilder::~CertPathBuilder() = default;
+
+void CertPathBuilder::AddCertIssuerSource(
+ CertIssuerSource* cert_issuer_source) {
+ cert_path_iter_->AddCertIssuerSource(cert_issuer_source);
+}
+
+void CertPathBuilder::SetIterationLimit(uint32_t limit) {
+ max_iteration_count_ = limit;
+}
+
+void CertPathBuilder::SetDeadline(base::TimeTicks deadline) {
+ deadline_ = deadline;
+}
+
+void CertPathBuilder::SetDepthLimit(uint32_t limit) {
+ max_path_building_depth_ = limit;
+}
+
+void CertPathBuilder::SetExploreAllPaths(bool explore_all_paths) {
+ explore_all_paths_ = explore_all_paths;
+}
+
+CertPathBuilder::Result CertPathBuilder::Run() {
+ uint32_t iteration_count = 0;
+
+ while (true) {
+ std::unique_ptr<CertPathBuilderResultPath> result_path =
+ std::make_unique<CertPathBuilderResultPath>();
+
+ if (!cert_path_iter_->GetNextPath(
+ &result_path->certs, &result_path->last_cert_trust,
+ &result_path->errors, deadline_, &iteration_count,
+ max_iteration_count_, max_path_building_depth_)) {
+ // There are no more paths to check or limits were exceeded.
+ if (result_path->errors.ContainsError(
+ cert_errors::kIterationLimitExceeded)) {
+ out_result_.exceeded_iteration_limit = true;
+ }
+ if (result_path->errors.ContainsError(cert_errors::kDeadlineExceeded)) {
+ out_result_.exceeded_deadline = true;
+ }
+ if (!result_path->certs.empty()) {
+ // It shouldn't be possible to get here without adding one of the
+ // errors above, but just in case, add an error if there isn't one
+ // already.
+ if (!result_path->errors.ContainsHighSeverityErrors()) {
+ result_path->errors.GetOtherErrors()->AddError(
+ cert_errors::kInternalError);
+ }
+ AddResultPath(std::move(result_path));
+ }
+ out_result_.iteration_count = iteration_count;
+ RecordIterationCountHistogram(iteration_count);
+ return std::move(out_result_);
+ }
+
+ if (result_path->last_cert_trust.HasUnspecifiedTrust()) {
+ // Partial path, don't attempt to verify. Just double check that it is
+ // marked with an error, and move on.
+ if (!result_path->errors.ContainsHighSeverityErrors()) {
+ result_path->errors.GetOtherErrors()->AddError(
+ cert_errors::kInternalError);
+ }
+ } else {
+ // Verify the entire certificate chain.
+ VerifyCertificateChain(
+ result_path->certs, result_path->last_cert_trust, delegate_, time_,
+ key_purpose_, initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_,
+ &result_path->user_constrained_policy_set, &result_path->errors);
+ }
+
+ DVLOG(1) << "CertPathBuilder VerifyCertificateChain errors:\n"
+ << result_path->errors.ToDebugString(result_path->certs);
+
+ // Give the delegate a chance to add errors to the path.
+ delegate_->CheckPathAfterVerification(*this, result_path.get());
+
+ bool path_is_good = result_path->IsValid();
+
+ AddResultPath(std::move(result_path));
+
+ if (path_is_good && !explore_all_paths_) {
+ out_result_.iteration_count = iteration_count;
+ RecordIterationCountHistogram(iteration_count);
+ // Found a valid path, return immediately.
+ return std::move(out_result_);
+ }
+ // Path did not verify. Try more paths.
+ }
+}
+
+void CertPathBuilder::AddResultPath(
+ std::unique_ptr<CertPathBuilderResultPath> result_path) {
+ // TODO(mattm): If there are no valid paths, set best_result_index based on
+ // number or severity of errors. If there are multiple valid paths, could set
+ // best_result_index based on prioritization (since due to AIA and such, the
+ // actual order results were discovered may not match the ideal).
+ if (!out_result_.HasValidPath()) {
+ const CertPathBuilderResultPath* old_best_path =
+ out_result_.GetBestPathPossiblyInvalid();
+ // If |result_path| is a valid path or if the previous best result did not
+ // end in a trust anchor but the |result_path| does, then update the best
+ // result to the new result.
+ if (result_path->IsValid() ||
+ (!result_path->last_cert_trust.HasUnspecifiedTrust() && old_best_path &&
+ old_best_path->last_cert_trust.HasUnspecifiedTrust())) {
+ out_result_.best_result_index = out_result_.paths.size();
+ }
+ }
+ if (result_path->certs.size() > out_result_.max_depth_seen) {
+ out_result_.max_depth_seen = result_path->certs.size();
+ }
+ out_result_.paths.push_back(std::move(result_path));
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/path_builder.h b/chromium/net/cert/pki/path_builder.h
new file mode 100644
index 00000000000..c4bd8a72581
--- /dev/null
+++ b/chromium/net/cert/pki/path_builder.h
@@ -0,0 +1,247 @@
+// Copyright 2016 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 NET_CERT_PKI_PATH_BUILDER_H_
+#define NET_CERT_PKI_PATH_BUILDER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/supports_user_data.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/cert/pki/trust_store.h"
+#include "net/cert/pki/verify_certificate_chain.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+
+namespace net {
+
+namespace der {
+struct GeneralizedTime;
+}
+
+class CertPathBuilder;
+class CertPathIter;
+class CertIssuerSource;
+
+// Base class for custom data that CertPathBuilderDelegate can attach to paths.
+class NET_EXPORT CertPathBuilderDelegateData {
+ public:
+ virtual ~CertPathBuilderDelegateData() = default;
+};
+
+// Represents a single candidate path that was built or is being processed.
+//
+// This is used both to represent valid paths, as well as invalid/partial ones.
+//
+// Consumers must use |IsValid()| to test whether the
+// CertPathBuilderResultPath is the result of a successful certificate
+// verification.
+struct NET_EXPORT CertPathBuilderResultPath {
+ CertPathBuilderResultPath();
+ ~CertPathBuilderResultPath();
+
+ // Returns true if the candidate path is valid. A "valid" path is one which
+ // chains to a trusted root, and did not have any high severity errors added
+ // to it during certificate verification.
+ bool IsValid() const;
+
+ // Returns the chain's root certificate or nullptr if the chain doesn't
+ // chain to a trust anchor.
+ const ParsedCertificate* GetTrustedCert() const;
+
+ // Path in the forward direction:
+ //
+ // certs[0] is the target certificate
+ // certs[i] was issued by certs[i+1]
+ // certs.back() is the root certificate (which may or may not be trusted).
+ ParsedCertificateList certs;
+
+ // Describes the trustedness of the final certificate in the chain,
+ // |certs.back()|
+ //
+ // For result paths where |IsValid()|, the final certificate is trusted.
+ // However for failed or partially constructed paths the final certificate may
+ // not be a trust anchor.
+ CertificateTrust last_cert_trust;
+
+ // The set of policies that the certificate is valid for (of the
+ // subset of policies user requested during verification).
+ std::set<der::Input> user_constrained_policy_set;
+
+ // Slot for per-path data that may set by CertPathBuilderDelegate. The
+ // specific type is chosen by the delegate. Can be nullptr when unused.
+ std::unique_ptr<CertPathBuilderDelegateData> delegate_data;
+
+ // The set of errors and warnings associated with this path (bucketed
+ // per-certificate). Note that consumers should always use |IsValid()| to
+ // determine validity of the CertPathBuilderResultPath, and not just inspect
+ // |errors|.
+ CertPathErrors errors;
+};
+
+// CertPathBuilderDelegate controls policies for certificate verification and
+// path building.
+class NET_EXPORT CertPathBuilderDelegate
+ : public VerifyCertificateChainDelegate {
+ public:
+ // This is called during path building on candidate paths which have already
+ // been run through RFC 5280 verification. |path| may already have errors
+ // and warnings set on it. Delegates can "reject" a candidate path from path
+ // building by adding high severity errors.
+ virtual void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+ CertPathBuilderResultPath* path) = 0;
+};
+
+// Checks whether a certificate is trusted by building candidate paths to trust
+// anchors and verifying those paths according to RFC 5280. Each instance of
+// CertPathBuilder is used for a single verification.
+//
+// WARNING: This implementation is currently experimental. Consult an OWNER
+// before using it.
+class NET_EXPORT CertPathBuilder {
+ public:
+ // Provides the overall result of path building. This includes the paths that
+ // were attempted.
+ struct NET_EXPORT Result : public base::SupportsUserData {
+ Result();
+ Result(Result&&);
+
+ Result(const Result&) = delete;
+ Result& operator=(const Result&) = delete;
+
+ ~Result() override;
+ Result& operator=(Result&&);
+
+ // Returns true if there was a valid path.
+ bool HasValidPath() const;
+
+ // Returns true if any of the attempted paths contain |error_id|.
+ bool AnyPathContainsError(CertErrorId error_id) const;
+
+ // Returns the CertPathBuilderResultPath for the best valid path, or nullptr
+ // if there was none.
+ const CertPathBuilderResultPath* GetBestValidPath() const;
+
+ // Returns the best CertPathBuilderResultPath or nullptr if there was none.
+ const CertPathBuilderResultPath* GetBestPathPossiblyInvalid() const;
+
+ // List of paths that were attempted and the result for each.
+ std::vector<std::unique_ptr<CertPathBuilderResultPath>> paths;
+
+ // Index into |paths|. Before use, |paths.empty()| must be checked.
+ // NOTE: currently the definition of "best" is fairly limited. Valid is
+ // better than invalid, but otherwise nothing is guaranteed.
+ size_t best_result_index = 0;
+
+ // The iteration count reached by path building.
+ uint32_t iteration_count = 0;
+
+ // The max depth seen while path building.
+ uint32_t max_depth_seen = 0;
+
+ // True if the search stopped because it exceeded the iteration limit
+ // configured with |SetIterationLimit|.
+ bool exceeded_iteration_limit = false;
+
+ // True if the search stopped because it exceeded the deadline configured
+ // with |SetDeadline|.
+ bool exceeded_deadline = false;
+ };
+
+ // Creates a CertPathBuilder that attempts to find a path from |cert| to a
+ // trust anchor in |trust_store| and is valid at |time|.
+ //
+ // The caller must keep |trust_store| and |delegate| valid for the lifetime
+ // of the CertPathBuilder.
+ //
+ // See VerifyCertificateChain() for a more detailed explanation of the
+ // same-named parameters not defined below.
+ //
+ // * |delegate|: Must be non-null. The delegate is called at various points in
+ // path building to verify specific parts of certificates or the
+ // final chain. See CertPathBuilderDelegate and
+ // VerifyCertificateChainDelegate for more information.
+ CertPathBuilder(scoped_refptr<ParsedCertificate> cert,
+ TrustStore* trust_store,
+ CertPathBuilderDelegate* delegate,
+ const der::GeneralizedTime& time,
+ KeyPurpose key_purpose,
+ InitialExplicitPolicy initial_explicit_policy,
+ const std::set<der::Input>& user_initial_policy_set,
+ InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
+ InitialAnyPolicyInhibit initial_any_policy_inhibit);
+
+ CertPathBuilder(const CertPathBuilder&) = delete;
+ CertPathBuilder& operator=(const CertPathBuilder&) = delete;
+
+ ~CertPathBuilder();
+
+ // Adds a CertIssuerSource to provide intermediates for use in path building.
+ // Multiple sources may be added. Must not be called after Run is called.
+ // The |*cert_issuer_source| must remain valid for the lifetime of the
+ // CertPathBuilder.
+ //
+ // (If no issuer sources are added, the target certificate will only verify if
+ // it is a trust anchor or is directly signed by a trust anchor.)
+ void AddCertIssuerSource(CertIssuerSource* cert_issuer_source);
+
+ // Sets a limit to the number of times to repeat the process of considering a
+ // new intermediate over all potential paths. Setting |limit| to 0 disables
+ // the iteration limit, which is the default.
+ void SetIterationLimit(uint32_t limit);
+
+ // Sets a deadline for completing path building. If |deadline| has passed and
+ // path building has not completed, path building will stop. Note that this
+ // is not a hard limit, there is no guarantee how far past |deadline| time
+ // will be when path building is aborted.
+ void SetDeadline(base::TimeTicks deadline);
+
+ // Sets a limit to the number of certificates to be added in a path from leaf
+ // to root. Setting |limit| to 0 disables this limit, which is the default.
+ void SetDepthLimit(uint32_t limit);
+
+ // If |explore_all_paths| is false (the default), path building will stop as
+ // soon as a valid path is found. If |explore_all_paths| is true, path
+ // building will continue until all possible paths have been exhausted (or
+ // iteration limit / deadline is exceeded).
+ void SetExploreAllPaths(bool explore_all_paths);
+
+ // Returns the deadline for path building, if any. If no deadline is set,
+ // |deadline().is_null()| will be true.
+ base::TimeTicks deadline() const { return deadline_; }
+
+ // Executes verification of the target certificate.
+ //
+ // Run must not be called more than once on each CertPathBuilder instance.
+ Result Run();
+
+ private:
+ void AddResultPath(std::unique_ptr<CertPathBuilderResultPath> result_path);
+
+ // |out_result_| may be referenced by other members, so should be initialized
+ // first.
+ Result out_result_;
+
+ std::unique_ptr<CertPathIter> cert_path_iter_;
+ raw_ptr<CertPathBuilderDelegate> delegate_;
+ const der::GeneralizedTime time_;
+ const KeyPurpose key_purpose_;
+ const InitialExplicitPolicy initial_explicit_policy_;
+ const std::set<der::Input> user_initial_policy_set_;
+ const InitialPolicyMappingInhibit initial_policy_mapping_inhibit_;
+ const InitialAnyPolicyInhibit initial_any_policy_inhibit_;
+ uint32_t max_iteration_count_ = 0;
+ base::TimeTicks deadline_;
+ uint32_t max_path_building_depth_ = 0;
+ bool explore_all_paths_ = false;
+};
+
+} // namespace net
+
+#endif // NET_CERT_PKI_PATH_BUILDER_H_
diff --git a/chromium/net/cert/pki/path_builder_pkits_unittest.cc b/chromium/net/cert/pki/path_builder_pkits_unittest.cc
new file mode 100644
index 00000000000..e082f7d55fc
--- /dev/null
+++ b/chromium/net/cert/pki/path_builder_pkits_unittest.cc
@@ -0,0 +1,300 @@
+// Copyright 2016 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 "net/cert/pki/path_builder.h"
+
+#include "base/time/time.h"
+#include "net/base/net_errors.h"
+#include "net/cert/pki/cert_issuer_source_static.h"
+#include "net/cert/pki/common_cert_errors.h"
+#include "net/cert/pki/crl.h"
+#include "net/cert/pki/parse_certificate.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/cert/pki/simple_path_builder_delegate.h"
+#include "net/cert/pki/trust_store_in_memory.h"
+#include "net/cert/pki/verify_certificate_chain.h"
+#include "net/der/encode_values.h"
+#include "net/der/input.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+
+#include "net/cert/pki/nist_pkits_unittest.h"
+
+namespace net {
+
+namespace {
+
+class CrlCheckingPathBuilderDelegate : public SimplePathBuilderDelegate {
+ public:
+ CrlCheckingPathBuilderDelegate(const std::vector<std::string>& der_crls,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age,
+ size_t min_rsa_modulus_length_bits,
+ DigestPolicy digest_policy)
+ : SimplePathBuilderDelegate(min_rsa_modulus_length_bits, digest_policy),
+ der_crls_(der_crls),
+ verify_time_(verify_time),
+ max_age_(max_age) {}
+
+ void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+ CertPathBuilderResultPath* path) override {
+ SimplePathBuilderDelegate::CheckPathAfterVerification(path_builder, path);
+
+ if (!path->IsValid())
+ return;
+
+ // It would be preferable if this test could use
+ // CheckValidatedChainRevocation somehow, but that only supports getting
+ // CRLs by http distributionPoints. So this just settles for writing a
+ // little bit of wrapper code to test CheckCRL directly.
+ const ParsedCertificateList& certs = path->certs;
+ for (size_t reverse_i = 0; reverse_i < certs.size(); ++reverse_i) {
+ size_t i = certs.size() - reverse_i - 1;
+
+ // Trust anchors bypass OCSP/CRL revocation checks. (The only way to
+ // revoke trust anchors is via CRLSet or the built-in SPKI block list).
+ if (reverse_i == 0 && path->last_cert_trust.IsTrustAnchor())
+ continue;
+
+ // RFC 5280 6.3.3. [If the CRL was not specified in a distribution
+ // point], assume a DP with both the reasons and the
+ // cRLIssuer fields omitted and a distribution point
+ // name of the certificate issuer.
+ // Since this implementation only supports URI names in distribution
+ // points, this means a default-initialized ParsedDistributionPoint is
+ // sufficient.
+ ParsedDistributionPoint fake_cert_dp;
+ const ParsedDistributionPoint* cert_dp = &fake_cert_dp;
+
+ // If the target cert does have a distribution point, use it.
+ std::vector<ParsedDistributionPoint> distribution_points;
+ ParsedExtension crl_dp_extension;
+ if (certs[i]->GetExtension(der::Input(kCrlDistributionPointsOid),
+ &crl_dp_extension)) {
+ ASSERT_TRUE(ParseCrlDistributionPoints(crl_dp_extension.value,
+ &distribution_points));
+ // TODO(mattm): some test cases (some of the 4.14.* onlySomeReasons
+ // tests)) have two CRLs and two distribution points, one point
+ // corresponding to each CRL. Should select the matching point for
+ // each CRL. (Doesn't matter currently since we don't support
+ // reasons.)
+
+ // Look for a DistributionPoint without reasons.
+ for (const auto& dp : distribution_points) {
+ if (!dp.reasons) {
+ cert_dp = &dp;
+ break;
+ }
+ }
+ // If there were only DistributionPoints with reasons, just use the
+ // first one.
+ if (cert_dp == &fake_cert_dp && !distribution_points.empty())
+ cert_dp = &distribution_points[0];
+ }
+
+ bool cert_good = false;
+
+ for (const auto& der_crl : der_crls_) {
+ CRLRevocationStatus crl_status =
+ CheckCRL(der_crl, certs, i, *cert_dp, verify_time_, max_age_);
+ if (crl_status == CRLRevocationStatus::REVOKED) {
+ path->errors.GetErrorsForCert(i)->AddError(
+ cert_errors::kCertificateRevoked);
+ return;
+ }
+ if (crl_status == CRLRevocationStatus::GOOD) {
+ cert_good = true;
+ break;
+ }
+ }
+ if (!cert_good) {
+ // PKITS tests assume hard-fail revocation checking.
+ // From PKITS 4.4: "When running the tests in this section, the
+ // application should be configured in such a way that the
+ // certification path is not accepted unless valid, up-to-date
+ // revocation data is available for every certificate in the path."
+ path->errors.GetErrorsForCert(i)->AddError(
+ cert_errors::kUnableToCheckRevocation);
+ }
+ }
+ }
+
+ private:
+ std::vector<std::string> der_crls_;
+ const base::Time verify_time_;
+ const base::TimeDelta max_age_;
+};
+
+class PathBuilderPkitsTestDelegate {
+ public:
+ static void RunTest(std::vector<std::string> cert_ders,
+ std::vector<std::string> crl_ders,
+ const PkitsTestInfo& orig_info) {
+ PkitsTestInfo info = orig_info;
+
+ ASSERT_FALSE(cert_ders.empty());
+ ParsedCertificateList certs;
+ for (const std::string& der : cert_ders) {
+ CertErrors errors;
+ ASSERT_TRUE(ParsedCertificate::CreateAndAddToVector(
+ bssl::UniquePtr<CRYPTO_BUFFER>(
+ CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t*>(der.data()),
+ der.size(), nullptr)),
+ {}, &certs, &errors))
+ << errors.ToDebugString();
+ }
+ // First entry in the PKITS chain is the trust anchor.
+ // TODO(mattm): test with all possible trust anchors in the trust store?
+ TrustStoreInMemory trust_store;
+
+ trust_store.AddTrustAnchor(certs[0]);
+
+ // TODO(mattm): test with other irrelevant certs in cert_issuer_sources?
+ CertIssuerSourceStatic cert_issuer_source;
+ for (size_t i = 1; i < cert_ders.size() - 1; ++i)
+ cert_issuer_source.AddCert(certs[i]);
+
+ scoped_refptr<ParsedCertificate> target_cert(certs.back());
+
+ base::Time verify_time;
+ ASSERT_TRUE(der::GeneralizedTimeToTime(info.time, &verify_time));
+ CrlCheckingPathBuilderDelegate path_builder_delegate(
+ crl_ders, verify_time, /*max_age=*/base::Days(365 * 2), 1024,
+ SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+
+ base::StringPiece test_number = info.test_number;
+ if (test_number == "4.4.19" || test_number == "4.5.3" ||
+ test_number == "4.5.4" || test_number == "4.5.6") {
+ // 4.4.19 - fails since CRL is signed by a certificate that is not part
+ // of the verified chain, which is not supported.
+ // 4.5.3 - fails since non-URI distribution point names are not supported
+ // 4.5.4, 4.5.6 - fails since CRL is signed by a certificate that is not
+ // part of verified chain, and also non-URI distribution
+ // point names not supported
+ info.should_validate = false;
+ } else if (test_number == "4.14.1" || test_number == "4.14.4" ||
+ test_number == "4.14.5" || test_number == "4.14.7" ||
+ test_number == "4.14.18" || test_number == "4.14.19" ||
+ test_number == "4.14.22" || test_number == "4.14.24" ||
+ test_number == "4.14.25" || test_number == "4.14.28" ||
+ test_number == "4.14.29" || test_number == "4.14.30" ||
+ test_number == "4.14.33") {
+ // 4.14 tests:
+ // .1 - fails since non-URI distribution point names not supported
+ // .2, .3 - fails since non-URI distribution point names not supported
+ // (but test is expected to fail for other reason)
+ // .4, .5 - fails since non-URI distribution point names not supported,
+ // also uses nameRelativeToCRLIssuer which is not supported
+ // .6 - fails since non-URI distribution point names not supported, also
+ // uses nameRelativeToCRLIssuer which is not supported (but test is
+ // expected to fail for other reason)
+ // .7 - fails since relative distributionPointName not supported
+ // .8, .9 - fails since relative distributionPointName not supported (but
+ // test is expected to fail for other reason)
+ // .10, .11, .12, .13, .14, .27, .35 - PASS
+ // .15, .16, .17, .20, .21 - fails since onlySomeReasons is not supported
+ // (but test is expected to fail for other
+ // reason)
+ // .18, .19 - fails since onlySomeReasons is not supported
+ // .22, .24, .25, .28, .29, .30, .33 - fails since indirect CRLs are not
+ // supported
+ // .23, .26, .31, .32, .34 - fails since indirect CRLs are not supported
+ // (but test is expected to fail for other
+ // reason)
+ info.should_validate = false;
+ } else if (test_number == "4.15.1" || test_number == "4.15.5") {
+ // 4.15 tests:
+ // .1 - fails due to unhandled critical deltaCRLIndicator extension
+ // .2, .3, .6, .7, .8, .9, .10 - PASS since expected cert status is
+ // reflected in base CRL and delta CRL is
+ // ignored
+ // .5 - fails, cert status is "on hold" in base CRL but the delta CRL
+ // which removes the cert from CRL is ignored
+ info.should_validate = false;
+ } else if (test_number == "4.15.4") {
+ // 4.15.4 - Invalid delta-CRL Test4 has the target cert marked revoked in
+ // a delta-CRL. Since delta-CRLs are not supported, the chain validates
+ // successfully.
+ info.should_validate = true;
+ }
+
+ CertPathBuilder path_builder(
+ std::move(target_cert), &trust_store, &path_builder_delegate, info.time,
+ KeyPurpose::ANY_EKU, info.initial_explicit_policy,
+ info.initial_policy_set, info.initial_policy_mapping_inhibit,
+ info.initial_inhibit_any_policy);
+ path_builder.AddCertIssuerSource(&cert_issuer_source);
+
+ CertPathBuilder::Result result = path_builder.Run();
+
+ if (info.should_validate != result.HasValidPath()) {
+ for (size_t i = 0; i < result.paths.size(); ++i) {
+ const net::CertPathBuilderResultPath* result_path =
+ result.paths[i].get();
+ LOG(ERROR) << "path " << i << " errors:\n"
+ << result_path->errors.ToDebugString(result_path->certs);
+ }
+ }
+
+ ASSERT_EQ(info.should_validate, result.HasValidPath());
+
+ if (result.HasValidPath()) {
+ EXPECT_EQ(info.user_constrained_policy_set,
+ result.GetBestValidPath()->user_constrained_policy_set);
+ }
+ }
+};
+
+} // namespace
+
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest01SignatureVerification,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest02ValidityPeriods,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest03VerifyingNameChaining,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest04BasicCertificateRevocationTests,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(
+ PathBuilder,
+ PkitsTest05VerifyingPathswithSelfIssuedCertificates,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest06VerifyingBasicConstraints,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest07KeyUsage,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest08CertificatePolicies,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest09RequireExplicitPolicy,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest10PolicyMappings,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest11InhibitPolicyMapping,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest12InhibitAnyPolicy,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest13NameConstraints,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest14DistributionPoints,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest15DeltaCRLs,
+ PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ PkitsTest16PrivateCertificateExtensions,
+ PathBuilderPkitsTestDelegate);
+
+} // namespace net
diff --git a/chromium/net/cert/pki/path_builder_unittest.cc b/chromium/net/cert/pki/path_builder_unittest.cc
new file mode 100644
index 00000000000..80c5baa5eae
--- /dev/null
+++ b/chromium/net/cert/pki/path_builder_unittest.cc
@@ -0,0 +1,2555 @@
+// Copyright 2016 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 "net/cert/pki/path_builder.h"
+
+#include "base/base_paths.h"
+#include "base/callback_forward.h"
+#include "base/containers/span.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "net/cert/pem.h"
+#include "net/cert/pki/cert_error_params.h"
+#include "net/cert/pki/cert_issuer_source_static.h"
+#include "net/cert/pki/common_cert_errors.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/cert/pki/simple_path_builder_delegate.h"
+#include "net/cert/pki/test_helpers.h"
+#include "net/cert/pki/trust_store_collection.h"
+#include "net/cert/pki/trust_store_in_memory.h"
+#include "net/cert/pki/verify_certificate_chain.h"
+#include "net/der/input.h"
+#include "net/net_buildflags.h"
+#include "net/test/test_certificate_data.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+
+#if BUILDFLAG(IS_WIN)
+#include "base/win/wincrypt_shim.h"
+#include "crypto/scoped_capi_types.h"
+#include "net/cert/internal/trust_store_win.h"
+#endif // BUILDFLAG(IS_WIN)
+
+namespace net {
+
+// TODO(crbug.com/634443): Assert the errors for each ResultPath.
+
+namespace {
+
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::SetArgPointee;
+using ::testing::StrictMock;
+
+// AsyncCertIssuerSourceStatic always returns its certs asynchronously.
+class AsyncCertIssuerSourceStatic : public CertIssuerSource {
+ public:
+ class StaticAsyncRequest : public Request {
+ public:
+ explicit StaticAsyncRequest(ParsedCertificateList&& issuers) {
+ issuers_.swap(issuers);
+ issuers_iter_ = issuers_.begin();
+ }
+
+ StaticAsyncRequest(const StaticAsyncRequest&) = delete;
+ StaticAsyncRequest& operator=(const StaticAsyncRequest&) = delete;
+
+ ~StaticAsyncRequest() override = default;
+
+ void GetNext(ParsedCertificateList* out_certs) override {
+ if (issuers_iter_ != issuers_.end())
+ out_certs->push_back(std::move(*issuers_iter_++));
+ }
+
+ ParsedCertificateList issuers_;
+ ParsedCertificateList::iterator issuers_iter_;
+ };
+
+ ~AsyncCertIssuerSourceStatic() override = default;
+
+ void SetAsyncGetCallback(base::RepeatingClosure closure) {
+ async_get_callback_ = std::move(closure);
+ }
+
+ void AddCert(scoped_refptr<ParsedCertificate> cert) {
+ static_cert_issuer_source_.AddCert(std::move(cert));
+ }
+
+ void SyncGetIssuersOf(const ParsedCertificate* cert,
+ ParsedCertificateList* issuers) override {}
+ void AsyncGetIssuersOf(const ParsedCertificate* cert,
+ std::unique_ptr<Request>* out_req) override {
+ num_async_gets_++;
+ ParsedCertificateList issuers;
+ static_cert_issuer_source_.SyncGetIssuersOf(cert, &issuers);
+ auto req = std::make_unique<StaticAsyncRequest>(std::move(issuers));
+ *out_req = std::move(req);
+ if (!async_get_callback_.is_null())
+ async_get_callback_.Run();
+ }
+ int num_async_gets() const { return num_async_gets_; }
+
+ private:
+ CertIssuerSourceStatic static_cert_issuer_source_;
+
+ int num_async_gets_ = 0;
+ base::RepeatingClosure async_get_callback_;
+};
+
+::testing::AssertionResult ReadTestPem(const std::string& file_name,
+ const std::string& block_name,
+ std::string* result) {
+ const PemBlockMapping mappings[] = {
+ {block_name.c_str(), result},
+ };
+
+ return ReadTestDataFromPemFile(file_name, mappings);
+}
+
+::testing::AssertionResult ReadTestCert(
+ const std::string& file_name,
+ scoped_refptr<ParsedCertificate>* result) {
+ std::string der;
+ ::testing::AssertionResult r = ReadTestPem(
+ "net/data/ssl/certificates/" + file_name, "CERTIFICATE", &der);
+ if (!r)
+ return r;
+ CertErrors errors;
+ *result = ParsedCertificate::Create(
+ bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(
+ reinterpret_cast<const uint8_t*>(der.data()), der.size(), nullptr)),
+ {}, &errors);
+ if (!*result) {
+ return ::testing::AssertionFailure()
+ << "ParseCertificate::Create() failed:\n"
+ << errors.ToDebugString();
+ }
+ return ::testing::AssertionSuccess();
+}
+
+const void* kKey = &kKey;
+class TrustStoreThatStoresUserData : public TrustStore {
+ public:
+ class Data : public base::SupportsUserData::Data {
+ public:
+ explicit Data(int value) : value(value) {}
+
+ int value = 0;
+ };
+
+ // TrustStore implementation:
+ void SyncGetIssuersOf(const ParsedCertificate* cert,
+ ParsedCertificateList* issuers) override {}
+ CertificateTrust GetTrust(const ParsedCertificate* cert,
+ base::SupportsUserData* debug_data) const override {
+ debug_data->SetUserData(kKey, std::make_unique<Data>(1234));
+ return CertificateTrust::ForUnspecified();
+ }
+};
+
+TEST(PathBuilderResultUserDataTest, ModifyUserDataInConstructor) {
+ scoped_refptr<ParsedCertificate> a_by_b;
+ ASSERT_TRUE(ReadTestCert("multi-root-A-by-B.pem", &a_by_b));
+ SimplePathBuilderDelegate delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+ der::GeneralizedTime verify_time = {2017, 3, 1, 0, 0, 0};
+ TrustStoreThatStoresUserData trust_store;
+
+ // |trust_store| will unconditionally store user data in the
+ // CertPathBuilder::Result. This ensures that the Result object has been
+ // initialized before the first GetTrust call occurs (otherwise the test will
+ // crash or fail on ASAN bots).
+ CertPathBuilder path_builder(
+ a_by_b, &trust_store, &delegate, verify_time, KeyPurpose::ANY_EKU,
+ InitialExplicitPolicy::kFalse, {der::Input(kAnyPolicyOid)},
+ InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse);
+ CertPathBuilder::Result result = path_builder.Run();
+ auto* data = static_cast<TrustStoreThatStoresUserData::Data*>(
+ result.GetUserData(kKey));
+ ASSERT_TRUE(data);
+ EXPECT_EQ(1234, data->value);
+}
+
+class PathBuilderMultiRootTest : public ::testing::Test {
+ public:
+ PathBuilderMultiRootTest()
+ : delegate_(1024,
+ SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1) {}
+
+ void SetUp() override {
+ ASSERT_TRUE(ReadTestCert("multi-root-A-by-B.pem", &a_by_b_));
+ ASSERT_TRUE(ReadTestCert("multi-root-B-by-C.pem", &b_by_c_));
+ ASSERT_TRUE(ReadTestCert("multi-root-B-by-F.pem", &b_by_f_));
+ ASSERT_TRUE(ReadTestCert("multi-root-C-by-D.pem", &c_by_d_));
+ ASSERT_TRUE(ReadTestCert("multi-root-C-by-E.pem", &c_by_e_));
+ ASSERT_TRUE(ReadTestCert("multi-root-D-by-D.pem", &d_by_d_));
+ ASSERT_TRUE(ReadTestCert("multi-root-E-by-E.pem", &e_by_e_));
+ ASSERT_TRUE(ReadTestCert("multi-root-F-by-E.pem", &f_by_e_));
+ }
+
+ protected:
+ scoped_refptr<ParsedCertificate> a_by_b_, b_by_c_, b_by_f_, c_by_d_, c_by_e_,
+ d_by_d_, e_by_e_, f_by_e_;
+
+ SimplePathBuilderDelegate delegate_;
+ der::GeneralizedTime time_ = {2017, 3, 1, 0, 0, 0};
+
+ const InitialExplicitPolicy initial_explicit_policy_ =
+ InitialExplicitPolicy::kFalse;
+ const std::set<der::Input> user_initial_policy_set_ = {
+ der::Input(kAnyPolicyOid)};
+ const InitialPolicyMappingInhibit initial_policy_mapping_inhibit_ =
+ InitialPolicyMappingInhibit::kFalse;
+ const InitialAnyPolicyInhibit initial_any_policy_inhibit_ =
+ InitialAnyPolicyInhibit::kFalse;
+};
+
+// Tests when the target cert has the same name and key as a trust anchor,
+// however is signed by a different trust anchor. This should successfully build
+// a path, however the trust anchor will be the signer of this cert.
+//
+// (This test is very similar to TestEndEntityHasSameNameAndSpkiAsTrustAnchor
+// but with different data; also in this test the target cert itself is in the
+// trust store).
+TEST_F(PathBuilderMultiRootTest, TargetHasNameAndSpkiOfTrustAnchor) {
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(a_by_b_);
+ trust_store.AddTrustAnchor(b_by_f_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+
+ auto result = path_builder.Run();
+
+ ASSERT_TRUE(result.HasValidPath());
+ const auto& path = *result.GetBestValidPath();
+ ASSERT_EQ(2U, path.certs.size());
+ EXPECT_EQ(a_by_b_, path.certs[0]);
+ EXPECT_EQ(b_by_f_, path.certs[1]);
+}
+
+// If the target cert is has the same name and key as a trust anchor, however
+// is NOT itself signed by a trust anchor, it fails. Although the provided SPKI
+// is trusted, the certificate contents cannot be verified.
+TEST_F(PathBuilderMultiRootTest, TargetWithSameNameAsTrustAnchorFails) {
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(a_by_b_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+
+ auto result = path_builder.Run();
+
+ EXPECT_FALSE(result.HasValidPath());
+ EXPECT_EQ(1U, result.max_depth_seen);
+}
+
+// Test a failed path building when the trust anchor is provided as a
+// supplemental certificate. Conceptually the following paths could be built:
+//
+// B(C) <- C(D) <- [Trust anchor D]
+// B(C) <- C(D) <- D(D) <- [Trust anchor D]
+//
+// However the second one is extraneous given the shorter path.
+TEST_F(PathBuilderMultiRootTest, SelfSignedTrustAnchorSupplementalCert) {
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(d_by_d_);
+
+ // The (extraneous) trust anchor D(D) is supplied as a certificate, as is the
+ // intermediate needed for path building C(D).
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(d_by_d_);
+ sync_certs.AddCert(c_by_d_);
+
+ // C(D) is not valid at this time, so path building will fail.
+ der::GeneralizedTime expired_time = {2016, 1, 1, 0, 0, 0};
+
+ CertPathBuilder path_builder(
+ b_by_c_, &trust_store, &delegate_, expired_time, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_FALSE(result.HasValidPath());
+ ASSERT_EQ(1U, result.paths.size());
+
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ const auto& path0 = *result.paths[0];
+ ASSERT_EQ(3U, path0.certs.size());
+ EXPECT_EQ(b_by_c_, path0.certs[0]);
+ EXPECT_EQ(c_by_d_, path0.certs[1]);
+ EXPECT_EQ(d_by_d_, path0.certs[2]);
+}
+
+// Test verifying a certificate that is a trust anchor.
+TEST_F(PathBuilderMultiRootTest, TargetIsSelfSignedTrustAnchor) {
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(e_by_e_);
+ // This is not necessary for the test, just an extra...
+ trust_store.AddTrustAnchor(f_by_e_);
+
+ CertPathBuilder path_builder(
+ e_by_e_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+
+ auto result = path_builder.Run();
+
+ ASSERT_TRUE(result.HasValidPath());
+
+ // Verifying a trusted leaf certificate is not permitted, however this
+ // certificate is self-signed, and can chain to itself.
+ const auto& path = *result.GetBestValidPath();
+ ASSERT_EQ(2U, path.certs.size());
+ EXPECT_EQ(e_by_e_, path.certs[0]);
+ EXPECT_EQ(e_by_e_, path.certs[1]);
+}
+
+// If the target cert is directly issued by a trust anchor, it should verify
+// without any intermediate certs being provided.
+TEST_F(PathBuilderMultiRootTest, TargetDirectlySignedByTrustAnchor) {
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(b_by_f_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+
+ auto result = path_builder.Run();
+
+ ASSERT_TRUE(result.HasValidPath());
+ const auto& path = *result.GetBestValidPath();
+ ASSERT_EQ(2U, path.certs.size());
+ EXPECT_EQ(a_by_b_, path.certs[0]);
+ EXPECT_EQ(b_by_f_, path.certs[1]);
+}
+
+// Test that async cert queries are not made if the path can be successfully
+// built with synchronously available certs.
+TEST_F(PathBuilderMultiRootTest, TriesSyncFirst) {
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(e_by_e_);
+
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_f_);
+ sync_certs.AddCert(f_by_e_);
+
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(b_by_c_);
+ async_certs.AddCert(c_by_e_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&async_certs);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_TRUE(result.HasValidPath());
+ EXPECT_EQ(0, async_certs.num_async_gets());
+}
+
+// If async queries are needed, all async sources will be queried
+// simultaneously.
+TEST_F(PathBuilderMultiRootTest, TestAsyncSimultaneous) {
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(e_by_e_);
+
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_c_);
+ sync_certs.AddCert(b_by_f_);
+
+ AsyncCertIssuerSourceStatic async_certs1;
+ async_certs1.AddCert(c_by_e_);
+
+ AsyncCertIssuerSourceStatic async_certs2;
+ async_certs2.AddCert(f_by_e_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&async_certs1);
+ path_builder.AddCertIssuerSource(&async_certs2);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_TRUE(result.HasValidPath());
+ EXPECT_EQ(1, async_certs1.num_async_gets());
+ EXPECT_EQ(1, async_certs2.num_async_gets());
+}
+
+// Test that PathBuilder does not generate longer paths than necessary if one of
+// the supplied certs is itself a trust anchor.
+TEST_F(PathBuilderMultiRootTest, TestLongChain) {
+ // Both D(D) and C(D) are trusted roots.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(d_by_d_);
+ trust_store.AddTrustAnchor(c_by_d_);
+
+ // Certs B(C), and C(D) are all supplied.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_c_);
+ sync_certs.AddCert(c_by_d_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ ASSERT_TRUE(result.HasValidPath());
+
+ // The result path should be A(B) <- B(C) <- C(D)
+ // not the longer but also valid A(B) <- B(C) <- C(D) <- D(D)
+ EXPECT_EQ(3U, result.GetBestValidPath()->certs.size());
+}
+
+// Test that PathBuilder will backtrack and try a different path if the first
+// one doesn't work out.
+TEST_F(PathBuilderMultiRootTest, TestBacktracking) {
+ // Only D(D) is a trusted root.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(d_by_d_);
+
+ // Certs B(F) and F(E) are supplied synchronously, thus the path
+ // A(B) <- B(F) <- F(E) should be built first, though it won't verify.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_f_);
+ sync_certs.AddCert(f_by_e_);
+
+ // Certs B(C), and C(D) are supplied asynchronously, so the path
+ // A(B) <- B(C) <- C(D) <- D(D) should be tried second.
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(b_by_c_);
+ async_certs.AddCert(c_by_d_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+ path_builder.AddCertIssuerSource(&async_certs);
+
+ auto result = path_builder.Run();
+
+ ASSERT_TRUE(result.HasValidPath());
+
+ // The partial path should be returned even though it didn't reach a trust
+ // anchor.
+ ASSERT_EQ(2U, result.paths.size());
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ ASSERT_EQ(3U, result.paths[0]->certs.size());
+ EXPECT_EQ(a_by_b_, result.paths[0]->certs[0]);
+ EXPECT_EQ(b_by_f_, result.paths[0]->certs[1]);
+ EXPECT_EQ(f_by_e_, result.paths[0]->certs[2]);
+
+ // The result path should be A(B) <- B(C) <- C(D) <- D(D)
+ EXPECT_EQ(1U, result.best_result_index);
+ EXPECT_TRUE(result.paths[1]->IsValid());
+ const auto& path = *result.GetBestValidPath();
+ ASSERT_EQ(4U, path.certs.size());
+ EXPECT_EQ(a_by_b_, path.certs[0]);
+ EXPECT_EQ(b_by_c_, path.certs[1]);
+ EXPECT_EQ(c_by_d_, path.certs[2]);
+ EXPECT_EQ(d_by_d_, path.certs[3]);
+}
+
+// Test that if no path to a trust anchor was found, the partial path is
+// returned.
+TEST_F(PathBuilderMultiRootTest, TestOnlyPartialPathResult) {
+ TrustStoreInMemory trust_store;
+
+ // Certs B(F) and F(E) are supplied synchronously, thus the path
+ // A(B) <- B(F) <- F(E) should be built first, though it won't verify.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_f_);
+ sync_certs.AddCert(f_by_e_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_FALSE(result.HasValidPath());
+
+ // The partial path should be returned even though it didn't reach a trust
+ // anchor.
+ ASSERT_EQ(1U, result.paths.size());
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ ASSERT_EQ(3U, result.paths[0]->certs.size());
+ EXPECT_EQ(a_by_b_, result.paths[0]->certs[0]);
+ EXPECT_EQ(b_by_f_, result.paths[0]->certs[1]);
+ EXPECT_EQ(f_by_e_, result.paths[0]->certs[2]);
+}
+
+// Test that if two partial paths are returned, the first is marked as the best
+// path.
+TEST_F(PathBuilderMultiRootTest, TestTwoPartialPathResults) {
+ TrustStoreInMemory trust_store;
+
+ // Certs B(F) and F(E) are supplied synchronously, thus the path
+ // A(B) <- B(F) <- F(E) should be built first, though it won't verify.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_f_);
+ sync_certs.AddCert(f_by_e_);
+
+ // Certs B(C), and C(D) are supplied asynchronously, so the path
+ // A(B) <- B(C) <- C(D) <- D(D) should be tried second.
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(b_by_c_);
+ async_certs.AddCert(c_by_d_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+ path_builder.AddCertIssuerSource(&async_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_FALSE(result.HasValidPath());
+
+ // First partial path found should be marked as the best one.
+ EXPECT_EQ(0U, result.best_result_index);
+
+ ASSERT_EQ(2U, result.paths.size());
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ ASSERT_EQ(3U, result.paths[0]->certs.size());
+ EXPECT_EQ(a_by_b_, result.paths[0]->certs[0]);
+ EXPECT_EQ(b_by_f_, result.paths[0]->certs[1]);
+ EXPECT_EQ(f_by_e_, result.paths[0]->certs[2]);
+
+ EXPECT_FALSE(result.paths[1]->IsValid());
+ ASSERT_EQ(3U, result.paths[1]->certs.size());
+ EXPECT_EQ(a_by_b_, result.paths[1]->certs[0]);
+ EXPECT_EQ(b_by_c_, result.paths[1]->certs[1]);
+ EXPECT_EQ(c_by_d_, result.paths[1]->certs[2]);
+}
+
+// Test that if no valid path is found, and the first invalid path is a partial
+// path, but the 2nd invalid path ends with a cert with a trust record, the 2nd
+// path should be preferred.
+TEST_F(PathBuilderMultiRootTest, TestDistrustedPathPreferredOverPartialPath) {
+ // Only D(D) has a trust record, but it is distrusted.
+ TrustStoreInMemory trust_store;
+ trust_store.AddDistrustedCertificateForTest(d_by_d_);
+
+ // Certs B(F) and F(E) are supplied synchronously, thus the path
+ // A(B) <- B(F) <- F(E) should be built first, though it won't verify.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_f_);
+ sync_certs.AddCert(f_by_e_);
+
+ // Certs B(C), and C(D) are supplied asynchronously, so the path
+ // A(B) <- B(C) <- C(D) <- D(D) should be tried second.
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(b_by_c_);
+ async_certs.AddCert(c_by_d_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+ path_builder.AddCertIssuerSource(&async_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_FALSE(result.HasValidPath());
+
+ // The partial path should be returned even though it didn't reach a trust
+ // anchor.
+ ASSERT_EQ(2U, result.paths.size());
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ ASSERT_EQ(3U, result.paths[0]->certs.size());
+ EXPECT_EQ(a_by_b_, result.paths[0]->certs[0]);
+ EXPECT_EQ(b_by_f_, result.paths[0]->certs[1]);
+ EXPECT_EQ(f_by_e_, result.paths[0]->certs[2]);
+
+ // The result path should be A(B) <- B(C) <- C(D) <- D(D)
+ EXPECT_EQ(1U, result.best_result_index);
+ EXPECT_FALSE(result.paths[1]->IsValid());
+ const auto& path = *result.GetBestPathPossiblyInvalid();
+ ASSERT_EQ(4U, path.certs.size());
+ EXPECT_EQ(a_by_b_, path.certs[0]);
+ EXPECT_EQ(b_by_c_, path.certs[1]);
+ EXPECT_EQ(c_by_d_, path.certs[2]);
+ EXPECT_EQ(d_by_d_, path.certs[3]);
+}
+
+// Test that whichever order CertIssuerSource returns the issuers, the path
+// building still succeeds.
+TEST_F(PathBuilderMultiRootTest, TestCertIssuerOrdering) {
+ // Only D(D) is a trusted root.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(d_by_d_);
+
+ for (bool reverse_order : {false, true}) {
+ SCOPED_TRACE(reverse_order);
+ std::vector<scoped_refptr<ParsedCertificate>> certs = {
+ b_by_c_, b_by_f_, f_by_e_, c_by_d_, c_by_e_};
+ CertIssuerSourceStatic sync_certs;
+ if (reverse_order) {
+ for (auto it = certs.rbegin(); it != certs.rend(); ++it)
+ sync_certs.AddCert(*it);
+ } else {
+ for (const auto& cert : certs)
+ sync_certs.AddCert(cert);
+ }
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ ASSERT_TRUE(result.HasValidPath());
+
+ // The result path should be A(B) <- B(C) <- C(D) <- D(D)
+ const auto& path = *result.GetBestValidPath();
+ ASSERT_EQ(4U, path.certs.size());
+ EXPECT_EQ(a_by_b_, path.certs[0]);
+ EXPECT_EQ(b_by_c_, path.certs[1]);
+ EXPECT_EQ(c_by_d_, path.certs[2]);
+ EXPECT_EQ(d_by_d_, path.certs[3]);
+ }
+}
+
+TEST_F(PathBuilderMultiRootTest, TestIterationLimit) {
+ // D(D) is the trust root.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(d_by_d_);
+
+ // Certs B(C) and C(D) are supplied.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_c_);
+ sync_certs.AddCert(c_by_d_);
+
+ for (const bool insufficient_limit : {true, false}) {
+ SCOPED_TRACE(insufficient_limit);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ if (insufficient_limit) {
+ // A limit of one is insufficient to build a path in this case. Therefore
+ // building is expected to fail in this case.
+ path_builder.SetIterationLimit(1);
+ } else {
+ // The other tests in this file exercise the case that |SetIterationLimit|
+ // isn't called. Therefore set a sufficient limit for the path to be
+ // found.
+ path_builder.SetIterationLimit(5);
+ }
+
+ base::HistogramTester histogram_tester;
+ auto result = path_builder.Run();
+
+ EXPECT_EQ(!insufficient_limit, result.HasValidPath());
+ EXPECT_EQ(insufficient_limit, result.exceeded_iteration_limit);
+
+ if (insufficient_limit) {
+ EXPECT_EQ(2U, result.iteration_count);
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "Net.CertVerifier.PathBuilderIterationCount"),
+ ElementsAre(base::Bucket(/*sample=*/2, /*count=*/1)));
+ } else {
+ EXPECT_EQ(3U, result.iteration_count);
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "Net.CertVerifier.PathBuilderIterationCount"),
+ ElementsAre(base::Bucket(/*sample=*/3, /*count=*/1)));
+ }
+ }
+}
+
+TEST_F(PathBuilderMultiRootTest, TestTrivialDeadline) {
+ // C(D) is the trust root.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(c_by_d_);
+
+ // Cert B(C) is supplied.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_c_);
+
+ for (const bool insufficient_limit : {true, false}) {
+ SCOPED_TRACE(insufficient_limit);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ base::TimeTicks deadline;
+ if (insufficient_limit) {
+ // Set a deadline one millisecond in the past. Path building should fail
+ // since the deadline is already past.
+ deadline = base::TimeTicks::Now() - base::Milliseconds(1);
+ } else {
+ // The other tests in this file exercise the case that |SetDeadline|
+ // isn't called. Therefore set a sufficient limit for the path to be
+ // found.
+ deadline = base::TimeTicks::Now() + base::Days(1);
+ }
+ path_builder.SetDeadline(deadline);
+
+ auto result = path_builder.Run();
+
+ EXPECT_EQ(!insufficient_limit, result.HasValidPath());
+ EXPECT_EQ(insufficient_limit, result.exceeded_deadline);
+ EXPECT_EQ(deadline, path_builder.deadline());
+
+ if (insufficient_limit) {
+ ASSERT_EQ(1U, result.paths.size());
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ ASSERT_EQ(1U, result.paths[0]->certs.size());
+ EXPECT_EQ(a_by_b_, result.paths[0]->certs[0]);
+ EXPECT_TRUE(result.paths[0]->errors.ContainsError(
+ cert_errors::kDeadlineExceeded));
+ } else {
+ ASSERT_EQ(1U, result.paths.size());
+ EXPECT_TRUE(result.paths[0]->IsValid());
+ ASSERT_EQ(3U, result.paths[0]->certs.size());
+ EXPECT_EQ(a_by_b_, result.paths[0]->certs[0]);
+ EXPECT_EQ(b_by_c_, result.paths[0]->certs[1]);
+ EXPECT_EQ(c_by_d_, result.paths[0]->certs[2]);
+ }
+ }
+}
+
+TEST_F(PathBuilderMultiRootTest, TestDeadline) {
+ base::test::TaskEnvironment task_environment{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(d_by_d_);
+
+ // Cert B(C) is supplied statically.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_c_);
+
+ // Cert C(D) is supplied asynchronously and will advance time before returning
+ // the async result.
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(c_by_d_);
+ async_certs.SetAsyncGetCallback(base::BindLambdaForTesting(
+ [&] { task_environment.FastForwardBy(base::Seconds(2)); }));
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+ path_builder.AddCertIssuerSource(&async_certs);
+
+ base::TimeTicks deadline;
+ deadline = base::TimeTicks::Now() + base::Seconds(1);
+ path_builder.SetDeadline(deadline);
+
+ auto result = path_builder.Run();
+
+ EXPECT_FALSE(result.HasValidPath());
+ EXPECT_TRUE(result.exceeded_deadline);
+ EXPECT_EQ(deadline, path_builder.deadline());
+
+ // The chain returned should end in c_by_d_, since the deadline would only be
+ // checked again after the async results had been checked (since
+ // AsyncCertIssuerSourceStatic makes the async results available immediately.)
+ ASSERT_EQ(1U, result.paths.size());
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ ASSERT_EQ(3U, result.paths[0]->certs.size());
+ EXPECT_EQ(a_by_b_, result.paths[0]->certs[0]);
+ EXPECT_EQ(b_by_c_, result.paths[0]->certs[1]);
+ EXPECT_EQ(c_by_d_, result.paths[0]->certs[2]);
+ EXPECT_TRUE(
+ result.paths[0]->errors.ContainsError(cert_errors::kDeadlineExceeded));
+}
+
+TEST_F(PathBuilderMultiRootTest, TestDepthLimit) {
+ // D(D) is the trust root.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(d_by_d_);
+
+ // Certs B(C) and C(D) are supplied.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_c_);
+ sync_certs.AddCert(c_by_d_);
+
+ for (const bool insufficient_limit : {true, false}) {
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ if (insufficient_limit) {
+ // A limit of depth equal to 2 is insufficient to build the path.
+ // Therefore, building is expected to fail.
+ path_builder.SetDepthLimit(2);
+ } else {
+ // The other tests in this file exercise the case that |SetDepthLimit|
+ // isn't called. Therefore, set a sufficient limit for the path to be
+ // found.
+ path_builder.SetDepthLimit(5);
+ }
+
+ auto result = path_builder.Run();
+
+ EXPECT_EQ(!insufficient_limit, result.HasValidPath());
+ EXPECT_EQ(insufficient_limit,
+ result.AnyPathContainsError(cert_errors::kDepthLimitExceeded));
+ if (insufficient_limit) {
+ EXPECT_EQ(2U, result.max_depth_seen);
+ } else {
+ EXPECT_EQ(4U, result.max_depth_seen);
+ }
+ }
+}
+
+TEST_F(PathBuilderMultiRootTest, TestDepthLimitMultiplePaths) {
+ // This case tests path building backracking due to reaching the path depth
+ // limit. Given the root and issuer certificates below, there can be two paths
+ // from between the leaf to a trusted root, one has length of 3 and the other
+ // has length of 4. These certificates are specifically chosen because path
+ // building will first explore the 4-certificate long path then the
+ // 3-certificate long path. So with a depth limit of 3, we can test the
+ // backtracking code path.
+
+ // E(E) and C(D) are the trust roots.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(e_by_e_);
+ trust_store.AddTrustAnchor(c_by_d_);
+
+ // Certs B(C). B(F) and F(E) are supplied.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_c_);
+ sync_certs.AddCert(b_by_f_);
+ sync_certs.AddCert(f_by_e_);
+
+ CertPathBuilder path_builder(
+ a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ path_builder.SetDepthLimit(3);
+
+ auto result = path_builder.Run();
+
+ EXPECT_TRUE(result.HasValidPath());
+ EXPECT_TRUE(result.AnyPathContainsError(cert_errors::kDepthLimitExceeded));
+
+ ASSERT_EQ(result.paths.size(), 2u);
+
+ const CertPathBuilderResultPath* truncated_path = result.paths[0].get();
+ EXPECT_FALSE(truncated_path->IsValid());
+ EXPECT_TRUE(
+ truncated_path->errors.ContainsError(cert_errors::kDepthLimitExceeded));
+ ASSERT_EQ(truncated_path->certs.size(), 3u);
+ EXPECT_EQ(a_by_b_, truncated_path->certs[0]);
+ EXPECT_EQ(b_by_f_, truncated_path->certs[1]);
+ EXPECT_EQ(f_by_e_, truncated_path->certs[2]);
+
+ const CertPathBuilderResultPath* valid_path = result.paths[1].get();
+ EXPECT_TRUE(valid_path->IsValid());
+ EXPECT_FALSE(
+ valid_path->errors.ContainsError(cert_errors::kDepthLimitExceeded));
+ ASSERT_EQ(valid_path->certs.size(), 3u);
+ EXPECT_EQ(a_by_b_, valid_path->certs[0]);
+ EXPECT_EQ(b_by_c_, valid_path->certs[1]);
+ EXPECT_EQ(c_by_d_, valid_path->certs[2]);
+}
+
+#if BUILDFLAG(IS_WIN) && BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
+
+void AddToStoreWithEKURestriction(HCERTSTORE store,
+ const scoped_refptr<ParsedCertificate>& cert,
+ LPCSTR usage_identifier) {
+ crypto::ScopedPCCERT_CONTEXT os_cert(CertCreateCertificateContext(
+ X509_ASN_ENCODING, cert->der_cert().UnsafeData(),
+ cert->der_cert().Length()));
+
+ CERT_ENHKEY_USAGE usage;
+ memset(&usage, 0, sizeof(usage));
+ CertSetEnhancedKeyUsage(os_cert.get(), &usage);
+ if (usage_identifier) {
+ CertAddEnhancedKeyUsageIdentifier(os_cert.get(), usage_identifier);
+ }
+ CertAddCertificateContextToStore(store, os_cert.get(), CERT_STORE_ADD_ALWAYS,
+ nullptr);
+}
+
+bool AreCertsEq(const scoped_refptr<ParsedCertificate> cert_1,
+ const scoped_refptr<ParsedCertificate> cert_2) {
+ return cert_1 && cert_2 && cert_1->der_cert() == cert_2->der_cert();
+}
+
+// Test to ensure that path building stops when an intermediate cert is
+// encountered that is not usable for TLS because of EKU restrictions.
+TEST_F(PathBuilderMultiRootTest, TrustStoreWinOnlyFindTrustedTLSPath) {
+ crypto::ScopedHCERTSTORE root_store(CertOpenStore(
+ CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr));
+ crypto::ScopedHCERTSTORE intermediate_store(CertOpenStore(
+ CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr));
+ crypto::ScopedHCERTSTORE disallowed_store(CertOpenStore(
+ CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr));
+
+ AddToStoreWithEKURestriction(root_store.get(), d_by_d_,
+ szOID_PKIX_KP_SERVER_AUTH);
+ AddToStoreWithEKURestriction(root_store.get(), e_by_e_,
+ szOID_PKIX_KP_SERVER_AUTH);
+ AddToStoreWithEKURestriction(intermediate_store.get(), c_by_e_,
+ szOID_PKIX_KP_SERVER_AUTH);
+ AddToStoreWithEKURestriction(intermediate_store.get(), c_by_d_, nullptr);
+
+ std::unique_ptr<TrustStoreWin> trust_store = TrustStoreWin::CreateForTesting(
+ std::move(root_store), std::move(intermediate_store),
+ std::move(disallowed_store));
+
+ CertPathBuilder path_builder(
+ b_by_c_, trust_store.get(), &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+
+ // Check all paths.
+ path_builder.SetExploreAllPaths(true);
+
+ auto result = path_builder.Run();
+ ASSERT_TRUE(result.HasValidPath());
+ ASSERT_EQ(2U, result.paths.size());
+ const auto& path = *result.GetBestValidPath();
+ ASSERT_EQ(3U, path.certs.size());
+ EXPECT_TRUE(AreCertsEq(b_by_c_, path.certs[0]));
+ EXPECT_TRUE(AreCertsEq(c_by_e_, path.certs[1]));
+ EXPECT_TRUE(AreCertsEq(e_by_e_, path.certs[2]));
+
+ // Should only be one valid path, the one above.
+ int valid_paths = 0;
+ for (auto&& path : result.paths) {
+ valid_paths += path->IsValid() ? 1 : 0;
+ }
+ ASSERT_EQ(1, valid_paths);
+}
+
+// Test that if an intermediate is disabled for TLS, and it is the only
+// path, then path building should fail, even if the root is enabled for
+// TLS.
+TEST_F(PathBuilderMultiRootTest, TrustStoreWinNoPathEKURestrictions) {
+ crypto::ScopedHCERTSTORE root_store(CertOpenStore(
+ CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr));
+ crypto::ScopedHCERTSTORE intermediate_store(CertOpenStore(
+ CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr));
+ crypto::ScopedHCERTSTORE disallowed_store(CertOpenStore(
+ CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr));
+
+ AddToStoreWithEKURestriction(root_store.get(), d_by_d_,
+ szOID_PKIX_KP_SERVER_AUTH);
+ AddToStoreWithEKURestriction(intermediate_store.get(), c_by_d_, nullptr);
+ std::unique_ptr<TrustStoreWin> trust_store = TrustStoreWin::CreateForTesting(
+ std::move(root_store), std::move(intermediate_store),
+ std::move(disallowed_store));
+
+ CertPathBuilder path_builder(
+ b_by_c_, trust_store.get(), &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+
+ auto result = path_builder.Run();
+ ASSERT_FALSE(result.HasValidPath());
+}
+#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
+
+class PathBuilderKeyRolloverTest : public ::testing::Test {
+ public:
+ PathBuilderKeyRolloverTest()
+ : delegate_(1024,
+ SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1) {}
+
+ void SetUp() override {
+ ParsedCertificateList path;
+
+ VerifyCertChainTest test;
+ ASSERT_TRUE(ReadVerifyCertChainTestFromFile(
+ "net/data/verify_certificate_chain_unittest/key-rollover/oldchain.test",
+ &test));
+ path = test.chain;
+ ASSERT_EQ(3U, path.size());
+ target_ = path[0];
+ oldintermediate_ = path[1];
+ oldroot_ = path[2];
+ time_ = test.time;
+
+ ASSERT_TRUE(target_);
+ ASSERT_TRUE(oldintermediate_);
+
+ ASSERT_TRUE(ReadVerifyCertChainTestFromFile(
+ "net/data/verify_certificate_chain_unittest/"
+ "key-rollover/longrolloverchain.test",
+ &test));
+ path = test.chain;
+
+ ASSERT_EQ(5U, path.size());
+ newintermediate_ = path[1];
+ newroot_ = path[2];
+ newrootrollover_ = path[3];
+ ASSERT_TRUE(newintermediate_);
+ ASSERT_TRUE(newroot_);
+ ASSERT_TRUE(newrootrollover_);
+ }
+
+ protected:
+ // oldroot-------->newrootrollover newroot
+ // | | |
+ // v v v
+ // oldintermediate newintermediate
+ // | |
+ // +------------+-------------+
+ // |
+ // v
+ // target
+ scoped_refptr<ParsedCertificate> target_;
+ scoped_refptr<ParsedCertificate> oldintermediate_;
+ scoped_refptr<ParsedCertificate> newintermediate_;
+ scoped_refptr<ParsedCertificate> oldroot_;
+ scoped_refptr<ParsedCertificate> newroot_;
+ scoped_refptr<ParsedCertificate> newrootrollover_;
+
+ SimplePathBuilderDelegate delegate_;
+ der::GeneralizedTime time_;
+
+ const InitialExplicitPolicy initial_explicit_policy_ =
+ InitialExplicitPolicy::kFalse;
+ const std::set<der::Input> user_initial_policy_set_ = {
+ der::Input(kAnyPolicyOid)};
+ const InitialPolicyMappingInhibit initial_policy_mapping_inhibit_ =
+ InitialPolicyMappingInhibit::kFalse;
+ const InitialAnyPolicyInhibit initial_any_policy_inhibit_ =
+ InitialAnyPolicyInhibit::kFalse;
+};
+
+// Tests that if only the old root cert is trusted, the path builder can build a
+// path through the new intermediate and rollover cert to the old root.
+TEST_F(PathBuilderKeyRolloverTest, TestRolloverOnlyOldRootTrusted) {
+ // Only oldroot is trusted.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(oldroot_);
+
+ // Old intermediate cert is not provided, so the pathbuilder will need to go
+ // through the rollover cert.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(newintermediate_);
+ sync_certs.AddCert(newrootrollover_);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_TRUE(result.HasValidPath());
+
+ // Due to authorityKeyIdentifier prioritization, path builder will first
+ // attempt: target <- newintermediate <- newrootrollover <- oldroot
+ // which will succeed.
+ ASSERT_EQ(1U, result.paths.size());
+ const auto& path0 = *result.paths[0];
+ EXPECT_EQ(0U, result.best_result_index);
+ EXPECT_TRUE(path0.IsValid());
+ ASSERT_EQ(4U, path0.certs.size());
+ EXPECT_EQ(target_, path0.certs[0]);
+ EXPECT_EQ(newintermediate_, path0.certs[1]);
+ EXPECT_EQ(newrootrollover_, path0.certs[2]);
+ EXPECT_EQ(oldroot_, path0.certs[3]);
+}
+
+// Tests that if both old and new roots are trusted it builds a path through
+// the new intermediate.
+TEST_F(PathBuilderKeyRolloverTest, TestRolloverBothRootsTrusted) {
+ // Both oldroot and newroot are trusted.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(oldroot_);
+ trust_store.AddTrustAnchor(newroot_);
+
+ // Both old and new intermediates + rollover cert are provided.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(oldintermediate_);
+ sync_certs.AddCert(newintermediate_);
+ sync_certs.AddCert(newrootrollover_);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_TRUE(result.HasValidPath());
+
+ ASSERT_EQ(1U, result.paths.size());
+ const auto& path = *result.paths[0];
+ EXPECT_TRUE(result.paths[0]->IsValid());
+ ASSERT_EQ(3U, path.certs.size());
+ EXPECT_EQ(target_, path.certs[0]);
+ // The newer intermediate should be used as newer certs are prioritized in
+ // path building.
+ EXPECT_EQ(newintermediate_, path.certs[1]);
+ EXPECT_EQ(newroot_, path.certs[2]);
+}
+
+// If trust anchor query returned no results, and there are no issuer
+// sources, path building should fail at that point.
+TEST_F(PathBuilderKeyRolloverTest, TestAnchorsNoMatchAndNoIssuerSources) {
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(newroot_);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+
+ auto result = path_builder.Run();
+
+ EXPECT_FALSE(result.HasValidPath());
+
+ ASSERT_EQ(1U, result.paths.size());
+ const auto& path = *result.paths[0];
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ ASSERT_EQ(1U, path.certs.size());
+ EXPECT_EQ(target_, path.certs[0]);
+}
+
+// If a path to a trust anchor could not be found, and the last issuer(s) in
+// the chain were culled by the loop checker, the partial path up to that point
+// should be returned.
+TEST_F(PathBuilderKeyRolloverTest, TestReturnsPartialPathEndedByLoopChecker) {
+ TrustStoreInMemory trust_store;
+
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(newintermediate_);
+ sync_certs.AddCert(newroot_);
+ sync_certs.AddCert(newrootrollover_);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_FALSE(result.HasValidPath());
+ ASSERT_EQ(2U, result.paths.size());
+
+ // Since none of the certs are trusted, the path builder should build 4
+ // candidate paths, all of which are disallowed due to the loop checker:
+ // target->newintermediate->newroot->newroot
+ // target->newintermediate->newroot->newrootrollover
+ // target->newintermediate->newrootrollover->newroot
+ // target->newintermediate->newrootrollover->newrootrollover
+ // This should end up returning the 2 partial paths which are the longest
+ // paths for which no acceptable issuers could be found:
+ // target->newintermediate->newroot
+ // target->newintermediate->newrootrollover
+
+ {
+ const auto& path = *result.paths[0];
+ EXPECT_FALSE(path.IsValid());
+ ASSERT_EQ(3U, path.certs.size());
+ EXPECT_EQ(target_, path.certs[0]);
+ EXPECT_EQ(newintermediate_, path.certs[1]);
+ EXPECT_EQ(newroot_, path.certs[2]);
+ EXPECT_TRUE(path.errors.ContainsError(cert_errors::kNoIssuersFound));
+ }
+
+ {
+ const auto& path = *result.paths[1];
+ EXPECT_FALSE(path.IsValid());
+ ASSERT_EQ(3U, path.certs.size());
+ EXPECT_EQ(target_, path.certs[0]);
+ EXPECT_EQ(newintermediate_, path.certs[1]);
+ EXPECT_EQ(newrootrollover_, path.certs[2]);
+ EXPECT_TRUE(path.errors.ContainsError(cert_errors::kNoIssuersFound));
+ }
+}
+
+// Tests that multiple trust root matches on a single path will be considered.
+// Both roots have the same subject but different keys. Only one of them will
+// verify.
+TEST_F(PathBuilderKeyRolloverTest, TestMultipleRootMatchesOnlyOneWorks) {
+ TrustStoreCollection trust_store_collection;
+ TrustStoreInMemory trust_store1;
+ TrustStoreInMemory trust_store2;
+ trust_store_collection.AddTrustStore(&trust_store1);
+ trust_store_collection.AddTrustStore(&trust_store2);
+ // Add two trust anchors (newroot_ and oldroot_). Path building will attempt
+ // them in this same order, as trust_store1 was added to
+ // trust_store_collection first.
+ trust_store1.AddTrustAnchor(newroot_);
+ trust_store2.AddTrustAnchor(oldroot_);
+
+ // Only oldintermediate is supplied, so the path with newroot should fail,
+ // oldroot should succeed.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(oldintermediate_);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store_collection, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_TRUE(result.HasValidPath());
+ ASSERT_EQ(1U, result.paths.size());
+
+ // Due to authorityKeyIdentifier prioritization, path builder will first
+ // attempt: target <- old intermediate <- oldroot
+ // which should succeed.
+ EXPECT_TRUE(result.paths[result.best_result_index]->IsValid());
+ const auto& path = *result.paths[result.best_result_index];
+ ASSERT_EQ(3U, path.certs.size());
+ EXPECT_EQ(target_, path.certs[0]);
+ EXPECT_EQ(oldintermediate_, path.certs[1]);
+ EXPECT_EQ(oldroot_, path.certs[2]);
+}
+
+// Tests that the path builder doesn't build longer than necessary paths,
+// by skipping certs where the same Name+SAN+SPKI is already in the current
+// path.
+TEST_F(PathBuilderKeyRolloverTest, TestRolloverLongChain) {
+ // Only oldroot is trusted.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(oldroot_);
+
+ // New intermediate and new root are provided synchronously.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(newintermediate_);
+ sync_certs.AddCert(newroot_);
+
+ // Rollover cert is only provided asynchronously. This will force the
+ // pathbuilder to first try building a longer than necessary path.
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(newrootrollover_);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+ path_builder.AddCertIssuerSource(&async_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_TRUE(result.HasValidPath());
+ ASSERT_EQ(3U, result.paths.size());
+
+ // Path builder will first attempt:
+ // target <- newintermediate <- newroot <- oldroot
+ // but it will fail since newroot is self-signed.
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ const auto& path0 = *result.paths[0];
+ ASSERT_EQ(4U, path0.certs.size());
+ EXPECT_EQ(target_, path0.certs[0]);
+ EXPECT_EQ(newintermediate_, path0.certs[1]);
+ EXPECT_EQ(newroot_, path0.certs[2]);
+ EXPECT_EQ(oldroot_, path0.certs[3]);
+
+ // Path builder will next attempt: target <- newintermediate <- oldroot
+ // but it will fail since newintermediate is signed by newroot.
+ EXPECT_FALSE(result.paths[1]->IsValid());
+ const auto& path1 = *result.paths[1];
+ ASSERT_EQ(3U, path1.certs.size());
+ EXPECT_EQ(target_, path1.certs[0]);
+ EXPECT_EQ(newintermediate_, path1.certs[1]);
+ EXPECT_EQ(oldroot_, path1.certs[2]);
+
+ // Path builder will skip:
+ // target <- newintermediate <- newroot <- newrootrollover <- ...
+ // Since newroot and newrootrollover have the same Name+SAN+SPKI.
+
+ // Finally path builder will use:
+ // target <- newintermediate <- newrootrollover <- oldroot
+ EXPECT_EQ(2U, result.best_result_index);
+ EXPECT_TRUE(result.paths[2]->IsValid());
+ const auto& path2 = *result.paths[2];
+ ASSERT_EQ(4U, path2.certs.size());
+ EXPECT_EQ(target_, path2.certs[0]);
+ EXPECT_EQ(newintermediate_, path2.certs[1]);
+ EXPECT_EQ(newrootrollover_, path2.certs[2]);
+ EXPECT_EQ(oldroot_, path2.certs[3]);
+}
+
+// Tests that when SetExploreAllPaths is combined with SetIterationLimit the
+// path builder will return all the paths that were able to be built before the
+// iteration limit was reached.
+TEST_F(PathBuilderKeyRolloverTest, ExploreAllPathsWithIterationLimit) {
+ struct Expectation {
+ int iteration_limit;
+ size_t expected_num_paths;
+ std::vector<scoped_refptr<ParsedCertificate>> partial_path;
+ } kExpectations[] = {
+ // No iteration limit. All possible paths should be built.
+ {0, 4, {}},
+ // Limit 1 is only enough to reach the intermediate, no complete path
+ // should be built.
+ {1, 0, {target_, newintermediate_}},
+ // Limit 2 allows reaching the root on the first path.
+ {2, 1, {target_, newintermediate_}},
+ // Next iteration uses oldroot instead of newroot.
+ {3, 2, {target_, newintermediate_}},
+ // Backtracking to the target cert.
+ {4, 2, {target_}},
+ // Adding oldintermediate.
+ {5, 2, {target_, oldintermediate_}},
+ // Trying oldroot.
+ {6, 3, {target_, oldintermediate_}},
+ // Trying newroot.
+ {7, 4, {target_, oldintermediate_}},
+ };
+
+ // Trust both old and new roots.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(oldroot_);
+ trust_store.AddTrustAnchor(newroot_);
+
+ // Intermediates and root rollover are all provided synchronously.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(oldintermediate_);
+ sync_certs.AddCert(newintermediate_);
+
+ for (const auto& expectation : kExpectations) {
+ SCOPED_TRACE(expectation.iteration_limit);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ // Explore all paths, rather than stopping at the first valid path.
+ path_builder.SetExploreAllPaths(true);
+
+ // Limit the number of iterations.
+ path_builder.SetIterationLimit(expectation.iteration_limit);
+
+ auto result = path_builder.Run();
+
+ EXPECT_EQ(expectation.expected_num_paths > 0, result.HasValidPath());
+ if (expectation.partial_path.empty()) {
+ ASSERT_EQ(expectation.expected_num_paths, result.paths.size());
+ } else {
+ ASSERT_EQ(1 + expectation.expected_num_paths, result.paths.size());
+ const auto& path = *result.paths[result.paths.size() - 1];
+ EXPECT_FALSE(path.IsValid());
+ EXPECT_EQ(expectation.partial_path, path.certs);
+ EXPECT_TRUE(
+ path.errors.ContainsError(cert_errors::kIterationLimitExceeded));
+ }
+
+ if (expectation.expected_num_paths > 0) {
+ // Path builder will first build path: target <- newintermediate <-
+ // newroot
+ const auto& path0 = *result.paths[0];
+ EXPECT_TRUE(path0.IsValid());
+ ASSERT_EQ(3U, path0.certs.size());
+ EXPECT_EQ(target_, path0.certs[0]);
+ EXPECT_EQ(newintermediate_, path0.certs[1]);
+ EXPECT_EQ(newroot_, path0.certs[2]);
+ EXPECT_EQ(3U, result.max_depth_seen);
+ }
+
+ if (expectation.expected_num_paths > 1) {
+ // Next path: target <- newintermediate <- oldroot
+ const auto& path1 = *result.paths[1];
+ EXPECT_FALSE(path1.IsValid());
+ ASSERT_EQ(3U, path1.certs.size());
+ EXPECT_EQ(target_, path1.certs[0]);
+ EXPECT_EQ(newintermediate_, path1.certs[1]);
+ EXPECT_EQ(oldroot_, path1.certs[2]);
+ EXPECT_EQ(3U, result.max_depth_seen);
+ }
+
+ if (expectation.expected_num_paths > 2) {
+ // Next path: target <- oldintermediate <- oldroot
+ const auto& path2 = *result.paths[2];
+ EXPECT_TRUE(path2.IsValid());
+ ASSERT_EQ(3U, path2.certs.size());
+ EXPECT_EQ(target_, path2.certs[0]);
+ EXPECT_EQ(oldintermediate_, path2.certs[1]);
+ EXPECT_EQ(oldroot_, path2.certs[2]);
+ EXPECT_EQ(3U, result.max_depth_seen);
+ }
+
+ if (expectation.expected_num_paths > 3) {
+ // Final path: target <- oldintermediate <- newroot
+ const auto& path3 = *result.paths[3];
+ EXPECT_FALSE(path3.IsValid());
+ ASSERT_EQ(3U, path3.certs.size());
+ EXPECT_EQ(target_, path3.certs[0]);
+ EXPECT_EQ(oldintermediate_, path3.certs[1]);
+ EXPECT_EQ(newroot_, path3.certs[2]);
+ EXPECT_EQ(3U, result.max_depth_seen);
+ }
+ }
+}
+
+// If the target cert is a trust anchor, however is not itself *signed* by a
+// trust anchor, then it is not considered valid (the SPKI and name of the
+// trust anchor matches the SPKI and subject of the targe certificate, but the
+// rest of the certificate cannot be verified).
+TEST_F(PathBuilderKeyRolloverTest, TestEndEntityIsTrustRoot) {
+ // Trust newintermediate.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(newintermediate_);
+
+ // Newintermediate is also the target cert.
+ CertPathBuilder path_builder(
+ newintermediate_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+
+ auto result = path_builder.Run();
+
+ EXPECT_FALSE(result.HasValidPath());
+}
+
+// If target has same Name+SAN+SPKI as a necessary intermediate, test if a path
+// can still be built.
+// Since LoopChecker will prevent the intermediate from being included, this
+// currently does NOT verify. This case shouldn't occur in the web PKI.
+TEST_F(PathBuilderKeyRolloverTest,
+ TestEndEntityHasSameNameAndSpkiAsIntermediate) {
+ // Trust oldroot.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(oldroot_);
+
+ // New root rollover is provided synchronously.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(newrootrollover_);
+
+ // Newroot is the target cert.
+ CertPathBuilder path_builder(
+ newroot_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ // This could actually be OK, but CertPathBuilder does not build the
+ // newroot <- newrootrollover <- oldroot path.
+ EXPECT_FALSE(result.HasValidPath());
+}
+
+// If target has same Name+SAN+SPKI as the trust root, test that a (trivial)
+// path can still be built.
+TEST_F(PathBuilderKeyRolloverTest,
+ TestEndEntityHasSameNameAndSpkiAsTrustAnchor) {
+ // Trust newrootrollover.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(newrootrollover_);
+
+ // Newroot is the target cert.
+ CertPathBuilder path_builder(
+ newroot_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+
+ auto result = path_builder.Run();
+
+ ASSERT_TRUE(result.HasValidPath());
+
+ const CertPathBuilderResultPath* best_result = result.GetBestValidPath();
+
+ // Newroot has same name+SPKI as newrootrollover, thus the path is valid and
+ // only contains newroot.
+ EXPECT_TRUE(best_result->IsValid());
+ ASSERT_EQ(2U, best_result->certs.size());
+ EXPECT_EQ(newroot_, best_result->certs[0]);
+ EXPECT_EQ(newrootrollover_, best_result->certs[1]);
+}
+
+// Test that PathBuilder will not try the same path twice if multiple
+// CertIssuerSources provide the same certificate.
+TEST_F(PathBuilderKeyRolloverTest, TestDuplicateIntermediates) {
+ // Create a separate copy of oldintermediate.
+ scoped_refptr<ParsedCertificate> oldintermediate_dupe(
+ ParsedCertificate::Create(
+ bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(
+ oldintermediate_->der_cert().UnsafeData(),
+ oldintermediate_->der_cert().Length(), nullptr)),
+ {}, nullptr));
+
+ // Only newroot is a trusted root.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(newroot_);
+
+ // The oldintermediate is supplied synchronously by |sync_certs1| and
+ // another copy of oldintermediate is supplied synchronously by |sync_certs2|.
+ // The path target <- oldintermediate <- newroot should be built first,
+ // though it won't verify. It should not be attempted again even though
+ // oldintermediate was supplied twice.
+ CertIssuerSourceStatic sync_certs1;
+ sync_certs1.AddCert(oldintermediate_);
+ CertIssuerSourceStatic sync_certs2;
+ sync_certs2.AddCert(oldintermediate_dupe);
+
+ // The newintermediate is supplied asynchronously, so the path
+ // target <- newintermediate <- newroot should be tried second.
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(newintermediate_);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs1);
+ path_builder.AddCertIssuerSource(&sync_certs2);
+ path_builder.AddCertIssuerSource(&async_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_TRUE(result.HasValidPath());
+ ASSERT_EQ(2U, result.paths.size());
+
+ // Path builder will first attempt: target <- oldintermediate <- newroot
+ // but it will fail since oldintermediate is signed by oldroot.
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ const auto& path0 = *result.paths[0];
+
+ ASSERT_EQ(3U, path0.certs.size());
+ EXPECT_EQ(target_, path0.certs[0]);
+ // Compare the DER instead of ParsedCertificate pointer, don't care which copy
+ // of oldintermediate was used in the path.
+ EXPECT_EQ(oldintermediate_->der_cert(), path0.certs[1]->der_cert());
+ EXPECT_EQ(newroot_, path0.certs[2]);
+
+ // Path builder will next attempt: target <- newintermediate <- newroot
+ // which will succeed.
+ EXPECT_EQ(1U, result.best_result_index);
+ EXPECT_TRUE(result.paths[1]->IsValid());
+ const auto& path1 = *result.paths[1];
+ ASSERT_EQ(3U, path1.certs.size());
+ EXPECT_EQ(target_, path1.certs[0]);
+ EXPECT_EQ(newintermediate_, path1.certs[1]);
+ EXPECT_EQ(newroot_, path1.certs[2]);
+}
+
+// Test when PathBuilder is given a cert via CertIssuerSources that has the same
+// SPKI as a trust anchor.
+TEST_F(PathBuilderKeyRolloverTest, TestDuplicateIntermediateAndRoot) {
+ // Create a separate copy of newroot.
+ scoped_refptr<ParsedCertificate> newroot_dupe(ParsedCertificate::Create(
+ bssl::UniquePtr<CRYPTO_BUFFER>(
+ CRYPTO_BUFFER_new(newroot_->der_cert().UnsafeData(),
+ newroot_->der_cert().Length(), nullptr)),
+ {}, nullptr));
+
+ // Only newroot is a trusted root.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(newroot_);
+
+ // The oldintermediate and newroot are supplied synchronously by |sync_certs|.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(oldintermediate_);
+ sync_certs.AddCert(newroot_dupe);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ auto result = path_builder.Run();
+
+ EXPECT_FALSE(result.HasValidPath());
+ ASSERT_EQ(1U, result.paths.size());
+
+ // Path builder attempt: target <- oldintermediate <- newroot
+ // but it will fail since oldintermediate is signed by oldroot.
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ const auto& path = *result.paths[0];
+ ASSERT_EQ(3U, path.certs.size());
+ EXPECT_EQ(target_, path.certs[0]);
+ EXPECT_EQ(oldintermediate_, path.certs[1]);
+ // Compare the DER instead of ParsedCertificate pointer, don't care which copy
+ // of newroot was used in the path.
+ EXPECT_EQ(newroot_->der_cert(), path.certs[2]->der_cert());
+}
+
+class MockCertIssuerSourceRequest : public CertIssuerSource::Request {
+ public:
+ MOCK_METHOD1(GetNext, void(ParsedCertificateList*));
+};
+
+class MockCertIssuerSource : public CertIssuerSource {
+ public:
+ MOCK_METHOD2(SyncGetIssuersOf,
+ void(const ParsedCertificate*, ParsedCertificateList*));
+ MOCK_METHOD2(AsyncGetIssuersOf,
+ void(const ParsedCertificate*, std::unique_ptr<Request>*));
+};
+
+// Helper class to pass the Request to the PathBuilder when it calls
+// AsyncGetIssuersOf. (GoogleMock has a ByMove helper, but it apparently can
+// only be used with Return, not SetArgPointee.)
+class CertIssuerSourceRequestMover {
+ public:
+ explicit CertIssuerSourceRequestMover(
+ std::unique_ptr<CertIssuerSource::Request> req)
+ : request_(std::move(req)) {}
+ void MoveIt(const ParsedCertificate* cert,
+ std::unique_ptr<CertIssuerSource::Request>* out_req) {
+ *out_req = std::move(request_);
+ }
+
+ private:
+ std::unique_ptr<CertIssuerSource::Request> request_;
+};
+
+// Functor that when called with a ParsedCertificateList* will append the
+// specified certificate.
+class AppendCertToList {
+ public:
+ explicit AppendCertToList(const scoped_refptr<ParsedCertificate>& cert)
+ : cert_(cert) {}
+
+ void operator()(ParsedCertificateList* out) { out->push_back(cert_); }
+
+ private:
+ scoped_refptr<ParsedCertificate> cert_;
+};
+
+// Test that a single CertIssuerSource returning multiple async batches of
+// issuers is handled correctly. Due to the StrictMocks, it also tests that path
+// builder does not request issuers of certs that it shouldn't.
+TEST_F(PathBuilderKeyRolloverTest, TestMultipleAsyncIssuersFromSingleSource) {
+ StrictMock<MockCertIssuerSource> cert_issuer_source;
+
+ // Only newroot is a trusted root.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(newroot_);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&cert_issuer_source);
+
+ // Create the mock CertIssuerSource::Request...
+ auto target_issuers_req_owner =
+ std::make_unique<StrictMock<MockCertIssuerSourceRequest>>();
+ // Keep a raw pointer to the Request...
+ StrictMock<MockCertIssuerSourceRequest>* target_issuers_req =
+ target_issuers_req_owner.get();
+ // Setup helper class to pass ownership of the Request to the PathBuilder when
+ // it calls AsyncGetIssuersOf.
+ CertIssuerSourceRequestMover req_mover(std::move(target_issuers_req_owner));
+ {
+ ::testing::InSequence s;
+ EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(target_.get(), _));
+ EXPECT_CALL(cert_issuer_source, AsyncGetIssuersOf(target_.get(), _))
+ .WillOnce(Invoke(&req_mover, &CertIssuerSourceRequestMover::MoveIt));
+ }
+
+ EXPECT_CALL(*target_issuers_req, GetNext(_))
+ // First async batch: return oldintermediate_.
+ .WillOnce(Invoke(AppendCertToList(oldintermediate_)))
+ // Second async batch: return newintermediate_.
+ .WillOnce(Invoke(AppendCertToList(newintermediate_)));
+ {
+ ::testing::InSequence s;
+ // oldintermediate_ does not create a valid path, so both sync and async
+ // lookups are expected.
+ EXPECT_CALL(cert_issuer_source,
+ SyncGetIssuersOf(oldintermediate_.get(), _));
+ EXPECT_CALL(cert_issuer_source,
+ AsyncGetIssuersOf(oldintermediate_.get(), _));
+ }
+
+ // newroot_ is in the trust store, so this path will be completed
+ // synchronously. AsyncGetIssuersOf will not be called on newintermediate_.
+ EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(newintermediate_.get(), _));
+
+ // Ensure pathbuilder finished and filled result.
+ auto result = path_builder.Run();
+
+ // Note that VerifyAndClearExpectations(target_issuers_req) is not called
+ // here. PathBuilder could have destroyed it already, so just let the
+ // expectations get checked by the destructor.
+ ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source);
+
+ EXPECT_TRUE(result.HasValidPath());
+ ASSERT_EQ(2U, result.paths.size());
+
+ // Path builder first attempts: target <- oldintermediate <- newroot
+ // but it will fail since oldintermediate is signed by oldroot.
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ const auto& path0 = *result.paths[0];
+ ASSERT_EQ(3U, path0.certs.size());
+ EXPECT_EQ(target_, path0.certs[0]);
+ EXPECT_EQ(oldintermediate_, path0.certs[1]);
+ EXPECT_EQ(newroot_, path0.certs[2]);
+
+ // After the second batch of async results, path builder will attempt:
+ // target <- newintermediate <- newroot which will succeed.
+ EXPECT_TRUE(result.paths[1]->IsValid());
+ const auto& path1 = *result.paths[1];
+ ASSERT_EQ(3U, path1.certs.size());
+ EXPECT_EQ(target_, path1.certs[0]);
+ EXPECT_EQ(newintermediate_, path1.certs[1]);
+ EXPECT_EQ(newroot_, path1.certs[2]);
+}
+
+// Test that PathBuilder will not try the same path twice if CertIssuerSources
+// asynchronously provide the same certificate multiple times.
+TEST_F(PathBuilderKeyRolloverTest, TestDuplicateAsyncIntermediates) {
+ StrictMock<MockCertIssuerSource> cert_issuer_source;
+
+ // Only newroot is a trusted root.
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(newroot_);
+
+ CertPathBuilder path_builder(
+ target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU,
+ initial_explicit_policy_, user_initial_policy_set_,
+ initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
+ path_builder.AddCertIssuerSource(&cert_issuer_source);
+
+ // Create the mock CertIssuerSource::Request...
+ auto target_issuers_req_owner =
+ std::make_unique<StrictMock<MockCertIssuerSourceRequest>>();
+ // Keep a raw pointer to the Request...
+ StrictMock<MockCertIssuerSourceRequest>* target_issuers_req =
+ target_issuers_req_owner.get();
+ // Setup helper class to pass ownership of the Request to the PathBuilder when
+ // it calls AsyncGetIssuersOf.
+ CertIssuerSourceRequestMover req_mover(std::move(target_issuers_req_owner));
+ {
+ ::testing::InSequence s;
+ EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(target_.get(), _));
+ EXPECT_CALL(cert_issuer_source, AsyncGetIssuersOf(target_.get(), _))
+ .WillOnce(Invoke(&req_mover, &CertIssuerSourceRequestMover::MoveIt));
+ }
+
+ scoped_refptr<ParsedCertificate> oldintermediate_dupe(
+ ParsedCertificate::Create(
+ bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(
+ oldintermediate_->der_cert().UnsafeData(),
+ oldintermediate_->der_cert().Length(), nullptr)),
+ {}, nullptr));
+
+ EXPECT_CALL(*target_issuers_req, GetNext(_))
+ // First async batch: return oldintermediate_.
+ .WillOnce(Invoke(AppendCertToList(oldintermediate_)))
+ // Second async batch: return a different copy of oldintermediate_ again.
+ .WillOnce(Invoke(AppendCertToList(oldintermediate_dupe)))
+ // Third async batch: return newintermediate_.
+ .WillOnce(Invoke(AppendCertToList(newintermediate_)));
+
+ {
+ ::testing::InSequence s;
+ // oldintermediate_ does not create a valid path, so both sync and async
+ // lookups are expected.
+ EXPECT_CALL(cert_issuer_source,
+ SyncGetIssuersOf(oldintermediate_.get(), _));
+ EXPECT_CALL(cert_issuer_source,
+ AsyncGetIssuersOf(oldintermediate_.get(), _));
+ }
+
+ // newroot_ is in the trust store, so this path will be completed
+ // synchronously. AsyncGetIssuersOf will not be called on newintermediate_.
+ EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(newintermediate_.get(), _));
+
+ // Ensure pathbuilder finished and filled result.
+ auto result = path_builder.Run();
+
+ ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source);
+
+ EXPECT_TRUE(result.HasValidPath());
+ ASSERT_EQ(2U, result.paths.size());
+
+ // Path builder first attempts: target <- oldintermediate <- newroot
+ // but it will fail since oldintermediate is signed by oldroot.
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ const auto& path0 = *result.paths[0];
+ ASSERT_EQ(3U, path0.certs.size());
+ EXPECT_EQ(target_, path0.certs[0]);
+ EXPECT_EQ(oldintermediate_, path0.certs[1]);
+ EXPECT_EQ(newroot_, path0.certs[2]);
+
+ // The second async result does not generate any path.
+
+ // After the third batch of async results, path builder will attempt:
+ // target <- newintermediate <- newroot which will succeed.
+ EXPECT_TRUE(result.paths[1]->IsValid());
+ const auto& path1 = *result.paths[1];
+ ASSERT_EQ(3U, path1.certs.size());
+ EXPECT_EQ(target_, path1.certs[0]);
+ EXPECT_EQ(newintermediate_, path1.certs[1]);
+ EXPECT_EQ(newroot_, path1.certs[2]);
+}
+
+class PathBuilderSimpleChainTest : public ::testing::Test {
+ public:
+ PathBuilderSimpleChainTest() = default;
+
+ protected:
+ void SetUp() override {
+ // Read a simple test chain comprised of a target, intermediate, and root.
+ ASSERT_TRUE(ReadVerifyCertChainTestFromFile(
+ "net/data/verify_certificate_chain_unittest/target-and-intermediate/"
+ "main.test",
+ &test_));
+ ASSERT_EQ(3u, test_.chain.size());
+ }
+
+ // Runs the path builder for the target certificate while |distrusted_cert| is
+ // blocked, and |delegate| if non-null.
+ CertPathBuilder::Result RunPathBuilder(
+ const scoped_refptr<ParsedCertificate>& distrusted_cert,
+ CertPathBuilderDelegate* optional_delegate) {
+ // Set up the trust store such that |distrusted_cert| is blocked, and
+ // the root is trusted (except if it was |distrusted_cert|).
+ TrustStoreInMemory trust_store;
+ if (distrusted_cert != test_.chain.back())
+ trust_store.AddTrustAnchor(test_.chain.back());
+ if (distrusted_cert)
+ trust_store.AddDistrustedCertificateForTest(distrusted_cert);
+
+ // Add the single intermediate.
+ CertIssuerSourceStatic intermediates;
+ intermediates.AddCert(test_.chain[1]);
+
+ SimplePathBuilderDelegate default_delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+ CertPathBuilderDelegate* delegate =
+ optional_delegate ? optional_delegate : &default_delegate;
+
+ const InitialExplicitPolicy initial_explicit_policy =
+ InitialExplicitPolicy::kFalse;
+ const std::set<der::Input> user_initial_policy_set = {
+ der::Input(kAnyPolicyOid)};
+ const InitialPolicyMappingInhibit initial_policy_mapping_inhibit =
+ InitialPolicyMappingInhibit::kFalse;
+ const InitialAnyPolicyInhibit initial_any_policy_inhibit =
+ InitialAnyPolicyInhibit::kFalse;
+
+ CertPathBuilder path_builder(
+ test_.chain.front(), &trust_store, delegate, test_.time,
+ KeyPurpose::ANY_EKU, initial_explicit_policy, user_initial_policy_set,
+ initial_policy_mapping_inhibit, initial_any_policy_inhibit);
+ path_builder.AddCertIssuerSource(&intermediates);
+ return path_builder.Run();
+ }
+
+ protected:
+ VerifyCertChainTest test_;
+};
+
+// Test fixture for running the path builder over a simple chain, while varying
+// the trustedness of certain certificates.
+class PathBuilderDistrustTest : public PathBuilderSimpleChainTest {
+ public:
+ PathBuilderDistrustTest() = default;
+
+ protected:
+ // Runs the path builder for the target certificate while |distrusted_cert| is
+ // blocked.
+ CertPathBuilder::Result RunPathBuilderWithDistrustedCert(
+ const scoped_refptr<ParsedCertificate>& distrusted_cert) {
+ return RunPathBuilder(distrusted_cert, nullptr);
+ }
+};
+
+// Tests that path building fails when the target, intermediate, or root are
+// distrusted (but the path is otherwise valid).
+TEST_F(PathBuilderDistrustTest, TargetIntermediateRoot) {
+ // First do a control test -- path building without any blocked
+ // certificates should work.
+ CertPathBuilder::Result result = RunPathBuilderWithDistrustedCert(nullptr);
+ {
+ ASSERT_TRUE(result.HasValidPath());
+ // The built path should be identical the the one read from disk.
+ const auto& path = *result.GetBestValidPath();
+ ASSERT_EQ(test_.chain.size(), path.certs.size());
+ for (size_t i = 0; i < test_.chain.size(); ++i)
+ EXPECT_EQ(test_.chain[i], path.certs[i]);
+ }
+
+ // Try path building when only the target is blocked - should fail.
+ result = RunPathBuilderWithDistrustedCert(test_.chain[0]);
+ {
+ EXPECT_FALSE(result.HasValidPath());
+ ASSERT_LT(result.best_result_index, result.paths.size());
+ const auto& best_path = result.paths[result.best_result_index];
+
+ // The built chain has length 1 since path building stopped once
+ // it encountered the blocked certificate (target).
+ ASSERT_EQ(1u, best_path->certs.size());
+ EXPECT_EQ(best_path->certs[0], test_.chain[0]);
+ EXPECT_TRUE(best_path->errors.ContainsHighSeverityErrors());
+ best_path->errors.ContainsError(cert_errors::kDistrustedByTrustStore);
+ }
+
+ // Try path building when only the intermediate is blocked - should fail.
+ result = RunPathBuilderWithDistrustedCert(test_.chain[1]);
+ {
+ EXPECT_FALSE(result.HasValidPath());
+ ASSERT_LT(result.best_result_index, result.paths.size());
+ const auto& best_path = result.paths[result.best_result_index];
+
+ // The built chain has length 2 since path building stopped once
+ // it encountered the blocked certificate (intermediate).
+ ASSERT_EQ(2u, best_path->certs.size());
+ EXPECT_EQ(best_path->certs[0], test_.chain[0]);
+ EXPECT_EQ(best_path->certs[1], test_.chain[1]);
+ EXPECT_TRUE(best_path->errors.ContainsHighSeverityErrors());
+ best_path->errors.ContainsError(cert_errors::kDistrustedByTrustStore);
+ }
+
+ // Try path building when only the root is blocked - should fail.
+ result = RunPathBuilderWithDistrustedCert(test_.chain[2]);
+ {
+ EXPECT_FALSE(result.HasValidPath());
+ ASSERT_LT(result.best_result_index, result.paths.size());
+ const auto& best_path = result.paths[result.best_result_index];
+
+ // The built chain has length 3 since path building stopped once
+ // it encountered the blocked certificate (root).
+ ASSERT_EQ(3u, best_path->certs.size());
+ EXPECT_EQ(best_path->certs[0], test_.chain[0]);
+ EXPECT_EQ(best_path->certs[1], test_.chain[1]);
+ EXPECT_EQ(best_path->certs[2], test_.chain[2]);
+ EXPECT_TRUE(best_path->errors.ContainsHighSeverityErrors());
+ best_path->errors.ContainsError(cert_errors::kDistrustedByTrustStore);
+ }
+}
+
+// Test fixture for running the path builder over a simple chain, while varying
+// what CheckPathAfterVerification() does.
+class PathBuilderCheckPathAfterVerificationTest
+ : public PathBuilderSimpleChainTest {};
+
+class CertPathBuilderDelegateBase : public SimplePathBuilderDelegate {
+ public:
+ CertPathBuilderDelegateBase()
+ : SimplePathBuilderDelegate(
+ 1024,
+ SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1) {}
+ void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+ CertPathBuilderResultPath* path) override {
+ ADD_FAILURE() << "Tests must override this";
+ }
+};
+
+class MockPathBuilderDelegate : public CertPathBuilderDelegateBase {
+ public:
+ MOCK_METHOD2(CheckPathAfterVerification,
+ void(const CertPathBuilder& path_builder,
+ CertPathBuilderResultPath* path));
+};
+
+TEST_F(PathBuilderCheckPathAfterVerificationTest, NoOpToValidPath) {
+ StrictMock<MockPathBuilderDelegate> delegate;
+ // Just verify that the hook is called.
+ EXPECT_CALL(delegate, CheckPathAfterVerification(_, _));
+
+ CertPathBuilder::Result result = RunPathBuilder(nullptr, &delegate);
+ EXPECT_TRUE(result.HasValidPath());
+}
+
+DEFINE_CERT_ERROR_ID(kWarningFromDelegate, "Warning from delegate");
+
+class AddWarningPathBuilderDelegate : public CertPathBuilderDelegateBase {
+ public:
+ void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+ CertPathBuilderResultPath* path) override {
+ path->errors.GetErrorsForCert(1)->AddWarning(kWarningFromDelegate, nullptr);
+ }
+};
+
+TEST_F(PathBuilderCheckPathAfterVerificationTest, AddsWarningToValidPath) {
+ AddWarningPathBuilderDelegate delegate;
+ CertPathBuilder::Result result = RunPathBuilder(nullptr, &delegate);
+ ASSERT_TRUE(result.HasValidPath());
+
+ // A warning should have been added to certificate at index 1 in the path.
+ const CertErrors* cert1_errors =
+ result.GetBestValidPath()->errors.GetErrorsForCert(1);
+ ASSERT_TRUE(cert1_errors);
+ EXPECT_TRUE(cert1_errors->ContainsError(kWarningFromDelegate));
+}
+
+DEFINE_CERT_ERROR_ID(kErrorFromDelegate, "Error from delegate");
+
+class AddErrorPathBuilderDelegate : public CertPathBuilderDelegateBase {
+ public:
+ void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+ CertPathBuilderResultPath* path) override {
+ path->errors.GetErrorsForCert(2)->AddError(kErrorFromDelegate, nullptr);
+ }
+};
+
+TEST_F(PathBuilderCheckPathAfterVerificationTest, AddsErrorToValidPath) {
+ AddErrorPathBuilderDelegate delegate;
+ CertPathBuilder::Result result = RunPathBuilder(nullptr, &delegate);
+
+ // Verification failed.
+ ASSERT_FALSE(result.HasValidPath());
+
+ ASSERT_LT(result.best_result_index, result.paths.size());
+ const CertPathBuilderResultPath* failed_path =
+ result.paths[result.best_result_index].get();
+ ASSERT_TRUE(failed_path);
+
+ // An error should have been added to certificate at index 2 in the path.
+ const CertErrors* cert2_errors = failed_path->errors.GetErrorsForCert(2);
+ ASSERT_TRUE(cert2_errors);
+ EXPECT_TRUE(cert2_errors->ContainsError(kErrorFromDelegate));
+}
+
+TEST_F(PathBuilderCheckPathAfterVerificationTest, NoopToAlreadyInvalidPath) {
+ StrictMock<MockPathBuilderDelegate> delegate;
+ // Just verify that the hook is called (on an invalid path).
+ EXPECT_CALL(delegate, CheckPathAfterVerification(_, _));
+
+ // Run the pathbuilder with certificate at index 1 actively distrusted.
+ CertPathBuilder::Result result = RunPathBuilder(test_.chain[1], &delegate);
+ EXPECT_FALSE(result.HasValidPath());
+}
+
+struct DelegateData : public CertPathBuilderDelegateData {
+ int value = 0xB33F;
+};
+
+class SetsDelegateDataPathBuilderDelegate : public CertPathBuilderDelegateBase {
+ public:
+ void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+ CertPathBuilderResultPath* path) override {
+ path->delegate_data = std::make_unique<DelegateData>();
+ }
+};
+
+TEST_F(PathBuilderCheckPathAfterVerificationTest, SetsDelegateData) {
+ SetsDelegateDataPathBuilderDelegate delegate;
+ CertPathBuilder::Result result = RunPathBuilder(nullptr, &delegate);
+ ASSERT_TRUE(result.HasValidPath());
+
+ DelegateData* data = reinterpret_cast<DelegateData*>(
+ result.GetBestValidPath()->delegate_data.get());
+
+ EXPECT_EQ(0xB33F, data->value);
+}
+
+TEST(PathBuilderPrioritizationTest, DatePrioritization) {
+ std::string test_dir =
+ "net/data/path_builder_unittest/validity_date_prioritization/";
+ scoped_refptr<ParsedCertificate> root =
+ ReadCertFromFile(test_dir + "root.pem");
+ ASSERT_TRUE(root);
+ scoped_refptr<ParsedCertificate> int_ac =
+ ReadCertFromFile(test_dir + "int_ac.pem");
+ ASSERT_TRUE(int_ac);
+ scoped_refptr<ParsedCertificate> int_ad =
+ ReadCertFromFile(test_dir + "int_ad.pem");
+ ASSERT_TRUE(int_ad);
+ scoped_refptr<ParsedCertificate> int_bc =
+ ReadCertFromFile(test_dir + "int_bc.pem");
+ ASSERT_TRUE(int_bc);
+ scoped_refptr<ParsedCertificate> int_bd =
+ ReadCertFromFile(test_dir + "int_bd.pem");
+ ASSERT_TRUE(int_bd);
+ scoped_refptr<ParsedCertificate> target =
+ ReadCertFromFile(test_dir + "target.pem");
+ ASSERT_TRUE(target);
+
+ SimplePathBuilderDelegate delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+ der::GeneralizedTime verify_time = {2017, 3, 1, 0, 0, 0};
+
+ // Distrust the root certificate. This will force the path builder to attempt
+ // all possible paths.
+ TrustStoreInMemory trust_store;
+ trust_store.AddDistrustedCertificateForTest(root);
+
+ for (bool reverse_input_order : {false, true}) {
+ SCOPED_TRACE(reverse_input_order);
+
+ CertIssuerSourceStatic intermediates;
+ // Test with the intermediates supplied in two different orders to ensure
+ // the results don't depend on input ordering.
+ if (reverse_input_order) {
+ intermediates.AddCert(int_bd);
+ intermediates.AddCert(int_bc);
+ intermediates.AddCert(int_ad);
+ intermediates.AddCert(int_ac);
+ } else {
+ intermediates.AddCert(int_ac);
+ intermediates.AddCert(int_ad);
+ intermediates.AddCert(int_bc);
+ intermediates.AddCert(int_bd);
+ }
+
+ CertPathBuilder path_builder(
+ target, &trust_store, &delegate, verify_time, KeyPurpose::ANY_EKU,
+ InitialExplicitPolicy::kFalse, {der::Input(kAnyPolicyOid)},
+ InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse);
+ path_builder.AddCertIssuerSource(&intermediates);
+
+ CertPathBuilder::Result result = path_builder.Run();
+ EXPECT_FALSE(result.HasValidPath());
+ ASSERT_EQ(4U, result.paths.size());
+
+ // Path builder should have attempted paths using the intermediates in
+ // order: bd, bc, ad, ac
+
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ ASSERT_EQ(3U, result.paths[0]->certs.size());
+ EXPECT_EQ(target, result.paths[0]->certs[0]);
+ EXPECT_EQ(int_bd, result.paths[0]->certs[1]);
+ EXPECT_EQ(root, result.paths[0]->certs[2]);
+
+ EXPECT_FALSE(result.paths[1]->IsValid());
+ ASSERT_EQ(3U, result.paths[1]->certs.size());
+ EXPECT_EQ(target, result.paths[1]->certs[0]);
+ EXPECT_EQ(int_bc, result.paths[1]->certs[1]);
+ EXPECT_EQ(root, result.paths[1]->certs[2]);
+
+ EXPECT_FALSE(result.paths[2]->IsValid());
+ ASSERT_EQ(3U, result.paths[2]->certs.size());
+ EXPECT_EQ(target, result.paths[2]->certs[0]);
+ EXPECT_EQ(int_ad, result.paths[2]->certs[1]);
+ EXPECT_EQ(root, result.paths[2]->certs[2]);
+
+ EXPECT_FALSE(result.paths[3]->IsValid());
+ ASSERT_EQ(3U, result.paths[3]->certs.size());
+ EXPECT_EQ(target, result.paths[3]->certs[0]);
+ EXPECT_EQ(int_ac, result.paths[3]->certs[1]);
+ EXPECT_EQ(root, result.paths[3]->certs[2]);
+ }
+}
+
+TEST(PathBuilderPrioritizationTest, KeyIdPrioritization) {
+ std::string test_dir =
+ "net/data/path_builder_unittest/key_id_prioritization/";
+ scoped_refptr<ParsedCertificate> root =
+ ReadCertFromFile(test_dir + "root.pem");
+ ASSERT_TRUE(root);
+ scoped_refptr<ParsedCertificate> int_matching_ski_a =
+ ReadCertFromFile(test_dir + "int_matching_ski_a.pem");
+ ASSERT_TRUE(int_matching_ski_a);
+ scoped_refptr<ParsedCertificate> int_matching_ski_b =
+ ReadCertFromFile(test_dir + "int_matching_ski_b.pem");
+ ASSERT_TRUE(int_matching_ski_b);
+ scoped_refptr<ParsedCertificate> int_no_ski_a =
+ ReadCertFromFile(test_dir + "int_no_ski_a.pem");
+ ASSERT_TRUE(int_no_ski_a);
+ scoped_refptr<ParsedCertificate> int_no_ski_b =
+ ReadCertFromFile(test_dir + "int_no_ski_b.pem");
+ ASSERT_TRUE(int_no_ski_b);
+ scoped_refptr<ParsedCertificate> int_different_ski_a =
+ ReadCertFromFile(test_dir + "int_different_ski_a.pem");
+ ASSERT_TRUE(int_different_ski_a);
+ scoped_refptr<ParsedCertificate> int_different_ski_b =
+ ReadCertFromFile(test_dir + "int_different_ski_b.pem");
+ ASSERT_TRUE(int_different_ski_b);
+ scoped_refptr<ParsedCertificate> target =
+ ReadCertFromFile(test_dir + "target.pem");
+ ASSERT_TRUE(target);
+
+ SimplePathBuilderDelegate delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+ der::GeneralizedTime verify_time = {2017, 3, 1, 0, 0, 0};
+
+ // Distrust the root certificate. This will force the path builder to attempt
+ // all possible paths.
+ TrustStoreInMemory trust_store;
+ trust_store.AddDistrustedCertificateForTest(root);
+
+ for (bool reverse_input_order : {false, true}) {
+ SCOPED_TRACE(reverse_input_order);
+
+ CertIssuerSourceStatic intermediates;
+ // Test with the intermediates supplied in two different orders to ensure
+ // the results don't depend on input ordering.
+ if (reverse_input_order) {
+ intermediates.AddCert(int_different_ski_b);
+ intermediates.AddCert(int_different_ski_a);
+ intermediates.AddCert(int_no_ski_b);
+ intermediates.AddCert(int_no_ski_a);
+ intermediates.AddCert(int_matching_ski_b);
+ intermediates.AddCert(int_matching_ski_a);
+ } else {
+ intermediates.AddCert(int_matching_ski_a);
+ intermediates.AddCert(int_matching_ski_b);
+ intermediates.AddCert(int_no_ski_a);
+ intermediates.AddCert(int_no_ski_b);
+ intermediates.AddCert(int_different_ski_a);
+ intermediates.AddCert(int_different_ski_b);
+ }
+
+ CertPathBuilder path_builder(
+ target, &trust_store, &delegate, verify_time, KeyPurpose::ANY_EKU,
+ InitialExplicitPolicy::kFalse, {der::Input(kAnyPolicyOid)},
+ InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse);
+ path_builder.AddCertIssuerSource(&intermediates);
+
+ CertPathBuilder::Result result = path_builder.Run();
+ EXPECT_FALSE(result.HasValidPath());
+ ASSERT_EQ(6U, result.paths.size());
+
+ // Path builder should have attempted paths using the intermediates in
+ // order: matching_ski_b, matching_ski_a, no_ski_b, no_ski_a,
+ // different_ski_b, different_ski_a
+
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ ASSERT_EQ(3U, result.paths[0]->certs.size());
+ EXPECT_EQ(target, result.paths[0]->certs[0]);
+ EXPECT_EQ(int_matching_ski_b, result.paths[0]->certs[1]);
+ EXPECT_EQ(root, result.paths[0]->certs[2]);
+
+ EXPECT_FALSE(result.paths[1]->IsValid());
+ ASSERT_EQ(3U, result.paths[1]->certs.size());
+ EXPECT_EQ(target, result.paths[1]->certs[0]);
+ EXPECT_EQ(int_matching_ski_a, result.paths[1]->certs[1]);
+ EXPECT_EQ(root, result.paths[1]->certs[2]);
+
+ EXPECT_FALSE(result.paths[2]->IsValid());
+ ASSERT_EQ(3U, result.paths[2]->certs.size());
+ EXPECT_EQ(target, result.paths[2]->certs[0]);
+ EXPECT_EQ(int_no_ski_b, result.paths[2]->certs[1]);
+ EXPECT_EQ(root, result.paths[2]->certs[2]);
+
+ EXPECT_FALSE(result.paths[3]->IsValid());
+ ASSERT_EQ(3U, result.paths[3]->certs.size());
+ EXPECT_EQ(target, result.paths[3]->certs[0]);
+ EXPECT_EQ(int_no_ski_a, result.paths[3]->certs[1]);
+ EXPECT_EQ(root, result.paths[3]->certs[2]);
+
+ EXPECT_FALSE(result.paths[4]->IsValid());
+ ASSERT_EQ(3U, result.paths[4]->certs.size());
+ EXPECT_EQ(target, result.paths[4]->certs[0]);
+ EXPECT_EQ(int_different_ski_b, result.paths[4]->certs[1]);
+ EXPECT_EQ(root, result.paths[4]->certs[2]);
+
+ EXPECT_FALSE(result.paths[5]->IsValid());
+ ASSERT_EQ(3U, result.paths[5]->certs.size());
+ EXPECT_EQ(target, result.paths[5]->certs[0]);
+ EXPECT_EQ(int_different_ski_a, result.paths[5]->certs[1]);
+ EXPECT_EQ(root, result.paths[5]->certs[2]);
+ }
+}
+
+TEST(PathBuilderPrioritizationTest, TrustAndKeyIdPrioritization) {
+ std::string test_dir =
+ "net/data/path_builder_unittest/key_id_prioritization/";
+ scoped_refptr<ParsedCertificate> root =
+ ReadCertFromFile(test_dir + "root.pem");
+ ASSERT_TRUE(root);
+ scoped_refptr<ParsedCertificate> trusted_and_matching =
+ ReadCertFromFile(test_dir + "int_matching_ski_a.pem");
+ ASSERT_TRUE(trusted_and_matching);
+ scoped_refptr<ParsedCertificate> matching =
+ ReadCertFromFile(test_dir + "int_matching_ski_b.pem");
+ ASSERT_TRUE(matching);
+ scoped_refptr<ParsedCertificate> distrusted_and_matching =
+ ReadCertFromFile(test_dir + "int_matching_ski_c.pem");
+ ASSERT_TRUE(distrusted_and_matching);
+ scoped_refptr<ParsedCertificate> trusted_and_no_match_data =
+ ReadCertFromFile(test_dir + "int_no_ski_a.pem");
+ ASSERT_TRUE(trusted_and_no_match_data);
+ scoped_refptr<ParsedCertificate> no_match_data =
+ ReadCertFromFile(test_dir + "int_no_ski_b.pem");
+ ASSERT_TRUE(no_match_data);
+ scoped_refptr<ParsedCertificate> distrusted_and_no_match_data =
+ ReadCertFromFile(test_dir + "int_no_ski_c.pem");
+ ASSERT_TRUE(distrusted_and_no_match_data);
+ scoped_refptr<ParsedCertificate> trusted_and_mismatch =
+ ReadCertFromFile(test_dir + "int_different_ski_a.pem");
+ ASSERT_TRUE(trusted_and_mismatch);
+ scoped_refptr<ParsedCertificate> mismatch =
+ ReadCertFromFile(test_dir + "int_different_ski_b.pem");
+ ASSERT_TRUE(mismatch);
+ scoped_refptr<ParsedCertificate> distrusted_and_mismatch =
+ ReadCertFromFile(test_dir + "int_different_ski_c.pem");
+ ASSERT_TRUE(distrusted_and_mismatch);
+ scoped_refptr<ParsedCertificate> target =
+ ReadCertFromFile(test_dir + "target.pem");
+ ASSERT_TRUE(target);
+
+ SimplePathBuilderDelegate delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+ der::GeneralizedTime verify_time = {2017, 3, 1, 0, 0, 0};
+
+ for (bool reverse_input_order : {false, true}) {
+ SCOPED_TRACE(reverse_input_order);
+
+ TrustStoreInMemory trust_store;
+ // Test with the intermediates supplied in two different orders to ensure
+ // the results don't depend on input ordering.
+ if (reverse_input_order) {
+ trust_store.AddTrustAnchor(trusted_and_matching);
+ trust_store.AddCertificateWithUnspecifiedTrust(matching);
+ trust_store.AddDistrustedCertificateForTest(distrusted_and_matching);
+ trust_store.AddTrustAnchor(trusted_and_no_match_data);
+ trust_store.AddCertificateWithUnspecifiedTrust(no_match_data);
+ trust_store.AddDistrustedCertificateForTest(distrusted_and_no_match_data);
+ trust_store.AddTrustAnchor(trusted_and_mismatch);
+ trust_store.AddCertificateWithUnspecifiedTrust(mismatch);
+ trust_store.AddDistrustedCertificateForTest(distrusted_and_mismatch);
+ } else {
+ trust_store.AddDistrustedCertificateForTest(distrusted_and_matching);
+ trust_store.AddCertificateWithUnspecifiedTrust(no_match_data);
+ trust_store.AddTrustAnchor(trusted_and_no_match_data);
+ trust_store.AddTrustAnchor(trusted_and_matching);
+ trust_store.AddCertificateWithUnspecifiedTrust(matching);
+ trust_store.AddCertificateWithUnspecifiedTrust(mismatch);
+ trust_store.AddDistrustedCertificateForTest(distrusted_and_no_match_data);
+ trust_store.AddTrustAnchor(trusted_and_mismatch);
+ trust_store.AddDistrustedCertificateForTest(distrusted_and_mismatch);
+ }
+ // Also distrust the root certificate. This will force the path builder to
+ // report paths that included an unspecified trust intermediate.
+ trust_store.AddDistrustedCertificateForTest(root);
+
+ CertPathBuilder path_builder(
+ target, &trust_store, &delegate, verify_time, KeyPurpose::ANY_EKU,
+ InitialExplicitPolicy::kFalse, {der::Input(kAnyPolicyOid)},
+ InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse);
+ path_builder.SetExploreAllPaths(true);
+
+ CertPathBuilder::Result result = path_builder.Run();
+ EXPECT_TRUE(result.HasValidPath());
+ ASSERT_EQ(9U, result.paths.size());
+
+ // Path builder should have attempted paths using the intermediates in
+ // order: trusted_and_matching, trusted_and_no_match_data, matching,
+ // no_match_data, trusted_and_mismatch, mismatch, distrusted_and_matching,
+ // distrusted_and_no_match_data, distrusted_and_mismatch.
+
+ EXPECT_TRUE(result.paths[0]->IsValid());
+ ASSERT_EQ(2U, result.paths[0]->certs.size());
+ EXPECT_EQ(target, result.paths[0]->certs[0]);
+ EXPECT_EQ(trusted_and_matching, result.paths[0]->certs[1]);
+
+ EXPECT_TRUE(result.paths[1]->IsValid());
+ ASSERT_EQ(2U, result.paths[1]->certs.size());
+ EXPECT_EQ(target, result.paths[1]->certs[0]);
+ EXPECT_EQ(trusted_and_no_match_data, result.paths[1]->certs[1]);
+
+ EXPECT_FALSE(result.paths[2]->IsValid());
+ ASSERT_EQ(3U, result.paths[2]->certs.size());
+ EXPECT_EQ(target, result.paths[2]->certs[0]);
+ EXPECT_EQ(matching, result.paths[2]->certs[1]);
+ EXPECT_EQ(root, result.paths[2]->certs[2]);
+
+ EXPECT_FALSE(result.paths[3]->IsValid());
+ ASSERT_EQ(3U, result.paths[3]->certs.size());
+ EXPECT_EQ(target, result.paths[3]->certs[0]);
+ EXPECT_EQ(no_match_data, result.paths[3]->certs[1]);
+ EXPECT_EQ(root, result.paths[3]->certs[2]);
+
+ // Although this intermediate is trusted, it has the wrong key, so
+ // the path should not be valid.
+ EXPECT_FALSE(result.paths[4]->IsValid());
+ ASSERT_EQ(2U, result.paths[4]->certs.size());
+ EXPECT_EQ(target, result.paths[4]->certs[0]);
+ EXPECT_EQ(trusted_and_mismatch, result.paths[4]->certs[1]);
+
+ EXPECT_FALSE(result.paths[5]->IsValid());
+ ASSERT_EQ(3U, result.paths[5]->certs.size());
+ EXPECT_EQ(target, result.paths[5]->certs[0]);
+ EXPECT_EQ(mismatch, result.paths[5]->certs[1]);
+ EXPECT_EQ(root, result.paths[5]->certs[2]);
+
+ EXPECT_FALSE(result.paths[6]->IsValid());
+ ASSERT_EQ(2U, result.paths[6]->certs.size());
+ EXPECT_EQ(target, result.paths[6]->certs[0]);
+ EXPECT_EQ(distrusted_and_matching, result.paths[6]->certs[1]);
+
+ EXPECT_FALSE(result.paths[7]->IsValid());
+ ASSERT_EQ(2U, result.paths[7]->certs.size());
+ EXPECT_EQ(target, result.paths[7]->certs[0]);
+ EXPECT_EQ(distrusted_and_no_match_data, result.paths[7]->certs[1]);
+
+ EXPECT_FALSE(result.paths[8]->IsValid());
+ ASSERT_EQ(2U, result.paths[8]->certs.size());
+ EXPECT_EQ(target, result.paths[8]->certs[0]);
+ EXPECT_EQ(distrusted_and_mismatch, result.paths[8]->certs[1]);
+ }
+}
+
+// PathBuilder does not support prioritization based on the issuer name &
+// serial in authorityKeyIdentifier, so this test just ensures that it does not
+// affect prioritization order and that it is generally just ignored
+// completely.
+TEST(PathBuilderPrioritizationTest, KeyIdNameAndSerialPrioritization) {
+ std::string test_dir =
+ "net/data/path_builder_unittest/key_id_name_and_serial_prioritization/";
+ scoped_refptr<ParsedCertificate> root =
+ ReadCertFromFile(test_dir + "root.pem");
+ ASSERT_TRUE(root);
+ scoped_refptr<ParsedCertificate> root2 =
+ ReadCertFromFile(test_dir + "root2.pem");
+ ASSERT_TRUE(root2);
+ scoped_refptr<ParsedCertificate> int_matching =
+ ReadCertFromFile(test_dir + "int_matching.pem");
+ ASSERT_TRUE(int_matching);
+ scoped_refptr<ParsedCertificate> int_match_name_only =
+ ReadCertFromFile(test_dir + "int_match_name_only.pem");
+ ASSERT_TRUE(int_match_name_only);
+ scoped_refptr<ParsedCertificate> int_mismatch =
+ ReadCertFromFile(test_dir + "int_mismatch.pem");
+ ASSERT_TRUE(int_mismatch);
+ scoped_refptr<ParsedCertificate> target =
+ ReadCertFromFile(test_dir + "target.pem");
+ ASSERT_TRUE(target);
+
+ SimplePathBuilderDelegate delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+ der::GeneralizedTime verify_time = {2017, 3, 1, 0, 0, 0};
+
+ // Distrust the root certificates. This will force the path builder to attempt
+ // all possible paths.
+ TrustStoreInMemory trust_store;
+ trust_store.AddDistrustedCertificateForTest(root);
+ trust_store.AddDistrustedCertificateForTest(root2);
+
+ for (bool reverse_input_order : {false, true}) {
+ SCOPED_TRACE(reverse_input_order);
+
+ CertIssuerSourceStatic intermediates;
+ // Test with the intermediates supplied in two different orders to ensure
+ // the results don't depend on input ordering.
+ if (reverse_input_order) {
+ intermediates.AddCert(int_mismatch);
+ intermediates.AddCert(int_match_name_only);
+ intermediates.AddCert(int_matching);
+ } else {
+ intermediates.AddCert(int_matching);
+ intermediates.AddCert(int_match_name_only);
+ intermediates.AddCert(int_mismatch);
+ }
+
+ CertPathBuilder path_builder(
+ target, &trust_store, &delegate, verify_time, KeyPurpose::ANY_EKU,
+ InitialExplicitPolicy::kFalse, {der::Input(kAnyPolicyOid)},
+ InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse);
+ path_builder.AddCertIssuerSource(&intermediates);
+
+ CertPathBuilder::Result result = path_builder.Run();
+ EXPECT_FALSE(result.HasValidPath());
+ ASSERT_EQ(3U, result.paths.size());
+
+ // The serial & issuer method is not used in prioritization, so the certs
+ // should have been prioritized based on dates. The test certs have the
+ // date priority order in the reverse of what authorityKeyIdentifier
+ // prioritization would have done if it were supported.
+ // Path builder should have attempted paths using the intermediates in
+ // order: mismatch, match_name_only, matching
+
+ EXPECT_FALSE(result.paths[0]->IsValid());
+ ASSERT_EQ(3U, result.paths[0]->certs.size());
+ EXPECT_EQ(target, result.paths[0]->certs[0]);
+ EXPECT_EQ(int_mismatch, result.paths[0]->certs[1]);
+ EXPECT_EQ(root2, result.paths[0]->certs[2]);
+
+ EXPECT_FALSE(result.paths[1]->IsValid());
+ ASSERT_EQ(3U, result.paths[1]->certs.size());
+ EXPECT_EQ(target, result.paths[1]->certs[0]);
+ EXPECT_EQ(int_match_name_only, result.paths[1]->certs[1]);
+ EXPECT_EQ(root, result.paths[1]->certs[2]);
+
+ EXPECT_FALSE(result.paths[2]->IsValid());
+ ASSERT_EQ(3U, result.paths[2]->certs.size());
+ EXPECT_EQ(target, result.paths[2]->certs[0]);
+ EXPECT_EQ(int_matching, result.paths[2]->certs[1]);
+ EXPECT_EQ(root, result.paths[2]->certs[2]);
+ }
+}
+
+TEST(PathBuilderPrioritizationTest, SelfIssuedPrioritization) {
+ std::string test_dir =
+ "net/data/path_builder_unittest/self_issued_prioritization/";
+ scoped_refptr<ParsedCertificate> root1 =
+ ReadCertFromFile(test_dir + "root1.pem");
+ ASSERT_TRUE(root1);
+ scoped_refptr<ParsedCertificate> root1_cross =
+ ReadCertFromFile(test_dir + "root1_cross.pem");
+ ASSERT_TRUE(root1_cross);
+ scoped_refptr<ParsedCertificate> target =
+ ReadCertFromFile(test_dir + "target.pem");
+ ASSERT_TRUE(target);
+
+ SimplePathBuilderDelegate delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+ der::GeneralizedTime verify_time = {2017, 3, 1, 0, 0, 0};
+
+ TrustStoreInMemory trust_store;
+ trust_store.AddTrustAnchor(root1);
+ trust_store.AddTrustAnchor(root1_cross);
+ CertPathBuilder path_builder(
+ target, &trust_store, &delegate, verify_time, KeyPurpose::ANY_EKU,
+ InitialExplicitPolicy::kFalse, {der::Input(kAnyPolicyOid)},
+ InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse);
+ path_builder.SetExploreAllPaths(true);
+
+ CertPathBuilder::Result result = path_builder.Run();
+ EXPECT_TRUE(result.HasValidPath());
+
+ // Path builder should have built paths to both trusted roots.
+ ASSERT_EQ(2U, result.paths.size());
+
+ // |root1| should have been preferred because it is self-issued, even though
+ // the notBefore date is older than |root1_cross|.
+ EXPECT_TRUE(result.paths[0]->IsValid());
+ ASSERT_EQ(2U, result.paths[0]->certs.size());
+ EXPECT_EQ(target, result.paths[0]->certs[0]);
+ EXPECT_EQ(root1, result.paths[0]->certs[1]);
+
+ EXPECT_TRUE(result.paths[1]->IsValid());
+ ASSERT_EQ(2U, result.paths[1]->certs.size());
+ EXPECT_EQ(target, result.paths[1]->certs[0]);
+ EXPECT_EQ(root1_cross, result.paths[1]->certs[1]);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/pki/path_builder_verify_certificate_chain_unittest.cc b/chromium/net/cert/pki/path_builder_verify_certificate_chain_unittest.cc
new file mode 100644
index 00000000000..1db806bb67a
--- /dev/null
+++ b/chromium/net/cert/pki/path_builder_verify_certificate_chain_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright 2016 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 "net/cert/pki/path_builder.h"
+
+#include "net/cert/pki/cert_issuer_source_static.h"
+#include "net/cert/pki/simple_path_builder_delegate.h"
+#include "net/cert/pki/trust_store_in_memory.h"
+#include "net/cert/pki/verify_certificate_chain_typed_unittest.h"
+
+namespace net {
+
+namespace {
+
+class PathBuilderTestDelegate {
+ public:
+ static void Verify(const VerifyCertChainTest& test,
+ const std::string& test_file_path) {
+ SimplePathBuilderDelegate path_builder_delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+ ASSERT_FALSE(test.chain.empty());
+
+ TrustStoreInMemory trust_store;
+
+ switch (test.last_cert_trust.type) {
+ case CertificateTrustType::TRUSTED_ANCHOR:
+ trust_store.AddTrustAnchor(test.chain.back());
+ break;
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+ trust_store.AddTrustAnchorWithExpiration(test.chain.back());
+ break;
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
+ trust_store.AddTrustAnchorWithConstraints(test.chain.back());
+ break;
+ case CertificateTrustType::UNSPECIFIED:
+ trust_store.AddCertificateWithUnspecifiedTrust(test.chain.back());
+ break;
+ case CertificateTrustType::DISTRUSTED:
+ trust_store.AddDistrustedCertificateForTest(test.chain.back());
+ break;
+ }
+
+ CertIssuerSourceStatic intermediate_cert_issuer_source;
+ for (size_t i = 1; i < test.chain.size(); ++i)
+ intermediate_cert_issuer_source.AddCert(test.chain[i]);
+
+ // First cert in the |chain| is the target.
+ CertPathBuilder path_builder(
+ test.chain.front(), &trust_store, &path_builder_delegate, test.time,
+ test.key_purpose, test.initial_explicit_policy,
+ test.user_initial_policy_set, test.initial_policy_mapping_inhibit,
+ test.initial_any_policy_inhibit);
+ path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
+
+ CertPathBuilder::Result result = path_builder.Run();
+ EXPECT_EQ(!test.HasHighSeverityErrors(), result.HasValidPath());
+ }
+};
+
+} // namespace
+
+INSTANTIATE_TYPED_TEST_SUITE_P(PathBuilder,
+ VerifyCertificateChainSingleRootTest,
+ PathBuilderTestDelegate);
+
+} // namespace net
diff --git a/chromium/net/cert/pki/revocation_util.cc b/chromium/net/cert/pki/revocation_util.cc
new file mode 100644
index 00000000000..17a75b03c8e
--- /dev/null
+++ b/chromium/net/cert/pki/revocation_util.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 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 "net/cert/pki/revocation_util.h"
+
+#include "base/time/time.h"
+#include "net/der/encode_values.h"
+#include "net/der/parse_values.h"
+
+namespace net {
+
+bool CheckRevocationDateValid(const der::GeneralizedTime& this_update,
+ const der::GeneralizedTime* next_update,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age) {
+ der::GeneralizedTime verify_time_der;
+ if (!der::EncodeTimeAsGeneralizedTime(verify_time, &verify_time_der))
+ return false;
+
+ if (this_update > verify_time_der)
+ return false; // Response is not yet valid.
+
+ if (next_update && (*next_update <= verify_time_der))
+ return false; // Response is no longer valid.
+
+ der::GeneralizedTime earliest_this_update;
+ if (!der::EncodeTimeAsGeneralizedTime(verify_time - max_age,
+ &earliest_this_update)) {
+ return false;
+ }
+ if (this_update < earliest_this_update)
+ return false; // Response is too old.
+
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/revocation_util.h b/chromium/net/cert/pki/revocation_util.h
new file mode 100644
index 00000000000..2966a0542de
--- /dev/null
+++ b/chromium/net/cert/pki/revocation_util.h
@@ -0,0 +1,33 @@
+// Copyright 2019 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 NET_CERT_PKI_REVOCATION_UTIL_H_
+#define NET_CERT_PKI_REVOCATION_UTIL_H_
+
+#include "net/base/net_export.h"
+
+namespace base {
+class Time;
+class TimeDelta;
+} // namespace base
+
+namespace net {
+
+namespace der {
+struct GeneralizedTime;
+}
+
+// Returns true if a revocation status with |this_update| field and potentially
+// a |next_update| field, is valid at |verify_time| and not older than
+// |max_age|. Expressed differently, returns true if |this_update <=
+// verify_time < next_update|, and |this_update >= verify_time - max_age|.
+[[nodiscard]] NET_EXPORT_PRIVATE bool CheckRevocationDateValid(
+ const der::GeneralizedTime& this_update,
+ const der::GeneralizedTime* next_update,
+ const base::Time& verify_time,
+ const base::TimeDelta& max_age);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_REVOCATION_UTIL_H_
diff --git a/chromium/net/cert/pki/signature_algorithm.cc b/chromium/net/cert/pki/signature_algorithm.cc
new file mode 100644
index 00000000000..a7ff1852587
--- /dev/null
+++ b/chromium/net/cert/pki/signature_algorithm.cc
@@ -0,0 +1,487 @@
+// Copyright 2015 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 "net/cert/pki/signature_algorithm.h"
+
+#include "base/check.h"
+#include "net/cert/pki/cert_error_params.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "net/der/parser.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "third_party/boringssl/src/include/openssl/digest.h"
+
+namespace net {
+
+namespace {
+
+// md2WithRSAEncryption
+// In dotted notation: 1.2.840.113549.1.1.2
+const uint8_t kOidMd2WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x02};
+
+// md4WithRSAEncryption
+// In dotted notation: 1.2.840.113549.1.1.3
+const uint8_t kOidMd4WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x03};
+
+// md5WithRSAEncryption
+// In dotted notation: 1.2.840.113549.1.1.4
+const uint8_t kOidMd5WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x04};
+
+// From RFC 5912:
+//
+// sha1WithRSAEncryption OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
+// pkcs-1(1) 5 }
+//
+// In dotted notation: 1.2.840.113549.1.1.5
+const uint8_t kOidSha1WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05};
+
+// sha1WithRSASignature is a deprecated equivalent of
+// sha1WithRSAEncryption.
+//
+// It originates from the NIST Open Systems Environment (OSE)
+// Implementor's Workshop (OIW).
+//
+// It is supported for compatibility with Microsoft's certificate APIs and
+// tools, particularly makecert.exe, which default(ed/s) to this OID for SHA-1.
+//
+// See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1042479
+//
+// In dotted notation: 1.3.14.3.2.29
+const uint8_t kOidSha1WithRsaSignature[] = {0x2b, 0x0e, 0x03, 0x02, 0x1d};
+
+// From RFC 5912:
+//
+// pkcs-1 OBJECT IDENTIFIER ::=
+// { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
+
+// From RFC 5912:
+//
+// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 }
+//
+// In dotted notation: 1.2.840.113549.1.1.11
+const uint8_t kOidSha256WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b};
+
+// From RFC 5912:
+//
+// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 }
+//
+// In dotted notation: 1.2.840.113549.1.1.11
+const uint8_t kOidSha384WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0c};
+
+// From RFC 5912:
+//
+// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 }
+//
+// In dotted notation: 1.2.840.113549.1.1.13
+const uint8_t kOidSha512WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0d};
+
+// From RFC 5912:
+//
+// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) ansi-X9-62(10045)
+// signatures(4) 1 }
+//
+// In dotted notation: 1.2.840.10045.4.1
+const uint8_t kOidEcdsaWithSha1[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01};
+
+// From RFC 5912:
+//
+// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
+// ecdsa-with-SHA2(3) 2 }
+//
+// In dotted notation: 1.2.840.10045.4.3.2
+const uint8_t kOidEcdsaWithSha256[] = {0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x02};
+
+// From RFC 5912:
+//
+// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
+// ecdsa-with-SHA2(3) 3 }
+//
+// In dotted notation: 1.2.840.10045.4.3.3
+const uint8_t kOidEcdsaWithSha384[] = {0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x03};
+
+// From RFC 5912:
+//
+// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
+// ecdsa-with-SHA2(3) 4 }
+//
+// In dotted notation: 1.2.840.10045.4.3.4
+const uint8_t kOidEcdsaWithSha512[] = {0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x04};
+
+// From RFC 5912:
+//
+// id-RSASSA-PSS OBJECT IDENTIFIER ::= { pkcs-1 10 }
+//
+// In dotted notation: 1.2.840.113549.1.1.10
+const uint8_t kOidRsaSsaPss[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0a};
+
+// From RFC 5912:
+//
+// dsa-with-sha1 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 3 }
+//
+// In dotted notation: 1.2.840.10040.4.3
+const uint8_t kOidDsaWithSha1[] = {0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x03};
+
+// From RFC 5912:
+//
+// dsa-with-sha256 OBJECT IDENTIFIER ::= {
+// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101)
+// csor(3) algorithms(4) id-dsa-with-sha2(3) 2 }
+//
+// In dotted notation: 2.16.840.1.101.3.4.3.2
+const uint8_t kOidDsaWithSha256[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x04, 0x03, 0x02};
+
+// From RFC 5912:
+//
+// id-mgf1 OBJECT IDENTIFIER ::= { pkcs-1 8 }
+//
+// In dotted notation: 1.2.840.113549.1.1.8
+const uint8_t kOidMgf1[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x08};
+
+// Returns true if |input| is empty.
+[[nodiscard]] bool IsEmpty(const der::Input& input) {
+ return input.Length() == 0;
+}
+
+// Returns true if the entirety of the input is a NULL value.
+[[nodiscard]] bool IsNull(const der::Input& input) {
+ der::Parser parser(input);
+ der::Input null_value;
+ if (!parser.ReadTag(der::kNull, &null_value))
+ return false;
+
+ // NULL values are TLV encoded; the value is expected to be empty.
+ if (!IsEmpty(null_value))
+ return false;
+
+ // By definition of this function, the entire input must be a NULL.
+ return !parser.HasMore();
+}
+
+[[nodiscard]] bool IsNullOrEmpty(const der::Input& input) {
+ return IsNull(input) || IsEmpty(input);
+}
+
+// Parses a MaskGenAlgorithm as defined by RFC 5912:
+//
+// MaskGenAlgorithm ::= AlgorithmIdentifier{ALGORITHM,
+// {PKCS1MGFAlgorithms}}
+//
+// mgf1SHA1 MaskGenAlgorithm ::= {
+// algorithm id-mgf1,
+// parameters HashAlgorithm : sha1Identifier
+// }
+//
+// --
+// -- Define the set of mask generation functions
+// --
+// -- If the identifier is id-mgf1, any of the listed hash
+// -- algorithms may be used.
+// --
+//
+// PKCS1MGFAlgorithms ALGORITHM ::= {
+// { IDENTIFIER id-mgf1 PARAMS TYPE HashAlgorithm ARE required },
+// ...
+// }
+//
+// Note that the possible mask gen algorithms is extensible. However at present
+// the only function supported is MGF1, as that is the singular mask gen
+// function defined by RFC 4055 / RFC 5912.
+[[nodiscard]] bool ParseMaskGenAlgorithm(const der::Input input,
+ DigestAlgorithm* mgf1_hash) {
+ der::Input oid;
+ der::Input params;
+ if (!ParseAlgorithmIdentifier(input, &oid, &params))
+ return false;
+
+ // MGF1 is the only supported mask generation algorithm.
+ if (oid != der::Input(kOidMgf1))
+ return false;
+
+ return ParseHashAlgorithm(params, mgf1_hash);
+}
+
+// Parses the parameters for an RSASSA-PSS signature algorithm, as defined by
+// RFC 5912:
+//
+// sa-rsaSSA-PSS SIGNATURE-ALGORITHM ::= {
+// IDENTIFIER id-RSASSA-PSS
+// PARAMS TYPE RSASSA-PSS-params ARE required
+// HASHES { mda-sha1 | mda-sha224 | mda-sha256 | mda-sha384
+// | mda-sha512 }
+// PUBLIC-KEYS { pk-rsa | pk-rsaSSA-PSS }
+// SMIME-CAPS { IDENTIFIED BY id-RSASSA-PSS }
+// }
+//
+// RSASSA-PSS-params ::= SEQUENCE {
+// hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier,
+// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
+// saltLength [2] INTEGER DEFAULT 20,
+// trailerField [3] INTEGER DEFAULT 1
+// }
+//
+// Which is to say the parameters MUST be present, and of type
+// RSASSA-PSS-params. Additionally, we only support the RSA-PSS parameter
+// combinations representable by TLS 1.3 (RFC 8446).
+//
+// Note also that DER encoding (ITU-T X.690 section 11.5) prohibits
+// specifying default values explicitly. The parameter should instead be
+// omitted to indicate a default value.
+absl::optional<SignatureAlgorithm> ParseRsaPss(const der::Input& params) {
+ der::Parser parser(params);
+ der::Parser params_parser;
+ if (!parser.ReadSequence(&params_parser))
+ return absl::nullopt;
+
+ // There shouldn't be anything after the sequence (by definition the
+ // parameters is a single sequence).
+ if (parser.HasMore())
+ return absl::nullopt;
+
+ // The default values for hashAlgorithm, maskGenAlgorithm, and saltLength
+ // correspond to SHA-1, which we do not support with RSA-PSS, so treat them as
+ // required fields. Explicitly-specified defaults will be rejected later, when
+ // we limit combinations. Additionally, as the trailerField is required to be
+ // the default, we simply ignore it and reject it as any other trailing data.
+ //
+ // hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier,
+ // maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
+ // saltLength [2] INTEGER DEFAULT 20,
+ // trailerField [3] INTEGER DEFAULT 1
+ der::Input field;
+ DigestAlgorithm hash, mgf1_hash;
+ der::Parser salt_length_parser;
+ uint64_t salt_length;
+ if (!params_parser.ReadTag(der::ContextSpecificConstructed(0), &field) ||
+ !ParseHashAlgorithm(field, &hash) ||
+ !params_parser.ReadTag(der::ContextSpecificConstructed(1), &field) ||
+ !ParseMaskGenAlgorithm(field, &mgf1_hash) ||
+ !params_parser.ReadConstructed(der::ContextSpecificConstructed(2),
+ &salt_length_parser) ||
+ !salt_length_parser.ReadUint64(&salt_length) ||
+ salt_length_parser.HasMore() || params_parser.HasMore()) {
+ return absl::nullopt;
+ }
+
+ // Only combinations of RSASSA-PSS-params specified by TLS 1.3 (RFC 8446) are
+ // supported.
+ if (hash != mgf1_hash) {
+ return absl::nullopt; // TLS 1.3 always matches MGF-1 and message hash.
+ }
+ if (hash == DigestAlgorithm::Sha256 && salt_length == 32) {
+ return SignatureAlgorithm::kRsaPssSha256;
+ }
+ if (hash == DigestAlgorithm::Sha384 && salt_length == 48) {
+ return SignatureAlgorithm::kRsaPssSha384;
+ }
+ if (hash == DigestAlgorithm::Sha512 && salt_length == 64) {
+ return SignatureAlgorithm::kRsaPssSha512;
+ }
+
+ return absl::nullopt;
+}
+
+DEFINE_CERT_ERROR_ID(kUnknownSignatureAlgorithm, "Unknown signature algorithm");
+
+} // namespace
+
+[[nodiscard]] bool ParseAlgorithmIdentifier(const der::Input& input,
+ der::Input* algorithm,
+ der::Input* parameters) {
+ der::Parser parser(input);
+
+ der::Parser algorithm_identifier_parser;
+ if (!parser.ReadSequence(&algorithm_identifier_parser))
+ return false;
+
+ // There shouldn't be anything after the sequence. This is by definition,
+ // as the input to this function is expected to be a single
+ // AlgorithmIdentifier.
+ if (parser.HasMore())
+ return false;
+
+ if (!algorithm_identifier_parser.ReadTag(der::kOid, algorithm))
+ return false;
+
+ // Read the optional parameters to a der::Input. The parameters can be at
+ // most one TLV (for instance NULL or a sequence).
+ //
+ // Note that nothing is allowed after the single optional "parameters" TLV.
+ // This is because RFC 5912's notation for AlgorithmIdentifier doesn't
+ // explicitly list an extension point after "parameters".
+ *parameters = der::Input();
+ if (algorithm_identifier_parser.HasMore() &&
+ !algorithm_identifier_parser.ReadRawTLV(parameters)) {
+ return false;
+ }
+ return !algorithm_identifier_parser.HasMore();
+}
+
+[[nodiscard]] bool ParseHashAlgorithm(const der::Input& input,
+ DigestAlgorithm* out) {
+ CBS cbs;
+ CBS_init(&cbs, input.UnsafeData(), input.Length());
+ const EVP_MD* md = EVP_parse_digest_algorithm(&cbs);
+
+ if (md == EVP_sha1()) {
+ *out = DigestAlgorithm::Sha1;
+ } else if (md == EVP_sha256()) {
+ *out = DigestAlgorithm::Sha256;
+ } else if (md == EVP_sha384()) {
+ *out = DigestAlgorithm::Sha384;
+ } else if (md == EVP_sha512()) {
+ *out = DigestAlgorithm::Sha512;
+ } else {
+ // TODO(eroman): Support MD2, MD4, MD5 for completeness?
+ // Unsupported digest algorithm.
+ return false;
+ }
+
+ return true;
+}
+
+absl::optional<SignatureAlgorithm> ParseSignatureAlgorithm(
+ const der::Input& algorithm_identifier,
+ CertErrors* errors) {
+ der::Input oid;
+ der::Input params;
+ if (!ParseAlgorithmIdentifier(algorithm_identifier, &oid, &params))
+ return absl::nullopt;
+
+ // TODO(eroman): Each OID is tested for equality in order, which is not
+ // particularly efficient.
+
+ // RFC 5912 requires that the parameters for RSA PKCS#1 v1.5 algorithms be
+ // NULL ("PARAMS TYPE NULL ARE required"), however an empty parameter is also
+ // allowed for compatibility with non-compliant OCSP responders.
+ //
+ // TODO(svaldez): Add warning about non-strict parsing.
+ if (oid == der::Input(kOidSha1WithRsaEncryption) && IsNullOrEmpty(params)) {
+ return SignatureAlgorithm::kRsaPkcs1Sha1;
+ }
+ if (oid == der::Input(kOidSha256WithRsaEncryption) && IsNullOrEmpty(params)) {
+ return SignatureAlgorithm::kRsaPkcs1Sha256;
+ }
+ if (oid == der::Input(kOidSha384WithRsaEncryption) && IsNullOrEmpty(params)) {
+ return SignatureAlgorithm::kRsaPkcs1Sha384;
+ }
+ if (oid == der::Input(kOidSha512WithRsaEncryption) && IsNullOrEmpty(params)) {
+ return SignatureAlgorithm::kRsaPkcs1Sha512;
+ }
+ if (oid == der::Input(kOidSha1WithRsaSignature) && IsNullOrEmpty(params)) {
+ return SignatureAlgorithm::kRsaPkcs1Sha1;
+ }
+ if (oid == der::Input(kOidMd2WithRsaEncryption) && IsNullOrEmpty(params)) {
+ return SignatureAlgorithm::kRsaPkcs1Md2;
+ }
+ if (oid == der::Input(kOidMd4WithRsaEncryption) && IsNullOrEmpty(params)) {
+ return SignatureAlgorithm::kRsaPkcs1Md4;
+ }
+ if (oid == der::Input(kOidMd5WithRsaEncryption) && IsNullOrEmpty(params)) {
+ return SignatureAlgorithm::kRsaPkcs1Md5;
+ }
+
+ // RFC 5912 requires that the parameters for ECDSA algorithms be absent
+ // ("PARAMS TYPE NULL ARE absent"):
+ if (oid == der::Input(kOidEcdsaWithSha1) && IsEmpty(params)) {
+ return SignatureAlgorithm::kEcdsaSha1;
+ }
+ if (oid == der::Input(kOidEcdsaWithSha256) && IsEmpty(params)) {
+ return SignatureAlgorithm::kEcdsaSha256;
+ }
+ if (oid == der::Input(kOidEcdsaWithSha384) && IsEmpty(params)) {
+ return SignatureAlgorithm::kEcdsaSha384;
+ }
+ if (oid == der::Input(kOidEcdsaWithSha512) && IsEmpty(params)) {
+ return SignatureAlgorithm::kEcdsaSha512;
+ }
+
+ if (oid == der::Input(kOidRsaSsaPss)) {
+ return ParseRsaPss(params);
+ }
+
+ // RFC 5912 requires that the parameters for DSA algorithms be absent.
+ //
+ // TODO(svaldez): Add warning about non-strict parsing.
+ if (oid == der::Input(kOidDsaWithSha1) && IsNullOrEmpty(params)) {
+ return SignatureAlgorithm::kDsaSha1;
+ }
+ if (oid == der::Input(kOidDsaWithSha256) && IsNullOrEmpty(params)) {
+ return SignatureAlgorithm::kDsaSha256;
+ }
+
+ // Unknown signature algorithm.
+ if (errors) {
+ errors->AddError(kUnknownSignatureAlgorithm,
+ CreateCertErrorParams2Der("oid", oid, "params", params));
+ }
+ return absl::nullopt;
+}
+
+absl::optional<DigestAlgorithm> GetTlsServerEndpointDigestAlgorithm(
+ SignatureAlgorithm alg) {
+ // See RFC 5929, section 4.1. RFC 5929 breaks the signature algorithm
+ // abstraction by trying to extract individual digest algorithms. (While
+ // common, this is not a universal property of signature algorithms.) We
+ // implement this within the library, so callers do not need to condition over
+ // all algorithms.
+ switch (alg) {
+ // If the single digest algorithm is MD5 or SHA-1, use SHA-256.
+ case SignatureAlgorithm::kRsaPkcs1Md5:
+ case SignatureAlgorithm::kRsaPkcs1Sha1:
+ case SignatureAlgorithm::kEcdsaSha1:
+ return DigestAlgorithm::Sha256;
+
+ case SignatureAlgorithm::kRsaPkcs1Sha256:
+ case SignatureAlgorithm::kEcdsaSha256:
+ return DigestAlgorithm::Sha256;
+
+ case SignatureAlgorithm::kRsaPkcs1Sha384:
+ case SignatureAlgorithm::kEcdsaSha384:
+ return DigestAlgorithm::Sha384;
+
+ case SignatureAlgorithm::kRsaPkcs1Sha512:
+ case SignatureAlgorithm::kEcdsaSha512:
+ return DigestAlgorithm::Sha512;
+
+ // It is ambiguous whether hash-matching RSASSA-PSS instantiations count as
+ // using one or multiple digests, but the corresponding digest is the only
+ // reasonable interpretation.
+ case SignatureAlgorithm::kRsaPssSha256:
+ return DigestAlgorithm::Sha256;
+ case SignatureAlgorithm::kRsaPssSha384:
+ return DigestAlgorithm::Sha384;
+ case SignatureAlgorithm::kRsaPssSha512:
+ return DigestAlgorithm::Sha512;
+
+ // Do not return anything for these legacy algorithms.
+ case SignatureAlgorithm::kDsaSha1:
+ case SignatureAlgorithm::kDsaSha256:
+ case SignatureAlgorithm::kRsaPkcs1Md2:
+ case SignatureAlgorithm::kRsaPkcs1Md4:
+ return absl::nullopt;
+ }
+ return absl::nullopt;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/signature_algorithm.h b/chromium/net/cert/pki/signature_algorithm.h
new file mode 100644
index 00000000000..e6e2569bbae
--- /dev/null
+++ b/chromium/net/cert/pki/signature_algorithm.h
@@ -0,0 +1,95 @@
+// Copyright 2015 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 NET_CERT_PKI_SIGNATURE_ALGORITHM_H_
+#define NET_CERT_PKI_SIGNATURE_ALGORITHM_H_
+
+#include <stdint.h>
+
+#include "net/base/net_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+
+class CertErrors;
+
+namespace der {
+class Input;
+} // namespace der
+
+// The digest algorithm used within a signature.
+enum class DigestAlgorithm {
+ Md2,
+ Md4,
+ Md5,
+ Sha1,
+ Sha256,
+ Sha384,
+ Sha512,
+};
+
+// The signature algorithm used within a certificate.
+enum class SignatureAlgorithm {
+ kRsaPkcs1Sha1,
+ kRsaPkcs1Sha256,
+ kRsaPkcs1Sha384,
+ kRsaPkcs1Sha512,
+ kEcdsaSha1,
+ kEcdsaSha256,
+ kEcdsaSha384,
+ kEcdsaSha512,
+ // These RSA-PSS constants match RFC 8446 and refer to RSASSA-PSS with MGF-1,
+ // using the specified hash as both the signature and MGF-1 hash, and the hash
+ // length as the salt length.
+ kRsaPssSha256,
+ kRsaPssSha384,
+ kRsaPssSha512,
+ // These algorithms can be parsed but are not supported.
+ // TODO(https://crbug.com/1321688): Remove these.
+ kRsaPkcs1Md2,
+ kRsaPkcs1Md4,
+ kRsaPkcs1Md5,
+ kDsaSha1,
+ kDsaSha256,
+};
+
+// Parses AlgorithmIdentifier as defined by RFC 5280 section 4.1.1.2:
+//
+// AlgorithmIdentifier ::= SEQUENCE {
+// algorithm OBJECT IDENTIFIER,
+// parameters ANY DEFINED BY algorithm OPTIONAL }
+[[nodiscard]] NET_EXPORT bool ParseAlgorithmIdentifier(const der::Input& input,
+ der::Input* algorithm,
+ der::Input* parameters);
+
+// Parses a HashAlgorithm as defined by RFC 5912:
+//
+// HashAlgorithm ::= AlgorithmIdentifier{DIGEST-ALGORITHM,
+// {HashAlgorithms}}
+//
+// HashAlgorithms DIGEST-ALGORITHM ::= {
+// { IDENTIFIER id-sha1 PARAMS TYPE NULL ARE preferredPresent } |
+// { IDENTIFIER id-sha224 PARAMS TYPE NULL ARE preferredPresent } |
+// { IDENTIFIER id-sha256 PARAMS TYPE NULL ARE preferredPresent } |
+// { IDENTIFIER id-sha384 PARAMS TYPE NULL ARE preferredPresent } |
+// { IDENTIFIER id-sha512 PARAMS TYPE NULL ARE preferredPresent }
+// }
+[[nodiscard]] bool ParseHashAlgorithm(const der::Input& input,
+ DigestAlgorithm* out);
+
+// Parses an AlgorithmIdentifier into a signature algorithm and returns it, or
+// returns `absl::nullopt` if `algorithm_identifer` either cannot be parsed or
+// is not a recognized signature algorithm.
+NET_EXPORT absl::optional<SignatureAlgorithm> ParseSignatureAlgorithm(
+ const der::Input& algorithm_identifier,
+ CertErrors* errors);
+
+// Returns the hash to be used with the tls-server-end-point channel binding
+// (RFC 5929) or `absl::nullopt`, if not supported for this signature algorithm.
+absl::optional<DigestAlgorithm> GetTlsServerEndpointDigestAlgorithm(
+ SignatureAlgorithm alg);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_SIGNATURE_ALGORITHM_H_
diff --git a/chromium/net/cert/pki/signature_algorithm_unittest.cc b/chromium/net/cert/pki/signature_algorithm_unittest.cc
new file mode 100644
index 00000000000..2247675ca76
--- /dev/null
+++ b/chromium/net/cert/pki/signature_algorithm_unittest.cc
@@ -0,0 +1,1468 @@
+// Copyright 2015 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 "net/cert/pki/signature_algorithm.h"
+
+#include <memory>
+
+#include "base/containers/span.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/cert/pem.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+// Parses a SignatureAlgorithm given an empty DER input.
+TEST(SignatureAlgorithmTest, ParseDerEmpty) {
+ CertErrors errors;
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(), &errors));
+ // TODO(crbug.com/634443): Test the errors.
+ // EXPECT_FALSE(errors.empty());
+}
+
+// Parses a SignatureAlgorithm given invalid DER input.
+TEST(SignatureAlgorithmTest, ParseDerBogus) {
+ const uint8_t kData[] = {0x00};
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a SignatureAlgorithm with an unsupported algorithm OID.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 66 (bogus)
+TEST(SignatureAlgorithmTest, ParseDerRsaPssUnsupportedAlgorithmOid) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x03, // SEQUENCE (3 bytes)
+ 0x06, 0x01, // OBJECT IDENTIFIER (1 bytes)
+ 0x42,
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a sha1WithRSAEncryption which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerSha1WithRSAEncryptionNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Sha1);
+}
+
+// Parses a sha1WithRSAEncryption which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+TEST(SignatureAlgorithmTest, ParseDerSha1WithRSAEncryptionNoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0B, // SEQUENCE (11 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Sha1);
+}
+
+// Parses a sha1WithRSAEncryption which contains an unexpected parameters
+// field. Instead of being NULL it is an integer.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+// INTEGER 0
+TEST(SignatureAlgorithmTest, ParseDerSha1WithRSAEncryptionNonNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0E, // SEQUENCE (14 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
+ 0x02, 0x01, 0x00, // INTEGER (1 byte)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a sha1WithRSASignature which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.3.14.3.2.29
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerSha1WithRSASignatureNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x09, // SEQUENCE (9 bytes)
+ 0x06, 0x05, // OBJECT IDENTIFIER (5 bytes)
+ 0x2b, 0x0e, 0x03, 0x02, 0x1d,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Sha1);
+}
+
+// Parses a sha1WithRSASignature which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.3.14.3.2.29
+TEST(SignatureAlgorithmTest, ParseDerSha1WithRSASignatureNoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x07, // SEQUENCE (7 bytes)
+ 0x06, 0x05, // OBJECT IDENTIFIER (5 bytes)
+ 0x2b, 0x0e, 0x03, 0x02, 0x1d,
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Sha1);
+}
+
+// Parses a sha1WithRSAEncryption which contains values after the sequence.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+// NULL
+// INTEGER 0
+TEST(SignatureAlgorithmTest, ParseDerSha1WithRsaEncryptionDataAfterSequence) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
+ 0x05, 0x00, // NULL (0 bytes)
+ 0x02, 0x01, 0x00, // INTEGER (1 byte)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a sha1WithRSAEncryption which contains a bad NULL parameters field.
+// Normally NULL is encoded as {0x05, 0x00} (tag for NULL and length of 0). Here
+// NULL is encoded as having a length of 1 instead, followed by data 0x09.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerSha1WithRSAEncryptionBadNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0E, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
+ 0x05, 0x01, 0x09, // NULL (1 byte)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a sha1WithRSAEncryption which contains a NULL parameters field,
+// followed by an integer.
+//
+// SEQUENCE (3 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+// NULL
+// INTEGER 0
+TEST(SignatureAlgorithmTest,
+ ParseDerSha1WithRSAEncryptionNullParamsThenInteger) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x10, // SEQUENCE (16 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
+ 0x05, 0x00, // NULL (0 bytes)
+ 0x02, 0x01, 0x00, // INTEGER (1 byte)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a SignatureAlgorithm given DER which does not encode a sequence.
+//
+// INTEGER 0
+TEST(SignatureAlgorithmTest, ParseDerNotASequence) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x02, 0x01, 0x00, // INTEGER (1 byte)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a sha256WithRSAEncryption which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.11
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerSha256WithRSAEncryptionNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Sha256);
+}
+
+// Parses a sha256WithRSAEncryption which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.11
+TEST(SignatureAlgorithmTest, ParseDerSha256WithRSAEncryptionNoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0B, // SEQUENCE (11 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Sha256);
+}
+
+// Parses a sha384WithRSAEncryption which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.12
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerSha384WithRSAEncryptionNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Sha384);
+}
+
+// Parses a sha384WithRSAEncryption which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.12
+TEST(SignatureAlgorithmTest, ParseDerSha384WithRSAEncryptionNoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0B, // SEQUENCE (11 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c,
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Sha384);
+}
+
+// Parses a sha512WithRSAEncryption which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.13
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerSha512WithRSAEncryptionNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Sha512);
+}
+
+// Parses a sha512WithRSAEncryption which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.13
+TEST(SignatureAlgorithmTest, ParseDerSha512WithRSAEncryptionNoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0B, // SEQUENCE (11 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d,
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Sha512);
+}
+
+// Parses a sha224WithRSAEncryption which contains a NULL parameters field.
+// This fails because the parsing code does not enumerate this OID (even though
+// it is in fact valid).
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.14
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerSha224WithRSAEncryptionNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0e,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a ecdsa-with-SHA1 which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.2.840.10045.4.1
+TEST(SignatureAlgorithmTest, ParseDerEcdsaWithSHA1NoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x09, // SEQUENCE (9 bytes)
+ 0x06, 0x07, // OBJECT IDENTIFIER (7 bytes)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01,
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kEcdsaSha1);
+}
+
+// Parses a ecdsa-with-SHA1 which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.10045.4.1
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerEcdsaWithSHA1NullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0B, // SEQUENCE (11 bytes)
+ 0x06, 0x07, // OBJECT IDENTIFIER (7 bytes)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a ecdsa-with-SHA256 which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.2.840.10045.4.3.2
+TEST(SignatureAlgorithmTest, ParseDerEcdsaWithSHA256NoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0A, // SEQUENCE (10 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kEcdsaSha256);
+}
+
+// Parses a ecdsa-with-SHA256 which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.10045.4.3.2
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerEcdsaWithSHA256NullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0C, // SEQUENCE (12 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a ecdsa-with-SHA384 which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.2.840.10045.4.3.3
+TEST(SignatureAlgorithmTest, ParseDerEcdsaWithSHA384NoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0A, // SEQUENCE (10 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03,
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kEcdsaSha384);
+}
+
+// Parses a ecdsa-with-SHA384 which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.10045.4.3.3
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerEcdsaWithSHA384NullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0C, // SEQUENCE (12 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a ecdsa-with-SHA512 which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.2.840.10045.4.3.4
+TEST(SignatureAlgorithmTest, ParseDerEcdsaWithSHA512NoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0A, // SEQUENCE (10 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04,
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kEcdsaSha512);
+}
+
+// Parses a ecdsa-with-SHA512 which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.10045.4.3.4
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerEcdsaWithSHA512NullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0C, // SEQUENCE (12 bytes)
+ 0x06, 0x08, // OBJECT IDENTIFIER (8 bytes)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that uses SHA256 and a salt length of 32.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (4 elem)
+// [0] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1
+// NULL
+// [1] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.8
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1
+// NULL
+// [2] (1 elem)
+// INTEGER 32
+TEST(SignatureAlgorithmTest, ParseDerRsaPss) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x41, // SEQUENCE (65 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x34, // SEQUENCE (52 bytes)
+ 0xA0, 0x0F, // [0] (15 bytes)
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, // NULL (0 bytes)
+ 0xA1, 0x1C, // [1] (28 bytes)
+ 0x30, 0x1A, // SEQUENCE (26 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08,
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, // NULL (0 bytes)
+ 0xA2, 0x03, // [2] (3 bytes)
+ 0x02, 0x01, // INTEGER (1 byte)
+ 0x20,
+
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPssSha256);
+}
+
+// Parses a rsaPss algorithm that has an empty parameters. This encodes the
+// default, SHA-1, which we do not support.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (0 elem)
+TEST(SignatureAlgorithmTest, ParseDerRsaPssEmptyParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x00, // SEQUENCE (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that has NULL parameters. This fails.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerRsaPssNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that has no parameters. This fails.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+TEST(SignatureAlgorithmTest, ParseDerRsaPssNoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0B, // SEQUENCE (11 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that has data after the parameters sequence.
+//
+// SEQUENCE (3 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (0 elem)
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerRsaPssDataAfterParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0F, // SEQUENCE (15 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x00, // SEQUENCE (0 bytes)
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that has unrecognized data (NULL) within the
+// parameters sequence.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (2 elem)
+// [2] (1 elem)
+// INTEGER 23
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerRsaPssNullInsideParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x14, // SEQUENCE (62 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x07, // SEQUENCE (5 bytes)
+ 0xA2, 0x03, // [2] (3 bytes)
+ 0x02, 0x01, // INTEGER (1 byte)
+ 0x17,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that has an unsupported trailer value (2). Only
+// trailer values of 1 are allowed by RFC 4055.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (1 elem)
+// [3] (1 elem)
+// INTEGER 2
+TEST(SignatureAlgorithmTest, ParseDerRsaPssUnsupportedTrailer) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x12, // SEQUENCE (18 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x05, // SEQUENCE (5 bytes)
+ 0xA3, 0x03, // [3] (3 bytes)
+ 0x02, 0x01, // INTEGER (1 byte)
+ 0x02,
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that has extra data appearing after the trailer in
+// the [3] section.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (1 elem)
+// [3] (2 elem)
+// INTEGER 1
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerRsaPssBadTrailer) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x14, // SEQUENCE (20 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x07, // SEQUENCE (7 bytes)
+ 0xA3, 0x05, // [3] (5 bytes)
+ 0x02, 0x01, // INTEGER (1 byte)
+ 0x01,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that uses SHA384 for the hash, and leaves the rest
+// as defaults, specifying a SHA-1 MGF-1 hash. This fails because we require
+// the hashes match.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (1 elem)
+// [0] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.2
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerRsaPssNonDefaultHash) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x1E, // SEQUENCE (30 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x11, // SEQUENCE (17 bytes)
+ 0xA0, 0x0F, // [0] (15 bytes)
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that uses an invalid hash algorithm (twiddled the
+// bytes for the SHA-384 OID a bit).
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (1 elem)
+// [0] (1 elem)
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 2.16.840.2.103.19.4.2.2
+TEST(SignatureAlgorithmTest, ParseDerRsaPssUnsupportedHashOid) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x1C, // SEQUENCE (28 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x0F, // SEQUENCE (15 bytes)
+ 0xA0, 0x0D, // [0] (13 bytes)
+ 0x30, 0x0B, // SEQUENCE (11 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x02, 0x67, 0x13, 0x04, 0x02, 0x02,
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that uses SHA512 MGF1 for the mask gen, and
+// defaults (SHA-1) for the rest. This fails because we require the hashes
+// match.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (1 elem)
+// [1] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.8
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.3
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerRsaPssNonDefaultMaskGen) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x2B, // SEQUENCE (43 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x1E, // SEQUENCE (30 bytes)
+ 0xA1, 0x1C, // [1] (28 bytes)
+ 0x30, 0x1A, // SEQUENCE (26 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08,
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that uses a mask gen with an unrecognized OID
+// (twiddled some of the bits).
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (1 elem)
+// [1] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113618.1.2.8
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.3
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerRsaPssUnsupportedMaskGen) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x2B, // SEQUENCE (43 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x1E, // SEQUENCE (30 bytes)
+ 0xA1, 0x1C, // [1] (28 bytes)
+ 0x30, 0x1A, // SEQUENCE (26 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x52, 0x01, 0x02, 0x08,
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that uses SHA256 for the hash, and SHA512 for the
+// MGF1. This fails because we require the hashes match.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (2 elem)
+// [0] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1
+// NULL
+// [1] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.8
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.3
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerRsaPssNonDefaultHashAndMaskGen) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x3C, // SEQUENCE (60 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x2F, // SEQUENCE (47 bytes)
+ 0xA0, 0x0F, // [0] (15 bytes)
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, // NULL (0 bytes)
+ 0xA1, 0x1C, // [1] (28 bytes)
+ 0x30, 0x1A, // SEQUENCE (26 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08,
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that uses SHA256 for the hash, and SHA256 for the
+// MGF1, and a salt length of 10. This fails because we require a standard salt
+// length.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (3 elem)
+// [0] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1
+// NULL
+// [1] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.8
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1
+// NULL
+// [2] (1 elem)
+// INTEGER 10
+TEST(SignatureAlgorithmTest, ParseDerRsaPssNonDefaultHashAndMaskGenAndSalt) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x41, // SEQUENCE (65 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x34, // SEQUENCE (52 bytes)
+ 0xA0, 0x0F, // [0] (15 bytes)
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, // NULL (0 bytes)
+ 0xA1, 0x1C, // [1] (28 bytes)
+ 0x30, 0x1A, // SEQUENCE (26 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08,
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, // NULL (0 bytes)
+ 0xA2, 0x03, // [2] (3 bytes)
+ 0x02, 0x01, // INTEGER (1 byte)
+ 0x0A,
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that specifies default hash (SHA1).
+// It is invalid to specify the default.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (1 elem)
+// [0] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.3.14.3.2.26
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerRsaPssSpecifiedDefaultHash) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x1A, // SEQUENCE (26 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0xA0, 0x0B, // [0] (11 bytes)
+ 0x30, 0x09, // SEQUENCE (9 bytes)
+ 0x06, 0x05, // OBJECT IDENTIFIER (5 bytes)
+ 0x2B, 0x0E, 0x03, 0x02, 0x1A,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that specifies default mask gen algorithm (SHA1).
+// It is invalid to specify the default.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (1 elem)
+// [1] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.8
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.3.14.3.2.26
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerRsaPssSpecifiedDefaultMaskGen) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x27, // SEQUENCE (39 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x1A, // SEQUENCE (26 bytes)
+ 0xA1, 0x18, // [1] (24 bytes)
+ 0x30, 0x16, // SEQUENCE (22 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08,
+ 0x30, 0x09, // SEQUENCE (9 bytes)
+ 0x06, 0x05, // OBJECT IDENTIFIER (5 bytes)
+ 0x2B, 0x0E, 0x03, 0x02, 0x1A,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that specifies default salt length.
+// It is invalid to specify the default.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (1 elem)
+// [2] (1 elem)
+// INTEGER 20
+TEST(SignatureAlgorithmTest, ParseDerRsaPssSpecifiedDefaultSaltLength) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x12, // SEQUENCE (18 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x05, // SEQUENCE (5 bytes)
+ 0xA2, 0x03, // [2] (3 bytes)
+ 0x02, 0x01, // INTEGER (1 byte)
+ 0x14,
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that specifies default trailer field.
+// It is invalid to specify the default.
+TEST(SignatureAlgorithmTest, ParseDerRsaPssSpecifiedDefaultTrailerField) {
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // SEQUENCE {
+ // [0] {
+ // SEQUENCE {
+ // # sha256
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.1 }
+ // NULL {}
+ // }
+ // }
+ // [1] {
+ // SEQUENCE {
+ // # mgf1
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 }
+ // SEQUENCE {
+ // # sha256
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.1 }
+ // NULL {}
+ // }
+ // }
+ // }
+ // [2] {
+ // INTEGER { 32 }
+ // }
+ // [3] {
+ // INTEGER { 1 }
+ // }
+ // }
+ // }
+ const uint8_t kData[] = {
+ 0x30, 0x46, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0a, 0x30, 0x39, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30,
+ 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20, 0xa3, 0x03, 0x02, 0x01, 0x01};
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+// Parses a rsaPss algorithm that specifies multiple default parameter values.
+// It is invalid to specify a default value.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.10
+// SEQUENCE (3 elem)
+// [0] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.3.14.3.2.26
+// NULL
+// [1] (1 elem)
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.8
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.3.14.3.2.26
+// NULL
+// [2] (1 elem)
+// INTEGER 20
+// [3] (1 elem)
+// INTEGER 1
+TEST(SignatureAlgorithmTest, ParseDerRsaPssMultipleDefaultParameterValues) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x3E, // SEQUENCE (62 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A,
+ 0x30, 0x31, // SEQUENCE (49 bytes)
+ 0xA0, 0x0B, // [0] (11 bytes)
+ 0x30, 0x09, // SEQUENCE (9 bytes)
+ 0x06, 0x05, // OBJECT IDENTIFIER (5 bytes)
+ 0x2B, 0x0E, 0x03, 0x02, 0x1A,
+ 0x05, 0x00, // NULL (0 bytes)
+ 0xA1, 0x18, // [1] (24 bytes)
+ 0x30, 0x16, // SEQUENCE (22 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08,
+ 0x30, 0x09, // SEQUENCE (9 bytes)
+ 0x06, 0x05, // OBJECT IDENTIFIER (5 bytes)
+ 0x2B, 0x0E, 0x03, 0x02, 0x1A,
+ 0x05, 0x00, // NULL (0 bytes)
+ 0xA2, 0x03, // [2] (3 bytes)
+ 0x02, 0x01, // INTEGER (1 byte)
+ 0x14,
+ 0xA3, 0x03, // [3] (3 bytes)
+ 0x02, 0x01, // INTEGER (1 byte)
+ 0x01,
+ };
+ // clang-format on
+ EXPECT_FALSE(ParseSignatureAlgorithm(der::Input(kData), nullptr));
+}
+
+TEST(SignatureAlgorithmTest, ParseRsaPss) {
+ // Test data generated with https://github.com/google/der-ascii.
+ struct {
+ std::vector<uint8_t> data;
+ SignatureAlgorithm expected;
+ } kValidTests[] = {
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // SEQUENCE {
+ // [0] {
+ // SEQUENCE {
+ // # sha256
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.1 }
+ // NULL {}
+ // }
+ // }
+ // [1] {
+ // SEQUENCE {
+ // # mgf1
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 }
+ // SEQUENCE {
+ // # sha256
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.1 }
+ // NULL {}
+ // }
+ // }
+ // }
+ // [2] {
+ // INTEGER { 32 }
+ // }
+ // }
+ // }
+ {{0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30,
+ 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20},
+ SignatureAlgorithm::kRsaPssSha256},
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // SEQUENCE {
+ // [0] {
+ // SEQUENCE {
+ // # sha384
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.2 }
+ // NULL {}
+ // }
+ // }
+ // [1] {
+ // SEQUENCE {
+ // # mgf1
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 }
+ // SEQUENCE {
+ // # sha384
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.2 }
+ // NULL {}
+ // }
+ // }
+ // }
+ // [2] {
+ // INTEGER { 48 }
+ // }
+ // }
+ // }
+ {{0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30,
+ 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
+ 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x30},
+ SignatureAlgorithm::kRsaPssSha384},
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // SEQUENCE {
+ // [0] {
+ // SEQUENCE {
+ // # sha512
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.3 }
+ // NULL {}
+ // }
+ // }
+ // [1] {
+ // SEQUENCE {
+ // # mgf1
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 }
+ // SEQUENCE {
+ // # sha512
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.3 }
+ // NULL {}
+ // }
+ // }
+ // }
+ // [2] {
+ // INTEGER { 64 }
+ // }
+ // }
+ // }
+ {{0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30,
+ 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+ 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x40},
+ SignatureAlgorithm::kRsaPssSha512},
+
+ // The same inputs as above, but the NULLs in the digest algorithms are
+ // omitted.
+ {{0x30, 0x3d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0a, 0x30, 0x30, 0xa0, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0xa1, 0x1a, 0x30,
+ 0x18, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x08, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x01, 0xa2, 0x03, 0x02, 0x01, 0x20},
+ SignatureAlgorithm::kRsaPssSha256},
+ {{0x30, 0x3d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0a, 0x30, 0x30, 0xa0, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0xa1, 0x1a, 0x30,
+ 0x18, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x08, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x02, 0xa2, 0x03, 0x02, 0x01, 0x30},
+ SignatureAlgorithm::kRsaPssSha384},
+ {{0x30, 0x3d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0a, 0x30, 0x30, 0xa0, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0xa1, 0x1a, 0x30,
+ 0x18, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x08, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x03, 0xa2, 0x03, 0x02, 0x01, 0x40},
+ SignatureAlgorithm::kRsaPssSha512}};
+ for (const auto& t : kValidTests) {
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(t.data.data(), t.data.size()),
+ nullptr),
+ t.expected);
+ }
+
+ struct {
+ std::vector<uint8_t> data;
+ } kInvalidTests[] = {
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // SEQUENCE {
+ // [0] {
+ // SEQUENCE {
+ // # sha256
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.1 }
+ // NULL {}
+ // }
+ // }
+ // [1] {
+ // SEQUENCE {
+ // # mgf1
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 }
+ // SEQUENCE {
+ // # sha384
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.2 }
+ // NULL {}
+ // }
+ // }
+ // }
+ // }
+ // }
+ {{0x30, 0x3c, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0a, 0x30, 0x2f, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1,
+ 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+ 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00}},
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // SEQUENCE {
+ // [0] {
+ // SEQUENCE {
+ // # md5
+ // OBJECT_IDENTIFIER { 1.2.840.113549.2.5 }
+ // NULL {}
+ // }
+ // }
+ // [1] {
+ // SEQUENCE {
+ // # mgf1
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 }
+ // SEQUENCE {
+ // # md5
+ // OBJECT_IDENTIFIER { 1.2.840.113549.2.5 }
+ // NULL {}
+ // }
+ // }
+ // }
+ // [2] {
+ // INTEGER { 16 }
+ // }
+ // }
+ // }
+ {{0x30, 0x3f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0a, 0x30, 0x32, 0xa0, 0x0e, 0x30, 0x0c, 0x06, 0x08, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0xa1, 0x1b,
+ 0x30, 0x19, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x08, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x02, 0x05, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x10}},
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // # SHA-1 with salt length 20 is the default.
+ // SEQUENCE {}
+ // }
+ {{0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0a, 0x30, 0x00}},
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // SEQUENCE {
+ // [2] {
+ // INTEGER { 21 }
+ // }
+ // }
+ // }
+ {{0x30, 0x12, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0a, 0x30, 0x05, 0xa2, 0x03, 0x02, 0x01, 0x15}},
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // SEQUENCE {
+ // [0] {
+ // SEQUENCE {
+ // # sha256
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.1 }
+ // NULL {}
+ // }
+ // }
+ // [1] {
+ // SEQUENCE {
+ // # mgf1
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 }
+ // SEQUENCE {
+ // # sha256
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.1 }
+ // NULL {}
+ // }
+ // }
+ // }
+ // [2] {
+ // INTEGER { 33 }
+ // }
+ // }
+ // }
+ {{0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30,
+ 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x21}},
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // SEQUENCE {
+ // [0] {
+ // SEQUENCE {
+ // # sha384
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.2 }
+ // NULL {}
+ // }
+ // }
+ // [1] {
+ // SEQUENCE {
+ // # mgf1
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 }
+ // SEQUENCE {
+ // # sha384
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.2 }
+ // NULL {}
+ // }
+ // }
+ // }
+ // [2] {
+ // INTEGER { 49 }
+ // }
+ // }
+ // }
+ {{0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30,
+ 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
+ 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x31}},
+
+ // SEQUENCE {
+ // # rsassa-pss
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 }
+ // SEQUENCE {
+ // [0] {
+ // SEQUENCE {
+ // # sha512
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.3 }
+ // NULL {}
+ // }
+ // }
+ // [1] {
+ // SEQUENCE {
+ // # mgf1
+ // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 }
+ // SEQUENCE {
+ // # sha512
+ // OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.3 }
+ // NULL {}
+ // }
+ // }
+ // }
+ // [2] {
+ // INTEGER { 65 }
+ // }
+ // }
+ // }
+ {{0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30,
+ 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+ 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x41}},
+ };
+ for (const auto& t : kInvalidTests) {
+ EXPECT_FALSE(ParseSignatureAlgorithm(
+ der::Input(t.data.data(), t.data.size()), nullptr));
+ }
+}
+
+// Parses a md5WithRSAEncryption which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.4
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerMd5WithRsaEncryptionNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Md5);
+}
+
+// Parses a md4WithRSAEncryption which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.3
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerMd4WithRsaEncryptionNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x03,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Md4);
+}
+
+// Parses a md2WithRSAEncryption which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.113549.1.1.2
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerMd2WithRsaEncryptionNullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0D, // SEQUENCE (13 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kRsaPkcs1Md2);
+}
+
+// Parses a dsaWithSha1 which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 1.2.840.10040.4.3
+TEST(SignatureAlgorithmTest, ParseDerDsaWithSha1NoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x09, // SEQUENCE (9 bytes)
+ 0x06, 0x07, // OBJECT IDENTIFIER (7 bytes)
+ 0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x03,
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kDsaSha1);
+}
+
+// Parses a dsaWithSha1 which contains a NULL parameters field.
+//
+// SEQUENCE (2 elem)
+// OBJECT IDENTIFIER 1.2.840.10040.4.3
+// NULL
+TEST(SignatureAlgorithmTest, ParseDerDsaWithSha1NullParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0B, // SEQUENCE (9 bytes)
+ 0x06, 0x07, // OBJECT IDENTIFIER (7 bytes)
+ 0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x03,
+ 0x05, 0x00, // NULL (0 bytes)
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kDsaSha1);
+}
+
+// Parses a dsaWithSha256 which contains no parameters field.
+//
+// SEQUENCE (1 elem)
+// OBJECT IDENTIFIER 2.16.840.1.101.3.4.3.2
+TEST(SignatureAlgorithmTest, ParseDerDsaWithSha256NoParams) {
+ // clang-format off
+ const uint8_t kData[] = {
+ 0x30, 0x0B, // SEQUENCE (11 bytes)
+ 0x06, 0x09, // OBJECT IDENTIFIER (9 bytes)
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x02
+ };
+ // clang-format on
+ EXPECT_EQ(ParseSignatureAlgorithm(der::Input(kData), nullptr),
+ SignatureAlgorithm::kDsaSha256);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/pki/simple_path_builder_delegate.cc b/chromium/net/cert/pki/simple_path_builder_delegate.cc
new file mode 100644
index 00000000000..aa961254d3a
--- /dev/null
+++ b/chromium/net/cert/pki/simple_path_builder_delegate.cc
@@ -0,0 +1,126 @@
+// Copyright 2017 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 "net/cert/pki/simple_path_builder_delegate.h"
+
+#include "net/cert/pki/cert_error_params.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/signature_algorithm.h"
+#include "net/cert/pki/verify_signed_data.h"
+#include "third_party/boringssl/src/include/openssl/bn.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "third_party/boringssl/src/include/openssl/digest.h"
+#include "third_party/boringssl/src/include/openssl/ec.h"
+#include "third_party/boringssl/src/include/openssl/ec_key.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "third_party/boringssl/src/include/openssl/nid.h"
+#include "third_party/boringssl/src/include/openssl/rsa.h"
+
+namespace net {
+
+DEFINE_CERT_ERROR_ID(SimplePathBuilderDelegate::kRsaModulusTooSmall,
+ "RSA modulus too small");
+
+namespace {
+
+DEFINE_CERT_ERROR_ID(kUnacceptableCurveForEcdsa,
+ "Only P-256, P-384, P-521 are supported for ECDSA");
+
+bool IsAcceptableCurveForEcdsa(int curve_nid) {
+ switch (curve_nid) {
+ case NID_X9_62_prime256v1:
+ case NID_secp384r1:
+ case NID_secp521r1:
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+SimplePathBuilderDelegate::SimplePathBuilderDelegate(
+ size_t min_rsa_modulus_length_bits,
+ DigestPolicy digest_policy)
+ : min_rsa_modulus_length_bits_(min_rsa_modulus_length_bits),
+ digest_policy_(digest_policy) {}
+
+void SimplePathBuilderDelegate::CheckPathAfterVerification(
+ const CertPathBuilder& path_builder,
+ CertPathBuilderResultPath* path) {
+ // Do nothing - consider all candidate paths valid.
+}
+
+bool SimplePathBuilderDelegate::IsSignatureAlgorithmAcceptable(
+ SignatureAlgorithm algorithm,
+ CertErrors* errors) {
+ switch (algorithm) {
+ case SignatureAlgorithm::kRsaPkcs1Sha1:
+ case SignatureAlgorithm::kEcdsaSha1:
+ return digest_policy_ == DigestPolicy::kWeakAllowSha1;
+
+ case SignatureAlgorithm::kRsaPkcs1Sha256:
+ case SignatureAlgorithm::kRsaPkcs1Sha384:
+ case SignatureAlgorithm::kRsaPkcs1Sha512:
+ case SignatureAlgorithm::kEcdsaSha256:
+ case SignatureAlgorithm::kEcdsaSha384:
+ case SignatureAlgorithm::kEcdsaSha512:
+ case SignatureAlgorithm::kRsaPssSha256:
+ case SignatureAlgorithm::kRsaPssSha384:
+ case SignatureAlgorithm::kRsaPssSha512:
+ return true;
+
+ case SignatureAlgorithm::kRsaPkcs1Md2:
+ case SignatureAlgorithm::kRsaPkcs1Md4:
+ case SignatureAlgorithm::kRsaPkcs1Md5:
+ case SignatureAlgorithm::kDsaSha1:
+ case SignatureAlgorithm::kDsaSha256:
+ // TODO(https://crbug.com/1321688): We do not implement DSA, MD2, MD4, or
+ // MD5 anyway. Remove them from the parser altogether, so code does not
+ // need to handle them.
+ return false;
+ }
+}
+
+bool SimplePathBuilderDelegate::IsPublicKeyAcceptable(EVP_PKEY* public_key,
+ CertErrors* errors) {
+ int pkey_id = EVP_PKEY_id(public_key);
+ if (pkey_id == EVP_PKEY_RSA) {
+ // Extract the modulus length from the key.
+ RSA* rsa = EVP_PKEY_get0_RSA(public_key);
+ if (!rsa)
+ return false;
+ unsigned int modulus_length_bits = RSA_bits(rsa);
+
+ if (modulus_length_bits < min_rsa_modulus_length_bits_) {
+ errors->AddError(
+ kRsaModulusTooSmall,
+ CreateCertErrorParams2SizeT("actual", modulus_length_bits, "minimum",
+ min_rsa_modulus_length_bits_));
+ return false;
+ }
+
+ return true;
+ }
+
+ if (pkey_id == EVP_PKEY_EC) {
+ // Extract the curve name.
+ EC_KEY* ec = EVP_PKEY_get0_EC_KEY(public_key);
+ if (!ec)
+ return false; // Unexpected.
+ int curve_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
+
+ if (!IsAcceptableCurveForEcdsa(curve_nid)) {
+ errors->AddError(kUnacceptableCurveForEcdsa);
+ return false;
+ }
+
+ return true;
+ }
+
+ // Unexpected key type.
+ return false;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/simple_path_builder_delegate.h b/chromium/net/cert/pki/simple_path_builder_delegate.h
new file mode 100644
index 00000000000..db1b368c215
--- /dev/null
+++ b/chromium/net/cert/pki/simple_path_builder_delegate.h
@@ -0,0 +1,65 @@
+// Copyright 2017 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 NET_CERT_PKI_SIMPLE_PATH_BUILDER_DELEGATE_H_
+#define NET_CERT_PKI_SIMPLE_PATH_BUILDER_DELEGATE_H_
+
+#include <stddef.h>
+
+#include "net/base/net_export.h"
+#include "net/cert/pki/path_builder.h"
+#include "net/cert/pki/signature_algorithm.h"
+
+namespace net {
+
+class CertErrors;
+
+// SimplePathBuilderDelegate is an implementation of CertPathBuilderDelegate
+// that uses some default policies:
+//
+// * RSA public keys must be >= |min_rsa_modulus_length_bits|.
+// * Signature algorithm can be RSA PKCS#1, RSASSA-PSS or ECDSA
+// * Digest algorithm can be SHA256, SHA348 or SHA512.
+// * If the |digest_policy| was set to kAllowSha1, then SHA1 is
+// additionally accepted.
+// * EC named curve can be P-256, P-384, P-521.
+class NET_EXPORT SimplePathBuilderDelegate : public CertPathBuilderDelegate {
+ public:
+ enum class DigestPolicy {
+ // Accepts digests of SHA256, SHA348 or SHA512
+ kStrong,
+
+ // Accepts everything that kStrong does, plus SHA1.
+ kWeakAllowSha1,
+
+ kMaxValue = kWeakAllowSha1
+ };
+
+ // Error emitted when a public key is rejected because it is an RSA key with a
+ // modulus size that is too small.
+ static const CertErrorId kRsaModulusTooSmall;
+
+ SimplePathBuilderDelegate(size_t min_rsa_modulus_length_bits,
+ DigestPolicy digest_policy);
+
+ // Accepts RSA PKCS#1, RSASSA-PSS or ECDA using any of the SHA* digests
+ // (including SHA1).
+ bool IsSignatureAlgorithmAcceptable(SignatureAlgorithm signature_algorithm,
+ CertErrors* errors) override;
+
+ // Requires RSA keys be >= |min_rsa_modulus_length_bits_|.
+ bool IsPublicKeyAcceptable(EVP_PKEY* public_key, CertErrors* errors) override;
+
+ // No-op implementation.
+ void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+ CertPathBuilderResultPath* path) override;
+
+ private:
+ const size_t min_rsa_modulus_length_bits_;
+ const DigestPolicy digest_policy_;
+};
+
+} // namespace net
+
+#endif // NET_CERT_PKI_SIMPLE_PATH_BUILDER_DELEGATE_H_
diff --git a/chromium/net/cert/pki/simple_path_builder_delegate_unittest.cc b/chromium/net/cert/pki/simple_path_builder_delegate_unittest.cc
new file mode 100644
index 00000000000..e9613a1e61f
--- /dev/null
+++ b/chromium/net/cert/pki/simple_path_builder_delegate_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright 2017 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 "net/cert/pki/simple_path_builder_delegate.h"
+
+#include <memory>
+#include <set>
+
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/signature_algorithm.h"
+#include "net/cert/pki/test_helpers.h"
+#include "net/cert/pki/verify_signed_data.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "net/der/parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/nid.h"
+
+namespace net {
+
+namespace {
+
+// Reads the public key and algorithm from the test data at |file_name|.
+void ReadTestCase(const char* file_name,
+ SignatureAlgorithm* signature_algorithm,
+ bssl::UniquePtr<EVP_PKEY>* public_key) {
+ std::string path =
+ std::string("net/data/verify_signed_data_unittest/") + file_name;
+
+ std::string public_key_str;
+ std::string algorithm_str;
+
+ const PemBlockMapping mappings[] = {
+ {"PUBLIC KEY", &public_key_str},
+ {"ALGORITHM", &algorithm_str},
+ };
+
+ ASSERT_TRUE(ReadTestDataFromPemFile(path, mappings));
+
+ CertErrors algorithm_errors;
+ absl::optional<SignatureAlgorithm> sigalg_opt =
+ ParseSignatureAlgorithm(der::Input(&algorithm_str), &algorithm_errors);
+ ASSERT_TRUE(sigalg_opt) << algorithm_errors.ToDebugString();
+ *signature_algorithm = *sigalg_opt;
+
+ ASSERT_TRUE(ParsePublicKey(der::Input(&public_key_str), public_key));
+}
+
+class SimplePathBuilderDelegate1024SuccessTest
+ : public ::testing::TestWithParam<const char*> {};
+
+const char* kSuccess1024Filenames[] = {
+ "rsa-pkcs1-sha1.pem", "rsa-pkcs1-sha256.pem",
+ "rsa2048-pkcs1-sha512.pem", "ecdsa-secp384r1-sha256.pem",
+ "ecdsa-prime256v1-sha512.pem", "rsa-pss-sha256.pem",
+ "ecdsa-secp384r1-sha256.pem", "ecdsa-prime256v1-sha512.pem",
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ SimplePathBuilderDelegate1024SuccessTest,
+ ::testing::ValuesIn(kSuccess1024Filenames));
+
+TEST_P(SimplePathBuilderDelegate1024SuccessTest, IsAcceptableSignatureAndKey) {
+ SignatureAlgorithm signature_algorithm;
+ bssl::UniquePtr<EVP_PKEY> public_key;
+ ASSERT_NO_FATAL_FAILURE(
+ ReadTestCase(GetParam(), &signature_algorithm, &public_key));
+ ASSERT_TRUE(public_key);
+
+ CertErrors errors;
+ SimplePathBuilderDelegate delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+
+ EXPECT_TRUE(
+ delegate.IsSignatureAlgorithmAcceptable(signature_algorithm, &errors));
+
+ EXPECT_TRUE(delegate.IsPublicKeyAcceptable(public_key.get(), &errors));
+}
+
+class SimplePathBuilderDelegate2048FailTest
+ : public ::testing::TestWithParam<const char*> {};
+
+const char* kFail2048Filenames[] = {"rsa-pkcs1-sha1.pem",
+ "rsa-pkcs1-sha256.pem"};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ SimplePathBuilderDelegate2048FailTest,
+ ::testing::ValuesIn(kFail2048Filenames));
+
+TEST_P(SimplePathBuilderDelegate2048FailTest, RsaKeySmallerThan2048) {
+ SignatureAlgorithm signature_algorithm;
+ bssl::UniquePtr<EVP_PKEY> public_key;
+ ASSERT_NO_FATAL_FAILURE(
+ ReadTestCase(GetParam(), &signature_algorithm, &public_key));
+ ASSERT_TRUE(public_key);
+
+ CertErrors errors;
+ SimplePathBuilderDelegate delegate(
+ 2048, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+
+ EXPECT_TRUE(
+ delegate.IsSignatureAlgorithmAcceptable(signature_algorithm, &errors));
+
+ EXPECT_FALSE(delegate.IsPublicKeyAcceptable(public_key.get(), &errors));
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/pki/test_helpers.cc b/chromium/net/cert/pki/test_helpers.cc
new file mode 100644
index 00000000000..914b6a3921a
--- /dev/null
+++ b/chromium/net/cert/pki/test_helpers.cc
@@ -0,0 +1,377 @@
+// Copyright 2015 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 "net/cert/pki/test_helpers.h"
+
+#include "base/base64.h"
+#include "base/base_paths.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "net/cert/pem.h"
+#include "net/cert/pki/cert_error_params.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/der/parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+
+#include <sstream>
+
+namespace net {
+
+namespace {
+
+bool GetValue(base::StringPiece prefix,
+ base::StringPiece line,
+ std::string* value,
+ bool* has_value) {
+ if (!base::StartsWith(line, prefix))
+ return false;
+
+ if (*has_value) {
+ ADD_FAILURE() << "Duplicated " << prefix;
+ return false;
+ }
+
+ *has_value = true;
+ *value = std::string(line.substr(prefix.size()));
+ return true;
+}
+
+} // namespace
+
+namespace der {
+
+void PrintTo(const Input& data, ::std::ostream* os) {
+ std::string b64;
+ base::Base64Encode(
+ base::StringPiece(reinterpret_cast<const char*>(data.UnsafeData()),
+ data.Length()),
+ &b64);
+
+ *os << "[" << b64 << "]";
+}
+
+} // namespace der
+
+der::Input SequenceValueFromString(const std::string* s) {
+ der::Parser parser((der::Input(s)));
+ der::Input data;
+ if (!parser.ReadTag(der::kSequence, &data)) {
+ ADD_FAILURE();
+ return der::Input();
+ }
+ if (parser.HasMore()) {
+ ADD_FAILURE();
+ return der::Input();
+ }
+ return data;
+}
+
+::testing::AssertionResult ReadTestDataFromPemFile(
+ const std::string& file_path_ascii,
+ const PemBlockMapping* mappings,
+ size_t mappings_length) {
+ // Compute the full path, relative to the src/ directory.
+ base::FilePath src_root;
+ base::PathService::Get(base::DIR_SOURCE_ROOT, &src_root);
+ base::FilePath filepath = src_root.AppendASCII(file_path_ascii);
+
+ // Read the full contents of the PEM file.
+ std::string file_data;
+ if (!base::ReadFileToString(filepath, &file_data)) {
+ return ::testing::AssertionFailure()
+ << "Couldn't read file: " << filepath.value();
+ }
+
+ // mappings_copy is used to keep track of which mappings have already been
+ // satisfied (by nulling the |value| field). This is used to track when
+ // blocks are mulitply defined.
+ std::vector<PemBlockMapping> mappings_copy(mappings,
+ mappings + mappings_length);
+
+ // Build the |pem_headers| vector needed for PEMTokenzier.
+ std::vector<std::string> pem_headers;
+ for (const auto& mapping : mappings_copy) {
+ pem_headers.push_back(mapping.block_name);
+ }
+
+ PEMTokenizer pem_tokenizer(file_data, pem_headers);
+ while (pem_tokenizer.GetNext()) {
+ for (auto& mapping : mappings_copy) {
+ // Find the mapping for this block type.
+ if (pem_tokenizer.block_type() == mapping.block_name) {
+ if (!mapping.value) {
+ return ::testing::AssertionFailure()
+ << "PEM block defined multiple times: " << mapping.block_name;
+ }
+
+ // Copy the data to the result.
+ mapping.value->assign(pem_tokenizer.data());
+
+ // Mark the mapping as having been satisfied.
+ mapping.value = nullptr;
+ }
+ }
+ }
+
+ // Ensure that all specified blocks were found.
+ for (const auto& mapping : mappings_copy) {
+ if (mapping.value && !mapping.optional) {
+ return ::testing::AssertionFailure()
+ << "PEM block missing: " << mapping.block_name;
+ }
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+VerifyCertChainTest::VerifyCertChainTest()
+ : user_initial_policy_set{der::Input(kAnyPolicyOid)} {}
+VerifyCertChainTest::~VerifyCertChainTest() = default;
+
+bool VerifyCertChainTest::HasHighSeverityErrors() const {
+ // This function assumes that high severity warnings are prefixed with
+ // "ERROR: " and warnings are prefixed with "WARNING: ". This is an
+ // implementation detail of CertError::ToDebugString).
+ //
+ // Do a quick sanity-check to confirm this.
+ CertError error(CertError::SEVERITY_HIGH, "unused", nullptr);
+ EXPECT_EQ("ERROR: unused\n", error.ToDebugString());
+ CertError warning(CertError::SEVERITY_WARNING, "unused", nullptr);
+ EXPECT_EQ("WARNING: unused\n", warning.ToDebugString());
+
+ // Do a simple substring test (not perfect, but good enough for our test
+ // corpus).
+ return expected_errors.find("ERROR: ") != std::string::npos;
+}
+
+bool ReadCertChainFromFile(const std::string& file_path_ascii,
+ ParsedCertificateList* chain) {
+ // Reset all the out parameters to their defaults.
+ *chain = ParsedCertificateList();
+
+ std::string file_data = ReadTestFileToString(file_path_ascii);
+ if (file_data.empty())
+ return false;
+
+ std::vector<std::string> pem_headers = {"CERTIFICATE"};
+
+ PEMTokenizer pem_tokenizer(file_data, pem_headers);
+ while (pem_tokenizer.GetNext()) {
+ const std::string& block_data = pem_tokenizer.data();
+
+ CertErrors errors;
+ if (!ParsedCertificate::CreateAndAddToVector(
+ bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(
+ reinterpret_cast<const uint8_t*>(block_data.data()),
+ block_data.size(), nullptr)),
+ {}, chain, &errors)) {
+ ADD_FAILURE() << errors.ToDebugString();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+scoped_refptr<ParsedCertificate> ReadCertFromFile(
+ const std::string& file_path_ascii) {
+ ParsedCertificateList chain;
+ if (!ReadCertChainFromFile(file_path_ascii, &chain))
+ return nullptr;
+ if (chain.size() != 1)
+ return nullptr;
+ return chain[0];
+}
+
+bool ReadVerifyCertChainTestFromFile(const std::string& file_path_ascii,
+ VerifyCertChainTest* test) {
+ // Reset all the out parameters to their defaults.
+ *test = {};
+
+ std::string file_data = ReadTestFileToString(file_path_ascii);
+ if (file_data.empty())
+ return false;
+
+ bool has_chain = false;
+ bool has_trust = false;
+ bool has_time = false;
+ bool has_errors = false;
+ bool has_key_purpose = false;
+
+ base::StringPiece kExpectedErrors = "expected_errors:";
+
+ std::istringstream stream(file_data);
+ for (std::string line; std::getline(stream, line, '\n');) {
+ size_t start = line.find_first_not_of(" \n\t\r\f\v");
+ if (start == std::string::npos) {
+ continue;
+ }
+ size_t end = line.find_last_not_of(" \n\t\r\f\v");
+ if (end == std::string::npos) {
+ continue;
+ }
+ line = line.substr(start, end + 1);
+ if (line.empty()) {
+ continue;
+ }
+ base::StringPiece line_piece(line);
+
+ std::string value;
+
+ // For details on the file format refer to:
+ // net/data/verify_certificate_chain_unittest/README.
+ if (GetValue("chain: ", line_piece, &value, &has_chain)) {
+ // Interpret the |chain| path as being relative to the .test file.
+ size_t slash = file_path_ascii.rfind('/');
+ if (slash == std::string::npos) {
+ ADD_FAILURE() << "Bad path - expecting slashes";
+ return false;
+ }
+ std::string chain_path = file_path_ascii.substr(0, slash) + "/" + value;
+
+ ReadCertChainFromFile(chain_path, &test->chain);
+ } else if (GetValue("utc_time: ", line_piece, &value, &has_time)) {
+ if (value == "DEFAULT") {
+ value = "211005120000Z";
+ }
+ if (!der::ParseUTCTime(der::Input(&value), &test->time)) {
+ ADD_FAILURE() << "Failed parsing UTC time";
+ return false;
+ }
+ } else if (GetValue("key_purpose: ", line_piece, &value,
+ &has_key_purpose)) {
+ if (value == "ANY_EKU") {
+ test->key_purpose = KeyPurpose::ANY_EKU;
+ } else if (value == "SERVER_AUTH") {
+ test->key_purpose = KeyPurpose::SERVER_AUTH;
+ } else if (value == "CLIENT_AUTH") {
+ test->key_purpose = KeyPurpose::CLIENT_AUTH;
+ } else {
+ ADD_FAILURE() << "Unrecognized key_purpose: " << value;
+ return false;
+ }
+ } else if (GetValue("last_cert_trust: ", line_piece, &value, &has_trust)) {
+ if (value == "TRUSTED_ANCHOR") {
+ test->last_cert_trust = CertificateTrust::ForTrustAnchor();
+ } else if (value == "TRUSTED_ANCHOR_WITH_EXPIRATION") {
+ test->last_cert_trust =
+ CertificateTrust::ForTrustAnchorEnforcingExpiration();
+ } else if (value == "TRUSTED_ANCHOR_WITH_CONSTRAINTS") {
+ test->last_cert_trust =
+ CertificateTrust::ForTrustAnchorEnforcingConstraints();
+ } else if (value == "DISTRUSTED") {
+ test->last_cert_trust = CertificateTrust::ForDistrusted();
+ } else if (value == "UNSPECIFIED") {
+ test->last_cert_trust = CertificateTrust::ForUnspecified();
+ } else {
+ ADD_FAILURE() << "Unrecognized last_cert_trust: " << value;
+ return false;
+ }
+ } else if (base::StartsWith(line_piece, "#")) {
+ // Skip comments.
+ continue;
+ } else if (line_piece == kExpectedErrors) {
+ has_errors = true;
+ // The errors start on the next line, and extend until the end of the
+ // file.
+ std::string prefix =
+ std::string("\n") + std::string(kExpectedErrors) + std::string("\n");
+ size_t errors_start = file_data.find(prefix);
+ if (errors_start == std::string::npos) {
+ ADD_FAILURE() << "expected_errors not found";
+ return false;
+ }
+ test->expected_errors = file_data.substr(errors_start + prefix.size());
+ break;
+ } else {
+ ADD_FAILURE() << "Unknown line: " << line_piece;
+ return false;
+ }
+ }
+
+ if (!has_chain) {
+ ADD_FAILURE() << "Missing chain: ";
+ return false;
+ }
+
+ if (!has_trust) {
+ ADD_FAILURE() << "Missing last_cert_trust: ";
+ return false;
+ }
+
+ if (!has_time) {
+ ADD_FAILURE() << "Missing time: ";
+ return false;
+ }
+
+ if (!has_key_purpose) {
+ ADD_FAILURE() << "Missing key_purpose: ";
+ return false;
+ }
+
+ if (!has_errors) {
+ ADD_FAILURE() << "Missing errors:";
+ return false;
+ }
+
+ return true;
+}
+
+std::string ReadTestFileToString(const std::string& file_path_ascii) {
+ // Compute the full path, relative to the src/ directory.
+ base::FilePath src_root;
+ base::PathService::Get(base::DIR_SOURCE_ROOT, &src_root);
+ base::FilePath filepath = src_root.AppendASCII(file_path_ascii);
+
+ // Read the full contents of the file.
+ std::string file_data;
+ if (!base::ReadFileToString(filepath, &file_data)) {
+ ADD_FAILURE() << "Couldn't read file: " << filepath.value();
+ return std::string();
+ }
+
+ return file_data;
+}
+
+void VerifyCertPathErrors(const std::string& expected_errors_str,
+ const CertPathErrors& actual_errors,
+ const ParsedCertificateList& chain,
+ const std::string& errors_file_path) {
+ std::string actual_errors_str = actual_errors.ToDebugString(chain);
+
+ if (expected_errors_str != actual_errors_str) {
+ ADD_FAILURE() << "Cert path errors don't match expectations ("
+ << errors_file_path << ")\n\n"
+ << "EXPECTED:\n\n"
+ << expected_errors_str << "\n"
+ << "ACTUAL:\n\n"
+ << actual_errors_str << "\n"
+ << "===> Use "
+ "net/data/verify_certificate_chain_unittest/"
+ "rebase-errors.py to rebaseline.\n";
+ }
+}
+
+void VerifyCertErrors(const std::string& expected_errors_str,
+ const CertErrors& actual_errors,
+ const std::string& errors_file_path) {
+ std::string actual_errors_str = actual_errors.ToDebugString();
+
+ if (expected_errors_str != actual_errors_str) {
+ ADD_FAILURE() << "Cert errors don't match expectations ("
+ << errors_file_path << ")\n\n"
+ << "EXPECTED:\n\n"
+ << expected_errors_str << "\n"
+ << "ACTUAL:\n\n"
+ << actual_errors_str << "\n"
+ << "===> Use "
+ "net/data/parse_certificate_unittest/"
+ "rebase-errors.py to rebaseline.\n";
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/test_helpers.h b/chromium/net/cert/pki/test_helpers.h
new file mode 100644
index 00000000000..0fe301af316
--- /dev/null
+++ b/chromium/net/cert/pki/test_helpers.h
@@ -0,0 +1,155 @@
+// Copyright 2015 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 NET_CERT_PKI_TEST_HELPERS_H_
+#define NET_CERT_PKI_TEST_HELPERS_H_
+
+#include <stddef.h>
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/cert/pki/trust_store.h"
+#include "net/cert/pki/verify_certificate_chain.h"
+#include "net/der/input.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace der {
+
+// This function is used by GTest to support EXPECT_EQ() for der::Input.
+void PrintTo(const Input& data, ::std::ostream* os);
+
+} // namespace der
+
+// Parses |s| as a DER SEQUENCE TLV and returns a der::Input corresponding to
+// the value portion. On error returns an empty der::Input and adds a gtest
+// failure.
+//
+// The returned der::Input() is only valid so long as the input string is alive
+// and is not mutated.
+der::Input SequenceValueFromString(const std::string* s);
+
+// Helper structure that maps a PEM block header (for instance "CERTIFICATE") to
+// the destination where the value for that block should be written.
+struct PemBlockMapping {
+ // The name of the PEM header. Example "CERTIFICATE".
+ const char* block_name;
+
+ // The destination where the read value should be written to.
+ raw_ptr<std::string> value;
+
+ // True to indicate that the block is not required to be present. If the
+ // block is optional and is not present, then |value| will not be modified.
+ bool optional;
+};
+
+// ReadTestDataFromPemFile() is a helper function that reads a PEM test file
+// rooted in the "src/" directory.
+//
+// * file_path_ascii:
+// The path to the PEM file, relative to src. For instance
+// "net/data/verify_signed_data_unittest/foopy.pem"
+//
+// * mappings:
+// An array of length |mappings_length| which maps the expected PEM
+// headers to the destination to write its data.
+//
+// The function ensures that each of the chosen mappings is satisfied exactly
+// once. In other words, the header must be present (unless marked as
+// optional=true), have valid data, and appear no more than once.
+::testing::AssertionResult ReadTestDataFromPemFile(
+ const std::string& file_path_ascii,
+ const PemBlockMapping* mappings,
+ size_t mappings_length);
+
+// This is the same as the variant above, however it uses template magic so an
+// mappings array can be passed in directly (and the correct length is
+// inferred).
+template <size_t N>
+::testing::AssertionResult ReadTestDataFromPemFile(
+ const std::string& file_path_ascii,
+ const PemBlockMapping (&mappings)[N]) {
+ return ReadTestDataFromPemFile(file_path_ascii, mappings, N);
+}
+
+// Test cases are comprised of all the parameters to certificate
+// verification, as well as the expected outputs.
+struct VerifyCertChainTest {
+ VerifyCertChainTest();
+ ~VerifyCertChainTest();
+
+ // The chain of certificates (with the zero-th being the target).
+ ParsedCertificateList chain;
+
+ // Details on the trustedness of the last certificate.
+ CertificateTrust last_cert_trust;
+
+ // The time to use when verifying the chain.
+ der::GeneralizedTime time;
+
+ // The Key Purpose to use when verifying the chain.
+ KeyPurpose key_purpose = KeyPurpose::ANY_EKU;
+
+ InitialExplicitPolicy initial_explicit_policy = InitialExplicitPolicy::kFalse;
+
+ std::set<der::Input> user_initial_policy_set;
+
+ InitialPolicyMappingInhibit initial_policy_mapping_inhibit =
+ InitialPolicyMappingInhibit::kFalse;
+
+ InitialAnyPolicyInhibit initial_any_policy_inhibit =
+ InitialAnyPolicyInhibit::kFalse;
+
+ // The expected errors/warnings from verification (as a string).
+ std::string expected_errors;
+
+ // Returns true if |expected_errors| contains any high severity errors (a
+ // non-empty expected_errors doesn't necessarily mean verification is
+ // expected to fail, as it may have contained warnings).
+ bool HasHighSeverityErrors() const;
+};
+
+// Reads a test case from |file_path_ascii| (which is relative to //src).
+// Generally |file_path_ascii| will start with:
+// net/data/verify_certificate_chain_unittest/
+bool ReadVerifyCertChainTestFromFile(const std::string& file_path_ascii,
+ VerifyCertChainTest* test);
+
+// Reads a certificate chain from |file_path_ascii|
+bool ReadCertChainFromFile(const std::string& file_path_ascii,
+ ParsedCertificateList* chain);
+
+// Reads a certificate from |file_path_ascii|. Returns nullptr if the file
+// contained more that one certificate.
+scoped_refptr<ParsedCertificate> ReadCertFromFile(
+ const std::string& file_path_ascii);
+
+// Reads a data file relative to the src root directory.
+std::string ReadTestFileToString(const std::string& file_path_ascii);
+
+// Asserts that |actual_errors| matches |expected_errors_str|.
+//
+// This is a helper function to simplify rebasing the error expectations when
+// they originate from a test file.
+void VerifyCertPathErrors(const std::string& expected_errors_str,
+ const CertPathErrors& actual_errors,
+ const ParsedCertificateList& chain,
+ const std::string& errors_file_path);
+
+// Asserts that |actual_errors| matches |expected_errors_str|.
+//
+// This is a helper function to simplify rebasing the error expectations when
+// they originate from a test file.
+void VerifyCertErrors(const std::string& expected_errors_str,
+ const CertErrors& actual_errors,
+ const std::string& errors_file_path);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_TEST_HELPERS_H_
diff --git a/chromium/net/cert/pki/trust_store.cc b/chromium/net/cert/pki/trust_store.cc
new file mode 100644
index 00000000000..ee504bff53f
--- /dev/null
+++ b/chromium/net/cert/pki/trust_store.cc
@@ -0,0 +1,93 @@
+// Copyright 2016 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 "net/cert/pki/trust_store.h"
+
+#include "base/notreached.h"
+
+namespace net {
+
+CertificateTrust CertificateTrust::ForTrustAnchor() {
+ CertificateTrust result;
+ result.type = CertificateTrustType::TRUSTED_ANCHOR;
+ return result;
+}
+
+CertificateTrust CertificateTrust::ForTrustAnchorEnforcingExpiration() {
+ CertificateTrust result;
+ result.type = CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION;
+ return result;
+}
+
+CertificateTrust CertificateTrust::ForTrustAnchorEnforcingConstraints() {
+ CertificateTrust result;
+ result.type = CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS;
+ return result;
+}
+
+CertificateTrust CertificateTrust::ForUnspecified() {
+ CertificateTrust result;
+ result.type = CertificateTrustType::UNSPECIFIED;
+ return result;
+}
+
+CertificateTrust CertificateTrust::ForDistrusted() {
+ CertificateTrust result;
+ result.type = CertificateTrustType::DISTRUSTED;
+ return result;
+}
+
+bool CertificateTrust::IsTrustAnchor() const {
+ switch (type) {
+ case CertificateTrustType::DISTRUSTED:
+ case CertificateTrustType::UNSPECIFIED:
+ return false;
+ case CertificateTrustType::TRUSTED_ANCHOR:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
+ return true;
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+bool CertificateTrust::IsDistrusted() const {
+ switch (type) {
+ case CertificateTrustType::DISTRUSTED:
+ return true;
+ case CertificateTrustType::UNSPECIFIED:
+ case CertificateTrustType::TRUSTED_ANCHOR:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
+ return false;
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+bool CertificateTrust::HasUnspecifiedTrust() const {
+ switch (type) {
+ case CertificateTrustType::UNSPECIFIED:
+ return true;
+ case CertificateTrustType::DISTRUSTED:
+ case CertificateTrustType::TRUSTED_ANCHOR:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
+ return false;
+ }
+
+ NOTREACHED();
+ return true;
+}
+
+TrustStore::TrustStore() = default;
+
+void TrustStore::AsyncGetIssuersOf(const ParsedCertificate* cert,
+ std::unique_ptr<Request>* out_req) {
+ out_req->reset();
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/trust_store.h b/chromium/net/cert/pki/trust_store.h
new file mode 100644
index 00000000000..1c3a721ea29
--- /dev/null
+++ b/chromium/net/cert/pki/trust_store.h
@@ -0,0 +1,90 @@
+// Copyright 2016 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 NET_CERT_PKI_TRUST_STORE_H_
+#define NET_CERT_PKI_TRUST_STORE_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/supports_user_data.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/cert_issuer_source.h"
+#include "net/cert/pki/parsed_certificate.h"
+
+namespace net {
+
+enum class CertificateTrustType {
+ // This certificate is explicitly blocked (distrusted).
+ DISTRUSTED,
+
+ // The trustedness of this certificate is unknown (inherits trust from
+ // its issuer).
+ UNSPECIFIED,
+
+ // This certificate is a trust anchor (as defined by RFC 5280). The only
+ // fields in the certificate that are meaningful are its name and SPKI.
+ TRUSTED_ANCHOR,
+
+ // This certificate is a trust anchor which additionally has expiration
+ // enforced. The only fields in the certificate that are meaningful are its
+ // name, SPKI, and validity period.
+ TRUSTED_ANCHOR_WITH_EXPIRATION,
+
+ // This certificate is a trust anchor for which some of the fields in the
+ // certificate (in addition to the name and SPKI) should be used during the
+ // verification process. See VerifyCertificateChain() for details on how
+ // constraints are applied.
+ TRUSTED_ANCHOR_WITH_CONSTRAINTS,
+
+ LAST = TRUSTED_ANCHOR_WITH_CONSTRAINTS
+};
+
+// Describes the level of trust in a certificate. See CertificateTrustType for
+// details.
+//
+// TODO(eroman): Right now this is just a glorified wrapper around an enum...
+struct NET_EXPORT CertificateTrust {
+ static CertificateTrust ForTrustAnchor();
+ static CertificateTrust ForTrustAnchorEnforcingExpiration();
+ static CertificateTrust ForTrustAnchorEnforcingConstraints();
+ static CertificateTrust ForUnspecified();
+ static CertificateTrust ForDistrusted();
+
+ bool IsTrustAnchor() const;
+ bool IsDistrusted() const;
+ bool HasUnspecifiedTrust() const;
+
+ CertificateTrustType type = CertificateTrustType::UNSPECIFIED;
+};
+
+// Interface for finding intermediates / trust anchors, and testing the
+// trustedness of certificates.
+class NET_EXPORT TrustStore : public CertIssuerSource {
+ public:
+ TrustStore();
+
+ TrustStore(const TrustStore&) = delete;
+ TrustStore& operator=(const TrustStore&) = delete;
+
+ // Returns the trusted of |cert|, which must be non-null.
+ //
+ // Optionally, if |debug_data| is non-null, debug information may be added
+ // (any added Data must implement the Clone method.) The same |debug_data|
+ // object may be passed to multiple GetTrust calls for a single verification,
+ // so implementations should check whether they already added data with a
+ // certain key and update it instead of overwriting it.
+ virtual CertificateTrust GetTrust(
+ const ParsedCertificate* cert,
+ base::SupportsUserData* debug_data) const = 0;
+
+ // Disable async issuers for TrustStore, as it isn't needed.
+ // TODO(mattm): Pass debug_data here too.
+ void AsyncGetIssuersOf(const ParsedCertificate* cert,
+ std::unique_ptr<Request>* out_req) final;
+};
+
+} // namespace net
+
+#endif // NET_CERT_PKI_TRUST_STORE_H_
diff --git a/chromium/net/cert/pki/trust_store_collection.cc b/chromium/net/cert/pki/trust_store_collection.cc
new file mode 100644
index 00000000000..03657c4d4a0
--- /dev/null
+++ b/chromium/net/cert/pki/trust_store_collection.cc
@@ -0,0 +1,46 @@
+// Copyright 2016 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 "net/cert/pki/trust_store_collection.h"
+
+namespace net {
+
+TrustStoreCollection::TrustStoreCollection() = default;
+TrustStoreCollection::~TrustStoreCollection() = default;
+
+void TrustStoreCollection::AddTrustStore(TrustStore* store) {
+ DCHECK(store);
+ stores_.push_back(store);
+}
+
+void TrustStoreCollection::SyncGetIssuersOf(const ParsedCertificate* cert,
+ ParsedCertificateList* issuers) {
+ for (auto* store : stores_) {
+ store->SyncGetIssuersOf(cert, issuers);
+ }
+}
+
+CertificateTrust TrustStoreCollection::GetTrust(
+ const ParsedCertificate* cert,
+ base::SupportsUserData* debug_data) const {
+ // The current aggregate result.
+ CertificateTrust result = CertificateTrust::ForUnspecified();
+
+ for (auto* store : stores_) {
+ CertificateTrust cur_trust = store->GetTrust(cert, debug_data);
+
+ // * If any stores distrust the certificate, consider it untrusted.
+ // * If multiple stores consider it trusted, use the trust result from the
+ // last one
+ if (!cur_trust.HasUnspecifiedTrust()) {
+ result = cur_trust;
+ if (result.IsDistrusted())
+ break;
+ }
+ }
+
+ return result;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/trust_store_collection.h b/chromium/net/cert/pki/trust_store_collection.h
new file mode 100644
index 00000000000..4d168aa6cfb
--- /dev/null
+++ b/chromium/net/cert/pki/trust_store_collection.h
@@ -0,0 +1,44 @@
+// Copyright 2016 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 NET_CERT_PKI_TRUST_STORE_COLLECTION_H_
+#define NET_CERT_PKI_TRUST_STORE_COLLECTION_H_
+
+#include "base/memory/ref_counted.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/trust_store.h"
+
+namespace net {
+
+// TrustStoreCollection is an implementation of TrustStore which combines the
+// results from multiple TrustStores.
+//
+// The order of the matches will correspond to a concatenation of matches in
+// the order the stores were added.
+class NET_EXPORT TrustStoreCollection : public TrustStore {
+ public:
+ TrustStoreCollection();
+
+ TrustStoreCollection(const TrustStoreCollection&) = delete;
+ TrustStoreCollection& operator=(const TrustStoreCollection&) = delete;
+
+ ~TrustStoreCollection() override;
+
+ // Includes results from |store| in the combined output. |store| must
+ // outlive the TrustStoreCollection.
+ void AddTrustStore(TrustStore* store);
+
+ // TrustStore implementation:
+ void SyncGetIssuersOf(const ParsedCertificate* cert,
+ ParsedCertificateList* issuers) override;
+ CertificateTrust GetTrust(const ParsedCertificate* cert,
+ base::SupportsUserData* debug_data) const override;
+
+ private:
+ std::vector<TrustStore*> stores_;
+};
+
+} // namespace net
+
+#endif // NET_CERT_PKI_TRUST_STORE_COLLECTION_H_
diff --git a/chromium/net/cert/pki/trust_store_collection_unittest.cc b/chromium/net/cert/pki/trust_store_collection_unittest.cc
new file mode 100644
index 00000000000..8b17c5a8d8d
--- /dev/null
+++ b/chromium/net/cert/pki/trust_store_collection_unittest.cc
@@ -0,0 +1,182 @@
+// Copyright 2016 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 "net/cert/pki/trust_store_collection.h"
+
+#include "net/cert/pki/test_helpers.h"
+#include "net/cert/pki/trust_store_in_memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+class TrustStoreCollectionTest : public testing::Test {
+ public:
+ void SetUp() override {
+ ParsedCertificateList chain;
+ ASSERT_TRUE(ReadCertChainFromFile(
+ "net/data/verify_certificate_chain_unittest/key-rollover/oldchain.pem",
+ &chain));
+
+ ASSERT_EQ(3U, chain.size());
+ target_ = chain[0];
+ oldintermediate_ = chain[1];
+ oldroot_ = chain[2];
+ ASSERT_TRUE(target_);
+ ASSERT_TRUE(oldintermediate_);
+ ASSERT_TRUE(oldroot_);
+
+ ASSERT_TRUE(
+ ReadCertChainFromFile("net/data/verify_certificate_chain_unittest/"
+ "key-rollover/longrolloverchain.pem",
+ &chain));
+
+ ASSERT_EQ(5U, chain.size());
+ newintermediate_ = chain[1];
+ newroot_ = chain[2];
+ newrootrollover_ = chain[3];
+ ASSERT_TRUE(newintermediate_);
+ ASSERT_TRUE(newroot_);
+ ASSERT_TRUE(newrootrollover_);
+ }
+
+ protected:
+ scoped_refptr<ParsedCertificate> oldroot_;
+ scoped_refptr<ParsedCertificate> newroot_;
+ scoped_refptr<ParsedCertificate> newrootrollover_;
+
+ scoped_refptr<ParsedCertificate> target_;
+ scoped_refptr<ParsedCertificate> oldintermediate_;
+ scoped_refptr<ParsedCertificate> newintermediate_;
+};
+
+// Collection contains no stores, should return no results.
+TEST_F(TrustStoreCollectionTest, NoStores) {
+ ParsedCertificateList issuers;
+
+ TrustStoreCollection collection;
+ collection.SyncGetIssuersOf(target_.get(), &issuers);
+
+ EXPECT_TRUE(issuers.empty());
+}
+
+// Collection contains only one store.
+TEST_F(TrustStoreCollectionTest, OneStore) {
+ ParsedCertificateList issuers;
+
+ TrustStoreCollection collection;
+ TrustStoreInMemory in_memory;
+ in_memory.AddTrustAnchor(newroot_);
+ collection.AddTrustStore(&in_memory);
+ collection.SyncGetIssuersOf(newintermediate_.get(), &issuers);
+
+ ASSERT_EQ(1U, issuers.size());
+ EXPECT_EQ(newroot_.get(), issuers[0].get());
+
+ // newroot_ is trusted.
+ CertificateTrust trust =
+ collection.GetTrust(newroot_.get(), /*debug_data=*/nullptr);
+ EXPECT_EQ(CertificateTrustType::TRUSTED_ANCHOR, trust.type);
+
+ // oldroot_ is not.
+ trust = collection.GetTrust(oldroot_.get(), /*debug_data=*/nullptr);
+ EXPECT_EQ(CertificateTrustType::UNSPECIFIED, trust.type);
+}
+
+// SyncGetIssuersOf() should append to its output parameters rather than assign
+// them.
+TEST_F(TrustStoreCollectionTest, OutputVectorsAppendedTo) {
+ ParsedCertificateList issuers;
+
+ // Populate the out-parameter with some values.
+ issuers.resize(3);
+
+ TrustStoreCollection collection;
+ TrustStoreInMemory in_memory;
+ in_memory.AddTrustAnchor(newroot_);
+ collection.AddTrustStore(&in_memory);
+ collection.SyncGetIssuersOf(newintermediate_.get(), &issuers);
+
+ ASSERT_EQ(4U, issuers.size());
+ EXPECT_EQ(newroot_.get(), issuers[3].get());
+
+ // newroot_ is trusted.
+ CertificateTrust trust =
+ collection.GetTrust(newroot_.get(), /*debug_data=*/nullptr);
+ EXPECT_EQ(CertificateTrustType::TRUSTED_ANCHOR, trust.type);
+
+ // newrootrollover_ is not.
+ trust = collection.GetTrust(newrootrollover_.get(), /*debug_data=*/nullptr);
+ EXPECT_EQ(CertificateTrustType::UNSPECIFIED, trust.type);
+}
+
+// Collection contains two stores.
+TEST_F(TrustStoreCollectionTest, TwoStores) {
+ ParsedCertificateList issuers;
+
+ TrustStoreCollection collection;
+ TrustStoreInMemory in_memory1;
+ TrustStoreInMemory in_memory2;
+ in_memory1.AddTrustAnchor(newroot_);
+ in_memory2.AddTrustAnchor(oldroot_);
+ collection.AddTrustStore(&in_memory1);
+ collection.AddTrustStore(&in_memory2);
+ collection.SyncGetIssuersOf(newintermediate_.get(), &issuers);
+
+ ASSERT_EQ(2U, issuers.size());
+ EXPECT_EQ(newroot_.get(), issuers[0].get());
+ EXPECT_EQ(oldroot_.get(), issuers[1].get());
+
+ // newroot_ is trusted.
+ CertificateTrust trust =
+ collection.GetTrust(newroot_.get(), /*debug_data=*/nullptr);
+ EXPECT_EQ(CertificateTrustType::TRUSTED_ANCHOR, trust.type);
+
+ // oldroot_ is trusted.
+ trust = collection.GetTrust(oldroot_.get(), /*debug_data=*/nullptr);
+ EXPECT_EQ(CertificateTrustType::TRUSTED_ANCHOR, trust.type);
+
+ // newrootrollover_ is not.
+ trust = collection.GetTrust(newrootrollover_.get(), /*debug_data=*/nullptr);
+ EXPECT_EQ(CertificateTrustType::UNSPECIFIED, trust.type);
+}
+
+// Collection contains two stores. The certificate is marked as trusted in one,
+// but distrusted in the other.
+TEST_F(TrustStoreCollectionTest, DistrustTakesPriority) {
+ ParsedCertificateList issuers;
+
+ TrustStoreCollection collection;
+ TrustStoreInMemory in_memory1;
+ TrustStoreInMemory in_memory2;
+
+ // newroot_ is trusted in store1, distrusted in store2.
+ in_memory1.AddTrustAnchor(newroot_);
+ in_memory2.AddDistrustedCertificateForTest(newroot_);
+
+ // oldintermediate is distrusted in store1, trusted in store2.
+ in_memory1.AddDistrustedCertificateForTest(oldintermediate_);
+ in_memory2.AddTrustAnchor(oldintermediate_);
+
+ collection.AddTrustStore(&in_memory1);
+ collection.AddTrustStore(&in_memory2);
+
+ // newroot_ is distrusted..
+ CertificateTrust trust =
+ collection.GetTrust(newroot_.get(), /*debug_data=*/nullptr);
+ EXPECT_EQ(CertificateTrustType::DISTRUSTED, trust.type);
+
+ // oldintermediate_ is distrusted.
+ trust = collection.GetTrust(oldintermediate_.get(), /*debug_data=*/nullptr);
+ EXPECT_EQ(CertificateTrustType::DISTRUSTED, trust.type);
+
+ // newrootrollover_ is unspecified.
+ trust = collection.GetTrust(newrootrollover_.get(), /*debug_data=*/nullptr);
+ EXPECT_EQ(CertificateTrustType::UNSPECIFIED, trust.type);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/pki/trust_store_in_memory.cc b/chromium/net/cert/pki/trust_store_in_memory.cc
new file mode 100644
index 00000000000..7769b992429
--- /dev/null
+++ b/chromium/net/cert/pki/trust_store_in_memory.cc
@@ -0,0 +1,92 @@
+// Copyright 2016 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 "net/cert/pki/trust_store_in_memory.h"
+
+namespace net {
+
+TrustStoreInMemory::TrustStoreInMemory() = default;
+TrustStoreInMemory::~TrustStoreInMemory() = default;
+
+bool TrustStoreInMemory::IsEmpty() const {
+ return entries_.empty();
+}
+
+void TrustStoreInMemory::Clear() {
+ entries_.clear();
+}
+
+void TrustStoreInMemory::AddTrustAnchor(scoped_refptr<ParsedCertificate> cert) {
+ AddCertificate(std::move(cert), CertificateTrust::ForTrustAnchor());
+}
+
+void TrustStoreInMemory::AddTrustAnchorWithExpiration(
+ scoped_refptr<ParsedCertificate> cert) {
+ AddCertificate(std::move(cert),
+ CertificateTrust::ForTrustAnchorEnforcingExpiration());
+}
+
+void TrustStoreInMemory::AddTrustAnchorWithConstraints(
+ scoped_refptr<ParsedCertificate> cert) {
+ AddCertificate(std::move(cert),
+ CertificateTrust::ForTrustAnchorEnforcingConstraints());
+}
+
+void TrustStoreInMemory::AddDistrustedCertificateForTest(
+ scoped_refptr<ParsedCertificate> cert) {
+ AddCertificate(std::move(cert), CertificateTrust::ForDistrusted());
+}
+
+void TrustStoreInMemory::AddCertificateWithUnspecifiedTrust(
+ scoped_refptr<ParsedCertificate> cert) {
+ AddCertificate(std::move(cert), CertificateTrust::ForUnspecified());
+}
+
+void TrustStoreInMemory::SyncGetIssuersOf(const ParsedCertificate* cert,
+ ParsedCertificateList* issuers) {
+ auto range = entries_.equal_range(cert->normalized_issuer().AsStringPiece());
+ for (auto it = range.first; it != range.second; ++it)
+ issuers->push_back(it->second.cert);
+}
+
+CertificateTrust TrustStoreInMemory::GetTrust(
+ const ParsedCertificate* cert,
+ base::SupportsUserData* debug_data) const {
+ const Entry* entry = GetEntry(cert);
+ return entry ? entry->trust : CertificateTrust::ForUnspecified();
+}
+
+bool TrustStoreInMemory::Contains(const ParsedCertificate* cert) const {
+ return GetEntry(cert) != nullptr;
+}
+
+TrustStoreInMemory::Entry::Entry() = default;
+TrustStoreInMemory::Entry::Entry(const Entry& other) = default;
+TrustStoreInMemory::Entry::~Entry() = default;
+
+void TrustStoreInMemory::AddCertificate(scoped_refptr<ParsedCertificate> cert,
+ const CertificateTrust& trust) {
+ Entry entry;
+ entry.cert = std::move(cert);
+ entry.trust = trust;
+
+ // TODO(mattm): should this check for duplicate certificates?
+ entries_.insert(
+ std::make_pair(entry.cert->normalized_subject().AsStringPiece(), entry));
+}
+
+const TrustStoreInMemory::Entry* TrustStoreInMemory::GetEntry(
+ const ParsedCertificate* cert) const {
+ auto range = entries_.equal_range(cert->normalized_subject().AsStringPiece());
+ for (auto it = range.first; it != range.second; ++it) {
+ if (cert == it->second.cert.get() ||
+ cert->der_cert() == it->second.cert->der_cert()) {
+ // NOTE: ambiguity when there are duplicate entries.
+ return &it->second;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/trust_store_in_memory.h b/chromium/net/cert/pki/trust_store_in_memory.h
new file mode 100644
index 00000000000..1d6a7c69257
--- /dev/null
+++ b/chromium/net/cert/pki/trust_store_in_memory.h
@@ -0,0 +1,91 @@
+// Copyright 2016 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 NET_CERT_PKI_TRUST_STORE_IN_MEMORY_H_
+#define NET_CERT_PKI_TRUST_STORE_IN_MEMORY_H_
+
+#include <unordered_map>
+
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/trust_store.h"
+
+namespace net {
+
+// A very simple implementation of a TrustStore, which contains a set of
+// certificates and their trustedness.
+class NET_EXPORT TrustStoreInMemory : public TrustStore {
+ public:
+ TrustStoreInMemory();
+
+ TrustStoreInMemory(const TrustStoreInMemory&) = delete;
+ TrustStoreInMemory& operator=(const TrustStoreInMemory&) = delete;
+
+ ~TrustStoreInMemory() override;
+
+ // Returns whether the TrustStore is in the initial empty state.
+ bool IsEmpty() const;
+
+ // Empties the trust store, resetting it to original state.
+ void Clear();
+
+ // Adds a certificate as a trust anchor (only the SPKI and subject will be
+ // used during verification).
+ void AddTrustAnchor(scoped_refptr<ParsedCertificate> cert);
+
+ // Adds a certificate as a trust anchor which will have expiration enforced.
+ // See VerifyCertificateChain for details.
+ void AddTrustAnchorWithExpiration(scoped_refptr<ParsedCertificate> cert);
+
+ // Adds a certificate as a trust anchor and extracts anchor constraints from
+ // the certificate. See VerifyCertificateChain for details.
+ void AddTrustAnchorWithConstraints(scoped_refptr<ParsedCertificate> cert);
+
+ // TODO(eroman): This is marked "ForTest" as the current implementation
+ // requires an exact match on the certificate DER (a wider match by say
+ // issuer/serial is probably what we would want for a real implementation).
+ void AddDistrustedCertificateForTest(scoped_refptr<ParsedCertificate> cert);
+
+ // Adds a certificate to the store, that is neither trusted nor untrusted.
+ void AddCertificateWithUnspecifiedTrust(
+ scoped_refptr<ParsedCertificate> cert);
+
+ // TrustStore implementation:
+ void SyncGetIssuersOf(const ParsedCertificate* cert,
+ ParsedCertificateList* issuers) override;
+ CertificateTrust GetTrust(const ParsedCertificate* cert,
+ base::SupportsUserData* debug_data) const override;
+
+ // Returns true if the trust store contains the given ParsedCertificate
+ // (matches by DER).
+ bool Contains(const ParsedCertificate* cert) const;
+
+ private:
+ struct Entry {
+ Entry();
+ Entry(const Entry& other);
+ ~Entry();
+
+ scoped_refptr<ParsedCertificate> cert;
+ CertificateTrust trust;
+ };
+
+ // Multimap from normalized subject -> Entry.
+ std::unordered_multimap<base::StringPiece, Entry, base::StringPieceHash>
+ entries_;
+
+ // Adds a certificate with the specified trust settings. Both trusted and
+ // distrusted certificates require a full DER match.
+ void AddCertificate(scoped_refptr<ParsedCertificate> cert,
+ const CertificateTrust& trust);
+
+ // Returns the `Entry` matching `cert`, or `nullptr` if not in the trust
+ // store.
+ const Entry* GetEntry(const ParsedCertificate* cert) const;
+};
+
+} // namespace net
+
+#endif // NET_CERT_PKI_TRUST_STORE_IN_MEMORY_H_
diff --git a/chromium/net/cert/pki/verify_certificate_chain.cc b/chromium/net/cert/pki/verify_certificate_chain.cc
new file mode 100644
index 00000000000..5fea3878087
--- /dev/null
+++ b/chromium/net/cert/pki/verify_certificate_chain.cc
@@ -0,0 +1,1357 @@
+// Copyright 2015 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 "net/cert/pki/verify_certificate_chain.h"
+
+#include <algorithm>
+
+#include "base/check.h"
+#include "base/memory/raw_ptr.h"
+#include "net/cert/pki/cert_error_params.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/common_cert_errors.h"
+#include "net/cert/pki/extended_key_usage.h"
+#include "net/cert/pki/name_constraints.h"
+#include "net/cert/pki/parse_certificate.h"
+#include "net/cert/pki/signature_algorithm.h"
+#include "net/cert/pki/trust_store.h"
+#include "net/cert/pki/verify_signed_data.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+
+namespace net {
+
+namespace {
+
+bool IsHandledCriticalExtension(const ParsedExtension& extension) {
+ if (extension.oid == der::Input(kBasicConstraintsOid))
+ return true;
+ // Key Usage is NOT processed for end-entity certificates (this is the
+ // responsibility of callers), however it is considered "handled" here in
+ // order to allow being marked as critical.
+ if (extension.oid == der::Input(kKeyUsageOid))
+ return true;
+ if (extension.oid == der::Input(kExtKeyUsageOid))
+ return true;
+ if (extension.oid == der::Input(kNameConstraintsOid))
+ return true;
+ if (extension.oid == der::Input(kSubjectAltNameOid))
+ return true;
+ if (extension.oid == der::Input(kCertificatePoliciesOid)) {
+ // Policy qualifiers are skipped during processing, so if the
+ // extension is marked critical need to ensure there weren't any
+ // qualifiers other than User Notice / CPS.
+ //
+ // This follows from RFC 5280 section 4.2.1.4:
+ //
+ // If this extension is critical, the path validation software MUST
+ // be able to interpret this extension (including the optional
+ // qualifier), or MUST reject the certificate.
+ std::vector<der::Input> unused_policies;
+ CertErrors unused_errors;
+ return ParseCertificatePoliciesExtensionOids(
+ extension.value, true /*fail_parsing_unknown_qualifier_oids*/,
+ &unused_policies, &unused_errors);
+
+ // TODO(eroman): Give a better error message.
+ }
+ if (extension.oid == der::Input(kPolicyMappingsOid))
+ return true;
+ if (extension.oid == der::Input(kPolicyConstraintsOid))
+ return true;
+ if (extension.oid == der::Input(kInhibitAnyPolicyOid))
+ return true;
+
+ return false;
+}
+
+// Adds errors to |errors| if the certificate contains unconsumed _critical_
+// extensions.
+void VerifyNoUnconsumedCriticalExtensions(const ParsedCertificate& cert,
+ CertErrors* errors) {
+ for (const auto& it : cert.extensions()) {
+ const ParsedExtension& extension = it.second;
+ if (extension.critical && !IsHandledCriticalExtension(extension)) {
+ errors->AddError(cert_errors::kUnconsumedCriticalExtension,
+ CreateCertErrorParams2Der("oid", extension.oid, "value",
+ extension.value));
+ }
+ }
+}
+
+// Returns true if |cert| was self-issued. The definition of self-issuance
+// comes from RFC 5280 section 6.1:
+//
+// A certificate is self-issued if the same DN appears in the subject
+// and issuer fields (the two DNs are the same if they match according
+// to the rules specified in Section 7.1). In general, the issuer and
+// subject of the certificates that make up a path are different for
+// each certificate. However, a CA may issue a certificate to itself to
+// support key rollover or changes in certificate policies. These
+// self-issued certificates are not counted when evaluating path length
+// or name constraints.
+[[nodiscard]] bool IsSelfIssued(const ParsedCertificate& cert) {
+ return cert.normalized_subject() == cert.normalized_issuer();
+}
+
+// Adds errors to |errors| if |cert| is not valid at time |time|.
+//
+// The certificate's validity requirements are described by RFC 5280 section
+// 4.1.2.5:
+//
+// The validity period for a certificate is the period of time from
+// notBefore through notAfter, inclusive.
+void VerifyTimeValidity(const ParsedCertificate& cert,
+ const der::GeneralizedTime& time,
+ CertErrors* errors) {
+ if (time < cert.tbs().validity_not_before)
+ errors->AddError(cert_errors::kValidityFailedNotBefore);
+
+ if (cert.tbs().validity_not_after < time)
+ errors->AddError(cert_errors::kValidityFailedNotAfter);
+}
+
+// Adds errors to |errors| if |cert| has internally inconsistent signature
+// algorithms.
+//
+// X.509 certificates contain two different signature algorithms:
+// (1) The signatureAlgorithm field of Certificate
+// (2) The signature field of TBSCertificate
+//
+// According to RFC 5280 section 4.1.1.2 and 4.1.2.3 these two fields must be
+// equal:
+//
+// This field MUST contain the same algorithm identifier as the
+// signature field in the sequence tbsCertificate (Section 4.1.2.3).
+//
+// The spec is not explicit about what "the same algorithm identifier" means.
+// Our interpretation is that the two DER-encoded fields must be byte-for-byte
+// identical.
+//
+// In practice however there are certificates which use different encodings for
+// specifying RSA with SHA1 (different OIDs). This is special-cased for
+// compatibility sake.
+bool VerifySignatureAlgorithmsMatch(const ParsedCertificate& cert,
+ CertErrors* errors) {
+ const der::Input& alg1_tlv = cert.signature_algorithm_tlv();
+ const der::Input& alg2_tlv = cert.tbs().signature_algorithm_tlv;
+
+ // Ensure that the two DER-encoded signature algorithms are byte-for-byte
+ // equal.
+ if (alg1_tlv == alg2_tlv)
+ return true;
+
+ // But make a compatibility concession if alternate encodings are used
+ // TODO(eroman): Turn this warning into an error.
+ // TODO(eroman): Add a unit-test that exercises this case.
+ absl::optional<SignatureAlgorithm> alg1 =
+ ParseSignatureAlgorithm(alg1_tlv, errors);
+ if (!alg1) {
+ errors->AddError(cert_errors::kUnacceptableSignatureAlgorithm);
+ return false;
+ }
+ absl::optional<SignatureAlgorithm> alg2 =
+ ParseSignatureAlgorithm(alg2_tlv, errors);
+ if (!alg2) {
+ errors->AddError(cert_errors::kUnacceptableSignatureAlgorithm);
+ return false;
+ }
+
+ if (*alg1 == *alg2) {
+ errors->AddWarning(
+ cert_errors::kSignatureAlgorithmsDifferentEncoding,
+ CreateCertErrorParams2Der("Certificate.algorithm", alg1_tlv,
+ "TBSCertificate.signature", alg2_tlv));
+ return true;
+ }
+
+ errors->AddError(
+ cert_errors::kSignatureAlgorithmMismatch,
+ CreateCertErrorParams2Der("Certificate.algorithm", alg1_tlv,
+ "TBSCertificate.signature", alg2_tlv));
+ return false;
+}
+
+// Verify that |cert| can be used for |required_key_purpose|.
+void VerifyExtendedKeyUsage(const ParsedCertificate& cert,
+ KeyPurpose required_key_purpose,
+ CertErrors* errors) {
+ switch (required_key_purpose) {
+ case KeyPurpose::ANY_EKU:
+ return;
+ case KeyPurpose::SERVER_AUTH: {
+ // TODO(eroman): Is it OK for the target certificate to omit the EKU?
+ if (!cert.has_extended_key_usage())
+ return;
+
+ for (const auto& key_purpose_oid : cert.extended_key_usage()) {
+ if (key_purpose_oid == der::Input(kAnyEKU))
+ return;
+ if (key_purpose_oid == der::Input(kServerAuth))
+ return;
+ }
+
+ // Check if the certificate contains Netscape Server Gated Crypto.
+ // nsSGC is a deprecated mechanism, and not part of RFC 5280's
+ // profile. Some unexpired certificate chains still rely on it though
+ // (there are intermediates valid until 2020 that use it).
+ bool has_nsgc = false;
+
+ for (const auto& key_purpose_oid : cert.extended_key_usage()) {
+ if (key_purpose_oid == der::Input(kNetscapeServerGatedCrypto)) {
+ has_nsgc = true;
+ break;
+ }
+ }
+
+ if (has_nsgc) {
+ errors->AddWarning(cert_errors::kEkuLacksServerAuthButHasGatedCrypto);
+
+ // Allow NSGC for legacy RSA SHA1 intermediates, for compatibility with
+ // platform verifiers.
+ //
+ // In practice the chain will be rejected with or without this
+ // compatibility hack. The difference is whether the final error will be
+ // ERR_CERT_WEAK_SIGNATURE_ALGORITHM (with compatibility hack) vs
+ // ERR_CERT_INVALID (without hack).
+ //
+ // TODO(https://crbug.com/843735): Remove this once error-for-error
+ // equivalence between builtin verifier and platform verifier is less
+ // important.
+ if ((cert.has_basic_constraints() && cert.basic_constraints().is_ca) &&
+ cert.signature_algorithm() == SignatureAlgorithm::kRsaPkcs1Sha1) {
+ return;
+ }
+ }
+
+ errors->AddError(cert_errors::kEkuLacksServerAuth);
+ break;
+ }
+ case KeyPurpose::CLIENT_AUTH: {
+ // TODO(eroman): Is it OK for the target certificate to omit the EKU?
+ if (!cert.has_extended_key_usage())
+ return;
+
+ for (const auto& key_purpose_oid : cert.extended_key_usage()) {
+ if (key_purpose_oid == der::Input(kAnyEKU))
+ return;
+ if (key_purpose_oid == der::Input(kClientAuth))
+ return;
+ }
+
+ errors->AddError(cert_errors::kEkuLacksClientAuth);
+ break;
+ }
+ }
+}
+
+// Returns |true| if |policies| contains the OID |search_oid|.
+bool SetContains(const std::set<der::Input>& policies,
+ const der::Input& search_oid) {
+ return policies.count(search_oid) > 0;
+}
+
+// Representation of RFC 5280's "valid_policy_tree", used to keep track of the
+// valid policies and policy re-mappings.
+//
+// ValidPolicyTree differs slightly from RFC 5280's description in that:
+//
+// (1) It does not track "qualifier_set". This is not needed as it is not
+// output by this implementation.
+//
+// (2) It only stores the most recent level of the policy tree rather than
+// the full tree of nodes.
+class ValidPolicyTree {
+ public:
+ ValidPolicyTree() = default;
+
+ ValidPolicyTree(const ValidPolicyTree&) = delete;
+ ValidPolicyTree& operator=(const ValidPolicyTree&) = delete;
+
+ struct Node {
+ // |root_policy| is equivalent to |valid_policy|, but in the domain of the
+ // caller.
+ //
+ // The reason for this distinction is the Policy Mappings extension.
+ //
+ // So whereas |valid_policy| is in the remapped domain defined by the
+ // issuing certificate, |root_policy| is in the fixed domain of the caller.
+ //
+ // OIDs in "user_initial_policy_set" and "user_constrained_policy_set" are
+ // directly comparable to |root_policy| values, but not necessarily to
+ // |valid_policy|.
+ //
+ // In terms of the valid policy tree, |root_policy| can be found by
+ // starting at the node's root ancestor, and finding the first node with a
+ // valid_policy other than anyPolicy. This is effectively the same process
+ // as used during policy tree intersection in RFC 5280 6.1.5.g.iii.1
+ der::Input root_policy;
+
+ // The same as RFC 5280's "valid_policy" variable.
+ der::Input valid_policy;
+
+ // The same as RFC 5280s "expected_policy_set" variable.
+ std::set<der::Input> expected_policy_set;
+
+ // Note that RFC 5280's "qualifier_set" is omitted.
+ };
+
+ // Level represents all the nodes at depth "i" in the valid_policy_tree.
+ using Level = std::vector<Node>;
+
+ // Initializes the ValidPolicyTree for the given "user_initial_policy_set".
+ //
+ // In RFC 5280, the valid_policy_tree is initialized to a root node at depth
+ // 0 of "anyPolicy"; the intersection with the "user_initial_policy_set" is
+ // done at the end (Wrap Up) as described in section 6.1.5 step g.
+ //
+ // Whereas in this implementation, the restriction on policies is added here,
+ // and intersecting the valid policy tree during Wrap Up is no longer needed.
+ //
+ // The final "user_constrained_policy_set" obtained will be the same. The
+ // advantages of this approach is simpler code.
+ void Init(const std::set<der::Input>& user_initial_policy_set) {
+ Clear();
+ for (const der::Input& policy_oid : user_initial_policy_set)
+ AddRootNode(policy_oid);
+ }
+
+ // Returns the current level (i.e. all nodes at depth i in the valid
+ // policy tree).
+ const Level& current_level() const { return current_level_; }
+ Level& current_level() { return current_level_; }
+
+ // In RFC 5280 valid_policy_tree may be set to null. That is represented here
+ // by emptiness.
+ bool IsNull() const { return current_level_.empty(); }
+ void SetNull() { Clear(); }
+
+ // This implementation keeps only the last level of the valid policy
+ // tree. Calling StartLevel() returns the nodes for the previous
+ // level, and starts a new level.
+ Level StartLevel() {
+ Level prev_level;
+ std::swap(prev_level, current_level_);
+ return prev_level;
+ }
+
+ // Gets the set of policies (in terms of root authority's policy domain) that
+ // are valid at the curent level of the policy tree.
+ //
+ // For example:
+ //
+ // * If the valid policy tree was initialized with anyPolicy, then this
+ // function returns what X.509 calls "authorities-constrained-policy-set".
+ //
+ // * If the valid policy tree was instead initialized with the
+ // "user-initial-policy_set", then this function returns what X.509
+ // calls "user-constrained-policy-set"
+ // ("authorities-constrained-policy-set" intersected with the
+ // "user-initial-policy-set").
+ void GetValidRootPolicySet(std::set<der::Input>* policy_set) {
+ policy_set->clear();
+ for (const Node& node : current_level_)
+ policy_set->insert(node.root_policy);
+
+ // If the result includes anyPolicy, simplify it to a set of size 1.
+ if (policy_set->size() > 1 &&
+ SetContains(*policy_set, der::Input(kAnyPolicyOid))) {
+ *policy_set = {der::Input(kAnyPolicyOid)};
+ }
+ }
+
+ // Adds a node |n| to the current level which is a child of |parent|
+ // such that:
+ // * n.valid_policy = policy_oid
+ // * n.expected_policy_set = {policy_oid}
+ void AddNode(const Node& parent, const der::Input& policy_oid) {
+ AddNodeWithExpectedPolicySet(parent, policy_oid, {policy_oid});
+ }
+
+ // Adds a node |n| to the current level which is a child of |parent|
+ // such that:
+ // * n.valid_policy = policy_oid
+ // * n.expected_policy_set = expected_policy_set
+ void AddNodeWithExpectedPolicySet(
+ const Node& parent,
+ const der::Input& policy_oid,
+ const std::set<der::Input>& expected_policy_set) {
+ Node new_node;
+ new_node.valid_policy = policy_oid;
+ new_node.expected_policy_set = expected_policy_set;
+
+ // Consider the root policy as the first policy other than anyPolicy (or
+ // anyPolicy if it hasn't been restricted yet).
+ new_node.root_policy = (parent.root_policy == der::Input(kAnyPolicyOid))
+ ? policy_oid
+ : parent.root_policy;
+
+ current_level_.push_back(std::move(new_node));
+ }
+
+ // Returns the first node having valid_policy == anyPolicy in |level|, or
+ // nullptr if there is none.
+ static const Node* FindAnyPolicyNode(const Level& level) {
+ for (const Node& node : level) {
+ if (node.valid_policy == der::Input(kAnyPolicyOid))
+ return &node;
+ }
+ return nullptr;
+ }
+
+ // Deletes all nodes |n| in |level| where |n.valid_policy| matches the given
+ // |valid_policy|. This may re-order the nodes in |level|.
+ static void DeleteNodesMatchingValidPolicy(const der::Input& valid_policy,
+ Level* level) {
+ // This works by swapping nodes to the end of the vector, and then doing a
+ // single resize to delete them all.
+ auto cur = level->begin();
+ auto end = level->end();
+ while (cur != end) {
+ bool should_delete_node = cur->valid_policy == valid_policy;
+ if (should_delete_node) {
+ end = std::prev(end);
+ if (cur != end)
+ std::iter_swap(cur, end);
+ } else {
+ ++cur;
+ }
+ }
+ level->erase(end, level->end());
+ }
+
+ private:
+ // Deletes all nodes in the valid policy tree.
+ void Clear() { current_level_.clear(); }
+
+ // Adds a node to the current level for OID |policy_oid|. The current level
+ // is assumed to be the root level.
+ void AddRootNode(const der::Input& policy_oid) {
+ Node new_node;
+ new_node.root_policy = policy_oid;
+ new_node.valid_policy = policy_oid;
+ new_node.expected_policy_set = {policy_oid};
+ current_level_.push_back(std::move(new_node));
+ }
+
+ Level current_level_;
+};
+
+// Class that encapsulates the state variables used by certificate path
+// validation.
+class PathVerifier {
+ public:
+ // Same parameters and meaning as VerifyCertificateChain().
+ void Run(const ParsedCertificateList& certs,
+ const CertificateTrust& last_cert_trust,
+ VerifyCertificateChainDelegate* delegate,
+ const der::GeneralizedTime& time,
+ KeyPurpose required_key_purpose,
+ InitialExplicitPolicy initial_explicit_policy,
+ const std::set<der::Input>& user_initial_policy_set,
+ InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
+ InitialAnyPolicyInhibit initial_any_policy_inhibit,
+ std::set<der::Input>* user_constrained_policy_set,
+ CertPathErrors* errors);
+
+ private:
+ // Verifies and updates the valid policies. This corresponds with RFC 5280
+ // section 6.1.3 steps d-f.
+ void VerifyPolicies(const ParsedCertificate& cert,
+ bool is_target_cert,
+ CertErrors* errors);
+
+ // Applies the policy mappings. This corresponds with RFC 5280 section 6.1.4
+ // steps a-b.
+ void VerifyPolicyMappings(const ParsedCertificate& cert, CertErrors* errors);
+
+ // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
+ // Processing" procedure.
+ void BasicCertificateProcessing(const ParsedCertificate& cert,
+ bool is_target_cert,
+ const der::GeneralizedTime& time,
+ KeyPurpose required_key_purpose,
+ CertErrors* errors,
+ bool* shortcircuit_chain_validation);
+
+ // This function corresponds to RFC 5280 section 6.1.4's "Preparation for
+ // Certificate i+1" procedure. |cert| is expected to be an intermediate.
+ void PrepareForNextCertificate(const ParsedCertificate& cert,
+ CertErrors* errors);
+
+ // This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up
+ // Procedure". It does processing for the final certificate (the target cert).
+ void WrapUp(const ParsedCertificate& cert, CertErrors* errors);
+
+ // Enforces trust anchor constraints compatibile with RFC 5937.
+ //
+ // Note that the anchor constraints are encoded via the attached certificate
+ // itself.
+ void ApplyTrustAnchorConstraints(const ParsedCertificate& cert,
+ KeyPurpose required_key_purpose,
+ CertErrors* errors);
+
+ // Initializes the path validation algorithm given anchor constraints. This
+ // follows the description in RFC 5937
+ void ProcessRootCertificate(const ParsedCertificate& cert,
+ const CertificateTrust& trust,
+ const der::GeneralizedTime& time,
+ KeyPurpose required_key_purpose,
+ CertErrors* errors,
+ bool* shortcircuit_chain_validation);
+
+ // Parses |spki| to an EVP_PKEY and checks whether the public key is accepted
+ // by |delegate_|. On failure parsing returns nullptr. If either parsing the
+ // key or key policy failed, adds a high-severity error to |errors|.
+ bssl::UniquePtr<EVP_PKEY> ParseAndCheckPublicKey(const der::Input& spki,
+ CertErrors* errors);
+
+ ValidPolicyTree valid_policy_tree_;
+
+ // Will contain a NameConstraints for each previous cert in the chain which
+ // had nameConstraints. This corresponds to the permitted_subtrees and
+ // excluded_subtrees state variables from RFC 5280.
+ std::vector<const NameConstraints*> name_constraints_list_;
+
+ // |explicit_policy_| corresponds with the same named variable from RFC 5280
+ // section 6.1.2:
+ //
+ // explicit_policy: an integer that indicates if a non-NULL
+ // valid_policy_tree is required. The integer indicates the
+ // number of non-self-issued certificates to be processed before
+ // this requirement is imposed. Once set, this variable may be
+ // decreased, but may not be increased. That is, if a certificate in the
+ // path requires a non-NULL valid_policy_tree, a later certificate cannot
+ // remove this requirement. If initial-explicit-policy is set, then the
+ // initial value is 0, otherwise the initial value is n+1.
+ size_t explicit_policy_;
+
+ // |inhibit_any_policy_| corresponds with the same named variable from RFC
+ // 5280 section 6.1.2:
+ //
+ // inhibit_anyPolicy: an integer that indicates whether the
+ // anyPolicy policy identifier is considered a match. The
+ // integer indicates the number of non-self-issued certificates
+ // to be processed before the anyPolicy OID, if asserted in a
+ // certificate other than an intermediate self-issued
+ // certificate, is ignored. Once set, this variable may be
+ // decreased, but may not be increased. That is, if a
+ // certificate in the path inhibits processing of anyPolicy, a
+ // later certificate cannot permit it. If initial-any-policy-
+ // inhibit is set, then the initial value is 0, otherwise the
+ // initial value is n+1.
+ size_t inhibit_any_policy_;
+
+ // |policy_mapping_| corresponds with the same named variable from RFC 5280
+ // section 6.1.2:
+ //
+ // policy_mapping: an integer that indicates if policy mapping
+ // is permitted. The integer indicates the number of non-self-
+ // issued certificates to be processed before policy mapping is
+ // inhibited. Once set, this variable may be decreased, but may
+ // not be increased. That is, if a certificate in the path
+ // specifies that policy mapping is not permitted, it cannot be
+ // overridden by a later certificate. If initial-policy-
+ // mapping-inhibit is set, then the initial value is 0,
+ // otherwise the initial value is n+1.
+ size_t policy_mapping_;
+
+ // |working_public_key_| is an amalgamation of 3 separate variables from RFC
+ // 5280:
+ // * working_public_key
+ // * working_public_key_algorithm
+ // * working_public_key_parameters
+ //
+ // They are combined for simplicity since the signature verification takes an
+ // EVP_PKEY, and the parameter inheritence is not applicable for the supported
+ // key types. |working_public_key_| may be null if parsing failed.
+ //
+ // An approximate explanation of |working_public_key_| is this description
+ // from RFC 5280 section 6.1.2:
+ //
+ // working_public_key: the public key used to verify the
+ // signature of a certificate.
+ bssl::UniquePtr<EVP_PKEY> working_public_key_;
+
+ // |working_normalized_issuer_name_| is the normalized value of the
+ // working_issuer_name variable in RFC 5280 section 6.1.2:
+ //
+ // working_issuer_name: the issuer distinguished name expected
+ // in the next certificate in the chain.
+ der::Input working_normalized_issuer_name_;
+
+ // |max_path_length_| corresponds with the same named variable in RFC 5280
+ // section 6.1.2.
+ //
+ // max_path_length: this integer is initialized to n, is
+ // decremented for each non-self-issued certificate in the path,
+ // and may be reduced to the value in the path length constraint
+ // field within the basic constraints extension of a CA
+ // certificate.
+ size_t max_path_length_;
+
+ raw_ptr<VerifyCertificateChainDelegate> delegate_;
+};
+
+void PathVerifier::VerifyPolicies(const ParsedCertificate& cert,
+ bool is_target_cert,
+ CertErrors* errors) {
+ // From RFC 5280 section 6.1.3:
+ //
+ // (d) If the certificate policies extension is present in the
+ // certificate and the valid_policy_tree is not NULL, process
+ // the policy information by performing the following steps in
+ // order:
+ if (cert.has_policy_oids() && !valid_policy_tree_.IsNull()) {
+ ValidPolicyTree::Level previous_level = valid_policy_tree_.StartLevel();
+
+ // Identify if there was a node with valid_policy == anyPolicy at depth i-1.
+ const ValidPolicyTree::Node* any_policy_node_prev_level =
+ ValidPolicyTree::FindAnyPolicyNode(previous_level);
+
+ // (1) For each policy P not equal to anyPolicy in the
+ // certificate policies extension, let P-OID denote the OID
+ // for policy P and P-Q denote the qualifier set for policy
+ // P. Perform the following steps in order:
+ bool cert_has_any_policy = false;
+ for (const der::Input& p_oid : cert.policy_oids()) {
+ if (p_oid == der::Input(kAnyPolicyOid)) {
+ cert_has_any_policy = true;
+ continue;
+ }
+
+ // (i) For each node of depth i-1 in the valid_policy_tree
+ // where P-OID is in the expected_policy_set, create a
+ // child node as follows: set the valid_policy to P-OID,
+ // set the qualifier_set to P-Q, and set the
+ // expected_policy_set to {P-OID}.
+ bool found_match = false;
+ for (const ValidPolicyTree::Node& prev_node : previous_level) {
+ if (SetContains(prev_node.expected_policy_set, p_oid)) {
+ valid_policy_tree_.AddNode(prev_node, p_oid);
+ found_match = true;
+ }
+ }
+
+ // (ii) If there was no match in step (i) and the
+ // valid_policy_tree includes a node of depth i-1 with
+ // the valid_policy anyPolicy, generate a child node with
+ // the following values: set the valid_policy to P-OID,
+ // set the qualifier_set to P-Q, and set the
+ // expected_policy_set to {P-OID}.
+ if (!found_match && any_policy_node_prev_level)
+ valid_policy_tree_.AddNode(*any_policy_node_prev_level, p_oid);
+ }
+
+ // (2) If the certificate policies extension includes the policy
+ // anyPolicy with the qualifier set AP-Q and either (a)
+ // inhibit_anyPolicy is greater than 0 or (b) i<n and the
+ // certificate is self-issued, then:
+ //
+ // For each node in the valid_policy_tree of depth i-1, for
+ // each value in the expected_policy_set (including
+ // anyPolicy) that does not appear in a child node, create a
+ // child node with the following values: set the valid_policy
+ // to the value from the expected_policy_set in the parent
+ // node, set the qualifier_set to AP-Q, and set the
+ // expected_policy_set to the value in the valid_policy from
+ // this node.
+ if (cert_has_any_policy && ((inhibit_any_policy_ > 0) ||
+ (!is_target_cert && IsSelfIssued(cert)))) {
+ // Keep track of the existing policies at depth i.
+ std::set<der::Input> child_node_policies;
+ for (const ValidPolicyTree::Node& node :
+ valid_policy_tree_.current_level())
+ child_node_policies.insert(node.valid_policy);
+
+ for (const ValidPolicyTree::Node& prev_node : previous_level) {
+ for (const der::Input& expected_policy :
+ prev_node.expected_policy_set) {
+ if (!SetContains(child_node_policies, expected_policy)) {
+ child_node_policies.insert(expected_policy);
+ valid_policy_tree_.AddNode(prev_node, expected_policy);
+ }
+ }
+ }
+ }
+
+ // (3) If there is a node in the valid_policy_tree of depth i-1
+ // or less without any child nodes, delete that node. Repeat
+ // this step until there are no nodes of depth i-1 or less
+ // without children.
+ //
+ // Nothing needs to be done for this step, since this implementation only
+ // stores the nodes at depth i, and the entire level has already been
+ // calculated.
+ }
+
+ // (e) If the certificate policies extension is not present, set the
+ // valid_policy_tree to NULL.
+ if (!cert.has_policy_oids())
+ valid_policy_tree_.SetNull();
+
+ // (f) Verify that either explicit_policy is greater than 0 or the
+ // valid_policy_tree is not equal to NULL;
+ if (!((explicit_policy_ > 0) || !valid_policy_tree_.IsNull()))
+ errors->AddError(cert_errors::kNoValidPolicy);
+}
+
+void PathVerifier::VerifyPolicyMappings(const ParsedCertificate& cert,
+ CertErrors* errors) {
+ if (!cert.has_policy_mappings())
+ return;
+
+ // From RFC 5280 section 6.1.4:
+ //
+ // (a) If a policy mappings extension is present, verify that the
+ // special value anyPolicy does not appear as an
+ // issuerDomainPolicy or a subjectDomainPolicy.
+ for (const ParsedPolicyMapping& mapping : cert.policy_mappings()) {
+ if (mapping.issuer_domain_policy == der::Input(kAnyPolicyOid) ||
+ mapping.subject_domain_policy == der::Input(kAnyPolicyOid)) {
+ // Because this implementation continues processing certificates after
+ // this error, clear the valid policy tree to ensure the
+ // "user_constrained_policy_set" output upon failure is empty.
+ valid_policy_tree_.SetNull();
+ errors->AddError(cert_errors::kPolicyMappingAnyPolicy);
+ }
+ }
+
+ // (b) If a policy mappings extension is present, then for each
+ // issuerDomainPolicy ID-P in the policy mappings extension:
+ //
+ // (1) If the policy_mapping variable is greater than 0, for each
+ // node in the valid_policy_tree of depth i where ID-P is the
+ // valid_policy, set expected_policy_set to the set of
+ // subjectDomainPolicy values that are specified as
+ // equivalent to ID-P by the policy mappings extension.
+ //
+ // If no node of depth i in the valid_policy_tree has a
+ // valid_policy of ID-P but there is a node of depth i with a
+ // valid_policy of anyPolicy, then generate a child node of
+ // the node of depth i-1 that has a valid_policy of anyPolicy
+ // as follows:
+ //
+ // (i) set the valid_policy to ID-P;
+ //
+ // (ii) set the qualifier_set to the qualifier set of the
+ // policy anyPolicy in the certificate policies
+ // extension of certificate i; and
+ //
+ // (iii) set the expected_policy_set to the set of
+ // subjectDomainPolicy values that are specified as
+ // equivalent to ID-P by the policy mappings extension.
+ //
+ if (policy_mapping_ > 0) {
+ const ValidPolicyTree::Node* any_policy_node =
+ ValidPolicyTree::FindAnyPolicyNode(valid_policy_tree_.current_level());
+
+ // Group mappings by issuer domain policy.
+ std::map<der::Input, std::set<der::Input>> mappings;
+ for (const ParsedPolicyMapping& mapping : cert.policy_mappings()) {
+ mappings[mapping.issuer_domain_policy].insert(
+ mapping.subject_domain_policy);
+ }
+
+ for (const auto& it : mappings) {
+ const der::Input& issuer_domain_policy = it.first;
+ const std::set<der::Input>& subject_domain_policies = it.second;
+ bool found_node = false;
+
+ for (ValidPolicyTree::Node& node : valid_policy_tree_.current_level()) {
+ if (node.valid_policy == issuer_domain_policy) {
+ node.expected_policy_set = subject_domain_policies;
+ found_node = true;
+ }
+ }
+
+ if (!found_node && any_policy_node) {
+ valid_policy_tree_.AddNodeWithExpectedPolicySet(
+ *any_policy_node, issuer_domain_policy, subject_domain_policies);
+ }
+ }
+ }
+
+ // (b) If a policy mappings extension is present, then for each
+ // issuerDomainPolicy ID-P in the policy mappings extension:
+ //
+ // ...
+ //
+ // (2) If the policy_mapping variable is equal to 0:
+ //
+ // (i) delete each node of depth i in the valid_policy_tree
+ // where ID-P is the valid_policy.
+ //
+ // (ii) If there is a node in the valid_policy_tree of depth
+ // i-1 or less without any child nodes, delete that
+ // node. Repeat this step until there are no nodes of
+ // depth i-1 or less without children.
+ if (policy_mapping_ == 0) {
+ for (const ParsedPolicyMapping& mapping : cert.policy_mappings()) {
+ ValidPolicyTree::DeleteNodesMatchingValidPolicy(
+ mapping.issuer_domain_policy, &valid_policy_tree_.current_level());
+ }
+ }
+}
+
+void PathVerifier::BasicCertificateProcessing(
+ const ParsedCertificate& cert,
+ bool is_target_cert,
+ const der::GeneralizedTime& time,
+ KeyPurpose required_key_purpose,
+ CertErrors* errors,
+ bool* shortcircuit_chain_validation) {
+ *shortcircuit_chain_validation = false;
+ // Check that the signature algorithms in Certificate vs TBSCertificate
+ // match. This isn't part of RFC 5280 section 6.1.3, but is mandated by
+ // sections 4.1.1.2 and 4.1.2.3.
+ if (!VerifySignatureAlgorithmsMatch(cert, errors)) {
+ CHECK(errors->ContainsAnyErrorWithSeverity(CertError::SEVERITY_HIGH));
+ *shortcircuit_chain_validation = true;
+ }
+
+ // Check whether this signature algorithm is allowed.
+ if (!delegate_->IsSignatureAlgorithmAcceptable(cert.signature_algorithm(),
+ errors)) {
+ *shortcircuit_chain_validation = true;
+ errors->AddError(cert_errors::kUnacceptableSignatureAlgorithm);
+ }
+
+ if (working_public_key_) {
+ // Verify the digital signature using the previous certificate's key (RFC
+ // 5280 section 6.1.3 step a.1).
+ if (!VerifySignedData(cert.signature_algorithm(),
+ cert.tbs_certificate_tlv(), cert.signature_value(),
+ working_public_key_.get())) {
+ *shortcircuit_chain_validation = true;
+ errors->AddError(cert_errors::kVerifySignedDataFailed);
+ }
+ }
+ if (*shortcircuit_chain_validation)
+ return;
+
+ // Check the time range for the certificate's validity, ensuring it is valid
+ // at |time|.
+ // (RFC 5280 section 6.1.3 step a.2)
+ VerifyTimeValidity(cert, time, errors);
+
+ // RFC 5280 section 6.1.3 step a.3 calls for checking the certificate's
+ // revocation status here. In this implementation revocation checking is
+ // implemented separately from path validation.
+
+ // Verify the certificate's issuer name matches the issuing certificate's
+ // subject name. (RFC 5280 section 6.1.3 step a.4)
+ if (cert.normalized_issuer() != working_normalized_issuer_name_)
+ errors->AddError(cert_errors::kSubjectDoesNotMatchIssuer);
+
+ // Name constraints (RFC 5280 section 6.1.3 step b & c)
+ // If certificate i is self-issued and it is not the final certificate in the
+ // path, skip this step for certificate i.
+ if (!name_constraints_list_.empty() &&
+ (!IsSelfIssued(cert) || is_target_cert)) {
+ for (const NameConstraints* nc : name_constraints_list_) {
+ nc->IsPermittedCert(cert.normalized_subject(), cert.subject_alt_names(),
+ errors);
+ }
+ }
+
+ // RFC 5280 section 6.1.3 step d - f.
+ VerifyPolicies(cert, is_target_cert, errors);
+
+ // The key purpose is checked not just for the end-entity certificate, but
+ // also interpreted as a constraint when it appears in intermediates. This
+ // goes beyond what RFC 5280 describes, but is the de-facto standard. See
+ // https://wiki.mozilla.org/CA:CertificatePolicyV2.1#Frequently_Asked_Questions
+ VerifyExtendedKeyUsage(cert, required_key_purpose, errors);
+}
+
+void PathVerifier::PrepareForNextCertificate(const ParsedCertificate& cert,
+ CertErrors* errors) {
+ // RFC 5280 section 6.1.4 step a-b
+ VerifyPolicyMappings(cert, errors);
+
+ // From RFC 5280 section 6.1.4 step c:
+ //
+ // Assign the certificate subject name to working_normalized_issuer_name.
+ working_normalized_issuer_name_ = cert.normalized_subject();
+
+ // From RFC 5280 section 6.1.4 step d:
+ //
+ // Assign the certificate subjectPublicKey to working_public_key.
+ working_public_key_ = ParseAndCheckPublicKey(cert.tbs().spki_tlv, errors);
+
+ // Note that steps e and f are omitted as they are handled by
+ // the assignment to |working_spki| above. See the definition
+ // of |working_spki|.
+
+ // From RFC 5280 section 6.1.4 step g:
+ if (cert.has_name_constraints())
+ name_constraints_list_.push_back(&cert.name_constraints());
+
+ // (h) If certificate i is not self-issued:
+ if (!IsSelfIssued(cert)) {
+ // (1) If explicit_policy is not 0, decrement explicit_policy by
+ // 1.
+ if (explicit_policy_ > 0)
+ explicit_policy_ -= 1;
+
+ // (2) If policy_mapping is not 0, decrement policy_mapping by 1.
+ if (policy_mapping_ > 0)
+ policy_mapping_ -= 1;
+
+ // (3) If inhibit_anyPolicy is not 0, decrement inhibit_anyPolicy
+ // by 1.
+ if (inhibit_any_policy_ > 0)
+ inhibit_any_policy_ -= 1;
+ }
+
+ // (i) If a policy constraints extension is included in the
+ // certificate, modify the explicit_policy and policy_mapping
+ // state variables as follows:
+ if (cert.has_policy_constraints()) {
+ // (1) If requireExplicitPolicy is present and is less than
+ // explicit_policy, set explicit_policy to the value of
+ // requireExplicitPolicy.
+ if (cert.policy_constraints().require_explicit_policy &&
+ cert.policy_constraints().require_explicit_policy.value() <
+ explicit_policy_) {
+ explicit_policy_ =
+ cert.policy_constraints().require_explicit_policy.value();
+ }
+
+ // (2) If inhibitPolicyMapping is present and is less than
+ // policy_mapping, set policy_mapping to the value of
+ // inhibitPolicyMapping.
+ if (cert.policy_constraints().inhibit_policy_mapping &&
+ cert.policy_constraints().inhibit_policy_mapping.value() <
+ policy_mapping_) {
+ policy_mapping_ =
+ cert.policy_constraints().inhibit_policy_mapping.value();
+ }
+ }
+
+ // (j) If the inhibitAnyPolicy extension is included in the
+ // certificate and is less than inhibit_anyPolicy, set
+ // inhibit_anyPolicy to the value of inhibitAnyPolicy.
+ if (cert.has_inhibit_any_policy() &&
+ cert.inhibit_any_policy() < inhibit_any_policy_) {
+ inhibit_any_policy_ = cert.inhibit_any_policy();
+ }
+
+ // From RFC 5280 section 6.1.4 step k:
+ //
+ // If certificate i is a version 3 certificate, verify that the
+ // basicConstraints extension is present and that cA is set to
+ // TRUE. (If certificate i is a version 1 or version 2
+ // certificate, then the application MUST either verify that
+ // certificate i is a CA certificate through out-of-band means
+ // or reject the certificate. Conforming implementations may
+ // choose to reject all version 1 and version 2 intermediate
+ // certificates.)
+ //
+ // This code implicitly rejects non version 3 intermediates, since they
+ // can't contain a BasicConstraints extension.
+ if (!cert.has_basic_constraints()) {
+ errors->AddError(cert_errors::kMissingBasicConstraints);
+ } else if (!cert.basic_constraints().is_ca) {
+ errors->AddError(cert_errors::kBasicConstraintsIndicatesNotCa);
+ }
+
+ // From RFC 5280 section 6.1.4 step l:
+ //
+ // If the certificate was not self-issued, verify that
+ // max_path_length is greater than zero and decrement
+ // max_path_length by 1.
+ if (!IsSelfIssued(cert)) {
+ if (max_path_length_ == 0) {
+ errors->AddError(cert_errors::kMaxPathLengthViolated);
+ } else {
+ --max_path_length_;
+ }
+ }
+
+ // From RFC 5280 section 6.1.4 step m:
+ //
+ // If pathLenConstraint is present in the certificate and is
+ // less than max_path_length, set max_path_length to the value
+ // of pathLenConstraint.
+ if (cert.has_basic_constraints() && cert.basic_constraints().has_path_len &&
+ cert.basic_constraints().path_len < max_path_length_) {
+ max_path_length_ = cert.basic_constraints().path_len;
+ }
+
+ // From RFC 5280 section 6.1.4 step n:
+ //
+ // If a key usage extension is present, verify that the
+ // keyCertSign bit is set.
+ if (cert.has_key_usage() &&
+ !cert.key_usage().AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)) {
+ errors->AddError(cert_errors::kKeyCertSignBitNotSet);
+ }
+
+ // From RFC 5280 section 6.1.4 step o:
+ //
+ // Recognize and process any other critical extension present in
+ // the certificate. Process any other recognized non-critical
+ // extension present in the certificate that is relevant to path
+ // processing.
+ VerifyNoUnconsumedCriticalExtensions(cert, errors);
+}
+
+// Checks that if the target certificate has properties that only a CA should
+// have (keyCertSign, CA=true, pathLenConstraint), then its other properties
+// are consistent with being a CA. If it does, adds errors to |errors|.
+//
+// This follows from some requirements in RFC 5280 section 4.2.1.9. In
+// particular:
+//
+// CAs MUST NOT include the pathLenConstraint field unless the cA
+// boolean is asserted and the key usage extension asserts the
+// keyCertSign bit.
+//
+// And:
+//
+// If the cA boolean is not asserted, then the keyCertSign bit in the key
+// usage extension MUST NOT be asserted.
+//
+// TODO(eroman): Strictly speaking the first requirement is on CAs and not the
+// certificate client, so could be skipped.
+//
+// TODO(eroman): I don't believe Firefox enforces the keyCertSign restriction
+// for compatibility reasons. Investigate if we need to similarly relax this
+// constraint.
+void VerifyTargetCertHasConsistentCaBits(const ParsedCertificate& cert,
+ CertErrors* errors) {
+ // Check if the certificate contains any property specific to CAs.
+ bool has_ca_property =
+ (cert.has_basic_constraints() &&
+ (cert.basic_constraints().is_ca ||
+ cert.basic_constraints().has_path_len)) ||
+ (cert.has_key_usage() &&
+ cert.key_usage().AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
+
+ // If it "looks" like a CA because it has a CA-only property, then check that
+ // it sets ALL the properties expected of a CA.
+ if (has_ca_property) {
+ bool success = cert.has_basic_constraints() &&
+ cert.basic_constraints().is_ca &&
+ (!cert.has_key_usage() ||
+ cert.key_usage().AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
+ if (!success) {
+ // TODO(eroman): Add DER for basic constraints and key usage.
+ errors->AddError(cert_errors::kTargetCertInconsistentCaBits);
+ }
+ }
+}
+
+void PathVerifier::WrapUp(const ParsedCertificate& cert, CertErrors* errors) {
+ // From RFC 5280 section 6.1.5:
+ // (a) If explicit_policy is not 0, decrement explicit_policy by 1.
+ if (explicit_policy_ > 0)
+ explicit_policy_ -= 1;
+
+ // (b) If a policy constraints extension is included in the
+ // certificate and requireExplicitPolicy is present and has a
+ // value of 0, set the explicit_policy state variable to 0.
+ if (cert.has_policy_constraints() &&
+ cert.policy_constraints().require_explicit_policy.has_value() &&
+ cert.policy_constraints().require_explicit_policy == 0) {
+ explicit_policy_ = 0;
+ }
+
+ // Note step c-e are omitted as the verification function does
+ // not output the working public key.
+
+ // From RFC 5280 section 6.1.5 step f:
+ //
+ // Recognize and process any other critical extension present in
+ // the certificate n. Process any other recognized non-critical
+ // extension present in certificate n that is relevant to path
+ // processing.
+ //
+ // Note that this is duplicated by PrepareForNextCertificate() so as to
+ // directly match the procedures in RFC 5280's section 6.1.
+ VerifyNoUnconsumedCriticalExtensions(cert, errors);
+
+ // RFC 5280 section 6.1.5 step g is skipped, as the intersection of valid
+ // policies was computed during previous steps.
+ //
+ // If either (1) the value of explicit_policy variable is greater than
+ // zero or (2) the valid_policy_tree is not NULL, then path processing
+ // has succeeded.
+ if (!(explicit_policy_ > 0 || !valid_policy_tree_.IsNull())) {
+ errors->AddError(cert_errors::kNoValidPolicy);
+ }
+
+ // The following check is NOT part of RFC 5280 6.1.5's "Wrap-Up Procedure",
+ // however is implied by RFC 5280 section 4.2.1.9.
+ VerifyTargetCertHasConsistentCaBits(cert, errors);
+
+ // Check the public key for the target certificate. The public key for the
+ // other certificates is already checked by PrepareForNextCertificate().
+ // Note that this step is not part of RFC 5280 6.1.5.
+ ParseAndCheckPublicKey(cert.tbs().spki_tlv, errors);
+}
+
+void PathVerifier::ApplyTrustAnchorConstraints(const ParsedCertificate& cert,
+ KeyPurpose required_key_purpose,
+ CertErrors* errors) {
+ // This is not part of RFC 5937 nor RFC 5280, but matches the EKU handling
+ // done for intermediates (described in Web PKI's Baseline Requirements).
+ VerifyExtendedKeyUsage(cert, required_key_purpose, errors);
+
+ // The following enforcements follow from RFC 5937 (primarily section 3.2):
+
+ // Initialize name constraints initial-permitted/excluded-subtrees.
+ if (cert.has_name_constraints())
+ name_constraints_list_.push_back(&cert.name_constraints());
+
+ // TODO(eroman): Initialize user-initial-policy-set based on anchor
+ // constraints.
+
+ // TODO(eroman): Initialize inhibit any policy based on anchor constraints.
+
+ // TODO(eroman): Initialize require explicit policy based on anchor
+ // constraints.
+
+ // TODO(eroman): Initialize inhibit policy mapping based on anchor
+ // constraints.
+
+ // From RFC 5937 section 3.2:
+ //
+ // If a basic constraints extension is associated with the trust
+ // anchor and contains a pathLenConstraint value, set the
+ // max_path_length state variable equal to the pathLenConstraint
+ // value from the basic constraints extension.
+ //
+ // NOTE: RFC 5937 does not say to enforce the CA=true part of basic
+ // constraints.
+ if (cert.has_basic_constraints() && cert.basic_constraints().has_path_len)
+ max_path_length_ = cert.basic_constraints().path_len;
+
+ // From RFC 5937 section 2:
+ //
+ // Extensions may be marked critical or not critical. When trust anchor
+ // constraints are enforced, clients MUST reject certification paths
+ // containing a trust anchor with unrecognized critical extensions.
+ VerifyNoUnconsumedCriticalExtensions(cert, errors);
+}
+
+void PathVerifier::ProcessRootCertificate(const ParsedCertificate& cert,
+ const CertificateTrust& trust,
+ const der::GeneralizedTime& time,
+ KeyPurpose required_key_purpose,
+ CertErrors* errors,
+ bool* shortcircuit_chain_validation) {
+ *shortcircuit_chain_validation = false;
+ switch (trust.type) {
+ case CertificateTrustType::UNSPECIFIED:
+ // Doesn't chain to a trust anchor - implicitly distrusted
+ errors->AddError(cert_errors::kCertIsNotTrustAnchor);
+ *shortcircuit_chain_validation = true;
+ break;
+ case CertificateTrustType::DISTRUSTED:
+ // Chains to an actively distrusted certificate.
+ errors->AddError(cert_errors::kDistrustedByTrustStore);
+ *shortcircuit_chain_validation = true;
+ break;
+ case CertificateTrustType::TRUSTED_ANCHOR:
+ break;
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_EXPIRATION:
+ VerifyTimeValidity(cert, time, errors);
+ break;
+ case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
+ ApplyTrustAnchorConstraints(cert, required_key_purpose, errors);
+ break;
+ }
+ if (*shortcircuit_chain_validation)
+ return;
+
+ // Use the certificate's SPKI and subject when verifying the next certificate.
+ working_public_key_ = ParseAndCheckPublicKey(cert.tbs().spki_tlv, errors);
+ working_normalized_issuer_name_ = cert.normalized_subject();
+}
+
+bssl::UniquePtr<EVP_PKEY> PathVerifier::ParseAndCheckPublicKey(
+ const der::Input& spki,
+ CertErrors* errors) {
+ // Parse the public key.
+ bssl::UniquePtr<EVP_PKEY> pkey;
+ if (!ParsePublicKey(spki, &pkey)) {
+ errors->AddError(cert_errors::kFailedParsingSpki);
+ return nullptr;
+ }
+
+ // Check if the key is acceptable by the delegate.
+ if (!delegate_->IsPublicKeyAcceptable(pkey.get(), errors))
+ errors->AddError(cert_errors::kUnacceptablePublicKey);
+
+ return pkey;
+}
+
+void PathVerifier::Run(
+ const ParsedCertificateList& certs,
+ const CertificateTrust& last_cert_trust,
+ VerifyCertificateChainDelegate* delegate,
+ const der::GeneralizedTime& time,
+ KeyPurpose required_key_purpose,
+ InitialExplicitPolicy initial_explicit_policy,
+ const std::set<der::Input>& user_initial_policy_set,
+ InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
+ InitialAnyPolicyInhibit initial_any_policy_inhibit,
+ std::set<der::Input>* user_constrained_policy_set,
+ CertPathErrors* errors) {
+ // This implementation is structured to mimic the description of certificate
+ // path verification given by RFC 5280 section 6.1.
+ DCHECK(delegate);
+ DCHECK(errors);
+
+ delegate_ = delegate;
+
+ // An empty chain is necessarily invalid.
+ if (certs.empty()) {
+ errors->GetOtherErrors()->AddError(cert_errors::kChainIsEmpty);
+ return;
+ }
+
+ // Verifying a trusted leaf certificate is not permitted. (It isn't a
+ // well-specified operation.) See https://crbug.com/814994.
+ if (certs.size() == 1) {
+ errors->GetOtherErrors()->AddError(cert_errors::kChainIsLength1);
+ return;
+ }
+
+ // RFC 5280's "n" variable is the length of the path, which does not count
+ // the trust anchor. (Although in practice it doesn't really change behaviors
+ // if n is used in place of n+1).
+ const size_t n = certs.size() - 1;
+
+ valid_policy_tree_.Init(user_initial_policy_set);
+
+ // RFC 5280 section section 6.1.2:
+ //
+ // If initial-explicit-policy is set, then the initial value
+ // [of explicit_policy] is 0, otherwise the initial value is n+1.
+ explicit_policy_ =
+ initial_explicit_policy == InitialExplicitPolicy::kTrue ? 0 : n + 1;
+
+ // RFC 5280 section section 6.1.2:
+ //
+ // If initial-any-policy-inhibit is set, then the initial value
+ // [of inhibit_anyPolicy] is 0, otherwise the initial value is n+1.
+ inhibit_any_policy_ =
+ initial_any_policy_inhibit == InitialAnyPolicyInhibit::kTrue ? 0 : n + 1;
+
+ // RFC 5280 section section 6.1.2:
+ //
+ // If initial-policy-mapping-inhibit is set, then the initial value
+ // [of policy_mapping] is 0, otherwise the initial value is n+1.
+ policy_mapping_ =
+ initial_policy_mapping_inhibit == InitialPolicyMappingInhibit::kTrue
+ ? 0
+ : n + 1;
+
+ // RFC 5280 section section 6.1.2:
+ //
+ // max_path_length: this integer is initialized to n, ...
+ max_path_length_ = n;
+
+ // Iterate over all the certificates in the reverse direction: starting from
+ // the root certificate and progressing towards the target certificate.
+ //
+ // * i=0 : Root certificate (i.e. trust anchor)
+ // * i=1 : Certificate issued by root
+ // * i=x : Certificate i=x is issued by certificate i=x-1
+ // * i=n : Target certificate.
+ for (size_t i = 0; i < certs.size(); ++i) {
+ const size_t index_into_certs = certs.size() - i - 1;
+
+ // |is_target_cert| is true if the current certificate is the target
+ // certificate being verified. The target certificate isn't necessarily an
+ // end-entity certificate.
+ const bool is_target_cert = index_into_certs == 0;
+ const bool is_root_cert = i == 0;
+
+ const ParsedCertificate& cert = *certs[index_into_certs];
+
+ // Output errors for the current certificate into an error bucket that is
+ // associated with that certificate.
+ CertErrors* cert_errors = errors->GetErrorsForCert(index_into_certs);
+
+ if (is_root_cert) {
+ bool shortcircuit_chain_validation = false;
+ ProcessRootCertificate(cert, last_cert_trust, time, required_key_purpose,
+ cert_errors, &shortcircuit_chain_validation);
+ if (shortcircuit_chain_validation) {
+ // Chains that don't start from a trusted root should short-circuit the
+ // rest of the verification, as accumulating more errors from untrusted
+ // certificates would not be meaningful.
+ CHECK(cert_errors->ContainsAnyErrorWithSeverity(
+ CertError::SEVERITY_HIGH));
+ return;
+ }
+
+ // Don't do any other checks for root certificates.
+ continue;
+ }
+
+ bool shortcircuit_chain_validation = false;
+ // Per RFC 5280 section 6.1:
+ // * Do basic processing for each certificate
+ // * If it is the last certificate in the path (target certificate)
+ // - Then run "Wrap up"
+ // - Otherwise run "Prepare for Next cert"
+ BasicCertificateProcessing(cert, is_target_cert, time, required_key_purpose,
+ cert_errors, &shortcircuit_chain_validation);
+ if (shortcircuit_chain_validation) {
+ // Signature errors should short-circuit the rest of the verification, as
+ // accumulating more errors from untrusted certificates would not be
+ // meaningful.
+ CHECK(
+ cert_errors->ContainsAnyErrorWithSeverity(CertError::SEVERITY_HIGH));
+ return;
+ }
+ if (!is_target_cert) {
+ PrepareForNextCertificate(cert, cert_errors);
+ } else {
+ WrapUp(cert, cert_errors);
+ }
+ }
+
+ if (user_constrained_policy_set) {
+ // valid_policy_tree_ already contains the intersection of valid policies
+ // with user_initial_policy_set.
+ valid_policy_tree_.GetValidRootPolicySet(user_constrained_policy_set);
+ }
+
+ // TODO(eroman): RFC 5280 forbids duplicate certificates per section 6.1:
+ //
+ // A certificate MUST NOT appear more than once in a prospective
+ // certification path.
+}
+
+} // namespace
+
+VerifyCertificateChainDelegate::~VerifyCertificateChainDelegate() = default;
+
+void VerifyCertificateChain(
+ const ParsedCertificateList& certs,
+ const CertificateTrust& last_cert_trust,
+ VerifyCertificateChainDelegate* delegate,
+ const der::GeneralizedTime& time,
+ KeyPurpose required_key_purpose,
+ InitialExplicitPolicy initial_explicit_policy,
+ const std::set<der::Input>& user_initial_policy_set,
+ InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
+ InitialAnyPolicyInhibit initial_any_policy_inhibit,
+ std::set<der::Input>* user_constrained_policy_set,
+ CertPathErrors* errors) {
+ PathVerifier verifier;
+ verifier.Run(certs, last_cert_trust, delegate, time, required_key_purpose,
+ initial_explicit_policy, user_initial_policy_set,
+ initial_policy_mapping_inhibit, initial_any_policy_inhibit,
+ user_constrained_policy_set, errors);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/verify_certificate_chain.h b/chromium/net/cert/pki/verify_certificate_chain.h
new file mode 100644
index 00000000000..3dd187e6ff2
--- /dev/null
+++ b/chromium/net/cert/pki/verify_certificate_chain.h
@@ -0,0 +1,248 @@
+// Copyright 2015 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 NET_CERT_PKI_VERIFY_CERTIFICATE_CHAIN_H_
+#define NET_CERT_PKI_VERIFY_CERTIFICATE_CHAIN_H_
+
+#include <set>
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/der/input.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+
+namespace net {
+
+namespace der {
+struct GeneralizedTime;
+}
+
+struct CertificateTrust;
+
+// The key purpose (extended key usage) to check for during verification.
+enum class KeyPurpose {
+ ANY_EKU,
+ SERVER_AUTH,
+ CLIENT_AUTH,
+};
+
+enum class InitialExplicitPolicy {
+ kFalse,
+ kTrue,
+};
+
+enum class InitialPolicyMappingInhibit {
+ kFalse,
+ kTrue,
+};
+
+enum class InitialAnyPolicyInhibit {
+ kFalse,
+ kTrue,
+};
+
+// VerifyCertificateChainDelegate exposes delegate methods used when verifying a
+// chain.
+class NET_EXPORT VerifyCertificateChainDelegate {
+ public:
+ // Implementations should return true if |signature_algorithm| is allowed for
+ // certificate signing, false otherwise. When returning false implementations
+ // can optionally add high-severity errors to |errors| with details on why it
+ // was rejected.
+ virtual bool IsSignatureAlgorithmAcceptable(
+ SignatureAlgorithm signature_algorithm,
+ CertErrors* errors) = 0;
+
+ // Implementations should return true if |public_key| is acceptable. This is
+ // called for each certificate in the chain, including the target certificate.
+ // When returning false implementations can optionally add high-severity
+ // errors to |errors| with details on why it was rejected.
+ //
+ // |public_key| can be assumed to be non-null.
+ virtual bool IsPublicKeyAcceptable(EVP_PKEY* public_key,
+ CertErrors* errors) = 0;
+
+ virtual ~VerifyCertificateChainDelegate();
+};
+
+// VerifyCertificateChain() verifies an ordered certificate path in accordance
+// with RFC 5280's "Certification Path Validation" algorithm (section 6).
+//
+// -----------------------------------------
+// Deviations from RFC 5280
+// -----------------------------------------
+//
+// * If Extended Key Usage appears on intermediates, it is treated as
+// a restriction on subordinate certificates.
+// * No revocation checking is performed.
+//
+// -----------------------------------------
+// Additional responsibilities of the caller
+// -----------------------------------------
+//
+// After successful path verification, the caller is responsible for
+// subsequently checking:
+//
+// * The end-entity's KeyUsage before using its SPKI.
+// * The end-entity's name/subjectAltName. Name constraints from intermediates
+// will have already been applied, so it is sufficient to check the
+// end-entity for a match. The caller MUST NOT check hostnames on the
+// commonName field because this implementation does not apply dnsName
+// constraints on commonName.
+//
+// ---------
+// Inputs
+// ---------
+//
+// certs:
+// A non-empty chain of DER-encoded certificates, listed in the
+// "forward" direction. The first certificate is the target
+// certificate to verify, and the last certificate has trustedness
+// given by |last_cert_trust| (generally a trust anchor).
+//
+// * certs[0] is the target certificate to verify.
+// * certs[i+1] holds the certificate that issued cert_chain[i].
+// * certs[N-1] the root certificate
+//
+// Note that THIS IS NOT identical in meaning to the same named
+// "certs" input defined in RFC 5280 section 6.1.1.a. The differences
+// are:
+//
+// * The order of certificates is reversed
+// * In RFC 5280 "certs" DOES NOT include the trust anchor
+//
+// last_cert_trust:
+// Trustedness of |certs.back()|. The trustedness of |certs.back()|
+// MUST BE decided by the caller -- this function takes it purely as
+// an input. Moreover, the CertificateTrust can be used to specify
+// trust anchor constraints.
+//
+// This combined with |certs.back()| (the root certificate) fills a
+// similar role to "trust anchor information" defined in RFC 5280
+// section 6.1.1.d.
+//
+// delegate:
+// |delegate| must be non-null. It is used to answer policy questions such
+// as whether a signature algorithm is acceptable, or a public key is strong
+// enough.
+//
+// time:
+// The UTC time to use for expiration checks. This is equivalent to
+// the input from RFC 5280 section 6.1.1:
+//
+// (b) the current date/time.
+//
+// required_key_purpose:
+// The key purpose that the target certificate needs to be valid for.
+//
+// user_initial_policy_set:
+// This is equivalent to the same named input in RFC 5280 section
+// 6.1.1:
+//
+// (c) user-initial-policy-set: A set of certificate policy
+// identifiers naming the policies that are acceptable to the
+// certificate user. The user-initial-policy-set contains the
+// special value any-policy if the user is not concerned about
+// certificate policy.
+//
+// initial_policy_mapping_inhibit:
+// This is equivalent to the same named input in RFC 5280 section
+// 6.1.1:
+//
+// (e) initial-policy-mapping-inhibit, which indicates if policy
+// mapping is allowed in the certification path.
+//
+// initial_explicit_policy:
+// This is equivalent to the same named input in RFC 5280 section
+// 6.1.1:
+//
+// (f) initial-explicit-policy, which indicates if the path must be
+// valid for at least one of the certificate policies in the
+// user-initial-policy-set.
+//
+// initial_any_policy_inhibit:
+// This is equivalent to the same named input in RFC 5280 section
+// 6.1.1:
+//
+// (g) initial-any-policy-inhibit, which indicates whether the
+// anyPolicy OID should be processed if it is included in a
+// certificate.
+//
+// ---------
+// Outputs
+// ---------
+//
+// user_constrained_policy_set:
+// Can be null. If non-null, |user_constrained_policy_set| will be filled
+// with the matching policies (intersected with user_initial_policy_set).
+// This is equivalent to the same named output in X.509 section 10.2.
+// Note that it is OK for this to point to input user_initial_policy_set.
+//
+// errors:
+// Must be non-null. The set of errors/warnings encountered while
+// validating the path are appended to this structure. If verification
+// failed, then there is guaranteed to be at least 1 high severity error
+// written to |errors|.
+//
+// -------------------------
+// Trust Anchor constraints
+// -------------------------
+//
+// Conceptually, VerifyCertificateChain() sets RFC 5937's
+// "enforceTrustAnchorConstraints" to true.
+//
+// One specifies trust anchor constraints using the |last_cert_trust|
+// parameter in conjunction with extensions appearing in |certs.back()|.
+//
+// The trust anchor |certs.back()| is always passed as a certificate to
+// this function, however the manner in which that certificate is
+// interpreted depends on |last_cert_trust|:
+//
+// TRUSTED_ANCHOR:
+//
+// No properties from the root certificate, other than its Subject and
+// SPKI, are checked during verification. This is the usual
+// interpretation for a "trust anchor".
+//
+// TRUSTED_ANCHOR_WITH_EXPIRATION:
+//
+// The validity period of the root is checked, in addition to Subject and SPKI.
+//
+// TRUSTED_ANCHOR_WITH_CONSTRAINTS:
+//
+// Only a subset of extensions and properties from the certificate are checked,
+// as described by RFC 5937.
+//
+// * Signature: No
+// * Validity (expiration): No
+// * Key usage: No
+// * Extended key usage: Yes (not part of RFC 5937)
+// * Basic constraints: Yes, but only the pathlen (CA=false is accepted)
+// * Name constraints: Yes
+// * Certificate policies: Not currently, TODO(crbug.com/634453)
+// * Policy Mappings: No
+// * inhibitAnyPolicy: Not currently, TODO(crbug.com/634453)
+// * PolicyConstraints: Not currently, TODO(crbug.com/634452)
+//
+// The presence of any other unrecognized extension marked as critical fails
+// validation.
+NET_EXPORT void VerifyCertificateChain(
+ const ParsedCertificateList& certs,
+ const CertificateTrust& last_cert_trust,
+ VerifyCertificateChainDelegate* delegate,
+ const der::GeneralizedTime& time,
+ KeyPurpose required_key_purpose,
+ InitialExplicitPolicy initial_explicit_policy,
+ const std::set<der::Input>& user_initial_policy_set,
+ InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
+ InitialAnyPolicyInhibit initial_any_policy_inhibit,
+ std::set<der::Input>* user_constrained_policy_set,
+ CertPathErrors* errors);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_VERIFY_CERTIFICATE_CHAIN_H_
diff --git a/chromium/net/cert/pki/verify_certificate_chain_pkits_unittest.cc b/chromium/net/cert/pki/verify_certificate_chain_pkits_unittest.cc
new file mode 100644
index 00000000000..7a2a4aa32ec
--- /dev/null
+++ b/chromium/net/cert/pki/verify_certificate_chain_pkits_unittest.cc
@@ -0,0 +1,129 @@
+// Copyright 2016 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 "net/cert/pki/verify_certificate_chain.h"
+
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/cert/pki/simple_path_builder_delegate.h"
+#include "net/cert/pki/trust_store.h"
+#include "net/der/input.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
+
+// These require CRL support, which is not implemented at the
+// VerifyCertificateChain level.
+#define Section7InvalidkeyUsageCriticalcRLSignFalseTest4 \
+ DISABLED_Section7InvalidkeyUsageCriticalcRLSignFalseTest4
+#define Section7InvalidkeyUsageNotCriticalcRLSignFalseTest5 \
+ DISABLED_Section7InvalidkeyUsageNotCriticalcRLSignFalseTest5
+
+#include "net/cert/pki/nist_pkits_unittest.h"
+
+namespace net {
+
+namespace {
+
+class VerifyCertificateChainPkitsTestDelegate {
+ public:
+ static void RunTest(std::vector<std::string> cert_ders,
+ std::vector<std::string> crl_ders,
+ const PkitsTestInfo& info) {
+ ASSERT_FALSE(cert_ders.empty());
+
+ // PKITS lists chains from trust anchor to target, whereas
+ // VerifyCertificateChain takes them starting with the target and ending
+ // with the trust anchor.
+ std::vector<scoped_refptr<ParsedCertificate>> input_chain;
+ CertErrors parsing_errors;
+ for (auto i = cert_ders.rbegin(); i != cert_ders.rend(); ++i) {
+ ASSERT_TRUE(ParsedCertificate::CreateAndAddToVector(
+ bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(
+ reinterpret_cast<const uint8_t*>(i->data()), i->size(), nullptr)),
+ {}, &input_chain, &parsing_errors))
+ << parsing_errors.ToDebugString();
+ }
+
+ SimplePathBuilderDelegate path_builder_delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+
+ std::set<der::Input> user_constrained_policy_set;
+
+ CertPathErrors path_errors;
+ VerifyCertificateChain(
+ input_chain, CertificateTrust::ForTrustAnchor(), &path_builder_delegate,
+ info.time, KeyPurpose::ANY_EKU, info.initial_explicit_policy,
+ info.initial_policy_set, info.initial_policy_mapping_inhibit,
+ info.initial_inhibit_any_policy, &user_constrained_policy_set,
+ &path_errors);
+ bool did_succeed = !path_errors.ContainsHighSeverityErrors();
+
+ EXPECT_EQ(info.should_validate, did_succeed);
+ EXPECT_EQ(info.user_constrained_policy_set, user_constrained_policy_set);
+
+ // Check that the errors match expectations. The errors are saved in a
+ // parallel file, as they don't apply generically to the third_party
+ // PKITS data.
+ if (!info.should_validate && !did_succeed) {
+ std::string errors_file_path =
+ std::string(
+ "net/data/verify_certificate_chain_unittest/pkits_errors/") +
+ info.test_number + std::string(".txt");
+
+ std::string expected_errors = ReadTestFileToString(errors_file_path);
+
+ // Check that the errors match.
+ VerifyCertPathErrors(expected_errors, path_errors, input_chain,
+ errors_file_path);
+ } else if (!did_succeed) {
+ // If it failed and wasn't supposed to fail, print the errors.
+ EXPECT_EQ("", path_errors.ToDebugString(input_chain));
+ }
+ }
+};
+
+} // namespace
+
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest01SignatureVerification,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest02ValidityPeriods,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest03VerifyingNameChaining,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest06VerifyingBasicConstraints,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest07KeyUsage,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest08CertificatePolicies,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest09RequireExplicitPolicy,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest10PolicyMappings,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest11InhibitPolicyMapping,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest12InhibitAnyPolicy,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest13NameConstraints,
+ VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ PkitsTest16PrivateCertificateExtensions,
+ VerifyCertificateChainPkitsTestDelegate);
+
+// These require CRL support, which is not implemented at the
+// VerifyCertificateChain level:
+// PkitsTest04BasicCertificateRevocationTests,
+// PkitsTest05VerifyingPathswithSelfIssuedCertificates,
+// PkitsTest14DistributionPoints, PkitsTest15DeltaCRLs
+
+} // namespace net
diff --git a/chromium/net/cert/pki/verify_certificate_chain_typed_unittest.h b/chromium/net/cert/pki/verify_certificate_chain_typed_unittest.h
new file mode 100644
index 00000000000..c563f17ffa0
--- /dev/null
+++ b/chromium/net/cert/pki/verify_certificate_chain_typed_unittest.h
@@ -0,0 +1,228 @@
+// Copyright 2016 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 NET_CERT_PKI_VERIFY_CERTIFICATE_CHAIN_TYPED_UNITTEST_H_
+#define NET_CERT_PKI_VERIFY_CERTIFICATE_CHAIN_TYPED_UNITTEST_H_
+
+#include "net/cert/pem.h"
+#include "net/cert/pki/parsed_certificate.h"
+#include "net/cert/pki/test_helpers.h"
+#include "net/cert/pki/trust_store.h"
+#include "net/cert/pki/verify_certificate_chain.h"
+#include "net/der/input.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+template <typename TestDelegate>
+class VerifyCertificateChainTest : public ::testing::Test {
+ public:
+ void RunTest(const char* file_name) {
+ VerifyCertChainTest test;
+
+ std::string path =
+ std::string("net/data/verify_certificate_chain_unittest/") + file_name;
+
+ SCOPED_TRACE("Test file: " + path);
+
+ if (!ReadVerifyCertChainTestFromFile(path, &test)) {
+ ADD_FAILURE() << "Couldn't load test case: " << path;
+ return;
+ }
+
+ TestDelegate::Verify(test, path);
+ }
+};
+
+// Tests that have only one root. These can be tested without requiring any
+// path-building ability.
+template <typename TestDelegate>
+class VerifyCertificateChainSingleRootTest
+ : public VerifyCertificateChainTest<TestDelegate> {};
+
+TYPED_TEST_SUITE_P(VerifyCertificateChainSingleRootTest);
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, Simple) {
+ this->RunTest("target-and-intermediate/main.test");
+ this->RunTest("target-and-intermediate/ta-with-expiration.test");
+ this->RunTest("target-and-intermediate/ta-with-constraints.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, BasicConstraintsCa) {
+ this->RunTest("intermediate-lacks-basic-constraints/main.test");
+ this->RunTest("intermediate-basic-constraints-ca-false/main.test");
+ this->RunTest("intermediate-basic-constraints-not-critical/main.test");
+ this->RunTest("root-lacks-basic-constraints/main.test");
+ this->RunTest("root-lacks-basic-constraints/ta-with-constraints.test");
+ this->RunTest("root-basic-constraints-ca-false/main.test");
+ this->RunTest("root-basic-constraints-ca-false/ta-with-constraints.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, BasicConstraintsPathlen) {
+ this->RunTest("violates-basic-constraints-pathlen-0/main.test");
+ this->RunTest("basic-constraints-pathlen-0-self-issued/main.test");
+ this->RunTest("target-has-pathlen-but-not-ca/main.test");
+ this->RunTest("violates-pathlen-1-from-root/main.test");
+ this->RunTest("violates-pathlen-1-from-root/ta-with-constraints.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, UnknownExtension) {
+ this->RunTest("intermediate-unknown-critical-extension/main.test");
+ this->RunTest("intermediate-unknown-non-critical-extension/main.test");
+ this->RunTest("target-unknown-critical-extension/main.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, WeakSignature) {
+ this->RunTest("target-signed-with-md5/main.test");
+ this->RunTest("intermediate-signed-with-md5/main.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, WrongSignature) {
+ this->RunTest("target-wrong-signature/main.test");
+ this->RunTest("intermediate-and-target-wrong-signature/main.test");
+ this->RunTest("incorrect-trust-anchor/main.test");
+ this->RunTest("target-wrong-signature-no-authority-key-identifier/main.test");
+ this->RunTest(
+ "intermediate-wrong-signature-no-authority-key-identifier/main.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, LastCertificateNotTrusted) {
+ this->RunTest("target-and-intermediate/distrusted-root.test");
+ this->RunTest("target-and-intermediate/distrusted-root-expired.test");
+ this->RunTest("target-and-intermediate/unspecified-trust-root.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, WeakPublicKey) {
+ this->RunTest("target-signed-by-512bit-rsa/main.test");
+ this->RunTest("target-has-512bit-rsa-key/main.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedUsingEcdsa) {
+ this->RunTest("target-signed-using-ecdsa/main.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, Expired) {
+ this->RunTest("expired-target/not-before.test");
+ this->RunTest("expired-target/not-after.test");
+ this->RunTest("expired-intermediate/not-before.test");
+ this->RunTest("expired-intermediate/not-after.test");
+ this->RunTest("expired-root/not-before.test");
+ this->RunTest("expired-root/not-before-ta-with-expiration.test");
+ this->RunTest("expired-root/not-after.test");
+ this->RunTest("expired-root/not-after-ta-with-expiration.test");
+ this->RunTest("expired-root/not-after-ta-with-constraints.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetNotEndEntity) {
+ this->RunTest("target-not-end-entity/main.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyUsage) {
+ this->RunTest("intermediate-lacks-signing-key-usage/main.test");
+ this->RunTest("target-has-keycertsign-but-not-ca/main.test");
+
+ this->RunTest("target-serverauth-various-keyusages/rsa-decipherOnly.test");
+ this->RunTest(
+ "target-serverauth-various-keyusages/rsa-digitalSignature.test");
+ this->RunTest("target-serverauth-various-keyusages/rsa-keyAgreement.test");
+ this->RunTest("target-serverauth-various-keyusages/rsa-keyEncipherment.test");
+
+ this->RunTest("target-serverauth-various-keyusages/ec-decipherOnly.test");
+ this->RunTest("target-serverauth-various-keyusages/ec-digitalSignature.test");
+ this->RunTest("target-serverauth-various-keyusages/ec-keyAgreement.test");
+ this->RunTest("target-serverauth-various-keyusages/ec-keyEncipherment.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExtendedKeyUsage) {
+ this->RunTest("intermediate-eku-clientauth/any.test");
+ this->RunTest("intermediate-eku-clientauth/serverauth.test");
+ this->RunTest("intermediate-eku-clientauth/clientauth.test");
+ this->RunTest("intermediate-eku-any-and-clientauth/any.test");
+ this->RunTest("intermediate-eku-any-and-clientauth/serverauth.test");
+ this->RunTest("intermediate-eku-any-and-clientauth/clientauth.test");
+ this->RunTest("target-eku-clientauth/any.test");
+ this->RunTest("target-eku-clientauth/serverauth.test");
+ this->RunTest("target-eku-clientauth/clientauth.test");
+ this->RunTest("target-eku-none/any.test");
+ this->RunTest("target-eku-none/serverauth.test");
+ this->RunTest("target-eku-none/clientauth.test");
+ this->RunTest("root-eku-clientauth/serverauth.test");
+ this->RunTest("root-eku-clientauth/serverauth-ta-with-constraints.test");
+ this->RunTest("intermediate-eku-server-gated-crypto/sha1-eku-any.test");
+ this->RunTest(
+ "intermediate-eku-server-gated-crypto/sha1-eku-clientAuth.test");
+ this->RunTest(
+ "intermediate-eku-server-gated-crypto/sha1-eku-serverAuth.test");
+ this->RunTest("intermediate-eku-server-gated-crypto/sha256-eku-any.test");
+ this->RunTest(
+ "intermediate-eku-server-gated-crypto/sha256-eku-clientAuth.test");
+ this->RunTest(
+ "intermediate-eku-server-gated-crypto/sha256-eku-serverAuth.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+ IssuerAndSubjectNotByteForByteEqual) {
+ this->RunTest("issuer-and-subject-not-byte-for-byte-equal/target.test");
+ this->RunTest("issuer-and-subject-not-byte-for-byte-equal/anchor.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TrustAnchorNotSelfSigned) {
+ this->RunTest("non-self-signed-root/main.test");
+ this->RunTest("non-self-signed-root/ta-with-constraints.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRollover) {
+ this->RunTest("key-rollover/oldchain.test");
+ this->RunTest("key-rollover/rolloverchain.test");
+ this->RunTest("key-rollover/longrolloverchain.test");
+ this->RunTest("key-rollover/newchain.test");
+}
+
+// Test coverage of policies comes primarily from the PKITS tests. The
+// tests here only cover aspects not already tested by PKITS.
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, Policies) {
+ this->RunTest("unknown-critical-policy-qualifier/main.test");
+ this->RunTest("unknown-non-critical-policy-qualifier/main.test");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ManyNames) {
+ this->RunTest("many-names/ok-all-types.test");
+ this->RunTest("many-names/ok-different-types-dns.test");
+ this->RunTest("many-names/ok-different-types-ips.test");
+ this->RunTest("many-names/ok-different-types-dirnames.test");
+ this->RunTest("many-names/toomany-all-types.test");
+ this->RunTest("many-names/toomany-dns-excluded.test");
+ this->RunTest("many-names/toomany-dns-permitted.test");
+ this->RunTest("many-names/toomany-ips-excluded.test");
+ this->RunTest("many-names/toomany-ips-permitted.test");
+ this->RunTest("many-names/toomany-dirnames-excluded.test");
+ this->RunTest("many-names/toomany-dirnames-permitted.test");
+}
+
+// TODO(eroman): Add test that invalid validity dates where the day or month
+// ordinal not in range, like "March 39, 2016" are rejected.
+
+REGISTER_TYPED_TEST_SUITE_P(VerifyCertificateChainSingleRootTest,
+ Simple,
+ BasicConstraintsCa,
+ BasicConstraintsPathlen,
+ UnknownExtension,
+ WeakSignature,
+ WrongSignature,
+ LastCertificateNotTrusted,
+ WeakPublicKey,
+ TargetSignedUsingEcdsa,
+ Expired,
+ TargetNotEndEntity,
+ KeyUsage,
+ ExtendedKeyUsage,
+ IssuerAndSubjectNotByteForByteEqual,
+ TrustAnchorNotSelfSigned,
+ KeyRollover,
+ Policies,
+ ManyNames);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_VERIFY_CERTIFICATE_CHAIN_TYPED_UNITTEST_H_
diff --git a/chromium/net/cert/pki/verify_certificate_chain_unittest.cc b/chromium/net/cert/pki/verify_certificate_chain_unittest.cc
new file mode 100644
index 00000000000..a98532ebc0a
--- /dev/null
+++ b/chromium/net/cert/pki/verify_certificate_chain_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2015 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 "net/cert/pki/verify_certificate_chain.h"
+
+#include "net/cert/pki/simple_path_builder_delegate.h"
+#include "net/cert/pki/test_helpers.h"
+#include "net/cert/pki/trust_store.h"
+#include "net/cert/pki/verify_certificate_chain_typed_unittest.h"
+
+namespace net {
+
+namespace {
+
+class VerifyCertificateChainTestDelegate {
+ public:
+ static void Verify(const VerifyCertChainTest& test,
+ const std::string& test_file_path) {
+ SimplePathBuilderDelegate delegate(
+ 1024, SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
+
+ CertPathErrors errors;
+ // TODO(eroman): Check user_constrained_policy_set.
+ VerifyCertificateChain(
+ test.chain, test.last_cert_trust, &delegate, test.time,
+ test.key_purpose, test.initial_explicit_policy,
+ test.user_initial_policy_set, test.initial_policy_mapping_inhibit,
+ test.initial_any_policy_inhibit,
+ nullptr /*user_constrained_policy_set*/, &errors);
+ VerifyCertPathErrors(test.expected_errors, errors, test.chain,
+ test_file_path);
+ }
+};
+
+} // namespace
+
+INSTANTIATE_TYPED_TEST_SUITE_P(VerifyCertificateChain,
+ VerifyCertificateChainSingleRootTest,
+ VerifyCertificateChainTestDelegate);
+
+} // namespace net
diff --git a/chromium/net/cert/pki/verify_name_match.cc b/chromium/net/cert/pki/verify_name_match.cc
new file mode 100644
index 00000000000..b17ab7e2296
--- /dev/null
+++ b/chromium/net/cert/pki/verify_name_match.cc
@@ -0,0 +1,418 @@
+// Copyright 2015 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 "net/cert/pki/verify_name_match.h"
+
+#include "base/check.h"
+#include "base/notreached.h"
+#include "base/strings/string_util.h"
+#include "net/cert/pki/cert_error_params.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/parse_name.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "net/der/tag.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+
+namespace net {
+
+DEFINE_CERT_ERROR_ID(kFailedConvertingAttributeValue,
+ "Failed converting AttributeValue to string");
+DEFINE_CERT_ERROR_ID(kFailedNormalizingString, "Failed normalizing string");
+
+namespace {
+
+// Types of character set checking that NormalizeDirectoryString can perform.
+enum CharsetEnforcement {
+ NO_ENFORCEMENT,
+ ENFORCE_PRINTABLE_STRING,
+ ENFORCE_ASCII,
+};
+
+// Normalizes |output|, a UTF-8 encoded string, as if it contained
+// only ASCII characters.
+//
+// This could be considered a partial subset of RFC 5280 rules, and
+// is compatible with RFC 2459/3280.
+//
+// In particular, RFC 5280, Section 7.1 describes how UTF8String
+// and PrintableString should be compared - using the LDAP StringPrep
+// profile of RFC 4518, with case folding and whitespace compression.
+// However, because it is optional for 2459/3280 implementations and because
+// it's desirable to avoid the size cost of the StringPrep tables,
+// this function treats |output| as if it was composed of ASCII.
+//
+// That is, rather than folding all whitespace characters, it only
+// folds ' '. Rather than case folding using locale-aware handling,
+// it only folds A-Z to a-z.
+//
+// This gives better results than outright rejecting (due to mismatched
+// encodings), or from doing a strict binary comparison (the minimum
+// required by RFC 3280), and is sufficient for those certificates
+// publicly deployed.
+//
+// If |charset_enforcement| is not NO_ENFORCEMENT and |output| contains any
+// characters not allowed in the specified charset, returns false.
+//
+// NOTE: |output| will be modified regardless of the return.
+[[nodiscard]] bool NormalizeDirectoryString(
+ CharsetEnforcement charset_enforcement,
+ std::string* output) {
+ // Normalized version will always be equal or shorter than input.
+ // Normalize in place and then truncate the output if necessary.
+ std::string::const_iterator read_iter = output->begin();
+ std::string::iterator write_iter = output->begin();
+
+ for (; read_iter != output->end() && *read_iter == ' '; ++read_iter) {
+ // Ignore leading whitespace.
+ }
+
+ for (; read_iter != output->end(); ++read_iter) {
+ const unsigned char c = *read_iter;
+ if (c == ' ') {
+ // If there are non-whitespace characters remaining in input, compress
+ // multiple whitespace chars to a single space, otherwise ignore trailing
+ // whitespace.
+ std::string::const_iterator next_iter = read_iter + 1;
+ if (next_iter != output->end() && *next_iter != ' ')
+ *(write_iter++) = ' ';
+ } else if (base::IsAsciiUpper(c)) {
+ // Fold case.
+ *(write_iter++) = c + ('a' - 'A');
+ } else {
+ // Note that these checks depend on the characters allowed by earlier
+ // conditions also being valid for the enforced charset.
+ switch (charset_enforcement) {
+ case ENFORCE_PRINTABLE_STRING:
+ // See NormalizePrintableStringValue comment for the acceptable list
+ // of characters.
+ if (!(base::IsAsciiLower(c) || (c >= '\'' && c <= ':') || c == '=' ||
+ c == '?'))
+ return false;
+ break;
+ case ENFORCE_ASCII:
+ if (c > 0x7F)
+ return false;
+ break;
+ case NO_ENFORCEMENT:
+ break;
+ }
+ *(write_iter++) = c;
+ }
+ }
+ if (write_iter != output->end())
+ output->erase(write_iter, output->end());
+ return true;
+}
+
+// Converts the value of X509NameAttribute |attribute| to UTF-8, normalizes it,
+// and stores in |output|. The type of |attribute| must be one of the types for
+// which IsNormalizableDirectoryString is true.
+//
+// If the value of |attribute| can be normalized, returns true and sets
+// |output| to the case folded, normalized value. If the value of |attribute|
+// is invalid, returns false.
+// NOTE: |output| will be modified regardless of the return.
+[[nodiscard]] bool NormalizeValue(X509NameAttribute attribute,
+ std::string* output,
+ CertErrors* errors) {
+ DCHECK(errors);
+
+ if (!attribute.ValueAsStringUnsafe(output)) {
+ errors->AddError(kFailedConvertingAttributeValue,
+ CreateCertErrorParams1SizeT("tag", attribute.value_tag));
+ return false;
+ }
+
+ bool success = false;
+ switch (attribute.value_tag) {
+ case der::kPrintableString:
+ success = NormalizeDirectoryString(ENFORCE_PRINTABLE_STRING, output);
+ break;
+ case der::kBmpString:
+ case der::kUniversalString:
+ case der::kUtf8String:
+ success = NormalizeDirectoryString(NO_ENFORCEMENT, output);
+ break;
+ case der::kIA5String:
+ success = NormalizeDirectoryString(ENFORCE_ASCII, output);
+ break;
+ default:
+ NOTREACHED();
+ success = false;
+ break;
+ }
+
+ if (!success) {
+ errors->AddError(kFailedNormalizingString,
+ CreateCertErrorParams1SizeT("tag", attribute.value_tag));
+ }
+
+ return success;
+}
+
+// Returns true if |tag| is a string type that NormalizeValue can handle.
+bool IsNormalizableDirectoryString(der::Tag tag) {
+ switch (tag) {
+ case der::kPrintableString:
+ case der::kUtf8String:
+ // RFC 5280 only requires handling IA5String for comparing domainComponent
+ // values, but handling it here avoids the need to special case anything.
+ case der::kIA5String:
+ case der::kUniversalString:
+ case der::kBmpString:
+ return true;
+ // TeletexString isn't normalized. Section 8 of RFC 5280 briefly
+ // describes the historical confusion between treating TeletexString
+ // as Latin1String vs T.61, and there are even incompatibilities within
+ // T.61 implementations. As this time is virtually unused, simply
+ // treat it with a binary comparison, as permitted by RFC 3280/5280.
+ default:
+ return false;
+ }
+}
+
+// Returns true if the value of X509NameAttribute |a| matches |b|.
+bool VerifyValueMatch(X509NameAttribute a, X509NameAttribute b) {
+ if (IsNormalizableDirectoryString(a.value_tag) &&
+ IsNormalizableDirectoryString(b.value_tag)) {
+ std::string a_normalized, b_normalized;
+ // TODO(eroman): Plumb this down.
+ CertErrors unused_errors;
+ if (!NormalizeValue(a, &a_normalized, &unused_errors) ||
+ !NormalizeValue(b, &b_normalized, &unused_errors))
+ return false;
+ return a_normalized == b_normalized;
+ }
+ // Attributes encoded with different types may be assumed to be unequal.
+ if (a.value_tag != b.value_tag)
+ return false;
+ // All other types use binary comparison.
+ return a.value == b.value;
+}
+
+// Verifies that |a_parser| and |b_parser| are the same length and that every
+// AttributeTypeAndValue in |a_parser| has a matching AttributeTypeAndValue in
+// |b_parser|.
+bool VerifyRdnMatch(der::Parser* a_parser, der::Parser* b_parser) {
+ RelativeDistinguishedName a_type_and_values, b_type_and_values;
+ if (!ReadRdn(a_parser, &a_type_and_values) ||
+ !ReadRdn(b_parser, &b_type_and_values))
+ return false;
+
+ // RFC 5280 section 7.1:
+ // Two relative distinguished names RDN1 and RDN2 match if they have the same
+ // number of naming attributes and for each naming attribute in RDN1 there is
+ // a matching naming attribute in RDN2.
+ if (a_type_and_values.size() != b_type_and_values.size())
+ return false;
+
+ // The ordering of elements may differ due to denormalized values sorting
+ // differently in the DER encoding. Since the number of elements should be
+ // small, a naive linear search for each element should be fine. (Hostile
+ // certificates already have ways to provoke pathological behavior.)
+ for (const auto& a : a_type_and_values) {
+ auto b_iter = b_type_and_values.begin();
+ for (; b_iter != b_type_and_values.end(); ++b_iter) {
+ const auto& b = *b_iter;
+ if (a.type == b.type && VerifyValueMatch(a, b)) {
+ break;
+ }
+ }
+ if (b_iter == b_type_and_values.end())
+ return false;
+ // Remove the matched element from b_type_and_values to ensure duplicate
+ // elements in a_type_and_values can't match the same element in
+ // b_type_and_values multiple times.
+ b_type_and_values.erase(b_iter);
+ }
+
+ // Every element in |a_type_and_values| had a matching element in
+ // |b_type_and_values|.
+ return true;
+}
+
+enum NameMatchType {
+ EXACT_MATCH,
+ SUBTREE_MATCH,
+};
+
+// Verify that |a| matches |b|. If |match_type| is EXACT_MATCH, returns true if
+// they are an exact match as defined by RFC 5280 7.1. If |match_type| is
+// SUBTREE_MATCH, returns true if |a| is within the subtree defined by |b| as
+// defined by RFC 5280 7.1.
+//
+// |a| and |b| are ASN.1 RDNSequence values (not including the Sequence tag),
+// defined in RFC 5280 section 4.1.2.4:
+//
+// Name ::= CHOICE { -- only one possibility for now --
+// rdnSequence RDNSequence }
+//
+// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+//
+// RelativeDistinguishedName ::=
+// SET SIZE (1..MAX) OF AttributeTypeAndValue
+bool VerifyNameMatchInternal(const der::Input& a,
+ const der::Input& b,
+ NameMatchType match_type) {
+ // Empty Names are allowed. RFC 5280 section 4.1.2.4 requires "The issuer
+ // field MUST contain a non-empty distinguished name (DN)", while section
+ // 4.1.2.6 allows for the Subject to be empty in certain cases. The caller is
+ // assumed to have verified those conditions.
+
+ // RFC 5280 section 7.1:
+ // Two distinguished names DN1 and DN2 match if they have the same number of
+ // RDNs, for each RDN in DN1 there is a matching RDN in DN2, and the matching
+ // RDNs appear in the same order in both DNs.
+
+ // As an optimization, first just compare the number of RDNs:
+ der::Parser a_rdn_sequence_counter(a);
+ der::Parser b_rdn_sequence_counter(b);
+ while (a_rdn_sequence_counter.HasMore() && b_rdn_sequence_counter.HasMore()) {
+ if (!a_rdn_sequence_counter.SkipTag(der::kSet) ||
+ !b_rdn_sequence_counter.SkipTag(der::kSet)) {
+ return false;
+ }
+ }
+ // If doing exact match and either of the sequences has more elements than the
+ // other, not a match. If doing a subtree match, the first Name may have more
+ // RDNs than the second.
+ if (b_rdn_sequence_counter.HasMore())
+ return false;
+ if (match_type == EXACT_MATCH && a_rdn_sequence_counter.HasMore())
+ return false;
+
+ // Verify that RDNs in |a| and |b| match.
+ der::Parser a_rdn_sequence(a);
+ der::Parser b_rdn_sequence(b);
+ while (a_rdn_sequence.HasMore() && b_rdn_sequence.HasMore()) {
+ der::Parser a_rdn, b_rdn;
+ if (!a_rdn_sequence.ReadConstructed(der::kSet, &a_rdn) ||
+ !b_rdn_sequence.ReadConstructed(der::kSet, &b_rdn)) {
+ return false;
+ }
+ if (!VerifyRdnMatch(&a_rdn, &b_rdn))
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool NormalizeName(const der::Input& name_rdn_sequence,
+ std::string* normalized_rdn_sequence,
+ CertErrors* errors) {
+ DCHECK(errors);
+
+ // RFC 5280 section 4.1.2.4
+ // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ der::Parser rdn_sequence_parser(name_rdn_sequence);
+
+ bssl::ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), 0))
+ return false;
+
+ while (rdn_sequence_parser.HasMore()) {
+ // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
+ der::Parser rdn_parser;
+ if (!rdn_sequence_parser.ReadConstructed(der::kSet, &rdn_parser))
+ return false;
+ RelativeDistinguishedName type_and_values;
+ if (!ReadRdn(&rdn_parser, &type_and_values))
+ return false;
+
+ CBB rdn_cbb;
+ if (!CBB_add_asn1(cbb.get(), &rdn_cbb, CBS_ASN1_SET))
+ return false;
+
+ for (const auto& type_and_value : type_and_values) {
+ // AttributeTypeAndValue ::= SEQUENCE {
+ // type AttributeType,
+ // value AttributeValue }
+ CBB attribute_type_and_value_cbb, type_cbb, value_cbb;
+ if (!CBB_add_asn1(&rdn_cbb, &attribute_type_and_value_cbb,
+ CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+
+ // AttributeType ::= OBJECT IDENTIFIER
+ if (!CBB_add_asn1(&attribute_type_and_value_cbb, &type_cbb,
+ CBS_ASN1_OBJECT) ||
+ !CBB_add_bytes(&type_cbb, type_and_value.type.UnsafeData(),
+ type_and_value.type.Length())) {
+ return false;
+ }
+
+ // AttributeValue ::= ANY -- DEFINED BY AttributeType
+ if (IsNormalizableDirectoryString(type_and_value.value_tag)) {
+ std::string normalized_value;
+ if (!NormalizeValue(type_and_value, &normalized_value, errors))
+ return false;
+ if (!CBB_add_asn1(&attribute_type_and_value_cbb, &value_cbb,
+ CBS_ASN1_UTF8STRING) ||
+ !CBB_add_bytes(
+ &value_cbb,
+ reinterpret_cast<const uint8_t*>(normalized_value.data()),
+ normalized_value.size()))
+ return false;
+ } else {
+ if (!CBB_add_asn1(&attribute_type_and_value_cbb, &value_cbb,
+ type_and_value.value_tag) ||
+ !CBB_add_bytes(&value_cbb, type_and_value.value.UnsafeData(),
+ type_and_value.value.Length()))
+ return false;
+ }
+
+ if (!CBB_flush(&rdn_cbb))
+ return false;
+ }
+
+ // Ensure the encoded AttributeTypeAndValue values in the SET OF are sorted.
+ if (!CBB_flush_asn1_set_of(&rdn_cbb) || !CBB_flush(cbb.get()))
+ return false;
+ }
+
+ normalized_rdn_sequence->assign(CBB_data(cbb.get()),
+ CBB_data(cbb.get()) + CBB_len(cbb.get()));
+ return true;
+}
+
+bool VerifyNameMatch(const der::Input& a_rdn_sequence,
+ const der::Input& b_rdn_sequence) {
+ return VerifyNameMatchInternal(a_rdn_sequence, b_rdn_sequence, EXACT_MATCH);
+}
+
+bool VerifyNameInSubtree(const der::Input& name_rdn_sequence,
+ const der::Input& parent_rdn_sequence) {
+ return VerifyNameMatchInternal(name_rdn_sequence, parent_rdn_sequence,
+ SUBTREE_MATCH);
+}
+
+bool NameContainsEmailAddress(const der::Input& name_rdn_sequence,
+ bool* contained_email_address) {
+ der::Parser rdn_sequence_parser(name_rdn_sequence);
+
+ while (rdn_sequence_parser.HasMore()) {
+ der::Parser rdn_parser;
+ if (!rdn_sequence_parser.ReadConstructed(der::kSet, &rdn_parser))
+ return false;
+
+ RelativeDistinguishedName type_and_values;
+ if (!ReadRdn(&rdn_parser, &type_and_values))
+ return false;
+
+ for (const auto& type_and_value : type_and_values) {
+ if (type_and_value.type == der::Input(kTypeEmailAddressOid)) {
+ *contained_email_address = true;
+ return true;
+ }
+ }
+ }
+
+ *contained_email_address = false;
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/verify_name_match.h b/chromium/net/cert/pki/verify_name_match.h
new file mode 100644
index 00000000000..4e49d435df5
--- /dev/null
+++ b/chromium/net/cert/pki/verify_name_match.h
@@ -0,0 +1,57 @@
+// Copyright 2015 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 NET_CERT_PKI_VERIFY_NAME_MATCH_H_
+#define NET_CERT_PKI_VERIFY_NAME_MATCH_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class CertErrors;
+
+namespace der {
+class Input;
+} // namespace der
+
+// Normalizes DER-encoded X.501 Name |name_rdn_sequence| (which should not
+// include the Sequence tag). If successful, returns true and stores the
+// normalized DER-encoded Name into |normalized_rdn_sequence| (not including an
+// outer Sequence tag). Returns false if there was an error parsing or
+// normalizing the input, and adds error information to |errors|. |errors| must
+// be non-null.
+NET_EXPORT bool NormalizeName(const der::Input& name_rdn_sequence,
+ std::string* normalized_rdn_sequence,
+ CertErrors* errors);
+
+// Compares DER-encoded X.501 Name values according to RFC 5280 rules.
+// |a_rdn_sequence| and |b_rdn_sequence| should be the DER-encoded RDNSequence
+// values (not including the Sequence tag).
+// Returns true if |a_rdn_sequence| and |b_rdn_sequence| match.
+NET_EXPORT bool VerifyNameMatch(const der::Input& a_rdn_sequence,
+ const der::Input& b_rdn_sequence);
+
+// Compares |name_rdn_sequence| and |parent_rdn_sequence| and return true if
+// |name_rdn_sequence| is within the subtree defined by |parent_rdn_sequence| as
+// defined by RFC 5280 section 7.1. |name_rdn_sequence| and
+// |parent_rdn_sequence| should be the DER-encoded sequence values (not
+// including the Sequence tag).
+NET_EXPORT bool VerifyNameInSubtree(const der::Input& name_rdn_sequence,
+ const der::Input& parent_rdn_sequence);
+
+// Helper functions:
+
+// Checks if |name_rdn_sequence| contains an emailAddress attribute type.
+// If the return value is true, |*contained_email_address| will be set to
+// indicate whether an emailAddress attribute was present.
+// Returns false if there was a parsing error.
+[[nodiscard]] bool NameContainsEmailAddress(const der::Input& name_rdn_sequence,
+ bool* contained_email_address);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_VERIFY_NAME_MATCH_H_
diff --git a/chromium/net/cert/pki/verify_name_match_fuzzer.cc b/chromium/net/cert/pki/verify_name_match_fuzzer.cc
new file mode 100644
index 00000000000..02ae46f62bd
--- /dev/null
+++ b/chromium/net/cert/pki/verify_name_match_fuzzer.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 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 "net/cert/pki/verify_name_match.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <vector>
+
+#include "net/der/input.h"
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fuzzed_data(data, size);
+
+ // Intentionally using uint16_t here to avoid empty |second_part|.
+ size_t first_part_size = fuzzed_data.ConsumeIntegral<uint16_t>();
+ std::vector<uint8_t> first_part =
+ fuzzed_data.ConsumeBytes<uint8_t>(first_part_size);
+ std::vector<uint8_t> second_part =
+ fuzzed_data.ConsumeRemainingBytes<uint8_t>();
+
+ net::der::Input in1(first_part.data(), first_part.size());
+ net::der::Input in2(second_part.data(), second_part.size());
+ bool match = net::VerifyNameMatch(in1, in2);
+ bool reverse_order_match = net::VerifyNameMatch(in2, in1);
+ // Result should be the same regardless of argument order.
+ CHECK_EQ(match, reverse_order_match);
+ return 0;
+}
diff --git a/chromium/net/cert/pki/verify_name_match_normalizename_fuzzer.cc b/chromium/net/cert/pki/verify_name_match_normalizename_fuzzer.cc
new file mode 100644
index 00000000000..dc5c810c501
--- /dev/null
+++ b/chromium/net/cert/pki/verify_name_match_normalizename_fuzzer.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 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 "net/cert/pki/verify_name_match.h"
+
+#include "net/cert/pki/cert_errors.h"
+#include "net/der/input.h"
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ net::der::Input in(data, size);
+ std::string normalized_der;
+ net::CertErrors errors;
+ bool success = net::NormalizeName(in, &normalized_der, &errors);
+ if (success) {
+ // If the input was successfully normalized, re-normalizing it should
+ // produce the same output again.
+ std::string renormalized_der;
+ bool renormalize_success = net::NormalizeName(
+ net::der::Input(&normalized_der), &renormalized_der, &errors);
+ CHECK(renormalize_success);
+ CHECK_EQ(normalized_der, renormalized_der);
+ }
+ return 0;
+}
diff --git a/chromium/net/cert/pki/verify_name_match_unittest.cc b/chromium/net/cert/pki/verify_name_match_unittest.cc
new file mode 100644
index 00000000000..59660c0c936
--- /dev/null
+++ b/chromium/net/cert/pki/verify_name_match_unittest.cc
@@ -0,0 +1,612 @@
+// Copyright 2015 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 "net/cert/pki/verify_name_match.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "net/cert/pki/test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+// Loads test data from file. The filename is constructed from the parameters:
+// |prefix| describes the type of data being tested, e.g. "ascii",
+// "unicode_bmp", "unicode_supplementary", and "invalid".
+// |value_type| indicates what ASN.1 type is used to encode the data.
+// |suffix| indicates any additional modifications, such as caseswapping,
+// whitespace adding, etc.
+::testing::AssertionResult LoadTestData(const std::string& prefix,
+ const std::string& value_type,
+ const std::string& suffix,
+ std::string* result) {
+ std::string path = "net/data/verify_name_match_unittest/names/" + prefix +
+ "-" + value_type + "-" + suffix + ".pem";
+
+ const PemBlockMapping mappings[] = {
+ {"NAME", result},
+ };
+
+ return ReadTestDataFromPemFile(path, mappings);
+}
+
+bool TypesAreComparable(const std::string& type_1, const std::string& type_2) {
+ if (type_1 == type_2)
+ return true;
+ if ((type_1 == "PRINTABLESTRING" || type_1 == "UTF8" ||
+ type_1 == "BMPSTRING" || type_1 == "UNIVERSALSTRING") &&
+ (type_2 == "PRINTABLESTRING" || type_2 == "UTF8" ||
+ type_2 == "BMPSTRING" || type_2 == "UNIVERSALSTRING")) {
+ return true;
+ }
+ return false;
+}
+
+// All string types.
+static const char* kValueTypes[] = {"PRINTABLESTRING", "T61STRING", "UTF8",
+ "BMPSTRING", "UNIVERSALSTRING"};
+// String types that can encode the Unicode Basic Multilingual Plane.
+static const char* kUnicodeBMPValueTypes[] = {"UTF8", "BMPSTRING",
+ "UNIVERSALSTRING"};
+// String types that can encode the Unicode Supplementary Planes.
+static const char* kUnicodeSupplementaryValueTypes[] = {"UTF8",
+ "UNIVERSALSTRING"};
+
+static const char* kMangleTypes[] = {"unmangled", "case_swap",
+ "extra_whitespace"};
+
+} // namespace
+
+class VerifyNameMatchSimpleTest
+ : public ::testing::TestWithParam<
+ ::testing::tuple<const char*, const char*>> {
+ public:
+ std::string value_type() const { return ::testing::get<0>(GetParam()); }
+ std::string suffix() const { return ::testing::get<1>(GetParam()); }
+};
+
+// Compare each input against itself, verifies that all input data is parsed
+// successfully.
+TEST_P(VerifyNameMatchSimpleTest, ExactEquality) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), suffix(), &der));
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&der),
+ SequenceValueFromString(&der)));
+
+ std::string der_extra_attr;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), suffix() + "-extra_attr",
+ &der_extra_attr));
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&der_extra_attr),
+ SequenceValueFromString(&der_extra_attr)));
+
+ std::string der_extra_rdn;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), suffix() + "-extra_rdn",
+ &der_extra_rdn));
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&der_extra_rdn),
+ SequenceValueFromString(&der_extra_rdn)));
+}
+
+// Ensure that a Name does not match another Name which is exactly the same but
+// with an extra attribute in one Relative Distinguished Name.
+TEST_P(VerifyNameMatchSimpleTest, ExtraAttrDoesNotMatch) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), suffix(), &der));
+ std::string der_extra_attr;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), suffix() + "-extra_attr",
+ &der_extra_attr));
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&der),
+ SequenceValueFromString(&der_extra_attr)));
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&der_extra_attr),
+ SequenceValueFromString(&der)));
+}
+
+// Ensure that a Name does not match another Name which has the same number of
+// RDNs and attributes, but where one of the attributes is duplicated in one of
+// the names but not in the other.
+TEST_P(VerifyNameMatchSimpleTest, DupeAttrDoesNotMatch) {
+ std::string der_dupe_attr;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), suffix() + "-dupe_attr",
+ &der_dupe_attr));
+ std::string der_extra_attr;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), suffix() + "-extra_attr",
+ &der_extra_attr));
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&der_dupe_attr),
+ SequenceValueFromString(&der_extra_attr)));
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&der_extra_attr),
+ SequenceValueFromString(&der_dupe_attr)));
+ // However, the name with a dupe attribute should match itself.
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&der_dupe_attr),
+ SequenceValueFromString(&der_dupe_attr)));
+}
+
+// Ensure that a Name does not match another Name which is exactly the same but
+// with an extra Relative Distinguished Name.
+TEST_P(VerifyNameMatchSimpleTest, ExtraRdnDoesNotMatch) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), suffix(), &der));
+ std::string der_extra_rdn;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), suffix() + "-extra_rdn",
+ &der_extra_rdn));
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&der),
+ SequenceValueFromString(&der_extra_rdn)));
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&der_extra_rdn),
+ SequenceValueFromString(&der)));
+}
+
+// Runs VerifyNameMatchSimpleTest for all combinations of value_type and and
+// suffix.
+INSTANTIATE_TEST_SUITE_P(InstantiationName,
+ VerifyNameMatchSimpleTest,
+ ::testing::Combine(::testing::ValuesIn(kValueTypes),
+ ::testing::ValuesIn(kMangleTypes)));
+
+class VerifyNameMatchNormalizationTest
+ : public ::testing::TestWithParam<::testing::tuple<bool, const char*>> {
+ public:
+ bool expected_result() const { return ::testing::get<0>(GetParam()); }
+ std::string value_type() const { return ::testing::get<1>(GetParam()); }
+};
+
+// Verify matching is case insensitive (for the types which currently support
+// normalization).
+TEST_P(VerifyNameMatchNormalizationTest, CaseInsensitivity) {
+ std::string normal;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), "unmangled", &normal));
+ std::string case_swap;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), "case_swap", &case_swap));
+ EXPECT_EQ(expected_result(),
+ VerifyNameMatch(SequenceValueFromString(&normal),
+ SequenceValueFromString(&case_swap)));
+ EXPECT_EQ(expected_result(),
+ VerifyNameMatch(SequenceValueFromString(&case_swap),
+ SequenceValueFromString(&normal)));
+}
+
+// Verify matching folds whitespace (for the types which currently support
+// normalization).
+TEST_P(VerifyNameMatchNormalizationTest, CollapseWhitespace) {
+ std::string normal;
+ ASSERT_TRUE(LoadTestData("ascii", value_type(), "unmangled", &normal));
+ std::string whitespace;
+ ASSERT_TRUE(
+ LoadTestData("ascii", value_type(), "extra_whitespace", &whitespace));
+ EXPECT_EQ(expected_result(),
+ VerifyNameMatch(SequenceValueFromString(&normal),
+ SequenceValueFromString(&whitespace)));
+ EXPECT_EQ(expected_result(),
+ VerifyNameMatch(SequenceValueFromString(&whitespace),
+ SequenceValueFromString(&normal)));
+}
+
+// Runs VerifyNameMatchNormalizationTest for each (expected_result, value_type)
+// tuple.
+INSTANTIATE_TEST_SUITE_P(
+ InstantiationName,
+ VerifyNameMatchNormalizationTest,
+ ::testing::Values(
+ ::testing::make_tuple(true,
+ static_cast<const char*>("PRINTABLESTRING")),
+ ::testing::make_tuple(false, static_cast<const char*>("T61STRING")),
+ ::testing::make_tuple(true, static_cast<const char*>("UTF8")),
+ ::testing::make_tuple(true, static_cast<const char*>("BMPSTRING")),
+ ::testing::make_tuple(true,
+ static_cast<const char*>("UNIVERSALSTRING"))));
+
+class VerifyNameMatchDifferingTypesTest
+ : public ::testing::TestWithParam<
+ ::testing::tuple<const char*, const char*>> {
+ public:
+ std::string value_type_1() const { return ::testing::get<0>(GetParam()); }
+ std::string value_type_2() const { return ::testing::get<1>(GetParam()); }
+};
+
+TEST_P(VerifyNameMatchDifferingTypesTest, NormalizableTypesAreEqual) {
+ std::string der_1;
+ ASSERT_TRUE(LoadTestData("ascii", value_type_1(), "unmangled", &der_1));
+ std::string der_2;
+ ASSERT_TRUE(LoadTestData("ascii", value_type_2(), "unmangled", &der_2));
+ if (TypesAreComparable(value_type_1(), value_type_2())) {
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&der_1),
+ SequenceValueFromString(&der_2)));
+ } else {
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&der_1),
+ SequenceValueFromString(&der_2)));
+ }
+}
+
+TEST_P(VerifyNameMatchDifferingTypesTest, NormalizableTypesInSubtrees) {
+ std::string der_1;
+ ASSERT_TRUE(LoadTestData("ascii", value_type_1(), "unmangled", &der_1));
+ std::string der_1_extra_rdn;
+ ASSERT_TRUE(LoadTestData("ascii", value_type_1(), "unmangled-extra_rdn",
+ &der_1_extra_rdn));
+ std::string der_1_extra_attr;
+ ASSERT_TRUE(LoadTestData("ascii", value_type_1(), "unmangled-extra_attr",
+ &der_1_extra_attr));
+ std::string der_2;
+ ASSERT_TRUE(LoadTestData("ascii", value_type_2(), "unmangled", &der_2));
+ std::string der_2_extra_rdn;
+ ASSERT_TRUE(LoadTestData("ascii", value_type_2(), "unmangled-extra_rdn",
+ &der_2_extra_rdn));
+ std::string der_2_extra_attr;
+ ASSERT_TRUE(LoadTestData("ascii", value_type_2(), "unmangled-extra_attr",
+ &der_2_extra_attr));
+
+ if (TypesAreComparable(value_type_1(), value_type_2())) {
+ EXPECT_TRUE(VerifyNameInSubtree(SequenceValueFromString(&der_1),
+ SequenceValueFromString(&der_2)));
+ EXPECT_TRUE(VerifyNameInSubtree(SequenceValueFromString(&der_2),
+ SequenceValueFromString(&der_1)));
+ EXPECT_TRUE(VerifyNameInSubtree(SequenceValueFromString(&der_1_extra_rdn),
+ SequenceValueFromString(&der_2)));
+ EXPECT_TRUE(VerifyNameInSubtree(SequenceValueFromString(&der_2_extra_rdn),
+ SequenceValueFromString(&der_1)));
+ } else {
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&der_1),
+ SequenceValueFromString(&der_2)));
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&der_2),
+ SequenceValueFromString(&der_1)));
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&der_1_extra_rdn),
+ SequenceValueFromString(&der_2)));
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&der_2_extra_rdn),
+ SequenceValueFromString(&der_1)));
+ }
+
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&der_1),
+ SequenceValueFromString(&der_2_extra_rdn)));
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&der_2),
+ SequenceValueFromString(&der_1_extra_rdn)));
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&der_1_extra_attr),
+ SequenceValueFromString(&der_2)));
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&der_2_extra_attr),
+ SequenceValueFromString(&der_1)));
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&der_1),
+ SequenceValueFromString(&der_2_extra_attr)));
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&der_2),
+ SequenceValueFromString(&der_1_extra_attr)));
+}
+
+// Runs VerifyNameMatchDifferingTypesTest for all combinations of value types in
+// value_type1 and value_type_2.
+INSTANTIATE_TEST_SUITE_P(InstantiationName,
+ VerifyNameMatchDifferingTypesTest,
+ ::testing::Combine(::testing::ValuesIn(kValueTypes),
+ ::testing::ValuesIn(kValueTypes)));
+
+class VerifyNameMatchUnicodeConversionTest
+ : public ::testing::TestWithParam<
+ ::testing::tuple<const char*,
+ ::testing::tuple<const char*, const char*>>> {
+ public:
+ std::string prefix() const { return ::testing::get<0>(GetParam()); }
+ std::string value_type_1() const {
+ return ::testing::get<0>(::testing::get<1>(GetParam()));
+ }
+ std::string value_type_2() const {
+ return ::testing::get<1>(::testing::get<1>(GetParam()));
+ }
+};
+
+TEST_P(VerifyNameMatchUnicodeConversionTest, UnicodeConversionsAreEqual) {
+ std::string der_1;
+ ASSERT_TRUE(LoadTestData(prefix(), value_type_1(), "unmangled", &der_1));
+ std::string der_2;
+ ASSERT_TRUE(LoadTestData(prefix(), value_type_2(), "unmangled", &der_2));
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&der_1),
+ SequenceValueFromString(&der_2)));
+}
+
+// Runs VerifyNameMatchUnicodeConversionTest with prefix="unicode_bmp" for all
+// combinations of Basic Multilingual Plane-capable value types in value_type1
+// and value_type_2.
+INSTANTIATE_TEST_SUITE_P(
+ BMPConversion,
+ VerifyNameMatchUnicodeConversionTest,
+ ::testing::Combine(
+ ::testing::Values("unicode_bmp"),
+ ::testing::Combine(::testing::ValuesIn(kUnicodeBMPValueTypes),
+ ::testing::ValuesIn(kUnicodeBMPValueTypes))));
+
+// Runs VerifyNameMatchUnicodeConversionTest with prefix="unicode_supplementary"
+// for all combinations of Unicode Supplementary Plane-capable value types in
+// value_type1 and value_type_2.
+INSTANTIATE_TEST_SUITE_P(
+ SMPConversion,
+ VerifyNameMatchUnicodeConversionTest,
+ ::testing::Combine(
+ ::testing::Values("unicode_supplementary"),
+ ::testing::Combine(
+ ::testing::ValuesIn(kUnicodeSupplementaryValueTypes),
+ ::testing::ValuesIn(kUnicodeSupplementaryValueTypes))));
+
+// Matching should fail if a PrintableString contains invalid characters.
+TEST(VerifyNameMatchInvalidDataTest, FailOnInvalidPrintableStringChars) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("ascii", "PRINTABLESTRING", "unmangled", &der));
+ // Find a known location inside a PrintableString in the DER-encoded data.
+ size_t replace_location = der.find("0123456789");
+ ASSERT_NE(std::string::npos, replace_location);
+ for (int c = 0; c < 256; ++c) {
+ SCOPED_TRACE(base::NumberToString(c));
+ if (base::IsAsciiAlpha(c) || base::IsAsciiDigit(c))
+ continue;
+ switch (c) {
+ case ' ':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case '-':
+ case '.':
+ case '/':
+ case ':':
+ case '=':
+ case '?':
+ continue;
+ }
+ der.replace(replace_location, 1, 1, c);
+ // Verification should fail due to the invalid character.
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&der),
+ SequenceValueFromString(&der)));
+ std::string normalized_der;
+ CertErrors errors;
+ EXPECT_FALSE(
+ NormalizeName(SequenceValueFromString(&der), &normalized_der, &errors));
+ }
+}
+
+// Matching should fail if an IA5String contains invalid characters.
+TEST(VerifyNameMatchInvalidDataTest, FailOnInvalidIA5StringChars) {
+ std::string der;
+ ASSERT_TRUE(LoadTestData("ascii", "mixed", "rdn_dupetype_sorting_1", &der));
+ // Find a known location inside an IA5String in the DER-encoded data.
+ size_t replace_location = der.find("eXaMple");
+ ASSERT_NE(std::string::npos, replace_location);
+ for (int c = 0; c < 256; ++c) {
+ SCOPED_TRACE(base::NumberToString(c));
+ der.replace(replace_location, 1, 1, c);
+ bool expected_result = (c <= 127);
+ EXPECT_EQ(expected_result, VerifyNameMatch(SequenceValueFromString(&der),
+ SequenceValueFromString(&der)));
+ std::string normalized_der;
+ CertErrors errors;
+ EXPECT_EQ(expected_result, NormalizeName(SequenceValueFromString(&der),
+ &normalized_der, &errors));
+ }
+}
+
+TEST(VerifyNameMatchInvalidDataTest, FailOnAttributeTypeAndValueExtraData) {
+ std::string invalid;
+ ASSERT_TRUE(
+ LoadTestData("invalid", "AttributeTypeAndValue", "extradata", &invalid));
+ // Verification should fail due to extra element in AttributeTypeAndValue
+ // sequence.
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&invalid),
+ SequenceValueFromString(&invalid)));
+ std::string normalized_der;
+ CertErrors errors;
+ EXPECT_FALSE(NormalizeName(SequenceValueFromString(&invalid), &normalized_der,
+ &errors));
+}
+
+TEST(VerifyNameMatchInvalidDataTest, FailOnAttributeTypeAndValueShort) {
+ std::string invalid;
+ ASSERT_TRUE(LoadTestData("invalid", "AttributeTypeAndValue", "onlyOneElement",
+ &invalid));
+ // Verification should fail due to AttributeTypeAndValue sequence having only
+ // one element.
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&invalid),
+ SequenceValueFromString(&invalid)));
+ std::string normalized_der;
+ CertErrors errors;
+ EXPECT_FALSE(NormalizeName(SequenceValueFromString(&invalid), &normalized_der,
+ &errors));
+}
+
+TEST(VerifyNameMatchInvalidDataTest, FailOnAttributeTypeAndValueEmpty) {
+ std::string invalid;
+ ASSERT_TRUE(
+ LoadTestData("invalid", "AttributeTypeAndValue", "empty", &invalid));
+ // Verification should fail due to empty AttributeTypeAndValue sequence.
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&invalid),
+ SequenceValueFromString(&invalid)));
+ std::string normalized_der;
+ CertErrors errors;
+ EXPECT_FALSE(NormalizeName(SequenceValueFromString(&invalid), &normalized_der,
+ &errors));
+}
+
+TEST(VerifyNameMatchInvalidDataTest, FailOnBadAttributeType) {
+ std::string invalid;
+ ASSERT_TRUE(LoadTestData("invalid", "AttributeTypeAndValue",
+ "badAttributeType", &invalid));
+ // Verification should fail due to Attribute Type not being an OID.
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&invalid),
+ SequenceValueFromString(&invalid)));
+ std::string normalized_der;
+ CertErrors errors;
+ EXPECT_FALSE(NormalizeName(SequenceValueFromString(&invalid), &normalized_der,
+ &errors));
+}
+
+TEST(VerifyNameMatchInvalidDataTest, FailOnAttributeTypeAndValueNotSequence) {
+ std::string invalid;
+ ASSERT_TRUE(LoadTestData("invalid", "AttributeTypeAndValue", "setNotSequence",
+ &invalid));
+ // Verification should fail due to AttributeTypeAndValue being a Set instead
+ // of a Sequence.
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&invalid),
+ SequenceValueFromString(&invalid)));
+ std::string normalized_der;
+ CertErrors errors;
+ EXPECT_FALSE(NormalizeName(SequenceValueFromString(&invalid), &normalized_der,
+ &errors));
+}
+
+TEST(VerifyNameMatchInvalidDataTest, FailOnRdnNotSet) {
+ std::string invalid;
+ ASSERT_TRUE(LoadTestData("invalid", "RDN", "sequenceInsteadOfSet", &invalid));
+ // Verification should fail due to RDN being a Sequence instead of a Set.
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&invalid),
+ SequenceValueFromString(&invalid)));
+ std::string normalized_der;
+ CertErrors errors;
+ EXPECT_FALSE(NormalizeName(SequenceValueFromString(&invalid), &normalized_der,
+ &errors));
+}
+
+TEST(VerifyNameMatchInvalidDataTest, FailOnEmptyRdn) {
+ std::string invalid;
+ ASSERT_TRUE(LoadTestData("invalid", "RDN", "empty", &invalid));
+ // Verification should fail due to RDN having zero AttributeTypeAndValues.
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&invalid),
+ SequenceValueFromString(&invalid)));
+ std::string normalized_der;
+ CertErrors errors;
+ EXPECT_FALSE(NormalizeName(SequenceValueFromString(&invalid), &normalized_der,
+ &errors));
+}
+
+// Matching should fail if a BMPString contains surrogates.
+TEST(VerifyNameMatchInvalidDataTest, FailOnBmpStringSurrogates) {
+ std::string normal;
+ ASSERT_TRUE(LoadTestData("unicode_bmp", "BMPSTRING", "unmangled", &normal));
+ // Find a known location inside a BMPSTRING in the DER-encoded data.
+ size_t replace_location = normal.find("\x67\x71\x4e\xac");
+ ASSERT_NE(std::string::npos, replace_location);
+ // Replace with U+1D400 MATHEMATICAL BOLD CAPITAL A, which requires surrogates
+ // to represent.
+ std::string invalid =
+ normal.replace(replace_location, 4, std::string("\xd8\x35\xdc\x00", 4));
+ // Verification should fail due to the invalid codepoints.
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&invalid),
+ SequenceValueFromString(&invalid)));
+ std::string normalized_der;
+ CertErrors errors;
+ EXPECT_FALSE(NormalizeName(SequenceValueFromString(&invalid), &normalized_der,
+ &errors));
+}
+
+TEST(VerifyNameMatchTest, EmptyNameMatching) {
+ std::string empty;
+ ASSERT_TRUE(LoadTestData("valid", "Name", "empty", &empty));
+ // Empty names are equal.
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&empty),
+ SequenceValueFromString(&empty)));
+ // An empty name normalized is unchanged.
+ std::string normalized_empty_der;
+ CertErrors errors;
+ EXPECT_TRUE(NormalizeName(SequenceValueFromString(&empty),
+ &normalized_empty_der, &errors));
+ EXPECT_EQ(SequenceValueFromString(&empty), der::Input(&normalized_empty_der));
+
+ // An empty name is not equal to non-empty name.
+ std::string non_empty;
+ ASSERT_TRUE(
+ LoadTestData("ascii", "PRINTABLESTRING", "unmangled", &non_empty));
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&empty),
+ SequenceValueFromString(&non_empty)));
+ EXPECT_FALSE(VerifyNameMatch(SequenceValueFromString(&non_empty),
+ SequenceValueFromString(&empty)));
+}
+
+// Matching should succeed when the RDNs are sorted differently but are still
+// equal after normalizing.
+TEST(VerifyNameMatchRDNSorting, Simple) {
+ std::string a;
+ ASSERT_TRUE(LoadTestData("ascii", "PRINTABLESTRING", "rdn_sorting_1", &a));
+ std::string b;
+ ASSERT_TRUE(LoadTestData("ascii", "PRINTABLESTRING", "rdn_sorting_2", &b));
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&a),
+ SequenceValueFromString(&b)));
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&b),
+ SequenceValueFromString(&a)));
+}
+
+// Matching should succeed when the RDNs are sorted differently but are still
+// equal after normalizing, even in malformed RDNs that contain multiple
+// elements with the same type.
+TEST(VerifyNameMatchRDNSorting, DuplicateTypes) {
+ std::string a;
+ ASSERT_TRUE(LoadTestData("ascii", "mixed", "rdn_dupetype_sorting_1", &a));
+ std::string b;
+ ASSERT_TRUE(LoadTestData("ascii", "mixed", "rdn_dupetype_sorting_2", &b));
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&a),
+ SequenceValueFromString(&b)));
+ EXPECT_TRUE(VerifyNameMatch(SequenceValueFromString(&b),
+ SequenceValueFromString(&a)));
+}
+
+TEST(VerifyNameInSubtreeInvalidDataTest, FailOnEmptyRdn) {
+ std::string valid;
+ ASSERT_TRUE(LoadTestData("ascii", "PRINTABLESTRING", "unmangled", &valid));
+ std::string invalid;
+ ASSERT_TRUE(LoadTestData("invalid", "RDN", "empty", &invalid));
+ // For both |name| and |parent|, a RelativeDistinguishedName must have at
+ // least one AttributeTypeAndValue.
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&valid),
+ SequenceValueFromString(&invalid)));
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&invalid),
+ SequenceValueFromString(&valid)));
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&invalid),
+ SequenceValueFromString(&invalid)));
+}
+
+TEST(VerifyNameInSubtreeTest, EmptyNameMatching) {
+ std::string empty;
+ ASSERT_TRUE(LoadTestData("valid", "Name", "empty", &empty));
+ std::string non_empty;
+ ASSERT_TRUE(
+ LoadTestData("ascii", "PRINTABLESTRING", "unmangled", &non_empty));
+ // Empty name is in the subtree defined by empty name.
+ EXPECT_TRUE(VerifyNameInSubtree(SequenceValueFromString(&empty),
+ SequenceValueFromString(&empty)));
+ // Any non-empty name is in the subtree defined by empty name.
+ EXPECT_TRUE(VerifyNameInSubtree(SequenceValueFromString(&non_empty),
+ SequenceValueFromString(&empty)));
+ // Empty name is not in the subtree defined by non-empty name.
+ EXPECT_FALSE(VerifyNameInSubtree(SequenceValueFromString(&empty),
+ SequenceValueFromString(&non_empty)));
+}
+
+// Verify that the normalized output matches the pre-generated expected value
+// for a single larger input that exercises all of the string types, unicode
+// (basic and supplemental planes), whitespace collapsing, case folding, as
+// well as SET sorting.
+TEST(NameNormalizationTest, TestEverything) {
+ std::string expected_normalized_der;
+ ASSERT_TRUE(
+ LoadTestData("unicode", "mixed", "normalized", &expected_normalized_der));
+
+ std::string raw_der;
+ ASSERT_TRUE(LoadTestData("unicode", "mixed", "unnormalized", &raw_der));
+ std::string normalized_der;
+ CertErrors errors;
+ ASSERT_TRUE(NormalizeName(SequenceValueFromString(&raw_der), &normalized_der,
+ &errors));
+ EXPECT_EQ(SequenceValueFromString(&expected_normalized_der),
+ der::Input(&normalized_der));
+ // Re-normalizing an already normalized Name should not change it.
+ std::string renormalized_der;
+ ASSERT_TRUE(
+ NormalizeName(der::Input(&normalized_der), &renormalized_der, &errors));
+ EXPECT_EQ(normalized_der, renormalized_der);
+}
+
+// Unknown AttributeValue types normalize as-is, even non-primitive tags.
+TEST(NameNormalizationTest, NormalizeCustom) {
+ std::string raw_der;
+ ASSERT_TRUE(LoadTestData("custom", "custom", "normalized", &raw_der));
+
+ std::string normalized_der;
+ CertErrors errors;
+ ASSERT_TRUE(NormalizeName(SequenceValueFromString(&raw_der), &normalized_der,
+ &errors));
+ EXPECT_EQ(SequenceValueFromString(&raw_der), der::Input(&normalized_der));
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/verify_name_match_verifynameinsubtree_fuzzer.cc b/chromium/net/cert/pki/verify_name_match_verifynameinsubtree_fuzzer.cc
new file mode 100644
index 00000000000..996a6353342
--- /dev/null
+++ b/chromium/net/cert/pki/verify_name_match_verifynameinsubtree_fuzzer.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 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 "net/cert/pki/verify_name_match.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <vector>
+
+#include "net/der/input.h"
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fuzzed_data(data, size);
+
+ // Intentionally using uint16_t here to avoid empty |second_part|.
+ size_t first_part_size = fuzzed_data.ConsumeIntegral<uint16_t>();
+ std::vector<uint8_t> first_part =
+ fuzzed_data.ConsumeBytes<uint8_t>(first_part_size);
+ std::vector<uint8_t> second_part =
+ fuzzed_data.ConsumeRemainingBytes<uint8_t>();
+
+ net::der::Input in1(first_part.data(), first_part.size());
+ net::der::Input in2(second_part.data(), second_part.size());
+ bool match = net::VerifyNameInSubtree(in1, in2);
+ bool reverse_order_match = net::VerifyNameInSubtree(in2, in1);
+ // If both InSubtree matches are true, then in1 == in2 (modulo normalization).
+ if (match && reverse_order_match)
+ CHECK(net::VerifyNameMatch(in1, in2));
+ return 0;
+}
diff --git a/chromium/net/cert/pki/verify_signed_data.cc b/chromium/net/cert/pki/verify_signed_data.cc
new file mode 100644
index 00000000000..5dc399129a2
--- /dev/null
+++ b/chromium/net/cert/pki/verify_signed_data.cc
@@ -0,0 +1,217 @@
+// Copyright 2015 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 "net/cert/pki/verify_signed_data.h"
+
+#include "base/numerics/safe_math.h"
+#include "crypto/openssl_util.h"
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/signature_algorithm.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "net/der/parser.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "third_party/boringssl/src/include/openssl/digest.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "third_party/boringssl/src/include/openssl/rsa.h"
+
+namespace net {
+
+// Parses an RSA public key or EC public key from SPKI to an EVP_PKEY. Returns
+// true on success.
+//
+// This function only recognizes the "pk-rsa" (rsaEncryption) flavor of RSA
+// public key from RFC 5912.
+//
+// pk-rsa PUBLIC-KEY ::= {
+// IDENTIFIER rsaEncryption
+// KEY RSAPublicKey
+// PARAMS TYPE NULL ARE absent
+// -- Private key format not in this module --
+// CERT-KEY-USAGE {digitalSignature, nonRepudiation,
+// keyEncipherment, dataEncipherment, keyCertSign, cRLSign}
+// }
+//
+// COMPATIBILITY NOTE: RFC 5912 and RFC 3279 are in disagreement on the value
+// of parameters for rsaEncryption. Whereas RFC 5912 says they must be absent,
+// RFC 3279 says they must be NULL:
+//
+// The rsaEncryption OID is intended to be used in the algorithm field
+// of a value of type AlgorithmIdentifier. The parameters field MUST
+// have ASN.1 type NULL for this algorithm identifier.
+//
+// Following RFC 3279 in this case.
+//
+// In the case of parsing EC keys, RFC 5912 describes all the ECDSA
+// signature algorithms as requiring a public key of type "pk-ec":
+//
+// pk-ec PUBLIC-KEY ::= {
+// IDENTIFIER id-ecPublicKey
+// KEY ECPoint
+// PARAMS TYPE ECParameters ARE required
+// -- Private key format not in this module --
+// CERT-KEY-USAGE { digitalSignature, nonRepudiation, keyAgreement,
+// keyCertSign, cRLSign }
+// }
+//
+// Moreover RFC 5912 stipulates what curves are allowed. The ECParameters
+// MUST NOT use an implicitCurve or specificCurve for PKIX:
+//
+// ECParameters ::= CHOICE {
+// namedCurve CURVE.&id({NamedCurve})
+// -- implicitCurve NULL
+// -- implicitCurve MUST NOT be used in PKIX
+// -- specifiedCurve SpecifiedCurve
+// -- specifiedCurve MUST NOT be used in PKIX
+// -- Details for specifiedCurve can be found in [X9.62]
+// -- Any future additions to this CHOICE should be coordinated
+// -- with ANSI X.9.
+// }
+// -- If you need to be able to decode ANSI X.9 parameter structures,
+// -- uncomment the implicitCurve and specifiedCurve above, and also
+// -- uncomment the following:
+// --(WITH COMPONENTS {namedCurve PRESENT})
+//
+// The namedCurves are extensible. The ones described by RFC 5912 are:
+//
+// NamedCurve CURVE ::= {
+// { ID secp192r1 } | { ID sect163k1 } | { ID sect163r2 } |
+// { ID secp224r1 } | { ID sect233k1 } | { ID sect233r1 } |
+// { ID secp256r1 } | { ID sect283k1 } | { ID sect283r1 } |
+// { ID secp384r1 } | { ID sect409k1 } | { ID sect409r1 } |
+// { ID secp521r1 } | { ID sect571k1 } | { ID sect571r1 },
+// ... -- Extensible
+// }
+bool ParsePublicKey(const der::Input& public_key_spki,
+ bssl::UniquePtr<EVP_PKEY>* public_key) {
+ // Parse the SPKI to an EVP_PKEY.
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ CBS cbs;
+ CBS_init(&cbs, public_key_spki.UnsafeData(), public_key_spki.Length());
+ public_key->reset(EVP_parse_public_key(&cbs));
+ if (!*public_key || CBS_len(&cbs) != 0) {
+ public_key->reset();
+ return false;
+ }
+ return true;
+}
+
+bool VerifySignedData(SignatureAlgorithm algorithm,
+ const der::Input& signed_data,
+ const der::BitString& signature_value,
+ EVP_PKEY* public_key) {
+ int expected_pkey_id = 1;
+ const EVP_MD* digest = nullptr;
+ bool is_rsa_pss = false;
+ switch (algorithm) {
+ case SignatureAlgorithm::kRsaPkcs1Sha1:
+ expected_pkey_id = EVP_PKEY_RSA;
+ digest = EVP_sha1();
+ break;
+ case SignatureAlgorithm::kRsaPkcs1Sha256:
+ expected_pkey_id = EVP_PKEY_RSA;
+ digest = EVP_sha256();
+ break;
+ case SignatureAlgorithm::kRsaPkcs1Sha384:
+ expected_pkey_id = EVP_PKEY_RSA;
+ digest = EVP_sha384();
+ break;
+ case SignatureAlgorithm::kRsaPkcs1Sha512:
+ expected_pkey_id = EVP_PKEY_RSA;
+ digest = EVP_sha512();
+ break;
+
+ case SignatureAlgorithm::kEcdsaSha1:
+ expected_pkey_id = EVP_PKEY_EC;
+ digest = EVP_sha1();
+ break;
+ case SignatureAlgorithm::kEcdsaSha256:
+ expected_pkey_id = EVP_PKEY_EC;
+ digest = EVP_sha256();
+ break;
+ case SignatureAlgorithm::kEcdsaSha384:
+ expected_pkey_id = EVP_PKEY_EC;
+ digest = EVP_sha384();
+ break;
+ case SignatureAlgorithm::kEcdsaSha512:
+ expected_pkey_id = EVP_PKEY_EC;
+ digest = EVP_sha512();
+ break;
+
+ case SignatureAlgorithm::kRsaPssSha256:
+ expected_pkey_id = EVP_PKEY_RSA;
+ digest = EVP_sha256();
+ is_rsa_pss = true;
+ break;
+ case SignatureAlgorithm::kRsaPssSha384:
+ expected_pkey_id = EVP_PKEY_RSA;
+ digest = EVP_sha384();
+ is_rsa_pss = true;
+ break;
+ case SignatureAlgorithm::kRsaPssSha512:
+ expected_pkey_id = EVP_PKEY_RSA;
+ digest = EVP_sha512();
+ is_rsa_pss = true;
+ break;
+
+ case SignatureAlgorithm::kDsaSha1:
+ case SignatureAlgorithm::kDsaSha256:
+ case SignatureAlgorithm::kRsaPkcs1Md2:
+ case SignatureAlgorithm::kRsaPkcs1Md4:
+ case SignatureAlgorithm::kRsaPkcs1Md5:
+ // DSA, MD2, MD4, and MD5 are not supported. See
+ // https://crbug.com/1321688.
+ return false;
+ }
+
+ if (expected_pkey_id != EVP_PKEY_id(public_key))
+ return false;
+
+ // For the supported algorithms the signature value must be a whole
+ // number of bytes.
+ if (signature_value.unused_bits() != 0)
+ return false;
+ const der::Input& signature_value_bytes = signature_value.bytes();
+
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ bssl::ScopedEVP_MD_CTX ctx;
+ EVP_PKEY_CTX* pctx = nullptr; // Owned by |ctx|.
+
+ if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, nullptr, public_key))
+ return false;
+
+ if (is_rsa_pss) {
+ // All supported RSASSA-PSS algorithms match signing and MGF-1 digest. They
+ // also use the digest length as the salt length, which is specified with -1
+ // in OpenSSL's API.
+ if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
+ !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1)) {
+ return false;
+ }
+ }
+
+ if (!EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(),
+ signed_data.Length())) {
+ return false;
+ }
+
+ return 1 == EVP_DigestVerifyFinal(ctx.get(),
+ signature_value_bytes.UnsafeData(),
+ signature_value_bytes.Length());
+}
+
+bool VerifySignedData(SignatureAlgorithm algorithm,
+ const der::Input& signed_data,
+ const der::BitString& signature_value,
+ const der::Input& public_key_spki) {
+ bssl::UniquePtr<EVP_PKEY> public_key;
+ if (!ParsePublicKey(public_key_spki, &public_key))
+ return false;
+ return VerifySignedData(algorithm, signed_data, signature_value,
+ public_key.get());
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pki/verify_signed_data.h b/chromium/net/cert/pki/verify_signed_data.h
new file mode 100644
index 00000000000..b904992dc1c
--- /dev/null
+++ b/chromium/net/cert/pki/verify_signed_data.h
@@ -0,0 +1,49 @@
+// Copyright 2015 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 NET_CERT_PKI_VERIFY_SIGNED_DATA_H_
+#define NET_CERT_PKI_VERIFY_SIGNED_DATA_H_
+
+#include "crypto/openssl_util.h"
+#include "net/base/net_export.h"
+#include "net/cert/pki/signature_algorithm.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+
+namespace net {
+
+namespace der {
+class BitString;
+class Input;
+} // namespace der
+
+// Verifies that |signature_value| is a valid signature of |signed_data| using
+// the algorithm |algorithm| and the public key |public_key|.
+//
+// |algorithm| - The parsed AlgorithmIdentifier
+// |signed_data| - The blob of data to verify
+// |signature_value| - The BIT STRING for the signature's value
+// |public_key| - The parsed (non-null) public key.
+//
+// Returns true if verification was successful.
+[[nodiscard]] NET_EXPORT bool VerifySignedData(
+ SignatureAlgorithm algorithm,
+ const der::Input& signed_data,
+ const der::BitString& signature_value,
+ EVP_PKEY* public_key);
+
+// Same as above overload, only the public key is inputted as an SPKI and will
+// be parsed internally.
+[[nodiscard]] NET_EXPORT bool VerifySignedData(
+ SignatureAlgorithm algorithm,
+ const der::Input& signed_data,
+ const der::BitString& signature_value,
+ const der::Input& public_key_spki);
+
+[[nodiscard]] NET_EXPORT bool ParsePublicKey(
+ const der::Input& public_key_spki,
+ bssl::UniquePtr<EVP_PKEY>* public_key);
+
+} // namespace net
+
+#endif // NET_CERT_PKI_VERIFY_SIGNED_DATA_H_
diff --git a/chromium/net/cert/pki/verify_signed_data_unittest.cc b/chromium/net/cert/pki/verify_signed_data_unittest.cc
new file mode 100644
index 00000000000..8a0a26e9cb0
--- /dev/null
+++ b/chromium/net/cert/pki/verify_signed_data_unittest.cc
@@ -0,0 +1,190 @@
+// Copyright 2015 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 "net/cert/pki/verify_signed_data.h"
+
+#include <memory>
+#include <set>
+
+#include "net/cert/pki/cert_errors.h"
+#include "net/cert/pki/signature_algorithm.h"
+#include "net/cert/pki/test_helpers.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+#include "net/der/parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+
+namespace {
+
+enum VerifyResult {
+ SUCCESS,
+ FAILURE,
+};
+
+// Reads test data from |file_name| and runs VerifySignedData() over its
+// inputs.
+//
+// If expected_result was SUCCESS then the test will only succeed if
+// VerifySignedData() returns true.
+//
+// If expected_result was FAILURE then the test will only succeed if
+// VerifySignedData() returns false.
+void RunTestCase(VerifyResult expected_result, const char* file_name) {
+ std::string path =
+ std::string("net/data/verify_signed_data_unittest/") + file_name;
+
+ std::string public_key;
+ std::string algorithm;
+ std::string signed_data;
+ std::string signature_value;
+
+ const PemBlockMapping mappings[] = {
+ {"PUBLIC KEY", &public_key},
+ {"ALGORITHM", &algorithm},
+ {"DATA", &signed_data},
+ {"SIGNATURE", &signature_value},
+ };
+
+ ASSERT_TRUE(ReadTestDataFromPemFile(path, mappings));
+
+ CertErrors algorithm_errors;
+ absl::optional<SignatureAlgorithm> signature_algorithm =
+ ParseSignatureAlgorithm(der::Input(&algorithm), &algorithm_errors);
+ ASSERT_TRUE(signature_algorithm) << algorithm_errors.ToDebugString();
+
+ der::Parser signature_value_parser((der::Input(&signature_value)));
+ absl::optional<der::BitString> signature_value_bit_string =
+ signature_value_parser.ReadBitString();
+ ASSERT_TRUE(signature_value_bit_string.has_value())
+ << "The signature value is not a valid BIT STRING";
+
+ bool expected_result_bool = expected_result == SUCCESS;
+
+ bool result = VerifySignedData(*signature_algorithm, der::Input(&signed_data),
+ signature_value_bit_string.value(),
+ der::Input(&public_key));
+
+ EXPECT_EQ(expected_result_bool, result);
+}
+
+// Read the descriptions in the test files themselves for details on what is
+// being tested.
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha1) {
+ RunTestCase(SUCCESS, "rsa-pkcs1-sha1.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha256) {
+ RunTestCase(SUCCESS, "rsa-pkcs1-sha256.pem");
+}
+
+TEST(VerifySignedDataTest, Rsa2048Pkcs1Sha512) {
+ RunTestCase(SUCCESS, "rsa2048-pkcs1-sha512.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha256KeyEncodedBer) {
+ RunTestCase(FAILURE, "rsa-pkcs1-sha256-key-encoded-ber.pem");
+}
+
+TEST(VerifySignedDataTest, EcdsaSecp384r1Sha256) {
+ RunTestCase(SUCCESS, "ecdsa-secp384r1-sha256.pem");
+}
+
+TEST(VerifySignedDataTest, EcdsaPrime256v1Sha512) {
+ RunTestCase(SUCCESS, "ecdsa-prime256v1-sha512.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPssSha256) {
+ RunTestCase(SUCCESS, "rsa-pss-sha256.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPssSha256WrongSalt) {
+ RunTestCase(FAILURE, "rsa-pss-sha256-wrong-salt.pem");
+}
+
+TEST(VerifySignedDataTest, EcdsaSecp384r1Sha256CorruptedData) {
+ RunTestCase(FAILURE, "ecdsa-secp384r1-sha256-corrupted-data.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha1WrongAlgorithm) {
+ RunTestCase(FAILURE, "rsa-pkcs1-sha1-wrong-algorithm.pem");
+}
+
+TEST(VerifySignedDataTest, EcdsaPrime256v1Sha512WrongSignatureFormat) {
+ RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-wrong-signature-format.pem");
+}
+
+TEST(VerifySignedDataTest, EcdsaUsingRsaKey) {
+ RunTestCase(FAILURE, "ecdsa-using-rsa-key.pem");
+}
+
+TEST(VerifySignedDataTest, RsaUsingEcKey) {
+ RunTestCase(FAILURE, "rsa-using-ec-key.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha1BadKeyDerNull) {
+ RunTestCase(FAILURE, "rsa-pkcs1-sha1-bad-key-der-null.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha1BadKeyDerLength) {
+ RunTestCase(FAILURE, "rsa-pkcs1-sha1-bad-key-der-length.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha256UsingEcdsaAlgorithm) {
+ RunTestCase(FAILURE, "rsa-pkcs1-sha256-using-ecdsa-algorithm.pem");
+}
+
+TEST(VerifySignedDataTest, EcdsaPrime256v1Sha512UsingRsaAlgorithm) {
+ RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-using-rsa-algorithm.pem");
+}
+
+TEST(VerifySignedDataTest, EcdsaPrime256v1Sha512UsingEcdhKey) {
+ RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-using-ecdh-key.pem");
+}
+
+TEST(VerifySignedDataTest, EcdsaPrime256v1Sha512UsingEcmqvKey) {
+ RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-using-ecmqv-key.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha1KeyParamsAbsent) {
+ RunTestCase(FAILURE, "rsa-pkcs1-sha1-key-params-absent.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha1UsingPssKeyNoParams) {
+ RunTestCase(FAILURE, "rsa-pkcs1-sha1-using-pss-key-no-params.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPssSha256UsingPssKeyWithParams) {
+ // We do not support RSA-PSS SPKIs.
+ RunTestCase(FAILURE, "rsa-pss-sha256-using-pss-key-with-params.pem");
+}
+
+TEST(VerifySignedDataTest, EcdsaPrime256v1Sha512SpkiParamsNull) {
+ RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-spki-params-null.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha256UsingIdEaRsa) {
+ RunTestCase(FAILURE, "rsa-pkcs1-sha256-using-id-ea-rsa.pem");
+}
+
+TEST(VerifySignedDataTest, RsaPkcs1Sha256SpkiNonNullParams) {
+ RunTestCase(FAILURE, "rsa-pkcs1-sha256-spki-non-null-params.pem");
+}
+
+TEST(VerifySignedDataTest, EcdsaPrime256v1Sha512UnusedBitsSignature) {
+ RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-unused-bits-signature.pem");
+}
+
+TEST(VerifySignedDataTest, Ecdsa384) {
+ // Using the regular policy both secp384r1 and secp256r1 should be accepted.
+ RunTestCase(SUCCESS, "ecdsa-secp384r1-sha256.pem");
+ RunTestCase(SUCCESS, "ecdsa-prime256v1-sha512.pem");
+}
+
+} // namespace
+
+} // namespace net