diff --git a/.idea/gradle.xml b/.idea/gradle.xml index dedc124465b8022d9d3cf9008e20fe523a2d62ff..3b5efa05b8bd283b87c1670577d6e307124cad83 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -7,6 +7,7 @@ <option name="testRunner" value="GRADLE" /> <option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="gradleJvm" value="11" /> <option name="modules"> <set> <option value="$PROJECT_DIR$" /> diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2c2c251fdbbfef52f9769357417bcc468e0f3d6c..f743aa5b102586b6b100fde7f485ca1c59fa940d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,6 +33,9 @@ android { } dependencies { + implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs"))) + implementation((project(":cmix"))) + api(platform(project(":depconstraints"))) kapt(platform(project(":depconstraints"))) androidTestApi(platform(project(":depconstraints"))) @@ -43,7 +46,7 @@ dependencies { testImplementation(Libs.JUNIT) testImplementation(Libs.TRUTH) - + androidTestImplementation(Libs.ESPRESSO_CORE) androidTestImplementation(Libs.EXT_JUNIT) } \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/DappModule.kt b/app/src/main/java/io/elixxir/dapp/DappModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..392a4e138c8801fd4a8fbcf560c172b3b14cbd24 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/DappModule.kt @@ -0,0 +1,43 @@ +package io.elixxir.dapp + +import io.elixxir.dapp.android.model.AndroidConfig +import io.elixxir.dapp.api.model.* +import io.elixxir.dapp.logger.data.Logger +import io.elixxir.dapp.api.model.CommonProperties +import io.elixxir.dapp.api.model.DappConfig + +/** + * Singleton entry point to modules provided by the dApp SDK. + */ +class DappModule private constructor( + config: DappConfig, + logger: Logger = Logger.newInstance(config.loggerConfig) +) : DappApi, + AndroidConfig by config.androidConfig, + Logger by logger, + CommonProperties +{ + override val accountApi: AccountApi + get() = TODO("Not yet implemented") + override val directoryApi: DirectoryApi + get() = TODO("Not yet implemented") + override val messagesApi: MessagesApi + get() = TODO("Not yet implemented") + override val networkApi: NetworkApi + get() = TODO("Not yet implemented") + override val requestsApi: RequestsApi + get() = TODO("Not yet implemented") + override val groupsApi: GroupsApi + get() = TODO("Not yet implemented") + + companion object { + @Volatile + private var instance: DappModule? = null + + fun getInstance(config: DappConfig): DappModule { + return instance ?: DappModule(config).apply { + instance = this + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/android/data/DefaultAndroidConfig.kt b/app/src/main/java/io/elixxir/dapp/android/data/DefaultAndroidConfig.kt new file mode 100644 index 0000000000000000000000000000000000000000..62a36d55c9c6ac48014a0bb470a8dc78b530ac2b --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/android/data/DefaultAndroidConfig.kt @@ -0,0 +1,11 @@ +package io.elixxir.dapp.android.data + +import android.content.Context +import io.elixxir.dapp.android.model.AndroidConfig +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +data class DefaultAndroidConfig( + override val context: () -> Context, + override val dispatcher: CoroutineDispatcher = Dispatchers.IO, +) : AndroidConfig \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/android/model/AndroidConfig.kt b/app/src/main/java/io/elixxir/dapp/android/model/AndroidConfig.kt new file mode 100644 index 0000000000000000000000000000000000000000..e31adddda82f68e0d92abac2931cf8587ff907f4 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/android/model/AndroidConfig.kt @@ -0,0 +1,12 @@ +package io.elixxir.dapp.android.model + +import android.content.Context +import kotlinx.coroutines.CoroutineDispatcher + +/** + * Android-specific properties used at runtime. + */ +interface AndroidConfig { + val context: () -> Context + val dispatcher: CoroutineDispatcher +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/data/AccountModule.kt b/app/src/main/java/io/elixxir/dapp/api/data/AccountModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..b6e0c2ada41f78220bb78f7fe29ea30cf5e3acd0 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/data/AccountModule.kt @@ -0,0 +1,42 @@ +package io.elixxir.dapp.api.data + +import io.elixxir.dapp.api.model.AccountApi +import io.elixxir.dapp.backup.model.Backup +import io.elixxir.dapp.session.repository.SessionDataSource +import io.elixxir.dapp.user.model.User +import io.elixxir.dapp.user.model.UserUpdateData +import kotlinx.coroutines.* + +internal class AccountModule( + private val sessionManager: SessionDataSource +) : AccountApi { + private val scope: CoroutineScope = CoroutineScope( + CoroutineName("AccountModule") + + Job() + + Dispatchers.Default + ) + + override fun getCurrentUser(): User { + TODO("Not yet implemented") + } + + override fun createAccount(username: String) { + scope.launch { + sessionManager.createSession() + } + } + + override fun restoreAccount(backup: Backup) { + TODO("Not yet implemented") + } + + override fun updateAccount(updateData: UserUpdateData) { + TODO("Not yet implemented") + } + + override fun deleteAccount() { + scope.launch { + sessionManager.deleteSession() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/data/NetworkModule.kt b/app/src/main/java/io/elixxir/dapp/api/data/NetworkModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..7a0e356035c5ffe383f84e43874074fc274b17f3 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/data/NetworkModule.kt @@ -0,0 +1,30 @@ +package io.elixxir.dapp.api.data + +import io.elixxir.dapp.api.model.NetworkApi +import io.elixxir.dapp.network.model.ConnectionStatus +import io.elixxir.dapp.network.data.NetworkManager +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow + +internal class NetworkModule( + private val networkManager: NetworkManager +) : NetworkApi { + private val scope: CoroutineScope = CoroutineScope( + CoroutineName("AccountModule") + + Job() + + Dispatchers.Default + ) + + override val connectionStatus: Flow<ConnectionStatus> by networkManager::connectionStatus + + override fun connect(): Result<ConnectionStatus> { + TODO("Not yet implemented") + } + + override fun disconnect(): Result<ConnectionStatus> { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/model/AccountApi.kt b/app/src/main/java/io/elixxir/dapp/api/model/AccountApi.kt new file mode 100644 index 0000000000000000000000000000000000000000..8b231f83ef9ddeb4aefe7a63f7b9266d938f93e2 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/model/AccountApi.kt @@ -0,0 +1,13 @@ +package io.elixxir.dapp.api.model + +import io.elixxir.dapp.backup.model.Backup +import io.elixxir.dapp.user.model.User +import io.elixxir.dapp.user.model.UserUpdateData + +interface AccountApi { + fun getCurrentUser(): User + fun createAccount(username: String) + fun restoreAccount(backup: Backup) + fun updateAccount(updateData: UserUpdateData) + fun deleteAccount() +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/model/CommonProperties.kt b/app/src/main/java/io/elixxir/dapp/api/model/CommonProperties.kt new file mode 100644 index 0000000000000000000000000000000000000000..ced9abdb6078976c3d4b926ced42177f47fbb6a4 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/model/CommonProperties.kt @@ -0,0 +1,6 @@ +package io.elixxir.dapp.api.model + +import io.elixxir.dapp.android.model.AndroidConfig +import io.elixxir.dapp.logger.data.Logger + +internal interface CommonProperties : Logger, AndroidConfig \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/model/DappApi.kt b/app/src/main/java/io/elixxir/dapp/api/model/DappApi.kt new file mode 100644 index 0000000000000000000000000000000000000000..e6998cad67d8104c89cf580211247446c419ebc8 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/model/DappApi.kt @@ -0,0 +1,10 @@ +package io.elixxir.dapp.api.model + +interface DappApi { + val accountApi: AccountApi + val directoryApi: DirectoryApi + val messagesApi: MessagesApi + val networkApi: NetworkApi + val requestsApi: RequestsApi + val groupsApi: GroupsApi +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/model/DappConfig.kt b/app/src/main/java/io/elixxir/dapp/api/model/DappConfig.kt new file mode 100644 index 0000000000000000000000000000000000000000..6b56f69920fb0acda678828b3e76e654f6004b46 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/model/DappConfig.kt @@ -0,0 +1,15 @@ +package io.elixxir.dapp.api.model + +import io.elixxir.dapp.android.model.AndroidConfig +import io.elixxir.dapp.logger.model.LoggerConfig +import io.elixxir.dapp.network.model.NetworkConfig + +/** + * Describes configurable options, and satisfies dependencies for, + * modules exposed by the dApps Kotlin SDK. + */ +interface DappConfig { + val androidConfig: AndroidConfig + val loggerConfig: LoggerConfig + val networkConfig: NetworkConfig +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/model/DirectoryApi.kt b/app/src/main/java/io/elixxir/dapp/api/model/DirectoryApi.kt new file mode 100644 index 0000000000000000000000000000000000000000..6ff493985951db5c1b246c0e1d6ecdad4e079dcf --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/model/DirectoryApi.kt @@ -0,0 +1,10 @@ +package io.elixxir.dapp.api.model + +import io.elixxir.dapp.user.model.User +import io.elixxir.dapp.user.model.UserQuery + +interface DirectoryApi { + fun getContacts(): List<User> + fun findUser(params: UserQuery): User? + fun blockUser(user: User) +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/model/GroupsApi.kt b/app/src/main/java/io/elixxir/dapp/api/model/GroupsApi.kt new file mode 100644 index 0000000000000000000000000000000000000000..8cbccf2ab4afc5fa70f24307cbfdeef64ef5a5a7 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/model/GroupsApi.kt @@ -0,0 +1,16 @@ +package io.elixxir.dapp.api.model + +import io.elixxir.dapp.group.model.Group +import io.elixxir.dapp.messaging.model.Message +import io.elixxir.dapp.user.model.User +import kotlinx.coroutines.flow.Flow + +interface GroupsApi { + val groupMessages: Flow<Message> + fun sendInvitation(group: Group) + fun resendInvitation(member: User) + fun joinGroup(group: Group) + fun leaveGroup(group: Group) + fun sendMessage(message: Message, group: Group) + fun retryMessage(message: Message, group: Group) +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/model/MessagesApi.kt b/app/src/main/java/io/elixxir/dapp/api/model/MessagesApi.kt new file mode 100644 index 0000000000000000000000000000000000000000..0e7c76b6c46aefc635dc87eac9cbdcb462296cf3 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/model/MessagesApi.kt @@ -0,0 +1,11 @@ +package io.elixxir.dapp.api.model + +import io.elixxir.dapp.messaging.model.Message +import io.elixxir.dapp.user.model.User +import kotlinx.coroutines.flow.Flow + +interface MessagesApi { + val messages: Flow<Message> + fun sendMessage(message: Message, recipient: User) + fun retryMessage(message: Message, recipient: User) +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/model/NetworkApi.kt b/app/src/main/java/io/elixxir/dapp/api/model/NetworkApi.kt new file mode 100644 index 0000000000000000000000000000000000000000..7313681e02fa1b707a261648a85efbe5ff1545e2 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/model/NetworkApi.kt @@ -0,0 +1,10 @@ +package io.elixxir.dapp.api.model + +import io.elixxir.dapp.network.model.ConnectionStatus +import kotlinx.coroutines.flow.Flow + +interface NetworkApi { + val connectionStatus: Flow<ConnectionStatus> + fun connect(): Result<ConnectionStatus> + fun disconnect(): Result<ConnectionStatus> +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/model/RequestsApi.kt b/app/src/main/java/io/elixxir/dapp/api/model/RequestsApi.kt new file mode 100644 index 0000000000000000000000000000000000000000..154bc8b86fe01b0ca7031e88ccb3224094b9ad47 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/model/RequestsApi.kt @@ -0,0 +1,13 @@ +package io.elixxir.dapp.api.model + +import io.elixxir.dapp.request.model.IncomingRequest +import io.elixxir.dapp.request.model.Request +import io.elixxir.dapp.request.model.OutgoingRequest +import kotlinx.coroutines.flow.Flow + +interface RequestsApi { + val requests: Flow<Request> + fun sendRequest(request: OutgoingRequest) + fun acceptRequest(request: IncomingRequest) + fun retryRequest(request: Request) +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/api/model/RetryStrategy.kt b/app/src/main/java/io/elixxir/dapp/api/model/RetryStrategy.kt new file mode 100644 index 0000000000000000000000000000000000000000..91fa23967c74f6af58bf657c8aa01ba5257d03f4 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/api/model/RetryStrategy.kt @@ -0,0 +1,16 @@ +package io.elixxir.dapp.api.model + +interface RetryStrategy { + val maxRetries: Int + val retryDelayMs: Long +} + +/** + * Remote data that is critical to the function of the SDK. + * Generally means many retries and reasonable delay to not + * overwhelm network resources. + */ +data class CriticalRemoteDataStrategy( + override val maxRetries: Int = 29, + override val retryDelayMs: Long = 1000L +) : RetryStrategy \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/backup/model/Backup.kt b/app/src/main/java/io/elixxir/dapp/backup/model/Backup.kt new file mode 100644 index 0000000000000000000000000000000000000000..72bd8329420d7c2489fd5324f58cf9d1e776eb48 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/backup/model/Backup.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.backup.model + +interface Backup { +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/AuthEventListener.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/AuthEventListener.kt new file mode 100644 index 0000000000000000000000000000000000000000..4eca68a8c98c453bff18b1d3f8a00209e5f1d5a2 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/AuthEventListener.kt @@ -0,0 +1,14 @@ +package io.elixxir.dapp.bindings.data + +import bindings.AuthCallbacks + +interface AuthEventListener { + fun onConfirm(var1: ByteArray?, var2: ByteArray?, var3: Long, var5: Long) + + fun onRequest(var1: ByteArray?, var2: ByteArray?, var3: Long, var5: Long) + + fun onReset(var1: ByteArray?, var2: ByteArray?, var3: Long, var5: Long) +} + +@JvmInline +internal value class AuthCallbacksAdapter(val value: AuthCallbacks) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/Backup.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/Backup.kt new file mode 100644 index 0000000000000000000000000000000000000000..547cebe85f6d67b8b96a3e2a9e0e1d9529b474d1 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/Backup.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.data + +internal interface Backup { +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/BackupAdapter.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/BackupAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..2a3af5d63808070f60609eeb22f604cdfa429401 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/BackupAdapter.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.data + +@JvmInline +internal value class BackupAdapter(val backup: bindings.Backup) : Backup \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/Bindings.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/Bindings.kt new file mode 100644 index 0000000000000000000000000000000000000000..6083e320e48cde159bdaeae4d771a6ed8e98c677 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/Bindings.kt @@ -0,0 +1,68 @@ +package io.elixxir.dapp.bindings.data + +import io.elixxir.dapp.bindings.model.* +import io.elixxir.dapp.bindings.model.E2eParams +import io.elixxir.dapp.logger.model.LoggerConfig +import io.elixxir.dapp.network.repository.Ndf +import io.elixxir.dapp.session.model.SessionPassword + +internal interface Bindings { + val defaultE2eParams: E2eParams + val defaultCmixParams: CmixParams + val defaultFileTransferParams: FileTransferParams + val gitVersion: String + + fun generateSecret(byteLength: Long): SessionPassword + + fun loadCmix( + sessionFileDirectory: String, + sessionPassword: SessionPassword, + cmixParams: CmixParams + ): Cmix + + fun login( + cmixId: CmixId, + authCallbacks: AuthCallbacksAdapter, + receptionIdentity: ReceptionIdentity, + e2eParams: E2eParams + ): E2e + + fun getOrCreateUd( + e2eId: E2eId, + networkFollowerStatus: NetworkFollowerStatus, + username: String, + signature: RegistrationValidationSignature, + udCert: UdCertificate, + contact: Contact, + udIpAddress: UdIpAddress + ): UserDiscovery + + fun newUdFromBackup( + e2eId: E2eId, + networkFollowerStatus: NetworkFollowerStatus, + emailFact: Fact, + phoneFact: Fact, + udCert: UdCertificate, + contact: Contact, + udAddress: UdIpAddress + ): UserDiscovery + + fun newDummyTrafficManager(): DummyTrafficManager + + fun registerLogger(loggerConfig: LoggerConfig) + + fun initializeBackup(): Backup + + fun resumeBackup(): Backup + + fun fetchSignedNdf( + url: String, + cert: String + ): Ndf + + fun getReceptionIdentity( + key: SessionPassword, + cmixId: CmixId + ): ReceptionIdentity +} + diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/BindingsAdapter.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/BindingsAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..3cdfe670515414b94a50af68e78a97be7cd879b7 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/BindingsAdapter.kt @@ -0,0 +1,140 @@ +package io.elixxir.dapp.bindings.data + +import io.elixxir.dapp.bindings.model.* +import io.elixxir.dapp.bindings.model.E2eParams +import io.elixxir.dapp.bindings.model.ReceptionIdentity +import io.elixxir.dapp.logger.model.LoggerConfig +import io.elixxir.dapp.network.repository.Ndf +import io.elixxir.dapp.session.model.SessionPassword +import io.elixxir.dapp.util.toBase64String +import bindings.Bindings as CoreBindings + +internal class BindingsAdapter: Bindings { + override val defaultE2eParams: E2eParams + get() = E2eParams(CoreBindings.getDefaultE2EParams()) + override val defaultCmixParams: CmixParams + get() = CmixParams(CoreBindings.getDefaultCMixParams()) + override val defaultFileTransferParams: FileTransferParams + get() = FileTransferParams(CoreBindings.getDefaultFileTransferParams()) + override val gitVersion: String + get() = CoreBindings.getGitVersion() + + override fun generateSecret(byteLength: Long): SessionPassword { + return SessionPassword(CoreBindings.generateSecret(byteLength)) + } + + override fun loadCmix( + sessionFileDirectory: String, + sessionPassword: SessionPassword, + cmixParams: CmixParams + ): Cmix { + return CmixAdapter( + CoreBindings.loadCmix( + sessionFileDirectory, + sessionPassword.value, + cmixParams.value + ) + ) + } + + override fun login( + cmixId: CmixId, + authCallbacks: AuthCallbacksAdapter, + receptionIdentity: ReceptionIdentity, + e2eParams: E2eParams + ): E2e { + // TODO: Use factory method to get E2E implementation + return E2eAdapter( + CoreBindings.login( + cmixId.value, + authCallbacks.value, + receptionIdentity.value, + e2eParams.value + ) + ) + } + + override fun getOrCreateUd( + e2eId: E2eId, + networkFollowerStatus: NetworkFollowerStatus, + username: String, + signature: RegistrationValidationSignature, + udCert: UdCertificate, + contact: Contact, + udIpAddress: UdIpAddress + ): UserDiscovery { + // TODO: Use factory method to get UD implementation + return UserDiscoveryAdapter( + CoreBindings.newOrLoadUd( + e2eId.value, + { networkFollowerStatus.code }, + username, + signature.value, + udCert.value, + contact.value, + udIpAddress.value + ) + ) + } + + override fun newUdFromBackup( + e2eId: E2eId, + networkFollowerStatus: NetworkFollowerStatus, + emailFact: Fact, + phoneFact: Fact, + udCert: UdCertificate, + contact: Contact, + udIpAddress: UdIpAddress + ): UserDiscovery { + // TODO: Use factory method to get UD implementation + return UserDiscoveryAdapter( + CoreBindings.newUdManagerFromBackup( + e2eId.value, + { networkFollowerStatus.code }, + emailFact.value, + phoneFact.value, + udCert.value, + contact.value, + udIpAddress.value + ) + ) + } + + override fun newDummyTrafficManager(): DummyTrafficManager { + TODO("Not yet implemented") + } + + override fun registerLogger(loggerConfig: LoggerConfig) { + with (loggerConfig) { + CoreBindings.logLevel(logLevel.code) + CoreBindings.registerLogWriter(logWriter) + } + } + + override fun initializeBackup(): Backup { + TODO("Not yet implemented") + } + + override fun resumeBackup(): Backup { + TODO("Not yet implemented") + } + + override fun fetchSignedNdf( + url: String, + cert: String + ): Ndf { + return CoreBindings.downloadAndVerifySignedNdfWithUrl(url, cert).toBase64String() + } + + override fun getReceptionIdentity( + key: SessionPassword, + cmixId: CmixId + ): ReceptionIdentity { + return ReceptionIdentity( + CoreBindings.loadReceptionIdentity( + key.value.toBase64String(), + cmixId.value + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/Cmix.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/Cmix.kt new file mode 100644 index 0000000000000000000000000000000000000000..ef91c1f5b20cf25aeb691f2a3826d611cb664be9 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/Cmix.kt @@ -0,0 +1,31 @@ +package io.elixxir.dapp.bindings.data + +import io.elixxir.dapp.bindings.model.* +import io.elixxir.dapp.bindings.model.Contact +import io.elixxir.dapp.bindings.model.E2eId +import io.elixxir.dapp.bindings.model.NetworkFollowerStatus + +internal interface Cmix { + fun connect( + e2eId: E2eId, + recipientContact: Contact, + e2eParams: E2eParams + ): Connection + + fun startNetworkFollower(timeoutMs: Long) + + fun stopNetworkFollower() + + fun isNetworkHealthy(): Boolean + + fun registerHealthListener(listener: NetworkHealthListener): HealthListenerId + + fun unregisterHealthListener(id: HealthListenerId) + + fun getNodeRegistrationStatus(): NodeRegistrationStatus + + fun makeReceptionIdentity(): ReceptionIdentity + + fun getNetworkFollowerStatus(): NetworkFollowerStatus +} + diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/CmixAdapter.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/CmixAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..7c7203b420eee201f6b19c9062adf296b788f253 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/CmixAdapter.kt @@ -0,0 +1,62 @@ +package io.elixxir.dapp.bindings.data + +import io.elixxir.dapp.bindings.model.* +import io.elixxir.dapp.bindings.model.Connection +import io.elixxir.dapp.bindings.model.Contact +import io.elixxir.dapp.bindings.model.E2eId +import io.elixxir.dapp.bindings.model.E2eParams +import io.elixxir.dapp.bindings.model.NetworkFollowerStatus +import bindings.Cmix as CoreCmix + +@JvmInline +internal value class CmixAdapter(private val cmix: CoreCmix) : Cmix { + + override fun connect( + e2eId: E2eId, + recipientContact: Contact, + e2eParams: E2eParams + ): Connection { + // TODO: Use factory method to get Connection implementation + return ConnectionAdapter( + cmix.connect( + e2eId.value, + recipientContact.value, + e2eParams.value + ) + ) + } + + override fun startNetworkFollower(timeoutMs: Long) { + cmix.startNetworkFollower(timeoutMs) + } + + override fun stopNetworkFollower() { + cmix.stopNetworkFollower() + } + + override fun isNetworkHealthy(): Boolean { + return cmix.isHealthy + } + + override fun registerHealthListener(listener: NetworkHealthListener): HealthListenerId { + return HealthListenerId( + cmix.addHealthCallback(HealthCallbackAdapter.placeholder) + ) + } + + override fun unregisterHealthListener(id: HealthListenerId) { + cmix.removeHealthCallback(id.value) + } + + override fun getNodeRegistrationStatus(): NodeRegistrationStatus { + return NodeRegistrationStatus(cmix.nodeRegistrationStatus) + } + + override fun makeReceptionIdentity(): ReceptionIdentity { + return ReceptionIdentity(cmix.makeReceptionIdentity()) + } + + override fun getNetworkFollowerStatus(): NetworkFollowerStatus { + return NetworkFollowerStatus.from(cmix.networkFollowerStatus()) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/DummyTrafficManager.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/DummyTrafficManager.kt new file mode 100644 index 0000000000000000000000000000000000000000..4939e23eafb11a2ecfdbc0cef9f848f02b735392 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/DummyTrafficManager.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.data + +interface DummyTrafficManager { +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/E2e.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/E2e.kt new file mode 100644 index 0000000000000000000000000000000000000000..ce271d3af252c4e1e45d76a9b8edae8c2d85ceed --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/E2e.kt @@ -0,0 +1,49 @@ +package io.elixxir.dapp.bindings.data + +import io.elixxir.dapp.bindings.model.* +import io.elixxir.dapp.bindings.model.E2eParams +import io.elixxir.dapp.bindings.model.ReceptionIdentity +import io.elixxir.dapp.user.model.UserId + +internal interface E2e { + val payloadSize: Long + + fun getReceptionId(): ReceptionIdentity + fun getContact(): Contact + fun callAllReceivedRequests() + fun confirmRequest(userId: UserId) + fun deleteAllRequests() + fun deleteRequest(request: Request) + fun deleteSentRequests() + + fun getAllPartnerIds(): PartnersList + + fun hasAuthenticatedChannel(contact: Contact): Boolean + + fun registerListener( + senderId: UserId, + messageType: MessageType, + e2eListener: E2eListener + ) + + fun reset(contact: Contact): RoundId + + fun sendRequest( + contact: Contact, + myFactsList: FactsList + ): RoundId + + fun verifyOwnership( + receivedUserId: UserId, + verifiedContact: Contact, + e2eHandler: E2eHandler + ): Boolean + + fun sendE2e( + messageType: MessageType, + receiverId: UserId, + payload: Payload, + params: E2eParams + ): SendReport +} + diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/E2eAdapter.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/E2eAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..588f8e95c68c54335da1ea389459fa8cab67f9e6 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/E2eAdapter.kt @@ -0,0 +1,104 @@ +package io.elixxir.dapp.bindings.data + +import io.elixxir.dapp.bindings.model.* +import io.elixxir.dapp.bindings.model.E2eParams +import io.elixxir.dapp.bindings.model.ReceptionIdentity +import bindings.E2e as CoreE2e +import io.elixxir.dapp.user.model.UserId + +@JvmInline +internal value class E2eAdapter(private val e2e: CoreE2e) : E2e { + override val payloadSize: Long + get() = e2e.payloadSize() + + override fun getReceptionId(): ReceptionIdentity { + return ReceptionIdentity(e2e.receptionID) + } + + override fun getContact(): Contact { + return Contact(e2e.contact) + } + + override fun callAllReceivedRequests() { + e2e.callAllReceivedRequests() + } + + override fun confirmRequest(userId: UserId) { + e2e.confirm(userId.value) + } + + override fun deleteAllRequests() { + e2e.deleteAllRequests() + } + + override fun deleteRequest(request: Request) { + e2e.deleteRequest(request.value) + } + + override fun deleteSentRequests() { + e2e.deleteSentRequests() + } + + override fun getAllPartnerIds(): PartnersList { + return PartnersList(e2e.allPartnerIDs) + } + + override fun hasAuthenticatedChannel(contact: Contact): Boolean { + return e2e.hasAuthenticatedChannel(contact.value) + } + + override fun registerListener( + senderId: UserId, + messageType: MessageType, + e2eListener: E2eListener + ) { + e2e.registerListener( + senderId.value, + messageType.code, + e2eListener + ) + } + + override fun reset(contact: Contact): RoundId { + return RoundId( + e2e.reset(contact.value) + ) + } + + override fun sendRequest( + contact: Contact, + myFactsList: FactsList + ): RoundId { + return RoundId( + e2e.request(contact.value, myFactsList.value) + ) + } + + override fun verifyOwnership( + receivedUserId: UserId, + verifiedContact: Contact, + e2eHandler: E2eHandler + ): Boolean { + return e2e.verifyOwnership( + receivedUserId.value, + verifiedContact.value, + e2eHandler.value + ) + } + + override fun sendE2e( + messageType: MessageType, + receiverId: UserId, + payload: Payload, + params: E2eParams + ): SendReport { + return SendReport( + e2e.sendE2E( + messageType.code, + receiverId.value, + payload.value, + params.value + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/GroupChat.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/GroupChat.kt new file mode 100644 index 0000000000000000000000000000000000000000..ff2d4d85dc13963b169eca4d310649f2f3daa343 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/GroupChat.kt @@ -0,0 +1,14 @@ +package io.elixxir.dapp.bindings.data + +import io.elixxir.dapp.bindings.model.GroupInfo +import io.elixxir.dapp.bindings.model.GroupReport +import io.elixxir.dapp.group.model.Group +import io.elixxir.dapp.group.model.GroupId + +internal interface GroupChat { + fun getGroup(groupId: GroupId): Group + fun joinGroup(trackedGroupId: Long) + fun leaveGroup(groupId: GroupId) + fun makeGroup(info: GroupInfo): GroupReport + fun resendInvitations(group: Group): GroupReport +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/GroupChatAdapter.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/GroupChatAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..277bc79934ddb01b67f6d5fcafd91a4b67483845 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/GroupChatAdapter.kt @@ -0,0 +1,37 @@ +package io.elixxir.dapp.bindings.data + +import io.elixxir.dapp.bindings.model.GroupAdapter +import io.elixxir.dapp.bindings.model.GroupInfo +import io.elixxir.dapp.bindings.model.GroupReport +import io.elixxir.dapp.group.model.Group +import io.elixxir.dapp.group.model.GroupId +import bindings.GroupChat as CoreGroupChat + +internal class GroupChatAdapter(private val groupChat: CoreGroupChat) : GroupChat { + override fun getGroup(groupId: GroupId): Group { + return GroupAdapter(groupChat.getGroup(groupId.value)) + } + + override fun joinGroup(trackedGroupId: Long) { + groupChat.joinGroup(trackedGroupId) + } + + override fun leaveGroup(groupId: GroupId) { + groupChat.leaveGroup(groupId.value) + } + + override fun makeGroup(info: GroupInfo): GroupReport { + return GroupReport( + groupChat.makeGroup( + info.membershipData.value, + info.description.toByteArray(), + info.name.toByteArray()) + ) + } + + override fun resendInvitations(group: Group): GroupReport { + return GroupReport( + groupChat.resendRequest(group.groupId.value) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/UdListener.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/UdListener.kt new file mode 100644 index 0000000000000000000000000000000000000000..5a5132de556ea0fca521f2d32b9921922e29a81d --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/UdListener.kt @@ -0,0 +1,35 @@ +package io.elixxir.dapp.bindings.data + +import io.elixxir.dapp.bindings.model.Contact + +internal interface UdListener { + val onComplete: (Contact?, Exception?) -> Unit +} + +internal class UdSearchResultListener( + private val listener: UdListener +) : bindings.UdSearchCallback, + bindings.UdLookupCallback, + UdListener by listener +{ + + override fun callback( + contactData: ByteArray?, + error: java.lang.Exception? + ) { + onComplete( + Contact(contactData ?: byteArrayOf()), + error + ) + } + + companion object { + val placeholder: UdSearchResultListener = + UdSearchResultListener( + object : UdListener { + override val onComplete: (Contact?, Exception?) -> Unit + = { _, _, -> } + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/UserDiscovery.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/UserDiscovery.kt new file mode 100644 index 0000000000000000000000000000000000000000..124a9bb25130f0e64f3d1bc8cc1f5a60a85a5138 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/UserDiscovery.kt @@ -0,0 +1,38 @@ +package io.elixxir.dapp.bindings.data + +import io.elixxir.dapp.bindings.model.ConfirmationCode +import io.elixxir.dapp.bindings.model.ConfirmationId +import io.elixxir.dapp.bindings.model.Contact +import io.elixxir.dapp.bindings.model.FactsList +import io.elixxir.dapp.user.model.UserId + +internal interface UserDiscovery { + fun getContact(): Contact + + fun getFacts(): FactsList + + fun registerUsername(username: String) + + fun registerEmail(email: String): ConfirmationId + + fun registerPhone(phone: String): ConfirmationId + + fun confirmTwoFactorAuth( + confirmationId: ConfirmationId, + confirmationCode: ConfirmationCode + ) + + fun removeEmail() + + fun removePhone() + + fun deleteUser() + + fun findUserById(userId: UserId): Result<Contact> + + fun usernameSearch(username: String): Result<Contact> + + fun phoneSearch(phone: String): Result<Contact> + + fun emailSearch(email: String): Result<Contact> +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/data/UserDiscoveryAdapter.kt b/app/src/main/java/io/elixxir/dapp/bindings/data/UserDiscoveryAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..9b1161b1a18f3c94af299ef92649e38d98006382 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/data/UserDiscoveryAdapter.kt @@ -0,0 +1,103 @@ +package io.elixxir.dapp.bindings.data + +import bindings.Bindings +import io.elixxir.dapp.bindings.model.* +import io.elixxir.dapp.bindings.model.Contact +import io.elixxir.dapp.bindings.model.Fact +import io.elixxir.dapp.bindings.model.FactsList +import io.elixxir.dapp.user.model.UserId +import bindings.UserDiscovery as CoreUserDiscovery + +@JvmInline +internal value class UserDiscoveryAdapter(private val ud: CoreUserDiscovery) : UserDiscovery { + + override fun getContact(): Contact { + return Contact(ud.contact) + } + + override fun getFacts(): FactsList { + return FactsList(ud.facts) + } + + override fun registerUsername(username: String) { + TODO("Not yet implemented") + } + + override fun registerEmail(email: String): ConfirmationId{ + return ConfirmationId( + ud.sendRegisterFact(Fact.placeholder) + ) + } + + override fun registerPhone(phone: String): ConfirmationId { + return ConfirmationId( + ud.sendRegisterFact(Fact.placeholder) + ) + } + + override fun confirmTwoFactorAuth( + confirmationId: ConfirmationId, + confirmationCode: ConfirmationCode + ) { + ud.confirmFact( + confirmationId.value, + confirmationCode.value + ) + } + + override fun removeEmail() { + ud.removeFact(Fact.placeholder) + } + + override fun removePhone() { + ud.removeFact(Fact.placeholder) + } + + override fun deleteUser() { + ud.permanentDeleteAccount(ud.contact) + } + + override fun findUserById(userId: UserId): Result<Contact> { + Bindings.lookupUD( + E2eId.placeholder, + userId.value, + UdSearchResultListener.placeholder, + LookupId.placeholder, + SearchParams.placeholder + ) + return Result.success(Contact(byteArrayOf())) + } + + override fun usernameSearch(username: String): Result<Contact> { + Bindings.searchUD( + E2eId.placeholder, + Contact.placeholder, + UdSearchResultListener.placeholder, + FactsList.placeholder, + SearchParams.placeholder, + ) + return Result.success(Contact(byteArrayOf())) + } + + override fun phoneSearch(phone: String): Result<Contact> { + Bindings.searchUD( + E2eId.placeholder, + Contact.placeholder, + UdSearchResultListener.placeholder, + FactsList.placeholder, + SearchParams.placeholder, + ) + return Result.success(Contact(byteArrayOf())) + } + + override fun emailSearch(email: String): Result<Contact> { + Bindings.searchUD( + E2eId.placeholder, + Contact.placeholder, + UdSearchResultListener.placeholder, + FactsList.placeholder, + SearchParams.placeholder, + ) + return Result.success(Contact(byteArrayOf())) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/AuthenticatedConnection.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/AuthenticatedConnection.kt new file mode 100644 index 0000000000000000000000000000000000000000..72b5c6ae5e9791c035fa9e11b4260836416aa066 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/AuthenticatedConnection.kt @@ -0,0 +1,5 @@ +package io.elixxir.dapp.bindings.model + +internal interface AuthenticatedConnection : Connection { + fun isAuthenticated() +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/CmixId.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/CmixId.kt new file mode 100644 index 0000000000000000000000000000000000000000..4978a8f2da2e36bf6d08590829bed89fd01248a0 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/CmixId.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +value class CmixId(val value: Long) { + + companion object { + val placeholder: Long = 0 + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/CmixParams.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/CmixParams.kt new file mode 100644 index 0000000000000000000000000000000000000000..9667fd5b108437f3b42c14ba256694d1149ce8a0 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/CmixParams.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +value class CmixParams(val value: ByteArray) { + + companion object { + val placeholder = byteArrayOf() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/ConfirmationCode.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/ConfirmationCode.kt new file mode 100644 index 0000000000000000000000000000000000000000..1ba08f6e9674a1fe1d074cd0f29cfa636f9314e3 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/ConfirmationCode.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +value class ConfirmationCode(val value: String) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/ConfirmationId.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/ConfirmationId.kt new file mode 100644 index 0000000000000000000000000000000000000000..dee10cb12ab517d2de7313bb00202fd811c09186 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/ConfirmationId.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +value class ConfirmationId(val value: String) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/Connection.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/Connection.kt new file mode 100644 index 0000000000000000000000000000000000000000..de4edff39b2bd0a1bf8d9ee45fc690c3da892e77 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/Connection.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.bindings.model + +internal interface Connection { + fun getId(): Long + fun getPartner(): ByteArray + fun registerListener() + fun sendE2e() + fun close() +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/ConnectionAdapter.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/ConnectionAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..28342c5b161a910f20dd4e1ac89d43662f180f3b --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/ConnectionAdapter.kt @@ -0,0 +1,26 @@ +package io.elixxir.dapp.bindings.model + +import bindings.Connection as CoreConnection + +@JvmInline +value class ConnectionAdapter(private val connection: CoreConnection) : Connection { + override fun getId(): Long { + TODO("Not yet implemented") + } + + override fun getPartner(): ByteArray { + TODO("Not yet implemented") + } + + override fun registerListener() { + TODO("Not yet implemented") + } + + override fun sendE2e() { + TODO("Not yet implemented") + } + + override fun close() { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/Contact.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/Contact.kt new file mode 100644 index 0000000000000000000000000000000000000000..86b5a5c6e978ba3d028bde03d307e5edfa48fac3 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/Contact.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class Contact(val value: ByteArray) { + + companion object { + val placeholder: ByteArray = byteArrayOf() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/E2eHandler.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/E2eHandler.kt new file mode 100644 index 0000000000000000000000000000000000000000..c184ee9a20eb07a1dea3bcf49099191fc1fb540f --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/E2eHandler.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class E2eHandler(val value: Long) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/E2eId.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/E2eId.kt new file mode 100644 index 0000000000000000000000000000000000000000..2810ce3867490c0a0aad4f41086f0bb684237fe1 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/E2eId.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class E2eId(val value: Long) { + + companion object { + val placeholder: Long = 0 + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/E2eListener.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/E2eListener.kt new file mode 100644 index 0000000000000000000000000000000000000000..07f58d8c6cd8fbb571f56da39d57f1269fea8254 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/E2eListener.kt @@ -0,0 +1,5 @@ +package io.elixxir.dapp.bindings.model + +import bindings.Listener + +internal interface E2eListener : Listener \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/E2eParams.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/E2eParams.kt new file mode 100644 index 0000000000000000000000000000000000000000..cd0fc1c3f6bffe109eb57f7c2fc1328691fdb7b3 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/E2eParams.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class E2eParams(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/Fact.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/Fact.kt new file mode 100644 index 0000000000000000000000000000000000000000..86b9b915f506d762c0cc7c254a7025b33e9784ab --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/Fact.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class Fact(val value: ByteArray) { + + companion object { + val placeholder: ByteArray = Fact(byteArrayOf()).value + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/FactsList.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/FactsList.kt new file mode 100644 index 0000000000000000000000000000000000000000..fa3a49c24bc4a1e79f9885f26ddbb2528b08e5c9 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/FactsList.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class FactsList(val value: ByteArray) { + + companion object { + val placeholder: ByteArray = byteArrayOf() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/FileTransferParams.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/FileTransferParams.kt new file mode 100644 index 0000000000000000000000000000000000000000..fc7cf4d6ebab8f04b69400d980eea505dbe3583e --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/FileTransferParams.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +value class FileTransferParams(val value: ByteArray) { + + companion object { + val plaeholder = byteArrayOf() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/GroupAdapter.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/GroupAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..07a97a7dd461756db4966f651cd4f6e2f5ed3908 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/GroupAdapter.kt @@ -0,0 +1,20 @@ +package io.elixxir.dapp.bindings.model + +import io.elixxir.dapp.group.model.Group +import io.elixxir.dapp.group.model.GroupId +import io.elixxir.dapp.user.model.User +import bindings.Group as CoreGroup + +@JvmInline +internal value class GroupAdapter(val value: CoreGroup): Group { + override val groupId: GroupId + get() = GroupId(value.id) + override val name: String + get() = String(value.name) + override val description: String + get() = String(value.initMessage) + override val members: List<User> + get() = listOf() + override val creator: User + get() = User.placeholder +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/GroupInfo.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/GroupInfo.kt new file mode 100644 index 0000000000000000000000000000000000000000..84b84ddc462d0d106fa3540432617f7529acdae0 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/GroupInfo.kt @@ -0,0 +1,7 @@ +package io.elixxir.dapp.bindings.model + +internal data class GroupInfo( + val membershipData: GroupMembership, + val description: String, + val name: String +) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/GroupMembership.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/GroupMembership.kt new file mode 100644 index 0000000000000000000000000000000000000000..04c32bf12a8c43c125e569625d0256a9e3c00238 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/GroupMembership.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class GroupMembership(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/GroupReport.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/GroupReport.kt new file mode 100644 index 0000000000000000000000000000000000000000..c70edbb833711757ee5d25fbcb4cec8cef6abc2c --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/GroupReport.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class GroupReport(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/HealthCallback.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/HealthCallback.kt new file mode 100644 index 0000000000000000000000000000000000000000..bc5aec42b9d98577f51da4b26cdc19228eaac4d3 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/HealthCallback.kt @@ -0,0 +1,24 @@ +package io.elixxir.dapp.bindings.model + +internal interface NetworkHealthListener { + fun onHealthChanged(isHealthy: Boolean) +} + +internal class HealthCallbackAdapter( + private val listener: NetworkHealthListener +) : NetworkHealthListener by listener, + bindings.NetworkHealthCallback +{ + override fun callback(isHealthy: Boolean) { + onHealthChanged(isHealthy) + } + + companion object { + val placeholder: HealthCallbackAdapter = + HealthCallbackAdapter( + object : NetworkHealthListener { + override fun onHealthChanged(isHealthy: Boolean) { } + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/HealthListenerId.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/HealthListenerId.kt new file mode 100644 index 0000000000000000000000000000000000000000..5034d6810b296e4fb05643409eb1b3c8fafc07c9 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/HealthListenerId.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class HealthListenerId(val value: Long) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/LookupId.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/LookupId.kt new file mode 100644 index 0000000000000000000000000000000000000000..928bf0491d37544a3550318cfabdf704126824cb --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/LookupId.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +value class LookupId(val value: ByteArray) { + + companion object { + val placeholder: ByteArray = byteArrayOf() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/MessageType.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/MessageType.kt new file mode 100644 index 0000000000000000000000000000000000000000..c72785377958e613dba8694708ffaf9a796d0c03 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/MessageType.kt @@ -0,0 +1,23 @@ +package io.elixxir.dapp.bindings.model + +internal enum class MessageType(val code: Long) { + NoType(0), + XxMessage(2), + KeyExchangeTrigger(30), + KeyExchangeConfirm(31), + KeyExchangeTriggerEphemeral(32), + KeyExchangeConfirmEphemeral(33), + E2eClose(34), + GroupCreationRequest(40), + NewFileTransfer(50), + EndFileTransfer(51), + ConnectionAuthenticationRequest(60); + + companion object { + fun from(code: Long): MessageType { + return values().firstOrNull { + it.code == code + } ?: throw(IllegalArgumentException("Invalid code")) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/NetworkFollowerStatus.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/NetworkFollowerStatus.kt new file mode 100644 index 0000000000000000000000000000000000000000..fdc5058d5782df2f9c14a203dc6a379f5afb4f04 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/NetworkFollowerStatus.kt @@ -0,0 +1,12 @@ +package io.elixxir.dapp.bindings.model + +internal enum class NetworkFollowerStatus(val code: Long) { + STOPPED(0), + STARTING(1000), + RUNNING(2000), + STOPPING(3000); + + companion object { + fun from(code: Long) = values().first { it.code == code } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/NodeRegistrationStatus.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/NodeRegistrationStatus.kt new file mode 100644 index 0000000000000000000000000000000000000000..620eee54a3914d7de4577827aa5882f7f6a01ee7 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/NodeRegistrationStatus.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class NodeRegistrationStatus(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/PartnersList.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/PartnersList.kt new file mode 100644 index 0000000000000000000000000000000000000000..c040838a3ecf90d655185b1fb5205a54891501b2 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/PartnersList.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class PartnersList(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/Payload.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/Payload.kt new file mode 100644 index 0000000000000000000000000000000000000000..1901047bd5ceb496bcbf16374a8bd8bdd85206f6 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/Payload.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class Payload(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/ReceptionIdentity.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/ReceptionIdentity.kt new file mode 100644 index 0000000000000000000000000000000000000000..313a564aaf07e932da7ea052cb3160e3f8a2dc1e --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/ReceptionIdentity.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class ReceptionIdentity(val value: ByteArray) diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/RegistrationValidationSignature.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/RegistrationValidationSignature.kt new file mode 100644 index 0000000000000000000000000000000000000000..5609b0185dfde3ba9dca9cd3e96a0101f9ed340b --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/RegistrationValidationSignature.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +value class RegistrationValidationSignature(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/Request.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/Request.kt new file mode 100644 index 0000000000000000000000000000000000000000..b1bc8a67e0c28e17f69407d63bc174316f992dc1 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/Request.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class Request(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/RoundId.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/RoundId.kt new file mode 100644 index 0000000000000000000000000000000000000000..e88ff523684c705974bbf21696564551243c70a7 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/RoundId.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +value class RoundId(val value: Long) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/SearchParams.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/SearchParams.kt new file mode 100644 index 0000000000000000000000000000000000000000..90cd95dc520e947d4e3d57cdfaf7aa6955bf6d97 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/SearchParams.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class SearchParams(val value: ByteArray) { + + companion object { + val placeholder: ByteArray = byteArrayOf() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/SendReport.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/SendReport.kt new file mode 100644 index 0000000000000000000000000000000000000000..8226a6664132985184a07368cc19854c2d6d1042 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/SendReport.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +internal value class SendReport(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/UdAddress.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/UdAddress.kt new file mode 100644 index 0000000000000000000000000000000000000000..c0f1b03d7ca22458cc3988bcf93a12eeceefadcf --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/UdAddress.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +value class UdIpAddress(val value: String) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/bindings/model/UdCertificate.kt b/app/src/main/java/io/elixxir/dapp/bindings/model/UdCertificate.kt new file mode 100644 index 0000000000000000000000000000000000000000..f4b9aa8f64ac56913a3d2dacfb4187709d3ba5f3 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/bindings/model/UdCertificate.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.bindings.model + +@JvmInline +value class UdCertificate(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/group/model/Group.kt b/app/src/main/java/io/elixxir/dapp/group/model/Group.kt new file mode 100644 index 0000000000000000000000000000000000000000..6e97e6a94ce0c84a2d9c1cba75e7c31f8029a9bc --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/group/model/Group.kt @@ -0,0 +1,11 @@ +package io.elixxir.dapp.group.model + +import io.elixxir.dapp.user.model.User + +interface Group { + val groupId: GroupId + val name: String + val description: String + val members: List<User> + val creator: User +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/group/model/GroupId.kt b/app/src/main/java/io/elixxir/dapp/group/model/GroupId.kt new file mode 100644 index 0000000000000000000000000000000000000000..8cd1a184f4d5ae548a42e32238e292575ff1c39b --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/group/model/GroupId.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.group.model + +@JvmInline +value class GroupId(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/logger/data/Logger.kt b/app/src/main/java/io/elixxir/dapp/logger/data/Logger.kt new file mode 100644 index 0000000000000000000000000000000000000000..c2dd325844c7ade7fb7a03a358266dac4fb11e69 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/logger/data/Logger.kt @@ -0,0 +1,23 @@ +package io.elixxir.dapp.logger.data + +import io.elixxir.dapp.logger.model.LoggerConfig +import io.elixxir.dapp.logger.model.LogLevel + +internal interface Logger { + val config: LoggerConfig + + fun logFatal(message: String) = log(message, LogLevel.Fatal) + fun logError(message: String) = log(message, LogLevel.Error) + fun logWarn(message: String) = log(message, LogLevel.Warn) + fun log(message: String, level: LogLevel = LogLevel.Debug): () -> Unit = { + if (config.logLevel <= level) { + config.logWriter(message) + } + } + + companion object { + fun newInstance(config: LoggerConfig) = object : Logger { + override val config: LoggerConfig = config + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/logger/data/NoLogging.kt b/app/src/main/java/io/elixxir/dapp/logger/data/NoLogging.kt new file mode 100644 index 0000000000000000000000000000000000000000..064485546ec7380173bdb81b54609b38c4fd9561 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/logger/data/NoLogging.kt @@ -0,0 +1,9 @@ +package io.elixxir.dapp.logger.data + +import io.elixxir.dapp.logger.model.LogLevel +import io.elixxir.dapp.logger.model.LoggerConfig + +data class NoLogging( + override val logLevel: LogLevel = LogLevel.None, + override val logWriter: (String) -> Unit = {} +) : LoggerConfig \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/logger/data/Timber.kt b/app/src/main/java/io/elixxir/dapp/logger/data/Timber.kt new file mode 100644 index 0000000000000000000000000000000000000000..cc1eb579fc360df1ed21976d42dcae289c31ce70 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/logger/data/Timber.kt @@ -0,0 +1,10 @@ +package io.elixxir.dapp.logger.data + +import io.elixxir.dapp.logger.model.LoggerConfig +import io.elixxir.dapp.logger.model.LogLevel +import timber.log.Timber + +internal data class Timber( + override val logLevel: LogLevel = LogLevel.Debug, + override val logWriter: (String) -> Unit = { Timber.v(it) } +) : LoggerConfig diff --git a/app/src/main/java/io/elixxir/dapp/logger/model/LogLevel.kt b/app/src/main/java/io/elixxir/dapp/logger/model/LogLevel.kt new file mode 100644 index 0000000000000000000000000000000000000000..1147f7423eab6e5f59813044e50d3aeea4067126 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/logger/model/LogLevel.kt @@ -0,0 +1,20 @@ +package io.elixxir.dapp.logger.model + +enum class LogLevel(val code: Long) { + Trace(0), + Debug(1), + Info(2), + Warn(3), + Error(4), + Critical(5), + Fatal(6), + None(Long.MAX_VALUE); + + companion object { + fun from(code: Long): LogLevel { + return values().firstOrNull { + it.code == code + } ?: throw(IllegalArgumentException("Invalid code")) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/logger/model/LoggerConfig.kt b/app/src/main/java/io/elixxir/dapp/logger/model/LoggerConfig.kt new file mode 100644 index 0000000000000000000000000000000000000000..ee5b449e77f1c6152394697ed546032cdcf0e3c9 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/logger/model/LoggerConfig.kt @@ -0,0 +1,10 @@ +package io.elixxir.dapp.logger.model + +/** + * Responsible for routing helpful output of [logLevel] severity or greater + * to the provided [logWriter]. + */ +interface LoggerConfig { + val logLevel: LogLevel + val logWriter: (String) -> Unit +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/messaging/model/Message.kt b/app/src/main/java/io/elixxir/dapp/messaging/model/Message.kt new file mode 100644 index 0000000000000000000000000000000000000000..964cb63562895b09215faefe4d616ff62301c649 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/messaging/model/Message.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.messaging.model + +interface Message { +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/network/data/NetworkManager.kt b/app/src/main/java/io/elixxir/dapp/network/data/NetworkManager.kt new file mode 100644 index 0000000000000000000000000000000000000000..7bdcf4d1e28ddc440563e5bfdde6a113893959a6 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/network/data/NetworkManager.kt @@ -0,0 +1,94 @@ +package io.elixxir.dapp.network.data + +import bindings.Bindings +import bindings.Cmix +import io.elixxir.dapp.bindings.data.AuthCallbacksAdapter +import io.elixxir.dapp.bindings.data.CmixAdapter +import io.elixxir.dapp.bindings.data.E2e +import io.elixxir.dapp.bindings.data.E2eAdapter +import io.elixxir.dapp.bindings.model.E2eParams +import io.elixxir.dapp.bindings.model.ReceptionIdentity +import io.elixxir.dapp.api.model.CommonProperties +import io.elixxir.dapp.network.model.* +import io.elixxir.dapp.network.repository.NdfDataSource +import io.elixxir.dapp.session.model.* +import io.elixxir.dapp.session.repository.SessionKeyStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.withContext + +internal interface NetworkManager { + val connectionStatus: Flow<ConnectionStatus> +} + +internal class DappNetworkManager private constructor( + properties: CommonProperties, + keyStore: SessionKeyStore, + ndfSource: NdfDataSource +): NetworkManager, CommonProperties by properties { + + override val connectionStatus: Flow<ConnectionStatus> by ::_connectionStatus + private val _connectionStatus: MutableStateFlow<ConnectionStatus> = + MutableStateFlow(ConnectionStatus.DISCONNECTED) + + fun login( + cmixId: Long, + authCallbacks: AuthCallbacksAdapter, + identity: ReceptionIdentity, + e2eParamsJson: E2eParams + ): E2e { + return E2eAdapter( + Bindings.login(cmixId, authCallbacks.value, identity.value, e2eParamsJson.value) + ) + } + + fun newCmix( + ndfJson: String, + sessionFolderPath: String, + sessionPassword: SessionPassword, + registrationCode: String + ) { + Bindings.newCmix( + ndfJson, + sessionFolderPath, + sessionPassword.value, + registrationCode + ) + } + + suspend fun loadCmix( + sessionFolderPath: String, + sessionPassword: SessionPassword, + cmixParamsJson: E2eParams + ): CmixAdapter = withContext(dispatcher) { + CmixAdapter( + Bindings.loadCmix(sessionFolderPath, sessionPassword.value, cmixParamsJson.value) + ) + } + + fun createIdentity(): ReceptionIdentity { + return ReceptionIdentity( + Cmix().makeReceptionIdentity() + ) + } + + fun initNetworkFollower() { + + } + + fun stopNetworkFollower() { + + } + + fun initUserDiscovery() { + + } + + companion object { + internal fun newInstance( + properties: CommonProperties, + keyStore: SessionKeyStore, + ndfSource: NdfDataSource + ) = DappNetworkManager(properties, keyStore, ndfSource) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/network/model/ConnectionStatus.kt b/app/src/main/java/io/elixxir/dapp/network/model/ConnectionStatus.kt new file mode 100644 index 0000000000000000000000000000000000000000..39e8f6061b41f12b97efd6610d9877c3c1da9507 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/network/model/ConnectionStatus.kt @@ -0,0 +1,5 @@ +package io.elixxir.dapp.network.model + +enum class ConnectionStatus { + DISCONNECTED, CONNECTING, CONNECTED +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/network/model/Environment.kt b/app/src/main/java/io/elixxir/dapp/network/model/Environment.kt new file mode 100644 index 0000000000000000000000000000000000000000..03e52f196c707327935baca045b5924b6ce19f41 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/network/model/Environment.kt @@ -0,0 +1,5 @@ +package io.elixxir.dapp.network.model + +enum class Environment { + MainNet, ReleaseNet +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/network/model/NetworkConfig.kt b/app/src/main/java/io/elixxir/dapp/network/model/NetworkConfig.kt new file mode 100644 index 0000000000000000000000000000000000000000..849714c06433d21bd316f5351e25e4457d0fa086 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/network/model/NetworkConfig.kt @@ -0,0 +1,41 @@ +package io.elixxir.dapp.network.model + +import androidx.annotation.RawRes +import io.elixxir.dapp.R +import io.elixxir.dapp.api.model.CriticalRemoteDataStrategy +import io.elixxir.dapp.api.model.RetryStrategy + +interface NetworkConfig { + val environment: Environment get() = Environment.MainNet + val retryStrategy: RetryStrategy get() = CriticalRemoteDataStrategy() +} + +internal abstract class NdfSettings : NetworkConfig { + abstract val ndfUrl: String + abstract val certificateRef: Int + + companion object { + fun create(config: NetworkConfig): NdfSettings { + return when (config.environment) { + Environment.MainNet -> MainNet(config.retryStrategy) + Environment.ReleaseNet -> ReleaseNet(config.retryStrategy) + } + } + } +} + +internal data class MainNet( + override val retryStrategy: RetryStrategy, + override val ndfUrl: String = + "https://elixxir-bins.s3.us-west-1.amazonaws.com/ndf/mainnet.json", + @RawRes + override val certificateRef: Int = R.raw.mainnet, +) : NdfSettings() + +internal data class ReleaseNet( + override val retryStrategy: RetryStrategy, + override val ndfUrl: String = + "https://elixxir-bins.s3.us-west-1.amazonaws.com/ndf/release.json", + @RawRes + override val certificateRef: Int = R.raw.release +) : NdfSettings() \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/network/repository/NdfDataSource.kt b/app/src/main/java/io/elixxir/dapp/network/repository/NdfDataSource.kt new file mode 100644 index 0000000000000000000000000000000000000000..d62d49d2fa95999033ee896c036518966ac761fb --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/network/repository/NdfDataSource.kt @@ -0,0 +1,61 @@ +package io.elixxir.dapp.network.repository + +import androidx.annotation.RawRes +import io.elixxir.dapp.api.model.CommonProperties +import io.elixxir.dapp.api.model.RetryStrategy +import io.elixxir.dapp.bindings.data.Bindings +import io.elixxir.dapp.network.model.NdfSettings +import kotlinx.coroutines.* +typealias Ndf = String + +internal interface NdfDataSource { + suspend fun fetchNdf(): Ndf +} + +internal class RemoteNdfDataSource private constructor( + properties: CommonProperties, + private val settings: NdfSettings, + private val bindings: Bindings +) : NdfDataSource, + CommonProperties by properties, + RetryStrategy by settings.retryStrategy +{ + + override suspend fun fetchNdf(): Ndf { + return retryFetchNdf() + } + + private suspend fun retryFetchNdf(retries: Int = 0): Ndf = withContext(dispatcher) { + val ndf = downloadAndVerifySignedNdfWithUrl( + settings.ndfUrl, + readCertificate(settings.certificateRef) + ) + + if (ndf.isEmpty() && retries <= maxRetries) { + ensureActive() + delay(retryDelayMs) + retryFetchNdf(retries + 1) + } else { + ndf + } + } + + private fun downloadAndVerifySignedNdfWithUrl( + url: String, + certificate: String + ): Ndf = bindings.fetchSignedNdf(url, certificate) + + private fun readCertificate(@RawRes certRef: Int): String { + val reader= context().resources.openRawResource(certRef) + .bufferedReader() + return reader.use { reader.readText() } + } + + companion object { + internal fun newInstance( + properties: CommonProperties, + settings: NdfSettings, + bindings: Bindings + ) = RemoteNdfDataSource(properties, settings, bindings) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/preferences/KeyStorePreferences.kt b/app/src/main/java/io/elixxir/dapp/preferences/KeyStorePreferences.kt new file mode 100644 index 0000000000000000000000000000000000000000..6ab3d5799d7bcb80bb2d7fda00321012cbbac567 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/preferences/KeyStorePreferences.kt @@ -0,0 +1,5 @@ +package io.elixxir.dapp.preferences + +interface KeyStorePreferences { + var password: String +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/request/model/Request.kt b/app/src/main/java/io/elixxir/dapp/request/model/Request.kt new file mode 100644 index 0000000000000000000000000000000000000000..2d6ca6ef3e8588793ef2cf2883f48dbcd968621f --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/request/model/Request.kt @@ -0,0 +1,8 @@ +package io.elixxir.dapp.request.model + +interface Request { +} + +interface OutgoingRequest : Request +interface IncomingRequest: Request +interface RequestConfirmation: Request \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/session/model/SecureHardwareException.kt b/app/src/main/java/io/elixxir/dapp/session/model/SecureHardwareException.kt new file mode 100644 index 0000000000000000000000000000000000000000..2475ec6abf4643bc691cb4d040a0937d74b6aed2 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/session/model/SecureHardwareException.kt @@ -0,0 +1,6 @@ +package io.elixxir.dapp.session.model + +class SecureHardwareException : Exception() { + override val message: String + get() = "OS is not hardware-backed and require secure hardware is enabled." +} diff --git a/app/src/main/java/io/elixxir/dapp/session/model/SessionPassword.kt b/app/src/main/java/io/elixxir/dapp/session/model/SessionPassword.kt new file mode 100644 index 0000000000000000000000000000000000000000..8dfddc9fee4259416d3ffe3e1fde7cdabbdc655b --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/session/model/SessionPassword.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.session.model + +@JvmInline +value class SessionPassword(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/session/repository/SessionDataSource.kt b/app/src/main/java/io/elixxir/dapp/session/repository/SessionDataSource.kt new file mode 100644 index 0000000000000000000000000000000000000000..3f316d7f3800ed962dc298ed1081d614dd7d521e --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/session/repository/SessionDataSource.kt @@ -0,0 +1,88 @@ +package io.elixxir.dapp.session.repository + +import java.io.File +import io.elixxir.dapp.api.model.CommonProperties +import kotlinx.coroutines.* + +/** + * Responsible for creation, deletion & storage of folder used to persist + * Cmix session. + */ +internal interface SessionDataSource { + val sessionFolder: File + + fun doesSessionExist(): Boolean + + /** + * Only needs to be created once per app installation. + * Return the [sessionFolder] if successful. + */ + suspend fun createSession(): Result<File> + + /** + * Should be called during account deletion. + */ + suspend fun deleteSession(): Result<Unit> + + /** + * Restore session + */ + suspend fun restoreSession(): Result<Unit> +} + +internal class LocalSessionDataSource private constructor( + properties: CommonProperties +) : SessionDataSource, CommonProperties by properties { + + override val sessionFolder: File get() { + return try { + File(context().filesDir, "cmix/session") + } catch (e: Exception) { + logFatal(e.message ?: "Couldn't access filesDir.") + throw e + } + } + + override fun doesSessionExist(): Boolean = sessionFolder.exists() + + override suspend fun createSession(): Result<File> = withContext(dispatcher) { + try { + Result.success(createSessionFolder()) + } catch (e: Exception) { + Result.failure(e) + } + } + + private fun createSessionFolder(): File { + return sessionFolder.apply { + if (exists()) { + log("Session folder from previous installation was found.") + log("It contains ${listFiles()?.size ?: 0} files.") + log("Deleting!") + deleteRecursively() + } + mkdir() + log("Session folder successfully created at: $absolutePath") + } + } + + override suspend fun deleteSession(): Result<Unit> = withContext(dispatcher) { + try { + if (sessionFolder.deleteRecursively()) { + Result.success(Unit) + } else { + Result.failure(Exception("Failed to delete all files.")) + } + } catch (e: Exception) { + Result.failure(e) + } + } + + override suspend fun restoreSession(): Result<Unit> { + TODO("Not yet implemented") + } + + companion object { + internal fun newInstance(properties: CommonProperties) = LocalSessionDataSource(properties) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/session/repository/SessionKeyStore.kt b/app/src/main/java/io/elixxir/dapp/session/repository/SessionKeyStore.kt new file mode 100644 index 0000000000000000000000000000000000000000..0607c31fb2eb2b86c84347e4cc86a2b4bfc5dc64 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/session/repository/SessionKeyStore.kt @@ -0,0 +1,238 @@ +package io.elixxir.dapp.session.repository + +import android.os.Build +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyInfo +import android.security.keystore.KeyProperties +import io.elixxir.dapp.api.model.CommonProperties +import io.elixxir.dapp.bindings.data.Bindings +import io.elixxir.dapp.preferences.KeyStorePreferences +import io.elixxir.dapp.session.model.SecureHardwareException +import io.elixxir.dapp.session.model.SessionPassword +import io.elixxir.dapp.util.fromBase64toByteArray +import io.elixxir.dapp.util.toBase64String +import kotlinx.coroutines.withContext +import java.security.* +import java.security.spec.MGF1ParameterSpec +import java.security.spec.RSAKeyGenParameterSpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException +import javax.crypto.spec.OAEPParameterSpec +import javax.crypto.spec.PSource +import kotlin.system.measureTimeMillis + +internal interface SessionKeyStore { + suspend fun createSessionPassword(requireSecureHardware: Boolean): Result<Unit> + suspend fun rsaDecryptPassword(): SessionPassword +} + +internal class DappSessionKeystore private constructor( + properties: CommonProperties, + private val preferences: KeyStorePreferences, + private val bindings: Bindings +) : SessionKeyStore, CommonProperties by properties { + + override suspend fun createSessionPassword(requireSecureHardware: Boolean): Result<Unit> = + withContext(dispatcher) { + if (requireSecureHardware && !isHardwareBackedKeyStore()) { + return@withContext Result.failure(SecureHardwareException()) + } + + try { + deletePreviousKeys() + generateKeys() + generatePassword() + Result.success(Unit) + } catch (e: Exception) { + logError(e.message ?: "An error occured while creating session password.") + Result.failure(e) + } + } + + private fun generatePassword() { + val encryptionTime = measureTimeMillis { + val passwordLength = PASSWORD_LENGTH + log("Generating a password...") + log("Generating a password with $passwordLength bytes") + + var secret: SessionPassword + val generationTime = measureTimeMillis { + do { + secret = bindings.generateSecret(passwordLength) + log("Password (Bytearray): $secret") + log("Password (String64): ${secret.value.toBase64String()}") + + val isAllZeroes = byteArrayOf(passwordLength.toByte()).contentEquals(secret.value) + log("IsAllZeroes: $isAllZeroes") + } while (isAllZeroes) + } + log("Total generation time: $generationTime ms") + rsaEncryptPwd(secret.value) + } + log("Total encryption time: $encryptionTime ms") + } + + private fun deletePreviousKeys() { + val keystore = getKeystore() + if (keystore.containsAlias(KEY_ALIAS)) { + log("Deleting key alias") + keystore.deleteEntry(KEY_ALIAS) + } + } + + private fun checkGenerateKeys(): Boolean { + return try { + val areKeysGenerated = generateKeys() + if (areKeysGenerated) { + log("Keystore keys successfully generated") + true + } else { + log("Error generating keystore keys") + false + } + } catch (err: Exception) { + log("An error occured while generating keys.") + false + } + } + + private fun generateKeys(): Boolean { + val keystore = getKeystore() + if (!keystore.containsAlias(KEY_ALIAS)) { + log("Keystore alias does not exist, credentials") + val keyGenerator = getKeyPairGenerator() + keyGenerator.genKeyPair() + } else { + log("Keystore alias already exist") + } + + return true + } + + private fun getKeyPairGenerator(): KeyPairGenerator { + val keyGenerator = KeyPairGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore" + ) + val keyGenParameterSpec = getKeygenParamSpec() + keyGenerator.initialize(keyGenParameterSpec) + return keyGenerator + } + + private fun getPrivateKey(): PrivateKey? { + val keyStore: KeyStore = getKeystore() + return keyStore.getKey(KEY_ALIAS, null) as PrivateKey? + } + + private fun getPublicKey(): PublicKey { + return getKeystore().getCertificate(KEY_ALIAS).publicKey + } + + private fun getKeystore(): KeyStore { + val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE) + keyStore.load(null) + return keyStore + } + + private fun getKeygenParamSpec(): KeyGenParameterSpec { + val keyGenSpec = KeyGenParameterSpec.Builder( + KEY_ALIAS, + KEY_PURPOSE + ).setAlgorithmParameterSpec(RSAKeyGenParameterSpec(KEY_SIZE, RSAKeyGenParameterSpec.F4)) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) + .setBlockModes(KeyProperties.BLOCK_MODE_ECB) + .setDigests(KeyProperties.DIGEST_SHA1) + .setRandomizedEncryptionRequired(true) + + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + keyGenSpec.setUserAuthenticationParameters( + 1000, + KeyProperties.AUTH_BIOMETRIC_STRONG or KeyProperties.AUTH_DEVICE_CREDENTIAL + ) + } else { + keyGenSpec.setUserAuthenticationValidityDurationSeconds(1000) + } + + return keyGenSpec.build() + } + + @Throws( + NoSuchAlgorithmException::class, + NoSuchPaddingException::class, + InvalidKeyException::class, + IllegalBlockSizeException::class, + BadPaddingException::class + ) + private fun rsaEncryptPwd(pwd: ByteArray): ByteArray { + log("Bytecount: ${pwd.size}") + log("Before encryption: ${pwd.toBase64String()}") + val secretKey = getPublicKey() + + val cipher = Cipher.getInstance(KEYSTORE_ALGORITHM) + cipher.init(Cipher.ENCRYPT_MODE, secretKey, cipherMode) + val encryptedBytes = cipher.doFinal(pwd) + log("Encrypted: ${encryptedBytes.toBase64String()}") + preferences.password = encryptedBytes.toBase64String() + + return encryptedBytes + } + + @Throws( + NoSuchAlgorithmException::class, + NoSuchPaddingException::class, + InvalidKeyException::class, + IllegalBlockSizeException::class, + BadPaddingException::class + ) + override suspend fun rsaDecryptPassword(): SessionPassword = withContext(dispatcher) { + val encryptedBytes = preferences.password.fromBase64toByteArray() + val key = getPrivateKey() + val cipher1 = Cipher.getInstance(KEYSTORE_ALGORITHM) + println("Initializing Decrypt") + cipher1.init(Cipher.DECRYPT_MODE, key, cipherMode) + val decryptedBytes = cipher1.doFinal(encryptedBytes) + println("Decrypted: ${decryptedBytes.toBase64String()}") + + SessionPassword(decryptedBytes) + } + + private fun isHardwareBackedKeyStore(): Boolean { + return try { + val privateKey = getPrivateKey() + val keyFactory = KeyFactory.getInstance(privateKey?.algorithm, ANDROID_KEYSTORE) + val keyInfo: KeyInfo = keyFactory.getKeySpec(privateKey, KeyInfo::class.java) + val securityLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + (keyInfo.securityLevel == KeyProperties.SECURITY_LEVEL_TRUSTED_ENVIRONMENT || + keyInfo.securityLevel == KeyProperties.SECURITY_LEVEL_STRONGBOX) + } else { + keyInfo.isInsideSecureHardware + } + return securityLevel + } catch (e: Exception) { + logError(e.message ?: "An error occured while checking hardware backed key store.") + false + } + } + + companion object { + private const val KEY_ALIAS = "dAppSession" + private const val ANDROID_KEYSTORE = "AndroidKeyStore" + private const val KEY_PURPOSE = + KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT + private const val KEYSTORE_ALGORITHM = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding" + private const val KEY_SIZE = 2048 + private val cipherMode = OAEPParameterSpec( + "SHA-1", "MGF1", + MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT + ) + private const val PASSWORD_LENGTH = 64L + + internal fun newInstance( + properties: CommonProperties, + preferences: KeyStorePreferences, + bindings: Bindings + ) = DappSessionKeystore(properties, preferences, bindings) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/user/model/User.kt b/app/src/main/java/io/elixxir/dapp/user/model/User.kt new file mode 100644 index 0000000000000000000000000000000000000000..c24e22376e3aca42596669f4387cc917c91789d2 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/user/model/User.kt @@ -0,0 +1,18 @@ +package io.elixxir.dapp.user.model + +interface User { + val userId: Long + val username: String + val phone: String? + val email: String? + + companion object { + val placeholder = object : User { + override val userId: Long = 0 + override val username: String = "" + override val phone: String? = null + override val email: String? = null + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/user/model/UserId.kt b/app/src/main/java/io/elixxir/dapp/user/model/UserId.kt new file mode 100644 index 0000000000000000000000000000000000000000..92d92772ac1ef0ea51548c1cd2d53feec78c5436 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/user/model/UserId.kt @@ -0,0 +1,4 @@ +package io.elixxir.dapp.user.model + +@JvmInline +value class UserId(val value: ByteArray) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/user/model/UserQuery.kt b/app/src/main/java/io/elixxir/dapp/user/model/UserQuery.kt new file mode 100644 index 0000000000000000000000000000000000000000..0debdfb23fcd0d389c3c21fc4b2322e5018551b9 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/user/model/UserQuery.kt @@ -0,0 +1,10 @@ +package io.elixxir.dapp.user.model + +interface UserQuery { + val query: String + val queryType: QueryType + + enum class QueryType { + USERNAME, USERID, PHONE, EMAIL + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/user/model/UserUpdateData.kt b/app/src/main/java/io/elixxir/dapp/user/model/UserUpdateData.kt new file mode 100644 index 0000000000000000000000000000000000000000..0df45d32dc36cf7d20e9667e28d19b09916ac154 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/user/model/UserUpdateData.kt @@ -0,0 +1,6 @@ +package io.elixxir.dapp.user.model + +data class UserUpdateData( + val newPhone: String?, + val newEmail: String? +) \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/user/repository/UserDataSource.kt b/app/src/main/java/io/elixxir/dapp/user/repository/UserDataSource.kt new file mode 100644 index 0000000000000000000000000000000000000000..037e4f40c06759d7da7c7c66f8448449c3a5f751 --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/user/repository/UserDataSource.kt @@ -0,0 +1,36 @@ +package io.elixxir.dapp.user.repository + +import io.elixxir.dapp.bindings.data.UserDiscovery +import io.elixxir.dapp.api.model.CommonProperties +import io.elixxir.dapp.user.model.User + +interface UserDataSource { + suspend fun createUser(username: String): Result<User> + suspend fun updateProfile(): Result<User> + suspend fun deleteProfile(): Result<Unit> +} + +internal class RemoteUserDataSource private constructor( + properties: CommonProperties, + private val ud: UserDiscovery +) : UserDataSource, CommonProperties by properties { + + override suspend fun createUser(username: String): Result<User> { + TODO("Not yet implemented") + } + + override suspend fun updateProfile(): Result<User> { + TODO("Not yet implemented") + } + + override suspend fun deleteProfile(): Result<Unit> { + TODO("Not yet implemented") + } + + companion object { + internal fun newInstance( + properties: CommonProperties, + ud: UserDiscovery + ) = RemoteUserDataSource(properties, ud) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/elixxir/dapp/util/ByteArray.kt b/app/src/main/java/io/elixxir/dapp/util/ByteArray.kt new file mode 100644 index 0000000000000000000000000000000000000000..a7cd66b8448698fd37a215414766aebd25a9f02e --- /dev/null +++ b/app/src/main/java/io/elixxir/dapp/util/ByteArray.kt @@ -0,0 +1,11 @@ +package io.elixxir.dapp.util + +import android.util.Base64 + +fun ByteArray.toBase64String(): String { + return Base64.encodeToString(this, Base64.NO_WRAP) +} + +fun String.fromBase64toByteArray(): ByteArray { + return Base64.decode(this, Base64.NO_WRAP) +} \ No newline at end of file diff --git a/app/src/main/res/raw/mainnet.crt b/app/src/main/res/raw/mainnet.crt new file mode 100644 index 0000000000000000000000000000000000000000..096e3b5cda98b012d892d6531ab1a4696d13a457 --- /dev/null +++ b/app/src/main/res/raw/mainnet.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIUO0qHXSeKrOMucO+Zz82Mf1Zlq4gwDQYJKoZIhvcNAQEL +BQAwgYAxCzAJBgNVBAYTAktZMRQwEgYDVQQHDAtHZW9yZ2UgVG93bjETMBEGA1UE +CgwKeHggbmV0d29yazEPMA0GA1UECwwGRGV2T3BzMRMwEQYDVQQDDAp4eC5uZXR3 +b3JrMSAwHgYJKoZIhvcNAQkBFhFhZG1pbnNAeHgubmV0d29yazAeFw0yMTEwMzAy +MjI5MjZaFw0zMTEwMjgyMjI5MjZaMIGAMQswCQYDVQQGEwJLWTEUMBIGA1UEBwwL +R2VvcmdlIFRvd24xEzARBgNVBAoMCnh4IG5ldHdvcmsxDzANBgNVBAsMBkRldk9w +czETMBEGA1UEAwwKeHgubmV0d29yazEgMB4GCSqGSIb3DQEJARYRYWRtaW5zQHh4 +Lm5ldHdvcmswggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQD08ixnPWwz +FtBIEWx2SnFjBsdrSWCp9NcWXRtGWeq3ACz+ixiflj/U9U4b57aULeOAvcoC7bwU +j5w3oYxRmXIV40QSevx1z9mNcW3xbbacQ+yCgPPhhj3/c285gVVOUzURLBTNAi9I +EA59zAb8Vy0E6zfq4HRAhH11Q/10QgDjEXuGXra1k3IlemVsouiJGNAtKojNDE1N +x9HnraSEiXzdnV2GDplEvMHaLd3s9vs4XsiLB3VwKyHv7EH9+LOIra6pr5BWw+kD +2qHKGmQMOQe0a7nCirW/k9axH0WiA0XWuQu3U1WfcMEfdC/xn1vtubrdYjtzpXUy +oUEX5eHfu4OlA/zoH+trocfARDyBmTVbDy0P9imH//a6GUKDui9r3fXwEy5YPMhb +dKaNc7QWLPHMh1n25h559z6PqxxPT6UqFFbZD2gTw1sbbpjyqhLbnYguurkxY3jZ +ztW337hROzQ1/abbg/P59JA95Pmhkl8nqqDEf0buOmvMazq3Lwg92nuZ8gsdMKXB +xaEtTTpxhTPOqzc1/XQgScZnc+092MBDh3C2GMxzylOIdk+yF2Gyb+VWPUe29dSa +azzxsDXzRy8y8jaOjdSUWaLa/MgS5Dg1AfHtD55bdvqYzw3NEXIVarpMlzl+Z+6w +jvuwz8GyoMSVe+YEGgvSDvlfY/z19aqneQIDAQABoxkwFzAVBgNVHREEDjAMggp4 +eC5uZXR3b3JrMA0GCSqGSIb3DQEBCwUAA4ICAQCp0JDub2w5vZQvFREyA+utZ/+s +XT05j1iTgIRKMa3nofDGERYJUG7FcTd373I2baS70PGx8FF1QuXhn4DNNZlW/SZt +pa1d0pAerqFrIzwOuWVDponYHQ8ayvsT7awCbwZEZE4RhooqS4LqnvtgFu/g7LuM +zkFN8TER7HAUn3P7BujLvcgtqk2LMDz+AgBRszDp/Bw7+1EJDeG9d7hC/stXgDV/ +vpD1YDpxSmW4zjezFJqV6OdMOwo9RWVIktK3RXbFc6I5UJZ5kmzPe/I2oPPCBQvD +G3VqFLQe5ik5rXP7SgAN1fL/7KuQna0s42hkV64Z2ymCX69G1ofpgpEFaQLaxLbj +QOun0r8A3NyKvHRIh4K0dFcc3FSOF60Y6k769HKbOPmSDjSSg0qO9GEONBJ8BxAT +IHcHoTAOQoqGehdzepXQSjHsPqTXv3ZFFwCCgO0toI0Qhqwo89X6R3k+i4Kaktr7 +mLiPO8s0nq1PZ1XrybKE9BCHkYH1JkUDA+M0pn4QAEx/BuM0QnGXoi1sImW3pEUG +NP7fjkISrD48P8P/TLS45sx5pB8MNGEsRw0lBKmuOdWDmdfhOltB6JxmbhpstNZp +6LVLK6SEOwE76xnHiisR2KyhTTiroUq73BgPFWkWhoJDPbmL1DHgnbdKwwstG8Qu +UGb8k8vh6tzqYZAOKg== +-----END CERTIFICATE----- diff --git a/app/src/main/res/raw/release.crt b/app/src/main/res/raw/release.crt new file mode 100644 index 0000000000000000000000000000000000000000..b8d5847f8709d8f888f32ce2024dcffe33af4131 --- /dev/null +++ b/app/src/main/res/raw/release.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFtjCCA56gAwIBAgIJAJnUcpLbGSQiMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYD +VQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUNsYXJlbW9udDEQMA4GA1UE +CgwHRWxpeHhpcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEzARBgNVBAMMCmVsaXh4 +aXIuaW8xHzAdBgkqhkiG9w0BCQEWEGFkbWluQGVsaXh4aXIuaW8wHhcNMjAxMTE3 +MTkwMTUyWhcNMjIxMTE3MTkwMTUyWjCBjDELMAkGA1UEBhMCVVMxCzAJBgNVBAgM +AkNBMRIwEAYDVQQHDAlDbGFyZW1vbnQxEDAOBgNVBAoMB0VsaXh4aXIxFDASBgNV +BAsMC0RldmVsb3BtZW50MRMwEQYDVQQDDAplbGl4eGlyLmlvMR8wHQYJKoZIhvcN +AQkBFhBhZG1pbkBlbGl4eGlyLmlvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAvtByOoSS8SeMLvvHIuOGfnx0VgweveJHX93LUyJxr1RlVBXCgC5/QOQN +N3dmKWzu4YwaA2jtwaAMhkgdfyOcw6kuqfvQjxv99XRIRKM4GZQkJiym2cnorNu7 +hm2/bxmj5TjpP9+vFzbjkJrpRQ80hsV7I9+NKzIhMK4YTgte/F/q9URESlMZxTbb +MFh3s5iiBfBLRNFFsHVdy8OVH+Jv5901cLn+yowaMDLrBMOWGlRROg82ZeRAranX +9X1s+6BclJ/cBe/LcDxGso5sco6UzrWHzpDTnOTzHoamQHYCXtAZP4XbzcqI6A5i +GFM2akuG9Wv3XZZv/6eJRnKS2GLkvv7dtzv+nalxoBKtyIE8ICIVOrb+pVJvY1Id +HOXkK9MEJJ6sZhddipUaQw6hD4I0dNEt30Ugq9zTgFcEnM2R7qKpIDmxrRbcl280 +TQGNYgdidzleNdZbjcTvsMVhcxPXCY+bVX1xICD1oJiZZbZFejBvPEfLYyzSdYp+ +awX5OnLVSrQtTJu9yz5q3q5pHhxJnqS/CVGLTvzLfmk7BGwRZZuK87LnSixyYfpd +S23qI45AEUINEE0HDZsI+KBq0oVlDB0Z3AZpWauRDqY3o6JIbIOpqmZc6KntyL7j +YCAhbB1tchS47PpbIxUgMMGoR3MBkJutPqtTWCEE3l5jvv0CknUCAwEAAaMZMBcw +FQYDVR0RBA4wDIIKZWxpeHhpci5pbzANBgkqhkiG9w0BAQsFAAOCAgEACLoxE3nh +3VzXH2lQo1QzjKkG/+1m75T0l9Wn9uxa2W/90qBCfim1CPfWUstGdRLBi8gjDevV +zK5HN+Cpz2E22qByeN9fl6rJC4zd1vIdexEre5h7goWoV+qFPhOACElor1tF5UQ2 +GD+NFH+Z0ALG1u8db0hBv8NCbtD4YzcQzzINEbs9gp/Sq3cRzkz1wCufFwJwr7+R +0YqZfPj/v/w9G9wSUys1s3i4xr2u87T/bPF68VRg6r1+kXRSRevXd99wKwap52jY +zOwsDGZF9BHMpFVYR/yZhfzSK3F1DmvwuqOsfwSFIjrUjfRlwS28zyZ8rjBq1suD +EAdvYCLDmBSGssNh8E20PHmk5UROYFGEEhlK5ZKj/f1HOmMiOX461XK6HODYyitq +Six2dPi1ZlBJW83DyFqSWJaUR/CluBYmqrWoBX+chv54bU2Y9j/sA/O98wa7trsk +ctzvAcXjhXm6ESRVVD/iZvkW5MP2mkgbDpW3RP9souK5JzbcpC7i3hEcAqPSPgzL +94kHDpYNY7jcGQC4CjPdfBi+Tf6il/QLFRFgyHm2ze3+qrlPT6SQ4hSSH1iXyf4v +tlqu6u77fbF9yaHtq7dvYxH1WioIUxMqbIC1CNgGC1Y/LhzgLRKPSTBCrbQyTcGc +0b5cTzVKxdP6v6WOAXVOEkXTcBPZ4nEZxY0= +-----END CERTIFICATE-----