mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-19 19:08:26 +00:00
Generate "prekeys" at push registration time.
This generates a large number of key exchange messages and registers them with the server during signup.
This commit is contained in:
parent
cfb7b8fcba
commit
2042ca6cb7
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
|
BIN
libs/protobuf-java-2.4.1.jar
Normal file
BIN
libs/protobuf-java-2.4.1.jar
Normal file
Binary file not shown.
3
protobuf/Makefile
Normal file
3
protobuf/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
all:
|
||||
protoc --java_out=../src/ PreKeyEntity.proto
|
9
protobuf/PreKeyEntity.proto
Normal file
9
protobuf/PreKeyEntity.proto
Normal file
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
43
src/org/thoughtcrime/securesms/crypto/PreKeyPair.java
Normal file
43
src/org/thoughtcrime/securesms/crypto/PreKeyPair.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
113
src/org/thoughtcrime/securesms/crypto/PreKeyUtil.java
Normal file
113
src/org/thoughtcrime/securesms/crypto/PreKeyUtil.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
125
src/org/thoughtcrime/securesms/database/keys/PreKeyRecord.java
Normal file
125
src/org/thoughtcrime/securesms/database/keys/PreKeyRecord.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
451
src/org/thoughtcrime/securesms/encoded/PreKeyProtos.java
Normal file
451
src/org/thoughtcrime/securesms/encoded/PreKeyProtos.java
Normal file
@ -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)
|
||||
}
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user