Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
Signed-off-by: tobiasKaminsky <[email protected]>
  • Loading branch information
tobiasKaminsky committed Sep 27, 2023
1 parent f773222 commit 01a84f1
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class Data {
private String filename;
private String mimetype;
private String key;
private int version;
private double version;

public String getKey() {
return this.key;
Expand All @@ -40,7 +40,7 @@ public String getMimetype() {
return this.mimetype;
}

public int getVersion() {
public double getVersion() {
return this.version;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
*/
package com.owncloud.android.datamodel.e2e.v2.encrypted

import com.owncloud.android.datamodel.EncryptedFiledrop

/**
* Decrypted class representation of metadata json of folder metadata.
*/
data class EncryptedFolderMetadataFile(
val metadata: EncryptedMetadata,
val users: List<EncryptedUser>,
// val filedrop: Map<String, EncryptedFolderMetadataFileV1.EncryptedFile>, TODO re-enable in v2.1
val filedrop: MutableMap<String, EncryptedFiledrop>,
val version: String = "2.0"
)
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ private RemoteOperationResult encryptedCreateV1(OCFile parent, OwnCloudClient cl
String encryptedFileName = createRandomFileName(metadata);
encryptedRemotePath = parent.getRemotePath() + encryptedFileName;

RemoteOperationResult result = new CreateFolderRemoteOperation(encryptedRemotePath,
true,
token)
RemoteOperationResult<String> result = new CreateFolderRemoteOperation(encryptedRemotePath,
true,
token)
.execute(client);

if (result.isSuccess()) {
Expand All @@ -184,7 +184,7 @@ private RemoteOperationResult encryptedCreateV1(OCFile parent, OwnCloudClient cl

// unlock folder
if (token != null) {
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolder(parent, client, token);
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolderV1(parent, client, token);

if (unlockFolderResult.isSuccess()) {
token = null;
Expand Down Expand Up @@ -217,7 +217,7 @@ private RemoteOperationResult encryptedCreateV1(OCFile parent, OwnCloudClient cl

return result;
} catch (Exception e) {
if (!EncryptionUtils.unlockFolder(parent, client, token).isSuccess()) {
if (!EncryptionUtils.unlockFolderV1(parent, client, token).isSuccess()) {
throw new RuntimeException("Could not clean up after failing folder creation!", e);

Check warning on line 221 in app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java#L221

The String literal "Could not clean up after failing folder creation!" appears 4 times in this file; the first occurrence is on line 221
}

Expand All @@ -241,7 +241,7 @@ private RemoteOperationResult encryptedCreateV1(OCFile parent, OwnCloudClient cl
} finally {
// unlock folder
if (token != null) {
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolder(parent, client, token);
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolderV1(parent, client, token);

if (!unlockFolderResult.isSuccess()) {
// TODO E2E: do better
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ protected RemoteOperationResult run(OwnCloudClient client) {
client,
context,
user,
token);
token
);

if (object instanceof DecryptedFolderMetadataFileV1) {
throw new RuntimeException("Trying to share on e2e v1!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.MimeType;
import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.theme.CapabilityUtils;

import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -484,8 +485,10 @@ private void synchronizeData(List<Object> folderAndFiles) {
mContext);
}

if (encryptedAncestor && object == null) {
throw new IllegalStateException("metadata is null!");
if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) {
if (encryptedAncestor && object == null) {

Check warning on line 489 in app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java#L489

These nested if statements could be combined
throw new IllegalStateException("metadata is null!");
}
}

// get current data about local contents of the folder to synchronize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
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.decrypted.DecryptedMetadata;
import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFile;
import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFolderMetadataFileV1;
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFolderMetadataFile;
Expand Down Expand Up @@ -68,6 +69,7 @@
import com.owncloud.android.utils.MimeType;
import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.UriUtils;
import com.owncloud.android.utils.theme.CapabilityUtils;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.RequestEntity;
Expand All @@ -86,6 +88,7 @@
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -461,8 +464,11 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare
return result;
}
/***** E2E *****/
// whenever we change something, increase counter
long counter = parentFile.getE2eCounter() + 1;
// Only on V2+: whenever we change something, increase counter
long counter = -1;
if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) {
counter = parentFile.getE2eCounter() + 1;
}

// we might have an old token from interrupted upload
if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) {
Expand All @@ -484,11 +490,25 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare

Object object = EncryptionUtils.downloadFolderMetadata(parentFile, client, mContext, user, token);

if (object == null) {
// TODO return error
return new RemoteOperationResult(new IllegalStateException("Metadata does not exist"));
if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) {
if (object == null) {
// TODO return error
return new RemoteOperationResult(new IllegalStateException("Metadata does not exist"));
} else {
metadataExists = true;
}
} else {
metadataExists = true;
// v1 is allowed to be null, thus create it
DecryptedFolderMetadataFileV1 metadata = new DecryptedFolderMetadataFileV1();
metadata.setMetadata(new DecryptedMetadata());
metadata.getMetadata().setVersion(1.2);
metadata.getMetadata().setMetadataKeys(new HashMap<>());
String metadataKey = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey());
String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(metadataKey, publicKey);
metadata.getMetadata().setMetadataKey(encryptedMetadataKey);

object = metadata;
metadataExists = false;
}

// todo fail if no metadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1772,18 +1772,20 @@ private void encryptFolder(OCFile folder,
requireContext(),
user,
storageManager);

// unlock folder
EncryptionUtils.unlockFolder(folder, client, token);

} else if (ocCapability.getEndToEndEncryptionApiVersion() == E2EVersion.V1_0 ||
ocCapability.getEndToEndEncryptionApiVersion() == E2EVersion.V1_1 ||
ocCapability.getEndToEndEncryptionApiVersion() == E2EVersion.V1_2
) {
// TODO encrypt on V1
// unlock folder
EncryptionUtils.unlockFolderV1(folder, client, token);
} else if (ocCapability.getEndToEndEncryptionApiVersion() == E2EVersion.UNKNOWN) {
throw new IllegalArgumentException("Unknown E2E version");
}

// unlock folder
EncryptionUtils.unlockFolder(folder, client, token);

mAdapter.setEncryptionAttributeForItemID(remoteId, shouldBeEncrypted);
} else if (remoteOperationResult.getHttpCode() == HttpStatus.SC_FORBIDDEN) {
Snackbar.make(getRecyclerView(),
Expand Down
53 changes: 35 additions & 18 deletions app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@
import com.owncloud.android.lib.resources.e2ee.UpdateMetadataV2RemoteOperation;
import com.owncloud.android.lib.resources.status.E2EVersion;
import com.owncloud.android.lib.resources.status.NextcloudVersion;
import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.operations.UploadException;
import com.owncloud.android.utils.theme.CapabilityUtils;

import org.apache.commons.httpclient.HttpStatus;

Expand Down Expand Up @@ -415,19 +417,19 @@ public static DecryptedFolderMetadataFileV1 decryptFolderMetaData(EncryptedFolde
return null;
}

OCCapability capability = CapabilityUtils.getCapability(context);

// decrypt metadata
EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2();
String serializedEncryptedMetadata = getMetadataOperationResult.getResultData().getMetadata();

return encryptionUtilsV2.parseAnyMetadata(getMetadataOperationResult.getResultData(),
user,
client,
context,
folder);
/*
E2EVersion version = determinateVersion(serializedEncryptedMetadata);
// return encryptionUtilsV2.parseAnyMetadata(getMetadataOperationResult.getResultData(),
// user,
// client,
// context,
// folder);

E2EVersion version = determinateVersion(serializedEncryptedMetadata);

switch (version) {
case UNKNOWN:
Expand All @@ -437,16 +439,32 @@ public static DecryptedFolderMetadataFileV1 decryptFolderMetaData(EncryptedFolde
case V1_0, V1_1, V1_2:
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
String privateKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PRIVATE_KEY);
String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY);
EncryptedFolderMetadataFileV1 encryptedFolderMetadata = EncryptionUtils.deserializeJSON(
serializedEncryptedMetadata, new TypeToken<>() {
});

try {
return decryptFolderMetaData(encryptedFolderMetadata,
privateKey,
arbitraryDataProvider,
user,
folder.getLocalId());
DecryptedFolderMetadataFileV1 v1 = decryptFolderMetaData(encryptedFolderMetadata,
privateKey,
arbitraryDataProvider,
user,
folder.getLocalId());

if (capability.getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) {
new EncryptionUtilsV2().migrateV1ToV2(
v1,
client.getUserId(),
publicKey,
folder,
new FileDataStorageManager(user, context.getContentResolver()),
client,
user,
context
);
} else {
return v1;
}
} catch (Exception e) {
// TODO do not crash, but show meaningful error
Log_OC.e(TAG, "Could not decrypt metadata for " + folder.getDecryptedFileName(), e);
Expand All @@ -461,8 +479,6 @@ public static DecryptedFolderMetadataFileV1 decryptFolderMetaData(EncryptedFolde
folder);
}
return null;
*/
}

public static E2EVersion determinateVersion(String metadata) {
Expand Down Expand Up @@ -1278,17 +1294,17 @@ public static Pair<Boolean, DecryptedFolderMetadataFileV1> retrieveMetadataV1(OC
long localId = parentFile.getLocalId();

GetMetadataRemoteOperation getMetadataOperation = new GetMetadataRemoteOperation(localId);
RemoteOperationResult getMetadataOperationResult = getMetadataOperation.execute(client);
RemoteOperationResult<MetadataResponse> getMetadataOperationResult = getMetadataOperation.execute(client);

DecryptedFolderMetadataFileV1 metadata;

if (getMetadataOperationResult.isSuccess()) {
// decrypt metadata
String serializedEncryptedMetadata = (String) getMetadataOperationResult.getData().get(0);
String serializedEncryptedMetadata = getMetadataOperationResult.getResultData().getMetadata();


EncryptedFolderMetadataFileV1 encryptedFolderMetadata = EncryptionUtils.deserializeJSON(
serializedEncryptedMetadata, new TypeToken<EncryptedFolderMetadataFileV1>() {
serializedEncryptedMetadata, new TypeToken<>() {
});

return new Pair<>(Boolean.TRUE, decryptFolderMetaData(encryptedFolderMetadata,
Expand All @@ -1298,6 +1314,7 @@ public static Pair<Boolean, DecryptedFolderMetadataFileV1> retrieveMetadataV1(OC
localId));

} else if (getMetadataOperationResult.getHttpCode() == HttpStatus.SC_NOT_FOUND) {
// TODO extract
// new metadata
metadata = new DecryptedFolderMetadataFileV1();
metadata.setMetadata(new DecryptedMetadata());
Expand Down
47 changes: 43 additions & 4 deletions app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ import com.google.gson.reflect.TypeToken
import com.nextcloud.client.account.User
import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
import com.owncloud.android.datamodel.EncryptedFiledrop
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.e2e.v1.decrypted.Data
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1
import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFolderMetadataFileV1
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile
Expand Down Expand Up @@ -145,7 +147,8 @@ class EncryptionUtilsV2 {

return EncryptedFolderMetadataFile(
encryptedMetadata,
encryptedUsers
encryptedUsers,
mutableMapOf()
)

// if (metadataFile.users.isEmpty()) {
Expand Down Expand Up @@ -183,8 +186,6 @@ class EncryptionUtilsV2 {
val parent =
storageManager.getFileById(ocFile.parentId) ?: throw IllegalStateException("Cannot retrieve metadata")

// val decryptedFolderMetadataFile = if (encryptedUser == null) {

val decryptedFolderMetadataFile = if (parent.isEncrypted) {
// we are in a subfolder, decrypt information is in top most encrypted folder
val topMostMetadata = retrieveTopMostMetadata(
Expand All @@ -199,10 +200,48 @@ class EncryptionUtilsV2 {
decryptedMetadata.metadataKey = topMostMetadata.metadata.metadataKey
decryptedMetadata.keyChecksums.addAll(topMostMetadata.metadata.keyChecksums)

val fileDrop = metadataFile.filedrop
if (fileDrop.isNotEmpty()) {
for (entry in fileDrop) {
val key: String = entry.key
val encryptedFile: EncryptedFiledrop = entry.value

// decrypt key
val encryptedKey = EncryptionUtils.decryptStringAsymmetric(
encryptedFile.encryptedKey,
privateKey
)

// decrypt encrypted blob with key
val decryptedData = EncryptionUtils.decryptStringSymmetricAsString(
encryptedFile.encrypted,
EncryptionUtils.decodeStringToBase64Bytes(encryptedKey),
EncryptionUtils.decodeStringToBase64Bytes(encryptedFile.encryptedInitializationVector),
EncryptionUtils.decodeStringToBase64Bytes(encryptedFile.encryptedTag)
)

val data = EncryptionUtils.deserializeJSON(decryptedData,
object : TypeToken<Data>() {})

val decryptedFile = DecryptedFile(
data.filename,
data.mimetype,
encryptedFile.initializationVector,
encryptedFile.authenticationTag,
encryptedFile.encryptedKey
)

decryptedMetadata.files[key] = decryptedFile

// remove from filedrop
fileDrop.remove(key)
}
}

DecryptedFolderMetadataFile(
decryptedMetadata,
topMostMetadata.users,
topMostMetadata.filedrop
mutableMapOf()
)
} else {
val encryptedUser = metadataFile.users.find { it.userId == userId }
Expand Down
Loading

0 comments on commit 01a84f1

Please sign in to comment.