mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 21:48:50 +03:00
Added check self keys + force DL after initialize Xsigning
This commit is contained in:
parent
6ab540045b
commit
390879e3fd
4 changed files with 126 additions and 1 deletions
|
@ -45,6 +45,9 @@ class XSigningTest : InstrumentedTest {
|
|||
|
||||
assertTrue("Signing Keys should be trusted", myCrossSigningKeys?.isTrusted == true)
|
||||
|
||||
assertTrue("Signing Keys should be trusted", aliceSession.getCrossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
|
||||
|
||||
|
||||
mTestHelper.signout(aliceSession)
|
||||
}
|
||||
|
||||
|
|
|
@ -38,11 +38,15 @@ import im.vector.matrix.android.internal.crypto.tasks.UploadSignaturesTask
|
|||
import im.vector.matrix.android.internal.crypto.tasks.UploadSigningKeysTask
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.TaskConstraints
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.olm.OlmPkSigning
|
||||
import org.matrix.olm.OlmUtility
|
||||
import timber.log.Timber
|
||||
|
@ -58,6 +62,8 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
private val deviceListManager: DeviceListManager,
|
||||
private val uploadSigningKeysTask: UploadSigningKeysTask,
|
||||
private val uploadSignaturesTask: UploadSignaturesTask,
|
||||
private val cryptoCoroutineScope: CoroutineScope,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val taskExecutor: TaskExecutor) : CrossSigningService {
|
||||
|
||||
private var olmUtility: OlmUtility? = null
|
||||
|
@ -200,6 +206,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
val crossSigningInfo = MXCrossSigningInfo(myUserID, listOf(params.masterKey, params.userKey, params.selfSignedKey))
|
||||
cryptoStore.setMyCrossSigningInfo(crossSigningInfo)
|
||||
cryptoStore.setUserKeysAsTrusted(myUserID)
|
||||
cryptoStore.storePrivateKeysInfo(masterKeyPrivateKey?.toBase64NoPadding(), uskPrivateKey?.toBase64NoPadding(), sskPrivateKey?.toBase64NoPadding())
|
||||
|
||||
// TODO we should ensure that they are sent
|
||||
// TODO error handling?
|
||||
|
@ -244,6 +251,21 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
this.callback = object : MatrixCallback<SignatureUploadResponse> {
|
||||
override fun onSuccess(data: SignatureUploadResponse) {
|
||||
Timber.i("## CrossSigning - signatures succesfuly uploaded")
|
||||
|
||||
// Force download of my keys now
|
||||
kotlin.runCatching {
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
deviceListManager.downloadKeys(listOf(myUserID), true)
|
||||
}
|
||||
}.foldToCallback(object : MatrixCallback<Any> {
|
||||
override fun onSuccess(data: Any) {
|
||||
callback?.onSuccess(Unit)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback?.onFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
|
@ -253,7 +275,6 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
}.executeBy(taskExecutor)
|
||||
|
||||
crossSigningState = CrossSigningState.Trusted
|
||||
callback?.onSuccess(Unit)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
|
@ -291,6 +312,9 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
*/
|
||||
override fun checkUserTrust(userId: String): UserTrustResult {
|
||||
Timber.d("## CrossSigning checkUserTrust for $userId")
|
||||
if (userId == credentials.userId) {
|
||||
return checkSelfTrust()
|
||||
}
|
||||
// I trust a user if I trust his master key
|
||||
// I can trust the master key if it is signed by my user key
|
||||
// TODO what if the master key is signed by a device key that i have verified
|
||||
|
@ -328,6 +352,96 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
return UserTrustResult.Success
|
||||
}
|
||||
|
||||
private fun checkSelfTrust(): UserTrustResult {
|
||||
// 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 myUserId = credentials.userId
|
||||
val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(myUserId)
|
||||
|
||||
val myMasterKey = myCrossSigningInfo?.masterKey()
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
|
||||
|
||||
// Is the master key trusted
|
||||
// 1) check if I know the private key
|
||||
val masterPrivateKey = cryptoStore.getCrossSigningPrivateKeys()?.master
|
||||
|
||||
var isMaterKeyTrusted = false
|
||||
if (masterPrivateKey != null) {
|
||||
// Check if private match public
|
||||
var olmPkSigning: OlmPkSigning? = null
|
||||
try {
|
||||
olmPkSigning = OlmPkSigning()
|
||||
val expectedPK = olmPkSigning.initWithSeed(Base64.decode(masterPrivateKey, Base64.NO_PADDING))
|
||||
isMaterKeyTrusted = myMasterKey.unpaddedBase64PublicKey == expectedPK
|
||||
} catch (failure: Throwable) {
|
||||
olmPkSigning?.releaseSigning()
|
||||
}
|
||||
} else {
|
||||
// Maybe it's signed by a locally trusted device?
|
||||
myMasterKey.signatures?.get(myUserId)?.forEach { (key, value) ->
|
||||
val potentialDeviceId = if (key.startsWith("ed25519:")) key.substring("ed25519:".length) else key
|
||||
val potentialDevice = cryptoStore.getUserDevice(myUserId, potentialDeviceId)
|
||||
if (potentialDevice != null && potentialDevice.isVerified) {
|
||||
// Check signature validity?
|
||||
try {
|
||||
olmUtility?.verifyEd25519Signature(value, potentialDevice.fingerprint(), myMasterKey.canonicalSignable())
|
||||
isMaterKeyTrusted = true
|
||||
return@forEach
|
||||
} catch (failure: Throwable) {
|
||||
// log
|
||||
Timber.v(failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMaterKeyTrusted) {
|
||||
return UserTrustResult.KeysNotTrusted(myCrossSigningInfo)
|
||||
}
|
||||
|
||||
val myUserKey = myCrossSigningInfo.userKey()
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
|
||||
|
||||
val userKeySignaturesMadeByMyMasterKey = myUserKey.signatures
|
||||
?.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")
|
||||
return UserTrustResult.KeyNotSigned(myUserKey)
|
||||
}
|
||||
|
||||
// Check that Alice USK signature of Alice MSK is valid
|
||||
try {
|
||||
olmUtility!!.verifyEd25519Signature(userKeySignaturesMadeByMyMasterKey, myMasterKey.unpaddedBase64PublicKey, myUserKey.canonicalSignable())
|
||||
} catch (failure: Throwable) {
|
||||
return UserTrustResult.InvalidSignature(myUserKey, userKeySignaturesMadeByMyMasterKey)
|
||||
}
|
||||
|
||||
val mySSKey = myCrossSigningInfo.selfSigningKey()
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
|
||||
|
||||
val SSKeySignaturesMadeByMyMasterKey = mySSKey.signatures
|
||||
?.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")
|
||||
return UserTrustResult.KeyNotSigned(mySSKey)
|
||||
}
|
||||
|
||||
// Check that Alice USK signature of Alice MSK is valid
|
||||
try {
|
||||
olmUtility!!.verifyEd25519Signature(SSKeySignaturesMadeByMyMasterKey, myMasterKey.unpaddedBase64PublicKey, mySSKey.canonicalSignable())
|
||||
} catch (failure: Throwable) {
|
||||
return UserTrustResult.InvalidSignature(mySSKey, SSKeySignaturesMadeByMyMasterKey)
|
||||
}
|
||||
|
||||
return UserTrustResult.Success
|
||||
}
|
||||
|
||||
override fun getUserCrossSigningKeys(userId: String): MXCrossSigningInfo? {
|
||||
return cryptoStore.getCrossSigningInfo(userId)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package im.vector.matrix.android.internal.crypto.crosssigning
|
||||
|
||||
import android.util.Base64
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
||||
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
||||
|
@ -26,3 +27,7 @@ fun CryptoDeviceInfo.canonicalSignable(): String {
|
|||
fun CryptoCrossSigningKey.canonicalSignable(): String {
|
||||
return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
|
||||
}
|
||||
|
||||
fun ByteArray.toBase64NoPadding() : String? {
|
||||
return Base64.encodeToString(this, Base64.NO_PADDING)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package im.vector.matrix.android.internal.crypto.crosssigning
|
||||
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
|
||||
|
||||
sealed class UserTrustResult {
|
||||
|
@ -31,3 +32,5 @@ sealed class UserTrustResult {
|
|||
data class KeyNotSigned(val key: CryptoCrossSigningKey) : UserTrustResult()
|
||||
data class InvalidSignature(val key: CryptoCrossSigningKey, val signature: String) : UserTrustResult()
|
||||
}
|
||||
|
||||
fun UserTrustResult.isVerified() = this is UserTrustResult.Success
|
||||
|
|
Loading…
Reference in a new issue