forked from mirrors/gecko-dev
Bug 1838710: Removed old Sync key migrations. r=markh
Differential Revision: https://phabricator.services.mozilla.com/D181473
This commit is contained in:
parent
da7273bd10
commit
59bace8c22
2 changed files with 47 additions and 145 deletions
|
|
@ -95,18 +95,6 @@ export class FxAccountsKeys {
|
|||
return true;
|
||||
}
|
||||
|
||||
// For sync-related scopes, we might have stored the keys in a legacy format.
|
||||
if (scope == SCOPE_OLD_SYNC) {
|
||||
if (userData.kSync && userData.kXCS) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// `kB` is deprecated, but if we have it, we can use it to derive any scoped key.
|
||||
if (userData.kB) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we have a `keyFetchToken` we can fetch `kB`.
|
||||
if (userData.keyFetchToken) {
|
||||
return true;
|
||||
|
|
@ -252,7 +240,7 @@ export class FxAccountsKeys {
|
|||
*/
|
||||
async _migrateOrFetchKeys(currentState, userData) {
|
||||
// If the required scopes are present in `scopedKeys`, then we know that we've
|
||||
// previously applied all the other migrations below
|
||||
// previously applied all earlier migrations
|
||||
// so we are safe to delete deprecated fields that older migrations
|
||||
// might have depended on.
|
||||
if (
|
||||
|
|
@ -264,42 +252,6 @@ export class FxAccountsKeys {
|
|||
return this._removeDeprecatedKeys(currentState, userData);
|
||||
}
|
||||
|
||||
// Bug 1661407 - migrate from legacy storage of keys as top-level account
|
||||
// data fields, to storing them as scoped keys in the `scopedKeys` object.
|
||||
if (
|
||||
LEGACY_DERIVED_KEYS_NAMES.every(name => userData.hasOwnProperty(name))
|
||||
) {
|
||||
log.info("Migrating from legacy key fields to scopedKeys.");
|
||||
const scopedKeys = userData.scopedKeys || {};
|
||||
await currentState.updateUserAccountData({
|
||||
scopedKeys: {
|
||||
...scopedKeys,
|
||||
...(await this._deriveScopedKeysFromAccountData(userData)),
|
||||
},
|
||||
});
|
||||
userData = await currentState.getUserAccountData();
|
||||
return userData;
|
||||
}
|
||||
// Bug 1426306 - Migrate from kB to derived keys.
|
||||
if (userData.kB) {
|
||||
log.info("Migrating kB to derived keys.");
|
||||
const { uid, kB, sessionToken } = userData;
|
||||
const scopedKeysMetadata = await this._fetchScopedKeysMetadata(
|
||||
sessionToken
|
||||
);
|
||||
await currentState.updateUserAccountData({
|
||||
uid,
|
||||
...(await this._deriveKeys(
|
||||
uid,
|
||||
CommonUtils.hexToBytes(kB),
|
||||
scopedKeysMetadata
|
||||
)),
|
||||
kA: null, // Remove kA and kB from storage.
|
||||
kB: null,
|
||||
});
|
||||
userData = await currentState.getUserAccountData();
|
||||
return userData;
|
||||
}
|
||||
// Otherwise, we need to fetch from the network and unwrap.
|
||||
if (!userData.sessionToken) {
|
||||
throw new Error("No sessionToken");
|
||||
|
|
@ -541,44 +493,6 @@ export class FxAccountsKeys {
|
|||
return scopedKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the `scopedKeys` data field based on current account data.
|
||||
*
|
||||
* This is a backwards-compatibility convenience for users who are already signed in to Firefox
|
||||
* and have legacy fields like `kSync` and `kXCS` in their top-level account data, but do not have
|
||||
* the newer `scopedKeys` field. We populate it with the scoped keys for sync.
|
||||
*
|
||||
*/
|
||||
async _deriveScopedKeysFromAccountData(userData) {
|
||||
const scopedKeysMetadata = await this._fetchScopedKeysMetadata(
|
||||
userData.sessionToken
|
||||
);
|
||||
const scopedKeys = userData.scopedKeys || {};
|
||||
for (const scope of LEGACY_DERIVED_KEY_SCOPES) {
|
||||
if (scopedKeysMetadata.hasOwnProperty(scope)) {
|
||||
let kid, key;
|
||||
if (scope == SCOPE_OLD_SYNC) {
|
||||
({ kXCS: kid, kSync: key } = userData);
|
||||
} else {
|
||||
// Should never happen, but a nice internal consistency check.
|
||||
throw new Error(`Unexpected legacy key-bearing scope: ${scope}`);
|
||||
}
|
||||
if (!kid || !key) {
|
||||
throw new Error(
|
||||
`Account is missing legacy key fields for scope: ${scope}`
|
||||
);
|
||||
}
|
||||
scopedKeys[scope] = await this._formatLegacyScopedKey(
|
||||
CommonUtils.hexToArrayBuffer(kid),
|
||||
CommonUtils.hexToArrayBuffer(key),
|
||||
scope,
|
||||
scopedKeysMetadata[scope]
|
||||
);
|
||||
}
|
||||
}
|
||||
return scopedKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a scoped key for an individual OAuth scope.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -701,59 +701,6 @@ add_test(function test_getKeyForScope() {
|
|||
});
|
||||
});
|
||||
|
||||
add_task(async function test_getKeyForScope_kb_migration() {
|
||||
let fxa = new MockFxAccounts();
|
||||
let user = getTestUser("eusebius");
|
||||
|
||||
user.verified = true;
|
||||
// Set-up the deprecated set of keys.
|
||||
user.kA = "e0245ab7f10e483470388e0a28f0a03379a3b417174fb2b42feab158b4ac2dbd";
|
||||
user.kB = "eaf9570b7219a4187d3d6bf3cec2770c2e0719b7cc0dfbb38243d6f1881675e9";
|
||||
|
||||
await fxa.setSignedInUser(user);
|
||||
await fxa.keys.getKeyForScope(SCOPE_OLD_SYNC);
|
||||
let newUser = await fxa._internal.getUserAccountData();
|
||||
Assert.equal(newUser.kA, null);
|
||||
Assert.equal(newUser.kB, null);
|
||||
Assert.deepEqual(newUser.scopedKeys, {
|
||||
"https://identity.mozilla.com/apps/oldsync": {
|
||||
kid: "1234567890123-IqQv4onc7VcVE1kTQkyyOw",
|
||||
k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
|
||||
kty: "oct",
|
||||
},
|
||||
});
|
||||
// These hex values were manually confirmed to be equivalent to the b64 values above.
|
||||
Assert.equal(
|
||||
newUser.kSync,
|
||||
"0d6fe59791b05fa489e463ea25502e3143f6b7a903aa152e95cd9c6eddbac5b4" +
|
||||
"dc68a19097ef65dbd147010ee45222444e66b8b3d7c8a441ebb7dd3dce015a9e"
|
||||
);
|
||||
Assert.equal(newUser.kXCS, "22a42fe289dced5715135913424cb23b");
|
||||
});
|
||||
|
||||
add_task(async function test_getKeyForScope_scopedKeys_migration() {
|
||||
let fxa = new MockFxAccounts();
|
||||
let user = getTestUser("eusebius");
|
||||
|
||||
user.verified = true;
|
||||
// Set-up the keys in deprecated fields.
|
||||
user.kSync = MOCK_ACCOUNT_KEYS.kSync;
|
||||
user.kXCS = MOCK_ACCOUNT_KEYS.kXCS;
|
||||
Assert.equal(user.scopedKeys, null);
|
||||
|
||||
await fxa.setSignedInUser(user);
|
||||
await fxa.keys.getKeyForScope(SCOPE_OLD_SYNC);
|
||||
let newUser = await fxa._internal.getUserAccountData();
|
||||
Assert.equal(newUser.kA, null);
|
||||
Assert.equal(newUser.kB, null);
|
||||
// It should have correctly formatted the corresponding scoped keys.
|
||||
const expectedScopedKeys = { ...MOCK_ACCOUNT_KEYS.scopedKeys };
|
||||
Assert.deepEqual(newUser.scopedKeys, expectedScopedKeys);
|
||||
// And left the existing key fields unchanged.
|
||||
Assert.equal(newUser.kSync, user.kSync);
|
||||
Assert.equal(newUser.kXCS, user.kXCS);
|
||||
});
|
||||
|
||||
add_task(
|
||||
async function test_getKeyForScope_scopedKeys_migration_removes_deprecated_high_level_keys() {
|
||||
let fxa = new MockFxAccounts();
|
||||
|
|
@ -886,8 +833,8 @@ add_task(async function test_getKeyForScope_invalid_token() {
|
|||
await fxa._internal.abortExistingFlow();
|
||||
});
|
||||
|
||||
// This is the exact same test vectors as
|
||||
// https://github.com/mozilla/fxa-crypto-relier/blob/f94f441159029a645a474d4b6439c38308da0bb0/test/deriver/ScopedKeys.js#L58
|
||||
// Test vectors from
|
||||
// https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol#Test_Vectors
|
||||
add_task(async function test_getKeyForScope_oldsync() {
|
||||
let fxa = new MockFxAccounts();
|
||||
let client = fxa._internal.fxAccountsClient;
|
||||
|
|
@ -900,22 +847,63 @@ add_task(async function test_getKeyForScope_oldsync() {
|
|||
keyRotationTimestamp: 1510726317123,
|
||||
},
|
||||
});
|
||||
|
||||
// We mock the server returning the wrapKB from our test vectors
|
||||
client.accountKeys = async () => {
|
||||
return {
|
||||
wrapKB: CommonUtils.hexToBytes(
|
||||
"404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
// We set the user to have the keyFetchToken and unwrapBKey from our test vectors
|
||||
let user = {
|
||||
...getTestUser("eusebius"),
|
||||
uid: "aeaa1725c7a24ff983c6295725d5fc9b",
|
||||
kB: "eaf9570b7219a4187d3d6bf3cec2770c2e0719b7cc0dfbb38243d6f1881675e9",
|
||||
keyFetchToken:
|
||||
"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
|
||||
unwrapBKey:
|
||||
"6ea660be9c89ec355397f89afb282ea0bf21095760c8c5009bbcc894155bbe2a",
|
||||
sessionToken: "mock session token, used in metadata request",
|
||||
verified: true,
|
||||
};
|
||||
await fxa.setSignedInUser(user);
|
||||
|
||||
// We derive, persist and return the sync key
|
||||
const key = await fxa.keys.getKeyForScope(SCOPE_OLD_SYNC);
|
||||
|
||||
// We verify the key returned matches what we would expect from the test vectors
|
||||
// kb = 2ee722fdd8ccaa721bdeb2d1b76560efef705b04349d9357c3e592cf4906e075 (from test vectors)
|
||||
//
|
||||
// kid can be verified by "${keyRotationTimestamp}-${sha256(kb)[0:16]}"
|
||||
//
|
||||
// k can be verified by HKDF(kb, undefined, "identity.mozilla.com/picl/v1/oldsync", 64)
|
||||
Assert.deepEqual(key, {
|
||||
scope: SCOPE_OLD_SYNC,
|
||||
kid: "1510726317123-IqQv4onc7VcVE1kTQkyyOw",
|
||||
k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
|
||||
kid: "1510726317123-BAik7hEOdpGnPZnPBSdaTg",
|
||||
k: "fwM5VZu0Spf5XcFRZYX2zk6MrqZP7zvovCBcvuKwgYMif3hz98FHmIVa3qjKjrW0J244Zj-P5oWaOcQbvypmpw",
|
||||
kty: "oct",
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_getScopedKeys_cached_key() {
|
||||
let fxa = new MockFxAccounts();
|
||||
let user = {
|
||||
...getTestUser("eusebius"),
|
||||
uid: "aeaa1725c7a24ff983c6295725d5fc9b",
|
||||
verified: true,
|
||||
...MOCK_ACCOUNT_KEYS,
|
||||
};
|
||||
|
||||
await fxa.setSignedInUser(user);
|
||||
let key = await fxa.keys.getKeyForScope(SCOPE_OLD_SYNC);
|
||||
Assert.deepEqual(key, {
|
||||
scope: SCOPE_OLD_SYNC,
|
||||
...MOCK_ACCOUNT_KEYS.scopedKeys[SCOPE_OLD_SYNC],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_getScopedKeys_unavailable_scope() {
|
||||
let fxa = new MockFxAccounts();
|
||||
let user = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue