diff --git a/res/layout/fragment_key_pair_migration_success_bottom_sheet.xml b/res/layout/fragment_key_pair_migration_success_bottom_sheet.xml
new file mode 100644
index 0000000000..eaff60652e
--- /dev/null
+++ b/res/layout/fragment_key_pair_migration_success_bottom_sheet.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java
index 20dbd7e6f6..b64bafbe8d 100644
--- a/src/org/thoughtcrime/securesms/ApplicationContext.java
+++ b/src/org/thoughtcrime/securesms/ApplicationContext.java
@@ -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.");
diff --git a/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt
index 3d10e33ee4..813160526a 100644
--- a/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt
@@ -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) {
diff --git a/src/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt
index 43901fa7b3..557db6b311 100644
--- a/src/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt
@@ -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)
+ }
}
}
diff --git a/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt
index abc9d8bb3e..a3c6850c77 100644
--- a/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt
@@ -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
diff --git a/src/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt b/src/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt
index fb0d4ad151..3dbbfe2ebc 100644
--- a/src/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt
+++ b/src/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt
@@ -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
diff --git a/src/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationBottomSheet.kt b/src/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationBottomSheet.kt
index ede0e74d55..cadc589be8 100644
--- a/src/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationBottomSheet.kt
+++ b/src/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationBottomSheet.kt
@@ -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
diff --git a/src/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationSuccessBottomSheet.kt b/src/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationSuccessBottomSheet.kt
new file mode 100644
index 0000000000..7355f4ecf1
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationSuccessBottomSheet.kt
@@ -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(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()
+ }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceProtocol.kt
index 4f37c49c73..ba2fe07836 100644
--- a/src/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceProtocol.kt
+++ b/src/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceProtocol.kt
@@ -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)
}
}
}
diff --git a/src/org/thoughtcrime/securesms/loki/utilities/KeyPairUtilities.kt b/src/org/thoughtcrime/securesms/loki/utilities/KeyPairUtilities.kt
index ff7f0fd37b..6213d3214c 100644
--- a/src/org/thoughtcrime/securesms/loki/utilities/KeyPairUtilities.kt
+++ b/src/org/thoughtcrime/securesms/loki/utilities/KeyPairUtilities.kt
@@ -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)
+ }
}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
index 2069274cd7..2f6346bcce 100644
--- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
+++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
@@ -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
}