CVE-2021-40823
Description
A logic error in the room key sharing functionality of matrix-js-sdk (aka Matrix Javascript SDK) before 12.4.1 allows a malicious Matrix homeserver present in an encrypted room to steal room encryption keys (via crafted Matrix protocol messages) that were originally sent by affected Matrix clients participating in that room. This allows the homeserver to decrypt end-to-end encrypted messages sent by affected clients.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A logic error in matrix-js-sdk before 12.4.1 allows a malicious homeserver to steal room encryption keys, breaking E2EE.
Vulnerability
A logic error in the room key sharing functionality of matrix-js-sdk (Matrix JavaScript SDK) before version 12.4.1 allows a malicious Matrix homeserver present in an encrypted room to steal room encryption keys via crafted Matrix protocol messages [1][2][3]. The bug occurs in the OutboundSessionInfo class where the markSharedWithDevice method did not verify the target device's identity key, allowing a homeserver to impersonate a user's device [4].
Exploitation
An attacker must control a Matrix homeserver that is a member of an encrypted room. The attacker can then send crafted Matrix protocol messages to a vulnerable client, tricking it into sharing encryption keys for messages previously sent by that client to a compromised user account [2]. The attack requires prior compromise of the recipient's account or homeserver credentials [2].
Impact
Successful exploitation allows the malicious homeserver to decrypt end-to-end encrypted messages sent by affected clients in that room [1][2]. This breaks the confidentiality guarantees of Matrix's E2EE, potentially exposing all past and future messages in the room.
Mitigation
The vulnerability is fixed in matrix-js-sdk version 12.4.1, released on 2021-09-13 [2][4]. Users should upgrade immediately. If unable to upgrade, keep vulnerable clients offline to prevent key disclosure [2]. No workaround exists other than patching. There is no evidence of exploitation in the wild [2].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
matrix-js-sdknpm | < 12.4.1 | 12.4.1 |
Affected products
2- matrix-js-sdk/matrix-js-sdkdescription
Patches
1894c24880da0Verify target device key on reshare
1 file changed · +29 −9
src/crypto/algorithms/megolm.ts+29 −9 modified@@ -101,6 +101,13 @@ interface IPayload extends Partial<IMessage> { } /* eslint-enable camelcase */ +interface SharedWithData { + // The identity key of the device we shared with + deviceKey: string; + // The message index of the ratchet we shared with that device + messageIndex: number; +} + /** * @private * @constructor @@ -115,12 +122,12 @@ interface IPayload extends Partial<IMessage> { * * @property {object} sharedWithDevices * devices with which we have shared the session key - * userId -> {deviceId -> msgindex} + * userId -> {deviceId -> SharedWithData} */ class OutboundSessionInfo { public useCount = 0; public creationTime: number; - public sharedWithDevices: Record<string, Record<string, number>> = {}; + public sharedWithDevices: Record<string, Record<string, SharedWithData>> = {}; public blockedDevicesNotified: Record<string, Record<string, boolean>> = {}; constructor(public readonly sessionId: string, public readonly sharedHistory = false) { @@ -150,11 +157,11 @@ class OutboundSessionInfo { return false; } - public markSharedWithDevice(userId: string, deviceId: string, chainIndex: number): void { + public markSharedWithDevice(userId: string, deviceId: string, deviceKey: string, chainIndex: number): void { if (!this.sharedWithDevices[userId]) { this.sharedWithDevices[userId] = {}; } - this.sharedWithDevices[userId][deviceId] = chainIndex; + this.sharedWithDevices[userId][deviceId] = { deviceKey, messageIndex: chainIndex }; } public markNotifiedBlockedDevice(userId: string, deviceId: string): void { @@ -572,6 +579,7 @@ class MegolmEncryption extends EncryptionAlgorithm { payload: IPayload, ): Promise<void> { const contentMap = {}; + const deviceInfoByDeviceId = new Map<string, DeviceInfo>(); const promises = []; for (let i = 0; i < userDeviceMap.length; i++) { @@ -584,6 +592,7 @@ class MegolmEncryption extends EncryptionAlgorithm { const userId = val.userId; const deviceInfo = val.deviceInfo; const deviceId = deviceInfo.deviceId; + deviceInfoByDeviceId.set(deviceId, deviceInfo); if (!contentMap[userId]) { contentMap[userId] = {}; @@ -636,7 +645,10 @@ class MegolmEncryption extends EncryptionAlgorithm { for (const userId of Object.keys(contentMap)) { for (const deviceId of Object.keys(contentMap[userId])) { session.markSharedWithDevice( - userId, deviceId, chainIndex, + userId, + deviceId, + deviceInfoByDeviceId.get(deviceId).getIdentityKey(), + chainIndex, ); } } @@ -719,19 +731,27 @@ class MegolmEncryption extends EncryptionAlgorithm { logger.debug(`megolm session ${sessionId} never shared with user ${userId}`); return; } - const sentChainIndex = obSessionInfo.sharedWithDevices[userId][device.deviceId]; - if (sentChainIndex === undefined) { + const sessionSharedData = obSessionInfo.sharedWithDevices[userId][device.deviceId]; + if (sessionSharedData === undefined) { logger.debug( "megolm session ID " + sessionId + " never shared with device " + userId + ":" + device.deviceId, ); return; } + if (sessionSharedData.deviceKey !== device.getIdentityKey()) { + logger.warn( + `Session has been shared with device ${device.deviceId} but with identity ` + + `key ${sessionSharedData.deviceKey}. Key is now ${device.getIdentityKey()}!`, + ); + return; + } + // get the key from the inbound session: the outbound one will already // have been ratcheted to the next chain index. const key = await this.olmDevice.getInboundGroupSessionKey( - this.roomId, senderKey, sessionId, sentChainIndex, + this.roomId, senderKey, sessionId, sessionSharedData.messageIndex, ); if (!key) { @@ -882,7 +902,7 @@ class MegolmEncryption extends EncryptionAlgorithm { const deviceId = deviceInfo.deviceId; session.markSharedWithDevice( - userId, deviceId, key.chain_index, + userId, deviceId, deviceInfo.getIdentityKey(), key.chain_index, ); }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-23cm-x6j7-6hq3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-40823ghsaADVISORY
- github.com/matrix-org/matrix-js-sdk/commit/894c24880da0e1cc81818f51c0db80e3c9fb2be9ghsaWEB
- github.com/matrix-org/matrix-js-sdk/releases/tag/v12.4.1ghsax_refsource_MISCWEB
- github.com/matrix-org/matrix-js-sdk/security/advisories/GHSA-23cm-x6j7-6hq3ghsaWEB
- matrix.org/blog/2021/09/13/vulnerability-disclosure-key-sharingghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.