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