Split again

This commit is contained in:
Benoit Marty 2019-05-20 10:30:33 +02:00
parent e0e41d9e5c
commit 532a028e41
32 changed files with 1041 additions and 856 deletions

View File

@ -34,7 +34,6 @@ interface KeysBackupService {
fun backupAllGroupSessions(progressListener: ProgressListener?, callback: MatrixCallback<Unit>?)
fun getKeysBackupTrust(keysBackupVersion: KeysVersionResult, callback: MatrixCallback<KeysBackupVersionTrust>)
fun getBackupProgress(progressListener: ProgressListener)
fun maybeBackupKeys()
fun getVersion(version: String, callback: MatrixCallback<KeysVersionResult?>)
fun forceUsingLastVersion(callback: MatrixCallback<Boolean>)
fun checkAndStartKeysBackup()

View File

@ -37,15 +37,19 @@ import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting
import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory
import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
import im.vector.matrix.android.internal.crypto.model.*
import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedMessage
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.*
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
@ -53,7 +57,6 @@ import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.convertToUTF8
import org.matrix.olm.OlmManager
import timber.log.Timber
import java.util.*
@ -71,11 +74,13 @@ import java.util.*
internal class CryptoManager(
// The credentials,
private val mCredentials: Credentials,
private val mMyDeviceInfoHolder: MyDeviceInfoHolder,
// the crypto store
private val mCryptoStore: IMXCryptoStore,
// Olm device
private val mOlmDevice: MXOlmDevice,
cryptoConfig: MXCryptoConfig?,
// Set of parameters used to configure/customize the end-to-end crypto.
private val mCryptoConfig: MXCryptoConfig = MXCryptoConfig(),
// Device list manager
private val deviceListManager: DeviceListManager,
// The key backup service.
@ -98,6 +103,12 @@ internal class CryptoManager(
private val mOlmManager: OlmManager,
// Actions
private val mSetDeviceVerificationAction: SetDeviceVerificationAction,
private val mMegolmSessionDataImporter: MegolmSessionDataImporter,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
// Repository
private val mWarnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
private val mMXMegolmEncryptionFactory: MXMegolmEncryptionFactory,
private val mMXOlmEncryptionFactory: MXOlmEncryptionFactory,
// Tasks
private val mClaimOneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask,
private val mDeleteDeviceTask: DeleteDeviceTask,
@ -108,19 +119,11 @@ internal class CryptoManager(
private val mUploadKeysTask: UploadKeysTask,
// TaskExecutor
private val mTaskExecutor: TaskExecutor
) : KeysBackup.KeysBackupCryptoListener,
DefaultSasVerificationService.SasCryptoListener,
CryptoService {
) : CryptoService {

// MXEncrypting instance for each room.
private val mRoomEncryptors: MutableMap<String, IMXEncrypting> = HashMap()

// Our device keys
/**
* @return my device info
*/
private val myDevice: MXDeviceInfo

// the encryption is starting
private var mIsStarting: Boolean = false

@ -148,84 +151,6 @@ internal class CryptoManager(
// initialization callbacks
private val mInitializationCallbacks = ArrayList<MatrixCallback<Unit>>()

// Warn the user if some new devices are detected while encrypting a message.
private var mWarnOnUnknownDevices = true

// Set of parameters used to configure/customize the end-to-end crypto.
private var mCryptoConfig: MXCryptoConfig? = null

init {
if (null != cryptoConfig) {
mCryptoConfig = cryptoConfig
} else {
// Consider the default configuration value
mCryptoConfig = MXCryptoConfig()
}

var deviceId = mCredentials.deviceId
// deviceId should always be defined
val refreshDevicesList = !TextUtils.isEmpty(deviceId)

if (TextUtils.isEmpty(deviceId)) {
// use the stored one
deviceId = this.mCryptoStore.getDeviceId()

// Should not happen anymore
TODO()
//mSession.setDeviceId(deviceId)
}

if (TextUtils.isEmpty(deviceId)) {
deviceId = UUID.randomUUID().toString()
// Should not happen anymore
TODO()
//mSession.setDeviceId(deviceId)
Timber.d("Warning: No device id in MXCredentials. An id was created. Think of storing it")
this.mCryptoStore.storeDeviceId(deviceId)
}

myDevice = MXDeviceInfo(deviceId!!, mCredentials.userId)

val keys = HashMap<String, String>()

if (!TextUtils.isEmpty(mOlmDevice.deviceEd25519Key)) {
keys["ed25519:" + mCredentials.deviceId] = mOlmDevice.deviceEd25519Key!!
}

if (!TextUtils.isEmpty(mOlmDevice.deviceCurve25519Key)) {
keys["curve25519:" + mCredentials.deviceId] = mOlmDevice.deviceCurve25519Key!!
}

myDevice.keys = keys

myDevice.algorithms = MXCryptoAlgorithms.supportedAlgorithms()
myDevice.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED

// Add our own deviceinfo to the store
val endToEndDevicesForUser = this.mCryptoStore.getUserDevices(mCredentials.userId)

val myDevices: MutableMap<String, MXDeviceInfo>

if (null != endToEndDevicesForUser) {
myDevices = HashMap(endToEndDevicesForUser)
} else {
myDevices = HashMap()
}

myDevices[myDevice.deviceId] = myDevice

this.mCryptoStore.storeUserDevices(mCredentials.userId, myDevices)

if (refreshDevicesList) {
// ensure to have the up-to-date devices list
// got some issues when upgrading from Riot < 0.6.4
deviceListManager.handleDeviceListsChanges(listOf(mCredentials.userId), null)
}

mKeysBackup.setCryptoInternalListener(this)
mSasVerificationService.setCryptoInternalListener(this)
}

override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback<Unit>) {
mSetDeviceNameTask
.configureWith(SetDeviceNameTask.Params(deviceId, deviceName))
@ -245,7 +170,7 @@ internal class CryptoManager(
}

override fun getMyDevice(): MXDeviceInfo {
return myDevice
return mMyDeviceInfoHolder.myDevice
}

override fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) {
@ -462,7 +387,6 @@ internal class CryptoManager(
*
* @param userId the user id
* @param deviceId the device id
* @param callback the asynchronous callback
*/
override fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo? {
return if (!TextUtils.isEmpty(userId) && !TextUtils.isEmpty(deviceId)) {
@ -529,7 +453,6 @@ internal class CryptoManager(
* @param verificationStatus the new verification status
* @param deviceId the unique identifier for the device.
* @param userId the owner of the device
* @param callback the asynchronous callback
*/
override fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String) {
mSetDeviceVerificationAction.handle(verificationStatus, deviceId, userId)
@ -555,34 +478,20 @@ internal class CryptoManager(
return false
}

val encryptingClass = MXCryptoAlgorithms.encryptorClassForAlgorithm(algorithm)
val encryptingClass = MXCryptoAlgorithms.hasEncryptorClassForAlgorithm(algorithm)

if (null == encryptingClass) {
if (!encryptingClass) {
Timber.e("## setEncryptionInRoom() : Unable to encrypt with " + algorithm!!)
return false
}

mCryptoStore.storeRoomAlgorithm(roomId, algorithm!!)

val alg: IMXEncrypting

try {
val ctor = encryptingClass.constructors[0]
alg = ctor.newInstance() as IMXEncrypting
} catch (e: Exception) {
Timber.e(e, "## setEncryptionInRoom() : fail to load the class")
return false
val alg: IMXEncrypting = when (algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM -> mMXMegolmEncryptionFactory.instantiate(roomId)
else -> mMXOlmEncryptionFactory.instantiate(roomId)
}

alg.initWithMatrixSession(this,
mOlmDevice,
mKeysBackup,
deviceListManager,
mCredentials,
mSendToDeviceTask,
mTaskExecutor,
roomId)

synchronized(mRoomEncryptors) {
mRoomEncryptors.put(roomId, alg)
}
@ -641,44 +550,7 @@ internal class CryptoManager(
return if (null != map) ArrayList(map.values) else ArrayList()
}

/**
* Try to make sure we have established olm sessions for the given users.
* It must be called in getEncryptingThreadHandler() thread.
* The callback is called in the UI thread.
*
* @param users a list of user ids.
* @param callback the asynchronous callback
*/
fun ensureOlmSessionsForUsers(users: List<String>, callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>) {
Timber.d("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users")

val devicesByUser = HashMap<String /* userId */, MutableList<MXDeviceInfo>>()

for (userId in users) {
devicesByUser[userId] = ArrayList()

val devices = getUserDevices(userId)

for (device in devices) {
val key = device.identityKey()

if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) {
// Don't bother setting up session to ourself
continue
}

if (device.isVerified) {
// Don't bother setting up sessions with blocked users
continue
}

devicesByUser[userId]!!.add(device)
}
}

ensureOlmSessionsForDevices(devicesByUser, callback)
}

// TODO Remove ?
/**
* Try to make sure we have established olm sessions for the given devices.
* It must be called in getCryptoHandler() thread.
@ -689,150 +561,9 @@ internal class CryptoManager(
*/
fun ensureOlmSessionsForDevices(devicesByUser: Map<String, List<MXDeviceInfo>>,
callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>?) {
val devicesWithoutSession = ArrayList<MXDeviceInfo>()

val results = MXUsersDevicesMap<MXOlmSessionResult>()

val userIds = devicesByUser.keys

for (userId in userIds) {
val deviceInfos = devicesByUser[userId]

for (deviceInfo in deviceInfos!!) {
val deviceId = deviceInfo.deviceId
val key = deviceInfo.identityKey()

val sessionId = mOlmDevice.getSessionId(key!!)

if (TextUtils.isEmpty(sessionId)) {
devicesWithoutSession.add(deviceInfo)
}

val olmSessionResult = MXOlmSessionResult(deviceInfo, sessionId)
results.setObject(olmSessionResult, userId, deviceId)
}
}

if (devicesWithoutSession.size == 0) {
callback?.onSuccess(results)
return
}

// Prepare the request for claiming one-time keys
val usersDevicesToClaim = MXUsersDevicesMap<String>()

val oneTimeKeyAlgorithm = MXKey.KEY_SIGNED_CURVE_25519_TYPE

for (device in devicesWithoutSession) {
usersDevicesToClaim.setObject(oneTimeKeyAlgorithm, device.userId, device.deviceId)
}

// TODO: this has a race condition - if we try to send another message
// while we are claiming a key, we will end up claiming two and setting up
// two sessions.
//
// That should eventually resolve itself, but it's poor form.

Timber.d("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")

mClaimOneTimeKeysForUsersDeviceTask
.configureWith(ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim))
.dispatchTo(object : MatrixCallback<MXUsersDevicesMap<MXKey>> {
override fun onSuccess(data: MXUsersDevicesMap<MXKey>) {
try {
Timber.d("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $data")

for (userId in userIds) {
val deviceInfos = devicesByUser[userId]

for (deviceInfo in deviceInfos!!) {

var oneTimeKey: MXKey? = null

val deviceIds = data.getUserDeviceIds(userId)

if (null != deviceIds) {
for (deviceId in deviceIds) {
val olmSessionResult = results.getObject(deviceId, userId)

if (null != olmSessionResult!!.mSessionId) {
// We already have a result for this device
continue
}

val key = data.getObject(deviceId, userId)

if (TextUtils.equals(key!!.type, oneTimeKeyAlgorithm)) {
oneTimeKey = key
}

if (null == oneTimeKey) {
Timber.d("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm
+ " for device " + userId + " : " + deviceId)
continue
}

// Update the result for this device in results
olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo)
}
}
}
}
} catch (e: Exception) {
Timber.e(e, "## ensureOlmSessionsForDevices() " + e.message)
}

callback?.onSuccess(results)
}

override fun onFailure(failure: Throwable) {
Timber.e(failure, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed")

callback?.onFailure(failure)
}
})
.executeBy(mTaskExecutor)
mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, callback)
}

private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? {
var sessionId: String? = null

val deviceId = deviceInfo.deviceId
val signKeyId = "ed25519:$deviceId"
val signature = oneTimeKey.signatureForUserId(userId, signKeyId)

if (!TextUtils.isEmpty(signature) && !TextUtils.isEmpty(deviceInfo.fingerprint())) {
var isVerified = false
var errorMessage: String? = null

try {
mOlmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature)
isVerified = true
} catch (e: Exception) {
errorMessage = e.message
}

// Check one-time key signature
if (isVerified) {
sessionId = mOlmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value)

if (!TextUtils.isEmpty(sessionId)) {
Timber.d("## verifyKeyAndStartSession() : Started new sessionid " + sessionId
+ " for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")")
} else {
// Possibly a bad key
Timber.e("## verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId")
}
} else {
Timber.e("## verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId
+ ":" + deviceId + " Error " + errorMessage)
}
}

return sessionId
}


/**
* Encrypt an event content according to the configuration of the room.
*
@ -865,7 +596,7 @@ internal class CryptoManager(
}

// Check whether the event content must be encrypted for the invited members.
val encryptForInvitedMembers = mCryptoConfig!!.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers()
val encryptForInvitedMembers = mCryptoConfig.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers()

val userIds = if (encryptForInvitedMembers) {
room.getActiveRoomMemberIds()
@ -939,7 +670,7 @@ internal class CryptoManager(
val exceptions = ArrayList<MXDecryptionException>()

var result: MXEventDecryptionResult? = null
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(this, event.roomId, eventContent["algorithm"] as String)
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, eventContent["algorithm"] as String)

if (null == alg) {
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String)
@ -954,7 +685,7 @@ internal class CryptoManager(
}

if (null != result) {
results.add(result)
results.add(result) // TODO simplify
}
}

@ -976,72 +707,6 @@ internal class CryptoManager(
mOlmDevice.resetReplayAttackCheckInTimeline(timelineId)
}

/**
* Encrypt an event payload for a list of devices.
* This method must be called from the getCryptoHandler() thread.
*
* @param payloadFields fields to include in the encrypted payload.
* @param deviceInfos list of device infos to encrypt for.
* @return the content for an m.room.encrypted event.
*/
fun encryptMessage(payloadFields: Map<String, Any>, deviceInfos: List<MXDeviceInfo>): EncryptedMessage {
val deviceInfoParticipantKey = HashMap<String, MXDeviceInfo>()
val participantKeys = ArrayList<String>()

for (di in deviceInfos) {
participantKeys.add(di.identityKey()!!)
deviceInfoParticipantKey[di.identityKey()!!] = di
}

val payloadJson = HashMap(payloadFields)

payloadJson["sender"] = mCredentials.userId
payloadJson["sender_device"] = mCredentials.deviceId

// Include the Ed25519 key so that the recipient knows what
// device this message came from.
// We don't need to include the curve25519 key since the
// recipient will already know this from the olm headers.
// When combined with the device keys retrieved from the
// homeserver signed by the ed25519 key this proves that
// the curve25519 key and the ed25519 key are owned by
// the same device.
val keysMap = HashMap<String, String>()
keysMap["ed25519"] = mOlmDevice.deviceEd25519Key!!
payloadJson["keys"] = keysMap

val ciphertext = HashMap<String, Any>()

for (deviceKey in participantKeys) {
val sessionId = mOlmDevice.getSessionId(deviceKey)

if (!TextUtils.isEmpty(sessionId)) {
Timber.d("Using sessionid $sessionId for device $deviceKey")
val deviceInfo = deviceInfoParticipantKey[deviceKey]

payloadJson["recipient"] = deviceInfo!!.userId

val recipientsKeysMap = HashMap<String, String>()
recipientsKeysMap["ed25519"] = deviceInfo.fingerprint()!!
payloadJson["recipient_keys"] = recipientsKeysMap

// FIXME We have to canonicalize the JSON
//JsonUtility.canonicalize(JsonUtility.getGson(false).toJsonTree(payloadJson)).toString()

val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson))
ciphertext[deviceKey] = mOlmDevice.encryptMessage(deviceKey, sessionId!!, payloadString!!)!!
}
}

val res = EncryptedMessage()

res.algorithm = MXCRYPTO_ALGORITHM_OLM
res.senderKey = mOlmDevice.deviceCurve25519Key
res.cipherText = ciphertext

return res
}

/**
* Handle the 'toDevice' event
*
@ -1061,13 +726,7 @@ internal class CryptoManager(
*
* @param event the key event.
*/
private fun onRoomKeyEvent(event: Event?) {
// sanity check
if (null == event) {
Timber.e("## onRoomKeyEvent() : null event")
return
}

private fun onRoomKeyEvent(event: Event) {
val roomKeyContent = event.content.toModel<RoomKeyContent>()!!

if (TextUtils.isEmpty(roomKeyContent.roomId) || TextUtils.isEmpty(roomKeyContent.algorithm)) {
@ -1075,14 +734,14 @@ internal class CryptoManager(
return
}

val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(this, roomKeyContent.roomId, roomKeyContent.algorithm)
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm)

if (null == alg) {
Timber.e("## onRoomKeyEvent() : Unable to handle keys for " + roomKeyContent.algorithm!!)
Timber.e("## onRoomKeyEvent() : Unable to handle keys for " + roomKeyContent.algorithm)
return
}

alg.onRoomKeyEvent(event)
alg.onRoomKeyEvent(event, mKeysBackup)
}

/**
@ -1097,7 +756,7 @@ internal class CryptoManager(
val room = mRoomService.getRoom(roomId)!!

// Check whether the event content must be encrypted for the invited members.
val encryptForInvitedMembers = mCryptoConfig!!.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers()
val encryptForInvitedMembers = mCryptoConfig.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers()

val userIds = if (encryptForInvitedMembers) {
room.getActiveRoomMemberIds()
@ -1138,7 +797,7 @@ internal class CryptoManager(
deviceListManager.startTrackingDeviceList(Arrays.asList(userId))
} else if (membership == Membership.INVITE
&& room.shouldEncryptForInvitedMembers()
&& mCryptoConfig!!.mEnableEncryptionForInvitedMembers) {
&& mCryptoConfig.mEnableEncryptionForInvitedMembers) {
// track the deviceList for this invited user.
// Caution: there's a big edge case here in that federated servers do not
// know what other servers are in the room at the time they've been invited.
@ -1159,14 +818,14 @@ internal class CryptoManager(
private fun uploadDeviceKeys(callback: MatrixCallback<KeysUploadResponse>) {
// Prepare the device keys data to send
// Sign it
val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, myDevice.signalableJSONDictionary())
val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary())

myDevice.signatures = mObjectSigner.signObject(canonicalJson)
getMyDevice().signatures = mObjectSigner.signObject(canonicalJson)

// For now, we set the device id explicitly, as we may not be using the
// same one as used in login.
mUploadKeysTask
.configureWith(UploadKeysTask.Params(myDevice.toDeviceKeys(), null, myDevice.deviceId))
.configureWith(UploadKeysTask.Params(getMyDevice().toDeviceKeys(), null, getMyDevice().deviceId))
.dispatchTo(callback)
.executeBy(mTaskExecutor)
}
@ -1264,98 +923,7 @@ internal class CryptoManager(

Timber.d("## importRoomKeys : JSON parsing " + (t2 - t1) + " ms")

importMegolmSessionsData(importedSessions, true, progressListener, callback)
}

/**
* Import a list of megolm session keys.
*
* @param megolmSessionsData megolm sessions.
* @param backUpKeys true to back up them to the homeserver.
* @param progressListener the progress listener
* @param callback
*/
override fun importMegolmSessionsData(megolmSessionsData: List<MegolmSessionData>,
backUpKeys: Boolean,
progressListener: ProgressListener?,
callback: MatrixCallback<ImportRoomKeysResult>) {
val t0 = System.currentTimeMillis()

val totalNumbersOfKeys = megolmSessionsData.size
var cpt = 0
var lastProgress = 0
var totalNumbersOfImportedKeys = 0

if (progressListener != null) {
progressListener.onProgress(0, 100)
}

val sessions = mOlmDevice.importInboundGroupSessions(megolmSessionsData)

for (megolmSessionData in megolmSessionsData) {
cpt++


val decrypting = roomDecryptorProvider.getOrCreateRoomDecryptor(this, megolmSessionData.roomId, megolmSessionData.algorithm)

if (null != decrypting) {
try {
val sessionId = megolmSessionData.sessionId
Timber.d("## importRoomKeys retrieve mSenderKey " + megolmSessionData.senderKey + " sessionId " + sessionId)

totalNumbersOfImportedKeys++

// cancel any outstanding room key requests for this session
val roomKeyRequestBody = RoomKeyRequestBody()

roomKeyRequestBody.algorithm = megolmSessionData.algorithm
roomKeyRequestBody.roomId = megolmSessionData.roomId
roomKeyRequestBody.senderKey = megolmSessionData.senderKey
roomKeyRequestBody.sessionId = megolmSessionData.sessionId

cancelRoomKeyRequest(roomKeyRequestBody)

// Have another go at decrypting events sent with this session
decrypting.onNewSession(megolmSessionData.senderKey!!, sessionId!!)
} catch (e: Exception) {
Timber.e(e, "## importRoomKeys() : onNewSession failed")
}
}

if (progressListener != null) {
val progress = 100 * cpt / totalNumbersOfKeys

if (lastProgress != progress) {
lastProgress = progress

progressListener.onProgress(progress, 100)
}
}
}

// Do not back up the key if it comes from a backup recovery
if (backUpKeys) {
mKeysBackup.maybeBackupKeys()
} else {
mCryptoStore.markBackupDoneForInboundGroupSessions(sessions)
}

val t1 = System.currentTimeMillis()

Timber.d("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)")

val finalTotalNumbersOfImportedKeys = totalNumbersOfImportedKeys

callback.onSuccess(ImportRoomKeysResult(totalNumbersOfKeys, finalTotalNumbersOfImportedKeys))
}

/**
* Tells if the encryption must fail if some unknown devices are detected.
*
* @return true to warn when some unknown devices are detected.
*/
fun warnOnUnknownDevices(): Boolean {
return mWarnOnUnknownDevices
mMegolmSessionDataImporter.handle(importedSessions, true, progressListener, callback)
}

/**
@ -1364,7 +932,7 @@ internal class CryptoManager(
* @param warn true to warn when some unknown devices are detected.
*/
override fun setWarnOnUnknownDevices(warn: Boolean) {
mWarnOnUnknownDevices = warn
mWarnOnUnknownDevicesRepository.setWarnOnUnknownDevices(warn)
}

/**
@ -1468,7 +1036,6 @@ internal class CryptoManager(
* Add this room to the ones which don't encrypt messages to unverified devices.
*
* @param roomId the room id
* @param callback the asynchronous callback
*/
override fun setRoomBlacklistUnverifiedDevices(roomId: String) {
setRoomBlacklistUnverifiedDevices(roomId, true)
@ -1478,22 +1045,12 @@ internal class CryptoManager(
* Remove this room to the ones which don't encrypt messages to unverified devices.
*
* @param roomId the room id
* @param callback the asynchronous callback
*/
override fun setRoomUnBlacklistUnverifiedDevices(roomId: String) {
setRoomBlacklistUnverifiedDevices(roomId, false)
}

/**
* Send a request for some room keys, if we have not already done so.
*
* @param requestBody requestBody
* @param recipients recipients
*/
fun requestRoomKey(requestBody: RoomKeyRequestBody, recipients: List<Map<String, String>>) {
mOutgoingRoomKeyRequestManager.sendRoomKeyRequest(requestBody, recipients)
}

// TODO Check if this method is still necessary
/**
* Cancel any earlier room key request
*
@ -1548,7 +1105,7 @@ internal class CryptoManager(
* ========================================================================================== */

override fun toString(): String {
return myDevice.userId + " (" + myDevice.deviceId + ")"
return "CryptoManager of " + mCredentials.userId + " (" + mCredentials.deviceId + ")"

}


View File

@ -19,11 +19,16 @@ package im.vector.matrix.android.internal.crypto
import android.content.Context
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
import im.vector.matrix.android.internal.crypto.actions.*
import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryptionFactory
import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory
import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory
import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory
import im.vector.matrix.android.internal.crypto.api.CryptoApi
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.*
import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreMigration
@ -88,7 +93,7 @@ internal class CryptoModule {
}

scope(DefaultSession.SCOPE) {
RoomDecryptorProvider(get(), get(), get(), get(), get())
RoomDecryptorProvider(get(), get())
}

scope(DefaultSession.SCOPE) {
@ -113,6 +118,56 @@ internal class CryptoModule {
SetDeviceVerificationAction(get(), get(), get())
}

// Device info
scope(DefaultSession.SCOPE) {
MyDeviceInfoHolder(get(), get(), get())
}

scope(DefaultSession.SCOPE) {
EnsureOlmSessionsForDevicesAction(get(), get(), get())
}

scope(DefaultSession.SCOPE) {
EnsureOlmSessionsForUsersAction(get(), get(), get())
}

scope(DefaultSession.SCOPE) {
MegolmSessionDataImporter(get(), get(), get(), get())
}

scope(DefaultSession.SCOPE) {
MessageEncrypter(get(), get())
}


scope(DefaultSession.SCOPE) {
WarnOnUnknownDeviceRepository()
}

// Factories
scope(DefaultSession.SCOPE) {
MXMegolmDecryptionFactory(
get(), get(), get(), get(), get(), get(), get(), get(), get()
)
}

scope(DefaultSession.SCOPE) {
MXMegolmEncryptionFactory(
get(), get(), get(), get(), get(), get(), get(), get(), get(), get()
)
}

scope(DefaultSession.SCOPE) {
MXOlmDecryptionFactory(
get(), get()
)
}

scope(DefaultSession.SCOPE) {
MXOlmEncryptionFactory(
get(), get(), get(), get(), get()
)
}

// CryptoManager
scope(DefaultSession.SCOPE) {
@ -131,8 +186,14 @@ internal class CryptoModule {
get(),
get(),
get(),
get(),
get(),
// Actions
get(),
get(),
get(),
// Factory
get(), get(),
// Tasks
get(), get(), get(), get(), get(), get(), get(),
// Task executor
@ -200,6 +261,7 @@ internal class CryptoModule {
get(),
get(),
get(),
get(),
// Task
get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
// Task executor
@ -255,7 +317,7 @@ internal class CryptoModule {
* ========================================================================================== */

scope(DefaultSession.SCOPE) {
DefaultSasVerificationService(get(), get(), get(), get(), get())
DefaultSasVerificationService(get(), get(), get(), get(), get(), get(), get())
}

}

View File

@ -38,7 +38,7 @@ internal class IncomingRoomKeyRequestManager(
private val mReceivedRoomKeyRequestCancellations = ArrayList<IncomingRoomKeyRequestCancellation>()

// the listeners
val mRoomKeysRequestListeners: MutableSet<RoomKeysRequestListener> = HashSet()
private val mRoomKeysRequestListeners: MutableSet<RoomKeysRequestListener> = HashSet()

init {
mReceivedRoomKeyRequests.addAll(mCryptoStore.getPendingIncomingRoomKeyRequests())

View File

@ -17,60 +17,19 @@

package im.vector.matrix.android.internal.crypto

import android.text.TextUtils
import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting
import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting
import timber.log.Timber
import java.util.*

internal object MXCryptoAlgorithms {

// encryptors map
private val mEncryptors: MutableMap<String, Class<IMXEncrypting>>

// decryptors map
private val mDecryptors: MutableMap<String, Class<IMXDecrypting>>

init {
mEncryptors = HashMap()
try {
mEncryptors[MXCRYPTO_ALGORITHM_MEGOLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryption") as Class<IMXEncrypting>
} catch (e: Exception) {
Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_MEGOLM")
}

try {
mEncryptors[MXCRYPTO_ALGORITHM_OLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryption") as Class<IMXEncrypting>
} catch (e: Exception) {
Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_OLM")
}

mDecryptors = HashMap()
try {
mDecryptors[MXCRYPTO_ALGORITHM_MEGOLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryption") as Class<IMXDecrypting>
} catch (e: Exception) {
Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_MEGOLM")
}

try {
mDecryptors[MXCRYPTO_ALGORITHM_OLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryption") as Class<IMXDecrypting>
} catch (e: Exception) {
Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_OLM")
}

}

/**
* Get the class implementing encryption for the provided algorithm.
*
* @param algorithm the algorithm tag.
* @return A class implementing 'IMXEncrypting'.
*/
fun encryptorClassForAlgorithm(algorithm: String?): Class<IMXEncrypting>? {
return if (!TextUtils.isEmpty(algorithm)) {
mEncryptors[algorithm]
} else {
null
fun hasEncryptorClassForAlgorithm(algorithm: String?): Boolean {
return when (algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM,
MXCRYPTO_ALGORITHM_OLM -> true
else -> false
}
}

@ -81,11 +40,11 @@ internal object MXCryptoAlgorithms {
* @return A class implementing 'IMXDecrypting'.
*/

fun decryptorClassForAlgorithm(algorithm: String?): Class<IMXDecrypting>? {
return if (!TextUtils.isEmpty(algorithm)) {
mDecryptors[algorithm]
} else {
null
fun hasDecryptorClassForAlgorithm(algorithm: String?): Boolean {
return when (algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM,
MXCRYPTO_ALGORITHM_OLM -> true
else -> false
}
}

@ -93,6 +52,6 @@ internal object MXCryptoAlgorithms {
* @return The list of registered algorithms.
*/
fun supportedAlgorithms(): List<String> {
return ArrayList(mEncryptors.keys)
return listOf(MXCRYPTO_ALGORITHM_MEGOLM, MXCRYPTO_ALGORITHM_OLM)
}
}

View File

@ -37,7 +37,7 @@ internal class MXOutgoingRoomKeyRequestManager(
private val mTaskExecutor: TaskExecutor) {

// running
var mClientRunning: Boolean = false
private var mClientRunning: Boolean = false

// transaction counter
private var mTxnCtr: Int = 0

View File

@ -0,0 +1,70 @@
/*
* Copyright 2019 New Vector 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 android.text.TextUtils
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import java.util.*

internal class MyDeviceInfoHolder(
// The credentials,
credentials: Credentials,
// the crypto store
cryptoStore: IMXCryptoStore,
// Olm device
olmDevice: MXOlmDevice
) {
// Our device keys
/**
* my device info
*/
val myDevice: MXDeviceInfo = MXDeviceInfo(credentials.deviceId!!, credentials.userId)

init {
val keys = HashMap<String, String>()

if (!TextUtils.isEmpty(olmDevice.deviceEd25519Key)) {
keys["ed25519:" + credentials.deviceId] = olmDevice.deviceEd25519Key!!
}

if (!TextUtils.isEmpty(olmDevice.deviceCurve25519Key)) {
keys["curve25519:" + credentials.deviceId] = olmDevice.deviceCurve25519Key!!
}

myDevice.keys = keys

myDevice.algorithms = MXCryptoAlgorithms.supportedAlgorithms()
myDevice.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED

// Add our own deviceinfo to the store
val endToEndDevicesForUser = cryptoStore.getUserDevices(credentials.userId)

val myDevices: MutableMap<String, MXDeviceInfo>

if (null != endToEndDevicesForUser) {
myDevices = HashMap(endToEndDevicesForUser)
} else {
myDevices = HashMap()
}

myDevices[myDevice.deviceId] = myDevice

cryptoStore.storeUserDevices(credentials.userId, myDevices)
}
}

View File

@ -108,7 +108,9 @@ internal class OneTimeKeysManager(
// But that message might never arrive leaving us stuck with duff
// private keys clogging up our local storage.
// So we need some kind of engineering compromise to balance all of
// these factors. // TODO Why we do not set mOneTimeKeyCount here?
// these factors.
// TODO Why we do not set mOneTimeKeyCount here?
// TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also)
val keyCount = data.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
uploadOTK(keyCount, keyLimit, callback)
}

View File

@ -17,19 +17,15 @@
package im.vector.matrix.android.internal.crypto

import android.text.TextUtils
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryptionFactory
import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory
import timber.log.Timber
import java.util.*

internal class RoomDecryptorProvider(
val mCredentials: Credentials,
val olmDevice: MXOlmDevice,
val deviceListManager: DeviceListManager,
val mSendToDeviceTask: SendToDeviceTask,
val mTaskExecutor: TaskExecutor
private val mMXOlmDecryptionFactory: MXOlmDecryptionFactory,
private val mMXMegolmDecryptionFactory: MXMegolmDecryptionFactory
) {

// A map from algorithm to MXDecrypting instance, for each room
@ -43,20 +39,15 @@ internal class RoomDecryptorProvider(
* @param roomId the room id
* @param algorithm the crypto algorithm
* @return the decryptor
* TODO do not provide cryptoManager? // TODO Create another method for the case of roomId is null
* // TODO Create another method for the case of roomId is null
*/
fun getOrCreateRoomDecryptor(cryptoManager: CryptoManager, roomId: String?, algorithm: String?): IMXDecrypting? {
fun getOrCreateRoomDecryptor(roomId: String?, algorithm: String?): IMXDecrypting? {
// sanity check
if (TextUtils.isEmpty(algorithm)) {
Timber.e("## getRoomDecryptor() : null algorithm")
return null
}

if (null == mRoomDecryptors) {
Timber.e("## getRoomDecryptor() : null mRoomDecryptors")
return null
}

var alg: IMXDecrypting? = null

if (!TextUtils.isEmpty(roomId)) {
@ -73,27 +64,21 @@ internal class RoomDecryptorProvider(
}
}

val decryptingClass = MXCryptoAlgorithms.decryptorClassForAlgorithm(algorithm)
val decryptingClass = MXCryptoAlgorithms.hasDecryptorClassForAlgorithm(algorithm)

if (null != decryptingClass) {
try {
val ctor = decryptingClass.constructors[0]
alg = ctor.newInstance() as IMXDecrypting

if (null != alg) {
alg!!.initWithMatrixSession(mCredentials, cryptoManager, olmDevice, deviceListManager, mSendToDeviceTask, mTaskExecutor)

if (!TextUtils.isEmpty(roomId)) {
synchronized(mRoomDecryptors) {
mRoomDecryptors[roomId]!!.put(algorithm!!, alg!!)
}
}
}
} catch (e: Exception) {
Timber.e(e, "## getRoomDecryptor() : fail to load the class")
return null
if (decryptingClass) {
alg = when (algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM -> mMXMegolmDecryptionFactory.instantiate()
else -> mMXOlmDecryptionFactory.instantiate()
}

if (null != alg) {
if (!TextUtils.isEmpty(roomId)) {
synchronized(mRoomDecryptors) {
mRoomDecryptors[roomId]!!.put(algorithm!!, alg!!)
}
}
}
}

return alg

View File

@ -0,0 +1,182 @@
/*
* Copyright 2019 New Vector 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.actions

import android.text.TextUtils
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.internal.crypto.MXOlmDevice
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXKey
import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import timber.log.Timber
import java.util.*

internal class EnsureOlmSessionsForDevicesAction(private val mOlmDevice: MXOlmDevice,
private val mClaimOneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask,
private val mTaskExecutor: TaskExecutor) {


fun handle(devicesByUser: Map<String, List<MXDeviceInfo>>, callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>?) {
val devicesWithoutSession = ArrayList<MXDeviceInfo>()

val results = MXUsersDevicesMap<MXOlmSessionResult>()

val userIds = devicesByUser.keys

for (userId in userIds) {
val deviceInfos = devicesByUser[userId]

for (deviceInfo in deviceInfos!!) {
val deviceId = deviceInfo.deviceId
val key = deviceInfo.identityKey()

val sessionId = mOlmDevice.getSessionId(key!!)

if (TextUtils.isEmpty(sessionId)) {
devicesWithoutSession.add(deviceInfo)
}

val olmSessionResult = MXOlmSessionResult(deviceInfo, sessionId)
results.setObject(olmSessionResult, userId, deviceId)
}
}

if (devicesWithoutSession.size == 0) {
callback?.onSuccess(results)
return
}

// Prepare the request for claiming one-time keys
val usersDevicesToClaim = MXUsersDevicesMap<String>()

val oneTimeKeyAlgorithm = MXKey.KEY_SIGNED_CURVE_25519_TYPE

for (device in devicesWithoutSession) {
usersDevicesToClaim.setObject(oneTimeKeyAlgorithm, device.userId, device.deviceId)
}

// TODO: this has a race condition - if we try to send another message
// while we are claiming a key, we will end up claiming two and setting up
// two sessions.
//
// That should eventually resolve itself, but it's poor form.

Timber.d("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")

mClaimOneTimeKeysForUsersDeviceTask
.configureWith(ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim))
.dispatchTo(object : MatrixCallback<MXUsersDevicesMap<MXKey>> {
override fun onSuccess(data: MXUsersDevicesMap<MXKey>) {
try {
Timber.d("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $data")

for (userId in userIds) {
val deviceInfos = devicesByUser[userId]

for (deviceInfo in deviceInfos!!) {

var oneTimeKey: MXKey? = null

val deviceIds = data.getUserDeviceIds(userId)

if (null != deviceIds) {
for (deviceId in deviceIds) {
val olmSessionResult = results.getObject(deviceId, userId)

if (null != olmSessionResult!!.mSessionId) {
// We already have a result for this device
continue
}

val key = data.getObject(deviceId, userId)

if (TextUtils.equals(key!!.type, oneTimeKeyAlgorithm)) {
oneTimeKey = key
}

if (null == oneTimeKey) {
Timber.d("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm
+ " for device " + userId + " : " + deviceId)
continue
}

// Update the result for this device in results
olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo)
}
}
}
}
} catch (e: Exception) {
Timber.e(e, "## ensureOlmSessionsForDevices() " + e.message)
}

callback?.onSuccess(results)
}

override fun onFailure(failure: Throwable) {
Timber.e(failure, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed")

callback?.onFailure(failure)
}
})
.executeBy(mTaskExecutor)
}


private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? {
var sessionId: String? = null

val deviceId = deviceInfo.deviceId
val signKeyId = "ed25519:$deviceId"
val signature = oneTimeKey.signatureForUserId(userId, signKeyId)

if (!TextUtils.isEmpty(signature) && !TextUtils.isEmpty(deviceInfo.fingerprint())) {
var isVerified = false
var errorMessage: String? = null

try {
mOlmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature)
isVerified = true
} catch (e: Exception) {
errorMessage = e.message
}

// Check one-time key signature
if (isVerified) {
sessionId = mOlmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value)

if (!TextUtils.isEmpty(sessionId)) {
Timber.d("## verifyKeyAndStartSession() : Started new sessionid " + sessionId
+ " for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")")
} else {
// Possibly a bad key
Timber.e("## verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId")
}
} else {
Timber.e("## verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId
+ ":" + deviceId + " Error " + errorMessage)
}
}

return sessionId
}

}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2019 New Vector 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.actions

import android.text.TextUtils
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.internal.crypto.MXOlmDevice
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import timber.log.Timber
import java.util.*

internal class EnsureOlmSessionsForUsersAction(private val mOlmDevice: MXOlmDevice,
private val mCryptoStore: IMXCryptoStore,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction) {

/**
* Try to make sure we have established olm sessions for the given users.
* It must be called in getEncryptingThreadHandler() thread.
* The callback is called in the UI thread.
*
* @param users a list of user ids.
* @param callback the asynchronous callback
*/
fun handle(users: List<String>, callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>) {
Timber.d("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users")

val devicesByUser = HashMap<String /* userId */, MutableList<MXDeviceInfo>>()

for (userId in users) {
devicesByUser[userId] = ArrayList()

val devices = mCryptoStore.getUserDevices(userId)?.values ?: emptyList()

for (device in devices) {
val key = device.identityKey()

if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) {
// Don't bother setting up session to ourself
continue
}

if (device.isVerified) {
// Don't bother setting up sessions with blocked users
continue
}

devicesByUser[userId]!!.add(device)
}
}

mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, callback)
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright 2019 New Vector 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.actions

import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.listeners.ProgressListener
import im.vector.matrix.android.internal.crypto.*
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import timber.log.Timber

internal class MegolmSessionDataImporter(private val mOlmDevice: MXOlmDevice,
private val roomDecryptorProvider: RoomDecryptorProvider,
private val mOutgoingRoomKeyRequestManager: MXOutgoingRoomKeyRequestManager,
private val mCryptoStore: IMXCryptoStore) {

/**
* Import a list of megolm session keys.
*
* @param megolmSessionsData megolm sessions.
* @param backUpKeys true to back up them to the homeserver.
* @param progressListener the progress listener
* @param callback
*/
fun handle(megolmSessionsData: List<MegolmSessionData>,
fromBackup: Boolean,
progressListener: ProgressListener?,
callback: MatrixCallback<ImportRoomKeysResult>) {
val t0 = System.currentTimeMillis()

val totalNumbersOfKeys = megolmSessionsData.size
var cpt = 0
var lastProgress = 0
var totalNumbersOfImportedKeys = 0

if (progressListener != null) {
CryptoAsyncHelper.getUiHandler().post {
progressListener.onProgress(0, 100)
}
}

val sessions = mOlmDevice.importInboundGroupSessions(megolmSessionsData)

for (megolmSessionData in megolmSessionsData) {
cpt++


val decrypting = roomDecryptorProvider.getOrCreateRoomDecryptor(megolmSessionData.roomId, megolmSessionData.algorithm)

if (null != decrypting) {
try {
val sessionId = megolmSessionData.sessionId
Timber.d("## importRoomKeys retrieve mSenderKey " + megolmSessionData.senderKey + " sessionId " + sessionId)

totalNumbersOfImportedKeys++

// cancel any outstanding room key requests for this session
val roomKeyRequestBody = RoomKeyRequestBody()

roomKeyRequestBody.algorithm = megolmSessionData.algorithm
roomKeyRequestBody.roomId = megolmSessionData.roomId
roomKeyRequestBody.senderKey = megolmSessionData.senderKey
roomKeyRequestBody.sessionId = megolmSessionData.sessionId

mOutgoingRoomKeyRequestManager.cancelRoomKeyRequest(roomKeyRequestBody)

// Have another go at decrypting events sent with this session
decrypting.onNewSession(megolmSessionData.senderKey!!, sessionId!!)
} catch (e: Exception) {
Timber.e(e, "## importRoomKeys() : onNewSession failed")
}
}

if (progressListener != null) {
CryptoAsyncHelper.getUiHandler().post {
val progress = 100 * cpt / totalNumbersOfKeys

if (lastProgress != progress) {
lastProgress = progress

progressListener.onProgress(progress, 100)
}
}
}
}

// Do not back up the key if it comes from a backup recovery
if (fromBackup) {
mCryptoStore.markBackupDoneForInboundGroupSessions(sessions)
}

val t1 = System.currentTimeMillis()

Timber.d("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)")

val finalTotalNumbersOfImportedKeys = totalNumbersOfImportedKeys

CryptoAsyncHelper.getUiHandler().post {
callback.onSuccess(ImportRoomKeysResult(totalNumbersOfKeys, finalTotalNumbersOfImportedKeys))
}
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2019 New Vector 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.actions

import android.text.TextUtils
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_OLM
import im.vector.matrix.android.internal.crypto.MXOlmDevice
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedMessage
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.util.convertToUTF8
import timber.log.Timber
import java.util.*

internal class MessageEncrypter(private val mCredentials: Credentials,
private val mOlmDevice: MXOlmDevice) {

/**
* Encrypt an event payload for a list of devices.
* This method must be called from the getCryptoHandler() thread.
*
* @param payloadFields fields to include in the encrypted payload.
* @param deviceInfos list of device infos to encrypt for.
* @return the content for an m.room.encrypted event.
*/
fun encryptMessage(payloadFields: Map<String, Any>, deviceInfos: List<MXDeviceInfo>): EncryptedMessage {
val deviceInfoParticipantKey = HashMap<String, MXDeviceInfo>()
val participantKeys = ArrayList<String>()

for (di in deviceInfos) {
participantKeys.add(di.identityKey()!!)
deviceInfoParticipantKey[di.identityKey()!!] = di
}

val payloadJson = HashMap(payloadFields)

payloadJson["sender"] = mCredentials.userId
payloadJson["sender_device"] = mCredentials.deviceId

// Include the Ed25519 key so that the recipient knows what
// device this message came from.
// We don't need to include the curve25519 key since the
// recipient will already know this from the olm headers.
// When combined with the device keys retrieved from the
// homeserver signed by the ed25519 key this proves that
// the curve25519 key and the ed25519 key are owned by
// the same device.
val keysMap = HashMap<String, String>()
keysMap["ed25519"] = mOlmDevice.deviceEd25519Key!!
payloadJson["keys"] = keysMap

val ciphertext = HashMap<String, Any>()

for (deviceKey in participantKeys) {
val sessionId = mOlmDevice.getSessionId(deviceKey)

if (!TextUtils.isEmpty(sessionId)) {
Timber.d("Using sessionid $sessionId for device $deviceKey")
val deviceInfo = deviceInfoParticipantKey[deviceKey]

payloadJson["recipient"] = deviceInfo!!.userId

val recipientsKeysMap = HashMap<String, String>()
recipientsKeysMap["ed25519"] = deviceInfo.fingerprint()!!
payloadJson["recipient_keys"] = recipientsKeysMap

// FIXME We have to canonicalize the JSON
//JsonUtility.canonicalize(JsonUtility.getGson(false).toJsonTree(payloadJson)).toString()

val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson))
ciphertext[deviceKey] = mOlmDevice.encryptMessage(deviceKey, sessionId!!, payloadString!!)!!
}
}

val res = EncryptedMessage()

res.algorithm = MXCRYPTO_ALGORITHM_OLM
res.senderKey = mOlmDevice.deviceCurve25519Key
res.cipherText = ciphertext

return res
}

}

View File

@ -17,30 +17,17 @@

package im.vector.matrix.android.internal.crypto.algorithms

import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.crypto.*
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor
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

/**
* An interface for decrypting data
*/
internal interface IMXDecrypting {

/**
* Init the object fields
*
* @param matrixSession the session
*/
fun initWithMatrixSession(credentials: Credentials,
crypto: CryptoManager,
olmDevice: MXOlmDevice,
deviceListManager: DeviceListManager,
sendToDeviceTask: SendToDeviceTask,
taskExecutor: TaskExecutor)

/**
* Decrypt an event
*
@ -57,7 +44,7 @@ internal interface IMXDecrypting {
*
* @param event the key event.
*/
fun onRoomKeyEvent(event: Event)
fun onRoomKeyEvent(event: Event, keysBackup: KeysBackup) {}

/**
* Check if the some messages can be decrypted with a new session
@ -65,7 +52,7 @@ internal interface IMXDecrypting {
* @param senderKey the session sender key
* @param sessionId the session id
*/
fun onNewSession(senderKey: String, sessionId: String)
fun onNewSession(senderKey: String, sessionId: String) {}

/**
* Determine if we have the keys necessary to respond to a room key request
@ -73,12 +60,12 @@ internal interface IMXDecrypting {
* @param request keyRequest
* @return true if we have the keys and could (theoretically) share
*/
fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean
fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean = false

/**
* Send the response to a room key request.
*
* @param request keyRequest
*/
fun shareKeysWithDevice(request: IncomingRoomKeyRequest)
fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {}
}

View File

@ -18,35 +18,13 @@
package im.vector.matrix.android.internal.crypto.algorithms

import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.internal.crypto.DeviceListManager
import im.vector.matrix.android.internal.crypto.MXOlmDevice
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor

/**
* An interface for encrypting data
*/
internal interface IMXEncrypting {

/**
* Init
*
* @param matrixSession the related 'MXSession'.
* @param roomId the id of the room we will be sending to.
*/
fun initWithMatrixSession(crypto: CryptoManager,
olmDevice: MXOlmDevice,
keysBackup: KeysBackup,
deviceListManager: DeviceListManager,
credentials: Credentials,
sendToDeviceTask: SendToDeviceTask,
taskExecutor: TaskExecutor,
roomId: String)

/**
* Encrypt an event content according to the configuration of the room.
*

View File

@ -18,7 +18,6 @@
package im.vector.matrix.android.internal.crypto.algorithms.megolm

import android.text.TextUtils
import androidx.annotation.Keep
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.crypto.MXCryptoError
@ -26,8 +25,11 @@ 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.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.MXOlmSessionResult
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
@ -42,44 +44,22 @@ import im.vector.matrix.android.internal.task.configureWith
import timber.log.Timber
import java.util.*

@Keep
internal class MXMegolmDecryption : IMXDecrypting {
/**
* The olm device interface
*/

// the matrix credentials
private lateinit var mCredentials: Credentials

private lateinit var mCrypto: CryptoManager
private lateinit var mOlmDevice: MXOlmDevice
private lateinit var mDeviceListManager: DeviceListManager
private lateinit var mCryptoStore: IMXCryptoStore
private lateinit var mSendToDeviceTask: SendToDeviceTask
private lateinit var mTaskExecutor: TaskExecutor
internal class MXMegolmDecryption(private val mCredentials: Credentials,
private val mOlmDevice: MXOlmDevice,
private val mDeviceListManager: DeviceListManager,
private val mOutgoingRoomKeyRequestManager: MXOutgoingRoomKeyRequestManager,
private val mMessageEncrypter: MessageEncrypter,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
private val mCryptoStore: IMXCryptoStore,
private val mSendToDeviceTask: SendToDeviceTask,
private val mTaskExecutor: TaskExecutor)
: IMXDecrypting {

/**
* Events which we couldn't decrypt due to unknown sessions / indexes: map from
* senderKey|sessionId to timelines to list of MatrixEvents.
*/
private var mPendingEvents: MutableMap<String /* senderKey|sessionId */, MutableMap<String /* timelineId */, MutableList<Event>>> = HashMap()

/**
* Init the object fields
*/
override fun initWithMatrixSession(credentials: Credentials,
crypto: CryptoManager,
olmDevice: MXOlmDevice,
deviceListManager: DeviceListManager,
sendToDeviceTask: SendToDeviceTask,
taskExecutor: TaskExecutor) {
mCredentials = credentials
mCrypto = crypto
mOlmDevice = olmDevice
mDeviceListManager = deviceListManager
mSendToDeviceTask = sendToDeviceTask
mTaskExecutor = taskExecutor
}
private var mPendingEvents: MutableMap<String /* senderKey|sessionId */, MutableMap<String /* timelineId */, MutableList<Event>>> = HashMap()

@Throws(MXDecryptionException::class)
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? {
@ -87,8 +67,8 @@ internal class MXMegolmDecryption : IMXDecrypting {
}

@Throws(MXDecryptionException::class)
private fun decryptEvent(event: Event?, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult? {
// sanity check
private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult? {
// sanity check // TODO Remove check
if (null == event) {
Timber.e("## decryptEvent() : null event")
return null
@ -185,7 +165,7 @@ internal class MXMegolmDecryption : IMXDecrypting {
requestBody.senderKey = encryptedEventContent.senderKey
requestBody.sessionId = encryptedEventContent.sessionId

mCrypto.requestRoomKey(requestBody, recipients)
mOutgoingRoomKeyRequestManager.sendRoomKeyRequest(requestBody, recipients)
}

/**
@ -217,9 +197,9 @@ internal class MXMegolmDecryption : IMXDecrypting {
/**
* Handle a key event.
*
* @param roomKeyEvent the key event.
* @param event the key event.
*/
override fun onRoomKeyEvent(event: Event) {
override fun onRoomKeyEvent(event: Event, keysBackup: KeysBackup) {
var exportFormat = false
val roomKeyContent = event.content.toModel<RoomKeyContent>()!!

@ -274,7 +254,7 @@ internal class MXMegolmDecryption : IMXDecrypting {
val added = mOlmDevice.addInboundGroupSession(roomKeyContent.sessionId!!, roomKeyContent.sessionKey!!, roomKeyContent.roomId!!, senderKey, forwarding_curve25519_key_chain!!, keysClaimed, exportFormat)

if (added) {
mCrypto.getKeysBackupService().maybeBackupKeys()
keysBackup.maybeBackupKeys()

val content = RoomKeyRequestBody()

@ -283,7 +263,7 @@ internal class MXMegolmDecryption : IMXDecrypting {
content.sessionId = roomKeyContent.sessionId
content.senderKey = senderKey

mCrypto.cancelRoomKeyRequest(content)
mOutgoingRoomKeyRequestManager.cancelRoomKeyRequest(content)

onNewSession(senderKey, roomKeyContent.sessionId!!)
}
@ -334,14 +314,13 @@ internal class MXMegolmDecryption : IMXDecrypting {
}

override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean {
return (null != request
&& null != request.mRequestBody
return (null != request.mRequestBody
&& mOlmDevice.hasInboundSessionKeys(request.mRequestBody!!.roomId!!, request.mRequestBody!!.senderKey!!, request.mRequestBody!!.sessionId!!))
}

override fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {
// sanity checks
if (null == request || null == request.mRequestBody) {
if (request.mRequestBody == null) {
return
}

@ -358,9 +337,9 @@ internal class MXMegolmDecryption : IMXDecrypting {
val devicesByUser = HashMap<String, List<MXDeviceInfo>>()
devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo))

mCrypto.ensureOlmSessionsForDevices(devicesByUser, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
override fun onSuccess(map: MXUsersDevicesMap<MXOlmSessionResult>) {
val olmSessionResult = map.getObject(deviceId, userId)
mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) {
val olmSessionResult = data.getObject(deviceId, userId)

if (null == olmSessionResult || null == olmSessionResult.mSessionId) {
// no session with this device, probably because there
@ -380,7 +359,7 @@ internal class MXMegolmDecryption : IMXDecrypting {
payloadJson["type"] = EventType.FORWARDED_ROOM_KEY
payloadJson["content"] = inboundGroupSession!!.exportKeys()!!

val encodedPayload = mCrypto.encryptMessage(payloadJson, Arrays.asList(deviceInfo))
val encodedPayload = mMessageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo))
val sendToDeviceMap = MXUsersDevicesMap<Any>()
sendToDeviceMap.setObject(encodedPayload, userId, deviceId)


View File

@ -0,0 +1,51 @@
/*
* Copyright 2019 New Vector 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.algorithms.megolm

import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.crypto.DeviceListManager
import im.vector.matrix.android.internal.crypto.MXOlmDevice
import im.vector.matrix.android.internal.crypto.MXOutgoingRoomKeyRequestManager
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.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor

internal class MXMegolmDecryptionFactory(private val mCredentials: Credentials,
private val mOlmDevice: MXOlmDevice,
private val mDeviceListManager: DeviceListManager,
private val mOutgoingRoomKeyRequestManager: MXOutgoingRoomKeyRequestManager,
private val mMessageEncrypter: MessageEncrypter,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
private val mCryptoStore: IMXCryptoStore,
private val mSendToDeviceTask: SendToDeviceTask,
private val mTaskExecutor: TaskExecutor) {

fun instantiate(): MXMegolmDecryption {
return MXMegolmDecryption(
mCredentials,
mOlmDevice,
mDeviceListManager,
mOutgoingRoomKeyRequestManager,
mMessageEncrypter,
mEnsureOlmSessionsForDevicesAction,
mCryptoStore,
mSendToDeviceTask,
mTaskExecutor)
}
}

View File

@ -19,7 +19,6 @@
package im.vector.matrix.android.internal.crypto.algorithms.megolm

import android.text.TextUtils
import androidx.annotation.Keep
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.failure.Failure
@ -27,13 +26,20 @@ 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
import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.internal.crypto.*
import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper
import im.vector.matrix.android.internal.crypto.DeviceListManager
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import im.vector.matrix.android.internal.crypto.MXOlmDevice
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.IMXEncrypting
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.MXOlmSessionResult
import im.vector.matrix.android.internal.crypto.model.MXQueuedEncryption
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.task.TaskExecutor
@ -42,22 +48,22 @@ import im.vector.matrix.android.internal.util.convertToUTF8
import timber.log.Timber
import java.util.*

@Keep
internal class MXMegolmEncryption : IMXEncrypting {
internal class MXMegolmEncryption(
// The id of the room we will be sending to.
private var mRoomId: String,

private lateinit var mCrypto: CryptoManager
private lateinit var olmDevice: MXOlmDevice
private lateinit var mKeysBackup: KeysBackup
private lateinit var mDeviceListManager: DeviceListManager
private val olmDevice: MXOlmDevice,
private val mKeysBackup: KeysBackup,
private val mCryptoStore: IMXCryptoStore,
private val mDeviceListManager: DeviceListManager,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
private val mCredentials: Credentials,
private val mSendToDeviceTask: SendToDeviceTask,
private val mTaskExecutor: TaskExecutor,
private val mMessageEncrypter: MessageEncrypter,
private val mWarnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository
) : IMXEncrypting {

private lateinit var mCredentials: Credentials
private lateinit var mSendToDeviceTask: SendToDeviceTask
private lateinit var mTaskExecutor: TaskExecutor

// The id of the room we will be sending to.
private lateinit var mRoomId: String

private var mDeviceId: String? = null

// OutboundSessionInfo. Null if we haven't yet started setting one up. Note
// that even if this is non-null, it may not be ready for use (in which
@ -69,9 +75,11 @@ internal class MXMegolmEncryption : IMXEncrypting {

private val mPendingEncryptions = ArrayList<MXQueuedEncryption>()

// Default rotation periods
// TODO: Make it configurable via parameters
// Session rotation periods
private var mSessionRotationPeriodMsgs: Int = 0
private var mSessionRotationPeriodMs: Int = 0
private var mSessionRotationPeriodMsgs: Int = 100
private var mSessionRotationPeriodMs: Int = 7 * 24 * 3600 * 1000

/**
* @return a snapshot of the pending encryptions
@ -87,32 +95,6 @@ internal class MXMegolmEncryption : IMXEncrypting {
return list
}

override fun initWithMatrixSession(crypto: CryptoManager,
olmDevice: MXOlmDevice,
keysBackup: KeysBackup,
deviceListManager: DeviceListManager,
credentials: Credentials,
sendToDeviceTask: SendToDeviceTask,
taskExecutor: TaskExecutor,
roomId: String) {
mCrypto = crypto
this.olmDevice = olmDevice
mDeviceListManager = deviceListManager
mKeysBackup = keysBackup
mCredentials = credentials
mSendToDeviceTask = sendToDeviceTask
mTaskExecutor = taskExecutor

mRoomId = roomId
mDeviceId = mCredentials.deviceId


// Default rotation periods
// TODO: Make it configurable via parameters
mSessionRotationPeriodMsgs = 100
mSessionRotationPeriodMs = 7 * 24 * 3600 * 1000
}

override fun encryptEventContent(eventContent: Content,
eventType: String,
userIds: List<String>,
@ -334,7 +316,7 @@ internal class MXMegolmEncryption : IMXEncrypting {
val t0 = System.currentTimeMillis()
Timber.d("## shareUserDevicesKey() : starts")

mCrypto.ensureOlmSessionsForDevices(devicesByUser, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) {
Timber.d("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
+ (System.currentTimeMillis() - t0) + " ms")
@ -367,7 +349,7 @@ internal class MXMegolmEncryption : IMXEncrypting {

Timber.d("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID")
//noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument
contentMap.setObject(mCrypto.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID)
contentMap.setObject(mMessageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID)
haveTargets = true
}
}
@ -453,7 +435,7 @@ internal class MXMegolmEncryption : IMXEncrypting {

// 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"] = mDeviceId!!
map["device_id"] = mCredentials.deviceId!!

CryptoAsyncHelper.getUiHandler().post { queuedEncryption.mApiCallback?.onSuccess(map.toContent()!!) }

@ -480,7 +462,8 @@ internal class MXMegolmEncryption : IMXEncrypting {
// an m.new_device.
mDeviceListManager.downloadKeys(userIds, false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
val encryptToVerifiedDevicesOnly = mCrypto.getGlobalBlacklistUnverifiedDevices() || mCrypto.isRoomBlacklistUnverifiedDevices(mRoomId)
val encryptToVerifiedDevicesOnly = mCryptoStore.getGlobalBlacklistUnverifiedDevices()
|| mCryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(mRoomId)

val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
@ -491,7 +474,7 @@ internal class MXMegolmEncryption : IMXEncrypting {
for (deviceId in deviceIds!!) {
val deviceInfo = data.getObject(deviceId, userId)

if (mCrypto.warnOnUnknownDevices() && deviceInfo!!.isUnknown) {
if (mWarnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) {
// The device is not yet known by the user
unknownDevices.setObject(deviceInfo, userId, deviceId)
continue

View File

@ -0,0 +1,58 @@
/*
* Copyright 2019 New Vector 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.algorithms.megolm

import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.crypto.DeviceListManager
import im.vector.matrix.android.internal.crypto.MXOlmDevice
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.keysbackup.KeysBackup
import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor

internal class MXMegolmEncryptionFactory(
private val olmDevice: MXOlmDevice,
private val mKeysBackup: KeysBackup,
private val mCryptoStore: IMXCryptoStore,
private val mDeviceListManager: DeviceListManager,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,

private val mCredentials: Credentials,
private val mSendToDeviceTask: SendToDeviceTask,
private val mTaskExecutor: TaskExecutor,
private val mMessageEncrypter: MessageEncrypter,
private val mWarnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository) {

fun instantiate(roomId: String): MXMegolmEncryption {
return MXMegolmEncryption(
roomId,

olmDevice,
mKeysBackup,
mCryptoStore,
mDeviceListManager,
mEnsureOlmSessionsForDevicesAction,
mCredentials,
mSendToDeviceTask,
mTaskExecutor,
mMessageEncrypter,
mWarnOnUnknownDevicesRepository)
}
}

View File

@ -18,60 +18,30 @@
package im.vector.matrix.android.internal.crypto.algorithms.olm

import android.text.TextUtils
import androidx.annotation.Keep
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.internal.crypto.*
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
import im.vector.matrix.android.internal.crypto.model.event.OlmEventContent
import im.vector.matrix.android.internal.crypto.model.event.OlmPayloadContent
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.util.convertFromUTF8
import timber.log.Timber
import java.util.*

/**
* An interface for encrypting data
*/
@Keep
internal class MXOlmDecryption : IMXDecrypting {
internal class MXOlmDecryption(
// The olm device interface
private val mOlmDevice: MXOlmDevice,

// The olm device interface
private lateinit var mOlmDevice: MXOlmDevice

// the matrix credentials
private lateinit var mCredentials: Credentials

private lateinit var mCrypto: CryptoManager
private lateinit var mCryptoStore: IMXCryptoStore
private lateinit var mSendToDeviceTask: SendToDeviceTask
private lateinit var mTaskExecutor: TaskExecutor

override fun initWithMatrixSession(credentials: Credentials,
crypto: CryptoManager,
olmDevice: MXOlmDevice,
deviceListManager: DeviceListManager,
sendToDeviceTask: SendToDeviceTask,
taskExecutor: TaskExecutor) {
mCredentials = credentials
mCrypto = crypto
mOlmDevice = olmDevice
mSendToDeviceTask = sendToDeviceTask
mTaskExecutor = taskExecutor
}
// the matrix credentials
private val mCredentials: Credentials)
: IMXDecrypting {

@Throws(MXDecryptionException::class)
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? {
// sanity check
if (null == event) {
Timber.e("## decryptEvent() : null event")
return null
}

val olmEventContent = event.content.toModel<OlmEventContent>()!!

if (null == olmEventContent.ciphertext) {
@ -89,7 +59,7 @@ internal class MXOlmDecryption : IMXDecrypting {

// The message for myUser
val message = olmEventContent.ciphertext!![mOlmDevice.deviceCurve25519Key] as Map<String, Any>
val payloadString = decryptMessage(message, olmEventContent.senderKey)
val payloadString = decryptMessage(message, olmEventContent.senderKey!!)

if (null == payloadString) {
Timber.e("## decryptEvent() Failed to decrypt Olm event (id= " + event.eventId + " ) from " + olmEventContent.senderKey)
@ -164,27 +134,13 @@ internal class MXOlmDecryption : IMXDecrypting {
}

val result = MXEventDecryptionResult()
// TODO result.mClearEvent = payload
// FIXME result.mClearEvent = payload
result.mSenderCurve25519Key = olmEventContent.senderKey
result.mClaimedEd25519Key = olmPayloadContent.keys!!.get("ed25519")

return result
}

override fun onRoomKeyEvent(event: Event) {
// No impact for olm
}

override fun onNewSession(senderKey: String, sessionId: String) {
// No impact for olm
}

override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean {
return false
}

override fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {}

/**
* Attempt to decrypt an Olm message.
*
@ -192,8 +148,8 @@ internal class MXOlmDecryption : IMXDecrypting {
* @param message message object, with 'type' and 'body' fields.
* @return payload, if decrypted successfully.
*/
private fun decryptMessage(message: Map<String, Any>, theirDeviceIdentityKey: String?): String? {
val sessionIdsSet = mOlmDevice.getSessionIds(theirDeviceIdentityKey!!)
private fun decryptMessage(message: Map<String, Any>, theirDeviceIdentityKey: String): String? {
val sessionIdsSet = mOlmDevice.getSessionIds(theirDeviceIdentityKey)

val sessionIds: List<String>


View File

@ -0,0 +1,30 @@
/*
* Copyright 2019 New Vector 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.algorithms.olm

import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.crypto.MXOlmDevice

internal class MXOlmDecryptionFactory(private val mOlmDevice: MXOlmDevice,
private val mCredentials: Credentials) {

fun instantiate(): MXOlmDecryption {
return MXOlmDecryption(
mOlmDevice,
mCredentials)
}
}

View File

@ -19,49 +19,30 @@
package im.vector.matrix.android.internal.crypto.algorithms.olm

import android.text.TextUtils
import androidx.annotation.Keep
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.crypto.DeviceListManager
import im.vector.matrix.android.internal.crypto.MXOlmDevice
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting
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.MXOlmSessionResult
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import java.util.*

@Keep
internal class MXOlmEncryption : IMXEncrypting {
private lateinit var mCrypto: CryptoManager
private lateinit var mOlmDevice: MXOlmDevice
private lateinit var mDeviceListManager: DeviceListManager
internal class MXOlmEncryption(
private var mRoomId: String,

private lateinit var mCredentials: Credentials
private lateinit var mSendToDeviceTask: SendToDeviceTask
private lateinit var mTaskExecutor: TaskExecutor
private val mOlmDevice: MXOlmDevice,
private val mCryptoStore: IMXCryptoStore,
private val mMessageEncrypter: MessageEncrypter,
private val mDeviceListManager: DeviceListManager,
private val mEnsureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction)
: IMXEncrypting {

private lateinit var mRoomId: String

override fun initWithMatrixSession(crypto: CryptoManager,
olmDevice: MXOlmDevice,
keysBackup: KeysBackup,
deviceListManager: DeviceListManager,
credentials: Credentials,
sendToDeviceTask: SendToDeviceTask,
taskExecutor: TaskExecutor,
roomId: String) {
mCrypto = crypto
mOlmDevice = olmDevice
mDeviceListManager = deviceListManager

mRoomId = roomId
}

override fun encryptEventContent(eventContent: Content,
eventType: String,
@ -75,24 +56,22 @@ internal class MXOlmEncryption : IMXEncrypting {
val deviceInfos = ArrayList<MXDeviceInfo>()

for (userId in userIds) {
val devices = mCrypto.getUserDevices(userId)
val devices = mCryptoStore.getUserDevices(userId)?.values ?: emptyList()

if (null != devices) {
for (device in devices) {
val key = device.identityKey()
for (device in devices) {
val key = device.identityKey()

if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) {
// Don't bother setting up session to ourself
continue
}

if (device.isBlocked) {
// Don't bother setting up sessions with blocked users
continue
}

deviceInfos.add(device)
if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) {
// Don't bother setting up session to ourself
continue
}

if (device.isBlocked) {
// Don't bother setting up sessions with blocked users
continue
}

deviceInfos.add(device)
}
}

@ -101,7 +80,7 @@ internal class MXOlmEncryption : IMXEncrypting {
messageMap["type"] = eventType
messageMap["content"] = eventContent

mCrypto.encryptMessage(messageMap, deviceInfos)
mMessageEncrypter.encryptMessage(messageMap, deviceInfos)

callback.onSuccess(messageMap.toContent()!!)
}
@ -118,7 +97,7 @@ internal class MXOlmEncryption : IMXEncrypting {
mDeviceListManager.downloadKeys(users, false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {

override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
mCrypto.ensureOlmSessionsForUsers(users, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
mEnsureOlmSessionsForUsersAction.handle(users, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) {
callback?.onSuccess(Unit)
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2019 New Vector 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.algorithms.olm

import im.vector.matrix.android.internal.crypto.DeviceListManager
import im.vector.matrix.android.internal.crypto.MXOlmDevice
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore

internal class MXOlmEncryptionFactory(private val mOlmDevice: MXOlmDevice,
private val mCryptoStore: IMXCryptoStore,
private val mMessageEncrypter: MessageEncrypter,
private val mDeviceListManager: DeviceListManager,
private val mEnsureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) {

fun instantiate(roomId: String): MXOlmEncryption {
return MXOlmEncryption(
roomId,

mOlmDevice,
mCryptoStore,
mMessageEncrypter,
mDeviceListManager,
mEnsureOlmSessionsForUsersAction)
}
}

View File

@ -31,6 +31,7 @@ import im.vector.matrix.android.api.listeners.StepProgressListener
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
import im.vector.matrix.android.internal.crypto.*
import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter
import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrustSignature
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData
@ -67,6 +68,8 @@ internal class KeysBackup(
private val mCryptoStore: IMXCryptoStore,
private val mOlmDevice: MXOlmDevice,
private val mObjectSigner: ObjectSigner,
// Actions
private val mMegolmSessionDataImporter: MegolmSessionDataImporter,
// Tasks
private val mCreateKeysBackupVersionTask: CreateKeysBackupVersionTask,
private val mDeleteBackupTask: DeleteBackupTask,
@ -115,9 +118,6 @@ internal class KeysBackup(
override val currentBackupVersion: String?
get() = mKeysBackupVersion?.version

// Internal listener
private lateinit var mKeysBackupCryptoListener: KeysBackupCryptoListener

override fun addListener(listener: KeysBackupService.KeysBackupStateListener) {
mKeysBackupStateManager.addListener(listener)
}
@ -749,7 +749,20 @@ internal class KeysBackup(
null
}

mKeysBackupCryptoListener.importMegolmSessionsData(sessionsData, backUp, progressListener, callback)
mMegolmSessionDataImporter.handle(sessionsData, !backUp, progressListener, object : MatrixCallback<ImportRoomKeysResult> {
override fun onSuccess(data: ImportRoomKeysResult) {
// Do not back up the key if it comes from a backup recovery
if (backUp) {
maybeBackupKeys()
}

callback.onSuccess(data)
}

override fun onFailure(failure: Throwable) {
callback.onFailure(failure)
}
})
}

override fun onFailure(failure: Throwable) {
@ -887,7 +900,7 @@ internal class KeysBackup(
/**
* Do a backup if there are new keys, with a delay
*/
override fun maybeBackupKeys() {
fun maybeBackupKeys() {
when {
isStucked -> {
// If not already done, or in error case, check for a valid backup version on the homeserver.
@ -1388,7 +1401,7 @@ internal class KeysBackup(
@WorkerThread
fun encryptGroupSession(session: MXOlmInboundGroupSession2): KeyBackupData {
// Gather information for each key
val device = mKeysBackupCryptoListener.deviceWithIdentityKey(session.mSenderKey!!, MXCRYPTO_ALGORITHM_MEGOLM)
val device = mCryptoStore.deviceWithIdentityKey(session.mSenderKey!!)

// Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at
// https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format
@ -1479,21 +1492,6 @@ internal class KeysBackup(
private const val KEY_BACKUP_SEND_KEYS_MAX_COUNT = 100
}


fun setCryptoInternalListener(listener: KeysBackupCryptoListener) {
mKeysBackupCryptoListener = listener
}

interface KeysBackupCryptoListener {
fun importMegolmSessionsData(megolmSessionsData: List<MegolmSessionData>,
backUpKeys: Boolean,
progressListener: ProgressListener?,
callback: MatrixCallback<ImportRoomKeysResult>)

fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo?
}


/* ==========================================================================================
* DEBUG INFO
* ========================================================================================== */

View File

@ -22,7 +22,7 @@ import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
import timber.log.Timber
import java.util.*

internal class KeysBackupStateManager(val uiHandler: Handler) {
internal class KeysBackupStateManager(private val uiHandler: Handler) {

private val mListeners = ArrayList<KeysBackupService.KeysBackupStateListener>()


View File

@ -0,0 +1,40 @@
/*
* Copyright 2019 New Vector 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.repository

internal class WarnOnUnknownDeviceRepository {

// Warn the user if some new devices are detected while encrypting a message.
private var mWarnOnUnknownDevices = true

/**
* Tells if the encryption must fail if some unknown devices are detected.
*
* @return true to warn when some unknown devices are detected.
*/
fun warnOnUnknownDevices() = mWarnOnUnknownDevices

/**
* Update the warn status when some unknown devices are detected.
*
* @param warn true to warn when some unknown devices are detected.
*/
fun setWarnOnUnknownDevices(warn: Boolean) {
mWarnOnUnknownDevices = warn
}

}

View File

@ -201,7 +201,7 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals
.let { u ->
// Add the devices
// Ensure all other devices are deleted
u.devices.deleteAllFromRealm() // Device is null!!
u.devices.deleteAllFromRealm()

u.devices.addAll(
devices.map {

View File

@ -29,6 +29,7 @@ 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.internal.crypto.CryptoAsyncHelper
import im.vector.matrix.android.internal.crypto.DeviceListManager
import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
@ -48,6 +49,7 @@ import kotlin.collections.HashMap
*/
internal class DefaultSasVerificationService(private val mCredentials: Credentials,
private val mCryptoStore: IMXCryptoStore,
private val mMyDeviceInfoHolder: MyDeviceInfoHolder,
private val deviceListManager: DeviceListManager,
private val setDeviceVerificationAction: SetDeviceVerificationAction,
private val mSendToDeviceTask: SendToDeviceTask,
@ -86,10 +88,6 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
}
}

// Internal listener
private lateinit var mCryptoListener: SasCryptoListener


private var listeners = ArrayList<SasVerificationService.SasVerificationListener>()

override fun addListener(listener: SasVerificationService.SasVerificationListener) {
@ -188,11 +186,12 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
Timber.d("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}")
val tx = IncomingSASVerificationTransaction(
this,
setDeviceVerificationAction,
mCredentials,
mCryptoStore,
mSendToDeviceTask,
mTaskExecutor,
mCryptoListener.getMyDevice().fingerprint()!!,
mMyDeviceInfoHolder.myDevice.fingerprint()!!,
startReq.transactionID!!,
otherUserId)
addTransaction(tx)
@ -362,11 +361,12 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
if (KeyVerificationStart.VERIF_METHOD_SAS == method) {
val tx = OutgoingSASVerificationRequest(
this,
setDeviceVerificationAction,
mCredentials,
mCryptoStore,
mSendToDeviceTask,
mTaskExecutor,
mCryptoListener.getMyDevice().fingerprint()!!,
mMyDeviceInfoHolder.myDevice.fingerprint()!!,
txID,
userId,
deviceID)
@ -424,12 +424,4 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
})
.executeBy(mTaskExecutor)
}

fun setCryptoInternalListener(listener: SasCryptoListener) {
mCryptoListener = listener
}

interface SasCryptoListener {
fun getMyDevice(): MXDeviceInfo
}
}

View File

@ -23,6 +23,7 @@ import im.vector.matrix.android.api.session.crypto.sas.SasMode
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationKey
@ -35,6 +36,7 @@ import timber.log.Timber

internal class IncomingSASVerificationTransaction(
private val mSasVerificationService: DefaultSasVerificationService,
private val mSetDeviceVerificationAction: SetDeviceVerificationAction,
private val mCredentials: Credentials,
private val mCryptoStore: IMXCryptoStore,
private val mSendToDeviceTask: SendToDeviceTask,
@ -44,6 +46,7 @@ internal class IncomingSASVerificationTransaction(
otherUserID: String)
: SASVerificationTransaction(
mSasVerificationService,
mSetDeviceVerificationAction,
mCredentials,
mCryptoStore,
mSendToDeviceTask,

View File

@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.crypto.sas.CancelCode
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept
@ -33,6 +34,7 @@ import timber.log.Timber

internal class OutgoingSASVerificationRequest(
private val mSasVerificationService: DefaultSasVerificationService,
private val mSetDeviceVerificationAction: SetDeviceVerificationAction,
private val mCredentials: Credentials,
private val mCryptoStore: IMXCryptoStore,
private val mSendToDeviceTask: SendToDeviceTask,
@ -43,6 +45,7 @@ internal class OutgoingSASVerificationRequest(
otherDeviceId: String)
: SASVerificationTransaction(
mSasVerificationService,
mSetDeviceVerificationAction,
mCredentials,
mCryptoStore,
mSendToDeviceTask,

View File

@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.crypto.sas.SasMode
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXKey
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
@ -42,6 +43,7 @@ import kotlin.properties.Delegates
*/
internal abstract class SASVerificationTransaction(
private val mSasVerificationService: DefaultSasVerificationService,
private val mSetDeviceVerificationAction: SetDeviceVerificationAction,
private val mCredentials: Credentials,
private val mCryptoStore: IMXCryptoStore,
private val mSendToDeviceTask: SendToDeviceTask,
@ -245,7 +247,7 @@ internal abstract class SASVerificationTransaction(
}

private fun setDeviceVerified(deviceId: String, userId: String) {
mSasVerificationService.setDeviceVerification(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED,
mSetDeviceVerificationAction.handle(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED,
deviceId,
userId)
}

View File

@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.core.epoxy.EmptyItem_
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import timber.log.Timber

class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
private val roomNameItemFactory: RoomNameItemFactory,
@ -59,6 +60,8 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
else -> null
}
} catch (e: Exception) {
Timber.e(e, "Error")

defaultItemFactory.create(event, e)
}
return (computedModel ?: EmptyItem_())