From a325caa56587ab89b0610cd7c3596d9f039a341e Mon Sep 17 00:00:00 2001 From: BuddahLD Date: Thu, 26 Sep 2019 18:47:55 +0300 Subject: [PATCH 01/13] Added group chats --- build.gradle | 86 +- ethree-common/build.gradle | 68 +- ethree-common/proguard-rules.txt | 29 + .../1.json | 69 ++ ethree-common/src/main/AndroidManifest.xml | 38 + .../android/common/EThreeCore.kt | 771 ++++++++++++++++++ ...eteListener.kt => OnKeyChangedCallback.kt} | 17 +- .../android/common/exception/Exceptions.kt | 154 ++++ .../android/common/exceptions/Exceptions.kt | 90 -- .../android/common/interaction/EThreeCore.kt | 655 --------------- .../common/interaction/KeyManagerCloud.kt | 106 --- .../android/common/manager/GroupManager.kt | 127 +++ .../android/common/manager/LookupManager.kt | 163 ++++ .../android/common/model/Completable.kt | 74 -- .../FindUsersResult.kt} | 25 +- .../android/common/model/Group.kt | 318 ++++++++ .../android/common/model/GroupInfo.kt | 32 +- .../android/common/model/LookupResult.kt | 4 + .../KeyManagerLocal.kt => model/RawGroup.kt} | 21 +- .../android/common/model/Result.kt | 73 -- .../android/common/model/Ticket.kt | 129 +++ .../android/common/storage/CardStorage.kt | 52 ++ .../common/storage/cloud/CloudKeyManager.kt | 141 ++++ .../storage/cloud/CloudTicketStorage.kt | 217 +++++ .../common/storage/local/GroupStorageFile.kt | 183 +++++ .../common/storage/local/KeyStorageLocal.kt | 40 +- .../common/storage/sql/ETheeDatabase.kt | 44 + .../common/storage/sql/SQLCardStorage.kt | 149 ++++ .../android/common/storage/sql/dao/CardDao.kt | 62 ++ .../common/storage/sql/model/CardEntity.kt | 50 ++ .../android/common/{ => util}/Const.kt | 2 +- .../common/worker/AuthorizationWorker.kt | 129 +++ .../android/common/worker/BackupWorker.kt | 163 ++++ .../android/common/worker/GroupWorker.kt | 178 ++++ .../android/common/worker/PeerToPeerWorker.kt | 443 ++++++++++ .../android/common/worker/SearchWorker.kt | 156 ++++ ethree-enclave/build.gradle | 12 +- ethree-enclave/src/main/AndroidManifest.xml | 3 +- .../ethreeenclave/interaction/EThree.kt | 71 +- ethree-kotlin/build.gradle | 22 +- .../android/ethree/interaction/EThree.kt | 66 +- gradle/wrapper/gradle-wrapper.properties | 37 +- tests/.gitignore | 3 +- tests/build.gradle | 52 +- tests/proguard-rules.pro | 1 + .../android/ethree/common/model/TicketTest.kt | 125 +++ .../common/storage/sql/SQLCardStorageTest.kt | 240 ++++++ .../interaction/async/EThreeAuthTest.kt | 30 +- .../interaction/async/EThreeBackupTest.kt | 42 +- .../interaction/async/EThreeEncryptionTest.kt | 88 +- .../interaction/async/EThreeNegativeTest.kt | 70 +- .../interaction/async/EThreeScopesTest.kt | 17 +- .../interaction/profile/EThreeProfile.kt | 2 +- .../interaction/sync/EThreeSyncNegative.kt | 55 +- .../interaction/sync/EThreeSyncPositive.kt | 24 +- .../java/interaction/EThreeScopesTest.java | 6 +- .../java/interaction/EThreeTestPositive.java | 6 +- .../android/ethree/utils/TestConfig.kt | 7 +- .../android/ethree/utils/TestUtils.kt | 59 ++ .../ethree/worker/AuthenticationTests.kt | 218 +++++ .../android/ethree/worker/BackupTests.kt | 297 +++++++ .../android/ethree/worker/GroupTests.kt | 666 +++++++++++++++ .../android/ethree/worker/PeerToPeerTest.kt | 286 +++++++ .../android/ethree/worker/SearchTests.kt | 182 +++++ tests/src/main/AndroidManifest.xml | 4 +- tests/src/main/assets/databases/cards.json | 17 + tests/src/main/assets/databases/cards.sqlite3 | Bin 0 -> 20480 bytes testsenclave/build.gradle | 19 +- .../android/testsenclave/EThreeEnclaveTest.kt | 2 +- .../android/testsenclave/utils/TestConfig.kt | 6 +- 70 files changed, 6266 insertions(+), 1527 deletions(-) create mode 100644 ethree-common/proguard-rules.txt create mode 100644 ethree-common/schemas/com.virgilsecurity.android.common.storage.sql.ETheeDatabase/1.json create mode 100644 ethree-common/src/main/AndroidManifest.xml create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt rename ethree-common/src/main/java/com/virgilsecurity/android/common/callback/{OnCompleteListener.kt => OnKeyChangedCallback.kt} (81%) create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/exception/Exceptions.kt delete mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/exceptions/Exceptions.kt delete mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/EThreeCore.kt delete mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/KeyManagerCloud.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt delete mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/model/Completable.kt rename ethree-common/src/main/java/com/virgilsecurity/android/common/{callback/OnResultListener.kt => model/FindUsersResult.kt} (75%) create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt rename ethree-enclave/src/main/java/com/virgilsecurity/android/ethreeenclave/interaction/KeyManagerLocalEnclave.kt => ethree-common/src/main/java/com/virgilsecurity/android/common/model/GroupInfo.kt (65%) rename ethree-common/src/main/java/com/virgilsecurity/android/common/{interaction/KeyManagerLocal.kt => model/RawGroup.kt} (84%) delete mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/model/Result.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/storage/CardStorage.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt rename ethree-kotlin/src/main/java/com/virgilsecurity/android/ethree/interaction/KeyManagerLocalDefault.kt => ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt (63%) create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt rename ethree-common/src/main/java/com/virgilsecurity/android/common/{ => util}/Const.kt (97%) create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt create mode 100644 ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt create mode 100644 tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/model/TicketTest.kt create mode 100644 tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/storage/sql/SQLCardStorageTest.kt create mode 100644 tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/AuthenticationTests.kt create mode 100644 tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/BackupTests.kt create mode 100644 tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/GroupTests.kt create mode 100644 tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/PeerToPeerTest.kt create mode 100644 tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/SearchTests.kt create mode 100644 tests/src/main/assets/databases/cards.json create mode 100644 tests/src/main/assets/databases/cards.sqlite3 diff --git a/build.gradle b/build.gradle index 6e171f41..3c99e878 100644 --- a/build.gradle +++ b/build.gradle @@ -34,32 +34,41 @@ buildscript { ext.versions = [ // Virgil - virgilSdk : '5.1.2', - virgilCrypto: '0.8.0', - pythia : '0.3.1', - keyknox : '0.2.1', + virgilSdk : '6.0-SNAPSHOT', + virgilCrypto: '0.10.2', + pythia : '0.3.2-SNAPSHOT', // Kotlin - kotlin : '1.3.40', + kotlin : '1.3.50', coroutines : '1.3.0-M1', // Gradle - gradle : '3.4.2', + gradle : '3.5.0', // Maven mavenPublish: '3.6.2', // Android android : '4.1.1.4', + appCompat : '28.0.0', + + // Room + room : '2.2.0-rc01', // Docs dokka : '0.9.18', // Tests junit : '4.12', - testsRunner : '1.0.2', + testsRunner : '1.1.1', espresso : '3.0.2', ] + ext.androidOptions = [ + compileSdkVersion : 28, + minSdkVersion : 23, + targetSdkVersion : 28, + buildToolsVersion : "28.0.3" + ] repositories { google() jcenter() @@ -85,66 +94,3 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir } - -// Default value is ALL -enum ArtifactType { - ALL, - COMMON, - STANDARD -} - -static String getArtifactType(Project project) { - def systemProperty = null - if (System.getenv('ARTIFACT_TYPE') != null) - systemProperty = System.getenv('ARTIFACT_TYPE') - if (System.getProperty('ARTIFACT_TYPE') != null) - systemProperty = System.getProperty('ARTIFACT_TYPE') - - if (systemProperty != null) - return systemProperty - else if (project.hasProperty('ARTIFACT_TYPE')) { - return project.findProperty('ARTIFACT_TYPE') - } else { - return ArtifactType.ALL - } -} - -task installEthree() { - def artifactType - try { - artifactType = getArtifactType(project) as ArtifactType - } catch (IllegalArgumentException ignored) { - throw new IllegalArgumentException("Please, choose one of the available types: " + ArtifactType.values() + "." - + " You entered: " + getArtifactType(project)) - } - - if (artifactType == ArtifactType.STANDARD) { - dependsOn ':ethree-kotlin:publishToMavenLocal' - } else if (artifactType == ArtifactType.ALL) { - dependsOn ':ethree-common:publishToMavenLocal', ':ethree-kotlin:publishToMavenLocal' - } else if (artifactType == ArtifactType.COMMON) { - dependsOn ':ethree-common:publishToMavenLocal' - } else { - throw new IllegalArgumentException("Please, choose one of artifacts type: " + ArtifactType.values()) - } -} - -task publishEthree() { - def artifactType - try { - artifactType = getArtifactType(project) as ArtifactType - } catch (IllegalArgumentException ignored) { - throw new IllegalArgumentException("Please, choose one of the available types: " + ArtifactType.values() + "." - + " You entered: " + getArtifactType(project)) - } - - if (artifactType == ArtifactType.STANDARD) { - dependsOn ':ethree-kotlin:publish' - } else if (artifactType == ArtifactType.ALL) { - dependsOn ':ethree-common:publish', ':ethree-kotlin:publish' - } else if (artifactType == ArtifactType.COMMON) { - dependsOn ':ethree-common:publish' - } else { - throw new IllegalArgumentException("Please, choose one of artifacts type: " + ArtifactType.values()) - } -} diff --git a/ethree-common/build.gradle b/ethree-common/build.gradle index 19829cf2..90c9d719 100644 --- a/ethree-common/build.gradle +++ b/ethree-common/build.gradle @@ -32,53 +32,83 @@ */ plugins { - id 'java-library' - id 'maven-publish' id 'signing' + id 'maven-publish' } -apply plugin: 'kotlin' -apply plugin: 'maven' +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' + +apply plugin: 'digital.wup.android-maven-publish' apply plugin: 'org.jetbrains.dokka' group 'com.virgilsecurity' version '0.4.1' +android { + compileSdkVersion androidOptions.compileSdkVersion + buildToolsVersion androidOptions.buildToolsVersion + + defaultConfig { + minSdkVersion androidOptions.minSdkVersion + targetSdkVersion androidOptions.targetSdkVersion + consumerProguardFiles 'proguard-rules.txt' + + javaCompileOptions { + annotationProcessorOptions { + arguments = [ + "room.schemaLocation":"$projectDir/schemas".toString(), + "room.incremental":"true", + "room.expandProjection":"true"] + } + } + } +} + dependencies { // Kotlin compileOnly "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin" compileOnly "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines" - // Virgil - api "com.virgilsecurity.sdk:sdk:$versions.virgilSdk" - implementation "com.virgilsecurity.sdk:crypto-android:$versions.virgilSdk" + // Virgil Crypto + implementation "com.virgilsecurity.crypto:pythia-android:$versions.virgilCrypto" + + // Virgil SDK + api "com.virgilsecurity.sdk:crypto-android:$versions.virgilSdk" - implementation("com.virgilsecurity:pythia:$versions.pythia") { - exclude group: 'com.virgilsecurity.sdk', module: 'crypto' - exclude group: 'com.virgilsecurity.crypto', module: 'common' - exclude group: 'com.virgilsecurity.crypto', module: 'foundation' - exclude group: 'com.virgilsecurity.crypto', module: 'pythia' + api "com.virgilsecurity:common:$versions.virgilSdk" + api ("com.virgilsecurity.sdk:sdk:$versions.virgilSdk") { + exclude group: 'com.virgilsecurity.crypto' } - implementation "com.virgilsecurity.crypto:pythia-android:$versions.virgilCrypto" - implementation("com.virgilsecurity:keyknox:$versions.keyknox") { - exclude group: 'com.virgilsecurity.sdk', module: 'crypto' - exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8' + api ("com.virgilsecurity.sdk:keyknox:$versions.virgilSdk") { + exclude group: 'com.virgilsecurity.sdk', module: "sdk" + } + api ("com.virgilsecurity:pythia:$versions.pythia") { + exclude group: 'com.virgilsecurity.crypto' + exclude group: 'com.virgilsecurity.sdk' } // Android compileOnly "com.google.android:android:$versions.android" + + // Room + implementation "androidx.room:room-runtime:$versions.room" + implementation "androidx.room:room-ktx:$versions.room" + kapt "androidx.room:room-compiler:$versions.room" } sourceCompatibility = "8" targetCompatibility = "8" task sourcesJar(type: Jar) { - from(sourceSets["main"].allSource) + from(project.android.sourceSets.main.java.srcDirs) classifier = 'sources' } task javadocJar(type: Jar, dependsOn: 'dokka') { - from "$buildDir/javadoc" + from("$buildDir/javadoc") classifier = 'javadoc' } @@ -89,7 +119,7 @@ publishing { publications { mavenJava(MavenPublication) { artifactId = 'ethree-common' - from components.java + from components.android artifact sourcesJar artifact javadocJar pom { diff --git a/ethree-common/proguard-rules.txt b/ethree-common/proguard-rules.txt new file mode 100644 index 00000000..cc2e5948 --- /dev/null +++ b/ethree-common/proguard-rules.txt @@ -0,0 +1,29 @@ +# ------------ +# --- Gson --- +# ------------ +-keepattributes Signature + +# For using GSON @Expose annotation +-keepattributes Annotation + +# Gson specific classes +-dontwarn sun.misc.** +-keep class com.google.gson.stream.** { *; } +-keep class com.google.** +-dontwarn com.google.** + +# Prevent proguard from stripping interface information from TypeAdapterFactory, +# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) +-keep class * implements com.google.gson.TypeAdapterFactory +-keep class * implements com.google.gson.JsonSerializer +-keep class * implements com.google.gson.JsonDeserializer + +# ------------------------- +# --- DatatypeConverter --- +# ------------------------- +-dontwarn javax.xml.bind.** + +# ----------------------- +# --- Virgil Security --- +# ----------------------- +-keep class com.virgilsecurity.crypto.** { *; } diff --git a/ethree-common/schemas/com.virgilsecurity.android.common.storage.sql.ETheeDatabase/1.json b/ethree-common/schemas/com.virgilsecurity.android.common.storage.sql.ETheeDatabase/1.json new file mode 100644 index 00000000..70ccf4ac --- /dev/null +++ b/ethree-common/schemas/com.virgilsecurity.android.common.storage.sql.ETheeDatabase/1.json @@ -0,0 +1,69 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "8dcd33dbd4adb92d67a256cf63a8ec45", + "entities": [ + { + "tableName": "ethree_cards", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `identity` TEXT NOT NULL, `is_outdated` INTEGER NOT NULL, `card` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "identifier", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "identity", + "columnName": "identity", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isOutdated", + "columnName": "is_outdated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "card", + "columnName": "card", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_ethree_cards_id", + "unique": true, + "columnNames": [ + "id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ethree_cards_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_ethree_cards_identity", + "unique": false, + "columnNames": [ + "identity" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ethree_cards_identity` ON `${TABLE_NAME}` (`identity`)" + } + ], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8dcd33dbd4adb92d67a256cf63a8ec45')" + ] + } +} diff --git a/ethree-common/src/main/AndroidManifest.xml b/ethree-common/src/main/AndroidManifest.xml new file mode 100644 index 00000000..864e0dfc --- /dev/null +++ b/ethree-common/src/main/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt new file mode 100644 index 00000000..b771a8dc --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt @@ -0,0 +1,771 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common + +import android.content.Context +import com.virgilsecurity.android.common.callback.OnGetTokenCallback +import com.virgilsecurity.android.common.callback.OnKeyChangedCallback +import com.virgilsecurity.android.common.exception.* +import com.virgilsecurity.android.common.manager.GroupManager +import com.virgilsecurity.android.common.manager.LookupManager +import com.virgilsecurity.android.common.model.FindUsersResult +import com.virgilsecurity.android.common.model.Group +import com.virgilsecurity.android.common.model.LookupResult +import com.virgilsecurity.android.common.storage.cloud.CloudKeyManager +import com.virgilsecurity.android.common.storage.cloud.CloudTicketStorage +import com.virgilsecurity.android.common.storage.local.GroupStorageFile +import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.android.common.storage.sql.SQLCardStorage +import com.virgilsecurity.android.common.util.Const +import com.virgilsecurity.android.common.util.Const.VIRGIL_BASE_URL +import com.virgilsecurity.android.common.util.Const.VIRGIL_CARDS_SERVICE_PATH +import com.virgilsecurity.android.common.worker.* +import com.virgilsecurity.common.model.Completable +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.common.model.Result +import com.virgilsecurity.keyknox.build.VersionVirgilAgent +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.cards.CardManager +import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier +import com.virgilsecurity.sdk.client.HttpClient +import com.virgilsecurity.sdk.client.VirgilCardClient +import com.virgilsecurity.sdk.crypto.* +import com.virgilsecurity.sdk.jwt.Jwt +import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider +import com.virgilsecurity.sdk.jwt.contract.AccessTokenProvider +import com.virgilsecurity.sdk.storage.DefaultKeyStorage +import com.virgilsecurity.sdk.storage.KeyStorage +import java.io.InputStream +import java.io.OutputStream +import java.util.* + +/** + * [EThreeCore] class simplifies work with Virgil Services to easily implement End to End Encrypted + * communication. + */ +abstract class EThreeCore +/** + * @constructor Initializing [CardManager] with provided in [EThreeCore.initialize] callback + * [onGetTokenCallback] using [CachingJwtProvider] also initializing [DefaultKeyStorage] with + * default settings. + */ +constructor(identity: String, + getTokenCallback: OnGetTokenCallback, + keyChangedCallback: OnKeyChangedCallback?, + context: Context) { + + private var accessTokenProvider: AccessTokenProvider + private val rootPath: String + + private val cloudKeyManager: CloudKeyManager + val lookupManager: LookupManager + lateinit var keyStorageLocal: KeyStorageLocal + private set + private lateinit var authorizationWorker: AuthorizationWorker + private lateinit var backupWorker: BackupWorker + private lateinit var groupWorker: GroupWorker + private lateinit var p2pWorker: PeerToPeerWorker + private lateinit var searchWorker: SearchWorker + + var groupManager: GroupManager? = null + private set + + protected val crypto: VirgilCrypto = VirgilCrypto() + + protected abstract val keyStorage: KeyStorage + + val cardManager: CardManager + val identity: String + + init { + this.identity = identity + val cardCrypto = VirgilCardCrypto(crypto) + val virgilCardVerifier = VirgilCardVerifier(cardCrypto) + val httpClient = HttpClient(Const.ETHREE_NAME, VersionVirgilAgent.VERSION) // FIXME wrong VersionVirgilAgent - from keyknox + this.accessTokenProvider = CachingJwtProvider { Jwt(getTokenCallback.onGetToken()) } + + cardManager = CardManager(cardCrypto, + accessTokenProvider, + VirgilCardVerifier(cardCrypto, false, false), + VirgilCardClient(VIRGIL_BASE_URL + VIRGIL_CARDS_SERVICE_PATH, + httpClient)) + + cloudKeyManager = CloudKeyManager(identity, + crypto, + accessTokenProvider) + + val cardStorageSqlite = SQLCardStorage(context, this.identity, crypto, virgilCardVerifier) + + this.lookupManager = LookupManager(cardStorageSqlite, cardManager, keyChangedCallback) + this.rootPath = context.filesDir.absolutePath + } + + /** + * Should be called on each new instance of `EThreeCore` child objects. Is up to developer. + * + * Initialization of workers not in constructor, because they depend on `keyStorageLocal` that + * is available only after child object of `EThreeCore` is constructed. + */ + protected fun initializeCore() { + this.keyStorageLocal = KeyStorageLocal(identity, keyStorage, crypto) + this.authorizationWorker = AuthorizationWorker(cardManager, + keyStorageLocal, + identity, + ::publishCardThenSaveLocal, + ::privateKeyDeleted) + this.backupWorker = BackupWorker(keyStorageLocal, cloudKeyManager, ::privateKeyChanged) + this.groupWorker = GroupWorker(identity, crypto, ::getGroupManager, ::computeSessionId) + this.p2pWorker = PeerToPeerWorker(keyStorageLocal, crypto) + this.searchWorker = SearchWorker(lookupManager) + + if (keyStorageLocal.exists()) { + privateKeyChanged() + } + + lookupManager.startUpdateCachedCards() + } + + internal fun getGroupManager(): GroupManager = + groupManager ?: throw EThreeException("No private key on device. You should call " + + "register() of retrievePrivateKey()") + + /** + * Publishes the public key in Virgil's Cards Service in case no public key for current + * identity is published yet. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws CryptoException + */ + @Synchronized + @JvmOverloads + fun register(keyPair: VirgilKeyPair? = null) = authorizationWorker.register(keyPair) + + /** + * Revokes the public key for current *identity* in Virgil's Cards Service. After this operation + * you can call [EThreeCore.register] again. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws UnRegistrationException if there's no public key published yet, or if there's more + * than one public key is published. + */ + @Synchronized fun unregister() = authorizationWorker.unregister() + + /** + * Generates new key pair, publishes new public key for current identity and deprecating old + * public key, saves private key to the local storage. All data that was encrypted earlier + * will become undecryptable. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws EThreeException + * @throws CryptoException + */ + @Synchronized fun rotatePrivateKey() = authorizationWorker.rotatePrivateKey() + + /** + * Checks whether the private key is present in the local storage of current device. + * Returns *true* if the key is present in the local key storage otherwise *false*. + */ + fun hasLocalPrivateKey() = authorizationWorker.hasLocalPrivateKey() + + /** + * ! *WARNING* ! If you call this function after [register] without using [backupPrivateKey] + * then you loose private key permanently, as well you won't be able to use identity that + * was used with that private key no more. + * + * Cleans up user's private key from a device - call this function when you want to log your + * user out of the device. + * + * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * @throws PrivateKeyNotFoundException + */ + fun cleanup() = authorizationWorker.cleanup() + + /** + * Returns cards from local storage for given [identities]. // TODO add Result/Completable reference in all fun's descriptions + * + * @param identities Identities. + * + * @return [FindUsersResult] with found users. + */ + fun findCachedUsers(identities: List): Result = + searchWorker.findCachedUsers(identities) + + /** + * Returns card from local storage for given [identity]. + * + * @param identity Identity. + * + * @return [Card] if it exists, *null* otherwise. + */ + fun findCachedUser(identity: String): Result = + searchWorker.findCachedUser(identity) + + /** + * Retrieves users Cards from the Virgil Cloud or local storage if exists. + * + * @param identities Array of identities to find. + * @param forceReload Will not use local cached cards if *true*. + * + * @return [FindUsersResult] with found users. + */ + fun findUsers(identities: List, forceReload: Boolean = false): Result = + searchWorker.findUsers(identities, forceReload) + + /** + * Retrieves user Card from the Virgil Cloud or local storage if exists. + * + * @param identity Identity to find. + * @param forceReload Will not use local cached card if *true*. + * + * @return [Card] that corresponds to provided [identity]. + */ + fun findUser(identity: String, forceReload: Boolean = false): Result = + searchWorker.findUser(identity, forceReload) + + /** + * Retrieves user public key from the cloud for encryption/verification operations. + * + * Searches for public key with specified [identity] and returns map of [String] -> + * [PublicKey] in [onResultListener] callback or [Throwable] if something went wrong. + * + * [PublicKeyNotFoundException] will be thrown if public key wasn't found. + * + * Can be called only if private key is on the device, otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * @throws PrivateKeyNotFoundException + * @throws PublicKeyDuplicateException + */ + @Deprecated("Use findUser instead.") // TODO add replaceWith + fun lookupPublicKeys(identity: String): Result = + searchWorker.lookupPublicKey(identity) + + /** + * Retrieves user public keys from the cloud for encryption/verification operations. + * + * Searches for public keys with specified [identities] and returns map of [String] -> + * [PublicKey] in [onResultListener] callback or [Throwable] if something went wrong. + * + * [PublicKeyNotFoundException] will be thrown for the first not found public key. + * [EThreeCore.register] + * + * Can be called only if private key is on the device, otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * To start execution of the current function, please see [Result] description. + * + * @throws PrivateKeyNotFoundException + * @throws PublicKeyDuplicateException + */ + @Deprecated("Use findUsers instead.") // TODO add replaceWith + fun lookupPublicKeys(identities: List): Result = + searchWorker.lookupPublicKeys(identities) + + /** + * Encrypts the user's private key using the user's [password] and backs up the encrypted + * private key to Virgil's cloud. This enables users to log in from other devices and have + * access to their private key to decrypt data. + * + * Encrypts loaded from private keys local storage user's *Private key* using *Public key* + * that is generated based on provided [password] after that backs up encrypted user's + * *Private key* to the Virgil's cloud storage. + * + * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws PrivateKeyNotFoundException + * @throws BackupKeyException + */ + fun backupPrivateKey(password: String): Completable = + backupWorker.backupPrivateKey(password) + + /** + * Pulls user's private key from the Virgil's cloud, decrypts it with *Private key* that + * is generated based on provided [password] and saves it to the current private keys + * local storage. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws WrongPasswordException + * @throws RestoreKeyException + */ + fun restorePrivateKey(password: String): Completable = + backupWorker.restorePrivateKey(password) + + /** + * Changes the password on a backed-up private key. + * + * Pulls user's private key from the Virgil's cloud storage, decrypts it with *Private key* + * that is generated based on provided [oldPassword] after that encrypts user's *Private key* + * using *Public key* that is generated based on provided [newPassword] and pushes encrypted + * user's *Private key* to the Virgil's cloud storage. + * + * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws PrivateKeyNotFoundException + */ + fun changePassword(oldPassword: String, + newPassword: String): Completable = + backupWorker.changePassword(oldPassword, newPassword) + + /** + * Deletes Private Key stored on Virgil's cloud. This will disable user to log in from + * other devices. + * + * Deletes private key backup using specified [password] and provides [onCompleteListener] + * callback that will notify you with successful completion or with a [Throwable] if + * something went wrong. + * + * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws PrivateKeyNotFoundException + * @throws WrongPasswordException + */ + @JvmOverloads + fun resetPrivateKeyBackup(password: String? = null): Completable = + backupWorker.resetPrivateKeyBackup(password) + + /** + * Creates group, saves in cloud and locally. + * + * @param identifier Identifier of group. Should be *> 10* length. + * @param users Cards of participants. Result of findUsers call. + * + * @return New [Group]. + */ + fun createGroup(identifier: Data, users: FindUsersResult): Result = + groupWorker.createGroup(identifier, users) + + /** + * Returns cached local group. + * + * @param identifier Identifier of group. Should be *> 10* length. + * + * @return [Group] if exists, null otherwise. + */ + fun getGroup(identifier: Data): Group? = groupWorker.getGroup(identifier) + + /** + * Loads group from cloud, saves locally. + * + * @param identifier Identifier of group. Should be *> 10* length. + * @param card Card of group initiator. + * + * @return Loaded [Group]. + */ + fun loadGroup(identifier: Data, card: Card): Result = + groupWorker.loadGroup(identifier, card) + + /** + * Deletes group from cloud and local storage. + * + * @param identifier Identifier of group. Should be *> 10* length. + */ + fun deleteGroup(identifier: Data): Completable = groupWorker.deleteGroup(identifier) + + /** + * Creates group, saves in cloud and locally. + * + * @param identifier Identifier of group. Should be *> 10* length. + * @param users Cards of participants. Result of findUsers call. + * + * @return New [Group]. + */ + fun createGroup(identifier: String, users: FindUsersResult): Result = + groupWorker.createGroup(identifier, users) + + /** + * Returns cached local group. + * + * @param identifier Identifier of group. Should be *> 10* length. + * + * @return [Group] if exists, null otherwise. + */ + internal fun getGroup(identifier: String): Group? = groupWorker.getGroup(identifier) + + /** + * Loads group from cloud, saves locally. + * + * @param identifier Identifier of group. Should be *> 10* length. + * @param card Card of group initiator. + * + * @return Loaded [Group]. + */ + fun loadGroup(identifier: String, card: Card): Result = + groupWorker.loadGroup(identifier, card) + + /** + * Deletes group from cloud and local storage. + * + * @param identifier Identifier of group. Should be *> 10* length. + */ + fun deleteGroup(identifier: String): Completable = groupWorker.deleteGroup(identifier) + + /** + * Signs then encrypts data for group of users. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param data Data to encrypt. + * @param users Result of findUsers call recipient Cards with Public Keys to sign and encrypt + * with. Use null to sign and encrypt for self. + * + * @return Encrypted Data. + */ + @JvmOverloads fun encrypt(data: Data, users: FindUsersResult? = null): Data = + p2pWorker.encrypt(data, users) + + /** + * Decrypts and verifies data from users. + * + * *Important* Requires private key in local storage. + * + * @param data Data to decrypt. + * @param user Sender Card with Public Key to verify with. Use null to decrypt and verify. + * from self. + * + * @return Decrypted Data. + */ + @JvmOverloads fun decrypt(data: Data, user: Card? = null): Data = + p2pWorker.decrypt(data, user) + + /** + * Decrypts and verifies data from users. + * + * *Important* Requires private key in local storage. + * + * @param data Data to decrypt. + * @param user Sender Card with Public Key to verify with. + * @param date Date of encryption to use proper card version. + * + * @return Decrypted Data. + */ + fun decrypt(data: Data, user: Card, date: Date): Data = p2pWorker.decrypt(data, user, date) + + /** + * Encrypts data stream. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param inputStream Data stream to be encrypted. + * @param outputStream Stream with encrypted data. + * @param users Result of findUsers call recipient Cards with Public Keys to sign and encrypt + * with. Use null to sign and encrypt for self. + */ + @JvmOverloads fun encrypt(inputStream: InputStream, + outputStream: OutputStream, + users: FindUsersResult? = null) = + p2pWorker.encrypt(inputStream, outputStream, users) + + /** + * Decrypts encrypted stream. + * + * *Important* Requires private key in local storage. + * + * @param inputStream Stream with encrypted data. + * @param outputStream Stream with decrypted data. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + fun decrypt(inputStream: InputStream, outputStream: OutputStream) = + p2pWorker.decrypt(inputStream, outputStream) + + /** + * Signs then encrypts string for group of users. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param text String to encrypt. String should be *UTF-8* encoded. + * @param users Result of findUsers call recipient Cards with Public Keys to sign and encrypt + * with. Use null to sign and encrypt for self. + * + * @return Encrypted base64String. + */ + @JvmOverloads fun encrypt(text: String, users: FindUsersResult? = null): String = + p2pWorker.encrypt(text, users) + + /** + * Decrypts and verifies base64 string from users. + * + * *Important* Requires private key in local storage. + * + * @param text Encrypted String. + * @param user Sender Card with Public Key to verify with. Use null to decrypt and verify + * from self. + * + * @return Decrypted String. + */ + @JvmOverloads fun decrypt(text: String, user: Card? = null): String = + p2pWorker.decrypt(text, user) + + /** + * Decrypts and verifies base64 string from users. + * + * *Important* Requires private key in local storage. + * + * @param text Encrypted String. + * @param user Sender Card with Public Key to verify with. + * @param date Date of encryption to use proper card version. + * + * @return Decrypted String. + */ + fun decrypt(text: String, user: Card, date: Date): String = p2pWorker.decrypt(text, user, date) + + /** + * Signs and encrypts data for user. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * @param data Data to encrypt. + * @param user User Card to encrypt for. + * + * @return Encrypted data. + */ + fun encrypt(data: Data, user: Card): Data = p2pWorker.encrypt(data, user) + + /** + * Signs and encrypts string for user. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * @param text String to encrypt. + * @param user User Card to encrypt for. + * + * @return Encrypted String. + */ + fun encrypt(text: String, user: Card): String = p2pWorker.encrypt(text, user) + + /** + * Encrypts data stream. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * @param inputStream Data stream to be encrypted. + * @param outputStream Stream with encrypted data. + * @param user User Card to encrypt for. + */ + fun encrypt(inputStream: InputStream, outputStream: OutputStream, user: Card) = + p2pWorker.encrypt(inputStream, outputStream, user) + + // Backward compatibility deprecated methods -------------------------------------------------- + + /** + * Signs then encrypts data for a group of users. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param text String to encrypt. + * @param lookupResult Result of lookupPublicKeys call recipient PublicKeys to sign and + * encrypt with. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + @Deprecated("Use encryptForUsers method instead.") // TODO change to actual fun name + fun encrypt(text: String, lookupResult: LookupResult): String = + p2pWorker.encrypt(text, lookupResult) + + /** + * Signs then encrypts data for a group of users. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param data Data to encrypt + * @param lookupResult Result of lookupPublicKeys call recipient PublicKeys to sign and + * encrypt with. + * + * @return Encrypted Data. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + @Deprecated("Use encryptForUsers method instead.") + @JvmOverloads fun encrypt(data: ByteArray, lookupResult: LookupResult? = null): ByteArray = + p2pWorker.encrypt(data, lookupResult) + + /** + * Encrypts data stream for a group of users. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param inputStream Data stream to be encrypted. + * @param outputStream Stream with encrypted data. + * @param lookupResult Result of lookupPublicKeys call recipient PublicKeys to sign and + * encrypt with. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + @Deprecated("Use encryptForUsers method instead.") // TODO change to actual methods signature + fun encrypt(inputStream: InputStream, + outputStream: OutputStream, + lookupResult: LookupResult) = + p2pWorker.encrypt(inputStream, outputStream, lookupResult) + + /** + * Decrypts and verifies encrypted text that is in base64 [String] format. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param base64String Encrypted String. + * @param sendersKey Sender PublicKey to verify with. + * + * @return Decrypted String. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + @Deprecated("Use decryptFromUser method instead.") + fun decrypt(base64String: String, sendersKey: VirgilPublicKey): String = + p2pWorker.decrypt(base64String, sendersKey) + + /** + * Decrypts and verifies encrypted data. + * + * *Important* Requires private key in local storage. + * + * @param data Data to decrypt. + * @param sendersKey Sender PublicKey to verify with. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + @Deprecated("Use decryptFromUser method instead.") + @JvmOverloads fun decrypt(data: ByteArray, sendersKey: VirgilPublicKey? = null): ByteArray = + p2pWorker.decrypt(data, sendersKey) + + internal fun privateKeyChanged(newCard: Card? = null) { + val selfKeyPair = keyStorageLocal.load() + + val localGroupStorage = GroupStorageFile(identity, crypto, selfKeyPair, rootPath) + val ticketStorageCloud = CloudTicketStorage(accessTokenProvider, keyStorageLocal) + + this.groupManager = GroupManager(localGroupStorage, + ticketStorageCloud, + this.keyStorageLocal, + this.lookupManager, + this.crypto) + + if (newCard != null) { + this.lookupManager.cardStorage.storeCard(newCard) + } + } + + internal fun privateKeyDeleted() { + groupManager?.localGroupStorage?.reset() + groupManager = null + + lookupManager.cardStorage.reset() + } + + internal fun computeSessionId(identifier: Data): Data { + if (identifier.data.size <= 10) { + throw GroupIdTooShortException("Group Id length should be > 10") + } + + val hash = crypto.computeHash(identifier.data, HashAlgorithm.SHA512) + .sliceArray(IntRange(0, 31)) + + return Data(hash) + } + + internal fun publishCardThenSaveLocal(keyPair: VirgilKeyPair? = null, + previousCardId: String? = null) { + val virgilKeyPair = keyPair ?: crypto.generateKeyPair() + + val card = if (previousCardId != null) { + cardManager.publishCard(virgilKeyPair.privateKey, + virgilKeyPair.publicKey, + this.identity, + previousCardId) + } else { + cardManager.publishCard(virgilKeyPair.privateKey, + virgilKeyPair.publicKey, + this.identity) + } + + val privateKeyData = Data(crypto.exportPrivateKey(virgilKeyPair.privateKey)) + + keyStorageLocal.store(privateKeyData) + privateKeyChanged(card) + } +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/callback/OnCompleteListener.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/callback/OnKeyChangedCallback.kt similarity index 81% rename from ethree-common/src/main/java/com/virgilsecurity/android/common/callback/OnCompleteListener.kt rename to ethree-common/src/main/java/com/virgilsecurity/android/common/callback/OnKeyChangedCallback.kt index b1b25281..67fd68ff 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/callback/OnCompleteListener.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/callback/OnKeyChangedCallback.kt @@ -34,19 +34,14 @@ package com.virgilsecurity.android.common.callback /** - * Interface that is intended to signal if some asynchronous process is completed successfully - * or not. + * OnKeyChangedCallback can be used to track users keys rotations. */ -interface OnCompleteListener { +interface OnKeyChangedCallback { /** - * This method will be called if asynchronous process is completed successfully. + * This function is called when some local card became outdated + * + * @param identity Identity, which key was changed. */ - fun onSuccess() - - /** - * This method will be called if asynchronous process is failed and provide [throwable] - * cause. - */ - fun onError(throwable: Throwable) + fun keyChanged(identity: String) } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/exception/Exceptions.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/exception/Exceptions.kt new file mode 100644 index 00000000..89a1885e --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/exception/Exceptions.kt @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.exception + +/** + * . _ _ + * .| || | _ + * -| || || | Created by: + * .| || || |- Danylo Oliinyk + * ..\_ || | on + * ....| _/ 10/23/18 + * ...-| | \ at Virgil Security + * ....|_|- + */ + +/** + * Exceptions + */ +open class EThreeException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : RuntimeException(message, throwable) + +class BackupKeyException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class StringEncodingException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class RestoreKeyException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class WrongPasswordException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class PrivateKeyNotFoundException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class PublicKeyNotFoundException @JvmOverloads constructor( + val identity: String, + override val message: String? = null, + throwable: Throwable? = null +) : EThreeException(message, throwable) + +class PublicKeyDuplicateException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class UnRegistrationException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class RawGroupException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class FileGroupStorageException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class FindUsersException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +open class GroupException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class InconsistentStateGroupException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : GroupException(message, throwable) + +class InvalidParticipantsCountGroupException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : GroupException(message, throwable) + +class GroupIdTooShortException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : GroupException(message, throwable) + +class GroupNotFoundException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : GroupException(message, throwable) + +class PermissionDeniedGroupException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : GroupException(message, throwable) + +class VerificationFailedGroupException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : GroupException(message, throwable) + +class GroupIsOutdatedGroupException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : GroupException(message, throwable) + +class MessageNotFromThisGroupException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : GroupException(message, throwable) + +class MissingCachedGroupException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : GroupException(message, throwable) + +class InvalidChangeParticipantsGroupException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : GroupException(message, throwable) + +open class CardStorageException @JvmOverloads constructor( + override val message: String? = null, throwable: Throwable? = null +) : EThreeException(message, throwable) + +class InconsistentCardStorageException @JvmOverloads constructor( + override val message: String? = "Storage turned into inconsistency state", + throwable: Throwable? = null +) : CardStorageException(message, throwable) + +class EmptyIdentitiesStorageException @JvmOverloads constructor(throwable: Throwable? = null +) : CardStorageException("Empty identities", throwable) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/exceptions/Exceptions.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/exceptions/Exceptions.kt deleted file mode 100644 index 12f1f147..00000000 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/exceptions/Exceptions.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2015-2019, Virgil Security, Inc. - * - * Lead Maintainer: Virgil Security Inc. - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * (1) Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * (2) Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * (3) Neither the name of virgil nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.virgilsecurity.android.common.exceptions - -/** - * . _ _ - * .| || | _ - * -| || || | Created by: - * .| || || |- Danylo Oliinyk - * ..\_ || | on - * ....| _/ 10/23/18 - * ...-| | \ at Virgil Security - * ....|_|- - */ - -/** - * Exceptions - */ -class BackupKeyException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : RuntimeException(message, throwable) - -class RestoreKeyException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : RuntimeException(message, throwable) - -class WrongPasswordException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : RuntimeException(message, throwable) - -class PrivateKeyNotFoundException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : RuntimeException(message, throwable) - -class PublicKeyNotFoundException @JvmOverloads constructor( - val identity: String, - override val message: String? = null, - throwable: Throwable? = null -) : RuntimeException(message, throwable) - -class PublicKeyDuplicateException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : RuntimeException(message, throwable) - -class CardNotFoundException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : RuntimeException(message, throwable) - -class RegistrationException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : RuntimeException(message, throwable) - -class PrivateKeyExistsException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : RuntimeException(message, throwable) - -class UnRegistrationException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : RuntimeException(message, throwable) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/EThreeCore.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/EThreeCore.kt deleted file mode 100644 index cec35767..00000000 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/EThreeCore.kt +++ /dev/null @@ -1,655 +0,0 @@ -/* - * Copyright (c) 2015-2019, Virgil Security, Inc. - * - * Lead Maintainer: Virgil Security Inc. - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * (1) Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * (2) Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * (3) Neither the name of virgil nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.virgilsecurity.android.common.interaction - -import com.virgilsecurity.android.common.Const -import com.virgilsecurity.android.common.Const.NO_CONTEXT -import com.virgilsecurity.android.common.Const.VIRGIL_BASE_URL -import com.virgilsecurity.android.common.Const.VIRGIL_CARDS_SERVICE_PATH -import com.virgilsecurity.android.common.exceptions.* -import com.virgilsecurity.android.common.model.Completable -import com.virgilsecurity.android.common.model.LookupResult -import com.virgilsecurity.android.common.model.Result -import com.virgilsecurity.keyknox.build.VersionVirgilAgent -import com.virgilsecurity.keyknox.exception.DecryptionFailedException -import com.virgilsecurity.keyknox.exception.EntryAlreadyExistsException -import com.virgilsecurity.pythia.brainkey.BrainKey -import com.virgilsecurity.pythia.brainkey.BrainKeyContext -import com.virgilsecurity.pythia.client.VirgilPythiaClient -import com.virgilsecurity.pythia.crypto.VirgilPythiaCrypto -import com.virgilsecurity.sdk.cards.CardManager -import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier -import com.virgilsecurity.sdk.client.HttpClient -import com.virgilsecurity.sdk.client.VirgilCardClient -import com.virgilsecurity.sdk.crypto.VirgilCardCrypto -import com.virgilsecurity.sdk.crypto.VirgilCrypto -import com.virgilsecurity.sdk.crypto.VirgilPrivateKey -import com.virgilsecurity.sdk.crypto.VirgilPublicKey -import com.virgilsecurity.sdk.exception.EmptyArgumentException -import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider -import com.virgilsecurity.sdk.jwt.contract.AccessTokenProvider -import com.virgilsecurity.sdk.storage.DefaultKeyStorage -import com.virgilsecurity.sdk.utils.ConvertionUtils -import java.io.InputStream -import java.io.OutputStream - -/** - * [EThreeCore] class simplifies work with Virgil Services to easily implement End to End Encrypted - * communication. - */ -abstract class EThreeCore -/** - * @constructor Initializing [CardManager] with provided in [EThreeCore.initialize] callback - * [onGetTokenCallback] using [CachingJwtProvider] also initializing [DefaultKeyStorage] with - * default settings. - */ -constructor(private val tokenProvider: AccessTokenProvider) { - - private val virgilCrypto = VirgilCrypto() - private val cardManager: CardManager - protected abstract val keyManagerLocal: KeyManagerLocal - private val keyManagerCloud: KeyManagerCloud - - init { - cardManager = VirgilCardCrypto().let { cardCrypto -> - val httpClient = HttpClient(Const.ETHREE_NAME, VersionVirgilAgent.VERSION) - CardManager(cardCrypto, - tokenProvider, - VirgilCardVerifier(cardCrypto, false, false), - VirgilCardClient(VIRGIL_BASE_URL + VIRGIL_CARDS_SERVICE_PATH, - httpClient)) - } - - keyManagerCloud = KeyManagerCloud( - currentIdentity(), - tokenProvider, - VersionVirgilAgent.VERSION) - } - - /** - * Publishes the public key in Virgil's Cards Service in case no public key for current - * identity is published yet. Otherwise [RegistrationException] will be thrown. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws RegistrationException - * @throws CryptoException - */ - @Synchronized fun register() = object : Completable { - override fun execute() { - if (cardManager.searchCards(currentIdentity()).isNotEmpty()) - throw RegistrationException("Card with identity " + - "${currentIdentity()} already exists") - - if (keyManagerLocal.exists()) - throw PrivateKeyExistsException("You already have a Private Key on this device" + - "for identity: ${currentIdentity()}. Please, use" + - "\'cleanup()\' function first.") - - virgilCrypto.generateKeyPair().run { - cardManager.publishCard(this.privateKey, this.publicKey, currentIdentity()) - keyManagerLocal.store(virgilCrypto.exportPrivateKey(this.privateKey)) - } - } - } - - /** - * Revokes the public key for current *identity* in Virgil's Cards Service. After this operation - * you can call [EThreeCore.register] again. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws UnRegistrationException if there's no public key published yet, or if there's more - * than one public key is published. - */ - @Synchronized fun unregister() = object : Completable { - override fun execute() { - val foundCards = cardManager.searchCards(currentIdentity()) - if (foundCards.isEmpty()) - throw UnRegistrationException("Card with identity " + - "${currentIdentity()} does not exist.") - - if (foundCards.size > 1) - throw UnRegistrationException("Too many cards with identity: " + - "${currentIdentity()}.") - - cardManager.revokeCard(foundCards.first().identifier).run { - if (hasLocalPrivateKey()) cleanup() - } - } - } - - /** - * ! *WARNING* ! If you call this function after [register] without using [backupPrivateKey] - * then you loose private key permanently, as well you won't be able to use identity that - * was used with that private key no more. - * - * Cleans up user's private key from a device - call this function when you want to log your - * user out of the device. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * @throws PrivateKeyNotFoundException - */ - fun cleanup() { - checkPrivateKeyOrThrow() - - keyManagerLocal.delete() - } - - /** - * Checks whether the private key is present in the local storage of current device. - * Returns *true* if the key is present in the local key storage otherwise *false*. - */ - fun hasLocalPrivateKey() = keyManagerLocal.exists() - - /** - * Encrypts the user's private key using the user's [password] and backs up the encrypted - * private key to Virgil's cloud. This enables users to log in from other devices and have - * access to their private key to decrypt data. - * - * Encrypts loaded from private keys local storage user's *Private key* using *Public key* - * that is generated based on provided [password] after that backs up encrypted user's - * *Private key* to the Virgil's cloud storage. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws PrivateKeyNotFoundException - * @throws BackupKeyException - */ - fun backupPrivateKey(password: String) = object : Completable { - override fun execute() { - try { - checkPrivateKeyOrThrow() - - if (password.isBlank()) - throw IllegalArgumentException("\'password\' should not be empty") - - with(keyManagerLocal.load()) { - keyManagerCloud.store(password, this.value, this.meta) - } - } catch (throwable: Throwable) { - if (throwable is EntryAlreadyExistsException) - throw BackupKeyException("Key with identity ${currentIdentity()} " + - "already backed up.") - else - throw throwable - } - } - } - - /** - * Deletes the user's private key from Virgil's cloud. - * - * Deletes private key backup using specified [password] and provides [onCompleteListener] - * callback that will notify you with successful completion or with a [Throwable] if - * something went wrong. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws PrivateKeyNotFoundException - * @throws WrongPasswordException - */ - @JvmOverloads fun resetPrivateKeyBackup(password: String? = null) = object : Completable { - override fun execute() { - try { - checkPrivateKeyOrThrow() - - if (password == null) { - keyManagerCloud.deleteAll() - } else { - if (password.isBlank()) - throw IllegalArgumentException("\'password\' should not be empty") - - keyManagerCloud.delete(password) - } - } catch (throwable: Throwable) { - if (throwable is DecryptionFailedException) - throw WrongPasswordException("Specified password is not valid.") - else - throw throwable - } - } - - } - - /** - * Pulls user's private key from the Virgil's cloud, decrypts it with *Private key* that - * is generated based on provided [password] and saves it to the current private keys - * local storage. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws WrongPasswordException - * @throws RestoreKeyException - */ - fun restorePrivateKey(password: String) = object : Completable { - override fun execute() { - try { - if (keyManagerLocal.exists()) - throw RestoreKeyException("You already have a Private Key on this device" + - "for identity: ${currentIdentity()}. Please, use" + - "\'cleanup()\' function first.") - - if (keyManagerCloud.exists(password)) { - Thread.sleep(THROTTLE_TIMEOUT) // To avoid next request been throttled - - val keyEntry = keyManagerCloud.retrieve(password) - keyManagerLocal.store(keyEntry.data) - } else { - throw RestoreKeyException("There is no key backup with " + - "identity: ${currentIdentity()}") - } - } catch (throwable: Throwable) { - if (throwable is DecryptionFailedException) - throw WrongPasswordException("Specified password is not valid.") - else - throw throwable - } - } - } - - /** - * Generates new key pair, publishes new public key for current identity and deprecating old - * public key, saves private key to the local storage. All data that was encrypted earlier - * will become undecryptable. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws PrivateKeyExistsException - * @throws CardNotFoundException - * @throws CryptoException - */ - @Synchronized fun rotatePrivateKey() = object : Completable { - override fun execute() { - if (keyManagerLocal.exists()) - throw PrivateKeyExistsException("You already have a Private Key on this device" + - "for identity: ${currentIdentity()}. Please, use" + - "\'cleanup()\' function first.") - - val cards = cardManager.searchCards(currentIdentity()) - if (cards.isEmpty()) - throw CardNotFoundException("No cards was found " + - "with identity: ${currentIdentity()}") - if (cards.size > 1) - throw IllegalStateException("${cards.size} cards was found " + - "with identity: ${currentIdentity()}. How? (: " + - "Should be <= 1. Please, contact developers if " + - "it was not an intended behaviour.") - - (cards.first() to virgilCrypto.generateKeyPair()).run { - val rawCard = cardManager.generateRawCard(this.second.privateKey, - this.second.publicKey, - currentIdentity(), - this.first.identifier) - cardManager.publishCard(rawCard) - - keyManagerLocal.store(virgilCrypto.exportPrivateKey(this.second.privateKey)) - } - } - } - - /** - * Changes the password of the private key backup. - * - * Pulls user's private key from the Virgil's cloud storage, decrypts it with *Private key* - * that is generated based on provided [oldPassword] after that encrypts user's *Private key* - * using *Public key* that is generated based on provided [newPassword] and pushes encrypted - * user's *Private key* to the Virgil's cloud storage. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws PrivateKeyNotFoundException - */ - fun changePassword(oldPassword: String, - newPassword: String) = object : Completable { - override fun execute() { - checkPrivateKeyOrThrow() - - if (oldPassword.isBlank()) - throw IllegalArgumentException("\'oldPassword\' should not be empty") - if (newPassword.isBlank()) - throw IllegalArgumentException("\'newPassword\' should not be empty") - if (newPassword == oldPassword) - throw IllegalArgumentException("\'newPassword\' can't be the same as the old one") - - val brainKeyContext = BrainKeyContext.Builder() - .setAccessTokenProvider(tokenProvider) - .setPythiaClient(VirgilPythiaClient(VIRGIL_BASE_URL)) - .setPythiaCrypto(VirgilPythiaCrypto()) - .build() - - val keyPair = BrainKey(brainKeyContext).generateKeyPair(newPassword) - - Thread.sleep(THROTTLE_TIMEOUT) // To avoid next request been throttled - - keyManagerCloud.updateRecipients(oldPassword, - listOf(keyPair.publicKey), - keyPair.privateKey) - } - } - - /** - * Signs then encrypts text for a group of users. - * - * Signs then encrypts provided [text] using [lookupResult] with recipients public keys and - * returns encrypted message converted to *base64* [String]. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ - @JvmOverloads fun encrypt(text: String, lookupResult: LookupResult? = null): String { - checkPrivateKeyOrThrow() - - if (text.isBlank()) throw EmptyArgumentException("text") - if (lookupResult?.isEmpty() == true) throw EmptyArgumentException("publicKeys") - if (lookupResult?.values?.contains(loadCurrentPublicKey()) == true) - throw IllegalArgumentException("You should not include your own public key.") - - return signThenEncryptData(text.toByteArray(), lookupResult).let { - ConvertionUtils.toBase64String(it) - } - } - - /** - * Signs then encrypts text/other data for a group of users. - * - * Signs then encrypts provided [data] using [publicKeys] list of recipients and returns - * encrypted data as [ByteArray]. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ - @JvmOverloads fun encrypt(data: ByteArray, lookupResult: LookupResult? = null): ByteArray { - checkPrivateKeyOrThrow() - - if (data.isEmpty()) throw EmptyArgumentException("data") - if (lookupResult?.isEmpty() == true) throw EmptyArgumentException("publicKeys") - if (lookupResult?.values?.contains(loadCurrentPublicKey()) == true) - throw IllegalArgumentException("You should not include your own public key.") - - return signThenEncryptData(data, lookupResult) - } - - /** - * Encrypts Input Stream for a group of users. - * - * Encrypts provided [inputStream] using [publicKeys] list of recipients and returns it - * encrypted in the [outputStream]. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ - @JvmOverloads fun encrypt(inputStream: InputStream, - outputStream: OutputStream, - lookupResult: LookupResult? = null) { - checkPrivateKeyOrThrow() - - if (inputStream.available() == 0) throw EmptyArgumentException("inputStream") - if (lookupResult?.values?.contains(loadCurrentPublicKey()) == true) - throw IllegalArgumentException("You should not include your own public key.") - - (lookupResult?.values?.toMutableList()?.apply { add(loadCurrentPublicKey()) } - ?: listOf(loadCurrentPublicKey())).run { - virgilCrypto.encrypt(inputStream, outputStream, this) - } - } - - /** - * Signs then encrypts text/other data for a group of users. - * - * Signs then encrypts provided [data] using [publicKeys] list of recipients and returns - * encrypted data as [ByteArray]. - * - * @throws CryptoException - */ - private fun signThenEncryptData(data: ByteArray, - lookupResult: LookupResult? = null): ByteArray = - (lookupResult?.values?.toMutableList()?.apply { add(loadCurrentPublicKey()) } - ?: listOf(loadCurrentPublicKey())).run { - virgilCrypto.signThenEncrypt(data, loadCurrentPrivateKey(), this) - } - - /** - * Decrypts then verifies encrypted text that is in base64 [String] format. - * - * Decrypts provided [base64String] (that was previously encrypted with [encrypt] function) - * using current user's private key. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ - @JvmOverloads fun decrypt(base64String: String, sendersKey: VirgilPublicKey? = null): String { - checkPrivateKeyOrThrow() - - if (base64String.isBlank()) throw EmptyArgumentException("data") - if (sendersKey == loadCurrentPublicKey()) - throw IllegalArgumentException("You should not provide your own public key.") - - return String(decryptAndVerifyData(ConvertionUtils.base64ToBytes(base64String), sendersKey)) - } - - /** - * Decrypts then verifies encrypted data. - * - * Decrypts provided [data] using current user's private key then verifies that data was - * encrypted by sender with her [sendersKey] and returns decrypted data in [ByteArray]. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ - @JvmOverloads fun decrypt(data: ByteArray, sendersKey: VirgilPublicKey? = null): ByteArray { - checkPrivateKeyOrThrow() - - if (data.isEmpty()) throw EmptyArgumentException("data") - if (sendersKey == loadCurrentPublicKey()) - throw IllegalArgumentException("You should not provide your own public key.") - - return decryptAndVerifyData(data, sendersKey) - } - - /** - * Decrypts encrypted inputStream. - * - * Decrypts provided [inputStream] using current user's private key and returns decrypted data - * in the [outputStream]. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ - fun decrypt(inputStream: InputStream, - outputStream: OutputStream) { - checkPrivateKeyOrThrow() - - if (inputStream.available() == 0) throw EmptyArgumentException("inputStream") - - return virgilCrypto.decrypt(inputStream, outputStream, loadCurrentPrivateKey()) - } - - /** - * Decrypts then verifies encrypted data. - * - * Decrypts provided [data] using current user's private key then verifies that data was - * encrypted by sender with her [sendersKey] and returns decrypted data in [ByteArray]. - * - * @throws CryptoException - */ - private fun decryptAndVerifyData(data: ByteArray, - sendersKey: VirgilPublicKey? = null): ByteArray = - (sendersKey == null).let { isNull -> - (if (isNull) { - listOf(loadCurrentPublicKey()) - } else { - mutableListOf(sendersKey as VirgilPublicKey).apply { - add(loadCurrentPublicKey()) - } - }) - }.let { keys -> - virgilCrypto.decryptThenVerify( - data, - loadCurrentPrivateKey(), - keys - ) - } - - /** - * Retrieves user public key from the cloud for encryption/verification operations. - * - * Searches for public key with specified [identity] and returns map of [String] -> - * [PublicKey] in [onResultListener] callback or [Throwable] if something went wrong. - * - * [PublicKeyNotFoundException] will be thrown if public key wasn't found. - * - * Can be called only if private key is on the device, otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * @throws PrivateKeyNotFoundException - * @throws PublicKeyDuplicateException - */ - fun lookupPublicKeys(identity: String) = - lookupPublicKeys(listOf(identity)) - - /** - * Retrieves user public keys from the cloud for encryption/verification operations. - * - * Searches for public keys with specified [identities] and returns map of [String] -> - * [PublicKey] in [onResultListener] callback or [Throwable] if something went wrong. - * - * [PublicKeyNotFoundException] will be thrown for the first not found public key. - * [EThreeCore.register] - * - * Can be called only if private key is on the device, otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * To start execution of the current function, please see [Result] description. - * - * @throws PrivateKeyNotFoundException - * @throws PublicKeyDuplicateException - */ - fun lookupPublicKeys(identities: List) = object : Result { - override fun get(): LookupResult { - if (identities.isEmpty()) throw EmptyArgumentException("identities") - identities.groupingBy { it } - .eachCount() - .filter { it.value > 1 } - .run { - if (this.isNotEmpty()) - throw PublicKeyDuplicateException( - "Duplicates are not allowed. " + - "Duplicated identities:\n${this}" - ) - } - - return identities.map { - it to cardManager.searchCards(it) - }.map { - if (it.second.size > 1) - throw IllegalStateException("${it.second.size} cards was found with " + - "identity: ${currentIdentity()}. How? (: " + - "Should be <= 1. Please, contact developers " + - "if it was not an intended behaviour.") - else - it - }.map { - if (it.second.isNotEmpty()) - it.first to it.second.first().publicKey as VirgilPublicKey - else - throw PublicKeyNotFoundException(it.first) - }.toMap() - } - } - - /** - * Loads and returns current user's [PrivateKey]. Current user's identity is taken - * from [tokenProvider]. - */ - private fun loadCurrentPrivateKey(): VirgilPrivateKey = - keyManagerLocal.load().let { - virgilCrypto.importPrivateKey(it.value).privateKey - } - - /** - * Loads and returns current user's [PublicKey] that is extracted from current - * user's [PrivateKey]. Current user's identity is taken from [tokenProvider]. - */ - private fun loadCurrentPublicKey(): VirgilPublicKey = - virgilCrypto.extractPublicKey(loadCurrentPrivateKey()) - - /** - * Extracts current user's *Identity* from Json Web Token received from [tokenProvider]. - */ - private fun currentIdentity() = tokenProvider.getToken(NO_CONTEXT).identity - - /** - * Checks if private key for current identity is present in local key storage or throws an - * [PrivateKeyNotFoundException] exception. - */ - private fun checkPrivateKeyOrThrow() { - if (!keyManagerLocal.exists()) throw PrivateKeyNotFoundException( - "You have to get private key first. Use \'register\' " + - "or \'restorePrivateKey\' functions.") - } - - companion object { - private const val THROTTLE_TIMEOUT = 2 * 1000L // 2 seconds - } -} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/KeyManagerCloud.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/KeyManagerCloud.kt deleted file mode 100644 index 27e5aa86..00000000 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/KeyManagerCloud.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2015-2019, Virgil Security, Inc. - * - * Lead Maintainer: Virgil Security Inc. - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * (1) Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * (2) Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * (3) Neither the name of virgil nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.virgilsecurity.android.common.interaction - -import com.virgilsecurity.android.common.Const -import com.virgilsecurity.android.common.Const.VIRGIL_BASE_URL -import com.virgilsecurity.keyknox.KeyknoxManager -import com.virgilsecurity.keyknox.client.HttpClient -import com.virgilsecurity.keyknox.client.KeyknoxClient -import com.virgilsecurity.keyknox.cloud.CloudKeyStorage -import com.virgilsecurity.keyknox.crypto.KeyknoxCrypto -import com.virgilsecurity.pythia.brainkey.BrainKey -import com.virgilsecurity.pythia.brainkey.BrainKeyContext -import com.virgilsecurity.pythia.client.VirgilPythiaClient -import com.virgilsecurity.pythia.crypto.VirgilPythiaCrypto -import com.virgilsecurity.sdk.crypto.VirgilPrivateKey -import com.virgilsecurity.sdk.crypto.VirgilPublicKey -import com.virgilsecurity.sdk.jwt.contract.AccessTokenProvider -import java.net.URL - -/** - * KeyManagerCloud - */ -class KeyManagerCloud( - private val identity: String, - private val tokenProvider: AccessTokenProvider, - ethreeVersion: String -) { - - private val keyknoxClient: KeyknoxClient = - KeyknoxClient(URL(VIRGIL_BASE_URL), HttpClient(Const.ETHREE_NAME, ethreeVersion)) - private val brainKeyContext: BrainKeyContext = BrainKeyContext.Builder() - .setAccessTokenProvider(tokenProvider) - .setPythiaClient(VirgilPythiaClient(VIRGIL_BASE_URL, - Const.ETHREE_NAME, - Const.ETHREE_NAME, - ethreeVersion)) - .setPythiaCrypto(VirgilPythiaCrypto()) - .build() - - fun exists(password: String) = initCloudKeyStorage(password).exists(identity) - - fun store(password: String, data: ByteArray, meta: Map?) = - initCloudKeyStorage(password).store(identity, data, meta) - - fun retrieve(password: String) = initCloudKeyStorage(password).retrieve(identity) - - fun delete(password: String) = initCloudKeyStorage(password).delete(identity) - - fun deleteAll() = - keyknoxClient.resetValue(tokenProvider.getToken(Const.NO_CONTEXT).stringRepresentation()) - - fun updateRecipients(password: String, - publicKeys: List, - privateKey: VirgilPrivateKey) = - initCloudKeyStorage(password).updateRecipients(publicKeys, privateKey) - - /** - * Initializes [SyncKeyStorage] with default settings, [tokenProvider] and provided - * [password] after that returns initialized [SyncKeyStorage] object. - */ - private fun initCloudKeyStorage(password: String): CloudKeyStorage = - BrainKey(brainKeyContext).generateKeyPair(password) - .let { keyPair -> - val keyknoxManager = KeyknoxManager(tokenProvider, - keyknoxClient, - listOf(keyPair.publicKey), - keyPair.privateKey, - KeyknoxCrypto()) - val cloudKeyStorage = CloudKeyStorage(keyknoxManager).also { cloudKeyStorage -> - cloudKeyStorage.retrieveCloudEntries() - } - cloudKeyStorage - } -} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt new file mode 100644 index 00000000..6cd4ba15 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.manager + +import com.virgilsecurity.android.common.exception.GroupException +import com.virgilsecurity.android.common.exception.GroupNotFoundException +import com.virgilsecurity.android.common.model.Group +import com.virgilsecurity.android.common.model.GroupInfo +import com.virgilsecurity.android.common.model.RawGroup +import com.virgilsecurity.android.common.model.Ticket +import com.virgilsecurity.android.common.storage.cloud.CloudTicketStorage +import com.virgilsecurity.android.common.storage.local.GroupStorageFile +import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.crypto.VirgilCrypto + +/** + * GroupManager + */ +class GroupManager( + internal val localGroupStorage: GroupStorageFile, + private val cloudTicketStorage: CloudTicketStorage, + private val keyStorageLocal: KeyStorageLocal, + private val lookupManager: LookupManager, + private val crypto: VirgilCrypto +) { + + private val identity: String = localGroupStorage.identity + + private fun parse(rawGroup: RawGroup): Group = Group(rawGroup, + crypto, + keyStorageLocal, + this, + lookupManager) + + internal fun store(ticket: Ticket, cards: List): Group { + val rawGroup = RawGroup(GroupInfo(this.identity), listOf(ticket)) + + cloudTicketStorage.store(ticket, cards) + localGroupStorage.store(rawGroup) + + return parse(rawGroup) + } + + internal fun pull(sessionId: Data, card: Card): Group { + val tickets = cloudTicketStorage.retrieve(sessionId, + card.identity, + card.publicKey) + + if (tickets.isEmpty()) { + localGroupStorage.delete(sessionId) + + throw GroupNotFoundException("Group with provided id was not found") + } + + val rawGroup = RawGroup(GroupInfo(card.identity), tickets) + + localGroupStorage.store(rawGroup) + + return parse(rawGroup) + } + + internal fun addAccess(cards: List, sessionId: Data) = + cloudTicketStorage.addRecipients(cards, sessionId) + + internal fun reAddAccess(card: Card, sessionId: Data) = + cloudTicketStorage.reAddRecipient(card, sessionId) + + internal fun retrieve(sessionId: Data): Group? { + val rawGroup = localGroupStorage.retrieve(sessionId, MAX_TICKETS_IN_GROUP) + + return if (rawGroup == null) rawGroup else parse(rawGroup) + } + + internal fun retrieve(sessionId: Data, epoch: Long): Group? { + val rawGroup = localGroupStorage.retrieve(sessionId, epoch) + + return if (rawGroup == null) rawGroup else parse(rawGroup) + } + + internal fun removeAccess(identities: Set, sessionId: Data) { + identities.forEach { + cloudTicketStorage.removeRecipient(it, sessionId) + } + } + + internal fun delete(sessionId: Data) { + cloudTicketStorage.delete(sessionId) + localGroupStorage.delete(sessionId) + } + + companion object { + const val MAX_TICKETS_IN_GROUP = 50 + } +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt new file mode 100644 index 00000000..12c32c65 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.manager + +import com.virgilsecurity.android.common.callback.OnKeyChangedCallback +import com.virgilsecurity.android.common.exception.FindUsersException +import com.virgilsecurity.android.common.model.FindUsersResult +import com.virgilsecurity.android.common.storage.CardStorage +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.cards.CardManager +import com.virgilsecurity.sdk.exception.EmptyArgumentException + +/** + * LookupManager + */ +class LookupManager( + internal val cardStorage: CardStorage, + private val cardManager: CardManager, + private val onKeyChangedCallback: OnKeyChangedCallback? = null +) { + + internal fun startUpdateCachedCards() { + val cardIdsNewest = cardStorage.getNewestCardIds() + + val cardIdsChunked = cardIdsNewest.chunked(MAX_GET_OUTDATED_COUNT) + + for (cardIds in cardIdsChunked) { + val outdatedIds = cardManager.getOutdated(cardIds) + + for (outdatedId in outdatedIds) { + val outdatedCard = + cardStorage.getCard(outdatedId) + ?: throw FindUsersException("Card with id: $outdatedId was not found " + + "locally. Try to call findUsers first") + + onKeyChangedCallback?.keyChanged(outdatedCard.identity) + + val newCard = lookupCard(outdatedCard.identity, true) + + cardStorage.storeCard(newCard) + } + } + // TODO add exception handling for DB requests, etc + } + + internal fun lookupCachedCards(identities: List): FindUsersResult { + if (identities.isEmpty()) throw EmptyArgumentException("identities") + + val result: MutableMap = mutableMapOf() + val cards = cardStorage.searchCards(identities) + + for (identity in identities) { + val card = cards.firstOrNull { it.identity == identity } + ?: throw FindUsersException("Card with identity: $identity was not found " + + "locally. Try to call findUsers first") + + result[identity] = card + } + + return FindUsersResult(result) + } + + internal fun lookupCachedCard(identity: String): Card { + val cards = cardStorage.searchCards(listOf(identity)) + + if (cards.size >= 2) + throw FindUsersException("Found duplicated Cards for identity: $identity") + + return cards.firstOrNull() + ?: throw FindUsersException("Card with identity: $identity was not found " + + "locally. Try to call findUsers first") + } + + internal fun lookupCards(identities: List, + forceReload: Boolean = false): FindUsersResult { + if (identities.isEmpty()) throw EmptyArgumentException("identities") + + val result: MutableMap = mutableMapOf() + val identitiesDistincted: MutableList = identities.distinct().toMutableList() + + if (!forceReload) { + val cards = cardStorage.searchCards(identitiesDistincted) + + val identitiesToRemove = mutableSetOf() + for (identity in identitiesDistincted) { + val card = cards.firstOrNull { it.identity == identity } + if (card != null) { + identitiesToRemove.add(identity) + result[identity] = card + } + } + identitiesDistincted.removeAll(identitiesToRemove) + } + + if (identitiesDistincted.isNotEmpty()) { + val identitiesChunks = identitiesDistincted.chunked(MAX_SEARCH_COUNT) + + for (identitiesChunk in identitiesChunks) { + val cards = cardManager.searchCards(identitiesChunk) + + for (card in cards) { + if (result[card.identity] != null) { + throw FindUsersException("Found duplicated Cards for identity: " + + card.identity) + } + + cardStorage.storeCard(card) + result[card.identity] = card + } + } + } + + if (result.keys.toList().sorted() != identities.distinct().sorted()) { + throw FindUsersException("Card for one or more of provided identities was not found") + } + + return FindUsersResult(result) + } + + internal fun lookupCard(identity: String, forceReload: Boolean = false): Card { + val cards = lookupCards(listOf(identity), forceReload) + + return cards[identity] + ?: throw FindUsersException("Card for one or more of provided identities " + + "was not found") + } + + companion object { + private const val MAX_SEARCH_COUNT = 50 + private const val MAX_GET_OUTDATED_COUNT = 1_000 + } +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Completable.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Completable.kt deleted file mode 100644 index 0fae7b88..00000000 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Completable.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2015-2019, Virgil Security, Inc. - * - * Lead Maintainer: Virgil Security Inc. - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * (1) Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * (2) Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * (3) Neither the name of virgil nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.virgilsecurity.android.common.model - -import com.virgilsecurity.android.common.callback.OnCompleteListener -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch - -/** - * Completable's class intent is to give possibility to call a completable operation that got only Success or Error - * without actual result *synchronously* or *asynchronously*. - */ -interface Completable { - - /** - * Call this method to complete operation *synchronously*. - */ - fun execute() - - /** - * Call this method to complete operation *asynchronously*. You'll get the completion status in the - * [onCompleteListener]. Provided [scope] will be used to execute operation. - */ - fun addCallback(onCompleteListener: OnCompleteListener, scope: CoroutineScope = GlobalScope) { - scope.launch { - try { - execute() - onCompleteListener.onSuccess() - } catch (throwable: Throwable) { - onCompleteListener.onError(throwable) - } - } - } - - /** - * Call this method to complete operation *asynchronously*. You'll get the completion status in the - * [onCompleteListener]. - */ - fun addCallback(onCompleteListener: OnCompleteListener) = - addCallback(onCompleteListener, GlobalScope) - -} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/callback/OnResultListener.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/FindUsersResult.kt similarity index 75% rename from ethree-common/src/main/java/com/virgilsecurity/android/common/callback/OnResultListener.kt rename to ethree-common/src/main/java/com/virgilsecurity/android/common/model/FindUsersResult.kt index 88134990..ae75729a 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/callback/OnResultListener.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/FindUsersResult.kt @@ -31,23 +31,20 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.common.callback +package com.virgilsecurity.android.common.model + +import com.virgilsecurity.sdk.cards.Card /** - * Interface that is intended to return ** type result if some asynchronous process is - * completed successfully, otherwise error will be returned. + * FindUsersResult is used for the result of findUsers call. */ -interface OnResultListener { +class FindUsersResult : HashMap { + + constructor(initialCapacity: Int, loadFactor: Float) : super(initialCapacity, loadFactor) + + constructor(initialCapacity: Int) : super(initialCapacity) - /** - * This method will be called if asynchronous process is completed successfully and - * provide ** type [result]. - */ - fun onSuccess(result: T) + constructor() : super() - /** - * This method will be called if asynchronous process is failed and provide [throwable] - * cause. - */ - fun onError(throwable: Throwable) + constructor(map: Map) : super(map) } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt new file mode 100644 index 00000000..66242d6e --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.model + +import com.virgilsecurity.android.common.exception.* +import com.virgilsecurity.android.common.manager.GroupManager +import com.virgilsecurity.android.common.manager.LookupManager +import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.common.model.Completable +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.crypto.foundation.FoundationException +import com.virgilsecurity.crypto.foundation.GroupSession +import com.virgilsecurity.crypto.foundation.GroupSessionMessage +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.utils.ConvertionUtils +import java.util.* +import kotlin.collections.HashSet + +/** + * Group + */ +class Group constructor( + rawGroup: RawGroup, + private val crypto: VirgilCrypto, + private val keyStorageLocal: KeyStorageLocal, + private val groupManager: GroupManager, + private val lookupManager: LookupManager +) { + + val initiator: String = rawGroup.info.initiator + var participants: MutableSet + private set + + internal var session: GroupSession + + private val selfIdentity: String = keyStorageLocal.identity + + init { + val tickets = rawGroup.tickets.sortedBy { it.groupMessage.epoch } // TODO check sort order matches $0.groupMessage.getEpoch() < $1.groupMessage.getEpoch() + val lastTicket = tickets.lastOrNull() ?: throw GroupException("Group is invalid") + + validateParticipantsCount(lastTicket.participants.size) + + this.participants = lastTicket.participants.toMutableSet() + this.session = generateSession(tickets) + } + + internal fun checkPermissions() { + if (selfIdentity != initiator) { + throw PermissionDeniedGroupException("Only group initiator can do changed on group") + } + } + + internal fun generateSession(tickets: List): GroupSession { + val session = GroupSession() + session.setRng(crypto.rng) + + tickets.forEach { + session.addEpoch(it.groupMessage) + } + + return session + } + + /** + * Signs and encrypts data for group. + * + * @param string string to encrypt. + * + * @return encrypted base64String. + * + * @notice Requires private key in local storage. + */ + fun encrypt(string: String): String { + return ConvertionUtils.toBase64String(encrypt(string.toByteArray())) + } + + /** + * Signs and encrypts data for group. + * + * @param data byte array to encrypt. + * + * @return encrypted byte array. + * + * @notice Requires private key in local storage. + */ + fun encrypt(data: ByteArray): ByteArray { + val selfKeyPair = this.keyStorageLocal.load() + val encrypted = this.session.encrypt(data, selfKeyPair.privateKey.privateKey) + return encrypted.serialize() + } + + /** + * Decrypts and verifies data from group participant. + * + * @param data encrypted byte array. + * @param senderCard sender Card to verify with. + * @param date date of message. Use it to prevent verifying new messages with old card. + * + * @return decrypted byte array. + */ + fun decrypt(data: ByteArray, senderCard: Card, date: Date? = null): ByteArray { + val encrypted = GroupSessionMessage.deserialize(data) + var card = senderCard + + if (date != null) { + // Find a card which is actual for the date + var previousCard = card.previousCard + while (previousCard != null) { + if (!date.before(card.createdAt)) { + break + } + previousCard = card.previousCard + card = previousCard + } + } + + if (!Arrays.equals(this.session.sessionId, encrypted.sessionId)) { + throw MessageNotFromThisGroupException() + } + + val messageEpoch = encrypted.epoch + val currentEpoch = this.session.currentEpoch + + if (currentEpoch < messageEpoch) { + throw GroupIsOutdatedGroupException() + } + + try { + return if (currentEpoch - messageEpoch < GroupManager.MAX_TICKETS_IN_GROUP) { + this.session.decrypt(encrypted, card.publicKey.publicKey) + } else { + val sessionId = encrypted.sessionId + + val tempGroup = this.groupManager.retrieve(Data(sessionId), messageEpoch) + ?: throw MissingCachedGroupException() + + tempGroup.decrypt(data, senderCard) + } + } catch (e: FoundationException) { + throw VerificationFailedGroupException() + } + } + + /** + * Decrypts and verifies base64 string from group participant. + * + * @param text encryted String. + * @param senderCard sender Card to verify with. + * @param date date of message. Use it to prevent verifying new messages with old card. + * + * @return decrypted String. + */ + fun decrypt(text: String, senderCard: Card, date: Date? = null): String { + val data: ByteArray + try { + data = ConvertionUtils.base64ToBytes(text) + } catch (e: Exception) { + throw StringEncodingException() + } + + val decryptedData = this.decrypt(data, senderCard, date) + return ConvertionUtils.toString(decryptedData) + } + + /** + * Updates group. + */ + fun update(): Completable = object : Completable { + override fun execute() { + val sessionId = this@Group.session.sessionId + val card = lookupManager.lookupCard(this@Group.initiator) + val group = groupManager.pull(Data(sessionId), card) + this@Group.session = group.session + this@Group.participants = group.participants + } + } + + /** + * Adds new participants to group. + * + * @param participants Cards of users to add. Result of findUsers call. + * + * @notice New participant will be able to decrypt all history + */ + fun add(participants: FindUsersResult): Completable = object : Completable { + override fun execute() { + checkPermissions() + + val oldSet = this@Group.participants + val newSet = oldSet.union(participants.keys) + + validateParticipantsCount(newSet.size) + + if (newSet == oldSet) throw InvalidChangeParticipantsGroupException() + + val addSet = newSet.subtract(oldSet) + + val addedCards = mutableListOf() + addSet.forEach { + val card = participants[it] ?: throw InconsistentStateGroupException() + addedCards.add(card) + } + + this@Group.shareTickets(addedCards) + } + } + + /** + * Share group access and history on new Card of existing participant. + * + * @param Participant Card. + */ + fun reAdd(participant: Card): Completable = object : Completable { + override fun execute() { + checkPermissions() + + groupManager.reAddAccess(participant, Data(this@Group.session.sessionId)) + } + } + + /** + * Removes participants from group. + * + * *Note* Removed participant will not be able to decrypt previous history again after group + * update. + * + * @param Cards of users to remove. Result of findUsers call. + */ + fun remove(participants: FindUsersResult): Completable = object : Completable { + override fun execute() { + checkPermissions() + + val oldSet = this@Group.participants + val newSet = oldSet.subtract(participants.keys) + + validateParticipantsCount(newSet.size) + + if (newSet == oldSet) throw InvalidChangeParticipantsGroupException() + + val newSetLookup = lookupManager.lookupCards(newSet.toList()) + addNewTicket(newSetLookup) + + val removedSet = oldSet.subtract(newSet) + groupManager.removeAccess(removedSet, Data(this@Group.session.sessionId)) + } + } + + fun add(participant: Card): Completable = + add(FindUsersResult(mapOf(participant.identity to participant))) + + fun remove(participant: Card): Completable = + remove(FindUsersResult(mapOf(participant.identity to participant))) + + private fun shareTickets(cards: List) { + val sessionId = this.session.sessionId + groupManager.addAccess(cards, Data(sessionId)) + val newParticipants = cards.map { it.identity } + this.participants.addAll(newParticipants.toSet()) + } + + private fun addNewTicket(participants: FindUsersResult) { + val newSet = HashSet(participants.keys) + + val ticketMessage = this.session.createGroupTicket().ticketMessage + val ticket = Ticket(ticketMessage, newSet) + + groupManager.store(ticket, participants.values.toList()) + this.session.addEpoch(ticket.groupMessage) + + newSet.add(this.initiator) + + this.participants = newSet + } + + companion object { + internal fun validateParticipantsCount(count: Int) { + if (count !in VALID_PARTICIPANTS_COUNT_RANGE) { + throw InvalidParticipantsCountGroupException("Please check valid participants count range in " + + "Group.VALID_PARTICIPANTS_COUNT_RANGE") + } + } + + val VALID_PARTICIPANTS_COUNT_RANGE = 2..100 + } +} diff --git a/ethree-enclave/src/main/java/com/virgilsecurity/android/ethreeenclave/interaction/KeyManagerLocalEnclave.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/GroupInfo.kt similarity index 65% rename from ethree-enclave/src/main/java/com/virgilsecurity/android/ethreeenclave/interaction/KeyManagerLocalEnclave.kt rename to ethree-common/src/main/java/com/virgilsecurity/android/common/model/GroupInfo.kt index 2db6d8b3..8339c6f5 100644 --- a/ethree-enclave/src/main/java/com/virgilsecurity/android/ethreeenclave/interaction/KeyManagerLocalEnclave.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/GroupInfo.kt @@ -31,33 +31,23 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethreeenclave.interaction +package com.virgilsecurity.android.common.model -import android.content.Context -import com.virgilsecurity.android.common.interaction.KeyManagerLocal -import com.virgilsecurity.sdk.androidutils.storage.AndroidKeyStorage -import com.virgilsecurity.sdk.storage.JsonKeyEntry -import com.virgilsecurity.sdk.storage.KeyEntry -import com.virgilsecurity.sdk.storage.KeyStorage -import java.time.Duration +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.common.util.SerializeUtils /** - * KeyManagerLocalEnclave + * GroupInfo */ -class KeyManagerLocalEnclave( - private val keyStorage: AndroidKeyStorage, - private val identity: String -) : KeyManagerLocal { +class GroupInfo(internal val initiator: String) { - override fun exists() = keyStorage.exists(identity) - - override fun store(privateKey: ByteArray) = keyStorage.store(JsonKeyEntry(identity, privateKey)) - - override fun load(): KeyEntry = keyStorage.load(identity) - - override fun delete() = keyStorage.delete(identity) + internal fun serialize(): Data { + return SerializeUtils.serialize(this) + } companion object { - private const val KEYSTORE_NAME = "virgil_android_keystore" + internal fun deserialize(data: Data): GroupInfo { + return SerializeUtils.deserialize(data, GroupInfo::class.java) + } } } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/LookupResult.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/LookupResult.kt index 6cc39029..15280a0c 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/LookupResult.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/LookupResult.kt @@ -40,3 +40,7 @@ import com.virgilsecurity.sdk.crypto.VirgilPublicKey */ typealias LookupResult = Map + +fun LookupResult?.toPublicKeys(): List? { + return this?.values?.toList() +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/KeyManagerLocal.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/RawGroup.kt similarity index 84% rename from ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/KeyManagerLocal.kt rename to ethree-common/src/main/java/com/virgilsecurity/android/common/model/RawGroup.kt index 778954cc..5b8301c7 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/interaction/KeyManagerLocal.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/RawGroup.kt @@ -31,20 +31,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.common.interaction +package com.virgilsecurity.android.common.model -import com.virgilsecurity.sdk.storage.KeyEntry +import com.virgilsecurity.android.common.exception.RawGroupException /** - * KeyManagerLocal + * RawGroup */ -interface KeyManagerLocal { +class RawGroup( + val info: GroupInfo, + val tickets: List +) { - fun exists() : Boolean - - fun store(privateKey: ByteArray) - - fun load(): KeyEntry - - fun delete() + init { + if (tickets.isEmpty()) throw RawGroupException("Tickets are empty") + } } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Result.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Result.kt deleted file mode 100644 index 98c56a11..00000000 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Result.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2015-2019, Virgil Security, Inc. - * - * Lead Maintainer: Virgil Security Inc. - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * (1) Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * (2) Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * (3) Neither the name of virgil nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.virgilsecurity.android.common.model - -import com.virgilsecurity.android.common.callback.OnResultListener -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch - -/** - * Result's class intent is to give possibility to call an operation that returns result in Success or Error in case of - * error *synchronously* or *asynchronously*. - */ -interface Result { - - /** - * Call this method to get result *synchronously*. - */ - fun get(): T - - /** - * Call this method to get result *asynchronously*. You'll get the result of an operation in the - * [onResultListener]. Provided [scope] will be used to execute operation. - */ - fun addCallback(onResultListener: OnResultListener, scope: CoroutineScope = GlobalScope) { - scope.launch { - try { - val result = get() - onResultListener.onSuccess(result) - } catch (throwable: Throwable) { - onResultListener.onError(throwable) - } - } - } - - /** - * Call this method to get result *asynchronously*. You'll get the result of an operation in the - * [onResultListener]. - */ - fun addCallback(onResultListener: OnResultListener) = - addCallback(onResultListener, GlobalScope) -} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt new file mode 100644 index 00000000..d757dc47 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.model + +import android.os.Parcel +import android.os.Parcelable +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.common.util.SerializeUtils +import com.virgilsecurity.crypto.foundation.GroupSessionMessage +import com.virgilsecurity.crypto.foundation.GroupSessionTicket +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import java.io.Serializable + + +/** + * Ticket + */ +class Ticket : Parcelable { // TODO test parcelable implementation + + val groupMessage: GroupSessionMessage + val participants: Set + + private constructor(parcel: Parcel) { + val groupMessageDataLength = parcel.readInt() + val serializedGroupMessage = ByteArray(groupMessageDataLength) + parcel.readByteArray(serializedGroupMessage) + this.groupMessage = GroupSessionMessage.deserialize(serializedGroupMessage) + + this.participants = parcel.readSerializable() as Set + } + + internal constructor(groupMessage: GroupSessionMessage, participants: Set) { + this.groupMessage = groupMessage + this.participants = participants + } + + constructor(crypto: VirgilCrypto, + sessionId: Data, + participants: Set) { + require(participants is Serializable) { "Please, use serializable Set for participants." } + + val ticket = GroupSessionTicket() + ticket.setRng(crypto.rng) + + ticket.setupTicketAsNew(sessionId.data) + + this.groupMessage = ticket.ticketMessage + this.participants = participants + } + + internal fun serialize(): Data = SerializeUtils.serialize(this) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + val groupMessageData = this.groupMessage.serialize() + parcel.writeInt(groupMessageData.size) + parcel.writeByteArray(groupMessageData) + + parcel.writeSerializable(participants as Serializable) + } + + override fun describeContents(): Int { + return hashCode() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Ticket + + if (!groupMessage.serialize().contentEquals(other.groupMessage.serialize())) return false + if (participants != other.participants) return false + + return true + } + + override fun hashCode(): Int { + var result = groupMessage.hashCode() + result = 31 * result + participants.hashCode() + return result + } + + companion object { + @JvmStatic internal fun deserialize(data: Data): Ticket = + SerializeUtils.deserialize(data, Ticket::class.java) + + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Ticket { + return Ticket(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/CardStorage.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/CardStorage.kt new file mode 100644 index 00000000..82a80ef7 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/CardStorage.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.storage + +import com.virgilsecurity.sdk.cards.Card + +/** + * Virgil Cards storage. + */ +interface CardStorage { + + fun storeCard(card: Card) + + fun getCard(cardId: String): Card? + + fun searchCards(identities: List): List + + fun getNewestCardIds(): List + + fun reset() +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt new file mode 100644 index 00000000..ff692d42 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.storage.cloud + +import com.virgilsecurity.android.common.exception.WrongPasswordException +import com.virgilsecurity.android.common.util.Const +import com.virgilsecurity.android.common.util.Const.VIRGIL_BASE_URL +import com.virgilsecurity.keyknox.KeyknoxManager +import com.virgilsecurity.keyknox.build.VersionVirgilAgent +import com.virgilsecurity.keyknox.client.HttpClient +import com.virgilsecurity.keyknox.client.KeyknoxClient +import com.virgilsecurity.keyknox.cloud.CloudKeyStorage +import com.virgilsecurity.keyknox.exception.DecryptionFailedException +import com.virgilsecurity.keyknox.exception.KeyknoxCryptoException +import com.virgilsecurity.keyknox.model.CloudEntry +import com.virgilsecurity.pythia.brainkey.BrainKey +import com.virgilsecurity.pythia.brainkey.BrainKeyContext +import com.virgilsecurity.pythia.client.VirgilPythiaClient +import com.virgilsecurity.pythia.crypto.VirgilPythiaCrypto +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.crypto.VirgilPrivateKey +import com.virgilsecurity.sdk.crypto.VirgilPublicKey +import com.virgilsecurity.sdk.crypto.exceptions.DecryptionException +import com.virgilsecurity.sdk.jwt.contract.AccessTokenProvider +import java.net.URL + +/** + * CloudKeyManager + */ +class CloudKeyManager( + private val identity: String, + private val crypto: VirgilCrypto, + private val tokenProvider: AccessTokenProvider, + private val baseUrl: String = VIRGIL_BASE_URL +) { + + private val keyknoxClient: KeyknoxClient + private val keyknoxManager: KeyknoxManager + private val brainKey: BrainKey + + init { + val httpClient = HttpClient(tokenProvider, Const.ETHREE_NAME, VersionVirgilAgent.VERSION) + keyknoxClient = KeyknoxClient(httpClient, URL(baseUrl)) + this.keyknoxManager = KeyknoxManager(keyknoxClient) + + // TODO change VirgilPythiaClient to have tokenProvider inside + val pythiaClient = VirgilPythiaClient(baseUrl, Const.ETHREE_NAME, Const.ETHREE_NAME, + VersionVirgilAgent.VERSION) + val brainKeyContext = BrainKeyContext.Builder() + .setAccessTokenProvider(tokenProvider) + .setPythiaClient(pythiaClient) + .setPythiaCrypto(VirgilPythiaCrypto()) + .build() + + this.brainKey = BrainKey(brainKeyContext) + } + + fun exists(password: String) = setupCloudKeyStorage(password).exists(identity) + + fun store(key: VirgilPrivateKey, password: String) { + val exportedIdentityKey = this.crypto.exportPrivateKey(key) + setupCloudKeyStorage(password).store(this.identity, exportedIdentityKey) + } + + fun retrieve(password: String): CloudEntry { + return setupCloudKeyStorage(password).retrieve(identity) + } + + fun delete(password: String) { + setupCloudKeyStorage(password).delete(identity) + } + + fun deleteAll() { + this.keyknoxManager.resetValue() + } + + fun changePassword(oldPassword: String, newPassword: String) { + val cloudKeyStorage = setupCloudKeyStorage(oldPassword) + + Thread.sleep(2000) + + val brainKeyPair = this.brainKey.generateKeyPair(newPassword) + + try { + cloudKeyStorage.updateRecipients(listOf(brainKeyPair.publicKey), brainKeyPair.privateKey) + } + catch (e: KeyknoxCryptoException) { + throw WrongPasswordException() + } + } + + /** + * Initializes [SyncKeyStorage] with default settings, [tokenProvider] and provided + * [password] after that returns initialized [SyncKeyStorage] object. + */ + private fun setupCloudKeyStorage(password: String): CloudKeyStorage { + val brainKeyPair = this.brainKey.generateKeyPair(password) + + val cloudKeyStorage = CloudKeyStorage(this.keyknoxManager, listOf(brainKeyPair.publicKey), + brainKeyPair.privateKey) + + try { + cloudKeyStorage.retrieveCloudEntries() + } catch (e: DecryptionFailedException) { + throw WrongPasswordException() + } + + return cloudKeyStorage + } +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt new file mode 100644 index 00000000..484f543f --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.storage.cloud + +import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.android.common.model.Ticket +import com.virgilsecurity.android.common.util.Const +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.common.util.toHexString +import com.virgilsecurity.crypto.foundation.GroupSessionMessage +import com.virgilsecurity.keyknox.KeyknoxManager +import com.virgilsecurity.keyknox.client.* +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.crypto.VirgilPublicKey +import com.virgilsecurity.sdk.jwt.contract.AccessTokenProvider +import java.net.URL + +/** + * CloudTicketStorage + */ +class CloudTicketStorage( + accessTokenProvider: AccessTokenProvider, + private val keyStorageLocal: KeyStorageLocal, + baseUrl: String? = Const.VIRGIL_BASE_URL +) { + + private val identity: String get() = keyStorageLocal.identity + private val keyknoxManager: KeyknoxManager + + init { + val keyknoxClient = KeyknoxClient(accessTokenProvider, URL(baseUrl)) + this.keyknoxManager = KeyknoxManager(keyknoxClient) + } + + internal fun store(ticket: Ticket, cards: Collection) { + val selfKeyPair = keyStorageLocal.load() + + val groupMessage = ticket.groupMessage + + val sessionId = groupMessage.sessionId.toHexString() + val epoch = groupMessage.epoch + val ticketData = groupMessage.serialize() + + val identities = cards.map { it.identity } + val publicKeys = cards.map { it.publicKey } + + val params = KeyknoxPushParams(identities + this.identity, + GROUP_SESSION_ROOT, + sessionId, + "\\$epoch") + + keyknoxManager.pushValue(params, + ticketData, + null, + publicKeys + selfKeyPair.publicKey, + selfKeyPair.privateKey) + } + + internal fun retrieve(sessionId: Data, + identity: String, + identityPublicKey: VirgilPublicKey): List { + val selfKeyPair = keyStorageLocal.load() + + val sessionIdHex = sessionId.toHexString() + + val getParams = KeyknoxGetKeysParams(identity, + GROUP_SESSION_ROOT, + sessionIdHex) + + val epochs = keyknoxManager.getKeys(getParams) + + val tickets = mutableListOf() + for (epoch in epochs) { + val pullParams = KeyknoxPullParams(identity, + GROUP_SESSION_ROOT, + sessionIdHex, + epoch) + val response = keyknoxManager.pullValue(pullParams, + listOf(identityPublicKey), + selfKeyPair.privateKey) + + val groupMessage = GroupSessionMessage.deserialize(response.value) + val participants = response.identities.toSet() + val ticket = Ticket(groupMessage, participants) + + tickets.add(ticket) + } + + return tickets + } + + internal fun addRecipients(cards: Collection, sessionId: Data) { + val selfKeyPair = keyStorageLocal.load() + + val sessionIdHex = sessionId.toHexString() + + val identities = cards.map { it.identity } + val publicKeys = cards.map { it.publicKey } + + val getParams = KeyknoxGetKeysParams(this.identity, + GROUP_SESSION_ROOT, + sessionIdHex) + + val epochs = keyknoxManager.getKeys(getParams) + + for (epoch in epochs) { + val pullParams = KeyknoxPullParams(this.identity, + GROUP_SESSION_ROOT, + sessionIdHex, + epoch) + val response = keyknoxManager.pullValue(pullParams, + listOf(selfKeyPair.publicKey), + selfKeyPair.privateKey) + + val pushParams = KeyknoxPushParams(identities, + GROUP_SESSION_ROOT, + sessionIdHex, + epoch) + + keyknoxManager.pushValue(pushParams, + response.value, + response.keyknoxHash, + publicKeys + selfKeyPair.publicKey, + selfKeyPair.privateKey) + } + } + + internal fun reAddRecipient(card: Card, sessionId: Data) { + val selfKeyPair = keyStorageLocal.load() + + val path = sessionId.toHexString() + + val getParams = KeyknoxGetKeysParams(this.identity, + GROUP_SESSION_ROOT, + path) + + val epochs = keyknoxManager.getKeys(getParams) + + for (epoch in epochs) { + val pullParams = KeyknoxPullParams(this.identity, + GROUP_SESSION_ROOT, + path, + epoch) + + val response = keyknoxManager.pullValue(pullParams, + listOf(selfKeyPair.publicKey), + selfKeyPair.privateKey) + + removeRecipient(card.identity, sessionId, epoch) + + val pushParams = KeyknoxPushParams(listOf(card.identity), + GROUP_SESSION_ROOT, + path, + epoch) + + keyknoxManager.pushValue(pushParams, + response.value, + response.keyknoxHash, + listOf(card.publicKey, selfKeyPair.publicKey), + selfKeyPair.privateKey) + } + } + + internal fun removeRecipient(identity: String, sessionId: Data, epoch: String? = null) { + val sessionIdHex = sessionId.toHexString() + + val params = KeyknoxDeleteRecipientParams(identity, + GROUP_SESSION_ROOT, + sessionIdHex, + epoch) + + keyknoxManager.deleteRecipient(params) + } + + internal fun delete(sessionId: Data) { + val sessionIdHex = sessionId.toHexString() + + val params = KeyknoxResetParams(GROUP_SESSION_ROOT, sessionIdHex, null) + + keyknoxManager.resetValue(params) + } + + companion object { + private const val GROUP_SESSION_ROOT = "group-sessions" + } +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt new file mode 100644 index 00000000..dcdbf4f6 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.storage.local + +import com.virgilsecurity.android.common.exception.FileGroupStorageException +import com.virgilsecurity.android.common.exception.RawGroupException +import com.virgilsecurity.android.common.model.GroupInfo +import com.virgilsecurity.android.common.model.RawGroup +import com.virgilsecurity.android.common.model.Ticket +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.common.util.toHexString +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.crypto.VirgilKeyPair +import com.virgilsecurity.sdk.storage.FileSystem +import com.virgilsecurity.sdk.storage.FileSystemEncrypted +import com.virgilsecurity.sdk.storage.FileSystemEncryptedCredentials +import com.virgilsecurity.sdk.storage.exceptions.DirectoryNotExistsException +import com.virgilsecurity.sdk.utils.ConvertionUtils +import java.io.File + +/** + * GroupStorageFile + */ +class GroupStorageFile internal constructor( + internal val identity: String, + crypto: VirgilCrypto, + identityKeyPair: VirgilKeyPair, + rootPath: String +) { // TODO use internal everywhere possible + + private val fileSystemEncrypted: FileSystem + + init { + val credentials = FileSystemEncryptedCredentials(crypto, identityKeyPair) + val fullPath: String = rootPath + + File.separator + + ConvertionUtils.toHex(identityKeyPair.privateKey.identifier) + + File.separator + + STORAGE_POSTFIX_E3KIT + + File.separator + + STORAGE_POSTFIX_GROUPS + + fileSystemEncrypted = FileSystemEncrypted(fullPath, credentials) + } + + internal fun store(group: RawGroup) { + val ticket = group.tickets.lastOrNull() ?: throw RawGroupException("Empty tickets") + + val subdir = ticket.groupMessage.sessionId.toHexString() + + store(group.info, subdir) + group.tickets.forEach { + store(it, subdir) + } + } + + private fun store(ticket: Ticket, subdir: String) { + val subdir = "$subdir/$TICKETS_SUBDIR" + val name = ticket.groupMessage.epoch.toString() + + val data = ticket.serialize() + fileSystemEncrypted.write(data, name, subdir) + } + + private fun store(info: GroupInfo, subdir: String) { + val data = info.serialize() + fileSystemEncrypted.write(data, GROUP_INFO_NAME, subdir) + } + + internal fun retrieveInfo(sessionId: Data): GroupInfo? = retrieveGroupInfo(sessionId) + + internal fun retrieve(sessionId: Data, count: Int): RawGroup? { + val tickets = retrieveLastTickets(count, sessionId) + val groupInfo = retrieveGroupInfo(sessionId) ?: return null + + return RawGroup(groupInfo, tickets) + } + + internal fun retrieve(sessionId: Data, epoch: Long): RawGroup? { + val ticket = retrieveTicket(sessionId, epoch) + val groupInfo = retrieveGroupInfo(sessionId) ?: return null + + return RawGroup(groupInfo, listOf(ticket)) + } + + internal fun delete(sessionId: Data) = fileSystemEncrypted.deleteDirectory(sessionId.toHexString()) + + internal fun reset() = fileSystemEncrypted.delete() + + private fun retrieveTicket(sessionId: Data, epoch: Long): Ticket { + val subdir = sessionId.toHexString() + File.separator + TICKETS_SUBDIR + val name = epoch.toString() + + val data = fileSystemEncrypted.read(name, subdir) + + return Ticket.deserialize(data) + } + + private fun retrieveLastTickets(count: Int, sessionId: Data): List { + val result = mutableListOf() + + val subdir = sessionId.toHexString() + File.separator + TICKETS_SUBDIR + + var epochs = listOf() + try { + epochs = fileSystemEncrypted + .listFiles(subdir).map { name -> + try { + name.toLong() + } catch (exception: NumberFormatException) { + throw FileGroupStorageException("Invalid file name") + } + } + .sorted() + .takeLast(count) + } + catch (e: DirectoryNotExistsException) { + // No tickets for this session + } + + epochs.forEach { epoch -> + try { + retrieveTicket(sessionId, epoch) + } catch (throwable: Throwable) { + throw FileGroupStorageException("File is empty") + }.also { ticket -> + result.add(ticket) + } + } + + return result + } + + private fun retrieveGroupInfo(sessionId: Data): GroupInfo? { + val subdir = sessionId.toHexString() + + if (!fileSystemEncrypted.exists(GROUP_INFO_NAME, subdir)) { + return null + } + + val data = fileSystemEncrypted.read(GROUP_INFO_NAME, subdir) + + return GroupInfo.deserialize(data) + } + + companion object { + private const val GROUP_INFO_NAME = "GROUP_INFO" + private const val TICKETS_SUBDIR = "TICKETS" + private const val STORAGE_POSTFIX_E3KIT = "VIRGIL-E3KIT" + private const val STORAGE_POSTFIX_GROUPS = "GROUPS" + } +} diff --git a/ethree-kotlin/src/main/java/com/virgilsecurity/android/ethree/interaction/KeyManagerLocalDefault.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt similarity index 63% rename from ethree-kotlin/src/main/java/com/virgilsecurity/android/ethree/interaction/KeyManagerLocalDefault.kt rename to ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt index b61d5aea..bf2d3aed 100644 --- a/ethree-kotlin/src/main/java/com/virgilsecurity/android/ethree/interaction/KeyManagerLocalDefault.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt @@ -31,32 +31,36 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethree.interaction +package com.virgilsecurity.android.common.storage.local -import android.content.Context -import com.virgilsecurity.android.common.interaction.KeyManagerLocal -import com.virgilsecurity.sdk.storage.DefaultKeyStorage +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.crypto.VirgilKeyPair +import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryNotFoundException import com.virgilsecurity.sdk.storage.JsonKeyEntry -import com.virgilsecurity.sdk.storage.KeyEntry import com.virgilsecurity.sdk.storage.KeyStorage /** - * KeyManagerLocalDefault + * Local KeyStorage. */ -class KeyManagerLocalDefault(val identity: String, context: Context) : KeyManagerLocal { +class KeyStorageLocal( + val identity: String, + private val keyStorage: KeyStorage, + private val crypto: VirgilCrypto +) { - private val keyStorage: KeyStorage = DefaultKeyStorage(context.filesDir.absolutePath, - KEYSTORE_NAME) + fun exists() = keyStorage.exists(identity) - override fun exists() = keyStorage.exists(identity) + fun store(privateKeyData: Data) = + keyStorage.store(JsonKeyEntry(identity, privateKeyData.data)) - override fun store(privateKey: ByteArray) = keyStorage.store(JsonKeyEntry(identity, privateKey)) - - override fun load(): KeyEntry = keyStorage.load(identity) - - override fun delete() = keyStorage.delete(identity) - - companion object { - private const val KEYSTORE_NAME = "virgil.keystore" + fun load(): VirgilKeyPair = try { + val privateKeyData = keyStorage.load(identity) + crypto.importPrivateKey(privateKeyData.value) + } catch (e: KeyEntryNotFoundException) { + throw PrivateKeyNotFoundException("No private key on device. You should call register() or retrievePrivateKey()") } + + fun delete() = keyStorage.delete(identity) } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt new file mode 100644 index 00000000..a021a108 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.storage.sql + +import androidx.room.Database +import androidx.room.RoomDatabase +import com.virgilsecurity.android.common.storage.sql.dao.CardDao +import com.virgilsecurity.android.common.storage.sql.model.CardEntity + +@Database(entities = arrayOf(CardEntity::class), version = 1) +abstract class ETheeDatabase: RoomDatabase() { + abstract fun cardDao(): CardDao +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt new file mode 100644 index 00000000..29ce7d6b --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.storage.sql + +import android.content.Context +import androidx.room.Room +import com.virgilsecurity.android.common.exception.EmptyIdentitiesStorageException +import com.virgilsecurity.android.common.exception.InconsistentCardStorageException +import com.virgilsecurity.android.common.storage.CardStorage +import com.virgilsecurity.android.common.storage.sql.model.CardEntity +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.cards.CardManager +import com.virgilsecurity.sdk.cards.validation.CardVerifier +import com.virgilsecurity.sdk.crypto.VirgilCardCrypto +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider +import com.virgilsecurity.sdk.utils.ConvertionUtils + +/** + * SQL-based Virgil Cards storage. + */ +class SQLCardStorage(context: Context, + userIdentifier: String, + crypto: VirgilCrypto, + verifier: CardVerifier, + database: ETheeDatabase? = null) : CardStorage { + + private val db: ETheeDatabase + private val cardManager: CardManager + + init { + + if (database == null) { + val dbName = String.format("ethree-database-%s", userIdentifier) + this.db = Room.databaseBuilder( + context, + ETheeDatabase::class.java, dbName + ).build() + } else { + db = database + } + + val tokenProvider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback(function = { + return@RenewJwtCallback null + })) + cardManager = CardManager(VirgilCardCrypto(crypto), tokenProvider, verifier) + } + + override fun storeCard(card: Card) { + var currentCard: Card? = card + var previousCardId: String? = null + var isOutdated = card.isOutdated + while (currentCard != null) { + //TODO make CardManager.exportCardAsJson static and use it instead of serializeToJson + val cardEntity = CardEntity(currentCard.identifier, currentCard.identity, isOutdated, + ConvertionUtils.serializeToJson(currentCard.rawCard)) + db.cardDao().insert(cardEntity) + + previousCardId = currentCard.previousCardId + currentCard = currentCard.previousCard + isOutdated = true + } + if (previousCardId != null) { + db.cardDao().markOutdatedById(previousCardId) + } + } + + override fun getCard(cardId: String): Card? { + val cardEntity = db.cardDao().load(cardId) ?: return null + + val card = cardManager.importCardAsJson(cardEntity.card) + card.isOutdated = cardEntity.isOutdated + + if (cardId != card.identifier) { + throw InconsistentCardStorageException() + } + + return card + } + + override fun searchCards(identities: List): List { + if (identities.isEmpty()) { + throw EmptyIdentitiesStorageException() + } + + val cards = mutableListOf() + val entities = db.cardDao().loadAllByIdentity(identities) + + for (entity in entities) { + val card = cardManager.importCardAsJson(entity.card) + cards.add(card) + } + + val result = mutableListOf() + for (card in cards) { + if (card.identity !in identities) { + throw InconsistentCardStorageException("Got wrong card from SQL storage") + } + val nextCard = cards.firstOrNull() { it.previousCardId == card.identifier } + if (nextCard != null) { + nextCard.previousCard = card + card.isOutdated = true + continue + } + result.add(card) + } + return result + } + + override fun getNewestCardIds(): List { + return db.cardDao().getNewestCardIds() + } + + override fun reset() { + db.cardDao().deleteAll() + } + +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt new file mode 100644 index 00000000..8eb13c70 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.storage.sql.dao + +import androidx.room.* +import com.virgilsecurity.android.common.storage.sql.model.CardEntity + +@Dao +interface CardDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insert(card: CardEntity) + + @Query("SELECT * FROM ethree_cards WHERE id = :cardId LIMIT 1") + fun load(cardId: String): CardEntity? + + @Query("SELECT * FROM ethree_cards WHERE identity IN (:identities)") + fun loadAllByIdentity(identities: List): List + + @Query("SELECT id FROM ethree_cards WHERE is_outdated = 0") + fun getNewestCardIds(): List + + @Query("DELETE FROM ethree_cards") + fun deleteAll() + + @Query("UPDATE ethree_cards SET is_outdated = 1 WHERE id = :cardId") + fun markOutdatedById(cardId: String) + + @Query("UPDATE ethree_cards SET is_outdated = :isOutdated WHERE id = :cardId") + fun setOutdatedById(cardId: String, isOutdated: Boolean) +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt new file mode 100644 index 00000000..020cdf8a --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.storage.sql.model + +import androidx.annotation.NonNull +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity(tableName = "ethree_cards", + indices = arrayOf(Index(value = ["id"], unique = true), + Index(value = ["identity"], unique = false))) +data class CardEntity( + @PrimaryKey @ColumnInfo(name = "id") val identifier: String, + @ColumnInfo(name = "identity") @NonNull val identity: String, + @ColumnInfo(name = "is_outdated") @NonNull val isOutdated: Boolean, + @ColumnInfo(name = "card") @NonNull val card: String +) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/Const.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/util/Const.kt similarity index 97% rename from ethree-common/src/main/java/com/virgilsecurity/android/common/Const.kt rename to ethree-common/src/main/java/com/virgilsecurity/android/common/util/Const.kt index 994afd47..574682b2 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/Const.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/util/Const.kt @@ -31,7 +31,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.common +package com.virgilsecurity.android.common.util /** * Const diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt new file mode 100644 index 00000000..1cb7824f --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.worker + +import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException +import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.common.model.Completable +import com.virgilsecurity.sdk.cards.CardManager +import com.virgilsecurity.sdk.crypto.VirgilKeyPair + +/** + * AuthorizationWorker + */ +internal class AuthorizationWorker( + private val cardManager: CardManager, + private val keyStorageLocal: KeyStorageLocal, + private val identity: String, + private val publishCardThenSaveLocal: (VirgilKeyPair?, String?) -> Unit, + private val privateKeyDeleted: () -> Unit +) { + + /** // TODO check throws for all functions + * Publishes Card on Virgil Cards Service and saves Private Key in local storage. + * + * To start execution of the current function, please see [Completable] description. + * + * @param keyPair `VirgilKeyPair` to publish Card with. Will generate if not specified. + */ + @Synchronized + @JvmOverloads + internal fun register(keyPair: VirgilKeyPair? = null) = object : Completable { + override fun execute() { + if (keyStorageLocal.exists()) + throw EThreeException("Private key already exists in local key storage") + + val cards = cardManager.searchCards(this@AuthorizationWorker.identity) + if (cards.isNotEmpty()) throw EThreeException("User is already registered") + + publishCardThenSaveLocal(keyPair, null) + } + } + + /** + * Revokes Card from Virgil Cards Service, deletes Private Key from local storage + * + * To start execution of the current function, please see [Completable] description. + */ + @Synchronized internal fun unregister() = object : Completable { + override fun execute() { + val cards = cardManager.searchCards(this@AuthorizationWorker.identity) + val card = cards.firstOrNull() ?: throw EThreeException("User is not registered") + + cardManager.revokeCard(card.identifier) + keyStorageLocal.delete() + privateKeyDeleted() + } + } + + /** + * Generates new Private Key, publishes new Card to replace the current one on Virgil Cards + * Service and saves new Private Key in local storage + * + * To start execution of the current function, please see [Completable] description. + */ + @Synchronized internal fun rotatePrivateKey() = object : Completable { + override fun execute() { + if (keyStorageLocal.exists()) + throw EThreeException("Private key already exists in local key storage.") + + val cards = cardManager.searchCards(this@AuthorizationWorker.identity) + val card = cards.firstOrNull() ?: throw EThreeException("User is not registered") + + publishCardThenSaveLocal(null, card.identifier) + } + } + + /** + * Checks existence of private key in local key storage. + * Returns *true* if the key is present in the local key storage otherwise *false*. + */ + internal fun hasLocalPrivateKey() = keyStorageLocal.exists() + + /** + * ! *WARNING* ! If you call this function after [register] without using [backupPrivateKey] + * then you will loose private key permanently, as well you won't be able to use identity that + * was used with that private key no more. + * + * Deletes Private Key from local storage, cleans local cards storage. + * + * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] // TODO check exception type + * exception will be thrown. + */ + internal fun cleanup() { + keyStorageLocal.delete() + privateKeyDeleted() + } +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt new file mode 100644 index 00000000..27b12c3d --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.worker + +import com.virgilsecurity.android.common.exception.* +import com.virgilsecurity.android.common.storage.cloud.CloudKeyManager +import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.common.model.Completable +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.keyknox.exception.EntryAlreadyExistsException +import com.virgilsecurity.keyknox.exception.EntryNotFoundException +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryAlreadyExistsException + +/** + * BackupWorker + */ +internal class BackupWorker( + private val keyStorageLocal: KeyStorageLocal, + private val keyManagerCloud: CloudKeyManager, + private val privateKeyChanged: (Card?) -> Unit +) { + + /** + * Encrypts the user's private key using the user's [password] and backs up the encrypted + * private key to Virgil's cloud. This enables users to log in from other devices and have + * access to their private key to decrypt data. + * + * Encrypts loaded from private keys local storage user's *Private key* using *Public key* + * that is generated based on provided [password] after that backs up encrypted user's + * *Private key* to the Virgil's cloud storage. + * + * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws PrivateKeyNotFoundException + * @throws BackupKeyException + */ + internal fun backupPrivateKey(password: String): Completable = object : Completable { + override fun execute() { + try { + val identityKeyPair = keyStorageLocal.load() + keyManagerCloud.store(identityKeyPair.privateKey, password) + } + catch (e: EntryAlreadyExistsException) { + throw BackupKeyException("Can't backup private key", e) + } + } + } + + /** + * Pulls user's private key from the Virgil's cloud, decrypts it with *Private key* that + * is generated based on provided [password] and saves it to the current private keys + * local storage. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws WrongPasswordException + * @throws RestoreKeyException + */ + internal fun restorePrivateKey(password: String): Completable = object : Completable { + override fun execute() { + try { + val entry = keyManagerCloud.retrieve(password) + keyStorageLocal.store(Data(entry.data)) + privateKeyChanged(null) + } + catch (e: KeyEntryAlreadyExistsException) { + throw RestoreKeyException("Can't restore private key", e) + } + } + } + + /** + * Changes the password on a backed-up private key. + * + * Pulls user's private key from the Virgil's cloud storage, decrypts it with *Private key* + * that is generated based on provided [oldPassword] after that encrypts user's *Private key* + * using *Public key* that is generated based on provided [newPassword] and pushes encrypted + * user's *Private key* to the Virgil's cloud storage. + * + * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws PrivateKeyNotFoundException + */ + internal fun changePassword(oldPassword: String, + newPassword: String): Completable = object : Completable { + override fun execute() { + if (oldPassword == newPassword) throw EThreeException("To change password, please" + + "provide new password that " + + "differs from the old one.") + keyManagerCloud.changePassword(oldPassword, newPassword) + } + } + + /** + * Deletes Private Key stored on Virgil's cloud. This will disable user to log in from + * other devices. + * + * Deletes private key backup using specified [password] and provides [onCompleteListener] + * callback that will notify you with successful completion or with a [Throwable] if + * something went wrong. + * + * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * To start execution of the current function, please see [Completable] description. + * + * @throws PrivateKeyNotFoundException + * @throws WrongPasswordException + */ + @JvmOverloads + internal fun resetPrivateKeyBackup(password: String? = null): Completable = object : Completable { + override fun execute() { + if (password != null) + try { + keyManagerCloud.delete(password) + } + catch (e: EntryNotFoundException) { + throw PrivateKeyNotFoundException("Can't reset private key: private key not found", e) + } + else + keyManagerCloud.deleteAll() + } + + } +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt new file mode 100644 index 00000000..5325c850 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.worker + +import com.virgilsecurity.android.common.exception.GroupNotFoundException +import com.virgilsecurity.android.common.manager.GroupManager +import com.virgilsecurity.android.common.model.FindUsersResult +import com.virgilsecurity.android.common.model.Group +import com.virgilsecurity.android.common.model.Ticket +import com.virgilsecurity.common.model.Completable +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.common.model.Result +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import java.nio.charset.StandardCharsets + +/** + * GroupWorker + */ +internal class GroupWorker( + private val identity: String, + private val crypto: VirgilCrypto, + private val getGroupManager: () -> GroupManager, + private val computeSessionId: (Data) -> Data +) { + + /** + * Creates group, saves in cloud and locally. + * + * @param identifier Identifier of group. Should be *> 10* length. + * @param users Cards of participants. Result of findUsers call. + * + * @return New [Group]. + */ + internal fun createGroup(identifier: Data, users: FindUsersResult): Result = + object : Result { + override fun get(): Group { + val sessionId = computeSessionId(identifier) + val participants = users.keys + identity + + Group.validateParticipantsCount(participants.size) + + val ticket = Ticket(crypto, sessionId, participants) + + return getGroupManager().store(ticket, users.values.toList()) + } + } + + /** + * Returns cached local group. + * + * @param identifier Identifier of group. Should be *> 10* length. + * + * @return [Group] if exists, null otherwise. + */ + internal fun getGroup(identifier: Data): Group? { + val sessionId = computeSessionId(identifier) + return getGroupManager().retrieve(sessionId) + } + + /** + * Loads group from cloud, saves locally. + * + * @param identifier Identifier of group. Should be *> 10* length. + * @param card Card of group initiator. + * + * @return Loaded [Group]. + */ + internal fun loadGroup(identifier: Data, card: Card): Result = + object : Result { + override fun get(): Group { + val sessionId = computeSessionId(identifier) + return getGroupManager().pull(sessionId, card) + } + } + + /** + * Deletes group from cloud and local storage. + * + * @param identifier Identifier of group. Should be *> 10* length. + */ + internal fun deleteGroup(identifier: Data): Completable = + object : Completable { + override fun execute() { + val sessionId = computeSessionId(identifier) + val group = getGroupManager().retrieve(sessionId) + ?: throw GroupNotFoundException("Group with provided id not found " + + "locally. Try to call loadGroup first") + + group.checkPermissions() + + getGroupManager().delete(sessionId) + } + } + + /** + * Creates group, saves in cloud and locally. + * + * @param identifier Identifier of group. Should be *> 10* length. + * @param users Cards of participants. Result of findUsers call. + * + * @return New [Group]. + */ + internal fun createGroup(identifier: String, users: FindUsersResult): Result { + val identifierData = Data(identifier.toByteArray(StandardCharsets.UTF_8)) + + return createGroup(identifierData, users) + } + + /** + * Returns cached local group. + * + * @param identifier Identifier of group. Should be *> 10* length. + * + * @return [Group] if exists, null otherwise. + */ + internal fun getGroup(identifier: String): Group? { + val identifierData = Data(identifier.toByteArray(StandardCharsets.UTF_8)) + + return getGroup(identifierData) + } + + /** + * Loads group from cloud, saves locally. + * + * @param identifier Identifier of group. Should be *> 10* length. + * @param card Card of group initiator. + * + * @return Loaded [Group]. + */ + internal fun loadGroup(identifier: String, card: Card): Result { + val identifierData = Data(identifier.toByteArray(StandardCharsets.UTF_8)) + + return loadGroup(identifierData, card) + } + + /** + * Deletes group from cloud and local storage. + * + * @param identifier Identifier of group. Should be *> 10* length. + */ + internal fun deleteGroup(identifier: String): Completable { + val identifierData = Data(identifier.toByteArray(StandardCharsets.UTF_8)) + + return deleteGroup(identifierData) + } +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt new file mode 100644 index 00000000..994cf503 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.worker + +import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException +import com.virgilsecurity.android.common.model.FindUsersResult +import com.virgilsecurity.android.common.model.LookupResult +import com.virgilsecurity.android.common.model.toPublicKeys +import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.crypto.VirgilPublicKey +import com.virgilsecurity.sdk.crypto.exceptions.SignatureIsNotValidException +import com.virgilsecurity.sdk.crypto.exceptions.VerificationException +import com.virgilsecurity.sdk.exception.EmptyArgumentException +import java.io.InputStream +import java.io.OutputStream +import java.nio.charset.StandardCharsets +import java.util.* + +/** + * PeerToPeerWorker + */ +internal class PeerToPeerWorker( + private val keyStorageLocal: KeyStorageLocal, + private val crypto: VirgilCrypto +) { + + /** + * Signs then encrypts data for group of users. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param data Data to encrypt. + * @param users Result of findUsers call recipient Cards with Public Keys to sign and encrypt + * with. Use null to sign and encrypt for self. + * + * @return Encrypted Data. + */ + @JvmOverloads internal fun encrypt(data: Data, users: FindUsersResult? = null): Data = + encryptInternal(data, users?.map { it.value.publicKey }) + + /** + * Decrypts and verifies data from users. + * + * *Important* Requires private key in local storage. + * + * @param data Data to decrypt. + * @param user Sender Card with Public Key to verify with. Use null to decrypt and verify. + * from self. + * + * @return Decrypted Data. + */ + @JvmOverloads internal fun decrypt(data: Data, user: Card? = null): Data = + decryptInternal(data, user?.publicKey) + + /** + * Decrypts and verifies data from users. + * + * *Important* Requires private key in local storage. + * + * @param data Data to decrypt. + * @param user Sender Card with Public Key to verify with. + * @param date Date of encryption to use proper card version. + * + * @return Decrypted Data. + */ + internal fun decrypt(data: Data, user: Card, date: Date): Data { + var card = user + + while (card.previousCard != null) { // TODO test it with new card + if (card.createdAt <= date) { + break + } + + card = card.previousCard + } + + return decryptInternal(data, card.publicKey) + } + + /** + * Encrypts data stream. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param inputStream Data stream to be encrypted. + * @param outputStream Stream with encrypted data. + * @param users Result of findUsers call recipient Cards with Public Keys to sign and encrypt + * with. Use null to sign and encrypt for self. + */ + @JvmOverloads internal fun encrypt(inputStream: InputStream, + outputStream: OutputStream, + users: FindUsersResult? = null) = + encryptInternal(inputStream, outputStream, users?.map { it.value.publicKey }) + + /** + * Decrypts encrypted stream. + * + * *Important* Requires private key in local storage. + * + * @param inputStream Stream with encrypted data. + * @param outputStream Stream with decrypted data. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + internal fun decrypt(inputStream: InputStream, outputStream: OutputStream) { + if (inputStream.available() == 0) throw EmptyArgumentException("inputStream") + + val selfKeyPair = keyStorageLocal.load() + + crypto.decrypt(inputStream, outputStream, selfKeyPair.privateKey) + } + + /** + * Signs then encrypts string for group of users. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param text String to encrypt. String should be *UTF-8* encoded. + * @param users Result of findUsers call recipient Cards with Public Keys to sign and encrypt + * with. Use null to sign and encrypt for self. + * + * @return Encrypted base64String. + */ + @JvmOverloads internal fun encrypt(text: String, users: FindUsersResult? = null): String { + require(text.isNotEmpty()) { "\'text\' should not be empty" } + if (users != null) require(users.isNotEmpty()) { "Passed empty FindUsersResult" } + + val data = Data(text.toByteArray(StandardCharsets.UTF_8)) // TODO check exception type and wrap with "String to Data failed" message + return encrypt(data, users).toBase64String() + } + + /** + * Decrypts and verifies base64 string from users. + * + * *Important* Requires private key in local storage. + * + * @param text Encrypted String. + * @param user Sender Card with Public Key to verify with. Use null to decrypt and verify + * from self. + * + * @return Decrypted String. + */ + @JvmOverloads internal fun decrypt(text: String, user: Card? = null): String { + require(text.isNotEmpty()) { "\'text\' should not be empty" } + + val data = try { + Data.fromBase64String(text) + } catch (exception: IllegalArgumentException) { + throw EThreeException("Error while converting String to Data. Wrong base64 String.") + } + + val decryptedData = decrypt(data, user) + + return String(decryptedData.data, StandardCharsets.UTF_8) + } + + /** + * Decrypts and verifies base64 string from users. + * + * *Important* Requires private key in local storage. + * + * @param text Encrypted String. + * @param user Sender Card with Public Key to verify with. + * @param date Date of encryption to use proper card version. + * + * @return Decrypted String. + */ + internal fun decrypt(text: String, user: Card, date: Date): String { + require(text.isNotEmpty()) { "\'text\' should not be empty" } + + val data = try { + Data.fromBase64String(text) + } catch (exception: IllegalArgumentException) { + throw EThreeException("Error while converting String to Data. Wrong base64 String.") + } + + val decryptedData = decrypt(data, user, date) + + return String(decryptedData.data, StandardCharsets.UTF_8) + } + + /** + * Signs and encrypts data for user. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * @param data Data to encrypt. + * @param user User Card to encrypt for. + * + * @return Encrypted data. + */ + internal fun encrypt(data: Data, user: Card): Data = + encrypt(data, FindUsersResult(mutableMapOf(user.identity to user))) + + /** + * Signs and encrypts string for user. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * @param text String to encrypt. + * @param user User Card to encrypt for. + * + * @return Encrypted String. + */ + internal fun encrypt(text: String, user: Card): String = + encrypt(text, FindUsersResult(mutableMapOf(user.identity to user))) + + /** + * Encrypts data stream. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * @param inputStream Data stream to be encrypted. + * @param outputStream Stream with encrypted data. + * @param user User Card to encrypt for. + */ + internal fun encrypt(inputStream: InputStream, outputStream: OutputStream, user: Card) = + encrypt(inputStream, outputStream, FindUsersResult(mutableMapOf(user.identity to user))) + + internal fun encryptInternal(inputStream: InputStream, + outputStream: OutputStream, + publicKeys: List?) { + val selfKeyPair = keyStorageLocal.load() + val pubKeys = mutableListOf(selfKeyPair.publicKey) + + if (publicKeys != null) { + if (publicKeys.isEmpty()) { + throw EThreeException("Passed empty FindUsersResult") + } + + pubKeys += publicKeys + } + + crypto.encrypt(inputStream, outputStream, pubKeys) + } + + internal fun encryptInternal(data: Data, + publicKeys: List?): Data { // TODO check for empty/null args + require(data.data.isNotEmpty()) { "\'data\' should not be empty." } + + val selfKeyPair = keyStorageLocal.load() + val pubKeys = mutableListOf(selfKeyPair.publicKey) + + if (publicKeys != null) { + if (publicKeys.isEmpty()) + throw EThreeException("Passed empty FindUsersResult") + + pubKeys += publicKeys + } + + return Data(crypto.signThenEncrypt(data.data, selfKeyPair.privateKey, pubKeys)) + } + + internal fun decryptInternal(data: Data, publicKey: VirgilPublicKey?): Data { + require(data.data.isNotEmpty()) { "\'data\' should not be empty." } + + val selfKeyPair = keyStorageLocal.load() + val pubKey = publicKey ?: selfKeyPair.publicKey + + return try { + Data(crypto.decryptThenVerify(data.data, selfKeyPair.privateKey, pubKey)) + } catch (exception: Throwable) { + when (exception) { + is SignatureIsNotValidException, is VerificationException -> { // TODO test this case + throw EThreeException("Verification of message failed. This may be caused by " + + "rotating sender key. Try finding new one") + } + else -> throw exception + } + } + } + + // Backward compatibility deprecated methods -------------------------------------------------- + + /** + * Signs then encrypts data for a group of users. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param text String to encrypt. + * @param lookupResult Result of lookupPublicKeys call recipient PublicKeys to sign and + * encrypt with. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + @Deprecated("Use encryptForUsers method instead.") // TODO change to actual fun name + internal fun encrypt(text: String, lookupResult: LookupResult): String { + val data = Data(text.toByteArray(StandardCharsets.UTF_8)) // TODO check exception type + + return encryptInternal(data, lookupResult.toPublicKeys()).toBase64String() + } + + /** + * Signs then encrypts data for a group of users. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param data Data to encrypt + * @param lookupResult Result of lookupPublicKeys call recipient PublicKeys to sign and + * encrypt with. + * + * @return Encrypted Data. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + @Deprecated("Use encryptForUsers method instead.") + @JvmOverloads internal fun encrypt(data: ByteArray, + lookupResult: LookupResult? = null): ByteArray = + encryptInternal(Data(data), lookupResult.toPublicKeys()).data + + /** + * Encrypts data stream for a group of users. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param inputStream Data stream to be encrypted. + * @param outputStream Stream with encrypted data. + * @param lookupResult Result of lookupPublicKeys call recipient PublicKeys to sign and + * encrypt with. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + @Deprecated("Use encryptForUsers method instead.") // TODO change to actual methods signature + internal fun encrypt(inputStream: InputStream, + outputStream: OutputStream, + lookupResult: LookupResult) = + encryptInternal(inputStream, outputStream, lookupResult.toPublicKeys()) + + /** + * Decrypts and verifies encrypted text that is in base64 [String] format. + * + * *Important* Automatically includes self key to recipientsKeys. + * + * *Important* Requires private key in local storage. + * + * *Note* Avoid key duplication. + * + * @param base64String Encrypted String. + * @param sendersKey Sender PublicKey to verify with. + * + * @return Decrypted String. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + @Deprecated("Use decryptFromUser method instead.") + internal fun decrypt(base64String: String, sendersKey: VirgilPublicKey): String { + val data = try { + Data.fromBase64String(base64String) + } catch (exception: IllegalArgumentException) { + throw EThreeException("Error while converting String to Data. Wrong base64 String.") + } + + val decryptedData = decryptInternal(data, sendersKey) + + return String(decryptedData.data, StandardCharsets.UTF_8) + } + + /** + * Decrypts and verifies encrypted data. + * + * *Important* Requires private key in local storage. + * + * @param data Data to decrypt. + * @param sendersKey Sender PublicKey to verify with. + * + * @throws PrivateKeyNotFoundException + * @throws CryptoException + */ + @Deprecated("Use decryptFromUser method instead.") + @JvmOverloads internal fun decrypt(data: ByteArray, + sendersKey: VirgilPublicKey? = null): ByteArray = + decryptInternal(Data(data), sendersKey).data +} diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt new file mode 100644 index 00000000..b4e55d31 --- /dev/null +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.common.worker + +import com.virgilsecurity.android.common.EThreeCore +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException +import com.virgilsecurity.android.common.exception.PublicKeyDuplicateException +import com.virgilsecurity.android.common.exception.PublicKeyNotFoundException +import com.virgilsecurity.android.common.manager.LookupManager +import com.virgilsecurity.android.common.model.FindUsersResult +import com.virgilsecurity.android.common.model.LookupResult +import com.virgilsecurity.common.model.Result +import com.virgilsecurity.sdk.cards.Card + +/** + * SearchWorker + */ +internal class SearchWorker( + private val lookupManager: LookupManager +) { + + /** + * Returns cards from local storage for given [identities]. // TODO add Result/Completable reference in all fun's descriptions + * + * @param identities Identities. + * + * @return [FindUsersResult] with found users. + */ + internal fun findCachedUsers(identities: List): Result = + object : Result { + override fun get(): FindUsersResult { + return lookupManager.lookupCachedCards(identities) + } + } + + /** + * Returns card from local storage for given [identity]. + * + * @param identity Identity. + * + * @return [Card] if it exists, *null* otherwise. + */ + internal fun findCachedUser(identity: String): Result = object : Result { + override fun get(): Card? { + return lookupManager.lookupCachedCard(identity) + } + } + + /** + * Retrieves users Cards from the Virgil Cloud or local storage if exists. + * + * @param identities Array of identities to find. + * @param forceReload Will not use local cached cards if *true*. + * + * @return [FindUsersResult] with found users. + */ + internal fun findUsers(identities: List, + forceReload: Boolean = false): Result = + object : Result { + override fun get(): FindUsersResult { + return lookupManager.lookupCards(identities, forceReload) + } + } + + /** + * Retrieves user Card from the Virgil Cloud or local storage if exists. + * + * @param identity Identity to find. + * @param forceReload Will not use local cached card if *true*. + * + * @return [Card] that corresponds to provided [identity]. + */ + internal fun findUser(identity: String, + forceReload: Boolean = false): Result = object : Result { + override fun get(): Card { + return lookupManager.lookupCard(identity, forceReload) + } + } + + /** + * Retrieves user public key from the cloud for encryption/verification operations. + * + * Searches for public key with specified [identity] and returns map of [String] -> + * [PublicKey] in [onResultListener] callback or [Throwable] if something went wrong. + * + * [PublicKeyNotFoundException] will be thrown if public key wasn't found. + * + * Can be called only if private key is on the device, otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * @throws PrivateKeyNotFoundException + * @throws PublicKeyDuplicateException + */ + @Deprecated("Use findUser instead.") // TODO add replaceWith + internal fun lookupPublicKey(identity: String): Result = + lookupPublicKeys(listOf(identity)) + + /** + * Retrieves user public keys from the cloud for encryption/verification operations. + * + * Searches for public keys with specified [identities] and returns map of [String] -> + * [PublicKey] in [onResultListener] callback or [Throwable] if something went wrong. + * + * [PublicKeyNotFoundException] will be thrown for the first not found public key. + * [EThreeCore.register] + * + * Can be called only if private key is on the device, otherwise [PrivateKeyNotFoundException] + * exception will be thrown. + * + * To start execution of the current function, please see [Result] description. + * + * @throws PrivateKeyNotFoundException + * @throws PublicKeyDuplicateException + */ + @Deprecated("Use findUsers instead.") // TODO add replaceWith + internal fun lookupPublicKeys(identities: List): Result = + object : Result { + override fun get(): LookupResult { + require(identities.isNotEmpty()) { "\'identities\' should not be empty" } + + val cards = findUsers(identities, true).get() + return cards.mapValues { it.component2().publicKey } + } + } +} diff --git a/ethree-enclave/build.gradle b/ethree-enclave/build.gradle index 8c58036e..f1373a6a 100644 --- a/ethree-enclave/build.gradle +++ b/ethree-enclave/build.gradle @@ -33,23 +33,23 @@ plugins { id 'signing' + id 'maven-publish' } apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' -apply plugin: 'maven-publish' apply plugin: 'digital.wup.android-maven-publish' apply plugin: 'org.jetbrains.dokka' android { - compileSdkVersion 28 - buildToolsVersion "28.0.3" + compileSdkVersion androidOptions.compileSdkVersion + buildToolsVersion androidOptions.buildToolsVersion defaultConfig { - minSdkVersion 23 - targetSdkVersion 28 + minSdkVersion androidOptions.minSdkVersion + targetSdkVersion androidOptions.targetSdkVersion consumerProguardFiles 'proguard-rules.txt' } } @@ -62,7 +62,7 @@ dependencies { api project(':ethree-common') // Virgil - api "com.virgilsecurity.sdk:android-utils:6.0.1-SNAPSHOT" + implementation "com.virgilsecurity.sdk:android-utils:$versions.virgilSdk" // Kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin" diff --git a/ethree-enclave/src/main/AndroidManifest.xml b/ethree-enclave/src/main/AndroidManifest.xml index 605ef08a..6bb72213 100644 --- a/ethree-enclave/src/main/AndroidManifest.xml +++ b/ethree-enclave/src/main/AndroidManifest.xml @@ -31,5 +31,4 @@ ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> - + diff --git a/ethree-enclave/src/main/java/com/virgilsecurity/android/ethreeenclave/interaction/EThree.kt b/ethree-enclave/src/main/java/com/virgilsecurity/android/ethreeenclave/interaction/EThree.kt index 934bffdb..944186f7 100644 --- a/ethree-enclave/src/main/java/com/virgilsecurity/android/ethreeenclave/interaction/EThree.kt +++ b/ethree-enclave/src/main/java/com/virgilsecurity/android/ethreeenclave/interaction/EThree.kt @@ -34,45 +34,44 @@ package com.virgilsecurity.android.ethreeenclave.interaction import android.content.Context -import com.virgilsecurity.android.common.Const.NO_CONTEXT +import com.virgilsecurity.android.common.EThreeCore import com.virgilsecurity.android.common.callback.OnGetTokenCallback -import com.virgilsecurity.android.common.interaction.EThreeCore -import com.virgilsecurity.android.common.interaction.KeyManagerLocal -import com.virgilsecurity.android.common.model.Result +import com.virgilsecurity.android.common.callback.OnKeyChangedCallback +import com.virgilsecurity.android.common.util.Const.NO_CONTEXT +import com.virgilsecurity.common.model.Result import com.virgilsecurity.sdk.androidutils.storage.AndroidKeyEntry import com.virgilsecurity.sdk.androidutils.storage.AndroidKeyStorage import com.virgilsecurity.sdk.crypto.exceptions.KeyStorageException import com.virgilsecurity.sdk.jwt.Jwt import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider -import com.virgilsecurity.sdk.jwt.contract.AccessTokenProvider import com.virgilsecurity.sdk.storage.DefaultKeyStorage +import com.virgilsecurity.sdk.storage.KeyStorage /** * [EThree] class simplifies work with Virgil Services to easily implement End to End Encrypted * communication. */ class EThree -private constructor( +@JvmOverloads constructor( + identity: String, + tokenCallback: OnGetTokenCallback, context: Context, - tokenProvider: AccessTokenProvider, - alias: String, - isAuthenticationRequired: Boolean, - keyValidityDuration: Int -) : EThreeCore(tokenProvider) { + alias: String = "VirgilAndroidKeyStorage", + isAuthenticationRequired: Boolean = true, + keyValidityDuration: Int = 60 * 5, // 5 min + keyChangedCallback: OnKeyChangedCallback? = null +) : EThreeCore(identity, tokenCallback, keyChangedCallback, context) { - override val keyManagerLocal: KeyManagerLocal + override val keyStorage: KeyStorage init { synchronized(this@EThree) { - val keyStorageAndroid = AndroidKeyStorage.Builder(alias) + keyStorage = AndroidKeyStorage.Builder(alias) .isAuthenticationRequired(isAuthenticationRequired) .withKeyValidityDuration(keyValidityDuration) .onPath(context.filesDir.absolutePath) .build() - keyManagerLocal = KeyManagerLocalEnclave(keyStorageAndroid, - tokenProvider.getToken(NO_CONTEXT).identity) - // Migration from old storage to new val keyStorageDefault = DefaultKeyStorage(context.filesDir.absolutePath, KEYSTORE_NAME) keyStorageDefault.names().forEach { name -> @@ -85,12 +84,12 @@ private constructor( // If any error happens - restore state of storages. try { - keyStorageAndroid.store(keyEntryAndroid) + keyStorage.store(keyEntryAndroid) } catch (throwable: Throwable) { - keyStorageAndroid.names().forEach { keyStorageAndroid.delete(it) } + keyStorage.names().forEach { keyStorage.delete(it) } throw KeyStorageException("Error while migrating keys from legacy key " - + "storage to the new one. All keys are restored in legacy key storage.") + + "storage to the new one. All keys are restored in legacy key storage.") } } } @@ -98,6 +97,8 @@ private constructor( // If migration was successful - remove keys from old storage. keyStorageDefault.names().forEach { keyStorageDefault.delete(it) } } + + initializeCore() } companion object { @@ -114,11 +115,15 @@ private constructor( * or on keys migration failure. */ @Throws(KeyStorageException::class) - @JvmStatic fun initialize(context: Context, - onGetTokenCallback: OnGetTokenCallback, - alias: String = "VirgilAndroidKeyStorage", - isAuthenticationRequired: Boolean = true, - keyValidityDuration: Int = 60 * 5 // 5 min + @JvmOverloads + @JvmStatic + @Deprecated("Use constructor instead") + fun initialize(context: Context, + onGetTokenCallback: OnGetTokenCallback, + alias: String = "VirgilAndroidKeyStorage", + isAuthenticationRequired: Boolean = true, + keyValidityDuration: Int = 60 * 5, // 5 min + keyChangedCallback: OnKeyChangedCallback? = null ) = object : Result { override fun get(): EThree { val tokenProvider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback { @@ -128,12 +133,18 @@ private constructor( // Just check whether we can get token, otherwise there's no reasons to // initialize EThree. We have caching JWT provider, so sequential calls // won't take much time, as token will be cached after first call. - tokenProvider.getToken(NO_CONTEXT) - return EThree(context, - tokenProvider, - alias, - isAuthenticationRequired, - keyValidityDuration) + val token = tokenProvider.getToken(NO_CONTEXT) + + val eThree = EThree(token.identity, + onGetTokenCallback, + context, + alias, + isAuthenticationRequired, + keyValidityDuration, + keyChangedCallback) + eThree.initializeCore() + + return eThree } } diff --git a/ethree-kotlin/build.gradle b/ethree-kotlin/build.gradle index bbefa46f..41f08f8c 100644 --- a/ethree-kotlin/build.gradle +++ b/ethree-kotlin/build.gradle @@ -44,12 +44,12 @@ apply plugin: 'digital.wup.android-maven-publish' apply plugin: 'org.jetbrains.dokka' android { - compileSdkVersion 28 - buildToolsVersion "28.0.3" + compileSdkVersion androidOptions.compileSdkVersion + buildToolsVersion androidOptions.buildToolsVersion defaultConfig { - minSdkVersion 21 - targetSdkVersion 28 + minSdkVersion androidOptions.minSdkVersion + targetSdkVersion androidOptions.targetSdkVersion consumerProguardFiles 'proguard-rules.txt' } } @@ -61,20 +61,6 @@ dependencies { // Inner dependencies api project(':ethree-common') - // Virgil - api "com.virgilsecurity.sdk:crypto-android:$versions.virgilSdk" - implementation("com.virgilsecurity:pythia:$versions.pythia") { - exclude group: 'com.virgilsecurity.sdk', module: 'crypto' - exclude group: 'com.virgilsecurity.crypto', module: 'common' - exclude group: 'com.virgilsecurity.crypto', module: 'foundation' - exclude group: 'com.virgilsecurity.crypto', module: 'pythia' - } - implementation "com.virgilsecurity.crypto:pythia-android:$versions.virgilCrypto" - implementation("com.virgilsecurity:keyknox:$versions.keyknox") { - exclude group: 'com.virgilsecurity.sdk', module: 'crypto' - exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8' - } - // Kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines" diff --git a/ethree-kotlin/src/main/java/com/virgilsecurity/android/ethree/interaction/EThree.kt b/ethree-kotlin/src/main/java/com/virgilsecurity/android/ethree/interaction/EThree.kt index 232bc096..760ffc52 100644 --- a/ethree-kotlin/src/main/java/com/virgilsecurity/android/ethree/interaction/EThree.kt +++ b/ethree-kotlin/src/main/java/com/virgilsecurity/android/ethree/interaction/EThree.kt @@ -34,30 +34,33 @@ package com.virgilsecurity.android.ethree.interaction import android.content.Context -import com.virgilsecurity.android.common.Const.NO_CONTEXT +import com.virgilsecurity.android.common.EThreeCore import com.virgilsecurity.android.common.callback.OnGetTokenCallback -import com.virgilsecurity.android.common.interaction.EThreeCore -import com.virgilsecurity.android.common.interaction.KeyManagerLocal -import com.virgilsecurity.android.common.model.Result +import com.virgilsecurity.android.common.callback.OnKeyChangedCallback +import com.virgilsecurity.android.common.util.Const.NO_CONTEXT +import com.virgilsecurity.common.model.Result import com.virgilsecurity.sdk.jwt.Jwt import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider -import com.virgilsecurity.sdk.jwt.contract.AccessTokenProvider +import com.virgilsecurity.sdk.storage.DefaultKeyStorage +import com.virgilsecurity.sdk.storage.KeyStorage /** * [EThree] class simplifies work with Virgil Services to easily implement End to End Encrypted * communication. */ -class EThree -private constructor( +class EThree( + identity: String, + tokenCallback: OnGetTokenCallback, context: Context, - tokenProvider: AccessTokenProvider -) : EThreeCore(tokenProvider) { + keyChangedCallback: OnKeyChangedCallback? = null +) : EThreeCore(identity, tokenCallback, keyChangedCallback, context) { - override val keyManagerLocal: KeyManagerLocal + override val keyStorage: KeyStorage init { - keyManagerLocal = - KeyManagerLocalDefault(tokenProvider.getToken(NO_CONTEXT).identity, context) + keyStorage = DefaultKeyStorage(context.filesDir.absolutePath, KEYSTORE_NAME) + + initializeCore() } companion object { @@ -70,19 +73,32 @@ private constructor( * * To start execution of the current function, please see [Result] description. */ - @JvmStatic fun initialize(context: Context, - onGetTokenCallback: OnGetTokenCallback) = object : Result { - override fun get(): EThree { - val tokenProvider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback { - Jwt(onGetTokenCallback.onGetToken()) - }) + @JvmStatic + @JvmOverloads + @Deprecated("Use constructor instead") + fun initialize(context: Context, + onGetTokenCallback: OnGetTokenCallback, + keyChangedCallback: OnKeyChangedCallback? = null) = + object : Result { + override fun get(): EThree { + val tokenProvider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback { + Jwt(onGetTokenCallback.onGetToken()) + }) + + // Just check whether we can get token, otherwise there's no reasons to + // initialize EThree. We have caching JWT provider, so sequential calls + // won't take much time, as token will be cached after first call. + val token = tokenProvider.getToken(NO_CONTEXT) + val ethree = EThree(token.identity, + onGetTokenCallback, + context, + keyChangedCallback) + ethree.initializeCore() + + return ethree + } + } - // Just check whether we can get token, otherwise there's no reasons to - // initialize EThree. We have caching JWT provider, so sequential calls - // won't take much time, as token will be cached after first call. - tokenProvider.getToken(NO_CONTEXT) - return EThree(context, tokenProvider) - } - } + private const val KEYSTORE_NAME = "virgil.keystore" } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 119f8760..044c8948 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,39 +1,6 @@ -# -# Copyright (c) 2015-2019, Virgil Security, Inc. -# -# Lead Maintainer: Virgil Security Inc. -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# (1) Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# (2) Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# (3) Neither the name of virgil nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -#Thu Jun 06 13:26:36 EEST 2019 +#Mon Sep 02 14:00:04 EEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/tests/.gitignore b/tests/.gitignore index 5550d12d..1dfb2cec 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -172,4 +172,5 @@ crashlytics-build.properties fabric.properties # Project-specific -!crypto/libs/virgil_crypto_java.jar \ No newline at end of file +!crypto/libs/virgil_crypto_java.jar +compat_data.json diff --git a/tests/build.gradle b/tests/build.gradle index 031ed044..20b21008 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -42,14 +42,24 @@ def API_PUBLIC_KEY_ID = hasProperty('API_PUBLIC_KEY_ID') ? API_PUBLIC_KEY_ID : S def VIRGIL_BASE_URL = hasProperty('VIRGIL_BASE_URL') ? VIRGIL_BASE_URL : System.getenv('VIRGIL_BASE_URL') android { - compileSdkVersion 28 + compileSdkVersion androidOptions.compileSdkVersion defaultConfig { applicationId "com.virgilsecurity.android.ethree" - minSdkVersion 21 - targetSdkVersion 28 - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + minSdkVersion androidOptions.minSdkVersion + targetSdkVersion androidOptions.targetSdkVersion +// testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + multiDexEnabled true } buildTypes { + debug { + minifyEnabled false + useProguard false + } + release { + minifyEnabled false + useProguard false + } buildTypes.each { it.buildConfigField "String", "APP_ID", "$APP_ID" it.buildConfigField "String", "API_PRIVATE_KEY", "$API_PRIVATE_KEY" @@ -66,26 +76,32 @@ dependencies { // Kotlin androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" - implementation 'com.android.support:appcompat-v7:28.0.0' + implementation "com.android.support:appcompat-v7:$versions.appCompat" // Coroutines androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines" // Virgil - androidTestImplementation("com.virgilsecurity:pythia:$versions.pythia") { - exclude group: 'com.virgilsecurity.sdk', module: 'crypto' - exclude group: 'com.virgilsecurity.crypto', module: 'common' - exclude group: 'com.virgilsecurity.crypto', module: 'foundation' - exclude group: 'com.virgilsecurity.crypto', module: 'pythia' - } - androidTestImplementation "com.virgilsecurity.crypto:pythia-android:$versions.virgilCrypto" - androidTestImplementation("com.virgilsecurity:keyknox:$versions.keyknox") { - exclude group: 'com.virgilsecurity.sdk', module: 'crypto' - exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8' - } +// androidTestImplementation ("com.virgilsecurity:pythia:$versions.pythia") { +// exclude group: 'com.virgilsecurity.crypto' +// exclude group: 'com.virgilsecurity.sdk' +// } +// androidTestImplementation "com.virgilsecurity.crypto:pythia-android:$versions.virgilCrypto" +// androidTestImplementation ("com.virgilsecurity.sdk:sdk:$versions.virgilSdk") { +// exclude group: 'com.virgilsecurity.crypto' +// } +// androidTestImplementation "com.virgilsecurity.sdk:crypto-android:$versions.virgilSdk" +// androidTestImplementation "com.virgilsecurity.crypto:common-android:$versions.virgilCrypto" +// androidTestImplementation "com.virgilsecurity:common:$versions.virgilSdk" // Tests core testImplementation "junit:junit:$versions.junit" - androidTestImplementation "com.android.support.test:runner:$versions.testsRunner" - androidTestImplementation "com.android.support.test.espresso:espresso-core:$versions.espresso" + androidTestImplementation "androidx.test.ext:junit:$versions.testsRunner" + androidTestImplementation "androidx.test:runner:$versions.testsRunner" +// androidTestImplementation "androidx.test:rules:$versions.testsRunner" + + // Room + implementation "androidx.room:room-runtime:$versions.room" + implementation "androidx.room:room-ktx:$versions.room" + testImplementation "androidx.room:room-testing:$versions.room" } diff --git a/tests/proguard-rules.pro b/tests/proguard-rules.pro index f1b42451..c6e6e3c6 100644 --- a/tests/proguard-rules.pro +++ b/tests/proguard-rules.pro @@ -19,3 +19,4 @@ # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile +-keep class androidx.lifecycle.** { *; } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/model/TicketTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/model/TicketTest.kt new file mode 100644 index 00000000..f0d4d83b --- /dev/null +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/model/TicketTest.kt @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.ethree.common.model + +import android.os.Parcel +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.virgilsecurity.android.common.exception.GroupIdTooShortException +import com.virgilsecurity.android.common.model.Ticket +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.sdk.crypto.HashAlgorithm +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import org.junit.Assert.assertArrayEquals +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import java.util.* +import kotlin.collections.HashMap + + +/** + * TicketTest + */ +@RunWith(AndroidJUnit4::class) +class TicketTest { + + @Test fun ticket_parcelable_with_serializable_participants() { + val crypto = VirgilCrypto() + val identifierData = Data(UUID.randomUUID().toString().toByteArray()) + val sessionId = computeSessionId(identifierData, crypto) + val participantsSet = setOf("Bob", "Alice", "Jane") + val ticket = Ticket(crypto, sessionId, participantsSet) + + val groupMessage = ticket.groupMessage + val participants = ticket.participants + + val parcel = Parcel.obtain() + ticket.writeToParcel(parcel, ticket.describeContents()) + parcel.setDataPosition(0) + + val createdFromParcel = Ticket.CREATOR.createFromParcel(parcel) + assertArrayEquals(createdFromParcel.groupMessage.serialize(), groupMessage.serialize()) + assertEquals(createdFromParcel.participants, participants) + } + + @Test(expected = IllegalArgumentException::class) + fun ticket_parcelable_with_not_serializable_participants() { + val crypto = VirgilCrypto() + val identifierData = Data(UUID.randomUUID().toString().toByteArray()) + val sessionId = computeSessionId(identifierData, crypto) + + val participantsSet = NotSerializableSet("Bob", "Alice", "Jane") + + // Only Serializable Set's are supported + Ticket(crypto, sessionId, participantsSet) + } + + private fun computeSessionId(identifier: Data, crypto: VirgilCrypto): Data { + if (identifier.data.size <= 10) { + throw GroupIdTooShortException("Group Id length should be > 10") + } + + val hash = crypto.computeHash(identifier.data, HashAlgorithm.SHA512) + .sliceArray(IntRange(0, 31)) + + return Data(hash) + } + + private class NotSerializableSet(vararg values: String) : Set { + + @Transient + private var map: HashMap + + init { + val valuesToObjects = values.map { it to PRESENT }.toMap() + map = HashMap(valuesToObjects) + } + + + override val size: Int + get() = map.size + + override fun contains(element: String): Boolean = map.contains(element) + + override fun containsAll(elements: Collection): Boolean = containsAll(elements) + + override fun isEmpty(): Boolean = map.isEmpty() + + override fun iterator(): Iterator = map.keys.iterator() + + companion object { + private val PRESENT = Any() + } + } +} diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/storage/sql/SQLCardStorageTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/storage/sql/SQLCardStorageTest.kt new file mode 100644 index 00000000..0dfe3032 --- /dev/null +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/storage/sql/SQLCardStorageTest.kt @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.ethree.common.storage.sql + +import androidx.room.Room +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import com.virgilsecurity.android.common.storage.CardStorage +import com.virgilsecurity.android.common.storage.sql.ETheeDatabase +import com.virgilsecurity.android.common.storage.sql.SQLCardStorage +import com.virgilsecurity.android.common.storage.sql.model.CardEntity +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.context +import com.virgilsecurity.sdk.cards.CardManager +import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier +import com.virgilsecurity.sdk.crypto.VirgilCardCrypto +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider +import org.junit.After +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.io.InputStreamReader +import java.util.* + + +@RunWith(AndroidJUnit4::class) +class SQLCardStorageTest { + + private val cIdentity1 = "8DA6A11D-F8BC-4A1D-A221-CEE3A2D70631" + private val cIdentity2 = "D4E8E4CA-6FB4-42B6-A3FF-DBBC19201DD6" + + private val cCardId1 = "b2e6c8bee5cfa40fa2ac2bc8961057600bced26bc5b29aab04014c5141a91bd4" + private val cCardId2 = "9ff917a7a1aa0891b875d4a9e43972a0fb694879bf8987790c1615dd864a38a4" + private val cCardId3 = "e66465a08232beb55e33b4ce5e8772d748911c9b830797336e1ce342c78829a2" + + private lateinit var identity: String + private lateinit var storage: CardStorage + private lateinit var crypto: VirgilCrypto + private lateinit var verifier: VirgilCardVerifier + private lateinit var cardManager: CardManager + private lateinit var db: ETheeDatabase + + @Before + fun setup() { + identity = UUID.randomUUID().toString() + crypto = VirgilCrypto() + verifier = VirgilCardVerifier(VirgilCardCrypto(crypto)) + val tokenProvider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback(function = { + return@RenewJwtCallback null + })) + cardManager = CardManager(VirgilCardCrypto(VirgilCrypto()), tokenProvider, VirgilCardVerifier(VirgilCardCrypto(VirgilCrypto()))) + + db = Room.inMemoryDatabaseBuilder(TestConfig.context, ETheeDatabase::class.java).build() + prePopulateDatabase() + + this.storage = SQLCardStorage(TestConfig.context, identity, crypto, verifier, db) + } + + @After + fun tearDown() { + } + + @Test + fun getCard() { + // Cards predefined in database should match + checkCardsById() + } + + @Test + fun searchCards() { + // Cards predefined in database should match + checkCardsByIdentity() + } + + @Test + fun storeCard() { + // Cards predefined in database should match + val identity2 = UUID.randomUUID().toString() + val storage2 = SQLCardStorage(TestConfig.context, identity2, crypto, verifier) + + val cards = storage.searchCards(listOf(this.cIdentity1, this.cIdentity2)) + assertEquals(2, cards.size) + + storage2.storeCard(cards[0]) + storage2.storeCard(cards[1]) + + checkCardsByIdentity(storage2) + checkCardsById(storage2) + } + + @Test + fun storeCard_rotate_card() { + // Rotate card should update both cards + val identity2 = UUID.randomUUID().toString() + val storage2 = SQLCardStorage(TestConfig.context, identity2, crypto, verifier) + + val card1 = storage.getCard(this.cCardId1) + assertNotNull(card1) + val card2 = storage.getCard(this.cCardId2) + assertNotNull(card2) + val card3 = storage.getCard(this.cCardId3) + assertNotNull(card3) + + card2!!.isOutdated = false + storage2.storeCard(card2) + storage2.storeCard(card1!!) + storage2.storeCard(card3!!) + + checkCardsByIdentity(storage2) + checkCardsById(storage2) + } + + @Test + fun getNewestCardIds() { + // Cards predefined in database should match + val identity2 = UUID.randomUUID().toString() + val storage2 = SQLCardStorage(TestConfig.context, identity2, crypto, verifier) + + val cards = storage.searchCards(listOf(this.cIdentity1, this.cIdentity2)) + assertEquals(2, cards.size) + + storage2.storeCard(cards[0]) + storage2.storeCard(cards[1]) + + val ids = storage2.getNewestCardIds() + assertEquals(2, ids.size) + + assertTrue(ids.contains(this.cCardId1)) + assertTrue(ids.contains(this.cCardId3)) + } + + @Test + fun reset() { + // Predefined database should be empty + val identity2 = UUID.randomUUID().toString() + val storage2 = SQLCardStorage(TestConfig.context, identity2, crypto, verifier) + + val cards = storage.searchCards(listOf(this.cIdentity1, this.cIdentity2)) + + storage2.storeCard(cards[0]) + storage2.storeCard(cards[1]) + + storage2.reset() + + assertNull(storage2.getCard(this.cCardId1)) + assertNull(storage2.getCard(this.cCardId2)) + assertNull(storage2.getCard(this.cCardId3)) + } + + private fun checkCardsById(storage: CardStorage = this.storage) { + val card1 = storage.getCard(this.cCardId1) + assertNotNull(card1) + val card2 = storage.getCard(this.cCardId2) + assertNotNull(card2) + val card3 = storage.getCard(this.cCardId3) + assertNotNull(card3) + + assertEquals(this.cIdentity1, card1!!.identity) + assertEquals(this.cIdentity1, card2!!.identity) + assertEquals(this.cIdentity2, card3!!.identity) + + assertEquals(card2.identifier, card1.previousCardId) + assertNull(card2.previousCardId) + assertNull(card3.previousCardId) + + assertNull(card1.previousCard) + assertNull(card2.previousCard) + assertNull(card3.previousCard) + + assertFalse(card1.isOutdated) + assertTrue(card2.isOutdated) + assertFalse(card3.isOutdated) + } + + private fun checkCardsByIdentity(storage: CardStorage = this.storage) { + val cards = storage.searchCards(listOf(this.cIdentity1, this.cIdentity2)) + assertEquals(2, cards.size) + + val card1 = cards.first { it.identity == this.cIdentity1 } + val card2 = cards.first { it.identity == this.cIdentity2 } + + assertNotNull(card1.previousCardId) + assertNull(card2.previousCardId) + + assertNotNull(card1.previousCard) + assertNull(card1.previousCard.previousCard) + assertNull(card2.previousCard) + + assertFalse(card1.isOutdated) + assertTrue(card1.previousCard.isOutdated) + assertFalse(card2.isOutdated) + } + + private fun prePopulateDatabase() { + val sampleJson = JsonParser().parse(InputStreamReader(context.assets.open("databases/cards.json"))) as JsonObject + sampleJson.entrySet().forEach { + val cardId = it.key + val identity = (it.value as JsonObject)["identity"].asString + val isOutdated = (it.value as JsonObject)["is_outdated"].asBoolean + val cardData = (it.value as JsonObject)["card"].asString + + val cardEntity = CardEntity(cardId, identity, isOutdated, cardData) + db.cardDao().insert(cardEntity) + } + } +} diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt index d9ebb984..fe45f781 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt @@ -33,23 +33,23 @@ package com.virgilsecurity.android.ethree.interaction.async -import com.virgilsecurity.android.common.exceptions.CardNotFoundException -import com.virgilsecurity.android.common.exceptions.PrivateKeyExistsException -import com.virgilsecurity.android.common.exceptions.RegistrationException -import com.virgilsecurity.android.common.callback.OnCompleteListener +import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback -import com.virgilsecurity.android.common.callback.OnResultListener +import com.virgilsecurity.android.common.exception.* import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.ethree.utils.TestConfig import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilBaseUrl import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilCrypto import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.common.callback.OnCompleteListener +import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.model.RawSignedModel import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier import com.virgilsecurity.sdk.client.VirgilCardClient import com.virgilsecurity.sdk.common.TimeSpan import com.virgilsecurity.sdk.crypto.* +import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryNotFoundException import com.virgilsecurity.sdk.jwt.JwtGenerator import com.virgilsecurity.sdk.jwt.accessProviders.GeneratorJwtProvider import com.virgilsecurity.sdk.storage.DefaultKeyStorage @@ -60,11 +60,14 @@ import org.hamcrest.core.IsEqual.equalTo import org.hamcrest.core.IsNot.not import org.junit.Assert.* import org.junit.Before +import org.junit.Ignore import org.junit.Test +import org.junit.runner.RunWith import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit +@RunWith(AndroidJUnit4::class) class EThreeAuthTest { private val identity = UUID.randomUUID().toString() @@ -190,7 +193,7 @@ class EThreeAuthTest { } override fun onError(throwable: Throwable) { - assertTrue(throwable is RegistrationException) + assertTrue(throwable is EThreeException) waiter.countDown() } }) @@ -211,7 +214,7 @@ class EThreeAuthTest { } override fun onError(throwable: Throwable) { - assertTrue(throwable is PrivateKeyExistsException) + assertTrue(throwable is EThreeException) waiter.countDown() } }) @@ -230,7 +233,7 @@ class EThreeAuthTest { } override fun onError(throwable: Throwable) { - assertTrue(throwable is CardNotFoundException) + assertTrue(throwable is EThreeException) waiter.countDown() } } @@ -251,7 +254,7 @@ class EThreeAuthTest { } override fun onError(throwable: Throwable) { - assertTrue(throwable is PrivateKeyExistsException) + assertTrue(throwable is EThreeException) waiterTwo.countDown() } }) @@ -311,6 +314,7 @@ class EThreeAuthTest { assertNotNull("Error during keys rotation", encrypted) } + @Ignore("iOS does not have this exception for now, so keeping SDKs with the same structure.") @Test fun rotate_when_multiply_cards_available() { val cardManager = initCardManager(identity) val publishPair = generateRawCard(identity, cardManager) @@ -355,7 +359,7 @@ class EThreeAuthTest { } override fun onError(throwable: Throwable) { - if (throwable is IllegalStateException) + if (throwable is FindUsersException) rotateFailed = true waiterTwo.countDown() @@ -406,11 +410,15 @@ class EThreeAuthTest { val waiter = CountDownLatch(1) eThree.unregister().addCallback(object : OnCompleteListener { override fun onSuccess() { + fail("unregister should fail without local private key") waiter.countDown() } override fun onError(throwable: Throwable) { - fail(throwable.message) + if (throwable !is KeyEntryNotFoundException) + fail(throwable.message) + + waiter.countDown() } }) waiter.await(TestUtils.THROTTLE_TIMEOUT, TimeUnit.SECONDS) diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt index eb8fc9d2..228db59b 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt @@ -33,17 +33,19 @@ package com.virgilsecurity.android.ethree.interaction.async -import com.virgilsecurity.android.common.exceptions.BackupKeyException -import com.virgilsecurity.android.common.exceptions.PrivateKeyNotFoundException -import com.virgilsecurity.android.common.exceptions.RestoreKeyException -import com.virgilsecurity.android.common.exceptions.WrongPasswordException -import com.virgilsecurity.android.common.callback.OnCompleteListener +import android.util.Log +import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback -import com.virgilsecurity.android.common.callback.OnResultListener +import com.virgilsecurity.android.common.exception.BackupKeyException +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException +import com.virgilsecurity.android.common.exception.RestoreKeyException +import com.virgilsecurity.android.common.exception.WrongPasswordException import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.ethree.utils.TestConfig import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilBaseUrl import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.common.callback.OnCompleteListener +import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.keyknox.KeyknoxManager import com.virgilsecurity.keyknox.client.KeyknoxClient import com.virgilsecurity.keyknox.cloud.CloudKeyStorage @@ -62,6 +64,7 @@ import com.virgilsecurity.sdk.storage.KeyStorage import org.junit.Assert.* import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import java.net.URL import java.util.* import java.util.concurrent.CountDownLatch @@ -74,6 +77,7 @@ import java.util.concurrent.TimeUnit * 10/9/18 * at Virgil Security */ +@RunWith(AndroidJUnit4::class) class EThreeBackupTest { private lateinit var jwtGenerator: JwtGenerator @@ -160,18 +164,18 @@ class EThreeBackupTest { .build() val keyPair = BrainKey(brainKeyContext).generateKeyPair(passwordBrainKey) - val syncKeyStorage = - SyncKeyStorage( - identity, keyStorage, CloudKeyStorage( - KeyknoxManager( - tokenProvider, - KeyknoxClient(URL(virgilBaseUrl)), - listOf(keyPair.publicKey), - keyPair.privateKey, - KeyknoxCrypto() - ) - ) - ) + val syncKeyStorage = SyncKeyStorage( + identity, + keyStorage, + CloudKeyStorage( + KeyknoxManager( + KeyknoxClient(tokenProvider, URL(virgilBaseUrl)), + KeyknoxCrypto() + ), + listOf(keyPair.publicKey), + keyPair.privateKey + ) + ) syncKeyStorage.sync() @@ -311,6 +315,7 @@ class EThreeBackupTest { } override fun onError(throwable: Throwable) { + Log.d(TAG, "Exception type is: " + throwable.javaClass.canonicalName) if (throwable is RestoreKeyException) failedToRestore = true @@ -558,6 +563,7 @@ class EThreeBackupTest { } companion object { + const val TAG = "EThreeBackupTest" const val WRONG_PASSWORD = "WRONG_PASSWORD" } } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt index 82559fc7..b58754b0 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt @@ -33,14 +33,16 @@ package com.virgilsecurity.android.ethree.interaction.async -import com.virgilsecurity.android.common.model.LookupResult -import com.virgilsecurity.android.common.exceptions.PrivateKeyNotFoundException -import com.virgilsecurity.android.common.callback.OnCompleteListener +import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback -import com.virgilsecurity.android.common.callback.OnResultListener +import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException +import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.ethree.utils.TestConfig import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.common.callback.OnCompleteListener +import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.model.RawSignedModel import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier @@ -60,6 +62,7 @@ import com.virgilsecurity.sdk.utils.Tuple import org.junit.Assert.* import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.util.* @@ -73,6 +76,7 @@ import java.util.concurrent.TimeUnit * 10/9/18 * at Virgil Security */ +@RunWith(AndroidJUnit4::class) class EThreeEncryptionTest { private val identity = UUID.randomUUID().toString() @@ -228,16 +232,20 @@ class EThreeEncryptionTest { //STE-2 @Test fun lookup_zero_users() { + val waiter = CountDownLatch(1) eThree.lookupPublicKeys(listOf()) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { fail("Illegal State") + waiter.countDown() } override fun onError(throwable: Throwable) { - assertTrue(throwable is EmptyArgumentException) + assertTrue(throwable is IllegalArgumentException) + waiter.countDown() } }) + waiter.await(TestUtils.THROTTLE_TIMEOUT, TimeUnit.SECONDS) } @Test fun encrypt_adding_owner_public_key() { @@ -259,16 +267,15 @@ class EThreeEncryptionTest { } }) waiter.await(TestUtils.THROTTLE_TIMEOUT, TimeUnit.SECONDS) - assertNotNull(eThreeKeys) + val lookupResult = eThreeKeys ?: error("") assertEquals(2, eThreeKeys?.size) - var failedEncrypt = false try { - eThree.encrypt(RAW_TEXT, eThreeKeys) + val encrypted = eThree.encrypt(RAW_TEXT, lookupResult) + assertNotNull(encrypted) } catch (e: IllegalArgumentException) { - failedEncrypt = true + fail(e.message) } - assertTrue(failedEncrypt) } // STE-3 @@ -291,11 +298,11 @@ class EThreeEncryptionTest { } }) waiter.await(TestUtils.THROTTLE_TIMEOUT, TimeUnit.SECONDS) - assertNotNull(eThreeKeys) + val lookupResult = eThreeKeys ?: error("") assertEquals(2, eThreeKeys?.size) val lookupEntry = - eThreeKeys?.toMutableMap()?.apply { remove(identity) } // We need only identity + lookupResult.toMutableMap().apply { remove(identity) } // We need only identity val encryptedForOne = eThree.encrypt(RAW_TEXT, lookupEntry) val wrongPublicKey = TestConfig.virgilCrypto.generateKeyPair().publicKey @@ -307,13 +314,14 @@ class EThreeEncryptionTest { } assertTrue(failedWithWrongKey) - val decryptedByTwo = eThreeTwo.decrypt(encryptedForOne, eThreeKeys!![identity]) + val publicKey = lookupResult[identity] ?: error("") + val decryptedByTwo = eThreeTwo.decrypt(encryptedForOne, publicKey) assertEquals(RAW_TEXT, decryptedByTwo) } // STE-4 - @Test(expected = EmptyArgumentException::class) + @Test(expected = EThreeException::class) fun encrypt_for_zero_users() { eThree.encrypt(RAW_TEXT, mapOf()) } @@ -333,54 +341,6 @@ class EThreeEncryptionTest { assertTrue(failedDecrypt) } - // STE-6 - @Test fun encrypt_decrypt_without_register() { - var eThreeTwo: EThree? = null - val identity = UUID.randomUUID().toString() - - val waiter = CountDownLatch(1) - EThree.initialize(TestConfig.context, - object : OnGetTokenCallback { - override fun onGetToken(): String { - return jwtGenerator.generateToken( - identity) - .stringRepresentation() - } - }) - .addCallback(object : OnResultListener { - override fun onSuccess(result: EThree) { - eThreeTwo = result - waiter.countDown() - } - - override fun onError(throwable: Throwable) { - fail(throwable.message) - } - - }) - - - waiter.await(TestUtils.THROTTLE_TIMEOUT, TimeUnit.SECONDS) - - val keys = TestConfig.virgilCrypto.generateKeyPair() - - var failedToEncrypt = false - try { - eThreeTwo!!.encrypt(RAW_TEXT, mapOf(identity to keys.publicKey)) - } catch (exception: PrivateKeyNotFoundException) { - failedToEncrypt = true - } - assertTrue(failedToEncrypt) - - var failedToDecrypt = false - try { - eThreeTwo!!.decrypt("fakeEncryptedText", keys.publicKey) - } catch (exception: PrivateKeyNotFoundException) { - failedToDecrypt = true - } - assertTrue(failedToDecrypt) - } - @Test fun encrypt_decrypt_without_register_for_owner() { var eThreeTwo: EThree? = null @@ -475,11 +435,11 @@ class EThreeEncryptionTest { } }) waiter.await(TestUtils.THROTTLE_TIMEOUT, TimeUnit.SECONDS) - assertNotNull(eThreeKeys) + val lookupResult = eThreeKeys ?: error("") assertEquals(2, eThreeKeys?.size) val lookupEntry = - eThreeKeys?.toMutableMap()?.apply { remove(identity) } // We need only identityTwo + lookupResult.toMutableMap().apply { remove(identity) } // We need only identityTwo ByteArrayOutputStream().use { eThree.encrypt(ByteArrayInputStream(RAW_TEXT.toByteArray()), it, lookupEntry) diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt index d8dd5b0b..e6483503 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt @@ -33,23 +33,22 @@ package com.virgilsecurity.android.ethree.interaction.async -import com.virgilsecurity.android.common.model.LookupResult -import com.virgilsecurity.android.common.exceptions.PrivateKeyNotFoundException -import com.virgilsecurity.android.common.exceptions.PublicKeyDuplicateException -import com.virgilsecurity.android.common.exceptions.PublicKeyNotFoundException -import com.virgilsecurity.android.common.exceptions.UnRegistrationException -import com.virgilsecurity.android.common.callback.OnCompleteListener +import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback -import com.virgilsecurity.android.common.callback.OnResultListener +import com.virgilsecurity.android.common.exception.* +import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.ethree.utils.TestConfig import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.common.callback.OnCompleteListener +import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.model.RawSignedModel import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier import com.virgilsecurity.sdk.client.VirgilCardClient import com.virgilsecurity.sdk.common.TimeSpan import com.virgilsecurity.sdk.crypto.* +import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryNotFoundException import com.virgilsecurity.sdk.jwt.JwtGenerator import com.virgilsecurity.sdk.jwt.accessProviders.GeneratorJwtProvider import com.virgilsecurity.sdk.storage.DefaultKeyStorage @@ -59,6 +58,7 @@ import org.junit.Assert.assertTrue import org.junit.Assert.fail import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -70,6 +70,7 @@ import java.util.concurrent.TimeUnit * 10/24/18 * at Virgil Security */ +@RunWith(AndroidJUnit4::class) class EThreeNegativeTest { private lateinit var jwtGenerator: JwtGenerator @@ -158,7 +159,7 @@ class EThreeNegativeTest { return eThree } - @Test(expected = PrivateKeyNotFoundException::class) + @Test(expected = KeyEntryNotFoundException::class) fun cleanup_fail_without_bootstrap() { eThree.cleanup() } @@ -215,23 +216,23 @@ class EThreeNegativeTest { assertTrue(failed) } - @Test(expected = PrivateKeyNotFoundException::class) - fun encrypt_text_fail_without_bootstrap() { + @Test(expected = IllegalArgumentException::class) + fun encrypt_text_empty() { eThree.encrypt("") } - @Test(expected = PrivateKeyNotFoundException::class) - fun encrypt_data_fail_without_bootstrap() { + @Test(expected = IllegalArgumentException::class) + fun encrypt_data_empty() { eThree.encrypt(ByteArray(0)) } - @Test(expected = PrivateKeyNotFoundException::class) - fun decrypt_text_fail_without_bootstrap() { + @Test(expected = IllegalArgumentException::class) + fun decrypt_text_empty() { eThree.decrypt("") } - @Test(expected = PrivateKeyNotFoundException::class) - fun decrypt_data_fail_without_bootstrap() { + @Test(expected = IllegalArgumentException::class) + fun decrypt_data_empty() { eThree.decrypt(ByteArray(0)) } @@ -258,18 +259,15 @@ class EThreeNegativeTest { var failed = false val waiter = CountDownLatch(1) - eThree.lookupPublicKeys(listOf(identity, - WRONG_IDENTITY)) + eThree.lookupPublicKeys(listOf(identity, WRONG_IDENTITY)) .addCallback(object : OnResultListener> { override fun onSuccess(result: Map) { fail("Illegal State") } override fun onError(throwable: Throwable) { - if (throwable is PublicKeyNotFoundException - && throwable.identity == WRONG_IDENTITY) { + if (throwable is FindUsersException) failed = true - } waiter.countDown() } @@ -301,30 +299,6 @@ class EThreeNegativeTest { assertTrue(failed) } - @Test fun lookup_with_duplicate_identities() { - var failed = false - val waiter = CountDownLatch(1) - registerEThree(eThree) - eThree.lookupPublicKeys(listOf(identity, identity, identity, - WRONG_IDENTITY, - WRONG_IDENTITY, - WRONG_IDENTITY + identity)) - .addCallback(object : OnResultListener { - override fun onSuccess(result: LookupResult) { - fail("Illegal State") - } - - override fun onError(throwable: Throwable) { - if (throwable is PublicKeyDuplicateException) - failed = true - - waiter.countDown() - } - }) - waiter.await(TestUtils.THROTTLE_TIMEOUT, TimeUnit.SECONDS) - assertTrue(failed) - } - @Test fun change_pass_with_same_new() { var failed = false val waiter = CountDownLatch(1) @@ -335,7 +309,7 @@ class EThreeNegativeTest { } override fun onError(throwable: Throwable) { - if (throwable is IllegalArgumentException) + if (throwable is EThreeException) failed = true waiter.countDown() @@ -363,7 +337,7 @@ class EThreeNegativeTest { } override fun onError(throwable: Throwable) { - if (throwable is UnRegistrationException) + if (throwable is EThreeException) failed = true waiter.countDown() @@ -382,7 +356,7 @@ class EThreeNegativeTest { } override fun onError(throwable: Throwable) { - if (throwable is UnRegistrationException) + if (throwable is EThreeException) failed = true waiter.countDown() diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt index a3f49c66..df9d9ad8 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt @@ -33,12 +33,13 @@ package com.virgilsecurity.android.ethree.interaction.async -import com.virgilsecurity.android.common.callback.OnCompleteListener +import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback -import com.virgilsecurity.android.common.callback.OnResultListener import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.ethree.utils.TestConfig import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.common.callback.OnCompleteListener +import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier import com.virgilsecurity.sdk.client.VirgilCardClient @@ -53,12 +54,14 @@ import kotlinx.coroutines.* import org.junit.Assert.* import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import java.util.* import java.util.concurrent.TimeUnit /** * EThreeScopesTest */ +@RunWith(AndroidJUnit4::class) class EThreeScopesTest { private val identity = UUID.randomUUID().toString() @@ -155,9 +158,11 @@ class EThreeScopesTest { delay(A_FEW_NETWORK_CALLS_DELAY) scope.cancel() - assertNotEquals(0, timesBeforeCancel) // Called at least once - assertTrue(timesBeforeCancel < NUMBER_OF_REPEATS) // But cancelled before initialize has - // been called NUMBER_OF_REPEATS times + // Called at least once But cancelled before initialize has been called NUMBER_OF_REPEATS times + assertNotEquals(0, timesBeforeCancel) + assertTrue("$timesBeforeCancel should be less than $NUMBER_OF_REPEATS", + timesBeforeCancel < NUMBER_OF_REPEATS) + println("TIMES BEFORE: $timesBeforeCancel") } @Test fun get_result_without_waiting_callback() { @@ -185,6 +190,6 @@ class EThreeScopesTest { companion object { const val NUMBER_OF_REPEATS = 100 - const val A_FEW_NETWORK_CALLS_DELAY = 5000L // 5 sec + const val A_FEW_NETWORK_CALLS_DELAY = 1000L // 5 sec } } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/profile/EThreeProfile.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/profile/EThreeProfile.kt index 0434cbe5..8b7fab66 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/profile/EThreeProfile.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/profile/EThreeProfile.kt @@ -62,7 +62,7 @@ class EThreeProfile { @Ignore("Needed only to profile from time to time") @Test fun encrypt_debug_profile() { Debug.startMethodTracing("EThreeProfile_encrypt") - crypto.encrypt(oneMbData, keyPair.publicKey) + crypto.encrypt(oneMbData, keyPair.publicKey) // TODO add 1000 loops, or so Debug.stopMethodTracing() } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt index c42510b3..029a522a 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt @@ -33,20 +33,12 @@ package com.virgilsecurity.android.ethree.interaction.sync -import com.virgilsecurity.android.common.exceptions.* import com.virgilsecurity.android.common.callback.OnGetTokenCallback +import com.virgilsecurity.android.common.exception.* import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.ethree.utils.TestConfig import com.virgilsecurity.android.ethree.utils.TestUtils -import com.virgilsecurity.keyknox.KeyknoxManager -import com.virgilsecurity.keyknox.client.KeyknoxClient -import com.virgilsecurity.keyknox.cloud.CloudKeyStorage -import com.virgilsecurity.keyknox.crypto.KeyknoxCrypto -import com.virgilsecurity.keyknox.storage.SyncKeyStorage -import com.virgilsecurity.pythia.brainkey.BrainKey -import com.virgilsecurity.pythia.brainkey.BrainKeyContext -import com.virgilsecurity.pythia.client.VirgilPythiaClient -import com.virgilsecurity.pythia.crypto.VirgilPythiaCrypto +import com.virgilsecurity.keyknox.exception.EntryNotFoundException import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.model.RawSignedModel import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier @@ -58,7 +50,6 @@ import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.VirgilKeyPair import com.virgilsecurity.sdk.exception.EmptyArgumentException import com.virgilsecurity.sdk.jwt.JwtGenerator -import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider import com.virgilsecurity.sdk.jwt.accessProviders.GeneratorJwtProvider import com.virgilsecurity.sdk.storage.DefaultKeyStorage import com.virgilsecurity.sdk.storage.KeyStorage @@ -66,7 +57,6 @@ import com.virgilsecurity.sdk.utils.Tuple import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test -import java.net.URL import java.util.* import java.util.concurrent.TimeUnit @@ -121,35 +111,6 @@ class EThreeSyncNegative { ) } - private fun initSyncKeyStorage(identity: String, passwordBrainKey: String): SyncKeyStorage { - val tokenProvider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback { - jwtGenerator.generateToken(identity) - }) - val brainKeyContext = BrainKeyContext.Builder() - .setAccessTokenProvider(tokenProvider) - .setPythiaClient(VirgilPythiaClient(TestConfig.virgilBaseUrl)) - .setPythiaCrypto(VirgilPythiaCrypto()) - .build() - val keyPair = BrainKey(brainKeyContext).generateKeyPair(passwordBrainKey) - - val syncKeyStorage = - SyncKeyStorage( - identity, keyStorage, CloudKeyStorage( - KeyknoxManager( - tokenProvider, - KeyknoxClient(URL(TestConfig.virgilBaseUrl)), - listOf(keyPair.publicKey), - keyPair.privateKey, - KeyknoxCrypto() - ) - ) - ) - - syncKeyStorage.sync() - - return syncKeyStorage - } - private fun generateRawCard(identity: String, cardManager: CardManager): Tuple { return VirgilCrypto().generateKeyPair().let { @@ -165,7 +126,7 @@ class EThreeSyncNegative { try { eThree.register().execute() } catch (throwable: Throwable) { - assertTrue(throwable is RegistrationException) + assertTrue(throwable is EThreeException) } } @@ -175,7 +136,7 @@ class EThreeSyncNegative { try { eThree.unregister().execute() } catch (throwable: Throwable) { - assertTrue(throwable is UnRegistrationException) + assertTrue(throwable is EThreeException) } } @@ -187,7 +148,7 @@ class EThreeSyncNegative { try { eThree.backupPrivateKey(password).execute() } catch (throwable: Throwable) { - assertTrue(throwable is PrivateKeyNotFoundException) + assertTrue(throwable is EThreeException) } } @@ -213,7 +174,7 @@ class EThreeSyncNegative { try { eThree.restorePrivateKey(password).execute() } catch (throwable: Throwable) { - assertTrue(throwable is RestoreKeyException) + assertTrue(throwable is EntryNotFoundException) } } @@ -224,7 +185,7 @@ class EThreeSyncNegative { try { eThree.rotatePrivateKey().execute() } catch (throwable: Throwable) { - assertTrue(throwable is CardNotFoundException) + assertTrue(throwable is EThreeException) } } @@ -248,7 +209,7 @@ class EThreeSyncNegative { try { eThree.lookupPublicKeys(listOf()).get() } catch (throwable: Throwable) { - assertTrue(throwable is EmptyArgumentException) + assertTrue(throwable is IllegalArgumentException) } } } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt index 92c808ed..a3671e01 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt @@ -133,18 +133,18 @@ class EThreeSyncPositive { .build() val keyPair = BrainKey(brainKeyContext).generateKeyPair(passwordBrainKey) - val syncKeyStorage = - SyncKeyStorage( - identity, keyStorage, CloudKeyStorage( - KeyknoxManager( - tokenProvider, - KeyknoxClient(URL(TestConfig.virgilBaseUrl)), - listOf(keyPair.publicKey), - keyPair.privateKey, - KeyknoxCrypto() - ) - ) - ) + val syncKeyStorage = SyncKeyStorage( + identity, + keyStorage, + CloudKeyStorage( + KeyknoxManager( + KeyknoxClient(tokenProvider, URL(TestConfig.virgilBaseUrl)), + KeyknoxCrypto() + ), + listOf(keyPair.publicKey), + keyPair.privateKey + ) + ) syncKeyStorage.sync() diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java index f351241a..9d21cfff 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java @@ -33,13 +33,13 @@ package com.virgilsecurity.android.ethree.java.interaction; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.virgilsecurity.android.common.callback.OnCompleteListener; import com.virgilsecurity.android.common.callback.OnGetTokenCallback; -import com.virgilsecurity.android.common.callback.OnResultListener; import com.virgilsecurity.android.ethree.interaction.EThree; import com.virgilsecurity.android.ethree.utils.TestConfig; +import com.virgilsecurity.common.callback.OnCompleteListener; +import com.virgilsecurity.common.callback.OnResultListener; import com.virgilsecurity.sdk.cards.Card; import com.virgilsecurity.sdk.cards.CardManager; import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier; diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java index b8b98eeb..ff24603a 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java @@ -33,14 +33,14 @@ package com.virgilsecurity.android.ethree.java.interaction; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.virgilsecurity.android.common.callback.OnCompleteListener; import com.virgilsecurity.android.common.callback.OnGetTokenCallback; -import com.virgilsecurity.android.common.callback.OnResultListener; import com.virgilsecurity.android.ethree.interaction.EThree; import com.virgilsecurity.android.ethree.utils.TestConfig; import com.virgilsecurity.android.ethree.utils.TestUtils; +import com.virgilsecurity.common.callback.OnCompleteListener; +import com.virgilsecurity.common.callback.OnResultListener; import com.virgilsecurity.sdk.cards.Card; import com.virgilsecurity.sdk.cards.CardManager; import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier; diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt index e9ea37db..6ac56c6c 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt @@ -33,11 +33,10 @@ package com.virgilsecurity.android.ethree.utils -import android.support.test.InstrumentationRegistry +import androidx.test.platform.app.InstrumentationRegistry import com.virgilsecurity.android.ethree.BuildConfig import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.VirgilPrivateKey -import com.virgilsecurity.sdk.crypto.VirgilPublicKey import com.virgilsecurity.sdk.utils.ConvertionUtils class TestConfig { @@ -53,8 +52,8 @@ class TestConfig { val virgilBaseUrl = BuildConfig.VIRGIL_BASE_URL const val VIRGIL_CARDS_SERVICE_PATH = "/card/v5/" - val context = InstrumentationRegistry.getTargetContext() - val DIRECTORY_PATH = InstrumentationRegistry.getTargetContext().filesDir.absolutePath + val context = InstrumentationRegistry.getInstrumentation().targetContext + val DIRECTORY_PATH = InstrumentationRegistry.getInstrumentation().targetContext.filesDir.absolutePath val KEYSTORE_NAME = "virgil.keystore" } } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt index 16485496..a2ce2976 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt @@ -33,6 +33,23 @@ package com.virgilsecurity.android.ethree.utils +import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilCrypto +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.cards.CardManager +import com.virgilsecurity.sdk.cards.ModelSigner +import com.virgilsecurity.sdk.cards.model.RawCardContent +import com.virgilsecurity.sdk.cards.model.RawSignedModel +import com.virgilsecurity.sdk.client.VirgilCardClient +import com.virgilsecurity.sdk.common.TimeSpan +import com.virgilsecurity.sdk.crypto.VirgilAccessTokenSigner +import com.virgilsecurity.sdk.crypto.VirgilCardCrypto +import com.virgilsecurity.sdk.jwt.Jwt +import com.virgilsecurity.sdk.jwt.JwtGenerator +import com.virgilsecurity.sdk.jwt.accessProviders.ConstAccessTokenProvider +import com.virgilsecurity.sdk.utils.ConvertionUtils +import java.util.* +import java.util.concurrent.TimeUnit + class TestUtils { companion object { @@ -41,5 +58,47 @@ class TestUtils { fun pause(timeout: Long = THROTTLE_TIMEOUT) { Thread.sleep(timeout) } + + fun generateTokenString(identity: String): String = + JwtGenerator( + TestConfig.appId, + TestConfig.apiKey, + TestConfig.apiPublicKeyId, + TimeSpan.fromTime(600, TimeUnit.SECONDS), + VirgilAccessTokenSigner(virgilCrypto) + ).generateToken(identity).stringRepresentation() + + fun generateToken(identity: String): Jwt = + JwtGenerator( + TestConfig.appId, + TestConfig.apiKey, + TestConfig.apiPublicKeyId, + TimeSpan.fromTime(600, TimeUnit.SECONDS), + VirgilAccessTokenSigner(virgilCrypto) + ).generateToken(identity) + + fun publishCard(identity: String? = null, previousCardId: String? = null): Card { + val keyPair = virgilCrypto.generateKeyPair() + val exportedPublicKey = virgilCrypto.exportPublicKey(keyPair.publicKey) + val identityNew = identity ?: UUID.randomUUID().toString() + val content = RawCardContent(identityNew, + ConvertionUtils.toBase64String(exportedPublicKey), + "5.0", + Date(), + previousCardId) + val snapshot = content.snapshot() + val rawCard = RawSignedModel(snapshot) + val token = generateToken(identityNew) + val provider = ConstAccessTokenProvider(token) + val signer = ModelSigner(VirgilCardCrypto(virgilCrypto)) + signer.selfSign(rawCard, keyPair.privateKey) + val cardClient = VirgilCardClient(TestConfig.virgilBaseUrl + TestConfig.VIRGIL_CARDS_SERVICE_PATH) + + val responseRawCard = + cardClient.publishCard(rawCard, + provider.getToken(null).stringRepresentation()) + + return Card.parse(VirgilCardCrypto(virgilCrypto), responseRawCard) + } } } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/AuthenticationTests.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/AuthenticationTests.kt new file mode 100644 index 00000000..c1786233 --- /dev/null +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/AuthenticationTests.kt @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.ethree.worker + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.virgilsecurity.android.common.callback.OnGetTokenCallback +import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.ethree.interaction.EThree +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilCrypto +import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryNotFoundException +import com.virgilsecurity.sdk.storage.DefaultKeyStorage +import com.virgilsecurity.sdk.storage.JsonKeyEntry +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.util.* + +/** + * AuthenticationTests + */ +@RunWith(AndroidJUnit4::class) +class AuthenticationTests { + + private lateinit var identity: String + private lateinit var password: String + private lateinit var crypto: VirgilCrypto + private lateinit var keyStorage: DefaultKeyStorage + private lateinit var ethree: EThree + + @Before fun setup() { + this.identity = UUID.randomUUID().toString() + this.password = UUID.randomUUID().toString() + this.crypto = VirgilCrypto() + this.keyStorage = DefaultKeyStorage(TestConfig.DIRECTORY_PATH, TestConfig.KEYSTORE_NAME) + this.ethree = EThree(identity, + object : OnGetTokenCallback { + override fun onGetToken(): String { + return TestUtils.generateTokenString(identity) + } + }, + TestConfig.context) + + assertNotNull(this.ethree) + } + + // test01 STE_8 + @Test fun cleanup() { + val keyPair = virgilCrypto.generateKeyPair() + val data = virgilCrypto.exportPrivateKey(keyPair.privateKey) + + keyStorage.store(JsonKeyEntry(ethree.identity, data)) + + ethree.cleanup() + + try { + keyStorage.load(ethree.identity) + } catch (throwable: Throwable) { + if (throwable !is KeyEntryNotFoundException) + fail() + } + } + + // test02 STE_9 + @Test fun register() { + ethree.register().execute() + + val retrievedEntry = keyStorage.load(ethree.identity) + assertNotNull(retrievedEntry) + + val cards = ethree.cardManager.searchCards(ethree.identity) + assertTrue(cards.isNotEmpty()) + } + + // test03 STE_10 + @Test fun register_with_published_card() { + val card = TestUtils.publishCard(ethree.identity) + assertNotNull(card) + + try { + ethree.register().execute() + } catch (throwable: Throwable) { + if (throwable !is EThreeException) + fail() + } + } + + // test04 STE_11 + @Test fun register_with_local_key_present() { + val keyPair = virgilCrypto.generateKeyPair() + val data = virgilCrypto.exportPrivateKey(keyPair.privateKey) + + keyStorage.store(JsonKeyEntry(ethree.identity, data)) + + try { + ethree.register().execute() + } catch (throwable: Throwable) { + if (throwable !is EThreeException) + fail() + } + } + + // test05 STE_12 + @Test(expected = EThreeException::class) fun rotate_without_published_card() { + ethree.rotatePrivateKey().execute() + } + + // test06 STE_13 + + @Test fun rotate_with_local_key_present() { + ethree.register().execute() + + try { + ethree.rotatePrivateKey().execute() + } catch (throwable: Throwable) { + if (throwable !is EThreeException) + fail() + } + } + + // test07 STE_14 + @Test fun rotate_private_key() { + val card = TestUtils.publishCard(ethree.identity) + assertNotNull(card) + + ethree.rotatePrivateKey().execute() + + val cards = ethree.cardManager.searchCards(ethree.identity) + + assertEquals(card.identifier, cards.first().previousCardId) + assertNotEquals(card.identifier, cards.first().identifier) + + val retrievedEntry = keyStorage.load(ethree.identity) + assertNotNull(retrievedEntry) + + val keyPair = crypto.importPrivateKey(retrievedEntry.value) + + val keyOne = crypto.exportPublicKey(card.publicKey) + val keyTwo = crypto.exportPublicKey(keyPair.publicKey) + val keyThree = crypto.exportPublicKey(cards.first().publicKey) + + assertFalse(Arrays.equals(keyOne, keyTwo)) + assertTrue(Arrays.equals(keyTwo, keyThree)) + } + + // test08 STE_20 + @Test fun unregister() { + try { + ethree.unregister().execute() + } catch (throwable: Throwable) { + if (throwable !is EThreeException) + fail() + } + + ethree.register().execute() + ethree.unregister().execute() + + try { + keyStorage.load(ethree.identity) + } catch (throwable: Throwable) { + if (throwable !is KeyEntryNotFoundException) + fail() + } + + val cards = ethree.cardManager.searchCards(ethree.identity) + assertTrue(cards.isEmpty()) + } + + // test09 STE_44 + @Test fun register_with_provided_key() { + val keyPair = virgilCrypto.generateKeyPair() + val data = virgilCrypto.exportPrivateKey(keyPair.privateKey) + + ethree.register(keyPair).execute() + + val keyEntry = keyStorage.load(ethree.identity) + assertNotNull(keyEntry) + assertArrayEquals(data, keyEntry.value) + + val cards = ethree.cardManager.searchCards(ethree.identity) + assertTrue(cards.isNotEmpty()) + assertEquals(ethree.identity, cards.first().identity) + } +} diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/BackupTests.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/BackupTests.kt new file mode 100644 index 00000000..1634cbd7 --- /dev/null +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/BackupTests.kt @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.ethree.worker + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.virgilsecurity.android.common.callback.OnGetTokenCallback +import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException +import com.virgilsecurity.android.common.exception.WrongPasswordException +import com.virgilsecurity.android.ethree.interaction.EThree +import com.virgilsecurity.android.ethree.interaction.async.EThreeBackupTest.Companion.WRONG_PASSWORD +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.keyknox.KeyknoxManager +import com.virgilsecurity.keyknox.client.KeyknoxClient +import com.virgilsecurity.keyknox.cloud.CloudKeyStorage +import com.virgilsecurity.keyknox.crypto.KeyknoxCrypto +import com.virgilsecurity.keyknox.exception.EntryNotFoundException +import com.virgilsecurity.keyknox.storage.SyncKeyStorage +import com.virgilsecurity.pythia.brainkey.BrainKey +import com.virgilsecurity.pythia.brainkey.BrainKeyContext +import com.virgilsecurity.pythia.client.VirgilPythiaClient +import com.virgilsecurity.pythia.crypto.VirgilPythiaCrypto +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryNotFoundException +import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider +import com.virgilsecurity.sdk.storage.DefaultKeyStorage +import com.virgilsecurity.sdk.storage.JsonKeyEntry +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.net.URL +import java.util.* + +/** + * BackupTests + */ +@RunWith(AndroidJUnit4::class) +class BackupTests { + + private lateinit var identity: String + private lateinit var password: String + private lateinit var crypto: VirgilCrypto + private lateinit var keyStorage: DefaultKeyStorage + private lateinit var ethree: EThree + + @Before fun setup() { + this.identity = UUID.randomUUID().toString() + this.password = UUID.randomUUID().toString() + this.crypto = VirgilCrypto() + this.keyStorage = DefaultKeyStorage(TestConfig.DIRECTORY_PATH, TestConfig.KEYSTORE_NAME) + this.ethree = EThree(identity, + object : OnGetTokenCallback { + override fun onGetToken(): String { + return TestUtils.generateTokenString(identity) + } + }, + TestConfig.context) + + assertNotNull(this.ethree) + } + + private fun initSyncKeyStorage(identity: String, passwordBrainKey: String): SyncKeyStorage { + val tokenProvider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback { + TestUtils.generateToken(identity) + }) + val brainKeyContext = BrainKeyContext.Builder() + .setAccessTokenProvider(tokenProvider) + .setPythiaClient(VirgilPythiaClient(TestConfig.virgilBaseUrl)) + .setPythiaCrypto(VirgilPythiaCrypto()) + .build() + val keyPair = BrainKey(brainKeyContext).generateKeyPair(passwordBrainKey) + + val syncKeyStorage = SyncKeyStorage( + identity, + keyStorage, + CloudKeyStorage( + KeyknoxManager( + KeyknoxClient(tokenProvider, URL(TestConfig.virgilBaseUrl)), + KeyknoxCrypto() + ), + listOf(keyPair.publicKey), + keyPair.privateKey + ) + ) + + syncKeyStorage.sync() + + return syncKeyStorage + } + + // test01 STE_15 + @Test fun backup_private_key() { + try { + ethree.backupPrivateKey(password).execute() + } catch (throwable: Throwable) { + if (throwable !is EThreeException) + fail() + } + + TestUtils.pause() + + val keyPair = TestConfig.virgilCrypto.generateKeyPair() + val data = TestConfig.virgilCrypto.exportPrivateKey(keyPair.privateKey) + + keyStorage.store(JsonKeyEntry(ethree.identity, data)) + + ethree.backupPrivateKey(password).execute() + + TestUtils.pause() + + val syncKeyStorage = initSyncKeyStorage(ethree.identity, password) + + val syncEntry = syncKeyStorage.retrieve(ethree.identity) + assertNotNull(syncEntry) + assertArrayEquals(data, syncEntry.value) + + TestUtils.pause() + + try { + ethree.backupPrivateKey(password).execute() + fail() + } catch (throwable: Throwable) { + // We're good + } + } + + // test02 STE_16 + @Test fun restore_private_key() { + val keyPair = TestConfig.virgilCrypto.generateKeyPair() + val data = TestConfig.virgilCrypto.exportPrivateKey(keyPair.privateKey) + + TestUtils.publishCard(ethree.identity) + + val syncKeyStorage = initSyncKeyStorage(ethree.identity, password) + syncKeyStorage.store(ethree.identity, data) + + TestUtils.pause() + + try { + ethree.restorePrivateKey(WRONG_PASSWORD).execute() + } catch (throwable: Throwable) { + if (throwable !is WrongPasswordException) + fail() + } + + + TestUtils.pause() + + ethree.restorePrivateKey(password).execute() + + val retrievedEntry = keyStorage.load(ethree.identity) + assertNotNull(retrievedEntry) + assertArrayEquals(data, retrievedEntry.value) + + TestUtils.pause() + + try { + ethree.restorePrivateKey(password).execute() + fail() + } catch (throwable: Throwable) { + // We're good + } + } + + // test03 STE_17 + @Test fun change_private_key() { + val keyPair = TestConfig.virgilCrypto.generateKeyPair() + val data = TestConfig.virgilCrypto.exportPrivateKey(keyPair.privateKey) + + TestUtils.publishCard(ethree.identity) + + val syncKeyStorage = initSyncKeyStorage(ethree.identity, password) + syncKeyStorage.store(ethree.identity, data) + + TestUtils.pause() + + val passwordNew = UUID.randomUUID().toString() + + ethree.changePassword(password, passwordNew).execute() + + TestUtils.pause() + + try { + ethree.restorePrivateKey(password).execute() + } catch (throwable: Throwable) { + if (throwable !is WrongPasswordException) + fail() + } + + TestUtils.pause() + + ethree.restorePrivateKey(passwordNew).execute() + + val retrievedEntry = keyStorage.load(ethree.identity) + assertNotNull(retrievedEntry) + assertArrayEquals(data, retrievedEntry.value) + } + + // test04 STE_18 + @Test fun reset_private_key_backup() { + try { + ethree.resetPrivateKeyBackup(password).execute() + } catch (throwable: Throwable) { + if (throwable !is PrivateKeyNotFoundException) + fail() + } + + val keyPair = TestConfig.virgilCrypto.generateKeyPair() + val data = TestConfig.virgilCrypto.exportPrivateKey(keyPair.privateKey) + + TestUtils.pause() + + val syncKeyStorage = initSyncKeyStorage(ethree.identity, password) + syncKeyStorage.store(ethree.identity, data) + + TestUtils.pause() + + ethree.resetPrivateKeyBackup(password).execute() + + syncKeyStorage.sync() + + try { + syncKeyStorage.retrieve(ethree.identity) + fail() + } catch (throwable: Throwable) { + // We're good + } + } + + // test05 STE_19 + @Test fun reset_private_key_backup_no_password() { + try { + ethree.resetPrivateKeyBackup(password).execute() + } catch (throwable: Throwable) { + if (throwable !is PrivateKeyNotFoundException) + fail() + } + + val keyPair = TestConfig.virgilCrypto.generateKeyPair() + val data = TestConfig.virgilCrypto.exportPrivateKey(keyPair.privateKey) + + TestUtils.pause() + + val syncKeyStorage = initSyncKeyStorage(ethree.identity, password) + syncKeyStorage.store(ethree.identity, data) + + TestUtils.pause() + + ethree.resetPrivateKeyBackup().execute() + + syncKeyStorage.sync() + + try { + syncKeyStorage.retrieve(ethree.identity) + fail() + } catch (throwable: Throwable) { + // We're good + } + } + + companion object { + private const val WRONG_PASSWORD = "WRONG_PASSWORD" + } +} diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/GroupTests.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/GroupTests.kt new file mode 100644 index 00000000..58dfcbe7 --- /dev/null +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/GroupTests.kt @@ -0,0 +1,666 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.ethree.worker + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import com.virgilsecurity.android.common.callback.OnGetTokenCallback +import com.virgilsecurity.android.common.exception.* +import com.virgilsecurity.android.common.model.* +import com.virgilsecurity.android.ethree.interaction.EThree +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.crypto.foundation.Base64 +import com.virgilsecurity.sdk.common.TimeSpan +import com.virgilsecurity.sdk.crypto.VirgilAccessTokenSigner +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.jwt.JwtGenerator +import com.virgilsecurity.sdk.storage.DefaultKeyStorage +import com.virgilsecurity.sdk.storage.JsonKeyEntry +import org.junit.Assert.* +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import java.io.InputStreamReader +import java.util.* +import java.util.concurrent.TimeUnit + +@RunWith(AndroidJUnit4::class) +class GroupTests { + private lateinit var crypto: VirgilCrypto + private lateinit var ethree: EThree + private lateinit var groupId: Data + + @Before + fun setup() { + this.crypto = TestConfig.virgilCrypto + + this.ethree = createEThree() + this.groupId = Data(this.crypto.generateRandomData(100)) + } + + @Test + fun ste26() { + // Create with invalid participants count. Should throw error + val card = this.ethree.findUser(ethree.identity).get() + + try { + val users = FindUsersResult() + users[ethree.identity] = card + this.ethree.createGroup(groupId, users).get() + fail() + } catch (e: InvalidParticipantsCountGroupException) { + } + + val users = FindUsersResult() + for (i in 0 until 100) { + val identity = UUID.randomUUID().toString() + users[identity] = card + } + try { + this.ethree.createGroup(groupId, users).get() + fail() + } catch (e: InvalidParticipantsCountGroupException) { + } + + val firstEntry = users.entries.first() + val newUsers = FindUsersResult() + newUsers[firstEntry.key] = firstEntry.value + + val group = this.ethree.createGroup(groupId, newUsers).get() + assertEquals(2, group.participants.size) + assertTrue(group.participants.contains(this.ethree.identity)) + assertTrue(group.participants.contains(newUsers.keys.first())) + } + + @Test + fun ste27() { + // createGroup should add self + val ethree2 = createEThree() + + val groupId2 = Data(this.crypto.generateRandomData(100)) + + val users = this.ethree.findUsers(listOf(ethree.identity, ethree2.identity)).get() + + val group1 = this.ethree.createGroup(groupId, users).get() + + val users2 = FindUsersResult() + val ethree2Card = users.get(ethree2.identity) + assertNotNull(ethree2Card) + users2[ethree2.identity] = ethree2Card!! + val group2 = this.ethree.createGroup(groupId2, users2).get() + + assertTrue(group2.participants.contains(ethree.identity)) + assertEquals(group1.participants, group2.participants) + } + + @Test + fun ste28() { + // groupId should not be short + val ethree2 = createEThree() + val invalidGroupId = Data(this.crypto.generateRandomData(5)) + + val lookup = this.ethree.findUsers(listOf(ethree2.identity)).get() + + try { + this.ethree.createGroup(invalidGroupId, lookup).get() + fail() + } catch (e: GroupIdTooShortException) { + } + } + + @Test + fun ste29() { + // get group + val ethree2 = createEThree() + assertNull(this.ethree.getGroup(groupId)) + + val lookup = this.ethree.findUsers(listOf(ethree2.identity)).get() + + val group = this.ethree.createGroup(groupId, lookup).get() + assertNotNull(group) + + val cachedGroup = this.ethree.getGroup(groupId) + assertNotNull(cachedGroup) + assertEquals(cachedGroup!!.participants, group.participants) + assertEquals(cachedGroup.initiator, group.initiator) + } + + @Test + fun ste30() { + // load_group + val ethree2 = createEThree() + + val lookup = this.ethree.findUsers(listOf(ethree2.identity)).get() + + val group1 = this.ethree.createGroup(groupId, lookup).get() + + val card = ethree2.findUser(this.ethree.identity).get() + + val group2 = ethree2.loadGroup(groupId, card).get() + assertNotNull(group2) + assertEquals(group1.participants, group2.participants) + assertEquals(group1.initiator, group2.initiator) + } + + @Test + fun ste31() { + // load alien or non-existing group should throw error + val ethree2 = createEThree() + val ethree3 = createEThree() + + val card1 = ethree2.findUser(this.ethree.identity).get() + + try { + ethree2.loadGroup(groupId, card1).get() + fail() + } catch (e: GroupNotFoundException) { + } + + val lookup = this.ethree.findUsers(listOf(ethree3.identity)).get() + + this.ethree.createGroup(groupId, lookup).get() + + try { + ethree2.loadGroup(groupId, card1).get() + fail() + } catch (e: GroupNotFoundException) { + } + } + + @Test + fun ste32() { + // actions on deleted group should throw error + val ethree2 = createEThree() + + val lookup = this.ethree.findUsers(listOf(ethree2.identity)).get() + this.ethree.createGroup(groupId, lookup).get() + val card1 = ethree2.findUser(this.ethree.identity).get() + val group2 = ethree2.loadGroup(groupId, card1).get() + + this.ethree.deleteGroup(groupId).execute() + val gr = this.ethree.getGroup(groupId) + assertNull(gr) + + try { + this.ethree.loadGroup(groupId, card1).get() + fail() + } catch (e: GroupNotFoundException) { + } + + try { + group2.update().execute() + fail() + } catch (e: GroupNotFoundException) { + } + + try { + ethree2.loadGroup(groupId, card1).get() + fail() + } catch (e: GroupNotFoundException) { + } + + assertNull(ethree2.getGroup(groupId)) + } + + @Test + fun ste33() { + // add more than max should throw error + val participants = mutableSetOf() + + for (i in 0 until 100) { + val identity = UUID.randomUUID().toString() + participants.add(identity) + } + + val sessionId = Data(this.crypto.generateRandomData(32)) + + val ticket = Ticket(this.crypto, sessionId, participants) + val rawGroup = RawGroup(GroupInfo(this.ethree.identity), listOf(ticket)) + + assertNotNull(this.ethree.groupManager) + val group = Group(rawGroup, this.crypto, ethree.keyStorageLocal, + this.ethree.groupManager!!, this.ethree.lookupManager) + + val card = TestUtils.publishCard() + + try { + group.add(card).execute() + fail() + } catch (e: InvalidParticipantsCountGroupException) { + } + } + + @Test + fun ste34() { + // remove last participant should throw error + val ethree2 = createEThree() + + val lookup = this.ethree.findUsers(listOf(ethree2.identity)).get() + val card = lookup[ethree2.identity] + assertNotNull(card) + + val group1 = this.ethree.createGroup(groupId, lookup).get() + + try { + group1.remove(card!!).execute() + fail() + } catch (e: InvalidParticipantsCountGroupException) { + } + } + + @Test + fun ste35() { + // remove + val ethree2 = createEThree() + val ethree3 = createEThree() + + val lookup = this.ethree.findUsers(listOf(ethree2.identity, ethree3.identity)).get() + val card2 = lookup[ethree2.identity] + assertNotNull(card2) + + val group1 = this.ethree.createGroup(groupId, lookup).get() + + val card1 = ethree2.findUser(this.ethree.identity).get() + val group2 = ethree2.loadGroup(groupId, card1).get() + val group3 = ethree3.loadGroup(groupId, card1).get() + + group1.remove(card2!!).execute() + assertFalse(group1.participants.contains(ethree2.identity)) + + group3.update().execute() + assertFalse(group3.participants.contains(ethree2.identity)) + + try { + group2.update().execute() + fail() + } catch (e: GroupNotFoundException) { + } + + try { + ethree2.loadGroup(groupId, card1).get() + fail() + } catch (e: GroupNotFoundException) { + } + + assertNull(ethree2.getGroup(groupId)) + } + + @Test + fun ste36() { + // change group by noninitiator should_throw_error + val ethree2 = createEThree() + val ethree3 = createEThree() + val ethree4 = createEThree() + val identities = listOf(ethree2.identity, ethree3.identity) + + val lookup = this.ethree.findUsers(identities).get() + this.ethree.createGroup(this.groupId, lookup).get() + val card3 = lookup[ethree3.identity] + assertNotNull(card3) + + val ethree1Card = ethree2.findUser(this.ethree.identity).get() + val group2 = ethree2.loadGroup(this.groupId, ethree1Card).get() + + try { + ethree2.deleteGroup(groupId).execute() + fail() + } catch (e: PermissionDeniedGroupException) { + } + + try { + group2.remove(card3!!).execute() + fail() + } catch (e: PermissionDeniedGroupException) { + } + + try { + val ethree4Card = ethree2.findUser(ethree4.identity).get() + group2.add(ethree4Card).execute() + fail() + } catch (e: PermissionDeniedGroupException) { + } + } + + @Test + fun ste37() { + // add + val ethree2 = createEThree() + val ethree3 = createEThree() + + val lookup = this.ethree.findUsers(listOf(ethree2.identity)).get() + + val card1 = ethree2.findUser(this.ethree.identity).get() + + val group1 = this.ethree.createGroup(this.groupId, lookup).get() + + val group2 = ethree2.loadGroup(this.groupId, card1).get() + + val card3 = this.ethree.findUser(ethree3.identity).get() + group1.add(card3).execute() + + val participants = setOf(this.ethree.identity, ethree2.identity, ethree3.identity) + assertEquals(participants, group1.participants) + + group2.update().execute() + + val group3 = ethree3.loadGroup(this.groupId, card1).get() + + assertEquals(participants, group2.participants) + assertEquals(participants, group3.participants) + } + + @Test + fun ste38() { + // decrypt with old card should throw error + val ethree2 = createEThree() + + val lookup = this.ethree.findUsers(listOf(ethree2.identity)).get() + val group1 = this.ethree.createGroup(groupId, lookup).get() + + val card1 = ethree2.findUser(this.ethree.identity).get() + val group2 = ethree2.loadGroup(this.groupId, card1).get() + + val card2 = this.ethree.findUser(ethree2.identity).get() + + ethree2.cleanup() + ethree2.rotatePrivateKey().execute() + + val encrypted = group2.encrypt("Some text") + + try { + group1.decrypt(encrypted, card2) + fail() + } catch (e: VerificationFailedGroupException) { + } + } + + @Test + fun ste39() { + // integration_encryption + val ethree2 = createEThree() + val ethree3 = createEThree() + + val identities = listOf(ethree2.identity) + + val card1 = ethree2.findUser(this.ethree.identity).get() + + // User1 creates group, encrypts + val lookup = this.ethree.findUsers(identities).get() + val group1 = this.ethree.createGroup(this.groupId, lookup).get() + + val message1 = UUID.randomUUID().toString() + val encrypted1 = group1.encrypt(message1) + val selfDecrypted1 = group1.decrypt(encrypted1, card1) + assertEquals(message1, selfDecrypted1) + + // User2 updates group, decrypts + val group2 = ethree2.loadGroup(this.groupId, card1).get() + val decrypted1 = group2.decrypt(encrypted1, card1) + assertEquals(message1, decrypted1) + + // Add User3, encrypts + val card3 = this.ethree.findUser(ethree3.identity).get() + group1.add(card3).execute() + + val message2 = UUID.randomUUID().toString() + val encrypted2 = group1.encrypt(message2) + val selfDecrypted2 = group1.decrypt(encrypted2, card1) + assertEquals(message2, selfDecrypted2) + + // Other updates, decrypts + group2.update() + val group3 = ethree3.loadGroup(groupId, card1).get() + + val decrypted22 = group2.decrypt(encrypted2, card1) + assertEquals(message2, decrypted22) + + val decrypted23 = group3.decrypt(encrypted2, card1) + assertEquals(message2, decrypted23) + + // Remove User2 + group1.remove(lookup).execute() + + val message3 = UUID.randomUUID().toString() + val encrypted3 = group1.encrypt(message3) + val selfDecrypted3 = group1.decrypt(encrypted3, card1) + assertEquals(message3, selfDecrypted3) + + // Other updates, decrypts + try { + group2.decrypt(encrypted3, card1) + fail() + } catch (e: GroupException) { + } + + group3.update().execute() + val decrypted3 = group3.decrypt(encrypted3, card1) + assertEquals(message3, decrypted3) + + // User3 rotates key + ethree3.cleanup() + ethree3.rotatePrivateKey().execute() + + try { + group3.update().execute() + fail() + } catch (e: Exception) { // TODO do we need GroupException here? + } + + assertNull(ethree3.getGroup(this.groupId)) + + try { + ethree3.loadGroup(groupId, card1).get() + fail() + } catch (e: Exception) { // TODO do we need GroupException here? + } + + // User 1 encrypts, reAdds User3 + val message4 = UUID.randomUUID().toString() + val encrypted4 = group1.encrypt(message4) + + val newCard3 = this.ethree.findUser(ethree3.identity, true).get() + group1.reAdd(newCard3).execute() + + val newGroup3 = ethree3.loadGroup(this.groupId, card1).get() + val decrypted4 = newGroup3.decrypt(encrypted4, card1) + assertEquals(message4, decrypted4) + } + + @Test + fun ste42() { + // decrypt with old group should throw error + val ethree2 = createEThree() + val ethree3 = createEThree() + + val lookup = this.ethree.findUsers(listOf(ethree2.identity, ethree3.identity)).get() + val card3 = lookup[ethree3.identity] + assertNotNull(card3) + + val group1 = this.ethree.createGroup(this.groupId, lookup).get() + + val card1 = ethree2.findUser(this.ethree.identity).get() + val group2 = ethree2.loadGroup(this.groupId, card1).get() + + group1.remove(card3!!).execute() + + val message = UUID.randomUUID().toString() + val encrypted = group1.encrypt(message) + + try { + group2.decrypt(encrypted, card1) + fail() + } catch (e: GroupIsOutdatedGroupException) { + } + } + + @Test + fun ste43() { + // decrypt with old group should throw error + val ethree2 = createEThree() + + val lookup = this.ethree.findUsers(listOf(ethree2.identity)).get() + val group1 = this.ethree.createGroup(this.groupId, lookup).get() + + val card1 = ethree2.findUser(this.ethree.identity).get() + val group2 = ethree2.loadGroup(this.groupId, card1).get() + + val date1 = Date() + val message1 = UUID.randomUUID().toString() + val encrypted1 = group2.encrypt(message1) + + Thread.sleep(1000) + + ethree2.cleanup() + ethree2.rotatePrivateKey().execute() + + val date2 = Date() + val message2 = UUID.randomUUID().toString() + val encrypted2 = group2.encrypt(message2) + + val card2 = this.ethree.findUser(ethree2.identity, true).get() + + try { + group1.decrypt(encrypted1, card2) + fail() + } catch (e: VerificationFailedGroupException) { + } + + try { + group1.decrypt(encrypted1, card2, date2) + fail() + } catch (e: VerificationFailedGroupException) { + } + + val dectypted1 = group1.decrypt(encrypted1, card2, date1) + assertEquals(message1, dectypted1) + + try { + group1.decrypt(encrypted2, card2, date1) + fail() + } catch (e: VerificationFailedGroupException) { + } + + val dectypted2 = group1.decrypt(encrypted2, card2, date2) + assertEquals(message2, dectypted2) + } + + @Test + fun ste45() { + // Compatability test + val compatDataStream = + this.javaClass.classLoader?.getResourceAsStream("compat_data.json") + val compatJson = JsonParser().parse(InputStreamReader(compatDataStream)) as JsonObject + val groupCompatJson = compatJson.getAsJsonObject("Group") + + val keyStorage = DefaultKeyStorage(TestConfig.DIRECTORY_PATH, TestConfig.KEYSTORE_NAME) + if (keyStorage.exists(groupCompatJson.get("Identity").asString)) { + keyStorage.delete(groupCompatJson.get("Identity").asString) + } + + val privateKeyData = Base64.decode(groupCompatJson.get("PrivateKey").asString.toByteArray()) + keyStorage.store(JsonKeyEntry(groupCompatJson.get("Identity").asString, privateKeyData)) + + val privateKeyCompat = crypto.importPrivateKey(Base64.decode(compatJson.get("ApiPrivateKey").asString.toByteArray())) + val jwtGenerator = JwtGenerator( + compatJson.get("AppId").asString, + privateKeyCompat.privateKey, + compatJson.get("ApiKeyId").asString, + TimeSpan.fromTime(600, TimeUnit.SECONDS), + VirgilAccessTokenSigner(TestConfig.virgilCrypto) + ) + + val tokenCallback = object : OnGetTokenCallback { + override fun onGetToken(): String { + return jwtGenerator + .generateToken(groupCompatJson.get("Identity").asString) + .stringRepresentation() + } + } + + val ethree = EThree(groupCompatJson.get("Identity").asString, + tokenCallback, + TestConfig.context) + + // Load group + val initiatorCard = ethree.findUser(groupCompatJson.get("Initiator").asString).get() + + val groupIdData = Data.fromBase64String(groupCompatJson.get("GroupId").asString) + val group = ethree.loadGroup(groupIdData, initiatorCard).get() + + val compatParticipants = + groupCompatJson.get("Participants").asJsonArray.map { it.asString }.toSet() + assertTrue(group.participants.sorted() == compatParticipants.sorted()) + + val decrypted = group.decrypt(groupCompatJson.get("EncryptedText").asString, initiatorCard) + val originCompatText = groupCompatJson.get("OriginText").asString + + assertEquals(originCompatText, decrypted) + } + + @Test + fun ste46() { + // string identifier + val ethree2 = createEThree() + + val identifier = Data(this.crypto.generateRandomData(32)) + + val result = this.ethree.findUsers(listOf(ethree2.identity)).get() + this.ethree.createGroup(identifier, result).get() + + val card1 = ethree2.findUser(this.ethree.identity).get() + ethree2.loadGroup(identifier, card1) + + this.ethree.getGroup(identifier) + ethree2.getGroup(identifier) + + this.ethree.deleteGroup(identifier) + } + + private fun createEThree(): EThree { + val identity = UUID.randomUUID().toString() + val tokenCallback = object : OnGetTokenCallback { + override fun onGetToken(): String { + return TestUtils.generateTokenString(identity) + } + } + + val ethree = EThree(identity, tokenCallback, TestConfig.context) + ethree.register().execute() + return ethree + } +} diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/PeerToPeerTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/PeerToPeerTest.kt new file mode 100644 index 00000000..bb186711 --- /dev/null +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/PeerToPeerTest.kt @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.ethree.worker + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.virgilsecurity.android.common.callback.OnGetTokenCallback +import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.model.FindUsersResult +import com.virgilsecurity.android.ethree.interaction.EThree +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.common.model.Data +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.storage.DefaultKeyStorage +import com.virgilsecurity.sdk.utils.ConvertionUtils +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.util.* + +/** + * PeerToPeerTest + */ +@RunWith(AndroidJUnit4::class) +class PeerToPeerTest { + + private lateinit var identity: String + private lateinit var crypto: VirgilCrypto + private lateinit var ethree: EThree + + @Before fun setup() { + this.identity = UUID.randomUUID().toString() + this.crypto = VirgilCrypto() + this.ethree = EThree(identity, + object : OnGetTokenCallback { + override fun onGetToken(): String { + return TestUtils.generateTokenString(identity) + } + }, + TestConfig.context) + + assertNotNull(this.ethree) + } + + // test01 STE_3 + @Test fun encrypt_decrypt() { + val identityTwo = UUID.randomUUID().toString() + + ethree.register().execute() + + val ethreeTwo = EThree(identityTwo, + object : OnGetTokenCallback { + override fun onGetToken(): String { + return TestUtils.generateTokenString(identityTwo) + } + }, + TestConfig.context) + + assertNotNull(ethreeTwo) + + ethreeTwo.register().execute() + + val card = ethree.findUser(ethreeTwo.identity).get() + assertNotNull(card) + + val encrypted = ethree.encrypt(TEXT, card) + + val otherCard = TestUtils.publishCard() + + try { + ethreeTwo.decrypt(encrypted, otherCard) + fail() + } catch (throwable: Throwable) { + // We're good + } + + val cardTwo = ethreeTwo.findUser(ethree.identity).get() + val decryptedTwo = ethreeTwo.decrypt(encrypted, cardTwo) + assertEquals(TEXT, decryptedTwo) + } + + // test02 STE_4 + @Test(expected = IllegalArgumentException::class) fun encrypt_empty_keys() { + ethree.register().execute() + + ethree.encrypt(TEXT, FindUsersResult()) + } + + // test03 STE_5 + @Test fun decrypted_text_not_verified() { + ethree.register().execute() + + val plainData = TEXT.toByteArray() + val card = ethree.findUser(ethree.identity).get() + val encryptedData = crypto.encrypt(plainData, card.publicKey) + val encryptedString = ConvertionUtils.toBase64String(encryptedData) + + val otherCard = TestUtils.publishCard() + + try { + ethree.decrypt(encryptedString, otherCard) + fail() + } catch (throwable: Throwable) { + // We're food + } + } + + // test04 STE_6 + @Test fun encrypt_decrypt_without_private_key() { + val keyStorage = DefaultKeyStorage(TestConfig.DIRECTORY_PATH, TestConfig.KEYSTORE_NAME) + assertFalse(keyStorage.exists(ethree.identity)) +// keyStorage.delete(ethree.identity) // FIXME check why we delete key in swift + + val card = TestUtils.publishCard() + + try { + ethree.encrypt(TEXT, FindUsersResult(mapOf(ethree.identity to card))) + } catch (throwable: Throwable) { + if (throwable !is EThreeException) + fail() + } + + try { + ethree.decrypt(Data(TEXT.toByteArray()).toBase64String(), card) + } catch (throwable: Throwable) { + if (throwable !is EThreeException) + fail() + } + } + + // test05 STE_22 + @Test fun encrypt_decrypt_stream() { + ethree.register().execute() + + val inputStream = ByteArrayInputStream(TEXT.toByteArray()) + val outputStream = ByteArrayOutputStream() + + ethree.encrypt(inputStream, outputStream) + val encryptedData = outputStream.toByteArray() + + val inputStreamTwo = ByteArrayInputStream(encryptedData) + val outputStreamTwo = ByteArrayOutputStream() + + ethree.decrypt(inputStreamTwo, outputStreamTwo) + + val decryptedData = outputStreamTwo.toByteArray() + + assertArrayEquals(TEXT.toByteArray(), decryptedData) + } + + // test06 STE_40 + @Test fun decrypt_encrypted_text_with_old_card() { + val identityTwo = UUID.randomUUID().toString() + + ethree.register().execute() + + val ethreeTwo = EThree(identityTwo, + object : OnGetTokenCallback { + override fun onGetToken(): String { + return TestUtils.generateTokenString(identityTwo) + } + }, + TestConfig.context) + + assertNotNull(ethreeTwo) + + ethreeTwo.register().execute() + + val card = ethree.findUser(ethreeTwo.identity).get() + assertNotNull(card) + + val dateOne = Date() + + TestUtils.pause(1000) // 1 sec + + val encrypted = ethree.encrypt(TEXT, FindUsersResult(mapOf(card.identity to card))) + + ethree.cleanup() + + ethree.rotatePrivateKey().execute() + + val dateTwo = Date() + + val encryptedTwo = + ethree.encrypt(TEXT + TEXT, FindUsersResult(mapOf(card.identity to card))) + + val cardTwo = ethreeTwo.findUser(ethree.identity).get() + assertNotNull(cardTwo) + + try { + ethreeTwo.decrypt(encrypted, cardTwo) + fail() + } catch (throwable: Throwable) { + // We're good + } + + try { + ethreeTwo.decrypt(encrypted, cardTwo, dateTwo) + fail() + } catch (throwable: Throwable) { + // We're good + } + + val decrypted = ethreeTwo.decrypt(encrypted, cardTwo, dateOne) + assertEquals(TEXT, decrypted) + + try { + ethreeTwo.decrypt(encryptedTwo, cardTwo, dateOne) + fail() + } catch (throwable: Throwable) { + // We're good + } + + val decryptedTwo = ethreeTwo.decrypt(encryptedTwo, cardTwo, dateTwo) + assertEquals(TEXT + TEXT, decryptedTwo) + } + + // test07 STE_41 + @Test fun encrypt_decrypt_deprecated_methods() { + val identityTwo = UUID.randomUUID().toString() + + ethree.register().execute() + + val ethreeTwo = EThree.initialize(TestConfig.context, + object : OnGetTokenCallback { + override fun onGetToken(): String { + return TestUtils.generateTokenString(identityTwo) + } + }).get() + + ethreeTwo.register().execute() + + val lookupResult = ethree.lookupPublicKeys(ethreeTwo.identity).get() + assertTrue(lookupResult.isNotEmpty()) + + val encrypted = ethree.encrypt(TEXT, lookupResult) + + val lookupResultTwo = ethreeTwo.lookupPublicKeys(ethree.identity).get() + assertTrue(lookupResultTwo.isNotEmpty()) + + val publicKey = lookupResultTwo[ethree.identity] + ?: error("publicKey should not be null") + + val decrypted = ethreeTwo.decrypt(encrypted, publicKey) + + assertEquals(TEXT, decrypted) + } + + companion object { + private const val TEXT = "Hello, my name is text. I am here to be encrypted (:" + } +} diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/SearchTests.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/SearchTests.kt new file mode 100644 index 00000000..d72221d8 --- /dev/null +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/SearchTests.kt @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.ethree.worker + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.virgilsecurity.android.common.callback.OnGetTokenCallback +import com.virgilsecurity.android.common.callback.OnKeyChangedCallback +import com.virgilsecurity.android.common.exception.FindUsersException +import com.virgilsecurity.android.ethree.interaction.EThree +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.exception.EmptyArgumentException +import com.virgilsecurity.sdk.storage.DefaultKeyStorage +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.util.* + +/** + * SearchTests + */ +@RunWith(AndroidJUnit4::class) +class SearchTests { + + private lateinit var identity: String + private lateinit var password: String + private lateinit var crypto: VirgilCrypto + private lateinit var keyStorage: DefaultKeyStorage + private lateinit var ethree: EThree + + @Before fun setup() { + this.identity = UUID.randomUUID().toString() + this.password = UUID.randomUUID().toString() + this.crypto = VirgilCrypto() + this.keyStorage = DefaultKeyStorage(TestConfig.DIRECTORY_PATH, TestConfig.KEYSTORE_NAME) + this.ethree = setupDevice() + + assertNotNull(this.ethree) + } + + private fun setupDevice(identity: String? = null): EThree { + val identityNew = identity ?: UUID.randomUUID().toString() + + val tokenCallback = object : OnGetTokenCallback { + override fun onGetToken(): String { + return TestUtils.generateTokenString(identityNew) + } + } + + val ethree = EThree(identityNew, tokenCallback, TestConfig.context) + + ethree.register().execute() + + return ethree + } + + // test01 STE_1 + @Test fun find_users() { + val cardOne = TestUtils.publishCard() + val cardTwo = TestUtils.publishCard() + val cardThree = TestUtils.publishCard(cardTwo.identity, cardTwo.identifier) + + val lookup = ethree.findUsers(listOf(cardOne.identity, + cardTwo.identity, + cardThree.identity)).get() + + assertEquals(2, lookup.size) + assertTrue(lookup.filterValues { it.identifier == cardOne.identifier }.isNotEmpty()) + assertTrue(lookup.filterValues { it.identifier == cardThree.identifier }.isNotEmpty()) + } + + // test02 STE_2 + @Test fun find_empty_list() { + try { + ethree.findUsers(listOf()) + } catch (throwable: Throwable) { + if (throwable !is EmptyArgumentException) + fail() + } + } + + // test03 STE_23 + @Test fun find_cached_user() { + val cardOne = TestUtils.publishCard() + + val foundCardOne = ethree.findUser(cardOne.identity).get() + assertEquals(cardOne.identifier, foundCardOne.identifier) + + val cardTwo = TestUtils.publishCard(cardOne.identity, cardOne.identifier) + + val foundCardTwo = ethree.findUser(cardOne.identity).get() + assertEquals(foundCardOne.identifier, foundCardTwo.identifier) + + val foundCardThree = ethree.findUser(cardOne.identity, true).get() + assertEquals(cardTwo.identifier, foundCardThree.identifier) + + val cachedCard = ethree.findCachedUser(cardOne.identity).get() ?: error("") + assertEquals(cachedCard.identifier, cardTwo.identifier) + } + + // test04 STE_24 + @Test fun find_duplicate_cards() { + val cardOne = TestUtils.publishCard() + TestUtils.publishCard(cardOne.identity) + + try { + ethree.findUser(cardOne.identity).get() + } catch (throwable: Throwable) { + if (throwable !is FindUsersException) + fail() + } + } + + // test05 STE_25 + @Test fun ethree_with_key_changed_callback() { + val card = TestUtils.publishCard() + + val onKeyChangedCallback = object : OnKeyChangedCallback { + var called = false + + override fun keyChanged(identity: String) { + assertEquals(card.identity, identity) + called = true + } + } + + ethree.findUser(card.identity).get() + + val cardNew = TestUtils.publishCard(card.identity, card.identifier) + val tokenCallback = object : OnGetTokenCallback { + override fun onGetToken(): String { + return TestUtils.generateTokenString(ethree.identity) + } + } + + val ethreeNew = EThree(ethree.identity, + tokenCallback, + TestConfig.context, + onKeyChangedCallback) + + TestUtils.pause(3 * 1000) // 3 sec + + assertTrue(onKeyChangedCallback.called) + + val cardCached = ethreeNew.findCachedUser(card.identity).get() ?: error("") + + assertEquals(cardNew.identifier, cardCached.identifier) + } +} diff --git a/tests/src/main/AndroidManifest.xml b/tests/src/main/AndroidManifest.xml index 8b32b8ef..f05b7e3f 100644 --- a/tests/src/main/AndroidManifest.xml +++ b/tests/src/main/AndroidManifest.xml @@ -32,7 +32,9 @@ --> + xmlns:tools="http://schemas.android.com/tools" + package="com.virgilsecurity.android.ethree"> + QPSA}A+@Y`~^P$-<|+h_UqP)_rmvxkGcE`GHCuXfK1zxn$2m!*Fc&c6Ipp~RQ| zS~|&hV1WWq017|>C;$bZ02F`%Pyh-*0VwdF1*EeVS2tJ1Fr4|pF7$(05+?im;}Pqu z{Pkz73K$KPMVSLu`Ei4CM=l>5y?l88rg8e>>epArcO8WOM;)EL??{(LMgP#>yA~h! zbo2JCcz$(tReU>295)V*BOG5&-glufjW&2c@^#ntPpV&Q19UdH*I{k^kE=EvjvV?g)l zu{zw#e}168%(v6?KNl|a%OI~{fdWtf3P1rU00n;00^fgicK-HK|NO^cKv4umI!GPE zF*k5YGQe?{@B%WZ*J_wwBkDAYdbC@|ks4jYaVkK)04Ff7RU?Bi{f24HQ+X!O6jCyv;9dN>zRV|VhQQ?#I-n$qrOQZj-`@^u~kmxJ3vPi1SE3Q zXJr;aq)5ctI>^+>PE^ohl?;&5gxRS;!47fScq_IoHSY8D*k?sb(sWGbbVAnbv|}N! zuU}oS!_mx1HmhKL{mt*b`*4__y~Kl*Q|y*4_C?0jxdtP04TS-Us3KjNL+#MRsW=!9 zs#~Qpm#nE;-?prZF{KtNPq0)*Vq%x<-VvH)&fTuJpROXi5_jTwr^o`@QPs}eY7D)* zp1F%mCVdDeSmlGSgLruT=G7A;;`n&qSf|m7HSmhu6Ve$~yW`ubKB)59%pO$9c_r1x zBcILUknPsy(nJdC8=X;*Fw5A#KCeD2%V)eEa5y z%adbj{-Nt)0p-=*ARxV=Lm)#3b3DxT>NJHSWQ{@)*9&}%ay`<`GtqGo0znCnLyKmS)6Ebe=7?TuIXy3g-N7D za;RW(yRXN6ZMLvoE#7&cg}H(+1=?6MOn!Du%~@mLxB3egH!=&8$KF&OI;IkKLXqrG zZBo+wv5bM8ZGyDJMLT;k!evuIY%PvuP9o*3HI{^aC{H4!!+E4VwTX_Fx5=W^&HyH9 zM%3pw&MWN9aNJFvl8xv$z+LaTfI3V^%msF znWp<*z=b}uehBAhOb0&jdn3sOgKBCd%U#=Grt2sryX|f|2dhvL6Gf5Af`3 z0`LEyO$a-H0#E=7KmjNK1)u;FfC5ke3P6Ehk^sE_|H-RyQTkiqwDfJE^vBWDDbHQFHiO2Q~q3cc4+vNzdD^hwc+8%0WY5t?mqz} CuB$iz literal 0 HcmV?d00001 diff --git a/testsenclave/build.gradle b/testsenclave/build.gradle index a119405e..d9cbdd70 100644 --- a/testsenclave/build.gradle +++ b/testsenclave/build.gradle @@ -42,12 +42,13 @@ def API_PUBLIC_KEY_ID = hasProperty('API_PUBLIC_KEY_ID') ? API_PUBLIC_KEY_ID : S def VIRGIL_BASE_URL = hasProperty('VIRGIL_BASE_URL') ? VIRGIL_BASE_URL : System.getenv('VIRGIL_BASE_URL') android { - compileSdkVersion 28 + compileSdkVersion androidOptions.compileSdkVersion defaultConfig { applicationId "com.virgilsecurity.android.testsenclave" - minSdkVersion 23 - targetSdkVersion 28 - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + minSdkVersion androidOptions.minSdkVersion + targetSdkVersion androidOptions.targetSdkVersion +// testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { buildTypes.each { @@ -65,12 +66,16 @@ dependencies { implementation project(':ethree-kotlin') implementation project(':ethree-enclave') + // Virgil + androidTestImplementation "com.virgilsecurity.sdk:android-utils:$versions.virgilSdk" + // Kotlin androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" - implementation 'com.android.support:appcompat-v7:28.0.0' + implementation "com.android.support:appcompat-v7:$versions.appCompat" // Tests core testImplementation "junit:junit:$versions.junit" - androidTestImplementation "com.android.support.test:runner:$versions.testsRunner" - androidTestImplementation "com.android.support.test.espresso:espresso-core:$versions.espresso" + androidTestImplementation "androidx.test.ext:junit:$versions.testsRunner" + androidTestImplementation "androidx.test:runner:$versions.testsRunner" + androidTestImplementation "androidx.test:rules:$versions.testsRunner" } diff --git a/testsenclave/src/androidTest/java/com/virgilsecurity/android/testsenclave/EThreeEnclaveTest.kt b/testsenclave/src/androidTest/java/com/virgilsecurity/android/testsenclave/EThreeEnclaveTest.kt index d4a2c0e8..11a97463 100644 --- a/testsenclave/src/androidTest/java/com/virgilsecurity/android/testsenclave/EThreeEnclaveTest.kt +++ b/testsenclave/src/androidTest/java/com/virgilsecurity/android/testsenclave/EThreeEnclaveTest.kt @@ -33,7 +33,7 @@ package com.virgilsecurity.android.testsenclave -import android.support.test.runner.AndroidJUnit4 +import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.ethreeenclave.interaction.EThree import com.virgilsecurity.android.testsenclave.utils.TestConfig diff --git a/testsenclave/src/androidTest/java/com/virgilsecurity/android/testsenclave/utils/TestConfig.kt b/testsenclave/src/androidTest/java/com/virgilsecurity/android/testsenclave/utils/TestConfig.kt index b540289c..0bbd6408 100644 --- a/testsenclave/src/androidTest/java/com/virgilsecurity/android/testsenclave/utils/TestConfig.kt +++ b/testsenclave/src/androidTest/java/com/virgilsecurity/android/testsenclave/utils/TestConfig.kt @@ -33,7 +33,7 @@ package com.virgilsecurity.android.testsenclave.utils -import android.support.test.InstrumentationRegistry +import androidx.test.platform.app.InstrumentationRegistry import com.virgilsecurity.android.testsenclave.BuildConfig import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.VirgilPrivateKey @@ -52,8 +52,8 @@ class TestConfig { val virgilBaseUrl = BuildConfig.VIRGIL_BASE_URL const val VIRGIL_CARDS_SERVICE_PATH = "/card/v5/" - val context = InstrumentationRegistry.getTargetContext() - val DIRECTORY_PATH = InstrumentationRegistry.getTargetContext().filesDir.absolutePath + val context = InstrumentationRegistry.getInstrumentation().targetContext + val DIRECTORY_PATH = InstrumentationRegistry.getInstrumentation().targetContext.filesDir.absolutePath val KEYSTORE_NAME = "virgil.keystore" } } From 7a4c2b0be99629ef1fcdd7f4b6fe4b3264ec5894 Mon Sep 17 00:00:00 2001 From: BuddahLD Date: Thu, 26 Sep 2019 18:55:22 +0300 Subject: [PATCH 02/13] Prepared for release --- build.gradle | 14 +++++++------- ethree-common/build.gradle | 8 ++++++-- ethree-enclave/build.gradle | 8 ++++++-- ethree-kotlin/build.gradle | 8 ++++++-- tests/build.gradle | 3 +-- .../ethree/interaction/async/EThreeScopesTest.kt | 2 +- testsenclave/build.gradle | 3 +-- 7 files changed, 28 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index 3c99e878..f2ce464c 100644 --- a/build.gradle +++ b/build.gradle @@ -34,9 +34,9 @@ buildscript { ext.versions = [ // Virgil - virgilSdk : '6.0-SNAPSHOT', + virgilSdk : '6.0', virgilCrypto: '0.10.2', - pythia : '0.3.2-SNAPSHOT', + pythia : '0.3.2', // Kotlin kotlin : '1.3.50', @@ -64,10 +64,11 @@ buildscript { espresso : '3.0.2', ] ext.androidOptions = [ - compileSdkVersion : 28, - minSdkVersion : 23, - targetSdkVersion : 28, - buildToolsVersion : "28.0.3" + compileSdkVersion : 28, + minSdkVersionEnclave: 23, + minSdkVersionRegular: 21, + targetSdkVersion : 28, + buildToolsVersion : "28.0.3" ] repositories { google() @@ -84,7 +85,6 @@ buildscript { allprojects { repositories { - mavenLocal() google() jcenter() mavenCentral() diff --git a/ethree-common/build.gradle b/ethree-common/build.gradle index 90c9d719..d9ff77bf 100644 --- a/ethree-common/build.gradle +++ b/ethree-common/build.gradle @@ -45,14 +45,14 @@ apply plugin: 'digital.wup.android-maven-publish' apply plugin: 'org.jetbrains.dokka' group 'com.virgilsecurity' -version '0.4.1' +version '0.4.2' android { compileSdkVersion androidOptions.compileSdkVersion buildToolsVersion androidOptions.buildToolsVersion defaultConfig { - minSdkVersion androidOptions.minSdkVersion + minSdkVersion androidOptions.minSdkVersionRegular targetSdkVersion androidOptions.targetSdkVersion consumerProguardFiles 'proguard-rules.txt' @@ -65,6 +65,10 @@ android { } } } + buildTypes { + buildTypes { + } + } } dependencies { diff --git a/ethree-enclave/build.gradle b/ethree-enclave/build.gradle index f1373a6a..d7e98fcf 100644 --- a/ethree-enclave/build.gradle +++ b/ethree-enclave/build.gradle @@ -48,14 +48,18 @@ android { buildToolsVersion androidOptions.buildToolsVersion defaultConfig { - minSdkVersion androidOptions.minSdkVersion + minSdkVersion androidOptions.minSdkVersionEnclave targetSdkVersion androidOptions.targetSdkVersion consumerProguardFiles 'proguard-rules.txt' } + buildTypes { + buildTypes { + } + } } group 'com.virgilsecurity' -version '0.5.0' +version '0.5.1' dependencies { // Inner dependencies diff --git a/ethree-kotlin/build.gradle b/ethree-kotlin/build.gradle index 41f08f8c..2114738b 100644 --- a/ethree-kotlin/build.gradle +++ b/ethree-kotlin/build.gradle @@ -48,14 +48,18 @@ android { buildToolsVersion androidOptions.buildToolsVersion defaultConfig { - minSdkVersion androidOptions.minSdkVersion + minSdkVersion androidOptions.minSdkVersionRegular targetSdkVersion androidOptions.targetSdkVersion consumerProguardFiles 'proguard-rules.txt' } + buildTypes { + buildTypes { + } + } } group 'com.virgilsecurity' -version '0.5.1' +version '0.5.2' dependencies { // Inner dependencies diff --git a/tests/build.gradle b/tests/build.gradle index 20b21008..75950782 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -45,9 +45,8 @@ android { compileSdkVersion androidOptions.compileSdkVersion defaultConfig { applicationId "com.virgilsecurity.android.ethree" - minSdkVersion androidOptions.minSdkVersion + minSdkVersion androidOptions.minSdkVersionRegular targetSdkVersion androidOptions.targetSdkVersion -// testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt index df9d9ad8..69a6ca2b 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt @@ -190,6 +190,6 @@ class EThreeScopesTest { companion object { const val NUMBER_OF_REPEATS = 100 - const val A_FEW_NETWORK_CALLS_DELAY = 1000L // 5 sec + const val A_FEW_NETWORK_CALLS_DELAY = 5000L // 5 sec } } diff --git a/testsenclave/build.gradle b/testsenclave/build.gradle index d9cbdd70..ca4e2297 100644 --- a/testsenclave/build.gradle +++ b/testsenclave/build.gradle @@ -45,9 +45,8 @@ android { compileSdkVersion androidOptions.compileSdkVersion defaultConfig { applicationId "com.virgilsecurity.android.testsenclave" - minSdkVersion androidOptions.minSdkVersion + minSdkVersion androidOptions.minSdkVersionEnclave targetSdkVersion androidOptions.targetSdkVersion -// testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { From 7395740e2cdb937c81beef4cf6dd36bca944e1a1 Mon Sep 17 00:00:00 2001 From: BuddahLD Date: Thu, 26 Sep 2019 19:41:27 +0300 Subject: [PATCH 03/13] Resolved TODOs --- .travis.yml | 2 + ethree-common/build.gradle | 22 ++ .../android/common/EThreeCore.kt | 119 +++++--- .../android/common/exception/Exceptions.kt | 25 -- .../android/common/manager/GroupManager.kt | 3 +- .../android/common/manager/LookupManager.kt | 46 ++- .../android/common/model/Group.kt | 17 +- .../android/common/model/Ticket.kt | 2 +- .../common/storage/cloud/CloudKeyManager.kt | 31 +-- .../storage/cloud/CloudTicketStorage.kt | 14 +- .../common/storage/local/GroupStorageFile.kt | 7 +- .../common/storage/local/KeyStorageLocal.kt | 16 +- .../common/storage/sql/ETheeDatabase.kt | 2 +- .../common/storage/sql/SQLCardStorage.kt | 12 +- .../android/common/storage/sql/dao/CardDao.kt | 5 +- .../common/storage/sql/model/CardEntity.kt | 2 +- .../common/worker/AuthorizationWorker.kt | 33 --- .../android/common/worker/BackupWorker.kt | 84 ++---- .../android/common/worker/GroupWorker.kt | 74 ++--- .../android/common/worker/PeerToPeerWorker.kt | 262 +++--------------- .../android/common/worker/SearchWorker.kt | 69 +---- ethree-enclave/build.gradle | 22 -- ethree-kotlin/build.gradle | 24 -- tests/.gitignore | 1 + tests/build.gradle | 3 + .../{ethree => }/common/model/TicketTest.kt | 3 +- .../common/storage/sql/SQLCardStorageTest.kt | 8 +- .../{ethree => common}/utils/TestConfig.kt | 2 +- .../{ethree => common}/utils/TestUtils.kt | 5 +- .../worker/AuthenticationTests.kt | 9 +- .../{ethree => common}/worker/BackupTests.kt | 9 +- .../{ethree => common}/worker/GroupTests.kt | 7 +- .../worker/PeerToPeerTest.kt | 38 ++- .../{ethree => common}/worker/SearchTests.kt | 6 +- .../interaction/async/EThreeAuthTest.kt | 15 +- .../interaction/async/EThreeBackupTest.kt | 6 +- .../interaction/async/EThreeEncryptionTest.kt | 6 +- .../interaction/async/EThreeNegativeTest.kt | 11 +- .../interaction/async/EThreeScopesTest.kt | 4 +- .../interaction/sync/EThreeSyncNegative.kt | 5 +- .../interaction/sync/EThreeSyncPositive.kt | 4 +- .../java/interaction/EThreeScopesTest.java | 2 +- .../java/interaction/EThreeTestPositive.java | 4 +- .../resources/compat_data.json.enc | Bin 0 -> 1808 bytes testsenclave/build.gradle | 3 + 45 files changed, 352 insertions(+), 692 deletions(-) rename tests/src/androidTest/java/com/virgilsecurity/android/{ethree => }/common/model/TicketTest.kt (97%) rename tests/src/androidTest/java/com/virgilsecurity/android/{ethree => }/common/storage/sql/SQLCardStorageTest.kt (96%) rename tests/src/androidTest/java/com/virgilsecurity/android/{ethree => common}/utils/TestConfig.kt (98%) rename tests/src/androidTest/java/com/virgilsecurity/android/{ethree => common}/utils/TestUtils.kt (96%) rename tests/src/androidTest/java/com/virgilsecurity/android/{ethree => common}/worker/AuthenticationTests.kt (95%) rename tests/src/androidTest/java/com/virgilsecurity/android/{ethree => common}/worker/BackupTests.kt (96%) rename tests/src/androidTest/java/com/virgilsecurity/android/{ethree => common}/worker/GroupTests.kt (99%) rename tests/src/androidTest/java/com/virgilsecurity/android/{ethree => common}/worker/PeerToPeerTest.kt (88%) rename tests/src/androidTest/java/com/virgilsecurity/android/{ethree => common}/worker/SearchTests.kt (97%) create mode 100644 tests/src/androidTest/resources/compat_data.json.enc diff --git a/.travis.yml b/.travis.yml index 37b3317f..0e0eb3b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ branches: only: - dev - master + - travis-setup before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock @@ -55,6 +56,7 @@ before_install: - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" - chmod +x gradlew - ./gradlew dependencies || true + - openssl aes-256-cbc -K $encrypted_62e1b1822e5d_key -iv $encrypted_62e1b1822e5d_iv -in compat_data.json.enc -out compat_data.json -d before_script: - android list targets diff --git a/ethree-common/build.gradle b/ethree-common/build.gradle index d9ff77bf..c6a4d961 100644 --- a/ethree-common/build.gradle +++ b/ethree-common/build.gradle @@ -103,6 +103,28 @@ dependencies { kapt "androidx.room:room-compiler:$versions.room" } +task generateVersionVirgilAgent { + outputs.dir "$buildDir/generated" + doFirst { + def versionFile = file("$buildDir/generated/release/com/virgilsecurity/android/common/build/VersionVirgilAgent.kt") + versionFile.parentFile.mkdirs() + versionFile.text = + """ +package com.virgilsecurity.android.common.build; + +object VersionVirgilAgent { + const val VERSION = "$project.version" +} +""" + } +} + +project.android.sourceSets.main.java.srcDirs = ["${buildDir}/generated/release/", "src/main/java"] + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + dependsOn(generateVersionVirgilAgent) +} + sourceCompatibility = "8" targetCompatibility = "8" diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt index b771a8dc..bdfa2907 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt @@ -34,6 +34,7 @@ package com.virgilsecurity.android.common import android.content.Context +import com.virgilsecurity.android.common.build.VersionVirgilAgent import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.callback.OnKeyChangedCallback import com.virgilsecurity.android.common.exception.* @@ -54,7 +55,6 @@ import com.virgilsecurity.android.common.worker.* import com.virgilsecurity.common.model.Completable import com.virgilsecurity.common.model.Data import com.virgilsecurity.common.model.Result -import com.virgilsecurity.keyknox.build.VersionVirgilAgent import com.virgilsecurity.sdk.cards.Card import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier @@ -80,25 +80,25 @@ abstract class EThreeCore * [onGetTokenCallback] using [CachingJwtProvider] also initializing [DefaultKeyStorage] with * default settings. */ -constructor(identity: String, +constructor(val identity: String, getTokenCallback: OnGetTokenCallback, keyChangedCallback: OnKeyChangedCallback?, context: Context) { - private var accessTokenProvider: AccessTokenProvider private val rootPath: String - private val cloudKeyManager: CloudKeyManager - val lookupManager: LookupManager - lateinit var keyStorageLocal: KeyStorageLocal + + private var accessTokenProvider: AccessTokenProvider + var groupManager: GroupManager? = null private set + private lateinit var authorizationWorker: AuthorizationWorker private lateinit var backupWorker: BackupWorker private lateinit var groupWorker: GroupWorker private lateinit var p2pWorker: PeerToPeerWorker private lateinit var searchWorker: SearchWorker - var groupManager: GroupManager? = null + lateinit var keyStorageLocal: KeyStorageLocal private set protected val crypto: VirgilCrypto = VirgilCrypto() @@ -106,13 +106,12 @@ constructor(identity: String, protected abstract val keyStorage: KeyStorage val cardManager: CardManager - val identity: String + val lookupManager: LookupManager init { - this.identity = identity val cardCrypto = VirgilCardCrypto(crypto) val virgilCardVerifier = VirgilCardVerifier(cardCrypto) - val httpClient = HttpClient(Const.ETHREE_NAME, VersionVirgilAgent.VERSION) // FIXME wrong VersionVirgilAgent - from keyknox + val httpClient = HttpClient(Const.ETHREE_NAME, VersionVirgilAgent.VERSION) this.accessTokenProvider = CachingJwtProvider { Jwt(getTokenCallback.onGetToken()) } cardManager = CardManager(cardCrypto, @@ -166,11 +165,12 @@ constructor(identity: String, * * To start execution of the current function, please see [Completable] description. * + * @throws EThreeException * @throws CryptoException */ @Synchronized @JvmOverloads - fun register(keyPair: VirgilKeyPair? = null) = authorizationWorker.register(keyPair) + fun register(keyPair: VirgilKeyPair? = null): Completable = authorizationWorker.register(keyPair) /** * Revokes the public key for current *identity* in Virgil's Cards Service. After this operation @@ -178,10 +178,10 @@ constructor(identity: String, * * To start execution of the current function, please see [Completable] description. * - * @throws UnRegistrationException if there's no public key published yet, or if there's more + * @throws EThreeException if there's no public key published yet, or if there's more * than one public key is published. */ - @Synchronized fun unregister() = authorizationWorker.unregister() + @Synchronized fun unregister(): Completable = authorizationWorker.unregister() /** * Generates new key pair, publishes new public key for current identity and deprecating old @@ -193,13 +193,13 @@ constructor(identity: String, * @throws EThreeException * @throws CryptoException */ - @Synchronized fun rotatePrivateKey() = authorizationWorker.rotatePrivateKey() + @Synchronized fun rotatePrivateKey(): Completable = authorizationWorker.rotatePrivateKey() /** * Checks whether the private key is present in the local storage of current device. * Returns *true* if the key is present in the local key storage otherwise *false*. */ - fun hasLocalPrivateKey() = authorizationWorker.hasLocalPrivateKey() + fun hasLocalPrivateKey(): Boolean = authorizationWorker.hasLocalPrivateKey() /** * ! *WARNING* ! If you call this function after [register] without using [backupPrivateKey] @@ -217,11 +217,15 @@ constructor(identity: String, fun cleanup() = authorizationWorker.cleanup() /** - * Returns cards from local storage for given [identities]. // TODO add Result/Completable reference in all fun's descriptions + * Returns cards from local storage for given [identities]. + * + * To start execution of the current function, please see [Result] description. * * @param identities Identities. * * @return [FindUsersResult] with found users. + * + * @throws FindUsersException If no cached user was found. */ fun findCachedUsers(identities: List): Result = searchWorker.findCachedUsers(identities) @@ -229,9 +233,13 @@ constructor(identity: String, /** * Returns card from local storage for given [identity]. * + * To start execution of the current function, please see [Result] description. + * * @param identity Identity. * * @return [Card] if it exists, *null* otherwise. + * + * @throws FindUsersException If card duplicates was found or card was not found at all. */ fun findCachedUser(identity: String): Result = searchWorker.findCachedUser(identity) @@ -239,10 +247,14 @@ constructor(identity: String, /** * Retrieves users Cards from the Virgil Cloud or local storage if exists. * + * To start execution of the current function, please see [Result] description. + * * @param identities Array of identities to find. * @param forceReload Will not use local cached cards if *true*. * * @return [FindUsersResult] with found users. + * + * @throws FindUsersException If card duplicates was found or at least one card was not found. */ fun findUsers(identities: List, forceReload: Boolean = false): Result = searchWorker.findUsers(identities, forceReload) @@ -250,10 +262,14 @@ constructor(identity: String, /** * Retrieves user Card from the Virgil Cloud or local storage if exists. * + * To start execution of the current function, please see [Result] description. + * * @param identity Identity to find. * @param forceReload Will not use local cached card if *true*. * * @return [Card] that corresponds to provided [identity]. + * + * @throws FindUsersException If card duplicates was found or at least one card was not found. */ fun findUser(identity: String, forceReload: Boolean = false): Result = searchWorker.findUser(identity, forceReload) @@ -264,15 +280,14 @@ constructor(identity: String, * Searches for public key with specified [identity] and returns map of [String] -> * [PublicKey] in [onResultListener] callback or [Throwable] if something went wrong. * - * [PublicKeyNotFoundException] will be thrown if public key wasn't found. + * [FindUsersException] will be thrown if public key wasn't found. * - * Can be called only if private key is on the device, otherwise [PrivateKeyNotFoundException] - * exception will be thrown. + * To start execution of the current function, please see [Result] description. * - * @throws PrivateKeyNotFoundException - * @throws PublicKeyDuplicateException + * @throws FindUsersException */ - @Deprecated("Use findUser instead.") // TODO add replaceWith + @Deprecated("Check 'replace with' section.", + ReplaceWith("findUser(String)")) fun lookupPublicKeys(identity: String): Result = searchWorker.lookupPublicKey(identity) @@ -282,18 +297,14 @@ constructor(identity: String, * Searches for public keys with specified [identities] and returns map of [String] -> * [PublicKey] in [onResultListener] callback or [Throwable] if something went wrong. * - * [PublicKeyNotFoundException] will be thrown for the first not found public key. - * [EThreeCore.register] - * - * Can be called only if private key is on the device, otherwise [PrivateKeyNotFoundException] - * exception will be thrown. + * [FindUsersException] will be thrown if public key wasn't found. * * To start execution of the current function, please see [Result] description. * - * @throws PrivateKeyNotFoundException - * @throws PublicKeyDuplicateException + * @throws FindUsersException */ - @Deprecated("Use findUsers instead.") // TODO add replaceWith + @Deprecated("Check 'replace with' section.", + ReplaceWith("findUsers(List)")) fun lookupPublicKeys(identities: List): Result = searchWorker.lookupPublicKeys(identities) @@ -338,12 +349,9 @@ constructor(identity: String, * using *Public key* that is generated based on provided [newPassword] and pushes encrypted * user's *Private key* to the Virgil's cloud storage. * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * * To start execution of the current function, please see [Completable] description. * - * @throws PrivateKeyNotFoundException + * @throws WrongPasswordException If [oldPassword] is wrong. */ fun changePassword(oldPassword: String, newPassword: String): Completable = @@ -357,13 +365,10 @@ constructor(identity: String, * callback that will notify you with successful completion or with a [Throwable] if * something went wrong. * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * * To start execution of the current function, please see [Completable] description. * + * @throws WrongPasswordException If [password] is wrong. * @throws PrivateKeyNotFoundException - * @throws WrongPasswordException */ @JvmOverloads fun resetPrivateKeyBackup(password: String? = null): Completable = @@ -372,10 +377,15 @@ constructor(identity: String, /** * Creates group, saves in cloud and locally. * + * To start execution of the current function, please see [Result] description. + * * @param identifier Identifier of group. Should be *> 10* length. * @param users Cards of participants. Result of findUsers call. * * @return New [Group]. + * + * @throws InvalidParticipantsCountGroupException If participants count is out of + * [Group.VALID_PARTICIPANTS_COUNT_RANGE] range. */ fun createGroup(identifier: Data, users: FindUsersResult): Result = groupWorker.createGroup(identifier, users) @@ -392,6 +402,8 @@ constructor(identity: String, /** * Loads group from cloud, saves locally. * + * To start execution of the current function, please see [Result] description. + * * @param identifier Identifier of group. Should be *> 10* length. * @param card Card of group initiator. * @@ -403,13 +415,19 @@ constructor(identity: String, /** * Deletes group from cloud and local storage. * + * To start execution of the current function, please see [Completable] description. + * * @param identifier Identifier of group. Should be *> 10* length. + * + * @throws GroupNotFoundException If group was not found. */ fun deleteGroup(identifier: Data): Completable = groupWorker.deleteGroup(identifier) /** * Creates group, saves in cloud and locally. * + * To start execution of the current function, please see [Result] description. + * * @param identifier Identifier of group. Should be *> 10* length. * @param users Cards of participants. Result of findUsers call. * @@ -430,6 +448,8 @@ constructor(identity: String, /** * Loads group from cloud, saves locally. * + * To start execution of the current function, please see [Result] description. + * * @param identifier Identifier of group. Should be *> 10* length. * @param card Card of group initiator. * @@ -441,6 +461,8 @@ constructor(identity: String, /** * Deletes group from cloud and local storage. * + * To start execution of the current function, please see [Completable] description. + * * @param identifier Identifier of group. Should be *> 10* length. */ fun deleteGroup(identifier: String): Completable = groupWorker.deleteGroup(identifier) @@ -473,6 +495,8 @@ constructor(identity: String, * from self. * * @return Decrypted Data. + * + * @throws EThreeException If verification of message failed. */ @JvmOverloads fun decrypt(data: Data, user: Card? = null): Data = p2pWorker.decrypt(data, user) @@ -487,6 +511,8 @@ constructor(identity: String, * @param date Date of encryption to use proper card version. * * @return Decrypted Data. + * + * @throws EThreeException If verification of message failed. */ fun decrypt(data: Data, user: Card, date: Date): Data = p2pWorker.decrypt(data, user, date) @@ -628,7 +654,8 @@ constructor(identity: String, * @throws PrivateKeyNotFoundException * @throws CryptoException */ - @Deprecated("Use encryptForUsers method instead.") // TODO change to actual fun name + @Deprecated("Check 'replace with' section.", + ReplaceWith("encrypt(String, FindUsersResult)")) fun encrypt(text: String, lookupResult: LookupResult): String = p2pWorker.encrypt(text, lookupResult) @@ -650,7 +677,8 @@ constructor(identity: String, * @throws PrivateKeyNotFoundException * @throws CryptoException */ - @Deprecated("Use encryptForUsers method instead.") + @Deprecated("Check 'replace with' section.", + ReplaceWith("encrypt(ByteArray, FindUsersResult)")) @JvmOverloads fun encrypt(data: ByteArray, lookupResult: LookupResult? = null): ByteArray = p2pWorker.encrypt(data, lookupResult) @@ -671,7 +699,8 @@ constructor(identity: String, * @throws PrivateKeyNotFoundException * @throws CryptoException */ - @Deprecated("Use encryptForUsers method instead.") // TODO change to actual methods signature + @Deprecated("Check \'replace with\' section.", + ReplaceWith("encrypt(InputStream, OutputStream, FindUsersResult)")) fun encrypt(inputStream: InputStream, outputStream: OutputStream, lookupResult: LookupResult) = @@ -691,10 +720,12 @@ constructor(identity: String, * * @return Decrypted String. * + * @throws EThreeException If verification of message failed. * @throws PrivateKeyNotFoundException * @throws CryptoException */ - @Deprecated("Use decryptFromUser method instead.") + @Deprecated("Check 'replace with' section.", + ReplaceWith("decrypt(String, Card)")) fun decrypt(base64String: String, sendersKey: VirgilPublicKey): String = p2pWorker.decrypt(base64String, sendersKey) @@ -706,10 +737,12 @@ constructor(identity: String, * @param data Data to decrypt. * @param sendersKey Sender PublicKey to verify with. * + * @throws EThreeException If verification of message failed. * @throws PrivateKeyNotFoundException * @throws CryptoException */ - @Deprecated("Use decryptFromUser method instead.") + @Deprecated("Check 'replace with' section.", + ReplaceWith("decrypt(Data, Card)")) @JvmOverloads fun decrypt(data: ByteArray, sendersKey: VirgilPublicKey? = null): ByteArray = p2pWorker.decrypt(data, sendersKey) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/exception/Exceptions.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/exception/Exceptions.kt index 89a1885e..a2a97084 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/exception/Exceptions.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/exception/Exceptions.kt @@ -33,17 +33,6 @@ package com.virgilsecurity.android.common.exception -/** - * . _ _ - * .| || | _ - * -| || || | Created by: - * .| || || |- Danylo Oliinyk - * ..\_ || | on - * ....| _/ 10/23/18 - * ...-| | \ at Virgil Security - * ....|_|- - */ - /** * Exceptions */ @@ -71,20 +60,6 @@ class PrivateKeyNotFoundException @JvmOverloads constructor( override val message: String? = null, throwable: Throwable? = null ) : EThreeException(message, throwable) -class PublicKeyNotFoundException @JvmOverloads constructor( - val identity: String, - override val message: String? = null, - throwable: Throwable? = null -) : EThreeException(message, throwable) - -class PublicKeyDuplicateException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : EThreeException(message, throwable) - -class UnRegistrationException @JvmOverloads constructor( - override val message: String? = null, throwable: Throwable? = null -) : EThreeException(message, throwable) - class RawGroupException @JvmOverloads constructor( override val message: String? = null, throwable: Throwable? = null ) : EThreeException(message, throwable) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt index 6cd4ba15..0f63f66c 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt @@ -33,7 +33,6 @@ package com.virgilsecurity.android.common.manager -import com.virgilsecurity.android.common.exception.GroupException import com.virgilsecurity.android.common.exception.GroupNotFoundException import com.virgilsecurity.android.common.model.Group import com.virgilsecurity.android.common.model.GroupInfo @@ -93,7 +92,7 @@ class GroupManager( } internal fun addAccess(cards: List, sessionId: Data) = - cloudTicketStorage.addRecipients(cards, sessionId) + cloudTicketStorage.addRecipients(cards, sessionId) internal fun reAddAccess(card: Card, sessionId: Data) = cloudTicketStorage.reAddRecipient(card, sessionId) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt index 12c32c65..7a266ef3 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt @@ -37,9 +37,11 @@ import com.virgilsecurity.android.common.callback.OnKeyChangedCallback import com.virgilsecurity.android.common.exception.FindUsersException import com.virgilsecurity.android.common.model.FindUsersResult import com.virgilsecurity.android.common.storage.CardStorage +import com.virgilsecurity.keyknox.utils.unwrapCompanionClass import com.virgilsecurity.sdk.cards.Card import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.exception.EmptyArgumentException +import java.util.logging.Logger /** * LookupManager @@ -51,27 +53,39 @@ class LookupManager( ) { internal fun startUpdateCachedCards() { - val cardIdsNewest = cardStorage.getNewestCardIds() + try { + logger.fine("Updating cached cards started") - val cardIdsChunked = cardIdsNewest.chunked(MAX_GET_OUTDATED_COUNT) + val cardIdsNewest = cardStorage.getNewestCardIds() - for (cardIds in cardIdsChunked) { - val outdatedIds = cardManager.getOutdated(cardIds) + val cardIdsChunked = cardIdsNewest.chunked(MAX_GET_OUTDATED_COUNT) - for (outdatedId in outdatedIds) { - val outdatedCard = - cardStorage.getCard(outdatedId) - ?: throw FindUsersException("Card with id: $outdatedId was not found " + - "locally. Try to call findUsers first") + for (cardIds in cardIdsChunked) { + val outdatedIds = cardManager.getOutdated(cardIds) - onKeyChangedCallback?.keyChanged(outdatedCard.identity) + for (outdatedId in outdatedIds) { + logger.fine("Cached card with id: $outdatedId expired") - val newCard = lookupCard(outdatedCard.identity, true) + val outdatedCard = + cardStorage.getCard(outdatedId) + ?: throw FindUsersException("Card with id: $outdatedId was not found " + + "locally. Try to call findUsers first") - cardStorage.storeCard(newCard) + onKeyChangedCallback?.keyChanged(outdatedCard.identity) + + val newCard = lookupCard(outdatedCard.identity, true) + + cardStorage.storeCard(newCard) + + logger.fine("Cached card with id: $outdatedId updated to card " + + "with id: ${newCard.identifier}") + } } + + logger.fine("Updating cached card finished") + } catch (throwable: Throwable) { + logger.fine("Updating cached cards failed: ${throwable.message}") } - // TODO add exception handling for DB requests, etc } internal fun lookupCachedCards(identities: List): FindUsersResult { @@ -92,6 +106,8 @@ class LookupManager( } internal fun lookupCachedCard(identity: String): Card { + require(identity.isNotEmpty()) { "\'identity\' should not be empty" } + val cards = cardStorage.searchCards(listOf(identity)) if (cards.size >= 2) @@ -149,6 +165,8 @@ class LookupManager( } internal fun lookupCard(identity: String, forceReload: Boolean = false): Card { + require(identity.isNotEmpty()) { "\'identity\' should not be empty" } + val cards = lookupCards(listOf(identity), forceReload) return cards[identity] @@ -159,5 +177,7 @@ class LookupManager( companion object { private const val MAX_SEARCH_COUNT = 50 private const val MAX_GET_OUTDATED_COUNT = 1_000 + + private val logger = Logger.getLogger(unwrapCompanionClass(this.javaClass).name) } } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt index 66242d6e..6c55ee28 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt @@ -68,7 +68,7 @@ class Group constructor( private val selfIdentity: String = keyStorageLocal.identity init { - val tickets = rawGroup.tickets.sortedBy { it.groupMessage.epoch } // TODO check sort order matches $0.groupMessage.getEpoch() < $1.groupMessage.getEpoch() + val tickets = rawGroup.tickets.sortedBy { it.groupMessage.epoch } val lastTicket = tickets.lastOrNull() ?: throw GroupException("Group is invalid") validateParticipantsCount(lastTicket.participants.size) @@ -104,6 +104,8 @@ class Group constructor( * @notice Requires private key in local storage. */ fun encrypt(string: String): String { + require(string.isNotEmpty()) { "\'string\' should not be empty" } + return ConvertionUtils.toBase64String(encrypt(string.toByteArray())) } @@ -117,6 +119,8 @@ class Group constructor( * @notice Requires private key in local storage. */ fun encrypt(data: ByteArray): ByteArray { + require(data.isNotEmpty()) { "\'data\' should not be empty" } + val selfKeyPair = this.keyStorageLocal.load() val encrypted = this.session.encrypt(data, selfKeyPair.privateKey.privateKey) return encrypted.serialize() @@ -132,6 +136,8 @@ class Group constructor( * @return decrypted byte array. */ fun decrypt(data: ByteArray, senderCard: Card, date: Date? = null): ByteArray { + require(data.isNotEmpty()) { "\'data\' should not be empty" } + val encrypted = GroupSessionMessage.deserialize(data) var card = senderCard @@ -165,7 +171,7 @@ class Group constructor( val sessionId = encrypted.sessionId val tempGroup = this.groupManager.retrieve(Data(sessionId), messageEpoch) - ?: throw MissingCachedGroupException() + ?: throw MissingCachedGroupException() tempGroup.decrypt(data, senderCard) } @@ -184,6 +190,8 @@ class Group constructor( * @return decrypted String. */ fun decrypt(text: String, senderCard: Card, date: Date? = null): String { + require(text.isNotEmpty()) { "\'text\' should not be empty" } + val data: ByteArray try { data = ConvertionUtils.base64ToBytes(text) @@ -308,8 +316,9 @@ class Group constructor( companion object { internal fun validateParticipantsCount(count: Int) { if (count !in VALID_PARTICIPANTS_COUNT_RANGE) { - throw InvalidParticipantsCountGroupException("Please check valid participants count range in " + - "Group.VALID_PARTICIPANTS_COUNT_RANGE") + throw InvalidParticipantsCountGroupException("Please check valid participants " + + "count range in " + + "Group.VALID_PARTICIPANTS_COUNT_RANGE") } } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt index d757dc47..2c16263d 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt @@ -46,7 +46,7 @@ import java.io.Serializable /** * Ticket */ -class Ticket : Parcelable { // TODO test parcelable implementation +class Ticket : Parcelable { val groupMessage: GroupSessionMessage val participants: Set diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt index ff692d42..5333d6d1 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt @@ -50,33 +50,30 @@ import com.virgilsecurity.pythia.client.VirgilPythiaClient import com.virgilsecurity.pythia.crypto.VirgilPythiaCrypto import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.VirgilPrivateKey -import com.virgilsecurity.sdk.crypto.VirgilPublicKey -import com.virgilsecurity.sdk.crypto.exceptions.DecryptionException import com.virgilsecurity.sdk.jwt.contract.AccessTokenProvider import java.net.URL /** * CloudKeyManager */ -class CloudKeyManager( +internal class CloudKeyManager( private val identity: String, private val crypto: VirgilCrypto, private val tokenProvider: AccessTokenProvider, - private val baseUrl: String = VIRGIL_BASE_URL + baseUrl: String = VIRGIL_BASE_URL ) { - private val keyknoxClient: KeyknoxClient private val keyknoxManager: KeyknoxManager private val brainKey: BrainKey init { val httpClient = HttpClient(tokenProvider, Const.ETHREE_NAME, VersionVirgilAgent.VERSION) - keyknoxClient = KeyknoxClient(httpClient, URL(baseUrl)) + val keyknoxClient = KeyknoxClient(httpClient, URL(baseUrl)) this.keyknoxManager = KeyknoxManager(keyknoxClient) // TODO change VirgilPythiaClient to have tokenProvider inside val pythiaClient = VirgilPythiaClient(baseUrl, Const.ETHREE_NAME, Const.ETHREE_NAME, - VersionVirgilAgent.VERSION) + VersionVirgilAgent.VERSION) val brainKeyContext = BrainKeyContext.Builder() .setAccessTokenProvider(tokenProvider) .setPythiaClient(pythiaClient) @@ -86,26 +83,26 @@ class CloudKeyManager( this.brainKey = BrainKey(brainKeyContext) } - fun exists(password: String) = setupCloudKeyStorage(password).exists(identity) + internal fun exists(password: String) = setupCloudKeyStorage(password).exists(identity) - fun store(key: VirgilPrivateKey, password: String) { + internal fun store(key: VirgilPrivateKey, password: String) { val exportedIdentityKey = this.crypto.exportPrivateKey(key) setupCloudKeyStorage(password).store(this.identity, exportedIdentityKey) } - fun retrieve(password: String): CloudEntry { + internal fun retrieve(password: String): CloudEntry { return setupCloudKeyStorage(password).retrieve(identity) } - fun delete(password: String) { + internal fun delete(password: String) { setupCloudKeyStorage(password).delete(identity) } - fun deleteAll() { + internal fun deleteAll() { this.keyknoxManager.resetValue() } - fun changePassword(oldPassword: String, newPassword: String) { + internal fun changePassword(oldPassword: String, newPassword: String) { val cloudKeyStorage = setupCloudKeyStorage(oldPassword) Thread.sleep(2000) @@ -113,9 +110,9 @@ class CloudKeyManager( val brainKeyPair = this.brainKey.generateKeyPair(newPassword) try { - cloudKeyStorage.updateRecipients(listOf(brainKeyPair.publicKey), brainKeyPair.privateKey) - } - catch (e: KeyknoxCryptoException) { + cloudKeyStorage.updateRecipients(listOf(brainKeyPair.publicKey), + brainKeyPair.privateKey) + } catch (e: KeyknoxCryptoException) { throw WrongPasswordException() } } @@ -128,7 +125,7 @@ class CloudKeyManager( val brainKeyPair = this.brainKey.generateKeyPair(password) val cloudKeyStorage = CloudKeyStorage(this.keyknoxManager, listOf(brainKeyPair.publicKey), - brainKeyPair.privateKey) + brainKeyPair.privateKey) try { cloudKeyStorage.retrieveCloudEntries() diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt index 484f543f..2e7ac1bb 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt @@ -33,8 +33,8 @@ package com.virgilsecurity.android.common.storage.cloud -import com.virgilsecurity.android.common.storage.local.KeyStorageLocal import com.virgilsecurity.android.common.model.Ticket +import com.virgilsecurity.android.common.storage.local.KeyStorageLocal import com.virgilsecurity.android.common.util.Const import com.virgilsecurity.common.model.Data import com.virgilsecurity.common.util.toHexString @@ -78,7 +78,7 @@ class CloudTicketStorage( val params = KeyknoxPushParams(identities + this.identity, GROUP_SESSION_ROOT, sessionId, - "\\$epoch") + "$epoch") // FIXME remove slash? check compat test keyknoxManager.pushValue(params, ticketData, @@ -95,17 +95,17 @@ class CloudTicketStorage( val sessionIdHex = sessionId.toHexString() val getParams = KeyknoxGetKeysParams(identity, - GROUP_SESSION_ROOT, - sessionIdHex) + GROUP_SESSION_ROOT, + sessionIdHex) val epochs = keyknoxManager.getKeys(getParams) val tickets = mutableListOf() for (epoch in epochs) { val pullParams = KeyknoxPullParams(identity, - GROUP_SESSION_ROOT, - sessionIdHex, - epoch) + GROUP_SESSION_ROOT, + sessionIdHex, + epoch) val response = keyknoxManager.pullValue(pullParams, listOf(identityPublicKey), selfKeyPair.privateKey) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt index dcdbf4f6..f3b62b33 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt @@ -52,12 +52,12 @@ import java.io.File /** * GroupStorageFile */ -class GroupStorageFile internal constructor( +class GroupStorageFile( internal val identity: String, crypto: VirgilCrypto, identityKeyPair: VirgilKeyPair, rootPath: String -) { // TODO use internal everywhere possible +) { private val fileSystemEncrypted: FileSystem @@ -144,8 +144,7 @@ class GroupStorageFile internal constructor( } .sorted() .takeLast(count) - } - catch (e: DirectoryNotExistsException) { + } catch (e: DirectoryNotExistsException) { // No tickets for this session } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt index bf2d3aed..3fcb48dc 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt @@ -50,17 +50,23 @@ class KeyStorageLocal( private val crypto: VirgilCrypto ) { - fun exists() = keyStorage.exists(identity) + internal fun exists() = keyStorage.exists(identity) - fun store(privateKeyData: Data) = + internal fun store(privateKeyData: Data) = keyStorage.store(JsonKeyEntry(identity, privateKeyData.data)) - fun load(): VirgilKeyPair = try { + internal fun load(): VirgilKeyPair = try { val privateKeyData = keyStorage.load(identity) crypto.importPrivateKey(privateKeyData.value) } catch (e: KeyEntryNotFoundException) { - throw PrivateKeyNotFoundException("No private key on device. You should call register() or retrievePrivateKey()") + throw PrivateKeyNotFoundException("No private key on device. You should call register() " + + "or retrievePrivateKey()") } - fun delete() = keyStorage.delete(identity) + internal fun delete() = try { + keyStorage.delete(identity) + } catch (exception: KeyEntryNotFoundException) { + throw PrivateKeyNotFoundException("No private key on device. You should call register() " + + "or retrievePrivateKey()") + } } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt index a021a108..38f695b2 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt @@ -39,6 +39,6 @@ import com.virgilsecurity.android.common.storage.sql.dao.CardDao import com.virgilsecurity.android.common.storage.sql.model.CardEntity @Database(entities = arrayOf(CardEntity::class), version = 1) -abstract class ETheeDatabase: RoomDatabase() { +abstract class ETheeDatabase : RoomDatabase() { abstract fun cardDao(): CardDao } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt index 29ce7d6b..17b91ce2 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt @@ -45,7 +45,6 @@ import com.virgilsecurity.sdk.cards.validation.CardVerifier import com.virgilsecurity.sdk.crypto.VirgilCardCrypto import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider -import com.virgilsecurity.sdk.utils.ConvertionUtils /** * SQL-based Virgil Cards storage. @@ -64,8 +63,8 @@ class SQLCardStorage(context: Context, if (database == null) { val dbName = String.format("ethree-database-%s", userIdentifier) this.db = Room.databaseBuilder( - context, - ETheeDatabase::class.java, dbName + context, + ETheeDatabase::class.java, dbName ).build() } else { db = database @@ -82,9 +81,10 @@ class SQLCardStorage(context: Context, var previousCardId: String? = null var isOutdated = card.isOutdated while (currentCard != null) { - //TODO make CardManager.exportCardAsJson static and use it instead of serializeToJson - val cardEntity = CardEntity(currentCard.identifier, currentCard.identity, isOutdated, - ConvertionUtils.serializeToJson(currentCard.rawCard)) + val cardEntity = CardEntity(currentCard.identifier, + currentCard.identity, + isOutdated, + CardManager.exportCardAsJson(currentCard)) db.cardDao().insert(cardEntity) previousCardId = currentCard.previousCardId diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt index 8eb13c70..21b8b276 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt @@ -33,7 +33,10 @@ package com.virgilsecurity.android.common.storage.sql.dao -import androidx.room.* +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query import com.virgilsecurity.android.common.storage.sql.model.CardEntity @Dao diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt index 020cdf8a..5b5fdcc8 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt @@ -41,7 +41,7 @@ import androidx.room.PrimaryKey @Entity(tableName = "ethree_cards", indices = arrayOf(Index(value = ["id"], unique = true), - Index(value = ["identity"], unique = false))) + Index(value = ["identity"], unique = false))) data class CardEntity( @PrimaryKey @ColumnInfo(name = "id") val identifier: String, @ColumnInfo(name = "identity") @NonNull val identity: String, diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt index 1cb7824f..62bd4039 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt @@ -34,7 +34,6 @@ package com.virgilsecurity.android.common.worker import com.virgilsecurity.android.common.exception.EThreeException -import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.common.storage.local.KeyStorageLocal import com.virgilsecurity.common.model.Completable import com.virgilsecurity.sdk.cards.CardManager @@ -51,13 +50,6 @@ internal class AuthorizationWorker( private val privateKeyDeleted: () -> Unit ) { - /** // TODO check throws for all functions - * Publishes Card on Virgil Cards Service and saves Private Key in local storage. - * - * To start execution of the current function, please see [Completable] description. - * - * @param keyPair `VirgilKeyPair` to publish Card with. Will generate if not specified. - */ @Synchronized @JvmOverloads internal fun register(keyPair: VirgilKeyPair? = null) = object : Completable { @@ -72,11 +64,6 @@ internal class AuthorizationWorker( } } - /** - * Revokes Card from Virgil Cards Service, deletes Private Key from local storage - * - * To start execution of the current function, please see [Completable] description. - */ @Synchronized internal fun unregister() = object : Completable { override fun execute() { val cards = cardManager.searchCards(this@AuthorizationWorker.identity) @@ -88,12 +75,6 @@ internal class AuthorizationWorker( } } - /** - * Generates new Private Key, publishes new Card to replace the current one on Virgil Cards - * Service and saves new Private Key in local storage - * - * To start execution of the current function, please see [Completable] description. - */ @Synchronized internal fun rotatePrivateKey() = object : Completable { override fun execute() { if (keyStorageLocal.exists()) @@ -106,22 +87,8 @@ internal class AuthorizationWorker( } } - /** - * Checks existence of private key in local key storage. - * Returns *true* if the key is present in the local key storage otherwise *false*. - */ internal fun hasLocalPrivateKey() = keyStorageLocal.exists() - /** - * ! *WARNING* ! If you call this function after [register] without using [backupPrivateKey] - * then you will loose private key permanently, as well you won't be able to use identity that - * was used with that private key no more. - * - * Deletes Private Key from local storage, cleans local cards storage. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] // TODO check exception type - * exception will be thrown. - */ internal fun cleanup() { keyStorageLocal.delete() privateKeyDeleted() diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt index 27b12c3d..1fbea617 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt @@ -33,7 +33,10 @@ package com.virgilsecurity.android.common.worker -import com.virgilsecurity.android.common.exception.* +import com.virgilsecurity.android.common.exception.BackupKeyException +import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException +import com.virgilsecurity.android.common.exception.RestoreKeyException import com.virgilsecurity.android.common.storage.cloud.CloudKeyManager import com.virgilsecurity.android.common.storage.local.KeyStorageLocal import com.virgilsecurity.common.model.Completable @@ -52,108 +55,55 @@ internal class BackupWorker( private val privateKeyChanged: (Card?) -> Unit ) { - /** - * Encrypts the user's private key using the user's [password] and backs up the encrypted - * private key to Virgil's cloud. This enables users to log in from other devices and have - * access to their private key to decrypt data. - * - * Encrypts loaded from private keys local storage user's *Private key* using *Public key* - * that is generated based on provided [password] after that backs up encrypted user's - * *Private key* to the Virgil's cloud storage. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws PrivateKeyNotFoundException - * @throws BackupKeyException - */ internal fun backupPrivateKey(password: String): Completable = object : Completable { override fun execute() { try { + require(password.isNotEmpty()) { "\'password\' should not be empty" } + val identityKeyPair = keyStorageLocal.load() keyManagerCloud.store(identityKeyPair.privateKey, password) - } - catch (e: EntryAlreadyExistsException) { + } catch (e: EntryAlreadyExistsException) { throw BackupKeyException("Can't backup private key", e) } } } - /** - * Pulls user's private key from the Virgil's cloud, decrypts it with *Private key* that - * is generated based on provided [password] and saves it to the current private keys - * local storage. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws WrongPasswordException - * @throws RestoreKeyException - */ internal fun restorePrivateKey(password: String): Completable = object : Completable { override fun execute() { try { + require(password.isNotEmpty()) { "\'password\' should not be empty" } + val entry = keyManagerCloud.retrieve(password) keyStorageLocal.store(Data(entry.data)) privateKeyChanged(null) - } - catch (e: KeyEntryAlreadyExistsException) { + } catch (e: KeyEntryAlreadyExistsException) { throw RestoreKeyException("Can't restore private key", e) } } } - /** - * Changes the password on a backed-up private key. - * - * Pulls user's private key from the Virgil's cloud storage, decrypts it with *Private key* - * that is generated based on provided [oldPassword] after that encrypts user's *Private key* - * using *Public key* that is generated based on provided [newPassword] and pushes encrypted - * user's *Private key* to the Virgil's cloud storage. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws PrivateKeyNotFoundException - */ internal fun changePassword(oldPassword: String, - newPassword: String): Completable = object : Completable { + newPassword: String): Completable = object : Completable { override fun execute() { + require(oldPassword.isNotEmpty()) { "\'oldPassword\' should not be empty" } + require(newPassword.isNotEmpty()) { "\'newPassword\' should not be empty" } if (oldPassword == newPassword) throw EThreeException("To change password, please" + "provide new password that " + "differs from the old one.") + keyManagerCloud.changePassword(oldPassword, newPassword) } } - /** - * Deletes Private Key stored on Virgil's cloud. This will disable user to log in from - * other devices. - * - * Deletes private key backup using specified [password] and provides [onCompleteListener] - * callback that will notify you with successful completion or with a [Throwable] if - * something went wrong. - * - * Can be called only if private key is on the device otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * To start execution of the current function, please see [Completable] description. - * - * @throws PrivateKeyNotFoundException - * @throws WrongPasswordException - */ @JvmOverloads internal fun resetPrivateKeyBackup(password: String? = null): Completable = object : Completable { override fun execute() { if (password != null) try { keyManagerCloud.delete(password) - } - catch (e: EntryNotFoundException) { - throw PrivateKeyNotFoundException("Can't reset private key: private key not found", e) + } catch (exception: EntryNotFoundException) { + throw PrivateKeyNotFoundException("Can't reset private key: private key not " + + "found", exception) } else keyManagerCloud.deleteAll() diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt index 5325c850..88110d7d 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt @@ -55,17 +55,11 @@ internal class GroupWorker( private val computeSessionId: (Data) -> Data ) { - /** - * Creates group, saves in cloud and locally. - * - * @param identifier Identifier of group. Should be *> 10* length. - * @param users Cards of participants. Result of findUsers call. - * - * @return New [Group]. - */ internal fun createGroup(identifier: Data, users: FindUsersResult): Result = object : Result { override fun get(): Group { + require(identifier.data.isNotEmpty()) { "\'identifier\' should not be empty" } + val sessionId = computeSessionId(identifier) val participants = users.keys + identity @@ -77,46 +71,32 @@ internal class GroupWorker( } } - /** - * Returns cached local group. - * - * @param identifier Identifier of group. Should be *> 10* length. - * - * @return [Group] if exists, null otherwise. - */ internal fun getGroup(identifier: Data): Group? { + require(identifier.data.isNotEmpty()) { "\'identifier\' should not be empty" } + val sessionId = computeSessionId(identifier) return getGroupManager().retrieve(sessionId) } - /** - * Loads group from cloud, saves locally. - * - * @param identifier Identifier of group. Should be *> 10* length. - * @param card Card of group initiator. - * - * @return Loaded [Group]. - */ internal fun loadGroup(identifier: Data, card: Card): Result = object : Result { override fun get(): Group { + require(identifier.data.isNotEmpty()) { "\'identifier\' should not be empty" } + val sessionId = computeSessionId(identifier) return getGroupManager().pull(sessionId, card) } } - /** - * Deletes group from cloud and local storage. - * - * @param identifier Identifier of group. Should be *> 10* length. - */ internal fun deleteGroup(identifier: Data): Completable = object : Completable { override fun execute() { + require(identifier.data.isNotEmpty()) { "\'identifier\' should not be empty" } + val sessionId = computeSessionId(identifier) val group = getGroupManager().retrieve(sessionId) ?: throw GroupNotFoundException("Group with provided id not found " + - "locally. Try to call loadGroup first") + "locally. Try to call loadGroup first") group.checkPermissions() @@ -124,53 +104,33 @@ internal class GroupWorker( } } - /** - * Creates group, saves in cloud and locally. - * - * @param identifier Identifier of group. Should be *> 10* length. - * @param users Cards of participants. Result of findUsers call. - * - * @return New [Group]. - */ internal fun createGroup(identifier: String, users: FindUsersResult): Result { + require(identifier.isNotEmpty()) { "\'identifier\' should not be empty" } + val identifierData = Data(identifier.toByteArray(StandardCharsets.UTF_8)) return createGroup(identifierData, users) } - /** - * Returns cached local group. - * - * @param identifier Identifier of group. Should be *> 10* length. - * - * @return [Group] if exists, null otherwise. - */ internal fun getGroup(identifier: String): Group? { + require(identifier.isNotEmpty()) { "\'identifier\' should not be empty" } + val identifierData = Data(identifier.toByteArray(StandardCharsets.UTF_8)) return getGroup(identifierData) } - /** - * Loads group from cloud, saves locally. - * - * @param identifier Identifier of group. Should be *> 10* length. - * @param card Card of group initiator. - * - * @return Loaded [Group]. - */ internal fun loadGroup(identifier: String, card: Card): Result { + require(identifier.isNotEmpty()) { "\'identifier\' should not be empty" } + val identifierData = Data(identifier.toByteArray(StandardCharsets.UTF_8)) return loadGroup(identifierData, card) } - /** - * Deletes group from cloud and local storage. - * - * @param identifier Identifier of group. Should be *> 10* length. - */ internal fun deleteGroup(identifier: String): Completable { + require(identifier.isNotEmpty()) { "\'identifier\' should not be empty" } + val identifierData = Data(identifier.toByteArray(StandardCharsets.UTF_8)) return deleteGroup(identifierData) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt index 994cf503..e0cfce26 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt @@ -34,7 +34,6 @@ package com.virgilsecurity.android.common.worker import com.virgilsecurity.android.common.exception.EThreeException -import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.common.model.FindUsersResult import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.common.model.toPublicKeys @@ -44,7 +43,6 @@ import com.virgilsecurity.sdk.cards.Card import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.VirgilPublicKey import com.virgilsecurity.sdk.crypto.exceptions.SignatureIsNotValidException -import com.virgilsecurity.sdk.crypto.exceptions.VerificationException import com.virgilsecurity.sdk.exception.EmptyArgumentException import java.io.InputStream import java.io.OutputStream @@ -59,53 +57,16 @@ internal class PeerToPeerWorker( private val crypto: VirgilCrypto ) { - /** - * Signs then encrypts data for group of users. - * - * *Important* Automatically includes self key to recipientsKeys. - * - * *Important* Requires private key in local storage. - * - * *Note* Avoid key duplication. - * - * @param data Data to encrypt. - * @param users Result of findUsers call recipient Cards with Public Keys to sign and encrypt - * with. Use null to sign and encrypt for self. - * - * @return Encrypted Data. - */ @JvmOverloads internal fun encrypt(data: Data, users: FindUsersResult? = null): Data = encryptInternal(data, users?.map { it.value.publicKey }) - /** - * Decrypts and verifies data from users. - * - * *Important* Requires private key in local storage. - * - * @param data Data to decrypt. - * @param user Sender Card with Public Key to verify with. Use null to decrypt and verify. - * from self. - * - * @return Decrypted Data. - */ @JvmOverloads internal fun decrypt(data: Data, user: Card? = null): Data = decryptInternal(data, user?.publicKey) - /** - * Decrypts and verifies data from users. - * - * *Important* Requires private key in local storage. - * - * @param data Data to decrypt. - * @param user Sender Card with Public Key to verify with. - * @param date Date of encryption to use proper card version. - * - * @return Decrypted Data. - */ internal fun decrypt(data: Data, user: Card, date: Date): Data { var card = user - while (card.previousCard != null) { // TODO test it with new card + while (card.previousCard != null) { if (card.createdAt <= date) { break } @@ -116,36 +77,11 @@ internal class PeerToPeerWorker( return decryptInternal(data, card.publicKey) } - /** - * Encrypts data stream. - * - * *Important* Automatically includes self key to recipientsKeys. - * - * *Important* Requires private key in local storage. - * - * *Note* Avoid key duplication. - * - * @param inputStream Data stream to be encrypted. - * @param outputStream Stream with encrypted data. - * @param users Result of findUsers call recipient Cards with Public Keys to sign and encrypt - * with. Use null to sign and encrypt for self. - */ @JvmOverloads internal fun encrypt(inputStream: InputStream, outputStream: OutputStream, users: FindUsersResult? = null) = encryptInternal(inputStream, outputStream, users?.map { it.value.publicKey }) - /** - * Decrypts encrypted stream. - * - * *Important* Requires private key in local storage. - * - * @param inputStream Stream with encrypted data. - * @param outputStream Stream with decrypted data. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ internal fun decrypt(inputStream: InputStream, outputStream: OutputStream) { if (inputStream.available() == 0) throw EmptyArgumentException("inputStream") @@ -154,47 +90,26 @@ internal class PeerToPeerWorker( crypto.decrypt(inputStream, outputStream, selfKeyPair.privateKey) } - /** - * Signs then encrypts string for group of users. - * - * *Important* Automatically includes self key to recipientsKeys. - * - * *Important* Requires private key in local storage. - * - * *Note* Avoid key duplication. - * - * @param text String to encrypt. String should be *UTF-8* encoded. - * @param users Result of findUsers call recipient Cards with Public Keys to sign and encrypt - * with. Use null to sign and encrypt for self. - * - * @return Encrypted base64String. - */ @JvmOverloads internal fun encrypt(text: String, users: FindUsersResult? = null): String { require(text.isNotEmpty()) { "\'text\' should not be empty" } + if (users != null) require(users.isNotEmpty()) { "Passed empty FindUsersResult" } - val data = Data(text.toByteArray(StandardCharsets.UTF_8)) // TODO check exception type and wrap with "String to Data failed" message + val data = try { + Data(text.toByteArray(StandardCharsets.UTF_8)) + } catch (exception: IllegalArgumentException) { + throw EThreeException("Error while converting String to Data. ${exception.message}") + } return encrypt(data, users).toBase64String() } - /** - * Decrypts and verifies base64 string from users. - * - * *Important* Requires private key in local storage. - * - * @param text Encrypted String. - * @param user Sender Card with Public Key to verify with. Use null to decrypt and verify - * from self. - * - * @return Decrypted String. - */ @JvmOverloads internal fun decrypt(text: String, user: Card? = null): String { require(text.isNotEmpty()) { "\'text\' should not be empty" } val data = try { Data.fromBase64String(text) } catch (exception: IllegalArgumentException) { - throw EThreeException("Error while converting String to Data. Wrong base64 String.") + throw EThreeException("Error while converting String to Data. ${exception.message}") } val decryptedData = decrypt(data, user) @@ -202,24 +117,13 @@ internal class PeerToPeerWorker( return String(decryptedData.data, StandardCharsets.UTF_8) } - /** - * Decrypts and verifies base64 string from users. - * - * *Important* Requires private key in local storage. - * - * @param text Encrypted String. - * @param user Sender Card with Public Key to verify with. - * @param date Date of encryption to use proper card version. - * - * @return Decrypted String. - */ internal fun decrypt(text: String, user: Card, date: Date): String { require(text.isNotEmpty()) { "\'text\' should not be empty" } val data = try { Data.fromBase64String(text) } catch (exception: IllegalArgumentException) { - throw EThreeException("Error while converting String to Data. Wrong base64 String.") + throw EThreeException("Error while converting String to Data. ${exception.message}") } val decryptedData = decrypt(data, user, date) @@ -227,53 +131,20 @@ internal class PeerToPeerWorker( return String(decryptedData.data, StandardCharsets.UTF_8) } - /** - * Signs and encrypts data for user. - * - * *Important* Automatically includes self key to recipientsKeys. - * - * *Important* Requires private key in local storage. - * - * @param data Data to encrypt. - * @param user User Card to encrypt for. - * - * @return Encrypted data. - */ internal fun encrypt(data: Data, user: Card): Data = encrypt(data, FindUsersResult(mutableMapOf(user.identity to user))) - /** - * Signs and encrypts string for user. - * - * *Important* Automatically includes self key to recipientsKeys. - * - * *Important* Requires private key in local storage. - * - * @param text String to encrypt. - * @param user User Card to encrypt for. - * - * @return Encrypted String. - */ internal fun encrypt(text: String, user: Card): String = encrypt(text, FindUsersResult(mutableMapOf(user.identity to user))) - /** - * Encrypts data stream. - * - * *Important* Automatically includes self key to recipientsKeys. - * - * *Important* Requires private key in local storage. - * - * @param inputStream Data stream to be encrypted. - * @param outputStream Stream with encrypted data. - * @param user User Card to encrypt for. - */ internal fun encrypt(inputStream: InputStream, outputStream: OutputStream, user: Card) = encrypt(inputStream, outputStream, FindUsersResult(mutableMapOf(user.identity to user))) - internal fun encryptInternal(inputStream: InputStream, - outputStream: OutputStream, - publicKeys: List?) { + private fun encryptInternal(inputStream: InputStream, + outputStream: OutputStream, + publicKeys: List?) { + if (inputStream.available() == 0) throw EmptyArgumentException("inputStream") + val selfKeyPair = keyStorageLocal.load() val pubKeys = mutableListOf(selfKeyPair.publicKey) @@ -288,8 +159,8 @@ internal class PeerToPeerWorker( crypto.encrypt(inputStream, outputStream, pubKeys) } - internal fun encryptInternal(data: Data, - publicKeys: List?): Data { // TODO check for empty/null args + private fun encryptInternal(data: Data, + publicKeys: List?): Data { require(data.data.isNotEmpty()) { "\'data\' should not be empty." } val selfKeyPair = keyStorageLocal.load() @@ -305,7 +176,7 @@ internal class PeerToPeerWorker( return Data(crypto.signThenEncrypt(data.data, selfKeyPair.privateKey, pubKeys)) } - internal fun decryptInternal(data: Data, publicKey: VirgilPublicKey?): Data { + private fun decryptInternal(data: Data, publicKey: VirgilPublicKey?): Data { require(data.data.isNotEmpty()) { "\'data\' should not be empty." } val selfKeyPair = keyStorageLocal.load() @@ -314,8 +185,8 @@ internal class PeerToPeerWorker( return try { Data(crypto.decryptThenVerify(data.data, selfKeyPair.privateKey, pubKey)) } catch (exception: Throwable) { - when (exception) { - is SignatureIsNotValidException, is VerificationException -> { // TODO test this case + when (exception.cause) { + is SignatureIsNotValidException -> { throw EThreeException("Verification of message failed. This may be caused by " + "rotating sender key. Try finding new one") } @@ -326,98 +197,38 @@ internal class PeerToPeerWorker( // Backward compatibility deprecated methods -------------------------------------------------- - /** - * Signs then encrypts data for a group of users. - * - * *Important* Automatically includes self key to recipientsKeys. - * - * *Important* Requires private key in local storage. - * - * *Note* Avoid key duplication. - * - * @param text String to encrypt. - * @param lookupResult Result of lookupPublicKeys call recipient PublicKeys to sign and - * encrypt with. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ - @Deprecated("Use encryptForUsers method instead.") // TODO change to actual fun name + @Deprecated("Use encryptForUsers method instead.") internal fun encrypt(text: String, lookupResult: LookupResult): String { - val data = Data(text.toByteArray(StandardCharsets.UTF_8)) // TODO check exception type + require(text.isNotEmpty()) { "\'text\' should not be empty" } + + val data = try { + Data(text.toByteArray(StandardCharsets.UTF_8)) + } catch (exception: IllegalArgumentException) { + throw EThreeException("Error while converting String to Data. ${exception.message}") + } return encryptInternal(data, lookupResult.toPublicKeys()).toBase64String() } - /** - * Signs then encrypts data for a group of users. - * - * *Important* Automatically includes self key to recipientsKeys. - * - * *Important* Requires private key in local storage. - * - * *Note* Avoid key duplication. - * - * @param data Data to encrypt - * @param lookupResult Result of lookupPublicKeys call recipient PublicKeys to sign and - * encrypt with. - * - * @return Encrypted Data. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ @Deprecated("Use encryptForUsers method instead.") @JvmOverloads internal fun encrypt(data: ByteArray, lookupResult: LookupResult? = null): ByteArray = encryptInternal(Data(data), lookupResult.toPublicKeys()).data - /** - * Encrypts data stream for a group of users. - * - * *Important* Automatically includes self key to recipientsKeys. - * - * *Important* Requires private key in local storage. - * - * *Note* Avoid key duplication. - * - * @param inputStream Data stream to be encrypted. - * @param outputStream Stream with encrypted data. - * @param lookupResult Result of lookupPublicKeys call recipient PublicKeys to sign and - * encrypt with. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ - @Deprecated("Use encryptForUsers method instead.") // TODO change to actual methods signature + @Deprecated("Use encryptForUsers method instead.") internal fun encrypt(inputStream: InputStream, outputStream: OutputStream, lookupResult: LookupResult) = encryptInternal(inputStream, outputStream, lookupResult.toPublicKeys()) - /** - * Decrypts and verifies encrypted text that is in base64 [String] format. - * - * *Important* Automatically includes self key to recipientsKeys. - * - * *Important* Requires private key in local storage. - * - * *Note* Avoid key duplication. - * - * @param base64String Encrypted String. - * @param sendersKey Sender PublicKey to verify with. - * - * @return Decrypted String. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ @Deprecated("Use decryptFromUser method instead.") internal fun decrypt(base64String: String, sendersKey: VirgilPublicKey): String { + require(base64String.isNotEmpty()) { "\'text\' should not be empty" } + val data = try { Data.fromBase64String(base64String) } catch (exception: IllegalArgumentException) { - throw EThreeException("Error while converting String to Data. Wrong base64 String.") + throw EThreeException("Error while converting String to Data. ${exception.message}") } val decryptedData = decryptInternal(data, sendersKey) @@ -425,17 +236,6 @@ internal class PeerToPeerWorker( return String(decryptedData.data, StandardCharsets.UTF_8) } - /** - * Decrypts and verifies encrypted data. - * - * *Important* Requires private key in local storage. - * - * @param data Data to decrypt. - * @param sendersKey Sender PublicKey to verify with. - * - * @throws PrivateKeyNotFoundException - * @throws CryptoException - */ @Deprecated("Use decryptFromUser method instead.") @JvmOverloads internal fun decrypt(data: ByteArray, sendersKey: VirgilPublicKey? = null): ByteArray = diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt index b4e55d31..0ea4328a 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt @@ -33,10 +33,6 @@ package com.virgilsecurity.android.common.worker -import com.virgilsecurity.android.common.EThreeCore -import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException -import com.virgilsecurity.android.common.exception.PublicKeyDuplicateException -import com.virgilsecurity.android.common.exception.PublicKeyNotFoundException import com.virgilsecurity.android.common.manager.LookupManager import com.virgilsecurity.android.common.model.FindUsersResult import com.virgilsecurity.android.common.model.LookupResult @@ -50,13 +46,6 @@ internal class SearchWorker( private val lookupManager: LookupManager ) { - /** - * Returns cards from local storage for given [identities]. // TODO add Result/Completable reference in all fun's descriptions - * - * @param identities Identities. - * - * @return [FindUsersResult] with found users. - */ internal fun findCachedUsers(identities: List): Result = object : Result { override fun get(): FindUsersResult { @@ -64,27 +53,12 @@ internal class SearchWorker( } } - /** - * Returns card from local storage for given [identity]. - * - * @param identity Identity. - * - * @return [Card] if it exists, *null* otherwise. - */ internal fun findCachedUser(identity: String): Result = object : Result { override fun get(): Card? { return lookupManager.lookupCachedCard(identity) } } - /** - * Retrieves users Cards from the Virgil Cloud or local storage if exists. - * - * @param identities Array of identities to find. - * @param forceReload Will not use local cached cards if *true*. - * - * @return [FindUsersResult] with found users. - */ internal fun findUsers(identities: List, forceReload: Boolean = false): Result = object : Result { @@ -93,14 +67,6 @@ internal class SearchWorker( } } - /** - * Retrieves user Card from the Virgil Cloud or local storage if exists. - * - * @param identity Identity to find. - * @param forceReload Will not use local cached card if *true*. - * - * @return [Card] that corresponds to provided [identity]. - */ internal fun findUser(identity: String, forceReload: Boolean = false): Result = object : Result { override fun get(): Card { @@ -108,42 +74,11 @@ internal class SearchWorker( } } - /** - * Retrieves user public key from the cloud for encryption/verification operations. - * - * Searches for public key with specified [identity] and returns map of [String] -> - * [PublicKey] in [onResultListener] callback or [Throwable] if something went wrong. - * - * [PublicKeyNotFoundException] will be thrown if public key wasn't found. - * - * Can be called only if private key is on the device, otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * @throws PrivateKeyNotFoundException - * @throws PublicKeyDuplicateException - */ - @Deprecated("Use findUser instead.") // TODO add replaceWith + @Deprecated("Use findUser instead.") internal fun lookupPublicKey(identity: String): Result = lookupPublicKeys(listOf(identity)) - /** - * Retrieves user public keys from the cloud for encryption/verification operations. - * - * Searches for public keys with specified [identities] and returns map of [String] -> - * [PublicKey] in [onResultListener] callback or [Throwable] if something went wrong. - * - * [PublicKeyNotFoundException] will be thrown for the first not found public key. - * [EThreeCore.register] - * - * Can be called only if private key is on the device, otherwise [PrivateKeyNotFoundException] - * exception will be thrown. - * - * To start execution of the current function, please see [Result] description. - * - * @throws PrivateKeyNotFoundException - * @throws PublicKeyDuplicateException - */ - @Deprecated("Use findUsers instead.") // TODO add replaceWith + @Deprecated("Use findUsers instead.") internal fun lookupPublicKeys(identities: List): Result = object : Result { override fun get(): LookupResult { diff --git a/ethree-enclave/build.gradle b/ethree-enclave/build.gradle index d7e98fcf..87b37581 100644 --- a/ethree-enclave/build.gradle +++ b/ethree-enclave/build.gradle @@ -76,28 +76,6 @@ dependencies { compileOnly "com.google.android:android:$versions.android" } -task generateVersionVirgilAgent { - outputs.dir "$buildDir/generated" - doFirst { - def versionFile = file("$buildDir/generated/release/com/virgilsecurity/android/ethreeenclave/build/VersionVirgilAgent.kt") - versionFile.parentFile.mkdirs() - versionFile.text = - """ -package com.virgilsecurity.android.ethreeenclave.build; - -object VersionVirgilAgent { - const val VERSION = "$project.version" -} -""" - } -} - -project.android.sourceSets.main.java.srcDirs = ["${buildDir}/generated/release/", "src/main/java"] - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - dependsOn(generateVersionVirgilAgent) -} - sourceCompatibility = "8" targetCompatibility = "8" diff --git a/ethree-kotlin/build.gradle b/ethree-kotlin/build.gradle index 2114738b..6a37521b 100644 --- a/ethree-kotlin/build.gradle +++ b/ethree-kotlin/build.gradle @@ -73,30 +73,6 @@ dependencies { compileOnly "com.google.android:android:$versions.android" } -task generateVersionVirgilAgent { - outputs.dir "$buildDir/generated" - doFirst { - def versionFile = file("$buildDir/generated/release/com/virgilsecurity/android/ethree/build/VersionVirgilAgent.kt") - versionFile.parentFile.mkdirs() - versionFile.text = - """ -package com.virgilsecurity.android.ethree.build; - -object VersionVirgilAgent { - const val VERSION = "$project.version" -} -""" - } -} - - - -project.android.sourceSets.main.java.srcDirs = ["${buildDir}/generated/release/", "src/main/java"] - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - dependsOn(generateVersionVirgilAgent) -} - sourceCompatibility = "8" targetCompatibility = "8" diff --git a/tests/.gitignore b/tests/.gitignore index 1dfb2cec..8f5f7600 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -174,3 +174,4 @@ fabric.properties # Project-specific !crypto/libs/virgil_crypto_java.jar compat_data.json +!compat_data.json.enc diff --git a/tests/build.gradle b/tests/build.gradle index 75950782..55315b25 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -67,6 +67,9 @@ android { it.buildConfigField "String", "VIRGIL_BASE_URL", "$VIRGIL_BASE_URL" } } + lintOptions { + abortOnError false + } } dependencies { diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/model/TicketTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/common/model/TicketTest.kt similarity index 97% rename from tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/model/TicketTest.kt rename to tests/src/androidTest/java/com/virgilsecurity/android/common/model/TicketTest.kt index f0d4d83b..36cb1e72 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/model/TicketTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/common/model/TicketTest.kt @@ -31,12 +31,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethree.common.model +package com.virgilsecurity.android.common.model import android.os.Parcel import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.exception.GroupIdTooShortException -import com.virgilsecurity.android.common.model.Ticket import com.virgilsecurity.common.model.Data import com.virgilsecurity.sdk.crypto.HashAlgorithm import com.virgilsecurity.sdk.crypto.VirgilCrypto diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/storage/sql/SQLCardStorageTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorageTest.kt similarity index 96% rename from tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/storage/sql/SQLCardStorageTest.kt rename to tests/src/androidTest/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorageTest.kt index 0dfe3032..b77c0d0b 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/common/storage/sql/SQLCardStorageTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorageTest.kt @@ -31,18 +31,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethree.common.storage.sql +package com.virgilsecurity.android.common.storage.sql import androidx.room.Room import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.gson.JsonObject import com.google.gson.JsonParser import com.virgilsecurity.android.common.storage.CardStorage -import com.virgilsecurity.android.common.storage.sql.ETheeDatabase -import com.virgilsecurity.android.common.storage.sql.SQLCardStorage import com.virgilsecurity.android.common.storage.sql.model.CardEntity -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.context +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestConfig.Companion.context import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier import com.virgilsecurity.sdk.crypto.VirgilCardCrypto diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt b/tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestConfig.kt similarity index 98% rename from tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt rename to tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestConfig.kt index 6ac56c6c..9f83a9cf 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestConfig.kt @@ -31,7 +31,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethree.utils +package com.virgilsecurity.android.common.utils import androidx.test.platform.app.InstrumentationRegistry import com.virgilsecurity.android.ethree.BuildConfig diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt b/tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestUtils.kt similarity index 96% rename from tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt rename to tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestUtils.kt index a2ce2976..a00d0bad 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestUtils.kt @@ -31,11 +31,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethree.utils +package com.virgilsecurity.android.common.utils -import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilCrypto +import com.virgilsecurity.android.common.utils.TestConfig.Companion.virgilCrypto import com.virgilsecurity.sdk.cards.Card -import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.ModelSigner import com.virgilsecurity.sdk.cards.model.RawCardContent import com.virgilsecurity.sdk.cards.model.RawSignedModel diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/AuthenticationTests.kt b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/AuthenticationTests.kt similarity index 95% rename from tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/AuthenticationTests.kt rename to tests/src/androidTest/java/com/virgilsecurity/android/common/worker/AuthenticationTests.kt index c1786233..1e62c6cc 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/AuthenticationTests.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/AuthenticationTests.kt @@ -31,15 +31,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethree.worker +package com.virgilsecurity.android.common.worker import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilCrypto -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestConfig.Companion.virgilCrypto +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryNotFoundException import com.virgilsecurity.sdk.storage.DefaultKeyStorage diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/BackupTests.kt b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/BackupTests.kt similarity index 96% rename from tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/BackupTests.kt rename to tests/src/androidTest/java/com/virgilsecurity/android/common/worker/BackupTests.kt index 1634cbd7..a48af4e8 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/BackupTests.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/BackupTests.kt @@ -31,7 +31,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethree.worker +package com.virgilsecurity.android.common.worker import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback @@ -39,21 +39,18 @@ import com.virgilsecurity.android.common.exception.EThreeException import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.common.exception.WrongPasswordException import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.interaction.async.EThreeBackupTest.Companion.WRONG_PASSWORD -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.keyknox.KeyknoxManager import com.virgilsecurity.keyknox.client.KeyknoxClient import com.virgilsecurity.keyknox.cloud.CloudKeyStorage import com.virgilsecurity.keyknox.crypto.KeyknoxCrypto -import com.virgilsecurity.keyknox.exception.EntryNotFoundException import com.virgilsecurity.keyknox.storage.SyncKeyStorage import com.virgilsecurity.pythia.brainkey.BrainKey import com.virgilsecurity.pythia.brainkey.BrainKeyContext import com.virgilsecurity.pythia.client.VirgilPythiaClient import com.virgilsecurity.pythia.crypto.VirgilPythiaCrypto import com.virgilsecurity.sdk.crypto.VirgilCrypto -import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryNotFoundException import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider import com.virgilsecurity.sdk.storage.DefaultKeyStorage import com.virgilsecurity.sdk.storage.JsonKeyEntry diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/GroupTests.kt b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt similarity index 99% rename from tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/GroupTests.kt rename to tests/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt index 58dfcbe7..f80d20d7 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/GroupTests.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt @@ -31,7 +31,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethree.worker +package com.virgilsecurity.android.common.worker import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.gson.JsonObject @@ -40,8 +40,8 @@ import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.* import com.virgilsecurity.android.common.model.* import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.common.model.Data import com.virgilsecurity.crypto.foundation.Base64 import com.virgilsecurity.sdk.common.TimeSpan @@ -52,7 +52,6 @@ import com.virgilsecurity.sdk.storage.DefaultKeyStorage import com.virgilsecurity.sdk.storage.JsonKeyEntry import org.junit.Assert.* import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import java.io.InputStreamReader diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/PeerToPeerTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt similarity index 88% rename from tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/PeerToPeerTest.kt rename to tests/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt index bb186711..c18634b1 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/PeerToPeerTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt @@ -31,15 +31,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethree.worker +package com.virgilsecurity.android.common.worker import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.EThreeException import com.virgilsecurity.android.common.model.FindUsersResult import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.common.model.Data import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.storage.DefaultKeyStorage @@ -143,7 +143,6 @@ class PeerToPeerTest { @Test fun encrypt_decrypt_without_private_key() { val keyStorage = DefaultKeyStorage(TestConfig.DIRECTORY_PATH, TestConfig.KEYSTORE_NAME) assertFalse(keyStorage.exists(ethree.identity)) -// keyStorage.delete(ethree.identity) // FIXME check why we delete key in swift val card = TestUtils.publishCard() @@ -280,6 +279,37 @@ class PeerToPeerTest { assertEquals(TEXT, decrypted) } + @Test fun signature_invalid_test() { + ethree.register().execute() + + val identityTwo = UUID.randomUUID().toString() + val ethreeTwo = EThree(identityTwo, + object : OnGetTokenCallback { + override fun onGetToken(): String { + return TestUtils.generateTokenString(identityTwo) + } + }, + TestConfig.context) + ethreeTwo.register().execute() + + val cardTwo = ethree.findUser(ethreeTwo.identity).get() + val encrypted = ethree.encrypt(TEXT, cardTwo) + + // Outdate ethree pub key + ethree.cleanup() + ethree.rotatePrivateKey().execute() + + val cardOne = ethreeTwo.findUser(ethree.identity).get() + + try { + ethreeTwo.decrypt(encrypted, cardOne) + fail() + } catch (throwable: Throwable) { + if (throwable !is EThreeException) + fail() + } + } + companion object { private const val TEXT = "Hello, my name is text. I am here to be encrypted (:" } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/SearchTests.kt b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/SearchTests.kt similarity index 97% rename from tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/SearchTests.kt rename to tests/src/androidTest/java/com/virgilsecurity/android/common/worker/SearchTests.kt index d72221d8..cc587e54 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/worker/SearchTests.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/SearchTests.kt @@ -31,15 +31,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.virgilsecurity.android.ethree.worker +package com.virgilsecurity.android.common.worker import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.callback.OnKeyChangedCallback import com.virgilsecurity.android.common.exception.FindUsersException import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.exception.EmptyArgumentException import com.virgilsecurity.sdk.storage.DefaultKeyStorage diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt index fe45f781..b70abdab 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt @@ -35,12 +35,14 @@ package com.virgilsecurity.android.ethree.interaction.async import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback -import com.virgilsecurity.android.common.exception.* +import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.exception.FindUsersException +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilBaseUrl -import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilCrypto -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestConfig.Companion.virgilBaseUrl +import com.virgilsecurity.android.common.utils.TestConfig.Companion.virgilCrypto +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager @@ -49,7 +51,6 @@ import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier import com.virgilsecurity.sdk.client.VirgilCardClient import com.virgilsecurity.sdk.common.TimeSpan import com.virgilsecurity.sdk.crypto.* -import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryNotFoundException import com.virgilsecurity.sdk.jwt.JwtGenerator import com.virgilsecurity.sdk.jwt.accessProviders.GeneratorJwtProvider import com.virgilsecurity.sdk.storage.DefaultKeyStorage @@ -415,7 +416,7 @@ class EThreeAuthTest { } override fun onError(throwable: Throwable) { - if (throwable !is KeyEntryNotFoundException) + if (throwable !is PrivateKeyNotFoundException) fail(throwable.message) waiter.countDown() diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt index 228db59b..c3c4a796 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt @@ -41,9 +41,9 @@ import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.common.exception.RestoreKeyException import com.virgilsecurity.android.common.exception.WrongPasswordException import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilBaseUrl -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestConfig.Companion.virgilBaseUrl +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.keyknox.KeyknoxManager diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt index b58754b0..8b30c3fa 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt @@ -36,11 +36,10 @@ package com.virgilsecurity.android.ethree.interaction.async import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.EThreeException -import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager @@ -52,7 +51,6 @@ import com.virgilsecurity.sdk.crypto.VirgilAccessTokenSigner import com.virgilsecurity.sdk.crypto.VirgilCardCrypto import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.VirgilKeyPair -import com.virgilsecurity.sdk.exception.EmptyArgumentException import com.virgilsecurity.sdk.jwt.JwtGenerator import com.virgilsecurity.sdk.jwt.accessProviders.GeneratorJwtProvider import com.virgilsecurity.sdk.storage.DefaultKeyStorage diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt index e6483503..de0e4616 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt @@ -35,11 +35,13 @@ package com.virgilsecurity.android.ethree.interaction.async import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback -import com.virgilsecurity.android.common.exception.* +import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.exception.FindUsersException +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager @@ -48,7 +50,6 @@ import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier import com.virgilsecurity.sdk.client.VirgilCardClient import com.virgilsecurity.sdk.common.TimeSpan import com.virgilsecurity.sdk.crypto.* -import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryNotFoundException import com.virgilsecurity.sdk.jwt.JwtGenerator import com.virgilsecurity.sdk.jwt.accessProviders.GeneratorJwtProvider import com.virgilsecurity.sdk.storage.DefaultKeyStorage @@ -159,7 +160,7 @@ class EThreeNegativeTest { return eThree } - @Test(expected = KeyEntryNotFoundException::class) + @Test(expected = PrivateKeyNotFoundException::class) fun cleanup_fail_without_bootstrap() { eThree.cleanup() } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt index 69a6ca2b..b7eb39ff 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt @@ -36,8 +36,8 @@ package com.virgilsecurity.android.ethree.interaction.async import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt index 029a522a..55a64e4e 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt @@ -36,8 +36,8 @@ package com.virgilsecurity.android.ethree.interaction.sync import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.* import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.keyknox.exception.EntryNotFoundException import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.model.RawSignedModel @@ -48,7 +48,6 @@ import com.virgilsecurity.sdk.crypto.VirgilAccessTokenSigner import com.virgilsecurity.sdk.crypto.VirgilCardCrypto import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.VirgilKeyPair -import com.virgilsecurity.sdk.exception.EmptyArgumentException import com.virgilsecurity.sdk.jwt.JwtGenerator import com.virgilsecurity.sdk.jwt.accessProviders.GeneratorJwtProvider import com.virgilsecurity.sdk.storage.DefaultKeyStorage diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt index a3671e01..37089c87 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt @@ -35,8 +35,8 @@ package com.virgilsecurity.android.ethree.interaction.sync import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.ethree.utils.TestConfig -import com.virgilsecurity.android.ethree.utils.TestUtils +import com.virgilsecurity.android.common.utils.TestConfig +import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.keyknox.KeyknoxManager import com.virgilsecurity.keyknox.client.KeyknoxClient import com.virgilsecurity.keyknox.cloud.CloudKeyStorage diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java index 9d21cfff..0bbb5cb1 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java @@ -37,7 +37,7 @@ import com.virgilsecurity.android.common.callback.OnGetTokenCallback; import com.virgilsecurity.android.ethree.interaction.EThree; -import com.virgilsecurity.android.ethree.utils.TestConfig; +import com.virgilsecurity.android.common.utils.TestConfig; import com.virgilsecurity.common.callback.OnCompleteListener; import com.virgilsecurity.common.callback.OnResultListener; import com.virgilsecurity.sdk.cards.Card; diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java index ff24603a..1e19b9cb 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java @@ -37,8 +37,8 @@ import com.virgilsecurity.android.common.callback.OnGetTokenCallback; import com.virgilsecurity.android.ethree.interaction.EThree; -import com.virgilsecurity.android.ethree.utils.TestConfig; -import com.virgilsecurity.android.ethree.utils.TestUtils; +import com.virgilsecurity.android.common.utils.TestConfig; +import com.virgilsecurity.android.common.utils.TestUtils; import com.virgilsecurity.common.callback.OnCompleteListener; import com.virgilsecurity.common.callback.OnResultListener; import com.virgilsecurity.sdk.cards.Card; diff --git a/tests/src/androidTest/resources/compat_data.json.enc b/tests/src/androidTest/resources/compat_data.json.enc new file mode 100644 index 0000000000000000000000000000000000000000..f452bf3d0e2dde84a0bc9ceaeb3e717232294e0c GIT binary patch literal 1808 zcmV+r2k-c4H*`Hl#l%MKb+6HU{`6Q=t7pHB0SX%Nkgg=F;7A)sGGv^?_lkY|c<9J_ zMBrn!G&q&ee0S7cXW+&`Dk76PWLWO&EZ*BG3lj^6X9{{RT^|^Qgh6AZZ%sruL4abx zYV^Cl%1h`Fec53Rvc=Xhz10yuQ6zKdhQ0KOWMEiqZVs^@e*p5h>oFG%!aSqhb|0aJ za*>(vnjjixS&W`@;KPlAkSViJ%sxYk;;kFVYC_1HI7{*3#w;eT$JC#_f7z#A;=lJi z@6=s4MDnj$#&08{Sk#MF7jk72d4NXhyEKSCE=kGoxc!yseQMRyBMdXF8?Io1ElzeM ztC)amp~O8{PoWk8^C+;k~W@4bOuN3kMy z9zy&(4E}w;`5Odj(v&GXk=n|jtz6E)d`}oD?FNV#JppK_B)uP#a0(J3g(!d~KX!a=d#M~|&vCj{GOm|(nSIx%doWhs6b-gMm+m_f z=)^)AL=iOl-2jFH7J^bGKtw*TDK7#i;wr96j#l5@5ltg5q%@_Q7Z@xx6*dgn{GRY# zwhRuE4W;|zy$fiX>=1amgLQJJIHf!Z!l9nkV!gLnA09=(sXs|y!*E}>_;+D?Euj%n zRc)#}Tb8;t_-Ey&rqG3f_aV0KG$2sfV4BD%%4&e~Dk4ZOb3!BxxvO#~bQhB3piuvC z+ABIyVvV)8R->rMx@B>@U$s5sbF2_ok6TY6B-B;5p*_H*RI07lZuK@4WhQa+{*Kw2 zi{TYsdU;bDFh%1$On>Zp*xBobSPh7G9{zz0Y1FCcpJ)Q1-b2K4eU!J2g1B&3ukukl zuz$bGbcn6#0&KjR;0e|=CF!SBI!`aS%%USMM_TlhlK?y>k}k6v&A?Snyl^4{yZBXR zNEPXNismcG{-BM^NDF7}DKQPp7900h)&0}h9z){H!3kPyoQs2k^yvRBMV+?#*^Fbn zRlaakE~XX?gGsTx5^Ce|F=#9vI zkW51_8juceaNWmyNd%Vu(-n@6-S3B|VxaCp7+oebGjm>zWKxNkMk8|*8`$yBF()+~ z3oSil4OWe^*7*KGM1ng*G2d~bWKt4eL$p}YJqv?1)jcKhOxY%Q=Lgza2xR9I0aYl= zv^a2Hqau?r(rtoq_;rxB{v~0Jiq_gDC`3QSn16;DJ#Orw^YS{nN}n(T!XmiXD|-cJ zO(J*6sQ5fpmhbSl5{v$8A(49PgCcg>%S?qg^xCpbF7{7K^1E(K4N|aR=r>e07x?qQ z%}6lo`w5L??b-6{JX-u|5+By)a-Aa!aZ^AyL2hY<55aMG1u>2~BieU*nW7M?zulFR zcmx%K(_1M!jO5jf$pS@~BXMO{tYTqQDjoinqoL=%%_qV!15o} z?}dw=tOELzc-Gh_ml&FlSu0|CCZnWorxrfeDGY~{v%<(x?e3(_3om_%LduKFy<6*X7 zftVupAG!pD1%rTHpt?q@#=bi~ zib;PNMei3iQ^h-X0$FxQ%bPA8BZfp|WA`c@HNpD)5$V+I5_Ss496&mroI_RJk(Zb+YAH+gh@ji2Nl9{i}XTsgZj<8_ejbh zIw=~P-_KmM65~83O+hfEZzvCh;pVA6w;-OOpg=Sc5&0N$zfTG3VkOvF@x$zfM4;jw zI)pbn9@%Ot;{8VJk}>~c0yKqx;#&{tvcwW~$- z`K7PsJiP$AH|bYp!J)1sEq+^jSpSg0Uv7E&YABUFu4*{lsI6{JZNMXsMDoHtKGau4 yp%sWlIVsO0BaE%BVk_j|HX1OO3`~6GYb07D@ZF1OEVIx}_ePU%WsNL0$r-e$1!BMe literal 0 HcmV?d00001 diff --git a/testsenclave/build.gradle b/testsenclave/build.gradle index ca4e2297..1c3c0489 100644 --- a/testsenclave/build.gradle +++ b/testsenclave/build.gradle @@ -58,6 +58,9 @@ android { it.buildConfigField "String", "VIRGIL_BASE_URL", "$VIRGIL_BASE_URL" } } + lintOptions { + abortOnError false + } } dependencies { From dca159f7546bafac1b6ad065009e02651eafe94b Mon Sep 17 00:00:00 2001 From: BuddahLD Date: Thu, 26 Sep 2019 19:55:55 +0300 Subject: [PATCH 04/13] Resolved epoch slash question --- .../android/common/storage/cloud/CloudTicketStorage.kt | 2 +- .../com/virgilsecurity/android/common/worker/GroupTests.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt index 2e7ac1bb..c858a7de 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt @@ -78,7 +78,7 @@ class CloudTicketStorage( val params = KeyknoxPushParams(identities + this.identity, GROUP_SESSION_ROOT, sessionId, - "$epoch") // FIXME remove slash? check compat test + "$epoch") keyknoxManager.pushValue(params, ticketData, diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt index f80d20d7..f5063af7 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt @@ -477,7 +477,7 @@ class GroupTests { try { group3.update().execute() fail() - } catch (e: Exception) { // TODO do we need GroupException here? + } catch (e: Exception) { } assertNull(ethree3.getGroup(this.groupId)) @@ -485,7 +485,7 @@ class GroupTests { try { ethree3.loadGroup(groupId, card1).get() fail() - } catch (e: Exception) { // TODO do we need GroupException here? + } catch (e: Exception) { } // User 1 encrypts, reAdds User3 @@ -580,7 +580,7 @@ class GroupTests { @Test fun ste45() { - // Compatability test + // Compatibility test val compatDataStream = this.javaClass.classLoader?.getResourceAsStream("compat_data.json") val compatJson = JsonParser().parse(InputStreamReader(compatDataStream)) as JsonObject From 2a585592982ab87e5badb89db3e55fb7892a3fe1 Mon Sep 17 00:00:00 2001 From: BuddahLD Date: Thu, 26 Sep 2019 21:52:47 +0300 Subject: [PATCH 05/13] Travis build fixed --- .travis.yml | 85 ++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0e0eb3b4..65434f1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,9 @@ jdk: oraclejdk8 # Include branches branches: - only: + only: - dev - master - - travis-setup before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock @@ -15,60 +14,60 @@ before_cache: cache: directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - - $HOME/.android/build-cache + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + - $HOME/.android/build-cache env: global: - - ANDROID_BUILD_API=28 - - ANDROID_BUILD_TOOLS=28.0.3 - - ANDROID_EMULATOR_API=24 - - ADB_INSTALL_TIMEOUT=5 # minutes - - ANDROID_ABI=default/armeabi-v7a - - secure: Nz/+LNm9NO3PgvrwLIKDVjlJTskzZPKYXWnNe1T8kUflcQp6dygc9M3tqOgXVRTb33DPhZFZoM04fHJ7iAAyOydH0uNt3nI8zujFiQg7wGOm4vUczk6PYQrWNe/uAyrov+KjLbjNElBxZPDgMDTRD2e3j+UG0X+Xzjm3LqrWChpDjS037EuCpEILNdNETkHeAMVwAvcHrWkXiFMO6aYCHV+teo0zivrELbo6MCaxfMOKY0eMckgxJFtQbAF3hshr98325cczvIzQt5J/bFBkMaBx0IkSDAktDk++jVnoqIfpQVkM6x4mSjz64Q22BW5wIHsgfkT/Cjm7vn1LNpQsPO28p1Ja/W3TqVCeU1XR6sm7Zn+3b7ecSfGjh7p8L6gu9rzpsGIHOG88aWZuqpnF5Vgxjs4l2AXd/luAjv0ATLY45A9SMllLgzixrV4R0xn/DWUlKHsZ4KjiZZipFRXHYvxI/iB8BJR+fRSPKqWWtVxB44mHN7zBS5PvRfu/L7AugF3C2+5OgU+r15u+hQYdHxOrQhWEUKObSJ42MKjEzwaH6oioYKee0ImdB1iPu0IP1jvcImtWanPGU/wxP4ssjTdEOAZepJg90gNes/Hx2gdal6iRtvN2G1xpCJXty4EBLbK2lIfXP6//IiQG0EDLAJfw5npRBluiDvG/tcDxW1E= - - secure: C7b6XlTt+gqr3O4avm7CwgheSi0o0Hn0SQPZHa1Fs9+xbcNLI5EAE6BMNIDH/70aOl/DygJptxmr7Fx8NwAiU+dGWKqe6NhIqpZiizGz9o1J3FpoF5+v+r0777b2oHrqnUiBT7MO//3R1kFL4WmEyoEKzkwT5yMz3qnCI1feKCvKXtLdoyDt3E9w9fMz83lWSXIp0YYpaMyUDKBVwdkbPFbfvr9Ew2spWjTrUkb0gVTbz+GHzdX0Y0PrKHusdZiLQSd3MqmXP7y4VcIafzKEWgXaxe94GBc/r5v9RLBwhqORIhtcu0GJMq9KuWH2uROspgJ9jzgAAJcrTWxaNJ02oarRQF7QdJPdfuIq5XLGqOaMCLKAFjAaogyPacfbUmcfeFokbPW9I7LoHIDcCydSdYC3K3Iw725FoPoNhVncOuwxouMnaWECXLQVlxCQmNN8QSlTBC6bFrgQ/2NwkMPSkMKr+/dKxnPvnWmo6uzqGSZwnLoGlnIryNPzIf8CZTsSWcl6dWxw7LYNMuPOVZGz4AMN8NKFqIrbl0sE3cr00z8N1FHwX2hD2D6dYRMpnv5l6N6ZxxKQT4BwmmpnFIIiKJyoNodUlgYtZX3iJICLQcpmHTrTle+h6XT24biUFF/uPO2TUGmhCEVzLgdlPvbiCePlAg1oytJIxmXjrmCJx/A= + - ANDROID_BUILD_API=28 + - ANDROID_BUILD_TOOLS=28.0.3 + - ANDROID_EMULATOR_API=24 + - ADB_INSTALL_TIMEOUT=5 # minutes + - ANDROID_ABI=default/armeabi-v7a + - secure: Nz/+LNm9NO3PgvrwLIKDVjlJTskzZPKYXWnNe1T8kUflcQp6dygc9M3tqOgXVRTb33DPhZFZoM04fHJ7iAAyOydH0uNt3nI8zujFiQg7wGOm4vUczk6PYQrWNe/uAyrov+KjLbjNElBxZPDgMDTRD2e3j+UG0X+Xzjm3LqrWChpDjS037EuCpEILNdNETkHeAMVwAvcHrWkXiFMO6aYCHV+teo0zivrELbo6MCaxfMOKY0eMckgxJFtQbAF3hshr98325cczvIzQt5J/bFBkMaBx0IkSDAktDk++jVnoqIfpQVkM6x4mSjz64Q22BW5wIHsgfkT/Cjm7vn1LNpQsPO28p1Ja/W3TqVCeU1XR6sm7Zn+3b7ecSfGjh7p8L6gu9rzpsGIHOG88aWZuqpnF5Vgxjs4l2AXd/luAjv0ATLY45A9SMllLgzixrV4R0xn/DWUlKHsZ4KjiZZipFRXHYvxI/iB8BJR+fRSPKqWWtVxB44mHN7zBS5PvRfu/L7AugF3C2+5OgU+r15u+hQYdHxOrQhWEUKObSJ42MKjEzwaH6oioYKee0ImdB1iPu0IP1jvcImtWanPGU/wxP4ssjTdEOAZepJg90gNes/Hx2gdal6iRtvN2G1xpCJXty4EBLbK2lIfXP6//IiQG0EDLAJfw5npRBluiDvG/tcDxW1E= + - secure: C7b6XlTt+gqr3O4avm7CwgheSi0o0Hn0SQPZHa1Fs9+xbcNLI5EAE6BMNIDH/70aOl/DygJptxmr7Fx8NwAiU+dGWKqe6NhIqpZiizGz9o1J3FpoF5+v+r0777b2oHrqnUiBT7MO//3R1kFL4WmEyoEKzkwT5yMz3qnCI1feKCvKXtLdoyDt3E9w9fMz83lWSXIp0YYpaMyUDKBVwdkbPFbfvr9Ew2spWjTrUkb0gVTbz+GHzdX0Y0PrKHusdZiLQSd3MqmXP7y4VcIafzKEWgXaxe94GBc/r5v9RLBwhqORIhtcu0GJMq9KuWH2uROspgJ9jzgAAJcrTWxaNJ02oarRQF7QdJPdfuIq5XLGqOaMCLKAFjAaogyPacfbUmcfeFokbPW9I7LoHIDcCydSdYC3K3Iw725FoPoNhVncOuwxouMnaWECXLQVlxCQmNN8QSlTBC6bFrgQ/2NwkMPSkMKr+/dKxnPvnWmo6uzqGSZwnLoGlnIryNPzIf8CZTsSWcl6dWxw7LYNMuPOVZGz4AMN8NKFqIrbl0sE3cr00z8N1FHwX2hD2D6dYRMpnv5l6N6ZxxKQT4BwmmpnFIIiKJyoNodUlgYtZX3iJICLQcpmHTrTle+h6XT24biUFF/uPO2TUGmhCEVzLgdlPvbiCePlAg1oytJIxmXjrmCJx/A= android: components: - - tools # to get the new `repository-11.xml` - - tools # to install Android SDK tools - - platform-tools - - build-tools-$ANDROID_BUILD_TOOLS - - android-$ANDROID_BUILD_API - - android-$ANDROID_EMULATOR_API - - extra-android-m2repository # for design library - - extra-google-m2repository - - extra-google-google_play_services - - addon-google_apis-google-28 # google play services - - sys-img-armeabi-v7a-addon-google_apis-google-$ANDROID_BUILD_API - - sys-img-armeabi-v7a-android-$ANDROID_EMULATOR_API + - tools # to get the new `repository-11.xml` + - tools # to install Android SDK tools + - platform-tools + - build-tools-$ANDROID_BUILD_TOOLS + - android-$ANDROID_BUILD_API + - android-$ANDROID_EMULATOR_API + - extra-android-m2repository # for design library + - extra-google-m2repository + - extra-google-google_play_services + - addon-google_apis-google-28 # google play services + - sys-img-armeabi-v7a-addon-google_apis-google-$ANDROID_BUILD_API + - sys-img-armeabi-v7a-android-$ANDROID_EMULATOR_API licenses: - - 'android-sdk-preview-license-52d11cd2' - - 'android-sdk-license-.+' - - 'google-gdk-license-.+' + - 'android-sdk-preview-license-52d11cd2' + - 'android-sdk-license-.+' + - 'google-gdk-license-.+' before_install: - - yes | sdkmanager "platforms;android-28" - - mkdir -p "$ANDROID_HOME/licenses" - - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" - - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" - - chmod +x gradlew - - ./gradlew dependencies || true - - openssl aes-256-cbc -K $encrypted_62e1b1822e5d_key -iv $encrypted_62e1b1822e5d_iv -in compat_data.json.enc -out compat_data.json -d + - yes | sdkmanager "platforms;android-28" + - mkdir -p "$ANDROID_HOME/licenses" + - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" + - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" + - chmod +x gradlew + - ./gradlew dependencies || true + - openssl aes-256-cbc -K $encrypted_62e1b1822e5d_key -iv $encrypted_62e1b1822e5d_iv -in tests/src/androidTest/resources/compat_data.json.enc -out tests/src/androidTest/resources/compat_data.json -d before_script: -- android list targets -- android list sdk --extended --no-ui --all -- echo "y" | android update sdk -a --no-ui --filter sys-img-armeabi-v7a-android-$ANDROID_EMULATOR_API,sys-img-x86_64-android-$ANDROID_EMULATOR_API -- echo no | android create avd --force -n test -t android-$ANDROID_EMULATOR_API --abi $ANDROID_ABI -- QEMU_AUDIO_DRV=none emulator -avd test -no-window & -- android-wait-for-emulator -- adb shell input keyevent 82 & + - android list targets + - android list sdk --extended --no-ui --all + - echo "y" | android update sdk -a --no-ui --filter sys-img-armeabi-v7a-android-$ANDROID_EMULATOR_API,sys-img-x86_64-android-$ANDROID_EMULATOR_API + - echo no | android create avd --force -n test -t android-$ANDROID_EMULATOR_API --abi $ANDROID_ABI + - QEMU_AUDIO_DRV=none emulator -avd test -no-window & + - android-wait-for-emulator + - adb shell input keyevent 82 & script: -- "./gradlew clean build connectedCheck -PdisablePreDex --stacktrace" + - "./gradlew clean build connectedCheck -PdisablePreDex --stacktrace" after_success: - .ci/push-javadoc-to-gh-pages.sh + .ci/push-javadoc-to-gh-pages.sh From 02c255bb8c0f4143697238b5cbd2f81c7ba4d651 Mon Sep 17 00:00:00 2001 From: BuddahLD Date: Fri, 27 Sep 2019 16:42:07 +0300 Subject: [PATCH 06/13] Added internal, moved tests to common module --- .travis.yml | 100 +++---- ethree-common/.gitignore | 178 ++++++++++++- ethree-common/build.gradle | 45 +++- .../android/common/model/TicketTest.kt | 0 .../common/storage/sql/SQLCardStorageTest.kt | 9 +- .../android/common/utils/TestConfig.kt | 5 +- .../android/common/utils/TestUtils.kt | 0 .../common/worker/AuthenticationTests.kt | 3 +- .../android/common/worker/BackupTests.kt | 2 +- .../android/common/worker/GroupTests.kt | 58 ++++- .../android/common/worker/PeerToPeerTest.kt | 11 +- .../android/common/worker/SearchTests.kt | 2 +- .../resources/compat}/compat_data.json.enc | Bin .../resources/databases/cards.json | 17 ++ .../resources/databases/cards.sqlite3 | Bin 0 -> 20480 bytes ethree-common/src/main/AndroidManifest.xml | 11 +- .../android/common/EThreeCore.kt | 37 ++- .../android/common/manager/GroupManager.kt | 18 +- .../android/common/manager/LookupManager.kt | 6 +- .../android/common/model/Group.kt | 69 ++--- .../android/common/model/GroupInfo.kt | 2 +- .../android/common/model/RawGroup.kt | 6 +- .../android/common/model/Ticket.kt | 12 +- .../android/common/storage/CardStorage.kt | 2 +- .../common/storage/cloud/CloudKeyManager.kt | 6 +- .../storage/cloud/CloudTicketStorage.kt | 16 +- ...roupStorageFile.kt => FileGroupStorage.kt} | 4 +- ...{KeyStorageLocal.kt => LocalKeyStorage.kt} | 4 +- .../common/storage/sql/ETheeDatabase.kt | 2 +- .../common/storage/sql/SQLCardStorage.kt | 14 +- .../android/common/storage/sql/dao/CardDao.kt | 2 +- .../common/storage/sql/model/CardEntity.kt | 6 +- .../common/worker/AuthorizationWorker.kt | 16 +- .../android/common/worker/BackupWorker.kt | 10 +- .../android/common/worker/GroupWorker.kt | 2 +- .../android/common/worker/PeerToPeerWorker.kt | 14 +- .../android/common/worker/SearchWorker.kt | 2 +- ethree-enclave/build.gradle | 10 +- ethree-kotlin/build.gradle | 10 +- tests/.gitignore | 2 - tests/build.gradle | 13 - .../interaction/async/EThreeAuthTest.kt | 11 +- .../interaction/async/EThreeBackupTest.kt | 6 +- .../interaction/async/EThreeEncryptionTest.kt | 40 +-- .../interaction/async/EThreeNegativeTest.kt | 16 +- .../interaction/async/EThreeScopesTest.kt | 4 +- .../interaction/profile/EThreeProfile.kt | 3 +- .../interaction/sync/EThreeSyncNegative.kt | 11 +- .../interaction/sync/EThreeSyncPositive.kt | 10 +- .../java/interaction/EThreeScopesTest.java | 2 +- .../java/interaction/EThreeTestPositive.java | 4 +- .../android/ethree/utils/TestConfig.kt | 60 +++++ .../android/ethree/utils/TestUtils.kt | 103 ++++++++ tests/src/main/AndroidManifest.xml | 16 +- .../drawable-v24/ic_launcher_foreground.xml | 46 ++-- .../res/drawable/ic_launcher_background.xml | 243 ++++++++++++------ .../res/mipmap-anydpi-v26/ic_launcher.xml | 7 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 7 +- tests/src/main/res/values/colors.xml | 3 +- .../android/testsenclave/utils/TestConfig.kt | 3 +- testsenclave/src/main/AndroidManifest.xml | 5 +- 61 files changed, 939 insertions(+), 387 deletions(-) rename {tests => ethree-common}/src/androidTest/java/com/virgilsecurity/android/common/model/TicketTest.kt (100%) rename {tests => ethree-common}/src/androidTest/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorageTest.kt (95%) rename {tests => ethree-common}/src/androidTest/java/com/virgilsecurity/android/common/utils/TestConfig.kt (95%) rename {tests => ethree-common}/src/androidTest/java/com/virgilsecurity/android/common/utils/TestUtils.kt (100%) rename {tests => ethree-common}/src/androidTest/java/com/virgilsecurity/android/common/worker/AuthenticationTests.kt (98%) rename {tests => ethree-common}/src/androidTest/java/com/virgilsecurity/android/common/worker/BackupTests.kt (100%) rename {tests => ethree-common}/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt (87%) rename {tests => ethree-common}/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt (97%) rename {tests => ethree-common}/src/androidTest/java/com/virgilsecurity/android/common/worker/SearchTests.kt (100%) rename {tests/src/androidTest/resources => ethree-common/src/androidTest/resources/compat}/compat_data.json.enc (100%) create mode 100644 ethree-common/src/androidTest/resources/databases/cards.json create mode 100644 ethree-common/src/androidTest/resources/databases/cards.sqlite3 rename ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/{GroupStorageFile.kt => FileGroupStorage.kt} (98%) rename ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/{KeyStorageLocal.kt => LocalKeyStorage.kt} (97%) create mode 100644 tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt create mode 100644 tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt diff --git a/.travis.yml b/.travis.yml index 65434f1d..76340e5e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,70 +4,70 @@ jdk: oraclejdk8 # Include branches branches: - only: - - dev - - master + only: + - dev + - master before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - - $HOME/.android/build-cache + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + - $HOME/.android/build-cache env: - global: - - ANDROID_BUILD_API=28 - - ANDROID_BUILD_TOOLS=28.0.3 - - ANDROID_EMULATOR_API=24 - - ADB_INSTALL_TIMEOUT=5 # minutes - - ANDROID_ABI=default/armeabi-v7a - - secure: Nz/+LNm9NO3PgvrwLIKDVjlJTskzZPKYXWnNe1T8kUflcQp6dygc9M3tqOgXVRTb33DPhZFZoM04fHJ7iAAyOydH0uNt3nI8zujFiQg7wGOm4vUczk6PYQrWNe/uAyrov+KjLbjNElBxZPDgMDTRD2e3j+UG0X+Xzjm3LqrWChpDjS037EuCpEILNdNETkHeAMVwAvcHrWkXiFMO6aYCHV+teo0zivrELbo6MCaxfMOKY0eMckgxJFtQbAF3hshr98325cczvIzQt5J/bFBkMaBx0IkSDAktDk++jVnoqIfpQVkM6x4mSjz64Q22BW5wIHsgfkT/Cjm7vn1LNpQsPO28p1Ja/W3TqVCeU1XR6sm7Zn+3b7ecSfGjh7p8L6gu9rzpsGIHOG88aWZuqpnF5Vgxjs4l2AXd/luAjv0ATLY45A9SMllLgzixrV4R0xn/DWUlKHsZ4KjiZZipFRXHYvxI/iB8BJR+fRSPKqWWtVxB44mHN7zBS5PvRfu/L7AugF3C2+5OgU+r15u+hQYdHxOrQhWEUKObSJ42MKjEzwaH6oioYKee0ImdB1iPu0IP1jvcImtWanPGU/wxP4ssjTdEOAZepJg90gNes/Hx2gdal6iRtvN2G1xpCJXty4EBLbK2lIfXP6//IiQG0EDLAJfw5npRBluiDvG/tcDxW1E= - - secure: C7b6XlTt+gqr3O4avm7CwgheSi0o0Hn0SQPZHa1Fs9+xbcNLI5EAE6BMNIDH/70aOl/DygJptxmr7Fx8NwAiU+dGWKqe6NhIqpZiizGz9o1J3FpoF5+v+r0777b2oHrqnUiBT7MO//3R1kFL4WmEyoEKzkwT5yMz3qnCI1feKCvKXtLdoyDt3E9w9fMz83lWSXIp0YYpaMyUDKBVwdkbPFbfvr9Ew2spWjTrUkb0gVTbz+GHzdX0Y0PrKHusdZiLQSd3MqmXP7y4VcIafzKEWgXaxe94GBc/r5v9RLBwhqORIhtcu0GJMq9KuWH2uROspgJ9jzgAAJcrTWxaNJ02oarRQF7QdJPdfuIq5XLGqOaMCLKAFjAaogyPacfbUmcfeFokbPW9I7LoHIDcCydSdYC3K3Iw725FoPoNhVncOuwxouMnaWECXLQVlxCQmNN8QSlTBC6bFrgQ/2NwkMPSkMKr+/dKxnPvnWmo6uzqGSZwnLoGlnIryNPzIf8CZTsSWcl6dWxw7LYNMuPOVZGz4AMN8NKFqIrbl0sE3cr00z8N1FHwX2hD2D6dYRMpnv5l6N6ZxxKQT4BwmmpnFIIiKJyoNodUlgYtZX3iJICLQcpmHTrTle+h6XT24biUFF/uPO2TUGmhCEVzLgdlPvbiCePlAg1oytJIxmXjrmCJx/A= + global: + - ANDROID_BUILD_API=28 + - ANDROID_BUILD_TOOLS=28.0.3 + - ANDROID_EMULATOR_API=24 + - ADB_INSTALL_TIMEOUT=5 # minutes + - ANDROID_ABI=default/armeabi-v7a + - secure: Nz/+LNm9NO3PgvrwLIKDVjlJTskzZPKYXWnNe1T8kUflcQp6dygc9M3tqOgXVRTb33DPhZFZoM04fHJ7iAAyOydH0uNt3nI8zujFiQg7wGOm4vUczk6PYQrWNe/uAyrov+KjLbjNElBxZPDgMDTRD2e3j+UG0X+Xzjm3LqrWChpDjS037EuCpEILNdNETkHeAMVwAvcHrWkXiFMO6aYCHV+teo0zivrELbo6MCaxfMOKY0eMckgxJFtQbAF3hshr98325cczvIzQt5J/bFBkMaBx0IkSDAktDk++jVnoqIfpQVkM6x4mSjz64Q22BW5wIHsgfkT/Cjm7vn1LNpQsPO28p1Ja/W3TqVCeU1XR6sm7Zn+3b7ecSfGjh7p8L6gu9rzpsGIHOG88aWZuqpnF5Vgxjs4l2AXd/luAjv0ATLY45A9SMllLgzixrV4R0xn/DWUlKHsZ4KjiZZipFRXHYvxI/iB8BJR+fRSPKqWWtVxB44mHN7zBS5PvRfu/L7AugF3C2+5OgU+r15u+hQYdHxOrQhWEUKObSJ42MKjEzwaH6oioYKee0ImdB1iPu0IP1jvcImtWanPGU/wxP4ssjTdEOAZepJg90gNes/Hx2gdal6iRtvN2G1xpCJXty4EBLbK2lIfXP6//IiQG0EDLAJfw5npRBluiDvG/tcDxW1E= + - secure: C7b6XlTt+gqr3O4avm7CwgheSi0o0Hn0SQPZHa1Fs9+xbcNLI5EAE6BMNIDH/70aOl/DygJptxmr7Fx8NwAiU+dGWKqe6NhIqpZiizGz9o1J3FpoF5+v+r0777b2oHrqnUiBT7MO//3R1kFL4WmEyoEKzkwT5yMz3qnCI1feKCvKXtLdoyDt3E9w9fMz83lWSXIp0YYpaMyUDKBVwdkbPFbfvr9Ew2spWjTrUkb0gVTbz+GHzdX0Y0PrKHusdZiLQSd3MqmXP7y4VcIafzKEWgXaxe94GBc/r5v9RLBwhqORIhtcu0GJMq9KuWH2uROspgJ9jzgAAJcrTWxaNJ02oarRQF7QdJPdfuIq5XLGqOaMCLKAFjAaogyPacfbUmcfeFokbPW9I7LoHIDcCydSdYC3K3Iw725FoPoNhVncOuwxouMnaWECXLQVlxCQmNN8QSlTBC6bFrgQ/2NwkMPSkMKr+/dKxnPvnWmo6uzqGSZwnLoGlnIryNPzIf8CZTsSWcl6dWxw7LYNMuPOVZGz4AMN8NKFqIrbl0sE3cr00z8N1FHwX2hD2D6dYRMpnv5l6N6ZxxKQT4BwmmpnFIIiKJyoNodUlgYtZX3iJICLQcpmHTrTle+h6XT24biUFF/uPO2TUGmhCEVzLgdlPvbiCePlAg1oytJIxmXjrmCJx/A= android: - components: - - tools # to get the new `repository-11.xml` - - tools # to install Android SDK tools - - platform-tools - - build-tools-$ANDROID_BUILD_TOOLS - - android-$ANDROID_BUILD_API - - android-$ANDROID_EMULATOR_API - - extra-android-m2repository # for design library - - extra-google-m2repository - - extra-google-google_play_services - - addon-google_apis-google-28 # google play services - - sys-img-armeabi-v7a-addon-google_apis-google-$ANDROID_BUILD_API - - sys-img-armeabi-v7a-android-$ANDROID_EMULATOR_API + components: + - tools # to get the new `repository-11.xml` + - tools # to install Android SDK tools + - platform-tools + - build-tools-$ANDROID_BUILD_TOOLS + - android-$ANDROID_BUILD_API + - android-$ANDROID_EMULATOR_API + - extra-android-m2repository # for design library + - extra-google-m2repository + - extra-google-google_play_services + - addon-google_apis-google-28 # google play services + - sys-img-armeabi-v7a-addon-google_apis-google-$ANDROID_BUILD_API + - sys-img-armeabi-v7a-android-$ANDROID_EMULATOR_API - licenses: - - 'android-sdk-preview-license-52d11cd2' - - 'android-sdk-license-.+' - - 'google-gdk-license-.+' + licenses: + - 'android-sdk-preview-license-52d11cd2' + - 'android-sdk-license-.+' + - 'google-gdk-license-.+' before_install: - - yes | sdkmanager "platforms;android-28" - - mkdir -p "$ANDROID_HOME/licenses" - - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" - - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" - - chmod +x gradlew - - ./gradlew dependencies || true - - openssl aes-256-cbc -K $encrypted_62e1b1822e5d_key -iv $encrypted_62e1b1822e5d_iv -in tests/src/androidTest/resources/compat_data.json.enc -out tests/src/androidTest/resources/compat_data.json -d + - yes | sdkmanager "platforms;android-28" + - mkdir -p "$ANDROID_HOME/licenses" + - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" + - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" + - chmod +x gradlew + - ./gradlew dependencies || true + - openssl aes-256-cbc -K $encrypted_62e1b1822e5d_key -iv $encrypted_62e1b1822e5d_iv -in tests/src/androidTest/resources/compat/compat_data.json.enc -out tests/src/androidTest/resources/compat/compat_data.json -d before_script: - - android list targets - - android list sdk --extended --no-ui --all - - echo "y" | android update sdk -a --no-ui --filter sys-img-armeabi-v7a-android-$ANDROID_EMULATOR_API,sys-img-x86_64-android-$ANDROID_EMULATOR_API - - echo no | android create avd --force -n test -t android-$ANDROID_EMULATOR_API --abi $ANDROID_ABI - - QEMU_AUDIO_DRV=none emulator -avd test -no-window & - - android-wait-for-emulator - - adb shell input keyevent 82 & + - android list targets + - android list sdk --extended --no-ui --all + - echo "y" | android update sdk -a --no-ui --filter sys-img-armeabi-v7a-android-$ANDROID_EMULATOR_API,sys-img-x86_64-android-$ANDROID_EMULATOR_API + - echo no | android create avd --force -n test -t android-$ANDROID_EMULATOR_API --abi $ANDROID_ABI + - QEMU_AUDIO_DRV=none emulator -avd test -no-window & + - android-wait-for-emulator + - adb shell input keyevent 82 & script: - - "./gradlew clean build connectedCheck -PdisablePreDex --stacktrace" + - "./gradlew clean build connectedCheck -PdisablePreDex --stacktrace" after_success: - .ci/push-javadoc-to-gh-pages.sh + .ci/push-javadoc-to-gh-pages.sh diff --git a/ethree-common/.gitignore b/ethree-common/.gitignore index 796b96d1..8f5f7600 100644 --- a/ethree-common/.gitignore +++ b/ethree-common/.gitignore @@ -1 +1,177 @@ -/build +### Java ### +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +eclipse-aar/ + + +### Gradle ### +.gradle +build/ +gradle-wrapper.jar +gradle-wrapper.properties +gradle.properties + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + + +### Eclipse ### +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +nbactions.xml +.nb-gradle/ +.nb-gradle-properties + +### IntelliJ ### +### D ### +# Compiled Object files +*.o +*.obj + +# Compiled Dynamic libraries +#*.so +*.dylib +*.dll + +# Compiled Static libraries +*.a +*.lib + +# Executables +*.exe + +# DUB +.dub +docs.json +__dummy.html + + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries +# .idea/shelf + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ +/gen + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Project-specific +!crypto/libs/virgil_crypto_java.jar +compat_data.json +!compat_data.json.enc diff --git a/ethree-common/build.gradle b/ethree-common/build.gradle index c6a4d961..2133d920 100644 --- a/ethree-common/build.gradle +++ b/ethree-common/build.gradle @@ -45,7 +45,13 @@ apply plugin: 'digital.wup.android-maven-publish' apply plugin: 'org.jetbrains.dokka' group 'com.virgilsecurity' -version '0.4.2' +version '0.5.0-beta1' + +def APP_ID = hasProperty('APP_ID') ? APP_ID : System.getenv('APP_ID') +def API_PRIVATE_KEY = hasProperty('API_PRIVATE_KEY') ? API_PRIVATE_KEY : System.getenv('API_PRIVATE_KEY') +def API_PUBLIC_KEY = hasProperty('API_PUBLIC_KEY') ? API_PUBLIC_KEY : System.getenv('API_PUBLIC_KEY') +def API_PUBLIC_KEY_ID = hasProperty('API_PUBLIC_KEY_ID') ? API_PUBLIC_KEY_ID : System.getenv('API_PUBLIC_KEY_ID') +def VIRGIL_BASE_URL = hasProperty('VIRGIL_BASE_URL') ? VIRGIL_BASE_URL : System.getenv('VIRGIL_BASE_URL') android { compileSdkVersion androidOptions.compileSdkVersion @@ -59,14 +65,29 @@ android { javaCompileOptions { annotationProcessorOptions { arguments = [ - "room.schemaLocation":"$projectDir/schemas".toString(), - "room.incremental":"true", - "room.expandProjection":"true"] + "room.schemaLocation" : "$projectDir/schemas".toString(), + "room.incremental" : "true", + "room.expandProjection": "true"] } } + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { - buildTypes { + debug { + minifyEnabled false + useProguard false + } + release { + minifyEnabled false + useProguard false + } + buildTypes.each { + it.buildConfigField "String", "APP_ID", "$APP_ID" + it.buildConfigField "String", "API_PRIVATE_KEY", "$API_PRIVATE_KEY" + it.buildConfigField "String", "API_PUBLIC_KEY", "$API_PUBLIC_KEY" + it.buildConfigField "String", "API_PUBLIC_KEY_ID", "$API_PUBLIC_KEY_ID" + it.buildConfigField "String", "VIRGIL_BASE_URL", "$VIRGIL_BASE_URL" } } } @@ -83,13 +104,13 @@ dependencies { api "com.virgilsecurity.sdk:crypto-android:$versions.virgilSdk" api "com.virgilsecurity:common:$versions.virgilSdk" - api ("com.virgilsecurity.sdk:sdk:$versions.virgilSdk") { + api("com.virgilsecurity.sdk:sdk:$versions.virgilSdk") { exclude group: 'com.virgilsecurity.crypto' } - api ("com.virgilsecurity.sdk:keyknox:$versions.virgilSdk") { + api("com.virgilsecurity.sdk:keyknox:$versions.virgilSdk") { exclude group: 'com.virgilsecurity.sdk', module: "sdk" } - api ("com.virgilsecurity:pythia:$versions.pythia") { + api("com.virgilsecurity:pythia:$versions.pythia") { exclude group: 'com.virgilsecurity.crypto' exclude group: 'com.virgilsecurity.sdk' } @@ -101,6 +122,14 @@ dependencies { implementation "androidx.room:room-runtime:$versions.room" implementation "androidx.room:room-ktx:$versions.room" kapt "androidx.room:room-compiler:$versions.room" + + // Tests core + testImplementation "junit:junit:$versions.junit" + androidTestImplementation "androidx.test.ext:junit:$versions.testsRunner" + androidTestImplementation "androidx.test:runner:$versions.testsRunner" + + // Test internal + androidTestImplementation project(':ethree-kotlin') } task generateVersionVirgilAgent { diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/common/model/TicketTest.kt b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/model/TicketTest.kt similarity index 100% rename from tests/src/androidTest/java/com/virgilsecurity/android/common/model/TicketTest.kt rename to ethree-common/src/androidTest/java/com/virgilsecurity/android/common/model/TicketTest.kt diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorageTest.kt b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorageTest.kt similarity index 95% rename from tests/src/androidTest/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorageTest.kt rename to ethree-common/src/androidTest/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorageTest.kt index b77c0d0b..f754c9a7 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorageTest.kt +++ b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorageTest.kt @@ -40,7 +40,6 @@ import com.google.gson.JsonParser import com.virgilsecurity.android.common.storage.CardStorage import com.virgilsecurity.android.common.storage.sql.model.CardEntity import com.virgilsecurity.android.common.utils.TestConfig -import com.virgilsecurity.android.common.utils.TestConfig.Companion.context import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier import com.virgilsecurity.sdk.crypto.VirgilCardCrypto @@ -80,7 +79,9 @@ class SQLCardStorageTest { val tokenProvider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback(function = { return@RenewJwtCallback null })) - cardManager = CardManager(VirgilCardCrypto(VirgilCrypto()), tokenProvider, VirgilCardVerifier(VirgilCardCrypto(VirgilCrypto()))) + cardManager = CardManager(VirgilCardCrypto(VirgilCrypto()), + tokenProvider, + VirgilCardVerifier(VirgilCardCrypto(VirgilCrypto()))) db = Room.inMemoryDatabaseBuilder(TestConfig.context, ETheeDatabase::class.java).build() prePopulateDatabase() @@ -224,7 +225,9 @@ class SQLCardStorageTest { } private fun prePopulateDatabase() { - val sampleJson = JsonParser().parse(InputStreamReader(context.assets.open("databases/cards.json"))) as JsonObject + val databasesDataStream = + this.javaClass.classLoader?.getResourceAsStream("databases/cards.json") + val sampleJson = JsonParser().parse(InputStreamReader(databasesDataStream)) as JsonObject sampleJson.entrySet().forEach { val cardId = it.key val identity = (it.value as JsonObject)["identity"].asString diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestConfig.kt b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/utils/TestConfig.kt similarity index 95% rename from tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestConfig.kt rename to ethree-common/src/androidTest/java/com/virgilsecurity/android/common/utils/TestConfig.kt index 9f83a9cf..ab13f52f 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestConfig.kt +++ b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/utils/TestConfig.kt @@ -34,7 +34,7 @@ package com.virgilsecurity.android.common.utils import androidx.test.platform.app.InstrumentationRegistry -import com.virgilsecurity.android.ethree.BuildConfig +import com.virgilsecurity.android.common.BuildConfig import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.VirgilPrivateKey import com.virgilsecurity.sdk.utils.ConvertionUtils @@ -53,7 +53,8 @@ class TestConfig { const val VIRGIL_CARDS_SERVICE_PATH = "/card/v5/" val context = InstrumentationRegistry.getInstrumentation().targetContext - val DIRECTORY_PATH = InstrumentationRegistry.getInstrumentation().targetContext.filesDir.absolutePath + val DIRECTORY_PATH = InstrumentationRegistry.getInstrumentation() + .targetContext.filesDir.absolutePath val KEYSTORE_NAME = "virgil.keystore" } } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestUtils.kt b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/utils/TestUtils.kt similarity index 100% rename from tests/src/androidTest/java/com/virgilsecurity/android/common/utils/TestUtils.kt rename to ethree-common/src/androidTest/java/com/virgilsecurity/android/common/utils/TestUtils.kt diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/AuthenticationTests.kt b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/AuthenticationTests.kt similarity index 98% rename from tests/src/androidTest/java/com/virgilsecurity/android/common/worker/AuthenticationTests.kt rename to ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/AuthenticationTests.kt index 1e62c6cc..3ce1912a 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/AuthenticationTests.kt +++ b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/AuthenticationTests.kt @@ -36,11 +36,10 @@ package com.virgilsecurity.android.common.worker import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.EThreeException -import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException -import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.common.utils.TestConfig import com.virgilsecurity.android.common.utils.TestConfig.Companion.virgilCrypto import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryNotFoundException import com.virgilsecurity.sdk.storage.DefaultKeyStorage diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/BackupTests.kt b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/BackupTests.kt similarity index 100% rename from tests/src/androidTest/java/com/virgilsecurity/android/common/worker/BackupTests.kt rename to ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/BackupTests.kt index a48af4e8..cc52ad62 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/BackupTests.kt +++ b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/BackupTests.kt @@ -38,9 +38,9 @@ import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.EThreeException import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.common.exception.WrongPasswordException -import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.common.utils.TestConfig import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.keyknox.KeyknoxManager import com.virgilsecurity.keyknox.client.KeyknoxClient import com.virgilsecurity.keyknox.cloud.CloudKeyStorage diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt similarity index 87% rename from tests/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt rename to ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt index f5063af7..953366f0 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt +++ b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/GroupTests.kt @@ -36,18 +36,32 @@ package com.virgilsecurity.android.common.worker import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.gson.JsonObject import com.google.gson.JsonParser +import com.virgilsecurity.android.common.build.VersionVirgilAgent import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.* +import com.virgilsecurity.android.common.manager.GroupManager +import com.virgilsecurity.android.common.manager.LookupManager import com.virgilsecurity.android.common.model.* -import com.virgilsecurity.android.ethree.interaction.EThree +import com.virgilsecurity.android.common.storage.cloud.CloudTicketStorage +import com.virgilsecurity.android.common.storage.local.FileGroupStorage +import com.virgilsecurity.android.common.storage.local.LocalKeyStorage +import com.virgilsecurity.android.common.storage.sql.SQLCardStorage +import com.virgilsecurity.android.common.util.Const import com.virgilsecurity.android.common.utils.TestConfig import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.common.model.Data import com.virgilsecurity.crypto.foundation.Base64 +import com.virgilsecurity.sdk.cards.CardManager +import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier +import com.virgilsecurity.sdk.client.HttpClient +import com.virgilsecurity.sdk.client.VirgilCardClient import com.virgilsecurity.sdk.common.TimeSpan import com.virgilsecurity.sdk.crypto.VirgilAccessTokenSigner +import com.virgilsecurity.sdk.crypto.VirgilCardCrypto import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.jwt.JwtGenerator +import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider import com.virgilsecurity.sdk.storage.DefaultKeyStorage import com.virgilsecurity.sdk.storage.JsonKeyEntry import org.junit.Assert.* @@ -239,6 +253,39 @@ class GroupTests { @Test fun ste33() { // add more than max should throw error + val identity = UUID.randomUUID().toString() + val selfKeyPair = crypto.generateKeyPair() + + val localGroupStorage = FileGroupStorage(identity, + crypto, + selfKeyPair, + TestConfig.context.filesDir.absolutePath) + val accessTokenProvider = CachingJwtProvider { + TestUtils.generateToken(identity) + } + val keyStorage = DefaultKeyStorage(TestConfig.context.filesDir.absolutePath, + "virgil.keystore") + val localKeyStorage = LocalKeyStorage(identity, keyStorage, crypto) + val ticketStorageCloud = CloudTicketStorage(accessTokenProvider, localKeyStorage) + val virgilCardVerifier = VirgilCardVerifier(VirgilCardCrypto(crypto)) + val cardStorageSqlite = SQLCardStorage(TestConfig.context, + identity, + crypto, + virgilCardVerifier) + val httpClient = HttpClient(Const.ETHREE_NAME, VersionVirgilAgent.VERSION) + val cardManager = CardManager(VirgilCardCrypto(crypto), + accessTokenProvider, + VirgilCardVerifier(VirgilCardCrypto(crypto), false, false), + VirgilCardClient(Const.VIRGIL_BASE_URL + Const.VIRGIL_CARDS_SERVICE_PATH, + httpClient)) + val lookupManager = LookupManager(cardStorageSqlite, cardManager, null) + + val groupManager = GroupManager(localGroupStorage, + ticketStorageCloud, + localKeyStorage, + lookupManager, + this.crypto) + val participants = mutableSetOf() for (i in 0 until 100) { @@ -249,11 +296,10 @@ class GroupTests { val sessionId = Data(this.crypto.generateRandomData(32)) val ticket = Ticket(this.crypto, sessionId, participants) - val rawGroup = RawGroup(GroupInfo(this.ethree.identity), listOf(ticket)) + val rawGroup = RawGroup(GroupInfo(identity), listOf(ticket)) - assertNotNull(this.ethree.groupManager) - val group = Group(rawGroup, this.crypto, ethree.keyStorageLocal, - this.ethree.groupManager!!, this.ethree.lookupManager) + assertNotNull(groupManager) + val group = Group(rawGroup, this.crypto, localKeyStorage, groupManager, lookupManager) val card = TestUtils.publishCard() @@ -582,7 +628,7 @@ class GroupTests { fun ste45() { // Compatibility test val compatDataStream = - this.javaClass.classLoader?.getResourceAsStream("compat_data.json") + this.javaClass.classLoader?.getResourceAsStream("compat/compat_data.json") val compatJson = JsonParser().parse(InputStreamReader(compatDataStream)) as JsonObject val groupCompatJson = compatJson.getAsJsonObject("Group") diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt similarity index 97% rename from tests/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt rename to ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt index c18634b1..c0742c3f 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt +++ b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt @@ -37,9 +37,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.EThreeException import com.virgilsecurity.android.common.model.FindUsersResult -import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.common.utils.TestConfig import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.common.model.Data import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.storage.DefaultKeyStorage @@ -51,6 +51,7 @@ import org.junit.runner.RunWith import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.util.* +import kotlin.String.Companion /** * PeerToPeerTest @@ -263,18 +264,18 @@ class PeerToPeerTest { ethreeTwo.register().execute() - val lookupResult = ethree.lookupPublicKeys(ethreeTwo.identity).get() + val lookupResult = findUser(String).get() assertTrue(lookupResult.isNotEmpty()) - val encrypted = ethree.encrypt(TEXT, lookupResult) + val encrypted = encrypt(String, FindUsersResult) - val lookupResultTwo = ethreeTwo.lookupPublicKeys(ethree.identity).get() + val lookupResultTwo = findUser(String).get() assertTrue(lookupResultTwo.isNotEmpty()) val publicKey = lookupResultTwo[ethree.identity] ?: error("publicKey should not be null") - val decrypted = ethreeTwo.decrypt(encrypted, publicKey) + val decrypted = decrypt(String, Card) assertEquals(TEXT, decrypted) } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/SearchTests.kt b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/SearchTests.kt similarity index 100% rename from tests/src/androidTest/java/com/virgilsecurity/android/common/worker/SearchTests.kt rename to ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/SearchTests.kt index cc587e54..98ff2693 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/common/worker/SearchTests.kt +++ b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/SearchTests.kt @@ -37,9 +37,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.callback.OnKeyChangedCallback import com.virgilsecurity.android.common.exception.FindUsersException -import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.common.utils.TestConfig import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.exception.EmptyArgumentException import com.virgilsecurity.sdk.storage.DefaultKeyStorage diff --git a/tests/src/androidTest/resources/compat_data.json.enc b/ethree-common/src/androidTest/resources/compat/compat_data.json.enc similarity index 100% rename from tests/src/androidTest/resources/compat_data.json.enc rename to ethree-common/src/androidTest/resources/compat/compat_data.json.enc diff --git a/ethree-common/src/androidTest/resources/databases/cards.json b/ethree-common/src/androidTest/resources/databases/cards.json new file mode 100644 index 00000000..34072b0d --- /dev/null +++ b/ethree-common/src/androidTest/resources/databases/cards.json @@ -0,0 +1,17 @@ +{ + "b2e6c8bee5cfa40fa2ac2bc8961057600bced26bc5b29aab04014c5141a91bd4": { + "identity": "8DA6A11D-F8BC-4A1D-A221-CEE3A2D70631", + "card": "{\"content_snapshot\":\"eyJ2ZXJzaW9uIjoiNS4wIiwicHVibGljX2tleSI6Ik1Db3dCUVlESzJWd0F5RUFlM0ltTlZvNGM3TURwTitkQkNiQ2NLZUlZTnpYbTlxcEJ2bGdMeThTVWFnPSIsInByZXZpb3VzX2NhcmRfaWQiOiI5ZmY5MTdhN2ExYWEwODkxYjg3NWQ0YTllNDM5NzJhMGZiNjk0ODc5YmY4OTg3NzkwYzE2MTVkZDg2NGEzOGE0IiwiaWRlbnRpdHkiOiI4REE2QTExRC1GOEJDLTRBMUQtQTIyMS1DRUUzQTJENzA2MzEiLCJjcmVhdGVkX2F0IjoxNTY0NTgzNzY3fQ==\",\"signatures\":[{\"signature\":\"MFEwDQYJYIZIAWUDBAIDBQAEQFmgT4UsSYR9bgjP1N2arysAv03EXVXApwG3pmHDTJbUas2vCOaEIb\\/t+ArGKGjjq0vJSo0MSp5gamwySeDidAs=\",\"signer\":\"self\"},{\"signature\":\"MFEwDQYJYIZIAWUDBAIDBQAEQFedSgMbE\\/wVtqxLVAmskw5PLPwoEriMItQG+vVijAsl\\/snbHHcJ7HDcf5NTG3yLgP3l+8bQd9LIG5\\/XEwk+HwA=\",\"signer\":\"virgil\"}]}", + "is_outdated": false + }, + "9ff917a7a1aa0891b875d4a9e43972a0fb694879bf8987790c1615dd864a38a4": { + "identity": "8DA6A11D-F8BC-4A1D-A221-CEE3A2D70631", + "card": "{\"content_snapshot\":\"eyJ2ZXJzaW9uIjoiNS4wIiwicHVibGljX2tleSI6Ik1Db3dCUVlESzJWd0F5RUExS01yU2p6QVpFOE5SdHhpdVl3SmtaaXVUaFdQcWY3eTVsN3p1RlBJWWJFPSIsImlkZW50aXR5IjoiOERBNkExMUQtRjhCQy00QTFELUEyMjEtQ0VFM0EyRDcwNjMxIiwiY3JlYXRlZF9hdCI6MTU2NDU4Mzc2Nn0=\",\"signatures\":[{\"signature\":\"MFEwDQYJYIZIAWUDBAIDBQAEQKz0eeFp43LH4l5nWjoVGyvP\\/qu67MhoaYaJ\\/utJIlqH2gLMqnXBUQk692+9WX1YsZlJjPPviuaPdRdwUUUGQQU=\",\"signer\":\"self\"},{\"signature\":\"MFEwDQYJYIZIAWUDBAIDBQAEQIF7919OYmvnpQ4\\/ytp534vqRKOO4M48tq4wQizbhbjhtvuUvrBFfIa140jrqxdeZhb462FodPQEMAtyaZCKUwM=\",\"signer\":\"virgil\"}]}", + "is_outdated": true + }, + "e66465a08232beb55e33b4ce5e8772d748911c9b830797336e1ce342c78829a2": { + "identity": "D4E8E4CA-6FB4-42B6-A3FF-DBBC19201DD6", + "card": "{\"content_snapshot\":\"eyJ2ZXJzaW9uIjoiNS4wIiwicHVibGljX2tleSI6Ik1Db3dCUVlESzJWd0F5RUFkV2ZBWXowNnNoQUNjeFdUVWlhZkRIcE9PcHBrUkdrNWdCZjllUzQ0SlM0PSIsImlkZW50aXR5IjoiRDRFOEU0Q0EtNkZCNC00MkI2LUEzRkYtREJCQzE5MjAxREQ2IiwiY3JlYXRlZF9hdCI6MTU2NDU4NTYwOX0=\",\"signatures\":[{\"signature\":\"MFEwDQYJYIZIAWUDBAIDBQAEQIR0PND1Yic36leh\\/vQ+oMXmR8vJX+Vm6pRF4C6z52IP5PK4TMWobPcymrkY+lOllxQNG1ORROoXBfcKSWxkWAw=\",\"signer\":\"self\"},{\"signature\":\"MFEwDQYJYIZIAWUDBAIDBQAEQDFlyBswkrCV3+NSGwn67KFLwU\\/FznY\\/5o+wThgdCzliCP7oMjMe8uGSq4A1GNOEcNoMZhPz7Tku24dncXA0cgk=\",\"signer\":\"virgil\"}]}", + "is_outdated": false + } +} diff --git a/ethree-common/src/androidTest/resources/databases/cards.sqlite3 b/ethree-common/src/androidTest/resources/databases/cards.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..e46154b16bb70d39391788335188dc79bf5f3a50 GIT binary patch literal 20480 zcmeI(OON8n9RP6X?w#Iksh8Y(FOg7d57S~g_zhlZq!_zwOk;zMpM+J*FTmKm4F<19 z${cpJ%9qI@x8yVAp0AK^kYfxpt(lfKNzb;TY@{m5lnte-zkmK$30u%p+F=ruhx66c zNy>QPSA}A+@Y`~^P$-<|+h_UqP)_rmvxkGcE`GHCuXfK1zxn$2m!*Fc&c6Ipp~RQ| zS~|&hV1WWq017|>C;$bZ02F`%Pyh-*0VwdF1*EeVS2tJ1Fr4|pF7$(05+?im;}Pqu z{Pkz73K$KPMVSLu`Ei4CM=l>5y?l88rg8e>>epArcO8WOM;)EL??{(LMgP#>yA~h! zbo2JCcz$(tReU>295)V*BOG5&-glufjW&2c@^#ntPpV&Q19UdH*I{k^kE=EvjvV?g)l zu{zw#e}168%(v6?KNl|a%OI~{fdWtf3P1rU00n;00^fgicK-HK|NO^cKv4umI!GPE zF*k5YGQe?{@B%WZ*J_wwBkDAYdbC@|ks4jYaVkK)04Ff7RU?Bi{f24HQ+X!O6jCyv;9dN>zRV|VhQQ?#I-n$qrOQZj-`@^u~kmxJ3vPi1SE3Q zXJr;aq)5ctI>^+>PE^ohl?;&5gxRS;!47fScq_IoHSY8D*k?sb(sWGbbVAnbv|}N! zuU}oS!_mx1HmhKL{mt*b`*4__y~Kl*Q|y*4_C?0jxdtP04TS-Us3KjNL+#MRsW=!9 zs#~Qpm#nE;-?prZF{KtNPq0)*Vq%x<-VvH)&fTuJpROXi5_jTwr^o`@QPs}eY7D)* zp1F%mCVdDeSmlGSgLruT=G7A;;`n&qSf|m7HSmhu6Ve$~yW`ubKB)59%pO$9c_r1x zBcILUknPsy(nJdC8=X;*Fw5A#KCeD2%V)eEa5y z%adbj{-Nt)0p-=*ARxV=Lm)#3b3DxT>NJHSWQ{@)*9&}%ay`<`GtqGo0znCnLyKmS)6Ebe=7?TuIXy3g-N7D za;RW(yRXN6ZMLvoE#7&cg}H(+1=?6MOn!Du%~@mLxB3egH!=&8$KF&OI;IkKLXqrG zZBo+wv5bM8ZGyDJMLT;k!evuIY%PvuP9o*3HI{^aC{H4!!+E4VwTX_Fx5=W^&HyH9 zM%3pw&MWN9aNJFvl8xv$z+LaTfI3V^%msF znWp<*z=b}uehBAhOb0&jdn3sOgKBCd%U#=Grt2sryX|f|2dhvL6Gf5Af`3 z0`LEyO$a-H0#E=7KmjNK1)u;FfC5ke3P6Ehk^sE_|H-RyQTkiqwDfJE^vBWDDbHQFHiO2Q~q3cc4+vNzdD^hwc+8%0WY5t?mqz} CuB$iz literal 0 HcmV?d00001 diff --git a/ethree-common/src/main/AndroidManifest.xml b/ethree-common/src/main/AndroidManifest.xml index 864e0dfc..fbee13d1 100644 --- a/ethree-common/src/main/AndroidManifest.xml +++ b/ethree-common/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - - - + + + + diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt index bdfa2907..43da2a39 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt @@ -45,8 +45,8 @@ import com.virgilsecurity.android.common.model.Group import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.common.storage.cloud.CloudKeyManager import com.virgilsecurity.android.common.storage.cloud.CloudTicketStorage -import com.virgilsecurity.android.common.storage.local.GroupStorageFile -import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.android.common.storage.local.FileGroupStorage +import com.virgilsecurity.android.common.storage.local.LocalKeyStorage import com.virgilsecurity.android.common.storage.sql.SQLCardStorage import com.virgilsecurity.android.common.util.Const import com.virgilsecurity.android.common.util.Const.VIRGIL_BASE_URL @@ -86,11 +86,9 @@ constructor(val identity: String, context: Context) { private val rootPath: String - private val cloudKeyManager: CloudKeyManager private var accessTokenProvider: AccessTokenProvider - var groupManager: GroupManager? = null - private set + private var groupManager: GroupManager? = null private lateinit var authorizationWorker: AuthorizationWorker private lateinit var backupWorker: BackupWorker @@ -98,15 +96,16 @@ constructor(val identity: String, private lateinit var p2pWorker: PeerToPeerWorker private lateinit var searchWorker: SearchWorker - lateinit var keyStorageLocal: KeyStorageLocal - private set + internal lateinit var localKeyStorage: LocalKeyStorage + + internal val lookupManager: LookupManager + internal val cloudKeyManager: CloudKeyManager protected val crypto: VirgilCrypto = VirgilCrypto() protected abstract val keyStorage: KeyStorage val cardManager: CardManager - val lookupManager: LookupManager init { val cardCrypto = VirgilCardCrypto(crypto) @@ -133,22 +132,22 @@ constructor(val identity: String, /** * Should be called on each new instance of `EThreeCore` child objects. Is up to developer. * - * Initialization of workers not in constructor, because they depend on `keyStorageLocal` that + * Initialization of workers not in constructor, because they depend on `localKeyStorage` that * is available only after child object of `EThreeCore` is constructed. */ protected fun initializeCore() { - this.keyStorageLocal = KeyStorageLocal(identity, keyStorage, crypto) + this.localKeyStorage = LocalKeyStorage(identity, keyStorage, crypto) this.authorizationWorker = AuthorizationWorker(cardManager, - keyStorageLocal, + localKeyStorage, identity, ::publishCardThenSaveLocal, ::privateKeyDeleted) - this.backupWorker = BackupWorker(keyStorageLocal, cloudKeyManager, ::privateKeyChanged) + this.backupWorker = BackupWorker(localKeyStorage, cloudKeyManager, ::privateKeyChanged) this.groupWorker = GroupWorker(identity, crypto, ::getGroupManager, ::computeSessionId) - this.p2pWorker = PeerToPeerWorker(keyStorageLocal, crypto) + this.p2pWorker = PeerToPeerWorker(localKeyStorage, crypto) this.searchWorker = SearchWorker(lookupManager) - if (keyStorageLocal.exists()) { + if (localKeyStorage.exists()) { privateKeyChanged() } @@ -747,14 +746,14 @@ constructor(val identity: String, p2pWorker.decrypt(data, sendersKey) internal fun privateKeyChanged(newCard: Card? = null) { - val selfKeyPair = keyStorageLocal.load() + val selfKeyPair = localKeyStorage.load() - val localGroupStorage = GroupStorageFile(identity, crypto, selfKeyPair, rootPath) - val ticketStorageCloud = CloudTicketStorage(accessTokenProvider, keyStorageLocal) + val localGroupStorage = FileGroupStorage(identity, crypto, selfKeyPair, rootPath) + val ticketStorageCloud = CloudTicketStorage(accessTokenProvider, localKeyStorage) this.groupManager = GroupManager(localGroupStorage, ticketStorageCloud, - this.keyStorageLocal, + this.localKeyStorage, this.lookupManager, this.crypto) @@ -798,7 +797,7 @@ constructor(val identity: String, val privateKeyData = Data(crypto.exportPrivateKey(virgilKeyPair.privateKey)) - keyStorageLocal.store(privateKeyData) + localKeyStorage.store(privateKeyData) privateKeyChanged(card) } } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt index 0f63f66c..598b6951 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/GroupManager.kt @@ -39,8 +39,8 @@ import com.virgilsecurity.android.common.model.GroupInfo import com.virgilsecurity.android.common.model.RawGroup import com.virgilsecurity.android.common.model.Ticket import com.virgilsecurity.android.common.storage.cloud.CloudTicketStorage -import com.virgilsecurity.android.common.storage.local.GroupStorageFile -import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.android.common.storage.local.FileGroupStorage +import com.virgilsecurity.android.common.storage.local.LocalKeyStorage import com.virgilsecurity.common.model.Data import com.virgilsecurity.sdk.cards.Card import com.virgilsecurity.sdk.crypto.VirgilCrypto @@ -48,19 +48,19 @@ import com.virgilsecurity.sdk.crypto.VirgilCrypto /** * GroupManager */ -class GroupManager( - internal val localGroupStorage: GroupStorageFile, - private val cloudTicketStorage: CloudTicketStorage, - private val keyStorageLocal: KeyStorageLocal, +internal class GroupManager internal constructor( + internal val localGroupStorage: FileGroupStorage, + internal val cloudTicketStorage: CloudTicketStorage, + private val localKeyStorage: LocalKeyStorage, private val lookupManager: LookupManager, private val crypto: VirgilCrypto ) { - private val identity: String = localGroupStorage.identity + internal val identity: String = localGroupStorage.identity private fun parse(rawGroup: RawGroup): Group = Group(rawGroup, crypto, - keyStorageLocal, + localKeyStorage, this, lookupManager) @@ -121,6 +121,6 @@ class GroupManager( } companion object { - const val MAX_TICKETS_IN_GROUP = 50 + internal const val MAX_TICKETS_IN_GROUP = 50 } } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt index 7a266ef3..db4c3385 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/manager/LookupManager.kt @@ -46,10 +46,10 @@ import java.util.logging.Logger /** * LookupManager */ -class LookupManager( +internal class LookupManager internal constructor( internal val cardStorage: CardStorage, - private val cardManager: CardManager, - private val onKeyChangedCallback: OnKeyChangedCallback? = null + internal val cardManager: CardManager, + internal val onKeyChangedCallback: OnKeyChangedCallback? = null ) { internal fun startUpdateCachedCards() { diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt index 6c55ee28..bbb44412 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Group.kt @@ -36,7 +36,7 @@ package com.virgilsecurity.android.common.model import com.virgilsecurity.android.common.exception.* import com.virgilsecurity.android.common.manager.GroupManager import com.virgilsecurity.android.common.manager.LookupManager -import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.android.common.storage.local.LocalKeyStorage import com.virgilsecurity.common.model.Completable import com.virgilsecurity.common.model.Data import com.virgilsecurity.crypto.foundation.FoundationException @@ -51,10 +51,10 @@ import kotlin.collections.HashSet /** * Group */ -class Group constructor( +class Group internal constructor( rawGroup: RawGroup, private val crypto: VirgilCrypto, - private val keyStorageLocal: KeyStorageLocal, + private val localKeyStorage: LocalKeyStorage, private val groupManager: GroupManager, private val lookupManager: LookupManager ) { @@ -65,7 +65,7 @@ class Group constructor( internal var session: GroupSession - private val selfIdentity: String = keyStorageLocal.identity + private val selfIdentity: String = localKeyStorage.identity init { val tickets = rawGroup.tickets.sortedBy { it.groupMessage.epoch } @@ -77,13 +77,7 @@ class Group constructor( this.session = generateSession(tickets) } - internal fun checkPermissions() { - if (selfIdentity != initiator) { - throw PermissionDeniedGroupException("Only group initiator can do changed on group") - } - } - - internal fun generateSession(tickets: List): GroupSession { + private fun generateSession(tickets: List, crypto: VirgilCrypto): GroupSession { val session = GroupSession() session.setRng(crypto.rng) @@ -94,6 +88,36 @@ class Group constructor( return session } + private fun shareTickets(cards: List) { + val sessionId = this.session.sessionId + groupManager.addAccess(cards, Data(sessionId)) + val newParticipants = cards.map { it.identity } + this.participants.addAll(newParticipants.toSet()) + } + + private fun addNewTicket(participants: FindUsersResult) { + val newSet = HashSet(participants.keys) + + val ticketMessage = this.session.createGroupTicket().ticketMessage + val ticket = Ticket(ticketMessage, newSet) + + groupManager.store(ticket, participants.values.toList()) + this.session.addEpoch(ticket.groupMessage) + + newSet.add(this.initiator) + + this.participants = newSet + } + + internal fun checkPermissions() { + if (selfIdentity != initiator) { + throw PermissionDeniedGroupException("Only group initiator can do changed on group") + } + } + + internal fun generateSession(tickets: List): GroupSession = + generateSession(tickets, this.crypto) + /** * Signs and encrypts data for group. * @@ -121,7 +145,7 @@ class Group constructor( fun encrypt(data: ByteArray): ByteArray { require(data.isNotEmpty()) { "\'data\' should not be empty" } - val selfKeyPair = this.keyStorageLocal.load() + val selfKeyPair = this.localKeyStorage.load() val encrypted = this.session.encrypt(data, selfKeyPair.privateKey.privateKey) return encrypted.serialize() } @@ -292,27 +316,6 @@ class Group constructor( fun remove(participant: Card): Completable = remove(FindUsersResult(mapOf(participant.identity to participant))) - private fun shareTickets(cards: List) { - val sessionId = this.session.sessionId - groupManager.addAccess(cards, Data(sessionId)) - val newParticipants = cards.map { it.identity } - this.participants.addAll(newParticipants.toSet()) - } - - private fun addNewTicket(participants: FindUsersResult) { - val newSet = HashSet(participants.keys) - - val ticketMessage = this.session.createGroupTicket().ticketMessage - val ticket = Ticket(ticketMessage, newSet) - - groupManager.store(ticket, participants.values.toList()) - this.session.addEpoch(ticket.groupMessage) - - newSet.add(this.initiator) - - this.participants = newSet - } - companion object { internal fun validateParticipantsCount(count: Int) { if (count !in VALID_PARTICIPANTS_COUNT_RANGE) { diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/GroupInfo.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/GroupInfo.kt index 8339c6f5..c1c20e31 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/GroupInfo.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/GroupInfo.kt @@ -39,7 +39,7 @@ import com.virgilsecurity.common.util.SerializeUtils /** * GroupInfo */ -class GroupInfo(internal val initiator: String) { +internal class GroupInfo(internal val initiator: String) { internal fun serialize(): Data { return SerializeUtils.serialize(this) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/RawGroup.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/RawGroup.kt index 5b8301c7..dee9a3d7 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/RawGroup.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/RawGroup.kt @@ -38,9 +38,9 @@ import com.virgilsecurity.android.common.exception.RawGroupException /** * RawGroup */ -class RawGroup( - val info: GroupInfo, - val tickets: List +internal class RawGroup internal constructor( + internal val info: GroupInfo, + internal val tickets: List ) { init { diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt index 2c16263d..2b1d0d9b 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/model/Ticket.kt @@ -46,10 +46,10 @@ import java.io.Serializable /** * Ticket */ -class Ticket : Parcelable { +internal class Ticket : Parcelable { - val groupMessage: GroupSessionMessage - val participants: Set + internal val groupMessage: GroupSessionMessage + internal val participants: Set private constructor(parcel: Parcel) { val groupMessageDataLength = parcel.readInt() @@ -65,9 +65,9 @@ class Ticket : Parcelable { this.participants = participants } - constructor(crypto: VirgilCrypto, - sessionId: Data, - participants: Set) { + internal constructor(crypto: VirgilCrypto, + sessionId: Data, + participants: Set) { require(participants is Serializable) { "Please, use serializable Set for participants." } val ticket = GroupSessionTicket() diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/CardStorage.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/CardStorage.kt index 82a80ef7..d7caf02b 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/CardStorage.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/CardStorage.kt @@ -38,7 +38,7 @@ import com.virgilsecurity.sdk.cards.Card /** * Virgil Cards storage. */ -interface CardStorage { +internal interface CardStorage { fun storeCard(card: Card) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt index 5333d6d1..e68c230b 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudKeyManager.kt @@ -56,10 +56,10 @@ import java.net.URL /** * CloudKeyManager */ -internal class CloudKeyManager( +internal class CloudKeyManager internal constructor( private val identity: String, private val crypto: VirgilCrypto, - private val tokenProvider: AccessTokenProvider, + internal val tokenProvider: AccessTokenProvider, baseUrl: String = VIRGIL_BASE_URL ) { @@ -121,7 +121,7 @@ internal class CloudKeyManager( * Initializes [SyncKeyStorage] with default settings, [tokenProvider] and provided * [password] after that returns initialized [SyncKeyStorage] object. */ - private fun setupCloudKeyStorage(password: String): CloudKeyStorage { + internal fun setupCloudKeyStorage(password: String): CloudKeyStorage { val brainKeyPair = this.brainKey.generateKeyPair(password) val cloudKeyStorage = CloudKeyStorage(this.keyknoxManager, listOf(brainKeyPair.publicKey), diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt index c858a7de..f8fc97c1 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/cloud/CloudTicketStorage.kt @@ -34,7 +34,7 @@ package com.virgilsecurity.android.common.storage.cloud import com.virgilsecurity.android.common.model.Ticket -import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.android.common.storage.local.LocalKeyStorage import com.virgilsecurity.android.common.util.Const import com.virgilsecurity.common.model.Data import com.virgilsecurity.common.util.toHexString @@ -49,13 +49,13 @@ import java.net.URL /** * CloudTicketStorage */ -class CloudTicketStorage( +internal class CloudTicketStorage internal constructor( accessTokenProvider: AccessTokenProvider, - private val keyStorageLocal: KeyStorageLocal, + private val localKeyStorage: LocalKeyStorage, baseUrl: String? = Const.VIRGIL_BASE_URL ) { - private val identity: String get() = keyStorageLocal.identity + private val identity: String get() = localKeyStorage.identity private val keyknoxManager: KeyknoxManager init { @@ -64,7 +64,7 @@ class CloudTicketStorage( } internal fun store(ticket: Ticket, cards: Collection) { - val selfKeyPair = keyStorageLocal.load() + val selfKeyPair = localKeyStorage.load() val groupMessage = ticket.groupMessage @@ -90,7 +90,7 @@ class CloudTicketStorage( internal fun retrieve(sessionId: Data, identity: String, identityPublicKey: VirgilPublicKey): List { - val selfKeyPair = keyStorageLocal.load() + val selfKeyPair = localKeyStorage.load() val sessionIdHex = sessionId.toHexString() @@ -121,7 +121,7 @@ class CloudTicketStorage( } internal fun addRecipients(cards: Collection, sessionId: Data) { - val selfKeyPair = keyStorageLocal.load() + val selfKeyPair = localKeyStorage.load() val sessionIdHex = sessionId.toHexString() @@ -157,7 +157,7 @@ class CloudTicketStorage( } internal fun reAddRecipient(card: Card, sessionId: Data) { - val selfKeyPair = keyStorageLocal.load() + val selfKeyPair = localKeyStorage.load() val path = sessionId.toHexString() diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/FileGroupStorage.kt similarity index 98% rename from ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt rename to ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/FileGroupStorage.kt index f3b62b33..5277f2e6 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/GroupStorageFile.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/FileGroupStorage.kt @@ -50,9 +50,9 @@ import com.virgilsecurity.sdk.utils.ConvertionUtils import java.io.File /** - * GroupStorageFile + * FileGroupStorage */ -class GroupStorageFile( +internal class FileGroupStorage internal constructor( internal val identity: String, crypto: VirgilCrypto, identityKeyPair: VirgilKeyPair, diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/LocalKeyStorage.kt similarity index 97% rename from ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt rename to ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/LocalKeyStorage.kt index 3fcb48dc..2f55940a 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/KeyStorageLocal.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/local/LocalKeyStorage.kt @@ -44,8 +44,8 @@ import com.virgilsecurity.sdk.storage.KeyStorage /** * Local KeyStorage. */ -class KeyStorageLocal( - val identity: String, +class LocalKeyStorage internal constructor( + internal val identity: String, private val keyStorage: KeyStorage, private val crypto: VirgilCrypto ) { diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt index 38f695b2..59c97750 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/ETheeDatabase.kt @@ -39,6 +39,6 @@ import com.virgilsecurity.android.common.storage.sql.dao.CardDao import com.virgilsecurity.android.common.storage.sql.model.CardEntity @Database(entities = arrayOf(CardEntity::class), version = 1) -abstract class ETheeDatabase : RoomDatabase() { +internal abstract class ETheeDatabase : RoomDatabase() { abstract fun cardDao(): CardDao } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt index 17b91ce2..39e2535b 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/SQLCardStorage.kt @@ -49,11 +49,13 @@ import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider /** * SQL-based Virgil Cards storage. */ -class SQLCardStorage(context: Context, - userIdentifier: String, - crypto: VirgilCrypto, - verifier: CardVerifier, - database: ETheeDatabase? = null) : CardStorage { +internal class SQLCardStorage internal constructor( + context: Context, + userIdentifier: String, + crypto: VirgilCrypto, + verifier: CardVerifier, + database: ETheeDatabase? = null +) : CardStorage { private val db: ETheeDatabase private val cardManager: CardManager @@ -127,7 +129,7 @@ class SQLCardStorage(context: Context, if (card.identity !in identities) { throw InconsistentCardStorageException("Got wrong card from SQL storage") } - val nextCard = cards.firstOrNull() { it.previousCardId == card.identifier } + val nextCard = cards.firstOrNull { it.previousCardId == card.identifier } if (nextCard != null) { nextCard.previousCard = card card.isOutdated = true diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt index 21b8b276..c697f487 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/dao/CardDao.kt @@ -40,7 +40,7 @@ import androidx.room.Query import com.virgilsecurity.android.common.storage.sql.model.CardEntity @Dao -interface CardDao { +internal interface CardDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(card: CardEntity) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt index 5b5fdcc8..2b5db88e 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/storage/sql/model/CardEntity.kt @@ -40,9 +40,9 @@ import androidx.room.Index import androidx.room.PrimaryKey @Entity(tableName = "ethree_cards", - indices = arrayOf(Index(value = ["id"], unique = true), - Index(value = ["identity"], unique = false))) -data class CardEntity( + indices = [Index(value = ["id"], unique = true), Index(value = ["identity"], + unique = false)]) +internal data class CardEntity( @PrimaryKey @ColumnInfo(name = "id") val identifier: String, @ColumnInfo(name = "identity") @NonNull val identity: String, @ColumnInfo(name = "is_outdated") @NonNull val isOutdated: Boolean, diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt index 62bd4039..5581e3bb 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/AuthorizationWorker.kt @@ -34,7 +34,7 @@ package com.virgilsecurity.android.common.worker import com.virgilsecurity.android.common.exception.EThreeException -import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.android.common.storage.local.LocalKeyStorage import com.virgilsecurity.common.model.Completable import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.crypto.VirgilKeyPair @@ -42,9 +42,9 @@ import com.virgilsecurity.sdk.crypto.VirgilKeyPair /** * AuthorizationWorker */ -internal class AuthorizationWorker( +internal class AuthorizationWorker internal constructor( private val cardManager: CardManager, - private val keyStorageLocal: KeyStorageLocal, + private val localKeyStorage: LocalKeyStorage, private val identity: String, private val publishCardThenSaveLocal: (VirgilKeyPair?, String?) -> Unit, private val privateKeyDeleted: () -> Unit @@ -54,7 +54,7 @@ internal class AuthorizationWorker( @JvmOverloads internal fun register(keyPair: VirgilKeyPair? = null) = object : Completable { override fun execute() { - if (keyStorageLocal.exists()) + if (localKeyStorage.exists()) throw EThreeException("Private key already exists in local key storage") val cards = cardManager.searchCards(this@AuthorizationWorker.identity) @@ -70,14 +70,14 @@ internal class AuthorizationWorker( val card = cards.firstOrNull() ?: throw EThreeException("User is not registered") cardManager.revokeCard(card.identifier) - keyStorageLocal.delete() + localKeyStorage.delete() privateKeyDeleted() } } @Synchronized internal fun rotatePrivateKey() = object : Completable { override fun execute() { - if (keyStorageLocal.exists()) + if (localKeyStorage.exists()) throw EThreeException("Private key already exists in local key storage.") val cards = cardManager.searchCards(this@AuthorizationWorker.identity) @@ -87,10 +87,10 @@ internal class AuthorizationWorker( } } - internal fun hasLocalPrivateKey() = keyStorageLocal.exists() + internal fun hasLocalPrivateKey() = localKeyStorage.exists() internal fun cleanup() { - keyStorageLocal.delete() + localKeyStorage.delete() privateKeyDeleted() } } diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt index 1fbea617..26266df2 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/BackupWorker.kt @@ -38,7 +38,7 @@ import com.virgilsecurity.android.common.exception.EThreeException import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.common.exception.RestoreKeyException import com.virgilsecurity.android.common.storage.cloud.CloudKeyManager -import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.android.common.storage.local.LocalKeyStorage import com.virgilsecurity.common.model.Completable import com.virgilsecurity.common.model.Data import com.virgilsecurity.keyknox.exception.EntryAlreadyExistsException @@ -49,8 +49,8 @@ import com.virgilsecurity.sdk.crypto.exceptions.KeyEntryAlreadyExistsException /** * BackupWorker */ -internal class BackupWorker( - private val keyStorageLocal: KeyStorageLocal, +internal class BackupWorker internal constructor( + private val localKeyStorage: LocalKeyStorage, private val keyManagerCloud: CloudKeyManager, private val privateKeyChanged: (Card?) -> Unit ) { @@ -60,7 +60,7 @@ internal class BackupWorker( try { require(password.isNotEmpty()) { "\'password\' should not be empty" } - val identityKeyPair = keyStorageLocal.load() + val identityKeyPair = localKeyStorage.load() keyManagerCloud.store(identityKeyPair.privateKey, password) } catch (e: EntryAlreadyExistsException) { throw BackupKeyException("Can't backup private key", e) @@ -74,7 +74,7 @@ internal class BackupWorker( require(password.isNotEmpty()) { "\'password\' should not be empty" } val entry = keyManagerCloud.retrieve(password) - keyStorageLocal.store(Data(entry.data)) + localKeyStorage.store(Data(entry.data)) privateKeyChanged(null) } catch (e: KeyEntryAlreadyExistsException) { throw RestoreKeyException("Can't restore private key", e) diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt index 88110d7d..76c290aa 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/GroupWorker.kt @@ -48,7 +48,7 @@ import java.nio.charset.StandardCharsets /** * GroupWorker */ -internal class GroupWorker( +internal class GroupWorker internal constructor( private val identity: String, private val crypto: VirgilCrypto, private val getGroupManager: () -> GroupManager, diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt index e0cfce26..bd6ee1e9 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/PeerToPeerWorker.kt @@ -37,7 +37,7 @@ import com.virgilsecurity.android.common.exception.EThreeException import com.virgilsecurity.android.common.model.FindUsersResult import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.common.model.toPublicKeys -import com.virgilsecurity.android.common.storage.local.KeyStorageLocal +import com.virgilsecurity.android.common.storage.local.LocalKeyStorage import com.virgilsecurity.common.model.Data import com.virgilsecurity.sdk.cards.Card import com.virgilsecurity.sdk.crypto.VirgilCrypto @@ -52,8 +52,8 @@ import java.util.* /** * PeerToPeerWorker */ -internal class PeerToPeerWorker( - private val keyStorageLocal: KeyStorageLocal, +internal class PeerToPeerWorker internal constructor( + private val localKeyStorage: LocalKeyStorage, private val crypto: VirgilCrypto ) { @@ -85,7 +85,7 @@ internal class PeerToPeerWorker( internal fun decrypt(inputStream: InputStream, outputStream: OutputStream) { if (inputStream.available() == 0) throw EmptyArgumentException("inputStream") - val selfKeyPair = keyStorageLocal.load() + val selfKeyPair = localKeyStorage.load() crypto.decrypt(inputStream, outputStream, selfKeyPair.privateKey) } @@ -145,7 +145,7 @@ internal class PeerToPeerWorker( publicKeys: List?) { if (inputStream.available() == 0) throw EmptyArgumentException("inputStream") - val selfKeyPair = keyStorageLocal.load() + val selfKeyPair = localKeyStorage.load() val pubKeys = mutableListOf(selfKeyPair.publicKey) if (publicKeys != null) { @@ -163,7 +163,7 @@ internal class PeerToPeerWorker( publicKeys: List?): Data { require(data.data.isNotEmpty()) { "\'data\' should not be empty." } - val selfKeyPair = keyStorageLocal.load() + val selfKeyPair = localKeyStorage.load() val pubKeys = mutableListOf(selfKeyPair.publicKey) if (publicKeys != null) { @@ -179,7 +179,7 @@ internal class PeerToPeerWorker( private fun decryptInternal(data: Data, publicKey: VirgilPublicKey?): Data { require(data.data.isNotEmpty()) { "\'data\' should not be empty." } - val selfKeyPair = keyStorageLocal.load() + val selfKeyPair = localKeyStorage.load() val pubKey = publicKey ?: selfKeyPair.publicKey return try { diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt index 0ea4328a..b8ecfa8f 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/worker/SearchWorker.kt @@ -42,7 +42,7 @@ import com.virgilsecurity.sdk.cards.Card /** * SearchWorker */ -internal class SearchWorker( +internal class SearchWorker internal constructor( private val lookupManager: LookupManager ) { diff --git a/ethree-enclave/build.gradle b/ethree-enclave/build.gradle index 87b37581..f400281d 100644 --- a/ethree-enclave/build.gradle +++ b/ethree-enclave/build.gradle @@ -53,13 +53,19 @@ android { consumerProguardFiles 'proguard-rules.txt' } buildTypes { - buildTypes { + debug { + minifyEnabled false + useProguard false + } + release { + minifyEnabled false + useProguard false } } } group 'com.virgilsecurity' -version '0.5.1' +version '0.6.0-beta1' dependencies { // Inner dependencies diff --git a/ethree-kotlin/build.gradle b/ethree-kotlin/build.gradle index 6a37521b..d03991de 100644 --- a/ethree-kotlin/build.gradle +++ b/ethree-kotlin/build.gradle @@ -53,13 +53,19 @@ android { consumerProguardFiles 'proguard-rules.txt' } buildTypes { - buildTypes { + debug { + minifyEnabled false + useProguard false + } + release { + minifyEnabled false + useProguard false } } } group 'com.virgilsecurity' -version '0.5.2' +version '0.6.0-beta1' dependencies { // Inner dependencies diff --git a/tests/.gitignore b/tests/.gitignore index 8f5f7600..f9740bd0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -173,5 +173,3 @@ fabric.properties # Project-specific !crypto/libs/virgil_crypto_java.jar -compat_data.json -!compat_data.json.enc diff --git a/tests/build.gradle b/tests/build.gradle index 55315b25..6e0aa35b 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -83,19 +83,6 @@ dependencies { // Coroutines androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines" - // Virgil -// androidTestImplementation ("com.virgilsecurity:pythia:$versions.pythia") { -// exclude group: 'com.virgilsecurity.crypto' -// exclude group: 'com.virgilsecurity.sdk' -// } -// androidTestImplementation "com.virgilsecurity.crypto:pythia-android:$versions.virgilCrypto" -// androidTestImplementation ("com.virgilsecurity.sdk:sdk:$versions.virgilSdk") { -// exclude group: 'com.virgilsecurity.crypto' -// } -// androidTestImplementation "com.virgilsecurity.sdk:crypto-android:$versions.virgilSdk" -// androidTestImplementation "com.virgilsecurity.crypto:common-android:$versions.virgilCrypto" -// androidTestImplementation "com.virgilsecurity:common:$versions.virgilSdk" - // Tests core testImplementation "junit:junit:$versions.junit" androidTestImplementation "androidx.test.ext:junit:$versions.testsRunner" diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt index b70abdab..4edd3543 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt @@ -39,10 +39,10 @@ import com.virgilsecurity.android.common.exception.EThreeException import com.virgilsecurity.android.common.exception.FindUsersException import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.common.utils.TestConfig -import com.virgilsecurity.android.common.utils.TestConfig.Companion.virgilBaseUrl -import com.virgilsecurity.android.common.utils.TestConfig.Companion.virgilCrypto -import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilBaseUrl +import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilCrypto +import com.virgilsecurity.android.ethree.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager @@ -353,7 +353,8 @@ class EThreeAuthTest { var rotateFailed = false val waiterTwo = CountDownLatch(1) - eThree.lookupPublicKeys(listOf(identity)) + listOf(identity) + eThree.findUsers(List) .addCallback(object : OnResultListener> { override fun onSuccess(result: Map) { fail("Illegal state") diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt index c3c4a796..228db59b 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeBackupTest.kt @@ -41,9 +41,9 @@ import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.common.exception.RestoreKeyException import com.virgilsecurity.android.common.exception.WrongPasswordException import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.common.utils.TestConfig -import com.virgilsecurity.android.common.utils.TestConfig.Companion.virgilBaseUrl -import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilBaseUrl +import com.virgilsecurity.android.ethree.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.keyknox.KeyknoxManager diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt index 8b30c3fa..27f6e7ea 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt @@ -38,8 +38,8 @@ import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.EThreeException import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.common.utils.TestConfig -import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager @@ -66,6 +66,7 @@ import java.io.ByteArrayOutputStream import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit +import kotlin.String.Companion /** * Created by: @@ -174,7 +175,7 @@ class EThreeEncryptionTest { val publishedCardOne = cardManagerOne.publishCard(generateRawCard(identityOne, cardManagerOne).right) - eThree.lookupPublicKeys(identityOne) + findUser(String) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { assertTrue(result.isNotEmpty() && result.size == 1) @@ -208,7 +209,8 @@ class EThreeEncryptionTest { val publishedCardThree = cardManagerThree.publishCard(generateRawCard(identityThree, cardManagerThree).right) - eThree.lookupPublicKeys(listOf(identityOne, identityTwo, identityThree)) + listOf(identityOne, identityTwo, identityThree) + eThree.findUsers(List) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { @@ -231,7 +233,8 @@ class EThreeEncryptionTest { //STE-2 @Test fun lookup_zero_users() { val waiter = CountDownLatch(1) - eThree.lookupPublicKeys(listOf()) + listOf() + eThree.findUsers(List) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { fail("Illegal State") @@ -253,7 +256,8 @@ class EThreeEncryptionTest { var eThreeKeys: LookupResult? = null val waiter = CountDownLatch(1) - eThree.lookupPublicKeys(listOf(identity, identityTwo)) + listOf(identity, identityTwo) + eThree.findUsers(List) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { eThreeKeys = result @@ -269,7 +273,7 @@ class EThreeEncryptionTest { assertEquals(2, eThreeKeys?.size) try { - val encrypted = eThree.encrypt(RAW_TEXT, lookupResult) + val encrypted = encrypt(String, FindUsersResult) assertNotNull(encrypted) } catch (e: IllegalArgumentException) { fail(e.message) @@ -284,7 +288,8 @@ class EThreeEncryptionTest { var eThreeKeys: LookupResult? = null val waiter = CountDownLatch(1) - eThree.lookupPublicKeys(listOf(identity, identityTwo)) + listOf(identity, identityTwo) + eThree.findUsers(List) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { eThreeKeys = result @@ -301,19 +306,19 @@ class EThreeEncryptionTest { val lookupEntry = lookupResult.toMutableMap().apply { remove(identity) } // We need only identity - val encryptedForOne = eThree.encrypt(RAW_TEXT, lookupEntry) + val encryptedForOne = encrypt(String, FindUsersResult) val wrongPublicKey = TestConfig.virgilCrypto.generateKeyPair().publicKey var failedWithWrongKey = false try { - eThreeTwo.decrypt(encryptedForOne, wrongPublicKey) + decrypt(String, Card) } catch (throwable: Throwable) { failedWithWrongKey = true } assertTrue(failedWithWrongKey) val publicKey = lookupResult[identity] ?: error("") - val decryptedByTwo = eThreeTwo.decrypt(encryptedForOne, publicKey) + val decryptedByTwo = decrypt(String, Card) assertEquals(RAW_TEXT, decryptedByTwo) } @@ -321,7 +326,8 @@ class EThreeEncryptionTest { // STE-4 @Test(expected = EThreeException::class) fun encrypt_for_zero_users() { - eThree.encrypt(RAW_TEXT, mapOf()) + mapOf() + encrypt(String, FindUsersResult) } // STE-5 @@ -332,7 +338,7 @@ class EThreeEncryptionTest { var failedDecrypt = false try { - eThree.decrypt(encryptedWithoutSign, keyPair.publicKey) + decrypt(Data, Card) } catch (e: Exception) { failedDecrypt = true } @@ -398,7 +404,7 @@ class EThreeEncryptionTest { var eThreeKeys: LookupResult? = null val waiter = CountDownLatch(1) - eThree.lookupPublicKeys(identity).addCallback(object : OnResultListener { + findUser(String).addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { eThreeKeys = result waiter.countDown() @@ -421,7 +427,8 @@ class EThreeEncryptionTest { var eThreeKeys: LookupResult? = null val waiter = CountDownLatch(1) - eThree.lookupPublicKeys(listOf(identity, identityTwo)) + listOf(identity, identityTwo) + eThree.findUsers(List) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { eThreeKeys = result @@ -440,7 +447,8 @@ class EThreeEncryptionTest { lookupResult.toMutableMap().apply { remove(identity) } // We need only identityTwo ByteArrayOutputStream().use { - eThree.encrypt(ByteArrayInputStream(RAW_TEXT.toByteArray()), it, lookupEntry) + ByteArrayInputStream(RAW_TEXT.toByteArray()) + encrypt(InputStream, OutputStream, FindUsersResult) val encrypted = it.toByteArray() ByteArrayOutputStream().use { outputForDecrypted -> diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt index de0e4616..2ee2a461 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt @@ -40,8 +40,8 @@ import com.virgilsecurity.android.common.exception.FindUsersException import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.common.utils.TestConfig -import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager @@ -224,7 +224,8 @@ class EThreeNegativeTest { @Test(expected = IllegalArgumentException::class) fun encrypt_data_empty() { - eThree.encrypt(ByteArray(0)) + ByteArray(0) + encrypt(ByteArray, FindUsersResult) } @Test(expected = IllegalArgumentException::class) @@ -234,13 +235,15 @@ class EThreeNegativeTest { @Test(expected = IllegalArgumentException::class) fun decrypt_data_empty() { - eThree.decrypt(ByteArray(0)) + ByteArray(0) + decrypt(Data, Card) } @Test fun lookup_fail_without_bootstrap() { var failed = false val waiter = CountDownLatch(1) - eThree.lookupPublicKeys(listOf("")) + listOf("") + eThree.findUsers(List) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { fail("Not Bootstrapped") @@ -260,7 +263,8 @@ class EThreeNegativeTest { var failed = false val waiter = CountDownLatch(1) - eThree.lookupPublicKeys(listOf(identity, WRONG_IDENTITY)) + listOf(identity, WRONG_IDENTITY) + eThree.findUsers(List) .addCallback(object : OnResultListener> { override fun onSuccess(result: Map) { fail("Illegal State") diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt index b7eb39ff..69a6ca2b 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeScopesTest.kt @@ -36,8 +36,8 @@ package com.virgilsecurity.android.ethree.interaction.async import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.common.utils.TestConfig -import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener import com.virgilsecurity.sdk.cards.CardManager diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/profile/EThreeProfile.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/profile/EThreeProfile.kt index 8b7fab66..0d29c403 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/profile/EThreeProfile.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/profile/EThreeProfile.kt @@ -72,7 +72,8 @@ class EThreeProfile { Debug.stopMethodTracing() } - @Ignore("Needed only to profile from time to time") @Test fun generate_signature_debug_profile() { + @Ignore("Needed only to profile from time to time") @Test + fun generate_signature_debug_profile() { Debug.startMethodTracing("EThreeProfile_generate_signature") crypto.generateSignature(oneMbData, keyPair.privateKey) Debug.stopMethodTracing() diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt index 55a64e4e..770e228a 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt @@ -34,10 +34,12 @@ package com.virgilsecurity.android.ethree.interaction.sync import com.virgilsecurity.android.common.callback.OnGetTokenCallback -import com.virgilsecurity.android.common.exception.* +import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException +import com.virgilsecurity.android.common.exception.WrongPasswordException import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.common.utils.TestConfig -import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestUtils import com.virgilsecurity.keyknox.exception.EntryNotFoundException import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.model.RawSignedModel @@ -206,7 +208,8 @@ class EThreeSyncNegative { @Test fun lookup_zero_users() { val eThree = initEThree(identity) try { - eThree.lookupPublicKeys(listOf()).get() + listOf() + eThree.findUsers(List).get() } catch (throwable: Throwable) { assertTrue(throwable is IllegalArgumentException) } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt index 37089c87..754a789d 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt @@ -35,8 +35,8 @@ package com.virgilsecurity.android.ethree.interaction.sync import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.ethree.interaction.EThree -import com.virgilsecurity.android.common.utils.TestConfig -import com.virgilsecurity.android.common.utils.TestUtils +import com.virgilsecurity.android.ethree.utils.TestConfig +import com.virgilsecurity.android.ethree.utils.TestUtils import com.virgilsecurity.keyknox.KeyknoxManager import com.virgilsecurity.keyknox.client.KeyknoxClient import com.virgilsecurity.keyknox.cloud.CloudKeyStorage @@ -70,6 +70,7 @@ import org.junit.Test import java.net.URL import java.util.* import java.util.concurrent.TimeUnit +import kotlin.String.Companion /** * EThreeSyncPositive @@ -280,7 +281,7 @@ class EThreeSyncPositive { val publishedCardOne = cardManager.publishCard(generateRawCard(identity, cardManager).right) - val lookupResult = eThree.lookupPublicKeys(identity).get() + val lookupResult = findUser(String).get() assertTrue(lookupResult.isNotEmpty() && lookupResult.size == 1) assertEquals(publishedCardOne.publicKey, lookupResult[identity]) @@ -308,8 +309,9 @@ class EThreeSyncPositive { val publishedCardThree = cardManagerThree.publishCard(generateRawCard(identityThree, cardManagerThree).right) + listOf(identityOne, identityTwo, identityThree) val lookupResult = - eThree.lookupPublicKeys(listOf(identityOne, identityTwo, identityThree)).get() + eThree.findUsers(List).get() assertTrue(lookupResult.isNotEmpty() && lookupResult.size == 3) diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java index 0bbb5cb1..9d21cfff 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeScopesTest.java @@ -37,7 +37,7 @@ import com.virgilsecurity.android.common.callback.OnGetTokenCallback; import com.virgilsecurity.android.ethree.interaction.EThree; -import com.virgilsecurity.android.common.utils.TestConfig; +import com.virgilsecurity.android.ethree.utils.TestConfig; import com.virgilsecurity.common.callback.OnCompleteListener; import com.virgilsecurity.common.callback.OnResultListener; import com.virgilsecurity.sdk.cards.Card; diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java index 1e19b9cb..ff24603a 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/java/interaction/EThreeTestPositive.java @@ -37,8 +37,8 @@ import com.virgilsecurity.android.common.callback.OnGetTokenCallback; import com.virgilsecurity.android.ethree.interaction.EThree; -import com.virgilsecurity.android.common.utils.TestConfig; -import com.virgilsecurity.android.common.utils.TestUtils; +import com.virgilsecurity.android.ethree.utils.TestConfig; +import com.virgilsecurity.android.ethree.utils.TestUtils; import com.virgilsecurity.common.callback.OnCompleteListener; import com.virgilsecurity.common.callback.OnResultListener; import com.virgilsecurity.sdk.cards.Card; diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt new file mode 100644 index 00000000..b98b96a8 --- /dev/null +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.ethree.utils + +import androidx.test.platform.app.InstrumentationRegistry +import com.virgilsecurity.android.common.BuildConfig +import com.virgilsecurity.sdk.crypto.VirgilCrypto +import com.virgilsecurity.sdk.crypto.VirgilPrivateKey +import com.virgilsecurity.sdk.utils.ConvertionUtils + +class TestConfig { + companion object { + val virgilCrypto = VirgilCrypto(false) + val appId = BuildConfig.APP_ID + val apiKey: VirgilPrivateKey by lazy { + virgilCrypto.importPrivateKey(ConvertionUtils.base64ToBytes(BuildConfig.API_PRIVATE_KEY)) + .privateKey + } + val apiPublicKeyId = BuildConfig.API_PUBLIC_KEY_ID + + val virgilBaseUrl = BuildConfig.VIRGIL_BASE_URL + const val VIRGIL_CARDS_SERVICE_PATH = "/card/v5/" + + val context = InstrumentationRegistry.getInstrumentation().targetContext + val DIRECTORY_PATH = InstrumentationRegistry.getInstrumentation() + .targetContext.filesDir.absolutePath + val KEYSTORE_NAME = "virgil.keystore" + } +} diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt new file mode 100644 index 00000000..ae3df847 --- /dev/null +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestUtils.kt @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2015-2019, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.android.ethree.utils + +import com.virgilsecurity.android.ethree.utils.TestConfig.Companion.virgilCrypto +import com.virgilsecurity.sdk.cards.Card +import com.virgilsecurity.sdk.cards.ModelSigner +import com.virgilsecurity.sdk.cards.model.RawCardContent +import com.virgilsecurity.sdk.cards.model.RawSignedModel +import com.virgilsecurity.sdk.client.VirgilCardClient +import com.virgilsecurity.sdk.common.TimeSpan +import com.virgilsecurity.sdk.crypto.VirgilAccessTokenSigner +import com.virgilsecurity.sdk.crypto.VirgilCardCrypto +import com.virgilsecurity.sdk.jwt.Jwt +import com.virgilsecurity.sdk.jwt.JwtGenerator +import com.virgilsecurity.sdk.jwt.accessProviders.ConstAccessTokenProvider +import com.virgilsecurity.sdk.utils.ConvertionUtils +import java.util.* +import java.util.concurrent.TimeUnit + +class TestUtils { + + companion object { + const val THROTTLE_TIMEOUT = 2 * 1000L // 2 seconds + + fun pause(timeout: Long = THROTTLE_TIMEOUT) { + Thread.sleep(timeout) + } + + fun generateTokenString(identity: String): String = + JwtGenerator( + TestConfig.appId, + TestConfig.apiKey, + TestConfig.apiPublicKeyId, + TimeSpan.fromTime(600, TimeUnit.SECONDS), + VirgilAccessTokenSigner(virgilCrypto) + ).generateToken(identity).stringRepresentation() + + fun generateToken(identity: String): Jwt = + JwtGenerator( + TestConfig.appId, + TestConfig.apiKey, + TestConfig.apiPublicKeyId, + TimeSpan.fromTime(600, TimeUnit.SECONDS), + VirgilAccessTokenSigner(virgilCrypto) + ).generateToken(identity) + + fun publishCard(identity: String? = null, previousCardId: String? = null): Card { + val keyPair = virgilCrypto.generateKeyPair() + val exportedPublicKey = virgilCrypto.exportPublicKey(keyPair.publicKey) + val identityNew = identity ?: UUID.randomUUID().toString() + val content = RawCardContent(identityNew, + ConvertionUtils.toBase64String(exportedPublicKey), + "5.0", + Date(), + previousCardId) + val snapshot = content.snapshot() + val rawCard = RawSignedModel(snapshot) + val token = generateToken(identityNew) + val provider = ConstAccessTokenProvider(token) + val signer = ModelSigner(VirgilCardCrypto(virgilCrypto)) + signer.selfSign(rawCard, keyPair.privateKey) + val cardClient = VirgilCardClient(TestConfig.virgilBaseUrl + TestConfig.VIRGIL_CARDS_SERVICE_PATH) + + val responseRawCard = + cardClient.publishCard(rawCard, + provider.getToken(null).stringRepresentation()) + + return Card.parse(VirgilCardCrypto(virgilCrypto), responseRawCard) + } + } +} diff --git a/tests/src/main/AndroidManifest.xml b/tests/src/main/AndroidManifest.xml index f05b7e3f..485b70fb 100644 --- a/tests/src/main/AndroidManifest.xml +++ b/tests/src/main/AndroidManifest.xml @@ -34,13 +34,15 @@ - + + - + diff --git a/tests/src/main/res/drawable-v24/ic_launcher_foreground.xml b/tests/src/main/res/drawable-v24/ic_launcher_foreground.xml index ac34de40..9666db06 100644 --- a/tests/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ b/tests/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -32,36 +32,36 @@ --> + xmlns:aapt="http://schemas.android.com/aapt" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + android:fillType="evenOdd" + android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z" + android:strokeWidth="1" + android:strokeColor="#00000000"> + android:endX="78.5885" + android:endY="90.9159" + android:startX="48.7653" + android:startY="61.0927" + android:type="linear"> + android:color="#44000000" + android:offset="0.0" /> + android:color="#00000000" + android:offset="1.0" /> + android:fillColor="#FFFFFF" + android:fillType="nonZero" + android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z" + android:strokeWidth="1" + android:strokeColor="#00000000" /> diff --git a/tests/src/main/res/drawable/ic_launcher_background.xml b/tests/src/main/res/drawable/ic_launcher_background.xml index 4a09c332..671001ba 100644 --- a/tests/src/main/res/drawable/ic_launcher_background.xml +++ b/tests/src/main/res/drawable/ic_launcher_background.xml @@ -1,5 +1,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/tests/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index c352913d..0660549c 100644 --- a/tests/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/tests/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,4 @@ - - - - + + diff --git a/tests/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/tests/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index c352913d..0660549c 100644 --- a/tests/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/tests/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,4 @@ - - - - + + diff --git a/tests/src/main/res/values/colors.xml b/tests/src/main/res/values/colors.xml index 605d9555..e199f5e7 100644 --- a/tests/src/main/res/values/colors.xml +++ b/tests/src/main/res/values/colors.xml @@ -1,5 +1,4 @@ - - - - + + From 9afc71a6907bcab36af8f7fc57d1dc90ddb393d1 Mon Sep 17 00:00:00 2001 From: BuddahLD Date: Fri, 27 Sep 2019 18:54:06 +0300 Subject: [PATCH 07/13] Travis setup pt1 --- .travis.yml | 3 +- .../android/common/worker/PeerToPeerTest.kt | 9 +++-- .../interaction/async/EThreeAuthTest.kt | 3 +- .../interaction/async/EThreeEncryptionTest.kt | 38 +++++++++---------- .../interaction/async/EThreeNegativeTest.kt | 14 +++---- .../interaction/sync/EThreeSyncNegative.kt | 3 +- .../interaction/sync/EThreeSyncPositive.kt | 5 +-- .../android/ethree/utils/TestConfig.kt | 2 +- 8 files changed, 36 insertions(+), 41 deletions(-) diff --git a/.travis.yml b/.travis.yml index 76340e5e..3888e218 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ branches: only: - dev - master + - travis-setup before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock @@ -55,7 +56,7 @@ before_install: - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" - chmod +x gradlew - ./gradlew dependencies || true - - openssl aes-256-cbc -K $encrypted_62e1b1822e5d_key -iv $encrypted_62e1b1822e5d_iv -in tests/src/androidTest/resources/compat/compat_data.json.enc -out tests/src/androidTest/resources/compat/compat_data.json -d + - openssl aes-256-cbc -K $encrypted_62e1b1822e5d_key -iv $encrypted_62e1b1822e5d_iv -in ethree-common/src/androidTest/resources/compat/compat_data.json.enc -out ethree-common/src/androidTest/resources/compat/compat_data.json -d before_script: - android list targets diff --git a/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt index c0742c3f..67becc7a 100644 --- a/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt +++ b/ethree-common/src/androidTest/java/com/virgilsecurity/android/common/worker/PeerToPeerTest.kt @@ -41,6 +41,7 @@ import com.virgilsecurity.android.common.utils.TestConfig import com.virgilsecurity.android.common.utils.TestUtils import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.common.model.Data +import com.virgilsecurity.sdk.cards.Card import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.storage.DefaultKeyStorage import com.virgilsecurity.sdk.utils.ConvertionUtils @@ -264,18 +265,18 @@ class PeerToPeerTest { ethreeTwo.register().execute() - val lookupResult = findUser(String).get() + val lookupResult = ethree.lookupPublicKeys(ethreeTwo.identity).get() assertTrue(lookupResult.isNotEmpty()) - val encrypted = encrypt(String, FindUsersResult) + val encrypted = ethree.encrypt(TEXT, lookupResult) - val lookupResultTwo = findUser(String).get() + val lookupResultTwo = ethreeTwo.lookupPublicKeys(ethree.identity).get() assertTrue(lookupResultTwo.isNotEmpty()) val publicKey = lookupResultTwo[ethree.identity] ?: error("publicKey should not be null") - val decrypted = decrypt(String, Card) + val decrypted = ethreeTwo.decrypt(encrypted, publicKey) assertEquals(TEXT, decrypted) } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt index 4edd3543..ecaf515f 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeAuthTest.kt @@ -353,8 +353,7 @@ class EThreeAuthTest { var rotateFailed = false val waiterTwo = CountDownLatch(1) - listOf(identity) - eThree.findUsers(List) + eThree.lookupPublicKeys(listOf(identity)) .addCallback(object : OnResultListener> { override fun onSuccess(result: Map) { fail("Illegal state") diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt index 27f6e7ea..e3d262f9 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeEncryptionTest.kt @@ -36,12 +36,14 @@ package com.virgilsecurity.android.ethree.interaction.async import androidx.test.ext.junit.runners.AndroidJUnit4 import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.EThreeException +import com.virgilsecurity.android.common.model.FindUsersResult import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.ethree.utils.TestConfig import com.virgilsecurity.android.ethree.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener +import com.virgilsecurity.sdk.cards.Card import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.model.RawSignedModel import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier @@ -63,6 +65,8 @@ import org.junit.Test import org.junit.runner.RunWith import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.OutputStream import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -175,7 +179,7 @@ class EThreeEncryptionTest { val publishedCardOne = cardManagerOne.publishCard(generateRawCard(identityOne, cardManagerOne).right) - findUser(String) + eThree.lookupPublicKeys(identityOne) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { assertTrue(result.isNotEmpty() && result.size == 1) @@ -209,8 +213,7 @@ class EThreeEncryptionTest { val publishedCardThree = cardManagerThree.publishCard(generateRawCard(identityThree, cardManagerThree).right) - listOf(identityOne, identityTwo, identityThree) - eThree.findUsers(List) + eThree.lookupPublicKeys(listOf(identityOne, identityTwo, identityThree)) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { @@ -233,8 +236,7 @@ class EThreeEncryptionTest { //STE-2 @Test fun lookup_zero_users() { val waiter = CountDownLatch(1) - listOf() - eThree.findUsers(List) + eThree.lookupPublicKeys(listOf()) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { fail("Illegal State") @@ -256,8 +258,7 @@ class EThreeEncryptionTest { var eThreeKeys: LookupResult? = null val waiter = CountDownLatch(1) - listOf(identity, identityTwo) - eThree.findUsers(List) + eThree.lookupPublicKeys(listOf(identity, identityTwo)) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { eThreeKeys = result @@ -273,7 +274,7 @@ class EThreeEncryptionTest { assertEquals(2, eThreeKeys?.size) try { - val encrypted = encrypt(String, FindUsersResult) + val encrypted = eThree.encrypt(RAW_TEXT, lookupResult) assertNotNull(encrypted) } catch (e: IllegalArgumentException) { fail(e.message) @@ -288,8 +289,7 @@ class EThreeEncryptionTest { var eThreeKeys: LookupResult? = null val waiter = CountDownLatch(1) - listOf(identity, identityTwo) - eThree.findUsers(List) + eThree.lookupPublicKeys(listOf(identity, identityTwo)) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { eThreeKeys = result @@ -306,19 +306,19 @@ class EThreeEncryptionTest { val lookupEntry = lookupResult.toMutableMap().apply { remove(identity) } // We need only identity - val encryptedForOne = encrypt(String, FindUsersResult) + val encryptedForOne = eThree.encrypt(RAW_TEXT, lookupEntry) val wrongPublicKey = TestConfig.virgilCrypto.generateKeyPair().publicKey var failedWithWrongKey = false try { - decrypt(String, Card) + eThreeTwo.decrypt(encryptedForOne, wrongPublicKey) } catch (throwable: Throwable) { failedWithWrongKey = true } assertTrue(failedWithWrongKey) val publicKey = lookupResult[identity] ?: error("") - val decryptedByTwo = decrypt(String, Card) + val decryptedByTwo = eThreeTwo.decrypt(encryptedForOne, publicKey) assertEquals(RAW_TEXT, decryptedByTwo) } @@ -326,8 +326,7 @@ class EThreeEncryptionTest { // STE-4 @Test(expected = EThreeException::class) fun encrypt_for_zero_users() { - mapOf() - encrypt(String, FindUsersResult) + eThree.encrypt(RAW_TEXT, mapOf()) } // STE-5 @@ -338,7 +337,7 @@ class EThreeEncryptionTest { var failedDecrypt = false try { - decrypt(Data, Card) + eThree.decrypt(encryptedWithoutSign, keyPair.publicKey) } catch (e: Exception) { failedDecrypt = true } @@ -404,7 +403,7 @@ class EThreeEncryptionTest { var eThreeKeys: LookupResult? = null val waiter = CountDownLatch(1) - findUser(String).addCallback(object : OnResultListener { + eThree.lookupPublicKeys(identity).addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { eThreeKeys = result waiter.countDown() @@ -427,8 +426,7 @@ class EThreeEncryptionTest { var eThreeKeys: LookupResult? = null val waiter = CountDownLatch(1) - listOf(identity, identityTwo) - eThree.findUsers(List) + eThree.lookupPublicKeys(listOf(identity, identityTwo)) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { eThreeKeys = result @@ -448,7 +446,7 @@ class EThreeEncryptionTest { ByteArrayOutputStream().use { ByteArrayInputStream(RAW_TEXT.toByteArray()) - encrypt(InputStream, OutputStream, FindUsersResult) + eThree.encrypt(ByteArrayInputStream(RAW_TEXT.toByteArray()), it, lookupEntry) val encrypted = it.toByteArray() ByteArrayOutputStream().use { outputForDecrypted -> diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt index 2ee2a461..9e11ab5c 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/async/EThreeNegativeTest.kt @@ -38,12 +38,14 @@ import com.virgilsecurity.android.common.callback.OnGetTokenCallback import com.virgilsecurity.android.common.exception.EThreeException import com.virgilsecurity.android.common.exception.FindUsersException import com.virgilsecurity.android.common.exception.PrivateKeyNotFoundException +import com.virgilsecurity.android.common.model.FindUsersResult import com.virgilsecurity.android.common.model.LookupResult import com.virgilsecurity.android.ethree.interaction.EThree import com.virgilsecurity.android.ethree.utils.TestConfig import com.virgilsecurity.android.ethree.utils.TestUtils import com.virgilsecurity.common.callback.OnCompleteListener import com.virgilsecurity.common.callback.OnResultListener +import com.virgilsecurity.sdk.cards.Card import com.virgilsecurity.sdk.cards.CardManager import com.virgilsecurity.sdk.cards.model.RawSignedModel import com.virgilsecurity.sdk.cards.validation.VirgilCardVerifier @@ -224,8 +226,7 @@ class EThreeNegativeTest { @Test(expected = IllegalArgumentException::class) fun encrypt_data_empty() { - ByteArray(0) - encrypt(ByteArray, FindUsersResult) + eThree.encrypt(ByteArray(0)) } @Test(expected = IllegalArgumentException::class) @@ -235,15 +236,13 @@ class EThreeNegativeTest { @Test(expected = IllegalArgumentException::class) fun decrypt_data_empty() { - ByteArray(0) - decrypt(Data, Card) + eThree.decrypt(ByteArray(0)) } @Test fun lookup_fail_without_bootstrap() { var failed = false val waiter = CountDownLatch(1) - listOf("") - eThree.findUsers(List) + eThree.lookupPublicKeys(listOf("")) .addCallback(object : OnResultListener { override fun onSuccess(result: LookupResult) { fail("Not Bootstrapped") @@ -263,8 +262,7 @@ class EThreeNegativeTest { var failed = false val waiter = CountDownLatch(1) - listOf(identity, WRONG_IDENTITY) - eThree.findUsers(List) + eThree.lookupPublicKeys(listOf(identity, WRONG_IDENTITY)) .addCallback(object : OnResultListener> { override fun onSuccess(result: Map) { fail("Illegal State") diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt index 770e228a..1d23b539 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncNegative.kt @@ -208,8 +208,7 @@ class EThreeSyncNegative { @Test fun lookup_zero_users() { val eThree = initEThree(identity) try { - listOf() - eThree.findUsers(List).get() + eThree.lookupPublicKeys(listOf()).get() } catch (throwable: Throwable) { assertTrue(throwable is IllegalArgumentException) } diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt index 754a789d..0c0b1084 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/interaction/sync/EThreeSyncPositive.kt @@ -281,7 +281,7 @@ class EThreeSyncPositive { val publishedCardOne = cardManager.publishCard(generateRawCard(identity, cardManager).right) - val lookupResult = findUser(String).get() + val lookupResult = eThree.lookupPublicKeys(identity).get() assertTrue(lookupResult.isNotEmpty() && lookupResult.size == 1) assertEquals(publishedCardOne.publicKey, lookupResult[identity]) @@ -309,9 +309,8 @@ class EThreeSyncPositive { val publishedCardThree = cardManagerThree.publishCard(generateRawCard(identityThree, cardManagerThree).right) - listOf(identityOne, identityTwo, identityThree) val lookupResult = - eThree.findUsers(List).get() + eThree.lookupPublicKeys(listOf(identityOne, identityTwo, identityThree)).get() assertTrue(lookupResult.isNotEmpty() && lookupResult.size == 3) diff --git a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt index b98b96a8..10496d0f 100644 --- a/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt +++ b/tests/src/androidTest/java/com/virgilsecurity/android/ethree/utils/TestConfig.kt @@ -34,7 +34,7 @@ package com.virgilsecurity.android.ethree.utils import androidx.test.platform.app.InstrumentationRegistry -import com.virgilsecurity.android.common.BuildConfig +import com.virgilsecurity.android.ethree.BuildConfig import com.virgilsecurity.sdk.crypto.VirgilCrypto import com.virgilsecurity.sdk.crypto.VirgilPrivateKey import com.virgilsecurity.sdk.utils.ConvertionUtils From 9799dfee45e71bc44689ae31442ef64e39356ca5 Mon Sep 17 00:00:00 2001 From: Danylo Oliinyk Date: Mon, 30 Sep 2019 16:19:23 +0300 Subject: [PATCH 08/13] Update main README with group chats [ci skip] --- README.md | 291 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 189 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index a57f4578..c28d07df 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,24 @@ [![Build Status](https://travis-ci.com/VirgilSecurity/virgil-e3kit-kotlin.svg?branch=master)](https://travis-ci.com/VirgilSecurity/virgil-e3kit-kotlin) [![GitHub license](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://github.com/VirgilSecurity/virgil/blob/master/LICENSE) -[Introduction](#introduction) | [SDK Features](#sdk-features) | [Installation](#installation) | [Usage Examples](#usage-examples) | [Samples](#samples) | [License](#license) | [Docs](#docs) | [Support](#support) +[Introduction](#introduction) | [SDK Features](#features) | [Installation](#installation) | [Usage Examples](#usage-examples) | [Enable Group Chat](#enable-group-chat) | [Samples](#samples) | [License](#license) | [Docs](#docs) | [Support](#support) ## Introduction - [Virgil Security](https://virgilsecurity.com) provides the E3Kit SDK which simplifies work with Virgil services and presents an easy-to-use API for adding a security layer to any application. E3Kit interacts with Virgil Cards Service, Keyknox Service and Pythia Service. -Virgil E3kit allows you to setup user encryption with multidevice support in just a few simple steps. + [Virgil Security](https://virgilsecurity.com) provides the E3Kit which simplifies work with Virgil Cloud and presents an easy-to-use API for adding a security layer to any application. In a few simple steps you can add end-to-end encryption with multidevice and group chats support. -## SDK Features -- multi-device support -- group chats -- manage users' Public Keys +The E3Kit allows developers to get up and running with Virgil API quickly and add full end-to-end security to their existing digital solutions to become HIPAA and GDPR compliant and more. + +## Features +- Strong end-to-end encryption with authorization +- One-to-one and group encryption +- Files and stream encryption +- Recovery features for secret keys +- Strong secret keys storage, integration with Keychain +- Integration with any CPaaS providers like Nexmo, Firebase, Twilio, PubNub, etc. +- Public keys cache features +- Access encrypted data from multiple user devices +- Easy setup and integration into new or existing projects ## Installation @@ -21,151 +28,231 @@ You can install E3Kit SDK using [Gradle](https://gradle.org/). Please, choose pa | Package | Description | |----------|---------| -| [`E3Kit`](./ethree-kotlin) | Standard package for Java/Kotlin with methods responses in `callbacks` | +| [`E3Kit`](./ethree-kotlin) | Standard package for Android API 21+ (Java/Kotlin) | +| [`E3Kit`](./ethree-enclave) | Package with [Android Keystore](https://developer.android.com/training/articles/keystore) for Android API 23+ | ## Usage Examples -#### Initialize e3kit +> Be sure to import `OnCompleteListener` and `OnResultListener` from `com.virgilsecurity.common.callback`. -In order to interact with the Virgil Cloud, the Virgil e3kit SDK must be provided with a callback that it will call to fetch the Virgil JWT from your backend for the current user. +#### Register user +Use the following lines of code to authenticate user. ```kotlin -lateinit var eThree: EThree - -// Listener for E3Kit initialization -val initializeListener = - object : OnResultListener { - override fun onSuccess(result: EThree) { - // Init done! - eThree = result - } +// initialize E3Kit +val ethree = EThree(identity = "Bob", tokenCallback = tokenCallback, context = context) - override fun onError(throwable: Throwable) { - // Error handling - } +ethree.register().addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Done } -// initialize E3Kit -EThree.initialize(context, virgilTokenCallback).addCallback(initializeListener) + override fun onError(throwable: Throwable) { + // Error handling + } +}) ``` -#### Register user +#### Encrypt & decrypt -Use the following lines of code to register a user: +Virgil E3Kit lets you use a user's Private key and his or her Card to sign, then encrypt text. ```kotlin -// TODO: Initialize e3kit +// TODO: init and register user (see Register User) -val registerListener = - object : OnCompleteListener { - override fun onSuccess() { - // User private key loaded, ready for end-to-end encrypt! - } +// prepare a message +val messageToEncrypt = "Hello, Alice and Den!" - override fun onError(throwable: Throwable) { - // Error handling - } +// Search user's Cards to encrypt for +ethree.findUsers(listOf("Alice, Den")) + .addCallback(object : OnResultListener { + override fun onSuccess(result: FindUsersResult) { + // encrypt text + val encryptedMessage = ethree.encrypt(messageToEncrypt, result) + } + + override fun onError(throwable: Throwable) { + // Error handling + } + }) +``` + +Decrypt and verify the signed & encrypted data using sender's public key and receiver's private key: + +```kotlin +// TODO: init and register user (see Register User) + +// Find user +ethree.findUsers(listOf("bobUID")) + .addCallback(object : OnResultListener { + override fun onSuccess(result: FindUsersResult) { + // Decrypt text and verify if it was really written by Bob + val originText = ethree.decrypt(encryptedText, result["bobUID"]) } -eThree.register().addCallback(registerListener) + override fun onError(throwable: Throwable) { + // Error handling + } +}) ``` -This function generates PrivateKey/PublicKey keypair, saves PrivateKey locally on device and publishes PublicKey to Virgil Cards Service. -#### Sign and encrypt data/text +#### Encrypt & decrypt large files -This method signs the data/text with the sender's private key and encrypts the message for recipients' public key(s). +If the data that needs to be encrypted is too large for your RAM to encrypt all at once, use the following snippets to encrypt and decrypt streams. +Encryption: ```kotlin -// TODO: Initialize e3kit, Register e3kit user +// TODO: init and register user (see Register User) +// TODO: Get users UIDs -val lookupKeysListener = - object : OnResultListener { - override fun onSuccess(result: LookupResult) { - val text = "I was text but become byte array" - val data = text.toByteArray() +val usersToEncryptTo = listOf(user1UID, user2UID, user3UID) - // Encrypt data using user public keys - val encryptedData = eThree.encrypt(data, result) +// Find users +ethree.findUsers(usersToEncryptTo) + .addCallback(object : OnResultListener { + override fun onSuccess(result: FindUsersResult) { + val assetManager = context.assets - // Encrypt message using user public keys - val encryptedText = eThree.encrypt(text, result) + assetManager.open("some_file.txt").use { inputStream -> + ByteArrayOutputStream().use { outputStream -> + ethree.encrypt(inputStream, outputStream, result) + } } + } - override fun onError(throwable: Throwable) { - // Error handling - } + override fun onError(throwable: Throwable) { + // Error handling } +}) +``` -// Lookup destination user public keys -eThree.lookupPublicKeys(listOf("userUID1", "userUID2", "userUID3")).addCallback(lookupKeysListener) +Decryption: +> Stream encryption doesn’t sign the data. This is why decryption doesn’t need Card for verification unlike the general data decryption. +```kotlin +// TODO: init and register user (see Register User) + +ByteArrayOutputStream().use { outputStream -> + eThree.decrypt(encryptedStream, outputStream) +} ``` -#### Decrypt data/text and verify signature +## Enable Group Chat +In this section, you'll find out how to build a group chat using the Virgil E3Kit. -This method decrypts the data using the recipient's private key and verifies authenticity of the decrypted data with sender's public key. +We assume that your users have installed and initialized the E3Kit, and used snippet above to register. -```kotlin -// TODO: Initialize e3kit, Register e3kit user -val lookupKeysListener = - object : OnResultListener { - override fun onSuccess(result: LookupResult) { - // Decrypt data and verify if it was really written by Bob - val decryptedData = eThree.decrypt(encryptedData, result["bobUID"]) +#### Create group chat +Let's imagine Alice wants to start a group chat with Bob and Carol. First, Alice creates a new group ticket by running the `createGroup` feature and the E3Kit stores the ticket on the Virgil Cloud. This ticket holds a shared root key for future group encryption. - // Decrypt text and verify if it was really written by Bob - val decryptedText = eThree.decrypt(encryptedText, result["bobUID"]) - } +Alice has to specify a unique `identifier` of group with length > 10 and `findUsersResult` of participants. We recommend tying this identifier to your unique transport channel id. +```kotlin +ethree.createGroup(groupId, users).addCallback(object : OnResultListener { + override fun onSuccess(result: Group) { + // Group created and saved locally! + } - override fun onError(throwable: Throwable) { - // Error handling - } + override fun onError(throwable: Throwable) { + // Error handling } +}) +``` + +#### Start group chat session -// Lookup chat room member key -eThree.lookupPublicKeys("bobUID").addCallback(lookupKeysListener) +Now, other participants, Bob and Carol, want to join the Alice's group and have to start the group session by loading the group ticket using the `loadGroup` method. This function requires specifying the group `identifier` and group initiator's Card. +```kotlin +ethree.loadGroup(groupId, users["Alice"]!!).addCallback(object : OnResultListener { + override fun onSuccess(result: Group) { + // Group loaded and saved locally! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) ``` -#### Encrypt & decrypt large files +Use the loadGroup method to load and save group locally. Then, you can use the getGroup method to retrieve group instance from local storage. +```kotlin +val group = ethree.getGroup(groupId) +``` -If the data that needs to be encrypted is too large for your RAM to encrypt all at once, use the following snippets to encrypt and decrypt streams. -> Stream encryption doesn’t sign the data. This is why stream decryption doesn’t require VirgilPublicKey for verification unlike the general data decryption. +#### Encrypt and decrypt messages +To encrypt and decrypt messages, use the `encrypt` and `decrypt` E3Kit functions, which allows you to work with data and strings. -Encryption: +Use the following code-snippets to encrypt messages: ```kotlin -// TODO: initialize and register user (see EThree.initialize and EThree#register) - -// Listener for keys lookup -val lookupKeysListener = - object : OnResultListener { - override fun onSuccess(result: LookupResult) { - val assetManager = context.assets - - assetManager.open("some_file.txt").use { inputStream -> - ByteArrayOutputStream().use { outputStream -> - // Encrypt input stream using user public keys and writes output to the output stream - eThree.encrypt(inputStream, outputStream, result) - } - } - } +// prepare a message +val messageToEncrypt = "Hello, Bob and Carol!" - override fun onError(throwable: Throwable) { - // Error handling - } - } +val encrypted = group.encrypt(messageToEncrypt) +``` -// Lookup destination user public keys -eThree.lookupPublicKeys(listOf("userUID1", "userUID2", "userUID3")).addCallback(lookupKeysListener) +Use the following code-snippets to decrypt messages: +```kotlin +val decrypted = group.decrypt(encrypted, findUsersResult["Alice"]!!) ``` +At the decrypt step, you also use `findUsers` method to verify that the message hasn't been tempered with. -Decryption: +### Manage group chat +E3Kit also allows you to perform other operations, like participants management, while you work with group chat. In this version of E3Kit only group initiator can change participants or delete group. + +#### Add new participant +To add a new chat member, the chat owner has to use the `add` method and specify the new member's Card. New member will be able to decrypt all previous messages history. ```kotlin -// TODO: init SDK and register users - see EThree.initialize and EThree#register -ByteArrayOutputStream().use { outputStream -> - // Decrypt encrypted input stream and writes output to the output stream - eThree.decrypt(encryptedStream, outputStream) -} +group.add(users["Den"]!!).addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Den was added! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Remove participant +To remove participant, group owner has to use the `remove` method and specify the member's Card. Removed participants won't be able to load or update this group. +```kotlin +group.remove(users["Den"]!!).addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Den was removed! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Update group chat +In the event of changes in your group, i.e. adding a new participant, or deleting an existing one, each group chat participant has to update the encryption key by calling the `update` E3Kit method or reloading Group by `loadGroup`. +```kotlin +group.update().addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Group updated! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Delete group chat +To delete a group, the owner has to use the `deleteGroup` method and specify the group `identifier`. +```kotlin +ethree.deleteGroup(groupId).addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Group was deleted! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) ``` ## Samples From 8837dc3b129e53b6b3a713aae4a1f6113287ce6a Mon Sep 17 00:00:00 2001 From: Danylo Oliinyk Date: Mon, 30 Sep 2019 16:24:27 +0300 Subject: [PATCH 09/13] ethree-kotlin README with group chats [ci skip] --- ethree-kotlin/README.md | 252 ++++++++++++++++++++++++++++++---------- 1 file changed, 189 insertions(+), 63 deletions(-) diff --git a/ethree-kotlin/README.md b/ethree-kotlin/README.md index 7e77867b..91bc86a3 100644 --- a/ethree-kotlin/README.md +++ b/ethree-kotlin/README.md @@ -1,4 +1,4 @@ -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.virgilsecurity/ethree-kotlin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.virgilsecurity/ethree-kotlin) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.virgilsecurity/ethree/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.virgilsecurity/ethree) [![GitHub license](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://github.com/VirgilSecurity/virgil/blob/master/LICENSE) ## Installation of E3Kit @@ -21,101 +21,227 @@ Set up dependencies in your `build.gradle`: The **\** of the SDK can be found in the [Maven Central Repository](https://mvnrepository.com/artifact/com.virgilsecurity/ethree-kotlin) or in the header of current readme. -## Usage +## Usage Examples -#### Initialize e3kit: +> Be sure to import `OnCompleteListener` and `OnResultListener` from `com.virgilsecurity.common.callback`. + +#### Register user +Use the following lines of code to authenticate user. ```kotlin -lateinit var eThree: EThree - -// Listener for E3Kit initialization -val initializeListener = - object : OnResultListener { - override fun onSuccess(result: EThree) { - // Init done! - eThree = result - } +// initialize E3Kit +val ethree = EThree(identity = "Bob", tokenCallback = tokenCallback, context = context) - override fun onError(throwable: Throwable) { - // Error handling - } +ethree.register().addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Done } -// initialize E3Kit -EThree.initialize(context, virgilTokenCallback).addCallback(initializeListener) + override fun onError(throwable: Throwable) { + // Error handling + } +}) ``` -#### Register e3kit user -Use the following lines of code to register a user: +#### Encrypt & decrypt + +Virgil E3Kit lets you use a user's Private key and his or her Card to sign, then encrypt text. + ```kotlin -// TODO: Initialize e3kit +// TODO: init and register user (see Register User) + +// prepare a message +val messageToEncrypt = "Hello, Alice and Den!" + +// Search user's Cards to encrypt for +ethree.findUsers(listOf("Alice, Den")) + .addCallback(object : OnResultListener { + override fun onSuccess(result: FindUsersResult) { + // encrypt text + val encryptedMessage = ethree.encrypt(messageToEncrypt, result) + } + + override fun onError(throwable: Throwable) { + // Error handling + } + }) +``` -val registerListener = - object : OnCompleteListener { - override fun onSuccess() { - // User private key loaded, ready for end-to-end encrypt! - } +Decrypt and verify the signed & encrypted data using sender's public key and receiver's private key: - override fun onError(throwable: Throwable) { - // Error handling - } +```kotlin +// TODO: init and register user (see Register User) + +// Find user +ethree.findUsers(listOf("bobUID")) + .addCallback(object : OnResultListener { + override fun onSuccess(result: FindUsersResult) { + // Decrypt text and verify if it was really written by Bob + val originText = ethree.decrypt(encryptedText, result["bobUID"]) } -eThree.register().addCallback(registerListener) + override fun onError(throwable: Throwable) { + // Error handling + } +}) ``` -This function generates PrivateKey/PublicKey keypair, saves PrivateKey locally on device and publishes PublicKey to Virgil Cards Service. -#### Sign Then Encrypt data/text +#### Encrypt & decrypt large files -This method signs the data/text with the sender's private key and encrypts the message for recipients' public key(s). +If the data that needs to be encrypted is too large for your RAM to encrypt all at once, use the following snippets to encrypt and decrypt streams. +Encryption: ```kotlin -// TODO: Initialize e3kit, Register e3kit user +// TODO: init and register user (see Register User) +// TODO: Get users UIDs -val lookupKeysListener = - object : OnResultListener { - override fun onSuccess(result: LookupResult) { - val text = "I was text but become byte array" - val data = text.toByteArray() +val usersToEncryptTo = listOf(user1UID, user2UID, user3UID) - // Encrypt data using user public keys - val encryptedData = eThree.encrypt(data, result) +// Find users +ethree.findUsers(usersToEncryptTo) + .addCallback(object : OnResultListener { + override fun onSuccess(result: FindUsersResult) { + val assetManager = context.assets - // Encrypt message using user public keys - val encryptedText = eThree.encrypt(text, result) + assetManager.open("some_file.txt").use { inputStream -> + ByteArrayOutputStream().use { outputStream -> + ethree.encrypt(inputStream, outputStream, result) + } } + } - override fun onError(throwable: Throwable) { - // Error handling - } + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +Decryption: +> Stream encryption doesn’t sign the data. This is why decryption doesn’t need Card for verification unlike the general data decryption. +```kotlin +// TODO: init and register user (see Register User) + +ByteArrayOutputStream().use { outputStream -> + eThree.decrypt(encryptedStream, outputStream) +} +``` + +## Enable Group Chat +In this section, you'll find out how to build a group chat using the Virgil E3Kit. + +We assume that your users have installed and initialized the E3Kit, and used snippet above to register. + + +#### Create group chat +Let's imagine Alice wants to start a group chat with Bob and Carol. First, Alice creates a new group ticket by running the `createGroup` feature and the E3Kit stores the ticket on the Virgil Cloud. This ticket holds a shared root key for future group encryption. + +Alice has to specify a unique `identifier` of group with length > 10 and `findUsersResult` of participants. We recommend tying this identifier to your unique transport channel id. +```kotlin +ethree.createGroup(groupId, users).addCallback(object : OnResultListener { + override fun onSuccess(result: Group) { + // Group created and saved locally! } -// Lookup destination user public keys -eThree.lookupPublicKeys(listOf("userUID1", "userUID2", "userUID3")).addCallback(lookupKeysListener) + override fun onError(throwable: Throwable) { + // Error handling + } +}) ``` -#### Decrypt Then Verify data/text +#### Start group chat session +Now, other participants, Bob and Carol, want to join the Alice's group and have to start the group session by loading the group ticket using the `loadGroup` method. This function requires specifying the group `identifier` and group initiator's Card. ```kotlin -// TODO: Initialize e3kit, Register e3kit user +ethree.loadGroup(groupId, users["Alice"]!!).addCallback(object : OnResultListener { + override fun onSuccess(result: Group) { + // Group loaded and saved locally! + } -val lookupKeysListener = - object : OnResultListener { - override fun onSuccess(result: LookupResult) { - // Decrypt data and verify if it was really written by Bob - val decryptedData = eThree.decrypt(encryptedData, result["bobUID"]) + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` - // Decrypt text and verify if it was really written by Bob - val decryptedText = eThree.decrypt(encryptedText, result["bobUID"]) - } +Use the loadGroup method to load and save group locally. Then, you can use the getGroup method to retrieve group instance from local storage. +```kotlin +val group = ethree.getGroup(groupId) +``` - override fun onError(throwable: Throwable) { - // Error handling - } +#### Encrypt and decrypt messages +To encrypt and decrypt messages, use the `encrypt` and `decrypt` E3Kit functions, which allows you to work with data and strings. + +Use the following code-snippets to encrypt messages: +```kotlin +// prepare a message +val messageToEncrypt = "Hello, Bob and Carol!" + +val encrypted = group.encrypt(messageToEncrypt) +``` + +Use the following code-snippets to decrypt messages: +```kotlin +val decrypted = group.decrypt(encrypted, findUsersResult["Alice"]!!) +``` +At the decrypt step, you also use `findUsers` method to verify that the message hasn't been tempered with. + +### Manage group chat +E3Kit also allows you to perform other operations, like participants management, while you work with group chat. In this version of E3Kit only group initiator can change participants or delete group. + +#### Add new participant +To add a new chat member, the chat owner has to use the `add` method and specify the new member's Card. New member will be able to decrypt all previous messages history. +```kotlin +group.add(users["Den"]!!).addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Den was added! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Remove participant +To remove participant, group owner has to use the `remove` method and specify the member's Card. Removed participants won't be able to load or update this group. +```kotlin +group.remove(users["Den"]!!).addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Den was removed! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Update group chat +In the event of changes in your group, i.e. adding a new participant, or deleting an existing one, each group chat participant has to update the encryption key by calling the `update` E3Kit method or reloading Group by `loadGroup`. +```kotlin +group.update().addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Group updated! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Delete group chat +To delete a group, the owner has to use the `deleteGroup` method and specify the group `identifier`. +```kotlin +ethree.deleteGroup(groupId).addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Group was deleted! } -// Lookup chat room member key -eThree.lookupPublicKeys("bobUID").addCallback(lookupKeysListener) + override fun onError(throwable: Throwable) { + // Error handling + } +}) ``` -You can checkout [Tests](https://github.com/VirgilSecurity/virgil-e3kit-kotlin/tree/master/tests/src/androidTest/java/com/virgilsecurity/android/ethree/kotlin/interaction) to find out more of usage examples. +You can checkout [Tests](https://github.com/VirgilSecurity/virgil-e3kit-kotlin/tree/master/tests/src/androidTest/java/com/virgilsecurity/android/ethree/) to find out more of usage examples. From b7f0d8180c48daa78c8b99dad854be7180150903 Mon Sep 17 00:00:00 2001 From: Danylo Oliinyk Date: Mon, 30 Sep 2019 16:34:45 +0300 Subject: [PATCH 10/13] ethree-enclave README with group chats [ci skip] --- ethree-enclave/README.md | 250 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 ethree-enclave/README.md diff --git a/ethree-enclave/README.md b/ethree-enclave/README.md new file mode 100644 index 00000000..2fa347cf --- /dev/null +++ b/ethree-enclave/README.md @@ -0,0 +1,250 @@ +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.virgilsecurity/ethree-enclave/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.virgilsecurity/ethree-enclave) +[![GitHub license](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://github.com/VirgilSecurity/virgil/blob/master/LICENSE) + +## Installation of E3Kit Enclave + +> Android API 21+ is required. + +This `e3kit` module uses [Android Keystore](https://developer.android.com/training/articles/keystore) to store symmetric key which is used to encrypt `e3kit` user's local private key. +If you use this module after using [Standard e3kit module](../ethree-kotlin) all local private keys will be migrated to Android Keystore, so you won't be able to use them with [Standard e3kit module](../ethree-kotlin) any more. (You can migrate them to [Standard e3kit module](../ethree-kotlin) on your own if needed) + +To integrate E3Kit SDK into your Android project using Gradle, add jcenter() repository if missing: + +``` +repositories { + jcenter() +} +``` + +Set up dependencies in your `build.gradle`: + +``` + implementation 'com.virgilsecurity:ethree-enclave:' +``` + +The **\** of the SDK can be found in the [Maven Central Repository](https://mvnrepository.com/artifact/com.virgilsecurity/ethree-kotlin) or in the header of current readme. + +## Usage Examples + +> Be sure to import `OnCompleteListener` and `OnResultListener` from `com.virgilsecurity.common.callback`. + +#### Register user +Use the following lines of code to authenticate user. + +```kotlin +// initialize E3Kit +val ethree = EThree(identity = "Bob", tokenCallback = tokenCallback, context = context) + +ethree.register().addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Done + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Encrypt & decrypt + +Virgil E3Kit lets you use a user's Private key and his or her Card to sign, then encrypt text. + +```kotlin +// TODO: init and register user (see Register User) + +// prepare a message +val messageToEncrypt = "Hello, Alice and Den!" + +// Search user's Cards to encrypt for +ethree.findUsers(listOf("Alice, Den")) + .addCallback(object : OnResultListener { + override fun onSuccess(result: FindUsersResult) { + // encrypt text + val encryptedMessage = ethree.encrypt(messageToEncrypt, result) + } + + override fun onError(throwable: Throwable) { + // Error handling + } + }) +``` + +Decrypt and verify the signed & encrypted data using sender's public key and receiver's private key: + +```kotlin +// TODO: init and register user (see Register User) + +// Find user +ethree.findUsers(listOf("bobUID")) + .addCallback(object : OnResultListener { + override fun onSuccess(result: FindUsersResult) { + // Decrypt text and verify if it was really written by Bob + val originText = ethree.decrypt(encryptedText, result["bobUID"]) + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Encrypt & decrypt large files + +If the data that needs to be encrypted is too large for your RAM to encrypt all at once, use the following snippets to encrypt and decrypt streams. + +Encryption: +```kotlin +// TODO: init and register user (see Register User) +// TODO: Get users UIDs + +val usersToEncryptTo = listOf(user1UID, user2UID, user3UID) + +// Find users +ethree.findUsers(usersToEncryptTo) + .addCallback(object : OnResultListener { + override fun onSuccess(result: FindUsersResult) { + val assetManager = context.assets + + assetManager.open("some_file.txt").use { inputStream -> + ByteArrayOutputStream().use { outputStream -> + ethree.encrypt(inputStream, outputStream, result) + } + } + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +Decryption: +> Stream encryption doesn’t sign the data. This is why decryption doesn’t need Card for verification unlike the general data decryption. +```kotlin +// TODO: init and register user (see Register User) + +ByteArrayOutputStream().use { outputStream -> + eThree.decrypt(encryptedStream, outputStream) +} +``` + +## Enable Group Chat +In this section, you'll find out how to build a group chat using the Virgil E3Kit. + +We assume that your users have installed and initialized the E3Kit, and used snippet above to register. + + +#### Create group chat +Let's imagine Alice wants to start a group chat with Bob and Carol. First, Alice creates a new group ticket by running the `createGroup` feature and the E3Kit stores the ticket on the Virgil Cloud. This ticket holds a shared root key for future group encryption. + +Alice has to specify a unique `identifier` of group with length > 10 and `findUsersResult` of participants. We recommend tying this identifier to your unique transport channel id. +```kotlin +ethree.createGroup(groupId, users).addCallback(object : OnResultListener { + override fun onSuccess(result: Group) { + // Group created and saved locally! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Start group chat session + +Now, other participants, Bob and Carol, want to join the Alice's group and have to start the group session by loading the group ticket using the `loadGroup` method. This function requires specifying the group `identifier` and group initiator's Card. +```kotlin +ethree.loadGroup(groupId, users["Alice"]!!).addCallback(object : OnResultListener { + override fun onSuccess(result: Group) { + // Group loaded and saved locally! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +Use the loadGroup method to load and save group locally. Then, you can use the getGroup method to retrieve group instance from local storage. +```kotlin +val group = ethree.getGroup(groupId) +``` + +#### Encrypt and decrypt messages +To encrypt and decrypt messages, use the `encrypt` and `decrypt` E3Kit functions, which allows you to work with data and strings. + +Use the following code-snippets to encrypt messages: +```kotlin +// prepare a message +val messageToEncrypt = "Hello, Bob and Carol!" + +val encrypted = group.encrypt(messageToEncrypt) +``` + +Use the following code-snippets to decrypt messages: +```kotlin +val decrypted = group.decrypt(encrypted, findUsersResult["Alice"]!!) +``` +At the decrypt step, you also use `findUsers` method to verify that the message hasn't been tempered with. + +### Manage group chat +E3Kit also allows you to perform other operations, like participants management, while you work with group chat. In this version of E3Kit only group initiator can change participants or delete group. + +#### Add new participant +To add a new chat member, the chat owner has to use the `add` method and specify the new member's Card. New member will be able to decrypt all previous messages history. +```kotlin +group.add(users["Den"]!!).addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Den was added! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Remove participant +To remove participant, group owner has to use the `remove` method and specify the member's Card. Removed participants won't be able to load or update this group. +```kotlin +group.remove(users["Den"]!!).addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Den was removed! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Update group chat +In the event of changes in your group, i.e. adding a new participant, or deleting an existing one, each group chat participant has to update the encryption key by calling the `update` E3Kit method or reloading Group by `loadGroup`. +```kotlin +group.update().addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Group updated! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +#### Delete group chat +To delete a group, the owner has to use the `deleteGroup` method and specify the group `identifier`. +```kotlin +ethree.deleteGroup(groupId).addCallback(object : OnCompleteListener { + override fun onSuccess() { + // Group was deleted! + } + + override fun onError(throwable: Throwable) { + // Error handling + } +}) +``` + +You can checkout [Tests](https://github.com/VirgilSecurity/virgil-e3kit-kotlin/tree/master/tests/src/androidTest/java/com/virgilsecurity/android/ethree/) to find out more of usage examples. From 7ef022937bd3c6927b68162283f34fd6f43bd1b3 Mon Sep 17 00:00:00 2001 From: Mariia Malitska <37335222+MariiaMalitska@users.noreply.github.com> Date: Mon, 30 Sep 2019 17:31:46 +0300 Subject: [PATCH 11/13] [ci skip] Readme update --- README.md | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index c28d07df..f3a879bd 100644 --- a/README.md +++ b/README.md @@ -138,12 +138,11 @@ ByteArrayOutputStream().use { outputStream -> ``` ## Enable Group Chat -In this section, you'll find out how to build a group chat using the Virgil E3Kit. -We assume that your users have installed and initialized the E3Kit, and used snippet above to register. +E3Kit also provides you with tools for easy group chats creating and management. In this section we assume that your users have installed and initialized the E3Kit, and are already registered at Virgil Cloud. +### Create Group Chat -#### Create group chat Let's imagine Alice wants to start a group chat with Bob and Carol. First, Alice creates a new group ticket by running the `createGroup` feature and the E3Kit stores the ticket on the Virgil Cloud. This ticket holds a shared root key for future group encryption. Alice has to specify a unique `identifier` of group with length > 10 and `findUsersResult` of participants. We recommend tying this identifier to your unique transport channel id. @@ -159,9 +158,10 @@ ethree.createGroup(groupId, users).addCallback(object : OnResultListener }) ``` -#### Start group chat session +### Start Group Chat Session + +Now, other participants, Bob and Carol, want to join the Alice's group and have to start the group session using the `loadGroup` method that loads and saves the group ticket locally. This function requires specifying the group `identifier` and group initiator's Card. -Now, other participants, Bob and Carol, want to join the Alice's group and have to start the group session by loading the group ticket using the `loadGroup` method. This function requires specifying the group `identifier` and group initiator's Card. ```kotlin ethree.loadGroup(groupId, users["Alice"]!!).addCallback(object : OnResultListener { override fun onSuccess(result: Group) { @@ -174,15 +174,18 @@ ethree.loadGroup(groupId, users["Alice"]!!).addCallback(object : OnResultListene }) ``` -Use the loadGroup method to load and save group locally. Then, you can use the getGroup method to retrieve group instance from local storage. +Then, you can use the `getGroup` method to retrieve group instance from local storage. + ```kotlin val group = ethree.getGroup(groupId) ``` -#### Encrypt and decrypt messages +### Encrypt and Decrypt Messages + To encrypt and decrypt messages, use the `encrypt` and `decrypt` E3Kit functions, which allows you to work with data and strings. -Use the following code-snippets to encrypt messages: +Use the following code snippets to encrypt messages: + ```kotlin // prepare a message val messageToEncrypt = "Hello, Bob and Carol!" @@ -190,17 +193,21 @@ val messageToEncrypt = "Hello, Bob and Carol!" val encrypted = group.encrypt(messageToEncrypt) ``` -Use the following code-snippets to decrypt messages: +Use the following code snippets to decrypt messages: + ```kotlin val decrypted = group.decrypt(encrypted, findUsersResult["Alice"]!!) ``` -At the decrypt step, you also use `findUsers` method to verify that the message hasn't been tempered with. +At the decrypt step, you should also use `findUsers` method to verify that the message hasn't been tempered with. + +### Manage Group Chat -### Manage group chat -E3Kit also allows you to perform other operations, like participants management, while you work with group chat. In this version of E3Kit only group initiator can change participants or delete group. +E3Kit also allows you to perform other operations, like participants management, while you work with group chat. In current version of E3Kit only the group initiator can change participants or delete group. #### Add new participant -To add a new chat member, the chat owner has to use the `add` method and specify the new member's Card. New member will be able to decrypt all previous messages history. + +To add a new chat member, the chat owner needs to use the `add` method and specify the new member's Card. New member will be able to decrypt all previous messages history. + ```kotlin group.add(users["Den"]!!).addCallback(object : OnCompleteListener { override fun onSuccess() { @@ -214,7 +221,9 @@ group.add(users["Den"]!!).addCallback(object : OnCompleteListener { ``` #### Remove participant -To remove participant, group owner has to use the `remove` method and specify the member's Card. Removed participants won't be able to load or update this group. + +To remove a participant, group owner needs to use the `remove` method and specify the member's Card. Removed participants won't be able to load or update this group: + ```kotlin group.remove(users["Den"]!!).addCallback(object : OnCompleteListener { override fun onSuccess() { @@ -228,7 +237,9 @@ group.remove(users["Den"]!!).addCallback(object : OnCompleteListener { ``` #### Update group chat -In the event of changes in your group, i.e. adding a new participant, or deleting an existing one, each group chat participant has to update the encryption key by calling the `update` E3Kit method or reloading Group by `loadGroup`. + +In case of changes in your group, i.e. adding a new participant, or deleting an existing one, each group chat participant has to update the encryption key by calling the `update` E3Kit method or reloading Group by `loadGroup`: + ```kotlin group.update().addCallback(object : OnCompleteListener { override fun onSuccess() { @@ -242,7 +253,9 @@ group.update().addCallback(object : OnCompleteListener { ``` #### Delete group chat -To delete a group, the owner has to use the `deleteGroup` method and specify the group `identifier`. + +To delete a group, the owner needs to use the `deleteGroup` method and specify the group `identifier`: + ```kotlin ethree.deleteGroup(groupId).addCallback(object : OnCompleteListener { override fun onSuccess() { From 7f3cdad2e21d2a1b8b39553740b5d58d1ab9fe50 Mon Sep 17 00:00:00 2001 From: Mariia Malitska <37335222+MariiaMalitska@users.noreply.github.com> Date: Mon, 30 Sep 2019 17:37:21 +0300 Subject: [PATCH 12/13] [ci skip] Minor readme fix --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f3a879bd..b379a010 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Virgil E3Kit Android SDK +# Virgil E3Kit Android [![Build Status](https://travis-ci.com/VirgilSecurity/virgil-e3kit-kotlin.svg?branch=master)](https://travis-ci.com/VirgilSecurity/virgil-e3kit-kotlin) [![GitHub license](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://github.com/VirgilSecurity/virgil/blob/master/LICENSE) @@ -7,7 +7,7 @@ ## Introduction - [Virgil Security](https://virgilsecurity.com) provides the E3Kit which simplifies work with Virgil Cloud and presents an easy-to-use API for adding a security layer to any application. In a few simple steps you can add end-to-end encryption with multidevice and group chats support. + [Virgil Security](https://virgilsecurity.com) provides the E3Kit framework which simplifies work with Virgil Cloud and presents an easy-to-use API for adding a security layer to any application. In a few simple steps you can add end-to-end encryption with multidevice and group chats support. The E3Kit allows developers to get up and running with Virgil API quickly and add full end-to-end security to their existing digital solutions to become HIPAA and GDPR compliant and more. From ea0bdaf1d65640447d493ff09085f75944027058 Mon Sep 17 00:00:00 2001 From: BuddahLD Date: Mon, 30 Sep 2019 17:50:18 +0300 Subject: [PATCH 13/13] Fixed `getGroup` visibility modifier --- ethree-common/build.gradle | 2 +- .../main/java/com/virgilsecurity/android/common/EThreeCore.kt | 2 +- ethree-enclave/build.gradle | 2 +- ethree-kotlin/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ethree-common/build.gradle b/ethree-common/build.gradle index 2133d920..c45fa658 100644 --- a/ethree-common/build.gradle +++ b/ethree-common/build.gradle @@ -45,7 +45,7 @@ apply plugin: 'digital.wup.android-maven-publish' apply plugin: 'org.jetbrains.dokka' group 'com.virgilsecurity' -version '0.5.0-beta1' +version '0.5.1-beta1' def APP_ID = hasProperty('APP_ID') ? APP_ID : System.getenv('APP_ID') def API_PRIVATE_KEY = hasProperty('API_PRIVATE_KEY') ? API_PRIVATE_KEY : System.getenv('API_PRIVATE_KEY') diff --git a/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt b/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt index 43da2a39..1b96b253 100644 --- a/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt +++ b/ethree-common/src/main/java/com/virgilsecurity/android/common/EThreeCore.kt @@ -442,7 +442,7 @@ constructor(val identity: String, * * @return [Group] if exists, null otherwise. */ - internal fun getGroup(identifier: String): Group? = groupWorker.getGroup(identifier) + fun getGroup(identifier: String): Group? = groupWorker.getGroup(identifier) /** * Loads group from cloud, saves locally. diff --git a/ethree-enclave/build.gradle b/ethree-enclave/build.gradle index f400281d..caab48cf 100644 --- a/ethree-enclave/build.gradle +++ b/ethree-enclave/build.gradle @@ -65,7 +65,7 @@ android { } group 'com.virgilsecurity' -version '0.6.0-beta1' +version '0.6.1-beta1' dependencies { // Inner dependencies diff --git a/ethree-kotlin/build.gradle b/ethree-kotlin/build.gradle index d03991de..d7c07671 100644 --- a/ethree-kotlin/build.gradle +++ b/ethree-kotlin/build.gradle @@ -65,7 +65,7 @@ android { } group 'com.virgilsecurity' -version '0.6.0-beta1' +version '0.6.1-beta1' dependencies { // Inner dependencies