diff --git a/app/libcryfs b/app/libcryfs index f40c2bb..3c56f86 160000 --- a/app/libcryfs +++ b/app/libcryfs @@ -1 +1 @@ -Subproject commit f40c2bbdcde77d8128da181edb72bf0e7a2168b5 +Subproject commit 3c56f86d86afacaf4a07ae77aa3d146764d587ec diff --git a/app/libgocryptfs b/app/libgocryptfs index 79f9a10..6308adf 160000 --- a/app/libgocryptfs +++ b/app/libgocryptfs @@ -1 +1 @@ -Subproject commit 79f9a10e35847e46f8563941345355f15f2dba7c +Subproject commit 6308adf8e57379e1136f4f55ff0d7737c0d0a33b diff --git a/app/src/main/java/sushi/hardcore/droidfs/VolumeData.kt b/app/src/main/java/sushi/hardcore/droidfs/VolumeData.kt index acbd548..ea4f449 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/VolumeData.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/VolumeData.kt @@ -2,8 +2,12 @@ package sushi.hardcore.droidfs import android.os.Parcel import android.os.Parcelable +import sushi.hardcore.droidfs.filesystems.CryfsVolume +import sushi.hardcore.droidfs.filesystems.EncryptedVolume +import sushi.hardcore.droidfs.filesystems.GocryptfsVolume import sushi.hardcore.droidfs.util.PathUtils import java.io.File +import java.io.FileInputStream class VolumeData(val name: String, val isHidden: Boolean = false, val type: Byte, var encryptedHash: ByteArray? = null, var iv: ByteArray? = null): Parcelable { @@ -26,6 +30,28 @@ class VolumeData(val name: String, val isHidden: Boolean = false, val type: Byte name } + fun canRead(filesDir: String): Boolean { + val volumePath = getFullPath(filesDir) + if (!File(volumePath).canRead()) { + return false + } + val configFile = when (type) { + EncryptedVolume.GOCRYPTFS_VOLUME_TYPE -> PathUtils.pathJoin(volumePath, GocryptfsVolume.CONFIG_FILE_NAME) + EncryptedVolume.CRYFS_VOLUME_TYPE -> PathUtils.pathJoin(volumePath, CryfsVolume.CONFIG_FILE_NAME) + else -> return false + } + var success = true + try { + with (FileInputStream(configFile)) { + read() + close() + } + } catch (e: Exception) { + success = false + } + return success + } + fun canWrite(filesDir: String): Boolean { return File(getFullPath(filesDir)).canWrite() } diff --git a/app/src/main/java/sushi/hardcore/droidfs/VolumeOpener.kt b/app/src/main/java/sushi/hardcore/droidfs/VolumeOpener.kt index 60c4d23..7ba5f8a 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/VolumeOpener.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/VolumeOpener.kt @@ -39,6 +39,14 @@ class VolumeOpener( } } + private fun getErrorMsg(result: EncryptedVolume.InitResult): String { + return if (result.errorStringId == 0) { + activity.getString(R.string.unknown_error_code, result.errorCode) + } else { + activity.getString(result.errorStringId) + } + } + @SuppressLint("NewApi") // fingerprintProtector is non-null only when SDK_INT >= 23 fun openVolume(volume: VolumeData, isVolumeSaved: Boolean, callbacks: VolumeOpenerCallbacks) { val volumeId = volumeManager.getVolumeId(volume) @@ -60,17 +68,18 @@ class VolumeOpener( callbacks.onHashStorageReset() } override fun onPasswordHashDecrypted(hash: ByteArray) { - object : LoadingTask(activity, theme, R.string.loading_msg_open) { - override suspend fun doTask(): EncryptedVolume? { - val encryptedVolume = EncryptedVolume.init(volume, activity.filesDir.path, null, hash, null) + object : LoadingTask(activity, theme, R.string.loading_msg_open) { + override suspend fun doTask(): EncryptedVolume.InitResult { + val result = EncryptedVolume.init(volume, activity.filesDir.path, null, hash, null) Arrays.fill(hash, 0) - return encryptedVolume + return result } - }.startTask(activity.lifecycleScope) { encryptedVolume -> + }.startTask(activity.lifecycleScope) { result -> + val encryptedVolume = result.volume if (encryptedVolume == null) { CustomAlertDialogBuilder(activity, theme) .setTitle(R.string.open_volume_failed) - .setMessage(R.string.open_failed_hash_msg) + .setMessage(getErrorMsg(result)) .setPositiveButton(R.string.ok, null) .show() } else { @@ -168,17 +177,18 @@ class VolumeOpener( } else { null } - object : LoadingTask(activity, theme, R.string.loading_msg_open) { - override suspend fun doTask(): EncryptedVolume? { - val encryptedVolume = EncryptedVolume.init(volume, activity.filesDir.path, password, null, returnedHash) + object : LoadingTask(activity, theme, R.string.loading_msg_open) { + override suspend fun doTask(): EncryptedVolume.InitResult { + val result = EncryptedVolume.init(volume, activity.filesDir.path, password, null, returnedHash) Arrays.fill(password, 0) - return encryptedVolume + return result } - }.startTask(activity.lifecycleScope) { encryptedVolume -> + }.startTask(activity.lifecycleScope) { result -> + val encryptedVolume = result.volume if (encryptedVolume == null) { CustomAlertDialogBuilder(activity, theme) .setTitle(R.string.open_volume_failed) - .setMessage(R.string.open_volume_failed_msg) + .setMessage(getErrorMsg(result)) .setPositiveButton(R.string.ok) { _, _ -> askForPassword(volume, isVolumeSaved, callbacks, savePasswordHash) } diff --git a/app/src/main/java/sushi/hardcore/droidfs/adapters/VolumeAdapter.kt b/app/src/main/java/sushi/hardcore/droidfs/adapters/VolumeAdapter.kt index 356bcb0..8aa433a 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/adapters/VolumeAdapter.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/adapters/VolumeAdapter.kt @@ -92,8 +92,10 @@ class VolumeAdapter( itemView.findViewById(R.id.text_info).text = context.getString( if (volume.canWrite(context.filesDir.path)) { R.string.volume_type - } else { + } else if (volume.canRead(context.filesDir.path)) { R.string.volume_type_read_only + } else { + R.string.volume_type_inaccessible }, context.getString(if (volume.type == EncryptedVolume.GOCRYPTFS_VOLUME_TYPE) { R.string.gocryptfs diff --git a/app/src/main/java/sushi/hardcore/droidfs/add_volume/CreateVolumeFragment.kt b/app/src/main/java/sushi/hardcore/droidfs/add_volume/CreateVolumeFragment.kt index 8b2cd35..f6f6643 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/add_volume/CreateVolumeFragment.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/add_volume/CreateVolumeFragment.kt @@ -190,14 +190,14 @@ class CreateVolumeFragment: Fragment() { encryptedVolume, ), EncryptedVolume.GOCRYPTFS_VOLUME_TYPE) } else { - generateResult(CryfsVolume.create( + encryptedVolume.value = CryfsVolume.create( volumePath, CryfsVolume.getLocalStateDir(activity.filesDir.path), password, returnedHash, resources.getStringArray(R.array.cryfs_encryption_ciphers)[binding.spinnerCipher.selectedItemPosition], - encryptedVolume, - ), EncryptedVolume.CRYFS_VOLUME_TYPE) + ) + generateResult(encryptedVolume.value != null, EncryptedVolume.CRYFS_VOLUME_TYPE) } Arrays.fill(password, 0) return result diff --git a/app/src/main/java/sushi/hardcore/droidfs/filesystems/CryfsVolume.kt b/app/src/main/java/sushi/hardcore/droidfs/filesystems/CryfsVolume.kt index 2889be2..e9dfa98 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/filesystems/CryfsVolume.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/filesystems/CryfsVolume.kt @@ -2,6 +2,7 @@ package sushi.hardcore.droidfs.filesystems import android.os.Parcel import sushi.hardcore.droidfs.Constants +import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.explorers.ExplorerElement import sushi.hardcore.droidfs.util.ObjRef import sushi.hardcore.droidfs.util.PathUtils @@ -21,7 +22,8 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() { givenHash: ByteArray?, returnedHash: ObjRef?, createBaseDir: Boolean, - cipher: String? + cipher: String?, + errorCode: ObjRef, ): Long private external fun nativeChangeEncryptionKey( baseDir: String, @@ -58,22 +60,30 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() { returnedHash: ObjRef?, createBaseDir: Boolean, cipher: String? - ): CryfsVolume? { - val fusePtr = nativeInit(baseDir, localStateDir, password, givenHash, returnedHash, createBaseDir, cipher) - return if (fusePtr == 0L) { - null + ): InitResult { + val errorCode = ObjRef(null) + val fusePtr = nativeInit(baseDir, localStateDir, password, givenHash, returnedHash, createBaseDir, cipher, errorCode) + val result = InitResult.Builder() + if (fusePtr == 0L) { + result.errorCode = errorCode.value ?: 0 + result.errorStringId = when (errorCode.value) { + // Values from src/cryfs/impl/ErrorCodes.h + 11 -> R.string.wrong_password + 19 -> R.string.config_load_error + 20 -> R.string.filesystem_id_changed + else -> 0 + } } else { - CryfsVolume(fusePtr) + result.volume = CryfsVolume(fusePtr) } + return result.build() } - fun create(baseDir: String, localStateDir: String, password: ByteArray, returnedHash: ObjRef?, cipher: String?, volume: ObjRef): Boolean { - return init(baseDir, localStateDir, password, null, returnedHash, true, cipher)?.also { - volume.value = it - } != null + fun create(baseDir: String, localStateDir: String, password: ByteArray, returnedHash: ObjRef?, cipher: String?): EncryptedVolume? { + return init(baseDir, localStateDir, password, null, returnedHash, true, cipher).volume } - fun init(baseDir: String, localStateDir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ObjRef?): CryfsVolume? { + fun init(baseDir: String, localStateDir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ObjRef?): InitResult { return init(baseDir, localStateDir, password, givenHash, returnedHash, false, null) } diff --git a/app/src/main/java/sushi/hardcore/droidfs/filesystems/EncryptedVolume.kt b/app/src/main/java/sushi/hardcore/droidfs/filesystems/EncryptedVolume.kt index 864d5e2..a03244e 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/filesystems/EncryptedVolume.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/filesystems/EncryptedVolume.kt @@ -15,6 +15,17 @@ import java.io.InputStream import java.io.OutputStream abstract class EncryptedVolume: Parcelable { + + class InitResult(val errorCode: Int, val errorStringId: Int, val volume: EncryptedVolume?) { + class Builder { + var errorCode = 0 + var errorStringId = 0 + var volume: EncryptedVolume? = null + + fun build() = InitResult(errorCode, errorStringId, volume) + } + } + companion object { const val GOCRYPTFS_VOLUME_TYPE: Byte = 0 const val CRYFS_VOLUME_TYPE: Byte = 1 @@ -31,6 +42,11 @@ abstract class EncryptedVolume: Parcelable { override fun newArray(size: Int) = arrayOfNulls(size) } + /** + * Get the type of a volume. + * + * @return The volume type or -1 if the path is not recognized as a volume + */ fun getVolumeType(path: String): Byte { return if (File(path, GocryptfsVolume.CONFIG_FILE_NAME).isFile) { GOCRYPTFS_VOLUME_TYPE @@ -47,7 +63,7 @@ abstract class EncryptedVolume: Parcelable { password: ByteArray?, givenHash: ByteArray?, returnedHash: ObjRef? - ): EncryptedVolume? { + ): InitResult { return when (volume.type) { GOCRYPTFS_VOLUME_TYPE -> { GocryptfsVolume.init( diff --git a/app/src/main/java/sushi/hardcore/droidfs/filesystems/GocryptfsVolume.kt b/app/src/main/java/sushi/hardcore/droidfs/filesystems/GocryptfsVolume.kt index 8cba157..3fe1109 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/filesystems/GocryptfsVolume.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/filesystems/GocryptfsVolume.kt @@ -2,6 +2,7 @@ package sushi.hardcore.droidfs.filesystems import android.os.Parcel import android.util.Log +import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.explorers.ExplorerElement import sushi.hardcore.droidfs.util.ObjRef import kotlin.math.min @@ -75,13 +76,20 @@ class GocryptfsVolume(private val sessionID: Int): EncryptedVolume() { } } - 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?): InitResult { val sessionId = nativeInit(root_cipher_dir, password, givenHash, returnedHash) - return if (sessionId == -1) { - null + val result = InitResult.Builder() + if (sessionId < 0) { + result.errorCode = sessionId + result.errorStringId = when (sessionId) { + -1 -> R.string.config_load_error + -2 -> R.string.wrong_password + else -> 0 + } } else { - GocryptfsVolume(sessionId) + result.volume = GocryptfsVolume(sessionId) } + return result.build() } init { diff --git a/app/src/main/native/libcryfs.c b/app/src/main/native/libcryfs.c index b765118..fbdb579 100644 --- a/app/src/main/native/libcryfs.c +++ b/app/src/main/native/libcryfs.c @@ -7,9 +7,9 @@ Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeInit(JN jstring base_dir, jstring jlocalStateDir, jbyteArray password, jbyteArray givenHash, jobject returnedHash, - jboolean createBaseDir, - jstring cipher) { - return cryfs_init(env, base_dir, jlocalStateDir, password, givenHash, returnedHash, createBaseDir, cipher); + jboolean createBaseDir, jstring cipher, + jobject jerrorCode) { + return cryfs_init(env, base_dir, jlocalStateDir, password, givenHash, returnedHash, createBaseDir, cipher, jerrorCode); } JNIEXPORT jboolean JNICALL diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index d6c9195..e896072 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -27,7 +27,6 @@ إن المجلد المحدد ليس فارغاً إن عملية إنشاء المجلد المشفر قد فشلت. عملية الفتح قد فشلت - لقد تعذر فتح المجلد المشفر. من فضلك تحقق من كلمة المرور. مشاركة ملف إن صلاحية التخزين مرفوضة إن DroidFS لا يمكنه العمل بدون صلاحبات التخزين. @@ -58,7 +57,6 @@ تعذر الوصول إلى هذا المسار حفظ كلمة المرور ببصمة الإصبع يرجى لمس مستشعر بصمة الإصبع - فشل في فتح المجلد المشفر. ربما تغيرت كلمة المرور. IllegalBlockSizeException يمكن أن يحدث هذا إذا أضفت بصمة إصبع جديدة. يمكنك أن تقوم بإعادة تعيين تخزين التجزئة لحل هذه المشكلة. إعادة تعيين تخزين التجزئة diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 23d5447..f70bd0f 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -28,7 +28,6 @@ El directorio seleccionado no está vacío La creación del volumen ha fallado. Fallo al abrir - No se ha podido abrir el volumen. Por favor, comprueba tu contraseña. Compartir archivo Permiso de almacenamiento denegado DroidFS no puede funcionar sin permisos de almacenamiento. @@ -59,7 +58,6 @@ No se puede acceder a este directorio Guardar el hash de la contraseña mediante la huella dactilar Por favor, toca el sensor de huellas dactilares - No se ha podido abrir el volumen. La contraseña puede haber cambiado. IllegalBlockSizeException Esto puede ocurrir si has añadido una nueva huella digital. Restablecer el almacenamiento de hash puede resolver este problema. Restablecer el almacenamiento de hash diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index ebfe983..ad08e07 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -27,7 +27,6 @@ A pasta selecionada não está vazia Falha ao criar o volume. Não foi possível abrir - Falha ao abrir o volume. Por favor, verifique sua senha. Compartilhar arquivo Permissão do armazenamento negada DroidFS não pode funcionar sem permissão ao armazenamento. @@ -58,7 +57,6 @@ Não foi possível acessar a pasta Salvar o hash da senha usando impressão digital Por favor, toque no sensor da impressão digital - Falha ao abrir o volume. A senha podia ter mudado. IllegalBlockSizeException Isto poderia ter acontecido, se você tiver adicionado uma nova impressão digital. Tente resetar o hash do armazenamento para resolver este problema. Resetar o hash do armazenamento diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index aab693e..8f81cd0 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -26,7 +26,6 @@ Выбранная папка не пустая Невозможно создать том. Ошибка открытия - Невозможно открыть том. Проверьте пароль. Поделиться файлом Доступ к хранилищу запрещён DroidFS не может работать без разрешения на доступ к хранилищу. @@ -57,7 +56,6 @@ Невозможно получить доступ к этой папке. Сохранить хеш пароля отпечатком пальца Прикоснитесь к сканеру отпечатков пальцев - Невозможно открыть том. Возможно, изменился пароль. Это могло произойти, если вы добавили новый отпечаток пальца. Сброс хранилища хешей должен решить эту проблему. Сброс хранилища хешей Проверка подписи/MAC не выполнена. Хранилище ключей Android или сохранённый хеш был изменён. Сброс хранилища хешей должен решить эту проблему. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c08c1ff..0d2081f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,7 +28,6 @@ The selected directory isn\'t empty The volume creation has failed. Open failed - Failed to open the volume. Please check your password. Share File Storage permission denied DroidFS can\'t work without storage permissions. @@ -59,7 +58,6 @@ Unable to access this directory Save password hash using fingerprint Please touch the fingerprint sensor - Failed to open the volume. The password may have changed. IllegalBlockSizeException This can happen if you have added a new fingerprint. Resetting hash storage can solve this problem. Reset hash storage @@ -244,6 +242,7 @@ Deleting files… (%s) (%s, read-only) + (%s, inaccessible) I/O Error. Use fingerprint instead of current password Remember volume @@ -258,4 +257,8 @@ Black theme Fallback to password Prompt for password when fingerprint authentication is cancelled + Unknown error code: %d + The configuration file cannot be loaded. Make sure the volume is accessible. + The configuration file cannot be decrypted. Please check your password. + The filesystem id in the config file is different to the last time we opened this volume. This could mean an attacker replaced the filesystem with a different one.