You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
7.2 KiB
Kotlin
169 lines
7.2 KiB
Kotlin
package org.thoughtcrime.securesms.loki.redesign.activities
|
|
|
|
import android.content.ClipData
|
|
import android.content.ClipboardManager
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.graphics.Typeface
|
|
import android.net.Uri
|
|
import android.os.Bundle
|
|
import android.os.Handler
|
|
import android.text.Spannable
|
|
import android.text.SpannableStringBuilder
|
|
import android.text.method.LinkMovementMethod
|
|
import android.text.style.ClickableSpan
|
|
import android.text.style.StyleSpan
|
|
import android.view.View
|
|
import android.widget.Toast
|
|
import kotlinx.android.synthetic.main.activity_register.*
|
|
import network.loki.messenger.R
|
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
|
import org.thoughtcrime.securesms.database.Address
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
import org.thoughtcrime.securesms.database.IdentityDatabase
|
|
import org.thoughtcrime.securesms.loki.redesign.utilities.push
|
|
import org.thoughtcrime.securesms.loki.redesign.utilities.setUpActionBarSessionLogo
|
|
import org.thoughtcrime.securesms.util.Base64
|
|
import org.thoughtcrime.securesms.util.Hex
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
import org.whispersystems.curve25519.Curve25519
|
|
import org.whispersystems.libsignal.ecc.Curve
|
|
import org.whispersystems.libsignal.ecc.ECKeyPair
|
|
import org.whispersystems.libsignal.util.KeyHelper
|
|
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
|
|
import java.io.File
|
|
import java.io.FileOutputStream
|
|
|
|
class RegisterActivity : BaseActionBarActivity() {
|
|
private var seed: ByteArray? = null
|
|
private var keyPair: ECKeyPair? = null
|
|
set(value) { field = value; updatePublicKeyTextView() }
|
|
|
|
// region Lifecycle
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(R.layout.activity_register)
|
|
setUpLanguageFileDirectory()
|
|
setUpActionBarSessionLogo()
|
|
registerButton.setOnClickListener { register() }
|
|
copyButton.setOnClickListener { copyPublicKey() }
|
|
val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy")
|
|
termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
termsExplanation.setSpan(object : ClickableSpan() {
|
|
|
|
override fun onClick(widget: View) {
|
|
openURL("https://getsession.org/legal/#tos")
|
|
}
|
|
}, 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
termsExplanation.setSpan(object : ClickableSpan() {
|
|
|
|
override fun onClick(widget: View) {
|
|
openURL("https://getsession.org/legal/#privacy-policy")
|
|
}
|
|
}, 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
termsTextView.movementMethod = LinkMovementMethod.getInstance()
|
|
termsTextView.text = termsExplanation
|
|
updateKeyPair()
|
|
}
|
|
// 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()
|
|
}
|
|
}
|
|
// endregion
|
|
|
|
// region Updating
|
|
private fun updateKeyPair() {
|
|
val seedCandidate = Curve25519.getInstance(Curve25519.BEST).generateSeed(16)
|
|
try {
|
|
this.keyPair = Curve.generateKeyPair(seedCandidate + seedCandidate) // Validate the seed
|
|
} catch (exception: Exception) {
|
|
return updateKeyPair()
|
|
}
|
|
seed = seedCandidate
|
|
}
|
|
|
|
private fun updatePublicKeyTextView() {
|
|
val hexEncodedPublicKey = keyPair!!.hexEncodedPublicKey
|
|
val characterCount = hexEncodedPublicKey.count()
|
|
var count = 0
|
|
val limit = 32
|
|
fun animate() {
|
|
val numberOfIndexesToShuffle = 32 - count
|
|
val indexesToShuffle = (0 until characterCount).shuffled().subList(0, numberOfIndexesToShuffle)
|
|
var mangledHexEncodedPublicKey = hexEncodedPublicKey
|
|
for (index in indexesToShuffle) {
|
|
try {
|
|
mangledHexEncodedPublicKey = mangledHexEncodedPublicKey.substring(0, index) + "0123456789abcdef__".random() + mangledHexEncodedPublicKey.substring(index + 1, mangledHexEncodedPublicKey.count())
|
|
} catch (exception: Exception) {
|
|
// Do nothing
|
|
}
|
|
}
|
|
count += 1
|
|
if (count < limit) {
|
|
publicKeyTextView.text = mangledHexEncodedPublicKey
|
|
Handler().postDelayed({
|
|
animate()
|
|
}, 32)
|
|
} else {
|
|
publicKeyTextView.text = hexEncodedPublicKey
|
|
}
|
|
}
|
|
animate()
|
|
}
|
|
// endregion
|
|
|
|
// region Interaction
|
|
private fun register() {
|
|
IdentityKeyUtil.save(this, IdentityKeyUtil.lokiSeedKey, Hex.toStringCondensed(seed))
|
|
IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(keyPair!!.publicKey.serialize()))
|
|
IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(keyPair!!.privateKey.serialize()))
|
|
val userHexEncodedPublicKey = keyPair!!.hexEncodedPublicKey
|
|
val registrationID = KeyHelper.generateRegistrationId(false)
|
|
TextSecurePreferences.setLocalRegistrationId(this, registrationID)
|
|
DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey),
|
|
IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED,
|
|
true, System.currentTimeMillis(), true)
|
|
TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey)
|
|
TextSecurePreferences.setRestorationTime(this, 0)
|
|
TextSecurePreferences.setHasViewedSeed(this, false)
|
|
val intent = Intent(this, DisplayNameActivity::class.java)
|
|
push(intent)
|
|
}
|
|
|
|
private fun copyPublicKey() {
|
|
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
val clip = ClipData.newPlainText("Session ID", keyPair!!.hexEncodedPublicKey)
|
|
clipboard.primaryClip = clip
|
|
Toast.makeText(this, R.string.activity_register_public_key_copied_message, Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun openURL(url: String) {
|
|
try {
|
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
startActivity(intent)
|
|
} catch (e: Exception) {
|
|
Toast.makeText(this, "Couldn't open link", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
// endregion
|
|
} |