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

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: Added test suite runner. Updated some tests. 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/proto/revocation.pb.h"
11 #include "crypto/sha2.h"
12 #include "net/cert/internal/parse_certificate.h"
13 #include "net/cert/internal/parsed_certificate.h"
14 #include "net/cert/internal/signature_algorithm.h"
15 #include "net/cert/internal/signature_policy.h"
16 #include "net/cert/internal/trust_store.h"
17 #include "net/cert/internal/verify_certificate_chain.h"
18 #include "net/cert/internal/verify_signed_data.h"
19 #include "net/cert/x509_certificate.h"
20 #include "net/der/input.h"
21 #include "net/der/parser.h"
22 #include "net/der/parse_values.h"
23
24 namespace cast_certificate {
25 namespace {
26
27 enum CrlVersion {
28 // version 0: Spki Hash Algorithm = SHA-256
29 // Signature Algorithm = RSA-PKCS1 V1.5 with SHA-256
30 CRL_VERSION_0 = 0,
31 };
32
33 // -------------------------------------------------------------------------
34 // Cast CRL trust anchors.
35 // -------------------------------------------------------------------------
36
37 // There is one trusted root for Cast CRL certificate chains:
38 //
39 // (1) CN=Cast CRL Root CA (kCastCRLRootCaDer)
40 //
41 // These constants are defined by the file included next:
42
43 #include "components/cast_certificate/cast_crl_root_ca_cert_der-inc.h"
44
45 // Singleton for the Cast CRL trust store.
46 class CastCRLTrustStore {
47 public:
48 static CastCRLTrustStore* GetInstance() {
49 return base::Singleton<CastCRLTrustStore, base::LeakySingletonTraits<
50 CastCRLTrustStore>>::get();
51 }
52
53 static net::TrustStore& Get() { return GetInstance()->store_; }
54
55 private:
56 friend struct base::DefaultSingletonTraits<CastCRLTrustStore>;
57
58 CastCRLTrustStore() {
59 // Initialize the trust store with the root certificate.
60 // TODO (ryanchung): Add official Cast CRL Root here
61 // scoped_refptr<net::ParsedCertificate> root = net::ParsedCertificate::
62 // net::ParsedCertificate::CreateFromCertificateData(
63 // kCastCRLRootCaDer, sizeof(kCastCRLRootCaDer),
64 // net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, {});
65 // CHECK(root);
66 // store_.AddTrustedCertificate(std::move(root));
67 }
68
69 net::TrustStore store_;
70 DISALLOW_COPY_AND_ASSIGN(CastCRLTrustStore);
71 };
72
73 // Converts a base::Time::Exploded to a net::der::GeneralizedTime.
74 net::der::GeneralizedTime ConvertExplodedTime(
75 const base::Time::Exploded& exploded) {
76 net::der::GeneralizedTime result;
77 result.year = exploded.year;
78 result.month = exploded.month;
79 result.day = exploded.day_of_month;
80 result.hours = exploded.hour;
81 result.minutes = exploded.minute;
82 result.seconds = exploded.second;
83 return result;
84 }
85
86 // Converts a ner::der::GeneralizedTime to base::Time::Exploded
87 base::Time::Exploded ConvertGeneralizedTime(
88 const net::der::GeneralizedTime generalized) {
89 base::Time::Exploded result;
90 result.year = generalized.year;
91 result.month = generalized.month;
92 result.day_of_month = generalized.day;
93 result.hour = generalized.hours;
94 result.minute = generalized.minutes;
95 result.second = generalized.seconds;
96 return result;
97 }
98
99 // Specifies the signature verification policy.
100 std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() {
101 return base::WrapUnique(new net::SimpleSignaturePolicy(2048));
102 }
103
104 class ScopedCheckUnreferencedCert {
105 public:
106 explicit ScopedCheckUnreferencedCert(net::ParsedCertificate* cert)
107 : cert_(cert) {}
108 ~ScopedCheckUnreferencedCert() { DCHECK(cert_->HasOneRef()); }
109
110 private:
111 net::ParsedCertificate* cert_;
112 };
113
114 // Verifies the CRL is signed by a trusted CRL authority at the time the CRL was
115 // issued.
116 bool VerifyCRL(const Crl& crl,
117 const TbsCrl& tbs_crl,
118 const base::Time::Exploded& time,
119 base::Time* overall_validity_end) {
120 // Verify the trust of the CRL authority.
121 net::der::GeneralizedTime signing_time_generalized =
122 ConvertExplodedTime(time);
123 std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
124 scoped_refptr<net::ParsedCertificate> parsed_cert =
125 net::ParsedCertificate::CreateFromCertificateData(
126 reinterpret_cast<const uint8_t*>(crl.signer_cert().data()),
127 crl.signer_cert().size(),
128 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, {});
129 if (parsed_cert == nullptr)
130 return false;
131
132 // Parse the signature.
133 std::string signature;
134 signature = crl.signature();
135 net::der::BitString signature_value_bit_string;
136 signature_value_bit_string =
137 net::der::BitString(net::der::Input(&signature), 0);
138
139 // Verify the signature
140 std::unique_ptr<net::SignaturePolicy> policy =
141 base::WrapUnique(new net::SimpleSignaturePolicy(2048));
142 std::unique_ptr<net::SignatureAlgorithm> signature_algorithm_type =
143 net::SignatureAlgorithm::CreateRsaPkcs1(net::DigestAlgorithm::Sha256);
144 if (!VerifySignedData(*signature_algorithm_type,
145 net::der::Input(&crl.tbs_crl()),
146 signature_value_bit_string, parsed_cert->tbs().spki_tlv,
147 policy.get())) {
148 VLOG(2) << "CRL - Signature verification failed.";
149 return false;
150 }
151
152 // Verify the issuer certificate
153 ScopedCheckUnreferencedCert ref_checker(parsed_cert.get());
154 input_chain.push_back(std::move(parsed_cert));
155
156 auto signature_policy = CreateCastSignaturePolicy();
157 std::vector<scoped_refptr<net::ParsedCertificate>> trusted_chain;
158 if (!net::VerifyCertificateChain(input_chain, CastCRLTrustStore::Get(),
159 signature_policy.get(),
160 signing_time_generalized, &trusted_chain)) {
161 return false;
sheretov 2016/06/24 20:24:30 Any reason this failure is not logged like the sig
ryanchung 2016/06/29 22:09:47 Done.
162 }
163
164 // Verify the CRL is still valid
165 base::Time utc_time = base::Time::FromUTCExploded(time);
166 base::Time validity_start =
167 base::Time::UnixEpoch() +
168 base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis());
169 base::Time validity_end =
170 base::Time::UnixEpoch() +
171 base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis() +
172 tbs_crl.validity_period_millis());
173 if ((utc_time < validity_start) || (utc_time >= validity_end))
174 return false;
175
176 // Set CRL expiry to the earliest of the cert chain expiry and CRL expiry.
177 *overall_validity_end = validity_end;
178 for (const auto cert : trusted_chain) {
179 base::Time::Exploded cert_expiry_exploded =
180 ConvertGeneralizedTime(cert->tbs().validity_not_after);
181 base::Time utc_time = base::Time::FromUTCExploded(cert_expiry_exploded);
182 if (utc_time < *overall_validity_end)
183 *overall_validity_end = utc_time;
184 }
185 return true;
186 }
187
188 class CastCRLImpl : public CastCRL {
189 public:
190 CastCRLImpl(const TbsCrl& tbs_crl, const base::Time overall_validity_end);
191 ~CastCRLImpl() override;
192
193 bool CheckRevocation(
194 const std::vector<scoped_refptr<net::ParsedCertificate>>& trusted_chain,
195 const base::Time::Exploded& time) const override;
196
197 private:
198 base::Time validity_start;
199 base::Time validity_end;
200
201 // Hash of all revoked public key.
202 std::unordered_set<std::string> revoked_hashes_;
203
204 // Revoked serial number ranges indexed by issuer public key hash.
205 std::unordered_map<std::string, std::pair<uint64_t, uint64_t>>
206 revoked_serial_numbers_;
207 DISALLOW_COPY_AND_ASSIGN(CastCRLImpl);
208 };
209
210 CastCRLImpl::CastCRLImpl(const TbsCrl& tbs_crl,
211 const base::Time overall_validity_end) {
sheretov 2016/06/24 20:24:31 Any reason why this is not a reference?
ryanchung 2016/06/29 22:09:48 Done.
212 // Parse the validity information.
213 validity_start =
214 base::Time::UnixEpoch() +
215 base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis());
216 validity_end =
217 base::Time::UnixEpoch() +
218 base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis() +
219 tbs_crl.validity_period_millis());
220 if (overall_validity_end < validity_end)
221 validity_end = overall_validity_end;
222
223 // Parse the revoked hashes.
224 for (const auto& hash : tbs_crl.revoked_public_key_hashes()) {
225 revoked_hashes_.insert(hash);
226 }
227
228 // Parse the revoked serial ranges.
229 for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
230 std::string hash = range.issuer_public_key_hash();
231 uint64_t first_serial_number = range.first_serial_number();
232 uint64_t last_serial_number = range.last_serial_number();
233 std::pair<uint64_t, uint64_t> revocation_range(first_serial_number,
234 last_serial_number);
235 std::pair<std::string, std::pair<uint64_t, uint64_t>> revocation_entry(
236 hash, revocation_range);
237 revoked_serial_numbers_.insert(revocation_entry);
238 }
239 }
240
241 CastCRLImpl::~CastCRLImpl() {}
242
243 // Verifies the revocation status of the certificate chain, at the specified
244 // time.
245 bool CastCRLImpl::CheckRevocation(
246 const std::vector<scoped_refptr<net::ParsedCertificate>>& trusted_chain,
247 const base::Time::Exploded& time) const {
248 if (trusted_chain.empty())
249 return false;
250
251 // Check the validity of the CRl at the specified time.
sheretov 2016/06/24 20:24:31 nit: s/CRl/CRL/
ryanchung 2016/06/29 22:09:48 Done.
252 base::Time utc_time = base::Time::FromUTCExploded(time);
253 if ((utc_time < validity_start) || (utc_time >= validity_end)) {
254 VLOG(2) << "CRL expired. Perform hard fail.";
255 return false;
256 }
257
258 // Check revocation
259 std::string issuer_key_hash;
260 for (int i = trusted_chain.size() - 1; i >= 0; --i) {
261 // Check public key revocation.
262 scoped_refptr<net::ParsedCertificate> parsed_cert = trusted_chain[i];
263 if (parsed_cert == nullptr)
264 return false;
265 // Calculate the public key's hash.
266 std::string spki_hash =
267 crypto::SHA256HashString(parsed_cert->tbs().spki_tlv.AsString());
268 if (revoked_hashes_.find(spki_hash) != revoked_hashes_.end()) {
269 VLOG(2) << "Public key is revoked.";
270 return false;
271 }
272
273 // Check serial range revocation.
274 if (!issuer_key_hash.empty()) {
275 DCHECK(!issuer_key_hash.empty());
sheretov 2016/06/24 20:24:30 Is this DCHECK needed given the condition in the i
ryanchung 2016/06/29 22:09:48 Done.
276 auto issuer_revoked_serials =
277 revoked_serial_numbers_.find(issuer_key_hash);
278 if (issuer_revoked_serials != revoked_serial_numbers_.end()) {
279 uint64_t serial_number;
280 if (!net::der::ParseUint64(parsed_cert->tbs().serial_number,
281 &serial_number)) {
282 return false;
sheretov 2016/06/24 20:24:31 This effectively (once there is at least one revok
ryanchung 2016/06/29 22:09:48 Done.
283 }
284 if (issuer_revoked_serials->second.first <= serial_number &&
285 issuer_revoked_serials->second.second >= serial_number) {
286 VLOG(2) << "Serial number is revoked";
287 return false;
288 }
289 }
290 }
291 issuer_key_hash = spki_hash;
292 }
293 return true;
294 }
295
296 } // namespace
297
298 std::unique_ptr<CastCRL> ParseCRL(const std::string& crl_proto,
299 const base::Time::Exploded& time) {
300 CrlBundle cast_crl;
301 if (!cast_crl.ParseFromString(crl_proto)) {
302 LOG(ERROR) << "CRL - Binary could not be parsed.";
303 return nullptr;
304 }
305 for (auto const& crl : cast_crl.crls()) {
306 TbsCrl tbs_crl;
307 if (!tbs_crl.ParseFromString(crl.tbs_crl())) {
308 LOG(WARNING) << "Binary TBS CRL could not be parsed.";
309 continue;
310 }
311 switch (tbs_crl.version()) {
sheretov 2016/06/24 20:24:30 A switch statement seems less readable than a simp
ryanchung 2016/06/29 22:09:48 Done.
312 case CRL_VERSION_0: {
313 base::Time overall_validity_end;
314 if (!VerifyCRL(crl, tbs_crl, time, &overall_validity_end)) {
315 LOG(ERROR) << "CRL - Verification failed.";
316 continue;
317 }
318 return base::WrapUnique(new CastCRLImpl(tbs_crl, overall_validity_end));
319 }
320 default:
321 continue;
322 }
323 }
324 LOG(ERROR) << "No supported version of revocation data.";
325 return nullptr;
326 }
327
328 bool AddCRLTrustAnchorForTest(const uint8_t* data, size_t length) {
329 scoped_refptr<net::ParsedCertificate> anchor(
330 net::ParsedCertificate::CreateFromCertificateData(
331 data, length, net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE,
332 {}));
333 if (!anchor)
334 return false;
335 CastCRLTrustStore::Get().AddTrustedCertificate(std::move(anchor));
336 return true;
337 }
338
339 void ClearCRLTrustAnchorForTest() {
340 CastCRLTrustStore::Get().Clear();
341 }
342
343 } // namespace cast_certificate
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698