Merge pull request #1172 from vector-im/feature/ensure_olm_account_unicity

Feature/ensure olm account unicity
This commit is contained in:
Valere 2020-03-26 12:11:09 +01:00 committed by GitHub
commit 1d46b523b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 67 additions and 62 deletions

View file

@ -10,6 +10,7 @@ Improvements 🙌:
Bugfix 🐛: Bugfix 🐛:
- Missing avatar/displayname after verification request message (#841) - Missing avatar/displayname after verification request message (#841)
- Crypto | RiotX sometimes rotate the current device keys (#1170)
- RiotX can't restore cross signing keys saved by web in SSSS (#1174) - RiotX can't restore cross signing keys saved by web in SSSS (#1174)
Translations 🗣: Translations 🗣:

View file

@ -59,9 +59,6 @@ internal class MXOlmDevice @Inject constructor(
var deviceEd25519Key: String? = null var deviceEd25519Key: String? = null
private set private set
// The OLM lib account instance.
private var olmAccount: OlmAccount? = null
// The OLM lib utility instance. // The OLM lib utility instance.
private var olmUtility: OlmUtility? = null private var olmUtility: OlmUtility? = null
@ -86,19 +83,10 @@ internal class MXOlmDevice @Inject constructor(
init { init {
// Retrieve the account from the store // Retrieve the account from the store
olmAccount = store.getAccount() try {
store.getOrCreateOlmAccount()
if (null == olmAccount) { } catch (e: Exception) {
Timber.v("MXOlmDevice : create a new olm account") Timber.e(e, "MXOlmDevice : cannot initialize olmAccount")
// Else, create it
try {
olmAccount = OlmAccount()
store.storeAccount(olmAccount!!)
} catch (e: Exception) {
Timber.e(e, "MXOlmDevice : cannot initialize olmAccount")
}
} else {
Timber.v("MXOlmDevice : use an existing account")
} }
try { try {
@ -109,13 +97,13 @@ internal class MXOlmDevice @Inject constructor(
} }
try { try {
deviceCurve25519Key = olmAccount!!.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY] deviceCurve25519Key = store.getOlmAccount().identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY]
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## MXOlmDevice : cannot find ${OlmAccount.JSON_KEY_IDENTITY_KEY} with error") Timber.e(e, "## MXOlmDevice : cannot find ${OlmAccount.JSON_KEY_IDENTITY_KEY} with error")
} }
try { try {
deviceEd25519Key = olmAccount!!.identityKeys()[OlmAccount.JSON_KEY_FINGER_PRINT_KEY] deviceEd25519Key = store.getOlmAccount().identityKeys()[OlmAccount.JSON_KEY_FINGER_PRINT_KEY]
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## MXOlmDevice : cannot find ${OlmAccount.JSON_KEY_FINGER_PRINT_KEY} with error") Timber.e(e, "## MXOlmDevice : cannot find ${OlmAccount.JSON_KEY_FINGER_PRINT_KEY} with error")
} }
@ -126,7 +114,7 @@ internal class MXOlmDevice @Inject constructor(
*/ */
fun getOneTimeKeys(): Map<String, Map<String, String>>? { fun getOneTimeKeys(): Map<String, Map<String, String>>? {
try { try {
return olmAccount!!.oneTimeKeys() return store.getOlmAccount().oneTimeKeys()
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## getOneTimeKeys() : failed") Timber.e(e, "## getOneTimeKeys() : failed")
} }
@ -138,14 +126,13 @@ internal class MXOlmDevice @Inject constructor(
* @return The maximum number of one-time keys the olm account can store. * @return The maximum number of one-time keys the olm account can store.
*/ */
fun getMaxNumberOfOneTimeKeys(): Long { fun getMaxNumberOfOneTimeKeys(): Long {
return olmAccount?.maxOneTimeKeys() ?: -1 return store.getOlmAccount().maxOneTimeKeys()
} }
/** /**
* Release the instance * Release the instance
*/ */
fun release() { fun release() {
olmAccount?.releaseAccount()
olmUtility?.releaseUtility() olmUtility?.releaseUtility()
} }
@ -157,7 +144,7 @@ internal class MXOlmDevice @Inject constructor(
*/ */
fun signMessage(message: String): String? { fun signMessage(message: String): String? {
try { try {
return olmAccount!!.signMessage(message) return store.getOlmAccount().signMessage(message)
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## signMessage() : failed") Timber.e(e, "## signMessage() : failed")
} }
@ -170,8 +157,8 @@ internal class MXOlmDevice @Inject constructor(
*/ */
fun markKeysAsPublished() { fun markKeysAsPublished() {
try { try {
olmAccount!!.markOneTimeKeysAsPublished() store.getOlmAccount().markOneTimeKeysAsPublished()
store.storeAccount(olmAccount!!) store.saveOlmAccount()
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## markKeysAsPublished() : failed") Timber.e(e, "## markKeysAsPublished() : failed")
} }
@ -184,8 +171,8 @@ internal class MXOlmDevice @Inject constructor(
*/ */
fun generateOneTimeKeys(numKeys: Int) { fun generateOneTimeKeys(numKeys: Int) {
try { try {
olmAccount!!.generateOneTimeKeys(numKeys) store.getOlmAccount().generateOneTimeKeys(numKeys)
store.storeAccount(olmAccount!!) store.saveOlmAccount()
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## generateOneTimeKeys() : failed") Timber.e(e, "## generateOneTimeKeys() : failed")
} }
@ -205,7 +192,7 @@ internal class MXOlmDevice @Inject constructor(
try { try {
olmSession = OlmSession() olmSession = OlmSession()
olmSession.initOutboundSession(olmAccount!!, theirIdentityKey, theirOneTimeKey) olmSession.initOutboundSession(store.getOlmAccount(), theirIdentityKey, theirOneTimeKey)
val olmSessionWrapper = OlmSessionWrapper(olmSession, 0) val olmSessionWrapper = OlmSessionWrapper(olmSession, 0)
@ -245,7 +232,7 @@ internal class MXOlmDevice @Inject constructor(
try { try {
try { try {
olmSession = OlmSession() olmSession = OlmSession()
olmSession.initInboundSessionFrom(olmAccount!!, theirDeviceIdentityKey, ciphertext) olmSession.initInboundSessionFrom(store.getOlmAccount(), theirDeviceIdentityKey, ciphertext)
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## createInboundSession() : the session creation failed") Timber.e(e, "## createInboundSession() : the session creation failed")
return null return null
@ -254,8 +241,8 @@ internal class MXOlmDevice @Inject constructor(
Timber.v("## createInboundSession() : sessionId: ${olmSession.sessionIdentifier()}") Timber.v("## createInboundSession() : sessionId: ${olmSession.sessionIdentifier()}")
try { try {
olmAccount!!.removeOneTimeKeys(olmSession) store.getOlmAccount().removeOneTimeKeys(olmSession)
store.storeAccount(olmAccount!!) store.saveOlmAccount()
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## createInboundSession() : removeOneTimeKeys failed") Timber.e(e, "## createInboundSession() : removeOneTimeKeys failed")
} }

View file

@ -49,7 +49,9 @@ internal interface IMXCryptoStore {
/** /**
* @return the olm account * @return the olm account
*/ */
fun getAccount(): OlmAccount? fun getOlmAccount(): OlmAccount
fun getOrCreateOlmAccount(): OlmAccount
/** /**
* Retrieve the known inbound group sessions. * Retrieve the known inbound group sessions.
@ -159,7 +161,7 @@ internal interface IMXCryptoStore {
* *
* @param account the account to save * @param account the account to save
*/ */
fun storeAccount(account: OlmAccount) fun saveOlmAccount()
/** /**
* Store a device for a user. * Store a device for a user.

View file

@ -122,27 +122,7 @@ internal class RealmCryptoStore @Inject constructor(
.setRealmConfiguration(realmConfiguration) .setRealmConfiguration(realmConfiguration)
.build() .build()
/* ========================================================================================== init {
* Other data
* ========================================================================================== */
override fun hasData(): Boolean {
return doWithRealm(realmConfiguration) {
!it.isEmpty
// Check if there is a MetaData object
&& it.where<CryptoMetadataEntity>().count() > 0
}
}
override fun deleteStore() {
doRealmTransaction(realmConfiguration) {
it.deleteAll()
}
}
override fun open() {
realmLocker = Realm.getInstance(realmConfiguration)
// Ensure CryptoMetadataEntity is inserted in DB // Ensure CryptoMetadataEntity is inserted in DB
doRealmTransaction(realmConfiguration) { realm -> doRealmTransaction(realmConfiguration) { realm ->
var currentMetadata = realm.where<CryptoMetadataEntity>().findFirst() var currentMetadata = realm.where<CryptoMetadataEntity>().findFirst()
@ -173,6 +153,27 @@ internal class RealmCryptoStore @Inject constructor(
} }
} }
} }
/* ==========================================================================================
* Other data
* ========================================================================================== */
override fun hasData(): Boolean {
return doWithRealm(realmConfiguration) {
!it.isEmpty
// Check if there is a MetaData object
&& it.where<CryptoMetadataEntity>().count() > 0
}
}
override fun deleteStore() {
doRealmTransaction(realmConfiguration) {
it.deleteAll()
}
}
override fun open() {
realmLocker = Realm.getInstance(realmConfiguration)
}
override fun close() { override fun close() {
olmSessionsToRelease.forEach { olmSessionsToRelease.forEach {
@ -203,20 +204,31 @@ internal class RealmCryptoStore @Inject constructor(
}?.deviceId ?: "" }?.deviceId ?: ""
} }
override fun storeAccount(account: OlmAccount) { override fun saveOlmAccount() {
olmAccount = account
doRealmTransaction(realmConfiguration) { doRealmTransaction(realmConfiguration) {
it.where<CryptoMetadataEntity>().findFirst()?.putOlmAccount(account) it.where<CryptoMetadataEntity>().findFirst()?.putOlmAccount(olmAccount)
} }
} }
override fun getAccount(): OlmAccount? { override fun getOlmAccount(): OlmAccount {
if (olmAccount == null) { return olmAccount!!
olmAccount = doRealmQueryAndCopy(realmConfiguration) { it.where<CryptoMetadataEntity>().findFirst() }?.getOlmAccount() }
}
return olmAccount override fun getOrCreateOlmAccount(): OlmAccount {
doRealmTransaction(realmConfiguration) {
val metaData = it.where<CryptoMetadataEntity>().findFirst()
val existing = metaData!!.getOlmAccount()
if (existing == null) {
Timber.d("## Crypto Creating olm account")
val created = OlmAccount()
metaData.putOlmAccount(created)
olmAccount = created
} else {
Timber.d("## Crypto Access existing account")
olmAccount = existing
}
}
return olmAccount!!
} }
override fun storeUserDevice(userId: String?, deviceInfo: CryptoDeviceInfo?) { override fun storeUserDevice(userId: String?, deviceInfo: CryptoDeviceInfo?) {

View file

@ -25,6 +25,7 @@ import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.convertToUTF8 import im.vector.matrix.android.internal.util.convertToUTF8
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal interface UploadKeysTask : Task<UploadKeysTask.Params, KeysUploadResponse> { internal interface UploadKeysTask : Task<UploadKeysTask.Params, KeysUploadResponse> {
@ -50,6 +51,8 @@ internal class DefaultUploadKeysTask @Inject constructor(
oneTimeKeys = params.oneTimeKeys oneTimeKeys = params.oneTimeKeys
) )
Timber.i("## Uploading device keys -> $body")
return executeRequest(eventBus) { return executeRequest(eventBus) {
apiCall = if (encodedDeviceId.isBlank()) { apiCall = if (encodedDeviceId.isBlank()) {
cryptoApi.uploadKeys(body) cryptoApi.uploadKeys(body)