diff --git a/app/src/main/java/io/timelimit/android/crypto/DH.kt b/app/src/main/java/io/timelimit/android/crypto/DH.kt new file mode 100644 index 0000000..4c340eb --- /dev/null +++ b/app/src/main/java/io/timelimit/android/crypto/DH.kt @@ -0,0 +1,79 @@ +/* + * TimeLimit Copyright 2019 - 2022 Jonas Lochmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package io.timelimit.android.crypto + +import io.timelimit.android.extensions.base64 +import io.timelimit.android.sync.network.ServerDhKey +import java.security.KeyFactory +import java.security.KeyPairGenerator +import java.security.SecureRandom +import java.security.spec.ECGenParameterSpec +import java.security.spec.X509EncodedKeySpec +import javax.crypto.Cipher +import javax.crypto.KeyAgreement +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.SecretKeySpec + +data class DHHandshake(val keyVersion: String, val ownPublicKey: ByteArray, val sharedSecret: ByteArray) { + companion object { + private const val SHARED_SECRET_LENGTH = 32 + private const val AES_KEY_SIZE = 16 + private const val AES_AUTH_TAG_BITS = 128 + + fun fromServerKey(serverDhKey: ServerDhKey): DHHandshake { + val otherPublicKey = KeyFactory.getInstance("EC").generatePublic(X509EncodedKeySpec(serverDhKey.key)) + + val ownKeypair = KeyPairGenerator.getInstance("EC").let { + it.initialize(ECGenParameterSpec("secp256r1")) + it.genKeyPair() + } + + val ownPublicKey = ownKeypair.public.encoded + + val sharedSecret = KeyAgreement.getInstance("ECDH").let { + it.init(ownKeypair.private) + it.doPhase(otherPublicKey, true) + it.generateSecret() + } + + return DHHandshake( + keyVersion = serverDhKey.version, + ownPublicKey = ownPublicKey, + sharedSecret = sharedSecret + ) + } + } + + init { + if (sharedSecret.size != SHARED_SECRET_LENGTH) { + throw IllegalArgumentException() + } + } + + fun encrypt(cryptData: ByteArray, authData: ByteArray): String { + val aesKey = SecretKeySpec(sharedSecret.sliceArray(0 until AES_KEY_SIZE), "AES") + val iv = ByteArray(12).also { SecureRandom().nextBytes(it) } + + val cipher = Cipher.getInstance("AES/GCM/NoPadding").also { + it.init(Cipher.ENCRYPT_MODE, aesKey, GCMParameterSpec(AES_AUTH_TAG_BITS, iv)) + it.updateAAD(authData) + } + + val encryptedData = cipher.doFinal(cryptData) + + return (iv + encryptedData).base64() + "." + ownPublicKey.base64() + "." + keyVersion + } +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/data/dao/ConfigDao.kt b/app/src/main/java/io/timelimit/android/data/dao/ConfigDao.kt index cb5f730..c6de8c9 100644 --- a/app/src/main/java/io/timelimit/android/data/dao/ConfigDao.kt +++ b/app/src/main/java/io/timelimit/android/data/dao/ConfigDao.kt @@ -30,6 +30,7 @@ import io.timelimit.android.extensions.parseBase64 import io.timelimit.android.extensions.toJsonReader import io.timelimit.android.livedata.ignoreUnchanged import io.timelimit.android.livedata.map +import io.timelimit.android.sync.network.ServerDhKey import io.timelimit.android.update.UpdateStatus import java.io.StringWriter @@ -363,4 +364,19 @@ abstract class ConfigDao { fun getLastServerKeyResponseSequenceSync(): Long? = getValueOfKeySync(ConfigurationItemType.LastKeyResponseSequence)?.toLong() fun setLastServerKeyResponseSequenceSync(value: Long) = updateValueSync(ConfigurationItemType.LastKeyResponseSequence, value.toString()) + + @Transaction + open fun getLastDhKeySync(): ServerDhKey? { + val version = getValueOfKeySync(ConfigurationItemType.DhKeyVersion) + val key = getValueOfKeySync(ConfigurationItemType.DhKey) + + return if (version != null && key != null) ServerDhKey(version = version, key = key.parseBase64()) + else null + } + + @Transaction + open fun setLastDhKeySync(key: ServerDhKey) { + updateValueSync(ConfigurationItemType.DhKeyVersion, key.version) + updateValueSync(ConfigurationItemType.DhKey, key.key.base64()) + } } diff --git a/app/src/main/java/io/timelimit/android/data/model/ConfigurationItem.kt b/app/src/main/java/io/timelimit/android/data/model/ConfigurationItem.kt index 6bf9c47..d73029c 100644 --- a/app/src/main/java/io/timelimit/android/data/model/ConfigurationItem.kt +++ b/app/src/main/java/io/timelimit/android/data/model/ConfigurationItem.kt @@ -103,7 +103,9 @@ enum class ConfigurationItemType { SigningKey, SignSequenceNumber, LastServerKeyRequestSequence, - LastKeyResponseSequence + LastKeyResponseSequence, + DhKey, + DhKeyVersion } object ConfigurationItemTypeUtil { @@ -136,6 +138,8 @@ object ConfigurationItemTypeUtil { private const val SIGN_SEQUENCE_NUMBER = 28 private const val LAST_SERVER_KEY_REQUEST_SEQUENCE = 29 private const val LAST_SERVER_KEY_RESPONSE_SEQUENCE = 30 + private const val DH_KEY = 31 + private const val DH_KEY_VERSION = 32 val TYPES = listOf( ConfigurationItemType.OwnDeviceId, @@ -166,7 +170,9 @@ object ConfigurationItemTypeUtil { ConfigurationItemType.SigningKey, ConfigurationItemType.SignSequenceNumber, ConfigurationItemType.LastServerKeyRequestSequence, - ConfigurationItemType.LastKeyResponseSequence + ConfigurationItemType.LastKeyResponseSequence, + ConfigurationItemType.DhKey, + ConfigurationItemType.DhKeyVersion ) fun serialize(value: ConfigurationItemType) = when(value) { @@ -199,6 +205,8 @@ object ConfigurationItemTypeUtil { ConfigurationItemType.SignSequenceNumber -> SIGN_SEQUENCE_NUMBER ConfigurationItemType.LastServerKeyRequestSequence -> LAST_SERVER_KEY_REQUEST_SEQUENCE ConfigurationItemType.LastKeyResponseSequence -> LAST_SERVER_KEY_RESPONSE_SEQUENCE + ConfigurationItemType.DhKey -> DH_KEY + ConfigurationItemType.DhKeyVersion -> DH_KEY_VERSION } fun parse(value: Int) = when(value) { @@ -231,6 +239,8 @@ object ConfigurationItemTypeUtil { SIGN_SEQUENCE_NUMBER -> ConfigurationItemType.SignSequenceNumber LAST_SERVER_KEY_REQUEST_SEQUENCE -> ConfigurationItemType.LastServerKeyRequestSequence LAST_SERVER_KEY_RESPONSE_SEQUENCE -> ConfigurationItemType.LastKeyResponseSequence + DH_KEY -> ConfigurationItemType.DhKey + DH_KEY_VERSION -> ConfigurationItemType.DhKeyVersion else -> throw IllegalArgumentException() } } diff --git a/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt b/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt index bd03a9f..038a231 100644 --- a/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt +++ b/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt @@ -53,6 +53,8 @@ object ApplyServerDataStatus { database.config().setServerApiLevelSync(status.apiLevel) } + status.dh?.also { database.config().setLastDhKeySync(it) } + var didCreateNewActions = false run { diff --git a/app/src/main/java/io/timelimit/android/sync/network/ClientDataStatus.kt b/app/src/main/java/io/timelimit/android/sync/network/ClientDataStatus.kt index 62a8b65..3015af4 100644 --- a/app/src/main/java/io/timelimit/android/sync/network/ClientDataStatus.kt +++ b/app/src/main/java/io/timelimit/android/sync/network/ClientDataStatus.kt @@ -25,7 +25,8 @@ data class ClientDataStatus( val categories: Map, val userListVersion: String, val lastKeyRequestServerSequence: Long?, - val lastKeyResponseServerSequence: Long? + val lastKeyResponseServerSequence: Long?, + val dhKeyVersion: String? ) { companion object { private const val DEVICES = "devices" @@ -36,7 +37,8 @@ data class ClientDataStatus( private const val DEVICES_DETAIL = "devicesDetail" private const val LAST_KEY_REQUEST_SEQUENCE = "kri" private const val LAST_KEY_RESPONSE_SEQUENCE = "kr" - private const val CLIENT_LEVEL_VALUE = 4 + private const val DH = "dh" + private const val CLIENT_LEVEL_VALUE = 5 val empty = ClientDataStatus( deviceListVersion = "", @@ -45,7 +47,8 @@ data class ClientDataStatus( categories = emptyMap(), userListVersion = "", lastKeyRequestServerSequence = null, - lastKeyResponseServerSequence = null + lastKeyResponseServerSequence = null, + dhKeyVersion = null ) fun getClientDataStatusSync(database: Database): ClientDataStatus { @@ -83,7 +86,8 @@ data class ClientDataStatus( }, userListVersion = database.config().getUserListVersionSync(), lastKeyRequestServerSequence = database.config().getLastServerKeyRequestSequenceSync(), - lastKeyResponseServerSequence = database.config().getLastServerKeyResponseSequenceSync() + lastKeyResponseServerSequence = database.config().getLastServerKeyResponseSequenceSync(), + dhKeyVersion = database.config().getLastDhKeySync()?.version ) } } @@ -123,6 +127,7 @@ data class ClientDataStatus( lastKeyRequestServerSequence?.let { writer.name(LAST_KEY_REQUEST_SEQUENCE).value(it) } lastKeyResponseServerSequence?.let { writer.name(LAST_KEY_RESPONSE_SEQUENCE).value(it) } + dhKeyVersion?.let { writer.name(DH).value(it) } writer.endObject() } diff --git a/app/src/main/java/io/timelimit/android/sync/network/ParentPassword.kt b/app/src/main/java/io/timelimit/android/sync/network/ParentPassword.kt index 000ced5..242ecbe 100644 --- a/app/src/main/java/io/timelimit/android/sync/network/ParentPassword.kt +++ b/app/src/main/java/io/timelimit/android/sync/network/ParentPassword.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,38 +18,53 @@ package io.timelimit.android.sync.network import android.util.JsonWriter import io.timelimit.android.async.Threads import io.timelimit.android.coroutines.executeAndWait +import io.timelimit.android.crypto.DHHandshake import io.timelimit.android.crypto.PasswordHashing import org.json.JSONObject data class ParentPassword ( val parentPasswordHash: String, val parentPasswordSecondHash: String, - val parentPasswordSecondSalt: String + val parentPasswordSecondSalt: String, + val encrypted: Boolean ) { companion object { private const val HASH = "hash" private const val SECOND_HASH = "secondHash" private const val SECOND_SALT = "secondSalt" + private const val ENCRYPTED = "encrypted" - fun createSync(password: String): ParentPassword { + fun createSync(password: String, dhKey: ServerDhKey?): ParentPassword { + val hash = PasswordHashing.hashSync(password) val secondSalt = PasswordHashing.generateSalt() + val secondHash = PasswordHashing.hashSyncWithSalt(password, secondSalt) - return ParentPassword( - parentPasswordHash = PasswordHashing.hashSync(password), + if (dhKey == null) { + return ParentPassword( + parentPasswordHash = hash, parentPasswordSecondSalt = secondSalt, - parentPasswordSecondHash = PasswordHashing.hashSyncWithSalt(password, secondSalt) - ) + parentPasswordSecondHash = secondHash, + encrypted = false + ) + } else { + val handshake = DHHandshake.fromServerKey(dhKey) + val secondHashEncrypted = handshake.encrypt( + cryptData = secondHash.toByteArray(Charsets.US_ASCII), + authData = "ParentPassword:$hash:$secondSalt".toByteArray(Charsets.US_ASCII) + ) + + return ParentPassword( + parentPasswordHash = hash, + parentPasswordSecondSalt = secondSalt, + parentPasswordSecondHash = secondHashEncrypted, + encrypted = true + ) + } } - suspend fun createCoroutine(password: String) = Threads.crypto.executeAndWait { - createSync(password) + suspend fun createCoroutine(password: String, dhKey: ServerDhKey?) = Threads.crypto.executeAndWait { + createSync(password, dhKey) } - - fun parse(obj: JSONObject) = ParentPassword( - parentPasswordHash = obj.getString(HASH), - parentPasswordSecondHash = obj.getString(SECOND_HASH), - parentPasswordSecondSalt = obj.getString(SECOND_SALT) - ) } fun serialize(writer: JsonWriter) { @@ -59,6 +74,8 @@ data class ParentPassword ( writer.name(SECOND_HASH).value(parentPasswordSecondHash) writer.name(SECOND_SALT).value(parentPasswordSecondSalt) + if (encrypted) writer.name(ENCRYPTED).value(true) + writer.endObject() } } \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt b/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt index 87a0b68..21409e9 100644 --- a/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt +++ b/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt @@ -46,6 +46,7 @@ data class ServerDataStatus( val newUserList: ServerUserList?, val pendingKeyRequests: List, val keyResponses: List, + val dh: ServerDhKey?, val fullVersionUntil: Long, val message: String?, val apiLevel: Int @@ -63,6 +64,7 @@ data class ServerDataStatus( private const val NEW_USER_LIST = "users" private const val PENDING_KEY_REQUESTS = "krq" private const val KEY_RESPONSES = "kr" + private const val DH = "dh" private const val FULL_VERSION_UNTIL = "fullVersion" private const val MESSAGE = "message" private const val API_LEVEL = "apiLevel" @@ -80,6 +82,7 @@ data class ServerDataStatus( var newUserList: ServerUserList? = null var pendingKeyRequests = emptyList() var keyResponses = emptyList() + var dh: ServerDhKey? = null var fullVersionUntil: Long? = null var message: String? = null var apiLevel = 0 @@ -99,6 +102,7 @@ data class ServerDataStatus( NEW_USER_LIST -> newUserList = ServerUserList.parse(reader) PENDING_KEY_REQUESTS -> pendingKeyRequests = ServerKeyRequest.parseList(reader) KEY_RESPONSES -> keyResponses = ServerKeyResponse.parseList(reader) + DH -> dh = ServerDhKey.parse(reader) FULL_VERSION_UNTIL -> fullVersionUntil = reader.nextLong() MESSAGE -> message = reader.nextString() API_LEVEL -> apiLevel = reader.nextInt() @@ -120,6 +124,7 @@ data class ServerDataStatus( newUserList = newUserList, pendingKeyRequests = pendingKeyRequests, keyResponses = keyResponses, + dh = dh, fullVersionUntil = fullVersionUntil!!, message = message, apiLevel = apiLevel @@ -1264,4 +1269,31 @@ data class ServerKeyResponse( fun parseList(reader: JsonReader) = parseJsonArray(reader) { parse(reader) } } +} + +data class ServerDhKey(val version: String, val key: ByteArray) { + companion object { + private const val VERSION = "v" + private const val KEY = "k" + + fun parse(reader: JsonReader): ServerDhKey { + var version: String? = null + var key: ByteArray? = null + + reader.beginObject() + while (reader.hasNext()) { + when (reader.nextName()) { + VERSION -> version = reader.nextString() + KEY -> key = reader.nextString().parseBase64() + else -> reader.skipValue() + } + } + reader.endObject() + + return ServerDhKey( + version = version!!, + key = key!! + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/manage/child/advanced/password/SetChildPasswordDialogFragment.kt b/app/src/main/java/io/timelimit/android/ui/manage/child/advanced/password/SetChildPasswordDialogFragment.kt index 2a46056..542f4a4 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/child/advanced/password/SetChildPasswordDialogFragment.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/child/advanced/password/SetChildPasswordDialogFragment.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2020 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,8 @@ import android.view.ViewGroup import androidx.fragment.app.FragmentManager import androidx.lifecycle.Observer import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import io.timelimit.android.async.Threads +import io.timelimit.android.coroutines.executeAndWait import io.timelimit.android.coroutines.runAsync import io.timelimit.android.data.model.UserType import io.timelimit.android.databinding.SetChildPasswordDialogFragmentBinding @@ -77,10 +79,12 @@ class SetChildPasswordDialogFragment: BottomSheetDialogFragment() { dismissAllowingStateLoss() runAsync { + val dhKey = Threads.database.executeAndWait { auth.logic.database.config().getLastDhKeySync() } + auth.tryDispatchParentAction( SetChildPasswordAction( childId = childId, - newPassword = ParentPassword.createCoroutine(password) + newPassword = ParentPassword.createCoroutine(password, dhKey) ) ) } diff --git a/app/src/main/java/io/timelimit/android/ui/manage/child/advanced/password/UpdateChildPasswordViewModel.kt b/app/src/main/java/io/timelimit/android/ui/manage/child/advanced/password/UpdateChildPasswordViewModel.kt index 1967d7e..82c7b66 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/child/advanced/password/UpdateChildPasswordViewModel.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/child/advanced/password/UpdateChildPasswordViewModel.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -83,8 +83,10 @@ class UpdateChildPasswordViewModel(application: Application): AndroidViewModel(a PasswordHashing.hashSyncWithSalt(oldPassword, userEntry.secondPasswordSalt) } + val dhKey = Threads.database.executeAndWait { logic.database.config().getLastDhKeySync() } + val action = ChildChangePasswordAction( - password = ParentPassword.createCoroutine(newPassword) + password = ParentPassword.createCoroutine(newPassword, dhKey) ) ApplyActionUtil.applyChildAction( diff --git a/app/src/main/java/io/timelimit/android/ui/manage/parent/password/change/ChangeParentPasswordViewModel.kt b/app/src/main/java/io/timelimit/android/ui/manage/parent/password/change/ChangeParentPasswordViewModel.kt index 83e1a48..5082dfe 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/parent/password/change/ChangeParentPasswordViewModel.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/parent/password/change/ChangeParentPasswordViewModel.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -99,7 +99,7 @@ class ChangeParentPasswordViewModel(application: Application): AndroidViewModel( PasswordHashing.hashSyncWithSalt(oldPassword, userEntry.secondPasswordSalt) } - val newPasswordHash = ParentPassword.createCoroutine(newPassword) + val newPasswordHash = ParentPassword.createCoroutine(newPassword, null) if (BuildConfig.DEBUG) { Log.d(LOG_TAG, "created hashs") diff --git a/app/src/main/java/io/timelimit/android/ui/manage/parent/password/restore/RestoreParentPasswordViewModel.kt b/app/src/main/java/io/timelimit/android/ui/manage/parent/password/restore/RestoreParentPasswordViewModel.kt index 804118c..a44f8a6 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/parent/password/restore/RestoreParentPasswordViewModel.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/parent/password/restore/RestoreParentPasswordViewModel.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2020 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -78,7 +78,7 @@ class RestoreParentPasswordViewModel(application: Application): AndroidViewModel runAsync { try { - val parentPassword = ParentPassword.createCoroutine(newPassword) + val parentPassword = ParentPassword.createCoroutine(newPassword, null) val api = logic.serverLogic.getServerConfigCoroutine().api api.recoverPasswordByMailToken(mailAuthToken!!, parentPassword) diff --git a/app/src/main/java/io/timelimit/android/ui/setup/device/SetupDeviceModel.kt b/app/src/main/java/io/timelimit/android/ui/setup/device/SetupDeviceModel.kt index a432e2d..6e7542c 100644 --- a/app/src/main/java/io/timelimit/android/ui/setup/device/SetupDeviceModel.kt +++ b/app/src/main/java/io/timelimit/android/ui/setup/device/SetupDeviceModel.kt @@ -72,6 +72,7 @@ class SetupDeviceModel(application: Application): AndroidViewModel(application) var realUserId = userId var realAllowedAppsCategory = allowedAppsCategory val defaultCategories = DefaultCategories.with(getApplication()) + val dhKey = Threads.database.executeAndWait { logic.database.config().getLastDhKeySync() } val isUserAnChild = when (userId) { SetupDeviceFragment.NEW_PARENT -> { @@ -85,7 +86,7 @@ class SetupDeviceModel(application: Application): AndroidViewModel(application) name = username, timeZone = logic.timeApi.getSystemTimeZone().id, userType = UserType.Parent, - password = ParentPassword.createCoroutine(password) + password = ParentPassword.createCoroutine(password, dhKey) ) ) @@ -103,7 +104,7 @@ class SetupDeviceModel(application: Application): AndroidViewModel(application) timeZone = logic.timeApi.getSystemTimeZone().id, userType = UserType.Child, password = if (password.isEmpty()) null else ParentPassword.createCoroutine( - password + password, dhKey ) ) ) diff --git a/app/src/main/java/io/timelimit/android/ui/setup/parent/SetupParentModeModel.kt b/app/src/main/java/io/timelimit/android/ui/setup/parent/SetupParentModeModel.kt index edfd887..46fe2f6 100644 --- a/app/src/main/java/io/timelimit/android/ui/setup/parent/SetupParentModeModel.kt +++ b/app/src/main/java/io/timelimit/android/ui/setup/parent/SetupParentModeModel.kt @@ -93,7 +93,7 @@ class SetupParentModeModel(application: Application): AndroidViewModel(applicati val registerResponse = api.createFamilyByMailToken( mailToken = mailAuthToken.value!!, - parentPassword = ParentPassword.createCoroutine(parentPassword), + parentPassword = ParentPassword.createCoroutine(parentPassword, null), parentDevice = NewDeviceInfo( model = deviceModelName ), diff --git a/app/src/main/java/io/timelimit/android/ui/user/create/AddUserModel.kt b/app/src/main/java/io/timelimit/android/ui/user/create/AddUserModel.kt index 57a6d5d..44b9cc1 100644 --- a/app/src/main/java/io/timelimit/android/ui/user/create/AddUserModel.kt +++ b/app/src/main/java/io/timelimit/android/ui/user/create/AddUserModel.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,8 @@ package io.timelimit.android.ui.user.create import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData +import io.timelimit.android.async.Threads +import io.timelimit.android.coroutines.executeAndWait import io.timelimit.android.coroutines.runAsync import io.timelimit.android.data.IdGenerator import io.timelimit.android.data.model.AppRecommendation @@ -45,13 +47,15 @@ class AddUserModel(application: Application): AndroidViewModel(application) { createUserLock.withLock { statusInternal.value = AddUserModelStatus.Working + val dhKey = Threads.database.executeAndWait { logic.database.config().getLastDhKeySync() } + when (type) { UserType.Parent -> { if( model.tryDispatchParentAction( AddUserAction( name = name, - password = ParentPassword.createCoroutine(password), + password = ParentPassword.createCoroutine(password, dhKey), userType = UserType.Parent, userId = IdGenerator.generateId(), timeZone = logic.timeApi.getSystemTimeZone().id @@ -74,7 +78,7 @@ class AddUserModel(application: Application): AndroidViewModel(application) { val actions = ArrayList(listOf( AddUserAction( name = name, - password = if (password.isEmpty()) null else ParentPassword.createCoroutine(password), + password = if (password.isEmpty()) null else ParentPassword.createCoroutine(password, dhKey), userType = UserType.Child, userId = childId, timeZone = logic.timeApi.getSystemTimeZone().id