You've already forked matrix-js-sdk
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:
@@ -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'];
|
||||||
|
@@ -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];
|
||||||
|
@@ -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);
|
||||||
|
@@ -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,
|
||||||
|
@@ -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);
|
||||||
|
@@ -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: {
|
||||||
|
@@ -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(),
|
||||||
);
|
);
|
||||||
|
@@ -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);
|
||||||
});
|
});
|
||||||
|
@@ -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", {});
|
||||||
|
@@ -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",
|
||||||
|
@@ -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: {
|
||||||
|
@@ -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,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -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(
|
||||||
|
@@ -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
@@ -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) {
|
||||||
|
@@ -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);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
@@ -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(),
|
||||||
);
|
);
|
||||||
|
@@ -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),
|
||||||
|
@@ -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
|
|
||||||
* <key type>:<id> -> <base64-encoded key>>
|
|
||||||
*
|
|
||||||
* @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
175
src/crypto/deviceinfo.ts
Normal 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
|
||||||
|
* <key type>:<id> -> <base64-encoded key>>
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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) {
|
@@ -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;
|
||||||
|
@@ -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({
|
||||||
|
@@ -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.
|
||||||
|
10
src/utils.ts
10
src/utils.ts
@@ -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;
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user