diff --git a/examples/bot.ts b/examples/bot.ts index a1ac99c1..b958df9b 100644 --- a/examples/bot.ts +++ b/examples/bot.ts @@ -27,7 +27,7 @@ const dmTarget = creds?.['dmTarget'] ?? "@admin:localhost"; const homeserverUrl = creds?.['homeserverUrl'] ?? "http://localhost:8008"; const accessToken = creds?.['accessToken'] ?? 'YOUR_TOKEN'; const storage = new SimpleFsStorageProvider("./examples/storage/bot.json"); -const crypto = new RustSdkCryptoStorageProvider("./examples/storage/bot_sled", StoreType.Sled); +const crypto = new RustSdkCryptoStorageProvider("./examples/storage/bot_sqlite", StoreType.Sqlite); const client = new MatrixClient(homeserverUrl, accessToken, storage, crypto); AutojoinRoomsMixin.setupOnClient(client); diff --git a/examples/encryption_appservice.ts b/examples/encryption_appservice.ts index 90c8f3f6..4621897f 100644 --- a/examples/encryption_appservice.ts +++ b/examples/encryption_appservice.ts @@ -31,7 +31,7 @@ try { const dmTarget = creds?.['dmTarget'] ?? "@admin:localhost"; const homeserverUrl = creds?.['homeserverUrl'] ?? "http://localhost:8008"; const storage = new SimpleFsStorageProvider("./examples/storage/encryption_appservice.json"); -const crypto = new RustSdkAppserviceCryptoStorageProvider("./examples/storage/encryption_appservice_sled", StoreType.Sled); +const crypto = new RustSdkAppserviceCryptoStorageProvider("./examples/storage/encryption_appservice_sqlite", StoreType.Sqlite); const worksImage = fs.readFileSync("./examples/static/it-works.png"); const registration: IAppserviceRegistration = { diff --git a/examples/encryption_bot.ts b/examples/encryption_bot.ts index 011b1565..4dda4c56 100644 --- a/examples/encryption_bot.ts +++ b/examples/encryption_bot.ts @@ -29,7 +29,7 @@ const dmTarget = creds?.['dmTarget'] ?? "@admin:localhost"; const homeserverUrl = creds?.['homeserverUrl'] ?? "http://localhost:8008"; const accessToken = creds?.['accessToken'] ?? 'YOUR_TOKEN'; const storage = new SimpleFsStorageProvider("./examples/storage/encryption_bot.json"); -const crypto = new RustSdkCryptoStorageProvider("./examples/storage/encryption_bot_sled", StoreType.Sled); +const crypto = new RustSdkCryptoStorageProvider("./examples/storage/encryption_bot_sqlite", StoreType.Sqlite); const worksImage = fs.readFileSync("./examples/static/it-works.png"); const client = new MatrixClient(homeserverUrl, accessToken, storage, crypto); diff --git a/package.json b/package.json index 24e93dbf..273b76c8 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "tsconfig.json" ], "dependencies": { - "@matrix-org/matrix-sdk-crypto-nodejs": "0.1.0-beta.6", + "@matrix-org/matrix-sdk-crypto-nodejs": "0.1.0-beta.10", "@types/express": "^4.17.13", "another-json": "^0.2.0", "async-lock": "^1.3.2", diff --git a/src/e2ee/CryptoClient.ts b/src/e2ee/CryptoClient.ts index 74ddfb50..a70aae4d 100644 --- a/src/e2ee/CryptoClient.ts +++ b/src/e2ee/CryptoClient.ts @@ -168,12 +168,13 @@ export class CryptoClient { leftDeviceLists.map(u => new UserId(u))); await this.engine.lock.acquire(SYNC_LOCK_NAME, async () => { - const syncResp = await this.engine.machine.receiveSyncChanges(deviceMessages, deviceLists, otkCounts, unusedFallbackKeyAlgs); - const decryptedToDeviceMessages = JSON.parse(syncResp); - if (Array.isArray(decryptedToDeviceMessages)) { - for (const msg of decryptedToDeviceMessages) { + const syncResp = JSON.parse(await this.engine.machine.receiveSyncChanges(deviceMessages, deviceLists, otkCounts, unusedFallbackKeyAlgs)); + if (Array.isArray(syncResp) && syncResp.length === 2 && Array.isArray(syncResp[0])) { + for (const msg of syncResp[0] as IToDeviceMessage[]) { this.client.emit("to_device.decrypted", msg); } + } else { + LogService.error("CryptoClient", "OlmMachine.receiveSyncChanges did not return an expected value of [to-device events, room key changes]"); } await this.engine.run(); diff --git a/src/e2ee/RustEngine.ts b/src/e2ee/RustEngine.ts index c9eae9c8..63293081 100644 --- a/src/e2ee/RustEngine.ts +++ b/src/e2ee/RustEngine.ts @@ -121,9 +121,9 @@ export class RustEngine { }); await this.lock.acquire(roomId, async () => { - const requests = JSON.parse(await this.machine.shareRoomKey(new RoomId(roomId), members, settings)); + const requests = await this.machine.shareRoomKey(new RoomId(roomId), members, settings); for (const req of requests) { - await this.actuallyProcessToDeviceRequest(req.txn_id, req.event_type, req.messages); + await this.processToDeviceRequest(req); } }); } @@ -146,12 +146,7 @@ export class RustEngine { } private async processToDeviceRequest(request: ToDeviceRequest) { - const req = JSON.parse(request.body); - await this.actuallyProcessToDeviceRequest(req.txn_id, req.event_type, req.messages); - } - - private async actuallyProcessToDeviceRequest(id: string, type: string, messages: Record>) { - const resp = await this.client.sendToDevices(type, messages); - await this.machine.markRequestAsSent(id, RequestType.ToDevice, JSON.stringify(resp)); + const resp = await this.client.sendToDevices(request.eventType, JSON.parse(request.body).messages); + await this.machine.markRequestAsSent(request.txnId, RequestType.ToDevice, JSON.stringify(resp)); } } diff --git a/src/storage/RustSdkCryptoStorageProvider.ts b/src/storage/RustSdkCryptoStorageProvider.ts index fc4be490..7f2432b9 100644 --- a/src/storage/RustSdkCryptoStorageProvider.ts +++ b/src/storage/RustSdkCryptoStorageProvider.ts @@ -26,7 +26,7 @@ export class RustSdkCryptoStorageProvider implements ICryptoStorageProvider { */ public constructor( public readonly storagePath: string, - public readonly storageType: RustSdkCryptoStoreType = RustSdkCryptoStoreType.Sled, + public readonly storageType: RustSdkCryptoStoreType = RustSdkCryptoStoreType.Sqlite, ) { this.storagePath = path.resolve(this.storagePath); mkdirp.sync(storagePath); @@ -69,7 +69,7 @@ export class RustSdkAppserviceCryptoStorageProvider extends RustSdkCryptoStorage * @param baseStoragePath The *directory* to persist database details to. * @param storageType The storage type to use. Must be supported by the rust-sdk. */ - public constructor(private baseStoragePath: string, storageType: RustSdkCryptoStoreType = RustSdkCryptoStoreType.Sled) { + public constructor(private baseStoragePath: string, storageType: RustSdkCryptoStoreType = RustSdkCryptoStoreType.Sqlite) { super(path.join(baseStoragePath, "_default"), storageType); } diff --git a/test/MatrixClientTest.ts b/test/MatrixClientTest.ts index 6abdf93e..e1764e09 100644 --- a/test/MatrixClientTest.ts +++ b/test/MatrixClientTest.ts @@ -1,6 +1,5 @@ import * as tmp from "tmp"; import * as simple from "simple-mock"; -import { StoreType } from "@matrix-org/matrix-sdk-crypto-nodejs"; import { EventKind, @@ -48,13 +47,13 @@ describe('MatrixClient', () => { expect(client.accessToken).toEqual(accessToken); }); - it('should create a crypto client when requested', () => { + it('should create a crypto client when requested', () => testCryptoStores(async (cryptoStoreType) => { const homeserverUrl = "https://example.org"; const accessToken = "example_token"; - const client = new MatrixClient(homeserverUrl, accessToken, null, new RustSdkCryptoStorageProvider(tmp.dirSync().name, StoreType.Sled)); + const client = new MatrixClient(homeserverUrl, accessToken, null, new RustSdkCryptoStorageProvider(tmp.dirSync().name, cryptoStoreType)); expect(client.crypto).toBeDefined(); - }); + })); it('should NOT create a crypto client when requested', () => { const homeserverUrl = "https://example.org"; @@ -1401,8 +1400,7 @@ describe('MatrixClient', () => { describe('processSync', () => { interface ProcessSyncClient { userId: string; - - processSync(raw: any): Promise; + processSync(raw: any): MatrixClient["processSync"]; } it('should process non-room account data', async () => { diff --git a/test/TestUtils.ts b/test/TestUtils.ts index 7d6f9f8d..6b4b851f 100644 --- a/test/TestUtils.ts +++ b/test/TestUtils.ts @@ -40,14 +40,14 @@ export function createTestClient( const http = new HttpBackend(); const hsUrl = "https://localhost"; const accessToken = "s3cret"; - const client = new MatrixClient(hsUrl, accessToken, storage, cryptoStoreType !== undefined ? new RustSdkCryptoStorageProvider(tmp.dirSync().name, cryptoStoreType) : null); + const client = new MatrixClient(hsUrl, accessToken, storage, (cryptoStoreType !== undefined) ? new RustSdkCryptoStorageProvider(tmp.dirSync().name, cryptoStoreType) : null); (client).userId = userId; // private member access setRequestFn(http.requestFn); return { http, hsUrl, accessToken, client }; } -const CRYPTO_STORE_TYPES = [StoreType.Sled, StoreType.Sqlite]; +const CRYPTO_STORE_TYPES: StoreType[] = [StoreType.Sqlite]; export async function testCryptoStores(fn: (StoreType) => Promise): Promise { for (const st of CRYPTO_STORE_TYPES) { diff --git a/test/appservice/IntentTest.ts b/test/appservice/IntentTest.ts index 07713238..fadb0e78 100644 --- a/test/appservice/IntentTest.ts +++ b/test/appservice/IntentTest.ts @@ -1137,7 +1137,7 @@ describe('Intent', () => { beforeEach(() => { storage = new MemoryStorageProvider(); - cryptoStorage = new RustSdkAppserviceCryptoStorageProvider(tmp.dirSync().name, StoreType.Sled); + cryptoStorage = new RustSdkAppserviceCryptoStorageProvider(tmp.dirSync().name, StoreType.Sqlite); options = { homeserverUrl: hsUrl, storage: storage, diff --git a/test/encryption/CryptoClientTest.ts b/test/encryption/CryptoClientTest.ts index bab445f6..f472d57d 100644 --- a/test/encryption/CryptoClientTest.ts +++ b/test/encryption/CryptoClientTest.ts @@ -1,7 +1,7 @@ import * as simple from "simple-mock"; import HttpBackend from 'matrix-mock-request'; -import { EncryptedFile, MatrixClient, MembershipEvent, OTKAlgorithm, RoomEncryptionAlgorithm } from "../../src"; +import { EncryptedFile, EncryptionAlgorithm, IOlmEncrypted, IToDeviceMessage, MatrixClient, MembershipEvent, OTKAlgorithm, RoomEncryptionAlgorithm } from "../../src"; import { createTestClient, testCryptoStores, TEST_DEVICE_ID } from "../TestUtils"; export function bindNullEngine(http: HttpBackend) { @@ -85,6 +85,71 @@ describe('CryptoClient', () => { })); }); + describe('processSync', () => { + /** + * Helper class to be able to call {@link MatrixClient#processSync}, which is otherwise private. + */ + interface ProcessSyncClient { + processSync: MatrixClient["processSync"]; + } + + it('should process encrypted to-device messages', () => testCryptoStores(async (cryptoStoreType) => { + const userId = "@alice:example.org"; + const { client, http } = createTestClient(null, userId, cryptoStoreType); + const psClient = (client); + + await client.cryptoStore.setDeviceId(TEST_DEVICE_ID); + + const toDeviceMessage: IToDeviceMessage = { + type: "m.room.encrypted", + sender: userId, + content: { + algorithm: EncryptionAlgorithm.OlmV1Curve25519AesSha2, + sender_key: "sender_curve25519_key", + ciphertext: { + ["device_curve25519_key"]: { + type: 0, + body: "encrypted_payload_base_64", + }, + }, + }, + }; + const sync = { + to_device: { events: [toDeviceMessage] }, + device_unused_fallback_key_types: [OTKAlgorithm.Signed], + device_one_time_keys_count: { + [OTKAlgorithm.Signed]: 12, + [OTKAlgorithm.Unsigned]: 14, + }, + device_lists: { + changed: ["@bob:example.org"], + left: ["@charlie:example.org"], + }, + }; + + const toDeviceSpy = simple.stub().callFn((ev) => { + for (const prop in toDeviceMessage) { + expect(ev).toHaveProperty(prop); + } + }); + client.on("to_device.decrypted", toDeviceSpy); + + bindNullEngine(http); + await Promise.all([ + client.crypto.prepare([]), + http.flushAllExpected(), + ]); + + bindNullEngine(http); + await Promise.all([ + psClient.processSync(sync), + http.flushAllExpected(), + ]); + + expect(toDeviceSpy.callCount).toBe(1); + })); + }); + describe('isRoomEncrypted', () => { it('should fail when the crypto has not been prepared', () => testCryptoStores(async (cryptoStoreType) => { const userId = "@alice:example.org"; diff --git a/yarn.lock b/yarn.lock index f96ba4ad..090f6316 100644 --- a/yarn.lock +++ b/yarn.lock @@ -584,10 +584,10 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@matrix-org/matrix-sdk-crypto-nodejs@0.1.0-beta.6": - version "0.1.0-beta.6" - resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-nodejs/-/matrix-sdk-crypto-nodejs-0.1.0-beta.6.tgz#0ecae51103ee3c107af0d6d0738f33eb7cc9857e" - integrity sha512-JXyrHuCVMydUGgSetWsfqbbvHj3aUMOX5TUghlMtLFromyEu7wIsNgYt7PjJ+k3WdF4GVABRy4P6GNjaEMy2uA== +"@matrix-org/matrix-sdk-crypto-nodejs@0.1.0-beta.10": + version "0.1.0-beta.10" + resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-nodejs/-/matrix-sdk-crypto-nodejs-0.1.0-beta.10.tgz#52290c76ac997001b615c9fb78b70e36b6a4501f" + integrity sha512-AiSHgpHw75sJ1k9uqWk74Wps74XM+M7LsQZrLFlZh/nv9fhOk7JvRZlQczDK9qhD0Umt84PRcOumgT5bXbA/lw== dependencies: https-proxy-agent "^5.0.1" node-downloader-helper "^2.1.5"