Home | History | Annotate | Download | only in cert
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/cert/multi_log_ct_verifier.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback_helpers.h"
      9 #include "base/metrics/histogram.h"
     10 #include "net/base/net_errors.h"
     11 #include "net/base/net_log.h"
     12 #include "net/cert/ct_log_verifier.h"
     13 #include "net/cert/ct_objects_extractor.h"
     14 #include "net/cert/ct_serialization.h"
     15 #include "net/cert/ct_signed_certificate_timestamp_log_param.h"
     16 #include "net/cert/ct_verify_result.h"
     17 #include "net/cert/sct_status_flags.h"
     18 #include "net/cert/x509_certificate.h"
     19 
     20 namespace net {
     21 
     22 namespace {
     23 
     24 // Record SCT verification status. This metric would help detecting presence
     25 // of unknown CT logs as well as bad deployments (invalid SCTs).
     26 void LogSCTStatusToUMA(ct::SCTVerifyStatus status) {
     27   UMA_HISTOGRAM_ENUMERATION(
     28       "Net.CertificateTransparency.SCTStatus", status, ct::SCT_STATUS_MAX);
     29 }
     30 
     31 // Record SCT origin enum. This metric measure the popularity
     32 // of the various channels of providing SCTs for a certificate.
     33 void LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin) {
     34   UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTOrigin",
     35                             origin,
     36                             ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX);
     37 }
     38 
     39 // Count the number of SCTs that were available for each SSL connection
     40 // (including SCTs embedded in the certificate).
     41 // This metric would allow measuring:
     42 // * Of all SSL connections, how many had SCTs available for validation.
     43 // * When SCTs are available, how many are available per connection.
     44 void LogNumSCTsToUMA(const ct::CTVerifyResult& result) {
     45   UMA_HISTOGRAM_CUSTOM_COUNTS("Net.CertificateTransparency.SCTsPerConnection",
     46                               result.invalid_scts.size() +
     47                                   result.verified_scts.size() +
     48                                   result.unknown_logs_scts.size(),
     49                               1,
     50                               10,
     51                               11);
     52 }
     53 
     54 }  // namespace
     55 
     56 MultiLogCTVerifier::MultiLogCTVerifier() { }
     57 
     58 MultiLogCTVerifier::~MultiLogCTVerifier() { }
     59 
     60 void MultiLogCTVerifier::AddLog(scoped_ptr<CTLogVerifier> log_verifier) {
     61   DCHECK(log_verifier);
     62   if (!log_verifier)
     63     return;
     64 
     65   linked_ptr<CTLogVerifier> log(log_verifier.release());
     66   logs_[log->key_id()] = log;
     67 }
     68 
     69 int MultiLogCTVerifier::Verify(
     70     X509Certificate* cert,
     71     const std::string& stapled_ocsp_response,
     72     const std::string& sct_list_from_tls_extension,
     73     ct::CTVerifyResult* result,
     74     const BoundNetLog& net_log)  {
     75   DCHECK(cert);
     76   DCHECK(result);
     77 
     78   result->verified_scts.clear();
     79   result->invalid_scts.clear();
     80   result->unknown_logs_scts.clear();
     81 
     82   bool has_verified_scts = false;
     83 
     84   std::string embedded_scts;
     85   if (!cert->GetIntermediateCertificates().empty() &&
     86       ct::ExtractEmbeddedSCTList(
     87           cert->os_cert_handle(),
     88           &embedded_scts)) {
     89     ct::LogEntry precert_entry;
     90 
     91     has_verified_scts =
     92         ct::GetPrecertLogEntry(
     93             cert->os_cert_handle(),
     94             cert->GetIntermediateCertificates().front(),
     95             &precert_entry) &&
     96         VerifySCTs(
     97             embedded_scts,
     98             precert_entry,
     99             ct::SignedCertificateTimestamp::SCT_EMBEDDED,
    100             result);
    101   }
    102 
    103   std::string sct_list_from_ocsp;
    104   if (!stapled_ocsp_response.empty() &&
    105       !cert->GetIntermediateCertificates().empty()) {
    106     ct::ExtractSCTListFromOCSPResponse(
    107         cert->GetIntermediateCertificates().front(), cert->serial_number(),
    108         stapled_ocsp_response, &sct_list_from_ocsp);
    109   }
    110 
    111   // Log to Net Log, after extracting SCTs but before possibly failing on
    112   // X.509 entry creation.
    113   NetLog::ParametersCallback net_log_callback =
    114       base::Bind(&NetLogRawSignedCertificateTimestampCallback,
    115           &embedded_scts, &sct_list_from_ocsp, &sct_list_from_tls_extension);
    116 
    117   net_log.AddEvent(
    118       NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED,
    119       net_log_callback);
    120 
    121   ct::LogEntry x509_entry;
    122   if (ct::GetX509LogEntry(cert->os_cert_handle(), &x509_entry)) {
    123     has_verified_scts |= VerifySCTs(
    124         sct_list_from_ocsp,
    125         x509_entry,
    126         ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
    127         result);
    128 
    129     has_verified_scts |= VerifySCTs(
    130         sct_list_from_tls_extension,
    131         x509_entry,
    132         ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
    133         result);
    134   }
    135 
    136   NetLog::ParametersCallback net_log_checked_callback =
    137       base::Bind(&NetLogSignedCertificateTimestampCallback, result);
    138 
    139   net_log.AddEvent(
    140       NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED,
    141       net_log_checked_callback);
    142 
    143   LogNumSCTsToUMA(*result);
    144 
    145   if (has_verified_scts)
    146     return OK;
    147 
    148   return ERR_CT_NO_SCTS_VERIFIED_OK;
    149 }
    150 
    151 bool MultiLogCTVerifier::VerifySCTs(
    152     const std::string& encoded_sct_list,
    153     const ct::LogEntry& expected_entry,
    154     ct::SignedCertificateTimestamp::Origin origin,
    155     ct::CTVerifyResult* result) {
    156   if (logs_.empty())
    157     return false;
    158 
    159   base::StringPiece temp(encoded_sct_list);
    160   std::vector<base::StringPiece> sct_list;
    161 
    162   if (!ct::DecodeSCTList(&temp, &sct_list))
    163     return false;
    164 
    165   bool verified = false;
    166   for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin();
    167        it != sct_list.end(); ++it) {
    168     base::StringPiece encoded_sct(*it);
    169     LogSCTOriginToUMA(origin);
    170 
    171     scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct;
    172     if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) {
    173       LogSCTStatusToUMA(ct::SCT_STATUS_NONE);
    174       // XXX(rsleevi): Should we really just skip over bad SCTs?
    175       continue;
    176     }
    177     decoded_sct->origin = origin;
    178 
    179     verified |= VerifySingleSCT(decoded_sct, expected_entry, result);
    180   }
    181 
    182   return verified;
    183 }
    184 
    185 bool MultiLogCTVerifier::VerifySingleSCT(
    186     scoped_refptr<ct::SignedCertificateTimestamp> sct,
    187     const ct::LogEntry& expected_entry,
    188     ct::CTVerifyResult* result) {
    189 
    190   // Assume this SCT is untrusted until proven otherwise.
    191   IDToLogMap::iterator it = logs_.find(sct->log_id);
    192   if (it == logs_.end()) {
    193     DVLOG(1) << "SCT does not match any known log.";
    194     result->unknown_logs_scts.push_back(sct);
    195     LogSCTStatusToUMA(ct::SCT_STATUS_LOG_UNKNOWN);
    196     return false;
    197   }
    198 
    199   sct->log_description = it->second->description();
    200 
    201   if (!it->second->Verify(expected_entry, *sct)) {
    202     DVLOG(1) << "Unable to verify SCT signature.";
    203     result->invalid_scts.push_back(sct);
    204     LogSCTStatusToUMA(ct::SCT_STATUS_INVALID);
    205     return false;
    206   }
    207 
    208   // SCT verified ok, just make sure the timestamp is legitimate.
    209   if (sct->timestamp > base::Time::Now()) {
    210     DVLOG(1) << "SCT is from the future!";
    211     result->invalid_scts.push_back(sct);
    212     LogSCTStatusToUMA(ct::SCT_STATUS_INVALID);
    213     return false;
    214   }
    215 
    216   LogSCTStatusToUMA(ct::SCT_STATUS_OK);
    217   result->verified_scts.push_back(sct);
    218   return true;
    219 }
    220 
    221 } // namespace net
    222