Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add easy way to determine if the decryption failure is due to "DecryptionError: The sender has disabled encrypting to unverified devices." #3167

Merged
merged 7 commits into from
Feb 21, 2023
19 changes: 19 additions & 0 deletions spec/unit/models/event.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,31 @@ describe("MatrixEvent", () => {
expect(encryptedEvent.isEncrypted()).toBeTruthy();
expect(encryptedEvent.isBeingDecrypted()).toBeFalsy();
expect(encryptedEvent.isDecryptionFailure()).toBeTruthy();
expect(encryptedEvent.isEncryptedDisabledForUnverifiedDevices).toBeFalsy();
expect(encryptedEvent.getContent()).toEqual({
msgtype: "m.bad.encrypted",
body: "** Unable to decrypt: Error: test error **",
});
});

it(`should report "DecryptionError: The sender has disabled encrypting to unverified devices."`, async () => {
const crypto = {
decryptEvent: jest
.fn()
.mockRejectedValue("DecryptionError: The sender has disabled encrypting to unverified devices."),
} as unknown as Crypto;

await encryptedEvent.attemptDecryption(crypto);
expect(encryptedEvent.isEncrypted()).toBeTruthy();
expect(encryptedEvent.isBeingDecrypted()).toBeFalsy();
expect(encryptedEvent.isDecryptionFailure()).toBeTruthy();
expect(encryptedEvent.isEncryptedDisabledForUnverifiedDevices).toBeTruthy();
expect(encryptedEvent.getContent()).toEqual({
msgtype: "m.bad.encrypted",
body: "** Unable to decrypt: DecryptionError: The sender has disabled encrypting to unverified devices. **",
});
});

it("should retry decryption if a retry is queued", async () => {
const eventAttemptDecryptionSpy = jest.spyOn(encryptedEvent, "attemptDecryption");

Expand Down
4 changes: 4 additions & 0 deletions src/@types/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ export interface IEventDecryptionResult {
*/
claimedEd25519Key?: string;
untrusted?: boolean;
/**
* The sender doesn't authorize the unverified devices to decrypt his messages
*/
encryptedDisabledForUnverifiedDevices?: boolean;
}

interface Extensible {
Expand Down
17 changes: 17 additions & 0 deletions src/models/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { TypedEventEmitter } from "./typed-event-emitter";
import { EventStatus } from "./event-status";
import { DecryptionError } from "../crypto/algorithms";
import { CryptoBackend } from "../common-crypto/CryptoBackend";
import { WITHHELD_MESSAGES } from "../crypto/OlmDevice";

export { EventStatus } from "./event-status";

Expand Down Expand Up @@ -272,6 +273,12 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
private thread?: Thread;
private threadId?: string;

/*
* True if this event is an encrypted event which we failed to decrypt, the receiver's device is unverified and
* the sender has disabled encrypting to unverified devices.
*/
private encryptedDisabledForUnverifiedDevices = false;

/* Set an approximate timestamp for the event relative the local clock.
* This will inherently be approximate because it doesn't take into account
* the time between the server putting the 'age' field on the event as it sent
Expand Down Expand Up @@ -704,6 +711,14 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
return this.clearEvent?.content?.msgtype === "m.bad.encrypted";
}

/*
* True if this event is an encrypted event which we failed to decrypt, the receiver's device is unverified and
* the sender has disabled encrypting to unverified devices.
*/
public get isEncryptedDisabledForUnverifiedDevices(): boolean {
return this.isDecryptionFailure() && this.encryptedDisabledForUnverifiedDevices;
}

public shouldAttemptDecryption(): boolean {
if (this.isRedacted()) return false;
if (this.isBeingDecrypted()) return false;
Expand Down Expand Up @@ -899,6 +914,7 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
body: "** Unable to decrypt: " + reason + " **",
},
},
encryptedDisabledForUnverifiedDevices: reason === `DecryptionError: ${WITHHELD_MESSAGES["m.unverified"]}`,
};
}

Expand All @@ -920,6 +936,7 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
this.claimedEd25519Key = decryptionResult.claimedEd25519Key ?? null;
this.forwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain || [];
this.untrusted = decryptionResult.untrusted || false;
this.encryptedDisabledForUnverifiedDevices = decryptionResult.encryptedDisabledForUnverifiedDevices || false;
this.invalidateExtensibleEvent();
}

Expand Down