summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/test_oskeystore.js
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/unit/test_oskeystore.js')
-rw-r--r--security/manager/ssl/tests/unit/test_oskeystore.js272
1 files changed, 272 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/unit/test_oskeystore.js b/security/manager/ssl/tests/unit/test_oskeystore.js
new file mode 100644
index 0000000000..fcc9de6c59
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_oskeystore.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 the methods and attributes for interfacing with nsIOSKeyStore.
+
+// Ensure that the appropriate initialization has happened.
+do_get_profile();
+
+const LABELS = ["mylabel1", "mylabel2", "mylabel3"];
+
+async function delete_all_secrets() {
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+ for (let label of LABELS) {
+ if (await keystore.asyncSecretAvailable(label)) {
+ await keystore.asyncDeleteSecret(label);
+ ok(
+ !(await keystore.asyncSecretAvailable(label)),
+ label + " should be deleted now."
+ );
+ }
+ }
+}
+
+async function encrypt_decrypt_test() {
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+ ok(
+ !(await keystore.asyncSecretAvailable(LABELS[0])),
+ "The secret should not be available yet."
+ );
+
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(recoveryPhrase, "A recovery phrase should've been created.");
+ let recoveryPhrase2 = await keystore.asyncGenerateSecret(LABELS[1]);
+ ok(recoveryPhrase2, "A recovery phrase should've been created.");
+
+ let text = new Uint8Array([0x01, 0x00, 0x01]);
+ let ciphertext = "";
+ try {
+ ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
+ ok(ciphertext, "We should have a ciphertext now.");
+ } catch (e) {
+ ok(false, "Error encrypting " + e);
+ }
+
+ // Decrypting should give us the plaintext bytes again.
+ try {
+ let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
+ ok(
+ plaintext.toString() == text.toString(),
+ "Decrypted plaintext should be the same as text."
+ );
+ } catch (e) {
+ ok(false, "Error decrypting ciphertext " + e);
+ }
+
+ // Decrypting with a wrong key should throw an error.
+ try {
+ await keystore.asyncDecryptBytes(LABELS[1], ciphertext);
+ ok(false, "Decrypting with the wrong key should fail.");
+ } catch (e) {
+ ok(true, "Decrypting with the wrong key should fail " + e);
+ }
+}
+
+add_task(async function () {
+ await delete_all_secrets();
+ await encrypt_decrypt_test();
+ await delete_all_secrets();
+});
+
+// Test that using a recovery phrase works.
+add_task(async function () {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(recoveryPhrase, "A recovery phrase should've been created.");
+
+ let text = new Uint8Array([0x01, 0x00, 0x01]);
+ let ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
+ ok(ciphertext, "We should have a ciphertext now.");
+
+ await keystore.asyncDeleteSecret(LABELS[0]);
+ // Decrypting should fail after deleting the secret.
+ await keystore
+ .asyncDecryptBytes(LABELS[0], ciphertext)
+ .then(() =>
+ ok(false, "decrypting didn't throw as expected after deleting the secret")
+ )
+ .catch(() =>
+ ok(true, "decrypting threw as expected after deleting the secret")
+ );
+
+ await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
+ let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
+ ok(
+ plaintext.toString() == text.toString(),
+ "Decrypted plaintext should be the same as text."
+ );
+
+ await delete_all_secrets();
+});
+
+// Test that trying to use a non-base64 recovery phrase fails.
+add_task(async function () {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+ await keystore
+ .asyncRecoverSecret(LABELS[0], "@##$^&*()#$^&*(@#%&*_")
+ .then(() =>
+ ok(false, "base64-decoding non-base64 should have failed but didn't")
+ )
+ .catch(() => ok(true, "base64-decoding non-base64 failed as expected"));
+
+ ok(
+ !(await keystore.asyncSecretAvailable(LABELS[0])),
+ "we didn't recover a secret, so the secret shouldn't be available"
+ );
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(
+ recoveryPhrase && !!recoveryPhrase.length,
+ "we should be able to re-use that label to generate a new secret"
+ );
+ await delete_all_secrets();
+});
+
+// Test that re-using a label overwrites any previously-stored secret.
+add_task(async function () {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(recoveryPhrase, "A recovery phrase should've been created.");
+
+ let text = new Uint8Array([0x66, 0x6f, 0x6f, 0x66]);
+ let ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
+ ok(ciphertext, "We should have a ciphertext now.");
+
+ let newRecoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(newRecoveryPhrase, "A new recovery phrase should've been created.");
+
+ // The new secret replaced the old one so we shouldn't be able to decrypt the ciphertext now.
+ await keystore
+ .asyncDecryptBytes(LABELS[0], ciphertext)
+ .then(() =>
+ ok(false, "decrypting without the original key should have failed")
+ )
+ .catch(() =>
+ ok(true, "decrypting without the original key failed as expected")
+ );
+
+ await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
+ let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
+ ok(
+ plaintext.toString() == text.toString(),
+ "Decrypted plaintext should be the same as text (once we have the original key again)."
+ );
+
+ await delete_all_secrets();
+});
+
+// Test that re-using a label (this time using a recovery phrase) overwrites any previously-stored
+// secret.
+add_task(async function () {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(recoveryPhrase, "A recovery phrase should've been created.");
+
+ let newRecoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(newRecoveryPhrase, "A new recovery phrase should've been created.");
+
+ let text = new Uint8Array([0x66, 0x6f, 0x6f, 0x66]);
+ let ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
+ ok(ciphertext, "We should have a ciphertext now.");
+
+ await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
+
+ // We recovered the old secret, so decrypting ciphertext that had been encrypted with the newer
+ // key should fail.
+ await keystore
+ .asyncDecryptBytes(LABELS[0], ciphertext)
+ .then(() => ok(false, "decrypting without the new key should have failed"))
+ .catch(() => ok(true, "decrypting without the new key failed as expected"));
+
+ await keystore.asyncRecoverSecret(LABELS[0], newRecoveryPhrase);
+ let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
+ ok(
+ plaintext.toString() == text.toString(),
+ "Decrypted plaintext should be the same as text (once we have the new key again)."
+ );
+
+ await delete_all_secrets();
+});
+
+// Test that trying to use recovery phrases that are the wrong size fails.
+add_task(async function () {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+
+ await keystore
+ .asyncRecoverSecret(LABELS[0], "")
+ .then(() => ok(false, "'recovering' with an empty key should have failed"))
+ .catch(() => ok(true, "'recovering' with an empty key failed as expected"));
+ ok(
+ !(await keystore.asyncSecretAvailable(LABELS[0])),
+ "we didn't recover a secret, so the secret shouldn't be available"
+ );
+
+ await keystore
+ .asyncRecoverSecret(LABELS[0], "AAAAAA")
+ .then(() =>
+ ok(false, "recovering with a key that is too short should have failed")
+ )
+ .catch(() =>
+ ok(true, "recovering with a key that is too short failed as expected")
+ );
+ ok(
+ !(await keystore.asyncSecretAvailable(LABELS[0])),
+ "we didn't recover a secret, so the secret shouldn't be available"
+ );
+
+ await keystore
+ .asyncRecoverSecret(
+ LABELS[0],
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ )
+ .then(() =>
+ ok(false, "recovering with a key that is too long should have failed")
+ )
+ .catch(() =>
+ ok(true, "recovering with a key that is too long failed as expected")
+ );
+ ok(
+ !(await keystore.asyncSecretAvailable(LABELS[0])),
+ "we didn't recover a secret, so the secret shouldn't be available"
+ );
+
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(
+ recoveryPhrase && !!recoveryPhrase.length,
+ "we should be able to use that label to generate a new secret"
+ );
+ ok(
+ await keystore.asyncSecretAvailable(LABELS[0]),
+ "the generated secret should now be available"
+ );
+
+ await delete_all_secrets();
+});