|
@@ -28,12 +28,14 @@ import android.util.Pair;
|
|
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.gson.Gson;
|
|
import com.google.gson.Gson;
|
|
|
|
+import com.google.gson.GsonBuilder;
|
|
import com.google.gson.reflect.TypeToken;
|
|
import com.google.gson.reflect.TypeToken;
|
|
import com.nextcloud.client.account.User;
|
|
import com.nextcloud.client.account.User;
|
|
import com.owncloud.android.R;
|
|
import com.owncloud.android.R;
|
|
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
|
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
|
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
|
|
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
|
|
import com.owncloud.android.datamodel.DecryptedFolderMetadata;
|
|
import com.owncloud.android.datamodel.DecryptedFolderMetadata;
|
|
|
|
+import com.owncloud.android.datamodel.EncryptedFiledrop;
|
|
import com.owncloud.android.datamodel.EncryptedFolderMetadata;
|
|
import com.owncloud.android.datamodel.EncryptedFolderMetadata;
|
|
import com.owncloud.android.datamodel.OCFile;
|
|
import com.owncloud.android.datamodel.OCFile;
|
|
import com.owncloud.android.lib.common.OwnCloudClient;
|
|
import com.owncloud.android.lib.common.OwnCloudClient;
|
|
@@ -79,6 +81,7 @@ import java.security.spec.KeySpec;
|
|
import java.security.spec.PKCS8EncodedKeySpec;
|
|
import java.security.spec.PKCS8EncodedKeySpec;
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Arrays;
|
|
|
|
+import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
@@ -120,6 +123,8 @@ public final class EncryptionUtils {
|
|
private static final String AES = "AES";
|
|
private static final String AES = "AES";
|
|
private static final String RSA_CIPHER = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
|
|
private static final String RSA_CIPHER = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
|
|
private static final String RSA = "RSA";
|
|
private static final String RSA = "RSA";
|
|
|
|
+ @VisibleForTesting
|
|
|
|
+ public static final String MIGRATED_FOLDER_IDS = "MIGRATED_FOLDER_IDS";
|
|
|
|
|
|
private EncryptionUtils() {
|
|
private EncryptionUtils() {
|
|
// utility class -> private constructor
|
|
// utility class -> private constructor
|
|
@@ -129,12 +134,28 @@ public final class EncryptionUtils {
|
|
JSON
|
|
JSON
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
+ public static <T> T deserializeJSON(String json, TypeToken<T> type, boolean excludeTransient) {
|
|
|
|
+ if (excludeTransient) {
|
|
|
|
+ return new Gson().fromJson(json, type.getType());
|
|
|
|
+ } else {
|
|
|
|
+ return new GsonBuilder().excludeFieldsWithModifiers(0).create().fromJson(json, type.getType());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
public static <T> T deserializeJSON(String json, TypeToken<T> type) {
|
|
public static <T> T deserializeJSON(String json, TypeToken<T> type) {
|
|
- return new Gson().fromJson(json, type.getType());
|
|
|
|
|
|
+ return deserializeJSON(json, type, false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static String serializeJSON(Object data, boolean excludeTransient) {
|
|
|
|
+ if (excludeTransient) {
|
|
|
|
+ return new Gson().toJson(data);
|
|
|
|
+ } else {
|
|
|
|
+ return new GsonBuilder().excludeFieldsWithModifiers(0).create().toJson(data);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
public static String serializeJSON(Object data) {
|
|
public static String serializeJSON(Object data) {
|
|
- return new Gson().toJson(data);
|
|
|
|
|
|
+ return serializeJSON(data, false);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -148,19 +169,33 @@ public final class EncryptionUtils {
|
|
* @return EncryptedFolderMetadata encrypted folder metadata
|
|
* @return EncryptedFolderMetadata encrypted folder metadata
|
|
*/
|
|
*/
|
|
public static EncryptedFolderMetadata encryptFolderMetadata(DecryptedFolderMetadata decryptedFolderMetadata,
|
|
public static EncryptedFolderMetadata encryptFolderMetadata(DecryptedFolderMetadata decryptedFolderMetadata,
|
|
- String privateKey
|
|
|
|
|
|
+ String privateKey,
|
|
|
|
+ String publicKey,
|
|
|
|
+ ArbitraryDataProvider arbitraryDataProvider,
|
|
|
|
+ User user,
|
|
|
|
+ long parentId
|
|
)
|
|
)
|
|
throws NoSuchAlgorithmException, InvalidKeyException,
|
|
throws NoSuchAlgorithmException, InvalidKeyException,
|
|
InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException,
|
|
InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException,
|
|
- IllegalBlockSizeException, InvalidKeySpecException {
|
|
|
|
|
|
+ IllegalBlockSizeException, InvalidKeySpecException, CertificateException {
|
|
|
|
|
|
HashMap<String, EncryptedFolderMetadata.EncryptedFile> files = new HashMap<>();
|
|
HashMap<String, EncryptedFolderMetadata.EncryptedFile> files = new HashMap<>();
|
|
- HashMap<String, EncryptedFolderMetadata.EncryptedFile> filesdrop = new HashMap<>();
|
|
|
|
|
|
+ HashMap<String, EncryptedFiledrop> filesdrop = new HashMap<>();
|
|
EncryptedFolderMetadata encryptedFolderMetadata = new EncryptedFolderMetadata(decryptedFolderMetadata
|
|
EncryptedFolderMetadata encryptedFolderMetadata = new EncryptedFolderMetadata(decryptedFolderMetadata
|
|
.getMetadata(),
|
|
.getMetadata(),
|
|
files,
|
|
files,
|
|
filesdrop);
|
|
filesdrop);
|
|
|
|
|
|
|
|
+ // set new metadata key
|
|
|
|
+ byte[] metadataKeyBytes = EncryptionUtils.generateKey();
|
|
|
|
+ String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(
|
|
|
|
+ EncryptionUtils.encodeBytesToBase64String(metadataKeyBytes),
|
|
|
|
+ publicKey);
|
|
|
|
+ encryptedFolderMetadata.getMetadata().setMetadataKey(encryptedMetadataKey);
|
|
|
|
+
|
|
|
|
+ // store that this folder has been migrated
|
|
|
|
+ addIdToMigratedIds(parentId, user, arbitraryDataProvider);
|
|
|
|
+
|
|
// Encrypt each file in "files"
|
|
// Encrypt each file in "files"
|
|
for (Map.Entry<String, DecryptedFolderMetadata.DecryptedFile> entry : decryptedFolderMetadata
|
|
for (Map.Entry<String, DecryptedFolderMetadata.DecryptedFile> entry : decryptedFolderMetadata
|
|
.getFiles().entrySet()) {
|
|
.getFiles().entrySet()) {
|
|
@@ -169,39 +204,45 @@ public final class EncryptionUtils {
|
|
|
|
|
|
EncryptedFolderMetadata.EncryptedFile encryptedFile = new EncryptedFolderMetadata.EncryptedFile();
|
|
EncryptedFolderMetadata.EncryptedFile encryptedFile = new EncryptedFolderMetadata.EncryptedFile();
|
|
encryptedFile.setInitializationVector(decryptedFile.getInitializationVector());
|
|
encryptedFile.setInitializationVector(decryptedFile.getInitializationVector());
|
|
- encryptedFile.setMetadataKey(decryptedFile.getMetadataKey());
|
|
|
|
encryptedFile.setAuthenticationTag(decryptedFile.getAuthenticationTag());
|
|
encryptedFile.setAuthenticationTag(decryptedFile.getAuthenticationTag());
|
|
|
|
|
|
- byte[] decryptedMetadataKey = EncryptionUtils.decodeStringToBase64Bytes(EncryptionUtils.decryptStringAsymmetric(
|
|
|
|
- decryptedFolderMetadata.getMetadata().getMetadataKeys().get(encryptedFile.getMetadataKey()),
|
|
|
|
- privateKey));
|
|
|
|
-
|
|
|
|
// encrypt
|
|
// encrypt
|
|
String dataJson = EncryptionUtils.serializeJSON(decryptedFile.getEncrypted());
|
|
String dataJson = EncryptionUtils.serializeJSON(decryptedFile.getEncrypted());
|
|
- encryptedFile.setEncrypted(EncryptionUtils.encryptStringSymmetric(dataJson, decryptedMetadataKey));
|
|
|
|
|
|
+ encryptedFile.setEncrypted(EncryptionUtils.encryptStringSymmetric(dataJson, metadataKeyBytes));
|
|
|
|
|
|
files.put(key, encryptedFile);
|
|
files.put(key, encryptedFile);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // set checksum
|
|
|
|
+ String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC);
|
|
|
|
+ String checksum = EncryptionUtils.generateChecksum(decryptedFolderMetadata, mnemonic);
|
|
|
|
+ encryptedFolderMetadata.getMetadata().setChecksum(checksum);
|
|
|
|
+
|
|
return encryptedFolderMetadata;
|
|
return encryptedFolderMetadata;
|
|
}
|
|
}
|
|
|
|
|
|
@VisibleForTesting
|
|
@VisibleForTesting
|
|
public static void encryptFileDropFiles(DecryptedFolderMetadata decryptedFolderMetadata, EncryptedFolderMetadata encryptedFolderMetadata, String cert) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, CertificateException {
|
|
public static void encryptFileDropFiles(DecryptedFolderMetadata decryptedFolderMetadata, EncryptedFolderMetadata encryptedFolderMetadata, String cert) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, CertificateException {
|
|
- final Map<String, EncryptedFolderMetadata.EncryptedFile> filesdrop = encryptedFolderMetadata.getFiledrop();
|
|
|
|
|
|
+ final Map<String, EncryptedFiledrop> filesdrop = encryptedFolderMetadata.getFiledrop();
|
|
for (Map.Entry<String, DecryptedFolderMetadata.DecryptedFile> entry : decryptedFolderMetadata
|
|
for (Map.Entry<String, DecryptedFolderMetadata.DecryptedFile> entry : decryptedFolderMetadata
|
|
.getFiledrop().entrySet()) {
|
|
.getFiledrop().entrySet()) {
|
|
String key = entry.getKey();
|
|
String key = entry.getKey();
|
|
DecryptedFolderMetadata.DecryptedFile decryptedFile = entry.getValue();
|
|
DecryptedFolderMetadata.DecryptedFile decryptedFile = entry.getValue();
|
|
|
|
|
|
- EncryptedFolderMetadata.EncryptedFile encryptedFile = new EncryptedFolderMetadata.EncryptedFile();
|
|
|
|
- encryptedFile.setInitializationVector(decryptedFile.getInitializationVector());
|
|
|
|
- encryptedFile.setMetadataKey(decryptedFile.getMetadataKey());
|
|
|
|
- encryptedFile.setAuthenticationTag(decryptedFile.getAuthenticationTag());
|
|
|
|
|
|
+ // TODO
|
|
|
|
+ String dataJson = EncryptionUtils.serializeJSON(decryptedFile.getEncrypted());
|
|
|
|
+ EncryptedFiledrop encryptedFile = new EncryptedFiledrop(dataJson,
|
|
|
|
+ decryptedFile.getInitializationVector(),
|
|
|
|
+ decryptedFile.getAuthenticationTag(),
|
|
|
|
+ "123",
|
|
|
|
+ "123",
|
|
|
|
+ "123");
|
|
|
|
+// encryptedFile.setInitializationVector(decryptedFile.getInitializationVector());
|
|
|
|
+// encryptedFile.setAuthenticationTag(decryptedFile.getAuthenticationTag());
|
|
|
|
|
|
// encrypt
|
|
// encrypt
|
|
- String dataJson = EncryptionUtils.serializeJSON(decryptedFile.getEncrypted());
|
|
|
|
- encryptedFile.setEncrypted(EncryptionUtils.encryptStringAsymmetric(dataJson, cert));
|
|
|
|
|
|
+
|
|
|
|
+// encryptedFile.setEncrypted(EncryptionUtils.encryptStringAsymmetric(dataJson, cert));
|
|
|
|
|
|
filesdrop.put(key, encryptedFile);
|
|
filesdrop.put(key, encryptedFile);
|
|
}
|
|
}
|
|
@@ -211,7 +252,10 @@ public final class EncryptionUtils {
|
|
* decrypt folder metaData with private key
|
|
* decrypt folder metaData with private key
|
|
*/
|
|
*/
|
|
public static DecryptedFolderMetadata decryptFolderMetaData(EncryptedFolderMetadata encryptedFolderMetadata,
|
|
public static DecryptedFolderMetadata decryptFolderMetaData(EncryptedFolderMetadata encryptedFolderMetadata,
|
|
- String privateKey)
|
|
|
|
|
|
+ String privateKey,
|
|
|
|
+ ArbitraryDataProvider arbitraryDataProvider,
|
|
|
|
+ User user,
|
|
|
|
+ long remoteId)
|
|
throws NoSuchAlgorithmException, InvalidKeyException,
|
|
throws NoSuchAlgorithmException, InvalidKeyException,
|
|
InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException,
|
|
InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException,
|
|
IllegalBlockSizeException, InvalidKeySpecException {
|
|
IllegalBlockSizeException, InvalidKeySpecException {
|
|
@@ -220,46 +264,82 @@ public final class EncryptionUtils {
|
|
DecryptedFolderMetadata decryptedFolderMetadata = new DecryptedFolderMetadata(
|
|
DecryptedFolderMetadata decryptedFolderMetadata = new DecryptedFolderMetadata(
|
|
encryptedFolderMetadata.getMetadata(), files);
|
|
encryptedFolderMetadata.getMetadata(), files);
|
|
|
|
|
|
- for (Map.Entry<String, EncryptedFolderMetadata.EncryptedFile> entry : encryptedFolderMetadata
|
|
|
|
- .getFiles().entrySet()) {
|
|
|
|
- String key = entry.getKey();
|
|
|
|
- EncryptedFolderMetadata.EncryptedFile encryptedFile = entry.getValue();
|
|
|
|
|
|
+ byte[] decryptedMetadataKey = null;
|
|
|
|
|
|
- DecryptedFolderMetadata.DecryptedFile decryptedFile = new DecryptedFolderMetadata.DecryptedFile();
|
|
|
|
- decryptedFile.setInitializationVector(encryptedFile.getInitializationVector());
|
|
|
|
- decryptedFile.setMetadataKey(encryptedFile.getMetadataKey());
|
|
|
|
- decryptedFile.setAuthenticationTag(encryptedFile.getAuthenticationTag());
|
|
|
|
|
|
+ String encryptedMetadataKey = decryptedFolderMetadata.getMetadata().getMetadataKey();
|
|
|
|
|
|
- byte[] decryptedMetadataKey = EncryptionUtils.decodeStringToBase64Bytes(
|
|
|
|
- EncryptionUtils.decryptStringAsymmetric(decryptedFolderMetadata.getMetadata()
|
|
|
|
- .getMetadataKeys().get(encryptedFile.getMetadataKey()),
|
|
|
|
- privateKey));
|
|
|
|
|
|
+ if (encryptedMetadataKey != null) {
|
|
|
|
+ decryptedMetadataKey = decodeStringToBase64Bytes(
|
|
|
|
+ decryptStringAsymmetric(encryptedMetadataKey, privateKey));
|
|
|
|
+ }
|
|
|
|
|
|
- // decrypt
|
|
|
|
- String dataJson = EncryptionUtils.decryptStringSymmetric(encryptedFile.getEncrypted(), decryptedMetadataKey);
|
|
|
|
- decryptedFile.setEncrypted(EncryptionUtils.deserializeJSON(dataJson,
|
|
|
|
- new TypeToken<DecryptedFolderMetadata.Data>() {
|
|
|
|
- }));
|
|
|
|
|
|
+ if (encryptedFolderMetadata.getFiles() != null) {
|
|
|
|
+ for (Map.Entry<String, EncryptedFolderMetadata.EncryptedFile> entry : encryptedFolderMetadata
|
|
|
|
+ .getFiles().entrySet()) {
|
|
|
|
+ String key = entry.getKey();
|
|
|
|
+ EncryptedFolderMetadata.EncryptedFile encryptedFile = entry.getValue();
|
|
|
|
|
|
- files.put(key, decryptedFile);
|
|
|
|
|
|
+ DecryptedFolderMetadata.DecryptedFile decryptedFile = new DecryptedFolderMetadata.DecryptedFile();
|
|
|
|
+ decryptedFile.setInitializationVector(encryptedFile.getInitializationVector());
|
|
|
|
+ decryptedFile.setMetadataKey(encryptedFile.getMetadataKey());
|
|
|
|
+ decryptedFile.setAuthenticationTag(encryptedFile.getAuthenticationTag());
|
|
|
|
+
|
|
|
|
+ if (decryptedMetadataKey == null) {
|
|
|
|
+ decryptedMetadataKey = EncryptionUtils.decodeStringToBase64Bytes(
|
|
|
|
+ decryptStringAsymmetric(decryptedFolderMetadata.getMetadata()
|
|
|
|
+ .getMetadataKeys().get(encryptedFile.getMetadataKey()),
|
|
|
|
+ privateKey));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // decrypt
|
|
|
|
+ String dataJson = EncryptionUtils.decryptStringSymmetric(encryptedFile.getEncrypted(), decryptedMetadataKey);
|
|
|
|
+ decryptedFile.setEncrypted(EncryptionUtils.deserializeJSON(dataJson,
|
|
|
|
+ new TypeToken<DecryptedFolderMetadata.Data>() {
|
|
|
|
+ }));
|
|
|
|
+
|
|
|
|
+ files.put(key, decryptedFile);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- Map<String, EncryptedFolderMetadata.EncryptedFile> fileDrop = encryptedFolderMetadata.getFiledrop();
|
|
|
|
|
|
+ // verify checksum
|
|
|
|
+ String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC);
|
|
|
|
+ String checksum = EncryptionUtils.generateChecksum(decryptedFolderMetadata, mnemonic);
|
|
|
|
+ String decryptedFolderChecksum = decryptedFolderMetadata.getMetadata().getChecksum();
|
|
|
|
+
|
|
|
|
+ if (TextUtils.isEmpty(decryptedFolderChecksum) &&
|
|
|
|
+ isFolderMigrated(remoteId, user, arbitraryDataProvider)) {
|
|
|
|
+ throw new IllegalStateException("Possible downgrade attack detected!");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!TextUtils.isEmpty(decryptedFolderChecksum) && !decryptedFolderChecksum.equals(checksum)) {
|
|
|
|
+ throw new IllegalStateException("Wrong checksum!");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Map<String, EncryptedFiledrop> fileDrop = encryptedFolderMetadata.getFiledrop();
|
|
|
|
|
|
if (fileDrop != null) {
|
|
if (fileDrop != null) {
|
|
- for (Map.Entry<String, EncryptedFolderMetadata.EncryptedFile> entry : fileDrop.entrySet()) {
|
|
|
|
|
|
+ for (Map.Entry<String, EncryptedFiledrop> entry : fileDrop.entrySet()) {
|
|
String key = entry.getKey();
|
|
String key = entry.getKey();
|
|
- EncryptedFolderMetadata.EncryptedFile encryptedFile = entry.getValue();
|
|
|
|
|
|
+ EncryptedFiledrop encryptedFile = entry.getValue();
|
|
|
|
+
|
|
|
|
+ // decrypt key
|
|
|
|
+ String encryptedKey = decryptStringAsymmetric(encryptedFile.getEncryptedKey(),
|
|
|
|
+ privateKey);
|
|
|
|
+
|
|
|
|
+ // decrypt encrypted blob with key
|
|
|
|
+ String decryptedData = decryptStringSymmetric(
|
|
|
|
+ encryptedFile.getEncrypted(),
|
|
|
|
+ decodeStringToBase64Bytes(encryptedKey),
|
|
|
|
+ decodeStringToBase64Bytes(encryptedFile.getEncryptedInitializationVector()),
|
|
|
|
+ decodeStringToBase64Bytes(encryptedFile.getEncryptedTag())
|
|
|
|
+ );
|
|
|
|
|
|
DecryptedFolderMetadata.DecryptedFile decryptedFile = new DecryptedFolderMetadata.DecryptedFile();
|
|
DecryptedFolderMetadata.DecryptedFile decryptedFile = new DecryptedFolderMetadata.DecryptedFile();
|
|
decryptedFile.setInitializationVector(encryptedFile.getInitializationVector());
|
|
decryptedFile.setInitializationVector(encryptedFile.getInitializationVector());
|
|
- decryptedFile.setMetadataKey(encryptedFile.getMetadataKey());
|
|
|
|
decryptedFile.setAuthenticationTag(encryptedFile.getAuthenticationTag());
|
|
decryptedFile.setAuthenticationTag(encryptedFile.getAuthenticationTag());
|
|
|
|
|
|
- // decrypt
|
|
|
|
- String dataJson = EncryptionUtils.decryptStringAsymmetric(encryptedFile.getEncrypted(), privateKey);
|
|
|
|
|
|
|
|
- decryptedFile.setEncrypted(EncryptionUtils.deserializeJSON(dataJson,
|
|
|
|
|
|
+ decryptedFile.setEncrypted(EncryptionUtils.deserializeJSON(decryptedData,
|
|
new TypeToken<DecryptedFolderMetadata.Data>() {
|
|
new TypeToken<DecryptedFolderMetadata.Data>() {
|
|
}));
|
|
}));
|
|
|
|
|
|
@@ -294,6 +374,7 @@ public final class EncryptionUtils {
|
|
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
|
|
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
|
|
String serializedEncryptedMetadata = (String) getMetadataOperationResult.getData().get(0);
|
|
String serializedEncryptedMetadata = (String) getMetadataOperationResult.getData().get(0);
|
|
String privateKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PRIVATE_KEY);
|
|
String privateKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PRIVATE_KEY);
|
|
|
|
+ String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY);
|
|
|
|
|
|
EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.deserializeJSON(
|
|
EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.deserializeJSON(
|
|
serializedEncryptedMetadata, new TypeToken<EncryptedFolderMetadata>() {
|
|
serializedEncryptedMetadata, new TypeToken<EncryptedFolderMetadata>() {
|
|
@@ -306,7 +387,10 @@ public final class EncryptionUtils {
|
|
}
|
|
}
|
|
DecryptedFolderMetadata decryptedFolderMetadata = EncryptionUtils.decryptFolderMetaData(
|
|
DecryptedFolderMetadata decryptedFolderMetadata = EncryptionUtils.decryptFolderMetaData(
|
|
encryptedFolderMetadata,
|
|
encryptedFolderMetadata,
|
|
- privateKey);
|
|
|
|
|
|
+ privateKey,
|
|
|
|
+ arbitraryDataProvider,
|
|
|
|
+ user,
|
|
|
|
+ folder.getLocalId());
|
|
|
|
|
|
boolean transferredFiledrop = filesDropCountBefore > 0 && decryptedFolderMetadata.getFiles().size() ==
|
|
boolean transferredFiledrop = filesDropCountBefore > 0 && decryptedFolderMetadata.getFiles().size() ==
|
|
encryptedFolderMetadata.getFiles().size() + filesDropCountBefore;
|
|
encryptedFolderMetadata.getFiles().size() + filesDropCountBefore;
|
|
@@ -317,7 +401,11 @@ public final class EncryptionUtils {
|
|
|
|
|
|
// upload metadata
|
|
// upload metadata
|
|
EncryptedFolderMetadata encryptedFolderMetadataNew = encryptFolderMetadata(decryptedFolderMetadata,
|
|
EncryptedFolderMetadata encryptedFolderMetadataNew = encryptFolderMetadata(decryptedFolderMetadata,
|
|
- privateKey);
|
|
|
|
|
|
+ privateKey,
|
|
|
|
+ publicKey,
|
|
|
|
+ arbitraryDataProvider,
|
|
|
|
+ user,
|
|
|
|
+ folder.getLocalId());
|
|
|
|
|
|
String serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadataNew);
|
|
String serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadataNew);
|
|
|
|
|
|
@@ -605,6 +693,36 @@ public final class EncryptionUtils {
|
|
return encodedCryptedBytes + delimiter + encodedIV;
|
|
return encodedCryptedBytes + delimiter + encodedIV;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ public static String decryptStringSymmetric(String string,
|
|
|
|
+ byte[] encryptionKeyBytes,
|
|
|
|
+ byte[] iv,
|
|
|
|
+ byte[] authenticationTag)
|
|
|
|
+ throws NoSuchPaddingException,
|
|
|
|
+ NoSuchAlgorithmException,
|
|
|
|
+ InvalidAlgorithmParameterException,
|
|
|
|
+ InvalidKeyException,
|
|
|
|
+ IllegalBlockSizeException,
|
|
|
|
+ BadPaddingException {
|
|
|
|
+ Cipher cipher = Cipher.getInstance(AES_CIPHER);
|
|
|
|
+ Key key = new SecretKeySpec(encryptionKeyBytes, AES);
|
|
|
|
+ GCMParameterSpec spec = new GCMParameterSpec(128, iv);
|
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, key, spec);
|
|
|
|
+
|
|
|
|
+ byte[] bytes = decodeStringToBase64Bytes(string);
|
|
|
|
+
|
|
|
|
+ // check authentication tag
|
|
|
|
+ byte[] extractedAuthenticationTag = Arrays.copyOfRange(bytes,
|
|
|
|
+ bytes.length - (128 / 8),
|
|
|
|
+ bytes.length);
|
|
|
|
+
|
|
|
|
+ if (!Arrays.equals(extractedAuthenticationTag, authenticationTag)) {
|
|
|
|
+ throw new SecurityException("Tag not correct");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ byte[] encodedBytes = cipher.doFinal(bytes);
|
|
|
|
+
|
|
|
|
+ return decodeBase64BytesToString(encodedBytes);
|
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
/**
|
|
* Decrypt string with RSA algorithm, ECB mode, OAEPWithSHA-256AndMGF1 padding Asymmetric encryption, with private
|
|
* Decrypt string with RSA algorithm, ECB mode, OAEPWithSHA-256AndMGF1 padding Asymmetric encryption, with private
|
|
@@ -883,7 +1001,10 @@ public final class EncryptionUtils {
|
|
public static Pair<Boolean, DecryptedFolderMetadata> retrieveMetadata(OCFile parentFile,
|
|
public static Pair<Boolean, DecryptedFolderMetadata> retrieveMetadata(OCFile parentFile,
|
|
OwnCloudClient client,
|
|
OwnCloudClient client,
|
|
String privateKey,
|
|
String privateKey,
|
|
- String publicKey) throws UploadException,
|
|
|
|
|
|
+ String publicKey,
|
|
|
|
+ ArbitraryDataProvider arbitraryDataProvider,
|
|
|
|
+ User user)
|
|
|
|
+ throws UploadException,
|
|
InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException,
|
|
InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException,
|
|
IllegalBlockSizeException, InvalidKeyException, InvalidKeySpecException, CertificateException {
|
|
IllegalBlockSizeException, InvalidKeyException, InvalidKeySpecException, CertificateException {
|
|
GetMetadataRemoteOperation getMetadataOperation = new GetMetadataRemoteOperation(parentFile.getLocalId());
|
|
GetMetadataRemoteOperation getMetadataOperation = new GetMetadataRemoteOperation(parentFile.getLocalId());
|
|
@@ -900,7 +1021,11 @@ public final class EncryptionUtils {
|
|
serializedEncryptedMetadata, new TypeToken<EncryptedFolderMetadata>() {
|
|
serializedEncryptedMetadata, new TypeToken<EncryptedFolderMetadata>() {
|
|
});
|
|
});
|
|
|
|
|
|
- return new Pair<>(Boolean.TRUE, EncryptionUtils.decryptFolderMetaData(encryptedFolderMetadata, privateKey));
|
|
|
|
|
|
+ return new Pair<>(Boolean.TRUE, EncryptionUtils.decryptFolderMetaData(encryptedFolderMetadata,
|
|
|
|
+ privateKey,
|
|
|
|
+ arbitraryDataProvider,
|
|
|
|
+ user,
|
|
|
|
+ parentFile.getLocalId()));
|
|
|
|
|
|
} else if (getMetadataOperationResult.getHttpCode() == HttpStatus.SC_NOT_FOUND) {
|
|
} else if (getMetadataOperationResult.getHttpCode() == HttpStatus.SC_NOT_FOUND) {
|
|
// new metadata
|
|
// new metadata
|
|
@@ -909,7 +1034,7 @@ public final class EncryptionUtils {
|
|
metadata.getMetadata().setMetadataKeys(new HashMap<>());
|
|
metadata.getMetadata().setMetadataKeys(new HashMap<>());
|
|
String metadataKey = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey());
|
|
String metadataKey = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey());
|
|
String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(metadataKey, publicKey);
|
|
String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(metadataKey, publicKey);
|
|
- metadata.getMetadata().getMetadataKeys().put(0, encryptedMetadataKey);
|
|
|
|
|
|
+ metadata.getMetadata().setMetadataKey(encryptedMetadataKey);
|
|
|
|
|
|
return new Pair<>(Boolean.FALSE, metadata);
|
|
return new Pair<>(Boolean.FALSE, metadata);
|
|
} else {
|
|
} else {
|
|
@@ -984,4 +1109,83 @@ public final class EncryptionUtils {
|
|
file.isFolder() &&
|
|
file.isFolder() &&
|
|
user.getServer().getVersion().isNewerOrEqual(NextcloudVersion.nextcloud_26);
|
|
user.getServer().getVersion().isNewerOrEqual(NextcloudVersion.nextcloud_26);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ public static String generateChecksum(DecryptedFolderMetadata metadata,
|
|
|
|
+ String mnemonic) throws NoSuchAlgorithmException {
|
|
|
|
+ StringBuilder stringBuilder = new StringBuilder();
|
|
|
|
+ stringBuilder.append(mnemonic.replaceAll(" ", ""));
|
|
|
|
+
|
|
|
|
+ ArrayList<String> keys = new ArrayList<>(metadata.getFiles().keySet());
|
|
|
|
+ Collections.sort(keys);
|
|
|
|
+
|
|
|
|
+ for (String key : keys) {
|
|
|
|
+ stringBuilder.append(key);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ stringBuilder.append(metadata.getMetadata().getMetadataKey());
|
|
|
|
+
|
|
|
|
+ // sha256 hash-sum
|
|
|
|
+ return sha256(stringBuilder.toString());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * SHA-256 hash of metadata-key
|
|
|
|
+ */
|
|
|
|
+ public static String sha256(String string) throws NoSuchAlgorithmException {
|
|
|
|
+ byte[] bytes = MessageDigest
|
|
|
|
+ .getInstance("SHA-256")
|
|
|
|
+ .digest(string.getBytes(StandardCharsets.UTF_8));
|
|
|
|
+
|
|
|
|
+ return bytesToHex(bytes);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static String bytesToHex(byte[] bytes) {
|
|
|
|
+ StringBuilder result = new StringBuilder();
|
|
|
|
+ for (byte individualByte : bytes) {
|
|
|
|
+ result.append(Integer.toString((individualByte & 0xff) + 0x100, 16)
|
|
|
|
+ .substring(1));
|
|
|
|
+ }
|
|
|
|
+ return result.toString();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static void addIdToMigratedIds(long id,
|
|
|
|
+ User user,
|
|
|
|
+ ArbitraryDataProvider arbitraryDataProvider) {
|
|
|
|
+ Gson gson = new Gson();
|
|
|
|
+ String ids = arbitraryDataProvider.getValue(user, MIGRATED_FOLDER_IDS);
|
|
|
|
+
|
|
|
|
+ ArrayList<Long> arrayList = gson.fromJson(ids, ArrayList.class);
|
|
|
|
+
|
|
|
|
+ if (arrayList == null) {
|
|
|
|
+ arrayList = new ArrayList<>();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (arrayList.contains(id)) {
|
|
|
|
+ // nothing to do here
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ arrayList.add(id);
|
|
|
|
+
|
|
|
|
+ String json = gson.toJson(arrayList);
|
|
|
|
+ arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(),
|
|
|
|
+ MIGRATED_FOLDER_IDS,
|
|
|
|
+ json);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static boolean isFolderMigrated(long id,
|
|
|
|
+ User user,
|
|
|
|
+ ArbitraryDataProvider arbitraryDataProvider) {
|
|
|
|
+ Gson gson = new Gson();
|
|
|
|
+ String ids = arbitraryDataProvider.getValue(user, MIGRATED_FOLDER_IDS);
|
|
|
|
+
|
|
|
|
+ ArrayList<Long> arrayList = gson.fromJson(ids, new TypeToken<List<Long>>() {
|
|
|
|
+ }.getType());
|
|
|
|
+
|
|
|
|
+ if (arrayList == null) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return arrayList.contains(id);
|
|
|
|
+ }
|
|
}
|
|
}
|