QR code: modify APIs

This commit is contained in:
Benoit Marty 2020-01-24 17:30:10 +01:00
parent 345824daa2
commit efc8cfb9a1
9 changed files with 198 additions and 64 deletions

View file

@ -56,6 +56,7 @@ interface VerificationService {
fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String)
// Only SAS method is supported for the moment
fun beginKeyVerificationInDMs(method: VerificationMethod,
transactionId: String,
roomId: String,

View file

@ -20,7 +20,8 @@ import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.crypto.sas.SasMode
import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
import im.vector.matrix.android.internal.crypto.model.rest.supportedVerificationMethods
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.verification.SASDefaultVerificationTransaction
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoStart
import im.vector.matrix.android.internal.util.JsonCanonicalizer
@ -34,7 +35,8 @@ internal data class MessageVerificationStartContent(
@Json(name = "message_authentication_codes") override val messageAuthenticationCodes: List<String>?,
@Json(name = "short_authentication_string") override val shortAuthenticationStrings: List<String>?,
@Json(name = "method") override val method: String?,
@Json(name = "m.relates_to") val relatesTo: RelationDefaultContent?
@Json(name = "m.relates_to") val relatesTo: RelationDefaultContent?,
@Json(name = "secret") override val sharedSecret: String?
) : VerificationInfoStart {
override fun toCanonicalJson(): String? {
@ -44,22 +46,39 @@ internal data class MessageVerificationStartContent(
override val transactionID: String?
get() = relatesTo?.eventId
// TODO Move those method to the interface?
override fun isValid(): Boolean {
if (transactionID.isNullOrBlank()
|| fromDevice.isNullOrBlank()
|| method !in supportedVerificationMethods
|| keyAgreementProtocols.isNullOrEmpty()
|| hashes.isNullOrEmpty()
|| !hashes.contains("sha256") || messageAuthenticationCodes.isNullOrEmpty()
|| (!messageAuthenticationCodes.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256)
&& !messageAuthenticationCodes.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256_LONGKDF))
|| shortAuthenticationStrings.isNullOrEmpty()
|| !shortAuthenticationStrings.contains(SasMode.DECIMAL)) {
|| (method == VERIFICATION_METHOD_SAS && !isValidSas())
|| (method == VERIFICATION_METHOD_RECIPROCATE && !isValidReciprocate())) {
Timber.e("## received invalid verification request")
return false
}
return true
}
private fun isValidSas(): Boolean {
if (keyAgreementProtocols.isNullOrEmpty()
|| hashes.isNullOrEmpty()
|| !hashes.contains("sha256") || messageAuthenticationCodes.isNullOrEmpty()
|| (!messageAuthenticationCodes.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256)
&& !messageAuthenticationCodes.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256_LONGKDF))
|| shortAuthenticationStrings.isNullOrEmpty()
|| !shortAuthenticationStrings.contains(SasMode.DECIMAL)) {
return false
}
return true
}
private fun isValidReciprocate(): Boolean {
if (sharedSecret.isNullOrBlank()) {
return false
}
return true
}
override fun toEventContent() = toContent()
}

View file

@ -34,30 +34,48 @@ internal data class KeyVerificationStart(
@Json(name = "key_agreement_protocols") override val keyAgreementProtocols: List<String>? = null,
@Json(name = "hashes") override val hashes: List<String>? = null,
@Json(name = "message_authentication_codes") override val messageAuthenticationCodes: List<String>? = null,
@Json(name = "short_authentication_string") override val shortAuthenticationStrings: List<String>? = null
@Json(name = "short_authentication_string") override val shortAuthenticationStrings: List<String>? = null,
// For QR code verification
@Json(name = "secret") override val sharedSecret: String? = null
) : SendToDeviceObject, VerificationInfoStart {
override fun toCanonicalJson(): String? {
return JsonCanonicalizer.getCanonicalJson(KeyVerificationStart::class.java, this)
}
// TODO Move those method to the interface?
override fun isValid(): Boolean {
if (transactionID.isNullOrBlank()
|| fromDevice.isNullOrBlank()
|| method !in supportedVerificationMethods
|| keyAgreementProtocols.isNullOrEmpty()
|| hashes.isNullOrEmpty()
|| !hashes.contains("sha256")
|| messageAuthenticationCodes.isNullOrEmpty()
|| (!messageAuthenticationCodes.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256)
&& !messageAuthenticationCodes.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256_LONGKDF))
|| shortAuthenticationStrings.isNullOrEmpty()
|| !shortAuthenticationStrings.contains(SasMode.DECIMAL)) {
|| (method == VERIFICATION_METHOD_SAS && !isValidSas())
|| (method == VERIFICATION_METHOD_RECIPROCATE && !isValidReciprocate())) {
Timber.e("## received invalid verification request")
return false
}
return true
}
private fun isValidSas(): Boolean {
if (keyAgreementProtocols.isNullOrEmpty()
|| hashes.isNullOrEmpty()
|| !hashes.contains("sha256") || messageAuthenticationCodes.isNullOrEmpty()
|| (!messageAuthenticationCodes.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256)
&& !messageAuthenticationCodes.contains(SASDefaultVerificationTransaction.SAS_MAC_SHA256_LONGKDF))
|| shortAuthenticationStrings.isNullOrEmpty()
|| !shortAuthenticationStrings.contains(SasMode.DECIMAL)) {
return false
}
return true
}
private fun isValidReciprocate(): Boolean {
if (sharedSecret.isNullOrBlank()) {
return false
}
return true
}
override fun toSendToDeviceObject() = this
}

View file

@ -20,10 +20,8 @@ import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningServ
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
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.events.model.EventType
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
import im.vector.matrix.android.internal.crypto.model.rest.toValue
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import timber.log.Timber
@ -75,16 +73,15 @@ internal class DefaultOutgoingSASDefaultVerificationRequest(
cancel(CancelCode.UnexpectedMessage)
}
fun start(method: VerificationMethod) {
fun start() {
if (state != VerificationTxState.None) {
Timber.e("## SAS O: start verification from invalid state")
// should I cancel??
throw IllegalStateException("Interactive Key verification already started")
}
val startMessage = transport.createStart(
val startMessage = transport.createStartForSas(
credentials.deviceId ?: "",
method.toValue(),
transactionId,
KNOWN_AGREEMENT_PROTOCOLS,
KNOWN_HASHES,

View file

@ -23,19 +23,42 @@ import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
import im.vector.matrix.android.api.session.crypto.sas.*
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.VerificationService
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.api.session.crypto.sas.safeValueOf
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.LocalEcho
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.MessageRelationContent
import im.vector.matrix.android.api.session.room.model.message.MessageType
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationAcceptContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationCancelContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationDoneContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationKeyContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationMacContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationReadyContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationStartContent
import im.vector.matrix.android.internal.crypto.DeviceListManager
import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
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.rest.*
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.KeyVerificationKey
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.VERIFICATION_METHOD_RECIPROCATE
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.toValue
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
@ -43,10 +66,8 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.internal.toImmutableList
import timber.log.Timber
import java.util.*
import java.util.UUID
import javax.inject.Inject
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.set
@SessionScope
@ -708,7 +729,7 @@ internal class DefaultVerificationService @Inject constructor(
tx.transport = verificationTransportToDeviceFactory.createTransport(tx)
addTransaction(tx)
tx.start(method)
tx.start()
return txID
} else {
throw IllegalArgumentException("Unknown verification method")
@ -747,7 +768,12 @@ internal class DefaultVerificationService @Inject constructor(
otherUserId = userId
)
transport.sendVerificationRequest(methods.map { it.toValue() }, localID, userId, roomId) { syncedId, info ->
// Add reciprocate method if application declares it can scan or show QR codes
// Not sure if it ok to do that (?)
val reciprocateMethod = methods.firstOrNull { it == VerificationMethod.QR_CODE_SCAN || it == VerificationMethod.QR_CODE_SHOW }?.let { listOf(VERIFICATION_METHOD_RECIPROCATE) }.orEmpty()
val methodValues = (methods.map { it.toValue() } + reciprocateMethod).distinct()
transport.sendVerificationRequest(methodValues, localID, userId, roomId) { syncedId, info ->
// We need to update with the syncedID
updatePendingRequest(verificationRequest.copy(
transactionId = syncedId,
@ -807,7 +833,7 @@ internal class DefaultVerificationService @Inject constructor(
tx.transport = verificationTransportRoomMessageFactory.createTransport(roomId, tx)
addTransaction(tx)
tx.start(method)
tx.start()
return transactionId
} else {
throw IllegalArgumentException("Unknown verification method")

View file

@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.crypto.verification
internal interface VerificationInfoStart : VerificationInfo {
val method: String?
/**
* Alices device ID
*/
@ -58,5 +59,10 @@ internal interface VerificationInfoStart : VerificationInfo {
*/
val shortAuthenticationStrings: List<String>?
/**
* Shared secret, when starting verification with QR code
*/
val sharedSecret: String?
fun toCanonicalJson(): String?
}

View file

@ -60,13 +60,22 @@ internal interface VerificationTransport {
fun createKey(tid: String,
pubKey: String): VerificationInfoKey
fun createStart(fromDevice: String,
method: String,
transactionID: String,
keyAgreementProtocols: List<String>,
hashes: List<String>,
messageAuthenticationCodes: List<String>,
shortAuthenticationStrings: List<String>): VerificationInfoStart
/**
* Create start for SAS verification
*/
fun createStartForSas(fromDevice: String,
transactionID: String,
keyAgreementProtocols: List<String>,
hashes: List<String>,
messageAuthenticationCodes: List<String>,
shortAuthenticationStrings: List<String>): VerificationInfoStart
/**
* Create start for QR code verification
*/
fun createStartForQrCode(fromDevice: String,
transactionID: String,
sharedSecret: String): VerificationInfoStart
fun createMac(tid: String, mac: Map<String, String>, keys: String): VerificationInfoMac

View file

@ -16,14 +16,33 @@
package im.vector.matrix.android.internal.crypto.verification
import androidx.lifecycle.Observer
import androidx.work.*
import androidx.work.BackoffPolicy
import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.Operation
import androidx.work.WorkInfo
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.R
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
import im.vector.matrix.android.api.session.events.model.*
import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.LocalEcho
import im.vector.matrix.android.api.session.events.model.RelationType
import im.vector.matrix.android.api.session.events.model.UnsignedData
import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationAcceptContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationCancelContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationDoneContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationKeyContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationMacContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationReadyContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationStartContent
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
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.di.DeviceId
import im.vector.matrix.android.internal.di.SessionId
import im.vector.matrix.android.internal.di.UserId
@ -35,7 +54,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.*
import java.util.UUID
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -245,24 +264,42 @@ internal class VerificationTransportRoomMessage(
override fun createMac(tid: String, mac: Map<String, String>, keys: String) = MessageVerificationMacContent.create(tid, mac, keys)
override fun createStart(fromDevice: String,
method: String,
transactionID: String,
keyAgreementProtocols: List<String>,
hashes: List<String>,
messageAuthenticationCodes: List<String>,
shortAuthenticationStrings: List<String>): VerificationInfoStart {
override fun createStartForSas(fromDevice: String,
transactionID: String,
keyAgreementProtocols: List<String>,
hashes: List<String>,
messageAuthenticationCodes: List<String>,
shortAuthenticationStrings: List<String>): VerificationInfoStart {
return MessageVerificationStartContent(
fromDevice,
hashes,
keyAgreementProtocols,
messageAuthenticationCodes,
shortAuthenticationStrings,
method,
VERIFICATION_METHOD_SAS,
RelationDefaultContent(
type = RelationType.REFERENCE,
eventId = transactionID
)
),
null
)
}
override fun createStartForQrCode(fromDevice: String,
transactionID: String,
sharedSecret: String): VerificationInfoStart {
return MessageVerificationStartContent(
fromDevice,
null,
null,
null,
null,
VERIFICATION_METHOD_RECIPROCATE,
RelationDefaultContent(
type = RelationType.REFERENCE,
eventId = transactionID
),
sharedSecret
)
}

View file

@ -21,7 +21,14 @@ import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.*
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.KeyVerificationKey
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.KeyVerificationStart
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.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
@ -119,21 +126,35 @@ internal class VerificationTransportToDevice(
override fun createMac(tid: String, mac: Map<String, String>, keys: String) = KeyVerificationMac.create(tid, mac, keys)
override fun createStart(fromDevice: String,
method: String,
transactionID: String,
keyAgreementProtocols: List<String>,
hashes: List<String>,
messageAuthenticationCodes: List<String>,
shortAuthenticationStrings: List<String>): VerificationInfoStart {
override fun createStartForSas(fromDevice: String,
transactionID: String,
keyAgreementProtocols: List<String>,
hashes: List<String>,
messageAuthenticationCodes: List<String>,
shortAuthenticationStrings: List<String>): VerificationInfoStart {
return KeyVerificationStart(
fromDevice,
method,
VERIFICATION_METHOD_SAS,
transactionID,
keyAgreementProtocols,
hashes,
messageAuthenticationCodes,
shortAuthenticationStrings)
shortAuthenticationStrings,
null)
}
override fun createStartForQrCode(fromDevice: String,
transactionID: String,
sharedSecret: String): VerificationInfoStart {
return KeyVerificationStart(
fromDevice,
VERIFICATION_METHOD_RECIPROCATE,
transactionID,
null,
null,
null,
null,
sharedSecret)
}
override fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady {