diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 5a18241534..33b434ee15 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -79,6 +79,7 @@ import org.matrix.android.sdk.internal.crypto.model.event.SecretSendEventContent import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse +import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository @@ -91,6 +92,7 @@ import org.matrix.android.sdk.internal.crypto.tasks.GetDevicesTask import org.matrix.android.sdk.internal.crypto.tasks.NewUploadKeysTask import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask +import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.MoshiProvider @@ -171,6 +173,7 @@ internal class DefaultCryptoService @Inject constructor( private val deleteDeviceWithUserPasswordTask: DeleteDeviceWithUserPasswordTask, // Tasks private val getDevicesTask: GetDevicesTask, + private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask, private val downloadKeysForUsersTask: DownloadKeysForUsersTask, private val getDeviceInfoTask: GetDeviceInfoTask, private val setDeviceNameTask: SetDeviceNameTask, @@ -691,6 +694,7 @@ internal class DefaultCryptoService @Inject constructor( val t0 = System.currentTimeMillis() Timber.v("## CRYPTO | encryptEventContent() starts") runCatching { + preshareGroupSession(roomId, userIds) val content = safeAlgorithm.encryptEventContent(eventContent, eventType, userIds) Timber.v("## CRYPTO | encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms") MXEncryptEventContentResult(content, EventType.ENCRYPTED) @@ -956,6 +960,26 @@ internal class DefaultCryptoService @Inject constructor( olmMachine!!.receiveSyncChanges(toDevice, deviceChanges, keyCounts) } + private suspend fun preshareGroupSession(roomId: String, roomMembers: List) { + val request = olmMachine!!.getMissingSessions(roomMembers) + roomId == "est" + + if (request != null) { + when (request) { + is Request.KeysClaim -> { + val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(request.oneTimeKeys) + val response = oneTimeKeysForUsersDeviceTask.execute(claimParams) + val adapter = MoshiProvider.providesMoshi().adapter(KeysClaimResponse::class.java) + val json_response = adapter.toJson(response)!! + olmMachine!!.markRequestAsSent(request.requestId, RequestType.KEYS_CLAIM, json_response) + } + } + } + } + + // private suspend fun encrypt(roomId: String, eventType: String, content: Content) { + // } + private suspend fun sendOutgoingRequests() { // TODO these requests should be sent out in parallel for (outgoingRequest in olmMachine!!.outgoingRequests()) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt index 95b99c54e8..5bb9967581 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt @@ -50,56 +50,56 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor( } } - if (devicesWithoutSession.size == 0) { - return results - } + //if (devicesWithoutSession.size == 0) { + // return results + //} - // Prepare the request for claiming one-time keys - val usersDevicesToClaim = MXUsersDevicesMap() + //// Prepare the request for claiming one-time keys + //val usersDevicesToClaim = MXUsersDevicesMap() - val oneTimeKeyAlgorithm = MXKey.KEY_SIGNED_CURVE_25519_TYPE + //val oneTimeKeyAlgorithm = MXKey.KEY_SIGNED_CURVE_25519_TYPE - for (device in devicesWithoutSession) { - usersDevicesToClaim.setObject(device.userId, device.deviceId, oneTimeKeyAlgorithm) - } + //for (device in devicesWithoutSession) { + // usersDevicesToClaim.setObject(device.userId, device.deviceId, oneTimeKeyAlgorithm) + //} - // TODO: this has a race condition - if we try to send another message - // while we are claiming a key, we will end up claiming two and setting up - // two sessions. - // - // That should eventually resolve itself, but it's poor form. + //// TODO: this has a race condition - if we try to send another message + //// while we are claiming a key, we will end up claiming two and setting up + //// two sessions. + //// + //// That should eventually resolve itself, but it's poor form. - Timber.i("## CRYPTO | claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") + //Timber.i("## CRYPTO | claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") - val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim) - val oneTimeKeys = oneTimeKeysForUsersDeviceTask.execute(claimParams) - Timber.v("## CRYPTO | claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys") - for ((userId, deviceInfos) in devicesByUser) { - for (deviceInfo in deviceInfos) { - var oneTimeKey: MXKey? = null - val deviceIds = oneTimeKeys.getUserDeviceIds(userId) - if (null != deviceIds) { - for (deviceId in deviceIds) { - val olmSessionResult = results.getObject(userId, deviceId) - if (olmSessionResult!!.sessionId != null && !force) { - // We already have a result for this device - continue - } - val key = oneTimeKeys.getObject(userId, deviceId) - if (key?.type == oneTimeKeyAlgorithm) { - oneTimeKey = key - } - if (oneTimeKey == null) { - Timber.w("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm - + " for device " + userId + " : " + deviceId) - continue - } - // Update the result for this device in results - olmSessionResult.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) - } - } - } - } + //val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim) + //val oneTimeKeys = oneTimeKeysForUsersDeviceTask.execute(claimParams) + //Timber.v("## CRYPTO | claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys") + //for ((userId, deviceInfos) in devicesByUser) { + // for (deviceInfo in deviceInfos) { + // var oneTimeKey: MXKey? = null + // val deviceIds = oneTimeKeys.getUserDeviceIds(userId) + // if (null != deviceIds) { + // for (deviceId in deviceIds) { + // val olmSessionResult = results.getObject(userId, deviceId) + // if (olmSessionResult!!.sessionId != null && !force) { + // // We already have a result for this device + // continue + // } + // val key = oneTimeKeys.getObject(userId, deviceId) + // if (key?.type == oneTimeKeyAlgorithm) { + // oneTimeKey = key + // } + // if (oneTimeKey == null) { + // Timber.w("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm + // + " for device " + userId + " : " + deviceId) + // continue + // } + // // Update the result for this device in results + // olmSessionResult.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) + // } + // } + // } + //} return results } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/KeysClaimResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/KeysClaimResponse.kt index 22f4ce5a59..2dfdfe09a8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/KeysClaimResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/KeysClaimResponse.kt @@ -24,6 +24,11 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) internal data class KeysClaimResponse( + /// If any remote homeservers could not be reached, they are recorded here. + /// The names of the properties are the names of the unreachable servers. + @Json(name = "failures") + val failures: Map, + /** * The requested keys ordered by device by user. * TODO Type does not match spec, should be Map diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt index 3df6312adb..68e366ae03 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt @@ -27,10 +27,10 @@ import org.matrix.android.sdk.internal.task.Task import timber.log.Timber import javax.inject.Inject -internal interface ClaimOneTimeKeysForUsersDeviceTask : Task> { +internal interface ClaimOneTimeKeysForUsersDeviceTask : Task { data class Params( // a list of users, devices and key types to retrieve keys for. - val usersDevicesKeyTypesMap: MXUsersDevicesMap + val usersDevicesKeyTypesMap: Map> ) } @@ -39,26 +39,11 @@ internal class DefaultClaimOneTimeKeysForUsersDevice @Inject constructor( private val globalErrorReceiver: GlobalErrorReceiver ) : ClaimOneTimeKeysForUsersDeviceTask { - override suspend fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): MXUsersDevicesMap { - val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map) + override suspend fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): KeysClaimResponse { + val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap) - val keysClaimResponse = executeRequest(globalErrorReceiver) { + return executeRequest(globalErrorReceiver) { apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body) } - val map = MXUsersDevicesMap() - keysClaimResponse.oneTimeKeys?.let { oneTimeKeys -> - for ((userId, mapByUserId) in oneTimeKeys) { - for ((deviceId, deviceKey) in mapByUserId) { - val mxKey = MXKey.from(deviceKey) - - if (mxKey != null) { - map.setObject(userId, deviceId, mxKey) - } else { - Timber.e("## claimOneTimeKeysForUsersDevices : fail to create a MXKey") - } - } - } - } - return map } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/newCrypto.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/newCrypto.kt index d6fb5035a8..21c463c08f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/newCrypto.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/newCrypto.kt @@ -87,6 +87,10 @@ internal class OlmMachine(user_id: String, device_id: String, path: File) { inner.outgoingRequests() } + suspend fun getMissingSessions(users: List): Request? = withContext(Dispatchers.IO) { + inner.getMissingSessions(users) + } + suspend fun updateTrackedUsers(users: List) = withContext(Dispatchers.IO) { inner.updateTrackedUsers(users) } diff --git a/rust-sdk/src/machine.rs b/rust-sdk/src/machine.rs index 4f0b8b0b12..77c5cc064b 100644 --- a/rust-sdk/src/machine.rs +++ b/rust-sdk/src/machine.rs @@ -10,10 +10,12 @@ use tokio::runtime::Runtime; use matrix_sdk_common::{ api::r0::{ keys::{ - claim_keys::Response as KeysClaimResponse, get_keys::Response as KeysQueryResponse, + claim_keys::{Request as KeysClaimRequest, Response as KeysClaimResponse}, + get_keys::Response as KeysQueryResponse, upload_keys::Response as KeysUploadResponse, }, sync::sync_events::{DeviceLists as RumaDeviceLists, ToDevice}, + to_device::send_event_to_device::Response as ToDeviceResponse, }, assign, deserialized_responses::events::{AlgorithmInfo, SyncMessageEvent}, @@ -67,6 +69,7 @@ enum OwnedResponse { KeysClaim(KeysClaimResponse), KeysUpload(KeysUploadResponse), KeysQuery(KeysQueryResponse), + ToDevice(ToDeviceResponse), } impl From for OwnedResponse { @@ -87,18 +90,26 @@ impl From for OwnedResponse { } } +impl From for OwnedResponse { + fn from(response: ToDeviceResponse) -> Self { + OwnedResponse::ToDevice(response) + } +} + impl<'a> Into> for &'a OwnedResponse { fn into(self) -> IncomingResponse<'a> { match self { OwnedResponse::KeysClaim(r) => IncomingResponse::KeysClaim(r), OwnedResponse::KeysQuery(r) => IncomingResponse::KeysQuery(r), OwnedResponse::KeysUpload(r) => IncomingResponse::KeysUpload(r), + OwnedResponse::ToDevice(r) => IncomingResponse::ToDevice(r), } } } pub enum RequestType { KeysQuery, + KeysClaim, KeysUpload, ToDevice, } @@ -130,6 +141,10 @@ pub enum Request { request_id: String, users: Vec, }, + KeysClaim { + request_id: String, + one_time_keys: HashMap>, + }, } impl From for Request { @@ -268,7 +283,8 @@ impl OlmMachine { let response: OwnedResponse = match request_type { RequestType::KeysUpload => KeysUploadResponse::try_from(response).map(Into::into), RequestType::KeysQuery => KeysQueryResponse::try_from(response).map(Into::into), - RequestType::ToDevice => KeysClaimResponse::try_from(response).map(Into::into), + RequestType::ToDevice => ToDeviceResponse::try_from(response).map(Into::into), + RequestType::KeysClaim => KeysClaimResponse::try_from(response).map(Into::into), } .expect("Can't convert json string to response"); @@ -342,6 +358,35 @@ impl OlmMachine { .block_on(self.inner.update_tracked_users(users.iter())); } + pub fn get_missing_sessions( + &self, + users: Vec, + ) -> Result, CryptoStoreError> { + let users: Vec = users + .into_iter() + .filter_map(|u| UserId::try_from(u).ok()) + .collect(); + + Ok(self + .runtime + .block_on(self.inner.get_missing_sessions(users.iter()))? + .map(|(request_id, request)| Request::KeysClaim { + request_id: request_id.to_string(), + one_time_keys: request + .one_time_keys + .into_iter() + .map(|(u, d)| { + ( + u.to_string(), + d.into_iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(), + ) + }) + .collect(), + })) + } + pub fn decrypt_room_event( &self, event: &str, diff --git a/rust-sdk/src/olm.udl b/rust-sdk/src/olm.udl index 0d38c07081..d534a1cb36 100644 --- a/rust-sdk/src/olm.udl +++ b/rust-sdk/src/olm.udl @@ -55,10 +55,12 @@ interface Request { ToDevice(string request_id, string event_type, string body); KeysUpload(string request_id, string body); KeysQuery(string request_id, sequence users); + KeysClaim(string request_id, record> one_time_keys); }; enum RequestType { "KeysQuery", + "KeysClaim", "KeysUpload", "ToDevice", }; @@ -85,6 +87,9 @@ interface OlmMachine { sequence outgoing_requests(); void update_tracked_users(sequence users); + [Throws=CryptoStoreError] + Request? get_missing_sessions(sequence users); + [Throws=CryptoStoreError] void mark_request_as_sent( [ByRef] string request_id,