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

Side by Side Diff: net/cert/internal/verify_certificate_chain.cc

Issue 1414923007: Add initial code for verifying a certificate chain. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@test_driver
Patch Set: rebase (and fix a comment) Created 5 years 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 2015 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 "net/cert/internal/verify_certificate_chain.h"
6
7 #include "base/logging.h"
8 #include "net/cert/internal/parse_certificate.h"
9 #include "net/cert/internal/signature_algorithm.h"
10 #include "net/cert/internal/signature_policy.h"
11 #include "net/cert/internal/verify_signed_data.h"
12 #include "net/der/input.h"
13
14 namespace net {
15
16 namespace {
17
18 // TODO(eroman): Move into net/der (duplicated from test_helpers.cc).
19 static der::Input InputFromString(const std::string* s) {
20 return der::Input(reinterpret_cast<const uint8_t*>(s->data()), s->size());
21 }
22
23 // Map from OID to ParsedExtension.
24 using ExtensionsMap = std::map<der::Input, ParsedExtension>;
25
26 // Describes all parsed properties of a certificate that are relevant for
27 // certificate verification.
28 struct FullyParsedCert {
29 ParsedCertificate cert;
30 ParsedTbsCertificate tbs;
31
32 scoped_ptr<SignatureAlgorithm> signature_algorithm;
33
34 // Standard extensions that were parsed.
35 bool has_basic_constraints = false;
36 ParsedBasicConstraints basic_constraints;
37
38 bool has_key_usage = false;
39 der::BitString key_usage;
40
41 // The remaining extensions (excludes the standard ones above).
42 ExtensionsMap unconsumed_extensions;
43 };
44
45 // Removes the extension with OID |oid| from |unconsumed_extensions| and fills
46 // |extension| with the matching extension value. If there was no extension
47 // matching |oid| then returns |false|.
48 WARN_UNUSED_RESULT bool ConsumeExtension(const der::Input& oid,
49 ExtensionsMap* unconsumed_extensions,
50 ParsedExtension* extension) {
51 auto it = unconsumed_extensions->find(oid);
52 if (it == unconsumed_extensions->end())
53 return false;
54
55 *extension = it->second;
56 unconsumed_extensions->erase(it);
57 return true;
58 }
59
60 // Returns true if the certificate does not contain any unconsumed _critical_
61 // extensions.
62 WARN_UNUSED_RESULT bool VerifyNoUnconsumedCriticalExtensions(
63 const FullyParsedCert& cert) {
64 for (const auto& entry : cert.unconsumed_extensions) {
65 if (entry.second.critical)
66 return false;
67 }
68 return true;
69 }
70
71 // Parses an X.509 Certificate fully (including the TBSCertificate and
72 // standard extensions), saving all the properties to |out_|.
73 WARN_UNUSED_RESULT bool FullyParseCertificate(const der::Input& cert_tlv,
74 FullyParsedCert* out) {
75 // Reset everything.
76 *out = FullyParsedCert();
77
78 // Parse the outer Certificate.
79 if (!ParseCertificate(cert_tlv, &out->cert))
80 return false;
81
82 // Parse the signature algorithm contained in the Certificate (there is
83 // another one in the TBSCertificate, which is checked later by
84 // VerifySignatureAlgorithmsMatch)
85 out->signature_algorithm =
86 SignatureAlgorithm::CreateFromDer(out->cert.signature_algorithm_tlv);
87 if (!out->signature_algorithm)
88 return false;
89
90 // Parse the TBSCertificate.
91 if (!ParseTbsCertificate(out->cert.tbs_certificate_tlv, &out->tbs))
92 return false;
93
94 // Parse the standard X.509 extensions and remove them from
95 // |unconsumed_extensions|.
96 if (out->tbs.has_extensions) {
97 // ParseExtensions() ensures there are no duplicates, and maps the (unique)
98 // OID to the extension value.
99 if (!ParseExtensions(out->tbs.extensions_tlv, &out->unconsumed_extensions))
100 return false;
101
102 ParsedExtension extension;
103
104 // Basic constraints.
105 if (ConsumeExtension(BasicConstraintsOid(), &out->unconsumed_extensions,
106 &extension)) {
107 out->has_basic_constraints = true;
108 if (!ParseBasicConstraints(extension.value, &out->basic_constraints))
109 return false;
110 }
111
112 // KeyUsage.
113 if (ConsumeExtension(KeyUsageOid(), &out->unconsumed_extensions,
114 &extension)) {
115 out->has_key_usage = true;
116 if (!ParseKeyUsage(extension.value, &out->key_usage))
117 return false;
118 }
119 }
120
121 return true;
122 }
123
124 // Returns true if |name1| matches |name2|.
125 WARN_UNUSED_RESULT bool NameMatches(const der::Input& name1,
126 const der::Input& name2) {
127 // TODO(eroman): Should account for normalization (i.e. call
128 // VerifyNameMatches() instead).
129 return name1.Equals(name2);
130 }
131
132 // Returns true if |cert| was self-issued. The definition of self-issuance
133 // comes from RFC 5280 section 6.1:
134 //
135 // A certificate is self-issued if the same DN appears in the subject
136 // and issuer fields (the two DNs are the same if they match according
137 // to the rules specified in Section 7.1). In general, the issuer and
138 // subject of the certificates that make up a path are different for
139 // each certificate. However, a CA may issue a certificate to itself to
140 // support key rollover or changes in certificate policies. These
141 // self-issued certificates are not counted when evaluating path length
142 // or name constraints.
143 WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) {
144 return NameMatches(cert.tbs.subject_tlv, cert.tbs.issuer_tlv);
145 }
146
147 // Finds a trust anchor that matches |name| in |trust_store| or returns
148 // nullptr. The returned pointer references data in |trust_store|.
149 //
150 // TODO(eroman): This implementation is linear in the size of the trust store,
151 // and also presumes that all names are unique. In practice it is possible to
152 // have multiple SPKIs with the same name. Also this mechanism of
153 // searching is fairly primitive, and does not take advantage of other
154 // properties like the authority key id.
155 WARN_UNUSED_RESULT const TrustAnchor* FindTrustAnchorByName(
156 const TrustStore& trust_store,
157 const der::Input& name) {
158 for (const auto& anchor : trust_store.anchors) {
159 if (NameMatches(name, InputFromString(&anchor.name)))
160 return &anchor;
161 }
162 return nullptr;
163 }
164
165 // Returns true if |cert| is valid at time |time|.
166 //
167 // The certificate's validity requirements are described by RFC 5280 section
168 // 4.1.2.5:
169 //
170 // The validity period for a certificate is the period of time from
171 // notBefore through notAfter, inclusive.
172 WARN_UNUSED_RESULT bool VerifyTimeValidity(const FullyParsedCert& cert,
173 const der::GeneralizedTime time) {
174 return !(time < cert.tbs.validity_not_before) &&
175 !(cert.tbs.validity_not_after < time);
176 }
177
178 // Returns true if |signature_algorithm_tlv| is a valid algorithm encoding for
179 // RSA with SHA1.
180 WARN_UNUSED_RESULT bool IsRsaWithSha1SignatureAlgorithm(
181 const der::Input& signature_algorithm_tlv) {
182 scoped_ptr<SignatureAlgorithm> algorithm =
183 SignatureAlgorithm::CreateFromDer(signature_algorithm_tlv);
184
185 return algorithm &&
186 algorithm->algorithm() == SignatureAlgorithmId::RsaPkcs1 &&
187 algorithm->digest() == DigestAlgorithm::Sha1;
188 }
189
190 // Returns true if |cert| has internally consistent signature algorithms.
191 //
192 // X.509 certificates contain two different signature algorithms:
193 // (1) The signatureAlgorithm field of Certificate
194 // (2) The signature field of TBSCertificate
195 //
196 // According to RFC 5280 section 4.1.1.2 and 4.1.2.3 these two fields must be
197 // equal:
198 //
199 // This field MUST contain the same algorithm identifier as the
200 // signature field in the sequence tbsCertificate (Section 4.1.2.3).
201 //
202 // The spec is not explicit about what "the same algorithm identifier" means.
203 // Our interpretation is that the two DER-encoded fields must be byte-for-byte
204 // identical.
205 //
206 // In practice however there are certificates which use different encodings for
207 // specifying RSA with SHA1 (different OIDs). This is special-cased for
208 // compatibility sake.
davidben 2015/12/10 23:23:35 [Incidentally, I have a guess as to what happened.
eroman 2015/12/17 19:43:52 Good to know, thanks
209 WARN_UNUSED_RESULT bool VerifySignatureAlgorithmsMatch(
210 const FullyParsedCert& cert) {
211 const der::Input& alg1_tlv = cert.cert.signature_algorithm_tlv;
212 const der::Input& alg2_tlv = cert.tbs.signature_algorithm_tlv;
213
214 // Ensure that the two DER-encoded signature algorithms are byte-for-byte
215 // equal, but make a compatibility concession for RSA with SHA1.
216 return alg1_tlv.Equals(alg2_tlv) ||
217 (IsRsaWithSha1SignatureAlgorithm(alg1_tlv) &&
218 IsRsaWithSha1SignatureAlgorithm(alg2_tlv));
219 }
220
221 // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
222 // Processing" procedure.
223 WARN_UNUSED_RESULT bool BasicCertificateProcessing(
224 const FullyParsedCert& cert,
225 const SignaturePolicy* signature_policy,
226 const der::GeneralizedTime& time,
227 const der::Input& working_public_key,
228 const der::Input& working_issuer_name) {
229 // Check that the signature algorithms in Certificate vs TBSCertificate
230 // match. This isn't part of RFC section 6.1.3, but is mandated by sections
davidben 2015/12/10 23:23:35 Nit: RFC section -> section or RFC 5280 section?
eroman 2015/12/17 19:43:53 Done.
231 // 4.1.1.2 and 4.1.2.3.
232 if (!VerifySignatureAlgorithmsMatch(cert))
233 return false;
234
235 // Verify the digital signature using the previous certificate's (or trust
236 // anchor's) key (RFC 5280 section 6.1.3 step a.1).
237 if (!VerifySignedData(
238 *cert.signature_algorithm, cert.cert.tbs_certificate_tlv,
239 cert.cert.signature_value, working_public_key, signature_policy)) {
240 return false;
241 }
242
243 // Check the time range for the certificate's validity, ensuring it is valid
244 // at |time|.
245 // (RFC 5280 section 6.1.3 step a.2)
246 if (!VerifyTimeValidity(cert, time))
247 return false;
248
249 // TODO(eroman): Check revocation (RFC 5280 section 6.1.3 step a.3)
250
251 // Verify the certificate's issuer name matches the issuing certificate's (or
252 // trust anchor's) subject name. (RFC 5280 section 6.1.3 step a.4)
253 if (!NameMatches(cert.tbs.issuer_tlv, working_issuer_name))
254 return false;
255
256 return true;
257 }
258
259 // This function corresponds to RFC 5280 section 6.1.4's "Preparation for
260 // Certificate i+1" procedure. |cert| is expected to be an intermediary.
261 WARN_UNUSED_RESULT bool PrepareForNextCertificate(
262 const FullyParsedCert& cert,
263 size_t* max_path_length_ptr,
264 der::Input* working_public_key,
265 der::Input* working_issuer_name) {
266 // From RFC 5280 section 6.1.4 step c:
267 //
268 // Assign the certificate subject name to working_issuer_name.
269 *working_issuer_name = cert.tbs.subject_tlv;
270
271 // From RFC 5280 section 6.1.4 step d:
davidben 2015/12/10 23:23:35 Strictly speaking, this is steps d-f combined, wit
eroman 2015/12/17 19:43:53 Done. (added comment)
272 //
273 // Assign the certificate subjectPublicKey to working_public_key.
274 *working_public_key = cert.tbs.spki_tlv;
275
276 // From RFC 5280 section 6.1.4 step k:
277 //
278 // If certificate i is a version 3 certificate, verify that the
279 // basicConstraints extension is present and that cA is set to
280 // TRUE. (If certificate i is a version 1 or version 2
281 // certificate, then the application MUST either verify that
282 // certificate i is a CA certificate through out-of-band means
283 // or reject the certificate. Conforming implementations may
284 // choose to reject all version 1 and version 2 intermediate
285 // certificates.)
286 //
287 // This code implicitly rejects non version 3 intermediaries, since they
288 // can't contain a BasicConstraints extension.
289 if (!cert.has_basic_constraints || !cert.basic_constraints.is_ca)
290 return false;
291
292 // From RFC 5280 section 6.1.4 step l:
293 //
294 // If the certificate was not self-issued, verify that
295 // max_path_length is greater than zero and decrement
296 // max_path_length by 1.
297 if (!IsSelfIssued(cert)) {
298 if (*max_path_length_ptr == 0)
299 return false;
300 --(*max_path_length_ptr);
301 }
302
303 // From RFC 5280 section 6.1.4 step m:
304 //
305 // If pathLenConstraint is present in the certificate and is
306 // less than max_path_length, set max_path_length to the value
307 // of pathLenConstraint.
308 if (cert.basic_constraints.has_path_len &&
309 cert.basic_constraints.path_len < *max_path_length_ptr) {
310 *max_path_length_ptr = cert.basic_constraints.path_len;
311 }
312
313 // From RFC 5280 section 6.1.4 step n:
314 //
315 // If a key usage extension is present, verify that the
316 // keyCertSign bit is set.
317 if (cert.has_key_usage &&
318 !cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)) {
319 return false;
320 }
321
322 // From RFC 5280 section 6.1.4 step o:
323 //
324 // Recognize and process any other critical extension present in
325 // the certificate. Process any other recognized non-critical
326 // extension present in the certificate that is relevant to path
327 // processing.
328 if (!VerifyNoUnconsumedCriticalExtensions(cert))
329 return false;
330
davidben 2015/12/10 23:23:35 Nit: Add TODO about policies and name constraints
mattm 2015/12/17 02:37:35 I think it would make sense. It's kind of obvious
eroman 2015/12/17 19:43:53 Done. I have added numerous comments so each step
331 return true;
332 }
333
334 // Checks that if the target certificate has properties that only a CA should
335 // have (keyCertSign, CA=true, pathLenConstraint), then its other properties
336 // are consistent with being a CA.
337 //
338 // This follows from some requirements in RFC 5280 section 4.2.1.9. In
339 // particular:
340 //
341 // CAs MUST NOT include the pathLenConstraint field unless the cA
342 // boolean is asserted and the key usage extension asserts the
343 // keyCertSign bit.
344 //
345 // And:
346 //
347 // If the cA boolean is not asserted, then the keyCertSign bit in the key
348 // usage extension MUST NOT be asserted.
349 //
350 // TODO(eroman): Strictly speaking the first requirement is on CAs and not the
351 // certificate client, so could be skipped.
352 //
353 // TODO(eroman): I don't believe Firefox enforces the keyCertSign restriction
354 // for compatibility reasons. Investigate if we need to similarly relax this
355 // constraint.
356 WARN_UNUSED_RESULT bool VerifyTargetCertHasConsistentCaBits(
357 const FullyParsedCert& cert) {
358 // Check if the certificate contains any property specific to CAs.
359 bool has_ca_property =
360 (cert.has_basic_constraints &&
361 (cert.basic_constraints.is_ca || cert.basic_constraints.has_path_len)) ||
362 (cert.has_key_usage &&
363 cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
364
365 // If it "looks" like a CA because it has a CA-only property, then check that
366 // it sets ALL the properties expected of a CA.
367 if (has_ca_property) {
368 return cert.has_basic_constraints && cert.basic_constraints.is_ca &&
369 (!cert.has_key_usage ||
370 cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
371 }
372
373 return true;
374 }
375
376 // This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up Procedure".
377 // It does processing for the final certificate (the target cert).
378 WARN_UNUSED_RESULT bool WrapUp(const FullyParsedCert& cert) {
379 // From RFC 5280 section 6.1.5 step f:
380 //
381 // Recognize and process any other critical extension present in
382 // the certificate n. Process any other recognized non-critical
383 // extension present in certificate n that is relevant to path
384 // processing.
385 //
386 // Note that this is duplicated with PrepareForNextCertificate() so as to
387 // direclty match the procedures in RFC 5280's section 6.1.
davidben 2015/12/10 23:23:35 directly
eroman 2015/12/17 19:43:52 Done.
388 if (!VerifyNoUnconsumedCriticalExtensions(cert))
389 return false;
390
391 // The following check is NOT part of RFC 5280 6.1.5's "Wrap-Up Procedure",
392 // however is implied by RFC 5280 section 4.2.1.9.
393 if (!VerifyTargetCertHasConsistentCaBits(cert))
394 return false;
395
396 return true;
397 }
398
399 } // namespace
400
401 TrustAnchor::~TrustAnchor() {}
402
403 TrustStore::TrustStore() {}
404 TrustStore::~TrustStore() {}
405
406 // This implementation is structured to mimic the description of certificate
407 // path verification given by RFC 5280 section 6.1.
408 bool VerifyCertificateChain(const std::vector<der::Input>& certs_der,
409 const TrustStore& trust_store,
410 const SignaturePolicy* signature_policy,
411 const der::GeneralizedTime& time) {
412 // An empty chain is necessarily invalid.
413 if (certs_der.empty())
414 return false;
415
416 // |working_public_key| corresponds with the same named variable in RFC 5280
417 // section 6.1.2:
418 //
419 // working_public_key: the public key used to verify the
420 // signature of a certificate. The working_public_key is
421 // initialized from the trusted public key provided in the trust
422 // anchor information.
423 der::Input working_public_key;
davidben 2015/12/10 23:23:35 It's actually a combination of working_public_key,
eroman 2015/12/17 19:43:53 Good idea, done (also added a comment block trying
424
425 // |working_public_key| corresponds with the same named variable in RFC 5280
davidben 2015/12/10 23:23:35 working_issuer_name
eroman 2015/12/17 19:43:53 Done.
426 // section 6.1.2:
427 //
428 // working_issuer_name: the issuer distinguished name expected
429 // in the next certificate in the chain. The
430 // working_issuer_name is initialized to the trusted issuer name
431 // provided in the trust anchor information.
432 der::Input working_issuer_name;
433
434 // |max_path_length| corresponds with the same named variable in RFC 5280
435 // section 6.1.2:
436 //
437 // max_path_length: this integer is initialized to n, is
438 // decremented for each non-self-issued certificate in the path,
439 // and may be reduced to the value in the path length constraint
440 // field within the basic constraints extension of a CA
441 // certificate.
442 size_t max_path_length = certs_der.size();
443
444 // Iterate over all the certificates in the reverse direction: starting from
445 // the trust anchor and progressing towards the target certificate.
446 for (size_t i = 1; i <= certs_der.size(); ++i) {
447 // Note that |i| is a 1-based index as per RFC 5280, where the target
448 // certificate is i=N and the certificate signed by trust anchor is i=N.
davidben 2015/12/10 23:23:35 (I probably wouldn't have bothered, but whatever.
eroman 2015/12/17 19:43:52 Changed to 0-based indexing. Agreed, perhaps this
449 const size_t index_into_certs_der = certs_der.size() - i;
450 const bool is_target_cert = index_into_certs_der == 0;
451
452 // Parse the current certificate into |cert|.
453 FullyParsedCert cert;
454 const der::Input& cert_der = certs_der[index_into_certs_der];
455 if (!FullyParseCertificate(cert_der, &cert))
456 return false;
457
458 // When processing the first certificate, initialize |working_public_key|
459 // and |working_issuer_name| to the trust anchor per RFC 5280 section 6.1.2.
460 // This is done inside the loop in order to have access to the parsed
461 // certificate.
462 if (i == 1) {
463 const TrustAnchor* trust_anchor =
464 FindTrustAnchorByName(trust_store, cert.tbs.issuer_tlv);
465 if (!trust_anchor)
466 return false;
467 working_public_key = InputFromString(&trust_anchor->spki);
468 working_issuer_name = InputFromString(&trust_anchor->name);
469 }
470
471 // Per RFC 5280 section 6.1:
472 // * Do basic processing for each certificate
473 // * If it is the last certificate in the path (target certificate)
474 // - Then run "Wrap up"
475 // - Otherwise run "Prepare for Next cert"
476 if (!BasicCertificateProcessing(cert, signature_policy, time,
477 working_public_key, working_issuer_name)) {
478 return false;
479 }
480 if (!is_target_cert) {
481 if (!PrepareForNextCertificate(cert, &max_path_length,
482 &working_public_key,
483 &working_issuer_name)) {
484 return false;
485 }
486 } else {
487 if (!WrapUp(cert))
488 return false;
489 }
490 }
491
492 // TODO(eroman): Are duplicates possible? RFC 5280 forbids them per section
493 // 6.1:
494 //
495 // A certificate MUST NOT appear more than once in a prospective
496 // certification path.
davidben 2015/12/10 23:23:35 It's certainly possible in so far as I could const
eroman 2015/12/17 19:43:52 Perhaps not worth handling this at the verificatio
davidben 2015/12/17 20:02:26 None that I can think of.
497
498 return true;
499 }
500
501 } // namespace net
OLDNEW
« no previous file with comments | « net/cert/internal/verify_certificate_chain.h ('k') | net/cert/internal/verify_certificate_chain_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698