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/
6 // Tests various aspects of the nsISecretDecoderRing implementation.
10 let gSetPasswordShownCount = 0;
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".
21 QueryInterface: ChromeUtils.generateQI(["nsITokenPasswordDialogs"]),
26 // Returning false simulates the user canceling the password prompt.
30 QueryInterface: ChromeUtils.generateQI(["nsIPrompt"]),
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"]),
40 add_task(function setup() {
41 let windowWatcherCID = MockRegistrar.register(
42 "@mozilla.org/embedcomp/window-watcher;1",
45 registerCleanupFunction(() => {
46 MockRegistrar.unregister(windowWatcherCID);
50 add_task(function testEncryptString() {
51 let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
52 Ci.nsISecretDecoderRing
55 // Test valid inputs for encryptString() and decryptString().
58 " ", // First printable latin1 character (code point 32).
60 "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
61 "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
62 "aaa 一二三", // Includes Unicode with code points outside [0, 255].
64 for (let input of inputs) {
66 "@mozilla.org/intl/scriptableunicodeconverter"
67 ].createInstance(Ci.nsIScriptableUnicodeConverter);
68 converter.charset = "UTF-8";
70 let convertedInput = converter.ConvertFromUnicode(input);
71 convertedInput += converter.Finish();
73 let encrypted = sdr.encryptString(convertedInput);
78 "Encrypted input should not just be the input itself"
84 ok(false, `encryptString() should have returned Base64: ${e}`);
89 sdr.decryptString(encrypted),
90 "decryptString(encryptString(input)) should return input"
94 // Test invalid inputs for decryptString().
96 () => sdr.decryptString("*"),
97 /NS_ERROR_ILLEGAL_VALUE/,
98 "decryptString() should throw if given non-Base64 input"
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
109 registerCleanupFunction(() => {
110 MockRegistrar.unregister(tokenPasswordDialogsCID);
114 gSetPasswordShownCount,
116 "changePassword() dialog should have been shown zero times"
118 sdr.changePassword();
120 gSetPasswordShownCount,
122 "changePassword() dialog should have been shown exactly once"
127 add_task(async function testAsyncEncryptStrings() {
128 let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
129 Ci.nsISecretDecoderRing
132 // Test valid inputs for encryptString() and decryptString().
135 " ", // First printable latin1 character (code point 32).
137 "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
138 "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
139 "aaa 一二三", // Includes Unicode with code points outside [0, 255].
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];
147 "@mozilla.org/intl/scriptableunicodeconverter"
148 ].createInstance(Ci.nsIScriptableUnicodeConverter);
149 converter.charset = "UTF-8";
151 let convertedInput = converter.ConvertFromUnicode(input);
152 convertedInput += converter.Finish();
156 "Encrypted input should not just be the input itself"
162 ok(false, `encryptString() should have returned Base64: ${e}`);
167 sdr.decryptString(encrypted),
168 "decryptString(encryptString(input)) should return input"
173 add_task(async function testAsyncDecryptStrings() {
174 let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
175 Ci.nsISecretDecoderRing
178 // Test valid inputs for encryptString() and decryptString().
181 " ", // First printable latin1 character (code point 32).
183 "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
184 "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
185 "aaa 一二三", // Includes Unicode with code points outside [0, 255].
188 let convertedTestCases = testCases.map(tc => {
190 "@mozilla.org/intl/scriptableunicodeconverter"
191 ].createInstance(Ci.nsIScriptableUnicodeConverter);
192 converter.charset = "UTF-8";
194 let convertedInput = converter.ConvertFromUnicode(tc);
195 convertedInput += converter.Finish();
196 return convertedInput;
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];
207 "decrypted string should match expected value"
210 sdr.decryptString(encryptedStrings[i]),
211 convertedTestCases[i],
212 "decryptString(encryptString(input)) should return the initial decrypted string value"
217 add_task(async function testAsyncDecryptInvalidStrings() {
218 let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
219 Ci.nsISecretDecoderRing
222 // Test invalid inputs for sdr.asyncDecryptStrings
224 "~bmV0cGxheQ==", // invalid base64 encoding
225 "bmV0cGxheQ==", // valid base64 characters but not encrypted
226 "https://www.example.com", // website address from erroneous migration
229 let decrypteds = await sdr.asyncDecryptStrings(testCases);
233 "each testcase should still return a response"
235 for (let i = 0; i < decrypteds.length; i++) {
236 let decrypted = decrypteds[i];
241 "decrypted string should be empty when trying to decrypt an invalid input with asyncDecryptStrings"
245 () => sdr.decryptString(testCases[i]),
246 /NS_ERROR_ILLEGAL_VALUE|NS_ERROR_FAILURE/,
247 `Check testcase would have thrown: ${testCases[i]}`
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();
260 let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
261 Ci.nsISecretDecoderRing
264 await Assert.rejects(
265 sdr.asyncDecryptStrings(["irrelevant"]),
266 /NS_ERROR_NOT_AVAILABLE/,
267 "Check error is thrown instead of returning empty strings"
271 token.initPassword("");