Add support for registering with token and specifying UA.

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2015-08-20 10:16:45 -07:00
parent c05108727f
commit 4c93231c3c
7 changed files with 78 additions and 32 deletions
@@ -57,6 +57,7 @@ public class TextSecureAccountManager {
private final PushServiceSocket pushServiceSocket;
private final String user;
private final String userAgent;
/**
* Construct a TextSecureAccountManager.
@@ -65,12 +66,15 @@ public class TextSecureAccountManager {
* @param trustStore The {@link org.whispersystems.textsecure.api.push.TrustStore} for the TextSecure server's TLS certificate.
* @param user A TextSecure phone number.
* @param password A TextSecure password.
* @param userAgent A string which identifies the client software.
*/
public TextSecureAccountManager(String url, TrustStore trustStore,
String user, String password)
String user, String password,
String userAgent)
{
this.pushServiceSocket = new PushServiceSocket(url, trustStore, new StaticCredentialsProvider(user, password, null));
this.pushServiceSocket = new PushServiceSocket(url, trustStore, new StaticCredentialsProvider(user, password, null), userAgent);
this.user = user;
this.userAgent = userAgent;
}
/**
@@ -108,14 +112,13 @@ public class TextSecureAccountManager {
}
/**
* Verify a TextSecure account.
* Verify a TextSecure account with a received SMS or voice verification code.
*
* @param verificationCode The verification code received via SMS or Voice
* (see {@link #requestSmsVerificationCode} and
* {@link #requestVoiceVerificationCode}).
* @param signalingKey 52 random bytes. A 32 byte AES key and a 20 byte Hmac256 key,
* concatenated.
* @param supportsSms Indicate whether this client is capable of supporting encrypted SMS.
* @param axolotlRegistrationId A random 14-bit number that identifies this TextSecure install.
* This value should remain consistent across registrations for the
* same install, but probabilistically differ across registrations
@@ -123,12 +126,31 @@ public class TextSecureAccountManager {
*
* @throws IOException
*/
public void verifyAccount(String verificationCode, String signalingKey,
boolean supportsSms, int axolotlRegistrationId)
public void verifyAccountWithCode(String verificationCode, String signalingKey, int axolotlRegistrationId)
throws IOException
{
this.pushServiceSocket.verifyAccount(verificationCode, signalingKey,
supportsSms, axolotlRegistrationId);
this.pushServiceSocket.verifyAccountCode(verificationCode, signalingKey,
axolotlRegistrationId);
}
/**
* Verify a TextSecure account with a signed token from a trusted source.
*
* @param verificationToken The signed token provided by a trusted server.
* @param signalingKey 52 random bytes. A 32 byte AES key and a 20 byte Hmac256 key,
* concatenated.
* @param axolotlRegistrationId A random 14-bit number that identifies this TextSecure install.
* This value should remain consistent across registrations for the
* same install, but probabilistically differ across registrations
* for separate installs.
*
* @throws IOException
*/
public void verifyAccountWithToken(String verificationToken, String signalingKey, int axolotlRegistrationId)
throws IOException
{
this.pushServiceSocket.verifyAccountToken(verificationToken, signalingKey, axolotlRegistrationId);
}
/**
@@ -47,6 +47,7 @@ public class TextSecureMessageReceiver {
private final TrustStore trustStore;
private final String url;
private final CredentialsProvider credentialsProvider;
private final String userAgent;
/**
* Construct a TextSecureMessageReceiver.
@@ -59,9 +60,10 @@ public class TextSecureMessageReceiver {
* @param signalingKey The 52 byte signaling key assigned to this user at registration.
*/
public TextSecureMessageReceiver(String url, TrustStore trustStore,
String user, String password, String signalingKey)
String user, String password,
String signalingKey, String userAgent)
{
this(url, trustStore, new StaticCredentialsProvider(user, password, signalingKey));
this(url, trustStore, new StaticCredentialsProvider(user, password, signalingKey), userAgent);
}
/**
@@ -72,11 +74,14 @@ public class TextSecureMessageReceiver {
* the server's TLS signing certificate.
* @param credentials The TextSecure user's credentials.
*/
public TextSecureMessageReceiver(String url, TrustStore trustStore, CredentialsProvider credentials) {
public TextSecureMessageReceiver(String url, TrustStore trustStore,
CredentialsProvider credentials, String userAgent)
{
this.url = url;
this.trustStore = trustStore;
this.credentialsProvider = credentials;
this.socket = new PushServiceSocket(url, trustStore, credentials);
this.socket = new PushServiceSocket(url, trustStore, credentials, userAgent);
this.userAgent = userAgent;
}
/**
@@ -124,7 +129,7 @@ public class TextSecureMessageReceiver {
* @return A TextSecureMessagePipe for receiving TextSecure messages.
*/
public TextSecureMessagePipe createMessagePipe() {
WebSocketConnection webSocket = new WebSocketConnection(url, trustStore, credentialsProvider);
WebSocketConnection webSocket = new WebSocketConnection(url, trustStore, credentialsProvider, userAgent);
return new TextSecureMessagePipe(webSocket, credentialsProvider);
}
@@ -88,9 +88,10 @@ public class TextSecureMessageSender {
public TextSecureMessageSender(String url, TrustStore trustStore,
String user, String password,
AxolotlStore store,
String userAgent,
Optional<EventListener> eventListener)
{
this.socket = new PushServiceSocket(url, trustStore, new StaticCredentialsProvider(user, password, null));
this.socket = new PushServiceSocket(url, trustStore, new StaticCredentialsProvider(user, password, null), userAgent);
this.store = store;
this.localAddress = new TextSecureAddress(user);
this.eventListener = eventListener;
@@ -23,15 +23,11 @@ public class AccountAttributes {
@JsonProperty
private String signalingKey;
@JsonProperty
private boolean supportsSms;
@JsonProperty
private int registrationId;
public AccountAttributes(String signalingKey, boolean supportsSms, int registrationId) {
public AccountAttributes(String signalingKey, int registrationId) {
this.signalingKey = signalingKey;
this.supportsSms = supportsSms;
this.registrationId = registrationId;
}
@@ -41,11 +37,8 @@ public class AccountAttributes {
return signalingKey;
}
public boolean isSupportsSms() {
return supportsSms;
}
public int getRegistrationId() {
return registrationId;
}
}
@@ -79,7 +79,8 @@ public class PushServiceSocket {
private static final String CREATE_ACCOUNT_SMS_PATH = "/v1/accounts/sms/code/%s";
private static final String CREATE_ACCOUNT_VOICE_PATH = "/v1/accounts/voice/code/%s";
private static final String VERIFY_ACCOUNT_PATH = "/v1/accounts/code/%s";
private static final String VERIFY_ACCOUNT_CODE_PATH = "/v1/accounts/code/%s";
private static final String VERIFY_ACCOUNT_TOKEN_PATH = "/v1/accounts/token/%s";
private static final String REGISTER_GCM_PATH = "/v1/accounts/gcm/";
private static final String PREKEY_METADATA_PATH = "/v2/keys/";
@@ -103,12 +104,14 @@ public class PushServiceSocket {
private final String serviceUrl;
private final TrustManager[] trustManagers;
private final CredentialsProvider credentialsProvider;
private final String userAgent;
public PushServiceSocket(String serviceUrl, TrustStore trustStore, CredentialsProvider credentialsProvider)
public PushServiceSocket(String serviceUrl, TrustStore trustStore, CredentialsProvider credentialsProvider, String userAgent)
{
this.serviceUrl = serviceUrl;
this.credentialsProvider = credentialsProvider;
this.trustManagers = BlacklistingTrustManager.createFor(trustStore);
this.userAgent = userAgent;
}
public void createAccount(boolean voice) throws IOException {
@@ -116,12 +119,19 @@ public class PushServiceSocket {
makeRequest(String.format(path, credentialsProvider.getUser()), "GET", null);
}
public void verifyAccount(String verificationCode, String signalingKey,
boolean supportsSms, int registrationId)
public void verifyAccountCode(String verificationCode, String signalingKey, int registrationId)
throws IOException
{
AccountAttributes signalingKeyEntity = new AccountAttributes(signalingKey, supportsSms, registrationId);
makeRequest(String.format(VERIFY_ACCOUNT_PATH, verificationCode),
AccountAttributes signalingKeyEntity = new AccountAttributes(signalingKey, registrationId);
makeRequest(String.format(VERIFY_ACCOUNT_CODE_PATH, verificationCode),
"PUT", JsonUtil.toJson(signalingKeyEntity));
}
public void verifyAccountToken(String verificationToken, String signalingKey, int registrationId)
throws IOException
{
AccountAttributes signalingKeyEntity = new AccountAttributes(signalingKey, registrationId);
makeRequest(String.format(VERIFY_ACCOUNT_TOKEN_PATH, verificationToken),
"PUT", JsonUtil.toJson(signalingKeyEntity));
}
@@ -579,6 +589,10 @@ public class PushServiceSocket {
connection.setRequestProperty("Authorization", getAuthorizationHeader());
}
if (userAgent != null) {
connection.setRequestProperty("X-Signal-Agent", userAgent);
}
if (body != null) {
connection.setDoOutput(true);
}
@@ -31,6 +31,7 @@ public class OkHttpClientWrapper implements WebSocketListener {
private final TrustStore trustStore;
private final CredentialsProvider credentialsProvider;
private final WebSocketEventListener listener;
private final String userAgent;
private WebSocket webSocket;
private boolean closed;
@@ -38,6 +39,7 @@ public class OkHttpClientWrapper implements WebSocketListener {
public OkHttpClientWrapper(String uri, TrustStore trustStore,
CredentialsProvider credentialsProvider,
String userAgent,
WebSocketEventListener listener)
{
Log.w(TAG, "Connecting to: " + uri);
@@ -45,6 +47,7 @@ public class OkHttpClientWrapper implements WebSocketListener {
this.uri = uri;
this.trustStore = trustStore;
this.credentialsProvider = credentialsProvider;
this.userAgent = userAgent;
this.listener = listener;
}
@@ -127,7 +130,13 @@ public class OkHttpClientWrapper implements WebSocketListener {
okHttpClient.setReadTimeout(timeout, unit);
okHttpClient.setConnectTimeout(timeout, unit);
return WebSocket.newWebSocket(okHttpClient, new Request.Builder().url(filledUri).build());
Request.Builder requestBuilder = new Request.Builder().url(filledUri);
if (userAgent != null) {
requestBuilder.addHeader("X-Signal-Agent", userAgent);
}
return WebSocket.newWebSocket(okHttpClient, requestBuilder.build());
}
private SSLSocketFactory createTlsSocketFactory(TrustStore trustStore) {
@@ -27,13 +27,15 @@ public class WebSocketConnection implements WebSocketEventListener {
private final String wsUri;
private final TrustStore trustStore;
private final CredentialsProvider credentialsProvider;
private final String userAgent;
private OkHttpClientWrapper client;
private KeepAliveSender keepAliveSender;
public WebSocketConnection(String httpUri, TrustStore trustStore, CredentialsProvider credentialsProvider) {
public WebSocketConnection(String httpUri, TrustStore trustStore, CredentialsProvider credentialsProvider, String userAgent) {
this.trustStore = trustStore;
this.credentialsProvider = credentialsProvider;
this.userAgent = userAgent;
this.wsUri = httpUri.replace("https://", "wss://")
.replace("http://", "ws://") + "/v1/websocket/?login=%s&password=%s";
}
@@ -42,7 +44,7 @@ public class WebSocketConnection implements WebSocketEventListener {
Log.w(TAG, "WSC connect()...");
if (client == null) {
client = new OkHttpClientWrapper(wsUri, trustStore, credentialsProvider, this);
client = new OkHttpClientWrapper(wsUri, trustStore, credentialsProvider, userAgent, this);
client.connect(KEEPALIVE_TIMEOUT_SECONDS + 10, TimeUnit.SECONDS);
}
}