Allow opening CryFS volumes with password hash
This commit is contained in:
parent
cf4927a90b
commit
e01932acda
@ -1 +1 @@
|
|||||||
Subproject commit 356cf8a1604776cb2cc4f4ff873936f7b396bd49
|
Subproject commit cf822d6a5bb0bb3492ec350d2648f14c859f846f
|
@ -31,7 +31,7 @@ import sushi.hardcore.droidfs.explorers.ExplorerActivityDrop
|
|||||||
import sushi.hardcore.droidfs.explorers.ExplorerActivityPick
|
import sushi.hardcore.droidfs.explorers.ExplorerActivityPick
|
||||||
import sushi.hardcore.droidfs.file_operations.FileOperationService
|
import sushi.hardcore.droidfs.file_operations.FileOperationService
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
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.PathUtils
|
||||||
import sushi.hardcore.droidfs.util.WidgetUtil
|
import sushi.hardcore.droidfs.util.WidgetUtil
|
||||||
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
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) {
|
private fun askForPassword(volume: SavedVolume, position: Int, savePasswordHash: Boolean = false) {
|
||||||
val dialogBinding = DialogOpenVolumeBinding.inflate(layoutInflater)
|
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
|
dialogBinding.checkboxSavePassword.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
dialogBinding.checkboxSavePassword.isChecked = savePasswordHash
|
dialogBinding.checkboxSavePassword.isChecked = savePasswordHash
|
||||||
@ -564,10 +564,10 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun openVolumeWithPassword(volume: SavedVolume, position: Int, password: ByteArray, savePasswordHash: Boolean) {
|
private fun openVolumeWithPassword(volume: SavedVolume, position: Int, password: ByteArray, savePasswordHash: Boolean) {
|
||||||
val usfFingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
|
val returnedHash: ObjRef<ByteArray?>? = if (savePasswordHash) {
|
||||||
var returnedHash: ByteArray? = null
|
ObjRef(null)
|
||||||
if (savePasswordHash && usfFingerprint) {
|
} else {
|
||||||
returnedHash = ByteArray(GocryptfsVolume.KeyLen)
|
null
|
||||||
}
|
}
|
||||||
object : LoadingTask<EncryptedVolume?>(this, themeValue, R.string.loading_msg_open) {
|
object : LoadingTask<EncryptedVolume?>(this, themeValue, R.string.loading_msg_open) {
|
||||||
override suspend fun doTask(): EncryptedVolume? {
|
override suspend fun doTask(): EncryptedVolume? {
|
||||||
@ -594,7 +594,7 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
}
|
}
|
||||||
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)
|
||||||
volumeAdapter.onVolumeChanged(position)
|
volumeAdapter.onVolumeChanged(position)
|
||||||
startExplorer(encryptedVolume, volume.shortName)
|
startExplorer(encryptedVolume, volume.shortName)
|
||||||
}
|
}
|
||||||
@ -604,10 +604,10 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
encryptedVolume.close()
|
encryptedVolume.close()
|
||||||
isClosed = true
|
isClosed = true
|
||||||
}
|
}
|
||||||
Arrays.fill(returnedHash, 0)
|
Arrays.fill(returnedHash.value!!, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fingerprintProtector.savePasswordHash(volume, returnedHash)
|
fingerprintProtector.savePasswordHash(volume, returnedHash.value!!)
|
||||||
} else {
|
} else {
|
||||||
startExplorer(encryptedVolume, volume.shortName)
|
startExplorer(encryptedVolume, volume.shortName)
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import sushi.hardcore.droidfs.databinding.FragmentCreateVolumeBinding
|
|||||||
import sushi.hardcore.droidfs.filesystems.CryfsVolume
|
import sushi.hardcore.droidfs.filesystems.CryfsVolume
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
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.util.WidgetUtil
|
||||||
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -108,12 +109,8 @@ class CreateVolumeFragment: Fragment() {
|
|||||||
binding.spinnerVolumeType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
binding.spinnerVolumeType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||||
val ciphersArray = if (volumeTypes[position] == resources.getString(R.string.gocryptfs)) {
|
val ciphersArray = if (volumeTypes[position] == resources.getString(R.string.gocryptfs)) {
|
||||||
if (usfFingerprint && fingerprintProtector != null) {
|
|
||||||
binding.checkboxSavePassword.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
R.array.gocryptfs_encryption_ciphers
|
R.array.gocryptfs_encryption_ciphers
|
||||||
} else {
|
} else {
|
||||||
binding.checkboxSavePassword.visibility = View.GONE
|
|
||||||
R.array.cryfs_encryption_ciphers
|
R.array.cryfs_encryption_ciphers
|
||||||
}
|
}
|
||||||
with(encryptionCipherAdapter) {
|
with(encryptionCipherAdapter) {
|
||||||
@ -167,9 +164,11 @@ class CreateVolumeFragment: Fragment() {
|
|||||||
Arrays.fill(passwordConfirm, 0)
|
Arrays.fill(passwordConfirm, 0)
|
||||||
} else {
|
} else {
|
||||||
Arrays.fill(passwordConfirm, 0)
|
Arrays.fill(passwordConfirm, 0)
|
||||||
var returnedHash: ByteArray? = null
|
val returnedHash: ObjRef<ByteArray?>? = if (binding.checkboxSavePassword.isChecked) {
|
||||||
if (binding.checkboxSavePassword.isChecked)
|
ObjRef(null)
|
||||||
returnedHash = ByteArray(GocryptfsVolume.KeyLen)
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
object: LoadingTask<SavedVolume?>(requireActivity() as AppCompatActivity, themeValue, R.string.loading_msg_create) {
|
object: LoadingTask<SavedVolume?>(requireActivity() as AppCompatActivity, themeValue, R.string.loading_msg_create) {
|
||||||
override suspend fun doTask(): SavedVolume? {
|
override suspend fun doTask(): SavedVolume? {
|
||||||
val volumeFile = File(volumePath)
|
val volumeFile = File(volumePath)
|
||||||
@ -188,13 +187,16 @@ class CreateVolumeFragment: Fragment() {
|
|||||||
xchacha,
|
xchacha,
|
||||||
GocryptfsVolume.ScryptDefaultLogN,
|
GocryptfsVolume.ScryptDefaultLogN,
|
||||||
ConstValues.CREATOR,
|
ConstValues.CREATOR,
|
||||||
returnedHash
|
returnedHash?.apply {
|
||||||
|
value = ByteArray(GocryptfsVolume.KeyLen)
|
||||||
|
}?.value,
|
||||||
), EncryptedVolume.GOCRYPTFS_VOLUME_TYPE)
|
), EncryptedVolume.GOCRYPTFS_VOLUME_TYPE)
|
||||||
} else {
|
} else {
|
||||||
saveVolume(CryfsVolume.create(
|
saveVolume(CryfsVolume.create(
|
||||||
volumePath,
|
volumePath,
|
||||||
CryfsVolume.getLocalStateDir(activity.filesDir.path),
|
CryfsVolume.getLocalStateDir(activity.filesDir.path),
|
||||||
password,
|
password,
|
||||||
|
returnedHash,
|
||||||
resources.getStringArray(R.array.cryfs_encryption_ciphers)[binding.spinnerCipher.selectedItemPosition]
|
resources.getStringArray(R.array.cryfs_encryption_ciphers)[binding.spinnerCipher.selectedItemPosition]
|
||||||
), EncryptedVolume.CRYFS_VOLUME_TYPE)
|
), EncryptedVolume.CRYFS_VOLUME_TYPE)
|
||||||
}
|
}
|
||||||
@ -216,21 +218,21 @@ class CreateVolumeFragment: Fragment() {
|
|||||||
override fun onHashStorageReset() {
|
override fun onHashStorageReset() {
|
||||||
hashStorageReset = true
|
hashStorageReset = true
|
||||||
// retry
|
// retry
|
||||||
it.savePasswordHash(volume, returnedHash)
|
it.savePasswordHash(volume, returnedHash.value!!)
|
||||||
}
|
}
|
||||||
override fun onPasswordHashDecrypted(hash: ByteArray) {} // shouldn't happen here
|
override fun onPasswordHashDecrypted(hash: ByteArray) {} // shouldn't happen here
|
||||||
override fun onPasswordHashSaved() {
|
override fun onPasswordHashSaved() {
|
||||||
Arrays.fill(returnedHash, 0)
|
Arrays.fill(returnedHash.value!!, 0)
|
||||||
onVolumeCreated()
|
onVolumeCreated()
|
||||||
}
|
}
|
||||||
override fun onFailed(pending: Boolean) {
|
override fun onFailed(pending: Boolean) {
|
||||||
if (!pending) {
|
if (!pending) {
|
||||||
Arrays.fill(returnedHash, 0)
|
Arrays.fill(returnedHash.value!!, 0)
|
||||||
onVolumeCreated()
|
onVolumeCreated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it.savePasswordHash(volume, returnedHash)
|
it.savePasswordHash(volume, returnedHash.value!!)
|
||||||
}
|
}
|
||||||
} else onVolumeCreated()
|
} else onVolumeCreated()
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import sushi.hardcore.droidfs.ConstValues
|
|||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
||||||
|
import sushi.hardcore.droidfs.util.ObjRef
|
||||||
import sushi.hardcore.droidfs.util.PathUtils
|
import sushi.hardcore.droidfs.util.PathUtils
|
||||||
import sushi.hardcore.droidfs.util.Wiper
|
import sushi.hardcore.droidfs.util.Wiper
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -415,8 +416,6 @@ class FileOperationService : Service() {
|
|||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ObjRef<T>(var value: T)
|
|
||||||
|
|
||||||
private fun recursiveCopyVolume(
|
private fun recursiveCopyVolume(
|
||||||
src: DocumentFile,
|
src: DocumentFile,
|
||||||
dst: DocumentFile,
|
dst: DocumentFile,
|
||||||
|
@ -3,6 +3,7 @@ package sushi.hardcore.droidfs.filesystems
|
|||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import sushi.hardcore.droidfs.ConstValues
|
import sushi.hardcore.droidfs.ConstValues
|
||||||
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
||||||
|
import sushi.hardcore.droidfs.util.ObjRef
|
||||||
import sushi.hardcore.droidfs.util.PathUtils
|
import sushi.hardcore.droidfs.util.PathUtils
|
||||||
|
|
||||||
class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
|
class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
|
||||||
@ -16,7 +17,9 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
|
|||||||
private external fun nativeInit(
|
private external fun nativeInit(
|
||||||
baseDir: String,
|
baseDir: String,
|
||||||
localStateDir: String,
|
localStateDir: String,
|
||||||
password: ByteArray,
|
password: ByteArray?,
|
||||||
|
givenHash: ByteArray?,
|
||||||
|
returnedHash: ObjRef<ByteArray?>?,
|
||||||
createBaseDir: Boolean,
|
createBaseDir: Boolean,
|
||||||
cipher: String?
|
cipher: String?
|
||||||
): Long
|
): Long
|
||||||
@ -39,8 +42,16 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
|
|||||||
return PathUtils.pathJoin(filesDir, ConstValues.CRYFS_LOCAL_STATE_DIR)
|
return PathUtils.pathJoin(filesDir, ConstValues.CRYFS_LOCAL_STATE_DIR)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun init(baseDir: String, localStateDir: String, password: ByteArray, createBaseDir: Boolean, cipher: String?): CryfsVolume? {
|
private fun init(
|
||||||
val fusePtr = nativeInit(baseDir, localStateDir, password, createBaseDir, cipher)
|
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) {
|
return if (fusePtr == 0L) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
@ -48,12 +59,12 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create(baseDir: String, localStateDir: String, password: ByteArray, cipher: String?): Boolean {
|
fun create(baseDir: String, localStateDir: String, password: ByteArray, returnedHash: ObjRef<ByteArray?>?, cipher: String?): Boolean {
|
||||||
return init(baseDir, localStateDir, password, true, cipher)?.also { it.close() } != null
|
return init(baseDir, localStateDir, password, null, returnedHash, true, cipher)?.also { it.close() } != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun init(baseDir: String, localStateDir: String, password: ByteArray): CryfsVolume? {
|
fun init(baseDir: String, localStateDir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ObjRef<ByteArray?>?): CryfsVolume? {
|
||||||
return init(baseDir, localStateDir, password, false, null)
|
return init(baseDir, localStateDir, password, givenHash, returnedHash, false, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import android.os.Parcelable
|
|||||||
import sushi.hardcore.droidfs.ConstValues
|
import sushi.hardcore.droidfs.ConstValues
|
||||||
import sushi.hardcore.droidfs.SavedVolume
|
import sushi.hardcore.droidfs.SavedVolume
|
||||||
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
||||||
|
import sushi.hardcore.droidfs.util.ObjRef
|
||||||
import sushi.hardcore.droidfs.util.PathUtils
|
import sushi.hardcore.droidfs.util.PathUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
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) {
|
return when (volume.type) {
|
||||||
GOCRYPTFS_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 -> {
|
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()
|
else -> throw invalidVolumeType()
|
||||||
}
|
}
|
||||||
|
3
app/src/main/java/sushi/hardcore/droidfs/util/ObjRef.kt
Normal file
3
app/src/main/java/sushi/hardcore/droidfs/util/ObjRef.kt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package sushi.hardcore.droidfs.util
|
||||||
|
|
||||||
|
class ObjRef<T>(var value: T)
|
@ -5,9 +5,11 @@
|
|||||||
JNIEXPORT jlong JNICALL
|
JNIEXPORT jlong JNICALL
|
||||||
Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeInit(JNIEnv *env, jobject thiz,
|
Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeInit(JNIEnv *env, jobject thiz,
|
||||||
jstring base_dir, jstring jlocalStateDir,
|
jstring base_dir, jstring jlocalStateDir,
|
||||||
jbyteArray password, jboolean createBaseDir,
|
jbyteArray password, jbyteArray givenHash,
|
||||||
|
jobject returnedHash,
|
||||||
|
jboolean createBaseDir,
|
||||||
jstring cipher) {
|
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
|
JNIEXPORT jlong JNICALL
|
||||||
|
@ -17,16 +17,16 @@
|
|||||||
<requestFocus/>
|
<requestFocus/>
|
||||||
</EditText>
|
</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
|
<CheckBox
|
||||||
android:id="@+id/checkbox_default_open"
|
android:id="@+id/checkbox_default_open"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/default_open"/>
|
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>
|
</LinearLayout>
|
Loading…
Reference in New Issue
Block a user