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

Side by Side Diff: components/cast_certificate/cast_crl.cc

Issue 2050983002: Cast device revocation checking. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Bypass serial number range revocation check for serials > 64b Created 4 years, 5 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "components/cast_certificate/cast_crl.h"
6
7 #include <unordered_map>
8 #include <unordered_set>
9
10 #include "base/base64.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/memory/singleton.h"
13 #include "components/cast_certificate/proto/revocation.pb.h"
14 #include "crypto/sha2.h"
15 #include "net/cert/internal/parse_certificate.h"
16 #include "net/cert/internal/parsed_certificate.h"
17 #include "net/cert/internal/signature_algorithm.h"
18 #include "net/cert/internal/signature_policy.h"
19 #include "net/cert/internal/trust_store.h"
20 #include "net/cert/internal/verify_certificate_chain.h"
21 #include "net/cert/internal/verify_signed_data.h"
22 #include "net/cert/x509_certificate.h"
23 #include "net/der/input.h"
24 #include "net/der/parser.h"
25 #include "net/der/parse_values.h"
26
27 namespace cast_certificate {
28 namespace {
29
30 enum CrlVersion {
31 // version 0: Spki Hash Algorithm = SHA-256
32 // Signature Algorithm = RSA-PKCS1 V1.5 with SHA-256
33 CRL_VERSION_0 = 0,
34 };
35
36 // -------------------------------------------------------------------------
37 // Cast CRL trust anchors.
38 // -------------------------------------------------------------------------
39
40 // There is one trusted root for Cast CRL certificate chains:
41 //
42 // (1) CN=Cast CRL Root CA (kCastCRLRootCaDer)
43 //
44 // These constants are defined by the file included next:
45
46 #include "components/cast_certificate/cast_crl_root_ca_cert_der-inc.h"
47
48 // Singleton for the Cast CRL trust store.
49 class CastCRLTrustStore {
50 public:
51 static CastCRLTrustStore* GetInstance() {
52 return base::Singleton<CastCRLTrustStore, base::LeakySingletonTraits<
53 CastCRLTrustStore>>::get();
54 }
55
56 static net::TrustStore& Get() { return GetInstance()->store_; }
57
58 private:
59 friend struct base::DefaultSingletonTraits<CastCRLTrustStore>;
60
61 CastCRLTrustStore() {
62 // Initialize the trust store with the root certificate.
63 // TODO (ryanchung): Add official Cast CRL Root here
64 // scoped_refptr<net::ParsedCertificate> root = net::ParsedCertificate::
65 // net::ParsedCertificate::CreateFromCertificateData(
66 // kCastCRLRootCaDer, sizeof(kCastCRLRootCaDer),
67 // net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, {});
68 // CHECK(root);
69 // store_.AddTrustedCertificate(std::move(root));
70 }
71
72 net::TrustStore store_;
73 DISALLOW_COPY_AND_ASSIGN(CastCRLTrustStore);
74 };
75
76 // Converts a base::Time::Exploded to a net::der::GeneralizedTime.
77 net::der::GeneralizedTime ConvertExplodedTime(
78 const base::Time::Exploded& exploded) {
79 // Add 500 milliseconds and strip the milliseconds.
80 // Effectively rounding the original time to the nearest second.
81 base::Time rounded_time = base::Time::FromUTCExploded(exploded);
82 rounded_time += base::TimeDelta::FromMilliseconds(500);
83 base::Time::Exploded rounded_exploded_time;
84 rounded_time.UTCExplode(&rounded_exploded_time);
85
86 net::der::GeneralizedTime result;
87 result.year = rounded_exploded_time.year;
88 result.month = rounded_exploded_time.month;
89 result.day = rounded_exploded_time.day_of_month;
90 result.hours = rounded_exploded_time.hour;
91 result.minutes = rounded_exploded_time.minute;
92 result.seconds = rounded_exploded_time.second;
93 return result;
94 }
95
96 // Converts a ner::der::GeneralizedTime to base::Time::Exploded
97 base::Time::Exploded ConvertGeneralizedTime(
98 const net::der::GeneralizedTime generalized) {
99 base::Time::Exploded result = {0};
100 result.year = generalized.year;
101 result.month = generalized.month;
102 result.day_of_month = generalized.day;
103 result.hour = generalized.hours;
104 result.minute = generalized.minutes;
105 result.second = generalized.seconds;
106 return result;
107 }
108
109 // Specifies the signature verification policy.
110 std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() {
111 return base::WrapUnique(new net::SimpleSignaturePolicy(2048));
112 }
113
114 class ScopedCheckUnreferencedCert {
115 public:
116 explicit ScopedCheckUnreferencedCert(net::ParsedCertificate* cert)
117 : cert_(cert) {}
118 ~ScopedCheckUnreferencedCert() { DCHECK(cert_->HasOneRef()); }
119
120 private:
121 net::ParsedCertificate* cert_;
122 };
123
124 // Verifies the CRL is signed by a trusted CRL authority at the time the CRL was
125 // issued.
126 bool VerifyCRL(const Crl& crl,
sheretov 2016/07/08 18:07:07 Documenting the arguments might be useful for read
ryanchung 2016/07/08 22:49:29 Done.
127 const TbsCrl& tbs_crl,
128 const base::Time::Exploded& time,
129 base::Time* overall_validity_end) {
130 // Verify the trust of the CRL authority.
131 net::der::GeneralizedTime signing_time_generalized =
sheretov 2016/07/08 18:07:07 I would call this "verification_time_generalized".
ryanchung 2016/07/08 22:49:29 Done.
132 ConvertExplodedTime(time);
133 std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
134 scoped_refptr<net::ParsedCertificate> parsed_cert =
135 net::ParsedCertificate::CreateFromCertificateData(
136 reinterpret_cast<const uint8_t*>(crl.signer_cert().data()),
137 crl.signer_cert().size(),
138 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, {});
139 if (parsed_cert == nullptr) {
140 VLOG(2) << "CRL - Issuer certificate parsing failed.";
141 return false;
142 }
143
144 // Parse the signature.
145 std::string signature;
146 signature = crl.signature();
sheretov 2016/07/08 18:07:07 I would combine this with the previous line: std::
ryanchung 2016/07/08 22:49:29 Done.
147 net::der::BitString signature_value_bit_string;
148 signature_value_bit_string =
149 net::der::BitString(net::der::Input(&signature), 0);
150
151 // Verify the signature
sheretov 2016/07/08 18:07:07 style consistency nit: "// Parse the signature." c
ryanchung 2016/07/08 22:49:29 Done.
152 std::unique_ptr<net::SignaturePolicy> policy =
153 base::WrapUnique(new net::SimpleSignaturePolicy(2048));
154 std::unique_ptr<net::SignatureAlgorithm> signature_algorithm_type =
155 net::SignatureAlgorithm::CreateRsaPkcs1(net::DigestAlgorithm::Sha256);
156 if (!VerifySignedData(*signature_algorithm_type,
157 net::der::Input(&crl.tbs_crl()),
158 signature_value_bit_string, parsed_cert->tbs().spki_tlv,
159 policy.get())) {
160 VLOG(2) << "CRL - Signature verification failed.";
161 return false;
162 }
163
164 // Verify the issuer certificate
165 ScopedCheckUnreferencedCert ref_checker(parsed_cert.get());
166 input_chain.push_back(std::move(parsed_cert));
167
168 auto signature_policy = CreateCastSignaturePolicy();
169 std::vector<scoped_refptr<net::ParsedCertificate>> trusted_chain;
170 if (!net::VerifyCertificateChain(input_chain, CastCRLTrustStore::Get(),
171 signature_policy.get(),
172 signing_time_generalized, &trusted_chain)) {
173 VLOG(2) << "CRL - Issuer certificate verification failed.";
174 return false;
175 }
176
177 // Verify the CRL is still valid
178 base::Time utc_time = base::Time::FromUTCExploded(time);
179 base::Time validity_start =
180 base::Time::UnixEpoch() +
181 base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis());
182 base::Time validity_end =
183 base::Time::UnixEpoch() +
184 base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis() +
185 tbs_crl.validity_period_millis());
186 if ((utc_time < validity_start) || (utc_time >= validity_end)) {
187 VLOG(2) << "CRL - Expired.";
sheretov 2016/07/08 18:07:07 "CRL not time-valid" or something to that effect,
ryanchung 2016/07/08 22:49:29 Done.
188 return false;
189 }
190
191 // Set CRL expiry to the earliest of the cert chain expiry and CRL expiry.
192 *overall_validity_end = validity_end;
193 for (const auto cert : trusted_chain) {
194 base::Time::Exploded cert_expiry_exploded =
195 ConvertGeneralizedTime(cert->tbs().validity_not_after);
196 base::Time cert_time;
197 if (!base::Time::FromUTCExploded(cert_expiry_exploded, &cert_time))
198 return false;
sheretov 2016/07/08 18:07:07 This may warrant a log statement.
ryanchung 2016/07/08 22:49:29 Done.
199 cert_time += base::TimeDelta::FromMilliseconds(500);
sheretov 2016/07/08 18:07:07 Consider explaining the rounding logic and why it'
ryanchung 2016/07/08 22:49:29 Done.
200 if (cert_time < *overall_validity_end)
201 *overall_validity_end = cert_time;
202 }
203
204 // Perform sanity check on serial numbers
205 for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
206 uint64_t first_serial_number = range.first_serial_number();
207 uint64_t last_serial_number = range.last_serial_number();
208 if (last_serial_number < first_serial_number) {
209 VLOG(2) << "CRL - Malformed serial number range.";
210 return false;
211 }
212 }
213 return true;
214 }
215
216 class CastCRLImpl : public CastCRL {
217 public:
218 CastCRLImpl(const TbsCrl& tbs_crl, const base::Time& overall_validity_end);
219 ~CastCRLImpl() override;
220
221 bool CheckRevocation(
222 const std::vector<scoped_refptr<net::ParsedCertificate>>& trusted_chain,
223 const base::Time::Exploded& time) const override;
224
225 private:
226 base::Time validity_start;
227 base::Time validity_end;
228
229 // Hash of all revoked public key.
sheretov 2016/07/08 18:07:07 s/Hash/Hashes/ s/key/keys/
ryanchung 2016/07/08 22:49:29 Done.
230 std::unordered_set<std::string> revoked_hashes_;
231
232 // Revoked serial number ranges indexed by issuer public key hash.
233 std::unordered_map<std::string, std::unordered_map<uint64_t, uint64_t>>
sheretov 2016/07/08 18:07:07 std::unordered_map<uint64_t, uint64_t> seems like
ryanchung 2016/07/08 22:49:29 Done.
234 revoked_serial_numbers_;
235 DISALLOW_COPY_AND_ASSIGN(CastCRLImpl);
236 };
237
238 CastCRLImpl::CastCRLImpl(const TbsCrl& tbs_crl,
239 const base::Time& overall_validity_end) {
240 // Parse the validity information.
241 validity_start =
242 base::Time::UnixEpoch() +
243 base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis());
244 validity_end =
245 base::Time::UnixEpoch() +
246 base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis() +
247 tbs_crl.validity_period_millis());
248 if (overall_validity_end < validity_end)
249 validity_end = overall_validity_end;
250
251 // Parse the revoked hashes.
252 for (const auto& hash : tbs_crl.revoked_public_key_hashes()) {
253 revoked_hashes_.insert(hash);
254 }
255
256 // Parse the revoked serial ranges.
257 for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
258 std::string hash = range.issuer_public_key_hash();
259 uint64_t first_serial_number = range.first_serial_number();
260 uint64_t last_serial_number = range.last_serial_number();
261
262 auto issuer_revoked_serials = revoked_serial_numbers_.find(hash);
sheretov 2016/07/08 18:07:07 readability nit: I find that naming variables like
ryanchung 2016/07/08 22:49:29 Done.
263 if (issuer_revoked_serials == revoked_serial_numbers_.end()) {
264 std::unordered_map<uint64_t, uint64_t> serials;
265 std::pair<std::string, std::unordered_map<uint64_t, uint64_t>>
sheretov 2016/07/08 18:07:07 Consider using std::make_pair to improve readabili
ryanchung 2016/07/08 22:49:29 Done.
266 revocation_entry(hash, serials);
267 revoked_serial_numbers_.insert(revocation_entry);
268 issuer_revoked_serials = revoked_serial_numbers_.find(hash);
sheretov 2016/07/08 18:07:07 No need to call find(). insert() will give you the
ryanchung 2016/07/08 22:49:29 Done.
269 }
270 std::pair<uint64_t, uint64_t> revocation_range(first_serial_number,
271 last_serial_number);
272 issuer_revoked_serials->second.insert(revocation_range);
273 }
274 }
275
276 CastCRLImpl::~CastCRLImpl() {}
277
278 // Verifies the revocation status of the certificate chain, at the specified
279 // time.
280 bool CastCRLImpl::CheckRevocation(
281 const std::vector<scoped_refptr<net::ParsedCertificate>>& trusted_chain,
282 const base::Time::Exploded& time) const {
283 if (trusted_chain.empty())
284 return false;
285
286 // Check the validity of the CRL at the specified time.
287 base::Time utc_time = base::Time::FromUTCExploded(time);
288 if ((utc_time < validity_start) || (utc_time >= validity_end)) {
289 VLOG(2) << "CRL expired. Perform hard fail.";
sheretov 2016/07/08 18:07:07 "CRL not time-valid" or something to that effect,
ryanchung 2016/07/08 22:49:29 Done.
290 return false;
291 }
292
293 // Check revocation
294 std::string issuer_key_hash;
295 for (int i = trusted_chain.size() - 1; i >= 0; --i) {
296 // Check public key revocation.
297 scoped_refptr<net::ParsedCertificate> parsed_cert = trusted_chain[i];
298 if (parsed_cert == nullptr)
299 return false;
sheretov 2016/07/08 18:07:07 Log reason?
ryanchung 2016/07/08 22:49:29 Done.
300 // Calculate the public key's hash.
301 std::string spki_hash =
302 crypto::SHA256HashString(parsed_cert->tbs().spki_tlv.AsString());
303 if (revoked_hashes_.find(spki_hash) != revoked_hashes_.end()) {
304 VLOG(2) << "Public key is revoked.";
305 return false;
306 }
307
308 // Check serial range revocation.
309 if (!issuer_key_hash.empty()) {
310 auto issuer_revoked_serials =
311 revoked_serial_numbers_.find(issuer_key_hash);
312 if (issuer_revoked_serials != revoked_serial_numbers_.end()) {
313 for (auto const& revoked_serial : issuer_revoked_serials->second) {
314 uint64_t serial_number;
315 if (!net::der::ParseUint64(parsed_cert->tbs().serial_number,
sheretov 2016/07/08 18:07:07 Derive the value of serial_number once and then us
ryanchung 2016/07/08 22:49:29 Done.
316 &serial_number)) {
317 continue;
318 }
319 if (revoked_serial.first <= serial_number &&
320 revoked_serial.second >= serial_number) {
321 VLOG(2) << "Serial number is revoked";
322 return false;
323 }
324 }
325 }
326 }
327 issuer_key_hash = spki_hash;
328 }
329 return true;
330 }
331
332 } // namespace
333
334 std::unique_ptr<CastCRL> ParseAndVerifyCRL(const std::string& crl_proto,
335 const base::Time::Exploded& time) {
336 CrlBundle cast_crl;
sheretov 2016/07/08 18:07:07 s/cast_crl/crl_bundle/
ryanchung 2016/07/08 22:49:29 Done.
337 if (!cast_crl.ParseFromString(crl_proto)) {
338 LOG(ERROR) << "CRL - Binary could not be parsed.";
339 return nullptr;
340 }
341 for (auto const& crl : cast_crl.crls()) {
342 TbsCrl tbs_crl;
343 if (!tbs_crl.ParseFromString(crl.tbs_crl())) {
344 LOG(WARNING) << "Binary TBS CRL could not be parsed.";
345 continue;
346 }
347 if (tbs_crl.version() != CRL_VERSION_0) {
348 continue;
349 }
350 base::Time overall_validity_end;
351 if (!VerifyCRL(crl, tbs_crl, time, &overall_validity_end)) {
352 LOG(ERROR) << "CRL - Verification failed.";
353 return nullptr;
354 }
355 return base::WrapUnique(new CastCRLImpl(tbs_crl, overall_validity_end));
356 }
357 LOG(ERROR) << "No supported version of revocation data.";
358 return nullptr;
359 }
360
361 bool SetCRLTrustAnchorForTest(const uint8_t* data, size_t length) {
362 scoped_refptr<net::ParsedCertificate> anchor(
363 net::ParsedCertificate::CreateFromCertificateData(
364 data, length, net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE,
365 {}));
366 if (!anchor)
367 return false;
368 CastCRLTrustStore::Get().Clear();
369 CastCRLTrustStore::Get().AddTrustedCertificate(std::move(anchor));
370 return true;
371 }
372
373 void ClearCRLTrustAnchorForTest() {
374 CastCRLTrustStore::Get().Clear();
375 }
376
377 } // namespace cast_certificate
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698