From fdce098245c24b409b4f617962d54cf7f7774322 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Tue, 14 Feb 2023 12:12:20 +0100 Subject: [PATCH] create cross-signing class, support deriving msk from 4s stored privkey and check if they match the publicized one and then trust it --- src/matrix/Session.js | 23 +++++++++ src/matrix/verification/CrossSigning.ts | 68 +++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/matrix/verification/CrossSigning.ts diff --git a/src/matrix/Session.js b/src/matrix/Session.js index d4c68a8d..222c8ef2 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -31,6 +31,7 @@ import {Encryption as OlmEncryption} from "./e2ee/olm/Encryption"; import {Decryption as MegOlmDecryption} from "./e2ee/megolm/Decryption"; import {KeyLoader as MegOlmKeyLoader} from "./e2ee/megolm/decryption/KeyLoader"; import {KeyBackup} from "./e2ee/megolm/keybackup/KeyBackup"; +import {CrossSigning} from "./verification/CrossSigning"; import {Encryption as MegOlmEncryption} from "./e2ee/megolm/Encryption.js"; import {MEGOLM_ALGORITHM} from "./e2ee/common.js"; import {RoomEncryption} from "./e2ee/RoomEncryption.js"; @@ -59,6 +60,7 @@ export class Session { this._storage = storage; this._hsApi = hsApi; this._mediaRepository = mediaRepository; + this._features = features; this._syncInfo = null; this._sessionInfo = sessionInfo; this._rooms = new ObservableMap(); @@ -88,6 +90,7 @@ export class Session { this._getSyncToken = () => this.syncToken; this._olmWorker = olmWorker; this._keyBackup = new ObservableValue(undefined); + this._crossSigning = undefined; this._observedRoomStatus = new Map(); if (olm) { @@ -330,6 +333,20 @@ export class Session { txn ); if (keyBackup) { + if (this._features.crossSigning) { + this._crossSigning = new CrossSigning({ + storage: this._storage, + secretStorage, + platform: this._platform, + olm: this._olm, + deviceTracker: this._deviceTracker, + hsApi: this._hsApi, + ownUserId: this.userId + }); + await log.wrap("enable cross-signing", log => { + return this._crossSigning.init(log); + }); + } for (const room of this._rooms.values()) { if (room.isEncrypted) { room.enableKeyBackup(keyBackup); @@ -337,6 +354,8 @@ export class Session { } this._keyBackup.set(keyBackup); return true; + } else { + log.set("no_backup", true); } } catch (err) { log.catch(err); @@ -354,6 +373,10 @@ export class Session { return this._keyBackup; } + get crossSigning() { + return this._crossSigning; + } + get hasIdentity() { return !!this._e2eeAccount; } diff --git a/src/matrix/verification/CrossSigning.ts b/src/matrix/verification/CrossSigning.ts new file mode 100644 index 00000000..8c3b6b61 --- /dev/null +++ b/src/matrix/verification/CrossSigning.ts @@ -0,0 +1,68 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +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 type {SecretStorage} from "../ssss/SecretStorage"; +import type {Storage} from "../storage/idb/Storage"; +import type {Platform} from "../../platform/web/Platform"; +import type {DeviceTracker} from "../e2ee/DeviceTracker"; +import type * as OlmNamespace from "@matrix-org/olm"; +import type {HomeServerApi} from "../net/HomeServerApi"; +type Olm = typeof OlmNamespace; + +export class CrossSigning { + private readonly storage: Storage; + private readonly secretStorage: SecretStorage; + private readonly platform: Platform; + private readonly deviceTracker: DeviceTracker; + private readonly olm: Olm; + private readonly hsApi: HomeServerApi; + private readonly ownUserId: string; + private _isMasterKeyTrusted: boolean = false; + + constructor(options: {storage: Storage, secretStorage: SecretStorage, deviceTracker: DeviceTracker, platform: Platform, olm: Olm, ownUserId: string, hsApi: HomeServerApi}) { + this.storage = options.storage; + this.secretStorage = options.secretStorage; + this.platform = options.platform; + this.deviceTracker = options.deviceTracker; + this.olm = options.olm; + this.hsApi = options.hsApi; + this.ownUserId = options.ownUserId; + } + + async init(log) { + // use errorboundary here + const txn = await this.storage.readTxn([this.storage.storeNames.accountData]); + + const mskSeed = await this.secretStorage.readSecret("m.cross_signing.master", txn); + const signing = new this.olm.PkSigning(); + let derivedPublicKey; + try { + const seed = new Uint8Array(this.platform.encoding.base64.decode(mskSeed)); + derivedPublicKey = signing.init_with_seed(seed); + } finally { + signing.free(); + } + const publishedMasterKey = await this.deviceTracker.getMasterKeyForUser(this.ownUserId, this.hsApi, log); + log.set({publishedMasterKey, derivedPublicKey}); + this._isMasterKeyTrusted = publishedMasterKey === derivedPublicKey; + log.set("isMasterKeyTrusted", this.isMasterKeyTrusted); + } + + get isMasterKeyTrusted(): boolean { + return this._isMasterKeyTrusted; + } +} +