test_cert_storage.js (9121B)
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 "use strict"; 6 7 // This test checks a number of things: 8 // * it ensures that data loaded from revocations.txt on startup is present 9 // * it ensures that data served from OneCRL are persisted correctly 10 // * it ensures that items in the CertBlocklist are seen as revoked by the 11 // cert verifier 12 // * it does a sanity check to ensure other cert verifier behavior is 13 // unmodified 14 15 const { RemoteSecuritySettings } = ChromeUtils.importESModule( 16 "resource://gre/modules/psm/RemoteSecuritySettings.sys.mjs" 17 ); 18 19 // First, we need to setup appInfo for the blocklist service to work 20 var id = "xpcshell@tests.mozilla.org"; 21 var appName = "XPCShell"; 22 var version = "1"; 23 var platformVersion = "1.9.2"; 24 const { updateAppInfo } = ChromeUtils.importESModule( 25 "resource://testing-common/AppInfo.sys.mjs" 26 ); 27 updateAppInfo({ 28 name: appName, 29 ID: id, 30 version, 31 platformVersion: platformVersion ? platformVersion : "1.0", 32 crashReporter: true, 33 }); 34 35 // we need to ensure we setup revocation data before certDB, or we'll start with 36 // no revocation.txt in the profile 37 var gProfile = do_get_profile(); 38 39 var gRevocations = gProfile.clone(); 40 gRevocations.append("revocations.txt"); 41 if (!gRevocations.exists()) { 42 let existing = do_get_file("test_onecrl/sample_revocations.txt", false); 43 existing.copyTo(gProfile, "revocations.txt"); 44 } 45 46 var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService( 47 Ci.nsIX509CertDB 48 ); 49 50 const certBlocklist = [ 51 // test with some bad data ... 52 { 53 issuerName: "Some nonsense in issuer", 54 serialNumber: "AkHVNA==", 55 }, 56 { 57 issuerName: "MA0xCzAJBgNVBAMMAmNh", 58 serialNumber: "some nonsense in serial", 59 }, 60 { 61 issuerName: "and serial", 62 serialNumber: "some nonsense in both issuer", 63 }, 64 // some mixed 65 // In these case, the issuer name and the valid serialNumber correspond 66 // to test-int.pem in bad_certs/ 67 { 68 issuerName: "MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=", 69 serialNumber: "oops! more nonsense.", 70 }, 71 { 72 issuerName: "MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=", 73 serialNumber: "AxPrsRjtbFinLUfRzhtR8EeYh4Y=", 74 }, 75 // ... and some good 76 // In this case, the issuer name and the valid serialNumber correspond 77 // to other-test-ca.pem in bad_certs/ (for testing root revocation) 78 { 79 issuerName: "MBgxFjAUBgNVBAMMDU90aGVyIHRlc3QgQ0E=", 80 serialNumber: "VTTv5DQM+fh01nnfm3AoUJt4UIY=", 81 }, 82 // These items correspond to an entry in sample_revocations.txt where: 83 // isser name is the base-64 encoded subject DN for the shared Test 84 // Intermediate and the serialNumbers are base-64 encoded 78 and 31, 85 // respectively. 86 // We need this to ensure that existing items are retained if they're 87 // also in the blocklist 88 { 89 issuerName: "MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl", 90 serialNumber: "Tg==", 91 }, 92 { 93 issuerName: "MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl", 94 serialNumber: "Hw==", 95 }, 96 // This item revokes same-issuer-ee.pem by subject and pubKeyHash. 97 { 98 subject: "MCIxIDAeBgNVBAMMF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5", 99 pubKeyHash: "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=", 100 }, 101 ]; 102 103 function verify_cert(file, expectedError) { 104 let ee = constructCertFromFile(file); 105 return checkCertErrorGeneric( 106 certDB, 107 ee, 108 expectedError, 109 Ci.nsIX509CertDB.verifyUsageTLSServer 110 ); 111 } 112 113 // The certificate blocklist currently only applies to TLS server certificates. 114 async function verify_non_tls_usage_succeeds(file) { 115 let ee = constructCertFromFile(file); 116 await checkCertErrorGeneric( 117 certDB, 118 ee, 119 PRErrorCodeSuccess, 120 Ci.nsIX509CertDB.verifyUsageTLSClient 121 ); 122 await checkCertErrorGeneric( 123 certDB, 124 ee, 125 PRErrorCodeSuccess, 126 Ci.nsIX509CertDB.verifyUsageEmailSigner 127 ); 128 await checkCertErrorGeneric( 129 certDB, 130 ee, 131 PRErrorCodeSuccess, 132 Ci.nsIX509CertDB.verifyUsageEmailRecipient 133 ); 134 } 135 136 function load_cert(cert, trust) { 137 let file = "bad_certs/" + cert + ".pem"; 138 addCertFromFile(certDB, file, trust); 139 } 140 141 async function update_blocklist() { 142 const { OneCRLBlocklistClient } = RemoteSecuritySettings.init(); 143 144 const fakeEvent = { 145 current: certBlocklist, // with old .txt revocations. 146 deleted: [], 147 created: certBlocklist, // with new cert storage. 148 updated: [], 149 }; 150 await OneCRLBlocklistClient.emit("sync", { data: fakeEvent }); 151 // Save the last check timestamp, used by cert_storage to assert 152 // if the blocklist is «fresh». 153 Services.prefs.setIntPref( 154 OneCRLBlocklistClient.lastCheckTimePref, 155 Math.floor(Date.now() / 1000) 156 ); 157 } 158 159 function run_test() { 160 // import the certificates we need 161 load_cert("test-ca", "CTu,CTu,CTu"); 162 load_cert("test-int", ",,"); 163 load_cert("other-test-ca", "CTu,CTu,CTu"); 164 165 add_task(async function () { 166 // check some existing items in revocations.txt are blocked. 167 // This test corresponds to: 168 // issuer: MBIxEDAOBgNVBAMMB1Rlc3QgQ0E= (CN=Test CA) 169 // serial: Kg== (42) 170 let file = "test_onecrl/ee-revoked-by-revocations-txt.pem"; 171 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 172 173 // This test corresponds to: 174 // issuer: MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl (CN=Test Intermediate) 175 // serial: Tg== (78) 176 file = "test_onecrl/another-ee-revoked-by-revocations-txt.pem"; 177 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 178 179 // And this test corresponds to: 180 // issuer: MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl (CN=Test Intermediate) 181 // serial: Hw== (31) 182 // (we test this issuer twice to ensure we can read multiple serials) 183 file = "test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem"; 184 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 185 186 // Test that a certificate revoked by subject and public key hash in 187 // revocations.txt is revoked 188 // subject: MCsxKTAnBgNVBAMMIEVFIFJldm9rZWQgQnkgU3ViamVjdCBhbmQgUHViS2V5 189 // (CN=EE Revoked By Subject and PubKey) 190 // pubkeyhash: VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8= (this is the 191 // shared RSA SPKI) 192 file = "test_onecrl/ee-revoked-by-subject-and-pubkey.pem"; 193 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 194 195 // Soon we'll load a blocklist which revokes test-int.pem, which issued 196 // test-int-ee.pem. 197 // Check the cert validates before we load the blocklist 198 file = "test_onecrl/test-int-ee.pem"; 199 await verify_cert(file, PRErrorCodeSuccess); 200 201 // The blocklist also revokes other-test-ca.pem, which issued 202 // other-ca-ee.pem. Check the cert validates before we load the blocklist 203 file = "bad_certs/other-issuer-ee.pem"; 204 await verify_cert(file, PRErrorCodeSuccess); 205 206 // The blocklist will revoke same-issuer-ee.pem via subject / pubKeyHash. 207 // Check the cert validates before we load the blocklist 208 file = "test_onecrl/same-issuer-ee.pem"; 209 await verify_cert(file, PRErrorCodeSuccess); 210 }); 211 212 // blocklist load is async so we must use add_test from here 213 add_task(update_blocklist); 214 215 add_task(async function () { 216 // The blocklist will be loaded now. Let's check the data is sane. 217 // In particular, we should still have the revoked issuer / serial pair 218 // that was in revocations.txt but not the blocklist. 219 let file = "test_onecrl/ee-revoked-by-revocations-txt.pem"; 220 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 221 222 // We should also still have the revoked issuer / serial pairs that were in 223 // revocations.txt and are also in the blocklist. 224 file = "test_onecrl/another-ee-revoked-by-revocations-txt.pem"; 225 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 226 file = "test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem"; 227 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 228 229 // The cert revoked by subject and pubkeyhash should still be revoked. 230 file = "test_onecrl/ee-revoked-by-subject-and-pubkey.pem"; 231 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 232 233 // Check the blocklisted intermediate now causes a failure 234 file = "test_onecrl/test-int-ee.pem"; 235 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 236 await verify_non_tls_usage_succeeds(file); 237 238 // Check the ee with the blocklisted root also causes a failure 239 file = "bad_certs/other-issuer-ee.pem"; 240 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 241 await verify_non_tls_usage_succeeds(file); 242 243 // Check the ee blocked by subject / pubKey causes a failure 244 file = "test_onecrl/same-issuer-ee.pem"; 245 await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); 246 await verify_non_tls_usage_succeeds(file); 247 248 // Check a non-blocklisted chain still validates OK 249 file = "bad_certs/default-ee.pem"; 250 await verify_cert(file, PRErrorCodeSuccess); 251 252 // Check a bad cert is still bad (unknown issuer) 253 file = "bad_certs/unknownissuer.pem"; 254 await verify_cert(file, SEC_ERROR_UNKNOWN_ISSUER); 255 }); 256 257 run_next_test(); 258 }