diff --git a/common/src/commonMain/composeResources/values/strings.xml b/common/src/commonMain/composeResources/values/strings.xml index 3ad8f741..c1400bc7 100644 --- a/common/src/commonMain/composeResources/values/strings.xml +++ b/common/src/commonMain/composeResources/values/strings.xml @@ -489,6 +489,7 @@ Invalid URI Invalid card number Incorrect password + One time password's secret key must not be empty Failed to create a key, not enough memory. Please check your KDF server settings. Failed to generate OTP code Failed to create a passkey @@ -497,11 +498,16 @@ Failed to format the placeholder No installed app can handle this request Something went wrong + Failed to encrypt the biometric key + Failed to decrypt the biometric key Scan QR code Load and parse a QR code from an image file. Camera permission is required to scan the QR codes. + Notifications are disabled + Grant the notification permission to allow Keyguard to show one-time passwords when autofilling & more. + Barcode Show as Barcode diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/exception/OtpEmptySecretKeyException.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/exception/OtpEmptySecretKeyException.kt new file mode 100644 index 00000000..da7839af --- /dev/null +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/exception/OtpEmptySecretKeyException.kt @@ -0,0 +1,12 @@ +package com.artemchep.keyguard.common.exception + +import com.artemchep.keyguard.feature.localization.TextHolder +import com.artemchep.keyguard.res.Res +import com.artemchep.keyguard.res.* + +class OtpEmptySecretKeyException( + e: Throwable? = null, +) : IllegalArgumentException("One time password's secret key must not be empty", e), Readable { + override val title: TextHolder + get() = TextHolder.Res(Res.string.error_otp_key_must_not_be_empty) +} diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/exception/crypto/BiometricKeyDecryptException.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/exception/crypto/BiometricKeyDecryptException.kt new file mode 100644 index 00000000..18ccf71b --- /dev/null +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/exception/crypto/BiometricKeyDecryptException.kt @@ -0,0 +1,16 @@ +package com.artemchep.keyguard.common.exception.crypto + +import com.artemchep.keyguard.common.exception.Readable +import com.artemchep.keyguard.feature.localization.TextHolder +import com.artemchep.keyguard.res.Res +import com.artemchep.keyguard.res.* + +class BiometricKeyDecryptException( + e: Throwable, +) : RuntimeException("Failed to decrypt the biometric key", e), Readable { + override val title: TextHolder + get() = TextHolder.Res(Res.string.error_failed_decrypt_biometric_key) + + override val text: TextHolder? + get() = cause?.message?.let(TextHolder::Value) +} diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/exception/crypto/BiometricKeyEncryptException.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/exception/crypto/BiometricKeyEncryptException.kt new file mode 100644 index 00000000..2ce1e6b6 --- /dev/null +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/exception/crypto/BiometricKeyEncryptException.kt @@ -0,0 +1,16 @@ +package com.artemchep.keyguard.common.exception.crypto + +import com.artemchep.keyguard.common.exception.Readable +import com.artemchep.keyguard.feature.localization.TextHolder +import com.artemchep.keyguard.res.Res +import com.artemchep.keyguard.res.* + +class BiometricKeyEncryptException( + e: Throwable, +) : RuntimeException("Failed to encrypt the biometric key", e), Readable { + override val title: TextHolder + get() = TextHolder.Res(Res.string.error_failed_encrypt_biometric_key) + + override val text: TextHolder? + get() = cause?.message?.let(TextHolder::Value) +} diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/model/TotpToken.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/model/TotpToken.kt index bc428357..2788bacd 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/model/TotpToken.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/model/TotpToken.kt @@ -4,6 +4,7 @@ import arrow.core.Either import arrow.core.Either.Companion.catch import arrow.core.flatten import arrow.core.right +import com.artemchep.keyguard.common.exception.OtpEmptySecretKeyException import io.ktor.http.Url private const val PREFIX_OTP_AUTH = "otpauth://" @@ -163,7 +164,7 @@ private fun parseTotpAuth( } if (keyBase32.isBlank()) { - throw IllegalArgumentException("One time password key must not be empty.") + throw OtpEmptySecretKeyException() } builder.build( @@ -203,7 +204,7 @@ private fun parseHotpAuth( } if (keyBase32.isBlank()) { - throw IllegalArgumentException("One time password key must not be empty.") + throw OtpEmptySecretKeyException() } builder.build( @@ -266,8 +267,9 @@ private fun parseOtpMobile( ) } - val secret = requireNotNull(params["secret"]) { - "URI must include the mOTP secret" + val secret = params["secret"] + if (secret.isNullOrBlank()) { + throw OtpEmptySecretKeyException() } val pin = params["pin"] TotpToken.MobileAuth( diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/UnlockUseCaseImpl.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/UnlockUseCaseImpl.kt index b7631bec..331b9a47 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/UnlockUseCaseImpl.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/UnlockUseCaseImpl.kt @@ -5,6 +5,8 @@ import arrow.core.compose import arrow.core.getOrElse import arrow.core.memoize import arrow.core.partially1 +import com.artemchep.keyguard.common.exception.crypto.BiometricKeyDecryptException +import com.artemchep.keyguard.common.exception.crypto.BiometricKeyEncryptException import com.artemchep.keyguard.common.io.IO import com.artemchep.keyguard.common.io.attempt import com.artemchep.keyguard.common.io.dispatchOn @@ -214,8 +216,7 @@ class UnlockUseCaseImpl( } biometricKeyEncryptUseCase(cipherIo, masterKey) .handleErrorWith { e -> - val newMessage = "Failed to encrypt the biometric key!" - val newException = IllegalStateException(newMessage, e) + val newException = BiometricKeyEncryptException(e) ioRaise(newException) } .flatMap { encryptedMasterKey -> @@ -296,8 +297,7 @@ class UnlockUseCaseImpl( } decryptBiometricKeyUseCase(cipherIo, encryptedMasterKey) .handleErrorWith { e -> - val newMessage = "Failed to decrypt the biometric key!" - val newException = IllegalStateException(newMessage, e) + val newException = BiometricKeyDecryptException(e) ioRaise(newException) } // Try to unlock the vault using decrypted @@ -396,8 +396,7 @@ class UnlockUseCaseImpl( } biometricKeyEncryptUseCase(cipherIo, newMasterKey) .handleErrorWith { e -> - val newMessage = "Failed to encrypt the biometric key!" - val newException = IllegalStateException(newMessage, e) + val newException = BiometricKeyEncryptException(e) ioRaise(newException) } .flatMap { encryptedNewMasterKey -> diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/HomeScreen.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/HomeScreen.kt index 92388d7e..788dd043 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/HomeScreen.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/HomeScreen.kt @@ -509,8 +509,8 @@ private fun BannerStatusBadge( .isNotEmpty() -> { BannerStatusBadgeContentModel( count = 0, - title = TextHolder.Value("Notifications are disabled"), - text = TextHolder.Value("Grant the notification permission to allow Keyguard to show one-time passwords when autofilling & more."), + title = TextHolder.Res(Res.string.post_notifications_permission_banner_title), + text = TextHolder.Res(Res.string.post_notifications_permission_banner_text), error = false, onClick = { val permission = statusState.value.pendingPermissions