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 "net/base/net_errors.h" | |
8 #include "net/cert/ct_log_verifier.h" | |
9 #include "net/cert/ct_objects_extractor.h" | |
10 #include "net/cert/ct_serialization.h" | |
11 #include "net/cert/ct_verify_result.h" | |
12 #include "net/cert/x509_certificate.h" | |
13 | |
14 namespace net { | |
15 | |
16 MultiLogCTVerifier::MultiLogCTVerifier() { } | |
17 | |
18 MultiLogCTVerifier::~MultiLogCTVerifier() { } | |
19 | |
20 void MultiLogCTVerifier::AddLog(scoped_ptr<CTLogVerifier> log_verifier) { | |
21 if (!log_verifier) | |
22 return; | |
wtc
2013/11/21 23:26:34
Are you sure we need to allow a null |log_verifier
Eran M. (Google)
2013/11/23 21:02:07
No, but I wouldn't want to crash here in productio
wtc
2013/11/25 23:15:50
A crash may help expose a bug sooner. If we know n
| |
23 | |
24 std::string log_id(log_verifier->key_id()); | |
25 logs_[log_id].reset(log_verifier.release()); | |
26 } | |
27 | |
28 int MultiLogCTVerifier::Verify( | |
29 X509Certificate* cert, | |
30 const std::string& sct_list_from_ocsp, | |
31 const std::string& sct_list_from_tls_extension, | |
32 ct::CTVerifyResult* result) { | |
33 DCHECK(cert); | |
34 DCHECK(result); | |
35 | |
36 result->verified_scts.clear(); | |
37 result->unverified_scts.clear(); | |
38 result->unknown_logs_scts.clear(); | |
39 | |
40 bool has_verified_scts = false; | |
41 | |
42 std::string embedded_scts; | |
43 if (!cert->GetIntermediateCertificates().empty() && | |
44 ct::ExtractEmbeddedSCTList( | |
45 cert->os_cert_handle(), | |
46 &embedded_scts)) { | |
47 ct::LogEntry precert_entry; | |
48 | |
49 has_verified_scts = | |
50 ct::GetPrecertLogEntry( | |
51 cert->os_cert_handle(), | |
52 cert->GetIntermediateCertificates().front(), | |
53 &precert_entry) && | |
54 VerifySCTs( | |
55 embedded_scts, | |
56 precert_entry, | |
57 ct::SignedCertificateTimestamp::SCT_EMBEDDED, | |
58 result); | |
59 } | |
60 | |
61 ct::LogEntry x509_entry; | |
62 if (!ct::GetX509LogEntry(cert->os_cert_handle(), &x509_entry)) | |
63 return has_verified_scts ? OK : ERR_CT_LOG_ENTRY_CREATION_FAILED; | |
64 | |
65 has_verified_scts |= VerifySCTs( | |
66 sct_list_from_ocsp, | |
67 x509_entry, | |
68 ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE, | |
69 result); | |
70 | |
71 has_verified_scts |= VerifySCTs( | |
72 sct_list_from_tls_extension, | |
73 x509_entry, | |
74 ct::SignedCertificateTimestamp::SCT_FROM_TLS_HANDSHAKE, | |
75 result); | |
76 | |
77 if (has_verified_scts) | |
78 return OK; | |
79 | |
80 if (!result->unverified_scts.empty()) | |
81 return ERR_NO_SCTS_VERIFIED_OK; | |
82 | |
83 if (!result->unknown_logs_scts.empty()) | |
84 return ERR_NO_SCTS_FROM_KNOWN_LOGS; | |
85 | |
86 return ERR_NO_SCTS_PRESENT; | |
wtc
2013/11/21 23:26:34
It should be sufficient to just use one error code
Eran M. (Google)
2013/11/23 21:02:07
Will be done in the next patch, when I introduce t
| |
87 } | |
88 | |
89 bool MultiLogCTVerifier::VerifySCTs( | |
90 const std::string& encoded_sct_list, | |
91 const ct::LogEntry& expected_entry, | |
92 ct::SignedCertificateTimestamp::Origin origin, | |
93 ct::CTVerifyResult* result) { | |
94 if (logs_.empty()) | |
95 return false; | |
96 | |
97 base::StringPiece temp(encoded_sct_list); | |
98 std::vector<base::StringPiece> sct_list; | |
99 | |
100 if (!ct::DecodeSCTList(&temp, &sct_list)) | |
101 return false; | |
102 | |
103 bool verified = false; | |
104 for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin(); | |
105 it != sct_list.end(); ++it) { | |
106 base::StringPiece encoded_sct(*it); | |
107 ct::SignedCertificateTimestamp decoded_sct; | |
108 if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) { | |
109 // XXX(rsleevi): Should we really just skip over bad SCTs? | |
110 continue; | |
111 } | |
112 decoded_sct.origin = origin; | |
113 | |
114 verified |= VerifySingleSCT(decoded_sct, expected_entry, result); | |
115 } | |
116 | |
117 return verified; | |
118 } | |
119 | |
120 bool MultiLogCTVerifier::VerifySingleSCT( | |
121 const ct::SignedCertificateTimestamp& sct, | |
122 const ct::LogEntry& expected_entry, | |
123 ct::CTVerifyResult* result) { | |
124 | |
125 // Assume this SCT is untrusted until proven otherwise. | |
126 | |
127 IDToLogMap::iterator it = logs_.find(sct.log_id); | |
128 if (it == logs_.end()) { | |
129 DVLOG(1) << "SCT does not match any known log."; | |
130 result->unknown_logs_scts.push_back(sct); | |
131 return false; | |
132 } | |
133 | |
134 if (!it->second->Verify(expected_entry, sct)) { | |
135 DVLOG(1) << "Unable to verify SCT signature."; | |
136 result->unverified_scts.push_back(sct); | |
137 return false; | |
138 } | |
139 | |
140 // SCT verified ok, just make sure the timestamp is legitimate. | |
141 // Add 1 second to allow some slack for accepting SCTs which have *Just* | |
142 // been issued. | |
wtc
2013/11/21 23:26:34
1. The comment in the reference implementation say
Eran M. (Google)
2013/11/23 21:02:07
I've dropped this 1 second interval entirely. You'
| |
143 if (sct.timestamp + base::TimeDelta::FromSeconds(1) > base::Time::Now()) { | |
144 DVLOG(1) << "SCT is from the future!"; | |
145 result->unverified_scts.push_back(sct); | |
146 return false; | |
147 } | |
148 | |
149 result->verified_scts.push_back(sct); | |
150 return true; | |
151 } | |
152 | |
153 } // namespace net | |
OLD | NEW |