crypto: Initial support to answer to-device verification requests
This commit is contained in:
parent
e46578a087
commit
a4e1a5bbcb
|
@ -49,6 +49,7 @@ import org.matrix.android.sdk.api.listeners.ProgressListener
|
|||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.crypto.keyshare.GossipingRequestListener
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
|
@ -86,7 +87,7 @@ import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask
|
|||
import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
|
||||
import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService
|
||||
import org.matrix.android.sdk.internal.crypto.verification.RustVerificationService
|
||||
import org.matrix.android.sdk.internal.di.DeviceId
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
|
||||
|
@ -131,9 +132,6 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
private val mxCryptoConfig: MXCryptoConfig,
|
||||
// The key backup service.
|
||||
private val keysBackupService: DefaultKeysBackupService,
|
||||
// The verification service.
|
||||
private val verificationService: DefaultVerificationService,
|
||||
|
||||
private val crossSigningService: DefaultCrossSigningService,
|
||||
// Actions
|
||||
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
|
||||
|
@ -156,6 +154,9 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
private val isStarting = AtomicBoolean(false)
|
||||
private val isStarted = AtomicBoolean(false)
|
||||
private var olmMachine: OlmMachine? = null
|
||||
// The verification service.
|
||||
private var verificationService: RustVerificationService? = null
|
||||
|
||||
private val deviceObserver: DeviceUpdateObserver = DeviceUpdateObserver()
|
||||
|
||||
// Locks for some of our operations
|
||||
|
@ -179,6 +180,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
|
||||
EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
|
||||
EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
|
||||
else -> this.verificationService?.onEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,7 +317,10 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
try {
|
||||
setRustLogger()
|
||||
this.olmMachine = OlmMachine(userId, deviceId!!, dataDir, deviceObserver)
|
||||
val machine = OlmMachine(userId, deviceId!!, dataDir, deviceObserver)
|
||||
this.olmMachine = machine
|
||||
this.verificationService =
|
||||
RustVerificationService(this.taskExecutor, machine, this.sendToDeviceTask)
|
||||
Timber.v(
|
||||
"## CRYPTO | Successfully started up an Olm machine for " +
|
||||
"${userId}, ${deviceId}, identity keys: ${this.olmMachine?.identityKeys()}")
|
||||
|
@ -359,7 +364,32 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
/**
|
||||
* @return the VerificationService
|
||||
*/
|
||||
override fun verificationService() = verificationService
|
||||
override fun verificationService(): VerificationService {
|
||||
// TODO yet another problem because the CryptoService is started in the
|
||||
// sync loop
|
||||
//
|
||||
// The `KeyRequestHandler` and `IncomingVerificationHandler` want to add
|
||||
// listeners to the verification service, they are initialized in the
|
||||
// `ActiveSessionHolder` class in the `setActiveSession()` method. In
|
||||
// the `setActiveSession()` method we call the `start()` method of the
|
||||
// handlers without first calling the `start()` method of the
|
||||
// `DefaultCrytpoService`.
|
||||
//
|
||||
// The start method of the crypto service isn't part of the
|
||||
// `CryptoService` interface so it currently can't be called there. I'm
|
||||
// inclined to believe that it should be, and that it should be
|
||||
// initialized before anything else tries to do something with it.
|
||||
//
|
||||
// Let's initialize here as a workaround until we figure out if the
|
||||
// above conclusion is correct.
|
||||
if (verificationService == null) {
|
||||
runBlocking {
|
||||
internalStart()
|
||||
}
|
||||
}
|
||||
|
||||
return verificationService!!
|
||||
}
|
||||
|
||||
override fun crossSigningService() = crossSigningService
|
||||
|
||||
|
@ -677,6 +707,8 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
val sessionId = content.sessionId
|
||||
|
||||
notifyRoomKeyReceival(roomId, sessionId)
|
||||
} else {
|
||||
this.verificationService?.onEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,33 +26,44 @@ import kotlinx.coroutines.runBlocking
|
|||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
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.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.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
|
||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UnsignedDeviceInfo
|
||||
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.verification.getEmojiForCode
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import org.matrix.android.sdk.internal.session.sync.model.DeviceListResponse
|
||||
import org.matrix.android.sdk.internal.session.sync.model.DeviceOneTimeKeysCountSyncResponse
|
||||
import org.matrix.android.sdk.internal.session.sync.model.ToDeviceSyncResponse
|
||||
import timber.log.Timber
|
||||
import uniffi.olm.CancelCode as RustCancelCode
|
||||
import uniffi.olm.CryptoStoreErrorException
|
||||
import uniffi.olm.DecryptionErrorException
|
||||
import uniffi.olm.Sas as InnerSas
|
||||
import uniffi.olm.OutgoingVerificationRequest
|
||||
import uniffi.olm.Device
|
||||
import uniffi.olm.DeviceLists
|
||||
import uniffi.olm.KeyRequestPair
|
||||
import uniffi.olm.Logger
|
||||
import uniffi.olm.OlmMachine as InnerMachine
|
||||
import uniffi.olm.OutgoingVerificationRequest
|
||||
import uniffi.olm.ProgressListener as RustProgressListener
|
||||
import uniffi.olm.Request
|
||||
import uniffi.olm.RequestType
|
||||
import uniffi.olm.Sas as InnerSas
|
||||
import uniffi.olm.VerificationRequest as InnerRequest
|
||||
import uniffi.olm.setLogger
|
||||
|
||||
class CryptoLogger : Logger {
|
||||
|
@ -89,13 +100,9 @@ fun setRustLogger() {
|
|||
setLogger(CryptoLogger() as Logger)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Rust Device into a Kotlin CryptoDeviceInfo
|
||||
*/
|
||||
/** Convert a Rust Device into a Kotlin CryptoDeviceInfo */
|
||||
private fun toCryptoDeviceInfo(device: Device): CryptoDeviceInfo {
|
||||
val keys = device.keys.map { (keyId, key) ->
|
||||
"$keyId:$device.deviceId" to key
|
||||
}.toMap()
|
||||
val keys = device.keys.map { (keyId, key) -> "$keyId:$device.deviceId" to key }.toMap()
|
||||
|
||||
return CryptoDeviceInfo(
|
||||
device.deviceId,
|
||||
|
@ -110,8 +117,7 @@ private fun toCryptoDeviceInfo(device: Device): CryptoDeviceInfo {
|
|||
DeviceTrustLevel(crossSigningVerified = false, locallyVerified = false),
|
||||
device.isBlocked,
|
||||
// TODO
|
||||
null
|
||||
)
|
||||
null)
|
||||
}
|
||||
|
||||
internal class DeviceUpdateObserver {
|
||||
|
@ -126,27 +132,171 @@ internal class DeviceUpdateObserver {
|
|||
}
|
||||
}
|
||||
|
||||
internal class Sas(private val machine: InnerMachine, private var inner: InnerSas) {
|
||||
internal class VerificationRequest(
|
||||
private val machine: InnerMachine,
|
||||
private var inner: InnerRequest
|
||||
) {
|
||||
private fun refreshData() {
|
||||
val sas = this.machine.getVerification(this.inner.flowId)
|
||||
val request = this.machine.getVerificationRequest(this.inner.otherUserId, this.inner.flowId)
|
||||
|
||||
if (sas != null) {
|
||||
this.inner = sas
|
||||
}
|
||||
if (request != null) {
|
||||
this.inner = request
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
fun accept_with_methods(methods: List<VerificationMethod>): OutgoingVerificationRequest? {
|
||||
val stringMethods: MutableList<String> =
|
||||
methods.map {
|
||||
when (it) {
|
||||
VerificationMethod.QR_CODE_SCAN -> VERIFICATION_METHOD_QR_CODE_SCAN
|
||||
VerificationMethod.QR_CODE_SHOW -> VERIFICATION_METHOD_QR_CODE_SHOW
|
||||
VerificationMethod.SAS -> VERIFICATION_METHOD_SAS
|
||||
}
|
||||
}.toMutableList()
|
||||
|
||||
if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) ||
|
||||
stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SCAN)) {
|
||||
stringMethods.add(VERIFICATION_METHOD_RECIPROCATE)
|
||||
}
|
||||
|
||||
return this.machine.acceptVerificationRequest(
|
||||
this.inner.otherUserId, this.inner.flowId, stringMethods)
|
||||
}
|
||||
|
||||
fun isCanceled(): Boolean {
|
||||
refreshData()
|
||||
return this.inner.isCanceled
|
||||
return this.inner.isCancelled
|
||||
}
|
||||
|
||||
fun isDone(): Boolean {
|
||||
refreshData()
|
||||
return this.inner.isDone
|
||||
}
|
||||
|
||||
|
||||
fun isReady(): Boolean {
|
||||
refreshData()
|
||||
return this.inner.isReady
|
||||
}
|
||||
|
||||
fun toPendingVerificationRequest(): PendingVerificationRequest {
|
||||
refreshData()
|
||||
val code = this.inner.cancelCode
|
||||
|
||||
val cancelCode =
|
||||
if (code != null) {
|
||||
toCancelCode(code)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val ourMethods = this.inner.ourMethods
|
||||
val theirMethods = this.inner.theirMethods
|
||||
val otherDeviceId = this.inner.otherDeviceId
|
||||
|
||||
var requestInfo: ValidVerificationInfoRequest? = null
|
||||
var readyInfo: ValidVerificationInfoReady? = null
|
||||
|
||||
if (this.inner.weStarted && ourMethods != null) {
|
||||
requestInfo =
|
||||
ValidVerificationInfoRequest(
|
||||
this.inner.flowId,
|
||||
this.machine.deviceId(),
|
||||
ourMethods,
|
||||
null,
|
||||
)
|
||||
} else if (!this.inner.weStarted && ourMethods != null) {
|
||||
readyInfo =
|
||||
ValidVerificationInfoReady(
|
||||
this.inner.flowId,
|
||||
this.machine.deviceId(),
|
||||
ourMethods,
|
||||
)
|
||||
}
|
||||
|
||||
if (this.inner.weStarted && theirMethods != null && otherDeviceId != null) {
|
||||
readyInfo =
|
||||
ValidVerificationInfoReady(
|
||||
this.inner.flowId,
|
||||
otherDeviceId,
|
||||
theirMethods,
|
||||
)
|
||||
} else if (!this.inner.weStarted && theirMethods != null && otherDeviceId != null) {
|
||||
requestInfo =
|
||||
ValidVerificationInfoRequest(
|
||||
this.inner.flowId,
|
||||
otherDeviceId,
|
||||
theirMethods,
|
||||
System.currentTimeMillis(),
|
||||
)
|
||||
}
|
||||
|
||||
return PendingVerificationRequest(
|
||||
// Creation time
|
||||
System.currentTimeMillis(),
|
||||
// Who initiated the request
|
||||
!this.inner.weStarted,
|
||||
// Local echo id, what to do here?
|
||||
this.inner.flowId,
|
||||
// other user
|
||||
this.inner.otherUserId,
|
||||
// room id
|
||||
this.inner.roomId,
|
||||
// transaction id
|
||||
this.inner.flowId,
|
||||
// val requestInfo: ValidVerificationInfoRequest? = null,
|
||||
requestInfo,
|
||||
// val readyInfo: ValidVerificationInfoReady? = null,
|
||||
readyInfo,
|
||||
// cancel code if there is one
|
||||
cancelCode,
|
||||
// are we done/successful
|
||||
this.inner.isDone,
|
||||
// did another device answer the request
|
||||
this.inner.isPassive,
|
||||
// devices that should receive the events we send out
|
||||
null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toCancelCode(cancelCode: RustCancelCode): CancelCode {
|
||||
return when (cancelCode) {
|
||||
RustCancelCode.USER -> CancelCode.User
|
||||
RustCancelCode.TIMEOUT -> CancelCode.Timeout
|
||||
RustCancelCode.UNKNOWN_TRANSACTION -> CancelCode.UnknownTransaction
|
||||
RustCancelCode.UNKNOWN_METHOD -> CancelCode.UnknownMethod
|
||||
RustCancelCode.UNEXPECTED_MESSAGE -> CancelCode.UnexpectedMessage
|
||||
RustCancelCode.KEY_MISMATCH -> CancelCode.MismatchedKeys
|
||||
RustCancelCode.USER_MISMATCH -> CancelCode.MismatchedKeys
|
||||
RustCancelCode.INVALID_MESSAGE -> CancelCode.InvalidMessage
|
||||
// TODO why don't the ruma codes match what's in EA?
|
||||
RustCancelCode.ACCEPTED -> CancelCode.User
|
||||
}
|
||||
}
|
||||
|
||||
internal class Sas(private val machine: InnerMachine, private var inner: InnerSas) {
|
||||
private fun refreshData() {
|
||||
val sas = this.machine.getVerification(this.inner.flowId)
|
||||
|
||||
if (sas != null) {
|
||||
this.inner = sas
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
fun isCanceled(): Boolean {
|
||||
refreshData()
|
||||
return this.inner.isCancelled
|
||||
}
|
||||
|
||||
fun isDone(): Boolean {
|
||||
refreshData()
|
||||
return this.inner.isDone
|
||||
}
|
||||
|
||||
fun timedOut(): Boolean {
|
||||
refreshData()
|
||||
return this.inner.timedOut
|
||||
|
@ -162,9 +312,8 @@ internal class Sas(private val machine: InnerMachine, private var inner: InnerSa
|
|||
}
|
||||
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
suspend fun confirm(): OutgoingVerificationRequest? = withContext(Dispatchers.IO) {
|
||||
machine.confirmVerification(inner.flowId)
|
||||
}
|
||||
suspend fun confirm(): OutgoingVerificationRequest? =
|
||||
withContext(Dispatchers.IO) { machine.confirmVerification(inner.flowId) }
|
||||
|
||||
fun cancel(): OutgoingVerificationRequest? {
|
||||
return this.machine.cancelVerification(inner.flowId)
|
||||
|
@ -185,27 +334,26 @@ internal class Sas(private val machine: InnerMachine, private var inner: InnerSa
|
|||
}
|
||||
}
|
||||
|
||||
internal class OlmMachine(user_id: String, device_id: String, path: File, deviceObserver: DeviceUpdateObserver) {
|
||||
internal class OlmMachine(
|
||||
user_id: String,
|
||||
device_id: String,
|
||||
path: File,
|
||||
deviceObserver: DeviceUpdateObserver
|
||||
) {
|
||||
private val inner: InnerMachine = InnerMachine(user_id, device_id, path.toString())
|
||||
private val deviceUpdateObserver = deviceObserver
|
||||
|
||||
/**
|
||||
* Get our own user ID.
|
||||
*/
|
||||
/** Get our own user ID. */
|
||||
fun userId(): String {
|
||||
return this.inner.userId()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our own device ID.
|
||||
*/
|
||||
/** Get our own device ID. */
|
||||
fun deviceId(): String {
|
||||
return this.inner.deviceId()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our own public identity keys ID.
|
||||
*/
|
||||
/** Get our own public identity keys ID. */
|
||||
fun identityKeys(): Map<String, String> {
|
||||
return this.inner.identityKeys()
|
||||
}
|
||||
|
@ -213,36 +361,31 @@ internal class OlmMachine(user_id: String, device_id: String, path: File, device
|
|||
fun ownDevice(): CryptoDeviceInfo {
|
||||
val deviceId = this.deviceId()
|
||||
|
||||
val keys = this.identityKeys().map { (keyId, key) ->
|
||||
"$keyId:$deviceId" to key
|
||||
}.toMap()
|
||||
val keys = this.identityKeys().map { (keyId, key) -> "$keyId:$deviceId" to key }.toMap()
|
||||
|
||||
return CryptoDeviceInfo(
|
||||
this.deviceId(),
|
||||
this.userId(),
|
||||
// TODO pass the algorithms here.
|
||||
listOf(),
|
||||
keys,
|
||||
mapOf(),
|
||||
UnsignedDeviceInfo(),
|
||||
DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true),
|
||||
false,
|
||||
null
|
||||
)
|
||||
this.deviceId(),
|
||||
this.userId(),
|
||||
// TODO pass the algorithms here.
|
||||
listOf(),
|
||||
keys,
|
||||
mapOf(),
|
||||
UnsignedDeviceInfo(),
|
||||
DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true),
|
||||
false,
|
||||
null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of outgoing requests that need to be sent to the homeserver.
|
||||
*
|
||||
* After the request was sent out and a successful response was received
|
||||
* the response body should be passed back to the state machine using the
|
||||
* markRequestAsSent() method.
|
||||
* After the request was sent out and a successful response was received the response body
|
||||
* should be passed back to the state machine using the markRequestAsSent() method.
|
||||
*
|
||||
* @return the list of requests that needs to be sent to the homeserver
|
||||
*/
|
||||
suspend fun outgoingRequests(): List<Request> = withContext(Dispatchers.IO) {
|
||||
inner.outgoingRequests()
|
||||
}
|
||||
suspend fun outgoingRequests(): List<Request> =
|
||||
withContext(Dispatchers.IO) { inner.outgoingRequests() }
|
||||
|
||||
/**
|
||||
* Mark a request that was sent to the server as sent.
|
||||
|
@ -255,135 +398,127 @@ internal class OlmMachine(user_id: String, device_id: String, path: File, device
|
|||
*/
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
suspend fun markRequestAsSent(
|
||||
requestId: String,
|
||||
requestType: RequestType,
|
||||
responseBody: String
|
||||
) = withContext(Dispatchers.IO) {
|
||||
inner.markRequestAsSent(requestId, requestType, responseBody)
|
||||
requestId: String,
|
||||
requestType: RequestType,
|
||||
responseBody: String
|
||||
) =
|
||||
withContext(Dispatchers.IO) {
|
||||
inner.markRequestAsSent(requestId, requestType, responseBody)
|
||||
|
||||
if (requestType == RequestType.KEYS_QUERY) {
|
||||
updateLiveDevices()
|
||||
}
|
||||
}
|
||||
if (requestType == RequestType.KEYS_QUERY) {
|
||||
updateLiveDevices()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Let the state machine know about E2EE related sync changes that we
|
||||
* received from the server.
|
||||
* Let the state machine know about E2EE related sync changes that we received from the server.
|
||||
*
|
||||
* This needs to be called after every sync, ideally before processing
|
||||
* any other sync changes.
|
||||
* This needs to be called after every sync, ideally before processing any other sync changes.
|
||||
*
|
||||
* @param toDevice A serialized array of to-device events we received in the
|
||||
* current sync resposne.
|
||||
* @param toDevice A serialized array of to-device events we received in the current sync
|
||||
* resposne.
|
||||
*
|
||||
* @param deviceChanges The list of devices that have changed in some way
|
||||
* since the previous sync.
|
||||
* @param deviceChanges The list of devices that have changed in some way since the previous
|
||||
* sync.
|
||||
*
|
||||
* @param keyCounts The map of uploaded one-time key types and counts.
|
||||
*/
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
suspend fun receiveSyncChanges(
|
||||
toDevice: ToDeviceSyncResponse?,
|
||||
deviceChanges: DeviceListResponse?,
|
||||
keyCounts: DeviceOneTimeKeysCountSyncResponse?
|
||||
): ToDeviceSyncResponse = withContext(Dispatchers.IO) {
|
||||
val counts: MutableMap<String, Int> = mutableMapOf()
|
||||
toDevice: ToDeviceSyncResponse?,
|
||||
deviceChanges: DeviceListResponse?,
|
||||
keyCounts: DeviceOneTimeKeysCountSyncResponse?
|
||||
): ToDeviceSyncResponse =
|
||||
withContext(Dispatchers.IO) {
|
||||
val counts: MutableMap<String, Int> = mutableMapOf()
|
||||
|
||||
if (keyCounts?.signedCurve25519 != null) {
|
||||
counts["signed_curve25519"] = keyCounts.signedCurve25519
|
||||
if (keyCounts?.signedCurve25519 != null) {
|
||||
counts["signed_curve25519"] = keyCounts.signedCurve25519
|
||||
}
|
||||
|
||||
val devices =
|
||||
DeviceLists(
|
||||
deviceChanges?.changed ?: listOf(), deviceChanges?.left ?: listOf())
|
||||
val adapter =
|
||||
MoshiProvider.providesMoshi().adapter(ToDeviceSyncResponse::class.java)
|
||||
val events = adapter.toJson(toDevice ?: ToDeviceSyncResponse())!!
|
||||
|
||||
adapter.fromJson(inner.receiveSyncChanges(events, devices, counts))!!
|
||||
}
|
||||
|
||||
val devices = DeviceLists(deviceChanges?.changed ?: listOf(), deviceChanges?.left ?: listOf())
|
||||
val adapter = MoshiProvider.providesMoshi().adapter(ToDeviceSyncResponse::class.java)
|
||||
val events = adapter.toJson(toDevice ?: ToDeviceSyncResponse())!!
|
||||
|
||||
adapter.fromJson(inner.receiveSyncChanges(events, devices, counts))!!
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the given list of users to be tracked, triggering a key query request
|
||||
* for them.
|
||||
* Mark the given list of users to be tracked, triggering a key query request for them.
|
||||
*
|
||||
* *Note*: Only users that aren't already tracked will be considered for an
|
||||
* update. It's safe to call this with already tracked users, it won't
|
||||
* result in excessive keys query requests.
|
||||
* *Note*: Only users that aren't already tracked will be considered for an update. It's safe to
|
||||
* call this with already tracked users, it won't result in excessive keys query requests.
|
||||
*
|
||||
* @param users The users that should be queued up for a key query.
|
||||
*/
|
||||
suspend fun updateTrackedUsers(users: List<String>) = withContext(Dispatchers.IO) {
|
||||
inner.updateTrackedUsers(users)
|
||||
}
|
||||
suspend fun updateTrackedUsers(users: List<String>) =
|
||||
withContext(Dispatchers.IO) { inner.updateTrackedUsers(users) }
|
||||
|
||||
/**
|
||||
* Generate one-time key claiming requests for all the users we are missing
|
||||
* sessions for.
|
||||
* Generate one-time key claiming requests for all the users we are missing sessions for.
|
||||
*
|
||||
* After the request was sent out and a successful response was received
|
||||
* the response body should be passed back to the state machine using the
|
||||
* markRequestAsSent() method.
|
||||
* After the request was sent out and a successful response was received the response body
|
||||
* should be passed back to the state machine using the markRequestAsSent() method.
|
||||
*
|
||||
* This method should be called every time before a call to
|
||||
* shareRoomKey() is made.
|
||||
* This method should be called every time before a call to shareRoomKey() is made.
|
||||
*
|
||||
* @param users The list of users for which we would like to establish 1:1
|
||||
* Olm sessions for.
|
||||
* @param users The list of users for which we would like to establish 1:1 Olm sessions for.
|
||||
*
|
||||
* @return A keys claim request that needs to be sent out to the server.
|
||||
*/
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
suspend fun getMissingSessions(users: List<String>): Request? = withContext(Dispatchers.IO) {
|
||||
inner.getMissingSessions(users)
|
||||
}
|
||||
suspend fun getMissingSessions(users: List<String>): Request? =
|
||||
withContext(Dispatchers.IO) { inner.getMissingSessions(users) }
|
||||
|
||||
/**
|
||||
* Share a room key with the given list of users for the given room.
|
||||
*
|
||||
* After the request was sent out and a successful response was received
|
||||
* the response body should be passed back to the state machine using the
|
||||
* markRequestAsSent() method.
|
||||
* After the request was sent out and a successful response was received the response body
|
||||
* should be passed back to the state machine using the markRequestAsSent() method.
|
||||
*
|
||||
* This method should be called every time before a call to
|
||||
* `encrypt()` with the given `room_id` is made.
|
||||
* This method should be called every time before a call to `encrypt()` with the given `room_id`
|
||||
* is made.
|
||||
*
|
||||
* @param roomId The unique id of the room, note that this doesn't strictly
|
||||
* need to be a Matrix room, it just needs to be an unique identifier for
|
||||
* the group that will participate in the conversation.
|
||||
* @param roomId The unique id of the room, note that this doesn't strictly need to be a Matrix
|
||||
* room, it just needs to be an unique identifier for the group that will participate in the
|
||||
* conversation.
|
||||
*
|
||||
* @param users The list of users which are considered to be members of the
|
||||
* room and should receive the room key.
|
||||
* @param users The list of users which are considered to be members of the room and should
|
||||
* receive the room key.
|
||||
*
|
||||
* @return The list of requests that need to be sent out.
|
||||
*/
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
suspend fun shareRoomKey(roomId: String, users: List<String>): List<Request> = withContext(Dispatchers.IO) {
|
||||
inner.shareRoomKey(roomId, users)
|
||||
}
|
||||
suspend fun shareRoomKey(roomId: String, users: List<String>): List<Request> =
|
||||
withContext(Dispatchers.IO) { inner.shareRoomKey(roomId, users) }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* **Note**: A room key needs to be shared with the group of users that are
|
||||
* members in the given room. If this is not done this method will panic.
|
||||
* **Note**: A room key needs to be shared with the group of users that are members in the given
|
||||
* room. If this is not done this method will panic.
|
||||
*
|
||||
* The usual flow to encrypt an evnet using this state machine is as
|
||||
* follows:
|
||||
* The usual flow to encrypt an evnet using this state machine is as follows:
|
||||
*
|
||||
* 1. Get the one-time key claim request to establish 1:1 Olm sessions for
|
||||
* ```
|
||||
* the room members of the room we wish to participate in. This is done
|
||||
* using the [`get_missing_sessions()`](#method.get_missing_sessions)
|
||||
* method. This method call should be locked per call.
|
||||
*
|
||||
* ```
|
||||
* 2. Share a room key with all the room members using the shareRoomKey().
|
||||
* ```
|
||||
* This method call should be locked per room.
|
||||
*
|
||||
* ```
|
||||
* 3. Encrypt the event using this method.
|
||||
*
|
||||
* 4. Send the encrypted event to the server.
|
||||
*
|
||||
* After the room key is shared steps 1 and 2 will become noops, unless
|
||||
* there's some changes in the room membership or in the list of devices a
|
||||
* member has.
|
||||
* After the room key is shared steps 1 and 2 will become noops, unless there's some changes in
|
||||
* the room membership or in the list of devices a member has.
|
||||
*
|
||||
* @param roomId the ID of the room where the encrypted event will be sent to
|
||||
*
|
||||
|
@ -394,12 +529,13 @@ internal class OlmMachine(user_id: String, device_id: String, path: File, device
|
|||
* @return The encrypted version of the content
|
||||
*/
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
suspend fun encrypt(roomId: String, eventType: String, content: Content): Content = withContext(Dispatchers.IO) {
|
||||
val adapter = MoshiProvider.providesMoshi().adapter<Content>(Map::class.java)
|
||||
val contentString = adapter.toJson(content)
|
||||
val encrypted = inner.encrypt(roomId, eventType, contentString)
|
||||
adapter.fromJson(encrypted)!!
|
||||
}
|
||||
suspend fun encrypt(roomId: String, eventType: String, content: Content): Content =
|
||||
withContext(Dispatchers.IO) {
|
||||
val adapter = MoshiProvider.providesMoshi().adapter<Content>(Map::class.java)
|
||||
val contentString = adapter.toJson(content)
|
||||
val encrypted = inner.encrypt(roomId, eventType, contentString)
|
||||
adapter.fromJson(encrypted)!!
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt the given event that was sent in the given room.
|
||||
|
@ -411,62 +547,64 @@ internal class OlmMachine(user_id: String, device_id: String, path: File, device
|
|||
* @return the decrypted version of the event.
|
||||
*/
|
||||
@Throws(MXCryptoError::class)
|
||||
suspend fun decryptRoomEvent(event: Event): MXEventDecryptionResult = withContext(Dispatchers.IO) {
|
||||
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
|
||||
val serializedEvent = adapter.toJson(event)
|
||||
suspend fun decryptRoomEvent(event: Event): MXEventDecryptionResult =
|
||||
withContext(Dispatchers.IO) {
|
||||
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
|
||||
val serializedEvent = adapter.toJson(event)
|
||||
|
||||
try {
|
||||
val decrypted = inner.decryptRoomEvent(serializedEvent, event.roomId!!)
|
||||
try {
|
||||
val decrypted = inner.decryptRoomEvent(serializedEvent, event.roomId!!)
|
||||
|
||||
val deserializationAdapter = MoshiProvider.providesMoshi().adapter<JsonDict>(Map::class.java)
|
||||
val clearEvent = deserializationAdapter.fromJson(decrypted.clearEvent)!!
|
||||
val deserializationAdapter =
|
||||
MoshiProvider.providesMoshi().adapter<JsonDict>(Map::class.java)
|
||||
val clearEvent = deserializationAdapter.fromJson(decrypted.clearEvent)!!
|
||||
|
||||
MXEventDecryptionResult(
|
||||
clearEvent,
|
||||
decrypted.senderCurve25519Key,
|
||||
decrypted.claimedEd25519Key,
|
||||
decrypted.forwardingCurve25519Chain
|
||||
)
|
||||
} catch (throwable: Throwable) {
|
||||
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, throwable.message, "m.megolm.v1.aes-sha2")
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason)
|
||||
}
|
||||
}
|
||||
MXEventDecryptionResult(
|
||||
clearEvent,
|
||||
decrypted.senderCurve25519Key,
|
||||
decrypted.claimedEd25519Key,
|
||||
decrypted.forwardingCurve25519Chain)
|
||||
} catch (throwable: Throwable) {
|
||||
val reason =
|
||||
String.format(
|
||||
MXCryptoError.UNABLE_TO_DECRYPT_REASON,
|
||||
throwable.message,
|
||||
"m.megolm.v1.aes-sha2")
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the room key that was used to encrypt the given undecrypted
|
||||
* event.
|
||||
* Request the room key that was used to encrypt the given undecrypted event.
|
||||
*
|
||||
* @param event The that we're not able to decrypt and want to request a
|
||||
* room key for.
|
||||
* @param event The that we're not able to decrypt and want to request a room key for.
|
||||
*
|
||||
* @return a key request pair, consisting of an optional key request
|
||||
* cancellation and the key request itself. The cancellation *must* be sent
|
||||
* out before the request, otherwise devices will ignore the key request.
|
||||
* @return a key request pair, consisting of an optional key request cancellation and the key
|
||||
* request itself. The cancellation *must* be sent out before the request, otherwise devices
|
||||
* will ignore the key request.
|
||||
*/
|
||||
@Throws(DecryptionErrorException::class)
|
||||
suspend fun requestRoomKey(event: Event): KeyRequestPair = withContext(Dispatchers.IO) {
|
||||
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
|
||||
val serializedEvent = adapter.toJson(event)
|
||||
suspend fun requestRoomKey(event: Event): KeyRequestPair =
|
||||
withContext(Dispatchers.IO) {
|
||||
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
|
||||
val serializedEvent = adapter.toJson(event)
|
||||
|
||||
inner.requestRoomKey(serializedEvent, event.roomId!!)
|
||||
}
|
||||
inner.requestRoomKey(serializedEvent, event.roomId!!)
|
||||
}
|
||||
|
||||
/**
|
||||
* Export all of our room keys.
|
||||
*
|
||||
* @param passphrase The passphrase that should be used to encrypt the key
|
||||
* export.
|
||||
* @param passphrase The passphrase that should be used to encrypt the key export.
|
||||
*
|
||||
* @param rounds The number of rounds that should be used when expanding the
|
||||
* passphrase into an key.
|
||||
* @param rounds The number of rounds that should be used when expanding the passphrase into an
|
||||
* key.
|
||||
*
|
||||
* @return the encrypted key export as a bytearray.
|
||||
*/
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
suspend fun exportKeys(passphrase: String, rounds: Int): ByteArray = withContext(Dispatchers.IO) {
|
||||
inner.exportKeys(passphrase, rounds).toByteArray()
|
||||
}
|
||||
suspend fun exportKeys(passphrase: String, rounds: Int): ByteArray =
|
||||
withContext(Dispatchers.IO) { inner.exportKeys(passphrase, rounds).toByteArray() }
|
||||
|
||||
/**
|
||||
* Import room keys from the given serialized key export.
|
||||
|
@ -475,19 +613,23 @@ internal class OlmMachine(user_id: String, device_id: String, path: File, device
|
|||
*
|
||||
* @param passphrase The passphrase that was used to encrypt the key export.
|
||||
*
|
||||
* @param listener A callback that can be used to introspect the
|
||||
* progress of the key import.
|
||||
* @param listener A callback that can be used to introspect the progress of the key import.
|
||||
*/
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
suspend fun importKeys(keys: ByteArray, passphrase: String, listener: ProgressListener?): ImportRoomKeysResult = withContext(Dispatchers.IO) {
|
||||
val decodedKeys = String(keys, Charset.defaultCharset())
|
||||
suspend fun importKeys(
|
||||
keys: ByteArray,
|
||||
passphrase: String,
|
||||
listener: ProgressListener?
|
||||
): ImportRoomKeysResult =
|
||||
withContext(Dispatchers.IO) {
|
||||
val decodedKeys = String(keys, Charset.defaultCharset())
|
||||
|
||||
val rustListener = CryptoProgressListener(listener)
|
||||
val rustListener = CryptoProgressListener(listener)
|
||||
|
||||
val result = inner.importKeys(decodedKeys, passphrase, rustListener)
|
||||
val result = inner.importKeys(decodedKeys, passphrase, rustListener)
|
||||
|
||||
ImportRoomKeysResult(result.total, result.imported)
|
||||
}
|
||||
ImportRoomKeysResult(result.total, result.imported)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a `Device` from the store.
|
||||
|
@ -499,16 +641,17 @@ internal class OlmMachine(user_id: String, device_id: String, path: File, device
|
|||
* @return The Device if it found one.
|
||||
*/
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
suspend fun getDevice(userId: String, deviceId: String): CryptoDeviceInfo? = withContext(Dispatchers.IO) {
|
||||
// Our own device isn't part of our store on the rust side, return it
|
||||
// using our ownDevice method
|
||||
if (userId == userId() && deviceId == deviceId()) {
|
||||
ownDevice()
|
||||
} else {
|
||||
val device = inner.getDevice(userId, deviceId)
|
||||
if (device != null) toCryptoDeviceInfo(device) else null
|
||||
}
|
||||
}
|
||||
suspend fun getDevice(userId: String, deviceId: String): CryptoDeviceInfo? =
|
||||
withContext(Dispatchers.IO) {
|
||||
// Our own device isn't part of our store on the rust side, return it
|
||||
// using our ownDevice method
|
||||
if (userId == userId() && deviceId == deviceId()) {
|
||||
ownDevice()
|
||||
} else {
|
||||
val device = inner.getDevice(userId, deviceId)
|
||||
if (device != null) toCryptoDeviceInfo(device) else null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all devices of an user.
|
||||
|
@ -561,9 +704,7 @@ internal class OlmMachine(user_id: String, device_id: String, path: File, device
|
|||
return plainDevices
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all of our live device listeners.
|
||||
*/
|
||||
/** Update all of our live device listeners. */
|
||||
private suspend fun updateLiveDevices() {
|
||||
for ((liveDevice, users) in deviceUpdateObserver.listeners) {
|
||||
val devices = getUserDevices(users)
|
||||
|
@ -574,8 +715,8 @@ internal class OlmMachine(user_id: String, device_id: String, path: File, device
|
|||
/**
|
||||
* Get all the devices of multiple users as a live version.
|
||||
*
|
||||
* The live version will update the list of devices if some of the data
|
||||
* changes, or if new devices arrive for a certain user.
|
||||
* The live version will update the list of devices if some of the data changes, or if new
|
||||
* devices arrive for a certain user.
|
||||
*
|
||||
* @param userIds The ids of the device owners.
|
||||
*
|
||||
|
@ -589,24 +730,36 @@ internal class OlmMachine(user_id: String, device_id: String, path: File, device
|
|||
return devices
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard the currently active room key for the given room if there is one.
|
||||
*/
|
||||
/** Discard the currently active room key for the given room if there is one. */
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
fun discardRoomKey(roomId: String) {
|
||||
runBlocking { inner.discardRoomKey(roomId) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an active verification
|
||||
*/
|
||||
fun getVerification(flowId: String): Sas? {
|
||||
val sas = this.inner.getVerification(flowId)
|
||||
fun getVerificationRequests(userId: String): List<VerificationRequest> {
|
||||
return this.inner.getVerificationRequests(userId).map {
|
||||
VerificationRequest(this.inner, it)
|
||||
}
|
||||
}
|
||||
|
||||
return if (sas == null) {
|
||||
null
|
||||
} else {
|
||||
Sas(this.inner, sas)
|
||||
}
|
||||
}
|
||||
fun getVerificationRequest(userId: String, flowId: String): VerificationRequest? {
|
||||
val request = this.inner.getVerificationRequest(userId, flowId)
|
||||
|
||||
return if (request == null) {
|
||||
null
|
||||
} else {
|
||||
VerificationRequest(this.inner, request)
|
||||
}
|
||||
}
|
||||
|
||||
/** Get an active verification */
|
||||
fun getVerification(flowId: String): Sas? {
|
||||
val sas = this.inner.getVerification(flowId)
|
||||
|
||||
return if (sas == null) {
|
||||
null
|
||||
} else {
|
||||
Sas(this.inner, sas)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* Copyright 2020 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.verification
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.set
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.internal.crypto.OlmMachine
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationRequest
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import timber.log.Timber
|
||||
import uniffi.olm.OutgoingVerificationRequest
|
||||
|
||||
@SessionScope
|
||||
internal class RustVerificationService
|
||||
@Inject
|
||||
constructor(
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val olmMachine: OlmMachine,
|
||||
private val sendToDeviceTask: SendToDeviceTask,
|
||||
) : DefaultVerificationTransaction.Listener, VerificationService {
|
||||
|
||||
private val uiHandler = Handler(Looper.getMainLooper())
|
||||
private var listeners = ArrayList<VerificationService.Listener>()
|
||||
|
||||
override fun addListener(listener: VerificationService.Listener) {
|
||||
uiHandler.post {
|
||||
if (!listeners.contains(listener)) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeListener(listener: VerificationService.Listener) {
|
||||
uiHandler.post { listeners.remove(listener) }
|
||||
}
|
||||
|
||||
private fun dispatchTxAdded(tx: VerificationTransaction) {
|
||||
uiHandler.post {
|
||||
listeners.forEach {
|
||||
try {
|
||||
it.transactionCreated(tx)
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "## Error while notifying listeners")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun dispatchTxUpdated(tx: VerificationTransaction) {
|
||||
uiHandler.post {
|
||||
listeners.forEach {
|
||||
try {
|
||||
it.transactionUpdated(tx)
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "## Error while notifying listeners")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun dispatchRequestAdded(tx: PendingVerificationRequest) {
|
||||
Timber.v("## SAS dispatchRequestAdded txId:${tx.transactionId} ${tx}")
|
||||
uiHandler.post {
|
||||
listeners.forEach {
|
||||
try {
|
||||
it.verificationRequestCreated(tx)
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "## Error while notifying listeners")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun dispatchRequestUpdated(tx: PendingVerificationRequest) {
|
||||
uiHandler.post {
|
||||
listeners.forEach {
|
||||
try {
|
||||
it.verificationRequestUpdated(tx)
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "## Error while notifying listeners")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
|
||||
TODO()
|
||||
// setDeviceVerificationAction.handle(DeviceTrustLevel(false, true),
|
||||
// userId,
|
||||
// deviceID)
|
||||
|
||||
// listeners.forEach {
|
||||
// try {
|
||||
// it.markedAsManuallyVerified(userId, deviceID)
|
||||
// } catch (e: Throwable) {
|
||||
// Timber.e(e, "## Error while notifying listeners")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
suspend fun onEvent(event: Event) {
|
||||
when (event.getClearType()) {
|
||||
EventType.KEY_VERIFICATION_START -> {}
|
||||
EventType.KEY_VERIFICATION_CANCEL -> {}
|
||||
EventType.KEY_VERIFICATION_ACCEPT -> {}
|
||||
EventType.KEY_VERIFICATION_KEY -> {}
|
||||
EventType.KEY_VERIFICATION_MAC -> {}
|
||||
EventType.KEY_VERIFICATION_READY -> {}
|
||||
EventType.KEY_VERIFICATION_DONE -> {}
|
||||
MessageType.MSGTYPE_VERIFICATION_REQUEST -> {
|
||||
onRequestReceived(event)
|
||||
}
|
||||
else -> {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
event == event
|
||||
// TODO get the sender and flow id out of the event and depending on the
|
||||
// event type either get the verification request or verification and
|
||||
// dispatch updates here
|
||||
}
|
||||
|
||||
private fun onRequestReceived(event: Event) {
|
||||
val content = event.getClearContent().toModel<KeyVerificationRequest>() ?: return
|
||||
val flowId = content.transactionId
|
||||
val sender = event.senderId ?: return
|
||||
|
||||
val request = this.getExistingVerificationRequest(sender, flowId) ?: return
|
||||
|
||||
dispatchRequestAdded(request)
|
||||
}
|
||||
|
||||
override fun onPotentiallyInterestingEventRoomFailToDecrypt(event: Event) {
|
||||
// TODO This should be handled inside the rust-sdk decryption method
|
||||
}
|
||||
|
||||
// TODO All this methods should be delegated to a TransactionStore
|
||||
override fun getExistingTransaction(
|
||||
otherUserId: String,
|
||||
tid: String
|
||||
): VerificationTransaction? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getExistingVerificationRequests(
|
||||
otherUserId: String
|
||||
): List<PendingVerificationRequest> {
|
||||
return this.olmMachine.getVerificationRequests(otherUserId).map {
|
||||
it.toPendingVerificationRequest()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getExistingVerificationRequest(
|
||||
otherUserId: String,
|
||||
tid: String?
|
||||
): PendingVerificationRequest? {
|
||||
return if (tid != null) {
|
||||
val request = this.olmMachine.getVerificationRequest(otherUserId, tid)
|
||||
|
||||
if (request != null) {
|
||||
request.toPendingVerificationRequest()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getExistingVerificationRequestInRoom(
|
||||
roomId: String,
|
||||
tid: String?
|
||||
): PendingVerificationRequest? {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun beginKeyVerification(
|
||||
method: VerificationMethod,
|
||||
otherUserId: String,
|
||||
otherDeviceId: String,
|
||||
transactionId: String?
|
||||
): String? {
|
||||
// should check if already one (and cancel it)
|
||||
if (method == VerificationMethod.SAS) {
|
||||
// TODO start SAS verification here, don't we need to see if there's
|
||||
// a request?
|
||||
TODO()
|
||||
} else {
|
||||
throw IllegalArgumentException("Unknown verification method")
|
||||
}
|
||||
}
|
||||
|
||||
override fun requestKeyVerificationInDMs(
|
||||
methods: List<VerificationMethod>,
|
||||
otherUserId: String,
|
||||
roomId: String,
|
||||
localId: String?
|
||||
): PendingVerificationRequest {
|
||||
Timber.i("## SAS Requesting verification to user: $otherUserId in room $roomId")
|
||||
|
||||
// TODO cancel other active requests, create a new request here and
|
||||
// dispatch it
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun requestKeyVerification(
|
||||
methods: List<VerificationMethod>,
|
||||
otherUserId: String,
|
||||
otherDevices: List<String>?
|
||||
): PendingVerificationRequest {
|
||||
// This was mostly a copy paste of the InDMs method, do the same here
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun cancelVerificationRequest(request: PendingVerificationRequest) {
|
||||
// TODO get the request out of the olm machine and cancel here
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun declineVerificationRequestInDMs(
|
||||
otherUserId: String,
|
||||
transactionId: String,
|
||||
roomId: String
|
||||
) {
|
||||
// TODO get an existing verification request out of the olm machine and
|
||||
// cancel it. update the pending request afterwards
|
||||
}
|
||||
|
||||
override fun beginKeyVerificationInDMs(
|
||||
method: VerificationMethod,
|
||||
transactionId: String,
|
||||
roomId: String,
|
||||
otherUserId: String,
|
||||
otherDeviceId: String
|
||||
): String {
|
||||
// TODO fetch out the verification request nad start SAS, return the
|
||||
// flow id
|
||||
return ""
|
||||
}
|
||||
|
||||
override fun readyPendingVerificationInDMs(
|
||||
methods: List<VerificationMethod>,
|
||||
otherUserId: String,
|
||||
roomId: String,
|
||||
transactionId: String
|
||||
): Boolean {
|
||||
Timber.e("## TRYING TO READY PENDING ROOM VERIFICATION")
|
||||
// TODO do the same as readyPendingVerification
|
||||
return true
|
||||
}
|
||||
|
||||
override fun readyPendingVerification(
|
||||
methods: List<VerificationMethod>,
|
||||
otherUserId: String,
|
||||
transactionId: String
|
||||
): Boolean {
|
||||
val request = this.olmMachine.getVerificationRequest(otherUserId, transactionId)
|
||||
|
||||
return if (request != null) {
|
||||
val outgoingRequest = request.accept_with_methods(methods)
|
||||
|
||||
if (outgoingRequest != null) {
|
||||
runBlocking { sendRequest(outgoingRequest) }
|
||||
dispatchRequestUpdated(request.toPendingVerificationRequest())
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendRequest(request: OutgoingVerificationRequest) {
|
||||
when (request) {
|
||||
is OutgoingVerificationRequest.ToDevice -> {
|
||||
val adapter =
|
||||
MoshiProvider.providesMoshi()
|
||||
.adapter<Map<String, HashMap<String, Any>>>(Map::class.java)
|
||||
val body = adapter.fromJson(request.body)!!
|
||||
|
||||
val userMap = MXUsersDevicesMap<Any>()
|
||||
userMap.join(body)
|
||||
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(request.eventType, userMap)
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
// TODO move this into the VerificationRequest and Verification classes?
|
||||
}
|
||||
|
||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||
dispatchTxUpdated(tx)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue