| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |