mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-29 14:38:45 +03:00
Merge pull request #3079 from Dominaezzz/suspend_functions_9
Convert SharedSecretStorageService and AccountDataService to suspend functions
This commit is contained in:
commit
79bee63515
10 changed files with 229 additions and 360 deletions
|
@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto.ssss
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import org.matrix.android.sdk.InstrumentedTest
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.securestorage.EncryptedSecretContent
|
import org.matrix.android.sdk.api.session.securestorage.EncryptedSecretContent
|
||||||
import org.matrix.android.sdk.api.session.securestorage.KeySigner
|
import org.matrix.android.sdk.api.session.securestorage.KeySigner
|
||||||
|
@ -31,7 +30,6 @@ import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import org.matrix.android.sdk.common.TestMatrixCallback
|
|
||||||
import org.matrix.android.sdk.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
import org.matrix.android.sdk.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
||||||
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
||||||
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
|
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
|
||||||
|
@ -40,7 +38,6 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.amshove.kluent.shouldBe
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
|
@ -70,8 +67,8 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
val TEST_KEY_ID = "my.test.Key"
|
val TEST_KEY_ID = "my.test.Key"
|
||||||
|
|
||||||
mTestHelper.doSync<SsssKeyCreationInfo> {
|
mTestHelper.runBlockingTest {
|
||||||
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner, it)
|
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert Account data is updated
|
// Assert Account data is updated
|
||||||
|
@ -99,7 +96,9 @@ class QuadSTests : InstrumentedTest {
|
||||||
assertNull("Key was not generated from passphrase", parsed.passphrase)
|
assertNull("Key was not generated from passphrase", parsed.passphrase)
|
||||||
|
|
||||||
// Set as default key
|
// Set as default key
|
||||||
quadS.setDefaultKey(TEST_KEY_ID, object : MatrixCallback<Unit> {})
|
GlobalScope.launch {
|
||||||
|
quadS.setDefaultKey(TEST_KEY_ID)
|
||||||
|
}
|
||||||
|
|
||||||
var defaultKeyAccountData: UserAccountDataEvent? = null
|
var defaultKeyAccountData: UserAccountDataEvent? = null
|
||||||
val defaultDataLock = CountDownLatch(1)
|
val defaultDataLock = CountDownLatch(1)
|
||||||
|
@ -133,12 +132,11 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
// Store a secret
|
// Store a secret
|
||||||
val clearSecret = "42".toByteArray().toBase64NoPadding()
|
val clearSecret = "42".toByteArray().toBase64NoPadding()
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.storeSecret(
|
aliceSession.sharedSecretStorageService.storeSecret(
|
||||||
"secret.of.life",
|
"secret.of.life",
|
||||||
clearSecret,
|
clearSecret,
|
||||||
listOf(SharedSecretStorageService.KeyRef(null, keySpec)), // default key
|
listOf(SharedSecretStorageService.KeyRef(null, keySpec)) // default key
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,12 +153,11 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
// Try to decrypt??
|
// Try to decrypt??
|
||||||
|
|
||||||
val decryptedSecret = mTestHelper.doSync<String> {
|
val decryptedSecret = mTestHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.getSecret(
|
aliceSession.sharedSecretStorageService.getSecret(
|
||||||
"secret.of.life",
|
"secret.of.life",
|
||||||
null, // default key
|
null, // default key
|
||||||
keySpec!!,
|
keySpec!!
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,13 +173,13 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
val TEST_KEY_ID = "my.test.Key"
|
val TEST_KEY_ID = "my.test.Key"
|
||||||
|
|
||||||
mTestHelper.doSync<SsssKeyCreationInfo> {
|
mTestHelper.runBlockingTest {
|
||||||
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner, it)
|
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that we don't need to wait for an account data sync to access directly the keyid from DB
|
// Test that we don't need to wait for an account data sync to access directly the keyid from DB
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.runBlockingTest {
|
||||||
quadS.setDefaultKey(TEST_KEY_ID, it)
|
quadS.setDefaultKey(TEST_KEY_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.signOutAndClose(aliceSession)
|
mTestHelper.signOutAndClose(aliceSession)
|
||||||
|
@ -198,15 +195,14 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.storeSecret(
|
aliceSession.sharedSecretStorageService.storeSecret(
|
||||||
"my.secret",
|
"my.secret",
|
||||||
mySecretText.toByteArray().toBase64NoPadding(),
|
mySecretText.toByteArray().toBase64NoPadding(),
|
||||||
listOf(
|
listOf(
|
||||||
SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)),
|
SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)),
|
||||||
SharedSecretStorageService.KeyRef(keyId2, RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey))
|
SharedSecretStorageService.KeyRef(keyId2, RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey))
|
||||||
),
|
)
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,19 +215,17 @@ class QuadSTests : InstrumentedTest {
|
||||||
assertNotNull(encryptedContent?.get(keyId2))
|
assertNotNull(encryptedContent?.get(keyId2))
|
||||||
|
|
||||||
// Assert that can decrypt with both keys
|
// Assert that can decrypt with both keys
|
||||||
mTestHelper.doSync<String> {
|
mTestHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId1,
|
keyId1,
|
||||||
RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!!,
|
RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!!
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
mTestHelper.doSync<String> {
|
mTestHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId2,
|
keyId2,
|
||||||
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!,
|
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,50 +241,34 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.storeSecret(
|
aliceSession.sharedSecretStorageService.storeSecret(
|
||||||
"my.secret",
|
"my.secret",
|
||||||
mySecretText.toByteArray().toBase64NoPadding(),
|
mySecretText.toByteArray().toBase64NoPadding(),
|
||||||
listOf(SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey))),
|
listOf(SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)))
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val decryptCountDownLatch = CountDownLatch(1)
|
mTestHelper.runBlockingTest {
|
||||||
var error = false
|
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId1,
|
keyId1,
|
||||||
RawBytesKeySpec.fromPassphrase(
|
RawBytesKeySpec.fromPassphrase(
|
||||||
"A bad passphrase",
|
"A bad passphrase",
|
||||||
key1Info.content?.passphrase?.salt ?: "",
|
key1Info.content?.passphrase?.salt ?: "",
|
||||||
key1Info.content?.passphrase?.iterations ?: 0,
|
key1Info.content?.passphrase?.iterations ?: 0,
|
||||||
null),
|
null)
|
||||||
object : MatrixCallback<String> {
|
|
||||||
override fun onSuccess(data: String) {
|
|
||||||
decryptCountDownLatch.countDown()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
error = true
|
|
||||||
decryptCountDownLatch.countDown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
mTestHelper.await(decryptCountDownLatch)
|
|
||||||
|
|
||||||
error shouldBe true
|
|
||||||
|
|
||||||
// Now try with correct key
|
// Now try with correct key
|
||||||
mTestHelper.doSync<String> {
|
mTestHelper.runBlockingTest {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId1,
|
keyId1,
|
||||||
RawBytesKeySpec.fromPassphrase(
|
RawBytesKeySpec.fromPassphrase(
|
||||||
passphrase,
|
passphrase,
|
||||||
key1Info.content?.passphrase?.salt ?: "",
|
key1Info.content?.passphrase?.salt ?: "",
|
||||||
key1Info.content?.passphrase?.iterations ?: 0,
|
key1Info.content?.passphrase?.iterations ?: 0,
|
||||||
null),
|
null)
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,15 +299,15 @@ class QuadSTests : InstrumentedTest {
|
||||||
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
||||||
val quadS = session.sharedSecretStorageService
|
val quadS = session.sharedSecretStorageService
|
||||||
|
|
||||||
val creationInfo = mTestHelper.doSync<SsssKeyCreationInfo> {
|
val creationInfo = mTestHelper.runBlockingTest {
|
||||||
quadS.generateKey(keyId, null, keyId, emptyKeySigner, it)
|
quadS.generateKey(keyId, null, keyId, emptyKeySigner)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
||||||
|
|
||||||
if (asDefault) {
|
if (asDefault) {
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.runBlockingTest {
|
||||||
quadS.setDefaultKey(keyId, it)
|
quadS.setDefaultKey(keyId)
|
||||||
}
|
}
|
||||||
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||||
}
|
}
|
||||||
|
@ -340,21 +318,20 @@ class QuadSTests : InstrumentedTest {
|
||||||
private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
||||||
val quadS = session.sharedSecretStorageService
|
val quadS = session.sharedSecretStorageService
|
||||||
|
|
||||||
val creationInfo = mTestHelper.doSync<SsssKeyCreationInfo> {
|
val creationInfo = mTestHelper.runBlockingTest {
|
||||||
quadS.generateKeyWithPassphrase(
|
quadS.generateKeyWithPassphrase(
|
||||||
keyId,
|
keyId,
|
||||||
keyId,
|
keyId,
|
||||||
passphrase,
|
passphrase,
|
||||||
emptyKeySigner,
|
emptyKeySigner,
|
||||||
null,
|
null)
|
||||||
it)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
||||||
if (asDefault) {
|
if (asDefault) {
|
||||||
val setDefaultLatch = CountDownLatch(1)
|
mTestHelper.runBlockingTest {
|
||||||
quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
|
quadS.setDefaultKey(keyId)
|
||||||
mTestHelper.await(setDefaultLatch)
|
}
|
||||||
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,7 @@
|
||||||
package org.matrix.android.sdk.api.session.accountdata
|
package org.matrix.android.sdk.api.session.accountdata
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
|
||||||
interface AccountDataService {
|
interface AccountDataService {
|
||||||
|
@ -48,5 +46,5 @@ interface AccountDataService {
|
||||||
/**
|
/**
|
||||||
* Update the account data with the provided type and the provided account data content
|
* Update the account data with the provided type and the provided account data content
|
||||||
*/
|
*/
|
||||||
fun updateAccountData(type: String, content: Content, callback: MatrixCallback<Unit>? = null): Cancelable
|
suspend fun updateAccountData(type: String, content: Content)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.securestorage
|
package org.matrix.android.sdk.api.session.securestorage
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||||
|
@ -43,13 +42,12 @@ interface SharedSecretStorageService {
|
||||||
* @param keyName a human readable name
|
* @param keyName a human readable name
|
||||||
* @param keySigner Used to add a signature to the key (client should check key signature before storing secret)
|
* @param keySigner Used to add a signature to the key (client should check key signature before storing secret)
|
||||||
*
|
*
|
||||||
* @param callback Get key creation info
|
* @return key creation info
|
||||||
*/
|
*/
|
||||||
fun generateKey(keyId: String,
|
suspend fun generateKey(keyId: String,
|
||||||
key: SsssKeySpec?,
|
key: SsssKeySpec?,
|
||||||
keyName: String,
|
keyName: String,
|
||||||
keySigner: KeySigner?,
|
keySigner: KeySigner?): SsssKeyCreationInfo
|
||||||
callback: MatrixCallback<SsssKeyCreationInfo>)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a SSSS key using the given passphrase.
|
* Generates a SSSS key using the given passphrase.
|
||||||
|
@ -61,14 +59,13 @@ interface SharedSecretStorageService {
|
||||||
* @param keySigner Used to add a signature to the key (client should check key signature before retrieving secret)
|
* @param keySigner Used to add a signature to the key (client should check key signature before retrieving secret)
|
||||||
* @param progressListener The derivation of the passphrase may take long depending on the device, use this to report progress
|
* @param progressListener The derivation of the passphrase may take long depending on the device, use this to report progress
|
||||||
*
|
*
|
||||||
* @param callback Get key creation info
|
* @return key creation info
|
||||||
*/
|
*/
|
||||||
fun generateKeyWithPassphrase(keyId: String,
|
suspend fun generateKeyWithPassphrase(keyId: String,
|
||||||
keyName: String,
|
keyName: String,
|
||||||
passphrase: String,
|
passphrase: String,
|
||||||
keySigner: KeySigner,
|
keySigner: KeySigner,
|
||||||
progressListener: ProgressListener?,
|
progressListener: ProgressListener?): SsssKeyCreationInfo
|
||||||
callback: MatrixCallback<SsssKeyCreationInfo>)
|
|
||||||
|
|
||||||
fun getKey(keyId: String): KeyInfoResult
|
fun getKey(keyId: String): KeyInfoResult
|
||||||
|
|
||||||
|
@ -80,7 +77,7 @@ interface SharedSecretStorageService {
|
||||||
*/
|
*/
|
||||||
fun getDefaultKey(): KeyInfoResult
|
fun getDefaultKey(): KeyInfoResult
|
||||||
|
|
||||||
fun setDefaultKey(keyId: String, callback: MatrixCallback<Unit>)
|
suspend fun setDefaultKey(keyId: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether we have a key with a given ID.
|
* Check whether we have a key with a given ID.
|
||||||
|
@ -98,7 +95,7 @@ interface SharedSecretStorageService {
|
||||||
* @param secret The secret contents.
|
* @param secret The secret contents.
|
||||||
* @param keys The list of (ID,privateKey) of the keys to use to encrypt the secret.
|
* @param keys The list of (ID,privateKey) of the keys to use to encrypt the secret.
|
||||||
*/
|
*/
|
||||||
fun storeSecret(name: String, secretBase64: String, keys: List<KeyRef>, callback: MatrixCallback<Unit>)
|
suspend fun storeSecret(name: String, secretBase64: String, keys: List<KeyRef>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this call to determine which SSSSKeySpec to use for requesting secret
|
* Use this call to determine which SSSSKeySpec to use for requesting secret
|
||||||
|
@ -113,7 +110,7 @@ interface SharedSecretStorageService {
|
||||||
* @param secretKey the secret key to use (@see #RawBytesKeySpec)
|
* @param secretKey the secret key to use (@see #RawBytesKeySpec)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
fun getSecret(name: String, keyId: String?, secretKey: SsssKeySpec, callback: MatrixCallback<String>)
|
suspend fun getSecret(name: String, keyId: String?, secretKey: SsssKeySpec): String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if SSSS is configured
|
* Return true if SSSS is configured
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.secrets
|
package org.matrix.android.sdk.internal.crypto.secrets
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||||
import org.matrix.android.sdk.api.session.accountdata.AccountDataService
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataService
|
||||||
|
@ -43,10 +42,9 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.util.computeRecoveryKey
|
||||||
import org.matrix.android.sdk.internal.crypto.tools.HkdfSha256
|
import org.matrix.android.sdk.internal.crypto.tools.HkdfSha256
|
||||||
import org.matrix.android.sdk.internal.crypto.tools.withOlmDecryption
|
import org.matrix.android.sdk.internal.crypto.tools.withOlmDecryption
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.extensions.foldToCallback
|
|
||||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.olm.OlmPkMessage
|
import org.matrix.olm.OlmPkMessage
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
|
@ -64,21 +62,15 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
private val cryptoCoroutineScope: CoroutineScope
|
private val cryptoCoroutineScope: CoroutineScope
|
||||||
) : SharedSecretStorageService {
|
) : SharedSecretStorageService {
|
||||||
|
|
||||||
override fun generateKey(keyId: String,
|
override suspend fun generateKey(keyId: String,
|
||||||
key: SsssKeySpec?,
|
key: SsssKeySpec?,
|
||||||
keyName: String,
|
keyName: String,
|
||||||
keySigner: KeySigner?,
|
keySigner: KeySigner?): SsssKeyCreationInfo {
|
||||||
callback: MatrixCallback<SsssKeyCreationInfo>) {
|
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.main) {
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
val bytes = (key as? RawBytesKeySpec)?.privateKey
|
||||||
val bytes = try {
|
|
||||||
(key as? RawBytesKeySpec)?.privateKey
|
|
||||||
?: ByteArray(32).also {
|
?: ByteArray(32).also {
|
||||||
SecureRandom().nextBytes(it)
|
SecureRandom().nextBytes(it)
|
||||||
}
|
}
|
||||||
} catch (failure: Throwable) {
|
|
||||||
callback.onFailure(failure)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val storageKeyContent = SecretStorageKeyContent(
|
val storageKeyContent = SecretStorageKeyContent(
|
||||||
name = keyName,
|
name = keyName,
|
||||||
|
@ -92,34 +84,22 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
)
|
)
|
||||||
} ?: storageKeyContent
|
} ?: storageKeyContent
|
||||||
|
|
||||||
accountDataService.updateAccountData(
|
accountDataService.updateAccountData("$KEY_ID_BASE.$keyId", signedContent.toContent())
|
||||||
"$KEY_ID_BASE.$keyId",
|
SsssKeyCreationInfo(
|
||||||
signedContent.toContent(),
|
|
||||||
object : MatrixCallback<Unit> {
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
callback.onFailure(failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSuccess(data: Unit) {
|
|
||||||
callback.onSuccess(SsssKeyCreationInfo(
|
|
||||||
keyId = keyId,
|
keyId = keyId,
|
||||||
content = storageKeyContent,
|
content = storageKeyContent,
|
||||||
recoveryKey = computeRecoveryKey(bytes),
|
recoveryKey = computeRecoveryKey(bytes),
|
||||||
keySpec = RawBytesKeySpec(bytes)
|
keySpec = RawBytesKeySpec(bytes)
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generateKeyWithPassphrase(keyId: String,
|
override suspend fun generateKeyWithPassphrase(keyId: String,
|
||||||
keyName: String,
|
keyName: String,
|
||||||
passphrase: String,
|
passphrase: String,
|
||||||
keySigner: KeySigner,
|
keySigner: KeySigner,
|
||||||
progressListener: ProgressListener?,
|
progressListener: ProgressListener?): SsssKeyCreationInfo {
|
||||||
callback: MatrixCallback<SsssKeyCreationInfo>) {
|
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.main) {
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
|
||||||
val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener)
|
val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener)
|
||||||
|
|
||||||
val storageKeyContent = SecretStorageKeyContent(
|
val storageKeyContent = SecretStorageKeyContent(
|
||||||
|
@ -135,21 +115,13 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
|
|
||||||
accountDataService.updateAccountData(
|
accountDataService.updateAccountData(
|
||||||
"$KEY_ID_BASE.$keyId",
|
"$KEY_ID_BASE.$keyId",
|
||||||
signedContent.toContent(),
|
signedContent.toContent()
|
||||||
object : MatrixCallback<Unit> {
|
)
|
||||||
override fun onFailure(failure: Throwable) {
|
SsssKeyCreationInfo(
|
||||||
callback.onFailure(failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSuccess(data: Unit) {
|
|
||||||
callback.onSuccess(SsssKeyCreationInfo(
|
|
||||||
keyId = keyId,
|
keyId = keyId,
|
||||||
content = storageKeyContent,
|
content = storageKeyContent,
|
||||||
recoveryKey = computeRecoveryKey(privatePart.privateKey),
|
recoveryKey = computeRecoveryKey(privatePart.privateKey),
|
||||||
keySpec = RawBytesKeySpec(privatePart.privateKey)
|
keySpec = RawBytesKeySpec(privatePart.privateKey)
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,15 +140,12 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
} ?: KeyInfoResult.Error(SharedSecretStorageError.UnknownAlgorithm(keyId))
|
} ?: KeyInfoResult.Error(SharedSecretStorageError.UnknownAlgorithm(keyId))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setDefaultKey(keyId: String, callback: MatrixCallback<Unit>) {
|
override suspend fun setDefaultKey(keyId: String) {
|
||||||
val existingKey = getKey(keyId)
|
val existingKey = getKey(keyId)
|
||||||
if (existingKey is KeyInfoResult.Success) {
|
if (existingKey is KeyInfoResult.Success) {
|
||||||
accountDataService.updateAccountData(DEFAULT_KEY_ID,
|
accountDataService.updateAccountData(DEFAULT_KEY_ID, mapOf("key" to keyId))
|
||||||
mapOf("key" to keyId),
|
|
||||||
callback
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
callback.onFailure(SharedSecretStorageError.UnknownKey(keyId))
|
throw SharedSecretStorageError.UnknownKey(keyId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,10 +157,9 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
return getKey(keyId)
|
return getKey(keyId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun storeSecret(name: String, secretBase64: String, keys: List<SharedSecretStorageService.KeyRef>, callback: MatrixCallback<Unit>) {
|
override suspend fun storeSecret(name: String, secretBase64: String, keys: List<SharedSecretStorageService.KeyRef>) {
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.main) {
|
||||||
val encryptedContents = HashMap<String, EncryptedSecretContent>()
|
val encryptedContents = HashMap<String, EncryptedSecretContent>()
|
||||||
try {
|
|
||||||
keys.forEach {
|
keys.forEach {
|
||||||
val keyId = it.keyId
|
val keyId = it.keyId
|
||||||
// encrypt the content
|
// encrypt the content
|
||||||
|
@ -203,27 +171,17 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Unknown algorithm
|
// Unknown algorithm
|
||||||
callback.onFailure(SharedSecretStorageError.UnknownAlgorithm(key.keyInfo.content.algorithm ?: ""))
|
throw SharedSecretStorageError.UnknownAlgorithm(key.keyInfo.content.algorithm ?: "")
|
||||||
return@launch
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is KeyInfoResult.Error -> {
|
is KeyInfoResult.Error -> throw key.error
|
||||||
callback.onFailure(key.error)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accountDataService.updateAccountData(
|
accountDataService.updateAccountData(
|
||||||
type = name,
|
type = name,
|
||||||
content = mapOf(
|
content = mapOf("encrypted" to encryptedContents)
|
||||||
"encrypted" to encryptedContents
|
|
||||||
),
|
|
||||||
callback = callback
|
|
||||||
)
|
)
|
||||||
} catch (failure: Throwable) {
|
|
||||||
callback.onFailure(failure)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,33 +302,21 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSecret(name: String, keyId: String?, secretKey: SsssKeySpec, callback: MatrixCallback<String>) {
|
override suspend fun getSecret(name: String, keyId: String?, secretKey: SsssKeySpec): String {
|
||||||
val accountData = accountDataService.getAccountDataEvent(name) ?: return Unit.also {
|
val accountData = accountDataService.getAccountDataEvent(name) ?: throw SharedSecretStorageError.UnknownSecret(name)
|
||||||
callback.onFailure(SharedSecretStorageError.UnknownSecret(name))
|
val encryptedContent = accountData.content[ENCRYPTED] as? Map<*, *> ?: throw SharedSecretStorageError.SecretNotEncrypted(name)
|
||||||
}
|
val key = keyId?.let { getKey(it) } as? KeyInfoResult.Success ?: getDefaultKey() as? KeyInfoResult.Success
|
||||||
val encryptedContent = accountData.content[ENCRYPTED] as? Map<*, *> ?: return Unit.also {
|
?: throw SharedSecretStorageError.UnknownKey(name)
|
||||||
callback.onFailure(SharedSecretStorageError.SecretNotEncrypted(name))
|
|
||||||
}
|
|
||||||
val key = keyId?.let { getKey(it) } as? KeyInfoResult.Success ?: getDefaultKey() as? KeyInfoResult.Success ?: return Unit.also {
|
|
||||||
callback.onFailure(SharedSecretStorageError.UnknownKey(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
val encryptedForKey = encryptedContent[key.keyInfo.id] ?: return Unit.also {
|
val encryptedForKey = encryptedContent[key.keyInfo.id] ?: throw SharedSecretStorageError.SecretNotEncryptedWithKey(name, key.keyInfo.id)
|
||||||
callback.onFailure(SharedSecretStorageError.SecretNotEncryptedWithKey(name, key.keyInfo.id))
|
|
||||||
}
|
|
||||||
|
|
||||||
val secretContent = EncryptedSecretContent.fromJson(encryptedForKey)
|
val secretContent = EncryptedSecretContent.fromJson(encryptedForKey)
|
||||||
?: return Unit.also {
|
?: throw SharedSecretStorageError.ParsingError
|
||||||
callback.onFailure(SharedSecretStorageError.ParsingError)
|
|
||||||
}
|
|
||||||
|
|
||||||
val algorithm = key.keyInfo.content
|
val algorithm = key.keyInfo.content
|
||||||
if (SSSS_ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
|
if (SSSS_ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
|
||||||
val keySpec = secretKey as? RawBytesKeySpec ?: return Unit.also {
|
val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat
|
||||||
callback.onFailure(SharedSecretStorageError.BadKeyFormat)
|
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.main) {
|
||||||
}
|
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
|
||||||
runCatching {
|
|
||||||
// decrypt from recovery key
|
// decrypt from recovery key
|
||||||
withOlmDecryption { olmPkDecryption ->
|
withOlmDecryption { olmPkDecryption ->
|
||||||
olmPkDecryption.setPrivateKey(keySpec.privateKey)
|
olmPkDecryption.setPrivateKey(keySpec.privateKey)
|
||||||
|
@ -382,19 +328,14 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.foldToCallback(callback)
|
|
||||||
}
|
}
|
||||||
} else if (SSSS_ALGORITHM_AES_HMAC_SHA2 == algorithm.algorithm) {
|
} else if (SSSS_ALGORITHM_AES_HMAC_SHA2 == algorithm.algorithm) {
|
||||||
val keySpec = secretKey as? RawBytesKeySpec ?: return Unit.also {
|
val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat
|
||||||
callback.onFailure(SharedSecretStorageError.BadKeyFormat)
|
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.main) {
|
||||||
}
|
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
|
||||||
runCatching {
|
|
||||||
decryptAesHmacSha2(keySpec, name, secretContent)
|
decryptAesHmacSha2(keySpec, name, secretContent)
|
||||||
}.foldToCallback(callback)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback.onFailure(SharedSecretStorageError.UnsupportedAlgorithm(algorithm.algorithm ?: ""))
|
throw SharedSecretStorageError.UnsupportedAlgorithm(algorithm.algorithm ?: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,16 +18,15 @@ package org.matrix.android.sdk.internal.session.user.accountdata
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.accountdata.AccountDataService
|
import org.matrix.android.sdk.api.session.accountdata.AccountDataService
|
||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.session.sync.UserAccountDataSyncHandler
|
import org.matrix.android.sdk.internal.session.sync.UserAccountDataSyncHandler
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
|
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultAccountDataService @Inject constructor(
|
internal class DefaultAccountDataService @Inject constructor(
|
||||||
|
@ -54,26 +53,18 @@ internal class DefaultAccountDataService @Inject constructor(
|
||||||
return accountDataDataSource.getLiveAccountDataEvents(types)
|
return accountDataDataSource.getLiveAccountDataEvents(types)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAccountData(type: String, content: Content, callback: MatrixCallback<Unit>?): Cancelable {
|
override suspend fun updateAccountData(type: String, content: Content) {
|
||||||
return updateUserAccountDataTask.configureWith(UpdateUserAccountDataTask.AnyParams(
|
val params = UpdateUserAccountDataTask.AnyParams(type = type, any = content)
|
||||||
type = type,
|
awaitCallback<Unit> { callback ->
|
||||||
any = content
|
updateUserAccountDataTask.configureWith(params) {
|
||||||
)) {
|
this.retryCount = 5 // TODO: Need to refactor retrying out into a helper method.
|
||||||
this.retryCount = 5
|
this.callback = callback
|
||||||
this.callback = object : MatrixCallback<Unit> {
|
}
|
||||||
override fun onSuccess(data: Unit) {
|
.executeBy(taskExecutor)
|
||||||
|
}
|
||||||
// TODO Move that to the task (but it created a circular dependencies...)
|
// TODO Move that to the task (but it created a circular dependencies...)
|
||||||
monarchy.runTransactionSync { realm ->
|
monarchy.runTransactionSync { realm ->
|
||||||
userAccountDataSyncHandler.handleGenericAccountData(realm, type, content)
|
userAccountDataSyncHandler.handleGenericAccountData(realm, type, content)
|
||||||
}
|
}
|
||||||
callback?.onSuccess(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
callback?.onFailure(failure)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,6 @@ import org.matrix.android.sdk.api.session.securestorage.IntegrityResult
|
||||||
import org.matrix.android.sdk.api.session.securestorage.KeyInfoResult
|
import org.matrix.android.sdk.api.session.securestorage.KeyInfoResult
|
||||||
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
|
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
|
||||||
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
||||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
|
||||||
import org.matrix.android.sdk.rx.rx
|
import org.matrix.android.sdk.rx.rx
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
@ -220,13 +219,10 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
args.requestedSecrets.forEach {
|
args.requestedSecrets.forEach {
|
||||||
if (session.getAccountDataEvent(it) != null) {
|
if (session.getAccountDataEvent(it) != null) {
|
||||||
val res = awaitCallback<String> { callback ->
|
val res = session.sharedSecretStorageService.getSecret(
|
||||||
session.sharedSecretStorageService.getSecret(
|
|
||||||
name = it,
|
name = it,
|
||||||
keyId = keyInfo.id,
|
keyId = keyInfo.id,
|
||||||
secretKey = keySpec,
|
secretKey = keySpec)
|
||||||
callback = callback)
|
|
||||||
}
|
|
||||||
decryptedSecretMap[it] = res
|
decryptedSecretMap[it] = res
|
||||||
} else {
|
} else {
|
||||||
Timber.w("## Cannot find secret $it in SSSS, skip")
|
Timber.w("## Cannot find secret $it in SSSS, skip")
|
||||||
|
@ -292,13 +288,10 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
args.requestedSecrets.forEach {
|
args.requestedSecrets.forEach {
|
||||||
if (session.getAccountDataEvent(it) != null) {
|
if (session.getAccountDataEvent(it) != null) {
|
||||||
val res = awaitCallback<String> { callback ->
|
val res = session.sharedSecretStorageService.getSecret(
|
||||||
session.sharedSecretStorageService.getSecret(
|
|
||||||
name = it,
|
name = it,
|
||||||
keyId = keyInfo.id,
|
keyId = keyInfo.id,
|
||||||
secretKey = keySpec,
|
secretKey = keySpec)
|
||||||
callback = callback)
|
|
||||||
}
|
|
||||||
decryptedSecretMap[it] = res
|
decryptedSecretMap[it] = res
|
||||||
} else {
|
} else {
|
||||||
Timber.w("## Cannot find secret $it in SSSS, skip")
|
Timber.w("## Cannot find secret $it in SSSS, skip")
|
||||||
|
|
|
@ -97,7 +97,6 @@ class BackupToQuadSMigrationTask @Inject constructor(
|
||||||
when {
|
when {
|
||||||
params.passphrase?.isNotEmpty() == true -> {
|
params.passphrase?.isNotEmpty() == true -> {
|
||||||
reportProgress(params, R.string.bootstrap_progress_generating_ssss)
|
reportProgress(params, R.string.bootstrap_progress_generating_ssss)
|
||||||
awaitCallback {
|
|
||||||
quadS.generateKeyWithPassphrase(
|
quadS.generateKeyWithPassphrase(
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
"ssss_key",
|
"ssss_key",
|
||||||
|
@ -112,23 +111,18 @@ class BackupToQuadSMigrationTask @Inject constructor(
|
||||||
"$progress/$total")
|
"$progress/$total")
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
},
|
|
||||||
it
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
params.recoveryKey != null -> {
|
params.recoveryKey != null -> {
|
||||||
reportProgress(params, R.string.bootstrap_progress_generating_ssss_recovery)
|
reportProgress(params, R.string.bootstrap_progress_generating_ssss_recovery)
|
||||||
awaitCallback {
|
|
||||||
quadS.generateKey(
|
quadS.generateKey(
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
extractCurveKeyFromRecoveryKey(params.recoveryKey)?.let { RawBytesKeySpec(it) },
|
extractCurveKeyFromRecoveryKey(params.recoveryKey)?.let { RawBytesKeySpec(it) },
|
||||||
"ssss_key",
|
"ssss_key",
|
||||||
EmptyKeySigner(),
|
EmptyKeySigner()
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else -> {
|
else -> {
|
||||||
return Result.IllegalParams
|
return Result.IllegalParams
|
||||||
}
|
}
|
||||||
|
@ -137,14 +131,11 @@ class BackupToQuadSMigrationTask @Inject constructor(
|
||||||
// Ok, so now we have migrated the old keybackup secret as the quadS key
|
// Ok, so now we have migrated the old keybackup secret as the quadS key
|
||||||
// Now we need to store the keybackup key in SSSS in a compatible way
|
// Now we need to store the keybackup key in SSSS in a compatible way
|
||||||
reportProgress(params, R.string.bootstrap_progress_storing_in_sss)
|
reportProgress(params, R.string.bootstrap_progress_storing_in_sss)
|
||||||
awaitCallback<Unit> {
|
|
||||||
quadS.storeSecret(
|
quadS.storeSecret(
|
||||||
KEYBACKUP_SECRET_SSSS_NAME,
|
KEYBACKUP_SECRET_SSSS_NAME,
|
||||||
curveKey.toBase64NoPadding(),
|
curveKey.toBase64NoPadding(),
|
||||||
listOf(SharedSecretStorageService.KeyRef(info.keyId, info.keySpec)),
|
listOf(SharedSecretStorageService.KeyRef(info.keyId, info.keySpec))
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
// save for gossiping
|
// save for gossiping
|
||||||
keysBackupService.saveBackupRecoveryKey(recoveryKey, version.version)
|
keysBackupService.saveBackupRecoveryKey(recoveryKey, version.version)
|
||||||
|
|
|
@ -126,26 +126,22 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||||
|
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S key with pass: ${params.passphrase != null}")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S key with pass: ${params.passphrase != null}")
|
||||||
try {
|
try {
|
||||||
keyInfo = awaitCallback {
|
keyInfo = params.passphrase?.let { passphrase ->
|
||||||
params.passphrase?.let { passphrase ->
|
|
||||||
ssssService.generateKeyWithPassphrase(
|
ssssService.generateKeyWithPassphrase(
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
"ssss_key",
|
"ssss_key",
|
||||||
passphrase,
|
passphrase,
|
||||||
EmptyKeySigner(),
|
EmptyKeySigner(),
|
||||||
null,
|
null
|
||||||
it
|
|
||||||
)
|
)
|
||||||
} ?: run {
|
} ?: run {
|
||||||
ssssService.generateKey(
|
ssssService.generateKey(
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
params.keySpec,
|
params.keySpec,
|
||||||
"ssss_key",
|
"ssss_key",
|
||||||
EmptyKeySigner(),
|
EmptyKeySigner()
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} catch (failure: Failure) {
|
} catch (failure: Failure) {
|
||||||
Timber.e("## BootstrapCrossSigningTask: Creating 4S - Failed to generate key <${failure.localizedMessage}>")
|
Timber.e("## BootstrapCrossSigningTask: Creating 4S - Failed to generate key <${failure.localizedMessage}>")
|
||||||
return BootstrapResult.FailedToCreateSSSSKey(failure)
|
return BootstrapResult.FailedToCreateSSSSKey(failure)
|
||||||
|
@ -159,9 +155,7 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||||
|
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Set default key")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Set default key")
|
||||||
try {
|
try {
|
||||||
awaitCallback<Unit> {
|
ssssService.setDefaultKey(keyInfo.keyId)
|
||||||
ssssService.setDefaultKey(keyInfo.keyId, it)
|
|
||||||
}
|
|
||||||
} catch (failure: Failure) {
|
} catch (failure: Failure) {
|
||||||
// Maybe we could just ignore this error?
|
// Maybe we could just ignore this error?
|
||||||
Timber.e("## BootstrapCrossSigningTask: Creating 4S - Set default key error <${failure.localizedMessage}>")
|
Timber.e("## BootstrapCrossSigningTask: Creating 4S - Set default key error <${failure.localizedMessage}>")
|
||||||
|
@ -183,13 +177,11 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Storing MSK...")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Storing MSK...")
|
||||||
awaitCallback<Unit> {
|
|
||||||
ssssService.storeSecret(
|
ssssService.storeSecret(
|
||||||
MASTER_KEY_SSSS_NAME,
|
MASTER_KEY_SSSS_NAME,
|
||||||
mskPrivateKey,
|
mskPrivateKey,
|
||||||
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec)), it
|
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||||
)
|
)
|
||||||
}
|
|
||||||
params.progressListener?.onProgress(
|
params.progressListener?.onProgress(
|
||||||
WaitingViewData(
|
WaitingViewData(
|
||||||
stringProvider.getString(R.string.bootstrap_crosssigning_progress_save_usk),
|
stringProvider.getString(R.string.bootstrap_crosssigning_progress_save_usk),
|
||||||
|
@ -197,27 +189,22 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Storing USK...")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Storing USK...")
|
||||||
awaitCallback<Unit> {
|
|
||||||
ssssService.storeSecret(
|
ssssService.storeSecret(
|
||||||
USER_SIGNING_KEY_SSSS_NAME,
|
USER_SIGNING_KEY_SSSS_NAME,
|
||||||
uskPrivateKey,
|
uskPrivateKey,
|
||||||
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec)),
|
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||||
it
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
params.progressListener?.onProgress(
|
params.progressListener?.onProgress(
|
||||||
WaitingViewData(
|
WaitingViewData(
|
||||||
stringProvider.getString(R.string.bootstrap_crosssigning_progress_save_ssk), isIndeterminate = true
|
stringProvider.getString(R.string.bootstrap_crosssigning_progress_save_ssk), isIndeterminate = true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Storing SSK...")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Storing SSK...")
|
||||||
awaitCallback<Unit> {
|
|
||||||
ssssService.storeSecret(
|
ssssService.storeSecret(
|
||||||
SELF_SIGNING_KEY_SSSS_NAME,
|
SELF_SIGNING_KEY_SSSS_NAME,
|
||||||
sskPrivateKey,
|
sskPrivateKey,
|
||||||
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec)), it
|
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||||
)
|
)
|
||||||
}
|
|
||||||
} catch (failure: Failure) {
|
} catch (failure: Failure) {
|
||||||
Timber.e("## BootstrapCrossSigningTask: Creating 4S - Failed to store keys <${failure.localizedMessage}>")
|
Timber.e("## BootstrapCrossSigningTask: Creating 4S - Failed to store keys <${failure.localizedMessage}>")
|
||||||
// Maybe we could just ignore this error?
|
// Maybe we could just ignore this error?
|
||||||
|
@ -265,15 +252,13 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Save megolm backup key for gossiping")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Save megolm backup key for gossiping")
|
||||||
session.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version)
|
session.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version)
|
||||||
|
|
||||||
awaitCallback<Unit> {
|
|
||||||
extractCurveKeyFromRecoveryKey(creationInfo.recoveryKey)?.toBase64NoPadding()?.let { secret ->
|
extractCurveKeyFromRecoveryKey(creationInfo.recoveryKey)?.toBase64NoPadding()?.let { secret ->
|
||||||
ssssService.storeSecret(
|
ssssService.storeSecret(
|
||||||
KEYBACKUP_SECRET_SSSS_NAME,
|
KEYBACKUP_SECRET_SSSS_NAME,
|
||||||
secret,
|
secret,
|
||||||
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec)), it
|
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Existing megolm backup found")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Existing megolm backup found")
|
||||||
// ensure we store existing backup secret if we have it!
|
// ensure we store existing backup secret if we have it!
|
||||||
|
@ -284,15 +269,13 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||||
}
|
}
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Megolm key valid and known")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Megolm key valid and known")
|
||||||
awaitCallback<Unit> {
|
|
||||||
extractCurveKeyFromRecoveryKey(knownMegolmSecret!!.recoveryKey)?.toBase64NoPadding()?.let { secret ->
|
extractCurveKeyFromRecoveryKey(knownMegolmSecret!!.recoveryKey)?.toBase64NoPadding()?.let { secret ->
|
||||||
ssssService.storeSecret(
|
ssssService.storeSecret(
|
||||||
KEYBACKUP_SECRET_SSSS_NAME,
|
KEYBACKUP_SECRET_SSSS_NAME,
|
||||||
secret,
|
secret,
|
||||||
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec)), it
|
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Megolm key is unknown by this session")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Megolm key is unknown by this session")
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ import im.vector.app.core.platform.VectorViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
||||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
|
||||||
import org.matrix.android.sdk.rx.rx
|
import org.matrix.android.sdk.rx.rx
|
||||||
|
|
||||||
data class AccountDataViewState(
|
data class AccountDataViewState(
|
||||||
|
@ -58,9 +57,7 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A
|
||||||
|
|
||||||
private fun handleDeleteAccountData(action: AccountDataAction.DeleteAccountData) {
|
private fun handleDeleteAccountData(action: AccountDataAction.DeleteAccountData) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
awaitCallback {
|
session.updateAccountData(action.type, emptyMap())
|
||||||
session.updateAccountData(action.type, emptyMap(), it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -283,11 +283,12 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
||||||
"type" to "m.widget"
|
"type" to "m.widget"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
launchWidgetAPIAction(widgetPostAPIMediator, eventData) {
|
||||||
session.updateAccountData(
|
session.updateAccountData(
|
||||||
type = UserAccountDataTypes.TYPE_WIDGETS,
|
type = UserAccountDataTypes.TYPE_WIDGETS,
|
||||||
content = addUserWidgetBody,
|
content = addUserWidgetBody
|
||||||
callback = createWidgetAPICallback(widgetPostAPIMediator, eventData)
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
launchWidgetAPIAction(widgetPostAPIMediator, eventData) {
|
launchWidgetAPIAction(widgetPostAPIMediator, eventData) {
|
||||||
session.widgetService().createRoomWidget(
|
session.widgetService().createRoomWidget(
|
||||||
|
|
Loading…
Reference in a new issue