mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-12 03:27:42 +00:00
Implement auto-migration
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:padding="@dimen/large_spacing"
|
||||
app:behavior_hideable="true"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_shield"
|
||||
android:tint="?android:textColorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:text="Upgrade Successful!"
|
||||
android:textStyle="bold"
|
||||
android:textSize="@dimen/large_font_size" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:text="Your new and improved Session ID is:"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<TextView
|
||||
style="@style/SessionIDTextView"
|
||||
android:id="@+id/sessionIDTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="18dp"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Session.Button.Common.UnimportantOutline"
|
||||
android:id="@+id/copyButton"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginTop="@dimen/very_large_spacing"
|
||||
android:text="Copy" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Session.Button.Common.UnimportantOutline"
|
||||
android:id="@+id/okButton"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:text="@string/ok" />
|
||||
|
||||
</LinearLayout>
|
@@ -588,14 +588,19 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
});
|
||||
}
|
||||
|
||||
public void clearAllData() {
|
||||
public void clearAllData(boolean isMigratingToV2KeyPair) {
|
||||
String token = TextSecurePreferences.getFCMToken(this);
|
||||
if (token != null && !token.isEmpty()) {
|
||||
LokiPushNotificationManager.unregister(token, this);
|
||||
}
|
||||
boolean wasUnlinked = TextSecurePreferences.getWasUnlinked(this);
|
||||
String displayName = TextSecurePreferences.getProfileName(this);
|
||||
boolean isUsingFCM = TextSecurePreferences.isUsingFCM(this);
|
||||
TextSecurePreferences.clearAll(this);
|
||||
TextSecurePreferences.setWasUnlinked(this, wasUnlinked);
|
||||
if (isMigratingToV2KeyPair) {
|
||||
TextSecurePreferences.setIsMigratingKeyPair(this, true);
|
||||
TextSecurePreferences.setIsUsingFCM(this, isUsingFCM);
|
||||
TextSecurePreferences.setProfileName(this, displayName);
|
||||
}
|
||||
MasterSecretUtil.clear(this);
|
||||
if (!deleteDatabase("signal.db")) {
|
||||
Log.d("Loki", "Failed to delete database.");
|
||||
|
@@ -169,11 +169,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
}
|
||||
this.broadcastReceiver = broadcastReceiver
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("blockedContactsChanged"))
|
||||
// Clear all data if this is a secondary device
|
||||
if (TextSecurePreferences.getMasterHexEncodedPublicKey(this) != null) {
|
||||
TextSecurePreferences.setWasUnlinked(this, true)
|
||||
ApplicationContext.getInstance(this).clearAllData()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@@ -185,7 +180,11 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
if (hasViewedSeed || !isMasterDevice) {
|
||||
seedReminderView.visibility = View.GONE
|
||||
}
|
||||
// Show key pair migration sheet if needed
|
||||
showKeyPairMigrationSheetIfNeeded()
|
||||
showKeyPairMigrationSuccessSheetIfNeeded()
|
||||
}
|
||||
|
||||
private fun showKeyPairMigrationSheetIfNeeded() {
|
||||
if (KeyPairUtilities.hasV2KeyPair(this)) { return }
|
||||
val lastNudge = TextSecurePreferences.getLastKeyPairMigrationNudge(this)
|
||||
val nudgeInterval: Long = 3 * 24 * 60 * 60 * 1000 // 3 days
|
||||
@@ -196,6 +195,13 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
TextSecurePreferences.setLastKeyPairMigrationNudge(this, Date().time)
|
||||
}
|
||||
|
||||
private fun showKeyPairMigrationSuccessSheetIfNeeded() {
|
||||
if (!KeyPairUtilities.hasV2KeyPair(this) || !TextSecurePreferences.getIsMigratingKeyPair(this)) { return }
|
||||
val keyPairMigrationSuccessSheet = KeyPairMigrationSuccessBottomSheet()
|
||||
keyPairMigrationSuccessSheet.show(supportFragmentManager, keyPairMigrationSuccessSheet.tag)
|
||||
TextSecurePreferences.setIsMigratingKeyPair(this, false)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == CreateClosedGroupActivity.closedGroupCreatedResultCode) {
|
||||
|
@@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialog
|
||||
import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialogDelegate
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation
|
||||
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol
|
||||
import org.thoughtcrime.securesms.loki.utilities.KeyPairUtilities
|
||||
import org.thoughtcrime.securesms.loki.utilities.push
|
||||
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
|
||||
import org.thoughtcrime.securesms.loki.utilities.show
|
||||
@@ -44,9 +45,11 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
|
||||
fakeChatView.startAnimating()
|
||||
registerButton.setOnClickListener { register() }
|
||||
restoreButton.setOnClickListener { restore() }
|
||||
// linkButton.setOnClickListener { linkDevice() }
|
||||
if (TextSecurePreferences.getWasUnlinked(this)) {
|
||||
Toast.makeText(this, R.string.activity_landing_device_unlinked_dialog_title, Toast.LENGTH_LONG).show()
|
||||
if (TextSecurePreferences.getIsMigratingKeyPair(this)) {
|
||||
val displayName = TextSecurePreferences.getProfileName(this)
|
||||
if (displayName != null) {
|
||||
KeyPairUtilities.migrateToV2KeyPair(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -305,7 +305,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
val dialog = AlertDialog.Builder(this)
|
||||
dialog.setMessage("You’re upgrading to a new Session ID. This will give you improved privacy and security, but it will clear ALL app data. Contacts and conversations will be lost. Proceed?")
|
||||
dialog.setPositiveButton(R.string.yes) { _, _ ->
|
||||
applicationContext.clearAllData()
|
||||
applicationContext.clearAllData(true)
|
||||
}
|
||||
dialog.setNegativeButton(R.string.cancel) { _, _ ->
|
||||
// Do nothing
|
||||
|
@@ -27,13 +27,13 @@ class ClearAllDataDialog : DialogFragment() {
|
||||
|
||||
private fun clearAllData() {
|
||||
if (KeyPairUtilities.hasV2KeyPair(requireContext())) {
|
||||
ApplicationContext.getInstance(context).clearAllData()
|
||||
ApplicationContext.getInstance(context).clearAllData(false)
|
||||
} else {
|
||||
val dialog = AlertDialog.Builder(requireContext())
|
||||
val message = "We’ve upgraded the way Session IDs are generated, so you will be unable to restore your current Session ID."
|
||||
dialog.setMessage(message)
|
||||
dialog.setPositiveButton("Yes") { _, _ ->
|
||||
ApplicationContext.getInstance(context).clearAllData()
|
||||
ApplicationContext.getInstance(context).clearAllData(false)
|
||||
}
|
||||
dialog.setNegativeButton("Cancel") { _, _ ->
|
||||
// Do nothing
|
||||
|
@@ -43,7 +43,7 @@ class KeyPairMigrationBottomSheet : BottomSheetDialogFragment() {
|
||||
val dialog = AlertDialog.Builder(requireContext())
|
||||
dialog.setMessage("You’re upgrading to a new Session ID. This will give you improved privacy and security, but it will clear ALL app data. Contacts and conversations will be lost. Proceed?")
|
||||
dialog.setPositiveButton(R.string.yes) { _, _ ->
|
||||
applicationContext.clearAllData()
|
||||
applicationContext.clearAllData(true)
|
||||
}
|
||||
dialog.setNegativeButton(R.string.cancel) { _, _ ->
|
||||
// Do nothing
|
||||
|
@@ -0,0 +1,58 @@
|
||||
package org.thoughtcrime.securesms.loki.dialogs
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.Toast
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import kotlinx.android.synthetic.main.fragment_key_pair_migration_bottom_sheet.*
|
||||
import kotlinx.android.synthetic.main.fragment_key_pair_migration_success_bottom_sheet.*
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
|
||||
|
||||
class KeyPairMigrationSuccessBottomSheet : BottomSheetDialogFragment() {
|
||||
|
||||
private val sessionID by lazy {
|
||||
TextSecurePreferences.getLocalNumber(requireContext())
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_key_pair_migration_success_bottom_sheet, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
sessionIDTextView.text = sessionID
|
||||
copyButton.setOnClickListener { copySessionID() }
|
||||
okButton.setOnClickListener { dismiss() }
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
// Expand the bottom sheet by default
|
||||
dialog.setOnShowListener {
|
||||
val d = dialog as BottomSheetDialog
|
||||
val bottomSheet = d.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)
|
||||
BottomSheetBehavior.from(bottomSheet!!).setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
}
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun copySessionID() {
|
||||
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText("Session ID", sessionID)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
@@ -197,7 +197,7 @@ object MultiDeviceProtocol {
|
||||
FileServerAPI.shared.removeDeviceLink(slaveDeviceLink) // Attempt to clean up on the file server
|
||||
}
|
||||
TextSecurePreferences.setWasUnlinked(context, true)
|
||||
ApplicationContext.getInstance(context).clearAllData()
|
||||
ApplicationContext.getInstance(context).clearAllData(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,30 @@
|
||||
package org.thoughtcrime.securesms.loki.utilities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.goterl.lazycode.lazysodium.LazySodiumAndroid
|
||||
import com.goterl.lazycode.lazysodium.SodiumAndroid
|
||||
import com.goterl.lazycode.lazysodium.utils.Key
|
||||
import com.goterl.lazycode.lazysodium.utils.KeyPair
|
||||
import kotlinx.android.synthetic.main.activity_pn_mode.*
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
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.activities.HomeActivity
|
||||
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.DjbECPrivateKey
|
||||
import org.whispersystems.libsignal.ecc.DjbECPublicKey
|
||||
import org.whispersystems.libsignal.ecc.ECKeyPair
|
||||
import org.whispersystems.libsignal.util.KeyHelper
|
||||
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
|
||||
import org.whispersystems.signalservice.loki.utilities.toHexString
|
||||
|
||||
object KeyPairUtilities {
|
||||
@@ -60,4 +72,32 @@ object KeyPairUtilities {
|
||||
val ed25519SecretKey = Key.fromBase64String(hexEncodedED25519SecretKey)
|
||||
return KeyPair(ed25519PublicKey, ed25519SecretKey)
|
||||
}
|
||||
|
||||
fun migrateToV2KeyPair(context: AppCompatActivity) {
|
||||
val keyPairGenerationResult = generate()
|
||||
val seed = keyPairGenerationResult.seed
|
||||
val ed25519KeyPair = keyPairGenerationResult.ed25519KeyPair
|
||||
val x25519KeyPair = keyPairGenerationResult.x25519KeyPair
|
||||
store(context, seed, ed25519KeyPair, x25519KeyPair)
|
||||
val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey
|
||||
val registrationID = KeyHelper.generateRegistrationId(false)
|
||||
TextSecurePreferences.setLocalRegistrationId(context, registrationID)
|
||||
DatabaseFactory.getIdentityDatabase(context).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey),
|
||||
IdentityKeyUtil.getIdentityKeyPair(context).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED,
|
||||
true, System.currentTimeMillis(), true)
|
||||
TextSecurePreferences.setLocalNumber(context, userHexEncodedPublicKey)
|
||||
TextSecurePreferences.setRestorationTime(context, 0)
|
||||
TextSecurePreferences.setHasViewedSeed(context, false)
|
||||
TextSecurePreferences.setHasSeenWelcomeScreen(context, true)
|
||||
TextSecurePreferences.setPromptedPushRegistration(context, true)
|
||||
TextSecurePreferences.setIsUsingFCM(context, TextSecurePreferences.isUsingFCM(context))
|
||||
TextSecurePreferences.setHasSeenMultiDeviceRemovalSheet(context)
|
||||
TextSecurePreferences.setHasSeenLightThemeIntroSheet(context)
|
||||
val application = ApplicationContext.getInstance(context)
|
||||
application.setUpStorageAPIIfNeeded()
|
||||
application.setUpP2PAPIIfNeeded()
|
||||
val intent = Intent(context, HomeActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
context.show(intent)
|
||||
}
|
||||
}
|
@@ -1350,5 +1350,13 @@ public class TextSecurePreferences {
|
||||
public static void setLastKeyPairMigrationNudge(Context context, long newValue) {
|
||||
setLongPreference(context, "last_key_pair_migration_nudge", newValue);
|
||||
}
|
||||
|
||||
public static boolean getIsMigratingKeyPair(Context context) {
|
||||
return getBooleanPreference(context, "is_migrating_key_pair", false);
|
||||
}
|
||||
|
||||
public static void setIsMigratingKeyPair(Context context, boolean newValue) {
|
||||
setBooleanPreference(context, "is_migrating_key_pair", newValue);
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
Reference in New Issue
Block a user