diff --git a/build.gradle b/build.gradle index 6a6db47660..2e6f812538 100644 --- a/build.gradle +++ b/build.gradle @@ -212,6 +212,9 @@ dependencies { implementation 'org.greenrobot:eventbus:3.0.0' implementation 'com.googlecode.ez-vcard:ez-vcard:0.10.2' implementation 'org.lukhnos:nnio:0.2' + + compile 'com.madgag.spongycastle:pkix:1.54.0.0' + // uncomment for gplay, modified // implementation "com.google.firebase:firebase-messaging:${googleLibraryVersion}" // implementation "com.google.android.gms:play-services-base:${googleLibraryVersion}" @@ -236,7 +239,7 @@ dependencies { androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.1' // UIAutomator - for cross-app UI tests, and to grant screen is turned on in Espresso tests - //androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2' + androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2' // fix conflict in dependencies; see http://g.co/androidstudio/app-test-app-conflict for details //androidTestImplementation "com.android.support:support-annotations:${supportLibraryVersion}" implementation 'org.jetbrains:annotations:15.0' diff --git a/drawable_resources/decrypt.svg b/drawable_resources/decrypt.svg new file mode 100644 index 0000000000..c66a9a9bcd --- /dev/null +++ b/drawable_resources/decrypt.svg @@ -0,0 +1 @@ + diff --git a/drawable_resources/encrypt.svg b/drawable_resources/encrypt.svg new file mode 100644 index 0000000000..3d161917f6 --- /dev/null +++ b/drawable_resources/encrypt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/drawable_resources/ic_list_encrypted_folder.svg b/drawable_resources/ic_list_encrypted_folder.svg new file mode 100644 index 0000000000..e2b62a99b0 --- /dev/null +++ b/drawable_resources/ic_list_encrypted_folder.svg @@ -0,0 +1 @@ + diff --git a/lint.xml b/lint.xml index 3d202e0e1a..9791cbefc5 100644 --- a/lint.xml +++ b/lint.xml @@ -3,6 +3,7 @@ + diff --git a/settings.gradle b/settings.gradle index 6a2e4c284a..cb1608d965 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':' \ No newline at end of file +include ':nextcloud-android-library' \ No newline at end of file diff --git a/src/androidTest/assets/decrypted.json b/src/androidTest/assets/decrypted.json new file mode 100644 index 0000000000..8aa7f4a2cf --- /dev/null +++ b/src/androidTest/assets/decrypted.json @@ -0,0 +1,42 @@ +{ + "metadata":{ + "encrypted":{ + "metadataKeys":{ + "0":"s4k4LPDpxoO53TKwem3Lo1", + "2":"…", + "3":"NEWESTMETADATAKEY" + } + }, + "initializationVector":"kahzfT4u86Knc+e3", + "sharing":{ + "recipient":{ + "blah@schiessle.org":"PUBLIC KEY", + "bjoern@schiessle.org":"PUBLIC KEY" + }, + "signature":"HMACOFRECIPIENTANDNEWESTMETADATAKEY" + }, + "version":1 + }, + "files":{ + "ia7OEEEyXMoRa1QWQk8r":{ + "encrypted":{ + "key":"jtboLmgGR1OQf2uneqCVHpklQLlIwWL5TXAQ0keK", + "filename":"test.txt", + "authenticationTag":"HMAC of file", + "version":1 + }, + "metadataKey":0, + "initializationVector":"+mHu52HyZq+pAAIN" + }, + "n9WXAIXO2wRY4R8nXwmo":{ + "encrypted":{ + "key":"s4k4LPDpxoO53TKwem3Lo1yJnbNUYH2KLrSFT8Ea", + "filename":"test2.txt", + "authenticationTag":"HMAC of file", + "version":1 + }, + "metadataKey":0, + "initializationVector":"sOFd17hCKWIv0gyB" + } + } +} diff --git a/src/androidTest/assets/encrypted.json b/src/androidTest/assets/encrypted.json new file mode 100644 index 0000000000..c865d4cedf --- /dev/null +++ b/src/androidTest/assets/encrypted.json @@ -0,0 +1,26 @@ +{ + "metadata":{ + "encrypted":"L01QcEZlcnBGbGJYZk0zQVRpME5venpiMlorZkVKNEo0YXFyV0Vla25Ed0kzOWtFYUg3V0Y3RVRKdDYrOWc0Y095bmhJU1hCRDlVVWkvdFJTa2swa1NTcXlPTEFiRmhVUDZFSzRzUXhiYWkrRkRPQ3VuNk1PakVxNDlBSUhWYUZucUJIZWhyeWNQZzF2d1d0VHh0cFhud3FacE55TmZOaFRRaVA2Zz09", + "initializationVector":"kahzfT4u86Knc+e3", + "sharing":{ + "recipient":{ + "blah@schiessle.org":"PUBLIC KEY", + "bjoern@schiessle.org":"PUBLIC KEY" + }, + "signature":"HMACOFRECIPIENTANDNEWESTMETADATAKEY" + }, + "version":1 + }, + "files":{ + "ia7OEEEyXMoRa1QWQk8r":{ + "encrypted":"a2xMcFI0cERHa2lCM3U1ajR5UXdnLzNmN0dCK2xnSmk5ck93bHhYTTI2ZmdQQlNaLzkxOTRJK3pHTlJzSjhoTTNjdlBhb2VVaEhHdGtBd0MvVUJlbWd1VFlvZDFKM2hLSkNmZWhoNlhIclBJaGU3ZllQY3lnMHprV1M1QUpIOCs2aUE5Tno2ZkZtRHpYMExabXRZcUpyZnk5Y2hyUTEyL2M4RDE1VmliR1ltbUxqKzBTUlJyc2ZCdTRwenZiR1hCVjk5OTA5UDVjb0llUCtPcjhVM1VBL1ZUNkpPaDYvSlpSaHlHTkVDbEpDRT0\\u003d", + "metadataKey":0, + "initializationVector":"+mHu52HyZq+pAAIN" + }, + "n9WXAIXO2wRY4R8nXwmo":{ + "encrypted":"VncyZU4yZStaRmFqeXJEQkpZNlNZa09yL3FIbVNNVW1wVDFWTENJN0pnSVBkdzIySUlrRnFDMGdzcTMwdHZneFlweEJjeGt5Z0crSVlUUkdGVk5iUzlBczJaejFlNTZzeEQrTUVHVldjRGQ4VDVIN0p6ZFFlRWsvRkN4M2FoQXlFOHpXOHQ5TnhXQUYycmpvNE5xNVowUStPTGZPc0hqaVdpUUR3dm9TV0hPS3JSaVd5c1YwSEhOYmVzZkZQaEF4Mk0rLzdDU05jK2dmNmdqb2ZndzIwOC91YXNlQUlPb2FnV3k0dWd0SFAvYz0\\u003d", + "metadataKey":0, + "initializationVector":"sOFd17hCKWIv0gyB" + } + } +} \ No newline at end of file diff --git a/src/androidTest/assets/encrypted/ia7OEEEyXMoRa1QWQk8r b/src/androidTest/assets/encrypted/ia7OEEEyXMoRa1QWQk8r new file mode 100644 index 0000000000..566f365aef Binary files /dev/null and b/src/androidTest/assets/encrypted/ia7OEEEyXMoRa1QWQk8r differ diff --git a/src/androidTest/assets/encrypted/n9WXAIXO2wRY4R8nXwmo b/src/androidTest/assets/encrypted/n9WXAIXO2wRY4R8nXwmo new file mode 100644 index 0000000000..f1673ec062 Binary files /dev/null and b/src/androidTest/assets/encrypted/n9WXAIXO2wRY4R8nXwmo differ diff --git a/src/androidTest/assets/ia7OEEEyXMoRa1QWQk8r b/src/androidTest/assets/ia7OEEEyXMoRa1QWQk8r new file mode 100644 index 0000000000..628c953700 --- /dev/null +++ b/src/androidTest/assets/ia7OEEEyXMoRa1QWQk8r @@ -0,0 +1,103 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/androidTest/assets/n9WXAIXO2wRY4R8nXwmo b/src/androidTest/assets/n9WXAIXO2wRY4R8nXwmo new file mode 100644 index 0000000000..c16ef846c2 Binary files /dev/null and b/src/androidTest/assets/n9WXAIXO2wRY4R8nXwmo differ diff --git a/src/androidTest/assets/srEPevoPqPZpPEaeDnS3 b/src/androidTest/assets/srEPevoPqPZpPEaeDnS3 new file mode 100644 index 0000000000..c16ef846c2 Binary files /dev/null and b/src/androidTest/assets/srEPevoPqPZpPEaeDnS3 differ diff --git a/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityTest.java b/src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityTest.java similarity index 99% rename from androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityTest.java rename to src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityTest.java index d367976d8f..9a63e9a981 100644 --- a/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityTest.java +++ b/src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityTest.java @@ -19,6 +19,7 @@ package com.owncloud.android.authentication; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -29,7 +30,6 @@ import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiDevice; import android.test.suitebuilder.annotation.LargeTest; -import static org.junit.Assert.assertTrue; import com.owncloud.android.R; import org.junit.Before; @@ -39,18 +39,15 @@ import org.junit.runner.RunWith; import java.lang.reflect.Field; -import android.app.Activity; - import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; import static android.support.test.espresso.action.ViewActions.typeText; - +import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; import static android.support.test.espresso.matcher.ViewMatchers.withId; - -import static android.support.test.espresso.assertion.ViewAssertions.matches; import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertTrue; @RunWith(AndroidJUnit4.class) @LargeTest diff --git a/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java b/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java similarity index 98% rename from androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java rename to src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java index 725f855553..c2277144b7 100644 --- a/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java +++ b/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java @@ -120,7 +120,7 @@ public class OCFileUnitTest { ); assertThat(fileReadFromParcel.getLastSyncDateForProperties(), is(LAST_SYNC_DATE_FOR_PROPERTIES)); assertThat(fileReadFromParcel.getLastSyncDateForData(), is(LAST_SYNC_DATE_FOR_DATA)); - assertThat(fileReadFromParcel.setAvailableOffline(), is(true)); + assertThat(fileReadFromParcel.isAvailableOffline(), is(true)); assertThat(fileReadFromParcel.getEtag(), is(ETAG)); assertThat(fileReadFromParcel.isSharedViaLink(), is(true)); assertThat(fileReadFromParcel.isSharedWithSharee(), is(true)); diff --git a/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java b/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java similarity index 86% rename from androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java rename to src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java index 0cf7304284..f59a5bc00f 100644 --- a/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java +++ b/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java @@ -10,7 +10,6 @@ import android.support.test.runner.AndroidJUnit4; import com.owncloud.android.db.OCUpload; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,14 +39,14 @@ public class UploadStorageManagerTest { public void testDeleteAllUploads() { //Clean for (Account account : Accounts) { - uploadsStorageManager.removeAccountUploads(account); +// uploadsStorageManager.removeAccountUploads(account); } int accountRowsA = 3; int accountRowsB = 4; insertUploads(Accounts[0], accountRowsA); insertUploads(Accounts[1], accountRowsB); - Assert.assertTrue("Expected 4 removed uploads files", uploadsStorageManager.removeAccountUploads(Accounts[1]) == 4); +// Assert.assertTrue("Expected 4 removed uploads files", uploadsStorageManager.removeAccountUploads(Accounts[1]) == 4); } private void insertUploads(Account account, int rowsToInsert) { @@ -66,7 +65,7 @@ public class UploadStorageManagerTest { @After public void tearDown() { for (Account account : Accounts) { - uploadsStorageManager.removeAccountUploads(account); +// uploadsStorageManager.removeAccountUploads(account); } } } diff --git a/androidTest/java/com/owncloud/android/uiautomator/InitialTest.java b/src/androidTest/java/com/owncloud/android/uiautomator/InitialTest.java similarity index 100% rename from androidTest/java/com/owncloud/android/uiautomator/InitialTest.java rename to src/androidTest/java/com/owncloud/android/uiautomator/InitialTest.java diff --git a/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java b/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java new file mode 100644 index 0000000000..5a89250672 --- /dev/null +++ b/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java @@ -0,0 +1,357 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2017 Tobias Kaminsky + * Copyright (C) 2017 Nextcloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.util; + +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.support.test.runner.AndroidJUnit4; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import com.owncloud.android.datamodel.DecryptedFolderMetadata; +import com.owncloud.android.datamodel.EncryptedFolderMetadata; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.utils.CsrHelper; +import com.owncloud.android.utils.EncryptionUtils; + +import org.apache.commons.io.FileUtils; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import static android.support.test.InstrumentationRegistry.getInstrumentation; +import static com.owncloud.android.utils.EncryptionUtils.decodeStringToBase64Bytes; +import static com.owncloud.android.utils.EncryptionUtils.encodeBytesToBase64String; +import static com.owncloud.android.utils.EncryptionUtils.generateIV; +import static com.owncloud.android.utils.EncryptionUtils.generateKey; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; + +@RequiresApi(api = Build.VERSION_CODES.KITKAT) +@RunWith(AndroidJUnit4.class) +public class EncryptionTestIT { + private static String TAG = EncryptionTestIT.class.getSimpleName(); + + private String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAo" + + "IBAQDsn0JKS/THu328z1IgN0VzYU53HjSX03WJIgWkmyTaxbiKpoJaKbksXmfSpgzV" + + "GzKFvGfZ03fwFrN7Q8P8R2e8SNiell7mh1TDw9/0P7Bt/ER8PJrXORo+GviKHxaLr7" + + "Y0BJX9i/nW/L0L/VaE8CZTAqYBdcSJGgHJjY4UMf892ZPTa9T2Dl3ggdMZ7BQ2kiCi" + + "CC3qV99b0igRJGmmLQaGiAflhFzuDQPMifUMq75wI8RSRPdxUAtjTfkl68QHu7Umye" + + "yy33OQgdUKaTl5zcS3VSQbNjveVCNM4RDH1RlEc+7Wf1BY8APqT6jbiBcROJD2CeoL" + + "H2eiIJCi+61ZkSGfAgMBAAECggEBALFStCHrhBf+GL9a+qer4/8QZ/X6i91PmaBX/7" + + "SYk2jjjWVSXRNmex+V6+Y/jBRT2mvAgm8J+7LPwFdatE+lz0aZrMRD2gCWYF6Itpda" + + "90OlLkmQPVWWtGTgX2ta2tF5r2iSGzk0IdoL8zw98Q2UzpOcw30KnWtFMxuxWk0mHq" + + "pgp00g80cDWg3+RPbWOhdLp5bflQ36fKDfmjq05cGlIk6unnVyC5HXpvh4d4k2EWlX" + + "rjGsndVBPCjGkZePlLRgDHxT06r+5XdJ+1CBDZgCsmjGz3M8uOHyCfVW0WhB7ynzDT" + + "agVgz0iqpuhAi9sPt6iWWwpAnRw8cQgqEKw9bvKKECgYEA/WPi2PJtL6u/xlysh/H7" + + "A717CId6fPHCMDace39ZNtzUzc0nT5BemlcF0wZ74NeJSur3Q395YzB+eBMLs5p8mA" + + "95wgGvJhM65/J+HX+k9kt6Z556zLMvtG+j1yo4D0VEwm3xahB4SUUP+1kD7dNvo4+8" + + "xeSCyjzNllvYZZC0DrECgYEA7w8pEqhHHn0a+twkPCZJS+gQTB9Rm+FBNGJqB3XpWs" + + "TeLUxYRbVGk0iDve+eeeZ41drxcdyWP+WcL34hnrjgI1Fo4mK88saajpwUIYMy6+qM" + + "LY+jC2NRSBox56eH7nsVYvQQK9eKqv9wbB+PF9SwOIvuETN7fd8mAY02UnoaaU8CgY" + + "BoHRKocXPLkpZJuuppMVQiRUi4SHJbxDo19Tp2w+y0TihiJ1lvp7I3WGpcOt3LlMQk" + + "tEbExSvrRZGxZKH6Og/XqwQsYuTEkEIz679F/5yYVosE6GkskrOXQAfh8Mb3/04xVV" + + "tMaVgDQw0+CWVD4wyL+BNofGwBDNqsXTCdCsfxAQKBgQCDv2EtbRw0y1HRKv21QIxo" + + "ju5cZW4+cDfVPN+eWPdQFOs1H7wOPsc0aGRiiupV2BSEF3O1ApKziEE5U1QH+29bR4" + + "R8L1pemeGX8qCNj5bCubKjcWOz5PpouDcEqimZ3q98p3E6GEHN15UHoaTkx0yO/V8o" + + "j6zhQ9fYRxDHB5ACtQKBgQCOO7TJUO1IaLTjcrwS4oCfJyRnAdz49L1AbVJkIBK0fh" + + "JLecOFu3ZlQl/RStQb69QKb5MNOIMmQhg8WOxZxHcpmIDbkDAm/J/ovJXFSoBdOr5o" + + "uQsYsDZhsWW97zvLMzg5pH9/3/1BNz5q3Vu4HgfBSwWGt4E2NENj+XA+QAVmGA=="; + + private String cert = "-----BEGIN CERTIFICATE-----\n" + + "MIIDpzCCAo+gAwIBAgIBADANBgkqhkiG9w0BAQUFADBuMRowGAYDVQQDDBF3d3cu\n" + + "bmV4dGNsb3VkLmNvbTESMBAGA1UECgwJTmV4dGNsb3VkMRIwEAYDVQQHDAlTdHV0\n" + + "dGdhcnQxGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzELMAkGA1UEBhMCREUw\n" + + "HhcNMTcwOTI2MTAwNDMwWhcNMzcwOTIxMTAwNDMwWjBuMRowGAYDVQQDDBF3d3cu\n" + + "bmV4dGNsb3VkLmNvbTESMBAGA1UECgwJTmV4dGNsb3VkMRIwEAYDVQQHDAlTdHV0\n" + + "dGdhcnQxGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzELMAkGA1UEBhMCREUw\n" + + "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsn0JKS/THu328z1IgN0Vz\n" + + "YU53HjSX03WJIgWkmyTaxbiKpoJaKbksXmfSpgzVGzKFvGfZ03fwFrN7Q8P8R2e8\n" + + "SNiell7mh1TDw9/0P7Bt/ER8PJrXORo+GviKHxaLr7Y0BJX9i/nW/L0L/VaE8CZT\n" + + "AqYBdcSJGgHJjY4UMf892ZPTa9T2Dl3ggdMZ7BQ2kiCiCC3qV99b0igRJGmmLQaG\n" + + "iAflhFzuDQPMifUMq75wI8RSRPdxUAtjTfkl68QHu7Umyeyy33OQgdUKaTl5zcS3\n" + + "VSQbNjveVCNM4RDH1RlEc+7Wf1BY8APqT6jbiBcROJD2CeoLH2eiIJCi+61ZkSGf\n" + + "AgMBAAGjUDBOMB0GA1UdDgQWBBTFrXz2tk1HivD9rQ75qeoyHrAgIjAfBgNVHSME\n" + + "GDAWgBTFrXz2tk1HivD9rQ75qeoyHrAgIjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3\n" + + "DQEBBQUAA4IBAQARQTX21QKO77gAzBszFJ6xVnjfa23YZF26Z4X1KaM8uV8TGzuN\n" + + "JA95XmReeP2iO3r8EWXS9djVCD64m2xx6FOsrUI8HZaw1JErU8mmOaLAe8q9RsOm\n" + + "9Eq37e4vFp2YUEInYUqs87ByUcA4/8g3lEYeIUnRsRsWsA45S3wD7wy07t+KAn7j\n" + + "yMmfxdma6hFfG9iN/egN6QXUAyIPXvUvlUuZ7/BhWBj/3sHMrF9quy9Q2DOI8F3t\n" + + "1wdQrkq4BtStKhciY5AIXz9SqsctFHTv4Lwgtkapoel4izJnO0ZqYTXVe7THwri9\n" + + "H/gua6uJDWH9jk2/CiZDWfsyFuNUuXvDSp05\n" + + "-----END CERTIFICATE-----"; + + @Test + public void encryptStringAsymmetric() throws Exception { + byte[] key1 = EncryptionUtils.generateKey(); + String base64encodedKey = encodeBytesToBase64String(key1); + + String encryptedString = EncryptionUtils.encryptStringAsymmetric(base64encodedKey, cert); + String decryptedString = EncryptionUtils.decryptStringAsymmetric(encryptedString, privateKey); + + byte[] key2 = EncryptionUtils.decodeStringToBase64Bytes(decryptedString); + + assertTrue(Arrays.equals(key1, key2)); + } + + @Test + public void encryptStringSymmetric() throws Exception { + byte[] key = generateKey(); + + String encryptedString = EncryptionUtils.encryptStringSymmetric(privateKey, key); + String decryptedString = EncryptionUtils.decryptStringSymmetric(encryptedString, key); + + assertEquals(privateKey, decryptedString); + } + + @Test + public void encryptPrivateKey() throws Exception { + String keyPhrase = "moreovertelevisionfactorytendencyindependenceinternationalintellectualimpress" + + "interestvolunteer"; + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(4096, new SecureRandom()); + KeyPair keyPair = keyGen.generateKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + byte[] privateKeyBytes = privateKey.getEncoded(); + String privateKeyString = encodeBytesToBase64String(privateKeyBytes); + + String encryptedString = EncryptionUtils.encryptPrivateKey(privateKeyString, keyPhrase); + String decryptedString = EncryptionUtils.decryptPrivateKey(encryptedString, keyPhrase); + + assertEquals(privateKeyString, decryptedString); + } + + @Test + public void generateCSR() throws Exception { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048, new SecureRandom()); + KeyPair keyPair = keyGen.generateKeyPair(); + + String string = CsrHelper.generateCsrPemEncodedString(keyPair); + String urlEncoded = URLEncoder.encode("-----BEGIN CERTIFICATE REQUEST-----\n" + string + + "\n-----END CERTIFICATE REQUEST-----", "UTF-8"); + + Log_OC.d(TAG, "public: " + encodeBytesToBase64String(keyPair.getPublic().getEncoded())); + Log_OC.d(TAG, "csrPEM: " + string); + Log_OC.d(TAG, "csrPEM: " + urlEncoded); + } + + /** + * DecryptedFolderMetadata -> EncryptedFolderMetadata -> JSON -> encrypt + * -> decrypt -> JSON -> EncryptedFolderMetadata -> DecryptedFolderMetadata + */ + @Test + public void encryptionMetadata() throws Exception { + DecryptedFolderMetadata decryptedFolderMetadata1 = generateFolderMetadata(); + + // encrypt + EncryptedFolderMetadata encryptedFolderMetadata1 = EncryptionUtils.encryptFolderMetadata( + decryptedFolderMetadata1, privateKey); + + // serialize + String encryptedJson = EncryptionUtils.serializeJSON(encryptedFolderMetadata1); + + // de-serialize + EncryptedFolderMetadata encryptedFolderMetadata2 = EncryptionUtils.deserializeJSON(encryptedJson, + new TypeToken() { + }); + + // decrypt + DecryptedFolderMetadata decryptedFolderMetadata2 = EncryptionUtils.decryptFolderMetaData( + encryptedFolderMetadata2, privateKey); + + // compare + assertTrue(compareJsonStrings(EncryptionUtils.serializeJSON(decryptedFolderMetadata1), + EncryptionUtils.serializeJSON(decryptedFolderMetadata2))); + } + + @Test + public void testCryptFileWithoutMetadata() throws Exception { + byte[] key = EncryptionUtils.decodeStringToBase64Bytes("WANM0gRv+DhaexIsI0T3Lg=="); + byte[] iv = EncryptionUtils.decodeStringToBase64Bytes("gKm3n+mJzeY26q4OfuZEqg=="); + byte[] authTag = EncryptionUtils.decodeStringToBase64Bytes("PboI9tqHHX3QeAA22PIu4w=="); + + cryptFile("ia7OEEEyXMoRa1QWQk8r", "78f42172166f9dc8fd1a7156b1753353", key, iv, authTag); + } + + @Test + public void cryptFileWithMetadata() throws Exception { + DecryptedFolderMetadata metadata = generateFolderMetadata(); + + // n9WXAIXO2wRY4R8nXwmo + cryptFile("ia7OEEEyXMoRa1QWQk8r", + "78f42172166f9dc8fd1a7156b1753353", + EncryptionUtils.decodeStringToBase64Bytes(metadata.files.get("ia7OEEEyXMoRa1QWQk8r").encrypted.key), + EncryptionUtils.decodeStringToBase64Bytes(metadata.files.get("ia7OEEEyXMoRa1QWQk8r").initializationVector), + EncryptionUtils.decodeStringToBase64Bytes(metadata.files.get("ia7OEEEyXMoRa1QWQk8r").authenticationTag)); + + // n9WXAIXO2wRY4R8nXwmo + cryptFile("n9WXAIXO2wRY4R8nXwmo", + "825143ed1f21ebb0c3b3c3f005b2f5db", + EncryptionUtils.decodeStringToBase64Bytes(metadata.files.get("n9WXAIXO2wRY4R8nXwmo").encrypted.key), + EncryptionUtils.decodeStringToBase64Bytes(metadata.files.get("n9WXAIXO2wRY4R8nXwmo").initializationVector), + EncryptionUtils.decodeStringToBase64Bytes(metadata.files.get("n9WXAIXO2wRY4R8nXwmo").authenticationTag)); + } + + /** + * generates new keys and tests if they are unique + */ + @Test + public void testKey() { + Set keys = new HashSet<>(); + + for (int i = 0; i < 50; i++) { + assertTrue(keys.add(encodeBytesToBase64String(generateKey()))); + } + } + + /** + * generates new ivs and tests if they are unique + */ + @Test + public void testIV() { + Set ivs = new HashSet<>(); + + for (int i = 0; i < 50; i++) { + assertTrue(ivs.add(encodeBytesToBase64String(generateIV()))); + } + } + + + // Helper + private boolean compareJsonStrings(String expected, String actual) { + JsonParser parser = new JsonParser(); + JsonElement o1 = parser.parse(expected); + JsonElement o2 = parser.parse(actual); + + if (o1.equals(o2)) { + return true; + } else { + System.out.println("expected: " + o1); + System.out.println("actual: " + o2); + return false; + } + } + + private DecryptedFolderMetadata generateFolderMetadata() throws Exception { + String metadataKey0 = encodeBytesToBase64String(EncryptionUtils.generateKey()); + String metadataKey1 = encodeBytesToBase64String(EncryptionUtils.generateKey()); + String metadataKey2 = encodeBytesToBase64String(EncryptionUtils.generateKey()); + HashMap metadataKeys = new HashMap<>(); + metadataKeys.put(0, EncryptionUtils.encryptStringAsymmetric(metadataKey0, cert)); + metadataKeys.put(1, EncryptionUtils.encryptStringAsymmetric(metadataKey1, cert)); + metadataKeys.put(2, EncryptionUtils.encryptStringAsymmetric(metadataKey2, cert)); + DecryptedFolderMetadata.Encrypted encrypted = new DecryptedFolderMetadata.Encrypted(); + encrypted.metadataKeys = metadataKeys; + + DecryptedFolderMetadata.Metadata metadata1 = new DecryptedFolderMetadata.Metadata(); + metadata1.metadataKeys = metadataKeys; + metadata1.version = 1; + + DecryptedFolderMetadata.Sharing sharing = new DecryptedFolderMetadata.Sharing(); + sharing.signature = "HMACOFRECIPIENTANDNEWESTMETADATAKEY"; + HashMap recipient = new HashMap<>(); + recipient.put("blah@schiessle.org", "PUBLIC KEY"); + recipient.put("bjoern@schiessle.org", "PUBLIC KEY"); + sharing.recipient = recipient; + metadata1.sharing = sharing; + + HashMap files = new HashMap<>(); + + DecryptedFolderMetadata.Data data1 = new DecryptedFolderMetadata.Data(); + data1.key = "WANM0gRv+DhaexIsI0T3Lg=="; + data1.filename = "test.txt"; + data1.version = 1; + + DecryptedFolderMetadata.DecryptedFile file1 = new DecryptedFolderMetadata.DecryptedFile(); + file1.initializationVector = "gKm3n+mJzeY26q4OfuZEqg=="; + file1.encrypted = data1; + file1.metadataKey = 0; + file1.authenticationTag = "PboI9tqHHX3QeAA22PIu4w=="; + + files.put("ia7OEEEyXMoRa1QWQk8r", file1); + + DecryptedFolderMetadata.Data data2 = new DecryptedFolderMetadata.Data(); + data2.key = "9dfzbIYDt28zTyZfbcll+g=="; + data2.filename = "test2.txt"; + data2.version = 1; + + DecryptedFolderMetadata.DecryptedFile file2 = new DecryptedFolderMetadata.DecryptedFile(); + file2.initializationVector = "hnJLF8uhDvDoFK4ajuvwrg=="; + file2.encrypted = data2; + file2.metadataKey = 0; + file2.authenticationTag = "qOQZdu5soFO77Y7y4rAOVA=="; + + files.put("n9WXAIXO2wRY4R8nXwmo", file2); + + return new DecryptedFolderMetadata(metadata1, files); + } + + private void cryptFile(String fileName, String md5, byte[] key, byte[] iv, byte[] expectedAuthTag) + throws Exception { + File file = getFile(fileName); + assertEquals(md5, EncryptionUtils.getMD5Sum(file)); + + EncryptionUtils.EncryptedFile encryptedFile = EncryptionUtils.encryptFile(file, key, iv); + + File encryptedTempFile = File.createTempFile("file", "tmp"); + FileOutputStream fileOutputStream = new FileOutputStream(encryptedTempFile); + fileOutputStream.write(encryptedFile.encryptedBytes); + fileOutputStream.close(); + + byte[] authenticationTag = decodeStringToBase64Bytes(encryptedFile.authenticationTag); + + // verify authentication tag + assertTrue(Arrays.equals(expectedAuthTag, authenticationTag)); + + byte[] decryptedBytes = EncryptionUtils.decryptFile(encryptedTempFile, key, iv, authenticationTag); + + File decryptedFile = File.createTempFile("file", "dec"); + FileOutputStream fileOutputStream1 = new FileOutputStream(decryptedFile); + fileOutputStream1.write(decryptedBytes); + fileOutputStream1.close(); + + assertEquals(md5, EncryptionUtils.getMD5Sum(decryptedFile)); + } + + private File getFile(String filename) throws IOException { + InputStream inputStream = getInstrumentation().getContext().getAssets().open(filename); + File temp = File.createTempFile("file", "file"); + FileUtils.copyInputStreamToFile(inputStream, temp); + + return temp; + } +} \ No newline at end of file diff --git a/src/gplay/AndroidManifest.xml b/src/gplay/AndroidManifest.xml index b32ab3b432..f24dfb4af7 100644 --- a/src/gplay/AndroidManifest.xml +++ b/src/gplay/AndroidManifest.xml @@ -18,7 +18,9 @@ --> + package="com.owncloud.android" + android:versionCode="20000052" + android:versionName="2.0.0-e2e-02"> . --> + package="com.owncloud.android" + android:versionCode="20000052" + android:versionName="2.0.0-e2e-02"> + + + + + + \ No newline at end of file diff --git a/src/main/res/layout/setup_encryption_dialog.xml b/src/main/res/layout/setup_encryption_dialog.xml new file mode 100644 index 0000000000..9c095ea7dd --- /dev/null +++ b/src/main/res/layout/setup_encryption_dialog.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + diff --git a/src/main/res/menu/file_actions_menu.xml b/src/main/res/menu/file_actions_menu.xml index 719759655f..3deca1bc27 100644 --- a/src/main/res/menu/file_actions_menu.xml +++ b/src/main/res/menu/file_actions_menu.xml @@ -107,6 +107,18 @@ app:showAsAction="never" android:showAsAction="never" android:orderInCategory="1" /> + + Mit verschiedenen Kontos verbinden Automatisches Hochladen von Bildern & Videos Kalender & Kontakte mit DAVdroid synchronisieren - - +Keine Information verfügbar! + Keine neue Version verfügbar! + + Verzeichnis nicht leer! + Fehler beim entschlüsseln. Falsches Passwort?? + Entschlüsseln… + Hole Schlüssel… + Zum entschlüsseln des privaten Schlüssels bitte Passwort eingeben! + Erstelle neue Schlüssel… + Dieser 12 Worte Satz ist wie ein starkes Passwort: Es emöglicht Zugang zu den verschlüsselten Dateien. Bitte notieren Sie sich den Satz und bewahren ihn sicher auf. + Verschlüsselung einrichten + Notieren Sie sich den Verschlüsselungssatz. + Verschlüsselung erst ab KitKat unterstützt. + Verschlüsselung einrichten + Passwort… + diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index fc56de5202..2179aebbf4 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -38,6 +38,7 @@ #757575 #555555 + #EEEEEE diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index dacf302e33..9be8b9be17 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -268,6 +268,8 @@ Set as favorite Unset favorite Available offline + Set as encrypted + Unset encryption Rename Delete Do you really want to delete %1$s? @@ -753,4 +755,17 @@ Add name, picture and contact details on your profile page. Background image of drawer header Account icon + + Folder not empty! + Error while decrypting. Wrong password? + Decrypting… + Retrieving keys… + Please enter password to decrypt private key! + Generating new keys… + This 12 word phrase is like a very strong password: It provides full access to view and use your encrypted files. Please write it down and keep it somewhere safe. + Set up encryption + Note your encryption passphrase + Encryption not supported before KitKat. + Set up encryption + Password… diff --git a/src/modified/AndroidManifest.xml b/src/modified/AndroidManifest.xml index 77b4e52b6d..6a7ae040e1 100644 --- a/src/modified/AndroidManifest.xml +++ b/src/modified/AndroidManifest.xml @@ -18,7 +18,9 @@ --> + package="com.owncloud.android" + android:versionCode="20000052" + android:versionName="2.0.0-e2e-02"> . + */ + +package com.owncloud.android.utils; + +import android.os.Build; +import android.support.annotation.RequiresApi; + +import com.google.gson.reflect.TypeToken; +import com.owncloud.android.datamodel.DecryptedFolderMetadata; + +import org.junit.Test; + +import java.util.HashMap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@RequiresApi(api = Build.VERSION_CODES.KITKAT) +public class EncryptionTest { + private String encryptedString = "{\"metadata\":{\"encrypted\":\"np1sIwoAFCb/vRqV/jWOIe1UtyVO02EJhvPoh3VwcZuiSbDjwQO5QHUWtKXpHLyX6wfbkRX6nr8mSG0+HhLRud1t126UMxQK5BNINu99WlzPMa5PaKhTXlpuRUf3tR6PTQ\\u003d\\u003d\",\"initializationVector\":" + + "\"kahzfT4u86Knc+e3\",\"sharing\":{\"recipient\":{\"blah@schiessle.org\":\"PUBLIC KEY\"," + + "\"bjoern@schiessle.org\":\"PUBLIC KEY\"},\"signature\":\"HMACOFRECIPIENTANDNEWESTMETADATAKEY\"}," + + "\"version\":1},\"files\":{\"ia7OEEEyXMoRa1QWQk8r\":{\"encrypted\":\"yl52TIccvo62LezCaFjQFJs7a1Q281pOuj59oNXMX7ti+7+h1SjK1AAk1HuwT+CI7BT64+R0ZLgyR/vBPjWvAQuxi9JWgsCjFMX91Mv2m2zI/bNQCarczOfnmf4FZ3Nv6yPLSjShmfQzemQ99Z3g7UHyrZ6pKT18m17IueJHF3V5kOhd9vcH\",\"metadataKey\":0," + + "\"initializationVector\":\"+mHu52HyZq+pAAIN\"}," + + "\"n9WXAIXO2wRY4R8nXwmo\":{\"encrypted\":\"Z9YTAgY/0YqKQlDwiqENcZRRupjgmJ1f0bTy0hOHP2/mHxFtoHCftT4STvt21OJMx8wF6V3cquQIGJ976WxkhA4SQxaQNpznhm1W9e8y+B5x8QnxSasYnOSFtZ/xVgQq6IZRjvYdPo7rvZk49hnqkwnUyvqtCj14aCE42qoxVZCd9M6XaZEBTA\\u003d\\u003d\",\"metadataKey\":0,\"initializationVector\":" + + "\"sOFd17hCKWIv0gyB\"}}}"; + + private String decryptedString = "{\"metadata\":{\"encrypted\":{\"metadataKeys\":{\"0\":" + + "\"s4k4LPDpxoO53TKwem3Lo1\",\"2\":\"…\",\"3\":\"NEWESTMETADATAKEY\"}},\"initializationVector\":" + + "\"kahzfT4u86Knc+e3\",\"sharing\":{\"recipient\":{\"blah@schiessle.org\":\"PUBLIC KEY\"," + + "\"bjoern@schiessle.org\":\"PUBLIC KEY\"},\"signature\":\"HMACOFRECIPIENTANDNEWESTMETADATAKEY\"}," + + "\"version\":1},\"files\":{\"ia7OEEEyXMoRa1QWQk8r\":{\"encrypted\":{\"key\":" + + "\"jtboLmgGR1OQf2uneqCVHpklQLlIwWL5TXAQ0keK\",\"filename\":\"test.txt\",\"authenticationTag\":" + + "\"HMAC of file\",\"version\":1},\"metadataKey\":0,\"initializationVector\":\"+mHu52HyZq+pAAIN\"}," + + "\"n9WXAIXO2wRY4R8nXwmo\":{\"encrypted\":{\"key\":\"s4k4LPDpxoO53TKwem3Lo1yJnbNUYH2KLrSFT8Ea\"," + + "\"filename\":\"test2.txt\",\"authenticationTag\":\"HMAC of file\",\"version\":1}," + + "\"metadataKey\":0,\"initializationVector\":\"sOFd17hCKWIv0gyB\"}}}"; + + private String privateKey = "TUlJRXZRSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS2N3Z2dTakFnRUFBb0lCQVFEV0FKMDNuNlBLVDNJWlZTT1paSzgyd3dqRTVWSW4vclZ5L0VLbmsyMVgzQ1dPbkdsb0ZrUXdiSWFsYjlkVWNRcmQzM250NVFoMmhJeXM5TkdsMjdMUmhreU1qemYzaFdzbnljSVhZQURVSExweGJZbWpJRzRjbWlNQnI4ckhjVFEvM0dUTlBKWG00ZzUwQlZEOXZlRThoMkQrTXdIQXlTSkxZcG5FaDBKWExES0pvVnpQYldxdVZVdHZNMmdmWVFHNFgwbS9kamR5VzJ4ZGN2UkpZeUtpWDZ3UmY3U3ViaWJkR1V1MHgxckhRdGhnM1dtaXdLd09NR2pQQ3oxRDAwMC9vWjZnckRHcW03TG5XMVZaN0VNTWtWWjVjVlc4NlpGQ2lrcXNtcDd1MVg1YjdCTzBiaThyV2Z0Zm8zZnJaOUptdmkvZHpmeXo2RFRFOWIrZU9CNzlBZ01CQUFFQ2dnRUJBSURYYzlCR1k5VnRDWFBwQjNyVjNJdXExcis4bFQ4UklldHRweSsvR2dqWXVSL29XYW5hSmduRmZUZGpZNUFxVXZHTUY1dTcxZUdOSWlrTGFLRmo2WUF1VEM0Z0dBRUZLYU9WM0M0NGxhY2UrTDFMeHA4WTZsSjhGbkZ3aGpTWG1tNk1ZWUFUWnVqUDF3WFJJWmJ6V0FVYU9MSXl3VzV4YWgxYTZ0c2cyRGNrZ2h3YVoxMURiRTVkNEpNNXl0Q1J6YmZzWTA3cElrUmU4WlFLcm1sMXU0THlsWXVHVHBDZGMwSnNXdUx3d1NWbm8vK056RURiVVNKZThIRmpnM1k3L2JkajgzNE9INzBtQWdBVUdEQTJqQ1BqY1hzNHJnN25udkhuQm5WcVFaemFvVWhHYzFOak9pZzMwNitUV25OL1JUNWc0TThucDNuMXZETWF2dVBiM0lFQ2dZRUE2NVpQV243UlYxZVVVS20zcVRUN1BSa3NuLzZDVWdFaFQ1WDloc0c3cXJXMUVlalhuODE2S1A0R3VsaDc1S0dhQ3hUVmdGZDhHdHF1QVU3MGdaKzB5MHVGd2hTdGtWN1A0K2xlUXlYSmxtNDNWNU9zT3Jxb25ocTZyOCthZ3lGbnNuOEhGd3lVcG1VM0FqR2tXVUNzYWFNdWM4SEt0ajU4RkdGRlh6K3ZlcDBDZ1lFQTZJdUZVZmljLytpUXpsVjczTDdWSS9kRjJNcFRPeHVKb1c4MmM1OE1tdEhGMFFQSktlYU5YVGJuYzluUXZ0aFBWM3c1eEZWc3FkN3pKOEM4ZkJ5S21Xc2ZLOU1abGQvY3RZTk9EMTNLWlM2WTNvWXpGVU5ZRzFPSEhpamNZT3RSWUsvY3k0cS9SRnExQ0pZVCtPaEVTOXlmeGFqZlZXdzFPdGxNZGNuNlYrRUNnWUFwNlN5bTBjYldQZnRodWorMU4zcTJyT0xXZDhXaFp4Z1ErNE1GMVROWXRFakpMZDRtVEx5OXpDdFFQV3VWQ2ZiSW4rVTNsdGk2UWtzUWFvWnZCUVY1NFM2amoyQXRhMnVhaFNyQzBWY2lqdXNEaG43dVY4U2xrK1hBWHpPQ3ZvK2ZIcUFaUnFDdlZYUkt6S0FMVE1rZlpldGVwb3cwamJzdk9QckpiaC8rdFFLQmdIQUFpd1R4RmtVWGNXOC9zdm1lSERCSGI2ZTd3eHlyNWIwUVFJeXRwVGVJSTV2SkZBR1BYclR2dGNpUnR6M0VGMnJPbFZBZnlNZUViMTdOTUxzaVVBc1draHZjZiswMHRpdmlneDFaa2hycnQ0c3QzYnEzQmQrYmVtK25SSVdWc1VzOVNMM3NKTFU2Yndra3A1ZngzcnNmRndEdmxpbWhoWDNEblZUNkpBNWhBb0dBZUtSZGRBVFAxZmVDL1pIZWE0VWh1MEVIencrY2xraDBrdjFDQkNHNnhjM002Y245SzFUNlIwMHRZYlFjS1JvZHJ1ZnBZNUNOL3V2Zyt0VWRucTUxRFhwTU5tbDVMdGpRYnV3MktuZ1F1ZG9NQ1NFMHMzT0dnaGJqMzZtbEVsRERjUGxLaE0rb1hyaVRYdXUvWmljclZqcGxTcWxuSlN2aDh6STJGNFBLaDlnPQ=="; + + private String publicKey = "-----BEGIN CERTIFICATE-----\n" + + "MIIDpzCCAo+gAwIBAgIBADANBgkqhkiG9w0BAQUFADBuMRowGAYDVQQDDBF3d3cu\n" + + "bmV4dGNsb3VkLmNvbTESMBAGA1UECgwJTmV4dGNsb3VkMRIwEAYDVQQHDAlTdHV0\n" + + "dGdhcnQxGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzELMAkGA1UEBhMCREUw\n" + + "HhcNMTcwOTA0MTEwNTQyWhcNMzcwODMwMTEwNTQyWjBuMRowGAYDVQQDDBF3d3cu\n" + + "bmV4dGNsb3VkLmNvbTESMBAGA1UECgwJTmV4dGNsb3VkMRIwEAYDVQQHDAlTdHV0\n" + + "dGdhcnQxGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzELMAkGA1UEBhMCREUw\n" + + "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWAJ03n6PKT3IZVSOZZK82\n" + + "wwjE5VIn/rVy/EKnk21X3CWOnGloFkQwbIalb9dUcQrd33nt5Qh2hIys9NGl27LR\n" + + "hkyMjzf3hWsnycIXYADUHLpxbYmjIG4cmiMBr8rHcTQ/3GTNPJXm4g50BVD9veE8\n" + + "h2D+MwHAySJLYpnEh0JXLDKJoVzPbWquVUtvM2gfYQG4X0m/djdyW2xdcvRJYyKi\n" + + "X6wRf7SubibdGUu0x1rHQthg3WmiwKwOMGjPCz1D000/oZ6grDGqm7LnW1VZ7EMM\n" + + "kVZ5cVW86ZFCikqsmp7u1X5b7BO0bi8rWftfo3frZ9Jmvi/dzfyz6DTE9b+eOB79\n" + + "AgMBAAGjUDBOMB0GA1UdDgQWBBS3zNF86LEZFT/KDdscr4ZJEisXqDAfBgNVHSME\n" + + "GDAWgBS3zNF86LEZFT/KDdscr4ZJEisXqDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3\n" + + "DQEBBQUAA4IBAQCPMu5AKNlh0gTr/k9Vc7RJ01uF07D+lTPGIErfW7+qXO21kXyw\n" + + "+w8sxw+e1B/gah/bxotfO7ZuOhs49d8QRUzPy/miBFaZXfjfiqs7UXSDQ6oUbX3a\n" + + "X9eTFMHDcsSUbyqhwn2cghmPJEhE10mtH2DJNPqDYvdpekJ6sEUVaqx63CD3nxcl\n" + + "7fXh0IfmvDQOrSBszRqPY8pvnZJIEwqaENPk9Vgbzs8oXVstKl6wCqM0B36tmhBl\n" + + "f6Dz/EhriF3Rq9w5RrWZOpS6XAWwRpyHPN+lKPa321dF6EEsnvhX8G3UbLbr0uEg\n" + + "dR8lPhuKejU/Ds0ARwQGmFXFzidFNZL5ymos\n" + + "-----END CERTIFICATE-----"; + + @Test + public void deserializeJSON() { + String file = "ia7OEEEyXMoRa1QWQk8r"; + DecryptedFolderMetadata metadata = EncryptionUtils.deserializeJSON(decryptedString, + new TypeToken() {}); + + assertEquals("jtboLmgGR1OQf2uneqCVHpklQLlIwWL5TXAQ0keK", metadata.files.get(file).encrypted.key); + assertEquals("+mHu52HyZq+pAAIN", metadata.files.get(file).initializationVector); + } + + @Test + public void serializeJSON() { + try { + HashMap metadataKeys = new HashMap<>(); + metadataKeys.put(0, EncryptionUtils.encryptStringAsymmetric("s4k4LPDpxoO53TKwem3Lo1", publicKey)); + metadataKeys.put(1, EncryptionUtils.encryptStringAsymmetric("Q3ZobVJHbTlkK1VHT0g3ME", publicKey)); + metadataKeys.put(2, EncryptionUtils.encryptStringAsymmetric("lkK1VHT0g3ME3TKwem3Lo1", publicKey)); + DecryptedFolderMetadata.Encrypted encrypted = new DecryptedFolderMetadata.Encrypted(); + encrypted.metadataKeys = metadataKeys; + + DecryptedFolderMetadata.Metadata metadata1 = new DecryptedFolderMetadata.Metadata(); + metadata1.metadataKeys = metadataKeys; + metadata1.version = 1; + + DecryptedFolderMetadata.Sharing sharing = new DecryptedFolderMetadata.Sharing(); + sharing.signature = "HMACOFRECIPIENTANDNEWESTMETADATAKEY"; + HashMap recipient = new HashMap<>(); + recipient.put("blah@schiessle.org", "PUBLIC KEY"); + recipient.put("bjoern@schiessle.org", "PUBLIC KEY"); + sharing.recipient = recipient; + metadata1.sharing = sharing; + + HashMap files = new HashMap<>(); + + DecryptedFolderMetadata.Data data1 = new DecryptedFolderMetadata.Data(); + data1.key = "jtboLmgGR1OQf2uneqCVHpklQLlIwWL5TXAQ0keK"; + data1.filename = "test.txt"; + data1.version = 1; + + DecryptedFolderMetadata.DecryptedFile file1 = new DecryptedFolderMetadata.DecryptedFile(); + file1.initializationVector = "+mHu52HyZq+pAAIN"; + file1.encrypted = data1; + file1.metadataKey = 0; + file1.authenticationTag = "HMAC of file"; + + files.put("ia7OEEEyXMoRa1QWQk8r", file1); + + DecryptedFolderMetadata.Data data2 = new DecryptedFolderMetadata.Data(); + data2.key = "s4k4LPDpxoO53TKwem3Lo1yJnbNUYH2KLrSFT8Ea"; + data2.filename = "test2.txt"; + data2.version = 1; + + DecryptedFolderMetadata.DecryptedFile file2 = new DecryptedFolderMetadata.DecryptedFile(); + file2.initializationVector = "sOFd17hCKWIv0gyB"; + file2.encrypted = data2; + file2.metadataKey = 0; + file2.authenticationTag = "HMAC of file"; + + files.put("n9WXAIXO2wRY4R8nXwmo", file2); + + DecryptedFolderMetadata metadata = new DecryptedFolderMetadata(metadata1, files); + + // serialize + assertEquals(decryptedString, EncryptionUtils.serializeJSON(metadata)); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/owncloud/android/utils/ErrorMessageAdapterUnitTest.java b/src/test/java/com/owncloud/android/utils/ErrorMessageAdapterUnitTest.java index 9c232f3ea9..b00489dab5 100644 --- a/src/test/java/com/owncloud/android/utils/ErrorMessageAdapterUnitTest.java +++ b/src/test/java/com/owncloud/android/utils/ErrorMessageAdapterUnitTest.java @@ -21,8 +21,10 @@ package com.owncloud.android.utils; +import android.accounts.Account; import android.content.res.Resources; +import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.operations.RemoveFileOperation; @@ -65,10 +67,12 @@ public class ErrorMessageAdapterUnitTest { when(mMockResources.getString(R.string.forbidden_permissions_delete)) .thenReturn(MOCK_TO_DELETE); + Account account = new Account("name", MainApp.getAccountType()); + // ... when method under test is called ... String errorMessage = ErrorMessageAdapter.getErrorCauseMessage( new RemoteOperationResult(RemoteOperationResult.ResultCode.FORBIDDEN), - new RemoveFileOperation(PATH_TO_DELETE, false), + new RemoveFileOperation(PATH_TO_DELETE, false, account, MainApp.getAppContext()), mMockResources );