You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-23 17:02:25 +03:00
Refactoring and simplification in decryption error handling (#4138)
* Clean up decryption failure integ tests * Fix the names * Stop waiting as soon as the event is decrypted, even if code is wrong (so tests fail rather than time out if the code is wrong) * Bump timeouts on some tests These tend to fail due to slow init of wasm artifacts * Factor out `onDecryptionKeyMissingError` call * Factor out `onMegolmDecryptionError`
This commit is contained in:
committed by
GitHub
parent
cfcd191cbf
commit
dbab185f9d
@@ -81,7 +81,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(
|
||||
async () => {
|
||||
// anything that we don't have a specific matcher for silently returns a 404
|
||||
fetchMock.catch(404);
|
||||
fetchMock.config.warnOnFallback = false;
|
||||
@@ -107,7 +108,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s
|
||||
});
|
||||
|
||||
await initCrypto(aliceClient);
|
||||
});
|
||||
},
|
||||
/* it can take a while to initialise the crypto library on the first pass, so bump up the timeout. */
|
||||
10000,
|
||||
);
|
||||
|
||||
afterEach(async () => {
|
||||
await aliceClient.stopClient();
|
||||
|
||||
@@ -26,6 +26,7 @@ import * as testUtils from "../../test-utils/test-utils";
|
||||
import {
|
||||
advanceTimersUntil,
|
||||
CRYPTO_BACKENDS,
|
||||
emitPromise,
|
||||
getSyncResponse,
|
||||
InitCrypto,
|
||||
mkEventCustom,
|
||||
@@ -466,19 +467,13 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
|
||||
});
|
||||
|
||||
describe("Unable to decrypt error codes", function () {
|
||||
it("Encryption fails with expected UISI error", async () => {
|
||||
it("Decryption fails with UISI error", async () => {
|
||||
expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
|
||||
await startClientAndAwaitFirstSync();
|
||||
|
||||
const awaitUISI = new Promise<void>((resolve) => {
|
||||
aliceClient.on(MatrixEventEvent.Decrypted, (ev) => {
|
||||
if (ev.decryptionFailureReason === DecryptionFailureCode.MEGOLM_UNKNOWN_INBOUND_SESSION_ID) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
// A promise which resolves, with the MatrixEvent which wraps the event, once the decryption fails.
|
||||
const awaitDecryption = emitPromise(aliceClient, MatrixEventEvent.Decrypted);
|
||||
|
||||
// Alice gets both the events in a single sync
|
||||
const syncResponse = {
|
||||
next_batch: 1,
|
||||
rooms: {
|
||||
@@ -490,21 +485,16 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
|
||||
|
||||
syncResponder.sendOrQueueSyncResponse(syncResponse);
|
||||
await syncPromise(aliceClient);
|
||||
|
||||
await awaitUISI;
|
||||
const ev = await awaitDecryption;
|
||||
expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.MEGOLM_UNKNOWN_INBOUND_SESSION_ID);
|
||||
});
|
||||
|
||||
it("Encryption fails with expected Unknown Index error", async () => {
|
||||
it("Decryption fails with Unknown Index error", async () => {
|
||||
expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
|
||||
await startClientAndAwaitFirstSync();
|
||||
|
||||
const awaitUnknownIndex = new Promise<void>((resolve) => {
|
||||
aliceClient.on(MatrixEventEvent.Decrypted, (ev) => {
|
||||
if (ev.decryptionFailureReason === DecryptionFailureCode.OLM_UNKNOWN_MESSAGE_INDEX) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
// A promise which resolves, with the MatrixEvent which wraps the event, once the decryption fails.
|
||||
const awaitDecryption = emitPromise(aliceClient, MatrixEventEvent.Decrypted);
|
||||
|
||||
await aliceClient.getCrypto()!.importRoomKeys([testData.RATCHTED_MEGOLM_SESSION_DATA]);
|
||||
|
||||
@@ -521,10 +511,11 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
|
||||
syncResponder.sendOrQueueSyncResponse(syncResponse);
|
||||
await syncPromise(aliceClient);
|
||||
|
||||
await awaitUnknownIndex;
|
||||
const ev = await awaitDecryption;
|
||||
expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.OLM_UNKNOWN_MESSAGE_INDEX);
|
||||
});
|
||||
|
||||
it("Encryption fails with Unable to decrypt for other errors", async () => {
|
||||
it("Decryption fails with Unable to decrypt for other errors", async () => {
|
||||
expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
|
||||
await startClientAndAwaitFirstSync();
|
||||
|
||||
|
||||
@@ -192,7 +192,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(
|
||||
async () => {
|
||||
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA);
|
||||
|
||||
// ignore requests to send room key requests
|
||||
@@ -213,7 +214,9 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe
|
||||
await waitForDeviceList();
|
||||
await aliceClient.getCrypto()!.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID);
|
||||
await aliceClient.getCrypto()!.checkKeyBackupAndEnable();
|
||||
});
|
||||
} /* it can take a while to initialise the crypto library on the first pass, so bump up the timeout. */,
|
||||
10000,
|
||||
);
|
||||
|
||||
it("Alice checks key backups when receiving a message she can't decrypt", async () => {
|
||||
fetchMock.get("express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id", (url, request) => {
|
||||
|
||||
@@ -1683,52 +1683,50 @@ class EventDecryptor {
|
||||
forwardingCurve25519KeyChain: res.forwardingCurve25519KeyChain,
|
||||
};
|
||||
} catch (err) {
|
||||
// We need to map back to regular decryption errors (used for analytics for example)
|
||||
// The DecryptionErrors are used by react-sdk so is implicitly part of API, but poorly typed
|
||||
if (err instanceof RustSdkCryptoJs.MegolmDecryptionError) {
|
||||
this.onMegolmDecryptionError(event, err);
|
||||
} else {
|
||||
throw new DecryptionError(DecryptionFailureCode.UNKNOWN_ERROR, "Unknown error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a `MegolmDecryptionError` returned by the rust SDK.
|
||||
*
|
||||
* Fires off a request to the `perSessionBackupDownloader`, if appropriate, and then throws a `DecryptionError`.
|
||||
*/
|
||||
private onMegolmDecryptionError(event: MatrixEvent, err: RustSdkCryptoJs.MegolmDecryptionError): never {
|
||||
const content = event.getWireContent();
|
||||
let jsError;
|
||||
|
||||
// If the error looks like it might be recoverable from backup, queue up a request to try that.
|
||||
if (
|
||||
err.code === RustSdkCryptoJs.DecryptionErrorCode.MissingRoomKey ||
|
||||
err.code === RustSdkCryptoJs.DecryptionErrorCode.UnknownMessageIndex
|
||||
) {
|
||||
this.perSessionBackupDownloader.onDecryptionKeyMissingError(event.getRoomId()!, content.session_id!);
|
||||
}
|
||||
|
||||
const errorDetails = { session: content.sender_key + "|" + content.session_id };
|
||||
switch (err.code) {
|
||||
case RustSdkCryptoJs.DecryptionErrorCode.MissingRoomKey: {
|
||||
jsError = new DecryptionError(
|
||||
case RustSdkCryptoJs.DecryptionErrorCode.MissingRoomKey:
|
||||
throw new DecryptionError(
|
||||
DecryptionFailureCode.MEGOLM_UNKNOWN_INBOUND_SESSION_ID,
|
||||
"The sender's device has not sent us the keys for this message.",
|
||||
{
|
||||
session: content.sender_key + "|" + content.session_id,
|
||||
},
|
||||
errorDetails,
|
||||
);
|
||||
this.perSessionBackupDownloader.onDecryptionKeyMissingError(
|
||||
event.getRoomId()!,
|
||||
event.getWireContent().session_id!,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case RustSdkCryptoJs.DecryptionErrorCode.UnknownMessageIndex: {
|
||||
jsError = new DecryptionError(
|
||||
|
||||
case RustSdkCryptoJs.DecryptionErrorCode.UnknownMessageIndex:
|
||||
throw new DecryptionError(
|
||||
DecryptionFailureCode.OLM_UNKNOWN_MESSAGE_INDEX,
|
||||
"The sender's device has not sent us the keys for this message at this index.",
|
||||
{
|
||||
session: content.sender_key + "|" + content.session_id,
|
||||
},
|
||||
errorDetails,
|
||||
);
|
||||
this.perSessionBackupDownloader.onDecryptionKeyMissingError(
|
||||
event.getRoomId()!,
|
||||
event.getWireContent().session_id!,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// We don't map MismatchedIdentityKeys for now, as there is no equivalent in legacy.
|
||||
// Just put it on the `UNKNOWN_ERROR` bucket.
|
||||
default: {
|
||||
jsError = new DecryptionError(DecryptionFailureCode.UNKNOWN_ERROR, err.description, {
|
||||
session: content.sender_key + "|" + content.session_id,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw jsError;
|
||||
}
|
||||
throw new DecryptionError(DecryptionFailureCode.UNKNOWN_ERROR, "Unknown error");
|
||||
default:
|
||||
throw new DecryptionError(DecryptionFailureCode.UNKNOWN_ERROR, err.description, errorDetails);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user