mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-25 22:38:49 +00:00
Support for multi-device provisioning flow.
// FREEBIE
This commit is contained in:
124
src/org/thoughtcrime/securesms/DeviceProvisioningActivity.java
Normal file
124
src/org/thoughtcrime/securesms/DeviceProvisioningActivity.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.whispersystems.libaxolotl.IdentityKeyPair;
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.ecc.Curve;
|
||||
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
||||
import org.whispersystems.textsecure.api.TextSecureAccountManager;
|
||||
import org.whispersystems.textsecure.api.push.exceptions.NotFoundException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DeviceProvisioningActivity extends PassphraseRequiredActionBarActivity {
|
||||
|
||||
private static final String TAG = DeviceProvisioningActivity.class.getSimpleName();
|
||||
|
||||
private Button continueButton;
|
||||
private Button cancelButton;
|
||||
private Uri uri;
|
||||
private MasterSecret masterSecret;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setContentView(R.layout.device_provisioning_activity);
|
||||
|
||||
initializeResources();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewMasterSecret(MasterSecret masterSecret) {
|
||||
this.masterSecret = masterSecret;
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.continueButton = (Button)findViewById(R.id.continue_button);
|
||||
this.cancelButton = (Button)findViewById(R.id.cancel_button);
|
||||
this.uri = getIntent().getData();
|
||||
|
||||
this.continueButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleProvisioning();
|
||||
}
|
||||
});
|
||||
|
||||
this.cancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleProvisioning() {
|
||||
new ProgressDialogAsyncTask<Void, Void, Integer>(this, "Adding device...", "Adding new device...") {
|
||||
private static final int SUCCESS = 0;
|
||||
private static final int NO_DEVICE = 1;
|
||||
private static final int NETWORK_ERROR = 2;
|
||||
private static final int KEY_ERROR = 3;
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(Void... params) {
|
||||
try {
|
||||
Context context = DeviceProvisioningActivity.this;
|
||||
TextSecureAccountManager accountManager = TextSecureCommunicationFactory.createManager(context);
|
||||
String verificationCode = accountManager.getNewDeviceVerificationCode();
|
||||
String ephemeralId = uri.getQueryParameter("uuid");
|
||||
String publicKeyEncoded = uri.getQueryParameter("pub_key");
|
||||
ECPublicKey publicKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0);
|
||||
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
|
||||
|
||||
accountManager.addDevice(ephemeralId, publicKey, identityKeyPair, verificationCode);
|
||||
return SUCCESS;
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
Log.w(TAG, e);
|
||||
return NO_DEVICE;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return NETWORK_ERROR;
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w(TAG, e);
|
||||
return KEY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Integer result) {
|
||||
super.onPostExecute(result);
|
||||
|
||||
Context context = DeviceProvisioningActivity.this;
|
||||
|
||||
switch (result) {
|
||||
case SUCCESS:
|
||||
Toast.makeText(context, "Device added!", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
break;
|
||||
case NO_DEVICE:
|
||||
Toast.makeText(context, "No device found!", Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case NETWORK_ERROR:
|
||||
Toast.makeText(context, "Network error!", Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case KEY_ERROR:
|
||||
Toast.makeText(context, "Invalid QR code!", Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,15 @@ import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
||||
import org.thoughtcrime.securesms.push.SecurityEventListener;
|
||||
import org.thoughtcrime.securesms.push.TextSecurePushTrustStore;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
import org.whispersystems.textsecure.api.TextSecureAccountManager;
|
||||
import org.whispersystems.textsecure.api.TextSecureMessageReceiver;
|
||||
import org.whispersystems.textsecure.api.TextSecureMessageSender;
|
||||
import org.whispersystems.textsecure.api.push.PushAddress;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -52,13 +56,21 @@ public class TextSecureCommunicationModule {
|
||||
return new TextSecureMessageSenderFactory() {
|
||||
@Override
|
||||
public TextSecureMessageSender create(MasterSecret masterSecret) {
|
||||
return new TextSecureMessageSender(Release.PUSH_URL,
|
||||
new TextSecurePushTrustStore(context),
|
||||
TextSecurePreferences.getLocalNumber(context),
|
||||
TextSecurePreferences.getPushServerPassword(context),
|
||||
new TextSecureAxolotlStore(context, masterSecret),
|
||||
Optional.of((TextSecureMessageSender.EventListener)
|
||||
new SecurityEventListener(context)));
|
||||
try {
|
||||
String localNumber = TextSecurePreferences.getLocalNumber(context);
|
||||
Recipient localRecipient = RecipientFactory.getRecipientsFromString(context, localNumber, false).getPrimaryRecipient();
|
||||
|
||||
return new TextSecureMessageSender(Release.PUSH_URL,
|
||||
new TextSecurePushTrustStore(context),
|
||||
TextSecurePreferences.getLocalNumber(context),
|
||||
TextSecurePreferences.getPushServerPassword(context),
|
||||
localRecipient.getRecipientId(),
|
||||
new TextSecureAxolotlStore(context, masterSecret),
|
||||
Optional.of((TextSecureMessageSender.EventListener)
|
||||
new SecurityEventListener(context)));
|
||||
} catch (RecipientFormattingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class DeliveryReceiptJob extends ContextJob implements InjectableType {
|
||||
public void onRun() throws IOException {
|
||||
Log.w("DeliveryReceiptJob", "Sending delivery receipt...");
|
||||
TextSecureMessageSender messageSender = messageSenderFactory.create(null);
|
||||
PushAddress pushAddress = new PushAddress(-1, destination, 1, relay);
|
||||
PushAddress pushAddress = new PushAddress(-1, destination, relay);
|
||||
|
||||
messageSender.sendDeliveryReceipt(pushAddress, timestamp);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,9 @@ import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
||||
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
||||
import org.thoughtcrime.securesms.mms.MmsMediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.PushMediaConstraints;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
@@ -20,7 +18,6 @@ import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureAttachmentStream;
|
||||
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
||||
import org.whispersystems.textsecure.api.push.PushAddress;
|
||||
import org.whispersystems.textsecure.api.util.InvalidNumberException;
|
||||
|
||||
@@ -82,7 +79,7 @@ public abstract class PushSendJob extends SendJob {
|
||||
protected PushAddress getPushAddress(Recipient recipient) throws InvalidNumberException {
|
||||
String e164number = Util.canonicalizeNumber(context, recipient.getNumber());
|
||||
String relay = TextSecureDirectory.getInstance(context).getRelay(e164number);
|
||||
return new PushAddress(recipient.getRecipientId(), e164number, 1, relay);
|
||||
return new PushAddress(recipient.getRecipientId(), e164number, relay);
|
||||
}
|
||||
|
||||
protected boolean isSmsFallbackApprovalRequired(String destination, boolean media) {
|
||||
|
||||
@@ -16,8 +16,12 @@ import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.jobs.GcmRefreshJob;
|
||||
import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
@@ -230,21 +234,27 @@ public class RegistrationService extends Service {
|
||||
throws IOException
|
||||
{
|
||||
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
|
||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(this, masterSecret);
|
||||
List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(this, masterSecret);
|
||||
PreKeyRecord lastResort = PreKeyUtil.generateLastResortKey(this, masterSecret);
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(this, masterSecret, identityKey);
|
||||
accountManager.setPreKeys(identityKey.getPublicKey(),lastResort, signedPreKey, records);
|
||||
try {
|
||||
Recipient self = RecipientFactory.getRecipientsFromString(this, number, false).getPrimaryRecipient();
|
||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(this, masterSecret);
|
||||
List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(this, masterSecret);
|
||||
PreKeyRecord lastResort = PreKeyUtil.generateLastResortKey(this, masterSecret);
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(this, masterSecret, identityKey);
|
||||
accountManager.setPreKeys(identityKey.getPublicKey(),lastResort, signedPreKey, records);
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
|
||||
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
|
||||
|
||||
String gcmRegistrationId = GoogleCloudMessaging.getInstance(this).register(GcmRefreshJob.REGISTRATION_ID);
|
||||
TextSecurePreferences.setGcmRegistrationId(this, gcmRegistrationId);
|
||||
accountManager.setGcmId(Optional.of(gcmRegistrationId));
|
||||
String gcmRegistrationId = GoogleCloudMessaging.getInstance(this).register(GcmRefreshJob.REGISTRATION_ID);
|
||||
TextSecurePreferences.setGcmRegistrationId(this, gcmRegistrationId);
|
||||
accountManager.setGcmId(Optional.of(gcmRegistrationId));
|
||||
|
||||
DirectoryHelper.refreshDirectory(this, accountManager, number);
|
||||
DatabaseFactory.getIdentityDatabase(this).saveIdentity(masterSecret, self.getRecipientId(), identityKey.getPublicKey());
|
||||
DirectoryHelper.refreshDirectory(this, accountManager, number);
|
||||
|
||||
DirectoryRefreshListener.schedule(this);
|
||||
DirectoryRefreshListener.schedule(this);
|
||||
} catch (RecipientFormattingException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized String waitForChallenge() throws AccountVerificationTimeoutException {
|
||||
|
||||
Reference in New Issue
Block a user