1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-05 00:42:10 +03:00

Convert more of js-sdk crypto and fix underscored field accesses

This commit is contained in:
Michael Telatynski
2021-06-23 14:47:25 +01:00
parent 6017fead19
commit 5a8299f1a5
27 changed files with 789 additions and 744 deletions

View File

@@ -165,7 +165,7 @@ describe("DeviceList management:", function() {
aliceTestClient.httpBackend.flush('/keys/query', 1).then( aliceTestClient.httpBackend.flush('/keys/query', 1).then(
() => aliceTestClient.httpBackend.flush('/send/', 1), () => aliceTestClient.httpBackend.flush('/send/', 1),
), ),
aliceTestClient.client.crypto._deviceList.saveIfDirty(), aliceTestClient.client.crypto.deviceList.saveIfDirty(),
]); ]);
}).then(() => { }).then(() => {
aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => { aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => {
@@ -202,7 +202,7 @@ describe("DeviceList management:", function() {
return aliceTestClient.httpBackend.flush('/keys/query', 1); return aliceTestClient.httpBackend.flush('/keys/query', 1);
}).then((flushed) => { }).then((flushed) => {
expect(flushed).toEqual(0); expect(flushed).toEqual(0);
return aliceTestClient.client.crypto._deviceList.saveIfDirty(); return aliceTestClient.client.crypto.deviceList.saveIfDirty();
}).then(() => { }).then(() => {
aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => { aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => {
const bobStat = data.trackingStatus['@bob:xyz']; const bobStat = data.trackingStatus['@bob:xyz'];
@@ -235,7 +235,7 @@ describe("DeviceList management:", function() {
// wait for the client to stop processing the response // wait for the client to stop processing the response
return aliceTestClient.client.downloadKeys(['@bob:xyz']); return aliceTestClient.client.downloadKeys(['@bob:xyz']);
}).then(() => { }).then(() => {
return aliceTestClient.client.crypto._deviceList.saveIfDirty(); return aliceTestClient.client.crypto.deviceList.saveIfDirty();
}).then(() => { }).then(() => {
aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => { aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => {
const bobStat = data.trackingStatus['@bob:xyz']; const bobStat = data.trackingStatus['@bob:xyz'];
@@ -256,7 +256,7 @@ describe("DeviceList management:", function() {
// wait for the client to stop processing the response // wait for the client to stop processing the response
return aliceTestClient.client.downloadKeys(['@chris:abc']); return aliceTestClient.client.downloadKeys(['@chris:abc']);
}).then(() => { }).then(() => {
return aliceTestClient.client.crypto._deviceList.saveIfDirty(); return aliceTestClient.client.crypto.deviceList.saveIfDirty();
}).then(() => { }).then(() => {
aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => { aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => {
const bobStat = data.trackingStatus['@bob:xyz']; const bobStat = data.trackingStatus['@bob:xyz'];
@@ -286,7 +286,7 @@ describe("DeviceList management:", function() {
}, },
); );
await aliceTestClient.httpBackend.flush('/keys/query', 1); await aliceTestClient.httpBackend.flush('/keys/query', 1);
await aliceTestClient.client.crypto._deviceList.saveIfDirty(); await aliceTestClient.client.crypto.deviceList.saveIfDirty();
aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => { aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => {
const bobStat = data.trackingStatus['@bob:xyz']; const bobStat = data.trackingStatus['@bob:xyz'];
@@ -322,7 +322,7 @@ describe("DeviceList management:", function() {
); );
await aliceTestClient.flushSync(); await aliceTestClient.flushSync();
await aliceTestClient.client.crypto._deviceList.saveIfDirty(); await aliceTestClient.client.crypto.deviceList.saveIfDirty();
aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => { aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => {
const bobStat = data.trackingStatus['@bob:xyz']; const bobStat = data.trackingStatus['@bob:xyz'];
@@ -358,7 +358,7 @@ describe("DeviceList management:", function() {
); );
await aliceTestClient.flushSync(); await aliceTestClient.flushSync();
await aliceTestClient.client.crypto._deviceList.saveIfDirty(); await aliceTestClient.client.crypto.deviceList.saveIfDirty();
aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => { aliceTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => {
const bobStat = data.trackingStatus['@bob:xyz']; const bobStat = data.trackingStatus['@bob:xyz'];
@@ -379,7 +379,7 @@ describe("DeviceList management:", function() {
anotherTestClient.httpBackend.when('GET', '/sync').respond( anotherTestClient.httpBackend.when('GET', '/sync').respond(
200, getSyncResponse([])); 200, getSyncResponse([]));
await anotherTestClient.flushSync(); await anotherTestClient.flushSync();
await anotherTestClient.client.crypto._deviceList.saveIfDirty(); await anotherTestClient.client.crypto.deviceList.saveIfDirty();
anotherTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => { anotherTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => {
const bobStat = data.trackingStatus['@bob:xyz']; const bobStat = data.trackingStatus['@bob:xyz'];

View File

@@ -159,7 +159,7 @@ function aliDownloadsKeys() {
// check that the localStorage is updated as we expect (not sure this is // check that the localStorage is updated as we expect (not sure this is
// an integration test, but meh) // an integration test, but meh)
return Promise.all([p1, p2]).then(() => { return Promise.all([p1, p2]).then(() => {
return aliTestClient.client.crypto._deviceList.saveIfDirty(); return aliTestClient.client.crypto.deviceList.saveIfDirty();
}).then(() => { }).then(() => {
aliTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => { aliTestClient.cryptoStore.getEndToEndDeviceData(null, (data) => {
const devices = data.devices[bobUserId]; const devices = data.devices[bobUserId];

View File

@@ -336,7 +336,7 @@ describe("MatrixClient", function() {
var b = JSON.parse(JSON.stringify(o)); var b = JSON.parse(JSON.stringify(o));
delete(b.signatures); delete(b.signatures);
delete(b.unsigned); delete(b.unsigned);
return client.crypto._olmDevice.sign(anotherjson.stringify(b)); return client.crypto.olmDevice.sign(anotherjson.stringify(b));
}; };
logger.log("Ed25519: " + ed25519key); logger.log("Ed25519: " + ed25519key);

View File

@@ -1013,7 +1013,7 @@ describe("megolm", function() {
event: true, event: true,
}); });
event.senderCurve25519Key = testSenderKey; event.senderCurve25519Key = testSenderKey;
return testClient.client.crypto._onRoomKeyEvent(event); return testClient.client.crypto.onRoomKeyEvent(event);
}).then(() => { }).then(() => {
const event = testUtils.mkEvent({ const event = testUtils.mkEvent({
event: true, event: true,

View File

@@ -65,7 +65,7 @@ describe("Crypto", function() {
'YmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmI'; 'YmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmI';
device.keys["ed25519:FLIBBLE"] = device.keys["ed25519:FLIBBLE"] =
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
client.crypto._deviceList.getDeviceByIdentityKey = () => device; client.crypto.deviceList.getDeviceByIdentityKey = () => device;
encryptionInfo = client.getEventEncryptionInfo(event); encryptionInfo = client.getEventEncryptionInfo(event);
expect(encryptionInfo.encrypted).toBeTruthy(); expect(encryptionInfo.encrypted).toBeTruthy();
@@ -213,7 +213,7 @@ describe("Crypto", function() {
async function keyshareEventForEvent(event, index) { async function keyshareEventForEvent(event, index) {
const eventContent = event.getWireContent(); const eventContent = event.getWireContent();
const key = await aliceClient.crypto._olmDevice const key = await aliceClient.crypto.olmDevice
.getInboundGroupSessionKey( .getInboundGroupSessionKey(
roomId, eventContent.sender_key, eventContent.session_id, roomId, eventContent.sender_key, eventContent.session_id,
index, index,
@@ -285,7 +285,7 @@ describe("Crypto", function() {
} }
})); }));
const bobDecryptor = bobClient.crypto._getRoomDecryptor( const bobDecryptor = bobClient.crypto.getRoomDecryptor(
roomId, olmlib.MEGOLM_ALGORITHM, roomId, olmlib.MEGOLM_ALGORITHM,
); );
@@ -377,7 +377,7 @@ describe("Crypto", function() {
// key requests get queued until the sync has finished, but we don't // key requests get queued until the sync has finished, but we don't
// let the client set up enough for that to happen, so gut-wrench a bit // let the client set up enough for that to happen, so gut-wrench a bit
// to force it to send now. // to force it to send now.
aliceClient.crypto._outgoingRoomKeyRequestManager.sendQueuedRequests(); aliceClient.crypto.outgoingRoomKeyRequestManager.sendQueuedRequests();
jest.runAllTimers(); jest.runAllTimers();
await Promise.resolve(); await Promise.resolve();
expect(aliceClient.sendToDevice).toBeCalledTimes(1); expect(aliceClient.sendToDevice).toBeCalledTimes(1);

View File

@@ -365,9 +365,9 @@ describe("MegolmDecryption", function() {
bobClient1.initCrypto(), bobClient1.initCrypto(),
bobClient2.initCrypto(), bobClient2.initCrypto(),
]); ]);
const aliceDevice = aliceClient.crypto._olmDevice; const aliceDevice = aliceClient.crypto.olmDevice;
const bobDevice1 = bobClient1.crypto._olmDevice; const bobDevice1 = bobClient1.crypto.olmDevice;
const bobDevice2 = bobClient2.crypto._olmDevice; const bobDevice2 = bobClient2.crypto.olmDevice;
const encryptionCfg = { const encryptionCfg = {
"algorithm": "m.megolm.v1.aes-sha2", "algorithm": "m.megolm.v1.aes-sha2",
@@ -404,10 +404,10 @@ describe("MegolmDecryption", function() {
}, },
}; };
aliceClient.crypto._deviceList.storeDevicesForUser( aliceClient.crypto.deviceList.storeDevicesForUser(
"@bob:example.com", BOB_DEVICES, "@bob:example.com", BOB_DEVICES,
); );
aliceClient.crypto._deviceList.downloadKeys = async function(userIds) { aliceClient.crypto.deviceList.downloadKeys = async function(userIds) {
return this._getDevicesFromStore(userIds); return this._getDevicesFromStore(userIds);
}; };
@@ -468,8 +468,8 @@ describe("MegolmDecryption", function() {
aliceClient.initCrypto(), aliceClient.initCrypto(),
bobClient.initCrypto(), bobClient.initCrypto(),
]); ]);
const aliceDevice = aliceClient.crypto._olmDevice; const aliceDevice = aliceClient.crypto.olmDevice;
const bobDevice = bobClient.crypto._olmDevice; const bobDevice = bobClient.crypto.olmDevice;
const encryptionCfg = { const encryptionCfg = {
"algorithm": "m.megolm.v1.aes-sha2", "algorithm": "m.megolm.v1.aes-sha2",
@@ -508,10 +508,10 @@ describe("MegolmDecryption", function() {
}, },
}; };
aliceClient.crypto._deviceList.storeDevicesForUser( aliceClient.crypto.deviceList.storeDevicesForUser(
"@bob:example.com", BOB_DEVICES, "@bob:example.com", BOB_DEVICES,
); );
aliceClient.crypto._deviceList.downloadKeys = async function(userIds) { aliceClient.crypto.deviceList.downloadKeys = async function(userIds) {
return this._getDevicesFromStore(userIds); return this._getDevicesFromStore(userIds);
}; };
@@ -561,11 +561,11 @@ describe("MegolmDecryption", function() {
aliceClient.initCrypto(), aliceClient.initCrypto(),
bobClient.initCrypto(), bobClient.initCrypto(),
]); ]);
const bobDevice = bobClient.crypto._olmDevice; const bobDevice = bobClient.crypto.olmDevice;
const roomId = "!someroom"; const roomId = "!someroom";
aliceClient.crypto._onToDeviceEvent(new MatrixEvent({ aliceClient.crypto.onToDeviceEvent(new MatrixEvent({
type: "org.matrix.room_key.withheld", type: "org.matrix.room_key.withheld",
sender: "@bob:example.com", sender: "@bob:example.com",
content: { content: {
@@ -605,13 +605,13 @@ describe("MegolmDecryption", function() {
bobClient.initCrypto(), bobClient.initCrypto(),
]); ]);
aliceClient.crypto.downloadKeys = async () => {}; aliceClient.crypto.downloadKeys = async () => {};
const bobDevice = bobClient.crypto._olmDevice; const bobDevice = bobClient.crypto.olmDevice;
const roomId = "!someroom"; const roomId = "!someroom";
const now = Date.now(); const now = Date.now();
aliceClient.crypto._onToDeviceEvent(new MatrixEvent({ aliceClient.crypto.onToDeviceEvent(new MatrixEvent({
type: "org.matrix.room_key.withheld", type: "org.matrix.room_key.withheld",
sender: "@bob:example.com", sender: "@bob:example.com",
content: { content: {
@@ -655,7 +655,7 @@ describe("MegolmDecryption", function() {
aliceClient.initCrypto(), aliceClient.initCrypto(),
bobClient.initCrypto(), bobClient.initCrypto(),
]); ]);
const bobDevice = bobClient.crypto._olmDevice; const bobDevice = bobClient.crypto.olmDevice;
aliceClient.crypto.downloadKeys = async () => {}; aliceClient.crypto.downloadKeys = async () => {};
const roomId = "!someroom"; const roomId = "!someroom";
@@ -663,7 +663,7 @@ describe("MegolmDecryption", function() {
const now = Date.now(); const now = Date.now();
// pretend we got an event that we can't decrypt // pretend we got an event that we can't decrypt
aliceClient.crypto._onToDeviceEvent(new MatrixEvent({ aliceClient.crypto.onToDeviceEvent(new MatrixEvent({
type: "m.room.encrypted", type: "m.room.encrypted",
sender: "@bob:example.com", sender: "@bob:example.com",
content: { content: {

View File

@@ -296,7 +296,7 @@ describe("MegolmBackup", function() {
resolve(); resolve();
return Promise.resolve({}); return Promise.resolve({});
}; };
client.crypto._backupManager.backupGroupSession( client.crypto.backupManager.backupGroupSession(
"F0Q2NmyJNgUVj9DGsb4ZQt3aVxhVcUQhg7+gvW0oyKI", "F0Q2NmyJNgUVj9DGsb4ZQt3aVxhVcUQhg7+gvW0oyKI",
groupSession.session_id(), groupSession.session_id(),
); );
@@ -478,7 +478,7 @@ describe("MegolmBackup", function() {
); );
} }
}; };
client.crypto._backupManager.backupGroupSession( client.crypto.backupManager.backupGroupSession(
"F0Q2NmyJNgUVj9DGsb4ZQt3aVxhVcUQhg7+gvW0oyKI", "F0Q2NmyJNgUVj9DGsb4ZQt3aVxhVcUQhg7+gvW0oyKI",
groupSession.session_id(), groupSession.session_id(),
); );

View File

@@ -64,8 +64,8 @@ describe("Cross Signing", function() {
); );
alice.uploadDeviceSigningKeys = jest.fn(async (auth, keys) => { alice.uploadDeviceSigningKeys = jest.fn(async (auth, keys) => {
await olmlib.verifySignature( await olmlib.verifySignature(
alice.crypto._olmDevice, keys.master_key, "@alice:example.com", alice.crypto.olmDevice, keys.master_key, "@alice:example.com",
"Osborne2", alice.crypto._olmDevice.deviceEd25519Key, "Osborne2", alice.crypto.olmDevice.deviceEd25519Key,
); );
}); });
alice.uploadKeySignatures = async () => {}; alice.uploadKeySignatures = async () => {};
@@ -138,7 +138,7 @@ describe("Cross Signing", function() {
// set Alice's cross-signing key // set Alice's cross-signing key
await resetCrossSigningKeys(alice); await resetCrossSigningKeys(alice);
// Alice downloads Bob's device key // Alice downloads Bob's device key
alice.crypto._deviceList.storeCrossSigningForUser("@bob:example.com", { alice.crypto.deviceList.storeCrossSigningForUser("@bob:example.com", {
keys: { keys: {
master: { master: {
user_id: "@bob:example.com", user_id: "@bob:example.com",
@@ -203,12 +203,12 @@ describe("Cross Signing", function() {
alice.uploadKeySignatures = jest.fn(async (content) => { alice.uploadKeySignatures = jest.fn(async (content) => {
try { try {
await olmlib.verifySignature( await olmlib.verifySignature(
alice.crypto._olmDevice, alice.crypto.olmDevice,
content["@alice:example.com"][ content["@alice:example.com"][
"nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk" "nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk"
], ],
"@alice:example.com", "@alice:example.com",
"Osborne2", alice.crypto._olmDevice.deviceEd25519Key, "Osborne2", alice.crypto.olmDevice.deviceEd25519Key,
); );
olmlib.pkVerify( olmlib.pkVerify(
content["@alice:example.com"]["Osborne2"], content["@alice:example.com"]["Osborne2"],
@@ -222,7 +222,7 @@ describe("Cross Signing", function() {
}); });
}); });
const deviceInfo = alice.crypto._deviceList._devices["@alice:example.com"] const deviceInfo = alice.crypto.deviceList._devices["@alice:example.com"]
.Osborne2; .Osborne2;
const aliceDevice = { const aliceDevice = {
user_id: "@alice:example.com", user_id: "@alice:example.com",
@@ -230,7 +230,7 @@ describe("Cross Signing", function() {
}; };
aliceDevice.keys = deviceInfo.keys; aliceDevice.keys = deviceInfo.keys;
aliceDevice.algorithms = deviceInfo.algorithms; aliceDevice.algorithms = deviceInfo.algorithms;
await alice.crypto._signObject(aliceDevice); await alice.crypto.signObject(aliceDevice);
olmlib.pkSign(aliceDevice, selfSigningKey, "@alice:example.com"); olmlib.pkSign(aliceDevice, selfSigningKey, "@alice:example.com");
// feed sync result that includes master key, ssk, device key // feed sync result that includes master key, ssk, device key
@@ -358,7 +358,7 @@ describe("Cross Signing", function() {
["ed25519:" + bobMasterPubkey]: sskSig, ["ed25519:" + bobMasterPubkey]: sskSig,
}, },
}; };
alice.crypto._deviceList.storeCrossSigningForUser("@bob:example.com", { alice.crypto.deviceList.storeCrossSigningForUser("@bob:example.com", {
keys: { keys: {
master: { master: {
user_id: "@bob:example.com", user_id: "@bob:example.com",
@@ -387,7 +387,7 @@ describe("Cross Signing", function() {
["ed25519:" + bobPubkey]: sig, ["ed25519:" + bobPubkey]: sig,
}, },
}; };
alice.crypto._deviceList.storeDevicesForUser("@bob:example.com", { alice.crypto.deviceList.storeDevicesForUser("@bob:example.com", {
Dynabook: bobDevice, Dynabook: bobDevice,
}); });
// Bob's device key should be TOFU // Bob's device key should be TOFU
@@ -421,8 +421,8 @@ describe("Cross Signing", function() {
null, null,
aliceKeys, aliceKeys,
); );
alice.crypto._deviceList.startTrackingDeviceList("@bob:example.com"); alice.crypto.deviceList.startTrackingDeviceList("@bob:example.com");
alice.crypto._deviceList.stopTrackingAllDeviceLists = () => {}; alice.crypto.deviceList.stopTrackingAllDeviceLists = () => {};
alice.uploadDeviceSigningKeys = async () => {}; alice.uploadDeviceSigningKeys = async () => {};
alice.uploadKeySignatures = async () => {}; alice.uploadKeySignatures = async () => {};
@@ -437,14 +437,14 @@ describe("Cross Signing", function() {
]); ]);
const keyChangePromise = new Promise((resolve, reject) => { const keyChangePromise = new Promise((resolve, reject) => {
alice.crypto._deviceList.once("userCrossSigningUpdated", (userId) => { alice.crypto.deviceList.once("userCrossSigningUpdated", (userId) => {
if (userId === "@bob:example.com") { if (userId === "@bob:example.com") {
resolve(); resolve();
} }
}); });
}); });
const deviceInfo = alice.crypto._deviceList._devices["@alice:example.com"] const deviceInfo = alice.crypto.deviceList._devices["@alice:example.com"]
.Osborne2; .Osborne2;
const aliceDevice = { const aliceDevice = {
user_id: "@alice:example.com", user_id: "@alice:example.com",
@@ -452,7 +452,7 @@ describe("Cross Signing", function() {
}; };
aliceDevice.keys = deviceInfo.keys; aliceDevice.keys = deviceInfo.keys;
aliceDevice.algorithms = deviceInfo.algorithms; aliceDevice.algorithms = deviceInfo.algorithms;
await alice.crypto._signObject(aliceDevice); await alice.crypto.signObject(aliceDevice);
const bobOlmAccount = new global.Olm.Account(); const bobOlmAccount = new global.Olm.Account();
bobOlmAccount.create(); bobOlmAccount.create();
@@ -606,7 +606,7 @@ describe("Cross Signing", function() {
["ed25519:" + bobMasterPubkey]: sskSig, ["ed25519:" + bobMasterPubkey]: sskSig,
}, },
}; };
alice.crypto._deviceList.storeCrossSigningForUser("@bob:example.com", { alice.crypto.deviceList.storeCrossSigningForUser("@bob:example.com", {
keys: { keys: {
master: { master: {
user_id: "@bob:example.com", user_id: "@bob:example.com",
@@ -629,7 +629,7 @@ describe("Cross Signing", function() {
"ed25519:Dynabook": "someOtherPubkey", "ed25519:Dynabook": "someOtherPubkey",
}, },
}; };
alice.crypto._deviceList.storeDevicesForUser("@bob:example.com", { alice.crypto.deviceList.storeDevicesForUser("@bob:example.com", {
Dynabook: bobDevice, Dynabook: bobDevice,
}); });
// Bob's device key should be untrusted // Bob's device key should be untrusted
@@ -673,7 +673,7 @@ describe("Cross Signing", function() {
["ed25519:" + bobMasterPubkey]: sskSig, ["ed25519:" + bobMasterPubkey]: sskSig,
}, },
}; };
alice.crypto._deviceList.storeCrossSigningForUser("@bob:example.com", { alice.crypto.deviceList.storeCrossSigningForUser("@bob:example.com", {
keys: { keys: {
master: { master: {
user_id: "@bob:example.com", user_id: "@bob:example.com",
@@ -701,7 +701,7 @@ describe("Cross Signing", function() {
bobDevice.signatures = {}; bobDevice.signatures = {};
bobDevice.signatures["@bob:example.com"] = {}; bobDevice.signatures["@bob:example.com"] = {};
bobDevice.signatures["@bob:example.com"]["ed25519:" + bobPubkey] = sig; bobDevice.signatures["@bob:example.com"]["ed25519:" + bobPubkey] = sig;
alice.crypto._deviceList.storeDevicesForUser("@bob:example.com", { alice.crypto.deviceList.storeDevicesForUser("@bob:example.com", {
Dynabook: bobDevice, Dynabook: bobDevice,
}); });
// Alice verifies Bob's SSK // Alice verifies Bob's SSK
@@ -733,7 +733,7 @@ describe("Cross Signing", function() {
["ed25519:" + bobMasterPubkey2]: sskSig2, ["ed25519:" + bobMasterPubkey2]: sskSig2,
}, },
}; };
alice.crypto._deviceList.storeCrossSigningForUser("@bob:example.com", { alice.crypto.deviceList.storeCrossSigningForUser("@bob:example.com", {
keys: { keys: {
master: { master: {
user_id: "@bob:example.com", user_id: "@bob:example.com",
@@ -770,7 +770,7 @@ describe("Cross Signing", function() {
// Alice gets new signature for device // Alice gets new signature for device
const sig2 = bobSigning2.sign(bobDeviceString); const sig2 = bobSigning2.sign(bobDeviceString);
bobDevice.signatures["@bob:example.com"]["ed25519:" + bobPubkey2] = sig2; bobDevice.signatures["@bob:example.com"]["ed25519:" + bobPubkey2] = sig2;
alice.crypto._deviceList.storeDevicesForUser("@bob:example.com", { alice.crypto.deviceList.storeDevicesForUser("@bob:example.com", {
Dynabook: bobDevice, Dynabook: bobDevice,
}); });
@@ -805,20 +805,20 @@ describe("Cross Signing", function() {
bob.uploadKeySignatures = async () => {}; bob.uploadKeySignatures = async () => {};
// set Bob's cross-signing key // set Bob's cross-signing key
await resetCrossSigningKeys(bob); await resetCrossSigningKeys(bob);
alice.crypto._deviceList.storeDevicesForUser("@bob:example.com", { alice.crypto.deviceList.storeDevicesForUser("@bob:example.com", {
Dynabook: { Dynabook: {
algorithms: ["m.olm.curve25519-aes-sha256", "m.megolm.v1.aes-sha"], algorithms: ["m.olm.curve25519-aes-sha256", "m.megolm.v1.aes-sha"],
keys: { keys: {
"curve25519:Dynabook": bob.crypto._olmDevice.deviceCurve25519Key, "curve25519:Dynabook": bob.crypto.olmDevice.deviceCurve25519Key,
"ed25519:Dynabook": bob.crypto._olmDevice.deviceEd25519Key, "ed25519:Dynabook": bob.crypto.olmDevice.deviceEd25519Key,
}, },
verified: 1, verified: 1,
known: true, known: true,
}, },
}); });
alice.crypto._deviceList.storeCrossSigningForUser( alice.crypto.deviceList.storeCrossSigningForUser(
"@bob:example.com", "@bob:example.com",
bob.crypto._crossSigningInfo.toStorage(), bob.crypto.crossSigningInfo.toStorage(),
); );
alice.uploadDeviceSigningKeys = async () => {}; alice.uploadDeviceSigningKeys = async () => {};
@@ -838,7 +838,7 @@ describe("Cross Signing", function() {
expect(bobTrust.isTofu()).toBeTruthy(); expect(bobTrust.isTofu()).toBeTruthy();
// "forget" that Bob is trusted // "forget" that Bob is trusted
delete alice.crypto._deviceList._crossSigningInfo["@bob:example.com"] delete alice.crypto.deviceList.crossSigningInfo["@bob:example.com"]
.keys.master.signatures["@alice:example.com"]; .keys.master.signatures["@alice:example.com"];
const bobTrust2 = alice.checkUserTrust("@bob:example.com"); const bobTrust2 = alice.checkUserTrust("@bob:example.com");
@@ -848,7 +848,7 @@ describe("Cross Signing", function() {
upgradePromise = new Promise((resolve) => { upgradePromise = new Promise((resolve) => {
upgradeResolveFunc = resolve; upgradeResolveFunc = resolve;
}); });
alice.crypto._deviceList.emit("userCrossSigningUpdated", "@bob:example.com"); alice.crypto.deviceList.emit("userCrossSigningUpdated", "@bob:example.com");
await new Promise((resolve) => { await new Promise((resolve) => {
alice.crypto.on("userTrustStatusChanged", resolve); alice.crypto.on("userTrustStatusChanged", resolve);
}); });

View File

@@ -8,22 +8,22 @@ export async function resetCrossSigningKeys(client, {
} = {}) { } = {}) {
const crypto = client.crypto; const crypto = client.crypto;
const oldKeys = Object.assign({}, crypto._crossSigningInfo.keys); const oldKeys = Object.assign({}, crypto.crossSigningInfo.keys);
try { try {
await crypto._crossSigningInfo.resetKeys(level); await crypto.crossSigningInfo.resetKeys(level);
await crypto._signObject(crypto._crossSigningInfo.keys.master); await crypto._signObject(crypto.crossSigningInfo.keys.master);
// write a copy locally so we know these are trusted keys // write a copy locally so we know these are trusted keys
await crypto._cryptoStore.doTxn( await crypto._cryptoStore.doTxn(
'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT], 'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => { (txn) => {
crypto._cryptoStore.storeCrossSigningKeys( crypto._cryptoStore.storeCrossSigningKeys(
txn, crypto._crossSigningInfo.keys); txn, crypto.crossSigningInfo.keys);
}, },
); );
} catch (e) { } catch (e) {
// If anything failed here, revert the keys so we know to try again from the start // If anything failed here, revert the keys so we know to try again from the start
// next time. // next time.
crypto._crossSigningInfo.keys = oldKeys; crypto.crossSigningInfo.keys = oldKeys;
throw e; throw e;
} }
crypto._baseApis.emit("crossSigning.keysChanged", {}); crypto._baseApis.emit("crossSigning.keysChanged", {});

View File

@@ -99,11 +99,11 @@ describe("Secrets", function() {
}, },
}, },
); );
alice.crypto._crossSigningInfo.setKeys({ alice.crypto.crossSigningInfo.setKeys({
master: signingkeyInfo, master: signingkeyInfo,
}); });
const secretStorage = alice.crypto._secretStorage; const secretStorage = alice.crypto.secretStorage;
alice.setAccountData = async function(eventType, contents, callback) { alice.setAccountData = async function(eventType, contents, callback) {
alice.store.storeAccountDataEvents([ alice.store.storeAccountDataEvents([
@@ -120,7 +120,7 @@ describe("Secrets", function() {
const keyAccountData = { const keyAccountData = {
algorithm: SECRET_STORAGE_ALGORITHM_V1_AES, algorithm: SECRET_STORAGE_ALGORITHM_V1_AES,
}; };
await alice.crypto._crossSigningInfo.signObject(keyAccountData, 'master'); await alice.crypto.crossSigningInfo.signObject(keyAccountData, 'master');
alice.store.storeAccountDataEvents([ alice.store.storeAccountDataEvents([
new MatrixEvent({ new MatrixEvent({
@@ -234,11 +234,11 @@ describe("Secrets", function() {
}, },
); );
const vaxDevice = vax.client.crypto._olmDevice; const vaxDevice = vax.client.crypto.olmDevice;
const osborne2Device = osborne2.client.crypto._olmDevice; const osborne2Device = osborne2.client.crypto.olmDevice;
const secretStorage = osborne2.client.crypto._secretStorage; const secretStorage = osborne2.client.crypto.secretStorage;
osborne2.client.crypto._deviceList.storeDevicesForUser("@alice:example.com", { osborne2.client.crypto.deviceList.storeDevicesForUser("@alice:example.com", {
"VAX": { "VAX": {
user_id: "@alice:example.com", user_id: "@alice:example.com",
device_id: "VAX", device_id: "VAX",
@@ -249,7 +249,7 @@ describe("Secrets", function() {
}, },
}, },
}); });
vax.client.crypto._deviceList.storeDevicesForUser("@alice:example.com", { vax.client.crypto.deviceList.storeDevicesForUser("@alice:example.com", {
"Osborne2": { "Osborne2": {
user_id: "@alice:example.com", user_id: "@alice:example.com",
device_id: "Osborne2", device_id: "Osborne2",
@@ -265,7 +265,7 @@ describe("Secrets", function() {
const otks = (await osborne2Device.getOneTimeKeys()).curve25519; const otks = (await osborne2Device.getOneTimeKeys()).curve25519;
await osborne2Device.markKeysAsPublished(); await osborne2Device.markKeysAsPublished();
await vax.client.crypto._olmDevice.createOutboundSession( await vax.client.crypto.olmDevice.createOutboundSession(
osborne2Device.deviceCurve25519Key, osborne2Device.deviceCurve25519Key,
Object.values(otks)[0], Object.values(otks)[0],
); );
@@ -334,8 +334,8 @@ describe("Secrets", function() {
createSecretStorageKey, createSecretStorageKey,
}); });
const crossSigning = bob.crypto._crossSigningInfo; const crossSigning = bob.crypto.crossSigningInfo;
const secretStorage = bob.crypto._secretStorage; const secretStorage = bob.crypto.secretStorage;
expect(crossSigning.getId()).toBeTruthy(); expect(crossSigning.getId()).toBeTruthy();
expect(await crossSigning.isStoredInSecretStorage(secretStorage)) expect(await crossSigning.isStoredInSecretStorage(secretStorage))
@@ -376,10 +376,10 @@ describe("Secrets", function() {
]); ]);
this.emit("accountData", event); this.emit("accountData", event);
}; };
bob.crypto._backupManager.checkKeyBackup = async () => {}; bob.crypto.backupManager.checkKeyBackup = async () => {};
const crossSigning = bob.crypto._crossSigningInfo; const crossSigning = bob.crypto.crossSigningInfo;
const secretStorage = bob.crypto._secretStorage; const secretStorage = bob.crypto.secretStorage;
// Set up cross-signing keys from scratch with specific storage key // Set up cross-signing keys from scratch with specific storage key
await bob.bootstrapCrossSigning({ await bob.bootstrapCrossSigning({
@@ -394,7 +394,7 @@ describe("Secrets", function() {
}); });
// Clear local cross-signing keys and read from secret storage // Clear local cross-signing keys and read from secret storage
bob.crypto._deviceList.storeCrossSigningForUser( bob.crypto.deviceList.storeCrossSigningForUser(
"@bob:example.com", "@bob:example.com",
crossSigning.toStorage(), crossSigning.toStorage(),
); );
@@ -479,7 +479,7 @@ describe("Secrets", function() {
}, },
}), }),
]); ]);
alice.crypto._deviceList.storeCrossSigningForUser("@alice:example.com", { alice.crypto.deviceList.storeCrossSigningForUser("@alice:example.com", {
keys: { keys: {
master: { master: {
user_id: "@alice:example.com", user_id: "@alice:example.com",
@@ -619,7 +619,7 @@ describe("Secrets", function() {
}, },
}), }),
]); ]);
alice.crypto._deviceList.storeCrossSigningForUser("@alice:example.com", { alice.crypto.deviceList.storeCrossSigningForUser("@alice:example.com", {
keys: { keys: {
master: { master: {
user_id: "@alice:example.com", user_id: "@alice:example.com",

View File

@@ -49,7 +49,7 @@ describe("verification request integration tests with crypto layer", function()
verificationMethods: [verificationMethods.SAS], verificationMethods: [verificationMethods.SAS],
}, },
); );
alice.client.crypto._deviceList.getRawStoredDevicesForUser = function() { alice.client.crypto.deviceList.getRawStoredDevicesForUser = function() {
return { return {
Dynabook: { Dynabook: {
keys: { keys: {

View File

@@ -87,8 +87,8 @@ describe("SAS verification", function() {
}, },
); );
const aliceDevice = alice.client.crypto._olmDevice; const aliceDevice = alice.client.crypto.olmDevice;
const bobDevice = bob.client.crypto._olmDevice; const bobDevice = bob.client.crypto.olmDevice;
ALICE_DEVICES = { ALICE_DEVICES = {
Osborne2: { Osborne2: {
@@ -114,14 +114,14 @@ describe("SAS verification", function() {
}, },
}; };
alice.client.crypto._deviceList.storeDevicesForUser( alice.client.crypto.deviceList.storeDevicesForUser(
"@bob:example.com", BOB_DEVICES, "@bob:example.com", BOB_DEVICES,
); );
alice.client.downloadKeys = () => { alice.client.downloadKeys = () => {
return Promise.resolve(); return Promise.resolve();
}; };
bob.client.crypto._deviceList.storeDevicesForUser( bob.client.crypto.deviceList.storeDevicesForUser(
"@alice:example.com", ALICE_DEVICES, "@alice:example.com", ALICE_DEVICES,
); );
bob.client.downloadKeys = () => { bob.client.downloadKeys = () => {
@@ -296,9 +296,9 @@ describe("SAS verification", function() {
await resetCrossSigningKeys(bob.client); await resetCrossSigningKeys(bob.client);
bob.client.crypto._deviceList.storeCrossSigningForUser( bob.client.crypto.deviceList.storeCrossSigningForUser(
"@alice:example.com", { "@alice:example.com", {
keys: alice.client.crypto._crossSigningInfo.keys, keys: alice.client.crypto.crossSigningInfo.keys,
}, },
); );

View File

@@ -48,7 +48,7 @@ import {
retryNetworkOperation, retryNetworkOperation,
} from "./http-api"; } from "./http-api";
import { Crypto, fixBackupKey, IBootstrapCrossSigningOpts, isCryptoAvailable } from './crypto'; import { Crypto, fixBackupKey, IBootstrapCrossSigningOpts, isCryptoAvailable } from './crypto';
import { DeviceInfo } from "./crypto/deviceinfo"; import { DeviceInfo, IDevice } from "./crypto/deviceinfo";
import { decodeRecoveryKey } from './crypto/recoverykey'; import { decodeRecoveryKey } from './crypto/recoverykey';
import { keyFromAuthData } from './crypto/key_passphrase'; import { keyFromAuthData } from './crypto/key_passphrase';
import { User } from "./models/user"; import { User } from "./models/user";
@@ -64,7 +64,7 @@ import {
import { IIdentityServerProvider } from "./@types/IIdentityServerProvider"; import { IIdentityServerProvider } from "./@types/IIdentityServerProvider";
import type Request from "request"; import type Request from "request";
import { MatrixScheduler } from "./scheduler"; import { MatrixScheduler } from "./scheduler";
import { ICryptoCallbacks, IDeviceTrustLevel, ISecretStorageKeyInfo, NotificationCountType } from "./matrix"; import { ICryptoCallbacks, ISecretStorageKeyInfo, NotificationCountType } from "./matrix";
import { MemoryCryptoStore } from "./crypto/store/memory-crypto-store"; import { MemoryCryptoStore } from "./crypto/store/memory-crypto-store";
import { LocalStorageCryptoStore } from "./crypto/store/localStorage-crypto-store"; import { LocalStorageCryptoStore } from "./crypto/store/localStorage-crypto-store";
import { IndexedDBCryptoStore } from "./crypto/store/indexeddb-crypto-store"; import { IndexedDBCryptoStore } from "./crypto/store/indexeddb-crypto-store";
@@ -85,7 +85,7 @@ import {
IRecoveryKey, IRecoveryKey,
ISecretStorageKey, ISecretStorageKey,
} from "./crypto/api"; } from "./crypto/api";
import { CrossSigningInfo, UserTrustLevel } from "./crypto/CrossSigning"; import { CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./crypto/CrossSigning";
import { Room } from "./models/room"; import { Room } from "./models/room";
import { import {
ICreateRoomOpts, ICreateRoomOpts,
@@ -1265,7 +1265,7 @@ export class MatrixClient extends EventEmitter {
public downloadKeys( public downloadKeys(
userIds: string[], userIds: string[],
forceDownload?: boolean, forceDownload?: boolean,
): Promise<Record<string, Record<string, DeviceInfo>>> { ): Promise<Record<string, Record<string, IDevice>>> {
if (!this.crypto) { if (!this.crypto) {
return Promise.reject(new Error("End-to-end encryption disabled")); return Promise.reject(new Error("End-to-end encryption disabled"));
} }
@@ -1571,9 +1571,9 @@ export class MatrixClient extends EventEmitter {
* @param {string} userId The ID of the user whose devices is to be checked. * @param {string} userId The ID of the user whose devices is to be checked.
* @param {string} deviceId The ID of the device to check * @param {string} deviceId The ID of the device to check
* *
* @returns {IDeviceTrustLevel} * @returns {DeviceTrustLevel}
*/ */
public checkDeviceTrust(userId: string, deviceId: string): IDeviceTrustLevel { public checkDeviceTrust(userId: string, deviceId: string): DeviceTrustLevel {
if (!this.crypto) { if (!this.crypto) {
throw new Error("End-to-end encryption disabled"); throw new Error("End-to-end encryption disabled");
} }
@@ -1948,7 +1948,7 @@ export class MatrixClient extends EventEmitter {
* *
* @return {Promise<module:crypto/deviceinfo?>} * @return {Promise<module:crypto/deviceinfo?>}
*/ */
public getEventSenderDeviceInfo(event: MatrixEvent): Promise<DeviceInfo> { public async getEventSenderDeviceInfo(event: MatrixEvent): Promise<DeviceInfo> {
if (!this.crypto) { if (!this.crypto) {
return null; return null;
} }
@@ -2488,15 +2488,13 @@ export class MatrixClient extends EventEmitter {
targetRoomId: string, targetRoomId: string,
targetSessionId: string, targetSessionId: string,
backupInfo: IKeyBackupVersion, backupInfo: IKeyBackupVersion,
opts: IKeyBackupRestoreOpts, opts?: IKeyBackupRestoreOpts,
): Promise<IKeyBackupRestoreResult> { ): Promise<IKeyBackupRestoreResult> {
const privKey = await this.crypto.getSessionBackupPrivateKey(); const privKey = await this.crypto.getSessionBackupPrivateKey();
if (!privKey) { if (!privKey) {
throw new Error("Couldn't get key"); throw new Error("Couldn't get key");
} }
return this.restoreKeyBackup( return this.restoreKeyBackup(privKey, targetRoomId, targetSessionId, backupInfo, opts);
privKey, targetRoomId, targetSessionId, backupInfo, opts,
);
} }
private async restoreKeyBackup( private async restoreKeyBackup(

View File

@@ -1,6 +1,5 @@
/* /*
Copyright 2019 New Vector Ltd Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,22 +19,43 @@ limitations under the License.
* @module crypto/CrossSigning * @module crypto/CrossSigning
*/ */
import { decodeBase64, encodeBase64, pkSign, pkVerify } from './olmlib';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { decodeBase64, encodeBase64, pkSign, pkVerify } from './olmlib';
import { logger } from '../logger'; import { logger } from '../logger';
import { IndexedDBCryptoStore } from '../crypto/store/indexeddb-crypto-store'; import { IndexedDBCryptoStore } from '../crypto/store/indexeddb-crypto-store';
import { decryptAES, encryptAES } from './aes'; import { decryptAES, encryptAES } from './aes';
import { PkSigning } from "@matrix-org/olm";
import { DeviceInfo } from "./deviceinfo";
import { SecretStorage } from "./SecretStorage";
import { CryptoStore, MatrixClient } from "../client";
import { OlmDevice } from "./OlmDevice";
import { ICryptoCallbacks } from "../matrix";
const KEY_REQUEST_TIMEOUT_MS = 1000 * 60; const KEY_REQUEST_TIMEOUT_MS = 1000 * 60;
function publicKeyFromKeyInfo(keyInfo) { function publicKeyFromKeyInfo(keyInfo: any): any { // TODO types
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey } // `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
// We assume only a single key, and we want the bare form without type // We assume only a single key, and we want the bare form without type
// prefix, so we select the values. // prefix, so we select the values.
return Object.values(keyInfo.keys)[0]; return Object.values(keyInfo.keys)[0];
} }
interface ICacheCallbacks {
getCrossSigningKeyCache?(type: string, expectedPublicKey?: string): Promise<Uint8Array>;
storeCrossSigningKeyCache?(type: string, key: Uint8Array): Promise<void>;
}
export class CrossSigningInfo extends EventEmitter { export class CrossSigningInfo extends EventEmitter {
public keys: Record<string, any> = {}; // TODO types
public firstUse = true;
// This tracks whether we've ever verified this user with any identity.
// When you verify a user, any devices online at the time that receive
// the verifying signature via the homeserver will latch this to true
// and can use it in the future to detect cases where the user has
// become unverified later for any reason.
private crossSigningVerifiedBefore = false;
/** /**
* Information about a user's cross-signing keys * Information about a user's cross-signing keys
* *
@@ -46,27 +66,15 @@ export class CrossSigningInfo extends EventEmitter {
* Requires getCrossSigningKey and saveCrossSigningKeys * Requires getCrossSigningKey and saveCrossSigningKeys
* @param {object} cacheCallbacks Callbacks used to interact with the cache * @param {object} cacheCallbacks Callbacks used to interact with the cache
*/ */
constructor(userId, callbacks, cacheCallbacks) { constructor(
public readonly userId: string,
private callbacks: ICryptoCallbacks = {},
private cacheCallbacks: ICacheCallbacks = {},
) {
super(); super();
// you can't change the userId
Object.defineProperty(this, 'userId', {
enumerable: true,
value: userId,
});
this._callbacks = callbacks || {};
this._cacheCallbacks = cacheCallbacks || {};
this.keys = {};
this.firstUse = true;
// This tracks whether we've ever verified this user with any identity.
// When you verify a user, any devices online at the time that receive
// the verifying signature via the homeserver will latch this to true
// and can use it in the future to detect cases where the user has
// become unverifed later for any reason.
this.crossSigningVerifiedBefore = false;
} }
static fromStorage(obj, userId) { public static fromStorage(obj: object, userId: string): CrossSigningInfo {
const res = new CrossSigningInfo(userId); const res = new CrossSigningInfo(userId);
for (const prop in obj) { for (const prop in obj) {
if (obj.hasOwnProperty(prop)) { if (obj.hasOwnProperty(prop)) {
@@ -76,7 +84,7 @@ export class CrossSigningInfo extends EventEmitter {
return res; return res;
} }
toStorage() { public toStorage(): object {
return { return {
keys: this.keys, keys: this.keys,
firstUse: this.firstUse, firstUse: this.firstUse,
@@ -92,10 +100,10 @@ export class CrossSigningInfo extends EventEmitter {
* the stored public key for the given key type. * the stored public key for the given key type.
* @returns {Array} An array with [ public key, Olm.PkSigning ] * @returns {Array} An array with [ public key, Olm.PkSigning ]
*/ */
async getCrossSigningKey(type, expectedPubkey) { public async getCrossSigningKey(type: string, expectedPubkey?: string): Promise<[string, PkSigning]> {
const shouldCache = ["master", "self_signing", "user_signing"].indexOf(type) >= 0; const shouldCache = ["master", "self_signing", "user_signing"].indexOf(type) >= 0;
if (!this._callbacks.getCrossSigningKey) { if (!this.callbacks.getCrossSigningKey) {
throw new Error("No getCrossSigningKey callback supplied"); throw new Error("No getCrossSigningKey callback supplied");
} }
@@ -103,7 +111,7 @@ export class CrossSigningInfo extends EventEmitter {
expectedPubkey = this.getId(type); expectedPubkey = this.getId(type);
} }
function validateKey(key) { function validateKey(key: Uint8Array): [string, PkSigning] {
if (!key) return; if (!key) return;
const signing = new global.Olm.PkSigning(); const signing = new global.Olm.PkSigning();
const gotPubkey = signing.init_with_seed(key); const gotPubkey = signing.init_with_seed(key);
@@ -114,9 +122,8 @@ export class CrossSigningInfo extends EventEmitter {
} }
let privkey; let privkey;
if (this._cacheCallbacks.getCrossSigningKeyCache && shouldCache) { if (this.cacheCallbacks.getCrossSigningKeyCache && shouldCache) {
privkey = await this._cacheCallbacks privkey = await this.cacheCallbacks.getCrossSigningKeyCache(type, expectedPubkey);
.getCrossSigningKeyCache(type, expectedPubkey);
} }
const cacheresult = validateKey(privkey); const cacheresult = validateKey(privkey);
@@ -124,11 +131,11 @@ export class CrossSigningInfo extends EventEmitter {
return cacheresult; return cacheresult;
} }
privkey = await this._callbacks.getCrossSigningKey(type, expectedPubkey); privkey = await this.callbacks.getCrossSigningKey(type, expectedPubkey);
const result = validateKey(privkey); const result = validateKey(privkey);
if (result) { if (result) {
if (this._cacheCallbacks.storeCrossSigningKeyCache && shouldCache) { if (this.cacheCallbacks.storeCrossSigningKeyCache && shouldCache) {
await this._cacheCallbacks.storeCrossSigningKeyCache(type, privkey); await this.cacheCallbacks.storeCrossSigningKeyCache(type, privkey);
} }
return result; return result;
} }
@@ -156,10 +163,9 @@ export class CrossSigningInfo extends EventEmitter {
* with, or null if it is not present or not encrypted with a trusted * with, or null if it is not present or not encrypted with a trusted
* key * key
*/ */
async isStoredInSecretStorage(secretStorage) { public async isStoredInSecretStorage(secretStorage: SecretStorage): Promise<Record<string, object>> {
// check what SSSS keys have encrypted the master key (if any) // check what SSSS keys have encrypted the master key (if any)
const stored = const stored = await secretStorage.isStored("m.cross_signing.master", false) || {};
await secretStorage.isStored("m.cross_signing.master", false) || {};
// then check which of those SSSS keys have also encrypted the SSK and USK // then check which of those SSSS keys have also encrypted the SSK and USK
function intersect(s) { function intersect(s) {
for (const k of Object.keys(stored)) { for (const k of Object.keys(stored)) {
@@ -169,9 +175,7 @@ export class CrossSigningInfo extends EventEmitter {
} }
} }
for (const type of ["self_signing", "user_signing"]) { for (const type of ["self_signing", "user_signing"]) {
intersect( intersect(await secretStorage.isStored(`m.cross_signing.${type}`, false) || {});
await secretStorage.isStored(`m.cross_signing.${type}`, false) || {},
);
} }
return Object.keys(stored).length ? stored : null; return Object.keys(stored).length ? stored : null;
} }
@@ -184,7 +188,10 @@ export class CrossSigningInfo extends EventEmitter {
* @param {Map} keys The keys to store * @param {Map} keys The keys to store
* @param {SecretStorage} secretStorage The secret store using account data * @param {SecretStorage} secretStorage The secret store using account data
*/ */
static async storeInSecretStorage(keys, secretStorage) { public static async storeInSecretStorage(
keys: Map<string, Uint8Array>,
secretStorage: SecretStorage,
): Promise<void> {
for (const [type, privateKey] of keys) { for (const [type, privateKey] of keys) {
const encodedKey = encodeBase64(privateKey); const encodedKey = encodeBase64(privateKey);
await secretStorage.store(`m.cross_signing.${type}`, encodedKey); await secretStorage.store(`m.cross_signing.${type}`, encodedKey);
@@ -200,7 +207,7 @@ export class CrossSigningInfo extends EventEmitter {
* @param {SecretStorage} secretStorage The secret store using account data * @param {SecretStorage} secretStorage The secret store using account data
* @return {Uint8Array} The private key * @return {Uint8Array} The private key
*/ */
static async getFromSecretStorage(type, secretStorage) { public static async getFromSecretStorage(type: string, secretStorage: SecretStorage): Promise<Uint8Array> {
const encodedKey = await secretStorage.get(`m.cross_signing.${type}`); const encodedKey = await secretStorage.get(`m.cross_signing.${type}`);
if (!encodedKey) { if (!encodedKey) {
return null; return null;
@@ -215,8 +222,8 @@ export class CrossSigningInfo extends EventEmitter {
* "self_signing", or "user_signing". Optional, will check all by default. * "self_signing", or "user_signing". Optional, will check all by default.
* @returns {boolean} True if all keys are stored in the local cache. * @returns {boolean} True if all keys are stored in the local cache.
*/ */
async isStoredInKeyCache(type) { public async isStoredInKeyCache(type?: string): Promise<boolean> {
const cacheCallbacks = this._cacheCallbacks; const cacheCallbacks = this.cacheCallbacks;
if (!cacheCallbacks) return false; if (!cacheCallbacks) return false;
const types = type ? [type] : ["master", "self_signing", "user_signing"]; const types = type ? [type] : ["master", "self_signing", "user_signing"];
for (const t of types) { for (const t of types) {
@@ -232,9 +239,9 @@ export class CrossSigningInfo extends EventEmitter {
* *
* @returns {Map} A map from key type (string) to private key (Uint8Array) * @returns {Map} A map from key type (string) to private key (Uint8Array)
*/ */
async getCrossSigningKeysFromCache() { public async getCrossSigningKeysFromCache(): Promise<Map<string, Uint8Array>> {
const keys = new Map(); const keys = new Map();
const cacheCallbacks = this._cacheCallbacks; const cacheCallbacks = this.cacheCallbacks;
if (!cacheCallbacks) return keys; if (!cacheCallbacks) return keys;
for (const type of ["master", "self_signing", "user_signing"]) { for (const type of ["master", "self_signing", "user_signing"]) {
const privKey = await cacheCallbacks.getCrossSigningKeyCache(type); const privKey = await cacheCallbacks.getCrossSigningKeyCache(type);
@@ -255,8 +262,7 @@ export class CrossSigningInfo extends EventEmitter {
* *
* @return {string} the ID * @return {string} the ID
*/ */
getId(type) { public getId(type = "master"): string {
type = type || "master";
if (!this.keys[type]) return null; if (!this.keys[type]) return null;
const keyInfo = this.keys[type]; const keyInfo = this.keys[type];
return publicKeyFromKeyInfo(keyInfo); return publicKeyFromKeyInfo(keyInfo);
@@ -269,8 +275,8 @@ export class CrossSigningInfo extends EventEmitter {
* *
* @param {CrossSigningLevel} level The key types to reset * @param {CrossSigningLevel} level The key types to reset
*/ */
async resetKeys(level) { public async resetKeys(level?: CrossSigningLevel): Promise<void> {
if (!this._callbacks.saveCrossSigningKeys) { if (!this.callbacks.saveCrossSigningKeys) {
throw new Error("No saveCrossSigningKeys callback supplied"); throw new Error("No saveCrossSigningKeys callback supplied");
} }
@@ -289,8 +295,8 @@ export class CrossSigningInfo extends EventEmitter {
return; return;
} }
const privateKeys = {}; const privateKeys: Record<string, Uint8Array> = {};
const keys = {}; const keys: Record<string, object> = {};
let masterSigning; let masterSigning;
let masterPub; let masterPub;
@@ -347,7 +353,7 @@ export class CrossSigningInfo extends EventEmitter {
} }
Object.assign(this.keys, keys); Object.assign(this.keys, keys);
this._callbacks.saveCrossSigningKeys(privateKeys); this.callbacks.saveCrossSigningKeys(privateKeys);
} finally { } finally {
if (masterSigning) { if (masterSigning) {
masterSigning.free(); masterSigning.free();
@@ -358,12 +364,12 @@ export class CrossSigningInfo extends EventEmitter {
/** /**
* unsets the keys, used when another session has reset the keys, to disable cross-signing * unsets the keys, used when another session has reset the keys, to disable cross-signing
*/ */
clearKeys() { public clearKeys(): void {
this.keys = {}; this.keys = {};
} }
setKeys(keys) { public setKeys(keys: Record<string, any>): void {
const signingKeys = {}; const signingKeys: Record<string, object> = {};
if (keys.master) { if (keys.master) {
if (keys.master.user_id !== this.userId) { if (keys.master.user_id !== this.userId) {
const error = "Mismatched user ID " + keys.master.user_id + const error = "Mismatched user ID " + keys.master.user_id +
@@ -434,7 +440,7 @@ export class CrossSigningInfo extends EventEmitter {
} }
} }
updateCrossSigningVerifiedBefore(isCrossSigningVerified) { public updateCrossSigningVerifiedBefore(isCrossSigningVerified: boolean): void {
// It is critical that this value latches forward from false to true but // It is critical that this value latches forward from false to true but
// never back to false to avoid a downgrade attack. // never back to false to avoid a downgrade attack.
if (!this.crossSigningVerifiedBefore && isCrossSigningVerified) { if (!this.crossSigningVerifiedBefore && isCrossSigningVerified) {
@@ -442,7 +448,7 @@ export class CrossSigningInfo extends EventEmitter {
} }
} }
async signObject(data, type) { public async signObject<T extends object>(data: T, type: string): Promise<T> {
if (!this.keys[type]) { if (!this.keys[type]) {
throw new Error( throw new Error(
"Attempted to sign with " + type + " key but no such key present", "Attempted to sign with " + type + " key but no such key present",
@@ -457,7 +463,7 @@ export class CrossSigningInfo extends EventEmitter {
} }
} }
async signUser(key) { public async signUser(key: CrossSigningInfo): Promise<object> {
if (!this.keys.user_signing) { if (!this.keys.user_signing) {
logger.info("No user signing key: not signing user"); logger.info("No user signing key: not signing user");
return; return;
@@ -465,7 +471,7 @@ export class CrossSigningInfo extends EventEmitter {
return this.signObject(key.keys.master, "user_signing"); return this.signObject(key.keys.master, "user_signing");
} }
async signDevice(userId, device) { public async signDevice(userId: string, device: DeviceInfo): Promise<object> {
if (userId !== this.userId) { if (userId !== this.userId) {
throw new Error( throw new Error(
`Trying to sign ${userId}'s device; can only sign our own device`, `Trying to sign ${userId}'s device; can only sign our own device`,
@@ -492,7 +498,7 @@ export class CrossSigningInfo extends EventEmitter {
* *
* @returns {UserTrustLevel} * @returns {UserTrustLevel}
*/ */
checkUserTrust(userCrossSigning) { public checkUserTrust(userCrossSigning: CrossSigningInfo): UserTrustLevel {
// if we're checking our own key, then it's trusted if the master key // if we're checking our own key, then it's trusted if the master key
// and self-signing key match // and self-signing key match
if (this.userId === userCrossSigning.userId if (this.userId === userCrossSigning.userId
@@ -530,12 +536,17 @@ export class CrossSigningInfo extends EventEmitter {
* *
* @param {CrossSigningInfo} userCrossSigning Cross signing info for user * @param {CrossSigningInfo} userCrossSigning Cross signing info for user
* @param {module:crypto/deviceinfo} device The device to check * @param {module:crypto/deviceinfo} device The device to check
* @param {bool} localTrust Whether the device is trusted locally * @param {boolean} localTrust Whether the device is trusted locally
* @param {bool} trustCrossSignedDevices Whether we trust cross signed devices * @param {boolean} trustCrossSignedDevices Whether we trust cross signed devices
* *
* @returns {DeviceTrustLevel} * @returns {DeviceTrustLevel}
*/ */
checkDeviceTrust(userCrossSigning, device, localTrust, trustCrossSignedDevices) { public checkDeviceTrust(
userCrossSigning: CrossSigningInfo,
device: DeviceInfo,
localTrust: boolean,
trustCrossSignedDevices: boolean,
): DeviceTrustLevel {
const userTrust = this.checkUserTrust(userCrossSigning); const userTrust = this.checkUserTrust(userCrossSigning);
const userSSK = userCrossSigning.keys.self_signing; const userSSK = userCrossSigning.keys.self_signing;
@@ -552,29 +563,23 @@ export class CrossSigningInfo extends EventEmitter {
// if we can verify the user's SSK from their master key... // if we can verify the user's SSK from their master key...
pkVerify(userSSK, userCrossSigning.getId(), userCrossSigning.userId); pkVerify(userSSK, userCrossSigning.getId(), userCrossSigning.userId);
// ...and this device's key from their SSK... // ...and this device's key from their SSK...
pkVerify( pkVerify(deviceObj, publicKeyFromKeyInfo(userSSK), userCrossSigning.userId);
deviceObj, publicKeyFromKeyInfo(userSSK), userCrossSigning.userId,
);
// ...then we trust this device as much as far as we trust the user // ...then we trust this device as much as far as we trust the user
return DeviceTrustLevel.fromUserTrustLevel( return DeviceTrustLevel.fromUserTrustLevel(userTrust, localTrust, trustCrossSignedDevices);
userTrust, localTrust, trustCrossSignedDevices,
);
} catch (e) { } catch (e) {
return new DeviceTrustLevel( return new DeviceTrustLevel(false, false, localTrust, trustCrossSignedDevices);
false, false, localTrust, trustCrossSignedDevices,
);
} }
} }
/** /**
* @returns {object} Cache callbacks * @returns {object} Cache callbacks
*/ */
getCacheCallbacks() { public getCacheCallbacks(): ICacheCallbacks {
return this._cacheCallbacks; return this.cacheCallbacks;
} }
} }
function deviceToObject(device, userId) { function deviceToObject(device: DeviceInfo, userId: string) {
return { return {
algorithms: device.algorithms, algorithms: device.algorithms,
keys: device.keys, keys: device.keys,
@@ -584,49 +589,49 @@ function deviceToObject(device, userId) {
}; };
} }
export const CrossSigningLevel = { export enum CrossSigningLevel {
MASTER: 4, MASTER = 4,
USER_SIGNING: 2, USER_SIGNING = 2,
SELF_SIGNING: 1, SELF_SIGNING = 1,
}; }
/** /**
* Represents the ways in which we trust a user * Represents the ways in which we trust a user
*/ */
export class UserTrustLevel { export class UserTrustLevel {
constructor(crossSigningVerified, crossSigningVerifiedBefore, tofu) { constructor(
this._crossSigningVerified = crossSigningVerified; private readonly crossSigningVerified: boolean,
this._crossSigningVerifiedBefore = crossSigningVerifiedBefore; private readonly crossSigningVerifiedBefore: boolean,
this._tofu = tofu; private readonly tofu: boolean,
} ) {}
/** /**
* @returns {bool} true if this user is verified via any means * @returns {boolean} true if this user is verified via any means
*/ */
isVerified() { public isVerified(): boolean {
return this.isCrossSigningVerified(); return this.isCrossSigningVerified();
} }
/** /**
* @returns {bool} true if this user is verified via cross signing * @returns {boolean} true if this user is verified via cross signing
*/ */
isCrossSigningVerified() { public isCrossSigningVerified(): boolean {
return this._crossSigningVerified; return this.crossSigningVerified;
} }
/** /**
* @returns {bool} true if we ever verified this user before (at least for * @returns {boolean} true if we ever verified this user before (at least for
* the history of verifications observed by this device). * the history of verifications observed by this device).
*/ */
wasCrossSigningVerified() { public wasCrossSigningVerified(): boolean {
return this._crossSigningVerifiedBefore; return this.crossSigningVerifiedBefore;
} }
/** /**
* @returns {bool} true if this user's key is trusted on first use * @returns {boolean} true if this user's key is trusted on first use
*/ */
isTofu() { public isTofu(): boolean {
return this._tofu; return this.tofu;
} }
} }
@@ -634,58 +639,62 @@ export class UserTrustLevel {
* Represents the ways in which we trust a device * Represents the ways in which we trust a device
*/ */
export class DeviceTrustLevel { export class DeviceTrustLevel {
constructor(crossSigningVerified, tofu, localVerified, trustCrossSignedDevices) { constructor(
this._crossSigningVerified = crossSigningVerified; public readonly crossSigningVerified: boolean,
this._tofu = tofu; public readonly tofu: boolean,
this._localVerified = localVerified; private readonly localVerified: boolean,
this._trustCrossSignedDevices = trustCrossSignedDevices; private readonly trustCrossSignedDevices: boolean,
} ) {}
static fromUserTrustLevel(userTrustLevel, localVerified, trustCrossSignedDevices) { public static fromUserTrustLevel(
userTrustLevel: UserTrustLevel,
localVerified: boolean,
trustCrossSignedDevices: boolean,
): DeviceTrustLevel {
return new DeviceTrustLevel( return new DeviceTrustLevel(
userTrustLevel._crossSigningVerified, userTrustLevel.isCrossSigningVerified(),
userTrustLevel._tofu, userTrustLevel.isTofu(),
localVerified, localVerified,
trustCrossSignedDevices, trustCrossSignedDevices,
); );
} }
/** /**
* @returns {bool} true if this device is verified via any means * @returns {boolean} true if this device is verified via any means
*/ */
isVerified() { public isVerified(): boolean {
return Boolean(this.isLocallyVerified() || ( return Boolean(this.isLocallyVerified() || (
this._trustCrossSignedDevices && this.isCrossSigningVerified() this.trustCrossSignedDevices && this.isCrossSigningVerified()
)); ));
} }
/** /**
* @returns {bool} true if this device is verified via cross signing * @returns {boolean} true if this device is verified via cross signing
*/ */
isCrossSigningVerified() { public isCrossSigningVerified(): boolean {
return this._crossSigningVerified; return this.crossSigningVerified;
} }
/** /**
* @returns {bool} true if this device is verified locally * @returns {boolean} true if this device is verified locally
*/ */
isLocallyVerified() { public isLocallyVerified(): boolean {
return this._localVerified; return this.localVerified;
} }
/** /**
* @returns {bool} true if this device is trusted from a user's key * @returns {boolean} true if this device is trusted from a user's key
* that is trusted on first use * that is trusted on first use
*/ */
isTofu() { public isTofu(): boolean {
return this._tofu; return this.tofu;
} }
} }
export function createCryptoStoreCacheCallbacks(store, olmdevice) { export function createCryptoStoreCacheCallbacks(store: CryptoStore, olmDevice: OlmDevice): ICacheCallbacks {
return { return {
getCrossSigningKeyCache: async function(type, _expectedPublicKey) { getCrossSigningKeyCache: async function(type: string, _expectedPublicKey: string): Promise<Uint8Array> {
const key = await new Promise((resolve) => { const key = await new Promise<any>((resolve) => {
return store.doTxn( return store.doTxn(
'readonly', 'readonly',
[IndexedDBCryptoStore.STORE_ACCOUNT], [IndexedDBCryptoStore.STORE_ACCOUNT],
@@ -696,20 +705,20 @@ export function createCryptoStoreCacheCallbacks(store, olmdevice) {
}); });
if (key && key.ciphertext) { if (key && key.ciphertext) {
const pickleKey = Buffer.from(olmdevice._pickleKey); const pickleKey = Buffer.from(olmDevice._pickleKey);
const decrypted = await decryptAES(key, pickleKey, type); const decrypted = await decryptAES(key, pickleKey, type);
return decodeBase64(decrypted); return decodeBase64(decrypted);
} else { } else {
return key; return key;
} }
}, },
storeCrossSigningKeyCache: async function(type, key) { storeCrossSigningKeyCache: async function(type: string, key: Uint8Array): Promise<void> {
if (!(key instanceof Uint8Array)) { if (!(key instanceof Uint8Array)) {
throw new Error( throw new Error(
`storeCrossSigningKeyCache expects Uint8Array, got ${key}`, `storeCrossSigningKeyCache expects Uint8Array, got ${key}`,
); );
} }
const pickleKey = Buffer.from(olmdevice._pickleKey); const pickleKey = Buffer.from(olmDevice._pickleKey);
key = await encryptAES(encodeBase64(key), pickleKey, type); key = await encryptAES(encodeBase64(key), pickleKey, type);
return store.doTxn( return store.doTxn(
'readwrite', 'readwrite',
@@ -729,7 +738,7 @@ export function createCryptoStoreCacheCallbacks(store, olmdevice) {
* @param {string} userId The user ID being verified * @param {string} userId The user ID being verified
* @param {string} deviceId The device ID being verified * @param {string} deviceId The device ID being verified
*/ */
export async function requestKeysDuringVerification(baseApis, userId, deviceId) { export async function requestKeysDuringVerification(baseApis: MatrixClient, userId: string, deviceId: string) {
// If this is a self-verification, ask the other party for keys // If this is a self-verification, ask the other party for keys
if (baseApis.getUserId() !== userId) { if (baseApis.getUserId() !== userId) {
return; return;
@@ -739,7 +748,7 @@ export async function requestKeysDuringVerification(baseApis, userId, deviceId)
// it. We return here in order to test. // it. We return here in order to test.
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const client = baseApis; const client = baseApis;
const original = client.crypto._crossSigningInfo; const original = client.crypto.crossSigningInfo;
// We already have all of the infrastructure we need to validate and // We already have all of the infrastructure we need to validate and
// cache cross-signing keys, so instead of replicating that, here we set // cache cross-signing keys, so instead of replicating that, here we set
@@ -748,8 +757,7 @@ export async function requestKeysDuringVerification(baseApis, userId, deviceId)
const crossSigning = new CrossSigningInfo( const crossSigning = new CrossSigningInfo(
original.userId, original.userId,
{ getCrossSigningKey: async (type) => { { getCrossSigningKey: async (type) => {
logger.debug("Cross-signing: requesting secret", logger.debug("Cross-signing: requesting secret", type, deviceId);
type, deviceId);
const { promise } = client.requestSecret( const { promise } = client.requestSecret(
`m.cross_signing.${type}`, [deviceId], `m.cross_signing.${type}`, [deviceId],
); );
@@ -757,7 +765,7 @@ export async function requestKeysDuringVerification(baseApis, userId, deviceId)
const decoded = decodeBase64(result); const decoded = decodeBase64(result);
return Uint8Array.from(decoded); return Uint8Array.from(decoded);
} }, } },
original._cacheCallbacks, original.getCacheCallbacks(),
); );
crossSigning.keys = original.keys; crossSigning.keys = original.keys;
@@ -774,7 +782,8 @@ export async function requestKeysDuringVerification(baseApis, userId, deviceId)
}); });
// also request and cache the key backup key // also request and cache the key backup key
const backupKeyPromise = new Promise(async resolve => { // eslint-disable-next-line no-async-promise-executor
const backupKeyPromise = new Promise<void>(async resolve => {
const cachedKey = await client.crypto.getSessionBackupPrivateKey(); const cachedKey = await client.crypto.getSessionBackupPrivateKey();
if (!cachedKey) { if (!cachedKey) {
logger.info("No cached backup key found. Requesting..."); logger.info("No cached backup key found. Requesting...");
@@ -791,9 +800,7 @@ export async function requestKeysDuringVerification(baseApis, userId, deviceId)
logger.info("Backup key stored. Starting backup restore..."); logger.info("Backup key stored. Starting backup restore...");
const backupInfo = await client.getKeyBackupVersion(); const backupInfo = await client.getKeyBackupVersion();
// no need to await for this - just let it go in the bg // no need to await for this - just let it go in the bg
client.restoreKeyBackupWithCache( client.restoreKeyBackupWithCache(undefined, undefined, backupInfo).then(() => {
undefined, undefined, backupInfo,
).then(() => {
logger.info("Backup restored."); logger.info("Backup restored.");
}); });
} }

File diff suppressed because it is too large Load Diff

View File

@@ -184,7 +184,7 @@ export class EncryptionSetupOperation {
}); });
// pass the new keys to the main instance of our own CrossSigningInfo. // pass the new keys to the main instance of our own CrossSigningInfo.
crypto._crossSigningInfo.setKeys(this._crossSigningKeys.keys); crypto.crossSigningInfo.setKeys(this._crossSigningKeys.keys);
} }
// set account data // set account data
if (this._accountData) { if (this._accountData) {

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2018, 2019 New Vector Ltd Copyright 2018 - 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,44 +21,51 @@ limitations under the License.
*/ */
import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store'; import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store';
import { CryptoStore } from "../client";
/* eslint-disable camelcase */
interface IRoomEncryption {
algorithm: string;
rotation_period_ms: number;
rotation_period_msgs: number;
}
/* eslint-enable camelcase */
/** /**
* @alias module:crypto/RoomList * @alias module:crypto/RoomList
*/ */
export class RoomList { export class RoomList {
constructor(cryptoStore) { // Object of roomId -> room e2e info object (body of the m.room.encryption event)
this._cryptoStore = cryptoStore; private roomEncryption: Record<string, IRoomEncryption> = {};
// Object of roomId -> room e2e info object (body of the m.room.encryption event) constructor(private readonly cryptoStore: CryptoStore) {}
this._roomEncryption = {};
}
async init() { public async init(): Promise<void> {
await this._cryptoStore.doTxn( await this.cryptoStore.doTxn(
'readwrite', [IndexedDBCryptoStore.STORE_ROOMS], (txn) => { 'readwrite', [IndexedDBCryptoStore.STORE_ROOMS], (txn) => {
this._cryptoStore.getEndToEndRooms(txn, (result) => { this.cryptoStore.getEndToEndRooms(txn, (result) => {
this._roomEncryption = result; this.roomEncryption = result;
}); });
}, },
); );
} }
getRoomEncryption(roomId) { public getRoomEncryption(roomId: string): IRoomEncryption {
return this._roomEncryption[roomId] || null; return this.roomEncryption[roomId] || null;
} }
isRoomEncrypted(roomId) { public isRoomEncrypted(roomId: string): boolean {
return Boolean(this.getRoomEncryption(roomId)); return Boolean(this.getRoomEncryption(roomId));
} }
async setRoomEncryption(roomId, roomInfo) { public async setRoomEncryption(roomId: string, roomInfo: IRoomEncryption): Promise<void> {
// important that this happens before calling into the store // important that this happens before calling into the store
// as it prevents the Crypto::setRoomEncryption from calling // as it prevents the Crypto::setRoomEncryption from calling
// this twice for consecutive m.room.encryption events // this twice for consecutive m.room.encryption events
this._roomEncryption[roomId] = roomInfo; this.roomEncryption[roomId] = roomInfo;
await this._cryptoStore.doTxn( await this.cryptoStore.doTxn(
'readwrite', [IndexedDBCryptoStore.STORE_ROOMS], (txn) => { 'readwrite', [IndexedDBCryptoStore.STORE_ROOMS], (txn) => {
this._cryptoStore.storeEndToEndRoom(roomId, roomInfo, txn); this.cryptoStore.storeEndToEndRoom(roomId, roomInfo, txn);
}, },
); );
} }

View File

@@ -480,11 +480,11 @@ export class SecretStorage extends EventEmitter {
}; };
const encryptedContent = { const encryptedContent = {
algorithm: olmlib.OLM_ALGORITHM, algorithm: olmlib.OLM_ALGORITHM,
sender_key: this._baseApis.crypto._olmDevice.deviceCurve25519Key, sender_key: this._baseApis.crypto.olmDevice.deviceCurve25519Key,
ciphertext: {}, ciphertext: {},
}; };
await olmlib.ensureOlmSessionsForDevices( await olmlib.ensureOlmSessionsForDevices(
this._baseApis.crypto._olmDevice, this._baseApis.crypto.olmDevice,
this._baseApis, this._baseApis,
{ {
[sender]: [ [sender]: [
@@ -496,7 +496,7 @@ export class SecretStorage extends EventEmitter {
encryptedContent.ciphertext, encryptedContent.ciphertext,
this._baseApis.getUserId(), this._baseApis.getUserId(),
this._baseApis.deviceId, this._baseApis.deviceId,
this._baseApis.crypto._olmDevice, this._baseApis.crypto.olmDevice,
sender, sender,
this._baseApis.getStoredDevice(sender, deviceId), this._baseApis.getStoredDevice(sender, deviceId),
payload, payload,
@@ -527,7 +527,7 @@ export class SecretStorage extends EventEmitter {
if (requestControl) { if (requestControl) {
// make sure that the device that sent it is one of the devices that // make sure that the device that sent it is one of the devices that
// we requested from // we requested from
const deviceInfo = this._baseApis.crypto._deviceList.getDeviceByIdentityKey( const deviceInfo = this._baseApis.crypto.deviceList.getDeviceByIdentityKey(
olmlib.OLM_ALGORITHM, olmlib.OLM_ALGORITHM,
event.getSenderKey(), event.getSenderKey(),
); );

View File

@@ -64,16 +64,16 @@ export class DehydrationManager {
this.getDehydrationKeyFromCache(); this.getDehydrationKeyFromCache();
} }
async getDehydrationKeyFromCache(): Promise<void> { async getDehydrationKeyFromCache(): Promise<void> {
return await this.crypto._cryptoStore.doTxn( return await this.crypto.cryptoStore.doTxn(
'readonly', 'readonly',
[IndexedDBCryptoStore.STORE_ACCOUNT], [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => { (txn) => {
this.crypto._cryptoStore.getSecretStorePrivateKey( this.crypto.cryptoStore.getSecretStorePrivateKey(
txn, txn,
async (result) => { async (result) => {
if (result) { if (result) {
const { key, keyInfo, deviceDisplayName, time } = result; const { key, keyInfo, deviceDisplayName, time } = result;
const pickleKey = Buffer.from(this.crypto._olmDevice._pickleKey); const pickleKey = Buffer.from(this.crypto.olmDevice._pickleKey);
const decrypted = await decryptAES(key, pickleKey, DEHYDRATION_ALGORITHM); const decrypted = await decryptAES(key, pickleKey, DEHYDRATION_ALGORITHM);
this.key = decodeBase64(decrypted); this.key = decodeBase64(decrypted);
this.keyInfo = keyInfo; this.keyInfo = keyInfo;
@@ -114,11 +114,11 @@ export class DehydrationManager {
this.timeoutId = undefined; this.timeoutId = undefined;
} }
// clear storage // clear storage
await this.crypto._cryptoStore.doTxn( await this.crypto.cryptoStore.doTxn(
'readwrite', 'readwrite',
[IndexedDBCryptoStore.STORE_ACCOUNT], [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => { (txn) => {
this.crypto._cryptoStore.storeSecretStorePrivateKey( this.crypto.cryptoStore.storeSecretStorePrivateKey(
txn, "dehydration", null, txn, "dehydration", null,
); );
}, },
@@ -158,15 +158,15 @@ export class DehydrationManager {
this.timeoutId = undefined; this.timeoutId = undefined;
} }
try { try {
const pickleKey = Buffer.from(this.crypto._olmDevice._pickleKey); const pickleKey = Buffer.from(this.crypto.olmDevice._pickleKey);
// update the crypto store with the timestamp // update the crypto store with the timestamp
const key = await encryptAES(encodeBase64(this.key), pickleKey, DEHYDRATION_ALGORITHM); const key = await encryptAES(encodeBase64(this.key), pickleKey, DEHYDRATION_ALGORITHM);
await this.crypto._cryptoStore.doTxn( await this.crypto.cryptoStore.doTxn(
'readwrite', 'readwrite',
[IndexedDBCryptoStore.STORE_ACCOUNT], [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => { (txn) => {
this.crypto._cryptoStore.storeSecretStorePrivateKey( this.crypto.cryptoStore.storeSecretStorePrivateKey(
txn, "dehydration", txn, "dehydration",
{ {
keyInfo: this.keyInfo, keyInfo: this.keyInfo,
@@ -205,7 +205,7 @@ export class DehydrationManager {
} }
logger.log("Uploading account to server"); logger.log("Uploading account to server");
const dehydrateResult = await this.crypto._baseApis.http.authedRequest( const dehydrateResult = await this.crypto.baseApis.http.authedRequest(
undefined, undefined,
"PUT", "PUT",
"/dehydrated_device", "/dehydrated_device",
@@ -223,9 +223,9 @@ export class DehydrationManager {
const deviceId = dehydrateResult.device_id; const deviceId = dehydrateResult.device_id;
logger.log("Preparing device keys", deviceId); logger.log("Preparing device keys", deviceId);
const deviceKeys: DeviceKeys = { const deviceKeys: DeviceKeys = {
algorithms: this.crypto._supportedAlgorithms, algorithms: this.crypto.supportedAlgorithms,
device_id: deviceId, device_id: deviceId,
user_id: this.crypto._userId, user_id: this.crypto.userId,
keys: { keys: {
[`ed25519:${deviceId}`]: e2eKeys.ed25519, [`ed25519:${deviceId}`]: e2eKeys.ed25519,
[`curve25519:${deviceId}`]: e2eKeys.curve25519, [`curve25519:${deviceId}`]: e2eKeys.curve25519,
@@ -233,12 +233,12 @@ export class DehydrationManager {
}; };
const deviceSignature = account.sign(anotherjson.stringify(deviceKeys)); const deviceSignature = account.sign(anotherjson.stringify(deviceKeys));
deviceKeys.signatures = { deviceKeys.signatures = {
[this.crypto._userId]: { [this.crypto.userId]: {
[`ed25519:${deviceId}`]: deviceSignature, [`ed25519:${deviceId}`]: deviceSignature,
}, },
}; };
if (this.crypto._crossSigningInfo.getId("self_signing")) { if (this.crypto.crossSigningInfo.getId("self_signing")) {
await this.crypto._crossSigningInfo.signObject(deviceKeys, "self_signing"); await this.crypto.crossSigningInfo.signObject(deviceKeys, "self_signing");
} }
logger.log("Preparing one-time keys"); logger.log("Preparing one-time keys");
@@ -247,7 +247,7 @@ export class DehydrationManager {
const k: OneTimeKey = { key }; const k: OneTimeKey = { key };
const signature = account.sign(anotherjson.stringify(k)); const signature = account.sign(anotherjson.stringify(k));
k.signatures = { k.signatures = {
[this.crypto._userId]: { [this.crypto.userId]: {
[`ed25519:${deviceId}`]: signature, [`ed25519:${deviceId}`]: signature,
}, },
}; };
@@ -260,7 +260,7 @@ export class DehydrationManager {
const k: OneTimeKey = { key, fallback: true }; const k: OneTimeKey = { key, fallback: true };
const signature = account.sign(anotherjson.stringify(k)); const signature = account.sign(anotherjson.stringify(k));
k.signatures = { k.signatures = {
[this.crypto._userId]: { [this.crypto.userId]: {
[`ed25519:${deviceId}`]: signature, [`ed25519:${deviceId}`]: signature,
}, },
}; };
@@ -268,7 +268,7 @@ export class DehydrationManager {
} }
logger.log("Uploading keys to server"); logger.log("Uploading keys to server");
await this.crypto._baseApis.http.authedRequest( await this.crypto.baseApis.http.authedRequest(
undefined, undefined,
"POST", "POST",
"/keys/upload/" + encodeURI(deviceId), "/keys/upload/" + encodeURI(deviceId),

View File

@@ -1,168 +0,0 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @module crypto/deviceinfo
*/
/**
* Information about a user's device
*
* @constructor
* @alias module:crypto/deviceinfo
*
* @property {string} deviceId the ID of this device
*
* @property {string[]} algorithms list of algorithms supported by this device
*
* @property {Object.<string,string>} keys a map from
* &lt;key type&gt;:&lt;id&gt; -> &lt;base64-encoded key&gt;>
*
* @property {module:crypto/deviceinfo.DeviceVerification} verified
* whether the device has been verified/blocked by the user
*
* @property {boolean} known
* whether the user knows of this device's existence (useful when warning
* the user that a user has added new devices)
*
* @property {Object} unsigned additional data from the homeserver
*
* @param {string} deviceId id of the device
*/
export function DeviceInfo(deviceId) {
// you can't change the deviceId
Object.defineProperty(this, 'deviceId', {
enumerable: true,
value: deviceId,
});
this.algorithms = [];
this.keys = {};
this.verified = DeviceVerification.UNVERIFIED;
this.known = false;
this.unsigned = {};
this.signatures = {};
}
/**
* rehydrate a DeviceInfo from the session store
*
* @param {object} obj raw object from session store
* @param {string} deviceId id of the device
*
* @return {module:crypto~DeviceInfo} new DeviceInfo
*/
DeviceInfo.fromStorage = function(obj, deviceId) {
const res = new DeviceInfo(deviceId);
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
res[prop] = obj[prop];
}
}
return res;
};
/**
* Prepare a DeviceInfo for JSON serialisation in the session store
*
* @return {object} deviceinfo with non-serialised members removed
*/
DeviceInfo.prototype.toStorage = function() {
return {
algorithms: this.algorithms,
keys: this.keys,
verified: this.verified,
known: this.known,
unsigned: this.unsigned,
signatures: this.signatures,
};
};
/**
* Get the fingerprint for this device (ie, the Ed25519 key)
*
* @return {string} base64-encoded fingerprint of this device
*/
DeviceInfo.prototype.getFingerprint = function() {
return this.keys["ed25519:" + this.deviceId];
};
/**
* Get the identity key for this device (ie, the Curve25519 key)
*
* @return {string} base64-encoded identity key of this device
*/
DeviceInfo.prototype.getIdentityKey = function() {
return this.keys["curve25519:" + this.deviceId];
};
/**
* Get the configured display name for this device, if any
*
* @return {string?} displayname
*/
DeviceInfo.prototype.getDisplayName = function() {
return this.unsigned.device_display_name || null;
};
/**
* Returns true if this device is blocked
*
* @return {Boolean} true if blocked
*/
DeviceInfo.prototype.isBlocked = function() {
return this.verified == DeviceVerification.BLOCKED;
};
/**
* Returns true if this device is verified
*
* @return {Boolean} true if verified
*/
DeviceInfo.prototype.isVerified = function() {
return this.verified == DeviceVerification.VERIFIED;
};
/**
* Returns true if this device is unverified
*
* @return {Boolean} true if unverified
*/
DeviceInfo.prototype.isUnverified = function() {
return this.verified == DeviceVerification.UNVERIFIED;
};
/**
* Returns true if the user knows about this device's existence
*
* @return {Boolean} true if known
*/
DeviceInfo.prototype.isKnown = function() {
return this.known == true;
};
/**
* @enum
*/
DeviceInfo.DeviceVerification = {
VERIFIED: 1,
UNVERIFIED: 0,
BLOCKED: -1,
};
const DeviceVerification = DeviceInfo.DeviceVerification;

175
src/crypto/deviceinfo.ts Normal file
View File

@@ -0,0 +1,175 @@
/*
Copyright 2016 - 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @module crypto/deviceinfo
*/
export interface IDevice {
keys: Record<string, string>;
algorithms: string[];
verified: DeviceVerification;
known: boolean;
unsigned?: Record<string, any>;
signatures?: Record<string, string>;
}
enum DeviceVerification {
Blocked = -1,
Unverified = 0,
Verified = 1,
}
/**
* Information about a user's device
*
* @constructor
* @alias module:crypto/deviceinfo
*
* @property {string} deviceId the ID of this device
*
* @property {string[]} algorithms list of algorithms supported by this device
*
* @property {Object.<string,string>} keys a map from
* &lt;key type&gt;:&lt;id&gt; -> &lt;base64-encoded key&gt;>
*
* @property {module:crypto/deviceinfo.DeviceVerification} verified
* whether the device has been verified/blocked by the user
*
* @property {boolean} known
* whether the user knows of this device's existence (useful when warning
* the user that a user has added new devices)
*
* @property {Object} unsigned additional data from the homeserver
*
* @param {string} deviceId id of the device
*/
export class DeviceInfo {
/**
* rehydrate a DeviceInfo from the session store
*
* @param {object} obj raw object from session store
* @param {string} deviceId id of the device
*
* @return {module:crypto~DeviceInfo} new DeviceInfo
*/
public static fromStorage(obj: IDevice, deviceId: string): DeviceInfo {
const res = new DeviceInfo(deviceId);
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
res[prop] = obj[prop];
}
}
return res;
}
/**
* @enum
*/
public static DeviceVerification = {
VERIFIED: DeviceVerification.Verified,
UNVERIFIED: DeviceVerification.Unverified,
BLOCKED: DeviceVerification.Blocked,
};
public algorithms: string[];
public keys: Record<string, string> = {};
public verified = DeviceVerification.Unverified;
public known = false;
public unsigned: Record<string, any> = {};
public signatures: Record<string, string> = {};
constructor(public readonly deviceId: string) {}
/**
* Prepare a DeviceInfo for JSON serialisation in the session store
*
* @return {object} deviceinfo with non-serialised members removed
*/
public toStorage(): IDevice {
return {
algorithms: this.algorithms,
keys: this.keys,
verified: this.verified,
known: this.known,
unsigned: this.unsigned,
signatures: this.signatures,
};
}
/**
* Get the fingerprint for this device (ie, the Ed25519 key)
*
* @return {string} base64-encoded fingerprint of this device
*/
public getFingerprint(): string {
return this.keys["ed25519:" + this.deviceId];
}
/**
* Get the identity key for this device (ie, the Curve25519 key)
*
* @return {string} base64-encoded identity key of this device
*/
public getIdentityKey(): string {
return this.keys["curve25519:" + this.deviceId];
}
/**
* Get the configured display name for this device, if any
*
* @return {string?} displayname
*/
public getDisplayName(): string | null {
return this.unsigned.device_display_name || null;
}
/**
* Returns true if this device is blocked
*
* @return {Boolean} true if blocked
*/
public isBlocked(): boolean {
return this.verified == DeviceVerification.Blocked;
}
/**
* Returns true if this device is verified
*
* @return {Boolean} true if verified
*/
public isVerified(): boolean {
return this.verified == DeviceVerification.Verified;
}
/**
* Returns true if this device is unverified
*
* @return {Boolean} true if unverified
*/
public isUnverified(): boolean {
return this.verified == DeviceVerification.Unverified;
}
/**
* Returns true if the user knows about this device's existence
*
* @return {Boolean} true if known
*/
public isKnown(): boolean {
return this.known === true;
}
}

View File

@@ -29,7 +29,7 @@ import { logger } from '../logger';
import { OlmDevice } from "./OlmDevice"; import { OlmDevice } from "./OlmDevice";
import * as olmlib from "./olmlib"; import * as olmlib from "./olmlib";
import { DeviceList } from "./DeviceList"; import { DeviceList } from "./DeviceList";
import { DeviceInfo } from "./deviceinfo"; import { DeviceInfo, IDevice } from "./deviceinfo";
import * as algorithms from "./algorithms"; import * as algorithms from "./algorithms";
import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from './CrossSigning'; import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from './CrossSigning';
import { EncryptionSetupBuilder } from "./EncryptionSetup"; import { EncryptionSetupBuilder } from "./EncryptionSetup";
@@ -420,9 +420,7 @@ export class Crypto extends EventEmitter {
}; };
myDevices[this.deviceId] = deviceInfo; myDevices[this.deviceId] = deviceInfo;
this.deviceList.storeDevicesForUser( this.deviceList.storeDevicesForUser(this.userId, myDevices);
this.userId, myDevices,
);
this.deviceList.saveIfDirty(); this.deviceList.saveIfDirty();
} }
@@ -922,10 +920,7 @@ export class Crypto extends EventEmitter {
await this.crossSigningInfo.getCrossSigningKeysFromCache(); await this.crossSigningInfo.getCrossSigningKeysFromCache();
// This is writing to in-memory account data in // This is writing to in-memory account data in
// builder.accountDataClientAdapter so won't fail // builder.accountDataClientAdapter so won't fail
await CrossSigningInfo.storeInSecretStorage( await CrossSigningInfo.storeInSecretStorage(crossSigningPrivateKeys, secretStorage);
crossSigningPrivateKeys,
secretStorage,
);
} }
if (setupNewKeyBackup && !keyBackupInfo) { if (setupNewKeyBackup && !keyBackupInfo) {
@@ -1172,7 +1167,7 @@ export class Crypto extends EventEmitter {
// FIXME: do this in batches // FIXME: do this in batches
const users = {}; const users = {};
for (const [userId, crossSigningInfo] for (const [userId, crossSigningInfo]
of Object.entries(this.deviceList._crossSigningInfo)) { of Object.entries(this.deviceList.crossSigningInfo)) {
const upgradeInfo = await this.checkForDeviceVerificationUpgrade( const upgradeInfo = await this.checkForDeviceVerificationUpgrade(
userId, CrossSigningInfo.fromStorage(crossSigningInfo, userId), userId, CrossSigningInfo.fromStorage(crossSigningInfo, userId),
); );
@@ -1248,7 +1243,7 @@ export class Crypto extends EventEmitter {
private async checkForValidDeviceSignature( private async checkForValidDeviceSignature(
userId: string, userId: string,
key: any, // TODO types key: any, // TODO types
devices: Record<string, DeviceInfo>, devices: Record<string, IDevice>,
): Promise<string[]> { ): Promise<string[]> {
const deviceIds: string[] = []; const deviceIds: string[] = [];
if (devices && key.signatures && key.signatures[userId]) { if (devices && key.signatures && key.signatures[userId]) {
@@ -1934,7 +1929,7 @@ export class Crypto extends EventEmitter {
public downloadKeys( public downloadKeys(
userIds: string[], userIds: string[],
forceDownload?: boolean, forceDownload?: boolean,
): Promise<Record<string, Record<string, DeviceInfo>>> { ): Promise<Record<string, Record<string, IDevice>>> {
return this.deviceList.downloadKeys(userIds, forceDownload); return this.deviceList.downloadKeys(userIds, forceDownload);
} }
@@ -2003,7 +1998,7 @@ export class Crypto extends EventEmitter {
verified?: boolean, verified?: boolean,
blocked?: boolean, blocked?: boolean,
known?: boolean, known?: boolean,
): Promise<DeviceInfo> { ): Promise<DeviceInfo | CrossSigningInfo> {
// get rid of any `undefined`s here so we can just check // get rid of any `undefined`s here so we can just check
// for null rather than null or undefined // for null rather than null or undefined
if (verified === undefined) verified = null; if (verified === undefined) verified = null;
@@ -2068,7 +2063,7 @@ export class Crypto extends EventEmitter {
// This will emit events when it comes back down the sync // This will emit events when it comes back down the sync
// (we could do local echo to speed things up) // (we could do local echo to speed things up)
} }
return device; return device as any; // TODO types
} else { } else {
return xsk; return xsk;
} }

View File

@@ -1,6 +1,5 @@
/* /*
Copyright 2018 New Vector Ltd Copyright 2018 - 2021 The Matrix.org Foundation C.I.C.
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,7 +20,21 @@ const DEFAULT_ITERATIONS = 500000;
const DEFAULT_BITSIZE = 256; const DEFAULT_BITSIZE = 256;
export async function keyFromAuthData(authData, password) { /* eslint-disable camelcase */
interface IAuthData {
private_key_salt: string;
private_key_iterations: number;
private_key_bits?: number;
}
/* eslint-enable camelcase */
interface IKey {
key: Uint8Array;
salt: string;
iterations: number
}
export async function keyFromAuthData(authData: IAuthData, password: string): Promise<Uint8Array> {
if (!global.Olm) { if (!global.Olm) {
throw new Error("Olm is not available"); throw new Error("Olm is not available");
} }
@@ -40,7 +53,7 @@ export async function keyFromAuthData(authData, password) {
); );
} }
export async function keyFromPassphrase(password) { export async function keyFromPassphrase(password: string): Promise<IKey> {
if (!global.Olm) { if (!global.Olm) {
throw new Error("Olm is not available"); throw new Error("Olm is not available");
} }
@@ -52,7 +65,12 @@ export async function keyFromPassphrase(password) {
return { key, salt, iterations: DEFAULT_ITERATIONS }; return { key, salt, iterations: DEFAULT_ITERATIONS };
} }
export async function deriveKey(password, salt, iterations, numBits = DEFAULT_BITSIZE) { export async function deriveKey(
password: string,
salt: string,
iterations: number,
numBits = DEFAULT_BITSIZE,
): Promise<Uint8Array> {
const subtleCrypto = global.crypto.subtle; const subtleCrypto = global.crypto.subtle;
const TextEncoder = global.TextEncoder; const TextEncoder = global.TextEncoder;
if (!subtleCrypto || !TextEncoder) { if (!subtleCrypto || !TextEncoder) {

View File

@@ -31,17 +31,22 @@ export interface IKeyBackupRoomSessions {
[sessionId: string]: IKeyBackupSession; [sessionId: string]: IKeyBackupSession;
} }
/* eslint-disable camelcase */
export interface IKeyBackupVersion { export interface IKeyBackupVersion {
algorithm: string; algorithm: string;
auth_data: { // eslint-disable-line camelcase auth_data: {
public_key: string; // eslint-disable-line camelcase public_key: string;
signatures: ISignatures; signatures: ISignatures;
private_key_salt: string;
private_key_iterations: number;
private_key_bits?: number;
}; };
count: number; count: number;
etag: string; etag: string;
version: string; // number contained within version: string; // number contained within
recovery_key: string; // eslint-disable-line camelcase recovery_key: string;
} }
/* eslint-enable camelcase */
export interface IKeyBackupPrepareOpts { export interface IKeyBackupPrepareOpts {
secureSecretStorage: boolean; secureSecretStorage: boolean;

View File

@@ -292,7 +292,7 @@ export class VerificationBase extends EventEmitter {
await verifier(keyId, device, keyInfo); await verifier(keyId, device, keyInfo);
verifiedDevices.push(deviceId); verifiedDevices.push(deviceId);
} else { } else {
const crossSigningInfo = this._baseApis.crypto._deviceList const crossSigningInfo = this._baseApis.crypto.deviceList
.getStoredCrossSigningForUser(userId); .getStoredCrossSigningForUser(userId);
if (crossSigningInfo && crossSigningInfo.getId() === deviceId) { if (crossSigningInfo && crossSigningInfo.getId() === deviceId) {
await verifier(keyId, DeviceInfo.fromStorage({ await verifier(keyId, DeviceInfo.fromStorage({

View File

@@ -19,6 +19,7 @@ import { MemoryStore } from "./store/memory";
import { MatrixScheduler } from "./scheduler"; import { MatrixScheduler } from "./scheduler";
import { MatrixClient } from "./client"; import { MatrixClient } from "./client";
import { ICreateClientOpts } from "./client"; import { ICreateClientOpts } from "./client";
import { DeviceTrustLevel } from "./crypto/CrossSigning";
export * from "./client"; export * from "./client";
export * from "./http-api"; export * from "./http-api";
@@ -99,7 +100,7 @@ export function setCryptoStoreFactory(fac) {
} }
export interface ICryptoCallbacks { export interface ICryptoCallbacks {
getCrossSigningKey?: (keyType: string, pubKey: Uint8Array) => Promise<Uint8Array>; getCrossSigningKey?: (keyType: string, pubKey: string) => Promise<Uint8Array>;
saveCrossSigningKeys?: (keys: Record<string, Uint8Array>) => void; saveCrossSigningKeys?: (keys: Record<string, Uint8Array>) => void;
shouldUpgradeDeviceVerifications?: ( shouldUpgradeDeviceVerifications?: (
users: Record<string, any> users: Record<string, any>
@@ -112,7 +113,7 @@ export interface ICryptoCallbacks {
) => void; ) => void;
onSecretRequested?: ( onSecretRequested?: (
userId: string, deviceId: string, userId: string, deviceId: string,
requestId: string, secretName: string, deviceTrust: IDeviceTrustLevel requestId: string, secretName: string, deviceTrust: DeviceTrustLevel
) => Promise<string>; ) => Promise<string>;
getDehydrationKey?: ( getDehydrationKey?: (
keyInfo: ISecretStorageKeyInfo, keyInfo: ISecretStorageKeyInfo,
@@ -132,14 +133,6 @@ export interface ISecretStorageKeyInfo {
mac?: string; mac?: string;
} }
// TODO: Move this to `CrossSigning` once converted
export interface IDeviceTrustLevel {
isVerified(): boolean;
isCrossSigningVerified(): boolean;
isLocallyVerified(): boolean;
isTofu(): boolean;
}
/** /**
* Construct a Matrix Client. Similar to {@link module:client.MatrixClient} * Construct a Matrix Client. Similar to {@link module:client.MatrixClient}
* except that the 'request', 'store' and 'scheduler' dependencies are satisfied. * except that the 'request', 'store' and 'scheduler' dependencies are satisfied.

View File

@@ -435,12 +435,18 @@ export function isNullOrUndefined(val: any): boolean {
return val === null || val === undefined; return val === null || val === undefined;
} }
export interface IDeferred<T> {
resolve: (value: T) => void;
reject: (any) => void;
promise: Promise<T>;
}
// Returns a Deferred // Returns a Deferred
export function defer() { export function defer<T>(): IDeferred<T> {
let resolve; let resolve;
let reject; let reject;
const promise = new Promise((_resolve, _reject) => { const promise = new Promise<T>((_resolve, _reject) => {
resolve = _resolve; resolve = _resolve;
reject = _reject; reject = _reject;
}); });