Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(162)

Unified Diff: net/cert/cert_verify_proc_builtin.cc

Issue 2755483008: Add initial CertVerifyProcBuiltin. (Closed)
Patch Set: type notimplemented properly Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/cert/cert_verify_proc_builtin.h ('k') | net/cert/cert_verify_proc_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/cert/cert_verify_proc_builtin.cc
diff --git a/net/cert/cert_verify_proc_builtin.cc b/net/cert/cert_verify_proc_builtin.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0ac039942e1c9c61f002da8e01a00a75ed3d7fc1
--- /dev/null
+++ b/net/cert/cert_verify_proc_builtin.cc
@@ -0,0 +1,436 @@
+// Copyright (c) 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/cert_verify_proc_builtin.h"
+
+#include <string>
+#include <vector>
+
+#if defined(USE_NSS_CERTS)
+#include <cert.h>
+#include <pk11pub.h>
+#endif
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/sha1.h"
+#include "base/strings/string_piece.h"
+#include "crypto/sha2.h"
+#include "net/base/net_errors.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/internal/cert_errors.h"
+#include "net/cert/internal/cert_issuer_source_static.h"
+#include "net/cert/internal/parsed_certificate.h"
+#include "net/cert/internal/path_builder.h"
+#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/trust_store_collection.h"
+#include "net/cert/internal/trust_store_in_memory.h"
+#include "net/cert/internal/verify_certificate_chain.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+#include "net/der/encode_values.h"
+
+#if defined(USE_NSS_CERTS)
+#include "crypto/nss_util.h"
+#include "net/cert/internal/cert_issuer_source_nss.h"
+#include "net/cert/internal/trust_store_nss.h"
+#include "net/cert/scoped_nss_types.h"
+#endif
+
+namespace net {
+
+namespace {
+
+class CertVerifyProcBuiltin : public CertVerifyProc {
+ public:
+ CertVerifyProcBuiltin();
+
+ bool SupportsAdditionalTrustAnchors() const override;
+ bool SupportsOCSPStapling() const override;
+
+ protected:
+ ~CertVerifyProcBuiltin() override;
+
+ private:
+ int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ const std::string& ocsp_response,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) override;
+};
+
+CertVerifyProcBuiltin::CertVerifyProcBuiltin() {}
+
+CertVerifyProcBuiltin::~CertVerifyProcBuiltin() {}
+
+bool CertVerifyProcBuiltin::SupportsAdditionalTrustAnchors() const {
+ return true;
+}
+
+bool CertVerifyProcBuiltin::SupportsOCSPStapling() const {
+ // TODO(crbug.com/649017): Implement.
+ return false;
+}
+
+scoped_refptr<ParsedCertificate> ParseCertificateFromOSHandle(
+ X509Certificate::OSCertHandle cert_handle,
+ CertErrors* errors) {
+ std::string cert_bytes;
+ if (!X509Certificate::GetDEREncoded(cert_handle, &cert_bytes))
+ return nullptr;
+ return ParsedCertificate::Create(x509_util::CreateCryptoBuffer(cert_bytes),
+ {}, errors);
+}
+
+void AddIntermediatesToIssuerSource(X509Certificate* x509_cert,
+ CertIssuerSourceStatic* intermediates) {
+ const X509Certificate::OSCertHandles& cert_handles =
+ x509_cert->GetIntermediateCertificates();
+ CertErrors errors;
+ for (auto it = cert_handles.begin(); it != cert_handles.end(); ++it) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromOSHandle(*it, &errors);
+ if (cert)
+ intermediates->AddCert(std::move(cert));
+ // TODO(crbug.com/634443): Surface these parsing errors?
+ }
+}
+
+// The SystemTrustStore interface augments the TrustStore interface with some
+// additional functionality:
+//
+// * Determine if a trust anchor was one of the known roots
+// * Determine if a trust anchor was one of the "extra" ones that
+// was specified during verification.
+//
+// Implementations of SystemTrustStore create an effective trust
+// store that is the composition of:
+//
+// (1) System trust store
+// (2) |additional_trust_anchors|.
+// (3) Test certificates (if they are separate from system trust store)
+class SystemTrustStore {
+ public:
+ virtual ~SystemTrustStore() {}
+
+ virtual TrustStore* GetTrustStore() = 0;
+
+ // TODO(eroman): Can this be exposed through the TrustStore
+ // interface instead?
+ virtual CertIssuerSource* GetCertIssuerSource() = 0;
+
+ // IsKnownRoot returns true if the given trust anchor is a standard one (as
+ // opposed to a user-installed root)
+ virtual bool IsKnownRoot(
+ const scoped_refptr<TrustAnchor>& trust_anchor) const = 0;
+
+ virtual bool IsAdditionalTrustAnchor(
+ const scoped_refptr<TrustAnchor>& trust_anchor) const = 0;
+};
+
+#if defined(USE_NSS_CERTS)
+class SystemTrustStoreNSS : public SystemTrustStore {
+ public:
+ explicit SystemTrustStoreNSS(const CertificateList& additional_trust_anchors)
+ : trust_store_nss_(trustSSL) {
+ CertErrors errors;
+
+ trust_store_.AddTrustStore(&additional_trust_store_);
+ for (const auto& x509_cert : additional_trust_anchors) {
+ scoped_refptr<ParsedCertificate> cert =
+ ParseCertificateFromOSHandle(x509_cert->os_cert_handle(), &errors);
+ if (cert) {
+ additional_trust_store_.AddTrustAnchor(
+ TrustAnchor::CreateFromCertificateNoConstraints(std::move(cert)));
+ }
+ // TODO(eroman): Surface parsing errors of additional trust anchor.
+ }
+
+ trust_store_.AddTrustStore(&trust_store_nss_);
+ }
+
+ TrustStore* GetTrustStore() override { return &trust_store_; }
+
+ CertIssuerSource* GetCertIssuerSource() override {
+ return &cert_issuer_source_nss_;
+ }
+
+ // IsKnownRoot returns true if the given trust anchor is a standard one (as
+ // opposed to a user-installed root)
+ bool IsKnownRoot(
+ const scoped_refptr<TrustAnchor>& trust_anchor) const override {
+ // TODO(eroman): Based on how the TrustAnchors are created by this
+ // integration, there will always be an associated certificate. However this
+ // contradicts the API for TrustAnchor that states it is optional.
+ DCHECK(trust_anchor->cert());
+
+ // TODO(eroman): The overall approach of IsKnownRoot() is inefficient -- it
+ // requires searching for the trust anchor by DER in NSS, however path
+ // building already had a handle to it.
+ SECItem der_cert;
+ der_cert.data =
+ const_cast<uint8_t*>(trust_anchor->cert()->der_cert().UnsafeData());
+ der_cert.len = trust_anchor->cert()->der_cert().Length();
+ der_cert.type = siDERCertBuffer;
+ ScopedCERTCertificate nss_cert(
+ CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), &der_cert));
+ if (!nss_cert)
+ return false;
+
+ return IsKnownRoot(nss_cert.get());
+ }
+
+ bool IsAdditionalTrustAnchor(
+ const scoped_refptr<TrustAnchor>& trust_anchor) const override {
+ return additional_trust_store_.Contains(trust_anchor.get());
+ }
+
+ private:
+ // TODO(eroman): This function was copied verbatim from
+ // cert_verify_proc_nss.cc
+ //
+ // IsKnownRoot returns true if the given certificate is one that we believe
+ // is a standard (as opposed to user-installed) root.
+ bool IsKnownRoot(CERTCertificate* root) const {
+ if (!root || !root->slot)
+ return false;
+
+ // This magic name is taken from
+ // http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/security/nss/lib/ckfw/builtins/constants.c&rev=1.13&mark=86,89#79
+ return 0 == strcmp(PK11_GetSlotName(root->slot), "NSS Builtin Objects");
+ }
+
+ TrustStoreCollection trust_store_;
+ TrustStoreInMemory additional_trust_store_;
+
+ TrustStoreNSS trust_store_nss_;
+ CertIssuerSourceNSS cert_issuer_source_nss_;
+};
+#endif
+
+std::unique_ptr<SystemTrustStore> CreateSystemTrustStore(
+ const CertificateList& additional_trust_anchors) {
+#if defined(USE_NSS_CERTS)
+ return base::MakeUnique<SystemTrustStoreNSS>(additional_trust_anchors);
+#else
+ // TODO(crbug.com/649017): Integrate with other system trust stores.
+ NOTIMPLEMENTED();
+ return nullptr;
+#endif
+}
+
+// Appends the SHA1 and SHA256 hashes of |spki_bytes| to |*hashes|.
+void AppendPublicKeyHashes(const der::Input& spki_bytes,
+ HashValueVector* hashes) {
+ HashValue sha1(HASH_VALUE_SHA1);
+ base::SHA1HashBytes(spki_bytes.UnsafeData(), spki_bytes.Length(),
+ sha1.data());
+ hashes->push_back(sha1);
+
+ HashValue sha256(HASH_VALUE_SHA256);
+ crypto::SHA256HashString(spki_bytes.AsStringPiece(), sha256.data(),
+ crypto::kSHA256Length);
+ hashes->push_back(sha256);
+}
+
+// Appends the SubjectPublicKeyInfo hashes for all certificates (and trust
+// anchor) in |partial_path| to |*hashes|.
+void AppendPublicKeyHashes(const CertPathBuilder::ResultPath& partial_path,
+ HashValueVector* hashes) {
+ for (const scoped_refptr<ParsedCertificate>& cert : partial_path.path.certs)
+ AppendPublicKeyHashes(cert->tbs().spki_tlv, hashes);
+
+ if (partial_path.path.trust_anchor)
+ AppendPublicKeyHashes(partial_path.path.trust_anchor->spki(), hashes);
+}
+
+// Sets the bits on |cert_status| for all the errors encountered during the path
+// building of |partial_path|.
+void MapPathBuilderErrorsToCertStatus(
+ const CertPathBuilder::ResultPath& partial_path,
+ const std::string& hostname,
+ CertStatus* cert_status) {
+ // If path building was successful, there are no errors to map (there may have
+ // been warnings but they do not map to CertStatus).
+ if (partial_path.valid)
+ return;
+
+ LOG(ERROR) << "CertVerifyProcBuiltin for " << hostname << " failed:\n"
+ << partial_path.errors.ToDebugString();
+
+ if (partial_path.errors.ContainsError(kRsaModulusTooSmall))
+ *cert_status |= CERT_STATUS_WEAK_KEY;
+
+ if (partial_path.errors.ContainsError(kValidityFailedNotAfter) ||
+ partial_path.errors.ContainsError(kValidityFailedNotBefore)) {
+ *cert_status |= CERT_STATUS_DATE_INVALID;
+ }
+
+ // IMPORTANT: If the path was invalid for a reason that was not
+ // explicity checked above, set a general error. This is important as
+ // |cert_status| is what ultimately indicates whether verification was
+ // successful or not (absense of errors implies success).
+ if (!IsCertStatusError(*cert_status))
+ *cert_status |= CERT_STATUS_INVALID;
+}
+
+X509Certificate::OSCertHandle CreateOSCertHandle(
+ const scoped_refptr<ParsedCertificate>& certificate) {
+ return X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<const char*>(certificate->der_cert().UnsafeData()),
+ certificate->der_cert().Length());
+}
+
+// Creates a X509Certificate (chain) to return as the verified result.
+//
+// * |target_cert|: The original X509Certificate that was passed in to
+// VerifyInternal()
+// * |path|: The result (possibly failed) from path building.
+scoped_refptr<X509Certificate> CreateVerifiedCertChain(
+ X509Certificate* target_cert,
+ const CertPathBuilder::ResultPath& path) {
+ X509Certificate::OSCertHandles intermediates;
+
+ // Skip the first certificate in the path as that is the target certificate
+ for (size_t i = 1; i < path.path.certs.size(); ++i)
+ intermediates.push_back(CreateOSCertHandle(path.path.certs[i]));
+
+ if (path.path.trust_anchor) {
+ // TODO(eroman): This assumes that TrustAnchor::cert() cannot be null,
+ // which disagrees with the documentation.
+ intermediates.push_back(CreateOSCertHandle(path.path.trust_anchor->cert()));
+ }
+
+ scoped_refptr<X509Certificate> result = X509Certificate::CreateFromHandle(
+ target_cert->os_cert_handle(), intermediates);
+
+ for (const X509Certificate::OSCertHandle handle : intermediates)
+ X509Certificate::FreeOSCertHandle(handle);
+
+ return result;
+}
+
+// TODO(crbug.com/649017): Make use of |flags|, |crl_set|, and |ocsp_response|.
+// Also handle key usages, policies and EV.
+//
+// Any failure short-circuits from the function must set
+// |verify_result->cert_status|.
+void DoVerify(X509Certificate* input_cert,
+ const std::string& hostname,
+ const std::string& ocsp_response,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ CertErrors errors;
+
+ // Parse the target certificate.
+ scoped_refptr<ParsedCertificate> target =
+ ParseCertificateFromOSHandle(input_cert->os_cert_handle(), &errors);
+ if (!target) {
+ // TODO(crbug.com/634443): Surface these parsing errors?
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ return;
+ }
+
+ std::unique_ptr<SystemTrustStore> trust_store =
+ CreateSystemTrustStore(additional_trust_anchors);
+
+ // TODO(eroman): The path building code in this file enforces its idea of weak
+ // keys, and separately cert_verify_proc.cc also checks the chains with its
+ // own policy. These policies should be aligned, to give path building the
+ // best chance of finding a good path.
+ // Another difference to resolve is the path building here does not check the
+ // target certificate's key strength, whereas cert_verify_proc.cc does.
+ SimpleSignaturePolicy signature_policy(1024);
+
+ // Use the current time.
+ der::GeneralizedTime verification_time;
+ if (!der::EncodeTimeAsGeneralizedTime(base::Time::Now(),
+ &verification_time)) {
+ // This really shouldn't be possible unless Time::Now() returned
+ // something crazy.
+ verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
+ return;
+ }
+
+ // Initialize the path builder.
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(target, trust_store->GetTrustStore(),
+ &signature_policy, verification_time, &result);
+
+ // Allow the path builder to discover intermediates from the trust store.
+ if (trust_store->GetCertIssuerSource())
+ path_builder.AddCertIssuerSource(trust_store->GetCertIssuerSource());
+
+ // Allow the path builder to discover the explicitly provided intermediates in
+ // |input_cert|.
+ CertIssuerSourceStatic intermediates;
+ AddIntermediatesToIssuerSource(input_cert, &intermediates);
+ path_builder.AddCertIssuerSource(&intermediates);
+
+ // TODO(crbug.com/649017): Allow the path builder to discover intermediates
+ // through AIA fetching.
+
+ path_builder.Run();
+
+ if (result.best_result_index >= result.paths.size()) {
+ // TODO(crbug.com/634443): What errors to communicate? Maybe the path
+ // builder should always return some partial path (even if just containing
+ // the target), then there is a CertErrors to test.
+ verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
+ return;
+ }
+
+ // Use the best path that was built. This could be a partial path, or it could
+ // be a valid complete path.
+ const CertPathBuilder::ResultPath& partial_path =
+ *result.paths[result.best_result_index].get();
+
+ if (partial_path.path.trust_anchor) {
+ verify_result->is_issued_by_known_root =
+ trust_store->IsKnownRoot(partial_path.path.trust_anchor);
+
+ verify_result->is_issued_by_additional_trust_anchor =
+ trust_store->IsAdditionalTrustAnchor(partial_path.path.trust_anchor);
+ } else {
+ verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
+ }
+
+ verify_result->verified_cert =
+ CreateVerifiedCertChain(input_cert, partial_path);
+
+ AppendPublicKeyHashes(partial_path, &verify_result->public_key_hashes);
+ MapPathBuilderErrorsToCertStatus(partial_path, hostname,
+ &verify_result->cert_status);
+}
+
+int CertVerifyProcBuiltin::VerifyInternal(
+ X509Certificate* input_cert,
+ const std::string& hostname,
+ const std::string& ocsp_response,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ DoVerify(input_cert, hostname, ocsp_response, flags, crl_set,
+ additional_trust_anchors, verify_result);
+
+ return IsCertStatusError(verify_result->cert_status)
+ ? MapCertStatusToNetError(verify_result->cert_status)
+ : OK;
+}
+
+} // namespace
+
+scoped_refptr<CertVerifyProc> CreateCertVerifyProcBuiltin() {
+ return scoped_refptr<CertVerifyProc>(new CertVerifyProcBuiltin());
+}
+
+} // namespace net
« no previous file with comments | « net/cert/cert_verify_proc_builtin.h ('k') | net/cert/cert_verify_proc_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698