diff options
Diffstat (limited to 'security/pkix/include')
-rw-r--r-- | security/pkix/include/pkix/Input.h | 348 | ||||
-rw-r--r-- | security/pkix/include/pkix/Result.h | 235 | ||||
-rw-r--r-- | security/pkix/include/pkix/Time.h | 155 | ||||
-rw-r--r-- | security/pkix/include/pkix/pkix.h | 161 | ||||
-rw-r--r-- | security/pkix/include/pkix/pkixnss.h | 109 | ||||
-rw-r--r-- | security/pkix/include/pkix/pkixtypes.h | 409 |
6 files changed, 1417 insertions, 0 deletions
diff --git a/security/pkix/include/pkix/Input.h b/security/pkix/include/pkix/Input.h new file mode 100644 index 000000000..e09526fb4 --- /dev/null +++ b/security/pkix/include/pkix/Input.h @@ -0,0 +1,348 @@ +/* -*- 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_Input_h +#define mozilla_pkix_Input_h + +#include <algorithm> + +#include "pkix/Result.h" +#include "stdint.h" + +namespace mozilla { namespace pkix { + +class Reader; + +// An Input is a safety-oriented immutable weak reference to a array of bytes +// of a known size. The data can only be legally accessed by constructing a +// Reader object, which guarantees all accesses to the data are memory safe. +// Neither Input not Reader provide any facilities for modifying the data +// they reference. +// +// Inputs are small and should usually be passed by value, not by reference, +// though for inline functions the distinction doesn't matter: +// +// Result GoodExample(Input input); +// Result BadExample(const Input& input); +// Result WorseExample(const uint8_t* input, size_t len); +// +// Note that in the example, GoodExample has the same performance +// characteristics as WorseExample, but with much better safety guarantees. +class Input final +{ +public: + typedef uint16_t size_type; + + // This constructor is useful for inputs that are statically known to be of a + // fixed size, e.g.: + // + // static const uint8_t EXPECTED_BYTES[] = { 0x00, 0x01, 0x02 }; + // const Input expected(EXPECTED_BYTES); + // + // This is equivalent to (and preferred over): + // + // static const uint8_t EXPECTED_BYTES[] = { 0x00, 0x01, 0x02 }; + // Input expected; + // Result rv = expected.Init(EXPECTED_BYTES, sizeof EXPECTED_BYTES); + template <size_type N> + explicit Input(const uint8_t (&data)[N]) + : data(data) + , len(N) + { + } + + // Construct a valid, empty, Init-able Input. + Input() + : data(nullptr) + , len(0u) + { + } + + // This is intentionally not explicit in order to allow value semantics. + Input(const Input&) = default; + + // Initialize the input. data must be non-null and len must be less than + // 65536. Init may not be called more than once. + Result Init(const uint8_t* data, size_t len) + { + if (this->data) { + // already initialized + return Result::FATAL_ERROR_INVALID_ARGS; + } + if (!data || len > 0xffffu) { + // input too large + return Result::ERROR_BAD_DER; + } + + this->data = data; + this->len = len; + + return Success; + } + + // Initialize the input to be equivalent to the given input. Init may not be + // called more than once. + // + // This is basically operator=, but it wasn't given that name because + // normally callers do not check the result of operator=, and normally + // operator= can be used multiple times. + Result Init(Input other) + { + return Init(other.data, other.len); + } + + // Returns the length of the input. + // + // Having the return type be size_type instead of size_t avoids the need for + // callers to ensure that the result is small enough. + size_type GetLength() const { return static_cast<size_type>(len); } + + // Don't use this. It is here because we have some "friend" functions that we + // don't want to declare in this header file. + const uint8_t* UnsafeGetData() const { return data; } + +private: + const uint8_t* data; + size_t len; + + void operator=(const Input&) = delete; // Use Init instead. +}; + +inline bool +InputsAreEqual(const Input& a, const Input& b) +{ + return a.GetLength() == b.GetLength() && + std::equal(a.UnsafeGetData(), a.UnsafeGetData() + a.GetLength(), b.UnsafeGetData()); +} + +// An Reader is a cursor/iterator through the contents of an Input, designed to +// maximize safety during parsing while minimizing the performance cost of that +// safety. In particular, all methods do strict bounds checking to ensure +// buffer overflows are impossible, and they are all inline so that the +// compiler can coalesce as many of those checks together as possible. +// +// In general, Reader allows for one byte of lookahead and no backtracking. +// However, the Match* functions internally may have more lookahead. +class Reader final +{ +public: + Reader() + : input(nullptr) + , end(nullptr) + { + } + + explicit Reader(Input input) + : input(input.UnsafeGetData()) + , end(input.UnsafeGetData() + input.GetLength()) + { + } + + Result Init(Input input) + { + if (this->input) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + this->input = input.UnsafeGetData(); + this->end = input.UnsafeGetData() + input.GetLength(); + return Success; + } + + bool Peek(uint8_t expectedByte) const + { + return input < end && *input == expectedByte; + } + + Result Read(uint8_t& out) + { + Result rv = EnsureLength(1); + if (rv != Success) { + return rv; + } + out = *input++; + return Success; + } + + Result Read(uint16_t& out) + { + Result rv = EnsureLength(2); + if (rv != Success) { + return rv; + } + out = *input++; + out <<= 8u; + out |= *input++; + return Success; + } + + template <Input::size_type N> + bool MatchRest(const uint8_t (&toMatch)[N]) + { + // Normally we use EnsureLength which compares (input + len < end), but + // here we want to be sure that there is nothing following the matched + // bytes + if (static_cast<size_t>(end - input) != N) { + return false; + } + if (!std::equal(input, end, toMatch)) { + return false; + } + input = end; + return true; + } + + bool MatchRest(Input toMatch) + { + // Normally we use EnsureLength which compares (input + len < end), but + // here we want to be sure that there is nothing following the matched + // bytes + size_t remaining = static_cast<size_t>(end - input); + if (toMatch.GetLength() != remaining) { + return false; + } + if (!std::equal(input, end, toMatch.UnsafeGetData())) { + return false; + } + input = end; + return true; + } + + Result Skip(Input::size_type len) + { + Result rv = EnsureLength(len); + if (rv != Success) { + return rv; + } + input += len; + return Success; + } + + Result Skip(Input::size_type len, Reader& skipped) + { + Result rv = EnsureLength(len); + if (rv != Success) { + return rv; + } + rv = skipped.Init(input, len); + if (rv != Success) { + return rv; + } + input += len; + return Success; + } + + Result Skip(Input::size_type len, /*out*/ Input& skipped) + { + Result rv = EnsureLength(len); + if (rv != Success) { + return rv; + } + rv = skipped.Init(input, len); + if (rv != Success) { + return rv; + } + input += len; + return Success; + } + + void SkipToEnd() + { + input = end; + } + + Result SkipToEnd(/*out*/ Input& skipped) + { + return Skip(static_cast<Input::size_type>(end - input), skipped); + } + + Result EnsureLength(Input::size_type len) + { + if (static_cast<size_t>(end - input) < len) { + return Result::ERROR_BAD_DER; + } + return Success; + } + + bool AtEnd() const { return input == end; } + + class Mark final + { + public: + Mark(const Mark&) = default; // Intentionally not explicit. + private: + friend class Reader; + Mark(const Reader& input, const uint8_t* mark) : input(input), mark(mark) { } + const Reader& input; + const uint8_t* const mark; + void operator=(const Mark&) = delete; + }; + + Mark GetMark() const { return Mark(*this, input); } + + Result GetInput(const Mark& mark, /*out*/ Input& item) + { + if (&mark.input != this || mark.mark > input) { + return NotReached("invalid mark", Result::FATAL_ERROR_INVALID_ARGS); + } + return item.Init(mark.mark, + static_cast<Input::size_type>(input - mark.mark)); + } + +private: + Result Init(const uint8_t* data, Input::size_type len) + { + if (input) { + // already initialized + return Result::FATAL_ERROR_INVALID_ARGS; + } + input = data; + end = data + len; + return Success; + } + + const uint8_t* input; + const uint8_t* end; + + Reader(const Reader&) = delete; + void operator=(const Reader&) = delete; +}; + +inline bool +InputContains(const Input& input, uint8_t toFind) +{ + Reader reader(input); + for (;;) { + uint8_t b; + if (reader.Read(b) != Success) { + return false; + } + if (b == toFind) { + return true; + } + } +} + +} } // namespace mozilla::pkix + +#endif // mozilla_pkix_Input_h diff --git a/security/pkix/include/pkix/Result.h b/security/pkix/include/pkix/Result.h new file mode 100644 index 000000000..410b7bcbe --- /dev/null +++ b/security/pkix/include/pkix/Result.h @@ -0,0 +1,235 @@ +/* -*- 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_Result_h +#define mozilla_pkix_Result_h + +#include <cassert> + +namespace mozilla { namespace pkix { + +static const unsigned int FATAL_ERROR_FLAG = 0x800; + +// ---------------------------------------------------------------------------- +// SELECTED ERROR CODE EXPLANATIONS +// +// Result::ERROR_UNTRUSTED_CERT +// means that the end-entity certificate was actively distrusted. +// Result::ERROR_UNTRUSTED_ISSUER +// means that path building failed because of active distrust. +// Result::ERROR_INVALID_DER_TIME +// means the DER-encoded time was unexpected, such as being before the +// UNIX epoch (allowed by X500, but not valid here). +// Result::ERROR_EXPIRED_CERTIFICATE +// means the end entity certificate expired. +// Result::ERROR_EXPIRED_ISSUER_CERTIFICATE +// means the CA certificate expired. +// Result::ERROR_UNKNOWN_ISSUER +// means that the CA could not be found in the root store. +// Result::ERROR_POLICY_VALIDATION_FAILED +// means that an encoded policy could not be applied or wasn't present +// when expected. Usually this is in the context of Extended Validation. +// Result::ERROR_BAD_CERT_DOMAIN +// means that the certificate's name couldn't be matched to the +// reference identifier. +// Result::ERROR_CERT_NOT_IN_NAME_SPACE +// typically means the certificate violates name constraints applied +// by the issuer. +// Result::ERROR_BAD_DER +// means the input was improperly encoded. +// Result::ERROR_UNKNOWN_ERROR +// means that an external library (NSS) provided an error we didn't +// anticipate. See the map below in Result.h to add new ones. +// Result::FATAL_ERROR_LIBRARY_FAILURE +// is an unexpected fatal error indicating a library had an unexpected +// failure, and we can't proceed. +// Result::FATAL_ERROR_INVALID_ARGS +// means that we violated our own expectations on inputs and there's a +// bug somewhere. +// Result::FATAL_ERROR_INVALID_STATE +// means that we violated our own expectations on state and there's a +// bug somewhere. +// Result::FATAL_ERROR_NO_MEMORY +// means a memory allocation failed, prohibiting validation. +// ---------------------------------------------------------------------------- + +// The first argument to MOZILLA_PKIX_MAP() is used for building the mapping +// from error code to error name in MapResultToName. +// +// The second argument is for defining the value for the enum literal in the +// Result enum class. +// +// The third argument to MOZILLA_PKIX_MAP() is used, along with the first +// argument, for maintaining the mapping of mozilla::pkix error codes to +// NSS/NSPR error codes in pkixnss.cpp. +#define MOZILLA_PKIX_MAP_LIST \ + MOZILLA_PKIX_MAP(Success, 0, 0) \ + MOZILLA_PKIX_MAP(ERROR_BAD_DER, 1, \ + SEC_ERROR_BAD_DER) \ + MOZILLA_PKIX_MAP(ERROR_CA_CERT_INVALID, 2, \ + SEC_ERROR_CA_CERT_INVALID) \ + MOZILLA_PKIX_MAP(ERROR_BAD_SIGNATURE, 3, \ + SEC_ERROR_BAD_SIGNATURE) \ + MOZILLA_PKIX_MAP(ERROR_CERT_BAD_ACCESS_LOCATION, 4, \ + SEC_ERROR_CERT_BAD_ACCESS_LOCATION) \ + MOZILLA_PKIX_MAP(ERROR_CERT_NOT_IN_NAME_SPACE, 5, \ + SEC_ERROR_CERT_NOT_IN_NAME_SPACE) \ + MOZILLA_PKIX_MAP(ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 6, \ + SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) \ + MOZILLA_PKIX_MAP(ERROR_CONNECT_REFUSED, 7, \ + PR_CONNECT_REFUSED_ERROR) \ + MOZILLA_PKIX_MAP(ERROR_EXPIRED_CERTIFICATE, 8, \ + SEC_ERROR_EXPIRED_CERTIFICATE) \ + MOZILLA_PKIX_MAP(ERROR_EXTENSION_VALUE_INVALID, 9, \ + SEC_ERROR_EXTENSION_VALUE_INVALID) \ + MOZILLA_PKIX_MAP(ERROR_INADEQUATE_CERT_TYPE, 10, \ + SEC_ERROR_INADEQUATE_CERT_TYPE) \ + MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_USAGE, 11, \ + SEC_ERROR_INADEQUATE_KEY_USAGE) \ + MOZILLA_PKIX_MAP(ERROR_INVALID_ALGORITHM, 12, \ + SEC_ERROR_INVALID_ALGORITHM) \ + MOZILLA_PKIX_MAP(ERROR_INVALID_DER_TIME, 13, \ + SEC_ERROR_INVALID_TIME) \ + MOZILLA_PKIX_MAP(ERROR_KEY_PINNING_FAILURE, 14, \ + MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE) \ + MOZILLA_PKIX_MAP(ERROR_PATH_LEN_CONSTRAINT_INVALID, 15, \ + SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID) \ + MOZILLA_PKIX_MAP(ERROR_POLICY_VALIDATION_FAILED, 16, \ + SEC_ERROR_POLICY_VALIDATION_FAILED) \ + MOZILLA_PKIX_MAP(ERROR_REVOKED_CERTIFICATE, 17, \ + SEC_ERROR_REVOKED_CERTIFICATE) \ + MOZILLA_PKIX_MAP(ERROR_UNKNOWN_CRITICAL_EXTENSION, 18, \ + SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION) \ + MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ERROR, 19, \ + PR_UNKNOWN_ERROR) \ + MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ISSUER, 20, \ + SEC_ERROR_UNKNOWN_ISSUER) \ + MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_CERT, 21, \ + SEC_ERROR_UNTRUSTED_CERT) \ + MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_ISSUER, 22, \ + SEC_ERROR_UNTRUSTED_ISSUER) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_BAD_SIGNATURE, 23, \ + SEC_ERROR_OCSP_BAD_SIGNATURE) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_INVALID_SIGNING_CERT, 24, \ + SEC_ERROR_OCSP_INVALID_SIGNING_CERT) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_REQUEST, 25, \ + SEC_ERROR_OCSP_MALFORMED_REQUEST) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_RESPONSE, 26, \ + SEC_ERROR_OCSP_MALFORMED_RESPONSE) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_OLD_RESPONSE, 27, \ + SEC_ERROR_OCSP_OLD_RESPONSE) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_REQUEST_NEEDS_SIG, 28, \ + SEC_ERROR_OCSP_REQUEST_NEEDS_SIG) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONDER_CERT_INVALID, 29, \ + SEC_ERROR_OCSP_RESPONDER_CERT_INVALID) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_SERVER_ERROR, 30, \ + SEC_ERROR_OCSP_SERVER_ERROR) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_TRY_SERVER_LATER, 31, \ + SEC_ERROR_OCSP_TRY_SERVER_LATER) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_UNAUTHORIZED_REQUEST, 32, \ + SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, 33, \ + SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_CERT, 34, \ + SEC_ERROR_OCSP_UNKNOWN_CERT) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_FUTURE_RESPONSE, 35, \ + SEC_ERROR_OCSP_FUTURE_RESPONSE) \ + MOZILLA_PKIX_MAP(ERROR_INVALID_KEY, 36, \ + SEC_ERROR_INVALID_KEY) \ + MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_KEYALG, 37, \ + SEC_ERROR_UNSUPPORTED_KEYALG) \ + MOZILLA_PKIX_MAP(ERROR_EXPIRED_ISSUER_CERTIFICATE, 38, \ + SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE) \ + MOZILLA_PKIX_MAP(ERROR_CA_CERT_USED_AS_END_ENTITY, 39, \ + MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY) \ + MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_SIZE, 40, \ + MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE) \ + MOZILLA_PKIX_MAP(ERROR_V1_CERT_USED_AS_CA, 41, \ + MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA) \ + MOZILLA_PKIX_MAP(ERROR_BAD_CERT_DOMAIN, 42, \ + SSL_ERROR_BAD_CERT_DOMAIN) \ + MOZILLA_PKIX_MAP(ERROR_NO_RFC822NAME_MATCH, 43, \ + MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH) \ + MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_ELLIPTIC_CURVE, 44, \ + SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE) \ + MOZILLA_PKIX_MAP(ERROR_NOT_YET_VALID_CERTIFICATE, 45, \ + MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE) \ + MOZILLA_PKIX_MAP(ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE, 46, \ + MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE) \ + MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_EC_POINT_FORM, 47, \ + SEC_ERROR_UNSUPPORTED_EC_POINT_FORM) \ + MOZILLA_PKIX_MAP(ERROR_SIGNATURE_ALGORITHM_MISMATCH, 48, \ + MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONSE_FOR_CERT_MISSING, 49, \ + MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING) \ + MOZILLA_PKIX_MAP(ERROR_VALIDITY_TOO_LONG, 50, \ + MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG) \ + MOZILLA_PKIX_MAP(ERROR_REQUIRED_TLS_FEATURE_MISSING, 51, \ + MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING) \ + MOZILLA_PKIX_MAP(ERROR_INVALID_INTEGER_ENCODING, 52, \ + MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING) \ + MOZILLA_PKIX_MAP(ERROR_EMPTY_ISSUER_NAME, 53, \ + MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME) \ + MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_ARGS, FATAL_ERROR_FLAG | 1, \ + SEC_ERROR_INVALID_ARGS) \ + MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_STATE, FATAL_ERROR_FLAG | 2, \ + PR_INVALID_STATE_ERROR) \ + MOZILLA_PKIX_MAP(FATAL_ERROR_LIBRARY_FAILURE, FATAL_ERROR_FLAG | 3, \ + SEC_ERROR_LIBRARY_FAILURE) \ + MOZILLA_PKIX_MAP(FATAL_ERROR_NO_MEMORY, FATAL_ERROR_FLAG | 4, \ + SEC_ERROR_NO_MEMORY) \ + /* nothing here */ + +enum class Result +{ +#define MOZILLA_PKIX_MAP(name, value, nss_name) name = value, + MOZILLA_PKIX_MAP_LIST +#undef MOZILLA_PKIX_MAP +}; + +// Returns the stringified name of the given result, e.g. "Result::Success", +// or nullptr if result is unknown (invalid). +const char* MapResultToName(Result result); + +// We write many comparisons as (x != Success), and this shortened name makes +// those comparisons clearer, especially because the shortened name often +// results in less line wrapping. +static const Result Success = Result::Success; + +inline bool +IsFatalError(Result rv) +{ + return (static_cast<unsigned int>(rv) & FATAL_ERROR_FLAG) != 0; +} + +inline Result +NotReached(const char* /*explanation*/, Result result) +{ + assert(false); + return result; +} + +} } // namespace mozilla::pkix + +#endif // mozilla_pkix_Result_h diff --git a/security/pkix/include/pkix/Time.h b/security/pkix/include/pkix/Time.h new file mode 100644 index 000000000..d96ac82ff --- /dev/null +++ b/security/pkix/include/pkix/Time.h @@ -0,0 +1,155 @@ +/* -*- 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 2014 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_Time_h +#define mozilla_pkix_Time_h + +#include <ctime> +#include <limits> +#include <stdint.h> + +#include "pkix/Result.h" + +namespace mozilla { namespace pkix { + +// Time with a range from the first second of year 0 (AD) through at least the +// last second of year 9999, which is the range of legal times in X.509 and +// OCSP. This type has second-level precision. The time zone is always UTC. +// +// Pass by value, not by reference. +class Time final +{ +public: + // Construct an uninitialized instance. + // + // This will fail to compile because there is no default constructor: + // Time x; + // + // This will succeed, leaving the time uninitialized: + // Time x(Time::uninitialized); + enum Uninitialized { uninitialized }; + explicit Time(Uninitialized) { } + + bool operator==(const Time& other) const + { + return elapsedSecondsAD == other.elapsedSecondsAD; + } + bool operator>(const Time& other) const + { + return elapsedSecondsAD > other.elapsedSecondsAD; + } + bool operator>=(const Time& other) const + { + return elapsedSecondsAD >= other.elapsedSecondsAD; + } + bool operator<(const Time& other) const + { + return elapsedSecondsAD < other.elapsedSecondsAD; + } + bool operator<=(const Time& other) const + { + return elapsedSecondsAD <= other.elapsedSecondsAD; + } + + Result AddSeconds(uint64_t seconds) + { + if (std::numeric_limits<uint64_t>::max() - elapsedSecondsAD + < seconds) { + return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow + } + elapsedSecondsAD += seconds; + return Success; + } + + Result SubtractSeconds(uint64_t seconds) + { + if (seconds > elapsedSecondsAD) { + return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow + } + elapsedSecondsAD -= seconds; + return Success; + } + + static const uint64_t ONE_DAY_IN_SECONDS + = UINT64_C(24) * UINT64_C(60) * UINT64_C(60); + +private: + // This constructor is hidden to prevent accidents like this: + // + // Time foo(time_t t) + // { + // // WRONG! 1970-01-01-00:00:00 == time_t(0), but not Time(0)! + // return Time(t); + // } + explicit Time(uint64_t elapsedSecondsAD) + : elapsedSecondsAD(elapsedSecondsAD) + { + } + friend Time TimeFromElapsedSecondsAD(uint64_t); + friend class Duration; + + uint64_t elapsedSecondsAD; +}; + +inline Time TimeFromElapsedSecondsAD(uint64_t elapsedSecondsAD) +{ + return Time(elapsedSecondsAD); +} + +Time Now(); + +// Note the epoch is the unix epoch (ie 00:00:00 UTC, 1 January 1970) +Time TimeFromEpochInSeconds(uint64_t secondsSinceEpoch); + +class Duration final +{ +public: + Duration(Time timeA, Time timeB) + : durationInSeconds(timeA < timeB + ? timeB.elapsedSecondsAD - timeA.elapsedSecondsAD + : timeA.elapsedSecondsAD - timeB.elapsedSecondsAD) + { + } + + explicit Duration(uint64_t durationInSeconds) + : durationInSeconds(durationInSeconds) + { + } + + bool operator>(const Duration& other) const + { + return durationInSeconds > other.durationInSeconds; + } + bool operator<(const Duration& other) const + { + return durationInSeconds < other.durationInSeconds; + } + +private: + uint64_t durationInSeconds; +}; + +} } // namespace mozilla::pkix + +#endif // mozilla_pkix_Time_h diff --git a/security/pkix/include/pkix/pkix.h b/security/pkix/include/pkix/pkix.h new file mode 100644 index 000000000..da4f63615 --- /dev/null +++ b/security/pkix/include/pkix/pkix.h @@ -0,0 +1,161 @@ +/* -*- 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_pkix_h +#define mozilla_pkix_pkix_h + +#include "pkixtypes.h" + +namespace mozilla { namespace pkix { + +// ---------------------------------------------------------------------------- +// LIMITED SUPPORT FOR CERTIFICATE POLICIES +// +// If SEC_OID_X509_ANY_POLICY is passed as the value of the requiredPolicy +// parameter then all policy validation will be skipped. Otherwise, path +// building and validation will be done for the given policy. +// +// In RFC 5280 terms: +// +// * user-initial-policy-set = { requiredPolicy }. +// * initial-explicit-policy = true +// * initial-any-policy-inhibit = false +// +// We allow intermediate cerificates to use this extension but since +// we do not process the inhibit anyPolicy extesion we will fail if this +// extension is present. TODO(bug 989051) +// Because we force explicit policy and because we prohibit policy mapping, we +// do not bother processing the policy mapping, or policy constraint. +// +// ---------------------------------------------------------------------------- +// ERROR RANKING +// +// BuildCertChain prioritizes certain checks ahead of others so that when a +// certificate chain has multiple errors, the "most serious" error is +// returned. In practice, this ranking of seriousness is tied directly to how +// Firefox's certificate error override mechanism. +// +// The ranking is: +// +// 1. Active distrust (Result::ERROR_UNTRUSTED_CERT). +// 2. Problems with issuer-independent properties for CA certificates. +// 3. Unknown issuer (Result::ERROR_UNKNOWN_ISSUER). +// 4. Problems with issuer-independent properties for EE certificates. +// 5. Revocation. +// +// In particular, if BuildCertChain returns Result::ERROR_UNKNOWN_ISSUER then +// the caller can call CERT_CheckCertValidTimes to determine if the certificate +// is ALSO expired. +// +// It would be better if revocation were prioritized above expiration and +// unknown issuer. However, it is impossible to do revocation checking without +// knowing the issuer, since the issuer information is needed to validate the +// revocation information. Also, generally revocation checking only works +// during the validity period of the certificate. +// +// In general, when path building fails, BuildCertChain will return +// Result::ERROR_UNKNOWN_ISSUER. However, if all attempted paths resulted in +// the same error (which is trivially true when there is only one potential +// path), more specific errors will be returned. +// +// ---------------------------------------------------------------------------- +// Meanings of specific error codes can be found in Result.h + +// This function attempts to find a trustworthy path from the supplied +// certificate to a trust anchor. In the event that no trusted path is found, +// the method returns an error result; the error ranking is described above. +// +// Parameters: +// time: +// Timestamp for which the chain should be valid; this is useful to +// analyze whether a record was trustworthy when it was made. +// requiredKeyUsageIfPresent: +// What key usage bits must be set, if the extension is present at all, +// to be considered a valid chain. Multiple values should be OR'd +// together. If you don't want to specify anything, use +// KeyUsage::noParticularKeyUsageRequired. +// requiredEKUIfPresent: +// What extended key usage bits must be set, if the EKU extension +// exists, to be considered a valid chain. Multiple values should be +// OR'd together. If you don't want to specify anything, use +// KeyPurposeId::anyExtendedKeyUsage. +// requiredPolicy: +// This is the policy to apply; typically included in EV certificates. +// If there is no policy, pass in CertPolicyId::anyPolicy. +Result BuildCertChain(TrustDomain& trustDomain, Input cert, + Time time, EndEntityOrCA endEntityOrCA, + KeyUsage requiredKeyUsageIfPresent, + KeyPurposeId requiredEKUIfPresent, + const CertPolicyId& requiredPolicy, + /*optional*/ const Input* stapledOCSPResponse); + +// Verify that the given end-entity cert, which is assumed to have been already +// validated with BuildCertChain, is valid for the given hostname. The matching +// function attempts to implement RFC 6125 with a couple of differences: +// - IP addresses are out of scope of RFC 6125, but this method accepts them for +// backward compatibility (see SearchNames in pkixnames.cpp) +// - A wildcard in a DNS-ID may only appear as the entirety of the first label. +Result CheckCertHostname(Input cert, Input hostname, + NameMatchingPolicy& nameMatchingPolicy); + +// Construct an RFC-6960-encoded OCSP request, ready for submission to a +// responder, for the provided CertID. The request has no extensions. +static const size_t OCSP_REQUEST_MAX_LENGTH = 127; +Result CreateEncodedOCSPRequest(TrustDomain& trustDomain, + const CertID& certID, + /*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH], + /*out*/ size_t& outLen); + +// The out parameter expired will be true if the response has expired. If the +// response also indicates a revoked or unknown certificate, that error +// will be returned. Otherwise, Result::ERROR_OCSP_OLD_RESPONSE will be +// returned for an expired response. +// +// The optional parameter thisUpdate will be the thisUpdate value of +// the encoded response if it is considered trustworthy. Only +// good, unknown, or revoked responses that verify correctly are considered +// trustworthy. If the response is not trustworthy, thisUpdate will be 0. +// Similarly, the optional parameter validThrough will be the time through +// which the encoded response is considered trustworthy (that is, as long as +// the given time at which to validate is less than or equal to validThrough, +// the response will be considered trustworthy). +Result VerifyEncodedOCSPResponse(TrustDomain& trustDomain, + const CertID& certID, Time time, + uint16_t maxLifetimeInDays, + Input encodedResponse, + /* out */ bool& expired, + /* optional out */ Time* thisUpdate = nullptr, + /* optional out */ Time* validThrough = nullptr); + +// Check that the TLSFeature extensions in a given end-entity cert (which is +// assumed to have been already validated with BuildCertChain) are satisfied. +// The only feature which we cancurrently process a requirement for is +// status_request (OCSP stapling) so we reject any extension that specifies a +// requirement for another value. Empty extensions are also rejected. +Result CheckTLSFeaturesAreSatisfied(Input& cert, + const Input* stapledOCSPResponse); + +} } // namespace mozilla::pkix + +#endif // mozilla_pkix_pkix_h diff --git a/security/pkix/include/pkix/pkixnss.h b/security/pkix/include/pkix/pkixnss.h new file mode 100644 index 000000000..2650d8f68 --- /dev/null +++ b/security/pkix/include/pkix/pkixnss.h @@ -0,0 +1,109 @@ +/* -*- 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_pkixnss_h +#define mozilla_pkix_pkixnss_h + +#include "pkixtypes.h" +#include "prerror.h" +#include "seccomon.h" + +namespace mozilla { namespace pkix { + +// Verifies the PKCS#1.5 signature on the given data using the given RSA public +// key. +Result VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd, + Input subjectPublicKeyInfo, + void* pkcs11PinArg); + +// Verifies the ECDSA signature on the given data using the given ECC public +// key. +Result VerifyECDSASignedDigestNSS(const SignedDigest& sd, + Input subjectPublicKeyInfo, + void* pkcs11PinArg); + +// Computes the digest of the given data using the given digest algorithm. +// +// item contains the data to hash. +// digestBuf must point to a buffer to where the digest will be written. +// digestBufLen must be the size of the buffer, which must be exactly equal +// to the size of the digest output (20 for SHA-1, 32 for SHA-256, +// etc.) +// +// TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our +// other, extensive, memory safety efforts in mozilla::pkix, and we should find +// a way to provide a more-obviously-safe interface. +Result DigestBufNSS(Input item, + DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen); + +Result MapPRErrorCodeToResult(PRErrorCode errorCode); +PRErrorCode MapResultToPRErrorCode(Result result); + +// The error codes within each module must fit in 16 bits. We want these +// errors to fit in the same module as the NSS errors but not overlap with +// any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error +// involves negating the value of the error and then synthesizing an error +// in the NS_ERROR_MODULE_SECURITY module. Hence, PSM errors will start at +// a negative value that both doesn't overlap with the current value +// ranges for NSS errors and that will fit in 16 bits when negated. +static const PRErrorCode ERROR_BASE = -0x4000; +static const PRErrorCode ERROR_LIMIT = ERROR_BASE + 1000; + +enum ErrorCode +{ + MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = ERROR_BASE + 0, + MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY = ERROR_BASE + 1, + MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE = ERROR_BASE + 2, + MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA = ERROR_BASE + 3, + MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH = ERROR_BASE + 4, + MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = ERROR_BASE + 5, + MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = ERROR_BASE + 6, + MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH = ERROR_BASE + 7, + MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING = ERROR_BASE + 8, + MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG = ERROR_BASE + 9, + MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING = ERROR_BASE + 10, + MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING = ERROR_BASE + 11, + MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME = ERROR_BASE + 12, + END_OF_LIST +}; + +void RegisterErrorTable(); + +inline SECItem UnsafeMapInputToSECItem(Input input) +{ + SECItem result = { + siBuffer, + const_cast<uint8_t*>(input.UnsafeGetData()), + input.GetLength() + }; + static_assert(sizeof(decltype(input.GetLength())) <= sizeof(result.len), + "input.GetLength() must fit in a SECItem"); + return result; +} + +} } // namespace mozilla::pkix + +#endif // mozilla_pkix_pkixnss_h diff --git a/security/pkix/include/pkix/pkixtypes.h b/security/pkix/include/pkix/pkixtypes.h new file mode 100644 index 000000000..0a8f770a1 --- /dev/null +++ b/security/pkix/include/pkix/pkixtypes.h @@ -0,0 +1,409 @@ +/* -*- 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_pkixtypes_h +#define mozilla_pkix_pkixtypes_h + +#include "pkix/Input.h" +#include "pkix/Time.h" +#include "stdint.h" + +namespace mozilla { namespace pkix { + +enum class DigestAlgorithm +{ + sha512 = 1, + sha384 = 2, + sha256 = 3, + sha1 = 4, +}; + +enum class NamedCurve +{ + // secp521r1 (OID 1.3.132.0.35, RFC 5480) + secp521r1 = 1, + + // secp384r1 (OID 1.3.132.0.34, RFC 5480) + secp384r1 = 2, + + // secp256r1 (OID 1.2.840.10045.3.1.7, RFC 5480) + secp256r1 = 3, +}; + +struct SignedDigest final +{ + Input digest; + DigestAlgorithm digestAlgorithm; + Input signature; + + void operator=(const SignedDigest&) = delete; +}; + +enum class EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 }; + +enum class KeyUsage : uint8_t +{ + digitalSignature = 0, + nonRepudiation = 1, + keyEncipherment = 2, + dataEncipherment = 3, + keyAgreement = 4, + keyCertSign = 5, + // cRLSign = 6, + // encipherOnly = 7, + // decipherOnly = 8, + noParticularKeyUsageRequired = 0xff, +}; + +enum class KeyPurposeId +{ + anyExtendedKeyUsage = 0, + id_kp_serverAuth = 1, // id-kp-serverAuth + id_kp_clientAuth = 2, // id-kp-clientAuth + id_kp_codeSigning = 3, // id-kp-codeSigning + id_kp_emailProtection = 4, // id-kp-emailProtection + id_kp_OCSPSigning = 9, // id-kp-OCSPSigning +}; + +struct CertPolicyId final +{ + uint16_t numBytes; + static const uint16_t MAX_BYTES = 24; + uint8_t bytes[MAX_BYTES]; + + bool IsAnyPolicy() const; + + static const CertPolicyId anyPolicy; +}; + +enum class TrustLevel +{ + TrustAnchor = 1, // certificate is a trusted root CA certificate or + // equivalent *for the given policy*. + ActivelyDistrusted = 2, // certificate is known to be bad + InheritsTrust = 3 // certificate must chain to a trust anchor +}; + +// Extensions extracted during the verification flow. +// See TrustDomain::NoteAuxiliaryExtension. +enum class AuxiliaryExtension +{ + // Certificate Transparency data, specifically Signed Certificate + // Timestamps (SCTs). See RFC 6962. + + // SCT list embedded in the end entity certificate. Called by BuildCertChain + // after the certificate containing the SCTs has passed the revocation checks. + EmbeddedSCTList = 1, + // SCT list from OCSP response. Called by VerifyEncodedOCSPResponse + // when its result is a success and the SCT list is present. + SCTListFromOCSPResponse = 2 +}; + +// CertID references the information needed to do revocation checking for the +// certificate issued by the given issuer with the given serial number. +// +// issuer must be the DER-encoded issuer field from the certificate for which +// revocation checking is being done, **NOT** the subject field of the issuer +// certificate. (Those two fields must be equal to each other, but they may not +// be encoded exactly the same, and the encoding matters for OCSP.) +// issuerSubjectPublicKeyInfo is the entire DER-encoded subjectPublicKeyInfo +// field from the issuer's certificate. serialNumber is the entire DER-encoded +// serial number from the subject certificate (the certificate for which we are +// checking the revocation status). +struct CertID final +{ +public: + CertID(Input issuer, Input issuerSubjectPublicKeyInfo, Input serialNumber) + : issuer(issuer) + , issuerSubjectPublicKeyInfo(issuerSubjectPublicKeyInfo) + , serialNumber(serialNumber) + { + } + const Input issuer; + const Input issuerSubjectPublicKeyInfo; + const Input serialNumber; + + void operator=(const CertID&) = delete; +}; + +class DERArray +{ +public: + // Returns the number of DER-encoded items in the array. + virtual size_t GetLength() const = 0; + + // Returns a weak (non-owning) pointer the ith DER-encoded item in the array + // (0-indexed). The result is guaranteed to be non-null if i < GetLength(), + // and the result is guaranteed to be nullptr if i >= GetLength(). + virtual const Input* GetDER(size_t i) const = 0; +protected: + DERArray() { } + virtual ~DERArray() { } +}; + +// Applications control the behavior of path building and verification by +// implementing the TrustDomain interface. The TrustDomain is used for all +// cryptography and for determining which certificates are trusted or +// distrusted. +class TrustDomain +{ +public: + virtual ~TrustDomain() { } + + // Determine the level of trust in the given certificate for the given role. + // This will be called for every certificate encountered during path + // building. + // + // When policy.IsAnyPolicy(), then no policy-related checking should be done. + // When !policy.IsAnyPolicy(), then GetCertTrust MUST NOT return with + // trustLevel == TrustAnchor unless the given cert is considered a trust + // anchor *for that policy*. In particular, if the user has marked an + // intermediate certificate as trusted, but that intermediate isn't in the + // list of EV roots, then GetCertTrust must result in + // trustLevel == InheritsTrust instead of trustLevel == TrustAnchor + // (assuming the candidate cert is not actively distrusted). + virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA, + const CertPolicyId& policy, + Input candidateCertDER, + /*out*/ TrustLevel& trustLevel) = 0; + + class IssuerChecker + { + public: + // potentialIssuerDER is the complete DER encoding of the certificate to + // be checked as a potential issuer. + // + // If additionalNameConstraints is not nullptr then it must point to an + // encoded NameConstraints extension value; in that case, those name + // constraints will be checked in addition to any any name constraints + // contained in potentialIssuerDER. + virtual Result Check(Input potentialIssuerDER, + /*optional*/ const Input* additionalNameConstraints, + /*out*/ bool& keepGoing) = 0; + protected: + IssuerChecker(); + virtual ~IssuerChecker(); + + IssuerChecker(const IssuerChecker&) = delete; + void operator=(const IssuerChecker&) = delete; + }; + + // Search for a CA certificate with the given name. The implementation must + // call checker.Check with the DER encoding of the potential issuer + // certificate. The implementation must follow these rules: + // + // * The implementation must be reentrant and must limit the amount of stack + // space it uses; see the note on reentrancy and stack usage below. + // * When checker.Check does not return Success then immediately return its + // return value. + // * When checker.Check returns Success and sets keepGoing = false, then + // immediately return Success. + // * When checker.Check returns Success and sets keepGoing = true, then + // call checker.Check again with a different potential issuer certificate, + // if any more are available. + // * When no more potential issuer certificates are available, return + // Success. + // * Don't call checker.Check with the same potential issuer certificate more + // than once in a given call of FindIssuer. + // * The given time parameter may be used to filter out certificates that are + // not valid at the given time, or it may be ignored. + // + // Note on reentrancy and stack usage: checker.Check will attempt to + // recursively build a certificate path from the potential issuer it is given + // to a trusted root, as determined by this TrustDomain. That means that + // checker.Check may call any/all of the methods on this TrustDomain. In + // particular, there will be call stacks that look like this: + // + // BuildCertChain + // [...] + // TrustDomain::FindIssuer + // [...] + // IssuerChecker::Check + // [...] + // TrustDomain::FindIssuer + // [...] + // IssuerChecker::Check + // [...] + // + // checker.Check is responsible for limiting the recursion to a reasonable + // limit. + // + // checker.Check will verify that the subject's issuer field matches the + // potential issuer's subject field. It will also check that the potential + // issuer is valid at the given time. However, if the FindIssuer + // implementation has an efficient way of filtering potential issuers by name + // and/or validity period itself, then it is probably better for performance + // for it to do so. + virtual Result FindIssuer(Input encodedIssuerName, + IssuerChecker& checker, Time time) = 0; + + // Called as soon as we think we have a valid chain but before revocation + // checks are done. This function can be used to compute additional checks, + // especially checks that require the entire certificate chain. This callback + // can also be used to save a copy of the built certificate chain for later + // use. + // + // This function may be called multiple times, regardless of whether it + // returns success or failure. It is guaranteed that BuildCertChain will not + // return Success unless the last call to IsChainValid returns Success. Further, + // it is guaranteed that when BuildCertChain returns Success the last chain + // passed to IsChainValid is the valid chain that should be used for further + // operations that require the whole chain. + // + // Keep in mind, in particular, that if the application saves a copy of the + // certificate chain the last invocation of IsChainValid during a validation, + // it is still possible for BuildCertChain to fail, in which case the + // application must not assume anything about the validity of the last + // certificate chain passed to IsChainValid; especially, it would be very + // wrong to assume that the certificate chain is valid. + // + // certChain.GetDER(0) is the trust anchor. + virtual Result IsChainValid(const DERArray& certChain, Time time) = 0; + + virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA, + const CertID& certID, Time time, + Duration validityDuration, + /*optional*/ const Input* stapledOCSPresponse, + /*optional*/ const Input* aiaExtension) = 0; + + // Check that the given digest algorithm is acceptable for use in signatures. + // + // Return Success if the algorithm is acceptable, + // Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED if the algorithm is not + // acceptable, or another error code if another error occurred. + virtual Result CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg, + EndEntityOrCA endEntityOrCA, + Time notBefore) = 0; + + // Check that the RSA public key size is acceptable. + // + // Return Success if the key size is acceptable, + // Result::ERROR_INADEQUATE_KEY_SIZE if the key size is not acceptable, + // or another error code if another error occurred. + virtual Result CheckRSAPublicKeyModulusSizeInBits( + EndEntityOrCA endEntityOrCA, + unsigned int modulusSizeInBits) = 0; + + // Verify the given RSA PKCS#1.5 signature on the given digest using the + // given RSA public key. + // + // CheckRSAPublicKeyModulusSizeInBits will be called before calling this + // function, so it is not necessary to repeat those checks here. However, + // VerifyRSAPKCS1SignedDigest *is* responsible for doing the mathematical + // verification of the public key validity as specified in NIST SP 800-56A. + virtual Result VerifyRSAPKCS1SignedDigest( + const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) = 0; + + // Check that the given named ECC curve is acceptable for ECDSA signatures. + // + // Return Success if the curve is acceptable, + // Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE if the curve is not acceptable, + // or another error code if another error occurred. + virtual Result CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA, + NamedCurve curve) = 0; + + // Verify the given ECDSA signature on the given digest using the given ECC + // public key. + // + // CheckECDSACurveIsAcceptable will be called before calling this function, + // so it is not necessary to repeat that check here. However, + // VerifyECDSASignedDigest *is* responsible for doing the mathematical + // verification of the public key validity as specified in NIST SP 800-56A. + virtual Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) = 0; + + // Check that the validity duration is acceptable. + // + // Return Success if the validity duration is acceptable, + // Result::ERROR_VALIDITY_TOO_LONG if the validity duration is not acceptable, + // or another error code if another error occurred. + virtual Result CheckValidityIsAcceptable(Time notBefore, Time notAfter, + EndEntityOrCA endEntityOrCA, + KeyPurposeId keyPurpose) = 0; + + // For compatibility, a CA certificate with an extended key usage that + // contains the id-Netscape-stepUp OID but does not contain the + // id-kp-serverAuth OID may be considered valid for issuing server auth + // certificates. This function allows TrustDomain implementations to control + // this setting based on the start of the validity period of the certificate + // in question. + virtual Result NetscapeStepUpMatchesServerAuth(Time notBefore, + /*out*/ bool& matches) = 0; + + // Some certificate or OCSP response extensions do not directly participate + // in the verification flow, but might still be of interest to the clients + // (notably Certificate Transparency data, RFC 6962). Such extensions are + // extracted and passed to this function for further processing. + virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension, + Input extensionData) = 0; + + // Compute a digest of the data in item using the given digest algorithm. + // + // item contains the data to hash. + // digestBuf points to a buffer to where the digest will be written. + // digestBufLen will be the size of the digest output (20 for SHA-1, + // 32 for SHA-256, etc.). + // + // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our + // other, extensive, memory safety efforts in mozilla::pkix, and we should + // find a way to provide a more-obviously-safe interface. + virtual Result DigestBuf(Input item, + DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) = 0; +protected: + TrustDomain() { } + + TrustDomain(const TrustDomain&) = delete; + void operator=(const TrustDomain&) = delete; +}; + +enum class FallBackToSearchWithinSubject { No = 0, Yes = 1 }; + +// Applications control the behavior of matching presented name information from +// a certificate against a reference hostname by implementing the +// NameMatchingPolicy interface. Used in concert with CheckCertHostname. +class NameMatchingPolicy +{ +public: + virtual ~NameMatchingPolicy() { } + + // Given that the certificate in question has a notBefore field with the given + // value, should name matching fall back to searching within the subject + // common name field? + virtual Result FallBackToCommonName( + Time notBefore, + /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) = 0; + +protected: + NameMatchingPolicy() { } + + NameMatchingPolicy(const NameMatchingPolicy&) = delete; + void operator=(const NameMatchingPolicy&) = delete; +}; + +} } // namespace mozilla::pkix + +#endif // mozilla_pkix_pkixtypes_h |