Skip to content

Commit 9cfd907

Browse files
committed
Actually use sent_outbound_sessions table
1 parent f58d7ea commit 9cfd907

File tree

2 files changed

+119
-15
lines changed

2 files changed

+119
-15
lines changed

src/e2ee/CryptoClient.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,15 @@ export class CryptoClient {
478478
try {
479479
session.unpickle(this.pickleKey, currentSession.pickled);
480480

481+
const encrypted = session.encrypt(JSON.stringify({
482+
type: eventType,
483+
content: content,
484+
room_id: roomId,
485+
}));
486+
487+
currentSession.pickled = session.pickle(this.pickleKey);
488+
currentSession.usesLeft--;
489+
481490
const neededSessions: Record<string, string[]> = {};
482491
for (const userId of Object.keys(devices)) {
483492
neededSessions[userId] = devices[userId].map(d => d.device_id);
@@ -491,23 +500,19 @@ export class CryptoClient {
491500
LogService.warn("CryptoClient", `Unable to send Megolm session to ${userId} ${device.device_id}: No Olm session`);
492501
continue;
493502
}
494-
await this.encryptAndSendOlmMessage(device, olmSession, "m.room_key", <IMRoomKey>{
495-
algorithm: EncryptionAlgorithm.MegolmV1AesSha2,
496-
room_id: roomId,
497-
session_id: session.session_id(),
498-
session_key: session.session_key(),
499-
});
503+
const lastSession = await this.client.cryptoStore.getLastSentOutboundGroupSession(userId, device.device_id, roomId);
504+
if (lastSession?.sessionId !== session.session_id() || session.message_index() <= (lastSession?.index ?? Number.MAX_SAFE_INTEGER)) {
505+
await this.encryptAndSendOlmMessage(device, olmSession, "m.room_key", <IMRoomKey>{
506+
algorithm: EncryptionAlgorithm.MegolmV1AesSha2,
507+
room_id: roomId,
508+
session_id: session.session_id(),
509+
session_key: session.session_key(),
510+
});
511+
await this.client.cryptoStore.storeSentOutboundGroupSession(currentSession, session.message_index(), device);
512+
}
500513
}
501514
}
502515

503-
const encrypted = session.encrypt(JSON.stringify({
504-
type: eventType,
505-
content: content,
506-
room_id: roomId,
507-
}));
508-
509-
currentSession.pickled = session.pickle(this.pickleKey);
510-
currentSession.usesLeft--;
511516
await this.client.cryptoStore.storeOutboundGroupSession(currentSession);
512517

513518
const body = {

test/encryption/CryptoClientTest.ts

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1479,7 +1479,7 @@ describe('CryptoClient', () => {
14791479
ciphertext: {
14801480
"30KcbZc4ZmLxnLu3MraQ9vIrAjwtjR8uYmwCU/sViDE": {
14811481
type: 0,
1482-
body: "Awog+jA+wNz5Wnpw5isETy9LFDw0hoao06f7ewAhY0+yRGsSIJS/3l725T7pqoV3FKZY/cPH/2dV8W8yZeIWl1DKpaQlGiAFnYCGBRA+tqaR3SpDqbqtwgz1wzA0TV+Mjvzixbd1IyLgBQMKIAIldXBMsoIngiQkuLAvUYrz6QCFAwPeFb6hKlRKcBlTEAAisAWgrDGnYPaJv4asMwVsbNSXQOxRCE/sB0VZrYKH9OKwbZuP+jqHUPa6mtVBu3Sll2ROWJ94YtPycZXX45B4pT8XMvLL/jE6fH4gXZuheb6Q5iYV0XrHMNuIzyODjzbOzpvi7GXTFvb7YMFRskb2k965vfd9NRTpuUT9eb7vkLoIgCb9gK5WApEuS5/4lOIWHKdhqB1m4ViZ4W+eEo9TzniRvAMCfeX0G+OpCv5X9h1UomZl87Kh/q5ZSluuocWFOgG8sGvyLttl3AR3Vc500+9xc0u7GT6lNvJo9Z1kH1xPcCce4oHWByFgGvdIMHYrB7SFZ/AtbiQDt/BUTgxsLd8gysHqjiiOKblz3iN3kx//f2MCTrjKgWDtmCeTRnb1Z8Rn9hdPbkpX2+yvkrmdMYYXKfQXB6PAY+6gRFqGREFXaKq8n0NPN7mN//sp7CJGmMU+DIyq7cPWcmW7zLTBdyoafn8YkJRqjIVbA271imw77cFvDdU1uWFT14275u7Z0qtOrXZiuDLPQyaARbitv8Cc4VfFB1XwWG0V8+fR3oJvIcCba4Q7ALO6TJqpurETU6eT4BAZBmugWObL2kDxdmuJYWpKvKbPdGhLTfbFFn0Sl1lgNaMrGjDoF+LVx/1Oiq9s0DnKPf9gamGIYr2voiSQvibC5m4UgMKLkiZVbAVs20fSV3TD5XMJYman6Rk8mNHBd+6fXW+C2buXd8WStiZ2/hVNalvV/MJPqdzJDHRz3avjwJryunbO48syLMud0y+6K2e8RJV/974lyfQ6BvJ/C7pN/rY3Rh5F4NtG0pSL9ghBzKuQQvKuVGf7U8L9w52iRQrPso+UhUkn8kpLD6AWklU7o9NenWO7eQLhz33i/A0DnM3ILw0c5XyQrX7/UgIRHkLAeVMHLmYC4IBaY1Y24ToFuVKXdb0",
1482+
body: "Awog+jA+wNz5Wnpw5isETy9LFDw0hoao06f7ewAhY0+yRGsSIJS/3l725T7pqoV3FKZY/cPH/2dV8W8yZeIWl1DKpaQlGiAFnYCGBRA+tqaR3SpDqbqtwgz1wzA0TV+Mjvzixbd1IyLgBQMKIAIldXBMsoIngiQkuLAvUYrz6QCFAwPeFb6hKlRKcBlTEAAisAWgrDGnYPaJv4asMwVsbNSXQOxRCE/sB0VZrYKH9OKwbZuP+jqHUPa6mtVBu3Sll2ROWJ94YtPycZXX45B4pT8XMvLL/jE6fH4gXZuheb6Q5iYV0XrHMNuIzyODjzbOzpvi7GXTFvb7YMFRskb2k965vfd9NRTpuUT9eb7vkLoIgCb9gK5WApEuS5/4lOIWHKdhqB1m4ViZ4W+eEo9TzniRvAMCfeX0G+OpCv5X9h1UomZl87Kh/q5ZSluuocWFOgG8sGvyLttl3AR3Vc500+9xc0u7GT6lNvJo9Z1kH1xPcCce4oHWByFgGvdIMHYrB7SFZ/AtbiQDt/BUTgxsLd8gysHqjiiOKblz3iN3kx//f2MCTrjKgWDtmCeTRnb1Z8Rn9hdPbkpX2+yvkrmdMYYXKfQXB6PAY+6gRFqGREFXaKq8n0NPN7mN//sp7CJGmMU+DIyq7cPWcmW7zLTBdyoak0/EBQdCIXabvl9B3kfK32xEvn6BH7kFt1ayXUAGl6W/e8uzdKnkRvmnAT7yG147iKOT4DgW6a+msibvSZ2bOzzUxoMbYrdrX7OCBjS92e6IKDJ9mD8yi5apvcMnwS4AGw2U64hkG83U7lpp55tN2kPxLHpAmauQ51cNOZAt5bVPKOgUHCQD02Z1XgptdBjPOCCLaKDyoUawLDLKb8mWojiPZ+2/c6+ODeybYzCrDA2b681wo0WpvcROL0DuOb+1r1Po7AKy/tKUz2VJXTFGGergopp1XJwf7hMeur95J4hBdaCaMTSqWHvkNaIWrj/AZVFeVEZREKgl5x5DycMP6tzv5dX9M3gAcJcfvcU+ws4kqMyM+RsqI7ztB7tKu1CmQYNemHXH53ExuRz1FhBpgS6T/j2RQswLYLxVRGAgGrvi0FWTI8aBrAjUd6FyzDcanHUP2utinWs",
14831483
},
14841484
},
14851485
sender_key: "BZ2AhgUQPramkd0qQ6m6rcIM9cMwNE1fjI784sW3dSM",
@@ -1854,6 +1854,105 @@ describe('CryptoClient', () => {
18541854
device_id: TEST_DEVICE_ID,
18551855
});
18561856
});
1857+
1858+
it('should not spam room keys for multiple calls', async () => {
1859+
await client.crypto.prepare([]);
1860+
1861+
const deviceMap = {
1862+
[RECEIVER_DEVICE.user_id]: [RECEIVER_DEVICE],
1863+
};
1864+
const roomId = "!test:example.org";
1865+
1866+
// For this test, force all rooms to be encrypted
1867+
client.crypto.isRoomEncrypted = async () => true;
1868+
1869+
await client.cryptoStore.storeOlmSession(RECEIVER_DEVICE.user_id, RECEIVER_DEVICE.device_id, RECEIVER_OLM_SESSION);
1870+
1871+
const getSpy = simple.stub().callFn(async (rid) => {
1872+
expect(rid).toEqual(roomId);
1873+
return STATIC_OUTBOUND_SESSION;
1874+
});
1875+
client.cryptoStore.getCurrentOutboundGroupSession = getSpy;
1876+
1877+
const joinedSpy = simple.stub().callFn(async (rid) => {
1878+
expect(rid).toEqual(roomId);
1879+
return Object.keys(deviceMap);
1880+
});
1881+
client.getJoinedRoomMembers = joinedSpy;
1882+
1883+
const devicesSpy = simple.stub().callFn(async (uids) => {
1884+
expect(uids).toMatchObject(Object.keys(deviceMap));
1885+
return deviceMap;
1886+
});
1887+
(<any>client.crypto).deviceTracker.getDevicesFor = devicesSpy;
1888+
1889+
// We watch for the to-device messages to make sure we pass through the internal functions correctly
1890+
const toDeviceSpy = simple.stub().callFn(async (t, m) => {
1891+
expect(t).toEqual("m.room.encrypted");
1892+
expect(m).toMatchObject({
1893+
[RECEIVER_DEVICE.user_id]: {
1894+
[RECEIVER_DEVICE.device_id]: {
1895+
algorithm: "m.olm.v1.curve25519-aes-sha2",
1896+
ciphertext: {
1897+
"30KcbZc4ZmLxnLu3MraQ9vIrAjwtjR8uYmwCU/sViDE": {
1898+
type: 0,
1899+
body: expect.any(String),
1900+
},
1901+
},
1902+
sender_key: "BZ2AhgUQPramkd0qQ6m6rcIM9cMwNE1fjI784sW3dSM",
1903+
},
1904+
},
1905+
});
1906+
});
1907+
client.sendToDevices = toDeviceSpy;
1908+
1909+
const result = await client.crypto.encryptRoomEvent(roomId, "org.example.test", {
1910+
isTest: true,
1911+
hello: "world",
1912+
n: 42,
1913+
});
1914+
expect(getSpy.callCount).toBe(1);
1915+
expect(joinedSpy.callCount).toBe(1);
1916+
expect(devicesSpy.callCount).toBe(1);
1917+
expect(toDeviceSpy.callCount).toBe(1);
1918+
expect(result).toMatchObject({
1919+
algorithm: "m.megolm.v1.aes-sha2",
1920+
sender_key: "BZ2AhgUQPramkd0qQ6m6rcIM9cMwNE1fjI784sW3dSM",
1921+
ciphertext: expect.any(String),
1922+
session_id: STATIC_OUTBOUND_SESSION.sessionId,
1923+
device_id: TEST_DEVICE_ID,
1924+
});
1925+
1926+
const lastSent = await client.cryptoStore.getLastSentOutboundGroupSession(RECEIVER_DEVICE.user_id, RECEIVER_DEVICE.device_id, roomId);
1927+
expect(lastSent).toMatchObject({
1928+
sessionId: STATIC_OUTBOUND_SESSION.sessionId,
1929+
index: expect.any(Number),
1930+
});
1931+
1932+
const result2 = await client.crypto.encryptRoomEvent(roomId, "org.example.test", {
1933+
isTest: true,
1934+
hello: "world",
1935+
n: 42,
1936+
});
1937+
expect(getSpy.callCount).toBe(2);
1938+
expect(joinedSpy.callCount).toBe(2);
1939+
expect(devicesSpy.callCount).toBe(2);
1940+
expect(toDeviceSpy.callCount).toBe(1);
1941+
expect(result2).toMatchObject({
1942+
algorithm: "m.megolm.v1.aes-sha2",
1943+
sender_key: "BZ2AhgUQPramkd0qQ6m6rcIM9cMwNE1fjI784sW3dSM",
1944+
ciphertext: expect.any(String),
1945+
session_id: STATIC_OUTBOUND_SESSION.sessionId,
1946+
device_id: TEST_DEVICE_ID,
1947+
});
1948+
1949+
const lastSent2 = await client.cryptoStore.getLastSentOutboundGroupSession(RECEIVER_DEVICE.user_id, RECEIVER_DEVICE.device_id, roomId);
1950+
expect(lastSent2).toMatchObject({
1951+
sessionId: STATIC_OUTBOUND_SESSION.sessionId,
1952+
index: expect.any(Number),
1953+
});
1954+
expect(lastSent2.index).toEqual(lastSent.index);
1955+
});
18571956
});
18581957

18591958
describe('processInboundDeviceMessage', () => {

0 commit comments

Comments
 (0)