Skip to content

Commit

Permalink
Generate SignedPreKey records, improve SignedPreKey cleanup.
Browse files Browse the repository at this point in the history
  • Loading branch information
moxie0 committed Oct 20, 2014
1 parent 144f269 commit 5f5ddd7
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -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 changes: 7 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ dependencies {
androidTestCompile 'com.squareup:fest-android:1.0.8'

compile project(':library')

androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
}

dependencyVerification {
Expand Down Expand Up @@ -84,10 +87,10 @@ android {
assets.srcDirs = ['assets']
}
androidTest {
java.srcDirs = ['androidTest']
resources.srcDirs = ['androidTest']
aidl.srcDirs = ['androidTest']
renderscript.srcDirs = ['androidTest']
java.srcDirs = ['androidTest/java']
resources.srcDirs = ['androidTest/java']
aidl.srcDirs = ['androidTest/java']
renderscript.srcDirs = ['androidTest/java']
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public class PushServiceSocket {
private static final String PREKEY_METADATA_PATH = "/v2/keys/";
private static final String PREKEY_PATH = "/v2/keys/%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_VERIFY_PATH = "/v1/directory/%s";
Expand Down Expand Up @@ -251,6 +252,23 @@ public PreKeyBundle getPreKey(PushAddress destination) throws IOException {
}
}

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 {
String response = makeRequest(String.format(ATTACHMENT_PATH, ""), "GET", null);
AttachmentDescriptor attachmentKey = new Gson().fromJson(response, AttachmentDescriptor.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ public byte[] getSignature() {
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) {
return PreKeyEntity.forBuilder(builder)
.registerTypeAdapter(byte[].class, new ByteArrayJsonAdapter());
Expand Down
16 changes: 9 additions & 7 deletions src/org/thoughtcrime/securesms/RoutingActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.GcmRegistrationService;
import org.thoughtcrime.securesms.service.PreKeyService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.MasterSecret;

public class RoutingActivity extends PassphraseRequiredSherlockActivity {

private static final int STATE_CREATE_PASSPHRASE = 1;
private static final int STATE_PROMPT_PASSPHRASE = 2;

private static final int STATE_CONVERSATION_OR_LIST = 3;
private static final int STATE_UPGRADE_DATABASE = 4;
private static final int STATE_PROMPT_PUSH_REGISTRATION = 5;
private static final int STATE_CREATE_SIGNED_PREKEY = 6;

private MasterSecret masterSecret = null;
private boolean isVisible = false;
Expand Down Expand Up @@ -119,14 +122,13 @@ private void handleDisplayConversationOrList() {
final ConversationParameters parameters = getConversationParameters();
final Intent intent;

scheduleRefreshActions();

if (isShareAction()) {
intent = getShareIntent(parameters);
} else if (parameters.recipients != null) {
intent = getConversationIntent(parameters);
} else {
intent = getConversationListIntent();
if (isShareAction()) intent = getShareIntent(parameters);
else if (parameters.recipients != null) intent = getConversationIntent(parameters);
else intent = getConversationListIntent();

if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) {
PreKeyService.initiateCreateSigned(this, masterSecret);
}

startActivity(intent);
Expand Down

0 comments on commit 5f5ddd7

Please sign in to comment.