Added check self keys + force DL after initialize Xsigning

This commit is contained in:
Valere 2020-01-21 10:13:25 +01:00
parent 6ab540045b
commit 390879e3fd
4 changed files with 126 additions and 1 deletions

View file

@ -45,6 +45,9 @@ class XSigningTest : InstrumentedTest {
assertTrue("Signing Keys should be trusted", myCrossSigningKeys?.isTrusted == true) assertTrue("Signing Keys should be trusted", myCrossSigningKeys?.isTrusted == true)
assertTrue("Signing Keys should be trusted", aliceSession.getCrossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
mTestHelper.signout(aliceSession) mTestHelper.signout(aliceSession)
} }

View file

@ -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.crypto.tasks.UploadSigningKeysTask
import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.di.UserId 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.session.SessionScope
import im.vector.matrix.android.internal.task.TaskConstraints import im.vector.matrix.android.internal.task.TaskConstraints
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.JsonCanonicalizer 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.OlmPkSigning
import org.matrix.olm.OlmUtility import org.matrix.olm.OlmUtility
import timber.log.Timber import timber.log.Timber
@ -58,6 +62,8 @@ internal class DefaultCrossSigningService @Inject constructor(
private val deviceListManager: DeviceListManager, private val deviceListManager: DeviceListManager,
private val uploadSigningKeysTask: UploadSigningKeysTask, private val uploadSigningKeysTask: UploadSigningKeysTask,
private val uploadSignaturesTask: UploadSignaturesTask, private val uploadSignaturesTask: UploadSignaturesTask,
private val cryptoCoroutineScope: CoroutineScope,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val taskExecutor: TaskExecutor) : CrossSigningService { private val taskExecutor: TaskExecutor) : CrossSigningService {
private var olmUtility: OlmUtility? = null 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)) val crossSigningInfo = MXCrossSigningInfo(myUserID, listOf(params.masterKey, params.userKey, params.selfSignedKey))
cryptoStore.setMyCrossSigningInfo(crossSigningInfo) cryptoStore.setMyCrossSigningInfo(crossSigningInfo)
cryptoStore.setUserKeysAsTrusted(myUserID) cryptoStore.setUserKeysAsTrusted(myUserID)
cryptoStore.storePrivateKeysInfo(masterKeyPrivateKey?.toBase64NoPadding(), uskPrivateKey?.toBase64NoPadding(), sskPrivateKey?.toBase64NoPadding())
// TODO we should ensure that they are sent // TODO we should ensure that they are sent
// TODO error handling? // TODO error handling?
@ -244,6 +251,21 @@ internal class DefaultCrossSigningService @Inject constructor(
this.callback = object : MatrixCallback<SignatureUploadResponse> { this.callback = object : MatrixCallback<SignatureUploadResponse> {
override fun onSuccess(data: SignatureUploadResponse) { override fun onSuccess(data: SignatureUploadResponse) {
Timber.i("## CrossSigning - signatures succesfuly uploaded") 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) { override fun onFailure(failure: Throwable) {
@ -253,7 +275,6 @@ internal class DefaultCrossSigningService @Inject constructor(
}.executeBy(taskExecutor) }.executeBy(taskExecutor)
crossSigningState = CrossSigningState.Trusted crossSigningState = CrossSigningState.Trusted
callback?.onSuccess(Unit)
} }
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
@ -291,6 +312,9 @@ internal class DefaultCrossSigningService @Inject constructor(
*/ */
override fun checkUserTrust(userId: String): UserTrustResult { override fun checkUserTrust(userId: String): UserTrustResult {
Timber.d("## CrossSigning checkUserTrust for $userId") Timber.d("## CrossSigning checkUserTrust for $userId")
if (userId == credentials.userId) {
return checkSelfTrust()
}
// I trust a user if I trust his master key // I trust a user if I trust his master key
// I can trust the master key if it is signed by my user 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 // 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 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? { override fun getUserCrossSigningKeys(userId: String): MXCrossSigningInfo? {
return cryptoStore.getCrossSigningInfo(userId) return cryptoStore.getCrossSigningInfo(userId)
} }

View file

@ -15,6 +15,7 @@
*/ */
package im.vector.matrix.android.internal.crypto.crosssigning 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.CryptoCrossSigningKey
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.util.JsonCanonicalizer import im.vector.matrix.android.internal.util.JsonCanonicalizer
@ -26,3 +27,7 @@ fun CryptoDeviceInfo.canonicalSignable(): String {
fun CryptoCrossSigningKey.canonicalSignable(): String { fun CryptoCrossSigningKey.canonicalSignable(): String {
return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary()) return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
} }
fun ByteArray.toBase64NoPadding() : String? {
return Base64.encodeToString(this, Base64.NO_PADDING)
}

View file

@ -16,6 +16,7 @@
package im.vector.matrix.android.internal.crypto.crosssigning 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.crypto.crosssigning.MXCrossSigningInfo
import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
sealed class UserTrustResult { sealed class UserTrustResult {
@ -31,3 +32,5 @@ sealed class UserTrustResult {
data class KeyNotSigned(val key: CryptoCrossSigningKey) : UserTrustResult() data class KeyNotSigned(val key: CryptoCrossSigningKey) : UserTrustResult()
data class InvalidSignature(val key: CryptoCrossSigningKey, val signature: String) : UserTrustResult() data class InvalidSignature(val key: CryptoCrossSigningKey, val signature: String) : UserTrustResult()
} }
fun UserTrustResult.isVerified() = this is UserTrustResult.Success