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-06-04 16:37:45 +10:00
|
|
|
import kotlinx.android.synthetic.main.activity_key_pair.*
|
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.R
|
|
|
|
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
|
|
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
2019-06-04 16:37:45 +10:00
|
|
|
import org.whispersystems.libsignal.IdentityKeyPair
|
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-06-05 15:33:31 +10:00
|
|
|
import org.whispersystems.signalservice.loki.utilities.hexEncodedPrivateKey
|
|
|
|
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
|
2019-06-04 16:37:45 +10:00
|
|
|
import java.io.File
|
|
|
|
import java.io.FileOutputStream
|
|
|
|
|
|
|
|
class KeyPairActivity : BaseActionBarActivity() {
|
|
|
|
private lateinit var languageFileDirectory: File
|
2019-07-17 10:26:06 +10:00
|
|
|
private var mode = Mode.Register
|
|
|
|
set(newValue) { field = newValue; updateUI() }
|
2019-06-04 16:37:45 +10:00
|
|
|
private var keyPair: IdentityKeyPair? = null
|
|
|
|
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)
|
|
|
|
setContentView(R.layout.activity_key_pair)
|
|
|
|
setUpLanguageFileDirectory()
|
|
|
|
updateKeyPair()
|
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
|
|
|
|
private fun updateKeyPair() {
|
|
|
|
IdentityKeyUtil.generateIdentityKeys(this)
|
|
|
|
keyPair = IdentityKeyUtil.getIdentityKeyPair(this)
|
|
|
|
}
|
|
|
|
|
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-06-05 10:02:06 +10:00
|
|
|
mnemonic = MnemonicCodec(languageFileDirectory).encode(keyPair!!.hexEncodedPrivateKey)
|
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)
|
|
|
|
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-07-17 11:31:19 +10:00
|
|
|
val keyPair: IdentityKeyPair
|
|
|
|
when (mode) {
|
|
|
|
Mode.Register -> keyPair = this.keyPair!!
|
|
|
|
Mode.Restore -> {
|
|
|
|
val mnemonic = mnemonicEditText.text.toString()
|
|
|
|
val hexEncodedPrivateKey = MnemonicCodec(languageFileDirectory).decode(mnemonic)
|
|
|
|
IdentityKeyUtil.generateIdentityKeyPair(this, hexEncodedPrivateKey)
|
|
|
|
keyPair = IdentityKeyUtil.getIdentityKeyPair(this)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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)
|
2019-06-18 13:23:58 +10:00
|
|
|
val application = ApplicationContext.getInstance(this)
|
|
|
|
application.setUpP2PAPI()
|
2019-07-22 10:50:35 +10:00
|
|
|
application.startLongPollingIfNeeded()
|
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
|
|
|
}
|