summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/mozpkix/include
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/mozpkix/include')
-rw-r--r--security/nss/lib/mozpkix/include/pkix-test/pkixtestnss.h48
-rw-r--r--security/nss/lib/mozpkix/include/pkix-test/pkixtestutil.h406
-rw-r--r--security/nss/lib/mozpkix/include/pkix/Input.h310
-rw-r--r--security/nss/lib/mozpkix/include/pkix/Result.h219
-rw-r--r--security/nss/lib/mozpkix/include/pkix/Time.h137
-rw-r--r--security/nss/lib/mozpkix/include/pkix/pkix.h160
-rw-r--r--security/nss/lib/mozpkix/include/pkix/pkixcheck.h65
-rw-r--r--security/nss/lib/mozpkix/include/pkix/pkixder.h520
-rw-r--r--security/nss/lib/mozpkix/include/pkix/pkixnss.h106
-rw-r--r--security/nss/lib/mozpkix/include/pkix/pkixtypes.h400
-rw-r--r--security/nss/lib/mozpkix/include/pkix/pkixutil.h265
11 files changed, 2636 insertions, 0 deletions
diff --git a/security/nss/lib/mozpkix/include/pkix-test/pkixtestnss.h b/security/nss/lib/mozpkix/include/pkix-test/pkixtestnss.h
new file mode 100644
index 000000000..5ae776f6a
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix-test/pkixtestnss.h
@@ -0,0 +1,48 @@
+/* -*- 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 2018 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.
+ */
+
+// This file provides some implementation-specific test utilities. This is only
+// necessary because some PSM xpcshell test utilities overlap in functionality
+// with these test utilities, so the underlying implementation is shared.
+
+#ifndef mozilla_pkix_test_pkixtestnss_h
+#define mozilla_pkix_test_pkixtestnss_h
+
+#include <keyhi.h>
+#include <keythi.h>
+#include "mozpkix/nss_scoped_ptrs.h"
+#include "mozpkix/test/pkixtestutil.h"
+
+namespace mozilla {
+namespace pkix {
+namespace test {
+
+TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
+ const ScopedSECKEYPublicKey& publicKey,
+ const ScopedSECKEYPrivateKey& privateKey);
+}
+}
+} // namespace mozilla::pkix::test
+
+#endif // mozilla_pkix_test_pkixtestnss_h
diff --git a/security/nss/lib/mozpkix/include/pkix-test/pkixtestutil.h b/security/nss/lib/mozpkix/include/pkix-test/pkixtestutil.h
new file mode 100644
index 000000000..55c435419
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix-test/pkixtestutil.h
@@ -0,0 +1,406 @@
+/* -*- 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_test_pkixtestutil_h
+#define mozilla_pkix_test_pkixtestutil_h
+
+#include <cstdint>
+#include <cstring>
+#include <ctime>
+#include <string>
+
+#include "mozpkix/pkixtypes.h"
+
+namespace mozilla {
+namespace pkix {
+namespace test {
+
+typedef std::basic_string<uint8_t> ByteString;
+
+inline bool ENCODING_FAILED(const ByteString& bs) { return bs.empty(); }
+
+template <size_t L>
+inline ByteString BytesToByteString(const uint8_t (&bytes)[L]) {
+ return ByteString(bytes, L);
+}
+
+// XXX: Ideally, we should define this instead:
+//
+// template <typename T, std::size_t N>
+// constexpr inline std::size_t
+// ArrayLength(T (&)[N])
+// {
+// return N;
+// }
+//
+// However, we don't because not all supported compilers support constexpr,
+// and we need to calculate array lengths in static_assert sometimes.
+//
+// XXX: Evaluates its argument twice
+#define MOZILLA_PKIX_ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))
+
+bool InputEqualsByteString(Input input, const ByteString& bs);
+ByteString InputToByteString(Input input);
+
+// python DottedOIDToCode.py --tlv id-kp-OCSPSigning 1.3.6.1.5.5.7.3.9
+static const uint8_t tlv_id_kp_OCSPSigning[] = {0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x03, 0x09};
+
+// python DottedOIDToCode.py --tlv id-kp-serverAuth 1.3.6.1.5.5.7.3.1
+static const uint8_t tlv_id_kp_serverAuth[] = {0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x03, 0x01};
+
+enum class TestDigestAlgorithmID {
+ MD2,
+ MD5,
+ SHA1,
+ SHA224,
+ SHA256,
+ SHA384,
+ SHA512,
+};
+
+struct TestPublicKeyAlgorithm {
+ explicit TestPublicKeyAlgorithm(const ByteString& aAlgorithmIdentifier)
+ : algorithmIdentifier(aAlgorithmIdentifier) {}
+ bool operator==(const TestPublicKeyAlgorithm& other) const {
+ return algorithmIdentifier == other.algorithmIdentifier;
+ }
+ ByteString algorithmIdentifier;
+};
+
+ByteString DSS_P();
+ByteString DSS_Q();
+ByteString DSS_G();
+
+TestPublicKeyAlgorithm DSS();
+TestPublicKeyAlgorithm RSA_PKCS1();
+
+struct TestSignatureAlgorithm {
+ TestSignatureAlgorithm(const TestPublicKeyAlgorithm& publicKeyAlg,
+ TestDigestAlgorithmID digestAlg,
+ const ByteString& algorithmIdentifier, bool accepted);
+
+ TestPublicKeyAlgorithm publicKeyAlg;
+ TestDigestAlgorithmID digestAlg;
+ ByteString algorithmIdentifier;
+ bool accepted;
+};
+
+TestSignatureAlgorithm md2WithRSAEncryption();
+TestSignatureAlgorithm md5WithRSAEncryption();
+TestSignatureAlgorithm sha1WithRSAEncryption();
+TestSignatureAlgorithm sha256WithRSAEncryption();
+
+// e.g. YMDHMS(2016, 12, 31, 1, 23, 45) => 2016-12-31:01:23:45 (GMT)
+mozilla::pkix::Time YMDHMS(uint16_t year, uint16_t month, uint16_t day,
+ uint16_t hour, uint16_t minutes, uint16_t seconds);
+
+ByteString TLV(uint8_t tag, size_t length, const ByteString& value);
+
+inline ByteString TLV(uint8_t tag, const ByteString& value) {
+ return TLV(tag, value.length(), value);
+}
+
+// Although we can't enforce it without relying on Cuser-defined literals,
+// which aren't supported by all of our compilers yet, you should only pass
+// string literals as the last parameter to the following two functions.
+
+template <size_t N>
+inline ByteString TLV(uint8_t tag, const char (&value)[N]) {
+ static_assert(N > 0, "cannot have string literal of size 0");
+ assert(value[N - 1] == 0);
+ return TLV(tag, ByteString(reinterpret_cast<const uint8_t*>(&value), N - 1));
+}
+
+template <size_t N>
+inline ByteString TLV(uint8_t tag, size_t length, const char (&value)[N]) {
+ static_assert(N > 0, "cannot have string literal of size 0");
+ assert(value[N - 1] == 0);
+ return TLV(tag, length,
+ ByteString(reinterpret_cast<const uint8_t*>(&value), N - 1));
+}
+
+ByteString Boolean(bool value);
+ByteString Integer(long value);
+
+ByteString CN(const ByteString&, uint8_t encodingTag = 0x0c /*UTF8String*/);
+
+inline ByteString CN(const char* value,
+ uint8_t encodingTag = 0x0c /*UTF8String*/) {
+ return CN(
+ ByteString(reinterpret_cast<const uint8_t*>(value), std::strlen(value)),
+ encodingTag);
+}
+
+ByteString OU(const ByteString&, uint8_t encodingTag = 0x0c /*UTF8String*/);
+
+inline ByteString OU(const char* value,
+ uint8_t encodingTag = 0x0c /*UTF8String*/) {
+ return OU(
+ ByteString(reinterpret_cast<const uint8_t*>(value), std::strlen(value)),
+ encodingTag);
+}
+
+ByteString emailAddress(const ByteString&);
+
+inline ByteString emailAddress(const char* value) {
+ return emailAddress(
+ ByteString(reinterpret_cast<const uint8_t*>(value), std::strlen(value)));
+}
+
+// RelativeDistinguishedName ::=
+// SET SIZE (1..MAX) OF AttributeTypeAndValue
+//
+ByteString RDN(const ByteString& avas);
+
+// Name ::= CHOICE { -- only one possibility for now --
+// rdnSequence RDNSequence }
+//
+// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+//
+ByteString Name(const ByteString& rdns);
+
+inline ByteString CNToDERName(const ByteString& cn) {
+ return Name(RDN(CN(cn)));
+}
+
+inline ByteString CNToDERName(const char* cn) { return Name(RDN(CN(cn))); }
+
+// GeneralName ::= CHOICE {
+// otherName [0] OtherName,
+// rfc822Name [1] IA5String,
+// dNSName [2] IA5String,
+// x400Address [3] ORAddress,
+// directoryName [4] Name,
+// ediPartyName [5] EDIPartyName,
+// uniformResourceIdentifier [6] IA5String,
+// iPAddress [7] OCTET STRING,
+// registeredID [8] OBJECT IDENTIFIER }
+
+inline ByteString RFC822Name(const ByteString& name) {
+ // (2 << 6) means "context-specific", 1 is the GeneralName tag.
+ return TLV((2 << 6) | 1, name);
+}
+
+template <size_t L>
+inline ByteString RFC822Name(const char (&bytes)[L]) {
+ return RFC822Name(
+ ByteString(reinterpret_cast<const uint8_t*>(&bytes), L - 1));
+}
+
+inline ByteString DNSName(const ByteString& name) {
+ // (2 << 6) means "context-specific", 2 is the GeneralName tag.
+ return TLV((2 << 6) | 2, name);
+}
+
+template <size_t L>
+inline ByteString DNSName(const char (&bytes)[L]) {
+ return DNSName(ByteString(reinterpret_cast<const uint8_t*>(&bytes), L - 1));
+}
+
+inline ByteString DirectoryName(const ByteString& name) {
+ // (2 << 6) means "context-specific", (1 << 5) means "constructed", and 4 is
+ // the DirectoryName tag.
+ return TLV((2 << 6) | (1 << 5) | 4, name);
+}
+
+inline ByteString IPAddress() {
+ // (2 << 6) means "context-specific", 7 is the GeneralName tag.
+ return TLV((2 << 6) | 7, ByteString());
+}
+
+template <size_t L>
+inline ByteString IPAddress(const uint8_t (&bytes)[L]) {
+ // (2 << 6) means "context-specific", 7 is the GeneralName tag.
+ return TLV((2 << 6) | 7, ByteString(bytes, L));
+}
+
+// Names should be zero or more GeneralNames, like DNSName and IPAddress return,
+// concatenated together.
+//
+// CreatedEncodedSubjectAltName(ByteString()) results in a SAN with an empty
+// sequence. CreateEmptyEncodedSubjectName() results in a SAN without any
+// sequence.
+ByteString CreateEncodedSubjectAltName(const ByteString& names);
+ByteString CreateEncodedEmptySubjectAltName();
+
+class TestKeyPair {
+ public:
+ virtual ~TestKeyPair() {}
+
+ const TestPublicKeyAlgorithm publicKeyAlg;
+
+ // The DER encoding of the entire SubjectPublicKeyInfo structure. This is
+ // what is encoded in certificates.
+ const ByteString subjectPublicKeyInfo;
+
+ // The DER encoding of subjectPublicKeyInfo.subjectPublicKey. This is what is
+ // hashed to create CertIDs for OCSP.
+ const ByteString subjectPublicKey;
+
+ virtual Result SignData(const ByteString& tbs,
+ const TestSignatureAlgorithm& signatureAlgorithm,
+ /*out*/ ByteString& signature) const = 0;
+
+ virtual TestKeyPair* Clone() const = 0;
+
+ protected:
+ TestKeyPair(const TestPublicKeyAlgorithm& publicKeyAlg,
+ const ByteString& spk);
+ TestKeyPair(const TestKeyPair&) = delete;
+ void operator=(const TestKeyPair&) = delete;
+};
+
+TestKeyPair* CloneReusedKeyPair();
+TestKeyPair* GenerateKeyPair();
+TestKeyPair* GenerateDSSKeyPair();
+inline void DeleteTestKeyPair(TestKeyPair* keyPair) { delete keyPair; }
+typedef std::unique_ptr<TestKeyPair> ScopedTestKeyPair;
+
+Result TestVerifyECDSASignedDigest(const SignedDigest& signedDigest,
+ Input subjectPublicKeyInfo);
+Result TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+ Input subjectPublicKeyInfo);
+Result TestDigestBuf(Input item, DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf, size_t digestBufLen);
+
+// Replace one substring in item with another of the same length, but only if
+// the substring was found exactly once. The "same length" restriction is
+// useful for avoiding invalidating lengths encoded within the item. The
+// "only once" restriction is helpful for avoiding making accidental changes.
+//
+// The string to search for must be 8 or more bytes long so that it is
+// extremely unlikely that there will ever be any false positive matches
+// in digital signatures, keys, hashes, etc.
+Result TamperOnce(/*in/out*/ ByteString& item, const ByteString& from,
+ const ByteString& to);
+
+///////////////////////////////////////////////////////////////////////////////
+// Encode Certificates
+
+enum Version { v1 = 0, v2 = 1, v3 = 2 };
+
+// signature is assumed to be the DER encoding of an AlgorithmIdentifer. It is
+// put into the signature field of the TBSCertificate. In most cases, it will
+// be the same as signatureAlgorithm, which is the algorithm actually used
+// to sign the certificate.
+// serialNumber is assumed to be the DER encoding of an INTEGER.
+//
+// If extensions is null, then no extensions will be encoded. Otherwise,
+// extensions must point to an array of ByteStrings, terminated with an empty
+// ByteString. (If the first item of the array is empty then an empty
+// Extensions sequence will be encoded.)
+ByteString CreateEncodedCertificate(
+ long version, const TestSignatureAlgorithm& signature,
+ const ByteString& serialNumber, const ByteString& issuerNameDER,
+ time_t notBefore, time_t notAfter, const ByteString& subjectNameDER,
+ const TestKeyPair& subjectKeyPair,
+ /*optional*/ const ByteString* extensions, const TestKeyPair& issuerKeyPair,
+ const TestSignatureAlgorithm& signatureAlgorithm);
+
+ByteString CreateEncodedSerialNumber(long value);
+
+enum class Critical { No = 0, Yes = 1 };
+
+ByteString CreateEncodedBasicConstraints(
+ bool isCA,
+ /*optional in*/ const long* pathLenConstraint, Critical critical);
+
+// Creates a DER-encoded extKeyUsage extension with one EKU OID.
+ByteString CreateEncodedEKUExtension(Input eku, Critical critical);
+
+///////////////////////////////////////////////////////////////////////////////
+// Encode OCSP responses
+
+class OCSPResponseExtension final {
+ public:
+ OCSPResponseExtension();
+
+ ByteString id;
+ bool critical;
+ ByteString value;
+ OCSPResponseExtension* next;
+};
+
+class OCSPResponseContext final {
+ public:
+ OCSPResponseContext(const CertID& certID, std::time_t time);
+
+ const CertID& certID;
+ // TODO(bug 980538): add a way to specify what certificates are included.
+
+ // The fields below are in the order that they appear in an OCSP response.
+
+ enum OCSPResponseStatus {
+ successful = 0,
+ malformedRequest = 1,
+ internalError = 2,
+ tryLater = 3,
+ // 4 is not used
+ sigRequired = 5,
+ unauthorized = 6,
+ };
+ uint8_t responseStatus; // an OCSPResponseStatus or an invalid value
+ bool skipResponseBytes; // If true, don't include responseBytes
+
+ // responderID
+ ByteString signerNameDER; // If set, responderID will use the byName
+ // form; otherwise responderID will use the
+ // byKeyHash form.
+
+ std::time_t producedAt;
+
+ // SingleResponse extensions (for the certID given in the constructor).
+ OCSPResponseExtension* singleExtensions;
+ // ResponseData extensions.
+ OCSPResponseExtension* responseExtensions;
+ bool includeEmptyExtensions; // If true, include the extension wrapper
+ // regardless of if there are any actual
+ // extensions.
+ ScopedTestKeyPair signerKeyPair;
+ TestSignatureAlgorithm signatureAlgorithm;
+ bool badSignature; // If true, alter the signature to fail verification
+ const ByteString* certs; // optional; array terminated by an empty string
+
+ // The following fields are on a per-SingleResponse basis. In the future we
+ // may support including multiple SingleResponses per response.
+ enum CertStatus {
+ good = 0,
+ revoked = 1,
+ unknown = 2,
+ };
+ uint8_t certStatus; // CertStatus or an invalid value
+ std::time_t revocationTime; // For certStatus == revoked
+ std::time_t thisUpdate;
+ std::time_t nextUpdate;
+ bool includeNextUpdate;
+};
+
+ByteString CreateEncodedOCSPResponse(OCSPResponseContext& context);
+}
+}
+} // namespace mozilla::pkix::test
+
+#endif // mozilla_pkix_test_pkixtestutil_h
diff --git a/security/nss/lib/mozpkix/include/pkix/Input.h b/security/nss/lib/mozpkix/include/pkix/Input.h
new file mode 100644
index 000000000..11b2a0f7e
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix/Input.h
@@ -0,0 +1,310 @@
+/* -*- 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 "mozpkix/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 (&aData)[N]) : data(aData), 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* aData, size_t aLen) {
+ if (this->data) {
+ // already initialized
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ if (!aData || aLen > 0xffffu) {
+ // input too large
+ return Result::ERROR_BAD_DER;
+ }
+
+ this->data = aData;
+ this->len = aLen;
+
+ 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 aInput)
+ : input(aInput.UnsafeGetData()),
+ end(aInput.UnsafeGetData() + aInput.GetLength()) {}
+
+ Result Init(Input aInput) {
+ if (this->input) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ this->input = aInput.UnsafeGetData();
+ this->end = aInput.UnsafeGetData() + aInput.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& aInput, const uint8_t* aMark)
+ : input(aInput), mark(aMark) {}
+ 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/nss/lib/mozpkix/include/pkix/Result.h b/security/nss/lib/mozpkix/include/pkix/Result.h
new file mode 100644
index 000000000..29461dc1a
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix/Result.h
@@ -0,0 +1,219 @@
+/* -*- 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(ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED, 54, \
+ MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED) \
+ MOZILLA_PKIX_MAP(ERROR_SELF_SIGNED_CERT, 55, \
+ MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT) \
+ MOZILLA_PKIX_MAP(ERROR_MITM_DETECTED, 56, MOZILLA_PKIX_ERROR_MITM_DETECTED) \
+ 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/nss/lib/mozpkix/include/pkix/Time.h b/security/nss/lib/mozpkix/include/pkix/Time.h
new file mode 100644
index 000000000..8aea5479b
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix/Time.h
@@ -0,0 +1,137 @@
+/* -*- 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 <stdint.h>
+#include <ctime>
+#include <limits>
+
+#include "mozpkix/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 aElapsedSecondsAD)
+ : elapsedSecondsAD(aElapsedSecondsAD) {}
+ friend Time TimeFromElapsedSecondsAD(uint64_t);
+ friend class Duration;
+
+ uint64_t elapsedSecondsAD;
+};
+
+inline Time TimeFromElapsedSecondsAD(uint64_t aElapsedSecondsAD) {
+ return Time(aElapsedSecondsAD);
+}
+
+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 aDurationInSeconds)
+ : durationInSeconds(aDurationInSeconds) {}
+
+ 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/nss/lib/mozpkix/include/pkix/pkix.h b/security/nss/lib/mozpkix/include/pkix/pkix.h
new file mode 100644
index 000000000..1cd6548e4
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix/pkix.h
@@ -0,0 +1,160 @@
+/* -*- 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 "mozpkix/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/nss/lib/mozpkix/include/pkix/pkixcheck.h b/security/nss/lib/mozpkix/include/pkix/pkixcheck.h
new file mode 100644
index 000000000..e04780e57
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix/pkixcheck.h
@@ -0,0 +1,65 @@
+/* -*- 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_pkixcheck_h
+#define mozilla_pkix_pkixcheck_h
+
+#include "mozpkix/pkixtypes.h"
+
+namespace mozilla {
+namespace pkix {
+
+class BackCert;
+
+Result CheckIssuerIndependentProperties(TrustDomain& trustDomain,
+ const BackCert& cert, Time time,
+ KeyUsage requiredKeyUsageIfPresent,
+ KeyPurposeId requiredEKUIfPresent,
+ const CertPolicyId& requiredPolicy,
+ unsigned int subCACount,
+ /*out*/ TrustLevel& trustLevel);
+
+Result CheckNameConstraints(Input encodedNameConstraints,
+ const BackCert& firstChild,
+ KeyPurposeId requiredEKUIfPresent);
+
+Result CheckIssuer(Input encodedIssuer);
+
+// ParseValidity and CheckValidity are usually used together. First you parse
+// the dates from the DER Validity sequence, then you compare them to the time
+// at which you are validating. They are separate so that the notBefore and
+// notAfter times can be used for other things before they are checked against
+// the time of validation.
+Result ParseValidity(Input encodedValidity,
+ /*optional out*/ Time* notBeforeOut = nullptr,
+ /*optional out*/ Time* notAfterOut = nullptr);
+Result CheckValidity(Time time, Time notBefore, Time notAfter);
+
+// Check that a subject has TLS Feature (rfc7633) requirements that match its
+// potential issuer
+Result CheckTLSFeatures(const BackCert& subject, BackCert& potentialIssuer);
+}
+} // namespace mozilla::pkix
+
+#endif // mozilla_pkix_pkixcheck_h
diff --git a/security/nss/lib/mozpkix/include/pkix/pkixder.h b/security/nss/lib/mozpkix/include/pkix/pkixder.h
new file mode 100644
index 000000000..3aae0ecf6
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix/pkixder.h
@@ -0,0 +1,520 @@
+/* -*- 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_pkixder_h
+#define mozilla_pkix_pkixder_h
+
+// Expect* functions advance the input mark and return Success if the input
+// matches the given criteria; they fail with the input mark in an undefined
+// state if the input does not match the criteria.
+//
+// Match* functions advance the input mark and return true if the input matches
+// the given criteria; they return false without changing the input mark if the
+// input does not match the criteria.
+//
+// Skip* functions unconditionally advance the input mark and return Success if
+// they are able to do so; otherwise they fail with the input mark in an
+// undefined state.
+
+#include "mozpkix/Input.h"
+#include "mozpkix/pkixtypes.h"
+
+namespace mozilla {
+namespace pkix {
+namespace der {
+
+enum Class : uint8_t {
+ UNIVERSAL = 0 << 6,
+ // APPLICATION = 1 << 6, // unused
+ CONTEXT_SPECIFIC = 2 << 6,
+ // PRIVATE = 3 << 6 // unused
+};
+
+enum Constructed { CONSTRUCTED = 1 << 5 };
+
+enum Tag : uint8_t {
+ BOOLEAN = UNIVERSAL | 0x01,
+ INTEGER = UNIVERSAL | 0x02,
+ BIT_STRING = UNIVERSAL | 0x03,
+ OCTET_STRING = UNIVERSAL | 0x04,
+ NULLTag = UNIVERSAL | 0x05,
+ OIDTag = UNIVERSAL | 0x06,
+ ENUMERATED = UNIVERSAL | 0x0a,
+ UTF8String = UNIVERSAL | 0x0c,
+ SEQUENCE = UNIVERSAL | CONSTRUCTED | 0x10, // 0x30
+ SET = UNIVERSAL | CONSTRUCTED | 0x11, // 0x31
+ PrintableString = UNIVERSAL | 0x13,
+ TeletexString = UNIVERSAL | 0x14,
+ IA5String = UNIVERSAL | 0x16,
+ UTCTime = UNIVERSAL | 0x17,
+ GENERALIZED_TIME = UNIVERSAL | 0x18,
+};
+
+enum class EmptyAllowed { No = 0, Yes = 1 };
+
+Result ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag,
+ /*out*/ Input& value);
+Result End(Reader& input);
+
+inline Result ExpectTagAndGetValue(Reader& input, uint8_t tag,
+ /*out*/ Input& value) {
+ uint8_t actualTag;
+ Result rv = ReadTagAndGetValue(input, actualTag, value);
+ if (rv != Success) {
+ return rv;
+ }
+ if (tag != actualTag) {
+ return Result::ERROR_BAD_DER;
+ }
+ return Success;
+}
+
+inline Result ExpectTagAndGetValue(Reader& input, uint8_t tag,
+ /*out*/ Reader& value) {
+ Input valueInput;
+ Result rv = ExpectTagAndGetValue(input, tag, valueInput);
+ if (rv != Success) {
+ return rv;
+ }
+ return value.Init(valueInput);
+}
+
+inline Result ExpectTagAndEmptyValue(Reader& input, uint8_t tag) {
+ Reader value;
+ Result rv = ExpectTagAndGetValue(input, tag, value);
+ if (rv != Success) {
+ return rv;
+ }
+ return End(value);
+}
+
+inline Result ExpectTagAndSkipValue(Reader& input, uint8_t tag) {
+ Input ignoredValue;
+ return ExpectTagAndGetValue(input, tag, ignoredValue);
+}
+
+// Like ExpectTagAndGetValue, except the output Input will contain the
+// encoded tag and length along with the value.
+inline Result ExpectTagAndGetTLV(Reader& input, uint8_t tag,
+ /*out*/ Input& tlv) {
+ Reader::Mark mark(input.GetMark());
+ Result rv = ExpectTagAndSkipValue(input, tag);
+ if (rv != Success) {
+ return rv;
+ }
+ return input.GetInput(mark, tlv);
+}
+
+inline Result End(Reader& input) {
+ if (!input.AtEnd()) {
+ return Result::ERROR_BAD_DER;
+ }
+
+ return Success;
+}
+
+template <typename Decoder>
+inline Result Nested(Reader& input, uint8_t tag, Decoder decoder) {
+ Reader nested;
+ Result rv = ExpectTagAndGetValue(input, tag, nested);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = decoder(nested);
+ if (rv != Success) {
+ return rv;
+ }
+ return End(nested);
+}
+
+template <typename Decoder>
+inline Result Nested(Reader& input, uint8_t outerTag, uint8_t innerTag,
+ Decoder decoder) {
+ Reader nestedInput;
+ Result rv = ExpectTagAndGetValue(input, outerTag, nestedInput);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = Nested(nestedInput, innerTag, decoder);
+ if (rv != Success) {
+ return rv;
+ }
+ return End(nestedInput);
+}
+
+// This can be used to decode constructs like this:
+//
+// ...
+// foos SEQUENCE OF Foo,
+// ...
+// Foo ::= SEQUENCE {
+// }
+//
+// using code like this:
+//
+// Result Foo(Reader& r) { /*...*/ }
+//
+// rv = der::NestedOf(input, der::SEQEUENCE, der::SEQUENCE, Foo);
+//
+// or:
+//
+// Result Bar(Reader& r, int value) { /*...*/ }
+//
+// int value = /*...*/;
+//
+// rv = der::NestedOf(input, der::SEQUENCE, [value](Reader& r) {
+// return Bar(r, value);
+// });
+//
+// In these examples the function will get called once for each element of
+// foos.
+//
+template <typename Decoder>
+inline Result NestedOf(Reader& input, uint8_t outerTag, uint8_t innerTag,
+ EmptyAllowed mayBeEmpty, Decoder decoder) {
+ Reader inner;
+ Result rv = ExpectTagAndGetValue(input, outerTag, inner);
+ if (rv != Success) {
+ return rv;
+ }
+
+ if (inner.AtEnd()) {
+ if (mayBeEmpty != EmptyAllowed::Yes) {
+ return Result::ERROR_BAD_DER;
+ }
+ return Success;
+ }
+
+ do {
+ rv = Nested(inner, innerTag, decoder);
+ if (rv != Success) {
+ return rv;
+ }
+ } while (!inner.AtEnd());
+
+ return Success;
+}
+
+// Often, a function will need to decode an Input or Reader that contains
+// DER-encoded data wrapped in a SEQUENCE (or similar) with nothing after it.
+// This function reduces the boilerplate necessary for stripping the outermost
+// SEQUENCE (or similar) and ensuring that nothing follows it.
+inline Result ExpectTagAndGetValueAtEnd(Reader& outer, uint8_t expectedTag,
+ /*out*/ Reader& inner) {
+ Result rv = der::ExpectTagAndGetValue(outer, expectedTag, inner);
+ if (rv != Success) {
+ return rv;
+ }
+ return der::End(outer);
+}
+
+// Similar to the above, but takes an Input instead of a Reader&.
+inline Result ExpectTagAndGetValueAtEnd(Input outer, uint8_t expectedTag,
+ /*out*/ Reader& inner) {
+ Reader outerReader(outer);
+ return ExpectTagAndGetValueAtEnd(outerReader, expectedTag, inner);
+}
+
+// Universal types
+
+namespace internal {
+
+enum class IntegralValueRestriction {
+ NoRestriction,
+ MustBePositive,
+ MustBe0To127,
+};
+
+Result IntegralBytes(
+ Reader& input, uint8_t tag, IntegralValueRestriction valueRestriction,
+ /*out*/ Input& value,
+ /*optional out*/ Input::size_type* significantBytes = nullptr);
+
+// This parser will only parse values between 0..127. If this range is
+// increased then callers will need to be changed.
+Result IntegralValue(Reader& input, uint8_t tag, /*out*/ uint8_t& value);
+
+} // namespace internal
+
+Result BitStringWithNoUnusedBits(Reader& input, /*out*/ Input& value);
+
+inline Result Boolean(Reader& input, /*out*/ bool& value) {
+ Reader valueReader;
+ Result rv = ExpectTagAndGetValue(input, BOOLEAN, valueReader);
+ if (rv != Success) {
+ return rv;
+ }
+
+ uint8_t intValue;
+ rv = valueReader.Read(intValue);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = End(valueReader);
+ if (rv != Success) {
+ return rv;
+ }
+ switch (intValue) {
+ case 0:
+ value = false;
+ return Success;
+ case 0xFF:
+ value = true;
+ return Success;
+ default:
+ return Result::ERROR_BAD_DER;
+ }
+}
+
+// This is for BOOLEAN DEFAULT FALSE.
+// The standard stipulates that "The encoding of a set value or sequence value
+// shall not include an encoding for any component value which is equal to its
+// default value." However, it appears to be common that other libraries
+// incorrectly include the value of a BOOLEAN even when it's equal to the
+// default value, so we allow invalid explicit encodings here.
+inline Result OptionalBoolean(Reader& input, /*out*/ bool& value) {
+ value = false;
+ if (input.Peek(BOOLEAN)) {
+ Result rv = Boolean(input, value);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+ return Success;
+}
+
+// This parser will only parse values between 0..127. If this range is
+// increased then callers will need to be changed.
+inline Result Enumerated(Reader& input, uint8_t& value) {
+ return internal::IntegralValue(input, ENUMERATED | 0, value);
+}
+
+namespace internal {
+
+// internal::TimeChoice implements the shared functionality of GeneralizedTime
+// and TimeChoice. tag must be either UTCTime or GENERALIZED_TIME.
+//
+// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
+// eliminate the chance for complications in converting times to traditional
+// time formats that start at 1970.
+Result TimeChoice(Reader& input, uint8_t tag, /*out*/ Time& time);
+
+} // namespace internal
+
+// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
+// eliminate the chance for complications in converting times to traditional
+// time formats that start at 1970.
+inline Result GeneralizedTime(Reader& input, /*out*/ Time& time) {
+ return internal::TimeChoice(input, GENERALIZED_TIME, time);
+}
+
+// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
+// eliminate the chance for complications in converting times to traditional
+// time formats that start at 1970.
+inline Result TimeChoice(Reader& input, /*out*/ Time& time) {
+ uint8_t expectedTag = input.Peek(UTCTime) ? UTCTime : GENERALIZED_TIME;
+ return internal::TimeChoice(input, expectedTag, time);
+}
+
+// Parse a DER integer value into value. Empty values, negative values, and
+// zero are rejected. If significantBytes is not null, then it will be set to
+// the number of significant bytes in the value (the length of the value, less
+// the length of any leading padding), which is useful for key size checks.
+inline Result PositiveInteger(
+ Reader& input, /*out*/ Input& value,
+ /*optional out*/ Input::size_type* significantBytes = nullptr) {
+ return internal::IntegralBytes(
+ input, INTEGER, internal::IntegralValueRestriction::MustBePositive, value,
+ significantBytes);
+}
+
+// This parser will only parse values between 0..127. If this range is
+// increased then callers will need to be changed.
+inline Result Integer(Reader& input, /*out*/ uint8_t& value) {
+ return internal::IntegralValue(input, INTEGER, value);
+}
+
+// This parser will only parse values between 0..127. If this range is
+// increased then callers will need to be changed. The default value must be
+// -1; defaultValue is only a parameter to make it clear in the calling code
+// what the default value is.
+inline Result OptionalInteger(Reader& input, long defaultValue,
+ /*out*/ long& value) {
+ // If we need to support a different default value in the future, we need to
+ // test that parsedValue != defaultValue.
+ if (defaultValue != -1) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ if (!input.Peek(INTEGER)) {
+ value = defaultValue;
+ return Success;
+ }
+
+ uint8_t parsedValue;
+ Result rv = Integer(input, parsedValue);
+ if (rv != Success) {
+ return rv;
+ }
+ value = parsedValue;
+ return Success;
+}
+
+inline Result Null(Reader& input) {
+ return ExpectTagAndEmptyValue(input, NULLTag);
+}
+
+template <uint8_t Len>
+Result OID(Reader& input, const uint8_t (&expectedOid)[Len]) {
+ Reader value;
+ Result rv = ExpectTagAndGetValue(input, OIDTag, value);
+ if (rv != Success) {
+ return rv;
+ }
+ if (!value.MatchRest(expectedOid)) {
+ return Result::ERROR_BAD_DER;
+ }
+ return Success;
+}
+
+// PKI-specific types
+
+inline Result CertificateSerialNumber(Reader& input, /*out*/ Input& value) {
+ // http://tools.ietf.org/html/rfc5280#section-4.1.2.2:
+ //
+ // * "The serial number MUST be a positive integer assigned by the CA to
+ // each certificate."
+ // * "Certificate users MUST be able to handle serialNumber values up to 20
+ // octets. Conforming CAs MUST NOT use serialNumber values longer than 20
+ // octets."
+ // * "Note: Non-conforming CAs may issue certificates with serial numbers
+ // that are negative or zero. Certificate users SHOULD be prepared to
+ // gracefully handle such certificates."
+ return internal::IntegralBytes(
+ input, INTEGER, internal::IntegralValueRestriction::NoRestriction, value);
+}
+
+// x.509 and OCSP both use this same version numbering scheme, though OCSP
+// only supports v1.
+enum class Version { v1 = 0, v2 = 1, v3 = 2, v4 = 3, Uninitialized = 255 };
+
+// X.509 Certificate and OCSP ResponseData both use
+// "[0] EXPLICIT Version DEFAULT v1". Although an explicit encoding of v1 is
+// illegal, we support it because some real-world OCSP responses explicitly
+// encode it.
+Result OptionalVersion(Reader& input, /*out*/ Version& version);
+
+template <typename ExtensionHandler>
+inline Result OptionalExtensions(Reader& input, uint8_t tag,
+ ExtensionHandler extensionHandler) {
+ if (!input.Peek(tag)) {
+ return Success;
+ }
+
+ return Nested(input, tag, [extensionHandler](Reader& tagged) {
+ // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ //
+ // TODO(bug 997994): According to the specification, there should never be
+ // an empty sequence of extensions but we've found OCSP responses that have
+ // that (see bug 991898).
+ return NestedOf(
+ tagged, SEQUENCE, SEQUENCE, EmptyAllowed::Yes,
+ [extensionHandler](Reader& extension) -> Result {
+ // Extension ::= SEQUENCE {
+ // extnID OBJECT IDENTIFIER,
+ // critical BOOLEAN DEFAULT FALSE,
+ // extnValue OCTET STRING
+ // }
+ Reader extnID;
+ Result rv = ExpectTagAndGetValue(extension, OIDTag, extnID);
+ if (rv != Success) {
+ return rv;
+ }
+ bool critical;
+ rv = OptionalBoolean(extension, critical);
+ if (rv != Success) {
+ return rv;
+ }
+ Input extnValue;
+ rv = ExpectTagAndGetValue(extension, OCTET_STRING, extnValue);
+ if (rv != Success) {
+ return rv;
+ }
+ bool understood = false;
+ rv = extensionHandler(extnID, extnValue, critical, understood);
+ if (rv != Success) {
+ return rv;
+ }
+ if (critical && !understood) {
+ return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION;
+ }
+ return Success;
+ });
+ });
+}
+
+Result DigestAlgorithmIdentifier(Reader& input,
+ /*out*/ DigestAlgorithm& algorithm);
+
+enum class PublicKeyAlgorithm { RSA_PKCS1, ECDSA, Uninitialized };
+
+Result SignatureAlgorithmIdentifierValue(
+ Reader& input,
+ /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm,
+ /*out*/ DigestAlgorithm& digestAlgorithm);
+
+struct SignedDataWithSignature final {
+ public:
+ Input data;
+ Input algorithm;
+ Input signature;
+
+ void operator=(const SignedDataWithSignature&) = delete;
+};
+
+// Parses a SEQUENCE into tbs and then parses an AlgorithmIdentifier followed
+// by a BIT STRING into signedData. This handles the commonality between
+// parsing the signed/signature fields of certificates and OCSP responses. In
+// the case of an OCSP response, the caller needs to parse the certs
+// separately.
+//
+// Note that signatureAlgorithm is NOT parsed or validated.
+//
+// Certificate ::= SEQUENCE {
+// tbsCertificate TBSCertificate,
+// signatureAlgorithm AlgorithmIdentifier,
+// signatureValue BIT STRING }
+//
+// BasicOCSPResponse ::= SEQUENCE {
+// tbsResponseData ResponseData,
+// signatureAlgorithm AlgorithmIdentifier,
+// signature BIT STRING,
+// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+Result SignedData(Reader& input, /*out*/ Reader& tbs,
+ /*out*/ SignedDataWithSignature& signedDataWithSignature);
+}
+}
+} // namespace mozilla::pkix::der
+
+#endif // mozilla_pkix_pkixder_h
diff --git a/security/nss/lib/mozpkix/include/pkix/pkixnss.h b/security/nss/lib/mozpkix/include/pkix/pkixnss.h
new file mode 100644
index 000000000..b181ca541
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix/pkixnss.h
@@ -0,0 +1,106 @@
+/* -*- 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 <seccomon.h>
+#include "mozpkix/pkixtypes.h"
+#include "prerror.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,
+ MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED = ERROR_BASE + 13,
+ MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT = ERROR_BASE + 14,
+ MOZILLA_PKIX_ERROR_MITM_DETECTED = ERROR_BASE + 15,
+ 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/nss/lib/mozpkix/include/pkix/pkixtypes.h b/security/nss/lib/mozpkix/include/pkix/pkixtypes.h
new file mode 100644
index 000000000..6b12edbb1
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix/pkixtypes.h
@@ -0,0 +1,400 @@
+/* -*- 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 <memory>
+
+#include "mozpkix/Input.h"
+#include "mozpkix/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;
+ bool operator==(const CertPolicyId& other) 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 aIssuer, Input aIssuerSubjectPublicKeyInfo, Input aSerialNumber)
+ : issuer(aIssuer),
+ issuerSubjectPublicKeyInfo(aIssuerSubjectPublicKeyInfo),
+ serialNumber(aSerialNumber) {}
+ const Input issuer;
+ const Input issuerSubjectPublicKeyInfo;
+ const Input serialNumber;
+
+ void operator=(const CertID&) = delete;
+};
+typedef std::unique_ptr<CertID> ScopedCertID;
+
+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,
+ const CertPolicyId& requiredPolicy) = 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
diff --git a/security/nss/lib/mozpkix/include/pkix/pkixutil.h b/security/nss/lib/mozpkix/include/pkix/pkixutil.h
new file mode 100644
index 000000000..ca5b5a2d7
--- /dev/null
+++ b/security/nss/lib/mozpkix/include/pkix/pkixutil.h
@@ -0,0 +1,265 @@
+/* -*- 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 "mozpkix/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 aCertDER, EndEntityOrCA aEndEntityOrCA,
+ const BackCert* aChildCert)
+ : der(aCertDER),
+ endEntityOrCA(aEndEntityOrCA),
+ childCert(aChildCert),
+ version(der::Version::Uninitialized) {}
+
+ 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