Allow opening CryFS volumes with password hash

This commit is contained in:
Matéo Duparc 2022-06-29 13:43:56 +02:00
parent cf4927a90b
commit e01932acda
Signed by: hardcoresushi
GPG Key ID: AFE384344A45E13A
9 changed files with 73 additions and 42 deletions

@ -1 +1 @@
Subproject commit 356cf8a1604776cb2cc4f4ff873936f7b396bd49
Subproject commit cf822d6a5bb0bb3492ec350d2648f14c859f846f

View File

@ -31,7 +31,7 @@ import sushi.hardcore.droidfs.explorers.ExplorerActivityDrop
import sushi.hardcore.droidfs.explorers.ExplorerActivityPick
import sushi.hardcore.droidfs.file_operations.FileOperationService
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
import sushi.hardcore.droidfs.filesystems.GocryptfsVolume
import sushi.hardcore.droidfs.util.ObjRef
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
@ -535,7 +535,7 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
private fun askForPassword(volume: SavedVolume, position: Int, savePasswordHash: Boolean = false) {
val dialogBinding = DialogOpenVolumeBinding.inflate(layoutInflater)
if (!usfFingerprint || fingerprintProtector == null || volume.encryptedHash != null || volume.type == EncryptedVolume.CRYFS_VOLUME_TYPE) {
if (!usfFingerprint || fingerprintProtector == null || volume.encryptedHash != null) {
dialogBinding.checkboxSavePassword.visibility = View.GONE
} else {
dialogBinding.checkboxSavePassword.isChecked = savePasswordHash
@ -564,10 +564,10 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
}
private fun openVolumeWithPassword(volume: SavedVolume, position: Int, password: ByteArray, savePasswordHash: Boolean) {
val usfFingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
var returnedHash: ByteArray? = null
if (savePasswordHash && usfFingerprint) {
returnedHash = ByteArray(GocryptfsVolume.KeyLen)
val returnedHash: ObjRef<ByteArray?>? = if (savePasswordHash) {
ObjRef(null)
} else {
null
}
object : LoadingTask<EncryptedVolume?>(this, themeValue, R.string.loading_msg_open) {
override suspend fun doTask(): EncryptedVolume? {
@ -594,7 +594,7 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
}
override fun onPasswordHashDecrypted(hash: ByteArray) {}
override fun onPasswordHashSaved() {
Arrays.fill(returnedHash, 0)
Arrays.fill(returnedHash.value!!, 0)
volumeAdapter.onVolumeChanged(position)
startExplorer(encryptedVolume, volume.shortName)
}
@ -604,10 +604,10 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
encryptedVolume.close()
isClosed = true
}
Arrays.fill(returnedHash, 0)
Arrays.fill(returnedHash.value!!, 0)
}
}
fingerprintProtector.savePasswordHash(volume, returnedHash)
fingerprintProtector.savePasswordHash(volume, returnedHash.value!!)
} else {
startExplorer(encryptedVolume, volume.shortName)
}

View File

@ -18,6 +18,7 @@ import sushi.hardcore.droidfs.databinding.FragmentCreateVolumeBinding
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.io.File
@ -108,12 +109,8 @@ class CreateVolumeFragment: Fragment() {
binding.spinnerVolumeType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val ciphersArray = if (volumeTypes[position] == resources.getString(R.string.gocryptfs)) {
if (usfFingerprint && fingerprintProtector != null) {
binding.checkboxSavePassword.visibility = View.VISIBLE
}
R.array.gocryptfs_encryption_ciphers
} else {
binding.checkboxSavePassword.visibility = View.GONE
R.array.cryfs_encryption_ciphers
}
with(encryptionCipherAdapter) {
@ -167,9 +164,11 @@ class CreateVolumeFragment: Fragment() {
Arrays.fill(passwordConfirm, 0)
} else {
Arrays.fill(passwordConfirm, 0)
var returnedHash: ByteArray? = null
if (binding.checkboxSavePassword.isChecked)
returnedHash = ByteArray(GocryptfsVolume.KeyLen)
val returnedHash: ObjRef<ByteArray?>? = if (binding.checkboxSavePassword.isChecked) {
ObjRef(null)
} else {
null
}
object: LoadingTask<SavedVolume?>(requireActivity() as AppCompatActivity, themeValue, R.string.loading_msg_create) {
override suspend fun doTask(): SavedVolume? {
val volumeFile = File(volumePath)
@ -188,13 +187,16 @@ class CreateVolumeFragment: Fragment() {
xchacha,
GocryptfsVolume.ScryptDefaultLogN,
ConstValues.CREATOR,
returnedHash
returnedHash?.apply {
value = ByteArray(GocryptfsVolume.KeyLen)
}?.value,
), EncryptedVolume.GOCRYPTFS_VOLUME_TYPE)
} else {
saveVolume(CryfsVolume.create(
volumePath,
CryfsVolume.getLocalStateDir(activity.filesDir.path),
password,
returnedHash,
resources.getStringArray(R.array.cryfs_encryption_ciphers)[binding.spinnerCipher.selectedItemPosition]
), EncryptedVolume.CRYFS_VOLUME_TYPE)
}
@ -216,21 +218,21 @@ class CreateVolumeFragment: Fragment() {
override fun onHashStorageReset() {
hashStorageReset = true
// retry
it.savePasswordHash(volume, returnedHash)
it.savePasswordHash(volume, returnedHash.value!!)
}
override fun onPasswordHashDecrypted(hash: ByteArray) {} // shouldn't happen here
override fun onPasswordHashSaved() {
Arrays.fill(returnedHash, 0)
Arrays.fill(returnedHash.value!!, 0)
onVolumeCreated()
}
override fun onFailed(pending: Boolean) {
if (!pending) {
Arrays.fill(returnedHash, 0)
Arrays.fill(returnedHash.value!!, 0)
onVolumeCreated()
}
}
}
it.savePasswordHash(volume, returnedHash)
it.savePasswordHash(volume, returnedHash.value!!)
}
} else onVolumeCreated()
}

View File

@ -18,6 +18,7 @@ import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.explorers.ExplorerElement
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
import sushi.hardcore.droidfs.util.ObjRef
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.Wiper
import java.io.File
@ -415,8 +416,6 @@ class FileOperationService : Service() {
return count
}
internal class ObjRef<T>(var value: T)
private fun recursiveCopyVolume(
src: DocumentFile,
dst: DocumentFile,

View File

@ -3,6 +3,7 @@ package sushi.hardcore.droidfs.filesystems
import android.os.Parcel
import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.explorers.ExplorerElement
import sushi.hardcore.droidfs.util.ObjRef
import sushi.hardcore.droidfs.util.PathUtils
class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
@ -16,7 +17,9 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
private external fun nativeInit(
baseDir: String,
localStateDir: String,
password: ByteArray,
password: ByteArray?,
givenHash: ByteArray?,
returnedHash: ObjRef<ByteArray?>?,
createBaseDir: Boolean,
cipher: String?
): Long
@ -39,8 +42,16 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
return PathUtils.pathJoin(filesDir, ConstValues.CRYFS_LOCAL_STATE_DIR)
}
private fun init(baseDir: String, localStateDir: String, password: ByteArray, createBaseDir: Boolean, cipher: String?): CryfsVolume? {
val fusePtr = nativeInit(baseDir, localStateDir, password, createBaseDir, cipher)
private fun init(
baseDir: String,
localStateDir: String,
password: ByteArray?,
givenHash: ByteArray?,
returnedHash: ObjRef<ByteArray?>?,
createBaseDir: Boolean,
cipher: String?
): CryfsVolume? {
val fusePtr = nativeInit(baseDir, localStateDir, password, givenHash, returnedHash, createBaseDir, cipher)
return if (fusePtr == 0L) {
null
} else {
@ -48,12 +59,12 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
}
}
fun create(baseDir: String, localStateDir: String, password: ByteArray, cipher: String?): Boolean {
return init(baseDir, localStateDir, password, true, cipher)?.also { it.close() } != null
fun create(baseDir: String, localStateDir: String, password: ByteArray, returnedHash: ObjRef<ByteArray?>?, cipher: String?): Boolean {
return init(baseDir, localStateDir, password, null, returnedHash, true, cipher)?.also { it.close() } != null
}
fun init(baseDir: String, localStateDir: String, password: ByteArray): CryfsVolume? {
return init(baseDir, localStateDir, password, false, null)
fun init(baseDir: String, localStateDir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ObjRef<ByteArray?>?): CryfsVolume? {
return init(baseDir, localStateDir, password, givenHash, returnedHash, false, null)
}
}

View File

@ -7,6 +7,7 @@ import android.os.Parcelable
import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.SavedVolume
import sushi.hardcore.droidfs.explorers.ExplorerElement
import sushi.hardcore.droidfs.util.ObjRef
import sushi.hardcore.droidfs.util.PathUtils
import java.io.File
import java.io.FileOutputStream
@ -40,13 +41,26 @@ abstract class EncryptedVolume: Parcelable {
}
}
fun init(volume: SavedVolume, filesDir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ByteArray?): EncryptedVolume? {
fun init(
volume: SavedVolume,
filesDir: String,
password: ByteArray?,
givenHash: ByteArray?,
returnedHash: ObjRef<ByteArray?>?
): EncryptedVolume? {
return when (volume.type) {
GOCRYPTFS_VOLUME_TYPE -> {
GocryptfsVolume.init(volume.getFullPath(filesDir), password, givenHash, returnedHash)
GocryptfsVolume.init(
volume.getFullPath(filesDir),
password,
givenHash,
returnedHash?.apply {
value = ByteArray(GocryptfsVolume.KeyLen)
}?.value
)
}
CRYFS_VOLUME_TYPE -> {
CryfsVolume.init(volume.getFullPath(filesDir), CryfsVolume.getLocalStateDir(filesDir), password!!)
CryfsVolume.init(volume.getFullPath(filesDir), CryfsVolume.getLocalStateDir(filesDir), password, givenHash, returnedHash)
}
else -> throw invalidVolumeType()
}

View File

@ -0,0 +1,3 @@
package sushi.hardcore.droidfs.util
class ObjRef<T>(var value: T)

View File

@ -5,9 +5,11 @@
JNIEXPORT jlong JNICALL
Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeInit(JNIEnv *env, jobject thiz,
jstring base_dir, jstring jlocalStateDir,
jbyteArray password, jboolean createBaseDir,
jbyteArray password, jbyteArray givenHash,
jobject returnedHash,
jboolean createBaseDir,
jstring cipher) {
return cryfs_init(env, base_dir, jlocalStateDir, password, createBaseDir, cipher);
return cryfs_init(env, base_dir, jlocalStateDir, password, givenHash, returnedHash, createBaseDir, cipher);
}
JNIEXPORT jlong JNICALL

View File

@ -17,16 +17,16 @@
<requestFocus/>
</EditText>
<CheckBox
android:id="@+id/checkbox_save_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fingerprint_save_checkbox_text"/>
<CheckBox
android:id="@+id/checkbox_default_open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/default_open"/>
<CheckBox
android:id="@+id/checkbox_save_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fingerprint_save_checkbox_text"/>
</LinearLayout>