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 });