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

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: For review Created 4 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
(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 "base/base64.h"
8 #include "base/memory/ptr_util.h"
9 #include "base/memory/singleton.h"
10 #include "components/cast_certificate/cast_cert_validator.h"
11 #include "components/cast_certificate/proto/revocation.pb.h"
12 #include "crypto/sha2.h"
13 #include "net/cert/internal/parse_certificate.h"
14 #include "net/cert/internal/parsed_certificate.h"
15 #include "net/cert/internal/signature_algorithm.h"
16 #include "net/cert/internal/signature_policy.h"
17 #include "net/cert/internal/trust_store.h"
18 #include "net/cert/internal/verify_certificate_chain.h"
19 #include "net/cert/internal/verify_signed_data.h"
20 #include "net/cert/x509_certificate.h"
21 #include "net/der/input.h"
22 #include "net/der/parser.h"
23 #include "net/der/parse_values.h"
24
25 namespace cast_certificate {
26 namespace {
27 // -------------------------------------------------------------------------
28 // Cast CRL trust anchors.
29 // -------------------------------------------------------------------------
30
31 // There is one trusted root for Cast CRL certificate chains:
32 //
33 // (1) CN=Cast CRL Root CA (kCastCRLRootCaDer)
34 //
35 // These constants are defined by the file included next:
36
37 #include "components/cast_certificate/cast_crl_root_ca_cert_der-inc.h"
38
39 // Singleton for the Cast CRL trust store.
40 class CastCRLTrustStore {
41 public:
42 static CastCRLTrustStore* GetInstance() {
43 return base::Singleton<CastCRLTrustStore, base::LeakySingletonTraits<
44 CastCRLTrustStore>>::get();
45 }
46
47 static net::TrustStore& Get() { return GetInstance()->store_; }
48
49 private:
50 friend struct base::DefaultSingletonTraits<CastCRLTrustStore>;
51
52 CastCRLTrustStore() {
53 // Initialize the trust store with the root certificate.
54 // TODO (ryanchung): Add official Cast CRL Root here
55 // scoped_refptr<net::ParsedCertificate> root = net::ParsedCertificate::
56 // net::ParsedCertificate::CreateFromCertificateData(
57 // kCastCRLRootCaDer, sizeof(kCastCRLRootCaDer),
58 // net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE);
59 // CHECK(root);
60 // store_.AddTrustedCertificate(std::move(root));
61 }
62
63 net::TrustStore store_;
64 DISALLOW_COPY_AND_ASSIGN(CastCRLTrustStore);
65 };
66
67 // The parsed certificate information.
68 struct CastCertificateInfo {
69 uint64_t serial_number;
70 std::string issuer_name;
71 net::der::Input spki_tlv;
72 std::string key_hash;
73 };
74
75 // Parses a der encoded certificate for the information for CastCertificateInfo.
76 bool ParseCertificateInfo(scoped_refptr<net::ParsedCertificate>& cert,
77 CastCertificateInfo* parsed_cert) {
78 // Parse the serial number.
79 if (!net::der::ParseUint64(cert->tbs().serial_number,
80 &parsed_cert->serial_number)) {
81 return false;
82 }
83
84 // Parse the public key and calculate its hash.
sheretov 2016/06/13 19:18:35 This is wrong. We should hash the SPKI directly -
ryanchung 2016/06/21 21:45:06 OK, Done. Originally, the hash of the subject publ
85 net::der::Parser outer_parser(cert->tbs().spki_tlv);
86 net::der::Parser spki_parser;
87 net::der::BitString key_bits;
88 if (!outer_parser.ReadSequence(&spki_parser))
89 return false;
90 if (outer_parser.HasMore())
91 return false;
92 if (!spki_parser.SkipTag(net::der::kSequence))
93 return false;
94 if (!spki_parser.ReadBitString(&key_bits))
95 return false;
96 std::string public_key = key_bits.bytes().AsString();
97 parsed_cert->key_hash = crypto::SHA256HashString(public_key);
98 parsed_cert->spki_tlv = cert->tbs().spki_tlv;
99
100 // The issuer's name.
101 parsed_cert->issuer_name = cert->normalized_issuer().AsString();
102 return true;
103 }
104
105 // Converts a base::Time::Exploded to a net::der::GeneralizedTime.
106 net::der::GeneralizedTime ConvertExplodedTime(
107 const base::Time::Exploded& exploded) {
108 net::der::GeneralizedTime result;
109 result.year = exploded.year;
110 result.month = exploded.month;
111 result.day = exploded.day_of_month;
112 result.hours = exploded.hour;
113 result.minutes = exploded.minute;
114 result.seconds = exploded.second;
115 return result;
116 }
117
118 // Specifies the signature verification policy.
119 std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() {
120 return base::WrapUnique(new net::SimpleSignaturePolicy(2048));
121 }
122
123 class ScopedCheckUnreferencedCert {
124 public:
125 explicit ScopedCheckUnreferencedCert(net::ParsedCertificate* cert)
126 : cert_(cert) {}
127 ~ScopedCheckUnreferencedCert() { DCHECK(cert_->HasOneRef()); }
128
129 private:
130 net::ParsedCertificate* cert_;
131 };
132
133 // Verifies the CRL is signed by a trusted CRL authority at the time the CRL was
sheretov 2016/06/13 19:18:35 The CRL, including all the certs in the path shoul
ryanchung 2016/06/21 21:45:05 Added the current time here. As discussed offline,
134 // issued.
135 bool VerifyCRL(const Crl& crl, const TbsCrl& tbs_crl) {
136 // Verify the trust of the CRL authority.
137 base::Time signing_time =
138 base::Time::UnixEpoch() +
139 base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis());
140 base::Time::Exploded signing_time_exploded;
141 signing_time.UTCExplode(&signing_time_exploded);
142 net::der::GeneralizedTime signing_time_generalized =
143 ConvertExplodedTime(signing_time_exploded);
144 std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
145 scoped_refptr<net::ParsedCertificate> parsed_cert =
146 net::ParsedCertificate::CreateFromCertificateData(
147 reinterpret_cast<const uint8_t*>(crl.signer_cert().data()),
148 crl.signer_cert().size(),
149 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE);
150 if (parsed_cert == nullptr)
151 return false;
152 ScopedCheckUnreferencedCert ref_checker(parsed_cert.get());
153 input_chain.push_back(std::move(parsed_cert));
154
155 auto signature_policy = CreateCastSignaturePolicy();
156 if (!net::VerifyCertificateChain(input_chain, CastCRLTrustStore::Get(),
157 signature_policy.get(),
158 signing_time_generalized)) {
159 return false;
160 }
161
162 // Parse the signer certificate
163 if (input_chain.size() == 0)
164 return false;
165 CastCertificateInfo cert_info;
166 if (!ParseCertificateInfo(input_chain[0], &cert_info))
167 return false;
168
169 // Parse the signature.
170 std::string signature;
171 signature = crl.signature();
172 net::der::BitString signature_value_bit_string;
173 signature_value_bit_string =
174 net::der::BitString(net::der::Input(&signature), 0);
175
176 // Verify the signature
177 std::unique_ptr<net::SignaturePolicy> policy =
178 base::WrapUnique(new net::SimpleSignaturePolicy(2048));
179 std::unique_ptr<net::SignatureAlgorithm> signature_algorithm_type =
180 net::SignatureAlgorithm::CreateRsaPkcs1(net::DigestAlgorithm::Sha256);
181 if (!VerifySignedData(
182 *signature_algorithm_type, net::der::Input(&crl.tbs_crl()),
183 signature_value_bit_string, cert_info.spki_tlv, policy.get())) {
184 VLOG(2) << "CRL - Signature verification failed.";
185 return false;
186 }
187 return true;
188 }
189
190 class CastCRLImpl : public CastCRL {
191 public:
192 CastCRLImpl(const TbsCrl& tbs_crl);
193 ~CastCRLImpl() override;
194
195 bool VerifyDeviceCertRevocation(const std::vector<std::string>& certs,
196 const base::Time::Exploded& time) override;
197
198 private:
199 uint64_t issuance_time_millis_;
200 uint64_t validity_period_millis_;
201
202 // Hash of all revoked public key.
203 std::unordered_set<std::string> revoked_hashes_;
204
205 // Revoked serial number ranges indexed by issuer public key hash.
206 std::unordered_map<std::string, std::pair<uint64_t, uint64_t>>
207 revoked_serial_numbers_;
208 DISALLOW_COPY_AND_ASSIGN(CastCRLImpl);
209 };
210
211 CastCRLImpl::CastCRLImpl(const TbsCrl& tbs_crl) {
212 // Parse the validity information.
213 issuance_time_millis_ = tbs_crl.issuance_time_millis();
214 validity_period_millis_ = tbs_crl.validity_period_millis();
215
216 // Parse the revoked hashes.
217 for (const auto& hash : tbs_crl.revoked_public_key_hashes()) {
218 revoked_hashes_.insert(hash);
219 }
220
221 // Parse the revoked serial ranges.
222 for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
223 std::string hash = range.issuer_public_key_hash();
224 uint64_t first_serial_number = range.first_serial_number();
225 uint64_t last_serial_number = range.last_serial_number();
226 std::pair<uint64_t, uint64_t> revocation_range(first_serial_number,
227 last_serial_number);
228 std::pair<std::string, std::pair<uint64_t, uint64_t>> revocation_entry(
229 hash, revocation_range);
230 revoked_serial_numbers_.insert(revocation_entry);
231 }
232 }
233
234 CastCRLImpl::~CastCRLImpl() {}
235
236 // Verifies the revocation status of the certificate chain, at the specified
237 // time.
238 bool CastCRLImpl::VerifyDeviceCertRevocation(
sheretov 2016/06/13 19:18:35 I would call this checkRevocation
ryanchung 2016/06/21 21:45:05 Done.
239 const std::vector<std::string>& certs,
240 const base::Time::Exploded& time) {
241 if (certs.empty())
242 return false;
243
244 // Check the validity of the CRl at the specified time.
245 base::Time utc_time = base::Time::FromUTCExploded(time);
246 base::Time validity_start =
247 base::Time::UnixEpoch() +
248 base::TimeDelta::FromMilliseconds(issuance_time_millis_);
249 base::Time validity_end =
250 base::Time::UnixEpoch() +
251 base::TimeDelta::FromMilliseconds(issuance_time_millis_ +
252 validity_period_millis_);
253 if (utc_time < validity_start || utc_time > validity_end) {
sheretov 2016/06/13 19:18:35 nit: utc_time >= validity_end
ryanchung 2016/06/21 21:45:06 Done.
254 VLOG(2) << "CRL expired. Perform hard fail.";
255 return false;
256 }
257
258 // Assume the certs form a valid path.
259 std::string issuer_key_hash;
260 for (int i = certs.size() - 1; i >= 0; --i) {
261 // Check public key revocation.
262 scoped_refptr<net::ParsedCertificate> parsed_cert =
263 net::ParsedCertificate::CreateFromCertificateData(
264 reinterpret_cast<const uint8_t*>(certs[i].data()), certs[i].size(),
265 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE);
266 if (parsed_cert == nullptr)
267 return false;
268 ScopedCheckUnreferencedCert ref_checker(parsed_cert.get());
269 CastCertificateInfo cert_info;
270 if (!ParseCertificateInfo(parsed_cert, &cert_info))
271 return false;
272 if (revoked_hashes_.find(cert_info.key_hash) != revoked_hashes_.end()) {
273 VLOG(2) << "Public key is revoked.";
274 return false;
275 }
276
277 // Check serial range revocation.
278 if (issuer_key_hash.empty()) {
279 std::string issuer_cert =
280 FindCastTrustAnchorByName(cert_info.issuer_name);
sheretov 2016/06/13 19:18:35 This seems awkward here. Can we pass in the root's
ryanchung 2016/06/21 21:45:05 I've updated this function to take in the verified
281 if (issuer_cert.empty())
282 return false;
283 scoped_refptr<net::ParsedCertificate> parsed_issuer_cert =
284 net::ParsedCertificate::CreateFromCertificateData(
285 reinterpret_cast<const uint8_t*>(issuer_cert.data()),
286 issuer_cert.size(),
287 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE);
288 if (parsed_cert == nullptr)
289 return false;
290 ScopedCheckUnreferencedCert ref_checker(parsed_issuer_cert.get());
291 CastCertificateInfo issuer_cert_info;
292 if (!ParseCertificateInfo(parsed_issuer_cert, &issuer_cert_info))
293 return false;
294 if (revoked_hashes_.find(issuer_cert_info.key_hash) !=
sheretov 2016/06/13 19:18:35 Why are we checking revocation on the trust anchor
ryanchung 2016/06/21 21:45:06 Removed.
295 revoked_hashes_.end()) {
296 VLOG(2) << "Issuer public key is revoked.";
297 return false;
298 }
299 issuer_key_hash = issuer_cert_info.key_hash;
300 }
301 DCHECK(!issuer_key_hash.empty());
302 auto issuer_revoked_serials = revoked_serial_numbers_.find(issuer_key_hash);
303 if (issuer_revoked_serials != revoked_serial_numbers_.end()) {
304 if (issuer_revoked_serials->second.first <= cert_info.serial_number &&
305 issuer_revoked_serials->second.second >= cert_info.serial_number) {
306 VLOG(2) << "Serial number is revoked";
307 return false;
308 }
309 }
310 issuer_key_hash = cert_info.key_hash;
311 }
312 return true;
313 }
314
315 } // namespace
316
317 std::unique_ptr<CastCRL> ParseCRL(const std::string& crl_proto) {
318 CrlBundle cast_crl;
319 if (!cast_crl.ParseFromString(crl_proto)) {
320 LOG(ERROR) << "CRL - Binary could not be parsed.";
321 return nullptr;
322 }
323 TbsCrl tbs_crl;
324 Crl supported_crl;
325 bool supported_supported_crl_found = false;
sheretov 2016/06/13 19:18:35 supported_supported?
ryanchung 2016/06/21 21:45:05 Removed this due to comment below.
326 for (auto const& crl : cast_crl.crls()) {
327 if (!tbs_crl.ParseFromString(crl.tbs_crl())) {
328 LOG(WARNING) << "Binary TBS CRL could not be parsed.";
329 continue;
330 }
331 // The supported version of the CRL is version 0
332 // version 0: Spki Hash Algorithm = SHA-256
333 // Signature Algorithm = RSA-PKCS1 V1.5 with SHA-256
334 if (tbs_crl.version() == 0) {
sheretov 2016/06/13 19:18:35 Please create a constant for the CRL version, so w
ryanchung 2016/06/21 21:45:06 Done.
335 supported_crl = crl;
sheretov 2016/06/13 19:18:35 Is this assignment making a copy? Why have the tb
ryanchung 2016/06/21 21:45:05 Changed this to return in the loop.
336 supported_supported_crl_found = true;
337 break;
338 }
339 }
340 if (!supported_supported_crl_found) {
341 LOG(ERROR) << "No supported version of revocation data.";
342 return nullptr;
343 }
344 if (!VerifyCRL(supported_crl, tbs_crl)) {
345 LOG(ERROR) << "CRL - Verification failed.";
346 return nullptr;
347 }
348
349 return base::WrapUnique(new CastCRLImpl(tbs_crl));
350 }
351
352 bool AddCRLTrustAnchorForTest(const uint8_t* data, size_t length) {
353 scoped_refptr<net::ParsedCertificate> anchor(
354 net::ParsedCertificate::CreateFromCertificateData(
355 data, length,
356 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE));
357 if (!anchor)
358 return false;
359 CastCRLTrustStore::Get().AddTrustedCertificate(std::move(anchor));
360 return true;
361 }
362
363 void ClearCRLTrustAnchorForTest() {
364 CastCRLTrustStore::Get().Clear();
365 }
366
367 } // namespace cast_certificate
OLDNEW
« no previous file with comments | « components/cast_certificate/cast_crl.h ('k') | components/cast_certificate/cast_crl_root_ca_cert_der-inc.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698