From 87dec337d85c1566f714e3f25bd7aed7dc954372 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 5 Jul 2019 14:28:28 +0200 Subject: [PATCH 1/7] Rework Crypto using Try --- .../matrix/android/api/failure/Failure.kt | 2 +- .../api/session/crypto/CryptoService.kt | 4 +- .../api/session/crypto/MXCryptoError.kt | 68 +----- .../android/api/session/events/model/Event.kt | 45 ++-- .../android/internal/crypto/CryptoManager.kt | 30 ++- .../internal/crypto/MXDecryptionException.kt | 39 ---- .../crypto/MXEventDecryptionResult.kt | 10 +- .../android/internal/crypto/MXOlmDevice.kt | 209 +++++++++--------- .../crypto/algorithms/IMXDecrypting.kt | 8 +- .../crypto/algorithms/IMXEncrypting.kt | 1 - .../algorithms/megolm/MXMegolmDecryption.kt | 119 +++++----- .../algorithms/megolm/MXMegolmEncryption.kt | 45 ++-- .../crypto/algorithms/olm/MXOlmDecryption.kt | 80 ++++--- .../OlmDecryptionResult.kt} | 14 +- .../model/MXEncryptEventContentResult.kt | 2 +- .../room/timeline/TimelineEventFactory.kt | 14 +- .../session/sync/CryptoSyncHandler.kt | 9 +- .../timeline/factory/EncryptedItemFactory.kt | 15 +- 18 files changed, 308 insertions(+), 406 deletions(-) delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXDecryptionException.kt mode change 100644 => 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEventDecryptionResult.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/{MXDecryptionResult.kt => olm/OlmDecryptionResult.kt} (79%) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt index 593c33b933..152adb0665 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt @@ -38,7 +38,7 @@ sealed class Failure(cause: Throwable? = null) : Throwable(cause = cause) { data class RegistrationFlowError(val registrationFlowResponse: RegistrationFlowResponse) : Failure(RuntimeException(registrationFlowResponse.toString())) - data class CryptoError(val error: MXCryptoError) : Failure(RuntimeException(error.toString())) + data class CryptoError(val error: MXCryptoError) : Failure(error) abstract class FeatureFailure : Failure() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt index a3ece1ab1c..31458f2fc7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt @@ -98,9 +98,9 @@ interface CryptoService { roomId: String, callback: MatrixCallback) - fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? + fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult - fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback) + fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback) fun getEncryptionAlgorithm(roomId: String): String? diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt index 1e01350c4d..fbbacb4afb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt @@ -18,72 +18,28 @@ package im.vector.matrix.android.api.session.crypto -import android.text.TextUtils +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap /** * Represents a crypto error response. */ -class MXCryptoError(var code: String, - var message: String) { +sealed class MXCryptoError : Throwable() { - /** - * Describe the error with more details - */ - private var mDetailedErrorDescription: String? = null + // TODO Create data class for all cases, and remove error code + data class Base(val code: String, + val _message: String, + /** + * Describe the error with more details + */ + val detailedErrorDescription: String? = null) : MXCryptoError() - /** - * Data exception. - * Some exceptions provide some data to describe the exception - */ - var mExceptionData: Any? = null - - /** - * @return true if the current error is an olm one. - */ - val isOlmError: Boolean - get() = OLM_ERROR_CODE == code - - - /** - * @return the detailed error description - */ - val detailedErrorDescription: String? - get() = if (TextUtils.isEmpty(mDetailedErrorDescription)) { - message - } else mDetailedErrorDescription - - /** - * Create a crypto error - * - * @param code the error code (see XX_ERROR_CODE) - * @param shortErrorDescription the short error description - * @param detailedErrorDescription the detailed error description - */ - constructor(code: String, shortErrorDescription: String, detailedErrorDescription: String?) : this(code, shortErrorDescription) { - mDetailedErrorDescription = detailedErrorDescription - } - - /** - * Create a crypto error - * - * @param code the error code (see XX_ERROR_CODE) - * @param shortErrorDescription the short error description - * @param detailedErrorDescription the detailed error description - * @param exceptionData the exception data - */ - constructor(code: String, shortErrorDescription: String, detailedErrorDescription: String?, exceptionData: Any) : this(code, shortErrorDescription) { - mDetailedErrorDescription = detailedErrorDescription - mExceptionData = exceptionData - } + data class UnknownDevice(val deviceList: MXUsersDevicesMap) : MXCryptoError() companion object { - - // TODO Create sealed class - /** * Error codes */ - const val UNKNOWN_ERROR_CODE = "UNKNOWN_ERROR_CODE" const val ENCRYPTING_NOT_ENABLED_ERROR_CODE = "ENCRYPTING_NOT_ENABLED" const val UNABLE_TO_ENCRYPT_ERROR_CODE = "UNABLE_TO_ENCRYPT" const val UNABLE_TO_DECRYPT_ERROR_CODE = "UNABLE_TO_DECRYPT" @@ -138,4 +94,4 @@ class MXCryptoError(var code: String, const val UNKNOWN_DEVICES_REASON = "This room contains unknown devices which have not been verified.\n" + "We strongly recommend you verify them before continuing." const val NO_MORE_ALGORITHM_REASON = "Room was previously configured to use encryption, but is no longer." + " Perhaps the homeserver is hiding the configuration event." } -} +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt index 26587359ab..f02b7d31ab 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt @@ -140,37 +140,32 @@ data class Event( * * @param decryptionResult the decryption result, including the plaintext and some key info. */ - internal fun setClearData(decryptionResult: MXEventDecryptionResult?) { + internal fun setClearData(decryptionResult: MXEventDecryptionResult) { mClearEvent = null - if (decryptionResult != null) { - if (decryptionResult.clearEvent != null) { - val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java) - mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent) + mCryptoError = null - if (mClearEvent != null) { - mSenderCurve25519Key = decryptionResult.senderCurve25519Key - mClaimedEd25519Key = decryptionResult.claimedEd25519Key - mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain + val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java) + mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent) - // For encrypted events with relation, the m.relates_to is kept in clear, so we need to put it back - // in the clear event - try { - content?.get("m.relates_to")?.let { clearRelates -> - mClearEvent = mClearEvent?.copy( - content = HashMap(mClearEvent!!.content).apply { - this["m.relates_to"] = clearRelates - } - ) - } - } catch (e: Exception) { - Timber.e(e, "Unable to restore 'm.relates_to' the clear event") - } + if (mClearEvent != null) { + mSenderCurve25519Key = decryptionResult.senderCurve25519Key + mClaimedEd25519Key = decryptionResult.claimedEd25519Key + mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain + + // For encrypted events with relation, the m.relates_to is kept in clear, so we need to put it back + // in the clear event + try { + content?.get("m.relates_to")?.let { clearRelates -> + mClearEvent = mClearEvent?.copy( + content = HashMap(mClearEvent!!.content).apply { + this["m.relates_to"] = clearRelates + } + ) } - - + } catch (e: Exception) { + Timber.e(e, "Unable to restore 'm.relates_to' the clear event") } } - mCryptoError = null } /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt index 154bfbc96b..51a0d8c57b 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt @@ -569,8 +569,7 @@ internal class CryptoManager @Inject constructor( val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON) Timber.e("## encryptEventContent() : $reason") - callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNABLE_TO_ENCRYPT_ERROR_CODE, - MXCryptoError.UNABLE_TO_ENCRYPT, reason))) + callback.onFailure(Failure.CryptoError(MXCryptoError.Base(MXCryptoError.UNABLE_TO_ENCRYPT_ERROR_CODE, reason))) } } } @@ -580,10 +579,10 @@ internal class CryptoManager @Inject constructor( * * @param event the raw event. * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. - * @return the MXEventDecryptionResult data, or null in case of error + * @return the MXEventDecryptionResult data, or throw in case of error */ - @Throws(MXDecryptionException::class) - override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { + @Throws(MXCryptoError::class) + override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { return runBlocking { internalDecryptEvent(event, timeline).fold( { throw it }, @@ -599,7 +598,7 @@ internal class CryptoManager @Inject constructor( * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. * @param callback the callback to return data or null */ - override fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback) { + override fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback) { GlobalScope.launch(EmptyCoroutineContext) { val result = withContext(coroutineDispatchers.crypto) { internalDecryptEvent(event, timeline) @@ -616,18 +615,17 @@ internal class CryptoManager @Inject constructor( * @return the MXEventDecryptionResult data, or null in case of error wrapped into [Try] */ private suspend fun internalDecryptEvent(event: Event, timeline: String): Try { - return Try { - val eventContent = event.content - if (eventContent == null) { - Timber.e("## decryptEvent : empty event content") - throw MXDecryptionException(MXCryptoError(MXCryptoError.UNKNOWN_ERROR_CODE, MXCryptoError.UNKNOWN_ERROR_CODE)) - } + val eventContent = event.content + return if (eventContent == null) { + Timber.e("## decryptEvent : empty event content") + Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + } else { val algorithm = eventContent["algorithm"]?.toString() val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm) if (alg == null) { val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm) Timber.e("## decryptEvent() : $reason") - throw MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, MXCryptoError.UNABLE_TO_DECRYPT, reason)) + Try.Failure(MXCryptoError.Base(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, reason)) } else { alg.decryptEvent(event, timeline) } @@ -872,7 +870,7 @@ internal class CryptoManager @Inject constructor( /** * Check if the user ids list have some unknown devices. * A success means there is no unknown devices. - * If there are some unknown devices, a MXCryptoError.UNKNOWN_DEVICES_CODE exception is triggered. + * If there are some unknown devices, a MXCryptoError.UnknownDevice exception is triggered. * * @param userIds the user ids list * @param callback the asynchronous callback. @@ -890,9 +888,7 @@ internal class CryptoManager @Inject constructor( callback.onSuccess(Unit) } else { // trigger an an unknown devices exception - callback.onFailure( - Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE, - MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices))) + callback.onFailure(Failure.CryptoError(MXCryptoError.UnknownDevice(unknownDevices))) } } ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXDecryptionException.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXDecryptionException.kt deleted file mode 100644 index b0d91c21de..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXDecryptionException.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2016 OpenMarket Ltd - * Copyright 2017 Vector Creations Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.matrix.android.internal.crypto - -import im.vector.matrix.android.api.session.crypto.MXCryptoError - -/** - * This class represents a decryption exception - */ -class MXDecryptionException -( - /** - * the linked crypto error - */ - val cryptoError: MXCryptoError? -) : Exception() { - - override val message: String? - get() = cryptoError?.message ?: super.message - - override fun getLocalizedMessage(): String { - return cryptoError?.message ?: super.getLocalizedMessage() - } -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEventDecryptionResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEventDecryptionResult.kt old mode 100644 new mode 100755 index a0c44382db..332ca86097 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEventDecryptionResult.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEventDecryptionResult.kt @@ -1,6 +1,5 @@ /* * Copyright 2016 OpenMarket Ltd - * Copyright 2017 Vector Creations Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +17,6 @@ package im.vector.matrix.android.internal.crypto import im.vector.matrix.android.api.util.JsonDict -import java.util.* /** * The result of a (successful) call to decryptEvent. @@ -28,23 +26,23 @@ data class MXEventDecryptionResult( /** * The plaintext payload for the event (typically containing "type" and "content" fields). */ - var clearEvent: JsonDict? = null, + val clearEvent: JsonDict, /** * Key owned by the sender of this event. * See MXEvent.senderKey. */ - var senderCurve25519Key: String? = null, + val senderCurve25519Key: String? = null, /** * Ed25519 key claimed by the sender of this event. * See MXEvent.claimedEd25519Key. */ - var claimedEd25519Key: String? = null, + val claimedEd25519Key: String? = null, /** * List of curve25519 keys involved in telling us about the senderCurve25519Key and * claimedEd25519Key. See MXEvent.forwardingCurve25519KeyChain. */ - var forwardingCurve25519KeyChain: List = ArrayList() + val forwardingCurve25519KeyChain: List = emptyList() ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt index 7fc9cc5173..daf8c6739c 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt @@ -18,10 +18,11 @@ package im.vector.matrix.android.internal.crypto import android.text.TextUtils +import arrow.core.Try import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE import im.vector.matrix.android.api.util.JsonDict -import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult +import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore @@ -81,11 +82,6 @@ internal class MXOlmDevice @Inject constructor( // Values are true. private val inboundGroupSessionMessageIndexes: MutableMap> = HashMap() - /** - * inboundGroupSessionWithId error - */ - private var inboundGroupSessionWithIdError: MXCryptoError? = null - init { // Retrieve the account from the store olmAccount = store.getAccount() @@ -508,22 +504,26 @@ internal class MXOlmDevice @Inject constructor( forwardingCurve25519KeyChain: List, keysClaimed: Map, exportFormat: Boolean): Boolean { - val existingInboundSession = getInboundGroupSession(sessionId, senderKey, roomId) val session = OlmInboundGroupSessionWrapper(sessionKey, exportFormat) - if (null != existingInboundSession) { - // If we already have this session, consider updating it - Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId") + getInboundGroupSession(sessionId, senderKey, roomId).fold( + { + // Nothing to do in case of error + }, + { + // If we already have this session, consider updating it + Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId") - val existingFirstKnown = existingInboundSession.firstKnownIndex!! - val newKnownFirstIndex = session.firstKnownIndex!! + val existingFirstKnown = it.firstKnownIndex!! + val newKnownFirstIndex = session.firstKnownIndex - //If our existing session is better we keep it - if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) { - session.olmInboundGroupSession?.releaseSession() - return false - } - } + //If our existing session is better we keep it + if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) { + session.olmInboundGroupSession?.releaseSession() + return false + } + } + ) // sanity check if (null == session.olmInboundGroupSession) { @@ -594,20 +594,26 @@ internal class MXOlmDevice @Inject constructor( continue } - val existingOlmSession = getInboundGroupSession(sessionId, senderKey, roomId) - if (null != existingOlmSession) { - // If we already have this session, consider updating it - Timber.e("## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId") + getInboundGroupSession(sessionId, senderKey, roomId) + .fold( + { + // Session does not already exist, add it + sessions.add(session) + }, + { + // If we already have this session, consider updating it + Timber.e("## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId") - // For now we just ignore updates. TODO: implement something here - if (existingOlmSession.firstKnownIndex!! <= session.firstKnownIndex!!) { - //Ignore this, keep existing - session.olmInboundGroupSession!!.releaseSession() - continue - } - } - - sessions.add(session) + // For now we just ignore updates. TODO: implement something here + if (it.firstKnownIndex!! <= session.firstKnownIndex!!) { + //Ignore this, keep existing + session.olmInboundGroupSession!!.releaseSession() + } else { + sessions.add(session) + } + Unit + } + ) } store.storeInboundGroupSessions(sessions) @@ -637,81 +643,70 @@ internal class MXOlmDevice @Inject constructor( * @param senderKey the base64-encoded curve25519 key of the sender. * @return the decrypting result. Nil if the sessionId is unknown. */ - @Throws(MXDecryptionException::class) fun decryptGroupMessage(body: String, roomId: String, timeline: String?, sessionId: String, - senderKey: String): MXDecryptionResult? { - val result = MXDecryptionResult() - val session = getInboundGroupSession(sessionId, senderKey, roomId) - - if (null != session) { - // Check that the room id matches the original one for the session. This stops - // the HS pretending a message was targeting a different room. - if (TextUtils.equals(roomId, session.roomId)) { - var errorMessage = "" - var decryptResult: OlmInboundGroupSession.DecryptMessageResult? = null - try { - decryptResult = session.olmInboundGroupSession!!.decryptMessage(body) - } catch (e: Exception) { - Timber.e(e, "## decryptGroupMessage () : decryptMessage failed") - errorMessage = e.message ?: "" - } - - if (null != decryptResult) { - if (null != timeline) { - if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) { - inboundGroupSessionMessageIndexes[timeline] = HashMap() + senderKey: String): Try { + return getInboundGroupSession(sessionId, senderKey, roomId) + .flatMap { session -> + // Check that the room id matches the original one for the session. This stops + // the HS pretending a message was targeting a different room. + if (roomId == session.roomId) { + var errorMessage = "" + var decryptResult: OlmInboundGroupSession.DecryptMessageResult? = null + try { + decryptResult = session.olmInboundGroupSession!!.decryptMessage(body) + } catch (e: Exception) { + Timber.e(e, "## decryptGroupMessage () : decryptMessage failed") + errorMessage = e.message ?: "" } - val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex + if (null != decryptResult) { + if (null != timeline) { + if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) { + inboundGroupSessionMessageIndexes[timeline] = HashMap() + } - if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) { - val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) - Timber.e("## decryptGroupMessage() : $reason") - throw MXDecryptionException(MXCryptoError(MXCryptoError.DUPLICATED_MESSAGE_INDEX_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, reason)) + val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex + + if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) { + val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) + Timber.e("## decryptGroupMessage() : $reason") + return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.DUPLICATED_MESSAGE_INDEX_ERROR_CODE, reason)) + } + + inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) + } + + store.storeInboundGroupSessions(listOf(session)) + val payload = try { + val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) + val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) + adapter.fromJson(payloadString) + } catch (e: Exception) { + Timber.e("## decryptGroupMessage() : fails to parse the payload") + return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_DECRYPTED_FORMAT_ERROR_CODE, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) + } + + return@flatMap Try.just( + OlmDecryptionResult( + payload, + session.keysClaimed, + senderKey, + session.forwardingCurve25519KeyChain + ) + ) + } else { + Timber.e("## decryptGroupMessage() : failed to decode the message") + return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.OLM_ERROR_CODE, errorMessage)) } - - inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) + } else { + val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) + Timber.e("## decryptGroupMessage() : $reason") + return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, reason)) } - - store.storeInboundGroupSessions(listOf(session)) - try { - val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) - val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) - val payload = adapter.fromJson(payloadString) - result.payload = payload - } catch (e: Exception) { - Timber.e(e, "## decryptGroupMessage() : RLEncoder.encode failed " + e.message) - return null - } - - if (null == result.payload) { - Timber.e("## decryptGroupMessage() : fails to parse the payload") - return null - } - - result.keysClaimed = session.keysClaimed - result.senderKey = senderKey - result.forwardingCurve25519KeyChain = session.forwardingCurve25519KeyChain - } else { - Timber.e("## decryptGroupMessage() : failed to decode the message") - throw MXDecryptionException(MXCryptoError(MXCryptoError.OLM_ERROR_CODE, errorMessage, null)) } - } else { - val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) - Timber.e("## decryptGroupMessage() : $reason") - throw MXDecryptionException(MXCryptoError(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, reason)) - } - } else { - Timber.e("## decryptGroupMessage() : Cannot retrieve inbound group session $sessionId") - throw MXDecryptionException(inboundGroupSessionWithIdError) - } - - return result } /** @@ -725,7 +720,7 @@ internal class MXOlmDevice @Inject constructor( } } - // Utilities +// Utilities /** * Verify an ed25519 signature on a JSON object. @@ -775,27 +770,27 @@ internal class MXOlmDevice @Inject constructor( * @param senderKey the base64-encoded curve25519 key of the sender. * @return the inbound group session. */ - fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): OlmInboundGroupSessionWrapper? { - inboundGroupSessionWithIdError = null + fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): Try { + if (sessionId.isNullOrBlank() || senderKey.isNullOrBlank()) { + return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_SENDER_KEY_ERROR_CODE, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)) + } - if (sessionId.isNullOrBlank() || senderKey.isNullOrBlank()) return null val session = store.getInboundGroupSession(sessionId, senderKey) - if (null != session) { + return if (null != session) { // Check that the room id matches the original one for the session. This stops // the HS pretending a message was targeting a different room. if (!TextUtils.equals(roomId, session.roomId)) { val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) Timber.e("## getInboundGroupSession() : $errorDescription") - inboundGroupSessionWithIdError = MXCryptoError(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, errorDescription) + Try.Failure(MXCryptoError.Base(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, errorDescription)) + } else { + Try.just(session) } } else { Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId") - inboundGroupSessionWithIdError = MXCryptoError(MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE, - MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON, null) + Try.Failure(MXCryptoError.Base(MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)) } - return session } /** @@ -807,6 +802,6 @@ internal class MXOlmDevice @Inject constructor( * @return true if the unbound session keys are known. */ fun hasInboundSessionKeys(roomId: String, senderKey: String, sessionId: String): Boolean { - return null != getInboundGroupSession(sessionId, senderKey, roomId) + return getInboundGroupSession(sessionId, senderKey, roomId).isSuccess() } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt index c21ccbdb8b..8714e156e8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt @@ -17,9 +17,9 @@ package im.vector.matrix.android.internal.crypto.algorithms +import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest -import im.vector.matrix.android.internal.crypto.MXDecryptionException import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup @@ -33,11 +33,9 @@ internal interface IMXDecrypting { * * @param event the raw event. * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. - * @return the decryption information, or null in case of error - * @throws MXDecryptionException the decryption failure reason + * @return the decryption information, or an error */ - @Throws(MXDecryptionException::class) - suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult + suspend fun decryptEvent(event: Event, timeline: String): Try /** * Handle a key event. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt index 5e9d305e0e..544bbe6032 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.algorithms import arrow.core.Try -import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.events.model.Content /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 42418c0a2c..ee8e014552 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -28,7 +28,6 @@ import im.vector.matrix.android.internal.crypto.* import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting -import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap @@ -64,66 +63,65 @@ internal class MXMegolmDecryption(private val credentials: Credentials, */ private var pendingEvents: MutableMap>> = HashMap() - @Throws(MXDecryptionException::class) - override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { + override suspend fun decryptEvent(event: Event, timeline: String): Try { return decryptEvent(event, timeline, true) } - @Throws(MXDecryptionException::class) - private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult { - val encryptedEventContent = event.content.toModel()!! - if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) { - throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_FIELDS_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_FIELDS_REASON)) + private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): Try { + val encryptedEventContent = event.content.toModel() + ?: throw MXCryptoError.Base(MXCryptoError.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON) + + if (TextUtils.isEmpty(encryptedEventContent.senderKey) + || TextUtils.isEmpty(encryptedEventContent.sessionId) + || TextUtils.isEmpty(encryptedEventContent.ciphertext)) { + throw MXCryptoError.Base(MXCryptoError.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON) } - var cryptoError: MXCryptoError? = null - var decryptGroupMessageResult: MXDecryptionResult? = null + return olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext!!, event.roomId!!, timeline, encryptedEventContent.sessionId!!, encryptedEventContent.senderKey!!) + .fold( + { throwable -> + if (throwable is MXCryptoError.Base) { + if (throwable.code == MXCryptoError.OLM_ERROR_CODE) { + if (MXCryptoError.UNKNOWN_MESSAGE_INDEX == throwable._message) { + addEventToPendingList(event, timeline) + if (requestKeysOnFail) { + requestKeysForEvent(event) + } + } - try { - decryptGroupMessageResult = olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext!!, event.roomId!!, timeline, encryptedEventContent.sessionId!!, encryptedEventContent.senderKey!!) - } catch (e: MXDecryptionException) { - cryptoError = e.cryptoError - } - // the decryption succeeds - if (decryptGroupMessageResult?.payload != null) { - val eventDecryptionResult = MXEventDecryptionResult() + val reason = String.format(MXCryptoError.OLM_REASON, throwable._message) + val detailedReason = String.format(MXCryptoError.DETAILLED_OLM_REASON, encryptedEventContent.ciphertext, throwable._message) - eventDecryptionResult.clearEvent = decryptGroupMessageResult.payload - eventDecryptionResult.senderCurve25519Key = decryptGroupMessageResult.senderKey + throw MXCryptoError.Base( + MXCryptoError.OLM_ERROR_CODE, + reason, + detailedReason) + } else if (throwable.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { + addEventToPendingList(event, timeline) + if (requestKeysOnFail) { + requestKeysForEvent(event) + } + } + } - if (null != decryptGroupMessageResult.keysClaimed) { - eventDecryptionResult.claimedEd25519Key = decryptGroupMessageResult.keysClaimed?.get("ed25519") - } - - eventDecryptionResult.forwardingCurve25519KeyChain = decryptGroupMessageResult.forwardingCurve25519KeyChain - ?: emptyList() - return eventDecryptionResult - } else if (cryptoError != null) { - if (cryptoError.isOlmError) { - if (MXCryptoError.UNKNOWN_MESSAGE_INDEX == cryptoError.message) { - addEventToPendingList(event, timeline) - if (requestKeysOnFail) { - requestKeysForEvent(event) - } - } - - val reason = String.format(MXCryptoError.OLM_REASON, cryptoError.message) - val detailedReason = String.format(MXCryptoError.DETAILLED_OLM_REASON, encryptedEventContent.ciphertext, cryptoError.message) - - throw MXDecryptionException(MXCryptoError( - MXCryptoError.OLM_ERROR_CODE, - reason, - detailedReason)) - } else if (TextUtils.equals(cryptoError.code, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE)) { - addEventToPendingList(event, timeline) - if (requestKeysOnFail) { - requestKeysForEvent(event) - } - throw MXDecryptionException(cryptoError) - } - } - throw MXDecryptionException(MXCryptoError(MXCryptoError.UNKNOWN_ERROR_CODE, MXCryptoError.UNKNOWN_ERROR_CODE)) + throw throwable + }, + { decryptionResult -> + // the decryption succeeds + if (decryptionResult.payload != null) { + return Try.just( + MXEventDecryptionResult( + clearEvent = decryptionResult.payload, + senderCurve25519Key = decryptionResult.senderKey, + claimedEd25519Key = decryptionResult.keysClaimed?.get("ed25519"), + forwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain ?: emptyList() + ) + ) + } else { + throw MXCryptoError.Base(MXCryptoError.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON) + } + } + ) } @@ -319,11 +317,20 @@ internal class MXMegolmDecryption(private val credentials: Credentials, Try.just(Unit) } Timber.v("""## shareKeysWithDevice() : sharing keys for session ${body?.senderKey}|${body?.sessionId} with device $userId:$deviceId""") - val inboundGroupSession = olmDevice.getInboundGroupSession(body?.sessionId, body?.senderKey, body?.roomId) val payloadJson = HashMap() payloadJson["type"] = EventType.FORWARDED_ROOM_KEY - payloadJson["content"] = inboundGroupSession!!.exportKeys()!! + + olmDevice.getInboundGroupSession(body?.sessionId, body?.senderKey, body?.roomId) + .fold( + { + // TODO + }, + { + // TODO + payloadJson["content"] = it.exportKeys() ?: "" + } + ) val encodedPayload = messageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo)) val sendToDeviceMap = MXUsersDevicesMap() @@ -332,8 +339,6 @@ internal class MXMegolmDecryption(private val credentials: Credentials, val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) sendToDeviceTask.execute(sendToDeviceParams) } - - } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index c816cbb60e..ca9a105eaa 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -21,7 +21,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm import android.text.TextUtils import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials -import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.EventType @@ -254,29 +253,31 @@ internal class MXMegolmEncryption( /** * process the pending encryptions */ - private suspend fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content) = Try { - // Everything is in place, encrypt all pending events - val payloadJson = HashMap() - payloadJson["room_id"] = roomId - payloadJson["type"] = eventType - payloadJson["content"] = eventContent + private fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content): Try { + return Try { + // Everything is in place, encrypt all pending events + val payloadJson = HashMap() + payloadJson["room_id"] = roomId + payloadJson["type"] = eventType + payloadJson["content"] = eventContent - // Get canonical Json from + // Get canonical Json from - val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson)) - val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString!!) + val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson)) + val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString!!) - val map = HashMap() - map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM - map["sender_key"] = olmDevice.deviceCurve25519Key!! - map["ciphertext"] = ciphertext!! - map["session_id"] = session.sessionId + val map = HashMap() + map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM + map["sender_key"] = olmDevice.deviceCurve25519Key!! + map["ciphertext"] = ciphertext!! + map["session_id"] = session.sessionId - // Include our device ID so that recipients can send us a - // m.new_device message if they don't have our session key. - map["device_id"] = credentials.deviceId!! - session.useCount++ - map + // Include our device ID so that recipients can send us a + // m.new_device message if they don't have our session key. + map["device_id"] = credentials.deviceId!! + session.useCount++ + map + } } /** @@ -328,9 +329,7 @@ internal class MXMegolmEncryption( if (unknownDevices.isEmpty) { Try.just(devicesInRoom) } else { - val cryptoError = MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE, - MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices) - Try.Failure(Failure.CryptoError(cryptoError)) + Try.Failure(MXCryptoError.UnknownDevice(unknownDevices)) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt index 2714784c23..5ae2745e6f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt @@ -17,13 +17,13 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm +import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE import im.vector.matrix.android.api.util.JsonDict -import im.vector.matrix.android.internal.crypto.MXDecryptionException import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting @@ -41,30 +41,29 @@ internal class MXOlmDecryption( private val credentials: Credentials) : IMXDecrypting { - @Throws(MXDecryptionException::class) - override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { + override suspend fun decryptEvent(event: Event, timeline: String): Try { val olmEventContent = event.content.toModel() ?: run { Timber.e("## decryptEvent() : bad event format") - throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_EVENT_FORMAT_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_EVENT_FORMAT_ERROR_CODE, + MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON)) } val cipherText = olmEventContent.ciphertext ?: run { Timber.e("## decryptEvent() : missing cipher text") - throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_CIPHER_TEXT_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_CIPHER_TEXT_ERROR_CODE, + MXCryptoError.MISSING_CIPHER_TEXT_REASON)) } val senderKey = olmEventContent.senderKey ?: run { Timber.e("## decryptEvent() : missing sender key") - throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_SENDER_KEY_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_SENDER_KEY_ERROR_CODE, + MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON)) } val messageAny = cipherText[olmDevice.deviceCurve25519Key] ?: run { Timber.e("## decryptEvent() : our device ${olmDevice.deviceCurve25519Key} is not included in recipients") - throw MXDecryptionException(MXCryptoError(MXCryptoError.NOT_INCLUDE_IN_RECIPIENTS_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.NOT_INCLUDE_IN_RECIPIENTS_ERROR_CODE, + MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON)) } // The message for myUser @@ -74,14 +73,14 @@ internal class MXOlmDecryption( if (decryptedPayload == null) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey") - throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, + MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) } val payloadString = convertFromUTF8(decryptedPayload) if (payloadString == null) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey") - throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, + MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) } val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) @@ -89,73 +88,72 @@ internal class MXOlmDecryption( if (payload == null) { Timber.e("## decryptEvent failed : null payload") - throw MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, + MXCryptoError.MISSING_CIPHER_TEXT_REASON)) } val olmPayloadContent = OlmPayloadContent.fromJsonString(payloadString) ?: run { Timber.e("## decryptEvent() : bad olmPayloadContent format") - throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_DECRYPTED_FORMAT_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_DECRYPTED_FORMAT_ERROR_CODE, + MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) } if (olmPayloadContent.recipient.isNullOrBlank()) { val reason = String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient") Timber.e("## decryptEvent() : $reason") - throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, reason)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, + reason)) } if (olmPayloadContent.recipient != credentials.userId) { Timber.e("## decryptEvent() : Event ${event.eventId}: Intended recipient ${olmPayloadContent.recipient} does not match our id ${credentials.userId}") - throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_RECIPIENT_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))) + return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_RECIPIENT_ERROR_CODE, + String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))) } val recipientKeys = olmPayloadContent.recipient_keys ?: run { Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'recipient_keys' property; cannot prevent unknown-key attack") - throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys"))) + return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, + String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys"))) } val ed25519 = recipientKeys["ed25519"] if (ed25519 != olmDevice.deviceEd25519Key) { Timber.e("## decryptEvent() : Event ${event.eventId}: Intended recipient ed25519 key $ed25519 did not match ours") - throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_RECIPIENT_KEY_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.BAD_RECIPIENT_KEY_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_RECIPIENT_KEY_ERROR_CODE, + MXCryptoError.BAD_RECIPIENT_KEY_REASON)) } if (olmPayloadContent.sender.isNullOrBlank()) { Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'sender' property; cannot prevent unknown-key attack") - throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender"))) + return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, + String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender"))) } if (olmPayloadContent.sender != event.senderId) { Timber.e("Event ${event.eventId}: original sender ${olmPayloadContent.sender} does not match reported sender ${event.senderId}") - throw MXDecryptionException(MXCryptoError(MXCryptoError.FORWARDED_MESSAGE_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender))) + return Try.Failure(MXCryptoError.Base(MXCryptoError.FORWARDED_MESSAGE_ERROR_CODE, + String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender))) } if (olmPayloadContent.room_id != event.roomId) { Timber.e("## decryptEvent() : Event ${event.eventId}: original room ${olmPayloadContent.room_id} does not match reported room ${event.roomId}") - throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_ROOM_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id))) + return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_ROOM_ERROR_CODE, + String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id))) } val keys = olmPayloadContent.keys ?: run { Timber.e("## decryptEvent failed : null keys") - throw MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, + MXCryptoError.MISSING_CIPHER_TEXT_REASON)) } - val result = MXEventDecryptionResult() - result.clearEvent = payload - result.senderCurve25519Key = senderKey - result.claimedEd25519Key = keys["ed25519"] - - return result + return Try.just(MXEventDecryptionResult( + clearEvent = payload, + senderCurve25519Key = senderKey, + claimedEd25519Key = keys["ed25519"] + )) } /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/MXDecryptionResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/OlmDecryptionResult.kt similarity index 79% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/MXDecryptionResult.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/OlmDecryptionResult.kt index cb1e1f708c..8a28e4926f 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/MXDecryptionResult.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/OlmDecryptionResult.kt @@ -14,32 +14,32 @@ * limitations under the License. */ -package im.vector.matrix.android.internal.crypto.algorithms +package im.vector.matrix.android.internal.crypto.algorithms.olm import im.vector.matrix.android.api.util.JsonDict /** * This class represents the decryption result. */ -data class MXDecryptionResult( +data class OlmDecryptionResult( /** * The decrypted payload (with properties 'type', 'content') */ - var payload: JsonDict? = null, + val payload: JsonDict? = null, /** * keys that the sender of the event claims ownership of: * map from key type to base64-encoded key. */ - var keysClaimed: Map? = null, + val keysClaimed: Map? = null, /** * The curve25519 key that the sender of the event is known to have ownership of. */ - var senderKey: String? = null, + val senderKey: String? = null, /** * Devices which forwarded this session to us (normally empty). */ - var forwardingCurve25519KeyChain: List? = null -) \ No newline at end of file + val forwardingCurve25519KeyChain: List? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXEncryptEventContentResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXEncryptEventContentResult.kt index 159ae0d5ee..7eaeb5bb07 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXEncryptEventContentResult.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXEncryptEventContentResult.kt @@ -20,7 +20,7 @@ import im.vector.matrix.android.api.session.events.model.Content data class MXEncryptEventContentResult( /** - * The event content + * The encrypted event content */ val eventContent: Content, /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt index aeb35e10cf..1fc33b3a07 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt @@ -16,12 +16,12 @@ package im.vector.matrix.android.internal.session.room.timeline +import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineService -import im.vector.matrix.android.internal.crypto.MXDecryptionException import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity @@ -81,8 +81,8 @@ internal class SimpleTimelineEventFactory @Inject constructor(private val roomMe event.setClearData(result) } catch (failure: Throwable) { Timber.e("Encrypted event: decryption failed") - if (failure is MXDecryptionException) { - event.setCryptoError(failure.cryptoError) + if (failure is Failure.CryptoError) { + event.setCryptoError(failure.error) } } } @@ -135,14 +135,12 @@ internal class InMemoryTimelineEventFactory @Inject constructor(private val room } else { try { val result = cryptoService.decryptEvent(event, timelineId) - if (result != null) { - decryptionCache[cacheKey] = result - } + decryptionCache[cacheKey] = result event.setClearData(result) } catch (failure: Throwable) { Timber.e("Encrypted event: decryption failed ${failure.localizedMessage}") - if (failure is MXDecryptionException) { - event.setCryptoError(failure.cryptoError) + if (failure is Failure.CryptoError) { + event.setCryptoError(failure.error) } else { // Other error Timber.e("Other error, should be handled") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt index 8992091b96..5ee64b1cfb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt @@ -17,15 +17,14 @@ package im.vector.matrix.android.internal.session.sync import android.text.TextUtils +import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.internal.crypto.CryptoManager -import im.vector.matrix.android.internal.crypto.MXDecryptionException import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.session.sync.model.ToDeviceSyncResponse import timber.log.Timber @@ -40,7 +39,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoManager: // Decrypt event if necessary decryptEvent(event, null) if (TextUtils.equals(event.getClearType(), EventType.MESSAGE) - && event.mClearEvent?.content?.toModel()?.type == "m.bad.encrypted") { + && event.mClearEvent?.content?.toModel()?.type == "m.bad.encrypted") { Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : " + event.content) } else { sasVerificationService.onToDeviceEvent(event) @@ -66,8 +65,8 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoManager: var result: MXEventDecryptionResult? = null try { result = cryptoManager.decryptEvent(event, timelineId ?: "") - } catch (exception: MXDecryptionException) { - event.setCryptoError(exception.cryptoError) + } catch (exception: MXCryptoError) { + event.setCryptoError(exception) } if (null != result) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt index 54c15685a9..8ca7e22ac4 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt @@ -28,7 +28,6 @@ import im.vector.riotx.core.utils.DebouncedClickListener import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController import im.vector.riotx.features.home.room.detail.timeline.item.MessageTextItem_ -import im.vector.riotx.features.home.room.detail.timeline.item.NoticeItem_ import im.vector.riotx.features.home.room.detail.timeline.util.MessageInformationDataFactory import me.gujun.android.span.span import javax.inject.Inject @@ -49,10 +48,16 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat EventType.ENCRYPTED == event.root.getClearType() -> { val cryptoError = event.root.mCryptoError val errorDescription = - if (cryptoError?.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { - stringProvider.getString(R.string.notice_crypto_error_unkwown_inbound_session_id) + if (cryptoError is MXCryptoError.Base) { + if (cryptoError.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { + stringProvider.getString(R.string.notice_crypto_error_unkwown_inbound_session_id) + } else { + // TODO i18n + cryptoError._message + } } else { - cryptoError?.message + // Cannot happen (for now) + "Other error" } val message = stringProvider.getString(R.string.notice_crypto_unable_to_decrypt, errorDescription) @@ -77,7 +82,7 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat })) .longClickListener { view -> return@longClickListener callback?.onEventLongClicked(informationData, null, view) - ?: false + ?: false } } else -> null From 07f80f43bd4e10bbdf399815e29c6b8c95eadb68 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 5 Jul 2019 15:15:55 +0200 Subject: [PATCH 2/7] Display clear type --- .../home/room/detail/timeline/factory/TimelineItemFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index cb9d354b3c..4a927b1979 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -83,7 +83,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me NoticeItem_() .avatarRenderer(avatarRenderer) .informationData(informationData) - .noticeText("{ \"type\": ${event.root.type} }") + .noticeText("{ \"type\": ${event.root.getClearType()} }") .highlighted(highlight) .baseCallback(callback) } From 4a512d242523305ab1d2bb49d72af1b83f00fe81 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 5 Jul 2019 15:43:28 +0200 Subject: [PATCH 3/7] Create enum for errorType and fix a few issues --- .../api/session/crypto/CryptoService.kt | 1 + .../api/session/crypto/MXCryptoError.kt | 68 ++++++++--------- .../android/internal/crypto/CryptoManager.kt | 6 +- .../android/internal/crypto/MXOlmDevice.kt | 76 +++++++++---------- .../algorithms/megolm/MXMegolmDecryption.kt | 43 ++++++----- .../crypto/algorithms/olm/MXOlmDecryption.kt | 32 ++++---- .../room/timeline/TimelineEventFactory.kt | 12 +-- .../timeline/factory/EncryptedItemFactory.kt | 4 +- 8 files changed, 117 insertions(+), 125 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt index 31458f2fc7..0397b51439 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt @@ -98,6 +98,7 @@ interface CryptoService { roomId: String, callback: MatrixCallback) + @Throws(MXCryptoError::class) fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt index fbbacb4afb..d7a7a13bbf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt @@ -20,62 +20,56 @@ package im.vector.matrix.android.api.session.crypto import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import org.matrix.olm.OlmException /** * Represents a crypto error response. */ sealed class MXCryptoError : Throwable() { - // TODO Create data class for all cases, and remove error code - data class Base(val code: String, - val _message: String, + data class Base(val errorType: ErrorType, + val technicalMessage: String, /** * Describe the error with more details */ val detailedErrorDescription: String? = null) : MXCryptoError() + data class OlmError(val olmException: OlmException) : MXCryptoError() + data class UnknownDevice(val deviceList: MXUsersDevicesMap) : MXCryptoError() + enum class ErrorType { + ENCRYPTING_NOT_ENABLED_ERROR_CODE, + UNABLE_TO_ENCRYPT_ERROR_CODE, + UNABLE_TO_DECRYPT_ERROR_CODE, + UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE, + INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, + MISSING_FIELDS_ERROR_CODE, + BAD_EVENT_FORMAT_ERROR_CODE, + MISSING_SENDER_KEY_ERROR_CODE, + MISSING_CIPHER_TEXT_ERROR_CODE, + BAD_DECRYPTED_FORMAT_ERROR_CODE, + NOT_INCLUDE_IN_RECIPIENTS_ERROR_CODE, + BAD_RECIPIENT_ERROR_CODE, + BAD_RECIPIENT_KEY_ERROR_CODE, + FORWARDED_MESSAGE_ERROR_CODE, + BAD_ROOM_ERROR_CODE, + BAD_ENCRYPTED_MESSAGE_ERROR_CODE, + DUPLICATED_MESSAGE_INDEX_ERROR_CODE, + MISSING_PROPERTY_ERROR_CODE, + OLM_ERROR_CODE, + UNKNOWN_DEVICES_CODE, + UNKNOWN_MESSAGE_INDEX + } + companion object { /** - * Error codes + * Resource for technicalMessage */ - const val ENCRYPTING_NOT_ENABLED_ERROR_CODE = "ENCRYPTING_NOT_ENABLED" - const val UNABLE_TO_ENCRYPT_ERROR_CODE = "UNABLE_TO_ENCRYPT" - const val UNABLE_TO_DECRYPT_ERROR_CODE = "UNABLE_TO_DECRYPT" - const val UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE = "UNKNOWN_INBOUND_SESSION_ID" - const val INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE = "INBOUND_SESSION_MISMATCH_ROOM_ID" - const val MISSING_FIELDS_ERROR_CODE = "MISSING_FIELDS" - const val BAD_EVENT_FORMAT_ERROR_CODE = "BAD_EVENT_FORMAT_ERROR_CODE" - const val MISSING_SENDER_KEY_ERROR_CODE = "MISSING_SENDER_KEY_ERROR_CODE" - const val MISSING_CIPHER_TEXT_ERROR_CODE = "MISSING_CIPHER_TEXT" - const val BAD_DECRYPTED_FORMAT_ERROR_CODE = "BAD_DECRYPTED_FORMAT_ERROR_CODE" - const val NOT_INCLUDE_IN_RECIPIENTS_ERROR_CODE = "NOT_INCLUDE_IN_RECIPIENTS" - const val BAD_RECIPIENT_ERROR_CODE = "BAD_RECIPIENT" - const val BAD_RECIPIENT_KEY_ERROR_CODE = "BAD_RECIPIENT_KEY" - const val FORWARDED_MESSAGE_ERROR_CODE = "FORWARDED_MESSAGE" - const val BAD_ROOM_ERROR_CODE = "BAD_ROOM" - const val BAD_ENCRYPTED_MESSAGE_ERROR_CODE = "BAD_ENCRYPTED_MESSAGE" - const val DUPLICATED_MESSAGE_INDEX_ERROR_CODE = "DUPLICATED_MESSAGE_INDEX" - const val MISSING_PROPERTY_ERROR_CODE = "MISSING_PROPERTY" - const val OLM_ERROR_CODE = "OLM_ERROR_CODE" - const val UNKNOWN_DEVICES_CODE = "UNKNOWN_DEVICES_CODE" - const val UNKNOWN_MESSAGE_INDEX = "UNKNOWN_MESSAGE_INDEX" - - /** - * short error reasons - */ - const val UNABLE_TO_DECRYPT = "Unable to decrypt" - const val UNABLE_TO_ENCRYPT = "Unable to encrypt" - - /** - * Detailed error reasons - */ - const val ENCRYPTING_NOT_ENABLED_REASON = "Encryption not enabled" const val UNABLE_TO_ENCRYPT_REASON = "Unable to encrypt %s" const val UNABLE_TO_DECRYPT_REASON = "Unable to decrypt %1\$s. Algorithm: %2\$s" const val OLM_REASON = "OLM error: %1\$s" - const val DETAILLED_OLM_REASON = "Unable to decrypt %1\$s. OLM error: %2\$s" + const val DETAILED_OLM_REASON = "Unable to decrypt %1\$s. OLM error: %2\$s" const val UNKNOWN_INBOUND_SESSION_ID_REASON = "Unknown inbound session id" const val INBOUND_SESSION_MISMATCH_ROOM_ID_REASON = "Mismatched room_id for inbound group session (expected %1\$s, was %2\$s)" const val MISSING_FIELDS_REASON = "Missing fields in input" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt index 51a0d8c57b..ee4b8c2206 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt @@ -569,7 +569,7 @@ internal class CryptoManager @Inject constructor( val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON) Timber.e("## encryptEventContent() : $reason") - callback.onFailure(Failure.CryptoError(MXCryptoError.Base(MXCryptoError.UNABLE_TO_ENCRYPT_ERROR_CODE, reason))) + callback.onFailure(Failure.CryptoError(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_ENCRYPT_ERROR_CODE, reason))) } } } @@ -618,14 +618,14 @@ internal class CryptoManager @Inject constructor( val eventContent = event.content return if (eventContent == null) { Timber.e("## decryptEvent : empty event content") - Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) } else { val algorithm = eventContent["algorithm"]?.toString() val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm) if (alg == null) { val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm) Timber.e("## decryptEvent() : $reason") - Try.Failure(MXCryptoError.Base(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, reason)) + Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT_ERROR_CODE, reason)) } else { alg.decryptEvent(event, timeline) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt index daf8c6739c..249e526724 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt @@ -653,58 +653,52 @@ internal class MXOlmDevice @Inject constructor( // Check that the room id matches the original one for the session. This stops // the HS pretending a message was targeting a different room. if (roomId == session.roomId) { - var errorMessage = "" var decryptResult: OlmInboundGroupSession.DecryptMessageResult? = null try { decryptResult = session.olmInboundGroupSession!!.decryptMessage(body) - } catch (e: Exception) { + } catch (e: OlmException) { Timber.e(e, "## decryptGroupMessage () : decryptMessage failed") - errorMessage = e.message ?: "" + return@flatMap Try.Failure(MXCryptoError.OlmError(e)) } - if (null != decryptResult) { - if (null != timeline) { - if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) { - inboundGroupSessionMessageIndexes[timeline] = HashMap() - } - - val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex - - if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) { - val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) - Timber.e("## decryptGroupMessage() : $reason") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.DUPLICATED_MESSAGE_INDEX_ERROR_CODE, reason)) - } - - inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) + if (null != timeline) { + if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) { + inboundGroupSessionMessageIndexes[timeline] = HashMap() } - store.storeInboundGroupSessions(listOf(session)) - val payload = try { - val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) - val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) - adapter.fromJson(payloadString) - } catch (e: Exception) { - Timber.e("## decryptGroupMessage() : fails to parse the payload") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_DECRYPTED_FORMAT_ERROR_CODE, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) + val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex + + if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) { + val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) + Timber.e("## decryptGroupMessage() : $reason") + return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX_ERROR_CODE, reason)) } - return@flatMap Try.just( - OlmDecryptionResult( - payload, - session.keysClaimed, - senderKey, - session.forwardingCurve25519KeyChain - ) - ) - } else { - Timber.e("## decryptGroupMessage() : failed to decode the message") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.OLM_ERROR_CODE, errorMessage)) + inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) } + + store.storeInboundGroupSessions(listOf(session)) + val payload = try { + val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) + val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) + adapter.fromJson(payloadString) + } catch (e: Exception) { + Timber.e("## decryptGroupMessage() : fails to parse the payload") + return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT_ERROR_CODE, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) + } + + return@flatMap Try.just( + OlmDecryptionResult( + payload, + session.keysClaimed, + senderKey, + session.forwardingCurve25519KeyChain + ) + ) } else { val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) Timber.e("## decryptGroupMessage() : $reason") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, reason)) + return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, reason)) } } } @@ -772,7 +766,7 @@ internal class MXOlmDevice @Inject constructor( */ fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): Try { if (sessionId.isNullOrBlank() || senderKey.isNullOrBlank()) { - return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_SENDER_KEY_ERROR_CODE, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY_ERROR_CODE, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)) } val session = store.getInboundGroupSession(sessionId, senderKey) @@ -783,13 +777,13 @@ internal class MXOlmDevice @Inject constructor( if (!TextUtils.equals(roomId, session.roomId)) { val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) Timber.e("## getInboundGroupSession() : $errorDescription") - Try.Failure(MXCryptoError.Base(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, errorDescription)) + Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, errorDescription)) } else { Try.just(session) } } else { Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId") - Try.Failure(MXCryptoError.Base(MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)) + Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index ee8e014552..9df4f267c5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -69,34 +69,37 @@ internal class MXMegolmDecryption(private val credentials: Credentials, private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): Try { val encryptedEventContent = event.content.toModel() - ?: throw MXCryptoError.Base(MXCryptoError.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON) + ?: return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON)) if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) { - throw MXCryptoError.Base(MXCryptoError.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON) + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON)) } + // TODO Why AS says this code is unreachable? return olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext!!, event.roomId!!, timeline, encryptedEventContent.sessionId!!, encryptedEventContent.senderKey!!) .fold( { throwable -> - if (throwable is MXCryptoError.Base) { - if (throwable.code == MXCryptoError.OLM_ERROR_CODE) { - if (MXCryptoError.UNKNOWN_MESSAGE_INDEX == throwable._message) { - addEventToPendingList(event, timeline) - if (requestKeysOnFail) { - requestKeysForEvent(event) - } + if (throwable is MXCryptoError.OlmError) { + // TODO Check the value of .message + if (throwable.olmException.message == "UNKNOWN_MESSAGE_INDEX") { + addEventToPendingList(event, timeline) + if (requestKeysOnFail) { + requestKeysForEvent(event) } + } - val reason = String.format(MXCryptoError.OLM_REASON, throwable._message) - val detailedReason = String.format(MXCryptoError.DETAILLED_OLM_REASON, encryptedEventContent.ciphertext, throwable._message) + val reason = String.format(MXCryptoError.OLM_REASON, throwable.olmException.message) + val detailedReason = String.format(MXCryptoError.DETAILED_OLM_REASON, encryptedEventContent.ciphertext, reason) - throw MXCryptoError.Base( - MXCryptoError.OLM_ERROR_CODE, - reason, - detailedReason) - } else if (throwable.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { + return Try.Failure(MXCryptoError.Base( + MXCryptoError.ErrorType.OLM_ERROR_CODE, + reason, + detailedReason)) + } + if (throwable is MXCryptoError.Base) { + if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { addEventToPendingList(event, timeline) if (requestKeysOnFail) { requestKeysForEvent(event) @@ -104,12 +107,12 @@ internal class MXMegolmDecryption(private val credentials: Credentials, } } - throw throwable + return Try.Failure(throwable) }, { decryptionResult -> // the decryption succeeds - if (decryptionResult.payload != null) { - return Try.just( + return if (decryptionResult.payload != null) { + Try.just( MXEventDecryptionResult( clearEvent = decryptionResult.payload, senderCurve25519Key = decryptionResult.senderKey, @@ -118,7 +121,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials, ) ) } else { - throw MXCryptoError.Base(MXCryptoError.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON) + Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON)) } } ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt index 5ae2745e6f..3f11a0ec77 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt @@ -44,25 +44,25 @@ internal class MXOlmDecryption( override suspend fun decryptEvent(event: Event, timeline: String): Try { val olmEventContent = event.content.toModel() ?: run { Timber.e("## decryptEvent() : bad event format") - return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_EVENT_FORMAT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT_ERROR_CODE, MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON)) } val cipherText = olmEventContent.ciphertext ?: run { Timber.e("## decryptEvent() : missing cipher text") - return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_CIPHER_TEXT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT_ERROR_CODE, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) } val senderKey = olmEventContent.senderKey ?: run { Timber.e("## decryptEvent() : missing sender key") - return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_SENDER_KEY_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY_ERROR_CODE, MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON)) } val messageAny = cipherText[olmDevice.deviceCurve25519Key] ?: run { Timber.e("## decryptEvent() : our device ${olmDevice.deviceCurve25519Key} is not included in recipients") - return Try.Failure(MXCryptoError.Base(MXCryptoError.NOT_INCLUDE_IN_RECIPIENTS_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS_ERROR_CODE, MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON)) } @@ -73,13 +73,13 @@ internal class MXOlmDecryption( if (decryptedPayload == null) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey") - return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) } val payloadString = convertFromUTF8(decryptedPayload) if (payloadString == null) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey") - return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) } @@ -88,32 +88,32 @@ internal class MXOlmDecryption( if (payload == null) { Timber.e("## decryptEvent failed : null payload") - return Try.Failure(MXCryptoError.Base(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT_ERROR_CODE, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) } val olmPayloadContent = OlmPayloadContent.fromJsonString(payloadString) ?: run { Timber.e("## decryptEvent() : bad olmPayloadContent format") - return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_DECRYPTED_FORMAT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT_ERROR_CODE, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) } if (olmPayloadContent.recipient.isNullOrBlank()) { val reason = String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient") Timber.e("## decryptEvent() : $reason") - return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY_ERROR_CODE, reason)) } if (olmPayloadContent.recipient != credentials.userId) { Timber.e("## decryptEvent() : Event ${event.eventId}: Intended recipient ${olmPayloadContent.recipient} does not match our id ${credentials.userId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_RECIPIENT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_ERROR_CODE, String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))) } val recipientKeys = olmPayloadContent.recipient_keys ?: run { Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'recipient_keys' property; cannot prevent unknown-key attack") - return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY_ERROR_CODE, String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys"))) } @@ -121,31 +121,31 @@ internal class MXOlmDecryption( if (ed25519 != olmDevice.deviceEd25519Key) { Timber.e("## decryptEvent() : Event ${event.eventId}: Intended recipient ed25519 key $ed25519 did not match ours") - return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_RECIPIENT_KEY_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_KEY_ERROR_CODE, MXCryptoError.BAD_RECIPIENT_KEY_REASON)) } if (olmPayloadContent.sender.isNullOrBlank()) { Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'sender' property; cannot prevent unknown-key attack") - return Try.Failure(MXCryptoError.Base(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY_ERROR_CODE, String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender"))) } if (olmPayloadContent.sender != event.senderId) { Timber.e("Event ${event.eventId}: original sender ${olmPayloadContent.sender} does not match reported sender ${event.senderId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.FORWARDED_MESSAGE_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.FORWARDED_MESSAGE_ERROR_CODE, String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender))) } if (olmPayloadContent.room_id != event.roomId) { Timber.e("## decryptEvent() : Event ${event.eventId}: original room ${olmPayloadContent.room_id} does not match reported room ${event.roomId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.BAD_ROOM_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ROOM_ERROR_CODE, String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id))) } val keys = olmPayloadContent.keys ?: run { Timber.e("## decryptEvent failed : null keys") - return Try.Failure(MXCryptoError.Base(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT_ERROR_CODE, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt index 1fc33b3a07..ae0c69c193 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt @@ -16,8 +16,8 @@ package im.vector.matrix.android.internal.session.room.timeline -import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.crypto.CryptoService +import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineEvent @@ -81,8 +81,8 @@ internal class SimpleTimelineEventFactory @Inject constructor(private val roomMe event.setClearData(result) } catch (failure: Throwable) { Timber.e("Encrypted event: decryption failed") - if (failure is Failure.CryptoError) { - event.setCryptoError(failure.error) + if (failure is MXCryptoError) { + event.setCryptoError(failure) } } } @@ -138,9 +138,9 @@ internal class InMemoryTimelineEventFactory @Inject constructor(private val room decryptionCache[cacheKey] = result event.setClearData(result) } catch (failure: Throwable) { - Timber.e("Encrypted event: decryption failed ${failure.localizedMessage}") - if (failure is Failure.CryptoError) { - event.setCryptoError(failure.error) + Timber.e("Encrypted event: decryption failed: $failure") + if (failure is MXCryptoError) { + event.setCryptoError(failure) } else { // Other error Timber.e("Other error, should be handled") diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt index 8ca7e22ac4..7b0ccf87c4 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt @@ -49,11 +49,11 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat val cryptoError = event.root.mCryptoError val errorDescription = if (cryptoError is MXCryptoError.Base) { - if (cryptoError.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { + if (cryptoError.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { stringProvider.getString(R.string.notice_crypto_error_unkwown_inbound_session_id) } else { // TODO i18n - cryptoError._message + cryptoError.technicalMessage } } else { // Cannot happen (for now) From f3fab0dc08a667c94759ccea555534ea42790adf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 5 Jul 2019 15:52:37 +0200 Subject: [PATCH 4/7] Rename ErrorTypes --- .../api/session/crypto/MXCryptoError.kt | 40 +++++++++---------- .../android/internal/crypto/CryptoManager.kt | 6 +-- .../android/internal/crypto/MXOlmDevice.kt | 12 +++--- .../algorithms/megolm/MXMegolmDecryption.kt | 10 ++--- .../crypto/algorithms/olm/MXOlmDecryption.kt | 32 +++++++-------- .../timeline/factory/EncryptedItemFactory.kt | 2 +- 6 files changed, 51 insertions(+), 51 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt index d7a7a13bbf..42102124d7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt @@ -39,26 +39,26 @@ sealed class MXCryptoError : Throwable() { data class UnknownDevice(val deviceList: MXUsersDevicesMap) : MXCryptoError() enum class ErrorType { - ENCRYPTING_NOT_ENABLED_ERROR_CODE, - UNABLE_TO_ENCRYPT_ERROR_CODE, - UNABLE_TO_DECRYPT_ERROR_CODE, - UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE, - INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, - MISSING_FIELDS_ERROR_CODE, - BAD_EVENT_FORMAT_ERROR_CODE, - MISSING_SENDER_KEY_ERROR_CODE, - MISSING_CIPHER_TEXT_ERROR_CODE, - BAD_DECRYPTED_FORMAT_ERROR_CODE, - NOT_INCLUDE_IN_RECIPIENTS_ERROR_CODE, - BAD_RECIPIENT_ERROR_CODE, - BAD_RECIPIENT_KEY_ERROR_CODE, - FORWARDED_MESSAGE_ERROR_CODE, - BAD_ROOM_ERROR_CODE, - BAD_ENCRYPTED_MESSAGE_ERROR_CODE, - DUPLICATED_MESSAGE_INDEX_ERROR_CODE, - MISSING_PROPERTY_ERROR_CODE, - OLM_ERROR_CODE, - UNKNOWN_DEVICES_CODE, + ENCRYPTING_NOT_ENABLED, + UNABLE_TO_ENCRYPT, + UNABLE_TO_DECRYPT, + UNKNOWN_INBOUND_SESSION_ID, + INBOUND_SESSION_MISMATCH_ROOM_ID, + MISSING_FIELDS, + BAD_EVENT_FORMAT, + MISSING_SENDER_KEY, + MISSING_CIPHER_TEXT, + BAD_DECRYPTED_FORMAT, + NOT_INCLUDE_IN_RECIPIENTS, + BAD_RECIPIENT, + BAD_RECIPIENT_KEY, + FORWARDED_MESSAGE, + BAD_ROOM, + BAD_ENCRYPTED_MESSAGE, + DUPLICATED_MESSAGE_INDEX, + MISSING_PROPERTY, + OLM, + UNKNOWN_DEVICES, UNKNOWN_MESSAGE_INDEX } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt index ee4b8c2206..667f1446dc 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt @@ -569,7 +569,7 @@ internal class CryptoManager @Inject constructor( val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON) Timber.e("## encryptEventContent() : $reason") - callback.onFailure(Failure.CryptoError(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_ENCRYPT_ERROR_CODE, reason))) + callback.onFailure(Failure.CryptoError(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_ENCRYPT, reason))) } } } @@ -618,14 +618,14 @@ internal class CryptoManager @Inject constructor( val eventContent = event.content return if (eventContent == null) { Timber.e("## decryptEvent : empty event content") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) } else { val algorithm = eventContent["algorithm"]?.toString() val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm) if (alg == null) { val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm) Timber.e("## decryptEvent() : $reason") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT_ERROR_CODE, reason)) + Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason)) } else { alg.decryptEvent(event, timeline) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt index 249e526724..a9ea4a73db 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt @@ -671,7 +671,7 @@ internal class MXOlmDevice @Inject constructor( if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) { val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) Timber.e("## decryptGroupMessage() : $reason") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX_ERROR_CODE, reason)) + return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason)) } inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) @@ -684,7 +684,7 @@ internal class MXOlmDevice @Inject constructor( adapter.fromJson(payloadString) } catch (e: Exception) { Timber.e("## decryptGroupMessage() : fails to parse the payload") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT_ERROR_CODE, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) + return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) } return@flatMap Try.just( @@ -698,7 +698,7 @@ internal class MXOlmDevice @Inject constructor( } else { val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) Timber.e("## decryptGroupMessage() : $reason") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, reason)) + return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason)) } } } @@ -766,7 +766,7 @@ internal class MXOlmDevice @Inject constructor( */ fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): Try { if (sessionId.isNullOrBlank() || senderKey.isNullOrBlank()) { - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY_ERROR_CODE, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)) } val session = store.getInboundGroupSession(sessionId, senderKey) @@ -777,13 +777,13 @@ internal class MXOlmDevice @Inject constructor( if (!TextUtils.equals(roomId, session.roomId)) { val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) Timber.e("## getInboundGroupSession() : $errorDescription") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, errorDescription)) + Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, errorDescription)) } else { Try.just(session) } } else { Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)) + Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 9df4f267c5..14f9086244 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -69,12 +69,12 @@ internal class MXMegolmDecryption(private val credentials: Credentials, private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): Try { val encryptedEventContent = event.content.toModel() - ?: return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON)) + ?: return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) { - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON)) + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) } // TODO Why AS says this code is unreachable? @@ -94,12 +94,12 @@ internal class MXMegolmDecryption(private val credentials: Credentials, val detailedReason = String.format(MXCryptoError.DETAILED_OLM_REASON, encryptedEventContent.ciphertext, reason) return Try.Failure(MXCryptoError.Base( - MXCryptoError.ErrorType.OLM_ERROR_CODE, + MXCryptoError.ErrorType.OLM, reason, detailedReason)) } if (throwable is MXCryptoError.Base) { - if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { + if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) { addEventToPendingList(event, timeline) if (requestKeysOnFail) { requestKeysForEvent(event) @@ -121,7 +121,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials, ) ) } else { - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS_ERROR_CODE, MXCryptoError.MISSING_FIELDS_REASON)) + Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) } } ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt index 3f11a0ec77..e24132c333 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt @@ -44,25 +44,25 @@ internal class MXOlmDecryption( override suspend fun decryptEvent(event: Event, timeline: String): Try { val olmEventContent = event.content.toModel() ?: run { Timber.e("## decryptEvent() : bad event format") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT, MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON)) } val cipherText = olmEventContent.ciphertext ?: run { Timber.e("## decryptEvent() : missing cipher text") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) } val senderKey = olmEventContent.senderKey ?: run { Timber.e("## decryptEvent() : missing sender key") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON)) } val messageAny = cipherText[olmDevice.deviceCurve25519Key] ?: run { Timber.e("## decryptEvent() : our device ${olmDevice.deviceCurve25519Key} is not included in recipients") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS, MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON)) } @@ -73,13 +73,13 @@ internal class MXOlmDecryption( if (decryptedPayload == null) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) } val payloadString = convertFromUTF8(decryptedPayload) if (payloadString == null) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) } @@ -88,32 +88,32 @@ internal class MXOlmDecryption( if (payload == null) { Timber.e("## decryptEvent failed : null payload") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) } val olmPayloadContent = OlmPayloadContent.fromJsonString(payloadString) ?: run { Timber.e("## decryptEvent() : bad olmPayloadContent format") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) } if (olmPayloadContent.recipient.isNullOrBlank()) { val reason = String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient") Timber.e("## decryptEvent() : $reason") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, reason)) } if (olmPayloadContent.recipient != credentials.userId) { Timber.e("## decryptEvent() : Event ${event.eventId}: Intended recipient ${olmPayloadContent.recipient} does not match our id ${credentials.userId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT, String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))) } val recipientKeys = olmPayloadContent.recipient_keys ?: run { Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'recipient_keys' property; cannot prevent unknown-key attack") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys"))) } @@ -121,31 +121,31 @@ internal class MXOlmDecryption( if (ed25519 != olmDevice.deviceEd25519Key) { Timber.e("## decryptEvent() : Event ${event.eventId}: Intended recipient ed25519 key $ed25519 did not match ours") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_KEY_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_KEY, MXCryptoError.BAD_RECIPIENT_KEY_REASON)) } if (olmPayloadContent.sender.isNullOrBlank()) { Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'sender' property; cannot prevent unknown-key attack") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender"))) } if (olmPayloadContent.sender != event.senderId) { Timber.e("Event ${event.eventId}: original sender ${olmPayloadContent.sender} does not match reported sender ${event.senderId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.FORWARDED_MESSAGE_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.FORWARDED_MESSAGE, String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender))) } if (olmPayloadContent.room_id != event.roomId) { Timber.e("## decryptEvent() : Event ${event.eventId}: original room ${olmPayloadContent.room_id} does not match reported room ${event.roomId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ROOM_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ROOM, String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id))) } val keys = olmPayloadContent.keys ?: run { Timber.e("## decryptEvent failed : null keys") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT_ERROR_CODE, + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt index 7b0ccf87c4..0fd60157ac 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt @@ -49,7 +49,7 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat val cryptoError = event.root.mCryptoError val errorDescription = if (cryptoError is MXCryptoError.Base) { - if (cryptoError.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { + if (cryptoError.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) { stringProvider.getString(R.string.notice_crypto_error_unkwown_inbound_session_id) } else { // TODO i18n From 03050c3f25599a1681d3b31df15cb61019edcc3a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 5 Jul 2019 16:11:54 +0200 Subject: [PATCH 5/7] Cleanup --- .../algorithms/megolm/MXMegolmDecryption.kt | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 14f9086244..fb90db7dd1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -68,17 +68,20 @@ internal class MXMegolmDecryption(private val credentials: Credentials, } private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): Try { - val encryptedEventContent = event.content.toModel() - ?: return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) - - if (TextUtils.isEmpty(encryptedEventContent.senderKey) - || TextUtils.isEmpty(encryptedEventContent.sessionId) - || TextUtils.isEmpty(encryptedEventContent.ciphertext)) { + if (event.roomId.isNullOrBlank()) { return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) } - // TODO Why AS says this code is unreachable? - return olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext!!, event.roomId!!, timeline, encryptedEventContent.sessionId!!, encryptedEventContent.senderKey!!) + val encryptedEventContent = event.content.toModel() + ?: return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) + + if (encryptedEventContent.senderKey.isNullOrBlank() + || encryptedEventContent.sessionId.isNullOrBlank() + || encryptedEventContent.ciphertext.isNullOrBlank()) { + return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) + } + + return olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext, event.roomId, timeline, encryptedEventContent.sessionId, encryptedEventContent.senderKey) .fold( { throwable -> if (throwable is MXCryptoError.OlmError) { @@ -93,7 +96,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials, val reason = String.format(MXCryptoError.OLM_REASON, throwable.olmException.message) val detailedReason = String.format(MXCryptoError.DETAILED_OLM_REASON, encryptedEventContent.ciphertext, reason) - return Try.Failure(MXCryptoError.Base( + Try.Failure(MXCryptoError.Base( MXCryptoError.ErrorType.OLM, reason, detailedReason)) @@ -107,17 +110,17 @@ internal class MXMegolmDecryption(private val credentials: Credentials, } } - return Try.Failure(throwable) + Try.Failure(throwable) }, - { decryptionResult -> + { olmDecryptionResult -> // the decryption succeeds - return if (decryptionResult.payload != null) { + if (olmDecryptionResult.payload != null) { Try.just( MXEventDecryptionResult( - clearEvent = decryptionResult.payload, - senderCurve25519Key = decryptionResult.senderKey, - claimedEd25519Key = decryptionResult.keysClaimed?.get("ed25519"), - forwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain ?: emptyList() + clearEvent = olmDecryptionResult.payload, + senderCurve25519Key = olmDecryptionResult.senderKey, + claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"), + forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain ?: emptyList() ) ) } else { From 302d23ba96b5631671fc3cd98d85675517269a32 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 5 Jul 2019 16:28:15 +0200 Subject: [PATCH 6/7] Create a realm locker to fast up next Realm.getInstance calls --- .../android/internal/crypto/store/db/RealmCryptoStore.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt index 7c3bfabf70..9865614c9f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt @@ -31,6 +31,7 @@ import im.vector.matrix.android.internal.crypto.store.db.query.delete import im.vector.matrix.android.internal.crypto.store.db.query.getById import im.vector.matrix.android.internal.crypto.store.db.query.getOrCreate import im.vector.matrix.android.internal.session.SessionScope +import io.realm.Realm import io.realm.RealmConfiguration import io.realm.Sort import io.realm.kotlin.where @@ -47,6 +48,9 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati * Memory cache, to correctly release JNI objects * ========================================================================================== */ + // A realm instance, for faster future getInstance. Do not use it + private var realmLocker: Realm? = null + // The olm account private var olmAccount: OlmAccount? = null @@ -86,6 +90,8 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati } override fun open() { + realmLocker = Realm.getInstance(realmConfiguration) + // Ensure CryptoMetadataEntity is inserted in DB doWithRealm(realmConfiguration) { realm -> var currentMetadata = realm.where().findFirst() @@ -131,6 +137,9 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati inboundGroupSessionToRelease.clear() olmAccount?.releaseAccount() + + realmLocker?.close() + realmLocker = null } override fun storeDeviceId(deviceId: String) { From e5adf174a8e745dfa01a2233415244fb88432ba3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 5 Jul 2019 17:00:57 +0200 Subject: [PATCH 7/7] Fix crash when invalid urls for image --- .../im/vector/riotx/features/media/ImageContentRenderer.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt index 0f92cbffbe..412043e8f4 100644 --- a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt +++ b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt @@ -28,6 +28,7 @@ import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.utils.DimensionUtils.dpToPx import kotlinx.android.parcel.Parcelize +import timber.log.Timber import java.io.File import javax.inject.Inject @@ -94,6 +95,11 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: val fullSize = contentUrlResolver.resolveFullSize(data.url) val thumbnail = contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE) + if (fullSize.isNullOrBlank() || thumbnail.isNullOrBlank()) { + Timber.w("Invalid urls") + return + } + // TODO DECRYPT_FILE Decrypt file imageView.showImage( Uri.parse(thumbnail),