From 15d7a073f9b697e9f596876aa027cb9dfaa7ef52 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 11:29:52 +1000 Subject: [PATCH 01/17] Hopefully fix file loading crash --- .../ApplicationPreferencesActivity.java | 17 +++++----- .../securesms/DeviceListFragment.java | 17 +++++----- .../database/loaders/DeviceListLoader.java | 10 ++---- .../loki/activities/LinkedDevicesLoader.kt | 6 ++-- .../loki/activities/RegisterActivity.kt | 23 -------------- .../loki/activities/RestoreActivity.kt | 31 +++---------------- .../securesms/loki/activities/SeedActivity.kt | 8 +++-- .../dialogs/LinkDeviceMasterModeDialog.kt | 20 ++++++------ .../loki/dialogs/LinkDeviceSlaveModeDialog.kt | 12 ++++--- .../securesms/loki/dialogs/SeedDialog.kt | 19 +++++++----- .../loki/utilities/MnemonicUtilities.kt | 27 +++++----------- .../widgets/ProfilePreference.java | 12 +++---- 12 files changed, 73 insertions(+), 129 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java index 4400f061a8..7f75443aed 100644 --- a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java +++ b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java @@ -29,16 +29,17 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Build.VERSION; import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; + import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; -import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; -import android.util.Log; -import android.widget.Toast; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.loki.activities.HomeActivity; @@ -51,11 +52,8 @@ import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec; import org.whispersystems.signalservice.loki.utilities.HexEncodingKt; -import java.io.File; - import network.loki.messenger.R; /** @@ -337,13 +335,12 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA case PREFERENCE_CATEGORY_QR_CODE: break; case PREFERENCE_CATEGORY_LINKED_DEVICES: break; case PREFERENCE_CATEGORY_SEED: - File languageFileDirectory = new File(getContext().getApplicationInfo().dataDir); try { String hexEncodedSeed = IdentityKeyUtil.retrieve(getContext(), IdentityKeyUtil.lokiSeedKey); if (hexEncodedSeed == null) { hexEncodedSeed = HexEncodingKt.getHexEncodedPrivateKey(IdentityKeyUtil.getIdentityKeyPair(getContext())); // Legacy account } - String seed = new MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.Companion.getEnglish()); + String seed = ""; new AlertDialog.Builder(getContext()) .setTitle(R.string.activity_settings_seed_dialog_title) .setMessage(seed) diff --git a/src/org/thoughtcrime/securesms/DeviceListFragment.java b/src/org/thoughtcrime/securesms/DeviceListFragment.java index ea400e0481..54b17038f6 100644 --- a/src/org/thoughtcrime/securesms/DeviceListFragment.java +++ b/src/org/thoughtcrime/securesms/DeviceListFragment.java @@ -4,11 +4,6 @@ import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.fragment.app.ListFragment; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.appcompat.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -19,6 +14,12 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.ListFragment; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; + import com.melnykov.fab.FloatingActionButton; import org.thoughtcrime.securesms.database.DatabaseFactory; @@ -26,12 +27,10 @@ import org.thoughtcrime.securesms.database.loaders.DeviceListLoader; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.devicelist.Device; import org.thoughtcrime.securesms.loki.dialogs.DeviceEditingOptionsBottomSheet; -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.ViewUtil; import org.whispersystems.libsignal.util.guava.Function; -import java.io.File; import java.util.List; import java.util.Locale; @@ -48,7 +47,6 @@ public class DeviceListFragment extends ListFragment private static final String TAG = DeviceListFragment.class.getSimpleName(); - private File languageFileDirectory; private Locale locale; private View empty; private View progressContainer; @@ -85,7 +83,6 @@ public class DeviceListFragment extends ListFragment @Override public void onActivityCreated(Bundle bundle) { super.onActivityCreated(bundle); - this.languageFileDirectory = MnemonicUtilities.getLanguageFileDirectory(getContext()); getLoaderManager().initLoader(0, null, this); getListView().setOnItemClickListener(this); } @@ -107,7 +104,7 @@ public class DeviceListFragment extends ListFragment empty.setVisibility(View.GONE); progressContainer.setVisibility(View.VISIBLE); - return new DeviceListLoader(getActivity(), languageFileDirectory); + return new DeviceListLoader(getActivity()); } @Override diff --git a/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java b/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java index 711d557ce9..1f868d28ca 100644 --- a/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java +++ b/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.database.loaders; import android.content.Context; + import androidx.annotation.NonNull; import com.annimon.stream.Stream; @@ -8,13 +9,10 @@ import com.annimon.stream.Stream; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.devicelist.Device; import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities; import org.thoughtcrime.securesms.util.AsyncLoader; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec; import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol; -import java.io.File; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -23,11 +21,9 @@ import java.util.Set; public class DeviceListLoader extends AsyncLoader> { private static final String TAG = DeviceListLoader.class.getSimpleName(); - private MnemonicCodec mnemonicCodec; - public DeviceListLoader(Context context, File languageFileDirectory) { + public DeviceListLoader(Context context) { super(context); - this.mnemonicCodec = new MnemonicCodec(languageFileDirectory); } @Override @@ -45,7 +41,7 @@ public class DeviceListLoader extends AsyncLoader> { } private Device mapToDevice(@NonNull String hexEncodedPublicKey) { - String shortId = MnemonicUtilities.getFirst3Words(mnemonicCodec, hexEncodedPublicKey); + String shortId = ""; String name = DatabaseFactory.getLokiUserDatabase(getContext()).getDisplayName(hexEncodedPublicKey); return new Device(hexEncodedPublicKey, shortId, name); } diff --git a/src/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt b/src/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt index 785566b2d7..663f83dc84 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt @@ -13,8 +13,10 @@ import java.io.File class LinkedDevicesLoader(context: Context) : AsyncLoader>(context) { private val mnemonicCodec by lazy { - val languageFileDirectory = File(context.applicationInfo.dataDir) - MnemonicCodec(languageFileDirectory) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(context, fileName) + } + MnemonicCodec(loadFileContents) } override fun loadInBackground(): List? { diff --git a/src/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt index 9d5a71c85b..1ebf1ca1fd 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt @@ -44,7 +44,6 @@ class RegisterActivity : BaseActionBarActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_register) - setUpLanguageFileDirectory() setUpActionBarSessionLogo() registerButton.setOnClickListener { register() } copyButton.setOnClickListener { copyPublicKey() } @@ -69,28 +68,6 @@ class RegisterActivity : BaseActionBarActivity() { } // 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) diff --git a/src/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt index 195801191c..5c4ffa7863 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt @@ -18,6 +18,7 @@ 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.utilities.MnemonicUtilities import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo import org.thoughtcrime.securesms.util.Base64 @@ -31,12 +32,10 @@ import java.io.File import java.io.FileOutputStream class RestoreActivity : BaseActionBarActivity() { - private lateinit var languageFileDirectory: File // region Lifecycle override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setUpLanguageFileDirectory() setUpActionBarSessionLogo() setContentView(R.layout.activity_restore) mnemonicEditText.imeOptions = mnemonicEditText.imeOptions or 16777216 // Always use incognito keyboard @@ -61,34 +60,14 @@ class RestoreActivity : BaseActionBarActivity() { } // 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 Interaction private fun restore() { val mnemonic = mnemonicEditText.text.toString() try { - val hexEncodedSeed = MnemonicCodec(languageFileDirectory).decode(mnemonic) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(this, fileName) + } + val hexEncodedSeed = MnemonicCodec(loadFileContents).decode(mnemonic) var seed = Hex.fromStringCondensed(hexEncodedSeed) IdentityKeyUtil.save(this, IdentityKeyUtil.lokiSeedKey, Hex.toStringCondensed(seed)) if (seed.size == 16) { seed = seed + seed } diff --git a/src/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt index d0995ee019..c91113aa98 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt @@ -7,12 +7,14 @@ import android.os.Bundle import android.text.Spannable import android.text.SpannableString import android.text.style.ForegroundColorSpan +import android.util.Log import android.widget.LinearLayout import android.widget.Toast import kotlinx.android.synthetic.main.activity_seed.* import network.loki.messenger.R import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities import org.thoughtcrime.securesms.loki.utilities.getColorWithID import org.thoughtcrime.securesms.util.TextSecurePreferences import org.whispersystems.signalservice.loki.crypto.MnemonicCodec @@ -22,12 +24,14 @@ import java.io.File class SeedActivity : BaseActionBarActivity() { private val seed by lazy { - val languageFileDirectory = File(applicationInfo.dataDir) var hexEncodedSeed = IdentityKeyUtil.retrieve(this, IdentityKeyUtil.lokiSeedKey) if (hexEncodedSeed == null) { hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(this).hexEncodedPrivateKey // Legacy account } - MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(this, fileName) + } + MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) } // region Lifecycle diff --git a/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt b/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt index ed55eca238..91edb872c2 100644 --- a/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt +++ b/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt @@ -29,16 +29,15 @@ import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.Device import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLinkingSessionListener class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener { - private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) } private lateinit var contentView: View private var deviceLink: DeviceLink? = null var delegate: LinkDeviceMasterModeDialogDelegate? = null override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(context!!) - contentView = LayoutInflater.from(context!!).inflate(R.layout.dialog_link_device_master_mode, null) + val builder = AlertDialog.Builder(requireContext()) + contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_device_master_mode, null) val size = toPx(128, resources) - val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context!!) + val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext()) val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false) contentView.qrCodeImageView.setImageBitmap(qrCode) contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() } @@ -52,7 +51,7 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene } override fun requestUserAuthorization(deviceLink: DeviceLink) { - if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return } + if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterPublicKey != TextSecurePreferences.getLocalNumber(requireContext()) || this.deviceLink != null) { return } Util.runOnMain { this.deviceLink = deviceLink contentView.qrCodeImageView.visibility = View.GONE @@ -62,7 +61,10 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_master_mode_title_2) contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_master_mode_explanation_2) contentView.mnemonicTextView.visibility = View.VISIBLE - contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), deviceLink.slavePublicKey) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(requireContext(), fileName) + } + contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(loadFileContents), deviceLink.slavePublicKey) contentView.authorizeButton.visibility = View.VISIBLE } } @@ -85,15 +87,15 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene contentView.authorizeButton.visibility = View.GONE } FileServerAPI.shared.addDeviceLink(deviceLink).bind(SnodeAPI.sharedContext) { - MultiDeviceProtocol.signAndSendDeviceLinkMessage(context!!, deviceLink) + MultiDeviceProtocol.signAndSendDeviceLinkMessage(requireContext(), deviceLink) }.success { - TextSecurePreferences.setMultiDevice(context!!, true) + TextSecurePreferences.setMultiDevice(requireContext(), true) }.successUi { delegate?.onDeviceLinkRequestAuthorized() dismiss() }.fail { FileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem - DatabaseFactory.getLokiPreKeyBundleDatabase(context!!).removePreKeyBundle(deviceLink.slavePublicKey) + DatabaseFactory.getLokiPreKeyBundleDatabase(requireContext()).removePreKeyBundle(deviceLink.slavePublicKey) }.failUi { delegate?.onDeviceLinkAuthorizationFailed() dismiss() diff --git a/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt b/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt index d795e3148f..54f5d3d48b 100644 --- a/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt +++ b/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt @@ -21,16 +21,18 @@ import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.Device import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLinkingSessionListener class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener { - private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) } private lateinit var contentView: View private var deviceLink: DeviceLink? = null var delegate: LinkDeviceSlaveModeDialogDelegate? = null override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(context!!) - contentView = LayoutInflater.from(context!!).inflate(R.layout.dialog_link_device_slave_mode, null) + val builder = AlertDialog.Builder(requireContext()) + contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_device_slave_mode, null) val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) - contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), hexEncodedPublicKey) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(requireContext(), fileName) + } + contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(loadFileContents), hexEncodedPublicKey) contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() } builder.setView(contentView) DeviceLinkingSession.shared.startListeningForLinkingRequests() @@ -41,7 +43,7 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener } override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { - if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slavePublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return } + if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slavePublicKey != TextSecurePreferences.getLocalNumber(requireContext()) || this.deviceLink != null) { return } Util.runOnMain { this.deviceLink = deviceLink DeviceLinkingSession.shared.stopListeningForLinkingRequests() diff --git a/src/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt b/src/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt index 59f96ae433..a52e08f606 100644 --- a/src/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt +++ b/src/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt @@ -14,6 +14,7 @@ import android.widget.Toast import kotlinx.android.synthetic.main.dialog_seed.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities import org.whispersystems.signalservice.loki.crypto.MnemonicCodec import org.whispersystems.signalservice.loki.utilities.hexEncodedPrivateKey import java.io.File @@ -21,17 +22,19 @@ import java.io.File class SeedDialog : DialogFragment() { private val seed by lazy { - val languageFileDirectory = File(context!!.applicationInfo.dataDir) - var hexEncodedSeed = IdentityKeyUtil.retrieve(context!!, IdentityKeyUtil.lokiSeedKey) + var hexEncodedSeed = IdentityKeyUtil.retrieve(requireContext(), IdentityKeyUtil.lokiSeedKey) if (hexEncodedSeed == null) { - hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(context!!).hexEncodedPrivateKey // Legacy account + hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(requireContext()).hexEncodedPrivateKey // Legacy account } - MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(requireContext(), fileName) + } + MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(context!!) - val contentView = LayoutInflater.from(context!!).inflate(R.layout.dialog_seed, null) + val builder = AlertDialog.Builder(requireContext()) + val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_seed, null) contentView.seedTextView.text = seed contentView.cancelButton.setOnClickListener { dismiss() } contentView.copyButton.setOnClickListener { copySeed() } @@ -42,10 +45,10 @@ class SeedDialog : DialogFragment() { } private fun copySeed() { - val clipboard = activity!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("Seed", seed) clipboard.setPrimaryClip(clip) - Toast.makeText(context!!, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() dismiss() } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt b/src/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt index c1e10551c2..040419a1df 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt @@ -8,30 +8,17 @@ import java.io.FileOutputStream object MnemonicUtilities { - @JvmStatic - public fun getLanguageFileDirectory(context: Context): File { - val languages = listOf( "english", "japanese", "portuguese", "spanish" ) - val directory = File(context.applicationInfo.dataDir) - for (language in languages) { - val fileName = "$language.txt" - if (directory.list().contains(fileName)) { continue } - val inputStream = context.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) - } + public fun loadFileContents(context: Context, fileName: String): String { + val inputStream = context.assets.open("mnemonic/$fileName.txt") + val size = inputStream.available() + val buffer = ByteArray(size) + inputStream.read(buffer) inputStream.close() - outputStream.close() - } - return directory + return String(buffer) } @JvmStatic public fun getFirst3Words(codec: MnemonicCodec, hexEncodedPublicKey: String): String { - return codec.encode(hexEncodedPublicKey.removing05PrefixIfNeeded()).split(" ").slice(0 until 3).joinToString(" ") + return codec.encode(hexEncodedPublicKey.removing05PrefixIfNeeded()).split(" ").slice(0 until 3).joinToString(" ") } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java index 21ad43fdd4..f6c15d2ac5 100644 --- a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java +++ b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java @@ -7,9 +7,6 @@ import android.content.Context; import android.graphics.Outline; import android.graphics.drawable.Drawable; import android.os.Build; -import androidx.annotation.RequiresApi; -import androidx.preference.Preference; -import androidx.preference.PreferenceViewHolder; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; @@ -18,14 +15,16 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.RequiresApi; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec; import network.loki.messenger.R; @@ -124,8 +123,7 @@ public class ProfilePreference extends Preference { profileTagView.setVisibility(userMasterPublicKey == null ? View.GONE : View.VISIBLE); if (userMasterPublicKey != null && shortDeviceMnemonic == null) { - MnemonicCodec codec = new MnemonicCodec(MnemonicUtilities.getLanguageFileDirectory(context)); - shortDeviceMnemonic = MnemonicUtilities.getFirst3Words(codec, userPublicKey); + shortDeviceMnemonic = ""; } String tag = context.getResources().getString(R.string.activity_settings_linked_device_tag); From 75881273d5cd46b78e379e4157f637ea5baa55d7 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 11:36:54 +1000 Subject: [PATCH 02/17] Fix crash --- .../securesms/conversation/ConversationActivity.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index ed9620997d..4dab4651c6 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -1533,9 +1533,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void onSecurityUpdated() { - Log.i(TAG, "onSecurityUpdated()"); - updateReminders(recipient.hasSeenInviteReminder()); - updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId()); + if (recipient != null) { + updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId()); + } } protected void updateReminders(boolean seenInvite) { From a5abfc2755007f7f1bf10cf79ec9426636beb82e Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 11:40:34 +1000 Subject: [PATCH 03/17] Fix crash --- .../securesms/conversation/ConversationItem.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java index ff0dc8509a..aeb1712ceb 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -25,10 +25,6 @@ import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.Typeface; import android.net.Uri; -import androidx.annotation.DimenRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; @@ -50,6 +46,11 @@ import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.DimenRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + import com.annimon.stream.Stream; import org.thoughtcrime.securesms.ApplicationContext; @@ -975,7 +976,11 @@ public class ConversationItem extends LinearLayout } private void setAuthor(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { - String threadName = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(current.getThreadId()).getName(); + Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(current.getThreadId()); + String threadName = null; + if (recipient != null) { + threadName = recipient.getName(); + } boolean isRSSFeed = threadName != null && (threadName.equals("Loki News") || threadName.equals("Session Updates")); if (isGroupThread && !isRSSFeed && !current.isOutgoing()) { contactPhotoHolder.setVisibility(VISIBLE); From 38464bf1f739a0b184625cb93c6ce029e87f503e Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 11:53:04 +1000 Subject: [PATCH 04/17] Fix crash --- .../securesms/conversation/ConversationActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 4dab4651c6..e6cbd46077 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -1857,9 +1857,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity return; } + /* ApplicationContext.getInstance(this) .getJobManager() .add(new RetrieveProfileJob(recipient)); + */ } @Override From e1841db991ad7f078dca97db1772a6381d570677 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 12:15:02 +1000 Subject: [PATCH 05/17] Fix crash --- .../thoughtcrime/securesms/loki/activities/PathActivity.kt | 6 +++++- src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt index b42661be1f..7e3b641b12 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt @@ -142,7 +142,11 @@ class PathActivity : PassphraseRequiredActionBarActivity() { private fun getPathRow(snode: Snode, location: LineView.Location, dotAnimationStartDelay: Long, dotAnimationRepeatInterval: Long, isGuardSnode: Boolean): LinearLayout { val title = if (isGuardSnode) resources.getString(R.string.activity_path_guard_node_row_title) else resources.getString(R.string.activity_path_service_node_row_title) - val subtitle = IP2Country.shared.countryNamesCache[snode.ip] ?: "Resolving..." + val subtitle = if (IP2Country.isInitialized) { + IP2Country.shared.countryNamesCache[snode.ip] ?: "Resolving..." + } else { + "Resolving..." + } return getPathRow(title, subtitle, location, dotAnimationStartDelay, dotAnimationRepeatInterval) } // endregion diff --git a/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt b/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt index 8cd1204167..7f99b2c699 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt @@ -29,8 +29,10 @@ class IP2Country private constructor(private val context: Context) { public lateinit var shared: IP2Country + public val isInitialized: Boolean get() = ::shared.isInitialized + public fun configureIfNeeded(context: Context) { - if (::shared.isInitialized) { return; } + if (isInitialized) { return; } shared = IP2Country(context) } } From 1d6b3b3e7adb8c1fc1e17872a3185a4763c55574 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 19 Aug 2020 12:17:08 +1000 Subject: [PATCH 06/17] change backquote to simple quote and Invite Friends to Invite Contacts --- res/values/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 158f0f8a67..a1eb83fcbe 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1409,7 +1409,7 @@ Settings Lock Mark all read - Invite friends + Invite Contacts Help @@ -1714,7 +1714,7 @@ Your Recovery Phrase Meet your recovery phrase - Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don\’t give it to anyone. + Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don\'t give it to anyone. Hold to reveal Secure your account by saving your recovery phrase @@ -1732,7 +1732,7 @@ New Session Enter Session ID Scan QR Code - Scan a user\’s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings. + Scan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings. Enter Session ID of recipient Users can share their Session ID by going into their account settings and tapping "Share Session ID", or by sharing their QR code. From c35c08340d8f92d85c585ce9f9970888aa7e858a Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 13:39:02 +1000 Subject: [PATCH 07/17] Fix delivery receipts --- src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java | 9 +++++---- .../securesms/jobs/SendDeliveryReceiptJob.java | 4 +--- .../securesms/loki/protocol/SessionMetaProtocol.kt | 8 ++++++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 4ce544efb2..52bfb5541f 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -5,12 +5,13 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; +import android.text.TextUtils; +import android.util.Pair; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; -import android.text.TextUtils; -import android.util.Pair; import com.annimon.stream.Collectors; import com.annimon.stream.Stream; @@ -66,10 +67,10 @@ import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; -import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation; +import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol; import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; import org.thoughtcrime.securesms.loki.utilities.PromiseUtilities; @@ -303,7 +304,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { SessionMetaProtocol.handleProfileKeyUpdate(context, content); } - if (content.isNeedsReceipt() && SessionMetaProtocol.shouldSendDeliveryReceipt(Address.fromSerialized(content.getSender()))) { + if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.fromSerialized(content.getSender()))) { handleNeedsDeliveryReceipt(content, message); } } diff --git a/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java b/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java index dead5266cd..b19fbba85c 100644 --- a/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java @@ -10,7 +10,6 @@ import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; import org.thoughtcrime.securesms.recipients.Recipient; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; @@ -79,13 +78,12 @@ public class SendDeliveryReceiptJob extends BaseJob implements InjectableType { @Override public void onRun() throws IOException, UntrustedIdentityException { + Log.d("Loki", "Sending delivery receipt."); SignalServiceAddress remoteAddress = new SignalServiceAddress(address); SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, Collections.singletonList(messageId), timestamp); - if (!SessionMetaProtocol.shouldSendDeliveryReceipt(Address.fromSerialized(address))) { return; } - messageSender.sendReceipt(remoteAddress, UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)), receiptMessage); diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt index 1aacde4196..2327cbe3bb 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt @@ -79,8 +79,12 @@ object SessionMetaProtocol { } @JvmStatic - fun shouldSendDeliveryReceipt(address: Address): Boolean { - return !address.isGroup + fun shouldSendDeliveryReceipt(message: SignalServiceDataMessage, address: Address): Boolean { + if (address.isGroup) { return false } + val hasBody = message.body.isPresent && message.body.get().isNotEmpty() + val hasAttachment = message.attachments.isPresent && message.attachments.get().isNotEmpty() + val hasLinkPreview = message.previews.isPresent && message.previews.get().isNotEmpty() + return hasBody || hasAttachment || hasLinkPreview } /** From 6658ccdb6ed6276a2e1ab186a310084944527aee Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 13:40:26 +1000 Subject: [PATCH 08/17] Update version number --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 8bf64b9d56..00594e8c58 100644 --- a/build.gradle +++ b/build.gradle @@ -185,8 +185,8 @@ dependencies { implementation "com.opencsv:opencsv:4.6" } -def canonicalVersionCode = 65 -def canonicalVersionName = "1.4.1" +def canonicalVersionCode = 66 +def canonicalVersionName = "1.4.2" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, From 219d5ca401c7c6fdc7981c8393512ff1501895bb Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 13:48:54 +1000 Subject: [PATCH 09/17] Add recovery phrase disclaimer --- res/layout/dialog_seed.xml | 10 ++++++++++ res/values/strings.xml | 2 ++ 2 files changed, 12 insertions(+) diff --git a/res/layout/dialog_seed.xml b/res/layout/dialog_seed.xml index 37b523ad98..300f910810 100644 --- a/res/layout/dialog_seed.xml +++ b/res/layout/dialog_seed.xml @@ -65,4 +65,14 @@ + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index a1eb83fcbe..32907f5304 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1851,4 +1851,6 @@ Select Contacts + *Please note that it is not possible to use the same Session ID on multiple devices simultaneously + From fa664d07252f3b3734e1cf60019905e90fa6ab8c Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 14:31:33 +1000 Subject: [PATCH 10/17] Attempt to fix key store crash --- .../securesms/crypto/KeyStoreHelper.java | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java b/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java index 994c139434..fe032fa3d4 100644 --- a/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java +++ b/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java @@ -4,9 +4,10 @@ package org.thoughtcrime.securesms.crypto; import android.os.Build; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; +import android.util.Base64; + import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; -import android.util.Base64; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonGenerator; @@ -28,6 +29,7 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.UnrecoverableEntryException; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import javax.crypto.BadPaddingException; @@ -38,7 +40,7 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; -public class KeyStoreHelper { +public final class KeyStoreHelper { private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; private static final String KEY_ALIAS = "SignalSecret"; @@ -99,12 +101,38 @@ public class KeyStoreHelper { @RequiresApi(Build.VERSION_CODES.M) private static SecretKey getKeyStoreEntry() { + KeyStore keyStore = getKeyStore(); + + try { + // Attempt 1 + return getSecretKey(keyStore); + } catch (UnrecoverableKeyException e1) { + try { + // Attempt 2 + return getSecretKey(keyStore); + } catch (UnrecoverableKeyException e2) { + throw new AssertionError(e2); + } + } + } + + private static SecretKey getSecretKey(KeyStore keyStore) throws UnrecoverableKeyException { + try { + KeyStore.SecretKeyEntry entry = (KeyStore.SecretKeyEntry) keyStore.getEntry(KEY_ALIAS, null); + return entry.getSecretKey(); + } catch (UnrecoverableKeyException e) { + throw e; + } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableEntryException e) { + throw new AssertionError(e); + } + } + + private static KeyStore getKeyStore() { try { KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); - - return ((KeyStore.SecretKeyEntry) keyStore.getEntry(KEY_ALIAS, null)).getSecretKey(); - } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | UnrecoverableEntryException e) { + return keyStore; + } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) { throw new AssertionError(e); } } From 39792ef7f9e078e7856ac2cdefb13176fd00fe21 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 15:30:12 +1000 Subject: [PATCH 11/17] Fix SSK based closed group updating copy --- .../loki/protocol/ClosedGroupsProtocol.kt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt index 80b3f98496..71093220c8 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt @@ -218,7 +218,7 @@ object ClosedGroupsProtocol { public fun handleSharedSenderKeysUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { if (!isValid(closedGroupUpdate)) { return; } when (closedGroupUpdate.type) { - SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> handleNewClosedGroup(context, closedGroupUpdate) + SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> handleNewClosedGroup(context, closedGroupUpdate, senderPublicKey) SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> handleClosedGroupUpdate(context, closedGroupUpdate, senderPublicKey) SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST -> handleSenderKeyRequest(context, closedGroupUpdate, senderPublicKey) SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY -> handleSenderKey(context, closedGroupUpdate, senderPublicKey) @@ -244,7 +244,7 @@ object ClosedGroupsProtocol { } } - public fun handleNewClosedGroup(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate) { + public fun handleNewClosedGroup(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { // Prepare val sskDatabase = DatabaseFactory.getSSKDatabase(context) // Unwrap the message @@ -270,7 +270,7 @@ object ClosedGroupsProtocol { // Add the group to the user's set of public keys to poll for sskDatabase.setClosedGroupPrivateKey(groupPublicKey, groupPrivateKey.toHexString()) // Notify the user - insertIncomingInfoMessage(context, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) + insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) // Establish sessions if needed establishSessionsWithMembersIfNeeded(context, members) } @@ -311,6 +311,7 @@ object ClosedGroupsProtocol { // • Remove the group from the user's set of public keys to poll for if the current user was among the members that were removed val wasCurrentUserRemoved = !members.contains(userPublicKey) val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() + val wasSenderRemoved = !members.contains(senderPublicKey) if (wasAnyUserRemoved) { sskDatabase.removeAllClosedGroupRatchets(groupPublicKey) if (wasCurrentUserRemoved) { @@ -331,9 +332,9 @@ object ClosedGroupsProtocol { groupDB.updateTitle(groupID, name) groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) // Notify the user - val type0 = if (wasAnyUserRemoved) GroupContext.Type.QUIT else GroupContext.Type.UPDATE - val type1 = if (wasAnyUserRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE - insertIncomingInfoMessage(context, groupID, type0, type1, name, members, admins) + val type0 = if (wasSenderRemoved) GroupContext.Type.QUIT else GroupContext.Type.UPDATE + val type1 = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE + insertIncomingInfoMessage(context, senderPublicKey, groupID, type0, type1, name, members, admins) } public fun handleSenderKeyRequest(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { @@ -475,8 +476,8 @@ object ClosedGroupsProtocol { } } - private fun insertIncomingInfoMessage(context: Context, groupID: String, type0: GroupContext.Type, type1: SignalServiceGroup.Type, name: String, - members: Collection, admins: Collection) { + private fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type0: GroupContext.Type, type1: SignalServiceGroup.Type, + name: String, members: Collection, admins: Collection) { val groupContextBuilder = GroupContext.newBuilder() .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupID))) .setType(type0) @@ -484,7 +485,7 @@ object ClosedGroupsProtocol { .addAllMembers(members) .addAllAdmins(admins) val group = SignalServiceGroup(type1, GroupUtil.getDecodedId(groupID), GroupType.SIGNAL, name, members.toList(), null, admins.toList()) - val m = IncomingTextMessage(Address.fromSerialized(groupID), 1, System.currentTimeMillis(), "", Optional.of(group), 0, true) + val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, System.currentTimeMillis(), "", Optional.of(group), 0, true) val infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), "") val smsDB = DatabaseFactory.getSmsDatabase(context) smsDB.insertMessageInbox(infoMessage) From cc6c6dab3d86b26c098c63efc2fdc713aeacf5ae Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 16:32:15 +1000 Subject: [PATCH 12/17] Fix legacy closed group messaging --- .../securesms/loki/protocol/ClosedGroupsProtocol.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt index 71093220c8..3df6187a7b 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt @@ -411,8 +411,13 @@ object ClosedGroupsProtocol { if (GroupUtil.isOpenGroup(groupID)) { return listOf( Address.fromSerialized(groupID) ) } else { - val groupPublicKey = doubleDecodeGroupID(groupID).toHexString() - if (DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)) { + var groupPublicKey: String? = null + try { + groupPublicKey = doubleDecodeGroupID(groupID).toHexString() + } catch (exception: Exception) { + // Do nothing + } + if (groupPublicKey != null && DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)) { return listOf( Address.fromSerialized(groupPublicKey) ) } else { return DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, false).map { it.address } From a4c6b39ffafd37eb9ec9b566f281abf4a9564f0f Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 19 Aug 2020 16:33:51 +1000 Subject: [PATCH 13/17] Update build number --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 00594e8c58..02deb9f3c4 100644 --- a/build.gradle +++ b/build.gradle @@ -185,7 +185,7 @@ dependencies { implementation "com.opencsv:opencsv:4.6" } -def canonicalVersionCode = 66 +def canonicalVersionCode = 67 def canonicalVersionName = "1.4.2" def postFixSize = 10 From af1f45a6e4b8cea38d01600662b6662882aa53c2 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Thu, 20 Aug 2020 09:19:00 +1000 Subject: [PATCH 14/17] Fix received message hash values storage --- .../securesms/loki/database/LokiAPIDatabase.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt index 58728ed199..a3a3ddb54a 100644 --- a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt +++ b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt @@ -215,9 +215,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( override fun getReceivedMessageHashValues(publicKey: String): Set? { val database = databaseHelper.readableDatabase - val query = "$Companion.publicKey = ?" + val query = "${Companion.publicKey} = ?" return database.get(receivedMessageHashValuesTable2, query, arrayOf( publicKey )) { cursor -> - val receivedMessageHashValuesAsString = cursor.getString(cursor.getColumnIndexOrThrow(receivedMessageHashValues)) + val receivedMessageHashValuesAsString = cursor.getString(cursor.getColumnIndexOrThrow(Companion.receivedMessageHashValues)) receivedMessageHashValuesAsString.split("-").toSet() } } @@ -225,8 +225,8 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( override fun setReceivedMessageHashValues(publicKey: String, newValue: Set) { val database = databaseHelper.writableDatabase val receivedMessageHashValuesAsString = newValue.joinToString("-") - val row = wrap(mapOf( Companion.publicKey to publicKey, receivedMessageHashValues to receivedMessageHashValuesAsString )) - val query = "$Companion.publicKey = ?" + val row = wrap(mapOf( Companion.publicKey to publicKey, Companion.receivedMessageHashValues to receivedMessageHashValuesAsString )) + val query = "${Companion.publicKey} = ?" database.insertOrUpdate(receivedMessageHashValuesTable2, row, query, arrayOf( publicKey )) } From 86fa15d0c9349dce2aa857f26580b9f4937010d9 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Thu, 20 Aug 2020 09:26:33 +1000 Subject: [PATCH 15/17] Update build number --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 02deb9f3c4..8420040b85 100644 --- a/build.gradle +++ b/build.gradle @@ -185,7 +185,7 @@ dependencies { implementation "com.opencsv:opencsv:4.6" } -def canonicalVersionCode = 67 +def canonicalVersionCode = 68 def canonicalVersionName = "1.4.2" def postFixSize = 10 From 7e99aec4107408aca06023a1418cbe2d362a043b Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Thu, 20 Aug 2020 10:32:31 +1000 Subject: [PATCH 16/17] Fix messed up database table --- .../database/helpers/SQLCipherOpenHelper.java | 13 +++++++++---- .../securesms/loki/database/LokiAPIDatabase.kt | 10 +++++----- .../loki/utilities/GeneralUtilities.kt | 4 ---- .../loki/utilities/OpenGroupUtilities.kt | 18 +++++++++--------- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 4d0da39a3c..698910d674 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -6,9 +6,10 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.SystemClock; -import androidx.annotation.NonNull; import android.text.TextUtils; +import androidx.annotation.NonNull; + import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabaseHook; import net.sqlcipher.database.SQLiteOpenHelper; @@ -87,8 +88,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int lokiV10 = 31; private static final int lokiV11 = 32; private static final int lokiV12 = 33; + private static final int lokiV13 = 34; - private static final int DATABASE_VERSION = lokiV12; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes + private static final int DATABASE_VERSION = lokiV13; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -140,7 +142,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiAPIDatabase.getCreateOnionRequestPathTableCommand()); db.execSQL(LokiAPIDatabase.getCreateSwarmTableCommand()); db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command()); - db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable2Command()); + db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable3Command()); db.execSQL(LokiAPIDatabase.getCreateOpenGroupAuthTokenTableCommand()); db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand()); db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand()); @@ -609,11 +611,14 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { if (oldVersion < lokiV12) { db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command()); - db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable2Command()); db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupRatchetTableCommand()); db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupPrivateKeyTableCommand()); } + if (oldVersion < lokiV13) { + db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable3Command()); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt index a3a3ddb54a..e81f3c9fb6 100644 --- a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt +++ b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt @@ -38,10 +38,10 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( @JvmStatic val createLastMessageHashValueTable2Command = "CREATE TABLE $lastMessageHashValueTable2 ($snode TEXT, $publicKey TEXT, $lastMessageHashValue TEXT, PRIMARY KEY ($snode, $publicKey));" // Received message hash values - private val receivedMessageHashValuesTable2 = "received_message_hash_values_table" + private val receivedMessageHashValuesTable3 = "received_message_hash_values_table_3" private val receivedMessageHashValues = "received_message_hash_values" - @JvmStatic val createReceivedMessageHashValuesTable2Command - = "CREATE TABLE $receivedMessageHashValuesTable2 ($snode STRING, $publicKey STRING, $receivedMessageHashValues TEXT, PRIMARY KEY ($snode, $publicKey));" + @JvmStatic val createReceivedMessageHashValuesTable3Command + = "CREATE TABLE $receivedMessageHashValuesTable3 ($publicKey STRING PRIMARY KEY, $receivedMessageHashValues TEXT);" // Open group auth tokens private val openGroupAuthTokenTable = "loki_api_group_chat_auth_token_database" private val server = "server" @@ -216,7 +216,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( override fun getReceivedMessageHashValues(publicKey: String): Set? { val database = databaseHelper.readableDatabase val query = "${Companion.publicKey} = ?" - return database.get(receivedMessageHashValuesTable2, query, arrayOf( publicKey )) { cursor -> + return database.get(receivedMessageHashValuesTable3, query, arrayOf( publicKey )) { cursor -> val receivedMessageHashValuesAsString = cursor.getString(cursor.getColumnIndexOrThrow(Companion.receivedMessageHashValues)) receivedMessageHashValuesAsString.split("-").toSet() } @@ -227,7 +227,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( val receivedMessageHashValuesAsString = newValue.joinToString("-") val row = wrap(mapOf( Companion.publicKey to publicKey, Companion.receivedMessageHashValues to receivedMessageHashValuesAsString )) val query = "${Companion.publicKey} = ?" - database.insertOrUpdate(receivedMessageHashValuesTable2, row, query, arrayOf( publicKey )) + database.insertOrUpdate(receivedMessageHashValuesTable3, row, query, arrayOf( publicKey )) } override fun getAuthToken(server: String): String? { diff --git a/src/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt b/src/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt index 3ff3bd19a5..37bc30c395 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt @@ -19,7 +19,3 @@ fun toPx(dp: Int, resources: Resources): Int { val scale = resources.displayMetrics.density return (dp * scale).roundToInt() } - -fun isPublicChat(context: Context, recipient: String): Boolean { - return DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().values.map { it.server }.contains(recipient) -} diff --git a/src/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt b/src/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt index c2abd36dd5..4cf69ea315 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt @@ -23,15 +23,15 @@ object OpenGroupUtilities { val displayName = TextSecurePreferences.getProfileName(context) val lokiPublicChatAPI = application.publicChatAPI ?: throw Error("LokiPublicChatAPI is not initialized.") return application.publicChatManager.addChat(url, channel).then { group -> - DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url) - DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url) - lokiPublicChatAPI.getMessages(channel, url) - lokiPublicChatAPI.setDisplayName(displayName, url) - lokiPublicChatAPI.join(channel, url) - val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(context) - val profileUrl: String? = TextSecurePreferences.getProfilePictureURL(context) - lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl) - group + DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url) + DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url) + lokiPublicChatAPI.getMessages(channel, url) + lokiPublicChatAPI.setDisplayName(displayName, url) + lokiPublicChatAPI.join(channel, url) + val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(context) + val profileUrl: String? = TextSecurePreferences.getProfilePictureURL(context) + lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl) + group } } } \ No newline at end of file From 0f733bee5153ee03ded2c1f5cfb3ef05c0e8e406 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Thu, 20 Aug 2020 10:38:05 +1000 Subject: [PATCH 17/17] Update build number --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8420040b85..f88246e35d 100644 --- a/build.gradle +++ b/build.gradle @@ -185,7 +185,7 @@ dependencies { implementation "com.opencsv:opencsv:4.6" } -def canonicalVersionCode = 68 +def canonicalVersionCode = 69 def canonicalVersionName = "1.4.2" def postFixSize = 10