DroidFS/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt

189 lines
8.2 KiB
Kotlin

package sushi.hardcore.droidfs
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.text.InputType
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import sushi.hardcore.droidfs.databinding.ActivityChangePasswordBinding
import sushi.hardcore.droidfs.filesystems.CryfsVolume
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
import sushi.hardcore.droidfs.filesystems.GocryptfsVolume
import sushi.hardcore.droidfs.util.ObjRef
import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.util.*
class ChangePasswordActivity: BaseActivity() {
private lateinit var binding: ActivityChangePasswordBinding
private lateinit var volume: SavedVolume
private lateinit var volumeDatabase: VolumeDatabase
private var fingerprintProtector: FingerprintProtector? = null
private var usfFingerprint: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
volume = intent.getParcelableExtra("volume")!!
binding = ActivityChangePasswordBinding.inflate(layoutInflater)
setContentView(binding.root)
title = getString(R.string.change_password)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
binding.textVolumeName.text = volume.name
volumeDatabase = VolumeDatabase(this)
usfFingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
fingerprintProtector = FingerprintProtector.new(this, themeValue, volumeDatabase)
if (fingerprintProtector != null && volume.encryptedHash != null) {
binding.textCurrentPasswordLabel.visibility = View.GONE
binding.editCurrentPassword.visibility = View.GONE
}
}
if (!usfFingerprint || fingerprintProtector == null) {
binding.checkboxSavePassword.visibility = View.GONE
}
if (sharedPrefs.getBoolean(ConstValues.PIN_PASSWORDS_KEY, false)) {
arrayOf(binding.editCurrentPassword, binding.editNewPassword, binding.editPasswordConfirm).forEach {
it.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
}
}
binding.editPasswordConfirm.setOnEditorActionListener { _, _, _ ->
changeVolumePassword()
true
}
binding.button.setOnClickListener { changeVolumePassword() }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == android.R.id.home) {
finish()
true
} else super.onOptionsItemSelected(item)
}
private fun showCurrentPasswordInput() {
binding.textCurrentPasswordLabel.visibility = View.VISIBLE
binding.editCurrentPassword.visibility = View.VISIBLE
}
private fun changeVolumePassword() {
val newPassword = WidgetUtil.encodeEditTextContent(binding.editNewPassword)
val newPasswordConfirm = WidgetUtil.encodeEditTextContent(binding.editPasswordConfirm)
@SuppressLint("NewApi")
if (!newPassword.contentEquals(newPasswordConfirm)) {
Toast.makeText(this, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show()
Arrays.fill(newPassword, 0)
} else {
var changeWithCurrentPassword = true
volume.encryptedHash?.let { encryptedHash ->
volume.iv?.let { iv ->
fingerprintProtector?.let {
changeWithCurrentPassword = false
it.listener = object : FingerprintProtector.Listener {
override fun onHashStorageReset() {
showCurrentPasswordInput()
volume.encryptedHash = null
volume.iv = null
}
override fun onPasswordHashDecrypted(hash: ByteArray) {
changeVolumePassword(newPassword, hash)
}
override fun onPasswordHashSaved() {}
override fun onFailed(pending: Boolean) {
Arrays.fill(newPassword, 0)
}
}
it.loadPasswordHash(volume.name, encryptedHash, iv)
}
}
}
if (changeWithCurrentPassword) {
changeVolumePassword(newPassword)
}
}
Arrays.fill(newPasswordConfirm, 0)
}
private fun changeVolumePassword(newPassword: ByteArray, givenHash: ByteArray? = null) {
val returnedHash: ObjRef<ByteArray?>? = if (binding.checkboxSavePassword.isChecked) {
ObjRef(null)
} else {
null
}
val currentPassword = if (givenHash == null) {
WidgetUtil.encodeEditTextContent(binding.editCurrentPassword)
} else {
null
}
object : LoadingTask<Boolean>(this, themeValue, R.string.loading_msg_change_password) {
override suspend fun doTask(): Boolean {
val success = if (volume.type == EncryptedVolume.GOCRYPTFS_VOLUME_TYPE) {
GocryptfsVolume.changePassword(
volume.getFullPath(filesDir.path),
currentPassword,
givenHash,
newPassword,
returnedHash?.apply { value = ByteArray(GocryptfsVolume.KeyLen) }?.value
)
} else {
CryfsVolume.changePassword(
volume.getFullPath(filesDir.path),
filesDir.path,
currentPassword,
givenHash,
newPassword,
returnedHash
)
}
if (success) {
if (volumeDatabase.isHashSaved(volume.name)) {
volumeDatabase.removeHash(volume)
}
}
if (currentPassword != null)
Arrays.fill(currentPassword, 0)
Arrays.fill(newPassword, 0)
if (givenHash != null)
Arrays.fill(givenHash, 0)
return success
}
}.startTask(lifecycleScope) { success ->
if (success) {
@SuppressLint("NewApi") // if fingerprintProtector is null checkboxSavePassword is hidden
if (binding.checkboxSavePassword.isChecked && returnedHash != null) {
fingerprintProtector!!.let {
it.listener = object : FingerprintProtector.Listener {
override fun onHashStorageReset() {
// retry
it.savePasswordHash(volume, returnedHash.value!!)
}
override fun onPasswordHashDecrypted(hash: ByteArray) {}
override fun onPasswordHashSaved() {
Arrays.fill(returnedHash.value!!, 0)
finish()
}
override fun onFailed(pending: Boolean) {
if (!pending) {
Arrays.fill(returnedHash.value!!, 0)
finish()
}
}
}
it.savePasswordHash(volume, returnedHash.value!!)
}
} else {
finish()
}
} else {
CustomAlertDialogBuilder(this, themeValue)
.setTitle(R.string.error)
.setMessage(R.string.change_password_failed)
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
}