Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ea481f9dc | ||
|
|
e94fa0d752 | ||
|
|
817070e76f | ||
|
|
92b90cd798 | ||
|
|
33ecc4d690 | ||
|
|
7d5e66eb6e | ||
|
|
d4ac0c077d | ||
|
|
cbe87aa05c | ||
|
|
9e2f82954f | ||
|
|
d4d684b670 | ||
|
|
c43ef8bce0 | ||
|
|
54a882a11d | ||
|
|
73e2f6ce59 | ||
|
|
54d2184c72 | ||
|
|
832763f695 | ||
|
|
40629a3bcf | ||
|
|
5a3daf4846 | ||
|
|
fd1a18d2d0 | ||
|
|
ad5d6d5bb7 | ||
|
|
926d3c929f | ||
|
|
edc20883eb | ||
|
|
0d7363e36e | ||
|
|
ca6d8a8a0d | ||
|
|
c414334059 | ||
|
|
24a38985cf | ||
|
|
60f2d4d6b6 | ||
|
|
06659fd98f | ||
|
|
3c9c5213a7 | ||
|
|
a183f8d387 | ||
|
|
216446c55b | ||
|
|
bc143059f6 | ||
|
|
33000582ed | ||
|
|
e651f352bb | ||
|
|
cab4a06974 | ||
|
|
ccc1f5e9d6 | ||
|
|
b860aeff85 | ||
|
|
34c885f08d | ||
|
|
71ab6f5c7d | ||
|
|
61fbf382eb | ||
|
|
8b21f3f7d6 | ||
|
|
941d0089f4 | ||
|
|
8b8c6dd45f | ||
|
|
938545444e | ||
|
|
d827ab1b36 | ||
|
|
4701e59197 | ||
|
|
2b2da84918 | ||
|
|
d229a4274c | ||
|
|
64711771f0 | ||
|
|
ad54d2a05f | ||
|
|
068c40336c | ||
|
|
11cfc4f1a1 | ||
|
|
f51989b23e | ||
|
|
0b4fe84a41 | ||
|
|
b855a1805f | ||
|
|
339193af12 | ||
|
|
574f33c92d | ||
|
|
b6a9eb5bf2 | ||
|
|
54b43b7536 | ||
|
|
37c9fb7cd1 | ||
|
|
667d22bace | ||
|
|
3d782449ed | ||
|
|
003095b08c | ||
|
|
d121d9531e | ||
|
|
42aeca26f1 | ||
|
|
ea8a1bae46 | ||
|
|
054fcdca8d | ||
|
|
278220cf18 | ||
|
|
35eeaa9bd5 | ||
|
|
fbf02603ce | ||
|
|
fa423e4432 | ||
|
|
5caec4a146 | ||
|
|
dc77c43435 | ||
|
|
827298d1a2 | ||
|
|
48f5b932f7 | ||
|
|
90169e9468 | ||
|
|
b5fe378bc9 | ||
|
|
0f53c9d170 | ||
|
|
3474950830 | ||
|
|
39ee363150 | ||
|
|
411e3ceff6 | ||
|
|
b7f8c3b3d3 | ||
|
|
3efa8e6899 | ||
|
|
948f481530 | ||
|
|
716fdefa4c | ||
|
|
2dc893730a | ||
|
|
1af99ce155 | ||
|
|
0850f1b0f1 | ||
|
|
1ddc45fd9c | ||
|
|
977765c80f | ||
|
|
4b2d07ab35 | ||
|
|
003ebe6364 | ||
|
|
2f7c005c23 | ||
|
|
367b481d07 | ||
|
|
5cd8c922d2 | ||
|
|
a2fe8a9d5c | ||
|
|
ee921a8f49 | ||
|
|
e0394b4481 | ||
|
|
de100f5be7 | ||
|
|
a020a57be6 | ||
|
|
28f1a0a636 | ||
|
|
5a807ffc28 | ||
|
|
757cb1c846 | ||
|
|
4f066757e7 | ||
|
|
633aa9b057 | ||
|
|
a5c26b2e16 | ||
|
|
ca561d76ff | ||
|
|
107d999ee7 | ||
|
|
0f6c7660cb | ||
|
|
8ea4db03db | ||
|
|
81ee9e31c5 | ||
|
|
a3e900ecbe | ||
|
|
384fb3b2b5 | ||
|
|
d795aa30b3 | ||
|
|
bac4d63312 | ||
|
|
bf60f90019 | ||
|
|
852ca2ac05 | ||
|
|
038bebfdbb | ||
|
|
3b25b87aa8 | ||
|
|
5a62856e46 | ||
|
|
37a52df4e6 | ||
|
|
d3148b6766 | ||
|
|
6fb85aff6d | ||
|
|
ed45067227 | ||
|
|
0015711759 | ||
|
|
15390e477e | ||
|
|
a8c23413ba | ||
|
|
ce68429a9b | ||
|
|
1a9a88a5a1 | ||
|
|
7987362c25 | ||
|
|
5d42110d6c | ||
|
|
dec7fd4c8a | ||
|
|
82df23dd41 | ||
|
|
ce710b378f | ||
|
|
20fd881613 | ||
|
|
5fa429b0d5 | ||
|
|
630dce04fc | ||
|
|
0da1d8818e | ||
|
|
c84285c639 | ||
|
|
5a525a2e58 | ||
|
|
dda8a214a4 | ||
|
|
bd167cbb17 | ||
|
|
a0aaa7d724 | ||
|
|
25e03b3579 | ||
|
|
52ff4ecfd2 | ||
|
|
43c1576aab | ||
|
|
23c607430d | ||
|
|
829a92d371 | ||
|
|
559228af5b | ||
|
|
e8a0fac05b |
@@ -1,6 +1,6 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = fr_CA:fr-rCA,pt_BR:pt-rBR,pt_PT:pt,zh_CN:zh-rCN,zh_HK:zh-rHK,zh_TW:zh-rTW,da_DK:da-rDK,de_DE:de,fr_FR:fr,es_ES:es,hu_HU:hu,sv_SE:sv-rSE,bg_BG:bg,el_GR:el,kn_IN:kn-rIN,cs_CZ:cs
|
||||
lang_map = fr_CA:fr-rCA,pt_BR:pt-rBR,pt_PT:pt,zh_CN:zh-rCN,zh_HK:zh-rHK,zh_TW:zh-rTW,da_DK:da-rDK,de_DE:de,tr_TR:tr,fr_FR:fr,es_ES:es,hu_HU:hu,sv_SE:sv-rSE,bg_BG:bg,el_GR:el,kn_IN:kn-rIN,cs_CZ:cs
|
||||
|
||||
|
||||
[textsecure-official.master]
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.thoughtcrime.securesms"
|
||||
android:versionCode="63"
|
||||
android:versionName="2.0">
|
||||
android:versionCode="69"
|
||||
android:versionName="2.0.6">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/>
|
||||
|
||||
@@ -69,7 +69,8 @@
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="audio/*" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="text/*" />
|
||||
<data android:mimeType="text/plain" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
@@ -89,7 +90,7 @@
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".MmsPreferencesActivity"
|
||||
<activity android:name=".MmsPreferencesActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".ConversationListActivity"
|
||||
@@ -134,15 +135,15 @@
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".SingleContactSelectionActivity"
|
||||
android:label="@string/AndroidManifest__select_contact"
|
||||
<activity android:name=".NewConversationActivity"
|
||||
android:label="@string/AndroidManifest__select_contacts"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".PushContactSelectionActivity"
|
||||
android:label="@string/AndroidManifest__select_contacts"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
android:label="@string/AndroidManifest__select_contacts"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".AutoInitiateActivity"
|
||||
android:theme="@style/TextSecure.Light.Dialog"
|
||||
@@ -183,10 +184,28 @@
|
||||
<activity android:name=".RegistrationProgressActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".LogSubmitActivity"
|
||||
android:label="@string/AndroidManifest__log_submit"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".DummyActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay"
|
||||
android:enabled="true"
|
||||
android:allowTaskReparenting="true"
|
||||
android:noHistory="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:alwaysRetainTaskState="false"
|
||||
android:stateNotNeeded="true"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:finishOnTaskLaunch="true" />
|
||||
|
||||
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
|
||||
<service android:enabled="true" android:name=".service.KeyCachingService"/>
|
||||
<service android:enabled="true" android:name=".service.SendReceiveService"/>
|
||||
<service android:enabled="true" android:name=".service.RegistrationService"/>
|
||||
<service android:enabled="true" android:name=".service.DirectoryRefreshService"/>
|
||||
<service android:enabled="true" android:name=".service.PreKeyService"/>
|
||||
<service android:enabled="true" android:name=".gcm.GcmIntentService"/>
|
||||
|
||||
<service android:name=".service.QuickResponseService"
|
||||
|
||||
67
BUILDING.md
@@ -1,8 +1,67 @@
|
||||
Building TextSecure
|
||||
===================
|
||||
=====================
|
||||
|
||||
1. Ensure the 'Android Support Repository' is installed from the Android SDK manager.
|
||||
Basics
|
||||
------
|
||||
|
||||
Execute Gradle:
|
||||
TextSecure uses [Gradle](http://gradle.org) to build the project and to maintain
|
||||
dependencies.
|
||||
|
||||
./gradlew build
|
||||
Building TextSecure
|
||||
-------------------
|
||||
|
||||
The following steps should help you (re)build TextSecure from the command line.
|
||||
|
||||
1. Checkout the source somewhere on your filesystem with
|
||||
|
||||
git clone https://github.com/WhisperSystems/TextSecure.git
|
||||
|
||||
2. Make sure you have the [Android SDK](https://developer.android.com/sdk/index.html) installed somewhere on your system.
|
||||
3. Ensure the "Android Support Repository" and "Android SDK Build-tools" are installed from the Android SDK manager.
|
||||
4. Create a local.properties file at the root of your source checkout and add an sdk.dir entry to it.
|
||||
|
||||
sdk.dir=\<path to your sdk installation\>
|
||||
|
||||
5. Execute Gradle:
|
||||
|
||||
./gradlew build
|
||||
|
||||
Re-building native components
|
||||
-----------------------------
|
||||
|
||||
Note: This step is optional; native components are contained as binaries (see [library/libs](library/libs)).
|
||||
|
||||
1. Ensure that the Android NDK is installed.
|
||||
|
||||
Execute ndk-build:
|
||||
|
||||
cd library
|
||||
ndk-build
|
||||
|
||||
Afterwards, execute Gradle as above to re-create the APK.
|
||||
|
||||
Setting up a development environment
|
||||
------------------------------------
|
||||
|
||||
[Android Studio](https://developer.android.com/sdk/installing/studio.html) is the recommended development environment.
|
||||
|
||||
1. Install Android Studio
|
||||
2. Make sure the "Android Support Repository" is installed in the Android Studio SDK.
|
||||
3. Make sure the latest "Android SDK build-tools" is installed in the Android Studio SDK.
|
||||
4. Create a new Android Studio project. from the Quickstart pannel (use File > Close Project to see it), choose "Checkout from Version Control" then "git".
|
||||
5. Paste the URL for the TextSecure project when prompted (https://github.com/WhisperSystems/TextSecure.git)
|
||||
6. Android studio should detect the presence of a project file and ask you wethere to open it. Click "yes".
|
||||
7. Default config options should be good enough.
|
||||
8. Project initialisation and build should proceed.
|
||||
|
||||
Contributing code
|
||||
-----------------
|
||||
|
||||
Code contributions should be sent via github as pull requests, from feature branches [as explained here](https://help.github.com/articles/using-pull-requests).
|
||||
|
||||
Mailing list
|
||||
------------
|
||||
|
||||
Development discussion happens on the whispersystems mailing list.
|
||||
[To join](https://lists.riseup.net/www/info/whispersystems)
|
||||
Send emails to whispersystems@lists.riseup.net
|
||||
|
||||
@@ -10,8 +10,11 @@ TextSecure is a replacement for the standard text messaging application, allowin
|
||||
|
||||
Current BitHub Payment For Commit:
|
||||
=================
|
||||
[](https://whispersystems.org/blog/bithub/)
|
||||
[](https://whispersystems.org/blog/bithub/)
|
||||
|
||||
Building and contributing code
|
||||
==============================
|
||||
Instructions on how to build TextSecure, as well as on how to setup an IDE to modify it can be found in the "BUILDING.md" file.
|
||||
|
||||
Bug tracker
|
||||
-----------
|
||||
|
||||
BIN
artwork/ic_send.psd
Normal file
@@ -3,7 +3,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.8.+'
|
||||
classpath 'com.android.tools.build:gradle:0.9.+'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,14 @@ dependencies {
|
||||
compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar'
|
||||
compile 'com.android.support:support-v4:19.0.1'
|
||||
compile 'com.google.android.gcm:gcm-client:1.0.2'
|
||||
compile 'se.emilsjolander:stickylistheaders:2.2.0'
|
||||
|
||||
compile project(':library')
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion '19.0.0'
|
||||
buildToolsVersion '19.0.2'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
@@ -42,7 +43,7 @@ android {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
resources.srcDirs = ['src']
|
||||
aild.srcDirs = ['src']
|
||||
aidl.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
assets.srcDirs = ['assets']
|
||||
|
||||
3
contributing.md
Normal file
@@ -0,0 +1,3 @@
|
||||
##Translations
|
||||
|
||||
Please do not submit issues or pull requests for translation fixes. Anyone can update the translations in [Transifex](https://www.transifex.com/projects/p/textsecure-official/). Please submit your corrections there.
|
||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Sat Dec 21 23:48:05 PST 2013
|
||||
#Mon Mar 10 23:44:05 PDT 2014
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
|
||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-all.zip
|
||||
|
||||
@@ -4,7 +4,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.8.+'
|
||||
classpath 'com.android.tools.build:gradle:0.9.+'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,11 @@ dependencies {
|
||||
compile 'com.madgag:sc-light-jdk15on:1.47.0.2'
|
||||
compile 'com.googlecode.libphonenumber:libphonenumber:5.3'
|
||||
compile 'org.whispersystems:gson:2.2.4'
|
||||
compile fileTree(dir: 'libs', include: 'armeabi.jar')
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion '19.0.0'
|
||||
buildToolsVersion '19.0.2'
|
||||
|
||||
android {
|
||||
sourceSets {
|
||||
@@ -36,10 +35,11 @@ android {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
resources.srcDirs = ['src']
|
||||
aild.srcDirs = ['src']
|
||||
aidl.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
assets.srcDirs = ['assets']
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
library/jni/Application.mk
Normal file
@@ -0,0 +1 @@
|
||||
APP_ABI := armeabi armeabi-v7a x86
|
||||
@@ -22,11 +22,16 @@
|
||||
#include "curve25519-donna.h"
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_textsecure_crypto_ecc_Curve25519_generatePrivateKey
|
||||
(JNIEnv *env, jclass clazz, jbyteArray random)
|
||||
(JNIEnv *env, jclass clazz, jbyteArray random, jboolean ephemeral)
|
||||
{
|
||||
uint8_t* privateKey = (uint8_t*)(*env)->GetByteArrayElements(env, random, 0);
|
||||
|
||||
privateKey[0] &= 248;
|
||||
|
||||
if (ephemeral) {
|
||||
privateKey[0] |= 1;
|
||||
}
|
||||
|
||||
privateKey[31] &= 127;
|
||||
privateKey[31] |= 64;
|
||||
|
||||
|
||||
@@ -720,9 +720,6 @@ curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 32; ++i) e[i] = secret[i];
|
||||
e[0] &= 248;
|
||||
e[31] &= 127;
|
||||
e[31] |= 64;
|
||||
|
||||
fexpand(bp, basepoint);
|
||||
cmult(x, z, e, bp);
|
||||
|
||||
BIN
library/libs/armeabi-v7a/libcurve25519.so
Executable file
BIN
library/libs/armeabi/libcurve25519.so
Executable file
BIN
library/libs/x86/libcurve25519.so
Executable file
@@ -54,6 +54,13 @@ message SessionStructure {
|
||||
|
||||
optional uint32 remoteRegistrationId = 10;
|
||||
optional uint32 localRegistrationId = 11;
|
||||
|
||||
optional bool needsRefresh = 12;
|
||||
}
|
||||
|
||||
message RecordStructure {
|
||||
optional SessionStructure currentSession = 1;
|
||||
repeated SessionStructure previousSessions = 2;
|
||||
}
|
||||
|
||||
message PreKeyRecordStructure {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.whispersystems.textsecure.crypto;
|
||||
|
||||
public class DuplicateMessageException extends Exception {
|
||||
public DuplicateMessageException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -49,7 +48,7 @@ public class PreKeyUtil {
|
||||
|
||||
for (int i=0;i<BATCH_SIZE;i++) {
|
||||
int preKeyId = (preKeyIdOffset + i) % Medium.MAX_VALUE;
|
||||
ECKeyPair keyPair = Curve25519.generateKeyPair();
|
||||
ECKeyPair keyPair = Curve25519.generateKeyPair(true);
|
||||
PreKeyRecord record = new PreKeyRecord(context, masterSecret, preKeyId, keyPair);
|
||||
|
||||
record.save();
|
||||
@@ -70,7 +69,7 @@ public class PreKeyUtil {
|
||||
}
|
||||
}
|
||||
|
||||
ECKeyPair keyPair = Curve25519.generateKeyPair();
|
||||
ECKeyPair keyPair = Curve25519.generateKeyPair(true);
|
||||
PreKeyRecord record = new PreKeyRecord(context, masterSecret, Medium.MAX_VALUE, keyPair);
|
||||
|
||||
record.save();
|
||||
@@ -78,38 +77,38 @@ public class PreKeyUtil {
|
||||
return record;
|
||||
}
|
||||
|
||||
public static List<PreKeyRecord> getPreKeys(Context context, MasterSecret masterSecret) {
|
||||
List<PreKeyRecord> records = new LinkedList<PreKeyRecord>();
|
||||
File directory = getPreKeysDirectory(context);
|
||||
String[] keyRecordIds = directory.list();
|
||||
|
||||
Arrays.sort(keyRecordIds, new PreKeyRecordIdComparator());
|
||||
|
||||
for (String keyRecordId : keyRecordIds) {
|
||||
try {
|
||||
if (!keyRecordId.equals(PreKeyIndex.FILE_NAME) && Integer.parseInt(keyRecordId) != Medium.MAX_VALUE) {
|
||||
records.add(new PreKeyRecord(context, masterSecret, Integer.parseInt(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 List<PreKeyRecord> getPreKeys(Context context, MasterSecret masterSecret) {
|
||||
// List<PreKeyRecord> records = new LinkedList<PreKeyRecord>();
|
||||
// File directory = getPreKeysDirectory(context);
|
||||
// String[] keyRecordIds = directory.list();
|
||||
//
|
||||
// Arrays.sort(keyRecordIds, new PreKeyRecordIdComparator());
|
||||
//
|
||||
// for (String keyRecordId : keyRecordIds) {
|
||||
// try {
|
||||
// if (!keyRecordId.equals(PreKeyIndex.FILE_NAME) && Integer.parseInt(keyRecordId) != Medium.MAX_VALUE) {
|
||||
// records.add(new PreKeyRecord(context, masterSecret, Integer.parseInt(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();
|
||||
// }
|
||||
// }
|
||||
|
||||
private static void setNextPreKeyId(Context context, int id) {
|
||||
try {
|
||||
@@ -126,7 +125,7 @@ public class PreKeyUtil {
|
||||
try {
|
||||
File nextFile = new File(getPreKeysDirectory(context), PreKeyIndex.FILE_NAME);
|
||||
|
||||
if (nextFile.exists()) {
|
||||
if (!nextFile.exists()) {
|
||||
return Util.getSecureRandom().nextInt(Medium.MAX_VALUE);
|
||||
} else {
|
||||
InputStreamReader reader = new InputStreamReader(new FileInputStream(nextFile));
|
||||
|
||||
@@ -29,7 +29,7 @@ public abstract class SessionCipher {
|
||||
protected static final Object SESSION_LOCK = new Object();
|
||||
|
||||
public abstract CiphertextMessage encrypt(byte[] paddedMessage);
|
||||
public abstract byte[] decrypt(byte[] decodedMessage) throws InvalidMessageException;
|
||||
public abstract byte[] decrypt(byte[] decodedMessage) throws InvalidMessageException, DuplicateMessageException;
|
||||
public abstract int getRemoteRegistrationId();
|
||||
|
||||
public static SessionCipher createFor(Context context,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.whispersystems.textsecure.crypto;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.whispersystems.textsecure.crypto.ecc.Curve;
|
||||
@@ -14,10 +15,12 @@ import org.whispersystems.textsecure.crypto.ratchet.MessageKeys;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.RootKey;
|
||||
import org.whispersystems.textsecure.storage.RecipientDevice;
|
||||
import org.whispersystems.textsecure.storage.SessionRecordV2;
|
||||
import org.whispersystems.textsecure.storage.SessionState;
|
||||
import org.whispersystems.textsecure.util.Conversions;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
@@ -45,27 +48,28 @@ public class SessionCipherV2 extends SessionCipher {
|
||||
public CiphertextMessage encrypt(byte[] paddedMessage) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
SessionRecordV2 sessionRecord = getSessionRecord();
|
||||
ChainKey chainKey = sessionRecord.getSenderChainKey();
|
||||
SessionState sessionState = sessionRecord.getSessionState();
|
||||
ChainKey chainKey = sessionState.getSenderChainKey();
|
||||
MessageKeys messageKeys = chainKey.getMessageKeys();
|
||||
ECPublicKey senderEphemeral = sessionRecord.getSenderEphemeral();
|
||||
int previousCounter = sessionRecord.getPreviousCounter();
|
||||
ECPublicKey senderEphemeral = sessionState.getSenderEphemeral();
|
||||
int previousCounter = sessionState.getPreviousCounter();
|
||||
|
||||
byte[] ciphertextBody = getCiphertext(messageKeys, paddedMessage);
|
||||
CiphertextMessage ciphertextMessage = new WhisperMessageV2(messageKeys.getMacKey(),
|
||||
senderEphemeral, chainKey.getIndex(),
|
||||
previousCounter, ciphertextBody);
|
||||
|
||||
if (sessionRecord.hasPendingPreKey()) {
|
||||
Pair<Integer, ECPublicKey> pendingPreKey = sessionRecord.getPendingPreKey();
|
||||
int localRegistrationId = sessionRecord.getLocalRegistrationId();
|
||||
if (sessionState.hasPendingPreKey()) {
|
||||
Pair<Integer, ECPublicKey> pendingPreKey = sessionState.getPendingPreKey();
|
||||
int localRegistrationId = sessionState.getLocalRegistrationId();
|
||||
|
||||
ciphertextMessage = new PreKeyWhisperMessage(localRegistrationId, pendingPreKey.first,
|
||||
pendingPreKey.second,
|
||||
sessionRecord.getLocalIdentityKey(),
|
||||
sessionState.getLocalIdentityKey(),
|
||||
(WhisperMessageV2) ciphertextMessage);
|
||||
}
|
||||
|
||||
sessionRecord.setSenderChainKey(chainKey.getNextChainKey());
|
||||
sessionState.setSenderChainKey(chainKey.getNextChainKey());
|
||||
sessionRecord.save();
|
||||
|
||||
return ciphertextMessage;
|
||||
@@ -73,52 +77,88 @@ public class SessionCipherV2 extends SessionCipher {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decrypt(byte[] decodedMessage) throws InvalidMessageException {
|
||||
public byte[] decrypt(byte[] decodedMessage)
|
||||
throws InvalidMessageException, DuplicateMessageException
|
||||
{
|
||||
synchronized (SESSION_LOCK) {
|
||||
SessionRecordV2 sessionRecord = getSessionRecord();
|
||||
WhisperMessageV2 ciphertextMessage = new WhisperMessageV2(decodedMessage);
|
||||
ECPublicKey theirEphemeral = ciphertextMessage.getSenderEphemeral();
|
||||
int counter = ciphertextMessage.getCounter();
|
||||
ChainKey chainKey = getOrCreateChainKey(sessionRecord, theirEphemeral);
|
||||
MessageKeys messageKeys = getOrCreateMessageKeys(sessionRecord, theirEphemeral,
|
||||
chainKey, counter);
|
||||
SessionRecordV2 sessionRecord = getSessionRecord();
|
||||
SessionState sessionState = sessionRecord.getSessionState();
|
||||
List<SessionState> previousStates = sessionRecord.getPreviousSessions();
|
||||
|
||||
ciphertextMessage.verifyMac(messageKeys.getMacKey());
|
||||
try {
|
||||
byte[] plaintext = decrypt(sessionState, decodedMessage);
|
||||
sessionRecord.save();
|
||||
|
||||
byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody());
|
||||
return plaintext;
|
||||
} catch (InvalidMessageException e) {
|
||||
Log.w("SessionCipherV2", e);
|
||||
}
|
||||
|
||||
sessionRecord.clearPendingPreKey();
|
||||
sessionRecord.save();
|
||||
for (SessionState previousState : previousStates) {
|
||||
try {
|
||||
Log.w("SessionCipherV2", "Attempting decrypt on previous state...");
|
||||
byte[] plaintext = decrypt(previousState, decodedMessage);
|
||||
sessionRecord.save();
|
||||
|
||||
return plaintext;
|
||||
return plaintext;
|
||||
} catch (InvalidMessageException e) {
|
||||
Log.w("SessionCipherV2", e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidMessageException("No valid sessions.");
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] decrypt(SessionState sessionState, byte[] decodedMessage)
|
||||
throws InvalidMessageException, DuplicateMessageException
|
||||
{
|
||||
if (!sessionState.hasSenderChain()) {
|
||||
throw new InvalidMessageException("Uninitialized session!");
|
||||
}
|
||||
|
||||
WhisperMessageV2 ciphertextMessage = new WhisperMessageV2(decodedMessage);
|
||||
ECPublicKey theirEphemeral = ciphertextMessage.getSenderEphemeral();
|
||||
int counter = ciphertextMessage.getCounter();
|
||||
ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral);
|
||||
MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral,
|
||||
chainKey, counter);
|
||||
|
||||
ciphertextMessage.verifyMac(messageKeys.getMacKey());
|
||||
|
||||
byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody());
|
||||
|
||||
sessionState.clearPendingPreKey();
|
||||
|
||||
return plaintext;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemoteRegistrationId() {
|
||||
synchronized (SESSION_LOCK) {
|
||||
SessionRecordV2 sessionRecord = getSessionRecord();
|
||||
return sessionRecord.getRemoteRegistrationId();
|
||||
return sessionRecord.getSessionState().getRemoteRegistrationId();
|
||||
}
|
||||
}
|
||||
|
||||
private ChainKey getOrCreateChainKey(SessionRecordV2 sessionRecord, ECPublicKey theirEphemeral)
|
||||
private ChainKey getOrCreateChainKey(SessionState sessionState, ECPublicKey theirEphemeral)
|
||||
throws InvalidMessageException
|
||||
{
|
||||
try {
|
||||
if (sessionRecord.hasReceiverChain(theirEphemeral)) {
|
||||
return sessionRecord.getReceiverChainKey(theirEphemeral);
|
||||
if (sessionState.hasReceiverChain(theirEphemeral)) {
|
||||
return sessionState.getReceiverChainKey(theirEphemeral);
|
||||
} else {
|
||||
RootKey rootKey = sessionRecord.getRootKey();
|
||||
ECKeyPair ourEphemeral = sessionRecord.getSenderEphemeralPair();
|
||||
RootKey rootKey = sessionState.getRootKey();
|
||||
ECKeyPair ourEphemeral = sessionState.getSenderEphemeralPair();
|
||||
Pair<RootKey, ChainKey> receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral);
|
||||
ECKeyPair ourNewEphemeral = Curve.generateKeyPairForType(Curve.DJB_TYPE);
|
||||
ECKeyPair ourNewEphemeral = Curve.generateKeyPairForType(Curve.DJB_TYPE, true);
|
||||
Pair<RootKey, ChainKey> senderChain = receiverChain.first.createChain(theirEphemeral, ourNewEphemeral);
|
||||
|
||||
sessionRecord.setRootKey(senderChain.first);
|
||||
sessionRecord.addReceiverChain(theirEphemeral, receiverChain.second);
|
||||
sessionRecord.setPreviousCounter(sessionRecord.getSenderChainKey().getIndex()-1);
|
||||
sessionRecord.setSenderChain(ourNewEphemeral, senderChain.second);
|
||||
sessionState.setRootKey(senderChain.first);
|
||||
sessionState.addReceiverChain(theirEphemeral, receiverChain.second);
|
||||
sessionState.setPreviousCounter(sessionState.getSenderChainKey().getIndex()-1);
|
||||
sessionState.setSenderChain(ourNewEphemeral, senderChain.second);
|
||||
|
||||
return receiverChain.second;
|
||||
}
|
||||
@@ -127,30 +167,31 @@ public class SessionCipherV2 extends SessionCipher {
|
||||
}
|
||||
}
|
||||
|
||||
private MessageKeys getOrCreateMessageKeys(SessionRecordV2 sessionRecord,
|
||||
private MessageKeys getOrCreateMessageKeys(SessionState sessionState,
|
||||
ECPublicKey theirEphemeral,
|
||||
ChainKey chainKey, int counter)
|
||||
throws InvalidMessageException
|
||||
throws InvalidMessageException, DuplicateMessageException
|
||||
{
|
||||
if (chainKey.getIndex() > counter) {
|
||||
if (sessionRecord.hasMessageKeys(theirEphemeral, counter)) {
|
||||
return sessionRecord.removeMessageKeys(theirEphemeral, counter);
|
||||
if (sessionState.hasMessageKeys(theirEphemeral, counter)) {
|
||||
return sessionState.removeMessageKeys(theirEphemeral, counter);
|
||||
} else {
|
||||
throw new InvalidMessageException("Received message with old counter!");
|
||||
throw new DuplicateMessageException("Received message with old counter: " +
|
||||
chainKey.getIndex() + " , " + counter);
|
||||
}
|
||||
}
|
||||
|
||||
if (chainKey.getIndex() - counter > 500) {
|
||||
throw new InvalidMessageException("Over 500 messages into the future!");
|
||||
if (chainKey.getIndex() - counter > 2000) {
|
||||
throw new InvalidMessageException("Over 2000 messages into the future!");
|
||||
}
|
||||
|
||||
while (chainKey.getIndex() < counter) {
|
||||
MessageKeys messageKeys = chainKey.getMessageKeys();
|
||||
sessionRecord.setMessageKeys(theirEphemeral, messageKeys);
|
||||
sessionState.setMessageKeys(theirEphemeral, messageKeys);
|
||||
chainKey = chainKey.getNextChainKey();
|
||||
}
|
||||
|
||||
sessionRecord.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey());
|
||||
sessionState.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey());
|
||||
return chainKey.getMessageKeys();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ public class Curve {
|
||||
private static final int NIST_TYPE2 = 0x03;
|
||||
public static final int DJB_TYPE = 0x05;
|
||||
|
||||
public static ECKeyPair generateKeyPairForType(int keyType) {
|
||||
public static ECKeyPair generateKeyPairForType(int keyType, boolean ephemeral) {
|
||||
if (keyType == DJB_TYPE) {
|
||||
return Curve25519.generateKeyPair();
|
||||
return Curve25519.generateKeyPair(ephemeral);
|
||||
} else if (keyType == NIST_TYPE || keyType == NIST_TYPE2) {
|
||||
return CurveP256.generateKeyPair();
|
||||
} else {
|
||||
@@ -35,11 +35,11 @@ public class Curve {
|
||||
}
|
||||
}
|
||||
|
||||
public static ECKeyPair generateKeyPairForSession(int messageVersion) {
|
||||
public static ECKeyPair generateKeyPairForSession(int messageVersion, boolean ephemeral) {
|
||||
if (messageVersion <= CiphertextMessage.LEGACY_VERSION) {
|
||||
return generateKeyPairForType(NIST_TYPE);
|
||||
return generateKeyPairForType(NIST_TYPE, ephemeral);
|
||||
} else {
|
||||
return generateKeyPairForType(DJB_TYPE);
|
||||
return generateKeyPairForType(DJB_TYPE, ephemeral);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@ public class Curve25519 {
|
||||
|
||||
private static native byte[] calculateAgreement(byte[] ourPrivate, byte[] theirPublic);
|
||||
private static native byte[] generatePublicKey(byte[] privateKey);
|
||||
private static native byte[] generatePrivateKey(byte[] random);
|
||||
private static native byte[] generatePrivateKey(byte[] random, boolean ephemeral);
|
||||
|
||||
public static ECKeyPair generateKeyPair() {
|
||||
byte[] privateKey = generatePrivateKey();
|
||||
public static ECKeyPair generateKeyPair(boolean ephemeral) {
|
||||
byte[] privateKey = generatePrivateKey(ephemeral);
|
||||
byte[] publicKey = generatePublicKey(privateKey);
|
||||
|
||||
return new ECKeyPair(new DjbECPublicKey(publicKey), new DjbECPrivateKey(privateKey));
|
||||
@@ -65,11 +65,11 @@ public class Curve25519 {
|
||||
return new DjbECPublicKey(keyBytes);
|
||||
}
|
||||
|
||||
private static byte[] generatePrivateKey() {
|
||||
private static byte[] generatePrivateKey(boolean ephemeral) {
|
||||
byte[] privateKey = new byte[32];
|
||||
random.nextBytes(privateKey);
|
||||
|
||||
return generatePrivateKey(privateKey);
|
||||
return generatePrivateKey(privateKey, ephemeral);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@ import org.whispersystems.textsecure.crypto.ecc.ECKeyPair;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
|
||||
import org.whispersystems.textsecure.crypto.kdf.DerivedSecrets;
|
||||
import org.whispersystems.textsecure.crypto.kdf.HKDF;
|
||||
import org.whispersystems.textsecure.storage.SessionRecordV2;
|
||||
import org.whispersystems.textsecure.storage.SessionState;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class RatchetingSession {
|
||||
|
||||
public static void initializeSession(SessionRecordV2 sessionRecord,
|
||||
public static void initializeSession(SessionState sessionState,
|
||||
ECKeyPair ourBaseKey,
|
||||
ECPublicKey theirBaseKey,
|
||||
ECKeyPair ourEphemeralKey,
|
||||
@@ -27,48 +27,48 @@ public class RatchetingSession {
|
||||
throws InvalidKeyException
|
||||
{
|
||||
if (isAlice(ourBaseKey.getPublicKey(), theirBaseKey, ourEphemeralKey.getPublicKey(), theirEphemeralKey)) {
|
||||
initializeSessionAsAlice(sessionRecord, ourBaseKey, theirBaseKey, theirEphemeralKey,
|
||||
initializeSessionAsAlice(sessionState, ourBaseKey, theirBaseKey, theirEphemeralKey,
|
||||
ourIdentityKey, theirIdentityKey);
|
||||
} else {
|
||||
initializeSessionAsBob(sessionRecord, ourBaseKey, theirBaseKey,
|
||||
initializeSessionAsBob(sessionState, ourBaseKey, theirBaseKey,
|
||||
ourEphemeralKey, ourIdentityKey, theirIdentityKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initializeSessionAsAlice(SessionRecordV2 sessionRecord,
|
||||
private static void initializeSessionAsAlice(SessionState sessionState,
|
||||
ECKeyPair ourBaseKey, ECPublicKey theirBaseKey,
|
||||
ECPublicKey theirEphemeralKey,
|
||||
IdentityKeyPair ourIdentityKey,
|
||||
IdentityKey theirIdentityKey)
|
||||
throws InvalidKeyException
|
||||
{
|
||||
sessionRecord.setRemoteIdentityKey(theirIdentityKey);
|
||||
sessionRecord.setLocalIdentityKey(ourIdentityKey.getPublicKey());
|
||||
sessionState.setRemoteIdentityKey(theirIdentityKey);
|
||||
sessionState.setLocalIdentityKey(ourIdentityKey.getPublicKey());
|
||||
|
||||
ECKeyPair sendingKey = Curve.generateKeyPairForType(ourIdentityKey.getPublicKey().getPublicKey().getType());
|
||||
ECKeyPair sendingKey = Curve.generateKeyPairForType(ourIdentityKey.getPublicKey().getPublicKey().getType(), true);
|
||||
Pair<RootKey, ChainKey> receivingChain = calculate3DHE(true, ourBaseKey, theirBaseKey, ourIdentityKey, theirIdentityKey);
|
||||
Pair<RootKey, ChainKey> sendingChain = receivingChain.first.createChain(theirEphemeralKey, sendingKey);
|
||||
|
||||
sessionRecord.addReceiverChain(theirEphemeralKey, receivingChain.second);
|
||||
sessionRecord.setSenderChain(sendingKey, sendingChain.second);
|
||||
sessionRecord.setRootKey(sendingChain.first);
|
||||
sessionState.addReceiverChain(theirEphemeralKey, receivingChain.second);
|
||||
sessionState.setSenderChain(sendingKey, sendingChain.second);
|
||||
sessionState.setRootKey(sendingChain.first);
|
||||
}
|
||||
|
||||
private static void initializeSessionAsBob(SessionRecordV2 sessionRecord,
|
||||
private static void initializeSessionAsBob(SessionState sessionState,
|
||||
ECKeyPair ourBaseKey, ECPublicKey theirBaseKey,
|
||||
ECKeyPair ourEphemeralKey,
|
||||
IdentityKeyPair ourIdentityKey,
|
||||
IdentityKey theirIdentityKey)
|
||||
throws InvalidKeyException
|
||||
{
|
||||
sessionRecord.setRemoteIdentityKey(theirIdentityKey);
|
||||
sessionRecord.setLocalIdentityKey(ourIdentityKey.getPublicKey());
|
||||
sessionState.setRemoteIdentityKey(theirIdentityKey);
|
||||
sessionState.setLocalIdentityKey(ourIdentityKey.getPublicKey());
|
||||
|
||||
Pair<RootKey, ChainKey> sendingChain = calculate3DHE(false, ourBaseKey, theirBaseKey,
|
||||
ourIdentityKey, theirIdentityKey);
|
||||
|
||||
sessionRecord.setSenderChain(ourEphemeralKey, sendingChain.second);
|
||||
sessionRecord.setRootKey(sendingChain.first);
|
||||
sessionState.setSenderChain(ourEphemeralKey, sendingChain.second);
|
||||
sessionState.setRootKey(sendingChain.first);
|
||||
}
|
||||
|
||||
private static Pair<RootKey, ChainKey> calculate3DHE(boolean isAlice,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.whispersystems.textsecure.push;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ExpectationFailedException extends IOException {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.whispersystems.textsecure.push;
|
||||
|
||||
public class PreKeyStatus {
|
||||
|
||||
private int count;
|
||||
|
||||
public PreKeyStatus() {}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ public class PushServiceSocket {
|
||||
private static final String CREATE_ACCOUNT_VOICE_PATH = "/v1/accounts/voice/code/%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_METADATA_PATH = "/v1/keys/";
|
||||
private static final String PREKEY_PATH = "/v1/keys/%s";
|
||||
private static final String PREKEY_DEVICE_PATH = "/v1/keys/%s/%s";
|
||||
|
||||
@@ -123,6 +124,13 @@ public class PushServiceSocket {
|
||||
PreKeyList.toJson(new PreKeyList(lastResortEntity, entities)));
|
||||
}
|
||||
|
||||
public int getAvailablePreKeys() throws IOException {
|
||||
String responseText = makeRequest(PREKEY_METADATA_PATH, "GET", null);
|
||||
PreKeyStatus preKeyStatus = new Gson().fromJson(responseText, PreKeyStatus.class);
|
||||
|
||||
return preKeyStatus.getCount();
|
||||
}
|
||||
|
||||
public List<PreKeyEntity> getPreKeys(PushAddress destination) throws IOException {
|
||||
try {
|
||||
String deviceId = String.valueOf(destination.getDeviceId());
|
||||
@@ -207,7 +215,7 @@ public class PushServiceSocket {
|
||||
|
||||
public List<ContactTokenDetails> retrieveDirectory(Set<String> contactTokens) {
|
||||
try {
|
||||
ContactTokenList contactTokenList = new ContactTokenList(new LinkedList(contactTokens));
|
||||
ContactTokenList contactTokenList = new ContactTokenList(new LinkedList<String>(contactTokens));
|
||||
String response = makeRequest(DIRECTORY_TOKENS_PATH, "PUT", new Gson().toJson(contactTokenList));
|
||||
ContactTokenDetailsList activeTokens = new Gson().fromJson(response, ContactTokenDetailsList.class);
|
||||
|
||||
@@ -334,6 +342,10 @@ public class PushServiceSocket {
|
||||
throw new StaleDevicesException(new Gson().fromJson(response, StaleDevices.class));
|
||||
}
|
||||
|
||||
if (connection.getResponseCode() == 417) {
|
||||
throw new ExpectationFailedException();
|
||||
}
|
||||
|
||||
if (connection.getResponseCode() != 200 && connection.getResponseCode() != 204) {
|
||||
throw new IOException("Bad response: " + connection.getResponseCode() + " " + connection.getResponseMessage());
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class LocalKeyRecord extends Record {
|
||||
|
||||
this.localCurrentKeyPair = this.localNextKeyPair;
|
||||
this.localNextKeyPair = new KeyPair((this.localNextKeyPair.getId()+1) % Medium.MAX_VALUE,
|
||||
Curve.generateKeyPairForType(keyType),
|
||||
Curve.generateKeyPairForType(keyType, true),
|
||||
masterSecret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.util.Log;
|
||||
|
||||
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
|
||||
|
||||
/**
|
||||
* Helper class for generating key pairs and calculating ECDH agreements.
|
||||
@@ -34,6 +35,27 @@ public class Session {
|
||||
return hasV1Session(context, recipient) || hasV2Session(context, masterSecret, recipient);
|
||||
}
|
||||
|
||||
public static boolean hasEncryptCapableSession(Context context,
|
||||
MasterSecret masterSecret,
|
||||
CanonicalRecipient recipient)
|
||||
{
|
||||
RecipientDevice device = new RecipientDevice(recipient.getRecipientId(),
|
||||
RecipientDevice.DEFAULT_DEVICE_ID);
|
||||
|
||||
return hasEncryptCapableSession(context, masterSecret, recipient, device);
|
||||
}
|
||||
|
||||
public static boolean hasEncryptCapableSession(Context context,
|
||||
MasterSecret masterSecret,
|
||||
CanonicalRecipient recipient,
|
||||
RecipientDevice device)
|
||||
{
|
||||
return
|
||||
hasV1Session(context, recipient) ||
|
||||
(hasV2Session(context, masterSecret, recipient) &&
|
||||
!SessionRecordV2.needsRefresh(context, masterSecret, device));
|
||||
}
|
||||
|
||||
public static boolean hasRemoteIdentityKey(Context context,
|
||||
MasterSecret masterSecret,
|
||||
CanonicalRecipient recipient)
|
||||
@@ -48,7 +70,6 @@ public class Session {
|
||||
return SessionRecordV2.hasSession(context, masterSecret, recipient.getRecipientId(),
|
||||
RecipientDevice.DEFAULT_DEVICE_ID);
|
||||
}
|
||||
|
||||
private static boolean hasV1Session(Context context, CanonicalRecipient recipient) {
|
||||
return SessionRecordV1.hasSession(context, recipient) &&
|
||||
RemoteKeyRecord.hasRecord(context, recipient) &&
|
||||
@@ -69,7 +90,8 @@ public class Session {
|
||||
RecipientDevice.DEFAULT_DEVICE_ID))
|
||||
{
|
||||
return new SessionRecordV2(context, masterSecret, recipientId,
|
||||
RecipientDevice.DEFAULT_DEVICE_ID).getRemoteIdentityKey();
|
||||
RecipientDevice.DEFAULT_DEVICE_ID).getSessionState()
|
||||
.getRemoteIdentityKey();
|
||||
} else if (SessionRecordV1.hasSession(context, recipientId)) {
|
||||
return new SessionRecordV1(context, masterSecret, recipientId).getIdentityKey();
|
||||
} else {
|
||||
@@ -84,8 +106,7 @@ public class Session {
|
||||
recipient.getRecipientId(),
|
||||
RecipientDevice.DEFAULT_DEVICE_ID))
|
||||
{
|
||||
return new SessionRecordV2(context, masterSecret, recipient.getRecipientId(),
|
||||
RecipientDevice.DEFAULT_DEVICE_ID).getSessionVersion();
|
||||
return CiphertextMessage.CURRENT_VERSION;
|
||||
} else if (SessionRecordV1.hasSession(context, recipient)) {
|
||||
return new SessionRecordV1(context, masterSecret, recipient).getSessionVersion();
|
||||
}
|
||||
|
||||
@@ -18,26 +18,10 @@ package org.whispersystems.textsecure.storage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||
import org.whispersystems.textsecure.crypto.IdentityKeyPair;
|
||||
import org.whispersystems.textsecure.crypto.InvalidKeyException;
|
||||
import org.whispersystems.textsecure.crypto.InvalidMessageException;
|
||||
import org.whispersystems.textsecure.crypto.MasterCipher;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
import org.whispersystems.textsecure.crypto.ecc.Curve;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECKeyPair;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.ChainKey;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.MessageKeys;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.RootKey;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Chain;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingKeyExchange;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingPreKey;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -45,11 +29,11 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure;
|
||||
import static org.whispersystems.textsecure.storage.StorageProtos.SessionStructure;
|
||||
|
||||
/**
|
||||
* A disk record representing a current session.
|
||||
@@ -60,11 +44,15 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
public class SessionRecordV2 extends Record {
|
||||
|
||||
private static final Object FILE_LOCK = new Object();
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
|
||||
private static final int SINGLE_STATE_VERSION = 1;
|
||||
private static final int ARCHIVE_STATES_VERSION = 2;
|
||||
private static final int CURRENT_VERSION = 2;
|
||||
|
||||
private final MasterSecret masterSecret;
|
||||
private StorageProtos.SessionStructure sessionStructure =
|
||||
StorageProtos.SessionStructure.newBuilder().build();
|
||||
|
||||
private SessionState sessionState = new SessionState(SessionStructure.newBuilder().build());
|
||||
private List<SessionState> previousStates = new LinkedList<SessionState>();
|
||||
|
||||
public SessionRecordV2(Context context, MasterSecret masterSecret, RecipientDevice peer) {
|
||||
this(context, masterSecret, peer.getRecipientId(), peer.getDeviceId());
|
||||
@@ -80,6 +68,15 @@ public class SessionRecordV2 extends Record {
|
||||
return recipientId + (deviceId == RecipientDevice.DEFAULT_DEVICE_ID ? "" : "." + deviceId);
|
||||
}
|
||||
|
||||
public SessionState getSessionState() {
|
||||
return sessionState;
|
||||
}
|
||||
|
||||
|
||||
public List<SessionState> getPreviousSessions() {
|
||||
return previousStates;
|
||||
}
|
||||
|
||||
public static List<Integer> getSessionSubDevices(Context context, CanonicalRecipient recipient) {
|
||||
List<Integer> results = new LinkedList<Integer>();
|
||||
File parent = getParentDirectory(context, SESSIONS_DIRECTORY_V2);
|
||||
@@ -129,404 +126,49 @@ public class SessionRecordV2 extends Record {
|
||||
long recipientId, int deviceId)
|
||||
{
|
||||
return hasRecord(context, SESSIONS_DIRECTORY_V2, getRecordName(recipientId, deviceId)) &&
|
||||
new SessionRecordV2(context, masterSecret, recipientId, deviceId).hasSenderChain();
|
||||
new SessionRecordV2(context, masterSecret, recipientId, deviceId).sessionState.hasSenderChain();
|
||||
}
|
||||
|
||||
public static boolean needsRefresh(Context context, MasterSecret masterSecret,
|
||||
RecipientDevice recipient)
|
||||
{
|
||||
return new SessionRecordV2(context, masterSecret,
|
||||
recipient.getRecipientId(),
|
||||
recipient.getDeviceId()).getSessionState()
|
||||
.getNeedsRefresh();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.sessionStructure = StorageProtos.SessionStructure.newBuilder().build();
|
||||
this.sessionState = new SessionState(SessionStructure.newBuilder().build());
|
||||
this.previousStates = new LinkedList<SessionState>();
|
||||
}
|
||||
|
||||
public void setSessionVersion(int version) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setSessionVersion(version)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getSessionVersion() {
|
||||
return this.sessionStructure.getSessionVersion();
|
||||
}
|
||||
|
||||
public void setRemoteIdentityKey(IdentityKey identityKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRemoteIdentityPublic(ByteString.copyFrom(identityKey.serialize()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setLocalIdentityKey(IdentityKey identityKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setLocalIdentityPublic(ByteString.copyFrom(identityKey.serialize()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public IdentityKey getRemoteIdentityKey() {
|
||||
try {
|
||||
if (!this.sessionStructure.hasRemoteIdentityPublic()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new IdentityKey(this.sessionStructure.getRemoteIdentityPublic().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("SessionRecordV2", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IdentityKey getLocalIdentityKey() {
|
||||
try {
|
||||
return new IdentityKey(this.sessionStructure.getLocalIdentityPublic().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getPreviousCounter() {
|
||||
return sessionStructure.getPreviousCounter();
|
||||
}
|
||||
|
||||
public void setPreviousCounter(int previousCounter) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPreviousCounter(previousCounter)
|
||||
.build();
|
||||
}
|
||||
|
||||
public RootKey getRootKey() {
|
||||
return new RootKey(this.sessionStructure.getRootKey().toByteArray());
|
||||
}
|
||||
|
||||
public void setRootKey(RootKey rootKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRootKey(ByteString.copyFrom(rootKey.getKeyBytes()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public ECPublicKey getSenderEphemeral() {
|
||||
try {
|
||||
return Curve.decodePoint(sessionStructure.getSenderChain().getSenderEphemeral().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ECKeyPair getSenderEphemeralPair() {
|
||||
ECPublicKey publicKey = getSenderEphemeral();
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getSenderChain()
|
||||
.getSenderEphemeralPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public boolean hasReceiverChain(ECPublicKey senderEphemeral) {
|
||||
return getReceiverChain(senderEphemeral) != null;
|
||||
}
|
||||
|
||||
public boolean hasSenderChain() {
|
||||
return sessionStructure.hasSenderChain();
|
||||
}
|
||||
|
||||
private Pair<Chain,Integer> getReceiverChain(ECPublicKey senderEphemeral) {
|
||||
List<Chain> receiverChains = sessionStructure.getReceiverChainsList();
|
||||
int index = 0;
|
||||
|
||||
for (Chain receiverChain : receiverChains) {
|
||||
try {
|
||||
ECPublicKey chainSenderEphemeral = Curve.decodePoint(receiverChain.getSenderEphemeral().toByteArray(), 0);
|
||||
|
||||
if (chainSenderEphemeral.equals(senderEphemeral)) {
|
||||
return new Pair<Chain,Integer>(receiverChain,index);
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("SessionRecordV2", e);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ChainKey getReceiverChainKey(ECPublicKey senderEphemeral) {
|
||||
Pair<Chain,Integer> receiverChainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain receiverChain = receiverChainAndIndex.first;
|
||||
|
||||
if (receiverChain == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new ChainKey(receiverChain.getChainKey().getKey().toByteArray(),
|
||||
receiverChain.getChainKey().getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public void addReceiverChain(ECPublicKey senderEphemeral, ChainKey chainKey) {
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain chain = Chain.newBuilder()
|
||||
.setChainKey(chainKeyStructure)
|
||||
.setSenderEphemeral(ByteString.copyFrom(senderEphemeral.serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().addReceiverChains(chain).build();
|
||||
|
||||
if (this.sessionStructure.getReceiverChainsList().size() > 5) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.removeReceiverChains(0)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSenderChain(ECKeyPair senderEphemeralPair, ChainKey chainKey) {
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain senderChain = Chain.newBuilder()
|
||||
.setSenderEphemeral(ByteString.copyFrom(senderEphemeralPair.getPublicKey().serialize()))
|
||||
.setSenderEphemeralPrivate(ByteString.copyFrom(senderEphemeralPair.getPrivateKey().serialize()))
|
||||
.setChainKey(chainKeyStructure)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(senderChain).build();
|
||||
}
|
||||
|
||||
public ChainKey getSenderChainKey() {
|
||||
Chain.ChainKey chainKeyStructure = sessionStructure.getSenderChain().getChainKey();
|
||||
return new ChainKey(chainKeyStructure.getKey().toByteArray(), chainKeyStructure.getIndex());
|
||||
}
|
||||
|
||||
|
||||
public void setSenderChainKey(ChainKey nextChainKey) {
|
||||
Chain.ChainKey chainKey = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(nextChainKey.getKey()))
|
||||
.setIndex(nextChainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain chain = sessionStructure.getSenderChain().toBuilder()
|
||||
.setChainKey(chainKey).build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(chain).build();
|
||||
}
|
||||
|
||||
public boolean hasMessageKeys(ECPublicKey senderEphemeral, int counter) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
if (chain == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Chain.MessageKey> messageKeyList = chain.getMessageKeysList();
|
||||
|
||||
for (Chain.MessageKey messageKey : messageKeyList) {
|
||||
if (messageKey.getIndex() == counter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public MessageKeys removeMessageKeys(ECPublicKey senderEphemeral, int counter) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
if (chain == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Chain.MessageKey> messageKeyList = chain.getMessageKeysList();
|
||||
Iterator<Chain.MessageKey> messageKeyIterator = messageKeyList.iterator();
|
||||
MessageKeys result = null;
|
||||
|
||||
while (messageKeyIterator.hasNext()) {
|
||||
Chain.MessageKey messageKey = messageKeyIterator.next();
|
||||
|
||||
if (messageKey.getIndex() == counter) {
|
||||
result = new MessageKeys(new SecretKeySpec(messageKey.getCipherKey().toByteArray(), "AES"),
|
||||
new SecretKeySpec(messageKey.getMacKey().toByteArray(), "HmacSHA256"),
|
||||
messageKey.getIndex());
|
||||
|
||||
messageKeyIterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Chain updatedChain = chain.toBuilder().clearMessageKeys()
|
||||
.addAllMessageKeys(messageKeyList)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMessageKeys(ECPublicKey senderEphemeral, MessageKeys messageKeys) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
Chain.MessageKey messageKeyStructure = Chain.MessageKey.newBuilder()
|
||||
.setCipherKey(ByteString.copyFrom(messageKeys.getCipherKey().getEncoded()))
|
||||
.setMacKey(ByteString.copyFrom(messageKeys.getMacKey().getEncoded()))
|
||||
.setIndex(messageKeys.getCounter())
|
||||
.build();
|
||||
|
||||
Chain updatedChain = chain.toBuilder()
|
||||
.addMessageKeys(messageKeyStructure)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setReceiverChainKey(ECPublicKey senderEphemeral, ChainKey chainKey) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain updatedChain = chain.toBuilder().setChainKey(chainKeyStructure).build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setPendingKeyExchange(int sequence,
|
||||
ECKeyPair ourBaseKey,
|
||||
ECKeyPair ourEphemeralKey,
|
||||
IdentityKeyPair ourIdentityKey)
|
||||
{
|
||||
PendingKeyExchange structure =
|
||||
PendingKeyExchange.newBuilder()
|
||||
.setSequence(sequence)
|
||||
.setLocalBaseKey(ByteString.copyFrom(ourBaseKey.getPublicKey().serialize()))
|
||||
.setLocalBaseKeyPrivate(ByteString.copyFrom(ourBaseKey.getPrivateKey().serialize()))
|
||||
.setLocalEphemeralKey(ByteString.copyFrom(ourEphemeralKey.getPublicKey().serialize()))
|
||||
.setLocalEphemeralKeyPrivate(ByteString.copyFrom(ourEphemeralKey.getPrivateKey().serialize()))
|
||||
.setLocalIdentityKey(ByteString.copyFrom(ourIdentityKey.getPublicKey().serialize()))
|
||||
.setLocalIdentityKeyPrivate(ByteString.copyFrom(ourIdentityKey.getPrivateKey().serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPendingKeyExchange(structure)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getPendingKeyExchangeSequence() {
|
||||
return sessionStructure.getPendingKeyExchange().getSequence();
|
||||
}
|
||||
|
||||
public ECKeyPair getPendingKeyExchangeBaseKey() throws InvalidKeyException {
|
||||
ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalBaseKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalBaseKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public ECKeyPair getPendingKeyExchangeEphemeralKey() throws InvalidKeyException {
|
||||
ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalEphemeralKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalEphemeralKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public IdentityKeyPair getPendingKeyExchangeIdentityKey() throws InvalidKeyException {
|
||||
IdentityKey publicKey = new IdentityKey(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalIdentityKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getPublicKey().getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalIdentityKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new IdentityKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public boolean hasPendingKeyExchange() {
|
||||
return sessionStructure.hasPendingKeyExchange();
|
||||
}
|
||||
|
||||
public void setPendingPreKey(int preKeyId, ECPublicKey baseKey) {
|
||||
PendingPreKey pending = PendingPreKey.newBuilder()
|
||||
.setPreKeyId(preKeyId)
|
||||
.setBaseKey(ByteString.copyFrom(baseKey.serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPendingPreKey(pending)
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean hasPendingPreKey() {
|
||||
return this.sessionStructure.hasPendingPreKey();
|
||||
}
|
||||
|
||||
public Pair<Integer, ECPublicKey> getPendingPreKey() {
|
||||
try {
|
||||
return new Pair<Integer, ECPublicKey>(sessionStructure.getPendingPreKey().getPreKeyId(),
|
||||
Curve.decodePoint(sessionStructure.getPendingPreKey()
|
||||
.getBaseKey()
|
||||
.toByteArray(), 0));
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearPendingPreKey() {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.clearPendingPreKey()
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setRemoteRegistrationId(int registrationId) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRemoteRegistrationId(registrationId)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getRemoteRegistrationId() {
|
||||
return this.sessionStructure.getRemoteRegistrationId();
|
||||
}
|
||||
|
||||
public void setLocalRegistrationId(int registrationId) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setLocalRegistrationId(registrationId)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getLocalRegistrationId() {
|
||||
return this.sessionStructure.getLocalRegistrationId();
|
||||
public void archiveCurrentState() {
|
||||
this.previousStates.add(sessionState);
|
||||
this.sessionState = new SessionState(SessionStructure.newBuilder().build());
|
||||
}
|
||||
|
||||
public void save() {
|
||||
synchronized (FILE_LOCK) {
|
||||
try {
|
||||
List<SessionStructure> previousStructures = new LinkedList<SessionStructure>();
|
||||
|
||||
for (SessionState previousState : previousStates) {
|
||||
previousStructures.add(previousState.getStructure());
|
||||
}
|
||||
|
||||
RecordStructure record = RecordStructure.newBuilder()
|
||||
.setCurrentSession(sessionState.getStructure())
|
||||
.addAllPreviousSessions(previousStructures)
|
||||
.build();
|
||||
|
||||
RandomAccessFile file = openRandomAccessFile();
|
||||
FileChannel out = file.getChannel();
|
||||
out.position(0);
|
||||
|
||||
MasterCipher cipher = new MasterCipher(masterSecret);
|
||||
writeInteger(CURRENT_VERSION, out);
|
||||
writeBlob(cipher.encryptBytes(sessionStructure.toByteArray()), out);
|
||||
writeBlob(cipher.encryptBytes(record.toByteArray()), out);
|
||||
|
||||
out.truncate(out.position());
|
||||
file.close();
|
||||
@@ -549,11 +191,26 @@ public class SessionRecordV2 extends Record {
|
||||
MasterCipher cipher = new MasterCipher(masterSecret);
|
||||
byte[] encryptedBlob = readBlob(in);
|
||||
|
||||
if (versionMarker == SINGLE_STATE_VERSION) {
|
||||
byte[] plaintextBytes = cipher.decryptBytes(encryptedBlob);
|
||||
SessionStructure sessionStructure = SessionStructure.parseFrom(plaintextBytes);
|
||||
this.sessionState = new SessionState(sessionStructure);
|
||||
} else if (versionMarker == ARCHIVE_STATES_VERSION) {
|
||||
byte[] plaintextBytes = cipher.decryptBytes(encryptedBlob);
|
||||
RecordStructure recordStructure = RecordStructure.parseFrom(plaintextBytes);
|
||||
|
||||
this.sessionStructure = StorageProtos.SessionStructure
|
||||
.parseFrom(cipher.decryptBytes(encryptedBlob));
|
||||
this.sessionState = new SessionState(recordStructure.getCurrentSession());
|
||||
this.previousStates = new LinkedList<SessionState>();
|
||||
|
||||
for (SessionStructure sessionStructure : recordStructure.getPreviousSessionsList()) {
|
||||
this.previousStates.add(new SessionState(sessionStructure));
|
||||
}
|
||||
} else {
|
||||
throw new AssertionError("Unknown version: " + versionMarker);
|
||||
}
|
||||
|
||||
in.close();
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w("SessionRecordV2", "No session information found.");
|
||||
// XXX
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
package org.whispersystems.textsecure.storage;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||
import org.whispersystems.textsecure.crypto.IdentityKeyPair;
|
||||
import org.whispersystems.textsecure.crypto.InvalidKeyException;
|
||||
import org.whispersystems.textsecure.crypto.ecc.Curve;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECKeyPair;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.ChainKey;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.MessageKeys;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.RootKey;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Chain;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingKeyExchange;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingPreKey;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import static org.whispersystems.textsecure.storage.StorageProtos.SessionStructure;
|
||||
|
||||
public class SessionState {
|
||||
|
||||
private SessionStructure sessionStructure;
|
||||
|
||||
public SessionState(SessionStructure sessionStructure) {
|
||||
this.sessionStructure = sessionStructure;
|
||||
}
|
||||
|
||||
public SessionStructure getStructure() {
|
||||
return sessionStructure;
|
||||
}
|
||||
|
||||
public void setNeedsRefresh(boolean needsRefresh) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setNeedsRefresh(needsRefresh)
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean getNeedsRefresh() {
|
||||
return this.sessionStructure.getNeedsRefresh();
|
||||
}
|
||||
|
||||
public void setSessionVersion(int version) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setSessionVersion(version)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getSessionVersion() {
|
||||
return this.sessionStructure.getSessionVersion();
|
||||
}
|
||||
|
||||
public void setRemoteIdentityKey(IdentityKey identityKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRemoteIdentityPublic(ByteString.copyFrom(identityKey.serialize()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setLocalIdentityKey(IdentityKey identityKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setLocalIdentityPublic(ByteString.copyFrom(identityKey.serialize()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public IdentityKey getRemoteIdentityKey() {
|
||||
try {
|
||||
if (!this.sessionStructure.hasRemoteIdentityPublic()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new IdentityKey(this.sessionStructure.getRemoteIdentityPublic().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("SessionRecordV2", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IdentityKey getLocalIdentityKey() {
|
||||
try {
|
||||
return new IdentityKey(this.sessionStructure.getLocalIdentityPublic().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getPreviousCounter() {
|
||||
return sessionStructure.getPreviousCounter();
|
||||
}
|
||||
|
||||
public void setPreviousCounter(int previousCounter) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPreviousCounter(previousCounter)
|
||||
.build();
|
||||
}
|
||||
|
||||
public RootKey getRootKey() {
|
||||
return new RootKey(this.sessionStructure.getRootKey().toByteArray());
|
||||
}
|
||||
|
||||
public void setRootKey(RootKey rootKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRootKey(ByteString.copyFrom(rootKey.getKeyBytes()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public ECPublicKey getSenderEphemeral() {
|
||||
try {
|
||||
return Curve.decodePoint(sessionStructure.getSenderChain().getSenderEphemeral().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ECKeyPair getSenderEphemeralPair() {
|
||||
ECPublicKey publicKey = getSenderEphemeral();
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getSenderChain()
|
||||
.getSenderEphemeralPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public boolean hasReceiverChain(ECPublicKey senderEphemeral) {
|
||||
return getReceiverChain(senderEphemeral) != null;
|
||||
}
|
||||
|
||||
public boolean hasSenderChain() {
|
||||
return sessionStructure.hasSenderChain();
|
||||
}
|
||||
|
||||
private Pair<Chain,Integer> getReceiverChain(ECPublicKey senderEphemeral) {
|
||||
List<Chain> receiverChains = sessionStructure.getReceiverChainsList();
|
||||
int index = 0;
|
||||
|
||||
for (Chain receiverChain : receiverChains) {
|
||||
try {
|
||||
ECPublicKey chainSenderEphemeral = Curve.decodePoint(receiverChain.getSenderEphemeral().toByteArray(), 0);
|
||||
|
||||
if (chainSenderEphemeral.equals(senderEphemeral)) {
|
||||
return new Pair<Chain,Integer>(receiverChain,index);
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("SessionRecordV2", e);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ChainKey getReceiverChainKey(ECPublicKey senderEphemeral) {
|
||||
Pair<Chain,Integer> receiverChainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain receiverChain = receiverChainAndIndex.first;
|
||||
|
||||
if (receiverChain == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new ChainKey(receiverChain.getChainKey().getKey().toByteArray(),
|
||||
receiverChain.getChainKey().getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public void addReceiverChain(ECPublicKey senderEphemeral, ChainKey chainKey) {
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain chain = Chain.newBuilder()
|
||||
.setChainKey(chainKeyStructure)
|
||||
.setSenderEphemeral(ByteString.copyFrom(senderEphemeral.serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().addReceiverChains(chain).build();
|
||||
|
||||
if (this.sessionStructure.getReceiverChainsList().size() > 5) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.removeReceiverChains(0)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSenderChain(ECKeyPair senderEphemeralPair, ChainKey chainKey) {
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain senderChain = Chain.newBuilder()
|
||||
.setSenderEphemeral(ByteString.copyFrom(senderEphemeralPair.getPublicKey().serialize()))
|
||||
.setSenderEphemeralPrivate(ByteString.copyFrom(senderEphemeralPair.getPrivateKey().serialize()))
|
||||
.setChainKey(chainKeyStructure)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(senderChain).build();
|
||||
}
|
||||
|
||||
public ChainKey getSenderChainKey() {
|
||||
Chain.ChainKey chainKeyStructure = sessionStructure.getSenderChain().getChainKey();
|
||||
return new ChainKey(chainKeyStructure.getKey().toByteArray(), chainKeyStructure.getIndex());
|
||||
}
|
||||
|
||||
|
||||
public void setSenderChainKey(ChainKey nextChainKey) {
|
||||
Chain.ChainKey chainKey = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(nextChainKey.getKey()))
|
||||
.setIndex(nextChainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain chain = sessionStructure.getSenderChain().toBuilder()
|
||||
.setChainKey(chainKey).build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(chain).build();
|
||||
}
|
||||
|
||||
public boolean hasMessageKeys(ECPublicKey senderEphemeral, int counter) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
if (chain == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Chain.MessageKey> messageKeyList = chain.getMessageKeysList();
|
||||
|
||||
for (Chain.MessageKey messageKey : messageKeyList) {
|
||||
if (messageKey.getIndex() == counter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public MessageKeys removeMessageKeys(ECPublicKey senderEphemeral, int counter) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
if (chain == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Chain.MessageKey> messageKeyList = new LinkedList<Chain.MessageKey>(chain.getMessageKeysList());
|
||||
Iterator<Chain.MessageKey> messageKeyIterator = messageKeyList.iterator();
|
||||
MessageKeys result = null;
|
||||
|
||||
while (messageKeyIterator.hasNext()) {
|
||||
Chain.MessageKey messageKey = messageKeyIterator.next();
|
||||
|
||||
if (messageKey.getIndex() == counter) {
|
||||
result = new MessageKeys(new SecretKeySpec(messageKey.getCipherKey().toByteArray(), "AES"),
|
||||
new SecretKeySpec(messageKey.getMacKey().toByteArray(), "HmacSHA256"),
|
||||
messageKey.getIndex());
|
||||
|
||||
messageKeyIterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Chain updatedChain = chain.toBuilder().clearMessageKeys()
|
||||
.addAllMessageKeys(messageKeyList)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMessageKeys(ECPublicKey senderEphemeral, MessageKeys messageKeys) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
Chain.MessageKey messageKeyStructure = Chain.MessageKey.newBuilder()
|
||||
.setCipherKey(ByteString.copyFrom(messageKeys.getCipherKey().getEncoded()))
|
||||
.setMacKey(ByteString.copyFrom(messageKeys.getMacKey().getEncoded()))
|
||||
.setIndex(messageKeys.getCounter())
|
||||
.build();
|
||||
|
||||
Chain updatedChain = chain.toBuilder()
|
||||
.addMessageKeys(messageKeyStructure)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setReceiverChainKey(ECPublicKey senderEphemeral, ChainKey chainKey) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain updatedChain = chain.toBuilder().setChainKey(chainKeyStructure).build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setPendingKeyExchange(int sequence,
|
||||
ECKeyPair ourBaseKey,
|
||||
ECKeyPair ourEphemeralKey,
|
||||
IdentityKeyPair ourIdentityKey)
|
||||
{
|
||||
PendingKeyExchange structure =
|
||||
PendingKeyExchange.newBuilder()
|
||||
.setSequence(sequence)
|
||||
.setLocalBaseKey(ByteString.copyFrom(ourBaseKey.getPublicKey().serialize()))
|
||||
.setLocalBaseKeyPrivate(ByteString.copyFrom(ourBaseKey.getPrivateKey().serialize()))
|
||||
.setLocalEphemeralKey(ByteString.copyFrom(ourEphemeralKey.getPublicKey().serialize()))
|
||||
.setLocalEphemeralKeyPrivate(ByteString.copyFrom(ourEphemeralKey.getPrivateKey().serialize()))
|
||||
.setLocalIdentityKey(ByteString.copyFrom(ourIdentityKey.getPublicKey().serialize()))
|
||||
.setLocalIdentityKeyPrivate(ByteString.copyFrom(ourIdentityKey.getPrivateKey().serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPendingKeyExchange(structure)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getPendingKeyExchangeSequence() {
|
||||
return sessionStructure.getPendingKeyExchange().getSequence();
|
||||
}
|
||||
|
||||
public ECKeyPair getPendingKeyExchangeBaseKey() throws InvalidKeyException {
|
||||
ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalBaseKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalBaseKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public ECKeyPair getPendingKeyExchangeEphemeralKey() throws InvalidKeyException {
|
||||
ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalEphemeralKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalEphemeralKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public IdentityKeyPair getPendingKeyExchangeIdentityKey() throws InvalidKeyException {
|
||||
IdentityKey publicKey = new IdentityKey(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalIdentityKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getPublicKey().getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalIdentityKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new IdentityKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public boolean hasPendingKeyExchange() {
|
||||
return sessionStructure.hasPendingKeyExchange();
|
||||
}
|
||||
|
||||
public void setPendingPreKey(int preKeyId, ECPublicKey baseKey) {
|
||||
PendingPreKey pending = PendingPreKey.newBuilder()
|
||||
.setPreKeyId(preKeyId)
|
||||
.setBaseKey(ByteString.copyFrom(baseKey.serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPendingPreKey(pending)
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean hasPendingPreKey() {
|
||||
return this.sessionStructure.hasPendingPreKey();
|
||||
}
|
||||
|
||||
public Pair<Integer, ECPublicKey> getPendingPreKey() {
|
||||
try {
|
||||
return new Pair<Integer, ECPublicKey>(sessionStructure.getPendingPreKey().getPreKeyId(),
|
||||
Curve.decodePoint(sessionStructure.getPendingPreKey()
|
||||
.getBaseKey()
|
||||
.toByteArray(), 0));
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearPendingPreKey() {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.clearPendingPreKey()
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setRemoteRegistrationId(int registrationId) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRemoteRegistrationId(registrationId)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getRemoteRegistrationId() {
|
||||
return this.sessionStructure.getRemoteRegistrationId();
|
||||
}
|
||||
|
||||
public void setLocalRegistrationId(int registrationId) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setLocalRegistrationId(registrationId)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getLocalRegistrationId() {
|
||||
return this.sessionStructure.getLocalRegistrationId();
|
||||
}
|
||||
|
||||
public byte[] serialize() {
|
||||
return sessionStructure.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,10 @@ public final class StorageProtos {
|
||||
// optional uint32 localRegistrationId = 11;
|
||||
boolean hasLocalRegistrationId();
|
||||
int getLocalRegistrationId();
|
||||
|
||||
// optional bool needsRefresh = 12;
|
||||
boolean hasNeedsRefresh();
|
||||
boolean getNeedsRefresh();
|
||||
}
|
||||
public static final class SessionStructure extends
|
||||
com.google.protobuf.GeneratedMessage
|
||||
@@ -2992,6 +2996,16 @@ public final class StorageProtos {
|
||||
return localRegistrationId_;
|
||||
}
|
||||
|
||||
// optional bool needsRefresh = 12;
|
||||
public static final int NEEDSREFRESH_FIELD_NUMBER = 12;
|
||||
private boolean needsRefresh_;
|
||||
public boolean hasNeedsRefresh() {
|
||||
return ((bitField0_ & 0x00000400) == 0x00000400);
|
||||
}
|
||||
public boolean getNeedsRefresh() {
|
||||
return needsRefresh_;
|
||||
}
|
||||
|
||||
private void initFields() {
|
||||
sessionVersion_ = 0;
|
||||
localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY;
|
||||
@@ -3004,6 +3018,7 @@ public final class StorageProtos {
|
||||
pendingPreKey_ = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance();
|
||||
remoteRegistrationId_ = 0;
|
||||
localRegistrationId_ = 0;
|
||||
needsRefresh_ = false;
|
||||
}
|
||||
private byte memoizedIsInitialized = -1;
|
||||
public final boolean isInitialized() {
|
||||
@@ -3050,6 +3065,9 @@ public final class StorageProtos {
|
||||
if (((bitField0_ & 0x00000200) == 0x00000200)) {
|
||||
output.writeUInt32(11, localRegistrationId_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000400) == 0x00000400)) {
|
||||
output.writeBool(12, needsRefresh_);
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
@@ -3103,6 +3121,10 @@ public final class StorageProtos {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeUInt32Size(11, localRegistrationId_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000400) == 0x00000400)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBoolSize(12, needsRefresh_);
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
@@ -3269,6 +3291,8 @@ public final class StorageProtos {
|
||||
bitField0_ = (bitField0_ & ~0x00000200);
|
||||
localRegistrationId_ = 0;
|
||||
bitField0_ = (bitField0_ & ~0x00000400);
|
||||
needsRefresh_ = false;
|
||||
bitField0_ = (bitField0_ & ~0x00000800);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -3368,6 +3392,10 @@ public final class StorageProtos {
|
||||
to_bitField0_ |= 0x00000200;
|
||||
}
|
||||
result.localRegistrationId_ = localRegistrationId_;
|
||||
if (((from_bitField0_ & 0x00000800) == 0x00000800)) {
|
||||
to_bitField0_ |= 0x00000400;
|
||||
}
|
||||
result.needsRefresh_ = needsRefresh_;
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
@@ -3440,6 +3468,9 @@ public final class StorageProtos {
|
||||
if (other.hasLocalRegistrationId()) {
|
||||
setLocalRegistrationId(other.getLocalRegistrationId());
|
||||
}
|
||||
if (other.hasNeedsRefresh()) {
|
||||
setNeedsRefresh(other.getNeedsRefresh());
|
||||
}
|
||||
this.mergeUnknownFields(other.getUnknownFields());
|
||||
return this;
|
||||
}
|
||||
@@ -3539,6 +3570,11 @@ public final class StorageProtos {
|
||||
localRegistrationId_ = input.readUInt32();
|
||||
break;
|
||||
}
|
||||
case 96: {
|
||||
bitField0_ |= 0x00000800;
|
||||
needsRefresh_ = input.readBool();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4157,6 +4193,27 @@ public final class StorageProtos {
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional bool needsRefresh = 12;
|
||||
private boolean needsRefresh_ ;
|
||||
public boolean hasNeedsRefresh() {
|
||||
return ((bitField0_ & 0x00000800) == 0x00000800);
|
||||
}
|
||||
public boolean getNeedsRefresh() {
|
||||
return needsRefresh_;
|
||||
}
|
||||
public Builder setNeedsRefresh(boolean value) {
|
||||
bitField0_ |= 0x00000800;
|
||||
needsRefresh_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
public Builder clearNeedsRefresh() {
|
||||
bitField0_ = (bitField0_ & ~0x00000800);
|
||||
needsRefresh_ = false;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:textsecure.SessionStructure)
|
||||
}
|
||||
|
||||
@@ -4168,6 +4225,703 @@ public final class StorageProtos {
|
||||
// @@protoc_insertion_point(class_scope:textsecure.SessionStructure)
|
||||
}
|
||||
|
||||
public interface RecordStructureOrBuilder
|
||||
extends com.google.protobuf.MessageOrBuilder {
|
||||
|
||||
// optional .textsecure.SessionStructure currentSession = 1;
|
||||
boolean hasCurrentSession();
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getCurrentSession();
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder();
|
||||
|
||||
// repeated .textsecure.SessionStructure previousSessions = 2;
|
||||
java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure>
|
||||
getPreviousSessionsList();
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getPreviousSessions(int index);
|
||||
int getPreviousSessionsCount();
|
||||
java.util.List<? extends org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>
|
||||
getPreviousSessionsOrBuilderList();
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder(
|
||||
int index);
|
||||
}
|
||||
public static final class RecordStructure extends
|
||||
com.google.protobuf.GeneratedMessage
|
||||
implements RecordStructureOrBuilder {
|
||||
// Use RecordStructure.newBuilder() to construct.
|
||||
private RecordStructure(Builder builder) {
|
||||
super(builder);
|
||||
}
|
||||
private RecordStructure(boolean noInit) {}
|
||||
|
||||
private static final RecordStructure defaultInstance;
|
||||
public static RecordStructure getDefaultInstance() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
public RecordStructure getDefaultInstanceForType() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
public static final com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptor() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.internal_static_textsecure_RecordStructure_descriptor;
|
||||
}
|
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internalGetFieldAccessorTable() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.internal_static_textsecure_RecordStructure_fieldAccessorTable;
|
||||
}
|
||||
|
||||
private int bitField0_;
|
||||
// optional .textsecure.SessionStructure currentSession = 1;
|
||||
public static final int CURRENTSESSION_FIELD_NUMBER = 1;
|
||||
private org.whispersystems.textsecure.storage.StorageProtos.SessionStructure currentSession_;
|
||||
public boolean hasCurrentSession() {
|
||||
return ((bitField0_ & 0x00000001) == 0x00000001);
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getCurrentSession() {
|
||||
return currentSession_;
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder() {
|
||||
return currentSession_;
|
||||
}
|
||||
|
||||
// repeated .textsecure.SessionStructure previousSessions = 2;
|
||||
public static final int PREVIOUSSESSIONS_FIELD_NUMBER = 2;
|
||||
private java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure> previousSessions_;
|
||||
public java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure> getPreviousSessionsList() {
|
||||
return previousSessions_;
|
||||
}
|
||||
public java.util.List<? extends org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>
|
||||
getPreviousSessionsOrBuilderList() {
|
||||
return previousSessions_;
|
||||
}
|
||||
public int getPreviousSessionsCount() {
|
||||
return previousSessions_.size();
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getPreviousSessions(int index) {
|
||||
return previousSessions_.get(index);
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder(
|
||||
int index) {
|
||||
return previousSessions_.get(index);
|
||||
}
|
||||
|
||||
private void initFields() {
|
||||
currentSession_ = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance();
|
||||
previousSessions_ = java.util.Collections.emptyList();
|
||||
}
|
||||
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.writeMessage(1, currentSession_);
|
||||
}
|
||||
for (int i = 0; i < previousSessions_.size(); i++) {
|
||||
output.writeMessage(2, previousSessions_.get(i));
|
||||
}
|
||||
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
|
||||
.computeMessageSize(1, currentSession_);
|
||||
}
|
||||
for (int i = 0; i < previousSessions_.size(); i++) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeMessageSize(2, previousSessions_.get(i));
|
||||
}
|
||||
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.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(
|
||||
com.google.protobuf.ByteString data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure 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.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(byte[] data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(
|
||||
byte[] data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input).buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure 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.whispersystems.textsecure.storage.StorageProtos.RecordStructure 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.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(
|
||||
com.google.protobuf.CodedInputStream input)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input).buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure 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.whispersystems.textsecure.storage.StorageProtos.RecordStructure 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.whispersystems.textsecure.storage.StorageProtos.RecordStructureOrBuilder {
|
||||
public static final com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptor() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.internal_static_textsecure_RecordStructure_descriptor;
|
||||
}
|
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internalGetFieldAccessorTable() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.internal_static_textsecure_RecordStructure_fieldAccessorTable;
|
||||
}
|
||||
|
||||
// Construct using org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.newBuilder()
|
||||
private Builder() {
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
|
||||
private Builder(BuilderParent parent) {
|
||||
super(parent);
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
private void maybeForceBuilderInitialization() {
|
||||
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
|
||||
getCurrentSessionFieldBuilder();
|
||||
getPreviousSessionsFieldBuilder();
|
||||
}
|
||||
}
|
||||
private static Builder create() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder clear() {
|
||||
super.clear();
|
||||
if (currentSessionBuilder_ == null) {
|
||||
currentSession_ = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance();
|
||||
} else {
|
||||
currentSessionBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
previousSessions_ = java.util.Collections.emptyList();
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
} else {
|
||||
previousSessionsBuilder_.clear();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clone() {
|
||||
return create().mergeFrom(buildPartial());
|
||||
}
|
||||
|
||||
public com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptorForType() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.getDescriptor();
|
||||
}
|
||||
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.RecordStructure getDefaultInstanceForType() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.getDefaultInstance();
|
||||
}
|
||||
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.RecordStructure build() {
|
||||
org.whispersystems.textsecure.storage.StorageProtos.RecordStructure result = buildPartial();
|
||||
if (!result.isInitialized()) {
|
||||
throw newUninitializedMessageException(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private org.whispersystems.textsecure.storage.StorageProtos.RecordStructure buildParsed()
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
org.whispersystems.textsecure.storage.StorageProtos.RecordStructure result = buildPartial();
|
||||
if (!result.isInitialized()) {
|
||||
throw newUninitializedMessageException(
|
||||
result).asInvalidProtocolBufferException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.RecordStructure buildPartial() {
|
||||
org.whispersystems.textsecure.storage.StorageProtos.RecordStructure result = new org.whispersystems.textsecure.storage.StorageProtos.RecordStructure(this);
|
||||
int from_bitField0_ = bitField0_;
|
||||
int to_bitField0_ = 0;
|
||||
if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
to_bitField0_ |= 0x00000001;
|
||||
}
|
||||
if (currentSessionBuilder_ == null) {
|
||||
result.currentSession_ = currentSession_;
|
||||
} else {
|
||||
result.currentSession_ = currentSessionBuilder_.build();
|
||||
}
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
if (((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
previousSessions_ = java.util.Collections.unmodifiableList(previousSessions_);
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
}
|
||||
result.previousSessions_ = previousSessions_;
|
||||
} else {
|
||||
result.previousSessions_ = previousSessionsBuilder_.build();
|
||||
}
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(com.google.protobuf.Message other) {
|
||||
if (other instanceof org.whispersystems.textsecure.storage.StorageProtos.RecordStructure) {
|
||||
return mergeFrom((org.whispersystems.textsecure.storage.StorageProtos.RecordStructure)other);
|
||||
} else {
|
||||
super.mergeFrom(other);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public Builder mergeFrom(org.whispersystems.textsecure.storage.StorageProtos.RecordStructure other) {
|
||||
if (other == org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.getDefaultInstance()) return this;
|
||||
if (other.hasCurrentSession()) {
|
||||
mergeCurrentSession(other.getCurrentSession());
|
||||
}
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
if (!other.previousSessions_.isEmpty()) {
|
||||
if (previousSessions_.isEmpty()) {
|
||||
previousSessions_ = other.previousSessions_;
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
} else {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.addAll(other.previousSessions_);
|
||||
}
|
||||
onChanged();
|
||||
}
|
||||
} else {
|
||||
if (!other.previousSessions_.isEmpty()) {
|
||||
if (previousSessionsBuilder_.isEmpty()) {
|
||||
previousSessionsBuilder_.dispose();
|
||||
previousSessionsBuilder_ = null;
|
||||
previousSessions_ = other.previousSessions_;
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
previousSessionsBuilder_ =
|
||||
com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
|
||||
getPreviousSessionsFieldBuilder() : null;
|
||||
} else {
|
||||
previousSessionsBuilder_.addAllMessages(other.previousSessions_);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 10: {
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder subBuilder = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.newBuilder();
|
||||
if (hasCurrentSession()) {
|
||||
subBuilder.mergeFrom(getCurrentSession());
|
||||
}
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
setCurrentSession(subBuilder.buildPartial());
|
||||
break;
|
||||
}
|
||||
case 18: {
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder subBuilder = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.newBuilder();
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
addPreviousSessions(subBuilder.buildPartial());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int bitField0_;
|
||||
|
||||
// optional .textsecure.SessionStructure currentSession = 1;
|
||||
private org.whispersystems.textsecure.storage.StorageProtos.SessionStructure currentSession_ = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance();
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder> currentSessionBuilder_;
|
||||
public boolean hasCurrentSession() {
|
||||
return ((bitField0_ & 0x00000001) == 0x00000001);
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getCurrentSession() {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
return currentSession_;
|
||||
} else {
|
||||
return currentSessionBuilder_.getMessage();
|
||||
}
|
||||
}
|
||||
public Builder setCurrentSession(org.whispersystems.textsecure.storage.StorageProtos.SessionStructure value) {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
currentSession_ = value;
|
||||
onChanged();
|
||||
} else {
|
||||
currentSessionBuilder_.setMessage(value);
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
return this;
|
||||
}
|
||||
public Builder setCurrentSession(
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder builderForValue) {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
currentSession_ = builderForValue.build();
|
||||
onChanged();
|
||||
} else {
|
||||
currentSessionBuilder_.setMessage(builderForValue.build());
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
return this;
|
||||
}
|
||||
public Builder mergeCurrentSession(org.whispersystems.textsecure.storage.StorageProtos.SessionStructure value) {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
if (((bitField0_ & 0x00000001) == 0x00000001) &&
|
||||
currentSession_ != org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance()) {
|
||||
currentSession_ =
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.newBuilder(currentSession_).mergeFrom(value).buildPartial();
|
||||
} else {
|
||||
currentSession_ = value;
|
||||
}
|
||||
onChanged();
|
||||
} else {
|
||||
currentSessionBuilder_.mergeFrom(value);
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
return this;
|
||||
}
|
||||
public Builder clearCurrentSession() {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
currentSession_ = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance();
|
||||
onChanged();
|
||||
} else {
|
||||
currentSessionBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
return this;
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder getCurrentSessionBuilder() {
|
||||
bitField0_ |= 0x00000001;
|
||||
onChanged();
|
||||
return getCurrentSessionFieldBuilder().getBuilder();
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder() {
|
||||
if (currentSessionBuilder_ != null) {
|
||||
return currentSessionBuilder_.getMessageOrBuilder();
|
||||
} else {
|
||||
return currentSession_;
|
||||
}
|
||||
}
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>
|
||||
getCurrentSessionFieldBuilder() {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
currentSessionBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>(
|
||||
currentSession_,
|
||||
getParentForChildren(),
|
||||
isClean());
|
||||
currentSession_ = null;
|
||||
}
|
||||
return currentSessionBuilder_;
|
||||
}
|
||||
|
||||
// repeated .textsecure.SessionStructure previousSessions = 2;
|
||||
private java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure> previousSessions_ =
|
||||
java.util.Collections.emptyList();
|
||||
private void ensurePreviousSessionsIsMutable() {
|
||||
if (!((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
previousSessions_ = new java.util.ArrayList<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure>(previousSessions_);
|
||||
bitField0_ |= 0x00000002;
|
||||
}
|
||||
}
|
||||
|
||||
private com.google.protobuf.RepeatedFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder> previousSessionsBuilder_;
|
||||
|
||||
public java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure> getPreviousSessionsList() {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
return java.util.Collections.unmodifiableList(previousSessions_);
|
||||
} else {
|
||||
return previousSessionsBuilder_.getMessageList();
|
||||
}
|
||||
}
|
||||
public int getPreviousSessionsCount() {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
return previousSessions_.size();
|
||||
} else {
|
||||
return previousSessionsBuilder_.getCount();
|
||||
}
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getPreviousSessions(int index) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
return previousSessions_.get(index);
|
||||
} else {
|
||||
return previousSessionsBuilder_.getMessage(index);
|
||||
}
|
||||
}
|
||||
public Builder setPreviousSessions(
|
||||
int index, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure value) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.set(index, value);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.setMessage(index, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder setPreviousSessions(
|
||||
int index, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder builderForValue) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.set(index, builderForValue.build());
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.setMessage(index, builderForValue.build());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder addPreviousSessions(org.whispersystems.textsecure.storage.StorageProtos.SessionStructure value) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.add(value);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.addMessage(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder addPreviousSessions(
|
||||
int index, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure value) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.add(index, value);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.addMessage(index, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder addPreviousSessions(
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder builderForValue) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.add(builderForValue.build());
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.addMessage(builderForValue.build());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder addPreviousSessions(
|
||||
int index, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder builderForValue) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.add(index, builderForValue.build());
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.addMessage(index, builderForValue.build());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder addAllPreviousSessions(
|
||||
java.lang.Iterable<? extends org.whispersystems.textsecure.storage.StorageProtos.SessionStructure> values) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
super.addAll(values, previousSessions_);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.addAllMessages(values);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder clearPreviousSessions() {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
previousSessions_ = java.util.Collections.emptyList();
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.clear();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder removePreviousSessions(int index) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.remove(index);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.remove(index);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder getPreviousSessionsBuilder(
|
||||
int index) {
|
||||
return getPreviousSessionsFieldBuilder().getBuilder(index);
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder(
|
||||
int index) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
return previousSessions_.get(index); } else {
|
||||
return previousSessionsBuilder_.getMessageOrBuilder(index);
|
||||
}
|
||||
}
|
||||
public java.util.List<? extends org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>
|
||||
getPreviousSessionsOrBuilderList() {
|
||||
if (previousSessionsBuilder_ != null) {
|
||||
return previousSessionsBuilder_.getMessageOrBuilderList();
|
||||
} else {
|
||||
return java.util.Collections.unmodifiableList(previousSessions_);
|
||||
}
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder addPreviousSessionsBuilder() {
|
||||
return getPreviousSessionsFieldBuilder().addBuilder(
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance());
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder addPreviousSessionsBuilder(
|
||||
int index) {
|
||||
return getPreviousSessionsFieldBuilder().addBuilder(
|
||||
index, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance());
|
||||
}
|
||||
public java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder>
|
||||
getPreviousSessionsBuilderList() {
|
||||
return getPreviousSessionsFieldBuilder().getBuilderList();
|
||||
}
|
||||
private com.google.protobuf.RepeatedFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>
|
||||
getPreviousSessionsFieldBuilder() {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
previousSessionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>(
|
||||
previousSessions_,
|
||||
((bitField0_ & 0x00000002) == 0x00000002),
|
||||
getParentForChildren(),
|
||||
isClean());
|
||||
previousSessions_ = null;
|
||||
}
|
||||
return previousSessionsBuilder_;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:textsecure.RecordStructure)
|
||||
}
|
||||
|
||||
static {
|
||||
defaultInstance = new RecordStructure(true);
|
||||
defaultInstance.initFields();
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(class_scope:textsecure.RecordStructure)
|
||||
}
|
||||
|
||||
public interface PreKeyRecordStructureOrBuilder
|
||||
extends com.google.protobuf.MessageOrBuilder {
|
||||
|
||||
@@ -4656,6 +5410,11 @@ public final class StorageProtos {
|
||||
private static
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable;
|
||||
private static com.google.protobuf.Descriptors.Descriptor
|
||||
internal_static_textsecure_RecordStructure_descriptor;
|
||||
private static
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internal_static_textsecure_RecordStructure_fieldAccessorTable;
|
||||
private static com.google.protobuf.Descriptors.Descriptor
|
||||
internal_static_textsecure_PreKeyRecordStructure_descriptor;
|
||||
private static
|
||||
@@ -4671,7 +5430,7 @@ public final class StorageProtos {
|
||||
static {
|
||||
java.lang.String[] descriptorData = {
|
||||
"\n\032LocalStorageProtocol.proto\022\ntextsecure" +
|
||||
"\"\205\010\n\020SessionStructure\022\026\n\016sessionVersion\030" +
|
||||
"\"\233\010\n\020SessionStructure\022\026\n\016sessionVersion\030" +
|
||||
"\001 \001(\r\022\033\n\023localIdentityPublic\030\002 \001(\014\022\034\n\024re" +
|
||||
"moteIdentityPublic\030\003 \001(\014\022\017\n\007rootKey\030\004 \001(" +
|
||||
"\014\022\027\n\017previousCounter\030\005 \001(\r\0227\n\013senderChai" +
|
||||
@@ -4682,25 +5441,28 @@ public final class StorageProtos {
|
||||
"e.PendingKeyExchange\022A\n\rpendingPreKey\030\t ",
|
||||
"\001(\0132*.textsecure.SessionStructure.Pendin" +
|
||||
"gPreKey\022\034\n\024remoteRegistrationId\030\n \001(\r\022\033\n" +
|
||||
"\023localRegistrationId\030\013 \001(\r\032\253\002\n\005Chain\022\027\n\017" +
|
||||
"senderEphemeral\030\001 \001(\014\022\036\n\026senderEphemeral" +
|
||||
"Private\030\002 \001(\014\022=\n\010chainKey\030\003 \001(\0132+.textse" +
|
||||
"cure.SessionStructure.Chain.ChainKey\022B\n\013" +
|
||||
"messageKeys\030\004 \003(\0132-.textsecure.SessionSt" +
|
||||
"ructure.Chain.MessageKey\032&\n\010ChainKey\022\r\n\005" +
|
||||
"index\030\001 \001(\r\022\013\n\003key\030\002 \001(\014\032>\n\nMessageKey\022\r" +
|
||||
"\n\005index\030\001 \001(\r\022\021\n\tcipherKey\030\002 \001(\014\022\016\n\006macK",
|
||||
"ey\030\003 \001(\014\032\321\001\n\022PendingKeyExchange\022\020\n\010seque" +
|
||||
"nce\030\001 \001(\r\022\024\n\014localBaseKey\030\002 \001(\014\022\033\n\023local" +
|
||||
"BaseKeyPrivate\030\003 \001(\014\022\031\n\021localEphemeralKe" +
|
||||
"y\030\004 \001(\014\022 \n\030localEphemeralKeyPrivate\030\005 \001(" +
|
||||
"\014\022\030\n\020localIdentityKey\030\007 \001(\014\022\037\n\027localIden" +
|
||||
"tityKeyPrivate\030\010 \001(\014\0322\n\rPendingPreKey\022\020\n" +
|
||||
"\010preKeyId\030\001 \001(\r\022\017\n\007baseKey\030\002 \001(\014\"J\n\025PreK" +
|
||||
"eyRecordStructure\022\n\n\002id\030\001 \001(\r\022\021\n\tpublicK" +
|
||||
"ey\030\002 \001(\014\022\022\n\nprivateKey\030\003 \001(\014B6\n%org.whis" +
|
||||
"persystems.textsecure.storageB\rStoragePr",
|
||||
"otos"
|
||||
"\023localRegistrationId\030\013 \001(\r\022\024\n\014needsRefre" +
|
||||
"sh\030\014 \001(\010\032\253\002\n\005Chain\022\027\n\017senderEphemeral\030\001 " +
|
||||
"\001(\014\022\036\n\026senderEphemeralPrivate\030\002 \001(\014\022=\n\010c" +
|
||||
"hainKey\030\003 \001(\0132+.textsecure.SessionStruct" +
|
||||
"ure.Chain.ChainKey\022B\n\013messageKeys\030\004 \003(\0132" +
|
||||
"-.textsecure.SessionStructure.Chain.Mess" +
|
||||
"ageKey\032&\n\010ChainKey\022\r\n\005index\030\001 \001(\r\022\013\n\003key" +
|
||||
"\030\002 \001(\014\032>\n\nMessageKey\022\r\n\005index\030\001 \001(\r\022\021\n\tc",
|
||||
"ipherKey\030\002 \001(\014\022\016\n\006macKey\030\003 \001(\014\032\321\001\n\022Pendi" +
|
||||
"ngKeyExchange\022\020\n\010sequence\030\001 \001(\r\022\024\n\014local" +
|
||||
"BaseKey\030\002 \001(\014\022\033\n\023localBaseKeyPrivate\030\003 \001" +
|
||||
"(\014\022\031\n\021localEphemeralKey\030\004 \001(\014\022 \n\030localEp" +
|
||||
"hemeralKeyPrivate\030\005 \001(\014\022\030\n\020localIdentity" +
|
||||
"Key\030\007 \001(\014\022\037\n\027localIdentityKeyPrivate\030\010 \001" +
|
||||
"(\014\0322\n\rPendingPreKey\022\020\n\010preKeyId\030\001 \001(\r\022\017\n" +
|
||||
"\007baseKey\030\002 \001(\014\"\177\n\017RecordStructure\0224\n\016cur" +
|
||||
"rentSession\030\001 \001(\0132\034.textsecure.SessionSt" +
|
||||
"ructure\0226\n\020previousSessions\030\002 \003(\0132\034.text",
|
||||
"secure.SessionStructure\"J\n\025PreKeyRecordS" +
|
||||
"tructure\022\n\n\002id\030\001 \001(\r\022\021\n\tpublicKey\030\002 \001(\014\022" +
|
||||
"\022\n\nprivateKey\030\003 \001(\014B6\n%org.whispersystem" +
|
||||
"s.textsecure.storageB\rStorageProtos"
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||
@@ -4712,7 +5474,7 @@ public final class StorageProtos {
|
||||
internal_static_textsecure_SessionStructure_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_textsecure_SessionStructure_descriptor,
|
||||
new java.lang.String[] { "SessionVersion", "LocalIdentityPublic", "RemoteIdentityPublic", "RootKey", "PreviousCounter", "SenderChain", "ReceiverChains", "PendingKeyExchange", "PendingPreKey", "RemoteRegistrationId", "LocalRegistrationId", },
|
||||
new java.lang.String[] { "SessionVersion", "LocalIdentityPublic", "RemoteIdentityPublic", "RootKey", "PreviousCounter", "SenderChain", "ReceiverChains", "PendingKeyExchange", "PendingPreKey", "RemoteRegistrationId", "LocalRegistrationId", "NeedsRefresh", },
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.class,
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder.class);
|
||||
internal_static_textsecure_SessionStructure_Chain_descriptor =
|
||||
@@ -4755,8 +5517,16 @@ public final class StorageProtos {
|
||||
new java.lang.String[] { "PreKeyId", "BaseKey", },
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingPreKey.class,
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingPreKey.Builder.class);
|
||||
internal_static_textsecure_PreKeyRecordStructure_descriptor =
|
||||
internal_static_textsecure_RecordStructure_descriptor =
|
||||
getDescriptor().getMessageTypes().get(1);
|
||||
internal_static_textsecure_RecordStructure_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_textsecure_RecordStructure_descriptor,
|
||||
new java.lang.String[] { "CurrentSession", "PreviousSessions", },
|
||||
org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.class,
|
||||
org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.Builder.class);
|
||||
internal_static_textsecure_PreKeyRecordStructure_descriptor =
|
||||
getDescriptor().getMessageTypes().get(2);
|
||||
internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_textsecure_PreKeyRecordStructure_descriptor,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.whispersystems.textsecure.util;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.widget.EditText;
|
||||
@@ -88,14 +87,6 @@ public class Util {
|
||||
return value == null || value.length() == 0;
|
||||
}
|
||||
|
||||
public static void showAlertDialog(Context context, String title, String message) {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
|
||||
dialog.setTitle(title);
|
||||
dialog.setMessage(message);
|
||||
dialog.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
dialog.setPositiveButton(android.R.string.ok, null);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static int generateRegistrationId() {
|
||||
try {
|
||||
|
||||
17
res/anim/fade_scale_in.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<scale
|
||||
android:duration="150"
|
||||
android:fromXScale="0.85"
|
||||
android:fromYScale="0.85"
|
||||
android:toXScale="1.0"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%" />
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fromAlpha="0.6"
|
||||
android:toAlpha="1.0" />
|
||||
</set>
|
||||
17
res/anim/fade_scale_out.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<scale
|
||||
android:duration="150"
|
||||
android:fromXScale="1.0"
|
||||
android:fromYScale="1.0"
|
||||
android:toXScale="0.85"
|
||||
android:toYScale="0.85"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%" />
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.6" />
|
||||
</set>
|
||||
9
res/anim/slide_from_right.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<translate
|
||||
android:duration="150"
|
||||
android:fromXDelta="100%"
|
||||
android:toXDelta="0%" />
|
||||
</set>
|
||||
9
res/anim/slide_to_right.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<translate
|
||||
android:duration="150"
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="100%" />
|
||||
</set>
|
||||
BIN
res/drawable-hdpi-v11/ic_dialog_attach.png
Normal file
|
After Width: | Height: | Size: 648 B |
BIN
res/drawable-hdpi/ic_dialog_alert_holo_dark.png
Normal file
|
After Width: | Height: | Size: 928 B |
BIN
res/drawable-hdpi/ic_dialog_alert_holo_light.png
Normal file
|
After Width: | Height: | Size: 928 B |
BIN
res/drawable-hdpi/ic_dialog_attach.png
Normal file
|
After Width: | Height: | Size: 587 B |
BIN
res/drawable-hdpi/ic_dialog_info_holo_dark.png
Normal file
|
After Width: | Height: | Size: 646 B |
BIN
res/drawable-hdpi/ic_dialog_info_holo_light.png
Normal file
|
After Width: | Height: | Size: 683 B |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 406 B |
BIN
res/drawable-hdpi/ic_menu_refresh_holo_light.png
Normal file
|
After Width: | Height: | Size: 678 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 884 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 875 B |
BIN
res/drawable-hdpi/ic_send_push.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
res/drawable-hdpi/ic_send_sms_insecure.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-hdpi/ic_send_sms_insecure_dark.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
res/drawable-hdpi/ic_send_sms_secure.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-mdpi-v11/ic_dialog_attach.png
Normal file
|
After Width: | Height: | Size: 470 B |
BIN
res/drawable-mdpi/ic_dialog_alert_holo_dark.png
Normal file
|
After Width: | Height: | Size: 794 B |
BIN
res/drawable-mdpi/ic_dialog_alert_holo_light.png
Normal file
|
After Width: | Height: | Size: 794 B |
BIN
res/drawable-mdpi/ic_dialog_attach.png
Normal file
|
After Width: | Height: | Size: 415 B |
BIN
res/drawable-mdpi/ic_dialog_info_holo_dark.png
Normal file
|
After Width: | Height: | Size: 433 B |
BIN
res/drawable-mdpi/ic_dialog_info_holo_light.png
Normal file
|
After Width: | Height: | Size: 465 B |
|
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 293 B |
BIN
res/drawable-mdpi/ic_menu_refresh_holo_light.png
Normal file
|
After Width: | Height: | Size: 507 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 557 B |
|
Before Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 550 B |
BIN
res/drawable-mdpi/ic_send_push.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
res/drawable-mdpi/ic_send_sms_insecure.png
Normal file
|
After Width: | Height: | Size: 974 B |
BIN
res/drawable-mdpi/ic_send_sms_insecure_dark.png
Normal file
|
After Width: | Height: | Size: 858 B |
BIN
res/drawable-mdpi/ic_send_sms_secure.png
Normal file
|
After Width: | Height: | Size: 873 B |
BIN
res/drawable-xhdpi-v11/ic_dialog_attach.png
Normal file
|
After Width: | Height: | Size: 789 B |
BIN
res/drawable-xhdpi/ic_dialog_alert_holo_dark.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
res/drawable-xhdpi/ic_dialog_alert_holo_light.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
res/drawable-xhdpi/ic_dialog_attach.png
Normal file
|
After Width: | Height: | Size: 753 B |
BIN
res/drawable-xhdpi/ic_dialog_info_holo_dark.png
Normal file
|
After Width: | Height: | Size: 787 B |
BIN
res/drawable-xhdpi/ic_dialog_info_holo_light.png
Normal file
|
After Width: | Height: | Size: 860 B |
|
Before Width: | Height: | Size: 481 B After Width: | Height: | Size: 481 B |
BIN
res/drawable-xhdpi/ic_menu_refresh_holo_light.png
Normal file
|
After Width: | Height: | Size: 901 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
BIN
res/drawable-xhdpi/ic_send_push.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
res/drawable-xhdpi/ic_send_sms_insecure.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
res/drawable-xhdpi/ic_send_sms_insecure_dark.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
res/drawable-xhdpi/ic_send_sms_secure.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
res/drawable-xxhdpi-v11/ic_dialog_attach.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-xxhdpi/ic_dialog_alert_holo_dark.png
Normal file
|
After Width: | Height: | Size: 940 B |
BIN
res/drawable-xxhdpi/ic_dialog_alert_holo_light.png
Normal file
|
After Width: | Height: | Size: 996 B |
BIN
res/drawable-xxhdpi/ic_dialog_attach.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
res/drawable-xxhdpi/ic_dialog_info_holo_dark.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
res/drawable-xxhdpi/ic_dialog_info_holo_light.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-xxhdpi/ic_menu_refresh_holo_dark.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
res/drawable-xxhdpi/ic_menu_refresh_holo_light.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
res/drawable-xxhdpi/ic_send_push.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
res/drawable-xxhdpi/ic_send_sms_insecure.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
res/drawable-xxhdpi/ic_send_sms_insecure_dark.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
res/drawable-xxhdpi/ic_send_sms_secure.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
@@ -2,7 +2,7 @@
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="@color/card_background_active" />
|
||||
<solid android:color="@color/touch_highlight" />
|
||||
</shape>
|
||||
<bitmap android:src="@drawable/card" />
|
||||
</item>
|
||||
|
||||
10
res/drawable/contact_photo_background.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/touch_highlight" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:drawable="@android:color/transparent" />
|
||||
</selector>
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<solid android:color="@color/conversation_item_received_shadow_light" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:bottom="@dimen/conversation_item_drop_shadow_dist">
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#ffeeeeee" />
|
||||
<solid android:color="@color/conversation_item_received_background_light" />
|
||||
<!--stroke android:width="0.5dp" android:color="#03000000" /-->
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
|
||||