Mark copied secrets as sensitive and auto-clear the backup passphrase.

Co-authored-by: Greyson Parrelil <greyson@signal.org>
This commit is contained in:
Ehren Kret
2026-09-10 17:10:15 -04:00
committed by Cody Henthorne
co-authored by Greyson Parrelil
parent c6ff93af98
commit e90e421fea
10 changed files with 174 additions and 43 deletions
@@ -2,8 +2,6 @@ package org.thoughtcrime.securesms.backup;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -39,6 +37,7 @@ public class BackupDialog {
private static final String TAG = Log.tag(BackupDialog.class);
public static void showEnableBackupDialog(@NonNull Context context,
@Nullable Intent backupDirectorySelectionIntent,
@Nullable String backupDirectoryDisplayName,
@@ -103,7 +102,7 @@ public class BackupDialog {
textView.setOnClickListener(v -> checkBox.toggle());
dialog.findViewById(R.id.number_table).setOnClickListener(v -> {
((ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("text", Util.join(password, " ")));
Util.copyToClipboardSensitive(context, Util.join(password, " "));
Toast.makeText(context, R.string.BackupDialog_copied_to_clipboard, Toast.LENGTH_LONG).show();
});
@@ -65,7 +65,6 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
@VisibleForTesting
const val TIER = "tier"
const val CLIPBOARD_TIMEOUT_SECONDS = 60
private const val PDF_MIME_TYPE = "application/pdf"
@@ -192,7 +191,7 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
onSaveSuccessful = viewModel::onBackupKeySavedToPasswordManager
)
},
onCopyToClipboardClick = { Util.copyToClipboard(context, it, CLIPBOARD_TIMEOUT_SECONDS) },
onCopyToClipboardClick = { Util.copyToClipboardSensitive(context, it) },
onRequestSaveToPasswordManager = viewModel::onBackupKeySaveRequested,
onConfirmSaveToPasswordManager = viewModel::onBackupKeySaveConfirmed,
onSaveStateCleared = viewModel::onBackupKeySaveStateCleared,
@@ -212,7 +211,7 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
canOpenPasswordManagerSettings = passwordManagerSettingsIntent != null,
onNavigationClick = viewModel::goToPreviousStage,
mode = remember { MessageBackupsKeyRecordMode.Next(viewModel::goToNextStage) },
onCopyToClipboardClick = { Util.copyToClipboard(context, it, CLIPBOARD_TIMEOUT_SECONDS) },
onCopyToClipboardClick = { Util.copyToClipboardSensitive(context, it) },
onRequestSaveToPasswordManager = viewModel::onBackupKeySaveRequested,
onConfirmSaveToPasswordManager = viewModel::onBackupKeySaveConfirmed,
onSaveToPasswordManagerComplete = viewModel::onBackupKeySaveCompleted,
@@ -337,7 +336,7 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
}
}
SignalLoginViewDetailsAction.LaunchSaveAsPdf -> savePdfLauncher.launch(SignalLoginPdfRenderer.suggestedFileName(requireContext()))
is SignalLoginViewDetailsAction.CopyTextToClipboard -> Util.copyToClipboard(requireContext(), action.text, CLIPBOARD_TIMEOUT_SECONDS)
is SignalLoginViewDetailsAction.CopyTextToClipboard -> Util.copyToClipboardSensitive(requireContext(), action.text)
}
}
@@ -91,8 +91,6 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.CommunicationActions
import org.signal.core.ui.R as CoreUiR
private const val CLIPBOARD_TIMEOUT_SECONDS = 60
@Stable
sealed interface MessageBackupsKeyRecordMode {
data class Next(val onNextClick: () -> Unit) : MessageBackupsKeyRecordMode
@@ -133,7 +131,7 @@ fun MessageBackupsKeyRecordScreen(
keySaveState = keySaveState,
canOpenPasswordManagerSettings = passwordManagerSettingsIntent != null,
onNavigationClick = { onBackPressedDispatcher?.onBackPressed() },
onCopyToClipboardClick = { Util.copyToClipboard(context, it, CLIPBOARD_TIMEOUT_SECONDS) },
onCopyToClipboardClick = { Util.copyToClipboardSensitive(context, it) },
onRequestSaveToPasswordManager = backupKeyCredentialManagerHandler::onBackupKeySaveRequested,
onConfirmSaveToPasswordManager = backupKeyCredentialManagerHandler::onBackupKeySaveConfirmed,
onSaveToPasswordManagerComplete = backupKeyCredentialManagerHandler::onBackupKeySaveCompleted,
@@ -16,10 +16,6 @@ import org.thoughtcrime.securesms.util.viewModel
*/
class ForgotBackupKeyFragment : ComposeFragment() {
companion object {
const val CLIPBOARD_TIMEOUT_SECONDS = 60
}
private val viewModel: ForgotBackupKeyViewModel by viewModel { ForgotBackupKeyViewModel() }
@Composable
@@ -54,7 +54,7 @@ class TotpSetupFragment : ComposeFragment() {
when (action) {
TotpSetupAction.NavigateBack -> requireActivity().onBackPressedDispatcher.onBackPressed()
is TotpSetupAction.LaunchTotpApp -> launchTotpApp(action.uri)
is TotpSetupAction.CopyKeyToClipboard -> Util.copyToClipboard(requireContext(), action.key)
is TotpSetupAction.CopyKeyToClipboard -> Util.copyToClipboardSensitive(requireContext(), action.key)
TotpSetupAction.ShowKeyCopied -> toast(AppSettingsR.string.TotpSetupScreen__copied_to_clipboard)
TotpSetupAction.ShowNoTotpAppFound -> toast(AppSettingsR.string.TotpSetupScreen__no_authenticator_app_found)
TotpSetupAction.NavigateToCodeEntry -> findNavController().safeNavigate(R.id.action_authenticatorSetupFragment_to_authenticatorCodeEntryFragment)
@@ -28,10 +28,6 @@ import org.thoughtcrime.securesms.components.TemporaryScreenshotSecurity
*/
class SignalLoginViewDetailsFragment : ComposeFragment() {
companion object {
private const val CLIPBOARD_TIMEOUT_SECONDS = 60
}
private val viewModel: SignalLoginViewDetailsViewModel by viewModels()
private val savePdfLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("application/pdf")) { uri: Uri? ->
@@ -73,7 +69,7 @@ class SignalLoginViewDetailsFragment : ComposeFragment() {
}
}
SignalLoginViewDetailsAction.LaunchSaveAsPdf -> savePdfLauncher.launch(SignalLoginPdfRenderer.suggestedFileName(requireContext()))
is SignalLoginViewDetailsAction.CopyTextToClipboard -> Util.copyToClipboard(requireContext(), action.text, CLIPBOARD_TIMEOUT_SECONDS)
is SignalLoginViewDetailsAction.CopyTextToClipboard -> Util.copyToClipboardSensitive(requireContext(), action.text)
}
}
}
@@ -1,10 +1,5 @@
package org.thoughtcrime.securesms.payments.backup.phrase;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
@@ -23,13 +18,10 @@ import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.signal.core.util.ClearClipboardAlarmReceiver;
import org.signal.core.util.PendingIntentFlags;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.TemporaryScreenshotSecurity;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.payments.Mnemonic;
import org.signal.core.util.ServiceUtil;
import org.signal.core.util.Util;
import org.thoughtcrime.securesms.util.SystemWindowInsetsSetter;
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
@@ -37,13 +29,13 @@ import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import java.util.stream.Collectors;
public class PaymentsRecoveryPhraseFragment extends Fragment {
private static final int SPAN_COUNT = 2;
private static final int SPAN_COUNT = 2;
private static final int CLIPBOARD_TIMEOUT_SECONDS = 30;
public PaymentsRecoveryPhraseFragment() {
super(R.layout.payments_recovery_phrase_fragment);
@@ -102,14 +94,7 @@ public class PaymentsRecoveryPhraseFragment extends Fragment {
}
private void copyWordsToClipboard(List<String> words) {
ClipboardManager clipboardManager = ServiceUtil.getClipboardManager(requireContext());
clipboardManager.setPrimaryClip(ClipData.newPlainText(getString(R.string.app_name), Util.join(words, " ")));
AlarmManager alarmManager = ServiceUtil.getAlarmManager(requireContext());
Intent alarmIntent = new Intent(requireContext(), ClearClipboardAlarmReceiver.class);
PendingIntent pendingAlarmIntent = PendingIntent.getBroadcast(requireContext(), 0, alarmIntent, PendingIntentFlags.mutable());
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30), pendingAlarmIntent);
Util.copyToClipboardSensitive(requireContext(), Util.join(words, " "), CLIPBOARD_TIMEOUT_SECONDS);
}
private void setUpForConfirmation(@NonNull TextView message,