/* -*- 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. */ #ifndef mozilla_pkix_pkixutil_h #define mozilla_pkix_pkixutil_h #include "pkixder.h" namespace mozilla { namespace pkix { // During path building and verification, we build a linked list of BackCerts // from the current cert toward the end-entity certificate. The linked list // is used to verify properties that aren't local to the current certificate // and/or the direct link between the current certificate and its issuer, // such as name constraints. // // Each BackCert contains pointers to all the given certificate's extensions // so that we can parse the extension block once and then process the // extensions in an order that may be different than they appear in the cert. class BackCert final { public: // certDER and childCert must be valid for the lifetime of BackCert. BackCert(Input certDER, EndEntityOrCA endEntityOrCA, const BackCert* childCert) : der(certDER) , endEntityOrCA(endEntityOrCA) , childCert(childCert) { } Result Init(); const Input GetDER() const { return der; } const der::SignedDataWithSignature& GetSignedData() const { return signedData; } der::Version GetVersion() const { return version; } const Input GetSerialNumber() const { return serialNumber; } const Input GetSignature() const { return signature; } const Input GetIssuer() const { return issuer; } // XXX: "validity" is a horrible name for the structure that holds // notBefore & notAfter, but that is the name used in RFC 5280 and we use the // RFC 5280 names for everything. const Input GetValidity() const { return validity; } const Input GetSubject() const { return subject; } const Input GetSubjectPublicKeyInfo() const { return subjectPublicKeyInfo; } const Input* GetAuthorityInfoAccess() const { return MaybeInput(authorityInfoAccess); } const Input* GetBasicConstraints() const { return MaybeInput(basicConstraints); } const Input* GetCertificatePolicies() const { return MaybeInput(certificatePolicies); } const Input* GetExtKeyUsage() const { return MaybeInput(extKeyUsage); } const Input* GetKeyUsage() const { return MaybeInput(keyUsage); } const Input* GetInhibitAnyPolicy() const { return MaybeInput(inhibitAnyPolicy); } const Input* GetNameConstraints() const { return MaybeInput(nameConstraints); } const Input* GetSubjectAltName() const { return MaybeInput(subjectAltName); } const Input* GetRequiredTLSFeatures() const { return MaybeInput(requiredTLSFeatures); } const Input* GetSignedCertificateTimestamps() const { return MaybeInput(signedCertificateTimestamps); } private: const Input der; public: const EndEntityOrCA endEntityOrCA; BackCert const* const childCert; private: // When parsing certificates in BackCert::Init, we don't accept empty // extensions. Consequently, we don't have to store a distinction between // empty extensions and extensions that weren't included. However, when // *processing* extensions, we distinguish between whether an extension was // included or not based on whetehr the GetXXX function for the extension // returns nullptr. static inline const Input* MaybeInput(const Input& item) { return item.GetLength() > 0 ? &item : nullptr; } der::SignedDataWithSignature signedData; der::Version version; Input serialNumber; Input signature; Input issuer; // XXX: "validity" is a horrible name for the structure that holds // notBefore & notAfter, but that is the name used in RFC 5280 and we use the // RFC 5280 names for everything. Input validity; Input subject; Input subjectPublicKeyInfo; Input authorityInfoAccess; Input basicConstraints; Input certificatePolicies; Input extKeyUsage; Input inhibitAnyPolicy; Input keyUsage; Input nameConstraints; Input subjectAltName; Input criticalNetscapeCertificateType; Input requiredTLSFeatures; Input signedCertificateTimestamps; // RFC 6962 (Certificate Transparency) Result RememberExtension(Reader& extnID, Input extnValue, bool critical, /*out*/ bool& understood); BackCert(const BackCert&) = delete; void operator=(const BackCert&) = delete; }; class NonOwningDERArray final : public DERArray { public: NonOwningDERArray() : numItems(0) { // we don't need to initialize the items array because we always check // numItems before accessing i. } size_t GetLength() const override { return numItems; } const Input* GetDER(size_t i) const override { return i < numItems ? &items[i] : nullptr; } Result Append(Input der) { if (numItems >= MAX_LENGTH) { return Result::FATAL_ERROR_INVALID_ARGS; } Result rv = items[numItems].Init(der); // structure assignment if (rv != Success) { return rv; } ++numItems; return Success; } // Public so we can static_assert on this. Keep in sync with MAX_SUBCA_COUNT. static const size_t MAX_LENGTH = 8; private: Input items[MAX_LENGTH]; // avoids any heap allocations size_t numItems; NonOwningDERArray(const NonOwningDERArray&) = delete; void operator=(const NonOwningDERArray&) = delete; }; // Extracts the SignedCertificateTimestampList structure which is encoded as an // OCTET STRING within the X.509v3 / OCSP extensions (see RFC 6962 section 3.3). Result ExtractSignedCertificateTimestampListFromExtension(Input extnValue, Input& sctList); inline unsigned int DaysBeforeYear(unsigned int year) { assert(year <= 9999); return ((year - 1u) * 365u) + ((year - 1u) / 4u) // leap years are every 4 years, - ((year - 1u) / 100u) // except years divisible by 100, + ((year - 1u) / 400u); // except years divisible by 400. } static const size_t MAX_DIGEST_SIZE_IN_BYTES = 512 / 8; // sha-512 Result DigestSignedData(TrustDomain& trustDomain, const der::SignedDataWithSignature& signedData, /*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES], /*out*/ der::PublicKeyAlgorithm& publicKeyAlg, /*out*/ SignedDigest& signedDigest); Result VerifySignedDigest(TrustDomain& trustDomain, der::PublicKeyAlgorithm publicKeyAlg, const SignedDigest& signedDigest, Input signerSubjectPublicKeyInfo); // Combines DigestSignedData and VerifySignedDigest Result VerifySignedData(TrustDomain& trustDomain, const der::SignedDataWithSignature& signedData, Input signerSubjectPublicKeyInfo); // Extracts the key parameters from |subjectPublicKeyInfo|, invoking // the relevant methods of |trustDomain|. Result CheckSubjectPublicKeyInfo(Input subjectPublicKeyInfo, TrustDomain& trustDomain, EndEntityOrCA endEntityOrCA); // In a switch over an enum, sometimes some compilers are not satisfied that // all control flow paths have been considered unless there is a default case. // However, in our code, such a default case is almost always unreachable dead // code. That can be particularly problematic when the compiler wants the code // to choose a value, such as a return value, for the default case, but there's // no appropriate "impossible case" value to choose. // // MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM accounts for this. Example: // // // In xy.cpp // #include "xt.h" // // enum class XY { X, Y }; // // int func(XY xy) { // switch (xy) { // case XY::X: return 1; // case XY::Y; return 2; // MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM // } // } #if defined(__clang__) // Clang will warn if not all cases are covered (-Wswitch-enum) AND it will // warn if a switch statement that covers every enum label has a default case // (-W-covered-switch-default). Versions prior to 3.5 warned about unreachable // code in such default cases (-Wunreachable-code) even when // -W-covered-switch-default was disabled, but that changed in Clang 3.5. #define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM // empty #elif defined(__GNUC__) // GCC will warn if not all cases are covered (-Wswitch-enum). It does not // assume that the default case is unreachable. #define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \ default: assert(false); __builtin_unreachable(); #elif defined(_MSC_VER) // MSVC will warn if not all cases are covered (C4061, level 4). It does not // assume that the default case is unreachable. #define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \ default: assert(false); __assume(0); #else #error Unsupported compiler for MOZILLA_PKIX_UNREACHABLE_DEFAULT. #endif } } // namespace mozilla::pkix #endif // mozilla_pkix_pkixutil_h