update rust-sdk bindings

This commit is contained in:
valere 2022-11-28 21:48:46 +01:00
parent 2f3bbab4c4
commit d9342707fd
26 changed files with 535 additions and 368 deletions

2
.gitignore vendored
View file

@ -31,3 +31,5 @@ Cargo.lock
matrix-sdk-android/src/main/jniLibs/
matrix-sdk-android/libs/crypto-android-release.aar
matrix-sdk-android/libs/matrix-rust-sdk-crypto.aar

View file

@ -169,8 +169,6 @@ dependencies {
implementation libs.jetbrains.coroutinesCore
implementation libs.jetbrains.coroutinesAndroid
implementation 'org.matrix.rustcomponents:crypto-android:0.2.1-SNAPSHOT'
//implementation files('libs/crypto-android-release.aar')
// implementation(name: 'crypto-android-release', ext: 'aar')
implementation 'net.java.dev.jna:jna:5.10.0@aar'
@ -236,7 +234,8 @@ dependencies {
implementation libs.google.phonenumber
rustCryptoImplementation 'org.matrix.rustcomponents:crypto-android:0.2.1-SNAPSHOT'
// rustCryptoImplementation 'org.matrix.rustcomponents:crypto-android:0.2.1-SNAPSHOT'
rustCryptoImplementation files('libs/matrix-rust-sdk-crypto.aar')
testImplementation libs.tests.junit
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281

View file

@ -16,7 +16,7 @@
package org.matrix.android.sdk.api.session.crypto.model
import uniffi.olm.KeysImportResult
import org.matrix.rustcomponents.sdk.crypto.KeysImportResult
data class ImportRoomKeysResult(
val totalNumberOfKeys: Int,

View file

@ -104,7 +104,7 @@ interface VerificationService {
scannedData: String
): String?
suspend fun sasCodeMatch(theyMatch: Boolean, transactionId: String)
// suspend fun sasCodeMatch(theyMatch: Boolean, transactionId: String)
// This starts the short SAS flow, the one that doesn't start with a request, deprecated

View file

@ -16,82 +16,49 @@
package org.matrix.android.sdk.internal.crypto.verification
import android.os.Handler
import android.os.Looper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber
import javax.inject.Inject
@SessionScope
internal class VerificationListenersHolder @Inject constructor() {
internal class VerificationListenersHolder @Inject constructor(
private val coroutineDispatchers: MatrixCoroutineDispatchers
) {
private val listeners = ArrayList<VerificationService.Listener>()
private val uiHandler = Handler(Looper.getMainLooper())
fun listeners(): List<VerificationService.Listener> = listeners
fun addListener(listener: VerificationService.Listener) {
uiHandler.post {
if (!this.listeners.contains(listener)) {
this.listeners.add(listener)
}
}
}
fun removeListener(listener: VerificationService.Listener) {
uiHandler.post { this.listeners.remove(listener) }
}
val scope = CoroutineScope(SupervisorJob() + coroutineDispatchers.dmVerif)
val eventFlow = MutableSharedFlow<VerificationEvent>(extraBufferCapacity = 20, onBufferOverflow = BufferOverflow.SUSPEND)
fun dispatchTxAdded(tx: VerificationTransaction) {
uiHandler.post {
this.listeners.forEach {
try {
it.transactionCreated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
scope.launch {
eventFlow.emit(VerificationEvent.TransactionAdded(tx))
}
}
fun dispatchTxUpdated(tx: VerificationTransaction) {
uiHandler.post {
this.listeners.forEach {
try {
it.transactionUpdated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
scope.launch {
eventFlow.emit(VerificationEvent.TransactionUpdated(tx))
}
}
fun dispatchRequestAdded(verificationRequest: PendingVerificationRequest) {
Timber.v("## SAS dispatchRequestAdded txId:${verificationRequest.transactionId} $verificationRequest")
uiHandler.post {
this.listeners.forEach {
try {
it.verificationRequestCreated(verificationRequest)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
scope.launch {
eventFlow.emit(VerificationEvent.RequestAdded(verificationRequest))
}
}
fun dispatchRequestUpdated(verificationRequest: PendingVerificationRequest) {
uiHandler.post {
listeners.forEach {
try {
it.verificationRequestUpdated(verificationRequest)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
scope.launch {
eventFlow.emit(VerificationEvent.RequestUpdated(verificationRequest))
}
}
}

View file

@ -32,6 +32,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
import org.matrix.android.sdk.internal.network.parsing.CipherSuiteMoshiAdapter
import org.matrix.android.sdk.internal.network.parsing.ForceToBooleanJsonAdapter
import org.matrix.android.sdk.internal.network.parsing.RuntimeJsonAdapterFactory
@ -42,6 +43,9 @@ import org.matrix.android.sdk.internal.session.sync.parsing.DefaultLazyRoomSyncE
internal object MoshiProvider {
private val moshi: Moshi = Moshi.Builder()
// By default all numbers are transformed into floats by moshi
// this adapter tries to see first if it's a natural number before using float
.add(CheckNumberType.JSON_ADAPTER_FACTORY)
.add(UriMoshiAdapter())
.add(ForceToBooleanJsonAdapter())
.add(CipherSuiteMoshiAdapter())

View file

@ -279,8 +279,12 @@ internal abstract class SessionModule {
sessionParams: SessionParams,
retrofitFactory: RetrofitFactory
): Retrofit {
var uri = sessionParams.homeServerConnectionConfig.homeServerUriBase.toString()
if (uri == "http://localhost:8080") {
uri = "http://10.0.2.2:8080"
}
return retrofitFactory
.create(okHttpClient, sessionParams.homeServerConnectionConfig.homeServerUriBase.toString())
.create(okHttpClient, uri)
}
@JvmStatic

View file

@ -30,9 +30,10 @@ import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.SasVerification
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import org.matrix.android.sdk.internal.crypto.verification.prepareMethods
import uniffi.olm.CryptoStoreException
import uniffi.olm.SignatureException
import uniffi.olm.Device as InnerDevice
import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import org.matrix.rustcomponents.sdk.crypto.LocalTrust
import org.matrix.rustcomponents.sdk.crypto.SignatureException
import org.matrix.rustcomponents.sdk.crypto.Device as InnerDevice
/** Class representing a device that supports E2EE in the Matrix world
*
@ -88,7 +89,7 @@ internal class Device @AssistedInject constructor(
requestSender.sendVerificationRequest(result.request)
verificationRequestFactory.create(result.verification)
} catch (failure: Throwable) {
innerMachine.cancelVerification(result.verification.otherUserId, result.verification.flowId, CancelCode.UserError.value)
// innerMachine.cancelVerification(result.verification.otherUserId, result.verification.flowId, CancelCode.UserError.value)
null
}
} else {
@ -115,7 +116,8 @@ internal class Device @AssistedInject constructor(
requestSender.sendVerificationRequest(result.request)
sasVerificationFactory.create(result.sas)
} catch (failure: Throwable) {
innerMachine.cancelVerification(result.sas.otherUserId, result.sas.flowId, CancelCode.UserError.value)
result.sas.cancel(CancelCode.UserError.value)
// innerMachine.cancelVerification(result.sas.otherUserId, result.sas.flowId, CancelCode.UserError.value)
null
}
} else {
@ -132,7 +134,7 @@ internal class Device @AssistedInject constructor(
@Throws(CryptoStoreException::class)
suspend fun markAsTrusted() {
withContext(coroutineDispatchers.io) {
innerMachine.markDeviceAsTrusted(innerDevice.userId, innerDevice.deviceId)
innerMachine.setLocalTrust(innerDevice.userId, innerDevice.deviceId, LocalTrust.VERIFIED)
}
}
@ -169,18 +171,21 @@ internal class Device @AssistedInject constructor(
* This will not fetch out fresh data from the Rust side.
**/
internal fun toCryptoDeviceInfo(): CryptoDeviceInfo {
val keys = innerDevice.keys.map { (keyId, key) -> "$keyId:$innerDevice.deviceId" to key }.toMap()
// val keys = innerDevice.keys.map { (keyId, key) -> keyId to key }.toMap()
return CryptoDeviceInfo(
deviceId = innerDevice.deviceId,
userId = innerDevice.userId,
algorithms = innerDevice.algorithms,
keys = keys,
keys = innerDevice.keys,
// The Kotlin side doesn't need to care about signatures,
// so we're not filling this out
signatures = mapOf(),
unsigned = UnsignedDeviceInfo(innerDevice.displayName),
trustLevel = DeviceTrustLevel(crossSigningVerified = innerDevice.crossSigningTrusted, locallyVerified = innerDevice.locallyTrusted),
trustLevel = DeviceTrustLevel(
crossSigningVerified = innerDevice.crossSigningTrusted,
locallyVerified = innerDevice.locallyTrusted
),
isBlocked = innerDevice.isBlocked,
// TODO
firstTimeSeenLocalTs = null

View file

@ -21,8 +21,8 @@ import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import uniffi.olm.Request
import uniffi.olm.RequestType
import org.matrix.rustcomponents.sdk.crypto.Request
import org.matrix.rustcomponents.sdk.crypto.RequestType
import java.util.UUID
import javax.inject.Inject
import javax.inject.Provider

View file

@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.internal.crypto.model.rest.RestKeyInfo
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import uniffi.olm.CryptoStoreException
import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import javax.inject.Inject
import javax.inject.Provider
@ -44,7 +44,7 @@ internal class GetUserIdentityUseCase @Inject constructor(
val adapter = moshi.adapter(RestKeyInfo::class.java)
return when (identity) {
is uniffi.olm.UserIdentity.Other -> {
is org.matrix.rustcomponents.sdk.crypto.UserIdentity.Other -> {
val verified = innerMachine.isIdentityVerified(userId)
val masterKey = adapter.fromJson(identity.masterKey)!!.toCryptoModel().apply {
trustLevel = DeviceTrustLevel(verified, verified)
@ -62,7 +62,7 @@ internal class GetUserIdentityUseCase @Inject constructor(
verificationRequestFactory = verificationRequestFactory
)
}
is uniffi.olm.UserIdentity.Own -> {
is org.matrix.rustcomponents.sdk.crypto.UserIdentity.Own -> {
val verified = innerMachine.isIdentityVerified(userId)
val masterKey = adapter.fromJson(identity.masterKey)!!.toCryptoModel().apply {

View file

@ -19,6 +19,8 @@ package org.matrix.android.sdk.internal.crypto
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.adapter
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
@ -55,30 +57,32 @@ import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import org.matrix.android.sdk.internal.crypto.verification.VerificationsProvider
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.rustcomponents.sdk.crypto.BackupKeys
import org.matrix.rustcomponents.sdk.crypto.BackupRecoveryKey
import org.matrix.rustcomponents.sdk.crypto.CrossSigningKeyExport
import org.matrix.rustcomponents.sdk.crypto.CrossSigningStatus
import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import org.matrix.rustcomponents.sdk.crypto.DecryptionException
import org.matrix.rustcomponents.sdk.crypto.DeviceLists
import org.matrix.rustcomponents.sdk.crypto.EncryptionSettings
import org.matrix.rustcomponents.sdk.crypto.KeyRequestPair
import org.matrix.rustcomponents.sdk.crypto.Logger
import org.matrix.rustcomponents.sdk.crypto.MegolmV1BackupKey
import org.matrix.rustcomponents.sdk.crypto.Request
import org.matrix.rustcomponents.sdk.crypto.RequestType
import org.matrix.rustcomponents.sdk.crypto.RoomKeyCounts
import org.matrix.rustcomponents.sdk.crypto.setLogger
import timber.log.Timber
import uniffi.olm.BackupKeys
import uniffi.olm.BackupRecoveryKey
import uniffi.olm.CrossSigningKeyExport
import uniffi.olm.CrossSigningStatus
import uniffi.olm.CryptoStoreException
import uniffi.olm.DecryptionException
import uniffi.olm.DeviceLists
import uniffi.olm.KeyRequestPair
import uniffi.olm.Logger
import uniffi.olm.MegolmV1BackupKey
import uniffi.olm.Request
import uniffi.olm.RequestType
import uniffi.olm.RoomKeyCounts
import uniffi.olm.setLogger
import java.io.File
import java.nio.charset.Charset
import javax.inject.Inject
import uniffi.olm.OlmMachine as InnerMachine
import uniffi.olm.ProgressListener as RustProgressListener
import org.matrix.rustcomponents.sdk.crypto.OlmMachine as InnerMachine
import org.matrix.rustcomponents.sdk.crypto.ProgressListener as RustProgressListener
class CryptoLogger : Logger {
override fun log(logLine: String) {
@ -256,19 +260,33 @@ internal class OlmMachine @Inject constructor(
val devices =
DeviceLists(deviceChanges?.changed.orEmpty(), deviceChanges?.left.orEmpty())
val adapter =
moshi.adapter(ToDeviceSyncResponse::class.java)
val events = adapter.toJson(toDevice ?: ToDeviceSyncResponse())!!
val adapter = MoshiProvider.providesMoshi().adapter(ToDeviceSyncResponse::class.java)
val events = adapter.toJson(toDevice ?: ToDeviceSyncResponse()).also {
Timber.w("## VALR events: $it")
}
// TODO once our sync response type parses the unused fallback key
// field pass in the list of unused fallback keys here
adapter.fromJson(inner.receiveSyncChanges(events, devices, counts, unusedFallbackKeys = null)) ?: ToDeviceSyncResponse()
val receiveSyncChanges = inner.receiveSyncChanges(events, devices, counts, unusedFallbackKeys = null).also {
Timber.w("## VALR $it")
}
val outAdapter = moshi.adapter<List<Event>>(
Types.newParameterizedType(
List::class.java,
Event::class.java,
String::class.java,
Integer::class.java,
Any::class.java,
)
)
outAdapter.fromJson(receiveSyncChanges) ?: emptyList()
}
// We may get cross signing keys over a to-device event, update our listeners.
updateLivePrivateKeys()
return response
return ToDeviceSyncResponse(events = response)
}
suspend fun receiveUnencryptedVerificationEvent(roomId: String, event: Event) = withContext(coroutineDispatchers.io) {
@ -333,8 +351,10 @@ internal class OlmMachine @Inject constructor(
* @return The list of [Request.ToDevice] that need to be sent out.
*/
@Throws(CryptoStoreException::class)
suspend fun shareRoomKey(roomId: String, users: List<String>): List<Request> =
withContext(coroutineDispatchers.io) { inner.shareRoomKey(roomId, users) }
suspend fun shareRoomKey(roomId: String, users: List<String>, settings: EncryptionSettings): List<Request> =
withContext(coroutineDispatchers.io) {
inner.shareRoomKey(roomId, users, settings)
}
/**
* Encrypt the given event with the given type and content for the given room.
@ -450,7 +470,9 @@ internal class OlmMachine @Inject constructor(
*/
@Throws(CryptoStoreException::class)
suspend fun exportKeys(passphrase: String, rounds: Int): ByteArray =
withContext(coroutineDispatchers.io) { inner.exportKeys(passphrase, rounds).toByteArray() }
withContext(coroutineDispatchers.io) {
inner.exportRoomKeys(passphrase, rounds).toByteArray()
}
/**
* Import room keys from the given serialized key export.
@ -472,7 +494,7 @@ internal class OlmMachine @Inject constructor(
val rustListener = CryptoProgressListener(listener)
val result = inner.importKeys(decodedKeys, passphrase, rustListener)
val result = inner.importRoomKeys(decodedKeys, passphrase, rustListener)
ImportRoomKeysResult.fromOlm(result)
}
@ -501,7 +523,7 @@ internal class OlmMachine @Inject constructor(
}
}
inner.importDecryptedKeys(encodedKeys, rustListener).let {
inner.importDecryptedRoomKeys(encodedKeys, rustListener).let {
totalImported += it.imported
accTotal += it.total
details.putAll(it.keys)
@ -812,7 +834,7 @@ internal class OlmMachine @Inject constructor(
.build()
.adapter(MegolmBackupAuthData::class.java)
val serializedAuthData = adapter.toJson(authData)
inner.verifyBackup(serializedAuthData)
inner.verifyBackup(serializedAuthData).trusted
}
}
}

View file

@ -30,9 +30,12 @@ import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
import org.matrix.rustcomponents.sdk.crypto.EncryptionSettings
import org.matrix.rustcomponents.sdk.crypto.EventEncryptionAlgorithm
import org.matrix.rustcomponents.sdk.crypto.HistoryVisibility
import org.matrix.rustcomponents.sdk.crypto.Request
import org.matrix.rustcomponents.sdk.crypto.RequestType
import timber.log.Timber
import uniffi.olm.Request
import uniffi.olm.RequestType
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
@ -83,9 +86,26 @@ internal class PrepareToEncryptUseCase @Inject constructor(
claimMissingKeys(roomMembers)
val keyShareLock = roomKeyShareLocks.getOrPut(roomId) { Mutex() }
var sharedKey = false
cryptoStore.getBlockUnverifiedDevices(roomId)
cryptoStore.shouldShareHistory(roomId)
val settings = EncryptionSettings(
algorithm = EventEncryptionAlgorithm.MEGOLM_V1_AES_SHA2,
onlyAllowTrustedDevices = cryptoStore.getBlockUnverifiedDevices(roomId),
// TODO should take that from m.room.encryption event
rotationPeriod = (7 * 24 * 3600 * 1000).toULong(),
rotationPeriodMsgs = 100UL,
historyVisibility = if (cryptoStore.shouldShareHistory(roomId)) {
HistoryVisibility.SHARED
} else if (cryptoStore.shouldEncryptForInvitedMembers(roomId)) {
HistoryVisibility.INVITED
} else {
HistoryVisibility.JOINED
}
)
keyShareLock.withLock {
coroutineScope {
olmMachine.shareRoomKey(roomId, roomMembers).map {
olmMachine.shareRoomKey(roomId, roomMembers, settings).map {
when (it) {
is Request.ToDevice -> {
sharedKey = true

View file

@ -17,6 +17,7 @@
package org.matrix.android.sdk.internal.crypto
import androidx.lifecycle.LiveData
import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustResult
@ -24,6 +25,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.api.session.crypto.crosssigning.PrivateKeysInfo
import org.matrix.android.sdk.api.session.crypto.crosssigning.UserTrustResult
import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.util.Optional
import javax.inject.Inject
@ -213,4 +215,22 @@ internal class RustCrossSigningService @Inject constructor(
// TODO
// is this needed in rust?
}
override fun checkSelfTrust(myCrossSigningInfo: MXCrossSigningInfo?, myDevices: List<CryptoDeviceInfo>?): UserTrustResult {
// is this needed in rust? should be moved to internal API?
val verified = runBlocking {
val identity = olmMachine.getIdentity(olmMachine.userId()) as? OwnUserIdentity
identity?.verified()
}
return if (verified == null) {
UserTrustResult.CrossSigningNotConfigured(olmMachine.userId())
} else {
UserTrustResult.Success
}
}
override fun checkOtherMSKTrusted(myCrossSigningInfo: MXCrossSigningInfo?, otherInfo: MXCrossSigningInfo?): UserTrustResult {
// is this needed in rust? should be moved to internal API?
TODO()
}
}

View file

@ -683,11 +683,11 @@ internal class RustCryptoService @Inject constructor(
}
override fun enableShareKeyOnInvite(enable: Boolean) {
TODO("Not yet implemented")
TODO("Enable share key on invite not implemented")
}
override fun isShareKeysOnInviteEnabled(): Boolean {
TODO("Not yet implemented")
return false
}
override fun setRoomUnBlockUnverifiedDevices(roomId: String) {
@ -851,7 +851,7 @@ internal class RustCryptoService @Inject constructor(
override suspend fun prepareToEncrypt(roomId: String) = prepareToEncrypt.invoke(roomId, ensureAllMembersAreLoaded = true)
override suspend fun sendSharedHistoryKeys(roomId: String, userId: String, sessionInfoSet: Set<SessionInfo>?) {
TODO("Not yet implemented")
// TODO("Not yet implemented")
}
/* ==========================================================================================

View file

@ -26,9 +26,9 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import org.matrix.android.sdk.internal.crypto.verification.prepareMethods
import uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine
import uniffi.olm.SignatureException
import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import org.matrix.rustcomponents.sdk.crypto.OlmMachine
import org.matrix.rustcomponents.sdk.crypto.SignatureException
/**
* A sealed class representing user identities.

View file

@ -16,7 +16,7 @@
package org.matrix.android.sdk.api.session.crypto.keysbackup
import uniffi.olm.BackupRecoveryKey as InnerBackupRecoveryKey
import org.matrix.rustcomponents.sdk.crypto.BackupRecoveryKey as InnerBackupRecoveryKey
class BackupRecoveryKey internal constructor(internal val inner: InnerBackupRecoveryKey) : IBackupRecoveryKey {

View file

@ -61,9 +61,9 @@ import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.olm.OlmException
import org.matrix.rustcomponents.sdk.crypto.Request
import org.matrix.rustcomponents.sdk.crypto.RequestType
import timber.log.Timber
import uniffi.olm.Request
import uniffi.olm.RequestType
import java.security.InvalidParameterException
import javax.inject.Inject
import kotlin.random.Random

View file

@ -29,9 +29,9 @@ import org.matrix.android.sdk.internal.crypto.ComputeShieldForGroupUseCase
import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.rustcomponents.sdk.crypto.Request
import org.matrix.rustcomponents.sdk.crypto.RequestType
import timber.log.Timber
import uniffi.olm.Request
import uniffi.olm.RequestType
import javax.inject.Inject
private val loggerTag = LoggerTag("OutgoingRequestsProcessor", LoggerTag.CRYPTO)

View file

@ -62,11 +62,11 @@ import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.network.DEFAULT_REQUEST_RETRY_COUNT
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
import org.matrix.android.sdk.internal.session.room.send.SendResponse
import org.matrix.rustcomponents.sdk.crypto.OutgoingVerificationRequest
import org.matrix.rustcomponents.sdk.crypto.Request
import org.matrix.rustcomponents.sdk.crypto.SignatureUploadRequest
import org.matrix.rustcomponents.sdk.crypto.UploadSigningKeysRequest
import timber.log.Timber
import uniffi.olm.OutgoingVerificationRequest
import uniffi.olm.Request
import uniffi.olm.SignatureUploadRequest
import uniffi.olm.UploadSigningKeysRequest
import javax.inject.Inject
internal class RequestSender @Inject constructor(

View file

@ -74,6 +74,8 @@ internal class RustVerificationService @Inject constructor(
private val olmMachine: OlmMachine,
private val verificationListenersHolder: VerificationListenersHolder) : VerificationService {
override fun requestEventFlow() = verificationListenersHolder.eventFlow
/**
*
* All verification related events should be forwarded through this method to
@ -103,7 +105,7 @@ internal class RustVerificationService @Inject constructor(
}
}
private fun onRoomMessage(event: Event) {
private suspend fun onRoomMessage(event: Event) {
val messageContent = event.getClearContent()?.toModel<MessageContent>() ?: return
if (messageContent.msgType == MessageType.MSGTYPE_VERIFICATION_REQUEST) {
onRequest(event, fromRoomMessage = true)
@ -111,7 +113,7 @@ internal class RustVerificationService @Inject constructor(
}
/** Dispatch updates after a verification event has been received */
private fun onUpdate(event: Event) {
private suspend fun onUpdate(event: Event) {
val sender = event.senderId ?: return
val flowId = getFlowId(event) ?: return
@ -150,7 +152,7 @@ internal class RustVerificationService @Inject constructor(
}
/** Check if the request event created a nev verification request object and dispatch that it dis so */
private fun onRequest(event: Event, fromRoomMessage: Boolean) {
private suspend fun onRequest(event: Event, fromRoomMessage: Boolean) {
val flowId = if (fromRoomMessage) {
event.eventId
} else {
@ -162,26 +164,26 @@ internal class RustVerificationService @Inject constructor(
verificationListenersHolder.dispatchRequestAdded(request)
}
override fun addListener(listener: VerificationService.Listener) {
verificationListenersHolder.addListener(listener)
}
override fun removeListener(listener: VerificationService.Listener) {
verificationListenersHolder.removeListener(listener)
}
// override fun addListener(listener: VerificationService.Listener) {
// verificationListenersHolder.addListener(listener)
// }
//
// override fun removeListener(listener: VerificationService.Listener) {
// verificationListenersHolder.removeListener(listener)
// }
override suspend fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
olmMachine.getDevice(userId, deviceID)?.markAsTrusted()
}
override fun getExistingTransaction(
override suspend fun getExistingTransaction(
otherUserId: String,
tid: String,
): VerificationTransaction? {
return olmMachine.getVerification(otherUserId, tid)
}
override fun getExistingVerificationRequests(
override suspend fun getExistingVerificationRequests(
otherUserId: String
): List<PendingVerificationRequest> {
return olmMachine.getVerificationRequests(otherUserId).map {
@ -189,7 +191,7 @@ internal class RustVerificationService @Inject constructor(
}
}
override fun getExistingVerificationRequest(
override suspend fun getExistingVerificationRequest(
otherUserId: String,
tid: String?
): PendingVerificationRequest? {
@ -200,9 +202,9 @@ internal class RustVerificationService @Inject constructor(
}
}
override fun getExistingVerificationRequestInRoom(
override suspend fun getExistingVerificationRequestInRoom(
roomId: String,
tid: String?
tid: String
): PendingVerificationRequest? {
// This is only used in `RoomDetailViewModel` to resume the verification.
//
@ -251,13 +253,23 @@ internal class RustVerificationService @Inject constructor(
override suspend fun requestDeviceVerification(methods: List<VerificationMethod>,
otherUserId: String,
otherDeviceId: String?): PendingVerificationRequest? {
otherDeviceId: String?): PendingVerificationRequest {
// how do we send request to several devices in rust?
if (otherDeviceId == null) return null
olmMachine.ensureUsersKeys(listOf(otherUserId))
val request = if (otherDeviceId == null) {
// Todo
when (val identity = olmMachine.getIdentity(otherUserId)) {
is OwnUserIdentity -> identity.requestVerification(methods)
is UserIdentity -> {
throw IllegalArgumentException("to_device request only allowed for own user $otherUserId")
}
null -> throw IllegalArgumentException("Unknown identity")
}
} else {
val otherDevice = olmMachine.getDevice(otherUserId, otherDeviceId)
val verificationRequest = otherDevice?.requestVerification(methods)
return verificationRequest?.toPendingVerificationRequest()
otherDevice?.requestVerification(methods) ?: throw IllegalArgumentException("Unknown device $otherDeviceId")
}
return request.toPendingVerificationRequest()
}
override suspend fun readyPendingVerification(
@ -269,29 +281,15 @@ internal class RustVerificationService @Inject constructor(
return if (request != null) {
request.acceptWithMethods(methods)
if (request.isReady()) {
val qrcode = request.startQrVerification()
if (qrcode != null) {
verificationListenersHolder.dispatchTxAdded(qrcode)
}
true
} else {
false
}
request.isReady()
} else {
false
}
}
override suspend fun beginKeyVerification(
method: VerificationMethod,
otherUserId: String,
transactionId: String
): String? {
override suspend fun startKeyVerification(method: VerificationMethod, otherUserId: String, requestId: String): String? {
return if (method == VerificationMethod.SAS) {
val request = olmMachine.getVerificationRequest(otherUserId, transactionId)
val request = olmMachine.getVerificationRequest(otherUserId, requestId)
val sas = request?.startSasVerification()
@ -306,7 +304,13 @@ internal class RustVerificationService @Inject constructor(
}
}
override fun onPotentiallyInterestingEventRoomFailToDecrypt(event: Event) {
override suspend fun reciprocateQRVerification(otherUserId: String, requestId: String, scannedData: String): String? {
val matchingRequest = olmMachine.getVerificationRequest(otherUserId, requestId)
matchingRequest?.scanQrCode(scannedData)
return matchingRequest?.startQrVerification()?.transactionId
}
override suspend fun onPotentiallyInterestingEventRoomFailToDecrypt(event: Event) {
// not available in rust
}
@ -331,7 +335,6 @@ internal class RustVerificationService @Inject constructor(
// }
override suspend fun cancelVerificationRequest(request: PendingVerificationRequest) {
request.transactionId ?: return
cancelVerificationRequest(request.otherUserId, request.transactionId)
}

View file

@ -25,14 +25,16 @@ import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
import org.matrix.android.sdk.api.session.crypto.verification.SasTransactionState
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import uniffi.olm.CryptoStoreException
import uniffi.olm.Sas
import uniffi.olm.Verification
import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import org.matrix.rustcomponents.sdk.crypto.Sas
import org.matrix.rustcomponents.sdk.crypto.SasListener
import org.matrix.rustcomponents.sdk.crypto.SasState
/** Class representing a short auth string verification flow */
internal class SasVerification @AssistedInject constructor(
@ -40,60 +42,67 @@ internal class SasVerification @AssistedInject constructor(
private val olmMachine: OlmMachine,
private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val verificationListenersHolder: VerificationListenersHolder
private val verificationListenersHolder: VerificationListenersHolder,
) :
SasVerificationTransaction {
SasVerificationTransaction, SasListener {
init {
inner.setChangesListener(this)
}
var innerState: SasState = SasState.Started
@AssistedFactory
interface Factory {
fun create(inner: Sas): SasVerification
}
private val innerMachine = olmMachine.inner()
private fun dispatchTxUpdated() {
refreshData()
verificationListenersHolder.dispatchTxUpdated(this)
}
/** The user ID of the other user that is participating in this verification flow */
override val otherUserId: String = inner.otherUserId
override val otherUserId: String = inner.otherUserId()
/** Get the device id of the other user's device participating in this verification flow */
override var otherDeviceId: String?
get() = inner.otherDeviceId
@Suppress("UNUSED_PARAMETER")
set(value) {
}
override val otherDeviceId: String
get() = inner.otherDeviceId()
/** Did the other side initiate this verification flow */
override val isIncoming: Boolean
get() = !inner.weStarted
get() = !inner.weStarted()
override var state: VerificationTxState
get() {
refreshData()
val cancelInfo = inner.cancelInfo
private var decimals: List<Int>? = null
private var emojis: List<Int>? = null
return when {
cancelInfo != null -> {
val cancelCode = safeValueOf(cancelInfo.cancelCode)
VerificationTxState.Cancelled(cancelCode, cancelInfo.cancelledByUs)
override fun state(): SasTransactionState {
return when (val state = innerState) {
SasState.Started -> SasTransactionState.SasStarted
SasState.Accepted -> SasTransactionState.SasAccepted
is SasState.KeysExchanged -> {
this.decimals = state.decimals
this.emojis = state.emojis
SasTransactionState.SasShortCodeReady
}
inner.isDone -> VerificationTxState.Verified
inner.haveWeConfirmed -> VerificationTxState.SasMacSent
inner.canBePresented -> VerificationTxState.SasShortCodeReady
inner.hasBeenAccepted -> VerificationTxState.SasAccepted
else -> VerificationTxState.SasStarted
SasState.Confirmed -> SasTransactionState.SasMacSent
SasState.Done -> SasTransactionState.Done(false)
is SasState.Cancelled -> SasTransactionState.Cancelled(safeValueOf(state.cancelInfo.cancelCode), state.cancelInfo.cancelledByUs)
}
}
@Suppress("UNUSED_PARAMETER")
set(v) {
// refreshData()
// val cancelInfo = inner.cancelInfo
//
// return when {
// cancelInfo != null -> {
// val cancelCode = safeValueOf(cancelInfo.cancelCode)
// SasTransactionState.Cancelled(cancelCode, cancelInfo.cancelledByUs)
// }
// inner.isDone -> SasTransactionState.Done(true)
// inner.haveWeConfirmed -> SasTransactionState.SasAccepted
// inner.canBePresented -> SasTransactionState.SasShortCodeReady
// inner.hasBeenAccepted -> SasTransactionState.SasAccepted
// else -> SasTransactionState.SasStarted
// }
}
/** Get the unique id of this verification */
override val transactionId: String
get() = inner.flowId
get() = inner.flowId()
/** Cancel the verification flow
*
@ -136,13 +145,15 @@ internal class SasVerification @AssistedInject constructor(
cancelHelper(CancelCode.MismatchedSas)
}
override val method: VerificationMethod
get() = VerificationMethod.QR_CODE_SCAN
/** Is this verification happening over to-device messages */
override fun isToDeviceTransport(): Boolean = inner.roomId == null
override fun isToDeviceTransport(): Boolean = inner.roomId() == null
/** Does the verification flow support showing emojis as the short auth string */
override fun supportsEmoji(): Boolean {
refreshData()
return inner.supportsEmoji
return inner.supportsEmoji()
}
/** Confirm that the short authentication code matches on both sides
@ -177,8 +188,6 @@ internal class SasVerification @AssistedInject constructor(
* in a presentable state.
*/
override fun getDecimalCodeRepresentation(): String {
val decimals = innerMachine.getDecimals(inner.otherUserId, inner.flowId)
return decimals?.joinToString(" ") ?: ""
}
@ -189,14 +198,13 @@ internal class SasVerification @AssistedInject constructor(
* state.
*/
override fun getEmojiCodeRepresentation(): List<EmojiRepresentation> {
val emojiIndex = innerMachine.getEmojiIndex(inner.otherUserId, inner.flowId)
return emojiIndex?.map { getEmojiForCode(it) } ?: listOf()
return emojis?.map { getEmojiForCode(it) } ?: listOf()
}
internal suspend fun accept() {
val request = innerMachine.acceptSasVerification(inner.otherUserId, inner.flowId) ?: return
dispatchTxUpdated()
val request = inner.accept() ?: return Unit.also {
// TODO should throw here?
}
try {
sender.sendVerificationRequest(request)
} catch (failure: Throwable) {
@ -207,10 +215,8 @@ internal class SasVerification @AssistedInject constructor(
@Throws(CryptoStoreException::class)
private suspend fun confirm() {
val result = withContext(coroutineDispatchers.io) {
innerMachine.confirmVerification(inner.otherUserId, inner.flowId)
inner.confirm()
} ?: return
dispatchTxUpdated()
try {
for (verificationRequest in result.requests) {
sender.sendVerificationRequest(verificationRequest)
@ -225,24 +231,28 @@ internal class SasVerification @AssistedInject constructor(
}
private suspend fun cancelHelper(code: CancelCode) = withContext(NonCancellable) {
val request = innerMachine.cancelVerification(inner.otherUserId, inner.flowId, code.value) ?: return@withContext
dispatchTxUpdated()
val request = inner.cancel(code.value) ?: return@withContext
tryOrNull("Fail to send cancel request") {
sender.sendVerificationRequest(request, retryCount = Int.MAX_VALUE)
}
}
/** Fetch fresh data from the Rust side for our verification flow */
private fun refreshData() {
when (val verification = innerMachine.getVerification(inner.otherUserId, inner.flowId)) {
is Verification.SasV1 -> {
inner = verification.sas
}
else -> {
}
}
// private fun refreshData() {
// when (val verification = innerMachine.getVerification(inner.otherUserId, inner.flowId)) {
// is Verification.SasV1 -> {
// inner = verification.sas
// }
// else -> {
// }
// }
//
// return
// }
return
override fun onChange(state: SasState) {
innerState = state
verificationListenersHolder.dispatchTxUpdated(this)
}
override fun toString(): String {
@ -250,7 +260,7 @@ internal class SasVerification @AssistedInject constructor(
"otherUserId='$otherUserId', " +
"otherDeviceId=$otherDeviceId, " +
"isIncoming=$isIncoming, " +
"state=$state, " +
"state=${state()}, " +
"transactionId='$transactionId')"
}
}

View file

@ -24,18 +24,21 @@ import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.api.util.toBase64NoPadding
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
import org.matrix.android.sdk.internal.util.time.Clock
import uniffi.olm.VerificationRequest as InnerVerificationRequest
import org.matrix.rustcomponents.sdk.crypto.QrCode
import org.matrix.rustcomponents.sdk.crypto.VerificationRequest as InnerVerificationRequest
/** A verification request object
*
@ -74,12 +77,12 @@ internal class VerificationRequest @AssistedInject constructor(
* event that initiated the flow.
*/
internal fun flowId(): String {
return innerVerificationRequest.flowId
return innerVerificationRequest.flowId()
}
/** The user ID of the other user that is participating in this verification flow */
internal fun otherUser(): String {
return innerVerificationRequest.otherUserId
return innerVerificationRequest.otherUserId()
}
/** The device ID of the other user's device that is participating in this verification flow
@ -89,12 +92,12 @@ internal class VerificationRequest @AssistedInject constructor(
* */
internal fun otherDeviceId(): String? {
refreshData()
return innerVerificationRequest.otherDeviceId
return innerVerificationRequest.otherDeviceId()
}
/** Did we initiate this verification flow */
internal fun weStarted(): Boolean {
return innerVerificationRequest.weStarted
return innerVerificationRequest.weStarted()
}
/** Get the id of the room where this verification is happening
@ -102,7 +105,7 @@ internal class VerificationRequest @AssistedInject constructor(
* Will be null if the verification is not happening inside a room.
*/
internal fun roomId(): String? {
return innerVerificationRequest.roomId
return innerVerificationRequest.roomId()
}
/** Did the non-initiating side respond with a m.key.verification.read event
@ -113,13 +116,13 @@ internal class VerificationRequest @AssistedInject constructor(
*/
internal fun isReady(): Boolean {
refreshData()
return innerVerificationRequest.isReady
return innerVerificationRequest.isReady()
}
/** Did we advertise that we're able to scan QR codes */
internal fun canScanQrCodes(): Boolean {
refreshData()
return innerVerificationRequest.ourMethods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN) ?: false
return innerVerificationRequest.ourSupportedMethods()?.contains(VERIFICATION_METHOD_QR_CODE_SCAN) ?: false
}
/** Accept the verification request advertising the given methods as supported
@ -138,20 +141,28 @@ internal class VerificationRequest @AssistedInject constructor(
suspend fun acceptWithMethods(methods: List<VerificationMethod>) {
val stringMethods = prepareMethods(methods)
val request = innerOlmMachine.acceptVerificationRequest(
innerVerificationRequest.otherUserId,
innerVerificationRequest.flowId,
stringMethods
) ?: return
val request = innerVerificationRequest.accept(stringMethods)
?: return // should throw here?
// val request = innerOlmMachine.acceptVerificationRequest(
// innerVerificationRequest.otherUserId(),
// innerVerificationRequest.flowId,
// stringMethods
// ) ?: return
try {
dispatchRequestUpdated()
requestSender.sendVerificationRequest(request)
if (innerVerificationRequest.isReady()) {
activeQRCode = innerVerificationRequest.startQrVerification()
}
} catch (failure: Throwable) {
cancel(CancelCode.UserError)
}
}
var activeQRCode: QrCode? = null
/** Transition from a ready verification request into emoji verification
*
* This method will move the verification forward into emoji verification,
@ -167,7 +178,10 @@ internal class VerificationRequest @AssistedInject constructor(
*/
internal suspend fun startSasVerification(): SasVerification? {
return withContext(coroutineDispatchers.io) {
val result = innerOlmMachine.startSasVerification(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId) ?: return@withContext null
val result = innerVerificationRequest.startSasVerification()
?: return@withContext null
// sasStartResult.request
// val result = innerOlmMachine.startSasVerification(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId) ?: return@withContext null
try {
requestSender.sendVerificationRequest(result.request)
sasVerificationFactory.create(result.sas)
@ -195,7 +209,8 @@ internal class VerificationRequest @AssistedInject constructor(
// TODO again, what's the deal with ISO_8859_1?
val byteArray = data.toByteArray(Charsets.ISO_8859_1)
val encodedData = byteArray.toBase64NoPadding()
val result = innerOlmMachine.scanQrCode(otherUser(), flowId(), encodedData) ?: return null
// val result = innerOlmMachine.scanQrCode(otherUser(), flowId(), encodedData) ?: return null
val result = innerVerificationRequest.scanQrCode(encodedData) ?: return null
try {
requestSender.sendVerificationRequest(result.request)
} catch (failure: Throwable) {
@ -223,9 +238,12 @@ internal class VerificationRequest @AssistedInject constructor(
* QR code verification, or null if we can't yet transition into QR code verification.
*/
internal fun startQrVerification(): QrCodeVerification? {
val qrcode = innerOlmMachine.startQrVerification(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId)
return if (qrcode != null) {
qrCodeVerificationFactory.create(this, qrcode)
activeQRCode = innerVerificationRequest.startQrVerification()
// val qrcode = innerOlmMachine.startQrVerification(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId)
return if (activeQRCode != null) {
TODO("Is this reciprocate or just doing nothing?")
// activeQRCode.
// qrCodeVerificationFactory.create(this, qrcode)
} else {
null
}
@ -242,11 +260,13 @@ internal class VerificationRequest @AssistedInject constructor(
* The method turns into a noop, if the verification flow has already been cancelled.
*/
internal suspend fun cancel(cancelCode: CancelCode = CancelCode.User) = withContext(NonCancellable) {
val request = innerOlmMachine.cancelVerification(
innerVerificationRequest.otherUserId,
innerVerificationRequest.flowId,
cancelCode.value
) ?: return@withContext
// TODO damir how to add the code?
val request = innerVerificationRequest.cancel() ?: return@withContext
// val request = innerOlmMachine.cancelVerification(
// innerVerificationRequest.otherUserId,
// innerVerificationRequest.flowId,
// cancelCode.value
// ) ?: return@withContext
dispatchRequestUpdated()
tryOrNull("Fail to send cancel request") {
requestSender.sendVerificationRequest(request, retryCount = Int.MAX_VALUE)
@ -255,13 +275,24 @@ internal class VerificationRequest @AssistedInject constructor(
/** Fetch fresh data from the Rust side for our verification flow */
private fun refreshData() {
val request = innerOlmMachine.getVerificationRequest(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId)
val request = innerOlmMachine.getVerificationRequest(innerVerificationRequest.otherUserId(), innerVerificationRequest.flowId())
if (request != null) {
innerVerificationRequest = request
}
}
private fun state(): EVerificationState {
when {
innerVerificationRequest.weStarted() -> EVerificationState.WaitingForReady
innerVerificationRequest.isDone() -> EVerificationState.Done
innerVerificationRequest.isCancelled() -> EVerificationState.Cancelled
innerVerificationRequest.isReady() -> EVerificationState.Ready
innerVerificationRequest.isPassive() -> EVerificationState.HandledByOtherSession
}
return EVerificationState.Requested
}
/** Convert the VerificationRequest into a PendingVerificationRequest
*
* The public interface of the VerificationService dispatches the data class
@ -273,7 +304,7 @@ internal class VerificationRequest @AssistedInject constructor(
*/
internal fun toPendingVerificationRequest(): PendingVerificationRequest {
refreshData()
val cancelInfo = innerVerificationRequest.cancelInfo
val cancelInfo = innerVerificationRequest.cancelInfo()
val cancelCode =
if (cancelInfo != null) {
safeValueOf(cancelInfo.cancelCode)
@ -281,72 +312,79 @@ internal class VerificationRequest @AssistedInject constructor(
null
}
val ourMethods = innerVerificationRequest.ourMethods
val theirMethods = innerVerificationRequest.theirMethods
val otherDeviceId = innerVerificationRequest.otherDeviceId
val ourMethods = innerVerificationRequest.ourSupportedMethods()
val theirMethods = innerVerificationRequest.theirSupportedMethods()
val otherDeviceId = innerVerificationRequest.otherDeviceId()
var requestInfo: ValidVerificationInfoRequest? = null
var readyInfo: ValidVerificationInfoReady? = null
// var requestInfo: ValidVerificationInfoRequest? = null
// var readyInfo: ValidVerificationInfoReady? = null
//
// if (innerVerificationRequest.weStarted && ourMethods != null) {
// requestInfo =
// ValidVerificationInfoRequest(
// transactionId = innerVerificationRequest.flowId,
// fromDevice = innerOlmMachine.deviceId(),
// methods = ourMethods,
// timestamp = null,
// )
// } else if (!innerVerificationRequest.weStarted && ourMethods != null) {
// readyInfo =
// ValidVerificationInfoReady(
// transactionId = innerVerificationRequest.flowId,
// fromDevice = innerOlmMachine.deviceId(),
// methods = ourMethods,
// )
// }
//
// if (innerVerificationRequest.weStarted && theirMethods != null && otherDeviceId != null) {
// readyInfo =
// ValidVerificationInfoReady(
// transactionId = innerVerificationRequest.flowId,
// fromDevice = otherDeviceId,
// methods = theirMethods,
// )
// } else if (!innerVerificationRequest.weStarted && theirMethods != null && otherDeviceId != null) {
// requestInfo =
// ValidVerificationInfoRequest(
// transactionId = innerVerificationRequest.flowId,
// fromDevice = otherDeviceId,
// methods = theirMethods,
// timestamp = clock.epochMillis(),
// )
// }
if (innerVerificationRequest.weStarted && ourMethods != null) {
requestInfo =
ValidVerificationInfoRequest(
transactionId = innerVerificationRequest.flowId,
fromDevice = innerOlmMachine.deviceId(),
methods = ourMethods,
timestamp = null,
)
} else if (!innerVerificationRequest.weStarted && ourMethods != null) {
readyInfo =
ValidVerificationInfoReady(
transactionId = innerVerificationRequest.flowId,
fromDevice = innerOlmMachine.deviceId(),
methods = ourMethods,
)
}
if (innerVerificationRequest.weStarted && theirMethods != null && otherDeviceId != null) {
readyInfo =
ValidVerificationInfoReady(
transactionId = innerVerificationRequest.flowId,
fromDevice = otherDeviceId,
methods = theirMethods,
)
} else if (!innerVerificationRequest.weStarted && theirMethods != null && otherDeviceId != null) {
requestInfo =
ValidVerificationInfoRequest(
transactionId = innerVerificationRequest.flowId,
fromDevice = otherDeviceId,
methods = theirMethods,
timestamp = clock.epochMillis(),
)
}
innerVerificationRequest.startQrVerification()
return PendingVerificationRequest(
// Creation time
ageLocalTs = clock.epochMillis(),
state = state(),
// Who initiated the request
isIncoming = !innerVerificationRequest.weStarted,
isIncoming = !innerVerificationRequest.weStarted(),
// Local echo id, what to do here?
localId = innerVerificationRequest.flowId,
otherDeviceId = innerVerificationRequest.otherDeviceId(),
// other user
otherUserId = innerVerificationRequest.otherUserId,
otherUserId = innerVerificationRequest.otherUserId(),
// room id
roomId = innerVerificationRequest.roomId,
roomId = innerVerificationRequest.roomId(),
// transaction id
transactionId = innerVerificationRequest.flowId,
// val requestInfo: ValidVerificationInfoRequest? = null,
requestInfo = requestInfo,
// val readyInfo: ValidVerificationInfoReady? = null,
readyInfo = readyInfo,
transactionId = innerVerificationRequest.flowId(),
// cancel code if there is one
cancelConclusion = cancelCode,
// are we done/successful
isSuccessful = innerVerificationRequest.isDone,
isFinished = innerVerificationRequest.isDone() || innerVerificationRequest.isCancelled(),
// did another device answer the request
handledByOtherSession = innerVerificationRequest.isPassive,
handledByOtherSession = innerVerificationRequest.isPassive(),
// devices that should receive the events we send out
targetDevices = null
targetDevices = otherDeviceId?.let { listOf(it) },
// TODO qr,
qrCodeText = activeQRCode?.generateQrCode(),
isSasSupported = ourMethods.canSas() && theirMethods.canSas(),
weShouldDisplayQRCode = theirMethods.canScanQR() && ourMethods.canShowQR(),
weShouldShowScanOption = ourMethods.canScanQR() && theirMethods.canShowQR()
)
}
private fun List<String>?.canSas() = orEmpty().contains(VERIFICATION_METHOD_SAS)
private fun List<String>?.canShowQR() = orEmpty().contains(VERIFICATION_METHOD_RECIPROCATE) && orEmpty().contains(VERIFICATION_METHOD_QR_CODE_SHOW)
private fun List<String>?.canScanQR() = orEmpty().contains(VERIFICATION_METHOD_RECIPROCATE) && orEmpty().contains(VERIFICATION_METHOD_QR_CODE_SCAN)
}

View file

@ -21,7 +21,7 @@ import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
import javax.inject.Inject
import javax.inject.Provider
import uniffi.olm.OlmMachine as InnerOlmMachine
import org.matrix.rustcomponents.sdk.crypto.OlmMachine as InnerOlmMachine
internal class VerificationsProvider @Inject constructor(
private val olmMachine: Provider<OlmMachine>,
@ -49,26 +49,36 @@ internal class VerificationsProvider @Inject constructor(
* verification.
*/
fun getVerification(userId: String, flowId: String): VerificationTransaction? {
return when (val verification = innerMachine.getVerification(userId, flowId)) {
is uniffi.olm.Verification.QrCodeV1 -> {
val request = getVerificationRequest(userId, flowId) ?: return null
qrVerificationFactory.create(request, verification.qrcode)
}
is uniffi.olm.Verification.SasV1 -> {
sasVerificationFactory.create(verification.sas)
}
null -> {
// This branch exists because scanning a QR code is tied to the QrCodeVerification,
// i.e. instead of branching into a scanned QR code verification from the verification request,
// like it's done for SAS verifications, the public API expects us to create an empty dummy
// QrCodeVerification object that gets populated once a QR code is scanned.
val request = getVerificationRequest(userId, flowId) ?: return null
if (request.canScanQrCodes()) {
qrVerificationFactory.create(request, null)
val verification = innerMachine.getVerification(userId, flowId)
return if (verification?.asSas() != null) {
sasVerificationFactory.create(verification.asSas()!!)
} else if (verification?.asQr() != null) {
// qrVerificationFactory.create(verification, verification.asQr()!!)
// TODO
null
} else {
null
}
}
}
// return when (val verification = innerMachine.getVerification(userId, flowId)) {
// is org.matrix.rustcomponents.sdk.crypto.Verification. -> {
// val request = getVerificationRequest(userId, flowId) ?: return null
// qrVerificationFactory.create(request, verification.qrcode)
// }
// is org.matrix.rustcomponents.sdk.crypto.Verification.SasV1 -> {
// sasVerificationFactory.create(verification.sas)
// }
// null -> {
// // This branch exists because scanning a QR code is tied to the QrCodeVerification,
// // i.e. instead of branching into a scanned QR code verification from the verification request,
// // like it's done for SAS verifications, the public API expects us to create an empty dummy
// // QrCodeVerification object that gets populated once a QR code is scanned.
// val request = getVerificationRequest(userId, flowId) ?: return null
// if (request.canScanQrCodes()) {
// qrVerificationFactory.create(request, null)
// } else {
// null
// }
// }
// }
}
}

View file

@ -24,22 +24,21 @@ import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.QRCodeVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.util.fromBase64
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.VerificationListenersHolder
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import uniffi.olm.CryptoStoreException
import uniffi.olm.QrCode
import uniffi.olm.Verification
import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import org.matrix.rustcomponents.sdk.crypto.QrCode
/** Class representing a QR code based verification flow */
internal class QrCodeVerification @AssistedInject constructor(
@Assisted private var request: VerificationRequest,
@Assisted private var inner: QrCode?,
@Assisted private var inner: QrCode,
private val olmMachine: OlmMachine,
private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
@ -48,9 +47,12 @@ internal class QrCodeVerification @AssistedInject constructor(
@AssistedFactory
interface Factory {
fun create(request: VerificationRequest, inner: QrCode?): QrCodeVerification
fun create(request: VerificationRequest, inner: QrCode): QrCodeVerification
}
override val method: VerificationMethod
get() = VerificationMethod.QR_CODE_SCAN
private val innerMachine = olmMachine.inner()
private fun dispatchTxUpdated() {
@ -72,17 +74,17 @@ internal class QrCodeVerification @AssistedInject constructor(
*/
override val qrCodeText: String?
get() {
val data = inner?.let { innerMachine.generateQrCode(it.otherUserId, it.flowId) }
val data = inner.generateQrCode()
// TODO Why are we encoding this to ISO_8859_1? If we're going to encode, why not base64?
return data?.fromBase64()?.toString(Charsets.ISO_8859_1)
}
/** Pass the data from a scanned QR code into the QR code verification object */
override suspend fun userHasScannedOtherQrCode(otherQrCodeText: String) {
request.scanQrCode(otherQrCodeText)
dispatchTxUpdated()
}
// override suspend fun userHasScannedOtherQrCode(otherQrCodeText: String) {
// request.scanQrCode(otherQrCodeText)
// dispatchTxUpdated()
// }
/** Confirm that the other side has indeed scanned the QR code we presented */
override suspend fun otherUserScannedMyQrCode() {
@ -95,32 +97,35 @@ internal class QrCodeVerification @AssistedInject constructor(
cancelHelper(CancelCode.MismatchedKeys)
}
override var state: VerificationTxState
get() {
refreshData()
val inner = inner
val cancelInfo = inner?.cancelInfo
return if (inner != null) {
when {
cancelInfo != null -> {
val cancelCode = safeValueOf(cancelInfo.cancelCode)
val byMe = cancelInfo.cancelledByUs
VerificationTxState.Cancelled(cancelCode, byMe)
}
inner.isDone -> VerificationTxState.Verified
inner.reciprocated -> VerificationTxState.Started
inner.hasBeenConfirmed -> VerificationTxState.WaitingOtherReciprocateConfirm
inner.otherSideScanned -> VerificationTxState.QrScannedByOther
else -> VerificationTxState.None
}
} else {
VerificationTxState.None
}
}
@Suppress("UNUSED_PARAMETER")
set(value) {
override fun state(): QRCodeVerificationState {
return QRCodeVerificationState.Reciprocated
}
// override var state: VerificationTxState
// get() {
// refreshData()
// val inner = inner
// val cancelInfo = inner?.cancelInfo
//
// return if (inner != null) {
// when {
// cancelInfo != null -> {
// val cancelCode = safeValueOf(cancelInfo.cancelCode)
// val byMe = cancelInfo.cancelledByUs
// VerificationTxState.Cancelled(cancelCode, byMe)
// }
// inner.isDone -> VerificationTxState.Verified
// inner.reciprocated -> VerificationTxState.Started
// inner.hasBeenConfirmed -> VerificationTxState.WaitingOtherReciprocateConfirm
// inner.otherSideScanned -> VerificationTxState.QrScannedByOther
// else -> VerificationTxState.None
// }
// } else {
// VerificationTxState.None
// }
// }
// @Suppress("UNUSED_PARAMETER")
// set(value) {
// }
/** Get the unique id of this verification */
override val transactionId: String
@ -185,7 +190,7 @@ internal class QrCodeVerification @AssistedInject constructor(
@Throws(CryptoStoreException::class)
private suspend fun confirm() {
val result = withContext(coroutineDispatchers.io) {
innerMachine.confirmVerification(request.otherUser(), request.flowId())
inner.confirm()
} ?: return
dispatchTxUpdated()
try {
@ -202,7 +207,7 @@ internal class QrCodeVerification @AssistedInject constructor(
}
private suspend fun cancelHelper(code: CancelCode) = withContext(NonCancellable) {
val request = innerMachine.cancelVerification(request.otherUser(), request.flowId(), code.value) ?: return@withContext
val request = inner.cancel(code.value) ?: return@withContext
dispatchTxUpdated()
tryOrNull("Fail to send cancel verification request") {
sender.sendVerificationRequest(request, retryCount = Int.MAX_VALUE)
@ -211,13 +216,17 @@ internal class QrCodeVerification @AssistedInject constructor(
/** Fetch fresh data from the Rust side for our verification flow */
private fun refreshData() {
when (val verification = innerMachine.getVerification(request.otherUser(), request.flowId())) {
is Verification.QrCodeV1 -> {
inner = verification.qrcode
}
else -> {
}
innerMachine.getVerification(request.otherUser(), request.flowId())
?.asQr()?.let {
inner = it
}
// when (val verification = innerMachine.getVerification(request.otherUser(), request.flowId())) {
// is Verification.QrCodeV1 -> {
// inner = verification.qrcode
// }
// else -> {
// }
// }
return
}
@ -225,7 +234,7 @@ internal class QrCodeVerification @AssistedInject constructor(
override fun toString(): String {
return "QrCodeVerification(" +
"qrCodeText=$qrCodeText, " +
"state=$state, " +
"state=${state()}, " +
"transactionId='$transactionId', " +
"otherUserId='$otherUserId', " +
"otherDeviceId=$otherDeviceId, " +

View file

@ -0,0 +1,54 @@
/*
* 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.internal.crypto
import org.amshove.kluent.shouldNotContain
import org.junit.Test
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.sync.model.ToDeviceSyncResponse
import org.matrix.android.sdk.internal.di.MoshiProvider
class MoshiNumbersAsInt {
@Test
fun numberShouldNotPutAllAsFloat() {
val event = Event(
type = "m.room.encrypted",
eventId = null,
content = mapOf(
"algorithm" to "m.olm.v1.curve25519-aes-sha2",
"ciphertext" to mapOf(
"cfA3dINwtmMW0DbJmnT6NiGAbOSa299Hxs6KxHgbDBw" to mapOf(
"body" to "Awogc5...",
"type" to 1
),
),
),
prevContent = null,
originServerTs = null,
senderId = "@web:localhost:8481"
)
val toDeviceSyncResponse = ToDeviceSyncResponse(listOf(event))
val adapter = MoshiProvider.providesMoshi().adapter(ToDeviceSyncResponse::class.java)
val jsonString = adapter.toJson(toDeviceSyncResponse)
jsonString shouldNotContain "1.0"
}
}