OLD | NEW |
(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 |
OLD | NEW |