diff --git a/changelog.d/6702.bugfix b/changelog.d/6702.bugfix
new file mode 100644
index 0000000000..a1d646cf71
--- /dev/null
+++ b/changelog.d/6702.bugfix
@@ -0,0 +1 @@
+Add Warning shield when a user previously verified rotated their cross signing keys
diff --git a/dependencies.gradle b/dependencies.gradle
index f4165ad692..f5d64a78d1 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -167,7 +167,8 @@ ext.libs = [
         tests       : [
                 'kluent'                 : "org.amshove.kluent:kluent-android:1.68",
                 'timberJunitRule'        : "net.lachlanmckee:timber-junit-rule:1.0.1",
-                'junit'                  : "junit:junit:4.13.2"
+                'junit'                  : "junit:junit:4.13.2",
+                'robolectric'            : "org.robolectric:robolectric:4.8",
         ]
 ]
 
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
index 7b5ee97ae4..cbaa3153df 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
@@ -365,7 +365,7 @@ class CryptoTestHelper(val testHelper: CommonTestHelper) {
         }
 
         testHelper.retryPeriodically {
-            alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
+            bob.cryptoService().crossSigningService().isUserTrusted(alice.myUserId)
         }
     }
 
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt
index 2bb04a1faa..c4fb896934 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt
@@ -25,7 +25,6 @@ import org.junit.Assert.assertNull
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
 import org.junit.FixMethodOrder
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -42,13 +41,13 @@ import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
 import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
 import org.matrix.android.sdk.common.SessionTestParams
 import org.matrix.android.sdk.common.TestConstants
+import timber.log.Timber
 import kotlin.coroutines.Continuation
 import kotlin.coroutines.resume
 
 @RunWith(AndroidJUnit4::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @LargeTest
-@Ignore
 class XSigningTest : InstrumentedTest {
 
     @Test
@@ -214,4 +213,104 @@ class XSigningTest : InstrumentedTest {
         val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
         assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
     }
+
+    @Test
+    fun testWarnOnCrossSigningReset() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
+        val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+
+        val aliceSession = cryptoTestData.firstSession
+        val bobSession = cryptoTestData.secondSession
+
+        val aliceAuthParams = UserPasswordAuth(
+                user = aliceSession.myUserId,
+                password = TestConstants.PASSWORD
+        )
+        val bobAuthParams = UserPasswordAuth(
+                user = bobSession!!.myUserId,
+                password = TestConstants.PASSWORD
+        )
+
+        testHelper.waitForCallback<Unit> {
+            aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+                override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
+                    promise.resume(aliceAuthParams)
+                }
+            }, it)
+        }
+        testHelper.waitForCallback<Unit> {
+            bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+                override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
+                    promise.resume(bobAuthParams)
+                }
+            }, it)
+        }
+
+        cryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, cryptoTestData.roomId)
+
+        testHelper.retryPeriodically {
+            aliceSession.cryptoService().crossSigningService().isUserTrusted(bobSession.myUserId)
+        }
+
+        testHelper.retryPeriodically {
+            aliceSession.cryptoService().crossSigningService().checkUserTrust(bobSession.myUserId).isVerified()
+        }
+
+        aliceSession.cryptoService()
+        // Ensure also that bob device is trusted
+        testHelper.retryPeriodically {
+            val deviceInfo = aliceSession.cryptoService().getUserDevices(bobSession.myUserId).firstOrNull()
+            Timber.v("#TEST device:${deviceInfo?.shortDebugString()} trust ${deviceInfo?.trustLevel}")
+            deviceInfo?.trustLevel?.crossSigningVerified == true
+        }
+
+        val currentBobMSK = aliceSession.cryptoService().crossSigningService()
+                .getUserCrossSigningKeys(bobSession.myUserId)!!
+                .masterKey()!!.unpaddedBase64PublicKey!!
+
+        testHelper.waitForCallback<Unit> {
+            bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+                override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
+                    promise.resume(bobAuthParams)
+                }
+            }, it)
+        }
+
+        testHelper.retryPeriodically {
+            val newBobMsk = aliceSession.cryptoService().crossSigningService()
+                    .getUserCrossSigningKeys(bobSession.myUserId)
+                    ?.masterKey()?.unpaddedBase64PublicKey
+            newBobMsk != null && newBobMsk != currentBobMSK
+        }
+
+        // trick to force event to sync
+        bobSession.roomService().getRoom(cryptoTestData.roomId)!!.typingService().userIsTyping()
+
+        // assert that bob is not trusted anymore from alice s
+        testHelper.retryPeriodically {
+            val trust = aliceSession.cryptoService().crossSigningService().checkUserTrust(bobSession.myUserId)
+            !trust.isVerified()
+        }
+
+        // trick to force event to sync
+        bobSession.roomService().getRoom(cryptoTestData.roomId)!!.typingService().userStopsTyping()
+        bobSession.roomService().getRoom(cryptoTestData.roomId)!!.typingService().userIsTyping()
+
+        testHelper.retryPeriodically {
+            val info = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
+            info?.wasTrustedOnce == true
+        }
+
+        // trick to force event to sync
+        bobSession.roomService().getRoom(cryptoTestData.roomId)!!.typingService().userStopsTyping()
+        bobSession.roomService().getRoom(cryptoTestData.roomId)!!.typingService().userIsTyping()
+
+        testHelper.retryPeriodically {
+            !aliceSession.cryptoService().crossSigningService().isUserTrusted(bobSession.myUserId)
+        }
+
+        // Ensure also that bob device are not trusted
+        testHelper.retryPeriodically {
+            aliceSession.cryptoService().getUserDevices(bobSession.myUserId).first().trustLevel?.crossSigningVerified != true
+        }
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/MXCrossSigningInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/MXCrossSigningInfo.kt
index 9604decd62..30a2cfd719 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/MXCrossSigningInfo.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/MXCrossSigningInfo.kt
@@ -18,7 +18,8 @@ package org.matrix.android.sdk.api.session.crypto.crosssigning
 
 data class MXCrossSigningInfo(
         val userId: String,
-        val crossSigningKeys: List<CryptoCrossSigningKey>
+        val crossSigningKeys: List<CryptoCrossSigningKey>,
+        val wasTrustedOnce: Boolean
 ) {
 
     fun isTrusted(): Boolean = masterKey()?.trustLevel?.isVerified() == true &&
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/UserVerificationLevel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/UserVerificationLevel.kt
new file mode 100644
index 0000000000..e3c7057b6b
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/UserVerificationLevel.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022 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.api.session.crypto.model
+
+enum class UserVerificationLevel {
+
+    VERIFIED_ALL_DEVICES_TRUSTED,
+
+    VERIFIED_WITH_DEVICES_UNTRUSTED,
+
+    UNVERIFIED_BUT_WAS_PREVIOUSLY,
+
+    WAS_NEVER_VERIFIED,
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt
index d405bdce27..f4796155c6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt
@@ -60,7 +60,7 @@ import javax.inject.Inject
 
 @SessionScope
 internal class DefaultCrossSigningService @Inject constructor(
-        @UserId private val userId: String,
+        @UserId private val myUserId: String,
         @SessionId private val sessionId: String,
         private val cryptoStore: IMXCryptoStore,
         private val deviceListManager: DeviceListManager,
@@ -127,7 +127,7 @@ internal class DefaultCrossSigningService @Inject constructor(
                 }
 
                 // Recover local trust in case private key are there?
-                setUserKeysAsTrusted(userId, checkUserTrust(userId).isVerified())
+                setUserKeysAsTrusted(myUserId, checkUserTrust(myUserId).isVerified())
             }
         } catch (e: Throwable) {
             // Mmm this kind of a big issue
@@ -167,9 +167,13 @@ internal class DefaultCrossSigningService @Inject constructor(
                 }
 
                 override fun onSuccess(data: InitializeCrossSigningTask.Result) {
-                    val crossSigningInfo = MXCrossSigningInfo(userId, listOf(data.masterKeyInfo, data.userKeyInfo, data.selfSignedKeyInfo))
+                    val crossSigningInfo = MXCrossSigningInfo(
+                            myUserId,
+                            listOf(data.masterKeyInfo, data.userKeyInfo, data.selfSignedKeyInfo),
+                            true
+                    )
                     cryptoStore.setMyCrossSigningInfo(crossSigningInfo)
-                    setUserKeysAsTrusted(userId, true)
+                    setUserKeysAsTrusted(myUserId, true)
                     cryptoStore.storePrivateKeysInfo(data.masterKeyPK, data.userKeyPK, data.selfSigningKeyPK)
                     crossSigningOlm.masterPkSigning = OlmPkSigning().apply { initWithSeed(data.masterKeyPK.fromBase64()) }
                     crossSigningOlm.userPkSigning = OlmPkSigning().apply { initWithSeed(data.userKeyPK.fromBase64()) }
@@ -266,7 +270,7 @@ internal class DefaultCrossSigningService @Inject constructor(
             uskKeyPrivateKey: String?,
             sskPrivateKey: String?
     ): UserTrustResult {
-        val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return UserTrustResult.CrossSigningNotConfigured(userId)
+        val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
 
         var masterKeyIsTrusted = false
         var userKeyIsTrusted = false
@@ -330,7 +334,7 @@ internal class DefaultCrossSigningService @Inject constructor(
             val checkSelfTrust = checkSelfTrust()
             if (checkSelfTrust.isVerified()) {
                 cryptoStore.storePrivateKeysInfo(masterKeyPrivateKey, uskKeyPrivateKey, sskPrivateKey)
-                setUserKeysAsTrusted(userId, true)
+                setUserKeysAsTrusted(myUserId, true)
             }
             return checkSelfTrust
         }
@@ -351,7 +355,7 @@ internal class DefaultCrossSigningService @Inject constructor(
      * .
      */
     override fun isUserTrusted(otherUserId: String): Boolean {
-        return cryptoStore.getCrossSigningInfo(userId)?.isTrusted() == true
+        return cryptoStore.getCrossSigningInfo(otherUserId)?.isTrusted() == true
     }
 
     override fun isCrossSigningVerified(): Boolean {
@@ -363,7 +367,7 @@ internal class DefaultCrossSigningService @Inject constructor(
      */
     override fun checkUserTrust(otherUserId: String): UserTrustResult {
         Timber.v("## CrossSigning  checkUserTrust for $otherUserId")
-        if (otherUserId == userId) {
+        if (otherUserId == myUserId) {
             return checkSelfTrust()
         }
         // I trust a user if I trust his master key
@@ -371,16 +375,14 @@ internal class DefaultCrossSigningService @Inject constructor(
         // TODO what if the master key is signed by a device key that i have verified
 
         // First let's get my user key
-        val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
+        val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(myUserId)
 
-        checkOtherMSKTrusted(myCrossSigningInfo, cryptoStore.getCrossSigningInfo(otherUserId))
-
-        return UserTrustResult.Success
+        return checkOtherMSKTrusted(myCrossSigningInfo, cryptoStore.getCrossSigningInfo(otherUserId))
     }
 
     fun checkOtherMSKTrusted(myCrossSigningInfo: MXCrossSigningInfo?, otherInfo: MXCrossSigningInfo?): UserTrustResult {
         val myUserKey = myCrossSigningInfo?.userKey()
-                ?: return UserTrustResult.CrossSigningNotConfigured(userId)
+                ?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
 
         if (!myCrossSigningInfo.isTrusted()) {
             return UserTrustResult.KeysNotTrusted(myCrossSigningInfo)
@@ -391,7 +393,7 @@ internal class DefaultCrossSigningService @Inject constructor(
                 ?: return UserTrustResult.UnknownCrossSignatureInfo(otherInfo?.userId ?: "")
 
         val masterKeySignaturesMadeByMyUserKey = otherMasterKey.signatures
-                ?.get(userId) // Signatures made by me
+                ?.get(myUserId) // Signatures made by me
                 ?.get("ed25519:${myUserKey.unpaddedBase64PublicKey}")
 
         if (masterKeySignaturesMadeByMyUserKey.isNullOrBlank()) {
@@ -417,9 +419,9 @@ internal class DefaultCrossSigningService @Inject constructor(
         // Special case when it's me,
         // I have to check that MSK -> USK -> SSK
         // and that MSK is trusted (i know the private key, or is signed by a trusted device)
-        val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
+        val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(myUserId)
 
-        return checkSelfTrust(myCrossSigningInfo, cryptoStore.getUserDeviceList(userId))
+        return checkSelfTrust(myCrossSigningInfo, cryptoStore.getUserDeviceList(myUserId))
     }
 
     fun checkSelfTrust(myCrossSigningInfo: MXCrossSigningInfo?, myDevices: List<CryptoDeviceInfo>?): UserTrustResult {
@@ -429,7 +431,7 @@ internal class DefaultCrossSigningService @Inject constructor(
 //        val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
 
         val myMasterKey = myCrossSigningInfo?.masterKey()
-                ?: return UserTrustResult.CrossSigningNotConfigured(userId)
+                ?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
 
         // Is the master key trusted
         // 1) check if I know the private key
@@ -453,7 +455,7 @@ internal class DefaultCrossSigningService @Inject constructor(
             olmPkSigning?.releaseSigning()
         } else {
             // Maybe it's signed by a locally trusted device?
-            myMasterKey.signatures?.get(userId)?.forEach { (key, value) ->
+            myMasterKey.signatures?.get(myUserId)?.forEach { (key, value) ->
                 val potentialDeviceId = key.removePrefix("ed25519:")
                 val potentialDevice = myDevices?.firstOrNull { it.deviceId == potentialDeviceId } // cryptoStore.getUserDevice(userId, potentialDeviceId)
                 if (potentialDevice != null && potentialDevice.isVerified) {
@@ -475,14 +477,14 @@ internal class DefaultCrossSigningService @Inject constructor(
         }
 
         val myUserKey = myCrossSigningInfo.userKey()
-                ?: return UserTrustResult.CrossSigningNotConfigured(userId)
+                ?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
 
         val userKeySignaturesMadeByMyMasterKey = myUserKey.signatures
-                ?.get(userId) // Signatures made by me
+                ?.get(myUserId) // Signatures made by me
                 ?.get("ed25519:${myMasterKey.unpaddedBase64PublicKey}")
 
         if (userKeySignaturesMadeByMyMasterKey.isNullOrBlank()) {
-            Timber.d("## CrossSigning  checkUserTrust false for $userId, USK not signed by MSK")
+            Timber.d("## CrossSigning  checkUserTrust false for $myUserId, USK not signed by MSK")
             return UserTrustResult.KeyNotSigned(myUserKey)
         }
 
@@ -498,14 +500,14 @@ internal class DefaultCrossSigningService @Inject constructor(
         }
 
         val mySSKey = myCrossSigningInfo.selfSigningKey()
-                ?: return UserTrustResult.CrossSigningNotConfigured(userId)
+                ?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
 
         val ssKeySignaturesMadeByMyMasterKey = mySSKey.signatures
-                ?.get(userId) // Signatures made by me
+                ?.get(myUserId) // Signatures made by me
                 ?.get("ed25519:${myMasterKey.unpaddedBase64PublicKey}")
 
         if (ssKeySignaturesMadeByMyMasterKey.isNullOrBlank()) {
-            Timber.d("## CrossSigning  checkUserTrust false for $userId, SSK not signed by MSK")
+            Timber.d("## CrossSigning  checkUserTrust false for $myUserId, SSK not signed by MSK")
             return UserTrustResult.KeyNotSigned(mySSKey)
         }
 
@@ -555,14 +557,14 @@ internal class DefaultCrossSigningService @Inject constructor(
 
     override fun trustUser(otherUserId: String, callback: MatrixCallback<Unit>) {
         cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
-            Timber.d("## CrossSigning - Mark user $userId as trusted ")
+            Timber.d("## CrossSigning - Mark user $otherUserId as trusted ")
             // We should have this user keys
             val otherMasterKeys = getUserCrossSigningKeys(otherUserId)?.masterKey()
             if (otherMasterKeys == null) {
                 callback.onFailure(Throwable("## CrossSigning - Other master signing key is not known"))
                 return@launch
             }
-            val myKeys = getUserCrossSigningKeys(userId)
+            val myKeys = getUserCrossSigningKeys(myUserId)
             if (myKeys == null) {
                 callback.onFailure(Throwable("## CrossSigning - CrossSigning is not setup for this account"))
                 return@launch
@@ -586,16 +588,22 @@ internal class DefaultCrossSigningService @Inject constructor(
             }
 
             cryptoStore.setUserKeysAsTrusted(otherUserId, true)
-            // TODO update local copy with new signature directly here? kind of local echo of trust?
 
-            Timber.d("## CrossSigning - Upload signature of $userId MSK signed by USK")
+            Timber.d("## CrossSigning - Upload signature of $otherUserId MSK signed by USK")
             val uploadQuery = UploadSignatureQueryBuilder()
-                    .withSigningKeyInfo(otherMasterKeys.copyForSignature(userId, userPubKey, newSignature))
+                    .withSigningKeyInfo(otherMasterKeys.copyForSignature(myUserId, userPubKey, newSignature))
                     .build()
             uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) {
                 this.executionThread = TaskThread.CRYPTO
                 this.callback = callback
             }.executeBy(taskExecutor)
+
+            // Local echo for device cross trust, to avoid having to wait for a notification of key change
+            cryptoStore.getUserDeviceList(otherUserId)?.forEach { device ->
+                val updatedTrust = checkDeviceTrust(device.userId, device.deviceId, device.trustLevel?.isLocallyVerified() ?: false)
+                Timber.v("## CrossSigning - update trust for device ${device.deviceId} of user $otherUserId , verified=$updatedTrust")
+                cryptoStore.setDeviceTrust(device.userId, device.deviceId, updatedTrust.isCrossSignedVerified(), updatedTrust.isLocallyVerified())
+            }
         }
     }
 
@@ -604,20 +612,20 @@ internal class DefaultCrossSigningService @Inject constructor(
             cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
             checkSelfTrust()
             // re-verify all trusts
-            onUsersDeviceUpdate(listOf(userId))
+            onUsersDeviceUpdate(listOf(myUserId))
         }
     }
 
     override fun trustDevice(deviceId: String, callback: MatrixCallback<Unit>) {
         cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
             // This device should be yours
-            val device = cryptoStore.getUserDevice(userId, deviceId)
+            val device = cryptoStore.getUserDevice(myUserId, deviceId)
             if (device == null) {
                 callback.onFailure(IllegalArgumentException("This device [$deviceId] is not known, or not yours"))
                 return@launch
             }
 
-            val myKeys = getUserCrossSigningKeys(userId)
+            val myKeys = getUserCrossSigningKeys(myUserId)
             if (myKeys == null) {
                 callback.onFailure(Throwable("CrossSigning is not setup for this account"))
                 return@launch
@@ -639,7 +647,7 @@ internal class DefaultCrossSigningService @Inject constructor(
             }
             val toUpload = device.copy(
                     signatures = mapOf(
-                            userId
+                            myUserId
                                     to
                                     mapOf(
                                             "ed25519:$ssPubKey" to newSignature
@@ -661,8 +669,8 @@ internal class DefaultCrossSigningService @Inject constructor(
         val otherDevice = cryptoStore.getUserDevice(otherUserId, otherDeviceId)
                 ?: return DeviceTrustResult.UnknownDevice(otherDeviceId)
 
-        val myKeys = getUserCrossSigningKeys(userId)
-                ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(userId))
+        val myKeys = getUserCrossSigningKeys(myUserId)
+                ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(myUserId))
 
         if (!myKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(myKeys))
 
@@ -717,7 +725,7 @@ internal class DefaultCrossSigningService @Inject constructor(
 
     fun checkDeviceTrust(myKeys: MXCrossSigningInfo?, otherKeys: MXCrossSigningInfo?, otherDevice: CryptoDeviceInfo): DeviceTrustResult {
         val locallyTrusted = otherDevice.trustLevel?.isLocallyVerified()
-        myKeys ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(userId))
+        myKeys ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(myUserId))
 
         if (!myKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(myKeys))
 
@@ -805,7 +813,7 @@ internal class DefaultCrossSigningService @Inject constructor(
         cryptoStore.setUserKeysAsTrusted(otherUserId, trusted)
         // If it's me, recheck trust of all users and devices?
         val users = ArrayList<String>()
-        if (otherUserId == userId && currentTrust != trusted) {
+        if (otherUserId == myUserId && currentTrust != trusted) {
             // notify key requester
             outgoingKeyRequestManager.onSelfCrossSigningTrustChanged(trusted)
             cryptoStore.updateUsersTrust {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
index 6d845ec59e..fffc6707d7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
@@ -161,6 +161,7 @@ internal class UpdateTrustWorker(context: Context, params: WorkerParameters, ses
             // i have all the new trusts, update DB
             trusts.forEach {
                 val verified = it.value?.isVerified() == true
+                Timber.v("[$myUserId] ## CrossSigning - Updating user trust: ${it.key} to $verified")
                 updateCrossSigningKeysTrust(cryptoRealm, it.key, verified)
             }
 
@@ -259,21 +260,27 @@ internal class UpdateTrustWorker(context: Context, params: WorkerParameters, ses
         cryptoRealm.where(CrossSigningInfoEntity::class.java)
                 .equalTo(CrossSigningInfoEntityFields.USER_ID, userId)
                 .findFirst()
-                ?.crossSigningKeys
-                ?.forEach { info ->
-                    // optimization to avoid trigger updates when there is no change..
-                    if (info.trustLevelEntity?.isVerified() != verified) {
-                        Timber.d("## CrossSigning - Trust change for $userId : $verified")
-                        val level = info.trustLevelEntity
-                        if (level == null) {
-                            info.trustLevelEntity = cryptoRealm.createObject(TrustLevelEntity::class.java).also {
-                                it.locallyVerified = verified
-                                it.crossSignedVerified = verified
+                ?.let { userKeyInfo ->
+                    userKeyInfo
+                            .crossSigningKeys
+                            .forEach { key ->
+                                // optimization to avoid trigger updates when there is no change..
+                                if (key.trustLevelEntity?.isVerified() != verified) {
+                                    Timber.d("## CrossSigning - Trust change for $userId : $verified")
+                                    val level = key.trustLevelEntity
+                                    if (level == null) {
+                                        key.trustLevelEntity = cryptoRealm.createObject(TrustLevelEntity::class.java).also {
+                                            it.locallyVerified = verified
+                                            it.crossSignedVerified = verified
+                                        }
+                                    } else {
+                                        level.locallyVerified = verified
+                                        level.crossSignedVerified = verified
+                                    }
+                                }
                             }
-                        } else {
-                            level.locallyVerified = verified
-                            level.crossSignedVerified = verified
-                        }
+                    if (verified) {
+                        userKeyInfo.wasUserVerifiedOnce = true
                     }
                 }
     }
@@ -299,8 +306,18 @@ internal class UpdateTrustWorker(context: Context, params: WorkerParameters, ses
                     getCrossSigningInfo(cryptoRealm, userId)?.isTrusted() == true
                 }
 
+        val resetTrust = listToCheck
+                .filter { userId ->
+                    val crossSigningInfo = getCrossSigningInfo(cryptoRealm, userId)
+                    crossSigningInfo?.isTrusted() != true && crossSigningInfo?.wasTrustedOnce == true
+                }
+
         return if (allTrustedUserIds.isEmpty()) {
-            RoomEncryptionTrustLevel.Default
+            if (resetTrust.isEmpty()) {
+                RoomEncryptionTrustLevel.Default
+            } else {
+                RoomEncryptionTrustLevel.Warning
+            }
         } else {
             // If one of the verified user as an untrusted device -> warning
             // If all devices of all verified users are trusted -> green
@@ -327,11 +344,15 @@ internal class UpdateTrustWorker(context: Context, params: WorkerParameters, ses
                         if (hasWarning) {
                             RoomEncryptionTrustLevel.Warning
                         } else {
-                            if (listToCheck.size == allTrustedUserIds.size) {
-                                // all users are trusted and all devices are verified
-                                RoomEncryptionTrustLevel.Trusted
+                            if (resetTrust.isEmpty()) {
+                                if (listToCheck.size == allTrustedUserIds.size) {
+                                    // all users are trusted and all devices are verified
+                                    RoomEncryptionTrustLevel.Trusted
+                                } else {
+                                    RoomEncryptionTrustLevel.Default
+                                }
                             } else {
-                                RoomEncryptionTrustLevel.Default
+                                RoomEncryptionTrustLevel.Warning
                             }
                         }
                     }
@@ -344,7 +365,8 @@ internal class UpdateTrustWorker(context: Context, params: WorkerParameters, ses
                 userId = userId,
                 crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull {
                     crossSigningKeysMapper.map(userId, it)
-                }
+                },
+                wasTrustedOnce = xsignInfo.wasUserVerifiedOnce
         )
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt
index 3b8fa4cacd..6a2ef3bde1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt
@@ -1611,7 +1611,8 @@ internal class RealmCryptoStore @Inject constructor(
                 userId = userId,
                 crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull {
                     crossSigningKeysMapper.map(userId, it)
-                }
+                },
+                wasTrustedOnce = xsignInfo.wasUserVerifiedOnce
         )
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt
index 426d50a54f..de2b74308d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt
@@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo
 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo016
 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo017
 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo018
+import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo019
 import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
 import org.matrix.android.sdk.internal.util.time.Clock
 import javax.inject.Inject
@@ -49,7 +50,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(
         private val clock: Clock,
 ) : MatrixRealmMigration(
         dbName = "Crypto",
-        schemaVersion = 18L,
+        schemaVersion = 19L,
 ) {
     /**
      * Forces all RealmCryptoStoreMigration instances to be equal.
@@ -77,5 +78,6 @@ internal class RealmCryptoStoreMigration @Inject constructor(
         if (oldVersion < 16) MigrateCryptoTo016(realm).perform()
         if (oldVersion < 17) MigrateCryptoTo017(realm).perform()
         if (oldVersion < 18) MigrateCryptoTo018(realm).perform()
+        if (oldVersion < 19) MigrateCryptoTo019(realm).perform()
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo019.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo019.kt
new file mode 100644
index 0000000000..9d2eb60a60
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo019.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022 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.store.db.migration
+
+import io.realm.DynamicRealm
+import io.realm.DynamicRealmObject
+import org.matrix.android.sdk.api.session.crypto.crosssigning.KeyUsage
+import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntityFields
+import org.matrix.android.sdk.internal.crypto.store.db.model.KeyInfoEntityFields
+import org.matrix.android.sdk.internal.crypto.store.db.model.TrustLevelEntityFields
+import org.matrix.android.sdk.internal.util.database.RealmMigrator
+
+/**
+ * This migration is adding support for trusted flags on megolm sessions.
+ * We can't really assert the trust of existing keys, so for the sake of simplicity we are going to
+ * mark existing keys as safe.
+ * This migration can take long depending on the account
+ */
+internal class MigrateCryptoTo019(realm: DynamicRealm) : RealmMigrator(realm, 18) {
+
+    override fun doMigrate(realm: DynamicRealm) {
+        realm.schema.get("CrossSigningInfoEntity")
+                ?.addField(CrossSigningInfoEntityFields.WAS_USER_VERIFIED_ONCE, Boolean::class.java)
+                ?.transform { dynamicObject ->
+
+                    val knowKeys = dynamicObject.getList(CrossSigningInfoEntityFields.CROSS_SIGNING_KEYS.`$`)
+                    val msk = knowKeys.firstOrNull {
+                        it.getList(KeyInfoEntityFields.USAGES.`$`, String::class.java).orEmpty().contains(KeyUsage.MASTER.value)
+                    }
+                    val ssk = knowKeys.firstOrNull {
+                        it.getList(KeyInfoEntityFields.USAGES.`$`, String::class.java).orEmpty().contains(KeyUsage.SELF_SIGNING.value)
+                    }
+                    val isTrusted = isDynamicKeyInfoTrusted(msk?.get<DynamicRealmObject>(KeyInfoEntityFields.TRUST_LEVEL_ENTITY.`$`)) &&
+                            isDynamicKeyInfoTrusted(ssk?.get<DynamicRealmObject>(KeyInfoEntityFields.TRUST_LEVEL_ENTITY.`$`))
+
+                    dynamicObject.setBoolean(CrossSigningInfoEntityFields.WAS_USER_VERIFIED_ONCE, isTrusted)
+                }
+    }
+
+    private fun isDynamicKeyInfoTrusted(keyInfo: DynamicRealmObject?): Boolean {
+        if (keyInfo == null) return false
+        return !keyInfo.isNull(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED) && keyInfo.getBoolean(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED) &&
+                !keyInfo.isNull(TrustLevelEntityFields.LOCALLY_VERIFIED) && keyInfo.getBoolean(TrustLevelEntityFields.LOCALLY_VERIFIED)
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt
index 5aba9bb9ba..033b7662c5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt
@@ -25,6 +25,7 @@ import org.matrix.android.sdk.internal.extensions.clearWith
 internal open class CrossSigningInfoEntity(
         @PrimaryKey
         var userId: String? = null,
+        var wasUserVerifiedOnce: Boolean = false,
         var crossSigningKeys: RealmList<KeyInfoEntity> = RealmList()
 ) : RealmObject() {
 
diff --git a/vector/build.gradle b/vector/build.gradle
index ff0d907212..7bc97b1a57 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -289,6 +289,8 @@ dependencies {
     testImplementation libs.tests.junit
     testImplementation libs.tests.kluent
     testImplementation libs.mockk.mockk
+    testImplementation libs.androidx.coreTesting
+    testImplementation libs.tests.robolectric
     // Plant Timber tree for test
     testImplementation libs.tests.timberJunitRule
     testImplementation libs.airbnb.mavericksTesting
diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt
index 9c5ad49339..ef22aba624 100644
--- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt
+++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt
@@ -26,7 +26,7 @@ import im.vector.app.core.epoxy.onClick
 import im.vector.app.core.extensions.setTextOrHide
 import im.vector.app.features.displayname.getBestName
 import im.vector.app.features.home.AvatarRenderer
-import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
+import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
 import org.matrix.android.sdk.api.util.MatrixItem
 
 abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder>(@LayoutRes layoutId: Int) : VectorEpoxyModel<T>(layoutId) {
@@ -35,7 +35,7 @@ abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder>(@LayoutRes la
     @EpoxyAttribute var editable: Boolean = true
 
     @EpoxyAttribute
-    var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
+    var userVerificationLevel: UserVerificationLevel? = null
 
     @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
     var clickListener: ClickListener? = null
@@ -53,6 +53,6 @@ abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder>(@LayoutRes la
         holder.subtitleView.setTextOrHide(matrixId)
         holder.editableView.isVisible = editable
         avatarRenderer.render(matrixItem, holder.avatarImageView)
-        holder.avatarDecorationImageView.render(userEncryptionTrustLevel)
+        holder.avatarDecorationImageView.renderUser(userVerificationLevel)
     }
 }
diff --git a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt
index 4642fb8525..1990859668 100644
--- a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt
+++ b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt
@@ -24,6 +24,7 @@ import androidx.core.view.isVisible
 import im.vector.app.R
 import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
 import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
+import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
 
 class ShieldImageView @JvmOverloads constructor(
         context: Context,
@@ -102,6 +103,35 @@ class ShieldImageView @JvmOverloads constructor(
             }
         }
     }
+
+    fun renderUser(userVerificationLevel: UserVerificationLevel?, borderLess: Boolean = false) {
+        isVisible = userVerificationLevel != null
+        when (userVerificationLevel) {
+            UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED -> {
+                contentDescription = context.getString(R.string.a11y_trust_level_trusted)
+                setImageResource(
+                        if (borderLess) R.drawable.ic_shield_trusted_no_border
+                        else R.drawable.ic_shield_trusted
+                )
+            }
+            UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY,
+            UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED -> {
+                contentDescription = context.getString(R.string.a11y_trust_level_warning)
+                setImageResource(
+                        if (borderLess) R.drawable.ic_shield_warning_no_border
+                        else R.drawable.ic_shield_warning
+                )
+            }
+            UserVerificationLevel.WAS_NEVER_VERIFIED -> {
+                contentDescription = context.getString(R.string.a11y_trust_level_default)
+                setImageResource(
+                        if (borderLess) R.drawable.ic_shield_black_no_border
+                        else R.drawable.ic_shield_black
+                )
+            }
+            null -> Unit
+        }
+    }
 }
 
 @DrawableRes
diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt
index 2894cd4621..65d28a5ceb 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt
@@ -59,7 +59,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorPr
 import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet
 import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs
 import kotlinx.parcelize.Parcelize
-import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
+import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
 import org.matrix.android.sdk.api.session.room.powerlevels.Role
 import org.matrix.android.sdk.api.util.MatrixItem
 import javax.inject.Inject
@@ -235,23 +235,27 @@ class RoomMemberProfileFragment :
                         if (state.userMXCrossSigningInfo.isTrusted()) {
                             // User is trusted
                             if (state.allDevicesAreCrossSignedTrusted) {
-                                RoomEncryptionTrustLevel.Trusted
+                                UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED
                             } else {
-                                RoomEncryptionTrustLevel.Warning
+                                UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED
                             }
                         } else {
-                            RoomEncryptionTrustLevel.Default
+                            if (state.userMXCrossSigningInfo.wasTrustedOnce) {
+                                UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY
+                            } else {
+                                UserVerificationLevel.WAS_NEVER_VERIFIED
+                            }
                         }
                     } else {
                         // Legacy
                         if (state.allDevicesAreTrusted) {
-                            RoomEncryptionTrustLevel.Trusted
+                            UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED
                         } else {
-                            RoomEncryptionTrustLevel.Warning
+                            UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED
                         }
                     }
-                    headerViews.memberProfileDecorationImageView.render(trustLevel)
-                    views.matrixProfileDecorationToolbarAvatarImageView.render(trustLevel)
+                    headerViews.memberProfileDecorationImageView.renderUser(trustLevel)
+                    views.matrixProfileDecorationToolbarAvatarImageView.renderUser(trustLevel)
                 } else {
                     headerViews.memberProfileDecorationImageView.isVisible = false
                 }
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt
index 8f310a6a89..9adfeb2a0e 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt
@@ -129,7 +129,7 @@ class RoomMemberListController @Inject constructor(
             id(roomMember.userId)
             matrixItem(roomMember.toMatrixItem())
             avatarRenderer(host.avatarRenderer)
-            userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
+            userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
             clickListener {
                 host.callback?.onRoomMemberClicked(roomMember)
             }
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt
index 915ce51d91..9ddcde7e4a 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt
@@ -37,7 +37,8 @@ import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.query.QueryStringValue
 import org.matrix.android.sdk.api.session.Session
-import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
+import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
+import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.getRoom
@@ -116,14 +117,7 @@ class RoomMemberListViewModel @AssistedInject constructor(
                                 .map { deviceList ->
                                     // If any key change, emit the userIds list
                                     deviceList.groupBy { it.userId }.mapValues {
-                                        val allDeviceTrusted = it.value.fold(it.value.isNotEmpty()) { prev, next ->
-                                            prev && next.trustLevel?.isCrossSigningVerified().orFalse()
-                                        }
-                                        if (session.cryptoService().crossSigningService().getUserCrossSigningKeys(it.key)?.isTrusted().orFalse()) {
-                                            if (allDeviceTrusted) RoomEncryptionTrustLevel.Trusted else RoomEncryptionTrustLevel.Warning
-                                        } else {
-                                            RoomEncryptionTrustLevel.Default
-                                        }
+                                        getUserTrustLevel(it.key, it.value)
                                     }
                                 }
                     }
@@ -133,6 +127,29 @@ class RoomMemberListViewModel @AssistedInject constructor(
         }
     }
 
+    private fun getUserTrustLevel(userId: String, devices: List<CryptoDeviceInfo>): UserVerificationLevel {
+        val allDeviceTrusted = devices.fold(devices.isNotEmpty()) { prev, next ->
+            prev && next.trustLevel?.isCrossSigningVerified().orFalse()
+        }
+        val mxCrossSigningInfo = session.cryptoService().crossSigningService().getUserCrossSigningKeys(userId)
+        return when {
+            mxCrossSigningInfo == null -> {
+                UserVerificationLevel.WAS_NEVER_VERIFIED
+            }
+            mxCrossSigningInfo.isTrusted() -> {
+                if (allDeviceTrusted) UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED
+                else UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED
+            }
+            else -> {
+                if (mxCrossSigningInfo.wasTrustedOnce) {
+                    UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY
+                } else {
+                    UserVerificationLevel.WAS_NEVER_VERIFIED
+                }
+            }
+        }
+    }
+
     private fun observePowerLevel() {
         PowerLevelsFlowFactory(room).createFlow()
                 .onEach {
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt
index 3cea47e60d..7861970c28 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt
@@ -23,7 +23,7 @@ import com.airbnb.mvrx.Uninitialized
 import im.vector.app.R
 import im.vector.app.core.platform.GenericIdArgs
 import im.vector.app.features.roomprofile.RoomProfileArgs
-import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
+import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
@@ -36,7 +36,7 @@ data class RoomMemberListViewState(
         val ignoredUserIds: List<String> = emptyList(),
         val filter: String = "",
         val threePidInvites: Async<List<Event>> = Uninitialized,
-        val trustLevelMap: Async<Map<String, RoomEncryptionTrustLevel?>> = Uninitialized,
+        val trustLevelMap: Async<Map<String, UserVerificationLevel>> = Uninitialized,
         val actionsPermissions: ActionPermissions = ActionPermissions()
 ) : MavericksState {
 
diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt
index 5e6efcc816..3b74b4b38b 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt
@@ -77,7 +77,7 @@ class SpacePeopleListController @Inject constructor(
                                     id(roomMember.userId)
                                     matrixItem(roomMember.toMatrixItem())
                                     avatarRenderer(host.avatarRenderer)
-                                    userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
+                                    userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
                                             .apply {
                                                 val pl = host.toPowerLevelLabel(memberEntry.first)
                                                 if (memberEntry.first == RoomMemberListCategories.INVITE) {
diff --git a/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt b/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt
new file mode 100644
index 0000000000..5cad1fbc39
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2022 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.
+ * 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 im.vector.app.features
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import androidx.lifecycle.MutableLiveData
+import com.airbnb.mvrx.test.MvRxTestRule
+import im.vector.app.features.roomprofile.RoomProfileArgs
+import im.vector.app.features.roomprofile.members.RoomMemberListViewModel
+import im.vector.app.features.roomprofile.members.RoomMemberListViewState
+import im.vector.app.features.roomprofile.members.RoomMemberSummaryComparator
+import im.vector.app.test.test
+import im.vector.app.test.testCoroutineDispatchers
+import io.mockk.coEvery
+import io.mockk.every
+import io.mockk.mockk
+import org.junit.Rule
+import org.junit.Test
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.crypto.CryptoService
+import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
+import org.matrix.android.sdk.api.session.crypto.crosssigning.CryptoCrossSigningKey
+import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
+import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
+import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
+import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
+import org.matrix.android.sdk.api.session.room.Room
+import org.matrix.android.sdk.api.session.room.RoomService
+import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
+import org.matrix.android.sdk.api.session.room.members.MembershipService
+import org.matrix.android.sdk.api.session.room.model.Membership
+import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.session.room.state.StateService
+import org.matrix.android.sdk.api.session.user.UserService
+import org.matrix.android.sdk.api.util.Optional
+
+class MemberListViewModelTest {
+
+    @get:Rule
+    val mvrxTestRule = MvRxTestRule()
+
+    @get:Rule
+    val instantExecutorRule = InstantTaskExecutorRule()
+
+    private val fakeRoomId = "!roomId"
+    private val args = RoomProfileArgs(fakeRoomId)
+
+    private val aliceMxid = "@alice:example.com"
+    private val bobMxid = "@bob:example.com"
+    private val marcMxid = "@marc:example.com"
+
+    private val aliceDevice1 = CryptoDeviceInfo(
+            deviceId = "ALICE_1",
+            userId = aliceMxid,
+            trustLevel = DeviceTrustLevel(true, true)
+    )
+
+    private val aliceDevice2 = CryptoDeviceInfo(
+            deviceId = "ALICE_2",
+            userId = aliceMxid,
+            trustLevel = DeviceTrustLevel(false, false)
+    )
+
+    private val bobDevice1 = CryptoDeviceInfo(
+            deviceId = "BOB_1",
+            userId = bobMxid,
+            trustLevel = DeviceTrustLevel(true, true)
+    )
+
+    private val bobDevice2 = CryptoDeviceInfo(
+            deviceId = "BOB_2",
+            userId = bobMxid,
+            trustLevel = DeviceTrustLevel(true, true)
+    )
+
+    private val markDevice = CryptoDeviceInfo(
+            deviceId = "MARK_1",
+            userId = marcMxid,
+            trustLevel = DeviceTrustLevel(false, true)
+    )
+
+    private val fakeMembershipservice: MembershipService = mockk {
+
+        val memberList = mutableListOf<RoomMemberSummary>(
+                RoomMemberSummary(Membership.JOIN, aliceMxid, displayName = "Alice"),
+                RoomMemberSummary(Membership.JOIN, bobMxid, displayName = "Bob"),
+                RoomMemberSummary(Membership.JOIN, marcMxid, displayName = "marc")
+        )
+
+        every { getRoomMembers(any()) } returns memberList
+
+        every { getRoomMembersLive(any()) } returns MutableLiveData(memberList)
+
+        every { areAllMembersLoadedLive() } returns MutableLiveData(true)
+
+        coEvery { areAllMembersLoaded() } returns true
+    }
+
+    private val fakeRoomCryptoService: RoomCryptoService = mockk {
+        every { isEncrypted() } returns true
+    }
+    private val fakeRoom: Room = mockk {
+
+        val fakeStateService: StateService = mockk {
+            every { getStateEventLive(any(), any()) } returns MutableLiveData()
+            every { getStateEventsLive(any(), any()) } returns MutableLiveData()
+            every { getStateEvent(any(), any()) } returns null
+        }
+
+        every { stateService() } returns fakeStateService
+
+        every { coroutineDispatchers } returns testCoroutineDispatchers
+
+        every { getRoomSummaryLive() } returns MutableLiveData<Optional<RoomSummary>>(Optional(fakeRoomSummary))
+
+        every { membershipService() } returns fakeMembershipservice
+
+        every { roomCryptoService() } returns fakeRoomCryptoService
+
+        every { roomSummary() } returns fakeRoomSummary
+    }
+
+    private val fakeUserService: UserService = mockk {
+        every { getIgnoredUsersLive() } returns MutableLiveData()
+    }
+
+    val fakeSession: Session = mockk {
+
+        val fakeCrossSigningService: CrossSigningService = mockk {
+            every { isUserTrusted(aliceMxid) } returns true
+            every { isUserTrusted(bobMxid) } returns true
+            every { isUserTrusted(marcMxid) } returns false
+
+            every { getUserCrossSigningKeys(aliceMxid) } returns MXCrossSigningInfo(
+                    aliceMxid,
+                    crossSigningKeys = listOf(
+                            CryptoCrossSigningKey(
+                                    aliceMxid,
+                                    usages = listOf("master"),
+                                    keys = emptyMap(),
+                                    trustLevel = DeviceTrustLevel(true, true),
+                                    signatures = emptyMap()
+                            ),
+                            CryptoCrossSigningKey(
+                                    aliceMxid,
+                                    usages = listOf("self_signing"),
+                                    keys = emptyMap(),
+                                    trustLevel = DeviceTrustLevel(true, true),
+                                    signatures = emptyMap()
+                            ),
+                            CryptoCrossSigningKey(
+                                    aliceMxid,
+                                    usages = listOf("user_signing"),
+                                    keys = emptyMap(),
+                                    trustLevel = DeviceTrustLevel(true, true),
+                                    signatures = emptyMap()
+                            )
+                    ),
+                    true
+            )
+            every { getUserCrossSigningKeys(bobMxid) } returns MXCrossSigningInfo(
+                    aliceMxid,
+                    crossSigningKeys = listOf(
+                            CryptoCrossSigningKey(
+                                    bobMxid,
+                                    usages = listOf("master"),
+                                    keys = emptyMap(),
+                                    trustLevel = DeviceTrustLevel(true, true),
+                                    signatures = emptyMap()
+                            ),
+                            CryptoCrossSigningKey(
+                                    bobMxid,
+                                    usages = listOf("self_signing"),
+                                    keys = emptyMap(),
+                                    trustLevel = DeviceTrustLevel(true, true),
+                                    signatures = emptyMap()
+                            ),
+                            CryptoCrossSigningKey(
+                                    bobMxid,
+                                    usages = listOf("user_signing"),
+                                    keys = emptyMap(),
+                                    trustLevel = DeviceTrustLevel(true, true),
+                                    signatures = emptyMap()
+                            )
+                    ),
+                    true
+            )
+            every { getUserCrossSigningKeys(marcMxid) } returns MXCrossSigningInfo(
+                    aliceMxid,
+                    crossSigningKeys = listOf(
+                            CryptoCrossSigningKey(
+                                    marcMxid,
+                                    usages = listOf("master"),
+                                    keys = emptyMap(),
+                                    trustLevel = DeviceTrustLevel(false, false),
+                                    signatures = emptyMap()
+                            ),
+                            CryptoCrossSigningKey(
+                                    marcMxid,
+                                    usages = listOf("self_signing"),
+                                    keys = emptyMap(),
+                                    trustLevel = DeviceTrustLevel(false, false),
+                                    signatures = emptyMap()
+                            ),
+                            CryptoCrossSigningKey(
+                                    marcMxid,
+                                    usages = listOf("user_signing"),
+                                    keys = emptyMap(),
+                                    trustLevel = DeviceTrustLevel(false, false),
+                                    signatures = emptyMap()
+                            )
+                    ),
+                    true
+            )
+        }
+
+        val fakeCryptoService: CryptoService = mockk {
+            every { crossSigningService() } returns fakeCrossSigningService
+
+            every {
+                getLiveCryptoDeviceInfo(listOf(aliceMxid, bobMxid, marcMxid))
+            } returns MutableLiveData(
+                    listOf(
+                            aliceDevice1, aliceDevice2, bobDevice1, bobDevice2, markDevice
+                    )
+            )
+        }
+
+        val fakeRoomService: RoomService = mockk {
+            every { getRoom(any()) } returns fakeRoom
+        }
+        every { roomService() } returns fakeRoomService
+        every { userService() } returns fakeUserService
+        every { cryptoService() } returns fakeCryptoService
+    }
+
+    private val fakeRoomSummary = RoomSummary(
+            roomId = fakeRoomId,
+            displayName = "Fake Room",
+            topic = "A topic",
+            isEncrypted = true,
+            encryptionEventTs = 0,
+            typingUsers = emptyList(),
+    )
+
+    @Test
+    fun testBasicUserVerificationLevels() {
+        val viewModel = createViewModel()
+        viewModel
+                .test()
+                .assertLatestState {
+                    val trustMap = it.trustLevelMap.invoke() ?: return@assertLatestState false
+                    trustMap[aliceMxid] == UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED &&
+                            trustMap[bobMxid] == UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED &&
+                            trustMap[marcMxid] == UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY
+                }
+                .finish()
+    }
+
+    private fun createViewModel(): RoomMemberListViewModel {
+        return RoomMemberListViewModel(
+                RoomMemberListViewState(args),
+                RoomMemberSummaryComparator(),
+                fakeSession,
+        )
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/features/RoomMemberListControllerTest.kt b/vector/src/test/java/im/vector/app/features/RoomMemberListControllerTest.kt
new file mode 100644
index 0000000000..d32c3b5532
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/RoomMemberListControllerTest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2022 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.
+ * 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 im.vector.app.features
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.test.MvRxTestRule
+import im.vector.app.core.epoxy.profiles.ProfileMatrixItemWithPowerLevelWithPresence
+import im.vector.app.features.roomprofile.members.RoomMemberListCategories
+import im.vector.app.features.roomprofile.members.RoomMemberListController
+import im.vector.app.features.roomprofile.members.RoomMemberListViewState
+import io.mockk.every
+import io.mockk.mockk
+import org.amshove.kluent.shouldBeEqualTo
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
+import org.matrix.android.sdk.api.session.room.model.Membership
+import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class RoomMemberListControllerTest {
+
+    @get:Rule
+    val mvrxTestRule = MvRxTestRule()
+
+    @get:Rule
+    val instantExecutorRule = InstantTaskExecutorRule()
+
+    @Test
+    fun testControllerUserVerificationLevel() {
+        val roomListController = RoomMemberListController(
+                avatarRenderer = mockk {
+                },
+                stringProvider = mockk {
+                    every { getString(any()) } answers {
+                        this.args[0].toString()
+                    }
+                },
+                colorProvider = mockk {
+                    every { getColorFromAttribute(any()) } returns 0x0
+                },
+                roomMemberSummaryFilter = mockk(relaxed = true) {
+                    every { test(any()) } returns true
+                }
+        )
+
+        val fakeRoomSummary = RoomSummary(
+                roomId = "!roomId",
+                displayName = "Fake Room",
+                topic = "A topic",
+                isEncrypted = true,
+                encryptionEventTs = 0,
+                typingUsers = emptyList(),
+        )
+
+        val state = RoomMemberListViewState(
+                roomId = "!roomId",
+                roomSummary = Success(fakeRoomSummary),
+                areAllMembersLoaded = true,
+                roomMemberSummaries = Success(
+                        listOf(
+                                RoomMemberListCategories.USER to listOf(
+                                        RoomMemberSummary(
+                                                membership = Membership.JOIN,
+                                                userId = "@alice:example.com"
+                                        ),
+                                        RoomMemberSummary(
+                                                membership = Membership.JOIN,
+                                                userId = "@bob:example.com"
+                                        ),
+                                        RoomMemberSummary(
+                                                membership = Membership.JOIN,
+                                                userId = "@carl:example.com"
+                                        ),
+                                        RoomMemberSummary(
+                                                membership = Membership.JOIN,
+                                                userId = "@massy:example.com"
+                                        )
+                                )
+                        )
+                ),
+                trustLevelMap = Success(
+                        mapOf(
+                                "@alice:example.com" to UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY,
+                                "@bob:example.com" to UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED,
+                                "@carl:example.com" to UserVerificationLevel.WAS_NEVER_VERIFIED,
+                                "@massy:example.com" to UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED,
+                        )
+                )
+        )
+
+        roomListController.setData(state)
+
+        val models = roomListController.adapter.copyOfModels
+
+        val profileItems = models.filterIsInstance<ProfileMatrixItemWithPowerLevelWithPresence>()
+
+        profileItems.firstOrNull {
+            it.matrixItem.id == "@alice:example.com"
+        }!!.userVerificationLevel shouldBeEqualTo UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY
+
+        profileItems.firstOrNull {
+            it.matrixItem.id == "@bob:example.com"
+        }!!.userVerificationLevel shouldBeEqualTo UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED
+
+        profileItems.firstOrNull {
+            it.matrixItem.id == "@carl:example.com"
+        }!!.userVerificationLevel shouldBeEqualTo UserVerificationLevel.WAS_NEVER_VERIFIED
+
+        profileItems.firstOrNull {
+            it.matrixItem.id == "@massy:example.com"
+        }!!.userVerificationLevel shouldBeEqualTo UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED
+    }
+}