From 8bdb23c551562ab15ada0f71c0de7363723c83d8 Mon Sep 17 00:00:00 2001 From: Thor Arne Johansen Date: Wed, 20 Dec 2023 11:01:10 +0100 Subject: [PATCH 1/8] Added mechanism to create extension points for method calls. Implemented CryptoSetupExtensions as a replacement for Security customisations --- src/lifecycles/CryptoSetupExtensions.ts | 171 ++++++++++++++++ src/lifecycles/ExperimentalExtensions.ts | 32 +++ src/types/extensions.ts | 22 +++ .../lifecycles/CryptoSetupExtensions.test.tsx | 182 ++++++++++++++++++ 4 files changed, 407 insertions(+) create mode 100644 src/lifecycles/CryptoSetupExtensions.ts create mode 100644 src/lifecycles/ExperimentalExtensions.ts create mode 100644 src/types/extensions.ts create mode 100644 test/lifecycles/CryptoSetupExtensions.test.tsx diff --git a/src/lifecycles/CryptoSetupExtensions.ts b/src/lifecycles/CryptoSetupExtensions.ts new file mode 100644 index 0000000..28e100c --- /dev/null +++ b/src/lifecycles/CryptoSetupExtensions.ts @@ -0,0 +1,171 @@ +/* +Copyright 2023 Verji Tech AS +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + * Types copied (and renamed) from matrix-js-sdk + */ + +export interface SecretStorageKeyDescriptionCommon { + /** A human-readable name for this key. */ + // XXX: according to the spec, this is optional + name: string; + + /** The encryption algorithm used with this key. */ + algorithm: string; + + /** Information for deriving this key from a passphrase. */ + // XXX: according to the spec, this is optional + passphrase: PassphraseInfo; +} + +export interface SecretStorageKeyDescriptionAesV1 extends SecretStorageKeyDescriptionCommon { + // XXX: strictly speaking, we should be able to enforce the algorithm here. But + // this interface ends up being incorrectly used where other algorithms are in use (notably + // in device-dehydration support), and unpicking that is too much like hard work + // at the moment. + // algorithm: "m.secret_storage.v1.aes-hmac-sha2"; + + /** The 16-byte AES initialization vector, encoded as base64. */ + iv: string; + + /** The MAC of the result of encrypting 32 bytes of 0, encoded as base64. */ + mac: string; +} + +export type SecretStorageKeyDescription = SecretStorageKeyDescriptionAesV1; + +export interface PassphraseInfo { + /** The algorithm to be used to derive the key. */ + algorithm: "m.pbkdf2"; + + /** The number of PBKDF2 iterations to use. */ + iterations: number; + + /** The salt to be used for PBKDF2. */ + salt: string; + + /** The number of bits to generate. Defaults to 256. */ + bits?: number; +} + +/* + * Types copied (and renamed) from matrix-react-sdk + * (MatrixClientCreds and Kind) + */ + +export interface IExamineLoginResponseCreds { + homeserverUrl: string; + identityServerUrl?: string; + userId: string; + deviceId?: string; + accessToken: string; + refreshToken?: string; + guest?: boolean; + pickleKey?: string; + freshLogin?: boolean; +} + +export enum SetupEncryptionKind { + SET_UP_ENCRYPTION = "set_up_encryption", + UPGRADE_ENCRYPTION = "upgrade_encryption", + VERIFY_THIS_SESSION = "verify_this_session", +} + +export interface IExtendedMatrixClientCreds extends IExamineLoginResponseCreds { + secureBackupKey?: string; +} + +export interface IProvideCryptoSetupStore { + getInstance: () => ISetupEncryptionStoreProjection; +} + +export interface ISetupEncryptionStoreProjection { + usePassPhrase(): Promise; +} + +export interface IProvideCryptoSetupExtensions { + examineLoginResponse(response: any, credentials: IExtendedMatrixClientCreds): void; + persistCredentials(credentials: IExtendedMatrixClientCreds): void; + getSecretStorageKey(): Uint8Array | null; + createSecretStorageKey(): Uint8Array | null; + catchAccessSecretStorageError(e: Error): void; + setupEncryptionNeeded: (args: CryptoSetupArgs) => boolean; + getDehydrationKeyCallback(): + | ((keyInfo: SecretStorageKeyDescription, checkFunc: (key: Uint8Array) => void) => Promise) + | null; + SHOW_ENCRYPTION_SETUP_UI: boolean; +} + +export abstract class CryptoSetupExtensionsBase implements IProvideCryptoSetupExtensions { + public abstract examineLoginResponse(response: any, credentials: IExtendedMatrixClientCreds): void; + public abstract persistCredentials(credentials: IExtendedMatrixClientCreds): void; + public abstract getSecretStorageKey(): Uint8Array | null; + public abstract createSecretStorageKey(): Uint8Array | null; + public abstract catchAccessSecretStorageError(e: Error): void; + public abstract setupEncryptionNeeded(args: CryptoSetupArgs): boolean; + public abstract getDehydrationKeyCallback(): + | ((keyInfo: SecretStorageKeyDescription, checkFunc: (key: Uint8Array) => void) => Promise) + | null; + public abstract SHOW_ENCRYPTION_SETUP_UI: boolean; +} + +/* Define an interface for setupEncryptionNeeded to help enforce mandatory arguments */ +export interface CryptoSetupArgs { + kind: SetupEncryptionKind; + storeProvider: IProvideCryptoSetupStore; +} + +/** + * + * The default/empty crypto-extensions + * Can (and will) be used if none of the modules has an implementaion of IProvideCryptoSetupExtensions + * + * */ +export class DefaultCryptoSetupExtensions extends CryptoSetupExtensionsBase { + public SHOW_ENCRYPTION_SETUP_UI = true; + + public examineLoginResponse(response: any, credentials: IExtendedMatrixClientCreds): void { + console.log("Default empty examineLoginResponse() => void"); + } + public persistCredentials(credentials: IExtendedMatrixClientCreds): void { + console.log("Default empty persistCredentials() => void"); + } + + public getSecretStorageKey(): Uint8Array | null { + console.log("Default empty getSecretStorageKey() => null"); + return null; + } + + public createSecretStorageKey(): Uint8Array | null { + console.log("Default empty createSecretStorageKey() => null"); + return null; + } + + public catchAccessSecretStorageError(e: Error): void { + console.log("Default catchAccessSecretStorageError() => void"); + } + + public setupEncryptionNeeded(args: CryptoSetupArgs): boolean { + console.log("Default setupEncryptionNeeded() => false"); + return false; + } + + public getDehydrationKeyCallback(): + | ((keyInfo: SecretStorageKeyDescription, checkFunc: (key: Uint8Array) => void) => Promise) + | null { + console.log("Default empty getDehydrationKeyCallback() => null"); + return null; + } +} diff --git a/src/lifecycles/ExperimentalExtensions.ts b/src/lifecycles/ExperimentalExtensions.ts new file mode 100644 index 0000000..2478518 --- /dev/null +++ b/src/lifecycles/ExperimentalExtensions.ts @@ -0,0 +1,32 @@ +/* +Copyright 2023 Verji Tech AS +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * Mostly for test. To ensure we handle more than one module having extensions + * Can possibly also be useful for PoC development + */ +export interface IProvideExperimentalExtensions { + experimentalMethod(args?: any): any; +} + +export abstract class ExperimentalExtensionsBase implements IProvideExperimentalExtensions { + public abstract experimentalMethod(args?: any): any; +} + +export class DefaultExperimentalExtensions extends ExperimentalExtensionsBase { + public experimentalMethod(args?: any): any { + return null; + } +} diff --git a/src/types/extensions.ts b/src/types/extensions.ts new file mode 100644 index 0000000..e425241 --- /dev/null +++ b/src/types/extensions.ts @@ -0,0 +1,22 @@ +/* +Copyright 2023 Verji Tech AS +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { IProvideCryptoSetupExtensions } from "../lifecycles/CryptoSetupExtensions"; +import { IProvideExperimentalExtensions } from "../lifecycles/ExperimentalExtensions"; + +export type AllExtensions = { + cryptoSetup?: IProvideCryptoSetupExtensions; + experimental?: IProvideExperimentalExtensions; +}; diff --git a/test/lifecycles/CryptoSetupExtensions.test.tsx b/test/lifecycles/CryptoSetupExtensions.test.tsx new file mode 100644 index 0000000..5aa158f --- /dev/null +++ b/test/lifecycles/CryptoSetupExtensions.test.tsx @@ -0,0 +1,182 @@ +/* +Copyright 2023 Verji Tech AS + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { RuntimeModule } from "../../src"; +import { + IExtendedMatrixClientCreds, + CryptoSetupExtensionsBase, + SecretStorageKeyDescription, + CryptoSetupArgs, + IProvideCryptoSetupExtensions, + DefaultCryptoSetupExtensions, +} from "../../src/lifecycles/CryptoSetupExtensions"; +import { DefaultExperimentalExtensions, ExperimentalExtensionsBase } from "../../src/lifecycles/ExperimentalExtensions"; + +describe("Defaults", () => { + let module: RuntimeModule; + + beforeAll(() => { + module = new (class extends RuntimeModule { + public constructor() { + super(undefined as any); + + this.extensions = { + cryptoSetup: new DefaultCryptoSetupExtensions(), + experimental: new DefaultExperimentalExtensions(), + }; + } + })(); + }); + + it("returns default value for SHOW_ENCRYPTION_SETUP_UI", () => { + let result = module.extensions!.cryptoSetup!.SHOW_ENCRYPTION_SETUP_UI; + expect(result).toBeTruthy(); + }); + + it("returns default value for getSecretStorageKey()", () => { + let result = module.extensions!.cryptoSetup!.getSecretStorageKey(); + expect(result).toEqual(null); + }); + + it("returns default value of null instead of callback", async () => { + let callback = module.extensions!.cryptoSetup!.getDehydrationKeyCallback() as any; + expect(callback).toBeNull(); + }); + + it("must not throw when calling default examineLoginResponse()", () => { + var credentials = new (class implements IExtendedMatrixClientCreds { + identityServerUrl?: string | undefined; + userId: string = ""; + deviceId?: string | undefined; + accessToken: string = ""; + refreshToken?: string | undefined; + guest?: boolean | undefined; + pickleKey?: string | undefined; + freshLogin?: boolean | undefined; + homeserverUrl: string = ""; + secureBackupKey?: string | undefined = ""; + })(); + + let t = () => module.extensions!.cryptoSetup?.examineLoginResponse({ secureBackupKey: "my key" }, credentials); + expect(t).not.toThrow(); + }); +}); + +describe("Custom CryptoSetupExtensions", () => { + let module: RuntimeModule; + + beforeAll(() => { + module = new (class extends RuntimeModule { + public constructor() { + super(undefined as any); + + this.extensions = { + cryptoSetup: new (class extends CryptoSetupExtensionsBase { + persistCredentials(credentials: IExtendedMatrixClientCreds): void {} + catchAccessSecretStorageError(e: Error): void {} + setupEncryptionNeeded(args: CryptoSetupArgs): boolean { + return true; + } + getSecretStorageKey(): Uint8Array | null { + return new Uint8Array([0xaa, 0xbb, 0xbb, 0xaa]); + } + createSecretStorageKey(): Uint8Array | null { + return new Uint8Array([0xaa, 0xbb, 0xbb, 0xaa, 0xaa, 0xbb, 0xbb, 0xaa]); + } + getDehydrationKeyCallback(): + | (( + keyInfo: SecretStorageKeyDescription, + checkFunc: (key: Uint8Array) => void, + ) => Promise) + | null { + return (_, __) => Promise.resolve(new Uint8Array([0x0, 0x1, 0x2, 0x3])); + } + examineLoginResponse(response: any, credentials: IExtendedMatrixClientCreds): void { + credentials.secureBackupKey = "my secure backup key"; + } + SHOW_ENCRYPTION_SETUP_UI: boolean = false; + })(), + }; + } + })(); + }); + + it("overrides SHOW_ENCRYPTION_SETUP_UI custom setting from base class", () => { + let result = module.extensions!.cryptoSetup!.SHOW_ENCRYPTION_SETUP_UI; + expect(result).toBeFalsy(); + }); + + it("returns custom value when calling getSecretStorageKey", () => { + let result = module.extensions!.cryptoSetup!.getSecretStorageKey(); + expect(result).toEqual(Uint8Array.from([0xaa, 0xbb, 0xbb, 0xaa])); + }); + + it("returns callback which resolves to custom value when calling getDehydrationKeyCallback", async () => { + let callback = module.extensions!.cryptoSetup!.getDehydrationKeyCallback() as any; + const result = await callback({} as SecretStorageKeyDescription, () => {}); + const expected = Uint8Array.from([0x0, 0x1, 0x2, 0x3]); + expect(result).toEqual(expected); + }); + + it("must allow adding secure backup key to login response", () => { + var credentials = new (class implements IExtendedMatrixClientCreds { + identityServerUrl?: string | undefined; + userId: string = ""; + deviceId?: string | undefined; + accessToken: string = ""; + refreshToken?: string | undefined; + guest?: boolean | undefined; + pickleKey?: string | undefined; + freshLogin?: boolean | undefined; + homeserverUrl: string = ""; + secureBackupKey?: string | undefined = ""; + })(); + + module.extensions!.cryptoSetup?.examineLoginResponse({ secureBackupKey: "my key" }, credentials); + expect(credentials.secureBackupKey).toEqual("my secure backup key"); + }); +}); + +describe("Custom ExperimentalExtensions", () => { + let module: RuntimeModule; + + beforeAll(() => { + module = new (class extends RuntimeModule { + public constructor() { + super(undefined as any); + + this.extensions = { + cryptoSetup: {} as IProvideCryptoSetupExtensions, + experimental: new (class extends ExperimentalExtensionsBase { + experimentalMethod(args?: any) { + return "test 123"; + } + })(), + }; + } + })(); + }); + + it("must not throw calling experimentalMethod without arguments", () => { + let t = () => module.extensions!.experimental!.experimentalMethod(); + expect(t).not.toThrow(); + }); + + it("must return correct custom value for experimentalMethod", () => { + let result = module.extensions!.experimental!.experimentalMethod("test 123"); + expect(result).toEqual("test 123"); + }); +}); From 9f0a3185d6a011eba8e54327018ee386384045ad Mon Sep 17 00:00:00 2001 From: Thor Arne Johansen Date: Wed, 20 Dec 2023 11:03:30 +0100 Subject: [PATCH 2/8] Bumped version to 2.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9c69bbb..6d56f82 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@matrix-org/react-sdk-module-api", - "version": "2.3.0", + "version": "2.4.0", "description": "Module API surface for matrix-react-sdk", "main": "lib/index.js", "types": "lib/index.d.ts", From 547e77943a0a721651ea88e939f6a3ebfe678104 Mon Sep 17 00:00:00 2001 From: Thor Arne Johansen Date: Wed, 20 Dec 2023 11:08:43 +0100 Subject: [PATCH 3/8] Re-add changes to runtime module --- src/RuntimeModule.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/RuntimeModule.ts b/src/RuntimeModule.ts index 704561f..86945f1 100644 --- a/src/RuntimeModule.ts +++ b/src/RuntimeModule.ts @@ -18,6 +18,7 @@ import { EventEmitter } from "events"; import { ModuleApi } from "./ModuleApi"; import { PlainSubstitution } from "./types/translations"; +import { AllExtensions } from "./types/extensions"; // TODO: Type the event emitter with AnyLifecycle (extract TypedEventEmitter from js-sdk somehow?) // See https://github.com/matrix-org/matrix-react-sdk-module-api/issues/4 @@ -27,6 +28,9 @@ import { PlainSubstitution } from "./types/translations"; * will be provided information about the application state and can react to it. */ export abstract class RuntimeModule extends EventEmitter { + public extensions?: AllExtensions; + public moduleName: string = RuntimeModule.name; + protected constructor(protected readonly moduleApi: ModuleApi) { super(); } From b3796e94b9e905c8bdaf7d5e4b27c4a2728a908e Mon Sep 17 00:00:00 2001 From: Thor Arne Johansen Date: Thu, 21 Dec 2023 12:46:19 +0100 Subject: [PATCH 4/8] Whitespace --- src/RuntimeModule.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/RuntimeModule.ts b/src/RuntimeModule.ts index 86945f1..c5a7058 100644 --- a/src/RuntimeModule.ts +++ b/src/RuntimeModule.ts @@ -28,6 +28,7 @@ import { AllExtensions } from "./types/extensions"; * will be provided information about the application state and can react to it. */ export abstract class RuntimeModule extends EventEmitter { + public extensions?: AllExtensions; public moduleName: string = RuntimeModule.name; From 7eeb743e0c8f9a522bfad6dff298ff51f6fa4445 Mon Sep 17 00:00:00 2001 From: Thor Arne Johansen Date: Wed, 3 Jan 2024 15:28:24 +0100 Subject: [PATCH 5/8] ran lint:js-fix to have prettier to fix whitespace --- src/RuntimeModule.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/RuntimeModule.ts b/src/RuntimeModule.ts index c5a7058..86945f1 100644 --- a/src/RuntimeModule.ts +++ b/src/RuntimeModule.ts @@ -28,7 +28,6 @@ import { AllExtensions } from "./types/extensions"; * will be provided information about the application state and can react to it. */ export abstract class RuntimeModule extends EventEmitter { - public extensions?: AllExtensions; public moduleName: string = RuntimeModule.name; From 25718bb527bfac9799b91143fc48fac35eb48dad Mon Sep 17 00:00:00 2001 From: Thor Arne Johansen Date: Mon, 22 Jan 2024 15:54:05 +0100 Subject: [PATCH 6/8] Remove 'I' prefix in interface names --- src/lifecycles/CryptoSetupExtensions.ts | 28 +++++++++---------- src/lifecycles/ExperimentalExtensions.ts | 4 +-- src/types/extensions.ts | 8 +++--- .../lifecycles/CryptoSetupExtensions.test.tsx | 14 +++++----- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/lifecycles/CryptoSetupExtensions.ts b/src/lifecycles/CryptoSetupExtensions.ts index 28e100c..2b34ed2 100644 --- a/src/lifecycles/CryptoSetupExtensions.ts +++ b/src/lifecycles/CryptoSetupExtensions.ts @@ -65,7 +65,7 @@ export interface PassphraseInfo { * (MatrixClientCreds and Kind) */ -export interface IExamineLoginResponseCreds { +export interface ExamineLoginResponseCreds { homeserverUrl: string; identityServerUrl?: string; userId: string; @@ -83,21 +83,21 @@ export enum SetupEncryptionKind { VERIFY_THIS_SESSION = "verify_this_session", } -export interface IExtendedMatrixClientCreds extends IExamineLoginResponseCreds { +export interface ExtendedMatrixClientCreds extends ExamineLoginResponseCreds { secureBackupKey?: string; } -export interface IProvideCryptoSetupStore { - getInstance: () => ISetupEncryptionStoreProjection; +export interface ProvideCryptoSetupStore { + getInstance: () => SetupEncryptionStoreProjection; } -export interface ISetupEncryptionStoreProjection { +export interface SetupEncryptionStoreProjection { usePassPhrase(): Promise; } -export interface IProvideCryptoSetupExtensions { - examineLoginResponse(response: any, credentials: IExtendedMatrixClientCreds): void; - persistCredentials(credentials: IExtendedMatrixClientCreds): void; +export interface ProvideCryptoSetupExtensions { + examineLoginResponse(response: any, credentials: ExtendedMatrixClientCreds): void; + persistCredentials(credentials: ExtendedMatrixClientCreds): void; getSecretStorageKey(): Uint8Array | null; createSecretStorageKey(): Uint8Array | null; catchAccessSecretStorageError(e: Error): void; @@ -108,9 +108,9 @@ export interface IProvideCryptoSetupExtensions { SHOW_ENCRYPTION_SETUP_UI: boolean; } -export abstract class CryptoSetupExtensionsBase implements IProvideCryptoSetupExtensions { - public abstract examineLoginResponse(response: any, credentials: IExtendedMatrixClientCreds): void; - public abstract persistCredentials(credentials: IExtendedMatrixClientCreds): void; +export abstract class CryptoSetupExtensionsBase implements ProvideCryptoSetupExtensions { + public abstract examineLoginResponse(response: any, credentials: ExtendedMatrixClientCreds): void; + public abstract persistCredentials(credentials: ExtendedMatrixClientCreds): void; public abstract getSecretStorageKey(): Uint8Array | null; public abstract createSecretStorageKey(): Uint8Array | null; public abstract catchAccessSecretStorageError(e: Error): void; @@ -124,7 +124,7 @@ export abstract class CryptoSetupExtensionsBase implements IProvideCryptoSetupEx /* Define an interface for setupEncryptionNeeded to help enforce mandatory arguments */ export interface CryptoSetupArgs { kind: SetupEncryptionKind; - storeProvider: IProvideCryptoSetupStore; + storeProvider: ProvideCryptoSetupStore; } /** @@ -136,10 +136,10 @@ export interface CryptoSetupArgs { export class DefaultCryptoSetupExtensions extends CryptoSetupExtensionsBase { public SHOW_ENCRYPTION_SETUP_UI = true; - public examineLoginResponse(response: any, credentials: IExtendedMatrixClientCreds): void { + public examineLoginResponse(response: any, credentials: ExtendedMatrixClientCreds): void { console.log("Default empty examineLoginResponse() => void"); } - public persistCredentials(credentials: IExtendedMatrixClientCreds): void { + public persistCredentials(credentials: ExtendedMatrixClientCreds): void { console.log("Default empty persistCredentials() => void"); } diff --git a/src/lifecycles/ExperimentalExtensions.ts b/src/lifecycles/ExperimentalExtensions.ts index 2478518..fcf02e0 100644 --- a/src/lifecycles/ExperimentalExtensions.ts +++ b/src/lifecycles/ExperimentalExtensions.ts @@ -17,11 +17,11 @@ limitations under the License. * Mostly for test. To ensure we handle more than one module having extensions * Can possibly also be useful for PoC development */ -export interface IProvideExperimentalExtensions { +export interface ProvideExperimentalExtensions { experimentalMethod(args?: any): any; } -export abstract class ExperimentalExtensionsBase implements IProvideExperimentalExtensions { +export abstract class ExperimentalExtensionsBase implements ProvideExperimentalExtensions { public abstract experimentalMethod(args?: any): any; } diff --git a/src/types/extensions.ts b/src/types/extensions.ts index e425241..2fcbdcc 100644 --- a/src/types/extensions.ts +++ b/src/types/extensions.ts @@ -13,10 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { IProvideCryptoSetupExtensions } from "../lifecycles/CryptoSetupExtensions"; -import { IProvideExperimentalExtensions } from "../lifecycles/ExperimentalExtensions"; +import { ProvideCryptoSetupExtensions } from "../lifecycles/CryptoSetupExtensions"; +import { ProvideExperimentalExtensions } from "../lifecycles/ExperimentalExtensions"; export type AllExtensions = { - cryptoSetup?: IProvideCryptoSetupExtensions; - experimental?: IProvideExperimentalExtensions; + cryptoSetup?: ProvideCryptoSetupExtensions; + experimental?: ProvideExperimentalExtensions; }; diff --git a/test/lifecycles/CryptoSetupExtensions.test.tsx b/test/lifecycles/CryptoSetupExtensions.test.tsx index 5aa158f..15c6a40 100644 --- a/test/lifecycles/CryptoSetupExtensions.test.tsx +++ b/test/lifecycles/CryptoSetupExtensions.test.tsx @@ -16,11 +16,11 @@ limitations under the License. import { RuntimeModule } from "../../src"; import { - IExtendedMatrixClientCreds, + ExtendedMatrixClientCreds, CryptoSetupExtensionsBase, SecretStorageKeyDescription, CryptoSetupArgs, - IProvideCryptoSetupExtensions, + ProvideCryptoSetupExtensions, DefaultCryptoSetupExtensions, } from "../../src/lifecycles/CryptoSetupExtensions"; import { DefaultExperimentalExtensions, ExperimentalExtensionsBase } from "../../src/lifecycles/ExperimentalExtensions"; @@ -57,7 +57,7 @@ describe("Defaults", () => { }); it("must not throw when calling default examineLoginResponse()", () => { - var credentials = new (class implements IExtendedMatrixClientCreds { + var credentials = new (class implements ExtendedMatrixClientCreds { identityServerUrl?: string | undefined; userId: string = ""; deviceId?: string | undefined; @@ -85,7 +85,7 @@ describe("Custom CryptoSetupExtensions", () => { this.extensions = { cryptoSetup: new (class extends CryptoSetupExtensionsBase { - persistCredentials(credentials: IExtendedMatrixClientCreds): void {} + persistCredentials(credentials: ExtendedMatrixClientCreds): void {} catchAccessSecretStorageError(e: Error): void {} setupEncryptionNeeded(args: CryptoSetupArgs): boolean { return true; @@ -104,7 +104,7 @@ describe("Custom CryptoSetupExtensions", () => { | null { return (_, __) => Promise.resolve(new Uint8Array([0x0, 0x1, 0x2, 0x3])); } - examineLoginResponse(response: any, credentials: IExtendedMatrixClientCreds): void { + examineLoginResponse(response: any, credentials: ExtendedMatrixClientCreds): void { credentials.secureBackupKey = "my secure backup key"; } SHOW_ENCRYPTION_SETUP_UI: boolean = false; @@ -132,7 +132,7 @@ describe("Custom CryptoSetupExtensions", () => { }); it("must allow adding secure backup key to login response", () => { - var credentials = new (class implements IExtendedMatrixClientCreds { + var credentials = new (class implements ExtendedMatrixClientCreds { identityServerUrl?: string | undefined; userId: string = ""; deviceId?: string | undefined; @@ -159,7 +159,7 @@ describe("Custom ExperimentalExtensions", () => { super(undefined as any); this.extensions = { - cryptoSetup: {} as IProvideCryptoSetupExtensions, + cryptoSetup: {} as ProvideCryptoSetupExtensions, experimental: new (class extends ExperimentalExtensionsBase { experimentalMethod(args?: any) { return "test 123"; From 5a758d6ca30a8a9aa6d3a2637e3551b2d5f06d13 Mon Sep 17 00:00:00 2001 From: Thor Arne Johansen Date: Mon, 22 Jan 2024 15:58:20 +0100 Subject: [PATCH 7/8] Added links back to the origin files for copied types. Rename enum names (no UPPER_SNAKE_CASE) --- src/lifecycles/CryptoSetupExtensions.ts | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/lifecycles/CryptoSetupExtensions.ts b/src/lifecycles/CryptoSetupExtensions.ts index 2b34ed2..92c1b26 100644 --- a/src/lifecycles/CryptoSetupExtensions.ts +++ b/src/lifecycles/CryptoSetupExtensions.ts @@ -17,6 +17,9 @@ limitations under the License. * Types copied (and renamed) from matrix-js-sdk */ +/** + * Copied from https://github.com/matrix-org/matrix-js-sdk/blob/2337d5a7af6265bbcabbd42c1594cd8b1829b00b/src/secret-storage.ts#L39-L50 + */ export interface SecretStorageKeyDescriptionCommon { /** A human-readable name for this key. */ // XXX: according to the spec, this is optional @@ -30,6 +33,9 @@ export interface SecretStorageKeyDescriptionCommon { passphrase: PassphraseInfo; } +/** + * Copied from https://github.com/matrix-org/matrix-js-sdk/blob/2337d5a7af6265bbcabbd42c1594cd8b1829b00b/src/secret-storage.ts#L59-L71 + */ export interface SecretStorageKeyDescriptionAesV1 extends SecretStorageKeyDescriptionCommon { // XXX: strictly speaking, we should be able to enforce the algorithm here. But // this interface ends up being incorrectly used where other algorithms are in use (notably @@ -44,8 +50,14 @@ export interface SecretStorageKeyDescriptionAesV1 extends SecretStorageKeyDescri mac: string; } +/** + * Copied from https://github.com/matrix-org/matrix-js-sdk/blob/2337d5a7af6265bbcabbd42c1594cd8b1829b00b/src/secret-storage.ts#L78 + */ export type SecretStorageKeyDescription = SecretStorageKeyDescriptionAesV1; +/** + * Copied from https://github.com/matrix-org/matrix-js-sdk/blob/2337d5a7af6265bbcabbd42c1594cd8b1829b00b/src/secret-storage.ts#L85-L97 + */ export interface PassphraseInfo { /** The algorithm to be used to derive the key. */ algorithm: "m.pbkdf2"; @@ -61,10 +73,8 @@ export interface PassphraseInfo { } /* - * Types copied (and renamed) from matrix-react-sdk - * (MatrixClientCreds and Kind) + * Copied from https://github.com/matrix-org/matrix-react-sdk/blob/11096b207a1510569f5c54182e328f6148a6475c/src/MatrixClientPeg.ts#L57-L67 */ - export interface ExamineLoginResponseCreds { homeserverUrl: string; identityServerUrl?: string; @@ -77,10 +87,13 @@ export interface ExamineLoginResponseCreds { freshLogin?: boolean; } +/** + * Copied from https://github.com/matrix-org/matrix-react-sdk/blob/11096b207a1510569f5c54182e328f6148a6475c/src/toasts/SetupEncryptionToast.ts#L71-L75 + */ export enum SetupEncryptionKind { - SET_UP_ENCRYPTION = "set_up_encryption", - UPGRADE_ENCRYPTION = "upgrade_encryption", - VERIFY_THIS_SESSION = "verify_this_session", + SetUpEncryption = "set_up_encryption", + UpgradeEncryption = "upgrade_encryption", + VerifyThisSessions = "verify_this_session", } export interface ExtendedMatrixClientCreds extends ExamineLoginResponseCreds { From 0fc320f85e14548a095a49efd6849cc002bfffce Mon Sep 17 00:00:00 2001 From: Thor Arne Johansen Date: Mon, 22 Jan 2024 15:59:53 +0100 Subject: [PATCH 8/8] Whitespace fix from linter --- src/RuntimeModule.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/RuntimeModule.ts b/src/RuntimeModule.ts index c5a7058..86945f1 100644 --- a/src/RuntimeModule.ts +++ b/src/RuntimeModule.ts @@ -28,7 +28,6 @@ import { AllExtensions } from "./types/extensions"; * will be provided information about the application state and can react to it. */ export abstract class RuntimeModule extends EventEmitter { - public extensions?: AllExtensions; public moduleName: string = RuntimeModule.name;