tor-browser

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

test_sdr.js (7982B)


      1 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
      2 // Any copyright is dedicated to the Public Domain.
      3 // http://creativecommons.org/publicdomain/zero/1.0/
      4 "use strict";
      5 
      6 // Tests various aspects of the nsISecretDecoderRing implementation.
      7 
      8 do_get_profile();
      9 
     10 let gSetPasswordShownCount = 0;
     11 
     12 // Mock implementation of nsITokenPasswordDialogs.
     13 const gTokenPasswordDialogs = {
     14  setPassword(ctx, tokenName) {
     15    gSetPasswordShownCount++;
     16    info(`setPassword() called; shown ${gSetPasswordShownCount} times`);
     17    info(`tokenName: ${tokenName}`);
     18    return false; // Returning false means "the user didn't cancel".
     19  },
     20 
     21  QueryInterface: ChromeUtils.generateQI(["nsITokenPasswordDialogs"]),
     22 };
     23 
     24 let gMockPrompter = {
     25  promptPassword() {
     26    // Returning false simulates the user canceling the password prompt.
     27    return false;
     28  },
     29 
     30  QueryInterface: ChromeUtils.generateQI(["nsIPrompt"]),
     31 };
     32 
     33 // Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
     34 // to call promptPassword. We return the mock one, above.
     35 let gWindowWatcher = {
     36  getNewPrompter: () => gMockPrompter,
     37  QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"]),
     38 };
     39 
     40 add_task(function setup() {
     41  let windowWatcherCID = MockRegistrar.register(
     42    "@mozilla.org/embedcomp/window-watcher;1",
     43    gWindowWatcher
     44  );
     45  registerCleanupFunction(() => {
     46    MockRegistrar.unregister(windowWatcherCID);
     47  });
     48 });
     49 
     50 add_task(function testEncryptString() {
     51  let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
     52    Ci.nsISecretDecoderRing
     53  );
     54 
     55  // Test valid inputs for encryptString() and decryptString().
     56  let inputs = [
     57    "",
     58    " ", // First printable latin1 character (code point 32).
     59    "foo",
     60    "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
     61    "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
     62    "aaa 一二三", // Includes Unicode with code points outside [0, 255].
     63  ];
     64  for (let input of inputs) {
     65    let converter = Cc[
     66      "@mozilla.org/intl/scriptableunicodeconverter"
     67    ].createInstance(Ci.nsIScriptableUnicodeConverter);
     68    converter.charset = "UTF-8";
     69 
     70    let convertedInput = converter.ConvertFromUnicode(input);
     71    convertedInput += converter.Finish();
     72 
     73    let encrypted = sdr.encryptString(convertedInput);
     74 
     75    notEqual(
     76      convertedInput,
     77      encrypted,
     78      "Encrypted input should not just be the input itself"
     79    );
     80 
     81    try {
     82      atob(encrypted);
     83    } catch (e) {
     84      ok(false, `encryptString() should have returned Base64: ${e}`);
     85    }
     86 
     87    equal(
     88      convertedInput,
     89      sdr.decryptString(encrypted),
     90      "decryptString(encryptString(input)) should return input"
     91    );
     92  }
     93 
     94  // Test invalid inputs for decryptString().
     95  throws(
     96    () => sdr.decryptString("*"),
     97    /NS_ERROR_ILLEGAL_VALUE/,
     98    "decryptString() should throw if given non-Base64 input"
     99  );
    100 
    101  // Test calling changePassword() pops up the appropriate dialog.
    102  // Note: On Android, nsITokenPasswordDialogs is apparently not implemented,
    103  //       which also seems to prevent us from mocking out the interface.
    104  if (AppConstants.platform != "android") {
    105    let tokenPasswordDialogsCID = MockRegistrar.register(
    106      "@mozilla.org/nsTokenPasswordDialogs;1",
    107      gTokenPasswordDialogs
    108    );
    109    registerCleanupFunction(() => {
    110      MockRegistrar.unregister(tokenPasswordDialogsCID);
    111    });
    112 
    113    equal(
    114      gSetPasswordShownCount,
    115      0,
    116      "changePassword() dialog should have been shown zero times"
    117    );
    118    sdr.changePassword();
    119    equal(
    120      gSetPasswordShownCount,
    121      1,
    122      "changePassword() dialog should have been shown exactly once"
    123    );
    124  }
    125 });
    126 
    127 add_task(async function testAsyncEncryptStrings() {
    128  let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
    129    Ci.nsISecretDecoderRing
    130  );
    131 
    132  // Test valid inputs for encryptString() and decryptString().
    133  let inputs = [
    134    "",
    135    " ", // First printable latin1 character (code point 32).
    136    "foo",
    137    "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
    138    "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
    139    "aaa 一二三", // Includes Unicode with code points outside [0, 255].
    140  ];
    141 
    142  let encrypteds = await sdr.asyncEncryptStrings(inputs);
    143  for (let i = 0; i < inputs.length; i++) {
    144    let encrypted = encrypteds[i];
    145    let input = inputs[i];
    146    let converter = Cc[
    147      "@mozilla.org/intl/scriptableunicodeconverter"
    148    ].createInstance(Ci.nsIScriptableUnicodeConverter);
    149    converter.charset = "UTF-8";
    150 
    151    let convertedInput = converter.ConvertFromUnicode(input);
    152    convertedInput += converter.Finish();
    153    notEqual(
    154      convertedInput,
    155      encrypted,
    156      "Encrypted input should not just be the input itself"
    157    );
    158 
    159    try {
    160      atob(encrypted);
    161    } catch (e) {
    162      ok(false, `encryptString() should have returned Base64: ${e}`);
    163    }
    164 
    165    equal(
    166      convertedInput,
    167      sdr.decryptString(encrypted),
    168      "decryptString(encryptString(input)) should return input"
    169    );
    170  }
    171 });
    172 
    173 add_task(async function testAsyncDecryptStrings() {
    174  let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
    175    Ci.nsISecretDecoderRing
    176  );
    177 
    178  // Test valid inputs for encryptString() and decryptString().
    179  let testCases = [
    180    "",
    181    " ", // First printable latin1 character (code point 32).
    182    "foo",
    183    "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
    184    "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
    185    "aaa 一二三", // Includes Unicode with code points outside [0, 255].
    186  ];
    187 
    188  let convertedTestCases = testCases.map(tc => {
    189    let converter = Cc[
    190      "@mozilla.org/intl/scriptableunicodeconverter"
    191    ].createInstance(Ci.nsIScriptableUnicodeConverter);
    192    converter.charset = "UTF-8";
    193 
    194    let convertedInput = converter.ConvertFromUnicode(tc);
    195    convertedInput += converter.Finish();
    196    return convertedInput;
    197  });
    198 
    199  let encryptedStrings = convertedTestCases.map(tc => sdr.encryptString(tc));
    200  let decrypteds = await sdr.asyncDecryptStrings(encryptedStrings);
    201  for (let i = 0; i < encryptedStrings.length; i++) {
    202    let decrypted = decrypteds[i];
    203 
    204    equal(
    205      decrypted,
    206      testCases[i],
    207      "decrypted string should match expected value"
    208    );
    209    equal(
    210      sdr.decryptString(encryptedStrings[i]),
    211      convertedTestCases[i],
    212      "decryptString(encryptString(input)) should return the initial decrypted string value"
    213    );
    214  }
    215 });
    216 
    217 add_task(async function testAsyncDecryptInvalidStrings() {
    218  let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
    219    Ci.nsISecretDecoderRing
    220  );
    221 
    222  // Test invalid inputs for sdr.asyncDecryptStrings
    223  let testCases = [
    224    "~bmV0cGxheQ==", // invalid base64 encoding
    225    "bmV0cGxheQ==", // valid base64 characters but not encrypted
    226    "https://www.example.com", // website address from erroneous migration
    227  ];
    228 
    229  let decrypteds = await sdr.asyncDecryptStrings(testCases);
    230  equal(
    231    decrypteds.length,
    232    testCases.length,
    233    "each testcase should still return a response"
    234  );
    235  for (let i = 0; i < decrypteds.length; i++) {
    236    let decrypted = decrypteds[i];
    237 
    238    equal(
    239      decrypted,
    240      "",
    241      "decrypted string should be empty when trying to decrypt an invalid input with asyncDecryptStrings"
    242    );
    243 
    244    Assert.throws(
    245      () => sdr.decryptString(testCases[i]),
    246      /NS_ERROR_ILLEGAL_VALUE|NS_ERROR_FAILURE/,
    247      `Check testcase would have thrown: ${testCases[i]}`
    248    );
    249  }
    250 });
    251 
    252 add_task(async function testAsyncDecryptLoggedOut() {
    253  // Set a master password.
    254  let token = Cc["@mozilla.org/security/pk11tokendb;1"]
    255    .getService(Ci.nsIPK11TokenDB)
    256    .getInternalKeyToken();
    257  token.initPassword("password");
    258  token.logoutSimple();
    259 
    260  let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
    261    Ci.nsISecretDecoderRing
    262  );
    263 
    264  await Assert.rejects(
    265    sdr.asyncDecryptStrings(["irrelevant"]),
    266    /NS_ERROR_NOT_AVAILABLE/,
    267    "Check error is thrown instead of returning empty strings"
    268  );
    269 
    270  token.reset();
    271  token.initPassword("");
    272 });