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/src/main/jniLibs/
matrix-sdk-android/libs/crypto-android-release.aar 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.coroutinesCore
implementation libs.jetbrains.coroutinesAndroid 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(name: 'crypto-android-release', ext: 'aar')
implementation 'net.java.dev.jna:jna:5.10.0@aar' implementation 'net.java.dev.jna:jna:5.10.0@aar'
@ -236,7 +234,8 @@ dependencies {
implementation libs.google.phonenumber 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 testImplementation libs.tests.junit
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 // 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 package org.matrix.android.sdk.api.session.crypto.model
import uniffi.olm.KeysImportResult import org.matrix.rustcomponents.sdk.crypto.KeysImportResult
data class ImportRoomKeysResult( data class ImportRoomKeysResult(
val totalNumberOfKeys: Int, val totalNumberOfKeys: Int,

View file

@ -104,7 +104,7 @@ interface VerificationService {
scannedData: String scannedData: String
): 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 // 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 package org.matrix.android.sdk.internal.crypto.verification
import android.os.Handler import kotlinx.coroutines.CoroutineScope
import android.os.Looper 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.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.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@SessionScope @SessionScope
internal class VerificationListenersHolder @Inject constructor() { internal class VerificationListenersHolder @Inject constructor(
private val coroutineDispatchers: MatrixCoroutineDispatchers
) {
private val listeners = ArrayList<VerificationService.Listener>() val scope = CoroutineScope(SupervisorJob() + coroutineDispatchers.dmVerif)
val eventFlow = MutableSharedFlow<VerificationEvent>(extraBufferCapacity = 20, onBufferOverflow = BufferOverflow.SUSPEND)
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) }
}
fun dispatchTxAdded(tx: VerificationTransaction) { fun dispatchTxAdded(tx: VerificationTransaction) {
uiHandler.post { scope.launch {
this.listeners.forEach { eventFlow.emit(VerificationEvent.TransactionAdded(tx))
try {
it.transactionCreated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
} }
} }
fun dispatchTxUpdated(tx: VerificationTransaction) { fun dispatchTxUpdated(tx: VerificationTransaction) {
uiHandler.post { scope.launch {
this.listeners.forEach { eventFlow.emit(VerificationEvent.TransactionUpdated(tx))
try {
it.transactionUpdated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
} }
} }
fun dispatchRequestAdded(verificationRequest: PendingVerificationRequest) { fun dispatchRequestAdded(verificationRequest: PendingVerificationRequest) {
Timber.v("## SAS dispatchRequestAdded txId:${verificationRequest.transactionId} $verificationRequest") Timber.v("## SAS dispatchRequestAdded txId:${verificationRequest.transactionId} $verificationRequest")
uiHandler.post { scope.launch {
this.listeners.forEach { eventFlow.emit(VerificationEvent.RequestAdded(verificationRequest))
try {
it.verificationRequestCreated(verificationRequest)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
} }
} }
fun dispatchRequestUpdated(verificationRequest: PendingVerificationRequest) { fun dispatchRequestUpdated(verificationRequest: PendingVerificationRequest) {
uiHandler.post { scope.launch {
listeners.forEach { eventFlow.emit(VerificationEvent.RequestUpdated(verificationRequest))
try {
it.verificationRequestUpdated(verificationRequest)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
} }
} }
} }

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.MessageType
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent 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.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.CipherSuiteMoshiAdapter
import org.matrix.android.sdk.internal.network.parsing.ForceToBooleanJsonAdapter import org.matrix.android.sdk.internal.network.parsing.ForceToBooleanJsonAdapter
import org.matrix.android.sdk.internal.network.parsing.RuntimeJsonAdapterFactory 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 { internal object MoshiProvider {
private val moshi: Moshi = Moshi.Builder() 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(UriMoshiAdapter())
.add(ForceToBooleanJsonAdapter()) .add(ForceToBooleanJsonAdapter())
.add(CipherSuiteMoshiAdapter()) .add(CipherSuiteMoshiAdapter())

View file

@ -279,8 +279,12 @@ internal abstract class SessionModule {
sessionParams: SessionParams, sessionParams: SessionParams,
retrofitFactory: RetrofitFactory retrofitFactory: RetrofitFactory
): Retrofit { ): Retrofit {
var uri = sessionParams.homeServerConnectionConfig.homeServerUriBase.toString()
if (uri == "http://localhost:8080") {
uri = "http://10.0.2.2:8080"
}
return retrofitFactory return retrofitFactory
.create(okHttpClient, sessionParams.homeServerConnectionConfig.homeServerUriBase.toString()) .create(okHttpClient, uri)
} }
@JvmStatic @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.SasVerification
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import org.matrix.android.sdk.internal.crypto.verification.prepareMethods import org.matrix.android.sdk.internal.crypto.verification.prepareMethods
import uniffi.olm.CryptoStoreException import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import uniffi.olm.SignatureException import org.matrix.rustcomponents.sdk.crypto.LocalTrust
import uniffi.olm.Device as InnerDevice 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 /** Class representing a device that supports E2EE in the Matrix world
* *
@ -88,7 +89,7 @@ internal class Device @AssistedInject constructor(
requestSender.sendVerificationRequest(result.request) requestSender.sendVerificationRequest(result.request)
verificationRequestFactory.create(result.verification) verificationRequestFactory.create(result.verification)
} catch (failure: Throwable) { } 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 null
} }
} else { } else {
@ -115,7 +116,8 @@ internal class Device @AssistedInject constructor(
requestSender.sendVerificationRequest(result.request) requestSender.sendVerificationRequest(result.request)
sasVerificationFactory.create(result.sas) sasVerificationFactory.create(result.sas)
} catch (failure: Throwable) { } 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 null
} }
} else { } else {
@ -132,7 +134,7 @@ internal class Device @AssistedInject constructor(
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)
suspend fun markAsTrusted() { suspend fun markAsTrusted() {
withContext(coroutineDispatchers.io) { 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. * This will not fetch out fresh data from the Rust side.
**/ **/
internal fun toCryptoDeviceInfo(): CryptoDeviceInfo { 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( return CryptoDeviceInfo(
deviceId = innerDevice.deviceId, deviceId = innerDevice.deviceId,
userId = innerDevice.userId, userId = innerDevice.userId,
algorithms = innerDevice.algorithms, algorithms = innerDevice.algorithms,
keys = keys, keys = innerDevice.keys,
// The Kotlin side doesn't need to care about signatures, // The Kotlin side doesn't need to care about signatures,
// so we're not filling this out // so we're not filling this out
signatures = mapOf(), signatures = mapOf(),
unsigned = UnsignedDeviceInfo(innerDevice.displayName), unsigned = UnsignedDeviceInfo(innerDevice.displayName),
trustLevel = DeviceTrustLevel(crossSigningVerified = innerDevice.crossSigningTrusted, locallyVerified = innerDevice.locallyTrusted), trustLevel = DeviceTrustLevel(
crossSigningVerified = innerDevice.crossSigningTrusted,
locallyVerified = innerDevice.locallyTrusted
),
isBlocked = innerDevice.isBlocked, isBlocked = innerDevice.isBlocked,
// TODO // TODO
firstTimeSeenLocalTs = null 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.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor
import org.matrix.android.sdk.internal.crypto.network.RequestSender import org.matrix.android.sdk.internal.crypto.network.RequestSender
import uniffi.olm.Request import org.matrix.rustcomponents.sdk.crypto.Request
import uniffi.olm.RequestType import org.matrix.rustcomponents.sdk.crypto.RequestType
import java.util.UUID import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider 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.model.rest.RestKeyInfo
import org.matrix.android.sdk.internal.crypto.network.RequestSender 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.VerificationRequest
import uniffi.olm.CryptoStoreException import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider import javax.inject.Provider
@ -44,7 +44,7 @@ internal class GetUserIdentityUseCase @Inject constructor(
val adapter = moshi.adapter(RestKeyInfo::class.java) val adapter = moshi.adapter(RestKeyInfo::class.java)
return when (identity) { return when (identity) {
is uniffi.olm.UserIdentity.Other -> { is org.matrix.rustcomponents.sdk.crypto.UserIdentity.Other -> {
val verified = innerMachine.isIdentityVerified(userId) val verified = innerMachine.isIdentityVerified(userId)
val masterKey = adapter.fromJson(identity.masterKey)!!.toCryptoModel().apply { val masterKey = adapter.fromJson(identity.masterKey)!!.toCryptoModel().apply {
trustLevel = DeviceTrustLevel(verified, verified) trustLevel = DeviceTrustLevel(verified, verified)
@ -62,7 +62,7 @@ internal class GetUserIdentityUseCase @Inject constructor(
verificationRequestFactory = verificationRequestFactory verificationRequestFactory = verificationRequestFactory
) )
} }
is uniffi.olm.UserIdentity.Own -> { is org.matrix.rustcomponents.sdk.crypto.UserIdentity.Own -> {
val verified = innerMachine.isIdentityVerified(userId) val verified = innerMachine.isIdentityVerified(userId)
val masterKey = adapter.fromJson(identity.masterKey)!!.toCryptoModel().apply { 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.LiveData
import androidx.lifecycle.asLiveData import androidx.lifecycle.asLiveData
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.adapter
import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow 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.VerificationsProvider
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification 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.DeviceId
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.di.SessionFilesDirectory import org.matrix.android.sdk.internal.di.SessionFilesDirectory
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
import org.matrix.android.sdk.internal.session.SessionScope 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 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.io.File
import java.nio.charset.Charset import java.nio.charset.Charset
import javax.inject.Inject import javax.inject.Inject
import uniffi.olm.OlmMachine as InnerMachine import org.matrix.rustcomponents.sdk.crypto.OlmMachine as InnerMachine
import uniffi.olm.ProgressListener as RustProgressListener import org.matrix.rustcomponents.sdk.crypto.ProgressListener as RustProgressListener
class CryptoLogger : Logger { class CryptoLogger : Logger {
override fun log(logLine: String) { override fun log(logLine: String) {
@ -256,19 +260,33 @@ internal class OlmMachine @Inject constructor(
val devices = val devices =
DeviceLists(deviceChanges?.changed.orEmpty(), deviceChanges?.left.orEmpty()) DeviceLists(deviceChanges?.changed.orEmpty(), deviceChanges?.left.orEmpty())
val adapter =
moshi.adapter(ToDeviceSyncResponse::class.java) val adapter = MoshiProvider.providesMoshi().adapter(ToDeviceSyncResponse::class.java)
val events = adapter.toJson(toDevice ?: ToDeviceSyncResponse())!! val events = adapter.toJson(toDevice ?: ToDeviceSyncResponse()).also {
Timber.w("## VALR events: $it")
}
// TODO once our sync response type parses the unused fallback key // TODO once our sync response type parses the unused fallback key
// field pass in the list of unused fallback keys here // 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. // We may get cross signing keys over a to-device event, update our listeners.
updateLivePrivateKeys() updateLivePrivateKeys()
return response return ToDeviceSyncResponse(events = response)
} }
suspend fun receiveUnencryptedVerificationEvent(roomId: String, event: Event) = withContext(coroutineDispatchers.io) { 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. * @return The list of [Request.ToDevice] that need to be sent out.
*/ */
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)
suspend fun shareRoomKey(roomId: String, users: List<String>): List<Request> = suspend fun shareRoomKey(roomId: String, users: List<String>, settings: EncryptionSettings): List<Request> =
withContext(coroutineDispatchers.io) { inner.shareRoomKey(roomId, users) } withContext(coroutineDispatchers.io) {
inner.shareRoomKey(roomId, users, settings)
}
/** /**
* Encrypt the given event with the given type and content for the given room. * 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) @Throws(CryptoStoreException::class)
suspend fun exportKeys(passphrase: String, rounds: Int): ByteArray = 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. * Import room keys from the given serialized key export.
@ -472,7 +494,7 @@ internal class OlmMachine @Inject constructor(
val rustListener = CryptoProgressListener(listener) val rustListener = CryptoProgressListener(listener)
val result = inner.importKeys(decodedKeys, passphrase, rustListener) val result = inner.importRoomKeys(decodedKeys, passphrase, rustListener)
ImportRoomKeysResult.fromOlm(result) 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 totalImported += it.imported
accTotal += it.total accTotal += it.total
details.putAll(it.keys) details.putAll(it.keys)
@ -812,7 +834,7 @@ internal class OlmMachine @Inject constructor(
.build() .build()
.adapter(MegolmBackupAuthData::class.java) .adapter(MegolmBackupAuthData::class.java)
val serializedAuthData = adapter.toJson(authData) 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.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask 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 timber.log.Timber
import uniffi.olm.Request
import uniffi.olm.RequestType
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject import javax.inject.Inject
@ -83,9 +86,26 @@ internal class PrepareToEncryptUseCase @Inject constructor(
claimMissingKeys(roomMembers) claimMissingKeys(roomMembers)
val keyShareLock = roomKeyShareLocks.getOrPut(roomId) { Mutex() } val keyShareLock = roomKeyShareLocks.getOrPut(roomId) { Mutex() }
var sharedKey = false 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 { keyShareLock.withLock {
coroutineScope { coroutineScope {
olmMachine.shareRoomKey(roomId, roomMembers).map { olmMachine.shareRoomKey(roomId, roomMembers, settings).map {
when (it) { when (it) {
is Request.ToDevice -> { is Request.ToDevice -> {
sharedKey = true sharedKey = true

View file

@ -17,6 +17,7 @@
package org.matrix.android.sdk.internal.crypto package org.matrix.android.sdk.internal.crypto
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor 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.CrossSigningService
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustResult 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.PrivateKeysInfo
import org.matrix.android.sdk.api.session.crypto.crosssigning.UserTrustResult 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.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.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import javax.inject.Inject import javax.inject.Inject
@ -213,4 +215,22 @@ internal class RustCrossSigningService @Inject constructor(
// TODO // TODO
// is this needed in rust? // 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) { override fun enableShareKeyOnInvite(enable: Boolean) {
TODO("Not yet implemented") TODO("Enable share key on invite not implemented")
} }
override fun isShareKeysOnInviteEnabled(): Boolean { override fun isShareKeysOnInviteEnabled(): Boolean {
TODO("Not yet implemented") return false
} }
override fun setRoomUnBlockUnverifiedDevices(roomId: String) { 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 prepareToEncrypt(roomId: String) = prepareToEncrypt.invoke(roomId, ensureAllMembersAreLoaded = true)
override suspend fun sendSharedHistoryKeys(roomId: String, userId: String, sessionInfoSet: Set<SessionInfo>?) { 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.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import org.matrix.android.sdk.internal.crypto.verification.prepareMethods import org.matrix.android.sdk.internal.crypto.verification.prepareMethods
import uniffi.olm.CryptoStoreException import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import uniffi.olm.OlmMachine import org.matrix.rustcomponents.sdk.crypto.OlmMachine
import uniffi.olm.SignatureException import org.matrix.rustcomponents.sdk.crypto.SignatureException
/** /**
* A sealed class representing user identities. * A sealed class representing user identities.

View file

@ -16,7 +16,7 @@
package org.matrix.android.sdk.api.session.crypto.keysbackup 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 { 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.session.SessionScope
import org.matrix.android.sdk.internal.util.JsonCanonicalizer import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.olm.OlmException import org.matrix.olm.OlmException
import org.matrix.rustcomponents.sdk.crypto.Request
import org.matrix.rustcomponents.sdk.crypto.RequestType
import timber.log.Timber import timber.log.Timber
import uniffi.olm.Request
import uniffi.olm.RequestType
import java.security.InvalidParameterException import java.security.InvalidParameterException
import javax.inject.Inject import javax.inject.Inject
import kotlin.random.Random 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.CryptoSessionInfoProvider
import org.matrix.android.sdk.internal.crypto.OlmMachine import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.session.SessionScope 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 timber.log.Timber
import uniffi.olm.Request
import uniffi.olm.RequestType
import javax.inject.Inject import javax.inject.Inject
private val loggerTag = LoggerTag("OutgoingRequestsProcessor", LoggerTag.CRYPTO) 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.DEFAULT_REQUEST_RETRY_COUNT
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
import org.matrix.android.sdk.internal.session.room.send.SendResponse 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 timber.log.Timber
import uniffi.olm.OutgoingVerificationRequest
import uniffi.olm.Request
import uniffi.olm.SignatureUploadRequest
import uniffi.olm.UploadSigningKeysRequest
import javax.inject.Inject import javax.inject.Inject
internal class RequestSender @Inject constructor( internal class RequestSender @Inject constructor(

View file

@ -74,6 +74,8 @@ internal class RustVerificationService @Inject constructor(
private val olmMachine: OlmMachine, private val olmMachine: OlmMachine,
private val verificationListenersHolder: VerificationListenersHolder) : VerificationService { private val verificationListenersHolder: VerificationListenersHolder) : VerificationService {
override fun requestEventFlow() = verificationListenersHolder.eventFlow
/** /**
* *
* All verification related events should be forwarded through this method to * 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 val messageContent = event.getClearContent()?.toModel<MessageContent>() ?: return
if (messageContent.msgType == MessageType.MSGTYPE_VERIFICATION_REQUEST) { if (messageContent.msgType == MessageType.MSGTYPE_VERIFICATION_REQUEST) {
onRequest(event, fromRoomMessage = true) onRequest(event, fromRoomMessage = true)
@ -111,7 +113,7 @@ internal class RustVerificationService @Inject constructor(
} }
/** Dispatch updates after a verification event has been received */ /** 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 sender = event.senderId ?: return
val flowId = getFlowId(event) ?: 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 */ /** 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) { val flowId = if (fromRoomMessage) {
event.eventId event.eventId
} else { } else {
@ -162,26 +164,26 @@ internal class RustVerificationService @Inject constructor(
verificationListenersHolder.dispatchRequestAdded(request) verificationListenersHolder.dispatchRequestAdded(request)
} }
override fun addListener(listener: VerificationService.Listener) { // override fun addListener(listener: VerificationService.Listener) {
verificationListenersHolder.addListener(listener) // verificationListenersHolder.addListener(listener)
} // }
//
override fun removeListener(listener: VerificationService.Listener) { // override fun removeListener(listener: VerificationService.Listener) {
verificationListenersHolder.removeListener(listener) // verificationListenersHolder.removeListener(listener)
} // }
override suspend fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) { override suspend fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
olmMachine.getDevice(userId, deviceID)?.markAsTrusted() olmMachine.getDevice(userId, deviceID)?.markAsTrusted()
} }
override fun getExistingTransaction( override suspend fun getExistingTransaction(
otherUserId: String, otherUserId: String,
tid: String, tid: String,
): VerificationTransaction? { ): VerificationTransaction? {
return olmMachine.getVerification(otherUserId, tid) return olmMachine.getVerification(otherUserId, tid)
} }
override fun getExistingVerificationRequests( override suspend fun getExistingVerificationRequests(
otherUserId: String otherUserId: String
): List<PendingVerificationRequest> { ): List<PendingVerificationRequest> {
return olmMachine.getVerificationRequests(otherUserId).map { return olmMachine.getVerificationRequests(otherUserId).map {
@ -189,7 +191,7 @@ internal class RustVerificationService @Inject constructor(
} }
} }
override fun getExistingVerificationRequest( override suspend fun getExistingVerificationRequest(
otherUserId: String, otherUserId: String,
tid: String? tid: String?
): PendingVerificationRequest? { ): PendingVerificationRequest? {
@ -200,9 +202,9 @@ internal class RustVerificationService @Inject constructor(
} }
} }
override fun getExistingVerificationRequestInRoom( override suspend fun getExistingVerificationRequestInRoom(
roomId: String, roomId: String,
tid: String? tid: String
): PendingVerificationRequest? { ): PendingVerificationRequest? {
// This is only used in `RoomDetailViewModel` to resume the verification. // 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>, override suspend fun requestDeviceVerification(methods: List<VerificationMethod>,
otherUserId: String, otherUserId: String,
otherDeviceId: String?): PendingVerificationRequest? { otherDeviceId: String?): PendingVerificationRequest {
// how do we send request to several devices in rust? // how do we send request to several devices in rust?
if (otherDeviceId == null) return null
olmMachine.ensureUsersKeys(listOf(otherUserId)) 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 otherDevice = olmMachine.getDevice(otherUserId, otherDeviceId)
val verificationRequest = otherDevice?.requestVerification(methods) otherDevice?.requestVerification(methods) ?: throw IllegalArgumentException("Unknown device $otherDeviceId")
return verificationRequest?.toPendingVerificationRequest() }
return request.toPendingVerificationRequest()
} }
override suspend fun readyPendingVerification( override suspend fun readyPendingVerification(
@ -269,29 +281,15 @@ internal class RustVerificationService @Inject constructor(
return if (request != null) { return if (request != null) {
request.acceptWithMethods(methods) request.acceptWithMethods(methods)
if (request.isReady()) { request.isReady()
val qrcode = request.startQrVerification()
if (qrcode != null) {
verificationListenersHolder.dispatchTxAdded(qrcode)
}
true
} else {
false
}
} else { } else {
false false
} }
} }
override suspend fun beginKeyVerification( override suspend fun startKeyVerification(method: VerificationMethod, otherUserId: String, requestId: String): String? {
method: VerificationMethod,
otherUserId: String,
transactionId: String
): String? {
return if (method == VerificationMethod.SAS) { return if (method == VerificationMethod.SAS) {
val request = olmMachine.getVerificationRequest(otherUserId, transactionId) val request = olmMachine.getVerificationRequest(otherUserId, requestId)
val sas = request?.startSasVerification() 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 // not available in rust
} }
@ -331,7 +335,6 @@ internal class RustVerificationService @Inject constructor(
// } // }
override suspend fun cancelVerificationRequest(request: PendingVerificationRequest) { override suspend fun cancelVerificationRequest(request: PendingVerificationRequest) {
request.transactionId ?: return
cancelVerificationRequest(request.otherUserId, request.transactionId) 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.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode 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.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.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.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.internal.crypto.OlmMachine import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.network.RequestSender import org.matrix.android.sdk.internal.crypto.network.RequestSender
import uniffi.olm.CryptoStoreException import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import uniffi.olm.Sas import org.matrix.rustcomponents.sdk.crypto.Sas
import uniffi.olm.Verification import org.matrix.rustcomponents.sdk.crypto.SasListener
import org.matrix.rustcomponents.sdk.crypto.SasState
/** Class representing a short auth string verification flow */ /** Class representing a short auth string verification flow */
internal class SasVerification @AssistedInject constructor( internal class SasVerification @AssistedInject constructor(
@ -40,60 +42,67 @@ internal class SasVerification @AssistedInject constructor(
private val olmMachine: OlmMachine, private val olmMachine: OlmMachine,
private val sender: RequestSender, private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, 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 @AssistedFactory
interface Factory { interface Factory {
fun create(inner: Sas): SasVerification 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 */ /** 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 */ /** Get the device id of the other user's device participating in this verification flow */
override var otherDeviceId: String? override val otherDeviceId: String
get() = inner.otherDeviceId get() = inner.otherDeviceId()
@Suppress("UNUSED_PARAMETER")
set(value) {
}
/** Did the other side initiate this verification flow */ /** Did the other side initiate this verification flow */
override val isIncoming: Boolean override val isIncoming: Boolean
get() = !inner.weStarted get() = !inner.weStarted()
override var state: VerificationTxState private var decimals: List<Int>? = null
get() { private var emojis: List<Int>? = null
refreshData()
val cancelInfo = inner.cancelInfo
return when { override fun state(): SasTransactionState {
cancelInfo != null -> { return when (val state = innerState) {
val cancelCode = safeValueOf(cancelInfo.cancelCode) SasState.Started -> SasTransactionState.SasStarted
VerificationTxState.Cancelled(cancelCode, cancelInfo.cancelledByUs) SasState.Accepted -> SasTransactionState.SasAccepted
is SasState.KeysExchanged -> {
this.decimals = state.decimals
this.emojis = state.emojis
SasTransactionState.SasShortCodeReady
} }
inner.isDone -> VerificationTxState.Verified SasState.Confirmed -> SasTransactionState.SasMacSent
inner.haveWeConfirmed -> VerificationTxState.SasMacSent SasState.Done -> SasTransactionState.Done(false)
inner.canBePresented -> VerificationTxState.SasShortCodeReady is SasState.Cancelled -> SasTransactionState.Cancelled(safeValueOf(state.cancelInfo.cancelCode), state.cancelInfo.cancelledByUs)
inner.hasBeenAccepted -> VerificationTxState.SasAccepted
else -> VerificationTxState.SasStarted
} }
} // refreshData()
@Suppress("UNUSED_PARAMETER") // val cancelInfo = inner.cancelInfo
set(v) { //
// 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 */ /** Get the unique id of this verification */
override val transactionId: String override val transactionId: String
get() = inner.flowId get() = inner.flowId()
/** Cancel the verification flow /** Cancel the verification flow
* *
@ -136,13 +145,15 @@ internal class SasVerification @AssistedInject constructor(
cancelHelper(CancelCode.MismatchedSas) cancelHelper(CancelCode.MismatchedSas)
} }
override val method: VerificationMethod
get() = VerificationMethod.QR_CODE_SCAN
/** Is this verification happening over to-device messages */ /** 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 */ /** Does the verification flow support showing emojis as the short auth string */
override fun supportsEmoji(): Boolean { override fun supportsEmoji(): Boolean {
refreshData() return inner.supportsEmoji()
return inner.supportsEmoji
} }
/** Confirm that the short authentication code matches on both sides /** Confirm that the short authentication code matches on both sides
@ -177,8 +188,6 @@ internal class SasVerification @AssistedInject constructor(
* in a presentable state. * in a presentable state.
*/ */
override fun getDecimalCodeRepresentation(): String { override fun getDecimalCodeRepresentation(): String {
val decimals = innerMachine.getDecimals(inner.otherUserId, inner.flowId)
return decimals?.joinToString(" ") ?: "" return decimals?.joinToString(" ") ?: ""
} }
@ -189,14 +198,13 @@ internal class SasVerification @AssistedInject constructor(
* state. * state.
*/ */
override fun getEmojiCodeRepresentation(): List<EmojiRepresentation> { override fun getEmojiCodeRepresentation(): List<EmojiRepresentation> {
val emojiIndex = innerMachine.getEmojiIndex(inner.otherUserId, inner.flowId) return emojis?.map { getEmojiForCode(it) } ?: listOf()
return emojiIndex?.map { getEmojiForCode(it) } ?: listOf()
} }
internal suspend fun accept() { internal suspend fun accept() {
val request = innerMachine.acceptSasVerification(inner.otherUserId, inner.flowId) ?: return val request = inner.accept() ?: return Unit.also {
dispatchTxUpdated() // TODO should throw here?
}
try { try {
sender.sendVerificationRequest(request) sender.sendVerificationRequest(request)
} catch (failure: Throwable) { } catch (failure: Throwable) {
@ -207,10 +215,8 @@ internal class SasVerification @AssistedInject constructor(
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)
private suspend fun confirm() { private suspend fun confirm() {
val result = withContext(coroutineDispatchers.io) { val result = withContext(coroutineDispatchers.io) {
innerMachine.confirmVerification(inner.otherUserId, inner.flowId) inner.confirm()
} ?: return } ?: return
dispatchTxUpdated()
try { try {
for (verificationRequest in result.requests) { for (verificationRequest in result.requests) {
sender.sendVerificationRequest(verificationRequest) sender.sendVerificationRequest(verificationRequest)
@ -225,24 +231,28 @@ internal class SasVerification @AssistedInject constructor(
} }
private suspend fun cancelHelper(code: CancelCode) = withContext(NonCancellable) { private suspend fun cancelHelper(code: CancelCode) = withContext(NonCancellable) {
val request = innerMachine.cancelVerification(inner.otherUserId, inner.flowId, code.value) ?: return@withContext val request = inner.cancel(code.value) ?: return@withContext
dispatchTxUpdated()
tryOrNull("Fail to send cancel request") { tryOrNull("Fail to send cancel request") {
sender.sendVerificationRequest(request, retryCount = Int.MAX_VALUE) sender.sendVerificationRequest(request, retryCount = Int.MAX_VALUE)
} }
} }
/** Fetch fresh data from the Rust side for our verification flow */ /** Fetch fresh data from the Rust side for our verification flow */
private fun refreshData() { // private fun refreshData() {
when (val verification = innerMachine.getVerification(inner.otherUserId, inner.flowId)) { // when (val verification = innerMachine.getVerification(inner.otherUserId, inner.flowId)) {
is Verification.SasV1 -> { // is Verification.SasV1 -> {
inner = verification.sas // inner = verification.sas
} // }
else -> { // else -> {
} // }
} // }
//
// return
// }
return override fun onChange(state: SasState) {
innerState = state
verificationListenersHolder.dispatchTxUpdated(this)
} }
override fun toString(): String { override fun toString(): String {
@ -250,7 +260,7 @@ internal class SasVerification @AssistedInject constructor(
"otherUserId='$otherUserId', " + "otherUserId='$otherUserId', " +
"otherDeviceId=$otherDeviceId, " + "otherDeviceId=$otherDeviceId, " +
"isIncoming=$isIncoming, " + "isIncoming=$isIncoming, " +
"state=$state, " + "state=${state()}, " +
"transactionId='$transactionId')" "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.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.extensions.tryOrNull 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.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.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.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.api.util.toBase64NoPadding import org.matrix.android.sdk.api.util.toBase64NoPadding
import org.matrix.android.sdk.internal.crypto.OlmMachine 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_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.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
import org.matrix.android.sdk.internal.util.time.Clock 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 /** A verification request object
* *
@ -74,12 +77,12 @@ internal class VerificationRequest @AssistedInject constructor(
* event that initiated the flow. * event that initiated the flow.
*/ */
internal fun flowId(): String { internal fun flowId(): String {
return innerVerificationRequest.flowId return innerVerificationRequest.flowId()
} }
/** The user ID of the other user that is participating in this verification flow */ /** The user ID of the other user that is participating in this verification flow */
internal fun otherUser(): String { 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 /** 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? { internal fun otherDeviceId(): String? {
refreshData() refreshData()
return innerVerificationRequest.otherDeviceId return innerVerificationRequest.otherDeviceId()
} }
/** Did we initiate this verification flow */ /** Did we initiate this verification flow */
internal fun weStarted(): Boolean { internal fun weStarted(): Boolean {
return innerVerificationRequest.weStarted return innerVerificationRequest.weStarted()
} }
/** Get the id of the room where this verification is happening /** 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. * Will be null if the verification is not happening inside a room.
*/ */
internal fun roomId(): String? { internal fun roomId(): String? {
return innerVerificationRequest.roomId return innerVerificationRequest.roomId()
} }
/** Did the non-initiating side respond with a m.key.verification.read event /** 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 { internal fun isReady(): Boolean {
refreshData() refreshData()
return innerVerificationRequest.isReady return innerVerificationRequest.isReady()
} }
/** Did we advertise that we're able to scan QR codes */ /** Did we advertise that we're able to scan QR codes */
internal fun canScanQrCodes(): Boolean { internal fun canScanQrCodes(): Boolean {
refreshData() 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 /** 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>) { suspend fun acceptWithMethods(methods: List<VerificationMethod>) {
val stringMethods = prepareMethods(methods) val stringMethods = prepareMethods(methods)
val request = innerOlmMachine.acceptVerificationRequest( val request = innerVerificationRequest.accept(stringMethods)
innerVerificationRequest.otherUserId, ?: return // should throw here?
innerVerificationRequest.flowId, // val request = innerOlmMachine.acceptVerificationRequest(
stringMethods // innerVerificationRequest.otherUserId(),
) ?: return // innerVerificationRequest.flowId,
// stringMethods
// ) ?: return
try { try {
dispatchRequestUpdated() dispatchRequestUpdated()
requestSender.sendVerificationRequest(request) requestSender.sendVerificationRequest(request)
if (innerVerificationRequest.isReady()) {
activeQRCode = innerVerificationRequest.startQrVerification()
}
} catch (failure: Throwable) { } catch (failure: Throwable) {
cancel(CancelCode.UserError) cancel(CancelCode.UserError)
} }
} }
var activeQRCode: QrCode? = null
/** Transition from a ready verification request into emoji verification /** Transition from a ready verification request into emoji verification
* *
* This method will move the verification forward 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? { internal suspend fun startSasVerification(): SasVerification? {
return withContext(coroutineDispatchers.io) { 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 { try {
requestSender.sendVerificationRequest(result.request) requestSender.sendVerificationRequest(result.request)
sasVerificationFactory.create(result.sas) sasVerificationFactory.create(result.sas)
@ -195,7 +209,8 @@ internal class VerificationRequest @AssistedInject constructor(
// TODO again, what's the deal with ISO_8859_1? // TODO again, what's the deal with ISO_8859_1?
val byteArray = data.toByteArray(Charsets.ISO_8859_1) val byteArray = data.toByteArray(Charsets.ISO_8859_1)
val encodedData = byteArray.toBase64NoPadding() 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 { try {
requestSender.sendVerificationRequest(result.request) requestSender.sendVerificationRequest(result.request)
} catch (failure: Throwable) { } 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. * QR code verification, or null if we can't yet transition into QR code verification.
*/ */
internal fun startQrVerification(): QrCodeVerification? { internal fun startQrVerification(): QrCodeVerification? {
val qrcode = innerOlmMachine.startQrVerification(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId) activeQRCode = innerVerificationRequest.startQrVerification()
return if (qrcode != null) { // val qrcode = innerOlmMachine.startQrVerification(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId)
qrCodeVerificationFactory.create(this, qrcode) return if (activeQRCode != null) {
TODO("Is this reciprocate or just doing nothing?")
// activeQRCode.
// qrCodeVerificationFactory.create(this, qrcode)
} else { } else {
null null
} }
@ -242,11 +260,13 @@ internal class VerificationRequest @AssistedInject constructor(
* The method turns into a noop, if the verification flow has already been cancelled. * The method turns into a noop, if the verification flow has already been cancelled.
*/ */
internal suspend fun cancel(cancelCode: CancelCode = CancelCode.User) = withContext(NonCancellable) { internal suspend fun cancel(cancelCode: CancelCode = CancelCode.User) = withContext(NonCancellable) {
val request = innerOlmMachine.cancelVerification( // TODO damir how to add the code?
innerVerificationRequest.otherUserId, val request = innerVerificationRequest.cancel() ?: return@withContext
innerVerificationRequest.flowId, // val request = innerOlmMachine.cancelVerification(
cancelCode.value // innerVerificationRequest.otherUserId,
) ?: return@withContext // innerVerificationRequest.flowId,
// cancelCode.value
// ) ?: return@withContext
dispatchRequestUpdated() dispatchRequestUpdated()
tryOrNull("Fail to send cancel request") { tryOrNull("Fail to send cancel request") {
requestSender.sendVerificationRequest(request, retryCount = Int.MAX_VALUE) 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 */ /** Fetch fresh data from the Rust side for our verification flow */
private fun refreshData() { private fun refreshData() {
val request = innerOlmMachine.getVerificationRequest(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId) val request = innerOlmMachine.getVerificationRequest(innerVerificationRequest.otherUserId(), innerVerificationRequest.flowId())
if (request != null) { if (request != null) {
innerVerificationRequest = request 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 /** Convert the VerificationRequest into a PendingVerificationRequest
* *
* The public interface of the VerificationService dispatches the data class * The public interface of the VerificationService dispatches the data class
@ -273,7 +304,7 @@ internal class VerificationRequest @AssistedInject constructor(
*/ */
internal fun toPendingVerificationRequest(): PendingVerificationRequest { internal fun toPendingVerificationRequest(): PendingVerificationRequest {
refreshData() refreshData()
val cancelInfo = innerVerificationRequest.cancelInfo val cancelInfo = innerVerificationRequest.cancelInfo()
val cancelCode = val cancelCode =
if (cancelInfo != null) { if (cancelInfo != null) {
safeValueOf(cancelInfo.cancelCode) safeValueOf(cancelInfo.cancelCode)
@ -281,72 +312,79 @@ internal class VerificationRequest @AssistedInject constructor(
null null
} }
val ourMethods = innerVerificationRequest.ourMethods val ourMethods = innerVerificationRequest.ourSupportedMethods()
val theirMethods = innerVerificationRequest.theirMethods val theirMethods = innerVerificationRequest.theirSupportedMethods()
val otherDeviceId = innerVerificationRequest.otherDeviceId val otherDeviceId = innerVerificationRequest.otherDeviceId()
var requestInfo: ValidVerificationInfoRequest? = null // var requestInfo: ValidVerificationInfoRequest? = null
var readyInfo: ValidVerificationInfoReady? = 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) { innerVerificationRequest.startQrVerification()
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(),
)
}
return PendingVerificationRequest( return PendingVerificationRequest(
// Creation time // Creation time
ageLocalTs = clock.epochMillis(), ageLocalTs = clock.epochMillis(),
state = state(),
// Who initiated the request // Who initiated the request
isIncoming = !innerVerificationRequest.weStarted, isIncoming = !innerVerificationRequest.weStarted(),
// Local echo id, what to do here? // Local echo id, what to do here?
localId = innerVerificationRequest.flowId, otherDeviceId = innerVerificationRequest.otherDeviceId(),
// other user // other user
otherUserId = innerVerificationRequest.otherUserId, otherUserId = innerVerificationRequest.otherUserId(),
// room id // room id
roomId = innerVerificationRequest.roomId, roomId = innerVerificationRequest.roomId(),
// transaction id // transaction id
transactionId = innerVerificationRequest.flowId, transactionId = innerVerificationRequest.flowId(),
// val requestInfo: ValidVerificationInfoRequest? = null,
requestInfo = requestInfo,
// val readyInfo: ValidVerificationInfoReady? = null,
readyInfo = readyInfo,
// cancel code if there is one // cancel code if there is one
cancelConclusion = cancelCode, cancelConclusion = cancelCode,
// are we done/successful isFinished = innerVerificationRequest.isDone() || innerVerificationRequest.isCancelled(),
isSuccessful = innerVerificationRequest.isDone,
// did another device answer the request // did another device answer the request
handledByOtherSession = innerVerificationRequest.isPassive, handledByOtherSession = innerVerificationRequest.isPassive(),
// devices that should receive the events we send out // 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 org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider import javax.inject.Provider
import uniffi.olm.OlmMachine as InnerOlmMachine import org.matrix.rustcomponents.sdk.crypto.OlmMachine as InnerOlmMachine
internal class VerificationsProvider @Inject constructor( internal class VerificationsProvider @Inject constructor(
private val olmMachine: Provider<OlmMachine>, private val olmMachine: Provider<OlmMachine>,
@ -49,26 +49,36 @@ internal class VerificationsProvider @Inject constructor(
* verification. * verification.
*/ */
fun getVerification(userId: String, flowId: String): VerificationTransaction? { fun getVerification(userId: String, flowId: String): VerificationTransaction? {
return when (val verification = innerMachine.getVerification(userId, flowId)) { val verification = innerMachine.getVerification(userId, flowId)
is uniffi.olm.Verification.QrCodeV1 -> { return if (verification?.asSas() != null) {
val request = getVerificationRequest(userId, flowId) ?: return null sasVerificationFactory.create(verification.asSas()!!)
qrVerificationFactory.create(request, verification.qrcode) } else if (verification?.asQr() != null) {
} // qrVerificationFactory.create(verification, verification.asQr()!!)
is uniffi.olm.Verification.SasV1 -> { // TODO
sasVerificationFactory.create(verification.sas) null
}
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 { } else {
null 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.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.extensions.tryOrNull 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.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.QrCodeVerificationTransaction
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.api.util.fromBase64 import org.matrix.android.sdk.api.util.fromBase64
import org.matrix.android.sdk.internal.crypto.OlmMachine import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.network.RequestSender 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.VerificationListenersHolder
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import uniffi.olm.CryptoStoreException import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
import uniffi.olm.QrCode import org.matrix.rustcomponents.sdk.crypto.QrCode
import uniffi.olm.Verification
/** Class representing a QR code based verification flow */ /** Class representing a QR code based verification flow */
internal class QrCodeVerification @AssistedInject constructor( internal class QrCodeVerification @AssistedInject constructor(
@Assisted private var request: VerificationRequest, @Assisted private var request: VerificationRequest,
@Assisted private var inner: QrCode?, @Assisted private var inner: QrCode,
private val olmMachine: OlmMachine, private val olmMachine: OlmMachine,
private val sender: RequestSender, private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
@ -48,9 +47,12 @@ internal class QrCodeVerification @AssistedInject constructor(
@AssistedFactory @AssistedFactory
interface Factory { 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 val innerMachine = olmMachine.inner()
private fun dispatchTxUpdated() { private fun dispatchTxUpdated() {
@ -72,17 +74,17 @@ internal class QrCodeVerification @AssistedInject constructor(
*/ */
override val qrCodeText: String? override val qrCodeText: String?
get() { 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? // 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) return data?.fromBase64()?.toString(Charsets.ISO_8859_1)
} }
/** Pass the data from a scanned QR code into the QR code verification object */ /** Pass the data from a scanned QR code into the QR code verification object */
override suspend fun userHasScannedOtherQrCode(otherQrCodeText: String) { // override suspend fun userHasScannedOtherQrCode(otherQrCodeText: String) {
request.scanQrCode(otherQrCodeText) // request.scanQrCode(otherQrCodeText)
dispatchTxUpdated() // dispatchTxUpdated()
} // }
/** Confirm that the other side has indeed scanned the QR code we presented */ /** Confirm that the other side has indeed scanned the QR code we presented */
override suspend fun otherUserScannedMyQrCode() { override suspend fun otherUserScannedMyQrCode() {
@ -95,32 +97,35 @@ internal class QrCodeVerification @AssistedInject constructor(
cancelHelper(CancelCode.MismatchedKeys) cancelHelper(CancelCode.MismatchedKeys)
} }
override var state: VerificationTxState override fun state(): QRCodeVerificationState {
get() { return QRCodeVerificationState.Reciprocated
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 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 */ /** Get the unique id of this verification */
override val transactionId: String override val transactionId: String
@ -185,7 +190,7 @@ internal class QrCodeVerification @AssistedInject constructor(
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)
private suspend fun confirm() { private suspend fun confirm() {
val result = withContext(coroutineDispatchers.io) { val result = withContext(coroutineDispatchers.io) {
innerMachine.confirmVerification(request.otherUser(), request.flowId()) inner.confirm()
} ?: return } ?: return
dispatchTxUpdated() dispatchTxUpdated()
try { try {
@ -202,7 +207,7 @@ internal class QrCodeVerification @AssistedInject constructor(
} }
private suspend fun cancelHelper(code: CancelCode) = withContext(NonCancellable) { 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() dispatchTxUpdated()
tryOrNull("Fail to send cancel verification request") { tryOrNull("Fail to send cancel verification request") {
sender.sendVerificationRequest(request, retryCount = Int.MAX_VALUE) 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 */ /** Fetch fresh data from the Rust side for our verification flow */
private fun refreshData() { private fun refreshData() {
when (val verification = innerMachine.getVerification(request.otherUser(), request.flowId())) { innerMachine.getVerification(request.otherUser(), request.flowId())
is Verification.QrCodeV1 -> { ?.asQr()?.let {
inner = verification.qrcode inner = it
}
else -> {
}
} }
// when (val verification = innerMachine.getVerification(request.otherUser(), request.flowId())) {
// is Verification.QrCodeV1 -> {
// inner = verification.qrcode
// }
// else -> {
// }
// }
return return
} }
@ -225,7 +234,7 @@ internal class QrCodeVerification @AssistedInject constructor(
override fun toString(): String { override fun toString(): String {
return "QrCodeVerification(" + return "QrCodeVerification(" +
"qrCodeText=$qrCodeText, " + "qrCodeText=$qrCodeText, " +
"state=$state, " + "state=${state()}, " +
"transactionId='$transactionId', " + "transactionId='$transactionId', " +
"otherUserId='$otherUserId', " + "otherUserId='$otherUserId', " +
"otherDeviceId=$otherDeviceId, " + "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"
}
}