diff options
Diffstat (limited to 'security/certverifier')
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, ¬Before, ¬After) + != 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' |