QR code: handle the case where other user can scan QR codes

This commit is contained in:
Benoit Marty 2020-01-25 11:34:55 +01:00
parent df49ab8362
commit adc2d570eb
4 changed files with 72 additions and 9 deletions

View file

@ -55,11 +55,15 @@ import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationCancel
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationKey import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationKey
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationMac import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationMac
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SAS import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
import im.vector.matrix.android.internal.crypto.model.rest.supportedVerificationMethods import im.vector.matrix.android.internal.crypto.model.rest.supportedVerificationMethods
import im.vector.matrix.android.internal.crypto.model.rest.toValue import im.vector.matrix.android.internal.crypto.model.rest.toValue
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.verification.qrcode.QrCodeData
import im.vector.matrix.android.internal.crypto.verification.qrcode.generateSharedSecret
import im.vector.matrix.android.internal.crypto.verification.qrcode.toUrl
import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -72,6 +76,9 @@ import kotlin.collections.set
@SessionScope @SessionScope
internal class DefaultVerificationService @Inject constructor( internal class DefaultVerificationService @Inject constructor(
// TODO @UserId private val userId: String,
// TODO @DeviceId private val deviceId: String,
// TODO Do not use credential (do it in a dedicated commit)
private val credentials: Credentials, private val credentials: Credentials,
private val cryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>, private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
@ -650,7 +657,63 @@ internal class DefaultVerificationService @Inject constructor(
Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionID} fromDevice ${readyReq.fromDevice}") Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionID} fromDevice ${readyReq.fromDevice}")
return return
} }
updatePendingRequest(existingRequest.copy(readyInfo = readyReq))
var myGeneratedSharedSecret: String? = null
val qrCodeText = readyReq.methods
// Check if other user is able to scan QR code
?.takeIf { it.contains(VERIFICATION_METHOD_QR_CODE_SCAN) }
?.let {
// Build the QR code URL
val requestEventId = existingRequest.transactionId ?: run {
Timber.w("Unknown requestEventId")
return@let null
}
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
?.masterKey()
?.unpaddedBase64PublicKey
?: run {
Timber.w("Unable to get my master key")
return@let null
}
val otherUserMasterKey = crossSigningService.getUserCrossSigningKeys(existingRequest.otherUserId)
?.masterKey()
?.unpaddedBase64PublicKey
?: run {
Timber.w("Unable to get other user master key")
return@let null
}
val myDeviceId = credentials.deviceId
?: run {
Timber.w("Unable to get my deviceId")
return@let null
}
// TODO I'm pretty sure it's the correct key to put here
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()!!
val generatedSharedSecret = generateSharedSecret()
.also { myGeneratedSharedSecret = it }
QrCodeData(
userId = credentials.userId,
requestEventId = requestEventId,
action = QrCodeData.ACTION_VERIFY,
keys = hashMapOf(
myMasterKey to myMasterKey,
myDeviceId to myDeviceKey
),
sharedSecret = generatedSharedSecret,
otherUserKey = otherUserMasterKey
).toUrl()
}
updatePendingRequest(existingRequest.copy(
readyInfo = readyReq,
myGeneratedSecret = myGeneratedSharedSecret,
qrCodeText = qrCodeText
))
} }
private fun handleDoneReceived(senderId: String, doneInfo: VerificationInfo) { private fun handleDoneReceived(senderId: String, doneInfo: VerificationInfo) {

View file

@ -37,7 +37,9 @@ data class PendingVerificationRequest(
val readyInfo: VerificationInfoReady? = null, val readyInfo: VerificationInfoReady? = null,
val cancelConclusion: CancelCode? = null, val cancelConclusion: CancelCode? = null,
val isSuccessful: Boolean = false, val isSuccessful: Boolean = false,
val handledByOtherSession: Boolean = false val handledByOtherSession: Boolean = false,
val myGeneratedSecret: String? = null,
val qrCodeText: String? = null
) { ) {
val isReady: Boolean = readyInfo != null val isReady: Boolean = readyInfo != null

View file

@ -49,10 +49,10 @@ class VerificationChooseMethodController @Inject constructor(
notice(stringProvider.getString(R.string.verification_scan_notice)) notice(stringProvider.getString(R.string.verification_scan_notice))
} }
if (state.otherCanScanQrCode && !state.QRtext.isNullOrBlank()) { if (state.otherCanScanQrCode && !state.qrCodeText.isNullOrBlank()) {
bottomSheetVerificationQrCodeItem { bottomSheetVerificationQrCodeItem {
id("qr") id("qr")
data(state.QRtext) data(state.qrCodeText)
animate(false) animate(false)
} }

View file

@ -37,7 +37,7 @@ data class VerificationChooseMethodViewState(
val transactionId: String = "", val transactionId: String = "",
val otherCanShowQrCode: Boolean = false, val otherCanShowQrCode: Boolean = false,
val otherCanScanQrCode: Boolean = false, val otherCanScanQrCode: Boolean = false,
val QRtext: String? = null, val qrCodeText: String? = null,
val SASModeAvailable: Boolean = false val SASModeAvailable: Boolean = false
) : MvRxState ) : MvRxState
@ -57,8 +57,7 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
copy( copy(
otherCanShowQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SHOW) ?: false, otherCanShowQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SHOW) ?: false,
otherCanScanQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SCAN) ?: false, otherCanScanQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SCAN) ?: false,
// TODO qrCodeText = pvr?.qrCodeText,
QRtext = "https://www.example.org",
SASModeAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false SASModeAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false
) )
} }
@ -93,8 +92,7 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
transactionId = args.verificationId ?: "", transactionId = args.verificationId ?: "",
otherCanShowQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SHOW) ?: false, otherCanShowQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SHOW) ?: false,
otherCanScanQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SCAN) ?: false, otherCanScanQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SCAN) ?: false,
// TODO qrCodeText = pvr?.qrCodeText,
QRtext = "https://www.example.org",
SASModeAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false SASModeAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false
) )
} }