From ed59be63ae22f5153e645c1055286bc33b7efe0a Mon Sep 17 00:00:00 2001 From: daniele-verducci Date: Thu, 10 Sep 2026 18:54:41 +0200 Subject: [PATCH 1/4] Fix subfolders in encrypted folders Signed-off-by: daniele-verducci --- .../nextcloud/utils/e2ee/E2EEKeyInspector.kt | 54 +++++++++++++------ .../encrypted/EncryptedFolderMetadataFile.kt | 4 +- .../android/utils/EncryptionUtilsV2.kt | 2 +- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt b/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt index 224fc8364b11..407057ccc2cb 100644 --- a/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt +++ b/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt @@ -15,9 +15,11 @@ import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFolderMetadataFileV1 import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFolderMetadataFile +import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.OwnCloudClientFactory import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.e2ee.GetMetadataRemoteOperation +import com.owncloud.android.lib.resources.e2ee.MetadataResponse import com.owncloud.android.lib.resources.status.OCCapability import com.owncloud.android.lib.resources.users.GetPublicKeyRemoteOperation import com.owncloud.android.lib.resources.users.GetServerPublicKeyRemoteOperation @@ -87,11 +89,6 @@ class E2EEKeyInspector @Inject constructor( Log_OC.d(TAG, "checking folder metadata key") val client = OwnCloudClientFactory.createOwnCloudClient(accountManager.currentAccount, context) - val metadataResult = GetMetadataRemoteOperation(folder.localId).execute(client) - - if (!metadataResult.isSuccess) { - return@withContext false - } val privateKey = arbitraryDataProvider.getValue(accountManager.user, EncryptionUtils.PRIVATE_KEY) if (privateKey.isEmpty()) { @@ -99,26 +96,48 @@ class E2EEKeyInspector @Inject constructor( return@withContext false } - val metadata = metadataResult.resultData - return@withContext if (E2EVersionHelper.isV2Plus(capability)) { - decryptsMetadataV2(metadata.metadata, privateKey, client.userId) + decryptsMetadataV2(client, privateKey, folder) } else { - decryptsMetadataV1(metadata.metadata, privateKey, folder.localId) + decryptsMetadataV1(client, privateKey, folder) } } - private fun decryptsMetadataV2(serializedMetadata: String, privateKey: String, userId: String): Boolean { + private fun obtainMetadata(client: OwnCloudClient, folder: OCFile): MetadataResponse? { + val metadataResult = GetMetadataRemoteOperation(folder.localId).execute(client) + + if (!metadataResult.isSuccess) { + return null + } + + return metadataResult.resultData + } + + private fun getNearestEncryptedMetadataKey(client: OwnCloudClient, folder: OCFile): String? { + val metadata = obtainMetadata(client, folder) ?: return null val metadataFile = EncryptionUtils.deserializeJSON( - serializedMetadata, + metadata.metadata, object : TypeToken() {} ) - val user = metadataFile.users.find { it.userId == userId } - ?: throw IllegalStateException("cannot find current user in metadata") + val user = metadataFile.users?.find { it.userId == client.userId } + if (user != null) { + return user.encryptedMetadataKey + } + + val parentFolder = storageManager.getFileById(folder.parentId) + if (parentFolder?.isEncrypted == true) { + return getNearestEncryptedMetadataKey(client, parentFolder) + } + + return null + } + + private fun decryptsMetadataV2(client: OwnCloudClient, privateKey: String, folder: OCFile): Boolean { + val encryptedMetadataKey = getNearestEncryptedMetadataKey(client, folder) ?: return false return try { - EncryptionUtils.decryptStringAsymmetricV2(user.encryptedMetadataKey, privateKey) + EncryptionUtils.decryptStringAsymmetricV2(encryptedMetadataKey, privateKey) true } catch (e: Exception) { Log_OC.w(TAG, "user tried to decrypt folder's metadata with different private key: $e") @@ -126,9 +145,10 @@ class E2EEKeyInspector @Inject constructor( } } - private fun decryptsMetadataV1(serializedMetadata: String, privateKey: String, folderLocalId: Long): Boolean { + private fun decryptsMetadataV1(client: OwnCloudClient, privateKey: String, folder: OCFile): Boolean { + val metadata = obtainMetadata(client, folder) ?: return false val metadataFile = EncryptionUtils.deserializeJSON( - serializedMetadata, + metadata.metadata, object : TypeToken() {} ) @@ -138,7 +158,7 @@ class E2EEKeyInspector @Inject constructor( privateKey, arbitraryDataProvider, accountManager.user, - folderLocalId + folder.localId ) true } catch (e: Exception) { diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFolderMetadataFile.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFolderMetadataFile.kt index 347cd883ab81..c1b23e136946 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFolderMetadataFile.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFolderMetadataFile.kt @@ -7,14 +7,12 @@ */ package com.owncloud.android.datamodel.e2e.v2.encrypted -import com.nextcloud.utils.e2ee.E2EVersionHelper - /** * Decrypted class representation of metadata json of folder metadata. */ data class EncryptedFolderMetadataFile( val metadata: EncryptedMetadata, - val users: List, + val users: List?, @Transient val filedrop: MutableMap?, val version: String ) diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt b/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt index 432cef709305..b92d5cba134f 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt @@ -193,7 +193,7 @@ class EncryptionUtilsV2 { ) } else { // Top folder - val encryptedUser = metadataFile.users.find { it.userId == userId } + val encryptedUser = metadataFile.users?.find { it.userId == userId } ?: throw IllegalStateException("Cannot find current user in metadata") val decryptedMetadataKey = decryptMetadataKey(encryptedUser, privateKey) From 0a6c7c84a921fad8a4e70d1efd80ae9d437ab75e Mon Sep 17 00:00:00 2001 From: daniele-verducci Date: Mon, 14 Sep 2026 10:01:12 +0200 Subject: [PATCH 2/4] Better fix subfolders in encrypted folders (using existing metadata retrieval function) Signed-off-by: daniele-verducci Signed-off-by: daniele-verducci --- .../nextcloud/utils/e2ee/E2EEKeyInspector.kt | 67 ++++++++----------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt b/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt index 407057ccc2cb..5fb090395e5d 100644 --- a/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt +++ b/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt @@ -14,12 +14,10 @@ import com.owncloud.android.datamodel.ArbitraryDataProvider import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFolderMetadataFileV1 -import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFolderMetadataFile import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.OwnCloudClientFactory import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.e2ee.GetMetadataRemoteOperation -import com.owncloud.android.lib.resources.e2ee.MetadataResponse import com.owncloud.android.lib.resources.status.OCCapability import com.owncloud.android.lib.resources.users.GetPublicKeyRemoteOperation import com.owncloud.android.lib.resources.users.GetServerPublicKeyRemoteOperation @@ -27,6 +25,7 @@ import com.owncloud.android.operations.GetCapabilitiesOperation import com.owncloud.android.ui.dialog.setupEncryption.CertificateValidator import com.owncloud.android.ui.dialog.setupEncryption.model.DownloadKeyResult import com.owncloud.android.utils.EncryptionUtils +import com.owncloud.android.utils.EncryptionUtilsV2 import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.apache.commons.httpclient.HttpStatus @@ -89,6 +88,11 @@ class E2EEKeyInspector @Inject constructor( Log_OC.d(TAG, "checking folder metadata key") val client = OwnCloudClientFactory.createOwnCloudClient(accountManager.currentAccount, context) + val metadataResult = GetMetadataRemoteOperation(folder.localId).execute(client) + + if (!metadataResult.isSuccess) { + return@withContext false + } val privateKey = arbitraryDataProvider.getValue(accountManager.user, EncryptionUtils.PRIVATE_KEY) if (privateKey.isEmpty()) { @@ -96,48 +100,32 @@ class E2EEKeyInspector @Inject constructor( return@withContext false } + val metadata = metadataResult.resultData + return@withContext if (E2EVersionHelper.isV2Plus(capability)) { - decryptsMetadataV2(client, privateKey, folder) + decryptsMetadataV2(folder, privateKey, client) } else { - decryptsMetadataV1(client, privateKey, folder) + decryptsMetadataV1(metadata.metadata, privateKey, folder.localId) } } - private fun obtainMetadata(client: OwnCloudClient, folder: OCFile): MetadataResponse? { - val metadataResult = GetMetadataRemoteOperation(folder.localId).execute(client) - - if (!metadataResult.isSuccess) { - return null - } - - return metadataResult.resultData - } - - private fun getNearestEncryptedMetadataKey(client: OwnCloudClient, folder: OCFile): String? { - val metadata = obtainMetadata(client, folder) ?: return null - val metadataFile = EncryptionUtils.deserializeJSON( - metadata.metadata, - object : TypeToken() {} + private fun decryptsMetadataV2(ocFile: OCFile, privateKey: String, client: OwnCloudClient): Boolean { + val userId = client.userId + val metadata = EncryptionUtilsV2().retrieveTopMostMetadata( + ocFile, + storageManager, + client, + userId, + privateKey, + accountManager.user, + context, + arbitraryDataProvider ) - - val user = metadataFile.users?.find { it.userId == client.userId } - if (user != null) { - return user.encryptedMetadataKey - } - - val parentFolder = storageManager.getFileById(folder.parentId) - if (parentFolder?.isEncrypted == true) { - return getNearestEncryptedMetadataKey(client, parentFolder) - } - - return null - } - - private fun decryptsMetadataV2(client: OwnCloudClient, privateKey: String, folder: OCFile): Boolean { - val encryptedMetadataKey = getNearestEncryptedMetadataKey(client, folder) ?: return false + val user = metadata.users.find { it.userId == userId } + ?: throw IllegalStateException("cannot find current user in metadata") return try { - EncryptionUtils.decryptStringAsymmetricV2(encryptedMetadataKey, privateKey) + EncryptionUtils.decryptStringAsymmetricV2(user.decryptedMetadataKey, privateKey) true } catch (e: Exception) { Log_OC.w(TAG, "user tried to decrypt folder's metadata with different private key: $e") @@ -145,10 +133,9 @@ class E2EEKeyInspector @Inject constructor( } } - private fun decryptsMetadataV1(client: OwnCloudClient, privateKey: String, folder: OCFile): Boolean { - val metadata = obtainMetadata(client, folder) ?: return false + private fun decryptsMetadataV1(serializedMetadata: String, privateKey: String, folderLocalId: Long): Boolean { val metadataFile = EncryptionUtils.deserializeJSON( - metadata.metadata, + serializedMetadata, object : TypeToken() {} ) @@ -158,7 +145,7 @@ class E2EEKeyInspector @Inject constructor( privateKey, arbitraryDataProvider, accountManager.user, - folder.localId + folderLocalId ) true } catch (e: Exception) { From d232d451d63d42a6dd039e058b1083badaa91ab9 Mon Sep 17 00:00:00 2001 From: daniele-verducci Date: Mon, 14 Sep 2026 12:51:11 +0200 Subject: [PATCH 3/4] Proposal to fix metadata key check Signed-off-by: daniele-verducci --- .../nextcloud/utils/e2ee/E2EEKeyInspector.kt | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt b/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt index 5fb090395e5d..80844ecbbff6 100644 --- a/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt +++ b/app/src/main/java/com/nextcloud/utils/e2ee/E2EEKeyInspector.kt @@ -111,22 +111,19 @@ class E2EEKeyInspector @Inject constructor( private fun decryptsMetadataV2(ocFile: OCFile, privateKey: String, client: OwnCloudClient): Boolean { val userId = client.userId - val metadata = EncryptionUtilsV2().retrieveTopMostMetadata( - ocFile, - storageManager, - client, - userId, - privateKey, - accountManager.user, - context, - arbitraryDataProvider - ) - val user = metadata.users.find { it.userId == userId } - ?: throw IllegalStateException("cannot find current user in metadata") return try { - EncryptionUtils.decryptStringAsymmetricV2(user.decryptedMetadataKey, privateKey) - true + val metadataKey = EncryptionUtilsV2().retrieveTopMostMetadataKey( + ocFile, + storageManager, + client, + userId, + privateKey, + accountManager.user, + context, + arbitraryDataProvider + ) + !metadataKey.isEmpty() } catch (e: Exception) { Log_OC.w(TAG, "user tried to decrypt folder's metadata with different private key: $e") false From 7bdb3c3a018bc0b701a54b3b68b524f8a2131c52 Mon Sep 17 00:00:00 2001 From: daniele-verducci Date: Mon, 14 Sep 2026 13:00:43 +0200 Subject: [PATCH 4/4] Updated android-library Signed-off-by: daniele-verducci --- gradle/libs.versions.toml | 2 +- gradle/verification-metadata.xml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d4ef11def1f4..5ad435586df3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ androidCommonLibraryVersion = "f46fb3b6789dee1f476269903da61e9b48c99587" androidGifDrawableVersion = "1.2.32" androidImageCropperVersion = "4.7.0" -androidLibraryVersion ="d22a01f4b8327b6593d6af75f6be3aa7fbc2c854" +androidLibraryVersion ="8cbaa7e8a834f5c2fa6d1f24bd3b9923aa079f28" androidOpensslVersion = "3.5.6" androidPluginVersion = "9.4.0" androidsvgVersion = "1.4" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index bed32c794130..3b72d2603efc 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -21857,6 +21857,14 @@ + + + + + + + +