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()