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

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 (ParseExtensions() changed, leading to different design here) Created 5 years, 1 month 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 // Describes all parsed properties of a certificate.
24 struct FullyParsedCert {
25 CertificateVersion version;
26 scoped_ptr<SignatureAlgorithm> signature_algorithm;
27 scoped_ptr<SignatureAlgorithm> tbs_signature_algorithm;
28 der::BitString signature_value;
29 der::Input tbs_tlv;
30
31 // TODO(eroman): Everywhere this is consumed should also consider
32 // issuerAltName.
mattm 2015/11/11 23:55:44 Did you mean "consider issuerAltName" like "should
eroman 2015/11/12 01:58:43 Thanks, Done. The TODO was basically to figure ou
33 der::Input issuer_tlv;
34 der::GeneralizedTime validity_not_before;
35 der::GeneralizedTime validity_not_after;
36
37 // TODO(eroman): Everywhere this is consumed should also consider
38 // subjectAltName.
39 der::Input subject_tlv;
40 der::Input spki_tlv;
41
42 // Extensions
43 bool has_basic_constraints = false;
44 ParsedBasicConstraints basic_constraints;
45 bool has_key_usage = false;
46 der::BitString key_usage;
47 };
48
49 // Removes the extension with OID |oid| from |extensions| and fills |extension|
50 // with the matching extension value. If there was no extension matching |oid|
51 // then returns |false|.
52 WARN_UNUSED_RESULT bool ConsumeExtension(
53 const der::Input& oid,
54 std::map<der::Input, ParsedExtension>* extensions,
55 ParsedExtension* extension) {
56 auto it = extensions->find(oid);
57 if (it == extensions->end())
58 return false;
59
60 *extension = it->second;
61
62 // TODO(eroman): Could be faster to just reset the entry instead of
63 // deleting it, although a bit less clear.
64 extensions->erase(it);
65 return true;
66 }
67
68 // Parses a Certificate and saves all properties to |out|.
69 WARN_UNUSED_RESULT bool FullyParseCertificate(const der::Input& cert_tlv,
70 FullyParsedCert* out) {
71 // Parse the Certificate.
72 ParsedCertificate cert;
73 if (!ParseCertificate(cert_tlv, &cert))
74 return false;
75
76 // Extract values of interested from the parsed Certificate.
mattm 2015/11/11 23:55:44 interest
eroman 2015/11/12 01:58:43 Done.
77 out->tbs_tlv = cert.tbs_certificate_tlv;
78 out->signature_value = cert.signature_value;
79
80 // Parse the signature algorithm in the Certificate.
81 out->signature_algorithm =
82 SignatureAlgorithm::CreateFromDer(cert.signature_algorithm_tlv);
83 if (!out->signature_algorithm)
84 return false;
85
86 // Parse the TBSCertificate.
87 ParsedTbsCertificate tbs;
88 if (!ParseTbsCertificate(cert.tbs_certificate_tlv, &tbs))
89 return false;
90
91 // Parse the signature algorithm in the TBSCertificate.
92 out->tbs_signature_algorithm =
93 SignatureAlgorithm::CreateFromDer(tbs.signature_algorithm_tlv);
94 if (!out->tbs_signature_algorithm)
95 return false;
96
97 // Copy fields of interest from the TBSCertificate (just copying pointers to
98 // the data, not the actual DER).
99 out->issuer_tlv = tbs.issuer_tlv;
100 out->version = tbs.version;
101 out->spki_tlv = tbs.spki_tlv;
102 out->subject_tlv = tbs.subject_tlv;
103 out->validity_not_after = tbs.validity_not_after;
104 out->validity_not_before = tbs.validity_not_before;
105
106 // Parse the X.509 extensions.
107 out->has_basic_constraints = false;
108 out->has_key_usage = false;
109
110 if (tbs.has_extensions) {
111 // ParseExtensions() ensures there are no duplicates, and maps the (unique)
112 // OID to the extension value. The verification code must ensure that every
113 // critical extension is understood.
114 std::map<der::Input, ParsedExtension> extensions;
115 if (!ParseExtensions(tbs.extensions_tlv, &extensions))
116 return false;
117
118 ParsedExtension extension;
119
120 // Process each of the recognized extensions. In doing so, the processed
121 // extension is cleared from the |extensions| map.
122 if (ConsumeExtension(BasicConstraintsOid(), &extensions, &extension)) {
123 out->has_basic_constraints = true;
124 if (!ParseBasicConstraints(extension.value, &out->basic_constraints))
125 return false;
126 }
127
128 if (ConsumeExtension(KeyUsageOid(), &extensions, &extension)) {
129 out->has_key_usage = true;
130 if (!ParseKeyUsage(extension.value, &out->key_usage))
131 return false;
132 }
133
134 // Check that there aren't any unconsumed (unprocessed) critical
135 // extensions in |extensions|. It is OK however for there to be
136 // unconsumed non-critical extensions.
137 for (const auto& entry : extensions) {
138 if (entry.second.critical)
139 return false;
140 }
141 }
142
143 return true;
144 }
145
146 // Returns true if |name1| matches |name2|.
147 WARN_UNUSED_RESULT bool NameMatches(const der::Input& name1,
148 const der::Input& name2) {
149 // TODO(eroman): Should account for normalization (taht work is part of a
150 // different change).
151 return name1.Equals(name2);
152 }
153
154 // Returns true if |cert| was self-issued. Note that self-issued is not the
155 // same thing as self-signed, see RFC 5280 for the explanation.
156 WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) {
157 return NameMatches(cert.subject_tlv, cert.issuer_tlv);
158 }
159
160 // Finds a mapping in the trust store that matches |name|, or returns nullptr.
161 //
162 // TODO(eroman): This implementation is linear in the size of the trust store,
163 // and also presumes that all names are unique. In practice it is possible to
164 // have have multiple SPKIs with the same name. Also this mechanism of
165 // searching is fairly primitive, and does not take advantage of other
166 // properties like the authority key id.
167 WARN_UNUSED_RESULT const TrustedRoot* FindTrustedRootByName(
168 const TrustStore& trust_store,
169 const der::Input& name) {
170 for (const auto& root : trust_store.roots) {
171 if (NameMatches(name, InputFromString(&root.name)))
172 return &root;
173 }
174 return nullptr;
175 }
176
177 // Returns true if |cert| is valid at time |time|.
178 //
179 // The certificate's validity requirements are described by RFC 5280 section
180 // 4.1.2.5:
181 //
182 // The validity period for a certificate is the period of time from
183 // notBefore through notAfter, inclusive.
184 WARN_UNUSED_RESULT bool VerifyValidity(const FullyParsedCert& cert,
185 const der::GeneralizedTime time) {
186 return (!(time < cert.validity_not_before) &&
187 !(cert.validity_not_after < time));
188 }
189
190 // Returns true if |cert| has internally consistent signature algorithms.
191 //
192 // X.509 certificates contain two signature algorithms:
193 // (1) The signatureAlgorithm field of Certificate
194 // (2) The signature of TBSCertificate
195 //
196 // According to RFC 5280 section 4.1.1.2 these two fields must be in agreement:
197 //
198 // This field MUST contain the same algorithm identifier as the
199 // signature field in the sequence tbsCertificate (Section 4.1.2.3).
200 //
201 // The mechanism through which equality is determined is unspecified.
202 // The interpretation taken here is that they identify the same algorithm,
203 // but the DER-encoded AlgorithmIdentifier needn't be byte-for-byte equal.
204 // There are a small number of certificates that require this (having for
205 // instance specified a different OID for RSA with SHA-1).
206 WARN_UNUSED_RESULT bool VerifySignatureAlgorithsMatch(
207 const FullyParsedCert& cert) {
208 return cert.signature_algorithm->Equals(*cert.tbs_signature_algorithm);
209 }
210
211 // Returns true if |cert| has a correct key usage for the issuance of other
212 // certificates.
213 WARN_UNUSED_RESULT bool VerifyKeyUsageForIssuer(const FullyParsedCert& cert) {
214 // If the Key Usage extension is not present, then the key can be used for
215 // any operation.
216 if (!cert.has_key_usage)
217 return true;
218
219 // RFC 5280 section 4.2.1.9:
220 //
221 // If the keyUsage extension is present, then the subject public key
222 // MUST NOT be used to verify signatures on certificates or CRLs unless
223 // the corresponding keyCertSign or cRLSign bit is set.
224 if (!KeyUsageAssertsBit(cert.key_usage, KeyUsageBit::KEY_CERT_SIGN))
225 return false;
226
227 // RFC 5280 section 4.2.1.9:
228 //
229 // If the keyCertSign bit is asserted, then the cA bit in the basic
230 // constraints extension (Section 4.2.1.9) MUST also be asserted.
231 //
232 // NOTE: this normative requirement is not enforced by this function, but
233 // rather by VerifyBasicConstraintsForIssuer().
234 return true;
235 }
236
237 // Returns true if |cert| has a correct BasicConstraints extension for the
238 // issuance of other certificates.
239 WARN_UNUSED_RESULT bool VerifyBasicConstraintsForIssuer(
240 const FullyParsedCert& cert,
241 size_t current_cert_index,
242 size_t num_prev_self_issued_certs) {
243 DCHECK_GT(current_cert_index, 0u);
244
245 // Only V3 certificates have the concept of extensions.
246 if (cert.version == CertificateVersion::V1 ||
247 cert.version == CertificateVersion::V2) {
248 // RFC 5280:
249 //
250 // (If certificate i is a version 1 or version 2 certificate, then the
251 // application MUST either verify that certificate i is a CA
252 // certificate through out-of-band means or reject the certificate.
253 // Conforming implementations may choose to reject all version 1 and
254 // version 2 intermediate certificates.)
255 return false;
256 }
257
258 // RFC 5280 section 4.2.1.9:
259 //
260 // If the basic constraints extension is not present in a version 3
261 // certificate, or the extension is present but the cA boolean
262 // is not asserted, then the certified public key MUST NOT be used to
263 // verify certificate signatures.
264 if (!cert.has_basic_constraints || !cert.basic_constraints.is_ca)
265 return false;
266
267 // RFC 5280 section 4.2.1.9:
268 //
269 // Where pathLenConstraint does not appear, no limit is imposed.
270 if (cert.basic_constraints.has_path_len) {
271 // RFC 5280 section 4.2.1.9:
272 //
273 // ... In this case, it gives the maximum number of non-self-issued
274 // intermediate certificates that may follow this certificate in a valid
275 // certification path. (Note: The last certificate in the certification
276 // path is not an intermediate certificate, and is not included in this
277 // limit. Usually, the last certificate is an end entity certificate,
278 // but it can be a CA certificate.)
279 size_t current_path_len =
280 current_cert_index - 1 - num_prev_self_issued_certs;
281 if (current_path_len > cert.basic_constraints.path_len)
282 return false;
283 }
284
285 return true;
286 }
287
288 // Returns true if the subject of |issuing_cert| matches the issuer of
289 // |subordinate_cert|.
290 WARN_UNUSED_RESULT bool VerifyIssuerMatchesSubject(
291 const FullyParsedCert& issuing_cert,
292 const FullyParsedCert& subordinate_cert) {
293 // TODO(eroman): subjectAltName and issuerAltName ?
294 return NameMatches(issuing_cert.subject_tlv, subordinate_cert.issuer_tlv);
295 }
296
297 // Returns true if |cert| was signed by a trusted root in |trust_store|.
298 WARN_UNUSED_RESULT bool IsSignedByTrustAnchor(
299 const FullyParsedCert& cert,
300 const TrustStore& trust_store,
301 const SignaturePolicy* signature_policy) {
302 const TrustedRoot* trusted_root =
303 FindTrustedRootByName(trust_store, cert.issuer_tlv);
304
305 if (!trusted_root)
306 return false;
307
308 if (!VerifySignedData(
309 *cert.signature_algorithm, cert.tbs_tlv, cert.signature_value,
310 InputFromString(&trusted_root->spki), signature_policy)) {
311 return false;
312 }
313
314 return true;
315 }
316
317 // Returns true if |cert| has BasicConstraints and KeyUsage consistent with
318 // being an end-entity certificate.
319 WARN_UNUSED_RESULT bool VerifyTargetCertificateIsEndEntity(
320 const FullyParsedCert& cert) {
321 if (cert.has_basic_constraints) {
322 if (cert.basic_constraints.is_ca)
323 return false; // Not an end-entity certificate.
324
325 // RFC 5280 section 4.2.1.9:
326 //
327 // CAs MUST NOT include the pathLenConstraint field unless the cA
328 // boolean is asserted and the key usage extension asserts the
329 // keyCertSign bit.
330 if (cert.basic_constraints.has_path_len)
331 return false;
332 }
333
334 // RFC 5280 section 4.2.1.9:
335 //
336 // If the cA boolean is not asserted, then the keyCertSign bit in the key
337 // usage extension MUST NOT be asserted.
338 //
339 // TODO(eroman): Should "asserted" in the above apply only when the basic
340 // constraints extension is actually present? (In other words, if Basic
341 // Constraints was omitted, should keyCertSign be allowed even though it
342 // doesn't make sense?)
343 if (cert.has_key_usage &&
344 KeyUsageAssertsBit(cert.key_usage, KeyUsageBit::KEY_CERT_SIGN)) {
345 return false;
346 }
347
348 return true;
349 }
350
351 } // namespace
352
353 TrustedRoot::~TrustedRoot() {}
354
355 TrustStore::TrustStore() {}
356 TrustStore::~TrustStore() {}
357
358 bool VerifyCertificateChain(const std::vector<der::Input>& certs_der,
359 const TrustStore& trust_store,
360 const SignaturePolicy* signature_policy,
361 const der::GeneralizedTime& time) {
362 // An empty chain is invalid. Fail early since the rest of the code
363 // assumes a non-empty chain.
364 if (certs_der.empty())
365 return false;
366
367 // Fully parse all of the certificates. This is done up-front to simply
368 // access to properties.
369 std::vector<FullyParsedCert> certs(certs_der.size());
370 for (size_t i = 0; i < certs_der.size(); ++i) {
371 if (!FullyParseCertificate(certs_der[i], &certs[i]))
372 return false;
373 }
374
375 // TODO(eroman): Relax this and allow the caller to decide.
376 if (!VerifyTargetCertificateIsEndEntity(certs.front()))
377 return false;
378
379 // The last intermediary must be issued by a trusted root.
380 if (!IsSignedByTrustAnchor(certs.back(), trust_store, signature_policy))
381 return false;
382
383 // Walk the chain in the forward direction (from end entity towards trust
384 // anchor) and check all properties of the certificate, including issuance.
385 size_t num_prev_self_issued_certs = 0;
386 for (size_t i = 0; i < certs_der.size(); ++i) {
387 // |cert| is the current certificate -- either the target or an
388 // intermediary, but never a root.
389 const auto& cert = certs[i];
390
391 if (!VerifyValidity(cert, time))
392 return false;
393
394 if (!VerifySignatureAlgorithsMatch(cert))
395 return false;
396
397 // With the exception of the target (i=0), every other certificate (i) must
398 // be a CA. This section verifies that it is in fact a CA, and that it
399 // issued certificate (i-1).
400 if (i > 0) {
401 // The previous certificate in the chain, that purports to be issued by
402 // |cert|.
403 const auto& subordinate_cert = certs[i - 1];
404
405 if (!VerifyKeyUsageForIssuer(cert))
406 return false;
407
408 if (!VerifyBasicConstraintsForIssuer(cert, i, num_prev_self_issued_certs))
409 return false;
410
411 if (!VerifyIssuerMatchesSubject(cert, subordinate_cert))
412 return false;
413
414 // Verify the digital signature.
415 if (!VerifySignedData(*subordinate_cert.signature_algorithm,
416 subordinate_cert.tbs_tlv,
417 subordinate_cert.signature_value, cert.spki_tlv,
418 signature_policy)) {
419 return false;
420 }
421 }
422
423 // Keep track of how many certificates were self-issued, since some rules
424 // are different for self-issued certificates (notably pathlen for
425 // BasicConstraints).
426 if (IsSelfIssued(cert))
427 num_prev_self_issued_certs++;
428 }
429
430 // TODO(eroman):
431 // * Name constraints
432 // * Policy constraints
433 // * Extended Key Usage
434
435 return true;
436 }
437
438 } // 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