| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/cert/multi_log_ct_verifier.h" | 5 #include "net/cert/multi_log_ct_verifier.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/callback_helpers.h" | 10 #include "base/callback_helpers.h" |
| 11 #include "base/metrics/histogram_macros.h" | 11 #include "base/metrics/histogram_macros.h" |
| 12 #include "base/values.h" | 12 #include "base/values.h" |
| 13 #include "net/base/net_errors.h" | 13 #include "net/base/net_errors.h" |
| 14 #include "net/cert/ct_log_verifier.h" | 14 #include "net/cert/ct_log_verifier.h" |
| 15 #include "net/cert/ct_objects_extractor.h" | 15 #include "net/cert/ct_objects_extractor.h" |
| 16 #include "net/cert/ct_serialization.h" | 16 #include "net/cert/ct_serialization.h" |
| 17 #include "net/cert/ct_signed_certificate_timestamp_log_param.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" | 18 #include "net/cert/sct_status_flags.h" |
| 19 #include "net/cert/signed_certificate_timestamp_and_status.h" |
| 20 #include "net/cert/x509_certificate.h" | 20 #include "net/cert/x509_certificate.h" |
| 21 #include "net/log/net_log_event_type.h" | 21 #include "net/log/net_log_event_type.h" |
| 22 #include "net/log/net_log_parameters_callback.h" | 22 #include "net/log/net_log_parameters_callback.h" |
| 23 #include "net/log/net_log_with_source.h" | 23 #include "net/log/net_log_with_source.h" |
| 24 | 24 |
| 25 namespace net { | 25 namespace net { |
| 26 | 26 |
| 27 namespace { | 27 namespace { |
| 28 | 28 |
| 29 // Record SCT verification status. This metric would help detecting presence | 29 // Record SCT verification status. This metric would help detecting presence |
| (...skipping 13 matching lines...) Expand all Loading... |
| 43 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTOrigin", | 43 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTOrigin", |
| 44 origin, | 44 origin, |
| 45 ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX); | 45 ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX); |
| 46 } | 46 } |
| 47 | 47 |
| 48 // Count the number of SCTs that were available for each SSL connection | 48 // Count the number of SCTs that were available for each SSL connection |
| 49 // (including SCTs embedded in the certificate). | 49 // (including SCTs embedded in the certificate). |
| 50 // This metric would allow measuring: | 50 // This metric would allow measuring: |
| 51 // * Of all SSL connections, how many had SCTs available for validation. | 51 // * Of all SSL connections, how many had SCTs available for validation. |
| 52 // * When SCTs are available, how many are available per connection. | 52 // * When SCTs are available, how many are available per connection. |
| 53 void LogNumSCTsToUMA(const ct::CTVerifyResult& result) { | 53 void LogNumSCTsToUMA(const SignedCertificateTimestampAndStatusList& scts) { |
| 54 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.CertificateTransparency.SCTsPerConnection", | 54 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.CertificateTransparency.SCTsPerConnection", |
| 55 result.scts.size(), 1, 10, 11); | 55 scts.size(), 1, 10, 11); |
| 56 } | 56 } |
| 57 | 57 |
| 58 void AddSCTAndLogStatus(scoped_refptr<ct::SignedCertificateTimestamp> sct, | 58 void AddSCTAndLogStatus(scoped_refptr<ct::SignedCertificateTimestamp> sct, |
| 59 ct::SCTVerifyStatus status, | 59 ct::SCTVerifyStatus status, |
| 60 SignedCertificateTimestampAndStatusList* sct_list) { | 60 SignedCertificateTimestampAndStatusList* sct_list) { |
| 61 LogSCTStatusToUMA(status); | 61 LogSCTStatusToUMA(status); |
| 62 sct_list->push_back(SignedCertificateTimestampAndStatus(sct, status)); | 62 sct_list->push_back(SignedCertificateTimestampAndStatus(sct, status)); |
| 63 } | 63 } |
| 64 | 64 |
| 65 } // namespace | 65 } // namespace |
| 66 | 66 |
| 67 MultiLogCTVerifier::MultiLogCTVerifier() : observer_(nullptr) { | 67 MultiLogCTVerifier::MultiLogCTVerifier() : observer_(nullptr) { |
| 68 } | 68 } |
| 69 | 69 |
| 70 MultiLogCTVerifier::~MultiLogCTVerifier() { } | 70 MultiLogCTVerifier::~MultiLogCTVerifier() { } |
| 71 | 71 |
| 72 void MultiLogCTVerifier::AddLogs( | 72 void MultiLogCTVerifier::AddLogs( |
| 73 const std::vector<scoped_refptr<const CTLogVerifier>>& log_verifiers) { | 73 const std::vector<scoped_refptr<const CTLogVerifier>>& log_verifiers) { |
| 74 for (const auto& log_verifier : log_verifiers) { | 74 for (const auto& log_verifier : log_verifiers) { |
| 75 VLOG(1) << "Adding CT log: " << log_verifier->description(); | 75 VLOG(1) << "Adding CT log: " << log_verifier->description(); |
| 76 logs_[log_verifier->key_id()] = log_verifier; | 76 logs_[log_verifier->key_id()] = log_verifier; |
| 77 } | 77 } |
| 78 } | 78 } |
| 79 | 79 |
| 80 void MultiLogCTVerifier::SetObserver(Observer* observer) { | 80 void MultiLogCTVerifier::SetObserver(Observer* observer) { |
| 81 observer_ = observer; | 81 observer_ = observer; |
| 82 } | 82 } |
| 83 | 83 |
| 84 int MultiLogCTVerifier::Verify(X509Certificate* cert, | 84 int MultiLogCTVerifier::Verify( |
| 85 const std::string& stapled_ocsp_response, | 85 X509Certificate* cert, |
| 86 const std::string& sct_list_from_tls_extension, | 86 const std::string& stapled_ocsp_response, |
| 87 ct::CTVerifyResult* result, | 87 const std::string& sct_list_from_tls_extension, |
| 88 const NetLogWithSource& net_log) { | 88 SignedCertificateTimestampAndStatusList* output_scts, |
| 89 const NetLogWithSource& net_log) { |
| 89 DCHECK(cert); | 90 DCHECK(cert); |
| 90 DCHECK(result); | 91 DCHECK(output_scts); |
| 91 | 92 |
| 92 result->scts.clear(); | 93 output_scts->clear(); |
| 93 | 94 |
| 94 bool has_verified_scts = false; | 95 bool has_verified_scts = false; |
| 95 | 96 |
| 96 std::string embedded_scts; | 97 std::string embedded_scts; |
| 97 if (!cert->GetIntermediateCertificates().empty() && | 98 if (!cert->GetIntermediateCertificates().empty() && |
| 98 ct::ExtractEmbeddedSCTList( | 99 ct::ExtractEmbeddedSCTList( |
| 99 cert->os_cert_handle(), | 100 cert->os_cert_handle(), |
| 100 &embedded_scts)) { | 101 &embedded_scts)) { |
| 101 ct::LogEntry precert_entry; | 102 ct::LogEntry precert_entry; |
| 102 | 103 |
| 103 has_verified_scts = | 104 has_verified_scts = |
| 104 ct::GetPrecertLogEntry(cert->os_cert_handle(), | 105 ct::GetPrecertLogEntry(cert->os_cert_handle(), |
| 105 cert->GetIntermediateCertificates().front(), | 106 cert->GetIntermediateCertificates().front(), |
| 106 &precert_entry) && | 107 &precert_entry) && |
| 107 VerifySCTs(embedded_scts, precert_entry, | 108 VerifySCTs(embedded_scts, precert_entry, |
| 108 ct::SignedCertificateTimestamp::SCT_EMBEDDED, cert, result); | 109 ct::SignedCertificateTimestamp::SCT_EMBEDDED, cert, |
| 110 output_scts); |
| 109 } | 111 } |
| 110 | 112 |
| 111 std::string sct_list_from_ocsp; | 113 std::string sct_list_from_ocsp; |
| 112 if (!stapled_ocsp_response.empty() && | 114 if (!stapled_ocsp_response.empty() && |
| 113 !cert->GetIntermediateCertificates().empty()) { | 115 !cert->GetIntermediateCertificates().empty()) { |
| 114 ct::ExtractSCTListFromOCSPResponse( | 116 ct::ExtractSCTListFromOCSPResponse( |
| 115 cert->GetIntermediateCertificates().front(), cert->serial_number(), | 117 cert->GetIntermediateCertificates().front(), cert->serial_number(), |
| 116 stapled_ocsp_response, &sct_list_from_ocsp); | 118 stapled_ocsp_response, &sct_list_from_ocsp); |
| 117 } | 119 } |
| 118 | 120 |
| 119 // Log to Net Log, after extracting SCTs but before possibly failing on | 121 // Log to Net Log, after extracting SCTs but before possibly failing on |
| 120 // X.509 entry creation. | 122 // X.509 entry creation. |
| 121 NetLogParametersCallback net_log_callback = | 123 NetLogParametersCallback net_log_callback = |
| 122 base::Bind(&NetLogRawSignedCertificateTimestampCallback, &embedded_scts, | 124 base::Bind(&NetLogRawSignedCertificateTimestampCallback, &embedded_scts, |
| 123 &sct_list_from_ocsp, &sct_list_from_tls_extension); | 125 &sct_list_from_ocsp, &sct_list_from_tls_extension); |
| 124 | 126 |
| 125 net_log.AddEvent(NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED, | 127 net_log.AddEvent(NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED, |
| 126 net_log_callback); | 128 net_log_callback); |
| 127 | 129 |
| 128 ct::LogEntry x509_entry; | 130 ct::LogEntry x509_entry; |
| 129 if (ct::GetX509LogEntry(cert->os_cert_handle(), &x509_entry)) { | 131 if (ct::GetX509LogEntry(cert->os_cert_handle(), &x509_entry)) { |
| 130 has_verified_scts |= VerifySCTs( | 132 has_verified_scts |= |
| 131 sct_list_from_ocsp, x509_entry, | 133 VerifySCTs(sct_list_from_ocsp, x509_entry, |
| 132 ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE, cert, result); | 134 ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE, cert, |
| 135 output_scts); |
| 133 | 136 |
| 134 has_verified_scts |= VerifySCTs( | 137 has_verified_scts |= |
| 135 sct_list_from_tls_extension, x509_entry, | 138 VerifySCTs(sct_list_from_tls_extension, x509_entry, |
| 136 ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, cert, result); | 139 ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, cert, |
| 140 output_scts); |
| 137 } | 141 } |
| 138 | 142 |
| 139 NetLogParametersCallback net_log_checked_callback = | 143 NetLogParametersCallback net_log_checked_callback = |
| 140 base::Bind(&NetLogSignedCertificateTimestampCallback, result); | 144 base::Bind(&NetLogSignedCertificateTimestampCallback, output_scts); |
| 141 | 145 |
| 142 net_log.AddEvent(NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED, | 146 net_log.AddEvent(NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED, |
| 143 net_log_checked_callback); | 147 net_log_checked_callback); |
| 144 | 148 |
| 145 LogNumSCTsToUMA(*result); | 149 LogNumSCTsToUMA(*output_scts); |
| 146 | 150 |
| 147 if (has_verified_scts) | 151 if (has_verified_scts) |
| 148 return OK; | 152 return OK; |
| 149 | 153 |
| 150 return ERR_CT_NO_SCTS_VERIFIED_OK; | 154 return ERR_CT_NO_SCTS_VERIFIED_OK; |
| 151 } | 155 } |
| 152 | 156 |
| 153 bool MultiLogCTVerifier::VerifySCTs( | 157 bool MultiLogCTVerifier::VerifySCTs( |
| 154 const std::string& encoded_sct_list, | 158 const std::string& encoded_sct_list, |
| 155 const ct::LogEntry& expected_entry, | 159 const ct::LogEntry& expected_entry, |
| 156 ct::SignedCertificateTimestamp::Origin origin, | 160 ct::SignedCertificateTimestamp::Origin origin, |
| 157 X509Certificate* cert, | 161 X509Certificate* cert, |
| 158 ct::CTVerifyResult* result) { | 162 SignedCertificateTimestampAndStatusList* output_scts) { |
| 159 if (logs_.empty()) | 163 if (logs_.empty()) |
| 160 return false; | 164 return false; |
| 161 | 165 |
| 162 base::StringPiece temp(encoded_sct_list); | 166 base::StringPiece temp(encoded_sct_list); |
| 163 std::vector<base::StringPiece> sct_list; | 167 std::vector<base::StringPiece> sct_list; |
| 164 | 168 |
| 165 if (!ct::DecodeSCTList(&temp, &sct_list)) | 169 if (!ct::DecodeSCTList(&temp, &sct_list)) |
| 166 return false; | 170 return false; |
| 167 | 171 |
| 168 bool verified = false; | 172 bool verified = false; |
| 169 for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin(); | 173 for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin(); |
| 170 it != sct_list.end(); ++it) { | 174 it != sct_list.end(); ++it) { |
| 171 base::StringPiece encoded_sct(*it); | 175 base::StringPiece encoded_sct(*it); |
| 172 LogSCTOriginToUMA(origin); | 176 LogSCTOriginToUMA(origin); |
| 173 | 177 |
| 174 scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct; | 178 scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct; |
| 175 if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) { | 179 if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) { |
| 176 LogSCTStatusToUMA(ct::SCT_STATUS_NONE); | 180 LogSCTStatusToUMA(ct::SCT_STATUS_NONE); |
| 177 // XXX(rsleevi): Should we really just skip over bad SCTs? | 181 // XXX(rsleevi): Should we really just skip over bad SCTs? |
| 178 continue; | 182 continue; |
| 179 } | 183 } |
| 180 decoded_sct->origin = origin; | 184 decoded_sct->origin = origin; |
| 181 | 185 |
| 182 verified |= VerifySingleSCT(decoded_sct, expected_entry, cert, result); | 186 verified |= VerifySingleSCT(decoded_sct, expected_entry, cert, output_scts); |
| 183 } | 187 } |
| 184 | 188 |
| 185 return verified; | 189 return verified; |
| 186 } | 190 } |
| 187 | 191 |
| 188 bool MultiLogCTVerifier::VerifySingleSCT( | 192 bool MultiLogCTVerifier::VerifySingleSCT( |
| 189 scoped_refptr<ct::SignedCertificateTimestamp> sct, | 193 scoped_refptr<ct::SignedCertificateTimestamp> sct, |
| 190 const ct::LogEntry& expected_entry, | 194 const ct::LogEntry& expected_entry, |
| 191 X509Certificate* cert, | 195 X509Certificate* cert, |
| 192 ct::CTVerifyResult* result) { | 196 SignedCertificateTimestampAndStatusList* output_scts) { |
| 193 // Assume this SCT is untrusted until proven otherwise. | 197 // Assume this SCT is untrusted until proven otherwise. |
| 194 const auto& it = logs_.find(sct->log_id); | 198 const auto& it = logs_.find(sct->log_id); |
| 195 if (it == logs_.end()) { | 199 if (it == logs_.end()) { |
| 196 DVLOG(1) << "SCT does not match any known log."; | 200 DVLOG(1) << "SCT does not match any known log."; |
| 197 AddSCTAndLogStatus(sct, ct::SCT_STATUS_LOG_UNKNOWN, &(result->scts)); | 201 AddSCTAndLogStatus(sct, ct::SCT_STATUS_LOG_UNKNOWN, output_scts); |
| 198 return false; | 202 return false; |
| 199 } | 203 } |
| 200 | 204 |
| 201 sct->log_description = it->second->description(); | 205 sct->log_description = it->second->description(); |
| 202 | 206 |
| 203 if (!it->second->Verify(expected_entry, *sct.get())) { | 207 if (!it->second->Verify(expected_entry, *sct.get())) { |
| 204 DVLOG(1) << "Unable to verify SCT signature."; | 208 DVLOG(1) << "Unable to verify SCT signature."; |
| 205 AddSCTAndLogStatus(sct, ct::SCT_STATUS_INVALID_SIGNATURE, &(result->scts)); | 209 AddSCTAndLogStatus(sct, ct::SCT_STATUS_INVALID_SIGNATURE, output_scts); |
| 206 return false; | 210 return false; |
| 207 } | 211 } |
| 208 | 212 |
| 209 // SCT verified ok, just make sure the timestamp is legitimate. | 213 // SCT verified ok, just make sure the timestamp is legitimate. |
| 210 if (sct->timestamp > base::Time::Now()) { | 214 if (sct->timestamp > base::Time::Now()) { |
| 211 DVLOG(1) << "SCT is from the future!"; | 215 DVLOG(1) << "SCT is from the future!"; |
| 212 AddSCTAndLogStatus(sct, ct::SCT_STATUS_INVALID_TIMESTAMP, &(result->scts)); | 216 AddSCTAndLogStatus(sct, ct::SCT_STATUS_INVALID_TIMESTAMP, output_scts); |
| 213 return false; | 217 return false; |
| 214 } | 218 } |
| 215 | 219 |
| 216 AddSCTAndLogStatus(sct, ct::SCT_STATUS_OK, &(result->scts)); | 220 AddSCTAndLogStatus(sct, ct::SCT_STATUS_OK, output_scts); |
| 217 if (observer_) | 221 if (observer_) |
| 218 observer_->OnSCTVerified(cert, sct.get()); | 222 observer_->OnSCTVerified(cert, sct.get()); |
| 219 return true; | 223 return true; |
| 220 } | 224 } |
| 221 | 225 |
| 222 } // namespace net | 226 } // namespace net |
| OLD | NEW |