Generate "prekeys" at push registration time.

This generates a large number of key exchange messages and
registers them with the server during signup.
fork-5.53.8
Moxie Marlinspike 2013-08-15 08:25:30 -07:00
rodzic cfb7b8fcba
commit 2042ca6cb7
25 zmienionych plików z 1015 dodań i 109 usunięć

Wyświetl plik

@ -0,0 +1,16 @@
package org.whispersystems.textsecure.push;
import java.util.List;
public class PreKeyList {
private List<String> keys;
public PreKeyList(List<String> keys) {
this.keys = keys;
}
public List<String> getKeys() {
return keys;
}
}

Wyświetl plik

@ -39,6 +39,7 @@ public class PushServiceSocket {
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 PREKEY_PATH = "/v1/keys/";
private static final String DIRECTORY_PATH = "/v1/directory/";
private static final String MESSAGE_PATH = "/v1/messages/";
@ -105,6 +106,11 @@ public class PushServiceSocket {
throw new IOException("Got send failure: " + response.getFailure().get(0));
}
public void registerPreKeys(PreKeyList keys) throws IOException {
makeRequest(PREKEY_PATH, "PUT", new Gson().toJson(keys));
}
private List<PushAttachmentPointer> sendAttachments(List<PushAttachmentData> attachments)
throws IOException
{

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,3 @@
all:
protoc --java_out=../src/ PreKeyEntity.proto

Wyświetl plik

@ -0,0 +1,9 @@
package textsecure;
option java_package = "org.thoughtcrime.securesms.encoded";
option java_outer_classname = "PreKeyProtos";
message PreKeyEntity {
optional uint64 id = 1;
optional bytes key = 2;
}

Wyświetl plik

@ -388,6 +388,50 @@
android:textSize="16.0sp" />
</TableRow>
<TableRow>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center" >
<ImageView
android:id="@+id/generating_keys_complete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingLeft="4dip"
android:paddingRight="4dip"
android:src="@drawable/check_dark"
android:visibility="invisible"
android:contentDescription="Check"/>
<ProgressBar
android:id="@+id/generating_keys_progress"
style="?android:attr/android:progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:indeterminate="true"
android:paddingLeft="4dip"
android:paddingRight="4dip"
android:visibility="invisible" />
</FrameLayout>
<TextView
android:id="@+id/generating_keys_text"
style="@style/Registration.Constant"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="4.0dip"
android:paddingRight="8.0dip"
android:text="@string/registration_progress_activity__generating_keys"
android:textSize="16.0sp" />
</TableRow>
<TableRow>
<FrameLayout

Wyświetl plik

@ -519,6 +519,7 @@
<string name="registration_progress_activity__sms_verification_failed">SMS verification
failed.
</string>
<string name="registration_progress_activity__generating_keys">Generating keys...</string>
<!-- recipients_panel -->
<string name="recipients_panel__to">To</string>

Wyświetl plik

@ -369,7 +369,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
}
}.execute();
} else {
startActivity(new Intent(ApplicationPreferencesActivity.this, RegistrationActivity.class));
Intent intent = new Intent(ApplicationPreferencesActivity.this, RegistrationActivity.class);
intent.putExtra("master_secret", getIntent().getParcelableExtra("master_secret"));
startActivity(intent);
}
return false;

Wyświetl plik

@ -27,6 +27,7 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
import org.thoughtcrime.securesms.util.MemoryCleaner;
import org.thoughtcrime.securesms.util.VersionTracker;
import org.whispersystems.textsecure.util.Util;

Wyświetl plik

@ -24,6 +24,7 @@ import com.google.i18n.phonenumbers.AsYouTypeFormatter;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
import org.whispersystems.textsecure.util.Util;
@ -47,6 +48,8 @@ public class RegistrationActivity extends SherlockActivity {
private Button createButton;
private Button skipButton;
private MasterSecret masterSecret;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@ -70,6 +73,7 @@ public class RegistrationActivity extends SherlockActivity {
}
private void initializeResources() {
this.masterSecret = getIntent().getParcelableExtra("master_secret");
this.countrySpinner = (Spinner)findViewById(R.id.country_spinner);
this.countryCode = (TextView)findViewById(R.id.country_code);
this.number = (TextView)findViewById(R.id.number);
@ -191,6 +195,7 @@ public class RegistrationActivity extends SherlockActivity {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(self, RegistrationProgressActivity.class);
intent.putExtra("e164number", e164number);
intent.putExtra("master_secret", masterSecret);
startActivity(intent);
finish();
}

Wyświetl plik

@ -29,9 +29,10 @@ import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockActivity;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.service.RegistrationService;
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;
@ -58,15 +59,19 @@ public class RegistrationProgressActivity extends SherlockActivity {
private ProgressBar registrationProgress;
private ProgressBar connectingProgress;
private ProgressBar verificationProgress;
private ProgressBar generatingKeysProgress;
private ProgressBar gcmRegistrationProgress;
private ImageView connectingCheck;
private ImageView verificationCheck;
private ImageView generatingKeysCheck;
private ImageView gcmRegistrationCheck;
private TextView connectingText;
private TextView verificationText;
private TextView registrationTimerText;
private TextView generatingKeysText;
private TextView gcmRegistrationText;
private Button verificationFailureButton;
@ -76,6 +81,7 @@ public class RegistrationProgressActivity extends SherlockActivity {
private EditText codeEditText;
private MasterSecret masterSecret;
private volatile boolean visible;
@Override
@ -118,19 +124,23 @@ public class RegistrationProgressActivity extends SherlockActivity {
}
private void initializeResources() {
this.masterSecret = getIntent().getParcelableExtra("master_secret");
this.registrationLayout = (LinearLayout)findViewById(R.id.registering_layout);
this.verificationFailureLayout = (LinearLayout)findViewById(R.id.verification_failure_layout);
this.connectivityFailureLayout = (LinearLayout)findViewById(R.id.connectivity_failure_layout);
this.registrationProgress = (ProgressBar) findViewById(R.id.registration_progress);
this.connectingProgress = (ProgressBar) findViewById(R.id.connecting_progress);
this.verificationProgress = (ProgressBar) findViewById(R.id.verification_progress);
this.generatingKeysProgress = (ProgressBar) findViewById(R.id.generating_keys_progress);
this.gcmRegistrationProgress = (ProgressBar) findViewById(R.id.gcm_registering_progress);
this.connectingCheck = (ImageView) findViewById(R.id.connecting_complete);
this.verificationCheck = (ImageView) findViewById(R.id.verification_complete);
this.generatingKeysCheck = (ImageView) findViewById(R.id.generating_keys_complete);
this.gcmRegistrationCheck = (ImageView) findViewById(R.id.gcm_registering_complete);
this.connectingText = (TextView) findViewById(R.id.connecting_text);
this.verificationText = (TextView) findViewById(R.id.verification_text);
this.registrationTimerText = (TextView) findViewById(R.id.registration_timer);
this.generatingKeysText = (TextView) findViewById(R.id.generating_keys_text);
this.gcmRegistrationText = (TextView) findViewById(R.id.gcm_registering_text);
this.verificationFailureButton = (Button) findViewById(R.id.verification_failure_edit_button);
this.connectivityFailureButton = (Button) findViewById(R.id.connectivity_failure_edit_button);
@ -181,6 +191,7 @@ public class RegistrationProgressActivity extends SherlockActivity {
Intent intent = new Intent(this, RegistrationService.class);
intent.setAction(RegistrationService.REGISTER_NUMBER_ACTION);
intent.putExtra("e164number", getNumberDirective());
intent.putExtra("master_secret", masterSecret);
startService(intent);
} else {
startActivity(new Intent(this, RegistrationActivity.class));
@ -196,10 +207,13 @@ public class RegistrationProgressActivity extends SherlockActivity {
this.connectingCheck.setVisibility(View.INVISIBLE);
this.verificationProgress.setVisibility(View.INVISIBLE);
this.verificationCheck.setVisibility(View.INVISIBLE);
this.generatingKeysProgress.setVisibility(View.INVISIBLE);
this.generatingKeysCheck.setVisibility(View.INVISIBLE);
this.gcmRegistrationProgress.setVisibility(View.INVISIBLE);
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
this.connectingText.setTextColor(FOCUSED_COLOR);
this.verificationText.setTextColor(UNFOCUSED_COLOR);
this.generatingKeysText.setTextColor(UNFOCUSED_COLOR);
this.gcmRegistrationText.setTextColor(UNFOCUSED_COLOR);
this.timeoutProgressLayout.setVisibility(View.VISIBLE);
}
@ -212,15 +226,38 @@ public class RegistrationProgressActivity extends SherlockActivity {
this.connectingCheck.setVisibility(View.VISIBLE);
this.verificationProgress.setVisibility(View.VISIBLE);
this.verificationCheck.setVisibility(View.INVISIBLE);
this.generatingKeysProgress.setVisibility(View.INVISIBLE);
this.generatingKeysCheck.setVisibility(View.INVISIBLE);
this.gcmRegistrationProgress.setVisibility(View.INVISIBLE);
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
this.connectingText.setTextColor(UNFOCUSED_COLOR);
this.verificationText.setTextColor(FOCUSED_COLOR);
this.generatingKeysText.setTextColor(UNFOCUSED_COLOR);
this.gcmRegistrationText.setTextColor(UNFOCUSED_COLOR);
this.registrationProgress.setVisibility(View.VISIBLE);
this.timeoutProgressLayout.setVisibility(View.VISIBLE);
}
private void handleStateGeneratingKeys() {
this.registrationLayout.setVisibility(View.VISIBLE);
this.verificationFailureLayout.setVisibility(View.GONE);
this.connectivityFailureLayout.setVisibility(View.GONE);
this.connectingProgress.setVisibility(View.INVISIBLE);
this.connectingCheck.setVisibility(View.VISIBLE);
this.verificationProgress.setVisibility(View.INVISIBLE);
this.verificationCheck.setVisibility(View.VISIBLE);
this.generatingKeysProgress.setVisibility(View.VISIBLE);
this.generatingKeysCheck.setVisibility(View.INVISIBLE);
this.gcmRegistrationProgress.setVisibility(View.INVISIBLE);
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
this.connectingText.setTextColor(UNFOCUSED_COLOR);
this.verificationText.setTextColor(UNFOCUSED_COLOR);
this.generatingKeysText.setTextColor(FOCUSED_COLOR);
this.gcmRegistrationText.setTextColor(UNFOCUSED_COLOR);
this.registrationProgress.setVisibility(View.INVISIBLE);
this.timeoutProgressLayout.setVisibility(View.INVISIBLE);
}
private void handleStateGcmRegistering() {
this.registrationLayout.setVisibility(View.VISIBLE);
this.verificationFailureLayout.setVisibility(View.GONE);
@ -229,10 +266,13 @@ public class RegistrationProgressActivity extends SherlockActivity {
this.connectingCheck.setVisibility(View.VISIBLE);
this.verificationProgress.setVisibility(View.INVISIBLE);
this.verificationCheck.setVisibility(View.VISIBLE);
this.generatingKeysProgress.setVisibility(View.INVISIBLE);
this.generatingKeysCheck.setVisibility(View.VISIBLE);
this.gcmRegistrationProgress.setVisibility(View.VISIBLE);
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
this.connectingText.setTextColor(UNFOCUSED_COLOR);
this.verificationText.setTextColor(UNFOCUSED_COLOR);
this.generatingKeysText.setTextColor(UNFOCUSED_COLOR);
this.gcmRegistrationText.setTextColor(FOCUSED_COLOR);
this.registrationProgress.setVisibility(View.INVISIBLE);
this.timeoutProgressLayout.setVisibility(View.INVISIBLE);
@ -351,6 +391,7 @@ public class RegistrationProgressActivity extends SherlockActivity {
case RegistrationState.STATE_CONNECTING: handleStateConnecting(); break;
case RegistrationState.STATE_VERIFYING: handleStateVerifying(); break;
case RegistrationState.STATE_TIMER: handleTimerUpdate(); break;
case RegistrationState.STATE_GENERATING_KEYS: handleStateGeneratingKeys(); break;
case RegistrationState.STATE_GCM_REGISTERING: handleStateGcmRegistering(); break;
case RegistrationState.STATE_TIMEOUT: handleVerificationTimeout(state); break;
case RegistrationState.STATE_COMPLETE: handleVerificationComplete(); break;
@ -429,6 +470,7 @@ public class RegistrationProgressActivity extends SherlockActivity {
intent.setAction(RegistrationService.VOICE_REGISTER_ACTION);
intent.putExtra("e164number", e164number);
intent.putExtra("password", password);
intent.putExtra("master_secret", masterSecret);
startService(intent);
break;
case NETWORK_ERROR:
@ -504,6 +546,7 @@ public class RegistrationProgressActivity extends SherlockActivity {
intent.setAction(RegistrationService.VOICE_REQUESTED_ACTION);
intent.putExtra("e164number", e164number);
intent.putExtra("password", password);
intent.putExtra("master_secret", masterSecret);
startService(intent);
callButton.setEnabled(false);

Wyświetl plik

@ -109,7 +109,7 @@ public class RoutingActivity extends PassphraseRequiredSherlockActivity {
}
private void handlePushRegistration() {
Intent intent = new Intent(this, RegistrationActivity.class);
Intent intent = getPushRegistrationIntent();
intent.putExtra("next_intent", getConversationListIntent());
startActivity(intent);
finish();
@ -150,7 +150,10 @@ public class RoutingActivity extends PassphraseRequiredSherlockActivity {
}
private Intent getPushRegistrationIntent() {
return new Intent(this, RegistrationActivity.class);
Intent intent = new Intent(this, RegistrationActivity.class);
intent.putExtra("master_secret", masterSecret);
return intent;
}
private int getApplicationState() {

Wyświetl plik

@ -16,13 +16,12 @@
*/
package org.thoughtcrime.securesms.crypto;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.thoughtcrime.securesms.util.Hex;
import android.os.Parcel;
import android.os.Parcelable;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.thoughtcrime.securesms.util.Hex;
/**
* A class for representing an identity key.
*
@ -45,7 +44,7 @@ public class IdentityKey implements Parcelable, SerializableKey {
}
};
public static final int SIZE = 1 + 33;
public static final int SIZE = 1 + KeyUtil.POINT_SIZE;
private static final int VERSION = 1;
private ECPublicKeyParameters publicKey;
@ -75,19 +74,8 @@ public class IdentityKey implements Parcelable, SerializableKey {
if (version > VERSION)
throw new InvalidKeyException("Unsupported key version: " + version);
byte[] pointBytes = new byte[PublicKey.POINT_SIZE];
System.arraycopy(bytes, offset+1, pointBytes, 0, pointBytes.length);
ECPoint Q;
try {
Q = KeyUtil.decodePoint(pointBytes);
} catch (RuntimeException re) {
throw new InvalidKeyException(re);
}
this.publicKey = new ECPublicKeyParameters(Q, KeyUtil.domainParameters);
this.publicKey = KeyUtil.decodePoint(bytes, offset+1);
}
public byte[] serialize() {

Wyświetl plik

@ -30,6 +30,7 @@ import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.thoughtcrime.securesms.database.keys.LocalKeyRecord;
import org.thoughtcrime.securesms.database.keys.PreKeyRecord;
import org.thoughtcrime.securesms.database.keys.RemoteKeyRecord;
import org.thoughtcrime.securesms.database.keys.SessionRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -55,21 +56,36 @@ public class KeyUtil {
private static final ECCurve curve = new ECCurve.Fp(q, a, b);
private static final ECPoint g = new ECPoint.Fp(curve, x, y, true);
public static final int POINT_SIZE = 33;
public static final ECDomainParameters domainParameters = new ECDomainParameters(curve, g, n);
public static ECPoint decodePoint(byte[] pointBytes) {
synchronized (curve) {
return curve.decodePoint(pointBytes);
}
}
public static byte[] encodePoint(ECPoint point) {
synchronized (curve) {
return point.getEncoded();
}
}
public static ECPublicKeyParameters decodePoint(byte[] encoded, int offset)
throws InvalidKeyException
{
byte[] pointBytes = new byte[POINT_SIZE];
System.arraycopy(encoded, offset, pointBytes, 0, pointBytes.length);
synchronized (curve) {
ECPoint Q;
try {
Q = curve.decodePoint(pointBytes);
} catch (RuntimeException re) {
throw new InvalidKeyException(re);
}
return new ECPublicKeyParameters(Q, KeyUtil.domainParameters);
}
}
public static BigInteger calculateAgreement(ECDHBasicAgreement agreement, ECPublicKeyParameters remoteKey) {
synchronized (curve) {
return agreement.calculateAgreement(remoteKey);

Wyświetl plik

@ -0,0 +1,43 @@
package org.thoughtcrime.securesms.crypto;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.thoughtcrime.securesms.util.Util;
public class PreKeyPair {
private final MasterCipher masterCipher;
private final ECPrivateKeyParameters privateKey;
private final ECPublicKeyParameters publicKey;
public PreKeyPair(MasterSecret masterSecret, AsymmetricCipherKeyPair keyPair) {
this.masterCipher = new MasterCipher(masterSecret);
this.publicKey = (ECPublicKeyParameters)keyPair.getPublic();
this.privateKey = (ECPrivateKeyParameters)keyPair.getPrivate();
}
public PreKeyPair(MasterSecret masterSecret, byte[] serialized) throws InvalidKeyException {
if (serialized.length < KeyUtil.POINT_SIZE + 1)
throw new InvalidKeyException("Serialized length: " + serialized.length);
byte[] privateKeyBytes = new byte[serialized.length - KeyUtil.POINT_SIZE];
System.arraycopy(serialized, KeyUtil.POINT_SIZE, privateKeyBytes, 0, privateKeyBytes.length);
this.masterCipher = new MasterCipher(masterSecret);
this.publicKey = KeyUtil.decodePoint(serialized, 0);
this.privateKey = masterCipher.decryptKey(privateKeyBytes);
}
public ECPublicKeyParameters getPublicKey() {
return publicKey;
}
public byte[] serialize() {
byte[] publicKeyBytes = KeyUtil.encodePoint(publicKey.getQ());
byte[] privateKeyBytes = masterCipher.encryptKey(privateKey);
return Util.combine(publicKeyBytes, privateKeyBytes);
}
}

Wyświetl plik

@ -0,0 +1,113 @@
package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import android.util.Log;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.database.keys.InvalidKeyIdException;
import org.thoughtcrime.securesms.database.keys.PreKeyRecord;
import org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity;
import org.whispersystems.textsecure.push.PreKeyList;
import org.whispersystems.textsecure.util.Base64;
import java.io.File;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.LinkedList;
import java.util.List;
public class PreKeyUtil {
public static final int BATCH_SIZE = 70;
public static List<PreKeyRecord> generatePreKeys(Context context, MasterSecret masterSecret) {
List<PreKeyRecord> records = new LinkedList<PreKeyRecord>();
long preKeyIdOffset = getNextPreKeyId(context);
for (int i=0;i<BATCH_SIZE;i++) {
Log.w("PreKeyUtil", "Generating PreKey: " + (preKeyIdOffset + i));
PreKeyPair keyPair = new PreKeyPair(masterSecret, KeyUtil.generateKeyPair());
PreKeyRecord record = new PreKeyRecord(context, masterSecret, preKeyIdOffset + i, keyPair);
record.save();
records.add(record);
}
return records;
}
public static List<PreKeyRecord> getPreKeys(Context context, MasterSecret masterSecret) {
List<PreKeyRecord> records = new LinkedList<PreKeyRecord>();
File directory = getPreKeysDirectory(context);
String[] keyRecordIds = directory.list();
for (String keyRecordId : keyRecordIds) {
try {
records.add(new PreKeyRecord(context, masterSecret, Long.parseLong(keyRecordId)));
} catch (InvalidKeyIdException e) {
Log.w("PreKeyUtil", e);
new File(getPreKeysDirectory(context), keyRecordId).delete();
} catch (NumberFormatException nfe) {
Log.w("PreKeyUtil", nfe);
new File(getPreKeysDirectory(context), keyRecordId).delete();
}
}
return records;
}
public static void clearPreKeys(Context context) {
File directory = getPreKeysDirectory(context);
String[] keyRecords = directory.list();
for (String keyRecord : keyRecords) {
new File(directory, keyRecord).delete();
}
}
public static PreKeyList toJson(List<PreKeyRecord> records) {
List<String> encoded = new LinkedList<String>();
for (PreKeyRecord record : records) {
PreKeyEntity entity = PreKeyEntity.newBuilder().setId(record.getId())
.setKey(ByteString.copyFrom(KeyUtil.encodePoint(record.getKeyPair().getPublicKey().getQ())))
.build();
String encodedEntity = Base64.encodeBytesWithoutPadding(entity.toByteArray());
encoded.add(encodedEntity);
}
return new PreKeyList(encoded);
}
private static long getNextPreKeyId(Context context) {
try {
File directory = getPreKeysDirectory(context);
String[] keyRecordIds = directory.list();
long nextPreKeyId = 0;
for (String keyRecordId : keyRecordIds) {
if (Long.parseLong(keyRecordId) > nextPreKeyId)
nextPreKeyId = Long.parseLong(keyRecordId);
}
if (nextPreKeyId == 0)
nextPreKeyId = SecureRandom.getInstance("SHA1PRNG").nextInt(Integer.MAX_VALUE/2);
return nextPreKeyId;
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
private static File getPreKeysDirectory(Context context) {
File directory = new File(context.getFilesDir(), PreKeyRecord.PREKEY_DIRECTORY);
if (!directory.exists())
directory.mkdirs();
return directory;
}
}

Wyświetl plik

@ -16,21 +16,19 @@
*/
package org.thoughtcrime.securesms.crypto;
import android.util.Log;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.thoughtcrime.securesms.util.Hex;
import org.whispersystems.textsecure.util.Conversions;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.whispersystems.textsecure.util.Conversions;
import org.thoughtcrime.securesms.util.Hex;
import android.util.Log;
public class PublicKey {
public static final int POINT_SIZE = 33;
public static final int KEY_SIZE = 3 + POINT_SIZE;
public static final int KEY_SIZE = 3 + KeyUtil.POINT_SIZE;
private ECPublicKeyParameters publicKey;
private final ECPublicKeyParameters publicKey;
private int id;
public PublicKey(PublicKey publicKey) {
@ -50,20 +48,8 @@ public class PublicKey {
if ((bytes.length - offset) < KEY_SIZE)
throw new InvalidKeyException("Provided bytes are too short.");
this.id = Conversions.byteArrayToMedium(bytes, offset);
byte[] pointBytes = new byte[POINT_SIZE];
System.arraycopy(bytes, offset+3, pointBytes, 0, pointBytes.length);
ECPoint Q;
try {
Q = KeyUtil.decodePoint(pointBytes);
} catch (RuntimeException re) {
throw new InvalidKeyException(re);
}
this.publicKey = new ECPublicKeyParameters(Q, KeyUtil.domainParameters);
this.id = Conversions.byteArrayToMedium(bytes, offset);
this.publicKey = KeyUtil.decodePoint(bytes, offset + 3);
}
public PublicKey(byte[] bytes) throws InvalidKeyException {
@ -99,7 +85,7 @@ public class PublicKey {
public byte[] serialize() {
byte[] complete = new byte[KEY_SIZE];
byte[] serializedPoint = KeyUtil.encodePoint(publicKey.getQ());
Log.w("PublicKey", "Serializing public key point: " + Hex.toString(serializedPoint));
Conversions.mediumToByteArray(complete, 0, id);

Wyświetl plik

@ -19,22 +19,18 @@ package org.thoughtcrime.securesms.database.keys;
public class InvalidKeyIdException extends Exception {
public InvalidKeyIdException() {
// TODO Auto-generated constructor stub
}
public InvalidKeyIdException(String detailMessage) {
super(detailMessage);
// TODO Auto-generated constructor stub
}
public InvalidKeyIdException(Throwable throwable) {
super(throwable);
// TODO Auto-generated constructor stub
}
public InvalidKeyIdException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
// TODO Auto-generated constructor stub
}
}

Wyświetl plik

@ -25,8 +25,6 @@ import org.thoughtcrime.securesms.crypto.KeyUtil;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
import org.thoughtcrime.securesms.database.keys.InvalidKeyIdException;
import org.thoughtcrime.securesms.database.keys.Record;
import org.thoughtcrime.securesms.recipients.Recipient;
import java.io.FileInputStream;
@ -46,7 +44,7 @@ public class LocalKeyRecord extends Record {
private final MasterSecret masterSecret;
public LocalKeyRecord(Context context, MasterSecret masterSecret, Recipient recipient) {
super(context, getFileNameForRecipient(context, recipient));
super(context, SESSIONS_DIRECTORY, getFileNameForRecipient(context, recipient));
this.masterSecret = masterSecret;
this.masterCipher = new MasterCipher(masterSecret);
loadData();
@ -54,11 +52,11 @@ public class LocalKeyRecord extends Record {
public static boolean hasRecord(Context context, Recipient recipient) {
Log.w("LocalKeyRecord", "Checking: " + getFileNameForRecipient(context, recipient));
return Record.hasRecord(context, getFileNameForRecipient(context, recipient));
return Record.hasRecord(context, SESSIONS_DIRECTORY, getFileNameForRecipient(context, recipient));
}
public static void delete(Context context, Recipient recipient) {
Record.delete(context, getFileNameForRecipient(context, recipient));
Record.delete(context, SESSIONS_DIRECTORY, getFileNameForRecipient(context, recipient));
}
private static String getFileNameForRecipient(Context context, Recipient recipient) {
@ -121,12 +119,11 @@ public class LocalKeyRecord extends Record {
synchronized (FILE_LOCK) {
try {
FileInputStream in = this.openInputStream();
localCurrentKeyPair = readKeyPair(in);
localNextKeyPair = readKeyPair(in);
localCurrentKeyPair = readKeyPair(in, masterCipher);
localNextKeyPair = readKeyPair(in, masterCipher);
in.close();
} catch (FileNotFoundException e) {
Log.w("LocalKeyRecord", "No local keypair set found.");
return;
} catch (IOException ioe) {
Log.w("keyrecord", ioe);
// XXX
@ -141,8 +138,11 @@ public class LocalKeyRecord extends Record {
writeBlob(keyPairBytes, out);
}
private KeyPair readKeyPair(FileInputStream in) throws IOException, InvalidKeyException {
private KeyPair readKeyPair(FileInputStream in, MasterCipher masterCipher)
throws IOException, InvalidKeyException
{
byte[] keyPairBytes = readBlob(in);
return new KeyPair(keyPairBytes, masterCipher);
}
}

Wyświetl plik

@ -0,0 +1,125 @@
package org.thoughtcrime.securesms.database.keys;
import android.content.Context;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.PreKeyPair;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
public class PreKeyRecord extends Record {
private static final Object FILE_LOCK = new Object();
private static final int CURRENT_VERSION_MARKER = 1;
private final MasterCipher masterCipher;
private final MasterSecret masterSecret;
private PreKeyPair keyPair;
private long id;
public PreKeyRecord(Context context, MasterSecret masterSecret, long id)
throws InvalidKeyIdException
{
super(context, PREKEY_DIRECTORY, id+"");
this.id = id;
this.masterSecret = masterSecret;
this.masterCipher = new MasterCipher(masterSecret);
loadData();
}
public PreKeyRecord(Context context, MasterSecret masterSecret,
long id, PreKeyPair keyPair)
{
super(context, PREKEY_DIRECTORY, id+"");
this.id = id;
this.keyPair = keyPair;
this.masterSecret = masterSecret;
this.masterCipher = new MasterCipher(masterSecret);
}
public long getId() {
return id;
}
public PreKeyPair getKeyPair() {
return keyPair;
}
public static boolean hasRecord(Context context, long id) {
Log.w("PreKeyRecord", "Checking: " + id);
return Record.hasRecord(context, PREKEY_DIRECTORY, id+"");
}
public static void delete(Context context, long id) {
Record.delete(context, PREKEY_DIRECTORY, id+"");
}
public void save() {
synchronized (FILE_LOCK) {
try {
RandomAccessFile file = openRandomAccessFile();
FileChannel out = file.getChannel();
out.position(0);
writeInteger(CURRENT_VERSION_MARKER, out);
writeKeyPair(keyPair, out);
out.force(true);
out.truncate(out.position());
out.close();
file.close();
} catch (IOException ioe) {
Log.w("PreKeyRecord", ioe);
}
}
}
private void loadData() throws InvalidKeyIdException {
synchronized (FILE_LOCK) {
try {
FileInputStream in = this.openInputStream();
int recordVersion = readInteger(in);
if (recordVersion != CURRENT_VERSION_MARKER) {
Log.w("PreKeyRecord", "Invalid version: " + recordVersion);
return;
}
keyPair = readKeyPair(in, masterCipher);
in.close();
} catch (FileNotFoundException e) {
Log.w("PreKeyRecord", e);
throw new InvalidKeyIdException(e);
} catch (IOException ioe) {
Log.w("PreKeyRecord", ioe);
throw new InvalidKeyIdException(ioe);
} catch (InvalidKeyException ike) {
Log.w("LocalKeyRecord", ike);
throw new InvalidKeyIdException(ike);
}
}
}
private void writeKeyPair(PreKeyPair keyPair, FileChannel out) throws IOException {
byte[] serialized = keyPair.serialize();
writeBlob(serialized, out);
}
private PreKeyPair readKeyPair(FileInputStream in, MasterCipher masterCipher)
throws IOException, InvalidKeyException
{
byte[] keyPairBytes = readBlob(in);
return new PreKeyPair(masterSecret, keyPairBytes);
}
}

Wyświetl plik

@ -18,6 +18,9 @@ package org.thoughtcrime.securesms.database.keys;
import android.content.Context;
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
import org.thoughtcrime.securesms.crypto.KeyPair;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.whispersystems.textsecure.util.Conversions;
import java.io.File;
@ -30,24 +33,29 @@ import java.nio.channels.FileChannel;
public abstract class Record {
protected static final String SESSIONS_DIRECTORY = "sessions";
public static final String PREKEY_DIRECTORY = "prekeys";
protected final String address;
protected final String directory;
protected final Context context;
public Record(Context context, String address) {
this.context = context;
this.address = address;
public Record(Context context, String directory, String address) {
this.context = context;
this.directory = directory;
this.address = address;
}
public void delete() {
delete(this.context, this.address);
delete(this.context, this.directory, this.address);
}
protected static void delete(Context context, String address) {
getAddressFile(context, address).delete();
protected static void delete(Context context, String directory, String address) {
getAddressFile(context, directory, address).delete();
}
protected static boolean hasRecord(Context context, String address) {
return getAddressFile(context, address).exists();
protected static boolean hasRecord(Context context, String directory, String address) {
return getAddressFile(context, directory, address).exists();
}
protected RandomAccessFile openRandomAccessFile() throws FileNotFoundException {
@ -59,11 +67,11 @@ public abstract class Record {
}
private File getAddressFile() {
return getAddressFile(context, address);
return getAddressFile(context, directory, address);
}
private static File getAddressFile(Context context, String address) {
return new File(context.getFilesDir().getAbsolutePath() + File.separatorChar + "sessions", address);
private static File getAddressFile(Context context, String directory, String address) {
return new File(context.getFilesDir().getAbsolutePath() + File.separatorChar + directory, address);
}
protected byte[] readBlob(FileInputStream in) throws IOException {

Wyświetl plik

@ -47,17 +47,17 @@ public class RemoteKeyRecord extends Record {
private PublicKey remoteKeyLast;
public RemoteKeyRecord(Context context, Recipient recipient) {
super(context,getFileNameForRecipient(context, recipient));
super(context, SESSIONS_DIRECTORY, getFileNameForRecipient(context, recipient));
loadData();
}
public static void delete(Context context, Recipient recipient) {
Record.delete(context, getFileNameForRecipient(context, recipient));
Record.delete(context, SESSIONS_DIRECTORY, getFileNameForRecipient(context, recipient));
}
public static boolean hasRecord(Context context, Recipient recipient) {
Log.w("LocalKeyRecord", "Checking: " + getFileNameForRecipient(context, recipient));
return Record.hasRecord(context, getFileNameForRecipient(context, recipient));
return Record.hasRecord(context, SESSIONS_DIRECTORY, getFileNameForRecipient(context, recipient));
}
private static String getFileNameForRecipient(Context context, Recipient recipient) {
@ -126,7 +126,6 @@ public class RemoteKeyRecord extends Record {
in.close();
} catch (FileNotFoundException e) {
Log.w("RemoteKeyRecord", "No remote keys found.");
return;
} catch (IOException ioe) {
Log.w("keyrecord", ioe);
// XXX

Wyświetl plik

@ -58,19 +58,19 @@ public class SessionRecord extends Record {
}
public SessionRecord(Context context, MasterSecret masterSecret, long recipientId) {
super(context, recipientId+"");
super(context, SESSIONS_DIRECTORY, recipientId+"");
this.masterSecret = masterSecret;
this.sessionVersion = 31337;
loadData();
}
public static void delete(Context context, Recipient recipient) {
Record.delete(context, getRecipientId(context, recipient)+"");
Record.delete(context, SESSIONS_DIRECTORY, getRecipientId(context, recipient)+"");
}
public static boolean hasSession(Context context, Recipient recipient) {
Log.w("LocalKeyRecord", "Checking: " + getRecipientId(context, recipient));
return Record.hasRecord(context, getRecipientId(context, recipient)+"");
return Record.hasRecord(context, SESSIONS_DIRECTORY, getRecipientId(context, recipient)+"");
}
private static long getRecipientId(Context context, Recipient recipient) {

Wyświetl plik

@ -0,0 +1,451 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: PreKeyEntity.proto
package org.thoughtcrime.securesms.encoded;
public final class PreKeyProtos {
private PreKeyProtos() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
}
public interface PreKeyEntityOrBuilder
extends com.google.protobuf.MessageOrBuilder {
// optional uint64 id = 1;
boolean hasId();
long getId();
// optional bytes key = 2;
boolean hasKey();
com.google.protobuf.ByteString getKey();
}
public static final class PreKeyEntity extends
com.google.protobuf.GeneratedMessage
implements PreKeyEntityOrBuilder {
// Use PreKeyEntity.newBuilder() to construct.
private PreKeyEntity(Builder builder) {
super(builder);
}
private PreKeyEntity(boolean noInit) {}
private static final PreKeyEntity defaultInstance;
public static PreKeyEntity getDefaultInstance() {
return defaultInstance;
}
public PreKeyEntity getDefaultInstanceForType() {
return defaultInstance;
}
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return org.thoughtcrime.securesms.encoded.PreKeyProtos.internal_static_textsecure_PreKeyEntity_descriptor;
}
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
internalGetFieldAccessorTable() {
return org.thoughtcrime.securesms.encoded.PreKeyProtos.internal_static_textsecure_PreKeyEntity_fieldAccessorTable;
}
private int bitField0_;
// optional uint64 id = 1;
public static final int ID_FIELD_NUMBER = 1;
private long id_;
public boolean hasId() {
return ((bitField0_ & 0x00000001) == 0x00000001);
}
public long getId() {
return id_;
}
// optional bytes key = 2;
public static final int KEY_FIELD_NUMBER = 2;
private com.google.protobuf.ByteString key_;
public boolean hasKey() {
return ((bitField0_ & 0x00000002) == 0x00000002);
}
public com.google.protobuf.ByteString getKey() {
return key_;
}
private void initFields() {
id_ = 0L;
key_ = com.google.protobuf.ByteString.EMPTY;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
byte isInitialized = memoizedIsInitialized;
if (isInitialized != -1) return isInitialized == 1;
memoizedIsInitialized = 1;
return true;
}
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
getSerializedSize();
if (((bitField0_ & 0x00000001) == 0x00000001)) {
output.writeUInt64(1, id_);
}
if (((bitField0_ & 0x00000002) == 0x00000002)) {
output.writeBytes(2, key_);
}
getUnknownFields().writeTo(output);
}
private int memoizedSerializedSize = -1;
public int getSerializedSize() {
int size = memoizedSerializedSize;
if (size != -1) return size;
size = 0;
if (((bitField0_ & 0x00000001) == 0x00000001)) {
size += com.google.protobuf.CodedOutputStream
.computeUInt64Size(1, id_);
}
if (((bitField0_ & 0x00000002) == 0x00000002)) {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(2, key_);
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
}
private static final long serialVersionUID = 0L;
@java.lang.Override
protected java.lang.Object writeReplace()
throws java.io.ObjectStreamException {
return super.writeReplace();
}
public static org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity parseFrom(
com.google.protobuf.ByteString data)
throws com.google.protobuf.InvalidProtocolBufferException {
return newBuilder().mergeFrom(data).buildParsed();
}
public static org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity parseFrom(
com.google.protobuf.ByteString data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return newBuilder().mergeFrom(data, extensionRegistry)
.buildParsed();
}
public static org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return newBuilder().mergeFrom(data).buildParsed();
}
public static org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity parseFrom(
byte[] data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return newBuilder().mergeFrom(data, extensionRegistry)
.buildParsed();
}
public static org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity parseFrom(java.io.InputStream input)
throws java.io.IOException {
return newBuilder().mergeFrom(input).buildParsed();
}
public static org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity parseFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return newBuilder().mergeFrom(input, extensionRegistry)
.buildParsed();
}
public static org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity parseDelimitedFrom(java.io.InputStream input)
throws java.io.IOException {
Builder builder = newBuilder();
if (builder.mergeDelimitedFrom(input)) {
return builder.buildParsed();
} else {
return null;
}
}
public static org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity parseDelimitedFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
Builder builder = newBuilder();
if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
return builder.buildParsed();
} else {
return null;
}
}
public static org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity parseFrom(
com.google.protobuf.CodedInputStream input)
throws java.io.IOException {
return newBuilder().mergeFrom(input).buildParsed();
}
public static org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity parseFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return newBuilder().mergeFrom(input, extensionRegistry)
.buildParsed();
}
public static Builder newBuilder() { return Builder.create(); }
public Builder newBuilderForType() { return newBuilder(); }
public static Builder newBuilder(org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity prototype) {
return newBuilder().mergeFrom(prototype);
}
public Builder toBuilder() { return newBuilder(this); }
@java.lang.Override
protected Builder newBuilderForType(
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
Builder builder = new Builder(parent);
return builder;
}
public static final class Builder extends
com.google.protobuf.GeneratedMessage.Builder<Builder>
implements org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntityOrBuilder {
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return org.thoughtcrime.securesms.encoded.PreKeyProtos.internal_static_textsecure_PreKeyEntity_descriptor;
}
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
internalGetFieldAccessorTable() {
return org.thoughtcrime.securesms.encoded.PreKeyProtos.internal_static_textsecure_PreKeyEntity_fieldAccessorTable;
}
// Construct using org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity.newBuilder()
private Builder() {
maybeForceBuilderInitialization();
}
private Builder(BuilderParent parent) {
super(parent);
maybeForceBuilderInitialization();
}
private void maybeForceBuilderInitialization() {
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
}
}
private static Builder create() {
return new Builder();
}
public Builder clear() {
super.clear();
id_ = 0L;
bitField0_ = (bitField0_ & ~0x00000001);
key_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000002);
return this;
}
public Builder clone() {
return create().mergeFrom(buildPartial());
}
public com.google.protobuf.Descriptors.Descriptor
getDescriptorForType() {
return org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity.getDescriptor();
}
public org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity getDefaultInstanceForType() {
return org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity.getDefaultInstance();
}
public org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity build() {
org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
private org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity buildParsed()
throws com.google.protobuf.InvalidProtocolBufferException {
org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(
result).asInvalidProtocolBufferException();
}
return result;
}
public org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity buildPartial() {
org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity result = new org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity(this);
int from_bitField0_ = bitField0_;
int to_bitField0_ = 0;
if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
to_bitField0_ |= 0x00000001;
}
result.id_ = id_;
if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
to_bitField0_ |= 0x00000002;
}
result.key_ = key_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
}
public Builder mergeFrom(com.google.protobuf.Message other) {
if (other instanceof org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity) {
return mergeFrom((org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity)other);
} else {
super.mergeFrom(other);
return this;
}
}
public Builder mergeFrom(org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity other) {
if (other == org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity.getDefaultInstance()) return this;
if (other.hasId()) {
setId(other.getId());
}
if (other.hasKey()) {
setKey(other.getKey());
}
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
public final boolean isInitialized() {
return true;
}
public Builder mergeFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
com.google.protobuf.UnknownFieldSet.newBuilder(
this.getUnknownFields());
while (true) {
int tag = input.readTag();
switch (tag) {
case 0:
this.setUnknownFields(unknownFields.build());
onChanged();
return this;
default: {
if (!parseUnknownField(input, unknownFields,
extensionRegistry, tag)) {
this.setUnknownFields(unknownFields.build());
onChanged();
return this;
}
break;
}
case 8: {
bitField0_ |= 0x00000001;
id_ = input.readUInt64();
break;
}
case 18: {
bitField0_ |= 0x00000002;
key_ = input.readBytes();
break;
}
}
}
}
private int bitField0_;
// optional uint64 id = 1;
private long id_ ;
public boolean hasId() {
return ((bitField0_ & 0x00000001) == 0x00000001);
}
public long getId() {
return id_;
}
public Builder setId(long value) {
bitField0_ |= 0x00000001;
id_ = value;
onChanged();
return this;
}
public Builder clearId() {
bitField0_ = (bitField0_ & ~0x00000001);
id_ = 0L;
onChanged();
return this;
}
// optional bytes key = 2;
private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY;
public boolean hasKey() {
return ((bitField0_ & 0x00000002) == 0x00000002);
}
public com.google.protobuf.ByteString getKey() {
return key_;
}
public Builder setKey(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000002;
key_ = value;
onChanged();
return this;
}
public Builder clearKey() {
bitField0_ = (bitField0_ & ~0x00000002);
key_ = getDefaultInstance().getKey();
onChanged();
return this;
}
// @@protoc_insertion_point(builder_scope:textsecure.PreKeyEntity)
}
static {
defaultInstance = new PreKeyEntity(true);
defaultInstance.initFields();
}
// @@protoc_insertion_point(class_scope:textsecure.PreKeyEntity)
}
private static com.google.protobuf.Descriptors.Descriptor
internal_static_textsecure_PreKeyEntity_descriptor;
private static
com.google.protobuf.GeneratedMessage.FieldAccessorTable
internal_static_textsecure_PreKeyEntity_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\022PreKeyEntity.proto\022\ntextsecure\"\'\n\014PreK" +
"eyEntity\022\n\n\002id\030\001 \001(\004\022\013\n\003key\030\002 \001(\014B2\n\"org" +
".thoughtcrime.securesms.encodedB\014PreKeyP" +
"rotos"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
public com.google.protobuf.ExtensionRegistry assignDescriptors(
com.google.protobuf.Descriptors.FileDescriptor root) {
descriptor = root;
internal_static_textsecure_PreKeyEntity_descriptor =
getDescriptor().getMessageTypes().get(0);
internal_static_textsecure_PreKeyEntity_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_textsecure_PreKeyEntity_descriptor,
new java.lang.String[] { "Id", "Key", },
org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity.class,
org.thoughtcrime.securesms.encoded.PreKeyProtos.PreKeyEntity.Builder.class);
return null;
}
};
com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
}, assigner);
}
// @@protoc_insertion_point(outer_class_scope)
}

Wyświetl plik

@ -13,6 +13,9 @@ import android.util.Pair;
import com.google.android.gcm.GCMRegistrar;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
import org.thoughtcrime.securesms.database.keys.PreKeyRecord;
import org.thoughtcrime.securesms.gcm.GcmIntentService;
import org.thoughtcrime.securesms.gcm.GcmRegistrationTimeoutException;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -23,6 +26,7 @@ import org.whispersystems.textsecure.util.Util;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -61,6 +65,7 @@ public class RegistrationService extends Service {
public static final String GCM_REGISTRATION_ID = "GCMRegistrationId";
private static final long REGISTRATION_TIMEOUT_MILLIS = 120000;
private static final Object GENERATING_PREKEYS_SEMAPHOR = new Object();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final Binder binder = new RegistrationServiceBinder();
@ -73,6 +78,7 @@ public class RegistrationService extends Service {
private String challenge;
private String gcmRegistrationId;
private long verificationStartTime;
private boolean generatingPreKeys;
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
@ -80,9 +86,9 @@ public class RegistrationService extends Service {
executor.execute(new Runnable() {
@Override
public void run() {
if (intent.getAction().equals(REGISTER_NUMBER_ACTION)) handleRegistrationIntent(intent);
if (intent.getAction().equals(REGISTER_NUMBER_ACTION)) handleSmsRegistrationIntent(intent);
else if (intent.getAction().equals(VOICE_REQUESTED_ACTION)) handleVoiceRequestedIntent(intent);
else if (intent.getAction().equals(VOICE_REGISTER_ACTION)) handleVoiceRegisterIntent(intent);
else if (intent.getAction().equals(VOICE_REGISTER_ACTION)) handleVoiceRegistrationIntent(intent);
}
});
}
@ -135,6 +141,26 @@ public class RegistrationService extends Service {
registerReceiver(gcmRegistrationReceiver, filter);
}
private void initializePreKeyGenerator(final MasterSecret masterSecret) {
synchronized (GENERATING_PREKEYS_SEMAPHOR) {
if (generatingPreKeys) return;
else generatingPreKeys = true;
}
new Thread() {
public void run() {
if (PreKeyUtil.getPreKeys(RegistrationService.this, masterSecret).size() < PreKeyUtil.BATCH_SIZE) {
PreKeyUtil.generatePreKeys(RegistrationService.this, masterSecret);
}
synchronized (GENERATING_PREKEYS_SEMAPHOR) {
generatingPreKeys = false;
GENERATING_PREKEYS_SEMAPHOR.notifyAll();
}
}
}.start();
}
private synchronized void shutdownChallengeListener() {
if (challengeReceiver != null) {
unregisterReceiver(challengeReceiver);
@ -155,24 +181,20 @@ public class RegistrationService extends Service {
intent.getStringExtra("password")));
}
private void handleVoiceRegisterIntent(Intent intent) {
private void handleVoiceRegistrationIntent(Intent intent) {
markAsVerifying(true);
String number = intent.getStringExtra("e164number");
String password = intent.getStringExtra("password");
String number = intent.getStringExtra("e164number");
String password = intent.getStringExtra("password" );
MasterSecret masterSecret = intent.getParcelableExtra("master_secret");
try {
initializeGcmRegistrationListener();
initializePreKeyGenerator(masterSecret);
PushServiceSocket socket = new PushServiceSocket(this, number, password);
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
GCMRegistrar.register(this, GcmIntentService.GCM_SENDER_ID);
String gcmRegistrationId = waitForGcmRegistrationId();
socket.registerGcmId(gcmRegistrationId);
Pair<DirectoryDescriptor, File> directory = socket.retrieveDirectory();
NumberFilter.getInstance(this).update(directory.first, directory.second);
handleCommonRegistration(masterSecret, socket, number);
markAsVerified(number, password);
@ -195,15 +217,17 @@ public class RegistrationService extends Service {
}
}
private void handleRegistrationIntent(Intent intent) {
private void handleSmsRegistrationIntent(Intent intent) {
markAsVerifying(true);
String number = intent.getStringExtra("e164number");
String number = intent.getStringExtra("e164number");
MasterSecret masterSecret = intent.getParcelableExtra("master_secret");
try {
String password = Util.getSecret(18);
initializeChallengeListener();
initializeGcmRegistrationListener();
initializePreKeyGenerator(masterSecret);
setState(new RegistrationState(RegistrationState.STATE_CONNECTING, number));
PushServiceSocket socket = new PushServiceSocket(this, number, password);
@ -213,14 +237,7 @@ public class RegistrationService extends Service {
String challenge = waitForChallenge();
socket.verifyAccount(challenge);
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
GCMRegistrar.register(this, GcmIntentService.GCM_SENDER_ID);
String gcmRegistrationId = waitForGcmRegistrationId();
socket.registerGcmId(gcmRegistrationId);
Pair<DirectoryDescriptor, File> directory = socket.retrieveDirectory();
NumberFilter.getInstance(this).update(directory.first, directory.second);
handleCommonRegistration(masterSecret, socket, number);
markAsVerified(number, password);
setState(new RegistrationState(RegistrationState.STATE_COMPLETE, number));
@ -247,6 +264,22 @@ public class RegistrationService extends Service {
}
}
private void handleCommonRegistration(MasterSecret masterSecret, PushServiceSocket socket, String number)
throws GcmRegistrationTimeoutException, IOException
{
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
List<PreKeyRecord> records = waitForPreKeys(masterSecret);
socket.registerPreKeys(PreKeyUtil.toJson(records));
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
GCMRegistrar.register(this, GcmIntentService.GCM_SENDER_ID);
String gcmRegistrationId = waitForGcmRegistrationId();
socket.registerGcmId(gcmRegistrationId);
Pair<DirectoryDescriptor, File> directory = socket.retrieveDirectory();
NumberFilter.getInstance(this).update(directory.first, directory.second);
}
private synchronized String waitForChallenge() throws AccountVerificationTimeoutException {
this.verificationStartTime = System.currentTimeMillis();
@ -279,6 +312,20 @@ public class RegistrationService extends Service {
return this.gcmRegistrationId;
}
private List<PreKeyRecord> waitForPreKeys(MasterSecret masterSecret) {
synchronized (GENERATING_PREKEYS_SEMAPHOR) {
while (generatingPreKeys) {
try {
GENERATING_PREKEYS_SEMAPHOR.wait();
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
}
return PreKeyUtil.getPreKeys(this, masterSecret);
}
private synchronized void challengeReceived(String challenge) {
this.challenge = challenge;
notifyAll();
@ -367,6 +414,7 @@ public class RegistrationService extends Service {
public static final int STATE_GCM_TIMEOUT = 10;
public static final int STATE_VOICE_REQUESTED = 12;
public static final int STATE_GENERATING_KEYS = 13;
public final int state;
public final String number;