316 lines
10 KiB
JavaScript
316 lines
10 KiB
JavaScript
/* 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: SCOPE_APP_SYNC,
|
|
keyRotationTimestamp: 1510726317123,
|
|
keyRotationSecret:
|
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
};
|
|
|
|
const scopedKey = await keys._deriveLegacyScopedKey(
|
|
uid,
|
|
CommonUtils.hexToBytes(kB),
|
|
SCOPE_APP_SYNC,
|
|
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",
|
|
},
|
|
[SCOPE_APP_SYNC]: {
|
|
identifier: SCOPE_APP_SYNC,
|
|
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",
|
|
},
|
|
[SCOPE_APP_SYNC]: {
|
|
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 = {
|
|
[SCOPE_APP_SYNC]: {
|
|
kty: "oct",
|
|
kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
|
|
scope: SCOPE_APP_SYNC,
|
|
},
|
|
};
|
|
Assert.equal(keys.validScopedKeys(scopedKeys), false);
|
|
});
|
|
add_task(function test_unexpected_scope() {
|
|
const scopedKeys = {
|
|
[SCOPE_APP_SYNC]: {
|
|
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 = {
|
|
[SCOPE_APP_SYNC]: {
|
|
// Should be "oct"!
|
|
kty: "EC",
|
|
kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
|
|
k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
|
|
scope: SCOPE_APP_SYNC,
|
|
},
|
|
};
|
|
Assert.equal(keys.validScopedKeys(scopedKeys), false);
|
|
});
|
|
add_task(function test_invalid_kid_not_timestamp() {
|
|
const scopedKeys = {
|
|
[SCOPE_APP_SYNC]: {
|
|
kty: "oct",
|
|
// Does not have the timestamp!
|
|
kid: "IqQv4onc7VcVE1kTQkyyOw",
|
|
k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
|
|
scope: SCOPE_APP_SYNC,
|
|
},
|
|
};
|
|
Assert.equal(keys.validScopedKeys(scopedKeys), false);
|
|
});
|
|
add_task(function test_invalid_kid_not_valid_timestamp() {
|
|
const scopedKeys = {
|
|
[SCOPE_APP_SYNC]: {
|
|
kty: "oct",
|
|
// foo is not a valid timestamp!
|
|
kid: "foo-IqQv4onc7VcVE1kTQkyyOw",
|
|
k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
|
|
scope: SCOPE_APP_SYNC,
|
|
},
|
|
};
|
|
Assert.equal(keys.validScopedKeys(scopedKeys), false);
|
|
});
|
|
add_task(function test_invalid_kid_not_b64_fingerprint() {
|
|
const scopedKeys = {
|
|
[SCOPE_APP_SYNC]: {
|
|
kty: "oct",
|
|
// fingerprint not a valid base64 encoded string.
|
|
kid: "1510726318123-notvalidb64][",
|
|
k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
|
|
scope: SCOPE_APP_SYNC,
|
|
},
|
|
};
|
|
Assert.equal(keys.validScopedKeys(scopedKeys), false);
|
|
});
|
|
add_task(function test_invalid_k_not_base64() {
|
|
const scopedKeys = {
|
|
[SCOPE_APP_SYNC]: {
|
|
kty: "oct",
|
|
kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
|
|
k: "notavalidb64[]",
|
|
scope: SCOPE_APP_SYNC,
|
|
},
|
|
};
|
|
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
|
|
[SCOPE_APP_SYNC]: {
|
|
kty: "oct",
|
|
kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
|
|
k: "notavalidb64[]",
|
|
scope: SCOPE_APP_SYNC,
|
|
},
|
|
};
|
|
Assert.equal(keys.validScopedKeys(scopedKeys), false);
|
|
});
|
|
|
|
add_task(function test_valid_scopedkeys() {
|
|
const scopedKeys = {
|
|
[SCOPE_APP_SYNC]: {
|
|
kty: "oct",
|
|
kid: "1510726318123-IqQv4onc7VcVE1kTQkyyOw",
|
|
k: "DW_ll5GwX6SJ5GPqJVAuMUP2t6kDqhUulc2cbt26xbTcaKGQl-9l29FHAQ7kUiJETma4s9fIpEHrt909zgFang",
|
|
scope: SCOPE_APP_SYNC,
|
|
},
|
|
"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/
|
|
);
|
|
});
|