174 lines
7.5 KiB
Kotlin
Raw Normal View History

2019-06-04 16:37:45 +10:00
package org.thoughtcrime.securesms.loki
2019-06-05 16:05:08 +10:00
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
2019-06-05 13:33:54 +10:00
import android.content.Intent
2019-06-04 16:37:45 +10:00
import android.os.Bundle
2019-07-17 10:26:06 +10:00
import android.view.View
import android.view.inputmethod.InputMethodManager
2019-06-05 16:05:08 +10:00
import android.widget.Toast
2019-08-19 15:34:21 +10:00
import kotlinx.android.synthetic.main.activity_seed.*
import network.loki.messenger.R
2019-06-18 12:49:21 +10:00
import org.thoughtcrime.securesms.ApplicationContext
2019-06-04 16:37:45 +10:00
import org.thoughtcrime.securesms.BaseActionBarActivity
2019-06-05 13:33:54 +10:00
import org.thoughtcrime.securesms.ConversationListActivity
2019-06-04 16:37:45 +10:00
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
2019-06-05 13:33:54 +10:00
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.IdentityDatabase
2019-08-19 15:34:21 +10:00
import org.thoughtcrime.securesms.util.Hex
2019-06-05 13:33:54 +10:00
import org.thoughtcrime.securesms.util.TextSecurePreferences
2019-08-19 15:34:21 +10:00
import org.whispersystems.curve25519.Curve25519
2019-06-28 16:08:27 +10:00
import org.whispersystems.libsignal.util.KeyHelper
2019-06-04 16:37:45 +10:00
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
2019-09-05 09:38:36 +10:00
import org.whispersystems.signalservice.loki.utilities.Analytics
2019-06-05 15:33:31 +10:00
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
2019-06-04 16:37:45 +10:00
import java.io.File
import java.io.FileOutputStream
2019-08-19 15:34:21 +10:00
class SeedActivity : BaseActionBarActivity() {
2019-06-04 16:37:45 +10:00
private lateinit var languageFileDirectory: File
2019-07-17 10:26:06 +10:00
private var mode = Mode.Register
set(newValue) { field = newValue; updateUI() }
2019-08-19 15:34:21 +10:00
private var seed: ByteArray? = null
2019-06-04 16:37:45 +10:00
set(newValue) { field = newValue; updateMnemonic() }
private var mnemonic: String? = null
set(newValue) { field = newValue; updateMnemonicTextView() }
2019-07-17 10:26:06 +10:00
// region Types
enum class Mode { Register, Restore }
// endregion
2019-06-04 16:37:45 +10:00
// region Lifecycle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
2019-08-19 15:34:21 +10:00
setContentView(R.layout.activity_seed)
2019-06-04 16:37:45 +10:00
setUpLanguageFileDirectory()
2019-08-19 15:34:21 +10:00
updateSeed()
2019-06-05 16:05:08 +10:00
copyButton.setOnClickListener { copy() }
2019-07-17 10:26:06 +10:00
toggleModeButton.setOnClickListener { toggleMode() }
registerOrRestoreButton.setOnClickListener { registerOrRestore() }
2019-06-04 16:37:45 +10:00
}
// endregion
// region General
private fun setUpLanguageFileDirectory() {
val languages = listOf( "english", "japanese", "portuguese", "spanish" )
val directory = File(applicationInfo.dataDir)
for (language in languages) {
val fileName = "$language.txt"
if (directory.list().contains(fileName)) { continue }
val inputStream = assets.open("mnemonic/$fileName")
val file = File(directory, fileName)
val outputStream = FileOutputStream(file)
val buffer = ByteArray(1024)
while (true) {
val count = inputStream.read(buffer)
if (count < 0) { break }
outputStream.write(buffer, 0, count)
}
inputStream.close()
outputStream.close()
}
languageFileDirectory = directory
}
// endregion
// region Updating
2019-08-19 15:34:21 +10:00
private fun updateSeed() {
2019-08-21 09:50:03 +10:00
val seed = Curve25519.getInstance(Curve25519.BEST).generateSeed(16)
try {
IdentityKeyUtil.generateIdentityKeyPair(this, seed + seed)
} catch (exception: Exception) {
return updateSeed()
}
this.seed = seed
2019-06-04 16:37:45 +10:00
}
2019-07-17 10:26:06 +10:00
private fun updateUI() {
seedExplanationTextView1.visibility = if (mode == Mode.Register) View.VISIBLE else View.GONE
mnemonicTextView.visibility = if (mode == Mode.Register) View.VISIBLE else View.GONE
copyButton.visibility = if (mode == Mode.Register) View.VISIBLE else View.GONE
seedExplanationTextView2.visibility = if (mode == Mode.Restore) View.VISIBLE else View.GONE
mnemonicEditText.visibility = if (mode == Mode.Restore) View.VISIBLE else View.GONE
val toggleModeButtonTitleID = if (mode == Mode.Register) R.string.activity_key_pair_toggle_mode_button_title_1 else R.string.activity_key_pair_toggle_mode_button_title_2
toggleModeButton.setText(toggleModeButtonTitleID)
val registerOrRestoreButtonTitleID = if (mode == Mode.Register) R.string.activity_key_pair_register_or_restore_button_title_1 else R.string.activity_key_pair_register_or_restore_button_title_2
registerOrRestoreButton.setText(registerOrRestoreButtonTitleID)
if (mode == Mode.Restore) {
mnemonicEditText.requestFocus()
} else {
mnemonicEditText.clearFocus()
val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(mnemonicEditText.windowToken, 0)
}
}
2019-06-04 16:37:45 +10:00
private fun updateMnemonic() {
2019-08-19 15:34:21 +10:00
val hexEncodedSeed = Hex.toStringCondensed(seed)
mnemonic = MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed)
2019-06-04 16:37:45 +10:00
}
private fun updateMnemonicTextView() {
mnemonicTextView.text = mnemonic!!
}
// endregion
2019-06-05 13:33:54 +10:00
// region Interaction
2019-06-05 16:05:08 +10:00
private fun copy() {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Mnemonic", mnemonic)
2019-06-05 16:05:08 +10:00
clipboard.primaryClip = clip
Toast.makeText(this, R.string.activity_key_pair_mnemonic_copied_message, Toast.LENGTH_SHORT).show()
}
2019-07-17 10:26:06 +10:00
private fun toggleMode() {
mode = when (mode) {
Mode.Register -> Mode.Restore
Mode.Restore -> Mode.Register
}
}
private fun registerOrRestore() {
2019-08-19 15:34:21 +10:00
var seed: ByteArray
2019-07-17 11:31:19 +10:00
when (mode) {
2019-08-19 15:34:21 +10:00
Mode.Register -> seed = this.seed!!
2019-07-17 11:31:19 +10:00
Mode.Restore -> {
val mnemonic = mnemonicEditText.text.toString()
2019-08-01 09:26:06 +10:00
try {
2019-08-19 15:34:21 +10:00
val hexEncodedSeed = MnemonicCodec(languageFileDirectory).decode(mnemonic)
seed = Hex.fromStringCondensed(hexEncodedSeed)
2019-08-01 09:26:06 +10:00
} catch (e: Exception) {
val message = if (e is MnemonicCodec.DecodingError) e.description else MnemonicCodec.DecodingError.Generic.description
return Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
2019-07-17 11:31:19 +10:00
}
}
2019-08-21 09:50:03 +10:00
val hexEncodedSeed = Hex.toStringCondensed(seed)
IdentityKeyUtil.save(this, IdentityKeyUtil.lokiSeedKey, hexEncodedSeed)
2019-08-19 15:34:21 +10:00
if (seed.count() == 16) seed = seed + seed
2019-08-21 09:50:03 +10:00
if (mode == Mode.Restore) {
IdentityKeyUtil.generateIdentityKeyPair(this, seed)
}
2019-08-19 15:34:21 +10:00
val keyPair = IdentityKeyUtil.getIdentityKeyPair(this)
2019-07-17 11:31:19 +10:00
val publicKey = keyPair.publicKey
val hexEncodedPublicKey = keyPair.hexEncodedPublicKey
2019-06-28 16:08:27 +10:00
val registrationID = KeyHelper.generateRegistrationId(false)
TextSecurePreferences.setLocalRegistrationId(this, registrationID)
2019-06-05 13:33:54 +10:00
DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(hexEncodedPublicKey), publicKey,
2019-06-25 09:28:37 +10:00
IdentityDatabase.VerifiedStatus.VERIFIED, true, System.currentTimeMillis(), true)
2019-06-05 13:33:54 +10:00
TextSecurePreferences.setLocalNumber(this, hexEncodedPublicKey)
2019-06-05 14:56:10 +10:00
TextSecurePreferences.setPromptedPushRegistration(this, true)
val application = ApplicationContext.getInstance(this)
application.setUpP2PAPI()
application.startLongPollingIfNeeded()
2019-09-05 09:38:36 +10:00
when (mode) {
Mode.Register -> Analytics.shared.track("Seed Created")
Mode.Restore -> Analytics.shared.track("Seed Restored")
}
2019-06-05 13:33:54 +10:00
startActivity(Intent(this, ConversationListActivity::class.java))
2019-06-05 14:56:10 +10:00
finish()
2019-06-05 13:33:54 +10:00
}
// endregion
2019-06-04 16:37:45 +10:00
}