verification state integration

fix rust/kotlin flavor compilation pbs
This commit is contained in:
valere 2023-01-19 23:51:50 +01:00
parent f1d3eeb0a4
commit f9ed8a4dcf
24 changed files with 167 additions and 33 deletions

Binary file not shown.

View file

@ -23,7 +23,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.cancellable
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import org.amshove.kluent.fail
import org.junit.Assert.assertEquals

View file

@ -16,7 +16,6 @@
package org.matrix.android.sdk.internal.crypto
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult
@ -63,7 +62,7 @@ internal class DecryptRoomEventUseCase @Inject constructor(
claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"),
forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain
.orEmpty(),
isSafe = olmDecryptionResult.isSafe.orFalse()
messageVerificationState = olmDecryptionResult.verificationState
)
} else {
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
@ -139,7 +138,7 @@ internal class DecryptRoomEventUseCase @Inject constructor(
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
isSafe = result.isSafe
verificationState = result.messageVerificationState
)
}
}

View file

@ -103,7 +103,7 @@ internal class EventDecryptor @Inject constructor(
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
isSafe = result.isSafe
verificationState = result.messageVerificationState
)
}
}

View file

@ -23,11 +23,15 @@ import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.logger.LoggerTag
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.MessageVerificationState
import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
import org.matrix.android.sdk.api.util.JSON_DICT_PARAMETERIZED_TYPE
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXOutboundSessionInfo
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.SharedWithHelper
import org.matrix.android.sdk.internal.crypto.crosssigning.CrossSigningOlm
import org.matrix.android.sdk.internal.crypto.crosssigning.canonicalSignable
import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
@ -59,6 +63,7 @@ internal class MXOlmDevice @Inject constructor(
private val store: IMXCryptoStore,
private val olmSessionStore: OlmSessionStore,
private val inboundGroupSessionStore: InboundGroupSessionStore,
private val crossSigningOlm: CrossSigningOlm,
private val clock: Clock,
) {
@ -851,15 +856,60 @@ internal class MXOlmDevice @Inject constructor(
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)
}
val verificationState = if (sessionHolder.wrapper.sessionData.trusted.orFalse()) {
// let's get info on the device
val sendingDevice = store.deviceWithIdentityKey(senderKey)
if (sendingDevice == null) {
MessageVerificationState.UNKNOWN_DEVICE
} else {
val isDeviceOwnerOfSession = sessionHolder.wrapper.sessionData.keysClaimed?.get("ed25519") == sendingDevice.fingerprint()
if (!isDeviceOwnerOfSession) {
MessageVerificationState.MISMATCH
} else if (sendingDevice.isVerified) {
MessageVerificationState.VERIFIED
} else {
val isDeviceOwnerVerified = store.getCrossSigningInfo(sendingDevice.userId)?.isTrusted() ?: false
val isDeviceSignedByItsOwner = isDeviceSignByItsOwner(sendingDevice)
if (isDeviceSignedByItsOwner) {
if (isDeviceOwnerVerified) MessageVerificationState.VERIFIED
else MessageVerificationState.SIGNED_DEVICE_OF_UNVERIFIED_USER
} else {
if (isDeviceOwnerVerified) MessageVerificationState.UN_SIGNED_DEVICE_OF_VERIFIED_USER
else MessageVerificationState.UN_SIGNED_DEVICE
}
}
}
} else {
MessageVerificationState.UNSAFE_SOURCE
}
return OlmDecryptionResult(
payload,
wrapper.sessionData.keysClaimed,
senderKey,
wrapper.sessionData.forwardingCurve25519KeyChain,
isSafe = sessionHolder.wrapper.sessionData.trusted.orFalse()
isSafe = sessionHolder.wrapper.sessionData.trusted.orFalse(),
verificationState = verificationState,
)
}
private fun isDeviceSignByItsOwner(device: CryptoDeviceInfo): Boolean {
val otherKeys = store.getCrossSigningInfo(device.userId) ?: return false
val otherSSKSignature = device.signatures?.get(device.userId)?.get("ed25519:${otherKeys.selfSigningKey()?.unpaddedBase64PublicKey}")
?: return false
// Check bob's device is signed by bob's SSK
try {
crossSigningOlm.olmUtility.verifyEd25519Signature(
otherSSKSignature,
otherKeys.selfSigningKey()?.unpaddedBase64PublicKey,
device.canonicalSignable()
)
return true
} catch (e: Throwable) {
return false
}
}
/**
* Reset replay attack data for the given timeline.
*

View file

@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm
import dagger.Lazy
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.logger.LoggerTag
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.crypto.NewSessionListener
@ -100,7 +99,7 @@ internal class MXMegolmDecryption(
claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"),
forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain
.orEmpty(),
isSafe = olmDecryptionResult.isSafe.orFalse()
messageVerificationState = olmDecryptionResult.verificationState,
).also {
liveEventManager.get().dispatchLiveEventDecrypted(event, it)
}

View file

@ -114,7 +114,7 @@ internal class CryptoSyncHandler @Inject constructor(
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
isSafe = result.isSafe
verificationState = result.messageVerificationState,
)
return true
} else {

View file

@ -18,6 +18,16 @@ package org.matrix.android.sdk.api.session.crypto.model
import org.matrix.android.sdk.api.util.JsonDict
enum class MessageVerificationState {
VERIFIED,
SIGNED_DEVICE_OF_UNVERIFIED_USER,
UN_SIGNED_DEVICE_OF_VERIFIED_USER,
UN_SIGNED_DEVICE,
UNKNOWN_DEVICE,
UNSAFE_SOURCE,
MISMATCH,
}
/**
* The result of a (successful) call to decryptEvent.
*/
@ -45,5 +55,5 @@ data class MXEventDecryptionResult(
*/
val forwardingCurve25519KeyChain: List<String> = emptyList(),
val isSafe: Boolean = false
val messageVerificationState: MessageVerificationState? = null,
)

View file

@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.util.JsonDict
/**
* This class represents the decryption result.
* It's serialized in eventEntity to remember the decryption result
*/
@JsonClass(generateAdapter = true)
data class OlmDecryptionResult(
@ -50,4 +51,9 @@ data class OlmDecryptionResult(
* True if the key used to decrypt is considered safe (trusted).
*/
@Json(name = "key_safety") val isSafe: Boolean? = null,
/**
* Authenticity info for that message
*/
@Json(name = "verification_state") val verificationState: MessageVerificationState? = null,
)

View file

@ -1,5 +1,5 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +16,6 @@
package org.matrix.android.sdk.api.session.crypto.verification
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
interface VerificationTransaction {
val method: VerificationMethod
@ -44,7 +42,7 @@ interface VerificationTransaction {
internal fun VerificationTransaction.dbgState(): String? {
return when (this) {
is SasVerificationTransaction -> "${this.state()}"
is QrCodeVerification -> "${this.state()}"
is QrCodeVerificationTransaction -> "${this.state()}"
else -> "??"
}
}

View file

@ -19,6 +19,7 @@ import dagger.Lazy
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult
import org.matrix.android.sdk.api.session.crypto.model.MessageVerificationState
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
@ -78,7 +79,7 @@ internal class DefaultEncryptEventTask @Inject constructor(
forwardingCurve25519KeyChain = emptyList(),
senderCurve25519Key = result.eventContent["sender_key"] as? String,
claimedEd25519Key = cryptoService.get().getMyCryptoDevice().fingerprint(),
isSafe = true
messageVerificationState = MessageVerificationState.VERIFIED
)
} else {
null

View file

@ -210,7 +210,7 @@ private fun decryptIfNeeded(cryptoService: CryptoService?, eventEntity: EventEnt
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
isSafe = result.isSafe
verificationState = result.messageVerificationState
)
// Save decryption result, to not decrypt every time we enter the thread list
eventEntity.setDecryptionResult(result)

View file

@ -88,7 +88,7 @@ internal open class EventEntity(
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
isSafe = result.isSafe
verificationState = result.messageVerificationState
)
val adapter = MoshiProvider.providesMoshi().adapter(OlmDecryptionResult::class.java)
decryptionResultJson = adapter.toJson(decryptionResult)

View file

@ -251,7 +251,7 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
isSafe = result.isSafe
verificationState = result.messageVerificationState
)
} catch (e: MXCryptoError) {
if (e is MXCryptoError.Base) {

View file

@ -139,14 +139,13 @@ internal class SyncResponseHandler @Inject constructor(
try {
val timelineId = generateTimelineId(roomId)
// Event from sync does not have roomId, so add it to the event first
// note: runBlocking should be used here while we are in realm single thread executor, to avoid thread switching
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), timelineId)
event.mxDecryptionResult = OlmDecryptionResult(
payload = result.clearEvent,
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
isSafe = result.isSafe
verificationState = result.messageVerificationState
)
} catch (e: MXCryptoError) {
Timber.v(e, "Failed to decrypt $roomId")

View file

@ -105,7 +105,7 @@ internal class SyncResponsePostTreatmentAggregatorHandler @Inject constructor(
.enqueue()
}
private fun handleUserIdsForCheckingTrustAndAffectedRoomShields(userIdsWithDeviceUpdate: Collection<String>) {
private suspend fun handleUserIdsForCheckingTrustAndAffectedRoomShields(userIdsWithDeviceUpdate: Collection<String>) {
if (userIdsWithDeviceUpdate.isEmpty()) return
crossSigningService.checkTrustAndAffectedRoomShields(userIdsWithDeviceUpdate.toList())
}

View file

@ -38,7 +38,7 @@ internal class DecryptRoomEventUseCase @Inject constructor(private val olmMachin
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
isSafe = result.isSafe
verificationState = result.messageVerificationState
)
}
}

View file

@ -73,7 +73,6 @@ import org.matrix.rustcomponents.sdk.crypto.DecryptionException
import org.matrix.rustcomponents.sdk.crypto.DeviceLists
import org.matrix.rustcomponents.sdk.crypto.EncryptionSettings
import org.matrix.rustcomponents.sdk.crypto.KeyRequestPair
import org.matrix.rustcomponents.sdk.crypto.KeySafety
import org.matrix.rustcomponents.sdk.crypto.KeysImportResult
import org.matrix.rustcomponents.sdk.crypto.Logger
import org.matrix.rustcomponents.sdk.crypto.MegolmV1BackupKey
@ -431,7 +430,7 @@ internal class OlmMachine @Inject constructor(
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
}
val serializedEvent = adapter.toJson(event)
val decrypted = inner.decryptRoomEvent(serializedEvent, event.roomId)
val decrypted = inner.decryptRoomEvent(serializedEvent, event.roomId, false)
val deserializationAdapter =
moshi.adapter<JsonDict>(Map::class.java)
@ -443,7 +442,7 @@ internal class OlmMachine @Inject constructor(
senderCurve25519Key = decrypted.senderCurve25519Key,
claimedEd25519Key = decrypted.claimedEd25519Key,
forwardingCurve25519KeyChain = decrypted.forwardingCurve25519Chain,
isSafe = decrypted.keySafety == KeySafety.SAFE,
messageVerificationState = decrypted.verificationState.fromInner(),
)
} catch (throwable: Throwable) {
val reThrow = when (throwable) {

View file

@ -0,0 +1,32 @@
/*
* Copyright 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto
import org.matrix.android.sdk.api.session.crypto.model.MessageVerificationState
import org.matrix.rustcomponents.sdk.crypto.VerificationState as InnerVerificationState
fun InnerVerificationState.fromInner(): MessageVerificationState {
return when (this) {
InnerVerificationState.VERIFIED -> MessageVerificationState.VERIFIED
InnerVerificationState.SIGNED_DEVICE_OF_UNVERIFIED_USER -> MessageVerificationState.SIGNED_DEVICE_OF_UNVERIFIED_USER
InnerVerificationState.UN_SIGNED_DEVICE_OF_VERIFIED_USER -> MessageVerificationState.UN_SIGNED_DEVICE_OF_VERIFIED_USER
InnerVerificationState.UN_SIGNED_DEVICE -> MessageVerificationState.UN_SIGNED_DEVICE
InnerVerificationState.UNKNOWN_DEVICE -> MessageVerificationState.UNKNOWN_DEVICE
InnerVerificationState.UNSAFE_SOURCE -> MessageVerificationState.UNSAFE_SOURCE
InnerVerificationState.MISMATCH -> MessageVerificationState.MISMATCH
}
}

View file

@ -30,7 +30,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch

View file

@ -84,7 +84,7 @@ class ViewEditHistoryViewModel @AssistedInject constructor(
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
isSafe = result.isSafe
verificationState = result.messageVerificationState
)
} catch (e: MXCryptoError) {
Timber.w("Failed to decrypt event in history")

View file

@ -29,6 +29,7 @@ import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLay
import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.model.MessageVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.VerificationState
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
@ -74,9 +75,10 @@ class MessageInformationDataFactory @Inject constructor(
prevDisplayableEvent?.root?.localDateTime()?.toLocalDate() != date.toLocalDate()
val time = dateFormatter.format(event.root.originServerTs, DateFormatKind.MESSAGE_SIMPLE)
val e2eDecoration = runBlocking {
getE2EDecoration(roomSummary, params.lastEdit ?: event.root)
}
// val e2eDecoration = runBlocking {
// getE2EDecoration(roomSummary, params.lastEdit ?: event.root)
// }
val e2eDecoration = getE2EDecorationV2(roomSummary, params.lastEdit ?: event.root)
val senderId = runBlocking { getSenderId(event) }
// SendState Decoration
val sendStateDecoration = if (isSentByMe) {
@ -147,6 +149,47 @@ class MessageInformationDataFactory @Inject constructor(
}
}
private fun getE2EDecorationV2(roomSummary: RoomSummary?, event: Event): E2EDecoration {
if (roomSummary?.isEncrypted != true) {
// No decoration for clear room
// Questionable? what if the event is E2E?
return E2EDecoration.NONE
}
if (event.sendState != SendState.SYNCED) {
// we don't display e2e decoration if event not synced back
return E2EDecoration.NONE
}
return when (event.mxDecryptionResult?.verificationState) {
MessageVerificationState.VERIFIED -> E2EDecoration.NONE
MessageVerificationState.SIGNED_DEVICE_OF_UNVERIFIED_USER -> E2EDecoration.NONE
MessageVerificationState.UN_SIGNED_DEVICE_OF_VERIFIED_USER -> E2EDecoration.WARN_SENT_BY_UNVERIFIED
// We neither verified this user so not interesting in that warning?
MessageVerificationState.UN_SIGNED_DEVICE -> E2EDecoration.NONE
MessageVerificationState.UNKNOWN_DEVICE -> E2EDecoration.WARN_SENT_BY_DELETED_SESSION
MessageVerificationState.UNSAFE_SOURCE -> E2EDecoration.WARN_UNSAFE_KEY
MessageVerificationState.MISMATCH -> E2EDecoration.WARN_UNSAFE_KEY
null -> {
// No verification state.
// So could be a clear event, or a legacy decryption, or an UTD event
if (!event.isEncrypted()) {
e2EDecorationForClearEventInE2ERoom(event, roomSummary)
} else if (event.mxDecryptionResult != null) {
// No verification state, so could be a migrated old decryption?
if (event.mxDecryptionResult?.isSafe == true) {
// for past legacy decryption let's not decorate
E2EDecoration.NONE
} else {
E2EDecoration.WARN_UNSAFE_KEY
}
} else {
// Undecrypted event
E2EDecoration.NONE
}
}
}
}
private suspend fun getE2EDecoration(roomSummary: RoomSummary?, event: Event): E2EDecoration {
if (roomSummary?.isEncrypted != true) {
// No decoration for clear room

View file

@ -217,7 +217,7 @@ class NotifiableEventResolver @Inject constructor(
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
isSafe = result.isSafe
verificationState = result.messageVerificationState
)
} catch (ignore: MXCryptoError) {
}