123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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/.
- */
- #include "pkix/pkixcheck.h"
- #include "pkix/pkixder.h"
- #include "pkix/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
|