Skip to content

Commit

Permalink
Add easy way to determine if the decryption failure is due to "Decryp…
Browse files Browse the repository at this point in the history
…tionError: The sender has disabled encrypting to unverified devices." (#3167)

* Add isEncryptedDisabledForUnverifiedDevices in event.ts

* Add Tests

* Add isEncryptedDisabledForUnverifiedDevices properties to event

* Use WITHHELD_MESSAGES instead of hardcoded string

* Use getter instead of function

* Add documentation
  • Loading branch information
florianduros authored Feb 21, 2023
1 parent 937f370 commit 1f0c6a6
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 0 deletions.
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

0 comments on commit 1f0c6a6

Please sign in to comment.