tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }