Daggerization and Kotlinification of SecretStoringUtils
This commit is contained in:
parent
1ba8a58219
commit
384dd100e9
|
@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.group.GroupService
|
||||||
import im.vector.matrix.android.api.session.pushers.PushersService
|
import im.vector.matrix.android.api.session.pushers.PushersService
|
||||||
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
||||||
import im.vector.matrix.android.api.session.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
||||||
import im.vector.matrix.android.api.session.signout.SignOutService
|
import im.vector.matrix.android.api.session.signout.SignOutService
|
||||||
import im.vector.matrix.android.api.session.sync.FilterService
|
import im.vector.matrix.android.api.session.sync.FilterService
|
||||||
import im.vector.matrix.android.api.session.sync.SyncState
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
|
@ -50,7 +51,8 @@ interface Session :
|
||||||
FileService,
|
FileService,
|
||||||
PushRuleService,
|
PushRuleService,
|
||||||
PushersService,
|
PushersService,
|
||||||
InitialSyncProgressService {
|
InitialSyncProgressService,
|
||||||
|
SecureStorageService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The params associated to the session
|
* The params associated to the session
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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.api.session.securestorage
|
||||||
|
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
interface SecureStorageService {
|
||||||
|
|
||||||
|
fun securelyStoreObject(any: Any, keyAlias: String, outputStream: OutputStream)
|
||||||
|
|
||||||
|
fun <T> loadSecureSecret(inputStream: InputStream, keyAlias: String): T?
|
||||||
|
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ import javax.crypto.spec.GCMParameterSpec
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.PBEKeySpec
|
import javax.crypto.spec.PBEKeySpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
import javax.inject.Inject
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,15 +73,17 @@ import javax.security.auth.x500.X500Principal
|
||||||
* Important: Keys stored in the keystore can be wiped out (depends of the OS version, like for example if you
|
* Important: Keys stored in the keystore can be wiped out (depends of the OS version, like for example if you
|
||||||
* add a pin or change the schema); So you might and with a useless pile of bytes.
|
* add a pin or change the schema); So you might and with a useless pile of bytes.
|
||||||
*/
|
*/
|
||||||
object SecretStoringUtils {
|
internal class SecretStoringUtils @Inject constructor(private val context: Context) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
private const val ANDROID_KEY_STORE = "AndroidKeyStore"
|
private const val ANDROID_KEY_STORE = "AndroidKeyStore"
|
||||||
private const val AES_MODE = "AES/GCM/NoPadding";
|
private const val AES_MODE = "AES/GCM/NoPadding"
|
||||||
private const val RSA_MODE = "RSA/ECB/PKCS1Padding"
|
private const val RSA_MODE = "RSA/ECB/PKCS1Padding"
|
||||||
|
|
||||||
private const val FORMAT_API_M: Byte = 0
|
private const val FORMAT_API_M: Byte = 0
|
||||||
private const val FORMAT_1: Byte = 1
|
private const val FORMAT_1: Byte = 1
|
||||||
private const val FORMAT_2: Byte = 2
|
private const val FORMAT_2: Byte = 2
|
||||||
|
}
|
||||||
|
|
||||||
private val keyStore: KeyStore by lazy {
|
private val keyStore: KeyStore by lazy {
|
||||||
KeyStore.getInstance(ANDROID_KEY_STORE).apply {
|
KeyStore.getInstance(ANDROID_KEY_STORE).apply {
|
||||||
|
@ -109,13 +112,11 @@ object SecretStoringUtils {
|
||||||
* The secret is encrypted using the following method: AES/GCM/NoPadding
|
* The secret is encrypted using the following method: AES/GCM/NoPadding
|
||||||
*/
|
*/
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun securelyStoreString(secret: String, keyAlias: String, context: Context): ByteArray? {
|
fun securelyStoreString(secret: String, keyAlias: String): ByteArray? {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
return when {
|
||||||
return encryptStringM(secret, keyAlias)
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> encryptStringM(secret, keyAlias)
|
||||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> encryptStringK(secret, keyAlias)
|
||||||
return encryptStringK(secret, keyAlias, context)
|
else -> encryptForOldDevicesNotGood(secret, keyAlias)
|
||||||
} else {
|
|
||||||
return encryptForOldDevicesNotGood(secret, keyAlias)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,33 +124,27 @@ object SecretStoringUtils {
|
||||||
* Decrypt a secret that was encrypted by #securelyStoreString()
|
* Decrypt a secret that was encrypted by #securelyStoreString()
|
||||||
*/
|
*/
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun loadSecureSecret(encrypted: ByteArray, keyAlias: String, context: Context): String? {
|
fun loadSecureSecret(encrypted: ByteArray, keyAlias: String): String? {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
return when {
|
||||||
return decryptStringM(encrypted, keyAlias)
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> decryptStringM(encrypted, keyAlias)
|
||||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> decryptStringK(encrypted, keyAlias)
|
||||||
return decryptStringK(encrypted, keyAlias, context)
|
else -> decryptForOldDevicesNotGood(encrypted, keyAlias)
|
||||||
} else {
|
|
||||||
return decryptForOldDevicesNotGood(encrypted, keyAlias)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun securelyStoreObject(any: Any, keyAlias: String, output: OutputStream, context: Context) {
|
fun securelyStoreObject(any: Any, keyAlias: String, output: OutputStream) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
when {
|
||||||
saveSecureObjectM(keyAlias, output, any)
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> saveSecureObjectM(keyAlias, output, any)
|
||||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> saveSecureObjectK(keyAlias, output, any)
|
||||||
return saveSecureObjectK(keyAlias, output, any, context)
|
else -> saveSecureObjectOldNotGood(keyAlias, output, any)
|
||||||
} else {
|
|
||||||
return saveSecureObjectOldNotGood(keyAlias, output, any)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> loadSecureSecret(inputStream: InputStream, keyAlias: String, context: Context): T? {
|
fun <T> loadSecureSecret(inputStream: InputStream, keyAlias: String): T? {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
return when {
|
||||||
return loadSecureObjectM(keyAlias, inputStream)
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> loadSecureObjectM(keyAlias, inputStream)
|
||||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> loadSecureObjectK(keyAlias, inputStream)
|
||||||
return loadSecureObjectK(keyAlias, inputStream, context)
|
else -> loadSecureObjectOldNotGood(keyAlias, inputStream)
|
||||||
} else {
|
|
||||||
return loadSecureObjectOldNotGood(keyAlias, inputStream)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +177,7 @@ object SecretStoringUtils {
|
||||||
Generate a key pair for encryption
|
Generate a key pair for encryption
|
||||||
*/
|
*/
|
||||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||||
fun getOrGenerateKeyPairForAlias(alias: String, context: Context): KeyStore.PrivateKeyEntry {
|
fun getOrGenerateKeyPairForAlias(alias: String): KeyStore.PrivateKeyEntry {
|
||||||
val privateKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.PrivateKeyEntry)
|
val privateKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.PrivateKeyEntry)
|
||||||
|
|
||||||
if (privateKeyEntry != null) return privateKeyEntry
|
if (privateKeyEntry != null) return privateKeyEntry
|
||||||
|
@ -234,14 +229,14 @@ object SecretStoringUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||||
private fun encryptStringK(text: String, keyAlias: String, context: Context): ByteArray? {
|
private fun encryptStringK(text: String, keyAlias: String): ByteArray? {
|
||||||
//we generate a random symetric key
|
//we generate a random symetric key
|
||||||
val key = ByteArray(16)
|
val key = ByteArray(16)
|
||||||
secureRandom.nextBytes(key)
|
secureRandom.nextBytes(key)
|
||||||
val sKey = SecretKeySpec(key, "AES")
|
val sKey = SecretKeySpec(key, "AES")
|
||||||
|
|
||||||
//we encrypt this key thanks to the key store
|
//we encrypt this key thanks to the key store
|
||||||
val encryptedKey = rsaEncrypt(keyAlias, key, context)
|
val encryptedKey = rsaEncrypt(keyAlias, key)
|
||||||
|
|
||||||
val cipher = Cipher.getInstance(AES_MODE)
|
val cipher = Cipher.getInstance(AES_MODE)
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, sKey)
|
cipher.init(Cipher.ENCRYPT_MODE, sKey)
|
||||||
|
@ -286,12 +281,12 @@ object SecretStoringUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||||
private fun decryptStringK(data: ByteArray, keyAlias: String, context: Context): String? {
|
private fun decryptStringK(data: ByteArray, keyAlias: String): String? {
|
||||||
|
|
||||||
val (encryptedKey, iv, encrypted) = format1Extract(ByteArrayInputStream(data))
|
val (encryptedKey, iv, encrypted) = format1Extract(ByteArrayInputStream(data))
|
||||||
|
|
||||||
//we need to decrypt the key
|
//we need to decrypt the key
|
||||||
val sKeyBytes = rsaDecrypt(keyAlias, ByteArrayInputStream(encryptedKey), context)
|
val sKeyBytes = rsaDecrypt(keyAlias, ByteArrayInputStream(encryptedKey))
|
||||||
val cipher = Cipher.getInstance(AES_MODE)
|
val cipher = Cipher.getInstance(AES_MODE)
|
||||||
val spec = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) IvParameterSpec(iv) else GCMParameterSpec(128, iv)
|
val spec = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) IvParameterSpec(iv) else GCMParameterSpec(128, iv)
|
||||||
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sKeyBytes, "AES"), spec)
|
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sKeyBytes, "AES"), spec)
|
||||||
|
@ -321,14 +316,14 @@ object SecretStoringUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||||
private fun saveSecureObjectK(keyAlias: String, output: OutputStream, writeObject: Any, context: Context) {
|
private fun saveSecureObjectK(keyAlias: String, output: OutputStream, writeObject: Any) {
|
||||||
//we generate a random symetric key
|
//we generate a random symetric key
|
||||||
val key = ByteArray(16)
|
val key = ByteArray(16)
|
||||||
secureRandom.nextBytes(key)
|
secureRandom.nextBytes(key)
|
||||||
val sKey = SecretKeySpec(key, "AES")
|
val sKey = SecretKeySpec(key, "AES")
|
||||||
|
|
||||||
//we encrypt this key thanks to the key store
|
//we encrypt this key thanks to the key store
|
||||||
val encryptedKey = rsaEncrypt(keyAlias, key, context)
|
val encryptedKey = rsaEncrypt(keyAlias, key)
|
||||||
|
|
||||||
val cipher = Cipher.getInstance(AES_MODE)
|
val cipher = Cipher.getInstance(AES_MODE)
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, sKey)
|
cipher.init(Cipher.ENCRYPT_MODE, sKey)
|
||||||
|
@ -418,12 +413,12 @@ object SecretStoringUtils {
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun <T> loadSecureObjectK(keyAlias: String, inputStream: InputStream, context: Context): T? {
|
private fun <T> loadSecureObjectK(keyAlias: String, inputStream: InputStream): T? {
|
||||||
|
|
||||||
val (encryptedKey, iv, encrypted) = format1Extract(inputStream)
|
val (encryptedKey, iv, encrypted) = format1Extract(inputStream)
|
||||||
|
|
||||||
//we need to decrypt the key
|
//we need to decrypt the key
|
||||||
val sKeyBytes = rsaDecrypt(keyAlias, ByteArrayInputStream(encryptedKey), context)
|
val sKeyBytes = rsaDecrypt(keyAlias, ByteArrayInputStream(encryptedKey))
|
||||||
val cipher = Cipher.getInstance(AES_MODE)
|
val cipher = Cipher.getInstance(AES_MODE)
|
||||||
val spec = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) IvParameterSpec(iv) else GCMParameterSpec(128, iv)
|
val spec = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) IvParameterSpec(iv) else GCMParameterSpec(128, iv)
|
||||||
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sKeyBytes, "AES"), spec)
|
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sKeyBytes, "AES"), spec)
|
||||||
|
@ -464,8 +459,8 @@ object SecretStoringUtils {
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
private fun rsaEncrypt(alias: String, secret: ByteArray, context: Context): ByteArray {
|
private fun rsaEncrypt(alias: String, secret: ByteArray): ByteArray {
|
||||||
val privateKeyEntry = getOrGenerateKeyPairForAlias(alias, context)
|
val privateKeyEntry = getOrGenerateKeyPairForAlias(alias)
|
||||||
// Encrypt the text
|
// Encrypt the text
|
||||||
val inputCipher = Cipher.getInstance(RSA_MODE)
|
val inputCipher = Cipher.getInstance(RSA_MODE)
|
||||||
inputCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.certificate.publicKey)
|
inputCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.certificate.publicKey)
|
||||||
|
@ -480,8 +475,8 @@ object SecretStoringUtils {
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
private fun rsaDecrypt(alias: String, encrypted: InputStream, context: Context): ByteArray {
|
private fun rsaDecrypt(alias: String, encrypted: InputStream): ByteArray {
|
||||||
val privateKeyEntry = getOrGenerateKeyPairForAlias(alias, context)
|
val privateKeyEntry = getOrGenerateKeyPairForAlias(alias)
|
||||||
val output = Cipher.getInstance(RSA_MODE)
|
val output = Cipher.getInstance(RSA_MODE)
|
||||||
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.privateKey)
|
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.privateKey)
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,8 @@ import javax.inject.Inject
|
||||||
* then we generate a random secret key. The database key is encrypted with the secret key; The secret
|
* then we generate a random secret key. The database key is encrypted with the secret key; The secret
|
||||||
* key is encrypted with the public RSA key and stored with the encrypted key in the shared pref
|
* key is encrypted with the public RSA key and stored with the encrypted key in the shared pref
|
||||||
*/
|
*/
|
||||||
internal class RealmKeysUtils @Inject constructor(private val context: Context) {
|
internal class RealmKeysUtils @Inject constructor(context: Context,
|
||||||
|
private val secretStoringUtils: SecretStoringUtils) {
|
||||||
|
|
||||||
private val rng = SecureRandom()
|
private val rng = SecureRandom()
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ internal class RealmKeysUtils @Inject constructor(private val context: Context)
|
||||||
private fun createAndSaveKeyForDatabase(alias: String): ByteArray {
|
private fun createAndSaveKeyForDatabase(alias: String): ByteArray {
|
||||||
val key = generateKeyForRealm()
|
val key = generateKeyForRealm()
|
||||||
val encodedKey = Base64.encodeToString(key, Base64.NO_PADDING)
|
val encodedKey = Base64.encodeToString(key, Base64.NO_PADDING)
|
||||||
val toStore = SecretStoringUtils.securelyStoreString(encodedKey, alias, context)
|
val toStore = secretStoringUtils.securelyStoreString(encodedKey, alias)
|
||||||
sharedPreferences
|
sharedPreferences
|
||||||
.edit()
|
.edit()
|
||||||
.putString("${ENCRYPTED_KEY_PREFIX}_$alias", Base64.encodeToString(toStore!!, Base64.NO_PADDING))
|
.putString("${ENCRYPTED_KEY_PREFIX}_$alias", Base64.encodeToString(toStore!!, Base64.NO_PADDING))
|
||||||
|
@ -80,7 +81,7 @@ internal class RealmKeysUtils @Inject constructor(private val context: Context)
|
||||||
private fun extractKeyForDatabase(alias: String): ByteArray {
|
private fun extractKeyForDatabase(alias: String): ByteArray {
|
||||||
val encryptedB64 = sharedPreferences.getString("${ENCRYPTED_KEY_PREFIX}_$alias", null)
|
val encryptedB64 = sharedPreferences.getString("${ENCRYPTED_KEY_PREFIX}_$alias", null)
|
||||||
val encryptedKey = Base64.decode(encryptedB64, Base64.NO_PADDING)
|
val encryptedKey = Base64.decode(encryptedB64, Base64.NO_PADDING)
|
||||||
val b64 = SecretStoringUtils.loadSecureSecret(encryptedKey, alias, context)
|
val b64 = secretStoringUtils.loadSecureSecret(encryptedKey, alias)
|
||||||
return Base64.decode(b64!!, Base64.NO_PADDING)
|
return Base64.decode(b64!!, Base64.NO_PADDING)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +105,7 @@ internal class RealmKeysUtils @Inject constructor(private val context: Context)
|
||||||
// Delete elements related to the alias
|
// Delete elements related to the alias
|
||||||
fun clear(alias: String) {
|
fun clear(alias: String) {
|
||||||
if (hasKeyForDatabase(alias)) {
|
if (hasKeyForDatabase(alias)) {
|
||||||
SecretStoringUtils.safeDeleteKey(alias)
|
secretStoringUtils.safeDeleteKey(alias)
|
||||||
|
|
||||||
sharedPreferences
|
sharedPreferences
|
||||||
.edit()
|
.edit()
|
||||||
|
|
|
@ -35,6 +35,7 @@ import im.vector.matrix.android.api.session.group.GroupService
|
||||||
import im.vector.matrix.android.api.session.pushers.PushersService
|
import im.vector.matrix.android.api.session.pushers.PushersService
|
||||||
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
||||||
import im.vector.matrix.android.api.session.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
||||||
import im.vector.matrix.android.api.session.signout.SignOutService
|
import im.vector.matrix.android.api.session.signout.SignOutService
|
||||||
import im.vector.matrix.android.api.session.sync.FilterService
|
import im.vector.matrix.android.api.session.sync.FilterService
|
||||||
import im.vector.matrix.android.api.session.sync.SyncState
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
|
@ -63,6 +64,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
||||||
private val pushersService: Lazy<PushersService>,
|
private val pushersService: Lazy<PushersService>,
|
||||||
private val cryptoService: Lazy<DefaultCryptoService>,
|
private val cryptoService: Lazy<DefaultCryptoService>,
|
||||||
private val fileService: Lazy<FileService>,
|
private val fileService: Lazy<FileService>,
|
||||||
|
private val secureStorageService: Lazy<SecureStorageService>,
|
||||||
private val syncThreadProvider: Provider<SyncThread>,
|
private val syncThreadProvider: Provider<SyncThread>,
|
||||||
private val contentUrlResolver: ContentUrlResolver,
|
private val contentUrlResolver: ContentUrlResolver,
|
||||||
private val contentUploadProgressTracker: ContentUploadStateTracker,
|
private val contentUploadProgressTracker: ContentUploadStateTracker,
|
||||||
|
@ -78,7 +80,8 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
||||||
PushRuleService by pushRuleService.get(),
|
PushRuleService by pushRuleService.get(),
|
||||||
PushersService by pushersService.get(),
|
PushersService by pushersService.get(),
|
||||||
FileService by fileService.get(),
|
FileService by fileService.get(),
|
||||||
InitialSyncProgressService by initialSyncProgressService.get() {
|
InitialSyncProgressService by initialSyncProgressService.get(),
|
||||||
|
SecureStorageService by secureStorageService.get() {
|
||||||
|
|
||||||
private var isOpen = false
|
private var isOpen = false
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
||||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
||||||
import im.vector.matrix.android.internal.database.RealmKeysUtils
|
import im.vector.matrix.android.internal.database.RealmKeysUtils
|
||||||
import im.vector.matrix.android.internal.database.model.SessionRealmModule
|
import im.vector.matrix.android.internal.database.model.SessionRealmModule
|
||||||
|
@ -39,6 +40,7 @@ import im.vector.matrix.android.internal.session.room.EventRelationsAggregationU
|
||||||
import im.vector.matrix.android.internal.session.room.create.RoomCreateEventLiveObserver
|
import im.vector.matrix.android.internal.session.room.create.RoomCreateEventLiveObserver
|
||||||
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
|
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
|
||||||
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
|
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
|
||||||
|
import im.vector.matrix.android.internal.session.securestorage.DefaultSecureStorageService
|
||||||
import im.vector.matrix.android.internal.util.md5
|
import im.vector.matrix.android.internal.util.md5
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
@ -166,4 +168,7 @@ internal abstract class SessionModule {
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindInitialSyncProgressService(initialSyncProgressService: DefaultInitialSyncProgressService): InitialSyncProgressService
|
abstract fun bindInitialSyncProgressService(initialSyncProgressService: DefaultInitialSyncProgressService): InitialSyncProgressService
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindSecureStorageService(secureStorageService: DefaultSecureStorageService): SecureStorageService
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.session.securestorage
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
||||||
|
import im.vector.matrix.android.api.util.SecretStoringUtils
|
||||||
|
import im.vector.matrix.android.internal.di.UserMd5
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class DefaultSecureStorageService @Inject constructor(@UserMd5 private val userMd5: String,
|
||||||
|
private val secretStoringUtils: SecretStoringUtils) : SecureStorageService {
|
||||||
|
|
||||||
|
override fun securelyStoreObject(any: Any, keyAlias: String, outputStream: OutputStream) {
|
||||||
|
secretStoringUtils.securelyStoreObject(any, "${userMd5}_$keyAlias", outputStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T> loadSecureSecret(inputStream: InputStream, keyAlias: String): T? {
|
||||||
|
return secretStoringUtils.loadSecureSecret(inputStream, "${userMd5}_$keyAlias")
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,6 @@ import androidx.annotation.WorkerThread
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.Person
|
import androidx.core.app.Person
|
||||||
import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
||||||
import im.vector.matrix.android.api.util.SecretStoringUtils
|
|
||||||
import im.vector.riotx.BuildConfig
|
import im.vector.riotx.BuildConfig
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ActiveSessionHolder
|
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||||
|
@ -448,7 +447,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||||
val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME)
|
val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME)
|
||||||
if (!file.exists()) file.createNewFile()
|
if (!file.exists()) file.createNewFile()
|
||||||
FileOutputStream(file).use {
|
FileOutputStream(file).use {
|
||||||
SecretStoringUtils.securelyStoreObject(eventList, "notificationMgr", it, this.context)
|
activeSessionHolder.getSafeActiveSession()?.securelyStoreObject(eventList, KEY_ALIAS_SECRET_STORAGE, it)
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Timber.e(e, "## Failed to save cached notification info")
|
Timber.e(e, "## Failed to save cached notification info")
|
||||||
|
@ -461,7 +460,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||||
val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME)
|
val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME)
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
FileInputStream(file).use {
|
FileInputStream(file).use {
|
||||||
val events: ArrayList<NotifiableEvent>? = SecretStoringUtils.loadSecureSecret(it, "notificationMgr", this.context)
|
val events: ArrayList<NotifiableEvent>? = activeSessionHolder.getSafeActiveSession()?.loadSecureSecret(it, KEY_ALIAS_SECRET_STORAGE)
|
||||||
if (events != null) {
|
if (events != null) {
|
||||||
return ArrayList(events.mapNotNull { it as? NotifiableEvent })
|
return ArrayList(events.mapNotNull { it as? NotifiableEvent })
|
||||||
}
|
}
|
||||||
|
@ -486,5 +485,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||||
private const val ROOM_EVENT_NOTIFICATION_ID = 2
|
private const val ROOM_EVENT_NOTIFICATION_ID = 2
|
||||||
|
|
||||||
private const val ROOMS_NOTIFICATIONS_FILE_NAME = "im.vector.notifications.cache"
|
private const val ROOMS_NOTIFICATIONS_FILE_NAME = "im.vector.notifications.cache"
|
||||||
|
|
||||||
|
private const val KEY_ALIAS_SECRET_STORAGE = "notificationMgr"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue