Create asValidObject method - make it compiles.

This commit is contained in:
Benoit Marty 2020-03-05 15:29:44 +01:00
parent 7b5a50ec6e
commit a3f8a53a52
19 changed files with 204 additions and 181 deletions

View File

@ -37,8 +37,12 @@ internal data class MessageVerificationDoneContent(
return null
}
return ValidVerificationDone
return ValidVerificationDone(
transactionID!!
)
}
}
internal object ValidVerificationDone
internal data class ValidVerificationDone(
val transactionID: String
)

View File

@ -34,7 +34,7 @@ internal data class MessageVerificationStartContent(
@Json(name = "secret") override val sharedSecret: String?
) : VerificationInfoStart {
override fun toCanonicalJson(): String? {
override fun toCanonicalJson(): String {
return JsonCanonicalizer.getCanonicalJson(MessageVerificationStartContent::class.java, this)
}

View File

@ -36,7 +36,7 @@ internal data class KeyVerificationStart(
@Json(name = "secret") override val sharedSecret: String? = null
) : SendToDeviceObject, VerificationInfoStart {
override fun toCanonicalJson(): String? {
override fun toCanonicalJson(): String {
return JsonCanonicalizer.getCanonicalJson(KeyVerificationStart::class.java, this)
}

View File

@ -76,7 +76,7 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
}
}
override fun onVerificationStart(startReq: VerificationInfoStart) {
override fun onVerificationStart(startReq: ValidVerificationInfoStart.SasVerificationInfoStart) {
Timber.v("## SAS I: received verification request from state $state")
if (state != VerificationTxState.None) {
Timber.e("## SAS I: received verification request from invalid state")
@ -100,10 +100,10 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
// Select a key agreement protocol, a hash algorithm, a message authentication code,
// and short authentication string methods out of the lists given in requester's message.
val agreedProtocol = startReq!!.keyAgreementProtocols?.firstOrNull { KNOWN_AGREEMENT_PROTOCOLS.contains(it) }
val agreedHash = startReq!!.hashes?.firstOrNull { KNOWN_HASHES.contains(it) }
val agreedMac = startReq!!.messageAuthenticationCodes?.firstOrNull { KNOWN_MACS.contains(it) }
val agreedShortCode = startReq!!.shortAuthenticationStrings?.filter { KNOWN_SHORT_CODES.contains(it) }
val agreedProtocol = startReq!!.keyAgreementProtocols.firstOrNull { KNOWN_AGREEMENT_PROTOCOLS.contains(it) }
val agreedHash = startReq!!.hashes.firstOrNull { KNOWN_HASHES.contains(it) }
val agreedMac = startReq!!.messageAuthenticationCodes.firstOrNull { KNOWN_MACS.contains(it) }
val agreedShortCode = startReq!!.shortAuthenticationStrings.filter { KNOWN_SHORT_CODES.contains(it) }
// No common key sharing/hashing/hmac/SAS methods.
// If a device is unable to complete the verification because the devices are unable to find a common key sharing,
@ -141,12 +141,12 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
}
private fun doAccept(accept: VerificationInfoAccept) {
this.accepted = accept
this.accepted = accept.asValidObject()
Timber.v("## SAS incoming accept request id:$transactionId")
// The hash commitment is the hash (using the selected hash algorithm) of the unpadded base64 representation of QB,
// concatenated with the canonical JSON representation of the content of the m.key.verification.start message
val concat = getSAS().publicKey + startReq!!.toCanonicalJson()
val concat = getSAS().publicKey + startReq!!.canonicalJson
accept.commitment = hashUsingAgreedHashMethod(concat) ?: ""
// we need to send this to other device now
state = VerificationTxState.SendingAccept
@ -158,12 +158,12 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
}
}
override fun onVerificationAccept(accept: VerificationInfoAccept) {
override fun onVerificationAccept(accept: ValidVerificationInfoAccept) {
Timber.v("## SAS invalid message for incoming request id:$transactionId")
cancel(CancelCode.UnexpectedMessage)
}
override fun onKeyVerificationKey(vKey: VerificationInfoKey) {
override fun onKeyVerificationKey(vKey: ValidVerificationInfoKey) {
Timber.v("## SAS received key for request id:$transactionId")
if (state != VerificationTxState.SendingAccept && state != VerificationTxState.Accepted) {
Timber.e("## SAS received key from invalid state $state")
@ -213,7 +213,7 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
state = VerificationTxState.ShortCodeReady
}
override fun onKeyVerificationMac(vKey: VerificationInfoMac) {
override fun onKeyVerificationMac(vKey: ValidVerificationInfoMac) {
Timber.v("## SAS I: received mac for request id:$transactionId")
// Check for state?
if (state != VerificationTxState.SendingKey

View File

@ -74,7 +74,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
}
}
override fun onVerificationStart(startReq: VerificationInfoStart) {
override fun onVerificationStart(startReq: ValidVerificationInfoStart.SasVerificationInfoStart) {
Timber.e("## SAS O: onVerificationStart - unexpected id:$transactionId")
cancel(CancelCode.UnexpectedMessage)
}
@ -94,8 +94,8 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
KNOWN_MACS,
KNOWN_SHORT_CODES
)
startReq = startMessage
kk
startReq = startMessage.asValidObject() as? ValidVerificationInfoStart.SasVerificationInfoStart
state = VerificationTxState.SendingStart
sendToOther(
@ -130,7 +130,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
// )
// }
override fun onVerificationAccept(accept: VerificationInfoAccept) {
override fun onVerificationAccept(accept: ValidVerificationInfoAccept) {
Timber.v("## SAS O: onVerificationAccept id:$transactionId")
if (state != VerificationTxState.Started) {
Timber.e("## SAS O: received accept request from invalid state $state")
@ -141,7 +141,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
if (!KNOWN_AGREEMENT_PROTOCOLS.contains(accept.keyAgreementProtocol)
|| !KNOWN_HASHES.contains(accept.hash)
|| !KNOWN_MACS.contains(accept.messageAuthenticationCode)
|| accept.shortAuthenticationStrings!!.intersect(KNOWN_SHORT_CODES).isEmpty()) {
|| accept.shortAuthenticationStrings.intersect(KNOWN_SHORT_CODES).isEmpty()) {
Timber.e("## SAS O: received accept request from invalid state")
cancel(CancelCode.UnknownMethod)
return
@ -167,7 +167,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
}
}
override fun onKeyVerificationKey(vKey: VerificationInfoKey) {
override fun onKeyVerificationKey(vKey: ValidVerificationInfoKey) {
Timber.v("## SAS O: onKeyVerificationKey id:$transactionId")
if (state != VerificationTxState.SendingKey && state != VerificationTxState.KeySent) {
Timber.e("## received key from invalid state $state")
@ -182,7 +182,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
// in Bobs m.key.verification.key and the content of Alices m.key.verification.start message.
// check commitment
val concat = vKey.key + startReq!!.toCanonicalJson()
val concat = vKey.key + startReq!!.canonicalJson
val otherCommitment = hashUsingAgreedHashMethod(concat) ?: ""
if (accepted!!.commitment.equals(otherCommitment)) {
@ -206,7 +206,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
}
}
override fun onKeyVerificationMac(vKey: VerificationInfoMac) {
override fun onKeyVerificationMac(vKey: ValidVerificationInfoMac) {
Timber.v("## SAS O: onKeyVerificationMac id:$transactionId")
if (state != VerificationTxState.OnKeyReceived
&& state != VerificationTxState.ShortCodeReady

View File

@ -45,6 +45,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageVerificati
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.message.ValidVerificationDone
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
@ -268,9 +269,9 @@ internal class DefaultVerificationService @Inject constructor(
}
private fun onRequestReceived(event: Event) {
val requestInfo = event.getClearContent().toModel<KeyVerificationRequest>()!!
val validRequestInfo = event.getClearContent().toModel<KeyVerificationRequest>()?.asValidObject()
if (!requestInfo.isValid()) {
if (validRequestInfo == null) {
// ignore
Timber.e("## SAS Received invalid key request")
return
@ -278,7 +279,7 @@ internal class DefaultVerificationService @Inject constructor(
val senderId = event.senderId ?: return
// We don't want to block here
val otherDeviceId = requestInfo.fromDevice ?: return
val otherDeviceId = validRequestInfo.fromDevice
GlobalScope.launch {
if (checkKeysAreDownloaded(senderId, otherDeviceId) == null) {
@ -297,9 +298,9 @@ internal class DefaultVerificationService @Inject constructor(
isIncoming = true,
otherUserId = senderId, // requestInfo.toUserId,
roomId = null,
transactionId = requestInfo.transactionID,
localID = requestInfo.transactionID!!,
requestInfo = requestInfo
transactionId = validRequestInfo.transactionID,
localID = validRequestInfo.transactionID,
requestInfo = validRequestInfo
)
requestsForUser.add(pendingVerificationRequest)
dispatchRequestAdded(pendingVerificationRequest)
@ -307,10 +308,10 @@ internal class DefaultVerificationService @Inject constructor(
suspend fun onRoomRequestReceived(event: Event) {
Timber.v("## SAS Verification request from ${event.senderId} in room ${event.roomId}")
val requestInfo = event.getClearContent().toModel<MessageVerificationRequestContent>()
?: return
val requestInfo = event.getClearContent().toModel<MessageVerificationRequestContent>() ?: return
val validRequestInfo = requestInfo.asValidObject() ?: return
val senderId = event.senderId ?: return
val fromDevice = requestInfo.fromDevice ?: return
if (requestInfo.toUserId != userId) {
// I should ignore this, it's not for me
@ -320,8 +321,8 @@ internal class DefaultVerificationService @Inject constructor(
// We don't want to block here
GlobalScope.launch {
if (checkKeysAreDownloaded(senderId, fromDevice) == null) {
Timber.e("## SAS Verification device $fromDevice is not known")
if (checkKeysAreDownloaded(senderId, validRequestInfo.fromDevice) == null) {
Timber.e("## SAS Verification device ${validRequestInfo.fromDevice} is not known")
}
}
@ -338,7 +339,7 @@ internal class DefaultVerificationService @Inject constructor(
roomId = event.roomId,
transactionId = event.eventId,
localID = event.eventId!!,
requestInfo = requestInfo
requestInfo = validRequestInfo
)
requestsForUser.add(pendingVerificationRequest)
dispatchRequestAdded(pendingVerificationRequest)
@ -362,10 +363,12 @@ internal class DefaultVerificationService @Inject constructor(
relatesTo = event.content.toModel<MessageRelationContent>()?.relatesTo
)
val validStartReq = startReq?.asValidObject()
val otherUserId = event.senderId
if (startReq?.isValid()?.not() == true) {
if (validStartReq == null) {
Timber.e("## received invalid verification request")
if (startReq.transactionID != null) {
if (startReq?.transactionID != null) {
verificationTransportRoomMessageFactory.createTransport(event.roomId ?: "", null)
.cancelTransaction(
startReq.transactionID ?: "",
@ -377,14 +380,14 @@ internal class DefaultVerificationService @Inject constructor(
return
}
handleStart(otherUserId, startReq as VerificationInfoStart) {
handleStart(otherUserId, validStartReq) {
it.transport = verificationTransportRoomMessageFactory.createTransport(event.roomId ?: "", it)
}?.let {
verificationTransportRoomMessageFactory.createTransport(event.roomId ?: "", null)
.cancelTransaction(
startReq.transactionID ?: "",
validStartReq.transactionID,
otherUserId!!,
startReq.fromDevice ?: event.getSenderKey()!!,
validStartReq.fromDevice,
it
)
}
@ -392,16 +395,17 @@ internal class DefaultVerificationService @Inject constructor(
private suspend fun onStartRequestReceived(event: Event) {
Timber.e("## SAS received Start request ${event.eventId}")
val startReq = event.getClearContent().toModel<KeyVerificationStart>()!!
val startReq = event.getClearContent().toModel<KeyVerificationStart>()
val validStartReq = startReq?.asValidObject()
Timber.v("## SAS received Start request $startReq")
val otherUserId = event.senderId
if (!startReq.isValid()) {
val otherUserId = event.senderId!!
if (validStartReq == null) {
Timber.e("## SAS received invalid verification request")
if (startReq.transactionID != null) {
if (startReq?.transactionID != null) {
verificationTransportToDeviceFactory.createTransport(null).cancelTransaction(
startReq.transactionID,
otherUserId!!,
otherUserId,
startReq.fromDevice ?: event.getSenderKey()!!,
CancelCode.UnknownMethod
)
@ -409,13 +413,13 @@ internal class DefaultVerificationService @Inject constructor(
return
}
// Download device keys prior to everything
handleStart(otherUserId, startReq) {
handleStart(otherUserId, validStartReq) {
it.transport = verificationTransportToDeviceFactory.createTransport(it)
}?.let {
verificationTransportToDeviceFactory.createTransport(null).cancelTransaction(
startReq.transactionID ?: "",
otherUserId!!,
startReq.fromDevice ?: event.getSenderKey()!!,
validStartReq.transactionID,
otherUserId,
validStartReq.fromDevice,
it
)
}
@ -424,14 +428,14 @@ internal class DefaultVerificationService @Inject constructor(
/**
* Return a CancelCode to make the caller cancel the verification. Else return null
*/
private suspend fun handleStart(otherUserId: String?, startReq: VerificationInfoStart, txConfigure: (DefaultVerificationTransaction) -> Unit): CancelCode? {
private suspend fun handleStart(otherUserId: String?, startReq: ValidVerificationInfoStart, txConfigure: (DefaultVerificationTransaction) -> Unit): CancelCode? {
Timber.d("## SAS onStartRequestReceived ${startReq.transactionID}")
if (checkKeysAreDownloaded(otherUserId!!, startReq.fromDevice ?: "") != null) {
val tid = startReq.transactionID!!
if (checkKeysAreDownloaded(otherUserId!!, startReq.fromDevice) != null) {
val tid = startReq.transactionID
val existing = getExistingTransaction(otherUserId, tid)
when (startReq.method) {
VERIFICATION_METHOD_SAS -> {
when (startReq) {
is ValidVerificationInfoStart.SasVerificationInfoStart -> {
when (existing) {
is SasVerificationTransaction -> {
// should cancel both!
@ -462,7 +466,7 @@ internal class DefaultVerificationService @Inject constructor(
}
// Ok we can create a SAS transaction
Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}")
Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID}")
// If there is a corresponding request, we can auto accept
// as we are the one requesting in first place (or we accepted the request)
// I need to check if the pending request was related to this device also
@ -479,14 +483,14 @@ internal class DefaultVerificationService @Inject constructor(
cryptoStore,
crossSigningService,
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
startReq.transactionID!!,
startReq.transactionID,
otherUserId,
autoAccept).also { txConfigure(it) }
addTransaction(tx)
tx.acceptVerificationEvent(otherUserId, startReq)
tx.onVerificationStart(startReq)
return null
}
VERIFICATION_METHOD_RECIPROCATE -> {
is ValidVerificationInfoStart.ReciprocateVerificationInfoStart -> {
// Other user has scanned my QR code
if (existing is DefaultQrCodeVerificationTransaction) {
existing.onStartReceived(startReq)
@ -496,10 +500,6 @@ internal class DefaultVerificationService @Inject constructor(
return CancelCode.UnexpectedMessage
}
}
else -> {
Timber.e("## SAS onStartRequestReceived - unknown method ${startReq.method}")
return CancelCode.UnknownMethod
}
}
} else {
return CancelCode.UnexpectedMessage
@ -529,24 +529,27 @@ internal class DefaultVerificationService @Inject constructor(
// relates_to is in clear in encrypted payload
relatesTo = event.content.toModel<MessageRelationContent>()?.relatesTo
)
if (cancelReq == null || cancelReq.isValid().not()) {
val validCancelReq = cancelReq?.asValidObject()
if (validCancelReq == null) {
// ignore
Timber.e("## SAS Received invalid key request")
// TODO should we cancel?
return
}
getExistingVerificationRequest(event.senderId ?: "", cancelReq.transactionID)?.let {
updatePendingRequest(it.copy(cancelConclusion = safeValueOf(cancelReq.code)))
getExistingVerificationRequest(event.senderId ?: "", validCancelReq.transactionID)?.let {
updatePendingRequest(it.copy(cancelConclusion = safeValueOf(validCancelReq.code)))
// Should we remove it from the list?
}
handleOnCancel(event.senderId!!, cancelReq)
handleOnCancel(event.senderId!!, validCancelReq)
}
private fun onCancelReceived(event: Event) {
Timber.v("## SAS onCancelReceived")
val cancelReq = event.getClearContent().toModel<KeyVerificationCancel>()!!
val cancelReq = event.getClearContent().toModel<KeyVerificationCancel>()?.asValidObject()
if (!cancelReq.isValid()) {
if (cancelReq == null) {
// ignore
Timber.e("## SAS Received invalid cancel request")
return
@ -556,11 +559,11 @@ internal class DefaultVerificationService @Inject constructor(
handleOnCancel(otherUserId, cancelReq)
}
private fun handleOnCancel(otherUserId: String, cancelReq: VerificationInfoCancel) {
private fun handleOnCancel(otherUserId: String, cancelReq: ValidVerificationInfoCancel) {
Timber.v("## SAS onCancelReceived otherUser:$otherUserId reason:${cancelReq.reason}")
val existingTransaction = getExistingTransaction(otherUserId, cancelReq.transactionID!!)
val existingRequest = getExistingVerificationRequest(otherUserId, cancelReq.transactionID!!)
val existingTransaction = getExistingTransaction(otherUserId, cancelReq.transactionID)
val existingRequest = getExistingVerificationRequest(otherUserId, cancelReq.transactionID)
if (existingRequest != null) {
// Mark this request as cancelled
@ -582,30 +585,28 @@ internal class DefaultVerificationService @Inject constructor(
relatesTo = event.content.toModel<MessageRelationContent>()?.relatesTo
)
?: return
handleAccept(accept, event.senderId!!)
val validAccept = accept.asValidObject() ?: return
handleAccept(validAccept, event.senderId!!)
}
private fun onAcceptReceived(event: Event) {
Timber.d("## SAS Received Accept $event")
val acceptReq = event.getClearContent().toModel<KeyVerificationAccept>() ?: return
val acceptReq = event.getClearContent().toModel<KeyVerificationAccept>()?.asValidObject() ?: return
handleAccept(acceptReq, event.senderId!!)
}
private fun handleAccept(acceptReq: VerificationInfoAccept, senderId: String) {
if (!acceptReq.isValid()) {
// ignore
Timber.e("## SAS Received invalid accept request")
return
}
private fun handleAccept(acceptReq: ValidVerificationInfoAccept, senderId: String) {
val otherUserId = senderId
val existing = getExistingTransaction(otherUserId, acceptReq.transactionID!!)
val existing = getExistingTransaction(otherUserId, acceptReq.transactionID)
if (existing == null) {
Timber.e("## SAS Received invalid accept request")
return
}
if (existing is SASDefaultVerificationTransaction) {
existing.acceptVerificationEvent(otherUserId, acceptReq)
existing.onVerificationAccept(acceptReq)
} else {
// not other types now
}
@ -617,7 +618,8 @@ internal class DefaultVerificationService @Inject constructor(
// relates_to is in clear in encrypted payload
relatesTo = event.content.toModel<MessageRelationContent>()?.relatesTo
)
if (keyReq == null || keyReq.isValid().not()) {
?.asValidObject()
if (keyReq == null) {
// ignore
Timber.e("## SAS Received invalid key request")
// TODO should we cancel?
@ -627,9 +629,9 @@ internal class DefaultVerificationService @Inject constructor(
}
private fun onKeyReceived(event: Event) {
val keyReq = event.getClearContent().toModel<KeyVerificationKey>()!!
val keyReq = event.getClearContent().toModel<KeyVerificationKey>()?.asValidObject()
if (!keyReq.isValid()) {
if (keyReq == null) {
// ignore
Timber.e("## SAS Received invalid key request")
return
@ -637,16 +639,16 @@ internal class DefaultVerificationService @Inject constructor(
handleKeyReceived(event, keyReq)
}
private fun handleKeyReceived(event: Event, keyReq: VerificationInfoKey) {
private fun handleKeyReceived(event: Event, keyReq: ValidVerificationInfoKey) {
Timber.d("## SAS Received Key from ${event.senderId} with info $keyReq")
val otherUserId = event.senderId!!
val existing = getExistingTransaction(otherUserId, keyReq.transactionID!!)
val existing = getExistingTransaction(otherUserId, keyReq.transactionID)
if (existing == null) {
Timber.e("## SAS Received invalid key request")
return
}
if (existing is SASDefaultVerificationTransaction) {
existing.acceptVerificationEvent(otherUserId, keyReq)
existing.onKeyVerificationKey(keyReq)
} else {
// not other types now
}
@ -658,7 +660,8 @@ internal class DefaultVerificationService @Inject constructor(
// relates_to is in clear in encrypted payload
relatesTo = event.content.toModel<MessageRelationContent>()?.relatesTo
)
if (macReq == null || macReq.isValid().not() || event.senderId == null) {
?.asValidObject()
if (macReq == null || event.senderId == null) {
// ignore
Timber.e("## SAS Received invalid mac request")
// TODO should we cancel?
@ -673,13 +676,14 @@ internal class DefaultVerificationService @Inject constructor(
// relates_to is in clear in encrypted payload
relatesTo = event.content.toModel<MessageRelationContent>()?.relatesTo
)
if (readyReq == null || readyReq.isValid().not() || event.senderId == null) {
?.asValidObject()
if (readyReq == null || event.senderId == null) {
// ignore
Timber.e("## SAS Received invalid ready request")
// TODO should we cancel?
return
}
if (checkKeysAreDownloaded(event.senderId, readyReq.fromDevice ?: "") == null) {
if (checkKeysAreDownloaded(event.senderId, readyReq.fromDevice) == null) {
Timber.e("## SAS Verification device ${readyReq.fromDevice} is not known")
// TODO cancel?
return
@ -691,15 +695,15 @@ internal class DefaultVerificationService @Inject constructor(
}
private suspend fun onReadyReceived(event: Event) {
val readyReq = event.getClearContent().toModel<KeyVerificationReady>()
val readyReq = event.getClearContent().toModel<KeyVerificationReady>()?.asValidObject()
if (readyReq == null || readyReq.isValid().not() || event.senderId == null) {
if (readyReq == null || event.senderId == null) {
// ignore
Timber.e("## SAS Received invalid ready request")
// TODO should we cancel?
return
}
if (checkKeysAreDownloaded(event.senderId, readyReq.fromDevice ?: "") == null) {
if (checkKeysAreDownloaded(event.senderId, readyReq.fromDevice) == null) {
Timber.e("## SAS Verification device ${readyReq.fromDevice} is not known")
// TODO cancel?
return
@ -716,8 +720,9 @@ internal class DefaultVerificationService @Inject constructor(
// relates_to is in clear in encrypted payload
relatesTo = event.content.toModel<MessageRelationContent>()?.relatesTo
)
?.asValidObject()
if (doneReq == null || doneReq.isValid().not() || event.senderId == null) {
if (doneReq == null || event.senderId == null) {
// ignore
Timber.e("## SAS Received invalid Done request")
// TODO should we cancel?
@ -728,9 +733,9 @@ internal class DefaultVerificationService @Inject constructor(
}
private fun onMacReceived(event: Event) {
val macReq = event.getClearContent().toModel<KeyVerificationMac>()!!
val macReq = event.getClearContent().toModel<KeyVerificationMac>()?.asValidObject()
if (!macReq.isValid() || event.senderId == null) {
if (macReq == null || event.senderId == null) {
// ignore
Timber.e("## SAS Received invalid mac request")
return
@ -738,22 +743,22 @@ internal class DefaultVerificationService @Inject constructor(
handleMacReceived(event.senderId, macReq)
}
private fun handleMacReceived(senderId: String, macReq: VerificationInfoMac) {
private fun handleMacReceived(senderId: String, macReq: ValidVerificationInfoMac) {
Timber.v("## SAS Received $macReq")
val existing = getExistingTransaction(senderId, macReq.transactionID!!)
val existing = getExistingTransaction(senderId, macReq.transactionID)
if (existing == null) {
Timber.e("## SAS Received invalid Mac request")
return
}
if (existing is SASDefaultVerificationTransaction) {
existing.acceptVerificationEvent(senderId, macReq)
existing.onKeyVerificationMac(macReq)
} else {
// not other types known for now
}
}
private fun handleReadyReceived(senderId: String,
readyReq: VerificationInfoReady,
readyReq: ValidVerificationInfoReady,
transportCreator: (DefaultVerificationTransaction) -> VerificationTransport) {
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == readyReq.transactionID }
if (existingRequest == null) {
@ -763,16 +768,16 @@ internal class DefaultVerificationService @Inject constructor(
val qrCodeData = readyReq.methods
// 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 {
createQrCodeData(existingRequest.transactionId, existingRequest.otherUserId, readyReq.fromDevice)
}
if (readyReq.methods.orEmpty().contains(VERIFICATION_METHOD_RECIPROCATE)) {
if (readyReq.methods.contains(VERIFICATION_METHOD_RECIPROCATE)) {
// Create the pending transaction
val tx = DefaultQrCodeVerificationTransaction(
setDeviceVerificationAction,
readyReq.transactionID!!,
readyReq.transactionID,
senderId,
readyReq.fromDevice,
crossSigningService,
@ -886,7 +891,7 @@ internal class DefaultVerificationService @Inject constructor(
)
}
private fun handleDoneReceived(senderId: String, doneInfo: VerificationInfo) {
private fun handleDoneReceived(senderId: String, doneInfo: ValidVerificationDone) {
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneInfo.transactionID }
if (existingRequest == null) {
Timber.e("## SAS Received Done for unknown request txId:${doneInfo.transactionID}")
@ -1186,7 +1191,7 @@ internal class DefaultVerificationService @Inject constructor(
CancelCode.User,
null // TODO handle error?
)
updatePendingRequest(existingRequest.copy(readyInfo = readyMsg))
updatePendingRequest(existingRequest.copy(readyInfo = readyMsg.asValidObject()))
return true
} else {
Timber.e("## SAS readyPendingVerificationInDMs Verification not found")
@ -1214,7 +1219,7 @@ internal class DefaultVerificationService @Inject constructor(
}
if (methods.isNullOrEmpty()) {
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?
return false
}
// TODO this is not yet related to a transaction, maybe we should use another method like for cancel?
@ -1225,7 +1230,7 @@ internal class DefaultVerificationService @Inject constructor(
existingRequest.requestInfo?.fromDevice ?: "",
null // TODO handle error?
)
updatePendingRequest(existingRequest.copy(readyInfo = readyMsg))
updatePendingRequest(existingRequest.copy(readyInfo = readyMsg.asValidObject()))
return true
} else {
Timber.e("## SAS readyPendingVerification Verification not found")

View File

@ -41,6 +41,4 @@ internal abstract class DefaultVerificationTransaction(
fun removeListener(listener: Listener) {
listeners.remove(listener)
}
abstract fun acceptVerificationEvent(senderId: String, info: VerificationInfo)
}

View File

@ -33,8 +33,8 @@ data class PendingVerificationRequest(
val otherUserId: String,
val roomId: String?,
val transactionId: String? = null,
val requestInfo: VerificationInfoRequest? = null,
val readyInfo: VerificationInfoReady? = null,
val requestInfo: ValidVerificationInfoRequest? = null,
val readyInfo: ValidVerificationInfoReady? = null,
val cancelConclusion: CancelCode? = null,
val isSuccessful: Boolean = false,
val handledByOtherSession: Boolean = false,

View File

@ -89,13 +89,13 @@ internal abstract class SASDefaultVerificationTransaction(
private var olmSas: OlmSAS? = null
var startReq: VerificationInfoStart? = null
var accepted: VerificationInfoAccept? = null
var startReq: ValidVerificationInfoStart.SasVerificationInfoStart? = null
var accepted: ValidVerificationInfoAccept? = null
var otherKey: String? = null
var shortCodeBytes: ByteArray? = null
var myMac: VerificationInfoMac? = null
var theirMac: VerificationInfoMac? = null
var theirMac: ValidVerificationInfoMac? = null
fun getSAS(): OlmSAS {
if (olmSas == null) olmSas = OlmSAS()
@ -201,25 +201,13 @@ internal abstract class SASDefaultVerificationTransaction(
return transport is VerificationTransportToDevice
}
override fun acceptVerificationEvent(senderId: String, info: VerificationInfo) {
when (info) {
is VerificationInfoStart -> onVerificationStart(info)
is VerificationInfoAccept -> onVerificationAccept(info)
is VerificationInfoKey -> onKeyVerificationKey(info)
is VerificationInfoMac -> onKeyVerificationMac(info)
else -> {
// nop
}
}
}
abstract fun onVerificationStart(startReq: ValidVerificationInfoStart.SasVerificationInfoStart)
abstract fun onVerificationStart(startReq: VerificationInfoStart)
abstract fun onVerificationAccept(accept: ValidVerificationInfoAccept)
abstract fun onVerificationAccept(accept: VerificationInfoAccept)
abstract fun onKeyVerificationKey(vKey: ValidVerificationInfoKey)
abstract fun onKeyVerificationKey(vKey: VerificationInfoKey)
abstract fun onKeyVerificationMac(vKey: VerificationInfoMac)
abstract fun onKeyVerificationMac(vKey: ValidVerificationInfoMac)vMac
protected fun verifyMacs() {
Timber.v("## SAS verifying macs for id:$transactionId")
@ -238,7 +226,7 @@ internal abstract class SASDefaultVerificationTransaction(
userId + deviceId +
transactionId
val commaSeparatedListOfKeyIds = theirMac!!.mac!!.keys.sorted().joinToString(",")
val commaSeparatedListOfKeyIds = theirMac!!.mac.keys.sorted().joinToString(",")
val keyStrings = macUsingAgreedMethod(commaSeparatedListOfKeyIds, baseInfo + "KEY_IDS")
if (theirMac!!.keys != keyStrings) {
@ -250,7 +238,7 @@ internal abstract class SASDefaultVerificationTransaction(
val verifiedDevices = ArrayList<String>()
// cannot be empty because it has been validated
theirMac!!.mac!!.keys.forEach {
theirMac!!.mac.keys.forEach {
val keyIDNoPrefix = it.withoutPrefix("ed25519:")
val otherDeviceKey = otherUserKnownDevices?.get(keyIDNoPrefix)?.fingerprint()
if (otherDeviceKey == null) {
@ -273,7 +261,7 @@ internal abstract class SASDefaultVerificationTransaction(
val otherCrossSigningMasterKeyPublic = otherMasterKey?.unpaddedBase64PublicKey
if (otherCrossSigningMasterKeyPublic != null) {
// Did the user signed his master key
theirMac!!.mac!!.keys.forEach {
theirMac!!.mac.keys.forEach {
val keyIDNoPrefix = it.withoutPrefix("ed25519:")
if (keyIDNoPrefix == otherCrossSigningMasterKeyPublic) {
// Check the signature
@ -350,11 +338,11 @@ internal abstract class SASDefaultVerificationTransaction(
transport.cancelTransaction(transactionId, otherUserId, otherDeviceId ?: "", code)
}
protected fun sendToOther(type: String,
keyToDevice: VerificationInfo,
nextState: VerificationTxState,
onErrorReason: CancelCode,
onDone: (() -> Unit)?) {
protected fun <T> sendToOther(type: String,
keyToDevice: VerificationInfo<T>,
nextState: VerificationTxState,
onErrorReason: CancelCode,
onDone: (() -> Unit)?) {
transport.sendToOther(type, keyToDevice, nextState, onErrorReason, onDone)
}

View File

@ -31,6 +31,7 @@ internal interface VerificationInfoCancel : VerificationInfo<ValidVerificationIn
return null
}
return ValidVerificationInfoCancel(
transactionID!!,
code!!,
reason
)
@ -38,6 +39,7 @@ internal interface VerificationInfoCancel : VerificationInfo<ValidVerificationIn
}
internal data class ValidVerificationInfoCancel(
val transactionID: String,
val code: String,
val reason: String?
)

View File

@ -29,6 +29,7 @@ internal interface VerificationInfoKey : VerificationInfo<ValidVerificationInfoK
return null
}
return ValidVerificationInfoKey(
transactionID!!,
key!!
)
}
@ -39,5 +40,6 @@ internal interface VerificationInfoKeyFactory {
}
internal data class ValidVerificationInfoKey(
val transactionID: String,
val key: String
)

View File

@ -34,6 +34,7 @@ internal interface VerificationInfoMac : VerificationInfo<ValidVerificationInfoM
return null
}
return ValidVerificationInfoMac(
transactionID!!,
mac!!,
keys!!
)
@ -45,6 +46,7 @@ internal interface VerificationInfoMacFactory {
}
internal data class ValidVerificationInfoMac(
val transactionID: String,
val mac: Map<String, String>,
val keys: String
)

View File

@ -42,6 +42,7 @@ internal interface VerificationInfoReady : VerificationInfo<ValidVerificationInf
}
return ValidVerificationInfoReady(
transactionID!!,
fromDevice!!,
methods!!
)
@ -53,6 +54,7 @@ internal interface MessageVerificationReadyFactory {
}
internal data class ValidVerificationInfoReady(
val transactionID: String,
val fromDevice: String,
val methods: List<String>
)

View File

@ -40,6 +40,7 @@ internal interface VerificationInfoRequest : VerificationInfo<ValidVerificationI
return null
}
return ValidVerificationInfoRequest(
transactionID!!,
fromDevice!!,
methods!!,
timestamp
@ -48,6 +49,7 @@ internal interface VerificationInfoRequest : VerificationInfo<ValidVerificationI
}
internal data class ValidVerificationInfoRequest(
val transactionID: String,
val fromDevice: String,
val methods: List<String>,
val timestamp: Long?

View File

@ -61,7 +61,7 @@ internal interface VerificationInfoStart : VerificationInfo<ValidVerificationInf
*/
val sharedSecret: String?
fun toCanonicalJson(): String?
fun toCanonicalJson(): String
override fun asValidObject(): ValidVerificationInfoStart? {
if (transactionID.isNullOrBlank()
@ -81,10 +81,13 @@ internal interface VerificationInfoStart : VerificationInfo<ValidVerificationInf
null
} else {
ValidVerificationInfoStart.SasVerificationInfoStart(
transactionID!!,
fromDevice!!,
keyAgreementProtocols!!,
hashes!!,
messageAuthenticationCodes!!,
shortAuthenticationStrings!!
shortAuthenticationStrings!!,
canonicalJson = toCanonicalJson()
)
}
}
@ -93,6 +96,8 @@ internal interface VerificationInfoStart : VerificationInfo<ValidVerificationInf
null
} else {
ValidVerificationInfoStart.ReciprocateVerificationInfoStart(
transactionID!!,
fromDevice!!,
sharedSecret!!
)
}
@ -102,15 +107,22 @@ internal interface VerificationInfoStart : VerificationInfo<ValidVerificationInf
}
}
sealed class ValidVerificationInfoStart {
sealed class ValidVerificationInfoStart(
open val transactionID: String,
open val fromDevice: String) {
data class SasVerificationInfoStart(
override val transactionID: String,
override val fromDevice: String,
val keyAgreementProtocols: List<String>,
val hashes: List<String>,
val messageAuthenticationCodes: List<String>,
val shortAuthenticationStrings: List<String>
) : ValidVerificationInfoStart()
val shortAuthenticationStrings: List<String>,
val canonicalJson: String
) : ValidVerificationInfoStart(transactionID, fromDevice)
data class ReciprocateVerificationInfoStart(
override val transactionID: String,
override val fromDevice: String,
val sharedSecret: String
) : ValidVerificationInfoStart()
) : ValidVerificationInfoStart(transactionID, fromDevice)
}

View File

@ -27,18 +27,18 @@ internal interface VerificationTransport {
/**
* Sends a message
*/
fun sendToOther(type: String,
verificationInfo: VerificationInfo,
nextState: VerificationTxState,
onErrorReason: CancelCode,
onDone: (() -> Unit)?)
fun <T> sendToOther(type: String,
verificationInfo: VerificationInfo<T>,
nextState: VerificationTxState,
onErrorReason: CancelCode,
onDone: (() -> Unit)?)
fun sendVerificationRequest(supportedMethods: List<String>,
localID: String,
otherUserId: String,
roomId: String?,
toDevices: List<String>?,
callback: (String?, VerificationInfoRequest?) -> Unit)
callback: (String?, ValidVerificationInfoRequest?) -> Unit)
fun cancelTransaction(transactionId: String,
otherUserId: String,

View File

@ -21,7 +21,6 @@ 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.verification.CancelCode
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
@ -65,16 +64,15 @@ internal class VerificationTransportRoomMessage(
private val userId: String,
private val userDeviceId: String?,
private val roomId: String,
private val monarchy: Monarchy,
private val localEchoEventFactory: LocalEchoEventFactory,
private val tx: DefaultVerificationTransaction?
) : VerificationTransport {
override fun sendToOther(type: String,
verificationInfo: VerificationInfo,
nextState: VerificationTxState,
onErrorReason: CancelCode,
onDone: (() -> Unit)?) {
override fun <T> sendToOther(type: String,
verificationInfo: VerificationInfo<T>,
nextState: VerificationTxState,
onErrorReason: CancelCode,
onDone: (() -> Unit)?) {
Timber.d("## SAS sending msg type $type")
Timber.v("## SAS sending msg info $verificationInfo")
val event = createEventAndLocalEcho(
@ -142,17 +140,24 @@ internal class VerificationTransportRoomMessage(
otherUserId: String,
roomId: String?,
toDevices: List<String>?,
callback: (String?, VerificationInfoRequest?) -> Unit) {
callback: (String?, ValidVerificationInfoRequest?) -> Unit) {
Timber.d("## SAS sending verification request with supported methods: $supportedMethods")
// This transport requires a room
requireNotNull(roomId)
val validInfo = ValidVerificationInfoRequest(
transactionID = "",
fromDevice = userDeviceId ?: "",
methods = supportedMethods,
timestamp = System.currentTimeMillis()
)
val info = MessageVerificationRequestContent(
body = stringProvider.getString(R.string.key_verification_request_fallback_message, userId),
fromDevice = userDeviceId ?: "",
fromDevice = validInfo.fromDevice,
toUserId = otherUserId,
timestamp = System.currentTimeMillis(),
methods = supportedMethods
timestamp = validInfo.timestamp,
methods = validInfo.methods
)
val content = info.toContent()
@ -193,7 +198,7 @@ internal class VerificationTransportRoomMessage(
if (wInfo.outputData.getBoolean("failed", false)) {
callback(null, null)
} else if (wInfo.outputData.getString(localID) != null) {
callback(wInfo.outputData.getString(localID), info)
callback(wInfo.outputData.getString(localID), validInfo)
} else {
callback(null, null)
}
@ -347,7 +352,6 @@ internal class VerificationTransportRoomMessage(
internal class VerificationTransportRoomMessageFactory @Inject constructor(
private val workManagerProvider: WorkManagerProvider,
private val stringProvider: StringProvider,
private val monarchy: Monarchy,
@SessionId
private val sessionId: String,
@UserId
@ -357,6 +361,6 @@ internal class VerificationTransportRoomMessageFactory @Inject constructor(
private val localEchoEventFactory: LocalEchoEventFactory) {
fun createTransport(roomId: String, tx: DefaultVerificationTransaction?): VerificationTransportRoomMessage {
return VerificationTransportRoomMessage(workManagerProvider, stringProvider, sessionId, userId, deviceId, roomId, monarchy, localEchoEventFactory, tx)
return VerificationTransportRoomMessage(workManagerProvider, stringProvider, sessionId, userId, deviceId, roomId, localEchoEventFactory, tx)
}
}

View File

@ -50,14 +50,20 @@ internal class VerificationTransportToDevice(
otherUserId: String,
roomId: String?,
toDevices: List<String>?,
callback: (String?, VerificationInfoRequest?) -> Unit) {
callback: (String?, ValidVerificationInfoRequest?) -> Unit) {
Timber.d("## SAS sending verification request with supported methods: $supportedMethods")
val contentMap = MXUsersDevicesMap<Any>()
val keyReq = KeyVerificationRequest(
fromDevice = myDeviceId,
val validKeyReq = ValidVerificationInfoRequest(
transactionID = localID,
fromDevice = myDeviceId ?: "",
methods = supportedMethods,
timestamp = System.currentTimeMillis(),
transactionID = localID
timestamp = System.currentTimeMillis()
)
val keyReq = KeyVerificationRequest(
fromDevice = validKeyReq.fromDevice,
methods = validKeyReq.methods,
timestamp = validKeyReq.timestamp,
transactionID = validKeyReq.transactionID
)
toDevices?.forEach {
contentMap.setObject(otherUserId, it, keyReq)
@ -67,7 +73,7 @@ internal class VerificationTransportToDevice(
this.callback = object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
Timber.v("## verification [$tx.transactionId] send toDevice request success")
callback.invoke(localID, keyReq)
callback.invoke(localID, validKeyReq)
}
override fun onFailure(failure: Throwable) {
@ -103,11 +109,11 @@ internal class VerificationTransportToDevice(
.executeBy(taskExecutor)
}
override fun sendToOther(type: String,
verificationInfo: VerificationInfo,
nextState: VerificationTxState,
onErrorReason: CancelCode,
onDone: (() -> Unit)?) {
override fun <T> sendToOther(type: String,
verificationInfo: VerificationInfo<T>,
nextState: VerificationTxState,
onErrorReason: CancelCode,
onDone: (() -> Unit)?) {
Timber.d("## SAS sending msg type $type")
Timber.v("## SAS sending msg info $verificationInfo")
val tx = tx ?: return

View File

@ -28,8 +28,7 @@ import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64Safe
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.verification.DefaultVerificationTransaction
import im.vector.matrix.android.internal.crypto.verification.VerificationInfo
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoStart
import im.vector.matrix.android.internal.crypto.verification.ValidVerificationInfoStart
import im.vector.matrix.android.internal.util.exhaustive
import timber.log.Timber
@ -179,9 +178,6 @@ internal class DefaultQrCodeVerificationTransaction(
)
}
override fun acceptVerificationEvent(senderId: String, info: VerificationInfo) {
}
override fun cancel() {
cancel(CancelCode.User)
}
@ -194,14 +190,14 @@ internal class DefaultQrCodeVerificationTransaction(
override fun isToDeviceTransport() = false
// Other user has scanned our QR code. check that the secret matched, so we can trust him
fun onStartReceived(startReq: VerificationInfoStart) {
fun onStartReceived(startReq: ValidVerificationInfoStart.ReciprocateVerificationInfoStart) {
if (qrCodeData == null) {
// Should not happen
cancel(CancelCode.UnexpectedMessage)
return
}
if (startReq.sharedSecret?.fromBase64Safe()?.contentEquals(qrCodeData.sharedSecret.fromBase64()) == true) {
if (startReq.sharedSecret.fromBase64Safe()?.contentEquals(qrCodeData.sharedSecret.fromBase64()) == true) {
// Ok, we can trust the other user
// We can only trust the master key in this case
// But first, ask the user for a confirmation