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