test_keys.js (10216B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { FxAccountsKeys } = ChromeUtils.importESModule( 7 "resource://gre/modules/FxAccountsKeys.sys.mjs" 8 ); 9 10 // Ref https://github.com/mozilla/fxa-crypto-relier/ for the details 11 // of these test vectors. 12 13 add_task(async function test_derive_scoped_key_test_vector() { 14 const keys = new FxAccountsKeys(null); 15 const uid = "aeaa1725c7a24ff983c6295725d5fc9b"; 16 const kB = "8b2e1303e21eee06a945683b8d495b9bf079ca30baa37eb8392d9ffa4767be45"; 17 const scopedKeyMetadata = { 18 identifier: "app_key:https%3A//example.com", 19 keyRotationTimestamp: 1510726317000, 20 keyRotationSecret: 21 "517d478cb4f994aa69930416648a416fdaa1762c5abf401a2acf11a0f185e98d", 22 }; 23 24 const scopedKey = await keys._deriveScopedKey( 25 uid, 26 CommonUtils.hexToBytes(kB), 27 "app_key", 28 scopedKeyMetadata 29 ); 30 31 Assert.deepEqual(scopedKey, { 32 kty: "oct", 33 kid: "1510726317-Voc-Eb9IpoTINuo9ll7bjA", 34 k: "Kkbk1_Q0oCcTmggeDH6880bQrxin2RLu5D00NcJazdQ", 35 }); 36 }); 37 38 add_task(async function test_derive_legacy_sync_key_test_vector() { 39 const keys = new FxAccountsKeys(null); 40 const uid = "aeaa1725c7a24ff983c6295725d5fc9b"; 41 const kB = "eaf9570b7219a4187d3d6bf3cec2770c2e0719b7cc0dfbb38243d6f1881675e9"; 42 const scopedKeyMetadata = { 43 identifier: SCOPE_APP_SYNC, 44 keyRotationTimestamp: 1510726317123, 45 keyRotationSecret: 46 "0000000000000000000000000000000000000000000000000000000000000000", 47 }; 48 49 const scopedKey = await keys._deriveLegacyScopedKey( 50 uid, 51 CommonUtils.hexToBytes(kB), 52 SCOPE_APP_SYNC, 53 scopedKeyMetadata 54 ); 55 56 Assert.deepEqual(scopedKey, { 57 kty: "oct", 58 kid: "1510726317123-IqQv4onc7VcVE1kTQkyyOw", 59 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 60 }); 61 }); 62 63 add_task(async function test_derive_multiple_keys_at_once() { 64 const keys = new FxAccountsKeys(null); 65 const uid = "aeaa1725c7a24ff983c6295725d5fc9b"; 66 const kB = "eaf9570b7219a4187d3d6bf3cec2770c2e0719b7cc0dfbb38243d6f1881675e9"; 67 const scopedKeysMetadata = { 68 app_key: { 69 identifier: "app_key:https%3A//example.com", 70 keyRotationTimestamp: 1510726317000, 71 keyRotationSecret: 72 "517d478cb4f994aa69930416648a416fdaa1762c5abf401a2acf11a0f185e98d", 73 }, 74 [SCOPE_APP_SYNC]: { 75 identifier: SCOPE_APP_SYNC, 76 keyRotationTimestamp: 1510726318123, 77 keyRotationSecret: 78 "0000000000000000000000000000000000000000000000000000000000000000", 79 }, 80 }; 81 82 const scopedKeys = await keys._deriveScopedKeys( 83 uid, 84 CommonUtils.hexToBytes(kB), 85 scopedKeysMetadata 86 ); 87 88 Assert.deepEqual(scopedKeys, { 89 app_key: { 90 kty: "oct", 91 kid: "1510726317-tUkxiR1lTlFrTgkF0tJidA", 92 k: "TYK6Hmj86PfKiqsk9DZmX61nxk9VsExGrwo94HP-0wU", 93 }, 94 [SCOPE_APP_SYNC]: { 95 kty: "oct", 96 kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", 97 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 98 }, 99 }); 100 }); 101 102 add_task(function test_check_valid_scoped_keys() { 103 const keys = new FxAccountsKeys(null); 104 add_task(function test_missing_key_data() { 105 const scopedKeys = { 106 [SCOPE_APP_SYNC]: { 107 kty: "oct", 108 kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", 109 scope: SCOPE_APP_SYNC, 110 }, 111 }; 112 Assert.equal(keys.validScopedKeys(scopedKeys), false); 113 }); 114 add_task(function test_unexpected_scope() { 115 const scopedKeys = { 116 [SCOPE_APP_SYNC]: { 117 kty: "oct", 118 kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", 119 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 120 scope: "UnexpectedScope", 121 }, 122 }; 123 Assert.equal(keys.validScopedKeys(scopedKeys), false); 124 }); 125 add_task(function test_not_oct_key() { 126 const scopedKeys = { 127 [SCOPE_APP_SYNC]: { 128 // Should be "oct"! 129 kty: "EC", 130 kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", 131 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 132 scope: SCOPE_APP_SYNC, 133 }, 134 }; 135 Assert.equal(keys.validScopedKeys(scopedKeys), false); 136 }); 137 add_task(function test_invalid_kid_not_timestamp() { 138 const scopedKeys = { 139 [SCOPE_APP_SYNC]: { 140 kty: "oct", 141 // Does not have the timestamp! 142 kid: "IqQv4onc7VcVE1kTQkyyOw", 143 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 144 scope: SCOPE_APP_SYNC, 145 }, 146 }; 147 Assert.equal(keys.validScopedKeys(scopedKeys), false); 148 }); 149 add_task(function test_invalid_kid_not_valid_timestamp() { 150 const scopedKeys = { 151 [SCOPE_APP_SYNC]: { 152 kty: "oct", 153 // foo is not a valid timestamp! 154 kid: "foo-IqQv4onc7VcVE1kTQkyyOw", 155 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 156 scope: SCOPE_APP_SYNC, 157 }, 158 }; 159 Assert.equal(keys.validScopedKeys(scopedKeys), false); 160 }); 161 add_task(function test_invalid_kid_not_b64_fingerprint() { 162 const scopedKeys = { 163 [SCOPE_APP_SYNC]: { 164 kty: "oct", 165 // fingerprint not a valid base64 encoded string. 166 kid: "1510726318123-notvalidb64][", 167 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 168 scope: SCOPE_APP_SYNC, 169 }, 170 }; 171 Assert.equal(keys.validScopedKeys(scopedKeys), false); 172 }); 173 add_task(function test_invalid_k_not_base64() { 174 const scopedKeys = { 175 [SCOPE_APP_SYNC]: { 176 kty: "oct", 177 kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", 178 k: "notavalidb64[]", 179 scope: SCOPE_APP_SYNC, 180 }, 181 }; 182 Assert.equal(keys.validScopedKeys(scopedKeys), false); 183 }); 184 185 add_task(function test_multiple_scoped_keys_one_invalid() { 186 const scopedKeys = { 187 // Valid 188 "https://identity.mozilla.com/apps/otherscope": { 189 kty: "oct", 190 kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", 191 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 192 scope: "https://identity.mozilla.com/apps/otherscope", 193 }, 194 // Invalid 195 [SCOPE_APP_SYNC]: { 196 kty: "oct", 197 kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", 198 k: "notavalidb64[]", 199 scope: SCOPE_APP_SYNC, 200 }, 201 }; 202 Assert.equal(keys.validScopedKeys(scopedKeys), false); 203 }); 204 205 add_task(function test_valid_scopedkeys() { 206 const scopedKeys = { 207 [SCOPE_APP_SYNC]: { 208 kty: "oct", 209 kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", 210 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 211 scope: SCOPE_APP_SYNC, 212 }, 213 "https://identity.mozilla.com/apps/otherscope": { 214 kty: "oct", 215 kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", 216 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 217 scope: "https://identity.mozilla.com/apps/otherscope", 218 }, 219 }; 220 Assert.equal(keys.validScopedKeys(scopedKeys), true); 221 }); 222 add_task(function test_valid_kid_with_dash() { 223 const scopedKeys = { 224 "https://identity.mozilla.com/apps/oldsync": { 225 kty: "oct", 226 // kid contains another dash. The fingerprint must not be truncated. 227 kid: "1510726318123-I-Qv4onc7VcVE1kTQkyyOw", 228 k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", 229 scope: "https://identity.mozilla.com/apps/oldsync", 230 }, 231 }; 232 Assert.equal(keys.validScopedKeys(scopedKeys), true); 233 }); 234 }); 235 236 add_task(async function test_rejects_bad_scoped_key_data() { 237 const keys = new FxAccountsKeys(null); 238 const uid = "aeaa1725c7a24ff983c6295725d5fc9b"; 239 const kB = "8b2e1303e21eee06a945683b8d495b9bf079ca30baa37eb8392d9ffa4767be45"; 240 const scopedKeyMetadata = { 241 identifier: "app_key:https%3A//example.com", 242 keyRotationTimestamp: 1510726317000, 243 keyRotationSecret: 244 "517d478cb4f994aa69930416648a416fdaa1762c5abf401a2acf11a0f185e98d", 245 }; 246 247 await Assert.rejects( 248 keys._deriveScopedKey( 249 uid.slice(0, -1), 250 CommonUtils.hexToBytes(kB), 251 "app_key", 252 scopedKeyMetadata 253 ), 254 /uid must be a 32-character hex string/ 255 ); 256 await Assert.rejects( 257 keys._deriveScopedKey( 258 uid.slice(0, -1) + "Q", 259 CommonUtils.hexToBytes(kB), 260 "app_key", 261 scopedKeyMetadata 262 ), 263 /uid must be a 32-character hex string/ 264 ); 265 await Assert.rejects( 266 keys._deriveScopedKey( 267 uid, 268 CommonUtils.hexToBytes(kB).slice(0, -1), 269 "app_key", 270 scopedKeyMetadata 271 ), 272 /kBbytes must be exactly 32 bytes/ 273 ); 274 await Assert.rejects( 275 keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { 276 ...scopedKeyMetadata, 277 identifier: "foo", 278 }), 279 /identifier must be a string of length >= 10/ 280 ); 281 await Assert.rejects( 282 keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { 283 ...scopedKeyMetadata, 284 identifier: {}, 285 }), 286 /identifier must be a string of length >= 10/ 287 ); 288 await Assert.rejects( 289 keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { 290 ...scopedKeyMetadata, 291 keyRotationTimestamp: "xyz", 292 }), 293 /keyRotationTimestamp must be a number/ 294 ); 295 await Assert.rejects( 296 keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { 297 ...scopedKeyMetadata, 298 keyRotationTimestamp: 12345, 299 }), 300 /keyRotationTimestamp must round to a 10-digit number/ 301 ); 302 await Assert.rejects( 303 keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { 304 ...scopedKeyMetadata, 305 keyRotationSecret: scopedKeyMetadata.keyRotationSecret.slice(0, -1), 306 }), 307 /keyRotationSecret must be a 64-character hex string/ 308 ); 309 await Assert.rejects( 310 keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { 311 ...scopedKeyMetadata, 312 keyRotationSecret: scopedKeyMetadata.keyRotationSecret.slice(0, -1) + "z", 313 }), 314 /keyRotationSecret must be a 64-character hex string/ 315 ); 316 });