tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_oskeystore.js (8982B)


      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";
      5 
      6 // Tests the methods and attributes for interfacing with nsIOSKeyStore.
      7 
      8 // Ensure that the appropriate initialization has happened.
      9 do_get_profile();
     10 
     11 const LABELS = ["mylabel1", "mylabel2", "mylabel3"];
     12 
     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  }
     26 }
     27 
     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  );
     36 
     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.");
     41 
     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  }
     50 
     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  }
     62 
     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  }
     70 }
     71 
     72 add_task(async function () {
     73  await delete_all_secrets();
     74  await encrypt_decrypt_test();
     75  await delete_all_secrets();
     76 });
     77 
     78 // Test that using a recovery phrase works.
     79 add_task(async function () {
     80  await delete_all_secrets();
     81 
     82  let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
     83    Ci.nsIOSKeyStore
     84  );
     85 
     86  let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
     87  ok(recoveryPhrase, "A recovery phrase should've been created.");
     88 
     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.");
     92 
     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    );
    103 
    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  );
    111 
    112  await delete_all_secrets();
    113 });
    114 
    115 // Test that trying to use a non-base64 recovery phrase fails.
    116 add_task(async function () {
    117  await delete_all_secrets();
    118 
    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"));
    128 
    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();
    139 });
    140 
    141 // Test that re-using a label overwrites any previously-stored secret.
    142 add_task(async function () {
    143  await delete_all_secrets();
    144 
    145  let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
    146    Ci.nsIOSKeyStore
    147  );
    148 
    149  let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
    150  ok(recoveryPhrase, "A recovery phrase should've been created.");
    151 
    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.");
    155 
    156  let newRecoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
    157  ok(newRecoveryPhrase, "A new recovery phrase should've been created.");
    158 
    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    );
    168 
    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  );
    176 
    177  await delete_all_secrets();
    178 });
    179 
    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();
    184 
    185  let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
    186    Ci.nsIOSKeyStore
    187  );
    188 
    189  let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
    190  ok(recoveryPhrase, "A recovery phrase should've been created.");
    191 
    192  let newRecoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
    193  ok(newRecoveryPhrase, "A new recovery phrase should've been created.");
    194 
    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.");
    198 
    199  await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
    200 
    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"));
    207 
    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  );
    215 
    216  await delete_all_secrets();
    217 });
    218 
    219 // Test that trying to use recovery phrases that are the wrong size fails.
    220 add_task(async function () {
    221  await delete_all_secrets();
    222 
    223  let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
    224    Ci.nsIOSKeyStore
    225  );
    226 
    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  );
    235 
    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  );
    248 
    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  );
    264 
    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  );
    274 
    275  await delete_all_secrets();
    276 });