From 63b32a854fd8def37ac9b11ccc8a1aa78ca54e71 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 30 Aug 2023 07:33:01 +0200 Subject: [PATCH] wip Signed-off-by: tobiasKaminsky --- .../android/operations/UnshareOperation.java | 10 +- .../operations/UploadFileOperation.java | 210 ++++++++++++------ .../android/ui/activity/SettingsActivity.java | 4 +- .../dialog/SetupEncryptionDialogFragment.java | 3 +- .../fragment/FileDetailSharingFragment.java | 14 +- .../android/utils/EncryptionUtils.java | 11 +- .../android/utils/EncryptionUtilsV2.kt | 2 - app/src/main/res/values/strings.xml | 1 + 8 files changed, 171 insertions(+), 84 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UnshareOperation.java b/app/src/main/java/com/owncloud/android/operations/UnshareOperation.java index 4dc5298263a3..a3bfb90b7440 100644 --- a/app/src/main/java/com/owncloud/android/operations/UnshareOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UnshareOperation.java @@ -79,7 +79,7 @@ protected RemoteOperationResult run(OwnCloudClient client) { if (share != null) { OCFile file = getStorageManager().getFileByEncryptedRemotePath(remotePath); - if (file.isEncrypted()) { + if (file.isEncrypted() && share.getShareType() != ShareType.PUBLIC_LINK) { // E2E: lock folder try { token = EncryptionUtils.lockFolder(file, client, file.getE2eCounter() + 1); @@ -128,9 +128,11 @@ protected RemoteOperationResult run(OwnCloudClient client) { if (result.isSuccess()) { // E2E: unlock folder - RemoteOperationResult unlockResult = EncryptionUtils.unlockFolder(file, client, token); - if (!unlockResult.isSuccess()) { - return new RemoteOperationResult<>(new RuntimeException("Unlock failed")); + if (file.isEncrypted() && share.getShareType() != ShareType.PUBLIC_LINK) { + RemoteOperationResult unlockResult = EncryptionUtils.unlockFolder(file, client, token); + if (!unlockResult.isSuccess()) { + return new RemoteOperationResult<>(new RuntimeException("Unlock failed")); + } } Log_OC.d(TAG, "Share id = " + share.getRemoteId() + " deleted"); diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index c0a866502699..7b3feb1573d4 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -31,12 +31,17 @@ import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.Connectivity; import com.nextcloud.client.network.ConnectivityService; +import com.owncloud.android.datamodel.ArbitraryDataProvider; +import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.datamodel.UploadsStorageManager; +import com.owncloud.android.datamodel.e2e.v1.decrypted.Data; +import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile; +import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1; import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFile; -import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile; +import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFolderMetadataFileV1; import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFolderMetadataFile; import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.FileUploader; @@ -54,6 +59,7 @@ import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation; import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation; import com.owncloud.android.lib.resources.files.model.RemoteFile; +import com.owncloud.android.lib.resources.status.E2EVersion; import com.owncloud.android.operations.common.SyncOperation; import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.EncryptionUtilsV2; @@ -79,7 +85,9 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -442,6 +450,9 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare boolean metadataExists = false; String token = null; + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(getContext()); + String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); + try { // check conditions result = checkConditions(originalFile); @@ -465,19 +476,43 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare // Update metadata EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2(); - kotlin.Pair metadataPair = - encryptionUtilsV2.retrieveMetadata(parentFile, - client, - user, - mContext); +// kotlin.Pair metadataPair = +// encryptionUtilsV2.retrieveMetadata(parentFile, +// client, +// user, +// mContext); + + Object object = EncryptionUtils.downloadFolderMetadata(parentFile, client, mContext, user, token); + + if (object == null) { + // TODO return error + return new RemoteOperationResult(new IllegalStateException("Metadata does not exist")); + } else { + metadataExists = true; + } + + // todo fail if no metadata + +// metadataExists = metadataPair.getFirst(); +// DecryptedFolderMetadataFile metadata = metadataPair.getSecond(); - metadataExists = metadataPair.getFirst(); - DecryptedFolderMetadataFile metadata = metadataPair.getSecond(); // TODO E2E: check counter: must be less than our counter, check rest: signature, etc /**** E2E *****/ // check name collision - RemoteOperationResult collisionResult = checkNameCollision(client, metadata, parentFile.isEncrypted()); + List fileNames = new ArrayList<>(); + if (object instanceof DecryptedFolderMetadataFileV1 metadata) { + for (DecryptedFile file : metadata.getFiles().values()) { + fileNames.add(file.getEncrypted().getFilename()); + } + } else { + for (com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile file : + ((DecryptedFolderMetadataFile) object).getMetadata().getFiles().values()) { + fileNames.add(file.getFilename()); + } + } + + RemoteOperationResult collisionResult = checkNameCollision(client, fileNames, parentFile.isEncrypted()); if (collisionResult != null) { result = collisionResult; return collisionResult; @@ -510,8 +545,14 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare // new random file name, check if it exists in metadata String encryptedFileName = EncryptionUtils.generateUid(); - while (metadata.getMetadata().getFiles().get(encryptedFileName) != null) { - encryptedFileName = EncryptionUtils.generateUid(); + if (object instanceof DecryptedFolderMetadataFileV1 metadata) { + while (metadata.getFiles().get(encryptedFileName) != null) { + encryptedFileName = EncryptionUtils.generateUid(); + } + } else { + while (((DecryptedFolderMetadataFile) object).getMetadata().getFiles().get(encryptedFileName) != null) { + encryptedFileName = EncryptionUtils.generateUid(); + } } File encryptedTempFile = File.createTempFile("encFile", encryptedFileName); @@ -600,60 +641,79 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare mFile.setDecryptedRemotePath(parentFile.getDecryptedRemotePath() + originalFile.getName()); mFile.setRemotePath(parentFile.getRemotePath() + encryptedFileName); - // update metadata -// DecryptedFile decryptedFile = new DecryptedFile(); -// Data data = new Data(); -// data.setFilename(mFile.getDecryptedFileName()); -// data.setMimetype(mFile.getMimeType()); -// data.setKey(EncryptionUtils.encodeBytesToBase64String(key)); -// -// decryptedFile.setEncrypted(data); -// decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(iv)); -// decryptedFile.setAuthenticationTag(encryptedFile.authenticationTag); -// -// metadata.getFiles().put(encryptedFileName, decryptedFile); - - encryptionUtilsV2.addFileToMetadata( - encryptedFileName, - mFile, - iv, - encryptedFile.getAuthenticationTag(), - key, - metadata, - getStorageManager()); - - // TODO E2E: to check -// metadata.getFiles().put(encryptedFileName, decryptedFile); -// -// EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.encryptFolderMetadata(metadata, -// publicKey, -// arbitraryDataProvider, -// user, -// parentFile.getLocalId()); -// -// String serializedFolderMetadata; -// -// // check if we need metadataKeys -// if (metadata.getMetadata().getMetadataKey() != null) { -// serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata, true); -// } else { -// serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata); -// } - - // upload metadata - encryptionUtilsV2.serializeAndUploadMetadata(parentFile, - metadata, - token, - client, - metadataExists, - mContext, - user); - - // unlock - result = EncryptionUtils.unlockFolder(parentFile, client, token); - if (result.isSuccess()) { - token = null; + if (object instanceof DecryptedFolderMetadataFileV1 metadata) { + // update metadata + DecryptedFile decryptedFile = new DecryptedFile(); + Data data = new Data(); + data.setFilename(mFile.getDecryptedFileName()); + data.setMimetype(mFile.getMimeType()); + data.setKey(EncryptionUtils.encodeBytesToBase64String(key)); + + decryptedFile.setEncrypted(data); + decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(iv)); + decryptedFile.setAuthenticationTag(encryptedFile.getAuthenticationTag()); + + metadata.getFiles().put(encryptedFileName, decryptedFile); + + EncryptedFolderMetadataFileV1 encryptedFolderMetadata = + EncryptionUtils.encryptFolderMetadata(metadata, + publicKey, + parentFile.getLocalId(), + user, + arbitraryDataProvider + ); + + String serializedFolderMetadata; + + // check if we need metadataKeys + if (metadata.getMetadata().getMetadataKey() != null) { + serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata, true); + } else { + serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata); + } + + // upload metadata + EncryptionUtils.uploadMetadata(parentFile, + serializedFolderMetadata, + token, + client, + metadataExists, + E2EVersion.V1_2, + ""); + + // unlock + result = EncryptionUtils.unlockFolderV1(parentFile, client, token); + + if (result.isSuccess()) { + token = null; + } + } else { + DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; + encryptionUtilsV2.addFileToMetadata( + encryptedFileName, + mFile, + iv, + encryptedFile.getAuthenticationTag(), + key, + metadata, + getStorageManager()); + + // upload metadata + encryptionUtilsV2.serializeAndUploadMetadata(parentFile, + metadata, + token, + client, + metadataExists, + mContext, + user); + + // unlock + result = EncryptionUtils.unlockFolder(parentFile, client, token); + + if (result.isSuccess()) { + token = null; + } } } } catch (FileNotFoundException e) { @@ -954,18 +1014,18 @@ private RemoteOperationResult copyFile(File originalFile, String expectedPath) t @CheckResult private RemoteOperationResult checkNameCollision(OwnCloudClient client, - DecryptedFolderMetadataFile metadata, + List fileNames, boolean encrypted) throws OperationCancelledException { Log_OC.d(TAG, "Checking name collision in server"); - if (existsFile(client, mRemotePath, metadata, encrypted)) { + if (existsFile(client, mRemotePath, fileNames, encrypted)) { switch (mNameCollisionPolicy) { case CANCEL: Log_OC.d(TAG, "File exists; canceling"); throw new OperationCancelledException(); case RENAME: - mRemotePath = getNewAvailableRemotePath(client, mRemotePath, metadata, encrypted); + mRemotePath = getNewAvailableRemotePath(client, mRemotePath, fileNames, encrypted); mWasRenamed = true; createNewOCFile(mRemotePath); Log_OC.d(TAG, "File renamed as " + mRemotePath); @@ -1127,10 +1187,12 @@ private void createNewOCFile(String newRemotePath) { * * @param client OwnCloud client * @param remotePath remote path of the file - * @param metadata metadata of encrypted folder + * @param fileNames list of decrypted file names * @return new remote path */ - private String getNewAvailableRemotePath(OwnCloudClient client, String remotePath, DecryptedFolderMetadataFile metadata, + private String getNewAvailableRemotePath(OwnCloudClient client, + String remotePath, + List fileNames, boolean encrypted) { int extPos = remotePath.lastIndexOf('.'); String suffix; @@ -1147,20 +1209,22 @@ private String getNewAvailableRemotePath(OwnCloudClient client, String remotePat do { suffix = " (" + count + ")"; newPath = extPos >= 0 ? remotePathWithoutExtension + suffix + "." + extension : remotePath + suffix; - exists = existsFile(client, newPath, metadata, encrypted); + exists = existsFile(client, newPath, fileNames, encrypted); count++; } while (exists); return newPath; } - private boolean existsFile(OwnCloudClient client, String remotePath, DecryptedFolderMetadataFile metadata, + private boolean existsFile(OwnCloudClient client, + String remotePath, + List fileNames, boolean encrypted) { if (encrypted) { String fileName = new File(remotePath).getName(); - for (DecryptedFile file : metadata.getMetadata().getFiles().values()) { - if (file.getFilename().equalsIgnoreCase(fileName)) { + for (String name : fileNames) { + if (name.equalsIgnoreCase(fileName)) { return true; } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index da23f3168a8c..3017ff43b8b4 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -474,7 +474,7 @@ private void setupE2EKeysExist(PreferenceCategory preferenceCategoryMore) { } private void setupE2EMnemonicPreference(PreferenceCategory preferenceCategoryMore) { - String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC); + String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC).trim(); Preference pMnemonic = findPreference("mnemonic"); if (pMnemonic != null) { @@ -991,7 +991,7 @@ public void handleMnemonicRequest(Intent data) { RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) { ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this); - String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC); + String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC).trim(); AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.FallbackTheming_Dialog); AlertDialog alertDialog = builder.setTitle(R.string.prefs_e2e_mnemonic) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SetupEncryptionDialogFragment.java b/app/src/main/java/com/owncloud/android/ui/dialog/SetupEncryptionDialogFragment.java index 8eab16356029..1e8141495bc2 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SetupEncryptionDialogFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SetupEncryptionDialogFragment.java @@ -201,7 +201,8 @@ private Dialog createDialog(View v) { mnemonic); arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), - EncryptionUtils.PRIVATE_KEY, decryptedPrivateKey); + EncryptionUtils.PRIVATE_KEY, + decryptedPrivateKey); dialog1.dismiss(); Log_OC.d(TAG, "Private key successfully decrypted and stored"); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index e868078380dc..5bccea2fc27f 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -219,7 +219,18 @@ private void setupView() { if (file.canReshare()) { if (file.isEncrypted() || (parentFile != null && parentFile.isEncrypted())) { - binding.searchView.setQueryHint(getResources().getString(R.string.secure_share_search)); + if (file.getE2eCounter() == -1) { + // V1 cannot share + binding.searchContainer.setVisibility(View.GONE); + } else { + binding.searchView.setQueryHint(getResources().getString(R.string.secure_share_search)); + + if (file.isSharedViaLink()) { + binding.searchView.setQueryHint(getResources().getString(R.string.share_not_allowed_when_file_drop)); + binding.searchView.setInputType(InputType.TYPE_NULL); + disableSearchView(binding.searchView); + } + } } else { binding.searchView.setQueryHint(getResources().getString(R.string.share_search)); } @@ -429,6 +440,7 @@ public void refreshCapabilitiesFromDB() { * before reading database. */ public void refreshSharesFromDB() { + file = fileDataStorageManager.getFileById(file.getFileId()); ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesList.getAdapter(); if (adapter == null) { diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java index 03f81e7c9237..acc654dff8b5 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java @@ -55,6 +55,7 @@ import com.owncloud.android.lib.resources.e2ee.StoreMetadataRemoteOperation; import com.owncloud.android.lib.resources.e2ee.StoreMetadataV2RemoteOperation; import com.owncloud.android.lib.resources.e2ee.UnlockFileRemoteOperation; +import com.owncloud.android.lib.resources.e2ee.UnlockFileV1RemoteOperation; import com.owncloud.android.lib.resources.e2ee.UpdateMetadataRemoteOperation; import com.owncloud.android.lib.resources.e2ee.UpdateMetadataV2RemoteOperation; import com.owncloud.android.lib.resources.status.E2EVersion; @@ -234,7 +235,7 @@ public static EncryptedFolderMetadataFileV1 encryptFolderMetadata( } // set checksum - String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC); + String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC).trim(); String checksum = EncryptionUtils.generateChecksum(decryptedFolderMetadata, mnemonic); encryptedFolderMetadata.getMetadata().setChecksum(checksum); @@ -1421,6 +1422,14 @@ public static RemoteOperationResult unlockFolder(OCFile parentFolder, OwnC } } + public static RemoteOperationResult unlockFolderV1(OCFile parentFolder, OwnCloudClient client, String token) { + if (token != null) { + return new UnlockFileV1RemoteOperation(parentFolder.getLocalId(), token).execute(client); + } else { + return new RemoteOperationResult<>(new Exception("No token available")); + } + } + public static X509Certificate convertCertFromString(String string) throws CertificateException { String trimmedCert = string.replace("-----BEGIN CERTIFICATE-----\n", "") .replace("-----END CERTIFICATE-----\n", ""); 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 1a261d29fa15..7247b0b985e7 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt @@ -377,8 +377,6 @@ class EncryptionUtilsV2 { ocFile.setE2eCounter(metadataFile.metadata.counter) fileDataStorageManager.saveFile(ocFile) - // TODO change metadata key always? - return metadataFile } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 46ccbc377de2..ca0862f77b4b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1102,4 +1102,5 @@ More Secure sharing failed Secure sharing is not set up for this user + Resharing is not allowed during secure file drop