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