mirror of
https://github.com/oxen-io/session-android.git
synced 2025-04-16 10:51:25 +00:00
Generate SignedPreKey records, improve SignedPreKey cleanup.
This commit is contained in:
parent
144f269059
commit
5f5ddd7c26
@ -0,0 +1,92 @@
|
|||||||
|
package org.thoughtcrime.securesms.service;
|
||||||
|
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
|
import org.whispersystems.libaxolotl.ecc.Curve;
|
||||||
|
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
|
||||||
|
import org.whispersystems.libaxolotl.state.SignedPreKeyStore;
|
||||||
|
import org.whispersystems.textsecure.push.PushServiceSocket;
|
||||||
|
import org.whispersystems.textsecure.push.SignedPreKeyEntity;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.anyInt;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class PreKeyServiceTest extends AndroidTestCase {
|
||||||
|
|
||||||
|
public void testSignedPreKeyRotationNotRegistered() throws IOException {
|
||||||
|
SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class);
|
||||||
|
PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class);
|
||||||
|
|
||||||
|
when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(null);
|
||||||
|
|
||||||
|
PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore,
|
||||||
|
pushServiceSocket);
|
||||||
|
|
||||||
|
cleanTask.run();
|
||||||
|
|
||||||
|
verify(pushServiceSocket).getCurrentSignedPreKey();
|
||||||
|
verifyNoMoreInteractions(signedPreKeyStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSignedPreKeyEviction() throws Exception {
|
||||||
|
SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class);
|
||||||
|
PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class);
|
||||||
|
SignedPreKeyEntity currentSignedPreKeyEntity = mock(SignedPreKeyEntity.class);
|
||||||
|
|
||||||
|
when(currentSignedPreKeyEntity.getKeyId()).thenReturn(3133);
|
||||||
|
when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(currentSignedPreKeyEntity);
|
||||||
|
|
||||||
|
final SignedPreKeyRecord currentRecord = new SignedPreKeyRecord(3133, System.currentTimeMillis(), Curve.generateKeyPair(true), new byte[64]);
|
||||||
|
|
||||||
|
List<SignedPreKeyRecord> records = new LinkedList<SignedPreKeyRecord>() {{
|
||||||
|
add(new SignedPreKeyRecord(1, 10, Curve.generateKeyPair(true), new byte[32]));
|
||||||
|
add(new SignedPreKeyRecord(2, 11, Curve.generateKeyPair(true), new byte[32]));
|
||||||
|
add(new SignedPreKeyRecord(3, System.currentTimeMillis() - 90, Curve.generateKeyPair(true), new byte[64]));
|
||||||
|
add(new SignedPreKeyRecord(4, System.currentTimeMillis() - 100, Curve.generateKeyPair(true), new byte[64]));
|
||||||
|
add(currentRecord);
|
||||||
|
}};
|
||||||
|
|
||||||
|
when(signedPreKeyStore.loadSignedPreKeys()).thenReturn(records);
|
||||||
|
when(signedPreKeyStore.loadSignedPreKey(eq(3133))).thenReturn(currentRecord);
|
||||||
|
|
||||||
|
PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore, pushServiceSocket);
|
||||||
|
cleanTask.run();
|
||||||
|
|
||||||
|
verify(signedPreKeyStore).removeSignedPreKey(eq(1));
|
||||||
|
verify(signedPreKeyStore).removeSignedPreKey(eq(2));
|
||||||
|
verify(signedPreKeyStore, times(2)).removeSignedPreKey(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSignedPreKeyNoEviction() throws Exception {
|
||||||
|
SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class);
|
||||||
|
PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class);
|
||||||
|
SignedPreKeyEntity currentSignedPreKeyEntity = mock(SignedPreKeyEntity.class);
|
||||||
|
|
||||||
|
when(currentSignedPreKeyEntity.getKeyId()).thenReturn(3133);
|
||||||
|
when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(currentSignedPreKeyEntity);
|
||||||
|
|
||||||
|
final SignedPreKeyRecord currentRecord = new SignedPreKeyRecord(3133, System.currentTimeMillis(), Curve.generateKeyPair(true), new byte[64]);
|
||||||
|
|
||||||
|
List<SignedPreKeyRecord> records = new LinkedList<SignedPreKeyRecord>() {{
|
||||||
|
add(currentRecord);
|
||||||
|
}};
|
||||||
|
|
||||||
|
when(signedPreKeyStore.loadSignedPreKeys()).thenReturn(records);
|
||||||
|
when(signedPreKeyStore.loadSignedPreKey(eq(3133))).thenReturn(currentRecord);
|
||||||
|
|
||||||
|
PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore, pushServiceSocket);
|
||||||
|
cleanTask.run();
|
||||||
|
|
||||||
|
verify(signedPreKeyStore, never()).removeSignedPreKey(anyInt());
|
||||||
|
}
|
||||||
|
}
|
11
build.gradle
11
build.gradle
@ -40,6 +40,9 @@ dependencies {
|
|||||||
androidTestCompile 'com.squareup:fest-android:1.0.8'
|
androidTestCompile 'com.squareup:fest-android:1.0.8'
|
||||||
|
|
||||||
compile project(':library')
|
compile project(':library')
|
||||||
|
|
||||||
|
androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
|
||||||
|
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
@ -84,10 +87,10 @@ android {
|
|||||||
assets.srcDirs = ['assets']
|
assets.srcDirs = ['assets']
|
||||||
}
|
}
|
||||||
androidTest {
|
androidTest {
|
||||||
java.srcDirs = ['androidTest']
|
java.srcDirs = ['androidTest/java']
|
||||||
resources.srcDirs = ['androidTest']
|
resources.srcDirs = ['androidTest/java']
|
||||||
aidl.srcDirs = ['androidTest']
|
aidl.srcDirs = ['androidTest/java']
|
||||||
renderscript.srcDirs = ['androidTest']
|
renderscript.srcDirs = ['androidTest/java']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ public class PushServiceSocket {
|
|||||||
private static final String PREKEY_METADATA_PATH = "/v2/keys/";
|
private static final String PREKEY_METADATA_PATH = "/v2/keys/";
|
||||||
private static final String PREKEY_PATH = "/v2/keys/%s";
|
private static final String PREKEY_PATH = "/v2/keys/%s";
|
||||||
private static final String PREKEY_DEVICE_PATH = "/v2/keys/%s/%s";
|
private static final String PREKEY_DEVICE_PATH = "/v2/keys/%s/%s";
|
||||||
|
private static final String SIGNED_PREKEY_PATH = "/v2/keys/signed";
|
||||||
|
|
||||||
private static final String DIRECTORY_TOKENS_PATH = "/v1/directory/tokens";
|
private static final String DIRECTORY_TOKENS_PATH = "/v1/directory/tokens";
|
||||||
private static final String DIRECTORY_VERIFY_PATH = "/v1/directory/%s";
|
private static final String DIRECTORY_VERIFY_PATH = "/v1/directory/%s";
|
||||||
@ -251,6 +252,23 @@ public class PushServiceSocket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SignedPreKeyEntity getCurrentSignedPreKey() throws IOException {
|
||||||
|
try {
|
||||||
|
String responseText = makeRequest(SIGNED_PREKEY_PATH, "GET", null);
|
||||||
|
return SignedPreKeyEntity.fromJson(responseText);
|
||||||
|
} catch (NotFoundException e) {
|
||||||
|
Log.w("PushServiceSocket", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentSignedPreKey(SignedPreKeyRecord signedPreKey) throws IOException {
|
||||||
|
SignedPreKeyEntity signedPreKeyEntity = new SignedPreKeyEntity(signedPreKey.getId(),
|
||||||
|
signedPreKey.getKeyPair().getPublicKey(),
|
||||||
|
signedPreKey.getSignature());
|
||||||
|
makeRequest(SIGNED_PREKEY_PATH, "PUT", SignedPreKeyEntity.toJson(signedPreKeyEntity));
|
||||||
|
}
|
||||||
|
|
||||||
public long sendAttachment(PushAttachmentData attachment) throws IOException {
|
public long sendAttachment(PushAttachmentData attachment) throws IOException {
|
||||||
String response = makeRequest(String.format(ATTACHMENT_PATH, ""), "GET", null);
|
String response = makeRequest(String.format(ATTACHMENT_PATH, ""), "GET", null);
|
||||||
AttachmentDescriptor attachmentKey = new Gson().fromJson(response, AttachmentDescriptor.class);
|
AttachmentDescriptor attachmentKey = new Gson().fromJson(response, AttachmentDescriptor.class);
|
||||||
|
@ -30,6 +30,16 @@ public class SignedPreKeyEntity extends PreKeyEntity {
|
|||||||
return signature;
|
return signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String toJson(SignedPreKeyEntity entity) {
|
||||||
|
GsonBuilder builder = new GsonBuilder();
|
||||||
|
return forBuilder(builder).create().toJson(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignedPreKeyEntity fromJson(String serialized) {
|
||||||
|
GsonBuilder builder = new GsonBuilder();
|
||||||
|
return forBuilder(builder).create().fromJson(serialized, SignedPreKeyEntity.class);
|
||||||
|
}
|
||||||
|
|
||||||
public static GsonBuilder forBuilder(GsonBuilder builder) {
|
public static GsonBuilder forBuilder(GsonBuilder builder) {
|
||||||
return PreKeyEntity.forBuilder(builder)
|
return PreKeyEntity.forBuilder(builder)
|
||||||
.registerTypeAdapter(byte[].class, new ByteArrayJsonAdapter());
|
.registerTypeAdapter(byte[].class, new ByteArrayJsonAdapter());
|
||||||
|
@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
|||||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
import org.thoughtcrime.securesms.service.GcmRegistrationService;
|
import org.thoughtcrime.securesms.service.GcmRegistrationService;
|
||||||
|
import org.thoughtcrime.securesms.service.PreKeyService;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
|
|
||||||
@ -17,9 +18,11 @@ public class RoutingActivity extends PassphraseRequiredSherlockActivity {
|
|||||||
|
|
||||||
private static final int STATE_CREATE_PASSPHRASE = 1;
|
private static final int STATE_CREATE_PASSPHRASE = 1;
|
||||||
private static final int STATE_PROMPT_PASSPHRASE = 2;
|
private static final int STATE_PROMPT_PASSPHRASE = 2;
|
||||||
|
|
||||||
private static final int STATE_CONVERSATION_OR_LIST = 3;
|
private static final int STATE_CONVERSATION_OR_LIST = 3;
|
||||||
private static final int STATE_UPGRADE_DATABASE = 4;
|
private static final int STATE_UPGRADE_DATABASE = 4;
|
||||||
private static final int STATE_PROMPT_PUSH_REGISTRATION = 5;
|
private static final int STATE_PROMPT_PUSH_REGISTRATION = 5;
|
||||||
|
private static final int STATE_CREATE_SIGNED_PREKEY = 6;
|
||||||
|
|
||||||
private MasterSecret masterSecret = null;
|
private MasterSecret masterSecret = null;
|
||||||
private boolean isVisible = false;
|
private boolean isVisible = false;
|
||||||
@ -119,14 +122,13 @@ public class RoutingActivity extends PassphraseRequiredSherlockActivity {
|
|||||||
final ConversationParameters parameters = getConversationParameters();
|
final ConversationParameters parameters = getConversationParameters();
|
||||||
final Intent intent;
|
final Intent intent;
|
||||||
|
|
||||||
scheduleRefreshActions();
|
|
||||||
|
|
||||||
if (isShareAction()) {
|
if (isShareAction()) intent = getShareIntent(parameters);
|
||||||
intent = getShareIntent(parameters);
|
else if (parameters.recipients != null) intent = getConversationIntent(parameters);
|
||||||
} else if (parameters.recipients != null) {
|
else intent = getConversationListIntent();
|
||||||
intent = getConversationIntent(parameters);
|
|
||||||
} else {
|
if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) {
|
||||||
intent = getConversationListIntent();
|
PreKeyService.initiateCreateSigned(this, masterSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
|
@ -10,26 +10,30 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
|||||||
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
|
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.libaxolotl.IdentityKeyPair;
|
import org.whispersystems.libaxolotl.IdentityKeyPair;
|
||||||
|
import org.whispersystems.libaxolotl.InvalidKeyIdException;
|
||||||
|
import org.whispersystems.libaxolotl.state.PreKeyRecord;
|
||||||
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
|
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
|
||||||
import org.whispersystems.libaxolotl.state.SignedPreKeyStore;
|
import org.whispersystems.libaxolotl.state.SignedPreKeyStore;
|
||||||
import org.whispersystems.libaxolotl.state.PreKeyRecord;
|
|
||||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
import org.whispersystems.textsecure.crypto.PreKeyUtil;
|
import org.whispersystems.textsecure.crypto.PreKeyUtil;
|
||||||
import org.whispersystems.textsecure.push.PushServiceSocket;
|
import org.whispersystems.textsecure.push.PushServiceSocket;
|
||||||
|
import org.whispersystems.textsecure.push.SignedPreKeyEntity;
|
||||||
import org.whispersystems.textsecure.storage.TextSecurePreKeyStore;
|
import org.whispersystems.textsecure.storage.TextSecurePreKeyStore;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Iterator;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class PreKeyService extends Service {
|
public class PreKeyService extends Service {
|
||||||
|
|
||||||
private static final String TAG = PreKeyService.class.getSimpleName();
|
private static final String TAG = PreKeyService.class.getSimpleName();
|
||||||
public static final String REFRESH_ACTION = "org.thoughtcrime.securesms.PreKeyService.REFRESH";
|
public static final String REFRESH_ACTION = "org.thoughtcrime.securesms.PreKeyService.REFRESH";
|
||||||
|
public static final String CLEAN_ACTION = "org.thoughtcrime.securesms.PreKeyService.CLEAN";
|
||||||
|
public static final String CREATE_SIGNED_ACTION = "org.thoughtcrime.securesms.PreKeyService.CREATE_SIGNED";
|
||||||
|
|
||||||
private static final int PREKEY_MINIMUM = 10;
|
private static final int PREKEY_MINIMUM = 10;
|
||||||
|
|
||||||
@ -42,11 +46,36 @@ public class PreKeyService extends Service {
|
|||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void initiateClean(Context context, MasterSecret masterSecret) {
|
||||||
|
Intent intent = new Intent(context, PreKeyService.class);
|
||||||
|
intent.setAction(PreKeyService.CLEAN_ACTION);
|
||||||
|
intent.putExtra("master_secret", masterSecret);
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initiateCreateSigned(Context context, MasterSecret masterSecret) {
|
||||||
|
Intent intent = new Intent(context, PreKeyService.class);
|
||||||
|
intent.setAction(PreKeyService.CREATE_SIGNED_ACTION);
|
||||||
|
intent.putExtra("master_secret", masterSecret);
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flats, int startId) {
|
public int onStartCommand(Intent intent, int flats, int startId) {
|
||||||
if (REFRESH_ACTION.equals(intent.getAction())) {
|
if (intent == null) return START_NOT_STICKY;
|
||||||
MasterSecret masterSecret = intent.getParcelableExtra("master_secret");
|
if (intent.getAction() == null) return START_NOT_STICKY;
|
||||||
executor.execute(new RefreshTask(this, masterSecret));
|
|
||||||
|
MasterSecret masterSecret = intent.getParcelableExtra("master_secret");
|
||||||
|
|
||||||
|
if (masterSecret == null) {
|
||||||
|
Log.w(TAG, "No master secret!");
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (intent.getAction()) {
|
||||||
|
case REFRESH_ACTION: executor.execute(new RefreshTask(this, masterSecret)); break;
|
||||||
|
case CLEAN_ACTION: executor.execute(new CleanSignedPreKeysTask(this, masterSecret)); break;
|
||||||
|
case CREATE_SIGNED_ACTION: executor.execute(new CreateSignedPreKeyTask(this, masterSecret)); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
@ -57,6 +86,98 @@ public class PreKeyService extends Service {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class CreateSignedPreKeyTask implements Runnable {
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final MasterSecret masterSecret;
|
||||||
|
|
||||||
|
public CreateSignedPreKeyTask(Context context, MasterSecret masterSecret) {
|
||||||
|
this.context = context;
|
||||||
|
this.masterSecret = masterSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
||||||
|
Log.w(TAG, "Signed prekey already registered...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
|
||||||
|
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, masterSecret, identityKeyPair);
|
||||||
|
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
||||||
|
|
||||||
|
socket.setCurrentSignedPreKey(signedPreKeyRecord);
|
||||||
|
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CleanSignedPreKeysTask implements Runnable {
|
||||||
|
|
||||||
|
private final SignedPreKeyStore signedPreKeyStore;
|
||||||
|
private final PushServiceSocket socket;
|
||||||
|
|
||||||
|
public CleanSignedPreKeysTask(Context context, MasterSecret masterSecret) {
|
||||||
|
this(new TextSecurePreKeyStore(context, masterSecret), PushServiceSocketFactory.create(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CleanSignedPreKeysTask(SignedPreKeyStore signedPreKeyStore, PushServiceSocket socket) {
|
||||||
|
this.signedPreKeyStore = signedPreKeyStore;
|
||||||
|
this.socket = socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
SignedPreKeyEntity currentSignedPreKey = socket.getCurrentSignedPreKey();
|
||||||
|
|
||||||
|
if (currentSignedPreKey == null) return;
|
||||||
|
|
||||||
|
SignedPreKeyRecord currentRecord = signedPreKeyStore.loadSignedPreKey(currentSignedPreKey.getKeyId());
|
||||||
|
List<SignedPreKeyRecord> allRecords = signedPreKeyStore.loadSignedPreKeys();
|
||||||
|
List<SignedPreKeyRecord> oldRecords = removeCurrentRecord(allRecords, currentRecord);
|
||||||
|
SignedPreKeyRecord[] oldRecordsArray = oldRecords.toArray(new SignedPreKeyRecord[0]);
|
||||||
|
|
||||||
|
Arrays.sort(oldRecordsArray, new SignedPreKeySorter());
|
||||||
|
|
||||||
|
Log.w(TAG, "Existing signed prekey record count: " + oldRecordsArray.length);
|
||||||
|
|
||||||
|
if (oldRecordsArray.length > 3) {
|
||||||
|
long oldTimestamp = System.currentTimeMillis() - (14 * 24 * 60 * 60 * 1000);
|
||||||
|
SignedPreKeyRecord[] deletionCandidates = Arrays.copyOf(oldRecordsArray, oldRecordsArray.length - 1);
|
||||||
|
|
||||||
|
for (SignedPreKeyRecord deletionCandidate : deletionCandidates) {
|
||||||
|
Log.w(TAG, "Old signed prekey record timestamp: " + deletionCandidate.getTimestamp());
|
||||||
|
|
||||||
|
if (deletionCandidate.getTimestamp() <= oldTimestamp) {
|
||||||
|
Log.w(TAG, "Remove signed prekey record: " + deletionCandidate.getId());
|
||||||
|
signedPreKeyStore.removeSignedPreKey(deletionCandidate.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException | InvalidKeyIdException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SignedPreKeyRecord> removeCurrentRecord(List<SignedPreKeyRecord> records,
|
||||||
|
SignedPreKeyRecord currentRecord)
|
||||||
|
{
|
||||||
|
List<SignedPreKeyRecord> others = new LinkedList<>();
|
||||||
|
|
||||||
|
for (SignedPreKeyRecord record : records) {
|
||||||
|
if (record.getId() != currentRecord.getId()) {
|
||||||
|
others.add(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return others;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class RefreshTask implements Runnable {
|
private static class RefreshTask implements Runnable {
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
@ -68,15 +189,13 @@ public class PreKeyService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
SignedPreKeyRecord signedPreKeyRecord = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!TextSecurePreferences.isPushRegistered(context)) return;
|
if (!TextSecurePreferences.isPushRegistered(context)) return;
|
||||||
|
|
||||||
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
||||||
int availableKeys = socket.getAvailablePreKeys();
|
int availableKeys = socket.getAvailablePreKeys();
|
||||||
|
|
||||||
if (availableKeys >= PREKEY_MINIMUM) {
|
if (availableKeys >= PREKEY_MINIMUM && TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
||||||
Log.w(TAG, "Available keys sufficient: " + availableKeys);
|
Log.w(TAG, "Available keys sufficient: " + availableKeys);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -84,61 +203,28 @@ public class PreKeyService extends Service {
|
|||||||
List<PreKeyRecord> preKeyRecords = PreKeyUtil.generatePreKeys(context, masterSecret);
|
List<PreKeyRecord> preKeyRecords = PreKeyUtil.generatePreKeys(context, masterSecret);
|
||||||
PreKeyRecord lastResortKeyRecord = PreKeyUtil.generateLastResortKey(context, masterSecret);
|
PreKeyRecord lastResortKeyRecord = PreKeyUtil.generateLastResortKey(context, masterSecret);
|
||||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
|
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
|
||||||
|
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, masterSecret, identityKey);
|
||||||
signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, masterSecret, identityKey);
|
|
||||||
|
|
||||||
Log.w(TAG, "Registering new prekeys...");
|
Log.w(TAG, "Registering new prekeys...");
|
||||||
|
|
||||||
socket.registerPreKeys(identityKey.getPublicKey(), lastResortKeyRecord,
|
socket.registerPreKeys(identityKey.getPublicKey(), lastResortKeyRecord,
|
||||||
signedPreKeyRecord, preKeyRecords);
|
signedPreKeyRecord, preKeyRecords);
|
||||||
|
|
||||||
removeOldSignedPreKeysIfNecessary(signedPreKeyRecord);
|
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
||||||
|
PreKeyService.initiateClean(context, masterSecret);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
if (signedPreKeyRecord != null) {
|
|
||||||
Log.w(TAG, "Remote store failed, removing generated device key: " + signedPreKeyRecord.getId());
|
|
||||||
new TextSecurePreKeyStore(context, masterSecret).removeSignedPreKey(signedPreKeyRecord.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeOldSignedPreKeysIfNecessary(SignedPreKeyRecord currentSignedPreKey) {
|
|
||||||
SignedPreKeyStore signedPreKeyStore = new TextSecurePreKeyStore(context, masterSecret);
|
|
||||||
List<SignedPreKeyRecord> records = signedPreKeyStore.loadSignedPreKeys();
|
|
||||||
Iterator<SignedPreKeyRecord> iterator = records.iterator();
|
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
if (iterator.next().getId() == currentSignedPreKey.getId()) {
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SignedPreKeyRecord[] recordsArray = (SignedPreKeyRecord[])records.toArray();
|
|
||||||
Arrays.sort(recordsArray, new Comparator<SignedPreKeyRecord>() {
|
|
||||||
@Override
|
|
||||||
public int compare(SignedPreKeyRecord lhs, SignedPreKeyRecord rhs) {
|
|
||||||
if (lhs.getTimestamp() < rhs.getTimestamp()) return -1;
|
|
||||||
else if (lhs.getTimestamp() > rhs.getTimestamp()) return 1;
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Log.w(TAG, "Existing device key record count: " + recordsArray.length);
|
|
||||||
|
|
||||||
if (recordsArray.length > 3) {
|
|
||||||
long oldTimestamp = System.currentTimeMillis() - (14 * 24 * 60 * 60 * 1000);
|
|
||||||
SignedPreKeyRecord[] oldRecords = Arrays.copyOf(recordsArray, recordsArray.length - 1);
|
|
||||||
|
|
||||||
for (SignedPreKeyRecord oldRecord : oldRecords) {
|
|
||||||
Log.w(TAG, "Old device key record timestamp: " + oldRecord.getTimestamp());
|
|
||||||
|
|
||||||
if (oldRecord.getTimestamp() <= oldTimestamp) {
|
|
||||||
Log.w(TAG, "Remove device key record: " + oldRecord.getId());
|
|
||||||
signedPreKeyStore.removeSignedPreKey(oldRecord.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class SignedPreKeySorter implements Comparator<SignedPreKeyRecord> {
|
||||||
|
@Override
|
||||||
|
public int compare(SignedPreKeyRecord lhs, SignedPreKeyRecord rhs) {
|
||||||
|
if (lhs.getTimestamp() < rhs.getTimestamp()) return -1;
|
||||||
|
else if (lhs.getTimestamp() > rhs.getTimestamp()) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -281,6 +281,7 @@ public class RegistrationService extends Service {
|
|||||||
TextSecurePreferences.setLocalNumber(this, number);
|
TextSecurePreferences.setLocalNumber(this, number);
|
||||||
TextSecurePreferences.setPushServerPassword(this, password);
|
TextSecurePreferences.setPushServerPassword(this, password);
|
||||||
TextSecurePreferences.setSignalingKey(this, signalingKey);
|
TextSecurePreferences.setSignalingKey(this, signalingKey);
|
||||||
|
TextSecurePreferences.setSignedPreKeyRegistered(this, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setState(RegistrationState state) {
|
private void setState(RegistrationState state) {
|
||||||
|
@ -51,6 +51,15 @@ public class TextSecurePreferences {
|
|||||||
private static final String FALLBACK_SMS_ALLOWED_PREF = "pref_allow_sms_traffic_out";
|
private static final String FALLBACK_SMS_ALLOWED_PREF = "pref_allow_sms_traffic_out";
|
||||||
private static final String FALLBACK_SMS_ASK_REQUIRED_PREF = "pref_sms_fallback_ask";
|
private static final String FALLBACK_SMS_ASK_REQUIRED_PREF = "pref_sms_fallback_ask";
|
||||||
private static final String DIRECT_SMS_ALLOWED_PREF = "pref_sms_non_data_out";
|
private static final String DIRECT_SMS_ALLOWED_PREF = "pref_sms_non_data_out";
|
||||||
|
private static final String SIGNED_PREKEY_REGISTERED_PREF = "pref_signed_prekey_registered";
|
||||||
|
|
||||||
|
public static boolean isSignedPreKeyRegistered(Context context) {
|
||||||
|
return getBooleanPreference(context, SIGNED_PREKEY_REGISTERED_PREF, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setSignedPreKeyRegistered(Context context, boolean value) {
|
||||||
|
setBooleanPreference(context, SIGNED_PREKEY_REGISTERED_PREF, value);
|
||||||
|
}
|
||||||
|
|
||||||
private static final String GCM_REGISTRATION_ID_PREF = "pref_gcm_registration_id";
|
private static final String GCM_REGISTRATION_ID_PREF = "pref_gcm_registration_id";
|
||||||
private static final String GCM_REGISTRATION_ID_VERSION_PREF = "pref_gcm_registration_id_version";
|
private static final String GCM_REGISTRATION_ID_VERSION_PREF = "pref_gcm_registration_id_version";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user