crypto: Allow the displaying of QR codes

This commit is contained in:
Damir Jelić 2021-06-25 20:24:56 +02:00
parent d15269a4bd
commit 6523ca5afe
4 changed files with 212 additions and 23 deletions

View File

@ -16,6 +16,8 @@
package org.matrix.android.sdk.internal.crypto package org.matrix.android.sdk.internal.crypto
import android.os.Handler
import android.os.Looper
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import java.io.File import java.io.File
@ -26,10 +28,16 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.listeners.ProgressListener
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
@ -45,11 +53,14 @@ import uniffi.olm.Device
import uniffi.olm.DeviceLists import uniffi.olm.DeviceLists
import uniffi.olm.KeyRequestPair import uniffi.olm.KeyRequestPair
import uniffi.olm.Logger import uniffi.olm.Logger
import uniffi.olm.OutgoingVerificationRequest
import uniffi.olm.QrCode
import uniffi.olm.OlmMachine as InnerMachine import uniffi.olm.OlmMachine as InnerMachine
import uniffi.olm.ProgressListener as RustProgressListener import uniffi.olm.ProgressListener as RustProgressListener
import uniffi.olm.Request import uniffi.olm.Request
import uniffi.olm.RequestType import uniffi.olm.RequestType
import uniffi.olm.Sas import uniffi.olm.Verification
import uniffi.olm.VerificationRequest
import uniffi.olm.setLogger import uniffi.olm.setLogger
class CryptoLogger : Logger { class CryptoLogger : Logger {
@ -117,6 +128,136 @@ internal class DeviceUpdateObserver {
} }
} }
internal class QrCodeVerification(
private val machine: uniffi.olm.OlmMachine,
private var inner: QrCode,
private val sender: RequestSender,
private val listeners: ArrayList<VerificationService.Listener>,
) : QrCodeVerificationTransaction {
private val uiHandler = Handler(Looper.getMainLooper())
private var stateField: VerificationTxState = VerificationTxState.OnStarted
private fun dispatchTxUpdated() {
uiHandler.post {
listeners.forEach {
try {
it.transactionUpdated(this)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
}
override val qrCodeText: String?
get() {
val data = this.machine.generateQrCode(this.inner.otherUserId, this.inner.flowId)
// TODO Why are we encoding this to ISO_8859_1? If we're going to encode, why not base64?
return data?.fromBase64()?.toString(Charsets.ISO_8859_1)
}
override fun userHasScannedOtherQrCode(otherQrCodeText: String) {
TODO("Not yet implemented")
}
override fun otherUserScannedMyQrCode() {
val request = runBlocking { confirm() } ?: return
sendRequest(request)
}
override fun otherUserDidNotScannedMyQrCode() {
// TODO Is this code correct here? The old code seems to do this
cancelHelper(CancelCode.MismatchedKeys)
}
override var state: VerificationTxState
get() {
refreshData()
val state = when {
this.inner.isDone -> VerificationTxState.Verified
this.inner.otherSideScanned -> VerificationTxState.QrScannedByOther
this.inner.isCancelled -> {
val cancelCode = safeValueOf(this.inner.cancelCode)
val byMe = this.inner.cancelledByUs ?: false
VerificationTxState.Cancelled(cancelCode, byMe)
}
else -> {
VerificationTxState.None
}
}
return state
}
@Suppress("UNUSED_PARAMETER")
set(value) {}
override val transactionId: String
get() = this.inner.flowId
override val otherUserId: String
get() = this.inner.otherUserId
override var otherDeviceId: String?
get() = this.inner.otherDeviceId
@Suppress("UNUSED_PARAMETER")
set(value) {}
override val isIncoming: Boolean
get() = !this.inner.weStarted
override fun cancel() {
cancelHelper(CancelCode.User)
}
override fun cancel(code: CancelCode) {
cancelHelper(code)
}
override fun isToDeviceTransport(): Boolean {
return this.inner.roomId == null
}
@Throws(CryptoStoreErrorException::class)
suspend fun confirm(): OutgoingVerificationRequest? =
withContext(Dispatchers.IO) {
machine.confirmVerification(inner.otherUserId, inner.flowId)
}
private fun sendRequest(request: OutgoingVerificationRequest) {
runBlocking {
when (request) {
is OutgoingVerificationRequest.ToDevice -> {
sender.sendToDevice(request.eventType, request.body)
}
is OutgoingVerificationRequest.InRoom -> TODO()
}
}
refreshData()
dispatchTxUpdated()
}
private fun cancelHelper(code: CancelCode) {
val request = this.machine.cancelVerification(this.inner.otherUserId, inner.flowId, code.value)
if (request != null) {
sendRequest(request)
}
}
private fun refreshData() {
when (val verification = this.machine.getVerification(this.inner.otherUserId, this.inner.flowId)) {
is Verification.QrCodeV1 -> {
this.inner = verification.qrcode
}
else -> {}
}
return
}
}
internal class OlmMachine( internal class OlmMachine(
user_id: String, user_id: String,
device_id: String, device_id: String,
@ -523,24 +664,16 @@ internal class OlmMachine(
runBlocking { inner.discardRoomKey(roomId) } runBlocking { inner.discardRoomKey(roomId) }
} }
fun getVerificationRequests(userId: String): List<VerificationRequest> { fun getVerificationRequests(userId: String): List<uniffi.olm.VerificationRequest> {
return this.inner.getVerificationRequests(userId).map { return this.inner.getVerificationRequests(userId)
VerificationRequest(this.inner, it)
}
} }
fun getVerificationRequest(userId: String, flowId: String): VerificationRequest? { fun getVerificationRequest(userId: String, flowId: String): VerificationRequest? {
val request = this.inner.getVerificationRequest(userId, flowId) return this.inner.getVerificationRequest(userId, flowId)
return if (request == null) {
null
} else {
VerificationRequest(this.inner, request)
}
} }
/** Get an active verification */ /** Get an active verification */
fun getVerification(userId: String, flowId: String): Sas? { fun getVerification(userId: String, flowId: String): Verification? {
return this.inner.getVerification(userId, flowId) return this.inner.getVerification(userId, flowId)
} }
} }

View File

@ -33,6 +33,7 @@ import uniffi.olm.CryptoStoreErrorException
import uniffi.olm.OlmMachine import uniffi.olm.OlmMachine
import uniffi.olm.OutgoingVerificationRequest import uniffi.olm.OutgoingVerificationRequest
import uniffi.olm.Sas import uniffi.olm.Sas
import uniffi.olm.Verification
internal class SasVerification( internal class SasVerification(
private val machine: OlmMachine, private val machine: OlmMachine,
@ -57,10 +58,11 @@ internal class SasVerification(
} }
private fun refreshData() { private fun refreshData() {
val sas = this.machine.getVerification(this.inner.otherUserId, this.inner.flowId) when (val verification = this.machine.getVerification(this.inner.otherUserId, this.inner.flowId)) {
is Verification.SasV1 -> {
if (sas != null) { this.inner = verification.sas
this.inner = sas }
else -> {}
} }
return return

View File

@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificatio
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest
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.safeValueOf import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
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_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_QR_CODE_SHOW
@ -34,7 +35,9 @@ import uniffi.olm.VerificationRequest
internal class VerificationRequest( internal class VerificationRequest(
private val machine: OlmMachine, private val machine: OlmMachine,
private var inner: VerificationRequest private var inner: VerificationRequest,
private val sender: RequestSender,
private val listeners: ArrayList<VerificationService.Listener>,
) { ) {
private fun refreshData() { private fun refreshData() {
val request = this.machine.getVerificationRequest(this.inner.otherUserId, this.inner.flowId) val request = this.machine.getVerificationRequest(this.inner.otherUserId, this.inner.flowId)
@ -46,6 +49,21 @@ internal class VerificationRequest(
return return
} }
internal fun startQrVerification(): QrCodeVerification? {
val qrcode = this.machine.startQrVerification(this.inner.otherUserId, this.inner.flowId)
return if (qrcode != null) {
QrCodeVerification(
this.machine,
qrcode,
this.sender,
this.listeners,
)
} else {
null
}
}
fun acceptWithMethods(methods: List<VerificationMethod>): OutgoingVerificationRequest? { fun acceptWithMethods(methods: List<VerificationMethod>): OutgoingVerificationRequest? {
val stringMethods: MutableList<String> = val stringMethods: MutableList<String> =
methods methods

View File

@ -29,14 +29,17 @@ 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.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageType
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.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.model.rest.KeyVerificationDone import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationDone
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationKey import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationKey
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationRequest import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationRequest
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.OutgoingVerificationRequest import uniffi.olm.OutgoingVerificationRequest
import uniffi.olm.Verification
@SessionScope @SessionScope
internal class RustVerificationService internal class RustVerificationService
@ -183,13 +186,16 @@ constructor(
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 SasVerification(this.olmMachine.inner(), verification, this.requestSender, this.listeners) 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)
}
} }
override fun getExistingVerificationRequests( override fun getExistingVerificationRequests(
otherUserId: String otherUserId: String
): List<PendingVerificationRequest> { ): List<PendingVerificationRequest> {
return this.olmMachine.getVerificationRequests(otherUserId).map { return this.getVerificationRequests(otherUserId).map {
it.toPendingVerificationRequest() it.toPendingVerificationRequest()
} }
} }
@ -199,7 +205,7 @@ constructor(
tid: String? tid: String?
): PendingVerificationRequest? { ): PendingVerificationRequest? {
return if (tid != null) { return if (tid != null) {
olmMachine.getVerificationRequest(otherUserId, tid)?.toPendingVerificationRequest() this.getVerificationRequest(otherUserId, tid)?.toPendingVerificationRequest()
} else { } else {
null null
} }
@ -221,7 +227,7 @@ constructor(
// 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 flowId = transactionId ?: return null val flowId = transactionId ?: return null
val request = this.olmMachine.getVerificationRequest(otherUserId, flowId) val request = this.getVerificationRequest(otherUserId, flowId)
runBlocking { runBlocking {
val response = request?.startSasVerification() val response = request?.startSasVerification()
if (response != null) { if (response != null) {
@ -297,12 +303,38 @@ constructor(
return true return true
} }
private fun getVerificationRequest(otherUserId: String, transactionId: String): VerificationRequest? {
val request = this.olmMachine.getVerificationRequest(otherUserId, transactionId)
return if (request != null) {
VerificationRequest(
this.olmMachine.inner(),
request,
requestSender,
listeners,
)
} else {
null
}
}
private fun getVerificationRequests(userId: String): List<VerificationRequest> {
return this.olmMachine.getVerificationRequests(userId).map {
VerificationRequest(
this.olmMachine.inner(),
it,
this.requestSender,
this.listeners,
)
}
}
override fun readyPendingVerification( override fun readyPendingVerification(
methods: List<VerificationMethod>, methods: List<VerificationMethod>,
otherUserId: String, otherUserId: String,
transactionId: String transactionId: String
): Boolean { ): Boolean {
val request = this.olmMachine.getVerificationRequest(otherUserId, transactionId) val request = this.getVerificationRequest(otherUserId, transactionId)
return if (request != null) { return if (request != null) {
val outgoingRequest = request.acceptWithMethods(methods) val outgoingRequest = request.acceptWithMethods(methods)
@ -310,6 +342,10 @@ constructor(
if (outgoingRequest != null) { if (outgoingRequest != null) {
runBlocking { sendRequest(outgoingRequest) } runBlocking { sendRequest(outgoingRequest) }
dispatchRequestUpdated(request.toPendingVerificationRequest()) dispatchRequestUpdated(request.toPendingVerificationRequest())
val qrcode = request.startQrVerification()
if (qrcode != null) {
dispatchTxAdded(qrcode)
}
true true
} else { } else {
false false