Add libsignal-net CDSI implementation.
This commit is contained in:
parent
46c8b3b690
commit
56eae8c7bf
5 changed files with 66 additions and 12 deletions
|
@ -208,6 +208,7 @@ android {
|
||||||
buildConfigField("String", "GIPHY_API_KEY", "\"3o6ZsYH6U6Eri53TXy\"")
|
buildConfigField("String", "GIPHY_API_KEY", "\"3o6ZsYH6U6Eri53TXy\"")
|
||||||
buildConfigField("String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/registration/generate.html\"")
|
buildConfigField("String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/registration/generate.html\"")
|
||||||
buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/challenge/generate.html\"")
|
buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/challenge/generate.html\"")
|
||||||
|
buildConfigField("org.signal.libsignal.net.Network.Environment", "LIBSIGNAL_NET_ENV", "org.signal.libsignal.net.Network.Environment.PRODUCTION")
|
||||||
|
|
||||||
buildConfigField("String", "BUILD_DISTRIBUTION_TYPE", "\"unset\"")
|
buildConfigField("String", "BUILD_DISTRIBUTION_TYPE", "\"unset\"")
|
||||||
buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"unset\"")
|
buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"unset\"")
|
||||||
|
@ -385,6 +386,7 @@ android {
|
||||||
buildConfigField("String", "MOBILE_COIN_ENVIRONMENT", "\"testnet\"")
|
buildConfigField("String", "MOBILE_COIN_ENVIRONMENT", "\"testnet\"")
|
||||||
buildConfigField("String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/staging/registration/generate.html\"")
|
buildConfigField("String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/staging/registration/generate.html\"")
|
||||||
buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/staging/challenge/generate.html\"")
|
buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/staging/challenge/generate.html\"")
|
||||||
|
buildConfigField("org.signal.libsignal.net.Network.Environment", "LIBSIGNAL_NET_ENV", "org.signal.libsignal.net.Network.Environment.STAGING")
|
||||||
|
|
||||||
buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"Staging\"")
|
buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"Staging\"")
|
||||||
buildConfigField("String", "STRIPE_PUBLISHABLE_KEY", "\"pk_test_sngOd8FnXNkpce9nPXawKrJD00kIDngZkD\"")
|
buildConfigField("String", "STRIPE_PUBLISHABLE_KEY", "\"pk_test_sngOd8FnXNkpce9nPXawKrJD00kIDngZkD\"")
|
||||||
|
|
|
@ -126,7 +126,8 @@ object ContactDiscoveryRefreshV2 {
|
||||||
SignalDatabase.recipients.getAllServiceIdProfileKeyPairs(),
|
SignalDatabase.recipients.getAllServiceIdProfileKeyPairs(),
|
||||||
Optional.ofNullable(token),
|
Optional.ofNullable(token),
|
||||||
BuildConfig.CDSI_MRENCLAVE,
|
BuildConfig.CDSI_MRENCLAVE,
|
||||||
timeoutMs
|
timeoutMs,
|
||||||
|
if (FeatureFlags.useLibsignalNetForCdsiLookup()) BuildConfig.LIBSIGNAL_NET_ENV else null
|
||||||
) { tokenToSave ->
|
) { tokenToSave ->
|
||||||
stopwatch.split("network-pre-token")
|
stopwatch.split("network-pre-token")
|
||||||
if (!isPartialRefresh) {
|
if (!isPartialRefresh) {
|
||||||
|
|
|
@ -123,6 +123,7 @@ public final class FeatureFlags {
|
||||||
private static final String RETRY_RECEIPT_MAX_COUNT = "android.retryReceipt.maxCount";
|
private static final String RETRY_RECEIPT_MAX_COUNT = "android.retryReceipt.maxCount";
|
||||||
private static final String RETRY_RECEIPT_MAX_COUNT_RESET_AGE = "android.retryReceipt.maxCountResetAge";
|
private static final String RETRY_RECEIPT_MAX_COUNT_RESET_AGE = "android.retryReceipt.maxCountResetAge";
|
||||||
private static final String PREKEY_FORCE_REFRESH_INTERVAL = "android.prekeyForceRefreshInterval";
|
private static final String PREKEY_FORCE_REFRESH_INTERVAL = "android.prekeyForceRefreshInterval";
|
||||||
|
private static final String CDSI_LIBSIGNAL_NET = "android.cds.libsignal";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||||
|
@ -198,7 +199,8 @@ public final class FeatureFlags {
|
||||||
VIDEO_RECORD_1X_ZOOM,
|
VIDEO_RECORD_1X_ZOOM,
|
||||||
RETRY_RECEIPT_MAX_COUNT,
|
RETRY_RECEIPT_MAX_COUNT,
|
||||||
RETRY_RECEIPT_MAX_COUNT_RESET_AGE,
|
RETRY_RECEIPT_MAX_COUNT_RESET_AGE,
|
||||||
PREKEY_FORCE_REFRESH_INTERVAL
|
PREKEY_FORCE_REFRESH_INTERVAL,
|
||||||
|
CDSI_LIBSIGNAL_NET
|
||||||
);
|
);
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -271,7 +273,8 @@ public final class FeatureFlags {
|
||||||
VIDEO_RECORD_1X_ZOOM,
|
VIDEO_RECORD_1X_ZOOM,
|
||||||
RETRY_RECEIPT_MAX_COUNT,
|
RETRY_RECEIPT_MAX_COUNT,
|
||||||
RETRY_RECEIPT_MAX_COUNT_RESET_AGE,
|
RETRY_RECEIPT_MAX_COUNT_RESET_AGE,
|
||||||
PREKEY_FORCE_REFRESH_INTERVAL
|
PREKEY_FORCE_REFRESH_INTERVAL,
|
||||||
|
CDSI_LIBSIGNAL_NET
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -706,6 +709,11 @@ public final class FeatureFlags {
|
||||||
return getLong(PREKEY_FORCE_REFRESH_INTERVAL, TimeUnit.HOURS.toMillis(1));
|
return getLong(PREKEY_FORCE_REFRESH_INTERVAL, TimeUnit.HOURS.toMillis(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Make CDSI lookups via libsignal-net instead of native websocket. */
|
||||||
|
public static boolean useLibsignalNetForCdsiLookup() {
|
||||||
|
return getBoolean(CDSI_LIBSIGNAL_NET, false);
|
||||||
|
}
|
||||||
|
|
||||||
/** Only for rendering debug info. */
|
/** Only for rendering debug info. */
|
||||||
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
||||||
return new TreeMap<>(REMOTE_VALUES);
|
return new TreeMap<>(REMOTE_VALUES);
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.whispersystems.signalservice.api;
|
||||||
|
|
||||||
import com.squareup.wire.FieldEncoding;
|
import com.squareup.wire.FieldEncoding;
|
||||||
|
|
||||||
|
import org.signal.libsignal.net.Network;
|
||||||
import org.signal.libsignal.protocol.IdentityKeyPair;
|
import org.signal.libsignal.protocol.IdentityKeyPair;
|
||||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||||
import org.signal.libsignal.protocol.ecc.ECPublicKey;
|
import org.signal.libsignal.protocol.ecc.ECPublicKey;
|
||||||
|
@ -130,7 +131,6 @@ public class SignalServiceAccountManager {
|
||||||
private final GroupsV2Operations groupsV2Operations;
|
private final GroupsV2Operations groupsV2Operations;
|
||||||
private final SignalServiceConfiguration configuration;
|
private final SignalServiceConfiguration configuration;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a SignalServiceAccountManager.
|
* Construct a SignalServiceAccountManager.
|
||||||
* @param configuration The URL for the Signal Service.
|
* @param configuration The URL for the Signal Service.
|
||||||
|
@ -365,11 +365,12 @@ public class SignalServiceAccountManager {
|
||||||
Optional<byte[]> token,
|
Optional<byte[]> token,
|
||||||
String mrEnclave,
|
String mrEnclave,
|
||||||
Long timeoutMs,
|
Long timeoutMs,
|
||||||
|
@Nullable Network.Environment libsignalNetEnv,
|
||||||
Consumer<byte[]> tokenSaver)
|
Consumer<byte[]> tokenSaver)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
CdsiAuthResponse auth = pushServiceSocket.getCdsiAuth();
|
CdsiAuthResponse auth = pushServiceSocket.getCdsiAuth();
|
||||||
CdsiV2Service service = new CdsiV2Service(configuration, mrEnclave);
|
CdsiV2Service service = new CdsiV2Service(configuration, mrEnclave, libsignalNetEnv);
|
||||||
CdsiV2Service.Request request = new CdsiV2Service.Request(previousE164s, newE164s, serviceIds, token);
|
CdsiV2Service.Request request = new CdsiV2Service.Request(previousE164s, newE164s, serviceIds, token);
|
||||||
Single<ServiceResponse<CdsiV2Service.Response>> single = service.getRegisteredUsers(auth.getUsername(), auth.getPassword(), request, tokenSaver);
|
Single<ServiceResponse<CdsiV2Service.Response>> single = service.getRegisteredUsers(auth.getUsername(), auth.getPassword(), request, tokenSaver);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@ package org.whispersystems.signalservice.api.services;
|
||||||
|
|
||||||
import org.signal.cdsi.proto.ClientRequest;
|
import org.signal.cdsi.proto.ClientRequest;
|
||||||
import org.signal.cdsi.proto.ClientResponse;
|
import org.signal.cdsi.proto.ClientResponse;
|
||||||
|
import org.signal.core.util.logging.Log;
|
||||||
|
import org.signal.libsignal.net.CdsiLookupRequest;
|
||||||
|
import org.signal.libsignal.net.CdsiLookupResponse;
|
||||||
|
import org.signal.libsignal.net.Network;
|
||||||
import org.signal.libsignal.protocol.util.ByteUtil;
|
import org.signal.libsignal.protocol.util.ByteUtil;
|
||||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||||
|
@ -16,6 +20,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceConf
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -24,9 +29,11 @@ import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.core.Observable;
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
import okio.ByteString;
|
import okio.ByteString;
|
||||||
|
|
||||||
|
@ -40,16 +47,35 @@ public final class CdsiV2Service {
|
||||||
private static final UUID EMPTY_UUID = new UUID(0, 0);
|
private static final UUID EMPTY_UUID = new UUID(0, 0);
|
||||||
private static final int RESPONSE_ITEM_SIZE = 8 + 16 + 16; // 1 uint64 + 2 UUIDs
|
private static final int RESPONSE_ITEM_SIZE = 8 + 16 + 16; // 1 uint64 + 2 UUIDs
|
||||||
|
|
||||||
private final CdsiSocket cdsiSocket;
|
private static final Duration LIBSIGNAL_CDSI_TIMEOUT = Duration.ofSeconds(10);
|
||||||
|
|
||||||
public CdsiV2Service(SignalServiceConfiguration configuration, String mrEnclave) {
|
private final CdsiRequestHandler cdsiRequestHandler;
|
||||||
this.cdsiSocket = new CdsiSocket(configuration, mrEnclave);
|
|
||||||
}
|
public CdsiV2Service(SignalServiceConfiguration configuration, String mrEnclave, Network.Environment libsignalEnv) {
|
||||||
|
|
||||||
|
if (libsignalEnv != null) {
|
||||||
|
Network network = new Network(libsignalEnv);
|
||||||
|
this.cdsiRequestHandler = (username, password, request, tokenSaver) -> {
|
||||||
|
try {
|
||||||
|
Log.i(TAG, "Starting CDSI lookup via libsignal-net");
|
||||||
|
Future<CdsiLookupResponse> cdsiRequest = network.cdsiLookup(username, password, buildLibsignalRequest(request), LIBSIGNAL_CDSI_TIMEOUT, tokenSaver);
|
||||||
|
return Single.fromFuture(cdsiRequest).map(CdsiV2Service::parseLibsignalResponse).toObservable();
|
||||||
|
} catch (Exception exception) {
|
||||||
|
return Observable.error(exception);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
CdsiSocket cdsiSocket = new CdsiSocket(configuration, mrEnclave);
|
||||||
|
this.cdsiRequestHandler = (username, password, request, tokenSaver) -> {
|
||||||
|
return cdsiSocket
|
||||||
|
.connect(username, password, buildClientRequest(request), tokenSaver)
|
||||||
|
.map(CdsiV2Service::parseEntries);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Single<ServiceResponse<Response>> getRegisteredUsers(String username, String password, Request request, Consumer<byte[]> tokenSaver) {
|
public Single<ServiceResponse<Response>> getRegisteredUsers(String username, String password, Request request, Consumer<byte[]> tokenSaver) {
|
||||||
return cdsiSocket
|
return cdsiRequestHandler.handleRequest(username, password, request, tokenSaver)
|
||||||
.connect(username, password, buildClientRequest(request), tokenSaver)
|
|
||||||
.map(CdsiV2Service::parseEntries)
|
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
.flatMap(pages -> {
|
.flatMap(pages -> {
|
||||||
Map<String, ResponseItem> all = new HashMap<>();
|
Map<String, ResponseItem> all = new HashMap<>();
|
||||||
|
@ -139,6 +165,18 @@ public final class CdsiV2Service {
|
||||||
return ByteString.of(os.toByteArray());
|
return ByteString.of(os.toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static CdsiLookupRequest buildLibsignalRequest(Request request) {
|
||||||
|
HashMap<org.signal.libsignal.protocol.ServiceId, ProfileKey> serviceIds = new HashMap<>(request.serviceIds.size());
|
||||||
|
request.serviceIds.forEach((key, value) -> serviceIds.put(key.getLibSignalServiceId(), value));
|
||||||
|
return new CdsiLookupRequest(request.previousE164s, request.newE164s, serviceIds, false, Optional.ofNullable(request.token));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Response parseLibsignalResponse(CdsiLookupResponse response) {
|
||||||
|
HashMap<String, ResponseItem> responses = new HashMap<>(response.entries().size());
|
||||||
|
response.entries().forEach((key, value) -> responses.put(key, new ResponseItem(new PNI(value.pni), Optional.ofNullable(value.aci).map(ACI::new))));
|
||||||
|
return new Response(responses, response.debugPermitsUsed);
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Long> parseAndSortE164Strings(Collection<String> e164s) {
|
private static List<Long> parseAndSortE164Strings(Collection<String> e164s) {
|
||||||
return e164s.stream()
|
return e164s.stream()
|
||||||
.map(Long::parseLong)
|
.map(Long::parseLong)
|
||||||
|
@ -216,4 +254,8 @@ public final class CdsiV2Service {
|
||||||
return aci.isPresent();
|
return aci.isPresent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface CdsiRequestHandler {
|
||||||
|
Observable<Response> handleRequest(String username, String password, Request request, Consumer<byte[]> tokenSaver);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue