no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / security / manager / ssl / tests / unit / test_sdr.js
blob73c2219fc688059e9eaf7b1fd19f9dcdb2dcc0b5
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";
6 // Tests various aspects of the nsISecretDecoderRing implementation.
8 do_get_profile();
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".
19   },
21   QueryInterface: ChromeUtils.generateQI(["nsITokenPasswordDialogs"]),
24 let gMockPrompter = {
25   promptPassword() {
26     // Returning false simulates the user canceling the password prompt.
27     return false;
28   },
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",
43     gWindowWatcher
44   );
45   registerCleanupFunction(() => {
46     MockRegistrar.unregister(windowWatcherCID);
47   });
48 });
50 add_task(function testEncryptString() {
51   let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
52     Ci.nsISecretDecoderRing
53   );
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";
70     let convertedInput = converter.ConvertFromUnicode(input);
71     convertedInput += converter.Finish();
73     let encrypted = sdr.encryptString(convertedInput);
75     notEqual(
76       convertedInput,
77       encrypted,
78       "Encrypted input should not just be the input itself"
79     );
81     try {
82       atob(encrypted);
83     } catch (e) {
84       ok(false, `encryptString() should have returned Base64: ${e}`);
85     }
87     equal(
88       convertedInput,
89       sdr.decryptString(encrypted),
90       "decryptString(encryptString(input)) should return input"
91     );
92   }
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   );
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     });
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   }
127 add_task(async function testAsyncEncryptStrings() {
128   let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
129     Ci.nsISecretDecoderRing
130   );
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   ];
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";
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     );
159     try {
160       atob(encrypted);
161     } catch (e) {
162       ok(false, `encryptString() should have returned Base64: ${e}`);
163     }
165     equal(
166       convertedInput,
167       sdr.decryptString(encrypted),
168       "decryptString(encryptString(input)) should return input"
169     );
170   }
173 add_task(async function testAsyncDecryptStrings() {
174   let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
175     Ci.nsISecretDecoderRing
176   );
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   ];
188   let convertedTestCases = testCases.map(tc => {
189     let converter = Cc[
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;
197   });
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];
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   }
217 add_task(async function testAsyncDecryptInvalidStrings() {
218   let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
219     Ci.nsISecretDecoderRing
220   );
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   ];
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];
238     equal(
239       decrypted,
240       "",
241       "decrypted string should be empty when trying to decrypt an invalid input with asyncDecryptStrings"
242     );
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   }
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
262   );
264   await Assert.rejects(
265     sdr.asyncDecryptStrings(["irrelevant"]),
266     /NS_ERROR_NOT_AVAILABLE/,
267     "Check error is thrown instead of returning empty strings"
268   );
270   token.reset();
271   token.initPassword("");