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
rust impl of getEncryptionInfoForEvent (#3718)
This commit is contained in:
committed by
GitHub
parent
5e542b3869
commit
cb375e1351
@@ -30,6 +30,7 @@ import {
|
|||||||
IHttpOpts,
|
IHttpOpts,
|
||||||
IToDeviceEvent,
|
IToDeviceEvent,
|
||||||
MatrixClient,
|
MatrixClient,
|
||||||
|
MatrixEvent,
|
||||||
MatrixHttpApi,
|
MatrixHttpApi,
|
||||||
TypedEventEmitter,
|
TypedEventEmitter,
|
||||||
} from "../../../src";
|
} from "../../../src";
|
||||||
@@ -38,7 +39,13 @@ import { CryptoBackend } from "../../../src/common-crypto/CryptoBackend";
|
|||||||
import { IEventDecryptionResult } from "../../../src/@types/crypto";
|
import { IEventDecryptionResult } from "../../../src/@types/crypto";
|
||||||
import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor";
|
import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor";
|
||||||
import { ServerSideSecretStorage } from "../../../src/secret-storage";
|
import { ServerSideSecretStorage } from "../../../src/secret-storage";
|
||||||
import { CryptoCallbacks, ImportRoomKeysOpts, VerificationRequest } from "../../../src/crypto-api";
|
import {
|
||||||
|
CryptoCallbacks,
|
||||||
|
EventShieldColour,
|
||||||
|
EventShieldReason,
|
||||||
|
ImportRoomKeysOpts,
|
||||||
|
VerificationRequest,
|
||||||
|
} from "../../../src/crypto-api";
|
||||||
import * as testData from "../../test-utils/test-data";
|
import * as testData from "../../test-utils/test-data";
|
||||||
import { defer } from "../../../src/utils";
|
import { defer } from "../../../src/utils";
|
||||||
|
|
||||||
@@ -373,6 +380,111 @@ describe("RustCrypto", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe(".getEncryptionInfoForEvent", () => {
|
||||||
|
let rustCrypto: RustCrypto;
|
||||||
|
let olmMachine: Mocked<RustSdkCryptoJs.OlmMachine>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
olmMachine = {
|
||||||
|
getRoomEventEncryptionInfo: jest.fn(),
|
||||||
|
} as unknown as Mocked<RustSdkCryptoJs.OlmMachine>;
|
||||||
|
rustCrypto = new RustCrypto(
|
||||||
|
olmMachine,
|
||||||
|
{} as MatrixClient["http"],
|
||||||
|
TEST_USER,
|
||||||
|
TEST_DEVICE_ID,
|
||||||
|
{} as ServerSideSecretStorage,
|
||||||
|
{} as CryptoCallbacks,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function makeEncryptedEvent(): Promise<MatrixEvent> {
|
||||||
|
const encryptedEvent = mkEvent({
|
||||||
|
event: true,
|
||||||
|
type: "m.room.encrypted",
|
||||||
|
content: { algorithm: "fake_alg" },
|
||||||
|
room: "!room:id",
|
||||||
|
});
|
||||||
|
encryptedEvent.event.event_id = "$event:id";
|
||||||
|
const mockCryptoBackend = {
|
||||||
|
decryptEvent: () =>
|
||||||
|
({
|
||||||
|
clearEvent: { content: { body: "1234" } },
|
||||||
|
} as unknown as IEventDecryptionResult),
|
||||||
|
} as unknown as CryptoBackend;
|
||||||
|
await encryptedEvent.attemptDecryption(mockCryptoBackend);
|
||||||
|
return encryptedEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
it("should handle unencrypted events", async () => {
|
||||||
|
const event = mkEvent({ event: true, type: "m.room.message", content: { body: "xyz" } });
|
||||||
|
const res = await rustCrypto.getEncryptionInfoForEvent(event);
|
||||||
|
expect(res).toBe(null);
|
||||||
|
expect(olmMachine.getRoomEventEncryptionInfo).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes the event into the OlmMachine", async () => {
|
||||||
|
const encryptedEvent = await makeEncryptedEvent();
|
||||||
|
const res = await rustCrypto.getEncryptionInfoForEvent(encryptedEvent);
|
||||||
|
expect(res).toBe(null);
|
||||||
|
expect(olmMachine.getRoomEventEncryptionInfo).toHaveBeenCalledTimes(1);
|
||||||
|
const [passedEvent, passedRoom] = olmMachine.getRoomEventEncryptionInfo.mock.calls[0];
|
||||||
|
expect(passedRoom.toString()).toEqual("!room:id");
|
||||||
|
expect(JSON.parse(passedEvent)).toStrictEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
event_id: "$event:id",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[RustSdkCryptoJs.ShieldColor.None, EventShieldColour.NONE],
|
||||||
|
[RustSdkCryptoJs.ShieldColor.Grey, EventShieldColour.GREY],
|
||||||
|
[RustSdkCryptoJs.ShieldColor.Red, EventShieldColour.RED],
|
||||||
|
])("gets the right shield color (%i)", async (rustShield, expectedShield) => {
|
||||||
|
const mockEncryptionInfo = {
|
||||||
|
shieldState: jest.fn().mockReturnValue({ color: rustShield, message: null }),
|
||||||
|
} as unknown as RustSdkCryptoJs.EncryptionInfo;
|
||||||
|
olmMachine.getRoomEventEncryptionInfo.mockResolvedValue(mockEncryptionInfo);
|
||||||
|
|
||||||
|
const res = await rustCrypto.getEncryptionInfoForEvent(await makeEncryptedEvent());
|
||||||
|
expect(mockEncryptionInfo.shieldState).toHaveBeenCalledWith(false);
|
||||||
|
expect(res).not.toBe(null);
|
||||||
|
expect(res!.shieldColour).toEqual(expectedShield);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[null, null],
|
||||||
|
["Encrypted by an unverified user.", EventShieldReason.UNVERIFIED_IDENTITY],
|
||||||
|
["Encrypted by a device not verified by its owner.", EventShieldReason.UNSIGNED_DEVICE],
|
||||||
|
[
|
||||||
|
"The authenticity of this encrypted message can't be guaranteed on this device.",
|
||||||
|
EventShieldReason.AUTHENTICITY_NOT_GUARANTEED,
|
||||||
|
],
|
||||||
|
["Encrypted by an unknown or deleted device.", EventShieldReason.UNKNOWN_DEVICE],
|
||||||
|
["bloop", EventShieldReason.UNKNOWN],
|
||||||
|
])("gets the right shield reason (%s)", async (rustReason, expectedReason) => {
|
||||||
|
// suppress the warning from the unknown shield reason
|
||||||
|
jest.spyOn(console, "warn").mockImplementation(() => {});
|
||||||
|
|
||||||
|
const mockEncryptionInfo = {
|
||||||
|
shieldState: jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue({ color: RustSdkCryptoJs.ShieldColor.None, message: rustReason }),
|
||||||
|
} as unknown as RustSdkCryptoJs.EncryptionInfo;
|
||||||
|
olmMachine.getRoomEventEncryptionInfo.mockResolvedValue(mockEncryptionInfo);
|
||||||
|
|
||||||
|
const res = await rustCrypto.getEncryptionInfoForEvent(await makeEncryptedEvent());
|
||||||
|
expect(mockEncryptionInfo.shieldState).toHaveBeenCalledWith(false);
|
||||||
|
expect(res).not.toBe(null);
|
||||||
|
expect(res!.shieldReason).toEqual(expectedReason);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("get|setTrustCrossSignedDevices", () => {
|
describe("get|setTrustCrossSignedDevices", () => {
|
||||||
let rustCrypto: RustCrypto;
|
let rustCrypto: RustCrypto;
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import {
|
|||||||
DeviceVerificationStatus,
|
DeviceVerificationStatus,
|
||||||
EventEncryptionInfo,
|
EventEncryptionInfo,
|
||||||
EventShieldColour,
|
EventShieldColour,
|
||||||
|
EventShieldReason,
|
||||||
GeneratedSecretStorageKey,
|
GeneratedSecretStorageKey,
|
||||||
ImportRoomKeyProgressData,
|
ImportRoomKeyProgressData,
|
||||||
ImportRoomKeysOpts,
|
ImportRoomKeysOpts,
|
||||||
@@ -788,10 +789,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
|||||||
* Implementation of {@link CryptoApi.getEncryptionInfoForEvent}.
|
* Implementation of {@link CryptoApi.getEncryptionInfoForEvent}.
|
||||||
*/
|
*/
|
||||||
public async getEncryptionInfoForEvent(event: MatrixEvent): Promise<EventEncryptionInfo | null> {
|
public async getEncryptionInfoForEvent(event: MatrixEvent): Promise<EventEncryptionInfo | null> {
|
||||||
return {
|
return this.eventDecryptor.getEncryptionInfoForEvent(event);
|
||||||
shieldColour: EventShieldColour.NONE,
|
|
||||||
shieldReason: null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1484,14 +1482,7 @@ class EventDecryptor {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const res = (await this.olmMachine.decryptRoomEvent(
|
const res = (await this.olmMachine.decryptRoomEvent(
|
||||||
JSON.stringify({
|
stringifyEvent(event),
|
||||||
event_id: event.getId(),
|
|
||||||
type: event.getWireType(),
|
|
||||||
sender: event.getSender(),
|
|
||||||
state_key: event.getStateKey(),
|
|
||||||
content: event.getWireContent(),
|
|
||||||
origin_server_ts: event.getTs(),
|
|
||||||
}),
|
|
||||||
new RustSdkCryptoJs.RoomId(event.getRoomId()!),
|
new RustSdkCryptoJs.RoomId(event.getRoomId()!),
|
||||||
)) as RustSdkCryptoJs.DecryptedRoomEvent;
|
)) as RustSdkCryptoJs.DecryptedRoomEvent;
|
||||||
|
|
||||||
@@ -1549,6 +1540,20 @@ class EventDecryptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getEncryptionInfoForEvent(event: MatrixEvent): Promise<EventEncryptionInfo | null> {
|
||||||
|
if (!event.getClearContent()) {
|
||||||
|
// not successfully decrypted
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const encryptionInfo = await this.olmMachine.getRoomEventEncryptionInfo(
|
||||||
|
stringifyEvent(event),
|
||||||
|
new RustSdkCryptoJs.RoomId(event.getRoomId()!),
|
||||||
|
);
|
||||||
|
|
||||||
|
return rustEncryptionInfoToJsEncryptionInfo(encryptionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look for events which are waiting for a given megolm session
|
* Look for events which are waiting for a given megolm session
|
||||||
*
|
*
|
||||||
@@ -1606,6 +1611,61 @@ class EventDecryptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stringifyEvent(event: MatrixEvent): string {
|
||||||
|
return JSON.stringify({
|
||||||
|
event_id: event.getId(),
|
||||||
|
type: event.getWireType(),
|
||||||
|
sender: event.getSender(),
|
||||||
|
state_key: event.getStateKey(),
|
||||||
|
content: event.getWireContent(),
|
||||||
|
origin_server_ts: event.getTs(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function rustEncryptionInfoToJsEncryptionInfo(
|
||||||
|
encryptionInfo: RustSdkCryptoJs.EncryptionInfo | undefined,
|
||||||
|
): EventEncryptionInfo | null {
|
||||||
|
if (encryptionInfo === undefined) {
|
||||||
|
// not decrypted here
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use strict shield semantics.
|
||||||
|
const shieldState = encryptionInfo.shieldState(false);
|
||||||
|
|
||||||
|
let shieldColour: EventShieldColour;
|
||||||
|
switch (shieldState.color) {
|
||||||
|
case RustSdkCryptoJs.ShieldColor.Grey:
|
||||||
|
shieldColour = EventShieldColour.GREY;
|
||||||
|
break;
|
||||||
|
case RustSdkCryptoJs.ShieldColor.None:
|
||||||
|
shieldColour = EventShieldColour.NONE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
shieldColour = EventShieldColour.RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
let shieldReason: EventShieldReason | null;
|
||||||
|
if (shieldState.message === null) {
|
||||||
|
shieldReason = null;
|
||||||
|
} else if (shieldState.message === "Encrypted by an unverified user.") {
|
||||||
|
shieldReason = EventShieldReason.UNVERIFIED_IDENTITY;
|
||||||
|
} else if (shieldState.message === "Encrypted by a device not verified by its owner.") {
|
||||||
|
shieldReason = EventShieldReason.UNSIGNED_DEVICE;
|
||||||
|
} else if (
|
||||||
|
shieldState.message === "The authenticity of this encrypted message can't be guaranteed on this device."
|
||||||
|
) {
|
||||||
|
shieldReason = EventShieldReason.AUTHENTICITY_NOT_GUARANTEED;
|
||||||
|
} else if (shieldState.message === "Encrypted by an unknown or deleted device.") {
|
||||||
|
shieldReason = EventShieldReason.UNKNOWN_DEVICE;
|
||||||
|
} else {
|
||||||
|
logger.warn(`Unknown shield state message '${shieldState.message}'`);
|
||||||
|
shieldReason = EventShieldReason.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { shieldColour, shieldReason };
|
||||||
|
}
|
||||||
|
|
||||||
type RustCryptoEvents =
|
type RustCryptoEvents =
|
||||||
| CryptoEvent.VerificationRequestReceived
|
| CryptoEvent.VerificationRequestReceived
|
||||||
| CryptoEvent.UserTrustStatusChanged
|
| CryptoEvent.UserTrustStatusChanged
|
||||||
|
|||||||
Reference in New Issue
Block a user