mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +02:00
Add support for encrypted second password hashes
This commit is contained in:
parent
c135a640c6
commit
15168bed66
14 changed files with 207 additions and 35 deletions
79
app/src/main/java/io/timelimit/android/crypto/DH.kt
Normal file
79
app/src/main/java/io/timelimit/android/crypto/DH.kt
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,8 @@ object ApplyServerDataStatus {
|
|||
database.config().setServerApiLevelSync(status.apiLevel)
|
||||
}
|
||||
|
||||
status.dh?.also { database.config().setLastDhKeySync(it) }
|
||||
|
||||
var didCreateNewActions = false
|
||||
|
||||
run {
|
||||
|
|
|
@ -25,7 +25,8 @@ data class ClientDataStatus(
|
|||
val categories: Map<String, CategoryDataStatus>,
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 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()
|
||||
}
|
||||
}
|
|
@ -46,6 +46,7 @@ data class ServerDataStatus(
|
|||
val newUserList: ServerUserList?,
|
||||
val pendingKeyRequests: List<ServerKeyRequest>,
|
||||
val keyResponses: List<ServerKeyResponse>,
|
||||
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<ServerKeyRequest>()
|
||||
var keyResponses = emptyList<ServerKeyResponse>()
|
||||
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
|
||||
|
@ -1265,3 +1270,30 @@ 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!!
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 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(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 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")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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
|
||||
),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 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<ParentAction>(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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue