From c2a8e13eef1bdf4ce7a5ebb327b655cddbab5d98 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 16 Oct 2023 21:59:41 +0200 Subject: [PATCH 01/11] Pin code storage : add way to store the pin --- .../impl/storage/EncryptedPinCodeStorage.kt | 43 ++++++++ .../features/pin/impl/storage/PinCodeStore.kt | 52 +++++++++ .../storage/SharedPreferencesPinCodeStore.kt | 100 ++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/EncryptedPinCodeStorage.kt create mode 100644 features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/PinCodeStore.kt create mode 100644 features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/SharedPreferencesPinCodeStore.kt diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/EncryptedPinCodeStorage.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/EncryptedPinCodeStorage.kt new file mode 100644 index 0000000000..ae3bd7b893 --- /dev/null +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/EncryptedPinCodeStorage.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.pin.impl.storage + +/** + * Should be implemented by any class that provides access to the encrypted PIN code. + * All methods are suspending in case there are async IO operations involved. + */ +interface EncryptedPinCodeStorage { + /** + * Returns the encrypted PIN code. + */ + suspend fun getPinCode(): String? + + /** + * Saves the encrypted PIN code to some persistable storage. + */ + suspend fun savePinCode(pinCode: String) + + /** + * Deletes the PIN code from some persistable storage. + */ + suspend fun deletePinCode() + + /** + * Returns whether the PIN code is stored or not. + */ + suspend fun hasPinCode(): Boolean +} diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/PinCodeStore.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/PinCodeStore.kt new file mode 100644 index 0000000000..5c54cc26f9 --- /dev/null +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/PinCodeStore.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.pin.impl.storage + +interface PinCodeStore : EncryptedPinCodeStorage { + + interface Listener { + fun onPinSetUpChange(isConfigured: Boolean) + } + + /** + * Returns the remaining PIN code attempts. When this reaches 0 the PIN code access won't be available for some time. + */ + suspend fun getRemainingPinCodeAttemptsNumber(): Int + + /** + * Should decrement the number of remaining PIN code attempts. + * @return The remaining attempts. + */ + suspend fun onWrongPin(): Int + + /** + * Resets the counter of attempts for PIN code and biometric access. + */ + suspend fun resetCounter() + + /** + * Adds a listener to be notified when the PIN code us created or removed. + */ + fun addListener(listener: Listener) + + /** + * Removes a listener to be notified when the PIN code us created or removed. + */ + fun removeListener(listener: Listener) +} + + diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/SharedPreferencesPinCodeStore.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/SharedPreferencesPinCodeStore.kt new file mode 100644 index 0000000000..fc8155352f --- /dev/null +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/SharedPreferencesPinCodeStore.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.pin.impl.storage + +import android.content.SharedPreferences +import androidx.core.content.edit +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SingleIn +import kotlinx.coroutines.withContext +import java.util.concurrent.CopyOnWriteArrayList +import javax.inject.Inject + +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) +class SharedPreferencesPinCodeStore @Inject constructor( + private val dispatchers: CoroutineDispatchers, + private val sharedPreferences: SharedPreferences, +) : PinCodeStore { + + private val listeners = CopyOnWriteArrayList() + + override suspend fun getPinCode(): String? = withContext(dispatchers.io) { + sharedPreferences.getString(ENCODED_PIN_CODE_KEY, null) + } + + override suspend fun savePinCode(pinCode: String) = withContext(dispatchers.io) { + sharedPreferences.edit { + putString(ENCODED_PIN_CODE_KEY, pinCode) + } + withContext(dispatchers.main) { + listeners.forEach { it.onPinSetUpChange(isConfigured = true) } + } + } + + override suspend fun deletePinCode() = withContext(dispatchers.io) { + // Also reset the counters + resetCounter() + sharedPreferences.edit { + remove(ENCODED_PIN_CODE_KEY) + } + withContext(dispatchers.main) { + listeners.forEach { it.onPinSetUpChange(isConfigured = false) } + } + } + + override suspend fun hasPinCode(): Boolean = withContext(dispatchers.io) { + sharedPreferences.contains(ENCODED_PIN_CODE_KEY) + } + + override suspend fun getRemainingPinCodeAttemptsNumber(): Int = withContext(dispatchers.io) { + sharedPreferences.getInt(REMAINING_PIN_CODE_ATTEMPTS_KEY, MAX_PIN_CODE_ATTEMPTS_NUMBER_BEFORE_LOGOUT) + } + + override suspend fun onWrongPin(): Int = withContext(dispatchers.io) { + val remaining = getRemainingPinCodeAttemptsNumber() - 1 + sharedPreferences.edit { + putInt(REMAINING_PIN_CODE_ATTEMPTS_KEY, remaining) + } + remaining + } + + override suspend fun resetCounter() = withContext(dispatchers.io) { + sharedPreferences.edit { + remove(REMAINING_PIN_CODE_ATTEMPTS_KEY) + remove(REMAINING_BIOMETRICS_ATTEMPTS_KEY) + } + } + + override fun addListener(listener: PinCodeStore.Listener) { + listeners.add(listener) + } + + override fun removeListener(listener: PinCodeStore.Listener) { + listeners.remove(listener) + } + + companion object { + private const val ENCODED_PIN_CODE_KEY = "ENCODED_PIN_CODE_KEY" + private const val REMAINING_PIN_CODE_ATTEMPTS_KEY = "REMAINING_PIN_CODE_ATTEMPTS_KEY" + private const val REMAINING_BIOMETRICS_ATTEMPTS_KEY = "REMAINING_BIOMETRICS_ATTEMPTS_KEY" + + private const val MAX_PIN_CODE_ATTEMPTS_NUMBER_BEFORE_LOGOUT = 3 + } +} From 85102e379378006b765915ffe4febf7db06e3c17 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 17 Oct 2023 14:04:57 +0200 Subject: [PATCH 02/11] Crypto: add a small cryptography library module with CipherFactory --- gradle/libs.versions.toml | 10 +++ libraries/cryptography/api/build.gradle.kts | 23 ++++++ .../cryptography/api/CipherFactory.kt | 40 ++++++++++ libraries/cryptography/impl/build.gradle.kts | 35 +++++++++ .../impl/KeyStoreCipherFactory.kt | 76 +++++++++++++++++++ settings.gradle.kts | 2 + 6 files changed, 186 insertions(+) create mode 100644 libraries/cryptography/api/build.gradle.kts create mode 100644 libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CipherFactory.kt create mode 100644 libraries/cryptography/impl/build.gradle.kts create mode 100644 libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreCipherFactory.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7071a98879..15cf29dd4f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -57,6 +57,11 @@ autoservice = "1.1.1" # quality detekt = "1.23.1" dependencygraph = "0.12" +junit = "4.13.2" +androidx-test-ext-junit = "1.1.5" +espresso-core = "3.5.1" +appcompat = "1.6.1" +material = "1.9.0" [libraries] # Project @@ -184,6 +189,11 @@ google_autoservice_annotations = { module = "com.google.auto.service:auto-servic # value of `composecompiler` (which is used to set composeOptions.kotlinCompilerExtensionVersion. # See https://github.com/renovatebot/renovate/issues/18354 android_composeCompiler = { module = "androidx.compose.compiler:compiler", version.ref = "composecompiler" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } [bundles] diff --git a/libraries/cryptography/api/build.gradle.kts b/libraries/cryptography/api/build.gradle.kts new file mode 100644 index 0000000000..e8cee5dbd6 --- /dev/null +++ b/libraries/cryptography/api/build.gradle.kts @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 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. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.cryptography.api" +} diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CipherFactory.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CipherFactory.kt new file mode 100644 index 0000000000..0eec6e9fa9 --- /dev/null +++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CipherFactory.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.cryptography.api + +import javax.crypto.Cipher + +/** + * Factory to create [Cipher] instances for encryption and decryption. + * The implementation should use a secure way to store the keys. + */ +interface CipherFactory { + /** + * Create a [Cipher] instance for encryption. + * @param alias the alias of the key used for encryption. + * @return the [Cipher] instance. + */ + fun createEncryptionCipher(alias: String): Cipher + + /** + * Create a [Cipher] instance for decryption. + * @param alias the alias of the key used for encryption. + * @param initializationVector the initialization vector used for encryption. + * @return the [Cipher] instance. + */ + fun createDecryptionCipher(alias: String, initializationVector: ByteArray): Cipher +} diff --git a/libraries/cryptography/impl/build.gradle.kts b/libraries/cryptography/impl/build.gradle.kts new file mode 100644 index 0000000000..fa6f9db7ea --- /dev/null +++ b/libraries/cryptography/impl/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 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. + */ + +plugins { + id("io.element.android-library") + alias(libs.plugins.anvil) +} + +android { + namespace = "io.element.android.libraries.cryptography.impl" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + anvil(projects.anvilcodegen) + implementation(projects.anvilannotations) + implementation(projects.libraries.di) + implementation(projects.libraries.cryptography.api) +} diff --git a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreCipherFactory.kt b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreCipherFactory.kt new file mode 100644 index 0000000000..eb88b66239 --- /dev/null +++ b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreCipherFactory.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.cryptography.impl + +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.cryptography.api.CipherFactory +import io.element.android.libraries.di.AppScope +import java.security.KeyStore +import javax.crypto.Cipher +import javax.crypto.KeyGenerator +import javax.crypto.SecretKey +import javax.crypto.spec.GCMParameterSpec +import javax.inject.Inject + +private const val ANDROID_KEYSTORE = "AndroidKeyStore" +private const val ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_GCM +private const val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_NONE +private const val ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES +private const val ENCRYPTION_AES_TRANSFORMATION = "$ENCRYPTION_ALGORITHM/$ENCRYPTION_BLOCK_MODE/$ENCRYPTION_PADDING" + +/** + * Implementation of [CipherFactory] that uses the Android Keystore to store the keys. + */ +@ContributesBinding(AppScope::class) +class KeyStoreCipherFactory @Inject constructor() : CipherFactory { + + override fun createEncryptionCipher(alias: String): Cipher { + val cipher = Cipher.getInstance(ENCRYPTION_AES_TRANSFORMATION) + val secretKey = getOrGenerateKeyForAlias(alias) + cipher.init(Cipher.ENCRYPT_MODE, secretKey) + return cipher + } + + override fun createDecryptionCipher(alias: String, initializationVector: ByteArray): Cipher { + val cipher = Cipher.getInstance(ENCRYPTION_AES_TRANSFORMATION) + val secretKey = getOrGenerateKeyForAlias(alias) + val spec = GCMParameterSpec(128, initializationVector) + cipher.init(Cipher.DECRYPT_MODE, secretKey, spec) + return cipher + } + + private fun getOrGenerateKeyForAlias(alias: String): SecretKey { + val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE) + val secretKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.SecretKeyEntry) + ?.secretKey + return if (secretKeyEntry == null) { + val generator = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM, ANDROID_KEYSTORE) + val keyGenSpec = KeyGenParameterSpec.Builder( + alias, + KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT + ) + .setBlockModes(ENCRYPTION_BLOCK_MODE) + .setEncryptionPaddings(ENCRYPTION_PADDING) + .setKeySize(128) + .build() + generator.init(keyGenSpec) + generator.generateKey() + } else secretKeyEntry + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 105befcd04..590089adc6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,7 @@ import java.net.URI +include(":libraries:cryptography:api") + /* * Copyright (c) 2022 New Vector Ltd * From 493d67c8caeb2a20af8ee168562cc6760f8c732d Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 17 Oct 2023 18:28:07 +0200 Subject: [PATCH 03/11] Introduce CryptoService for simple cryptographic operations --- .../cryptography/api/CipherFactory.kt | 40 ------------- .../cryptography/api/CryptoService.kt | 23 ++++---- .../cryptography/api/EncryptionResult.kt | 56 +++++++++++++++++++ ...pherFactory.kt => DefaultCryptoService.kt} | 49 +++++++++------- 4 files changed, 96 insertions(+), 72 deletions(-) delete mode 100644 libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CipherFactory.kt rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationStateProvider.kt => libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CryptoService.kt (52%) create mode 100644 libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt rename libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/{KeyStoreCipherFactory.kt => DefaultCryptoService.kt} (66%) diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CipherFactory.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CipherFactory.kt deleted file mode 100644 index 0eec6e9fa9..0000000000 --- a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CipherFactory.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.cryptography.api - -import javax.crypto.Cipher - -/** - * Factory to create [Cipher] instances for encryption and decryption. - * The implementation should use a secure way to store the keys. - */ -interface CipherFactory { - /** - * Create a [Cipher] instance for encryption. - * @param alias the alias of the key used for encryption. - * @return the [Cipher] instance. - */ - fun createEncryptionCipher(alias: String): Cipher - - /** - * Create a [Cipher] instance for decryption. - * @param alias the alias of the key used for encryption. - * @param initializationVector the initialization vector used for encryption. - * @return the [Cipher] instance. - */ - fun createDecryptionCipher(alias: String, initializationVector: ByteArray): Cipher -} diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationStateProvider.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CryptoService.kt similarity index 52% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationStateProvider.kt rename to libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CryptoService.kt index 8e3f45ac07..a05e11da2a 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationStateProvider.kt +++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CryptoService.kt @@ -14,17 +14,18 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.auth +package io.element.android.libraries.cryptography.api -import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import javax.crypto.Cipher +import javax.crypto.SecretKey -open class PinAuthenticationStateProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - aPinAuthenticationState(), - ) +/** + * Simple service to provide cryptographic operations. + */ +interface CryptoService { + fun getOrCreateSecretKey(alias: String): SecretKey + fun createEncryptionCipher(key: SecretKey): Cipher + fun createDecryptionCipher(key: SecretKey, initializationVector: ByteArray): Cipher + fun encrypt(key: SecretKey, input: ByteArray): EncryptionResult + fun decrypt(key: SecretKey, encryptionResult: EncryptionResult): ByteArray } - -fun aPinAuthenticationState() = PinAuthenticationState( - eventSink = {} -) diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt new file mode 100644 index 0000000000..10affcfdcb --- /dev/null +++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.cryptography.api + +import android.util.Base64 +import java.nio.ByteBuffer + +/** + * Holds the result of an encryption operation. + */ +class EncryptionResult( + val encryptedByteArray: ByteArray, + val initializationVector: ByteArray +) { + fun toBase64(): String { + val initializationVectorSize = ByteBuffer.allocate(Int.SIZE_BYTES).putInt(initializationVector.size).array() + val cipherTextWithIv: ByteArray = + ByteBuffer.allocate(Int.SIZE_BYTES + initializationVector.size + encryptedByteArray.size) + .put(initializationVectorSize) + .put(initializationVector) + .put(encryptedByteArray) + .array() + return Base64.encodeToString(cipherTextWithIv, Base64.NO_WRAP) + } + + companion object { + /** + * @param base64 the base64 representation of the encrypted data. + * @return the [EncryptionResult] from the base64 representation. + */ + fun fromBase64(base64: String): EncryptionResult { + val cipherTextWithIv = Base64.decode(base64, Base64.NO_WRAP) + val buffer = ByteBuffer.wrap(cipherTextWithIv) + val initializationVectorSize = buffer.int + val initializationVector = ByteArray(initializationVectorSize) + buffer.get(initializationVector) + val encryptedByteArray = ByteArray(buffer.remaining()) + buffer.get(encryptedByteArray) + return EncryptionResult(encryptedByteArray, initializationVector) + } + } +} diff --git a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreCipherFactory.kt b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/DefaultCryptoService.kt similarity index 66% rename from libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreCipherFactory.kt rename to libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/DefaultCryptoService.kt index eb88b66239..dc4f771acf 100644 --- a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreCipherFactory.kt +++ b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/DefaultCryptoService.kt @@ -19,7 +19,8 @@ package io.element.android.libraries.cryptography.impl import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.libraries.cryptography.api.CipherFactory +import io.element.android.libraries.cryptography.api.CryptoService +import io.element.android.libraries.cryptography.api.EncryptionResult import io.element.android.libraries.di.AppScope import java.security.KeyStore import javax.crypto.Cipher @@ -34,28 +35,10 @@ private const val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_NONE private const val ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES private const val ENCRYPTION_AES_TRANSFORMATION = "$ENCRYPTION_ALGORITHM/$ENCRYPTION_BLOCK_MODE/$ENCRYPTION_PADDING" -/** - * Implementation of [CipherFactory] that uses the Android Keystore to store the keys. - */ @ContributesBinding(AppScope::class) -class KeyStoreCipherFactory @Inject constructor() : CipherFactory { - - override fun createEncryptionCipher(alias: String): Cipher { - val cipher = Cipher.getInstance(ENCRYPTION_AES_TRANSFORMATION) - val secretKey = getOrGenerateKeyForAlias(alias) - cipher.init(Cipher.ENCRYPT_MODE, secretKey) - return cipher - } - - override fun createDecryptionCipher(alias: String, initializationVector: ByteArray): Cipher { - val cipher = Cipher.getInstance(ENCRYPTION_AES_TRANSFORMATION) - val secretKey = getOrGenerateKeyForAlias(alias) - val spec = GCMParameterSpec(128, initializationVector) - cipher.init(Cipher.DECRYPT_MODE, secretKey, spec) - return cipher - } +class DefaultCryptoService @Inject constructor() : CryptoService { - private fun getOrGenerateKeyForAlias(alias: String): SecretKey { + override fun getOrCreateSecretKey(alias: String): SecretKey { val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE) val secretKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.SecretKeyEntry) ?.secretKey @@ -73,4 +56,28 @@ class KeyStoreCipherFactory @Inject constructor() : CipherFactory { generator.generateKey() } else secretKeyEntry } + + override fun createEncryptionCipher(key: SecretKey): Cipher { + return Cipher.getInstance(ENCRYPTION_AES_TRANSFORMATION).apply { + init(Cipher.ENCRYPT_MODE, key) + } + } + + override fun createDecryptionCipher(key: SecretKey, initializationVector: ByteArray): Cipher { + val spec = GCMParameterSpec(128, initializationVector) + return Cipher.getInstance(ENCRYPTION_AES_TRANSFORMATION).apply { + init(Cipher.DECRYPT_MODE, key, spec) + } + } + + override fun encrypt(key: SecretKey, input: ByteArray): EncryptionResult { + val cipher = createEncryptionCipher(key) + val encryptedData = cipher.doFinal(input) + return EncryptionResult(encryptedData, cipher.iv) + } + + override fun decrypt(key: SecretKey, encryptionResult: EncryptionResult): ByteArray { + val cipher = createDecryptionCipher(key, encryptionResult.initializationVector) + return cipher.doFinal(encryptionResult.encryptedByteArray) + } } From 981430731d36b70fdc4c0d40e25f579011bdc45d Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 17 Oct 2023 18:28:39 +0200 Subject: [PATCH 04/11] Pin : move some classes around and introduce PinCodeManager --- features/pin/impl/build.gradle.kts | 1 + .../pin/impl/pin/DefaultPinCodeManager.kt | 70 +++++++++++++++++++ .../features/pin/impl/pin/PinCodeManager.kt | 59 ++++++++++++++++ .../storage/EncryptedPinCodeStorage.kt | 8 +-- .../impl/{ => pin}/storage/PinCodeStore.kt | 2 +- .../storage/SharedPreferencesPinCodeStore.kt | 21 +++--- .../DefaultPinEntryPoint.kt | 2 +- .../impl/{ => presentation}/PinFlowNode.kt | 6 +- .../auth/PinAuthenticationEvents.kt | 2 +- .../auth/PinAuthenticationNode.kt | 2 +- .../auth/PinAuthenticationPresenter.kt | 2 +- .../auth/PinAuthenticationState.kt | 2 +- .../auth/PinAuthenticationStateProvider.kt | 30 ++++++++ .../auth/PinAuthenticationView.kt | 2 +- .../create/CreatePinEvents.kt | 2 +- .../create/CreatePinNode.kt | 2 +- .../create/CreatePinPresenter.kt | 2 +- .../create/CreatePinState.kt | 2 +- .../create/CreatePinStateProvider.kt | 2 +- .../create/CreatePinView.kt | 2 +- 20 files changed, 188 insertions(+), 33 deletions(-) create mode 100644 features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/DefaultPinCodeManager.kt create mode 100644 features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/PinCodeManager.kt rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => pin}/storage/EncryptedPinCodeStorage.kt (85%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => pin}/storage/PinCodeStore.kt (96%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => pin}/storage/SharedPreferencesPinCodeStore.kt (80%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/DefaultPinEntryPoint.kt (95%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/PinFlowNode.kt (91%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/auth/PinAuthenticationEvents.kt (91%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/auth/PinAuthenticationNode.kt (95%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/auth/PinAuthenticationPresenter.kt (95%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/auth/PinAuthenticationState.kt (91%) create mode 100644 features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationStateProvider.kt rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/auth/PinAuthenticationView.kt (97%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/create/CreatePinEvents.kt (91%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/create/CreatePinNode.kt (95%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/create/CreatePinPresenter.kt (94%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/create/CreatePinState.kt (91%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/create/CreatePinStateProvider.kt (93%) rename features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/{ => presentation}/create/CreatePinView.kt (96%) diff --git a/features/pin/impl/build.gradle.kts b/features/pin/impl/build.gradle.kts index 6cfc9fce11..e734035ab3 100644 --- a/features/pin/impl/build.gradle.kts +++ b/features/pin/impl/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.featureflag.api) + implementation(projects.libraries.cryptography.api) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/DefaultPinCodeManager.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/DefaultPinCodeManager.kt new file mode 100644 index 0000000000..4ee1a530a2 --- /dev/null +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/DefaultPinCodeManager.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.pin.impl.pin + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.pin.impl.pin.storage.PinCodeStore +import io.element.android.libraries.cryptography.api.CryptoService +import io.element.android.libraries.cryptography.api.EncryptionResult +import io.element.android.libraries.di.AppScope +import javax.inject.Inject + +private const val SECRET_KEY_ALIAS = "SECRET_KEY_ALIAS_PIN_CODE" + +@ContributesBinding(AppScope::class) +class DefaultPinCodeManager @Inject constructor( + private val cryptoService: CryptoService, + private val pinCodeStore: PinCodeStore, +) : PinCodeManager { + + override suspend fun isPinCodeAvailable(): Boolean { + return pinCodeStore.hasPinCode() + } + + override suspend fun createPinCode(pinCode: String) { + val secretKey = cryptoService.getOrCreateSecretKey(SECRET_KEY_ALIAS) + val encryptedPinCode = cryptoService.encrypt(secretKey, pinCode.toByteArray()).toBase64() + pinCodeStore.saveEncryptedPinCode(encryptedPinCode) + } + + override suspend fun verifyPinCode(pinCode: String): Boolean { + val encryptedPinCode = pinCodeStore.getEncryptedCode() ?: return false + return try { + val secretKey = cryptoService.getOrCreateSecretKey(SECRET_KEY_ALIAS) + val decryptedPinCode = cryptoService.decrypt(secretKey, EncryptionResult.fromBase64(encryptedPinCode)) + decryptedPinCode.contentEquals(pinCode.toByteArray()) + } catch (failure: Throwable) { + false + } + } + + override suspend fun deletePinCode() { + pinCodeStore.deleteEncryptedPinCode() + } + + override suspend fun getRemainingPinCodeAttemptsNumber(): Int { + return pinCodeStore.getRemainingPinCodeAttemptsNumber() + } + + override suspend fun onWrongPin(): Int { + return pinCodeStore.onWrongPin() + } + + override suspend fun resetCounter() { + pinCodeStore.resetCounter() + } +} diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/PinCodeManager.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/PinCodeManager.kt new file mode 100644 index 0000000000..df4262d9eb --- /dev/null +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/PinCodeManager.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.pin.impl.pin + +/** + * This interface is the main interface to manage the pin code. + * Implementation should take care of encrypting the pin code and storing it. + */ +interface PinCodeManager { + /** + * @return true if a pin code is available. + */ + suspend fun isPinCodeAvailable(): Boolean + + /** + * creates a new encrypted pin code. + * @param pinCode the clear pin code to create + */ + suspend fun createPinCode(pinCode: String) + + /** + * @return true if the pin code is correct + */ + suspend fun verifyPinCode(pinCode: String): Boolean + + /** + * deletes the previously created pin code + */ + suspend fun deletePinCode() + + /** + * @return the number of remaining attempts before the pin code is blocked + */ + suspend fun getRemainingPinCodeAttemptsNumber(): Int + + /** + * @return the number of remaining attempts before the pin code is blocked + */ + suspend fun onWrongPin(): Int + + /** + * Resets the counter of attempts for PIN code. + */ + suspend fun resetCounter() +} diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/EncryptedPinCodeStorage.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/EncryptedPinCodeStorage.kt similarity index 85% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/EncryptedPinCodeStorage.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/EncryptedPinCodeStorage.kt index ae3bd7b893..bf1dec8d0d 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/EncryptedPinCodeStorage.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/EncryptedPinCodeStorage.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.storage +package io.element.android.features.pin.impl.pin.storage /** * Should be implemented by any class that provides access to the encrypted PIN code. @@ -24,17 +24,17 @@ interface EncryptedPinCodeStorage { /** * Returns the encrypted PIN code. */ - suspend fun getPinCode(): String? + suspend fun getEncryptedCode(): String? /** * Saves the encrypted PIN code to some persistable storage. */ - suspend fun savePinCode(pinCode: String) + suspend fun saveEncryptedPinCode(pinCode: String) /** * Deletes the PIN code from some persistable storage. */ - suspend fun deletePinCode() + suspend fun deleteEncryptedPinCode() /** * Returns whether the PIN code is stored or not. diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/PinCodeStore.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/PinCodeStore.kt similarity index 96% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/PinCodeStore.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/PinCodeStore.kt index 5c54cc26f9..3476890275 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/PinCodeStore.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/PinCodeStore.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.storage +package io.element.android.features.pin.impl.pin.storage interface PinCodeStore : EncryptedPinCodeStorage { diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/SharedPreferencesPinCodeStore.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/SharedPreferencesPinCodeStore.kt similarity index 80% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/SharedPreferencesPinCodeStore.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/SharedPreferencesPinCodeStore.kt index fc8155352f..892336f01d 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/storage/SharedPreferencesPinCodeStore.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/SharedPreferencesPinCodeStore.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.storage +package io.element.android.features.pin.impl.pin.storage import android.content.SharedPreferences import androidx.core.content.edit @@ -26,6 +26,10 @@ import kotlinx.coroutines.withContext import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject +private const val ENCODED_PIN_CODE_KEY = "ENCODED_PIN_CODE_KEY" +private const val REMAINING_PIN_CODE_ATTEMPTS_KEY = "REMAINING_PIN_CODE_ATTEMPTS_KEY" +private const val MAX_PIN_CODE_ATTEMPTS_NUMBER_BEFORE_LOGOUT = 3 + @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) class SharedPreferencesPinCodeStore @Inject constructor( @@ -35,11 +39,11 @@ class SharedPreferencesPinCodeStore @Inject constructor( private val listeners = CopyOnWriteArrayList() - override suspend fun getPinCode(): String? = withContext(dispatchers.io) { + override suspend fun getEncryptedCode(): String? = withContext(dispatchers.io) { sharedPreferences.getString(ENCODED_PIN_CODE_KEY, null) } - override suspend fun savePinCode(pinCode: String) = withContext(dispatchers.io) { + override suspend fun saveEncryptedPinCode(pinCode: String) = withContext(dispatchers.io) { sharedPreferences.edit { putString(ENCODED_PIN_CODE_KEY, pinCode) } @@ -48,7 +52,7 @@ class SharedPreferencesPinCodeStore @Inject constructor( } } - override suspend fun deletePinCode() = withContext(dispatchers.io) { + override suspend fun deleteEncryptedPinCode() = withContext(dispatchers.io) { // Also reset the counters resetCounter() sharedPreferences.edit { @@ -78,7 +82,6 @@ class SharedPreferencesPinCodeStore @Inject constructor( override suspend fun resetCounter() = withContext(dispatchers.io) { sharedPreferences.edit { remove(REMAINING_PIN_CODE_ATTEMPTS_KEY) - remove(REMAINING_BIOMETRICS_ATTEMPTS_KEY) } } @@ -89,12 +92,4 @@ class SharedPreferencesPinCodeStore @Inject constructor( override fun removeListener(listener: PinCodeStore.Listener) { listeners.remove(listener) } - - companion object { - private const val ENCODED_PIN_CODE_KEY = "ENCODED_PIN_CODE_KEY" - private const val REMAINING_PIN_CODE_ATTEMPTS_KEY = "REMAINING_PIN_CODE_ATTEMPTS_KEY" - private const val REMAINING_BIOMETRICS_ATTEMPTS_KEY = "REMAINING_BIOMETRICS_ATTEMPTS_KEY" - - private const val MAX_PIN_CODE_ATTEMPTS_NUMBER_BEFORE_LOGOUT = 3 - } } diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/DefaultPinEntryPoint.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/DefaultPinEntryPoint.kt similarity index 95% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/DefaultPinEntryPoint.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/DefaultPinEntryPoint.kt index 920691cad2..17b7f6d612 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/DefaultPinEntryPoint.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/DefaultPinEntryPoint.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl +package io.element.android.features.pin.impl.presentation import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/PinFlowNode.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/PinFlowNode.kt similarity index 91% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/PinFlowNode.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/PinFlowNode.kt index a76504ce8a..acf10ffcce 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/PinFlowNode.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/PinFlowNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl +package io.element.android.features.pin.impl.presentation import android.os.Parcelable import androidx.compose.runtime.Composable @@ -27,8 +27,8 @@ import com.bumble.appyx.navmodel.backstack.BackStack import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.features.pin.impl.auth.PinAuthenticationNode -import io.element.android.features.pin.impl.create.CreatePinNode +import io.element.android.features.pin.impl.presentation.auth.PinAuthenticationNode +import io.element.android.features.pin.impl.presentation.create.CreatePinNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationEvents.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationEvents.kt similarity index 91% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationEvents.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationEvents.kt index 110c62660a..01835df18c 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationEvents.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationEvents.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.auth +package io.element.android.features.pin.impl.presentation.auth sealed interface PinAuthenticationEvents { data object Unlock : PinAuthenticationEvents diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationNode.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationNode.kt similarity index 95% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationNode.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationNode.kt index b5dab44c96..ec4b78dc15 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationNode.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.auth +package io.element.android.features.pin.impl.presentation.auth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationPresenter.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationPresenter.kt similarity index 95% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationPresenter.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationPresenter.kt index 5e7e274ba7..bb1e5d04ad 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationPresenter.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationPresenter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.auth +package io.element.android.features.pin.impl.presentation.auth import androidx.compose.runtime.Composable import io.element.android.features.pin.api.PinStateService diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationState.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationState.kt similarity index 91% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationState.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationState.kt index 2df1e50f83..3d319356ec 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationState.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.auth +package io.element.android.features.pin.impl.presentation.auth data class PinAuthenticationState( val eventSink: (PinAuthenticationEvents) -> Unit diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationStateProvider.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationStateProvider.kt new file mode 100644 index 0000000000..3cfbe5cb84 --- /dev/null +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationStateProvider.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.pin.impl.presentation.auth + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider + +open class PinAuthenticationStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aPinAuthenticationState(), + ) +} + +fun aPinAuthenticationState() = PinAuthenticationState( + eventSink = {} +) diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationView.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationView.kt similarity index 97% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationView.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationView.kt index 9fe689bb39..9efab19813 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/auth/PinAuthenticationView.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.auth +package io.element.android.features.pin.impl.presentation.auth import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinEvents.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinEvents.kt similarity index 91% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinEvents.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinEvents.kt index 280856b5c8..87aee27557 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinEvents.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinEvents.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.create +package io.element.android.features.pin.impl.presentation.create sealed interface CreatePinEvents { object MyEvent : CreatePinEvents diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinNode.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinNode.kt similarity index 95% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinNode.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinNode.kt index 0ed0343a5b..6b71510d5a 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinNode.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.create +package io.element.android.features.pin.impl.presentation.create import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinPresenter.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinPresenter.kt similarity index 94% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinPresenter.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinPresenter.kt index d45257b4bd..8cfdc22e46 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinPresenter.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinPresenter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.create +package io.element.android.features.pin.impl.presentation.create import androidx.compose.runtime.Composable import io.element.android.libraries.architecture.Presenter diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinState.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinState.kt similarity index 91% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinState.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinState.kt index c405db82ec..64c0ae0a5a 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinState.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.create +package io.element.android.features.pin.impl.presentation.create data class CreatePinState( val eventSink: (CreatePinEvents) -> Unit diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinStateProvider.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinStateProvider.kt similarity index 93% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinStateProvider.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinStateProvider.kt index 4bff72023e..0afe3442a2 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinStateProvider.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinStateProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.create +package io.element.android.features.pin.impl.presentation.create import androidx.compose.ui.tooling.preview.PreviewParameterProvider diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinView.kt b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinView.kt similarity index 96% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinView.kt rename to features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinView.kt index 64e5be4091..643f487d1b 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/create/CreatePinView.kt +++ b/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.create +package io.element.android.features.pin.impl.presentation.create import androidx.compose.foundation.layout.Box import androidx.compose.material3.MaterialTheme From 84368065715719b926856527c763dca1adcbd71b Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 17 Oct 2023 18:38:30 +0200 Subject: [PATCH 05/11] Pin: rename feature pin to lockscreen --- .../android/appnav/LoggedInFlowNode.kt | 24 +++++++++---------- .../{pin => lockscreen}/api/build.gradle.kts | 2 +- .../lockscreen/api/LockScreenEntryPoint.kt} | 4 ++-- .../lockscreen/api/LockScreenState.kt} | 8 +++---- .../lockscreen/api/LockScreenStateService.kt} | 6 ++--- .../{pin => lockscreen}/impl/build.gradle.kts | 4 ++-- .../impl/DefaultLockScreenEntryPoint.kt} | 8 +++---- .../lockscreen/impl/LockScreenFlowNode.kt} | 10 ++++---- .../impl}/auth/PinAuthenticationEvents.kt | 2 +- .../impl}/auth/PinAuthenticationNode.kt | 2 +- .../impl}/auth/PinAuthenticationPresenter.kt | 6 ++--- .../impl}/auth/PinAuthenticationState.kt | 2 +- .../auth/PinAuthenticationStateProvider.kt | 2 +- .../impl}/auth/PinAuthenticationView.kt | 2 +- .../impl}/create/CreatePinEvents.kt | 2 +- .../lockscreen/impl}/create/CreatePinNode.kt | 2 +- .../impl}/create/CreatePinPresenter.kt | 2 +- .../lockscreen/impl}/create/CreatePinState.kt | 2 +- .../impl}/create/CreatePinStateProvider.kt | 2 +- .../lockscreen/impl}/create/CreatePinView.kt | 2 +- .../impl/pin/DefaultPinCodeManager.kt | 4 ++-- .../lockscreen}/impl/pin/PinCodeManager.kt | 2 +- .../pin/storage/EncryptedPinCodeStorage.kt | 2 +- .../impl/pin/storage/PinCodeStore.kt | 2 +- .../storage/SharedPreferencesPinCodeStore.kt | 2 +- .../state/DefaultLockScreenStateService.kt} | 18 +++++++------- 26 files changed, 62 insertions(+), 62 deletions(-) rename features/{pin => lockscreen}/api/build.gradle.kts (92%) rename features/{pin/api/src/main/kotlin/io/element/android/features/pin/api/PinEntryPoint.kt => lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt} (86%) rename features/{pin/api/src/main/kotlin/io/element/android/features/pin/api/PinState.kt => lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenState.kt} (78%) rename features/{pin/api/src/main/kotlin/io/element/android/features/pin/api/PinStateService.kt => lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenStateService.kt} (85%) rename features/{pin => lockscreen}/impl/build.gradle.kts (93%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/DefaultPinEntryPoint.kt => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt} (78%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/PinFlowNode.kt => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt} (88%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/auth/PinAuthenticationEvents.kt (91%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/auth/PinAuthenticationNode.kt (95%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/auth/PinAuthenticationPresenter.kt (87%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/auth/PinAuthenticationState.kt (91%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/auth/PinAuthenticationStateProvider.kt (93%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/auth/PinAuthenticationView.kt (97%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/create/CreatePinEvents.kt (91%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/create/CreatePinNode.kt (95%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/create/CreatePinPresenter.kt (94%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/create/CreatePinState.kt (91%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/create/CreatePinStateProvider.kt (93%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl}/create/CreatePinView.kt (96%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen}/impl/pin/DefaultPinCodeManager.kt (94%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen}/impl/pin/PinCodeManager.kt (96%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen}/impl/pin/storage/EncryptedPinCodeStorage.kt (95%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen}/impl/pin/storage/PinCodeStore.kt (95%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen}/impl/pin/storage/SharedPreferencesPinCodeStore.kt (98%) rename features/{pin/impl/src/main/kotlin/io/element/android/features/pin/impl/state/DefaultPinStateService.kt => lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt} (75%) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 82786b1a0c..2f7eabb582 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -50,9 +50,9 @@ import io.element.android.features.ftue.api.state.FtueState import io.element.android.features.invitelist.api.InviteListEntryPoint import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus -import io.element.android.features.pin.api.PinEntryPoint -import io.element.android.features.pin.api.PinState -import io.element.android.features.pin.api.PinStateService +import io.element.android.features.lockscreen.api.LockScreenEntryPoint +import io.element.android.features.lockscreen.api.LockScreenState +import io.element.android.features.lockscreen.api.LockScreenStateService import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.features.roomlist.api.RoomListEntryPoint import io.element.android.features.verifysession.api.VerifySessionEntryPoint @@ -93,8 +93,8 @@ class LoggedInFlowNode @AssistedInject constructor( private val networkMonitor: NetworkMonitor, private val notificationDrawerManager: NotificationDrawerManager, private val ftueState: FtueState, - private val pinEntryPoint: PinEntryPoint, - private val pinStateService: PinStateService, + private val lockScreenEntryPoint: LockScreenEntryPoint, + private val lockScreenStateService: LockScreenStateService, private val matrixClient: MatrixClient, snackbarDispatcher: SnackbarDispatcher, ) : BackstackNode( @@ -136,12 +136,12 @@ class LoggedInFlowNode @AssistedInject constructor( }, onResume = { coroutineScope.launch { - pinStateService.entersForeground() + lockScreenStateService.entersForeground() } }, onPause = { coroutineScope.launch { - pinStateService.entersBackground() + lockScreenStateService.entersBackground() } }, onStop = { @@ -218,7 +218,7 @@ class LoggedInFlowNode @AssistedInject constructor( createNode(buildContext) } NavTarget.LockPermanent -> { - pinEntryPoint.createNode(this, buildContext) + lockScreenEntryPoint.createNode(this, buildContext) } NavTarget.RoomList -> { val callback = object : RoomListEntryPoint.Callback { @@ -345,9 +345,9 @@ class LoggedInFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { Box(modifier = modifier) { - val pinState by pinStateService.pinState.collectAsState() - when (pinState) { - PinState.Unlocked -> { + val lockScreenState by lockScreenStateService.state.collectAsState() + when (lockScreenState) { + LockScreenState.Unlocked -> { Children( navModel = backstack, modifier = Modifier, @@ -359,7 +359,7 @@ class LoggedInFlowNode @AssistedInject constructor( PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent) } } - PinState.Locked -> { + LockScreenState.Locked -> { MoveActivityToBackgroundBackHandler() PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LockPermanent) } diff --git a/features/pin/api/build.gradle.kts b/features/lockscreen/api/build.gradle.kts similarity index 92% rename from features/pin/api/build.gradle.kts rename to features/lockscreen/api/build.gradle.kts index 95b062b0c8..97f472517c 100644 --- a/features/pin/api/build.gradle.kts +++ b/features/lockscreen/api/build.gradle.kts @@ -19,7 +19,7 @@ plugins { } android { - namespace = "io.element.android.features.pin.api" + namespace = "io.element.android.features.lockscreen.api" } dependencies { diff --git a/features/pin/api/src/main/kotlin/io/element/android/features/pin/api/PinEntryPoint.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt similarity index 86% rename from features/pin/api/src/main/kotlin/io/element/android/features/pin/api/PinEntryPoint.kt rename to features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt index 1fe3caf574..3c9aceb2c8 100644 --- a/features/pin/api/src/main/kotlin/io/element/android/features/pin/api/PinEntryPoint.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt @@ -14,8 +14,8 @@ * limitations under the License. */ -package io.element.android.features.pin.api +package io.element.android.features.lockscreen.api import io.element.android.libraries.architecture.SimpleFeatureEntryPoint -interface PinEntryPoint : SimpleFeatureEntryPoint +interface LockScreenEntryPoint : SimpleFeatureEntryPoint diff --git a/features/pin/api/src/main/kotlin/io/element/android/features/pin/api/PinState.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenState.kt similarity index 78% rename from features/pin/api/src/main/kotlin/io/element/android/features/pin/api/PinState.kt rename to features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenState.kt index 0ff1b0b3d5..d1e53cfdcc 100644 --- a/features/pin/api/src/main/kotlin/io/element/android/features/pin/api/PinState.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenState.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package io.element.android.features.pin.api +package io.element.android.features.lockscreen.api -sealed interface PinState { - data object Unlocked : PinState - data object Locked : PinState +sealed interface LockScreenState { + data object Unlocked : LockScreenState + data object Locked : LockScreenState } diff --git a/features/pin/api/src/main/kotlin/io/element/android/features/pin/api/PinStateService.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenStateService.kt similarity index 85% rename from features/pin/api/src/main/kotlin/io/element/android/features/pin/api/PinStateService.kt rename to features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenStateService.kt index 4ecb473c18..2f2e6b2376 100644 --- a/features/pin/api/src/main/kotlin/io/element/android/features/pin/api/PinStateService.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenStateService.kt @@ -14,12 +14,12 @@ * limitations under the License. */ -package io.element.android.features.pin.api +package io.element.android.features.lockscreen.api import kotlinx.coroutines.flow.StateFlow -interface PinStateService { - val pinState: StateFlow +interface LockScreenStateService { + val state: StateFlow suspend fun entersForeground() suspend fun entersBackground() diff --git a/features/pin/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts similarity index 93% rename from features/pin/impl/build.gradle.kts rename to features/lockscreen/impl/build.gradle.kts index e734035ab3..51e766bd36 100644 --- a/features/pin/impl/build.gradle.kts +++ b/features/lockscreen/impl/build.gradle.kts @@ -22,7 +22,7 @@ plugins { } android { - namespace = "io.element.android.features.pin.impl" + namespace = "io.element.android.features.lockscreen.impl" } anvil { @@ -32,7 +32,7 @@ anvil { dependencies { implementation(projects.anvilannotations) anvil(projects.anvilcodegen) - api(projects.features.pin.api) + api(projects.features.lockscreen.api) implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/DefaultPinEntryPoint.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt similarity index 78% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/DefaultPinEntryPoint.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt index 17b7f6d612..736be374cd 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/DefaultPinEntryPoint.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt @@ -14,20 +14,20 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation +package io.element.android.features.lockscreen.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.pin.api.PinEntryPoint +import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.AppScope import javax.inject.Inject @ContributesBinding(AppScope::class) -class DefaultPinEntryPoint @Inject constructor() : PinEntryPoint { +class DefaultLockScreenEntryPoint @Inject constructor() : LockScreenEntryPoint { override fun createNode(parentNode: Node, buildContext: BuildContext): Node { - return parentNode.createNode(buildContext) + return parentNode.createNode(buildContext) } } diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/PinFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt similarity index 88% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/PinFlowNode.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt index acf10ffcce..d2989d53cc 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/PinFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation +package io.element.android.features.lockscreen.impl import android.os.Parcelable import androidx.compose.runtime.Composable @@ -27,8 +27,8 @@ import com.bumble.appyx.navmodel.backstack.BackStack import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.features.pin.impl.presentation.auth.PinAuthenticationNode -import io.element.android.features.pin.impl.presentation.create.CreatePinNode +import io.element.android.features.lockscreen.impl.auth.PinAuthenticationNode +import io.element.android.features.lockscreen.impl.create.CreatePinNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode @@ -36,10 +36,10 @@ import io.element.android.libraries.di.AppScope import kotlinx.parcelize.Parcelize @ContributesNode(AppScope::class) -class PinFlowNode @AssistedInject constructor( +class LockScreenFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, -) : BackstackNode( +) : BackstackNode( backstack = BackStack( initialElement = NavTarget.Auth, savedStateMap = buildContext.savedStateMap, diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationEvents.kt similarity index 91% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationEvents.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationEvents.kt index 01835df18c..f9f46c430a 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationEvents.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.auth +package io.element.android.features.lockscreen.impl.auth sealed interface PinAuthenticationEvents { data object Unlock : PinAuthenticationEvents diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationNode.kt similarity index 95% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationNode.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationNode.kt index ec4b78dc15..d236d40cf1 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.auth +package io.element.android.features.lockscreen.impl.auth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationPresenter.kt similarity index 87% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationPresenter.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationPresenter.kt index bb1e5d04ad..ecc82f421c 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationPresenter.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.auth +package io.element.android.features.lockscreen.impl.auth import androidx.compose.runtime.Composable -import io.element.android.features.pin.api.PinStateService +import io.element.android.features.lockscreen.api.LockScreenStateService import io.element.android.libraries.architecture.Presenter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject class PinAuthenticationPresenter @Inject constructor( - private val pinStateService: PinStateService, + private val pinStateService: LockScreenStateService, private val coroutineScope: CoroutineScope, ) : Presenter { diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationState.kt similarity index 91% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationState.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationState.kt index 3d319356ec..387467534f 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.auth +package io.element.android.features.lockscreen.impl.auth data class PinAuthenticationState( val eventSink: (PinAuthenticationEvents) -> Unit diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationStateProvider.kt similarity index 93% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationStateProvider.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationStateProvider.kt index 3cfbe5cb84..a2612ed858 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationStateProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.auth +package io.element.android.features.lockscreen.impl.auth import androidx.compose.ui.tooling.preview.PreviewParameterProvider diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationView.kt similarity index 97% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationView.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationView.kt index 9efab19813..2b62e46800 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/auth/PinAuthenticationView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.auth +package io.element.android.features.lockscreen.impl.auth import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt similarity index 91% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinEvents.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt index 87aee27557..deb3095e69 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.create +package io.element.android.features.lockscreen.impl.create sealed interface CreatePinEvents { object MyEvent : CreatePinEvents diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinNode.kt similarity index 95% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinNode.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinNode.kt index 6b71510d5a..3689c0cc76 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.create +package io.element.android.features.lockscreen.impl.create import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt similarity index 94% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinPresenter.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt index 8cfdc22e46..08ba24e074 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.create +package io.element.android.features.lockscreen.impl.create import androidx.compose.runtime.Composable import io.element.android.libraries.architecture.Presenter diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt similarity index 91% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinState.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt index 64c0ae0a5a..67311639ad 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.create +package io.element.android.features.lockscreen.impl.create data class CreatePinState( val eventSink: (CreatePinEvents) -> Unit diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt similarity index 93% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinStateProvider.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt index 0afe3442a2..a918b5193e 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.create +package io.element.android.features.lockscreen.impl.create import androidx.compose.ui.tooling.preview.PreviewParameterProvider diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt similarity index 96% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinView.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index 643f487d1b..120c0b6079 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/presentation/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.presentation.create +package io.element.android.features.lockscreen.impl.create import androidx.compose.foundation.layout.Box import androidx.compose.material3.MaterialTheme diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/DefaultPinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt similarity index 94% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/DefaultPinCodeManager.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt index 4ee1a530a2..e7e27bf5f3 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/DefaultPinCodeManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.pin +package io.element.android.features.lockscreen.impl.pin import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.pin.impl.pin.storage.PinCodeStore +import io.element.android.features.lockscreen.impl.pin.storage.PinCodeStore import io.element.android.libraries.cryptography.api.CryptoService import io.element.android.libraries.cryptography.api.EncryptionResult import io.element.android.libraries.di.AppScope diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/PinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt similarity index 96% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/PinCodeManager.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt index df4262d9eb..960b6ecba1 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/PinCodeManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.pin +package io.element.android.features.lockscreen.impl.pin /** * This interface is the main interface to manage the pin code. diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/EncryptedPinCodeStorage.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/EncryptedPinCodeStorage.kt similarity index 95% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/EncryptedPinCodeStorage.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/EncryptedPinCodeStorage.kt index bf1dec8d0d..2345eaf481 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/EncryptedPinCodeStorage.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/EncryptedPinCodeStorage.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.pin.storage +package io.element.android.features.lockscreen.impl.pin.storage /** * Should be implemented by any class that provides access to the encrypted PIN code. diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/PinCodeStore.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/PinCodeStore.kt similarity index 95% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/PinCodeStore.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/PinCodeStore.kt index 3476890275..e72cbca2db 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/PinCodeStore.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/PinCodeStore.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.pin.storage +package io.element.android.features.lockscreen.impl.pin.storage interface PinCodeStore : EncryptedPinCodeStorage { diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/SharedPreferencesPinCodeStore.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/SharedPreferencesPinCodeStore.kt similarity index 98% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/SharedPreferencesPinCodeStore.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/SharedPreferencesPinCodeStore.kt index 892336f01d..dbeabb53ed 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/pin/storage/SharedPreferencesPinCodeStore.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/SharedPreferencesPinCodeStore.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.pin.storage +package io.element.android.features.lockscreen.impl.pin.storage import android.content.SharedPreferences import androidx.core.content.edit diff --git a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/state/DefaultPinStateService.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt similarity index 75% rename from features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/state/DefaultPinStateService.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt index 9accef2b80..dbfeca2c6a 100644 --- a/features/pin/impl/src/main/kotlin/io/element/android/features/pin/impl/state/DefaultPinStateService.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.element.android.features.pin.impl.state +package io.element.android.features.lockscreen.impl.state import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.pin.api.PinState -import io.element.android.features.pin.api.PinStateService +import io.element.android.features.lockscreen.api.LockScreenState +import io.element.android.features.lockscreen.api.LockScreenStateService import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SingleIn import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -35,18 +35,18 @@ private const val GRACE_PERIOD_IN_MILLIS = 90 * 1000L @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) -class DefaultPinStateService @Inject constructor( +class DefaultLockScreenStateService @Inject constructor( private val featureFlagService: FeatureFlagService, -) : PinStateService { +) : LockScreenStateService { - private val _pinState = MutableStateFlow(PinState.Unlocked) - override val pinState: StateFlow = _pinState + private val _lockScreenState = MutableStateFlow(LockScreenState.Unlocked) + override val state: StateFlow = _lockScreenState private var lockJob: Job? = null override suspend fun unlock() { if (featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock)) { - _pinState.value = PinState.Unlocked + _lockScreenState.value = LockScreenState.Unlocked } } @@ -58,7 +58,7 @@ class DefaultPinStateService @Inject constructor( lockJob = launch { if (featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock)) { delay(GRACE_PERIOD_IN_MILLIS) - _pinState.value = PinState.Locked + _lockScreenState.value = LockScreenState.Locked } } } From d6d553e8e0650a74341edb333ce05390eb1bc7ae Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 18 Oct 2023 11:26:02 +0200 Subject: [PATCH 06/11] Pin code: add some tests --- features/lockscreen/impl/build.gradle.kts | 2 + .../impl/pin/DefaultPinCodeManager.kt | 14 +++-- .../impl/pin/DefaultPinCodeManagerTest.kt | 53 +++++++++++++++++ .../impl/pin/storage/InMemoryPinCodeStore.kt | 59 +++++++++++++++++++ .../cryptography/api/AESEncryptionSpecs.kt | 27 +++++++++ ...vice.kt => EncryptionDecryptionService.kt} | 5 +- .../cryptography/api/EncryptionResult.kt | 9 ++- .../cryptography/api/SecretKeyProvider.kt | 27 +++++++++ libraries/cryptography/impl/build.gradle.kts | 4 ++ ...e.kt => AESEncryptionDecryptionService.kt} | 41 +++---------- .../impl/KeyStoreSecretKeyProvider.kt | 55 +++++++++++++++++ .../AESEncryptionDecryptionServiceTest.kt | 54 +++++++++++++++++ libraries/cryptography/test/build.gradle.kts | 27 +++++++++ .../test/SimpleSecretKeyProvider.kt | 39 ++++++++++++ 14 files changed, 371 insertions(+), 45 deletions(-) create mode 100644 features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt create mode 100644 features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt create mode 100644 libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/AESEncryptionSpecs.kt rename libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/{CryptoService.kt => EncryptionDecryptionService.kt} (88%) create mode 100644 libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyProvider.kt rename libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/{DefaultCryptoService.kt => AESEncryptionDecryptionService.kt} (52%) create mode 100644 libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt create mode 100644 libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt create mode 100644 libraries/cryptography/test/build.gradle.kts create mode 100644 libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyProvider.kt diff --git a/features/lockscreen/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts index 51e766bd36..af63538db5 100644 --- a/features/lockscreen/impl/build.gradle.kts +++ b/features/lockscreen/impl/build.gradle.kts @@ -47,6 +47,8 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.cryptography.test) + testImplementation(projects.libraries.cryptography.impl) ksp(libs.showkase.processor) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt index e7e27bf5f3..e7529e9280 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt @@ -18,8 +18,9 @@ package io.element.android.features.lockscreen.impl.pin import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.lockscreen.impl.pin.storage.PinCodeStore -import io.element.android.libraries.cryptography.api.CryptoService +import io.element.android.libraries.cryptography.api.EncryptionDecryptionService import io.element.android.libraries.cryptography.api.EncryptionResult +import io.element.android.libraries.cryptography.api.SecretKeyProvider import io.element.android.libraries.di.AppScope import javax.inject.Inject @@ -27,7 +28,8 @@ private const val SECRET_KEY_ALIAS = "SECRET_KEY_ALIAS_PIN_CODE" @ContributesBinding(AppScope::class) class DefaultPinCodeManager @Inject constructor( - private val cryptoService: CryptoService, + private val secretKeyProvider: SecretKeyProvider, + private val encryptionDecryptionService: EncryptionDecryptionService, private val pinCodeStore: PinCodeStore, ) : PinCodeManager { @@ -36,16 +38,16 @@ class DefaultPinCodeManager @Inject constructor( } override suspend fun createPinCode(pinCode: String) { - val secretKey = cryptoService.getOrCreateSecretKey(SECRET_KEY_ALIAS) - val encryptedPinCode = cryptoService.encrypt(secretKey, pinCode.toByteArray()).toBase64() + val secretKey = secretKeyProvider.getOrCreateKey(SECRET_KEY_ALIAS) + val encryptedPinCode = encryptionDecryptionService.encrypt(secretKey, pinCode.toByteArray()).toBase64() pinCodeStore.saveEncryptedPinCode(encryptedPinCode) } override suspend fun verifyPinCode(pinCode: String): Boolean { val encryptedPinCode = pinCodeStore.getEncryptedCode() ?: return false return try { - val secretKey = cryptoService.getOrCreateSecretKey(SECRET_KEY_ALIAS) - val decryptedPinCode = cryptoService.decrypt(secretKey, EncryptionResult.fromBase64(encryptedPinCode)) + val secretKey = secretKeyProvider.getOrCreateKey(SECRET_KEY_ALIAS) + val decryptedPinCode = encryptionDecryptionService.decrypt(secretKey, EncryptionResult.fromBase64(encryptedPinCode)) decryptedPinCode.contentEquals(pinCode.toByteArray()) } catch (failure: Throwable) { false diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt new file mode 100644 index 0000000000..a3f29c5351 --- /dev/null +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.pin + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.lockscreen.impl.pin.storage.InMemoryPinCodeStore +import io.element.android.libraries.cryptography.impl.AESEncryptionDecryptionService +import io.element.android.libraries.cryptography.test.SimpleSecretKeyProvider +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultPinCodeManagerTest { + + private val pinCodeStore = InMemoryPinCodeStore() + private val secretKeyProvider = SimpleSecretKeyProvider() + private val encryptionDecryptionService = AESEncryptionDecryptionService() + private val pinCodeManager = DefaultPinCodeManager(secretKeyProvider, encryptionDecryptionService, pinCodeStore) + + @Test + fun given_a_pin_code_when_create_and_delete_assert_no_pin_code_left() = runTest { + pinCodeManager.createPinCode("1234") + assertThat(pinCodeManager.isPinCodeAvailable()).isTrue() + pinCodeManager.deletePinCode() + assertThat(pinCodeManager.isPinCodeAvailable()).isFalse() + } + + @Test + fun given_a_pin_code_when_create_and_verify_with_the_same_pin_succeed() = runTest { + val pinCode = "1234" + pinCodeManager.createPinCode(pinCode) + assertThat(pinCodeManager.verifyPinCode(pinCode)).isTrue() + } + + @Test + fun given_a_pin_code_when_create_and_verify_with_a_different_pin_fails() = runTest { + pinCodeManager.createPinCode("1234") + assertThat(pinCodeManager.verifyPinCode("1235")).isFalse() + } +} diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt new file mode 100644 index 0000000000..ed949c08de --- /dev/null +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.pin.storage + +class InMemoryPinCodeStore : PinCodeStore { + + private var pinCode: String? = null + private var remainingAttempts: Int = 3 + + override suspend fun getRemainingPinCodeAttemptsNumber(): Int { + return remainingAttempts + } + + override suspend fun onWrongPin(): Int { + return remainingAttempts-- + } + + override suspend fun resetCounter() { + remainingAttempts = 3 + } + + override fun addListener(listener: PinCodeStore.Listener) { + // no-op + } + + override fun removeListener(listener: PinCodeStore.Listener) { + // no-op + } + + override suspend fun getEncryptedCode(): String? { + return pinCode + } + + override suspend fun saveEncryptedPinCode(pinCode: String) { + this.pinCode = pinCode + } + + override suspend fun deleteEncryptedPinCode() { + pinCode = null + } + + override suspend fun hasPinCode(): Boolean { + return pinCode != null + } +} diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/AESEncryptionSpecs.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/AESEncryptionSpecs.kt new file mode 100644 index 0000000000..d4be4a1f4f --- /dev/null +++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/AESEncryptionSpecs.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.cryptography.api + +import android.security.keystore.KeyProperties + +object AESEncryptionSpecs { + const val BLOCK_MODE = KeyProperties.BLOCK_MODE_GCM + const val PADDINGS = KeyProperties.ENCRYPTION_PADDING_NONE + const val ALGORITHM = KeyProperties.KEY_ALGORITHM_AES + const val KEY_SIZE = 128 + const val CIPHER_TRANSFORMATION = "$ALGORITHM/$BLOCK_MODE/$PADDINGS" +} diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CryptoService.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionDecryptionService.kt similarity index 88% rename from libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CryptoService.kt rename to libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionDecryptionService.kt index a05e11da2a..d670f7b1d2 100644 --- a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/CryptoService.kt +++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionDecryptionService.kt @@ -20,10 +20,9 @@ import javax.crypto.Cipher import javax.crypto.SecretKey /** - * Simple service to provide cryptographic operations. + * Simple service to provide encryption and decryption operations. */ -interface CryptoService { - fun getOrCreateSecretKey(alias: String): SecretKey +interface EncryptionDecryptionService { fun createEncryptionCipher(key: SecretKey): Cipher fun createDecryptionCipher(key: SecretKey, initializationVector: ByteArray): Cipher fun encrypt(key: SecretKey, input: ByteArray): EncryptionResult diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt index 10affcfdcb..5aa3a0cbea 100644 --- a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt +++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt @@ -14,10 +14,13 @@ * limitations under the License. */ +@file:OptIn(ExperimentalEncodingApi::class) + package io.element.android.libraries.cryptography.api -import android.util.Base64 import java.nio.ByteBuffer +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi /** * Holds the result of an encryption operation. @@ -34,7 +37,7 @@ class EncryptionResult( .put(initializationVector) .put(encryptedByteArray) .array() - return Base64.encodeToString(cipherTextWithIv, Base64.NO_WRAP) + return Base64.encode(cipherTextWithIv) } companion object { @@ -43,7 +46,7 @@ class EncryptionResult( * @return the [EncryptionResult] from the base64 representation. */ fun fromBase64(base64: String): EncryptionResult { - val cipherTextWithIv = Base64.decode(base64, Base64.NO_WRAP) + val cipherTextWithIv = Base64.decode(base64) val buffer = ByteBuffer.wrap(cipherTextWithIv) val initializationVectorSize = buffer.int val initializationVector = ByteArray(initializationVectorSize) diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyProvider.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyProvider.kt new file mode 100644 index 0000000000..85f57ac07f --- /dev/null +++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyProvider.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.cryptography.api + +import javax.crypto.SecretKey + +/** + * Simple interface to get or create a secret key for a given alias. + * Implementation should be able to store the generated key securely. + */ +interface SecretKeyProvider { + fun getOrCreateKey(alias: String): SecretKey +} diff --git a/libraries/cryptography/impl/build.gradle.kts b/libraries/cryptography/impl/build.gradle.kts index fa6f9db7ea..263fecec27 100644 --- a/libraries/cryptography/impl/build.gradle.kts +++ b/libraries/cryptography/impl/build.gradle.kts @@ -29,7 +29,11 @@ anvil { dependencies { anvil(projects.anvilcodegen) + implementation(libs.dagger) implementation(projects.anvilannotations) implementation(projects.libraries.di) implementation(projects.libraries.cryptography.api) + + testImplementation(libs.test.junit) + testImplementation(libs.test.truth) } diff --git a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/DefaultCryptoService.kt b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionService.kt similarity index 52% rename from libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/DefaultCryptoService.kt rename to libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionService.kt index dc4f771acf..cf1ea93e3a 100644 --- a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/DefaultCryptoService.kt +++ b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionService.kt @@ -16,56 +16,31 @@ package io.element.android.libraries.cryptography.impl -import android.security.keystore.KeyGenParameterSpec -import android.security.keystore.KeyProperties import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.libraries.cryptography.api.CryptoService +import io.element.android.libraries.cryptography.api.AESEncryptionSpecs +import io.element.android.libraries.cryptography.api.EncryptionDecryptionService import io.element.android.libraries.cryptography.api.EncryptionResult import io.element.android.libraries.di.AppScope -import java.security.KeyStore import javax.crypto.Cipher -import javax.crypto.KeyGenerator import javax.crypto.SecretKey import javax.crypto.spec.GCMParameterSpec import javax.inject.Inject -private const val ANDROID_KEYSTORE = "AndroidKeyStore" -private const val ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_GCM -private const val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_NONE -private const val ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES -private const val ENCRYPTION_AES_TRANSFORMATION = "$ENCRYPTION_ALGORITHM/$ENCRYPTION_BLOCK_MODE/$ENCRYPTION_PADDING" - +/** + * Default implementation of [EncryptionDecryptionService] using AES encryption. + */ @ContributesBinding(AppScope::class) -class DefaultCryptoService @Inject constructor() : CryptoService { - - override fun getOrCreateSecretKey(alias: String): SecretKey { - val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE) - val secretKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.SecretKeyEntry) - ?.secretKey - return if (secretKeyEntry == null) { - val generator = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM, ANDROID_KEYSTORE) - val keyGenSpec = KeyGenParameterSpec.Builder( - alias, - KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT - ) - .setBlockModes(ENCRYPTION_BLOCK_MODE) - .setEncryptionPaddings(ENCRYPTION_PADDING) - .setKeySize(128) - .build() - generator.init(keyGenSpec) - generator.generateKey() - } else secretKeyEntry - } +class AESEncryptionDecryptionService @Inject constructor() : EncryptionDecryptionService { override fun createEncryptionCipher(key: SecretKey): Cipher { - return Cipher.getInstance(ENCRYPTION_AES_TRANSFORMATION).apply { + return Cipher.getInstance(AESEncryptionSpecs.CIPHER_TRANSFORMATION).apply { init(Cipher.ENCRYPT_MODE, key) } } override fun createDecryptionCipher(key: SecretKey, initializationVector: ByteArray): Cipher { val spec = GCMParameterSpec(128, initializationVector) - return Cipher.getInstance(ENCRYPTION_AES_TRANSFORMATION).apply { + return Cipher.getInstance(AESEncryptionSpecs.CIPHER_TRANSFORMATION).apply { init(Cipher.DECRYPT_MODE, key, spec) } } diff --git a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt new file mode 100644 index 0000000000..1da8bd7f1b --- /dev/null +++ b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.cryptography.impl + +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import io.element.android.libraries.cryptography.api.AESEncryptionSpecs +import io.element.android.libraries.cryptography.api.SecretKeyProvider +import java.security.KeyStore +import javax.crypto.KeyGenerator +import javax.crypto.SecretKey + +private const val ANDROID_KEYSTORE = "AndroidKeyStore" + +/** + * Default implementation of [SecretKeyProvider] that uses the Android Keystore to store the keys. + * The generated key uses AES algorithm, with a key size of 128 bits, and the GCM block mode. + */ +class KeyStoreSecretKeyProvider : SecretKeyProvider { + + override fun getOrCreateKey(alias: String): SecretKey { + val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE) + val secretKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.SecretKeyEntry) + ?.secretKey + return if (secretKeyEntry == null) { + val generator = KeyGenerator.getInstance(AESEncryptionSpecs.ALGORITHM, ANDROID_KEYSTORE) + val keyGenSpec = KeyGenParameterSpec.Builder( + alias, + KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT + ) + .setBlockModes(AESEncryptionSpecs.BLOCK_MODE) + .setEncryptionPaddings(AESEncryptionSpecs.PADDINGS) + .setKeySize(AESEncryptionSpecs.KEY_SIZE) + .build() + generator.init(keyGenSpec) + generator.generateKey() + } else { + secretKeyEntry + } + } +} diff --git a/libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt b/libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt new file mode 100644 index 0000000000..a693e29ef5 --- /dev/null +++ b/libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.cryptography.impl + +import android.security.keystore.KeyProperties +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertThrows +import org.junit.Test +import java.security.GeneralSecurityException +import javax.crypto.KeyGenerator + +class AESEncryptionDecryptionServiceTest { + + private val encryptionDecryptionService = AESEncryptionDecryptionService() + + @Test + fun given_a_valid_key_then_encrypt_decrypt_work() { + val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES) + keyGenerator.init(128) + val key = keyGenerator.generateKey() + val input = "Hello World".toByteArray() + val encryptionResult = encryptionDecryptionService.encrypt(key, input) + val decrypted = encryptionDecryptionService.decrypt(key, encryptionResult) + assertThat(decrypted).isEqualTo(input) + } + + @Test + fun given_a_wrong_key_then_decrypt_fail() { + val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES) + keyGenerator.init(128) + val encryptionKey = keyGenerator.generateKey() + val input = "Hello World".toByteArray() + val encryptionResult = encryptionDecryptionService.encrypt(encryptionKey, input) + val decryptionKey = keyGenerator.generateKey() + assertThrows(GeneralSecurityException::class.java) { + encryptionDecryptionService.decrypt(decryptionKey, encryptionResult) + } + } + +} diff --git a/libraries/cryptography/test/build.gradle.kts b/libraries/cryptography/test/build.gradle.kts new file mode 100644 index 0000000000..3b9074c897 --- /dev/null +++ b/libraries/cryptography/test/build.gradle.kts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 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. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.cryptography.test" + + dependencies { + api(projects.libraries.cryptography.api) + } +} diff --git a/libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyProvider.kt b/libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyProvider.kt new file mode 100644 index 0000000000..d06a545d78 --- /dev/null +++ b/libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyProvider.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.cryptography.test + +import io.element.android.libraries.cryptography.api.AESEncryptionSpecs +import io.element.android.libraries.cryptography.api.SecretKeyProvider +import javax.crypto.KeyGenerator +import javax.crypto.SecretKey + +class SimpleSecretKeyProvider : SecretKeyProvider { + + private var secretKeyForAlias = HashMap() + + override fun getOrCreateKey(alias: String): SecretKey { + return secretKeyForAlias.getOrPut(alias) { + generateKey() + } + } + + private fun generateKey(): SecretKey { + val keyGenerator = KeyGenerator.getInstance(AESEncryptionSpecs.ALGORITHM) + keyGenerator.init(AESEncryptionSpecs.KEY_SIZE) + return keyGenerator.generateKey() + } +} From 1f97e95a3dc63c1494c33832203bcd272e89f5b4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 18 Oct 2023 11:33:34 +0200 Subject: [PATCH 07/11] Fix warning --- .../features/lockscreen/impl/pin/PinCodeManager.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt index 960b6ecba1..5f84f5296d 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt @@ -27,28 +27,30 @@ interface PinCodeManager { suspend fun isPinCodeAvailable(): Boolean /** - * creates a new encrypted pin code. + * Creates a new encrypted pin code. * @param pinCode the clear pin code to create */ suspend fun createPinCode(pinCode: String) /** - * @return true if the pin code is correct + * @return true if the pin code is correct. */ suspend fun verifyPinCode(pinCode: String): Boolean /** - * deletes the previously created pin code + * Deletes the previously created pin code. */ suspend fun deletePinCode() /** - * @return the number of remaining attempts before the pin code is blocked + * @return the number of remaining attempts before the pin code is blocked. */ suspend fun getRemainingPinCodeAttemptsNumber(): Int /** - * @return the number of remaining attempts before the pin code is blocked + * Should be called when the pin code is incorrect. + * Will decrement the remaining attempts number. + * @return the number of remaining attempts before the pin code is blocked. */ suspend fun onWrongPin(): Int From 1d94b30b9fb98a50c3e08dff0f9ef600ba546ea2 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 18 Oct 2023 11:57:57 +0200 Subject: [PATCH 08/11] Revert settings.gradle.kts --- settings.gradle.kts | 2 -- 1 file changed, 2 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 590089adc6..105befcd04 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,7 +1,5 @@ import java.net.URI -include(":libraries:cryptography:api") - /* * Copyright (c) 2022 New Vector Ltd * From c635281954d30ed7032beb713f32115d606d16ff Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 18 Oct 2023 13:22:53 +0000 Subject: [PATCH 09/11] Update screenshots --- ...h_null_PinAuthenticationView-D-0_0_null_0,NEXUS_5,1.0,en].png} | 0 ...h_null_PinAuthenticationView-N-0_1_null_0,NEXUS_5,1.0,en].png} | 0 ...pl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png} | 0 ...pl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.pin.impl.auth_null_PinAuthenticationView-D-0_0_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-D-0_0_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.pin.impl.auth_null_PinAuthenticationView-N-0_1_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-N-0_1_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.pin.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.pin.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png} (100%) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.pin.impl.auth_null_PinAuthenticationView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-D-0_0_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.pin.impl.auth_null_PinAuthenticationView-D-0_0_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-D-0_0_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.pin.impl.auth_null_PinAuthenticationView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-N-0_1_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.pin.impl.auth_null_PinAuthenticationView-N-0_1_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-N-0_1_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.pin.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.pin.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.pin.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.pin.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png From a896b42fa26e8449ac30205e4e4f215e972b06da Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 18 Oct 2023 15:55:52 +0200 Subject: [PATCH 10/11] Pin : some clean up and fixes --- .../storage/SharedPreferencesPinCodeStore.kt | 23 +++++++++++++------ .../impl/pin/DefaultPinCodeManagerTest.kt | 6 ++--- .../impl/pin/storage/InMemoryPinCodeStore.kt | 6 +++-- .../impl/KeyStoreSecretKeyProvider.kt | 9 +++++++- .../AESEncryptionDecryptionServiceTest.kt | 4 ++-- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/SharedPreferencesPinCodeStore.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/SharedPreferencesPinCodeStore.kt index dbeabb53ed..27f4636400 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/SharedPreferencesPinCodeStore.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/SharedPreferencesPinCodeStore.kt @@ -22,6 +22,8 @@ import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SingleIn +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject @@ -38,6 +40,7 @@ class SharedPreferencesPinCodeStore @Inject constructor( ) : PinCodeStore { private val listeners = CopyOnWriteArrayList() + private val mutex = Mutex() override suspend fun getEncryptedCode(): String? = withContext(dispatchers.io) { sharedPreferences.getString(ENCODED_PIN_CODE_KEY, null) @@ -68,20 +71,26 @@ class SharedPreferencesPinCodeStore @Inject constructor( } override suspend fun getRemainingPinCodeAttemptsNumber(): Int = withContext(dispatchers.io) { - sharedPreferences.getInt(REMAINING_PIN_CODE_ATTEMPTS_KEY, MAX_PIN_CODE_ATTEMPTS_NUMBER_BEFORE_LOGOUT) + mutex.withLock { + sharedPreferences.getInt(REMAINING_PIN_CODE_ATTEMPTS_KEY, MAX_PIN_CODE_ATTEMPTS_NUMBER_BEFORE_LOGOUT) + } } override suspend fun onWrongPin(): Int = withContext(dispatchers.io) { - val remaining = getRemainingPinCodeAttemptsNumber() - 1 - sharedPreferences.edit { - putInt(REMAINING_PIN_CODE_ATTEMPTS_KEY, remaining) + mutex.withLock { + val remaining = getRemainingPinCodeAttemptsNumber() - 1 + sharedPreferences.edit { + putInt(REMAINING_PIN_CODE_ATTEMPTS_KEY, remaining) + } + remaining } - remaining } override suspend fun resetCounter() = withContext(dispatchers.io) { - sharedPreferences.edit { - remove(REMAINING_PIN_CODE_ATTEMPTS_KEY) + mutex.withLock { + sharedPreferences.edit { + remove(REMAINING_PIN_CODE_ATTEMPTS_KEY) + } } } diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt index a3f29c5351..8b14d15e5e 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt @@ -31,7 +31,7 @@ class DefaultPinCodeManagerTest { private val pinCodeManager = DefaultPinCodeManager(secretKeyProvider, encryptionDecryptionService, pinCodeStore) @Test - fun given_a_pin_code_when_create_and_delete_assert_no_pin_code_left() = runTest { + fun `given a pin code when create and delete assert no pin code left`() = runTest { pinCodeManager.createPinCode("1234") assertThat(pinCodeManager.isPinCodeAvailable()).isTrue() pinCodeManager.deletePinCode() @@ -39,14 +39,14 @@ class DefaultPinCodeManagerTest { } @Test - fun given_a_pin_code_when_create_and_verify_with_the_same_pin_succeed() = runTest { + fun `given a pin code when create and verify with the same pin succeed`() = runTest { val pinCode = "1234" pinCodeManager.createPinCode(pinCode) assertThat(pinCodeManager.verifyPinCode(pinCode)).isTrue() } @Test - fun given_a_pin_code_when_create_and_verify_with_a_different_pin_fails() = runTest { + fun `given a pin code when create and verify with a different pin fails`() = runTest { pinCodeManager.createPinCode("1234") assertThat(pinCodeManager.verifyPinCode("1235")).isFalse() } diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt index ed949c08de..0b7c2f256b 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt @@ -16,10 +16,12 @@ package io.element.android.features.lockscreen.impl.pin.storage +private const val DEFAULT_REMAINING_ATTEMPTS = 3 + class InMemoryPinCodeStore : PinCodeStore { private var pinCode: String? = null - private var remainingAttempts: Int = 3 + private var remainingAttempts: Int = DEFAULT_REMAINING_ATTEMPTS override suspend fun getRemainingPinCodeAttemptsNumber(): Int { return remainingAttempts @@ -30,7 +32,7 @@ class InMemoryPinCodeStore : PinCodeStore { } override suspend fun resetCounter() { - remainingAttempts = 3 + remainingAttempts = DEFAULT_REMAINING_ATTEMPTS } override fun addListener(listener: PinCodeStore.Listener) { diff --git a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt index 1da8bd7f1b..2cd09ea8f6 100644 --- a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt +++ b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt @@ -16,13 +16,17 @@ package io.element.android.libraries.cryptography.impl +import android.annotation.SuppressLint import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties +import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.cryptography.api.AESEncryptionSpecs import io.element.android.libraries.cryptography.api.SecretKeyProvider +import io.element.android.libraries.di.AppScope import java.security.KeyStore import javax.crypto.KeyGenerator import javax.crypto.SecretKey +import javax.inject.Inject private const val ANDROID_KEYSTORE = "AndroidKeyStore" @@ -30,8 +34,11 @@ private const val ANDROID_KEYSTORE = "AndroidKeyStore" * Default implementation of [SecretKeyProvider] that uses the Android Keystore to store the keys. * The generated key uses AES algorithm, with a key size of 128 bits, and the GCM block mode. */ -class KeyStoreSecretKeyProvider : SecretKeyProvider { +@ContributesBinding(AppScope::class) +class KeyStoreSecretKeyProvider @Inject constructor() : SecretKeyProvider { + // False positive lint issue + @SuppressLint("WrongConstant") override fun getOrCreateKey(alias: String): SecretKey { val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE) val secretKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.SecretKeyEntry) diff --git a/libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt b/libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt index a693e29ef5..38e1c924ca 100644 --- a/libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt +++ b/libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt @@ -28,7 +28,7 @@ class AESEncryptionDecryptionServiceTest { private val encryptionDecryptionService = AESEncryptionDecryptionService() @Test - fun given_a_valid_key_then_encrypt_decrypt_work() { + fun `given a valid key then encrypt decrypt work`() { val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES) keyGenerator.init(128) val key = keyGenerator.generateKey() @@ -39,7 +39,7 @@ class AESEncryptionDecryptionServiceTest { } @Test - fun given_a_wrong_key_then_decrypt_fail() { + fun `given a wrong key then decrypt fail`() { val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES) keyGenerator.init(128) val encryptionKey = keyGenerator.generateKey() From 436c9e83f00c48e5d9f3b46eec7d7c826f1800b3 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 18 Oct 2023 16:52:45 +0200 Subject: [PATCH 11/11] Fix kover... --- build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 704bbe6bbc..487776d948 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -251,9 +251,9 @@ koverMerged { // Some options can't be tested at the moment excludes += "io.element.android.features.preferences.impl.developer.DeveloperSettingsPresenter$*" // Temporary until we have actually something to test. - excludes += "io.element.android.features.pin.impl.auth.PinAuthenticationPresenter" - excludes += "io.element.android.features.pin.impl.auth.PinAuthenticationPresenter$*" - excludes += "io.element.android.features.pin.impl.create.CreatePinPresenter" + excludes += "io.element.android.features.lockscreen.impl.auth.PinAuthenticationPresenter" + excludes += "io.element.android.features.lockscreen.impl.auth.PinAuthenticationPresenter$*" + excludes += "io.element.android.features.lockscreen.impl.create.CreatePinPresenter" } bound { minValue = 85