Phone number privacy settings and certificate support behind feature flag.

fork-5.53.8
Alan Evans 2020-09-03 18:35:17 -03:00 zatwierdzone przez Cody Henthorne
rodzic abd3d4b546
commit 7b24e66ed3
23 zmienionych plików z 520 dodań i 144 usunięć

Wyświetl plik

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@ -10,6 +11,9 @@ import org.signal.libsignal.metadata.certificate.CertificateValidator;
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
import org.signal.zkgroup.profiles.ProfileKey;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.keyvalue.CertificateType;
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Base64;
@ -44,21 +48,17 @@ public class UnidentifiedAccessUtil {
try {
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
byte[] ourUnidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey());
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(recipient);
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
}
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) +
" | Our access key present? " + (ourUnidentifiedAccessKey != null) +
" | Our certificate present? " + (ourUnidentifiedAccessCertificate != null) +
" | UUID certificate supported? " + recipient.isUuidSupported());
if (theirUnidentifiedAccessKey != null &&
ourUnidentifiedAccessKey != null &&
ourUnidentifiedAccessCertificate != null)
{
if (theirUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) {
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(theirUnidentifiedAccessKey,
ourUnidentifiedAccessCertificate),
new UnidentifiedAccess(ourUnidentifiedAccessKey,
@ -75,13 +75,13 @@ public class UnidentifiedAccessUtil {
public static Optional<UnidentifiedAccessPair> getAccessForSync(@NonNull Context context) {
try {
byte[] ourUnidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey());
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(Recipient.self());
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
}
if (ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) {
if (ourUnidentifiedAccessCertificate != null) {
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(ourUnidentifiedAccessKey,
ourUnidentifiedAccessCertificate),
new UnidentifiedAccess(ourUnidentifiedAccessKey,
@ -95,6 +95,23 @@ public class UnidentifiedAccessUtil {
}
}
private static byte[] getUnidentifiedAccessCertificate(@NonNull Recipient recipient) {
CertificateType certificateType;
PhoneNumberPrivacyValues.PhoneNumberSharingMode sendPhoneNumberTo = SignalStore.phoneNumberPrivacy().getPhoneNumberSharingMode();
switch (sendPhoneNumberTo) {
case EVERYONE: certificateType = CertificateType.UUID_AND_E164; break;
case CONTACTS: certificateType = recipient.isSystemContact() ? CertificateType.UUID_AND_E164 : CertificateType.UUID_ONLY; break;
case NOBODY : certificateType = CertificateType.UUID_ONLY; break;
default : throw new AssertionError();
}
Log.i(TAG, String.format("Certificate type for %s with setting %s -> %s", recipient.getId(), sendPhoneNumberTo, certificateType));
return SignalStore.certificateValues()
.getUnidentifiedAccessCertificate(certificateType);
}
private static @Nullable byte[] getTargetUnidentifiedAccessKey(@NonNull Recipient recipient) {
ProfileKey theirProfileKey = ProfileKeyUtil.profileKeyOrNull(recipient.resolve().getProfileKey());

Wyświetl plik

@ -27,6 +27,8 @@ import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.CertificateType;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
@ -46,8 +48,8 @@ import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
@ -58,10 +60,12 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@ -330,23 +334,34 @@ public abstract class PushSendJob extends SendJob {
protected void rotateSenderCertificateIfNecessary() throws IOException {
try {
byte[] certificateBytes = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
Collection<CertificateType> requiredCertificateTypes = SignalStore.phoneNumberPrivacy()
.getRequiredCertificateTypes();
if (certificateBytes == null) {
throw new InvalidCertificateException("No certificate was present.");
Log.i(TAG, "Ensuring we have these certificates " + requiredCertificateTypes);
for (CertificateType certificateType : requiredCertificateTypes) {
byte[] certificateBytes = SignalStore.certificateValues()
.getUnidentifiedAccessCertificate(certificateType);
if (certificateBytes == null) {
throw new InvalidCertificateException(String.format("No certificate %s was present.", certificateType));
}
SenderCertificate certificate = new SenderCertificate(certificateBytes);
if (System.currentTimeMillis() > (certificate.getExpiration() - CERTIFICATE_EXPIRATION_BUFFER)) {
throw new InvalidCertificateException(String.format(Locale.US, "Certificate %s is expired, or close to it. Expires on: %d, currently: %d", certificateType, certificate.getExpiration(), System.currentTimeMillis()));
}
Log.d(TAG, String.format("Certificate %s is valid", certificateType));
}
SenderCertificate certificate = new SenderCertificate(certificateBytes);
if (System.currentTimeMillis() > (certificate.getExpiration() - CERTIFICATE_EXPIRATION_BUFFER)) {
throw new InvalidCertificateException("Certificate is expired, or close to it. Expires on: " + certificate.getExpiration() + ", currently: " + System.currentTimeMillis());
}
Log.d(TAG, "Certificate is valid.");
Log.d(TAG, "All certificates are valid.");
} catch (InvalidCertificateException e) {
Log.w(TAG, "Certificate was invalid at send time. Fetching a new one.", e);
RotateCertificateJob certificateJob = new RotateCertificateJob(context);
certificateJob.onRun();
Log.w(TAG, "A certificate was invalid at send time. Fetching new ones.", e);
if (!ApplicationDependencies.getJobManager().runSynchronously(new RotateCertificateJob(), 5000).isPresent()) {
throw new IOException("Timeout rotating certificate");
}
}
}

Wyświetl plik

@ -1,35 +1,37 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.CertificateType;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class RotateCertificateJob extends BaseJob {
public final class RotateCertificateJob extends BaseJob {
public static final String KEY = "RotateCertificateJob";
private static final String TAG = RotateCertificateJob.class.getSimpleName();
private static final String TAG = Log.tag(RotateCertificateJob.class);
public RotateCertificateJob(Context context) {
public RotateCertificateJob() {
this(new Job.Parameters.Builder()
.setQueue("__ROTATE_SENDER_CERTIFICATE__")
.addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(Parameters.UNLIMITED)
.build());
setContext(context);
}
private RotateCertificateJob(@NonNull Job.Parameters parameters) {
@ -57,10 +59,25 @@ public class RotateCertificateJob extends BaseJob {
}
synchronized (RotateCertificateJob.class) {
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
byte[] certificate = accountManager.getSenderCertificate();
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
Collection<CertificateType> certificateTypes = SignalStore.phoneNumberPrivacy()
.getAllCertificateTypes();
TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
Log.i(TAG, "Rotating these certificates " + certificateTypes);
for (CertificateType certificateType: certificateTypes) {
byte[] certificate;
switch (certificateType) {
case UUID_AND_E164: certificate = accountManager.getSenderCertificate(); break;
case UUID_ONLY : certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy(); break;
default : throw new AssertionError();
}
Log.i(TAG, String.format("Successfully got %s certificate", certificateType));
SignalStore.certificateValues()
.setUnidentifiedAccessCertificate(certificateType, certificate);
}
}
}

Wyświetl plik

@ -0,0 +1,6 @@
package org.thoughtcrime.securesms.keyvalue;
public enum CertificateType {
UUID_AND_E164,
UUID_ONLY
}

Wyświetl plik

@ -0,0 +1,45 @@
package org.thoughtcrime.securesms.keyvalue;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import java.util.Map;
public final class CertificateValues extends SignalStoreValues {
private static final String UD_CERTIFICATE_UUID_AND_E164 = "certificate.uuidAndE164";
private static final String UD_CERTIFICATE_UUID_ONLY = "certificate.uuidOnly";
CertificateValues(@NonNull KeyValueStore store) {
super(store);
}
@Override
void onFirstEverAppLaunch() {
}
@WorkerThread
public void setUnidentifiedAccessCertificate(@NonNull CertificateType certificateType,
@Nullable byte[] certificate)
{
KeyValueStore.Writer writer = getStore().beginWrite();
switch (certificateType) {
case UUID_AND_E164: writer.putBlob(UD_CERTIFICATE_UUID_AND_E164, certificate); break;
case UUID_ONLY : writer.putBlob(UD_CERTIFICATE_UUID_ONLY, certificate); break;
default : throw new AssertionError();
}
writer.commit();
}
public @Nullable byte[] getUnidentifiedAccessCertificate(@NonNull CertificateType certificateType) {
switch (certificateType) {
case UUID_AND_E164: return getBlob(UD_CERTIFICATE_UUID_AND_E164, null);
case UUID_ONLY : return getBlob(UD_CERTIFICATE_UUID_ONLY, null);
default : throw new AssertionError();
}
}
}

Wyświetl plik

@ -0,0 +1,86 @@
package org.thoughtcrime.securesms.keyvalue;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.util.FeatureFlags;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
public final class PhoneNumberPrivacyValues extends SignalStoreValues {
public static final String SHARING_MODE = "phoneNumberPrivacy.sharingMode";
public static final String LISTING_MODE = "phoneNumberPrivacy.listingMode";
private static final Collection<CertificateType> REGULAR_CERTIFICATE = Collections.singletonList(CertificateType.UUID_AND_E164);
private static final Collection<CertificateType> PRIVACY_CERTIFICATE = Collections.singletonList(CertificateType.UUID_ONLY);
private static final Collection<CertificateType> BOTH_CERTIFICATES = Collections.unmodifiableCollection(Arrays.asList(CertificateType.UUID_AND_E164, CertificateType.UUID_ONLY));
PhoneNumberPrivacyValues(@NonNull KeyValueStore store) {
super(store);
}
@Override
void onFirstEverAppLaunch() {
// TODO [ALAN] PhoneNumberPrivacy: During registration, set the attribute to so that new registrations start out as not listed
//getStore().beginWrite()
// .putInteger(LISTING_MODE, PhoneNumberListingMode.UNLISTED.ordinal())
// .apply();
}
public @NonNull PhoneNumberSharingMode getPhoneNumberSharingMode() {
if (!FeatureFlags.phoneNumberPrivacy()) return PhoneNumberSharingMode.EVERYONE;
return PhoneNumberSharingMode.values()[getInteger(SHARING_MODE, PhoneNumberSharingMode.EVERYONE.ordinal())];
}
public void setPhoneNumberSharingMode(@NonNull PhoneNumberSharingMode phoneNumberSharingMode) {
putInteger(SHARING_MODE, phoneNumberSharingMode.ordinal());
}
public @NonNull PhoneNumberListingMode getPhoneNumberListingMode() {
if (!FeatureFlags.phoneNumberPrivacy()) return PhoneNumberListingMode.LISTED;
return PhoneNumberListingMode.values()[getInteger(LISTING_MODE, PhoneNumberListingMode.LISTED.ordinal())];
}
public void setPhoneNumberListingMode(@NonNull PhoneNumberListingMode phoneNumberListingMode) {
putInteger(LISTING_MODE, phoneNumberListingMode.ordinal());
}
/**
* If you respect {@link #getPhoneNumberSharingMode}, then you will only ever need to fetch and store
* these certificates types.
*/
public Collection<CertificateType> getRequiredCertificateTypes() {
switch (getPhoneNumberSharingMode()) {
case EVERYONE: return REGULAR_CERTIFICATE;
case CONTACTS: return BOTH_CERTIFICATES;
case NOBODY : return PRIVACY_CERTIFICATE;
default : throw new AssertionError();
}
}
/**
* All certificate types required according to the feature flags.
*/
public Collection<CertificateType> getAllCertificateTypes() {
return FeatureFlags.phoneNumberPrivacy() ? BOTH_CERTIFICATES : REGULAR_CERTIFICATE;
}
/**
* Serialized, do not change ordinal/order
*/
public enum PhoneNumberSharingMode {
EVERYONE,
CONTACTS,
NOBODY
}
/**
* Serialized, do not change ordinal/order
*/
public enum PhoneNumberListingMode {
LISTED,
UNLISTED
}
}

Wyświetl plik

@ -13,32 +13,36 @@ public final class SignalStore {
private static final SignalStore INSTANCE = new SignalStore();
private final KeyValueStore store;
private final KbsValues kbsValues;
private final RegistrationValues registrationValues;
private final PinValues pinValues;
private final RemoteConfigValues remoteConfigValues;
private final StorageServiceValues storageServiceValues;
private final UiHints uiHints;
private final TooltipValues tooltipValues;
private final MiscellaneousValues misc;
private final InternalValues internalValues;
private final EmojiValues emojiValues;
private final SettingsValues settingsValues;
private final KeyValueStore store;
private final KbsValues kbsValues;
private final RegistrationValues registrationValues;
private final PinValues pinValues;
private final RemoteConfigValues remoteConfigValues;
private final StorageServiceValues storageServiceValues;
private final UiHints uiHints;
private final TooltipValues tooltipValues;
private final MiscellaneousValues misc;
private final InternalValues internalValues;
private final EmojiValues emojiValues;
private final SettingsValues settingsValues;
private final CertificateValues certificateValues;
private final PhoneNumberPrivacyValues phoneNumberPrivacyValues;
private SignalStore() {
this.store = ApplicationDependencies.getKeyValueStore();
this.kbsValues = new KbsValues(store);
this.registrationValues = new RegistrationValues(store);
this.pinValues = new PinValues(store);
this.remoteConfigValues = new RemoteConfigValues(store);
this.storageServiceValues = new StorageServiceValues(store);
this.uiHints = new UiHints(store);
this.tooltipValues = new TooltipValues(store);
this.misc = new MiscellaneousValues(store);
this.internalValues = new InternalValues(store);
this.emojiValues = new EmojiValues(store);
this.settingsValues = new SettingsValues(store);
this.store = ApplicationDependencies.getKeyValueStore();
this.kbsValues = new KbsValues(store);
this.registrationValues = new RegistrationValues(store);
this.pinValues = new PinValues(store);
this.remoteConfigValues = new RemoteConfigValues(store);
this.storageServiceValues = new StorageServiceValues(store);
this.uiHints = new UiHints(store);
this.tooltipValues = new TooltipValues(store);
this.misc = new MiscellaneousValues(store);
this.internalValues = new InternalValues(store);
this.emojiValues = new EmojiValues(store);
this.settingsValues = new SettingsValues(store);
this.certificateValues = new CertificateValues(store);
this.phoneNumberPrivacyValues = new PhoneNumberPrivacyValues(store);
}
public static void onFirstEverAppLaunch() {
@ -52,6 +56,8 @@ public final class SignalStore {
misc().onFirstEverAppLaunch();
internalValues().onFirstEverAppLaunch();
settings().onFirstEverAppLaunch();
certificateValues().onFirstEverAppLaunch();
phoneNumberPrivacy().onFirstEverAppLaunch();
}
public static @NonNull KbsValues kbsValues() {
@ -98,6 +104,14 @@ public final class SignalStore {
return INSTANCE.settingsValues;
}
public static @NonNull CertificateValues certificateValues() {
return INSTANCE.certificateValues;
}
public static @NonNull PhoneNumberPrivacyValues phoneNumberPrivacy() {
return INSTANCE.phoneNumberPrivacyValues;
}
public static @NonNull GroupsV2AuthorizationSignalStoreCache groupsV2AuthorizationCache() {
return new GroupsV2AuthorizationSignalStoreCache(getStore());
}

Wyświetl plik

@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import java.io.IOException;
import java.util.UUID;
@ -58,7 +57,6 @@ public class UuidMigrationJob extends MigrationJob {
ensureSelfRecipientExists(context);
fetchOwnUuid(context);
rotateSealedSenderCerts(context);
}
@Override
@ -78,14 +76,6 @@ public class UuidMigrationJob extends MigrationJob {
TextSecurePreferences.setLocalUuid(context, localUuid);
}
private static void rotateSealedSenderCerts(@NonNull Context context) throws IOException {
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
byte[] certificate = accountManager.getSenderCertificate();
TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
}
public static class Factory implements Job.Factory<UuidMigrationJob> {
@Override
public @NonNull UuidMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) {

Wyświetl plik

@ -7,6 +7,9 @@ import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.TextAppearanceSpan;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
@ -14,6 +17,7 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.autofill.HintConstants;
@ -37,6 +41,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.keyvalue.KbsValues;
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
import org.thoughtcrime.securesms.keyvalue.PinValues;
import org.thoughtcrime.securesms.keyvalue.SettingsValues;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@ -45,13 +50,13 @@ import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
import org.thoughtcrime.securesms.lock.v2.KbsConstants;
import org.thoughtcrime.securesms.lock.v2.RegistrationLockUtil;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.megaphone.MegaphoneRepository;
import org.thoughtcrime.securesms.megaphone.Megaphones;
import org.thoughtcrime.securesms.pin.RegistrationLockV2Dialog;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
@ -67,8 +72,10 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
private static final String TAG = Log.tag(AppProtectionPreferenceFragment.class);
private static final String PREFERENCE_CATEGORY_BLOCKED = "preference_category_blocked";
private static final String PREFERENCE_UNIDENTIFIED_LEARN_MORE = "pref_unidentified_learn_more";
private static final String PREFERENCE_CATEGORY_BLOCKED = "preference_category_blocked";
private static final String PREFERENCE_UNIDENTIFIED_LEARN_MORE = "pref_unidentified_learn_more";
private static final String PREFERENCE_WHO_CAN_SEE_PHONE_NUMBER = "pref_who_can_see_phone_number";
private static final String PREFERENCE_WHO_CAN_FIND_BY_PHONE_NUMBER = "pref_who_can_find_by_phone_number";
private CheckBoxPreference disablePassphrase;
@ -99,6 +106,18 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
this.findPreference(PREFERENCE_UNIDENTIFIED_LEARN_MORE).setOnPreferenceClickListener(new UnidentifiedLearnMoreClickListener());
disablePassphrase.setOnPreferenceChangeListener(new DisablePassphraseClickListener());
if (FeatureFlags.phoneNumberPrivacy()) {
Preference whoCanSeePhoneNumber = this.findPreference(PREFERENCE_WHO_CAN_SEE_PHONE_NUMBER);
Preference whoCanFindByPhoneNumber = this.findPreference(PREFERENCE_WHO_CAN_FIND_BY_PHONE_NUMBER);
whoCanSeePhoneNumber.setPreferenceDataStore(null);
whoCanSeePhoneNumber.setOnPreferenceClickListener(new PhoneNumberPrivacyWhoCanSeeClickListener());
whoCanFindByPhoneNumber.setPreferenceDataStore(null);
whoCanFindByPhoneNumber.setOnPreferenceClickListener(new PhoneNumberPrivacyWhoCanFindClickListener());
} else {
this.findPreference("category_phone_number_privacy").setVisible(false);
}
SwitchPreferenceCompat linkPreviewPref = (SwitchPreferenceCompat) this.findPreference(SettingsValues.LINK_PREVIEWS);
linkPreviewPref.setChecked(SignalStore.settings().isLinkPreviewsEnabled());
@ -138,6 +157,9 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
signalPinReminders.setEnabled(false);
registrationLockV2.setEnabled(false);
}
initializePhoneNumberPrivacyWhoCanSeeSummary();
initializePhoneNumberPrivacyWhoCanFindSummary();
}
@Override
@ -164,6 +186,27 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds));
}
private void initializePhoneNumberPrivacyWhoCanSeeSummary() {
Preference preference = findPreference(PREFERENCE_WHO_CAN_SEE_PHONE_NUMBER);
switch (SignalStore.phoneNumberPrivacy().getPhoneNumberSharingMode()) {
case EVERYONE: preference.setSummary(R.string.PhoneNumberPrivacy_everyone); break;
case CONTACTS: preference.setSummary(R.string.PhoneNumberPrivacy_my_contacts); break;
case NOBODY : preference.setSummary(R.string.PhoneNumberPrivacy_nobody); break;
default : throw new AssertionError();
}
}
private void initializePhoneNumberPrivacyWhoCanFindSummary() {
Preference preference = findPreference(PREFERENCE_WHO_CAN_FIND_BY_PHONE_NUMBER);
switch (SignalStore.phoneNumberPrivacy().getPhoneNumberListingMode()) {
case LISTED : preference.setSummary(R.string.PhoneNumberPrivacy_everyone); break;
case UNLISTED: preference.setSummary(R.string.PhoneNumberPrivacy_nobody); break;
default : throw new AssertionError();
}
}
private void initializeVisibility() {
if (TextSecurePreferences.isPasswordDisabled(getContext())) {
findPreference("pref_enable_passphrase_temporary").setVisible(false);
@ -504,4 +547,86 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
}
}
}
private final class PhoneNumberPrivacyWhoCanSeeClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
PhoneNumberPrivacyValues phoneNumberPrivacyValues = SignalStore.phoneNumberPrivacy();
final PhoneNumberPrivacyValues.PhoneNumberSharingMode[] value = { phoneNumberPrivacyValues.getPhoneNumberSharingMode() };
new AlertDialog.Builder(requireActivity())
.setTitle(R.string.preferences_app_protection__see_my_phone_number)
.setCancelable(true)
.setSingleChoiceItems(items(requireContext()), value[0].ordinal(), (dialog, which) -> value[0] = PhoneNumberPrivacyValues.PhoneNumberSharingMode.values()[which])
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
PhoneNumberPrivacyValues.PhoneNumberSharingMode phoneNumberSharingMode = value[0];
phoneNumberPrivacyValues.setPhoneNumberSharingMode(phoneNumberSharingMode);
Log.i(TAG, String.format("PhoneNumberSharingMode changed to %s. Scheduling storage value sync", phoneNumberSharingMode));
StorageSyncHelper.scheduleSyncForDataChange();
initializePhoneNumberPrivacyWhoCanSeeSummary();
})
.setNegativeButton(android.R.string.cancel, null)
.show();
return true;
}
private CharSequence[] items(Context context) {
return new CharSequence[]{
titleAndDescription(context, context.getString(R.string.PhoneNumberPrivacy_everyone), context.getString(R.string.PhoneNumberPrivacy_everyone_see_description)),
titleAndDescription(context, context.getString(R.string.PhoneNumberPrivacy_my_contacts), context.getString(R.string.PhoneNumberPrivacy_my_contacts_see_description)),
context.getString(R.string.PhoneNumberPrivacy_nobody) };
}
}
private final class PhoneNumberPrivacyWhoCanFindClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
PhoneNumberPrivacyValues phoneNumberPrivacyValues = SignalStore.phoneNumberPrivacy();
final PhoneNumberPrivacyValues.PhoneNumberListingMode[] value = { phoneNumberPrivacyValues.getPhoneNumberListingMode() };
new AlertDialog.Builder(requireActivity())
.setTitle(R.string.preferences_app_protection__find_me_by_phone_number)
.setCancelable(true)
.setSingleChoiceItems(items(requireContext()),
value[0].ordinal(),
(dialog, which) -> value[0] = PhoneNumberPrivacyValues.PhoneNumberListingMode.values()[which])
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
PhoneNumberPrivacyValues.PhoneNumberListingMode phoneNumberListingMode = value[0];
phoneNumberPrivacyValues.setPhoneNumberListingMode(phoneNumberListingMode);
Log.i(TAG, String.format("PhoneNumberListingMode changed to %s. Scheduling storage value sync", phoneNumberListingMode));
StorageSyncHelper.scheduleSyncForDataChange();
initializePhoneNumberPrivacyWhoCanFindSummary();
})
.setNegativeButton(android.R.string.cancel, null)
.show();
return true;
}
private CharSequence[] items(Context context) {
return new CharSequence[]{
titleAndDescription(context, context.getString(R.string.PhoneNumberPrivacy_everyone), context.getString(R.string.PhoneNumberPrivacy_everyone_find_description)),
context.getString(R.string.PhoneNumberPrivacy_nobody) };
}
}
/** Adds a detail row for radio group descriptions. */
private static CharSequence titleAndDescription(@NonNull Context context, @NonNull String header, @NonNull String description) {
SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append("\n");
builder.append(header);
builder.append("\n");
builder.setSpan(new TextAppearanceSpan(context, android.R.style.TextAppearance_Small), builder.length(), builder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
builder.append(description);
builder.append("\n");
return builder;
}
}

Wyświetl plik

@ -174,7 +174,7 @@ public final class CodeVerificationRequest {
private static void handleSuccessfulRegistration(@NonNull Context context) {
JobManager jobManager = ApplicationDependencies.getJobManager();
jobManager.add(new DirectoryRefreshJob(false));
jobManager.add(new RotateCertificateJob(context));
jobManager.add(new RotateCertificateJob());
DirectoryRefreshListener.schedule(context);
RotateSignedPreKeyListener.schedule(context);

Wyświetl plik

@ -4,7 +4,6 @@ package org.thoughtcrime.securesms.service;
import android.content.Context;
import android.content.Intent;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -22,7 +21,7 @@ public class RotateSenderCertificateListener extends PersistentAlarmManagerListe
@Override
protected long onAlarm(Context context, long scheduledTime) {
ApplicationDependencies.getJobManager().add(new RotateCertificateJob(context));
ApplicationDependencies.getJobManager().add(new RotateCertificateJob());
long nextTime = System.currentTimeMillis() + INTERVAL;
TextSecurePreferences.setUnidentifiedAccessCertificateRotationTime(context, nextTime);

Wyświetl plik

@ -6,6 +6,7 @@ import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.logging.Log;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.storage.SignalAccountRecord;
import org.whispersystems.signalservice.internal.storage.protos.AccountRecord;
import java.util.Arrays;
import java.util.Collection;
@ -55,16 +56,18 @@ class AccountConflictMerger implements StorageSyncHelper.ConflictMerger<SignalAc
familyName = local.getFamilyName().or("");
}
byte[] unknownFields = remote.serializeUnknownFields();
String avatarUrlPath = remote.getAvatarUrlPath().or(local.getAvatarUrlPath()).or("");
byte[] profileKey = remote.getProfileKey().or(local.getProfileKey()).orNull();
boolean noteToSelfArchived = remote.isNoteToSelfArchived();
boolean readReceipts = remote.isReadReceiptsEnabled();
boolean typingIndicators = remote.isTypingIndicatorsEnabled();
boolean sealedSenderIndicators = remote.isSealedSenderIndicatorsEnabled();
boolean linkPreviews = remote.isLinkPreviewsEnabled();
boolean matchesRemote = doParamsMatch(remote, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews);
boolean matchesLocal = doParamsMatch(local, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews);
byte[] unknownFields = remote.serializeUnknownFields();
String avatarUrlPath = remote.getAvatarUrlPath().or(local.getAvatarUrlPath()).or("");
byte[] profileKey = remote.getProfileKey().or(local.getProfileKey()).orNull();
boolean noteToSelfArchived = remote.isNoteToSelfArchived();
boolean readReceipts = remote.isReadReceiptsEnabled();
boolean typingIndicators = remote.isTypingIndicatorsEnabled();
boolean sealedSenderIndicators = remote.isSealedSenderIndicatorsEnabled();
boolean linkPreviews = remote.isLinkPreviewsEnabled();
boolean unlisted = remote.isPhoneNumberUnlisted();
AccountRecord.PhoneNumberSharingMode phoneNumberSharingMode = remote.getPhoneNumberSharingMode();
boolean matchesRemote = doParamsMatch(remote, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews, phoneNumberSharingMode, unlisted);
boolean matchesLocal = doParamsMatch(local, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews, phoneNumberSharingMode, unlisted );
if (matchesRemote) {
return remote;
@ -82,6 +85,9 @@ class AccountConflictMerger implements StorageSyncHelper.ConflictMerger<SignalAc
.setTypingIndicatorsEnabled(typingIndicators)
.setSealedSenderIndicatorsEnabled(sealedSenderIndicators)
.setLinkPreviewsEnabled(linkPreviews)
.setUnlistedPhoneNumber(unlisted)
.setPhoneNumberSharingMode(phoneNumberSharingMode)
.setUnlistedPhoneNumber(unlisted)
.build();
}
}
@ -96,7 +102,9 @@ class AccountConflictMerger implements StorageSyncHelper.ConflictMerger<SignalAc
boolean readReceipts,
boolean typingIndicators,
boolean sealedSenderIndicators,
boolean linkPreviewsEnabled)
boolean linkPreviewsEnabled,
AccountRecord.PhoneNumberSharingMode phoneNumberSharingMode,
boolean unlistedPhoneNumber)
{
return Arrays.equals(contact.serializeUnknownFields(), unknownFields) &&
Objects.equals(contact.getGivenName().or(""), givenName) &&
@ -107,6 +115,8 @@ class AccountConflictMerger implements StorageSyncHelper.ConflictMerger<SignalAc
contact.isReadReceiptsEnabled() == readReceipts &&
contact.isTypingIndicatorsEnabled() == typingIndicators &&
contact.isSealedSenderIndicatorsEnabled() == sealedSenderIndicators &&
contact.isLinkPreviewsEnabled() == linkPreviewsEnabled;
contact.isLinkPreviewsEnabled() == linkPreviewsEnabled &&
contact.getPhoneNumberSharingMode() == phoneNumberSharingMode &&
contact.isPhoneNumberUnlisted() == unlistedPhoneNumber;
}
}

Wyświetl plik

@ -14,11 +14,11 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
import org.thoughtcrime.securesms.jobs.StorageSyncJob;
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.SetUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@ -32,6 +32,7 @@ import org.whispersystems.signalservice.api.storage.SignalStorageManifest;
import org.whispersystems.signalservice.api.storage.SignalStorageRecord;
import org.whispersystems.signalservice.api.storage.StorageId;
import org.whispersystems.signalservice.api.util.OptionalUtil;
import org.whispersystems.signalservice.internal.storage.protos.AccountRecord;
import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord;
import java.nio.ByteBuffer;
@ -398,11 +399,22 @@ public final class StorageSyncHelper {
.setReadReceiptsEnabled(TextSecurePreferences.isReadReceiptsEnabled(context))
.setSealedSenderIndicatorsEnabled(TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context))
.setLinkPreviewsEnabled(SignalStore.settings().isLinkPreviewsEnabled())
.setUnlistedPhoneNumber(SignalStore.phoneNumberPrivacy().getPhoneNumberListingMode() == PhoneNumberPrivacyValues.PhoneNumberListingMode.UNLISTED)
.setPhoneNumberSharingMode(localToRemotePhoneNumberSharingMode(SignalStore.phoneNumberPrivacy().getPhoneNumberSharingMode()))
.build();
return SignalStorageRecord.forAccount(account);
}
private static AccountRecord.PhoneNumberSharingMode localToRemotePhoneNumberSharingMode(PhoneNumberPrivacyValues.PhoneNumberSharingMode phoneNumberPhoneNumberSharingMode) {
switch (phoneNumberPhoneNumberSharingMode) {
case EVERYONE: return AccountRecord.PhoneNumberSharingMode.EVERYBODY;
case CONTACTS: return AccountRecord.PhoneNumberSharingMode.CONTACTS_ONLY;
case NOBODY : return AccountRecord.PhoneNumberSharingMode.NOBODY;
default : throw new AssertionError();
}
}
public static void applyAccountStorageSyncUpdates(@NonNull Context context, Optional<StorageSyncHelper.RecordUpdate<SignalAccountRecord>> update) {
if (!update.isPresent()) {
return;

Wyświetl plik

@ -49,20 +49,21 @@ public final class FeatureFlags {
private static final long FETCH_INTERVAL = TimeUnit.HOURS.toMillis(2);
private static final String USERNAMES = "android.usernames";
private static final String ATTACHMENTS_V3 = "android.attachmentsV3.2";
private static final String REMOTE_DELETE = "android.remoteDelete";
private static final String GROUPS_V2_OLD_1 = "android.groupsv2";
private static final String GROUPS_V2_OLD_2 = "android.groupsv2.2";
private static final String GROUPS_V2 = "android.groupsv2.3";
private static final String GROUPS_V2_CREATE = "android.groupsv2.create.3";
private static final String GROUPS_V2_JOIN_VERSION = "android.groupsv2.joinVersion";
private static final String GROUPS_V2_LINKS_VERSION = "android.groupsv2.manageGroupLinksVersion";
private static final String GROUPS_V2_CAPACITY = "global.groupsv2.maxGroupSize";
private static final String CDS_VERSION = "android.cdsVersion";
private static final String INTERNAL_USER = "android.internalUser";
private static final String MENTIONS = "android.mentions";
private static final String VERIFY_V2 = "android.verifyV2";
private static final String USERNAMES = "android.usernames";
private static final String ATTACHMENTS_V3 = "android.attachmentsV3.2";
private static final String REMOTE_DELETE = "android.remoteDelete";
private static final String GROUPS_V2_OLD_1 = "android.groupsv2";
private static final String GROUPS_V2_OLD_2 = "android.groupsv2.2";
private static final String GROUPS_V2 = "android.groupsv2.3";
private static final String GROUPS_V2_CREATE = "android.groupsv2.create.3";
private static final String GROUPS_V2_JOIN_VERSION = "android.groupsv2.joinVersion";
private static final String GROUPS_V2_LINKS_VERSION = "android.groupsv2.manageGroupLinksVersion";
private static final String GROUPS_V2_CAPACITY = "global.groupsv2.maxGroupSize";
private static final String CDS_VERSION = "android.cdsVersion";
private static final String INTERNAL_USER = "android.internalUser";
private static final String MENTIONS = "android.mentions";
private static final String VERIFY_V2 = "android.verifyV2";
private static final String PHONE_NUMBER_PRIVACY_VERSION = "android.phoneNumberPrivacyVersion";
/**
* We will only store remote values for flags in this set. If you want a flag to be controllable
@ -279,6 +280,14 @@ public final class FeatureFlags {
return getBoolean(VERIFY_V2, false);
}
/**
* Whether the user can choose phone number privacy settings, and;
* Whether to fetch and store the secondary certificate
*/
public static boolean phoneNumberPrivacy() {
return getVersionFlag(PHONE_NUMBER_PRIVACY_VERSION) == VersionFlag.ON;
}
/** Only for rendering debug info. */
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
return new TreeMap<>(REMOTE_VALUES);

Wyświetl plik

@ -182,7 +182,6 @@ public class TextSecurePreferences {
private static final String NEEDS_MESSAGE_PULL = "pref_needs_message_pull";
private static final String UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF = "pref_unidentified_access_certificate_rotation_time";
private static final String UNIDENTIFIED_ACCESS_CERTIFICATE = "pref_unidentified_access_certificate_uuid";
public static final String UNIVERSAL_UNIDENTIFIED_ACCESS = "pref_universal_unidentified_access";
public static final String SHOW_UNIDENTIFIED_DELIVERY_INDICATORS = "pref_show_unidentifed_delivery_indicators";
private static final String UNIDENTIFIED_DELIVERY_ENABLED = "pref_unidentified_delivery_enabled";
@ -598,26 +597,6 @@ public class TextSecurePreferences {
setLongPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF, value);
}
public static void setUnidentifiedAccessCertificate(Context context, byte[] value) {
setStringPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE, Base64.encodeBytes(value));
}
public static byte[] getUnidentifiedAccessCertificate(Context context) {
return parseCertificate(getStringPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE, null));
}
private static byte[] parseCertificate(String raw) {
try {
if (raw != null) {
return Base64.decode(raw);
}
} catch (IOException e) {
Log.w(TAG, e);
}
return null;
}
public static boolean isUniversalUnidentifiedAccess(Context context) {
return getBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, false);
}

Wyświetl plik

@ -165,7 +165,8 @@
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/name_container" />
app:layout_constraintTop_toBottomOf="@id/name_container"
tools:visibility="visible" />
<EditText
android:id="@+id/profile_overview_username"
@ -182,7 +183,8 @@
android:visibility="gone"
app:layout_constraintEnd_toStartOf="@id/profile_overview_username_edit_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/profile_overview_username_label" />
app:layout_constraintTop_toBottomOf="@id/profile_overview_username_label"
tools:visibility="visible" />
<ImageView
android:id="@+id/profile_overview_username_edit_button"
@ -196,7 +198,8 @@
app:layout_constraintBottom_toBottomOf="@id/profile_overview_username"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/profile_overview_username"
app:srcCompat="@drawable/ic_compose_solid_24" />
app:srcCompat="@drawable/ic_compose_solid_24"
tools:visibility="visible" />
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
android:id="@+id/description_text"

Wyświetl plik

@ -2106,6 +2106,7 @@
<string name="preferences_advanced__disable_signal_built_in_emoji_support">Disable Signal\'s built-in emoji support</string>
<string name="preferences_advanced__relay_all_calls_through_the_signal_server_to_avoid_revealing_your_ip_address">Relay all calls through the Signal server to avoid revealing your IP address to your contact. Enabling will reduce call quality.</string>
<string name="preferences_advanced__always_relay_calls">Always relay calls</string>
<string name="preferences_app_protection__who_can">Who can…</string>
<string name="preferences_app_protection__app_access">App access</string>
<string name="preferences_app_protection__communication">Communication</string>
<string name="preferences_chats__chats">Chats</string>
@ -2505,6 +2506,14 @@
<string name="RegistrationActivity_code_support_subject">Signal Registration - Verification Code for Android</string>
<string name="BackupUtil_never">Never</string>
<string name="BackupUtil_unknown">Unknown</string>
<string name="preferences_app_protection__see_my_phone_number">See my phone number</string>
<string name="preferences_app_protection__find_me_by_phone_number">Find me by phone number</string>
<string name="PhoneNumberPrivacy_everyone">Everyone</string>
<string name="PhoneNumberPrivacy_my_contacts">My contacts</string>
<string name="PhoneNumberPrivacy_nobody">Nobody</string>
<string name="PhoneNumberPrivacy_everyone_see_description">Your phone number will be visible to all people and groups you message.</string>
<string name="PhoneNumberPrivacy_everyone_find_description">Anyone who has your phone number in their contacts will see you as a contact on Signal. Others will be able to find you in search.</string>
<string name="PhoneNumberPrivacy_my_contacts_see_description">Only your contacts will see your phone number on Signal.</string>
<string name="preferences_app_protection__screen_lock">Screen lock</string>
<string name="preferences_app_protection__lock_signal_access_with_android_screen_lock_or_fingerprint">Lock Signal access with Android screen lock or fingerprint</string>
<string name="preferences_app_protection__screen_lock_inactivity_timeout">Screen lock inactivity timeout</string>

Wyświetl plik

@ -1,6 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="category_phone_number_privacy"
android:title="@string/preferences_app_protection__who_can">
<Preference
android:key="pref_who_can_see_phone_number"
android:title="@string/preferences_app_protection__see_my_phone_number" />
<Preference
android:key="pref_who_can_find_by_phone_number"
android:title="@string/preferences_app_protection__find_me_by_phone_number" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences_app_protection__app_access">
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat

Wyświetl plik

@ -9,7 +9,6 @@ package org.whispersystems.signalservice.api;
import com.google.protobuf.ByteString;
import org.signal.zkgroup.VerificationFailedException;
import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.whispersystems.libsignal.IdentityKey;
@ -80,13 +79,10 @@ import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.sql.Time;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -148,8 +144,8 @@ public class SignalServiceAccountManager {
return this.pushServiceSocket.getSenderCertificate();
}
public byte[] getSenderCertificateLegacy() throws IOException {
return this.pushServiceSocket.getSenderCertificateLegacy();
public byte[] getSenderCertificateForPhoneNumberPrivacy() throws IOException {
return this.pushServiceSocket.getUuidOnlySenderCertificate();
}
/**

Wyświetl plik

@ -586,6 +586,7 @@ public class SignalServiceMessageSender {
quoteBuilder.setAuthorUuid(message.getQuote().get().getAuthor().getUuid().get().toString());
}
// TODO [Alan] PhoneNumberPrivacy: Do not set this number
if (message.getQuote().get().getAuthor().getNumber().isPresent()) {
quoteBuilder.setAuthorE164(message.getQuote().get().getAuthor().getNumber().get());
}
@ -682,6 +683,7 @@ public class SignalServiceMessageSender {
.setRemove(message.getReaction().get().isRemove())
.setTargetSentTimestamp(message.getReaction().get().getTargetSentTimestamp());
// TODO [Alan] PhoneNumberPrivacy: Do not set this number
if (message.getReaction().get().getTargetAuthor().getNumber().isPresent()) {
reactionBuilder.setTargetAuthorE164(message.getReaction().get().getTargetAuthor().getNumber().get());
}

Wyświetl plik

@ -80,6 +80,14 @@ public final class SignalAccountRecord implements SignalRecord {
return proto.getLinkPreviews();
}
public AccountRecord.PhoneNumberSharingMode getPhoneNumberSharingMode() {
return proto.getPhoneNumberSharingMode();
}
public boolean isPhoneNumberUnlisted() {
return proto.getUnlistedPhoneNumber();
}
AccountRecord toProto() {
return proto;
}
@ -159,6 +167,16 @@ public final class SignalAccountRecord implements SignalRecord {
return this;
}
public Builder setPhoneNumberSharingMode(AccountRecord.PhoneNumberSharingMode mode) {
builder.setPhoneNumberSharingMode(mode);
return this;
}
public Builder setUnlistedPhoneNumber(boolean unlisted) {
builder.setUnlistedPhoneNumber(unlisted);
return this;
}
public SignalAccountRecord build() {
AccountRecord proto = builder.build();

Wyświetl plik

@ -181,8 +181,8 @@ public class PushServiceSocket {
private static final String PROFILE_PATH = "/v1/profile/%s";
private static final String PROFILE_USERNAME_PATH = "/v1/profile/username/%s";
private static final String SENDER_CERTIFICATE_LEGACY_PATH = "/v1/certificate/delivery";
private static final String SENDER_CERTIFICATE_PATH = "/v1/certificate/delivery?includeUuid=true";
private static final String SENDER_CERTIFICATE_PATH = "/v1/certificate/delivery?includeUuid=true";
private static final String SENDER_CERTIFICATE_NO_E164_PATH = "/v1/certificate/delivery?includeUuid=true&includeE164=false";
private static final String KBS_AUTH_PATH = "/v1/backup/auth";
@ -363,13 +363,13 @@ public class PushServiceSocket {
makeServiceRequest(REGISTRATION_LOCK_PATH, "DELETE", null);
}
public byte[] getSenderCertificateLegacy() throws IOException {
String responseText = makeServiceRequest(SENDER_CERTIFICATE_LEGACY_PATH, "GET", null);
public byte[] getSenderCertificate() throws IOException {
String responseText = makeServiceRequest(SENDER_CERTIFICATE_PATH, "GET", null);
return JsonUtil.fromJson(responseText, SenderCertificate.class).getCertificate();
}
public byte[] getSenderCertificate() throws IOException {
String responseText = makeServiceRequest(SENDER_CERTIFICATE_PATH, "GET", null);
public byte[] getUuidOnlySenderCertificate() throws IOException {
String responseText = makeServiceRequest(SENDER_CERTIFICATE_NO_E164_PATH, "GET", null);
return JsonUtil.fromJson(responseText, SenderCertificate.class).getCertificate();
}

Wyświetl plik

@ -97,15 +97,24 @@ message GroupV2Record {
}
message AccountRecord {
bytes profileKey = 1;
string givenName = 2;
string familyName = 3;
string avatarUrlPath = 4;
bool noteToSelfArchived = 5;
bool readReceipts = 6;
bool sealedSenderIndicators = 7;
bool typingIndicators = 8;
bool proxiedLinkPreviews = 9;
enum PhoneNumberSharingMode {
EVERYBODY = 0;
CONTACTS_ONLY = 1;
NOBODY = 2;
}
bytes profileKey = 1;
string givenName = 2;
string familyName = 3;
string avatarUrlPath = 4;
bool noteToSelfArchived = 5;
bool readReceipts = 6;
bool sealedSenderIndicators = 7;
bool typingIndicators = 8;
bool proxiedLinkPreviews = 9;
// 10 is reserved for unread
bool linkPreviews = 11;
bool linkPreviews = 11;
PhoneNumberSharingMode phoneNumberSharingMode = 12;
bool unlistedPhoneNumber = 13;
}