Skip to content

Commit 5f5ddd7

Browse files
committed
Generate SignedPreKey records, improve SignedPreKey cleanup.
1 parent 144f269 commit 5f5ddd7

11 files changed

Lines changed: 285 additions & 64 deletions

File tree

androidTest/org/thoughtcrime/securesms/database/CanonicalAddressDatabaseTest.java renamed to androidTest/java/org/thoughtcrime/securesms/database/CanonicalAddressDatabaseTest.java

File renamed without changes.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package org.thoughtcrime.securesms.service;
2+
3+
import android.test.AndroidTestCase;
4+
5+
import org.whispersystems.libaxolotl.ecc.Curve;
6+
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
7+
import org.whispersystems.libaxolotl.state.SignedPreKeyStore;
8+
import org.whispersystems.textsecure.push.PushServiceSocket;
9+
import org.whispersystems.textsecure.push.SignedPreKeyEntity;
10+
11+
import java.io.IOException;
12+
import java.util.LinkedList;
13+
import java.util.List;
14+
15+
import static org.mockito.Matchers.anyInt;
16+
import static org.mockito.Matchers.eq;
17+
import static org.mockito.Mockito.mock;
18+
import static org.mockito.Mockito.never;
19+
import static org.mockito.Mockito.times;
20+
import static org.mockito.Mockito.verify;
21+
import static org.mockito.Mockito.verifyNoMoreInteractions;
22+
import static org.mockito.Mockito.when;
23+
24+
public class PreKeyServiceTest extends AndroidTestCase {
25+
26+
public void testSignedPreKeyRotationNotRegistered() throws IOException {
27+
SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class);
28+
PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class);
29+
30+
when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(null);
31+
32+
PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore,
33+
pushServiceSocket);
34+
35+
cleanTask.run();
36+
37+
verify(pushServiceSocket).getCurrentSignedPreKey();
38+
verifyNoMoreInteractions(signedPreKeyStore);
39+
}
40+
41+
public void testSignedPreKeyEviction() throws Exception {
42+
SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class);
43+
PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class);
44+
SignedPreKeyEntity currentSignedPreKeyEntity = mock(SignedPreKeyEntity.class);
45+
46+
when(currentSignedPreKeyEntity.getKeyId()).thenReturn(3133);
47+
when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(currentSignedPreKeyEntity);
48+
49+
final SignedPreKeyRecord currentRecord = new SignedPreKeyRecord(3133, System.currentTimeMillis(), Curve.generateKeyPair(true), new byte[64]);
50+
51+
List<SignedPreKeyRecord> records = new LinkedList<SignedPreKeyRecord>() {{
52+
add(new SignedPreKeyRecord(1, 10, Curve.generateKeyPair(true), new byte[32]));
53+
add(new SignedPreKeyRecord(2, 11, Curve.generateKeyPair(true), new byte[32]));
54+
add(new SignedPreKeyRecord(3, System.currentTimeMillis() - 90, Curve.generateKeyPair(true), new byte[64]));
55+
add(new SignedPreKeyRecord(4, System.currentTimeMillis() - 100, Curve.generateKeyPair(true), new byte[64]));
56+
add(currentRecord);
57+
}};
58+
59+
when(signedPreKeyStore.loadSignedPreKeys()).thenReturn(records);
60+
when(signedPreKeyStore.loadSignedPreKey(eq(3133))).thenReturn(currentRecord);
61+
62+
PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore, pushServiceSocket);
63+
cleanTask.run();
64+
65+
verify(signedPreKeyStore).removeSignedPreKey(eq(1));
66+
verify(signedPreKeyStore).removeSignedPreKey(eq(2));
67+
verify(signedPreKeyStore, times(2)).removeSignedPreKey(anyInt());
68+
}
69+
70+
public void testSignedPreKeyNoEviction() throws Exception {
71+
SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class);
72+
PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class);
73+
SignedPreKeyEntity currentSignedPreKeyEntity = mock(SignedPreKeyEntity.class);
74+
75+
when(currentSignedPreKeyEntity.getKeyId()).thenReturn(3133);
76+
when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(currentSignedPreKeyEntity);
77+
78+
final SignedPreKeyRecord currentRecord = new SignedPreKeyRecord(3133, System.currentTimeMillis(), Curve.generateKeyPair(true), new byte[64]);
79+
80+
List<SignedPreKeyRecord> records = new LinkedList<SignedPreKeyRecord>() {{
81+
add(currentRecord);
82+
}};
83+
84+
when(signedPreKeyStore.loadSignedPreKeys()).thenReturn(records);
85+
when(signedPreKeyStore.loadSignedPreKey(eq(3133))).thenReturn(currentRecord);
86+
87+
PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore, pushServiceSocket);
88+
cleanTask.run();
89+
90+
verify(signedPreKeyStore, never()).removeSignedPreKey(anyInt());
91+
}
92+
}

androidTest/org/thoughtcrime/securesms/util/PhoneNumberFormatterTest.java renamed to androidTest/java/org/thoughtcrime/securesms/util/PhoneNumberFormatterTest.java

File renamed without changes.

androidTest/org/thoughtcrime/securesms/util/UtilTest.java renamed to androidTest/java/org/thoughtcrime/securesms/util/UtilTest.java

File renamed without changes.

build.gradle

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ dependencies {
4040
androidTestCompile 'com.squareup:fest-android:1.0.8'
4141

4242
compile project(':library')
43+
44+
androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
45+
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
4346
}
4447

4548
dependencyVerification {
@@ -84,10 +87,10 @@ android {
8487
assets.srcDirs = ['assets']
8588
}
8689
androidTest {
87-
java.srcDirs = ['androidTest']
88-
resources.srcDirs = ['androidTest']
89-
aidl.srcDirs = ['androidTest']
90-
renderscript.srcDirs = ['androidTest']
90+
java.srcDirs = ['androidTest/java']
91+
resources.srcDirs = ['androidTest/java']
92+
aidl.srcDirs = ['androidTest/java']
93+
renderscript.srcDirs = ['androidTest/java']
9194
}
9295
}
9396
}

library/src/org/whispersystems/textsecure/push/PushServiceSocket.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public class PushServiceSocket {
7070
private static final String PREKEY_METADATA_PATH = "/v2/keys/";
7171
private static final String PREKEY_PATH = "/v2/keys/%s";
7272
private static final String PREKEY_DEVICE_PATH = "/v2/keys/%s/%s";
73+
private static final String SIGNED_PREKEY_PATH = "/v2/keys/signed";
7374

7475
private static final String DIRECTORY_TOKENS_PATH = "/v1/directory/tokens";
7576
private static final String DIRECTORY_VERIFY_PATH = "/v1/directory/%s";
@@ -251,6 +252,23 @@ public PreKeyBundle getPreKey(PushAddress destination) throws IOException {
251252
}
252253
}
253254

255+
public SignedPreKeyEntity getCurrentSignedPreKey() throws IOException {
256+
try {
257+
String responseText = makeRequest(SIGNED_PREKEY_PATH, "GET", null);
258+
return SignedPreKeyEntity.fromJson(responseText);
259+
} catch (NotFoundException e) {
260+
Log.w("PushServiceSocket", e);
261+
return null;
262+
}
263+
}
264+
265+
public void setCurrentSignedPreKey(SignedPreKeyRecord signedPreKey) throws IOException {
266+
SignedPreKeyEntity signedPreKeyEntity = new SignedPreKeyEntity(signedPreKey.getId(),
267+
signedPreKey.getKeyPair().getPublicKey(),
268+
signedPreKey.getSignature());
269+
makeRequest(SIGNED_PREKEY_PATH, "PUT", SignedPreKeyEntity.toJson(signedPreKeyEntity));
270+
}
271+
254272
public long sendAttachment(PushAttachmentData attachment) throws IOException {
255273
String response = makeRequest(String.format(ATTACHMENT_PATH, ""), "GET", null);
256274
AttachmentDescriptor attachmentKey = new Gson().fromJson(response, AttachmentDescriptor.class);

library/src/org/whispersystems/textsecure/push/SignedPreKeyEntity.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ public byte[] getSignature() {
3030
return signature;
3131
}
3232

33+
public static String toJson(SignedPreKeyEntity entity) {
34+
GsonBuilder builder = new GsonBuilder();
35+
return forBuilder(builder).create().toJson(entity);
36+
}
37+
38+
public static SignedPreKeyEntity fromJson(String serialized) {
39+
GsonBuilder builder = new GsonBuilder();
40+
return forBuilder(builder).create().fromJson(serialized, SignedPreKeyEntity.class);
41+
}
42+
3343
public static GsonBuilder forBuilder(GsonBuilder builder) {
3444
return PreKeyEntity.forBuilder(builder)
3545
.registerTypeAdapter(byte[].class, new ByteArrayJsonAdapter());

src/org/thoughtcrime/securesms/RoutingActivity.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,19 @@
1010
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
1111
import org.thoughtcrime.securesms.recipients.Recipients;
1212
import org.thoughtcrime.securesms.service.GcmRegistrationService;
13+
import org.thoughtcrime.securesms.service.PreKeyService;
1314
import org.thoughtcrime.securesms.util.TextSecurePreferences;
1415
import org.whispersystems.textsecure.crypto.MasterSecret;
1516

1617
public class RoutingActivity extends PassphraseRequiredSherlockActivity {
1718

1819
private static final int STATE_CREATE_PASSPHRASE = 1;
1920
private static final int STATE_PROMPT_PASSPHRASE = 2;
21+
2022
private static final int STATE_CONVERSATION_OR_LIST = 3;
2123
private static final int STATE_UPGRADE_DATABASE = 4;
2224
private static final int STATE_PROMPT_PUSH_REGISTRATION = 5;
25+
private static final int STATE_CREATE_SIGNED_PREKEY = 6;
2326

2427
private MasterSecret masterSecret = null;
2528
private boolean isVisible = false;
@@ -119,14 +122,13 @@ private void handleDisplayConversationOrList() {
119122
final ConversationParameters parameters = getConversationParameters();
120123
final Intent intent;
121124

122-
scheduleRefreshActions();
123125

124-
if (isShareAction()) {
125-
intent = getShareIntent(parameters);
126-
} else if (parameters.recipients != null) {
127-
intent = getConversationIntent(parameters);
128-
} else {
129-
intent = getConversationListIntent();
126+
if (isShareAction()) intent = getShareIntent(parameters);
127+
else if (parameters.recipients != null) intent = getConversationIntent(parameters);
128+
else intent = getConversationListIntent();
129+
130+
if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) {
131+
PreKeyService.initiateCreateSigned(this, masterSecret);
130132
}
131133

132134
startActivity(intent);

0 commit comments

Comments
 (0)