mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-17 12:30:07 +03:00
SAS verif, support signing and verification of Cross Signing
This commit is contained in:
parent
859c75df98
commit
98ba2d39a8
15 changed files with 208 additions and 79 deletions
|
@ -9,8 +9,7 @@ import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
|||
import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Assert.*
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -95,7 +94,7 @@ class XSigningTest : InstrumentedTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun test_CrossSigningTestAliceTrustBobNewDevice() {
|
||||
fun test_CrossSigningTestAliceTrustBobNewDevice() {
|
||||
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
|
@ -156,10 +155,10 @@ class XSigningTest : InstrumentedTest {
|
|||
}
|
||||
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
||||
if(data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId!!) == false) {
|
||||
if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId!!) == false) {
|
||||
fail("Bob should see the new device")
|
||||
}
|
||||
bobKeysLatch.countDown()
|
||||
bobKeysLatch.countDown()
|
||||
}
|
||||
})
|
||||
mTestHelper.await(bobKeysLatch)
|
||||
|
@ -189,7 +188,7 @@ class XSigningTest : InstrumentedTest {
|
|||
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
||||
//check that the device is seen
|
||||
if(data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) {
|
||||
if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) {
|
||||
fail("Alice should see the new device")
|
||||
}
|
||||
aliceKeysLatch.countDown()
|
||||
|
@ -197,18 +196,9 @@ class XSigningTest : InstrumentedTest {
|
|||
})
|
||||
mTestHelper.await(aliceKeysLatch)
|
||||
|
||||
val secondDevicetrustLatch = CountDownLatch(1)
|
||||
aliceSession.getCrossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId,object : MatrixCallback<Unit> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
fail("Failed to check trust cause:${failure.localizedMessage}")
|
||||
}
|
||||
|
||||
override fun onSuccess(data: Unit) {
|
||||
secondDevicetrustLatch.countDown()
|
||||
}
|
||||
})
|
||||
|
||||
mTestHelper.await(secondDevicetrustLatch)
|
||||
val result = aliceSession.getCrossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId)
|
||||
assertTrue("Bob second device should be trusted from alice POV", result.isSuccess())
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
|||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationCancel
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.toValue
|
||||
import org.junit.Assert.*
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
|
@ -36,9 +37,6 @@ import org.junit.runner.RunWith
|
|||
import org.junit.runners.MethodSorters
|
||||
import java.util.*
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.test.annotation.UiThreadTest
|
||||
import org.junit.Rule
|
||||
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
|
@ -69,7 +67,7 @@ class SASTest : InstrumentedTest {
|
|||
}
|
||||
bobSasMgr.addListener(bobListener)
|
||||
|
||||
val txID = aliceSasMgr.beginKeyVerificationSAS(bobSession.myUserId, bobSession.getMyDevice().deviceId)
|
||||
val txID = aliceSasMgr.beginKeyVerification(VerificationMethod.SAS, bobSession.myUserId, bobSession.getMyDevice().deviceId)
|
||||
assertNotNull("Alice should have a started transaction", txID)
|
||||
|
||||
val aliceKeyTx = aliceSasMgr.getExistingTransaction(bobSession.myUserId, txID!!)
|
||||
|
@ -147,10 +145,10 @@ class SASTest : InstrumentedTest {
|
|||
override fun transactionCreated(tx: SasVerificationTransaction) {}
|
||||
|
||||
override fun transactionUpdated(tx: SasVerificationTransaction) {
|
||||
if (tx.transactionId == tid && tx.cancelledReason != null) {
|
||||
cancelReason = tx.cancelledReason?.humanReadable
|
||||
cancelLatch.countDown()
|
||||
}
|
||||
if (tx.transactionId == tid && tx.cancelledReason != null) {
|
||||
cancelReason = tx.cancelledReason?.humanReadable
|
||||
cancelLatch.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
|
||||
|
@ -278,7 +276,7 @@ class SASTest : InstrumentedTest {
|
|||
codes: List<String> = SASVerificationTransaction.KNOWN_SHORT_CODES) {
|
||||
val startMessage = KeyVerificationStart(
|
||||
fromDevice = bobSession.getMyDevice().deviceId,
|
||||
method = KeyVerificationStart.VERIF_METHOD_SAS,
|
||||
method = VerificationMethod.SAS.toValue(),
|
||||
transactionID = tid,
|
||||
keyAgreementProtocols = protocols,
|
||||
hashes = hashes,
|
||||
|
@ -330,8 +328,8 @@ class SASTest : InstrumentedTest {
|
|||
|
||||
val bobUserId = bobSession!!.myUserId
|
||||
val bobDeviceId = bobSession.getMyDevice().deviceId
|
||||
aliceSasMgr.beginKeyVerificationSAS(bobUserId, bobDeviceId)
|
||||
aliceSasMgr.beginKeyVerificationSAS(bobUserId, bobDeviceId)
|
||||
aliceSasMgr.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId)
|
||||
aliceSasMgr.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId)
|
||||
|
||||
mTestHelper.await(aliceCreatedLatch)
|
||||
mTestHelper.await(aliceCancelledLatch)
|
||||
|
@ -388,7 +386,7 @@ class SASTest : InstrumentedTest {
|
|||
|
||||
val bobUserId = bobSession.myUserId
|
||||
val bobDeviceId = bobSession.getMyDevice().deviceId
|
||||
aliceSasMgr.beginKeyVerificationSAS(bobUserId, bobDeviceId)
|
||||
aliceSasMgr.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId)
|
||||
mTestHelper.await(aliceAcceptedLatch)
|
||||
|
||||
assertTrue("Should have receive a commitment", accepted!!.commitment?.trim()?.isEmpty() == false)
|
||||
|
@ -444,7 +442,7 @@ class SASTest : InstrumentedTest {
|
|||
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
|
||||
tx.performAccept()
|
||||
}
|
||||
else -> Unit
|
||||
else -> Unit
|
||||
}
|
||||
if (uxState === IncomingSasVerificationTransaction.UxState.SHOW_SAS) {
|
||||
bobSASLatch.countDown()
|
||||
|
@ -457,7 +455,7 @@ class SASTest : InstrumentedTest {
|
|||
|
||||
val bobUserId = bobSession.myUserId
|
||||
val bobDeviceId = bobSession.getMyDevice().deviceId
|
||||
val verificationSAS = aliceSasMgr.beginKeyVerificationSAS(bobUserId, bobDeviceId)
|
||||
val verificationSAS = aliceSasMgr.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId)
|
||||
mTestHelper.await(aliceSASLatch)
|
||||
mTestHelper.await(bobSASLatch)
|
||||
|
||||
|
@ -517,7 +515,7 @@ class SASTest : InstrumentedTest {
|
|||
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
|
||||
bobSASLatch.countDown()
|
||||
}
|
||||
else -> Unit
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -527,7 +525,7 @@ class SASTest : InstrumentedTest {
|
|||
|
||||
val bobUserId = bobSession.myUserId
|
||||
val bobDeviceId = bobSession.getMyDevice().deviceId
|
||||
aliceSasMgr.beginKeyVerificationSAS(bobUserId, bobDeviceId)
|
||||
aliceSasMgr.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId)
|
||||
mTestHelper.await(aliceSASLatch)
|
||||
mTestHelper.await(bobSASLatch)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package im.vector.matrix.android.api.session.crypto.crosssigning
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustResult
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
|
||||
|
||||
|
@ -43,5 +44,5 @@ interface CrossSigningService {
|
|||
*/
|
||||
fun signDevice(deviceId: String, callback: MatrixCallback<SignatureUploadResponse>)
|
||||
|
||||
fun checkDeviceTrust(userId: String, deviceId: String, callback: MatrixCallback<Unit>)
|
||||
fun checkDeviceTrust(userId: String, deviceId: String) : DeviceTrustResult
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
import im.vector.matrix.android.internal.crypto.crosssigning.DefaultCrossSigningService
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.*
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
|
@ -199,4 +201,7 @@ internal abstract class CryptoModule {
|
|||
@Binds
|
||||
abstract fun bindDeleteDeviceWithUserPasswordTask(deleteDeviceWithUserPasswordTask: DefaultDeleteDeviceWithUserPasswordTask)
|
||||
: DeleteDeviceWithUserPasswordTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindCrossSigningService(crossSigningService: DefaultCrossSigningService) : CrossSigningService
|
||||
}
|
||||
|
|
|
@ -26,13 +26,13 @@ import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningIn
|
|||
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||
import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.*
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
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.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
|
||||
|
@ -42,6 +42,8 @@ import org.matrix.olm.OlmUtility
|
|||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@SessionScope
|
||||
internal class DefaultCrossSigningService @Inject constructor(
|
||||
@UserId private val userId: String,
|
||||
private val credentials: Credentials,
|
||||
|
@ -153,9 +155,11 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
Timber.v("## CrossSigning - uskPublicKey:$uskPublicKey")
|
||||
|
||||
// Sign userSigningKey with master
|
||||
val signedUSK = JsonCanonicalizer.getCanonicalJson(Map::class.java, CrossSigningKeyInfo.Builder(myUserID, CrossSigningKeyInfo.KeyUsage.USER_SIGNING)
|
||||
val signedUSK = CrossSigningKeyInfo.Builder(myUserID, CrossSigningKeyInfo.KeyUsage.USER_SIGNING)
|
||||
.key(uskPublicKey)
|
||||
.build().signalableJSONDictionary()).let { masterPkOlm.sign(it) }
|
||||
.build()
|
||||
.canonicalSignable()
|
||||
.let { masterPkOlm.sign(it) }
|
||||
|
||||
//=================
|
||||
// SELF SIGNING KEY
|
||||
|
@ -322,9 +326,6 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
// olmUtility?.verifyEd25519Signature(masterKeySignaturesMadeByMyUserKey,
|
||||
// myUserKey.publicKeyBase64,
|
||||
// masterKey.publicKeyBase64)
|
||||
|
||||
}
|
||||
|
||||
|
@ -399,7 +400,6 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
}
|
||||
|
||||
// Sign with self signing
|
||||
// val newSignature = JsonCanonicalizer.getCanonicalJson(Map::class.java, device.signalableJSONDictionary()).let { userPkSigning?.sign(it) }
|
||||
val newSignature = selfSigningPkSigning?.sign(device.canonicalSignable())
|
||||
|
||||
if (newSignature == null) {
|
||||
|
@ -416,7 +416,6 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
)
|
||||
)
|
||||
)
|
||||
// device.addSignature(credentials.userId, ssPubKey, newSignature)
|
||||
|
||||
val uploadQuery = UploadSignatureQueryBuilder()
|
||||
.withDeviceInfo(toUpload)
|
||||
|
@ -441,30 +440,21 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
}.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun checkDeviceTrust(userId: String, deviceId: String, callback: MatrixCallback<Unit>) {
|
||||
override fun checkDeviceTrust(userId: String, deviceId: String): DeviceTrustResult {
|
||||
val otherDevice = cryptoStore.getUserDevice(userId, deviceId)
|
||||
if (otherDevice == null) {
|
||||
callback.onFailure(IllegalArgumentException("This device is not known, or not yours"))
|
||||
return
|
||||
}
|
||||
?: return DeviceTrustResult.UnknownDevice(deviceId)
|
||||
|
||||
val myKeys = getUserCrossSigningKeys(credentials.userId)
|
||||
if (myKeys == null) {
|
||||
callback.onFailure(Throwable("CrossSigning is not setup for this account"))
|
||||
return
|
||||
}
|
||||
?: return DeviceTrustResult.CrossSigningNotConfigured(credentials.userId)
|
||||
|
||||
if (!myKeys.isTrusted) return DeviceTrustResult.KeysNotTrusted(myKeys)
|
||||
|
||||
val otherKeys = getUserCrossSigningKeys(userId)
|
||||
if (otherKeys == null) {
|
||||
callback.onFailure(Throwable("CrossSigning is not setup for $userId"))
|
||||
return
|
||||
}
|
||||
?: return DeviceTrustResult.CrossSigningNotConfigured(userId)
|
||||
|
||||
|
||||
// TODO should we force verification ?
|
||||
if (!otherKeys.isTrusted) {
|
||||
callback.onFailure(Throwable("$userId is not trusted"))
|
||||
return
|
||||
}
|
||||
if (!otherKeys.isTrusted) return DeviceTrustResult.KeysNotTrusted(otherKeys)
|
||||
|
||||
// Check if the trust chain is valid
|
||||
/*
|
||||
|
@ -486,26 +476,18 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
*/
|
||||
|
||||
val otherSSKSignature = otherDevice.signatures?.get(userId)?.get("ed25519:${otherKeys.selfSigningKey()?.unpaddedBase64PublicKey}")
|
||||
|
||||
if (otherSSKSignature == null) {
|
||||
callback.onFailure(Throwable("Device ${otherDevice.deviceId} is not signed by $userId self signed key"))
|
||||
return
|
||||
}
|
||||
|
||||
?: return DeviceTrustResult.MissingDeviceSignature(deviceId, otherKeys.selfSigningKey()?.unpaddedBase64PublicKey
|
||||
?: "")
|
||||
|
||||
// Check bob's device is signed by bob's SSK
|
||||
try {
|
||||
olmUtility?.verifyEd25519Signature(otherSSKSignature, otherKeys.selfSigningKey()?.unpaddedBase64PublicKey, otherDevice.canonicalSignable())
|
||||
} catch (e: Throwable) {
|
||||
callback.onFailure(Throwable("Invalid self signed signature for Device ${otherDevice.deviceId}"))
|
||||
return DeviceTrustResult.InvalidDeviceSignature(deviceId, otherSSKSignature, e)
|
||||
}
|
||||
|
||||
callback.onSuccess(Unit)
|
||||
return DeviceTrustResult.Success(deviceId, true)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun MXDeviceInfo.canonicalSignable(): String {
|
||||
return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2020 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.matrix.android.internal.crypto.crosssigning
|
||||
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||
|
||||
sealed class DeviceTrustResult {
|
||||
|
||||
data class Success(val deviceID: String, val crossSigned: Boolean) : DeviceTrustResult()
|
||||
data class UnknownDevice(val deviceID: String) : DeviceTrustResult()
|
||||
data class CrossSigningNotConfigured(val userID: String) : DeviceTrustResult()
|
||||
data class KeysNotTrusted(val key: MXCrossSigningInfo) : DeviceTrustResult()
|
||||
data class MissingDeviceSignature(val deviceId: String, val signingKey: String) : DeviceTrustResult()
|
||||
data class InvalidDeviceSignature(val deviceId: String, val signingKey: String, val throwable: Throwable?) : DeviceTrustResult()
|
||||
|
||||
}
|
||||
|
||||
fun DeviceTrustResult.isSuccess(): Boolean = this is DeviceTrustResult.Success
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2020 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.matrix.android.internal.crypto.crosssigning
|
||||
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.CrossSigningKeyInfo
|
||||
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
||||
|
||||
|
||||
fun MXDeviceInfo.canonicalSignable(): String {
|
||||
return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
|
||||
}
|
||||
|
||||
fun CrossSigningKeyInfo.canonicalSignable(): String {
|
||||
return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
|
||||
}
|
|
@ -72,6 +72,7 @@ data class CrossSigningKeyInfo(
|
|||
userMap["ed25519:${signedWithNoPrefix}"] = signature
|
||||
signatures = updated
|
||||
}
|
||||
|
||||
// fun toXSigningKeys(): XSigningKeys {
|
||||
// return XSigningKeys(
|
||||
// userId = userId,
|
||||
|
|
|
@ -405,6 +405,7 @@ internal interface IMXCryptoStore {
|
|||
fun setCrossSigningInfo(userId: String, info: MXCrossSigningInfo?)
|
||||
|
||||
|
||||
fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?)
|
||||
fun getCrossSigningPrivateKeys() : PrivateKeysInfo?
|
||||
|
||||
fun setUserKeysAsTrusted(userId: String, trusted: Boolean = true)
|
||||
|
|
|
@ -303,6 +303,17 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati
|
|||
}
|
||||
}
|
||||
|
||||
override fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?) {
|
||||
doRealmTransaction(realmConfiguration) {realm ->
|
||||
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
|
||||
xSignMasterPrivateKey = msk
|
||||
xSignSelfSignedPrivateKey = ssk
|
||||
xSignUserPrivateKey = usk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getUserDevices(userId: String): Map<String, MXDeviceInfo>? {
|
||||
return doRealmQueryAndCopy(realmConfiguration) {
|
||||
it.where<UserEntity>()
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
import im.vector.matrix.android.internal.crypto.model.MXKeysObject
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse
|
||||
|
@ -36,9 +37,14 @@ internal class DefaultUploadSignaturesTask @Inject constructor(
|
|||
) : UploadSignaturesTask {
|
||||
|
||||
override suspend fun execute(params: UploadSignaturesTask.Params): SignatureUploadResponse {
|
||||
return executeRequest(eventBus) {
|
||||
val executeRequest = executeRequest<SignatureUploadResponse>(eventBus) {
|
||||
apiCall = cryptoApi.uploadSignatures(params.signatures)
|
||||
}
|
||||
if (executeRequest.failures?.isNotEmpty() == true) {
|
||||
//TODO better
|
||||
throw Failure.OtherServerError(executeRequest.toString(), 400)
|
||||
}
|
||||
return executeRequest
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.crypto.verification
|
|||
import android.util.Base64
|
||||
import im.vector.matrix.android.BuildConfig
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasMode
|
||||
|
@ -31,6 +32,7 @@ internal class DefaultIncomingSASVerificationTransaction(
|
|||
setDeviceVerificationAction: SetDeviceVerificationAction,
|
||||
override val credentials: Credentials,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
crossSigningService: CrossSigningService,
|
||||
deviceFingerprint: String,
|
||||
transactionId: String,
|
||||
otherUserID: String,
|
||||
|
@ -39,6 +41,7 @@ internal class DefaultIncomingSASVerificationTransaction(
|
|||
setDeviceVerificationAction,
|
||||
credentials,
|
||||
cryptoStore,
|
||||
crossSigningService,
|
||||
deviceFingerprint,
|
||||
transactionId,
|
||||
otherUserID,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package im.vector.matrix.android.internal.crypto.verification
|
||||
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
|
@ -30,6 +31,7 @@ internal class DefaultOutgoingSASVerificationRequest(
|
|||
setDeviceVerificationAction: SetDeviceVerificationAction,
|
||||
credentials: Credentials,
|
||||
cryptoStore: IMXCryptoStore,
|
||||
crossSigningService: CrossSigningService,
|
||||
deviceFingerprint: String,
|
||||
transactionId: String,
|
||||
otherUserId: String,
|
||||
|
@ -38,6 +40,7 @@ internal class DefaultOutgoingSASVerificationRequest(
|
|||
setDeviceVerificationAction,
|
||||
credentials,
|
||||
cryptoStore,
|
||||
crossSigningService,
|
||||
deviceFingerprint,
|
||||
transactionId,
|
||||
otherUserId,
|
||||
|
|
|
@ -21,8 +21,8 @@ import android.os.Looper
|
|||
import dagger.Lazy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.auth.data.sessionId
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.*
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
|
@ -56,7 +56,8 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
private val setDeviceVerificationAction: SetDeviceVerificationAction,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val sasTransportRoomMessageFactory: SasTransportRoomMessageFactory,
|
||||
private val sasTransportToDeviceFactory: SasTransportToDeviceFactory
|
||||
private val sasTransportToDeviceFactory: SasTransportToDeviceFactory,
|
||||
private val crossSigningService: CrossSigningService
|
||||
) : VerificationTransaction.Listener, SasVerificationService {
|
||||
|
||||
private val uiHandler = Handler(Looper.getMainLooper())
|
||||
|
@ -375,6 +376,7 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
setDeviceVerificationAction,
|
||||
credentials,
|
||||
cryptoStore,
|
||||
crossSigningService,
|
||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||
startReq.transactionID!!,
|
||||
otherUserId,
|
||||
|
@ -696,6 +698,7 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
setDeviceVerificationAction,
|
||||
credentials,
|
||||
cryptoStore,
|
||||
crossSigningService,
|
||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||
txID,
|
||||
userId,
|
||||
|
@ -793,6 +796,7 @@ internal class DefaultSasVerificationService @Inject constructor(
|
|||
setDeviceVerificationAction,
|
||||
credentials,
|
||||
cryptoStore,
|
||||
crossSigningService,
|
||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||
transactionId,
|
||||
otherUserId,
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
package im.vector.matrix.android.internal.crypto.verification
|
||||
|
||||
import android.os.Build
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.EmojiRepresentation
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasMode
|
||||
|
@ -25,6 +27,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
|||
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.MXKey
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.extensions.toUnsignedInt
|
||||
import org.matrix.olm.OlmSAS
|
||||
|
@ -39,6 +42,7 @@ internal abstract class SASVerificationTransaction(
|
|||
private val setDeviceVerificationAction: SetDeviceVerificationAction,
|
||||
open val credentials: Credentials,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val crossSigningService: CrossSigningService,
|
||||
private val deviceFingerprint: String,
|
||||
transactionId: String,
|
||||
otherUserId: String,
|
||||
|
@ -142,18 +146,46 @@ internal abstract class SASVerificationTransaction(
|
|||
otherUserId + otherDeviceId +
|
||||
transactionId
|
||||
|
||||
// Previously, with SAS verification, the m.key.verification.mac message only contained the user's device key.
|
||||
// It should now contain both the device key and the MSK.
|
||||
// So when Alice and Bob verify with SAS, the verification will verify the MSK.
|
||||
|
||||
val keyMap = HashMap<String, String>()
|
||||
|
||||
val keyId = "ed25519:${credentials.deviceId}"
|
||||
val macString = macUsingAgreedMethod(deviceFingerprint, baseInfo + keyId)
|
||||
val keyStrings = macUsingAgreedMethod(keyId, baseInfo + "KEY_IDS")
|
||||
|
||||
if (macString.isNullOrBlank() || keyStrings.isNullOrBlank()) {
|
||||
if (macString.isNullOrBlank()) {
|
||||
// Should not happen
|
||||
Timber.e("## SAS verification [$transactionId] failed to send KeyMac, empty key hashes.")
|
||||
cancel(CancelCode.UnexpectedMessage)
|
||||
return
|
||||
}
|
||||
|
||||
val macMsg = transport.createMac(transactionId, mapOf(keyId to macString), keyStrings)
|
||||
keyMap[keyId] = macString
|
||||
|
||||
cryptoStore.getMyCrossSigningInfo()?.takeIf { it.isTrusted }
|
||||
?.masterKey()
|
||||
?.unpaddedBase64PublicKey
|
||||
?.let { masterPublicKey ->
|
||||
val crossSigningKeyId = "ed25519:${masterPublicKey}"
|
||||
macUsingAgreedMethod(masterPublicKey, baseInfo + crossSigningKeyId)?.let { MSKMacString ->
|
||||
keyMap[crossSigningKeyId] = MSKMacString
|
||||
}
|
||||
}
|
||||
|
||||
val keyStrings = macUsingAgreedMethod(keyMap.keys.sorted().joinToString(","), baseInfo + "KEY_IDS")
|
||||
|
||||
if (macString.isNullOrBlank() || keyStrings.isNullOrBlank()) {
|
||||
// Should not happen
|
||||
Timber.e("## SAS verification [$transactionId] failed to send KeyMac, empty key hashes.")
|
||||
Timber.e("## SAS verification [$transactionId] failed to send KeyMac, empty key hashes.")
|
||||
cancel(CancelCode.UnexpectedMessage)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
val macMsg = transport.createMac(transactionId, keyMap, keyStrings)
|
||||
myMac = macMsg
|
||||
state = SasVerificationTxState.SendingMac
|
||||
sendToOther(EventType.KEY_VERIFICATION_MAC, macMsg, SasVerificationTxState.MacSent, CancelCode.User) {
|
||||
|
@ -227,7 +259,7 @@ internal abstract class SASVerificationTransaction(
|
|||
val keyIDNoPrefix = if (it.startsWith("ed25519:")) it.substring("ed25519:".length) else it
|
||||
val otherDeviceKey = otherUserKnownDevices?.get(keyIDNoPrefix)?.fingerprint()
|
||||
if (otherDeviceKey == null) {
|
||||
Timber.e("## SAS Verification: Could not find device $keyIDNoPrefix to verify")
|
||||
Timber.w("## SAS Verification: Could not find device $keyIDNoPrefix to verify")
|
||||
// just ignore and continue
|
||||
return@forEach
|
||||
}
|
||||
|
@ -241,14 +273,46 @@ internal abstract class SASVerificationTransaction(
|
|||
verifiedDevices.add(keyIDNoPrefix)
|
||||
}
|
||||
|
||||
var otherMasterKeyIsVerified = false
|
||||
val otherMasterKey = cryptoStore.getCrossSigningInfo(otherUserId)?.masterKey()
|
||||
val otherCrossSigningMasterKeyPublic = otherMasterKey?.unpaddedBase64PublicKey
|
||||
if (otherCrossSigningMasterKeyPublic != null) {
|
||||
//Did the user signed his master key
|
||||
theirMac!!.mac!!.keys.forEach {
|
||||
val keyIDNoPrefix = if (it.startsWith("ed25519:")) it.substring("ed25519:".length) else it
|
||||
if (keyIDNoPrefix == otherCrossSigningMasterKeyPublic) {
|
||||
//Check the signature
|
||||
val mac = macUsingAgreedMethod(otherCrossSigningMasterKeyPublic, baseInfo + it)
|
||||
if (mac != theirMac?.mac?.get(it)) {
|
||||
// WRONG!
|
||||
Timber.e("## SAS Verification: mac mismatch for MasterKey with id $keyIDNoPrefix")
|
||||
cancel(CancelCode.MismatchedKeys)
|
||||
return
|
||||
} else {
|
||||
otherMasterKeyIsVerified = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if none of the keys could be verified, then error because the app
|
||||
// should be informed about that
|
||||
if (verifiedDevices.isEmpty()) {
|
||||
if (verifiedDevices.isEmpty() && !otherMasterKeyIsVerified) {
|
||||
Timber.e("## SAS Verification: No devices verified")
|
||||
cancel(CancelCode.MismatchedKeys)
|
||||
return
|
||||
}
|
||||
|
||||
if (otherMasterKeyIsVerified) {
|
||||
// we should trust this master key
|
||||
// And check verification MSK -> SSK?
|
||||
crossSigningService.trustUser(otherUserId, object : MatrixCallback<SignatureUploadResponse> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure,"## SAS Verification: Failed to trust User $otherUserId")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TODO what if the otherDevice is not in this list? and should we
|
||||
verifiedDevices.forEach {
|
||||
setDeviceVerified(it, otherUserId)
|
||||
|
|
Loading…
Add table
Reference in a new issue