srctree

Robin Linden parent 835c487b fedcfa3a
Clean up password management

inlinesplit
atox/src/main/kotlin/BootReceiver.kt added: 52, removed: 41, total 11
@@ -13,7 +13,7 @@ class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
(context.applicationContext as App).component.inject(this)
toxStarter.tryLoadTox()
toxStarter.tryLoadTox(null)
}
}
}
 
atox/src/main/kotlin/ToxService.kt added: 52, removed: 41, total 11
@@ -76,7 +76,7 @@ class ToxService : LifecycleService() {
super.onCreate()
 
if (!tox.started) {
if (toxStarter.tryLoadTox() != ToxSaveStatus.Ok) {
if (toxStarter.tryLoadTox(null) != ToxSaveStatus.Ok) {
Log.e(TAG, "Tox service started without a Tox save")
stopSelf()
}
 
atox/src/main/kotlin/settings/Settings.kt added: 52, removed: 41, total 11
@@ -24,10 +24,6 @@ enum class BootstrapNodeSource {
class Settings @Inject constructor(private val ctx: Context) {
private val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
 
companion object {
var password: String? = null
}
 
var theme: Int
get() = preferences.getInt("theme", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
set(theme) {
 
atox/src/main/kotlin/tox/ToxStarter.kt added: 52, removed: 41, total 11
@@ -33,17 +33,17 @@ class ToxStarter @Inject constructor(
private val context: Context,
private val settings: Settings,
) {
fun startTox(save: ByteArray? = null): ToxSaveStatus {
fun startTox(save: ByteArray? = null, password: String? = null): ToxSaveStatus {
listenerCallbacks.setUp(eventListener)
listenerCallbacks.setUp(avEventListener)
val options =
SaveOptions(save, settings.udpEnabled, settings.proxyType, settings.proxyAddress, settings.proxyPort)
try {
tox.isBootstrapNeeded = true
tox.start(options, Settings.password, eventListener, avEventListener)
tox.start(options, password, eventListener, avEventListener)
} catch (e: ToxNewException) {
Log.e(TAG, e.message)
return testToxSave(options, Settings.password)
return testToxSave(options, password)
} catch (e: ToxDecryptionException) {
Log.e(TAG, e.message)
return ToxSaveStatus.Encrypted
@@ -59,9 +59,9 @@ class ToxStarter @Inject constructor(
stopService(Intent(this, ToxService::class.java))
}
 
fun tryLoadTox(): ToxSaveStatus {
fun tryLoadTox(password: String?): ToxSaveStatus {
tryLoadSave()?.also { save ->
val status = startTox(save)
val status = startTox(save, password)
if (status == ToxSaveStatus.Ok) {
userManager.verifyExists(tox.publicKey)
}
 
atox/src/main/kotlin/ui/addcontact/AddContactViewModel.kt added: 52, removed: 41, total 11
@@ -20,7 +20,7 @@ class AddContactViewModel @Inject constructor(
val contacts: LiveData<List<Contact>> = contactManager.getAll().asLiveData()
 
fun isToxRunning() = tox.started
fun tryLoadTox(): Boolean = toxStarter.tryLoadTox() == ToxSaveStatus.Ok
fun tryLoadTox(): Boolean = toxStarter.tryLoadTox(null) == ToxSaveStatus.Ok
 
fun addContact(toxId: ToxID, message: String) = contactManager.add(toxId, message)
}
 
atox/src/main/kotlin/ui/contactlist/ContactListFragment.kt added: 52, removed: 41, total 11
@@ -30,7 +30,6 @@ import ltd.evilcorp.atox.databinding.ContactListViewItemBinding
import ltd.evilcorp.atox.databinding.FragmentContactListBinding
import ltd.evilcorp.atox.databinding.FriendRequestItemBinding
import ltd.evilcorp.atox.databinding.NavHeaderContactListBinding
import ltd.evilcorp.atox.settings.Settings
import ltd.evilcorp.atox.truncated
import ltd.evilcorp.atox.ui.BaseFragment
import ltd.evilcorp.atox.ui.ReceiveShareDialog
@@ -286,7 +285,6 @@ class ContactListFragment :
.setTitle(R.string.quit_confirm)
.setPositiveButton(R.string.quit) { _, _ ->
viewModel.quitTox()
Settings.password = null
activity?.finishAffinity()
}
.setNegativeButton(R.string.cancel) { _, _ -> }
@@ -299,13 +297,13 @@ class ContactListFragment :
 
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!viewModel.isToxRunning()) viewModel.tryLoadTox()
if (!viewModel.isToxRunning()) viewModel.tryLoadTox(null)
}
 
override fun onStart() {
super.onStart()
if (!viewModel.isToxRunning()) {
when (val status = viewModel.tryLoadTox()) {
when (val status = viewModel.tryLoadTox(null)) {
ToxSaveStatus.BadProxyHost, ToxSaveStatus.BadProxyPort,
ToxSaveStatus.BadProxyType, ToxSaveStatus.ProxyNotFound -> {
Toast.makeText(requireContext(), getString(R.string.warn_proxy_broken), Toast.LENGTH_LONG).show()
@@ -325,23 +323,25 @@ class ContactListFragment :
.setTitle(getString(R.string.unlock_profile))
.setView(passwordEdit)
.setPositiveButton(android.R.string.ok) { _, _ ->
Settings.password = passwordEdit.text.toString()
if (viewModel.tryLoadTox() == ToxSaveStatus.Ok) {
val password = passwordEdit.text.toString()
if (viewModel.tryLoadTox(password) == ToxSaveStatus.Ok) {
// Hack to reload fragment.
parentFragmentManager.beginTransaction().detach(this).commitAllowingStateLoss()
parentFragmentManager.beginTransaction().attach(this).commitAllowingStateLoss()
} else {
Settings.password = null
Toast.makeText(
requireContext(),
getString(R.string.incorrect_password),
Toast.LENGTH_LONG,
).show()
}
}
.setNegativeButton(R.string.cancel) { _, _ -> }
.setOnDismissListener {
if (!viewModel.isToxRunning()) {
activity?.finishAffinity()
}
}
.setNegativeButton(R.string.cancel) { _, _ -> activity?.finishAffinity() }
.setOnDismissListener { activity?.finishAffinity() }
.show()
}
ToxSaveStatus.Ok -> {
 
atox/src/main/kotlin/ui/contactlist/ContactListViewModel.kt added: 52, removed: 41, total 11
@@ -16,7 +16,6 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ltd.evilcorp.atox.R
import ltd.evilcorp.atox.settings.Settings
import ltd.evilcorp.atox.tox.ToxStarter
import ltd.evilcorp.core.vo.Contact
import ltd.evilcorp.core.vo.FriendRequest
@@ -51,7 +50,7 @@ class ContactListViewModel @Inject constructor(
val friendRequests: LiveData<List<FriendRequest>> = friendRequestManager.getAll().asLiveData()
 
fun isToxRunning() = tox.started
fun tryLoadTox(): ToxSaveStatus = toxStarter.tryLoadTox()
fun tryLoadTox(password: String?): ToxSaveStatus = toxStarter.tryLoadTox(password)
fun quitTox() = toxStarter.stopTox()
 
fun acceptFriendRequest(friendRequest: FriendRequest) = friendRequestManager.accept(friendRequest)
@@ -77,7 +76,7 @@ class ContactListViewModel @Inject constructor(
FileInputStream(fd.fileDescriptor).use { ios ->
val saveData = ios.readBytes()
val save = SaveOptions(saveData, true, ProxyType.None, "", 0)
val toast = when (val status = testToxSave(save, Settings.password)) {
val toast = when (val status = testToxSave(save, tox.password)) {
ToxSaveStatus.Ok -> context.getText(R.string.tox_save_exported)
else -> context.getString(R.string.tox_save_export_failure, status.name)
}
 
atox/src/main/kotlin/ui/create_profile/CreateProfileFragment.kt added: 52, removed: 41, total 11
@@ -17,7 +17,6 @@ import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import ltd.evilcorp.atox.R
import ltd.evilcorp.atox.databinding.FragmentProfileBinding
import ltd.evilcorp.atox.settings.Settings
import ltd.evilcorp.atox.ui.BaseFragment
import ltd.evilcorp.atox.vmFactory
import ltd.evilcorp.core.vo.User
@@ -47,12 +46,11 @@ class CreateProfileFragment : BaseFragment<FragmentProfileBinding>(FragmentProfi
.setTitle(R.string.unlock_profile)
.setView(passwordEdit)
.setPositiveButton(android.R.string.ok) { _, _ ->
Settings.password = passwordEdit.text.toString()
if (viewModel.startTox(save) == ToxSaveStatus.Ok) {
val password = passwordEdit.text.toString()
if (viewModel.startTox(save, password) == ToxSaveStatus.Ok) {
viewModel.verifyUserExists(viewModel.publicKey)
findNavController().popBackStack()
} else {
Settings.password = null
Toast.makeText(
requireContext(),
getString(R.string.incorrect_password),
 
atox/src/main/kotlin/ui/create_profile/CreateProfileViewModel.kt added: 52, removed: 41, total 11
@@ -19,7 +19,7 @@ class CreateProfileViewModel @Inject constructor(
) : ViewModel() {
val publicKey: PublicKey by lazy { tox.publicKey }
 
fun startTox(save: ByteArray? = null): ToxSaveStatus = toxStarter.startTox(save)
fun startTox(save: ByteArray? = null, password: String? = null): ToxSaveStatus = toxStarter.startTox(save, password)
fun tryImportToxSave(uri: Uri): ByteArray? = resolver.openInputStream(uri)?.readBytes()
fun create(user: User) = userManager.create(user)
fun verifyUserExists(publicKey: PublicKey) = userManager.verifyExists(publicKey)
 
atox/src/main/kotlin/ui/settings/SettingsViewModel.kt added: 52, removed: 41, total 11
@@ -104,13 +104,14 @@ class SettingsViewModel @Inject constructor(
return
}
 
val password = tox.password
toxStarter.stopTox()
 
viewModelScope.launch {
while (tox.started) {
delay(200)
}
toxStarter.tryLoadTox()
toxStarter.tryLoadTox(password)
_committed.value = true
}
}
 
domain/src/main/kotlin/tox/Tox.kt added: 52, removed: 41, total 11
@@ -2,9 +2,11 @@ package ltd.evilcorp.domain.tox
 
import android.util.Log
import im.tox.tox4j.core.exceptions.ToxBootstrapException
import im.tox.tox4j.crypto.ToxCryptoConstants
import im.tox.tox4j.impl.jni.ToxCryptoImpl
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.random.Random
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
@@ -41,6 +43,20 @@ class Tox @Inject constructor(
private var toxAvRunning = false
 
private var passkey: ByteArray? = null
var password: String? = null
private set
 
fun changePassword(new: String?) {
passkey = if (new.isNullOrEmpty()) {
null
} else {
val salt = ByteArray(ToxCryptoConstants.SaltLength())
Random.Default.nextBytes(salt)
ToxCryptoImpl.passKeyDeriveWithSalt(new.toByteArray(), salt)
}
password = new
save()
}
 
private lateinit var tox: ToxWrapper
 
@@ -58,6 +74,7 @@ class Tox @Inject constructor(
)
}
 
this.password = password
started = true
 
fun loadContacts() = launch {