crypto: Allow the direct start of the short SAS flow

This commit is contained in:
Damir Jelić 2021-07-01 13:15:26 +02:00
parent 85e4b5eb49
commit d24c94d0f9
6 changed files with 142 additions and 48 deletions

View File

@ -49,7 +49,7 @@ import org.matrix.android.sdk.internal.session.sync.model.ToDeviceSyncResponse
import timber.log.Timber import timber.log.Timber
import uniffi.olm.CryptoStoreErrorException import uniffi.olm.CryptoStoreErrorException
import uniffi.olm.DecryptionErrorException import uniffi.olm.DecryptionErrorException
import uniffi.olm.Device import uniffi.olm.Device as InnerDevice
import uniffi.olm.DeviceLists import uniffi.olm.DeviceLists
import uniffi.olm.KeyRequestPair import uniffi.olm.KeyRequestPair
import uniffi.olm.Logger import uniffi.olm.Logger
@ -98,7 +98,7 @@ fun setRustLogger() {
} }
/** Convert a Rust Device into a Kotlin CryptoDeviceInfo */ /** Convert a Rust Device into a Kotlin CryptoDeviceInfo */
private fun toCryptoDeviceInfo(device: Device): CryptoDeviceInfo { private fun toCryptoDeviceInfo(device: InnerDevice): CryptoDeviceInfo {
val keys = device.keys.map { (keyId, key) -> "$keyId:$device.deviceId" to key }.toMap() val keys = device.keys.map { (keyId, key) -> "$keyId:$device.deviceId" to key }.toMap()
return CryptoDeviceInfo( return CryptoDeviceInfo(
@ -128,6 +128,36 @@ internal class DeviceUpdateObserver {
} }
} }
internal class Device(
private val machine: uniffi.olm.OlmMachine,
private var inner: InnerDevice,
private val sender: RequestSender,
private val listeners: ArrayList<VerificationService.Listener>,
) {
@Throws(CryptoStoreErrorException::class)
suspend fun startVerification(): SasVerification? {
val result = withContext(Dispatchers.IO) {
machine.startSasWithDevice(inner.userId, inner.deviceId)
}
return if (result != null) {
this.sender.sendVerificationRequest(result.request)
SasVerification(
this.machine, result.sas, this.sender, this.listeners,
)
} else {
null
}
}
@Throws(CryptoStoreErrorException::class)
suspend fun markAsTrusted() {
withContext(Dispatchers.IO) {
machine.markDeviceAsTrusted(inner.userId, inner.deviceId)
}
}
}
internal class QrCodeVerification( internal class QrCodeVerification(
private val machine: uniffi.olm.OlmMachine, private val machine: uniffi.olm.OlmMachine,
private var inner: QrCode, private var inner: QrCode,
@ -670,7 +700,7 @@ internal class OlmMachine(
runBlocking { inner.discardRoomKey(roomId) } runBlocking { inner.discardRoomKey(roomId) }
} }
fun getVerificationRequests(userId: String): List<uniffi.olm.VerificationRequest> { fun getVerificationRequests(userId: String): List<VerificationRequest> {
return this.inner.getVerificationRequests(userId) return this.inner.getVerificationRequests(userId)
} }

View File

@ -33,7 +33,6 @@ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_REC
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 timber.log.Timber import timber.log.Timber
import uniffi.olm.OlmMachine import uniffi.olm.OlmMachine
import uniffi.olm.Sas
import uniffi.olm.VerificationRequest import uniffi.olm.VerificationRequest
internal class VerificationRequest( internal class VerificationRequest(
@ -66,6 +65,21 @@ internal class VerificationRequest(
} }
} }
fun isCanceled(): Boolean {
refreshData()
return this.inner.isCancelled
}
fun isDone(): Boolean {
refreshData()
return this.inner.isDone
}
fun isReady(): Boolean {
refreshData()
return this.inner.isReady
}
internal fun startQrVerification(): QrCodeVerification? { internal fun startQrVerification(): QrCodeVerification? {
val qrcode = this.machine.startQrVerification(this.inner.otherUserId, this.inner.flowId) val qrcode = this.machine.startQrVerification(this.inner.otherUserId, this.inner.flowId)
@ -99,7 +113,10 @@ internal class VerificationRequest(
} }
val request = this.machine.acceptVerificationRequest( val request = this.machine.acceptVerificationRequest(
this.inner.otherUserId, this.inner.flowId, stringMethods) this.inner.otherUserId,
this.inner.flowId,
stringMethods
)
if (request != null) { if (request != null) {
this.sender.sendVerificationRequest(request) this.sender.sendVerificationRequest(request)
@ -116,29 +133,14 @@ internal class VerificationRequest(
} }
} }
fun isCanceled(): Boolean { suspend fun startSasVerification(): SasVerification? {
refreshData()
return this.inner.isCancelled
}
fun isDone(): Boolean {
refreshData()
return this.inner.isDone
}
fun isReady(): Boolean {
refreshData()
return this.inner.isReady
}
suspend fun startSasVerification(): Sas? {
refreshData() refreshData()
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val result = machine.startSasVerification(inner.otherUserId, inner.flowId) val result = machine.startSasVerification(inner.otherUserId, inner.flowId)
if (result != null) { if (result != null) {
sender.sendVerificationRequest(result.request) sender.sendVerificationRequest(result.request)
result.sas SasVerification(machine, result.sas, sender, listeners)
} else { } else {
null null
} }

View File

@ -20,7 +20,9 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
@ -30,6 +32,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.internal.crypto.Device
import org.matrix.android.sdk.internal.crypto.OlmMachine import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.QrCodeVerification import org.matrix.android.sdk.internal.crypto.QrCodeVerification
import org.matrix.android.sdk.internal.crypto.RequestSender import org.matrix.android.sdk.internal.crypto.RequestSender
@ -39,13 +42,13 @@ import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber import timber.log.Timber
import uniffi.olm.Verification import uniffi.olm.Verification
private fun getFlowId(event: Event): String? { @JsonClass(generateAdapter = true)
@JsonClass(generateAdapter = true) data class ToDeviceVerificationEvent(
data class ToDeviceVerificationEvent( @Json(name = "sender") val sender: String?,
@Json(name = "sender") val sender: String?, @Json(name = "transaction_id") val transactionId: String,
@Json(name = "transaction_id") val transactionId: String, )
)
private fun getFlowId(event: Event): String? {
return if (event.eventId != null) { return if (event.eventId != null) {
val relatesTo = event.content.toModel<MessageRelationContent>()?.relatesTo val relatesTo = event.content.toModel<MessageRelationContent>()?.relatesTo
relatesTo?.eventId relatesTo?.eventId
@ -186,9 +189,24 @@ internal class RustVerificationService(
} }
} }
private suspend fun getDevice(userId: String, deviceID: String): Device? {
val device = withContext(Dispatchers.IO) {
olmMachine.inner().getDevice(userId, deviceID)
}
return if (device != null) {
Device(this.olmMachine.inner(), device, this.requestSender, this.listeners)
} else {
null
}
}
override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) { override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
// TODO this doesn't seem to be used anymore? // TODO this doesn't seem to be used anymore?
runBlocking { olmMachine.markDeviceAsTrusted(userId, deviceID) } runBlocking {
val device = getDevice(userId, deviceID)
device?.markAsTrusted()
}
} }
override fun onPotentiallyInterestingEventRoomFailToDecrypt(event: Event) { override fun onPotentiallyInterestingEventRoomFailToDecrypt(event: Event) {
@ -200,9 +218,14 @@ internal class RustVerificationService(
tid: String, tid: String,
): VerificationTransaction? { ): VerificationTransaction? {
val verification = this.olmMachine.getVerification(otherUserId, tid) ?: return null val verification = this.olmMachine.getVerification(otherUserId, tid) ?: return null
return when (verification) { return when (verification) {
is Verification.QrCodeV1 -> QrCodeVerification(this.olmMachine.inner(), verification.qrcode, this.requestSender, this.listeners) is Verification.QrCodeV1 -> {
is Verification.SasV1 -> SasVerification(this.olmMachine.inner(), verification.sas, this.requestSender, this.listeners) QrCodeVerification(this.olmMachine.inner(), verification.qrcode, this.requestSender, this.listeners)
}
is Verification.SasV1 -> {
SasVerification(this.olmMachine.inner(), verification.sas, this.requestSender, this.listeners)
}
} }
} }
@ -235,7 +258,8 @@ 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.
TODO() // We don't support resuming in the rust-sdk, at least for now, so let's return null here.
return null
} }
override fun requestKeyVerification( override fun requestKeyVerification(
@ -303,27 +327,34 @@ internal class RustVerificationService(
): String? { ): String? {
// should check if already one (and cancel it) // should check if already one (and cancel it)
return if (method == VerificationMethod.SAS) { return if (method == VerificationMethod.SAS) {
val request = transactionId?.let { this.getVerificationRequest(otherUserId, it) } if (transactionId != null) {
val request = this.getVerificationRequest(otherUserId, transactionId)
if (request != null) {
runBlocking { runBlocking {
val sas = request.startSasVerification() val sas = request?.startSasVerification()
if (sas != null) { if (sas != null) {
val sasTransaction = SasVerification(olmMachine.inner(), sas, requestSender, listeners) dispatchTxAdded(sas)
dispatchTxAdded(sasTransaction) sas.transactionId
sasTransaction.transactionId
} else { } else {
null null
} }
} }
} else { } else {
// This should start the short SAS flow, the one that doesn't start with // This starts the short SAS flow, the one that doesn't start with
// a `m.key.verification.request`, Element web stopped doing this, might // a `m.key.verification.request`, Element web stopped doing this, might
// be wise do do so as well // be wise do do so as well
// DeviceListBottomSheetViewModel triggers this, interestingly the method that // DeviceListBottomSheetViewModel triggers this, interestingly the method that
// triggers this is called `manuallyVerify()` // triggers this is called `manuallyVerify()`
TODO() runBlocking {
val sas = getDevice(otherUserId, otherDeviceId)?.startVerification()
if (sas != null) {
dispatchTxAdded(sas)
sas.transactionId
} else {
null
}
}
} }
} else { } else {
throw IllegalArgumentException("Unknown verification method") throw IllegalArgumentException("Unknown verification method")

View File

@ -758,6 +758,30 @@ impl OlmMachine {
}) })
} }
pub fn start_sas_with_device(
&self,
user_id: &str,
device_id: &str,
) -> Result<Option<StartSasResult>, CryptoStoreError> {
let user_id = UserId::try_from(user_id)?;
Ok(
if let Some(device) = self
.runtime
.block_on(self.inner.get_device(&user_id, device_id.into()))?
{
let (sas, request) = self.runtime.block_on(device.start_verification())?;
Some(StartSasResult {
sas: sas.into(),
request: request.into(),
})
} else {
None
},
)
}
pub fn start_sas_verification( pub fn start_sas_verification(
&self, &self,
user_id: &str, user_id: &str,

View File

@ -199,6 +199,8 @@ interface OlmMachine {
sequence<string> methods sequence<string> methods
); );
[Throws=CryptoStoreError]
StartSasResult? start_sas_with_device([ByRef] string user_id, [ByRef] string device_id);
[Throws=CryptoStoreError] [Throws=CryptoStoreError]
StartSasResult? start_sas_verification([ByRef] string user_id, [ByRef] string flow_id); StartSasResult? start_sas_verification([ByRef] string user_id, [ByRef] string flow_id);
[Throws=CryptoStoreError] [Throws=CryptoStoreError]

View File

@ -19,7 +19,7 @@ use ruma::{
use matrix_sdk_crypto::{ use matrix_sdk_crypto::{
IncomingResponse, OutgoingRequest, OutgoingVerificationRequest as SdkVerificationRequest, IncomingResponse, OutgoingRequest, OutgoingVerificationRequest as SdkVerificationRequest,
ToDeviceRequest, RoomMessageRequest, RoomMessageRequest, ToDeviceRequest,
}; };
pub enum OutgoingVerificationRequest { pub enum OutgoingVerificationRequest {
@ -39,11 +39,7 @@ pub enum OutgoingVerificationRequest {
impl From<SdkVerificationRequest> for OutgoingVerificationRequest { impl From<SdkVerificationRequest> for OutgoingVerificationRequest {
fn from(r: SdkVerificationRequest) -> Self { fn from(r: SdkVerificationRequest) -> Self {
match r { match r {
SdkVerificationRequest::ToDevice(r) => Self::ToDevice { SdkVerificationRequest::ToDevice(r) => r.into(),
request_id: r.txn_id_string(),
event_type: r.event_type.to_string(),
body: serde_json::to_string(&r.messages).expect("Can't serialize to-device body"),
},
SdkVerificationRequest::InRoom(r) => Self::InRoom { SdkVerificationRequest::InRoom(r) => Self::InRoom {
request_id: r.txn_id.to_string(), request_id: r.txn_id.to_string(),
room_id: r.room_id.to_string(), room_id: r.room_id.to_string(),
@ -55,6 +51,16 @@ impl From<SdkVerificationRequest> for OutgoingVerificationRequest {
} }
} }
impl From<ToDeviceRequest> for OutgoingVerificationRequest {
fn from(r: ToDeviceRequest) -> Self {
Self::ToDevice {
request_id: r.txn_id_string(),
event_type: r.event_type.to_string(),
body: serde_json::to_string(&r.messages).expect("Can't serialize to-device body"),
}
}
}
pub enum Request { pub enum Request {
ToDevice { ToDevice {
request_id: String, request_id: String,
@ -138,8 +144,7 @@ impl From<&RoomMessageRequest> for Request {
request_id: r.txn_id.to_string(), request_id: r.txn_id.to_string(),
room_id: r.room_id.to_string(), room_id: r.room_id.to_string(),
event_type: r.content.event_type().to_string(), event_type: r.content.event_type().to_string(),
content: serde_json::to_string(&r.content) content: serde_json::to_string(&r.content).expect("Can't serialize message content"),
.expect("Can't serialize message content"),
} }
} }
} }