mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
Merge pull request #921 from vector-im/decorations
Add field in QR code and make some optional
This commit is contained in:
commit
506c2dd262
21 changed files with 453 additions and 136 deletions
|
@ -47,7 +47,10 @@ interface VerificationService {
|
||||||
|
|
||||||
fun getExistingVerificationRequestInRoom(roomId: String, tid: String?): PendingVerificationRequest?
|
fun getExistingVerificationRequestInRoom(roomId: String, tid: String?): PendingVerificationRequest?
|
||||||
|
|
||||||
fun beginKeyVerification(method: VerificationMethod, otherUserId: String, otherDeviceID: String): String?
|
fun beginKeyVerification(method: VerificationMethod,
|
||||||
|
otherUserId: String,
|
||||||
|
otherDeviceId: String,
|
||||||
|
transactionId: String?): String?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request a key verification from another user using toDevice events.
|
* Request a key verification from another user using toDevice events.
|
||||||
|
@ -62,11 +65,14 @@ interface VerificationService {
|
||||||
* Request a key verification from another user using toDevice events.
|
* Request a key verification from another user using toDevice events.
|
||||||
*/
|
*/
|
||||||
fun requestKeyVerification(methods: List<VerificationMethod>,
|
fun requestKeyVerification(methods: List<VerificationMethod>,
|
||||||
otherUserId: String,
|
otherUserId: String,
|
||||||
otherDevices: List<String>?
|
otherDevices: List<String>?
|
||||||
): PendingVerificationRequest
|
): PendingVerificationRequest
|
||||||
|
|
||||||
fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String)
|
fun declineVerificationRequestInDMs(otherUserId: String,
|
||||||
|
otherDeviceId: String,
|
||||||
|
transactionId: String,
|
||||||
|
roomId: String)
|
||||||
|
|
||||||
// Only SAS method is supported for the moment
|
// Only SAS method is supported for the moment
|
||||||
fun beginKeyVerificationInDMs(method: VerificationMethod,
|
fun beginKeyVerificationInDMs(method: VerificationMethod,
|
||||||
|
@ -84,6 +90,13 @@ interface VerificationService {
|
||||||
roomId: String,
|
roomId: String,
|
||||||
transactionId: String): Boolean
|
transactionId: String): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false if the request is unknown
|
||||||
|
*/
|
||||||
|
fun readyPendingVerification(methods: List<VerificationMethod>,
|
||||||
|
otherUserId: String,
|
||||||
|
transactionId: String): Boolean
|
||||||
|
|
||||||
// fun transactionUpdated(tx: SasVerificationTransaction)
|
// fun transactionUpdated(tx: SasVerificationTransaction)
|
||||||
|
|
||||||
interface VerificationListener {
|
interface VerificationListener {
|
||||||
|
|
|
@ -26,7 +26,7 @@ import im.vector.matrix.android.internal.crypto.verification.VerificationInfoReq
|
||||||
internal data class KeyVerificationRequest(
|
internal data class KeyVerificationRequest(
|
||||||
@Json(name = "from_device") override val fromDevice: String?,
|
@Json(name = "from_device") override val fromDevice: String?,
|
||||||
@Json(name = "methods") override val methods: List<String>,
|
@Json(name = "methods") override val methods: List<String>,
|
||||||
@Json(name = "methods") override val timestamp: Long?,
|
@Json(name = "timestamp") override val timestamp: Long?,
|
||||||
@Json(name = "transaction_id") override var transactionID: String? = null
|
@Json(name = "transaction_id") override var transactionID: String? = null
|
||||||
|
|
||||||
) : SendToDeviceObject, VerificationInfoRequest {
|
) : SendToDeviceObject, VerificationInfoRequest {
|
||||||
|
|
|
@ -55,6 +55,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationCancel
|
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.KeyVerificationReady
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationRequest
|
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationRequest
|
||||||
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_QR_CODE_SCAN
|
||||||
|
@ -110,25 +111,28 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
fun onToDeviceEvent(event: Event) {
|
fun onToDeviceEvent(event: Event) {
|
||||||
GlobalScope.launch(coroutineDispatchers.crypto) {
|
GlobalScope.launch(coroutineDispatchers.crypto) {
|
||||||
when (event.getClearType()) {
|
when (event.getClearType()) {
|
||||||
EventType.KEY_VERIFICATION_START -> {
|
EventType.KEY_VERIFICATION_START -> {
|
||||||
onStartRequestReceived(event)
|
onStartRequestReceived(event)
|
||||||
}
|
}
|
||||||
EventType.KEY_VERIFICATION_CANCEL -> {
|
EventType.KEY_VERIFICATION_CANCEL -> {
|
||||||
onCancelReceived(event)
|
onCancelReceived(event)
|
||||||
}
|
}
|
||||||
EventType.KEY_VERIFICATION_ACCEPT -> {
|
EventType.KEY_VERIFICATION_ACCEPT -> {
|
||||||
onAcceptReceived(event)
|
onAcceptReceived(event)
|
||||||
}
|
}
|
||||||
EventType.KEY_VERIFICATION_KEY -> {
|
EventType.KEY_VERIFICATION_KEY -> {
|
||||||
onKeyReceived(event)
|
onKeyReceived(event)
|
||||||
}
|
}
|
||||||
EventType.KEY_VERIFICATION_MAC -> {
|
EventType.KEY_VERIFICATION_MAC -> {
|
||||||
onMacReceived(event)
|
onMacReceived(event)
|
||||||
}
|
}
|
||||||
MessageType.MSGTYPE_VERIFICATION_REQUEST -> {
|
EventType.KEY_VERIFICATION_READY -> {
|
||||||
|
onReadyReceived(event)
|
||||||
|
}
|
||||||
|
MessageType.MSGTYPE_VERIFICATION_REQUEST -> {
|
||||||
onRequestReceived(event)
|
onRequestReceived(event)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,7 +267,6 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun onRequestReceived(event: Event) {
|
private fun onRequestReceived(event: Event) {
|
||||||
val requestInfo = event.getClearContent().toModel<KeyVerificationRequest>()!!
|
val requestInfo = event.getClearContent().toModel<KeyVerificationRequest>()!!
|
||||||
|
|
||||||
|
@ -279,7 +282,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
|
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
if (checkKeysAreDownloaded(senderId, otherDeviceId) == null) {
|
if (checkKeysAreDownloaded(senderId, otherDeviceId) == null) {
|
||||||
Timber.e("## Verification device $otherDeviceId is not knwon")
|
Timber.e("## Verification device $otherDeviceId is not known")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,14 +297,13 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
isIncoming = true,
|
isIncoming = true,
|
||||||
otherUserId = senderId, // requestInfo.toUserId,
|
otherUserId = senderId, // requestInfo.toUserId,
|
||||||
roomId = null,
|
roomId = null,
|
||||||
transactionId = event.eventId,
|
transactionId = requestInfo.transactionID,
|
||||||
requestInfo = requestInfo
|
requestInfo = requestInfo
|
||||||
)
|
)
|
||||||
requestsForUser.add(pendingVerificationRequest)
|
requestsForUser.add(pendingVerificationRequest)
|
||||||
dispatchRequestAdded(pendingVerificationRequest)
|
dispatchRequestAdded(pendingVerificationRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun onRoomRequestReceived(event: Event) {
|
suspend fun onRoomRequestReceived(event: Event) {
|
||||||
Timber.v("## SAS Verification request from ${event.senderId} in room ${event.roomId}")
|
Timber.v("## SAS Verification request from ${event.senderId} in room ${event.roomId}")
|
||||||
val requestInfo = event.getClearContent().toModel<MessageVerificationRequestContent>()
|
val requestInfo = event.getClearContent().toModel<MessageVerificationRequestContent>()
|
||||||
|
@ -318,7 +320,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
// We don't want to block here
|
// We don't want to block here
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
if (checkKeysAreDownloaded(senderId, fromDevice) == null) {
|
if (checkKeysAreDownloaded(senderId, fromDevice) == null) {
|
||||||
Timber.e("## SAS Verification device $fromDevice is not knwon")
|
Timber.e("## SAS Verification device $fromDevice is not known")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -677,7 +679,29 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReadyReceived(event.senderId, event.roomId!!, readyReq)
|
handleReadyReceived(event.senderId, readyReq) {
|
||||||
|
verificationTransportRoomMessageFactory.createTransport(event.roomId!!, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun onReadyReceived(event: Event) {
|
||||||
|
val readyReq = event.getClearContent().toModel<KeyVerificationReady>()
|
||||||
|
|
||||||
|
if (readyReq == null || readyReq.isValid().not() || event.senderId == null) {
|
||||||
|
// ignore
|
||||||
|
Timber.e("## SAS Received invalid ready request")
|
||||||
|
// TODO should we cancel?
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (checkKeysAreDownloaded(event.senderId, readyReq.fromDevice ?: "") == null) {
|
||||||
|
Timber.e("## SAS Verification device ${readyReq.fromDevice} is not known")
|
||||||
|
// TODO cancel?
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReadyReceived(event.senderId, readyReq) {
|
||||||
|
verificationTransportToDeviceFactory.createTransport(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onRoomDoneReceived(event: Event) {
|
private fun onRoomDoneReceived(event: Event) {
|
||||||
|
@ -722,7 +746,9 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleReadyReceived(senderId: String, roomId: String, readyReq: VerificationInfoReady) {
|
private fun handleReadyReceived(senderId: String,
|
||||||
|
readyReq: VerificationInfoReady,
|
||||||
|
transportCreator: (DefaultVerificationTransaction) -> VerificationTransport) {
|
||||||
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == readyReq.transactionID }
|
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == readyReq.transactionID }
|
||||||
if (existingRequest == null) {
|
if (existingRequest == null) {
|
||||||
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}")
|
||||||
|
@ -733,10 +759,10 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
// Check if other user is able to scan QR code
|
// Check if other user is able to scan QR code
|
||||||
?.takeIf { it.contains(VERIFICATION_METHOD_QR_CODE_SCAN) }
|
?.takeIf { it.contains(VERIFICATION_METHOD_QR_CODE_SCAN) }
|
||||||
?.let {
|
?.let {
|
||||||
createQrCodeData(existingRequest.transactionId, existingRequest.otherUserId)
|
createQrCodeData(existingRequest.transactionId, existingRequest.otherUserId, readyReq.fromDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readyReq.methods?.orEmpty().orEmpty().contains(VERIFICATION_METHOD_RECIPROCATE)) {
|
if (readyReq.methods.orEmpty().contains(VERIFICATION_METHOD_RECIPROCATE)) {
|
||||||
// Create the pending transaction
|
// Create the pending transaction
|
||||||
val tx = DefaultQrCodeVerificationTransaction(
|
val tx = DefaultQrCodeVerificationTransaction(
|
||||||
setDeviceVerificationAction,
|
setDeviceVerificationAction,
|
||||||
|
@ -750,7 +776,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
deviceId ?: "",
|
deviceId ?: "",
|
||||||
false)
|
false)
|
||||||
|
|
||||||
tx.transport = verificationTransportRoomMessageFactory.createTransport(roomId, tx)
|
tx.transport = transportCreator.invoke(tx)
|
||||||
|
|
||||||
addTransaction(tx)
|
addTransaction(tx)
|
||||||
}
|
}
|
||||||
|
@ -760,13 +786,25 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createQrCodeData(transactionId: String?, otherUserId: String): QrCodeData? {
|
private fun createQrCodeData(requestEventId: String?, otherUserId: String, otherDeviceId: String?): QrCodeData? {
|
||||||
// Build the QR code URL
|
requestEventId ?: run {
|
||||||
val requestEventId = transactionId ?: run {
|
|
||||||
Timber.w("## Unknown requestEventId")
|
Timber.w("## Unknown requestEventId")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return when {
|
||||||
|
userId != otherUserId ->
|
||||||
|
createQrCodeDataForDistinctUser(requestEventId, otherUserId, otherDeviceId)
|
||||||
|
crossSigningService.isCrossSigningVerified() ->
|
||||||
|
// This is a self verification and I am the old device (Osborne2)
|
||||||
|
createQrCodeDataForVerifiedDevice(requestEventId, otherDeviceId)
|
||||||
|
else ->
|
||||||
|
// This is a self verification and I am the new device (Dynabook)
|
||||||
|
createQrCodeDataForUnVerifiedDevice(requestEventId, otherDeviceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createQrCodeDataForDistinctUser(requestEventId: String, otherUserId: String, otherDeviceId: String?): QrCodeData? {
|
||||||
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
|
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
|
||||||
?.masterKey()
|
?.masterKey()
|
||||||
?.unpaddedBase64PublicKey
|
?.unpaddedBase64PublicKey
|
||||||
|
@ -789,9 +827,17 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()!!
|
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()
|
||||||
|
?: run {
|
||||||
|
Timber.w("## Unable to get my fingerprint")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val otherDeviceKey = otherDeviceId
|
||||||
|
?.let {
|
||||||
|
cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint()
|
||||||
|
}
|
||||||
|
|
||||||
val generatedSharedSecret = generateSharedSecret()
|
|
||||||
return QrCodeData(
|
return QrCodeData(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
requestEventId = requestEventId,
|
requestEventId = requestEventId,
|
||||||
|
@ -800,8 +846,95 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
myMasterKey to myMasterKey,
|
myMasterKey to myMasterKey,
|
||||||
myDeviceId to myDeviceKey
|
myDeviceId to myDeviceKey
|
||||||
),
|
),
|
||||||
sharedSecret = generatedSharedSecret,
|
sharedSecret = generateSharedSecret(),
|
||||||
otherUserKey = otherUserMasterKey
|
otherUserKey = otherUserMasterKey,
|
||||||
|
otherDeviceKey = otherDeviceKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a QR code to display on the old device (Osborne2)
|
||||||
|
private fun createQrCodeDataForVerifiedDevice(requestEventId: String, otherDeviceId: String?): QrCodeData? {
|
||||||
|
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
|
||||||
|
?.masterKey()
|
||||||
|
?.unpaddedBase64PublicKey
|
||||||
|
?: run {
|
||||||
|
Timber.w("## Unable to get my master key")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val otherDeviceKey = otherDeviceId
|
||||||
|
?.let {
|
||||||
|
cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint()
|
||||||
|
}
|
||||||
|
?: run {
|
||||||
|
Timber.w("## Unable to get other device data")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val myDeviceId = deviceId
|
||||||
|
?: run {
|
||||||
|
Timber.w("## Unable to get my deviceId")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()
|
||||||
|
?: run {
|
||||||
|
Timber.w("## Unable to get my fingerprint")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return QrCodeData(
|
||||||
|
userId = userId,
|
||||||
|
requestEventId = requestEventId,
|
||||||
|
action = QrCodeData.ACTION_VERIFY,
|
||||||
|
keys = hashMapOf(
|
||||||
|
myMasterKey to myMasterKey,
|
||||||
|
myDeviceId to myDeviceKey
|
||||||
|
),
|
||||||
|
sharedSecret = generateSharedSecret(),
|
||||||
|
otherUserKey = null,
|
||||||
|
otherDeviceKey = otherDeviceKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a QR code to display on the new device (Dynabook)
|
||||||
|
private fun createQrCodeDataForUnVerifiedDevice(requestEventId: String, otherDeviceId: String?): QrCodeData? {
|
||||||
|
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
|
||||||
|
?.masterKey()
|
||||||
|
?.unpaddedBase64PublicKey
|
||||||
|
?: run {
|
||||||
|
Timber.w("## Unable to get my master key")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val myDeviceId = deviceId
|
||||||
|
?: run {
|
||||||
|
Timber.w("## Unable to get my deviceId")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()
|
||||||
|
?: run {
|
||||||
|
Timber.w("## Unable to get my fingerprint")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val otherDeviceKey = otherDeviceId
|
||||||
|
?.let {
|
||||||
|
cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint()
|
||||||
|
}
|
||||||
|
|
||||||
|
return QrCodeData(
|
||||||
|
userId = userId,
|
||||||
|
requestEventId = requestEventId,
|
||||||
|
action = QrCodeData.ACTION_VERIFY,
|
||||||
|
keys = hashMapOf(
|
||||||
|
// Note: no master key here
|
||||||
|
myDeviceId to myDeviceKey
|
||||||
|
),
|
||||||
|
sharedSecret = generateSharedSecret(),
|
||||||
|
otherUserKey = myMasterKey,
|
||||||
|
otherDeviceKey = otherDeviceKey
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -866,8 +999,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beginKeyVerification(method: VerificationMethod, otherUserId: String, otherDeviceID: String): String? {
|
override fun beginKeyVerification(method: VerificationMethod, otherUserId: String, otherDeviceId: String, transactionId: String?): String? {
|
||||||
val txID = createUniqueIDForTransaction(otherUserId, otherDeviceID)
|
val txID = transactionId?.takeIf { it.isNotEmpty() } ?: createUniqueIDForTransaction(otherUserId, otherDeviceId)
|
||||||
// should check if already one (and cancel it)
|
// should check if already one (and cancel it)
|
||||||
if (method == VerificationMethod.SAS) {
|
if (method == VerificationMethod.SAS) {
|
||||||
val tx = DefaultOutgoingSASDefaultVerificationTransaction(
|
val tx = DefaultOutgoingSASDefaultVerificationTransaction(
|
||||||
|
@ -879,7 +1012,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||||
txID,
|
txID,
|
||||||
otherUserId,
|
otherUserId,
|
||||||
otherDeviceID)
|
otherDeviceId)
|
||||||
tx.transport = verificationTransportToDeviceFactory.createTransport(tx)
|
tx.transport = verificationTransportToDeviceFactory.createTransport(tx)
|
||||||
addTransaction(tx)
|
addTransaction(tx)
|
||||||
|
|
||||||
|
@ -1083,9 +1216,10 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
transactionId,
|
transactionId,
|
||||||
otherUserId,
|
otherUserId,
|
||||||
existingRequest.requestInfo?.fromDevice ?: "",
|
existingRequest.requestInfo?.fromDevice ?: "",
|
||||||
roomId,
|
|
||||||
existingRequest.requestInfo?.methods,
|
existingRequest.requestInfo?.methods,
|
||||||
methods)
|
methods) {
|
||||||
|
verificationTransportRoomMessageFactory.createTransport(roomId, it)
|
||||||
|
}
|
||||||
if (methods.isNullOrEmpty()) {
|
if (methods.isNullOrEmpty()) {
|
||||||
Timber.i("Cannot ready this request, no common methods found txId:$transactionId")
|
Timber.i("Cannot ready this request, no common methods found txId:$transactionId")
|
||||||
// TODO buttons should not be shown in this case?
|
// TODO buttons should not be shown in this case?
|
||||||
|
@ -1108,13 +1242,52 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun readyPendingVerification(methods: List<VerificationMethod>,
|
||||||
|
otherUserId: String,
|
||||||
|
transactionId: String): Boolean {
|
||||||
|
Timber.v("## SAS readyPendingVerification $otherUserId tx:$transactionId")
|
||||||
|
// Let's find the related request
|
||||||
|
val existingRequest = getExistingVerificationRequest(otherUserId, transactionId)
|
||||||
|
if (existingRequest != null) {
|
||||||
|
// we need to send a ready event, with matching methods
|
||||||
|
val transport = verificationTransportToDeviceFactory.createTransport(null)
|
||||||
|
val computedMethods = computeReadyMethods(
|
||||||
|
transactionId,
|
||||||
|
otherUserId,
|
||||||
|
existingRequest.requestInfo?.fromDevice ?: "",
|
||||||
|
existingRequest.requestInfo?.methods,
|
||||||
|
methods) {
|
||||||
|
verificationTransportToDeviceFactory.createTransport(it)
|
||||||
|
}
|
||||||
|
if (methods.isNullOrEmpty()) {
|
||||||
|
Timber.i("Cannot ready this request, no common methods found txId:$transactionId")
|
||||||
|
// TODO buttons should not be shown in this case?
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// TODO this is not yet related to a transaction, maybe we should use another method like for cancel?
|
||||||
|
val readyMsg = transport.createReady(transactionId, deviceId ?: "", computedMethods)
|
||||||
|
transport.sendVerificationReady(
|
||||||
|
readyMsg,
|
||||||
|
otherUserId,
|
||||||
|
existingRequest.requestInfo?.fromDevice ?: "",
|
||||||
|
null // TODO handle error?
|
||||||
|
)
|
||||||
|
updatePendingRequest(existingRequest.copy(readyInfo = readyMsg))
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
Timber.e("## SAS readyPendingVerification Verification not found")
|
||||||
|
// :/ should not be possible... unless live observer very slow
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun computeReadyMethods(
|
private fun computeReadyMethods(
|
||||||
transactionId: String,
|
transactionId: String,
|
||||||
otherUserId: String,
|
otherUserId: String,
|
||||||
otherDeviceId: String,
|
otherDeviceId: String,
|
||||||
roomId: String,
|
|
||||||
otherUserMethods: List<String>?,
|
otherUserMethods: List<String>?,
|
||||||
methods: List<VerificationMethod>): List<String> {
|
methods: List<VerificationMethod>,
|
||||||
|
transportCreator: (DefaultVerificationTransaction) -> VerificationTransport): List<String> {
|
||||||
if (otherUserMethods.isNullOrEmpty()) {
|
if (otherUserMethods.isNullOrEmpty()) {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
@ -1128,7 +1301,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
|
|
||||||
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods || VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods) {
|
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods || VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods) {
|
||||||
// Other user wants to verify using QR code. Cross-signing has to be setup
|
// Other user wants to verify using QR code. Cross-signing has to be setup
|
||||||
val qrCodeData = createQrCodeData(transactionId, otherUserId)
|
val qrCodeData = createQrCodeData(transactionId, otherUserId, otherDeviceId)
|
||||||
|
|
||||||
if (qrCodeData != null) {
|
if (qrCodeData != null) {
|
||||||
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods && VerificationMethod.QR_CODE_SHOW in methods) {
|
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods && VerificationMethod.QR_CODE_SHOW in methods) {
|
||||||
|
@ -1157,7 +1330,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
deviceId ?: "",
|
deviceId ?: "",
|
||||||
false)
|
false)
|
||||||
|
|
||||||
tx.transport = verificationTransportRoomMessageFactory.createTransport(roomId, tx)
|
tx.transport = transportCreator.invoke(tx)
|
||||||
|
|
||||||
addTransaction(tx)
|
addTransaction(tx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto.verification
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
|
|
||||||
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_QR_CODE_SCAN
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
|
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
||||||
|
@ -40,7 +39,7 @@ data class PendingVerificationRequest(
|
||||||
val isSuccessful: Boolean = false,
|
val isSuccessful: Boolean = false,
|
||||||
val handledByOtherSession: Boolean = false,
|
val handledByOtherSession: Boolean = false,
|
||||||
// In case of to device it is sent to a list of devices
|
// In case of to device it is sent to a list of devices
|
||||||
val targetDevices: List<String> ? = null
|
val targetDevices: List<String>? = null
|
||||||
) {
|
) {
|
||||||
val isReady: Boolean = readyInfo != null
|
val isReady: Boolean = readyInfo != null
|
||||||
val isSent: Boolean = transactionId != null
|
val isSent: Boolean = transactionId != null
|
||||||
|
|
|
@ -80,4 +80,10 @@ internal interface VerificationTransport {
|
||||||
fun createMac(tid: String, mac: Map<String, String>, keys: String): VerificationInfoMac
|
fun createMac(tid: String, mac: Map<String, String>, keys: String): VerificationInfoMac
|
||||||
|
|
||||||
fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady
|
fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady
|
||||||
|
|
||||||
|
// TODO Refactor
|
||||||
|
fun sendVerificationReady(keyReq: VerificationInfoReady,
|
||||||
|
otherUserId: String,
|
||||||
|
otherDeviceId: String,
|
||||||
|
callback: (() -> Unit)?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,6 +332,13 @@ internal class VerificationTransportRoomMessage(
|
||||||
localEchoEventFactory.saveLocalEcho(monarchy, it)
|
localEchoEventFactory.saveLocalEcho(monarchy, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun sendVerificationReady(keyReq: VerificationInfoReady,
|
||||||
|
otherUserId: String,
|
||||||
|
otherDeviceId: String,
|
||||||
|
callback: (() -> Unit)?) {
|
||||||
|
// Not applicable
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class VerificationTransportRoomMessageFactory @Inject constructor(
|
internal class VerificationTransportRoomMessageFactory @Inject constructor(
|
||||||
|
|
|
@ -50,7 +50,6 @@ internal class VerificationTransportToDevice(
|
||||||
roomId: String?,
|
roomId: String?,
|
||||||
toDevices: List<String>?,
|
toDevices: List<String>?,
|
||||||
callback: (String?, VerificationInfoRequest?) -> Unit) {
|
callback: (String?, VerificationInfoRequest?) -> Unit) {
|
||||||
|
|
||||||
val contentMap = MXUsersDevicesMap<Any>()
|
val contentMap = MXUsersDevicesMap<Any>()
|
||||||
val keyReq = KeyVerificationRequest(
|
val keyReq = KeyVerificationRequest(
|
||||||
fromDevice = myDeviceId,
|
fromDevice = myDeviceId,
|
||||||
|
@ -77,6 +76,30 @@ internal class VerificationTransportToDevice(
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun sendVerificationReady(keyReq: VerificationInfoReady,
|
||||||
|
otherUserId: String,
|
||||||
|
otherDeviceId: String,
|
||||||
|
callback: (() -> Unit)?) {
|
||||||
|
val contentMap = MXUsersDevicesMap<Any>()
|
||||||
|
|
||||||
|
contentMap.setObject(otherUserId, otherDeviceId, keyReq)
|
||||||
|
|
||||||
|
sendToDeviceTask
|
||||||
|
.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_READY, contentMap)) {
|
||||||
|
this.callback = object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
Timber.v("## verification [$tx.transactionId] send toDevice request success")
|
||||||
|
callback?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
Timber.e("## verification [$tx.transactionId] failed to send toDevice request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.executeBy(taskExecutor)
|
||||||
|
}
|
||||||
|
|
||||||
override fun sendToOther(type: String,
|
override fun sendToOther(type: String,
|
||||||
verificationInfo: VerificationInfo,
|
verificationInfo: VerificationInfo,
|
||||||
nextState: VerificationTxState,
|
nextState: VerificationTxState,
|
||||||
|
|
|
@ -88,12 +88,29 @@ internal class DefaultQrCodeVerificationTransaction(
|
||||||
}
|
}
|
||||||
|
|
||||||
// check master key
|
// check master key
|
||||||
if (otherQrCodeData.otherUserKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) {
|
if (otherQrCodeData.userId != userId
|
||||||
|
&& otherQrCodeData.otherUserKey == null) {
|
||||||
|
// Verification with other user, other_user_key is mandatory in this case
|
||||||
|
Timber.d("## Verification QR: Invalid, missing other_user_key")
|
||||||
|
cancel(CancelCode.QrCodeInvalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherQrCodeData.otherUserKey != null
|
||||||
|
&& otherQrCodeData.otherUserKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) {
|
||||||
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.otherUserKey}")
|
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.otherUserKey}")
|
||||||
cancel(CancelCode.MismatchedKeys)
|
cancel(CancelCode.MismatchedKeys)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check device key if available
|
||||||
|
if (otherQrCodeData.otherDeviceKey != null
|
||||||
|
&& otherQrCodeData.otherDeviceKey != cryptoStore.getUserDevice(otherQrCodeData.userId, otherDeviceId ?: "")?.fingerprint()) {
|
||||||
|
Timber.d("## Verification QR: Invalid other device key")
|
||||||
|
cancel(CancelCode.MismatchedKeys)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val toVerifyDeviceIds = mutableListOf<String>()
|
val toVerifyDeviceIds = mutableListOf<String>()
|
||||||
var canTrustOtherUserMasterKey = false
|
var canTrustOtherUserMasterKey = false
|
||||||
|
|
||||||
|
@ -147,8 +164,15 @@ internal class DefaultQrCodeVerificationTransaction(
|
||||||
// qrCodeData.sharedSecret will be used to send the start request
|
// qrCodeData.sharedSecret will be used to send the start request
|
||||||
start(otherQrCodeData.sharedSecret)
|
start(otherQrCodeData.sharedSecret)
|
||||||
|
|
||||||
|
val safeOtherDeviceId = otherDeviceId
|
||||||
|
if (!otherQrCodeData.otherDeviceKey.isNullOrBlank()
|
||||||
|
&& safeOtherDeviceId != null) {
|
||||||
|
// Locally verify the device
|
||||||
|
toVerifyDeviceIds.add(safeOtherDeviceId)
|
||||||
|
}
|
||||||
|
|
||||||
// Trust the other user
|
// Trust the other user
|
||||||
trust(canTrustOtherUserMasterKey, toVerifyDeviceIds)
|
trust(canTrustOtherUserMasterKey, toVerifyDeviceIds.distinct())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun start(remoteSecret: String) {
|
fun start(remoteSecret: String) {
|
||||||
|
@ -240,8 +264,8 @@ internal class DefaultQrCodeVerificationTransaction(
|
||||||
|
|
||||||
// TODO what if the otherDevice is not in this list? and should we
|
// TODO what if the otherDevice is not in this list? and should we
|
||||||
toVerifyDeviceIds.forEach {
|
toVerifyDeviceIds.forEach {
|
||||||
setDeviceVerified(otherUserId, it)
|
setDeviceVerified(otherUserId, it)
|
||||||
}
|
}
|
||||||
transport.done(transactionId)
|
transport.done(transactionId)
|
||||||
state = VerificationTxState.Verified
|
state = VerificationTxState.Verified
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ private const val ENCODING = "utf-8"
|
||||||
* &key_<keyid>=<key-in-base64>...
|
* &key_<keyid>=<key-in-base64>...
|
||||||
* &secret=<shared_secret>
|
* &secret=<shared_secret>
|
||||||
* &other_user_key=<master-key-in-base64>
|
* &other_user_key=<master-key-in-base64>
|
||||||
|
* &other_device_key=<device-key-in-base64>
|
||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* https://matrix.to/#/@user:matrix.org?
|
* https://matrix.to/#/@user:matrix.org?
|
||||||
|
@ -40,7 +41,8 @@ private const val ENCODING = "utf-8"
|
||||||
* &key_VJEDVKUYTQ=DL7LWIw7Qp%2B4AREDACTEDOwy2BjygumSWAGfzaWY
|
* &key_VJEDVKUYTQ=DL7LWIw7Qp%2B4AREDACTEDOwy2BjygumSWAGfzaWY
|
||||||
* &key_fsh%2FfQ08N3xvh4ySXsINB%2BJ2hREDACTEDVcVOG4qqo=fsh%2FfQ08N3xvh4ySXsINB%2BJ2hREDACTEDVcVOG4qqo
|
* &key_fsh%2FfQ08N3xvh4ySXsINB%2BJ2hREDACTEDVcVOG4qqo=fsh%2FfQ08N3xvh4ySXsINB%2BJ2hREDACTEDVcVOG4qqo
|
||||||
* &secret=AjQqw51Fp6UBuPolZ2FAD5WnXc22ZhJG6iGslrVvIdw%3D
|
* &secret=AjQqw51Fp6UBuPolZ2FAD5WnXc22ZhJG6iGslrVvIdw%3D
|
||||||
* &other_user_key=WqSVLkBCS%2Fi5NqR%2F%2FymC8T7K9RPxBIuqK8Usl6Y3big
|
* &other_user_key=WqSVLkBCS%2Fi5NqRREDACTEDRPxBIuqK8Usl6Y3big
|
||||||
|
* &other_device_key=WqSVLkBREDACTEDBsfszdvsdBEvefqsdcsfBvsfcsFb
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
fun QrCodeData.toUrl(): String {
|
fun QrCodeData.toUrl(): String {
|
||||||
|
@ -58,8 +60,15 @@ fun QrCodeData.toUrl(): String {
|
||||||
|
|
||||||
append("&secret=")
|
append("&secret=")
|
||||||
append(URLEncoder.encode(sharedSecret, ENCODING))
|
append(URLEncoder.encode(sharedSecret, ENCODING))
|
||||||
append("&other_user_key=")
|
|
||||||
append(URLEncoder.encode(otherUserKey, ENCODING))
|
if (!otherUserKey.isNullOrBlank()) {
|
||||||
|
append("&other_user_key=")
|
||||||
|
append(URLEncoder.encode(otherUserKey, ENCODING))
|
||||||
|
}
|
||||||
|
if (!otherDeviceKey.isNullOrBlank()) {
|
||||||
|
append("&other_device_key=")
|
||||||
|
append(URLEncoder.encode(otherDeviceKey, ENCODING))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +109,8 @@ fun String.toQrCodeData(): QrCodeData? {
|
||||||
|
|
||||||
val requestEventId = keyValues["request"]?.takeIf { MatrixPatterns.isEventId(it) } ?: return null
|
val requestEventId = keyValues["request"]?.takeIf { MatrixPatterns.isEventId(it) } ?: return null
|
||||||
val sharedSecret = keyValues["secret"] ?: return null
|
val sharedSecret = keyValues["secret"] ?: return null
|
||||||
val otherUserKey = keyValues["other_user_key"] ?: return null
|
val otherUserKey = keyValues["other_user_key"]
|
||||||
|
val otherDeviceKey = keyValues["other_device_key"]
|
||||||
|
|
||||||
val keys = keyValues.keys
|
val keys = keyValues.keys
|
||||||
.filter { it.startsWith("key_") }
|
.filter { it.startsWith("key_") }
|
||||||
|
@ -115,6 +125,7 @@ fun String.toQrCodeData(): QrCodeData? {
|
||||||
action,
|
action,
|
||||||
keys,
|
keys,
|
||||||
sharedSecret,
|
sharedSecret,
|
||||||
otherUserKey
|
otherUserKey,
|
||||||
|
otherDeviceKey
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,18 @@ data class QrCodeData(
|
||||||
// The action
|
// The action
|
||||||
val action: String,
|
val action: String,
|
||||||
// key_<key_id>: each key that the user wants verified will have an entry of this form, where the value is the key in unpadded base64.
|
// key_<key_id>: each key that the user wants verified will have an entry of this form, where the value is the key in unpadded base64.
|
||||||
// The QR code should contain at least the user's master cross-signing key.
|
// The QR code should contain at least the user's master cross-signing key. In the case where a device does not have a cross-signing key
|
||||||
|
// (as in the case where a user logs in to a new device, and is verifying against another device), thin the QR code should contain at
|
||||||
|
// least the device's key.
|
||||||
val keys: Map<String, String>,
|
val keys: Map<String, String>,
|
||||||
// random single-use shared secret in unpadded base64. It must be at least 256-bits long (43 characters when base64-encoded).
|
// random single-use shared secret in unpadded base64. It must be at least 256-bits long (43 characters when base64-encoded).
|
||||||
val sharedSecret: String,
|
val sharedSecret: String,
|
||||||
// the other user's master cross-signing key, in unpadded base64. In other words, if Alice is displaying the QR code,
|
// the other user's master cross-signing key, in unpadded base64. In other words, if Alice is displaying the QR code,
|
||||||
// this would be the copy of Bob's master cross-signing key that Alice has.
|
// this would be the copy of Bob's master cross-signing key that Alice has.
|
||||||
val otherUserKey: String
|
val otherUserKey: String?,
|
||||||
|
// The other device's key, in unpadded base64
|
||||||
|
// This is only needed when a user is verifying their own devices, where the other device has not yet been signed with the cross-signing key.
|
||||||
|
val otherDeviceKey: String?
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
const val ACTION_VERIFY = "verify"
|
const val ACTION_VERIFY = "verify"
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.verification.qrcode
|
package im.vector.matrix.android.internal.crypto.verification.qrcode
|
||||||
|
|
||||||
|
import org.amshove.kluent.shouldBe
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
import org.amshove.kluent.shouldBeNull
|
import org.amshove.kluent.shouldBeNull
|
||||||
import org.amshove.kluent.shouldNotBeNull
|
import org.amshove.kluent.shouldNotBeNull
|
||||||
|
@ -36,10 +37,18 @@ class QrCodeTest {
|
||||||
"2" to "ghijql"
|
"2" to "ghijql"
|
||||||
),
|
),
|
||||||
sharedSecret = "sharedSecret",
|
sharedSecret = "sharedSecret",
|
||||||
otherUserKey = "otherUserKey"
|
otherUserKey = "otherUserKey",
|
||||||
|
otherDeviceKey = "otherDeviceKey"
|
||||||
)
|
)
|
||||||
|
|
||||||
private val basicUrl = "https://matrix.to/#/@benoit:matrix.org?request=%24azertyazerty&action=verify&key_1=abcdef&key_2=ghijql&secret=sharedSecret&other_user_key=otherUserKey"
|
private val basicUrl = "https://matrix.to/#/@benoit:matrix.org" +
|
||||||
|
"?request=%24azertyazerty" +
|
||||||
|
"&action=verify" +
|
||||||
|
"&key_1=abcdef" +
|
||||||
|
"&key_2=ghijql" +
|
||||||
|
"&secret=sharedSecret" +
|
||||||
|
"&other_user_key=otherUserKey" +
|
||||||
|
"&other_device_key=otherDeviceKey"
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNominalCase() {
|
fun testNominalCase() {
|
||||||
|
@ -56,7 +65,8 @@ class QrCodeTest {
|
||||||
decodedData.keys["1"]?.shouldBeEqualTo("abcdef")
|
decodedData.keys["1"]?.shouldBeEqualTo("abcdef")
|
||||||
decodedData.keys["2"]?.shouldBeEqualTo("ghijql")
|
decodedData.keys["2"]?.shouldBeEqualTo("ghijql")
|
||||||
decodedData.sharedSecret shouldBeEqualTo "sharedSecret"
|
decodedData.sharedSecret shouldBeEqualTo "sharedSecret"
|
||||||
decodedData.otherUserKey shouldBeEqualTo "otherUserKey"
|
decodedData.otherUserKey?.shouldBeEqualTo("otherUserKey")
|
||||||
|
decodedData.otherDeviceKey?.shouldBeEqualTo("otherDeviceKey")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -81,7 +91,56 @@ class QrCodeTest {
|
||||||
decodedData.keys["1"]?.shouldBeEqualTo("abcdef")
|
decodedData.keys["1"]?.shouldBeEqualTo("abcdef")
|
||||||
decodedData.keys["2"]?.shouldBeEqualTo("ghijql")
|
decodedData.keys["2"]?.shouldBeEqualTo("ghijql")
|
||||||
decodedData.sharedSecret shouldBeEqualTo "sharedSecret"
|
decodedData.sharedSecret shouldBeEqualTo "sharedSecret"
|
||||||
decodedData.otherUserKey shouldBeEqualTo "otherUserKey"
|
decodedData.otherUserKey!! shouldBeEqualTo "otherUserKey"
|
||||||
|
decodedData.otherDeviceKey!! shouldBeEqualTo "otherDeviceKey"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNoOtherUserKey() {
|
||||||
|
val url = basicQrCodeData
|
||||||
|
.copy(
|
||||||
|
otherUserKey = null
|
||||||
|
)
|
||||||
|
.toUrl()
|
||||||
|
|
||||||
|
url shouldBeEqualTo basicUrl
|
||||||
|
.replace("&other_user_key=otherUserKey", "")
|
||||||
|
|
||||||
|
val decodedData = url.toQrCodeData()
|
||||||
|
|
||||||
|
decodedData.shouldNotBeNull()
|
||||||
|
|
||||||
|
decodedData.userId shouldBeEqualTo "@benoit:matrix.org"
|
||||||
|
decodedData.requestEventId shouldBeEqualTo "\$azertyazerty"
|
||||||
|
decodedData.keys["1"]?.shouldBeEqualTo("abcdef")
|
||||||
|
decodedData.keys["2"]?.shouldBeEqualTo("ghijql")
|
||||||
|
decodedData.sharedSecret shouldBeEqualTo "sharedSecret"
|
||||||
|
decodedData.otherUserKey shouldBe null
|
||||||
|
decodedData.otherDeviceKey?.shouldBeEqualTo("otherDeviceKey")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNoOtherDeviceKey() {
|
||||||
|
val url = basicQrCodeData
|
||||||
|
.copy(
|
||||||
|
otherDeviceKey = null
|
||||||
|
)
|
||||||
|
.toUrl()
|
||||||
|
|
||||||
|
url shouldBeEqualTo basicUrl
|
||||||
|
.replace("&other_device_key=otherDeviceKey", "")
|
||||||
|
|
||||||
|
val decodedData = url.toQrCodeData()
|
||||||
|
|
||||||
|
decodedData.shouldNotBeNull()
|
||||||
|
|
||||||
|
decodedData.userId shouldBeEqualTo "@benoit:matrix.org"
|
||||||
|
decodedData.requestEventId shouldBeEqualTo "\$azertyazerty"
|
||||||
|
decodedData.keys["1"]?.shouldBeEqualTo("abcdef")
|
||||||
|
decodedData.keys["2"]?.shouldBeEqualTo("ghijql")
|
||||||
|
decodedData.sharedSecret shouldBeEqualTo "sharedSecret"
|
||||||
|
decodedData.otherUserKey?.shouldBeEqualTo("otherUserKey")
|
||||||
|
decodedData.otherDeviceKey shouldBe null
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -149,11 +208,4 @@ class QrCodeTest {
|
||||||
.toQrCodeData()
|
.toQrCodeData()
|
||||||
.shouldBeNull()
|
.shouldBeNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMissingOtherUserKey() {
|
|
||||||
basicUrl.replace("&other_user_key=otherUserKey", "")
|
|
||||||
.toQrCodeData()
|
|
||||||
.shouldBeNull()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,16 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context
|
||||||
.apply {
|
.apply {
|
||||||
contentAction = Runnable {
|
contentAction = Runnable {
|
||||||
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
|
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
|
||||||
it.navigator.openRoom(it, pr.roomId ?: "", pr.transactionId)
|
val roomId = pr.roomId
|
||||||
|
if (roomId.isNullOrBlank()) {
|
||||||
|
session?.getVerificationService()
|
||||||
|
?.readyPendingVerification(supportedVerificationMethods,
|
||||||
|
pr.otherUserId,
|
||||||
|
pr.transactionId ?: "")
|
||||||
|
it.navigator.waitSessionVerification(it)
|
||||||
|
} else {
|
||||||
|
it.navigator.openRoom(it, roomId, pr.transactionId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dismissedAction = Runnable {
|
dismissedAction = Runnable {
|
||||||
|
|
|
@ -97,11 +97,10 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) {
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
it.otherUserMxItem?.let { matrixItem ->
|
state.otherUserMxItem?.let { matrixItem ->
|
||||||
|
if (state.isMe) {
|
||||||
if (it.waitForOtherUserMode) {
|
if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) {
|
||||||
if (it.sasTransactionState == VerificationTxState.Verified || it.qrTransactionState == VerificationTxState.Verified) {
|
|
||||||
otherUserAvatarImageView.setImageResource(R.drawable.ic_shield_trusted)
|
otherUserAvatarImageView.setImageResource(R.drawable.ic_shield_trusted)
|
||||||
} else {
|
} else {
|
||||||
otherUserAvatarImageView.setImageResource(R.drawable.ic_shield_warning)
|
otherUserAvatarImageView.setImageResource(R.drawable.ic_shield_warning)
|
||||||
|
@ -111,7 +110,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
} else {
|
} else {
|
||||||
avatarRenderer.render(matrixItem, otherUserAvatarImageView)
|
avatarRenderer.render(matrixItem, otherUserAvatarImageView)
|
||||||
|
|
||||||
if (it.sasTransactionState == VerificationTxState.Verified || it.qrTransactionState == VerificationTxState.Verified) {
|
if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) {
|
||||||
otherUserNameText.text = getString(R.string.verification_verified_user, matrixItem.getBestName())
|
otherUserNameText.text = getString(R.string.verification_verified_user, matrixItem.getBestName())
|
||||||
otherUserShield.isVisible = true
|
otherUserShield.isVisible = true
|
||||||
} else {
|
} else {
|
||||||
|
@ -122,8 +121,8 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Did the request result in a SAS transaction?
|
// Did the request result in a SAS transaction?
|
||||||
if (it.sasTransactionState != null) {
|
if (state.sasTransactionState != null) {
|
||||||
when (it.sasTransactionState) {
|
when (state.sasTransactionState) {
|
||||||
is VerificationTxState.None,
|
is VerificationTxState.None,
|
||||||
is VerificationTxState.SendingStart,
|
is VerificationTxState.SendingStart,
|
||||||
is VerificationTxState.Started,
|
is VerificationTxState.Started,
|
||||||
|
@ -141,20 +140,20 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
is VerificationTxState.Verifying -> {
|
is VerificationTxState.Verifying -> {
|
||||||
showFragment(VerificationEmojiCodeFragment::class, Bundle().apply {
|
showFragment(VerificationEmojiCodeFragment::class, Bundle().apply {
|
||||||
putParcelable(MvRx.KEY_ARG, VerificationArgs(
|
putParcelable(MvRx.KEY_ARG, VerificationArgs(
|
||||||
it.otherUserMxItem?.id ?: "",
|
state.otherUserMxItem?.id ?: "",
|
||||||
// If it was outgoing it.transaction id would be null, but the pending request
|
// If it was outgoing it.transaction id would be null, but the pending request
|
||||||
// would be updated (from localID to txId)
|
// would be updated (from localID to txId)
|
||||||
it.pendingRequest.invoke()?.transactionId ?: it.transactionId))
|
state.pendingRequest.invoke()?.transactionId ?: state.transactionId))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
is VerificationTxState.Verified -> {
|
is VerificationTxState.Verified -> {
|
||||||
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
||||||
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, it.isMe))
|
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
is VerificationTxState.Cancelled -> {
|
is VerificationTxState.Cancelled -> {
|
||||||
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
||||||
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(false, it.sasTransactionState.cancelCode.value, it.isMe))
|
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(false, state.sasTransactionState.cancelCode.value, state.isMe))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,20 +161,20 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
|
|
||||||
when (it.qrTransactionState) {
|
when (state.qrTransactionState) {
|
||||||
is VerificationTxState.QrScannedByOther -> {
|
is VerificationTxState.QrScannedByOther -> {
|
||||||
showFragment(VerificationQrScannedByOtherFragment::class, Bundle())
|
showFragment(VerificationQrScannedByOtherFragment::class, Bundle())
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
is VerificationTxState.Verified -> {
|
is VerificationTxState.Verified -> {
|
||||||
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
||||||
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, it.isMe))
|
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe))
|
||||||
})
|
})
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
is VerificationTxState.Cancelled -> {
|
is VerificationTxState.Cancelled -> {
|
||||||
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
||||||
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(false, it.qrTransactionState.cancelCode.value, it.isMe))
|
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(false, state.qrTransactionState.cancelCode.value, state.isMe))
|
||||||
})
|
})
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
|
@ -185,36 +184,36 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
// At this point there is no SAS transaction for this request
|
// At this point there is no SAS transaction for this request
|
||||||
|
|
||||||
// Transaction has not yet started
|
// Transaction has not yet started
|
||||||
if (it.pendingRequest.invoke()?.cancelConclusion != null) {
|
if (state.pendingRequest.invoke()?.cancelConclusion != null) {
|
||||||
// The request has been declined, we should dismiss
|
// The request has been declined, we should dismiss
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's an outgoing
|
// If it's an outgoing
|
||||||
if (it.pendingRequest.invoke() == null || it.pendingRequest.invoke()?.isIncoming == false || it.waitForOtherUserMode) {
|
if (state.pendingRequest.invoke() == null || state.pendingRequest.invoke()?.isIncoming == false || state.waitForOtherUserMode) {
|
||||||
Timber.v("## SAS show bottom sheet for outgoing request")
|
Timber.v("## SAS show bottom sheet for outgoing request")
|
||||||
if (it.pendingRequest.invoke()?.isReady == true) {
|
if (state.pendingRequest.invoke()?.isReady == true) {
|
||||||
Timber.v("## SAS show bottom sheet for outgoing and ready request")
|
Timber.v("## SAS show bottom sheet for outgoing and ready request")
|
||||||
// Show choose method fragment with waiting
|
// Show choose method fragment with waiting
|
||||||
showFragment(VerificationChooseMethodFragment::class, Bundle().apply {
|
showFragment(VerificationChooseMethodFragment::class, Bundle().apply {
|
||||||
putParcelable(MvRx.KEY_ARG, VerificationArgs(it.otherUserMxItem?.id
|
putParcelable(MvRx.KEY_ARG, VerificationArgs(state.otherUserMxItem?.id
|
||||||
?: "", it.pendingRequest.invoke()?.transactionId))
|
?: "", state.pendingRequest.invoke()?.transactionId))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Stay on the start fragment
|
// Stay on the start fragment
|
||||||
showFragment(VerificationRequestFragment::class, Bundle().apply {
|
showFragment(VerificationRequestFragment::class, Bundle().apply {
|
||||||
putParcelable(MvRx.KEY_ARG, VerificationArgs(
|
putParcelable(MvRx.KEY_ARG, VerificationArgs(
|
||||||
it.otherUserMxItem?.id ?: "",
|
state.otherUserMxItem?.id ?: "",
|
||||||
it.pendingRequest.invoke()?.transactionId,
|
state.pendingRequest.invoke()?.transactionId,
|
||||||
it.roomId))
|
state.roomId))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (it.pendingRequest.invoke()?.isIncoming == true) {
|
} else if (state.pendingRequest.invoke()?.isIncoming == true) {
|
||||||
Timber.v("## SAS show bottom sheet for Incoming request")
|
Timber.v("## SAS show bottom sheet for Incoming request")
|
||||||
// For incoming we can switch to choose method because ready is being sent or already sent
|
// For incoming we can switch to choose method because ready is being sent or already sent
|
||||||
showFragment(VerificationChooseMethodFragment::class, Bundle().apply {
|
showFragment(VerificationChooseMethodFragment::class, Bundle().apply {
|
||||||
putParcelable(MvRx.KEY_ARG, VerificationArgs(it.otherUserMxItem?.id
|
putParcelable(MvRx.KEY_ARG, VerificationArgs(state.otherUserMxItem?.id
|
||||||
?: "", it.pendingRequest.invoke()?.transactionId))
|
?: "", state.pendingRequest.invoke()?.transactionId))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
super.invalidate()
|
super.invalidate()
|
||||||
|
|
|
@ -47,7 +47,6 @@ import im.vector.riotx.core.extensions.exhaustive
|
||||||
import im.vector.riotx.core.platform.EmptyViewEvents
|
import im.vector.riotx.core.platform.EmptyViewEvents
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.core.utils.LiveEvent
|
import im.vector.riotx.core.utils.LiveEvent
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
data class VerificationBottomSheetViewState(
|
data class VerificationBottomSheetViewState(
|
||||||
val otherUserMxItem: MatrixItem? = null,
|
val otherUserMxItem: MatrixItem? = null,
|
||||||
|
@ -95,7 +94,15 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
||||||
|
|
||||||
val userItem = session.getUser(args.otherUserId)
|
val userItem = session.getUser(args.otherUserId)
|
||||||
|
|
||||||
val pr = session.getVerificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
|
val isWaitingForOtherMode = args.waitForIncomingRequest
|
||||||
|
|
||||||
|
val pr = if (isWaitingForOtherMode) {
|
||||||
|
// See if active tx for this user and take it
|
||||||
|
session.getVerificationService().getExistingVerificationRequest(args.otherUserId)
|
||||||
|
?.firstOrNull { !it.isFinished }
|
||||||
|
} else {
|
||||||
|
session.getVerificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
|
||||||
|
}
|
||||||
|
|
||||||
val sasTx = (pr?.transactionId ?: args.verificationId)?.let {
|
val sasTx = (pr?.transactionId ?: args.verificationId)?.let {
|
||||||
session.getVerificationService().getExistingTransaction(args.otherUserId, it) as? SasVerificationTransaction
|
session.getVerificationService().getExistingTransaction(args.otherUserId, it) as? SasVerificationTransaction
|
||||||
|
@ -105,9 +112,6 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
||||||
session.getVerificationService().getExistingTransaction(args.otherUserId, it) as? QrCodeVerificationTransaction
|
session.getVerificationService().getExistingTransaction(args.otherUserId, it) as? QrCodeVerificationTransaction
|
||||||
}
|
}
|
||||||
|
|
||||||
val isWaitingForOtherMode = args.waitForIncomingRequest
|
|
||||||
// TODO see if active tx for this user and take it
|
|
||||||
|
|
||||||
return fragment.verificationViewModelFactory.create(VerificationBottomSheetViewState(
|
return fragment.verificationViewModelFactory.create(VerificationBottomSheetViewState(
|
||||||
otherUserMxItem = userItem?.toMatrixItem(),
|
otherUserMxItem = userItem?.toMatrixItem(),
|
||||||
sasTransactionState = sasTx?.state,
|
sasTransactionState = sasTx?.state,
|
||||||
|
@ -177,16 +181,24 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
||||||
is VerificationAction.StartSASVerification -> {
|
is VerificationAction.StartSASVerification -> {
|
||||||
val request = session.getVerificationService().getExistingVerificationRequest(otherUserId, action.pendingRequestTransactionId)
|
val request = session.getVerificationService().getExistingVerificationRequest(otherUserId, action.pendingRequestTransactionId)
|
||||||
?: return@withState
|
?: return@withState
|
||||||
if (roomId == null) return@withState
|
|
||||||
val otherDevice = if (request.isIncoming) request.requestInfo?.fromDevice else request.readyInfo?.fromDevice
|
val otherDevice = if (request.isIncoming) request.requestInfo?.fromDevice else request.readyInfo?.fromDevice
|
||||||
session.getVerificationService().beginKeyVerificationInDMs(
|
if (roomId == null) {
|
||||||
VerificationMethod.SAS,
|
session.getVerificationService().beginKeyVerification(
|
||||||
transactionId = action.pendingRequestTransactionId,
|
VerificationMethod.SAS,
|
||||||
roomId = roomId,
|
otherUserId = request.otherUserId,
|
||||||
otherUserId = request.otherUserId,
|
otherDeviceId = otherDevice ?: "",
|
||||||
otherDeviceId = otherDevice ?: "",
|
transactionId = action.pendingRequestTransactionId
|
||||||
callback = null
|
)
|
||||||
)
|
} else {
|
||||||
|
session.getVerificationService().beginKeyVerificationInDMs(
|
||||||
|
VerificationMethod.SAS,
|
||||||
|
transactionId = action.pendingRequestTransactionId,
|
||||||
|
roomId = roomId,
|
||||||
|
otherUserId = request.otherUserId,
|
||||||
|
otherDeviceId = otherDevice ?: "",
|
||||||
|
callback = null
|
||||||
|
)
|
||||||
|
}
|
||||||
Unit
|
Unit
|
||||||
}
|
}
|
||||||
is VerificationAction.RemoteQrCodeScanned -> {
|
is VerificationAction.RemoteQrCodeScanned -> {
|
||||||
|
@ -194,11 +206,6 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
||||||
.getExistingTransaction(action.otherUserId, action.transactionId) as? QrCodeVerificationTransaction
|
.getExistingTransaction(action.otherUserId, action.transactionId) as? QrCodeVerificationTransaction
|
||||||
existingTransaction
|
existingTransaction
|
||||||
?.userHasScannedOtherQrCode(action.scannedData)
|
?.userHasScannedOtherQrCode(action.scannedData)
|
||||||
?.let { cancelCode ->
|
|
||||||
// Something went wrong
|
|
||||||
Timber.w("## Something is not right: $cancelCode")
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is VerificationAction.OtherUserScannedSuccessfully -> {
|
is VerificationAction.OtherUserScannedSuccessfully -> {
|
||||||
val transactionId = state.transactionId ?: return@withState
|
val transactionId = state.transactionId ?: return@withState
|
||||||
|
@ -241,7 +248,6 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
||||||
if (state.waitForOtherUserMode && state.transactionId == null) {
|
if (state.waitForOtherUserMode && state.transactionId == null) {
|
||||||
// is this an incoming with that user
|
// is this an incoming with that user
|
||||||
if (tx.isIncoming && tx.otherUserId == state.otherUserMxItem?.id) {
|
if (tx.isIncoming && tx.otherUserId == state.otherUserMxItem?.id) {
|
||||||
|
|
||||||
// Also auto accept incoming if needed!
|
// Also auto accept incoming if needed!
|
||||||
if (tx is IncomingSasVerificationTransaction) {
|
if (tx is IncomingSasVerificationTransaction) {
|
||||||
if (tx.uxState == IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) {
|
if (tx.uxState == IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) {
|
||||||
|
|
|
@ -52,7 +52,6 @@ class VerificationConclusionController @Inject constructor(
|
||||||
notice(stringProvider.getString(
|
notice(stringProvider.getString(
|
||||||
if (state.isSelfVerification) R.string.verification_conclusion_ok_self_notice
|
if (state.isSelfVerification) R.string.verification_conclusion_ok_self_notice
|
||||||
else R.string.verification_conclusion_ok_notice))
|
else R.string.verification_conclusion_ok_notice))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bottomSheetVerificationBigImageItem {
|
bottomSheetVerificationBigImageItem {
|
||||||
|
|
|
@ -53,9 +53,9 @@ class VerificationConclusionViewModel(initialState: VerificationConclusionViewSt
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
VerificationConclusionViewState(
|
VerificationConclusionViewState(
|
||||||
if (args.isSuccessFull) ConclusionState.SUCCESS
|
if (args.isSuccessFull) ConclusionState.SUCCESS else ConclusionState.CANCELLED,
|
||||||
else ConclusionState.CANCELLED
|
args.isMe
|
||||||
, args.isMe)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,6 @@ class VerificationRequestController @Inject constructor(
|
||||||
val state = viewState ?: return
|
val state = viewState ?: return
|
||||||
val matrixItem = viewState?.otherUserMxItem ?: return
|
val matrixItem = viewState?.otherUserMxItem ?: return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (state.waitForOtherUserMode) {
|
if (state.waitForOtherUserMode) {
|
||||||
bottomSheetVerificationNoticeItem {
|
bottomSheetVerificationNoticeItem {
|
||||||
id("notice")
|
id("notice")
|
||||||
|
@ -62,13 +60,11 @@ class VerificationRequestController @Inject constructor(
|
||||||
id("sep")
|
id("sep")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bottomSheetVerificationWaitingItem {
|
bottomSheetVerificationWaitingItem {
|
||||||
id("waiting")
|
id("waiting")
|
||||||
title(stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName()))
|
title(stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
val styledText = matrixItem.let {
|
val styledText = matrixItem.let {
|
||||||
stringProvider.getString(R.string.verification_request_notice, it.id)
|
stringProvider.getString(R.string.verification_request_notice, it.id)
|
||||||
.toSpannable()
|
.toSpannable()
|
||||||
|
|
|
@ -168,7 +168,6 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
addButton(
|
addButton(
|
||||||
getString(R.string.later),
|
getString(R.string.later),
|
||||||
Runnable {
|
Runnable {
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
addButton(
|
addButton(
|
||||||
|
|
|
@ -100,7 +100,7 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva
|
||||||
}
|
}
|
||||||
|
|
||||||
fun manuallyVerify(device: CryptoDeviceInfo) {
|
fun manuallyVerify(device: CryptoDeviceInfo) {
|
||||||
session.getVerificationService().beginKeyVerification(VerificationMethod.SAS, userId, device.deviceId)?.let { txID ->
|
session.getVerificationService().beginKeyVerification(VerificationMethod.SAS, userId, device.deviceId, null)?.let { txID ->
|
||||||
_requestLiveData.postValue(LiveEvent(Success(VerificationAction.StartSASVerification(userId, txID))))
|
_requestLiveData.postValue(LiveEvent(Success(VerificationAction.StartSASVerification(userId, txID))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,8 @@ import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationService
|
import im.vector.matrix.android.api.session.crypto.sas.VerificationService
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.sas.VerificationTransaction
|
||||||
|
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
||||||
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||||
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||||
|
@ -44,6 +43,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||||
import im.vector.riotx.core.extensions.postLiveEvent
|
import im.vector.riotx.core.extensions.postLiveEvent
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.core.utils.LiveEvent
|
import im.vector.riotx.core.utils.LiveEvent
|
||||||
|
import im.vector.riotx.features.crypto.verification.supportedVerificationMethods
|
||||||
|
|
||||||
data class DevicesViewState(
|
data class DevicesViewState(
|
||||||
val myDeviceId: String = "",
|
val myDeviceId: String = "",
|
||||||
|
@ -96,9 +96,9 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
|
||||||
|
|
||||||
override fun transactionCreated(tx: VerificationTransaction) {}
|
override fun transactionCreated(tx: VerificationTransaction) {}
|
||||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||||
if (tx.state == VerificationTxState.Verified) {
|
if (tx.state == VerificationTxState.Verified) {
|
||||||
refreshDevicesList()
|
refreshDevicesList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,16 +168,13 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleVerify(action: DevicesAction.VerifyMyDevice) {
|
private fun handleVerify(action: DevicesAction.VerifyMyDevice) {
|
||||||
// TODO Implement request in to DEVICE!!!
|
val txID = session.getVerificationService().requestKeyVerification(supportedVerificationMethods, session.myUserId, listOf(action.deviceId))
|
||||||
val txID = session.getVerificationService().beginKeyVerification(VerificationMethod.SAS, session.myUserId, action.deviceId)
|
_requestLiveData.postValue(LiveEvent(Success(
|
||||||
if (txID != null) {
|
action.copy(
|
||||||
_requestLiveData.postValue(LiveEvent(Success(
|
userId = session.myUserId,
|
||||||
action.copy(
|
transactionId = txID.transactionId
|
||||||
userId = session.myUserId,
|
)
|
||||||
transactionId = txID
|
)))
|
||||||
)
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handlePromptRename(action: DevicesAction.PromptRename) = withState { state ->
|
private fun handlePromptRename(action: DevicesAction.PromptRename) = withState { state ->
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:background="@drawable/circle"
|
|
||||||
android:contentDescription="@string/avatar"
|
android:contentDescription="@string/avatar"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
|
Loading…
Reference in a new issue