-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
fix(e2ee): handle E2EE v2.1 #16345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
fix(e2ee): handle E2EE v2.1 #16345
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| /* | ||
| * Nextcloud - Android Client | ||
| * | ||
| * SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com> | ||
| * SPDX-License-Identifier: AGPL-3.0-or-later | ||
| */ | ||
|
|
||
| package com.nextcloud.utils.e2ee | ||
|
|
||
| import com.google.gson.reflect.TypeToken | ||
| import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFolderMetadataFileV1 | ||
| import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFolderMetadataFile | ||
| import com.owncloud.android.lib.resources.status.E2EVersion | ||
| import com.owncloud.android.lib.resources.status.OCCapability | ||
| import com.owncloud.android.utils.EncryptionUtils | ||
|
|
||
| object E2EVersionHelper { | ||
|
|
||
| /** | ||
| * Returns true if the given E2EE version is v2 or newer. | ||
| */ | ||
| fun isV2Plus(capability: OCCapability): Boolean = isV2Plus(capability.endToEndEncryptionApiVersion) | ||
|
|
||
| /** | ||
| * Returns true if the given E2EE version is v2 or newer. | ||
| */ | ||
| fun isV2Plus(version: E2EVersion): Boolean = version == E2EVersion.V2_0 || version == E2EVersion.V2_1 | ||
|
|
||
| /** | ||
| * Returns true if the given E2EE version is v1.x. | ||
| */ | ||
| fun isV1(capability: OCCapability): Boolean = isV1(capability.endToEndEncryptionApiVersion) | ||
|
|
||
| /** | ||
| * Returns true if the given E2EE version is v1.x. | ||
| */ | ||
| fun isV1(version: E2EVersion): Boolean = | ||
| version == E2EVersion.V1_0 || version == E2EVersion.V1_1 || version == E2EVersion.V1_2 | ||
|
|
||
| /** | ||
| * Returns the latest supported E2EE version. | ||
| * | ||
| * @param isV2 indicates whether the E2EE v2 series should be used | ||
| */ | ||
| fun latestVersion(isV2: Boolean): E2EVersion = if (isV2) { | ||
| E2EVersion.V2_1 | ||
| } else { | ||
| E2EVersion.V1_2 | ||
| } | ||
|
|
||
| /** | ||
| * Maps a raw version string to an [E2EVersion]. | ||
| * | ||
| * @param version version string | ||
| * @return resolved [E2EVersion] or [E2EVersion.UNKNOWN] if unsupported | ||
| */ | ||
| fun fromVersionString(version: String?): E2EVersion = when (version?.trim()) { | ||
| "1.0" -> E2EVersion.V1_0 | ||
| "1.1" -> E2EVersion.V1_1 | ||
| "1.2" -> E2EVersion.V1_2 | ||
| "2", "2.0" -> E2EVersion.V2_0 | ||
| "2.1" -> E2EVersion.V2_1 | ||
| else -> E2EVersion.UNKNOWN | ||
| } | ||
|
|
||
| /** | ||
| * Determines the E2EE version by inspecting encrypted folder metadata. | ||
| * | ||
| * Supports both V1 and V2 metadata formats and falls back safely | ||
| * to [E2EVersion.UNKNOWN] if parsing fails. | ||
| */ | ||
| fun fromMetadata(metadata: String): E2EVersion = runCatching { | ||
| val v1 = EncryptionUtils.deserializeJSON<EncryptedFolderMetadataFileV1>( | ||
| metadata, | ||
| object : TypeToken<EncryptedFolderMetadataFileV1>() {} | ||
| ) | ||
|
|
||
| fromVersionString(v1?.metadata?.version.toString()).also { | ||
| if (it == E2EVersion.UNKNOWN) { | ||
| throw IllegalStateException("Unknown V1 version") | ||
| } | ||
| } | ||
| }.recoverCatching { | ||
| val v2 = EncryptionUtils.deserializeJSON<EncryptedFolderMetadataFile>( | ||
| metadata, | ||
| object : TypeToken<EncryptedFolderMetadataFile>() {} | ||
| ) | ||
|
|
||
| fromVersionString(v2.version) | ||
| }.getOrDefault(E2EVersion.UNKNOWN) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,8 @@ | |
| */ | ||
| package com.owncloud.android.datamodel.e2e.v2.decrypted | ||
|
|
||
| import com.nextcloud.utils.e2ee.E2EVersionHelper | ||
|
|
||
| /** | ||
| * Decrypted class representation of metadata json of folder metadata. | ||
| */ | ||
|
|
@@ -15,5 +17,5 @@ data class DecryptedFolderMetadataFile( | |
| var users: MutableList<DecryptedUser> = mutableListOf(), | ||
| @Transient | ||
| val filedrop: MutableMap<String, DecryptedFile> = HashMap(), | ||
| val version: String = "2.0" | ||
| val version: String = E2EVersionHelper.latestVersion(true).value | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replaces hardcoded hidden strings with actual latest version so that in future we dont come across issues like this. @tobiasKaminsky |
||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| import com.nextcloud.android.lib.resources.directediting.DirectEditingObtainRemoteOperation; | ||
| import com.nextcloud.client.account.User; | ||
| import com.nextcloud.common.NextcloudClient; | ||
| import com.nextcloud.utils.e2ee.E2EVersionHelper; | ||
| import com.nextcloud.utils.extensions.StringExtensionsKt; | ||
| import com.owncloud.android.datamodel.ArbitraryDataProvider; | ||
| import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; | ||
|
|
@@ -538,7 +539,9 @@ private void synchronizeData(List<Object> folderAndFiles) { | |
| mContext); | ||
| } | ||
|
|
||
| if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) { | ||
| final var capability = CapabilityUtils.getCapability(mContext); | ||
|
|
||
| if (E2EVersionHelper.INSTANCE.isV2Plus(capability)) { | ||
| if (encryptedAncestor && object == null) { | ||
| throw new IllegalStateException("metadata is null!"); | ||
| } | ||
|
|
@@ -548,10 +551,10 @@ private void synchronizeData(List<Object> folderAndFiles) { | |
| Map<String, OCFile> localFilesMap; | ||
| E2EVersion e2EVersion; | ||
| if (object instanceof DecryptedFolderMetadataFileV1 metadataFileV1) { | ||
| e2EVersion = E2EVersion.V1_2; | ||
| e2EVersion = E2EVersionHelper.INSTANCE.latestVersion(false); | ||
| localFilesMap = prefillLocalFilesMap(metadataFileV1, fileDataStorageManager.getFolderContent(mLocalFolder, false)); | ||
| } else { | ||
| e2EVersion = E2EVersion.V2_0; | ||
| e2EVersion = E2EVersionHelper.INSTANCE.latestVersion(true); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, for each version iteration, these hidden hardcoded values need to be upgraded. With |
||
| localFilesMap = prefillLocalFilesMap(object, fileDataStorageManager.getFolderContent(mLocalFolder, false)); | ||
|
|
||
| // update counter | ||
|
|
@@ -598,7 +601,7 @@ private void synchronizeData(List<Object> folderAndFiles) { | |
| FileStorageUtils.searchForLocalFileInDefaultPath(updatedFile, user.getAccountName()); | ||
|
|
||
| // update file name for encrypted files | ||
| if (e2EVersion == E2EVersion.V1_2) { | ||
| if (e2EVersion == E2EVersionHelper.INSTANCE.latestVersion(false)) { | ||
| updateFileNameForEncryptedFileV1(fileDataStorageManager, | ||
| (DecryptedFolderMetadataFileV1) object, | ||
| updatedFile); | ||
|
|
@@ -621,7 +624,7 @@ private void synchronizeData(List<Object> folderAndFiles) { | |
|
|
||
| // save updated contents in local database | ||
| // update file name for encrypted files | ||
| if (e2EVersion == E2EVersion.V1_2) { | ||
| if (e2EVersion == E2EVersionHelper.INSTANCE.latestVersion(false)) { | ||
| updateFileNameForEncryptedFileV1(fileDataStorageManager, | ||
| (DecryptedFolderMetadataFileV1) object, | ||
| mLocalFolder); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replacement of
public static E2EVersion determinateVersion(String metadata) {Tries v1 if fails v2 if fails E2EVersion.UNKNOWN