Move directory and push service socket into library.

This commit is contained in:
Moxie Marlinspike
2013-07-10 16:22:58 -07:00
parent a200d29514
commit 1ac06312a0
30 changed files with 49 additions and 51 deletions

View File

@@ -29,8 +29,8 @@ import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockActivity;
import org.thoughtcrime.securesms.gcm.PushServiceSocket;
import org.thoughtcrime.securesms.gcm.RateLimitException;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.push.RateLimitException;
import org.thoughtcrime.securesms.service.RegistrationService;
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
import org.whispersystems.textsecure.util.Util;

View File

@@ -1,7 +0,0 @@
package org.thoughtcrime.securesms;
public class Release {
public static final String PUSH_SERVICE_URL = "https://gcm.textsecure.whispersystems.org";
// public static final String PUSH_SERVICE_URL = "http://192.168.1.135:8080";
public static final boolean ENFORCE_SSL = true;
}

View File

@@ -27,7 +27,7 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.whispersystems.textsecure.util.Base64;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import org.thoughtcrime.securesms.util.InvalidMessageException;
/**

View File

@@ -32,7 +32,7 @@ import org.thoughtcrime.bouncycastle.asn1.DERInteger;
import org.thoughtcrime.bouncycastle.asn1.DERSequence;
import org.whispersystems.textsecure.util.Base64;
import org.thoughtcrime.securesms.util.Combiner;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import android.content.Context;
import android.content.SharedPreferences;

View File

@@ -22,7 +22,7 @@ import android.util.Log;
import org.thoughtcrime.securesms.database.keys.LocalKeyRecord;
import org.thoughtcrime.securesms.protocol.Message;
import org.whispersystems.textsecure.util.Base64;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import java.io.IOException;

View File

@@ -30,7 +30,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
/**
* This class processes key exchange interactions.

View File

@@ -21,7 +21,7 @@ import java.security.NoSuchAlgorithmException;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import org.thoughtcrime.securesms.util.Hex;
import android.util.Log;

View File

@@ -40,7 +40,7 @@ import org.thoughtcrime.securesms.database.keys.SessionKey;
import org.thoughtcrime.securesms.database.keys.SessionRecord;
import org.thoughtcrime.securesms.protocol.Message;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import android.content.Context;
import android.util.Log;

View File

@@ -18,7 +18,7 @@ package org.thoughtcrime.securesms.database.keys;
import android.content.Context;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import java.io.File;
import java.io.FileInputStream;

View File

@@ -19,7 +19,7 @@ package org.thoughtcrime.securesms.database.keys;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SessionCipher;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import org.thoughtcrime.securesms.util.Util;
import javax.crypto.spec.SecretKeySpec;

View File

@@ -1,86 +0,0 @@
/*
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.directory;
import org.thoughtcrime.securesms.util.Conversions;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* A simple bloom filter implementation that backs the RedPhone directory.
*
* @author Moxie Marlinspike
*
*/
public class BloomFilter {
private final MappedByteBuffer buffer;
private final long length;
private final int hashCount;
public BloomFilter(File bloomFilter, int hashCount)
throws IOException
{
this.length = bloomFilter.length();
this.buffer = new FileInputStream(bloomFilter).getChannel()
.map(FileChannel.MapMode.READ_ONLY, 0, length);
this.hashCount = hashCount;
}
public int getHashCount() {
return hashCount;
}
private boolean isBitSet(long bitIndex) {
int byteInQuestion = this.buffer.get((int)(bitIndex / 8));
int bitOffset = (0x01 << (bitIndex % 8));
return (byteInQuestion & bitOffset) > 0;
}
public boolean contains(String entity) {
try {
for (int i=0;i<this.hashCount;i++) {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec((i+"").getBytes(), "HmacSHA1"));
byte[] hashValue = mac.doFinal(entity.getBytes());
long bitIndex = Math.abs(Conversions.byteArrayToLong(hashValue, 0)) % (this.length * 8);
if (!isBitSet(bitIndex))
return false;
}
return true;
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (InvalidKeyException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -1,26 +0,0 @@
package org.thoughtcrime.securesms.directory;
public class DirectoryDescriptor {
private String version;
private long capacity;
private int hashCount;
public String getUrl() {
return url;
}
public int getHashCount() {
return hashCount;
}
public long getCapacity() {
return capacity;
}
public String getVersion() {
return version;
}
private String url;
}

View File

@@ -1,186 +0,0 @@
/*
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.directory;
import android.content.Context;
import android.util.Log;
import com.google.thoughtcrimegson.Gson;
import com.google.thoughtcrimegson.JsonParseException;
import com.google.thoughtcrimegson.annotations.SerializedName;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
/**
* Handles providing lookups, serializing, and deserializing the RedPhone directory.
*
* @author Moxie Marlinspike
*
*/
public class NumberFilter {
private static NumberFilter instance;
public synchronized static NumberFilter getInstance(Context context) {
if (instance == null)
instance = NumberFilter.deserializeFromFile(context);
return instance;
}
private static final String DIRECTORY_META_FILE = "directory.stat";
private File bloomFilter;
private String version;
private long capacity;
private int hashCount;
private Context context;
private NumberFilter(Context context, File bloomFilter, long capacity,
int hashCount, String version)
{
this.context = context.getApplicationContext();
this.bloomFilter = bloomFilter;
this.capacity = capacity;
this.hashCount = hashCount;
this.version = version;
}
public synchronized boolean containsNumber(String number) {
try {
if (bloomFilter == null) return false;
else if (number == null || number.length() == 0) return false;
String localNumber = TextSecurePreferences.getLocalNumber(context);
return new BloomFilter(bloomFilter, hashCount)
.contains(PhoneNumberFormatter.formatNumber(number, localNumber));
} catch (IOException ioe) {
Log.w("NumberFilter", ioe);
return false;
}
}
public synchronized void update(File bloomFilter, long capacity, int hashCount, String version)
{
if (this.bloomFilter != null)
this.bloomFilter.delete();
this.bloomFilter = bloomFilter;
this.capacity = capacity;
this.hashCount = hashCount;
this.version = version;
serializeToFile(context);
}
private void serializeToFile(Context context) {
if (this.bloomFilter == null)
return;
try {
FileOutputStream fout = context.openFileOutput(DIRECTORY_META_FILE, 0);
NumberFilterStorage storage = new NumberFilterStorage(bloomFilter.getAbsolutePath(),
capacity, hashCount, version);
storage.serializeToStream(fout);
fout.close();
} catch (IOException ioe) {
Log.w("NumberFilter", ioe);
}
}
private static NumberFilter deserializeFromFile(Context context) {
try {
FileInputStream fis = context.openFileInput(DIRECTORY_META_FILE);
NumberFilterStorage storage = NumberFilterStorage.fromStream(fis);
if (storage == null) return new NumberFilter(context, null, 0, 0, "0");
else return new NumberFilter(context,
new File(storage.getDataPath()),
storage.getCapacity(),
storage.getHashCount(),
storage.getVersion());
} catch (IOException ioe) {
Log.w("NumberFilter", ioe);
return new NumberFilter(context, null, 0, 0, "0");
}
}
private static class NumberFilterStorage {
@SerializedName("data_path")
private String dataPath;
@SerializedName("capacity")
private long capacity;
@SerializedName("hash_count")
private int hashCount;
@SerializedName("version")
private String version;
public NumberFilterStorage(String dataPath, long capacity, int hashCount, String version) {
this.dataPath = dataPath;
this.capacity = capacity;
this.hashCount = hashCount;
this.version = version;
}
public String getDataPath() {
return dataPath;
}
public long getCapacity() {
return capacity;
}
public int getHashCount() {
return hashCount;
}
public String getVersion() {
return version;
}
public void serializeToStream(OutputStream out) throws IOException {
out.write(new Gson().toJson(this).getBytes());
}
public static NumberFilterStorage fromStream(InputStream in) throws IOException {
try {
return new Gson().fromJson(new BufferedReader(new InputStreamReader(in)),
NumberFilterStorage.class);
} catch (JsonParseException jpe) {
Log.w("NumberFilter", jpe);
throw new IOException("JSON Parse Exception");
}
}
}
}

View File

@@ -13,6 +13,9 @@ import org.thoughtcrime.securesms.service.RegistrationService;
import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.push.IncomingGcmMessage;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.push.RateLimitException;
import org.whispersystems.textsecure.util.Util;
import java.io.IOException;

View File

@@ -1,18 +0,0 @@
package org.thoughtcrime.securesms.gcm;
import java.util.List;
public class GcmMessageResponse {
private List<String> success;
private List<String> failure;
public List<String> getSuccess() {
return success;
}
public List<String> getFailure() {
return failure;
}
}

View File

@@ -1,42 +0,0 @@
package org.thoughtcrime.securesms.gcm;
import java.util.LinkedList;
import java.util.List;
public class IncomingGcmMessage {
private String source;
private List<String> destinations;
private String messageText;
private List<String> attachments;
private long timestamp;
public IncomingGcmMessage(String source, List<String> destinations, String messageText, List<String> attachments, long timestamp) {
this.source = source;
this.destinations = destinations;
this.messageText = messageText;
this.attachments = attachments;
this.timestamp = timestamp;
}
public long getTimestampMillis() {
return timestamp;
}
public String getSource() {
return source;
}
public List<String> getAttachments() {
return attachments;
}
public String getMessageText() {
return messageText;
}
public List<String> getDestinations() {
return destinations;
}
}

View File

@@ -6,8 +6,10 @@ import android.content.Context;
import android.telephony.SmsManager;
import android.util.Log;
import org.thoughtcrime.securesms.directory.NumberFilter;
import org.whispersystems.textsecure.directory.NumberFilter;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.push.RateLimitException;
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
import java.io.IOException;

View File

@@ -1,37 +0,0 @@
package org.thoughtcrime.securesms.gcm;
import java.util.LinkedList;
import java.util.List;
public class OutgoingGcmMessage {
private List<String> destinations;
private String messageText;
private List<String> attachments;
public OutgoingGcmMessage(List<String> destinations, String messageText, List<String> attachments) {
this.destinations = destinations;
this.messageText = messageText;
this.attachments = attachments;
}
public OutgoingGcmMessage(String destination, String messageText) {
this.destinations = new LinkedList<String>();
this.destinations.add(destination);
this.messageText = messageText;
this.attachments = new LinkedList<String>();
}
public List<String> getDestinations() {
return destinations;
}
public String getMessageText() {
return messageText;
}
public List<String> getAttachments() {
return attachments;
}
}

View File

@@ -1,237 +0,0 @@
package org.thoughtcrime.securesms.gcm;
import android.content.Context;
import android.content.res.AssetManager;
import android.util.Base64;
import android.util.Log;
import com.google.thoughtcrimegson.Gson;
import org.thoughtcrime.securesms.Release;
import org.thoughtcrime.securesms.directory.DirectoryDescriptor;
import org.thoughtcrime.securesms.directory.NumberFilter;
import org.whispersystems.textsecure.util.Util;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.zip.GZIPInputStream;
public class PushServiceSocket {
private static final String CREATE_ACCOUNT_SMS_PATH = "/v1/accounts/sms/%s";
private static final String CREATE_ACCOUNT_VOICE_PATH = "/v1/accounts/voice/%s";
private static final String VERIFY_ACCOUNT_PATH = "/v1/accounts/code/%s";
private static final String REGISTER_GCM_PATH = "/v1/accounts/gcm/";
private static final String DIRECTORY_PATH = "/v1/directory/";
private static final String MESSAGE_PATH = "/v1/messages/";
private final String localNumber;
private final String password;
private final TrustManagerFactory trustManagerFactory;
public PushServiceSocket(Context context, String localNumber, String password) {
this.localNumber = localNumber;
this.password = password;
this.trustManagerFactory = initializeTrustManagerFactory(context);
}
public void createAccount(boolean voice) throws IOException, RateLimitException {
String path = voice ? CREATE_ACCOUNT_VOICE_PATH : CREATE_ACCOUNT_SMS_PATH;
makeRequest(String.format(path, localNumber), "POST", null);
}
public void verifyAccount(String verificationCode) throws IOException, RateLimitException {
makeRequest(String.format(VERIFY_ACCOUNT_PATH, verificationCode), "PUT", null);
}
public void registerGcmId(String gcmRegistrationId) throws IOException, RateLimitException {
GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId);
makeRequest(REGISTER_GCM_PATH, "PUT", new Gson().toJson(registration));
}
public void unregisterGcmId(String gcmRegistrationId) throws IOException, RateLimitException {
GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId);
makeRequest(REGISTER_GCM_PATH, "DELETE", new Gson().toJson(registration));
}
public void sendMessage(String recipient, String messageText)
throws IOException, RateLimitException
{
OutgoingGcmMessage message = new OutgoingGcmMessage(recipient, messageText);
String responseText = makeRequest(MESSAGE_PATH, "POST", new Gson().toJson(message));
GcmMessageResponse response = new Gson().fromJson(responseText, GcmMessageResponse.class);
if (response.getFailure().size() != 0)
throw new IOException("Got send failure: " + response.getFailure().get(0));
}
public void retrieveDirectory(Context context ) {
try {
DirectoryDescriptor directoryDescriptor = new Gson().fromJson(makeRequest(DIRECTORY_PATH, "GET", null),
DirectoryDescriptor.class);
File directoryData = downloadExternalFile(context, directoryDescriptor.getUrl());
NumberFilter.getInstance(context).update(directoryData,
directoryDescriptor.getCapacity(),
directoryDescriptor.getHashCount(),
directoryDescriptor.getVersion());
} catch (IOException ioe) {
Log.w("PushServiceSocket", ioe);
} catch (RateLimitException e) {
Log.w("PushServiceSocket", e);
}
}
private File downloadExternalFile(Context context, String url) throws IOException {
File download = File.createTempFile("directory", ".dat", context.getFilesDir());
URL downloadUrl = new URL(url);
HttpsURLConnection connection = (HttpsURLConnection)downloadUrl.openConnection();
connection.setDoInput(true);
if (connection.getResponseCode() != 200) {
throw new IOException("Bad response: " + connection.getResponseCode());
}
OutputStream output = new FileOutputStream(download);
InputStream input = new GZIPInputStream(connection.getInputStream());
byte[] buffer = new byte[4096];
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.close();
return download;
}
private String makeRequest(String urlFragment, String method, String body)
throws IOException, RateLimitException
{
HttpURLConnection connection = getConnection(urlFragment, method);
if (body != null) {
connection.setDoOutput(true);
}
connection.connect();
if (body != null) {
Log.w("PushServiceSocket", method + " -- " + body);
OutputStream out = connection.getOutputStream();
out.write(body.getBytes());
out.close();
}
if (connection.getResponseCode() == 413) {
throw new RateLimitException("Rate limit exceeded: " + connection.getResponseCode());
}
if (connection.getResponseCode() != 200) {
throw new IOException("Bad response: " + connection.getResponseCode() + " " + connection.getResponseMessage());
}
return Util.readFully(connection.getInputStream());
}
private HttpURLConnection getConnection(String urlFragment, String method) throws IOException {
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManagerFactory.getTrustManagers(), null);
URL url = new URL(String.format("%s%s", Release.PUSH_SERVICE_URL, urlFragment));
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
if (Release.ENFORCE_SSL) {
((HttpsURLConnection)connection).setSSLSocketFactory(context.getSocketFactory());
}
connection.setRequestMethod(method);
connection.setRequestProperty("Content-Type", "application/json");
if (password != null) {
connection.setRequestProperty("Authorization", getAuthorizationHeader());
}
return connection;
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (KeyManagementException e) {
throw new AssertionError(e);
} catch (MalformedURLException e) {
throw new AssertionError(e);
}
}
private String getAuthorizationHeader() {
try {
return "Basic " + new String(Base64.encode((localNumber + ":" + password).getBytes("UTF-8"), Base64.NO_WRAP));
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
}
private TrustManagerFactory initializeTrustManagerFactory(Context context) {
try {
AssetManager assetManager = context.getAssets();
InputStream keyStoreInputStream = assetManager.open("whisper.store");
KeyStore trustStore = KeyStore.getInstance("BKS");
trustStore.load(keyStoreInputStream, "whisper".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(trustStore);
return trustManagerFactory;
} catch (KeyStoreException kse) {
throw new AssertionError(kse);
} catch (CertificateException e) {
throw new AssertionError(e);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (IOException ioe) {
throw new AssertionError(ioe);
}
}
// private class Verification {
//
// private String verificationCode;
//
// public Verification() {}
//
// public Verification(String verificationCode) {
// this.verificationCode = verificationCode;
// }
// }
private class GcmRegistrationId {
private String gcmRegistrationId;
public GcmRegistrationId() {}
public GcmRegistrationId(String gcmRegistrationId) {
this.gcmRegistrationId = gcmRegistrationId;
}
}
}

View File

@@ -1,8 +0,0 @@
package org.thoughtcrime.securesms.gcm;
public class RateLimitException extends Exception {
public RateLimitException(String s) {
super(s);
}
}

View File

@@ -32,7 +32,7 @@ import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.service.MmsDownloader;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.util.Util;

View File

@@ -26,7 +26,7 @@ import java.util.zip.InflaterInputStream;
import org.thoughtcrime.securesms.crypto.SessionCipher;
import org.thoughtcrime.securesms.crypto.TransportDetails;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import android.util.Log;

View File

@@ -21,7 +21,7 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
import org.thoughtcrime.securesms.crypto.InvalidMessageException;
import org.thoughtcrime.securesms.crypto.PublicKey;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import java.nio.ByteBuffer;

View File

@@ -15,10 +15,10 @@ import android.util.Log;
import com.google.android.gcm.GCMRegistrar;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.gcm.GcmIntentService;
import org.thoughtcrime.securesms.gcm.GcmRegistrationTimeoutException;
import org.thoughtcrime.securesms.gcm.PushServiceSocket;
import org.thoughtcrime.securesms.gcm.RateLimitException;
import org.whispersystems.textsecure.push.GcmIntentService;
import org.whispersystems.textsecure.push.GcmRegistrationTimeoutException;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.push.RateLimitException;
import org.whispersystems.textsecure.util.Util;
import java.io.IOException;

View File

@@ -4,7 +4,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.SmsMessage;
import org.thoughtcrime.securesms.gcm.IncomingGcmMessage;
import org.whispersystems.textsecure.push.IncomingGcmMessage;
import java.util.List;

View File

@@ -6,7 +6,7 @@ import org.thoughtcrime.securesms.protocol.KeyExchangeWirePrefix;
import org.thoughtcrime.securesms.protocol.SecureMessageWirePrefix;
import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.whispersystems.textsecure.util.Base64;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.textsecure.util.Conversions;
import java.io.IOException;
import java.util.ArrayList;

View File

@@ -1,180 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.util;
public class Conversions {
public static byte intsToByteHighAndLow(int highValue, int lowValue) {
return (byte)((highValue << 4 | lowValue) & 0xFF);
}
public static int highBitsToInt(byte value) {
return (value & 0xFF) >> 4;
}
public static int lowBitsToInt(byte value) {
return (value & 0xF);
}
public static int highBitsToMedium(int value) {
return (value >> 12);
}
public static int lowBitsToMedium(int value) {
return (value & 0xFFF);
}
public static byte[] shortToByteArray(int value) {
byte[] bytes = new byte[2];
shortToByteArray(bytes, 0, value);
return bytes;
}
public static int shortToByteArray(byte[] bytes, int offset, int value) {
bytes[offset+1] = (byte)value;
bytes[offset] = (byte)(value >> 8);
return 2;
}
public static int shortToLittleEndianByteArray(byte[] bytes, int offset, int value) {
bytes[offset] = (byte)value;
bytes[offset+1] = (byte)(value >> 8);
return 2;
}
public static byte[] mediumToByteArray(int value) {
byte[] bytes = new byte[3];
mediumToByteArray(bytes, 0, value);
return bytes;
}
public static int mediumToByteArray(byte[] bytes, int offset, int value) {
bytes[offset + 2] = (byte)value;
bytes[offset + 1] = (byte)(value >> 8);
bytes[offset] = (byte)(value >> 16);
return 3;
}
public static byte[] intToByteArray(int value) {
byte[] bytes = new byte[4];
intToByteArray(bytes, 0, value);
return bytes;
}
public static int intToByteArray(byte[] bytes, int offset, int value) {
bytes[offset + 3] = (byte)value;
bytes[offset + 2] = (byte)(value >> 8);
bytes[offset + 1] = (byte)(value >> 16);
bytes[offset] = (byte)(value >> 24);
return 4;
}
public static int intToLittleEndianByteArray(byte[] bytes, int offset, int value) {
bytes[offset] = (byte)value;
bytes[offset+1] = (byte)(value >> 8);
bytes[offset+2] = (byte)(value >> 16);
bytes[offset+3] = (byte)(value >> 24);
return 4;
}
public static byte[] longToByteArray(long l) {
byte[] bytes = new byte[8];
longToByteArray(bytes, 0, l);
return bytes;
}
public static int longToByteArray(byte[] bytes, int offset, long value) {
bytes[offset + 7] = (byte)value;
bytes[offset + 6] = (byte)(value >> 8);
bytes[offset + 5] = (byte)(value >> 16);
bytes[offset + 4] = (byte)(value >> 24);
bytes[offset + 3] = (byte)(value >> 32);
bytes[offset + 2] = (byte)(value >> 40);
bytes[offset + 1] = (byte)(value >> 48);
bytes[offset] = (byte)(value >> 56);
return 8;
}
public static int longTo4ByteArray(byte[] bytes, int offset, long value) {
bytes[offset + 3] = (byte)value;
bytes[offset + 2] = (byte)(value >> 8);
bytes[offset + 1] = (byte)(value >> 16);
bytes[offset + 0] = (byte)(value >> 24);
return 4;
}
public static int byteArrayToShort(byte[] bytes) {
return byteArrayToShort(bytes, 0);
}
public static int byteArrayToShort(byte[] bytes, int offset) {
return
(bytes[offset] & 0xff) << 8 | (bytes[offset + 1] & 0xff);
}
// The SSL patented 3-byte Value.
public static int byteArrayToMedium(byte[] bytes, int offset) {
return
(bytes[offset] & 0xff) << 16 |
(bytes[offset + 1] & 0xff) << 8 |
(bytes[offset + 2] & 0xff);
}
public static int byteArrayToInt(byte[] bytes) {
return byteArrayToInt(bytes, 0);
}
public static int byteArrayToInt(byte[] bytes, int offset) {
return
(bytes[offset] & 0xff) << 24 |
(bytes[offset + 1] & 0xff) << 16 |
(bytes[offset + 2] & 0xff) << 8 |
(bytes[offset + 3] & 0xff);
}
public static int byteArrayToIntLittleEndian(byte[] bytes, int offset) {
return
(bytes[offset + 3] & 0xff) << 24 |
(bytes[offset + 2] & 0xff) << 16 |
(bytes[offset + 1] & 0xff) << 8 |
(bytes[offset] & 0xff);
}
public static long byteArrayToLong(byte[] bytes) {
return byteArrayToLong(bytes, 0);
}
public static long byteArray4ToLong(byte[] bytes, int offset) {
return
((bytes[offset + 0] & 0xffL) << 24) |
((bytes[offset + 1] & 0xffL) << 16) |
((bytes[offset + 2] & 0xffL) << 8) |
((bytes[offset + 3] & 0xffL));
}
public static long byteArrayToLong(byte[] bytes, int offset) {
return
((bytes[offset] & 0xffL) << 56) |
((bytes[offset + 1] & 0xffL) << 48) |
((bytes[offset + 2] & 0xffL) << 40) |
((bytes[offset + 3] & 0xffL) << 32) |
((bytes[offset + 4] & 0xffL) << 24) |
((bytes[offset + 5] & 0xffL) << 16) |
((bytes[offset + 6] & 0xffL) << 8) |
((bytes[offset + 7] & 0xffL));
}
}