| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
| /* This code is made available to you under your choice of the following sets |
| * of licensing terms: |
| */ |
| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| */ |
| /* Copyright 2013 Mozilla Contributors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "mozpkix/pkixcheck.h" |
| |
| #include "mozpkix/pkixder.h" |
| #include "mozpkix/pkixutil.h" |
| |
| namespace mozilla { namespace pkix { |
| |
| // 4.1.1.2 signatureAlgorithm |
| // 4.1.2.3 signature |
| |
| Result |
| CheckSignatureAlgorithm(TrustDomain& trustDomain, |
| EndEntityOrCA endEntityOrCA, |
| Time notBefore, |
| const der::SignedDataWithSignature& signedData, |
| Input signatureValue) |
| { |
| // 4.1.1.2. signatureAlgorithm |
| der::PublicKeyAlgorithm publicKeyAlg; |
| DigestAlgorithm digestAlg; |
| Reader signatureAlgorithmReader(signedData.algorithm); |
| Result rv = der::SignatureAlgorithmIdentifierValue(signatureAlgorithmReader, |
| publicKeyAlg, digestAlg); |
| if (rv != Success) { |
| return rv; |
| } |
| rv = der::End(signatureAlgorithmReader); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // 4.1.2.3. Signature |
| der::PublicKeyAlgorithm signedPublicKeyAlg; |
| DigestAlgorithm signedDigestAlg; |
| Reader signedSignatureAlgorithmReader(signatureValue); |
| rv = der::SignatureAlgorithmIdentifierValue(signedSignatureAlgorithmReader, |
| signedPublicKeyAlg, |
| signedDigestAlg); |
| if (rv != Success) { |
| return rv; |
| } |
| rv = der::End(signedSignatureAlgorithmReader); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // "This field MUST contain the same algorithm identifier as the |
| // signatureAlgorithm field in the sequence Certificate." However, it may |
| // be encoded differently. In particular, one of the fields may have a NULL |
| // parameter while the other one may omit the parameter field altogether, and |
| // these are considered equivalent. Some certificates generation software |
| // actually generates certificates like that, so we compare the parsed values |
| // instead of comparing the encoded values byte-for-byte. |
| // |
| // Along the same lines, we accept two different OIDs for RSA-with-SHA1, and |
| // we consider those OIDs to be equivalent here. |
| if (publicKeyAlg != signedPublicKeyAlg || digestAlg != signedDigestAlg) { |
| return Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH; |
| } |
| |
| // During the time of the deprecation of SHA-1 and the deprecation of RSA |
| // keys of less than 2048 bits, we will encounter many certs signed using |
| // SHA-1 and/or too-small RSA keys. With this in mind, we ask the trust |
| // domain early on if it knows it will reject the signature purely based on |
| // the digest algorithm and/or the RSA key size (if an RSA signature). This |
| // is a good optimization because it completely avoids calling |
| // trustDomain.FindIssuers (which may be slow) for such rejected certs, and |
| // more generally it short-circuits any path building with them (which, of |
| // course, is even slower). |
| |
| rv = trustDomain.CheckSignatureDigestAlgorithm(digestAlg, endEntityOrCA, |
| notBefore); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| switch (publicKeyAlg) { |
| case der::PublicKeyAlgorithm::RSA_PKCS1: |
| { |
| // The RSA computation may give a result that requires fewer bytes to |
| // encode than the public key (since it is modular arithmetic). However, |
| // the last step of generating a PKCS#1.5 signature is the I2OSP |
| // procedure, which pads any such shorter result with zeros so that it |
| // is exactly the same length as the public key. |
| unsigned int signatureSizeInBits = signedData.signature.GetLength() * 8u; |
| return trustDomain.CheckRSAPublicKeyModulusSizeInBits( |
| endEntityOrCA, signatureSizeInBits); |
| } |
| |
| case der::PublicKeyAlgorithm::ECDSA: |
| // In theory, we could implement a similar early-pruning optimization for |
| // ECDSA curves. However, since there has been no similar deprecation for |
| // for any curve that we support, the chances of us encountering a curve |
| // during path building is too low to be worth bothering with. |
| break; |
| case der::PublicKeyAlgorithm::Uninitialized: |
| { |
| assert(false); |
| return Result::FATAL_ERROR_LIBRARY_FAILURE; |
| } |
| MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM |
| } |
| |
| return Success; |
| } |
| |
| // 4.1.2.4 Issuer |
| |
| Result |
| CheckIssuer(Input encodedIssuer) |
| { |
| // "The issuer field MUST contain a non-empty distinguished name (DN)." |
| Reader issuer(encodedIssuer); |
| Input encodedRDNs; |
| ExpectTagAndGetValue(issuer, der::SEQUENCE, encodedRDNs); |
| Reader rdns(encodedRDNs); |
| // Check that the issuer name contains at least one RDN |
| // (Note: this does not check related grammar rules, such as there being one |
| // or more AVAs in each RDN, or the values in AVAs not being empty strings) |
| if (rdns.AtEnd()) { |
| return Result::ERROR_EMPTY_ISSUER_NAME; |
| } |
| return Success; |
| } |
| |
| // 4.1.2.5 Validity |
| |
| Result |
| ParseValidity(Input encodedValidity, |
| /*optional out*/ Time* notBeforeOut, |
| /*optional out*/ Time* notAfterOut) |
| { |
| Reader validity(encodedValidity); |
| Time notBefore(Time::uninitialized); |
| if (der::TimeChoice(validity, notBefore) != Success) { |
| return Result::ERROR_INVALID_DER_TIME; |
| } |
| |
| Time notAfter(Time::uninitialized); |
| if (der::TimeChoice(validity, notAfter) != Success) { |
| return Result::ERROR_INVALID_DER_TIME; |
| } |
| |
| if (der::End(validity) != Success) { |
| return Result::ERROR_INVALID_DER_TIME; |
| } |
| |
| if (notBefore > notAfter) { |
| return Result::ERROR_INVALID_DER_TIME; |
| } |
| |
| if (notBeforeOut) { |
| *notBeforeOut = notBefore; |
| } |
| if (notAfterOut) { |
| *notAfterOut = notAfter; |
| } |
| |
| return Success; |
| } |
| |
| Result |
| CheckValidity(Time time, Time notBefore, Time notAfter) |
| { |
| if (time < notBefore) { |
| return Result::ERROR_NOT_YET_VALID_CERTIFICATE; |
| } |
| |
| if (time > notAfter) { |
| return Result::ERROR_EXPIRED_CERTIFICATE; |
| } |
| |
| return Success; |
| } |
| |
| // 4.1.2.7 Subject Public Key Info |
| |
| Result |
| CheckSubjectPublicKeyInfoContents(Reader& input, TrustDomain& trustDomain, |
| EndEntityOrCA endEntityOrCA) |
| { |
| // Here, we validate the syntax and do very basic semantic validation of the |
| // public key of the certificate. The intention here is to filter out the |
| // types of bad inputs that are most likely to trigger non-mathematical |
| // security vulnerabilities in the TrustDomain, like buffer overflows or the |
| // use of unsafe elliptic curves. |
| // |
| // We don't check (all of) the mathematical properties of the public key here |
| // because it is more efficient for the TrustDomain to do it during signature |
| // verification and/or other use of the public key. In particular, we |
| // delegate the arithmetic validation of the public key, as specified in |
| // NIST SP800-56A section 5.6.2, to the TrustDomain, at least for now. |
| |
| Reader algorithm; |
| Input subjectPublicKey; |
| Result rv = der::ExpectTagAndGetValue(input, der::SEQUENCE, algorithm); |
| if (rv != Success) { |
| return rv; |
| } |
| rv = der::BitStringWithNoUnusedBits(input, subjectPublicKey); |
| if (rv != Success) { |
| return rv; |
| } |
| rv = der::End(input); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| Reader subjectPublicKeyReader(subjectPublicKey); |
| |
| Reader algorithmOID; |
| rv = der::ExpectTagAndGetValue(algorithm, der::OIDTag, algorithmOID); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // RFC 3279 Section 2.3.1 |
| // python DottedOIDToCode.py rsaEncryption 1.2.840.113549.1.1.1 |
| static const uint8_t rsaEncryption[] = { |
| 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 |
| }; |
| |
| // RFC 3279 Section 2.3.5 and RFC 5480 Section 2.1.1 |
| // python DottedOIDToCode.py id-ecPublicKey 1.2.840.10045.2.1 |
| static const uint8_t id_ecPublicKey[] = { |
| 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 |
| }; |
| |
| if (algorithmOID.MatchRest(id_ecPublicKey)) { |
| // An id-ecPublicKey AlgorithmIdentifier has a parameter that identifes |
| // the curve being used. Although RFC 5480 specifies multiple forms, we |
| // only supported the NamedCurve form, where the curve is identified by an |
| // OID. |
| |
| Reader namedCurveOIDValue; |
| rv = der::ExpectTagAndGetValue(algorithm, der::OIDTag, |
| namedCurveOIDValue); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // RFC 5480 |
| // python DottedOIDToCode.py secp256r1 1.2.840.10045.3.1.7 |
| static const uint8_t secp256r1[] = { |
| 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 |
| }; |
| |
| // RFC 5480 |
| // python DottedOIDToCode.py secp384r1 1.3.132.0.34 |
| static const uint8_t secp384r1[] = { |
| 0x2b, 0x81, 0x04, 0x00, 0x22 |
| }; |
| |
| // RFC 5480 |
| // python DottedOIDToCode.py secp521r1 1.3.132.0.35 |
| static const uint8_t secp521r1[] = { |
| 0x2b, 0x81, 0x04, 0x00, 0x23 |
| }; |
| |
| // Matching is attempted based on a rough estimate of the commonality of the |
| // elliptic curve, to minimize the number of MatchRest calls. |
| NamedCurve curve; |
| unsigned int bits; |
| if (namedCurveOIDValue.MatchRest(secp256r1)) { |
| curve = NamedCurve::secp256r1; |
| bits = 256; |
| } else if (namedCurveOIDValue.MatchRest(secp384r1)) { |
| curve = NamedCurve::secp384r1; |
| bits = 384; |
| } else if (namedCurveOIDValue.MatchRest(secp521r1)) { |
| curve = NamedCurve::secp521r1; |
| bits = 521; |
| } else { |
| return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE; |
| } |
| |
| rv = trustDomain.CheckECDSACurveIsAcceptable(endEntityOrCA, curve); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // RFC 5480 Section 2.2 says that the first octet will be 0x04 to indicate |
| // an uncompressed point, which is the only encoding we support. |
| uint8_t compressedOrUncompressed; |
| rv = subjectPublicKeyReader.Read(compressedOrUncompressed); |
| if (rv != Success) { |
| return rv; |
| } |
| if (compressedOrUncompressed != 0x04) { |
| return Result::ERROR_UNSUPPORTED_EC_POINT_FORM; |
| } |
| |
| // The point is encoded as two raw (not DER-encoded) integers, each padded |
| // to the bit length (rounded up to the nearest byte). |
| Input point; |
| rv = subjectPublicKeyReader.SkipToEnd(point); |
| if (rv != Success) { |
| return rv; |
| } |
| if (point.GetLength() != ((bits + 7) / 8u) * 2u) { |
| return Result::ERROR_BAD_DER; |
| } |
| |
| // XXX: We defer the mathematical verification of the validity of the point |
| // until signature verification. This means that if we never verify a |
| // signature, we'll never fully check whether the public key is valid. |
| } else if (algorithmOID.MatchRest(rsaEncryption)) { |
| // RFC 3279 Section 2.3.1 says "The parameters field MUST have ASN.1 type |
| // NULL for this algorithm identifier." |
| rv = der::ExpectTagAndEmptyValue(algorithm, der::NULLTag); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // RSAPublicKey :: = SEQUENCE{ |
| // modulus INTEGER, --n |
| // publicExponent INTEGER } --e |
| rv = der::Nested(subjectPublicKeyReader, der::SEQUENCE, |
| [&trustDomain, endEntityOrCA](Reader& r) { |
| Input modulus; |
| Input::size_type modulusSignificantBytes; |
| Result nestedRv = |
| der::PositiveInteger(r, modulus, &modulusSignificantBytes); |
| if (nestedRv != Success) { |
| return nestedRv; |
| } |
| // XXX: Should we do additional checks of the modulus? |
| nestedRv = trustDomain.CheckRSAPublicKeyModulusSizeInBits( |
| endEntityOrCA, modulusSignificantBytes * 8u); |
| if (nestedRv != Success) { |
| return nestedRv; |
| } |
| |
| // XXX: We don't allow the TrustDomain to validate the exponent. |
| // XXX: We don't do our own sanity checking of the exponent. |
| Input exponent; |
| return der::PositiveInteger(r, exponent); |
| }); |
| if (rv != Success) { |
| return rv; |
| } |
| } else { |
| return Result::ERROR_UNSUPPORTED_KEYALG; |
| } |
| |
| rv = der::End(algorithm); |
| if (rv != Success) { |
| return rv; |
| } |
| rv = der::End(subjectPublicKeyReader); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| return Success; |
| } |
| |
| Result |
| CheckSubjectPublicKeyInfo(Input subjectPublicKeyInfo, TrustDomain& trustDomain, |
| EndEntityOrCA endEntityOrCA) |
| { |
| Reader spkiReader(subjectPublicKeyInfo); |
| Result rv = der::Nested(spkiReader, der::SEQUENCE, [&](Reader& r) { |
| return CheckSubjectPublicKeyInfoContents(r, trustDomain, endEntityOrCA); |
| }); |
| if (rv != Success) { |
| return rv; |
| } |
| return der::End(spkiReader); |
| } |
| |
| // 4.2.1.3. Key Usage (id-ce-keyUsage) |
| |
| // As explained in the comment in CheckKeyUsage, bit 0 is the most significant |
| // bit and bit 7 is the least significant bit. |
| inline uint8_t KeyUsageToBitMask(KeyUsage keyUsage) |
| { |
| assert(keyUsage != KeyUsage::noParticularKeyUsageRequired); |
| return 0x80u >> static_cast<uint8_t>(keyUsage); |
| } |
| |
| Result |
| CheckKeyUsage(EndEntityOrCA endEntityOrCA, const Input* encodedKeyUsage, |
| KeyUsage requiredKeyUsageIfPresent) |
| { |
| if (!encodedKeyUsage) { |
| // TODO(bug 970196): Reject certificates that are being used to verify |
| // certificate signatures unless the certificate is a trust anchor, to |
| // reduce the chances of an end-entity certificate being abused as a CA |
| // certificate. |
| // if (endEntityOrCA == EndEntityOrCA::MustBeCA && !isTrustAnchor) { |
| // return Result::ERROR_INADEQUATE_KEY_USAGE; |
| // } |
| // |
| // TODO: Users may configure arbitrary certificates as trust anchors, not |
| // just roots. We should only allow a certificate without a key usage to be |
| // used as a CA when it is self-issued and self-signed. |
| return Success; |
| } |
| |
| Reader input(*encodedKeyUsage); |
| Reader value; |
| if (der::ExpectTagAndGetValue(input, der::BIT_STRING, value) != Success) { |
| return Result::ERROR_INADEQUATE_KEY_USAGE; |
| } |
| |
| uint8_t numberOfPaddingBits; |
| if (value.Read(numberOfPaddingBits) != Success) { |
| return Result::ERROR_INADEQUATE_KEY_USAGE; |
| } |
| if (numberOfPaddingBits > 7) { |
| return Result::ERROR_INADEQUATE_KEY_USAGE; |
| } |
| |
| uint8_t bits; |
| if (value.Read(bits) != Success) { |
| // Reject empty bit masks. |
| return Result::ERROR_INADEQUATE_KEY_USAGE; |
| } |
| |
| // The most significant bit is numbered 0 (digitalSignature) and the least |
| // significant bit is numbered 7 (encipherOnly), and the padding is in the |
| // least significant bits of the last byte. The numbering of bits in a byte |
| // is backwards from how we usually interpret them. |
| // |
| // For example, let's say bits is encoded in one byte with of value 0xB0 and |
| // numberOfPaddingBits == 4. Then, bits is 10110000 in binary: |
| // |
| // bit 0 bit 3 |
| // | | |
| // v v |
| // 10110000 |
| // ^^^^ |
| // | |
| // 4 padding bits |
| // |
| // Since bits is the last byte, we have to consider the padding by ensuring |
| // that the least significant 4 bits are all zero, since DER rules require |
| // all padding bits to be zero. Then we have to look at the bit N bits to the |
| // right of the most significant bit, where N is a value from the KeyUsage |
| // enumeration. |
| // |
| // Let's say we're interested in the keyCertSign (5) bit. We'd need to look |
| // at bit 5, which is zero, so keyCertSign is not asserted. (Since we check |
| // that the padding is all zeros, it is OK to read from the padding bits.) |
| // |
| // Let's say we're interested in the digitalSignature (0) bit. We'd need to |
| // look at the bit 0 (the most significant bit), which is set, so that means |
| // digitalSignature is asserted. Similarly, keyEncipherment (2) and |
| // dataEncipherment (3) are asserted. |
| // |
| // Note that since the KeyUsage enumeration is limited to values 0-7, we |
| // only ever need to examine the first byte test for |
| // requiredKeyUsageIfPresent. |
| |
| if (requiredKeyUsageIfPresent != KeyUsage::noParticularKeyUsageRequired) { |
| // Check that the required key usage bit is set. |
| if ((bits & KeyUsageToBitMask(requiredKeyUsageIfPresent)) == 0) { |
| return Result::ERROR_INADEQUATE_KEY_USAGE; |
| } |
| } |
| |
| // RFC 5280 says "The keyCertSign bit is asserted when the subject public |
| // key is used for verifying signatures on public key certificates. If the |
| // keyCertSign bit is asserted, then the cA bit in the basic constraints |
| // extension (Section 4.2.1.9) MUST also be asserted." |
| // However, we allow end-entity certificates (i.e. certificates without |
| // basicConstraints.cA set to TRUE) to claim keyCertSign for compatibility |
| // reasons. This does not compromise security because we only allow |
| // certificates with basicConstraints.cA set to TRUE to act as CAs. |
| if (requiredKeyUsageIfPresent == KeyUsage::keyCertSign && |
| endEntityOrCA != EndEntityOrCA::MustBeCA) { |
| return Result::ERROR_INADEQUATE_KEY_USAGE; |
| } |
| |
| // The padding applies to the last byte, so skip to the last byte. |
| while (!value.AtEnd()) { |
| if (value.Read(bits) != Success) { |
| return Result::ERROR_INADEQUATE_KEY_USAGE; |
| } |
| } |
| |
| // All of the padding bits must be zero, according to DER rules. |
| uint8_t paddingMask = static_cast<uint8_t>((1 << numberOfPaddingBits) - 1); |
| if ((bits & paddingMask) != 0) { |
| return Result::ERROR_INADEQUATE_KEY_USAGE; |
| } |
| |
| return Success; |
| } |
| |
| // RFC5820 4.2.1.4. Certificate Policies |
| |
| // "The user-initial-policy-set contains the special value any-policy if the |
| // user is not concerned about certificate policy." |
| // |
| // python DottedOIDToCode.py anyPolicy 2.5.29.32.0 |
| |
| static const uint8_t anyPolicy[] = { |
| 0x55, 0x1d, 0x20, 0x00 |
| }; |
| |
| /*static*/ const CertPolicyId CertPolicyId::anyPolicy = { |
| 4, { 0x55, 0x1d, 0x20, 0x00 } |
| }; |
| |
| bool |
| CertPolicyId::IsAnyPolicy() const { |
| if (this == &CertPolicyId::anyPolicy) { |
| return true; |
| } |
| return numBytes == sizeof(::mozilla::pkix::anyPolicy) && |
| std::equal(bytes, bytes + numBytes, ::mozilla::pkix::anyPolicy); |
| } |
| |
| bool |
| CertPolicyId::operator==(const CertPolicyId& other) const |
| { |
| return numBytes == other.numBytes && |
| std::equal(bytes, bytes + numBytes, other.bytes); |
| } |
| |
| // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation |
| Result |
| CheckCertificatePolicies(EndEntityOrCA endEntityOrCA, |
| const Input* encodedCertificatePolicies, |
| const Input* encodedInhibitAnyPolicy, |
| TrustLevel trustLevel, |
| const CertPolicyId& requiredPolicy) |
| { |
| if (requiredPolicy.numBytes == 0 || |
| requiredPolicy.numBytes > sizeof requiredPolicy.bytes) { |
| return Result::FATAL_ERROR_INVALID_ARGS; |
| } |
| |
| bool requiredPolicyFound = requiredPolicy.IsAnyPolicy(); |
| if (requiredPolicyFound) { |
| return Success; |
| } |
| |
| // Bug 989051. Until we handle inhibitAnyPolicy we will fail close when |
| // inhibitAnyPolicy extension is present and we are validating for a policy. |
| if (!requiredPolicyFound && encodedInhibitAnyPolicy) { |
| return Result::ERROR_POLICY_VALIDATION_FAILED; |
| } |
| |
| // The root CA certificate may omit the policies that it has been |
| // trusted for, so we cannot require the policies to be present in those |
| // certificates. Instead, the determination of which roots are trusted for |
| // which policies is made by the TrustDomain's GetCertTrust method. |
| if (trustLevel == TrustLevel::TrustAnchor && |
| endEntityOrCA == EndEntityOrCA::MustBeCA) { |
| requiredPolicyFound = true; |
| } |
| |
| Input requiredPolicyDER; |
| if (requiredPolicyDER.Init(requiredPolicy.bytes, requiredPolicy.numBytes) |
| != Success) { |
| return Result::FATAL_ERROR_INVALID_ARGS; |
| } |
| |
| if (encodedCertificatePolicies) { |
| Reader extension(*encodedCertificatePolicies); |
| Reader certificatePolicies; |
| Result rv = der::ExpectTagAndGetValue(extension, der::SEQUENCE, |
| certificatePolicies); |
| if (rv != Success) { |
| return Result::ERROR_POLICY_VALIDATION_FAILED; |
| } |
| if (!extension.AtEnd()) { |
| return Result::ERROR_POLICY_VALIDATION_FAILED; |
| } |
| |
| do { |
| // PolicyInformation ::= SEQUENCE { |
| // policyIdentifier CertPolicyId, |
| // policyQualifiers SEQUENCE SIZE (1..MAX) OF |
| // PolicyQualifierInfo OPTIONAL } |
| Reader policyInformation; |
| rv = der::ExpectTagAndGetValue(certificatePolicies, der::SEQUENCE, |
| policyInformation); |
| if (rv != Success) { |
| return Result::ERROR_POLICY_VALIDATION_FAILED; |
| } |
| |
| Reader policyIdentifier; |
| rv = der::ExpectTagAndGetValue(policyInformation, der::OIDTag, |
| policyIdentifier); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| if (policyIdentifier.MatchRest(requiredPolicyDER)) { |
| requiredPolicyFound = true; |
| } else if (endEntityOrCA == EndEntityOrCA::MustBeCA && |
| policyIdentifier.MatchRest(anyPolicy)) { |
| requiredPolicyFound = true; |
| } |
| |
| // RFC 5280 Section 4.2.1.4 says "Optional qualifiers, which MAY be |
| // present, are not expected to change the definition of the policy." Also, |
| // it seems that Section 6, which defines validation, does not require any |
| // matching of qualifiers. Thus, doing anything with the policy qualifiers |
| // would be a waste of time and a source of potential incompatibilities, so |
| // we just ignore them. |
| } while (!requiredPolicyFound && !certificatePolicies.AtEnd()); |
| } |
| |
| if (!requiredPolicyFound) { |
| return Result::ERROR_POLICY_VALIDATION_FAILED; |
| } |
| |
| return Success; |
| } |
| |
| static const long UNLIMITED_PATH_LEN = -1; // must be less than zero |
| |
| // BasicConstraints ::= SEQUENCE { |
| // cA BOOLEAN DEFAULT FALSE, |
| // pathLenConstraint INTEGER (0..MAX) OPTIONAL } |
| |
| // RFC5280 4.2.1.9. Basic Constraints (id-ce-basicConstraints) |
| Result |
| CheckBasicConstraints(EndEntityOrCA endEntityOrCA, |
| const Input* encodedBasicConstraints, |
| const der::Version version, TrustLevel trustLevel, |
| unsigned int subCACount) |
| { |
| bool isCA = false; |
| long pathLenConstraint = UNLIMITED_PATH_LEN; |
| |
| if (encodedBasicConstraints) { |
| Reader input(*encodedBasicConstraints); |
| Result rv = der::Nested(input, der::SEQUENCE, |
| [&isCA, &pathLenConstraint](Reader& r) { |
| Result nestedRv = der::OptionalBoolean(r, isCA); |
| if (nestedRv != Success) { |
| return nestedRv; |
| } |
| // TODO(bug 985025): If isCA is false, pathLenConstraint |
| // MUST NOT be included (as per RFC 5280 section |
| // 4.2.1.9), but for compatibility reasons, we don't |
| // check this. |
| return der::OptionalInteger(r, UNLIMITED_PATH_LEN, pathLenConstraint); |
| }); |
| if (rv != Success) { |
| return Result::ERROR_EXTENSION_VALUE_INVALID; |
| } |
| if (der::End(input) != Success) { |
| return Result::ERROR_EXTENSION_VALUE_INVALID; |
| } |
| } else { |
| // "If the basic constraints extension is not present in a version 3 |
| // certificate, or the extension is present but the cA boolean is not |
| // asserted, then the certified public key MUST NOT be used to verify |
| // certificate signatures." |
| // |
| // For compatibility, we must accept v1 trust anchors without basic |
| // constraints as CAs. |
| // |
| // There are devices with v1 certificates that are unlikely to be trust |
| // anchors. In order to allow applications to treat this case differently |
| // from other basic constraints violations (e.g. allowing certificate error |
| // overrides for only this case), we return a different error code. |
| // |
| // TODO: add check for self-signedness? |
| if (endEntityOrCA == EndEntityOrCA::MustBeCA && version == der::Version::v1) { |
| if (trustLevel == TrustLevel::TrustAnchor) { |
| isCA = true; |
| } else { |
| return Result::ERROR_V1_CERT_USED_AS_CA; |
| } |
| } |
| } |
| |
| if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) { |
| // CA certificates are not trusted as EE certs. |
| |
| if (isCA) { |
| // Note that this check prevents a delegated OCSP response signing |
| // certificate with the CA bit from successfully validating when we check |
| // it from pkixocsp.cpp, which is a good thing. |
| return Result::ERROR_CA_CERT_USED_AS_END_ENTITY; |
| } |
| |
| return Success; |
| } |
| |
| assert(endEntityOrCA == EndEntityOrCA::MustBeCA); |
| |
| // End-entity certificates are not allowed to act as CA certs. |
| if (!isCA) { |
| return Result::ERROR_CA_CERT_INVALID; |
| } |
| |
| if (pathLenConstraint >= 0 && |
| static_cast<long>(subCACount) > pathLenConstraint) { |
| return Result::ERROR_PATH_LEN_CONSTRAINT_INVALID; |
| } |
| |
| return Success; |
| } |
| |
| // 4.2.1.12. Extended Key Usage (id-ce-extKeyUsage) |
| |
| static Result |
| MatchEKU(Reader& value, KeyPurposeId requiredEKU, |
| EndEntityOrCA endEntityOrCA, TrustDomain& trustDomain, |
| Time notBefore, /*in/out*/ bool& found, |
| /*in/out*/ bool& foundOCSPSigning) |
| { |
| // See Section 5.9 of "A Layman's Guide to a Subset of ASN.1, BER, and DER" |
| // for a description of ASN.1 DER encoding of OIDs. |
| |
| // id-pkix OBJECT IDENTIFIER ::= |
| // { iso(1) identified-organization(3) dod(6) internet(1) |
| // security(5) mechanisms(5) pkix(7) } |
| // id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } |
| // id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } |
| // id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } |
| // id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } |
| // id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } |
| // id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } |
| static const uint8_t server[] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 1 }; |
| static const uint8_t client[] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 2 }; |
| static const uint8_t code [] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 3 }; |
| static const uint8_t email [] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 4 }; |
| static const uint8_t ocsp [] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 9 }; |
| |
| // id-Netscape OBJECT IDENTIFIER ::= { 2 16 840 1 113730 } |
| // id-Netscape-policy OBJECT IDENTIFIER ::= { id-Netscape 4 } |
| // id-Netscape-stepUp OBJECT IDENTIFIER ::= { id-Netscape-policy 1 } |
| static const uint8_t serverStepUp[] = |
| { (40*2)+16, 128+6,72, 1, 128+6,128+120,66, 4, 1 }; |
| |
| bool match = false; |
| |
| if (!found) { |
| switch (requiredEKU) { |
| case KeyPurposeId::id_kp_serverAuth: { |
| if (value.MatchRest(server)) { |
| match = true; |
| break; |
| } |
| // Potentially treat CA certs with step-up OID as also having SSL server |
| // type. Comodo has issued certificates that require this behavior that |
| // don't expire until June 2020! |
| if (endEntityOrCA == EndEntityOrCA::MustBeCA && |
| value.MatchRest(serverStepUp)) { |
| Result rv = trustDomain.NetscapeStepUpMatchesServerAuth(notBefore, |
| match); |
| if (rv != Success) { |
| return rv; |
| } |
| } |
| break; |
| } |
| |
| case KeyPurposeId::id_kp_clientAuth: |
| match = value.MatchRest(client); |
| break; |
| |
| case KeyPurposeId::id_kp_codeSigning: |
| match = value.MatchRest(code); |
| break; |
| |
| case KeyPurposeId::id_kp_emailProtection: |
| match = value.MatchRest(email); |
| break; |
| |
| case KeyPurposeId::id_kp_OCSPSigning: |
| match = value.MatchRest(ocsp); |
| break; |
| |
| case KeyPurposeId::anyExtendedKeyUsage: |
| return NotReached("anyExtendedKeyUsage should start with found==true", |
| Result::FATAL_ERROR_LIBRARY_FAILURE); |
| } |
| } |
| |
| if (match) { |
| found = true; |
| if (requiredEKU == KeyPurposeId::id_kp_OCSPSigning) { |
| foundOCSPSigning = true; |
| } |
| } else if (value.MatchRest(ocsp)) { |
| foundOCSPSigning = true; |
| } |
| |
| value.SkipToEnd(); // ignore unmatched OIDs. |
| |
| return Success; |
| } |
| |
| Result |
| CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA, |
| const Input* encodedExtendedKeyUsage, |
| KeyPurposeId requiredEKU, TrustDomain& trustDomain, |
| Time notBefore) |
| { |
| // XXX: We're using Result::ERROR_INADEQUATE_CERT_TYPE here so that callers |
| // can distinguish EKU mismatch from KU mismatch from basic constraints |
| // mismatch. We should probably add a new error code that is more clear for |
| // this type of problem. |
| |
| bool foundOCSPSigning = false; |
| |
| if (encodedExtendedKeyUsage) { |
| bool found = requiredEKU == KeyPurposeId::anyExtendedKeyUsage; |
| |
| Reader input(*encodedExtendedKeyUsage); |
| Result rv = der::NestedOf(input, der::SEQUENCE, der::OIDTag, |
| der::EmptyAllowed::No, [&](Reader& r) { |
| return MatchEKU(r, requiredEKU, endEntityOrCA, trustDomain, notBefore, |
| found, foundOCSPSigning); |
| }); |
| if (rv != Success) { |
| return Result::ERROR_INADEQUATE_CERT_TYPE; |
| } |
| if (der::End(input) != Success) { |
| return Result::ERROR_INADEQUATE_CERT_TYPE; |
| } |
| |
| // If the EKU extension was included, then the required EKU must be in the |
| // list. |
| if (!found) { |
| return Result::ERROR_INADEQUATE_CERT_TYPE; |
| } |
| } |
| |
| // pkixocsp.cpp depends on the following additional checks. |
| |
| if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) { |
| // When validating anything other than an delegated OCSP signing cert, |
| // reject any cert that also claims to be an OCSP responder, because such |
| // a cert does not make sense. For example, if an SSL certificate were to |
| // assert id-kp-OCSPSigning then it could sign OCSP responses for itself, |
| // if not for this check. |
| // That said, we accept CA certificates with id-kp-OCSPSigning because |
| // some CAs in Mozilla's CA program have issued such intermediate |
| // certificates, and because some CAs have reported some Microsoft server |
| // software wrongly requires CA certificates to have id-kp-OCSPSigning. |
| // Allowing this exception does not cause any security issues because we |
| // require delegated OCSP response signing certificates to be end-entity |
| // certificates. |
| if (foundOCSPSigning && requiredEKU != KeyPurposeId::id_kp_OCSPSigning) { |
| return Result::ERROR_INADEQUATE_CERT_TYPE; |
| } |
| // http://tools.ietf.org/html/rfc6960#section-4.2.2.2: |
| // "OCSP signing delegation SHALL be designated by the inclusion of |
| // id-kp-OCSPSigning in an extended key usage certificate extension |
| // included in the OCSP response signer's certificate." |
| // |
| // id-kp-OCSPSigning is the only EKU that isn't implicitly assumed when the |
| // EKU extension is missing from an end-entity certificate. However, any CA |
| // certificate can issue a delegated OCSP response signing certificate, so |
| // we can't require the EKU be explicitly included for CA certificates. |
| if (!foundOCSPSigning && requiredEKU == KeyPurposeId::id_kp_OCSPSigning) { |
| return Result::ERROR_INADEQUATE_CERT_TYPE; |
| } |
| } |
| |
| return Success; |
| } |
| |
| Result |
| CheckTLSFeatures(const BackCert& subject, BackCert& potentialIssuer) |
| { |
| const Input* issuerTLSFeatures = potentialIssuer.GetRequiredTLSFeatures(); |
| if (!issuerTLSFeatures) { |
| return Success; |
| } |
| |
| const Input* subjectTLSFeatures = subject.GetRequiredTLSFeatures(); |
| if (issuerTLSFeatures->GetLength() == 0 || |
| !subjectTLSFeatures || |
| !InputsAreEqual(*issuerTLSFeatures, *subjectTLSFeatures)) { |
| return Result::ERROR_REQUIRED_TLS_FEATURE_MISSING; |
| } |
| |
| return Success; |
| } |
| |
| Result |
| TLSFeaturesSatisfiedInternal(const Input* requiredTLSFeatures, |
| const Input* stapledOCSPResponse) |
| { |
| if (!requiredTLSFeatures) { |
| return Success; |
| } |
| |
| // RFC 6066 10.2: ExtensionType status_request |
| const static uint8_t status_request = 5; |
| const static uint8_t status_request_bytes[] = { status_request }; |
| |
| Reader input(*requiredTLSFeatures); |
| return der::NestedOf(input, der::SEQUENCE, der::INTEGER, |
| der::EmptyAllowed::No, [&](Reader& r) { |
| if (!r.MatchRest(status_request_bytes)) { |
| return Result::ERROR_REQUIRED_TLS_FEATURE_MISSING; |
| } |
| |
| if (!stapledOCSPResponse) { |
| return Result::ERROR_REQUIRED_TLS_FEATURE_MISSING; |
| } |
| |
| return Result::Success; |
| }); |
| } |
| |
| Result |
| CheckTLSFeaturesAreSatisfied(Input& cert, |
| const Input* stapledOCSPResponse) |
| { |
| BackCert backCert(cert, EndEntityOrCA::MustBeEndEntity, nullptr); |
| Result rv = backCert.Init(); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| return TLSFeaturesSatisfiedInternal(backCert.GetRequiredTLSFeatures(), |
| stapledOCSPResponse); |
| } |
| |
| Result |
| CheckIssuerIndependentProperties(TrustDomain& trustDomain, |
| const BackCert& cert, |
| Time time, |
| KeyUsage requiredKeyUsageIfPresent, |
| KeyPurposeId requiredEKUIfPresent, |
| const CertPolicyId& requiredPolicy, |
| unsigned int subCACount, |
| /*out*/ TrustLevel& trustLevel) |
| { |
| Result rv; |
| |
| const EndEntityOrCA endEntityOrCA = cert.endEntityOrCA; |
| |
| // Check the cert's trust first, because we want to minimize the amount of |
| // processing we do on a distrusted cert, in case it is trying to exploit |
| // some bug in our processing. |
| rv = trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy, cert.GetDER(), |
| trustLevel); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // IMPORTANT: We parse the validity interval here, so that we can use the |
| // notBefore and notAfter values in checks for things that might be deprecated |
| // over time. However, we must not fail for semantic errors until the end of |
| // this method, in order to preserve error ranking. |
| Time notBefore(Time::uninitialized); |
| Time notAfter(Time::uninitialized); |
| rv = ParseValidity(cert.GetValidity(), ¬Before, ¬After); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| if (trustLevel == TrustLevel::TrustAnchor && |
| endEntityOrCA == EndEntityOrCA::MustBeEndEntity && |
| requiredEKUIfPresent == KeyPurposeId::id_kp_OCSPSigning) { |
| // OCSP signer certificates can never be trust anchors, especially |
| // since we don't support designated OCSP responders. All of the checks |
| // below that are dependent on trustLevel rely on this overriding of the |
| // trust level for OCSP signers. |
| trustLevel = TrustLevel::InheritsTrust; |
| } |
| |
| switch (trustLevel) { |
| case TrustLevel::InheritsTrust: |
| rv = CheckSignatureAlgorithm(trustDomain, endEntityOrCA, notBefore, |
| cert.GetSignedData(), cert.GetSignature()); |
| if (rv != Success) { |
| return rv; |
| } |
| break; |
| |
| case TrustLevel::TrustAnchor: |
| // We don't even bother checking signatureAlgorithm or signature for |
| // syntactic validity for trust anchors, because we don't use those |
| // fields for anything, and because the trust anchor might be signed |
| // with a signature algorithm we don't actually support. |
| break; |
| |
| case TrustLevel::ActivelyDistrusted: |
| return Result::ERROR_UNTRUSTED_CERT; |
| } |
| |
| // Check the SPKI early, because it is one of the most selective properties |
| // of the certificate due to SHA-1 deprecation and the deprecation of |
| // certificates with keys weaker than RSA 2048. |
| rv = CheckSubjectPublicKeyInfo(cert.GetSubjectPublicKeyInfo(), trustDomain, |
| endEntityOrCA); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // 4.1.2.4. Issuer |
| rv = CheckIssuer(cert.GetIssuer()); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // 4.2.1.1. Authority Key Identifier is ignored (see bug 965136). |
| |
| // 4.2.1.2. Subject Key Identifier is ignored (see bug 965136). |
| |
| // 4.2.1.3. Key Usage |
| rv = CheckKeyUsage(endEntityOrCA, cert.GetKeyUsage(), |
| requiredKeyUsageIfPresent); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // 4.2.1.4. Certificate Policies |
| rv = CheckCertificatePolicies(endEntityOrCA, cert.GetCertificatePolicies(), |
| cert.GetInhibitAnyPolicy(), trustLevel, |
| requiredPolicy); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // 4.2.1.5. Policy Mappings are not supported; see the documentation about |
| // policy enforcement in pkix.h. |
| |
| // 4.2.1.6. Subject Alternative Name dealt with during name constraint |
| // checking and during name verification (CERT_VerifyCertName). |
| |
| // 4.2.1.7. Issuer Alternative Name is not something that needs checking. |
| |
| // 4.2.1.8. Subject Directory Attributes is not something that needs |
| // checking. |
| |
| // 4.2.1.9. Basic Constraints. |
| rv = CheckBasicConstraints(endEntityOrCA, cert.GetBasicConstraints(), |
| cert.GetVersion(), trustLevel, subCACount); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // 4.2.1.10. Name Constraints is dealt with in during path building. |
| |
| // 4.2.1.11. Policy Constraints are implicitly supported; see the |
| // documentation about policy enforcement in pkix.h. |
| |
| // 4.2.1.12. Extended Key Usage |
| rv = CheckExtendedKeyUsage(endEntityOrCA, cert.GetExtKeyUsage(), |
| requiredEKUIfPresent, trustDomain, notBefore); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| // 4.2.1.13. CRL Distribution Points is not supported, though the |
| // TrustDomain's CheckRevocation method may parse it and process it |
| // on its own. |
| |
| // 4.2.1.14. Inhibit anyPolicy is implicitly supported; see the documentation |
| // about policy enforcement in pkix.h. |
| |
| // IMPORTANT: Even though we parse validity above, we wait until this point to |
| // check it, so that error ranking works correctly. |
| rv = CheckValidity(time, notBefore, notAfter); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| rv = trustDomain.CheckValidityIsAcceptable(notBefore, notAfter, endEntityOrCA, |
| requiredEKUIfPresent); |
| if (rv != Success) { |
| return rv; |
| } |
| |
| return Success; |
| } |
| |
| } } // namespace mozilla::pkix |