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

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: Fixed proto again 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/path_builder.h"
18 #include "net/cert/internal/signature_algorithm.h"
19 #include "net/cert/internal/signature_policy.h"
20 #include "net/cert/internal/trust_store.h"
21 #include "net/cert/internal/verify_certificate_chain.h"
22 #include "net/cert/internal/verify_signed_data.h"
23 #include "net/cert/x509_certificate.h"
24 #include "net/der/input.h"
25 #include "net/der/parser.h"
26 #include "net/der/parse_values.h"
27
28 namespace cast_certificate {
29 namespace {
30
31 enum CrlVersion {
32 // version 0: Spki Hash Algorithm = SHA-256
33 // Signature Algorithm = RSA-PKCS1 V1.5 with SHA-256
34 CRL_VERSION_0 = 0,
35 };
36
37 // -------------------------------------------------------------------------
38 // Cast CRL trust anchors.
39 // -------------------------------------------------------------------------
40
41 // There is one trusted root for Cast CRL certificate chains:
42 //
43 // (1) CN=Cast CRL Root CA (kCastCRLRootCaDer)
44 //
45 // These constants are defined by the file included next:
46
47 #include "components/cast_certificate/cast_crl_root_ca_cert_der-inc.h"
48
49 // Singleton for the Cast CRL trust store.
50 class CastCRLTrustStore {
51 public:
52 static CastCRLTrustStore* GetInstance() {
53 return base::Singleton<CastCRLTrustStore, base::LeakySingletonTraits<
54 CastCRLTrustStore>>::get();
55 }
56
57 static net::TrustStore& Get() { return GetInstance()->store_; }
58
59 private:
60 friend struct base::DefaultSingletonTraits<CastCRLTrustStore>;
61
62 CastCRLTrustStore() {
63 // Initialize the trust store with the root certificate.
64 // TODO (ryanchung): Add official Cast CRL Root here
eroman 2016/07/15 22:52:48 nit: TODO(ryanchung) (not sure if that is normativ
ryanchung 2016/07/18 23:39:08 The root hasn't been generated due to administrati
eroman 2016/07/19 01:54:58 That's fine, can submit a follow-up CL, and includ
65 // scoped_refptr<net::ParsedCertificate> root = net::ParsedCertificate::
66 // net::ParsedCertificate::CreateFromCertificateData(
67 // kCastCRLRootCaDer, sizeof(kCastCRLRootCaDer),
68 // net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, {});
69 // CHECK(root);
70 // store_.AddTrustedCertificate(std::move(root));
71 }
72
73 net::TrustStore store_;
74 DISALLOW_COPY_AND_ASSIGN(CastCRLTrustStore);
75 };
76
77 // Converts a base::Time::Exploded to a net::der::GeneralizedTime.
78 net::der::GeneralizedTime convertTimeExploded(
eroman 2016/07/15 22:52:48 (1) convertTimeExploded --> ConvertTimeExploded (2
ryanchung 2016/07/18 23:39:08 Went with (2) I've updated the API in cast_crl.h a
79 const base::Time::Exploded& exploded) {
80 net::der::GeneralizedTime result;
81 result.year = exploded.year;
82 result.month = exploded.month;
83 result.day = exploded.day_of_month;
84 result.hours = exploded.hour;
85 result.minutes = exploded.minute;
86 result.seconds = exploded.second;
87 return result;
88 }
89
90 // Converts a uint64_t utc time to net::der::GeneralizedTime.
eroman 2016/07/15 22:52:48 not sure that "utc time" is the right terminology.
ryanchung 2016/07/18 23:39:08 Done.
91 net::der::GeneralizedTime ConvertTimeSeconds(uint64_t seconds) {
92 base::Time utc_time =
93 base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(seconds);
eroman 2016/07/15 22:52:48 This has the possibility of overflow -- FromSecond
ryanchung 2016/07/18 23:39:08 Done.
94 base::Time::Exploded exploded;
95 utc_time.UTCExplode(&exploded);
96 return convertTimeExploded(exploded);
97 }
98
99 // Specifies the signature verification policy.
100 // The required algorithms are:
101 // RSASSA PKCS#1 v1.5 with SHA-256, using RSA keys 2048-bits or longer.
102 std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() {
103 return base::WrapUnique(new net::SimpleSignaturePolicy(2048));
104 }
105
106 // Verifies the CRL is signed by a trusted CRL authority at the time the CRL
107 // was issued. Verifies the signature of |tbs_crl| is valid based on the
108 // certificate and signature in |crl|. The validity of |tbs_crl| is verified
109 // at |time|. The validity period of the CRL is adjusted to be the earliest
110 // of the issuer certificate chain's expiration and the CRL's expiration and
111 // the result is stored in |overall_not_after|.
112 bool VerifyCRL(const Crl& crl,
113 const TbsCrl& tbs_crl,
114 const base::Time::Exploded& time,
115 net::der::GeneralizedTime* overall_not_after) {
116 // Verify the trust of the CRL authority.
117 scoped_refptr<net::ParsedCertificate> parsed_cert =
118 net::ParsedCertificate::CreateFromCertificateData(
119 reinterpret_cast<const uint8_t*>(crl.signer_cert().data()),
120 crl.signer_cert().size(),
121 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, {});
122 if (parsed_cert == nullptr) {
123 VLOG(2) << "CRL - Issuer certificate parsing failed.";
124 return false;
125 }
126
127 // Parse the signature.
eroman 2016/07/15 22:52:48 nti: "Parse the signature" --> "Wrap the signature
ryanchung 2016/07/18 23:39:08 Done.
128 net::der::BitString signature_value_bit_string = net::der::BitString(
129 net::der::Input(base::StringPiece(crl.signature())), 0);
130
131 // Verify the signature.
132 auto signature_policy = CreateCastSignaturePolicy();
133 std::unique_ptr<net::SignatureAlgorithm> signature_algorithm_type =
134 net::SignatureAlgorithm::CreateRsaPkcs1(net::DigestAlgorithm::Sha256);
135 if (!VerifySignedData(*signature_algorithm_type,
136 net::der::Input(&crl.tbs_crl()),
137 signature_value_bit_string, parsed_cert->tbs().spki_tlv,
138 signature_policy.get())) {
139 VLOG(2) << "CRL - Signature verification failed.";
140 return false;
141 }
142
143 // Verify the issuer certificate.
144 net::CertPathBuilder::Result result;
145 net::CertPathBuilder path_builder(
146 parsed_cert.get(), &CastCRLTrustStore::Get(), signature_policy.get(),
147 convertTimeExploded(time), &result);
148 net::CompletionStatus rv = path_builder.Run(base::Closure());
149 DCHECK_EQ(rv, net::CompletionStatus::SYNC);
150 if (!result.is_success() || result.paths.empty() ||
151 !result.paths[result.best_result_index]->is_success()) {
152 VLOG(2) << "CRL - Issuer certificate verification failed.";
153 return false;
154 }
eroman 2016/07/15 22:52:48 Please add a comment about why no checks on the le
ryanchung 2016/07/18 23:39:08 Done. Ahhh just realized after uploading Patch23..
155
156 // Verify the CRL is still valid.
157 net::der::GeneralizedTime verification_time = convertTimeExploded(time);
158 net::der::GeneralizedTime not_before =
159 ConvertTimeSeconds(tbs_crl.not_before_seconds());
eroman 2016/07/15 22:52:48 What is the role of setting a notBefore on the CRL
ryanchung 2016/07/18 23:39:08 That's a very good point. If a certificate expires
160 net::der::GeneralizedTime not_after =
161 ConvertTimeSeconds(tbs_crl.not_after_seconds());
162 if ((verification_time < not_before) || (verification_time > not_after)) {
163 VLOG(2) << "CRL - Not time-valid.";
164 return false;
165 }
166
167 // Set CRL expiry to the earliest of the cert chain expiry and CRL expiry.
168 *overall_not_after = not_after;
169 for (const auto& cert : result.paths[result.best_result_index]->path) {
170 net::der::GeneralizedTime cert_not_after = cert->tbs().validity_not_after;
171 if (cert_not_after < *overall_not_after)
172 *overall_not_after = cert_not_after;
173 }
174
175 // Perform sanity check on serial numbers.
176 for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
177 uint64_t first_serial_number = range.first_serial_number();
178 uint64_t last_serial_number = range.last_serial_number();
179 if (last_serial_number < first_serial_number) {
180 VLOG(2) << "CRL - Malformed serial number range.";
181 return false;
182 }
183 }
184 return true;
185 }
186
187 class CastCRLImpl : public CastCRL {
188 public:
189 CastCRLImpl(const TbsCrl& tbs_crl,
190 const net::der::GeneralizedTime& overall_not_after);
191 ~CastCRLImpl() override;
192
193 bool CheckRevocation(const net::ParsedCertificateList& trusted_chain,
194 const base::Time::Exploded& time) const override;
195
196 private:
197 net::der::GeneralizedTime not_before;
eroman 2016/07/15 22:52:48 name private variables with trailing underscore.
ryanchung 2016/07/18 23:39:08 Done.
198 net::der::GeneralizedTime not_after;
eroman 2016/07/15 22:52:48 same
ryanchung 2016/07/18 23:39:08 Done.
199
200 // Revoked public key hashes.
201 // The values consist of the SHA256 hash of the SubjectPublicKeyInfo.
202 std::set<std::string> revoked_hashes_;
203
204 // Revoked serial number ranges indexed by issuer public key hash.
205 // The key is the SHA256 hash of issuer's SubjectPublicKeyInfo.
206 // The value is a list of revoked serial number ranges.
207 std::unordered_map<std::string, std::set<std::pair<uint64_t, uint64_t>>>
eroman 2016/07/15 22:52:48 For the second part, I suggest using a std::vector
ryanchung 2016/07/18 23:39:07 Done.
208 revoked_serial_numbers_;
209 DISALLOW_COPY_AND_ASSIGN(CastCRLImpl);
210 };
211
212 CastCRLImpl::CastCRLImpl(const TbsCrl& tbs_crl,
213 const net::der::GeneralizedTime& overall_not_after) {
214 // Parse the validity information.
215 not_before = ConvertTimeSeconds(tbs_crl.not_before_seconds());
216 not_after = ConvertTimeSeconds(tbs_crl.not_after_seconds());
217 if (overall_not_after < not_after)
218 not_after = overall_not_after;
219
220 // Parse the revoked hashes.
221 for (const auto& hash : tbs_crl.revoked_public_key_hashes()) {
222 revoked_hashes_.insert(hash);
223 }
224
225 // Parse the revoked serial ranges.
226 for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
227 std::string issuer_hash = range.issuer_public_key_hash();
228
229 auto issuer_iter = revoked_serial_numbers_.find(issuer_hash);
eroman 2016/07/15 22:52:48 How about using operator[], which combines the "ge
ryanchung 2016/07/18 23:39:08 Done.
230 if (issuer_iter == revoked_serial_numbers_.end()) {
231 std::set<std::pair<uint64_t, uint64_t>> serials;
232 std::pair<std::string, std::set<std::pair<uint64_t, uint64_t>>>
233 revocation_entry = std::make_pair(issuer_hash, serials);
234 issuer_iter = revoked_serial_numbers_.insert(revocation_entry).first;
235 }
236
237 uint64_t first_serial_number = range.first_serial_number();
238 uint64_t last_serial_number = range.last_serial_number();
239 std::pair<uint64_t, uint64_t> revocation_range(first_serial_number,
eroman 2016/07/15 22:52:48 Can you define a struct for this instead? struct
ryanchung 2016/07/18 23:39:08 Done.
240 last_serial_number);
241 issuer_iter->second.insert(revocation_range);
242 }
243 }
244
245 CastCRLImpl::~CastCRLImpl() {}
246
247 // Verifies the revocation status of the certificate chain, at the specified
248 // time.
249 bool CastCRLImpl::CheckRevocation(
250 const net::ParsedCertificateList& trusted_chain,
251 const base::Time::Exploded& time) const {
252 if (trusted_chain.empty())
253 return false;
254
255 // Check the validity of the CRL at the specified time.
256 net::der::GeneralizedTime verification_time = convertTimeExploded(time);
257 if ((verification_time < not_before) || (verification_time > not_after)) {
258 VLOG(2) << "CRL not time-valid. Perform hard fail.";
259 return false;
260 }
261
262 // Check revocation.
263 std::string issuer_key_hash;
264 for (int i = trusted_chain.size() - 1; i >= 0; --i) {
eroman 2016/07/15 22:52:48 style: I am generally fearful of this sort of loop
ryanchung 2016/07/18 23:39:08 Done. Forward direction sounds good. Thanks!
265 // Check public key revocation.
266 const scoped_refptr<net::ParsedCertificate> parsed_cert = trusted_chain[i];
267 // Calculate the public key's hash.
268 std::string spki_hash =
269 crypto::SHA256HashString(parsed_cert->tbs().spki_tlv.AsString());
270 if (revoked_hashes_.find(spki_hash) != revoked_hashes_.end()) {
271 VLOG(2) << "Public key is revoked.";
272 return false;
273 }
274
275 // Check serial range revocation.
276 if (!issuer_key_hash.empty()) {
277 auto issuer_iter = revoked_serial_numbers_.find(issuer_key_hash);
278 if (issuer_iter != revoked_serial_numbers_.end()) {
279 uint64_t serial_number;
280 // Only Google generated device certificates will be revoked by range.
281 // These will always be less than 64 bits in length.
282 if (!net::der::ParseUint64(parsed_cert->tbs().serial_number,
283 &serial_number)) {
284 continue;
285 }
286 for (auto const& revoked_serial : issuer_iter->second) {
287 if (revoked_serial.first <= serial_number &&
288 revoked_serial.second >= serial_number) {
289 VLOG(2) << "Serial number is revoked";
290 return false;
291 }
292 }
293 }
294 }
295 issuer_key_hash = spki_hash;
296 }
297 return true;
298 }
299
300 } // namespace
301
302 std::unique_ptr<CastCRL> ParseAndVerifyCRL(const std::string& crl_proto,
303 const base::Time::Exploded& time) {
304 CrlBundle crl_bundle;
305 if (!crl_bundle.ParseFromString(crl_proto)) {
306 LOG(ERROR) << "CRL - Binary could not be parsed.";
307 return nullptr;
308 }
309 for (auto const& crl : crl_bundle.crls()) {
310 TbsCrl tbs_crl;
311 if (!tbs_crl.ParseFromString(crl.tbs_crl())) {
312 LOG(WARNING) << "Binary TBS CRL could not be parsed.";
313 continue;
314 }
315 if (tbs_crl.version() != CRL_VERSION_0) {
316 continue;
317 }
318 net::der::GeneralizedTime overall_not_after;
319 if (!VerifyCRL(crl, tbs_crl, time, &overall_not_after)) {
320 LOG(ERROR) << "CRL - Verification failed.";
321 return nullptr;
eroman 2016/07/15 22:52:48 Failing here seems like a fine choice. However it
ryanchung 2016/07/18 23:39:08 The CRL bundle will contain at max a single copy f
322 }
323 return base::WrapUnique(new CastCRLImpl(tbs_crl, overall_not_after));
324 }
325 LOG(ERROR) << "No supported version of revocation data.";
326 return nullptr;
327 }
328
329 bool SetCRLTrustAnchorForTest(const std::string& cert) {
330 scoped_refptr<net::ParsedCertificate> anchor(
331 net::ParsedCertificate::CreateFromCertificateCopy(cert, {}));
332 CastCRLTrustStore::Get().Clear();
333 if (!anchor)
334 return false;
335 CastCRLTrustStore::Get().AddTrustedCertificate(std::move(anchor));
336 return true;
337 }
338
339 } // namespace cast_certificate
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698