2020-10-22 22:14:22 +02:00
|
|
|
package sushi.hardcore.droidfs
|
|
|
|
|
2021-06-07 14:55:01 +02:00
|
|
|
import android.Manifest
|
2020-10-22 22:14:22 +02:00
|
|
|
import android.app.KeyguardManager
|
2021-06-07 14:12:40 +02:00
|
|
|
import android.content.ActivityNotFoundException
|
2020-10-22 22:14:22 +02:00
|
|
|
import android.content.Context
|
2021-06-07 14:55:01 +02:00
|
|
|
import android.content.pm.PackageManager
|
2021-06-07 14:12:40 +02:00
|
|
|
import android.net.Uri
|
2020-10-22 22:14:22 +02:00
|
|
|
import android.os.Build
|
|
|
|
import android.security.keystore.KeyGenParameterSpec
|
|
|
|
import android.security.keystore.KeyPermanentlyInvalidatedException
|
|
|
|
import android.security.keystore.KeyProperties
|
2021-06-11 20:23:54 +02:00
|
|
|
import android.widget.*
|
2021-06-07 14:12:40 +02:00
|
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
2020-10-22 22:14:22 +02:00
|
|
|
import androidx.annotation.RequiresApi
|
2021-06-11 20:23:54 +02:00
|
|
|
import androidx.appcompat.widget.SwitchCompat
|
2020-10-22 22:14:22 +02:00
|
|
|
import androidx.biometric.BiometricManager
|
|
|
|
import androidx.biometric.BiometricPrompt
|
|
|
|
import androidx.core.content.ContextCompat
|
2020-11-04 18:42:04 +01:00
|
|
|
import sushi.hardcore.droidfs.util.PathUtils
|
2020-10-22 22:14:22 +02:00
|
|
|
import sushi.hardcore.droidfs.util.WidgetUtil
|
|
|
|
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
2021-06-11 20:23:54 +02:00
|
|
|
import sushi.hardcore.droidfs.widgets.ColoredImageButton
|
2020-10-22 22:14:22 +02:00
|
|
|
import java.security.KeyStore
|
|
|
|
import javax.crypto.*
|
|
|
|
import javax.crypto.spec.GCMParameterSpec
|
|
|
|
|
2021-06-07 14:12:40 +02:00
|
|
|
abstract class VolumeActionActivity : BaseActivity() {
|
2021-06-11 20:23:54 +02:00
|
|
|
|
|
|
|
companion object {
|
|
|
|
private const val STORAGE_PERMISSIONS_REQUEST = 0
|
|
|
|
private const val ANDROID_KEY_STORE = "AndroidKeyStore"
|
|
|
|
private const val KEY_ALIAS = "Hash Key"
|
|
|
|
private const val KEY_SIZE = 256
|
|
|
|
private const val GCM_TAG_LEN = 128
|
|
|
|
}
|
|
|
|
|
2020-11-04 18:42:04 +01:00
|
|
|
protected lateinit var currentVolumeName: String
|
|
|
|
protected lateinit var currentVolumePath: String
|
|
|
|
protected lateinit var volumeDatabase: VolumeDatabase
|
2021-06-07 14:12:40 +02:00
|
|
|
protected val pickDirectory = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
|
|
|
|
if (uri != null) {
|
|
|
|
onDirectoryPicked(uri)
|
|
|
|
}
|
|
|
|
}
|
2021-10-19 13:52:06 +02:00
|
|
|
protected var usf_fingerprint = false
|
2020-10-22 22:14:22 +02:00
|
|
|
private var biometricCanAuthenticateCode: Int = -1
|
|
|
|
private lateinit var biometricManager: BiometricManager
|
|
|
|
private lateinit var biometricPrompt: BiometricPrompt
|
|
|
|
private lateinit var keyStore: KeyStore
|
|
|
|
private lateinit var key: SecretKey
|
|
|
|
private lateinit var cipher: Cipher
|
2020-11-04 18:42:04 +01:00
|
|
|
private var isCipherReady = false
|
2020-10-22 22:14:22 +02:00
|
|
|
private var actionMode: Int? = null
|
|
|
|
private lateinit var onAuthenticationResult: (success: Boolean) -> Unit
|
|
|
|
private lateinit var onPasswordDecrypted: (password: ByteArray) -> Unit
|
|
|
|
private lateinit var dataToProcess: ByteArray
|
2020-11-04 18:42:04 +01:00
|
|
|
private lateinit var originalHiddenVolumeSectionLayoutParams: LinearLayout.LayoutParams
|
|
|
|
private lateinit var originalNormalVolumeSectionLayoutParams: LinearLayout.LayoutParams
|
2021-06-11 20:23:54 +02:00
|
|
|
protected lateinit var switchHiddenVolume: SwitchCompat
|
|
|
|
protected lateinit var checkboxRememberPath: CheckBox
|
|
|
|
protected lateinit var checkboxSavePassword: CheckBox
|
|
|
|
protected lateinit var editVolumeName: EditText
|
|
|
|
protected lateinit var editVolumePath: EditText
|
|
|
|
private lateinit var hiddenVolumeSection: LinearLayout
|
|
|
|
private lateinit var normalVolumeSection: LinearLayout
|
2020-10-22 22:14:22 +02:00
|
|
|
|
2021-06-07 16:34:50 +02:00
|
|
|
protected fun setupLayout() {
|
2021-06-11 20:23:54 +02:00
|
|
|
setSupportActionBar(findViewById(R.id.toolbar))
|
2021-06-07 16:34:50 +02:00
|
|
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
2021-06-11 20:23:54 +02:00
|
|
|
findViewById<ColoredImageButton>(R.id.button_pick_directory).setOnClickListener {
|
2021-06-07 16:34:50 +02:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) +
|
|
|
|
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE), STORAGE_PERMISSIONS_REQUEST)
|
|
|
|
} else {
|
|
|
|
safePickDirectory()
|
|
|
|
}
|
2021-06-07 14:55:01 +02:00
|
|
|
} else {
|
|
|
|
safePickDirectory()
|
|
|
|
}
|
2021-06-07 16:34:50 +02:00
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
switchHiddenVolume = findViewById(R.id.switch_hidden_volume)
|
|
|
|
checkboxRememberPath = findViewById(R.id.checkbox_remember_path)
|
|
|
|
checkboxSavePassword = findViewById(R.id.checkbox_save_password)
|
|
|
|
editVolumeName = findViewById(R.id.edit_volume_name)
|
|
|
|
editVolumePath = findViewById(R.id.edit_volume_path)
|
|
|
|
hiddenVolumeSection = findViewById(R.id.hidden_volume_section)
|
|
|
|
normalVolumeSection = findViewById(R.id.normal_volume_section)
|
|
|
|
switchHiddenVolume.setOnClickListener {
|
2021-06-07 16:34:50 +02:00
|
|
|
onClickSwitchHiddenVolume()
|
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
checkboxRememberPath.setOnClickListener {
|
|
|
|
if (!checkboxRememberPath.isChecked) {
|
|
|
|
checkboxSavePassword.isChecked = false
|
2021-06-07 16:34:50 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
checkboxSavePassword.setOnClickListener {
|
|
|
|
if (checkboxSavePassword.isChecked) {
|
2021-06-07 16:34:50 +02:00
|
|
|
if (biometricCanAuthenticateCode == 0) {
|
2021-10-19 13:52:06 +02:00
|
|
|
checkboxRememberPath.isChecked = true
|
2021-06-07 16:34:50 +02:00
|
|
|
} else {
|
2021-06-11 20:23:54 +02:00
|
|
|
checkboxSavePassword.isChecked = false
|
2021-06-07 16:34:50 +02:00
|
|
|
printAuthenticateImpossibleError()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected open fun onClickSwitchHiddenVolume() {
|
2021-06-11 20:23:54 +02:00
|
|
|
if (switchHiddenVolume.isChecked){
|
|
|
|
WidgetUtil.show(hiddenVolumeSection, originalHiddenVolumeSectionLayoutParams)
|
|
|
|
WidgetUtil.hide(normalVolumeSection)
|
2021-06-07 14:55:01 +02:00
|
|
|
} else {
|
2021-06-11 20:23:54 +02:00
|
|
|
WidgetUtil.show(normalVolumeSection, originalNormalVolumeSectionLayoutParams)
|
|
|
|
WidgetUtil.hide(hiddenVolumeSection)
|
2021-06-07 14:55:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 16:34:50 +02:00
|
|
|
protected open fun onPickingDirectory() {}
|
2021-10-19 15:54:38 +02:00
|
|
|
protected fun onDirectoryPicked(uri: Uri) {
|
|
|
|
val path = PathUtils.getFullPathFromTreeUri(uri, this)
|
|
|
|
if (path != null) {
|
|
|
|
editVolumePath.setText(path)
|
|
|
|
} else {
|
|
|
|
ColoredAlertDialogBuilder(this)
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(R.string.path_from_uri_null_error_msg)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
}
|
2021-06-07 16:34:50 +02:00
|
|
|
|
2021-06-07 14:55:01 +02:00
|
|
|
private fun safePickDirectory() {
|
2021-06-07 14:12:40 +02:00
|
|
|
try {
|
2021-06-07 14:55:01 +02:00
|
|
|
onPickingDirectory()
|
2021-06-07 14:12:40 +02:00
|
|
|
pickDirectory.launch(null)
|
|
|
|
} catch (e: ActivityNotFoundException) {
|
|
|
|
ColoredAlertDialogBuilder(this)
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(R.string.open_tree_failed)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 14:55:01 +02:00
|
|
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
|
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
|
|
when (requestCode) {
|
|
|
|
STORAGE_PERMISSIONS_REQUEST -> if (grantResults.size == 2) {
|
|
|
|
if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
ColoredAlertDialogBuilder(this)
|
|
|
|
.setTitle(R.string.storage_perm_denied)
|
|
|
|
.setMessage(R.string.storage_perm_denied_msg)
|
|
|
|
.setCancelable(false)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
} else {
|
|
|
|
safePickDirectory()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-19 13:52:06 +02:00
|
|
|
protected fun setupFingerprintStuff(mayDecrypt: Boolean = true) {
|
2021-06-11 20:23:54 +02:00
|
|
|
originalHiddenVolumeSectionLayoutParams = hiddenVolumeSection.layoutParams as LinearLayout.LayoutParams
|
|
|
|
originalNormalVolumeSectionLayoutParams = normalVolumeSection.layoutParams as LinearLayout.LayoutParams
|
|
|
|
WidgetUtil.hide(hiddenVolumeSection)
|
2020-11-04 18:42:04 +01:00
|
|
|
volumeDatabase = VolumeDatabase(this)
|
2020-10-22 22:14:22 +02:00
|
|
|
usf_fingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
|
2021-10-19 13:52:06 +02:00
|
|
|
val marshmallow = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
|
|
|
if (!marshmallow || !usf_fingerprint) {
|
|
|
|
WidgetUtil.hideWithPadding(checkboxSavePassword)
|
|
|
|
}
|
|
|
|
if (marshmallow && (mayDecrypt || usf_fingerprint)) {
|
2020-10-22 22:14:22 +02:00
|
|
|
biometricManager = BiometricManager.from(this)
|
|
|
|
biometricCanAuthenticateCode = canAuthenticate()
|
|
|
|
if (biometricCanAuthenticateCode == 0){
|
|
|
|
val executor = ContextCompat.getMainExecutor(this)
|
|
|
|
val activityContext = this
|
|
|
|
val callback = object: BiometricPrompt.AuthenticationCallback() {
|
|
|
|
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
|
|
|
super.onAuthenticationError(errorCode, errString)
|
|
|
|
Toast.makeText(applicationContext, errString, Toast.LENGTH_SHORT).show()
|
2020-10-23 13:21:14 +02:00
|
|
|
if (actionMode == Cipher.ENCRYPT_MODE){
|
|
|
|
onAuthenticationResult(false)
|
|
|
|
}
|
2020-10-22 22:14:22 +02:00
|
|
|
}
|
|
|
|
override fun onAuthenticationFailed() {
|
|
|
|
super.onAuthenticationFailed()
|
|
|
|
Toast.makeText(applicationContext, R.string.authentication_failed, Toast.LENGTH_SHORT).show()
|
2020-10-23 13:21:14 +02:00
|
|
|
if (actionMode == Cipher.ENCRYPT_MODE){
|
|
|
|
onAuthenticationResult(false)
|
|
|
|
}
|
2020-10-22 22:14:22 +02:00
|
|
|
}
|
|
|
|
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
|
|
|
super.onAuthenticationSucceeded(result)
|
2020-10-23 13:21:14 +02:00
|
|
|
var success = false
|
2020-10-22 22:14:22 +02:00
|
|
|
val cipherObject = result.cryptoObject?.cipher
|
|
|
|
if (cipherObject != null){
|
|
|
|
try {
|
|
|
|
when (actionMode) {
|
|
|
|
Cipher.ENCRYPT_MODE -> {
|
|
|
|
val cipherText = cipherObject.doFinal(dataToProcess)
|
2021-06-11 20:23:54 +02:00
|
|
|
success = volumeDatabase.addHash(Volume(currentVolumeName, switchHiddenVolume.isChecked, cipherText, cipherObject.iv))
|
2020-10-22 22:14:22 +02:00
|
|
|
}
|
|
|
|
Cipher.DECRYPT_MODE -> {
|
|
|
|
try {
|
|
|
|
val plainText = cipherObject.doFinal(dataToProcess)
|
|
|
|
onPasswordDecrypted(plainText)
|
|
|
|
} catch (e: AEADBadTagException){
|
|
|
|
ColoredAlertDialogBuilder(activityContext)
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(R.string.MAC_verification_failed)
|
|
|
|
.setPositiveButton(R.string.reset_hash_storage) { _, _ ->
|
|
|
|
resetHashStorage()
|
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e: IllegalBlockSizeException){
|
|
|
|
ColoredAlertDialogBuilder(activityContext)
|
|
|
|
.setTitle(R.string.illegal_block_size_exception)
|
|
|
|
.setMessage(R.string.illegal_block_size_exception_msg)
|
|
|
|
.setPositiveButton(R.string.reset_hash_storage) { _, _ ->
|
|
|
|
resetHashStorage()
|
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Toast.makeText(applicationContext, R.string.error_cipher_null, Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
2020-10-23 13:21:14 +02:00
|
|
|
if (actionMode == Cipher.ENCRYPT_MODE){
|
|
|
|
onAuthenticationResult(success)
|
|
|
|
}
|
2020-10-22 22:14:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
biometricPrompt = BiometricPrompt(this, executor, callback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun canAuthenticate(): Int {
|
|
|
|
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
|
|
|
return if (!keyguardManager.isKeyguardSecure) {
|
|
|
|
1
|
|
|
|
} else {
|
2021-06-07 14:12:40 +02:00
|
|
|
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)){
|
2020-10-22 22:14:22 +02:00
|
|
|
BiometricManager.BIOMETRIC_SUCCESS -> 0
|
|
|
|
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> 2
|
|
|
|
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> 3
|
|
|
|
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> 4
|
|
|
|
else -> -1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun printAuthenticateImpossibleError() {
|
|
|
|
Toast.makeText(this, when (biometricCanAuthenticateCode){
|
|
|
|
1 -> R.string.fingerprint_error_no_fingerprints
|
|
|
|
2 -> R.string.fingerprint_error_hw_not_present
|
|
|
|
3 -> R.string.fingerprint_error_hw_not_available
|
|
|
|
4 -> R.string.fingerprint_error_no_fingerprints
|
|
|
|
else -> R.string.error
|
|
|
|
}, Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
|
|
|
|
|
|
|
@RequiresApi(Build.VERSION_CODES.M)
|
|
|
|
private fun prepareCipher() {
|
|
|
|
keyStore = KeyStore.getInstance(ANDROID_KEY_STORE)
|
|
|
|
keyStore.load(null)
|
|
|
|
key = if (keyStore.containsAlias(KEY_ALIAS)){
|
|
|
|
keyStore.getKey(KEY_ALIAS, null) as SecretKey
|
|
|
|
} else {
|
|
|
|
val builder = KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
|
|
|
|
builder.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
|
|
|
builder.setKeySize(KEY_SIZE)
|
|
|
|
builder.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
|
|
|
builder.setUserAuthenticationRequired(true)
|
|
|
|
val keyGenerator = KeyGenerator.getInstance(
|
|
|
|
KeyProperties.KEY_ALGORITHM_AES,
|
|
|
|
ANDROID_KEY_STORE
|
|
|
|
)
|
|
|
|
keyGenerator.init(builder.build())
|
|
|
|
keyGenerator.generateKey()
|
|
|
|
}
|
|
|
|
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES+"/"+KeyProperties.BLOCK_MODE_GCM+"/"+KeyProperties.ENCRYPTION_PADDING_NONE)
|
2020-11-04 18:42:04 +01:00
|
|
|
isCipherReady = true
|
2020-10-22 22:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun alertKeyPermanentlyInvalidatedException(){
|
|
|
|
ColoredAlertDialogBuilder(this)
|
|
|
|
.setTitle(R.string.key_permanently_invalidated_exception)
|
|
|
|
.setMessage(R.string.key_permanently_invalidated_exception_msg)
|
|
|
|
.setPositiveButton(R.string.reset_hash_storage) { _, _ ->
|
|
|
|
resetHashStorage()
|
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
|
|
|
|
@RequiresApi(Build.VERSION_CODES.M)
|
|
|
|
protected fun savePasswordHash(plainText: ByteArray, onAuthenticationResult: (success: Boolean) -> Unit){
|
|
|
|
val biometricPromptInfo = BiometricPrompt.PromptInfo.Builder()
|
2020-11-04 18:42:04 +01:00
|
|
|
.setTitle(currentVolumeName)
|
2020-10-22 22:14:22 +02:00
|
|
|
.setSubtitle(getString(R.string.encrypt_action_description))
|
|
|
|
.setDescription(getString(R.string.fingerprint_instruction))
|
|
|
|
.setNegativeButtonText(getString(R.string.cancel))
|
2021-06-07 14:12:40 +02:00
|
|
|
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
|
2020-10-22 22:14:22 +02:00
|
|
|
.setConfirmationRequired(false)
|
|
|
|
.build()
|
2020-11-04 18:42:04 +01:00
|
|
|
if (!isCipherReady){
|
2020-10-22 22:14:22 +02:00
|
|
|
prepareCipher()
|
|
|
|
}
|
|
|
|
actionMode = Cipher.ENCRYPT_MODE
|
|
|
|
try {
|
|
|
|
cipher.init(Cipher.ENCRYPT_MODE, key)
|
|
|
|
this.onAuthenticationResult = onAuthenticationResult
|
|
|
|
dataToProcess = plainText
|
|
|
|
biometricPrompt.authenticate(biometricPromptInfo, BiometricPrompt.CryptoObject(cipher))
|
|
|
|
} catch (e: KeyPermanentlyInvalidatedException){
|
|
|
|
alertKeyPermanentlyInvalidatedException()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@RequiresApi(Build.VERSION_CODES.M)
|
2020-11-04 18:42:04 +01:00
|
|
|
protected fun loadPasswordHash(cipherText: ByteArray, iv: ByteArray, onPasswordDecrypted: (password: ByteArray) -> Unit){
|
2020-10-22 22:14:22 +02:00
|
|
|
val biometricPromptInfo = BiometricPrompt.PromptInfo.Builder()
|
2020-11-04 18:42:04 +01:00
|
|
|
.setTitle(currentVolumeName)
|
2020-10-22 22:14:22 +02:00
|
|
|
.setSubtitle(getString(R.string.decrypt_action_description))
|
|
|
|
.setDescription(getString(R.string.fingerprint_instruction))
|
|
|
|
.setNegativeButtonText(getString(R.string.cancel))
|
2021-06-07 14:12:40 +02:00
|
|
|
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
|
2020-10-22 22:14:22 +02:00
|
|
|
.setConfirmationRequired(false)
|
|
|
|
.build()
|
|
|
|
this.onPasswordDecrypted = onPasswordDecrypted
|
|
|
|
actionMode = Cipher.DECRYPT_MODE
|
2020-11-04 18:42:04 +01:00
|
|
|
if (!isCipherReady){
|
2020-10-22 22:14:22 +02:00
|
|
|
prepareCipher()
|
|
|
|
}
|
2020-11-04 18:42:04 +01:00
|
|
|
dataToProcess = cipherText
|
2020-10-22 22:14:22 +02:00
|
|
|
val gcmSpec = GCMParameterSpec(GCM_TAG_LEN, iv)
|
|
|
|
try {
|
|
|
|
cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec)
|
|
|
|
biometricPrompt.authenticate(biometricPromptInfo, BiometricPrompt.CryptoObject(cipher))
|
|
|
|
} catch (e: KeyPermanentlyInvalidatedException){
|
|
|
|
alertKeyPermanentlyInvalidatedException()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun resetHashStorage() {
|
|
|
|
keyStore.deleteEntry(KEY_ALIAS)
|
2020-11-04 18:42:04 +01:00
|
|
|
volumeDatabase.getVolumes().forEach { volume ->
|
|
|
|
volumeDatabase.removeHash(volume)
|
2020-10-22 22:14:22 +02:00
|
|
|
}
|
2020-11-04 18:42:04 +01:00
|
|
|
isCipherReady = false
|
2020-10-22 22:14:22 +02:00
|
|
|
Toast.makeText(this, R.string.hash_storage_reset, Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
2020-11-04 18:42:04 +01:00
|
|
|
|
|
|
|
protected fun loadVolumePath(callback: () -> Unit){
|
2021-06-11 20:23:54 +02:00
|
|
|
currentVolumeName = if (switchHiddenVolume.isChecked){
|
|
|
|
editVolumeName.text.toString()
|
2020-11-04 18:42:04 +01:00
|
|
|
} else {
|
2021-06-11 20:23:54 +02:00
|
|
|
editVolumePath.text.toString()
|
2020-11-04 18:42:04 +01:00
|
|
|
}
|
|
|
|
if (currentVolumeName.isEmpty()) {
|
2021-06-11 20:23:54 +02:00
|
|
|
Toast.makeText(this, if (switchHiddenVolume.isChecked) {R.string.enter_volume_name} else {R.string.enter_volume_path}, Toast.LENGTH_SHORT).show()
|
|
|
|
} else if (switchHiddenVolume.isChecked && currentVolumeName.contains("/")){
|
2020-11-04 18:42:04 +01:00
|
|
|
Toast.makeText(this, R.string.error_slash_in_name, Toast.LENGTH_SHORT).show()
|
|
|
|
} else {
|
2021-06-11 20:23:54 +02:00
|
|
|
currentVolumePath = if (switchHiddenVolume.isChecked) {
|
2020-11-04 18:42:04 +01:00
|
|
|
PathUtils.pathJoin(filesDir.path, currentVolumeName)
|
|
|
|
} else {
|
|
|
|
currentVolumeName
|
|
|
|
}
|
|
|
|
callback()
|
|
|
|
}
|
|
|
|
}
|
2021-10-19 15:54:38 +02:00
|
|
|
|
|
|
|
fun errorDirectoryNotWritable(errorMsg: Int) {
|
|
|
|
val dialog = ColoredAlertDialogBuilder(this)
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
if (PathUtils.isPathOnExternalStorage(currentVolumePath, this)) {
|
|
|
|
dialog.setView(
|
|
|
|
layoutInflater.inflate(R.layout.dialog_sdcard_error, null).apply {
|
|
|
|
findViewById<TextView>(R.id.path).text = PathUtils.getPackageDataFolder(this@VolumeActionActivity)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
dialog.setMessage(errorMsg)
|
|
|
|
}
|
|
|
|
dialog.show()
|
|
|
|
}
|
2020-10-22 22:14:22 +02:00
|
|
|
}
|