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

Unified Diff: components/cast_certificate/cast_crl.cc

Issue 2050983002: Cast device revocation checking. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: For review Created 4 years, 6 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 | « components/cast_certificate/cast_crl.h ('k') | components/cast_certificate/cast_crl_root_ca_cert_der-inc.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: components/cast_certificate/cast_crl.cc
diff --git a/components/cast_certificate/cast_crl.cc b/components/cast_certificate/cast_crl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1f1ae6531f59abcafd9e6b29b2d7959e32cbb69a
--- /dev/null
+++ b/components/cast_certificate/cast_crl.cc
@@ -0,0 +1,367 @@
+// 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 "components/cast_certificate/cast_crl.h"
+
+#include "base/base64.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/singleton.h"
+#include "components/cast_certificate/cast_cert_validator.h"
+#include "components/cast_certificate/proto/revocation.pb.h"
+#include "crypto/sha2.h"
+#include "net/cert/internal/parse_certificate.h"
+#include "net/cert/internal/parsed_certificate.h"
+#include "net/cert/internal/signature_algorithm.h"
+#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/trust_store.h"
+#include "net/cert/internal/verify_certificate_chain.h"
+#include "net/cert/internal/verify_signed_data.h"
+#include "net/cert/x509_certificate.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "net/der/parse_values.h"
+
+namespace cast_certificate {
+namespace {
+// -------------------------------------------------------------------------
+// Cast CRL trust anchors.
+// -------------------------------------------------------------------------
+
+// There is one trusted root for Cast CRL certificate chains:
+//
+// (1) CN=Cast CRL Root CA (kCastCRLRootCaDer)
+//
+// These constants are defined by the file included next:
+
+#include "components/cast_certificate/cast_crl_root_ca_cert_der-inc.h"
+
+// Singleton for the Cast CRL trust store.
+class CastCRLTrustStore {
+ public:
+ static CastCRLTrustStore* GetInstance() {
+ return base::Singleton<CastCRLTrustStore, base::LeakySingletonTraits<
+ CastCRLTrustStore>>::get();
+ }
+
+ static net::TrustStore& Get() { return GetInstance()->store_; }
+
+ private:
+ friend struct base::DefaultSingletonTraits<CastCRLTrustStore>;
+
+ CastCRLTrustStore() {
+ // Initialize the trust store with the root certificate.
+ // TODO (ryanchung): Add official Cast CRL Root here
+ // scoped_refptr<net::ParsedCertificate> root = net::ParsedCertificate::
+ // net::ParsedCertificate::CreateFromCertificateData(
+ // kCastCRLRootCaDer, sizeof(kCastCRLRootCaDer),
+ // net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE);
+ // CHECK(root);
+ // store_.AddTrustedCertificate(std::move(root));
+ }
+
+ net::TrustStore store_;
+ DISALLOW_COPY_AND_ASSIGN(CastCRLTrustStore);
+};
+
+// The parsed certificate information.
+struct CastCertificateInfo {
+ uint64_t serial_number;
+ std::string issuer_name;
+ net::der::Input spki_tlv;
+ std::string key_hash;
+};
+
+// Parses a der encoded certificate for the information for CastCertificateInfo.
+bool ParseCertificateInfo(scoped_refptr<net::ParsedCertificate>& cert,
+ CastCertificateInfo* parsed_cert) {
+ // Parse the serial number.
+ if (!net::der::ParseUint64(cert->tbs().serial_number,
+ &parsed_cert->serial_number)) {
+ return false;
+ }
+
+ // Parse the public key and calculate its hash.
sheretov 2016/06/13 19:18:35 This is wrong. We should hash the SPKI directly -
ryanchung 2016/06/21 21:45:06 OK, Done. Originally, the hash of the subject publ
+ net::der::Parser outer_parser(cert->tbs().spki_tlv);
+ net::der::Parser spki_parser;
+ net::der::BitString key_bits;
+ if (!outer_parser.ReadSequence(&spki_parser))
+ return false;
+ if (outer_parser.HasMore())
+ return false;
+ if (!spki_parser.SkipTag(net::der::kSequence))
+ return false;
+ if (!spki_parser.ReadBitString(&key_bits))
+ return false;
+ std::string public_key = key_bits.bytes().AsString();
+ parsed_cert->key_hash = crypto::SHA256HashString(public_key);
+ parsed_cert->spki_tlv = cert->tbs().spki_tlv;
+
+ // The issuer's name.
+ parsed_cert->issuer_name = cert->normalized_issuer().AsString();
+ return true;
+}
+
+// Converts a base::Time::Exploded to a net::der::GeneralizedTime.
+net::der::GeneralizedTime ConvertExplodedTime(
+ const base::Time::Exploded& exploded) {
+ net::der::GeneralizedTime result;
+ result.year = exploded.year;
+ result.month = exploded.month;
+ result.day = exploded.day_of_month;
+ result.hours = exploded.hour;
+ result.minutes = exploded.minute;
+ result.seconds = exploded.second;
+ return result;
+}
+
+// Specifies the signature verification policy.
+std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() {
+ return base::WrapUnique(new net::SimpleSignaturePolicy(2048));
+}
+
+class ScopedCheckUnreferencedCert {
+ public:
+ explicit ScopedCheckUnreferencedCert(net::ParsedCertificate* cert)
+ : cert_(cert) {}
+ ~ScopedCheckUnreferencedCert() { DCHECK(cert_->HasOneRef()); }
+
+ private:
+ net::ParsedCertificate* cert_;
+};
+
+// Verifies the CRL is signed by a trusted CRL authority at the time the CRL was
sheretov 2016/06/13 19:18:35 The CRL, including all the certs in the path shoul
ryanchung 2016/06/21 21:45:05 Added the current time here. As discussed offline,
+// issued.
+bool VerifyCRL(const Crl& crl, const TbsCrl& tbs_crl) {
+ // Verify the trust of the CRL authority.
+ base::Time signing_time =
+ base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis());
+ base::Time::Exploded signing_time_exploded;
+ signing_time.UTCExplode(&signing_time_exploded);
+ net::der::GeneralizedTime signing_time_generalized =
+ ConvertExplodedTime(signing_time_exploded);
+ std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
+ scoped_refptr<net::ParsedCertificate> parsed_cert =
+ net::ParsedCertificate::CreateFromCertificateData(
+ reinterpret_cast<const uint8_t*>(crl.signer_cert().data()),
+ crl.signer_cert().size(),
+ net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE);
+ if (parsed_cert == nullptr)
+ return false;
+ ScopedCheckUnreferencedCert ref_checker(parsed_cert.get());
+ input_chain.push_back(std::move(parsed_cert));
+
+ auto signature_policy = CreateCastSignaturePolicy();
+ if (!net::VerifyCertificateChain(input_chain, CastCRLTrustStore::Get(),
+ signature_policy.get(),
+ signing_time_generalized)) {
+ return false;
+ }
+
+ // Parse the signer certificate
+ if (input_chain.size() == 0)
+ return false;
+ CastCertificateInfo cert_info;
+ if (!ParseCertificateInfo(input_chain[0], &cert_info))
+ return false;
+
+ // Parse the signature.
+ std::string signature;
+ signature = crl.signature();
+ net::der::BitString signature_value_bit_string;
+ signature_value_bit_string =
+ net::der::BitString(net::der::Input(&signature), 0);
+
+ // Verify the signature
+ std::unique_ptr<net::SignaturePolicy> policy =
+ base::WrapUnique(new net::SimpleSignaturePolicy(2048));
+ std::unique_ptr<net::SignatureAlgorithm> signature_algorithm_type =
+ net::SignatureAlgorithm::CreateRsaPkcs1(net::DigestAlgorithm::Sha256);
+ if (!VerifySignedData(
+ *signature_algorithm_type, net::der::Input(&crl.tbs_crl()),
+ signature_value_bit_string, cert_info.spki_tlv, policy.get())) {
+ VLOG(2) << "CRL - Signature verification failed.";
+ return false;
+ }
+ return true;
+}
+
+class CastCRLImpl : public CastCRL {
+ public:
+ CastCRLImpl(const TbsCrl& tbs_crl);
+ ~CastCRLImpl() override;
+
+ bool VerifyDeviceCertRevocation(const std::vector<std::string>& certs,
+ const base::Time::Exploded& time) override;
+
+ private:
+ uint64_t issuance_time_millis_;
+ uint64_t validity_period_millis_;
+
+ // Hash of all revoked public key.
+ std::unordered_set<std::string> revoked_hashes_;
+
+ // Revoked serial number ranges indexed by issuer public key hash.
+ std::unordered_map<std::string, std::pair<uint64_t, uint64_t>>
+ revoked_serial_numbers_;
+ DISALLOW_COPY_AND_ASSIGN(CastCRLImpl);
+};
+
+CastCRLImpl::CastCRLImpl(const TbsCrl& tbs_crl) {
+ // Parse the validity information.
+ issuance_time_millis_ = tbs_crl.issuance_time_millis();
+ validity_period_millis_ = tbs_crl.validity_period_millis();
+
+ // Parse the revoked hashes.
+ for (const auto& hash : tbs_crl.revoked_public_key_hashes()) {
+ revoked_hashes_.insert(hash);
+ }
+
+ // Parse the revoked serial ranges.
+ for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
+ std::string hash = range.issuer_public_key_hash();
+ uint64_t first_serial_number = range.first_serial_number();
+ uint64_t last_serial_number = range.last_serial_number();
+ std::pair<uint64_t, uint64_t> revocation_range(first_serial_number,
+ last_serial_number);
+ std::pair<std::string, std::pair<uint64_t, uint64_t>> revocation_entry(
+ hash, revocation_range);
+ revoked_serial_numbers_.insert(revocation_entry);
+ }
+}
+
+CastCRLImpl::~CastCRLImpl() {}
+
+// Verifies the revocation status of the certificate chain, at the specified
+// time.
+bool CastCRLImpl::VerifyDeviceCertRevocation(
sheretov 2016/06/13 19:18:35 I would call this checkRevocation
ryanchung 2016/06/21 21:45:05 Done.
+ const std::vector<std::string>& certs,
+ const base::Time::Exploded& time) {
+ if (certs.empty())
+ return false;
+
+ // Check the validity of the CRl at the specified time.
+ base::Time utc_time = base::Time::FromUTCExploded(time);
+ base::Time validity_start =
+ base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(issuance_time_millis_);
+ base::Time validity_end =
+ base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(issuance_time_millis_ +
+ validity_period_millis_);
+ if (utc_time < validity_start || utc_time > validity_end) {
sheretov 2016/06/13 19:18:35 nit: utc_time >= validity_end
ryanchung 2016/06/21 21:45:06 Done.
+ VLOG(2) << "CRL expired. Perform hard fail.";
+ return false;
+ }
+
+ // Assume the certs form a valid path.
+ std::string issuer_key_hash;
+ for (int i = certs.size() - 1; i >= 0; --i) {
+ // Check public key revocation.
+ scoped_refptr<net::ParsedCertificate> parsed_cert =
+ net::ParsedCertificate::CreateFromCertificateData(
+ reinterpret_cast<const uint8_t*>(certs[i].data()), certs[i].size(),
+ net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE);
+ if (parsed_cert == nullptr)
+ return false;
+ ScopedCheckUnreferencedCert ref_checker(parsed_cert.get());
+ CastCertificateInfo cert_info;
+ if (!ParseCertificateInfo(parsed_cert, &cert_info))
+ return false;
+ if (revoked_hashes_.find(cert_info.key_hash) != revoked_hashes_.end()) {
+ VLOG(2) << "Public key is revoked.";
+ return false;
+ }
+
+ // Check serial range revocation.
+ if (issuer_key_hash.empty()) {
+ std::string issuer_cert =
+ FindCastTrustAnchorByName(cert_info.issuer_name);
sheretov 2016/06/13 19:18:35 This seems awkward here. Can we pass in the root's
ryanchung 2016/06/21 21:45:05 I've updated this function to take in the verified
+ if (issuer_cert.empty())
+ return false;
+ scoped_refptr<net::ParsedCertificate> parsed_issuer_cert =
+ net::ParsedCertificate::CreateFromCertificateData(
+ reinterpret_cast<const uint8_t*>(issuer_cert.data()),
+ issuer_cert.size(),
+ net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE);
+ if (parsed_cert == nullptr)
+ return false;
+ ScopedCheckUnreferencedCert ref_checker(parsed_issuer_cert.get());
+ CastCertificateInfo issuer_cert_info;
+ if (!ParseCertificateInfo(parsed_issuer_cert, &issuer_cert_info))
+ return false;
+ if (revoked_hashes_.find(issuer_cert_info.key_hash) !=
sheretov 2016/06/13 19:18:35 Why are we checking revocation on the trust anchor
ryanchung 2016/06/21 21:45:06 Removed.
+ revoked_hashes_.end()) {
+ VLOG(2) << "Issuer public key is revoked.";
+ return false;
+ }
+ issuer_key_hash = issuer_cert_info.key_hash;
+ }
+ DCHECK(!issuer_key_hash.empty());
+ auto issuer_revoked_serials = revoked_serial_numbers_.find(issuer_key_hash);
+ if (issuer_revoked_serials != revoked_serial_numbers_.end()) {
+ if (issuer_revoked_serials->second.first <= cert_info.serial_number &&
+ issuer_revoked_serials->second.second >= cert_info.serial_number) {
+ VLOG(2) << "Serial number is revoked";
+ return false;
+ }
+ }
+ issuer_key_hash = cert_info.key_hash;
+ }
+ return true;
+}
+
+} // namespace
+
+std::unique_ptr<CastCRL> ParseCRL(const std::string& crl_proto) {
+ CrlBundle cast_crl;
+ if (!cast_crl.ParseFromString(crl_proto)) {
+ LOG(ERROR) << "CRL - Binary could not be parsed.";
+ return nullptr;
+ }
+ TbsCrl tbs_crl;
+ Crl supported_crl;
+ bool supported_supported_crl_found = false;
sheretov 2016/06/13 19:18:35 supported_supported?
ryanchung 2016/06/21 21:45:05 Removed this due to comment below.
+ for (auto const& crl : cast_crl.crls()) {
+ if (!tbs_crl.ParseFromString(crl.tbs_crl())) {
+ LOG(WARNING) << "Binary TBS CRL could not be parsed.";
+ continue;
+ }
+ // The supported version of the CRL is version 0
+ // version 0: Spki Hash Algorithm = SHA-256
+ // Signature Algorithm = RSA-PKCS1 V1.5 with SHA-256
+ if (tbs_crl.version() == 0) {
sheretov 2016/06/13 19:18:35 Please create a constant for the CRL version, so w
ryanchung 2016/06/21 21:45:06 Done.
+ supported_crl = crl;
sheretov 2016/06/13 19:18:35 Is this assignment making a copy? Why have the tb
ryanchung 2016/06/21 21:45:05 Changed this to return in the loop.
+ supported_supported_crl_found = true;
+ break;
+ }
+ }
+ if (!supported_supported_crl_found) {
+ LOG(ERROR) << "No supported version of revocation data.";
+ return nullptr;
+ }
+ if (!VerifyCRL(supported_crl, tbs_crl)) {
+ LOG(ERROR) << "CRL - Verification failed.";
+ return nullptr;
+ }
+
+ return base::WrapUnique(new CastCRLImpl(tbs_crl));
+}
+
+bool AddCRLTrustAnchorForTest(const uint8_t* data, size_t length) {
+ scoped_refptr<net::ParsedCertificate> anchor(
+ net::ParsedCertificate::CreateFromCertificateData(
+ data, length,
+ net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE));
+ if (!anchor)
+ return false;
+ CastCRLTrustStore::Get().AddTrustedCertificate(std::move(anchor));
+ return true;
+}
+
+void ClearCRLTrustAnchorForTest() {
+ CastCRLTrustStore::Get().Clear();
+}
+
+} // namespace cast_certificate
« no previous file with comments | « components/cast_certificate/cast_crl.h ('k') | components/cast_certificate/cast_crl_root_ca_cert_der-inc.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698