1// Copyright 2014 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 "components/certificate_transparency/chrome_ct_policy_enforcer.h"
6
7#include <memory>
8#include <string>
9
10#include "base/stl_util.h"
11#include "base/time/time.h"
12#include "base/version.h"
13#include "crypto/rsa_private_key.h"
14#include "crypto/sha2.h"
15#include "net/cert/ct_policy_status.h"
16#include "net/cert/ct_verify_result.h"
17#include "net/cert/x509_certificate.h"
18#include "net/cert/x509_util.h"
19#include "net/log/net_log_with_source.h"
20#include "net/test/cert_test_util.h"
21#include "net/test/ct_test_util.h"
22#include "net/test/test_data_directory.h"
23#include "testing/gmock/include/gmock/gmock.h"
24#include "testing/gtest/include/gtest/gtest.h"
25
26using net::ct::CTPolicyCompliance;
27using net::ct::SCTList;
28using net::ct::SignedCertificateTimestamp;
29using net::NetLogWithSource;
30using net::X509Certificate;
31
32namespace certificate_transparency {
33
34namespace {
35
36const char kGoogleAviatorLogID[] =
37 "\x68\xf6\x98\xf8\x1f\x64\x82\xbe\x3a\x8c\xee\xb9\x28\x1d\x4c\xfc\x71\x51"
38 "\x5d\x67\x93\xd4\x44\xd1\x0a\x67\xac\xbb\x4f\x4f\xfb\xc4";
39static_assert(base::size(kGoogleAviatorLogID) - 1 == crypto::kSHA256Length,
40 "Incorrect log ID length.");
41
42class ChromeCTPolicyEnforcerTest : public ::testing::Test {
43 public:
44 void SetUp() override {
45 policy_enforcer_.reset(new ChromeCTPolicyEnforcer);
46
47 std::string der_test_cert(net::ct::GetDerEncodedX509Cert());
48 chain_ = X509Certificate::CreateFromBytes(der_test_cert.data(),
49 der_test_cert.size());
50 ASSERT_TRUE(chain_.get());
51 google_log_id_ = std::string(kGoogleAviatorLogID, crypto::kSHA256Length);
52 non_google_log_id_.assign(crypto::kSHA256Length, 1);
53 }
54
55 void FillListWithSCTsOfOrigin(
56 SignedCertificateTimestamp::Origin desired_origin,
57 size_t num_scts,
58 const std::vector<std::string>& desired_log_keys,
59 bool timestamp_past_enforcement_date,
60 SCTList* verified_scts) {
61 for (size_t i = 0; i < num_scts; ++i) {
62 scoped_refptr<SignedCertificateTimestamp> sct(
63 new SignedCertificateTimestamp());
64 sct->origin = desired_origin;
65 if (i < desired_log_keys.size())
66 sct->log_id = desired_log_keys[i];
67 else
68 sct->log_id = std::string(crypto::kSHA256Length, static_cast<char>(i));
69
70 if (timestamp_past_enforcement_date) {
71 EXPECT_TRUE(base::Time::FromUTCExploded({2015, 8, 0, 15, 0, 0, 0, 0},
72 &sct->timestamp));
73 } else {
74 EXPECT_TRUE(base::Time::FromUTCExploded({2015, 6, 0, 15, 0, 0, 0, 0},
75 &sct->timestamp));
76 }
77
78 verified_scts->push_back(sct);
79 }
80 }
81
82 void AddDisqualifiedLogSCT(SignedCertificateTimestamp::Origin desired_origin,
83 bool timestamp_after_disqualification_date,
84 SCTList* verified_scts) {
85 static const char kCertlyLogID[] =
86 "\xcd\xb5\x17\x9b\x7f\xc1\xc0\x46\xfe\xea\x31\x13\x6a\x3f\x8f\x00\x2e"
87 "\x61\x82\xfa\xf8\x89\x6f\xec\xc8\xb2\xf5\xb5\xab\x60\x49\x00";
88 static_assert(base::size(kCertlyLogID) - 1 == crypto::kSHA256Length,
89 "Incorrect log ID length.");
90
91 scoped_refptr<SignedCertificateTimestamp> sct(
92 new SignedCertificateTimestamp());
93 sct->origin = desired_origin;
94 sct->log_id = std::string(kCertlyLogID, crypto::kSHA256Length);
95 if (timestamp_after_disqualification_date) {
96 EXPECT_TRUE(base::Time::FromUTCExploded({2016, 4, 0, 16, 0, 0, 0, 0},
97 &sct->timestamp));
98 } else {
99 EXPECT_TRUE(base::Time::FromUTCExploded({2016, 4, 0, 1, 0, 0, 0, 0},
100 &sct->timestamp));
101 }
102
103 verified_scts->push_back(sct);
104 }
105
106 void FillListWithSCTsOfOrigin(
107 SignedCertificateTimestamp::Origin desired_origin,
108 size_t num_scts,
109 SCTList* verified_scts) {
110 std::vector<std::string> desired_log_ids;
111 desired_log_ids.push_back(google_log_id_);
112 FillListWithSCTsOfOrigin(desired_origin, num_scts, desired_log_ids, true,
113 verified_scts);
114 }
115
116 base::Time CreateTime(const base::Time::Exploded& exploded) {
117 base::Time result;
118 if (!base::Time::FromUTCExploded(exploded, &result)) {
119 ADD_FAILURE() << "Failed FromUTCExploded";
120 }
121 return result;
122 }
123
124 protected:
125 std::unique_ptr<net::CTPolicyEnforcer> policy_enforcer_;
126 scoped_refptr<X509Certificate> chain_;
127 std::string google_log_id_;
128 std::string non_google_log_id_;
129};
130
131TEST_F(ChromeCTPolicyEnforcerTest,
132 DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllGoogle) {
133 SCTList scts;
134 std::vector<std::string> desired_log_ids(2, google_log_id_);
135
136 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
137 desired_log_ids.size(), desired_log_ids, true,
138 &scts);
139
140 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
141 policy_enforcer_->CheckCompliance(chain_.get(), scts,
142 NetLogWithSource()));
143}
144
145TEST_F(ChromeCTPolicyEnforcerTest,
146 DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllNonGoogle) {
147 SCTList scts;
148 std::vector<std::string> desired_log_ids(2, non_google_log_id_);
149
150 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
151 desired_log_ids.size(), desired_log_ids, true,
152 &scts);
153
154 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
155 policy_enforcer_->CheckCompliance(chain_.get(), scts,
156 NetLogWithSource()));
157}
158
159TEST_F(ChromeCTPolicyEnforcerTest,
160 ConformsToCTPolicyIfSCTBeforeEnforcementDate) {
161 SCTList scts;
162 // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
163 // All 5 SCTs will be from non-Google logs.
164 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5,
165 std::vector<std::string>(), false, &scts);
166
167 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
168 policy_enforcer_->CheckCompliance(chain_.get(), scts,
169 NetLogWithSource()));
170}
171
172TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyWithNonEmbeddedSCTs) {
173 SCTList scts;
174 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
175 2, &scts);
176
177 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
178 policy_enforcer_->CheckCompliance(chain_.get(), scts,
179 NetLogWithSource()));
180}
181
182TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyWithEmbeddedSCTs) {
183 // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
184 SCTList scts;
185 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
186
187 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
188 policy_enforcer_->CheckCompliance(chain_.get(), scts,
189 NetLogWithSource()));
190}
191
192TEST_F(ChromeCTPolicyEnforcerTest,
193 ConformsToCTPolicyWithPooledNonEmbeddedSCTs) {
194 SCTList scts;
195 std::vector<std::string> desired_logs;
196
197 // One Google log, delivered via OCSP.
198 desired_logs.clear();
199 desired_logs.push_back(google_log_id_);
200 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
201 desired_logs.size(), desired_logs, true, &scts);
202
203 // One non-Google log, delivered via TLS.
204 desired_logs.clear();
205 desired_logs.push_back(non_google_log_id_);
206 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
207 desired_logs.size(), desired_logs, true, &scts);
208
209 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
210 policy_enforcer_->CheckCompliance(chain_.get(), scts,
211 NetLogWithSource()));
212}
213
214TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyWithPooledEmbeddedSCTs) {
215 SCTList scts;
216 std::vector<std::string> desired_logs;
217
218 // One Google log, delivered embedded.
219 desired_logs.clear();
220 desired_logs.push_back(google_log_id_);
221 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
222 desired_logs.size(), desired_logs, true, &scts);
223
224 // One non-Google log, delivered via OCSP.
225 desired_logs.clear();
226 desired_logs.push_back(non_google_log_id_);
227 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
228 desired_logs.size(), desired_logs, true, &scts);
229
230 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
231 policy_enforcer_->CheckCompliance(chain_.get(), scts,
232 NetLogWithSource()));
233}
234
235TEST_F(ChromeCTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughSCTs) {
236 // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
237 SCTList scts;
238 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 2, &scts);
239
240 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
241 policy_enforcer_->CheckCompliance(chain_.get(), scts,
242 NetLogWithSource()));
243}
244
245TEST_F(ChromeCTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
246 SCTList scts;
247
248 // The results should be the same before and after disqualification,
249 // regardless of the delivery method.
250
251 // SCT from before disqualification.
252 scts.clear();
253 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
254 1, &scts);
255 AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
256 false, &scts);
257 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
258 policy_enforcer_->CheckCompliance(chain_.get(), scts,
259 NetLogWithSource()));
260
261 // SCT from after disqualification.
262 scts.clear();
263 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
264 1, &scts);
265 AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
266 true, &scts);
267 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
268 policy_enforcer_->CheckCompliance(chain_.get(), scts,
269 NetLogWithSource()));
270
271 // Embedded SCT from before disqualification.
272 scts.clear();
273 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
274 1, &scts);
275 AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
276 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
277 policy_enforcer_->CheckCompliance(chain_.get(), scts,
278 NetLogWithSource()));
279
280 // Embedded SCT from after disqualification.
281 scts.clear();
282 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
283 1, &scts);
284 AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
285 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
286 policy_enforcer_->CheckCompliance(chain_.get(), scts,
287 NetLogWithSource()));
288}
289
290TEST_F(ChromeCTPolicyEnforcerTest,
291 ConformsWithDisqualifiedLogBeforeDisqualificationDate) {
292 SCTList scts;
293 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
294 AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
295
296 // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
297 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
298 policy_enforcer_->CheckCompliance(chain_.get(), scts,
299 NetLogWithSource()));
300}
301
302TEST_F(ChromeCTPolicyEnforcerTest,
303 DoesNotConformWithDisqualifiedLogAfterDisqualificationDate) {
304 SCTList scts;
305 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
306 AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
307
308 // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
309 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
310 policy_enforcer_->CheckCompliance(chain_.get(), scts,
311 NetLogWithSource()));
312}
313
314TEST_F(ChromeCTPolicyEnforcerTest,
315 DoesNotConformWithIssuanceDateAfterDisqualificationDate) {
316 SCTList scts;
317 AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
318 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
319 // Make sure all SCTs are after the disqualification date.
320 for (size_t i = 1; i < scts.size(); ++i)
321 scts[i]->timestamp = scts[0]->timestamp;
322
323 // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
324 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
325 policy_enforcer_->CheckCompliance(chain_.get(), scts,
326 NetLogWithSource()));
327}
328
329TEST_F(ChromeCTPolicyEnforcerTest,
330 DoesNotConformToCTPolicyNotEnoughUniqueEmbeddedLogs) {
331 SCTList scts;
332 std::vector<std::string> desired_logs;
333
334 // One Google Log.
335 desired_logs.clear();
336 desired_logs.push_back(google_log_id_);
337 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
338 desired_logs.size(), desired_logs, true, &scts);
339
340 // Two distinct non-Google logs.
341 desired_logs.clear();
342 desired_logs.push_back(std::string(crypto::kSHA256Length, 'A'));
343 desired_logs.push_back(std::string(crypto::kSHA256Length, 'B'));
344 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
345 desired_logs.size(), desired_logs, true, &scts);
346
347 // Two unique SCTs from the same non-Google log.
348 desired_logs.clear();
349 desired_logs.push_back(std::string(crypto::kSHA256Length, 'C'));
350 desired_logs.push_back(std::string(crypto::kSHA256Length, 'C'));
351 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
352 desired_logs.size(), desired_logs, true, &scts);
353
354 // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
355 // However, there are only 4 SCTs are from distinct logs.
356 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
357 policy_enforcer_->CheckCompliance(chain_.get(), scts,
358 NetLogWithSource()));
359}
360
361TEST_F(ChromeCTPolicyEnforcerTest,
362 ConformsToPolicyExactNumberOfSCTsForValidityPeriod) {
363 std::unique_ptr<crypto::RSAPrivateKey> private_key(
364 crypto::RSAPrivateKey::Create(1024));
365 ASSERT_TRUE(private_key);
366
367 // Test multiple validity periods
368 base::Time time_2015_3_0_25_11_25_0_0 =
369 CreateTime({2015, 3, 0, 25, 11, 25, 0, 0});
370
371 base::Time time_2016_6_0_6_11_25_0_0 =
372 CreateTime({2016, 6, 0, 6, 11, 25, 0, 0});
373
374 base::Time time_2016_6_0_25_11_25_0_0 =
375 CreateTime({2016, 6, 0, 25, 11, 25, 0, 0});
376
377 base::Time time_2016_6_0_27_11_25_0_0 =
378 CreateTime({2016, 6, 0, 27, 11, 25, 0, 0});
379
380 base::Time time_2017_6_0_25_11_25_0_0 =
381 CreateTime({2017, 6, 0, 25, 11, 25, 0, 0});
382
383 base::Time time_2017_6_0_28_11_25_0_0 =
384 CreateTime({2017, 6, 0, 28, 11, 25, 0, 0});
385
386 base::Time time_2018_6_0_25_11_25_0_0 =
387 CreateTime({2018, 6, 0, 25, 11, 25, 0, 0});
388
389 base::Time time_2018_6_0_27_11_25_0_0 =
390 CreateTime({2018, 6, 0, 27, 11, 25, 0, 0});
391
392 const struct TestData {
393 base::Time validity_start;
394 base::Time validity_end;
395 size_t scts_required;
396 } kTestData[] = {{// Cert valid for -14 months (nonsensical), needs 2 SCTs.
397 time_2016_6_0_6_11_25_0_0, time_2015_3_0_25_11_25_0_0, 2},
398 {// Cert valid for 14 months, needs 2 SCTs.
399 time_2015_3_0_25_11_25_0_0, time_2016_6_0_6_11_25_0_0, 2},
400 {// Cert valid for exactly 15 months, needs 3 SCTs.
401 time_2015_3_0_25_11_25_0_0, time_2016_6_0_25_11_25_0_0, 3},
402 {// Cert valid for over 15 months, needs 3 SCTs.
403 time_2015_3_0_25_11_25_0_0, time_2016_6_0_27_11_25_0_0, 3},
404 {// Cert valid for exactly 27 months, needs 3 SCTs.
405 time_2015_3_0_25_11_25_0_0, time_2017_6_0_25_11_25_0_0, 3},
406 {// Cert valid for over 27 months, needs 4 SCTs.
407 time_2015_3_0_25_11_25_0_0, time_2017_6_0_28_11_25_0_0, 4},
408 {// Cert valid for exactly 39 months, needs 4 SCTs.
409 time_2015_3_0_25_11_25_0_0, time_2018_6_0_25_11_25_0_0, 4},
410 {// Cert valid for over 39 months, needs 5 SCTs.
411 time_2015_3_0_25_11_25_0_0, time_2018_6_0_27_11_25_0_0, 5}};
412
413 for (size_t i = 0; i < base::size(kTestData); ++i) {
414 SCOPED_TRACE(i);
415 const base::Time& start = kTestData[i].validity_start;
416 const base::Time& end = kTestData[i].validity_end;
417 size_t required_scts = kTestData[i].scts_required;
418
419 // Create a self-signed certificate with exactly the validity period.
420 std::string cert_data;
421 ASSERT_TRUE(net::x509_util::CreateSelfSignedCert(
422 private_key->key(), net::x509_util::DIGEST_SHA256, "CN=test",
423 i * 10 + required_scts, start, end, {}, &cert_data));
424 scoped_refptr<X509Certificate> cert(
425 X509Certificate::CreateFromBytes(cert_data.data(), cert_data.size()));
426 ASSERT_TRUE(cert);
427
428 for (size_t i = 0; i < required_scts - 1; ++i) {
429 SCTList scts;
430 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, i,
431 std::vector<std::string>(), false, &scts);
432 EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
433 policy_enforcer_->CheckCompliance(cert.get(), scts,
434 NetLogWithSource()))
435 << " for: " << (end - start).InDays() << " and " << required_scts
436 << " scts=" << scts.size() << " i=" << i;
437 }
438 SCTList scts;
439 FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
440 required_scts, std::vector<std::string>(), false,
441 &scts);
442 EXPECT_EQ(
443 CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
444 policy_enforcer_->CheckCompliance(cert.get(), scts, NetLogWithSource()))
445 << " for: " << (end - start).InDays() << " and " << required_scts
446 << " scts=" << scts.size();
447 }
448}
449
450} // namespace
451
452} // namespace certificate_transparency
453