no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / security / manager / ssl / tests / unit / test_oskeystore.js
blob9efcaa69a6c8571618c0c609a58ba71600b0e438
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 the methods and attributes for interfacing with nsIOSKeyStore.
8 // Ensure that the appropriate initialization has happened.
9 do_get_profile();
11 const LABELS = ["mylabel1", "mylabel2", "mylabel3"];
13 async function delete_all_secrets() {
14   let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
15     Ci.nsIOSKeyStore
16   );
17   for (let label of LABELS) {
18     if (await keystore.asyncSecretAvailable(label)) {
19       await keystore.asyncDeleteSecret(label);
20       ok(
21         !(await keystore.asyncSecretAvailable(label)),
22         label + " should be deleted now."
23       );
24     }
25   }
28 async function encrypt_decrypt_test() {
29   let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
30     Ci.nsIOSKeyStore
31   );
32   ok(
33     !(await keystore.asyncSecretAvailable(LABELS[0])),
34     "The secret should not be available yet."
35   );
37   let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
38   ok(recoveryPhrase, "A recovery phrase should've been created.");
39   let recoveryPhrase2 = await keystore.asyncGenerateSecret(LABELS[1]);
40   ok(recoveryPhrase2, "A recovery phrase should've been created.");
42   let text = new Uint8Array([0x01, 0x00, 0x01]);
43   let ciphertext = "";
44   try {
45     ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
46     ok(ciphertext, "We should have a ciphertext now.");
47   } catch (e) {
48     ok(false, "Error encrypting " + e);
49   }
51   // Decrypting should give us the plaintext bytes again.
52   try {
53     let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
54     Assert.equal(
55       plaintext.toString(),
56       text.toString(),
57       "Decrypted plaintext should be the same as text."
58     );
59   } catch (e) {
60     ok(false, "Error decrypting ciphertext " + e);
61   }
63   // Decrypting with a wrong key should throw an error.
64   try {
65     await keystore.asyncDecryptBytes(LABELS[1], ciphertext);
66     ok(false, "Decrypting with the wrong key should fail.");
67   } catch (e) {
68     ok(true, "Decrypting with the wrong key should fail " + e);
69   }
72 add_task(async function () {
73   await delete_all_secrets();
74   await encrypt_decrypt_test();
75   await delete_all_secrets();
76 });
78 // Test that using a recovery phrase works.
79 add_task(async function () {
80   await delete_all_secrets();
82   let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
83     Ci.nsIOSKeyStore
84   );
86   let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
87   ok(recoveryPhrase, "A recovery phrase should've been created.");
89   let text = new Uint8Array([0x01, 0x00, 0x01]);
90   let ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
91   ok(ciphertext, "We should have a ciphertext now.");
93   await keystore.asyncDeleteSecret(LABELS[0]);
94   // Decrypting should fail after deleting the secret.
95   await keystore
96     .asyncDecryptBytes(LABELS[0], ciphertext)
97     .then(() =>
98       ok(false, "decrypting didn't throw as expected after deleting the secret")
99     )
100     .catch(() =>
101       ok(true, "decrypting threw as expected after deleting the secret")
102     );
104   await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
105   let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
106   Assert.equal(
107     plaintext.toString(),
108     text.toString(),
109     "Decrypted plaintext should be the same as text."
110   );
112   await delete_all_secrets();
115 // Test that trying to use a non-base64 recovery phrase fails.
116 add_task(async function () {
117   await delete_all_secrets();
119   let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
120     Ci.nsIOSKeyStore
121   );
122   await keystore
123     .asyncRecoverSecret(LABELS[0], "@##$^&*()#$^&*(@#%&*_")
124     .then(() =>
125       ok(false, "base64-decoding non-base64 should have failed but didn't")
126     )
127     .catch(() => ok(true, "base64-decoding non-base64 failed as expected"));
129   ok(
130     !(await keystore.asyncSecretAvailable(LABELS[0])),
131     "we didn't recover a secret, so the secret shouldn't be available"
132   );
133   let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
134   ok(
135     recoveryPhrase && !!recoveryPhrase.length,
136     "we should be able to re-use that label to generate a new secret"
137   );
138   await delete_all_secrets();
141 // Test that re-using a label overwrites any previously-stored secret.
142 add_task(async function () {
143   await delete_all_secrets();
145   let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
146     Ci.nsIOSKeyStore
147   );
149   let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
150   ok(recoveryPhrase, "A recovery phrase should've been created.");
152   let text = new Uint8Array([0x66, 0x6f, 0x6f, 0x66]);
153   let ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
154   ok(ciphertext, "We should have a ciphertext now.");
156   let newRecoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
157   ok(newRecoveryPhrase, "A new recovery phrase should've been created.");
159   // The new secret replaced the old one so we shouldn't be able to decrypt the ciphertext now.
160   await keystore
161     .asyncDecryptBytes(LABELS[0], ciphertext)
162     .then(() =>
163       ok(false, "decrypting without the original key should have failed")
164     )
165     .catch(() =>
166       ok(true, "decrypting without the original key failed as expected")
167     );
169   await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
170   let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
171   Assert.equal(
172     plaintext.toString(),
173     text.toString(),
174     "Decrypted plaintext should be the same as text (once we have the original key again)."
175   );
177   await delete_all_secrets();
180 // Test that re-using a label (this time using a recovery phrase) overwrites any previously-stored
181 // secret.
182 add_task(async function () {
183   await delete_all_secrets();
185   let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
186     Ci.nsIOSKeyStore
187   );
189   let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
190   ok(recoveryPhrase, "A recovery phrase should've been created.");
192   let newRecoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
193   ok(newRecoveryPhrase, "A new recovery phrase should've been created.");
195   let text = new Uint8Array([0x66, 0x6f, 0x6f, 0x66]);
196   let ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
197   ok(ciphertext, "We should have a ciphertext now.");
199   await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
201   // We recovered the old secret, so decrypting ciphertext that had been encrypted with the newer
202   // key should fail.
203   await keystore
204     .asyncDecryptBytes(LABELS[0], ciphertext)
205     .then(() => ok(false, "decrypting without the new key should have failed"))
206     .catch(() => ok(true, "decrypting without the new key failed as expected"));
208   await keystore.asyncRecoverSecret(LABELS[0], newRecoveryPhrase);
209   let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
210   Assert.equal(
211     plaintext.toString(),
212     text.toString(),
213     "Decrypted plaintext should be the same as text (once we have the new key again)."
214   );
216   await delete_all_secrets();
219 // Test that trying to use recovery phrases that are the wrong size fails.
220 add_task(async function () {
221   await delete_all_secrets();
223   let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
224     Ci.nsIOSKeyStore
225   );
227   await keystore
228     .asyncRecoverSecret(LABELS[0], "")
229     .then(() => ok(false, "'recovering' with an empty key should have failed"))
230     .catch(() => ok(true, "'recovering' with an empty key failed as expected"));
231   ok(
232     !(await keystore.asyncSecretAvailable(LABELS[0])),
233     "we didn't recover a secret, so the secret shouldn't be available"
234   );
236   await keystore
237     .asyncRecoverSecret(LABELS[0], "AAAAAA")
238     .then(() =>
239       ok(false, "recovering with a key that is too short should have failed")
240     )
241     .catch(() =>
242       ok(true, "recovering with a key that is too short failed as expected")
243     );
244   ok(
245     !(await keystore.asyncSecretAvailable(LABELS[0])),
246     "we didn't recover a secret, so the secret shouldn't be available"
247   );
249   await keystore
250     .asyncRecoverSecret(
251       LABELS[0],
252       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
253     )
254     .then(() =>
255       ok(false, "recovering with a key that is too long should have failed")
256     )
257     .catch(() =>
258       ok(true, "recovering with a key that is too long failed as expected")
259     );
260   ok(
261     !(await keystore.asyncSecretAvailable(LABELS[0])),
262     "we didn't recover a secret, so the secret shouldn't be available"
263   );
265   let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
266   ok(
267     recoveryPhrase && !!recoveryPhrase.length,
268     "we should be able to use that label to generate a new secret"
269   );
270   ok(
271     await keystore.asyncSecretAvailable(LABELS[0]),
272     "the generated secret should now be available"
273   );
275   await delete_all_secrets();