optimize OTP seed (check password =) remove legacy code
Auto Tag on Version Change / check-version (push) Successful in 4s

This commit is contained in:
2026-05-18 23:57:05 +05:00
parent b35f44f35b
commit d4f86bb738
4 changed files with 14 additions and 77 deletions
@@ -18,8 +18,6 @@ import kotlinx.coroutines.withContext
import sh.sar.basedbank.databinding.ActivityLockBinding
import sh.sar.basedbank.ui.home.HomeActivity
import sh.sar.basedbank.util.CredentialStore
import java.security.MessageDigest
import java.security.SecureRandom
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
@@ -31,7 +29,6 @@ class LockActivity : AppCompatActivity() {
private lateinit var salt: String
private lateinit var storedHash: String
private var biometricsEnabled = false
private var isLegacyFormat = false
private var isVerifying = false
private val lockPrefs get() = getSharedPreferences("lock_attempts", MODE_PRIVATE)
@@ -51,17 +48,9 @@ class LockActivity : AppCompatActivity() {
method = prefs.getString("security_method", "pin") ?: "pin"
biometricsEnabled = prefs.getBoolean("biometrics_enabled", false)
// Try new encrypted format first; fall back to legacy SHA-256
val stored = CredentialStore(this).loadSecurityHash()
if (stored != null) {
salt = stored.first
storedHash = stored.second
isLegacyFormat = false
} else {
salt = prefs.getString("security_salt", "") ?: ""
storedHash = prefs.getString("security_hash", "") ?: ""
isLegacyFormat = true
}
val stored = CredentialStore(this).loadSecurityHash() ?: run { finish(); return }
salt = stored.first
storedHash = stored.second
if (method == "pin") {
binding.viewPin.visibility = View.VISIBLE
@@ -150,7 +139,6 @@ class LockActivity : AppCompatActivity() {
val ok = withContext(Dispatchers.Default) { verify(entered) }
isVerifying = false
if (ok) {
migrateIfNeeded(entered)
resetFailures()
proceed()
} else {
@@ -175,7 +163,6 @@ class LockActivity : AppCompatActivity() {
val ok = withContext(Dispatchers.Default) { verify(entered) }
isVerifying = false
if (ok) {
migrateIfNeeded(entered)
resetFailures()
proceed()
} else {
@@ -217,30 +204,8 @@ class LockActivity : AppCompatActivity() {
private fun verify(input: String): Boolean {
if (storedHash.isBlank()) return false
return if (isLegacyFormat) {
sha256Legacy(salt + input) == storedHash
} else {
val saltBytes = Base64.decode(salt, Base64.NO_WRAP)
pbkdf2(input, saltBytes) == storedHash
}
}
/**
* On the first successful unlock after legacy SHA-256 format is detected,
* transparently migrate to PBKDF2 + CredentialStore.
*/
private fun migrateIfNeeded(input: String) {
if (!isLegacyFormat) return
try {
val newSalt = ByteArray(16).also { SecureRandom().nextBytes(it) }
val newHash = pbkdf2(input, newSalt)
val saltB64 = Base64.encodeToString(newSalt, Base64.NO_WRAP)
CredentialStore(this).saveSecurityHash(saltB64, newHash)
// Remove legacy plaintext fields
getSharedPreferences("prefs", MODE_PRIVATE).edit()
.remove("security_salt").remove("security_hash").apply()
isLegacyFormat = false
} catch (_: Exception) { /* migration will retry next unlock */ }
val saltBytes = Base64.decode(salt, Base64.NO_WRAP)
return pbkdf2(input, saltBytes) == storedHash
}
private fun triggerBiometric() {
@@ -313,9 +278,4 @@ class LockActivity : AppCompatActivity() {
}
}
/** Legacy: raw SHA-256(salt + input) — only used for migration path. */
private fun sha256Legacy(input: String) = MessageDigest.getInstance("SHA-256")
.digest(input.toByteArray()).joinToString("") { "%02x".format(it) }
}