This commit is contained in:
Valere 2020-02-13 14:06:04 +01:00 committed by Valere
parent 64647cb465
commit e0eede1150
11 changed files with 20 additions and 40 deletions

View file

@ -34,7 +34,7 @@ import im.vector.matrix.android.common.SessionTestParams
import im.vector.matrix.android.common.TestConstants import im.vector.matrix.android.common.TestConstants
import im.vector.matrix.android.common.TestMatrixCallback import im.vector.matrix.android.common.TestMatrixCallback
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecureStorage import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorage
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -57,7 +57,6 @@ class QuadSTests : InstrumentedTest {
@Test @Test
fun test_Generate4SKey() { fun test_Generate4SKey() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val aliceLatch = CountDownLatch(1) val aliceLatch = CountDownLatch(1)
@ -76,7 +75,6 @@ class QuadSTests : InstrumentedTest {
quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner, quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner,
object : MatrixCallback<SSSSKeyCreationInfo> { object : MatrixCallback<SSSSKeyCreationInfo> {
override fun onSuccess(data: SSSSKeyCreationInfo) { override fun onSuccess(data: SSSSKeyCreationInfo) {
recoveryKey = data.recoveryKey recoveryKey = data.recoveryKey
aliceLatch.countDown() aliceLatch.countDown()
@ -110,13 +108,13 @@ class QuadSTests : InstrumentedTest {
Assert.assertNotNull("Key should be stored in account data", accountData) Assert.assertNotNull("Key should be stored in account data", accountData)
val parsed = SecretStorageKeyContent.fromJson(accountData!!.content) val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
Assert.assertNotNull("Key Content cannot be parsed", parsed) Assert.assertNotNull("Key Content cannot be parsed", parsed)
Assert.assertEquals("Unexpected Algorithm", DefaultSharedSecureStorage.ALGORITHM_CURVE25519_AES_SHA2, parsed!!.algorithm) Assert.assertEquals("Unexpected Algorithm", DefaultSharedSecretStorage.ALGORITHM_CURVE25519_AES_SHA2, parsed!!.algorithm)
Assert.assertEquals("Unexpected key name", "Test Key", parsed.name) Assert.assertEquals("Unexpected key name", "Test Key", parsed.name)
Assert.assertNull("Key was not generated from passphrase", parsed.passphrase) Assert.assertNull("Key was not generated from passphrase", parsed.passphrase)
Assert.assertNotNull("Pubkey should be defined", parsed.publicKey) Assert.assertNotNull("Pubkey should be defined", parsed.publicKey)
val privateKeySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(recoveryKey!!) val privateKeySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(recoveryKey!!)
DefaultSharedSecureStorage.withOlmDecryption { olmPkDecryption -> DefaultSharedSecretStorage.withOlmDecryption { olmPkDecryption ->
val pubKey = olmPkDecryption.setPrivateKey(privateKeySpec!!.privateKey) val pubKey = olmPkDecryption.setPrivateKey(privateKeySpec!!.privateKey)
Assert.assertEquals("Unexpected Public Key", pubKey, parsed.publicKey) Assert.assertEquals("Unexpected Public Key", pubKey, parsed.publicKey)
} }
@ -128,10 +126,10 @@ class QuadSTests : InstrumentedTest {
val defaultDataLock = CountDownLatch(1) val defaultDataLock = CountDownLatch(1)
val liveDefAccountData = runBlocking(Dispatchers.Main) { val liveDefAccountData = runBlocking(Dispatchers.Main) {
aliceSession.getLiveAccountData(DefaultSharedSecureStorage.DEFAULT_KEY_ID) aliceSession.getLiveAccountData(DefaultSharedSecretStorage.DEFAULT_KEY_ID)
} }
val accountDefDataObserver = Observer<Optional<UserAccountDataEvent>?> { t -> val accountDefDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
if (t?.getOrNull()?.type == DefaultSharedSecureStorage.DEFAULT_KEY_ID) { if (t?.getOrNull()?.type == DefaultSharedSecretStorage.DEFAULT_KEY_ID) {
defaultKeyAccountData = t.getOrNull()!! defaultKeyAccountData = t.getOrNull()!!
defaultDataLock.countDown() defaultDataLock.countDown()
} }
@ -140,11 +138,9 @@ class QuadSTests : InstrumentedTest {
mTestHelper.await(defaultDataLock) mTestHelper.await(defaultDataLock)
Assert.assertNotNull(defaultKeyAccountData?.content) Assert.assertNotNull(defaultKeyAccountData?.content)
Assert.assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key")) Assert.assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key"))
mTestHelper.signout(aliceSession) mTestHelper.signout(aliceSession)
} }
@ -184,7 +180,7 @@ class QuadSTests : InstrumentedTest {
val decryptCountDownLatch = CountDownLatch(1) val decryptCountDownLatch = CountDownLatch(1)
aliceSession.sharedSecretStorageService.getSecret("secret.of.life", aliceSession.sharedSecretStorageService.getSecret("secret.of.life",
null, //default key null, // default key
keySpec!!, keySpec!!,
object : MatrixCallback<String> { object : MatrixCallback<String> {
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
@ -200,7 +196,6 @@ class QuadSTests : InstrumentedTest {
) )
mTestHelper.await(decryptCountDownLatch) mTestHelper.await(decryptCountDownLatch)
Assert.assertEquals("Secret mismatch", clearSecret, decryptedSecret) Assert.assertEquals("Secret mismatch", clearSecret, decryptedSecret)
mTestHelper.signout(aliceSession) mTestHelper.signout(aliceSession)
} }
@ -225,12 +220,11 @@ class QuadSTests : InstrumentedTest {
mTestHelper.await(countDownLatch) mTestHelper.await(countDownLatch)
//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
val defaultLatch = CountDownLatch(1) val defaultLatch = CountDownLatch(1)
quadS.setDefaultKey(TEST_KEY_ID, TestMatrixCallback(defaultLatch)) quadS.setDefaultKey(TEST_KEY_ID, TestMatrixCallback(defaultLatch))
mTestHelper.await(defaultLatch) mTestHelper.await(defaultLatch)
mTestHelper.signout(aliceSession) mTestHelper.signout(aliceSession)
} }
@ -357,7 +351,6 @@ 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 emptyKeySigner = object : KeySigner { val emptyKeySigner = object : KeySigner {
@ -372,7 +365,6 @@ class QuadSTests : InstrumentedTest {
quadS.generateKey(keyId, keyId, emptyKeySigner, quadS.generateKey(keyId, keyId, emptyKeySigner,
object : MatrixCallback<SSSSKeyCreationInfo> { object : MatrixCallback<SSSSKeyCreationInfo> {
override fun onSuccess(data: SSSSKeyCreationInfo) { override fun onSuccess(data: SSSSKeyCreationInfo) {
creationInfo = data creationInfo = data
generateLatch.countDown() generateLatch.countDown()
@ -393,14 +385,13 @@ class QuadSTests : InstrumentedTest {
val setDefaultLatch = CountDownLatch(1) val setDefaultLatch = CountDownLatch(1)
quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch)) quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
mTestHelper.await(setDefaultLatch) mTestHelper.await(setDefaultLatch)
assertAccountData(session, DefaultSharedSecureStorage.DEFAULT_KEY_ID) assertAccountData(session, DefaultSharedSecretStorage.DEFAULT_KEY_ID)
} }
return creationInfo!! return creationInfo!!
} }
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 emptyKeySigner = object : KeySigner { val emptyKeySigner = object : KeySigner {
@ -418,7 +409,6 @@ class QuadSTests : InstrumentedTest {
emptyKeySigner, emptyKeySigner,
null, null,
object : MatrixCallback<SSSSKeyCreationInfo> { object : MatrixCallback<SSSSKeyCreationInfo> {
override fun onSuccess(data: SSSSKeyCreationInfo) { override fun onSuccess(data: SSSSKeyCreationInfo) {
creationInfo = data creationInfo = data
generateLatch.countDown() generateLatch.countDown()
@ -439,7 +429,7 @@ class QuadSTests : InstrumentedTest {
val setDefaultLatch = CountDownLatch(1) val setDefaultLatch = CountDownLatch(1)
quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch)) quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
mTestHelper.await(setDefaultLatch) mTestHelper.await(setDefaultLatch)
assertAccountData(session, DefaultSharedSecureStorage.DEFAULT_KEY_ID) assertAccountData(session, DefaultSharedSecretStorage.DEFAULT_KEY_ID)
} }
return creationInfo!! return creationInfo!!

View file

@ -16,7 +16,7 @@
package im.vector.matrix.android.api.session.securestorage package im.vector.matrix.android.api.session.securestorage
data class SSSSKeyCreationInfo ( data class SSSSKeyCreationInfo(
val keyId: String = "", val keyId: String = "",
var content: SecretStorageKeyContent?, var content: SecretStorageKeyContent?,
val recoveryKey: String = "" val recoveryKey: String = ""

View file

@ -64,4 +64,3 @@ data class Curve25519AesSha2KeySpec(
return privateKey.contentHashCode() return privateKey.contentHashCode()
} }
} }

View file

@ -98,4 +98,3 @@ data class SSSSPassphrase(
@Json(name = "iterations") val iterations: Int, @Json(name = "iterations") val iterations: Int,
@Json(name = "salt") val salt: String? @Json(name = "salt") val salt: String?
) )

View file

@ -23,7 +23,9 @@ sealed class SharedSecretStorageError(message: String?) : Throwable(message) {
data class UnknownAlgorithm(val keyId: String) : SharedSecretStorageError("Unknown algorithm $keyId") data class UnknownAlgorithm(val keyId: String) : SharedSecretStorageError("Unknown algorithm $keyId")
data class UnsupportedAlgorithm(val algorithm: String) : SharedSecretStorageError("Unknown algorithm $algorithm") data class UnsupportedAlgorithm(val algorithm: String) : SharedSecretStorageError("Unknown algorithm $algorithm")
data class SecretNotEncrypted(val secretName: String) : SharedSecretStorageError("Missing content for secret $secretName") data class SecretNotEncrypted(val secretName: String) : SharedSecretStorageError("Missing content for secret $secretName")
data class SecretNotEncryptedWithKey(val secretName: String, val keyId: String) : SharedSecretStorageError("Missing content for secret $secretName with key $keyId") data class SecretNotEncryptedWithKey(val secretName: String, val keyId: String)
: SharedSecretStorageError("Missing content for secret $secretName with key $keyId")
object BadKeyFormat : SharedSecretStorageError("Bad Key Format") object BadKeyFormat : SharedSecretStorageError("Bad Key Format")
object ParsingError : SharedSecretStorageError("parsing Error") object ParsingError : SharedSecretStorageError("parsing Error")
data class OtherError(val reason: Throwable) : SharedSecretStorageError(reason.localizedMessage) data class OtherError(val reason: Throwable) : SharedSecretStorageError(reason.localizedMessage)

View file

@ -42,7 +42,7 @@ import org.matrix.olm.OlmPkEncryption
import org.matrix.olm.OlmPkMessage import org.matrix.olm.OlmPkMessage
import javax.inject.Inject import javax.inject.Inject
internal class DefaultSharedSecureStorage @Inject constructor( internal class DefaultSharedSecretStorage @Inject constructor(
private val accountDataService: AccountDataService, private val accountDataService: AccountDataService,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope private val cryptoCoroutineScope: CoroutineScope
@ -52,7 +52,6 @@ internal class DefaultSharedSecureStorage @Inject constructor(
keyName: String, keyName: String,
keySigner: KeySigner, keySigner: KeySigner,
callback: MatrixCallback<SSSSKeyCreationInfo>) { callback: MatrixCallback<SSSSKeyCreationInfo>) {
cryptoCoroutineScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
val pkDecryption = OlmPkDecryption() val pkDecryption = OlmPkDecryption()
val pubKey: String val pubKey: String
@ -108,7 +107,6 @@ internal class DefaultSharedSecureStorage @Inject constructor(
progressListener: ProgressListener?, progressListener: ProgressListener?,
callback: MatrixCallback<SSSSKeyCreationInfo>) { callback: MatrixCallback<SSSSKeyCreationInfo>) {
cryptoCoroutineScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener) val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener)
val pkDecryption = OlmPkDecryption() val pkDecryption = OlmPkDecryption()
@ -152,7 +150,6 @@ internal class DefaultSharedSecureStorage @Inject constructor(
} }
} }
) )
} }
} }
@ -191,13 +188,11 @@ internal class DefaultSharedSecureStorage @Inject constructor(
} }
override fun storeSecret(name: String, secretBase64: String, keys: List<String>?, callback: MatrixCallback<Unit>) { override fun storeSecret(name: String, secretBase64: String, keys: List<String>?, callback: MatrixCallback<Unit>) {
cryptoCoroutineScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
val encryptedContents = HashMap<String, EncryptedSecretContent>() val encryptedContents = HashMap<String, EncryptedSecretContent>()
try { try {
if (keys == null || keys.isEmpty()) { if (keys == null || keys.isEmpty()) {
//use default key // use default key
val key = getDefaultKey() val key = getDefaultKey()
when (key) { when (key) {
is KeyInfoResult.Success -> { is KeyInfoResult.Success -> {
@ -263,7 +258,6 @@ internal class DefaultSharedSecureStorage @Inject constructor(
} catch (failure: Throwable) { } catch (failure: Throwable) {
callback.onFailure(failure) callback.onFailure(failure)
} }
} }
// Add default key // Add default key
@ -363,5 +357,3 @@ internal class DefaultSharedSecureStorage @Inject constructor(
} }
} }
} }

View file

@ -22,8 +22,7 @@ import java.lang.reflect.Type
class AccountDataJsonAdapterFactory<UserAccountData> : JsonAdapter.Factory { class AccountDataJsonAdapterFactory<UserAccountData> : JsonAdapter.Factory {
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? { override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
} }
} }

View file

@ -36,7 +36,7 @@ import im.vector.matrix.android.api.session.accountdata.AccountDataService
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
import im.vector.matrix.android.api.session.securestorage.SecureStorageService import im.vector.matrix.android.api.session.securestorage.SecureStorageService
import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecureStorage import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorage
import im.vector.matrix.android.internal.crypto.verification.VerificationMessageLiveObserver import im.vector.matrix.android.internal.crypto.verification.VerificationMessageLiveObserver
import im.vector.matrix.android.internal.database.LiveEntityObserver import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.database.SessionRealmConfigurationFactory import im.vector.matrix.android.internal.database.SessionRealmConfigurationFactory
@ -272,5 +272,5 @@ internal abstract class SessionModule {
abstract fun bindAccountDataServiceService(accountDataService: DefaultAccountDataService): AccountDataService abstract fun bindAccountDataServiceService(accountDataService: DefaultAccountDataService): AccountDataService
@Binds @Binds
abstract fun bindSharedSecuredSecretStorageService(service: DefaultSharedSecureStorage): SharedSecretStorageService abstract fun bindSharedSecuredSecretStorageService(service: DefaultSharedSecretStorage): SharedSecretStorageService
} }

View file

@ -211,7 +211,8 @@ internal class UserAccountDataSyncHandler @Inject constructor(
} }
fun handleGenericAccountData(realm: Realm, type: String, content: Content?) { fun handleGenericAccountData(realm: Realm, type: String, content: Content?) {
val existing = realm.where<UserAccountDataEntity>().equalTo(UserAccountDataEntityFields.TYPE, type) val existing = realm.where<UserAccountDataEntity>()
.equalTo(UserAccountDataEntityFields.TYPE, type)
.findFirst() .findFirst()
if (existing != null) { if (existing != null) {
// Update current value // Update current value

View file

@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.session.user.accountdata
import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.sync.UserAccountDataSyncHandler
import im.vector.matrix.android.internal.session.sync.model.accountdata.BreadcrumbsContent import im.vector.matrix.android.internal.session.sync.model.accountdata.BreadcrumbsContent
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task

View file

@ -39,7 +39,6 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A
private val session: Session) private val session: Session)
: VectorViewModel<AccountDataViewState, EmptyAction, EmptyViewEvents>(initialState) { : VectorViewModel<AccountDataViewState, EmptyAction, EmptyViewEvents>(initialState) {
init { init {
session.rx().liveAccountData(emptyList()) session.rx().liveAccountData(emptyList())
.execute { .execute {