mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 08:39:22 +01:00
Always use sealed sender when sending stories.
This commit is contained in:
@@ -77,7 +77,7 @@ class AdvancedPrivacySettingsViewModel(
|
||||
fun setCensorshipCircumventionEnabled(enabled: Boolean) {
|
||||
SignalStore.settings().setCensorshipCircumventionEnabled(enabled)
|
||||
SignalStore.misc().isServiceReachableWithoutCircumvention = false
|
||||
ApplicationDependencies.resetNetworkConnectionsAfterProxyChange()
|
||||
ApplicationDependencies.resetAllNetworkConnections()
|
||||
refresh()
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECPublicKey;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||
import org.thoughtcrime.securesms.keyvalue.CertificateType;
|
||||
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
@@ -68,8 +70,8 @@ public class UnidentifiedAccessUtil {
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static Map<RecipientId, Optional<UnidentifiedAccessPair>> getAccessMapFor(@NonNull Context context, @NonNull List<Recipient> recipients) {
|
||||
List<Optional<UnidentifiedAccessPair>> accessList = getAccessFor(context, recipients, true);
|
||||
public static Map<RecipientId, Optional<UnidentifiedAccessPair>> getAccessMapFor(@NonNull Context context, @NonNull List<Recipient> recipients, boolean isForStory) {
|
||||
List<Optional<UnidentifiedAccessPair>> accessList = getAccessFor(context, recipients, true, isForStory);
|
||||
|
||||
Iterator<Recipient> recipientIterator = recipients.iterator();
|
||||
Iterator<Optional<UnidentifiedAccessPair>> accessIterator = accessList.iterator();
|
||||
@@ -82,9 +84,14 @@ public class UnidentifiedAccessUtil {
|
||||
|
||||
return accessMap;
|
||||
}
|
||||
|
||||
|
||||
@WorkerThread
|
||||
public static List<Optional<UnidentifiedAccessPair>> getAccessFor(@NonNull Context context, @NonNull List<Recipient> recipients, boolean log) {
|
||||
return getAccessFor(context, recipients, false, log);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static List<Optional<UnidentifiedAccessPair>> getAccessFor(@NonNull Context context, @NonNull List<Recipient> recipients, boolean isForStory, boolean log) {
|
||||
byte[] ourUnidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey());
|
||||
|
||||
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
||||
@@ -96,7 +103,7 @@ public class UnidentifiedAccessUtil {
|
||||
Map<CertificateType, Integer> typeCounts = new HashMap<>();
|
||||
|
||||
for (Recipient recipient : recipients) {
|
||||
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
|
||||
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient, isForStory);
|
||||
CertificateType certificateType = getUnidentifiedAccessCertificateType(recipient);
|
||||
byte[] ourUnidentifiedAccessCertificate = SignalStore.certificateValues().getUnidentifiedAccessCertificate(certificateType);
|
||||
|
||||
@@ -168,28 +175,40 @@ public class UnidentifiedAccessUtil {
|
||||
.getUnidentifiedAccessCertificate(getUnidentifiedAccessCertificateType(recipient));
|
||||
}
|
||||
|
||||
private static @Nullable byte[] getTargetUnidentifiedAccessKey(@NonNull Recipient recipient) {
|
||||
private static @Nullable byte[] getTargetUnidentifiedAccessKey(@NonNull Recipient recipient, boolean isForStory) {
|
||||
ProfileKey theirProfileKey = ProfileKeyUtil.profileKeyOrNull(recipient.resolve().getProfileKey());
|
||||
|
||||
byte[] accessKey;
|
||||
|
||||
switch (recipient.resolve().getUnidentifiedAccessMode()) {
|
||||
case UNKNOWN:
|
||||
if (theirProfileKey == null) {
|
||||
return UNRESTRICTED_KEY;
|
||||
accessKey = UNRESTRICTED_KEY;
|
||||
} else {
|
||||
return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
|
||||
accessKey = UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
|
||||
}
|
||||
break;
|
||||
case DISABLED:
|
||||
return null;
|
||||
accessKey = null;
|
||||
break;
|
||||
case ENABLED:
|
||||
if (theirProfileKey == null) {
|
||||
return null;
|
||||
accessKey = null;
|
||||
} else {
|
||||
return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
|
||||
accessKey = UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
|
||||
}
|
||||
break;
|
||||
case UNRESTRICTED:
|
||||
return UNRESTRICTED_KEY;
|
||||
accessKey = UNRESTRICTED_KEY;
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unknown mode: " + recipient.getUnidentifiedAccessMode().getMode());
|
||||
}
|
||||
|
||||
if (accessKey == null && isForStory) {
|
||||
accessKey = UNRESTRICTED_KEY;
|
||||
}
|
||||
|
||||
return accessKey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ public class ApplicationDependencies {
|
||||
}
|
||||
}
|
||||
|
||||
public static void resetNetworkConnectionsAfterProxyChange() {
|
||||
public static void resetAllNetworkConnections() {
|
||||
synchronized (LOCK) {
|
||||
closeConnections();
|
||||
if (signalWebSocket != null) {
|
||||
|
||||
@@ -63,6 +63,7 @@ import org.thoughtcrime.securesms.service.PendingRetryReceiptManager;
|
||||
import org.thoughtcrime.securesms.service.TrimThreadsByDateManager;
|
||||
import org.thoughtcrime.securesms.service.webrtc.SignalCallManager;
|
||||
import org.thoughtcrime.securesms.shakereport.ShakeToReport;
|
||||
import org.thoughtcrime.securesms.stories.Stories;
|
||||
import org.thoughtcrime.securesms.util.AlarmSleepTimer;
|
||||
import org.thoughtcrime.securesms.util.AppForegroundObserver;
|
||||
import org.thoughtcrime.securesms.util.ByteUnit;
|
||||
@@ -398,7 +399,8 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
|
||||
signalServiceConfigurationSupplier.get(),
|
||||
Optional.of(new DynamicCredentialsProvider()),
|
||||
BuildConfig.SIGNAL_AGENT,
|
||||
healthMonitor);
|
||||
healthMonitor,
|
||||
Stories.isFeatureEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -407,7 +409,8 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
|
||||
signalServiceConfigurationSupplier.get(),
|
||||
Optional.empty(),
|
||||
BuildConfig.SIGNAL_AGENT,
|
||||
healthMonitor);
|
||||
healthMonitor,
|
||||
Stories.isFeatureEnabled());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.jobmanager.Data
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.stories.Stories
|
||||
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
|
||||
import org.whispersystems.signalservice.internal.util.StaticCredentialsProvider
|
||||
import org.whispersystems.signalservice.internal.websocket.WebSocketConnection
|
||||
@@ -78,7 +79,8 @@ class CheckServiceReachabilityJob private constructor(params: Parameters) : Base
|
||||
),
|
||||
BuildConfig.SIGNAL_AGENT,
|
||||
null,
|
||||
""
|
||||
"",
|
||||
Stories.isFeatureEnabled()
|
||||
)
|
||||
|
||||
try {
|
||||
|
||||
@@ -102,6 +102,11 @@ public final class PushDecryptMessageJob extends BaseJob {
|
||||
List<Job> jobs = new LinkedList<>();
|
||||
DecryptionResult result = MessageDecryptionUtil.decrypt(context, envelope);
|
||||
|
||||
if (result.getState() == MessageState.DECRYPTED_OK && envelope.isStory() && !isStoryMessage(result)) {
|
||||
Log.w(TAG, "Envelope was flagged as a story, but it did not have any story-related content! Dropping.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.getContent() != null) {
|
||||
if (result.getContent().getSenderKeyDistributionMessage().isPresent()) {
|
||||
handleSenderKeyDistributionMessage(result.getContent().getSender(), result.getContent().getSenderDevice(), result.getContent().getSenderKeyDistributionMessage().get());
|
||||
@@ -172,6 +177,25 @@ public final class PushDecryptMessageJob extends BaseJob {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStoryMessage(@NonNull DecryptionResult result) {
|
||||
if (result.getContent() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.getContent().getStoryMessage().isPresent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (result.getContent().getDataMessage().isPresent() &&
|
||||
result.getContent().getDataMessage().get().getStoryContext().isPresent() &&
|
||||
result.getContent().getDataMessage().get().getGroupContext().isPresent())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean needsMigration() {
|
||||
return TextSecurePreferences.getNeedsSqlCipherMigration(context);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ public final class SenderKeyDistributionSendJob extends BaseJob {
|
||||
SenderKeyDistributionMessage message = messageSender.getOrCreateNewGroupSession(distributionId);
|
||||
List<Optional<UnidentifiedAccessPair>> access = UnidentifiedAccessUtil.getAccessFor(context, Collections.singletonList(targetRecipient));
|
||||
|
||||
SendMessageResult result = messageSender.sendSenderKeyDistributionMessage(distributionId, address, access, message, Optional.ofNullable(groupId).map(GroupId::getDecodedId), false).get(0);
|
||||
SendMessageResult result = messageSender.sendSenderKeyDistributionMessage(distributionId, address, access, message, Optional.ofNullable(groupId).map(GroupId::getDecodedId), false, false).get(0);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
List<SignalProtocolAddress> addresses = result.getSuccess()
|
||||
|
||||
@@ -94,7 +94,7 @@ public final class GroupSendUtil {
|
||||
boolean urgent)
|
||||
throws IOException, UntrustedIdentityException
|
||||
{
|
||||
return sendMessage(context, groupId, getDistributionId(groupId), messageId, allTargets, isRecipientUpdate, DataSendOperation.resendable(message, contentHint, messageId, urgent), null);
|
||||
return sendMessage(context, groupId, getDistributionId(groupId), messageId, allTargets, isRecipientUpdate, false, DataSendOperation.resendable(message, contentHint, messageId, urgent), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +116,7 @@ public final class GroupSendUtil {
|
||||
boolean urgent)
|
||||
throws IOException, UntrustedIdentityException
|
||||
{
|
||||
return sendMessage(context, groupId, getDistributionId(groupId), null, allTargets, isRecipientUpdate, DataSendOperation.unresendable(message, contentHint, urgent), null);
|
||||
return sendMessage(context, groupId, getDistributionId(groupId), null, allTargets, isRecipientUpdate, false, DataSendOperation.unresendable(message, contentHint, urgent), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +133,7 @@ public final class GroupSendUtil {
|
||||
@Nullable CancelationSignal cancelationSignal)
|
||||
throws IOException, UntrustedIdentityException
|
||||
{
|
||||
return sendMessage(context, groupId, getDistributionId(groupId), null, allTargets, false, new TypingSendOperation(message), cancelationSignal);
|
||||
return sendMessage(context, groupId, getDistributionId(groupId), null, allTargets, false, false, new TypingSendOperation(message), cancelationSignal);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,11 +149,11 @@ public final class GroupSendUtil {
|
||||
@NonNull SignalServiceCallMessage message)
|
||||
throws IOException, UntrustedIdentityException
|
||||
{
|
||||
return sendMessage(context, groupId, getDistributionId(groupId), null, allTargets, false, new CallSendOperation(message), null);
|
||||
return sendMessage(context, groupId, getDistributionId(groupId), null, allTargets, false, false, new CallSendOperation(message), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all of the logic of sending a story to a group. Will do sender key sends and legacy 1:1 sends as-needed, and give you back a list of
|
||||
* Handles all of the logic of sending a story to a distribution list. Will do sender key sends and legacy 1:1 sends as-needed, and give you back a list of
|
||||
* {@link SendMessageResult}s just like we're used to.
|
||||
*
|
||||
* @param isRecipientUpdate True if you've already sent this message to some recipients in the past, otherwise false.
|
||||
@@ -175,6 +175,7 @@ public final class GroupSendUtil {
|
||||
messageId,
|
||||
allTargets,
|
||||
isRecipientUpdate,
|
||||
true,
|
||||
new StorySendOperation(messageId, null, sentTimestamp, message, manifest),
|
||||
null);
|
||||
}
|
||||
@@ -201,6 +202,7 @@ public final class GroupSendUtil {
|
||||
messageId,
|
||||
allTargets,
|
||||
isRecipientUpdate,
|
||||
true,
|
||||
new StorySendOperation(messageId, groupId, sentTimestamp, message, Collections.emptySet()),
|
||||
null);
|
||||
}
|
||||
@@ -219,6 +221,7 @@ public final class GroupSendUtil {
|
||||
@Nullable MessageId relatedMessageId,
|
||||
@NonNull List<Recipient> allTargets,
|
||||
boolean isRecipientUpdate,
|
||||
boolean isStorySend,
|
||||
@NonNull SendOperation sendOperation,
|
||||
@Nullable CancelationSignal cancelationSignal)
|
||||
throws IOException, UntrustedIdentityException
|
||||
@@ -228,7 +231,7 @@ public final class GroupSendUtil {
|
||||
Set<Recipient> unregisteredTargets = allTargets.stream().filter(Recipient::isUnregistered).collect(Collectors.toSet());
|
||||
List<Recipient> registeredTargets = allTargets.stream().filter(r -> !unregisteredTargets.contains(r)).collect(Collectors.toList());
|
||||
|
||||
RecipientData recipients = new RecipientData(context, registeredTargets);
|
||||
RecipientData recipients = new RecipientData(context, registeredTargets, isStorySend);
|
||||
Optional<GroupRecord> groupRecord = groupId != null ? SignalDatabase.groups().getGroup(groupId) : Optional.empty();
|
||||
|
||||
List<Recipient> senderKeyTargets = new LinkedList<>();
|
||||
@@ -257,6 +260,11 @@ public final class GroupSendUtil {
|
||||
Log.i(TAG, "No DistributionId. Using legacy.");
|
||||
legacyTargets.addAll(senderKeyTargets);
|
||||
senderKeyTargets.clear();
|
||||
} else if (isStorySend) {
|
||||
Log.i(TAG, "Sending a story. Using sender key for all " + allTargets.size() + " recipients.");
|
||||
senderKeyTargets.clear();
|
||||
senderKeyTargets.addAll(registeredTargets);
|
||||
legacyTargets.clear();
|
||||
} else if (SignalStore.internalValues().removeSenderKeyMinimum()) {
|
||||
Log.i(TAG, "Sender key minimum removed. Using for " + senderKeyTargets.size() + " recipients.");
|
||||
} else if (senderKeyTargets.size() < 2) {
|
||||
@@ -681,7 +689,7 @@ public final class GroupSendUtil {
|
||||
@Nullable CancelationSignal cancelationSignal)
|
||||
throws IOException, UntrustedIdentityException
|
||||
{
|
||||
return messageSender.sendStory(targets, access, isRecipientUpdate, message, getSentTimestamp(), manifest);
|
||||
throw new UnsupportedOperationException("Stories can only be send via sender key!");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -767,8 +775,8 @@ public final class GroupSendUtil {
|
||||
private final Map<RecipientId, SignalServiceAddress> addressById;
|
||||
private final RecipientAccessList accessList;
|
||||
|
||||
RecipientData(@NonNull Context context, @NonNull List<Recipient> recipients) throws IOException {
|
||||
this.accessById = UnidentifiedAccessUtil.getAccessMapFor(context, recipients);
|
||||
RecipientData(@NonNull Context context, @NonNull List<Recipient> recipients, boolean isForStory) throws IOException {
|
||||
this.accessById = UnidentifiedAccessUtil.getAccessMapFor(context, recipients, isForStory);
|
||||
this.addressById = mapAddresses(context, recipients);
|
||||
this.accessList = new RecipientAccessList(recipients);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import org.thoughtcrime.securesms.jobmanager.JobTracker;
|
||||
import org.thoughtcrime.securesms.jobs.MarkerJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushProcessMessageJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.stories.Stories;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -82,7 +84,7 @@ public class RestStrategy extends MessageRetrievalStrategy {
|
||||
|
||||
receiver.setSoTimeoutMillis(timeout);
|
||||
|
||||
receiver.retrieveMessages(envelope -> {
|
||||
receiver.retrieveMessages(Stories.isFeatureEnabled(), envelope -> {
|
||||
Log.i(TAG, "Retrieved an envelope." + timeSuffix(startTime));
|
||||
String jobId = processor.processEnvelope(envelope);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
@@ -25,6 +26,7 @@ class StoriesPrivacySettingsRepository {
|
||||
return Completable.fromAction {
|
||||
SignalStore.storyValues().isFeatureDisabled = !isEnabled
|
||||
Stories.onStorySettingsChanged(Recipient.self().id)
|
||||
ApplicationDependencies.resetAllNetworkConnections()
|
||||
|
||||
SignalDatabase.mms.getAllOutgoingStories(false, -1).use { reader ->
|
||||
reader.map { record -> record.id }
|
||||
|
||||
@@ -249,7 +249,10 @@ public final class FeatureFlags {
|
||||
*/
|
||||
private static final Map<String, OnFlagChange> FLAG_CHANGE_LISTENERS = new HashMap<String, OnFlagChange>() {{
|
||||
put(MESSAGE_PROCESSOR_ALARM_INTERVAL, change -> MessageProcessReceiver.startOrUpdateAlarm(ApplicationDependencies.getApplication()));
|
||||
put(STORIES, change -> ApplicationDependencies.getJobManager().startChain(new RefreshAttributesJob()).then(new RefreshOwnProfileJob()).enqueue());
|
||||
put(STORIES, change -> {
|
||||
ApplicationDependencies.getJobManager().startChain(new RefreshAttributesJob()).then(new RefreshOwnProfileJob()).enqueue();
|
||||
ApplicationDependencies.resetAllNetworkConnections();
|
||||
});
|
||||
put(GIFT_BADGE_RECEIVE_SUPPORT, change -> ApplicationDependencies.getJobManager().startChain(new RefreshAttributesJob()).then(new RefreshOwnProfileJob()).enqueue());
|
||||
}};
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public final class SignalProxyUtil {
|
||||
public static void enableProxy(@NonNull SignalProxy proxy) {
|
||||
SignalStore.proxy().enableProxy(proxy);
|
||||
Conscrypt.setUseEngineSocketByDefault(true);
|
||||
ApplicationDependencies.resetNetworkConnectionsAfterProxyChange();
|
||||
ApplicationDependencies.resetAllNetworkConnections();
|
||||
startListeningToWebsocket();
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public final class SignalProxyUtil {
|
||||
public static void disableProxy() {
|
||||
SignalStore.proxy().disableProxy();
|
||||
Conscrypt.setUseEngineSocketByDefault(false);
|
||||
ApplicationDependencies.resetNetworkConnectionsAfterProxyChange();
|
||||
ApplicationDependencies.resetAllNetworkConnections();
|
||||
startListeningToWebsocket();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user