crypto: Allow verifications to be requested

This commit is contained in:
Damir Jelić 2021-07-08 12:49:44 +02:00
parent d24c94d0f9
commit 33c2184c52
8 changed files with 182 additions and 49 deletions

View file

@ -145,31 +145,31 @@ internal class RequestSender @Inject constructor(
} }
} }
suspend fun sendRoomMessage(request: OutgoingVerificationRequest.InRoom) { suspend fun sendRoomMessage(request: OutgoingVerificationRequest.InRoom): String {
sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId) return sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId)
} }
suspend fun sendRoomMessage(request: Request.RoomMessage) { suspend fun sendRoomMessage(request: Request.RoomMessage): String {
sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId) 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<Content>(Map::class.java) val adapter = MoshiProvider.providesMoshi().adapter<Content>(Map::class.java)
val jsonContent = adapter.fromJson(content) val jsonContent = adapter.fromJson(content)
val event = Event(eventType, transactionId, jsonContent, roomId = roomId) val event = Event(eventType, transactionId, jsonContent, roomId = roomId)
val params = SendVerificationMessageTask.Params(event) val params = SendVerificationMessageTask.Params(event)
this.sendVerificationMessageTask.get().execute(params) return this.sendVerificationMessageTask.get().execute(params)
} }
suspend fun sendToDevice(request: Request.ToDevice) { suspend fun sendToDevice(request: Request.ToDevice) {
sendToDevice(request.eventType, request.body) sendToDevice(request.eventType, request.body, request.requestId)
} }
suspend fun sendToDevice(request: OutgoingVerificationRequest.ToDevice) { 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. // TODO this produces floats for the Olm type fields, which are integers originally.
val adapter = MoshiProvider val adapter = MoshiProvider
.providesMoshi() .providesMoshi()
@ -179,7 +179,7 @@ internal class RequestSender @Inject constructor(
val userMap = MXUsersDevicesMap<Any>() val userMap = MXUsersDevicesMap<Any>()
userMap.join(jsonBody) userMap.join(jsonBody)
val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap) val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap, transactionId)
sendToDeviceTask.execute(sendToDeviceParams) sendToDeviceTask.execute(sendToDeviceParams)
} }
} }

View file

@ -203,21 +203,20 @@ internal class QrCodeVerification(
override var state: VerificationTxState override var state: VerificationTxState
get() { get() {
refreshData() refreshData()
val state = when {
this.inner.isDone -> VerificationTxState.Verified return when {
this.inner.hasBeenConfirmed -> VerificationTxState.WaitingOtherReciprocateConfirm inner.isDone -> VerificationTxState.Verified
this.inner.otherSideScanned -> VerificationTxState.QrScannedByOther inner.hasBeenConfirmed -> VerificationTxState.WaitingOtherReciprocateConfirm
this.inner.isCancelled -> { inner.otherSideScanned -> VerificationTxState.QrScannedByOther
val cancelCode = safeValueOf(this.inner.cancelCode) inner.isCancelled -> {
val byMe = this.inner.cancelledByUs ?: false val cancelCode = safeValueOf(inner.cancelCode)
val byMe = inner.cancelledByUs ?: false
VerificationTxState.Cancelled(cancelCode, byMe) VerificationTxState.Cancelled(cancelCode, byMe)
} }
else -> { else -> {
VerificationTxState.None VerificationTxState.None
} }
} }
return state
} }
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
set(value) {} set(value) {}

View file

@ -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_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_RECIPROCATE
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS 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 timber.log.Timber
import uniffi.olm.OlmMachine import uniffi.olm.OlmMachine
import uniffi.olm.VerificationRequest import uniffi.olm.VerificationRequest
@ -96,16 +97,7 @@ internal class VerificationRequest(
} }
suspend fun acceptWithMethods(methods: List<VerificationMethod>) { suspend fun acceptWithMethods(methods: List<VerificationMethod>) {
val stringMethods: MutableList<String> = val stringMethods: MutableList<String> = methods.map { it.toValue() }.toMutableList()
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()
if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) || if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) ||
stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SCAN)) { stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SCAN)) {

View file

@ -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.RequestSender
import org.matrix.android.sdk.internal.crypto.SasVerification import org.matrix.android.sdk.internal.crypto.SasVerification
import org.matrix.android.sdk.internal.crypto.VerificationRequest 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 org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber import timber.log.Timber
import uniffi.olm.Verification import uniffi.olm.Verification
@ -258,7 +262,22 @@ internal class RustVerificationService(
tid: String? tid: String?
): PendingVerificationRequest? { ): PendingVerificationRequest? {
// This is only used in `RoomDetailViewModel` to resume the verification. // 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 return null
} }
@ -267,8 +286,19 @@ internal class RustVerificationService(
otherUserId: String, otherUserId: String,
otherDevices: List<String>? otherDevices: List<String>?
): PendingVerificationRequest { ): PendingVerificationRequest {
// This was mostly a copy paste of the InDMs method, do the same here
TODO() val stringMethods: MutableList<String> = 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( override fun requestKeyVerificationInDMs(
@ -278,10 +308,21 @@ internal class RustVerificationService(
localId: String? localId: String?
): PendingVerificationRequest { ): PendingVerificationRequest {
Timber.i("## SAS Requesting verification to user: $otherUserId in room $roomId") Timber.i("## SAS Requesting verification to user: $otherUserId in room $roomId")
val stringMethods: MutableList<String> = methods.map { it.toValue() }.toMutableList()
// TODO cancel other active requests, create a new request here and if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) ||
// dispatch it stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SCAN)) {
TODO() 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( override fun readyPendingVerification(
@ -347,10 +388,10 @@ internal class RustVerificationService(
// DeviceListBottomSheetViewModel triggers this, interestingly the method that // DeviceListBottomSheetViewModel triggers this, interestingly the method that
// triggers this is called `manuallyVerify()` // triggers this is called `manuallyVerify()`
runBlocking { runBlocking {
val sas = getDevice(otherUserId, otherDeviceId)?.startVerification() val verification = getDevice(otherUserId, otherDeviceId)?.startVerification()
if (sas != null) { if (verification != null) {
dispatchTxAdded(sas) dispatchTxAdded(verification)
sas.transactionId verification.transactionId
} else { } else {
null null
} }

View file

@ -25,11 +25,11 @@ features = ["lax_deserialize"]
[dependencies.matrix-sdk-common] [dependencies.matrix-sdk-common]
git = "https://github.com/matrix-org/matrix-rust-sdk/" git = "https://github.com/matrix-org/matrix-rust-sdk/"
rev = "9052843acbc762388f4ae59e6dad0d580914cf59" rev = "c5df7c5356c2540ede95f41e3565e91ca7de5777"
[dependencies.matrix-sdk-crypto] [dependencies.matrix-sdk-crypto]
git = "https://github.com/matrix-org/matrix-rust-sdk/" git = "https://github.com/matrix-org/matrix-rust-sdk/"
rev = "9052843acbc762388f4ae59e6dad0d580914cf59" rev = "c5df7c5356c2540ede95f41e3565e91ca7de5777"
features = ["sled_cryptostore"] features = ["sled_cryptostore"]
[dependencies.tokio] [dependencies.tokio]

View file

@ -8,7 +8,8 @@ pub use device::Device;
pub use error::{CryptoStoreError, DecryptionError, KeyImportError, MachineCreationError}; pub use error::{CryptoStoreError, DecryptionError, KeyImportError, MachineCreationError};
pub use logger::{set_logger, Logger}; pub use logger::{set_logger, Logger};
pub use machine::{ pub use machine::{
KeyRequestPair, OlmMachine, QrCode, Sas, StartSasResult, Verification, VerificationRequest, KeyRequestPair, OlmMachine, QrCode, RequestVerificationResult, Sas, StartSasResult,
Verification, VerificationRequest,
}; };
pub use responses::{ pub use responses::{
DeviceLists, KeysImportResult, OutgoingVerificationRequest, Request, RequestType, DeviceLists, KeysImportResult, OutgoingVerificationRequest, Request, RequestType,

View file

@ -22,7 +22,7 @@ use ruma::{
key::verification::VerificationMethod, room::encrypted::EncryptedEventContent, key::verification::VerificationMethod, room::encrypted::EncryptedEventContent,
AnyMessageEventContent, EventContent, SyncMessageEvent, AnyMessageEventContent, EventContent, SyncMessageEvent,
}, },
DeviceKeyAlgorithm, RoomId, UserId, DeviceKeyAlgorithm, EventId, RoomId, UserId,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::value::RawValue; use serde_json::value::RawValue;
@ -128,6 +128,11 @@ impl From<InnerSas> for Sas {
} }
} }
pub struct RequestVerificationResult {
pub verification: VerificationRequest,
pub request: OutgoingVerificationRequest,
}
pub struct VerificationRequest { pub struct VerificationRequest {
pub other_user_id: String, pub other_user_id: String,
pub other_device_id: Option<String>, pub other_device_id: Option<String>,
@ -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<Verification> { pub fn get_verification(&self, user_id: &str, flow_id: &str) -> Option<Verification> {
let user_id = UserId::try_from(user_id).ok()?; let user_id = UserId::try_from(user_id).ok()?;
self.inner self.inner
@ -758,6 +758,87 @@ impl OlmMachine {
}) })
} }
pub fn request_verification(
&self,
user_id: &str,
room_id: &str,
event_id: &str,
methods: Vec<String>,
) -> Result<Option<VerificationRequest>, 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<String>,
) -> Result<Option<String>, 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<String>,
) -> Result<Option<RequestVerificationResult>, 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( pub fn start_sas_with_device(
&self, &self,
user_id: &str, user_id: &str,

View file

@ -117,6 +117,11 @@ dictionary VerificationRequest {
}; };
dictionary RequestVerificationResult {
VerificationRequest verification;
OutgoingVerificationRequest request;
};
[Enum] [Enum]
interface Verification { interface Verification {
SasV1(Sas sas); SasV1(Sas sas);
@ -199,6 +204,20 @@ interface OlmMachine {
sequence<string> methods sequence<string> methods
); );
[Throws=CryptoStoreError]
VerificationRequest? request_verification(
[ByRef] string user_id,
[ByRef] string room_id,
[ByRef] string event_id,
sequence<string> methods
);
[Throws=CryptoStoreError]
string? verification_request_content(
[ByRef] string user_id,
sequence<string> methods
);
[Throws=CryptoStoreError]
RequestVerificationResult? request_self_verification(sequence<string> methods);
[Throws=CryptoStoreError] [Throws=CryptoStoreError]
StartSasResult? start_sas_with_device([ByRef] string user_id, [ByRef] string device_id); StartSasResult? start_sas_with_device([ByRef] string user_id, [ByRef] string device_id);
[Throws=CryptoStoreError] [Throws=CryptoStoreError]