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 "net/base/net_errors.h"
     10 #include "net/base/net_log.h"
     11 #include "net/cert/ct_log_verifier.h"
     12 #include "net/cert/ct_objects_extractor.h"
     13 #include "net/cert/ct_serialization.h"
     14 #include "net/cert/ct_signed_certificate_timestamp_log_param.h"
     15 #include "net/cert/ct_verify_result.h"
     16 #include "net/cert/x509_certificate.h"
     17 
     18 namespace net {
     19 
     20 MultiLogCTVerifier::MultiLogCTVerifier() { }
     21 
     22 MultiLogCTVerifier::~MultiLogCTVerifier() { }
     23 
     24 void MultiLogCTVerifier::AddLog(scoped_ptr<CTLogVerifier> log_verifier) {
     25   DCHECK(log_verifier);
     26   if (!log_verifier)
     27     return;
     28 
     29   linked_ptr<CTLogVerifier> log(log_verifier.release());
     30   logs_[log->key_id()] = log;
     31 }
     32 
     33 int MultiLogCTVerifier::Verify(
     34     X509Certificate* cert,
     35     const std::string& stapled_ocsp_response,
     36     const std::string& sct_list_from_tls_extension,
     37     ct::CTVerifyResult* result,
     38     const BoundNetLog& net_log)  {
     39   DCHECK(cert);
     40   DCHECK(result);
     41 
     42   result->verified_scts.clear();
     43   result->invalid_scts.clear();
     44   result->unknown_logs_scts.clear();
     45 
     46   bool has_verified_scts = false;
     47 
     48   std::string embedded_scts;
     49   if (!cert->GetIntermediateCertificates().empty() &&
     50       ct::ExtractEmbeddedSCTList(
     51           cert->os_cert_handle(),
     52           &embedded_scts)) {
     53     ct::LogEntry precert_entry;
     54 
     55     has_verified_scts =
     56         ct::GetPrecertLogEntry(
     57             cert->os_cert_handle(),
     58             cert->GetIntermediateCertificates().front(),
     59             &precert_entry) &&
     60         VerifySCTs(
     61             embedded_scts,
     62             precert_entry,
     63             ct::SignedCertificateTimestamp::SCT_EMBEDDED,
     64             result);
     65   }
     66 
     67   std::string sct_list_from_ocsp;
     68   if (!stapled_ocsp_response.empty() &&
     69       !cert->GetIntermediateCertificates().empty()) {
     70     ct::ExtractSCTListFromOCSPResponse(
     71         cert->GetIntermediateCertificates().front(), cert->serial_number(),
     72         stapled_ocsp_response, &sct_list_from_ocsp);
     73   }
     74 
     75   // Log to Net Log, after extracting SCTs but before possibly failing on
     76   // X.509 entry creation.
     77   NetLog::ParametersCallback net_log_callback =
     78       base::Bind(&NetLogRawSignedCertificateTimestampCallback,
     79           &embedded_scts, &sct_list_from_ocsp, &sct_list_from_tls_extension);
     80 
     81   net_log.AddEvent(
     82       NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED,
     83       net_log_callback);
     84 
     85   ct::LogEntry x509_entry;
     86   if (ct::GetX509LogEntry(cert->os_cert_handle(), &x509_entry)) {
     87     has_verified_scts |= VerifySCTs(
     88         sct_list_from_ocsp,
     89         x509_entry,
     90         ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
     91         result);
     92 
     93     has_verified_scts |= VerifySCTs(
     94         sct_list_from_tls_extension,
     95         x509_entry,
     96         ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
     97         result);
     98   }
     99 
    100   NetLog::ParametersCallback net_log_checked_callback =
    101       base::Bind(&NetLogSignedCertificateTimestampCallback, result);
    102 
    103   net_log.AddEvent(
    104       NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED,
    105       net_log_checked_callback);
    106 
    107   if (has_verified_scts)
    108     return OK;
    109 
    110   return ERR_CT_NO_SCTS_VERIFIED_OK;
    111 }
    112 
    113 bool MultiLogCTVerifier::VerifySCTs(
    114     const std::string& encoded_sct_list,
    115     const ct::LogEntry& expected_entry,
    116     ct::SignedCertificateTimestamp::Origin origin,
    117     ct::CTVerifyResult* result) {
    118   if (logs_.empty())
    119     return false;
    120 
    121   base::StringPiece temp(encoded_sct_list);
    122   std::vector<base::StringPiece> sct_list;
    123 
    124   if (!ct::DecodeSCTList(&temp, &sct_list))
    125     return false;
    126 
    127   bool verified = false;
    128   for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin();
    129        it != sct_list.end(); ++it) {
    130     base::StringPiece encoded_sct(*it);
    131 
    132     scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct;
    133     if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) {
    134       // XXX(rsleevi): Should we really just skip over bad SCTs?
    135       continue;
    136     }
    137     decoded_sct->origin = origin;
    138 
    139     verified |= VerifySingleSCT(decoded_sct, expected_entry, result);
    140   }
    141 
    142   return verified;
    143 }
    144 
    145 bool MultiLogCTVerifier::VerifySingleSCT(
    146     scoped_refptr<ct::SignedCertificateTimestamp> sct,
    147     const ct::LogEntry& expected_entry,
    148     ct::CTVerifyResult* result) {
    149 
    150   // Assume this SCT is untrusted until proven otherwise.
    151   IDToLogMap::iterator it = logs_.find(sct->log_id);
    152   if (it == logs_.end()) {
    153     DVLOG(1) << "SCT does not match any known log.";
    154     result->unknown_logs_scts.push_back(sct);
    155     return false;
    156   }
    157 
    158   sct->log_description = it->second->description();
    159 
    160   if (!it->second->Verify(expected_entry, *sct)) {
    161     DVLOG(1) << "Unable to verify SCT signature.";
    162     result->invalid_scts.push_back(sct);
    163     return false;
    164   }
    165 
    166   // SCT verified ok, just make sure the timestamp is legitimate.
    167   if (sct->timestamp > base::Time::Now()) {
    168     DVLOG(1) << "SCT is from the future!";
    169     result->invalid_scts.push_back(sct);
    170     return false;
    171   }
    172 
    173   result->verified_scts.push_back(sct);
    174   return true;
    175 }
    176 
    177 } // namespace net
    178