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 uniffi.olm.CryptoStoreErrorException
import uniffi.olm.DecryptionErrorException
import uniffi.olm.Device
import uniffi.olm.Device as InnerDevice
import uniffi.olm.DeviceLists
import uniffi.olm.KeyRequestPair
import uniffi.olm.Logger
@ -98,7 +98,7 @@ fun setRustLogger() {
}
/** 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()
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(
private val machine: uniffi.olm.OlmMachine,
private var inner: QrCode,
@ -670,7 +700,7 @@ internal class OlmMachine(
runBlocking { inner.discardRoomKey(roomId) }
}
fun getVerificationRequests(userId: String): List<uniffi.olm.VerificationRequest> {
fun getVerificationRequests(userId: String): List<VerificationRequest> {
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 timber.log.Timber
import uniffi.olm.OlmMachine
import uniffi.olm.Sas
import uniffi.olm.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? {
val qrcode = this.machine.startQrVerification(this.inner.otherUserId, this.inner.flowId)
@ -99,7 +113,10 @@ internal class VerificationRequest(
}
val request = this.machine.acceptVerificationRequest(
this.inner.otherUserId, this.inner.flowId, stringMethods)
this.inner.otherUserId,
this.inner.flowId,
stringMethods
)
if (request != null) {
this.sender.sendVerificationRequest(request)
@ -116,29 +133,14 @@ 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
}
suspend fun startSasVerification(): Sas? {
suspend fun startSasVerification(): SasVerification? {
refreshData()
return withContext(Dispatchers.IO) {
val result = machine.startSasVerification(inner.otherUserId, inner.flowId)
if (result != null) {
sender.sendVerificationRequest(result.request)
result.sas
SasVerification(machine, result.sas, sender, listeners)
} else {
null
}

View File

@ -20,7 +20,9 @@ import android.os.Handler
import android.os.Looper
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import kotlinx.coroutines.Dispatchers
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.VerificationMethod
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.room.model.message.MessageRelationContent
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.QrCodeVerification
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 uniffi.olm.Verification
private fun getFlowId(event: Event): String? {
@JsonClass(generateAdapter = true)
data class ToDeviceVerificationEvent(
@Json(name = "sender") val sender: String?,
@Json(name = "transaction_id") val transactionId: String,
)
@JsonClass(generateAdapter = true)
data class ToDeviceVerificationEvent(
@Json(name = "sender") val sender: String?,
@Json(name = "transaction_id") val transactionId: String,
)
private fun getFlowId(event: Event): String? {
return if (event.eventId != null) {
val relatesTo = event.content.toModel<MessageRelationContent>()?.relatesTo
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) {
// 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) {
@ -200,9 +218,14 @@ internal class RustVerificationService(
tid: String,
): VerificationTransaction? {
val verification = this.olmMachine.getVerification(otherUserId, tid) ?: return null
return when (verification) {
is Verification.QrCodeV1 -> QrCodeVerification(this.olmMachine.inner(), verification.qrcode, this.requestSender, this.listeners)
is Verification.SasV1 -> SasVerification(this.olmMachine.inner(), verification.sas, this.requestSender, this.listeners)
is Verification.QrCodeV1 -> {
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?
): PendingVerificationRequest? {
// 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(
@ -303,27 +327,34 @@ internal class RustVerificationService(
): String? {
// should check if already one (and cancel it)
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 {
val sas = request.startSasVerification()
val sas = request?.startSasVerification()
if (sas != null) {
val sasTransaction = SasVerification(olmMachine.inner(), sas, requestSender, listeners)
dispatchTxAdded(sasTransaction)
sasTransaction.transactionId
dispatchTxAdded(sas)
sas.transactionId
} else {
null
}
}
} 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
// be wise do do so as well
// DeviceListBottomSheetViewModel triggers this, interestingly the method that
// triggers this is called `manuallyVerify()`
TODO()
runBlocking {
val sas = getDevice(otherUserId, otherDeviceId)?.startVerification()
if (sas != null) {
dispatchTxAdded(sas)
sas.transactionId
} else {
null
}
}
}
} else {
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(
&self,
user_id: &str,

View File

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

View File

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