mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-15 02:21:08 +00:00
android: StrongBox for new keystore keys, show key storage level (#7637)
* android: create new keystore keys in StrongBox * android: show keystore key storage level * ui: save keystore key before marking it stored * android: show keystore storage level in the footer text instead of a separate row * android: return keystore storage label, drop enum --------- Co-authored-by: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com>
This commit is contained in:
+38
-6
@@ -1,8 +1,11 @@
|
||||
package chat.simplex.common.platform
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Build
|
||||
import android.security.keystore.KeyGenParameterSpec
|
||||
import android.security.keystore.KeyInfo
|
||||
import android.security.keystore.KeyProperties
|
||||
import android.security.keystore.StrongBoxUnavailableException
|
||||
import chat.simplex.common.views.helpers.AlertManager
|
||||
import chat.simplex.common.views.helpers.generalGetString
|
||||
import chat.simplex.res.MR
|
||||
@@ -68,15 +71,44 @@ internal class Cryptor: CryptorInterface {
|
||||
keyStore.deleteEntry(alias)
|
||||
}
|
||||
|
||||
override fun keyStorage(alias: String): String? {
|
||||
val secretKey = getSecretKey(alias) ?: return null
|
||||
val keyInfo = SecretKeyFactory.getInstance(secretKey.algorithm, "AndroidKeyStore").getKeySpec(secretKey, KeyInfo::class.java) as KeyInfo
|
||||
val storage = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
when (keyInfo.securityLevel) {
|
||||
KeyProperties.SECURITY_LEVEL_STRONGBOX -> MR.strings.keystore_key_storage_strongbox
|
||||
KeyProperties.SECURITY_LEVEL_TRUSTED_ENVIRONMENT -> MR.strings.keystore_key_storage_tee
|
||||
KeyProperties.SECURITY_LEVEL_SOFTWARE -> MR.strings.keystore_key_storage_software
|
||||
else -> return null
|
||||
}
|
||||
} else {
|
||||
// isInsideSecureHardware does not distinguish StrongBox, which createSecretKey only requests on API 31+
|
||||
@Suppress("DEPRECATION")
|
||||
if (keyInfo.isInsideSecureHardware) MR.strings.keystore_key_storage_tee else MR.strings.keystore_key_storage_software
|
||||
}
|
||||
return generalGetString(storage)
|
||||
}
|
||||
|
||||
private fun createSecretKey(alias: String): SecretKey? {
|
||||
if (keyStore.containsAlias(alias)) return getSecretKey(alias)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
try {
|
||||
return generateKey(keySpec(alias).setIsStrongBoxBacked(true))
|
||||
} catch (e: StrongBoxUnavailableException) {
|
||||
Log.i(TAG, "StrongBox is unavailable, using default keystore: ${e.message}")
|
||||
}
|
||||
}
|
||||
return generateKey(keySpec(alias))
|
||||
}
|
||||
|
||||
private fun keySpec(alias: String): KeyGenParameterSpec.Builder =
|
||||
KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
|
||||
.setBlockModes(BLOCK_MODE)
|
||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
||||
|
||||
private fun generateKey(spec: KeyGenParameterSpec.Builder): SecretKey {
|
||||
val keyGenerator: KeyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM, "AndroidKeyStore")
|
||||
keyGenerator.init(
|
||||
KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
|
||||
.setBlockModes(BLOCK_MODE)
|
||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
||||
.build()
|
||||
)
|
||||
keyGenerator.init(spec.build())
|
||||
return keyGenerator.generateKey()
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -59,6 +59,7 @@ actual fun DatabaseEncryptionFooter(
|
||||
useKeychain: MutableState<Boolean>,
|
||||
chatDbEncrypted: Boolean?,
|
||||
storedKey: MutableState<Boolean>,
|
||||
keyStorage: String?,
|
||||
initialRandomDBPassphrase: MutableState<Boolean>,
|
||||
migration: Boolean,
|
||||
) {
|
||||
@@ -66,7 +67,7 @@ actual fun DatabaseEncryptionFooter(
|
||||
SectionTextFooter(generalGetString(MR.strings.database_is_not_encrypted))
|
||||
} else if (useKeychain.value) {
|
||||
if (storedKey.value) {
|
||||
SectionTextFooter(generalGetString(MR.strings.keychain_is_storing_securely))
|
||||
SectionTextFooter(String.format(generalGetString(MR.strings.keychain_is_storing_securely), keyStorage?.let { " ($it)" } ?: ""))
|
||||
if (initialRandomDBPassphrase.value && !migration) {
|
||||
SectionTextFooter(generalGetString(MR.strings.encrypted_with_random_passphrase))
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user