mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-12 13:37:56 +00:00
Merge pull request #390 from loki-project/session-protocol
Session Protocol
This commit is contained in:
9
res/drawable/ic_shield.xml
Normal file
9
res/drawable/ic_shield.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:pathData="M466.5,83.7l-192,-80a48.15,48.15 0,0 0,-36.9 0l-192,80C27.7,91.1 16,108.6 16,128c0,198.5 114.5,335.7 221.5,380.3 11.8,4.9 25.1,4.9 36.9,0C360.1,472.6 496,349.3 496,128c0,-19.4 -11.7,-36.9 -29.5,-44.3zM256.1,446.3l-0.1,-381 175.9,73.3c-3.3,151.4 -82.1,261.1 -175.8,307.7z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
60
res/layout/fragment_key_pair_migration_bottom_sheet.xml
Normal file
60
res/layout/fragment_key_pair_migration_bottom_sheet.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?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="Session IDs Just Got Better"
|
||||
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="We’ve upgraded Session IDs to make them even more private and secure. We recommend upgrading to a new Session ID now."
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:text="You will lose existing contacts and conversations, but you’ll gain even more privacy and security. You will need to upgrade your Session ID eventually, but you can choose to delay the upgrade if you need to save contacts or conversations."
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Session.Button.Common.UnimportantOutline"
|
||||
android:id="@+id/upgradeNowButton"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginTop="@dimen/very_large_spacing"
|
||||
android:text="Upgrade Now" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Session.Button.Common.UnimportantOutline"
|
||||
android:id="@+id/upgradeLaterButton"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:text="Upgrade Later" />
|
||||
|
||||
</LinearLayout>
|
@@ -588,7 +588,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
});
|
||||
}
|
||||
|
||||
public void clearData() {
|
||||
public void clearAllData() {
|
||||
String token = TextSecurePreferences.getFCMToken(this);
|
||||
if (token != null && !token.isEmpty()) {
|
||||
LokiPushNotificationManager.unregister(token, this);
|
||||
|
@@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.api.SessionProtocolImpl;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
||||
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceOpenGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
||||
@@ -157,6 +158,7 @@ public class SignalCommunicationModule {
|
||||
DatabaseFactory.getLokiThreadDatabase(context),
|
||||
DatabaseFactory.getLokiMessageDatabase(context),
|
||||
DatabaseFactory.getLokiPreKeyBundleDatabase(context),
|
||||
new SessionProtocolImpl(context),
|
||||
new SessionResetImplementation(context),
|
||||
DatabaseFactory.getLokiUserDatabase(context),
|
||||
DatabaseFactory.getGroupDatabase(context),
|
||||
|
@@ -65,6 +65,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
||||
import org.thoughtcrime.securesms.loki.api.SessionProtocolImpl;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
|
||||
@@ -259,7 +260,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
|
||||
SessionResetProtocol sessionResetProtocol = new SessionResetImplementation(context);
|
||||
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
||||
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, DatabaseFactory.getSSKDatabase(context), sessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator());
|
||||
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, DatabaseFactory.getSSKDatabase(context), new SessionProtocolImpl(context), sessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator());
|
||||
|
||||
SignalServiceContent content = cipher.decrypt(envelope);
|
||||
|
||||
|
@@ -6,8 +6,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
@@ -31,14 +29,9 @@ import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob
|
||||
import org.thoughtcrime.securesms.loki.dialogs.ConversationOptionsBottomSheet
|
||||
import org.thoughtcrime.securesms.loki.dialogs.LightThemeFeatureIntroBottomSheet
|
||||
import org.thoughtcrime.securesms.loki.dialogs.MultiDeviceRemovalBottomSheet
|
||||
import org.thoughtcrime.securesms.loki.dialogs.UserDetailsBottomSheet
|
||||
import org.thoughtcrime.securesms.loki.dialogs.*
|
||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation
|
||||
import org.thoughtcrime.securesms.loki.utilities.*
|
||||
@@ -57,6 +50,7 @@ import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiD
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol
|
||||
import org.whispersystems.signalservice.loki.utilities.toHexString
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate {
|
||||
private lateinit var glide: GlideRequests
|
||||
@@ -178,7 +172,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
// Clear all data if this is a secondary device
|
||||
if (TextSecurePreferences.getMasterHexEncodedPublicKey(this) != null) {
|
||||
TextSecurePreferences.setWasUnlinked(this, true)
|
||||
ApplicationContext.getInstance(this).clearData()
|
||||
ApplicationContext.getInstance(this).clearAllData()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,36 +185,15 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
if (hasViewedSeed || !isMasterDevice) {
|
||||
seedReminderView.visibility = View.GONE
|
||||
}
|
||||
|
||||
// Multi device removal sheet
|
||||
if (!TextSecurePreferences.getHasSeenMultiDeviceRemovalSheet(this)) {
|
||||
TextSecurePreferences.setHasSeenMultiDeviceRemovalSheet(this)
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||
val deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey)
|
||||
if (deviceLinks.isNotEmpty()) {
|
||||
val bottomSheet = MultiDeviceRemovalBottomSheet()
|
||||
bottomSheet.onOKTapped = {
|
||||
bottomSheet.dismiss()
|
||||
}
|
||||
bottomSheet.onLinkTapped = {
|
||||
bottomSheet.dismiss()
|
||||
val url = "https://getsession.org/faq"
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
startActivity(intent)
|
||||
}
|
||||
bottomSheet.show(supportFragmentManager, bottomSheet.tag)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Light theme introduction sheet
|
||||
if (!TextSecurePreferences.hasSeenLightThemeIntroSheet(this) &&
|
||||
UiModeUtilities.isDayUiMode(this)) {
|
||||
TextSecurePreferences.setHasSeenLightThemeIntroSheet(this)
|
||||
val bottomSheet = LightThemeFeatureIntroBottomSheet()
|
||||
bottomSheet.show(supportFragmentManager, bottomSheet.tag)
|
||||
return
|
||||
}
|
||||
// Show key pair migration sheet if needed
|
||||
if (KeyPairUtilities.hasV2KeyPair(this)) { return }
|
||||
val lastNudge = TextSecurePreferences.getLastKeyPairMigrationNudge(this)
|
||||
val nudgeInterval: Long = 3 * 24 * 60 * 60 * 1000 // 3 days
|
||||
val nudge = (Date().time - lastNudge > nudgeInterval)
|
||||
if (!nudge) { return }
|
||||
val keyPairMigrationSheet = KeyPairMigrationBottomSheet()
|
||||
keyPairMigrationSheet.show(supportFragmentManager, keyPairMigrationSheet.tag)
|
||||
TextSecurePreferences.setLastKeyPairMigrationNudge(this, Date().time)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
|
101
src/org/thoughtcrime/securesms/loki/api/SessionProtocolImpl.kt
Normal file
101
src/org/thoughtcrime/securesms/loki/api/SessionProtocolImpl.kt
Normal file
@@ -0,0 +1,101 @@
|
||||
package org.thoughtcrime.securesms.loki.api
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.goterl.lazycode.lazysodium.LazySodiumAndroid
|
||||
import com.goterl.lazycode.lazysodium.SodiumAndroid
|
||||
import com.goterl.lazycode.lazysodium.interfaces.Box
|
||||
import com.goterl.lazycode.lazysodium.interfaces.Sign
|
||||
import com.goterl.lazycode.lazysodium.utils.KeyPair
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.loki.utilities.KeyPairUtilities
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.libsignal.util.Hex
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type.CLOSED_GROUP_CIPHERTEXT_VALUE
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER_VALUE
|
||||
import org.whispersystems.signalservice.loki.api.SnodeAPI
|
||||
import org.whispersystems.signalservice.loki.api.crypto.SessionProtocol
|
||||
import org.whispersystems.signalservice.loki.utilities.removing05PrefixIfNeeded
|
||||
import org.whispersystems.signalservice.loki.utilities.toHexString
|
||||
|
||||
class SessionProtocolImpl(private val context: Context) : SessionProtocol {
|
||||
|
||||
override fun encrypt(plaintext: ByteArray, recipientHexEncodedX25519PublicKey: String): ByteArray {
|
||||
val userED25519KeyPair = KeyPairUtilities.getUserED25519KeyPair(context) ?: throw SessionProtocol.Exception.NoUserED25519KeyPair
|
||||
val recipientX25519PublicKey = Hex.fromStringCondensed(recipientHexEncodedX25519PublicKey.removing05PrefixIfNeeded())
|
||||
val sodium = LazySodiumAndroid(SodiumAndroid())
|
||||
|
||||
val verificationData = plaintext + userED25519KeyPair.publicKey.asBytes + recipientX25519PublicKey
|
||||
val signature = ByteArray(Sign.BYTES)
|
||||
try {
|
||||
sodium.cryptoSignDetached(signature, verificationData, verificationData.size.toLong(), userED25519KeyPair.secretKey.asBytes)
|
||||
} catch (exception: Exception) {
|
||||
Log.d("Loki", "Couldn't sign message due to error: $exception.")
|
||||
throw SessionProtocol.Exception.SigningFailed
|
||||
}
|
||||
val plaintextWithMetadata = plaintext + userED25519KeyPair.publicKey.asBytes + signature
|
||||
val ciphertext = ByteArray(plaintextWithMetadata.size + Box.SEALBYTES)
|
||||
try {
|
||||
sodium.cryptoBoxSeal(ciphertext, plaintextWithMetadata, plaintextWithMetadata.size.toLong(), recipientX25519PublicKey)
|
||||
} catch (exception: Exception) {
|
||||
Log.d("Loki", "Couldn't encrypt message due to error: $exception.")
|
||||
throw SessionProtocol.Exception.EncryptionFailed
|
||||
}
|
||||
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
override fun decrypt(envelope: SignalServiceEnvelope): Pair<ByteArray, String> {
|
||||
val ciphertext = envelope.content ?: throw SessionProtocol.Exception.NoData
|
||||
val recipientX25519PrivateKey: ByteArray
|
||||
val recipientX25519PublicKey: ByteArray
|
||||
when (envelope.type) {
|
||||
UNIDENTIFIED_SENDER_VALUE -> {
|
||||
recipientX25519PrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
||||
recipientX25519PublicKey = Hex.fromStringCondensed(TextSecurePreferences.getLocalNumber(context).removing05PrefixIfNeeded())
|
||||
}
|
||||
CLOSED_GROUP_CIPHERTEXT_VALUE -> {
|
||||
val hexEncodedGroupPublicKey = envelope.source
|
||||
val sskDB = DatabaseFactory.getSSKDatabase(context)
|
||||
if (!sskDB.isSSKBasedClosedGroup(hexEncodedGroupPublicKey)) { throw SessionProtocol.Exception.InvalidGroupPublicKey }
|
||||
val hexEncodedGroupPrivateKey = sskDB.getClosedGroupPrivateKey(hexEncodedGroupPublicKey) ?: throw SessionProtocol.Exception.NoGroupPrivateKey
|
||||
recipientX25519PrivateKey = Hex.fromStringCondensed(hexEncodedGroupPrivateKey)
|
||||
recipientX25519PublicKey = Hex.fromStringCondensed(hexEncodedGroupPublicKey.removing05PrefixIfNeeded())
|
||||
}
|
||||
else -> throw AssertionError()
|
||||
}
|
||||
val sodium = LazySodiumAndroid(SodiumAndroid())
|
||||
val signatureSize = Sign.BYTES
|
||||
val ed25519PublicKeySize = Sign.PUBLICKEYBYTES
|
||||
|
||||
// 1. ) Decrypt the message
|
||||
val plaintextWithMetadata = ByteArray(ciphertext.size - Box.SEALBYTES)
|
||||
try {
|
||||
sodium.cryptoBoxSealOpen(plaintextWithMetadata, ciphertext, ciphertext.size.toLong(), recipientX25519PublicKey, recipientX25519PrivateKey)
|
||||
} catch (exception: Exception) {
|
||||
Log.d("Loki", "Couldn't decrypt message due to error: $exception.")
|
||||
throw SessionProtocol.Exception.DecryptionFailed
|
||||
}
|
||||
if (plaintextWithMetadata.size <= (signatureSize + ed25519PublicKeySize)) { throw SessionProtocol.Exception.DecryptionFailed }
|
||||
// 2. ) Get the message parts
|
||||
val signature = plaintextWithMetadata.sliceArray(plaintextWithMetadata.size - signatureSize until plaintextWithMetadata.size)
|
||||
val senderED25519PublicKey = plaintextWithMetadata.sliceArray(plaintextWithMetadata.size - (signatureSize + ed25519PublicKeySize) until plaintextWithMetadata.size - signatureSize)
|
||||
val plaintext = plaintextWithMetadata.sliceArray(0 until plaintextWithMetadata.size - (signatureSize + ed25519PublicKeySize))
|
||||
// 3. ) Verify the signature
|
||||
val verificationData = (plaintext + senderED25519PublicKey + recipientX25519PublicKey)
|
||||
try {
|
||||
val isValid = sodium.cryptoSignVerifyDetached(signature, verificationData, verificationData.size, senderED25519PublicKey)
|
||||
if (!isValid) { throw SessionProtocol.Exception.InvalidSignature }
|
||||
} catch (exception: Exception) {
|
||||
Log.d("Loki", "Couldn't verify message signature due to error: $exception.")
|
||||
throw SessionProtocol.Exception.InvalidSignature
|
||||
}
|
||||
// 4. ) Get the sender's X25519 public key
|
||||
val senderX25519PublicKey = ByteArray(Sign.CURVE25519_PUBLICKEYBYTES)
|
||||
sodium.convertPublicKeyEd25519ToCurve25519(senderX25519PublicKey, senderED25519PublicKey)
|
||||
|
||||
return Pair(plaintext, "05" + senderX25519PublicKey.toHexString())
|
||||
}
|
||||
}
|
@@ -3,13 +3,18 @@ package org.thoughtcrime.securesms.loki.database
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.goterl.lazycode.lazysodium.utils.Key
|
||||
import com.goterl.lazycode.lazysodium.utils.KeyPair
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.database.Database
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import org.thoughtcrime.securesms.loki.utilities.*
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope
|
||||
import org.whispersystems.signalservice.loki.api.Snode
|
||||
import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink
|
||||
import org.whispersystems.signalservice.loki.utilities.toHexString
|
||||
import java.util.*
|
||||
|
||||
class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiAPIDatabaseProtocol {
|
||||
|
@@ -27,13 +27,13 @@ class ClearAllDataDialog : DialogFragment() {
|
||||
|
||||
private fun clearAllData() {
|
||||
if (KeyPairUtilities.hasV2KeyPair(requireContext())) {
|
||||
ApplicationContext.getInstance(context).clearData()
|
||||
ApplicationContext.getInstance(context).clearAllData()
|
||||
} 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).clearData()
|
||||
ApplicationContext.getInstance(context).clearAllData()
|
||||
}
|
||||
dialog.setNegativeButton("Cancel") { _, _ ->
|
||||
// Do nothing
|
||||
|
@@ -0,0 +1,53 @@
|
||||
package org.thoughtcrime.securesms.loki.dialogs
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
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 network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
|
||||
class KeyPairMigrationBottomSheet : BottomSheetDialogFragment() {
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_key_pair_migration_bottom_sheet, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
upgradeNowButton.setOnClickListener { upgradeNow() }
|
||||
upgradeLaterButton.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 upgradeNow() {
|
||||
val applicationContext = requireContext().applicationContext as ApplicationContext
|
||||
dismiss()
|
||||
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()
|
||||
}
|
||||
dialog.setNegativeButton(R.string.cancel) { _, _ ->
|
||||
// Do nothing
|
||||
}
|
||||
dialog.create().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).clearData()
|
||||
ApplicationContext.getInstance(context).clearAllData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,10 @@
|
||||
package org.thoughtcrime.securesms.loki.utilities
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
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 org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.util.Base64
|
||||
@@ -11,6 +13,7 @@ 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.signalservice.loki.utilities.toHexString
|
||||
|
||||
object KeyPairUtilities {
|
||||
|
||||
@@ -49,4 +52,12 @@ object KeyPairUtilities {
|
||||
fun hasV2KeyPair(context: Context): Boolean {
|
||||
return (IdentityKeyUtil.retrieve(context, IdentityKeyUtil.ED25519_SECRET_KEY) != null)
|
||||
}
|
||||
|
||||
fun getUserED25519KeyPair(context: Context): KeyPair? {
|
||||
val hexEncodedED25519PublicKey = IdentityKeyUtil.retrieve(context, IdentityKeyUtil.ED25519_PUBLIC_KEY) ?: return null
|
||||
val hexEncodedED25519SecretKey = IdentityKeyUtil.retrieve(context, IdentityKeyUtil.ED25519_SECRET_KEY) ?: return null
|
||||
val ed25519PublicKey = Key.fromBase64String(hexEncodedED25519PublicKey)
|
||||
val ed25519SecretKey = Key.fromBase64String(hexEncodedED25519SecretKey)
|
||||
return KeyPair(ed25519PublicKey, ed25519SecretKey)
|
||||
}
|
||||
}
|
@@ -1342,5 +1342,13 @@ public class TextSecurePreferences {
|
||||
public static void setLastSnodePoolRefreshDate(Context context, Date date) {
|
||||
setLongPreference(context, "last_snode_pool_refresh_date", date.getTime());
|
||||
}
|
||||
|
||||
public static long getLastKeyPairMigrationNudge(Context context) {
|
||||
return getLongPreference(context, "last_key_pair_migration_nudge", 0);
|
||||
}
|
||||
|
||||
public static void setLastKeyPairMigrationNudge(Context context, long newValue) {
|
||||
setLongPreference(context, "last_key_pair_migration_nudge", newValue);
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
Reference in New Issue
Block a user