summaryrefslogtreecommitdiffstats
path: root/services/fxaccounts/tests/xpcshell/test_keys.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/fxaccounts/tests/xpcshell/test_keys.js')
-rw-r--r--services/fxaccounts/tests/xpcshell/test_keys.js252
1 files changed, 252 insertions, 0 deletions
diff --git a/services/fxaccounts/tests/xpcshell/test_keys.js b/services/fxaccounts/tests/xpcshell/test_keys.js
new file mode 100644
index 0000000000..f984ba5deb
--- /dev/null
+++ b/services/fxaccounts/tests/xpcshell/test_keys.js
@@ -0,0 +1,252 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { FX_OAUTH_CLIENT_ID } = ChromeUtils.import(
+ "resource://gre/modules/FxAccountsCommon.js"
+);
+const { FxAccountsKeys } = ChromeUtils.import(
+ "resource://gre/modules/FxAccountsKeys.jsm"
+);
+
+// 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(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/
+ );
+});
+
+add_task(
+ async function test_graceful_handling_of_missing_ecosystem_telemetry_scope() {
+ const mockClient = {
+ getScopedKeyData: sinon
+ .mock()
+ .once()
+ .withExactArgs(
+ "session-token",
+ FX_OAUTH_CLIENT_ID,
+ SCOPE_OLD_SYNC + " " + SCOPE_ECOSYSTEM_TELEMETRY
+ )
+ .returns(
+ Promise.resolve({
+ // N.B. no SCOPE_ECOSYSTEM_TELEMETRY entry.
+ [SCOPE_OLD_SYNC]: {
+ identifier: SCOPE_OLD_SYNC,
+ keyRotationTimestamp: 1234567890000,
+ keyRotationSecret:
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ },
+ })
+ ),
+ accountKeys: sinon
+ .mock()
+ .once()
+ .withExactArgs("key-fetch-token")
+ .returns({
+ wrapKB: "00000000000000000000000000000000",
+ }),
+ };
+ const mockState = {
+ getUserAccountData: async () => {
+ return {
+ uid: "aeaa1725c7a24ff983c6295725d5fc9b",
+ sessionToken: "session-token",
+ keyFetchToken: "key-fetch-token",
+ unwrapBKey:
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ };
+ },
+ updateUserAccountData: sinon.spy(async data => {
+ return data;
+ }),
+ };
+
+ const keys = new FxAccountsKeys({ fxAccountsClient: mockClient });
+ await keys._fetchAndUnwrapAndDeriveKeys(
+ mockState,
+ "session-token",
+ "key-fetch-token"
+ );
+
+ Assert.ok(mockState.updateUserAccountData.calledOnce);
+ const userData = mockState.updateUserAccountData.firstCall.args[0];
+
+ Assert.ok(userData.kSync);
+ Assert.ok(userData.scopedKeys[SCOPE_OLD_SYNC]);
+ Assert.equal(userData.ecosystemUserId, null);
+ Assert.equal(userData.scopedKeys[SCOPE_ECOSYSTEM_TELEMETRY], null);
+
+ mockClient.getScopedKeyData.verify();
+ mockClient.accountKeys.verify();
+ }
+);