summaryrefslogtreecommitdiffstats
path: root/security/certverifier
diff options
context:
space:
mode:
Diffstat (limited to 'security/certverifier')
-rw-r--r--security/certverifier/BRNameMatchingPolicy.cpp45
-rw-r--r--security/certverifier/BRNameMatchingPolicy.h59
-rw-r--r--security/certverifier/CNNICHashWhitelist.inc1647
-rw-r--r--security/certverifier/CTKnownLogs.h155
-rw-r--r--security/certverifier/CTLogVerifier.cpp283
-rw-r--r--security/certverifier/CTLogVerifier.h68
-rw-r--r--security/certverifier/CTObjectsExtractor.cpp396
-rw-r--r--security/certverifier/CTObjectsExtractor.h43
-rw-r--r--security/certverifier/CTSerialization.cpp554
-rw-r--r--security/certverifier/CTSerialization.h70
-rw-r--r--security/certverifier/CTVerifyResult.cpp18
-rw-r--r--security/certverifier/CTVerifyResult.h41
-rw-r--r--security/certverifier/CertVerifier.cpp903
-rw-r--r--security/certverifier/CertVerifier.h242
-rw-r--r--security/certverifier/ExtendedValidation.cpp1389
-rw-r--r--security/certverifier/ExtendedValidation.h29
-rw-r--r--security/certverifier/MultiLogCTVerifier.cpp193
-rw-r--r--security/certverifier/MultiLogCTVerifier.h86
-rw-r--r--security/certverifier/NSSCertDBTrustDomain.cpp1292
-rw-r--r--security/certverifier/NSSCertDBTrustDomain.h201
-rw-r--r--security/certverifier/OCSPCache.cpp343
-rw-r--r--security/certverifier/OCSPCache.h138
-rw-r--r--security/certverifier/OCSPRequestor.cpp219
-rw-r--r--security/certverifier/OCSPRequestor.h28
-rw-r--r--security/certverifier/OCSPVerificationTrustDomain.cpp128
-rw-r--r--security/certverifier/OCSPVerificationTrustDomain.h89
-rw-r--r--security/certverifier/SignedCertificateTimestamp.cpp45
-rw-r--r--security/certverifier/SignedCertificateTimestamp.h142
-rw-r--r--security/certverifier/SignedTreeHead.h31
-rw-r--r--security/certverifier/StartComAndWoSignData.inc89
-rw-r--r--security/certverifier/moz.build100
-rw-r--r--security/certverifier/tests/gtest/CTLogVerifierTest.cpp129
-rw-r--r--security/certverifier/tests/gtest/CTObjectsExtractorTest.cpp83
-rw-r--r--security/certverifier/tests/gtest/CTSerializationTest.cpp271
-rw-r--r--security/certverifier/tests/gtest/CTTestUtils.cpp747
-rw-r--r--security/certverifier/tests/gtest/CTTestUtils.h132
-rw-r--r--security/certverifier/tests/gtest/MultiLogCTVerifierTest.cpp233
-rw-r--r--security/certverifier/tests/gtest/moz.build21
38 files changed, 10682 insertions, 0 deletions
diff --git a/security/certverifier/BRNameMatchingPolicy.cpp b/security/certverifier/BRNameMatchingPolicy.cpp
new file mode 100644
index 000000000..cab267a95
--- /dev/null
+++ b/security/certverifier/BRNameMatchingPolicy.cpp
@@ -0,0 +1,45 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BRNameMatchingPolicy.h"
+
+#include "mozilla/Assertions.h"
+
+using namespace mozilla::psm;
+using namespace mozilla::pkix;
+
+Result
+BRNameMatchingPolicy::FallBackToCommonName(
+ Time notBefore,
+ /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName)
+{
+ // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
+ static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
+ // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
+ static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
+ switch (mMode)
+ {
+ case Mode::Enforce:
+ fallBackToCommonName = FallBackToSearchWithinSubject::No;
+ break;
+ case Mode::EnforceAfter23August2015:
+ fallBackToCommonName = notBefore > AUGUST_23_2015
+ ? FallBackToSearchWithinSubject::No
+ : FallBackToSearchWithinSubject::Yes;
+ break;
+ case Mode::EnforceAfter23August2016:
+ fallBackToCommonName = notBefore > AUGUST_23_2016
+ ? FallBackToSearchWithinSubject::No
+ : FallBackToSearchWithinSubject::Yes;
+ break;
+ case Mode::DoNotEnforce:
+ fallBackToCommonName = FallBackToSearchWithinSubject::Yes;
+ break;
+ default:
+ MOZ_CRASH("Unexpected Mode");
+ }
+ return Success;
+}
diff --git a/security/certverifier/BRNameMatchingPolicy.h b/security/certverifier/BRNameMatchingPolicy.h
new file mode 100644
index 000000000..009f21440
--- /dev/null
+++ b/security/certverifier/BRNameMatchingPolicy.h
@@ -0,0 +1,59 @@
+/* -*- 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 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/. */
+
+#ifndef BRNameMatchingPolicy_h
+#define BRNameMatchingPolicy_h
+
+#include "pkix/pkixtypes.h"
+
+namespace mozilla { namespace psm {
+
+// According to the Baseline Requirements version 1.3.3 section 7.1.4.2.2.a,
+// the requirements of the subject common name field are as follows:
+// "If present, this field MUST contain a single IP address or Fully‐Qualified
+// Domain Name that is one of the values contained in the Certificate’s
+// subjectAltName extension". Consequently, since any name information present
+// in the common name must be present in the subject alternative name extension,
+// when performing name matching, it should not be necessary to fall back to the
+// common name. Because this consequence has not commonly been enforced, this
+// implementation provides a mechanism to start enforcing it gradually while
+// maintaining some backwards compatibility. If configured with the mode
+// "EnforceAfter23August2016", name matching will only fall back to using the
+// subject common name for certificates where the notBefore field is before 23
+// August 2016. Similarly, the mode "EnforceAfter23August2015" is also
+// available. This is to provide a balance between allowing preexisting
+// long-lived certificates and detecting newly-issued problematic certificates.
+// Note that this implementation does not actually directly enforce that if the
+// subject common name is present, its value corresponds to a dNSName or
+// iPAddress entry in the subject alternative name extension.
+
+class BRNameMatchingPolicy : public mozilla::pkix::NameMatchingPolicy
+{
+public:
+ enum class Mode {
+ DoNotEnforce = 0,
+ EnforceAfter23August2016 = 1,
+ EnforceAfter23August2015 = 2,
+ Enforce = 3,
+ };
+
+ explicit BRNameMatchingPolicy(Mode mode)
+ : mMode(mode)
+ {
+ }
+
+ virtual mozilla::pkix::Result FallBackToCommonName(
+ mozilla::pkix::Time notBefore,
+ /*out*/ mozilla::pkix::FallBackToSearchWithinSubject& fallBacktoCommonName)
+ override;
+
+private:
+ Mode mMode;
+};
+
+} } // namespace mozilla::psm
+
+#endif // BRNameMatchingPolicy_h
diff --git a/security/certverifier/CNNICHashWhitelist.inc b/security/certverifier/CNNICHashWhitelist.inc
new file mode 100644
index 000000000..ed4149f1c
--- /dev/null
+++ b/security/certverifier/CNNICHashWhitelist.inc
@@ -0,0 +1,1647 @@
+// 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/.
+//
+//***************************************************************************
+// This file was automatically generated by makeCNNICHashes.js. It shouldn't
+// need to be manually edited.
+//***************************************************************************
+
+// This file may be removed after Sun Feb 02 2020 21:45:13 GMT-0800 (PST)
+
+#define CNNIC_WHITELIST_HASH_LEN 32
+
+struct WhitelistedCNNICHash {
+ const uint8_t hash[CNNIC_WHITELIST_HASH_LEN];
+};
+
+static const struct WhitelistedCNNICHash WhitelistedCNNICHashes[] = {
+ {
+ { 0x00, 0xC5, 0x9F, 0x5E, 0xF3, 0xB4, 0x6D, 0xBC, 0xA0, 0xA8, 0xBB, 0xA5, 0x0A, 0x72, 0xD4, 0xE1,
+ 0x83, 0x9A, 0x94, 0xFB, 0x1A, 0x58, 0x5A, 0xD7, 0x2A, 0x7A, 0xAC, 0x3C, 0x72, 0x56, 0x1F, 0xC0 },
+ },
+ {
+ { 0x02, 0x01, 0x4E, 0x80, 0xF5, 0xC4, 0xF3, 0x8B, 0xA9, 0xD9, 0x04, 0x79, 0x1A, 0x63, 0xF6, 0x4D,
+ 0x05, 0xF9, 0xE2, 0x03, 0xA1, 0xF1, 0x2B, 0x06, 0xD6, 0x55, 0x94, 0x01, 0x41, 0x0E, 0x73, 0x36 },
+ },
+ {
+ { 0x02, 0x35, 0x38, 0xE2, 0x48, 0x15, 0x28, 0x75, 0x29, 0x2F, 0x2C, 0x83, 0x9A, 0xB3, 0x2B, 0xC7,
+ 0x35, 0x1E, 0x2B, 0x29, 0x99, 0x1D, 0x66, 0xAE, 0xA6, 0x16, 0xCB, 0x0B, 0x26, 0xA5, 0xE3, 0x75 },
+ },
+ {
+ { 0x02, 0xEC, 0x35, 0xF5, 0x83, 0x4C, 0xD2, 0xC3, 0x43, 0x33, 0x39, 0x9A, 0xEA, 0x6B, 0xDA, 0x84,
+ 0x68, 0xAB, 0x8D, 0x74, 0xEF, 0x6C, 0xA5, 0x2D, 0x33, 0x7A, 0x30, 0x69, 0x4C, 0x3F, 0x95, 0xA4 },
+ },
+ {
+ { 0x03, 0xE0, 0x6E, 0x0B, 0x7A, 0x2C, 0xBA, 0xE4, 0xB6, 0x8B, 0xCE, 0x5F, 0x83, 0xE7, 0xA9, 0x31,
+ 0x6E, 0xD7, 0x82, 0x3E, 0x8D, 0x94, 0x85, 0x38, 0xF1, 0x94, 0x3F, 0xA4, 0x27, 0xD7, 0x91, 0x0E },
+ },
+ {
+ { 0x04, 0x0F, 0x53, 0x7A, 0x51, 0x95, 0x95, 0xCC, 0xFF, 0xDE, 0x35, 0xE0, 0xD1, 0x28, 0xB7, 0x99,
+ 0x92, 0x2B, 0xA9, 0x37, 0xA2, 0xE8, 0x65, 0x84, 0x36, 0x62, 0xF1, 0xF4, 0x50, 0x02, 0xB8, 0x2D },
+ },
+ {
+ { 0x04, 0x18, 0xD5, 0x3E, 0xBC, 0x8E, 0x71, 0x41, 0x25, 0x1B, 0x4D, 0xC8, 0xFA, 0x7B, 0x2B, 0xD8,
+ 0xFD, 0x3A, 0x1C, 0x65, 0x2A, 0xA1, 0x16, 0xE7, 0xFC, 0x70, 0x0B, 0x2A, 0xB5, 0x1A, 0x2A, 0x1A },
+ },
+ {
+ { 0x06, 0xD4, 0x08, 0xFF, 0xA9, 0x93, 0xAF, 0x04, 0x45, 0x9C, 0x45, 0x67, 0x1A, 0xAB, 0xD8, 0x7E,
+ 0xF9, 0x2B, 0x85, 0x6B, 0x1B, 0x42, 0xC6, 0x7E, 0x00, 0x5E, 0xB4, 0xD2, 0x71, 0x58, 0xA8, 0x42 },
+ },
+ {
+ { 0x07, 0x19, 0x4F, 0x47, 0xF4, 0xCE, 0xD0, 0x96, 0xD1, 0x06, 0x8D, 0x34, 0x49, 0x3B, 0x67, 0x37,
+ 0x14, 0x45, 0x16, 0x93, 0xA6, 0xA2, 0x71, 0x2F, 0x70, 0x8F, 0x59, 0x36, 0x12, 0x11, 0xC6, 0x21 },
+ },
+ {
+ { 0x07, 0x8F, 0xEE, 0x58, 0x8A, 0x2C, 0x55, 0xC8, 0xE2, 0xC1, 0x78, 0x71, 0xAA, 0xB6, 0xE4, 0x00,
+ 0xB3, 0xFD, 0xBC, 0xDC, 0xF3, 0x91, 0x46, 0xA0, 0x89, 0x37, 0xF9, 0xAC, 0x06, 0xA1, 0xB8, 0xBD },
+ },
+ {
+ { 0x07, 0xE9, 0x60, 0x9E, 0x05, 0xDC, 0x0A, 0x1E, 0x52, 0x15, 0x06, 0x49, 0xEB, 0xF4, 0x1F, 0x6D,
+ 0xE3, 0x86, 0x7C, 0x9C, 0x25, 0xFE, 0x17, 0x7B, 0xAB, 0xCF, 0xD9, 0xB3, 0x70, 0x46, 0x13, 0x8B },
+ },
+ {
+ { 0x08, 0x21, 0x0A, 0xC3, 0xA2, 0x95, 0x56, 0xF6, 0x8D, 0x33, 0xB4, 0x40, 0x87, 0x9C, 0x54, 0x63,
+ 0x64, 0x04, 0xE9, 0x7C, 0x4D, 0x9F, 0x97, 0x82, 0x23, 0xD2, 0x42, 0xAB, 0xE5, 0x38, 0x5E, 0x4E },
+ },
+ {
+ { 0x08, 0xC2, 0xD3, 0x17, 0xA8, 0x4A, 0x3C, 0xBE, 0x38, 0xDE, 0x64, 0xA2, 0x4D, 0xD4, 0x27, 0x91,
+ 0x09, 0xE2, 0xBC, 0x02, 0x2B, 0x93, 0xB1, 0x05, 0xA8, 0x94, 0xA5, 0x1A, 0xDC, 0x3E, 0xE5, 0xCC },
+ },
+ {
+ { 0x09, 0x9F, 0x3E, 0x71, 0xB5, 0x00, 0xD1, 0x5B, 0x03, 0x7B, 0x93, 0xAA, 0x5F, 0xB4, 0x16, 0x19,
+ 0x0A, 0xD1, 0xDF, 0x86, 0x73, 0xAB, 0x31, 0xA8, 0xF6, 0xD9, 0x7F, 0x59, 0x5E, 0x8E, 0x16, 0xE9 },
+ },
+ {
+ { 0x09, 0xEB, 0xDD, 0x1B, 0x7F, 0xFA, 0x4E, 0xD7, 0x4B, 0xEB, 0xAE, 0x96, 0xBA, 0x10, 0x65, 0xDC,
+ 0x7D, 0xA1, 0xC5, 0xD3, 0x18, 0x3C, 0xC5, 0x94, 0x19, 0xE9, 0x78, 0x36, 0xAF, 0x7F, 0x6D, 0x70 },
+ },
+ {
+ { 0x0A, 0x01, 0x88, 0x81, 0x2C, 0x9D, 0xE8, 0x8A, 0x2F, 0x0A, 0x5C, 0x4C, 0x57, 0xE6, 0xF9, 0xA8,
+ 0x15, 0x69, 0xE9, 0xC7, 0x09, 0xC0, 0x95, 0x40, 0x80, 0xE5, 0xE4, 0xE6, 0x62, 0x85, 0x6D, 0xF8 },
+ },
+ {
+ { 0x0A, 0x42, 0x19, 0x7E, 0x48, 0x70, 0xB2, 0x34, 0x20, 0xF5, 0x51, 0x9F, 0xB8, 0x39, 0xB6, 0xCC,
+ 0x83, 0x03, 0x52, 0x9A, 0xA9, 0x06, 0x9A, 0xD1, 0xA0, 0x90, 0x86, 0xCF, 0x6C, 0xBA, 0x07, 0xC2 },
+ },
+ {
+ { 0x0B, 0x03, 0xE1, 0x27, 0xC2, 0xE3, 0x3E, 0xAD, 0xBC, 0xB0, 0x99, 0x80, 0x46, 0xCC, 0x9B, 0xA7,
+ 0x33, 0x46, 0x3E, 0x0C, 0xA6, 0x43, 0x52, 0x27, 0x81, 0xB0, 0x3D, 0x81, 0x53, 0x97, 0xEB, 0x4F },
+ },
+ {
+ { 0x0B, 0x1E, 0x1E, 0x73, 0x43, 0xA0, 0xE9, 0x1C, 0x2A, 0x27, 0xDD, 0x2A, 0x4D, 0x7E, 0x6B, 0xF1,
+ 0xE8, 0x04, 0x4B, 0x58, 0xCE, 0x1A, 0xE8, 0x1E, 0x27, 0xD8, 0x14, 0xFD, 0x2D, 0xC0, 0x18, 0x93 },
+ },
+ {
+ { 0x0B, 0x48, 0xD5, 0x5C, 0xAC, 0x84, 0xFD, 0xEE, 0x15, 0xD8, 0x1A, 0xFF, 0x99, 0x07, 0xBB, 0x9A,
+ 0x57, 0x11, 0xA9, 0x5C, 0xE2, 0x3A, 0x8D, 0x4D, 0x5E, 0x88, 0x62, 0xBF, 0x15, 0xA7, 0x6A, 0x75 },
+ },
+ {
+ { 0x0B, 0xFE, 0xA1, 0x38, 0x31, 0x67, 0x3E, 0xC9, 0x69, 0xD0, 0x5F, 0xD8, 0x67, 0xB6, 0x69, 0xF2,
+ 0x71, 0x24, 0xAF, 0xEB, 0x7C, 0x60, 0x8C, 0xFE, 0x54, 0xCF, 0x46, 0x33, 0x06, 0xCC, 0x99, 0x2E },
+ },
+ {
+ { 0x0C, 0xB9, 0x31, 0x93, 0xF1, 0x65, 0x26, 0xE1, 0xD1, 0x65, 0x52, 0x11, 0x7B, 0xA2, 0x1A, 0xAC,
+ 0xB9, 0xF1, 0xD7, 0xA8, 0x93, 0x56, 0xA3, 0x5D, 0xE4, 0xF6, 0x65, 0xE9, 0x39, 0x90, 0x79, 0x38 },
+ },
+ {
+ { 0x0D, 0x16, 0x1B, 0xB9, 0xCA, 0x0D, 0x20, 0xE4, 0x67, 0x35, 0x89, 0x67, 0x22, 0x78, 0xB0, 0xA3,
+ 0xC5, 0xE2, 0x69, 0x30, 0xA4, 0xDC, 0x3A, 0x82, 0x16, 0x85, 0x43, 0x24, 0x27, 0xC7, 0x31, 0x5A },
+ },
+ {
+ { 0x0D, 0x66, 0x45, 0x6B, 0x0B, 0xF4, 0xAA, 0x54, 0x16, 0xE4, 0x4D, 0x9F, 0xDB, 0x40, 0x38, 0x3D,
+ 0x34, 0x3D, 0x7B, 0x3F, 0x6A, 0xFE, 0x69, 0xAA, 0x08, 0x95, 0xBB, 0x1A, 0xB5, 0xE0, 0x61, 0xA0 },
+ },
+ {
+ { 0x0D, 0x71, 0xC8, 0xCA, 0x16, 0x56, 0x59, 0xEF, 0xAF, 0x69, 0x65, 0x29, 0x28, 0x9A, 0xAE, 0x25,
+ 0xD9, 0xC4, 0x2A, 0x1B, 0xBB, 0x03, 0x5A, 0x2B, 0x8C, 0x61, 0x14, 0x7E, 0x1B, 0x8B, 0x90, 0x52 },
+ },
+ {
+ { 0x0E, 0xFD, 0x68, 0x73, 0xD6, 0x0E, 0x77, 0x96, 0x2D, 0xF6, 0x00, 0x16, 0xDC, 0x3B, 0xAF, 0x9C,
+ 0xA7, 0x1E, 0x7D, 0x86, 0x19, 0xE7, 0xEB, 0xAA, 0x3A, 0xF2, 0xDC, 0xB5, 0xBA, 0x24, 0xDE, 0xC2 },
+ },
+ {
+ { 0x0E, 0xFF, 0x3C, 0xFF, 0xDA, 0x4A, 0x3E, 0x87, 0x23, 0x4A, 0x86, 0xC7, 0x0D, 0x49, 0x8C, 0x62,
+ 0x60, 0x7F, 0x37, 0x44, 0xEA, 0x71, 0xF1, 0x83, 0x1D, 0xCF, 0xCA, 0xF3, 0xAF, 0x15, 0x56, 0x9C },
+ },
+ {
+ { 0x10, 0x83, 0x6D, 0xA0, 0xCD, 0x6A, 0xC0, 0x95, 0xDD, 0x7A, 0xC3, 0x4D, 0x99, 0x01, 0x90, 0x9A,
+ 0x8E, 0xF8, 0x4D, 0x6E, 0xE0, 0x5B, 0x83, 0x43, 0x03, 0xD4, 0x7F, 0xC0, 0xA5, 0xF9, 0x14, 0xFA },
+ },
+ {
+ { 0x11, 0xA4, 0x02, 0x7B, 0x45, 0xFC, 0x9A, 0x6F, 0x40, 0x21, 0x25, 0xC3, 0xCA, 0x22, 0x68, 0xE0,
+ 0x15, 0xA3, 0x1B, 0xA4, 0xFD, 0xB0, 0x05, 0x9D, 0x66, 0x6B, 0x73, 0xC8, 0x51, 0xD5, 0x35, 0x92 },
+ },
+ {
+ { 0x12, 0x6B, 0x1B, 0xA6, 0x38, 0xC7, 0xE6, 0x99, 0xBC, 0xBC, 0x54, 0xF5, 0x79, 0xAC, 0xD3, 0x9F,
+ 0xE6, 0x1D, 0x08, 0x22, 0x5F, 0xE5, 0xB1, 0xF9, 0x01, 0x88, 0xB2, 0x3F, 0xD8, 0x43, 0x3E, 0x8E },
+ },
+ {
+ { 0x13, 0x5D, 0x3E, 0xDA, 0x6E, 0x55, 0x9B, 0xF5, 0xEE, 0x23, 0x0A, 0xA5, 0xBA, 0x59, 0xBB, 0x6A,
+ 0x2A, 0x0F, 0x07, 0x82, 0x2F, 0xED, 0x38, 0x44, 0x7E, 0x6A, 0xBC, 0x5C, 0x23, 0xAA, 0xD0, 0x27 },
+ },
+ {
+ { 0x13, 0x6A, 0x40, 0x09, 0x81, 0xB1, 0xA3, 0xE0, 0x5F, 0xDC, 0xAC, 0x20, 0xA2, 0x36, 0xF8, 0x6E,
+ 0x94, 0xE5, 0xEE, 0x58, 0x59, 0xD8, 0xFD, 0x45, 0xE9, 0xE9, 0xC5, 0xA6, 0xC5, 0xC0, 0xA4, 0x13 },
+ },
+ {
+ { 0x14, 0x21, 0x28, 0xA6, 0x65, 0x1C, 0xDC, 0x18, 0x70, 0xC2, 0x67, 0x5E, 0xC0, 0xB0, 0xEF, 0x32,
+ 0xB5, 0xD4, 0xC1, 0x55, 0x35, 0x8E, 0x7E, 0xD9, 0x5A, 0x98, 0xE8, 0x3B, 0x1A, 0xD8, 0xBE, 0x4D },
+ },
+ {
+ { 0x14, 0x47, 0x25, 0xA6, 0x79, 0x1C, 0x60, 0x0C, 0x4C, 0x2C, 0xF3, 0x94, 0x3F, 0x3E, 0xCF, 0x40,
+ 0xD6, 0x31, 0xD7, 0x60, 0xE4, 0x51, 0xEF, 0x28, 0x29, 0xAF, 0xFB, 0xEE, 0x74, 0x80, 0xAD, 0x17 },
+ },
+ {
+ { 0x15, 0x27, 0x2A, 0xBC, 0x1F, 0x0C, 0x4D, 0x1D, 0x1A, 0x92, 0x08, 0x73, 0x55, 0xA1, 0xE0, 0x42,
+ 0x6C, 0x2B, 0xB5, 0xB4, 0x37, 0x30, 0x00, 0xB8, 0x2C, 0x2C, 0xCA, 0xB7, 0xFA, 0xD6, 0xFA, 0x20 },
+ },
+ {
+ { 0x15, 0x48, 0x1F, 0xDE, 0x4E, 0x3F, 0x72, 0x49, 0x66, 0x87, 0xDF, 0x57, 0x5F, 0xB5, 0xB1, 0x27,
+ 0xBD, 0x6D, 0xEB, 0x66, 0x1D, 0xD9, 0x07, 0x71, 0x8B, 0xA0, 0x65, 0xC7, 0xDA, 0x66, 0x76, 0xD1 },
+ },
+ {
+ { 0x15, 0x5A, 0x88, 0x39, 0x60, 0x8B, 0x77, 0x25, 0x34, 0x6A, 0x72, 0x40, 0xE4, 0xE2, 0x50, 0x3A,
+ 0xCC, 0x7B, 0x8B, 0xEF, 0x0B, 0x1B, 0xE6, 0x15, 0xB9, 0x02, 0x4A, 0x88, 0xE6, 0x52, 0x11, 0xF9 },
+ },
+ {
+ { 0x15, 0x5D, 0x88, 0x6E, 0x99, 0x1D, 0x40, 0x0A, 0xBF, 0x2F, 0x83, 0xC2, 0x80, 0xD1, 0x24, 0x6D,
+ 0xCE, 0x02, 0xA6, 0x28, 0x31, 0x26, 0xC6, 0x17, 0xE4, 0x17, 0xD2, 0xB7, 0xEA, 0xC1, 0x19, 0x24 },
+ },
+ {
+ { 0x18, 0x1E, 0xBB, 0x29, 0x8D, 0x20, 0x68, 0x5C, 0x48, 0xF7, 0x53, 0x89, 0x80, 0xC5, 0x63, 0xC8,
+ 0xF7, 0x48, 0x95, 0x4C, 0xF2, 0x64, 0x41, 0x9A, 0x72, 0xFC, 0xC6, 0x34, 0x0A, 0x10, 0x23, 0x80 },
+ },
+ {
+ { 0x19, 0x77, 0x3E, 0xE9, 0xE9, 0x35, 0x6B, 0x88, 0x11, 0xD6, 0x56, 0x79, 0x9C, 0x53, 0x16, 0x0B,
+ 0x61, 0x73, 0xFA, 0x8A, 0x81, 0x47, 0x97, 0xDB, 0xCD, 0x55, 0xB2, 0x27, 0x38, 0x70, 0x60, 0x3E },
+ },
+ {
+ { 0x1A, 0x9E, 0xC6, 0x8C, 0xED, 0xB6, 0xBD, 0x94, 0x0C, 0x95, 0x34, 0xE6, 0x84, 0xBB, 0x04, 0x9F,
+ 0xF1, 0xE2, 0x3B, 0x66, 0xA1, 0x33, 0x01, 0x2F, 0xC3, 0x99, 0xEB, 0x4F, 0xB5, 0xD3, 0xAA, 0x35 },
+ },
+ {
+ { 0x1B, 0x7B, 0xF8, 0xD9, 0xE8, 0x29, 0x3C, 0x53, 0xDD, 0x59, 0xEC, 0x97, 0xFE, 0x16, 0xF0, 0xEA,
+ 0xB4, 0x68, 0x5B, 0x95, 0xCE, 0x14, 0xD2, 0x62, 0x3E, 0x70, 0x94, 0x2C, 0xFF, 0x25, 0xE7, 0x30 },
+ },
+ {
+ { 0x1B, 0xD7, 0xB3, 0x62, 0xBC, 0x14, 0x66, 0xFA, 0xC0, 0x5E, 0xC5, 0x9E, 0x12, 0xE8, 0x1B, 0xE7,
+ 0x35, 0x38, 0xC4, 0x97, 0x28, 0xF5, 0xAD, 0xBA, 0x2D, 0x81, 0xFC, 0xDB, 0xC4, 0x65, 0x7C, 0x1B },
+ },
+ {
+ { 0x1B, 0xEC, 0xFE, 0x78, 0xCE, 0x5E, 0x77, 0xA9, 0x77, 0xBB, 0x5F, 0xE3, 0x49, 0x91, 0x06, 0xC6,
+ 0x4C, 0xF2, 0xB0, 0x76, 0x16, 0x59, 0x49, 0x04, 0x11, 0x17, 0xCD, 0x8A, 0xBC, 0xD9, 0x05, 0xD4 },
+ },
+ {
+ { 0x1B, 0xF4, 0x8A, 0x83, 0x3C, 0xE4, 0x05, 0x64, 0x8C, 0xC0, 0xBD, 0xD3, 0xB5, 0xB8, 0xC1, 0x8E,
+ 0xB5, 0x13, 0x15, 0x34, 0x29, 0x3A, 0xB2, 0x63, 0x44, 0xB5, 0x00, 0x76, 0x48, 0x11, 0x41, 0xED },
+ },
+ {
+ { 0x1C, 0x04, 0x82, 0x0F, 0x7B, 0x4A, 0x2F, 0x1E, 0x38, 0x5D, 0xE1, 0xDE, 0x16, 0xB2, 0x22, 0x6E,
+ 0x88, 0x3D, 0x9C, 0x34, 0x66, 0x3E, 0x1B, 0x64, 0xE8, 0x5B, 0x98, 0x0E, 0xAF, 0xF0, 0xB9, 0xD3 },
+ },
+ {
+ { 0x1D, 0x9E, 0xC0, 0x06, 0xA5, 0x26, 0xFA, 0xB5, 0xCE, 0x2E, 0x71, 0xFD, 0xFC, 0x07, 0xC0, 0x11,
+ 0xF7, 0x65, 0x7B, 0xF8, 0x5F, 0x5D, 0x03, 0x52, 0xB8, 0xCB, 0x21, 0x8D, 0x4F, 0xCB, 0xC4, 0x43 },
+ },
+ {
+ { 0x1E, 0x78, 0xF8, 0x08, 0x84, 0xE3, 0x2A, 0x2E, 0xA5, 0xAD, 0x1E, 0xE8, 0x35, 0x88, 0xAC, 0xDB,
+ 0x18, 0x4A, 0x4A, 0x6E, 0x87, 0x56, 0x5B, 0xF5, 0x03, 0xB5, 0x69, 0x7A, 0xBF, 0xAE, 0x64, 0xA4 },
+ },
+ {
+ { 0x1F, 0x11, 0x85, 0xA5, 0x21, 0xE2, 0x8E, 0x95, 0x17, 0x1C, 0xF3, 0x86, 0x07, 0x8A, 0x76, 0x4A,
+ 0x9A, 0x3E, 0x71, 0xC2, 0x59, 0xBC, 0xDC, 0x5F, 0x8E, 0x66, 0xE1, 0xB5, 0x20, 0x55, 0xA2, 0x6D },
+ },
+ {
+ { 0x1F, 0x23, 0xD7, 0xA6, 0x38, 0x17, 0x1F, 0x6D, 0x09, 0x99, 0x64, 0xE0, 0xFA, 0x01, 0x72, 0x1C,
+ 0x06, 0xCC, 0xEB, 0x8E, 0xA2, 0x98, 0xBF, 0xD0, 0x04, 0x8E, 0x13, 0x8D, 0x98, 0xFC, 0x36, 0x24 },
+ },
+ {
+ { 0x1F, 0xC7, 0xF8, 0x10, 0x4E, 0x27, 0xFF, 0x2A, 0x45, 0x56, 0xF9, 0x1E, 0x05, 0x42, 0x17, 0xC5,
+ 0x8F, 0x69, 0x3F, 0x70, 0x36, 0x25, 0x9E, 0x39, 0x80, 0xB5, 0x59, 0x5B, 0x04, 0x3D, 0x11, 0x92 },
+ },
+ {
+ { 0x20, 0x0B, 0x49, 0xBD, 0xD6, 0x35, 0x02, 0x57, 0xCC, 0xD4, 0xE6, 0xAD, 0xE1, 0xCB, 0x75, 0x13,
+ 0x8D, 0xD6, 0xD9, 0x06, 0xFE, 0xF3, 0x49, 0xC0, 0xC9, 0x86, 0xA5, 0x1B, 0x29, 0xB9, 0xE5, 0x2D },
+ },
+ {
+ { 0x21, 0x09, 0xF3, 0x10, 0x7D, 0x97, 0xF8, 0x70, 0x48, 0x70, 0x8E, 0xC8, 0x7C, 0xA2, 0xDC, 0x31,
+ 0x8B, 0x2F, 0x2B, 0x57, 0x47, 0xC3, 0x38, 0xBD, 0x9C, 0x6D, 0xBC, 0xD6, 0x0F, 0xD6, 0xBE, 0xA2 },
+ },
+ {
+ { 0x21, 0x78, 0xE8, 0x28, 0x3A, 0x73, 0x39, 0x6E, 0x08, 0xC0, 0xA1, 0x1A, 0x88, 0x72, 0xFA, 0x4A,
+ 0x9F, 0xCC, 0x05, 0x67, 0x0C, 0xEE, 0xFF, 0xB8, 0x95, 0x83, 0x8E, 0xB6, 0x59, 0xDE, 0x38, 0xDB },
+ },
+ {
+ { 0x22, 0x01, 0x71, 0xF7, 0x0E, 0x1F, 0xC3, 0xC4, 0xF7, 0x8D, 0xA6, 0xC8, 0xB1, 0xD7, 0x2C, 0x3B,
+ 0xA8, 0x31, 0x9A, 0x46, 0xF8, 0x19, 0x2D, 0x1E, 0x19, 0xB9, 0xE2, 0x9A, 0xBA, 0x18, 0xEE, 0x87 },
+ },
+ {
+ { 0x23, 0x19, 0xCB, 0x3D, 0x58, 0xC6, 0xD5, 0x53, 0x62, 0x5D, 0xE5, 0xF4, 0x25, 0x2B, 0xF0, 0x29,
+ 0xAB, 0x83, 0x05, 0xEB, 0xF2, 0x2F, 0xA2, 0x3E, 0x99, 0x73, 0x04, 0x66, 0xDE, 0x24, 0xD6, 0xC3 },
+ },
+ {
+ { 0x23, 0x8A, 0x80, 0xCC, 0x9B, 0x58, 0x9A, 0xDC, 0x89, 0xB7, 0xA8, 0xF3, 0x4D, 0xDF, 0x12, 0x48,
+ 0x73, 0x4B, 0x9F, 0x7F, 0x78, 0x20, 0xB6, 0x04, 0x07, 0x66, 0xC5, 0x41, 0x3A, 0xD2, 0xBD, 0xEF },
+ },
+ {
+ { 0x23, 0x9C, 0x79, 0x5F, 0x0C, 0x55, 0xA5, 0x53, 0x16, 0x2A, 0x9C, 0xA0, 0x6E, 0x88, 0x01, 0xE1,
+ 0x19, 0xBD, 0xFF, 0x54, 0x35, 0x4A, 0x3F, 0x68, 0x43, 0xCF, 0x2A, 0x2F, 0xA6, 0x01, 0x75, 0x8E },
+ },
+ {
+ { 0x24, 0x62, 0x52, 0x48, 0x32, 0xC1, 0x54, 0xD8, 0x4D, 0xF5, 0x8E, 0xD7, 0x75, 0x22, 0x3B, 0xBE,
+ 0x25, 0x7D, 0xEA, 0xF7, 0x0E, 0xF9, 0xD2, 0x08, 0x61, 0x4E, 0xC0, 0xF5, 0x97, 0x7F, 0x6D, 0x58 },
+ },
+ {
+ { 0x24, 0x6D, 0x0C, 0x31, 0x48, 0x72, 0x75, 0x59, 0xF9, 0x9A, 0xD0, 0xC1, 0x50, 0x37, 0x70, 0x06,
+ 0xB7, 0xA1, 0x7A, 0x60, 0x3A, 0x47, 0x3B, 0x6A, 0xAC, 0xD2, 0x4E, 0x16, 0xC6, 0xC5, 0x1B, 0x42 },
+ },
+ {
+ { 0x25, 0x1B, 0xB7, 0xC5, 0x42, 0x33, 0xDA, 0x44, 0xBF, 0x53, 0xB5, 0x8A, 0xF2, 0x9A, 0xE1, 0x74,
+ 0xB9, 0x78, 0xBA, 0xDB, 0x89, 0xA9, 0x50, 0xAB, 0x3E, 0x5F, 0x9B, 0x4D, 0x0D, 0xCD, 0xBC, 0x62 },
+ },
+ {
+ { 0x26, 0x03, 0xCB, 0xDF, 0x69, 0x75, 0xE3, 0x68, 0x83, 0x7F, 0x95, 0x1A, 0x00, 0x49, 0xFD, 0xC3,
+ 0xC4, 0xB2, 0x39, 0xF0, 0x82, 0xF6, 0xBF, 0x89, 0x5D, 0xB8, 0xF3, 0x27, 0x05, 0xE6, 0x9C, 0xF3 },
+ },
+ {
+ { 0x27, 0x50, 0x11, 0x93, 0xE4, 0x61, 0xCA, 0xCE, 0x55, 0x32, 0xFA, 0xD5, 0xD5, 0xB2, 0x7E, 0x01,
+ 0x16, 0x57, 0x92, 0xE0, 0x4F, 0x24, 0x21, 0x93, 0x2F, 0x39, 0x28, 0xAF, 0x9F, 0xCD, 0xA4, 0xF3 },
+ },
+ {
+ { 0x27, 0xA8, 0x41, 0xAE, 0xCF, 0xE0, 0xA1, 0x39, 0x37, 0x51, 0xC2, 0x55, 0xF9, 0x06, 0xDB, 0x9E,
+ 0x88, 0x6B, 0xBA, 0x4D, 0x7C, 0x44, 0xEC, 0x63, 0xCE, 0x7D, 0xC6, 0xDE, 0xC1, 0x8B, 0xB9, 0x20 },
+ },
+ {
+ { 0x28, 0x07, 0x10, 0x60, 0x44, 0x03, 0x45, 0xD0, 0x0E, 0x80, 0xB9, 0xD7, 0xCB, 0xE1, 0x87, 0xC1,
+ 0xD8, 0xB0, 0xF2, 0xEF, 0x5D, 0x0A, 0xAC, 0x9C, 0xCE, 0xEF, 0x9A, 0x8C, 0x5A, 0x06, 0xF3, 0x02 },
+ },
+ {
+ { 0x28, 0xD9, 0x51, 0x84, 0xB5, 0xEA, 0x14, 0x0F, 0x47, 0x4F, 0x3A, 0xF6, 0xCE, 0x70, 0x52, 0xE8,
+ 0x59, 0x3C, 0xF3, 0xA5, 0x01, 0x0F, 0x52, 0x24, 0x1A, 0x1E, 0x36, 0x64, 0x60, 0xE5, 0x91, 0x9E },
+ },
+ {
+ { 0x29, 0x01, 0x93, 0xE3, 0x7A, 0x38, 0x87, 0xFD, 0x36, 0x15, 0xDF, 0x12, 0x2E, 0x95, 0x21, 0x17,
+ 0x42, 0x15, 0xEE, 0x68, 0xF7, 0x44, 0xB2, 0xFA, 0x35, 0xD2, 0x9C, 0x5D, 0xF1, 0x08, 0xF5, 0x5B },
+ },
+ {
+ { 0x2A, 0x0F, 0x70, 0x67, 0x6E, 0x18, 0x4D, 0x49, 0x39, 0xA4, 0x04, 0xDE, 0x35, 0xAC, 0x84, 0xAB,
+ 0x81, 0xAF, 0xEC, 0x36, 0x17, 0xE7, 0xE1, 0xBF, 0x34, 0x67, 0xD4, 0x19, 0x25, 0x5D, 0xD8, 0x17 },
+ },
+ {
+ { 0x2A, 0xA6, 0x47, 0x8C, 0xC7, 0x5D, 0x67, 0xA8, 0xCA, 0x55, 0xB2, 0xE1, 0x63, 0xFD, 0xBB, 0xBC,
+ 0x9D, 0x74, 0xB4, 0xE5, 0xF3, 0x7B, 0x7D, 0xBD, 0x13, 0xC9, 0x4E, 0x85, 0x8D, 0x40, 0xDA, 0xD0 },
+ },
+ {
+ { 0x2B, 0xF1, 0xE3, 0xF0, 0x37, 0x5A, 0x9A, 0x21, 0xC0, 0x7A, 0x92, 0x18, 0x04, 0x2F, 0x18, 0x77,
+ 0x3F, 0x43, 0xEA, 0xB0, 0xF5, 0xC0, 0x00, 0x26, 0x45, 0x40, 0x48, 0x2F, 0x04, 0xAE, 0x18, 0xEF },
+ },
+ {
+ { 0x2C, 0x82, 0x47, 0x4F, 0x0E, 0xF6, 0xCB, 0x65, 0x0A, 0x13, 0xEF, 0x20, 0x99, 0x6E, 0x65, 0x7B,
+ 0x67, 0x24, 0xF0, 0xA0, 0xD5, 0xEE, 0x24, 0x6D, 0x26, 0xBB, 0xFA, 0x0A, 0xBB, 0x2C, 0x22, 0xE1 },
+ },
+ {
+ { 0x2C, 0x9B, 0xE1, 0x2D, 0xA4, 0x99, 0xEA, 0xBB, 0x2F, 0xFD, 0xF9, 0x91, 0x6F, 0x2B, 0x27, 0x18,
+ 0x81, 0x19, 0x5B, 0x74, 0x19, 0xBD, 0x1E, 0xEF, 0x8D, 0x50, 0x77, 0x2A, 0xB9, 0x46, 0x4A, 0xA8 },
+ },
+ {
+ { 0x2C, 0xBD, 0xD5, 0x6C, 0xE4, 0xB4, 0x06, 0x09, 0xE9, 0xAA, 0x52, 0x1E, 0xAA, 0x76, 0xAC, 0x7E,
+ 0x55, 0x73, 0x7B, 0xF4, 0x3E, 0x2B, 0x0C, 0x30, 0xDD, 0xCF, 0x59, 0x87, 0x2E, 0xAB, 0xE7, 0x7B },
+ },
+ {
+ { 0x2D, 0xDE, 0xE4, 0x5F, 0x72, 0x78, 0x38, 0xDE, 0xAD, 0xE6, 0x7E, 0x9C, 0xA7, 0x05, 0xEB, 0xB4,
+ 0xC2, 0xE9, 0x40, 0xAE, 0x1B, 0x9D, 0x62, 0x35, 0x72, 0x18, 0x04, 0x58, 0x31, 0xE9, 0x8F, 0xDE },
+ },
+ {
+ { 0x2E, 0x5D, 0xD2, 0x55, 0x09, 0x6D, 0x64, 0x83, 0x10, 0x5C, 0xB6, 0x03, 0x6C, 0x59, 0x17, 0x57,
+ 0xFD, 0x98, 0x49, 0x70, 0x66, 0x05, 0x3F, 0x83, 0x39, 0xE4, 0xD8, 0xD0, 0xC3, 0x75, 0x49, 0x03 },
+ },
+ {
+ { 0x2E, 0xD2, 0x05, 0x8F, 0x39, 0xEA, 0xBA, 0x5C, 0xB3, 0xD7, 0xDF, 0x24, 0xCA, 0x74, 0xA7, 0x7D,
+ 0xDC, 0x12, 0x06, 0x01, 0x52, 0x7B, 0x0F, 0x51, 0x06, 0x91, 0x05, 0xCA, 0x88, 0x37, 0x6E, 0x20 },
+ },
+ {
+ { 0x30, 0x7B, 0x09, 0x34, 0xEF, 0x97, 0x85, 0xE7, 0x08, 0xED, 0x48, 0x1A, 0x99, 0x7A, 0x8A, 0x88,
+ 0xB7, 0xBF, 0x22, 0xDD, 0x26, 0xAA, 0x17, 0x17, 0x31, 0xB8, 0xF7, 0xE0, 0xD5, 0x97, 0xB7, 0x08 },
+ },
+ {
+ { 0x30, 0xCB, 0x41, 0x11, 0xFB, 0x10, 0x08, 0x6F, 0xC6, 0xA4, 0x1F, 0x04, 0xB7, 0xE9, 0xD4, 0xCF,
+ 0x66, 0x10, 0xBB, 0x06, 0x59, 0xD8, 0xE2, 0xAC, 0x80, 0x4F, 0xC8, 0x96, 0xB0, 0x25, 0x42, 0xBB },
+ },
+ {
+ { 0x30, 0xE0, 0x69, 0x80, 0x9C, 0x79, 0x90, 0xF0, 0xB5, 0xF2, 0x66, 0xE8, 0x94, 0x59, 0x96, 0x42,
+ 0xE8, 0x53, 0x50, 0xAB, 0x82, 0x81, 0x05, 0x34, 0xC7, 0xF3, 0xFD, 0x67, 0x0C, 0x1B, 0xEB, 0x18 },
+ },
+ {
+ { 0x31, 0x53, 0x47, 0x52, 0xB6, 0xF5, 0x48, 0x20, 0x91, 0x5C, 0x39, 0x5B, 0xEE, 0x97, 0x5B, 0xC5,
+ 0x4E, 0x3F, 0x07, 0xC0, 0x8C, 0xD3, 0x4C, 0x5A, 0x51, 0x15, 0xDE, 0xF0, 0x17, 0xDB, 0x2B, 0x54 },
+ },
+ {
+ { 0x31, 0xB8, 0x3E, 0x01, 0x90, 0x98, 0x95, 0xBC, 0x74, 0x2D, 0x6B, 0xE8, 0x40, 0x0A, 0xDE, 0x51,
+ 0xB2, 0x09, 0x83, 0xF6, 0x83, 0xA2, 0xAA, 0xEE, 0xB2, 0x5F, 0x58, 0xDF, 0x98, 0x1B, 0xDE, 0x0D },
+ },
+ {
+ { 0x32, 0xEF, 0x13, 0x33, 0x86, 0xBF, 0x0C, 0x63, 0xCF, 0x29, 0xD6, 0x2B, 0x0D, 0x76, 0x88, 0x9E,
+ 0x9D, 0x9D, 0x53, 0x2E, 0xE4, 0x90, 0x38, 0x94, 0x4D, 0xBC, 0x21, 0x49, 0xD8, 0xCA, 0xA5, 0xD1 },
+ },
+ {
+ { 0x34, 0x06, 0x4F, 0xF9, 0x3B, 0x27, 0x4C, 0xF5, 0xA7, 0x24, 0xEC, 0x19, 0x64, 0x50, 0x4A, 0x71,
+ 0x0A, 0xB9, 0x7B, 0xA1, 0x10, 0x3C, 0xD9, 0xB9, 0x8C, 0x81, 0xD0, 0xAB, 0xCF, 0x3B, 0x19, 0xBD },
+ },
+ {
+ { 0x34, 0x65, 0xC2, 0xF9, 0xA0, 0xCF, 0x36, 0xE5, 0xEE, 0xF0, 0x27, 0x1C, 0x52, 0x91, 0x2D, 0x58,
+ 0x6F, 0xB2, 0x0B, 0x94, 0x43, 0xE7, 0xD5, 0x82, 0xA3, 0xE2, 0x23, 0x93, 0xFA, 0xC8, 0x1B, 0xB4 },
+ },
+ {
+ { 0x35, 0x98, 0x10, 0xFF, 0xFE, 0xD1, 0x3A, 0x2C, 0x25, 0xCD, 0x91, 0xFC, 0xF0, 0x85, 0x59, 0x33,
+ 0xC9, 0x94, 0xA9, 0xDF, 0xC9, 0x39, 0x2D, 0x97, 0x07, 0xC3, 0xC0, 0xE7, 0x30, 0x0F, 0x90, 0x8D },
+ },
+ {
+ { 0x35, 0xDF, 0x79, 0x2B, 0x10, 0x0A, 0x79, 0xA6, 0x5C, 0x44, 0x87, 0x04, 0x30, 0x9D, 0xD6, 0x4B,
+ 0x54, 0x39, 0x4E, 0xBA, 0xE8, 0xC4, 0x8C, 0x3B, 0xD5, 0xDE, 0xE9, 0xCC, 0x68, 0x7D, 0x60, 0x34 },
+ },
+ {
+ { 0x36, 0x45, 0xEF, 0x7F, 0x5D, 0x15, 0xA5, 0x46, 0x7E, 0x85, 0x30, 0x7D, 0xDA, 0x15, 0xCB, 0xBB,
+ 0x55, 0xB7, 0x30, 0xAE, 0xF8, 0xEF, 0x9C, 0x71, 0x5D, 0x7D, 0x9F, 0xB4, 0x7F, 0xDF, 0x33, 0xAD },
+ },
+ {
+ { 0x36, 0xB4, 0xFE, 0x74, 0x3B, 0x6D, 0xF4, 0x4A, 0x71, 0x3E, 0x91, 0x4C, 0xAB, 0xFB, 0xF2, 0xBE,
+ 0x60, 0x24, 0x9B, 0x46, 0x43, 0x4D, 0x04, 0x43, 0x59, 0x12, 0x5A, 0x10, 0x6A, 0x37, 0xEB, 0x1C },
+ },
+ {
+ { 0x36, 0xF5, 0xA9, 0x7D, 0x79, 0x3F, 0x84, 0x97, 0x44, 0xD6, 0xAB, 0x39, 0xB7, 0xA8, 0x18, 0xF8,
+ 0x17, 0x6E, 0x65, 0x20, 0xDC, 0x86, 0x3D, 0xCE, 0x43, 0xB3, 0x98, 0xC3, 0x0B, 0x5E, 0xDB, 0x09 },
+ },
+ {
+ { 0x38, 0x23, 0x4E, 0x55, 0x9D, 0x30, 0x27, 0xD1, 0x61, 0xDA, 0x8C, 0x98, 0x88, 0x04, 0x9A, 0x4D,
+ 0x20, 0xAC, 0xF2, 0x00, 0x90, 0xAD, 0x1A, 0x22, 0x2B, 0x73, 0x9A, 0xC8, 0x6E, 0xB7, 0x6F, 0x06 },
+ },
+ {
+ { 0x39, 0x02, 0x27, 0xCE, 0x88, 0x1C, 0x71, 0x8B, 0x59, 0xA6, 0xBC, 0x31, 0x90, 0xD5, 0x17, 0xE7,
+ 0x1E, 0x1E, 0x58, 0x66, 0x93, 0xC8, 0xBF, 0x8A, 0x30, 0x27, 0x26, 0x20, 0x13, 0xFE, 0x16, 0x63 },
+ },
+ {
+ { 0x39, 0x21, 0x5C, 0xAA, 0x37, 0x1A, 0xBE, 0x57, 0x6A, 0xB9, 0x3B, 0x18, 0xC2, 0xF3, 0x75, 0x5E,
+ 0xE2, 0x6F, 0x8C, 0x3A, 0xDB, 0x75, 0x9B, 0x6F, 0x34, 0x78, 0x9F, 0xB8, 0xEC, 0xF0, 0x54, 0x28 },
+ },
+ {
+ { 0x39, 0x7B, 0xA8, 0x8A, 0x05, 0xDA, 0xFD, 0x7D, 0x58, 0xFA, 0xCF, 0x45, 0x60, 0xA6, 0x88, 0xAB,
+ 0xEE, 0xD2, 0x13, 0xE0, 0xF8, 0x8C, 0x76, 0xB6, 0x2A, 0xB2, 0xFD, 0xE3, 0x67, 0xC3, 0x2D, 0x32 },
+ },
+ {
+ { 0x39, 0x7D, 0x00, 0x6E, 0xF8, 0xAF, 0xB2, 0x0F, 0x43, 0x61, 0xA6, 0xC9, 0x72, 0xF0, 0xC5, 0x7C,
+ 0xC0, 0x87, 0x74, 0x01, 0x06, 0x12, 0x78, 0x3F, 0xBA, 0xBC, 0xB8, 0xD6, 0xF6, 0x03, 0x9E, 0x2C },
+ },
+ {
+ { 0x3A, 0xCF, 0x85, 0x3C, 0x4E, 0x45, 0x02, 0xBD, 0x82, 0xD5, 0x85, 0xD5, 0xE0, 0x82, 0xC4, 0xB3,
+ 0xAD, 0x03, 0xCD, 0xB6, 0xB5, 0x05, 0xCA, 0x80, 0x47, 0x19, 0x88, 0xEC, 0x4C, 0x58, 0x99, 0x9E },
+ },
+ {
+ { 0x3A, 0xEA, 0x2C, 0xEF, 0xAE, 0x63, 0x44, 0xFF, 0xAE, 0x67, 0x49, 0x4C, 0x68, 0x4E, 0x1E, 0xBF,
+ 0x87, 0x95, 0x40, 0xB5, 0x3D, 0x40, 0xF5, 0x16, 0x9F, 0x78, 0x89, 0x7F, 0x1B, 0x38, 0xAB, 0x66 },
+ },
+ {
+ { 0x3B, 0x47, 0x85, 0x0B, 0xF8, 0x4C, 0x4C, 0xF2, 0xCA, 0x6C, 0x31, 0xB3, 0x78, 0x39, 0xC9, 0x50,
+ 0x76, 0x63, 0x70, 0xD7, 0xF4, 0xB6, 0x4A, 0xD0, 0x18, 0x55, 0xCA, 0xCF, 0xE3, 0x51, 0x2F, 0xC3 },
+ },
+ {
+ { 0x3B, 0x6E, 0x3B, 0xB7, 0x00, 0x04, 0xBD, 0x78, 0xC9, 0x69, 0xA7, 0xFB, 0xD5, 0x11, 0x33, 0xA2,
+ 0xB3, 0xC4, 0xDF, 0xB6, 0xBA, 0x38, 0x5D, 0xCE, 0x3F, 0xB8, 0x4D, 0x73, 0x6B, 0xEA, 0xB1, 0xD9 },
+ },
+ {
+ { 0x3B, 0xAA, 0x31, 0x31, 0x70, 0x68, 0xAC, 0xE0, 0x89, 0xAE, 0xB4, 0xA8, 0x8D, 0x7E, 0xDE, 0xBE,
+ 0x94, 0xAB, 0x4A, 0xCE, 0x46, 0xBB, 0xD2, 0x68, 0x3E, 0x3F, 0xDF, 0xF5, 0x59, 0x30, 0x0F, 0x93 },
+ },
+ {
+ { 0x3C, 0x38, 0x36, 0x2E, 0x16, 0x8B, 0xB4, 0xA7, 0x59, 0xC4, 0x80, 0x55, 0x1C, 0xB1, 0x65, 0x6F,
+ 0x6A, 0x96, 0x8B, 0x9B, 0x43, 0xCB, 0xE0, 0xD7, 0x39, 0x75, 0x4A, 0xB7, 0x8A, 0x28, 0x87, 0x0E },
+ },
+ {
+ { 0x3D, 0x14, 0x47, 0x2D, 0xCE, 0x4A, 0xFD, 0xC2, 0x27, 0x6C, 0x81, 0x47, 0x97, 0xC7, 0xBC, 0x7A,
+ 0x6C, 0x14, 0xF7, 0x95, 0x3E, 0x7E, 0x9F, 0xEA, 0x69, 0x51, 0x04, 0x0F, 0x2D, 0xAF, 0xBE, 0x9A },
+ },
+ {
+ { 0x3E, 0x8E, 0x9B, 0xAD, 0x8E, 0xD9, 0xB5, 0x72, 0x38, 0x2E, 0x59, 0x8D, 0x2D, 0x73, 0x67, 0xE1,
+ 0xFD, 0x6A, 0xF6, 0x95, 0x25, 0x00, 0x9D, 0x67, 0xB4, 0xE8, 0xAF, 0x80, 0xD9, 0x15, 0x85, 0x49 },
+ },
+ {
+ { 0x3F, 0x27, 0xBD, 0xCA, 0x9B, 0x0E, 0x42, 0xF3, 0xF6, 0xD0, 0x91, 0x2C, 0x92, 0xE2, 0xDA, 0x65,
+ 0xCB, 0x35, 0x8F, 0x0B, 0x8F, 0x80, 0x5B, 0xEC, 0x5D, 0xE9, 0x32, 0x51, 0xD9, 0xC4, 0xB1, 0x99 },
+ },
+ {
+ { 0x3F, 0x2E, 0xA6, 0x4E, 0xFB, 0xD6, 0xBF, 0xC4, 0x0A, 0xF0, 0xAD, 0x46, 0xA4, 0xA2, 0x57, 0x84,
+ 0x19, 0xD8, 0x68, 0x6E, 0x38, 0x98, 0x8B, 0x91, 0x47, 0x01, 0x8C, 0x36, 0x29, 0x31, 0xE4, 0xF9 },
+ },
+ {
+ { 0x3F, 0x4F, 0x28, 0x8B, 0xAF, 0x5B, 0xDE, 0x86, 0x72, 0xD6, 0xAD, 0xD1, 0x50, 0xE3, 0x23, 0x79,
+ 0x49, 0x9A, 0x16, 0xC5, 0x81, 0xFB, 0x77, 0x37, 0xEC, 0x49, 0x80, 0xE4, 0xF9, 0xC3, 0x3D, 0x4D },
+ },
+ {
+ { 0x3F, 0x92, 0x54, 0x89, 0x64, 0xCC, 0xDE, 0xFB, 0x29, 0x96, 0x5A, 0x27, 0xC1, 0x6C, 0x2F, 0xED,
+ 0x28, 0xD9, 0xB9, 0x14, 0x0E, 0x4F, 0xB5, 0x5B, 0x37, 0x22, 0x4C, 0x67, 0xB2, 0xA0, 0x55, 0x1F },
+ },
+ {
+ { 0x40, 0x58, 0xEC, 0x4A, 0x7A, 0x7B, 0xA0, 0xB8, 0x65, 0xA7, 0x39, 0xA0, 0x0C, 0x85, 0xF3, 0x44,
+ 0x58, 0x79, 0xD6, 0x5E, 0x1D, 0x42, 0x2E, 0xED, 0x07, 0x65, 0x5A, 0x8E, 0x3E, 0xC3, 0x18, 0xCF },
+ },
+ {
+ { 0x41, 0x29, 0x6B, 0x9F, 0xAA, 0xD6, 0x41, 0x33, 0xFC, 0xCB, 0xA6, 0xBA, 0x74, 0x54, 0x11, 0xEC,
+ 0xC9, 0x11, 0xFD, 0x8E, 0xD5, 0x41, 0x90, 0x0F, 0x9E, 0x20, 0x36, 0x08, 0xEE, 0xA3, 0x59, 0x2D },
+ },
+ {
+ { 0x41, 0x88, 0x71, 0x80, 0x7E, 0xDC, 0xED, 0xA8, 0x57, 0xD7, 0xE8, 0x48, 0x31, 0x71, 0x81, 0xE1,
+ 0xE8, 0x33, 0xF5, 0x4C, 0x89, 0xA6, 0x11, 0xA2, 0x30, 0xAD, 0x99, 0x06, 0x5D, 0x45, 0x86, 0x95 },
+ },
+ {
+ { 0x41, 0xA6, 0x8D, 0xFD, 0x90, 0xDA, 0x6D, 0x12, 0x09, 0x84, 0x85, 0xBF, 0x6F, 0x87, 0x24, 0x5F,
+ 0x4E, 0xC0, 0x54, 0x71, 0xDA, 0x59, 0xD0, 0x81, 0x06, 0x01, 0x53, 0xA2, 0x22, 0x25, 0x23, 0x7F },
+ },
+ {
+ { 0x42, 0x08, 0x71, 0xD8, 0xAC, 0x49, 0x3C, 0xF9, 0x46, 0x8B, 0xB3, 0x76, 0x97, 0x6D, 0x65, 0x5E,
+ 0xF0, 0xAF, 0xAA, 0xC2, 0x3D, 0x77, 0x00, 0x92, 0x20, 0xC3, 0xAF, 0x8B, 0xDD, 0x37, 0x5A, 0x24 },
+ },
+ {
+ { 0x42, 0x5D, 0x4E, 0xBF, 0x1B, 0xDE, 0x0B, 0xF8, 0xD1, 0xDB, 0xD3, 0x3D, 0x8D, 0x16, 0x34, 0xC4,
+ 0xFA, 0xFE, 0xB6, 0xF8, 0x05, 0xF1, 0xCC, 0xB5, 0x34, 0xAC, 0xB7, 0x2A, 0xED, 0xA2, 0xCD, 0x0A },
+ },
+ {
+ { 0x44, 0x12, 0x63, 0x80, 0xA0, 0x73, 0xFE, 0xA1, 0xA2, 0x00, 0x4F, 0x71, 0x1D, 0xF2, 0xCA, 0x47,
+ 0xC2, 0xC4, 0xB4, 0xFF, 0x64, 0x4E, 0x76, 0xAF, 0xBE, 0x27, 0x97, 0xC9, 0x63, 0x7C, 0x6A, 0xF9 },
+ },
+ {
+ { 0x44, 0x25, 0xDD, 0xFB, 0xBA, 0xFB, 0xE1, 0xAA, 0xCE, 0x25, 0x85, 0x70, 0x48, 0x96, 0x9D, 0xC8,
+ 0x9D, 0xF5, 0x97, 0x7B, 0xB2, 0xE3, 0x34, 0x7C, 0x9C, 0xEB, 0x0E, 0x5A, 0x7B, 0x68, 0xC5, 0x31 },
+ },
+ {
+ { 0x45, 0x63, 0xCF, 0x13, 0xC2, 0x49, 0x2C, 0xAA, 0x92, 0xF5, 0x5B, 0x17, 0x26, 0x3A, 0xDD, 0x72,
+ 0x04, 0xA8, 0x0F, 0xE6, 0x24, 0x0C, 0x4D, 0x63, 0xE8, 0x39, 0x59, 0x58, 0xF6, 0x94, 0xCD, 0x33 },
+ },
+ {
+ { 0x45, 0xCB, 0x86, 0xCA, 0x97, 0x52, 0x29, 0xB7, 0xD5, 0xDA, 0xFC, 0x05, 0xEB, 0x0C, 0x53, 0x65,
+ 0x82, 0x3A, 0x91, 0xA9, 0x8B, 0x7D, 0xBE, 0x81, 0xAB, 0x5F, 0x17, 0x8B, 0x2D, 0xA4, 0xAD, 0x9E },
+ },
+ {
+ { 0x46, 0x9B, 0xD8, 0x04, 0xE9, 0x98, 0xAE, 0x27, 0x9A, 0xC3, 0xFE, 0x1B, 0x52, 0x88, 0x46, 0xE7,
+ 0xAE, 0xC7, 0x6C, 0x56, 0xB8, 0x0B, 0x40, 0xF3, 0x24, 0x20, 0x8F, 0x5A, 0x9F, 0x64, 0x5C, 0xB5 },
+ },
+ {
+ { 0x46, 0xCD, 0x08, 0x08, 0x8D, 0x36, 0x06, 0x2C, 0x56, 0x71, 0x09, 0x2C, 0x02, 0x76, 0x7A, 0x25,
+ 0x0D, 0xE7, 0x0B, 0xF3, 0xE1, 0x53, 0x63, 0x69, 0x66, 0xE6, 0x6E, 0xC5, 0x7E, 0x8E, 0xE9, 0xF5 },
+ },
+ {
+ { 0x47, 0x84, 0xF6, 0xCD, 0x59, 0x3D, 0x7B, 0x31, 0x2E, 0xB1, 0xF6, 0x19, 0xE1, 0x11, 0xDF, 0x3B,
+ 0x48, 0x6D, 0x1B, 0xF8, 0x37, 0x15, 0xAD, 0x8D, 0xAB, 0xA5, 0x72, 0xAF, 0xB2, 0x61, 0xD5, 0xBE },
+ },
+ {
+ { 0x48, 0xC5, 0xD4, 0xFF, 0x5D, 0x08, 0x4A, 0xC1, 0x95, 0xB1, 0xA6, 0xA2, 0x19, 0xF8, 0x1B, 0xBD,
+ 0xF9, 0xD2, 0xE5, 0xC0, 0x70, 0xEC, 0x97, 0xDF, 0x3C, 0xB0, 0xB7, 0x3E, 0xF4, 0x70, 0xDC, 0xE9 },
+ },
+ {
+ { 0x49, 0xDC, 0xF8, 0xFA, 0x68, 0xE9, 0x2B, 0x5C, 0x21, 0xFE, 0xF9, 0x3D, 0x26, 0x0C, 0x24, 0x8C,
+ 0xE3, 0xBE, 0x98, 0x62, 0x68, 0x68, 0xE7, 0x5A, 0x3F, 0x63, 0x34, 0xBB, 0x7D, 0xC1, 0x81, 0xEC },
+ },
+ {
+ { 0x4B, 0x1F, 0xC8, 0x2D, 0x24, 0x72, 0x92, 0x7A, 0xC1, 0x7C, 0x58, 0x43, 0x07, 0xCB, 0x96, 0xD6,
+ 0xFD, 0xDB, 0x8D, 0x50, 0xA5, 0x29, 0x53, 0x07, 0xD3, 0x0C, 0x75, 0x88, 0x59, 0x6A, 0xD4, 0x0B },
+ },
+ {
+ { 0x4B, 0x35, 0x02, 0xFF, 0xAD, 0x64, 0x16, 0x39, 0x4F, 0x2F, 0x78, 0x47, 0x76, 0x13, 0x39, 0x69,
+ 0xA5, 0x5C, 0xA8, 0xF3, 0x9F, 0x78, 0x3C, 0x26, 0x0F, 0xFE, 0xDB, 0xA8, 0xFC, 0xE4, 0x19, 0x70 },
+ },
+ {
+ { 0x4B, 0x51, 0xFC, 0x11, 0x4B, 0xAC, 0x8E, 0x2D, 0x2A, 0xF2, 0xAE, 0x56, 0x84, 0x42, 0x9C, 0xCA,
+ 0xAB, 0x21, 0x39, 0xC9, 0xB3, 0x51, 0xBF, 0x7E, 0x1B, 0x03, 0x0A, 0xE8, 0x62, 0x4A, 0xC1, 0x72 },
+ },
+ {
+ { 0x4C, 0xD0, 0xD6, 0x7E, 0xCC, 0x3B, 0x01, 0xC8, 0xC2, 0x63, 0x4E, 0x7A, 0x73, 0x76, 0x12, 0xF6,
+ 0x3A, 0x17, 0xFF, 0x51, 0x0A, 0x77, 0xA8, 0x04, 0xBB, 0x33, 0x1B, 0x2B, 0xE5, 0x8D, 0xFE, 0x0C },
+ },
+ {
+ { 0x4D, 0xCF, 0xEB, 0xDC, 0x15, 0x4B, 0x0C, 0x85, 0x46, 0x7F, 0x6F, 0x52, 0xAD, 0x80, 0x4E, 0x19,
+ 0x1D, 0x5B, 0xC8, 0x13, 0x51, 0x72, 0x0E, 0xC0, 0xD1, 0x9B, 0xD2, 0x5B, 0xF8, 0xF0, 0xA5, 0x53 },
+ },
+ {
+ { 0x4F, 0x19, 0xDD, 0x12, 0x92, 0x4C, 0xE0, 0xC1, 0x4F, 0x82, 0xC0, 0x56, 0xC7, 0xD4, 0x2B, 0xAC,
+ 0x43, 0xD0, 0x13, 0x3A, 0xAF, 0x89, 0xC1, 0xEF, 0xDC, 0xFA, 0x3C, 0x3E, 0x47, 0x09, 0x7D, 0x59 },
+ },
+ {
+ { 0x4F, 0xFB, 0x59, 0x19, 0xBC, 0x38, 0x5C, 0x8C, 0x58, 0xE4, 0x62, 0xBF, 0x13, 0x22, 0x10, 0xD8,
+ 0xB7, 0x86, 0x12, 0xD0, 0xC2, 0x2A, 0x6B, 0x6A, 0x68, 0x2E, 0x0B, 0x9E, 0x9C, 0x9F, 0x9A, 0x44 },
+ },
+ {
+ { 0x50, 0xF4, 0x78, 0x1E, 0xB1, 0xC1, 0x46, 0x70, 0xD9, 0xA5, 0x52, 0xC3, 0x49, 0x5F, 0xB9, 0xF6,
+ 0xAE, 0x86, 0x8A, 0xB1, 0xC9, 0xD9, 0x83, 0xE0, 0x82, 0x68, 0x65, 0xA1, 0x02, 0xEC, 0xA6, 0xD3 },
+ },
+ {
+ { 0x51, 0x6A, 0x2F, 0x33, 0x60, 0xC7, 0x6F, 0xC4, 0x6A, 0xB2, 0x88, 0x7F, 0x88, 0xE8, 0xD0, 0x8E,
+ 0xFB, 0xD8, 0x44, 0x5A, 0xA7, 0xBB, 0xD2, 0x29, 0xDF, 0xC7, 0x1A, 0x90, 0x4F, 0x55, 0xAE, 0xB4 },
+ },
+ {
+ { 0x52, 0x1F, 0x6C, 0x6A, 0x84, 0x36, 0x65, 0x79, 0xCA, 0x2D, 0xEA, 0xEB, 0x23, 0x15, 0xBF, 0x8E,
+ 0x53, 0x1C, 0x9F, 0xA4, 0x7B, 0x89, 0x9D, 0xA2, 0x72, 0x16, 0xA9, 0x98, 0x82, 0x86, 0xAF, 0xE5 },
+ },
+ {
+ { 0x52, 0xFF, 0x8B, 0x6E, 0x98, 0xB0, 0x96, 0x19, 0x90, 0x03, 0xDE, 0x97, 0xBC, 0xCF, 0xD2, 0xA7,
+ 0xF1, 0xAC, 0x57, 0xA8, 0x31, 0x35, 0xB9, 0x55, 0xFF, 0x68, 0x63, 0x36, 0xA6, 0x91, 0xD5, 0xCA },
+ },
+ {
+ { 0x53, 0x79, 0x64, 0x58, 0xDA, 0x97, 0xCE, 0x36, 0x78, 0xF2, 0xD1, 0xD9, 0xB2, 0xA5, 0xB2, 0xFB,
+ 0x30, 0x75, 0xEA, 0xFA, 0xF6, 0xFF, 0x04, 0x78, 0xB5, 0x72, 0xDD, 0xFD, 0x70, 0x99, 0xAE, 0xE2 },
+ },
+ {
+ { 0x53, 0x9C, 0xA9, 0xE1, 0xF0, 0x6A, 0xF2, 0x10, 0x7F, 0x96, 0xBF, 0x4B, 0x7D, 0xD4, 0xCE, 0xCD,
+ 0x9E, 0xD1, 0x1A, 0x38, 0xD6, 0x70, 0x91, 0x69, 0x9C, 0x56, 0x26, 0xE2, 0x7A, 0x1F, 0x54, 0xA5 },
+ },
+ {
+ { 0x55, 0x21, 0xF9, 0x63, 0x57, 0x81, 0x58, 0xB8, 0xD0, 0xE7, 0xC4, 0x91, 0xCD, 0xB8, 0x5C, 0x3D,
+ 0xE9, 0xD5, 0x2E, 0xA5, 0x1F, 0xFC, 0xB0, 0x93, 0xD3, 0x12, 0x28, 0x11, 0x13, 0x14, 0x97, 0xEB },
+ },
+ {
+ { 0x55, 0xD0, 0xEB, 0xE3, 0x2C, 0xBA, 0x09, 0xF6, 0x58, 0x4D, 0x9E, 0x7B, 0x57, 0x92, 0xA4, 0x03,
+ 0xC2, 0x1D, 0x39, 0xD6, 0xE1, 0xF5, 0xE8, 0xED, 0x37, 0xB9, 0x3F, 0xA6, 0x1D, 0x88, 0x35, 0x16 },
+ },
+ {
+ { 0x58, 0x1A, 0xDE, 0x64, 0x84, 0x95, 0xB4, 0xB1, 0x62, 0x9C, 0x3C, 0x7C, 0x78, 0xEF, 0xBE, 0xF2,
+ 0x75, 0x06, 0x56, 0x65, 0xB2, 0x41, 0x1C, 0x0E, 0x5F, 0xCF, 0xBC, 0x7E, 0xB4, 0xBE, 0x34, 0x0B },
+ },
+ {
+ { 0x59, 0xC9, 0xE8, 0xDF, 0x03, 0x0B, 0x1C, 0xD5, 0x89, 0xA8, 0xB3, 0x4F, 0xE7, 0x42, 0x51, 0xEA,
+ 0xD5, 0xA5, 0xFB, 0xE9, 0xE6, 0x13, 0x67, 0xCA, 0x76, 0xAF, 0xD9, 0xDD, 0xD9, 0xC6, 0xF1, 0x6F },
+ },
+ {
+ { 0x59, 0xE9, 0xFA, 0x2F, 0xF0, 0x76, 0x89, 0x33, 0x28, 0x33, 0xC6, 0x40, 0xF5, 0x05, 0xFA, 0x24,
+ 0x09, 0xEB, 0x88, 0x93, 0x32, 0x57, 0xC1, 0x93, 0xB0, 0x07, 0xD3, 0xA2, 0x89, 0x6A, 0x98, 0x50 },
+ },
+ {
+ { 0x59, 0xEE, 0x9B, 0x36, 0x80, 0xAE, 0x20, 0x56, 0x83, 0x9C, 0x0B, 0xF6, 0x9E, 0xE6, 0x63, 0x26,
+ 0x57, 0x16, 0xA8, 0xE2, 0x4C, 0xC6, 0x49, 0x95, 0xFB, 0xA6, 0xCB, 0x6F, 0x0C, 0x12, 0x39, 0xDC },
+ },
+ {
+ { 0x5A, 0x84, 0xAF, 0xE6, 0x74, 0x05, 0xAB, 0xE8, 0x4A, 0x0C, 0xD4, 0x2C, 0x2B, 0xA2, 0xE4, 0xC8,
+ 0x8F, 0x35, 0xE0, 0xA5, 0x95, 0xE5, 0x69, 0xA3, 0xE1, 0x86, 0x69, 0x44, 0x40, 0x5B, 0xE7, 0x36 },
+ },
+ {
+ { 0x5A, 0x8E, 0x86, 0x21, 0x2C, 0x06, 0x33, 0x94, 0x94, 0xF8, 0x5B, 0x5F, 0x85, 0x11, 0xDF, 0x00,
+ 0x00, 0x23, 0x94, 0x07, 0x8F, 0xFC, 0x77, 0x4D, 0x43, 0x6F, 0x0D, 0x63, 0x86, 0xD7, 0xA6, 0xF7 },
+ },
+ {
+ { 0x5A, 0xC0, 0x98, 0x2D, 0xA0, 0xC8, 0x3D, 0x0B, 0xA9, 0x38, 0x1A, 0x5C, 0xD8, 0x7B, 0x80, 0xD1,
+ 0x10, 0xF2, 0x6E, 0xE8, 0x39, 0x27, 0x1B, 0xC2, 0x70, 0x60, 0x8F, 0xD1, 0x43, 0x7F, 0x55, 0xB0 },
+ },
+ {
+ { 0x5B, 0x29, 0x3D, 0x30, 0x9F, 0x64, 0x24, 0xBC, 0x26, 0x4F, 0x4B, 0xB0, 0x18, 0xAE, 0xF5, 0x0E,
+ 0x63, 0xE3, 0x37, 0xD1, 0x4D, 0xF0, 0x64, 0xC5, 0x7A, 0x23, 0x52, 0x83, 0x42, 0x16, 0x1C, 0x68 },
+ },
+ {
+ { 0x5C, 0x7F, 0xF0, 0x55, 0xC2, 0xFD, 0x03, 0x3F, 0x34, 0xC4, 0xC4, 0xF7, 0xC4, 0xFB, 0x7D, 0xDA,
+ 0xAA, 0xFB, 0x43, 0x56, 0xC5, 0x60, 0xC9, 0x9E, 0xDF, 0xF0, 0x74, 0xDA, 0x04, 0xAF, 0x65, 0x7C },
+ },
+ {
+ { 0x5C, 0xD2, 0x44, 0x6A, 0x8E, 0x4A, 0x0F, 0xA7, 0xE3, 0xCD, 0xF8, 0x00, 0x5D, 0xED, 0xCE, 0xBA,
+ 0xE9, 0xE6, 0x81, 0x9A, 0x8A, 0x69, 0x87, 0x31, 0x55, 0x5B, 0x7D, 0xC9, 0xD0, 0xA2, 0x3F, 0xC0 },
+ },
+ {
+ { 0x5C, 0xEB, 0xEB, 0xD8, 0x34, 0x01, 0xB7, 0x0B, 0xAC, 0xB5, 0x4F, 0x66, 0xA9, 0xB7, 0x78, 0x55,
+ 0x69, 0x6E, 0xCE, 0x16, 0x7F, 0xE6, 0xC6, 0x0A, 0x05, 0x16, 0x8B, 0xE4, 0x39, 0x19, 0xC8, 0x0F },
+ },
+ {
+ { 0x5E, 0x23, 0xDB, 0xD4, 0xD0, 0xC9, 0xBF, 0xB1, 0x5F, 0x61, 0x6A, 0x95, 0x17, 0xA1, 0x30, 0xD8,
+ 0x66, 0xA8, 0xCB, 0x0B, 0x18, 0x96, 0x3D, 0x54, 0xE7, 0xED, 0xAE, 0xE2, 0x61, 0xCB, 0x1C, 0x19 },
+ },
+ {
+ { 0x5F, 0x8B, 0x88, 0x8E, 0xE9, 0x6C, 0x0C, 0x0F, 0x5A, 0x91, 0x72, 0x90, 0xAC, 0xA6, 0x5A, 0xFD,
+ 0x6E, 0xBD, 0xAE, 0x05, 0xA0, 0x2A, 0xAF, 0x04, 0x29, 0xE9, 0x72, 0xEC, 0x01, 0x90, 0xEC, 0xFC },
+ },
+ {
+ { 0x62, 0x2E, 0xC3, 0xBE, 0x7C, 0xF5, 0xE4, 0xE6, 0x3F, 0x74, 0x18, 0x69, 0x28, 0x74, 0x40, 0x05,
+ 0xCB, 0xB7, 0x8D, 0xF3, 0x06, 0xB8, 0x67, 0xC3, 0xFC, 0xAD, 0x5E, 0x2B, 0xA7, 0x53, 0x96, 0x83 },
+ },
+ {
+ { 0x62, 0x6F, 0x7E, 0xB4, 0xFD, 0x9B, 0x71, 0xFF, 0xAA, 0x0C, 0x8E, 0xC9, 0x65, 0x54, 0x64, 0xE6,
+ 0x5E, 0x7F, 0x96, 0xCF, 0xA3, 0x82, 0x73, 0x97, 0x41, 0x35, 0x66, 0xAA, 0x2C, 0xC1, 0xE5, 0x72 },
+ },
+ {
+ { 0x63, 0x64, 0x15, 0x61, 0x77, 0xDC, 0xDF, 0x60, 0x4D, 0xF9, 0x1E, 0x31, 0x32, 0x2E, 0x57, 0x74,
+ 0x69, 0x1E, 0x0C, 0x41, 0xFA, 0x0D, 0x2F, 0x25, 0x7A, 0xD7, 0xF9, 0xF0, 0x25, 0x98, 0x14, 0x45 },
+ },
+ {
+ { 0x65, 0x66, 0x00, 0xA4, 0x5E, 0x45, 0x6A, 0xBA, 0x5B, 0x00, 0x8D, 0x87, 0x91, 0x54, 0xB7, 0x69,
+ 0x0D, 0x7F, 0x27, 0x31, 0x02, 0x09, 0x7D, 0x8F, 0xD8, 0xC3, 0xDE, 0xAB, 0x30, 0xD8, 0x4A, 0xB2 },
+ },
+ {
+ { 0x65, 0xED, 0x61, 0xA8, 0x8C, 0x55, 0xEF, 0xB0, 0x38, 0x07, 0x1A, 0xEE, 0xDE, 0xF8, 0xE1, 0x83,
+ 0xE2, 0x37, 0x38, 0x46, 0x97, 0x26, 0xEB, 0x99, 0x68, 0x0C, 0xD2, 0x44, 0x72, 0x73, 0x6B, 0xEC },
+ },
+ {
+ { 0x66, 0x50, 0xB2, 0xEA, 0x64, 0x4C, 0x3F, 0x4E, 0x8C, 0x9E, 0x3C, 0x46, 0xAC, 0xEA, 0xC4, 0x52,
+ 0x33, 0xD8, 0x66, 0xE3, 0x98, 0xFF, 0x90, 0xEB, 0x59, 0xB2, 0xC6, 0x25, 0x20, 0x82, 0xAC, 0x04 },
+ },
+ {
+ { 0x66, 0xBE, 0x7E, 0xA1, 0x13, 0x8B, 0xCB, 0xA4, 0xDE, 0x0B, 0x41, 0x28, 0x5D, 0x9A, 0x13, 0x3F,
+ 0xA7, 0xF5, 0x70, 0xA3, 0xC8, 0x13, 0x55, 0x79, 0xB8, 0x60, 0x19, 0x9D, 0x0A, 0x51, 0x45, 0x7C },
+ },
+ {
+ { 0x69, 0x01, 0x4B, 0xBC, 0x84, 0x29, 0xD8, 0x5F, 0x41, 0xC2, 0x22, 0xD9, 0x7F, 0x7E, 0xD5, 0x35,
+ 0xCF, 0x81, 0x23, 0x9A, 0xF2, 0x7A, 0xCC, 0x88, 0x70, 0xDC, 0xD4, 0x08, 0x34, 0x8B, 0x48, 0xBA },
+ },
+ {
+ { 0x69, 0x21, 0x1F, 0x36, 0x3A, 0x2D, 0xBE, 0x01, 0x5B, 0x31, 0xCB, 0xD9, 0xFC, 0x5E, 0x94, 0xC2,
+ 0xF6, 0xF4, 0x3C, 0x58, 0xDB, 0xDE, 0xE9, 0xE3, 0xE4, 0x6B, 0x19, 0xD7, 0x59, 0xBB, 0xB8, 0x81 },
+ },
+ {
+ { 0x69, 0x75, 0x67, 0xBB, 0xAC, 0x94, 0xEE, 0xC3, 0xE6, 0xFA, 0x4A, 0x4E, 0x46, 0xFA, 0x51, 0x74,
+ 0x05, 0xF3, 0x77, 0xC0, 0xDE, 0xE3, 0xD4, 0x29, 0x91, 0x4E, 0x6B, 0x7E, 0xA0, 0x8C, 0xB1, 0xA6 },
+ },
+ {
+ { 0x6A, 0xAC, 0xC5, 0x09, 0x2F, 0x12, 0xBC, 0x94, 0xA0, 0xAD, 0x0E, 0x9E, 0xF6, 0x36, 0x43, 0x7D,
+ 0x36, 0x0D, 0xC7, 0xC9, 0xF1, 0x40, 0x44, 0x17, 0xA3, 0x36, 0x91, 0x94, 0x4E, 0x76, 0x31, 0x36 },
+ },
+ {
+ { 0x6B, 0x4A, 0x8C, 0xB6, 0x07, 0xF5, 0x1C, 0x83, 0x0D, 0xE7, 0x20, 0xF4, 0xBB, 0xDE, 0xDF, 0x49,
+ 0x10, 0x15, 0x13, 0xDF, 0xD1, 0xDB, 0x0B, 0x0A, 0x97, 0xCC, 0x3F, 0xDD, 0x9A, 0x39, 0xC6, 0xE7 },
+ },
+ {
+ { 0x6C, 0x8F, 0xD1, 0xE6, 0xE1, 0x1B, 0xAF, 0xA6, 0x17, 0x78, 0x13, 0xA0, 0x44, 0x40, 0xB1, 0xB9,
+ 0x6A, 0x1C, 0xDB, 0x7C, 0x2D, 0x70, 0x3F, 0x55, 0xDE, 0x85, 0x7C, 0x80, 0xA8, 0x9E, 0x73, 0x25 },
+ },
+ {
+ { 0x6C, 0xC6, 0xDC, 0xDA, 0x58, 0xC6, 0x1F, 0xB2, 0x86, 0x70, 0xD1, 0xC2, 0x01, 0x76, 0x57, 0xB0,
+ 0xC5, 0xD6, 0x1A, 0x26, 0xC9, 0xCB, 0xD1, 0xEA, 0x75, 0x5C, 0x68, 0x20, 0xB5, 0xF6, 0xD6, 0x7D },
+ },
+ {
+ { 0x6D, 0x32, 0xF4, 0x93, 0x40, 0x56, 0xEE, 0x17, 0x14, 0xCA, 0x72, 0x70, 0x3F, 0x64, 0x46, 0x9B,
+ 0x98, 0x58, 0xFC, 0x39, 0x96, 0x4B, 0x4C, 0x03, 0x93, 0xB3, 0x7D, 0xDE, 0xAB, 0x8B, 0x19, 0x75 },
+ },
+ {
+ { 0x6D, 0xC9, 0x87, 0x5C, 0xD3, 0x46, 0xA2, 0x2B, 0x47, 0xB2, 0x80, 0xB1, 0xB1, 0x45, 0x0D, 0x87,
+ 0x8E, 0x09, 0x8B, 0xB2, 0xE2, 0xA9, 0xE3, 0xC2, 0x5C, 0xC7, 0x6A, 0xFF, 0x93, 0xC0, 0xBE, 0xAB },
+ },
+ {
+ { 0x6E, 0x1A, 0x88, 0x63, 0xF2, 0x93, 0x4B, 0x39, 0x01, 0x23, 0x7E, 0x84, 0xD0, 0x76, 0x27, 0x04,
+ 0x23, 0x06, 0x78, 0x7F, 0x2D, 0xE0, 0x66, 0x30, 0xBD, 0x37, 0xD8, 0x03, 0x94, 0x35, 0xBF, 0xCA },
+ },
+ {
+ { 0x6F, 0x3B, 0xB3, 0x4B, 0x5D, 0x32, 0x91, 0xDF, 0xB3, 0xE4, 0x12, 0x71, 0xA1, 0xD7, 0x30, 0xCD,
+ 0xBC, 0xFF, 0xC1, 0x0B, 0x68, 0x05, 0x9D, 0xCC, 0xD3, 0x1C, 0x47, 0x4B, 0xB7, 0x44, 0x16, 0xE5 },
+ },
+ {
+ { 0x6F, 0xBD, 0xCD, 0xF1, 0xB4, 0x37, 0x9F, 0xC4, 0x73, 0xAB, 0x5E, 0xEA, 0x4E, 0xC2, 0xF4, 0x84,
+ 0xCE, 0x91, 0xD1, 0x0E, 0x31, 0x34, 0x5F, 0x15, 0xA7, 0x6A, 0x84, 0x85, 0xB8, 0xFF, 0xFB, 0x7E },
+ },
+ {
+ { 0x6F, 0xDC, 0x18, 0xD6, 0x55, 0x14, 0xDD, 0xCE, 0xF0, 0x2F, 0xEA, 0x81, 0x7A, 0x1B, 0x70, 0x84,
+ 0x71, 0x95, 0xFF, 0x5C, 0x07, 0xB1, 0x3D, 0x6A, 0x97, 0x1E, 0x0E, 0x77, 0x4B, 0x44, 0x10, 0xA0 },
+ },
+ {
+ { 0x70, 0xB8, 0xEC, 0xD5, 0x62, 0xEC, 0x3D, 0x9F, 0x48, 0x64, 0x75, 0x2A, 0x3A, 0x8C, 0x54, 0x39,
+ 0x93, 0xB4, 0x38, 0x72, 0x8F, 0xE2, 0x71, 0x81, 0xF4, 0xC0, 0x8D, 0xE6, 0xA0, 0xD8, 0xB7, 0x9A },
+ },
+ {
+ { 0x71, 0x1E, 0xF0, 0x96, 0x33, 0x43, 0x8A, 0xC5, 0xBE, 0x9D, 0xA8, 0x12, 0x2E, 0x7A, 0xCF, 0x0E,
+ 0xA2, 0x68, 0xB8, 0x72, 0xAD, 0xDC, 0x3E, 0xE8, 0x37, 0x2B, 0x91, 0x6D, 0x60, 0x65, 0xCF, 0xA8 },
+ },
+ {
+ { 0x72, 0x1B, 0x1F, 0x92, 0x9D, 0xA7, 0xEA, 0xF8, 0x96, 0x24, 0x64, 0x7B, 0xA3, 0xCC, 0x4E, 0x1E,
+ 0xD1, 0x57, 0x54, 0xAB, 0x83, 0x6E, 0x33, 0x58, 0xB0, 0x35, 0xA1, 0xF2, 0x27, 0x4A, 0x43, 0xBE },
+ },
+ {
+ { 0x72, 0xE7, 0x49, 0x87, 0x21, 0x0C, 0x7E, 0xF6, 0x67, 0x46, 0xE4, 0x9A, 0x96, 0xDF, 0x55, 0xCC,
+ 0x6F, 0xAD, 0xF7, 0xA6, 0x31, 0xC7, 0xAE, 0x3F, 0x3E, 0x9E, 0x18, 0x72, 0x3D, 0xE5, 0x2A, 0x6E },
+ },
+ {
+ { 0x73, 0x3B, 0x42, 0x24, 0x25, 0x8D, 0xEE, 0x07, 0x0E, 0xDF, 0xA3, 0x41, 0x1F, 0xBC, 0x9B, 0xAD,
+ 0x31, 0x65, 0xBE, 0x66, 0x0F, 0x34, 0x0A, 0xA2, 0x30, 0x8A, 0x5A, 0x33, 0x23, 0xFA, 0xBF, 0xA7 },
+ },
+ {
+ { 0x76, 0x98, 0x67, 0x60, 0xAC, 0xFE, 0x55, 0x59, 0xA2, 0xA2, 0xAB, 0x2A, 0x4E, 0x85, 0x49, 0x83,
+ 0xC5, 0xFD, 0xE6, 0x73, 0xCE, 0x8E, 0xB1, 0x71, 0x23, 0x49, 0x48, 0x64, 0x86, 0x7A, 0x98, 0xB1 },
+ },
+ {
+ { 0x77, 0xDD, 0xC8, 0x1B, 0xD2, 0x8B, 0x9D, 0x46, 0x1E, 0x7D, 0x3C, 0xD4, 0xA8, 0x12, 0x2A, 0xA9,
+ 0x8A, 0x24, 0x60, 0xFB, 0xA0, 0x8F, 0x1B, 0x7B, 0xAC, 0xB6, 0x6C, 0x92, 0xD7, 0x99, 0x1C, 0xCC },
+ },
+ {
+ { 0x78, 0x0C, 0x33, 0xFE, 0x95, 0x4C, 0xC4, 0xDB, 0x39, 0x04, 0xD7, 0x6A, 0x68, 0x58, 0xBC, 0xD1,
+ 0x01, 0x7F, 0x52, 0xDA, 0x59, 0x9D, 0x36, 0xDA, 0xE6, 0x66, 0xC0, 0x4E, 0x41, 0xAF, 0x8D, 0xCD },
+ },
+ {
+ { 0x78, 0xC9, 0x30, 0x40, 0x5A, 0x72, 0x0D, 0x9F, 0x00, 0x66, 0xDD, 0x88, 0xA2, 0xA8, 0xDA, 0xFB,
+ 0xBE, 0x6C, 0xD6, 0x5D, 0x54, 0xB7, 0x76, 0x06, 0x42, 0x1B, 0x45, 0x43, 0x8C, 0x65, 0x8A, 0xD4 },
+ },
+ {
+ { 0x79, 0x8F, 0x83, 0xB1, 0xC4, 0xC6, 0x5C, 0x4D, 0x5D, 0xEA, 0x13, 0x03, 0x53, 0x53, 0xD8, 0xED,
+ 0xE5, 0xD7, 0x1D, 0x99, 0x47, 0xF4, 0x34, 0xFD, 0xEA, 0x0D, 0xBC, 0x1E, 0xC8, 0x2F, 0x45, 0x35 },
+ },
+ {
+ { 0x79, 0xA8, 0xFC, 0x72, 0x70, 0xB2, 0xE5, 0xF3, 0x35, 0x6B, 0x09, 0xC6, 0xB8, 0x64, 0xFC, 0x92,
+ 0xE5, 0xFB, 0xC9, 0xE6, 0x9B, 0xEC, 0x93, 0xA4, 0xE3, 0x3B, 0x8D, 0xF5, 0x75, 0x60, 0x17, 0xBE },
+ },
+ {
+ { 0x7B, 0xFE, 0x47, 0xAE, 0xBA, 0x8B, 0x0A, 0x3A, 0x94, 0x5A, 0x88, 0xD8, 0xEF, 0x18, 0x91, 0xC9,
+ 0x89, 0x97, 0x8A, 0xBF, 0x12, 0x2E, 0xC5, 0xE0, 0x51, 0x4B, 0xE3, 0x6C, 0x3A, 0x7F, 0x22, 0x9B },
+ },
+ {
+ { 0x7D, 0x20, 0xC7, 0xA9, 0x27, 0x26, 0x2B, 0xE7, 0x38, 0xD2, 0x58, 0xD0, 0xFD, 0x97, 0x6E, 0x9A,
+ 0xF3, 0x6E, 0xF7, 0x99, 0x5F, 0x05, 0xE2, 0x87, 0x6A, 0x29, 0xAE, 0xBC, 0x3A, 0x24, 0xAA, 0xCE },
+ },
+ {
+ { 0x7E, 0x2E, 0xDB, 0x9D, 0x38, 0xF9, 0x29, 0x3C, 0xDD, 0xD6, 0x03, 0xB1, 0x75, 0xC9, 0xB2, 0x05,
+ 0xAC, 0x0B, 0x55, 0x3A, 0x4B, 0xF5, 0xFB, 0x08, 0xC2, 0x46, 0xEC, 0xF9, 0xC8, 0x49, 0xDB, 0x28 },
+ },
+ {
+ { 0x7F, 0x95, 0x9B, 0x06, 0x34, 0xDA, 0x94, 0xFA, 0xCA, 0xDA, 0xB0, 0x21, 0xCF, 0x94, 0x20, 0x78,
+ 0x16, 0x00, 0x36, 0x13, 0xEF, 0x09, 0xEB, 0x54, 0xF6, 0x48, 0x60, 0x50, 0x08, 0x19, 0x02, 0x75 },
+ },
+ {
+ { 0x7F, 0x9A, 0x69, 0xCF, 0xA2, 0xF5, 0x0C, 0x13, 0xE1, 0xB7, 0x11, 0xDD, 0x6B, 0x14, 0x69, 0x2B,
+ 0xDB, 0x77, 0xD9, 0xFF, 0xD8, 0xC1, 0x10, 0xAE, 0x5D, 0x05, 0xA4, 0xCB, 0x73, 0x12, 0x37, 0x48 },
+ },
+ {
+ { 0x80, 0x20, 0x56, 0xE1, 0xDB, 0x9D, 0x9B, 0x73, 0x21, 0xD1, 0xFF, 0xBB, 0xE1, 0x2F, 0x5C, 0xBE,
+ 0xDE, 0xC3, 0x6D, 0x0B, 0x5E, 0xC2, 0xA4, 0xE1, 0x8D, 0x99, 0x54, 0x36, 0x4C, 0xEC, 0x81, 0x29 },
+ },
+ {
+ { 0x80, 0x97, 0x63, 0x4C, 0xE3, 0x3D, 0x41, 0x53, 0x3D, 0x41, 0x5D, 0xAF, 0xDB, 0x8B, 0xA1, 0x91,
+ 0xC0, 0x30, 0x52, 0xAC, 0x8B, 0xAA, 0x25, 0x54, 0x34, 0x77, 0x3A, 0x16, 0x4B, 0x91, 0x1D, 0x6E },
+ },
+ {
+ { 0x80, 0xD0, 0x17, 0x09, 0x34, 0xD2, 0x2A, 0xEA, 0x73, 0x3F, 0x11, 0x5E, 0x52, 0x42, 0xC6, 0xB8,
+ 0x6D, 0x7F, 0xCF, 0xB4, 0x90, 0x4E, 0x65, 0xB7, 0xB7, 0xB9, 0x07, 0xF2, 0xCA, 0x94, 0xED, 0x71 },
+ },
+ {
+ { 0x81, 0x1D, 0xF2, 0xF4, 0x73, 0x6F, 0x85, 0x62, 0xE2, 0x02, 0xFD, 0x00, 0x75, 0x32, 0xF1, 0xDE,
+ 0x40, 0x17, 0x86, 0x1E, 0xFA, 0xBE, 0x67, 0x34, 0x20, 0xC2, 0x7F, 0x2E, 0x2A, 0x33, 0xFA, 0xC1 },
+ },
+ {
+ { 0x81, 0x1E, 0x37, 0x86, 0x37, 0xB1, 0xD2, 0xCB, 0xB1, 0x89, 0xAF, 0xD6, 0x74, 0x95, 0xFE, 0x8A,
+ 0xB9, 0xD8, 0x3A, 0x74, 0x2E, 0x35, 0x8C, 0xBB, 0xDB, 0xD1, 0x54, 0x98, 0xBF, 0x9C, 0x7B, 0x56 },
+ },
+ {
+ { 0x81, 0xA0, 0xF1, 0xD0, 0x29, 0x46, 0x8E, 0xE8, 0x66, 0x36, 0x4A, 0x19, 0x8A, 0x26, 0x08, 0x58,
+ 0x30, 0xC2, 0xA4, 0x16, 0xE4, 0x9E, 0x22, 0x4C, 0xE8, 0x09, 0x66, 0xFC, 0xC4, 0x99, 0xD6, 0x36 },
+ },
+ {
+ { 0x82, 0x56, 0x8B, 0x3B, 0xB3, 0xC6, 0x55, 0xD7, 0xF2, 0x2D, 0x8C, 0x97, 0xA5, 0x66, 0x9C, 0xC8,
+ 0x34, 0xA2, 0xDD, 0x7C, 0xDA, 0xE7, 0x5A, 0x26, 0x45, 0x59, 0x55, 0x16, 0x46, 0x55, 0x8E, 0x14 },
+ },
+ {
+ { 0x82, 0x7C, 0x8C, 0x80, 0x11, 0x1F, 0xF2, 0x21, 0xC3, 0xEB, 0x1E, 0xF5, 0xC0, 0xD5, 0xD4, 0x34,
+ 0x48, 0x31, 0x86, 0xE2, 0x09, 0x00, 0x75, 0x63, 0x15, 0x8E, 0x9E, 0x76, 0xD2, 0x79, 0x0F, 0x1C },
+ },
+ {
+ { 0x82, 0x92, 0x67, 0xC5, 0xAD, 0x70, 0xE5, 0x45, 0x18, 0x02, 0x3A, 0xB7, 0x85, 0xFA, 0x3C, 0xDE,
+ 0xD6, 0x6F, 0x42, 0x5D, 0xE1, 0xF3, 0x2F, 0xCD, 0x72, 0x1B, 0x49, 0x46, 0x3A, 0x5A, 0x5F, 0x5B },
+ },
+ {
+ { 0x83, 0x34, 0xEA, 0xB8, 0x1C, 0x60, 0x4E, 0x99, 0xD5, 0x40, 0x51, 0x3E, 0xF2, 0xE3, 0x7A, 0xBA,
+ 0x71, 0x4F, 0x07, 0xB2, 0xBA, 0x01, 0x0A, 0xD7, 0x1D, 0xC4, 0xE1, 0x1A, 0x92, 0x18, 0xC1, 0x8C },
+ },
+ {
+ { 0x83, 0x54, 0x7A, 0xCA, 0x3C, 0xED, 0x73, 0xDF, 0x99, 0x14, 0xF3, 0x15, 0x60, 0x74, 0x63, 0x79,
+ 0x29, 0x4C, 0x76, 0x0E, 0xF9, 0xA8, 0xB7, 0x6E, 0x00, 0x06, 0x46, 0xC7, 0x39, 0x07, 0x21, 0x65 },
+ },
+ {
+ { 0x83, 0x89, 0xC8, 0x79, 0xB6, 0x3B, 0x82, 0x9D, 0x2D, 0x39, 0xA8, 0xCF, 0xB7, 0x87, 0xE7, 0x72,
+ 0x77, 0xD5, 0xCF, 0xA3, 0xE3, 0x6F, 0xDA, 0xCB, 0xAB, 0x4D, 0x18, 0xB2, 0xB0, 0x4E, 0x32, 0x94 },
+ },
+ {
+ { 0x84, 0x23, 0xB3, 0xF1, 0xCC, 0x85, 0x2B, 0x49, 0xCF, 0x81, 0xB7, 0xD5, 0xFF, 0x51, 0xA7, 0xA5,
+ 0x6A, 0x84, 0x78, 0x3A, 0x2D, 0xF7, 0x43, 0x61, 0xFF, 0x2E, 0xEE, 0x0F, 0x92, 0x12, 0xC1, 0x59 },
+ },
+ {
+ { 0x84, 0x7B, 0x5F, 0x1E, 0xEB, 0x2A, 0x44, 0x13, 0xC8, 0xFA, 0x37, 0x98, 0x21, 0x97, 0x37, 0xE1,
+ 0x92, 0xBA, 0x72, 0x72, 0xA1, 0x08, 0xB7, 0x17, 0x28, 0xA8, 0xD1, 0x65, 0x17, 0xF6, 0x1E, 0x9D },
+ },
+ {
+ { 0x85, 0x31, 0xB2, 0xBF, 0xC5, 0x45, 0x79, 0xE8, 0xF1, 0x8F, 0x27, 0xB2, 0xE6, 0xEC, 0xC0, 0xF8,
+ 0x90, 0x64, 0xEE, 0x86, 0x87, 0x0E, 0xCC, 0x8B, 0xBE, 0x0C, 0xE6, 0x86, 0xEC, 0xDA, 0x2C, 0x17 },
+ },
+ {
+ { 0x85, 0x76, 0x0F, 0x59, 0x51, 0x90, 0xE9, 0xB4, 0x67, 0x8B, 0xBF, 0x44, 0xEF, 0xB5, 0xCF, 0x8F,
+ 0x6B, 0x19, 0x37, 0xA9, 0xB8, 0x6B, 0x31, 0xB7, 0x51, 0xBE, 0xCF, 0x72, 0x18, 0x03, 0xB0, 0x1C },
+ },
+ {
+ { 0x85, 0xF0, 0x79, 0x36, 0xB4, 0x29, 0x1F, 0x36, 0xD9, 0xB7, 0x5F, 0x42, 0xE8, 0xB7, 0xEE, 0x8A,
+ 0x64, 0xE6, 0x32, 0xA1, 0x18, 0x11, 0x65, 0xFE, 0x72, 0xB4, 0x88, 0x23, 0xC3, 0xD9, 0x9D, 0x9D },
+ },
+ {
+ { 0x86, 0x12, 0x9F, 0xE7, 0x61, 0x99, 0x4D, 0x7B, 0x64, 0xE4, 0x02, 0x85, 0x8F, 0x88, 0xC5, 0x2B,
+ 0x3E, 0xB9, 0xC0, 0x71, 0xFF, 0xBE, 0x80, 0x02, 0x80, 0xAC, 0x8C, 0x0C, 0x6F, 0x79, 0xE7, 0xA6 },
+ },
+ {
+ { 0x86, 0x19, 0x6B, 0x0F, 0xD3, 0x0F, 0x8F, 0x57, 0x56, 0x98, 0xB5, 0xEE, 0xF2, 0x69, 0xD0, 0x69,
+ 0x2F, 0x88, 0xAD, 0xEA, 0xC4, 0x83, 0x6A, 0x62, 0x67, 0xAB, 0xC8, 0x36, 0x23, 0x34, 0x00, 0x86 },
+ },
+ {
+ { 0x86, 0xD1, 0x8B, 0xCD, 0xDE, 0x16, 0x45, 0x42, 0x48, 0x6E, 0x56, 0x44, 0x2C, 0xE1, 0xB8, 0x8B,
+ 0x1A, 0x10, 0x73, 0x7C, 0xBD, 0x5E, 0xA4, 0xAA, 0xB8, 0xD5, 0xB8, 0xAF, 0x51, 0xF5, 0x29, 0x09 },
+ },
+ {
+ { 0x88, 0x8D, 0x6D, 0x77, 0xD8, 0x1C, 0x62, 0x91, 0xCB, 0x84, 0xD9, 0xD6, 0x56, 0x27, 0x82, 0xFD,
+ 0x2E, 0xB3, 0x42, 0x5D, 0x49, 0x1E, 0x68, 0x74, 0x20, 0x28, 0x4B, 0x76, 0xA1, 0xDE, 0xBF, 0xAB },
+ },
+ {
+ { 0x89, 0xAF, 0x0E, 0x54, 0xC7, 0x62, 0x77, 0x86, 0x93, 0x52, 0x9D, 0x0A, 0x95, 0x0B, 0x78, 0x33,
+ 0xF5, 0xEA, 0xBA, 0xF3, 0x42, 0x79, 0x72, 0x60, 0x7F, 0xB2, 0xC7, 0x0C, 0x96, 0xA3, 0x21, 0x61 },
+ },
+ {
+ { 0x89, 0xDA, 0xC7, 0x89, 0x6B, 0x46, 0xF2, 0xFC, 0x8B, 0xEA, 0x62, 0x11, 0xFF, 0x98, 0xB6, 0x1F,
+ 0xAA, 0x15, 0x7B, 0xA8, 0xC4, 0xAD, 0x6F, 0xD1, 0x75, 0x92, 0x75, 0xCE, 0x39, 0x41, 0xC3, 0x28 },
+ },
+ {
+ { 0x8A, 0x09, 0x85, 0xBF, 0x86, 0xE8, 0xC9, 0xB9, 0x17, 0xEC, 0x84, 0xDA, 0x2A, 0x56, 0x73, 0x1E,
+ 0x75, 0x2A, 0xA0, 0xDC, 0x52, 0x87, 0xC2, 0xBF, 0x39, 0x51, 0x0B, 0xB3, 0xF0, 0xF2, 0x0A, 0xD1 },
+ },
+ {
+ { 0x8A, 0xAF, 0x36, 0x3C, 0xC9, 0xD8, 0x44, 0x15, 0xA7, 0xEB, 0x0D, 0x72, 0xDA, 0x08, 0xB3, 0x58,
+ 0x80, 0x68, 0x55, 0x9C, 0xB0, 0xA9, 0xAE, 0x92, 0xB8, 0xF4, 0x60, 0x2E, 0xDA, 0x23, 0x82, 0xAA },
+ },
+ {
+ { 0x8A, 0xB2, 0x77, 0x62, 0xF4, 0xA2, 0xE3, 0x11, 0x22, 0x04, 0x96, 0x98, 0x39, 0x99, 0xC8, 0xC4,
+ 0x60, 0x96, 0x3D, 0xFC, 0x1B, 0x88, 0x51, 0x11, 0x1D, 0xA4, 0x1D, 0x3F, 0x3B, 0x0A, 0x6E, 0x94 },
+ },
+ {
+ { 0x8A, 0xD1, 0xD5, 0x48, 0x95, 0x27, 0xB5, 0x28, 0xE5, 0xB5, 0xD6, 0xA5, 0x95, 0x78, 0x87, 0x08,
+ 0x88, 0x8A, 0x3F, 0xB1, 0x9F, 0x2C, 0x7C, 0x8B, 0x38, 0x07, 0x0E, 0x1F, 0x38, 0x98, 0x96, 0x8B },
+ },
+ {
+ { 0x8A, 0xDB, 0x49, 0xD4, 0x15, 0x53, 0x56, 0x70, 0x5B, 0x64, 0x42, 0x6A, 0x99, 0x0F, 0x58, 0xB3,
+ 0xA0, 0x71, 0xEF, 0x78, 0x2E, 0x6C, 0x09, 0x53, 0x07, 0xD7, 0x74, 0x74, 0xD5, 0xB5, 0x7A, 0x62 },
+ },
+ {
+ { 0x8B, 0x3A, 0x10, 0x35, 0xC3, 0xFD, 0xF3, 0x45, 0xFB, 0x70, 0x80, 0x44, 0x83, 0xA5, 0x04, 0x49,
+ 0xA3, 0xD7, 0x60, 0xC6, 0xBA, 0x48, 0xF5, 0xB8, 0x2D, 0x6B, 0xB2, 0x62, 0xED, 0x9D, 0xE3, 0x73 },
+ },
+ {
+ { 0x8B, 0x3A, 0x75, 0xCB, 0xC3, 0x62, 0xD2, 0x35, 0x57, 0x0E, 0x5D, 0xE7, 0x04, 0x29, 0x38, 0x70,
+ 0x8A, 0x1B, 0x0F, 0xCE, 0xB4, 0x59, 0x86, 0x2A, 0x38, 0x67, 0xB7, 0x34, 0xCD, 0xCB, 0x97, 0x94 },
+ },
+ {
+ { 0x8C, 0x3E, 0x7C, 0x1D, 0xCC, 0x7D, 0xD8, 0xE7, 0xD8, 0xBF, 0x7B, 0x5B, 0x3A, 0xE5, 0xE0, 0x27,
+ 0x2E, 0x81, 0x1A, 0xB9, 0xF3, 0xC3, 0xC5, 0x38, 0xE5, 0x74, 0x71, 0x77, 0xE6, 0x2D, 0x62, 0x92 },
+ },
+ {
+ { 0x8C, 0x7C, 0x65, 0x7B, 0xDA, 0x13, 0xCA, 0x62, 0xF2, 0x9A, 0x65, 0xC6, 0xD5, 0x19, 0x3A, 0x93,
+ 0xCF, 0x6C, 0x58, 0x77, 0x18, 0xAD, 0xCA, 0x67, 0x15, 0x8E, 0x97, 0xD3, 0x6A, 0x62, 0x3E, 0xCA },
+ },
+ {
+ { 0x8C, 0xA6, 0x79, 0x62, 0xC4, 0xA8, 0x09, 0x13, 0x33, 0xF2, 0x4E, 0xFD, 0x60, 0xEE, 0x70, 0xCF,
+ 0xED, 0xDB, 0xD6, 0x41, 0x59, 0x04, 0x70, 0x9E, 0x78, 0x5C, 0x33, 0x1B, 0x1E, 0xF5, 0x8F, 0x8E },
+ },
+ {
+ { 0x8E, 0x18, 0xFD, 0xBD, 0xB0, 0x08, 0x16, 0x00, 0x35, 0xFA, 0xF5, 0x01, 0x5B, 0xE7, 0xDA, 0xF4,
+ 0x63, 0xB5, 0xC4, 0x14, 0xEA, 0xBC, 0x8B, 0x89, 0xF3, 0xDB, 0xA2, 0x05, 0xAB, 0x09, 0xA6, 0x43 },
+ },
+ {
+ { 0x8F, 0x10, 0x10, 0x47, 0x93, 0xE8, 0x55, 0x42, 0xBC, 0x06, 0x04, 0xD6, 0xCF, 0x21, 0x5F, 0x78,
+ 0x80, 0xBD, 0x6A, 0x4D, 0xD0, 0xFD, 0xF1, 0xE7, 0xA5, 0xB9, 0xCA, 0x12, 0x46, 0xF5, 0xC4, 0x09 },
+ },
+ {
+ { 0x8F, 0x71, 0x27, 0x76, 0x2E, 0xE7, 0x51, 0x69, 0xBD, 0xC3, 0x5B, 0x04, 0xA7, 0x28, 0xE9, 0xD3,
+ 0x1B, 0x7E, 0x4D, 0x37, 0x89, 0xAA, 0x2C, 0x46, 0xD8, 0xA3, 0x1B, 0x3D, 0xFA, 0x81, 0xA9, 0x7E },
+ },
+ {
+ { 0x8F, 0x94, 0x15, 0x92, 0x6F, 0x40, 0x49, 0xEA, 0x41, 0x8A, 0x30, 0x7C, 0x76, 0x36, 0xE4, 0x9B,
+ 0x14, 0x4F, 0xA5, 0x3E, 0x52, 0xE1, 0x04, 0x15, 0x5F, 0x58, 0x03, 0x5E, 0x45, 0x41, 0xCD, 0x6E },
+ },
+ {
+ { 0x90, 0xB3, 0xA1, 0x85, 0x36, 0x86, 0xAF, 0xEB, 0x15, 0x4A, 0xEF, 0x7E, 0x84, 0x0D, 0x38, 0x04,
+ 0x4E, 0x7D, 0x7F, 0x6D, 0xC4, 0xCE, 0x82, 0x8C, 0xE3, 0x97, 0x55, 0xAC, 0x88, 0xE4, 0x2E, 0x07 },
+ },
+ {
+ { 0x90, 0xE2, 0x51, 0x86, 0x7F, 0x6B, 0x0C, 0x14, 0xBD, 0x9B, 0x51, 0x0C, 0xFD, 0xA8, 0x48, 0x49,
+ 0x72, 0xFD, 0xF0, 0xE0, 0x6D, 0xC1, 0x1F, 0x5D, 0x1D, 0x59, 0x0B, 0xE3, 0xFC, 0x38, 0xDF, 0xF0 },
+ },
+ {
+ { 0x91, 0x90, 0xF8, 0x25, 0x51, 0x0C, 0x65, 0x98, 0xE1, 0x9D, 0x17, 0xDB, 0xBE, 0x6E, 0x7C, 0x82,
+ 0x31, 0x86, 0x9C, 0xA7, 0xF6, 0xE3, 0x07, 0xA2, 0xC2, 0xCC, 0x54, 0x77, 0x8D, 0x4A, 0x89, 0xB3 },
+ },
+ {
+ { 0x91, 0xC7, 0x6E, 0xF8, 0xC7, 0x05, 0x3B, 0x2A, 0x27, 0x0B, 0x97, 0x19, 0x78, 0x3C, 0x85, 0x10,
+ 0xA2, 0x89, 0x0A, 0x48, 0x40, 0x18, 0x63, 0x72, 0x6E, 0x23, 0x3A, 0x82, 0xBF, 0x9A, 0x0B, 0xCF },
+ },
+ {
+ { 0x92, 0x3F, 0x0F, 0x8C, 0x40, 0x5A, 0x02, 0xE6, 0x82, 0xC4, 0xB4, 0x66, 0x5A, 0x7E, 0xE7, 0x16,
+ 0xAA, 0x57, 0xE0, 0xA5, 0x86, 0xC2, 0x4A, 0x16, 0x5A, 0xAD, 0x7E, 0x5B, 0xDA, 0x22, 0x78, 0x24 },
+ },
+ {
+ { 0x92, 0x71, 0x44, 0x12, 0x1C, 0x23, 0x63, 0x57, 0x07, 0xE9, 0x40, 0x7F, 0x7F, 0xFF, 0x6A, 0x64,
+ 0x63, 0x5D, 0x7C, 0xE9, 0x06, 0x66, 0xD4, 0x29, 0x94, 0x09, 0x7A, 0xF4, 0x0C, 0x31, 0x36, 0xFB },
+ },
+ {
+ { 0x94, 0xDC, 0x80, 0x07, 0x49, 0x1D, 0xA8, 0xBF, 0xB7, 0x39, 0x14, 0xAD, 0xCE, 0xF7, 0x1A, 0x12,
+ 0x41, 0x58, 0xBA, 0xD1, 0x7B, 0xA8, 0x8F, 0xA9, 0x46, 0x57, 0x9B, 0xBC, 0x2D, 0x64, 0x97, 0x8D },
+ },
+ {
+ { 0x95, 0x68, 0x33, 0xAE, 0xE6, 0x61, 0x19, 0x26, 0xE9, 0x52, 0x72, 0xA1, 0xF5, 0x88, 0xF9, 0x2A,
+ 0xF5, 0x2C, 0xAE, 0x70, 0x7A, 0xCD, 0xCC, 0x82, 0x63, 0x99, 0x7B, 0xFA, 0x8C, 0x71, 0x9C, 0xA8 },
+ },
+ {
+ { 0x95, 0x89, 0xDA, 0xC9, 0xEC, 0xE7, 0x6D, 0xF5, 0x72, 0x01, 0x96, 0xDC, 0x58, 0x6D, 0x17, 0x9D,
+ 0x73, 0x5D, 0xF7, 0x17, 0x92, 0x6C, 0x06, 0x1E, 0xA7, 0x0C, 0x40, 0x85, 0x64, 0x8F, 0xF3, 0x12 },
+ },
+ {
+ { 0x96, 0xA4, 0x59, 0x90, 0xFC, 0xD0, 0x1C, 0x9C, 0x2A, 0xF0, 0x64, 0x5F, 0x87, 0xB9, 0x69, 0x8B,
+ 0x05, 0xAF, 0xE6, 0x94, 0x32, 0xEB, 0x57, 0x01, 0x08, 0x20, 0x13, 0xBA, 0xC5, 0xB0, 0x55, 0x60 },
+ },
+ {
+ { 0x96, 0xEB, 0x44, 0xAA, 0x6A, 0x20, 0x49, 0xE6, 0xBA, 0xFF, 0xE6, 0xB5, 0x21, 0xC4, 0xAD, 0x8C,
+ 0x58, 0x77, 0x26, 0xCA, 0xA0, 0x12, 0xE8, 0xFB, 0x8E, 0x8E, 0x21, 0x89, 0x77, 0xBF, 0x1D, 0xF6 },
+ },
+ {
+ { 0x97, 0x4F, 0x51, 0xA6, 0x04, 0x68, 0x48, 0xFA, 0xA7, 0xB3, 0x3F, 0xD2, 0x39, 0x13, 0x86, 0x42,
+ 0x8B, 0xD5, 0x24, 0xEA, 0xEB, 0xA8, 0x01, 0x4E, 0x6D, 0x1F, 0xE2, 0x54, 0x38, 0x3F, 0x41, 0x79 },
+ },
+ {
+ { 0x97, 0x8D, 0x6F, 0x1E, 0x9A, 0xA3, 0xA3, 0xCE, 0xB1, 0xAD, 0xA6, 0x09, 0xE2, 0x00, 0x95, 0xFB,
+ 0xC3, 0x3A, 0x6B, 0xBC, 0x6A, 0x21, 0xD8, 0x0A, 0x4E, 0xCB, 0x27, 0x3C, 0x60, 0xAC, 0x2A, 0xC7 },
+ },
+ {
+ { 0x98, 0xB5, 0x92, 0x4E, 0x06, 0xCD, 0xEA, 0x1B, 0xA1, 0x7F, 0xDB, 0x1B, 0x13, 0x97, 0x90, 0x24,
+ 0xB1, 0xC2, 0x5B, 0x0A, 0x69, 0x0C, 0xFE, 0x87, 0x8D, 0x4C, 0xB4, 0x07, 0x76, 0xB9, 0x6F, 0xB0 },
+ },
+ {
+ { 0x99, 0xA5, 0x5F, 0x76, 0xCB, 0xEA, 0x0F, 0x3E, 0x60, 0x71, 0xD3, 0x82, 0x18, 0x1A, 0xF6, 0xCB,
+ 0x25, 0xBD, 0xC5, 0x87, 0x5E, 0x29, 0xF0, 0xF4, 0xD7, 0x19, 0xA9, 0xD3, 0x5B, 0x5B, 0xD6, 0xBF },
+ },
+ {
+ { 0x9A, 0x4B, 0x49, 0x93, 0xB4, 0xED, 0x8C, 0x27, 0xE7, 0x7F, 0x3C, 0x8A, 0xAF, 0xDB, 0xDC, 0x11,
+ 0x1A, 0x36, 0xB7, 0x3C, 0xCA, 0xDB, 0x87, 0x04, 0x98, 0x25, 0x00, 0xD1, 0xB0, 0xF1, 0x09, 0xF2 },
+ },
+ {
+ { 0x9A, 0x5F, 0xAB, 0xE5, 0x8A, 0x1E, 0xAE, 0x4B, 0x20, 0xBA, 0xB3, 0xA7, 0xEB, 0x5E, 0x42, 0xA2,
+ 0xDA, 0x83, 0x11, 0x59, 0x25, 0x7D, 0xD4, 0xE3, 0x55, 0x2E, 0xC6, 0xF7, 0xD2, 0x67, 0xFA, 0xBA },
+ },
+ {
+ { 0x9A, 0xAE, 0x9D, 0x45, 0xAA, 0x04, 0x03, 0x06, 0x4B, 0xC5, 0xA7, 0x4D, 0xD0, 0x32, 0x5D, 0xA4,
+ 0x1E, 0x12, 0xCF, 0x58, 0x6C, 0x46, 0x2E, 0xE0, 0x6C, 0x2B, 0xB4, 0x56, 0xF8, 0x44, 0x1C, 0x4F },
+ },
+ {
+ { 0x9B, 0x8F, 0x9F, 0xC4, 0xAF, 0xA7, 0x04, 0x0D, 0x4E, 0x59, 0x4D, 0x66, 0x7C, 0x44, 0x44, 0xB5,
+ 0x25, 0x88, 0x20, 0xC0, 0x8F, 0x89, 0x91, 0x0E, 0xD3, 0x42, 0x1C, 0xB4, 0xA9, 0x7B, 0xB7, 0x9E },
+ },
+ {
+ { 0x9C, 0x70, 0x8D, 0x5B, 0xAB, 0x37, 0xF5, 0xB6, 0xBC, 0x8A, 0x77, 0x53, 0x12, 0x57, 0x2A, 0xB2,
+ 0x79, 0x21, 0x6D, 0x55, 0x6D, 0xA7, 0x4A, 0xC2, 0xA7, 0xC0, 0x41, 0xE8, 0xCE, 0xB0, 0xBE, 0x0A },
+ },
+ {
+ { 0x9C, 0xCA, 0x23, 0x7C, 0xDF, 0xCA, 0x2C, 0x72, 0xC6, 0x09, 0x25, 0x4A, 0x72, 0x57, 0xFE, 0xD5,
+ 0x3A, 0xF1, 0x44, 0xAB, 0xC2, 0x5E, 0xCD, 0x8E, 0xF7, 0x01, 0x30, 0x8C, 0xB1, 0x3C, 0xF7, 0x69 },
+ },
+ {
+ { 0x9D, 0x6B, 0xDF, 0xCF, 0x0C, 0xBF, 0xFE, 0xEA, 0x3B, 0x1A, 0xC7, 0xE9, 0x63, 0xCB, 0xB5, 0xF2,
+ 0x7F, 0xBD, 0xA8, 0x9D, 0x27, 0x77, 0xF6, 0x0E, 0x56, 0x5B, 0x27, 0x78, 0x54, 0xEF, 0xB0, 0x19 },
+ },
+ {
+ { 0x9D, 0xAC, 0x33, 0x14, 0xB2, 0x5B, 0xB7, 0x9A, 0x39, 0xCD, 0x01, 0xEC, 0x4B, 0x33, 0xA1, 0x2F,
+ 0x47, 0x51, 0x2F, 0x54, 0x09, 0xFF, 0x09, 0x5D, 0x40, 0xAA, 0xD6, 0x20, 0x84, 0xEF, 0x15, 0xBE },
+ },
+ {
+ { 0x9F, 0x24, 0x5C, 0x0A, 0x0E, 0xC6, 0x3A, 0xAA, 0xCB, 0xF9, 0x69, 0xC6, 0xFC, 0x24, 0xA1, 0x07,
+ 0x15, 0x83, 0xB7, 0x79, 0xA5, 0x8A, 0xB6, 0x23, 0xDD, 0x15, 0x31, 0xA2, 0xCA, 0x9F, 0x87, 0x51 },
+ },
+ {
+ { 0x9F, 0xAF, 0x1C, 0x11, 0xA3, 0xC7, 0xE2, 0x41, 0xF8, 0x63, 0x71, 0x97, 0xE8, 0x99, 0x68, 0xDB,
+ 0x86, 0x6A, 0xD0, 0x1A, 0x5D, 0x4E, 0xD5, 0x34, 0x59, 0x48, 0x65, 0xB9, 0x70, 0x75, 0xF2, 0x60 },
+ },
+ {
+ { 0x9F, 0xFA, 0x4E, 0xF4, 0xFC, 0xF2, 0xCF, 0xD1, 0xB2, 0x7C, 0x6A, 0x62, 0xE3, 0xC4, 0x23, 0x5B,
+ 0xD8, 0x3C, 0xC5, 0xE0, 0x06, 0xE9, 0x2A, 0x55, 0xE4, 0xA9, 0x86, 0xE6, 0x30, 0x53, 0x57, 0xE3 },
+ },
+ {
+ { 0xA0, 0xC2, 0xD2, 0x07, 0xA4, 0x7E, 0x18, 0xD0, 0x37, 0x14, 0xD5, 0xB3, 0x44, 0x5D, 0x88, 0xBE,
+ 0x81, 0xFF, 0x5E, 0x1D, 0x16, 0x07, 0x3D, 0xC1, 0x16, 0x6B, 0xB5, 0x44, 0x8F, 0xF6, 0x52, 0xDF },
+ },
+ {
+ { 0xA1, 0x97, 0x7D, 0x0C, 0x92, 0x7C, 0x21, 0xEB, 0x47, 0x6F, 0x67, 0xBE, 0xFE, 0xD6, 0xCF, 0x2C,
+ 0x61, 0xB7, 0x45, 0xF0, 0xCE, 0x8D, 0x26, 0x58, 0x3D, 0x03, 0xB2, 0x70, 0x02, 0xD5, 0xCD, 0xAF },
+ },
+ {
+ { 0xA2, 0x6C, 0x37, 0x5E, 0xB3, 0x19, 0x6E, 0x28, 0x3B, 0xEC, 0x60, 0x3D, 0xB6, 0xBB, 0xDA, 0xE2,
+ 0x49, 0x55, 0xE4, 0xBA, 0x91, 0x0C, 0xD4, 0x2D, 0x9E, 0xAC, 0x55, 0xCA, 0xC6, 0x10, 0x3A, 0xB9 },
+ },
+ {
+ { 0xA3, 0xA4, 0xFC, 0x03, 0xE1, 0x75, 0xF2, 0x68, 0x02, 0x57, 0x46, 0x34, 0xDE, 0x70, 0x7D, 0x2F,
+ 0x92, 0xF4, 0xD0, 0xCB, 0x90, 0xCD, 0xB6, 0x1D, 0xD1, 0x95, 0x8B, 0xCF, 0x0C, 0x55, 0x20, 0x86 },
+ },
+ {
+ { 0xA6, 0x62, 0xFC, 0x81, 0xC9, 0x09, 0x34, 0xB9, 0xB4, 0xD6, 0x30, 0xB5, 0xD8, 0x2E, 0x86, 0xF2,
+ 0x36, 0x3E, 0xC1, 0x5C, 0xCF, 0xCD, 0xAF, 0xA7, 0xA2, 0x0C, 0x9B, 0x4E, 0x3A, 0x90, 0x0D, 0xD1 },
+ },
+ {
+ { 0xA6, 0xA4, 0xA3, 0xF6, 0x1F, 0xA5, 0x8C, 0xE9, 0x70, 0xB4, 0x58, 0xB7, 0xC3, 0x7C, 0x05, 0x2E,
+ 0xAD, 0x1E, 0xB2, 0x0B, 0x85, 0x67, 0xE3, 0x51, 0xAD, 0x8E, 0x6F, 0xBA, 0x49, 0xC2, 0x69, 0x2C },
+ },
+ {
+ { 0xA6, 0xDE, 0x6C, 0x3B, 0x8C, 0x14, 0x05, 0xCB, 0xE1, 0x2D, 0xB4, 0x09, 0x97, 0x61, 0x71, 0xAC,
+ 0xB5, 0x1F, 0xB3, 0xDC, 0xFB, 0xB7, 0x6E, 0xE3, 0x84, 0x95, 0x39, 0xCD, 0x8A, 0xB0, 0x66, 0xDF },
+ },
+ {
+ { 0xA8, 0x53, 0xAD, 0xC1, 0xC2, 0x18, 0x59, 0xAF, 0x7C, 0x46, 0x2B, 0x4A, 0xA0, 0xA5, 0x74, 0xCA,
+ 0x9F, 0xEE, 0xFB, 0x18, 0x5A, 0x1F, 0xDB, 0xB6, 0xC1, 0x0E, 0x17, 0xD6, 0x01, 0xB7, 0x09, 0x8F },
+ },
+ {
+ { 0xA8, 0xDF, 0xF0, 0x6A, 0x17, 0x35, 0xB4, 0x6D, 0x17, 0xDA, 0xEB, 0xC3, 0x43, 0x43, 0x18, 0x31,
+ 0x3B, 0x2D, 0x9E, 0x7C, 0x3E, 0xF4, 0x8F, 0x28, 0x53, 0x75, 0x35, 0x13, 0xE1, 0xB2, 0x53, 0xA8 },
+ },
+ {
+ { 0xA8, 0xE3, 0x8C, 0x6E, 0xC0, 0x93, 0xF5, 0xAF, 0x53, 0x88, 0xF1, 0xE7, 0x66, 0xD7, 0x5F, 0xFB,
+ 0x57, 0xDD, 0xBE, 0x3E, 0x9D, 0xC2, 0xE0, 0xBE, 0x57, 0xBB, 0x88, 0x36, 0x46, 0xC5, 0xC0, 0x32 },
+ },
+ {
+ { 0xA9, 0x0B, 0x8D, 0xE1, 0x7F, 0x6B, 0x68, 0x37, 0x56, 0x21, 0x2D, 0xB3, 0xAB, 0x34, 0x89, 0x6E,
+ 0x91, 0x70, 0x93, 0x11, 0x3E, 0x47, 0xCA, 0x35, 0x96, 0x2E, 0xAC, 0xCA, 0x9C, 0xB3, 0x86, 0xF0 },
+ },
+ {
+ { 0xA9, 0x71, 0x2F, 0x85, 0xED, 0x2E, 0x25, 0xAD, 0xA5, 0x7D, 0xC1, 0xF0, 0xF8, 0x6D, 0xE1, 0x07,
+ 0xB5, 0xE2, 0xF0, 0x36, 0x09, 0x53, 0xF1, 0xED, 0x12, 0x5E, 0x37, 0x07, 0x59, 0x47, 0x1D, 0x09 },
+ },
+ {
+ { 0xAA, 0xEB, 0xFE, 0x2D, 0x21, 0xB7, 0xE5, 0x35, 0x1B, 0xB9, 0x99, 0x69, 0x44, 0x44, 0x19, 0xEF,
+ 0x21, 0xC9, 0x68, 0x8C, 0xE0, 0x53, 0x24, 0x88, 0x84, 0xCA, 0xB0, 0xB8, 0x95, 0x10, 0x30, 0xFF },
+ },
+ {
+ { 0xAB, 0x41, 0x28, 0x10, 0x9C, 0xAB, 0x8A, 0x58, 0x7C, 0x8F, 0xF4, 0xC7, 0xF6, 0x87, 0x34, 0x49,
+ 0x98, 0x18, 0xD1, 0x3F, 0x52, 0x26, 0x76, 0xD0, 0x66, 0xB3, 0x52, 0x17, 0x6F, 0xD2, 0x35, 0x96 },
+ },
+ {
+ { 0xAB, 0x80, 0xD9, 0xBA, 0x0A, 0xEF, 0xAD, 0x7B, 0xEC, 0xCE, 0x7F, 0x5E, 0x61, 0x59, 0x9A, 0xF5,
+ 0x26, 0x69, 0xBF, 0x59, 0x50, 0x7F, 0x8E, 0xF1, 0x99, 0x13, 0xC4, 0x2E, 0xE1, 0x29, 0xDA, 0xF0 },
+ },
+ {
+ { 0xAB, 0xEB, 0x6A, 0xA0, 0xD1, 0xB0, 0xE0, 0x49, 0xD6, 0x9D, 0xF8, 0x3A, 0xDD, 0x19, 0xF7, 0x26,
+ 0x8A, 0x38, 0xDE, 0x6C, 0x00, 0x72, 0x60, 0x68, 0xC2, 0xEE, 0xE4, 0x55, 0x44, 0xF6, 0xD6, 0x7A },
+ },
+ {
+ { 0xAC, 0x1B, 0x4C, 0x64, 0x6C, 0xAE, 0xFB, 0x10, 0x8A, 0x54, 0xCA, 0xB5, 0x4A, 0x96, 0xE9, 0x66,
+ 0x6E, 0x72, 0xA8, 0x20, 0x22, 0x44, 0xEF, 0x3D, 0x7C, 0xA9, 0x34, 0xDF, 0xCC, 0x24, 0xFC, 0xA7 },
+ },
+ {
+ { 0xAC, 0x7C, 0x14, 0xB9, 0x56, 0x8F, 0x92, 0x07, 0x5A, 0xD4, 0xA3, 0xBA, 0x3D, 0x4B, 0x01, 0x84,
+ 0x91, 0xF3, 0x66, 0x1A, 0x37, 0x9B, 0x3D, 0xFE, 0xDD, 0x6F, 0xD3, 0xC3, 0x2E, 0xFA, 0x84, 0x7D },
+ },
+ {
+ { 0xAD, 0x69, 0x54, 0x5F, 0x9F, 0x85, 0x25, 0x5F, 0xE4, 0x16, 0x51, 0x3D, 0x94, 0xDB, 0x31, 0x50,
+ 0x5F, 0x38, 0x4B, 0x52, 0x3C, 0x2C, 0xA2, 0x6E, 0xDC, 0x0A, 0x54, 0x9A, 0x8F, 0x16, 0x26, 0xF9 },
+ },
+ {
+ { 0xAE, 0x03, 0x19, 0xFE, 0xA6, 0xA6, 0x5E, 0x84, 0xE8, 0x54, 0xB5, 0x15, 0x50, 0xEA, 0x44, 0x4F,
+ 0xA3, 0xB8, 0xBB, 0x50, 0xAE, 0x93, 0x74, 0x01, 0x3C, 0xFE, 0xF3, 0x88, 0x73, 0x5D, 0x0B, 0xD3 },
+ },
+ {
+ { 0xAE, 0x4D, 0xF3, 0x97, 0x9B, 0x74, 0x27, 0x34, 0xA3, 0x39, 0xC4, 0x70, 0x1D, 0x5E, 0x13, 0x21,
+ 0x26, 0x3F, 0xF4, 0x4E, 0x67, 0x56, 0x49, 0x05, 0xF4, 0x9E, 0x25, 0x34, 0x62, 0xB8, 0x02, 0x25 },
+ },
+ {
+ { 0xAF, 0x1F, 0x37, 0x1F, 0x34, 0x84, 0x57, 0x51, 0x65, 0x2D, 0xC7, 0x48, 0x23, 0xF3, 0x01, 0x5C,
+ 0x5A, 0x11, 0xCA, 0x65, 0x3F, 0x28, 0x70, 0x1E, 0xDD, 0x4A, 0x7E, 0x0D, 0x23, 0x17, 0x1B, 0xBB },
+ },
+ {
+ { 0xAF, 0x6B, 0x80, 0x51, 0x47, 0x14, 0x0A, 0x0E, 0x41, 0x81, 0xD8, 0x6A, 0x7E, 0x8F, 0x07, 0x69,
+ 0xB6, 0x1D, 0x46, 0xD7, 0xB6, 0xFA, 0xC6, 0xE6, 0xF9, 0x59, 0x6D, 0xE9, 0x4A, 0xA8, 0xE2, 0xE8 },
+ },
+ {
+ { 0xB0, 0x5C, 0x14, 0x33, 0x61, 0x75, 0x9B, 0xE1, 0x52, 0xFD, 0x76, 0xA5, 0xFF, 0xA4, 0x87, 0x2D,
+ 0xD4, 0x2E, 0xA0, 0x60, 0xAE, 0x40, 0xA3, 0x83, 0x13, 0xB7, 0xB5, 0x4A, 0xEC, 0x06, 0x73, 0xC2 },
+ },
+ {
+ { 0xB0, 0xE0, 0xE1, 0x6C, 0x5F, 0x69, 0x1F, 0x66, 0xA9, 0x57, 0x3B, 0xD3, 0xCF, 0x43, 0xF9, 0xDF,
+ 0xD2, 0xAD, 0x3E, 0x56, 0x15, 0x54, 0x63, 0x7F, 0x1E, 0x7B, 0x71, 0x91, 0x4D, 0x62, 0x73, 0x38 },
+ },
+ {
+ { 0xB2, 0xDC, 0x86, 0x25, 0x6C, 0xCF, 0xF4, 0xBB, 0x14, 0xFD, 0x70, 0x27, 0x9F, 0xCC, 0x3C, 0xE9,
+ 0x25, 0xC5, 0x1F, 0xB7, 0x17, 0xE5, 0x87, 0x6F, 0x29, 0x1B, 0xA1, 0x70, 0x73, 0x43, 0x85, 0x68 },
+ },
+ {
+ { 0xB3, 0x0D, 0x88, 0x44, 0x30, 0x43, 0xF5, 0xF3, 0x72, 0x32, 0xBB, 0x9B, 0xAC, 0xB9, 0x94, 0xC5,
+ 0xBA, 0xE9, 0x3A, 0x46, 0xFC, 0x87, 0xF1, 0x51, 0x29, 0xC9, 0x74, 0x69, 0xA5, 0x81, 0x4E, 0xCA },
+ },
+ {
+ { 0xB3, 0x1A, 0xF0, 0xC2, 0xE5, 0x1E, 0xA2, 0x1C, 0x91, 0x04, 0xF9, 0x4F, 0xAA, 0x66, 0xE0, 0xCC,
+ 0xC0, 0x41, 0x34, 0xD5, 0x80, 0x9A, 0x2A, 0x26, 0x70, 0xA3, 0xB7, 0xBC, 0x7D, 0xD9, 0x64, 0xF8 },
+ },
+ {
+ { 0xB3, 0xE6, 0x42, 0x06, 0x6E, 0x41, 0x78, 0x67, 0xD9, 0x0F, 0xB9, 0xB2, 0xBA, 0x15, 0x41, 0x98,
+ 0xA5, 0xC5, 0xF6, 0xCC, 0x82, 0x9B, 0x51, 0x39, 0xDF, 0xD6, 0x91, 0xE5, 0x1A, 0xD3, 0x74, 0xAD },
+ },
+ {
+ { 0xB3, 0xF4, 0xB1, 0x6F, 0x8E, 0xCE, 0xBB, 0x41, 0x47, 0x4F, 0x92, 0x4F, 0xEE, 0xF9, 0xB0, 0xBD,
+ 0x97, 0x9B, 0x36, 0x36, 0xC3, 0x4F, 0xF2, 0x72, 0x3F, 0x67, 0x3C, 0x8E, 0xEE, 0x2A, 0xF1, 0x52 },
+ },
+ {
+ { 0xB4, 0xD4, 0x67, 0xFC, 0x5E, 0x97, 0xDB, 0x25, 0xA1, 0xFD, 0xB0, 0x06, 0xD2, 0x77, 0x66, 0xB9,
+ 0x99, 0x5B, 0xB9, 0xC7, 0x7B, 0x66, 0x43, 0x97, 0x08, 0xA4, 0x59, 0xB0, 0x43, 0xD0, 0x33, 0x24 },
+ },
+ {
+ { 0xB4, 0xED, 0xCD, 0x6F, 0x8A, 0x01, 0x82, 0xB7, 0x17, 0xF0, 0x6F, 0xE1, 0xD7, 0xAC, 0x9C, 0x62,
+ 0x33, 0xD4, 0x38, 0x22, 0xE9, 0xFD, 0x14, 0xDB, 0x98, 0xF7, 0xF8, 0x4E, 0x32, 0x79, 0x6D, 0x08 },
+ },
+ {
+ { 0xB5, 0xE5, 0xDC, 0xDE, 0xCB, 0x8D, 0xEB, 0x27, 0x13, 0x4F, 0x02, 0xA5, 0x18, 0x79, 0x43, 0x16,
+ 0xF0, 0x8F, 0xAF, 0x9C, 0x2B, 0x1F, 0xDA, 0xD6, 0xD4, 0x86, 0x61, 0xF5, 0x7E, 0xA6, 0x45, 0xD9 },
+ },
+ {
+ { 0xB5, 0xEF, 0x42, 0xC4, 0xBC, 0xED, 0xF1, 0x7B, 0xEC, 0xC7, 0x5B, 0xF4, 0x63, 0x66, 0x49, 0xCE,
+ 0xBF, 0xF8, 0x71, 0x1B, 0xCE, 0xFF, 0xFA, 0x69, 0x5C, 0xC2, 0x52, 0xFA, 0x57, 0x4D, 0x42, 0x18 },
+ },
+ {
+ { 0xB6, 0x82, 0x3C, 0x9D, 0xBC, 0x8E, 0x8C, 0x05, 0x4B, 0xCF, 0x60, 0xF2, 0x38, 0x21, 0xAC, 0x6C,
+ 0x58, 0x19, 0x73, 0x51, 0xEA, 0xCF, 0xA5, 0x57, 0x4C, 0xF0, 0x41, 0xB4, 0xCE, 0x6B, 0x84, 0x04 },
+ },
+ {
+ { 0xB7, 0x06, 0xDE, 0x1B, 0xD1, 0xEE, 0x2F, 0x4C, 0xEC, 0x6C, 0xE0, 0x92, 0x02, 0x2B, 0x49, 0x32,
+ 0x81, 0xE2, 0x9A, 0x21, 0x73, 0x50, 0x8C, 0x9B, 0xD0, 0xFB, 0xC2, 0xC3, 0xD9, 0x68, 0xE3, 0xE7 },
+ },
+ {
+ { 0xB8, 0x74, 0x36, 0x95, 0x1C, 0xEC, 0x37, 0x7E, 0xEF, 0x73, 0xDE, 0x4B, 0x74, 0xF2, 0x83, 0xC4,
+ 0x2B, 0x2C, 0xCB, 0x1C, 0xA3, 0x7C, 0x5B, 0x30, 0xAA, 0xD6, 0x55, 0xA7, 0x40, 0x1A, 0x3D, 0x2F },
+ },
+ {
+ { 0xB9, 0x8D, 0x83, 0x38, 0x55, 0xC3, 0x67, 0x88, 0x62, 0xB6, 0x2F, 0x36, 0x50, 0xDB, 0x00, 0xA3,
+ 0x45, 0xF4, 0x6A, 0x0E, 0x8E, 0x01, 0x1A, 0x20, 0x01, 0x3F, 0xD8, 0xED, 0xCE, 0x25, 0x27, 0x0D },
+ },
+ {
+ { 0xBA, 0x51, 0xAF, 0xF5, 0xD5, 0xD3, 0x10, 0x5F, 0x34, 0xA2, 0xB3, 0x3A, 0x83, 0xE3, 0xAD, 0xFD,
+ 0x12, 0xD7, 0x9C, 0xA6, 0x05, 0x90, 0x9D, 0x96, 0x03, 0x3E, 0x32, 0xA5, 0xCF, 0x2F, 0x71, 0xF6 },
+ },
+ {
+ { 0xBB, 0x5C, 0xB3, 0x78, 0xB7, 0xB9, 0x48, 0x7F, 0xA6, 0x1B, 0xC0, 0x91, 0x3D, 0xA1, 0xDF, 0x26,
+ 0xA1, 0xCF, 0xEF, 0xF7, 0x45, 0x2D, 0x9B, 0xA3, 0x6C, 0xAC, 0x47, 0xA8, 0x5C, 0x7F, 0xF3, 0x48 },
+ },
+ {
+ { 0xBC, 0x14, 0x2E, 0xBA, 0xC2, 0x78, 0xA8, 0xFE, 0x8C, 0xA8, 0xBC, 0x2C, 0x62, 0xFB, 0xCC, 0x40,
+ 0x17, 0xFF, 0x24, 0x96, 0x98, 0xBE, 0xED, 0xFB, 0x1E, 0xF3, 0x6F, 0x37, 0x5F, 0xB3, 0x9F, 0x72 },
+ },
+ {
+ { 0xBD, 0x2E, 0x2F, 0x37, 0xC9, 0x66, 0xC3, 0x86, 0xD9, 0x70, 0x44, 0xFD, 0xE3, 0xE3, 0xF9, 0x00,
+ 0xFB, 0x1A, 0x0B, 0x04, 0x03, 0xB5, 0x81, 0x72, 0x5F, 0x34, 0xE3, 0xC1, 0x90, 0x05, 0x60, 0x56 },
+ },
+ {
+ { 0xBE, 0xB9, 0x09, 0x0C, 0x92, 0xD1, 0x6B, 0xD0, 0x5A, 0xF3, 0x91, 0x5A, 0x39, 0xCC, 0x2A, 0xFA,
+ 0x9F, 0x6A, 0x8A, 0x6F, 0xBE, 0xD4, 0xFE, 0x54, 0xD9, 0xDE, 0x32, 0x49, 0x23, 0xB3, 0x93, 0x5A },
+ },
+ {
+ { 0xBF, 0x38, 0xE6, 0xAE, 0x32, 0x0F, 0x69, 0x16, 0x16, 0x0D, 0xA6, 0x06, 0x86, 0x83, 0xBF, 0x49,
+ 0xF2, 0xB2, 0x2B, 0x25, 0x24, 0x84, 0x63, 0x68, 0xF5, 0x04, 0x51, 0x81, 0x52, 0x40, 0x25, 0x9A },
+ },
+ {
+ { 0xBF, 0x60, 0xAE, 0xB3, 0x91, 0xC0, 0xFB, 0xD0, 0x49, 0x53, 0x52, 0x6D, 0xA9, 0xFD, 0x59, 0x96,
+ 0x9A, 0x82, 0xF1, 0xEE, 0x81, 0xA7, 0x97, 0x98, 0xA4, 0x17, 0x1E, 0x14, 0x59, 0x39, 0x19, 0x67 },
+ },
+ {
+ { 0xBF, 0xF4, 0x3A, 0x97, 0x20, 0x48, 0x2D, 0x13, 0x4C, 0xD5, 0xEE, 0x8A, 0x88, 0x99, 0xE1, 0xA7,
+ 0x36, 0xBF, 0x54, 0xA2, 0xB7, 0x86, 0x26, 0x9C, 0x0D, 0xCB, 0x8B, 0xA1, 0x92, 0xA8, 0x1F, 0xA4 },
+ },
+ {
+ { 0xC0, 0x09, 0xA1, 0xBE, 0x5B, 0xE8, 0xAF, 0xB5, 0x25, 0x8E, 0x12, 0x85, 0x5C, 0x64, 0xD0, 0x4D,
+ 0x13, 0xE8, 0xCC, 0xC4, 0x7B, 0x02, 0xBF, 0x3B, 0x51, 0xC6, 0xE1, 0x18, 0x05, 0xAE, 0xEC, 0xEB },
+ },
+ {
+ { 0xC0, 0x9F, 0xFA, 0x0E, 0xDD, 0x16, 0xBA, 0x55, 0xF2, 0x3C, 0xEA, 0xF7, 0x2B, 0x11, 0x34, 0xE9,
+ 0x28, 0xDB, 0xA1, 0xC2, 0x34, 0x5A, 0x5A, 0xB5, 0x63, 0x1E, 0x25, 0x41, 0x24, 0x05, 0x4A, 0xDB },
+ },
+ {
+ { 0xC0, 0xAB, 0xD1, 0xC3, 0x56, 0x2F, 0xBC, 0x7F, 0xF7, 0xBD, 0x38, 0x95, 0x54, 0x60, 0xC3, 0xFC,
+ 0x43, 0x55, 0x0D, 0x97, 0x7F, 0x25, 0xE3, 0x43, 0xD4, 0x9C, 0xD4, 0xAF, 0xAD, 0xF2, 0x09, 0x3C },
+ },
+ {
+ { 0xC0, 0xFE, 0xB7, 0x2A, 0x5F, 0x33, 0x16, 0x5C, 0x0D, 0xC7, 0xC4, 0x24, 0x7E, 0x23, 0xF3, 0x8C,
+ 0xC6, 0x1F, 0x25, 0x24, 0x42, 0xB2, 0xF6, 0x13, 0x40, 0x92, 0xDE, 0x3B, 0xAD, 0x7E, 0x45, 0x0D },
+ },
+ {
+ { 0xC1, 0x77, 0x12, 0x97, 0xA4, 0xE8, 0xDC, 0x53, 0x75, 0x19, 0x5E, 0x1B, 0x63, 0x04, 0x2B, 0x59,
+ 0x19, 0x09, 0xF1, 0xD7, 0xEB, 0x5D, 0x25, 0xF2, 0x97, 0xAE, 0x7A, 0x61, 0xC1, 0x53, 0x8F, 0x9E },
+ },
+ {
+ { 0xC1, 0x86, 0xBE, 0x26, 0xE4, 0x47, 0x89, 0x7C, 0x48, 0x3C, 0x43, 0xFD, 0xC0, 0x86, 0xE2, 0x60,
+ 0x74, 0x17, 0xEB, 0x3E, 0xA7, 0x88, 0xEC, 0x03, 0x10, 0xA7, 0x9D, 0xA9, 0x24, 0x1D, 0x16, 0xDE },
+ },
+ {
+ { 0xC1, 0xDE, 0x5F, 0xA3, 0x92, 0x13, 0x68, 0x58, 0x11, 0xA5, 0xBA, 0x93, 0x12, 0x1D, 0xE7, 0xA3,
+ 0x95, 0x98, 0x4E, 0x84, 0x44, 0x4E, 0x58, 0xF1, 0x63, 0xB7, 0xA6, 0x20, 0xAE, 0x3B, 0xBF, 0xA8 },
+ },
+ {
+ { 0xC2, 0xAD, 0xDF, 0x99, 0xCF, 0xC4, 0x2C, 0xE0, 0xE5, 0xA0, 0x93, 0xBC, 0xBF, 0x87, 0x40, 0x7C,
+ 0x61, 0x1F, 0x9D, 0x0A, 0xBF, 0x2A, 0x35, 0xD6, 0xE8, 0x03, 0xA3, 0x8E, 0xCB, 0x92, 0xC7, 0xB3 },
+ },
+ {
+ { 0xC2, 0xE7, 0x92, 0x11, 0x6A, 0x05, 0x00, 0x00, 0xBD, 0x47, 0x59, 0x1D, 0x93, 0x04, 0x71, 0xE6,
+ 0x17, 0x4C, 0x93, 0x85, 0xF5, 0xDC, 0x32, 0xB7, 0x62, 0x31, 0x65, 0x5F, 0xC8, 0x5E, 0x22, 0xE2 },
+ },
+ {
+ { 0xC3, 0x79, 0x03, 0xC5, 0x3A, 0xE6, 0x02, 0xEC, 0x96, 0x9E, 0xC3, 0x3F, 0x63, 0xFE, 0x9A, 0xB2,
+ 0x0C, 0x39, 0x5F, 0x83, 0x0D, 0x30, 0xE4, 0xEE, 0x9D, 0x8D, 0xD9, 0x05, 0x92, 0x1E, 0xC1, 0xA0 },
+ },
+ {
+ { 0xC3, 0xCF, 0x54, 0x16, 0xA5, 0x31, 0xAF, 0x4B, 0xFA, 0xE8, 0x9C, 0x45, 0x14, 0x3F, 0x20, 0xCC,
+ 0x1B, 0x3E, 0x18, 0x1D, 0x29, 0xC2, 0xD0, 0xE8, 0xFF, 0x7D, 0x3F, 0x2A, 0x66, 0xB1, 0x82, 0xFE },
+ },
+ {
+ { 0xC4, 0x98, 0xA1, 0xB6, 0x9F, 0x54, 0x40, 0x86, 0x17, 0x47, 0x47, 0x71, 0x5A, 0x27, 0x4D, 0x3F,
+ 0xB5, 0x90, 0x19, 0xBE, 0x09, 0x21, 0x31, 0xBC, 0xFA, 0xA8, 0x3A, 0x39, 0x5F, 0x7E, 0x57, 0x3C },
+ },
+ {
+ { 0xC4, 0xE2, 0x8D, 0xD8, 0x3F, 0xE3, 0x0C, 0x96, 0x33, 0x8C, 0xEF, 0x77, 0x73, 0xC6, 0xDF, 0xCA,
+ 0x6C, 0xE4, 0xFA, 0x96, 0x41, 0xBE, 0xAB, 0x38, 0x05, 0xA8, 0xEF, 0xB6, 0xCD, 0xC3, 0xCF, 0x0A },
+ },
+ {
+ { 0xC5, 0x00, 0xB8, 0x3F, 0x3E, 0x06, 0x6C, 0xD1, 0xDD, 0x0E, 0xBC, 0xD7, 0x3D, 0xD4, 0x01, 0x61,
+ 0xB9, 0x25, 0x9A, 0xA7, 0x7A, 0xB8, 0xA6, 0x47, 0xE8, 0x57, 0x1F, 0xF3, 0x37, 0xCF, 0x94, 0x6D },
+ },
+ {
+ { 0xC5, 0x29, 0x5B, 0xA6, 0xE2, 0x7E, 0x72, 0x10, 0x22, 0xFE, 0xB2, 0x1E, 0x78, 0xEB, 0x7B, 0x03,
+ 0x57, 0xC9, 0xCD, 0x56, 0x5B, 0xD0, 0xE5, 0x96, 0x72, 0xF6, 0x66, 0x34, 0x2B, 0x79, 0x94, 0x9D },
+ },
+ {
+ { 0xC6, 0x12, 0x75, 0x6B, 0xA5, 0x42, 0x34, 0x4A, 0xDC, 0x1B, 0x80, 0xE9, 0x38, 0x84, 0x5A, 0x1E,
+ 0xD6, 0xE9, 0x38, 0xFE, 0xF4, 0x0D, 0x04, 0xEC, 0x86, 0x55, 0x8F, 0x4B, 0x21, 0x05, 0x2F, 0xD2 },
+ },
+ {
+ { 0xC6, 0x17, 0xE0, 0x85, 0x5B, 0xF1, 0x4F, 0xBF, 0x21, 0xAF, 0x00, 0x82, 0x25, 0xCA, 0xBE, 0x40,
+ 0x4F, 0x73, 0x8C, 0x27, 0x8A, 0x4A, 0x42, 0x87, 0xF1, 0xEE, 0x38, 0x01, 0x27, 0xC5, 0x61, 0xFA },
+ },
+ {
+ { 0xC6, 0xA4, 0x24, 0xBF, 0x7C, 0xFE, 0x31, 0x72, 0x74, 0x7A, 0x47, 0x14, 0xA0, 0xEF, 0xB9, 0x17,
+ 0x93, 0x8C, 0x5E, 0xBD, 0x59, 0x12, 0x9D, 0xED, 0x7A, 0x81, 0x18, 0xC7, 0xF6, 0x59, 0xD1, 0x33 },
+ },
+ {
+ { 0xC6, 0xAD, 0x1D, 0x7A, 0x14, 0x1A, 0x91, 0x75, 0x2D, 0x31, 0xFB, 0xC1, 0x06, 0x16, 0xBF, 0x1C,
+ 0xA2, 0xFB, 0x5B, 0x02, 0xE8, 0x46, 0xB5, 0x9E, 0x63, 0x34, 0x6B, 0x31, 0x92, 0xA7, 0x52, 0x92 },
+ },
+ {
+ { 0xC7, 0x01, 0x83, 0x64, 0x38, 0xF3, 0x7B, 0xEA, 0x8A, 0x88, 0x16, 0x10, 0x63, 0x70, 0x86, 0xF8,
+ 0x8D, 0x9A, 0x11, 0x5E, 0x00, 0x92, 0x46, 0xD2, 0x7F, 0x48, 0x9F, 0xA7, 0x18, 0x51, 0x88, 0xA8 },
+ },
+ {
+ { 0xC7, 0xFF, 0x8E, 0xFD, 0xEC, 0xDF, 0x00, 0xD1, 0xFC, 0x8D, 0x55, 0x2D, 0x2A, 0x70, 0x70, 0xE5,
+ 0xE3, 0x3D, 0x42, 0xE5, 0x90, 0xF5, 0x86, 0xC6, 0xAE, 0xDE, 0x03, 0x2B, 0x2D, 0x86, 0x7B, 0xD5 },
+ },
+ {
+ { 0xC7, 0xFF, 0xB4, 0x9F, 0xBC, 0x94, 0x72, 0x24, 0x5C, 0x8E, 0x95, 0xDE, 0x62, 0x9A, 0xF5, 0xC1,
+ 0xBF, 0xEA, 0xC5, 0x50, 0x04, 0xC1, 0x54, 0x82, 0x3A, 0x58, 0xBA, 0xE8, 0x05, 0x6E, 0x3C, 0x64 },
+ },
+ {
+ { 0xC8, 0x37, 0xD6, 0xF2, 0xAB, 0x14, 0x79, 0x91, 0x42, 0xED, 0x3C, 0x79, 0xBE, 0xD9, 0x44, 0x1E,
+ 0x92, 0x50, 0xBD, 0x05, 0x20, 0x25, 0xAD, 0x8A, 0xF4, 0x40, 0x41, 0xAC, 0x19, 0xEF, 0xBB, 0x4C },
+ },
+ {
+ { 0xC9, 0x43, 0x10, 0x03, 0xBB, 0xEA, 0xB5, 0x8E, 0x35, 0x2F, 0xDE, 0xB4, 0x5B, 0x7F, 0xCF, 0x15,
+ 0xC7, 0x3F, 0x07, 0x34, 0xA0, 0x7D, 0x6C, 0xBD, 0xF6, 0x32, 0x92, 0x92, 0xEB, 0x81, 0x2C, 0x93 },
+ },
+ {
+ { 0xC9, 0x72, 0xF4, 0xF9, 0x6E, 0x71, 0x33, 0xE1, 0x6E, 0x55, 0x57, 0xA0, 0x57, 0xB1, 0xD4, 0x2B,
+ 0xA9, 0x2D, 0x98, 0x5C, 0xAE, 0xE7, 0x3C, 0xAF, 0xDA, 0xEB, 0x55, 0xEC, 0xA2, 0xE4, 0xAB, 0xB0 },
+ },
+ {
+ { 0xC9, 0x78, 0x37, 0x2C, 0x9E, 0x11, 0x60, 0x71, 0xB6, 0x1B, 0x90, 0x92, 0xA9, 0xAA, 0x96, 0x81,
+ 0x62, 0x36, 0x55, 0xA6, 0x6F, 0x4F, 0xCB, 0xC4, 0xD3, 0xA6, 0x7E, 0xFD, 0x56, 0x72, 0x48, 0x30 },
+ },
+ {
+ { 0xCA, 0x55, 0x6F, 0x82, 0xC9, 0x68, 0x4C, 0x9A, 0xF3, 0x55, 0x7D, 0x3E, 0x2D, 0x88, 0xAF, 0x92,
+ 0xED, 0x25, 0x9C, 0x20, 0xFF, 0xD1, 0xDD, 0xE9, 0xF7, 0x9D, 0x6B, 0x92, 0xC6, 0x1E, 0xE1, 0xB9 },
+ },
+ {
+ { 0xCA, 0xBE, 0x25, 0x56, 0xF1, 0xBB, 0x56, 0x57, 0x0C, 0xEF, 0x3A, 0x87, 0x03, 0x32, 0x71, 0xA1,
+ 0xF2, 0x1D, 0x09, 0xB7, 0xFD, 0x04, 0x12, 0x83, 0x18, 0xE5, 0xE7, 0xBC, 0xE3, 0xA2, 0x01, 0xE2 },
+ },
+ {
+ { 0xCA, 0xDC, 0xD5, 0xAE, 0x1B, 0x75, 0x6A, 0xB7, 0x41, 0xB3, 0x56, 0x9C, 0x42, 0xA5, 0x41, 0x1F,
+ 0x09, 0x3E, 0x4E, 0x1F, 0x01, 0x2E, 0xC5, 0x79, 0x91, 0xCB, 0xD6, 0xDB, 0xE0, 0x8F, 0xAA, 0xC1 },
+ },
+ {
+ { 0xCB, 0x7A, 0x43, 0x8D, 0x16, 0xE4, 0xA5, 0xF3, 0xC5, 0x6F, 0xDF, 0x19, 0x1E, 0x1D, 0xAF, 0x9F,
+ 0x32, 0x5C, 0x65, 0x0B, 0xD6, 0x2F, 0x07, 0xC4, 0x67, 0x71, 0x72, 0x07, 0x35, 0x1A, 0xE3, 0x29 },
+ },
+ {
+ { 0xCC, 0x2A, 0x70, 0x6F, 0xE6, 0x8F, 0x5D, 0x17, 0xF4, 0xAB, 0xAF, 0x60, 0x86, 0xE5, 0xBD, 0x97,
+ 0xAE, 0x35, 0xEB, 0x35, 0x9F, 0x75, 0xC0, 0x92, 0xBB, 0xA4, 0x93, 0xFE, 0x11, 0xF2, 0x69, 0xFD },
+ },
+ {
+ { 0xCC, 0x30, 0xD8, 0x19, 0xDE, 0x54, 0x05, 0xF6, 0x49, 0xC8, 0xB7, 0xA8, 0x14, 0x8F, 0x26, 0xD7,
+ 0x71, 0x08, 0x3E, 0xC5, 0x18, 0xF9, 0xB6, 0x6F, 0xF5, 0x47, 0xF2, 0x82, 0x2D, 0x11, 0x93, 0x6D },
+ },
+ {
+ { 0xCC, 0x65, 0xCD, 0xC5, 0x33, 0x62, 0xD4, 0x21, 0x62, 0x7E, 0xAE, 0xF5, 0xD0, 0xC8, 0xE4, 0xC4,
+ 0xE2, 0x40, 0xAD, 0xE0, 0xC9, 0xD4, 0x20, 0xBE, 0x67, 0x1E, 0x70, 0xF0, 0xFB, 0xAC, 0x8D, 0x0A },
+ },
+ {
+ { 0xCD, 0xB1, 0x62, 0x53, 0xD2, 0x2E, 0xD5, 0xD4, 0x26, 0xCF, 0xA1, 0xB0, 0x5C, 0xEC, 0xD8, 0x6E,
+ 0xF1, 0xB7, 0xDE, 0xAA, 0x07, 0xC5, 0x70, 0x5E, 0xBB, 0xAF, 0x7D, 0x9A, 0x80, 0x7D, 0x56, 0x16 },
+ },
+ {
+ { 0xCD, 0xC0, 0x39, 0xF3, 0xA2, 0xD1, 0xBB, 0xA5, 0xE8, 0x09, 0x4E, 0x55, 0x23, 0xCF, 0x60, 0x47,
+ 0x09, 0x7D, 0x4B, 0x3C, 0xD4, 0xEC, 0x4E, 0xD6, 0xAA, 0x8E, 0xB7, 0xB4, 0xD8, 0xB5, 0x77, 0x7D },
+ },
+ {
+ { 0xCD, 0xC4, 0xEA, 0x92, 0x02, 0xE3, 0x3E, 0xDD, 0x0F, 0x2D, 0x3A, 0xE8, 0x6A, 0xCA, 0xC7, 0xFB,
+ 0x25, 0x35, 0x4B, 0x02, 0x23, 0x5B, 0x09, 0x33, 0xAA, 0x81, 0xA3, 0x13, 0xB5, 0xFD, 0xFE, 0xEC },
+ },
+ {
+ { 0xCE, 0x4C, 0x2F, 0x8F, 0x16, 0x46, 0x8A, 0x58, 0x88, 0xE9, 0x0F, 0x73, 0x4E, 0x4D, 0x22, 0x02,
+ 0xDF, 0xAD, 0xBF, 0xA6, 0x6F, 0x5B, 0x35, 0x75, 0x2B, 0xAA, 0x76, 0x21, 0xA7, 0x60, 0xB0, 0x88 },
+ },
+ {
+ { 0xCE, 0x81, 0x44, 0x58, 0x54, 0x03, 0x1F, 0x3D, 0x0F, 0x5C, 0x88, 0x75, 0x46, 0x4D, 0xCD, 0x5B,
+ 0xA6, 0xC8, 0x90, 0xF4, 0x49, 0xB3, 0x20, 0x7B, 0xCA, 0x2B, 0xC9, 0x61, 0x82, 0x2D, 0x27, 0xC4 },
+ },
+ {
+ { 0xCF, 0xA0, 0xC0, 0x0C, 0xB2, 0xFB, 0x4B, 0x85, 0x7A, 0xAD, 0x22, 0xB1, 0x3A, 0x90, 0xE3, 0x46,
+ 0xA0, 0x3E, 0x6B, 0x79, 0xAB, 0xD5, 0xD2, 0x75, 0xB5, 0x43, 0x24, 0x68, 0x17, 0x92, 0xD6, 0xD1 },
+ },
+ {
+ { 0xD0, 0xF5, 0x93, 0xC1, 0xA8, 0x1B, 0x1E, 0xF8, 0x51, 0x69, 0x81, 0xEE, 0x56, 0xF1, 0xD5, 0x98,
+ 0xA2, 0xA6, 0x03, 0x48, 0x8C, 0x67, 0x8C, 0x1B, 0x7B, 0xBE, 0xA6, 0x44, 0x6B, 0x00, 0x83, 0xAD },
+ },
+ {
+ { 0xD2, 0x90, 0x3C, 0xA2, 0x55, 0x17, 0x27, 0xED, 0x01, 0x71, 0xCC, 0x4A, 0x43, 0xB3, 0xCA, 0xE0,
+ 0x09, 0xB7, 0x47, 0xB9, 0xF4, 0xF8, 0x48, 0x72, 0x92, 0x27, 0xBF, 0x59, 0x02, 0xF2, 0x3E, 0x47 },
+ },
+ {
+ { 0xD2, 0xE8, 0xA1, 0x23, 0x7A, 0x93, 0xF5, 0x78, 0xD1, 0xBA, 0x8F, 0x09, 0xE4, 0xFF, 0x10, 0x7B,
+ 0x62, 0x35, 0x78, 0x85, 0x42, 0xAA, 0x61, 0x83, 0xD1, 0x76, 0xDB, 0xF1, 0xC8, 0x8D, 0xCF, 0xB6 },
+ },
+ {
+ { 0xD5, 0x04, 0x88, 0x96, 0x86, 0x07, 0x29, 0xA8, 0xFA, 0x5D, 0x23, 0x57, 0x81, 0x2B, 0xA5, 0x6C,
+ 0xBE, 0x84, 0xC9, 0xAB, 0x7D, 0x14, 0xDF, 0x47, 0x64, 0xE0, 0xB6, 0x62, 0x0F, 0xA3, 0x20, 0x10 },
+ },
+ {
+ { 0xD5, 0x41, 0xA7, 0x7E, 0x13, 0x6E, 0x9E, 0x70, 0x3B, 0xB9, 0x9F, 0x80, 0x68, 0xCF, 0xEE, 0x86,
+ 0xA4, 0xB9, 0xF0, 0x89, 0xE0, 0x2D, 0x0C, 0x6C, 0xB6, 0xD4, 0xA3, 0x94, 0x6C, 0x6B, 0x16, 0x7A },
+ },
+ {
+ { 0xD5, 0x83, 0x94, 0x96, 0xCD, 0xC8, 0x5B, 0xE3, 0xD1, 0xF1, 0xAC, 0x65, 0x2E, 0xFA, 0x92, 0xBE,
+ 0xA3, 0xB0, 0x61, 0xC1, 0x3D, 0xAD, 0x5A, 0x82, 0x11, 0x22, 0xCF, 0xE9, 0xC7, 0x1A, 0x5A, 0x32 },
+ },
+ {
+ { 0xD5, 0xA4, 0xEE, 0x46, 0x95, 0xB5, 0x65, 0xA6, 0x7E, 0x50, 0x48, 0x66, 0xFE, 0x5B, 0xA3, 0xC0,
+ 0xED, 0xCA, 0xEE, 0xD5, 0x2A, 0xD0, 0xAF, 0x07, 0xE6, 0x79, 0x17, 0x73, 0x85, 0x12, 0xC8, 0xF5 },
+ },
+ {
+ { 0xD6, 0x25, 0xC0, 0x59, 0x2B, 0x25, 0xDC, 0x03, 0xAA, 0x7E, 0x87, 0x8E, 0x6A, 0x85, 0x09, 0x1B,
+ 0xAA, 0x07, 0x8D, 0x26, 0x8B, 0xBD, 0xB4, 0x9F, 0x09, 0x67, 0x94, 0x08, 0x61, 0x2D, 0x1E, 0xFE },
+ },
+ {
+ { 0xD6, 0xD1, 0xB3, 0x5C, 0xBC, 0x12, 0xFB, 0x1C, 0x70, 0xA0, 0xB4, 0x3B, 0xA5, 0x9A, 0xB3, 0xD3,
+ 0x22, 0x5F, 0x37, 0x32, 0x64, 0xDD, 0x87, 0xFB, 0xCA, 0x00, 0x61, 0xEC, 0x1C, 0x4D, 0xA1, 0x1A },
+ },
+ {
+ { 0xD7, 0x32, 0x49, 0x74, 0xB5, 0x60, 0x09, 0x62, 0x17, 0x61, 0xF7, 0xC0, 0xFF, 0x68, 0x9D, 0xDE,
+ 0x47, 0x74, 0x99, 0x85, 0xE1, 0xEE, 0x8B, 0x5C, 0x89, 0x61, 0xDD, 0x8F, 0x6A, 0x78, 0xBB, 0xF5 },
+ },
+ {
+ { 0xD9, 0x2E, 0x3E, 0xE3, 0x82, 0xC8, 0xDC, 0xAF, 0xA0, 0x39, 0x3D, 0x9F, 0x9A, 0x00, 0xBF, 0x4C,
+ 0xD9, 0xD5, 0x64, 0x26, 0x2B, 0x18, 0x0F, 0x68, 0x16, 0x0B, 0x20, 0x34, 0xC5, 0x44, 0xD1, 0x0A },
+ },
+ {
+ { 0xD9, 0x65, 0xF7, 0x41, 0x62, 0x04, 0xDA, 0x83, 0x1A, 0xF6, 0x6B, 0xFA, 0x8F, 0x90, 0xD1, 0x41,
+ 0xE9, 0x93, 0xF0, 0x00, 0x21, 0x33, 0xF2, 0x8D, 0xE9, 0x7F, 0x56, 0x4A, 0x1D, 0x60, 0x4E, 0xCC },
+ },
+ {
+ { 0xD9, 0x7F, 0x55, 0xB9, 0x57, 0x9B, 0x05, 0xAE, 0x4A, 0x3E, 0xD7, 0xFC, 0x55, 0x8C, 0x58, 0x45,
+ 0x64, 0x51, 0x60, 0xDA, 0xB3, 0x53, 0x85, 0xC1, 0x38, 0xBC, 0x89, 0x9C, 0x4D, 0xAD, 0x8B, 0x36 },
+ },
+ {
+ { 0xDA, 0xDF, 0x97, 0x13, 0x34, 0x14, 0xAD, 0x51, 0x3F, 0xC7, 0x50, 0x14, 0xE9, 0x56, 0x65, 0xDA,
+ 0xD7, 0x76, 0xB1, 0x50, 0x4B, 0x15, 0x67, 0x43, 0x4F, 0xD8, 0x2A, 0x79, 0xA2, 0x20, 0xE9, 0xA1 },
+ },
+ {
+ { 0xDE, 0xCD, 0xB9, 0xFC, 0x1D, 0xDE, 0xC9, 0x7E, 0x09, 0xC3, 0x02, 0x6A, 0xCE, 0xB7, 0x6B, 0xDA,
+ 0xE9, 0xDE, 0xB6, 0x62, 0x75, 0x1D, 0xDA, 0x34, 0x9D, 0x2F, 0xA6, 0xBD, 0x75, 0xCA, 0x59, 0x14 },
+ },
+ {
+ { 0xDE, 0xD1, 0x9A, 0xD5, 0xDE, 0x99, 0x65, 0xD9, 0x22, 0x5C, 0x1B, 0xBA, 0x5F, 0xB4, 0xD8, 0x90,
+ 0xC8, 0xE5, 0xC0, 0x35, 0xE4, 0x85, 0x27, 0x52, 0xB6, 0x69, 0xB0, 0x40, 0x0F, 0x24, 0xF1, 0x74 },
+ },
+ {
+ { 0xDF, 0x30, 0xBF, 0x8D, 0x1B, 0xF9, 0x37, 0x8E, 0x43, 0x3E, 0xF9, 0xE1, 0xB3, 0xA2, 0x28, 0xA0,
+ 0x7E, 0x36, 0x58, 0xA5, 0xBC, 0x43, 0x88, 0x23, 0x45, 0x4D, 0xB0, 0x6A, 0x67, 0x94, 0x4C, 0x6E },
+ },
+ {
+ { 0xDF, 0x69, 0xF9, 0x6A, 0x85, 0x67, 0x8F, 0x6C, 0xAF, 0x3F, 0xDE, 0x25, 0xEC, 0xFB, 0x5D, 0xF4,
+ 0x74, 0x70, 0x87, 0xC2, 0xAF, 0x3B, 0x00, 0x65, 0xFB, 0x15, 0x10, 0x55, 0xCB, 0xCB, 0xA8, 0xC1 },
+ },
+ {
+ { 0xE0, 0x0B, 0xD7, 0x86, 0xD1, 0xF2, 0xF4, 0x46, 0xC4, 0xBA, 0x83, 0x99, 0xD4, 0xD8, 0xD5, 0xA0,
+ 0xD1, 0x98, 0x57, 0x8F, 0x42, 0x99, 0xFD, 0xFD, 0xAF, 0xF7, 0x8C, 0x3F, 0x67, 0x71, 0xF3, 0x94 },
+ },
+ {
+ { 0xE0, 0x8B, 0x2C, 0xC2, 0x7A, 0xE8, 0xE2, 0xEF, 0x1A, 0x33, 0x01, 0x7A, 0x9A, 0xC2, 0x5D, 0xDA,
+ 0xFB, 0x5E, 0xA1, 0x12, 0xC9, 0x56, 0xB0, 0x02, 0xFE, 0x6C, 0x79, 0x80, 0x14, 0xAA, 0x90, 0x65 },
+ },
+ {
+ { 0xE1, 0xB2, 0xE8, 0x6B, 0x0D, 0xA8, 0x69, 0xE9, 0x25, 0x26, 0x6C, 0x1B, 0x56, 0x88, 0x34, 0x5A,
+ 0x17, 0xB0, 0xF6, 0xE2, 0xA2, 0x14, 0x94, 0x54, 0x7E, 0xAC, 0x09, 0x7C, 0x8B, 0xF5, 0x3C, 0x5A },
+ },
+ {
+ { 0xE1, 0xD6, 0x44, 0xA0, 0x96, 0xBD, 0x8A, 0x6C, 0xAC, 0xBB, 0xDA, 0x3E, 0x7F, 0xC3, 0x38, 0xEA,
+ 0xDD, 0xC1, 0x2F, 0x23, 0x6C, 0x72, 0x61, 0xE4, 0x5F, 0x8A, 0xD2, 0xD8, 0x42, 0x42, 0x4F, 0x72 },
+ },
+ {
+ { 0xE2, 0x24, 0x10, 0xB5, 0xA6, 0x7F, 0xED, 0xC2, 0x64, 0x69, 0x4C, 0x44, 0x9D, 0x84, 0xFA, 0x1A,
+ 0x02, 0xBC, 0x8B, 0x21, 0x28, 0xC1, 0x25, 0x60, 0x71, 0x58, 0xC9, 0x1B, 0x05, 0x38, 0x6C, 0x6A },
+ },
+ {
+ { 0xE2, 0xA8, 0x47, 0xC3, 0xF0, 0x9B, 0xEB, 0x6F, 0x05, 0x68, 0x6F, 0x17, 0x79, 0x1B, 0x05, 0xF1,
+ 0xFE, 0x25, 0xF7, 0x71, 0x86, 0x9C, 0x42, 0x63, 0xA5, 0x5B, 0x94, 0x18, 0x77, 0xE4, 0x79, 0x04 },
+ },
+ {
+ { 0xE2, 0xF3, 0x9A, 0x9D, 0x48, 0xA3, 0x22, 0x10, 0x55, 0xB3, 0xC8, 0xA3, 0xEB, 0x14, 0x39, 0xD6,
+ 0xB8, 0x73, 0x01, 0x3E, 0xE4, 0xD0, 0x97, 0x12, 0x20, 0x64, 0xF2, 0x7E, 0xC0, 0x3D, 0xD4, 0xDA },
+ },
+ {
+ { 0xE2, 0xF5, 0xDE, 0x57, 0xCD, 0x67, 0x24, 0x9A, 0x7E, 0x1F, 0x45, 0x5B, 0x85, 0xC0, 0x6F, 0x0D,
+ 0x80, 0x9E, 0x75, 0xA5, 0x5C, 0x6B, 0x05, 0x48, 0x16, 0xE0, 0x19, 0x89, 0x9A, 0x3A, 0x02, 0xFF },
+ },
+ {
+ { 0xE6, 0x44, 0xD1, 0x1C, 0x37, 0x07, 0x0F, 0x89, 0x69, 0x33, 0x08, 0x17, 0x8D, 0x6B, 0xE4, 0x95,
+ 0x94, 0x96, 0x92, 0xC1, 0xFB, 0xEB, 0x30, 0xED, 0x32, 0x9B, 0x74, 0x02, 0x7F, 0xCF, 0xFD, 0x48 },
+ },
+ {
+ { 0xE6, 0xB0, 0xF2, 0xE2, 0x5B, 0xD5, 0x16, 0xE4, 0xBB, 0xA3, 0x7A, 0x2B, 0xF2, 0xE2, 0xC7, 0x2A,
+ 0x1E, 0x53, 0x9C, 0x60, 0x30, 0xF3, 0xCF, 0x9B, 0xBE, 0x5E, 0x79, 0x72, 0x8D, 0x68, 0x64, 0x78 },
+ },
+ {
+ { 0xE6, 0xE5, 0x4D, 0xE7, 0xB4, 0x97, 0x54, 0xD3, 0x57, 0xB0, 0xA8, 0xD9, 0x4A, 0x4D, 0x4F, 0x80,
+ 0xAC, 0xD1, 0x99, 0x4C, 0xCC, 0x1C, 0x99, 0x08, 0xE9, 0xF0, 0xD9, 0x21, 0xE4, 0x28, 0xB8, 0x38 },
+ },
+ {
+ { 0xE7, 0x0C, 0xBB, 0x7A, 0xF7, 0xAA, 0x20, 0xB9, 0x89, 0x0B, 0xC1, 0xF9, 0xFA, 0x00, 0xD8, 0x09,
+ 0x0B, 0x5A, 0xC9, 0x82, 0x5E, 0xA9, 0xD2, 0xFD, 0xF7, 0x7C, 0xA4, 0xDA, 0xE9, 0x44, 0x51, 0xB2 },
+ },
+ {
+ { 0xE8, 0x16, 0xF9, 0x92, 0x94, 0xA1, 0x3A, 0xC2, 0xFA, 0x2B, 0xFB, 0x76, 0xC2, 0x2D, 0xFA, 0x71,
+ 0xBC, 0x3D, 0xA4, 0x8F, 0x67, 0x1E, 0xF7, 0x7C, 0x00, 0xAA, 0x8E, 0x45, 0x9B, 0x7C, 0xC8, 0x2A },
+ },
+ {
+ { 0xE8, 0x21, 0x3C, 0x45, 0x51, 0x81, 0x61, 0xBC, 0x36, 0x37, 0x3D, 0xCD, 0x2D, 0x4B, 0x21, 0xB7,
+ 0x6A, 0x7C, 0x06, 0x6D, 0xF5, 0x52, 0x6E, 0x88, 0x8B, 0x6E, 0xED, 0x09, 0xA9, 0xEE, 0xD0, 0x62 },
+ },
+ {
+ { 0xE9, 0xF5, 0x71, 0xC7, 0x71, 0x64, 0xAB, 0xEA, 0xE1, 0x85, 0x28, 0x37, 0x5C, 0xFD, 0xC7, 0x21,
+ 0x9A, 0x6B, 0xDE, 0x46, 0x1B, 0x19, 0x73, 0xBE, 0x2B, 0xB8, 0xBD, 0xF0, 0xDA, 0x78, 0xB2, 0xB4 },
+ },
+ {
+ { 0xEB, 0x11, 0x63, 0xAA, 0xEF, 0xE8, 0xFD, 0x88, 0xE1, 0x32, 0x7B, 0x48, 0xA9, 0xC0, 0x06, 0x2E,
+ 0x06, 0xF0, 0xA6, 0xEA, 0xA0, 0xA0, 0x18, 0x24, 0x7F, 0x9F, 0xA4, 0xE3, 0x4E, 0x3A, 0x47, 0x4C },
+ },
+ {
+ { 0xEC, 0x4B, 0xBD, 0xEB, 0x15, 0x12, 0x1D, 0x96, 0x76, 0x4D, 0x6C, 0x01, 0xB2, 0x7E, 0xD5, 0xAE,
+ 0x86, 0x46, 0x5C, 0x46, 0xD5, 0xA4, 0x0E, 0x34, 0xAE, 0xFC, 0x09, 0x2D, 0x3E, 0x8B, 0xB1, 0x76 },
+ },
+ {
+ { 0xEC, 0x5F, 0xA4, 0x73, 0x12, 0x1E, 0x3F, 0x49, 0xF0, 0x95, 0x3A, 0x2A, 0x91, 0x83, 0x39, 0xE3,
+ 0x6F, 0x3C, 0xB6, 0xB8, 0xD8, 0xB8, 0x9E, 0x91, 0x74, 0x23, 0xDA, 0xCE, 0xAC, 0xE6, 0xD5, 0x8A },
+ },
+ {
+ { 0xEC, 0xCE, 0x4E, 0x52, 0x82, 0xFD, 0x2E, 0xE0, 0x03, 0xA4, 0x03, 0x2C, 0x80, 0xD3, 0x32, 0x1A,
+ 0x69, 0x47, 0x25, 0x98, 0x94, 0x59, 0x09, 0xCB, 0x25, 0x55, 0x7A, 0xA8, 0x47, 0x74, 0x2D, 0xDF },
+ },
+ {
+ { 0xED, 0x5B, 0xB8, 0x6A, 0x95, 0xA5, 0xFE, 0x2B, 0x17, 0x08, 0xF2, 0x56, 0x75, 0x4A, 0x89, 0xC4,
+ 0x29, 0x67, 0x9B, 0x30, 0x75, 0x8E, 0xE0, 0x12, 0x2B, 0x9E, 0x50, 0x85, 0x8D, 0xE2, 0x10, 0x4B },
+ },
+ {
+ { 0xED, 0xC1, 0xBF, 0x3E, 0xFB, 0xF7, 0xE1, 0xD9, 0x5E, 0x19, 0xC5, 0x5E, 0xCA, 0xE7, 0x7E, 0x83,
+ 0x69, 0x46, 0xAB, 0x0A, 0x26, 0xA7, 0x8E, 0x32, 0xA4, 0x72, 0xC9, 0xD3, 0x6C, 0x69, 0xCE, 0xCD },
+ },
+ {
+ { 0xED, 0xF4, 0xDF, 0x97, 0x2C, 0xAD, 0x6C, 0x47, 0x0B, 0xAB, 0x5D, 0x66, 0x42, 0xF6, 0x60, 0xB8,
+ 0x42, 0xD6, 0xC9, 0x73, 0x07, 0x44, 0x93, 0xE4, 0xEF, 0x1B, 0xBF, 0x31, 0x1A, 0x92, 0x79, 0x95 },
+ },
+ {
+ { 0xEE, 0x0C, 0xF6, 0x2B, 0x9D, 0x8E, 0x42, 0xA2, 0x23, 0xB9, 0xA9, 0x60, 0xB5, 0xE9, 0x67, 0x0C,
+ 0xCC, 0x34, 0x6D, 0x89, 0x93, 0x8F, 0xFA, 0x5D, 0xF7, 0x98, 0x65, 0xE4, 0x13, 0xD6, 0x31, 0x54 },
+ },
+ {
+ { 0xEE, 0x34, 0xE1, 0xA1, 0x9B, 0xC8, 0x89, 0xF8, 0x5F, 0x7F, 0x0F, 0x5B, 0xF8, 0x72, 0xB1, 0xAC,
+ 0x56, 0x5E, 0xC6, 0xF1, 0x9D, 0xB5, 0x17, 0xBA, 0x4E, 0xD7, 0x55, 0xC4, 0x18, 0x5F, 0x69, 0xE8 },
+ },
+ {
+ { 0xEF, 0x36, 0xA2, 0x29, 0x89, 0x65, 0xE4, 0x98, 0x84, 0x59, 0xB9, 0x21, 0x6A, 0xB3, 0x3C, 0x3C,
+ 0xA8, 0x42, 0xD2, 0x16, 0x83, 0xB6, 0x2A, 0x2B, 0xF1, 0x53, 0x0D, 0x30, 0xB0, 0xAE, 0x78, 0x25 },
+ },
+ {
+ { 0xEF, 0xAF, 0xCA, 0x84, 0x90, 0x30, 0x7B, 0x0F, 0x62, 0x2B, 0xF4, 0x3A, 0x0E, 0xB3, 0xC5, 0x1A,
+ 0xCB, 0xDD, 0xDE, 0xDC, 0x23, 0x92, 0xF1, 0x61, 0xAC, 0xED, 0x16, 0x71, 0xA6, 0x53, 0x60, 0x7E },
+ },
+ {
+ { 0xEF, 0xD1, 0xE0, 0xE7, 0x3F, 0xA8, 0x71, 0x00, 0xB7, 0x6A, 0x93, 0x23, 0x49, 0xC4, 0x5D, 0x09,
+ 0xB2, 0x8B, 0x2D, 0x8A, 0x00, 0x17, 0x19, 0xA5, 0x8D, 0xFA, 0xCC, 0x74, 0x84, 0xC7, 0xCF, 0x42 },
+ },
+ {
+ { 0xF0, 0x11, 0xAD, 0x9E, 0xDD, 0x4F, 0xE7, 0x18, 0x8D, 0x77, 0x2E, 0xBA, 0xFA, 0x5B, 0xF5, 0x32,
+ 0x92, 0x47, 0x77, 0x88, 0xDC, 0x12, 0x80, 0x32, 0x76, 0xB0, 0x00, 0xC4, 0x41, 0x91, 0x03, 0xF0 },
+ },
+ {
+ { 0xF0, 0x2F, 0x9D, 0xA4, 0x5D, 0x9E, 0xB9, 0x86, 0x19, 0x4E, 0x06, 0xF5, 0xE6, 0x18, 0x95, 0x45,
+ 0x12, 0xC9, 0x02, 0x6E, 0x7C, 0xA7, 0xB5, 0x1E, 0x66, 0x5D, 0xB6, 0xAD, 0xBA, 0xC1, 0xF6, 0x00 },
+ },
+ {
+ { 0xF0, 0x6B, 0x35, 0x95, 0x36, 0xD1, 0x34, 0x32, 0x8B, 0x36, 0x00, 0x4D, 0xA9, 0xA9, 0x19, 0x0C,
+ 0x3A, 0x76, 0x69, 0xE8, 0x27, 0x8D, 0xB9, 0xF7, 0x58, 0x57, 0xC4, 0x8D, 0x64, 0x4B, 0xE2, 0x03 },
+ },
+ {
+ { 0xF0, 0xCF, 0xC7, 0x79, 0x13, 0x39, 0x7D, 0xE2, 0x38, 0xED, 0xB5, 0x9F, 0x0F, 0x99, 0x23, 0xC6,
+ 0xD4, 0x11, 0x0A, 0x4B, 0x3A, 0xC8, 0xAC, 0x76, 0x55, 0x6A, 0x0C, 0x92, 0x44, 0xF0, 0x3F, 0xC1 },
+ },
+ {
+ { 0xF2, 0xB1, 0x95, 0x84, 0x6E, 0xE2, 0xB9, 0xAB, 0x5F, 0x18, 0xE6, 0x80, 0x21, 0xF8, 0xDF, 0x7C,
+ 0x0B, 0x60, 0x58, 0xDE, 0xDE, 0x86, 0xC5, 0xD5, 0x90, 0xF2, 0xE8, 0x64, 0x3A, 0xFE, 0x04, 0x52 },
+ },
+ {
+ { 0xF2, 0xE5, 0x30, 0x0C, 0x39, 0xF2, 0x86, 0xC6, 0x78, 0x99, 0x90, 0x9C, 0x7C, 0xE7, 0x35, 0x9B,
+ 0x09, 0x45, 0xD2, 0xAF, 0xD3, 0x4A, 0x6D, 0xD6, 0x9E, 0x08, 0xCD, 0xA5, 0x44, 0xC8, 0x7B, 0x3A },
+ },
+ {
+ { 0xF3, 0x0C, 0x0A, 0xED, 0x70, 0x6D, 0x22, 0x55, 0x5F, 0x07, 0x09, 0x6A, 0xF4, 0xB8, 0xBE, 0xDC,
+ 0x16, 0x3C, 0x0F, 0x6E, 0xD5, 0x34, 0x6E, 0xFC, 0x28, 0xE8, 0xCF, 0xAF, 0x84, 0x2F, 0xA5, 0xD9 },
+ },
+ {
+ { 0xF6, 0x13, 0xD5, 0x90, 0x46, 0xD1, 0x66, 0x71, 0xD3, 0xC5, 0x60, 0x17, 0x6F, 0x3D, 0x77, 0xFD,
+ 0xC5, 0x1E, 0x5F, 0x57, 0xB5, 0xE4, 0x8A, 0xE7, 0xA4, 0xB9, 0x70, 0x0A, 0x11, 0xD4, 0x69, 0x3A },
+ },
+ {
+ { 0xF6, 0x54, 0x6B, 0x2F, 0xFE, 0x2B, 0xAE, 0xF7, 0x35, 0xE8, 0x25, 0x67, 0xA6, 0xE2, 0x36, 0x75,
+ 0x03, 0x94, 0xC1, 0x19, 0x14, 0x09, 0x87, 0x0C, 0x6F, 0xBE, 0x95, 0x2D, 0x08, 0xA3, 0x3A, 0xBA },
+ },
+ {
+ { 0xF6, 0xAA, 0xEF, 0x12, 0xFC, 0x25, 0x2D, 0xD9, 0xE7, 0xF7, 0x75, 0x2C, 0x2F, 0x74, 0x5D, 0x59,
+ 0xD6, 0x37, 0x57, 0xC6, 0xCC, 0x14, 0xD2, 0x25, 0x3A, 0x64, 0x7C, 0xD1, 0x81, 0x49, 0x39, 0x93 },
+ },
+ {
+ { 0xF8, 0x64, 0x44, 0x3E, 0x2F, 0x63, 0x9E, 0x7C, 0xFF, 0xD2, 0x42, 0x21, 0xF6, 0x1B, 0xBF, 0xF0,
+ 0x7C, 0xCE, 0x5C, 0x61, 0xDD, 0xB1, 0x68, 0xB3, 0xB4, 0x04, 0xD7, 0xC8, 0xCD, 0xCA, 0x18, 0xB2 },
+ },
+ {
+ { 0xF8, 0x94, 0xF9, 0x67, 0x36, 0x9C, 0xE7, 0xCF, 0xA3, 0x1A, 0xC1, 0x9A, 0x66, 0x65, 0xB0, 0xC4,
+ 0x24, 0xBA, 0x40, 0x8A, 0xD5, 0xD3, 0x65, 0xF1, 0x68, 0xD8, 0xBE, 0xEB, 0x79, 0xF4, 0x89, 0xF3 },
+ },
+ {
+ { 0xF8, 0xCF, 0x1E, 0x08, 0x6A, 0x6A, 0x06, 0x3F, 0xAD, 0x25, 0x74, 0x25, 0xAA, 0xE7, 0x20, 0x01,
+ 0x40, 0x05, 0xB4, 0x15, 0x91, 0x2D, 0xBB, 0x8C, 0x0B, 0xC9, 0x99, 0xAF, 0x48, 0x48, 0xCF, 0xE5 },
+ },
+ {
+ { 0xFB, 0x9A, 0xF7, 0x9D, 0xEA, 0x18, 0xAF, 0x62, 0x99, 0x85, 0x0E, 0x25, 0x15, 0x9B, 0x4F, 0xB2,
+ 0x24, 0xCB, 0xB0, 0xF1, 0x4E, 0xAD, 0x7E, 0x85, 0xF6, 0x0C, 0x2A, 0xB2, 0x09, 0xEA, 0x45, 0x0D },
+ },
+ {
+ { 0xFB, 0xC4, 0xC9, 0xBA, 0xCF, 0xE3, 0xDA, 0x64, 0x13, 0x18, 0x26, 0x6B, 0x72, 0x58, 0x56, 0x00,
+ 0x35, 0xBC, 0x64, 0x60, 0x8E, 0x34, 0xB9, 0x90, 0xCA, 0x92, 0xA5, 0x52, 0xF3, 0x14, 0x21, 0x61 },
+ },
+ {
+ { 0xFB, 0xED, 0xD3, 0x88, 0x89, 0xF0, 0xB4, 0x1F, 0x73, 0x4D, 0xE2, 0xF4, 0xC9, 0xD6, 0xF2, 0x7C,
+ 0x8D, 0x4A, 0xA9, 0xAB, 0x73, 0x64, 0x91, 0xE1, 0x64, 0xE1, 0x21, 0xB7, 0xBC, 0xAF, 0x44, 0xE8 },
+ },
+ {
+ { 0xFC, 0x01, 0xA5, 0x5A, 0x36, 0xCC, 0x8B, 0x7B, 0x7C, 0xA2, 0xEA, 0xB0, 0x84, 0x60, 0xC2, 0x8D,
+ 0x1D, 0x6C, 0xD8, 0x9C, 0x57, 0x59, 0x94, 0x05, 0xD5, 0x37, 0x4B, 0x91, 0xAA, 0xEB, 0xC8, 0x79 },
+ },
+ {
+ { 0xFC, 0x4D, 0x9A, 0x37, 0xE5, 0xF7, 0x32, 0x72, 0xD0, 0xA9, 0xDF, 0xCC, 0xE9, 0x03, 0x12, 0xC7,
+ 0x52, 0xE1, 0xB5, 0x2E, 0xB6, 0x54, 0xC4, 0x2C, 0x36, 0x94, 0x4B, 0x90, 0x2A, 0x30, 0x41, 0x07 },
+ },
+ {
+ { 0xFC, 0x56, 0xDB, 0xA1, 0xE7, 0xAF, 0xBD, 0xAA, 0x07, 0x33, 0xC6, 0x91, 0x1C, 0x5F, 0x1F, 0x18,
+ 0x28, 0xCB, 0x12, 0x98, 0x31, 0x40, 0x1A, 0x3C, 0xFD, 0xEA, 0xA7, 0x24, 0x62, 0x95, 0x35, 0x94 },
+ },
+ {
+ { 0xFC, 0x83, 0xC2, 0x89, 0x89, 0x5A, 0x92, 0x08, 0xC9, 0xB1, 0x7A, 0x16, 0xBC, 0xE5, 0xCE, 0x80,
+ 0xE8, 0xF4, 0xA0, 0x77, 0x21, 0x25, 0x29, 0xCE, 0x0B, 0xC7, 0xF5, 0x42, 0xC6, 0xCB, 0xDE, 0x1A },
+ },
+ {
+ { 0xFC, 0xA6, 0x23, 0x5D, 0x2A, 0xA4, 0xB1, 0xB2, 0x51, 0x50, 0x78, 0x57, 0xB4, 0xF0, 0x08, 0xDF,
+ 0xD5, 0x27, 0x04, 0x2C, 0xE0, 0x45, 0x01, 0xAA, 0xE2, 0x9D, 0xD2, 0x05, 0xBB, 0xEF, 0xCE, 0x0D },
+ },
+ {
+ { 0xFC, 0xE7, 0x34, 0xE1, 0x2B, 0x8E, 0xFB, 0x43, 0x12, 0x71, 0xBF, 0xF6, 0x7A, 0x7A, 0x0A, 0x93,
+ 0xB2, 0x19, 0xDD, 0x5E, 0x5D, 0xCC, 0x12, 0x58, 0x59, 0x4D, 0x96, 0xFC, 0xE1, 0x93, 0xB8, 0x60 },
+ },
+ {
+ { 0xFD, 0x9C, 0xFE, 0x14, 0xDA, 0xD8, 0x97, 0x8C, 0x5B, 0xC8, 0x88, 0x93, 0x8F, 0x16, 0xF3, 0xB3,
+ 0x98, 0xF7, 0x63, 0xA3, 0xAD, 0xAF, 0xAA, 0x4A, 0xD9, 0x41, 0xB7, 0xE3, 0x87, 0xEB, 0x4F, 0x4A },
+ },
+ {
+ { 0xFD, 0xED, 0x92, 0xCB, 0x40, 0x91, 0x66, 0x82, 0x3A, 0x35, 0xE2, 0x17, 0xF3, 0x0B, 0x38, 0xC4,
+ 0x86, 0xF8, 0x3E, 0xF2, 0xD4, 0xF2, 0x7B, 0x05, 0xF1, 0x8C, 0x74, 0x49, 0x81, 0x33, 0x9A, 0x1C },
+ },
+ {
+ { 0xFE, 0x26, 0xB2, 0xA6, 0x45, 0xA3, 0x1A, 0x91, 0x11, 0x00, 0x09, 0x9A, 0xA9, 0xA2, 0x93, 0x9F,
+ 0x49, 0xE9, 0xFB, 0xEA, 0x64, 0x48, 0x7B, 0xDF, 0x68, 0xA5, 0x23, 0x70, 0x32, 0x92, 0xD6, 0xA0 },
+ },
+ {
+ { 0xFE, 0x42, 0x1B, 0x24, 0x4E, 0x0E, 0x81, 0x6D, 0x9F, 0x26, 0xB3, 0x52, 0xC8, 0x31, 0xD9, 0x30,
+ 0xE1, 0xC1, 0xC5, 0xD2, 0xFA, 0x4E, 0x0A, 0x1C, 0x77, 0x96, 0xA1, 0xF2, 0x02, 0x0E, 0xF1, 0x67 },
+ },
+ {
+ { 0xFE, 0x4F, 0x35, 0x6C, 0x7F, 0x9B, 0xFC, 0x17, 0xFF, 0xCB, 0x68, 0xD0, 0x76, 0x4E, 0xCB, 0x2A,
+ 0x87, 0xCA, 0xA0, 0xAE, 0x4C, 0xB5, 0x66, 0x62, 0x21, 0x04, 0xD3, 0x6F, 0xFB, 0x52, 0xCB, 0x29 },
+ },
+ {
+ { 0xFF, 0x82, 0x6E, 0x2D, 0x0C, 0xB7, 0x71, 0x68, 0x68, 0x67, 0x5A, 0xE4, 0xB4, 0x31, 0xB6, 0x37,
+ 0x1E, 0x9F, 0x0C, 0xDF, 0xCC, 0xB4, 0x9D, 0x43, 0xBA, 0x30, 0x49, 0xBF, 0xDD, 0x2C, 0x41, 0xB1 },
+ },
+ {
+ { 0xFF, 0xDC, 0x6B, 0x85, 0xFE, 0x7B, 0x10, 0x83, 0xB5, 0x41, 0x6F, 0x80, 0x6F, 0xC2, 0x44, 0xB9,
+ 0xE4, 0xDF, 0x42, 0x99, 0xFB, 0xE3, 0xF6, 0x81, 0xAF, 0x3F, 0x5C, 0xF4, 0x22, 0x5A, 0x8E, 0xAF },
+ },
+};
diff --git a/security/certverifier/CTKnownLogs.h b/security/certverifier/CTKnownLogs.h
new file mode 100644
index 000000000..ff95c31b9
--- /dev/null
+++ b/security/certverifier/CTKnownLogs.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This 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/. */
+
+/* This file is generated by print_log_list.py from
+ * https://github.com/google/certificate-transparency/ */
+
+#ifndef CTKnownLogs_h
+#define CTKnownLogs_h
+
+#include <stddef.h>
+
+struct CTLogInfo {
+ const char* const logName;
+ const char* const logUrl;
+ const char* const logKey;
+ const size_t logKeyLength;
+};
+
+const CTLogInfo kCTLogList[] = {
+ { "Google 'Pilot' log",
+ "https://ct.googleapis.com/pilot/",
+ "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+ "\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x7d\xa8\x4b\x12\x29\x80\xa3\x3d\xad"
+ "\xd3\x5a\x77\xb8\xcc\xe2\x88\xb3\xa5\xfd\xf1\xd3\x0c\xcd\x18\x0c\xe8\x41"
+ "\x46\xe8\x81\x01\x1b\x15\xe1\x4b\xf1\x1b\x62\xdd\x36\x0a\x08\x18\xba\xed"
+ "\x0b\x35\x84\xd0\x9e\x40\x3c\x2d\x9e\x9b\x82\x65\xbd\x1f\x04\x10\x41\x4c"
+ "\xa0",
+ 91 },
+ { "Google 'Aviator' log",
+ "https://ct.googleapis.com/aviator/",
+ "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+ "\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\xf4\xcc\x69\xb2\xe4\x0e\x90\xa3"
+ "\x8a\xea\x5a\x70\x09\x4f\xef\x13\x62\xd0\x8d\x49\x60\xff\x1b\x40\x50\x07"
+ "\x0c\x6d\x71\x86\xda\x25\x49\x8d\x65\xe1\x08\x0d\x47\x34\x6b\xbd\x27\xbc"
+ "\x96\x21\x3e\x34\xf5\x87\x76\x31\xb1\x7f\x1d\xc9\x85\x3b\x0d\xf7\x1f\x3f"
+ "\xe9",
+ 91 },
+ { "DigiCert Log Server",
+ "https://ct1.digicert-ct.com/log/",
+ "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+ "\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x02\x46\xc5\xbe\x1b\xbb\x82\x40\x16"
+ "\xe8\xc1\xd2\xac\x19\x69\x13\x59\xf8\xf8\x70\x85\x46\x40\xb9\x38\xb0\x23"
+ "\x82\xa8\x64\x4c\x7f\xbf\xbb\x34\x9f\x4a\x5f\x28\x8a\xcf\x19\xc4\x00\xf6"
+ "\x36\x06\x93\x65\xed\x4c\xf5\xa9\x21\x62\x5a\xd8\x91\xeb\x38\x24\x40\xac"
+ "\xe8",
+ 91 },
+ { "Google 'Rocketeer' log",
+ "https://ct.googleapis.com/rocketeer/",
+ "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+ "\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x20\x5b\x18\xc8\x3c\xc1\x8b\xb3\x31"
+ "\x08\x00\xbf\xa0\x90\x57\x2b\xb7\x47\x8c\x6f\xb5\x68\xb0\x8e\x90\x78\xe9"
+ "\xa0\x73\xea\x4f\x28\x21\x2e\x9c\xc0\xf4\x16\x1b\xaa\xf9\xd5\xd7\xa9\x80"
+ "\xc3\x4e\x2f\x52\x3c\x98\x01\x25\x46\x24\x25\x28\x23\x77\x2d\x05\xc2\x40"
+ "\x7a",
+ 91 },
+ { "Certly.IO log",
+ "https://log.certly.io/",
+ "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+ "\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x0b\x23\xcb\x85\x62\x98\x61\x48\x04"
+ "\x73\xeb\x54\x5d\xf3\xd0\x07\x8c\x2d\x19\x2d\x8c\x36\xf5\xeb\x8f\x01\x42"
+ "\x0a\x7c\x98\x26\x27\xc1\xb5\xdd\x92\x93\xb0\xae\xf8\x9b\x3d\x0c\xd8\x4c"
+ "\x4e\x1d\xf9\x15\xfb\x47\x68\x7b\xba\x66\xb7\x25\x9c\xd0\x4a\xc2\x66\xdb"
+ "\x48",
+ 91 },
+ { "Izenpe log",
+ "https://ct.izenpe.com/",
+ "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+ "\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x27\x64\x39\x0c\x2d\xdc\x50\x18\xf8"
+ "\x21\x00\xa2\x0e\xed\x2c\xea\x3e\x75\xba\x9f\x93\x64\x09\x00\x11\xc4\x11"
+ "\x17\xab\x5c\xcf\x0f\x74\xac\xb5\x97\x90\x93\x00\x5b\xb8\xeb\xf7\x27\x3d"
+ "\xd9\xb2\x0a\x81\x5f\x2f\x0d\x75\x38\x94\x37\x99\x1e\xf6\x07\x76\xe0\xee"
+ "\xbe",
+ 91 },
+ { "Symantec log",
+ "https://ct.ws.symantec.com/",
+ "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+ "\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x96\xea\xac\x1c\x46\x0c\x1b\x55\xdc"
+ "\x0d\xfc\xb5\x94\x27\x46\x57\x42\x70\x3a\x69\x18\xe2\xbf\x3b\xc4\xdb\xab"
+ "\xa0\xf4\xb6\x6c\xc0\x53\x3f\x4d\x42\x10\x33\xf0\x58\x97\x8f\x6b\xbe\x72"
+ "\xf4\x2a\xec\x1c\x42\xaa\x03\x2f\x1a\x7e\x28\x35\x76\x99\x08\x3d\x21\x14"
+ "\x86",
+ 91 },
+ { "Venafi log",
+ "https://ctlog.api.venafi.com/",
+ "\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05"
+ "\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xa2\x5a\x48"
+ "\x1f\x17\x52\x95\x35\xcb\xa3\x5b\x3a\x1f\x53\x82\x76\x94\xa3\xff\x80\xf2"
+ "\x1c\x37\x3c\xc0\xb1\xbd\xc1\x59\x8b\xab\x2d\x65\x93\xd7\xf3\xe0\x04\xd5"
+ "\x9a\x6f\xbf\xd6\x23\x76\x36\x4f\x23\x99\xcb\x54\x28\xad\x8c\x15\x4b\x65"
+ "\x59\x76\x41\x4a\x9c\xa6\xf7\xb3\x3b\x7e\xb1\xa5\x49\xa4\x17\x51\x6c\x80"
+ "\xdc\x2a\x90\x50\x4b\x88\x24\xe9\xa5\x12\x32\x93\x04\x48\x90\x02\xfa\x5f"
+ "\x0e\x30\x87\x8e\x55\x76\x05\xee\x2a\x4c\xce\xa3\x6a\x69\x09\x6e\x25\xad"
+ "\x82\x76\x0f\x84\x92\xfa\x38\xd6\x86\x4e\x24\x8f\x9b\xb0\x72\xcb\x9e\xe2"
+ "\x6b\x3f\xe1\x6d\xc9\x25\x75\x23\x88\xa1\x18\x58\x06\x23\x33\x78\xda\x00"
+ "\xd0\x38\x91\x67\xd2\xa6\x7d\x27\x97\x67\x5a\xc1\xf3\x2f\x17\xe6\xea\xd2"
+ "\x5b\xe8\x81\xcd\xfd\x92\x68\xe7\xf3\x06\xf0\xe9\x72\x84\xee\x01\xa5\xb1"
+ "\xd8\x33\xda\xce\x83\xa5\xdb\xc7\xcf\xd6\x16\x7e\x90\x75\x18\xbf\x16\xdc"
+ "\x32\x3b\x6d\x8d\xab\x82\x17\x1f\x89\x20\x8d\x1d\x9a\xe6\x4d\x23\x08\xdf"
+ "\x78\x6f\xc6\x05\xbf\x5f\xae\x94\x97\xdb\x5f\x64\xd4\xee\x16\x8b\xa3\x84"
+ "\x6c\x71\x2b\xf1\xab\x7f\x5d\x0d\x32\xee\x04\xe2\x90\xec\x41\x9f\xfb\x39"
+ "\xc1\x02\x03\x01\x00\x01",
+ 294 },
+ { "Symantec 'Vega' log",
+ "https://vega.ws.symantec.com/",
+ "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+ "\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xea\x95\x9e\x02\xff\xee\xf1\x33\x6d"
+ "\x4b\x87\xbc\xcd\xfd\x19\x17\x62\xff\x94\xd3\xd0\x59\x07\x3f\x02\x2d\x1c"
+ "\x90\xfe\xc8\x47\x30\x3b\xf1\xdd\x0d\xb8\x11\x0c\x5d\x1d\x86\xdd\xab\xd3"
+ "\x2b\x46\x66\xfb\x6e\x65\xb7\x3b\xfd\x59\x68\xac\xdf\xa6\xf8\xce\xd2\x18"
+ "\x4d",
+ 91 },
+ { "CNNIC CT log",
+ "https://ctserver.cnnic.cn/",
+ "\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05"
+ "\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xbf\xb5\x08"
+ "\x61\x9a\x29\x32\x04\xd3\x25\x63\xe9\xd8\x85\xe1\x86\xe0\x1f\xd6\x5e\x9a"
+ "\xf7\x33\x3b\x80\x1b\xe7\xb6\x3e\x5f\x2d\xa1\x66\xf6\x95\x4a\x84\xa6\x21"
+ "\x56\x79\xe8\xf7\x85\xee\x5d\xe3\x7c\x12\xc0\xe0\x89\x22\x09\x22\x3e\xba"
+ "\x16\x95\x06\xbd\xa8\xb9\xb1\xa9\xb2\x7a\xd6\x61\x2e\x87\x11\xb9\x78\x40"
+ "\x89\x75\xdb\x0c\xdc\x90\xe0\xa4\x79\xd6\xd5\x5e\x6e\xd1\x2a\xdb\x34\xf4"
+ "\x99\x3f\x65\x89\x3b\x46\xc2\x29\x2c\x15\x07\x1c\xc9\x4b\x1a\x54\xf8\x6c"
+ "\x1e\xaf\x60\x27\x62\x0a\x65\xd5\x9a\xb9\x50\x36\x16\x6e\x71\xf6\x1f\x01"
+ "\xf7\x12\xa7\xfc\xbf\xf6\x21\xa3\x29\x90\x86\x2d\x77\xde\xbb\x4c\xd4\xcf"
+ "\xfd\xd2\xcf\x82\x2c\x4d\xd4\xf2\xc2\x2d\xac\xa9\xbe\xea\xc3\x19\x25\x43"
+ "\xb2\xe5\x9a\x6c\x0d\xc5\x1c\xa5\x8b\xf7\x3f\x30\xaf\xb9\x01\x91\xb7\x69"
+ "\x12\x12\xe5\x83\x61\xfe\x34\x00\xbe\xf6\x71\x8a\xc7\xeb\x50\x92\xe8\x59"
+ "\xfe\x15\x91\xeb\x96\x97\xf8\x23\x54\x3f\x2d\x8e\x07\xdf\xee\xda\xb3\x4f"
+ "\xc8\x3c\x9d\x6f\xdf\x3c\x2c\x43\x57\xa1\x47\x0c\x91\x04\xf4\x75\x4d\xda"
+ "\x89\x81\xa4\x14\x06\x34\xb9\x98\xc3\xda\xf1\xfd\xed\x33\x36\xd3\x16\x2d"
+ "\x35\x02\x03\x01\x00\x01",
+ 294 },
+ { "WoSign log",
+ "https://ctlog.wosign.com/",
+ "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+ "\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xcc\x11\x88\x7b\x2d\x66\xcb\xae\x8f"
+ "\x4d\x30\x66\x27\x19\x25\x22\x93\x21\x46\xb4\x2f\x01\xd3\xc6\xf9\x2b\xd5"
+ "\xc8\xba\x73\x9b\x06\xa2\xf0\x8a\x02\x9c\xd0\x6b\x46\x18\x30\x85\xba\xe9"
+ "\x24\x8b\x0e\xd1\x5b\x70\x28\x0c\x7e\xf1\x3a\x45\x7f\x5a\xf3\x82\x42\x60"
+ "\x31",
+ 91 },
+ { "StartCom log",
+ "https://ct.startssl.com/",
+ "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+ "\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x48\xf3\x59\xf3\xf6\x05\x18\xd3\xdb"
+ "\xb2\xed\x46\x7e\xcf\xc8\x11\xb5\x57\xb1\xa8\xd6\x4c\xe6\x9f\xb7\x4a\x1a"
+ "\x14\x86\x43\xa9\x48\xb0\xcb\x5a\x3f\x3c\x4a\xca\xdf\xc4\x82\x14\x55\x9a"
+ "\xf8\xf7\x8e\x40\x55\xdc\xf4\xd2\xaf\xea\x75\x74\xfb\x4e\x7f\x60\x86\x2e"
+ "\x51",
+ 91 }
+};
+
+#endif // CTKnownLogs_h
diff --git a/security/certverifier/CTLogVerifier.cpp b/security/certverifier/CTLogVerifier.cpp
new file mode 100644
index 000000000..53e83c39c
--- /dev/null
+++ b/security/certverifier/CTLogVerifier.cpp
@@ -0,0 +1,283 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CTLogVerifier.h"
+
+#include "CTSerialization.h"
+#include "hasht.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "pkix/pkixnss.h"
+#include "pkixutil.h"
+
+namespace mozilla { namespace ct {
+
+using namespace mozilla::pkix;
+
+// A TrustDomain used to extract the SCT log signature parameters
+// given its subjectPublicKeyInfo.
+// Only RSASSA-PKCS1v15 with SHA-256 and ECDSA (using the NIST P-256 curve)
+// with SHA-256 are allowed.
+// RSA keys must be at least 2048 bits.
+// See See RFC 6962, Section 2.1.4.
+class SignatureParamsTrustDomain final : public TrustDomain
+{
+public:
+ SignatureParamsTrustDomain()
+ : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous)
+ {
+ }
+
+ Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
+ TrustLevel&) override
+ {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result FindIssuer(Input, IssuerChecker&, Time) override
+ {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
+ const Input*, const Input*) override
+ {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result IsChainValid(const DERArray&, Time) override
+ {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result DigestBuf(Input, DigestAlgorithm, uint8_t*, size_t) override
+ {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
+ Time) override
+ {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve curve) override
+ {
+ MOZ_ASSERT(mSignatureAlgorithm ==
+ DigitallySigned::SignatureAlgorithm::Anonymous);
+ if (curve != NamedCurve::secp256r1) {
+ return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
+ }
+ mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::ECDSA;
+ return Success;
+ }
+
+ Result VerifyECDSASignedDigest(const SignedDigest&, Input) override
+ {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA,
+ unsigned int modulusSizeInBits)
+ override
+ {
+ MOZ_ASSERT(mSignatureAlgorithm ==
+ DigitallySigned::SignatureAlgorithm::Anonymous);
+ // Require RSA keys of at least 2048 bits. See RFC 6962, Section 2.1.4.
+ if (modulusSizeInBits < 2048) {
+ return Result::ERROR_INADEQUATE_KEY_SIZE;
+ }
+ mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::RSA;
+ return Success;
+ }
+
+ Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override
+ {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
+ KeyPurposeId) override
+ {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result NetscapeStepUpMatchesServerAuth(Time, bool&) override
+ {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override
+ {
+ }
+
+ DigitallySigned::SignatureAlgorithm mSignatureAlgorithm;
+};
+
+
+CTLogVerifier::CTLogVerifier()
+ : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous)
+{
+}
+
+Result
+CTLogVerifier::Init(Input subjectPublicKeyInfo)
+{
+ SignatureParamsTrustDomain trustDomain;
+ Result rv = CheckSubjectPublicKeyInfo(subjectPublicKeyInfo, trustDomain,
+ EndEntityOrCA::MustBeEndEntity);
+ if (rv != Success) {
+ return rv;
+ }
+ mSignatureAlgorithm = trustDomain.mSignatureAlgorithm;
+
+ rv = InputToBuffer(subjectPublicKeyInfo, mSubjectPublicKeyInfo);
+ if (rv != Success) {
+ return rv;
+ }
+
+ if (!mKeyId.resizeUninitialized(SHA256_LENGTH)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ rv = DigestBufNSS(subjectPublicKeyInfo, DigestAlgorithm::sha256,
+ mKeyId.begin(), mKeyId.length());
+ if (rv != Success) {
+ return rv;
+ }
+
+ return Success;
+}
+
+Result
+CTLogVerifier::Verify(const LogEntry& entry,
+ const SignedCertificateTimestamp& sct)
+{
+ if (mKeyId.empty() || sct.logId != mKeyId) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ if (!SignatureParametersMatch(sct.signature)) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ Buffer serializedLogEntry;
+ Result rv = EncodeLogEntry(entry, serializedLogEntry);
+ if (rv != Success) {
+ return rv;
+ }
+
+ Input logEntryInput;
+ rv = BufferToInput(serializedLogEntry, logEntryInput);
+ if (rv != Success) {
+ return rv;
+ }
+ Input sctExtensionsInput;
+ rv = BufferToInput(sct.extensions, sctExtensionsInput);
+ if (rv != Success) {
+ return rv;
+ }
+
+ Buffer serializedData;
+ rv = EncodeV1SCTSignedData(sct.timestamp, logEntryInput, sctExtensionsInput,
+ serializedData);
+ if (rv != Success) {
+ return rv;
+ }
+ return VerifySignature(serializedData, sct.signature.signatureData);
+}
+
+Result
+CTLogVerifier::VerifySignedTreeHead(const SignedTreeHead& sth)
+{
+ if (!SignatureParametersMatch(sth.signature)) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ Buffer serializedData;
+ Result rv = EncodeTreeHeadSignature(sth, serializedData);
+ if (rv != Success) {
+ return rv;
+ }
+ return VerifySignature(serializedData, sth.signature.signatureData);
+}
+
+bool
+CTLogVerifier::SignatureParametersMatch(const DigitallySigned& signature)
+{
+ return signature.SignatureParametersMatch(
+ DigitallySigned::HashAlgorithm::SHA256, mSignatureAlgorithm);
+}
+
+Result
+CTLogVerifier::VerifySignature(Input data, Input signature)
+{
+ uint8_t digest[SHA256_LENGTH];
+ Result rv = DigestBufNSS(data, DigestAlgorithm::sha256, digest,
+ ArrayLength(digest));
+ if (rv != Success) {
+ return rv;
+ }
+
+ SignedDigest signedDigest;
+ signedDigest.digestAlgorithm = DigestAlgorithm::sha256;
+ rv = signedDigest.digest.Init(digest, ArrayLength(digest));
+ if (rv != Success) {
+ return rv;
+ }
+ rv = signedDigest.signature.Init(signature);
+ if (rv != Success) {
+ return rv;
+ }
+
+ Input spki;
+ rv = BufferToInput(mSubjectPublicKeyInfo, spki);
+ if (rv != Success) {
+ return rv;
+ }
+
+ switch (mSignatureAlgorithm) {
+ case DigitallySigned::SignatureAlgorithm::RSA:
+ rv = VerifyRSAPKCS1SignedDigestNSS(signedDigest, spki, nullptr);
+ break;
+ case DigitallySigned::SignatureAlgorithm::ECDSA:
+ rv = VerifyECDSASignedDigestNSS(signedDigest, spki, nullptr);
+ break;
+ // We do not expect new values added to this enum any time soon,
+ // so just listing all the available ones seems to be the easiest way
+ // to suppress warning C4061 on MSVC (which expects all values of the
+ // enum to be explicitly handled).
+ case DigitallySigned::SignatureAlgorithm::Anonymous:
+ case DigitallySigned::SignatureAlgorithm::DSA:
+ default:
+ MOZ_ASSERT_UNREACHABLE("RSA/ECDSA expected");
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ if (rv != Success) {
+ if (IsFatalError(rv)) {
+ return rv;
+ }
+ // If the error is non-fatal, we assume the signature was invalid.
+ return Result::ERROR_BAD_SIGNATURE;
+ }
+ return Success;
+}
+
+Result
+CTLogVerifier::VerifySignature(const Buffer& data, const Buffer& signature)
+{
+ Input dataInput;
+ Result rv = BufferToInput(data, dataInput);
+ if (rv != Success) {
+ return rv;
+ }
+ Input signatureInput;
+ rv = BufferToInput(signature, signatureInput);
+ if (rv != Success) {
+ return rv;
+ }
+ return VerifySignature(dataInput, signatureInput);
+}
+
+} } // namespace mozilla::ct
diff --git a/security/certverifier/CTLogVerifier.h b/security/certverifier/CTLogVerifier.h
new file mode 100644
index 000000000..6edd554ca
--- /dev/null
+++ b/security/certverifier/CTLogVerifier.h
@@ -0,0 +1,68 @@
+/* -*- 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 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/. */
+
+#ifndef CTLogVerifier_h
+#define CTLogVerifier_h
+
+#include "pkix/Input.h"
+#include "pkix/pkix.h"
+#include "SignedCertificateTimestamp.h"
+#include "SignedTreeHead.h"
+
+namespace mozilla { namespace ct {
+
+// Verifies Signed Certificate Timestamps (SCTs) provided by a specific log
+// using the public key of that log. Assumes the SCT being verified
+// matches the log by log key ID and signature parameters (an error is returned
+// otherwise).
+// The verification functions return Success if the provided SCT has passed
+// verification, ERROR_BAD_SIGNATURE if failed verification, or other result
+// on error.
+class CTLogVerifier
+{
+public:
+ CTLogVerifier();
+
+ // Initializes the verifier with log-specific information.
+ // |subjectPublicKeyInfo| is a DER-encoded SubjectPublicKeyInfo.
+ // An error is returned if |subjectPublicKeyInfo| refers to an unsupported
+ // public key.
+ pkix::Result Init(pkix::Input subjectPublicKeyInfo);
+
+ // Returns the log's key ID, which is a SHA256 hash of its public key.
+ // See RFC 6962, Section 3.2.
+ const Buffer& keyId() const { return mKeyId; }
+
+ // Verifies that |sct| contains a valid signature for |entry|.
+ // |sct| must be signed by the verifier's log.
+ pkix::Result Verify(const LogEntry& entry,
+ const SignedCertificateTimestamp& sct);
+
+ // Verifies the signature in |sth|.
+ // |sth| must be signed by the verifier's log.
+ pkix::Result VerifySignedTreeHead(const SignedTreeHead& sth);
+
+ // Returns true if the signature and hash algorithms in |signature|
+ // match those of the log.
+ bool SignatureParametersMatch(const DigitallySigned& signature);
+
+private:
+ // Performs the underlying verification using the log's public key. Note
+ // that |signature| contains the raw signature data (i.e. without any
+ // DigitallySigned struct encoding).
+ // Returns Success if passed verification, ERROR_BAD_SIGNATURE if failed
+ // verification, or other result on error.
+ pkix::Result VerifySignature(pkix::Input data, pkix::Input signature);
+ pkix::Result VerifySignature(const Buffer& data, const Buffer& signature);
+
+ Buffer mSubjectPublicKeyInfo;
+ Buffer mKeyId;
+ DigitallySigned::SignatureAlgorithm mSignatureAlgorithm;
+};
+
+} } // namespace mozilla::ct
+
+#endif // CTLogVerifier_h
diff --git a/security/certverifier/CTObjectsExtractor.cpp b/security/certverifier/CTObjectsExtractor.cpp
new file mode 100644
index 000000000..33354f97b
--- /dev/null
+++ b/security/certverifier/CTObjectsExtractor.cpp
@@ -0,0 +1,396 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CTObjectsExtractor.h"
+
+#include "hasht.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Casting.h"
+#include "mozilla/Move.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/RangedPtr.h"
+#include "mozilla/Vector.h"
+#include "pkix/pkixnss.h"
+#include "pkixutil.h"
+
+namespace mozilla { namespace ct {
+
+using namespace mozilla::pkix;
+
+// Holds a non-owning pointer to a byte buffer and allows writing chunks of data
+// to the buffer, placing the later chunks after the earlier ones
+// in a stream-like fashion.
+// Note that writing to Output always succeeds. If the internal buffer
+// overflows, an error flag is turned on and you won't be able to retrieve
+// the final data.
+class Output
+{
+public:
+ Output(uint8_t* buffer, size_t length)
+ : begin(buffer)
+ , end(buffer + length)
+ , current(buffer, begin, end)
+ , overflowed(false)
+ {
+ }
+
+ template <size_t N>
+ explicit Output(uint8_t (&buffer)[N])
+ : Output(buffer, N)
+ {
+ }
+
+ void Write(Input data)
+ {
+ Write(data.UnsafeGetData(), data.GetLength());
+ }
+
+ void Write(uint8_t b)
+ {
+ Write(&b, 1);
+ }
+
+ bool IsOverflowed() const { return overflowed; }
+
+ Result GetInput(/*out*/ Input& input) const
+ {
+ if (overflowed) {
+ return Result::FATAL_ERROR_INVALID_STATE;
+ }
+ size_t length = AssertedCast<size_t>(current.get() - begin);
+ return input.Init(begin, length);
+ }
+
+private:
+ uint8_t* begin;
+ uint8_t* end;
+ RangedPtr<uint8_t> current;
+ bool overflowed;
+
+ Output(const Output&) = delete;
+ void operator=(const Output&) = delete;
+
+ void Write(const uint8_t* data, size_t length)
+ {
+ size_t available = AssertedCast<size_t>(end - current.get());
+ if (available < length) {
+ overflowed = true;
+ }
+ if (overflowed) {
+ return;
+ }
+ PodCopy(current.get(), data, length);
+ current += length;
+ }
+};
+
+// For reference:
+//
+// Certificate ::= SEQUENCE {
+// tbsCertificate TBSCertificate,
+// signatureAlgorithm AlgorithmIdentifier,
+// signatureValue BIT STRING }
+//
+// TBSCertificate ::= SEQUENCE {
+// version [0] EXPLICIT Version DEFAULT v1,
+// serialNumber CertificateSerialNumber,
+// signature AlgorithmIdentifier,
+// issuer Name,
+// validity Validity,
+// subject Name,
+// subjectPublicKeyInfo SubjectPublicKeyInfo,
+// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
+// -- If present, version MUST be v2 or v3
+// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
+// -- If present, version MUST be v2 or v3
+// extensions [3] EXPLICIT Extensions OPTIONAL
+// -- If present, version MUST be v3
+// }
+
+// python DottedOIDToCode.py id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
+// See Section 3.3 of RFC 6962.
+static const uint8_t EMBEDDED_SCT_LIST_OID[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
+};
+// Maximum length of DER TLV header
+static const size_t MAX_TLV_HEADER_LENGTH = 4;
+// DER tag of the "extensions [3]" field from TBSCertificate
+static const uint8_t EXTENSIONS_CONTEXT_TAG =
+ der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 3;
+
+// Given a leaf certificate, extracts the DER-encoded TBSCertificate component
+// of the corresponding Precertificate.
+// Basically, the extractor needs to remove the embedded SCTs extension
+// from the certificate and return its TBSCertificate. We do it in an ad hoc
+// manner by breaking the source DER into several parts and then joining
+// the right parts, taking care to update the relevant TLV headers.
+// See WriteOutput for more details on the parts involved.
+class PrecertTBSExtractor
+{
+public:
+ // |buffer| is the buffer to be used for writing the output. Since the
+ // required buffer size is not generally known in advance, it's best
+ // to use at least the size of the input certificate DER.
+ PrecertTBSExtractor(Input der, uint8_t* buffer, size_t bufferLength)
+ : mDER(der)
+ , mOutput(buffer, bufferLength)
+ {
+ }
+
+ // Performs the extraction.
+ Result Init()
+ {
+ Reader tbsReader;
+ Result rv = GetTBSCertificate(tbsReader);
+ if (rv != Success) {
+ return rv;
+ }
+
+ rv = ExtractTLVsBeforeExtensions(tbsReader);
+ if (rv != Success) {
+ return rv;
+ }
+
+ rv = ExtractOptionalExtensionsExceptSCTs(tbsReader);
+ if (rv != Success) {
+ return rv;
+ }
+
+ return WriteOutput();
+ }
+
+ // Use to retrieve the result after a successful call to Init.
+ // The returned Input points to the buffer supplied in the constructor.
+ Input GetPrecertTBS()
+ {
+ return mPrecertTBS;
+ }
+
+private:
+ Result GetTBSCertificate(Reader& tbsReader)
+ {
+ Reader certificateReader;
+ Result rv = der::ExpectTagAndGetValueAtEnd(mDER, der::SEQUENCE,
+ certificateReader);
+ if (rv != Success) {
+ return rv;
+ }
+ return ExpectTagAndGetValue(certificateReader, der::SEQUENCE, tbsReader);
+ }
+
+ Result ExtractTLVsBeforeExtensions(Reader& tbsReader)
+ {
+ Reader::Mark tbsBegin = tbsReader.GetMark();
+ while (!tbsReader.AtEnd()) {
+ if (tbsReader.Peek(EXTENSIONS_CONTEXT_TAG)) {
+ break;
+ }
+ uint8_t tag;
+ Input tagValue;
+ Result rv = der::ReadTagAndGetValue(tbsReader, tag, tagValue);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+ return tbsReader.GetInput(tbsBegin, mTLVsBeforeExtensions);
+ }
+
+ Result ExtractOptionalExtensionsExceptSCTs(Reader& tbsReader)
+ {
+ if (!tbsReader.Peek(EXTENSIONS_CONTEXT_TAG)) {
+ return Success;
+ }
+
+ Reader extensionsContextReader;
+ Result rv = der::ExpectTagAndGetValueAtEnd(tbsReader,
+ EXTENSIONS_CONTEXT_TAG,
+ extensionsContextReader);
+ if (rv != Success) {
+ return rv;
+ }
+
+ Reader extensionsReader;
+ rv = der::ExpectTagAndGetValueAtEnd(extensionsContextReader, der::SEQUENCE,
+ extensionsReader);
+ if (rv != Success) {
+ return rv;
+ }
+
+ while (!extensionsReader.AtEnd()) {
+ Reader::Mark extensionTLVBegin = extensionsReader.GetMark();
+ Reader extension;
+ rv = der::ExpectTagAndGetValue(extensionsReader, der::SEQUENCE,
+ extension);
+ if (rv != Success) {
+ return rv;
+ }
+ Reader extensionID;
+ rv = der::ExpectTagAndGetValue(extension, der::OIDTag, extensionID);
+ if (rv != Success) {
+ return rv;
+ }
+ if (!extensionID.MatchRest(EMBEDDED_SCT_LIST_OID)) {
+ Input extensionTLV;
+ rv = extensionsReader.GetInput(extensionTLVBegin, extensionTLV);
+ if (rv != Success) {
+ return rv;
+ }
+ if (!mExtensionTLVs.append(Move(extensionTLV))) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ }
+ }
+ return Success;
+ }
+
+ Result WriteOutput()
+ {
+ // What should be written here:
+ //
+ // TBSCertificate ::= SEQUENCE (TLV with header |tbsHeader|)
+ // dump of |mTLVsBeforeExtensions|
+ // extensions [3] OPTIONAL (TLV with header |extensionsContextHeader|)
+ // SEQUENCE (TLV with with header |extensionsHeader|)
+ // dump of |mExtensionTLVs|
+
+ Result rv;
+ if (mExtensionTLVs.length() > 0) {
+ uint8_t tbsHeaderBuffer[MAX_TLV_HEADER_LENGTH];
+ uint8_t extensionsContextHeaderBuffer[MAX_TLV_HEADER_LENGTH];
+ uint8_t extensionsHeaderBuffer[MAX_TLV_HEADER_LENGTH];
+
+ Input tbsHeader;
+ Input extensionsContextHeader;
+ Input extensionsHeader;
+
+ // Count the total size of the extensions. Note that since
+ // the extensions data is contained within mDER (an Input),
+ // their combined length won't overflow Input::size_type.
+ Input::size_type extensionsValueLength = 0;
+ for (auto& extensionTLV : mExtensionTLVs) {
+ extensionsValueLength += extensionTLV.GetLength();
+ }
+
+ rv = MakeTLVHeader(der::SEQUENCE, extensionsValueLength,
+ extensionsHeaderBuffer, extensionsHeader);
+ if (rv != Success) {
+ return rv;
+ }
+ Input::size_type extensionsContextLength =
+ AssertedCast<Input::size_type>(extensionsHeader.GetLength() +
+ extensionsValueLength);
+ rv = MakeTLVHeader(EXTENSIONS_CONTEXT_TAG,
+ extensionsContextLength,
+ extensionsContextHeaderBuffer,
+ extensionsContextHeader);
+ if (rv != Success) {
+ return rv;
+ }
+ Input::size_type tbsLength =
+ AssertedCast<Input::size_type>(mTLVsBeforeExtensions.GetLength() +
+ extensionsContextHeader.GetLength() +
+ extensionsHeader.GetLength() +
+ extensionsValueLength);
+ rv = MakeTLVHeader(der::SEQUENCE, tbsLength, tbsHeaderBuffer, tbsHeader);
+ if (rv != Success) {
+ return rv;
+ }
+
+ mOutput.Write(tbsHeader);
+ mOutput.Write(mTLVsBeforeExtensions);
+ mOutput.Write(extensionsContextHeader);
+ mOutput.Write(extensionsHeader);
+ for (auto& extensionTLV : mExtensionTLVs) {
+ mOutput.Write(extensionTLV);
+ }
+ } else {
+ uint8_t tbsHeaderBuffer[MAX_TLV_HEADER_LENGTH];
+ Input tbsHeader;
+ rv = MakeTLVHeader(der::SEQUENCE, mTLVsBeforeExtensions.GetLength(),
+ tbsHeaderBuffer, tbsHeader);
+ if (rv != Success) {
+ return rv;
+ }
+ mOutput.Write(tbsHeader);
+ mOutput.Write(mTLVsBeforeExtensions);
+ }
+
+ return mOutput.GetInput(mPrecertTBS);
+ }
+
+ Result MakeTLVHeader(uint8_t tag, size_t length,
+ uint8_t (&buffer)[MAX_TLV_HEADER_LENGTH],
+ /*out*/ Input& header)
+ {
+ Output output(buffer);
+ output.Write(tag);
+ if (length < 128) {
+ output.Write(AssertedCast<uint8_t>(length));
+ } else if (length < 256) {
+ output.Write(0x81u);
+ output.Write(AssertedCast<uint8_t>(length));
+ } else if (length < 65536) {
+ output.Write(0x82u);
+ output.Write(AssertedCast<uint8_t>(length / 256));
+ output.Write(AssertedCast<uint8_t>(length % 256));
+ } else {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ return output.GetInput(header);
+ }
+
+ Input mDER;
+ Input mTLVsBeforeExtensions;
+ Vector<Input, 16> mExtensionTLVs;
+ Output mOutput;
+ Input mPrecertTBS;
+};
+
+Result
+GetPrecertLogEntry(Input leafCertificate, Input issuerSubjectPublicKeyInfo,
+ LogEntry& output)
+{
+ MOZ_ASSERT(leafCertificate.GetLength() > 0);
+ MOZ_ASSERT(issuerSubjectPublicKeyInfo.GetLength() > 0);
+ output.Reset();
+
+ Buffer precertTBSBuffer;
+ if (!precertTBSBuffer.resize(leafCertificate.GetLength())) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+
+ PrecertTBSExtractor extractor(leafCertificate,
+ precertTBSBuffer.begin(),
+ precertTBSBuffer.length());
+ Result rv = extractor.Init();
+ if (rv != Success) {
+ return rv;
+ }
+ Input precertTBS(extractor.GetPrecertTBS());
+ MOZ_ASSERT(precertTBS.UnsafeGetData() == precertTBSBuffer.begin());
+ precertTBSBuffer.shrinkTo(precertTBS.GetLength());
+
+ output.type = LogEntry::Type::Precert;
+ output.tbsCertificate = Move(precertTBSBuffer);
+
+ if (!output.issuerKeyHash.resizeUninitialized(SHA256_LENGTH)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ return DigestBufNSS(issuerSubjectPublicKeyInfo, DigestAlgorithm::sha256,
+ output.issuerKeyHash.begin(),
+ output.issuerKeyHash.length());
+}
+
+Result
+GetX509LogEntry(Input leafCertificate, LogEntry& output)
+{
+ MOZ_ASSERT(leafCertificate.GetLength() > 0);
+ output.Reset();
+ output.type = LogEntry::Type::X509;
+ return InputToBuffer(leafCertificate, output.leafCertificate);
+}
+
+} } // namespace mozilla::ct
diff --git a/security/certverifier/CTObjectsExtractor.h b/security/certverifier/CTObjectsExtractor.h
new file mode 100644
index 000000000..207cd554a
--- /dev/null
+++ b/security/certverifier/CTObjectsExtractor.h
@@ -0,0 +1,43 @@
+/* -*- 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 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/. */
+
+#ifndef CTObjectsExtractor_h
+#define CTObjectsExtractor_h
+
+#include "pkix/Input.h"
+#include "pkix/Result.h"
+#include "SignedCertificateTimestamp.h"
+
+namespace mozilla { namespace ct {
+
+// Obtains a PrecertChain log entry for |leafCertificate|, a DER-encoded
+// X.509v3 certificate that contains an X.509v3 extension with the
+// OID 1.3.6.1.4.1.11129.2.4.2.
+// |issuerSubjectPublicKeyInfo| is a DER-encoded SPKI of |leafCertificate|'s
+// issuer.
+// On success, fills |output| with the data for a PrecertChain log entry.
+// If |leafCertificate| does not contain the required extension,
+// an error is returned.
+// The returned |output| is intended to be verified by CTLogVerifier::Verify.
+// Note that |leafCertificate| is not checked for validity or well-formedness.
+// You might want to validate it first using pkix::BuildCertChain or similar.
+pkix::Result GetPrecertLogEntry(pkix::Input leafCertificate,
+ pkix::Input issuerSubjectPublicKeyInfo,
+ LogEntry& output);
+
+// Obtains an X509Chain log entry for |leafCertificate|, a DER-encoded
+// X.509v3 certificate that is not expected to contain an X.509v3 extension
+// with the OID 1.3.6.1.4.1.11129.2.4.2 (meaning a certificate without
+// an embedded SCT).
+// On success, fills |output| with the data for an X509Chain log entry.
+// The returned |output| is intended to be verified by CTLogVerifier::Verify.
+// Note that |leafCertificate| is not checked for validity or well-formedness.
+// You might want to validate it first using pkix::BuildCertChain or similar.
+pkix::Result GetX509LogEntry(pkix::Input leafCertificate, LogEntry& output);
+
+} } // namespace mozilla::ct
+
+#endif // CTObjectsExtractor_h
diff --git a/security/certverifier/CTSerialization.cpp b/security/certverifier/CTSerialization.cpp
new file mode 100644
index 000000000..c15d0303e
--- /dev/null
+++ b/security/certverifier/CTSerialization.cpp
@@ -0,0 +1,554 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CTSerialization.h"
+
+#include <stdint.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
+
+namespace mozilla { namespace ct {
+
+using namespace mozilla::pkix;
+
+typedef mozilla::pkix::Result Result;
+
+// Note: length is always specified in bytes.
+// Signed Certificate Timestamp (SCT) Version length
+static const size_t kVersionLength = 1;
+
+// Members of a V1 SCT
+static const size_t kLogIdLength = 32;
+static const size_t kTimestampLength = 8;
+static const size_t kExtensionsLengthBytes = 2;
+static const size_t kHashAlgorithmLength = 1;
+static const size_t kSigAlgorithmLength = 1;
+static const size_t kSignatureLengthBytes = 2;
+
+// Members of the digitally-signed struct of a V1 SCT
+static const size_t kSignatureTypeLength = 1;
+static const size_t kLogEntryTypeLength = 2;
+static const size_t kAsn1CertificateLengthBytes = 3;
+static const size_t kTbsCertificateLengthBytes = 3;
+
+static const size_t kSCTListLengthBytes = 2;
+static const size_t kSerializedSCTLengthBytes = 2;
+
+// Members of digitally-signed struct of a STH
+static const size_t kTreeSizeLength = 8;
+
+// Length of sha256RootHash buffer of SignedTreeHead
+static const size_t kSthRootHashLength = 32;
+
+enum class SignatureType {
+ CertificateTimestamp = 0,
+ TreeHash = 1,
+};
+
+// Reads a TLS-encoded variable length unsigned integer from |in|.
+// The integer is expected to be in big-endian order, which is used by TLS.
+// Note: does not check if the output parameter overflows while reading.
+// |length| indicates the size (in bytes) of the serialized integer.
+static Result
+UncheckedReadUint(size_t length, Reader& in, uint64_t& out)
+{
+ uint64_t result = 0;
+ for (size_t i = 0; i < length; ++i) {
+ uint8_t value;
+ Result rv = in.Read(value);
+ if (rv != Success) {
+ return rv;
+ }
+ result = (result << 8) | value;
+ }
+ out = result;
+ return Success;
+}
+
+// Performs overflow sanity checks and calls UncheckedReadUint.
+template <size_t length, typename T>
+static inline Result
+ReadUint(Reader& in, T& out)
+{
+ uint64_t value;
+ static_assert(mozilla::IsUnsigned<T>::value, "T must be unsigned");
+ static_assert(length <= 8, "At most 8 byte integers can be read");
+ static_assert(sizeof(T) >= length, "T must be able to hold <length> bytes");
+ Result rv = UncheckedReadUint(length, in, value);
+ if (rv != Success) {
+ return rv;
+ }
+ out = static_cast<T>(value);
+ return Success;
+}
+
+// Reads |length| bytes from |in|.
+static Result
+ReadFixedBytes(size_t length, Reader& in, Input& out)
+{
+ return in.Skip(length, out);
+}
+
+// Reads a length-prefixed variable amount of bytes from |in|, updating |out|
+// on success. |prefixLength| indicates the number of bytes needed to represent
+// the length.
+template <size_t prefixLength>
+static inline Result
+ReadVariableBytes(Reader& in, Input& out)
+{
+ size_t length;
+ Result rv = ReadUint<prefixLength>(in, length);
+ if (rv != Success) {
+ return rv;
+ }
+ return ReadFixedBytes(length, in, out);
+}
+
+// Reads a serialized hash algorithm.
+static Result
+ReadHashAlgorithm(Reader& in, DigitallySigned::HashAlgorithm& out)
+{
+ unsigned int value;
+ Result rv = ReadUint<kHashAlgorithmLength>(in, value);
+ if (rv != Success) {
+ return rv;
+ }
+ DigitallySigned::HashAlgorithm algo =
+ static_cast<DigitallySigned::HashAlgorithm>(value);
+ switch (algo) {
+ case DigitallySigned::HashAlgorithm::None:
+ case DigitallySigned::HashAlgorithm::MD5:
+ case DigitallySigned::HashAlgorithm::SHA1:
+ case DigitallySigned::HashAlgorithm::SHA224:
+ case DigitallySigned::HashAlgorithm::SHA256:
+ case DigitallySigned::HashAlgorithm::SHA384:
+ case DigitallySigned::HashAlgorithm::SHA512:
+ out = algo;
+ return Success;
+ }
+ return Result::ERROR_BAD_DER;
+}
+
+// Reads a serialized signature algorithm.
+static Result
+ReadSignatureAlgorithm(Reader& in, DigitallySigned::SignatureAlgorithm& out)
+{
+ unsigned int value;
+ Result rv = ReadUint<kSigAlgorithmLength>(in, value);
+ if (rv != Success) {
+ return rv;
+ }
+ DigitallySigned::SignatureAlgorithm algo =
+ static_cast<DigitallySigned::SignatureAlgorithm>(value);
+ switch (algo) {
+ case DigitallySigned::SignatureAlgorithm::Anonymous:
+ case DigitallySigned::SignatureAlgorithm::RSA:
+ case DigitallySigned::SignatureAlgorithm::DSA:
+ case DigitallySigned::SignatureAlgorithm::ECDSA:
+ out = algo;
+ return Success;
+ }
+ return Result::ERROR_BAD_DER;
+}
+
+// Reads a serialized version enum.
+static Result
+ReadVersion(Reader& in, SignedCertificateTimestamp::Version& out)
+{
+ unsigned int value;
+ Result rv = ReadUint<kVersionLength>(in, value);
+ if (rv != Success) {
+ return rv;
+ }
+ SignedCertificateTimestamp::Version version =
+ static_cast<SignedCertificateTimestamp::Version>(value);
+ switch (version) {
+ case SignedCertificateTimestamp::Version::V1:
+ out = version;
+ return Success;
+ }
+ return Result::ERROR_BAD_DER;
+}
+
+// Writes a TLS-encoded variable length unsigned integer to |output|.
+// Note: range/overflow checks are not performed on the input parameters.
+// |length| indicates the size (in bytes) of the integer to be written.
+// |value| the value itself to be written.
+static Result
+UncheckedWriteUint(size_t length, uint64_t value, Buffer& output)
+{
+ if (!output.reserve(length + output.length())) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ for (; length > 0; --length) {
+ uint8_t nextByte = (value >> ((length - 1) * 8)) & 0xFF;
+ output.infallibleAppend(nextByte);
+ }
+ return Success;
+}
+
+// Performs sanity checks on T and calls UncheckedWriteUint.
+template <size_t length, typename T>
+static inline Result
+WriteUint(T value, Buffer& output)
+{
+ static_assert(length <= 8, "At most 8 byte integers can be written");
+ static_assert(sizeof(T) >= length, "T must be able to hold <length> bytes");
+ if (mozilla::IsSigned<T>::value) {
+ // We accept signed integer types assuming the actual value is non-negative.
+ if (value < 0) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ }
+ if (sizeof(T) > length) {
+ // We allow the value variable to take more bytes than is written,
+ // but the unwritten bytes must be zero.
+ // Note: when "sizeof(T) == length" holds, "value >> (length * 8)" is
+ // undefined since the shift is too big. On some compilers, this would
+ // produce a warning even though the actual code is unreachable.
+ if (value >> (length * 8 - 1) > 1) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ }
+ return UncheckedWriteUint(length, static_cast<uint64_t>(value), output);
+}
+
+// Writes an array to |output| from |input|.
+// Should be used in one of two cases:
+// * The length of |input| has already been encoded into the |output| stream.
+// * The length of |input| is fixed and the reader is expected to specify that
+// length when reading.
+// If the length of |input| is dynamic and data is expected to follow it,
+// WriteVariableBytes must be used.
+static Result
+WriteEncodedBytes(Input input, Buffer& output)
+{
+ if (!output.append(input.UnsafeGetData(), input.GetLength())) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ return Success;
+}
+
+// Same as above, but the source data is in a Buffer.
+static Result
+WriteEncodedBytes(const Buffer& source, Buffer& output)
+{
+ if (!output.appendAll(source)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ return Success;
+}
+
+// A variable-length byte array is prefixed by its length when serialized.
+// This writes the length prefix.
+// |prefixLength| indicates the number of bytes needed to represent the length.
+// |dataLength| is the length of the byte array following the prefix.
+// Fails if |dataLength| is more than 2^|prefixLength| - 1.
+template <size_t prefixLength>
+static Result
+WriteVariableBytesPrefix(size_t dataLength, Buffer& output)
+{
+ const size_t maxAllowedInputSize =
+ static_cast<size_t>(((1 << (prefixLength * 8)) - 1));
+ if (dataLength > maxAllowedInputSize) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ return WriteUint<prefixLength>(dataLength, output);
+}
+
+// Writes a variable-length array to |output|.
+// |prefixLength| indicates the number of bytes needed to represent the length.
+// |input| is the array itself.
+// Fails if the size of |input| is more than 2^|prefixLength| - 1.
+template <size_t prefixLength>
+static Result
+WriteVariableBytes(Input input, Buffer& output)
+{
+ Result rv = WriteVariableBytesPrefix<prefixLength>(input.GetLength(), output);
+ if (rv != Success) {
+ return rv;
+ }
+ return WriteEncodedBytes(input, output);
+}
+
+// Same as above, but the source data is in a Buffer.
+template <size_t prefixLength>
+static Result
+WriteVariableBytes(const Buffer& source, Buffer& output)
+{
+ Input input;
+ Result rv = BufferToInput(source, input);
+ if (rv != Success) {
+ return rv;
+ }
+ return WriteVariableBytes<prefixLength>(input, output);
+}
+
+// Writes a LogEntry of type X.509 cert to |output|.
+// |input| is the LogEntry containing the certificate.
+static Result
+EncodeAsn1CertLogEntry(const LogEntry& entry, Buffer& output)
+{
+ return WriteVariableBytes<kAsn1CertificateLengthBytes>(entry.leafCertificate,
+ output);
+}
+
+// Writes a LogEntry of type PreCertificate to |output|.
+// |input| is the LogEntry containing the TBSCertificate and issuer key hash.
+static Result
+EncodePrecertLogEntry(const LogEntry& entry, Buffer& output)
+{
+ if (entry.issuerKeyHash.length() != kLogIdLength) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ Result rv = WriteEncodedBytes(entry.issuerKeyHash, output);
+ if (rv != Success) {
+ return rv;
+ }
+ return WriteVariableBytes<kTbsCertificateLengthBytes>(entry.tbsCertificate,
+ output);
+}
+
+
+Result
+EncodeDigitallySigned(const DigitallySigned& data, Buffer& output)
+{
+ Result rv = WriteUint<kHashAlgorithmLength>(
+ static_cast<unsigned int>(data.hashAlgorithm), output);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = WriteUint<kSigAlgorithmLength>(
+ static_cast<unsigned int>(data.signatureAlgorithm), output);
+ if (rv != Success) {
+ return rv;
+ }
+ return WriteVariableBytes<kSignatureLengthBytes>(data.signatureData, output);
+}
+
+Result
+DecodeDigitallySigned(Reader& reader, DigitallySigned& output)
+{
+ DigitallySigned result;
+
+ Result rv = ReadHashAlgorithm(reader, result.hashAlgorithm);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = ReadSignatureAlgorithm(reader, result.signatureAlgorithm);
+ if (rv != Success) {
+ return rv;
+ }
+
+ Input signatureData;
+ rv = ReadVariableBytes<kSignatureLengthBytes>(reader, signatureData);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = InputToBuffer(signatureData, result.signatureData);
+ if (rv != Success) {
+ return rv;
+ }
+
+ output = Move(result);
+ return Success;
+}
+
+Result
+EncodeLogEntry(const LogEntry& entry, Buffer& output)
+{
+ Result rv = WriteUint<kLogEntryTypeLength>(
+ static_cast<unsigned int>(entry.type), output);
+ if (rv != Success) {
+ return rv;
+ }
+ switch (entry.type) {
+ case LogEntry::Type::X509:
+ return EncodeAsn1CertLogEntry(entry, output);
+ case LogEntry::Type::Precert:
+ return EncodePrecertLogEntry(entry, output);
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected LogEntry type");
+ }
+ return Result::ERROR_BAD_DER;
+}
+
+static Result
+WriteTimeSinceEpoch(uint64_t timestamp, Buffer& output)
+{
+ return WriteUint<kTimestampLength>(timestamp, output);
+}
+
+Result
+EncodeV1SCTSignedData(uint64_t timestamp, Input serializedLogEntry,
+ Input extensions, Buffer& output)
+{
+ Result rv = WriteUint<kVersionLength>(static_cast<unsigned int>(
+ SignedCertificateTimestamp::Version::V1), output);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = WriteUint<kSignatureTypeLength>(static_cast<unsigned int>(
+ SignatureType::CertificateTimestamp), output);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = WriteTimeSinceEpoch(timestamp, output);
+ if (rv != Success) {
+ return rv;
+ }
+ // NOTE: serializedLogEntry must already be serialized and contain the
+ // length as the prefix.
+ rv = WriteEncodedBytes(serializedLogEntry, output);
+ if (rv != Success) {
+ return rv;
+ }
+ return WriteVariableBytes<kExtensionsLengthBytes>(extensions, output);
+}
+
+Result
+EncodeTreeHeadSignature(const SignedTreeHead& signedTreeHead,
+ Buffer& output)
+{
+ Result rv = WriteUint<kVersionLength>(
+ static_cast<unsigned int>(signedTreeHead.version), output);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = WriteUint<kSignatureTypeLength>(
+ static_cast<unsigned int>(SignatureType::TreeHash), output);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = WriteTimeSinceEpoch(signedTreeHead.timestamp, output);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = WriteUint<kTreeSizeLength>(signedTreeHead.treeSize, output);
+ if (rv != Success) {
+ return rv;
+ }
+ if (signedTreeHead.sha256RootHash.length() != kSthRootHashLength) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ return WriteEncodedBytes(signedTreeHead.sha256RootHash, output);
+}
+
+Result
+DecodeSCTList(Input input, Reader& listReader)
+{
+ Reader inputReader(input);
+ Input listData;
+ Result rv = ReadVariableBytes<kSCTListLengthBytes>(inputReader, listData);
+ if (rv != Success) {
+ return rv;
+ }
+ return listReader.Init(listData);
+}
+
+Result
+ReadSCTListItem(Reader& listReader, Input& output)
+{
+ if (listReader.AtEnd()) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ Result rv = ReadVariableBytes<kSerializedSCTLengthBytes>(listReader, output);
+ if (rv != Success) {
+ return rv;
+ }
+ if (output.GetLength() == 0) {
+ return Result::ERROR_BAD_DER;
+ }
+ return Success;
+}
+
+Result
+DecodeSignedCertificateTimestamp(Reader& reader,
+ SignedCertificateTimestamp& output)
+{
+ SignedCertificateTimestamp result;
+
+ Result rv = ReadVersion(reader, result.version);
+ if (rv != Success) {
+ return rv;
+ }
+
+ uint64_t timestamp;
+ Input logId;
+ Input extensions;
+
+ rv = ReadFixedBytes(kLogIdLength, reader, logId);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = ReadUint<kTimestampLength>(reader, timestamp);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = ReadVariableBytes<kExtensionsLengthBytes>(reader, extensions);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = DecodeDigitallySigned(reader, result.signature);
+ if (rv != Success) {
+ return rv;
+ }
+
+ rv = InputToBuffer(logId, result.logId);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = InputToBuffer(extensions, result.extensions);
+ if (rv != Success) {
+ return rv;
+ }
+ result.timestamp = timestamp;
+
+ result.origin = SignedCertificateTimestamp::Origin::Unknown;
+ result.verificationStatus =
+ SignedCertificateTimestamp::VerificationStatus::None;
+
+ output = Move(result);
+ return Success;
+}
+
+Result
+EncodeSCTList(const Vector<pkix::Input>& scts, Buffer& output)
+{
+ // Find out the total size of the SCT list to be written so we can
+ // write the prefix for the list before writing its contents.
+ size_t sctListLength = 0;
+ for (auto& sct : scts) {
+ sctListLength +=
+ /* data size */ sct.GetLength() +
+ /* length prefix size */ kSerializedSCTLengthBytes;
+ }
+
+ if (!output.reserve(kSCTListLengthBytes + sctListLength)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+
+ // Write the prefix for the SCT list.
+ Result rv = WriteVariableBytesPrefix<kSCTListLengthBytes>(sctListLength,
+ output);
+ if (rv != Success) {
+ return rv;
+ }
+ // Now write each SCT from the list.
+ for (auto& sct : scts) {
+ rv = WriteVariableBytes<kSerializedSCTLengthBytes>(sct, output);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+ return Success;
+}
+
+} } // namespace mozilla::ct
diff --git a/security/certverifier/CTSerialization.h b/security/certverifier/CTSerialization.h
new file mode 100644
index 000000000..ad3b216a8
--- /dev/null
+++ b/security/certverifier/CTSerialization.h
@@ -0,0 +1,70 @@
+/* -*- 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 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/. */
+
+#ifndef CTSerialization_h
+#define CTSerialization_h
+
+#include "mozilla/Vector.h"
+#include "pkix/Input.h"
+#include "pkix/Result.h"
+#include "SignedCertificateTimestamp.h"
+#include "SignedTreeHead.h"
+
+// Utility functions for encoding/decoding structures used by Certificate
+// Transparency to/from the TLS wire format encoding.
+namespace mozilla { namespace ct {
+
+// Encodes the DigitallySigned |data| to |output|.
+pkix::Result EncodeDigitallySigned(const DigitallySigned& data,
+ Buffer& output);
+
+// Reads and decodes a DigitallySigned object from |reader|.
+// On failure, the cursor position of |reader| is undefined.
+pkix::Result DecodeDigitallySigned(pkix::Reader& reader,
+ DigitallySigned& output);
+
+// Encodes the |input| LogEntry to |output|. The size of the entry
+// must not exceed the allowed size in RFC6962.
+pkix::Result EncodeLogEntry(const LogEntry& entry, Buffer& output);
+
+// Encodes the data signed by a Signed Certificate Timestamp (SCT) into
+// |output|. The signature included in the SCT can then be verified over these
+// bytes.
+// |timestamp| timestamp from the SCT.
+// |serializedLogEntry| the log entry signed by the SCT.
+// |extensions| CT extensions.
+pkix::Result EncodeV1SCTSignedData(uint64_t timestamp,
+ pkix::Input serializedLogEntry,
+ pkix::Input extensions,
+ Buffer& output);
+
+// Encodes the data signed by a Signed Tree Head (STH) |signedTreeHead| into
+// |output|. The signature included in the |signedTreeHead| can then be
+// verified over these bytes.
+pkix::Result EncodeTreeHeadSignature(const SignedTreeHead& signedTreeHead,
+ Buffer& output);
+
+// Decodes a list of Signed Certificate Timestamps
+// (SignedCertificateTimestampList as defined in RFC6962). This list
+// is typically obtained from the CT extension in a certificate.
+// To extract the individual items of the list, call ReadSCTListItem on
+// the returned reader until the reader reaches its end.
+// Note that the validity of each extracted SCT should be checked separately.
+pkix::Result DecodeSCTList(pkix::Input input, pkix::Reader& listReader);
+
+// Reads a single SCT from the reader returned by DecodeSCTList.
+pkix::Result ReadSCTListItem(pkix::Reader& listReader, pkix::Input& result);
+
+// Decodes a single SCT from |input| to |output|.
+pkix::Result DecodeSignedCertificateTimestamp(pkix::Reader& input,
+ SignedCertificateTimestamp& output);
+
+// Encodes a list of SCTs (|scts|) to |output|.
+pkix::Result EncodeSCTList(const Vector<pkix::Input>& scts, Buffer& output);
+
+} } // namespace mozilla::ct
+
+#endif // CTSerialization_h
diff --git a/security/certverifier/CTVerifyResult.cpp b/security/certverifier/CTVerifyResult.cpp
new file mode 100644
index 000000000..fc0b4adae
--- /dev/null
+++ b/security/certverifier/CTVerifyResult.cpp
@@ -0,0 +1,18 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CTVerifyResult.h"
+
+namespace mozilla { namespace ct {
+
+void
+CTVerifyResult::Reset()
+{
+ scts.clear();
+ decodingErrors = 0;
+}
+
+} } // namespace mozilla::ct
diff --git a/security/certverifier/CTVerifyResult.h b/security/certverifier/CTVerifyResult.h
new file mode 100644
index 000000000..fc6d28339
--- /dev/null
+++ b/security/certverifier/CTVerifyResult.h
@@ -0,0 +1,41 @@
+/* -*- 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 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/. */
+
+#ifndef CTVerifyResult_h
+#define CTVerifyResult_h
+
+#include "mozilla/Vector.h"
+#include "SignedCertificateTimestamp.h"
+
+namespace mozilla { namespace ct {
+
+typedef Vector<SignedCertificateTimestamp> SCTList;
+
+// Holds Signed Certificate Timestamps verification results.
+class CTVerifyResult
+{
+public:
+ // SCTs that were processed during the verification. For each SCT,
+ // the verification result is stored in its |verificationStatus| field.
+ SCTList scts;
+
+ // The verifier makes the best effort to extract the available SCTs
+ // from the binary sources provided to it.
+ // If some SCT cannot be extracted due to encoding errors, the verifier
+ // proceeds to the next available one. In other words, decoding errors are
+ // effectively ignored.
+ // Note that a serialized SCT may fail to decode for a "legitimate" reason,
+ // e.g. if the SCT is from a future version of the Certificate Transparency
+ // standard.
+ // |decodingErrors| field counts the errors of the above kind.
+ size_t decodingErrors;
+
+ void Reset();
+};
+
+} } // namespace mozilla::ct
+
+#endif // CTVerifyResult_h
diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp
new file mode 100644
index 000000000..61d8fcdb8
--- /dev/null
+++ b/security/certverifier/CertVerifier.cpp
@@ -0,0 +1,903 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CertVerifier.h"
+
+#include <stdint.h>
+
+#include "CTKnownLogs.h"
+#include "ExtendedValidation.h"
+#include "MultiLogCTVerifier.h"
+#include "NSSCertDBTrustDomain.h"
+#include "NSSErrorsService.h"
+#include "cert.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Casting.h"
+#include "nsNSSComponent.h"
+#include "nsServiceManagerUtils.h"
+#include "pk11pub.h"
+#include "pkix/pkix.h"
+#include "pkix/pkixnss.h"
+#include "prerror.h"
+#include "secerr.h"
+#include "secmod.h"
+#include "sslerr.h"
+
+using namespace mozilla::ct;
+using namespace mozilla::pkix;
+using namespace mozilla::psm;
+
+mozilla::LazyLogModule gCertVerifierLog("certverifier");
+
+namespace mozilla { namespace psm {
+
+const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1;
+const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2;
+const CertVerifier::Flags CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST = 4;
+
+CertVerifier::CertVerifier(OcspDownloadConfig odc,
+ OcspStrictConfig osc,
+ OcspGetConfig ogc,
+ uint32_t certShortLifetimeInDays,
+ PinningMode pinningMode,
+ SHA1Mode sha1Mode,
+ BRNameMatchingPolicy::Mode nameMatchingMode,
+ NetscapeStepUpPolicy netscapeStepUpPolicy,
+ CertificateTransparencyMode ctMode)
+ : mOCSPDownloadConfig(odc)
+ , mOCSPStrict(osc == ocspStrict)
+ , mOCSPGETEnabled(ogc == ocspGetEnabled)
+ , mCertShortLifetimeInDays(certShortLifetimeInDays)
+ , mPinningMode(pinningMode)
+ , mSHA1Mode(sha1Mode)
+ , mNameMatchingMode(nameMatchingMode)
+ , mNetscapeStepUpPolicy(netscapeStepUpPolicy)
+ , mCTMode(ctMode)
+{
+ LoadKnownCTLogs();
+}
+
+CertVerifier::~CertVerifier()
+{
+}
+
+Result
+IsCertChainRootBuiltInRoot(const UniqueCERTCertList& chain, bool& result)
+{
+ if (!chain || CERT_LIST_EMPTY(chain)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ CERTCertListNode* rootNode = CERT_LIST_TAIL(chain);
+ if (!rootNode) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ CERTCertificate* root = rootNode->cert;
+ if (!root) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ return IsCertBuiltInRoot(root, result);
+}
+
+Result
+IsCertBuiltInRoot(CERTCertificate* cert, bool& result)
+{
+ result = false;
+#ifdef DEBUG
+ nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
+ if (!component) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ nsresult rv = component->IsCertTestBuiltInRoot(cert, result);
+ if (NS_FAILED(rv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ if (result) {
+ return Success;
+ }
+#endif // DEBUG
+ AutoSECMODListReadLock lock;
+ for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
+ list = list->next) {
+ for (int i = 0; i < list->module->slotCount; i++) {
+ PK11SlotInfo* slot = list->module->slots[i];
+ // PK11_HasRootCerts should return true if and only if the given slot has
+ // an object with a CKA_CLASS of CKO_NETSCAPE_BUILTIN_ROOT_LIST, which
+ // should be true only of the builtin root list.
+ // If we can find a copy of the given certificate on the slot with the
+ // builtin root list, that certificate must be a builtin.
+ if (PK11_IsPresent(slot) && PK11_HasRootCerts(slot) &&
+ PK11_FindCertInSlot(slot, cert, nullptr) != CK_INVALID_HANDLE) {
+ result = true;
+ return Success;
+ }
+ }
+ }
+ return Success;
+}
+
+static Result
+BuildCertChainForOneKeyUsage(NSSCertDBTrustDomain& trustDomain, Input certDER,
+ Time time, KeyUsage ku1, KeyUsage ku2,
+ KeyUsage ku3, KeyPurposeId eku,
+ const CertPolicyId& requiredPolicy,
+ const Input* stapledOCSPResponse,
+ /*optional out*/ CertVerifier::OCSPStaplingStatus*
+ ocspStaplingStatus)
+{
+ trustDomain.ResetAccumulatedState();
+ Result rv = BuildCertChain(trustDomain, certDER, time,
+ EndEntityOrCA::MustBeEndEntity, ku1,
+ eku, requiredPolicy, stapledOCSPResponse);
+ if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
+ trustDomain.ResetAccumulatedState();
+ rv = BuildCertChain(trustDomain, certDER, time,
+ EndEntityOrCA::MustBeEndEntity, ku2,
+ eku, requiredPolicy, stapledOCSPResponse);
+ if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
+ trustDomain.ResetAccumulatedState();
+ rv = BuildCertChain(trustDomain, certDER, time,
+ EndEntityOrCA::MustBeEndEntity, ku3,
+ eku, requiredPolicy, stapledOCSPResponse);
+ if (rv != Success) {
+ rv = Result::ERROR_INADEQUATE_KEY_USAGE;
+ }
+ }
+ }
+ if (ocspStaplingStatus) {
+ *ocspStaplingStatus = trustDomain.GetOCSPStaplingStatus();
+ }
+ return rv;
+}
+
+void
+CertVerifier::LoadKnownCTLogs()
+{
+ mCTVerifier = MakeUnique<MultiLogCTVerifier>();
+ for (const CTLogInfo& log : kCTLogList) {
+ Input publicKey;
+ Result rv = publicKey.Init(
+ BitwiseCast<const uint8_t*, const char*>(log.logKey), log.logKeyLength);
+ if (rv != Success) {
+ MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log");
+ continue;
+ }
+ rv = mCTVerifier->AddLog(publicKey);
+ if (rv != Success) {
+ MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log");
+ continue;
+ }
+ }
+}
+
+Result
+CertVerifier::VerifySignedCertificateTimestamps(
+ NSSCertDBTrustDomain& trustDomain, const UniqueCERTCertList& builtChain,
+ Input sctsFromTLS, Time time,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo)
+{
+ if (ctInfo) {
+ ctInfo->Reset();
+ }
+ if (mCTMode == CertificateTransparencyMode::Disabled) {
+ return Success;
+ }
+ if (ctInfo) {
+ ctInfo->enabled = true;
+ }
+
+ if (!builtChain || CERT_LIST_EMPTY(builtChain)) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ bool gotScts = false;
+ Input embeddedSCTs = trustDomain.GetSCTListFromCertificate();
+ if (embeddedSCTs.GetLength() > 0) {
+ gotScts = true;
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("Got embedded SCT data of length %zu\n",
+ static_cast<size_t>(embeddedSCTs.GetLength())));
+ }
+ Input sctsFromOCSP = trustDomain.GetSCTListFromOCSPStapling();
+ if (sctsFromOCSP.GetLength() > 0) {
+ gotScts = true;
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("Got OCSP SCT data of length %zu\n",
+ static_cast<size_t>(sctsFromOCSP.GetLength())));
+ }
+ if (sctsFromTLS.GetLength() > 0) {
+ gotScts = true;
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("Got TLS SCT data of length %zu\n",
+ static_cast<size_t>(sctsFromTLS.GetLength())));
+ }
+ if (!gotScts) {
+ return Success;
+ }
+
+ CERTCertListNode* endEntityNode = CERT_LIST_HEAD(builtChain);
+ if (!endEntityNode || CERT_LIST_END(endEntityNode, builtChain)) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ CERTCertListNode* issuerNode = CERT_LIST_NEXT(endEntityNode);
+ if (!issuerNode || CERT_LIST_END(issuerNode, builtChain)) {
+ // Issuer certificate is required for SCT verification.
+ // TODO(bug 1294580): change this to Result::FATAL_ERROR_INVALID_ARGS
+ return Success;
+ }
+
+ CERTCertificate* endEntity = endEntityNode->cert;
+ CERTCertificate* issuer = issuerNode->cert;
+ if (!endEntity || !issuer) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ Input endEntityDER;
+ Result rv = endEntityDER.Init(endEntity->derCert.data,
+ endEntity->derCert.len);
+ if (rv != Success) {
+ return rv;
+ }
+
+ Input issuerPublicKeyDER;
+ rv = issuerPublicKeyDER.Init(issuer->derPublicKey.data,
+ issuer->derPublicKey.len);
+ if (rv != Success) {
+ return rv;
+ }
+
+ CTVerifyResult result;
+ rv = mCTVerifier->Verify(endEntityDER, issuerPublicKeyDER,
+ embeddedSCTs, sctsFromOCSP, sctsFromTLS, time,
+ result);
+ if (rv != Success) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("SCT verification failed with fatal error %i\n", rv));
+ return rv;
+ }
+
+ if (MOZ_LOG_TEST(gCertVerifierLog, LogLevel::Debug)) {
+ size_t verifiedCount = 0;
+ size_t unknownLogCount = 0;
+ size_t invalidSignatureCount = 0;
+ size_t invalidTimestampCount = 0;
+ for (const SignedCertificateTimestamp& sct : result.scts) {
+ switch (sct.verificationStatus) {
+ case SignedCertificateTimestamp::VerificationStatus::OK:
+ verifiedCount++;
+ break;
+ case SignedCertificateTimestamp::VerificationStatus::UnknownLog:
+ unknownLogCount++;
+ break;
+ case SignedCertificateTimestamp::VerificationStatus::InvalidSignature:
+ invalidSignatureCount++;
+ break;
+ case SignedCertificateTimestamp::VerificationStatus::InvalidTimestamp:
+ invalidTimestampCount++;
+ break;
+ case SignedCertificateTimestamp::VerificationStatus::None:
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected SCT verificationStatus");
+ }
+ }
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("SCT verification result: "
+ "verified=%zu unknownLog=%zu "
+ "invalidSignature=%zu invalidTimestamp=%zu "
+ "decodingErrors=%zu\n",
+ verifiedCount, unknownLogCount,
+ invalidSignatureCount, invalidTimestampCount,
+ result.decodingErrors));
+ }
+
+ if (ctInfo) {
+ ctInfo->processedSCTs = true;
+ ctInfo->verifyResult = Move(result);
+ }
+ return Success;
+}
+
+bool
+CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode)
+{
+ switch (mSHA1Mode) {
+ case SHA1Mode::Forbidden:
+ return mode != SHA1Mode::Forbidden;
+ case SHA1Mode::ImportedRoot:
+ return mode != SHA1Mode::Forbidden && mode != SHA1Mode::ImportedRoot;
+ case SHA1Mode::ImportedRootOrBefore2016:
+ return mode == SHA1Mode::Allowed;
+ case SHA1Mode::Allowed:
+ return false;
+ // MSVC warns unless we explicitly handle this now-unused option.
+ case SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
+ default:
+ MOZ_ASSERT(false, "unexpected SHA1Mode type");
+ return true;
+ }
+}
+
+static const unsigned int MIN_RSA_BITS = 2048;
+static const unsigned int MIN_RSA_BITS_WEAK = 1024;
+
+Result
+CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
+ Time time, void* pinArg, const char* hostname,
+ /*out*/ UniqueCERTCertList& builtChain,
+ /*optional*/ const Flags flags,
+ /*optional*/ const SECItem* stapledOCSPResponseSECItem,
+ /*optional*/ const SECItem* sctsFromTLSSECItem,
+ /*optional*/ const NeckoOriginAttributes& originAttributes,
+ /*optional out*/ SECOidTag* evOidPolicy,
+ /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
+ /*optional out*/ KeySizeStatus* keySizeStatus,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult,
+ /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo)
+{
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n"));
+
+ PR_ASSERT(cert);
+ PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
+ PR_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus);
+ PR_ASSERT(usage == certificateUsageSSLServer || !sha1ModeResult);
+
+ if (evOidPolicy) {
+ *evOidPolicy = SEC_OID_UNKNOWN;
+ }
+ if (ocspStaplingStatus) {
+ if (usage != certificateUsageSSLServer) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ *ocspStaplingStatus = OCSP_STAPLING_NEVER_CHECKED;
+ }
+
+ if (keySizeStatus) {
+ if (usage != certificateUsageSSLServer) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ *keySizeStatus = KeySizeStatus::NeverChecked;
+ }
+
+ if (sha1ModeResult) {
+ if (usage != certificateUsageSSLServer) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ *sha1ModeResult = SHA1ModeResult::NeverChecked;
+ }
+
+ if (!cert ||
+ (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ Input certDER;
+ Result rv = certDER.Init(cert->derCert.data, cert->derCert.len);
+ if (rv != Success) {
+ return rv;
+ }
+
+ // We configure the OCSP fetching modes separately for EV and non-EV
+ // verifications.
+ NSSCertDBTrustDomain::OCSPFetching defaultOCSPFetching
+ = (mOCSPDownloadConfig == ocspOff) ||
+ (mOCSPDownloadConfig == ocspEVOnly) ||
+ (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
+ : !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
+ : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
+
+ OcspGetConfig ocspGETConfig = mOCSPGETEnabled ? ocspGetEnabled
+ : ocspGetDisabled;
+
+ Input stapledOCSPResponseInput;
+ const Input* stapledOCSPResponse = nullptr;
+ if (stapledOCSPResponseSECItem) {
+ rv = stapledOCSPResponseInput.Init(stapledOCSPResponseSECItem->data,
+ stapledOCSPResponseSECItem->len);
+ if (rv != Success) {
+ // The stapled OCSP response was too big.
+ return Result::ERROR_OCSP_MALFORMED_RESPONSE;
+ }
+ stapledOCSPResponse = &stapledOCSPResponseInput;
+ }
+
+ Input sctsFromTLSInput;
+ if (sctsFromTLSSECItem) {
+ rv = sctsFromTLSInput.Init(sctsFromTLSSECItem->data,
+ sctsFromTLSSECItem->len);
+ // Silently discard the error of the extension being too big,
+ // do not fail the verification.
+ MOZ_ASSERT(rv == Success);
+ }
+
+ switch (usage) {
+ case certificateUsageSSLClient: {
+ // XXX: We don't really have a trust bit for SSL client authentication so
+ // just use trustEmail as it is the closest alternative.
+ NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
+ mOCSPCache, pinArg, ocspGETConfig,
+ mCertShortLifetimeInDays,
+ pinningDisabled, MIN_RSA_BITS_WEAK,
+ ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed,
+ NetscapeStepUpPolicy::NeverMatch,
+ originAttributes,
+ builtChain, nullptr, nullptr);
+ rv = BuildCertChain(trustDomain, certDER, time,
+ EndEntityOrCA::MustBeEndEntity,
+ KeyUsage::digitalSignature,
+ KeyPurposeId::id_kp_clientAuth,
+ CertPolicyId::anyPolicy, stapledOCSPResponse);
+ break;
+ }
+
+ case certificateUsageSSLServer: {
+ // TODO: When verifying a certificate in an SSL handshake, we should
+ // restrict the acceptable key usage based on the key exchange method
+ // chosen by the server.
+
+ // These configurations are in order of most restrictive to least
+ // restrictive. This enables us to gather telemetry on the expected
+ // results of setting the default policy to a particular configuration.
+ SHA1Mode sha1ModeConfigurations[] = {
+ SHA1Mode::Forbidden,
+ SHA1Mode::ImportedRoot,
+ SHA1Mode::ImportedRootOrBefore2016,
+ SHA1Mode::Allowed,
+ };
+
+ SHA1ModeResult sha1ModeResults[] = {
+ SHA1ModeResult::SucceededWithoutSHA1,
+ SHA1ModeResult::SucceededWithImportedRoot,
+ SHA1ModeResult::SucceededWithImportedRootOrSHA1Before2016,
+ SHA1ModeResult::SucceededWithSHA1,
+ };
+
+ size_t sha1ModeConfigurationsCount = MOZ_ARRAY_LENGTH(sha1ModeConfigurations);
+
+ static_assert(MOZ_ARRAY_LENGTH(sha1ModeConfigurations) ==
+ MOZ_ARRAY_LENGTH(sha1ModeResults),
+ "digestAlgorithm array lengths differ");
+
+ rv = Result::ERROR_UNKNOWN_ERROR;
+
+ // Try to validate for EV first.
+ NSSCertDBTrustDomain::OCSPFetching evOCSPFetching
+ = (mOCSPDownloadConfig == ocspOff) ||
+ (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
+ : NSSCertDBTrustDomain::FetchOCSPForEV;
+
+ CertPolicyId evPolicy;
+ SECOidTag evPolicyOidTag;
+ SECStatus srv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
+ for (size_t i = 0;
+ i < sha1ModeConfigurationsCount && rv != Success && srv == SECSuccess;
+ i++) {
+ // Don't attempt verification if the SHA1 mode set by preferences
+ // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
+ // (To put it another way, only attempt verification if the SHA1 mode
+ // option we're on is as restrictive or more restrictive than
+ // mSHA1Mode.) This allows us to gather telemetry information while
+ // still enforcing the mode set by preferences.
+ if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[i])) {
+ continue;
+ }
+
+ // Because of the try-strict and fallback approach, we have to clear any
+ // previously noted telemetry information
+ if (pinningTelemetryInfo) {
+ pinningTelemetryInfo->Reset();
+ }
+
+ NSSCertDBTrustDomain
+ trustDomain(trustSSL, evOCSPFetching,
+ mOCSPCache, pinArg, ocspGETConfig,
+ mCertShortLifetimeInDays, mPinningMode, MIN_RSA_BITS,
+ ValidityCheckingMode::CheckForEV,
+ sha1ModeConfigurations[i], mNetscapeStepUpPolicy,
+ originAttributes, builtChain, pinningTelemetryInfo,
+ hostname);
+ rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
+ KeyUsage::digitalSignature,// (EC)DHE
+ KeyUsage::keyEncipherment, // RSA
+ KeyUsage::keyAgreement, // (EC)DH
+ KeyPurposeId::id_kp_serverAuth,
+ evPolicy, stapledOCSPResponse,
+ ocspStaplingStatus);
+ if (rv == Success &&
+ sha1ModeConfigurations[i] == SHA1Mode::ImportedRoot) {
+ bool isBuiltInRoot = false;
+ rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
+ if (rv != Success) {
+ break;
+ }
+ if (isBuiltInRoot) {
+ rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ }
+ }
+ if (rv == Success) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("cert is EV with status %i\n", sha1ModeResults[i]));
+ if (evOidPolicy) {
+ *evOidPolicy = evPolicyOidTag;
+ }
+ if (sha1ModeResult) {
+ *sha1ModeResult = sha1ModeResults[i];
+ }
+ rv = VerifySignedCertificateTimestamps(trustDomain, builtChain,
+ sctsFromTLSInput, time,
+ ctInfo);
+ if (rv != Success) {
+ break;
+ }
+ }
+ }
+ if (rv == Success) {
+ break;
+ }
+
+ if (flags & FLAG_MUST_BE_EV) {
+ rv = Result::ERROR_POLICY_VALIDATION_FAILED;
+ break;
+ }
+
+ // Now try non-EV.
+ unsigned int keySizeOptions[] = {
+ MIN_RSA_BITS,
+ MIN_RSA_BITS_WEAK
+ };
+
+ KeySizeStatus keySizeStatuses[] = {
+ KeySizeStatus::LargeMinimumSucceeded,
+ KeySizeStatus::CompatibilityRisk
+ };
+
+ static_assert(MOZ_ARRAY_LENGTH(keySizeOptions) ==
+ MOZ_ARRAY_LENGTH(keySizeStatuses),
+ "keySize array lengths differ");
+
+ size_t keySizeOptionsCount = MOZ_ARRAY_LENGTH(keySizeStatuses);
+
+ for (size_t i = 0; i < keySizeOptionsCount && rv != Success; i++) {
+ for (size_t j = 0; j < sha1ModeConfigurationsCount && rv != Success;
+ j++) {
+ // Don't attempt verification if the SHA1 mode set by preferences
+ // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
+ // (To put it another way, only attempt verification if the SHA1 mode
+ // option we're on is as restrictive or more restrictive than
+ // mSHA1Mode.) This allows us to gather telemetry information while
+ // still enforcing the mode set by preferences.
+ if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[j])) {
+ continue;
+ }
+
+ // invalidate any telemetry info relating to failed chains
+ if (pinningTelemetryInfo) {
+ pinningTelemetryInfo->Reset();
+ }
+
+ NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
+ mOCSPCache, pinArg, ocspGETConfig,
+ mCertShortLifetimeInDays,
+ mPinningMode, keySizeOptions[i],
+ ValidityCheckingMode::CheckingOff,
+ sha1ModeConfigurations[j],
+ mNetscapeStepUpPolicy,
+ originAttributes, builtChain,
+ pinningTelemetryInfo, hostname);
+ rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
+ KeyUsage::digitalSignature,//(EC)DHE
+ KeyUsage::keyEncipherment,//RSA
+ KeyUsage::keyAgreement,//(EC)DH
+ KeyPurposeId::id_kp_serverAuth,
+ CertPolicyId::anyPolicy,
+ stapledOCSPResponse,
+ ocspStaplingStatus);
+ if (rv == Success &&
+ sha1ModeConfigurations[j] == SHA1Mode::ImportedRoot) {
+ bool isBuiltInRoot = false;
+ rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
+ if (rv != Success) {
+ break;
+ }
+ if (isBuiltInRoot) {
+ rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ }
+ }
+ if (rv == Success) {
+ if (keySizeStatus) {
+ *keySizeStatus = keySizeStatuses[i];
+ }
+ if (sha1ModeResult) {
+ *sha1ModeResult = sha1ModeResults[j];
+ }
+ rv = VerifySignedCertificateTimestamps(trustDomain, builtChain,
+ sctsFromTLSInput, time,
+ ctInfo);
+ if (rv != Success) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (rv == Success) {
+ break;
+ }
+
+ if (keySizeStatus) {
+ *keySizeStatus = KeySizeStatus::AlreadyBad;
+ }
+ // The telemetry probe CERT_CHAIN_SHA1_POLICY_STATUS gives us feedback on
+ // the result of setting a specific policy. However, we don't want noise
+ // from users who have manually set the policy to something other than the
+ // default, so we only collect for ImportedRoot (which is the default).
+ if (sha1ModeResult && mSHA1Mode == SHA1Mode::ImportedRoot) {
+ *sha1ModeResult = SHA1ModeResult::Failed;
+ }
+
+ break;
+ }
+
+ case certificateUsageSSLCA: {
+ NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
+ mOCSPCache, pinArg, ocspGETConfig,
+ mCertShortLifetimeInDays,
+ pinningDisabled, MIN_RSA_BITS_WEAK,
+ ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed, mNetscapeStepUpPolicy,
+ originAttributes, builtChain, nullptr,
+ nullptr);
+ rv = BuildCertChain(trustDomain, certDER, time,
+ EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign,
+ KeyPurposeId::id_kp_serverAuth,
+ CertPolicyId::anyPolicy, stapledOCSPResponse);
+ break;
+ }
+
+ case certificateUsageEmailSigner: {
+ NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
+ mOCSPCache, pinArg, ocspGETConfig,
+ mCertShortLifetimeInDays,
+ pinningDisabled, MIN_RSA_BITS_WEAK,
+ ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed,
+ NetscapeStepUpPolicy::NeverMatch,
+ originAttributes, builtChain, nullptr,
+ nullptr);
+ rv = BuildCertChain(trustDomain, certDER, time,
+ EndEntityOrCA::MustBeEndEntity,
+ KeyUsage::digitalSignature,
+ KeyPurposeId::id_kp_emailProtection,
+ CertPolicyId::anyPolicy, stapledOCSPResponse);
+ if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
+ rv = BuildCertChain(trustDomain, certDER, time,
+ EndEntityOrCA::MustBeEndEntity,
+ KeyUsage::nonRepudiation,
+ KeyPurposeId::id_kp_emailProtection,
+ CertPolicyId::anyPolicy, stapledOCSPResponse);
+ }
+ break;
+ }
+
+ case certificateUsageEmailRecipient: {
+ // TODO: The higher level S/MIME processing should pass in which key
+ // usage it is trying to verify for, and base its algorithm choices
+ // based on the result of the verification(s).
+ NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
+ mOCSPCache, pinArg, ocspGETConfig,
+ mCertShortLifetimeInDays,
+ pinningDisabled, MIN_RSA_BITS_WEAK,
+ ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed,
+ NetscapeStepUpPolicy::NeverMatch,
+ originAttributes, builtChain, nullptr,
+ nullptr);
+ rv = BuildCertChain(trustDomain, certDER, time,
+ EndEntityOrCA::MustBeEndEntity,
+ KeyUsage::keyEncipherment, // RSA
+ KeyPurposeId::id_kp_emailProtection,
+ CertPolicyId::anyPolicy, stapledOCSPResponse);
+ if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
+ rv = BuildCertChain(trustDomain, certDER, time,
+ EndEntityOrCA::MustBeEndEntity,
+ KeyUsage::keyAgreement, // ECDH/DH
+ KeyPurposeId::id_kp_emailProtection,
+ CertPolicyId::anyPolicy, stapledOCSPResponse);
+ }
+ break;
+ }
+
+ case certificateUsageObjectSigner: {
+ NSSCertDBTrustDomain trustDomain(trustObjectSigning, defaultOCSPFetching,
+ mOCSPCache, pinArg, ocspGETConfig,
+ mCertShortLifetimeInDays,
+ pinningDisabled, MIN_RSA_BITS_WEAK,
+ ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed,
+ NetscapeStepUpPolicy::NeverMatch,
+ originAttributes, builtChain, nullptr,
+ nullptr);
+ rv = BuildCertChain(trustDomain, certDER, time,
+ EndEntityOrCA::MustBeEndEntity,
+ KeyUsage::digitalSignature,
+ KeyPurposeId::id_kp_codeSigning,
+ CertPolicyId::anyPolicy, stapledOCSPResponse);
+ break;
+ }
+
+ case certificateUsageVerifyCA:
+ case certificateUsageStatusResponder: {
+ // XXX This is a pretty useless way to verify a certificate. It is used
+ // by the certificate viewer UI. Because we don't know what trust bit is
+ // interesting, we just try them all.
+ mozilla::pkix::EndEntityOrCA endEntityOrCA;
+ mozilla::pkix::KeyUsage keyUsage;
+ KeyPurposeId eku;
+ if (usage == certificateUsageVerifyCA) {
+ endEntityOrCA = EndEntityOrCA::MustBeCA;
+ keyUsage = KeyUsage::keyCertSign;
+ eku = KeyPurposeId::anyExtendedKeyUsage;
+ } else {
+ endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
+ keyUsage = KeyUsage::digitalSignature;
+ eku = KeyPurposeId::id_kp_OCSPSigning;
+ }
+
+ NSSCertDBTrustDomain sslTrust(trustSSL, defaultOCSPFetching, mOCSPCache,
+ pinArg, ocspGETConfig, mCertShortLifetimeInDays,
+ pinningDisabled, MIN_RSA_BITS_WEAK,
+ ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed,
+ NetscapeStepUpPolicy::NeverMatch,
+ originAttributes, builtChain, nullptr,
+ nullptr);
+ rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA,
+ keyUsage, eku, CertPolicyId::anyPolicy,
+ stapledOCSPResponse);
+ if (rv == Result::ERROR_UNKNOWN_ISSUER) {
+ NSSCertDBTrustDomain emailTrust(trustEmail, defaultOCSPFetching,
+ mOCSPCache, pinArg, ocspGETConfig,
+ mCertShortLifetimeInDays,
+ pinningDisabled, MIN_RSA_BITS_WEAK,
+ ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed,
+ NetscapeStepUpPolicy::NeverMatch,
+ originAttributes, builtChain, nullptr,
+ nullptr);
+ rv = BuildCertChain(emailTrust, certDER, time, endEntityOrCA,
+ keyUsage, eku, CertPolicyId::anyPolicy,
+ stapledOCSPResponse);
+ if (rv == Result::ERROR_UNKNOWN_ISSUER) {
+ NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
+ defaultOCSPFetching, mOCSPCache,
+ pinArg, ocspGETConfig,
+ mCertShortLifetimeInDays,
+ pinningDisabled,
+ MIN_RSA_BITS_WEAK,
+ ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed,
+ NetscapeStepUpPolicy::NeverMatch,
+ originAttributes, builtChain,
+ nullptr, nullptr);
+ rv = BuildCertChain(objectSigningTrust, certDER, time,
+ endEntityOrCA, keyUsage, eku,
+ CertPolicyId::anyPolicy, stapledOCSPResponse);
+ }
+ }
+
+ break;
+ }
+
+ default:
+ rv = Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ if (rv != Success) {
+ return rv;
+ }
+
+ return Success;
+}
+
+Result
+CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert,
+ /*optional*/ const SECItem* stapledOCSPResponse,
+ /*optional*/ const SECItem* sctsFromTLS,
+ Time time,
+ /*optional*/ void* pinarg,
+ const char* hostname,
+ /*out*/ UniqueCERTCertList& builtChain,
+ /*optional*/ bool saveIntermediatesInPermanentDatabase,
+ /*optional*/ Flags flags,
+ /*optional*/ const NeckoOriginAttributes& originAttributes,
+ /*optional out*/ SECOidTag* evOidPolicy,
+ /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
+ /*optional out*/ KeySizeStatus* keySizeStatus,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult,
+ /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo)
+{
+ PR_ASSERT(peerCert);
+ // XXX: PR_ASSERT(pinarg)
+ PR_ASSERT(hostname);
+ PR_ASSERT(hostname[0]);
+
+ if (evOidPolicy) {
+ *evOidPolicy = SEC_OID_UNKNOWN;
+ }
+
+ if (!hostname || !hostname[0]) {
+ return Result::ERROR_BAD_CERT_DOMAIN;
+ }
+
+ // CreateCertErrorRunnable assumes that CheckCertHostname is only called
+ // if VerifyCert succeeded.
+ Result rv = VerifyCert(peerCert.get(), certificateUsageSSLServer, time,
+ pinarg, hostname, builtChain, flags,
+ stapledOCSPResponse, sctsFromTLS, originAttributes,
+ evOidPolicy, ocspStaplingStatus, keySizeStatus,
+ sha1ModeResult, pinningTelemetryInfo, ctInfo);
+ if (rv != Success) {
+ return rv;
+ }
+
+ Input peerCertInput;
+ rv = peerCertInput.Init(peerCert->derCert.data, peerCert->derCert.len);
+ if (rv != Success) {
+ return rv;
+ }
+
+ Input stapledOCSPResponseInput;
+ Input* responseInputPtr = nullptr;
+ if (stapledOCSPResponse) {
+ rv = stapledOCSPResponseInput.Init(stapledOCSPResponse->data,
+ stapledOCSPResponse->len);
+ if (rv != Success) {
+ // The stapled OCSP response was too big.
+ return Result::ERROR_OCSP_MALFORMED_RESPONSE;
+ }
+ responseInputPtr = &stapledOCSPResponseInput;
+ }
+
+ if (!(flags & FLAG_TLS_IGNORE_STATUS_REQUEST)) {
+ rv = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+
+ Input hostnameInput;
+ rv = hostnameInput.Init(BitwiseCast<const uint8_t*, const char*>(hostname),
+ strlen(hostname));
+ if (rv != Success) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ bool isBuiltInRoot;
+ rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
+ if (rv != Success) {
+ return rv;
+ }
+ BRNameMatchingPolicy nameMatchingPolicy(
+ isBuiltInRoot ? mNameMatchingMode
+ : BRNameMatchingPolicy::Mode::DoNotEnforce);
+ rv = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy);
+ if (rv != Success) {
+ // Treat malformed name information as a domain mismatch.
+ if (rv == Result::ERROR_BAD_DER) {
+ return Result::ERROR_BAD_CERT_DOMAIN;
+ }
+
+ return rv;
+ }
+
+ if (saveIntermediatesInPermanentDatabase) {
+ SaveIntermediateCerts(builtChain);
+ }
+
+ return Success;
+}
+
+} } // namespace mozilla::psm
diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h
new file mode 100644
index 000000000..d88c3f33c
--- /dev/null
+++ b/security/certverifier/CertVerifier.h
@@ -0,0 +1,242 @@
+/* -*- 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 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/. */
+
+#ifndef CertVerifier_h
+#define CertVerifier_h
+
+#include "BRNameMatchingPolicy.h"
+#include "CTVerifyResult.h"
+#include "OCSPCache.h"
+#include "ScopedNSSTypes.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/UniquePtr.h"
+#include "pkix/pkixtypes.h"
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+// Silence "RootingAPI.h(718): warning C4324: 'js::DispatchWrapper<T>':
+// structure was padded due to alignment specifier with [ T=void * ]"
+#pragma warning(disable:4324)
+// Silence "Value.h(448): warning C4365: 'return': conversion from 'const
+// int32_t' to 'JS::Value::PayloadType', signed/unsigned mismatch"
+#pragma warning(disable:4365)
+// Silence "warning C5031: #pragma warning(pop): likely mismatch, popping
+// warning state pushed in different file
+#pragma warning(disable:5031)
+#endif /* defined(_MSC_VER) */
+#include "mozilla/BasePrincipal.h"
+#if defined(_MSC_VER)
+#pragma warning(pop) /* popping the pragma in Vector.h */
+#pragma warning(pop) /* popping the pragma in this file */
+#endif /* defined(_MSC_VER) */
+
+namespace mozilla { namespace ct {
+
+// Including MultiLogCTVerifier.h would bring along all of its dependent
+// headers and force us to export them in moz.build. Just forward-declare
+// the class here instead.
+class MultiLogCTVerifier;
+
+} } // namespace mozilla::ct
+
+namespace mozilla { namespace psm {
+
+typedef mozilla::pkix::Result Result;
+
+// These values correspond to the CERT_CHAIN_KEY_SIZE_STATUS telemetry.
+enum class KeySizeStatus {
+ NeverChecked = 0,
+ LargeMinimumSucceeded = 1,
+ CompatibilityRisk = 2,
+ AlreadyBad = 3,
+};
+
+// These values correspond to the CERT_CHAIN_SHA1_POLICY_STATUS telemetry.
+enum class SHA1ModeResult {
+ NeverChecked = 0,
+ SucceededWithoutSHA1 = 1,
+ SucceededWithImportedRoot = 2,
+ SucceededWithImportedRootOrSHA1Before2016 = 3,
+ SucceededWithSHA1 = 4,
+ Failed = 5,
+};
+
+enum class NetscapeStepUpPolicy : uint32_t;
+
+class PinningTelemetryInfo
+{
+public:
+ PinningTelemetryInfo() { Reset(); }
+
+ // Should we accumulate pinning telemetry for the result?
+ bool accumulateResult;
+ Telemetry::ID certPinningResultHistogram;
+ int32_t certPinningResultBucket;
+ // Should we accumulate telemetry for the root?
+ bool accumulateForRoot;
+ int32_t rootBucket;
+
+ void Reset() { accumulateForRoot = false; accumulateResult = false; }
+};
+
+class CertificateTransparencyInfo
+{
+public:
+ CertificateTransparencyInfo() { Reset(); }
+
+ // Was CT enabled?
+ bool enabled;
+ // Did we receive and process any binary SCT data from the supported sources?
+ bool processedSCTs;
+ // Verification result of the processed SCTs.
+ mozilla::ct::CTVerifyResult verifyResult;
+
+ void Reset() { enabled = false; processedSCTs = false; verifyResult.Reset(); }
+};
+
+class NSSCertDBTrustDomain;
+
+class CertVerifier
+{
+public:
+ typedef unsigned int Flags;
+ // XXX: FLAG_LOCAL_ONLY is ignored in the classic verification case
+ static const Flags FLAG_LOCAL_ONLY;
+ // Don't perform fallback DV validation on EV validation failure.
+ static const Flags FLAG_MUST_BE_EV;
+ // TLS feature request_status should be ignored
+ static const Flags FLAG_TLS_IGNORE_STATUS_REQUEST;
+
+ // These values correspond to the SSL_OCSP_STAPLING telemetry.
+ enum OCSPStaplingStatus {
+ OCSP_STAPLING_NEVER_CHECKED = 0,
+ OCSP_STAPLING_GOOD = 1,
+ OCSP_STAPLING_NONE = 2,
+ OCSP_STAPLING_EXPIRED = 3,
+ OCSP_STAPLING_INVALID = 4,
+ };
+
+ // *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV
+ // Only one usage per verification is supported.
+ mozilla::pkix::Result VerifyCert(
+ CERTCertificate* cert,
+ SECCertificateUsage usage,
+ mozilla::pkix::Time time,
+ void* pinArg,
+ const char* hostname,
+ /*out*/ UniqueCERTCertList& builtChain,
+ Flags flags = 0,
+ /*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
+ /*optional in*/ const SECItem* sctsFromTLS = nullptr,
+ /*optional in*/ const NeckoOriginAttributes& originAttributes =
+ NeckoOriginAttributes(),
+ /*optional out*/ SECOidTag* evOidPolicy = nullptr,
+ /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
+ /*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult = nullptr,
+ /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr);
+
+ mozilla::pkix::Result VerifySSLServerCert(
+ const UniqueCERTCertificate& peerCert,
+ /*optional*/ const SECItem* stapledOCSPResponse,
+ /*optional*/ const SECItem* sctsFromTLS,
+ mozilla::pkix::Time time,
+ /*optional*/ void* pinarg,
+ const char* hostname,
+ /*out*/ UniqueCERTCertList& builtChain,
+ /*optional*/ bool saveIntermediatesInPermanentDatabase = false,
+ /*optional*/ Flags flags = 0,
+ /*optional*/ const NeckoOriginAttributes& originAttributes =
+ NeckoOriginAttributes(),
+ /*optional out*/ SECOidTag* evOidPolicy = nullptr,
+ /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
+ /*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult = nullptr,
+ /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr);
+
+ enum PinningMode {
+ pinningDisabled = 0,
+ pinningAllowUserCAMITM = 1,
+ pinningStrict = 2,
+ pinningEnforceTestMode = 3
+ };
+
+ enum class SHA1Mode {
+ Allowed = 0,
+ Forbidden = 1,
+ // There used to be a policy that only allowed SHA1 for certificates issued
+ // before 2016. This is no longer available. If a user has selected this
+ // policy in about:config, it now maps to Forbidden.
+ UsedToBeBefore2016ButNowIsForbidden = 2,
+ ImportedRoot = 3,
+ ImportedRootOrBefore2016 = 4,
+ };
+
+ enum OcspDownloadConfig {
+ ocspOff = 0,
+ ocspOn = 1,
+ ocspEVOnly = 2
+ };
+ enum OcspStrictConfig { ocspRelaxed = 0, ocspStrict };
+ enum OcspGetConfig { ocspGetDisabled = 0, ocspGetEnabled = 1 };
+
+ enum class CertificateTransparencyMode {
+ Disabled = 0,
+ TelemetryOnly = 1,
+ };
+
+ CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
+ OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
+ PinningMode pinningMode, SHA1Mode sha1Mode,
+ BRNameMatchingPolicy::Mode nameMatchingMode,
+ NetscapeStepUpPolicy netscapeStepUpPolicy,
+ CertificateTransparencyMode ctMode);
+ ~CertVerifier();
+
+ void ClearOCSPCache() { mOCSPCache.Clear(); }
+
+ const OcspDownloadConfig mOCSPDownloadConfig;
+ const bool mOCSPStrict;
+ const bool mOCSPGETEnabled;
+ const uint32_t mCertShortLifetimeInDays;
+ const PinningMode mPinningMode;
+ const SHA1Mode mSHA1Mode;
+ const BRNameMatchingPolicy::Mode mNameMatchingMode;
+ const NetscapeStepUpPolicy mNetscapeStepUpPolicy;
+ const CertificateTransparencyMode mCTMode;
+
+private:
+ OCSPCache mOCSPCache;
+
+ // We only have a forward declaration of MultiLogCTVerifier (see above),
+ // so we keep a pointer to it and allocate dynamically.
+ UniquePtr<mozilla::ct::MultiLogCTVerifier> mCTVerifier;
+
+ void LoadKnownCTLogs();
+ mozilla::pkix::Result VerifySignedCertificateTimestamps(
+ NSSCertDBTrustDomain& trustDomain,
+ const UniqueCERTCertList& builtChain,
+ mozilla::pkix::Input sctsFromTLS,
+ mozilla::pkix::Time time,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo);
+
+ // Returns true if the configured SHA1 mode is more restrictive than the given
+ // mode. SHA1Mode::Forbidden is more restrictive than any other mode except
+ // Forbidden. Next is ImportedRoot, then ImportedRootOrBefore2016, then
+ // Allowed. (A mode is never more restrictive than itself.)
+ bool SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode);
+};
+
+mozilla::pkix::Result IsCertBuiltInRoot(CERTCertificate* cert, bool& result);
+mozilla::pkix::Result CertListContainsExpectedKeys(
+ const CERTCertList* certList, const char* hostname, mozilla::pkix::Time time,
+ CertVerifier::PinningMode pinningMode);
+
+} } // namespace mozilla::psm
+
+#endif // CertVerifier_h
diff --git a/security/certverifier/ExtendedValidation.cpp b/security/certverifier/ExtendedValidation.cpp
new file mode 100644
index 000000000..8ac5828de
--- /dev/null
+++ b/security/certverifier/ExtendedValidation.cpp
@@ -0,0 +1,1389 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ExtendedValidation.h"
+
+#include "base64.h"
+#include "cert.h"
+#include "certdb.h"
+#include "hasht.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Casting.h"
+#include "mozilla/PodOperations.h"
+#include "pk11pub.h"
+#include "pkix/pkixtypes.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "secerr.h"
+
+extern mozilla::LazyLogModule gPIPNSSLog;
+
+#define CONST_OID static const unsigned char
+#define OI(x) { siDEROID, (unsigned char*) x, sizeof x }
+
+struct nsMyTrustedEVInfo
+{
+ const char* dotted_oid;
+ const char* oid_name; // Set this to null to signal an invalid structure,
+ // (We can't have an empty list, so we'll use a dummy entry)
+ SECOidTag oid_tag;
+ const unsigned char ev_root_sha256_fingerprint[SHA256_LENGTH];
+ const char* issuer_base64;
+ const char* serial_base64;
+};
+
+// HOWTO enable additional CA root certificates for EV:
+//
+// For each combination of "root certificate" and "policy OID",
+// one entry must be added to the array named myTrustedEVInfos.
+//
+// We use the combination of "issuer name" and "serial number" to
+// uniquely identify the certificate. In order to avoid problems
+// because of encodings when comparing certificates, we don't
+// use plain text representation, we rather use the original encoding
+// as it can be found in the root certificate (in base64 format).
+//
+// We can use the NSS utility named "pp" to extract the encoding.
+//
+// Build standalone NSS including the NSS tools, then run
+// pp -t certificate-identity -i the-cert-filename
+//
+// You will need the output from sections "Issuer", "Fingerprint (SHA-256)",
+// "Issuer DER Base64" and "Serial DER Base64".
+//
+// The new section consists of 8 lines:
+//
+// - a comment that should contain the human readable issuer name
+// of the certificate, as printed by the pp tool
+// - the EV policy OID that is associated to the EV grant
+// - a text description of the EV policy OID. The array can contain
+// multiple entries with the same OID.
+// Please make sure to use the identical OID text description for
+// all entries with the same policy OID (use the text search
+// feature of your text editor to find duplicates).
+// When adding a new policy OID that is not yet contained in the array,
+// please make sure that your new description is different from
+// all the other descriptions (again use the text search feature
+// to be sure).
+// - the constant SEC_OID_UNKNOWN
+// (it will be replaced at runtime with another identifier)
+// - the SHA-256 fingerprint
+// - the "Issuer DER Base64" as printed by the pp tool.
+// Remove all whitespaces. If you use multiple lines, make sure that
+// only the final line will be followed by a comma.
+// - the "Serial DER Base64" (as printed by pp)
+//
+// After adding an entry, test it locally against the test site that
+// has been provided by the CA. Note that you must use a version of NSS
+// where the root certificate has already been added and marked as trusted
+// for issuing SSL server certificates (at least).
+//
+// If you are able to connect to the site without certificate errors,
+// but you don't see the EV status indicator, then most likely the CA
+// has a problem in their infrastructure. The most common problems are
+// related to the CA's OCSP infrastructure, either they use an incorrect
+// OCSP signing certificate, or OCSP for the intermediate certificates
+// isn't working, or OCSP isn't working at all.
+
+#ifdef DEBUG
+static const size_t NUM_TEST_EV_ROOTS = 2;
+#endif
+
+static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
+ // IMPORTANT! When extending this list, if you add another entry that uses
+ // the same dotted_oid as an existing entry, use the same oid_name.
+#ifdef DEBUG
+ // Debug EV certificates should all use the following OID:
+ // 1.3.6.1.4.1.13769.666.666.666.1.500.9.1.
+ // (multiple entries with the same OID is ok)
+ // If you add or remove debug EV certs you must also modify NUM_TEST_EV_ROOTS
+ // so that the correct number of certs are skipped as these debug EV certs are
+ // NOT part of the default trust store.
+ {
+ // This is the PSM xpcshell testing EV certificate. It can be generated
+ // using pycert.py and the following specification:
+ //
+ // issuer:evroot
+ // subject:evroot
+ // subjectKey:ev
+ // issuerKey:ev
+ // validity:20150101-20350101
+ // extension:basicConstraints:cA,
+ // extension:keyUsage:keyCertSign,cRLSign
+ //
+ // If this ever needs to change, re-generate the certificate and update the
+ // following entry with the new fingerprint, issuer, and serial number.
+ "1.3.6.1.4.1.13769.666.666.666.1.500.9.1",
+ "DEBUGtesting EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xE4, 0xFB, 0x04, 0x16, 0x10, 0x32, 0x67, 0x08, 0x6C, 0x84, 0x2E,
+ 0x91, 0xF3, 0xEF, 0x0E, 0x45, 0x99, 0xBC, 0xA8, 0x54, 0x73, 0xF5,
+ 0x03, 0x2C, 0x7B, 0xDC, 0x09, 0x70, 0x76, 0x49, 0xBF, 0xAA },
+ "MBExDzANBgNVBAMMBmV2cm9vdA==",
+ "W9j5PS8YoKgynZdYa9i2Kwexnp8=",
+ },
+ {
+ // This is an RSA root with an inadequate key size. It is used to test that
+ // minimum key sizes are enforced when verifying for EV. It can be
+ // generated using pycert.py and the following specification:
+ //
+ // issuer:ev_root_rsa_2040
+ // subject:ev_root_rsa_2040
+ // issuerKey:evRSA2040
+ // subjectKey:evRSA2040
+ // validity:20150101-20350101
+ // extension:basicConstraints:cA,
+ // extension:keyUsage:cRLSign,keyCertSign
+ //
+ // If this ever needs to change, re-generate the certificate and update the
+ // following entry with the new fingerprint, issuer, and serial number.
+ "1.3.6.1.4.1.13769.666.666.666.1.500.9.1",
+ "DEBUGtesting EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x49, 0x46, 0x10, 0xF4, 0xF5, 0xB1, 0x96, 0xE7, 0xFB, 0xFA, 0x4D,
+ 0xA6, 0x34, 0x03, 0xD0, 0x99, 0x22, 0xD4, 0x77, 0x20, 0x3F, 0x84,
+ 0xE0, 0xDF, 0x1C, 0xAD, 0xB4, 0xC2, 0x76, 0xBB, 0x63, 0x24 },
+ "MBsxGTAXBgNVBAMMEGV2X3Jvb3RfcnNhXzIwNDA=",
+ "P1iIBgxk6kH+x64EUBTV3qoHuas=",
+ },
+#endif
+ {
+ // OU=Security Communication EV RootCA1,O="SECOM Trust Systems CO.,LTD.",C=JP
+ "1.2.392.200091.100.721.1",
+ "SECOM EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xA2, 0x2D, 0xBA, 0x68, 0x1E, 0x97, 0x37, 0x6E, 0x2D, 0x39, 0x7D,
+ 0x72, 0x8A, 0xAE, 0x3A, 0x9B, 0x62, 0x96, 0xB9, 0xFD, 0xBA, 0x60,
+ 0xBC, 0x2E, 0x11, 0xF6, 0x47, 0xF2, 0xC6, 0x75, 0xFB, 0x37 },
+ "MGAxCzAJBgNVBAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENP"
+ "LixMVEQuMSowKAYDVQQLEyFTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVWIFJvb3RD"
+ "QTE=",
+ "AA==",
+ },
+ {
+ // CN=Cybertrust Global Root,O=Cybertrust, Inc
+ "1.3.6.1.4.1.6334.1.100.1",
+ "Cybertrust EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x96, 0x0A, 0xDF, 0x00, 0x63, 0xE9, 0x63, 0x56, 0x75, 0x0C, 0x29,
+ 0x65, 0xDD, 0x0A, 0x08, 0x67, 0xDA, 0x0B, 0x9C, 0xBD, 0x6E, 0x77,
+ 0x71, 0x4A, 0xEA, 0xFB, 0x23, 0x49, 0xAB, 0x39, 0x3D, 0xA3 },
+ "MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVz"
+ "dCBHbG9iYWwgUm9vdA==",
+ "BAAAAAABD4WqLUg=",
+ },
+ {
+ // CN=SwissSign Gold CA - G2,O=SwissSign AG,C=CH
+ "2.16.756.1.89.1.2.1.1",
+ "SwissSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x62, 0xDD, 0x0B, 0xE9, 0xB9, 0xF5, 0x0A, 0x16, 0x3E, 0xA0, 0xF8,
+ 0xE7, 0x5C, 0x05, 0x3B, 0x1E, 0xCA, 0x57, 0xEA, 0x55, 0xC8, 0x68,
+ 0x8F, 0x64, 0x7C, 0x68, 0x81, 0xF2, 0xC8, 0x35, 0x7B, 0x95 },
+ "MEUxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMT"
+ "FlN3aXNzU2lnbiBHb2xkIENBIC0gRzI=",
+ "ALtAHEP1Xk+w",
+ },
+ {
+ // CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
+ "1.3.6.1.4.1.23223.1.1.1",
+ "StartCom EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xC7, 0x66, 0xA9, 0xBE, 0xF2, 0xD4, 0x07, 0x1C, 0x86, 0x3A, 0x31,
+ 0xAA, 0x49, 0x20, 0xE8, 0x13, 0xB2, 0xD1, 0x98, 0x60, 0x8C, 0xB7,
+ 0xB7, 0xCF, 0xE2, 0x11, 0x43, 0xB8, 0x36, 0xDF, 0x09, 0xEA },
+ "MH0xCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSswKQYDVQQL"
+ "EyJTZWN1cmUgRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYDVQQDEyBT"
+ "dGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+ "AQ==",
+ },
+ {
+ // CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
+ "1.3.6.1.4.1.23223.1.1.1",
+ "StartCom EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xE1, 0x78, 0x90, 0xEE, 0x09, 0xA3, 0xFB, 0xF4, 0xF4, 0x8B, 0x9C,
+ 0x41, 0x4A, 0x17, 0xD6, 0x37, 0xB7, 0xA5, 0x06, 0x47, 0xE9, 0xBC,
+ 0x75, 0x23, 0x22, 0x72, 0x7F, 0xCC, 0x17, 0x42, 0xA9, 0x11 },
+ "MH0xCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSswKQYDVQQL"
+ "EyJTZWN1cmUgRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYDVQQDEyBT"
+ "dGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+ "LQ==",
+ },
+ {
+ // CN=StartCom Certification Authority G2,O=StartCom Ltd.,C=IL
+ "1.3.6.1.4.1.23223.1.1.1",
+ "StartCom EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xC7, 0xBA, 0x65, 0x67, 0xDE, 0x93, 0xA7, 0x98, 0xAE, 0x1F, 0xAA,
+ 0x79, 0x1E, 0x71, 0x2D, 0x37, 0x8F, 0xAE, 0x1F, 0x93, 0xC4, 0x39,
+ 0x7F, 0xEA, 0x44, 0x1B, 0xB7, 0xCB, 0xE6, 0xFD, 0x59, 0x95 },
+ "MFMxCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSwwKgYDVQQD"
+ "EyNTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBHMg==",
+ "Ow==",
+ },
+ {
+ // CN=VeriSign Class 3 Public Primary Certification Authority - G5,OU="(c) 2006 VeriSign, Inc. - For authorized use only",OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
+ "2.16.840.1.113733.1.7.23.6",
+ "VeriSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x9A, 0xCF, 0xAB, 0x7E, 0x43, 0xC8, 0xD8, 0x80, 0xD0, 0x6B, 0x26,
+ 0x2A, 0x94, 0xDE, 0xEE, 0xE4, 0xB4, 0x65, 0x99, 0x89, 0xC3, 0xD0,
+ 0xCA, 0xF1, 0x9B, 0xAF, 0x64, 0x05, 0xE4, 0x1A, 0xB7, 0xDF },
+ "MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV"
+ "BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA2IFZl"
+ "cmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMT"
+ "PFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB"
+ "dXRob3JpdHkgLSBHNQ==",
+ "GNrRniZ96LtKIVjNzGs7Sg==",
+ },
+ {
+ // CN=GeoTrust Primary Certification Authority,O=GeoTrust Inc.,C=US
+ "1.3.6.1.4.1.14370.1.6",
+ "GeoTrust EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x37, 0xD5, 0x10, 0x06, 0xC5, 0x12, 0xEA, 0xAB, 0x62, 0x64, 0x21,
+ 0xF1, 0xEC, 0x8C, 0x92, 0x01, 0x3F, 0xC5, 0xF8, 0x2A, 0xE9, 0x8E,
+ 0xE5, 0x33, 0xEB, 0x46, 0x19, 0xB8, 0xDE, 0xB4, 0xD0, 0x6C },
+ "MFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQD"
+ "EyhHZW9UcnVzdCBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5",
+ "GKy1av1pthU6Y2yv2vrEoQ==",
+ },
+ {
+ // CN=thawte Primary Root CA,OU="(c) 2006 thawte, Inc. - For authorized use only",OU=Certification Services Division,O="thawte, Inc.",C=US
+ "2.16.840.1.113733.1.7.48.1",
+ "Thawte EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x8D, 0x72, 0x2F, 0x81, 0xA9, 0xC1, 0x13, 0xC0, 0x79, 0x1D, 0xF1,
+ 0x36, 0xA2, 0x96, 0x6D, 0xB2, 0x6C, 0x95, 0x0A, 0x97, 0x1D, 0xB4,
+ 0x6B, 0x41, 0x99, 0xF4, 0xEA, 0x54, 0xB7, 0x8B, 0xFB, 0x9F },
+ "MIGpMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMuMSgwJgYDVQQL"
+ "Ex9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYDVQQLEy8oYykg"
+ "MjAwNiB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0G"
+ "A1UEAxMWdGhhd3RlIFByaW1hcnkgUm9vdCBDQQ==",
+ "NE7VVyDV7exJ9C/ON9srbQ==",
+ },
+ {
+ // CN=XRamp Global Certification Authority,O=XRamp Security Services Inc,OU=www.xrampsecurity.com,C=US
+ "2.16.840.1.114404.1.1.2.4.1",
+ "Trustwave EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xCE, 0xCD, 0xDC, 0x90, 0x50, 0x99, 0xD8, 0xDA, 0xDF, 0xC5, 0xB1,
+ 0xD2, 0x09, 0xB7, 0x37, 0xCB, 0xE2, 0xC1, 0x8C, 0xFB, 0x2C, 0x10,
+ 0xC0, 0xFF, 0x0B, 0xCF, 0x0D, 0x32, 0x86, 0xFC, 0x1A, 0xA2 },
+ "MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29t"
+ "MSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMT"
+ "JFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+ "UJRs7Bjq1ZxN1ZfvdY+grQ==",
+ },
+ {
+ // CN=SecureTrust CA,O=SecureTrust Corporation,C=US
+ "2.16.840.1.114404.1.1.2.4.1",
+ "Trustwave EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xF1, 0xC1, 0xB5, 0x0A, 0xE5, 0xA2, 0x0D, 0xD8, 0x03, 0x0E, 0xC9,
+ 0xF6, 0xBC, 0x24, 0x82, 0x3D, 0xD3, 0x67, 0xB5, 0x25, 0x57, 0x59,
+ 0xB4, 0xE7, 0x1B, 0x61, 0xFC, 0xE9, 0xF7, 0x37, 0x5D, 0x73 },
+ "MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlv"
+ "bjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=",
+ "DPCOXAgWpa1Cf/DrJxhZ0A==",
+ },
+ {
+ // CN=Secure Global CA,O=SecureTrust Corporation,C=US
+ "2.16.840.1.114404.1.1.2.4.1",
+ "Trustwave EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x42, 0x00, 0xF5, 0x04, 0x3A, 0xC8, 0x59, 0x0E, 0xBB, 0x52, 0x7D,
+ 0x20, 0x9E, 0xD1, 0x50, 0x30, 0x29, 0xFB, 0xCB, 0xD4, 0x1C, 0xA1,
+ 0xB5, 0x06, 0xEC, 0x27, 0xF1, 0x5A, 0xDE, 0x7D, 0xAC, 0x69 },
+ "MEoxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlv"
+ "bjEZMBcGA1UEAxMQU2VjdXJlIEdsb2JhbCBDQQ==",
+ "B1YipOjUiolN9BPI8PjqpQ==",
+ },
+ {
+ // CN=COMODO ECC Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
+ "1.3.6.1.4.1.6449.1.2.1.5.1",
+ "Comodo EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x17, 0x93, 0x92, 0x7A, 0x06, 0x14, 0x54, 0x97, 0x89, 0xAD, 0xCE,
+ 0x2F, 0x8F, 0x34, 0xF7, 0xF0, 0xB6, 0x6D, 0x0F, 0x3A, 0xE3, 0xA3,
+ 0xB8, 0x4D, 0x21, 0xEC, 0x15, 0xDB, 0xBA, 0x4F, 0xAD, 0xC7 },
+ "MIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAw"
+ "DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDErMCkG"
+ "A1UEAxMiQ09NT0RPIEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+ "H0evqmIAcFBUTAGem2OZKg==",
+ },
+ {
+ // CN=COMODO Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
+ "1.3.6.1.4.1.6449.1.2.1.5.1",
+ "Comodo EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x0C, 0x2C, 0xD6, 0x3D, 0xF7, 0x80, 0x6F, 0xA3, 0x99, 0xED, 0xE8,
+ 0x09, 0x11, 0x6B, 0x57, 0x5B, 0xF8, 0x79, 0x89, 0xF0, 0x65, 0x18,
+ 0xF9, 0x80, 0x8C, 0x86, 0x05, 0x03, 0x17, 0x8B, 0xAF, 0x66 },
+ "MIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAw"
+ "DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEnMCUG"
+ "A1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5",
+ "ToEtioJl4AsC7j41AkblPQ==",
+ },
+ {
+ // CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE
+ "1.3.6.1.4.1.6449.1.2.1.5.1",
+ "Comodo EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x68, 0x7F, 0xA4, 0x51, 0x38, 0x22, 0x78, 0xFF, 0xF0, 0xC8, 0xB1,
+ 0x1F, 0x8D, 0x43, 0xD5, 0x76, 0x67, 0x1C, 0x6E, 0xB2, 0xBC, 0xEA,
+ 0xB4, 0x13, 0xFB, 0x83, 0xD9, 0x65, 0xD0, 0x6D, 0x2F, 0xF2 },
+ "MG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMd"
+ "QWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0"
+ "IEV4dGVybmFsIENBIFJvb3Q=",
+ "AQ==",
+ },
+ {
+ // CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
+ "1.3.6.1.4.1.6449.1.2.1.5.1",
+ "Comodo EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x6E, 0xA5, 0x47, 0x41, 0xD0, 0x04, 0x66, 0x7E, 0xED, 0x1B, 0x48,
+ 0x16, 0x63, 0x4A, 0xA3, 0xA7, 0x9E, 0x6E, 0x4B, 0x96, 0x95, 0x0F,
+ 0x82, 0x79, 0xDA, 0xFC, 0x8D, 0x9B, 0xD8, 0x81, 0x21, 0x37 },
+ "MIGXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFr"
+ "ZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsT"
+ "GGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEfMB0GA1UEAxMWVVROLVVTRVJGaXJz"
+ "dC1IYXJkd2FyZQ==",
+ "RL4Mi1AAJLQR0zYq/mUK/Q==",
+ },
+ {
+ // OU=Go Daddy Class 2 Certification Authority,O=\"The Go Daddy Group, Inc.\",C=US
+ "2.16.840.1.114413.1.7.23.3",
+ "Go Daddy EV OID a",
+ SEC_OID_UNKNOWN,
+ { 0xC3, 0x84, 0x6B, 0xF2, 0x4B, 0x9E, 0x93, 0xCA, 0x64, 0x27, 0x4C,
+ 0x0E, 0xC6, 0x7C, 0x1E, 0xCC, 0x5E, 0x02, 0x4F, 0xFC, 0xAC, 0xD2,
+ 0xD7, 0x40, 0x19, 0x35, 0x0E, 0x81, 0xFE, 0x54, 0x6A, 0xE4 },
+ "MGMxCzAJBgNVBAYTAlVTMSEwHwYDVQQKExhUaGUgR28gRGFkZHkgR3JvdXAsIElu"
+ "Yy4xMTAvBgNVBAsTKEdvIERhZGR5IENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRo"
+ "b3JpdHk=",
+ "AA==",
+ },
+ {
+ // CN=Go Daddy Root Certificate Authority - G2,O="GoDaddy.com, Inc.",L=Scottsdale,ST=Arizona,C=US
+ "2.16.840.1.114413.1.7.23.3",
+ "Go Daddy EV OID a",
+ SEC_OID_UNKNOWN,
+ { 0x45, 0x14, 0x0B, 0x32, 0x47, 0xEB, 0x9C, 0xC8, 0xC5, 0xB4, 0xF0,
+ 0xD7, 0xB5, 0x30, 0x91, 0xF7, 0x32, 0x92, 0x08, 0x9E, 0x6E, 0x5A,
+ 0x63, 0xE2, 0x74, 0x9D, 0xD3, 0xAC, 0xA9, 0x19, 0x8E, 0xDA },
+ "MIGDMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2Nv"
+ "dHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xMTAvBgNVBAMTKEdv"
+ "IERhZGR5IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzI=",
+ "AA==",
+ },
+ {
+ // OU=Starfield Class 2 Certification Authority,O=\"Starfield Technologies, Inc.\",C=US
+ "2.16.840.1.114414.1.7.23.3",
+ "Go Daddy EV OID b",
+ SEC_OID_UNKNOWN,
+ { 0x14, 0x65, 0xFA, 0x20, 0x53, 0x97, 0xB8, 0x76, 0xFA, 0xA6, 0xF0,
+ 0xA9, 0x95, 0x8E, 0x55, 0x90, 0xE4, 0x0F, 0xCC, 0x7F, 0xAA, 0x4F,
+ 0xB7, 0xC2, 0xC8, 0x67, 0x75, 0x21, 0xFB, 0x5F, 0xB6, 0x58 },
+ "MGgxCzAJBgNVBAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVz"
+ "LCBJbmMuMTIwMAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9u"
+ "IEF1dGhvcml0eQ==",
+ "AA==",
+ },
+ {
+ // CN=Starfield Root Certificate Authority - G2,O="Starfield Technologies, Inc.",L=Scottsdale,ST=Arizona,C=US
+ "2.16.840.1.114414.1.7.23.3",
+ "Go Daddy EV OID b",
+ SEC_OID_UNKNOWN,
+ { 0x2C, 0xE1, 0xCB, 0x0B, 0xF9, 0xD2, 0xF9, 0xE1, 0x02, 0x99, 0x3F,
+ 0xBE, 0x21, 0x51, 0x52, 0xC3, 0xB2, 0xDD, 0x0C, 0xAB, 0xDE, 0x1C,
+ 0x68, 0xE5, 0x31, 0x9B, 0x83, 0x91, 0x54, 0xDB, 0xB7, 0xF5 },
+ "MIGPMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2Nv"
+ "dHRzZGFsZTElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEy"
+ "MDAGA1UEAxMpU3RhcmZpZWxkIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0g"
+ "RzI=",
+ "AA==",
+ },
+ {
+ // CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
+ "2.16.840.1.114412.2.1",
+ "DigiCert EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x74, 0x31, 0xE5, 0xF4, 0xC3, 0xC1, 0xCE, 0x46, 0x90, 0x77, 0x4F,
+ 0x0B, 0x61, 0xE0, 0x54, 0x40, 0x88, 0x3B, 0xA9, 0xA0, 0x1E, 0xD0,
+ 0x0B, 0xA6, 0xAB, 0xD7, 0x80, 0x6E, 0xD3, 0xB1, 0x18, 0xCF },
+ "MGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT"
+ "EHd3dy5kaWdpY2VydC5jb20xKzApBgNVBAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJh"
+ "bmNlIEVWIFJvb3QgQ0E=",
+ "AqxcJmoLQJuPC3nyrkYldw==",
+ },
+ {
+ // CN=QuoVadis Root CA 2,O=QuoVadis Limited,C=BM
+ "1.3.6.1.4.1.8024.0.2.100.1.2",
+ "Quo Vadis EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x85, 0xA0, 0xDD, 0x7D, 0xD7, 0x20, 0xAD, 0xB7, 0xFF, 0x05, 0xF8,
+ 0x3D, 0x54, 0x2B, 0x20, 0x9D, 0xC7, 0xFF, 0x45, 0x28, 0xF7, 0xD6,
+ 0x77, 0xB1, 0x83, 0x89, 0xFE, 0xA5, 0xE5, 0xC4, 0x9E, 0x86 },
+ "MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYD"
+ "VQQDExJRdW9WYWRpcyBSb290IENBIDI=",
+ "BQk=",
+ },
+ {
+ // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
+ "1.3.6.1.4.1.782.1.2.1.8.1",
+ "Network Solutions EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x15, 0xF0, 0xBA, 0x00, 0xA3, 0xAC, 0x7A, 0xF3, 0xAC, 0x88, 0x4C,
+ 0x07, 0x2B, 0x10, 0x11, 0xA0, 0x77, 0xBD, 0x77, 0xC0, 0x97, 0xF4,
+ 0x01, 0x64, 0xB2, 0xF8, 0x59, 0x8A, 0xBD, 0x83, 0x86, 0x0C },
+ "MGIxCzAJBgNVBAYTAlVTMSEwHwYDVQQKExhOZXR3b3JrIFNvbHV0aW9ucyBMLkwu"
+ "Qy4xMDAuBgNVBAMTJ05ldHdvcmsgU29sdXRpb25zIENlcnRpZmljYXRlIEF1dGhv"
+ "cml0eQ==",
+ "V8szb8JcFuZHFhfjkDFo4A==",
+ },
+ {
+ // CN=Entrust Root Certification Authority,OU="(c) 2006 Entrust, Inc.",OU=www.entrust.net/CPS is incorporated by reference,O="Entrust, Inc.",C=US
+ "2.16.840.1.114028.10.1.2",
+ "Entrust EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x73, 0xC1, 0x76, 0x43, 0x4F, 0x1B, 0xC6, 0xD5, 0xAD, 0xF4, 0x5B,
+ 0x0E, 0x76, 0xE7, 0x27, 0x28, 0x7C, 0x8D, 0xE5, 0x76, 0x16, 0xC1,
+ 0xE6, 0xE6, 0x14, 0x1A, 0x2B, 0x2C, 0xBC, 0x7D, 0x8E, 0x4C },
+ "MIGwMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjE5MDcGA1UE"
+ "CxMwd3d3LmVudHJ1c3QubmV0L0NQUyBpcyBpbmNvcnBvcmF0ZWQgYnkgcmVmZXJl"
+ "bmNlMR8wHQYDVQQLExYoYykgMjAwNiBFbnRydXN0LCBJbmMuMS0wKwYDVQQDEyRF"
+ "bnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=",
+ "RWtQVA==",
+ },
+ {
+ // CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE
+ "1.3.6.1.4.1.4146.1.1",
+ "GlobalSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xEB, 0xD4, 0x10, 0x40, 0xE4, 0xBB, 0x3E, 0xC7, 0x42, 0xC9, 0xE3,
+ 0x81, 0xD3, 0x1E, 0xF2, 0xA4, 0x1A, 0x48, 0xB6, 0x68, 0x5C, 0x96,
+ 0xE7, 0xCE, 0xF3, 0xC1, 0xDF, 0x6C, 0xD4, 0x33, 0x1C, 0x99 },
+ "MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYD"
+ "VQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=",
+ "BAAAAAABFUtaw5Q=",
+ },
+ {
+ // CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R2
+ "1.3.6.1.4.1.4146.1.1",
+ "GlobalSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xCA, 0x42, 0xDD, 0x41, 0x74, 0x5F, 0xD0, 0xB8, 0x1E, 0xB9, 0x02,
+ 0x36, 0x2C, 0xF9, 0xD8, 0xBF, 0x71, 0x9D, 0xA1, 0xBD, 0x1B, 0x1E,
+ 0xFC, 0x94, 0x6F, 0x5B, 0x4C, 0x99, 0xF4, 0x2C, 0x1B, 0x9E },
+ "MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpH"
+ "bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu",
+ "BAAAAAABD4Ym5g0=",
+ },
+ {
+ // CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R3
+ "1.3.6.1.4.1.4146.1.1",
+ "GlobalSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xCB, 0xB5, 0x22, 0xD7, 0xB7, 0xF1, 0x27, 0xAD, 0x6A, 0x01, 0x13,
+ 0x86, 0x5B, 0xDF, 0x1C, 0xD4, 0x10, 0x2E, 0x7D, 0x07, 0x59, 0xAF,
+ 0x63, 0x5A, 0x7C, 0xF4, 0x72, 0x0D, 0xC9, 0x63, 0xC5, 0x3B },
+ "MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpH"
+ "bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu",
+ "BAAAAAABIVhTCKI=",
+ },
+ {
+ // CN=Buypass Class 3 Root CA,O=Buypass AS-983163327,C=NO
+ "2.16.578.1.26.1.3.3",
+ "Buypass EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xED, 0xF7, 0xEB, 0xBC, 0xA2, 0x7A, 0x2A, 0x38, 0x4D, 0x38, 0x7B,
+ 0x7D, 0x40, 0x10, 0xC6, 0x66, 0xE2, 0xED, 0xB4, 0x84, 0x3E, 0x4C,
+ 0x29, 0xB4, 0xAE, 0x1D, 0x5B, 0x93, 0x32, 0xE6, 0xB2, 0x4D },
+ "ME4xCzAJBgNVBAYTAk5PMR0wGwYDVQQKDBRCdXlwYXNzIEFTLTk4MzE2MzMyNzEg"
+ "MB4GA1UEAwwXQnV5cGFzcyBDbGFzcyAzIFJvb3QgQ0E=",
+ "Ag==",
+ },
+ {
+ // CN=Class 2 Primary CA,O=Certplus,C=FR
+ "1.3.6.1.4.1.22234.2.5.2.3.1",
+ "Certplus EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x0F, 0x99, 0x3C, 0x8A, 0xEF, 0x97, 0xBA, 0xAF, 0x56, 0x87, 0x14,
+ 0x0E, 0xD5, 0x9A, 0xD1, 0x82, 0x1B, 0xB4, 0xAF, 0xAC, 0xF0, 0xAA,
+ 0x9A, 0x58, 0xB5, 0xD5, 0x7A, 0x33, 0x8A, 0x3A, 0xFB, 0xCB },
+ "MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xh"
+ "c3MgMiBQcmltYXJ5IENB",
+ "AIW9S/PY2uNp9pTXX8OlRCM=",
+ },
+ {
+ // CN=Chambers of Commerce Root - 2008,O=AC Camerfirma S.A.,serialNumber=A82743287,L=Madrid (see current address at www.camerfirma.com/address),C=EU
+ "1.3.6.1.4.1.17326.10.14.2.1.2",
+ "Camerfirma EV OID a",
+ SEC_OID_UNKNOWN,
+ { 0x06, 0x3E, 0x4A, 0xFA, 0xC4, 0x91, 0xDF, 0xD3, 0x32, 0xF3, 0x08,
+ 0x9B, 0x85, 0x42, 0xE9, 0x46, 0x17, 0xD8, 0x93, 0xD7, 0xFE, 0x94,
+ 0x4E, 0x10, 0xA7, 0x93, 0x7E, 0xE2, 0x9D, 0x96, 0x93, 0xC0 },
+ "MIGuMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBh"
+ "ZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ"
+ "QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMT"
+ "IENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4",
+ "AKPaQn6ksa7a",
+ },
+ {
+ // CN=Global Chambersign Root - 2008,O=AC Camerfirma S.A.,serialNumber=A82743287,L=Madrid (see current address at www.camerfirma.com/address),C=EU
+ "1.3.6.1.4.1.17326.10.8.12.1.2",
+ "Camerfirma EV OID b",
+ SEC_OID_UNKNOWN,
+ { 0x13, 0x63, 0x35, 0x43, 0x93, 0x34, 0xA7, 0x69, 0x80, 0x16, 0xA0,
+ 0xD3, 0x24, 0xDE, 0x72, 0x28, 0x4E, 0x07, 0x9D, 0x7B, 0x52, 0x20,
+ 0xBB, 0x8F, 0xBD, 0x74, 0x78, 0x16, 0xEE, 0xBE, 0xBA, 0xCA },
+ "MIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBh"
+ "ZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ"
+ "QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMT"
+ "Hkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwOA==",
+ "AMnN0+nVfSPO",
+ },
+ {
+ // CN=AffirmTrust Commercial,O=AffirmTrust,C=US
+ "1.3.6.1.4.1.34697.2.1",
+ "AffirmTrust EV OID a",
+ SEC_OID_UNKNOWN,
+ { 0x03, 0x76, 0xAB, 0x1D, 0x54, 0xC5, 0xF9, 0x80, 0x3C, 0xE4, 0xB2,
+ 0xE2, 0x01, 0xA0, 0xEE, 0x7E, 0xEF, 0x7B, 0x57, 0xB6, 0x36, 0xE8,
+ 0xA9, 0x3C, 0x9B, 0x8D, 0x48, 0x60, 0xC9, 0x6F, 0x5F, 0xA7 },
+ "MEQxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEfMB0GA1UEAwwW"
+ "QWZmaXJtVHJ1c3QgQ29tbWVyY2lhbA==",
+ "d3cGJyapsXw=",
+ },
+ {
+ // CN=AffirmTrust Networking,O=AffirmTrust,C=US
+ "1.3.6.1.4.1.34697.2.2",
+ "AffirmTrust EV OID b",
+ SEC_OID_UNKNOWN,
+ { 0x0A, 0x81, 0xEC, 0x5A, 0x92, 0x97, 0x77, 0xF1, 0x45, 0x90, 0x4A,
+ 0xF3, 0x8D, 0x5D, 0x50, 0x9F, 0x66, 0xB5, 0xE2, 0xC5, 0x8F, 0xCD,
+ 0xB5, 0x31, 0x05, 0x8B, 0x0E, 0x17, 0xF3, 0xF0, 0xB4, 0x1B },
+ "MEQxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEfMB0GA1UEAwwW"
+ "QWZmaXJtVHJ1c3QgTmV0d29ya2luZw==",
+ "fE8EORzUmS0=",
+ },
+ {
+ // CN=AffirmTrust Premium,O=AffirmTrust,C=US
+ "1.3.6.1.4.1.34697.2.3",
+ "AffirmTrust EV OID c",
+ SEC_OID_UNKNOWN,
+ { 0x70, 0xA7, 0x3F, 0x7F, 0x37, 0x6B, 0x60, 0x07, 0x42, 0x48, 0x90,
+ 0x45, 0x34, 0xB1, 0x14, 0x82, 0xD5, 0xBF, 0x0E, 0x69, 0x8E, 0xCC,
+ 0x49, 0x8D, 0xF5, 0x25, 0x77, 0xEB, 0xF2, 0xE9, 0x3B, 0x9A },
+ "MEExCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEcMBoGA1UEAwwT"
+ "QWZmaXJtVHJ1c3QgUHJlbWl1bQ==",
+ "bYwURrGmCu4=",
+ },
+ {
+ // CN=AffirmTrust Premium ECC,O=AffirmTrust,C=US
+ "1.3.6.1.4.1.34697.2.4",
+ "AffirmTrust EV OID d",
+ SEC_OID_UNKNOWN,
+ { 0xBD, 0x71, 0xFD, 0xF6, 0xDA, 0x97, 0xE4, 0xCF, 0x62, 0xD1, 0x64,
+ 0x7A, 0xDD, 0x25, 0x81, 0xB0, 0x7D, 0x79, 0xAD, 0xF8, 0x39, 0x7E,
+ 0xB4, 0xEC, 0xBA, 0x9C, 0x5E, 0x84, 0x88, 0x82, 0x14, 0x23 },
+ "MEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwX"
+ "QWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0M=",
+ "dJclisc/elQ=",
+ },
+ {
+ // CN=Certum Trusted Network CA,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL
+ "1.2.616.1.113527.2.5.1.1",
+ "Certum EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x5C, 0x58, 0x46, 0x8D, 0x55, 0xF5, 0x8E, 0x49, 0x7E, 0x74, 0x39,
+ 0x82, 0xD2, 0xB5, 0x00, 0x10, 0xB6, 0xD1, 0x65, 0x37, 0x4A, 0xCF,
+ 0x83, 0xA7, 0xD4, 0xA3, 0x2D, 0xB7, 0x68, 0xC4, 0x40, 0x8E },
+ "MH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBT"
+ "LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAg"
+ "BgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0E=",
+ "BETA",
+ },
+ {
+ // CN=Certum Trusted Network CA 2,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL
+ "1.2.616.1.113527.2.5.1.1",
+ "Certum EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xB6, 0x76, 0xF2, 0xED, 0xDA, 0xE8, 0x77, 0x5C, 0xD3, 0x6C, 0xB0,
+ 0xF6, 0x3C, 0xD1, 0xD4, 0x60, 0x39, 0x61, 0xF4, 0x9E, 0x62, 0x65,
+ 0xBA, 0x01, 0x3A, 0x2F, 0x03, 0x07, 0xB6, 0xD0, 0xB8, 0x04 },
+ "MIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg"
+ "Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSQw"
+ "IgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBIDI=",
+ "IdbQSk8lD8kyN/yqXhKN6Q==",
+ },
+ {
+ // CN=Izenpe.com,O=IZENPE S.A.,C=ES
+ "1.3.6.1.4.1.14777.6.1.1",
+ "Izenpe EV OID 1",
+ SEC_OID_UNKNOWN,
+ { 0x25, 0x30, 0xCC, 0x8E, 0x98, 0x32, 0x15, 0x02, 0xBA, 0xD9, 0x6F,
+ 0x9B, 0x1F, 0xBA, 0x1B, 0x09, 0x9E, 0x2D, 0x29, 0x9E, 0x0F, 0x45,
+ 0x48, 0xBB, 0x91, 0x4F, 0x36, 0x3B, 0xC0, 0xD4, 0x53, 0x1F },
+ "MDgxCzAJBgNVBAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjETMBEGA1UEAwwK"
+ "SXplbnBlLmNvbQ==",
+ "ALC3WhZIX7/hy/WL1xnmfQ==",
+ },
+ {
+ // CN=Izenpe.com,O=IZENPE S.A.,C=ES
+ "1.3.6.1.4.1.14777.6.1.2",
+ "Izenpe EV OID 2",
+ SEC_OID_UNKNOWN,
+ { 0x25, 0x30, 0xCC, 0x8E, 0x98, 0x32, 0x15, 0x02, 0xBA, 0xD9, 0x6F,
+ 0x9B, 0x1F, 0xBA, 0x1B, 0x09, 0x9E, 0x2D, 0x29, 0x9E, 0x0F, 0x45,
+ 0x48, 0xBB, 0x91, 0x4F, 0x36, 0x3B, 0xC0, 0xD4, 0x53, 0x1F },
+ "MDgxCzAJBgNVBAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjETMBEGA1UEAwwK"
+ "SXplbnBlLmNvbQ==",
+ "ALC3WhZIX7/hy/WL1xnmfQ==",
+ },
+ {
+ // CN=T-TeleSec GlobalRoot Class 3,OU=T-Systems Trust Center,O=T-Systems Enterprise Services GmbH,C=DE
+ "1.3.6.1.4.1.7879.13.24.1",
+ "T-Systems EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xFD, 0x73, 0xDA, 0xD3, 0x1C, 0x64, 0x4F, 0xF1, 0xB4, 0x3B, 0xEF,
+ 0x0C, 0xCD, 0xDA, 0x96, 0x71, 0x0B, 0x9C, 0xD9, 0x87, 0x5E, 0xCA,
+ 0x7E, 0x31, 0x70, 0x7A, 0xF3, 0xE9, 0x6D, 0x52, 0x2B, 0xBD },
+ "MIGCMQswCQYDVQQGEwJERTErMCkGA1UECgwiVC1TeXN0ZW1zIEVudGVycHJpc2Ug"
+ "U2VydmljZXMgR21iSDEfMB0GA1UECwwWVC1TeXN0ZW1zIFRydXN0IENlbnRlcjEl"
+ "MCMGA1UEAwwcVC1UZWxlU2VjIEdsb2JhbFJvb3QgQ2xhc3MgMw==",
+ "AQ==",
+ },
+ {
+ // CN=China Internet Network Information Center EV Certificates Root,O=China Internet Network Information Center,C=CN
+ "1.3.6.1.4.1.29836.1.10",
+ "CNNIC EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x1C, 0x01, 0xC6, 0xF4, 0xDB, 0xB2, 0xFE, 0xFC, 0x22, 0x55, 0x8B,
+ 0x2B, 0xCA, 0x32, 0x56, 0x3F, 0x49, 0x84, 0x4A, 0xCF, 0xC3, 0x2B,
+ 0x7B, 0xE4, 0xB0, 0xFF, 0x59, 0x9F, 0x9E, 0x8C, 0x7A, 0xF7 },
+ "MIGKMQswCQYDVQQGEwJDTjEyMDAGA1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29y"
+ "ayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMMPkNoaW5hIEludGVybmV0IE5l"
+ "dHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRlcyBSb290",
+ "SJ8AAQ==",
+ },
+ {
+ // CN=TWCA Root Certification Authority,OU=Root CA,O=TAIWAN-CA,C=TW
+ "1.3.6.1.4.1.40869.1.1.22.3",
+ "TWCA EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xBF, 0xD8, 0x8F, 0xE1, 0x10, 0x1C, 0x41, 0xAE, 0x3E, 0x80, 0x1B,
+ 0xF8, 0xBE, 0x56, 0x35, 0x0E, 0xE9, 0xBA, 0xD1, 0xA6, 0xB9, 0xBD,
+ 0x51, 0x5E, 0xDC, 0x5C, 0x6D, 0x5B, 0x87, 0x11, 0xAC, 0x44 },
+ "MF8xCzAJBgNVBAYTAlRXMRIwEAYDVQQKDAlUQUlXQU4tQ0ExEDAOBgNVBAsMB1Jv"
+ "b3QgQ0ExKjAoBgNVBAMMIVRXQ0EgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0"
+ "eQ==",
+ "AQ==",
+ },
+ {
+ // CN=D-TRUST Root Class 3 CA 2 EV 2009,O=D-Trust GmbH,C=DE
+ "1.3.6.1.4.1.4788.2.202.1",
+ "D-TRUST EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xEE, 0xC5, 0x49, 0x6B, 0x98, 0x8C, 0xE9, 0x86, 0x25, 0xB9, 0x34,
+ 0x09, 0x2E, 0xEC, 0x29, 0x08, 0xBE, 0xD0, 0xB0, 0xF3, 0x16, 0xC2,
+ 0xD4, 0x73, 0x0C, 0x84, 0xEA, 0xF1, 0xF3, 0xD3, 0x48, 0x81 },
+ "MFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMM"
+ "IUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOQ==",
+ "CYP0",
+ },
+ {
+ // CN=Swisscom Root EV CA 2,OU=Digital Certificate Services,O=Swisscom,C=ch
+ "2.16.756.1.83.21.0",
+ "Swisscom EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xD9, 0x5F, 0xEA, 0x3C, 0xA4, 0xEE, 0xDC, 0xE7, 0x4C, 0xD7, 0x6E,
+ 0x75, 0xFC, 0x6D, 0x1F, 0xF6, 0x2C, 0x44, 0x1F, 0x0F, 0xA8, 0xBC,
+ 0x77, 0xF0, 0x34, 0xB1, 0x9E, 0x5D, 0xB2, 0x58, 0x01, 0x5D },
+ "MGcxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln"
+ "aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEeMBwGA1UEAxMVU3dpc3Njb20gUm9v"
+ "dCBFViBDQSAy",
+ "APL6ZOJ0Y9ON/RAdBB92ylg=",
+ },
+ {
+ // CN=VeriSign Universal Root Certification Authority,OU="(c) 2008 VeriSign, Inc. - For authorized use only",OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
+ "2.16.840.1.113733.1.7.23.6",
+ "VeriSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x23, 0x99, 0x56, 0x11, 0x27, 0xA5, 0x71, 0x25, 0xDE, 0x8C, 0xEF,
+ 0xEA, 0x61, 0x0D, 0xDF, 0x2F, 0xA0, 0x78, 0xB5, 0xC8, 0x06, 0x7F,
+ 0x4E, 0x82, 0x82, 0x90, 0xBF, 0xB8, 0x60, 0xE8, 0x4B, 0x3C },
+ "MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV"
+ "BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZl"
+ "cmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMT"
+ "L1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5",
+ "QBrEZCGzEyEDDrvkEhrFHQ==",
+ },
+ {
+ // CN=GeoTrust Primary Certification Authority - G3,OU=(c) 2008 GeoTrust Inc. - For authorized use only,O=GeoTrust Inc.,C=US
+ "1.3.6.1.4.1.14370.1.6",
+ "GeoTrust EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xB4, 0x78, 0xB8, 0x12, 0x25, 0x0D, 0xF8, 0x78, 0x63, 0x5C, 0x2A,
+ 0xA7, 0xEC, 0x7D, 0x15, 0x5E, 0xAA, 0x62, 0x5E, 0xE8, 0x29, 0x16,
+ 0xE2, 0xCD, 0x29, 0x43, 0x61, 0x88, 0x6C, 0xD1, 0xFB, 0xD4 },
+ "MIGYMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjE5MDcGA1UE"
+ "CxMwKGMpIDIwMDggR2VvVHJ1c3QgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBv"
+ "bmx5MTYwNAYDVQQDEy1HZW9UcnVzdCBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0"
+ "aG9yaXR5IC0gRzM=",
+ "FaxulBmyeUtB9iepwxgPHw==",
+ },
+ {
+ // CN=thawte Primary Root CA - G3,OU="(c) 2008 thawte, Inc. - For authorized use only",OU=Certification Services Division,O="thawte, Inc.",C=US
+ "2.16.840.1.113733.1.7.48.1",
+ "Thawte EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x4B, 0x03, 0xF4, 0x58, 0x07, 0xAD, 0x70, 0xF2, 0x1B, 0xFC, 0x2C,
+ 0xAE, 0x71, 0xC9, 0xFD, 0xE4, 0x60, 0x4C, 0x06, 0x4C, 0xF5, 0xFF,
+ 0xB6, 0x86, 0xBA, 0xE5, 0xDB, 0xAA, 0xD7, 0xFD, 0xD3, 0x4C },
+ "MIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMuMSgwJgYDVQQL"
+ "Ex9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYDVQQLEy8oYykg"
+ "MjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG"
+ "A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz",
+ "YAGXt0an6rS0mtZLL/eQ+w==",
+ },
+ {
+ // CN = Autoridad de Certificacion Firmaprofesional CIF A62634068, C = ES
+ "1.3.6.1.4.1.13177.10.1.3.10",
+ "Firmaprofesional EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x04, 0x04, 0x80, 0x28, 0xBF, 0x1F, 0x28, 0x64, 0xD4, 0x8F, 0x9A,
+ 0xD4, 0xD8, 0x32, 0x94, 0x36, 0x6A, 0x82, 0x88, 0x56, 0x55, 0x3F,
+ 0x3B, 0x14, 0x30, 0x3F, 0x90, 0x14, 0x7F, 0x5D, 0x40, 0xEF },
+ "MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNh"
+ "Y2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=",
+ "U+w77vuySF8=",
+ },
+ {
+ // CN = TWCA Global Root CA, OU = Root CA, O = TAIWAN-CA, C = TW
+ "1.3.6.1.4.1.40869.1.1.22.3",
+ "TWCA EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x59, 0x76, 0x90, 0x07, 0xF7, 0x68, 0x5D, 0x0F, 0xCD, 0x50, 0x87,
+ 0x2F, 0x9F, 0x95, 0xD5, 0x75, 0x5A, 0x5B, 0x2B, 0x45, 0x7D, 0x81,
+ 0xF3, 0x69, 0x2B, 0x61, 0x0A, 0x98, 0x67, 0x2F, 0x0E, 0x1B },
+ "MFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jv"
+ "b3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0E=",
+ "DL4=",
+ },
+ {
+ // CN = E-Tugra Certification Authority, OU = E-Tugra Sertifikasyon Merkezi, O = E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş., L = Ankara, C = TR
+ "2.16.792.3.0.4.1.1.4",
+ "ETugra EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xB0, 0xBF, 0xD5, 0x2B, 0xB0, 0xD7, 0xD9, 0xBD, 0x92, 0xBF, 0x5D,
+ 0x4D, 0xC1, 0x3D, 0xA2, 0x55, 0xC0, 0x2C, 0x54, 0x2F, 0x37, 0x83,
+ 0x65, 0xEA, 0x89, 0x39, 0x11, 0xF5, 0x5E, 0x55, 0xF2, 0x3C },
+ "MIGyMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMUAwPgYDVQQKDDdFLVR1"
+ "xJ9yYSBFQkcgQmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEu"
+ "xZ4uMSYwJAYDVQQLDB1FLVR1Z3JhIFNlcnRpZmlrYXN5b24gTWVya2V6aTEoMCYG"
+ "A1UEAwwfRS1UdWdyYSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+ "amg+nFGby1M=",
+ },
+ {
+ // CN=Actalis Authentication Root CA,O=Actalis S.p.A./03358520967,L=Milan,C=IT
+ "1.3.159.1.17.1",
+ "Actalis EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x55, 0x92, 0x60, 0x84, 0xEC, 0x96, 0x3A, 0x64, 0xB9, 0x6E, 0x2A,
+ 0xBE, 0x01, 0xCE, 0x0B, 0xA8, 0x6A, 0x64, 0xFB, 0xFE, 0xBC, 0xC7,
+ 0xAA, 0xB5, 0xAF, 0xC1, 0x55, 0xB3, 0x7F, 0xD7, 0x60, 0x66 },
+ "MGsxCzAJBgNVBAYTAklUMQ4wDAYDVQQHDAVNaWxhbjEjMCEGA1UECgwaQWN0YWxp"
+ "cyBTLnAuQS4vMDMzNTg1MjA5NjcxJzAlBgNVBAMMHkFjdGFsaXMgQXV0aGVudGlj"
+ "YXRpb24gUm9vdCBDQQ==",
+ "VwoRl0LE48w=",
+ },
+ {
+ // CN=Certification Authority of WoSign,O=WoSign CA Limited,C=CN
+ "1.3.6.1.4.1.36305.2",
+ "WoSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x4B, 0x22, 0xD5, 0xA6, 0xAE, 0xC9, 0x9F, 0x3C, 0xDB, 0x79, 0xAA,
+ 0x5E, 0xC0, 0x68, 0x38, 0x47, 0x9C, 0xD5, 0xEC, 0xBA, 0x71, 0x64,
+ 0xF7, 0xF2, 0x2D, 0xC1, 0xD6, 0x5F, 0x63, 0xD8, 0x57, 0x08 },
+ "MFUxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEqMCgG"
+ "A1UEAxMhQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgb2YgV29TaWdu",
+ "XmjWEXGUY1BWAGjzPsnFkQ==",
+ },
+ {
+ // CN=CA ...............,O=WoSign CA Limited,C=CN
+ "1.3.6.1.4.1.36305.2",
+ "WoSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xD6, 0xF0, 0x34, 0xBD, 0x94, 0xAA, 0x23, 0x3F, 0x02, 0x97, 0xEC,
+ 0xA4, 0x24, 0x5B, 0x28, 0x39, 0x73, 0xE4, 0x47, 0xAA, 0x59, 0x0F,
+ 0x31, 0x0C, 0x77, 0xF4, 0x8F, 0xDF, 0x83, 0x11, 0x22, 0x54 },
+ "MEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkG"
+ "A1UEAwwSQ0Eg5rKD6YCa5qC56K+B5Lmm",
+ "UHBrzdgT/BtOOzNy0hFIjQ==",
+ },
+ {
+ // CN=DigiCert Assured ID Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US
+ "2.16.840.1.114412.2.1",
+ "DigiCert EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x7D, 0x05, 0xEB, 0xB6, 0x82, 0x33, 0x9F, 0x8C, 0x94, 0x51, 0xEE,
+ 0x09, 0x4E, 0xEB, 0xFE, 0xFA, 0x79, 0x53, 0xA1, 0x14, 0xED, 0xB2,
+ 0xF4, 0x49, 0x49, 0x45, 0x2F, 0xAB, 0x7D, 0x2F, 0xC1, 0x85 },
+ "MGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT"
+ "EHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQg"
+ "Um9vdCBHMg==",
+ "C5McOtY5Z+pnI7/Dr5r0Sw==",
+ },
+ {
+ // CN=DigiCert Assured ID Root G3,OU=www.digicert.com,O=DigiCert Inc,C=US
+ "2.16.840.1.114412.2.1",
+ "DigiCert EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x7E, 0x37, 0xCB, 0x8B, 0x4C, 0x47, 0x09, 0x0C, 0xAB, 0x36, 0x55,
+ 0x1B, 0xA6, 0xF4, 0x5D, 0xB8, 0x40, 0x68, 0x0F, 0xBA, 0x16, 0x6A,
+ 0x95, 0x2D, 0xB1, 0x00, 0x71, 0x7F, 0x43, 0x05, 0x3F, 0xC2 },
+ "MGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT"
+ "EHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQg"
+ "Um9vdCBHMw==",
+ "C6Fa+h3foLVJRK/NJKBs7A==",
+ },
+ {
+ // CN=DigiCert Global Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US
+ "2.16.840.1.114412.2.1",
+ "DigiCert EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xCB, 0x3C, 0xCB, 0xB7, 0x60, 0x31, 0xE5, 0xE0, 0x13, 0x8F, 0x8D,
+ 0xD3, 0x9A, 0x23, 0xF9, 0xDE, 0x47, 0xFF, 0xC3, 0x5E, 0x43, 0xC1,
+ 0x14, 0x4C, 0xEA, 0x27, 0xD4, 0x6A, 0x5A, 0xB1, 0xCB, 0x5F },
+ "MGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT"
+ "EHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290"
+ "IEcy",
+ "Azrx5qcRqaC7KGSxHQn65Q==",
+ },
+ {
+ // CN=DigiCert Global Root G3,OU=www.digicert.com,O=DigiCert Inc,C=US
+ "2.16.840.1.114412.2.1",
+ "DigiCert EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x31, 0xAD, 0x66, 0x48, 0xF8, 0x10, 0x41, 0x38, 0xC7, 0x38, 0xF3,
+ 0x9E, 0xA4, 0x32, 0x01, 0x33, 0x39, 0x3E, 0x3A, 0x18, 0xCC, 0x02,
+ 0x29, 0x6E, 0xF9, 0x7C, 0x2A, 0xC9, 0xEF, 0x67, 0x31, 0xD0 },
+ "MGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT"
+ "EHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290"
+ "IEcz",
+ "BVVWvPJepDU1w6QP1atFcg==",
+ },
+ {
+ // CN=DigiCert Trusted Root G4,OU=www.digicert.com,O=DigiCert Inc,C=US
+ "2.16.840.1.114412.2.1",
+ "DigiCert EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x55, 0x2F, 0x7B, 0xDC, 0xF1, 0xA7, 0xAF, 0x9E, 0x6C, 0xE6, 0x72,
+ 0x01, 0x7F, 0x4F, 0x12, 0xAB, 0xF7, 0x72, 0x40, 0xC7, 0x8E, 0x76,
+ 0x1A, 0xC2, 0x03, 0xD1, 0xD9, 0xD2, 0x0A, 0xC8, 0x99, 0x88 },
+ "MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT"
+ "EHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9v"
+ "dCBHNA==",
+ "BZsbV56OITLiOQe9p3d1XA==",
+ },
+ {
+ // CN=QuoVadis Root CA 2 G3,O=QuoVadis Limited,C=BM
+ "1.3.6.1.4.1.8024.0.2.100.1.2",
+ "QuoVadis EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x8F, 0xE4, 0xFB, 0x0A, 0xF9, 0x3A, 0x4D, 0x0D, 0x67, 0xDB, 0x0B,
+ 0xEB, 0xB2, 0x3E, 0x37, 0xC7, 0x1B, 0xF3, 0x25, 0xDC, 0xBC, 0xDD,
+ 0x24, 0x0E, 0xA0, 0x4D, 0xAF, 0x58, 0xB4, 0x7E, 0x18, 0x40 },
+ "MEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYD"
+ "VQQDExVRdW9WYWRpcyBSb290IENBIDIgRzM=",
+ "RFc0JFuBiZs18s64KztbpybwdSg=",
+ },
+ {
+ // CN=COMODO RSA Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
+ "1.3.6.1.4.1.6449.1.2.1.5.1",
+ "Comodo EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x52, 0xF0, 0xE1, 0xC4, 0xE5, 0x8E, 0xC6, 0x29, 0x29, 0x1B, 0x60,
+ 0x31, 0x7F, 0x07, 0x46, 0x71, 0xB8, 0x5D, 0x7E, 0xA8, 0x0D, 0x5B,
+ 0x07, 0x27, 0x34, 0x63, 0x53, 0x4B, 0x32, 0xB4, 0x02, 0x34 },
+ "MIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAw"
+ "DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDErMCkG"
+ "A1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+ "TKr5yttjb+Af907YWwOGnQ==",
+ },
+ {
+ // CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
+ "1.3.6.1.4.1.6449.1.2.1.5.1",
+ "Comodo EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xE7, 0x93, 0xC9, 0xB0, 0x2F, 0xD8, 0xAA, 0x13, 0xE2, 0x1C, 0x31,
+ 0x22, 0x8A, 0xCC, 0xB0, 0x81, 0x19, 0x64, 0x3B, 0x74, 0x9C, 0x89,
+ 0x89, 0x64, 0xB1, 0x74, 0x6D, 0x46, 0xC3, 0xD4, 0xCB, 0xD2 },
+ "MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxML"
+ "SmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwG"
+ "A1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+ "Af1tMPyjylGoG7xkDjUDLQ==",
+ },
+ {
+ // CN=USERTrust ECC Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
+ "1.3.6.1.4.1.6449.1.2.1.5.1",
+ "Comodo EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x4F, 0xF4, 0x60, 0xD5, 0x4B, 0x9C, 0x86, 0xDA, 0xBF, 0xBC, 0xFC,
+ 0x57, 0x12, 0xE0, 0x40, 0x0D, 0x2B, 0xED, 0x3F, 0xBC, 0x4D, 0x4F,
+ 0xBD, 0xAA, 0x86, 0xE0, 0x6A, 0xDC, 0xD2, 0xA9, 0xAD, 0x7A },
+ "MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxML"
+ "SmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwG"
+ "A1UEAxMlVVNFUlRydXN0IEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+ "XIuZxVqUxdJxVt7NiYDMJg==",
+ },
+ {
+ // CN=GlobalSign,O=GlobalSign,OU=GlobalSign ECC Root CA - R4
+ "1.3.6.1.4.1.4146.1.1",
+ "GlobalSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xBE, 0xC9, 0x49, 0x11, 0xC2, 0x95, 0x56, 0x76, 0xDB, 0x6C, 0x0A,
+ 0x55, 0x09, 0x86, 0xD7, 0x6E, 0x3B, 0xA0, 0x05, 0x66, 0x7C, 0x44,
+ 0x2C, 0x97, 0x62, 0xB4, 0xFB, 0xB7, 0x73, 0xDE, 0x22, 0x8C },
+ "MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UE"
+ "ChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==",
+ "KjikHJYKBN5CsiilC+g0mAI=",
+ },
+ {
+ // CN=GlobalSign,O=GlobalSign,OU=GlobalSign ECC Root CA - R5
+ "1.3.6.1.4.1.4146.1.1",
+ "GlobalSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x17, 0x9F, 0xBC, 0x14, 0x8A, 0x3D, 0xD0, 0x0F, 0xD2, 0x4E, 0xA1,
+ 0x34, 0x58, 0xCC, 0x43, 0xBF, 0xA7, 0xF5, 0x9C, 0x81, 0x82, 0xD7,
+ 0x83, 0xA5, 0x13, 0xF6, 0xEB, 0xEC, 0x10, 0x0C, 0x89, 0x24 },
+ "MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNTETMBEGA1UE"
+ "ChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==",
+ "YFlJ4CYuu1X5CneKcflK2Gw=",
+ },
+ {
+ // CN=Entrust.net Certification Authority (2048),OU=(c) 1999 Entrust.net Limited,OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),O=Entrust.net
+ "2.16.840.1.114028.10.1.2",
+ "Entrust EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x6D, 0xC4, 0x71, 0x72, 0xE0, 0x1C, 0xBC, 0xB0, 0xBF, 0x62, 0x58,
+ 0x0D, 0x89, 0x5F, 0xE2, 0xB8, 0xAC, 0x9A, 0xD4, 0xF8, 0x73, 0x80,
+ 0x1E, 0x0C, 0x10, 0xB9, 0xC8, 0x37, 0xD2, 0x1E, 0xB1, 0x77 },
+ "MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3Qu"
+ "bmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMG"
+ "A1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50"
+ "cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp",
+ "OGPe+A==",
+ },
+ {
+ // CN=Staat der Nederlanden EV Root CA,O=Staat der Nederlanden,C=NL
+ "2.16.528.1.1003.1.2.7",
+ "Staat der Nederlanden EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x4D, 0x24, 0x91, 0x41, 0x4C, 0xFE, 0x95, 0x67, 0x46, 0xEC, 0x4C,
+ 0xEF, 0xA6, 0xCF, 0x6F, 0x72, 0xE2, 0x8A, 0x13, 0x29, 0x43, 0x2F,
+ 0x9D, 0x8A, 0x90, 0x7A, 0xC4, 0xCB, 0x5D, 0xAD, 0xC1, 0x5A },
+ "MFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4x"
+ "KTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBSb290IENB",
+ "AJiWjQ==",
+ },
+ {
+ // CN=Entrust Root Certification Authority - G2,OU="(c) 2009 Entrust, Inc. - for authorized use only",OU=See www.entrust.net/legal-terms,O="Entrust, Inc.",C=US
+ "2.16.840.1.114028.10.1.2",
+ "Entrust EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x43, 0xDF, 0x57, 0x74, 0xB0, 0x3E, 0x7F, 0xEF, 0x5F, 0xE4, 0x0D,
+ 0x93, 0x1A, 0x7B, 0xED, 0xF1, 0xBB, 0x2E, 0x6B, 0x42, 0x73, 0x8C,
+ 0x4E, 0x6D, 0x38, 0x41, 0x10, 0x3D, 0x3A, 0xA7, 0xF3, 0x39 },
+ "MIG+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UE"
+ "CxMfU2VlIHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMp"
+ "IDIwMDkgRW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTIw"
+ "MAYDVQQDEylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH"
+ "Mg==",
+ "SlOMKA==",
+ },
+ {
+ // CN=Entrust Root Certification Authority - EC1,OU="(c) 2012 Entrust, Inc. - for authorized use only",OU=See www.entrust.net/legal-terms,O="Entrust, Inc.",C=US
+ "2.16.840.1.114028.10.1.2",
+ "Entrust EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x02, 0xED, 0x0E, 0xB2, 0x8C, 0x14, 0xDA, 0x45, 0x16, 0x5C, 0x56,
+ 0x67, 0x91, 0x70, 0x0D, 0x64, 0x51, 0xD7, 0xFB, 0x56, 0xF0, 0xB2,
+ 0xAB, 0x1D, 0x3B, 0x8E, 0xB0, 0x70, 0xE5, 0x6E, 0xDF, 0xF5 },
+ "MIG/MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UE"
+ "CxMfU2VlIHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMp"
+ "IDIwMTIgRW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTMw"
+ "MQYDVQQDEypFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBF"
+ "QzE=",
+ "AKaLeSkAAAAAUNCR+Q==",
+ },
+ {
+ // CN=CFCA EV ROOT,O=China Financial Certification Authority,C=CN
+ "2.16.156.112554.3",
+ "CFCA EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x5C, 0xC3, 0xD7, 0x8E, 0x4E, 0x1D, 0x5E, 0x45, 0x54, 0x7A, 0x04,
+ 0xE6, 0x87, 0x3E, 0x64, 0xF9, 0x0C, 0xF9, 0x53, 0x6D, 0x1C, 0xCC,
+ 0x2E, 0xF8, 0x00, 0xF3, 0x55, 0xC4, 0xC5, 0xFD, 0x70, 0xFD },
+ "MFYxCzAJBgNVBAYTAkNOMTAwLgYDVQQKDCdDaGluYSBGaW5hbmNpYWwgQ2VydGlm"
+ "aWNhdGlvbiBBdXRob3JpdHkxFTATBgNVBAMMDENGQ0EgRVYgUk9PVA==",
+ "GErM1g==",
+ },
+ {
+ // CN=Certification Authority of WoSign G2,O=WoSign CA Limited,C=CN
+ "1.3.6.1.4.1.36305.2",
+ "WoSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0xD4, 0x87, 0xA5, 0x6F, 0x83, 0xB0, 0x74, 0x82, 0xE8, 0x5E, 0x96,
+ 0x33, 0x94, 0xC1, 0xEC, 0xC2, 0xC9, 0xE5, 0x1D, 0x09, 0x03, 0xEE,
+ 0x94, 0x6B, 0x02, 0xC3, 0x01, 0x58, 0x1E, 0xD9, 0x9E, 0x16 },
+ "MFgxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEtMCsG"
+ "A1UEAxMkQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgb2YgV29TaWduIEcy",
+ "ayXaioidfLwPBbOxemFFRA==",
+ },
+ {
+ // CN=CA WoSign ECC Root,O=WoSign CA Limited,C=CN
+ "1.3.6.1.4.1.36305.2",
+ "WoSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x8B, 0x45, 0xDA, 0x1C, 0x06, 0xF7, 0x91, 0xEB, 0x0C, 0xAB, 0xF2,
+ 0x6B, 0xE5, 0x88, 0xF5, 0xFB, 0x23, 0x16, 0x5C, 0x2E, 0x61, 0x4B,
+ 0xF8, 0x85, 0x56, 0x2D, 0x0D, 0xCE, 0x50, 0xB2, 0x9B, 0x02 },
+ "MEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkG"
+ "A1UEAxMSQ0EgV29TaWduIEVDQyBSb290",
+ "aEpYcIBr8I8C+vbe6LCQkA==",
+ },
+ {
+ // OU=Security Communication RootCA2,O="SECOM Trust Systems CO.,LTD.",C=JP
+ "1.2.392.200091.100.721.1",
+ "SECOM EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x51, 0x3B, 0x2C, 0xEC, 0xB8, 0x10, 0xD4, 0xCD, 0xE5, 0xDD, 0x85,
+ 0x39, 0x1A, 0xDF, 0xC6, 0xC2, 0xDD, 0x60, 0xD8, 0x7B, 0xB7, 0x36,
+ 0xD2, 0xB5, 0x21, 0x48, 0x4A, 0xA4, 0x7A, 0x0E, 0xBE, 0xF6 },
+ "MF0xCzAJBgNVBAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENP"
+ "LixMVEQuMScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTI=",
+ "AA==",
+ },
+ {
+ // CN=OISTE WISeKey Global Root GB CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH
+ "2.16.756.5.14.7.4.8",
+ "WISeKey EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x6B, 0x9C, 0x08, 0xE8, 0x6E, 0xB0, 0xF7, 0x67, 0xCF, 0xAD, 0x65,
+ 0xCD, 0x98, 0xB6, 0x21, 0x49, 0xE5, 0x49, 0x4A, 0x67, 0xF5, 0x84,
+ 0x5E, 0x7B, 0xD1, 0xED, 0x01, 0x9F, 0x27, 0xB8, 0x6B, 0xD6 },
+ "MG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNU"
+ "RSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds"
+ "b2JhbCBSb290IEdCIENB",
+ "drEgUnTwhYdGs/gjGvbCwA==",
+ },
+ {
+ // CN=Certplus Root CA G1,O=Certplus,C=FR
+ "1.3.6.1.4.1.22234.3.5.3.1",
+ "DocuSign EV OID 1",
+ SEC_OID_UNKNOWN,
+ { 0x15, 0x2A, 0x40, 0x2B, 0xFC, 0xDF, 0x2C, 0xD5, 0x48, 0x05, 0x4D,
+ 0x22, 0x75, 0xB3, 0x9C, 0x7F, 0xCA, 0x3E, 0xC0, 0x97, 0x80, 0x78,
+ 0xB0, 0xF0, 0xEA, 0x76, 0xE5, 0x61, 0xA6, 0xC7, 0x43, 0x3E },
+ "MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy"
+ "dHBsdXMgUm9vdCBDQSBHMQ==",
+ "ESBVg+QtPlRWhS2DN7cs3EYR",
+ },
+ {
+ // CN=Certplus Root CA G2,O=Certplus,C=FR
+ "1.3.6.1.4.1.22234.3.5.3.2",
+ "DocuSign EV OID 2",
+ SEC_OID_UNKNOWN,
+ { 0x6C, 0xC0, 0x50, 0x41, 0xE6, 0x44, 0x5E, 0x74, 0x69, 0x6C, 0x4C,
+ 0xFB, 0xC9, 0xF8, 0x0F, 0x54, 0x3B, 0x7E, 0xAB, 0xBB, 0x44, 0xB4,
+ 0xCE, 0x6F, 0x78, 0x7C, 0x6A, 0x99, 0x71, 0xC4, 0x2F, 0x17 },
+ "MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy"
+ "dHBsdXMgUm9vdCBDQSBHMg==",
+ "ESDZkc6uo+jF5//pAq/Pc7xV",
+ },
+ {
+ // CN=OpenTrust Root CA G1,O=OpenTrust,C=FR
+ "1.3.6.1.4.1.22234.2.14.3.11",
+ "DocuSign EV OID 3",
+ SEC_OID_UNKNOWN,
+ { 0x56, 0xC7, 0x71, 0x28, 0xD9, 0x8C, 0x18, 0xD9, 0x1B, 0x4C, 0xFD,
+ 0xFF, 0xBC, 0x25, 0xEE, 0x91, 0x03, 0xD4, 0x75, 0x8E, 0xA2, 0xAB,
+ 0xAD, 0x82, 0x6A, 0x90, 0xF3, 0x45, 0x7D, 0x46, 0x0E, 0xB4 },
+ "MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w"
+ "ZW5UcnVzdCBSb290IENBIEcx",
+ "ESCzkFU5fX82bWTCp59rY45n",
+ },
+ {
+ // CN=OpenTrust Root CA G2,O=OpenTrust,C=FR
+ "1.3.6.1.4.1.22234.2.14.3.11",
+ "DocuSign EV OID 3",
+ SEC_OID_UNKNOWN,
+ { 0x27, 0x99, 0x58, 0x29, 0xFE, 0x6A, 0x75, 0x15, 0xC1, 0xBF, 0xE8,
+ 0x48, 0xF9, 0xC4, 0x76, 0x1D, 0xB1, 0x6C, 0x22, 0x59, 0x29, 0x25,
+ 0x7B, 0xF4, 0x0D, 0x08, 0x94, 0xF2, 0x9E, 0xA8, 0xBA, 0xF2 },
+ "MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w"
+ "ZW5UcnVzdCBSb290IENBIEcy",
+ "ESChaRu/vbm9UpaPI+hIvyYR",
+ },
+ {
+ // CN=OpenTrust Root CA G3,O=OpenTrust,C=FR
+ "1.3.6.1.4.1.22234.2.14.3.11",
+ "DocuSign EV OID 3",
+ SEC_OID_UNKNOWN,
+ { 0xB7, 0xC3, 0x62, 0x31, 0x70, 0x6E, 0x81, 0x07, 0x8C, 0x36, 0x7C,
+ 0xB8, 0x96, 0x19, 0x8F, 0x1E, 0x32, 0x08, 0xDD, 0x92, 0x69, 0x49,
+ 0xDD, 0x8F, 0x57, 0x09, 0xA4, 0x10, 0xF7, 0x5B, 0x62, 0x92 },
+ "MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w"
+ "ZW5UcnVzdCBSb290IENBIEcz",
+ "ESDm+Ez8JLC+BUCs2oMbNGA/",
+ },
+ {
+ // CN=VeriSign Class 3 Public Primary Certification Authority - G4,OU="(c) 2007 VeriSign, Inc. - For authorized use only",OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
+ "2.16.840.1.113733.1.7.23.6",
+ "VeriSign EV OID",
+ SEC_OID_UNKNOWN,
+ { 0x69, 0xDD, 0xD7, 0xEA, 0x90, 0xBB, 0x57, 0xC9, 0x3E, 0x13, 0x5D,
+ 0xC8, 0x5E, 0xA6, 0xFC, 0xD5, 0x48, 0x0B, 0x60, 0x32, 0x39, 0xBD,
+ 0xC4, 0x54, 0xFC, 0x75, 0x8B, 0x2A, 0x26, 0xCF, 0x7F, 0x79 },
+ "MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV"
+ "BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA3IFZl"
+ "cmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMT"
+ "PFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB"
+ "dXRob3JpdHkgLSBHNA==",
+ "L4D+I4wOIg9IZxIokYessw==",
+ },
+};
+
+static SECOidTag
+RegisterOID(const SECItem& oidItem, const char* oidName)
+{
+ SECOidData od;
+ od.oid.len = oidItem.len;
+ od.oid.data = oidItem.data;
+ od.offset = SEC_OID_UNKNOWN;
+ od.desc = oidName;
+ od.mechanism = CKM_INVALID_MECHANISM;
+ od.supportedExtension = INVALID_CERT_EXTENSION;
+ return SECOID_AddEntry(&od);
+}
+
+static SECOidTag sCABForumEVOIDTag = SEC_OID_UNKNOWN;
+
+static bool
+isEVPolicy(SECOidTag policyOIDTag)
+{
+ if (policyOIDTag != SEC_OID_UNKNOWN && policyOIDTag == sCABForumEVOIDTag) {
+ return true;
+ }
+
+ for (const nsMyTrustedEVInfo& entry : myTrustedEVInfos) {
+ if (policyOIDTag == entry.oid_tag) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+namespace mozilla { namespace psm {
+
+bool
+CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& cert,
+ const mozilla::pkix::CertPolicyId& policy)
+{
+ PR_ASSERT(cert);
+ if (!cert) {
+ return false;
+ }
+
+ unsigned char fingerprint[SHA256_LENGTH];
+ SECStatus srv =
+ PK11_HashBuf(SEC_OID_SHA256, fingerprint, cert->derCert.data,
+ AssertedCast<int32_t>(cert->derCert.len));
+ if (srv != SECSuccess) {
+ return false;
+ }
+
+ const SECOidData* cabforumOIDData = SECOID_FindOIDByTag(sCABForumEVOIDTag);
+ for (const nsMyTrustedEVInfo& entry : myTrustedEVInfos) {
+ // This check ensures that only the specific roots we approve for EV get
+ // that status, and not certs (roots or otherwise) that happen to have an
+ // OID that's already been approved for EV.
+ if (!PodEqual(fingerprint, entry.ev_root_sha256_fingerprint)) {
+ continue;
+ }
+
+ if (cabforumOIDData && cabforumOIDData->oid.len == policy.numBytes &&
+ PodEqual(cabforumOIDData->oid.data, policy.bytes, policy.numBytes)) {
+ return true;
+ }
+ const SECOidData* oidData = SECOID_FindOIDByTag(entry.oid_tag);
+ if (oidData && oidData->oid.len == policy.numBytes &&
+ PodEqual(oidData->oid.data, policy.bytes, policy.numBytes)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+nsresult
+LoadExtendedValidationInfo()
+{
+ static const char* sCABForumOIDString = "2.23.140.1.1";
+ static const char* sCABForumOIDDescription = "CA/Browser Forum EV OID";
+
+ mozilla::ScopedAutoSECItem cabforumOIDItem;
+ if (SEC_StringToOID(nullptr, &cabforumOIDItem, sCABForumOIDString, 0)
+ != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+ sCABForumEVOIDTag = RegisterOID(cabforumOIDItem, sCABForumOIDDescription);
+ if (sCABForumEVOIDTag == SEC_OID_UNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (size_t iEV = 0; iEV < mozilla::ArrayLength(myTrustedEVInfos); ++iEV) {
+ nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV];
+
+ SECStatus srv;
+#ifdef DEBUG
+ // This section of code double-checks that we calculated the correct
+ // certificate hash given the issuer and serial number and that it is
+ // actually present in our loaded root certificates module. It is
+ // unnecessary to check this in non-debug builds since we will safely fall
+ // back to DV if the EV information is incorrect.
+ mozilla::ScopedAutoSECItem derIssuer;
+ srv = ATOB_ConvertAsciiToItem(&derIssuer, entry.issuer_base64);
+ MOZ_ASSERT(srv == SECSuccess, "Could not base64-decode built-in EV issuer");
+ if (srv != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mozilla::ScopedAutoSECItem serialNumber;
+ srv = ATOB_ConvertAsciiToItem(&serialNumber, entry.serial_base64);
+ MOZ_ASSERT(srv == SECSuccess, "Could not base64-decode built-in EV serial");
+ if (srv != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CERTIssuerAndSN ias;
+ ias.derIssuer.data = derIssuer.data;
+ ias.derIssuer.len = derIssuer.len;
+ ias.serialNumber.data = serialNumber.data;
+ ias.serialNumber.len = serialNumber.len;
+ ias.serialNumber.type = siUnsignedInteger;
+
+ UniqueCERTCertificate cert(CERT_FindCertByIssuerAndSN(nullptr, &ias));
+
+ // If an entry is missing in the NSS root database, it may be because the
+ // root database is out of sync with what we expect (e.g. a different
+ // version of system NSS is installed).
+ if (!cert) {
+ // The entries for the debug EV roots are at indices 0 through
+ // NUM_TEST_EV_ROOTS - 1. Since they're not built-in, they probably
+ // haven't been loaded yet.
+ MOZ_ASSERT(iEV < NUM_TEST_EV_ROOTS, "Could not find built-in EV root");
+ } else {
+ unsigned char certFingerprint[SHA256_LENGTH];
+ srv = PK11_HashBuf(SEC_OID_SHA256, certFingerprint, cert->derCert.data,
+ AssertedCast<int32_t>(cert->derCert.len));
+ MOZ_ASSERT(srv == SECSuccess, "Could not hash EV root");
+ if (srv != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+ bool same = PodEqual(certFingerprint, entry.ev_root_sha256_fingerprint);
+ MOZ_ASSERT(same, "EV root fingerprint mismatch");
+ if (!same) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+#endif
+ // This is the code that actually enables these roots for EV.
+ mozilla::ScopedAutoSECItem evOIDItem;
+ srv = SEC_StringToOID(nullptr, &evOIDItem, entry.dotted_oid, 0);
+ MOZ_ASSERT(srv == SECSuccess, "SEC_StringToOID failed");
+ if (srv != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+ entry.oid_tag = RegisterOID(evOIDItem, entry.oid_name);
+ if (entry.oid_tag == SEC_OID_UNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+// Find the first policy OID that is known to be an EV policy OID.
+SECStatus
+GetFirstEVPolicy(CERTCertificate* cert,
+ /*out*/ mozilla::pkix::CertPolicyId& policy,
+ /*out*/ SECOidTag& policyOidTag)
+{
+ if (!cert) {
+ PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+ return SECFailure;
+ }
+
+ if (cert->extensions) {
+ for (int i=0; cert->extensions[i]; i++) {
+ const SECItem* oid = &cert->extensions[i]->id;
+
+ SECOidTag oidTag = SECOID_FindOIDTag(oid);
+ if (oidTag != SEC_OID_X509_CERTIFICATE_POLICIES)
+ continue;
+
+ SECItem* value = &cert->extensions[i]->value;
+
+ CERTCertificatePolicies* policies;
+ CERTPolicyInfo** policyInfos;
+
+ policies = CERT_DecodeCertificatePoliciesExtension(value);
+ if (!policies)
+ continue;
+
+ policyInfos = policies->policyInfos;
+
+ bool found = false;
+ while (*policyInfos) {
+ const CERTPolicyInfo* policyInfo = *policyInfos++;
+
+ SECOidTag oid_tag = policyInfo->oid;
+ if (oid_tag != SEC_OID_UNKNOWN && isEVPolicy(oid_tag)) {
+ const SECOidData* oidData = SECOID_FindOIDByTag(oid_tag);
+ PR_ASSERT(oidData);
+ PR_ASSERT(oidData->oid.data);
+ PR_ASSERT(oidData->oid.len > 0);
+ PR_ASSERT(oidData->oid.len <= mozilla::pkix::CertPolicyId::MAX_BYTES);
+ if (oidData && oidData->oid.data && oidData->oid.len > 0 &&
+ oidData->oid.len <= mozilla::pkix::CertPolicyId::MAX_BYTES) {
+ policy.numBytes = static_cast<uint16_t>(oidData->oid.len);
+ memcpy(policy.bytes, oidData->oid.data, policy.numBytes);
+ policyOidTag = oid_tag;
+ found = true;
+ }
+ break;
+ }
+ }
+ CERT_DestroyCertificatePoliciesExtension(policies);
+ if (found) {
+ return SECSuccess;
+ }
+ }
+ }
+
+ PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
+ return SECFailure;
+}
+
+} } // namespace mozilla::psm
diff --git a/security/certverifier/ExtendedValidation.h b/security/certverifier/ExtendedValidation.h
new file mode 100644
index 000000000..0b84dcec0
--- /dev/null
+++ b/security/certverifier/ExtendedValidation.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ExtendedValidation_h
+#define ExtendedValidation_h
+
+#include "ScopedNSSTypes.h"
+#include "certt.h"
+#include "prtypes.h"
+
+namespace mozilla { namespace pkix { struct CertPolicyId; } }
+
+namespace mozilla { namespace psm {
+
+nsresult LoadExtendedValidationInfo();
+SECStatus GetFirstEVPolicy(CERTCertificate* cert,
+ /*out*/ mozilla::pkix::CertPolicyId& policy,
+ /*out*/ SECOidTag& policyOidTag);
+
+// CertIsAuthoritativeForEVPolicy does NOT evaluate whether the cert is trusted
+// or distrusted.
+bool CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& cert,
+ const mozilla::pkix::CertPolicyId& policy);
+
+} } // namespace mozilla::psm
+
+#endif // ExtendedValidation_h
diff --git a/security/certverifier/MultiLogCTVerifier.cpp b/security/certverifier/MultiLogCTVerifier.cpp
new file mode 100644
index 000000000..619649b9f
--- /dev/null
+++ b/security/certverifier/MultiLogCTVerifier.cpp
@@ -0,0 +1,193 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MultiLogCTVerifier.h"
+
+#include "CTObjectsExtractor.h"
+#include "CTSerialization.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+
+namespace mozilla { namespace ct {
+
+using namespace mozilla::pkix;
+
+// Note: this moves |sct| to the target list in |result|, invalidating |sct|.
+static Result
+StoreVerifiedSct(CTVerifyResult& result,
+ SignedCertificateTimestamp&& sct,
+ SignedCertificateTimestamp::VerificationStatus status)
+{
+ sct.verificationStatus = status;
+ if (!result.scts.append(Move(sct))) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ return Success;
+}
+
+Result
+MultiLogCTVerifier::AddLog(Input publicKey)
+{
+ CTLogVerifier log;
+ Result rv = log.Init(publicKey);
+ if (rv != Success) {
+ return rv;
+ }
+ if (!mLogs.append(Move(log))) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ return Success;
+}
+
+Result
+MultiLogCTVerifier::Verify(Input cert,
+ Input issuerSubjectPublicKeyInfo,
+ Input sctListFromCert,
+ Input sctListFromOCSPResponse,
+ Input sctListFromTLSExtension,
+ Time time,
+ CTVerifyResult& result)
+{
+ MOZ_ASSERT(cert.GetLength() > 0);
+ result.Reset();
+
+ Result rv;
+
+ // Verify embedded SCTs
+ if (issuerSubjectPublicKeyInfo.GetLength() > 0 &&
+ sctListFromCert.GetLength() > 0) {
+ LogEntry precertEntry;
+ rv = GetPrecertLogEntry(cert, issuerSubjectPublicKeyInfo, precertEntry);
+ if (rv != Success) {
+ return rv;
+ }
+ rv = VerifySCTs(sctListFromCert, precertEntry,
+ SignedCertificateTimestamp::Origin::Embedded, time,
+ result);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+
+ LogEntry x509Entry;
+ rv = GetX509LogEntry(cert, x509Entry);
+ if (rv != Success) {
+ return rv;
+ }
+
+ // Verify SCTs from a stapled OCSP response
+ if (sctListFromOCSPResponse.GetLength() > 0) {
+ rv = VerifySCTs(sctListFromOCSPResponse, x509Entry,
+ SignedCertificateTimestamp::Origin::OCSPResponse, time,
+ result);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+
+ // Verify SCTs from a TLS extension
+ if (sctListFromTLSExtension.GetLength() > 0) {
+ rv = VerifySCTs(sctListFromTLSExtension, x509Entry,
+ SignedCertificateTimestamp::Origin::TLSExtension, time,
+ result);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+ return Success;
+}
+
+Result
+MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
+ const LogEntry& expectedEntry,
+ SignedCertificateTimestamp::Origin origin,
+ Time time,
+ CTVerifyResult& result)
+{
+ Reader listReader;
+ Result rv = DecodeSCTList(encodedSctList, listReader);
+ if (rv != Success) {
+ result.decodingErrors++;
+ return Success;
+ }
+
+ while (!listReader.AtEnd()) {
+ Input encodedSct;
+ rv = ReadSCTListItem(listReader, encodedSct);
+ if (rv != Success) {
+ result.decodingErrors++;
+ return Success;
+ }
+
+ Reader encodedSctReader(encodedSct);
+ SignedCertificateTimestamp sct;
+ rv = DecodeSignedCertificateTimestamp(encodedSctReader, sct);
+ if (rv != Success) {
+ result.decodingErrors++;
+ continue;
+ }
+ sct.origin = origin;
+
+ rv = VerifySingleSCT(Move(sct), expectedEntry, time, result);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+ return Success;
+}
+
+Result
+MultiLogCTVerifier::VerifySingleSCT(SignedCertificateTimestamp&& sct,
+ const LogEntry& expectedEntry,
+ Time time,
+ CTVerifyResult& result)
+{
+ CTLogVerifier* matchingLog = nullptr;
+ for (auto& log : mLogs) {
+ if (log.keyId() == sct.logId) {
+ matchingLog = &log;
+ break;
+ }
+ }
+
+ if (!matchingLog) {
+ // SCT does not match any known log.
+ return StoreVerifiedSct(result, Move(sct),
+ SignedCertificateTimestamp::VerificationStatus::UnknownLog);
+ }
+
+ if (!matchingLog->SignatureParametersMatch(sct.signature)) {
+ // SCT signature parameters do not match the log's.
+ return StoreVerifiedSct(result, Move(sct),
+ SignedCertificateTimestamp::VerificationStatus::InvalidSignature);
+ }
+
+ Result rv = matchingLog->Verify(expectedEntry, sct);
+ if (rv != Success) {
+ if (rv == Result::ERROR_BAD_SIGNATURE) {
+ return StoreVerifiedSct(result, Move(sct),
+ SignedCertificateTimestamp::VerificationStatus::InvalidSignature);
+ }
+ return rv;
+ }
+
+ // |sct.timestamp| is measured in milliseconds since the epoch,
+ // ignoring leap seconds. When converting it to a second-level precision
+ // pkix::Time, we need to round it either up or down. In our case, rounding up
+ // is more "secure", although practically it does not matter.
+ Time sctTime = TimeFromEpochInSeconds((sct.timestamp + 999u) / 1000u);
+
+ // SCT verified ok, just make sure the timestamp is legitimate.
+ if (sctTime > time) {
+ return StoreVerifiedSct(result, Move(sct),
+ SignedCertificateTimestamp::VerificationStatus::InvalidTimestamp);
+ }
+
+ return StoreVerifiedSct(result, Move(sct),
+ SignedCertificateTimestamp::VerificationStatus::OK);
+}
+
+} } // namespace mozilla::ct
diff --git a/security/certverifier/MultiLogCTVerifier.h b/security/certverifier/MultiLogCTVerifier.h
new file mode 100644
index 000000000..a95e17f76
--- /dev/null
+++ b/security/certverifier/MultiLogCTVerifier.h
@@ -0,0 +1,86 @@
+/* -*- 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 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/. */
+
+#ifndef MultiLogCTVerifier_h
+#define MultiLogCTVerifier_h
+
+#include "CTLogVerifier.h"
+#include "CTVerifyResult.h"
+#include "mozilla/Vector.h"
+#include "pkix/Input.h"
+#include "pkix/Result.h"
+#include "pkix/Time.h"
+#include "SignedCertificateTimestamp.h"
+
+namespace mozilla { namespace ct {
+
+// A Certificate Transparency verifier that can verify Signed Certificate
+// Timestamps from multiple logs.
+class MultiLogCTVerifier
+{
+public:
+ // Adds a new log to the list of known logs to verify against.
+ pkix::Result AddLog(pkix::Input publicKey);
+
+ // Verifies SCTs embedded in the certificate itself, SCTs embedded in a
+ // stapled OCSP response, and SCTs obtained via the
+ // signed_certificate_timestamp TLS extension on the given |cert|.
+ //
+ // A certificate is permitted but not required to use multiple sources for
+ // SCTs. It is expected that most certificates will use only one source
+ // (embedding, TLS extension or OCSP stapling).
+ //
+ // The verifier stops on fatal errors (such as out of memory or invalid
+ // DER encoding of |cert|), but it does not stop on SCT decoding errors. See
+ // CTVerifyResult for more details.
+ //
+ // The internal state of the verifier object is not modified
+ // during the verification process.
+ //
+ // |cert| DER-encoded certificate to be validated using the provided SCTs.
+ // |sctListFromCert| SCT list embedded in |cert|, empty if not present.
+ // |issuerSubjectPublicKeyInfo| SPKI of |cert|'s issuer. Can be empty,
+ // in which case the embedded SCT list
+ // won't be verified.
+ // |sctListFromOCSPResponse| SCT list included in a stapled OCSP response
+ // for |cert|. Empty if not available.
+ // |sctListFromTLSExtension| is the SCT list from the TLS extension. Empty
+ // if no extension was present.
+ // |time| the current time. Used to make sure SCTs are not in the future.
+ // |result| will be filled with the SCTs present, divided into categories
+ // based on the verification result.
+ pkix::Result Verify(pkix::Input cert,
+ pkix::Input issuerSubjectPublicKeyInfo,
+ pkix::Input sctListFromCert,
+ pkix::Input sctListFromOCSPResponse,
+ pkix::Input sctListFromTLSExtension,
+ pkix::Time time,
+ CTVerifyResult& result);
+
+private:
+ // Verifies a list of SCTs from |encodedSctList| over |expectedEntry|,
+ // placing the verification results in |result|. The SCTs in the list
+ // come from |origin| (as will be reflected in the origin field of each SCT).
+ pkix::Result VerifySCTs(pkix::Input encodedSctList,
+ const LogEntry& expectedEntry,
+ SignedCertificateTimestamp::Origin origin,
+ pkix::Time time,
+ CTVerifyResult& result);
+
+ // Verifies a single, parsed SCT against all known logs.
+ // Note: moves |sct| to the target list in |result|, invalidating |sct|.
+ pkix::Result VerifySingleSCT(SignedCertificateTimestamp&& sct,
+ const ct::LogEntry& expectedEntry,
+ pkix::Time time,
+ CTVerifyResult& result);
+
+ // The list of known logs.
+ Vector<CTLogVerifier> mLogs;
+};
+
+} } // namespace mozilla::ct
+
+#endif // MultiLogCTVerifier_h
diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp
new file mode 100644
index 000000000..412adb403
--- /dev/null
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -0,0 +1,1292 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "NSSCertDBTrustDomain.h"
+
+#include <stdint.h>
+
+#include "ExtendedValidation.h"
+#include "NSSErrorsService.h"
+#include "OCSPRequestor.h"
+#include "OCSPVerificationTrustDomain.h"
+#include "PublicKeyPinningService.h"
+#include "cert.h"
+#include "certdb.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Casting.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "nsNSSCertificate.h"
+#include "nsServiceManagerUtils.h"
+#include "nss.h"
+#include "pk11pub.h"
+#include "pkix/Result.h"
+#include "pkix/pkix.h"
+#include "pkix/pkixnss.h"
+#include "prerror.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "secerr.h"
+
+#include "CNNICHashWhitelist.inc"
+#include "StartComAndWoSignData.inc"
+
+using namespace mozilla;
+using namespace mozilla::pkix;
+
+extern LazyLogModule gCertVerifierLog;
+
+static const uint64_t ServerFailureDelaySeconds = 5 * 60;
+
+namespace mozilla { namespace psm {
+
+const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[] = "Builtin Roots Module";
+
+NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
+ OCSPFetching ocspFetching,
+ OCSPCache& ocspCache,
+ /*optional but shouldn't be*/ void* pinArg,
+ CertVerifier::OcspGetConfig ocspGETConfig,
+ uint32_t certShortLifetimeInDays,
+ CertVerifier::PinningMode pinningMode,
+ unsigned int minRSABits,
+ ValidityCheckingMode validityCheckingMode,
+ CertVerifier::SHA1Mode sha1Mode,
+ NetscapeStepUpPolicy netscapeStepUpPolicy,
+ const NeckoOriginAttributes& originAttributes,
+ UniqueCERTCertList& builtChain,
+ /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
+ /*optional*/ const char* hostname)
+ : mCertDBTrustType(certDBTrustType)
+ , mOCSPFetching(ocspFetching)
+ , mOCSPCache(ocspCache)
+ , mPinArg(pinArg)
+ , mOCSPGetConfig(ocspGETConfig)
+ , mCertShortLifetimeInDays(certShortLifetimeInDays)
+ , mPinningMode(pinningMode)
+ , mMinRSABits(minRSABits)
+ , mValidityCheckingMode(validityCheckingMode)
+ , mSHA1Mode(sha1Mode)
+ , mNetscapeStepUpPolicy(netscapeStepUpPolicy)
+ , mOriginAttributes(originAttributes)
+ , mBuiltChain(builtChain)
+ , mPinningTelemetryInfo(pinningTelemetryInfo)
+ , mHostname(hostname)
+ , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
+ , mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED)
+ , mSCTListFromCertificate()
+ , mSCTListFromOCSPStapling()
+{
+}
+
+// If useRoots is true, we only use root certificates in the candidate list.
+// If useRoots is false, we only use non-root certificates in the list.
+static Result
+FindIssuerInner(const UniqueCERTCertList& candidates, bool useRoots,
+ Input encodedIssuerName, TrustDomain::IssuerChecker& checker,
+ /*out*/ bool& keepGoing)
+{
+ keepGoing = true;
+ for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
+ !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
+ bool candidateIsRoot = !!n->cert->isRoot;
+ if (candidateIsRoot != useRoots) {
+ continue;
+ }
+ Input certDER;
+ Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
+ if (rv != Success) {
+ continue; // probably too big
+ }
+
+ const SECItem encodedIssuerNameItem = {
+ siBuffer,
+ const_cast<unsigned char*>(encodedIssuerName.UnsafeGetData()),
+ encodedIssuerName.GetLength()
+ };
+ ScopedAutoSECItem nameConstraints;
+ SECStatus srv = CERT_GetImposedNameConstraints(&encodedIssuerNameItem,
+ &nameConstraints);
+ if (srv != SECSuccess) {
+ if (PR_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ // If no imposed name constraints were found, continue without them
+ rv = checker.Check(certDER, nullptr, keepGoing);
+ } else {
+ // Otherwise apply the constraints
+ Input nameConstraintsInput;
+ if (nameConstraintsInput.Init(nameConstraints.data, nameConstraints.len)
+ != Success) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ rv = checker.Check(certDER, &nameConstraintsInput, keepGoing);
+ }
+ if (rv != Success) {
+ return rv;
+ }
+ if (!keepGoing) {
+ break;
+ }
+ }
+
+ return Success;
+}
+
+Result
+NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
+ IssuerChecker& checker, Time)
+{
+ // TODO: NSS seems to be ambiguous between "no potential issuers found" and
+ // "there was an error trying to retrieve the potential issuers."
+ SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
+ UniqueCERTCertList
+ candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
+ &encodedIssuerNameItem, 0,
+ false));
+ if (candidates) {
+ // First, try all the root certs; then try all the non-root certs.
+ bool keepGoing;
+ Result rv = FindIssuerInner(candidates, true, encodedIssuerName, checker,
+ keepGoing);
+ if (rv != Success) {
+ return rv;
+ }
+ if (keepGoing) {
+ rv = FindIssuerInner(candidates, false, encodedIssuerName, checker,
+ keepGoing);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+ }
+
+ return Success;
+}
+
+Result
+NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
+ const CertPolicyId& policy,
+ Input candidateCertDER,
+ /*out*/ TrustLevel& trustLevel)
+{
+ // XXX: This would be cleaner and more efficient if we could get the trust
+ // information without constructing a CERTCertificate here, but NSS doesn't
+ // expose it in any other easy-to-use fashion. The use of
+ // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
+ // performance problem because NSS will just find the existing
+ // CERTCertificate in its in-memory cache and return it.
+ SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER);
+ UniqueCERTCertificate candidateCert(
+ CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem,
+ nullptr, false, true));
+ if (!candidateCert) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+
+ // Check the certificate against the OneCRL cert blocklist
+ if (!mCertBlocklist) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ // The certificate blocklist currently only applies to TLS server
+ // certificates.
+ if (mCertDBTrustType == trustSSL) {
+ bool isCertRevoked;
+ nsresult nsrv = mCertBlocklist->IsCertRevoked(
+ candidateCert->derIssuer.data,
+ candidateCert->derIssuer.len,
+ candidateCert->serialNumber.data,
+ candidateCert->serialNumber.len,
+ candidateCert->derSubject.data,
+ candidateCert->derSubject.len,
+ candidateCert->derPublicKey.data,
+ candidateCert->derPublicKey.len,
+ &isCertRevoked);
+ if (NS_FAILED(nsrv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ if (isCertRevoked) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: certificate is in blocklist"));
+ return Result::ERROR_REVOKED_CERTIFICATE;
+ }
+ }
+
+ // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where
+ // SECSuccess means that there is a trust record and SECFailure means there
+ // is not a trust record. I looked at NSS's internal uses of
+ // CERT_GetCertTrust, and all that code uses the result as a boolean meaning
+ // "We have a trust record."
+ CERTCertTrust trust;
+ if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
+ uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
+
+ // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
+ // because we can have active distrust for either type of cert. Note that
+ // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
+ // relevant trust bit isn't set then that means the cert must be considered
+ // distrusted.
+ uint32_t relevantTrustBit =
+ endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA
+ : CERTDB_TRUSTED;
+ if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD)))
+ == CERTDB_TERMINAL_RECORD) {
+ trustLevel = TrustLevel::ActivelyDistrusted;
+ return Success;
+ }
+
+ // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't
+ // needed to consider end-entity certs to be their own trust anchors since
+ // Gecko implemented nsICertOverrideService.
+ if (flags & CERTDB_TRUSTED_CA) {
+ if (policy.IsAnyPolicy()) {
+ trustLevel = TrustLevel::TrustAnchor;
+ return Success;
+ }
+ if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
+ trustLevel = TrustLevel::TrustAnchor;
+ return Success;
+ }
+ }
+ }
+
+ trustLevel = TrustLevel::InheritsTrust;
+ return Success;
+}
+
+Result
+NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf, size_t digestBufLen)
+{
+ return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
+}
+
+static PRIntervalTime
+OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching)
+{
+ switch (ocspFetching) {
+ case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
+ return PR_SecondsToInterval(2);
+ case NSSCertDBTrustDomain::FetchOCSPForEV:
+ case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
+ return PR_SecondsToInterval(10);
+ // The rest of these are error cases. Assert in debug builds, but return
+ // the default value corresponding to 2 seconds in release builds.
+ case NSSCertDBTrustDomain::NeverFetchOCSP:
+ case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
+ PR_NOT_REACHED("we should never see this OCSPFetching type here");
+ break;
+ }
+
+ PR_NOT_REACHED("we're not handling every OCSPFetching type");
+ return PR_SecondsToInterval(2);
+}
+
+// Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
+// CERT_GetGeneralNameByType. Returns a non-Result::Success result on error,
+// Success with url == nullptr when an OCSP URI was not found, and Success with
+// url != nullptr when an OCSP URI was found. The output url will be owned
+// by the arena.
+static Result
+GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena,
+ Input aiaExtension,
+ /*out*/ char const*& url)
+{
+ MOZ_ASSERT(arena.get());
+ if (!arena.get()) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ url = nullptr;
+ SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension);
+ CERTAuthInfoAccess** aia =
+ CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem);
+ if (!aia) {
+ return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
+ }
+ for (size_t i = 0; aia[i]; ++i) {
+ if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
+ // NSS chooses the **last** OCSP URL; we choose the **first**
+ CERTGeneralName* current = aia[i]->location;
+ if (!current) {
+ continue;
+ }
+ do {
+ if (current->type == certURI) {
+ const SECItem& location = current->name.other;
+ // (location.len + 1) must be small enough to fit into a uint32_t,
+ // but we limit it to a smaller bound to reduce OOM risk.
+ if (location.len > 1024 || memchr(location.data, 0, location.len)) {
+ // Reject embedded nulls. (NSS doesn't do this)
+ return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
+ }
+ // Copy the non-null-terminated SECItem into a null-terminated string.
+ char* nullTerminatedURL(
+ static_cast<char*>(PORT_ArenaAlloc(arena.get(), location.len + 1)));
+ if (!nullTerminatedURL) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ memcpy(nullTerminatedURL, location.data, location.len);
+ nullTerminatedURL[location.len] = 0;
+ url = nullTerminatedURL;
+ return Success;
+ }
+ current = CERT_GetNextGeneralName(current);
+ } while (current != aia[i]->location);
+ }
+ }
+
+ return Success;
+}
+
+Result
+NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
+ const CertID& certID, Time time,
+ Duration validityDuration,
+ /*optional*/ const Input* stapledOCSPResponse,
+ /*optional*/ const Input* aiaExtension)
+{
+ // Actively distrusted certificates will have already been blocked by
+ // GetCertTrust.
+
+ // TODO: need to verify that IsRevoked isn't called for trust anchors AND
+ // that that fact is documented in mozillapkix.
+
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
+
+ // Bug 991815: The BR allow OCSP for intermediates to be up to one year old.
+ // Since this affects EV there is no reason why DV should be more strict
+ // so all intermediatates are allowed to have OCSP responses up to one year
+ // old.
+ uint16_t maxOCSPLifetimeInDays = 10;
+ if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
+ maxOCSPLifetimeInDays = 365;
+ }
+
+ // If we have a stapled OCSP response then the verification of that response
+ // determines the result unless the OCSP response is expired. We make an
+ // exception for expired responses because some servers, nginx in particular,
+ // are known to serve expired responses due to bugs.
+ // We keep track of the result of verifying the stapled response but don't
+ // immediately return failure if the response has expired.
+ //
+ // We only set the OCSP stapling status if we're validating the end-entity
+ // certificate. Non-end-entity certificates would always be
+ // OCSP_STAPLING_NONE unless/until we implement multi-stapling.
+ Result stapledOCSPResponseResult = Success;
+ if (stapledOCSPResponse) {
+ PR_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
+ bool expired;
+ stapledOCSPResponseResult =
+ VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
+ maxOCSPLifetimeInDays,
+ *stapledOCSPResponse,
+ ResponseWasStapled, expired);
+ if (stapledOCSPResponseResult == Success) {
+ // stapled OCSP response present and good
+ mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: stapled OCSP response: good"));
+ return Success;
+ }
+ if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
+ expired) {
+ // stapled OCSP response present but expired
+ mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: expired stapled OCSP response"));
+ } else {
+ // stapled OCSP response present but invalid for some reason
+ mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
+ return stapledOCSPResponseResult;
+ }
+ } else if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
+ // no stapled OCSP response
+ mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: no stapled OCSP response"));
+ }
+
+ Result cachedResponseResult = Success;
+ Time cachedResponseValidThrough(Time::uninitialized);
+ bool cachedResponsePresent = mOCSPCache.Get(certID, mOriginAttributes,
+ cachedResponseResult,
+ cachedResponseValidThrough);
+ if (cachedResponsePresent) {
+ if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: cached OCSP response: good"));
+ return Success;
+ }
+ // If we have a cached revoked response, use it.
+ if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
+ return Result::ERROR_REVOKED_CERTIFICATE;
+ }
+ // The cached response may indicate an unknown certificate or it may be
+ // expired. Don't return with either of these statuses yet - we may be
+ // able to fetch a more recent one.
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: cached OCSP response: error %d",
+ cachedResponseResult));
+ // When a good cached response has expired, it is more convenient
+ // to convert that to an error code and just deal with
+ // cachedResponseResult from here on out.
+ if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
+ cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
+ }
+ // We may have a cached indication of server failure. Ignore it if
+ // it has expired.
+ if (cachedResponseResult != Success &&
+ cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
+ cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
+ cachedResponseValidThrough < time) {
+ cachedResponseResult = Success;
+ cachedResponsePresent = false;
+ }
+ } else {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: no cached OCSP response"));
+ }
+ // At this point, if and only if cachedErrorResult is Success, there was no
+ // cached response.
+ PR_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
+ (cachedResponsePresent && cachedResponseResult != Success));
+
+ // If we have a fresh OneCRL Blocklist we can skip OCSP for CA certs
+ bool blocklistIsFresh;
+ nsresult nsrv = mCertBlocklist->IsBlocklistFresh(&blocklistIsFresh);
+ if (NS_FAILED(nsrv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ // TODO: We still need to handle the fallback for expired responses. But,
+ // if/when we disable OCSP fetching by default, it would be ambiguous whether
+ // security.OCSP.enable==0 means "I want the default" or "I really never want
+ // you to ever fetch OCSP."
+
+ Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS);
+
+ if ((mOCSPFetching == NeverFetchOCSP) ||
+ (validityDuration < shortLifetime) ||
+ (endEntityOrCA == EndEntityOrCA::MustBeCA &&
+ (mOCSPFetching == FetchOCSPForDVHardFail ||
+ mOCSPFetching == FetchOCSPForDVSoftFail ||
+ blocklistIsFresh))) {
+ // We're not going to be doing any fetching, so if there was a cached
+ // "unknown" response, say so.
+ if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
+ return Result::ERROR_OCSP_UNKNOWN_CERT;
+ }
+ // If we're doing hard-fail, we want to know if we have a cached response
+ // that has expired.
+ if (mOCSPFetching == FetchOCSPForDVHardFail &&
+ cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
+ return Result::ERROR_OCSP_OLD_RESPONSE;
+ }
+
+ return Success;
+ }
+
+ if (mOCSPFetching == LocalOnlyOCSPForEV) {
+ if (cachedResponseResult != Success) {
+ return cachedResponseResult;
+ }
+ return Result::ERROR_OCSP_UNKNOWN_CERT;
+ }
+
+ UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!arena) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+
+ Result rv;
+ const char* url = nullptr; // owned by the arena
+
+ if (aiaExtension) {
+ rv = GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, url);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+
+ if (!url) {
+ if (mOCSPFetching == FetchOCSPForEV ||
+ cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
+ return Result::ERROR_OCSP_UNKNOWN_CERT;
+ }
+ if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
+ return Result::ERROR_OCSP_OLD_RESPONSE;
+ }
+ if (stapledOCSPResponseResult != Success) {
+ return stapledOCSPResponseResult;
+ }
+
+ // Nothing to do if we don't have an OCSP responder URI for the cert; just
+ // assume it is good. Note that this is the confusing, but intended,
+ // interpretation of "strict" revocation checking in the face of a
+ // certificate that lacks an OCSP responder URI.
+ return Success;
+ }
+
+ // Only request a response if we didn't have a cached indication of failure
+ // (don't keep requesting responses from a failing server).
+ Input response;
+ bool attemptedRequest;
+ if (cachedResponseResult == Success ||
+ cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
+ cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
+ uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
+ size_t ocspRequestLength;
+ rv = CreateEncodedOCSPRequest(*this, certID, ocspRequest,
+ ocspRequestLength);
+ if (rv != Success) {
+ return rv;
+ }
+ SECItem ocspRequestItem = {
+ siBuffer,
+ ocspRequest,
+ static_cast<unsigned int>(ocspRequestLength)
+ };
+ // Owned by arena
+ SECItem* responseSECItem = nullptr;
+ Result tempRV =
+ DoOCSPRequest(arena, url, mOriginAttributes, &ocspRequestItem,
+ OCSPFetchingTypeToTimeoutTime(mOCSPFetching),
+ mOCSPGetConfig == CertVerifier::ocspGetEnabled,
+ responseSECItem);
+ MOZ_ASSERT((tempRV != Success) || responseSECItem);
+ if (tempRV != Success) {
+ rv = tempRV;
+ } else if (response.Init(responseSECItem->data, responseSECItem->len)
+ != Success) {
+ rv = Result::ERROR_OCSP_MALFORMED_RESPONSE; // too big
+ }
+ attemptedRequest = true;
+ } else {
+ rv = cachedResponseResult;
+ attemptedRequest = false;
+ }
+
+ if (response.GetLength() == 0) {
+ Result error = rv;
+ if (attemptedRequest) {
+ Time timeout(time);
+ if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
+ }
+ rv = mOCSPCache.Put(certID, mOriginAttributes, error, time, timeout);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+ if (mOCSPFetching != FetchOCSPForDVSoftFail) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: returning SECFailure after "
+ "OCSP request failure"));
+ return error;
+ }
+ if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: returning SECFailure from cached "
+ "response after OCSP request failure"));
+ return cachedResponseResult;
+ }
+ if (stapledOCSPResponseResult != Success) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: returning SECFailure from expired "
+ "stapled response after OCSP request failure"));
+ return stapledOCSPResponseResult;
+ }
+
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: returning SECSuccess after "
+ "OCSP request failure"));
+ return Success; // Soft fail -> success :(
+ }
+
+ // If the response from the network has expired but indicates a revoked
+ // or unknown certificate, PR_GetError() will return the appropriate error.
+ // We actually ignore expired here.
+ bool expired;
+ rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
+ maxOCSPLifetimeInDays,
+ response, ResponseIsFromNetwork,
+ expired);
+ if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse"));
+ return rv;
+ }
+
+ if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
+ rv == Result::ERROR_REVOKED_CERTIFICATE) {
+ return rv;
+ }
+ if (stapledOCSPResponseResult != Success) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: returning SECFailure from expired stapled "
+ "response after OCSP request verification failure"));
+ return stapledOCSPResponseResult;
+ }
+
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: end of CheckRevocation"));
+
+ return Success; // Soft fail -> success :(
+}
+
+Result
+NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
+ const CertID& certID, Time time, uint16_t maxLifetimeInDays,
+ Input encodedResponse, EncodedResponseSource responseSource,
+ /*out*/ bool& expired)
+{
+ Time thisUpdate(Time::uninitialized);
+ Time validThrough(Time::uninitialized);
+
+ // We use a try and fallback approach which first mandates good signature
+ // digest algorithms, then falls back to SHA-1 if this fails. If a delegated
+ // OCSP response signing certificate was issued with a SHA-1 signature,
+ // verification initially fails. We cache the failure and then re-use that
+ // result even when doing fallback (i.e. when weak signature digest algorithms
+ // should succeed). To address this we use an OCSPVerificationTrustDomain
+ // here, rather than using *this, to ensure verification succeeds for all
+ // allowed signature digest algorithms.
+ OCSPVerificationTrustDomain trustDomain(*this);
+ Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, time,
+ maxLifetimeInDays, encodedResponse,
+ expired, &thisUpdate, &validThrough);
+ // If a response was stapled and expired, we don't want to cache it. Return
+ // early to simplify the logic here.
+ if (responseSource == ResponseWasStapled && expired) {
+ PR_ASSERT(rv != Success);
+ return rv;
+ }
+ // validThrough is only trustworthy if the response successfully verifies
+ // or it indicates a revoked or unknown certificate.
+ // If this isn't the case, store an indication of failure (to prevent
+ // repeatedly requesting a response from a failing server).
+ if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
+ rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
+ validThrough = time;
+ if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
+ }
+ }
+ if (responseSource == ResponseIsFromNetwork ||
+ rv == Success ||
+ rv == Result::ERROR_REVOKED_CERTIFICATE ||
+ rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: caching OCSP response"));
+ Result putRV = mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate,
+ validThrough);
+ if (putRV != Success) {
+ return putRV;
+ }
+ }
+
+ return rv;
+}
+
+static const uint8_t CNNIC_ROOT_CA_SUBJECT_DATA[] =
+ "\x30\x32\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x0E\x30"
+ "\x0C\x06\x03\x55\x04\x0A\x13\x05\x43\x4E\x4E\x49\x43\x31\x13\x30\x11\x06"
+ "\x03\x55\x04\x03\x13\x0A\x43\x4E\x4E\x49\x43\x20\x52\x4F\x4F\x54";
+
+static const uint8_t CNNIC_EV_ROOT_CA_SUBJECT_DATA[] =
+ "\x30\x81\x8A\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x32"
+ "\x30\x30\x06\x03\x55\x04\x0A\x0C\x29\x43\x68\x69\x6E\x61\x20\x49\x6E\x74"
+ "\x65\x72\x6E\x65\x74\x20\x4E\x65\x74\x77\x6F\x72\x6B\x20\x49\x6E\x66\x6F"
+ "\x72\x6D\x61\x74\x69\x6F\x6E\x20\x43\x65\x6E\x74\x65\x72\x31\x47\x30\x45"
+ "\x06\x03\x55\x04\x03\x0C\x3E\x43\x68\x69\x6E\x61\x20\x49\x6E\x74\x65\x72"
+ "\x6E\x65\x74\x20\x4E\x65\x74\x77\x6F\x72\x6B\x20\x49\x6E\x66\x6F\x72\x6D"
+ "\x61\x74\x69\x6F\x6E\x20\x43\x65\x6E\x74\x65\x72\x20\x45\x56\x20\x43\x65"
+ "\x72\x74\x69\x66\x69\x63\x61\x74\x65\x73\x20\x52\x6F\x6F\x74";
+
+class WhitelistedCNNICHashBinarySearchComparator
+{
+public:
+ explicit WhitelistedCNNICHashBinarySearchComparator(const uint8_t* aTarget,
+ size_t aTargetLength)
+ : mTarget(aTarget)
+ {
+ MOZ_ASSERT(aTargetLength == CNNIC_WHITELIST_HASH_LEN,
+ "Hashes should be of the same length.");
+ }
+
+ int operator()(const WhitelistedCNNICHash val) const {
+ return memcmp(mTarget, val.hash, CNNIC_WHITELIST_HASH_LEN);
+ }
+
+private:
+ const uint8_t* mTarget;
+};
+
+static bool
+CertIsStartComOrWoSign(const CERTCertificate* cert)
+{
+ for (const DataAndLength& dn : StartComAndWoSignDNs) {
+ if (cert->derSubject.len == dn.len &&
+ PodEqual(cert->derSubject.data, dn.data, dn.len)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// If a certificate in the given chain appears to have been issued by one of
+// seven roots operated by StartCom and WoSign that are not trusted to issue new
+// certificates, verify that the end-entity has a notBefore date before 21
+// October 2016. If the value of notBefore is after this time, the chain is not
+// valid.
+// (NB: While there are seven distinct roots being checked for, two of them
+// share distinguished names, resulting in six distinct distinguished names to
+// actually look for.)
+static Result
+CheckForStartComOrWoSign(const UniqueCERTCertList& certChain)
+{
+ if (CERT_LIST_EMPTY(certChain)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ const CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certChain);
+ if (!endEntityNode || !endEntityNode->cert) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ PRTime notBefore;
+ PRTime notAfter;
+ if (CERT_GetCertTimes(endEntityNode->cert, &notBefore, &notAfter)
+ != SECSuccess) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ // PRTime is microseconds since the epoch, whereas JS time is milliseconds.
+ // (new Date("2016-10-21T00:00:00Z")).getTime() * 1000
+ static const PRTime OCTOBER_21_2016 = 1477008000000000;
+ if (notBefore <= OCTOBER_21_2016) {
+ return Success;
+ }
+
+ for (const CERTCertListNode* node = CERT_LIST_HEAD(certChain);
+ !CERT_LIST_END(node, certChain); node = CERT_LIST_NEXT(node)) {
+ if (!node || !node->cert) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ if (CertIsStartComOrWoSign(node->cert)) {
+ return Result::ERROR_REVOKED_CERTIFICATE;
+ }
+ }
+ return Success;
+}
+
+Result
+NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time)
+{
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: IsChainValid"));
+
+ UniqueCERTCertList certList;
+ SECStatus srv = ConstructCERTCertListFromReversedDERArray(certArray,
+ certList);
+ if (srv != SECSuccess) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+ if (CERT_LIST_EMPTY(certList)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result rv = CheckForStartComOrWoSign(certList);
+ if (rv != Success) {
+ return rv;
+ }
+
+ // If the certificate appears to have been issued by a CNNIC root, only allow
+ // it if it is on the whitelist.
+ CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
+ if (!rootNode) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ CERTCertificate* root = rootNode->cert;
+ if (!root) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ if ((root->derSubject.len == sizeof(CNNIC_ROOT_CA_SUBJECT_DATA) - 1 &&
+ memcmp(root->derSubject.data, CNNIC_ROOT_CA_SUBJECT_DATA,
+ root->derSubject.len) == 0) ||
+ (root->derSubject.len == sizeof(CNNIC_EV_ROOT_CA_SUBJECT_DATA) - 1 &&
+ memcmp(root->derSubject.data, CNNIC_EV_ROOT_CA_SUBJECT_DATA,
+ root->derSubject.len) == 0)) {
+ CERTCertListNode* certNode = CERT_LIST_HEAD(certList);
+ if (!certNode) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ CERTCertificate* cert = certNode->cert;
+ if (!cert) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ Digest digest;
+ nsresult nsrv = digest.DigestBuf(SEC_OID_SHA256, cert->derCert.data,
+ cert->derCert.len);
+ if (NS_FAILED(nsrv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ const uint8_t* certHash(
+ BitwiseCast<uint8_t*, unsigned char*>(digest.get().data));
+ size_t certHashLen = digest.get().len;
+ size_t unused;
+ if (!mozilla::BinarySearchIf(WhitelistedCNNICHashes, 0,
+ ArrayLength(WhitelistedCNNICHashes),
+ WhitelistedCNNICHashBinarySearchComparator(
+ certHash, certHashLen),
+ &unused)) {
+ return Result::ERROR_REVOKED_CERTIFICATE;
+ }
+ }
+
+ bool isBuiltInRoot = false;
+ rv = IsCertBuiltInRoot(root, isBuiltInRoot);
+ if (rv != Success) {
+ return rv;
+ }
+ bool skipPinningChecksBecauseOfMITMMode =
+ (!isBuiltInRoot && mPinningMode == CertVerifier::pinningAllowUserCAMITM);
+ // If mHostname isn't set, we're not verifying in the context of a TLS
+ // handshake, so don't verify HPKP in those cases.
+ if (mHostname && (mPinningMode != CertVerifier::pinningDisabled) &&
+ !skipPinningChecksBecauseOfMITMMode) {
+ bool enforceTestMode =
+ (mPinningMode == CertVerifier::pinningEnforceTestMode);
+ bool chainHasValidPins;
+ nsresult nsrv = PublicKeyPinningService::ChainHasValidPins(
+ certList, mHostname, time, enforceTestMode, chainHasValidPins,
+ mPinningTelemetryInfo);
+ if (NS_FAILED(nsrv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ if (!chainHasValidPins) {
+ return Result::ERROR_KEY_PINNING_FAILURE;
+ }
+ }
+
+ mBuiltChain = Move(certList);
+
+ return Success;
+}
+
+Result
+NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm aAlg,
+ EndEntityOrCA endEntityOrCA,
+ Time notBefore)
+{
+ // (new Date("2016-01-01T00:00:00Z")).getTime() / 1000
+ static const Time JANUARY_FIRST_2016 = TimeFromEpochInSeconds(1451606400);
+
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: CheckSignatureDigestAlgorithm"));
+ if (aAlg == DigestAlgorithm::sha1) {
+ switch (mSHA1Mode) {
+ case CertVerifier::SHA1Mode::Forbidden:
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("SHA-1 certificate rejected"));
+ return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ case CertVerifier::SHA1Mode::ImportedRootOrBefore2016:
+ if (JANUARY_FIRST_2016 <= notBefore) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Post-2015 SHA-1 certificate rejected"));
+ return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ }
+ break;
+ case CertVerifier::SHA1Mode::Allowed:
+ // Enforcing that the resulting chain uses an imported root is only
+ // possible at a higher level. This is done in CertVerifier::VerifyCert.
+ case CertVerifier::SHA1Mode::ImportedRoot:
+ default:
+ break;
+ // MSVC warns unless we explicitly handle this now-unused option.
+ case CertVerifier::SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
+ MOZ_ASSERT_UNREACHABLE("unexpected SHA1Mode type");
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ }
+
+ return Success;
+}
+
+Result
+NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
+ EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
+{
+ if (modulusSizeInBits < mMinRSABits) {
+ return Result::ERROR_INADEQUATE_KEY_SIZE;
+ }
+ return Success;
+}
+
+Result
+NSSCertDBTrustDomain::VerifyRSAPKCS1SignedDigest(
+ const SignedDigest& signedDigest,
+ Input subjectPublicKeyInfo)
+{
+ return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+ mPinArg);
+}
+
+Result
+NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
+ EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve)
+{
+ switch (curve) {
+ case NamedCurve::secp256r1: // fall through
+ case NamedCurve::secp384r1: // fall through
+ case NamedCurve::secp521r1:
+ return Success;
+ }
+
+ return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
+}
+
+Result
+NSSCertDBTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+ Input subjectPublicKeyInfo)
+{
+ return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+ mPinArg);
+}
+
+Result
+NSSCertDBTrustDomain::CheckValidityIsAcceptable(Time notBefore, Time notAfter,
+ EndEntityOrCA endEntityOrCA,
+ KeyPurposeId keyPurpose)
+{
+ if (endEntityOrCA != EndEntityOrCA::MustBeEndEntity) {
+ return Success;
+ }
+ if (keyPurpose == KeyPurposeId::id_kp_OCSPSigning) {
+ return Success;
+ }
+
+ Duration DURATION_27_MONTHS_PLUS_SLOP((2 * 365 + 3 * 31 + 7) *
+ Time::ONE_DAY_IN_SECONDS);
+ Duration maxValidityDuration(UINT64_MAX);
+ Duration validityDuration(notBefore, notAfter);
+
+ switch (mValidityCheckingMode) {
+ case ValidityCheckingMode::CheckingOff:
+ return Success;
+ case ValidityCheckingMode::CheckForEV:
+ // The EV Guidelines say the maximum is 27 months, but we use a slightly
+ // higher limit here to (hopefully) minimize compatibility breakage.
+ maxValidityDuration = DURATION_27_MONTHS_PLUS_SLOP;
+ break;
+ default:
+ PR_NOT_REACHED("We're not handling every ValidityCheckingMode type");
+ }
+
+ if (validityDuration > maxValidityDuration) {
+ return Result::ERROR_VALIDITY_TOO_LONG;
+ }
+
+ return Success;
+}
+
+Result
+NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
+ /*out*/ bool& matches)
+{
+ // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
+ static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
+ // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
+ static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
+
+ switch (mNetscapeStepUpPolicy) {
+ case NetscapeStepUpPolicy::AlwaysMatch:
+ matches = true;
+ return Success;
+ case NetscapeStepUpPolicy::MatchBefore23August2016:
+ matches = notBefore < AUGUST_23_2016;
+ return Success;
+ case NetscapeStepUpPolicy::MatchBefore23August2015:
+ matches = notBefore < AUGUST_23_2015;
+ return Success;
+ case NetscapeStepUpPolicy::NeverMatch:
+ matches = false;
+ return Success;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
+ }
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+void
+NSSCertDBTrustDomain::ResetAccumulatedState()
+{
+ mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
+ mSCTListFromOCSPStapling = nullptr;
+ mSCTListFromCertificate = nullptr;
+}
+
+static Input
+SECItemToInput(const UniqueSECItem& item)
+{
+ Input result;
+ if (item) {
+ MOZ_ASSERT(item->type == siBuffer);
+ Result rv = result.Init(item->data, item->len);
+ // As used here, |item| originally comes from an Input,
+ // so there should be no issues converting it back.
+ MOZ_ASSERT(rv == Success);
+ Unused << rv; // suppresses warnings in release builds
+ }
+ return result;
+}
+
+Input
+NSSCertDBTrustDomain::GetSCTListFromCertificate() const
+{
+ return SECItemToInput(mSCTListFromCertificate);
+}
+
+Input
+NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const
+{
+ return SECItemToInput(mSCTListFromOCSPStapling);
+}
+
+void
+NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
+ Input extensionData)
+{
+ UniqueSECItem* out = nullptr;
+ switch (extension) {
+ case AuxiliaryExtension::EmbeddedSCTList:
+ out = &mSCTListFromCertificate;
+ break;
+ case AuxiliaryExtension::SCTListFromOCSPResponse:
+ out = &mSCTListFromOCSPStapling;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
+ }
+ if (out) {
+ SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
+ out->reset(SECITEM_DupItem(&extensionDataItem));
+ }
+}
+
+SECStatus
+InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules)
+{
+ // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
+ // module by NSS_Initialize because we will load it in InstallLoadableRoots
+ // later. It also allows us to work around a bug in the system NSS in
+ // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
+ // "/usr/lib/nss/libnssckbi.so".
+ uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
+ if (readOnly) {
+ flags |= NSS_INIT_READONLY;
+ }
+ if (!loadPKCS11Modules) {
+ flags |= NSS_INIT_NOMODDB;
+ }
+ return ::NSS_Initialize(dir, "", "", SECMOD_DB, flags);
+}
+
+void
+DisableMD5()
+{
+ NSS_SetAlgorithmPolicy(SEC_OID_MD5,
+ 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
+ NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
+ 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
+ NSS_SetAlgorithmPolicy(SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC,
+ 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
+}
+
+SECStatus
+LoadLoadableRoots(/*optional*/ const char* dir, const char* modNameUTF8)
+{
+ PR_ASSERT(modNameUTF8);
+
+ if (!modNameUTF8) {
+ PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+ return SECFailure;
+ }
+
+ UniquePtr<char, void(&)(char*)>
+ fullLibraryPath(PR_GetLibraryName(dir, "nssckbi"), PR_FreeLibraryName);
+ if (!fullLibraryPath) {
+ return SECFailure;
+ }
+
+ // Escape the \ and " characters.
+ nsAutoCString escapedFullLibraryPath(fullLibraryPath.get());
+ escapedFullLibraryPath.ReplaceSubstring("\\", "\\\\");
+ escapedFullLibraryPath.ReplaceSubstring("\"", "\\\"");
+ if (escapedFullLibraryPath.IsEmpty()) {
+ return SECFailure;
+ }
+
+ // If a module exists with the same name, delete it.
+ int modType;
+ SECMOD_DeleteModule(modNameUTF8, &modType);
+
+ nsAutoCString pkcs11ModuleSpec;
+ pkcs11ModuleSpec.AppendPrintf("name=\"%s\" library=\"%s\"", modNameUTF8,
+ escapedFullLibraryPath.get());
+ if (pkcs11ModuleSpec.IsEmpty()) {
+ return SECFailure;
+ }
+
+ UniqueSECMODModule rootsModule(
+ SECMOD_LoadUserModule(const_cast<char*>(pkcs11ModuleSpec.get()), nullptr,
+ false));
+ if (!rootsModule) {
+ return SECFailure;
+ }
+
+ if (!rootsModule->loaded) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+void
+UnloadLoadableRoots(const char* modNameUTF8)
+{
+ PR_ASSERT(modNameUTF8);
+ UniqueSECMODModule rootsModule(SECMOD_FindModule(modNameUTF8));
+
+ if (rootsModule) {
+ SECMOD_UnloadUserModule(rootsModule.get());
+ }
+}
+
+nsresult
+DefaultServerNicknameForCert(const CERTCertificate* cert,
+ /*out*/ nsCString& nickname)
+{
+ MOZ_ASSERT(cert);
+ NS_ENSURE_ARG_POINTER(cert);
+
+ UniquePORTString baseName(CERT_GetCommonName(&cert->subject));
+ if (!baseName) {
+ baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject));
+ }
+ if (!baseName) {
+ baseName = UniquePORTString(CERT_GetOrgName(&cert->subject));
+ }
+ if (!baseName) {
+ baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject));
+ }
+ if (!baseName) {
+ baseName = UniquePORTString(CERT_GetStateName(&cert->subject));
+ }
+ if (!baseName) {
+ baseName = UniquePORTString(CERT_GetCountryName(&cert->subject));
+ }
+ if (!baseName) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // This function is only used in contexts where a failure to find a suitable
+ // nickname does not block the overall task from succeeding.
+ // As such, we use an arbitrary limit to prevent this nickname searching
+ // process from taking forever.
+ static const uint32_t ARBITRARY_LIMIT = 500;
+ for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) {
+ nickname = baseName.get();
+ if (count != 1) {
+ nickname.AppendPrintf(" #%u", count);
+ }
+ if (nickname.IsEmpty()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject,
+ cert->dbhandle);
+ if (!conflict) {
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+/**
+ * Given a list of certificates representing a verified certificate path from an
+ * end-entity certificate to a trust anchor, imports the intermediate
+ * certificates into the permanent certificate database. This is an attempt to
+ * cope with misconfigured servers that don't include the appropriate
+ * intermediate certificates in the TLS handshake.
+ *
+ * @param certList the verified certificate list
+ */
+void
+SaveIntermediateCerts(const UniqueCERTCertList& certList)
+{
+ if (!certList) {
+ return;
+ }
+
+ UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (!slot) {
+ return;
+ }
+
+ bool isEndEntity = true;
+ for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
+ !CERT_LIST_END(node, certList);
+ node = CERT_LIST_NEXT(node)) {
+ if (isEndEntity) {
+ // Skip the end-entity; we only want to store intermediates
+ isEndEntity = false;
+ continue;
+ }
+
+ if (node->cert->slot) {
+ // This cert was found on a token; no need to remember it in the permanent
+ // database.
+ continue;
+ }
+
+ if (node->cert->isperm) {
+ // We don't need to remember certs already stored in perm db.
+ continue;
+ }
+
+ // No need to save the trust anchor - it's either already a permanent
+ // certificate or it's the Microsoft Family Safety root or an enterprise
+ // root temporarily imported via the child mode or enterprise root features.
+ // We don't want to import these because they're intended to be temporary
+ // (and because importing them happens to reset their trust settings, which
+ // breaks these features).
+ if (node == CERT_LIST_TAIL(certList)) {
+ continue;
+ }
+
+ nsAutoCString nickname;
+ nsresult rv = DefaultServerNicknameForCert(node->cert, nickname);
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+
+ // As mentioned in the documentation of this function, we're importing only
+ // to cope with misconfigured servers. As such, we ignore the return value
+ // below, since it doesn't really matter if the import fails.
+ Unused << PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE,
+ nickname.get(), false);
+ }
+}
+
+} } // namespace mozilla::psm
diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h
new file mode 100644
index 000000000..15a5a4a2c
--- /dev/null
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -0,0 +1,201 @@
+/* -*- 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 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/. */
+
+#ifndef NSSCertDBTrustDomain_h
+#define NSSCertDBTrustDomain_h
+
+#include "CertVerifier.h"
+#include "ScopedNSSTypes.h"
+#include "mozilla/BasePrincipal.h"
+#include "nsICertBlocklist.h"
+#include "nsString.h"
+#include "pkix/pkixtypes.h"
+#include "secmodt.h"
+
+namespace mozilla { namespace psm {
+
+enum class ValidityCheckingMode {
+ CheckingOff = 0,
+ CheckForEV = 1,
+};
+
+// Policy options for matching id-Netscape-stepUp with id-kp-serverAuth (for CA
+// certificates only):
+// * Always match: the step-up OID is considered equivalent to serverAuth
+// * Match before 23 August 2016: the OID is considered equivalent if the
+// certificate's notBefore is before 23 August 2016
+// * Match before 23 August 2015: similarly, but for 23 August 2015
+// * Never match: the OID is never considered equivalent to serverAuth
+enum class NetscapeStepUpPolicy : uint32_t {
+ AlwaysMatch = 0,
+ MatchBefore23August2016 = 1,
+ MatchBefore23August2015 = 2,
+ NeverMatch = 3,
+};
+
+SECStatus InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules);
+
+void DisableMD5();
+
+extern const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[];
+
+// The dir parameter is the path to the directory containing the NSS builtin
+// roots module. Usually this is the same as the path to the other NSS shared
+// libraries. If it is null then the (library) path will be searched.
+//
+// The modNameUTF8 parameter should usually be
+// BUILTIN_ROOTS_MODULE_DEFAULT_NAME.
+SECStatus LoadLoadableRoots(/*optional*/ const char* dir,
+ const char* modNameUTF8);
+
+void UnloadLoadableRoots(const char* modNameUTF8);
+
+nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
+ /*out*/ nsCString& nickname);
+
+void SaveIntermediateCerts(const UniqueCERTCertList& certList);
+
+class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain
+{
+
+public:
+ typedef mozilla::pkix::Result Result;
+
+ enum OCSPFetching {
+ NeverFetchOCSP = 0,
+ FetchOCSPForDVSoftFail = 1,
+ FetchOCSPForDVHardFail = 2,
+ FetchOCSPForEV = 3,
+ LocalOnlyOCSPForEV = 4,
+ };
+
+ NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
+ OCSPCache& ocspCache, void* pinArg,
+ CertVerifier::OcspGetConfig ocspGETConfig,
+ uint32_t certShortLifetimeInDays,
+ CertVerifier::PinningMode pinningMode,
+ unsigned int minRSABits,
+ ValidityCheckingMode validityCheckingMode,
+ CertVerifier::SHA1Mode sha1Mode,
+ NetscapeStepUpPolicy netscapeStepUpPolicy,
+ const NeckoOriginAttributes& originAttributes,
+ UniqueCERTCertList& builtChain,
+ /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
+ /*optional*/ const char* hostname = nullptr);
+
+ virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
+ IssuerChecker& checker,
+ mozilla::pkix::Time time) override;
+
+ virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertPolicyId& policy,
+ mozilla::pkix::Input candidateCertDER,
+ /*out*/ mozilla::pkix::TrustLevel& trustLevel)
+ override;
+
+ virtual Result CheckSignatureDigestAlgorithm(
+ mozilla::pkix::DigestAlgorithm digestAlg,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::Time notBefore) override;
+
+ virtual Result CheckRSAPublicKeyModulusSizeInBits(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ unsigned int modulusSizeInBits) override;
+
+ virtual Result VerifyRSAPKCS1SignedDigest(
+ const mozilla::pkix::SignedDigest& signedDigest,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual Result CheckECDSACurveIsAcceptable(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::NamedCurve curve) override;
+
+ virtual Result VerifyECDSASignedDigest(
+ const mozilla::pkix::SignedDigest& signedDigest,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual Result DigestBuf(mozilla::pkix::Input item,
+ mozilla::pkix::DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf,
+ size_t digestBufLen) override;
+
+ virtual Result CheckValidityIsAcceptable(
+ mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::KeyPurposeId keyPurpose) override;
+
+ virtual Result NetscapeStepUpMatchesServerAuth(
+ mozilla::pkix::Time notBefore,
+ /*out*/ bool& matches) override;
+
+ virtual Result CheckRevocation(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertID& certID,
+ mozilla::pkix::Time time,
+ mozilla::pkix::Duration validityDuration,
+ /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
+ /*optional*/ const mozilla::pkix::Input* aiaExtension)
+ override;
+
+ virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
+ mozilla::pkix::Time time) override;
+
+ virtual void NoteAuxiliaryExtension(
+ mozilla::pkix::AuxiliaryExtension extension,
+ mozilla::pkix::Input extensionData) override;
+
+ // Resets the OCSP stapling status and SCT lists accumulated during
+ // the chain building.
+ void ResetAccumulatedState();
+
+ CertVerifier::OCSPStaplingStatus GetOCSPStaplingStatus() const
+ {
+ return mOCSPStaplingStatus;
+ }
+
+ // SCT lists (see Certificate Transparency) extracted during
+ // certificate verification. Note that the returned Inputs are invalidated
+ // the next time a chain is built and by ResetAccumulatedState method
+ // (and when the TrustDomain object is destroyed).
+
+ mozilla::pkix::Input GetSCTListFromCertificate() const;
+ mozilla::pkix::Input GetSCTListFromOCSPStapling() const;
+
+private:
+ enum EncodedResponseSource {
+ ResponseIsFromNetwork = 1,
+ ResponseWasStapled = 2
+ };
+ Result VerifyAndMaybeCacheEncodedOCSPResponse(
+ const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
+ uint16_t maxLifetimeInDays, mozilla::pkix::Input encodedResponse,
+ EncodedResponseSource responseSource, /*out*/ bool& expired);
+
+ const SECTrustType mCertDBTrustType;
+ const OCSPFetching mOCSPFetching;
+ OCSPCache& mOCSPCache; // non-owning!
+ void* mPinArg; // non-owning!
+ const CertVerifier::OcspGetConfig mOCSPGetConfig;
+ const uint32_t mCertShortLifetimeInDays;
+ CertVerifier::PinningMode mPinningMode;
+ const unsigned int mMinRSABits;
+ ValidityCheckingMode mValidityCheckingMode;
+ CertVerifier::SHA1Mode mSHA1Mode;
+ NetscapeStepUpPolicy mNetscapeStepUpPolicy;
+ const NeckoOriginAttributes& mOriginAttributes;
+ UniqueCERTCertList& mBuiltChain; // non-owning
+ PinningTelemetryInfo* mPinningTelemetryInfo;
+ const char* mHostname; // non-owning - only used for pinning checks
+ nsCOMPtr<nsICertBlocklist> mCertBlocklist;
+ CertVerifier::OCSPStaplingStatus mOCSPStaplingStatus;
+ // Certificate Transparency data extracted during certificate verification
+ UniqueSECItem mSCTListFromCertificate;
+ UniqueSECItem mSCTListFromOCSPStapling;
+};
+
+} } // namespace mozilla::psm
+
+#endif // NSSCertDBTrustDomain_h
diff --git a/security/certverifier/OCSPCache.cpp b/security/certverifier/OCSPCache.cpp
new file mode 100644
index 000000000..cae58eba3
--- /dev/null
+++ b/security/certverifier/OCSPCache.cpp
@@ -0,0 +1,343 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+/* Copyright 2013 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "OCSPCache.h"
+
+#include <limits>
+
+#include "NSSCertDBTrustDomain.h"
+#include "pk11pub.h"
+#include "pkix/pkixnss.h"
+#include "ScopedNSSTypes.h"
+#include "secerr.h"
+
+extern mozilla::LazyLogModule gCertVerifierLog;
+
+using namespace mozilla::pkix;
+
+namespace mozilla { namespace psm {
+
+typedef mozilla::pkix::Result Result;
+
+static SECStatus
+DigestLength(UniquePK11Context& context, uint32_t length)
+{
+ // Restrict length to 2 bytes because it should be big enough for all
+ // inputs this code will actually see and that it is well-defined and
+ // type-size-independent.
+ if (length >= 65536) {
+ return SECFailure;
+ }
+ unsigned char array[2];
+ array[0] = length & 255;
+ array[1] = (length >> 8) & 255;
+
+ return PK11_DigestOp(context.get(), array, MOZ_ARRAY_LENGTH(array));
+}
+
+// Let derIssuer be the DER encoding of the issuer of certID.
+// Let derPublicKey be the DER encoding of the public key of certID.
+// Let serialNumber be the bytes of the serial number of certID.
+// Let serialNumberLen be the number of bytes of serialNumber.
+// Let firstPartyDomain be the first party domain of originAttributes.
+// It is only non-empty when "privacy.firstParty.isolate" is enabled, in order
+// to isolate OCSP cache by first party.
+// Let firstPartyDomainLen be the number of bytes of firstPartyDomain.
+// The value calculated is SHA384(derIssuer || derPublicKey || serialNumberLen
+// || serialNumber || firstPartyDomainLen || firstPartyDomain).
+// Because the DER encodings include the length of the data encoded, and we also
+// include the length of serialNumber and originAttributes, there do not exist
+// A(derIssuerA, derPublicKeyA, serialNumberLenA, serialNumberA,
+// originAttributesLenA, originAttributesA) and B(derIssuerB, derPublicKeyB,
+// serialNumberLenB, serialNumberB, originAttributesLenB, originAttributesB)
+// such that the concatenation of each tuple results in the same string of
+// bytes but where each part in A is not equal to its counterpart in B. This is
+// important because as a result it is computationally infeasible to find
+// collisions that would subvert this cache (given that SHA384 is a
+// cryptographically-secure hash function).
+static SECStatus
+CertIDHash(SHA384Buffer& buf, const CertID& certID,
+ const NeckoOriginAttributes& originAttributes)
+{
+ UniquePK11Context context(PK11_CreateDigestContext(SEC_OID_SHA384));
+ if (!context) {
+ return SECFailure;
+ }
+ SECStatus rv = PK11_DigestBegin(context.get());
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ SECItem certIDIssuer = UnsafeMapInputToSECItem(certID.issuer);
+ rv = PK11_DigestOp(context.get(), certIDIssuer.data, certIDIssuer.len);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ SECItem certIDIssuerSubjectPublicKeyInfo =
+ UnsafeMapInputToSECItem(certID.issuerSubjectPublicKeyInfo);
+ rv = PK11_DigestOp(context.get(), certIDIssuerSubjectPublicKeyInfo.data,
+ certIDIssuerSubjectPublicKeyInfo.len);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ SECItem certIDSerialNumber =
+ UnsafeMapInputToSECItem(certID.serialNumber);
+ rv = DigestLength(context, certIDSerialNumber.len);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ rv = PK11_DigestOp(context.get(), certIDSerialNumber.data,
+ certIDSerialNumber.len);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ // OCSP should not be isolated by containers.
+ NS_ConvertUTF16toUTF8 firstPartyDomain(originAttributes.mFirstPartyDomain);
+ if (!firstPartyDomain.IsEmpty()) {
+ rv = DigestLength(context, firstPartyDomain.Length());
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ rv = PK11_DigestOp(context.get(),
+ BitwiseCast<const unsigned char*>(firstPartyDomain.get()),
+ firstPartyDomain.Length());
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+ uint32_t outLen = 0;
+ rv = PK11_DigestFinal(context.get(), buf, &outLen, SHA384_LENGTH);
+ if (outLen != SHA384_LENGTH) {
+ return SECFailure;
+ }
+ return rv;
+}
+
+Result
+OCSPCache::Entry::Init(const CertID& aCertID,
+ const NeckoOriginAttributes& aOriginAttributes)
+{
+ SECStatus srv = CertIDHash(mIDHash, aCertID, aOriginAttributes);
+ if (srv != SECSuccess) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+ return Success;
+}
+
+OCSPCache::OCSPCache()
+ : mMutex("OCSPCache-mutex")
+{
+}
+
+OCSPCache::~OCSPCache()
+{
+ Clear();
+}
+
+// Returns false with index in an undefined state if no matching entry was
+// found.
+bool
+OCSPCache::FindInternal(const CertID& aCertID,
+ const NeckoOriginAttributes& aOriginAttributes,
+ /*out*/ size_t& index,
+ const MutexAutoLock& /* aProofOfLock */)
+{
+ if (mEntries.length() == 0) {
+ return false;
+ }
+
+ SHA384Buffer idHash;
+ SECStatus rv = CertIDHash(idHash, aCertID, aOriginAttributes);
+ if (rv != SECSuccess) {
+ return false;
+ }
+
+ // mEntries is sorted with the most-recently-used entry at the end.
+ // Thus, searching from the end will often be fastest.
+ index = mEntries.length();
+ while (index > 0) {
+ --index;
+ if (memcmp(mEntries[index]->mIDHash, idHash, SHA384_LENGTH) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static inline void
+LogWithCertID(const char* aMessage, const CertID& aCertID,
+ const NeckoOriginAttributes& aOriginAttributes)
+{
+ NS_ConvertUTF16toUTF8 firstPartyDomain(aOriginAttributes.mFirstPartyDomain);
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ (aMessage, &aCertID, firstPartyDomain.get()));
+}
+
+void
+OCSPCache::MakeMostRecentlyUsed(size_t aIndex,
+ const MutexAutoLock& /* aProofOfLock */)
+{
+ Entry* entry = mEntries[aIndex];
+ // Since mEntries is sorted with the most-recently-used entry at the end,
+ // aIndex is likely to be near the end, so this is likely to be fast.
+ mEntries.erase(mEntries.begin() + aIndex);
+ // erase() does not shrink or realloc memory, so the append below should
+ // always succeed.
+ MOZ_RELEASE_ASSERT(mEntries.append(entry));
+}
+
+bool
+OCSPCache::Get(const CertID& aCertID,
+ const NeckoOriginAttributes& aOriginAttributes,
+ Result& aResult, Time& aValidThrough)
+{
+ MutexAutoLock lock(mMutex);
+
+ size_t index;
+ if (!FindInternal(aCertID, aOriginAttributes, index, lock)) {
+ LogWithCertID("OCSPCache::Get(%p,\"%s\") not in cache", aCertID,
+ aOriginAttributes);
+ return false;
+ }
+ LogWithCertID("OCSPCache::Get(%p,\"%s\") in cache", aCertID,
+ aOriginAttributes);
+ aResult = mEntries[index]->mResult;
+ aValidThrough = mEntries[index]->mValidThrough;
+ MakeMostRecentlyUsed(index, lock);
+ return true;
+}
+
+Result
+OCSPCache::Put(const CertID& aCertID,
+ const NeckoOriginAttributes& aOriginAttributes,
+ Result aResult, Time aThisUpdate, Time aValidThrough)
+{
+ MutexAutoLock lock(mMutex);
+
+ size_t index;
+ if (FindInternal(aCertID, aOriginAttributes, index, lock)) {
+ // Never replace an entry indicating a revoked certificate.
+ if (mEntries[index]->mResult == Result::ERROR_REVOKED_CERTIFICATE) {
+ LogWithCertID("OCSPCache::Put(%p, \"%s\") already in cache as revoked - "
+ "not replacing", aCertID, aOriginAttributes);
+ MakeMostRecentlyUsed(index, lock);
+ return Success;
+ }
+
+ // Never replace a newer entry with an older one unless the older entry
+ // indicates a revoked certificate, which we want to remember.
+ if (mEntries[index]->mThisUpdate > aThisUpdate &&
+ aResult != Result::ERROR_REVOKED_CERTIFICATE) {
+ LogWithCertID("OCSPCache::Put(%p, \"%s\") already in cache with more "
+ "recent validity - not replacing", aCertID,
+ aOriginAttributes);
+ MakeMostRecentlyUsed(index, lock);
+ return Success;
+ }
+
+ // Only known good responses or responses indicating an unknown
+ // or revoked certificate should replace previously known responses.
+ if (aResult != Success &&
+ aResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
+ aResult != Result::ERROR_REVOKED_CERTIFICATE) {
+ LogWithCertID("OCSPCache::Put(%p, \"%s\") already in cache - not "
+ "replacing with less important status", aCertID,
+ aOriginAttributes);
+ MakeMostRecentlyUsed(index, lock);
+ return Success;
+ }
+
+ LogWithCertID("OCSPCache::Put(%p, \"%s\") already in cache - replacing",
+ aCertID, aOriginAttributes);
+ mEntries[index]->mResult = aResult;
+ mEntries[index]->mThisUpdate = aThisUpdate;
+ mEntries[index]->mValidThrough = aValidThrough;
+ MakeMostRecentlyUsed(index, lock);
+ return Success;
+ }
+
+ if (mEntries.length() == MaxEntries) {
+ LogWithCertID("OCSPCache::Put(%p, \"%s\") too full - evicting an entry",
+ aCertID, aOriginAttributes);
+ for (Entry** toEvict = mEntries.begin(); toEvict != mEntries.end();
+ toEvict++) {
+ // Never evict an entry that indicates a revoked or unknokwn certificate,
+ // because revoked responses are more security-critical to remember.
+ if ((*toEvict)->mResult != Result::ERROR_REVOKED_CERTIFICATE &&
+ (*toEvict)->mResult != Result::ERROR_OCSP_UNKNOWN_CERT) {
+ delete *toEvict;
+ mEntries.erase(toEvict);
+ break;
+ }
+ }
+ // Well, we tried, but apparently everything is revoked or unknown.
+ // We don't want to remove a cached revoked or unknown response. If we're
+ // trying to insert a good response, we can just return "successfully"
+ // without doing so. This means we'll lose some speed, but it's not a
+ // security issue. If we're trying to insert a revoked or unknown response,
+ // we can't. We should return with an error that causes the current
+ // verification to fail.
+ if (mEntries.length() == MaxEntries) {
+ return aResult;
+ }
+ }
+
+ Entry* newEntry = new (std::nothrow) Entry(aResult, aThisUpdate,
+ aValidThrough);
+ // Normally we don't have to do this in Gecko, because OOM is fatal.
+ // However, if we want to embed this in another project, OOM might not
+ // be fatal, so handle this case.
+ if (!newEntry) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ Result rv = newEntry->Init(aCertID, aOriginAttributes);
+ if (rv != Success) {
+ delete newEntry;
+ return rv;
+ }
+ if (!mEntries.append(newEntry)) {
+ delete newEntry;
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ LogWithCertID("OCSPCache::Put(%p, \"%s\") added to cache", aCertID,
+ aOriginAttributes);
+ return Success;
+}
+
+void
+OCSPCache::Clear()
+{
+ MutexAutoLock lock(mMutex);
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("OCSPCache::Clear: clearing cache"));
+ // First go through and delete the memory being pointed to by the pointers
+ // in the vector.
+ for (Entry** entry = mEntries.begin(); entry < mEntries.end();
+ entry++) {
+ delete *entry;
+ }
+ // Then remove the pointers themselves.
+ mEntries.clearAndFree();
+}
+
+} } // namespace mozilla::psm
diff --git a/security/certverifier/OCSPCache.h b/security/certverifier/OCSPCache.h
new file mode 100644
index 000000000..141bd05ce
--- /dev/null
+++ b/security/certverifier/OCSPCache.h
@@ -0,0 +1,138 @@
+/* -*- 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_psm_OCSPCache_h
+#define mozilla_psm_OCSPCache_h
+
+#include "hasht.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Vector.h"
+#include "pkix/Result.h"
+#include "pkix/Time.h"
+#include "prerror.h"
+#include "seccomon.h"
+
+namespace mozilla {
+class NeckoOriginAttributes;
+}
+
+namespace mozilla { namespace pkix {
+struct CertID;
+} } // namespace mozilla::pkix
+
+namespace mozilla { namespace psm {
+
+// make SHA384Buffer be of type "array of uint8_t of length SHA384_LENGTH"
+typedef uint8_t SHA384Buffer[SHA384_LENGTH];
+
+// OCSPCache can store and retrieve OCSP response verification results. Each
+// result is keyed on the certificate that purportedly corresponds to it (where
+// certificates are distinguished based on serial number, issuer, and
+// issuer public key, much like in an encoded OCSP response itself). A maximum
+// of 1024 distinct entries can be stored.
+// OCSPCache is thread-safe.
+class OCSPCache
+{
+public:
+ OCSPCache();
+ ~OCSPCache();
+
+ // Returns true if the status of the given certificate (issued by the given
+ // issuer) is in the cache, and false otherwise.
+ // If it is in the cache, returns by reference the error code of the cached
+ // status and the time through which the status is considered trustworthy.
+ // The passed in origin attributes are used to isolate the OCSP cache.
+ // We currently only use the first party domain portion of the attributes, and
+ // it is non-empty only when "privacy.firstParty.isolate" is enabled.
+ bool Get(const mozilla::pkix::CertID& aCertID,
+ const NeckoOriginAttributes& aOriginAttributes,
+ /*out*/ mozilla::pkix::Result& aResult,
+ /*out*/ mozilla::pkix::Time& aValidThrough);
+
+ // Caches the status of the given certificate (issued by the given issuer).
+ // The status is considered trustworthy through the given time.
+ // A status with an error code of SEC_ERROR_REVOKED_CERTIFICATE will not
+ // be replaced or evicted.
+ // A status with an error code of SEC_ERROR_OCSP_UNKNOWN_CERT will not
+ // be evicted when the cache is full.
+ // A status with a more recent thisUpdate will not be replaced with a
+ // status with a less recent thisUpdate unless the less recent status
+ // indicates the certificate is revoked.
+ // The passed in origin attributes are used to isolate the OCSP cache.
+ // We currently only use the first party domain portion of the attributes, and
+ // it is non-empty only when "privacy.firstParty.isolate" is enabled.
+ mozilla::pkix::Result Put(const mozilla::pkix::CertID& aCertID,
+ const NeckoOriginAttributes& aOriginAttributes,
+ mozilla::pkix::Result aResult,
+ mozilla::pkix::Time aThisUpdate,
+ mozilla::pkix::Time aValidThrough);
+
+ // Removes everything from the cache.
+ void Clear();
+
+private:
+ class Entry
+ {
+ public:
+ Entry(mozilla::pkix::Result aResult,
+ mozilla::pkix::Time aThisUpdate,
+ mozilla::pkix::Time aValidThrough)
+ : mResult(aResult)
+ , mThisUpdate(aThisUpdate)
+ , mValidThrough(aValidThrough)
+ {
+ }
+ mozilla::pkix::Result Init(const mozilla::pkix::CertID& aCertID,
+ const NeckoOriginAttributes& aOriginAttributes);
+
+ mozilla::pkix::Result mResult;
+ mozilla::pkix::Time mThisUpdate;
+ mozilla::pkix::Time mValidThrough;
+ // The SHA-384 hash of the concatenation of the DER encodings of the
+ // issuer name and issuer key, followed by the length of the serial number,
+ // the serial number, the length of the first party domain, and the first
+ // party domain (if "privacy.firstparty.isolate" is enabled).
+ // See the documentation for CertIDHash in OCSPCache.cpp.
+ SHA384Buffer mIDHash;
+ };
+
+ bool FindInternal(const mozilla::pkix::CertID& aCertID,
+ const NeckoOriginAttributes& aOriginAttributes,
+ /*out*/ size_t& index,
+ const MutexAutoLock& aProofOfLock);
+ void MakeMostRecentlyUsed(size_t aIndex, const MutexAutoLock& aProofOfLock);
+
+ Mutex mMutex;
+ static const size_t MaxEntries = 1024;
+ // Sorted with the most-recently-used entry at the end.
+ // Using 256 here reserves as much possible inline storage as the vector
+ // implementation will give us. 1024 bytes is the maximum it allows,
+ // which results in 256 Entry pointers or 128 Entry pointers, depending
+ // on the size of a pointer.
+ Vector<Entry*, 256> mEntries;
+};
+
+} } // namespace mozilla::psm
+
+#endif // mozilla_psm_OCSPCache_h
diff --git a/security/certverifier/OCSPRequestor.cpp b/security/certverifier/OCSPRequestor.cpp
new file mode 100644
index 000000000..2df131278
--- /dev/null
+++ b/security/certverifier/OCSPRequestor.cpp
@@ -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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OCSPRequestor.h"
+
+#include <limits>
+
+#include "ScopedNSSTypes.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Casting.h"
+#include "nsIURLParser.h"
+#include "nsNSSCallbacks.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "secerr.h"
+
+extern mozilla::LazyLogModule gCertVerifierLog;
+
+namespace mozilla {
+
+void
+ReleaseHttpServerSession(nsNSSHttpServerSession* httpServerSession)
+{
+ delete httpServerSession;
+}
+
+void
+ReleaseHttpRequestSession(nsNSSHttpRequestSession* httpRequestSession)
+{
+ httpRequestSession->Release();
+}
+
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueHTTPServerSession,
+ nsNSSHttpServerSession,
+ ReleaseHttpServerSession)
+
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueHTTPRequestSession,
+ nsNSSHttpRequestSession,
+ ReleaseHttpRequestSession)
+
+} // namespace mozilla
+
+namespace mozilla { namespace psm {
+
+static nsresult
+AppendEscapedBase64Item(const SECItem* encodedRequest, nsACString& path)
+{
+ nsresult rv;
+ nsDependentCSubstring requestAsSubstring(
+ BitwiseCast<char*, unsigned char*>(encodedRequest->data),
+ encodedRequest->len);
+ nsCString base64Request;
+ rv = Base64Encode(requestAsSubstring, base64Request);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("Setting up OCSP GET path, pre path =%s\n",
+ PromiseFlatCString(path).get()));
+
+ // The path transformation is not a direct url encoding. Three characters
+ // need change '+' -> "%2B", '/' -> "%2F", and '=' -> '%3D'.
+ // http://tools.ietf.org/html/rfc5019#section-5
+ base64Request.ReplaceSubstring("+", "%2B");
+ base64Request.ReplaceSubstring("/", "%2F");
+ base64Request.ReplaceSubstring("=", "%3D");
+ path.Append(base64Request);
+ return NS_OK;
+}
+
+Result
+DoOCSPRequest(const UniquePLArenaPool& arena, const char* url,
+ const NeckoOriginAttributes& originAttributes,
+ const SECItem* encodedRequest, PRIntervalTime timeout,
+ bool useGET,
+ /*out*/ SECItem*& encodedResponse)
+{
+ MOZ_ASSERT(arena.get());
+ MOZ_ASSERT(url);
+ MOZ_ASSERT(encodedRequest);
+ MOZ_ASSERT(encodedRequest->data);
+ if (!arena.get() || !url || !encodedRequest || !encodedRequest->data) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ uint32_t urlLen = PL_strlen(url);
+ if (urlLen > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ nsCOMPtr<nsIURLParser> urlParser = do_GetService(NS_STDURLPARSER_CONTRACTID);
+ if (!urlParser) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ uint32_t schemePos;
+ int32_t schemeLen;
+ uint32_t authorityPos;
+ int32_t authorityLen;
+ uint32_t pathPos;
+ int32_t pathLen;
+ nsresult nsrv = urlParser->ParseURL(url, static_cast<int32_t>(urlLen),
+ &schemePos, &schemeLen,
+ &authorityPos, &authorityLen,
+ &pathPos, &pathLen);
+ if (NS_FAILED(nsrv)) {
+ return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
+ }
+ if (schemeLen < 0 || authorityLen < 0) {
+ return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
+ }
+ nsAutoCString scheme(url + schemePos,
+ static_cast<nsAutoCString::size_type>(schemeLen));
+ if (!scheme.LowerCaseEqualsLiteral("http")) {
+ // We don't support HTTPS to avoid loops. See Bug 92923.
+ // We also in general only support HTTP.
+ return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
+ }
+
+ uint32_t hostnamePos;
+ int32_t hostnameLen;
+ int32_t port;
+ // We ignore user:password sections: if one is present, we send an OCSP
+ // request to the URL as normal without sending the username or password.
+ nsrv = urlParser->ParseAuthority(url + authorityPos, authorityLen,
+ nullptr, nullptr, nullptr, nullptr,
+ &hostnamePos, &hostnameLen, &port);
+ if (NS_FAILED(nsrv)) {
+ return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
+ }
+ if (hostnameLen < 0) {
+ return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
+ }
+ if (port == -1) {
+ port = 80;
+ } else if (port < 0 || port > 0xffff) {
+ return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
+ }
+ nsAutoCString
+ hostname(url + authorityPos + hostnamePos,
+ static_cast<nsACString_internal::size_type>(hostnameLen));
+
+ nsNSSHttpServerSession* serverSessionPtr = nullptr;
+ Result rv = nsNSSHttpInterface::createSessionFcn(
+ hostname.BeginReading(), static_cast<uint16_t>(port), &serverSessionPtr);
+ if (rv != Success) {
+ return rv;
+ }
+ UniqueHTTPServerSession serverSession(serverSessionPtr);
+
+ nsAutoCString path;
+ if (pathLen > 0) {
+ path.Assign(url + pathPos, static_cast<nsAutoCString::size_type>(pathLen));
+ } else {
+ path.Assign("/");
+ }
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("Setting up OCSP request: pre all path =%s pathlen=%d\n", path.get(),
+ pathLen));
+ nsAutoCString method("POST");
+ if (useGET) {
+ method.Assign("GET");
+ if (!StringEndsWith(path, NS_LITERAL_CSTRING("/"))) {
+ path.Append("/");
+ }
+ nsresult nsrv = AppendEscapedBase64Item(encodedRequest, path);
+ if (NS_WARN_IF(NS_FAILED(nsrv))) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ }
+
+ nsNSSHttpRequestSession* requestSessionPtr;
+ rv = nsNSSHttpInterface::createFcn(serverSession.get(), "http", path.get(),
+ method.get(), originAttributes, timeout,
+ &requestSessionPtr);
+ if (rv != Success) {
+ return rv;
+ }
+
+ UniqueHTTPRequestSession requestSession(requestSessionPtr);
+
+ if (!useGET) {
+ rv = nsNSSHttpInterface::setPostDataFcn(
+ requestSession.get(),
+ BitwiseCast<char*, unsigned char*>(encodedRequest->data),
+ encodedRequest->len, "application/ocsp-request");
+ if (rv != Success) {
+ return rv;
+ }
+ }
+
+ uint16_t httpResponseCode;
+ const char* httpResponseData;
+ uint32_t httpResponseDataLen = 0; // 0 means any response size is acceptable
+ rv = nsNSSHttpInterface::trySendAndReceiveFcn(requestSession.get(), nullptr,
+ &httpResponseCode, nullptr,
+ nullptr, &httpResponseData,
+ &httpResponseDataLen);
+ if (rv != Success) {
+ return rv;
+ }
+
+ if (httpResponseCode != 200) {
+ return Result::ERROR_OCSP_SERVER_ERROR;
+ }
+
+ encodedResponse = SECITEM_AllocItem(arena.get(), nullptr, httpResponseDataLen);
+ if (!encodedResponse) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+
+ memcpy(encodedResponse->data, httpResponseData, httpResponseDataLen);
+ return Success;
+}
+
+} } // namespace mozilla::psm
diff --git a/security/certverifier/OCSPRequestor.h b/security/certverifier/OCSPRequestor.h
new file mode 100644
index 000000000..f2e19475e
--- /dev/null
+++ b/security/certverifier/OCSPRequestor.h
@@ -0,0 +1,28 @@
+/* -*- 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 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/. */
+
+#ifndef OCSPRequestor_h
+#define OCSPRequestor_h
+
+#include "CertVerifier.h"
+#include "secmodt.h"
+
+namespace mozilla {
+class NeckoOriginAttributes;
+}
+
+namespace mozilla { namespace psm {
+
+// The memory returned via |encodedResponse| is owned by the given arena.
+Result DoOCSPRequest(const UniquePLArenaPool& arena, const char* url,
+ const NeckoOriginAttributes& originAttributes,
+ const SECItem* encodedRequest, PRIntervalTime timeout,
+ bool useGET,
+ /*out*/ SECItem*& encodedResponse);
+
+} } // namespace mozilla::psm
+
+#endif // OCSPRequestor_h
diff --git a/security/certverifier/OCSPVerificationTrustDomain.cpp b/security/certverifier/OCSPVerificationTrustDomain.cpp
new file mode 100644
index 000000000..fa018458c
--- /dev/null
+++ b/security/certverifier/OCSPVerificationTrustDomain.cpp
@@ -0,0 +1,128 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OCSPVerificationTrustDomain.h"
+
+using namespace mozilla;
+using namespace mozilla::pkix;
+
+namespace mozilla { namespace psm {
+
+OCSPVerificationTrustDomain::OCSPVerificationTrustDomain(
+ NSSCertDBTrustDomain& certDBTrustDomain)
+ : mCertDBTrustDomain(certDBTrustDomain)
+{
+}
+
+Result
+OCSPVerificationTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
+ const CertPolicyId& policy,
+ Input candidateCertDER,
+ /*out*/ TrustLevel& trustLevel)
+{
+ return mCertDBTrustDomain.GetCertTrust(endEntityOrCA, policy,
+ candidateCertDER, trustLevel);
+}
+
+
+Result
+OCSPVerificationTrustDomain::FindIssuer(Input, IssuerChecker&, Time)
+{
+ // We do not expect this to be called for OCSP signers
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+Result
+OCSPVerificationTrustDomain::IsChainValid(const DERArray&, Time)
+{
+ // We do not expect this to be called for OCSP signers
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+Result
+OCSPVerificationTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&,
+ Time, Duration, const Input*,
+ const Input*)
+{
+ // We do not expect this to be called for OCSP signers
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+Result
+OCSPVerificationTrustDomain::CheckSignatureDigestAlgorithm(
+ DigestAlgorithm aAlg, EndEntityOrCA aEEOrCA, Time notBefore)
+{
+ // The reason for wrapping the NSSCertDBTrustDomain in an
+ // OCSPVerificationTrustDomain is to allow us to bypass the weaker signature
+ // algorithm check - thus all allowable signature digest algorithms should
+ // always be accepted. This is only needed while we gather telemetry on SHA-1.
+ return Success;
+}
+
+Result
+OCSPVerificationTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
+ EndEntityOrCA aEEOrCA, unsigned int aModulusSizeInBits)
+{
+ return mCertDBTrustDomain.
+ CheckRSAPublicKeyModulusSizeInBits(aEEOrCA, aModulusSizeInBits);
+}
+
+Result
+OCSPVerificationTrustDomain::VerifyRSAPKCS1SignedDigest(
+ const SignedDigest& aSignedDigest, Input aSubjectPublicKeyInfo)
+{
+ return mCertDBTrustDomain.VerifyRSAPKCS1SignedDigest(aSignedDigest,
+ aSubjectPublicKeyInfo);
+}
+
+Result
+OCSPVerificationTrustDomain::CheckECDSACurveIsAcceptable(
+ EndEntityOrCA aEEOrCA, NamedCurve aCurve)
+{
+ return mCertDBTrustDomain.CheckECDSACurveIsAcceptable(aEEOrCA, aCurve);
+}
+
+Result
+OCSPVerificationTrustDomain::VerifyECDSASignedDigest(
+ const SignedDigest& aSignedDigest, Input aSubjectPublicKeyInfo)
+{
+ return mCertDBTrustDomain.VerifyECDSASignedDigest(aSignedDigest,
+ aSubjectPublicKeyInfo);
+}
+
+Result
+OCSPVerificationTrustDomain::CheckValidityIsAcceptable(
+ Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA,
+ KeyPurposeId keyPurpose)
+{
+ return mCertDBTrustDomain.CheckValidityIsAcceptable(notBefore, notAfter,
+ endEntityOrCA,
+ keyPurpose);
+}
+
+Result
+OCSPVerificationTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
+ /*out*/ bool& matches)
+{
+ return mCertDBTrustDomain.NetscapeStepUpMatchesServerAuth(notBefore, matches);
+}
+
+void
+OCSPVerificationTrustDomain::NoteAuxiliaryExtension(
+ AuxiliaryExtension extension, Input extensionData)
+{
+ mCertDBTrustDomain.NoteAuxiliaryExtension(extension, extensionData);
+}
+
+Result
+OCSPVerificationTrustDomain::DigestBuf(
+ Input item, DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf, size_t digestBufLen)
+{
+ return mCertDBTrustDomain.DigestBuf(item, digestAlg, digestBuf, digestBufLen);
+}
+
+} } // namespace mozilla::psm
diff --git a/security/certverifier/OCSPVerificationTrustDomain.h b/security/certverifier/OCSPVerificationTrustDomain.h
new file mode 100644
index 000000000..bf25e3804
--- /dev/null
+++ b/security/certverifier/OCSPVerificationTrustDomain.h
@@ -0,0 +1,89 @@
+/* -*- 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 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/. */
+
+#ifndef mozilla_psm__OCSPVerificationTrustDomain_h
+#define mozilla_psm__OCSPVerificationTrustDomain_h
+
+#include "pkix/pkixtypes.h"
+#include "NSSCertDBTrustDomain.h"
+
+namespace mozilla { namespace psm {
+
+typedef mozilla::pkix::Result Result;
+
+class OCSPVerificationTrustDomain : public mozilla::pkix::TrustDomain
+{
+public:
+ explicit OCSPVerificationTrustDomain(NSSCertDBTrustDomain& certDBTrustDomain);
+
+ virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
+ IssuerChecker& checker,
+ mozilla::pkix::Time time) override;
+
+ virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertPolicyId& policy,
+ mozilla::pkix::Input candidateCertDER,
+ /*out*/ mozilla::pkix::TrustLevel& trustLevel)
+ override;
+
+ virtual Result CheckSignatureDigestAlgorithm(
+ mozilla::pkix::DigestAlgorithm digestAlg,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::Time notBefore) override;
+
+ virtual Result CheckRSAPublicKeyModulusSizeInBits(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ unsigned int modulusSizeInBits) override;
+
+ virtual Result VerifyRSAPKCS1SignedDigest(
+ const mozilla::pkix::SignedDigest& signedDigest,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual Result CheckECDSACurveIsAcceptable(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::NamedCurve curve) override;
+
+ virtual Result VerifyECDSASignedDigest(
+ const mozilla::pkix::SignedDigest& signedDigest,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual Result DigestBuf(mozilla::pkix::Input item,
+ mozilla::pkix::DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf,
+ size_t digestBufLen) override;
+
+ virtual Result CheckValidityIsAcceptable(
+ mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::KeyPurposeId keyPurpose) override;
+
+ virtual Result NetscapeStepUpMatchesServerAuth(mozilla::pkix::Time notBefore,
+ /*out*/ bool& matches) override;
+
+ virtual Result CheckRevocation(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertID& certID,
+ mozilla::pkix::Time time,
+ mozilla::pkix::Duration validityDuration,
+ /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
+ /*optional*/ const mozilla::pkix::Input* aiaExtension)
+ override;
+
+ virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
+ mozilla::pkix::Time time) override;
+
+ virtual void NoteAuxiliaryExtension(
+ mozilla::pkix::AuxiliaryExtension extension,
+ mozilla::pkix::Input extensionData) override;
+
+private:
+ NSSCertDBTrustDomain& mCertDBTrustDomain;
+};
+
+
+} } // namespace mozilla::psm
+
+#endif // mozilla_psm__OCSPVerificationTrustDomain_h
diff --git a/security/certverifier/SignedCertificateTimestamp.cpp b/security/certverifier/SignedCertificateTimestamp.cpp
new file mode 100644
index 000000000..23a0397d3
--- /dev/null
+++ b/security/certverifier/SignedCertificateTimestamp.cpp
@@ -0,0 +1,45 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SignedCertificateTimestamp.h"
+
+namespace mozilla { namespace ct {
+
+void
+LogEntry::Reset()
+{
+ type = LogEntry::Type::X509;
+ leafCertificate.clear();
+ issuerKeyHash.clear();
+ tbsCertificate.clear();
+}
+
+bool
+DigitallySigned::SignatureParametersMatch(HashAlgorithm aHashAlgorithm,
+ SignatureAlgorithm aSignatureAlgorithm) const
+{
+ return (hashAlgorithm == aHashAlgorithm) &&
+ (signatureAlgorithm == aSignatureAlgorithm);
+}
+
+} } // namespace mozilla::ct
+
+
+namespace mozilla {
+
+bool
+operator==(const ct::Buffer& a, const ct::Buffer& b)
+{
+ return (a.empty() && b.empty()) ||
+ (a.length() == b.length() && memcmp(a.begin(), b.begin(), a.length()) == 0);
+}
+
+bool
+operator!=(const ct::Buffer& a, const ct::Buffer& b) {
+ return !(a == b);
+}
+
+} // namespace mozilla
diff --git a/security/certverifier/SignedCertificateTimestamp.h b/security/certverifier/SignedCertificateTimestamp.h
new file mode 100644
index 000000000..25b324f6f
--- /dev/null
+++ b/security/certverifier/SignedCertificateTimestamp.h
@@ -0,0 +1,142 @@
+/* -*- 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 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/. */
+
+#ifndef SignedCertificateTimestamp_h
+#define SignedCertificateTimestamp_h
+
+#include "mozilla/Vector.h"
+#include "pkix/Input.h"
+#include "pkix/Result.h"
+
+// Structures related to Certificate Transparency (RFC 6962).
+namespace mozilla { namespace ct {
+
+typedef Vector<uint8_t> Buffer;
+
+// LogEntry struct in RFC 6962, Section 3.1.
+struct LogEntry
+{
+
+ // LogEntryType enum in RFC 6962, Section 3.1.
+ enum class Type {
+ X509 = 0,
+ Precert = 1
+ };
+
+ void Reset();
+
+ Type type;
+
+ // Set if type == X509.
+ Buffer leafCertificate;
+
+ // Set if type == Precert.
+ Buffer issuerKeyHash;
+ Buffer tbsCertificate;
+};
+
+// Helper structure to represent Digitally Signed data, as described in
+// Sections 4.7 and 7.4.1.4.1 of RFC 5246.
+struct DigitallySigned
+{
+ enum class HashAlgorithm {
+ None = 0,
+ MD5 = 1,
+ SHA1 = 2,
+ SHA224 = 3,
+ SHA256 = 4,
+ SHA384 = 5,
+ SHA512 = 6,
+ };
+
+ enum class SignatureAlgorithm {
+ Anonymous = 0,
+ RSA = 1,
+ DSA = 2,
+ ECDSA = 3
+ };
+
+ // Returns true if |aHashAlgorithm| and |aSignatureAlgorithm|
+ // match this DigitallySigned hash and signature algorithms.
+ bool SignatureParametersMatch(HashAlgorithm aHashAlgorithm,
+ SignatureAlgorithm aSignatureAlgorithm) const;
+
+ HashAlgorithm hashAlgorithm;
+ SignatureAlgorithm signatureAlgorithm;
+ // 'signature' field.
+ Buffer signatureData;
+};
+
+// SignedCertificateTimestamp struct in RFC 6962, Section 3.2.
+struct SignedCertificateTimestamp
+{
+ // Version enum in RFC 6962, Section 3.2.
+ enum class Version {
+ V1 = 0,
+ };
+
+ Version version;
+ Buffer logId;
+ // "timestamp" is the current time in milliseconds, measured since the epoch,
+ // ignoring leap seconds. See RFC 6962, Section 3.2.
+ uint64_t timestamp;
+ Buffer extensions;
+ DigitallySigned signature;
+
+ // Supplementary fields, not defined in CT RFC. Set during the various
+ // stages of processing the received SCTs.
+
+ enum class Origin {
+ Unknown,
+ Embedded,
+ TLSExtension,
+ OCSPResponse
+ };
+
+ enum class VerificationStatus {
+ None,
+ // The SCT is from a known log, and the signature is valid.
+ OK,
+ // The SCT is from an unknown log and can not be verified.
+ UnknownLog,
+ // The SCT is from a known log, but the signature is invalid.
+ InvalidSignature,
+ // The SCT signature is valid, but the timestamp is in the future.
+ // Such SCT are considered invalid (see RFC 6962, Section 5.2).
+ InvalidTimestamp
+ };
+
+ Origin origin;
+ VerificationStatus verificationStatus;
+};
+
+
+inline pkix::Result BufferToInput(const Buffer& buffer, pkix::Input& input)
+{
+ return input.Init(buffer.begin(), buffer.length());
+}
+
+inline pkix::Result InputToBuffer(pkix::Input input, Buffer& buffer)
+{
+ buffer.clear();
+ if (!buffer.append(input.UnsafeGetData(), input.GetLength())) {
+ return pkix::Result::FATAL_ERROR_NO_MEMORY;
+ }
+ return pkix::Success;
+}
+
+} } // namespace mozilla::ct
+
+namespace mozilla {
+
+// Comparison operators are placed under mozilla namespace since
+// mozilla::ct::Buffer is actually mozilla::Vector.
+bool operator==(const ct::Buffer& a, const ct::Buffer& b);
+bool operator!=(const ct::Buffer& a, const ct::Buffer& b);
+
+} // namespace mozilla
+
+#endif // SignedCertificateTimestamp_h
diff --git a/security/certverifier/SignedTreeHead.h b/security/certverifier/SignedTreeHead.h
new file mode 100644
index 000000000..47231d17f
--- /dev/null
+++ b/security/certverifier/SignedTreeHead.h
@@ -0,0 +1,31 @@
+/* -*- 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 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/. */
+
+#ifndef SignedTreeHead_h
+#define SignedTreeHead_h
+
+#include "SignedCertificateTimestamp.h"
+
+namespace mozilla { namespace ct {
+
+// Signed Tree Head as defined in section 3.5. of RFC-6962.
+struct SignedTreeHead
+{
+ // Version enum in RFC 6962, Section 3.2. Note that while in the current
+ // RFC the STH and SCT share the versioning scheme, there are plans in
+ // RFC-6962-bis to use separate versions, so using a separate scheme here.
+ enum class Version { V1 = 0, };
+
+ Version version;
+ uint64_t timestamp;
+ uint64_t treeSize;
+ Buffer sha256RootHash;
+ DigitallySigned signature;
+};
+
+} } // namespace mozilla::ct
+
+#endif // SignedTreeHead_h
diff --git a/security/certverifier/StartComAndWoSignData.inc b/security/certverifier/StartComAndWoSignData.inc
new file mode 100644
index 000000000..3ba643397
--- /dev/null
+++ b/security/certverifier/StartComAndWoSignData.inc
@@ -0,0 +1,89 @@
+// /C=CN/O=WoSign CA Limited/CN=CA \xE6\xB2\x83\xE9\x80\x9A\xE6\xA0\xB9\xE8\xAF\x81\xE4\xB9\xA6
+// Using a consistent naming convention, this would actually be called
+// 'CA沃通根证书DN', but since GCC 6.2.1 apparently can't handle UTF-8
+// identifiers, this will have to do.
+static const uint8_t CAWoSignRootDN[72] = {
+ 0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x0C, 0x12, 0x43, 0x41, 0x20, 0xE6, 0xB2, 0x83, 0xE9, 0x80, 0x9A, 0xE6, 0xA0,
+ 0xB9, 0xE8, 0xAF, 0x81, 0xE4, 0xB9, 0xA6,
+};
+
+// /C=CN/O=WoSign CA Limited/CN=CA WoSign ECC Root
+static const uint8_t CAWoSignECCRootDN[72] = {
+ 0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x12, 0x43, 0x41, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x45,
+ 0x43, 0x43, 0x20, 0x52, 0x6F, 0x6F, 0x74,
+};
+
+// /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign
+static const uint8_t CertificationAuthorityofWoSignDN[87] = {
+ 0x30, 0x55, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x2A, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20,
+ 0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E,
+};
+
+// /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign G2
+static const uint8_t CertificationAuthorityofWoSignG2DN[90] = {
+ 0x30, 0x58, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x2D, 0x30, 0x2B, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x24, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20,
+ 0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x47, 0x32,
+};
+
+// /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority
+static const uint8_t StartComCertificationAuthorityDN[127] = {
+ 0x30, 0x7D, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E,
+ 0x31, 0x2B, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x22, 0x53, 0x65,
+ 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53,
+ 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79,
+};
+
+// /C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2
+static const uint8_t StartComCertificationAuthorityG2DN[85] = {
+ 0x30, 0x53, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E,
+ 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x47, 0x32,
+};
+
+struct DataAndLength {
+ const uint8_t* data;
+ uint32_t len;
+};
+
+static const DataAndLength StartComAndWoSignDNs[]= {
+ { CAWoSignRootDN,
+ sizeof(CAWoSignRootDN) },
+ { CAWoSignECCRootDN,
+ sizeof(CAWoSignECCRootDN) },
+ { CertificationAuthorityofWoSignDN,
+ sizeof(CertificationAuthorityofWoSignDN) },
+ { CertificationAuthorityofWoSignG2DN,
+ sizeof(CertificationAuthorityofWoSignG2DN) },
+ { StartComCertificationAuthorityDN,
+ sizeof(StartComCertificationAuthorityDN) },
+ { StartComCertificationAuthorityG2DN,
+ sizeof(StartComCertificationAuthorityG2DN) },
+};
diff --git a/security/certverifier/moz.build b/security/certverifier/moz.build
new file mode 100644
index 000000000..70f049340
--- /dev/null
+++ b/security/certverifier/moz.build
@@ -0,0 +1,100 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS += [
+ 'BRNameMatchingPolicy.h',
+ 'CertVerifier.h',
+ 'CTVerifyResult.h',
+ 'OCSPCache.h',
+ 'SignedCertificateTimestamp.h',
+ 'SignedTreeHead.h',
+]
+
+UNIFIED_SOURCES += [
+ 'BRNameMatchingPolicy.cpp',
+ 'CertVerifier.cpp',
+ 'CTLogVerifier.cpp',
+ 'CTObjectsExtractor.cpp',
+ 'CTSerialization.cpp',
+ 'CTVerifyResult.cpp',
+ 'MultiLogCTVerifier.cpp',
+ 'NSSCertDBTrustDomain.cpp',
+ 'OCSPCache.cpp',
+ 'OCSPRequestor.cpp',
+ 'OCSPVerificationTrustDomain.cpp',
+ 'SignedCertificateTimestamp.cpp',
+]
+
+if not CONFIG['NSS_NO_EV_CERTS']:
+ UNIFIED_SOURCES += [
+ 'ExtendedValidation.cpp',
+ ]
+
+LOCAL_INCLUDES += [
+ '/security/manager/ssl',
+ '/security/pkix/include',
+ '/security/pkix/lib',
+]
+
+DIRS += [
+ '../pkix',
+]
+
+TEST_DIRS += [
+ 'tests/gtest',
+]
+
+CXXFLAGS += ['-Wall']
+if CONFIG['_MSC_VER']:
+ # -Wall with Visual C++ enables too many problematic warnings
+ CXXFLAGS += [
+ '-wd4355', # 'this' used in base member initializer list
+ '-wd4464', # relative include path contains '..'
+ '-wd4480', # nonstandard extension used: specifying underlying type for
+ # enum 'enum'
+ '-wd4481', # nonstandard extension used: override specifier 'keyword'
+ '-wd4510', # default constructor could not be generated
+ '-wd4512', # assignment operator could not be generated
+ '-wd4514', # 'function': unreferenced inline function has been removed
+ '-wd4610', # struct 'symbol' can never be instantiated - user defined
+ # constructor required
+ '-wd4619', # pragma warning: there is no warning 'warning'
+ '-wd4623', # default constructor could not be generated because a base
+ # class default constructor is inaccessible or deleted
+ '-wd4625', # copy constructor could not be generated because a base
+ # class copy constructor is inaccessible or deleted
+ '-wd4626', # assignment operator could not be generated because a base
+ # class assignment operator is inaccessible or deleted
+ '-wd4640', # construction of local static object is not thread-safe
+ '-wd4710', # 'function': function not inlined
+ '-wd4711', # function 'function' selected for inline expansion
+ '-wd4820', # 'bytes' bytes padding added after construct 'member_name'
+ ]
+
+ # MSVC 2010's headers trigger these
+ CXXFLAGS += [
+ '-wd4548', # expression before comma has no effect; ...
+ '-wd4668', # 'symbol' is not defined as a preprocessor macro...
+ '-wd4987', # nonstandard extension used
+ ]
+
+ # MSVC 2015 triggers these
+ CXXFLAGS += [
+ '-wd4456', # declaration of 'rv' hides previous local declaration
+ '-wd4458', # declaration of 'input' hides class member
+ ]
+
+ # Gecko headers aren't warning-free enough for us to enable these warnings
+ CXXFLAGS += [
+ '-wd4100', # 'symbol' : unreferenced formal parameter
+ '-wd4127', # conditional expression is constant
+ '-wd4946', # reinterpret_cast used between related types
+ ]
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
+
+FINAL_LIBRARY = 'xul'
diff --git a/security/certverifier/tests/gtest/CTLogVerifierTest.cpp b/security/certverifier/tests/gtest/CTLogVerifierTest.cpp
new file mode 100644
index 000000000..2cf0085a1
--- /dev/null
+++ b/security/certverifier/tests/gtest/CTLogVerifierTest.cpp
@@ -0,0 +1,129 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CTLogVerifier.h"
+#include "CTTestUtils.h"
+#include "nss.h"
+
+#include "gtest/gtest.h"
+
+namespace mozilla { namespace ct {
+
+using namespace pkix;
+
+class CTLogVerifierTest : public ::testing::Test
+{
+public:
+ void SetUp() override
+ {
+ // Does nothing if NSS is already initialized.
+ MOZ_RELEASE_ASSERT(NSS_NoDB_Init(nullptr) == SECSuccess);
+
+ ASSERT_EQ(Success, mLog.Init(InputForBuffer(GetTestPublicKey())));
+ ASSERT_EQ(GetTestPublicKeyId(), mLog.keyId());
+ }
+
+protected:
+ CTLogVerifier mLog;
+};
+
+TEST_F(CTLogVerifierTest, VerifiesCertSCT)
+{
+ LogEntry certEntry;
+ GetX509CertLogEntry(certEntry);
+
+ SignedCertificateTimestamp certSct;
+ GetX509CertSCT(certSct);
+
+ EXPECT_EQ(Success, mLog.Verify(certEntry, certSct));
+}
+
+TEST_F(CTLogVerifierTest, VerifiesPrecertSCT)
+{
+ LogEntry precertEntry;
+ GetPrecertLogEntry(precertEntry);
+
+ SignedCertificateTimestamp precertSct;
+ GetPrecertSCT(precertSct);
+
+ EXPECT_EQ(Success, mLog.Verify(precertEntry, precertSct));
+}
+
+TEST_F(CTLogVerifierTest, FailsInvalidTimestamp)
+{
+ LogEntry certEntry;
+ GetX509CertLogEntry(certEntry);
+
+ SignedCertificateTimestamp certSct;
+ GetX509CertSCT(certSct);
+
+ // Mangle the timestamp, so that it should fail signature validation.
+ certSct.timestamp = 0;
+
+ EXPECT_EQ(Result::ERROR_BAD_SIGNATURE, mLog.Verify(certEntry, certSct));
+}
+
+TEST_F(CTLogVerifierTest, FailsInvalidSignature)
+{
+ LogEntry certEntry;
+ GetX509CertLogEntry(certEntry);
+
+ // Mangle the signature, making VerifyECDSASignedDigestNSS (used by
+ // CTLogVerifier) return ERROR_BAD_SIGNATURE.
+ SignedCertificateTimestamp certSct;
+ GetX509CertSCT(certSct);
+ certSct.signature.signatureData[20] ^= '\xFF';
+ EXPECT_EQ(Result::ERROR_BAD_SIGNATURE, mLog.Verify(certEntry, certSct));
+
+ // Make VerifyECDSASignedDigestNSS return ERROR_BAD_DER. We still expect
+ // the verifier to return ERROR_BAD_SIGNATURE.
+ SignedCertificateTimestamp certSct2;
+ GetX509CertSCT(certSct2);
+ certSct2.signature.signatureData[0] ^= '\xFF';
+ EXPECT_EQ(Result::ERROR_BAD_SIGNATURE, mLog.Verify(certEntry, certSct2));
+}
+
+TEST_F(CTLogVerifierTest, FailsInvalidLogID)
+{
+ LogEntry certEntry;
+ GetX509CertLogEntry(certEntry);
+
+ SignedCertificateTimestamp certSct;
+ GetX509CertSCT(certSct);
+
+ // Mangle the log ID, which should cause it to match a different log before
+ // attempting signature validation.
+ MOZ_RELEASE_ASSERT(certSct.logId.append('\x0'));
+
+ EXPECT_EQ(Result::FATAL_ERROR_INVALID_ARGS, mLog.Verify(certEntry, certSct));
+}
+
+TEST_F(CTLogVerifierTest, VerifiesValidSTH)
+{
+ SignedTreeHead sth;
+ GetSampleSignedTreeHead(sth);
+ EXPECT_EQ(Success, mLog.VerifySignedTreeHead(sth));
+}
+
+TEST_F(CTLogVerifierTest, DoesNotVerifyInvalidSTH)
+{
+ SignedTreeHead sth;
+ GetSampleSignedTreeHead(sth);
+ sth.sha256RootHash[0] ^= '\xFF';
+ EXPECT_EQ(Result::ERROR_BAD_SIGNATURE, mLog.VerifySignedTreeHead(sth));
+}
+
+// Test that excess data after the public key is rejected.
+TEST_F(CTLogVerifierTest, ExcessDataInPublicKey)
+{
+ Buffer key = GetTestPublicKey();
+ MOZ_RELEASE_ASSERT(key.append("extra", 5));
+
+ CTLogVerifier log;
+ EXPECT_NE(Success, log.Init(InputForBuffer(key)));
+}
+
+} } // namespace mozilla::ct
diff --git a/security/certverifier/tests/gtest/CTObjectsExtractorTest.cpp b/security/certverifier/tests/gtest/CTObjectsExtractorTest.cpp
new file mode 100644
index 000000000..a00a47bf2
--- /dev/null
+++ b/security/certverifier/tests/gtest/CTObjectsExtractorTest.cpp
@@ -0,0 +1,83 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CTLogVerifier.h"
+#include "CTObjectsExtractor.h"
+#include "CTSerialization.h"
+#include "CTTestUtils.h"
+#include "gtest/gtest.h"
+#include "nss.h"
+
+namespace mozilla { namespace ct {
+
+using namespace pkix;
+
+class CTObjectsExtractorTest : public ::testing::Test
+{
+public:
+ void SetUp() override
+ {
+ // Does nothing if NSS is already initialized.
+ MOZ_RELEASE_ASSERT(NSS_NoDB_Init(nullptr) == SECSuccess);
+
+ mTestCert = GetDEREncodedX509Cert();
+ mEmbeddedCert = GetDEREncodedTestEmbeddedCert();
+ mCaCert = GetDEREncodedCACert();
+ mCaCertSPKI = ExtractCertSPKI(mCaCert);
+
+ Buffer logPublicKey = GetTestPublicKey();
+ ASSERT_EQ(Success, mLog.Init(InputForBuffer(logPublicKey)));
+ }
+
+protected:
+ Buffer mTestCert;
+ Buffer mEmbeddedCert;
+ Buffer mCaCert;
+ Buffer mCaCertSPKI;
+ CTLogVerifier mLog;
+};
+
+TEST_F(CTObjectsExtractorTest, ExtractPrecert)
+{
+ LogEntry entry;
+ ASSERT_EQ(Success,
+ GetPrecertLogEntry(InputForBuffer(mEmbeddedCert),
+ InputForBuffer(mCaCertSPKI),
+ entry));
+
+ EXPECT_EQ(LogEntry::Type::Precert, entry.type);
+ // Should have empty leaf cert for this log entry type.
+ EXPECT_TRUE(entry.leafCertificate.empty());
+ EXPECT_EQ(GetDefaultIssuerKeyHash(), entry.issuerKeyHash);
+ EXPECT_EQ(GetDEREncodedTestTbsCert(), entry.tbsCertificate);
+}
+
+TEST_F(CTObjectsExtractorTest, ExtractOrdinaryX509Cert)
+{
+ LogEntry entry;
+ ASSERT_EQ(Success, GetX509LogEntry(InputForBuffer(mTestCert), entry));
+
+ EXPECT_EQ(LogEntry::Type::X509, entry.type);
+ // Should have empty tbsCertificate / issuerKeyHash for this log entry type.
+ EXPECT_TRUE(entry.tbsCertificate.empty());
+ EXPECT_TRUE(entry.issuerKeyHash.empty());
+ // Length of leafCertificate should be 718, see the CT Serialization tests.
+ EXPECT_EQ(718U, entry.leafCertificate.length());
+}
+
+// Test that an externally-provided SCT verifies over the LogEntry
+// of a regular X.509 Certificate
+TEST_F(CTObjectsExtractorTest, ComplementarySCTVerifies)
+{
+ SignedCertificateTimestamp sct;
+ GetX509CertSCT(sct);
+
+ LogEntry entry;
+ ASSERT_EQ(Success, GetX509LogEntry(InputForBuffer(mTestCert), entry));
+ EXPECT_EQ(Success, mLog.Verify(entry, sct));
+}
+
+} } // namespace mozilla::ct
diff --git a/security/certverifier/tests/gtest/CTSerializationTest.cpp b/security/certverifier/tests/gtest/CTSerializationTest.cpp
new file mode 100644
index 000000000..7c5aadd23
--- /dev/null
+++ b/security/certverifier/tests/gtest/CTSerializationTest.cpp
@@ -0,0 +1,271 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CTSerialization.h"
+#include "CTTestUtils.h"
+#include "gtest/gtest.h"
+#include "mozilla/Move.h"
+
+namespace mozilla { namespace ct {
+
+using namespace pkix;
+
+class CTSerializationTest : public ::testing::Test
+{
+public:
+ void SetUp() override
+ {
+ mTestDigitallySigned = GetTestDigitallySigned();
+ mTestSignatureData = GetTestDigitallySignedData();
+ }
+
+protected:
+ Buffer mTestDigitallySigned;
+ Buffer mTestSignatureData;
+};
+
+TEST_F(CTSerializationTest, DecodesDigitallySigned)
+{
+ Input digitallySigned = InputForBuffer(mTestDigitallySigned);
+ Reader digitallySignedReader(digitallySigned);
+
+ DigitallySigned parsed;
+ ASSERT_EQ(Success,
+ DecodeDigitallySigned(digitallySignedReader, parsed));
+ EXPECT_TRUE(digitallySignedReader.AtEnd());
+
+ EXPECT_EQ(DigitallySigned::HashAlgorithm::SHA256,
+ parsed.hashAlgorithm);
+ EXPECT_EQ(DigitallySigned::SignatureAlgorithm::ECDSA,
+ parsed.signatureAlgorithm);
+ EXPECT_EQ(mTestSignatureData, parsed.signatureData);
+}
+
+TEST_F(CTSerializationTest, FailsToDecodePartialDigitallySigned)
+{
+ Input partial;
+ ASSERT_EQ(Success,
+ partial.Init(mTestDigitallySigned.begin(),
+ mTestDigitallySigned.length() - 5));
+ Reader partialReader(partial);
+
+ DigitallySigned parsed;
+
+ EXPECT_NE(Success, DecodeDigitallySigned(partialReader, parsed));
+}
+
+TEST_F(CTSerializationTest, EncodesDigitallySigned)
+{
+ DigitallySigned digitallySigned;
+ digitallySigned.hashAlgorithm =
+ DigitallySigned::HashAlgorithm::SHA256;
+ digitallySigned.signatureAlgorithm =
+ DigitallySigned::SignatureAlgorithm::ECDSA;
+ digitallySigned.signatureData = cloneBuffer(mTestSignatureData);
+
+ Buffer encoded;
+
+ ASSERT_EQ(Success, EncodeDigitallySigned(digitallySigned, encoded));
+ EXPECT_EQ(mTestDigitallySigned, encoded);
+}
+
+TEST_F(CTSerializationTest, EncodesLogEntryForX509Cert)
+{
+ LogEntry entry;
+ GetX509CertLogEntry(entry);
+
+ Buffer encoded;
+ ASSERT_EQ(Success, EncodeLogEntry(entry, encoded));
+ EXPECT_EQ((718U + 5U), encoded.length());
+ // First two bytes are log entry type. Next, length:
+ // Length is 718 which is 512 + 206, which is 0x2ce
+ Buffer expectedPrefix;
+ MOZ_RELEASE_ASSERT(expectedPrefix.append("\0\0\0\x2\xCE", 5));
+ Buffer encodedPrefix;
+ MOZ_RELEASE_ASSERT(encodedPrefix.
+ append(encoded.begin(), encoded.begin() + 5));
+ EXPECT_EQ(expectedPrefix, encodedPrefix);
+}
+
+TEST_F(CTSerializationTest, EncodesLogEntryForPrecert)
+{
+ LogEntry entry;
+ GetPrecertLogEntry(entry);
+
+ Buffer encoded;
+ ASSERT_EQ(Success, EncodeLogEntry(entry, encoded));
+ // log entry type + issuer key + length + tbsCertificate
+ EXPECT_EQ((2U + 32U + 3U + entry.tbsCertificate.length()), encoded.length());
+
+ // First two bytes are log entry type.
+ Buffer expectedPrefix;
+ MOZ_RELEASE_ASSERT(expectedPrefix.append("\0\x1", 2));
+ Buffer encodedPrefix;
+ MOZ_RELEASE_ASSERT(encodedPrefix.
+ append(encoded.begin(), encoded.begin() + 2));
+ EXPECT_EQ(expectedPrefix, encodedPrefix);
+
+ // Next is the issuer key (32 bytes).
+ Buffer encodedKeyHash;
+ MOZ_RELEASE_ASSERT(encodedKeyHash.
+ append(encoded.begin() + 2, encoded.begin() + 2 + 32));
+ EXPECT_EQ(GetDefaultIssuerKeyHash(), encodedKeyHash);
+}
+
+TEST_F(CTSerializationTest, EncodesV1SCTSignedData)
+{
+ uint64_t timestamp = UINT64_C(0x139fe353cf5);
+ const uint8_t DUMMY_BYTES[] = { 0x61, 0x62, 0x63 }; // abc
+ Input dummyEntry(DUMMY_BYTES);
+ Input emptyExtensions;
+ Buffer encoded;
+ ASSERT_EQ(Success, EncodeV1SCTSignedData(
+ timestamp, dummyEntry, emptyExtensions, encoded));
+ EXPECT_EQ((size_t) 15, encoded.length());
+
+ const uint8_t EXPECTED_BYTES[] = {
+ 0x00, // version
+ 0x00, // signature type
+ 0x00, 0x00, 0x01, 0x39, 0xFE, 0x35, 0x3C, 0xF5, // timestamp
+ 0x61, 0x62, 0x63, // log signature
+ 0x00, 0x00 // extensions (empty)
+ };
+ Buffer expectedBuffer;
+ MOZ_RELEASE_ASSERT(
+ expectedBuffer.append(EXPECTED_BYTES, sizeof(EXPECTED_BYTES)));
+ EXPECT_EQ(expectedBuffer, encoded);
+}
+
+TEST_F(CTSerializationTest, DecodesSCTList)
+{
+ // Two items in the list: "abc", "def"
+ const uint8_t ENCODED[] = {
+ 0x00, 0x0a, 0x00, 0x03, 0x61, 0x62, 0x63, 0x00, 0x03, 0x64, 0x65, 0x66
+ };
+ const uint8_t DECODED_1[] = { 0x61, 0x62, 0x63 };
+ const uint8_t DECODED_2[] = { 0x64, 0x65, 0x66 };
+
+ Reader listReader;
+ ASSERT_EQ(Success, DecodeSCTList(Input(ENCODED), listReader));
+
+ Input decoded1;
+ ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded1));
+
+ Input decoded2;
+ ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded2));
+
+ EXPECT_TRUE(listReader.AtEnd());
+ EXPECT_TRUE(InputsAreEqual(decoded1, Input(DECODED_1)));
+ EXPECT_TRUE(InputsAreEqual(decoded2, Input(DECODED_2)));
+}
+
+TEST_F(CTSerializationTest, FailsDecodingInvalidSCTList)
+{
+ // A list with one item that's too short (the second one)
+ const uint8_t ENCODED[] = {
+ 0x00, 0x0a, 0x00, 0x03, 0x61, 0x62, 0x63, 0x00, 0x05, 0x64, 0x65, 0x66
+ };
+
+ Reader listReader;
+ ASSERT_EQ(Success, DecodeSCTList(Input(ENCODED), listReader));
+ Input decoded1;
+ EXPECT_EQ(Success, ReadSCTListItem(listReader, decoded1));
+ Input decoded2;
+ EXPECT_NE(Success, ReadSCTListItem(listReader, decoded2));
+}
+
+TEST_F(CTSerializationTest, EncodesSCTList)
+{
+ const uint8_t SCT_1[] = { 0x61, 0x62, 0x63 };
+ const uint8_t SCT_2[] = { 0x64, 0x65, 0x66 };
+
+ Vector<Input> list;
+ ASSERT_TRUE(list.append(Move(Input(SCT_1))));
+ ASSERT_TRUE(list.append(Move(Input(SCT_2))));
+
+ Buffer encodedList;
+ ASSERT_EQ(Success, EncodeSCTList(list, encodedList));
+
+ Reader listReader;
+ ASSERT_EQ(Success, DecodeSCTList(InputForBuffer(encodedList), listReader));
+
+ Input decoded1;
+ ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded1));
+ EXPECT_TRUE(InputsAreEqual(decoded1, Input(SCT_1)));
+
+ Input decoded2;
+ ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded2));
+ EXPECT_TRUE(InputsAreEqual(decoded2, Input(SCT_2)));
+
+ EXPECT_TRUE(listReader.AtEnd());
+}
+
+TEST_F(CTSerializationTest, DecodesSignedCertificateTimestamp)
+{
+ Buffer encodedSctBuffer = GetTestSignedCertificateTimestamp();
+ Input encodedSctInput = InputForBuffer(encodedSctBuffer);
+ Reader encodedSctReader(encodedSctInput);
+
+ SignedCertificateTimestamp sct;
+ ASSERT_EQ(Success,
+ DecodeSignedCertificateTimestamp(encodedSctReader, sct));
+ EXPECT_EQ(SignedCertificateTimestamp::Version::V1, sct.version);
+ EXPECT_EQ(GetTestPublicKeyId(), sct.logId);
+ const uint64_t expectedTime = 1365181456089;
+ EXPECT_EQ(expectedTime, sct.timestamp);
+ const size_t expectedSignatureLength = 71;
+ EXPECT_EQ(expectedSignatureLength, sct.signature.signatureData.length());
+ EXPECT_TRUE(sct.extensions.empty());
+}
+
+TEST_F(CTSerializationTest, FailsDecodingInvalidSignedCertificateTimestamp)
+{
+ SignedCertificateTimestamp sct;
+
+ // Invalid version
+ const uint8_t INVALID_VERSION_BYTES[] = { 0x02, 0x00 };
+ Input invalidVersionSctInput(INVALID_VERSION_BYTES);
+ Reader invalidVersionSctReader(invalidVersionSctInput);
+ EXPECT_EQ(Result::ERROR_BAD_DER,
+ DecodeSignedCertificateTimestamp(invalidVersionSctReader, sct));
+
+ // Valid version, invalid length (missing data)
+ const uint8_t INVALID_LENGTH_BYTES[] = { 0x00, 0x0a, 0x0b, 0x0c };
+ Input invalidLengthSctInput(INVALID_LENGTH_BYTES);
+ Reader invalidLengthSctReader(invalidLengthSctInput);
+ EXPECT_EQ(Result::ERROR_BAD_DER,
+ DecodeSignedCertificateTimestamp(invalidLengthSctReader, sct));
+}
+
+TEST_F(CTSerializationTest, EncodesValidSignedTreeHead)
+{
+ SignedTreeHead signedTreeHead;
+ GetSampleSignedTreeHead(signedTreeHead);
+
+ Buffer encoded;
+ ASSERT_EQ(Success,
+ EncodeTreeHeadSignature(signedTreeHead, encoded));
+ // Expected size is 50 bytes:
+ // Byte 0 is version, byte 1 is signature type
+ // Bytes 2-9 are timestamp
+ // Bytes 10-17 are tree size
+ // Bytes 18-49 are sha256 root hash
+ ASSERT_EQ(50u, encoded.length());
+ const uint8_t EXPECTED_BYTES_PREFIX[] = {
+ 0x00, // version
+ 0x01, // signature type
+ 0x00, 0x00, 0x01, 0x45, 0x3c, 0x5f, 0xb8, 0x35, // timestamp
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15 // tree size
+ // sha256 root hash should follow
+ };
+ Buffer expectedBuffer;
+ MOZ_RELEASE_ASSERT(expectedBuffer.append(EXPECTED_BYTES_PREFIX, 18));
+ Buffer hash = GetSampleSTHSHA256RootHash();
+ MOZ_RELEASE_ASSERT(expectedBuffer.append(hash.begin(), hash.length()));
+ EXPECT_EQ(expectedBuffer, encoded);
+}
+
+} } // namespace mozilla::ct
diff --git a/security/certverifier/tests/gtest/CTTestUtils.cpp b/security/certverifier/tests/gtest/CTTestUtils.cpp
new file mode 100644
index 000000000..18ae74035
--- /dev/null
+++ b/security/certverifier/tests/gtest/CTTestUtils.cpp
@@ -0,0 +1,747 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CTTestUtils.h"
+
+#include <stdint.h>
+#include <iomanip>
+
+#include "CTSerialization.h"
+#include "gtest/gtest.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+#include "mozilla/Vector.h"
+#include "pkix/Input.h"
+#include "pkix/pkix.h"
+#include "pkix/pkixnss.h"
+#include "pkix/pkixtypes.h"
+#include "pkix/Result.h"
+#include "pkixcheck.h"
+#include "pkixutil.h"
+#include "SignedCertificateTimestamp.h"
+#include "SignedTreeHead.h"
+
+namespace mozilla { namespace ct {
+
+using namespace mozilla::pkix;
+
+// The following test vectors are from the CT test data repository at
+// https://github.com/google/certificate-transparency/tree/master/test/testdata
+
+// test-cert.pem
+const char kDefaultDerCert[] =
+ "308202ca30820233a003020102020106300d06092a864886f70d01010505003055310b3009"
+ "06035504061302474231243022060355040a131b4365727469666963617465205472616e73"
+ "706172656e6379204341310e300c0603550408130557616c65733110300e06035504071307"
+ "4572772057656e301e170d3132303630313030303030305a170d3232303630313030303030"
+ "305a3052310b30090603550406130247423121301f060355040a1318436572746966696361"
+ "7465205472616e73706172656e6379310e300c0603550408130557616c65733110300e0603"
+ "55040713074572772057656e30819f300d06092a864886f70d010101050003818d00308189"
+ "02818100b1fa37936111f8792da2081c3fe41925008531dc7f2c657bd9e1de4704160b4c9f"
+ "19d54ada4470404c1c51341b8f1f7538dddd28d9aca48369fc5646ddcc7617f8168aae5b41"
+ "d43331fca2dadfc804d57208949061f9eef902ca47ce88c644e000f06eeeccabdc9dd2f68a"
+ "22ccb09dc76e0dbc73527765b1a37a8c676253dcc10203010001a381ac3081a9301d060355"
+ "1d0e041604146a0d982a3b62c44b6d2ef4e9bb7a01aa9cb798e2307d0603551d2304763074"
+ "80145f9d880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b30090603550406"
+ "1302474231243022060355040a131b4365727469666963617465205472616e73706172656e"
+ "6379204341310e300c0603550408130557616c65733110300e060355040713074572772057"
+ "656e82010030090603551d1304023000300d06092a864886f70d010105050003818100171c"
+ "d84aac414a9a030f22aac8f688b081b2709b848b4e5511406cd707fed028597a9faefc2eee"
+ "2978d633aaac14ed3235197da87e0f71b8875f1ac9e78b281749ddedd007e3ecf50645f8cb"
+ "f667256cd6a1647b5e13203bb8582de7d6696f656d1c60b95f456b7fcf338571908f1c6972"
+ "7d24c4fccd249295795814d1dac0e6";
+
+// key hash of test-cert.pem's issuer (ca-cert.pem)
+const char kDefaultIssuerKeyHash[] =
+ "02adddca08b8bf9861f035940c940156d8350fdff899a6239c6bd77255b8f8fc";
+
+const char kDefaultDerTbsCert[] =
+ "30820233a003020102020107300d06092a864886f70d01010505003055310b300906035504"
+ "061302474231243022060355040a131b4365727469666963617465205472616e7370617265"
+ "6e6379204341310e300c0603550408130557616c65733110300e0603550407130745727720"
+ "57656e301e170d3132303630313030303030305a170d3232303630313030303030305a3052"
+ "310b30090603550406130247423121301f060355040a131843657274696669636174652054"
+ "72616e73706172656e6379310e300c0603550408130557616c65733110300e060355040713"
+ "074572772057656e30819f300d06092a864886f70d010101050003818d0030818902818100"
+ "beef98e7c26877ae385f75325a0c1d329bedf18faaf4d796bf047eb7e1ce15c95ba2f80ee4"
+ "58bd7db86f8a4b252191a79bd700c38e9c0389b45cd4dc9a120ab21e0cb41cd0e72805a410"
+ "cd9c5bdb5d4927726daf1710f60187377ea25b1a1e39eed0b88119dc154dc68f7da8e30caf"
+ "158a33e6c9509f4a05b01409ff5dd87eb50203010001a381ac3081a9301d0603551d0e0416"
+ "04142031541af25c05ffd8658b6843794f5e9036f7b4307d0603551d230476307480145f9d"
+ "880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b3009060355040613024742"
+ "31243022060355040a131b4365727469666963617465205472616e73706172656e63792043"
+ "41310e300c0603550408130557616c65733110300e060355040713074572772057656e8201"
+ "0030090603551d1304023000";
+
+// DigitallySigned of test-cert.proof
+const char kTestDigitallySigned[] =
+ "0403004730450220606e10ae5c2d5a1b0aed49dc4937f48de71a4e9784e9c208dfbfe9ef53"
+ "6cf7f2022100beb29c72d7d06d61d06bdb38a069469aa86fe12e18bb7cc45689a2c0187ef5"
+ "a5";
+
+// test-cert.proof
+const char kTestSignedCertificateTimestamp[] =
+ "00df1c2ec11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d7640000013d"
+ "db27ded900000403004730450220606e10ae5c2d5a1b0aed49dc4937f48de71a4e9784e9c2"
+ "08dfbfe9ef536cf7f2022100beb29c72d7d06d61d06bdb38a069469aa86fe12e18bb7cc456"
+ "89a2c0187ef5a5";
+
+// ct-server-key-public.pem
+const char kEcP256PublicKey[] =
+ "3059301306072a8648ce3d020106082a8648ce3d0301070342000499783cb14533c0161a5a"
+ "b45bf95d08a29cd0ea8dd4c84274e2be59ad15c676960cf0afa1074a57ac644b23479e5b3f"
+ "b7b245eb4b420ef370210371a944beaceb";
+
+// key id (sha256) of ct-server-key-public.pem
+const char kTestKeyId[] =
+ "df1c2ec11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d764";
+
+// signature field of DigitallySigned from test-cert.proof
+const char kTestSCTSignatureData[] =
+ "30450220606e10ae5c2d5a1b0aed49dc4937f48de71a4e9784e9c208dfbfe9ef536cf7f202"
+ "2100beb29c72d7d06d61d06bdb38a069469aa86fe12e18bb7cc45689a2c0187ef5a5";
+
+// signature field of DigitallySigned from test-embedded-pre-cert.proof
+const char kTestSCTPrecertSignatureData[] =
+ "30450220482f6751af35dba65436be1fd6640f3dbf9a41429495924530288fa3e5e23e0602"
+ "2100e4edc0db3ac572b1e2f5e8ab6a680653987dcf41027dfeffa105519d89edbf08";
+
+// For the sample STH
+const char kSampleSTHSHA256RootHash[] =
+ "726467216167397babca293dca398e4ce6b621b18b9bc42f30c900d1f92ac1e4";
+const char kSampleSTHTreeHeadSignature[] =
+ "0403004730450220365a91a2a88f2b9332f41d8959fa7086da7e6d634b7b089bc9da066426"
+ "6c7a20022100e38464f3c0fd066257b982074f7ac87655e0c8f714768a050b4be9a7b441cb"
+ "d3";
+const size_t kSampleSTHTreeSize = 21u;
+const uint64_t kSampleSTHTimestamp = 1396877277237u;
+
+// test-embedded-cert.pem
+const char kTestEmbeddedCertData[] =
+ "30820359308202c2a003020102020107300d06092a864886f70d01010505"
+ "003055310b300906035504061302474231243022060355040a131b436572"
+ "7469666963617465205472616e73706172656e6379204341310e300c0603"
+ "550408130557616c65733110300e060355040713074572772057656e301e"
+ "170d3132303630313030303030305a170d3232303630313030303030305a"
+ "3052310b30090603550406130247423121301f060355040a131843657274"
+ "69666963617465205472616e73706172656e6379310e300c060355040813"
+ "0557616c65733110300e060355040713074572772057656e30819f300d06"
+ "092a864886f70d010101050003818d0030818902818100beef98e7c26877"
+ "ae385f75325a0c1d329bedf18faaf4d796bf047eb7e1ce15c95ba2f80ee4"
+ "58bd7db86f8a4b252191a79bd700c38e9c0389b45cd4dc9a120ab21e0cb4"
+ "1cd0e72805a410cd9c5bdb5d4927726daf1710f60187377ea25b1a1e39ee"
+ "d0b88119dc154dc68f7da8e30caf158a33e6c9509f4a05b01409ff5dd87e"
+ "b50203010001a382013a30820136301d0603551d0e041604142031541af2"
+ "5c05ffd8658b6843794f5e9036f7b4307d0603551d230476307480145f9d"
+ "880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b30090603"
+ "5504061302474231243022060355040a131b436572746966696361746520"
+ "5472616e73706172656e6379204341310e300c0603550408130557616c65"
+ "733110300e060355040713074572772057656e82010030090603551d1304"
+ "02300030818a060a2b06010401d679020402047c047a0078007600df1c2e"
+ "c11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d76400"
+ "00013ddb27df9300000403004730450220482f6751af35dba65436be1fd6"
+ "640f3dbf9a41429495924530288fa3e5e23e06022100e4edc0db3ac572b1"
+ "e2f5e8ab6a680653987dcf41027dfeffa105519d89edbf08300d06092a86"
+ "4886f70d0101050500038181008a0c4bef099d479279afa0a28e689f91e1"
+ "c4421be2d269a2ea6ca4e8215ddeddca1504a11e7c87c4b77e80f0e97903"
+ "5268f27ca20e166804ae556f316981f96a394ab7abfd3e255ac0044513fe"
+ "76570c6795abe4703133d303f89f3afa6bbcfc517319dfd95b934241211f"
+ "634035c3d078307a68c6075a2e20c89f36b8910ca0";
+
+const char kTestTbsCertData[] =
+ "30820233a003020102020107300d06092a864886f70d0101050500305531"
+ "0b300906035504061302474231243022060355040a131b43657274696669"
+ "63617465205472616e73706172656e6379204341310e300c060355040813"
+ "0557616c65733110300e060355040713074572772057656e301e170d3132"
+ "303630313030303030305a170d3232303630313030303030305a3052310b"
+ "30090603550406130247423121301f060355040a13184365727469666963"
+ "617465205472616e73706172656e6379310e300c0603550408130557616c"
+ "65733110300e060355040713074572772057656e30819f300d06092a8648"
+ "86f70d010101050003818d0030818902818100beef98e7c26877ae385f75"
+ "325a0c1d329bedf18faaf4d796bf047eb7e1ce15c95ba2f80ee458bd7db8"
+ "6f8a4b252191a79bd700c38e9c0389b45cd4dc9a120ab21e0cb41cd0e728"
+ "05a410cd9c5bdb5d4927726daf1710f60187377ea25b1a1e39eed0b88119"
+ "dc154dc68f7da8e30caf158a33e6c9509f4a05b01409ff5dd87eb5020301"
+ "0001a381ac3081a9301d0603551d0e041604142031541af25c05ffd8658b"
+ "6843794f5e9036f7b4307d0603551d230476307480145f9d880dc873e654"
+ "d4f80dd8e6b0c124b447c355a159a4573055310b30090603550406130247"
+ "4231243022060355040a131b4365727469666963617465205472616e7370"
+ "6172656e6379204341310e300c0603550408130557616c65733110300e06"
+ "0355040713074572772057656e82010030090603551d1304023000";
+
+// test-embedded-with-preca-cert.pem
+const char kTestEmbeddedWithPreCaCertData[] =
+ "30820359308202c2a003020102020108300d06092a864886f70d01010505"
+ "003055310b300906035504061302474231243022060355040a131b436572"
+ "7469666963617465205472616e73706172656e6379204341310e300c0603"
+ "550408130557616c65733110300e060355040713074572772057656e301e"
+ "170d3132303630313030303030305a170d3232303630313030303030305a"
+ "3052310b30090603550406130247423121301f060355040a131843657274"
+ "69666963617465205472616e73706172656e6379310e300c060355040813"
+ "0557616c65733110300e060355040713074572772057656e30819f300d06"
+ "092a864886f70d010101050003818d0030818902818100afaeeacac51ab7"
+ "cebdf9eacae7dd175295e193955a17989aef8d97ab7cdff7761093c0b823"
+ "d2a4e3a51a17b86f28162b66a2538935ebecdc1036233da2dd6531b0c63b"
+ "cc68761ebdc854037b77399246b870a7b72b14c9b1667de09a9640ed9f3f"
+ "3c725d950b4d26559869fe7f1e919a66eb76d35c0117c6bcd0d8cfd21028"
+ "b10203010001a382013a30820136301d0603551d0e04160414612c64efac"
+ "79b728397c9d93e6df86465fa76a88307d0603551d230476307480145f9d"
+ "880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b30090603"
+ "5504061302474231243022060355040a131b436572746966696361746520"
+ "5472616e73706172656e6379204341310e300c0603550408130557616c65"
+ "733110300e060355040713074572772057656e82010030090603551d1304"
+ "02300030818a060a2b06010401d679020402047c047a0078007600df1c2e"
+ "c11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d76400"
+ "00013ddb27e05b000004030047304502207aa79604c47480f3727b084f90"
+ "b3989f79091885e00484431a2a297cbf3a355c022100b49fd8120b0d644c"
+ "d7e75269b4da6317a9356cb950224fc11cc296b2e39b2386300d06092a86"
+ "4886f70d010105050003818100a3a86c41ad0088a25aedc4e7b529a2ddbf"
+ "9e187ffb362157e9302d961b73b43cba0ae1e230d9e45049b7e8c924792e"
+ "bbe7d175baa87b170dfad8ee788984599d05257994084e2e0e796fca5836"
+ "881c3e053553e06ab230f919089b914e4a8e2da45f8a87f2c81a25a61f04"
+ "fe1cace60155653827d41fad9f0658f287d058192c";
+
+// ca-cert.pem
+const char kCaCertData[] =
+ "308202d030820239a003020102020100300d06092a864886f70d01010505"
+ "003055310b300906035504061302474231243022060355040a131b436572"
+ "7469666963617465205472616e73706172656e6379204341310e300c0603"
+ "550408130557616c65733110300e060355040713074572772057656e301e"
+ "170d3132303630313030303030305a170d3232303630313030303030305a"
+ "3055310b300906035504061302474231243022060355040a131b43657274"
+ "69666963617465205472616e73706172656e6379204341310e300c060355"
+ "0408130557616c65733110300e060355040713074572772057656e30819f"
+ "300d06092a864886f70d010101050003818d0030818902818100d58a6853"
+ "6210a27119936e778321181c2a4013c6d07b8c76eb9157d3d0fb4b3b516e"
+ "cecbd1c98d91c52f743fab635d55099cd13abaf31ae541442451a74c7816"
+ "f2243cf848cf2831cce67ba04a5a23819f3cba37e624d9c3bdb299b839dd"
+ "fe2631d2cb3a84fc7bb2b5c52fcfc14fff406f5cd44669cbb2f7cfdf86fb"
+ "6ab9d1b10203010001a381af3081ac301d0603551d0e041604145f9d880d"
+ "c873e654d4f80dd8e6b0c124b447c355307d0603551d230476307480145f"
+ "9d880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b300906"
+ "035504061302474231243022060355040a131b4365727469666963617465"
+ "205472616e73706172656e6379204341310e300c0603550408130557616c"
+ "65733110300e060355040713074572772057656e820100300c0603551d13"
+ "040530030101ff300d06092a864886f70d0101050500038181000608cc4a"
+ "6d64f2205e146c04b276f92b0efa94a5daf23afc3806606d3990d0a1ea23"
+ "3d40295769463b046661e7fa1d179915209aea2e0a775176411227d7c003"
+ "07c7470e61584fd7334224727f51d690bc47a9df354db0f6eb25955de189"
+ "3c4dd5202b24a2f3e440d274b54e1bd376269ca96289b76ecaa41090e14f"
+ "3b0a942e";
+
+// intermediate-cert.pem
+const char kIntermediateCertData[] =
+ "308202dd30820246a003020102020109300d06092a864886f70d01010505"
+ "003055310b300906035504061302474231243022060355040a131b436572"
+ "7469666963617465205472616e73706172656e6379204341310e300c0603"
+ "550408130557616c65733110300e060355040713074572772057656e301e"
+ "170d3132303630313030303030305a170d3232303630313030303030305a"
+ "3062310b30090603550406130247423131302f060355040a132843657274"
+ "69666963617465205472616e73706172656e637920496e7465726d656469"
+ "617465204341310e300c0603550408130557616c65733110300e06035504"
+ "0713074572772057656e30819f300d06092a864886f70d01010105000381"
+ "8d0030818902818100d76a678d116f522e55ff821c90642508b7074b14d7"
+ "71159064f7927efdedb87135a1365ee7de18cbd5ce865f860c78f433b4d0"
+ "d3d3407702e7a3ef542b1dfe9bbaa7cdf94dc5975fc729f86f105f381b24"
+ "3535cf9c800f5ca780c1d3c84400ee65d16ee9cf52db8adffe50f5c49335"
+ "0b2190bf50d5bc36f3cac5a8daae92cd8b0203010001a381af3081ac301d"
+ "0603551d0e04160414965508050278479e8773764131bc143a47e229ab30"
+ "7d0603551d230476307480145f9d880dc873e654d4f80dd8e6b0c124b447"
+ "c355a159a4573055310b300906035504061302474231243022060355040a"
+ "131b4365727469666963617465205472616e73706172656e637920434131"
+ "0e300c0603550408130557616c65733110300e0603550407130745727720"
+ "57656e820100300c0603551d13040530030101ff300d06092a864886f70d"
+ "0101050500038181002206dab1c66b71dce095c3f6aa2ef72cf7761be7ab"
+ "d7fc39c31a4cfe1bd96d6734ca82f22dde5a0c8bbbdd825d7b6f3e7612ad"
+ "8db300a7e21169886023262284c3aa5d2191efda10bf9235d37b3a2a340d"
+ "59419b94a48566f3fac3cd8b53d5a4e98270ead297b07210f9ce4a2138b1"
+ "8811143b93fa4e7a87dd37e1385f2c2908";
+
+// test-embedded-with-intermediate-cert.pem
+const char kTestEmbeddedWithIntermediateCertData[] =
+ "30820366308202cfa003020102020102300d06092a864886f70d01010505"
+ "003062310b30090603550406130247423131302f060355040a1328436572"
+ "7469666963617465205472616e73706172656e637920496e7465726d6564"
+ "69617465204341310e300c0603550408130557616c65733110300e060355"
+ "040713074572772057656e301e170d3132303630313030303030305a170d"
+ "3232303630313030303030305a3052310b30090603550406130247423121"
+ "301f060355040a13184365727469666963617465205472616e7370617265"
+ "6e6379310e300c0603550408130557616c65733110300e06035504071307"
+ "4572772057656e30819f300d06092a864886f70d010101050003818d0030"
+ "818902818100bb272b26e5deb5459d4acca027e8f12a4d839ac3730a6a10"
+ "9ff7e25498ddbd3f1895d08ba41f8de34967a3a086ce13a90dd5adbb5418"
+ "4bdc08e1ac7826adb8dc9c717bfd7da5b41b4db1736e00f1dac3cec9819c"
+ "cb1a28ba120b020a820e940dd61f95b5432a4bc05d0818f18ce2154eb38d"
+ "2fa7d22d72b976e560db0c7fc77f0203010001a382013a30820136301d06"
+ "03551d0e04160414b1b148e658e703f5f7f3105f20b3c384d7eff1bf307d"
+ "0603551d23047630748014965508050278479e8773764131bc143a47e229"
+ "aba159a4573055310b300906035504061302474231243022060355040a13"
+ "1b4365727469666963617465205472616e73706172656e6379204341310e"
+ "300c0603550408130557616c65733110300e060355040713074572772057"
+ "656e82010930090603551d130402300030818a060a2b06010401d6790204"
+ "02047c047a0078007600df1c2ec11500945247a96168325ddc5c7959e8f7"
+ "c6d388fc002e0bbd3f74d7640000013ddb27e2a400000403004730450221"
+ "00a6d34517f3392d9ec5d257adf1c597dc45bd4cd3b73856c616a9fb99e5"
+ "ae75a802205e26c8d1c7e222fe8cda29baeb04a834ee97d34fd81718f1aa"
+ "e0cd66f4b8a93f300d06092a864886f70d0101050500038181000f95a5b4"
+ "e128a914b1e88be8b32964221b58f4558433d020a8e246cca65a40bcbf5f"
+ "2d48933ebc99be6927ca756472fb0bdc7f505f41f462f2bc19d0b299c990"
+ "918df8820f3d31db37979e8bad563b17f00ae67b0f8731c106c943a73bf5"
+ "36af168afe21ef4adfcae19a3cc074899992bf506bc5ce1decaaf07ffeeb"
+ "c805c039";
+
+// test-embedded-with-intermediate-preca-cert.pem
+const char kTestEmbeddedWithIntermediatePreCaCertData[] =
+ "30820366308202cfa003020102020103300d06092a864886f70d01010505"
+ "003062310b30090603550406130247423131302f060355040a1328436572"
+ "7469666963617465205472616e73706172656e637920496e7465726d6564"
+ "69617465204341310e300c0603550408130557616c65733110300e060355"
+ "040713074572772057656e301e170d3132303630313030303030305a170d"
+ "3232303630313030303030305a3052310b30090603550406130247423121"
+ "301f060355040a13184365727469666963617465205472616e7370617265"
+ "6e6379310e300c0603550408130557616c65733110300e06035504071307"
+ "4572772057656e30819f300d06092a864886f70d010101050003818d0030"
+ "818902818100d4497056cdfc65e1342cc3df6e654b8af0104702acd2275c"
+ "7d3fb1fc438a89b212110d6419bcc13ae47d64bba241e6706b9ed627f8b3"
+ "4a0d7dff1c44b96287c54bea9d10dc017bceb64f7b6aff3c35a474afec40"
+ "38ab3640b0cd1fb0582ec03b179a2776c8c435d14ab4882d59d7b724fa37"
+ "7ca6db08392173f9c6056b3abadf0203010001a382013a30820136301d06"
+ "03551d0e0416041432da5518d87f1d26ea2767973c0bef286e786a4a307d"
+ "0603551d23047630748014965508050278479e8773764131bc143a47e229"
+ "aba159a4573055310b300906035504061302474231243022060355040a13"
+ "1b4365727469666963617465205472616e73706172656e6379204341310e"
+ "300c0603550408130557616c65733110300e060355040713074572772057"
+ "656e82010930090603551d130402300030818a060a2b06010401d6790204"
+ "02047c047a0078007600df1c2ec11500945247a96168325ddc5c7959e8f7"
+ "c6d388fc002e0bbd3f74d7640000013ddb27e3be00000403004730450221"
+ "00d9f61a07fee021e3159f3ca2f570d833ff01374b2096cba5658c5e16fb"
+ "43eb3002200b76fe475138d8cf76833831304dabf043eb1213c96e13ff4f"
+ "a37f7cd3c8dc1f300d06092a864886f70d01010505000381810088ee4e9e"
+ "5eed6b112cc764b151ed929400e9406789c15fbbcfcdab2f10b400234139"
+ "e6ce65c1e51b47bf7c8950f80bccd57168567954ed35b0ce9346065a5eae"
+ "5bf95d41da8e27cee9eeac688f4bd343f9c2888327abd8b9f68dcb1e3050"
+ "041d31bda8e2dd6d39b3664de5ce0870f5fc7e6a00d6ed00528458d953d2"
+ "37586d73";
+
+static uint8_t
+CharToByte(char c)
+{
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ }
+ MOZ_RELEASE_ASSERT(false);
+ return 0;
+}
+
+static Buffer
+HexToBytes(const char* hexData)
+{
+ size_t hexLen = strlen(hexData);
+ MOZ_RELEASE_ASSERT(hexLen > 0 && (hexLen % 2 == 0));
+ size_t resultLen = hexLen / 2;
+ Buffer result;
+ MOZ_RELEASE_ASSERT(result.reserve(resultLen));
+ for (size_t i = 0; i < resultLen; ++i) {
+ uint8_t hi = CharToByte(hexData[i*2]);
+ uint8_t lo = CharToByte(hexData[i*2 + 1]);
+ result.infallibleAppend((hi << 4) | lo);
+ }
+ return result;
+}
+
+
+void
+GetX509CertLogEntry(LogEntry& entry)
+{
+ entry.Reset();
+ entry.type = ct::LogEntry::Type::X509;
+ entry.leafCertificate = HexToBytes(kDefaultDerCert);
+}
+
+Buffer
+GetDEREncodedX509Cert()
+{
+ return HexToBytes(kDefaultDerCert);
+}
+
+void
+GetPrecertLogEntry(LogEntry& entry)
+{
+ entry.Reset();
+ entry.type = ct::LogEntry::Type::Precert;
+ entry.issuerKeyHash = HexToBytes(kDefaultIssuerKeyHash);
+ entry.tbsCertificate = HexToBytes(kDefaultDerTbsCert);
+}
+
+Buffer
+GetTestDigitallySigned()
+{
+ return HexToBytes(kTestDigitallySigned);
+}
+
+Buffer
+GetTestDigitallySignedData()
+{
+ Buffer encoded = GetTestDigitallySigned();
+ // The encoded buffer contains the signature data itself from the 4th byte.
+ // The first bytes are:
+ // 1 byte of hash algorithm
+ // 1 byte of signature algorithm
+ // 2 bytes - prefix containing length of the signature data.
+ Buffer result;
+ MOZ_RELEASE_ASSERT(result.append(encoded.begin() + 4, encoded.end()));
+ return result;
+}
+
+Buffer
+GetTestSignedCertificateTimestamp()
+{
+ return HexToBytes(kTestSignedCertificateTimestamp);
+}
+
+Buffer
+GetTestPublicKey()
+{
+ return HexToBytes(kEcP256PublicKey);
+}
+
+Buffer
+GetTestPublicKeyId()
+{
+ return HexToBytes(kTestKeyId);
+}
+
+void
+GetX509CertSCT(SignedCertificateTimestamp& sct)
+{
+ sct.version = ct::SignedCertificateTimestamp::Version::V1;
+ sct.logId = HexToBytes(kTestKeyId);
+ // Time the log issued a SCT for this certificate, which is
+ // Fri Apr 5 10:04:16.089 2013
+ sct.timestamp = INT64_C(1365181456089);
+ sct.extensions.clear();
+
+ sct.signature.hashAlgorithm =
+ ct::DigitallySigned::HashAlgorithm::SHA256;
+ sct.signature.signatureAlgorithm =
+ ct::DigitallySigned::SignatureAlgorithm::ECDSA;
+ sct.signature.signatureData = HexToBytes(kTestSCTSignatureData);
+}
+
+void
+GetPrecertSCT(SignedCertificateTimestamp& sct)
+{
+ sct.version = ct::SignedCertificateTimestamp::Version::V1;
+ sct.logId = HexToBytes(kTestKeyId);
+ // Time the log issued a SCT for this Precertificate, which is
+ // Fri Apr 5 10:04:16.275 2013
+ sct.timestamp = INT64_C(1365181456275);
+ sct.extensions.clear();
+
+ sct.signature.hashAlgorithm =
+ ct::DigitallySigned::HashAlgorithm::SHA256;
+ sct.signature.signatureAlgorithm =
+ ct::DigitallySigned::SignatureAlgorithm::ECDSA;
+ sct.signature.signatureData = HexToBytes(kTestSCTPrecertSignatureData);
+}
+
+Buffer
+GetDefaultIssuerKeyHash()
+{
+ return HexToBytes(kDefaultIssuerKeyHash);
+}
+
+// A sample, valid STH
+void
+GetSampleSignedTreeHead(SignedTreeHead& sth)
+{
+ sth.version = SignedTreeHead::Version::V1;
+ sth.timestamp = kSampleSTHTimestamp;
+ sth.treeSize = kSampleSTHTreeSize;
+ sth.sha256RootHash = GetSampleSTHSHA256RootHash();
+ GetSampleSTHTreeHeadDecodedSignature(sth.signature);
+}
+
+Buffer
+GetSampleSTHSHA256RootHash()
+{
+ return HexToBytes(kSampleSTHSHA256RootHash);
+}
+
+Buffer
+GetSampleSTHTreeHeadSignature()
+{
+ return HexToBytes(kSampleSTHTreeHeadSignature);
+}
+
+void
+GetSampleSTHTreeHeadDecodedSignature(DigitallySigned& signature)
+{
+ Buffer ths = HexToBytes(kSampleSTHTreeHeadSignature);
+ Input thsInput;
+ ASSERT_EQ(Success, thsInput.Init(ths.begin(), ths.length()));
+ Reader thsReader(thsInput);
+ ASSERT_EQ(Success, DecodeDigitallySigned(thsReader, signature));
+ ASSERT_TRUE(thsReader.AtEnd());
+}
+
+Buffer
+GetDEREncodedTestEmbeddedCert()
+{
+ return HexToBytes(kTestEmbeddedCertData);
+}
+
+Buffer
+GetDEREncodedTestTbsCert()
+{
+ return HexToBytes(kTestTbsCertData);
+}
+
+Buffer
+GetDEREncodedTestEmbeddedWithPreCACert()
+{
+ return HexToBytes(kTestEmbeddedWithPreCaCertData);
+}
+
+Buffer
+GetDEREncodedCACert()
+{
+ return HexToBytes(kCaCertData);
+}
+
+Buffer
+GetDEREncodedIntermediateCert()
+{
+ return HexToBytes(kIntermediateCertData);
+}
+
+Buffer
+GetDEREncodedTestEmbeddedWithIntermediateCert()
+{
+ return HexToBytes(kTestEmbeddedWithIntermediateCertData);
+}
+
+Buffer
+GetDEREncodedTestEmbeddedWithIntermediatePreCACert()
+{
+ return HexToBytes(kTestEmbeddedWithIntermediatePreCaCertData);
+}
+
+Buffer
+ExtractCertSPKI(Input cert)
+{
+ BackCert backCert(cert, EndEntityOrCA::MustBeEndEntity, nullptr);
+ MOZ_RELEASE_ASSERT(backCert.Init() == Success);
+
+ Input spkiInput = backCert.GetSubjectPublicKeyInfo();
+ Buffer spki;
+ MOZ_RELEASE_ASSERT(InputToBuffer(spkiInput, spki) == Success);
+ return spki;
+}
+
+Buffer
+ExtractCertSPKI(const Buffer& cert)
+{
+ return ExtractCertSPKI(InputForBuffer(cert));
+}
+
+void
+ExtractEmbeddedSCTList(Input cert, Buffer& result)
+{
+ result.clear();
+ BackCert backCert(cert, EndEntityOrCA::MustBeEndEntity, nullptr);
+ ASSERT_EQ(Success, backCert.Init());
+ const Input* scts = backCert.GetSignedCertificateTimestamps();
+ if (scts) {
+ Input sctList;
+ ASSERT_EQ(Success,
+ ExtractSignedCertificateTimestampListFromExtension(*scts,
+ sctList));
+ ASSERT_EQ(Success, InputToBuffer(sctList, result));
+ }
+}
+
+void
+ExtractEmbeddedSCTList(const Buffer& cert, Buffer& result)
+{
+ ExtractEmbeddedSCTList(InputForBuffer(cert), result);
+}
+
+class OCSPExtensionTrustDomain : public TrustDomain
+{
+public:
+ Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
+ Input, TrustLevel&) override
+ {
+ ADD_FAILURE();
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result FindIssuer(Input, IssuerChecker&, Time) override
+ {
+ ADD_FAILURE();
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
+ const Input*, const Input*) override
+ {
+ ADD_FAILURE();
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result IsChainValid(const DERArray&, Time) override
+ {
+ ADD_FAILURE();
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result DigestBuf(Input item, DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf, size_t digestBufLen) override
+ {
+ return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
+ }
+
+ Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time)
+ override
+ {
+ ADD_FAILURE();
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
+ {
+ ADD_FAILURE();
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+ Input subjectPublicKeyInfo) override
+ {
+ return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+ nullptr);
+ }
+
+ Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
+ override
+ {
+ ADD_FAILURE();
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+ Input subjectPublicKeyInfo) override
+ {
+ return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+ nullptr);
+ }
+
+ Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId)
+ override
+ {
+ ADD_FAILURE();
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result NetscapeStepUpMatchesServerAuth(Time, bool&) override
+ {
+ ADD_FAILURE();
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ void NoteAuxiliaryExtension(AuxiliaryExtension extension, Input data) override
+ {
+ if (extension != AuxiliaryExtension::SCTListFromOCSPResponse) {
+ ADD_FAILURE();
+ return;
+ }
+ if (InputToBuffer(data, signedCertificateTimestamps) != Success) {
+ ADD_FAILURE();
+ return;
+ }
+ }
+
+ Buffer signedCertificateTimestamps;
+};
+
+void
+ExtractSCTListFromOCSPResponse(Input cert,
+ Input issuerSPKI,
+ Input encodedResponse,
+ Time time,
+ Buffer& result)
+{
+ result.clear();
+
+ BackCert backCert(cert, EndEntityOrCA::MustBeEndEntity, nullptr);
+ ASSERT_EQ(Success, backCert.Init());
+
+ CertID certID(backCert.GetIssuer(), issuerSPKI, backCert.GetSerialNumber());
+
+ bool expired;
+ OCSPExtensionTrustDomain trustDomain;
+ Result rv = VerifyEncodedOCSPResponse(trustDomain, certID,
+ time, /*time*/
+ 1000, /*maxLifetimeInDays*/
+ encodedResponse, expired);
+ ASSERT_EQ(Success, rv);
+
+ result = Move(trustDomain.signedCertificateTimestamps);
+}
+
+Buffer
+cloneBuffer(const Buffer& buffer)
+{
+ Buffer cloned;
+ MOZ_RELEASE_ASSERT(cloned.appendAll(buffer));
+ return cloned;
+}
+
+Input
+InputForBuffer(const Buffer& buffer)
+{
+ Input input;
+ MOZ_RELEASE_ASSERT(Success ==
+ input.Init(buffer.begin(), buffer.length()));
+ return input;
+}
+
+Input InputForSECItem(const SECItem& item)
+{
+ Input input;
+ MOZ_RELEASE_ASSERT(Success ==
+ input.Init(item.data, item.len));
+ return input;
+}
+
+} } // namespace mozilla::ct
+
+namespace mozilla {
+
+std::ostream&
+operator<<(std::ostream& stream, const ct::Buffer& buffer)
+{
+ if (buffer.empty()) {
+ stream << "EMPTY";
+ } else {
+ for (size_t i = 0; i < buffer.length(); ++i) {
+ if (i >= 1000) {
+ stream << "...";
+ break;
+ }
+ stream << std::hex << std::setw(2) << std::setfill('0')
+ << static_cast<unsigned>(buffer[i]);
+ }
+ }
+ stream << std::dec;
+ return stream;
+}
+
+} // namespace mozilla
diff --git a/security/certverifier/tests/gtest/CTTestUtils.h b/security/certverifier/tests/gtest/CTTestUtils.h
new file mode 100644
index 000000000..447d6bb1c
--- /dev/null
+++ b/security/certverifier/tests/gtest/CTTestUtils.h
@@ -0,0 +1,132 @@
+/* -*- 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 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/. */
+
+#ifndef CTTestUtils_h
+#define CTTestUtils_h
+
+#include <iostream>
+
+#include "pkix/Input.h"
+#include "pkix/Time.h"
+#include "seccomon.h"
+#include "SignedCertificateTimestamp.h"
+#include "SignedTreeHead.h"
+
+namespace mozilla { namespace ct {
+
+// Note: unless specified otherwise, all test data is taken from
+// Certificate Transparency test data repository at
+// https://github.com/google/certificate-transparency/tree/master/test/testdata
+
+// Fills |entry| with test data for an X.509 entry.
+void GetX509CertLogEntry(LogEntry& entry);
+
+// Returns a DER-encoded X509 cert. The SCT provided by
+// GetX509CertSCT is signed over this certificate.
+Buffer GetDEREncodedX509Cert();
+
+// Fills |entry| with test data for a Precertificate entry.
+void GetPrecertLogEntry(LogEntry& entry);
+
+// Returns the binary representation of a test DigitallySigned.
+Buffer GetTestDigitallySigned();
+
+// Returns the source data of the test DigitallySigned.
+Buffer GetTestDigitallySignedData();
+
+// Returns the binary representation of a test serialized SCT.
+Buffer GetTestSignedCertificateTimestamp();
+
+// Test log key.
+Buffer GetTestPublicKey();
+
+// ID of test log key.
+Buffer GetTestPublicKeyId();
+
+// SCT for the X509Certificate provided above.
+void GetX509CertSCT(SignedCertificateTimestamp& sct);
+
+// SCT for the Precertificate log entry provided above.
+void GetPrecertSCT(SignedCertificateTimestamp& sct);
+
+// Issuer key hash.
+Buffer GetDefaultIssuerKeyHash();
+
+// A sample, valid STH.
+void GetSampleSignedTreeHead(SignedTreeHead& sth);
+
+// The SHA256 root hash for the sample STH.
+Buffer GetSampleSTHSHA256RootHash();
+
+// The tree head signature for the sample STH.
+Buffer GetSampleSTHTreeHeadSignature();
+
+// The same signature as GetSampleSTHTreeHeadSignature, decoded.
+void GetSampleSTHTreeHeadDecodedSignature(DigitallySigned& signature);
+
+// Certificate with embedded SCT in an X509v3 extension.
+Buffer GetDEREncodedTestEmbeddedCert();
+
+// For the above certificate, the corresponsing TBSCertificate without
+// the embedded SCT extension.
+Buffer GetDEREncodedTestTbsCert();
+
+// As above, but signed with an intermediate CA certificate containing
+// the CT extended key usage OID 1.3.6.1.4.1.11129.2.4.4 for issuing precerts
+// (i.e. signed with a "precert CA certificate").
+Buffer GetDEREncodedTestEmbeddedWithPreCACert();
+
+// The issuer of the above certificates (self-signed root CA certificate).
+Buffer GetDEREncodedCACert();
+
+// An intermediate CA certificate issued by the above CA.
+Buffer GetDEREncodedIntermediateCert();
+
+// Certificate with embedded SCT signed by the intermediate certificate above.
+Buffer GetDEREncodedTestEmbeddedWithIntermediateCert();
+
+// As above, but signed by the precert CA certificate.
+Buffer GetDEREncodedTestEmbeddedWithIntermediatePreCACert();
+
+// Given a DER-encoded certificate, returns its SubjectPublicKeyInfo.
+Buffer ExtractCertSPKI(pkix::Input cert);
+Buffer ExtractCertSPKI(const Buffer& cert);
+
+// Extracts a SignedCertificateTimestampList from the provided leaf certificate
+// (kept in X.509v3 extension with OID 1.3.6.1.4.1.11129.2.4.2).
+void ExtractEmbeddedSCTList(pkix::Input cert, Buffer& result);
+void ExtractEmbeddedSCTList(const Buffer& cert, Buffer& result);
+
+// Extracts a SignedCertificateTimestampList that has been embedded within
+// an OCSP response as an extension with the OID 1.3.6.1.4.1.11129.2.4.5.
+// The OCSP response is verified, and the verification must succeed for the
+// extension to be extracted.
+void ExtractSCTListFromOCSPResponse(pkix::Input cert,
+ pkix::Input issuerSPKI,
+ pkix::Input encodedResponse,
+ pkix::Time time,
+ Buffer& result);
+
+// We need this in tests code since mozilla::Vector only allows move assignment.
+Buffer cloneBuffer(const Buffer& buffer);
+
+// Returns Input for the data stored in the buffer, failing assertion on error.
+pkix::Input InputForBuffer(const Buffer& buffer);
+
+// Returns Input for the data stored in the item, failing assertion on error.
+pkix::Input InputForSECItem(const SECItem& item);
+
+} } // namespace mozilla::ct
+
+
+namespace mozilla {
+
+// GTest needs this to be in Buffer's namespace (i.e. in mozilla::Vector's).
+std::ostream& operator<<(std::ostream& stream, const ct::Buffer& buf);
+
+} // namespace mozilla
+
+#endif // CTTestUtils_h
diff --git a/security/certverifier/tests/gtest/MultiLogCTVerifierTest.cpp b/security/certverifier/tests/gtest/MultiLogCTVerifierTest.cpp
new file mode 100644
index 000000000..f8f04804e
--- /dev/null
+++ b/security/certverifier/tests/gtest/MultiLogCTVerifierTest.cpp
@@ -0,0 +1,233 @@
+/* -*- 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MultiLogCTVerifier.h"
+
+#include <stdint.h>
+
+#include "CTLogVerifier.h"
+#include "CTObjectsExtractor.h"
+#include "CTSerialization.h"
+#include "CTTestUtils.h"
+#include "gtest/gtest.h"
+#include "mozilla/EnumSet.h"
+#include "mozilla/Move.h"
+#include "nss.h"
+
+namespace mozilla { namespace ct {
+
+using namespace mozilla::pkix;
+
+class MultiLogCTVerifierTest : public ::testing::Test
+{
+public:
+ MultiLogCTVerifierTest()
+ : mNow(Time::uninitialized)
+ {}
+
+ void SetUp() override
+ {
+ // Does nothing if NSS is already initialized.
+ MOZ_RELEASE_ASSERT(NSS_NoDB_Init(nullptr) == SECSuccess);
+
+ ASSERT_EQ(Success, mVerifier.AddLog(InputForBuffer(GetTestPublicKey())));
+
+ mTestCert = GetDEREncodedX509Cert();
+ mEmbeddedCert = GetDEREncodedTestEmbeddedCert();
+ mCaCert = GetDEREncodedCACert();
+ mCaCertSPKI = ExtractCertSPKI(mCaCert);
+ mIntermediateCert = GetDEREncodedIntermediateCert();
+ mIntermediateCertSPKI = ExtractCertSPKI(mIntermediateCert);
+
+ // Set the current time making sure all test timestamps are in the past.
+ mNow = TimeFromEpochInSeconds(1451606400u); // Date.parse("2016-01-01")/1000
+ }
+
+ void CheckForSingleVerifiedSCTInResult(const CTVerifyResult& result,
+ SignedCertificateTimestamp::Origin origin)
+ {
+ EXPECT_EQ(0U, result.decodingErrors);
+ ASSERT_EQ(1U, result.scts.length());
+ EXPECT_EQ(SignedCertificateTimestamp::VerificationStatus::OK,
+ result.scts[0].verificationStatus);
+ EXPECT_EQ(origin, result.scts[0].origin);
+ }
+
+ // Writes an SCTList containing a single |sct| into |output|.
+ void EncodeSCTListForTesting(Input sct, Buffer& output)
+ {
+ Vector<Input> list;
+ ASSERT_TRUE(list.append(Move(sct)));
+ ASSERT_EQ(Success, EncodeSCTList(list, output));
+ }
+
+ void GetSCTListWithInvalidLogID(Buffer& result)
+ {
+ result.clear();
+ Buffer sct(GetTestSignedCertificateTimestamp());
+ // Change a byte inside the Log ID part of the SCT so it does
+ // not match the log used in the tests.
+ sct[15] ^= '\xFF';
+ EncodeSCTListForTesting(InputForBuffer(sct), result);
+ }
+
+ void CheckPrecertVerification(const Buffer& cert, const Buffer& issuerSPKI)
+ {
+ Buffer sctList;
+ ExtractEmbeddedSCTList(cert, sctList);
+ ASSERT_FALSE(sctList.empty());
+
+ CTVerifyResult result;
+ ASSERT_EQ(Success,
+ mVerifier.Verify(InputForBuffer(cert), InputForBuffer(issuerSPKI),
+ InputForBuffer(sctList), Input(), Input(),
+ mNow, result));
+ CheckForSingleVerifiedSCTInResult(result,
+ SignedCertificateTimestamp::Origin::Embedded);
+ }
+
+protected:
+ MultiLogCTVerifier mVerifier;
+ Buffer mTestCert;
+ Buffer mEmbeddedCert;
+ Buffer mCaCert;
+ Buffer mCaCertSPKI;
+ Buffer mIntermediateCert;
+ Buffer mIntermediateCertSPKI;
+ Time mNow;
+};
+
+// Test that an embedded SCT can be extracted and the extracted SCT contains
+// the expected data. This tests the ExtractEmbeddedSCTList function from
+// CTTestUtils.h that other tests here rely upon.
+TEST_F(MultiLogCTVerifierTest, ExtractEmbeddedSCT)
+{
+ SignedCertificateTimestamp sct;
+
+ // Extract the embedded SCT.
+
+ Buffer sctList;
+ ExtractEmbeddedSCTList(mEmbeddedCert, sctList);
+ ASSERT_FALSE(sctList.empty());
+
+ Reader sctReader;
+ ASSERT_EQ(Success, DecodeSCTList(InputForBuffer(sctList), sctReader));
+ Input sctItemInput;
+ ASSERT_EQ(Success, ReadSCTListItem(sctReader, sctItemInput));
+ EXPECT_TRUE(sctReader.AtEnd()); // we only expect one sct in the list
+
+ Reader sctItemReader(sctItemInput);
+ ASSERT_EQ(Success, DecodeSignedCertificateTimestamp(sctItemReader, sct));
+
+ // Make sure the SCT contains the expected data.
+
+ EXPECT_EQ(SignedCertificateTimestamp::Version::V1, sct.version);
+ EXPECT_EQ(GetTestPublicKeyId(), sct.logId);
+
+ uint64_t expectedTimestamp = 1365181456275;
+ EXPECT_EQ(expectedTimestamp, sct.timestamp);
+}
+
+TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCT)
+{
+ CheckPrecertVerification(mEmbeddedCert, mCaCertSPKI);
+}
+
+TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithPreCA)
+{
+ CheckPrecertVerification(GetDEREncodedTestEmbeddedWithPreCACert(),
+ mCaCertSPKI);
+}
+
+TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithIntermediate)
+{
+ CheckPrecertVerification(GetDEREncodedTestEmbeddedWithIntermediateCert(),
+ mIntermediateCertSPKI);
+}
+
+TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithIntermediateAndPreCA)
+{
+ CheckPrecertVerification(GetDEREncodedTestEmbeddedWithIntermediatePreCACert(),
+ mIntermediateCertSPKI);
+}
+
+TEST_F(MultiLogCTVerifierTest, VerifiesSCTFromOCSP)
+{
+ Buffer sct(GetTestSignedCertificateTimestamp());
+ Buffer sctList;
+ EncodeSCTListForTesting(InputForBuffer(sct), sctList);
+
+ CTVerifyResult result;
+ ASSERT_EQ(Success,
+ mVerifier.Verify(InputForBuffer(mTestCert), Input(),
+ Input(), InputForBuffer(sctList), Input(),
+ mNow, result));
+
+ CheckForSingleVerifiedSCTInResult(result,
+ SignedCertificateTimestamp::Origin::OCSPResponse);
+}
+
+TEST_F(MultiLogCTVerifierTest, VerifiesSCTFromTLS)
+{
+ Buffer sct(GetTestSignedCertificateTimestamp());
+ Buffer sctList;
+ EncodeSCTListForTesting(InputForBuffer(sct), sctList);
+
+ CTVerifyResult result;
+ ASSERT_EQ(Success,
+ mVerifier.Verify(InputForBuffer(mTestCert), Input(),
+ Input(), Input(), InputForBuffer(sctList),
+ mNow, result));
+
+ CheckForSingleVerifiedSCTInResult(result,
+ SignedCertificateTimestamp::Origin::TLSExtension);
+}
+
+TEST_F(MultiLogCTVerifierTest, VerifiesSCTFromMultipleSources)
+{
+ Buffer sct(GetTestSignedCertificateTimestamp());
+ Buffer sctList;
+ EncodeSCTListForTesting(InputForBuffer(sct), sctList);
+
+ CTVerifyResult result;
+ ASSERT_EQ(Success,
+ mVerifier.Verify(InputForBuffer(mTestCert), Input(), Input(),
+ InputForBuffer(sctList), InputForBuffer(sctList),
+ mNow, result));
+
+ // The result should contain verified SCTs from TLS and OCSP origins.
+ EnumSet<SignedCertificateTimestamp::Origin> origins;
+ for (const SignedCertificateTimestamp& sct : result.scts) {
+ EXPECT_EQ(SignedCertificateTimestamp::VerificationStatus::OK,
+ sct.verificationStatus);
+ origins += sct.origin;
+ }
+ EXPECT_FALSE(
+ origins.contains(SignedCertificateTimestamp::Origin::Embedded));
+ EXPECT_TRUE(
+ origins.contains(SignedCertificateTimestamp::Origin::OCSPResponse));
+ EXPECT_TRUE(
+ origins.contains(SignedCertificateTimestamp::Origin::TLSExtension));
+}
+
+TEST_F(MultiLogCTVerifierTest, IdentifiesSCTFromUnknownLog)
+{
+ Buffer sctList;
+ GetSCTListWithInvalidLogID(sctList);
+
+ CTVerifyResult result;
+ ASSERT_EQ(Success,
+ mVerifier.Verify(InputForBuffer(mTestCert), Input(),
+ Input(), Input(), InputForBuffer(sctList),
+ mNow, result));
+
+ EXPECT_EQ(0U, result.decodingErrors);
+ ASSERT_EQ(1U, result.scts.length());
+ EXPECT_EQ(SignedCertificateTimestamp::VerificationStatus::UnknownLog,
+ result.scts[0].verificationStatus);
+}
+
+} } // namespace mozilla::ct
diff --git a/security/certverifier/tests/gtest/moz.build b/security/certverifier/tests/gtest/moz.build
new file mode 100644
index 000000000..c701e7eec
--- /dev/null
+++ b/security/certverifier/tests/gtest/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'CTLogVerifierTest.cpp',
+ 'CTObjectsExtractorTest.cpp',
+ 'CTSerializationTest.cpp',
+ 'CTTestUtils.cpp',
+ 'MultiLogCTVerifierTest.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '/security/certverifier',
+ '/security/pkix/include',
+ '/security/pkix/lib',
+]
+
+FINAL_LIBRARY = 'xul-gtest'