Library accepts push connection certificate as argument.

This commit is contained in:
Moxie Marlinspike 2013-11-27 11:08:58 -08:00
parent ff5ad4b85d
commit 1ab4e7e9de
15 changed files with 99 additions and 83 deletions

View File

@ -25,11 +25,11 @@ public class PushDestination {
}
public static PushDestination create(Context context,
PushServiceSocket.PushCredentials credentials,
String localNumber,
String destinationNumber)
throws InvalidNumberException
{
String e164destination = PhoneNumberFormatter.formatNumber(destinationNumber, credentials.getLocalNumber(context));
String e164destination = PhoneNumberFormatter.formatNumber(destinationNumber, localNumber);
String relay = Directory.getInstance(context).getRelay(e164destination);
return new PushDestination(e164destination, relay);

View File

@ -7,7 +7,6 @@ import android.util.Pair;
import com.google.thoughtcrimegson.Gson;
import org.apache.http.conn.ssl.StrictHostnameVerifier;
import org.whispersystems.textsecure.R;
import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.storage.PreKeyRecord;
import org.whispersystems.textsecure.util.Base64;
@ -57,16 +56,14 @@ public class PushServiceSocket {
private final String password;
private final TrustManagerFactory trustManagerFactory;
public PushServiceSocket(Context context, String serviceUrl, String localNumber, String password) {
public PushServiceSocket(Context context, String serviceUrl, TrustStore trustStore,
String localNumber, String password)
{
this.context = context.getApplicationContext();
this.serviceUrl = serviceUrl;
this.localNumber = localNumber;
this.password = password;
this.trustManagerFactory = initializeTrustManagerFactory(context);
}
public PushServiceSocket(Context context, String serviceUrl, PushCredentials credentials) {
this(context, serviceUrl, credentials.getLocalNumber(context), credentials.getPassword(context));
this.trustManagerFactory = initializeTrustManagerFactory(trustStore);
}
public void createAccount(boolean voice) throws IOException {
@ -76,7 +73,8 @@ public class PushServiceSocket {
public void verifyAccount(String verificationCode, String signalingKey) throws IOException {
SignalingKey signalingKeyEntity = new SignalingKey(signalingKey);
makeRequest(String.format(VERIFY_ACCOUNT_PATH, verificationCode), "PUT", new Gson().toJson(signalingKeyEntity));
makeRequest(String.format(VERIFY_ACCOUNT_PATH, verificationCode),
"PUT", new Gson().toJson(signalingKeyEntity));
}
public void registerGcmId(String gcmRegistrationId) throws IOException {
@ -373,15 +371,15 @@ public class PushServiceSocket {
}
}
private TrustManagerFactory initializeTrustManagerFactory(Context context) {
private TrustManagerFactory initializeTrustManagerFactory(TrustStore trustStore) {
try {
InputStream keyStoreInputStream = context.getResources().openRawResource(R.raw.whisper);
KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream keyStoreInputStream = trustStore.getKeyStoreInputStream();
KeyStore keyStore = KeyStore.getInstance("BKS");
trustStore.load(keyStoreInputStream, "whisper".toCharArray());
keyStore.load(keyStoreInputStream, trustStore.getKeyStorePassword().toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(trustStore);
trustManagerFactory.init(keyStore);
return trustManagerFactory;
} catch (KeyStoreException kse) {
@ -417,8 +415,8 @@ public class PushServiceSocket {
}
}
public interface PushCredentials {
public String getLocalNumber(Context context);
public String getPassword(Context context);
public interface TrustStore {
public InputStream getKeyStoreInputStream();
public String getKeyStorePassword();
}
}

View File

@ -1,5 +0,0 @@
Thank you for helping us test this BETA vesion of TextSecure.
This is BETA software, please do not use it in situations where security is critical.
Please report any problems at https://github.com/WhisperSystems/TextSecure/issues

View File

@ -45,11 +45,11 @@ import android.widget.Toast;
import com.actionbarsherlock.view.MenuItem;
import com.google.android.gcm.GCMRegistrar;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
import org.thoughtcrime.securesms.util.TextSecurePushCredentials;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
@ -57,6 +57,7 @@ import org.thoughtcrime.securesms.util.MemoryCleaner;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Trimmer;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.push.AuthorizationFailedException;
import org.whispersystems.textsecure.push.PushServiceSocket;
@ -307,8 +308,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
protected Integer doInBackground(Void... params) {
try {
Context context = ApplicationPreferencesActivity.this;
PushServiceSocket socket = new PushServiceSocket(context, Release.PUSH_URL,
TextSecurePushCredentials.getInstance());
PushServiceSocket socket = PushServiceSocketFactory.create(context);
socket.unregisterGcmId();
GCMRegistrar.unregister(context);

View File

@ -30,6 +30,7 @@ import android.widget.Toast;
import com.actionbarsherlock.app.SherlockActivity;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.service.RegistrationService;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.push.PushServiceSocket;
@ -498,7 +499,7 @@ public class RegistrationProgressActivity extends SherlockActivity {
@Override
protected Integer doInBackground(Void... params) {
try {
PushServiceSocket socket = new PushServiceSocket(context, Release.PUSH_URL, e164number, password);
PushServiceSocket socket = PushServiceSocketFactory.create(context, e164number, password);
socket.verifyAccount(code, signalingKey);
return SUCCESS;
} catch (RateLimitException e) {
@ -585,7 +586,7 @@ public class RegistrationProgressActivity extends SherlockActivity {
@Override
protected Integer doInBackground(Void... params) {
try {
PushServiceSocket socket = new PushServiceSocket(context, Release.PUSH_URL, e164number, password);
PushServiceSocket socket = PushServiceSocketFactory.create(context, e164number, password);
socket.createAccount(true);
return SUCCESS;

View File

@ -7,10 +7,10 @@ import android.util.Log;
import com.google.android.gcm.GCMBaseIntentService;
import org.thoughtcrime.securesms.Release;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.service.RegistrationService;
import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.TextSecurePushCredentials;
import org.whispersystems.textsecure.crypto.InvalidVersionException;
import org.whispersystems.textsecure.directory.Directory;
import org.whispersystems.textsecure.directory.NotInDirectoryException;
@ -34,7 +34,7 @@ public class GcmIntentService extends GCMBaseIntentService {
sendBroadcast(intent);
} else {
try {
PushServiceSocket pushSocket = new PushServiceSocket(context, Release.PUSH_URL, TextSecurePushCredentials.getInstance());
PushServiceSocket pushSocket = PushServiceSocketFactory.create(context);
pushSocket.registerGcmId(registrationId);
} catch (IOException e) {
Log.w("GcmIntentService", e);
@ -45,7 +45,7 @@ public class GcmIntentService extends GCMBaseIntentService {
@Override
protected void onUnregistered(Context context, String registrationId) {
try {
PushServiceSocket pushSocket = new PushServiceSocket(context, Release.PUSH_URL, TextSecurePushCredentials.getInstance());
PushServiceSocket pushSocket = PushServiceSocketFactory.create(context);
pushSocket.unregisterGcmId();
} catch (IOException ioe) {
Log.w("GcmIntentService", ioe);

View File

@ -0,0 +1,22 @@
package org.thoughtcrime.securesms.push;
import android.content.Context;
import org.thoughtcrime.securesms.Release;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.push.PushServiceSocket;
public class PushServiceSocketFactory {
public static PushServiceSocket create(Context context, String number, String password) {
return new PushServiceSocket(context, Release.PUSH_URL, new TextSecurePushTrustStore(context),
number, password);
}
public static PushServiceSocket create(Context context) {
return create(context,
TextSecurePreferences.getLocalNumber(context),
TextSecurePreferences.getPushServerPassword(context));
}
}

View File

@ -0,0 +1,27 @@
package org.thoughtcrime.securesms.push;
import android.content.Context;
import org.thoughtcrime.securesms.R;
import org.whispersystems.textsecure.push.PushServiceSocket;
import java.io.InputStream;
public class TextSecurePushTrustStore implements PushServiceSocket.TrustStore {
private final Context context;
public TextSecurePushTrustStore(Context context) {
this.context = context.getApplicationContext();
}
@Override
public InputStream getKeyStoreInputStream() {
return context.getResources().openRawResource(R.raw.whisper);
}
@Override
public String getKeyStorePassword() {
return "whisper";
}
}

View File

@ -8,8 +8,8 @@ import android.os.PowerManager;
import android.util.Log;
import org.thoughtcrime.securesms.Release;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.TextSecurePushCredentials;
import org.whispersystems.textsecure.directory.Directory;
import org.whispersystems.textsecure.push.ContactTokenDetails;
@ -60,7 +60,7 @@ public class DirectoryRefreshService extends Service {
try {
Log.w("DirectoryRefreshService", "Refreshing directory...");
Directory directory = Directory.getInstance(context);
PushServiceSocket socket = new PushServiceSocket(context, Release.PUSH_URL, TextSecurePushCredentials.getInstance());
PushServiceSocket socket = PushServiceSocketFactory.create(context);
Set<String> eligibleContactTokens = directory.getPushEligibleContactTokens(TextSecurePreferences.getLocalNumber(context));
List<ContactTokenDetails> activeTokens = socket.retrieveDirectory(eligibleContactTokens);

View File

@ -10,7 +10,7 @@ import org.thoughtcrime.securesms.Release;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingPartDatabase;
import org.thoughtcrime.securesms.database.PartDatabase;
import org.thoughtcrime.securesms.util.TextSecurePushCredentials;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.textsecure.crypto.AttachmentCipherInputStream;
import org.whispersystems.textsecure.crypto.InvalidMessageException;
@ -111,7 +111,7 @@ public class PushDownloader {
}
private File downloadAttachment(String relay, long contentLocation) throws IOException {
PushServiceSocket socket = new PushServiceSocket(context, Release.PUSH_URL, TextSecurePushCredentials.getInstance());
PushServiceSocket socket = PushServiceSocketFactory.create(context);
return socket.retrieveAttachment(relay, contentLocation);
}

View File

@ -13,10 +13,10 @@ import android.util.Log;
import com.google.android.gcm.GCMRegistrar;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.Release;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.gcm.GcmIntentService;
import org.thoughtcrime.securesms.gcm.GcmRegistrationTimeoutException;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.crypto.MasterSecret;
@ -198,7 +198,7 @@ public class RegistrationService extends Service {
initializeGcmRegistrationListener();
initializePreKeyGenerator(masterSecret);
PushServiceSocket socket = new PushServiceSocket(this, Release.PUSH_URL, number, password);
PushServiceSocket socket = PushServiceSocketFactory.create(this, number, password);
handleCommonRegistration(masterSecret, socket, number);
@ -238,7 +238,7 @@ public class RegistrationService extends Service {
initializePreKeyGenerator(masterSecret);
setState(new RegistrationState(RegistrationState.STATE_CONNECTING, number));
PushServiceSocket socket = new PushServiceSocket(this, Release.PUSH_URL, number, password);
PushServiceSocket socket = PushServiceSocketFactory.create(this, number, password);
socket.createAccount(false);
setState(new RegistrationState(RegistrationState.STATE_VERIFYING, number));

View File

@ -22,16 +22,15 @@ import android.util.Log;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.Release;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessorV2;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.mms.PartParser;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.TextSecurePushCredentials;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.textsecure.crypto.AttachmentCipher;
import org.whispersystems.textsecure.crypto.InvalidKeyException;
@ -70,12 +69,12 @@ public class PushTransport extends BaseTransport {
public void deliver(SmsMessageRecord message) throws IOException {
try {
TextSecurePushCredentials credentials = TextSecurePushCredentials.getInstance();
Recipient recipient = message.getIndividualRecipient();
long threadId = message.getThreadId();
PushServiceSocket socket = new PushServiceSocket(context, Release.PUSH_URL, credentials);
PushDestination destination = PushDestination.create(context, credentials,
recipient.getNumber());
String localNumber = TextSecurePreferences.getLocalNumber(context);
Recipient recipient = message.getIndividualRecipient();
long threadId = message.getThreadId();
PushServiceSocket socket = PushServiceSocketFactory.create(context);
PushDestination destination = PushDestination.create(context, localNumber,
recipient.getNumber());
String plaintextBody = message.getBody().getBody();
byte[] plaintext = PushMessageContent.newBuilder().setBody(plaintextBody).build().toByteArray();
@ -97,10 +96,9 @@ public class PushTransport extends BaseTransport {
throws IOException
{
try {
TextSecurePushCredentials credentials = TextSecurePushCredentials.getInstance();
PushServiceSocket socket = new PushServiceSocket(context, Release.PUSH_URL, credentials);
String messageBody = PartParser.getMessageText(message.getBody());
List<PushBody> pushBodies = new LinkedList<PushBody>();
PushServiceSocket socket = PushServiceSocketFactory.create(context);
String messageBody = PartParser.getMessageText(message.getBody());
List<PushBody> pushBodies = new LinkedList<PushBody>();
for (PushDestination destination : destinations) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, destination.getNumber(), false);

View File

@ -19,12 +19,11 @@ package org.thoughtcrime.securesms.transport;
import android.content.Context;
import android.util.Log;
import org.thoughtcrime.securesms.Release;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.mms.MmsSendResult;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.TextSecurePushCredentials;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.directory.Directory;
@ -115,24 +114,24 @@ public class UniversalTransport {
private List<PushDestination> getMediaDestinations(SendReq mediaMessage)
throws InvalidNumberException
{
TextSecurePushCredentials credentials = TextSecurePushCredentials.getInstance();
String localNumber = TextSecurePreferences.getLocalNumber(context);
LinkedList<PushDestination> destinations = new LinkedList<PushDestination>();
if (mediaMessage.getTo() != null) {
for (EncodedStringValue to : mediaMessage.getTo()) {
destinations.add(PushDestination.create(context, credentials, to.getString()));
destinations.add(PushDestination.create(context, localNumber, to.getString()));
}
}
if (mediaMessage.getCc() != null) {
for (EncodedStringValue cc : mediaMessage.getCc()) {
destinations.add(PushDestination.create(context, credentials, cc.getString()));
destinations.add(PushDestination.create(context, localNumber, cc.getString()));
}
}
if (mediaMessage.getBcc() != null) {
for (EncodedStringValue bcc : mediaMessage.getBcc()) {
destinations.add(PushDestination.create(context, credentials, bcc.getString()));
destinations.add(PushDestination.create(context, localNumber, bcc.getString()));
}
}
@ -146,7 +145,7 @@ public class UniversalTransport {
return directory.isActiveNumber(destination);
} catch (NotInDirectoryException e) {
try {
PushServiceSocket socket = new PushServiceSocket(context, Release.PUSH_URL, TextSecurePushCredentials.getInstance());
PushServiceSocket socket = PushServiceSocketFactory.create(context);
String contactToken = directory.getToken(destination);
ContactTokenDetails registeredUser = socket.getContactTokenDetails(contactToken);

View File

@ -1,24 +0,0 @@
package org.thoughtcrime.securesms.util;
import android.content.Context;
import org.whispersystems.textsecure.push.PushServiceSocket;
public class TextSecurePushCredentials implements PushServiceSocket.PushCredentials {
private static final TextSecurePushCredentials instance = new TextSecurePushCredentials();
public static TextSecurePushCredentials getInstance() {
return instance;
}
@Override
public String getLocalNumber(Context context) {
return TextSecurePreferences.getLocalNumber(context);
}
@Override
public String getPassword(Context context) {
return TextSecurePreferences.getPushServerPassword(context);
}
}