diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 313855c76c..338229e720 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -145,31 +145,31 @@ internal class RequestSender @Inject constructor( } } - suspend fun sendRoomMessage(request: OutgoingVerificationRequest.InRoom) { - sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId) + suspend fun sendRoomMessage(request: OutgoingVerificationRequest.InRoom): String { + return sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId) } - suspend fun sendRoomMessage(request: Request.RoomMessage) { - sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId) + suspend fun sendRoomMessage(request: Request.RoomMessage): String { + return sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId) } - suspend fun sendRoomMessage(eventType: String, roomId: String, content: String, transactionId: String) { + suspend fun sendRoomMessage(eventType: String, roomId: String, content: String, transactionId: String): String { val adapter = MoshiProvider.providesMoshi().adapter(Map::class.java) val jsonContent = adapter.fromJson(content) val event = Event(eventType, transactionId, jsonContent, roomId = roomId) val params = SendVerificationMessageTask.Params(event) - this.sendVerificationMessageTask.get().execute(params) + return this.sendVerificationMessageTask.get().execute(params) } suspend fun sendToDevice(request: Request.ToDevice) { - sendToDevice(request.eventType, request.body) + sendToDevice(request.eventType, request.body, request.requestId) } suspend fun sendToDevice(request: OutgoingVerificationRequest.ToDevice) { - sendToDevice(request.eventType, request.body) + sendToDevice(request.eventType, request.body, request.requestId) } - suspend fun sendToDevice(eventType: String, body: String) { + suspend fun sendToDevice(eventType: String, body: String, transactionId: String) { // TODO this produces floats for the Olm type fields, which are integers originally. val adapter = MoshiProvider .providesMoshi() @@ -179,7 +179,7 @@ internal class RequestSender @Inject constructor( val userMap = MXUsersDevicesMap() userMap.join(jsonBody) - val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap) + val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap, transactionId) sendToDeviceTask.execute(sendToDeviceParams) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt index 5a099c12db..96eda6a465 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt @@ -203,21 +203,20 @@ internal class QrCodeVerification( override var state: VerificationTxState get() { refreshData() - val state = when { - this.inner.isDone -> VerificationTxState.Verified - this.inner.hasBeenConfirmed -> VerificationTxState.WaitingOtherReciprocateConfirm - this.inner.otherSideScanned -> VerificationTxState.QrScannedByOther - this.inner.isCancelled -> { - val cancelCode = safeValueOf(this.inner.cancelCode) - val byMe = this.inner.cancelledByUs ?: false + + return when { + inner.isDone -> VerificationTxState.Verified + inner.hasBeenConfirmed -> VerificationTxState.WaitingOtherReciprocateConfirm + inner.otherSideScanned -> VerificationTxState.QrScannedByOther + inner.isCancelled -> { + val cancelCode = safeValueOf(inner.cancelCode) + val byMe = inner.cancelledByUs ?: false VerificationTxState.Cancelled(cancelCode, byMe) } - else -> { + else -> { VerificationTxState.None } } - - return state } @Suppress("UNUSED_PARAMETER") set(value) {} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/VerificationRequest.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/VerificationRequest.kt index f15ee7b405..2dd9e6f6fa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/VerificationRequest.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/VerificationRequest.kt @@ -31,6 +31,7 @@ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS +import org.matrix.android.sdk.internal.crypto.model.rest.toValue import timber.log.Timber import uniffi.olm.OlmMachine import uniffi.olm.VerificationRequest @@ -96,16 +97,7 @@ internal class VerificationRequest( } suspend fun acceptWithMethods(methods: List) { - val stringMethods: MutableList = - methods - .map { - when (it) { - VerificationMethod.QR_CODE_SCAN -> VERIFICATION_METHOD_QR_CODE_SCAN - VerificationMethod.QR_CODE_SHOW -> VERIFICATION_METHOD_QR_CODE_SHOW - VerificationMethod.SAS -> VERIFICATION_METHOD_SAS - } - } - .toMutableList() + val stringMethods: MutableList = methods.map { it.toValue() }.toMutableList() if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) || stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SCAN)) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt index 9a9078de65..b3fb53dfb2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt @@ -38,6 +38,10 @@ import org.matrix.android.sdk.internal.crypto.QrCodeVerification import org.matrix.android.sdk.internal.crypto.RequestSender import org.matrix.android.sdk.internal.crypto.SasVerification import org.matrix.android.sdk.internal.crypto.VerificationRequest +import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN +import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW +import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE +import org.matrix.android.sdk.internal.crypto.model.rest.toValue import org.matrix.android.sdk.internal.session.SessionScope import timber.log.Timber import uniffi.olm.Verification @@ -258,7 +262,22 @@ internal class RustVerificationService( tid: String? ): PendingVerificationRequest? { // This is only used in `RoomDetailViewModel` to resume the verification. - // We don't support resuming in the rust-sdk, at least for now, so let's return null here. + // + // Is this actually useful? SAS and QR code verifications ephemeral nature + // due to the usage of ephemeral secrets. In the case of SAS verification, the + // ephemeral key can't be stored due to libolm missing support for it, I would + // argue that the ephemeral secret for QR verifications shouldn't be persisted either. + // + // This means that once we transition from a verification request into an actual + // verification flow (SAS/QR) we won't be able to resume. In other words resumption + // is only supported before both sides agree to verify. + // + // We would either need to remember if the request transitioned into a flow and only + // support resumption if we didn't, otherwise we would risk getting different emojis + // or secrets in the QR code, not to mention that the flows could be interrupted in + // any non-starting state. + // + // In any case, we don't support resuming in the rust-sdk, so let's return null here. return null } @@ -267,8 +286,19 @@ internal class RustVerificationService( otherUserId: String, otherDevices: List? ): PendingVerificationRequest { - // This was mostly a copy paste of the InDMs method, do the same here - TODO() + + val stringMethods: MutableList = methods.map { it.toValue() }.toMutableList() + if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) || + stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SCAN)) { + stringMethods.add(VERIFICATION_METHOD_RECIPROCATE) + } + + val result = this.olmMachine.inner().requestSelfVerification(stringMethods) + runBlocking { + requestSender.sendVerificationRequest(result!!.request) + } + + return VerificationRequest(this.olmMachine.inner(), result!!.verification, this.requestSender, this.listeners).toPendingVerificationRequest() } override fun requestKeyVerificationInDMs( @@ -278,10 +308,21 @@ internal class RustVerificationService( localId: String? ): PendingVerificationRequest { Timber.i("## SAS Requesting verification to user: $otherUserId in room $roomId") + val stringMethods: MutableList = methods.map { it.toValue() }.toMutableList() - // TODO cancel other active requests, create a new request here and - // dispatch it - TODO() + if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) || + stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SCAN)) { + stringMethods.add(VERIFICATION_METHOD_RECIPROCATE) + } + + val content = this.olmMachine.inner().verificationRequestContent(otherUserId, stringMethods)!! + + val eventID = runBlocking { + requestSender.sendRoomMessage(EventType.MESSAGE, roomId, content, localId!!) + } + + val innerRequest = this.olmMachine.inner().requestVerification(otherUserId, roomId, eventID, stringMethods)!! + return VerificationRequest(this.olmMachine.inner(), innerRequest, this.requestSender, this.listeners).toPendingVerificationRequest() } override fun readyPendingVerification( @@ -347,10 +388,10 @@ internal class RustVerificationService( // DeviceListBottomSheetViewModel triggers this, interestingly the method that // triggers this is called `manuallyVerify()` runBlocking { - val sas = getDevice(otherUserId, otherDeviceId)?.startVerification() - if (sas != null) { - dispatchTxAdded(sas) - sas.transactionId + val verification = getDevice(otherUserId, otherDeviceId)?.startVerification() + if (verification != null) { + dispatchTxAdded(verification) + verification.transactionId } else { null } diff --git a/rust-sdk/Cargo.toml b/rust-sdk/Cargo.toml index 2c3399656e..29d19c41a6 100644 --- a/rust-sdk/Cargo.toml +++ b/rust-sdk/Cargo.toml @@ -25,11 +25,11 @@ features = ["lax_deserialize"] [dependencies.matrix-sdk-common] git = "https://github.com/matrix-org/matrix-rust-sdk/" -rev = "9052843acbc762388f4ae59e6dad0d580914cf59" +rev = "c5df7c5356c2540ede95f41e3565e91ca7de5777" [dependencies.matrix-sdk-crypto] git = "https://github.com/matrix-org/matrix-rust-sdk/" -rev = "9052843acbc762388f4ae59e6dad0d580914cf59" +rev = "c5df7c5356c2540ede95f41e3565e91ca7de5777" features = ["sled_cryptostore"] [dependencies.tokio] diff --git a/rust-sdk/src/lib.rs b/rust-sdk/src/lib.rs index 331c6336b2..fe02dc3ab7 100644 --- a/rust-sdk/src/lib.rs +++ b/rust-sdk/src/lib.rs @@ -8,7 +8,8 @@ pub use device::Device; pub use error::{CryptoStoreError, DecryptionError, KeyImportError, MachineCreationError}; pub use logger::{set_logger, Logger}; pub use machine::{ - KeyRequestPair, OlmMachine, QrCode, Sas, StartSasResult, Verification, VerificationRequest, + KeyRequestPair, OlmMachine, QrCode, RequestVerificationResult, Sas, StartSasResult, + Verification, VerificationRequest, }; pub use responses::{ DeviceLists, KeysImportResult, OutgoingVerificationRequest, Request, RequestType, diff --git a/rust-sdk/src/machine.rs b/rust-sdk/src/machine.rs index 83016e6c1e..7b7a204357 100644 --- a/rust-sdk/src/machine.rs +++ b/rust-sdk/src/machine.rs @@ -22,7 +22,7 @@ use ruma::{ key::verification::VerificationMethod, room::encrypted::EncryptedEventContent, AnyMessageEventContent, EventContent, SyncMessageEvent, }, - DeviceKeyAlgorithm, RoomId, UserId, + DeviceKeyAlgorithm, EventId, RoomId, UserId, }; use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; @@ -128,6 +128,11 @@ impl From for Sas { } } +pub struct RequestVerificationResult { + pub verification: VerificationRequest, + pub request: OutgoingVerificationRequest, +} + pub struct VerificationRequest { pub other_user_id: String, pub other_device_id: Option, @@ -716,11 +721,6 @@ impl OlmMachine { } } - pub fn request_verification(&self, user_id: &str) { - let _user_id = UserId::try_from(user_id).unwrap(); - todo!("Requesting key verification isn't yet supported") - } - pub fn get_verification(&self, user_id: &str, flow_id: &str) -> Option { let user_id = UserId::try_from(user_id).ok()?; self.inner @@ -758,6 +758,87 @@ impl OlmMachine { }) } + pub fn request_verification( + &self, + user_id: &str, + room_id: &str, + event_id: &str, + methods: Vec, + ) -> Result, CryptoStoreError> { + let user_id = UserId::try_from(user_id)?; + let event_id = EventId::try_from(event_id)?; + let room_id = RoomId::try_from(room_id)?; + + let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?; + + let methods = methods + .into_iter() + .map(|m| VerificationMethod::from(m)) + .collect(); + + Ok(if let Some(identity) = identity.and_then(|i| i.other()) { + let request = self.runtime.block_on(identity.request_verification( + &room_id, + &event_id, + Some(methods), + )); + + Some(request.into()) + } else { + None + }) + } + + pub fn verification_request_content( + &self, + user_id: &str, + methods: Vec, + ) -> Result, CryptoStoreError> { + let user_id = UserId::try_from(user_id)?; + + let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?; + + let methods = methods + .into_iter() + .map(|m| VerificationMethod::from(m)) + .collect(); + + Ok(if let Some(identity) = identity.and_then(|i| i.other()) { + let content = self + .runtime + .block_on(identity.verification_request_content(Some(methods))); + Some(serde_json::to_string(&content)?) + } else { + None + }) + } + + pub fn request_self_verification( + &self, + methods: Vec, + ) -> Result, CryptoStoreError> { + let identity = self + .runtime + .block_on(self.inner.get_identity(self.inner.user_id()))?; + + let methods = methods + .into_iter() + .map(|m| VerificationMethod::from(m)) + .collect(); + + Ok(if let Some(identity) = identity.and_then(|i| i.own()) { + let (verification, request) = self + .runtime + .block_on(identity.request_verification_with_methods(methods))?; + Some(RequestVerificationResult { + verification: verification.into(), + request: request.into(), + }) + } else { + None + }) + } + pub fn start_sas_with_device( &self, user_id: &str, diff --git a/rust-sdk/src/olm.udl b/rust-sdk/src/olm.udl index a924dc9f64..62c36f9a9a 100644 --- a/rust-sdk/src/olm.udl +++ b/rust-sdk/src/olm.udl @@ -117,6 +117,11 @@ dictionary VerificationRequest { }; +dictionary RequestVerificationResult { + VerificationRequest verification; + OutgoingVerificationRequest request; +}; + [Enum] interface Verification { SasV1(Sas sas); @@ -199,6 +204,20 @@ interface OlmMachine { sequence methods ); + [Throws=CryptoStoreError] + VerificationRequest? request_verification( + [ByRef] string user_id, + [ByRef] string room_id, + [ByRef] string event_id, + sequence methods + ); + [Throws=CryptoStoreError] + string? verification_request_content( + [ByRef] string user_id, + sequence methods + ); + [Throws=CryptoStoreError] + RequestVerificationResult? request_self_verification(sequence methods); [Throws=CryptoStoreError] StartSasResult? start_sas_with_device([ByRef] string user_id, [ByRef] string device_id); [Throws=CryptoStoreError]