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

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: ifdef out the tests on ios 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 // Parse the outer Certificate.
76 if (!ParseCertificate(cert_tlv, &out->cert))
77 return false;
78
79 // Parse the signature algorithm contained in the Certificate (there is
80 // another one in the TBSCertificate, which is checked later by
81 // VerifySignatureAlgorithmsMatch)
82 out->signature_algorithm =
83 SignatureAlgorithm::CreateFromDer(out->cert.signature_algorithm_tlv);
84 if (!out->signature_algorithm)
85 return false;
86
87 // Parse the TBSCertificate.
88 if (!ParseTbsCertificate(out->cert.tbs_certificate_tlv, &out->tbs))
89 return false;
90
91 // Reset state relating to extensions (which may not get overwritten). This is
92 // just a precaution, since in practice |out| will already be default
93 // initialize.
94 out->has_basic_constraints = false;
95 out->has_key_usage = false;
96 out->unconsumed_extensions.clear();
97
98 // Parse the standard X.509 extensions and remove them from
99 // |unconsumed_extensions|.
100 if (out->tbs.has_extensions) {
101 // ParseExtensions() ensures there are no duplicates, and maps the (unique)
102 // OID to the extension value.
103 if (!ParseExtensions(out->tbs.extensions_tlv, &out->unconsumed_extensions))
104 return false;
105
106 ParsedExtension extension;
107
108 // Basic constraints.
109 if (ConsumeExtension(BasicConstraintsOid(), &out->unconsumed_extensions,
110 &extension)) {
111 out->has_basic_constraints = true;
112 if (!ParseBasicConstraints(extension.value, &out->basic_constraints))
113 return false;
114 }
115
116 // KeyUsage.
117 if (ConsumeExtension(KeyUsageOid(), &out->unconsumed_extensions,
118 &extension)) {
119 out->has_key_usage = true;
120 if (!ParseKeyUsage(extension.value, &out->key_usage))
121 return false;
122 }
123 }
124
125 return true;
126 }
127
128 // Returns true if |name1| matches |name2|.
129 WARN_UNUSED_RESULT bool NameMatches(const der::Input& name1,
130 const der::Input& name2) {
131 // TODO(eroman): Should account for normalization (i.e. call
132 // VerifyNameMatches() instead).
133 return name1.Equals(name2);
134 }
135
136 // Returns true if |cert| was self-issued. The definition of self-issuance
137 // comes from RFC 5280 section 6.1:
138 //
139 // A certificate is self-issued if the same DN appears in the subject
140 // and issuer fields (the two DNs are the same if they match according
141 // to the rules specified in Section 7.1). In general, the issuer and
142 // subject of the certificates that make up a path are different for
143 // each certificate. However, a CA may issue a certificate to itself to
144 // support key rollover or changes in certificate policies. These
145 // self-issued certificates are not counted when evaluating path length
146 // or name constraints.
147 WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) {
148 return NameMatches(cert.tbs.subject_tlv, cert.tbs.issuer_tlv);
149 }
150
151 // Finds a trust anchor that matches |name| in |trust_store| or returns
152 // nullptr. The returned pointer references data in |trust_store|.
153 //
154 // TODO(eroman): This implementation is linear in the size of the trust store,
155 // and also presumes that all names are unique. In practice it is possible to
156 // have multiple SPKIs with the same name. Also this mechanism of
157 // searching is fairly primitive, and does not take advantage of other
158 // properties like the authority key id.
159 WARN_UNUSED_RESULT const TrustAnchor* FindTrustAnchorByName(
160 const TrustStore& trust_store,
161 const der::Input& name) {
162 for (const auto& anchor : trust_store.anchors) {
163 if (NameMatches(name, InputFromString(&anchor.name)))
164 return &anchor;
165 }
166 return nullptr;
167 }
168
169 // Returns true if |cert| is valid at time |time|.
170 //
171 // The certificate's validity requirements are described by RFC 5280 section
172 // 4.1.2.5:
173 //
174 // The validity period for a certificate is the period of time from
175 // notBefore through notAfter, inclusive.
176 WARN_UNUSED_RESULT bool VerifyTimeValidity(const FullyParsedCert& cert,
177 const der::GeneralizedTime time) {
178 return !(time < cert.tbs.validity_not_before) &&
179 !(cert.tbs.validity_not_after < time);
180 }
181
182 // Returns true if |signature_algorithm_tlv| is a valid algorithm encoding for
183 // RSA with SHA1.
184 WARN_UNUSED_RESULT bool IsRsaWithSha1SignatureAlgorithm(
185 const der::Input& signature_algorithm_tlv) {
186 scoped_ptr<SignatureAlgorithm> algorithm =
187 SignatureAlgorithm::CreateFromDer(signature_algorithm_tlv);
188
189 return algorithm &&
190 algorithm->algorithm() == SignatureAlgorithmId::RsaPkcs1 &&
191 algorithm->digest() == DigestAlgorithm::Sha1;
192 }
193
194 // Returns true if |cert| has internally consistent signature algorithms.
195 //
196 // X.509 certificates contain two different signature algorithms:
197 // (1) The signatureAlgorithm field of Certificate
198 // (2) The signature field of TBSCertificate
199 //
200 // According to RFC 5280 section 4.1.1.2 and 4.1.2.3 these two fields must be
201 // equal:
202 //
203 // This field MUST contain the same algorithm identifier as the
204 // signature field in the sequence tbsCertificate (Section 4.1.2.3).
205 //
206 // The spec is not explicit about what "the same algorithm identifier" means.
207 // Our interpretation is that the two DER-encoded fields must be byte-for-byte
208 // identical.
209 //
210 // In practice however there are certificates which use different encodings for
211 // specifying RSA with SHA1 (different OIDs). This is special-cased for
212 // compatibility sake.
213 WARN_UNUSED_RESULT bool VerifySignatureAlgorithmsMatch(
214 const FullyParsedCert& cert) {
215 const der::Input& alg1_tlv = cert.cert.signature_algorithm_tlv;
216 const der::Input& alg2_tlv = cert.tbs.signature_algorithm_tlv;
217
218 // Ensure that the two DER-encoded signature algorithms are byte-for-byte
219 // equal, but make a compatibility concession for RSA with SHA1.
220 return alg1_tlv.Equals(alg2_tlv) ||
221 (IsRsaWithSha1SignatureAlgorithm(alg1_tlv) &&
222 IsRsaWithSha1SignatureAlgorithm(alg2_tlv));
223 }
224
225 // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
226 // Processing" procedure.
227 WARN_UNUSED_RESULT bool BasicCertificateProcessing(
228 const FullyParsedCert& cert,
229 const SignaturePolicy* signature_policy,
230 const der::GeneralizedTime& time,
231 const der::Input& working_spki,
232 const der::Input& working_issuer_name) {
233 // Check that the signature algorithms in Certificate vs TBSCertificate
234 // match. This isn't part of RFC 5280 section 6.1.3, but is mandated by
235 // sections 4.1.1.2 and 4.1.2.3.
236 if (!VerifySignatureAlgorithmsMatch(cert))
237 return false;
238
239 // Verify the digital signature using the previous certificate's (or trust
240 // anchor's) key (RFC 5280 section 6.1.3 step a.1).
241 if (!VerifySignedData(
242 *cert.signature_algorithm, cert.cert.tbs_certificate_tlv,
243 cert.cert.signature_value, working_spki, signature_policy)) {
244 return false;
245 }
246
247 // Check the time range for the certificate's validity, ensuring it is valid
248 // at |time|.
249 // (RFC 5280 section 6.1.3 step a.2)
250 if (!VerifyTimeValidity(cert, time))
251 return false;
252
253 // TODO(eroman): Check revocation (RFC 5280 section 6.1.3 step a.3)
254
255 // Verify the certificate's issuer name matches the issuing certificate's (or
256 // trust anchor's) subject name. (RFC 5280 section 6.1.3 step a.4)
257 if (!NameMatches(cert.tbs.issuer_tlv, working_issuer_name))
258 return false;
259
260 // TODO(eroman): Steps b-f are omitted, as policy/name constraints are not yet
261 // implemented.
262
263 return true;
264 }
265
266 // This function corresponds to RFC 5280 section 6.1.4's "Preparation for
267 // Certificate i+1" procedure. |cert| is expected to be an intermediary.
268 WARN_UNUSED_RESULT bool PrepareForNextCertificate(
269 const FullyParsedCert& cert,
270 size_t* max_path_length_ptr,
271 der::Input* working_spki,
272 der::Input* working_issuer_name) {
273 // TODO(eroman): Steps a-b are omitted, as policy/name constraints are not yet
274 // implemented.
275
276 // From RFC 5280 section 6.1.4 step c:
277 //
278 // Assign the certificate subject name to working_issuer_name.
279 *working_issuer_name = cert.tbs.subject_tlv;
280
281 // From RFC 5280 section 6.1.4 step d:
282 //
283 // Assign the certificate subjectPublicKey to working_public_key.
284 *working_spki = cert.tbs.spki_tlv;
285
286 // Note that steps e and f are omitted as they are handled by
287 // the assignment to |working_spki| above. See the definition
288 // of |working_spki|.
289
290 // TODO(eroman): Steps g-j are omitted as policy/name constraints are not yet
291 // implemented.
292
293 // From RFC 5280 section 6.1.4 step k:
294 //
295 // If certificate i is a version 3 certificate, verify that the
296 // basicConstraints extension is present and that cA is set to
297 // TRUE. (If certificate i is a version 1 or version 2
298 // certificate, then the application MUST either verify that
299 // certificate i is a CA certificate through out-of-band means
300 // or reject the certificate. Conforming implementations may
301 // choose to reject all version 1 and version 2 intermediate
302 // certificates.)
303 //
304 // This code implicitly rejects non version 3 intermediaries, since they
305 // can't contain a BasicConstraints extension.
306 if (!cert.has_basic_constraints || !cert.basic_constraints.is_ca)
307 return false;
308
309 // From RFC 5280 section 6.1.4 step l:
310 //
311 // If the certificate was not self-issued, verify that
312 // max_path_length is greater than zero and decrement
313 // max_path_length by 1.
314 if (!IsSelfIssued(cert)) {
315 if (*max_path_length_ptr == 0)
316 return false;
317 --(*max_path_length_ptr);
318 }
319
320 // From RFC 5280 section 6.1.4 step m:
321 //
322 // If pathLenConstraint is present in the certificate and is
323 // less than max_path_length, set max_path_length to the value
324 // of pathLenConstraint.
325 if (cert.basic_constraints.has_path_len &&
326 cert.basic_constraints.path_len < *max_path_length_ptr) {
327 *max_path_length_ptr = cert.basic_constraints.path_len;
328 }
329
330 // From RFC 5280 section 6.1.4 step n:
331 //
332 // If a key usage extension is present, verify that the
333 // keyCertSign bit is set.
334 if (cert.has_key_usage &&
335 !cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)) {
336 return false;
337 }
338
339 // From RFC 5280 section 6.1.4 step o:
340 //
341 // Recognize and process any other critical extension present in
342 // the certificate. Process any other recognized non-critical
343 // extension present in the certificate that is relevant to path
344 // processing.
345 if (!VerifyNoUnconsumedCriticalExtensions(cert))
346 return false;
347
348 return true;
349 }
350
351 // Checks that if the target certificate has properties that only a CA should
352 // have (keyCertSign, CA=true, pathLenConstraint), then its other properties
353 // are consistent with being a CA.
354 //
355 // This follows from some requirements in RFC 5280 section 4.2.1.9. In
356 // particular:
357 //
358 // CAs MUST NOT include the pathLenConstraint field unless the cA
359 // boolean is asserted and the key usage extension asserts the
360 // keyCertSign bit.
361 //
362 // And:
363 //
364 // If the cA boolean is not asserted, then the keyCertSign bit in the key
365 // usage extension MUST NOT be asserted.
366 //
367 // TODO(eroman): Strictly speaking the first requirement is on CAs and not the
368 // certificate client, so could be skipped.
369 //
370 // TODO(eroman): I don't believe Firefox enforces the keyCertSign restriction
371 // for compatibility reasons. Investigate if we need to similarly relax this
372 // constraint.
373 WARN_UNUSED_RESULT bool VerifyTargetCertHasConsistentCaBits(
374 const FullyParsedCert& cert) {
375 // Check if the certificate contains any property specific to CAs.
376 bool has_ca_property =
377 (cert.has_basic_constraints &&
378 (cert.basic_constraints.is_ca || cert.basic_constraints.has_path_len)) ||
379 (cert.has_key_usage &&
380 cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
381
382 // If it "looks" like a CA because it has a CA-only property, then check that
383 // it sets ALL the properties expected of a CA.
384 if (has_ca_property) {
385 return cert.has_basic_constraints && cert.basic_constraints.is_ca &&
386 (!cert.has_key_usage ||
387 cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
388 }
389
390 return true;
391 }
392
393 // This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up Procedure".
394 // It does processing for the final certificate (the target cert).
395 WARN_UNUSED_RESULT bool WrapUp(const FullyParsedCert& cert) {
396 // TODO(eroman): Steps a-c are omitted as policy/name constraints are not yet
397 // implemented.
398
399 // Note step c-e are omitted the verification function does
400 // not output the working public key.
401
402 // From RFC 5280 section 6.1.5 step f:
403 //
404 // Recognize and process any other critical extension present in
405 // the certificate n. Process any other recognized non-critical
406 // extension present in certificate n that is relevant to path
407 // processing.
408 //
409 // Note that this is duplicated by PrepareForNextCertificate() so as to
410 // directly match the procedures in RFC 5280's section 6.1.
411 if (!VerifyNoUnconsumedCriticalExtensions(cert))
412 return false;
413
414 // TODO(eroman): Step g is omitted, as policy constraints are not yet
415 // implemented.
416
417 // The following check is NOT part of RFC 5280 6.1.5's "Wrap-Up Procedure",
418 // however is implied by RFC 5280 section 4.2.1.9.
419 if (!VerifyTargetCertHasConsistentCaBits(cert))
420 return false;
421
422 return true;
423 }
424
425 } // namespace
426
427 TrustAnchor::~TrustAnchor() {}
428
429 TrustStore::TrustStore() {}
430 TrustStore::~TrustStore() {}
431
432 // This implementation is structured to mimic the description of certificate
433 // path verification given by RFC 5280 section 6.1.
434 bool VerifyCertificateChain(const std::vector<der::Input>& certs_der,
435 const TrustStore& trust_store,
436 const SignaturePolicy* signature_policy,
437 const der::GeneralizedTime& time) {
438 // An empty chain is necessarily invalid.
439 if (certs_der.empty())
440 return false;
441
442 // |working_spki| is an amalgamation of 3 separate variables from RFC 5280:
443 // * working_public_key
444 // * working_public_key_algorithm
445 // * working_public_key_parameters
446 //
447 // They are combined for simplicity since the signature verification takes an
448 // SPKI, and the parameter inheritence is not applicable for the supported
449 // key types.
450 //
451 // An approximate explanation of |working_spki| is this description from RFC
452 // 5280 section 6.1.2:
453 //
454 // working_public_key: the public key used to verify the
455 // signature of a certificate. The working_public_key is
456 // initialized from the trusted public key provided in the trust
457 // anchor information.
458 der::Input working_spki;
459
460 // |working_issuer_name| corresponds with the same named variable in RFC 5280
461 // section 6.1.2:
462 //
463 // working_issuer_name: the issuer distinguished name expected
464 // in the next certificate in the chain. The
465 // working_issuer_name is initialized to the trusted issuer name
466 // provided in the trust anchor information.
467 der::Input working_issuer_name;
468
469 // |max_path_length| corresponds with the same named variable in RFC 5280
470 // section 6.1.2:
471 //
472 // max_path_length: this integer is initialized to n, is
473 // decremented for each non-self-issued certificate in the path,
474 // and may be reduced to the value in the path length constraint
475 // field within the basic constraints extension of a CA
476 // certificate.
477 size_t max_path_length = certs_der.size();
478
479 // Iterate over all the certificates in the reverse direction: starting from
480 // the trust anchor and progressing towards the target certificate.
481 //
482 // Note that |i| uses 0-based indexing whereas in RFC 5280 it is 1-based.
483 //
484 // * i=0 : Certificate signed by a trust anchor.
485 // * i=N-1 : Target certificate.
486 for (size_t i = 0; i < certs_der.size(); ++i) {
487 const size_t index_into_certs_der = certs_der.size() - i - 1;
488 const bool is_target_cert = index_into_certs_der == 0;
489
490 // Parse the current certificate into |cert|.
491 FullyParsedCert cert;
492 const der::Input& cert_der = certs_der[index_into_certs_der];
493 if (!FullyParseCertificate(cert_der, &cert))
494 return false;
495
496 // When processing the first certificate, initialize |working_spki|
497 // and |working_issuer_name| to the trust anchor per RFC 5280 section 6.1.2.
498 // This is done inside the loop in order to have access to the parsed
499 // certificate.
500 if (i == 0) {
501 const TrustAnchor* trust_anchor =
502 FindTrustAnchorByName(trust_store, cert.tbs.issuer_tlv);
503 if (!trust_anchor)
504 return false;
505 working_spki = InputFromString(&trust_anchor->spki);
506 working_issuer_name = InputFromString(&trust_anchor->name);
507 }
508
509 // Per RFC 5280 section 6.1:
510 // * Do basic processing for each certificate
511 // * If it is the last certificate in the path (target certificate)
512 // - Then run "Wrap up"
513 // - Otherwise run "Prepare for Next cert"
514 if (!BasicCertificateProcessing(cert, signature_policy, time, working_spki,
515 working_issuer_name)) {
516 return false;
517 }
518 if (!is_target_cert) {
519 if (!PrepareForNextCertificate(cert, &max_path_length, &working_spki,
520 &working_issuer_name)) {
521 return false;
522 }
523 } else {
524 if (!WrapUp(cert))
525 return false;
526 }
527 }
528
529 // TODO(eroman): RFC 5280 forbids duplicate certificates per section 6.1:
530 //
531 // A certificate MUST NOT appear more than once in a prospective
532 // certification path.
533
534 return true;
535 }
536
537 } // 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