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