Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(128)

Side by Side Diff: chrome/browser/ssl/chrome_expect_ct_reporter_unittest.cc

Issue 2959593002: Update SCT serialization format in Expect-CT reports (Closed)
Patch Set: remove unnecessary include Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "chrome/browser/ssl/chrome_expect_ct_reporter.h" 5 #include "chrome/browser/ssl/chrome_expect_ct_reporter.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/base64.h" 9 #include "base/base64.h"
10 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/json/json_reader.h" 11 #include "base/json/json_reader.h"
12 #include "base/message_loop/message_loop.h" 12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h" 13 #include "base/run_loop.h"
14 #include "base/test/histogram_tester.h" 14 #include "base/test/histogram_tester.h"
15 #include "base/test/scoped_feature_list.h" 15 #include "base/test/scoped_feature_list.h"
16 #include "base/values.h" 16 #include "base/values.h"
17 #include "chrome/common/chrome_features.h" 17 #include "chrome/common/chrome_features.h"
18 #include "content/public/test/test_browser_thread_bundle.h" 18 #include "content/public/test/test_browser_thread_bundle.h"
19 #include "net/cert/ct_serialization.h"
19 #include "net/cert/signed_certificate_timestamp_and_status.h" 20 #include "net/cert/signed_certificate_timestamp_and_status.h"
20 #include "net/test/cert_test_util.h" 21 #include "net/test/cert_test_util.h"
21 #include "net/test/test_data_directory.h" 22 #include "net/test/test_data_directory.h"
22 #include "net/test/url_request/url_request_failed_job.h" 23 #include "net/test/url_request/url_request_failed_job.h"
23 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" 24 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
24 #include "net/url_request/report_sender.h" 25 #include "net/url_request/report_sender.h"
25 #include "net/url_request/url_request_filter.h" 26 #include "net/url_request/url_request_filter.h"
26 #include "net/url_request/url_request_test_util.h" 27 #include "net/url_request/url_request_test_util.h"
27 #include "testing/gtest/include/gtest/gtest.h" 28 #include "testing/gtest/include/gtest/gtest.h"
28 #include "url/gurl.h" 29 #include "url/gurl.h"
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 EXPECT_EQ(pem_encoded_chain[i], cert_pem); 104 EXPECT_EQ(pem_encoded_chain[i], cert_pem);
104 } 105 }
105 } 106 }
106 107
107 // Converts the string value of a reported SCT's origin to a 108 // Converts the string value of a reported SCT's origin to a
108 // net::ct::SignedCertificateTimestamp::Origin value. 109 // net::ct::SignedCertificateTimestamp::Origin value.
109 net::ct::SignedCertificateTimestamp::Origin SCTOriginStringToOrigin( 110 net::ct::SignedCertificateTimestamp::Origin SCTOriginStringToOrigin(
110 const std::string& origin_string) { 111 const std::string& origin_string) {
111 if (origin_string == "embedded") 112 if (origin_string == "embedded")
112 return net::ct::SignedCertificateTimestamp::SCT_EMBEDDED; 113 return net::ct::SignedCertificateTimestamp::SCT_EMBEDDED;
113 if (origin_string == "from-tls-extension") 114 if (origin_string == "tls-extension")
114 return net::ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION; 115 return net::ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION;
115 if (origin_string == "from-ocsp-response") 116 if (origin_string == "ocsp")
116 return net::ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE; 117 return net::ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE;
117 NOTREACHED(); 118 NOTREACHED();
118 return net::ct::SignedCertificateTimestamp::SCT_EMBEDDED; 119 return net::ct::SignedCertificateTimestamp::SCT_EMBEDDED;
119 } 120 }
120 121
121 // Checks that an SCT |sct| appears (with the format determined by 122 // Checks that an SCT |sct| appears with status |status| in |report_list|, a
122 // |status|) in |report_list|, a list of SCTs from an Expect CT 123 // list of SCTs from an Expect-CT report.
123 // report. |status| determines the format in that only certain fields 124 ::testing::AssertionResult FindSCTInReportList(
124 // are reported for certain verify statuses; SCTs from unknown logs
125 // contain very little information, for example, to avoid compromising
126 // privacy.
127 void FindSCTInReportList(
128 const scoped_refptr<net::ct::SignedCertificateTimestamp>& sct, 125 const scoped_refptr<net::ct::SignedCertificateTimestamp>& sct,
129 net::ct::SCTVerifyStatus status, 126 net::ct::SCTVerifyStatus status,
mattm 2017/06/28 01:25:22 could be clearer if these were named expected_sct
estark 2017/06/28 04:44:03 Done.
130 const base::ListValue& report_list) { 127 const base::ListValue& report_list) {
131 bool found = false; 128 std::string expected_serialized_sct;
132 for (size_t i = 0; !found && i < report_list.GetSize(); i++) { 129 net::ct::EncodeSignedCertificateTimestamp(sct, &expected_serialized_sct);
130
131 for (size_t i = 0; i < report_list.GetSize(); i++) {
133 const base::DictionaryValue* report_sct; 132 const base::DictionaryValue* report_sct;
134 ASSERT_TRUE(report_list.GetDictionary(i, &report_sct)); 133 EXPECT_TRUE(report_list.GetDictionary(i, &report_sct));
mattm 2017/06/28 01:25:23 Shouldn't this one still be ASSERT_TRUE? Otherwise
estark 2017/06/28 04:44:03 Done.
135 134
136 std::string origin; 135 std::string serialized_sct;
137 ASSERT_TRUE(report_sct->GetString("origin", &origin)); 136 EXPECT_TRUE(report_sct->GetString("serialized_sct", &serialized_sct));
137 std::string decoded_serialized_sct;
138 EXPECT_TRUE(base::Base64Decode(serialized_sct, &decoded_serialized_sct));
139 if (decoded_serialized_sct != expected_serialized_sct)
140 continue;
138 141
142 std::string source;
143 EXPECT_TRUE(report_sct->GetString("source", &source));
144 EXPECT_EQ(sct->origin, SCTOriginStringToOrigin(source));
145
146 std::string report_status;
147 EXPECT_TRUE(report_sct->GetString("status", &report_status));
139 switch (status) { 148 switch (status) {
140 case net::ct::SCT_STATUS_LOG_UNKNOWN: 149 case net::ct::SCT_STATUS_LOG_UNKNOWN:
141 // SCTs from unknown logs only have an origin. 150 EXPECT_EQ("unknown", report_status);
142 EXPECT_FALSE(report_sct->HasKey("sct"));
143 EXPECT_FALSE(report_sct->HasKey("id"));
144 if (SCTOriginStringToOrigin(origin) == sct->origin)
145 found = true;
146 break; 151 break;
147
148 case net::ct::SCT_STATUS_INVALID_SIGNATURE: 152 case net::ct::SCT_STATUS_INVALID_SIGNATURE:
149 case net::ct::SCT_STATUS_INVALID_TIMESTAMP: { 153 case net::ct::SCT_STATUS_INVALID_TIMESTAMP: {
150 // Invalid SCTs have a log id and an origin and nothing else. 154 EXPECT_EQ("invalid", report_status);
151 EXPECT_FALSE(report_sct->HasKey("sct"));
152 std::string id_base64;
153 ASSERT_TRUE(report_sct->GetString("id", &id_base64));
154 std::string id;
155 ASSERT_TRUE(base::Base64Decode(id_base64, &id));
156 if (SCTOriginStringToOrigin(origin) == sct->origin && id == sct->log_id)
157 found = true;
158 break; 155 break;
159 } 156 }
160
161 case net::ct::SCT_STATUS_OK: { 157 case net::ct::SCT_STATUS_OK: {
162 // Valid SCTs have the full SCT. 158 EXPECT_EQ("valid", report_status);
163 const base::DictionaryValue* report_sct_object;
164 ASSERT_TRUE(report_sct->GetDictionary("sct", &report_sct_object));
165 int version;
166 ASSERT_TRUE(report_sct_object->GetInteger("sct_version", &version));
167 std::string id_base64;
168 ASSERT_TRUE(report_sct_object->GetString("id", &id_base64));
169 std::string id;
170 ASSERT_TRUE(base::Base64Decode(id_base64, &id));
171 std::string extensions_base64;
172 ASSERT_TRUE(
173 report_sct_object->GetString("extensions", &extensions_base64));
174 std::string extensions;
175 ASSERT_TRUE(base::Base64Decode(extensions_base64, &extensions));
176 std::string signature_data_base64;
177 ASSERT_TRUE(
178 report_sct_object->GetString("signature", &signature_data_base64));
179 std::string signature_data;
180 ASSERT_TRUE(base::Base64Decode(signature_data_base64, &signature_data));
181
182 if (version == sct->version &&
183 SCTOriginStringToOrigin(origin) == sct->origin &&
184 id == sct->log_id && extensions == sct->extensions &&
185 signature_data == sct->signature.signature_data) {
186 found = true;
187 }
188 break; 159 break;
189 } 160 }
190 default: 161 default:
mattm 2017/06/28 01:25:23 same comment about avoiding default:
estark 2017/06/28 04:44:03 Done.
191 NOTREACHED(); 162 NOTREACHED();
192 } 163 }
164 return ::testing::AssertionSuccess();
193 } 165 }
194 EXPECT_TRUE(found); 166
167 return ::testing::AssertionFailure() << "Failed to find SCT in report list";
195 } 168 }
196 169
197 // Checks that all |expected_scts| appears in the given lists of SCTs 170 // Checks that all |expected_scts| appears in the given lists of SCTs
198 // from an Expect CT report. 171 // from an Expect CT report.
199 void CheckReportSCTs( 172 void CheckReportSCTs(
200 const net::SignedCertificateTimestampAndStatusList& expected_scts, 173 const net::SignedCertificateTimestampAndStatusList& expected_scts,
201 const base::ListValue& unknown_scts, 174 const base::ListValue& scts) {
202 const base::ListValue& invalid_scts, 175 EXPECT_EQ(expected_scts.size(), scts.GetSize());
203 const base::ListValue& valid_scts) {
204 EXPECT_EQ(
205 expected_scts.size(),
206 unknown_scts.GetSize() + invalid_scts.GetSize() + valid_scts.GetSize());
207 for (const auto& expected_sct : expected_scts) { 176 for (const auto& expected_sct : expected_scts) {
208 switch (expected_sct.status) { 177 ASSERT_TRUE(
209 case net::ct::SCT_STATUS_LOG_UNKNOWN: 178 FindSCTInReportList(expected_sct.sct, expected_sct.status, scts));
210 ASSERT_NO_FATAL_FAILURE(FindSCTInReportList(
211 expected_sct.sct, net::ct::SCT_STATUS_LOG_UNKNOWN, unknown_scts));
212 break;
213 case net::ct::SCT_STATUS_INVALID_SIGNATURE:
214 ASSERT_NO_FATAL_FAILURE(FindSCTInReportList(
215 expected_sct.sct, net::ct::SCT_STATUS_INVALID_SIGNATURE,
216 invalid_scts));
217 break;
218 case net::ct::SCT_STATUS_INVALID_TIMESTAMP:
219 ASSERT_NO_FATAL_FAILURE(FindSCTInReportList(
220 expected_sct.sct, net::ct::SCT_STATUS_INVALID_TIMESTAMP,
221 invalid_scts));
222 break;
223 case net::ct::SCT_STATUS_OK:
224 ASSERT_NO_FATAL_FAILURE(FindSCTInReportList(
225 expected_sct.sct, net::ct::SCT_STATUS_OK, valid_scts));
226 break;
227 default:
228 NOTREACHED();
229 }
230 } 179 }
231 } 180 }
232 181
233 // Checks that the |serialized_report| deserializes properly and 182 // Checks that the |serialized_report| deserializes properly and
234 // contains the correct information (hostname, port, served and 183 // contains the correct information (hostname, port, served and
235 // validated certificate chains, SCTs) for the given |host_port| and 184 // validated certificate chains, SCTs) for the given |host_port| and
236 // |ssl_info|. 185 // |ssl_info|.
237 void CheckExpectCTReport(const std::string& serialized_report, 186 void CheckExpectCTReport(const std::string& serialized_report,
238 const net::HostPortPair& host_port, 187 const net::HostPortPair& host_port,
239 const std::string expiration_time, 188 const std::string expiration_time,
240 const net::SSLInfo& ssl_info) { 189 const net::SSLInfo& ssl_info) {
241 std::unique_ptr<base::Value> value(base::JSONReader::Read(serialized_report)); 190 std::unique_ptr<base::Value> value(base::JSONReader::Read(serialized_report));
242 ASSERT_TRUE(value); 191 ASSERT_TRUE(value);
243 ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY)); 192 ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
244 193
194 base::DictionaryValue* outer_report_dict;
195 ASSERT_TRUE(value->GetAsDictionary(&outer_report_dict));
196
245 base::DictionaryValue* report_dict; 197 base::DictionaryValue* report_dict;
246 ASSERT_TRUE(value->GetAsDictionary(&report_dict)); 198 ASSERT_TRUE(
199 outer_report_dict->GetDictionary("expect-ct-report", &report_dict));
247 200
248 std::string report_hostname; 201 std::string report_hostname;
249 EXPECT_TRUE(report_dict->GetString("hostname", &report_hostname)); 202 EXPECT_TRUE(report_dict->GetString("hostname", &report_hostname));
250 EXPECT_EQ(host_port.host(), report_hostname); 203 EXPECT_EQ(host_port.host(), report_hostname);
251 int report_port; 204 int report_port;
252 EXPECT_TRUE(report_dict->GetInteger("port", &report_port)); 205 EXPECT_TRUE(report_dict->GetInteger("port", &report_port));
253 EXPECT_EQ(host_port.port(), report_port); 206 EXPECT_EQ(host_port.port(), report_port);
254 207
255 std::string expiration; 208 std::string expiration;
256 EXPECT_TRUE(report_dict->GetString("effective-expiration-date", &expiration)); 209 EXPECT_TRUE(report_dict->GetString("effective-expiration-date", &expiration));
257 EXPECT_EQ(expiration_time, expiration); 210 EXPECT_EQ(expiration_time, expiration);
258 211
259 const base::ListValue* report_served_certificate_chain = nullptr; 212 const base::ListValue* report_served_certificate_chain = nullptr;
260 ASSERT_TRUE(report_dict->GetList("served-certificate-chain", 213 ASSERT_TRUE(report_dict->GetList("served-certificate-chain",
261 &report_served_certificate_chain)); 214 &report_served_certificate_chain));
262 ASSERT_NO_FATAL_FAILURE(CheckReportCertificateChain( 215 ASSERT_NO_FATAL_FAILURE(CheckReportCertificateChain(
263 ssl_info.unverified_cert, *report_served_certificate_chain)); 216 ssl_info.unverified_cert, *report_served_certificate_chain));
264 217
265 const base::ListValue* report_validated_certificate_chain = nullptr; 218 const base::ListValue* report_validated_certificate_chain = nullptr;
266 ASSERT_TRUE(report_dict->GetList("validated-certificate-chain", 219 ASSERT_TRUE(report_dict->GetList("validated-certificate-chain",
267 &report_validated_certificate_chain)); 220 &report_validated_certificate_chain));
268 ASSERT_NO_FATAL_FAILURE(CheckReportCertificateChain( 221 ASSERT_NO_FATAL_FAILURE(CheckReportCertificateChain(
269 ssl_info.cert, *report_validated_certificate_chain)); 222 ssl_info.cert, *report_validated_certificate_chain));
270 223
271 const base::ListValue* report_unknown_scts = nullptr; 224 const base::ListValue* report_scts = nullptr;
272 ASSERT_TRUE(report_dict->GetList("unknown-scts", &report_unknown_scts)); 225 ASSERT_TRUE(report_dict->GetList("scts", &report_scts));
273 const base::ListValue* report_invalid_scts = nullptr;
274 ASSERT_TRUE(report_dict->GetList("invalid-scts", &report_invalid_scts));
275 const base::ListValue* report_valid_scts = nullptr;
276 ASSERT_TRUE(report_dict->GetList("valid-scts", &report_valid_scts));
277 226
278 ASSERT_NO_FATAL_FAILURE(CheckReportSCTs( 227 ASSERT_NO_FATAL_FAILURE(
279 ssl_info.signed_certificate_timestamps, *report_unknown_scts, 228 CheckReportSCTs(ssl_info.signed_certificate_timestamps, *report_scts));
280 *report_invalid_scts, *report_valid_scts));
281 } 229 }
282 230
283 // A test network delegate that allows the user to specify a callback to 231 // A test network delegate that allows the user to specify a callback to
284 // be run whenever a net::URLRequest is destroyed. 232 // be run whenever a net::URLRequest is destroyed.
285 class TestExpectCTNetworkDelegate : public net::NetworkDelegateImpl { 233 class TestExpectCTNetworkDelegate : public net::NetworkDelegateImpl {
286 public: 234 public:
287 TestExpectCTNetworkDelegate() 235 TestExpectCTNetworkDelegate()
288 : url_request_destroyed_callback_(base::Bind(&base::DoNothing)) {} 236 : url_request_destroyed_callback_(base::Bind(&base::DoNothing)) {}
289 237
290 void set_url_request_destroyed_callback(const base::Closure& callback) { 238 void set_url_request_destroyed_callback(const base::Closure& callback) {
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
507 EXPECT_FALSE(sender->latest_serialized_report().empty()); 455 EXPECT_FALSE(sender->latest_serialized_report().empty());
508 EXPECT_EQ("application/json; charset=utf-8", sender->latest_content_type()); 456 EXPECT_EQ("application/json; charset=utf-8", sender->latest_content_type());
509 ASSERT_NO_FATAL_FAILURE( 457 ASSERT_NO_FATAL_FAILURE(
510 CheckExpectCTReport(sender->latest_serialized_report(), host_port, 458 CheckExpectCTReport(sender->latest_serialized_report(), host_port,
511 kExpirationTimeStr, ssl_info)); 459 kExpirationTimeStr, ssl_info));
512 460
513 histograms.ExpectTotalCount(kFailureHistogramName, 0); 461 histograms.ExpectTotalCount(kFailureHistogramName, 0);
514 histograms.ExpectTotalCount(kSendHistogramName, 1); 462 histograms.ExpectTotalCount(kSendHistogramName, 1);
515 histograms.ExpectBucketCount(kSendHistogramName, true, 1); 463 histograms.ExpectBucketCount(kSendHistogramName, true, 1);
516 } 464 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698