Remove unused code.
This commit is contained in:
parent
7711eb584c
commit
567f298bd0
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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 org.matrix.android.sdk.internal.crypto.attachments
|
|
||||||
|
|
||||||
import android.util.Base64
|
|
||||||
import org.matrix.android.sdk.internal.util.base64ToUnpaddedBase64
|
|
||||||
import java.io.FilterInputStream
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.security.MessageDigest
|
|
||||||
|
|
||||||
class MatrixDigestCheckInputStream(
|
|
||||||
inputStream: InputStream?,
|
|
||||||
private val expectedDigest: String
|
|
||||||
) : FilterInputStream(inputStream) {
|
|
||||||
|
|
||||||
private val digest = MessageDigest.getInstance("SHA-256")
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
override fun read(): Int {
|
|
||||||
val b = `in`.read()
|
|
||||||
if (b >= 0) {
|
|
||||||
digest.update(b.toByte())
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b == -1) {
|
|
||||||
ensureDigest()
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
override fun read(
|
|
||||||
b: ByteArray,
|
|
||||||
off: Int,
|
|
||||||
len: Int): Int {
|
|
||||||
val n = `in`.read(b, off, len)
|
|
||||||
if (n > 0) {
|
|
||||||
digest.update(b, off, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == -1) {
|
|
||||||
ensureDigest()
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private fun ensureDigest() {
|
|
||||||
val currentDigestValue = base64ToUnpaddedBase64(Base64.encodeToString(digest.digest(), Base64.DEFAULT))
|
|
||||||
if (currentDigestValue != expectedDigest) {
|
|
||||||
throw IOException("Bad digest")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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 org.matrix.android.sdk.internal.crypto.keysbackup.model
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonClass
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data model for response to [KeysBackup.isKeyBackupTrusted()].
|
|
||||||
*/
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class KeyBackupVersionTrust(
|
|
||||||
/**
|
|
||||||
* Flag to indicate if the backup is trusted.
|
|
||||||
* true if there is a signature that is valid & from a trusted device.
|
|
||||||
*/
|
|
||||||
var usable: Boolean = false,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signatures found in the backup version.
|
|
||||||
*/
|
|
||||||
var signatures: MutableList<KeyBackupVersionTrustSignature> = ArrayList()
|
|
||||||
)
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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 org.matrix.android.sdk.internal.crypto.keysbackup.model
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A signature in a the `KeyBackupVersionTrust` object.
|
|
||||||
*/
|
|
||||||
class KeyBackupVersionTrustSignature {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The device that signed the backup version.
|
|
||||||
*/
|
|
||||||
var device: CryptoDeviceInfo? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
*Flag to indicate the signature from this device is valid.
|
|
||||||
*/
|
|
||||||
var valid = false
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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 org.matrix.android.sdk.internal.crypto.model.event
|
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
|
||||||
import com.squareup.moshi.JsonClass
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class NewDeviceContent(
|
|
||||||
// the device id
|
|
||||||
@Json(name = "device_id")
|
|
||||||
val deviceId: String? = null,
|
|
||||||
|
|
||||||
// the room ids list
|
|
||||||
@Json(name = "rooms")
|
|
||||||
val rooms: List<String>? = null
|
|
||||||
)
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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 org.matrix.android.sdk.internal.crypto.model.rest
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class representing the dummy content
|
|
||||||
* Ref: https://matrix.org/docs/spec/client_server/latest#id82
|
|
||||||
*/
|
|
||||||
typealias DummyContent = Unit
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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 org.matrix.android.sdk.internal.crypto.model.rest
|
|
||||||
|
|
||||||
import org.matrix.olm.OlmPkMessage
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build from a OlmPkMessage object
|
|
||||||
*
|
|
||||||
* @param olmPkMessage OlmPkMessage
|
|
||||||
*/
|
|
||||||
class EncryptedBodyFileInfo(olmPkMessage: OlmPkMessage) {
|
|
||||||
var ciphertext = olmPkMessage.mCipherText
|
|
||||||
var mac = olmPkMessage.mMac
|
|
||||||
var ephemeral = olmPkMessage.mEphemeralKey
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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 org.matrix.android.sdk.internal.extensions
|
|
||||||
|
|
||||||
import arrow.core.Failure
|
|
||||||
import arrow.core.Success
|
|
||||||
import arrow.core.Try
|
|
||||||
import arrow.core.TryOf
|
|
||||||
import arrow.core.fix
|
|
||||||
|
|
||||||
inline fun <A> TryOf<A>.onError(f: (Throwable) -> Unit): Try<A> = fix()
|
|
||||||
.fold(
|
|
||||||
{
|
|
||||||
f(it)
|
|
||||||
Failure(it)
|
|
||||||
},
|
|
||||||
{ Success(it) }
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Same as doOnNext for Observables
|
|
||||||
*/
|
|
||||||
inline fun <A> Try<A>.alsoDo(f: (A) -> Unit) = map {
|
|
||||||
f(it)
|
|
||||||
it
|
|
||||||
}
|
|
|
@ -1,299 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("DEPRECATION")
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.os.Build
|
|
||||||
import android.preference.PreferenceManager
|
|
||||||
import android.security.KeyPairGeneratorSpec
|
|
||||||
import android.security.keystore.KeyGenParameterSpec
|
|
||||||
import android.security.keystore.KeyProperties
|
|
||||||
import android.util.Base64
|
|
||||||
import androidx.core.content.edit
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.OutputStream
|
|
||||||
import java.math.BigInteger
|
|
||||||
import java.security.InvalidAlgorithmParameterException
|
|
||||||
import java.security.InvalidKeyException
|
|
||||||
import java.security.KeyPairGenerator
|
|
||||||
import java.security.KeyStore
|
|
||||||
import java.security.KeyStoreException
|
|
||||||
import java.security.NoSuchAlgorithmException
|
|
||||||
import java.security.NoSuchProviderException
|
|
||||||
import java.security.PrivateKey
|
|
||||||
import java.security.SecureRandom
|
|
||||||
import java.security.UnrecoverableKeyException
|
|
||||||
import java.security.cert.CertificateException
|
|
||||||
import java.security.spec.AlgorithmParameterSpec
|
|
||||||
import java.security.spec.RSAKeyGenParameterSpec
|
|
||||||
import java.util.Calendar
|
|
||||||
import javax.crypto.Cipher
|
|
||||||
import javax.crypto.CipherInputStream
|
|
||||||
import javax.crypto.CipherOutputStream
|
|
||||||
import javax.crypto.IllegalBlockSizeException
|
|
||||||
import javax.crypto.KeyGenerator
|
|
||||||
import javax.crypto.NoSuchPaddingException
|
|
||||||
import javax.crypto.SecretKey
|
|
||||||
import javax.crypto.spec.GCMParameterSpec
|
|
||||||
import javax.crypto.spec.IvParameterSpec
|
|
||||||
import javax.crypto.spec.SecretKeySpec
|
|
||||||
import javax.security.auth.x500.X500Principal
|
|
||||||
|
|
||||||
object CompatUtil {
|
|
||||||
private val TAG = CompatUtil::class.java.simpleName
|
|
||||||
private const val ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"
|
|
||||||
private const val AES_GCM_CIPHER_TYPE = "AES/GCM/NoPadding"
|
|
||||||
private const val AES_GCM_KEY_SIZE_IN_BITS = 128
|
|
||||||
private const val AES_GCM_IV_LENGTH = 12
|
|
||||||
private const val AES_LOCAL_PROTECTION_KEY_ALIAS = "aes_local_protection"
|
|
||||||
|
|
||||||
private const val RSA_WRAP_LOCAL_PROTECTION_KEY_ALIAS = "rsa_wrap_local_protection"
|
|
||||||
private const val RSA_WRAP_CIPHER_TYPE = "RSA/NONE/PKCS1Padding"
|
|
||||||
private const val AES_WRAPPED_PROTECTION_KEY_SHARED_PREFERENCE = "aes_wrapped_local_protection"
|
|
||||||
|
|
||||||
private const val SHARED_KEY_ANDROID_VERSION_WHEN_KEY_HAS_BEEN_GENERATED = "android_version_when_key_has_been_generated"
|
|
||||||
|
|
||||||
private var sSecretKeyAndVersion: SecretKeyAndVersion? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the unique SecureRandom instance shared for all local storage encryption operations.
|
|
||||||
*/
|
|
||||||
private val prng: SecureRandom by lazy(LazyThreadSafetyMode.NONE) { SecureRandom() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the AES key used for local storage encryption/decryption with AES/GCM.
|
|
||||||
* The key is created if it does not exist already in the keystore.
|
|
||||||
* From Marshmallow, this key is generated and operated directly from the android keystore.
|
|
||||||
* From KitKat and before Marshmallow, this key is stored in the application shared preferences
|
|
||||||
* wrapped by a RSA key generated and operated directly from the android keystore.
|
|
||||||
*
|
|
||||||
* @param context the context holding the application shared preferences
|
|
||||||
*/
|
|
||||||
@Synchronized
|
|
||||||
@Throws(KeyStoreException::class,
|
|
||||||
CertificateException::class,
|
|
||||||
NoSuchAlgorithmException::class,
|
|
||||||
IOException::class,
|
|
||||||
NoSuchProviderException::class,
|
|
||||||
InvalidAlgorithmParameterException::class,
|
|
||||||
NoSuchPaddingException::class,
|
|
||||||
InvalidKeyException::class,
|
|
||||||
IllegalBlockSizeException::class,
|
|
||||||
UnrecoverableKeyException::class)
|
|
||||||
private fun getAesGcmLocalProtectionKey(context: Context): SecretKeyAndVersion {
|
|
||||||
if (sSecretKeyAndVersion == null) {
|
|
||||||
val keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER)
|
|
||||||
keyStore.load(null)
|
|
||||||
|
|
||||||
Timber.i(TAG, "Loading local protection key")
|
|
||||||
|
|
||||||
var key: SecretKey?
|
|
||||||
|
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
// Get the version of Android when the key has been generated, default to the current version of the system. In this case, the
|
|
||||||
// key will be generated
|
|
||||||
val androidVersionWhenTheKeyHasBeenGenerated = sharedPreferences
|
|
||||||
.getInt(SHARED_KEY_ANDROID_VERSION_WHEN_KEY_HAS_BEEN_GENERATED, Build.VERSION.SDK_INT)
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
if (keyStore.containsAlias(AES_LOCAL_PROTECTION_KEY_ALIAS)) {
|
|
||||||
Timber.i(TAG, "AES local protection key found in keystore")
|
|
||||||
key = keyStore.getKey(AES_LOCAL_PROTECTION_KEY_ALIAS, null) as SecretKey
|
|
||||||
} else {
|
|
||||||
// Check if a key has been created on version < M (in case of OS upgrade)
|
|
||||||
key = readKeyApiL(sharedPreferences, keyStore)
|
|
||||||
|
|
||||||
if (key == null) {
|
|
||||||
Timber.i(TAG, "Generating AES key with keystore")
|
|
||||||
val generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE_PROVIDER)
|
|
||||||
generator.init(
|
|
||||||
KeyGenParameterSpec.Builder(AES_LOCAL_PROTECTION_KEY_ALIAS,
|
|
||||||
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
|
|
||||||
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
|
||||||
.setKeySize(AES_GCM_KEY_SIZE_IN_BITS)
|
|
||||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
|
||||||
.build())
|
|
||||||
key = generator.generateKey()
|
|
||||||
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putInt(SHARED_KEY_ANDROID_VERSION_WHEN_KEY_HAS_BEEN_GENERATED, Build.VERSION.SDK_INT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
key = readKeyApiL(sharedPreferences, keyStore)
|
|
||||||
|
|
||||||
if (key == null) {
|
|
||||||
Timber.i(TAG, "Generating RSA key pair with keystore")
|
|
||||||
val generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE_PROVIDER)
|
|
||||||
val start = Calendar.getInstance()
|
|
||||||
val end = Calendar.getInstance()
|
|
||||||
end.add(Calendar.YEAR, 10)
|
|
||||||
|
|
||||||
generator.initialize(
|
|
||||||
KeyPairGeneratorSpec.Builder(context)
|
|
||||||
.setAlgorithmParameterSpec(RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
|
|
||||||
.setAlias(RSA_WRAP_LOCAL_PROTECTION_KEY_ALIAS)
|
|
||||||
.setSubject(X500Principal("CN=matrix-android-sdk"))
|
|
||||||
.setStartDate(start.time)
|
|
||||||
.setEndDate(end.time)
|
|
||||||
.setSerialNumber(BigInteger.ONE)
|
|
||||||
.build())
|
|
||||||
val keyPair = generator.generateKeyPair()
|
|
||||||
|
|
||||||
Timber.i(TAG, "Generating wrapped AES key")
|
|
||||||
|
|
||||||
val aesKeyRaw = ByteArray(AES_GCM_KEY_SIZE_IN_BITS / java.lang.Byte.SIZE)
|
|
||||||
prng.nextBytes(aesKeyRaw)
|
|
||||||
key = SecretKeySpec(aesKeyRaw, "AES")
|
|
||||||
|
|
||||||
val cipher = Cipher.getInstance(RSA_WRAP_CIPHER_TYPE)
|
|
||||||
cipher.init(Cipher.WRAP_MODE, keyPair.public)
|
|
||||||
val wrappedAesKey = cipher.wrap(key)
|
|
||||||
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putString(AES_WRAPPED_PROTECTION_KEY_SHARED_PREFERENCE, Base64.encodeToString(wrappedAesKey, 0))
|
|
||||||
putInt(SHARED_KEY_ANDROID_VERSION_WHEN_KEY_HAS_BEEN_GENERATED, Build.VERSION.SDK_INT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sSecretKeyAndVersion = SecretKeyAndVersion(key!!, androidVersionWhenTheKeyHasBeenGenerated)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sSecretKeyAndVersion!!
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the key, which may have been stored when the OS was < M
|
|
||||||
*
|
|
||||||
* @param sharedPreferences shared pref
|
|
||||||
* @param keyStore key store
|
|
||||||
* @return the key if it exists or null
|
|
||||||
*/
|
|
||||||
@Throws(KeyStoreException::class,
|
|
||||||
NoSuchPaddingException::class,
|
|
||||||
NoSuchAlgorithmException::class,
|
|
||||||
InvalidKeyException::class,
|
|
||||||
UnrecoverableKeyException::class)
|
|
||||||
private fun readKeyApiL(sharedPreferences: SharedPreferences, keyStore: KeyStore): SecretKey? {
|
|
||||||
val wrappedAesKeyString = sharedPreferences.getString(AES_WRAPPED_PROTECTION_KEY_SHARED_PREFERENCE, null)
|
|
||||||
if (wrappedAesKeyString != null && keyStore.containsAlias(RSA_WRAP_LOCAL_PROTECTION_KEY_ALIAS)) {
|
|
||||||
Timber.i(TAG, "RSA + wrapped AES local protection keys found in keystore")
|
|
||||||
val privateKey = keyStore.getKey(RSA_WRAP_LOCAL_PROTECTION_KEY_ALIAS, null) as PrivateKey
|
|
||||||
val wrappedAesKey = Base64.decode(wrappedAesKeyString, 0)
|
|
||||||
val cipher = Cipher.getInstance(RSA_WRAP_CIPHER_TYPE)
|
|
||||||
cipher.init(Cipher.UNWRAP_MODE, privateKey)
|
|
||||||
return cipher.unwrap(wrappedAesKey, "AES", Cipher.SECRET_KEY) as SecretKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key does not exist
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a CipherOutputStream instance.
|
|
||||||
* Before Kitkat, this method will return out as local storage encryption is not implemented for
|
|
||||||
* devices before KitKat.
|
|
||||||
*
|
|
||||||
* @param out the output stream
|
|
||||||
* @param context the context holding the application shared preferences
|
|
||||||
*/
|
|
||||||
@Throws(IOException::class,
|
|
||||||
CertificateException::class,
|
|
||||||
NoSuchAlgorithmException::class,
|
|
||||||
UnrecoverableKeyException::class,
|
|
||||||
InvalidKeyException::class,
|
|
||||||
InvalidAlgorithmParameterException::class,
|
|
||||||
NoSuchPaddingException::class,
|
|
||||||
NoSuchProviderException::class,
|
|
||||||
KeyStoreException::class,
|
|
||||||
IllegalBlockSizeException::class)
|
|
||||||
fun createCipherOutputStream(out: OutputStream, context: Context): OutputStream? {
|
|
||||||
val keyAndVersion = getAesGcmLocalProtectionKey(context)
|
|
||||||
|
|
||||||
val cipher = Cipher.getInstance(AES_GCM_CIPHER_TYPE)
|
|
||||||
val iv: ByteArray
|
|
||||||
|
|
||||||
if (keyAndVersion.androidVersionWhenTheKeyHasBeenGenerated >= Build.VERSION_CODES.M) {
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, keyAndVersion.secretKey)
|
|
||||||
iv = cipher.iv
|
|
||||||
} else {
|
|
||||||
iv = ByteArray(AES_GCM_IV_LENGTH)
|
|
||||||
prng.nextBytes(iv)
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, keyAndVersion.secretKey, IvParameterSpec(iv))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iv.size != AES_GCM_IV_LENGTH) {
|
|
||||||
Timber.e(TAG, "Invalid IV length ${iv.size}")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
out.write(iv.size)
|
|
||||||
out.write(iv)
|
|
||||||
|
|
||||||
return CipherOutputStream(out, cipher)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a CipherInputStream instance.
|
|
||||||
* Warning, if inputStream is not an encrypted stream, it's up to the caller to close and reopen inputStream, because the stream has been read.
|
|
||||||
*
|
|
||||||
* @param inputStream the input stream
|
|
||||||
* @param context the context holding the application shared preferences
|
|
||||||
* @return inputStream, or the created InputStream, or null if the InputStream inputStream does not contain encrypted data
|
|
||||||
*/
|
|
||||||
@Throws(NoSuchPaddingException::class,
|
|
||||||
NoSuchAlgorithmException::class,
|
|
||||||
CertificateException::class,
|
|
||||||
InvalidKeyException::class,
|
|
||||||
KeyStoreException::class,
|
|
||||||
UnrecoverableKeyException::class,
|
|
||||||
IllegalBlockSizeException::class,
|
|
||||||
NoSuchProviderException::class,
|
|
||||||
InvalidAlgorithmParameterException::class,
|
|
||||||
IOException::class)
|
|
||||||
fun createCipherInputStream(inputStream: InputStream, context: Context): InputStream? {
|
|
||||||
val ivLen = inputStream.read()
|
|
||||||
if (ivLen != AES_GCM_IV_LENGTH) {
|
|
||||||
Timber.e(TAG, "Invalid IV length $ivLen")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val iv = ByteArray(AES_GCM_IV_LENGTH)
|
|
||||||
inputStream.read(iv)
|
|
||||||
|
|
||||||
val cipher = Cipher.getInstance(AES_GCM_CIPHER_TYPE)
|
|
||||||
|
|
||||||
val keyAndVersion = getAesGcmLocalProtectionKey(context)
|
|
||||||
|
|
||||||
val spec: AlgorithmParameterSpec = if (keyAndVersion.androidVersionWhenTheKeyHasBeenGenerated >= Build.VERSION_CODES.M) {
|
|
||||||
GCMParameterSpec(AES_GCM_KEY_SIZE_IN_BITS, iv)
|
|
||||||
} else {
|
|
||||||
IvParameterSpec(iv)
|
|
||||||
}
|
|
||||||
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, keyAndVersion.secretKey, spec)
|
|
||||||
|
|
||||||
return CipherInputStream(inputStream, cipher)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* 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 org.matrix.android.sdk.internal.util
|
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MediatorLiveData
|
|
||||||
|
|
||||||
object LiveDataUtils {
|
|
||||||
|
|
||||||
fun <FIRST, SECOND, OUT> combine(firstSource: LiveData<FIRST>,
|
|
||||||
secondSource: LiveData<SECOND>,
|
|
||||||
mapper: (FIRST, SECOND) -> OUT): LiveData<OUT> {
|
|
||||||
return MediatorLiveData<OUT>().apply {
|
|
||||||
var firstValue: FIRST? = null
|
|
||||||
var secondValue: SECOND? = null
|
|
||||||
|
|
||||||
val valueDispatcher = {
|
|
||||||
firstValue?.let { safeFirst ->
|
|
||||||
secondValue?.let { safeSecond ->
|
|
||||||
val mappedValue = mapper(safeFirst, safeSecond)
|
|
||||||
postValue(mappedValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addSource(firstSource) {
|
|
||||||
firstValue = it
|
|
||||||
valueDispatcher()
|
|
||||||
}
|
|
||||||
|
|
||||||
addSource(secondSource) {
|
|
||||||
secondValue = it
|
|
||||||
valueDispatcher()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue