From 2724a05a4bb9cf7226b88311312665d6153cbdb2 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Tue, 18 Jul 2023 15:40:46 +0200 Subject: [PATCH] Import cross signing keys and verify local device in `bootstrapCrossSigning` --- spec/test-utils/mockEndpoints.ts | 5 +++- .../rust-crypto/CrossSigningIdentity.spec.ts | 9 ++++++- spec/unit/rust-crypto/rust-crypto.spec.ts | 3 +++ src/client.ts | 6 +++-- src/crypto-api.ts | 15 +++++++++++ src/rust-crypto/CrossSigningIdentity.ts | 12 +++++++-- src/rust-crypto/rust-crypto.ts | 26 ++++++++++++++++++- 7 files changed, 69 insertions(+), 7 deletions(-) diff --git a/spec/test-utils/mockEndpoints.ts b/spec/test-utils/mockEndpoints.ts index bd5bb819a42..47e46689d9f 100644 --- a/spec/test-utils/mockEndpoints.ts +++ b/spec/test-utils/mockEndpoints.ts @@ -38,7 +38,10 @@ export function mockInitialApiRequests(homeserverUrl: string) { */ export function mockSetupCrossSigningRequests(): void { // have account_data requests return an empty object - fetchMock.get("express:/_matrix/client/r0/user/:userId/account_data/:type", {}); + fetchMock.get("express:/_matrix/client/r0/user/:userId/account_data/:type", { + status: 404, + body: { errcode: "M_NOT_FOUND", error: "Account data not found." }, + }); // we expect a request to upload signatures for our device ... fetchMock.post({ url: "path:/_matrix/client/v3/keys/signatures/upload", name: "upload-sigs" }, {}); diff --git a/spec/unit/rust-crypto/CrossSigningIdentity.spec.ts b/spec/unit/rust-crypto/CrossSigningIdentity.spec.ts index 6187ec7ede3..b2865e69e3d 100644 --- a/spec/unit/rust-crypto/CrossSigningIdentity.spec.ts +++ b/spec/unit/rust-crypto/CrossSigningIdentity.spec.ts @@ -19,6 +19,7 @@ import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; import { CrossSigningIdentity } from "../../../src/rust-crypto/CrossSigningIdentity"; import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor"; +import { ServerSideSecretStorage } from "../../../src/secret-storage"; describe("CrossSigningIdentity", () => { describe("bootstrapCrossSigning", () => { @@ -31,6 +32,8 @@ describe("CrossSigningIdentity", () => { /** A mock OutgoingRequestProcessor which crossSigning is connected to */ let outgoingRequestProcessor: Mocked; + let secretStorage: Mocked; + beforeEach(async () => { await RustSdkCryptoJs.initAsync(); @@ -44,7 +47,11 @@ describe("CrossSigningIdentity", () => { makeOutgoingRequest: jest.fn(), } as unknown as Mocked; - crossSigning = new CrossSigningIdentity(olmMachine, outgoingRequestProcessor); + secretStorage = { + get: jest.fn(), + } as unknown as Mocked; + + crossSigning = new CrossSigningIdentity(olmMachine, outgoingRequestProcessor, secretStorage); }); it("should do nothing if keys are present on-device and in secret storage", async () => { diff --git a/spec/unit/rust-crypto/rust-crypto.spec.ts b/spec/unit/rust-crypto/rust-crypto.spec.ts index 74b55ff7396..2a1f9b75f55 100644 --- a/spec/unit/rust-crypto/rust-crypto.spec.ts +++ b/spec/unit/rust-crypto/rust-crypto.spec.ts @@ -180,6 +180,9 @@ describe("RustCrypto", () => { }; // @ts-ignore private property rustCrypto.crossSigningIdentity = mockCrossSigningIdentity; + // @ts-ignore private property + rustCrypto.olmMachine.getDevice = jest.fn().mockReturnValue({ verify: jest.fn().mockReturnValue({}) }); + await rustCrypto.bootstrapCrossSigning({}); expect(mockCrossSigningIdentity.bootstrapCrossSigning).toHaveBeenCalledWith({}); }); diff --git a/src/client.ts b/src/client.ts index a85b04f7dc2..8c1fda42901 100644 --- a/src/client.ts +++ b/src/client.ts @@ -2679,12 +2679,14 @@ export class MatrixClient extends TypedEventEmitter { - if (!this.crypto) { + if (!this.cryptoBackend) { throw new Error("End-to-end encryption disabled"); } - return this.crypto.checkOwnCrossSigningTrust(opts); + return this.cryptoBackend.checkOwnCrossSigningTrust(opts); } /** diff --git a/src/crypto-api.ts b/src/crypto-api.ts index 7428a3181c3..77390b8f334 100644 --- a/src/crypto-api.ts +++ b/src/crypto-api.ts @@ -229,6 +229,14 @@ export interface CryptoApi { */ createRecoveryKeyFromPassphrase(password?: string): Promise; + /** + * Check the cross signing trust of the current user + * Unneeded for new crypto + * + * @param opts - Options object. + */ + checkOwnCrossSigningTrust(opts?: CheckOwnCrossSigningTrustOpts): Promise; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Device/User verification @@ -490,5 +498,12 @@ export interface GeneratedSecretStorageKey { encodedPrivateKey?: string; } +/** + * Options object for {@link CryptoApi#checkOwnCrossSigningTrust}. + */ +export interface CheckOwnCrossSigningTrustOpts { + allowPrivateKeyRequests?: boolean; +} + export * from "./crypto-api/verification"; export * from "./crypto-api/keybackup"; diff --git a/src/rust-crypto/CrossSigningIdentity.ts b/src/rust-crypto/CrossSigningIdentity.ts index 2c43ed60397..608680802f8 100644 --- a/src/rust-crypto/CrossSigningIdentity.ts +++ b/src/rust-crypto/CrossSigningIdentity.ts @@ -20,6 +20,7 @@ import { BootstrapCrossSigningOpts } from "../crypto-api"; import { logger } from "../logger"; import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor"; import { UIAuthCallback } from "../interactive-auth"; +import { ServerSideSecretStorage } from "../secret-storage"; /** Manages the cross-signing keys for our own user. * @@ -29,6 +30,7 @@ export class CrossSigningIdentity { public constructor( private readonly olmMachine: OlmMachine, private readonly outgoingRequestProcessor: OutgoingRequestProcessor, + private readonly secretStorage: ServerSideSecretStorage, ) {} /** @@ -41,7 +43,13 @@ export class CrossSigningIdentity { } const olmDeviceStatus: CrossSigningStatus = await this.olmMachine.crossSigningStatus(); - const privateKeysInSecretStorage = false; // TODO + + // Fetch cross signing keys from the secret storage + const masterKey = await this.secretStorage.get("m.cross_signing.master"); + const selfSigningKey = await this.secretStorage.get("m.cross_signing.self_signing"); + const userSigningKey = await this.secretStorage.get("m.cross_signing.user_signing"); + const privateKeysInSecretStorage = Boolean(masterKey && selfSigningKey && userSigningKey); + const olmDeviceHasKeys = olmDeviceStatus.hasMaster && olmDeviceStatus.hasUserSigning && olmDeviceStatus.hasSelfSigning; @@ -67,7 +75,7 @@ export class CrossSigningIdentity { "bootStrapCrossSigning: Cross-signing private keys not found locally, but they are available " + "in secret storage, reading storage and caching locally", ); - throw new Error("TODO"); + await this.olmMachine.importCrossSigningKeys(masterKey, selfSigningKey, userSigningKey); } // TODO: we might previously have bootstrapped cross-signing but not completed uploading the keys to the diff --git a/src/rust-crypto/rust-crypto.ts b/src/rust-crypto/rust-crypto.ts index 7fa1d62215a..8df0d630df3 100644 --- a/src/rust-crypto/rust-crypto.ts +++ b/src/rust-crypto/rust-crypto.ts @@ -108,7 +108,7 @@ export class RustCrypto extends TypedEventEmitter { await this.crossSigningIdentity.bootstrapCrossSigning(opts); + + // Get the current device + const device: RustSdkCryptoJs.Device = await this.olmMachine.getDevice( + this.olmMachine.userId, + this.olmMachine.deviceId, + ); + + // Verify the device and upload the cross signing signatures + const request: RustSdkCryptoJs.SignatureUploadRequest = await device.verify(); + await this.outgoingRequestProcessor.makeOutgoingRequest(request); } /** @@ -658,6 +668,20 @@ export class RustCrypto extends TypedEventEmitter { + return; + } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SyncCryptoCallbacks implementation