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) {
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<Content>(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<Any>()
userMap.join(jsonBody)
val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap)
val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap, transactionId)
sendToDeviceTask.execute(sendToDeviceParams)
}
}

View file

@ -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) {}

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_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<VerificationMethod>) {
val stringMethods: MutableList<String> =
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<String> = methods.map { it.toValue() }.toMutableList()
if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) ||
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.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<String>?
): 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(
@ -278,10 +308,21 @@ internal class RustVerificationService(
localId: String?
): PendingVerificationRequest {
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
// 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
}

View file

@ -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]

View file

@ -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,

View file

@ -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<InnerSas> for Sas {
}
}
pub struct RequestVerificationResult {
pub verification: VerificationRequest,
pub request: OutgoingVerificationRequest,
}
pub struct VerificationRequest {
pub other_user_id: 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> {
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<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(
&self,
user_id: &str,

View file

@ -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<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]
StartSasResult? start_sas_with_device([ByRef] string user_id, [ByRef] string device_id);
[Throws=CryptoStoreError]