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 18namespace net { 19 20MultiLogCTVerifier::MultiLogCTVerifier() { } 21 22MultiLogCTVerifier::~MultiLogCTVerifier() { } 23 24void 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 33int 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 113bool 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 145bool 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