/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { FxAccountsKeys } = ChromeUtils.importESModule( "resource://gre/modules/FxAccountsKeys.sys.mjs" ); // Ref https://github.com/mozilla/fxa-crypto-relier/ for the details // of these test vectors. add_task(async function test_derive_scoped_key_test_vector() { const keys = new FxAccountsKeys(null); const uid = "aeaa1725c7a24ff983c6295725d5fc9b"; const kB = "8b2e1303e21eee06a945683b8d495b9bf079ca30baa37eb8392d9ffa4767be45"; const scopedKeyMetadata = { identifier: "app_key:https%3A//example.com", keyRotationTimestamp: 1510726317000, keyRotationSecret: "517d478cb4f994aa69930416648a416fdaa1762c5abf401a2acf11a0f185e98d", }; const scopedKey = await keys._deriveScopedKey( uid, CommonUtils.hexToBytes(kB), "app_key", scopedKeyMetadata ); Assert.deepEqual(scopedKey, { kty: "oct", kid: "1510726317-Voc-Eb9IpoTINuo9ll7bjA", k: "Kkbk1_Q0oCcTmggeDH6880bQrxin2RLu5D00NcJazdQ", }); }); add_task(async function test_derive_legacy_sync_key_test_vector() { const keys = new FxAccountsKeys(null); const uid = "aeaa1725c7a24ff983c6295725d5fc9b"; const kB = "eaf9570b7219a4187d3d6bf3cec2770c2e0719b7cc0dfbb38243d6f1881675e9"; const scopedKeyMetadata = { identifier: "https://identity.mozilla.com/apps/oldsync", keyRotationTimestamp: 1510726317123, keyRotationSecret: "0000000000000000000000000000000000000000000000000000000000000000", }; const scopedKey = await keys._deriveLegacyScopedKey( uid, CommonUtils.hexToBytes(kB), "https://identity.mozilla.com/apps/oldsync", scopedKeyMetadata ); Assert.deepEqual(scopedKey, { kty: "oct", kid: "1510726317123-IqQv4onc7VcVE1kTQkyyOw", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", }); }); add_task(async function test_derive_multiple_keys_at_once() { const keys = new FxAccountsKeys(null); const uid = "aeaa1725c7a24ff983c6295725d5fc9b"; const kB = "eaf9570b7219a4187d3d6bf3cec2770c2e0719b7cc0dfbb38243d6f1881675e9"; const scopedKeysMetadata = { app_key: { identifier: "app_key:https%3A//example.com", keyRotationTimestamp: 1510726317000, keyRotationSecret: "517d478cb4f994aa69930416648a416fdaa1762c5abf401a2acf11a0f185e98d", }, "https://identity.mozilla.com/apps/oldsync": { identifier: "https://identity.mozilla.com/apps/oldsync", keyRotationTimestamp: 1510726318123, keyRotationSecret: "0000000000000000000000000000000000000000000000000000000000000000", }, }; const scopedKeys = await keys._deriveScopedKeys( uid, CommonUtils.hexToBytes(kB), scopedKeysMetadata ); Assert.deepEqual(scopedKeys, { app_key: { kty: "oct", kid: "1510726317-tUkxiR1lTlFrTgkF0tJidA", k: "TYK6Hmj86PfKiqsk9DZmX61nxk9VsExGrwo94HP-0wU", }, "https://identity.mozilla.com/apps/oldsync": { kty: "oct", kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", }, }); }); add_task(function test_check_valid_scoped_keys() { const keys = new FxAccountsKeys(null); add_task(function test_missing_key_data() { const scopedKeys = { "https://identity.mozilla.com/apps/oldsync": { kty: "oct", kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", scope: "https://identity.mozilla.com/apps/oldsync", }, }; Assert.equal(keys.validScopedKeys(scopedKeys), false); }); add_task(function test_unexpected_scope() { const scopedKeys = { "https://identity.mozilla.com/apps/oldsync": { kty: "oct", kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", scope: "UnexpectedScope", }, }; Assert.equal(keys.validScopedKeys(scopedKeys), false); }); add_task(function test_not_oct_key() { const scopedKeys = { "https://identity.mozilla.com/apps/oldsync": { // Should be "oct"! kty: "EC", kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", scope: "https://identity.mozilla.com/apps/oldsync", }, }; Assert.equal(keys.validScopedKeys(scopedKeys), false); }); add_task(function test_invalid_kid_not_timestamp() { const scopedKeys = { "https://identity.mozilla.com/apps/oldsync": { kty: "oct", // Does not have the timestamp! kid: "IqQv4onc7VcVE1kTQkyyOw", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", scope: "https://identity.mozilla.com/apps/oldsync", }, }; Assert.equal(keys.validScopedKeys(scopedKeys), false); }); add_task(function test_invalid_kid_not_valid_timestamp() { const scopedKeys = { "https://identity.mozilla.com/apps/oldsync": { kty: "oct", // foo is not a valid timestamp! kid: "foo-IqQv4onc7VcVE1kTQkyyOw", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", scope: "https://identity.mozilla.com/apps/oldsync", }, }; Assert.equal(keys.validScopedKeys(scopedKeys), false); }); add_task(function test_invalid_kid_not_b64_fingerprint() { const scopedKeys = { "https://identity.mozilla.com/apps/oldsync": { kty: "oct", // fingerprint not a valid base64 encoded string. kid: "1510726318123-notvalidb64][", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", scope: "https://identity.mozilla.com/apps/oldsync", }, }; Assert.equal(keys.validScopedKeys(scopedKeys), false); }); add_task(function test_invalid_k_not_base64() { const scopedKeys = { "https://identity.mozilla.com/apps/oldsync": { kty: "oct", kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", k: "notavalidb64[]", scope: "https://identity.mozilla.com/apps/oldsync", }, }; Assert.equal(keys.validScopedKeys(scopedKeys), false); }); add_task(function test_multiple_scoped_keys_one_invalid() { const scopedKeys = { // Valid "https://identity.mozilla.com/apps/otherscope": { kty: "oct", kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", scope: "https://identity.mozilla.com/apps/otherscope", }, // Invalid "https://identity.mozilla.com/apps/oldsync": { kty: "oct", kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", k: "notavalidb64[]", scope: "https://identity.mozilla.com/apps/oldsync", }, }; Assert.equal(keys.validScopedKeys(scopedKeys), false); }); add_task(function test_valid_scopedkeys() { const scopedKeys = { "https://identity.mozilla.com/apps/oldsync": { kty: "oct", kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", scope: "https://identity.mozilla.com/apps/oldsync", }, "https://identity.mozilla.com/apps/otherscope": { kty: "oct", kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", scope: "https://identity.mozilla.com/apps/otherscope", }, }; Assert.equal(keys.validScopedKeys(scopedKeys), true); }); add_task(function test_valid_kid_with_dash() { const scopedKeys = { "https://identity.mozilla.com/apps/oldsync": { kty: "oct", // kid contains another dash. The fingerprint must not be truncated. kid: "1510726318123-I-Qv4onc7VcVE1kTQkyyOw", k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang", scope: "https://identity.mozilla.com/apps/oldsync", }, }; Assert.equal(keys.validScopedKeys(scopedKeys), true); }); }); add_task(async function test_rejects_bad_scoped_key_data() { const keys = new FxAccountsKeys(null); const uid = "aeaa1725c7a24ff983c6295725d5fc9b"; const kB = "8b2e1303e21eee06a945683b8d495b9bf079ca30baa37eb8392d9ffa4767be45"; const scopedKeyMetadata = { identifier: "app_key:https%3A//example.com", keyRotationTimestamp: 1510726317000, keyRotationSecret: "517d478cb4f994aa69930416648a416fdaa1762c5abf401a2acf11a0f185e98d", }; await Assert.rejects( keys._deriveScopedKey( uid.slice(0, -1), CommonUtils.hexToBytes(kB), "app_key", scopedKeyMetadata ), /uid must be a 32-character hex string/ ); await Assert.rejects( keys._deriveScopedKey( uid.slice(0, -1) + "Q", CommonUtils.hexToBytes(kB), "app_key", scopedKeyMetadata ), /uid must be a 32-character hex string/ ); await Assert.rejects( keys._deriveScopedKey( uid, CommonUtils.hexToBytes(kB).slice(0, -1), "app_key", scopedKeyMetadata ), /kBbytes must be exactly 32 bytes/ ); await Assert.rejects( keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { ...scopedKeyMetadata, identifier: "foo", }), /identifier must be a string of length >= 10/ ); await Assert.rejects( keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { ...scopedKeyMetadata, identifier: {}, }), /identifier must be a string of length >= 10/ ); await Assert.rejects( keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { ...scopedKeyMetadata, keyRotationTimestamp: "xyz", }), /keyRotationTimestamp must be a number/ ); await Assert.rejects( keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { ...scopedKeyMetadata, keyRotationTimestamp: 12345, }), /keyRotationTimestamp must round to a 10-digit number/ ); await Assert.rejects( keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { ...scopedKeyMetadata, keyRotationSecret: scopedKeyMetadata.keyRotationSecret.slice(0, -1), }), /keyRotationSecret must be a 64-character hex string/ ); await Assert.rejects( keys._deriveScopedKey(uid, CommonUtils.hexToBytes(kB), "app_key", { ...scopedKeyMetadata, keyRotationSecret: scopedKeyMetadata.keyRotationSecret.slice(0, -1) + "z", }), /keyRotationSecret must be a 64-character hex string/ ); });