summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/test_sdr.js
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/unit/test_sdr.js')
-rw-r--r--security/manager/ssl/tests/unit/test_sdr.js272
1 files changed, 272 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/unit/test_sdr.js b/security/manager/ssl/tests/unit/test_sdr.js
new file mode 100644
index 0000000000..e9e477efc5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sdr.js
@@ -0,0 +1,272 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests various aspects of the nsISecretDecoderRing implementation.
+
+do_get_profile();
+
+let gSetPasswordShownCount = 0;
+
+// Mock implementation of nsITokenPasswordDialogs.
+const gTokenPasswordDialogs = {
+ setPassword(ctx, tokenName) {
+ gSetPasswordShownCount++;
+ info(`setPassword() called; shown ${gSetPasswordShownCount} times`);
+ info(`tokenName: ${tokenName}`);
+ return false; // Returning false means "the user didn't cancel".
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsITokenPasswordDialogs"]),
+};
+
+let gMockPrompter = {
+ promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
+ // Returning false simulates the user canceling the password prompt.
+ return false;
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPrompt"]),
+};
+
+// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
+// to call promptPassword. We return the mock one, above.
+let gWindowWatcher = {
+ getNewPrompter: () => gMockPrompter,
+ QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"]),
+};
+
+add_task(function setup() {
+ let windowWatcherCID = MockRegistrar.register(
+ "@mozilla.org/embedcomp/window-watcher;1",
+ gWindowWatcher
+ );
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(windowWatcherCID);
+ });
+});
+
+add_task(function testEncryptString() {
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ // Test valid inputs for encryptString() and decryptString().
+ let inputs = [
+ "",
+ " ", // First printable latin1 character (code point 32).
+ "foo",
+ "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
+ "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
+ "aaa 一二三", // Includes Unicode with code points outside [0, 255].
+ ];
+ for (let input of inputs) {
+ let converter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+
+ let convertedInput = converter.ConvertFromUnicode(input);
+ convertedInput += converter.Finish();
+
+ let encrypted = sdr.encryptString(convertedInput);
+
+ notEqual(
+ convertedInput,
+ encrypted,
+ "Encrypted input should not just be the input itself"
+ );
+
+ try {
+ atob(encrypted);
+ } catch (e) {
+ ok(false, `encryptString() should have returned Base64: ${e}`);
+ }
+
+ equal(
+ convertedInput,
+ sdr.decryptString(encrypted),
+ "decryptString(encryptString(input)) should return input"
+ );
+ }
+
+ // Test invalid inputs for decryptString().
+ throws(
+ () => sdr.decryptString("*"),
+ /NS_ERROR_ILLEGAL_VALUE/,
+ "decryptString() should throw if given non-Base64 input"
+ );
+
+ // Test calling changePassword() pops up the appropriate dialog.
+ // Note: On Android, nsITokenPasswordDialogs is apparently not implemented,
+ // which also seems to prevent us from mocking out the interface.
+ if (AppConstants.platform != "android") {
+ let tokenPasswordDialogsCID = MockRegistrar.register(
+ "@mozilla.org/nsTokenPasswordDialogs;1",
+ gTokenPasswordDialogs
+ );
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(tokenPasswordDialogsCID);
+ });
+
+ equal(
+ gSetPasswordShownCount,
+ 0,
+ "changePassword() dialog should have been shown zero times"
+ );
+ sdr.changePassword();
+ equal(
+ gSetPasswordShownCount,
+ 1,
+ "changePassword() dialog should have been shown exactly once"
+ );
+ }
+});
+
+add_task(async function testAsyncEncryptStrings() {
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ // Test valid inputs for encryptString() and decryptString().
+ let inputs = [
+ "",
+ " ", // First printable latin1 character (code point 32).
+ "foo",
+ "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
+ "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
+ "aaa 一二三", // Includes Unicode with code points outside [0, 255].
+ ];
+
+ let encrypteds = await sdr.asyncEncryptStrings(inputs);
+ for (let i = 0; i < inputs.length; i++) {
+ let encrypted = encrypteds[i];
+ let input = inputs[i];
+ let converter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+
+ let convertedInput = converter.ConvertFromUnicode(input);
+ convertedInput += converter.Finish();
+ notEqual(
+ convertedInput,
+ encrypted,
+ "Encrypted input should not just be the input itself"
+ );
+
+ try {
+ atob(encrypted);
+ } catch (e) {
+ ok(false, `encryptString() should have returned Base64: ${e}`);
+ }
+
+ equal(
+ convertedInput,
+ sdr.decryptString(encrypted),
+ "decryptString(encryptString(input)) should return input"
+ );
+ }
+});
+
+add_task(async function testAsyncDecryptStrings() {
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ // Test valid inputs for encryptString() and decryptString().
+ let testCases = [
+ "",
+ " ", // First printable latin1 character (code point 32).
+ "foo",
+ "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
+ "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
+ "aaa 一二三", // Includes Unicode with code points outside [0, 255].
+ ];
+
+ let convertedTestCases = testCases.map(tc => {
+ let converter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+
+ let convertedInput = converter.ConvertFromUnicode(tc);
+ convertedInput += converter.Finish();
+ return convertedInput;
+ });
+
+ let encryptedStrings = convertedTestCases.map(tc => sdr.encryptString(tc));
+ let decrypteds = await sdr.asyncDecryptStrings(encryptedStrings);
+ for (let i = 0; i < encryptedStrings.length; i++) {
+ let decrypted = decrypteds[i];
+
+ equal(
+ decrypted,
+ testCases[i],
+ "decrypted string should match expected value"
+ );
+ equal(
+ sdr.decryptString(encryptedStrings[i]),
+ convertedTestCases[i],
+ "decryptString(encryptString(input)) should return the initial decrypted string value"
+ );
+ }
+});
+
+add_task(async function testAsyncDecryptInvalidStrings() {
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ // Test invalid inputs for sdr.asyncDecryptStrings
+ let testCases = [
+ "~bmV0cGxheQ==", // invalid base64 encoding
+ "bmV0cGxheQ==", // valid base64 characters but not encrypted
+ "https://www.example.com", // website address from erroneous migration
+ ];
+
+ let decrypteds = await sdr.asyncDecryptStrings(testCases);
+ equal(
+ decrypteds.length,
+ testCases.length,
+ "each testcase should still return a response"
+ );
+ for (let i = 0; i < decrypteds.length; i++) {
+ let decrypted = decrypteds[i];
+
+ equal(
+ decrypted,
+ "",
+ "decrypted string should be empty when trying to decrypt an invalid input with asyncDecryptStrings"
+ );
+
+ Assert.throws(
+ () => sdr.decryptString(testCases[i]),
+ /NS_ERROR_ILLEGAL_VALUE|NS_ERROR_FAILURE/,
+ `Check testcase would have thrown: ${testCases[i]}`
+ );
+ }
+});
+
+add_task(async function testAsyncDecryptLoggedOut() {
+ // Set a master password.
+ let token = Cc["@mozilla.org/security/pk11tokendb;1"]
+ .getService(Ci.nsIPK11TokenDB)
+ .getInternalKeyToken();
+ token.initPassword("password");
+ token.logoutSimple();
+
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ await Assert.rejects(
+ sdr.asyncDecryptStrings(["irrelevant"]),
+ /NS_ERROR_NOT_AVAILABLE/,
+ "Check error is thrown instead of returning empty strings"
+ );
+
+ token.reset();
+ token.initPassword("");
+});