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

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

Powered by Google App Engine
This is Rietveld 408576698