summaryrefslogtreecommitdiffstats
path: root/security/manager/tools
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /security/manager/tools
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/manager/tools')
-rw-r--r--security/manager/tools/.eslintrc.js13
-rw-r--r--security/manager/tools/KnownRootHashes.json1108
-rw-r--r--security/manager/tools/PreloadedHPKPins.json217
-rw-r--r--security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py159
-rw-r--r--security/manager/tools/crtshToIdentifyingStruct/requirements.txt4
-rw-r--r--security/manager/tools/dumpGoogleRoots.js104
-rw-r--r--security/manager/tools/genHPKPStaticPins.js674
-rw-r--r--security/manager/tools/genRootCAHashes.js269
-rwxr-xr-xsecurity/manager/tools/getCTKnownLogs.py334
-rw-r--r--security/manager/tools/log_list.json128
10 files changed, 3010 insertions, 0 deletions
diff --git a/security/manager/tools/.eslintrc.js b/security/manager/tools/.eslintrc.js
new file mode 100644
index 0000000000..44c1d9cba6
--- /dev/null
+++ b/security/manager/tools/.eslintrc.js
@@ -0,0 +1,13 @@
+/* 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/. */
+
+"use strict";
+
+module.exports = {
+ globals: {
+ // JS files in this folder are commonly xpcshell scripts where |arguments|
+ // is defined in the global scope.
+ arguments: false,
+ },
+};
diff --git a/security/manager/tools/KnownRootHashes.json b/security/manager/tools/KnownRootHashes.json
new file mode 100644
index 0000000000..5397aedb08
--- /dev/null
+++ b/security/manager/tools/KnownRootHashes.json
@@ -0,0 +1,1108 @@
+// 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 is an automatically generated file. It's used to maintain state for
+// runs of genRootCAHashes.js; you should never need to manually edit it
+//***************************************************************************
+
+// Notes:
+// binNumber 1 used to be for "GTE_CyberTrust_Global_Root", but that root was
+// removed from the built-in roots module, so now it is used to indicate that
+// the certificate is not a built-in and was found in the softoken (cert9.db).
+
+// binNumber 2 used to be for "Thawte_Server_CA", but that root was removed from
+// the built-in roots module, so now it is used to indicate that the certificate
+// is not a built-in and was found on an external PKCS#11 token.
+
+// binNumber 3 used to be for "Thawte_Premium_Server_CA", but that root was
+// removed from the built-in roots module, so now it is used to indicate that
+// the certificate is not a built-in and was temporarily imported from the OS as
+// part of the "Enterprise Roots" feature.
+
+{
+ "roots": [
+ {
+ "label": "OU_Equifax_Secure_Certificate_Authority_O_Equifax_C_US",
+ "binNumber": 4,
+ "sha256Fingerprint": "CCl6QEfbojaAxzHbbjF2U8p4SOG+vToLAXmnB/ks8Xg="
+ },
+ {
+ "label": "OU_VeriSign_Trust_Network_OU___c__1998_VeriSign__Inc____For_authorized_use_only__OU_Class_3_Public_Primary_Certification_Authority___G2_O__VeriSign__Inc___C_US",
+ "binNumber": 5,
+ "sha256Fingerprint": "g848Eiloilk9SF+BlzwPkZVDHto3zF42Qw55x6iIY4s="
+ },
+ {
+ "label": "GlobalSign_Root_CA",
+ "binNumber": 6,
+ "sha256Fingerprint": "69QQQOS7PsdCyeOB0x7ypBpItmhclufO88HfbNQzHJk="
+ },
+ {
+ "label": "GlobalSign",
+ "binNumber": 7,
+ "sha256Fingerprint": "ykLdQXRf0LgeuQI2LPnYv3Gdob0bHvyUb1tMmfQsG54="
+ },
+ {
+ "label": "VeriSign_Class_3_Public_Primary_Certification_Authority___G3",
+ "binNumber": 8,
+ "sha256Fingerprint": "6wTPXrHzmvp2LyuxIPKWy6Ugwbl9sViVZbgcuaF7ckQ="
+ },
+ {
+ "label": "VeriSign_Class_4_Public_Primary_Certification_Authority___G3",
+ "binNumber": 9,
+ "sha256Fingerprint": "44k2DQ/brrPSUFhLRzAxTiIvOcFWoCAUTo2WBWF5FQY="
+ },
+ {
+ "label": "Entrust_net_Certification_Authority__2048_",
+ "binNumber": 10,
+ "sha256Fingerprint": "bcRxcuAcvLC/YlgNiV/iuKya1PhzgB4MELnIN9IesXc="
+ },
+ {
+ "label": "Baltimore_CyberTrust_Root",
+ "binNumber": 11,
+ "sha256Fingerprint": "Fq9XqfZ2sKsSYJWqXrre8iqzERnWRKyVzUuT2/Pyaus="
+ },
+ {
+ "label": "Equifax_Secure_Global_eBusiness_CA_1",
+ "binNumber": 12,
+ "sha256Fingerprint": "Xwti6rXjU+plIWUWWPu2U1n0QygKSvvRBNd9EPnwTAc="
+ },
+ {
+ "label": "Equifax_Secure_eBusiness_CA_1",
+ "binNumber": 13,
+ "sha256Fingerprint": "z1b/RqShhhCd2WWEte61ilEMQnWw5flPQLuuhl4Z9nM="
+ },
+ {
+ "label": "AddTrust_Class_1_CA_Root",
+ "binNumber": 14,
+ "sha256Fingerprint": "jHIJJ5rATideFtB/07d16AFUtZaARuMfUt0ldmMk6ac="
+ },
+ {
+ "label": "AddTrust_External_CA_Root",
+ "binNumber": 15,
+ "sha256Fingerprint": "aH+kUTgieP/wyLEfjUPVdmccbrK86rQT+4PZZdBtL/I="
+ },
+ {
+ "label": "AddTrust_Public_CA_Root",
+ "binNumber": 16,
+ "sha256Fingerprint": "B5HKB0myB4Kq08fXvQzfyUhYNYQ+steZYAnOQ6tsaSc="
+ },
+ {
+ "label": "AddTrust_Qualified_CA_Root",
+ "binNumber": 17,
+ "sha256Fingerprint": "gJUhCAXbS7w1XkQo2P1uws3jq1+5eplCmI649NzQYBY="
+ },
+ {
+ "label": "Entrust_Root_Certification_Authority",
+ "binNumber": 18,
+ "sha256Fingerprint": "c8F2Q08bxtWt9FsOducnKHyN5XYWwebmFBorLLx9jkw="
+ },
+ {
+ "label": "OU_RSA_Security_2048_V3_O_RSA_Security_Inc",
+ "binNumber": 19,
+ "sha256Fingerprint": "r4tnYqHlKCKBYaldXFWe4mYnj3XXnoMBiaUDUGq9a0w="
+ },
+ {
+ "label": "GeoTrust_Global_CA",
+ "binNumber": 20,
+ "sha256Fingerprint": "/4VqLSUdzYjTZlb0UBJnmM+rqt5AeZxyLeTStds2pzo="
+ },
+ {
+ "label": "GeoTrust_Global_CA_2",
+ "binNumber": 21,
+ "sha256Fingerprint": "yi2CoIZ3By+KtnZP8DVnbP4+XjJeASFy3z+SCW23m4U="
+ },
+ {
+ "label": "GeoTrust_Universal_CA",
+ "binNumber": 22,
+ "sha256Fingerprint": "oEWbn2OyJVn1+l1MbbP59y/xk0IDNXjwc78dG0bLuRI="
+ },
+ {
+ "label": "GeoTrust_Universal_CA_2",
+ "binNumber": 23,
+ "sha256Fingerprint": "oCNPO8hSfKVijuyBrV1piV2laA3JHRy4R38z+Hi5Wws="
+ },
+ {
+ "label": "America_Online_Root_Certification_Authority_1",
+ "binNumber": 24,
+ "sha256Fingerprint": "d0BzEsY6FT1bwAtOUXWc39rCN9wqM7Z5RumOm/poCuM="
+ },
+ {
+ "label": "America_Online_Root_Certification_Authority_2",
+ "binNumber": 25,
+ "sha256Fingerprint": "fTtGWmAU5SbAr/zuISfSMRcnrYEcJoQtAGrzcwbMgL0="
+ },
+ {
+ "label": "Visa_eCommerce_Root",
+ "binNumber": 26,
+ "sha256Fingerprint": "afrJvVX7CseNU7vuXPHVl5if0KqrIKJRUb3xcz7n0SI="
+ },
+ {
+ "label": "Certum_CA",
+ "binNumber": 27,
+ "sha256Fingerprint": "2OD+vB2y440AlA830n1BNE2ZPnNLmdVlbZd41NgUNiQ="
+ },
+ {
+ "label": "AAA_Certificate_Services",
+ "binNumber": 28,
+ "sha256Fingerprint": "16eg+11+JzHXcelITrze9x1fDD4KKUh4K8g+4OppnvQ="
+ },
+ {
+ "label": "Secure_Certificate_Services",
+ "binNumber": 29,
+ "sha256Fingerprint": "vYHOO09lkdEaZ7X8ekf97yVSG/mqThi5498uNKeAO+g="
+ },
+ {
+ "label": "Trusted_Certificate_Services",
+ "binNumber": 30,
+ "sha256Fingerprint": "PwblVoHUlvW+Fp61OJ+fK4/2HhcI32iBckhJzV0ny2k="
+ },
+ {
+ "label": "QuoVadis_Root_Certification_Authority",
+ "binNumber": 31,
+ "sha256Fingerprint": "pF7eO7vwnIrhXHLvwHJo1pOiHJlv1R5nygeUYP1tiHM="
+ },
+ {
+ "label": "QuoVadis_Root_CA_2",
+ "binNumber": 32,
+ "sha256Fingerprint": "haDdfdcgrbf/Bfg9VCsgncf/RSj31nexg4n+peXEnoY="
+ },
+ {
+ "label": "QuoVadis_Root_CA_3",
+ "binNumber": 33,
+ "sha256Fingerprint": "GPH8fyBd+K3d63/gB91X4683WpxNjXNUa/Tx/tHhjTU="
+ },
+ {
+ "label": "OU_Security_Communication_RootCA1_O_SECOM_Trust_net_C_JP",
+ "binNumber": 34,
+ "sha256Fingerprint": "515y7Z9WDuxutIAAc6Q/w60ZGVo5IoIBeJWXSpkCa2w="
+ },
+ {
+ "label": "Sonera_Class2_CA",
+ "binNumber": 35,
+ "sha256Fingerprint": "eQi0AxTBOBALUY0HNYB/+/z4UYoAlTNxBbo4axU92Sc="
+ },
+ {
+ "label": "Staat_der_Nederlanden_Root_CA",
+ "binNumber": 36,
+ "sha256Fingerprint": "1B2CnowWWYIq+T/OYr/83iZPyE6LlQxf8nXQUjVGlaM="
+ },
+ {
+ "label": "UTN___DATACorp_SGC",
+ "binNumber": 37,
+ "sha256Fingerprint": "hfsvkd0SJ1oBRbY2U0+EAkrWi2m47ohoT/cRN1gFs0g="
+ },
+ {
+ "label": "UTN_USERFirst_Hardware",
+ "binNumber": 38,
+ "sha256Fingerprint": "bqVHQdAEZn7tG0gWY0qjp55uS5aVD4J52vyNm9iBITc="
+ },
+ {
+ "label": "Chambers_of_Commerce_Root",
+ "binNumber": 39,
+ "sha256Fingerprint": "DCWKEqVnSu8l8oun3Prs7qNI5UHm9cxO5jtxs2FgasM="
+ },
+ {
+ "label": "Global_Chambersign_Root",
+ "binNumber": 40,
+ "sha256Fingerprint": "7zy0F/yOv2+Xh2yeTs453h6l/mSRQdECi30RwLIpjO0="
+ },
+ {
+ "label": "NetLock_Kozjegyzoi__Class_A__Tanusitvanykiado",
+ "binNumber": 41,
+ "sha256Fingerprint": "fxLNX35eKQ7H2FF51bcsIKW+dQj/21v4GrloSn/J9mc="
+ },
+ {
+ "label": "XRamp_Global_Certification_Authority",
+ "binNumber": 42,
+ "sha256Fingerprint": "zs3ckFCZ2NrfxbHSCbc3y+LBjPssEMD/C88NMob8GqI="
+ },
+ {
+ "label": "OU_Go_Daddy_Class_2_Certification_Authority_O__The_Go_Daddy_Group__Inc___C_US",
+ "binNumber": 43,
+ "sha256Fingerprint": "w4Rr8kuek8pkJ0wOxnwezF4CT/ys0tdAGTUOgf5UauQ="
+ },
+ {
+ "label": "OU_Starfield_Class_2_Certification_Authority_O__Starfield_Technologies__Inc___C_US",
+ "binNumber": 44,
+ "sha256Fingerprint": "FGX6IFOXuHb6pvCplY5VkOQPzH+qT7fCyGd1Iftftlg="
+ },
+ {
+ "label": "StartCom_Certification_Authority",
+ "binNumber": 45,
+ "sha256Fingerprint": "x2apvvLUBxyGOjGqSSDoE7LRmGCMt7fP4hFDuDbfCeo="
+ },
+ {
+ "label": "O_Government_Root_Certification_Authority_C_TW",
+ "binNumber": 46,
+ "sha256Fingerprint": "dgApXu/oW54f1iTbdgYqqq5ZgYpU0ndM1MCywBEx4bM="
+ },
+ {
+ "label": "Swisscom_Root_CA_1",
+ "binNumber": 47,
+ "sha256Fingerprint": "IdsgEjZguy7UGCBdoR7nqFpl4rxuVbWvfniZyKJm2S4="
+ },
+ {
+ "label": "DigiCert_Assured_ID_Root_CA",
+ "binNumber": 48,
+ "sha256Fingerprint": "PpCZtQFej0hsALzqnREe5yH6ujVaibzx32lWHj3GMlw="
+ },
+ {
+ "label": "DigiCert_Global_Root_CA",
+ "binNumber": 49,
+ "sha256Fingerprint": "Q0ig6URMeMsmXgWNXolEtNhPlmK9Jtslf4k0pEPHAWE="
+ },
+ {
+ "label": "DigiCert_High_Assurance_EV_Root_CA",
+ "binNumber": 50,
+ "sha256Fingerprint": "dDHl9MPBzkaQd08LYeBUQIg7qaAe0Aumq9eAbtOxGM8="
+ },
+ {
+ "label": "Class_2_Primary_CA",
+ "binNumber": 51,
+ "sha256Fingerprint": "D5k8iu+Xuq9WhxQO1ZrRghu0r6zwqppYtdV6M4o6+8s="
+ },
+ {
+ "label": "DST_Root_CA_X3",
+ "binNumber": 52,
+ "sha256Fingerprint": "BocmAzGnJAPZCfEF5pvPDTLhvSST/8bZIG0RvNZ3Bzk="
+ },
+ {
+ "label": "DST_ACES_CA_X6",
+ "binNumber": 53,
+ "sha256Fingerprint": "dnyVWnZBLImvaI6QoccPVWz9a2Al2+oQQW1+toMfjEA="
+ },
+ {
+ "label": "T_RKTRUST_Elektronik_Sertifika_Hizmet_Sa_lay_c_s_",
+ "binNumber": 54,
+ "sha256Fingerprint": "RATjO14UDc+ZgFH9/IAox8gWFcXuc3sRG1iCM6m1NaA="
+ },
+ {
+ "label": "T_RKTRUST_Elektronik_Sertifika_Hizmet_Sa_lay_c_s_",
+ "binNumber": 55,
+ "sha256Fingerprint": "xHDPVH4jArl3+yndcaiae2wfYHd7Ayn1YBfzKL9Pa+Y="
+ },
+ {
+ "label": "SwissSign_Gold_CA___G2",
+ "binNumber": 56,
+ "sha256Fingerprint": "Yt0L6bn1ChY+oPjnXAU7HspX6lXIaI9kfGiB8sg1e5U="
+ },
+ {
+ "label": "SwissSign_Silver_CA___G2",
+ "binNumber": 57,
+ "sha256Fingerprint": "vmxNoru5ulm285OXaDdCRsPABZk/qY8CDR3tvtSKgdU="
+ },
+ {
+ "label": "GeoTrust_Primary_Certification_Authority",
+ "binNumber": 58,
+ "sha256Fingerprint": "N9UQBsUS6qtiZCHx7IySAT/F+CrpjuUz60YZuN600Gw="
+ },
+ {
+ "label": "thawte_Primary_Root_CA",
+ "binNumber": 59,
+ "sha256Fingerprint": "jXIvganBE8B5HfE2opZtsmyVCpcdtGtBmfTqVLeL+58="
+ },
+ {
+ "label": "VeriSign_Class_3_Public_Primary_Certification_Authority___G5",
+ "binNumber": 60,
+ "sha256Fingerprint": "ms+rfkPI2IDQayYqlN7u5LRlmYnD0Mrxm69kBeQat98="
+ },
+ {
+ "label": "SecureTrust_CA",
+ "binNumber": 61,
+ "sha256Fingerprint": "8cG1CuWiDdgDDsn2vCSCPdNntSVXWbTnG2H86fc3XXM="
+ },
+ {
+ "label": "Secure_Global_CA",
+ "binNumber": 62,
+ "sha256Fingerprint": "QgD1BDrIWQ67Un0gntFQMCn7y9QcobUG7CfxWt59rGk="
+ },
+ {
+ "label": "COMODO_Certification_Authority",
+ "binNumber": 63,
+ "sha256Fingerprint": "DCzWPfeAb6OZ7egJEWtXW/h5ifBlGPmAjIYFAxeLr2Y="
+ },
+ {
+ "label": "Network_Solutions_Certificate_Authority",
+ "binNumber": 64,
+ "sha256Fingerprint": "FfC6AKOsevOsiEwHKxARoHe9d8CX9AFksvhZir2Dhgw="
+ },
+ {
+ "label": "WellsSecure_Public_Root_Certificate_Authority",
+ "binNumber": 65,
+ "sha256Fingerprint": "pxJyrqqjz+hyf3+znw+z0eVCbpBgsG7m8T6aPFgzzUM="
+ },
+ {
+ "label": "COMODO_ECC_Certification_Authority",
+ "binNumber": 66,
+ "sha256Fingerprint": "F5OSegYUVJeJrc4vjzT38LZtDzrjo7hNIewV27pPrcc="
+ },
+ {
+ "label": "IGC_A",
+ "binNumber": 67,
+ "sha256Fingerprint": "ub6nhgqWLqNhHauXq22j4hwQaLl9VVde0OESecEciTI="
+ },
+ {
+ "label": "OU_Security_Communication_EV_RootCA1_O__SECOM_Trust_Systems_CO__LTD___C_JP",
+ "binNumber": 68,
+ "sha256Fingerprint": "oi26aB6XN24tOX1yiq46m2KWuf26YLwuEfZH8sZ1+zc="
+ },
+ {
+ "label": "OISTE_WISeKey_Global_Root_GA_CA",
+ "binNumber": 69,
+ "sha256Fingerprint": "Qckjhmq0yta3rVeAgVguAgeXpsvfT/94zoOWs4k31/U="
+ },
+ {
+ "label": "Microsec_e_Szigno_Root_CA",
+ "binNumber": 70,
+ "sha256Fingerprint": "Mno9dhq63qA065mEBidcsaR3bv2uL99tAWjqHE9VZ9A="
+ },
+ {
+ "label": "Certigna",
+ "binNumber": 71,
+ "sha256Fingerprint": "47ai2y7XzkiEL3rFMkHHtx1UFEv7QMEfPx0LQvXuoS0="
+ },
+ {
+ "label": "TC_TrustCenter_Class_2_CA_II",
+ "binNumber": 72,
+ "sha256Fingerprint": "5rj4dmSF+Aeuf42sFnBGHwfAoT7vOh/3F1ONerrTkbQ="
+ },
+ {
+ "label": "TC_TrustCenter_Class_3_CA_II",
+ "binNumber": 73,
+ "sha256Fingerprint": "jaCE/Pmc4Hci+JsyBZOYBvpcuBHhyBP2oQjH0zazQI4="
+ },
+ {
+ "label": "TC_TrustCenter_Universal_CA_I",
+ "binNumber": 74,
+ "sha256Fingerprint": "6/PAKoeJsft9URmV1mO3KQbZE84NXhBWiop34lhhZ+c="
+ },
+ {
+ "label": "Deutsche_Telekom_Root_CA_2",
+ "binNumber": 75,
+ "sha256Fingerprint": "thkaUNDDl399qZvNqshqIn2uuWeexwujsMnZInHBcNM="
+ },
+ {
+ "label": "ComSign_Secured_CA",
+ "binNumber": 76,
+ "sha256Fingerprint": "UHlBx0RgoLRwhiINTpkyVyq10bW7y4mAqxyxdlGoRNI="
+ },
+ {
+ "label": "Cybertrust_Global_Root",
+ "binNumber": 77,
+ "sha256Fingerprint": "lgrfAGPpY1Z1DCll3QoIZ9oLnL1ud3FK6vsjSas5PaM="
+ },
+ {
+ "label": "OU_ePKI_Root_Certification_Authority_O__Chunghwa_Telecom_Co___Ltd___C_TW",
+ "binNumber": 78,
+ "sha256Fingerprint": "wKb03GOiS/3PVO8qaggqCnLeNYA+L/X/Unrl2HIG39U="
+ },
+ {
+ "label": "T_B_TAK_UEKAE_K_k_Sertifika_Hizmet_Sa_lay_c_s____S_r_m_3",
+ "binNumber": 79,
+ "sha256Fingerprint": "5Mc0MNeltQkl30M3Cg0hbpp5udbbg3Ogxp6xzDHHxSo="
+ },
+ {
+ "label": "Buypass_Class_2_CA_1",
+ "binNumber": 80,
+ "sha256Fingerprint": "D06c3SZLAlVQ0XCAY0AhT+lENMmwL2l+xxD8X+r7Xjg="
+ },
+ {
+ "label": "Buypass_Class_3_CA_1",
+ "binNumber": 81,
+ "sha256Fingerprint": "t7ErFx+CHaqZDND+UIexKESLqOUYT4TFHgK1yPuWKyQ="
+ },
+ {
+ "label": "EBG_Elektronik_Sertifika_Hizmet_Sa_lay_c_s_",
+ "binNumber": 82,
+ "sha256Fingerprint": "Na5b3dj3rmNc/7pWgqjwC5X0hGLHEI7poOUpKwdKr7I="
+ },
+ {
+ "label": "OU_certSIGN_ROOT_CA_O_certSIGN_C_RO",
+ "binNumber": 83,
+ "sha256Fingerprint": "6qlixPpKa6/r5BUZbTUczYiNT1Pz+orm18RmqU5gQrs="
+ },
+ {
+ "label": "CNNIC_ROOT",
+ "binNumber": 84,
+ "sha256Fingerprint": "4oOTdz2oRaZ58ggMx/tEo7ehw3kst+t3Kf3Lao2Zrqc="
+ },
+ {
+ "label": "OU_ApplicationCA_O_Japanese_Government_C_JP",
+ "binNumber": 85,
+ "sha256Fingerprint": "LUdDfeF5USFaEvPFjlHHKaWAJu8fzApfs9ncAS9gDRk="
+ },
+ {
+ "label": "GeoTrust_Primary_Certification_Authority___G3",
+ "binNumber": 86,
+ "sha256Fingerprint": "tHi4EiUN+HhjXCqn7H0VXqpiXugpFuLNKUNhiGzR+9Q="
+ },
+ {
+ "label": "thawte_Primary_Root_CA___G2",
+ "binNumber": 87,
+ "sha256Fingerprint": "pDENUK8YpkRxkDcqhq+vi5Uf+0Mdg38eVoi0WXHtFVc="
+ },
+ {
+ "label": "thawte_Primary_Root_CA___G3",
+ "binNumber": 88,
+ "sha256Fingerprint": "SwP0WAetcPIb/Cyuccn95GBMBkz1/7aGuuXbqtf900w="
+ },
+ {
+ "label": "GeoTrust_Primary_Certification_Authority___G2",
+ "binNumber": 89,
+ "sha256Fingerprint": "Xtt6xDuCoGqHYejXvkl56/JhH33Xm/kcHGtWaiGe12Y="
+ },
+ {
+ "label": "VeriSign_Universal_Root_Certification_Authority",
+ "binNumber": 90,
+ "sha256Fingerprint": "I5lWESelcSXejO/qYQ3fL6B4tcgGf06CgpC/uGDoSzw="
+ },
+ {
+ "label": "VeriSign_Class_3_Public_Primary_Certification_Authority___G4",
+ "binNumber": 91,
+ "sha256Fingerprint": "ad3X6pC7V8k+E13IXqb81UgLYDI5vcRU/HWLKibPf3k="
+ },
+ {
+ "label": "NetLock_Arany__Class_Gold__F_tan_s_tv_ny",
+ "binNumber": 92,
+ "sha256Fingerprint": "bGHaw6Le8DFQa+A20qb+QBmU+9E9+cjUZlmSdMRG7Jg="
+ },
+ {
+ "label": "Staat_der_Nederlanden_Root_CA___G2",
+ "binNumber": 93,
+ "sha256Fingerprint": "ZoyDlH2mO3JL7OF0PDGg5q7Q247Fsxvjd7t4T5G2cW8="
+ },
+ {
+ "label": "CA_Disig",
+ "binNumber": 94,
+ "sha256Fingerprint": "kr9RGavsytCxMy3E4dBfunW1Z5BE7gyibpMfdE8vM88="
+ },
+ {
+ "label": "Juur_SK",
+ "binNumber": 95,
+ "sha256Fingerprint": "7MPpw0B1A77gkaqVL0E0j/iLqoY7ImS++sgHkBV06Tk="
+ },
+ {
+ "label": "Hongkong_Post_Root_CA_1",
+ "binNumber": 96,
+ "sha256Fingerprint": "+eZ9M2xRACrAVMYyAi1m3aLn4//xCtBh7THYu7QQz7I="
+ },
+ {
+ "label": "SecureSign_RootCA11",
+ "binNumber": 97,
+ "sha256Fingerprint": "vw/u+546WBrV+enbdYmYV0PSYQhcTTFPb11yWapCFhI="
+ },
+ {
+ "label": "ACEDICOM_Root",
+ "binNumber": 98,
+ "sha256Fingerprint": "A5UPtJpTHz4ZkZQjmN+p4Ooy17oc3ZvIXbV+2UALQ0o="
+ },
+ {
+ "label": "Microsec_e_Szigno_Root_CA_2009",
+ "binNumber": 99,
+ "sha256Fingerprint": "PF+B/qX6uCxkv6Lq7K/N6OB3/IYgp8rlNxY9827b83g="
+ },
+ {
+ "label": "e_Guven_Kok_Elektronik_Sertifika_Hizmet_Saglayicisi",
+ "binNumber": 100,
+ "sha256Fingerprint": "5gkHhGWkGXgMtqxMHAv7RlPZ2cxus5Rut/PWmZe61Zg="
+ },
+ {
+ "label": "GlobalSign",
+ "binNumber": 101,
+ "sha256Fingerprint": "y7Ui17fxJ61qAROGW98c1BAufQdZr2NafPRyDcljxTs="
+ },
+ {
+ "label": "Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068",
+ "binNumber": 102,
+ "sha256Fingerprint": "BASAKL8fKGTUj5rU2DKUNmqCiFZVPzsUMD+QFH9dQO8="
+ },
+ {
+ "label": "Izenpe_com",
+ "binNumber": 103,
+ "sha256Fingerprint": "JTDMjpgyFQK62W+bH7obCZ4tKZ4PRUi7kU82O8DUUx8="
+ },
+ {
+ "label": "Chambers_of_Commerce_Root___2008",
+ "binNumber": 104,
+ "sha256Fingerprint": "Bj5K+sSR39My8wibhULpRhfYk9f+lE4Qp5N+4p2Wk8A="
+ },
+ {
+ "label": "Global_Chambersign_Root___2008",
+ "binNumber": 105,
+ "sha256Fingerprint": "E2M1Q5M0p2mAFqDTJN5yKE4HnXtSILuPvXR4Fu6+uso="
+ },
+ {
+ "label": "Go_Daddy_Root_Certificate_Authority___G2",
+ "binNumber": 106,
+ "sha256Fingerprint": "RRQLMkfrnMjFtPDXtTCR9zKSCJ5uWmPidJ3TrKkZjto="
+ },
+ {
+ "label": "Starfield_Root_Certificate_Authority___G2",
+ "binNumber": 107,
+ "sha256Fingerprint": "LOHLC/nS+eECmT++IVFSw7LdDKveHGjlMZuDkVTbt/U="
+ },
+ {
+ "label": "Starfield_Services_Root_Certificate_Authority___G2",
+ "binNumber": 108,
+ "sha256Fingerprint": "Vo1pBaLIhwikswJRkO3P7bGXSmBqE8blKQ/LKuY+2rU="
+ },
+ {
+ "label": "AffirmTrust_Commercial",
+ "binNumber": 109,
+ "sha256Fingerprint": "A3arHVTF+YA85LLiAaDufu97V7Y26Kk8m41IYMlvX6c="
+ },
+ {
+ "label": "AffirmTrust_Networking",
+ "binNumber": 110,
+ "sha256Fingerprint": "CoHsWpKXd/FFkErzjV1Qn2a14sWPzbUxBYsOF/PwtBs="
+ },
+ {
+ "label": "AffirmTrust_Premium",
+ "binNumber": 111,
+ "sha256Fingerprint": "cKc/fzdrYAdCSJBFNLEUgtW/DmmOzEmN9SV36/LpO5o="
+ },
+ {
+ "label": "AffirmTrust_Premium_ECC",
+ "binNumber": 112,
+ "sha256Fingerprint": "vXH99tqX5M9i0WR63SWBsH15rfg5frTsupxehIiCFCM="
+ },
+ {
+ "label": "Certum_Trusted_Network_CA",
+ "binNumber": 113,
+ "sha256Fingerprint": "XFhGjVX1jkl+dDmC0rUAELbRZTdKz4On1KMtt2jEQI4="
+ },
+ {
+ "label": "Certinomis___Autorit__Racine",
+ "binNumber": 114,
+ "sha256Fingerprint": "/L/iiGIG9ysnWTyLBwKX4S12ntEO15MHBagJjv/BTRc="
+ },
+ {
+ "label": "Root_CA_Generalitat_Valenciana",
+ "binNumber": 115,
+ "sha256Fingerprint": "jE7f0ENI8yKWnn4ppM1NygBGVQYcFuGwdkIu80KtYw4="
+ },
+ {
+ "label": "A_Trust_nQual_03",
+ "binNumber": 116,
+ "sha256Fingerprint": "eTy/RVm5/eOKsi3xaGn2mIGuFMSwE5rHiKeKGvzKAvs="
+ },
+ {
+ "label": "TWCA_Root_Certification_Authority",
+ "binNumber": 117,
+ "sha256Fingerprint": "v9iP4RAcQa4+gBv4vlY1Dum60aa5vVFe3FxtW4cRrEQ="
+ },
+ {
+ "label": "OU_Security_Communication_RootCA2_O__SECOM_Trust_Systems_CO__LTD___C_JP",
+ "binNumber": 118,
+ "sha256Fingerprint": "UTss7LgQ1M3l3YU5Gt/Gwt1g2Hu3NtK1IUhKpHoOvvY="
+ },
+ {
+ "label": "EC_ACC",
+ "binNumber": 119,
+ "sha256Fingerprint": "iEl/AWAvMVQkauKMTVrvEPHYfrt2Ym9K4Lf5W6eWh5k="
+ },
+ {
+ "label": "Hellenic_Academic_and_Research_Institutions_RootCA_2011",
+ "binNumber": 120,
+ "sha256Fingerprint": "vBBPFaSL5wncpUKn4dS5328FRSfoAuqpLVlURCWK/nE="
+ },
+ {
+ "label": "Actalis_Authentication_Root_CA",
+ "binNumber": 121,
+ "sha256Fingerprint": "VZJghOyWOmS5biq+Ac4LqGpk+/68x6q1r8FVs3/XYGY="
+ },
+ {
+ "label": "OU_Trustis_FPS_Root_CA_O_Trustis_Limited_C_GB",
+ "binNumber": 122,
+ "sha256Fingerprint": "wbSCmaulII/pYwrOVcpooD7aWlGciAKg06Zzvo+OVX0="
+ },
+ {
+ "label": "StartCom_Certification_Authority",
+ "binNumber": 123,
+ "sha256Fingerprint": "4XiQ7gmj+/T0i5xBShfWN7elBkfpvHUjInJ/zBdCqRE="
+ },
+ {
+ "label": "StartCom_Certification_Authority_G2",
+ "binNumber": 124,
+ "sha256Fingerprint": "x7plZ96Tp5iuH6p5HnEtN4+uH5PEOX/qRBu3y+b9WZU="
+ },
+ {
+ "label": "Buypass_Class_2_Root_CA",
+ "binNumber": 125,
+ "sha256Fingerprint": "mhFAJRl8W7ldlOY9Vc1DeQhHtkayPN8RraSgDv8V+0g="
+ },
+ {
+ "label": "Buypass_Class_3_Root_CA",
+ "binNumber": 126,
+ "sha256Fingerprint": "7ffrvKJ6KjhNOHt9QBDGZuLttIQ+TCm0rh1bkzLmsk0="
+ },
+ {
+ "label": "T_TeleSec_GlobalRoot_Class_3",
+ "binNumber": 127,
+ "sha256Fingerprint": "/XPa0xxkT/G0O+8MzdqWcQuc2Ydeyn4xcHrz6W1SK70="
+ },
+ {
+ "label": "EE_Certification_Centre_Root_CA",
+ "binNumber": 128,
+ "sha256Fingerprint": "PoS6Q0KQhRbndXPAmS8JecoITkaFaB/xlcy6iiKbinY="
+ },
+ {
+ "label": "T_RKTRUST_Elektronik_Sertifika_Hizmet_Sa_lay_c_s_",
+ "binNumber": 129,
+ "sha256Fingerprint": "l4zZZvL6oHunqpUA2cAunXfyza2mrWunSvS5HGZZPFA="
+ },
+ {
+ "label": "D_TRUST_Root_Class_3_CA_2_2009",
+ "binNumber": 130,
+ "sha256Fingerprint": "SeekQqzw6mKHBQBUtSVktlDk9J5C40jWqjjgOelXscE="
+ },
+ {
+ "label": "D_TRUST_Root_Class_3_CA_2_EV_2009",
+ "binNumber": 131,
+ "sha256Fingerprint": "7sVJa5iM6YYluTQJLuwpCL7QsPMWwtRzDITq8fPTSIE="
+ },
+ {
+ "label": "PSCProcert",
+ "binNumber": 132,
+ "sha256Fingerprint": "PPw8FNH2hP8X44xDykQMALln7JM+i/4GTKHXLJDyrbA="
+ },
+ {
+ "label": "China_Internet_Network_Information_Center_EV_Certificates_Root",
+ "binNumber": 133,
+ "sha256Fingerprint": "HAHG9Nuy/vwiVYsryjJWP0mESs/DK3vksP9Zn56Mevc="
+ },
+ {
+ "label": "Swisscom_Root_CA_2",
+ "binNumber": 134,
+ "sha256Fingerprint": "8JsSLHEU9KCb1OpPSpnVWLRuTCXNgRQNKcBWE5FMOEE="
+ },
+ {
+ "label": "Swisscom_Root_EV_CA_2",
+ "binNumber": 135,
+ "sha256Fingerprint": "2V/qPKTu3OdM1251/G0f9ixEHw+ovHfwNLGeXbJYAV0="
+ },
+ {
+ "label": "CA_Disig_Root_R1",
+ "binNumber": 136,
+ "sha256Fingerprint": "+W8j9MPnnAd6RpiNWvWQBnag8DnLZF3RdUmyFsgkQM4="
+ },
+ {
+ "label": "CA_Disig_Root_R2",
+ "binNumber": 137,
+ "sha256Fingerprint": "4j1KA217cOn1lbFCIHnSuR7fux+2UaBjPqqKncX4BwM="
+ },
+ {
+ "label": "ACCVRAIZ1",
+ "binNumber": 138,
+ "sha256Fingerprint": "mm7AEuGn2p2+NBlNR4rXwNsYIvsHHfEpgUlu0QQ4QRM="
+ },
+ {
+ "label": "TWCA_Global_Root_CA",
+ "binNumber": 139,
+ "sha256Fingerprint": "WXaQB/doXQ/NUIcvn5XVdVpbK0V9gfNpK2EKmGcvDhs="
+ },
+ {
+ "label": "TeliaSonera_Root_CA_v1",
+ "binNumber": 140,
+ "sha256Fingerprint": "3Wk2/iH48HfBI6GlIcEiJPciVbc+A6cmBpPooksPo4k="
+ },
+ {
+ "label": "E_Tugra_Certification_Authority",
+ "binNumber": 141,
+ "sha256Fingerprint": "sL/VK7DX2b2Sv11NwT2iVcAsVC83g2XqiTkR9V5V8jw="
+ },
+ {
+ "label": "T_TeleSec_GlobalRoot_Class_2",
+ "binNumber": 142,
+ "sha256Fingerprint": "keL1eI1YEOunulhzfeFUio7KzQFFmLwLFD4EGxcFJVI="
+ },
+ {
+ "label": "Atos_TrustedRoot_2011",
+ "binNumber": 143,
+ "sha256Fingerprint": "81a+okS3qR6zXVPKmteGSs4Bji011fj5bd9opvQapHQ="
+ },
+ {
+ "label": "QuoVadis_Root_CA_1_G3",
+ "binNumber": 144,
+ "sha256Fingerprint": "ioZv0bJ2tX5XjpIcZYKKK+1Y6fLyiAVBNLfx9L/JzHQ="
+ },
+ {
+ "label": "QuoVadis_Root_CA_2_G3",
+ "binNumber": 145,
+ "sha256Fingerprint": "j+T7Cvk6TQ1n2wvrsj43xxvzJdy83SQOoE2vWLR+GEA="
+ },
+ {
+ "label": "QuoVadis_Root_CA_3_G3",
+ "binNumber": 146,
+ "sha256Fingerprint": "iO+B3iAusBhFLkP4ZHJc6l+9H8LZ0gVzBwnF2LhpD0Y="
+ },
+ {
+ "label": "DigiCert_Assured_ID_Root_G2",
+ "binNumber": 147,
+ "sha256Fingerprint": "fQXrtoIzn4yUUe4JTuv++nlToRTtsvRJSUUvq30vwYU="
+ },
+ {
+ "label": "DigiCert_Assured_ID_Root_G3",
+ "binNumber": 148,
+ "sha256Fingerprint": "fjfLi0xHCQyrNlUbpvRduEBoD7oWapUtsQBxf0MFP8I="
+ },
+ {
+ "label": "DigiCert_Global_Root_G2",
+ "binNumber": 149,
+ "sha256Fingerprint": "yzzLt2Ax5eATj43TmiP53kf/w15DwRRM6ifUalqxy18="
+ },
+ {
+ "label": "DigiCert_Global_Root_G3",
+ "binNumber": 150,
+ "sha256Fingerprint": "Ma1mSPgQQTjHOPOepDIBMzk+OhjMAilu+Xwqye9nMdA="
+ },
+ {
+ "label": "DigiCert_Trusted_Root_G4",
+ "binNumber": 151,
+ "sha256Fingerprint": "VS973PGnr55s5nIBf08Sq/dyQMeOdhrCA9HZ0grImYg="
+ },
+ {
+ "label": "Certification_Authority_of_WoSign",
+ "binNumber": 152,
+ "sha256Fingerprint": "SyLVpq7JnzzbeapewGg4R5zV7LpxZPfyLcHWX2PYVwg="
+ },
+ {
+ "label": "CA______",
+ "binNumber": 153,
+ "sha256Fingerprint": "1vA0vZSqIz8Cl+ykJFsoOXPkR6pZDzEMd/SP34MRIlQ="
+ },
+ {
+ "label": "COMODO_RSA_Certification_Authority",
+ "binNumber": 154,
+ "sha256Fingerprint": "UvDhxOWOxikpG2AxfwdGcbhdfqgNWwcnNGNTSzK0AjQ="
+ },
+ {
+ "label": "USERTrust_RSA_Certification_Authority",
+ "binNumber": 155,
+ "sha256Fingerprint": "55PJsC/YqhPiHDEiisywgRlkO3SciYlksXRtRsPUy9I="
+ },
+ {
+ "label": "USERTrust_ECC_Certification_Authority",
+ "binNumber": 156,
+ "sha256Fingerprint": "T/Rg1Uuchtq/vPxXEuBADSvtP7xNT72qhuBq3NKprXo="
+ },
+ {
+ "label": "GlobalSign",
+ "binNumber": 157,
+ "sha256Fingerprint": "vslJEcKVVnbbbApVCYbXbjugBWZ8RCyXYrT7t3PeIow="
+ },
+ {
+ "label": "GlobalSign",
+ "binNumber": 158,
+ "sha256Fingerprint": "F5+8FIo90A/STqE0WMxDv6f1nIGC14OlE/br7BAMiSQ="
+ },
+ {
+ "label": "Staat_der_Nederlanden_Root_CA___G3",
+ "binNumber": 159,
+ "sha256Fingerprint": "PE+wuVq4swAy9DK4b1Nf4XLBhdD9OYZYN882GH+m9Cg="
+ },
+ {
+ "label": "Staat_der_Nederlanden_EV_Root_CA",
+ "binNumber": 160,
+ "sha256Fingerprint": "TSSRQUz+lWdG7Ezvps9vcuKKEylDL52KkHrEy12twVo="
+ },
+ {
+ "label": "IdenTrust_Commercial_Root_CA_1",
+ "binNumber": 161,
+ "sha256Fingerprint": "XVZJm+TS4IvPytCKPjhyPVBQO95waUjkL1VgMBnlKK4="
+ },
+ {
+ "label": "IdenTrust_Public_Sector_Root_CA_1",
+ "binNumber": 162,
+ "sha256Fingerprint": "MNCJWppEiiYgkWNVItH1IBC1hnrK4Sx475WP1PQ4ny8="
+ },
+ {
+ "label": "Entrust_Root_Certification_Authority___G2",
+ "binNumber": 163,
+ "sha256Fingerprint": "Q99XdLA+f+9f5A2TGnvt8bsua0JzjE5tOEEQPTqn8zk="
+ },
+ {
+ "label": "Entrust_Root_Certification_Authority___EC1",
+ "binNumber": 164,
+ "sha256Fingerprint": "Au0OsowU2kUWXFZnkXANZFHX+1bwsqsdO46wcOVu3/U="
+ },
+ {
+ "label": "CFCA_EV_ROOT",
+ "binNumber": 165,
+ "sha256Fingerprint": "XMPXjk4dXkVUegTmhz5k+Qz5U20czC74APNVxMX9cP0="
+ },
+ {
+ "label": "T_RKTRUST_Elektronik_Sertifika_Hizmet_Sa_lay_c_s__H5",
+ "binNumber": 166,
+ "sha256Fingerprint": "STUbkDREwYXM3FxpPSTYVVyyCNaoFBMHaZ9K8GMZnXg="
+ },
+ {
+ "label": "T_RKTRUST_Elektronik_Sertifika_Hizmet_Sa_lay_c_s__H6",
+ "binNumber": 167,
+ "sha256Fingerprint": "jeeGVeG+f3hHgAuT9pTSHTaMwG4DPn+rBLteuZ2mtwA="
+ },
+ {
+ "label": "Certinomis___Root_CA",
+ "binNumber": 168,
+ "sha256Fingerprint": "Kpn1vBF0tzy7HWIIhOAcNOUcyzl42hJfDjMmiIO/QVg="
+ },
+ {
+ "label": "OISTE_WISeKey_Global_Root_GB_CA",
+ "binNumber": 169,
+ "sha256Fingerprint": "a5wI6G6w92fPrWXNmLYhSeVJSmf1hF570e0Bnye4a9Y="
+ },
+ {
+ "label": "Certification_Authority_of_WoSign_G2",
+ "binNumber": 170,
+ "sha256Fingerprint": "1Ielb4OwdILoXpYzlMHswsnlHQkD7pRrAsMBWB7ZnhY="
+ },
+ {
+ "label": "CA_WoSign_ECC_Root",
+ "binNumber": 171,
+ "sha256Fingerprint": "i0XaHAb3kesMq/Jr5Yj1+yMWXC5hS/iFVi0NzlCymwI="
+ },
+ {
+ "label": "SZAFIR_ROOT_CA2",
+ "binNumber": 172,
+ "sha256Fingerprint": "oTOdMygaC1blV9PTKxzn+TZ+sJS9X6cqflAEyN7Xyv4="
+ },
+ {
+ "label": "Certum_Trusted_Network_CA_2",
+ "binNumber": 173,
+ "sha256Fingerprint": "tnby7drod1zTbLD2PNHUYDlh9J5iZboBOi8DB7bQuAQ="
+ },
+ {
+ "label": "Hellenic_Academic_and_Research_Institutions_RootCA_2015",
+ "binNumber": 174,
+ "sha256Fingerprint": "oECSmgLOU7Ss9PL/xpgc5ElvdV5tRf4LKmkrzVJSPzY="
+ },
+ {
+ "label": "Hellenic_Academic_and_Research_Institutions_ECC_RootCA_2015",
+ "binNumber": 175,
+ "sha256Fingerprint": "RLVFqool5lpzyhXcJ/w20kwcuZU6BmU5sRWC3Eh7SDM="
+ },
+ {
+ "label": "Certplus_Root_CA_G1",
+ "binNumber": 176,
+ "sha256Fingerprint": "FSpAK/zfLNVIBU0idbOcf8o+wJeAeLDw6nblYabHQz4="
+ },
+ {
+ "label": "Certplus_Root_CA_G2",
+ "binNumber": 177,
+ "sha256Fingerprint": "bMBQQeZEXnRpbEz7yfgPVDt+q7tEtM5veHxqmXHELxc="
+ },
+ {
+ "label": "OpenTrust_Root_CA_G1",
+ "binNumber": 178,
+ "sha256Fingerprint": "VsdxKNmMGNkbTP3/vCXukQPUdY6iq62CapDzRX1GDrQ="
+ },
+ {
+ "label": "OpenTrust_Root_CA_G2",
+ "binNumber": 179,
+ "sha256Fingerprint": "J5lYKf5qdRXBv+hI+cR2HbFsIlkpJXv0DQiU8p6ouvI="
+ },
+ {
+ "label": "OpenTrust_Root_CA_G3",
+ "binNumber": 180,
+ "sha256Fingerprint": "t8NiMXBugQeMNny4lhmPHjII3ZJpSd2PVwmkEPdbYpI="
+ },
+ {
+ "label": "ISRG_Root_X1",
+ "binNumber": 181,
+ "sha256Fingerprint": "lrzsBiZJdvN0YHeazyjFp8/oo8Cq4RqP/O4FwL3fCMY="
+ },
+ {
+ "label": "OU_AC_RAIZ_FNMT_RCM_O_FNMT_RCM_C_ES",
+ "binNumber": 182,
+ "sha256Fingerprint": "68VXDCkBjE1nsaoSe68S9wO0YR68F7fatVc4lBebk/o="
+ },
+ {
+ "label": "Amazon_Root_CA_1",
+ "binNumber": 183,
+ "sha256Fingerprint": "js3miE89h7ESW6Maw/yxPXAW3n9XzJBP4cuXxq6YGW4="
+ },
+ {
+ "label": "Amazon_Root_CA_2",
+ "binNumber": 184,
+ "sha256Fingerprint": "G6WyqoxlQBqClgEY+AvsT2IwTYPOxHE6GcOcAR6kbbQ="
+ },
+ {
+ "label": "Amazon_Root_CA_3",
+ "binNumber": 185,
+ "sha256Fingerprint": "GM5s/nvxTmCy40e43+hoyzHQLrs62icVafUDQ7Rts6Q="
+ },
+ {
+ "label": "Amazon_Root_CA_4",
+ "binNumber": 186,
+ "sha256Fingerprint": "410oQZ7QICXPppA4zWI5YkWNpcaV+96jwisL+yWJcJI="
+ },
+ {
+ "label": "LuxTrust_Global_Root_2",
+ "binNumber": 187,
+ "sha256Fingerprint": "VEVfcSnCCxRHxBj5lxaPJMWPxQI79dpb4utuHdiQLtU="
+ },
+ {
+ "label": "TUBITAK_Kamu_SM_SSL_Kok_Sertifikasi___Surum_1",
+ "binNumber": 188,
+ "sha256Fingerprint": "Ru3DaJBG1TpFP7MQSrgNyuxliyZg6hYp3X6GeZBkhxY="
+ },
+ {
+ "label": "GDCA_TrustAUTH_R5_ROOT",
+ "binNumber": 189,
+ "sha256Fingerprint": "v/+P0EQzSH1qiqYMGil2ep/Cu7BeQg9xOhO5kokdOJM="
+ },
+ {
+ "label": "TrustCor_RootCert_CA_1",
+ "binNumber": 190,
+ "sha256Fingerprint": "1A6chs2P5GjBd2lZ9J6ndPpUhoS2xAbzkJJh9NziV1w="
+ },
+ {
+ "label": "TrustCor_RootCert_CA_2",
+ "binNumber": 191,
+ "sha256Fingerprint": "B1PpQDeMG9Xjg245Xa6ly4OeUEbxvQ6uGVHPEP7HyWU="
+ },
+ {
+ "label": "TrustCor_ECA_1",
+ "binNumber": 192,
+ "sha256Fingerprint": "WohdsZwB2RLFdZOIk4yvu98DGrLUjpHuFVibQpcdA5w="
+ },
+ {
+ "label": "SSL_com_Root_Certification_Authority_RSA",
+ "binNumber": 193,
+ "sha256Fingerprint": "hWZqVi7gvlzpJcHYiQpvdqh+wW1NfV8p6nQZzyASO2k="
+ },
+ {
+ "label": "SSL_com_Root_Certification_Authority_ECC",
+ "binNumber": 194,
+ "sha256Fingerprint": "NBe7BsxgB9oblhySC4q0zj+tgg5Kowuay8SnTr3OvGU="
+ },
+ {
+ "label": "SSL_com_EV_Root_Certification_Authority_RSA_R2",
+ "binNumber": 195,
+ "sha256Fingerprint": "LnvxbMIkhae74qqGlnUHYbCuOb47L+nQzG1O9zSRQlw="
+ },
+ {
+ "label": "SSL_com_EV_Root_Certification_Authority_ECC",
+ "binNumber": 196,
+ "sha256Fingerprint": "IqLB973tcEzB5wG19AjDEIgP6Va13ipKRPmchzolp8g="
+ },
+ {
+ "label": "GlobalSign",
+ "binNumber": 197,
+ "sha256Fingerprint": "LKvq/jfQbKIqunORwAM9JZgpUsRTZHNJdjo6ta1sz2k="
+ },
+ {
+ "label": "OISTE_WISeKey_Global_Root_GC_CA",
+ "binNumber": 198,
+ "sha256Fingerprint": "hWD5HDYk2rqVcLX+oNvjb/EagyO+lIaFT7PzSlVxGY0="
+ },
+ {
+ "label": "GTS_Root_R1",
+ "binNumber": 199,
+ "sha256Fingerprint": "KldUceMTQLwhWBy9LPE+FYRjID7OlLz508wZa/CaVHI="
+ },
+ {
+ "label": "GTS_Root_R2",
+ "binNumber": 200,
+ "sha256Fingerprint": "xF17sI5tZ+YuQjURC1ZOX3j9ku8FjIQK6k5kVddYXGA="
+ },
+ {
+ "label": "GTS_Root_R3",
+ "binNumber": 201,
+ "sha256Fingerprint": "FdW4d0YZ6n1Uzhym0LDEA+A3qRfxMeigTh5renG6vOU="
+ },
+ {
+ "label": "GTS_Root_R4",
+ "binNumber": 202,
+ "sha256Fingerprint": "ccylOR+eeUsEgCUws2PhIdqKMEO7JmYv6k3Kf8lRpL0="
+ },
+ {
+ "label": "UCA_Global_G2_Root",
+ "binNumber": 203,
+ "sha256Fingerprint": "m+oRyXb+AUdkwb5WpvkUtaVgMXq9mYg5M4LlFhqgSTw="
+ },
+ {
+ "label": "UCA_Extended_Validation_Root",
+ "binNumber": 204,
+ "sha256Fingerprint": "1Dr5s1RzdVyWhPwG19jLcO5cKOdz+ylOtB7nFyKSTSQ="
+ },
+ {
+ "label": "Certigna_Root_CA",
+ "binNumber": 205,
+ "sha256Fingerprint": "1I09I+7bUKRZ5VGXYBwnd0udexjJTVoFlRGhAlC5MWg="
+ },
+ {
+ "label": "emSign_Root_CA___G1",
+ "binNumber": 206,
+ "sha256Fingerprint": "QPavA0apmqHNHVVaTpzOYsf5Y0YD7kBmFYM9yMjQA2c="
+ },
+ {
+ "label": "emSign_ECC_Root_CA___G3",
+ "binNumber": 207,
+ "sha256Fingerprint": "hqHsugicSo07vic0xhK6NB2BPgQ8+eioYs1cV6Nrvms="
+ },
+ {
+ "label": "emSign_Root_CA___C1",
+ "binNumber": 208,
+ "sha256Fingerprint": "ElYJqjAdoKJJuXqCOctqNCFvRNysnzlUsUKS8ujIYI8="
+ },
+ {
+ "label": "emSign_ECC_Root_CA___C3",
+ "binNumber": 209,
+ "sha256Fingerprint": "vE2AmxUYnXjbPh2M9PlyanldoWQ8pfE1jh3bDtwNfrM="
+ },
+ {
+ "label": "Hongkong_Post_Root_CA_3",
+ "binNumber": 210,
+ "sha256Fingerprint": "Wi/APwyDsJC7+kBgSwmIRGx2Nhg9+YRuFxAaRH+479Y="
+ },
+ {
+ "label": "Entrust_Root_Certification_Authority___G4",
+ "binNumber": 211,
+ "sha256Fingerprint": "2zUX0fZzKi1auXxTPscHee4ycKYvtKxCODckYObwHog="
+ },
+ {
+ "label": "Microsoft_ECC_Root_Certificate_Authority_2017",
+ "binNumber": 212,
+ "sha256Fingerprint": "NY3znXZK+eG3ZunJct81LuFc+sInr2rR1w6OSm7cugI="
+ },
+ {
+ "label": "Microsoft_RSA_Root_Certificate_Authority_2017",
+ "binNumber": 213,
+ "sha256Fingerprint": "x0H3D0sqjYi/LnHBQSLvU+8Q66DPpeZM+iD0GIUwc+A="
+ },
+ {
+ "label": "e_Szigno_Root_CA_2017",
+ "binNumber": 214,
+ "sha256Fingerprint": "vrALMIObm8MsMuREeQWVBkHyZCGxXtCJGYtRiuLqG5k="
+ },
+ {
+ "label": "OU_certSIGN_ROOT_CA_G2_O_CERTSIGN_SA_C_RO",
+ "binNumber": 215,
+ "sha256Fingerprint": "ZXz+L6c/qjhGJXHzMqI2Okb85wIJUXEHAs37tu7aMwU="
+ },
+ {
+ "label": "Trustwave_Global_Certification_Authority",
+ "binNumber": 216,
+ "sha256Fingerprint": "l1UgFfXd/DyHiMAGlEVVQIiURQCE8QCGcIa8Giu1jcg="
+ },
+ {
+ "label": "Trustwave_Global_ECC_P256_Certification_Authority",
+ "binNumber": 217,
+ "sha256Fingerprint": "lFu8gl6lVPSJ0f1Rpz3fLqYkrHAZoFIFIlwip4zPqLQ="
+ },
+ {
+ "label": "Trustwave_Global_ECC_P384_Certification_Authority",
+ "binNumber": 218,
+ "sha256Fingerprint": "VZA4WcjAw+u4dZ7OTiVXIl/1dYu9OOvUgnZgHhvVgJc="
+ },
+ {
+ "label": "NAVER_Global_Root_Certification_Authority",
+ "binNumber": 219,
+ "sha256Fingerprint": "iPQ43Pj/0fqPQpEV/+X4KuHgbgxww3X6rXF7NKSecmU="
+ }
+ ],
+ "maxBin": 219
+} \ No newline at end of file
diff --git a/security/manager/tools/PreloadedHPKPins.json b/security/manager/tools/PreloadedHPKPins.json
new file mode 100644
index 0000000000..4afc14373b
--- /dev/null
+++ b/security/manager/tools/PreloadedHPKPins.json
@@ -0,0 +1,217 @@
+// -*- Mode: javascript; 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/.
+
+// The top-level element is a dictionary with two keys: "pinsets" maps details
+// of certificate pinning to a name and "entries" contains the HPKP details for
+// each host.
+//
+// "pinsets" is a list of objects. Each object has the following members:
+// name: (string) the name of the pinset
+// sha256_hashes: (list of strings) the set of allowed SPKIs hashes
+//
+// For a given pinset, a certificate is accepted if at least one of the
+// Subject Public Key Infos (SPKIs) is found in the chain. SPKIs are specified
+// as names, which must match up with the name given in the Mozilla root store.
+//
+// "entries" is a list of objects. Each object has the following members:
+// name: (string) the DNS name of the host in question
+// include_subdomains: (optional bool) whether subdomains of |name| are also covered
+// pins: (string) the |name| member of an object in |pinsets|
+//
+// "extra_certificates" is a list of base64-encoded certificates. These are used in
+// pinsets that reference certificates not in our root program (for example,
+// Facebook or intermediate CA certs).
+
+{
+ "chromium_data" : {
+ "cert_file_url": "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.pins?format=TEXT",
+ "json_file_url": "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.json?format=TEXT",
+ "substitute_pinsets": {
+ // Use the larger google_root_pems pinset instead of google
+ "google": "google_root_pems"
+ },
+ "production_pinsets": [
+ "google_root_pems",
+ "facebook",
+ "ncsccs"
+ ],
+ "production_domains": [
+ // Chrome's test domains.
+ "pinningtest.appspot.com",
+ "pinning-test.badssl.com",
+ // Dropbox
+ "dropbox.com",
+ "www.dropbox.com",
+ // Twitter
+ "api.twitter.com",
+ "business.twitter.com",
+ "dev.twitter.com",
+ "mobile.twitter.com",
+ "oauth.twitter.com",
+ "platform.twitter.com",
+ "twimg.com",
+ "www.twitter.com",
+ // Tor
+ "torproject.org",
+ "blog.torproject.org",
+ "check.torproject.org",
+ "dist.torproject.org",
+ "www.torproject.org",
+ // SpiderOak
+ "spideroak.com"
+ ],
+ "exclude_domains" : [
+ // Chrome's entry for twitter.com doesn't include subdomains, so replace
+ // it with our own entry below which also uses an expanded pinset.
+ "twitter.com"
+ ]
+ },
+ "pinsets": [
+ {
+ "name": "mozilla_services",
+ "sha256_hashes": [
+ "DigiCert Global Root CA",
+ "DigiCert High Assurance EV Root CA",
+ "ISRG Root X1"
+ ]
+ },
+ // For pinning tests on pinning.example.com, the certificate must be 'End
+ // Entity Test Cert'
+ {
+ "name": "mozilla_test",
+ "sha256_hashes": [
+ "End Entity Test Cert"
+ ]
+ },
+ // Google's root PEMs. Chrome pins only to their intermediate certs, but
+ // they'd like us to be more liberal. For the initial list, we are using
+ // the certs from https://pki.google.com/roots.pem.
+ // We have no built-in for commented out CAs.
+ // This list should be updated via the dumpGoogleRoots.js script.
+ {
+ "name": "google_root_pems",
+ "sha256_hashes": [
+ "AffirmTrust Commercial",
+ "AffirmTrust Networking",
+ "AffirmTrust Premium",
+ "AffirmTrust Premium ECC",
+ "Baltimore CyberTrust Root",
+ "Comodo AAA Services root",
+ "COMODO Certification Authority",
+ "COMODO ECC Certification Authority",
+ "COMODO RSA Certification Authority",
+ "Cybertrust Global Root",
+ "DigiCert Assured ID Root CA",
+ "DigiCert Assured ID Root G2",
+ "DigiCert Assured ID Root G3",
+ "DigiCert Global Root CA",
+ "DigiCert Global Root G2",
+ "DigiCert Global Root G3",
+ "DigiCert High Assurance EV Root CA",
+ "DigiCert Trusted Root G4",
+ "Entrust Root Certification Authority",
+ "Entrust Root Certification Authority - EC1",
+ "Entrust Root Certification Authority - G2",
+ "Entrust.net Premium 2048 Secure Server CA",
+ // "GeoTrust Global CA",
+ "GlobalSign ECC Root CA - R4",
+ "GlobalSign ECC Root CA - R5",
+ "GlobalSign Root CA",
+ "GlobalSign Root CA - R2",
+ "GlobalSign Root CA - R3",
+ "GlobalSign Root CA - R6",
+ "Go Daddy Class 2 CA",
+ "Go Daddy Root Certificate Authority - G2",
+ "GTS Root R1",
+ "GTS Root R2",
+ "GTS Root R3",
+ "GTS Root R4",
+ "Starfield Class 2 CA",
+ "Starfield Root Certificate Authority - G2",
+ "USERTrust ECC Certification Authority",
+ "USERTrust RSA Certification Authority"
+ ]
+ }
+ // The list above should be updated via the dumpGoogleRoots.js script.
+ ],
+
+ "entries": [
+ // Only domains that are operationally crucial to Firefox can have per-host
+ // telemetry reporting (the "id") field
+ { "name": "addons.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 1 },
+ { "name": "addons.mozilla.net", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 2 },
+ // AUS servers MUST remain in test mode
+ // see: https://bugzilla.mozilla.org/show_bug.cgi?id=1301956#c23
+ { "name": "aus4.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": true, "id": 3 },
+ { "name": "aus5.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": true, "id": 7 },
+ // Catchall for applications hosted under firefox.com
+ // see https://bugzilla.mozilla.org/show_bug.cgi?id=1494431
+ { "name": "firefox.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": true, "id": 15 },
+ // Firefox Accounts & sync
+ // superseded by catchall for firefox.com, but leaving for tracking
+ { "name": "accounts.firefox.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 4 },
+ { "name": "api.accounts.firefox.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 5 },
+ { "name": "sync.services.mozilla.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 13 },
+ // Catch-all for all CDN resources, including product delivery
+ // Telemetry IDs added in bug 1521983.
+ { "name": "cdn.mozilla.net", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 16 },
+ { "name": "cdn.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 17 },
+ { "name": "download.mozilla.org", "include_subdomains": false,
+ "pins": "mozilla_services", "test_mode": false, "id": 14 },
+ // Catch-all for everything hosted under services.mozilla.com
+ { "name": "services.mozilla.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 6 },
+ // Catch-all for everything hosted under telemetry.mozilla.org
+ // MUST remain in test mode in order to receive telemetry on broken pins
+ { "name": "telemetry.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": true, "id": 8 },
+ // Test Pilot
+ // superseded by catchall for firefox.com, but leaving for tracking
+ { "name": "testpilot.firefox.com", "include_subdomains": false,
+ "pins": "mozilla_services", "test_mode": false, "id": 9 },
+ // Crash report sites
+ { "name": "crash-reports.mozilla.com", "include_subdomains": false,
+ "pins": "mozilla_services", "test_mode": false, "id": 10 },
+ { "name": "crash-reports-xpsp2.mozilla.com", "include_subdomains": false,
+ "pins": "mozilla_services", "test_mode": false, "id": 11 },
+ { "name": "crash-stats.mozilla.org", "include_subdomains": false,
+ "pins": "mozilla_services", "test_mode": false, "id": 12 },
+ { "name": "include-subdomains.pinning.example.com",
+ "include_subdomains": true, "pins": "mozilla_test",
+ "test_mode": false },
+ // Example domain to collect per-host stats for telemetry tests.
+ { "name": "exclude-subdomains.pinning.example.com",
+ "include_subdomains": false, "pins": "mozilla_test",
+ "test_mode": false },
+ { "name": "test-mode.pinning.example.com", "include_subdomains": true,
+ "pins": "mozilla_test", "test_mode": true },
+ // Expand twitter's pinset to include all of *.twitter.com and use
+ // twitterCDN. More specific rules take precedence because we search for
+ // exact domain name first.
+ { "name": "twitter.com", "include_subdomains": true,
+ "pins": "twitterCDN", "test_mode": false }
+ ],
+ // When pinning to non-root certs, like intermediates,
+ // place the PEM of the pinned certificate in this array
+ // so Firefox can find the subject DN and public key
+ "extra_certificates": [
+ // Subject: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3
+ // Issuer: C=US, O=Internet Security Research Group, CN=ISRG Root X1
+ "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrXNSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHlNpi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7DcGu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgzuEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMBAAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEFBQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsGAQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYDVR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIBABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGxA/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRMUM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOuOsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vwp7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKRPB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5brUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt",
+ // Subject: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X4
+ // Issuer: C=US, O=Internet Security Research Group, CN=ISRG Root X1
+ "MIIFjTCCA3WgAwIBAgIRAJObmZ6kjhYNW0JZtD0gE9owDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0NDM0WhcNMjExMDA2MTU0NDM0WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhJHRCe7eRMdlz/ziq2M5EXLc5CtxErg29RbmXN2evvVBPX9MQVGv3QdqOY+ZtW8DoQKmMQfzRA4n/YmEJYNYHBXiakL0aZD5P3M93L4lry2evQU3FjQDAa/6NhNy18pUxqOj2kKBDSpN0XLM+Q2lLiSJHdFE+mWTDzSQB+YQvKHcXIqfdw2wITGYvN3TFb5OOsEY3FmHRUJjIsA9PWFN8rPbaLZZhUK1D3AqmT561Urmcju9O30azMdwg/GnCoyB1Puw4GzZOZmbS3/VmpJMve6YOlD5gPUpLHG+6tE0cPJFYbi9NxNpw2+0BOXbASefpNbUUBpDB5ZLiEP1rubSFAgMBAAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBTFsatOTLHNZDCTfsGEmQWr5gPiJTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEFBQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsGAQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYDVR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIBAF4tI1yGjZgld9lP01+zftU3aSV0un0d2GKUMO7GxvwTLWAKQz/eT+u3J4+GvpD+BMfopIxkJcDCzMChjjZtZZwJpIY7BatVrO6OkEmaRNITtbZ/hCwNkUnbk3C7EG3OGJZlo9b2wzA8v9WBsPzHpTvLfOr+dS57LLPZBhp3ArHaLbdk33lIONRPt9sseDEkmdHnVmGmBRf4+J0Wy67mddOvz5rHH8uzY94raOayf20gzzcmqmot4hPXtDG4Y49MoFMMT2kcWck3EOTAH6QiGWkGJ7cxMfSL3S0niA6wgFJtfETETOZu8AVDgENgCJ3DS0bz/dhVKvs3WRkaKuuR/W0nnC2VDdaFj4+CRF8LGtn/8ERaH48TktH5BDyDVcF9zfJ75Scxcy23jAL2N6w3n/t3nnqoXt9Im4FprDr+mP1g2Z6Lf2YA0jE3kZalgZ6lNHu4CmvJYoOTSJw9X2qlGl1K+B4U327rG1tRxgjM76pN6lIS02PMECoyKJigpOSBu4V8+LVaUMezCJH9Qf4EKeZTHddQ1t96zvNd2s9ewSKx/DblXbKsBDzIdHJ+qi6+F9DIVM5/ICdtDdulOO+dr/BXB+pBZ3uVxjRANvJKKpdxkePyluITSNZHbanWRN07gMvwBWOL060i4VrL9er1sBQrRjU9iNpZQGTnLVAxQVFu"
+ ]
+}
diff --git a/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py b/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py
new file mode 100644
index 0000000000..ce8ae1467a
--- /dev/null
+++ b/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python3
+#
+# 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 utility takes a series of https://crt.sh/ identifiers and writes to
+stdout all of those certs' distinguished name or SPKI fields in hex, with an
+array of all those. You'll need to post-process this list to handle any
+duplicates.
+
+Requires Python 3.
+"""
+import argparse
+import re
+import requests
+import sys
+import io
+
+from pyasn1.codec.der import decoder
+from pyasn1.codec.der import encoder
+from pyasn1_modules import pem
+from pyasn1_modules import rfc5280
+
+from cryptography import x509
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes
+from cryptography.x509.oid import NameOID
+
+assert sys.version_info >= (3, 2), "Requires Python 3.2 or later"
+
+
+def hex_string_for_struct(bytes):
+ return ["0x{:02X}".format(x) for x in bytes]
+
+
+def hex_string_human_readable(bytes):
+ return ["{:02X}".format(x) for x in bytes]
+
+
+def nameOIDtoString(oid):
+ if oid == NameOID.COUNTRY_NAME:
+ return "C"
+ if oid == NameOID.COMMON_NAME:
+ return "CN"
+ if oid == NameOID.LOCALITY_NAME:
+ return "L"
+ if oid == NameOID.ORGANIZATION_NAME:
+ return "O"
+ if oid == NameOID.ORGANIZATIONAL_UNIT_NAME:
+ return "OU"
+ raise Exception("Unknown OID: {}".format(oid))
+
+
+def print_block(pemData, identifierType="DN", crtshId=None):
+ substrate = pem.readPemFromFile(io.StringIO(pemData.decode("utf-8")))
+ cert, rest = decoder.decode(substrate, asn1Spec=rfc5280.Certificate())
+ octets = None
+
+ if identifierType == "DN":
+ der_subject = encoder.encode(cert["tbsCertificate"]["subject"])
+ octets = hex_string_for_struct(der_subject)
+ elif identifierType == "SPKI":
+ der_spki = encoder.encode(cert["tbsCertificate"]["subjectPublicKeyInfo"])
+ octets = hex_string_for_struct(der_spki)
+ else:
+ raise Exception("Unknown identifier type: " + identifierType)
+
+ cert = x509.load_pem_x509_certificate(pemData, default_backend())
+ common_name = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0]
+ block_name = "CA{}{}".format(
+ re.sub(r"[-:=_. ]", "", common_name.value), identifierType
+ )
+
+ fingerprint = hex_string_human_readable(cert.fingerprint(hashes.SHA256()))
+
+ dn_parts = [
+ "/{id}={value}".format(id=nameOIDtoString(part.oid), value=part.value)
+ for part in cert.subject
+ ]
+ distinguished_name = "".join(dn_parts)
+
+ print("// {dn}".format(dn=distinguished_name))
+ print("// SHA256 Fingerprint: " + ":".join(fingerprint[:16]))
+ print("// " + ":".join(fingerprint[16:]))
+ if crtshId:
+ print("// https://crt.sh/?id={crtsh} (crt.sh ID={crtsh})".format(crtsh=crtshId))
+ print("static const uint8_t {}[{}] = ".format(block_name, len(octets)) + "{")
+
+ while len(octets) > 0:
+ print(" " + ", ".join(octets[:13]) + ",")
+ octets = octets[13:]
+
+ print("};")
+ print()
+
+ return block_name
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-spki",
+ action="store_true",
+ help="Create a list of subject public key info fields",
+ )
+ parser.add_argument(
+ "-dn",
+ action="store_true",
+ help="Create a list of subject distinguished name fields",
+ )
+ parser.add_argument("-listname", help="Name of the final DataAndLength block")
+ parser.add_argument(
+ "certId", nargs="+", help="A list of PEM files on disk or crt.sh IDs"
+ )
+ args = parser.parse_args()
+
+ if not args.dn and not args.spki:
+ parser.print_help()
+ raise Exception("You must select either DN or SPKI matching")
+
+ blocks = []
+
+ print(
+ "// Script from security/manager/tools/crtshToIdentifyingStruct/"
+ + "crtshToIdentifyingStruct.py"
+ )
+ print("// Invocation: {}".format(" ".join(sys.argv)))
+ print()
+
+ identifierType = None
+ if args.dn:
+ identifierType = "DN"
+ else:
+ identifierType = "SPKI"
+
+ for certId in args.certId:
+ # Try a local file first, then crt.sh
+ try:
+ with open(certId, "rb") as pemFile:
+ blocks.append(
+ print_block(pemFile.read(), identifierType=identifierType)
+ )
+ except OSError:
+ r = requests.get("https://crt.sh/?d={}".format(certId))
+ r.raise_for_status()
+ blocks.append(
+ print_block(r.content, crtshId=certId, identifierType=identifierType)
+ )
+
+ print("static const DataAndLength " + args.listname + "[]= {")
+ for structName in blocks:
+ if len(structName) < 33:
+ print(" { " + "{name}, sizeof({name}) ".format(name=structName) + "},")
+ else:
+ print(" { " + "{},".format(structName))
+ print(" sizeof({})".format(structName) + " },")
+ print("};")
diff --git a/security/manager/tools/crtshToIdentifyingStruct/requirements.txt b/security/manager/tools/crtshToIdentifyingStruct/requirements.txt
new file mode 100644
index 0000000000..c5c04ec1cc
--- /dev/null
+++ b/security/manager/tools/crtshToIdentifyingStruct/requirements.txt
@@ -0,0 +1,4 @@
+cryptography >= 1.8
+requests >= 2.0
+pyasn1 >= 0.3
+pyasn1_modules >= 0.1 \ No newline at end of file
diff --git a/security/manager/tools/dumpGoogleRoots.js b/security/manager/tools/dumpGoogleRoots.js
new file mode 100644
index 0000000000..6d9650a2ab
--- /dev/null
+++ b/security/manager/tools/dumpGoogleRoots.js
@@ -0,0 +1,104 @@
+/* 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/. */
+"use strict";
+
+// This file is a helper script that generates the list of certificates that
+// make up the preloaded pinset for Google properties.
+//
+// How to run this file:
+// 1. [obtain firefox source code]
+// 2. [build/obtain firefox binaries]
+// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell dumpGoogleRoots.js'
+// 4. [paste the output into the appropriate section in
+// security/manager/tools/PreloadedHPKPins.json]
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+Services.prefs.setBoolPref("network.process.enabled", false);
+
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
+
+function downloadRoots() {
+ let req = new XMLHttpRequest();
+ req.open("GET", "https://pki.google.com/roots.pem", false);
+ try {
+ req.send();
+ } catch (e) {
+ throw new Error("ERROR: problem downloading Google Root PEMs: " + e);
+ }
+
+ if (req.status != 200) {
+ throw new Error(
+ "ERROR: problem downloading Google Root PEMs. Status: " + req.status
+ );
+ }
+
+ let pem = req.responseText;
+ let roots = [];
+ let currentPEM = "";
+ let readingRoot = false;
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ for (let line of pem.split(/[\r\n]/)) {
+ if (line == "-----END CERTIFICATE-----") {
+ if (currentPEM) {
+ roots.push(certDB.constructX509FromBase64(currentPEM));
+ }
+ currentPEM = "";
+ readingRoot = false;
+ continue;
+ }
+ if (readingRoot) {
+ currentPEM += line;
+ }
+ if (line == "-----BEGIN CERTIFICATE-----") {
+ readingRoot = true;
+ }
+ }
+ return roots;
+}
+
+function makeFormattedNickname(cert) {
+ if (cert.isBuiltInRoot) {
+ return `"${cert.displayName}"`;
+ }
+ // Otherwise, this isn't a built-in and we have to comment it out.
+ return `// "${cert.displayName}"`;
+}
+
+var roots = downloadRoots();
+var rootNicknames = [];
+for (var root of roots) {
+ rootNicknames.push(makeFormattedNickname(root));
+}
+rootNicknames.sort(function(rootA, rootB) {
+ let rootALowercase = rootA.toLowerCase().replace(/(^[^"]*")|"/g, "");
+ let rootBLowercase = rootB.toLowerCase().replace(/(^[^"]*")|"/g, "");
+ if (rootALowercase < rootBLowercase) {
+ return -1;
+ }
+ if (rootALowercase > rootBLowercase) {
+ return 1;
+ }
+ return 0;
+});
+dump(" {\n");
+dump(' "name": "google_root_pems",\n');
+dump(' "sha256_hashes": [\n');
+var first = true;
+for (var nickname of rootNicknames) {
+ if (!first) {
+ dump(",\n");
+ }
+ first = false;
+ dump(" " + nickname);
+}
+dump("\n");
+dump(" ]\n");
+dump(" }\n");
diff --git a/security/manager/tools/genHPKPStaticPins.js b/security/manager/tools/genHPKPStaticPins.js
new file mode 100644
index 0000000000..aebacd6d3c
--- /dev/null
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -0,0 +1,674 @@
+/* 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/. */
+
+// How to run this file:
+// 1. [obtain firefox source code]
+// 2. [build/obtain firefox binaries]
+// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \
+// [path to]/genHPKPStaticpins.js \
+// [absolute path to]/PreloadedHPKPins.json \
+// [an unused argument - see bug 1205406] \
+// [absolute path to]/StaticHPKPins.h
+"use strict";
+
+if (arguments.length != 3) {
+ throw new Error(
+ "Usage: genHPKPStaticPins.js " +
+ "<absolute path to PreloadedHPKPins.json> " +
+ "<an unused argument - see bug 1205406> " +
+ "<absolute path to StaticHPKPins.h>"
+ );
+}
+
+var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
+
+var gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const SHA256_PREFIX = "sha256/";
+const GOOGLE_PIN_PREFIX = "GOOGLE_PIN_";
+
+// Pins expire in 14 weeks (6 weeks on Beta + 8 weeks on stable)
+const PINNING_MINIMUM_REQUIRED_MAX_AGE = 60 * 60 * 24 * 7 * 14;
+
+const FILE_HEADER =
+ "/* This Source Code Form is subject to the terms of the Mozilla Public\n" +
+ " * License, v. 2.0. If a copy of the MPL was not distributed with this\n" +
+ " * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" +
+ "\n" +
+ "/*****************************************************************************/\n" +
+ "/* This is an automatically generated file. If you're not */\n" +
+ "/* PublicKeyPinningService.cpp, you shouldn't be #including it. */\n" +
+ "/*****************************************************************************/\n" +
+ "#include <stdint.h>" +
+ "\n";
+
+const DOMAINHEADER =
+ "/* Domainlist */\n" +
+ "struct TransportSecurityPreload {\n" +
+ " // See bug 1338873 about making these fields const.\n" +
+ " const char* mHost;\n" +
+ " bool mIncludeSubdomains;\n" +
+ " bool mTestMode;\n" +
+ " bool mIsMoz;\n" +
+ " int32_t mId;\n" +
+ " const StaticFingerprints* pinset;\n" +
+ "};\n\n";
+
+const PINSETDEF =
+ "/* Pinsets are each an ordered list by the actual value of the fingerprint */\n" +
+ "struct StaticFingerprints {\n" +
+ " // See bug 1338873 about making these fields const.\n" +
+ " size_t size;\n" +
+ " const char* const* data;\n" +
+ "};\n\n";
+
+// Command-line arguments
+var gStaticPins = parseJson(arguments[0]);
+
+// arguments[1] is ignored for now. See bug 1205406.
+
+// Open the output file.
+var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+file.initWithPath(arguments[2]);
+var gFileOutputStream = FileUtils.openSafeFileOutputStream(file);
+
+function writeString(string) {
+ gFileOutputStream.write(string, string.length);
+}
+
+function readFileToString(filename) {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.initWithPath(filename);
+ let stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
+ Ci.nsIFileInputStream
+ );
+ stream.init(file, -1, 0, 0);
+ let buf = NetUtil.readInputStreamToString(stream, stream.available());
+ return buf;
+}
+
+function stripComments(buf) {
+ let lines = buf.split("\n");
+ let entryRegex = /^\s*\/\//;
+ let data = "";
+ for (let i = 0; i < lines.length; ++i) {
+ let match = entryRegex.exec(lines[i]);
+ if (!match) {
+ data = data + lines[i];
+ }
+ }
+ return data;
+}
+
+function download(filename) {
+ let req = new XMLHttpRequest();
+ req.open("GET", filename, false); // doing the request synchronously
+ try {
+ req.send();
+ } catch (e) {
+ throw new Error(`ERROR: problem downloading '${filename}': ${e}`);
+ }
+
+ if (req.status != 200) {
+ throw new Error(
+ "ERROR: problem downloading '" + filename + "': status " + req.status
+ );
+ }
+
+ let resultDecoded;
+ try {
+ resultDecoded = atob(req.responseText);
+ } catch (e) {
+ throw new Error(
+ "ERROR: could not decode data as base64 from '" + filename + "': " + e
+ );
+ }
+ return resultDecoded;
+}
+
+function downloadAsJson(filename) {
+ // we have to filter out '//' comments, while not mangling the json
+ let result = download(filename).replace(/^(\s*)?\/\/[^\n]*\n/gm, "");
+ let data = null;
+ try {
+ data = JSON.parse(result);
+ } catch (e) {
+ throw new Error(
+ "ERROR: could not parse data from '" + filename + "': " + e
+ );
+ }
+ return data;
+}
+
+// Returns a Subject Public Key Digest from the given pem, if it exists.
+function getSKDFromPem(pem) {
+ let cert = gCertDB.constructX509FromBase64(pem, pem.length);
+ return cert.sha256SubjectPublicKeyInfoDigest;
+}
+
+/**
+ * Hashes |input| using the SHA-256 algorithm in the following manner:
+ * btoa(sha256(atob(input)))
+ *
+ * @argument {String} input Base64 string to decode and return the hash of.
+ * @returns {String} Base64 encoded SHA-256 hash.
+ */
+function sha256Base64(input) {
+ let decodedValue;
+ try {
+ decodedValue = atob(input);
+ } catch (e) {
+ throw new Error(`ERROR: could not decode as base64: '${input}': ${e}`);
+ }
+
+ // Convert |decodedValue| to an array so that it can be hashed by the
+ // nsICryptoHash instance below.
+ // In most cases across the code base, convertToByteArray() of
+ // nsIScriptableUnicodeConverter is used to do this, but the method doesn't
+ // seem to work here.
+ let data = [];
+ for (let i = 0; i < decodedValue.length; i++) {
+ data[i] = decodedValue.charCodeAt(i);
+ }
+
+ let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
+ Ci.nsICryptoHash
+ );
+ hasher.init(hasher.SHA256);
+ hasher.update(data, data.length);
+
+ // true is passed so that the hasher returns a Base64 encoded string.
+ return hasher.finish(true);
+}
+
+// Downloads the static certs file and tries to map Google Chrome nicknames
+// to Mozilla nicknames, as well as storing any hashes for pins for which we
+// don't have root PEMs. Each entry consists of a line containing the name of
+// the pin followed either by a hash in the format "sha256/" + base64(hash),
+// a PEM encoded public key, or a PEM encoded certificate.
+// For certificates that we have in our database,
+// return a map of Google's nickname to ours. For ones that aren't return a
+// map of Google's nickname to SHA-256 values. This code is modeled after agl's
+// https://github.com/agl/transport-security-state-generate, which doesn't
+// live in the Chromium repo because go is not an official language in
+// Chromium.
+// For all of the entries in this file:
+// - If the entry has a hash format, find the Mozilla pin name (cert nickname)
+// and stick the hash into certSKDToName
+// - If the entry has a PEM format, parse the PEM, find the Mozilla pin name
+// and stick the hash in certSKDToName
+// We MUST be able to find a corresponding cert nickname for the Chrome names,
+// otherwise we skip all pinsets referring to that Chrome name.
+function downloadAndParseChromeCerts(filename, certNameToSKD, certSKDToName) {
+ // Prefixes that we care about.
+ const BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
+ const END_CERT = "-----END CERTIFICATE-----";
+ const BEGIN_PUB_KEY = "-----BEGIN PUBLIC KEY-----";
+ const END_PUB_KEY = "-----END PUBLIC KEY-----";
+
+ // Parsing states.
+ const PRE_NAME = 0;
+ const POST_NAME = 1;
+ const IN_CERT = 2;
+ const IN_PUB_KEY = 3;
+ let state = PRE_NAME;
+
+ let lines = download(filename).split("\n");
+ let pemCert = "";
+ let pemPubKey = "";
+ let hash = "";
+ let chromeNameToHash = {};
+ let chromeNameToMozName = {};
+ let chromeName;
+ for (let line of lines) {
+ // Skip comments and newlines.
+ if (line.length == 0 || line[0] == "#") {
+ continue;
+ }
+ switch (state) {
+ case PRE_NAME:
+ chromeName = line;
+ state = POST_NAME;
+ break;
+ case POST_NAME:
+ if (line.startsWith(SHA256_PREFIX)) {
+ hash = line.substring(SHA256_PREFIX.length);
+ chromeNameToHash[chromeName] = hash;
+ certNameToSKD[chromeName] = hash;
+ certSKDToName[hash] = chromeName;
+ state = PRE_NAME;
+ } else if (line.startsWith(BEGIN_CERT)) {
+ state = IN_CERT;
+ } else if (line.startsWith(BEGIN_PUB_KEY)) {
+ state = IN_PUB_KEY;
+ } else {
+ throw new Error(
+ "ERROR: couldn't parse Chrome certificate file line: " + line
+ );
+ }
+ break;
+ case IN_CERT:
+ if (line.startsWith(END_CERT)) {
+ state = PRE_NAME;
+ hash = getSKDFromPem(pemCert);
+ pemCert = "";
+ let mozName;
+ if (hash in certSKDToName) {
+ mozName = certSKDToName[hash];
+ } else {
+ // Not one of our built-in certs. Prefix the name with
+ // GOOGLE_PIN_.
+ mozName = GOOGLE_PIN_PREFIX + chromeName;
+ dump(
+ "Can't find hash in builtin certs for Chrome nickname " +
+ chromeName +
+ ", inserting " +
+ mozName +
+ "\n"
+ );
+ certSKDToName[hash] = mozName;
+ certNameToSKD[mozName] = hash;
+ }
+ chromeNameToMozName[chromeName] = mozName;
+ } else {
+ pemCert += line;
+ }
+ break;
+ case IN_PUB_KEY:
+ if (line.startsWith(END_PUB_KEY)) {
+ state = PRE_NAME;
+ hash = sha256Base64(pemPubKey);
+ pemPubKey = "";
+ chromeNameToHash[chromeName] = hash;
+ certNameToSKD[chromeName] = hash;
+ certSKDToName[hash] = chromeName;
+ } else {
+ pemPubKey += line;
+ }
+ break;
+ default:
+ throw new Error(
+ "ERROR: couldn't parse Chrome certificate file " + line
+ );
+ }
+ }
+ return [chromeNameToHash, chromeNameToMozName];
+}
+
+// We can only import pinsets from chrome if for every name in the pinset:
+// - We have a hash from Chrome's static certificate file
+// - We have a builtin cert
+// If the pinset meets these requirements, we store a map array of pinset
+// objects:
+// {
+// pinset_name : {
+// // Array of names with entries in certNameToSKD
+// sha256_hashes: []
+// }
+// }
+// and an array of imported pinset entries:
+// { name: string, include_subdomains: boolean, test_mode: boolean,
+// pins: pinset_name }
+function downloadAndParseChromePins(
+ filename,
+ chromeNameToHash,
+ chromeNameToMozName,
+ certNameToSKD,
+ certSKDToName
+) {
+ let chromePreloads = downloadAsJson(filename);
+ let chromePins = chromePreloads.pinsets;
+ let chromeImportedPinsets = {};
+ let chromeImportedEntries = [];
+
+ chromePins.forEach(function(pin) {
+ let valid = true;
+ let pinset = { name: pin.name, sha256_hashes: [] };
+ // Translate the Chrome pinset format to ours
+ pin.static_spki_hashes.forEach(function(name) {
+ if (name in chromeNameToHash) {
+ let hash = chromeNameToHash[name];
+ pinset.sha256_hashes.push(certSKDToName[hash]);
+
+ // We should have already added hashes for all of these when we
+ // imported the certificate file.
+ if (!certNameToSKD[name]) {
+ throw new Error("ERROR: No hash for name: " + name);
+ }
+ } else if (name in chromeNameToMozName) {
+ pinset.sha256_hashes.push(chromeNameToMozName[name]);
+ } else {
+ dump(
+ "Skipping Chrome pinset " +
+ pinset.name +
+ ", couldn't find " +
+ "builtin " +
+ name +
+ " from cert file\n"
+ );
+ valid = false;
+ }
+ });
+ if (valid) {
+ chromeImportedPinsets[pinset.name] = pinset;
+ }
+ });
+
+ // Grab the domain entry lists. Chrome's entry format is similar to
+ // ours, except theirs includes a HSTS mode.
+ const cData = gStaticPins.chromium_data;
+ let entries = chromePreloads.entries;
+ entries.forEach(function(entry) {
+ // HSTS entry only
+ if (!entry.pins) {
+ return;
+ }
+ let pinsetName = cData.substitute_pinsets[entry.pins];
+ if (!pinsetName) {
+ pinsetName = entry.pins;
+ }
+
+ // We trim the entry name here to avoid breaking hostname comparisons in the
+ // HPKP implementation.
+ entry.name = entry.name.trim();
+
+ let isProductionDomain = cData.production_domains.includes(entry.name);
+ let isProductionPinset = cData.production_pinsets.includes(pinsetName);
+ let excludeDomain = cData.exclude_domains.includes(entry.name);
+ let isTestMode = !isProductionPinset && !isProductionDomain;
+ if (entry.pins && !excludeDomain && chromeImportedPinsets[entry.pins]) {
+ chromeImportedEntries.push({
+ name: entry.name,
+ include_subdomains: entry.include_subdomains,
+ test_mode: isTestMode,
+ is_moz: false,
+ pins: pinsetName,
+ });
+ }
+ });
+ return [chromeImportedPinsets, chromeImportedEntries];
+}
+
+// Returns a pair of maps [certNameToSKD, certSKDToName] between cert
+// nicknames and digests of the SPKInfo for the mozilla trust store
+function loadNSSCertinfo(extraCertificates) {
+ let allCerts = gCertDB.getCerts();
+ let certNameToSKD = {};
+ let certSKDToName = {};
+ for (let cert of allCerts) {
+ if (!cert.isBuiltInRoot) {
+ continue;
+ }
+ let name = cert.displayName;
+ let SKD = cert.sha256SubjectPublicKeyInfoDigest;
+ certNameToSKD[name] = SKD;
+ certSKDToName[SKD] = name;
+ }
+
+ for (let cert of extraCertificates) {
+ let name = cert.commonName;
+ let SKD = cert.sha256SubjectPublicKeyInfoDigest;
+ certNameToSKD[name] = SKD;
+ certSKDToName[SKD] = name;
+ }
+
+ {
+ // This is the pinning test certificate. The key hash identifies the
+ // default RSA key from pykey.
+ let name = "End Entity Test Cert";
+ let SKD = "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
+ certNameToSKD[name] = SKD;
+ certSKDToName[SKD] = name;
+ }
+ return [certNameToSKD, certSKDToName];
+}
+
+function parseJson(filename) {
+ let json = stripComments(readFileToString(filename));
+ return JSON.parse(json);
+}
+
+function nameToAlias(certName) {
+ // change the name to a string valid as a c identifier
+ // remove non-ascii characters
+ certName = certName.replace(/[^[:ascii:]]/g, "_");
+ // replace non word characters
+ certName = certName.replace(/[^A-Za-z0-9]/g, "_");
+
+ return "k" + certName + "Fingerprint";
+}
+
+function compareByName(a, b) {
+ return a.name.localeCompare(b.name);
+}
+
+function genExpirationTime() {
+ let now = new Date();
+ let nowMillis = now.getTime();
+ let expirationMillis = nowMillis + PINNING_MINIMUM_REQUIRED_MAX_AGE * 1000;
+ let expirationMicros = expirationMillis * 1000;
+ return (
+ "static const PRTime kPreloadPKPinsExpirationTime = INT64_C(" +
+ expirationMicros +
+ ");\n"
+ );
+}
+
+function writeFullPinset(certNameToSKD, certSKDToName, pinset) {
+ if (!pinset.sha256_hashes || pinset.sha256_hashes.length == 0) {
+ throw new Error(`ERROR: Pinset ${pinset.name} does not contain any hashes`);
+ }
+ writeFingerprints(
+ certNameToSKD,
+ certSKDToName,
+ pinset.name,
+ pinset.sha256_hashes
+ );
+}
+
+function writeFingerprints(certNameToSKD, certSKDToName, name, hashes) {
+ let varPrefix = "kPinset_" + name;
+ writeString("static const char* const " + varPrefix + "_Data[] = {\n");
+ let SKDList = [];
+ for (let certName of hashes) {
+ if (!(certName in certNameToSKD)) {
+ throw new Error(`ERROR: Can't find '${certName}' in certNameToSKD`);
+ }
+ SKDList.push(certNameToSKD[certName]);
+ }
+ for (let skd of SKDList.sort()) {
+ writeString(" " + nameToAlias(certSKDToName[skd]) + ",\n");
+ }
+ if (hashes.length == 0) {
+ // ANSI C requires that an initialiser list be non-empty.
+ writeString(" 0\n");
+ }
+ writeString("};\n");
+ writeString(
+ "static const StaticFingerprints " +
+ varPrefix +
+ " = {\n " +
+ "sizeof(" +
+ varPrefix +
+ "_Data) / sizeof(const char*),\n " +
+ varPrefix +
+ "_Data\n};\n\n"
+ );
+}
+
+function writeEntry(entry) {
+ let printVal = ` { "${entry.name}", `;
+ if (entry.include_subdomains) {
+ printVal += "true, ";
+ } else {
+ printVal += "false, ";
+ }
+ // Default to test mode if not specified.
+ let testMode = true;
+ if (entry.hasOwnProperty("test_mode")) {
+ testMode = entry.test_mode;
+ }
+ if (testMode) {
+ printVal += "true, ";
+ } else {
+ printVal += "false, ";
+ }
+ if (
+ entry.is_moz ||
+ (entry.pins.includes("mozilla") && entry.pins != "mozilla_test")
+ ) {
+ printVal += "true, ";
+ } else {
+ printVal += "false, ";
+ }
+ if ("id" in entry) {
+ if (entry.id >= 256) {
+ throw new Error("ERROR: Not enough buckets in histogram");
+ }
+ if (entry.id >= 0) {
+ printVal += entry.id + ", ";
+ }
+ } else {
+ printVal += "-1, ";
+ }
+ printVal += "&kPinset_" + entry.pins;
+ printVal += " },\n";
+ writeString(printVal);
+}
+
+function writeDomainList(chromeImportedEntries) {
+ writeString("/* Sort hostnames for binary search. */\n");
+ writeString(
+ "static const TransportSecurityPreload " +
+ "kPublicKeyPinningPreloadList[] = {\n"
+ );
+ let count = 0;
+ let mozillaDomains = {};
+ gStaticPins.entries.forEach(function(entry) {
+ mozillaDomains[entry.name] = true;
+ });
+ // For any domain for which we have set pins, exclude them from
+ // chromeImportedEntries.
+ for (let i = chromeImportedEntries.length - 1; i >= 0; i--) {
+ if (mozillaDomains[chromeImportedEntries[i].name]) {
+ dump(
+ "Skipping duplicate pinset for domain " +
+ JSON.stringify(chromeImportedEntries[i], undefined, 2) +
+ "\n"
+ );
+ chromeImportedEntries.splice(i, 1);
+ }
+ }
+ let sortedEntries = gStaticPins.entries;
+ sortedEntries.push.apply(sortedEntries, chromeImportedEntries);
+ for (let entry of sortedEntries.sort(compareByName)) {
+ count++;
+ writeEntry(entry);
+ }
+ writeString("};\n");
+
+ writeString("\n// Pinning Preload List Length = " + count + ";\n");
+ writeString("\nstatic const int32_t kUnknownId = -1;\n");
+}
+
+function writeFile(
+ certNameToSKD,
+ certSKDToName,
+ chromeImportedPinsets,
+ chromeImportedEntries
+) {
+ // Compute used pins from both Chrome's and our pinsets, so we can output
+ // them later.
+ let usedFingerprints = {};
+ let mozillaPins = {};
+ gStaticPins.pinsets.forEach(function(pinset) {
+ mozillaPins[pinset.name] = true;
+ pinset.sha256_hashes.forEach(function(name) {
+ usedFingerprints[name] = true;
+ });
+ });
+ for (let key in chromeImportedPinsets) {
+ let pinset = chromeImportedPinsets[key];
+ pinset.sha256_hashes.forEach(function(name) {
+ usedFingerprints[name] = true;
+ });
+ }
+
+ writeString(FILE_HEADER);
+
+ // Write actual fingerprints.
+ Object.keys(usedFingerprints)
+ .sort()
+ .forEach(function(certName) {
+ if (certName) {
+ writeString("/* " + certName + " */\n");
+ writeString("static const char " + nameToAlias(certName) + "[] =\n");
+ writeString(' "' + certNameToSKD[certName] + '";\n');
+ writeString("\n");
+ }
+ });
+
+ // Write the pinsets
+ writeString(PINSETDEF);
+ writeString("/* PreloadedHPKPins.json pinsets */\n");
+ gStaticPins.pinsets.sort(compareByName).forEach(function(pinset) {
+ writeFullPinset(certNameToSKD, certSKDToName, pinset);
+ });
+ writeString("/* Chrome static pinsets */\n");
+ for (let key in chromeImportedPinsets) {
+ if (mozillaPins[key]) {
+ dump("Skipping duplicate pinset " + key + "\n");
+ } else {
+ dump("Writing pinset " + key + "\n");
+ writeFullPinset(certNameToSKD, certSKDToName, chromeImportedPinsets[key]);
+ }
+ }
+
+ // Write the domainlist entries.
+ writeString(DOMAINHEADER);
+ writeDomainList(chromeImportedEntries);
+ writeString("\n");
+ writeString(genExpirationTime());
+}
+
+function loadExtraCertificates(certStringList) {
+ let constructedCerts = [];
+ for (let certString of certStringList) {
+ constructedCerts.push(gCertDB.constructX509FromBase64(certString));
+ }
+ return constructedCerts;
+}
+
+var extraCertificates = loadExtraCertificates(gStaticPins.extra_certificates);
+var [certNameToSKD, certSKDToName] = loadNSSCertinfo(extraCertificates);
+var [chromeNameToHash, chromeNameToMozName] = downloadAndParseChromeCerts(
+ gStaticPins.chromium_data.cert_file_url,
+ certNameToSKD,
+ certSKDToName
+);
+var [chromeImportedPinsets, chromeImportedEntries] = downloadAndParseChromePins(
+ gStaticPins.chromium_data.json_file_url,
+ chromeNameToHash,
+ chromeNameToMozName,
+ certNameToSKD,
+ certSKDToName
+);
+
+writeFile(
+ certNameToSKD,
+ certSKDToName,
+ chromeImportedPinsets,
+ chromeImportedEntries
+);
+
+FileUtils.closeSafeFileOutputStream(gFileOutputStream);
diff --git a/security/manager/tools/genRootCAHashes.js b/security/manager/tools/genRootCAHashes.js
new file mode 100644
index 0000000000..9e80ca90aa
--- /dev/null
+++ b/security/manager/tools/genRootCAHashes.js
@@ -0,0 +1,269 @@
+/* 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/. */
+"use strict";
+
+// How to run this file:
+// 1. [obtain firefox source code]
+// 2. [build/obtain firefox binaries]
+// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell genRootCAHashes.js \
+// [absolute path to]/RootHashes.inc'
+
+const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
+const CertDb = Cc[nsX509CertDB].getService(Ci.nsIX509CertDB);
+
+const { FileUtils } = ChromeUtils.import(
+ "resource://gre/modules/FileUtils.jsm"
+);
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+const { CommonUtils } = ChromeUtils.import(
+ "resource://services-common/utils.js"
+);
+
+const FILENAME_OUTPUT = "RootHashes.inc";
+const FILENAME_TRUST_ANCHORS = "KnownRootHashes.json";
+const ROOT_NOT_ASSIGNED = -1;
+
+const JSON_HEADER = `// 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 is an automatically generated file. It's used to maintain state for
+// runs of genRootCAHashes.js; you should never need to manually edit it
+//***************************************************************************
+
+// Notes:
+// binNumber 1 used to be for "GTE_CyberTrust_Global_Root", but that root was
+// removed from the built-in roots module, so now it is used to indicate that
+// the certificate is not a built-in and was found in the softoken (cert9.db).
+
+// binNumber 2 used to be for "Thawte_Server_CA", but that root was removed from
+// the built-in roots module, so now it is used to indicate that the certificate
+// is not a built-in and was found on an external PKCS#11 token.
+
+// binNumber 3 used to be for "Thawte_Premium_Server_CA", but that root was
+// removed from the built-in roots module, so now it is used to indicate that
+// the certificate is not a built-in and was temporarily imported from the OS as
+// part of the "Enterprise Roots" feature.
+
+`;
+
+const FILE_HEADER =
+ "/* This Source Code Form is subject to the terms of the Mozilla Public\n" +
+ " * License, v. 2.0. If a copy of the MPL was not distributed with this\n" +
+ " * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" +
+ "\n" +
+ "/*****************************************************************************/\n" +
+ "/* This is an automatically generated file. If you're not */\n" +
+ "/* RootCertificateTelemetryUtils.cpp, you shouldn't be #including it. */\n" +
+ "/*****************************************************************************/\n" +
+ "\n" +
+ "#define HASH_LEN 32\n";
+
+const FP_PREAMBLE =
+ "struct CertAuthorityHash {\n" +
+ " // See bug 1338873 about making these fields const.\n" +
+ " uint8_t hash[HASH_LEN];\n" +
+ " int32_t binNumber;\n" +
+ "};\n\n" +
+ "static const struct CertAuthorityHash ROOT_TABLE[] = {\n";
+
+const FP_POSTAMBLE = "};\n";
+
+// Helper
+function writeString(fos, string) {
+ fos.write(string, string.length);
+}
+
+// Remove all colons from a string
+function stripColons(hexString) {
+ return hexString.replace(/:/g, "");
+}
+
+// Expect an array of bytes and make it C-formatted
+function hexSlice(bytes, start, end) {
+ let ret = "";
+ for (let i = start; i < end; i++) {
+ let hex = (0 + bytes.charCodeAt(i).toString(16)).slice(-2).toUpperCase();
+ ret += "0x" + hex;
+ if (i < end - 1) {
+ ret += ", ";
+ }
+ }
+ return ret;
+}
+
+function stripComments(buf) {
+ let lines = buf.split("\n");
+ let entryRegex = /^\s*\/\//;
+ let data = "";
+ for (let i = 0; i < lines.length; i++) {
+ let match = entryRegex.exec(lines[i]);
+ if (!match) {
+ data = data + lines[i];
+ }
+ }
+ return data;
+}
+
+// Load the trust anchors JSON object from disk
+function loadTrustAnchors(file) {
+ if (file.exists()) {
+ let stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
+ Ci.nsIFileInputStream
+ );
+ stream.init(file, -1, 0, 0);
+ let buf = NetUtil.readInputStreamToString(stream, stream.available());
+ return JSON.parse(stripComments(buf));
+ }
+ // If there's no input file, bootstrap.
+ return { roots: [], maxBin: 0 };
+}
+
+// Saves our persistence file so that we don't lose track of the mapping
+// between bin numbers and the CA-hashes, even as CAs come and go.
+function writeTrustAnchors(file) {
+ let fos = FileUtils.openSafeFileOutputStream(file);
+
+ let serializedData = JSON.stringify(gTrustAnchors, null, " ");
+ fos.write(JSON_HEADER, JSON_HEADER.length);
+ fos.write(serializedData, serializedData.length);
+
+ FileUtils.closeSafeFileOutputStream(fos);
+}
+
+// Write the C++ header file
+function writeRootHashes(fos) {
+ try {
+ writeString(fos, FILE_HEADER);
+
+ // Output the sorted gTrustAnchors
+ writeString(fos, FP_PREAMBLE);
+ gTrustAnchors.roots.forEach(function(fp) {
+ let fpBytes = atob(fp.sha256Fingerprint);
+
+ writeString(fos, " {\n");
+ writeString(fos, " /* " + fp.label + " */\n");
+ writeString(fos, " { " + hexSlice(fpBytes, 0, 16) + ",\n");
+ writeString(fos, " " + hexSlice(fpBytes, 16, 32) + " },\n");
+ writeString(fos, " " + fp.binNumber + " /* Bin Number */\n");
+
+ writeString(fos, " },\n");
+ });
+ writeString(fos, FP_POSTAMBLE);
+
+ writeString(fos, "\n");
+ } catch (e) {
+ dump("ERROR: problem writing output: " + e + "\n");
+ }
+}
+
+// Scan our list (linearly) for the given fingerprint string
+function findTrustAnchorByFingerprint(sha256Fingerprint) {
+ for (let i = 0; i < gTrustAnchors.roots.length; i++) {
+ if (sha256Fingerprint == gTrustAnchors.roots[i].sha256Fingerprint) {
+ return i;
+ }
+ }
+ return ROOT_NOT_ASSIGNED;
+}
+
+// Get a clean label for a given certificate; usually the common name.
+function getLabelForCert(cert) {
+ let label = cert.commonName;
+
+ if (label.length < 5) {
+ label = cert.subjectName;
+ }
+
+ // replace non-ascii characters
+ label = label.replace(/[^[:ascii:]]/g, "_");
+ // replace non-word characters
+ label = label.replace(/[^A-Za-z0-9]/g, "_");
+ return label;
+}
+
+// Fill in the gTrustAnchors list with trust anchors from the database.
+function insertTrustAnchorsFromDatabase() {
+ // We only want CA certs for SSL
+ const CERT_TYPE = Ci.nsIX509Cert.CA_CERT;
+ const TRUST_TYPE = Ci.nsIX509CertDB.TRUSTED_SSL;
+
+ // Iterate through the whole Cert DB
+ for (let cert of CertDb.getCerts()) {
+ // Find the certificate in our existing list. Do it here because we need to check if
+ // it's untrusted too.
+
+ // If this is a trusted cert
+ if (CertDb.isCertTrusted(cert, CERT_TYPE, TRUST_TYPE)) {
+ // Base64 encode the hex string
+ let binaryFingerprint = CommonUtils.hexToBytes(
+ stripColons(cert.sha256Fingerprint)
+ );
+ let encodedFingerprint = btoa(binaryFingerprint);
+
+ // Scan to see if this is already in the database.
+ if (
+ findTrustAnchorByFingerprint(encodedFingerprint) == ROOT_NOT_ASSIGNED
+ ) {
+ // Let's get a usable name; some old certs do not have CN= filled out
+ let label = getLabelForCert(cert);
+
+ // Add to list
+ gTrustAnchors.maxBin += 1;
+ gTrustAnchors.roots.push({
+ label,
+ binNumber: gTrustAnchors.maxBin,
+ sha256Fingerprint: encodedFingerprint,
+ });
+ }
+ }
+ }
+}
+
+//
+// PRIMARY LOGIC
+//
+
+if (arguments.length != 1) {
+ throw new Error(
+ "Usage: genRootCAHashes.js <absolute path to current RootHashes.inc>"
+ );
+}
+
+var trustAnchorsFile = FileUtils.getFile("CurWorkD", [FILENAME_TRUST_ANCHORS]);
+var rootHashesFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+rootHashesFile.initWithPath(arguments[0]);
+
+// Open the known hashes file; this is to ensure stable bin numbers.
+var gTrustAnchors = loadTrustAnchors(trustAnchorsFile);
+
+// Collect all certificate entries
+insertTrustAnchorsFromDatabase();
+
+// Update known hashes before we sort
+writeTrustAnchors(trustAnchorsFile);
+
+// Sort all trust anchors before writing, as AccumulateRootCA.cpp
+// will perform binary searches
+gTrustAnchors.roots.sort(function(a, b) {
+ // We need to work from the binary values, not the base64 values.
+ let aBin = atob(a.sha256Fingerprint);
+ let bBin = atob(b.sha256Fingerprint);
+
+ if (aBin < bBin) {
+ return -1;
+ }
+ if (aBin > bBin) {
+ return 1;
+ }
+ return 0;
+});
+
+// Write the output file.
+var rootHashesFileOutputStream = FileUtils.openSafeFileOutputStream(
+ rootHashesFile
+);
+writeRootHashes(rootHashesFileOutputStream);
+FileUtils.closeSafeFileOutputStream(rootHashesFileOutputStream);
diff --git a/security/manager/tools/getCTKnownLogs.py b/security/manager/tools/getCTKnownLogs.py
new file mode 100755
index 0000000000..536d7ab0e1
--- /dev/null
+++ b/security/manager/tools/getCTKnownLogs.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env 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/.
+
+"""
+Parses a JSON file listing the known Certificate Transparency logs
+(log_list.json) and generates a C++ header file to be included in Firefox.
+
+The current log_list.json file available under security/manager/tools
+was originally downloaded from
+https://www.certificate-transparency.org/known-logs
+and edited to include the disqualification time for the disqualified logs using
+https://cs.chromium.org/chromium/src/net/cert/ct_known_logs_static-inc.h
+"""
+
+from __future__ import print_function
+from string import Template
+import argparse
+import base64
+import datetime
+import json
+import os.path
+import sys
+import textwrap
+import urllib2
+
+import six
+
+
+def decodebytes(s):
+ if six.PY3:
+ return base64.decodebytes(six.ensure_binary(s))
+ return base64.decodestring(s)
+
+
+OUTPUT_TEMPLATE = """\
+/* -*- 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 was automatically generated by $prog. */
+
+#ifndef $include_guard
+#define $include_guard
+
+#include "CTLog.h"
+
+#include <stddef.h>
+
+struct CTLogInfo
+{
+ // See bug 1338873 about making these fields const.
+ const char* name;
+ // Index within kCTLogOperatorList.
+ mozilla::ct::CTLogStatus status;
+ // 0 for qualified logs, disqualification time for disqualified logs
+ // (in milliseconds, measured since the epoch, ignoring leap seconds).
+ uint64_t disqualificationTime;
+ size_t operatorIndex;
+ const char* key;
+ size_t keyLength;
+};
+
+struct CTLogOperatorInfo
+{
+ // See bug 1338873 about making these fields const.
+ const char* name;
+ mozilla::ct::CTLogOperatorId id;
+};
+
+const CTLogInfo kCTLogList[] = {
+$logs
+};
+
+const CTLogOperatorInfo kCTLogOperatorList[] = {
+$operators
+};
+
+#endif // $include_guard
+"""
+
+
+def get_disqualification_time(time_str):
+ """
+ Convert a time string such as "2017-01-01T00:00:00Z" to an integer
+ representing milliseconds since the epoch.
+ Timezones in the string are not supported and will result in an exception.
+ """
+ t = datetime.datetime.strptime(time_str, "%Y-%m-%dT%H:%M:%SZ")
+ epoch = datetime.datetime.utcfromtimestamp(0)
+ seconds_since_epoch = (t - epoch).total_seconds()
+ return int(seconds_since_epoch * 1000)
+
+
+def get_hex_lines(blob, width):
+ """ Convert a binary string to a multiline text of C escape sequences. """
+ text = "".join(["\\x{:02x}".format(ord(c)) for c in blob])
+ # When escaped, a single byte takes 4 chars (e.g. "\x00").
+ # Make sure we don't break an escaped byte between the lines.
+ return textwrap.wrap(text, width - width % 4)
+
+
+def get_operator_and_index(json_data, operator_id):
+ """ Return operator's entry from the JSON along with its array index. """
+ matches = [
+ (operator, index)
+ for (index, operator) in enumerate(json_data["operators"])
+ if operator["id"] == operator_id
+ ]
+ assert len(matches) != 0, "No operators with id {0} defined.".format(operator_id)
+ assert len(matches) == 1, "Found multiple operators with id {0}.".format(
+ operator_id
+ )
+ return matches[0]
+
+
+def get_log_info_structs(json_data):
+ """ Return array of CTLogInfo initializers for the known logs. """
+ tmpl = Template(
+ textwrap.dedent(
+ """\
+ { $description,
+ $status,
+ $disqualification_time, // $disqualification_time_comment
+ $operator_index, // $operator_comment
+ $indented_log_key,
+ $log_key_len }"""
+ )
+ )
+ initializers = []
+ for log in json_data["logs"]:
+ log_key = decodebytes(log["key"])
+ # "operated_by" is a list, we assume here it always contains one item.
+ operated_by = log["operated_by"]
+ assert len(operated_by) == 1, "operated_by must contain one item."
+ operator, operator_index = get_operator_and_index(json_data, operated_by[0])
+ if "disqualification_time" in log:
+ status = "mozilla::ct::CTLogStatus::Disqualified"
+ disqualification_time = get_disqualification_time(
+ log["disqualification_time"]
+ )
+ disqualification_time_comment = 'Date.parse("{0}")'.format(
+ log["disqualification_time"]
+ )
+ else:
+ status = "mozilla::ct::CTLogStatus::Included"
+ disqualification_time = 0
+ disqualification_time_comment = "no disqualification time"
+ is_test_log = "test_only" in operator and operator["test_only"]
+ prefix = ""
+ suffix = ","
+ if is_test_log:
+ prefix = "#ifdef DEBUG\n"
+ suffix = ",\n#endif // DEBUG"
+ toappend = tmpl.substitute(
+ # Use json.dumps for C-escaping strings.
+ # Not perfect but close enough.
+ description=json.dumps(log["description"]),
+ operator_index=operator_index,
+ operator_comment="operated by {0}".
+ # The comment must not contain "/".
+ format(operator["name"]).replace("/", "|"),
+ status=status,
+ disqualification_time=disqualification_time,
+ disqualification_time_comment=disqualification_time_comment,
+ # Maximum line width is 80.
+ indented_log_key="\n".join(
+ [' "{0}"'.format(l) for l in get_hex_lines(log_key, 74)]
+ ),
+ log_key_len=len(log_key),
+ )
+ initializers.append(prefix + toappend + suffix)
+ return initializers
+
+
+def get_log_operator_structs(json_data):
+ """ Return array of CTLogOperatorInfo initializers. """
+ tmpl = Template(" { $name, $id }")
+ initializers = []
+ for operator in json_data["operators"]:
+ prefix = ""
+ suffix = ","
+ is_test_log = "test_only" in operator and operator["test_only"]
+ if is_test_log:
+ prefix = "#ifdef DEBUG\n"
+ suffix = ",\n#endif // DEBUG"
+ toappend = tmpl.substitute(name=json.dumps(operator["name"]), id=operator["id"])
+ initializers.append(prefix + toappend + suffix)
+ return initializers
+
+
+def generate_cpp_header_file(json_data, out_file):
+ """ Generate the C++ header file for the known logs. """
+ filename = os.path.basename(out_file.name)
+ include_guard = filename.replace(".", "_").replace("/", "_")
+ log_info_initializers = get_log_info_structs(json_data)
+ operator_info_initializers = get_log_operator_structs(json_data)
+ out_file.write(
+ Template(OUTPUT_TEMPLATE).substitute(
+ prog=os.path.basename(sys.argv[0]),
+ include_guard=include_guard,
+ logs="\n".join(log_info_initializers),
+ operators="\n".join(operator_info_initializers),
+ )
+ )
+
+
+def patch_in_test_logs(json_data):
+ """ Insert Mozilla-specific test log data. """
+ max_id = 0
+ for operator in json_data["operators"]:
+ if operator["id"] > max_id:
+ max_id = operator["id"]
+ mozilla_test_operator_1 = {
+ "name": "Mozilla Test Org 1",
+ "id": max_id + 1,
+ "test_only": True,
+ }
+ mozilla_test_operator_2 = {
+ "name": "Mozilla Test Org 2",
+ "id": max_id + 2,
+ "test_only": True,
+ }
+ json_data["operators"].append(mozilla_test_operator_1)
+ json_data["operators"].append(mozilla_test_operator_2)
+ # The easiest way to get this is
+ # `openssl x509 -noout -pubkey -in <path/to/default-ee.pem>`
+ mozilla_rsa_log_1 = {
+ "description": "Mozilla Test RSA Log 1",
+ "key": """
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2
+ ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF
+ h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n
+ cOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAv
+ OnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2nj
+ tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt
+ jQIDAQAB
+ """,
+ "operated_by": [max_id + 1],
+ }
+ # Similarly,
+ # `openssl x509 -noout -pubkey -in <path/to/other-test-ca.pem>`
+ mozilla_rsa_log_2 = {
+ "description": "Mozilla Test RSA Log 2",
+ "key": """
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXXGUmYJn3cIKmeR8bh2
+ w39c5TiwbErNIrHL1G+mWtoq3UHIwkmKxKOzwfYUh/QbaYlBvYClHDwSAkTFhKTE
+ SDMF5ROMAQbPCL6ahidguuai6PNvI8XZgxO53683g0XazlHU1tzSpss8xwbrzTBw
+ 7JjM5AqlkdcpWn9xxb5maR0rLf7ISURZC8Wj6kn9k7HXU0BfF3N2mZWGZiVHl+1C
+ aQiICBFCIGmYikP+5Izmh4HdIramnNKDdRMfkysSjOKG+n0lHAYq0n7wFvGHzdVO
+ gys1uJMPdLqQqovHYWckKrH9bWIUDRjEwLjGj8N0hFcyStfehuZVLx0eGR1xIWjT
+ uwIDAQAB
+ """,
+ "operated_by": [max_id + 2],
+ }
+ # `openssl x509 -noout -pubkey -in <path/to/root_secp256r1_256.pem`
+ mozilla_ec_log = {
+ "description": "Mozilla Test EC Log",
+ "key": """
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAET7+7u2Hg+PmxpgpZrIcE4uwFC0I+
+ PPcukj8sT3lLRVwqadIzRWw2xBGdBwbgDu3I0ZOQ15kbey0HowTqoEqmwA==
+ """,
+ "operated_by": [max_id + 1],
+ }
+ json_data["logs"].append(mozilla_rsa_log_1)
+ json_data["logs"].append(mozilla_rsa_log_2)
+ json_data["logs"].append(mozilla_ec_log)
+
+
+def run(args):
+ """
+ Load the input JSON file and generate the C++ header according to the
+ command line arguments.
+ """
+ if args.file:
+ print("Reading file: ", args.file)
+ with open(args.file, "rb") as json_file:
+ json_text = json_file.read()
+ elif args.url:
+ print("Fetching URL: ", args.url)
+ json_request = urllib2.urlopen(args.url)
+ try:
+ json_text = json_request.read()
+ finally:
+ json_request.close()
+
+ json_data = json.loads(json_text)
+
+ print("Writing output: ", args.out)
+
+ patch_in_test_logs(json_data)
+
+ with open(args.out, "w") as out_file:
+ generate_cpp_header_file(json_data, out_file)
+
+ print("Done.")
+
+
+def parse_arguments_and_run():
+ """ Parse the command line arguments and run the program. """
+ arg_parser = argparse.ArgumentParser(
+ description="Parses a JSON file listing the known "
+ "Certificate Transparency logs and generates "
+ "a C++ header file to be included in Firefox.",
+ epilog="Example: python %s --url" % os.path.basename(sys.argv[0]),
+ )
+
+ source_group = arg_parser.add_mutually_exclusive_group(required=True)
+ source_group.add_argument(
+ "--file",
+ nargs="?",
+ const="log_list.json",
+ help="Read the known CT logs JSON data from the "
+ "specified local file (%(const)s by default).",
+ )
+ source_group.add_argument(
+ "--url", help="Download the known CT logs JSON file " "from the specified URL."
+ )
+
+ arg_parser.add_argument(
+ "--out",
+ default="../../certverifier/CTKnownLogs.h",
+ help="Path and filename of the header file "
+ "to be generated. Defaults to %(default)s",
+ )
+
+ run(arg_parser.parse_args())
+
+
+if __name__ == "__main__":
+ parse_arguments_and_run()
diff --git a/security/manager/tools/log_list.json b/security/manager/tools/log_list.json
new file mode 100644
index 0000000000..74769aad57
--- /dev/null
+++ b/security/manager/tools/log_list.json
@@ -0,0 +1,128 @@
+{
+ "operators": [
+ {
+ "name": "Google",
+ "id": 0
+ },
+ {
+ "name": "DigiCert",
+ "id": 1
+ },
+ {
+ "name": "Certly",
+ "id": 2
+ },
+ {
+ "name": "Izenpe",
+ "id": 3
+ },
+ {
+ "name": "Symantec",
+ "id": 4
+ },
+ {
+ "name": "Venafi",
+ "id": 5
+ },
+ {
+ "name": "CNNIC",
+ "id": 7
+ },
+ {
+ "name": "WoSign",
+ "id": 8
+ },
+ {
+ "name": "StartCom",
+ "id": 9
+ }
+ ],
+ "logs": [
+ {
+ "description": "Google 'Pilot' log",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfahLEimAoz2t01p3uMziiLOl/fHTDM0YDOhBRuiBARsV4UvxG2LdNgoIGLrtCzWE0J5APC2em4JlvR8EEEFMoA==",
+ "url": "ct.googleapis.com/pilot",
+ "maximum_merge_delay": 86400,
+ "operated_by": [0]
+ },
+ {
+ "description": "Google 'Aviator' log",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1JYP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q==",
+ "url": "ct.googleapis.com/aviator",
+ "maximum_merge_delay": 86400,
+ "operated_by": [0]
+ },
+ {
+ "description": "DigiCert Log Server",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAkbFvhu7gkAW6MHSrBlpE1n4+HCFRkC5OLAjgqhkTH+/uzSfSl8ois8ZxAD2NgaTZe1M9akhYlrYkes4JECs6A==",
+ "url": "ct1.digicert-ct.com/log",
+ "maximum_merge_delay": 86400,
+ "operated_by": [1]
+ },
+ {
+ "description": "Google 'Rocketeer' log",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg==",
+ "url": "ct.googleapis.com/rocketeer",
+ "maximum_merge_delay": 86400,
+ "operated_by": [0]
+ },
+ {
+ "description": "Certly.IO log",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECyPLhWKYYUgEc+tUXfPQB4wtGS2MNvXrjwFCCnyYJifBtd2Sk7Cu+Js9DNhMTh35FftHaHu6ZrclnNBKwmbbSA==",
+ "url": "log.certly.io",
+ "maximum_merge_delay": 86400,
+ "operated_by": [2],
+ "disqualification_time": "2016-04-15T00:00:00Z"
+ },
+ {
+ "description": "Izenpe log",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ2Q5DC3cUBj4IQCiDu0s6j51up+TZAkAEcQRF6tczw90rLWXkJMAW7jr9yc92bIKgV8vDXU4lDeZHvYHduDuvg==",
+ "url": "ct.izenpe.com",
+ "maximum_merge_delay": 86400,
+ "operated_by": [3],
+ "disqualification_time": "2016-05-30T00:00:00Z"
+ },
+ {
+ "description": "Symantec log",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEluqsHEYMG1XcDfy1lCdGV0JwOmkY4r87xNuroPS2bMBTP01CEDPwWJePa75y9CrsHEKqAy8afig1dpkIPSEUhg==",
+ "url": "ct.ws.symantec.com",
+ "maximum_merge_delay": 86400,
+ "operated_by": [4]
+ },
+ {
+ "description": "Venafi log",
+ "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAolpIHxdSlTXLo1s6H1OCdpSj/4DyHDc8wLG9wVmLqy1lk9fz4ATVmm+/1iN2Nk8jmctUKK2MFUtlWXZBSpym97M7frGlSaQXUWyA3CqQUEuIJOmlEjKTBEiQAvpfDjCHjlV2Be4qTM6jamkJbiWtgnYPhJL6ONaGTiSPm7Byy57iaz/hbckldSOIoRhYBiMzeNoA0DiRZ9KmfSeXZ1rB8y8X5urSW+iBzf2SaOfzBvDpcoTuAaWx2DPazoOl28fP1hZ+kHUYvxbcMjttjauCFx+JII0dmuZNIwjfeG/GBb9frpSX219k1O4Wi6OEbHEr8at/XQ0y7gTikOxBn/s5wQIDAQAB",
+ "url": "ctlog.api.venafi.com",
+ "maximum_merge_delay": 86400,
+ "operated_by": [5]
+ },
+ {
+ "description": "Symantec 'Vega' log",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6pWeAv/u8TNtS4e8zf0ZF2L/lNPQWQc/Ai0ckP7IRzA78d0NuBEMXR2G3avTK0Zm+25ltzv9WWis36b4ztIYTQ==",
+ "url": "vega.ws.symantec.com",
+ "maximum_merge_delay": 86400,
+ "operated_by": [4]
+ },
+ {
+ "description": "CNNIC CT log",
+ "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7UIYZopMgTTJWPp2IXhhuAf1l6a9zM7gBvntj5fLaFm9pVKhKYhVnno94XuXeN8EsDgiSIJIj66FpUGvai5samyetZhLocRuXhAiXXbDNyQ4KR51tVebtEq2zT0mT9liTtGwiksFQccyUsaVPhsHq9gJ2IKZdWauVA2Fm5x9h8B9xKn/L/2IaMpkIYtd967TNTP/dLPgixN1PLCLaypvurDGSVDsuWabA3FHKWL9z8wr7kBkbdpEhLlg2H+NAC+9nGKx+tQkuhZ/hWR65aX+CNUPy2OB9/u2rNPyDydb988LENXoUcMkQT0dU3aiYGkFAY0uZjD2vH97TM20xYtNQIDAQAB",
+ "url": "ctserver.cnnic.cn",
+ "maximum_merge_delay": 86400,
+ "operated_by": [7]
+ },
+ {
+ "description": "WoSign log",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzBGIey1my66PTTBmJxklIpMhRrQvAdPG+SvVyLpzmwai8IoCnNBrRhgwhbrpJIsO0VtwKAx+8TpFf1rzgkJgMQ==",
+ "url": "ctlog.wosign.com",
+ "maximum_merge_delay": 86400,
+ "operated_by": [8]
+ },
+ {
+ "description": "StartCom log",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESPNZ8/YFGNPbsu1Gfs/IEbVXsajWTOaft0oaFIZDqUiwy1o/PErK38SCFFWa+PeOQFXc9NKv6nV0+05/YIYuUQ==",
+ "url": "ct.startssl.com",
+ "maximum_merge_delay": 86400,
+ "operated_by": [9]
+ }
+ ]
+}