import { MatrixClient } from "../MatrixClient" ; import { LogService } from "../logging/LogService" ; import { DeviceKeyAlgorithm , IMegolmEncrypted , IOlmEncrypted , IToDeviceMessage , OTKAlgorithm , OTKCounts , Signatures , } from "../models/Crypto" ; import { requiresReady } from "./decorators" ; import { RoomTracker } from "./RoomTracker" ; import { EncryptedRoomEvent } from "../models/events/EncryptedRoomEvent" ; import { RoomEvent } from "../models/events/RoomEvent" ; import { EncryptedFile } from "../models/events/MessageEvent" ; import { decryptFile as rustDecryptFile , encryptFile as rustEncryptFile , OlmMachine , } from "@turt2live/matrix-sdk-crypto-nodejs" ; import { RustSdkCryptoStorageProvider } from "../storage/RustSdkCryptoStorageProvider" ; import { SdkOlmEngine } from "./SdkOlmEngine" ; import { InternalOlmMachineFactory } from "./InternalOlmMachineFactory" ; category export class CryptoClient { private ready = false ; private deviceId : string ; private deviceEd25519 : string ; private deviceCurve25519 : string ; private roomTracker : RoomTracker ; private machine : OlmMachine ; public constructor ( private client ) { this . roomTracker = new RoomTracker ( this . client ) ; } private get storage ( ) : RustSdkCryptoStorageProvider { return < RustSdkCryptoStorageProvider > this . client . cryptoStore ; } /**
     * The device ID for the MatrixClient.
     */ public get clientDeviceId ( ) : string { return this . deviceId ; } see prepare public get isReady ( ) : boolean { return this . ready ; } param { string [ ] } roomIds public async prepare ( roomIds ) { await this . roomTracker . prepare ( roomIds ) ; if ( this . ready ) return ; const storedDeviceId = await this . client . cryptoStore . getDeviceId ( ) ; if ( storedDeviceId ) { this . deviceId = storedDeviceId ; } else { const deviceId = ( await this . client . getWhoAmI ( ) ) [ 'device_id' ] ; if ( ! deviceId ) { throw new Error ( "Encryption not possible: server not revealing device ID" ) ; } this . deviceId = deviceId ; await this . client . cryptoStore . setDeviceId ( this . deviceId ) ; } LogService . debug ( "CryptoClient" , "Starting with device ID:" , this . deviceId ) ; this . machine = new InternalOlmMachineFactory ( await this . client . getUserId ( ) , this . deviceId , new SdkOlmEngine ( this . client ) , this . storage . storagePath ) . build ( ) ; await this . machine . runEngine ( ) ; const identity = this . machine . identityKeys ; this . deviceCurve25519 = identity [ DeviceKeyAlgorithm . Curve25519 ] ; this . deviceEd25519 = identity [ DeviceKeyAlgorithm . Ed25519 ] ; this . ready = true ; } param { string } roomId returns { Promise < boolean > } @ requiresReady ( ) public async isRoomEncrypted ( roomId ) { const config = await this . roomTracker . getRoomCryptoConfig ( roomId ) ; return ! ! config ?. algorithm ; } param { IToDeviceMessage < IOlmEncrypted > } toDeviceMessages param { OTKCounts } otkCounts param { OTKAlgorithm [ ] } unusedFallbackKeyAlgs param { string [ ] } changedDeviceLists param { string [ ] } leftDeviceLists returns { Promise < void > } @ requiresReady ( ) public async updateSyncData ( toDeviceMessages , otkCounts , unusedFallbackKeyAlgs , changedDeviceLists , leftDeviceLists ) { await this . machine . pushSync ( toDeviceMessages , { changed : changedDeviceLists , left : leftDeviceLists } , otkCounts , unusedFallbackKeyAlgs ) ; } param { object } obj returns { Promise < Signatures > } @ requiresReady ( ) public async sign ( obj ) { obj = JSON . parse ( JSON . stringify ( obj ) ) ; const existingSignatures = obj [ 'signatures' ] || { } ; delete obj [ 'signatures' ] ; delete obj [ 'unsigned' ] ; const sig = await this . machine . sign ( obj ) ; return { ... sig , ... existingSignatures , } ; } param { string } roomId param { string } eventType param { any } content returns { Promise < IMegolmEncrypted > } @ requiresReady ( ) public async encryptRoomEvent ( roomId , eventType , content ) { if ( ! ( await this . isRoomEncrypted ( roomId ) ) ) { throw new Error ( "Room is not encrypted" ) ; } const encrypted = await this . machine . encryptRoomEvent ( roomId , eventType , content ) ; return encrypted as IMegolmEncrypted ; } param { EncryptedRoomEvent } event param { string } roomId returns { Promise < RoomEvent < unknown > > } @ requiresReady ( ) public async decryptRoomEvent ( event , roomId ) { const decrypted = await this . machine . decryptRoomEvent ( roomId , event . raw ) ; return new RoomEvent < unknown > ( { ... event . raw , type : decrypted . clearEvent . type || "io.t2bot.unknown" , content : ( typeof ( decrypted . clearEvent . content ) === 'object' ) ? decrypted . clearEvent . content : { } , } ) ; } param { Buffer } file returns { { buffer : Buffer , file : Omit < EncryptedFile , "url" > } } @ requiresReady ( ) public async encryptMedia ( file ) { const encrypted = rustEncryptFile ( file ) ; return { buffer : encrypted . data , file : { iv : encrypted . file . iv , key : encrypted . file . web_key , v : encrypted . file . v , hashes : encrypted . file . hashes as { sha256 : string ; } , } , } ; } param { EncryptedFile } file returns { Promise < Buffer > } @ requiresReady ( ) public async decryptMedia ( file ) { return rustDecryptFile ( ( await this . client . downloadContent ( file . url ) ) . data , { ... file , web_key : file . key as any , } ) ; } }