Allow changing password of CryFS volumes

This commit is contained in:
Matéo Duparc 2022-06-30 21:43:40 +02:00
parent 1a21a43f05
commit d1ca164934
Signed by untrusted user: hardcoresushi
GPG Key ID: AFE384344A45E13A
6 changed files with 76 additions and 29 deletions

@ -1 +1 @@
Subproject commit cf822d6a5bb0bb3492ec350d2648f14c859f846f Subproject commit c5be03ad3abea3aef5b0fcc5aa3af8cba2befdd1

View File

@ -9,7 +9,11 @@ import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import sushi.hardcore.droidfs.databinding.ActivityChangePasswordBinding 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.filesystems.GocryptfsVolume
import sushi.hardcore.droidfs.util.ObjRef
import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.util.* import java.util.*
@ -66,14 +70,12 @@ class ChangePasswordActivity: BaseActivity() {
} }
private fun changeVolumePassword() { private fun changeVolumePassword() {
val newPassword = CharArray(binding.editNewPassword.text.length) val newPassword = WidgetUtil.encodeEditTextContent(binding.editNewPassword)
binding.editNewPassword.text.getChars(0, newPassword.size, newPassword, 0) val newPasswordConfirm = WidgetUtil.encodeEditTextContent(binding.editPasswordConfirm)
val newPasswordConfirm = CharArray(binding.editPasswordConfirm.text.length)
binding.editPasswordConfirm.text.getChars(0, newPasswordConfirm.size, newPasswordConfirm, 0)
@SuppressLint("NewApi") @SuppressLint("NewApi")
if (!newPassword.contentEquals(newPasswordConfirm)) { if (!newPassword.contentEquals(newPasswordConfirm)) {
Toast.makeText(this, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show()
Arrays.fill(newPassword, 0.toChar()) Arrays.fill(newPassword, 0)
} else { } else {
var changeWithCurrentPassword = true var changeWithCurrentPassword = true
volume.encryptedHash?.let { encryptedHash -> volume.encryptedHash?.let { encryptedHash ->
@ -91,7 +93,7 @@ class ChangePasswordActivity: BaseActivity() {
} }
override fun onPasswordHashSaved() {} override fun onPasswordHashSaved() {}
override fun onFailed(pending: Boolean) { override fun onFailed(pending: Boolean) {
Arrays.fill(newPassword, 0.toChar()) Arrays.fill(newPassword, 0)
} }
} }
it.loadPasswordHash(volume.name, encryptedHash, iv) it.loadPasswordHash(volume.name, encryptedHash, iv)
@ -102,30 +104,48 @@ class ChangePasswordActivity: BaseActivity() {
changeVolumePassword(newPassword) changeVolumePassword(newPassword)
} }
} }
Arrays.fill(newPasswordConfirm, 0.toChar()) Arrays.fill(newPasswordConfirm, 0)
} }
private fun changeVolumePassword(newPassword: CharArray, givenHash: ByteArray? = null) { private fun changeVolumePassword(newPassword: ByteArray, givenHash: ByteArray? = null) {
var returnedHash: ByteArray? = null val returnedHash: ObjRef<ByteArray?>? = if (binding.checkboxSavePassword.isChecked) {
if (binding.checkboxSavePassword.isChecked) { ObjRef(null)
returnedHash = ByteArray(GocryptfsVolume.KeyLen) } else {
null
} }
var currentPassword: CharArray? = null val currentPassword = if (givenHash == null) {
if (givenHash == null) { WidgetUtil.encodeEditTextContent(binding.editCurrentPassword)
currentPassword = CharArray(binding.editCurrentPassword.text.length) } else {
binding.editCurrentPassword.text.getChars(0, currentPassword.size, currentPassword, 0) null
} }
object : LoadingTask<Boolean>(this, themeValue, R.string.loading_msg_change_password) { object : LoadingTask<Boolean>(this, themeValue, R.string.loading_msg_change_password) {
override suspend fun doTask(): Boolean { override suspend fun doTask(): Boolean {
val success = GocryptfsVolume.changePassword(volume.getFullPath(filesDir.path), currentPassword, givenHash, newPassword, returnedHash) 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 (success) {
if (volumeDatabase.isHashSaved(volume.name)) { if (volumeDatabase.isHashSaved(volume.name)) {
volumeDatabase.removeHash(volume) volumeDatabase.removeHash(volume)
} }
} }
if (currentPassword != null) if (currentPassword != null)
Arrays.fill(currentPassword, 0.toChar()) Arrays.fill(currentPassword, 0)
Arrays.fill(newPassword, 0.toChar()) Arrays.fill(newPassword, 0)
if (givenHash != null) if (givenHash != null)
Arrays.fill(givenHash, 0) Arrays.fill(givenHash, 0)
return success return success
@ -138,21 +158,21 @@ class ChangePasswordActivity: BaseActivity() {
it.listener = object : FingerprintProtector.Listener { it.listener = object : FingerprintProtector.Listener {
override fun onHashStorageReset() { override fun onHashStorageReset() {
// retry // retry
it.savePasswordHash(volume, returnedHash) it.savePasswordHash(volume, returnedHash.value!!)
} }
override fun onPasswordHashDecrypted(hash: ByteArray) {} override fun onPasswordHashDecrypted(hash: ByteArray) {}
override fun onPasswordHashSaved() { override fun onPasswordHashSaved() {
Arrays.fill(returnedHash, 0) Arrays.fill(returnedHash.value!!, 0)
finish() finish()
} }
override fun onFailed(pending: Boolean) { override fun onFailed(pending: Boolean) {
if (!pending) { if (!pending) {
Arrays.fill(returnedHash, 0) Arrays.fill(returnedHash.value!!, 0)
finish() finish()
} }
} }
} }
it.savePasswordHash(volume, returnedHash) it.savePasswordHash(volume, returnedHash.value!!)
} }
} else { } else {
finish() finish()

View File

@ -342,11 +342,7 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
val onlyOneAndWriteable = val onlyOneAndWriteable =
onlyOneSelected && onlyOneSelected &&
volumeAdapter.volumes[volumeAdapter.selectedItems.first()].canWrite(filesDir.path) volumeAdapter.volumes[volumeAdapter.selectedItems.first()].canWrite(filesDir.path)
menu.findItem(R.id.change_password).isVisible = menu.findItem(R.id.change_password).isVisible = onlyOneAndWriteable
onlyOneAndWriteable &&
// Only gocryptfs volumes support password change
!BuildConfig.GOCRYPTFS_DISABLED &&
volumeAdapter.volumes[volumeAdapter.selectedItems.first()].type == EncryptedVolume.GOCRYPTFS_VOLUME_TYPE
menu.findItem(R.id.remove_default_open).isVisible = menu.findItem(R.id.remove_default_open).isVisible =
onlyOneSelected && onlyOneSelected &&
volumeAdapter.volumes[volumeAdapter.selectedItems.first()].name == defaultVolumeName volumeAdapter.volumes[volumeAdapter.selectedItems.first()].name == defaultVolumeName

View File

@ -23,6 +23,14 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
createBaseDir: Boolean, createBaseDir: Boolean,
cipher: String? cipher: String?
): Long ): Long
private external fun nativeChangeEncryptionKey(
baseDir: String,
localStateDir: String,
currentPassword: ByteArray?,
givenHash: ByteArray?,
newPassword: ByteArray,
returnedHash: ObjRef<ByteArray?>?
): Boolean
private external fun nativeCreate(fusePtr: Long, path: String, mode: Int): Long private external fun nativeCreate(fusePtr: Long, path: String, mode: Int): Long
private external fun nativeOpen(fusePtr: Long, path: String, flags: Int): Long private external fun nativeOpen(fusePtr: Long, path: String, flags: Int): Long
private external fun nativeRead(fusePtr: Long, fileHandle: Long, buffer: ByteArray, offset: Long): Int private external fun nativeRead(fusePtr: Long, fileHandle: Long, buffer: ByteArray, offset: Long): Int
@ -66,6 +74,15 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
fun init(baseDir: String, localStateDir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ObjRef<ByteArray?>?): CryfsVolume? { fun init(baseDir: String, localStateDir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ObjRef<ByteArray?>?): CryfsVolume? {
return init(baseDir, localStateDir, password, givenHash, returnedHash, false, null) return init(baseDir, localStateDir, password, givenHash, returnedHash, false, null)
} }
fun changePassword(
baseDir: String, filesDir: String, currentPassword: ByteArray?,
givenHash: ByteArray?,
newPassword: ByteArray,
returnedHash: ObjRef<ByteArray?>?
): Boolean {
return nativeChangeEncryptionKey(baseDir, getLocalStateDir(filesDir), currentPassword, givenHash, newPassword, returnedHash)
}
} }
constructor(parcel: Parcel) : this(parcel.readLong()) constructor(parcel: Parcel) : this(parcel.readLong())

View File

@ -25,7 +25,13 @@ class GocryptfsVolume(private val sessionID: Int): EncryptedVolume() {
const val CONFIG_FILE_NAME = "gocryptfs.conf" const val CONFIG_FILE_NAME = "gocryptfs.conf"
external fun createVolume(root_cipher_dir: String, password: ByteArray, plainTextNames: Boolean, xchacha: Int, logN: Int, creator: String, returnedHash: ByteArray?): Boolean external fun createVolume(root_cipher_dir: String, password: ByteArray, plainTextNames: Boolean, xchacha: Int, logN: Int, creator: String, returnedHash: ByteArray?): Boolean
private external fun nativeInit(root_cipher_dir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ByteArray?): Int private external fun nativeInit(root_cipher_dir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ByteArray?): Int
external fun changePassword(root_cipher_dir: String, old_password: CharArray?, givenHash: ByteArray?, new_password: CharArray, returnedHash: ByteArray?): Boolean external fun changePassword(
root_cipher_dir: String,
currentPassword: ByteArray?,
givenHash: ByteArray?,
newPassword: ByteArray,
returnedHash: ByteArray?
): Boolean
fun init(root_cipher_dir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ByteArray?): GocryptfsVolume? { fun init(root_cipher_dir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ByteArray?): GocryptfsVolume? {
val sessionId = nativeInit(root_cipher_dir, password, givenHash, returnedHash) val sessionId = nativeInit(root_cipher_dir, password, givenHash, returnedHash)

View File

@ -12,6 +12,14 @@ Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeInit(JN
return cryfs_init(env, base_dir, jlocalStateDir, password, givenHash, returnedHash, createBaseDir, cipher); return cryfs_init(env, base_dir, jlocalStateDir, password, givenHash, returnedHash, createBaseDir, cipher);
} }
JNIEXPORT jboolean JNICALL
Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeChangeEncryptionKey(
JNIEnv *env, jobject thiz, jstring base_dir, jstring local_state_dir,
jbyteArray current_password, jbyteArray given_hash, jbyteArray new_password,
jobject returned_hash) {
return cryfs_change_encryption_key(env, base_dir, local_state_dir, current_password, given_hash, new_password, returned_hash);
}
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeCreate(JNIEnv *env, jobject thiz, Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeCreate(JNIEnv *env, jobject thiz,
jlong fuse_ptr, jstring path, jlong fuse_ptr, jstring path,