Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Commit

Permalink
Support for multiple service URLs
Browse files Browse the repository at this point in the history
// FREEBIE
  • Loading branch information
moxie0 committed Dec 30, 2016
1 parent 73c716d commit 43fc72d
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,16 @@ public class SignalServiceAccountManager {
/**
* Construct a SignalServiceAccountManager.
*
* @param url The URL for the Signal Service.
* @param trustStore The {@link org.whispersystems.signalservice.api.push.TrustStore} for the SignalService server's TLS certificate.
* @param urls The URL for the Signal Service.
* @param user A Signal Service phone number.
* @param password A Signal Service password.
* @param userAgent A string which identifies the client software.
*/
public SignalServiceAccountManager(SignalServiceUrl url, TrustStore trustStore,
public SignalServiceAccountManager(SignalServiceUrl[] urls,
String user, String password,
String userAgent)
{
this.pushServiceSocket = new PushServiceSocket(url, trustStore, new StaticCredentialsProvider(user, password, null), userAgent);
this.pushServiceSocket = new PushServiceSocket(urls, new StaticCredentialsProvider(user, password, null), userAgent);
this.user = user;
this.userAgent = userAgent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.push.TrustStore;
import org.whispersystems.signalservice.api.util.CredentialsProvider;
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import org.whispersystems.signalservice.internal.push.SignalServiceEnvelopeEntity;
Expand All @@ -34,43 +33,36 @@
public class SignalServiceMessageReceiver {

private final PushServiceSocket socket;
private final TrustStore trustStore;
private final SignalServiceUrl url;
private final SignalServiceUrl[] urls;
private final CredentialsProvider credentialsProvider;
private final String userAgent;

/**
* Construct a SignalServiceMessageReceiver.
*
* @param url The URL of the Signal Service.
* @param trustStore The {@link org.whispersystems.signalservice.api.push.TrustStore} containing
* the server's TLS signing certificate.
* @param urls The URL of the Signal Service.
* @param user The Signal Service username (eg. phone number).
* @param password The Signal Service user password.
* @param signalingKey The 52 byte signaling key assigned to this user at registration.
*/
public SignalServiceMessageReceiver(SignalServiceUrl url, TrustStore trustStore,
public SignalServiceMessageReceiver(SignalServiceUrl[] urls,
String user, String password,
String signalingKey, String userAgent)
{
this(url, trustStore, new StaticCredentialsProvider(user, password, signalingKey), userAgent);
this(urls, new StaticCredentialsProvider(user, password, signalingKey), userAgent);
}

/**
* Construct a SignalServiceMessageReceiver.
*
* @param url The URL of the Signal Service.
* @param trustStore The {@link org.whispersystems.signalservice.api.push.TrustStore} containing
* the server's TLS signing certificate.
* @param urls The URL of the Signal Service.
* @param credentials The Signal Service user's credentials.
*/
public SignalServiceMessageReceiver(SignalServiceUrl url, TrustStore trustStore,
CredentialsProvider credentials, String userAgent)
public SignalServiceMessageReceiver(SignalServiceUrl[] urls, CredentialsProvider credentials, String userAgent)
{
this.url = url;
this.trustStore = trustStore;
this.urls = urls;
this.credentialsProvider = credentials;
this.socket = new PushServiceSocket(url, trustStore, credentials, userAgent);
this.socket = new PushServiceSocket(urls, credentials, userAgent);
this.userAgent = userAgent;
}

Expand Down Expand Up @@ -119,7 +111,7 @@ public InputStream retrieveAttachment(SignalServiceAttachmentPointer pointer, Fi
* @return A SignalServiceMessagePipe for receiving Signal Service messages.
*/
public SignalServiceMessagePipe createMessagePipe() {
WebSocketConnection webSocket = new WebSocketConnection(url.getUrl(), trustStore, credentialsProvider, userAgent);
WebSocketConnection webSocket = new WebSocketConnection(urls[0].getUrl(), urls[0].getTrustStore(), credentialsProvider, userAgent);
return new SignalServiceMessagePipe(webSocket, credentialsProvider);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,20 @@ public class SignalServiceMessageSender {
/**
* Construct a SignalServiceMessageSender.
*
* @param url The URL of the Signal Service.
* @param trustStore The trust store containing the Signal Service's signing TLS certificate.
* @param urls The URL of the Signal Service.
* @param user The Signal Service username (eg phone number).
* @param password The Signal Service user password.
* @param store The SignalProtocolStore.
* @param eventListener An optional event listener, which fires whenever sessions are
* setup or torn down for a recipient.
*/
public SignalServiceMessageSender(SignalServiceUrl url, TrustStore trustStore,
public SignalServiceMessageSender(SignalServiceUrl[] urls,
String user, String password,
SignalProtocolStore store,
String userAgent,
Optional<EventListener> eventListener)
{
this.socket = new PushServiceSocket(url, trustStore, new StaticCredentialsProvider(user, password, null), userAgent);
this.socket = new PushServiceSocket(urls, new StaticCredentialsProvider(user, password, null), userAgent);
this.store = store;
this.localAddress = new SignalServiceAddress(user);
this.eventListener = eventListener;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo;
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.TrustStore;
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity;
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
import org.whispersystems.signalservice.api.push.exceptions.ExpectationFailedException;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
Expand All @@ -55,6 +54,7 @@
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -94,17 +94,24 @@ public class PushServiceSocket {
private static final String RECEIPT_PATH = "/v1/receipt/%s/%d";
private static final String ATTACHMENT_PATH = "/v1/attachments/%s";

private final SignalServiceUrl serviceUrl;
private final TrustManager[] trustManagers;
private final CredentialsProvider credentialsProvider;
private final String userAgent;
private final SignalConnectionInformation[] signalConnectionInformation;
private final CredentialsProvider credentialsProvider;
private final String userAgent;
private final SecureRandom random;

public PushServiceSocket(SignalServiceUrl serviceUrl, TrustStore trustStore, CredentialsProvider credentialsProvider, String userAgent)
{
this.serviceUrl = serviceUrl;
this.credentialsProvider = credentialsProvider;
this.trustManagers = BlacklistingTrustManager.createFor(trustStore);
this.userAgent = userAgent;
public PushServiceSocket(SignalServiceUrl[] serviceUrls, CredentialsProvider credentialsProvider, String userAgent) {
try {
this.credentialsProvider = credentialsProvider;
this.userAgent = userAgent;
this.signalConnectionInformation = new SignalConnectionInformation[serviceUrls.length];
this.random = SecureRandom.getInstance("SHA1PRNG");

for (int i = 0; i < serviceUrls.length; i++) {
signalConnectionInformation[i] = new SignalConnectionInformation(serviceUrls[i]);
}
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}

public void createAccount(boolean voice) throws IOException {
Expand Down Expand Up @@ -552,8 +559,13 @@ private Response getConnection(String urlFragment, String method, String body)
throws PushNetworkException
{
try {
Log.w(TAG, "Push service URL: " + serviceUrl.getUrl());
Log.w(TAG, "Opening URL: " + String.format("%s%s", serviceUrl.getUrl(), urlFragment));
SignalConnectionInformation connectionInformation = getRandom(signalConnectionInformation, random);
String url = connectionInformation.getUrl();
Optional<String> hostHeader = connectionInformation.getHostHeader();
TrustManager[] trustManagers = connectionInformation.getTrustManagers();

Log.w(TAG, "Push service URL: " + url);
Log.w(TAG, "Opening URL: " + String.format("%s%s", url, urlFragment));

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, null);
Expand All @@ -563,7 +575,7 @@ private Response getConnection(String urlFragment, String method, String body)
okHttpClient.setHostnameVerifier(new StrictHostnameVerifier());

Request.Builder request = new Request.Builder();
request.url(String.format("%s%s", serviceUrl.getUrl(), urlFragment));
request.url(String.format("%s%s", url, urlFragment));

if (body != null) {
request.method(method, RequestBody.create(MediaType.parse("application/json"), body));
Expand All @@ -579,11 +591,10 @@ private Response getConnection(String urlFragment, String method, String body)
request.addHeader("X-Signal-Agent", userAgent);
}

if (serviceUrl.getHostHeader().isPresent()) {
okHttpClient.networkInterceptors().add(new HostInterceptor(serviceUrl.getHostHeader().get()));
if (hostHeader.isPresent()) {
okHttpClient.networkInterceptors().add(new HostInterceptor(hostHeader.get()));
}


return okHttpClient.newCall(request.build()).execute();
} catch (IOException e) {
throw new PushNetworkException(e);
Expand All @@ -600,6 +611,12 @@ private String getAuthorizationHeader() {
}
}

private SignalConnectionInformation getRandom(SignalConnectionInformation[] connections,
SecureRandom random)
{
return connections[random.nextInt(connections.length)];
}

private static class GcmRegistrationId {

@JsonProperty
Expand Down Expand Up @@ -646,4 +663,29 @@ public Response intercept(Chain chain) throws IOException {
return chain.proceed(request.newBuilder().header("Host", host).build());
}
}

private static class SignalConnectionInformation {

private final String url;
private final Optional<String> hostHeader;
private final TrustManager[] trustManagers;

private SignalConnectionInformation(SignalServiceUrl signalServiceUrl) {
this.url = signalServiceUrl.getUrl();
this.hostHeader = signalServiceUrl.getHostHeader();
this.trustManagers = BlacklistingTrustManager.createFor(signalServiceUrl.getTrustStore());
}

String getUrl() {
return url;
}

Optional<String> getHostHeader() {
return hostHeader;
}

TrustManager[] getTrustManagers() {
return trustManagers;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@


import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.TrustStore;

public class SignalServiceUrl {

private final Optional<String> hostHeader;
private final String url;
private final Optional<String> hostHeader;
private TrustStore trustStore;

public SignalServiceUrl(String url) {
this(url, null);
public SignalServiceUrl(String url, TrustStore trustStore) {
this(url, null, trustStore);
}

public SignalServiceUrl(String url, String hostHeader) {
public SignalServiceUrl(String url, String hostHeader, TrustStore trustStore) {
this.url = url;
this.hostHeader = Optional.fromNullable(hostHeader);
this.trustStore = trustStore;
}


Expand All @@ -25,4 +28,8 @@ public Optional<String> getHostHeader() {
public String getUrl() {
return url;
}

public TrustStore getTrustStore() {
return trustStore;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package org.whispersystems.signalservice.internal.util;

import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.signalservice.api.push.TrustStore;

import java.io.IOException;
Expand All @@ -32,8 +33,8 @@
*/
public class BlacklistingTrustManager implements X509TrustManager {

private static final List<BigInteger> BLACKLIST = new LinkedList<BigInteger>() {{
add(new BigInteger("4098"));
private static final List<Pair<String, BigInteger>> BLACKLIST = new LinkedList<Pair<String, BigInteger>>() {{
add(new Pair<>("Open Whisper Systems", new BigInteger("4098")));
}};

public static TrustManager[] createFor(TrustManager[] trustManagers) {
Expand Down Expand Up @@ -85,8 +86,10 @@ public void checkServerTrusted(X509Certificate[] chain, String authType)
trustManager.checkServerTrusted(chain, authType);

for (X509Certificate certificate : chain) {
for (BigInteger blacklistedSerial : BLACKLIST) {
if (certificate.getSerialNumber().equals(blacklistedSerial)) {
for (Pair<String, BigInteger> blacklistedSerial : BLACKLIST) {
if (certificate.getIssuerDN().getName().equals(blacklistedSerial.first()) &&
certificate.getSerialNumber().equals(blacklistedSerial.second()))
{
throw new CertificateException("Blacklisted Serial: " + certificate.getSerialNumber());
}
}
Expand Down

0 comments on commit 43fc72d

Please sign in to comment.