Move PreKey ids to be Mediums, generate in circular buffer.
This commit is contained in:
parent
edb89ee3e9
commit
d1969412fb
14 changed files with 153 additions and 43 deletions
|
@ -18,4 +18,8 @@ public class PreKeyPublic {
|
|||
return KeyUtil.encodePoint(publicKey.getQ());
|
||||
}
|
||||
|
||||
public ECPublicKeyParameters getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,16 +3,17 @@ package org.whispersystems.textsecure.crypto;
|
|||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import org.whispersystems.textsecure.encoded.PreKeyProtos.PreKeyEntity;
|
||||
import org.whispersystems.textsecure.push.PreKeyList;
|
||||
import com.google.thoughtcrimegson.Gson;
|
||||
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
|
||||
import org.whispersystems.textsecure.storage.PreKeyRecord;
|
||||
import org.whispersystems.textsecure.util.Base64;
|
||||
import org.whispersystems.textsecure.util.Medium;
|
||||
import org.whispersystems.textsecure.util.Util;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
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;
|
||||
|
@ -24,17 +25,18 @@ public class PreKeyUtil {
|
|||
|
||||
public static List<PreKeyRecord> generatePreKeys(Context context, MasterSecret masterSecret) {
|
||||
List<PreKeyRecord> records = new LinkedList<PreKeyRecord>();
|
||||
long preKeyIdOffset = getNextPreKeyId(context);
|
||||
int preKeyIdOffset = getNextPreKeyId(context);
|
||||
|
||||
for (int i=0;i<BATCH_SIZE;i++) {
|
||||
Log.w("PreKeyUtil", "Generating PreKey: " + (preKeyIdOffset + i));
|
||||
PreKeyPair keyPair = new PreKeyPair(masterSecret, KeyUtil.generateKeyPair());
|
||||
PreKeyRecord record = new PreKeyRecord(context, masterSecret, preKeyIdOffset + i, keyPair);
|
||||
int preKeyId = (preKeyIdOffset + i) % Medium.MAX_VALUE;
|
||||
PreKeyPair keyPair = new PreKeyPair(masterSecret, KeyUtil.generateKeyPair());
|
||||
PreKeyRecord record = new PreKeyRecord(context, masterSecret, preKeyId, keyPair);
|
||||
|
||||
record.save();
|
||||
records.add(record);
|
||||
}
|
||||
|
||||
setNextPreKeyId(context, (preKeyIdOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE);
|
||||
return records;
|
||||
}
|
||||
|
||||
|
@ -47,7 +49,7 @@ public class PreKeyUtil {
|
|||
|
||||
for (String keyRecordId : keyRecordIds) {
|
||||
try {
|
||||
records.add(new PreKeyRecord(context, masterSecret, Long.parseLong(keyRecordId)));
|
||||
records.add(new PreKeyRecord(context, masterSecret, Integer.parseInt(keyRecordId)));
|
||||
} catch (InvalidKeyIdException e) {
|
||||
Log.w("PreKeyUtil", e);
|
||||
new File(getPreKeysDirectory(context), keyRecordId).delete();
|
||||
|
@ -69,23 +71,32 @@ public class PreKeyUtil {
|
|||
}
|
||||
}
|
||||
|
||||
private static long getNextPreKeyId(Context context) {
|
||||
private static void setNextPreKeyId(Context context, int id) {
|
||||
try {
|
||||
File directory = getPreKeysDirectory(context);
|
||||
String[] keyRecordIds = directory.list();
|
||||
long nextPreKeyId = 0;
|
||||
File nextFile = new File(getPreKeysDirectory(context), PreKeyIndex.FILE_NAME);
|
||||
FileOutputStream fout = new FileOutputStream(nextFile);
|
||||
fout.write(new Gson().toJson(new PreKeyIndex(id)).getBytes());
|
||||
fout.close();
|
||||
} catch (IOException e) {
|
||||
Log.w("PreKeyUtil", e);
|
||||
}
|
||||
}
|
||||
|
||||
for (String keyRecordId : keyRecordIds) {
|
||||
if (Long.parseLong(keyRecordId) > nextPreKeyId)
|
||||
nextPreKeyId = Long.parseLong(keyRecordId);
|
||||
private static int getNextPreKeyId(Context context) {
|
||||
try {
|
||||
File nextFile = new File(getPreKeysDirectory(context), PreKeyIndex.FILE_NAME);
|
||||
|
||||
if (nextFile.exists()) {
|
||||
return Util.getSecureRandom().nextInt(Medium.MAX_VALUE);
|
||||
} else {
|
||||
InputStreamReader reader = new InputStreamReader(new FileInputStream(nextFile));
|
||||
PreKeyIndex index = new Gson().fromJson(reader, PreKeyIndex.class);
|
||||
reader.close();
|
||||
return index.nextPreKeyId;
|
||||
}
|
||||
|
||||
if (nextPreKeyId == 0)
|
||||
nextPreKeyId = SecureRandom.getInstance("SHA1PRNG").nextInt(Integer.MAX_VALUE/2);
|
||||
|
||||
return nextPreKeyId;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (IOException e) {
|
||||
Log.w("PreKeyUtil", e);
|
||||
return Util.getSecureRandom().nextInt(Medium.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,4 +125,16 @@ public class PreKeyUtil {
|
|||
}
|
||||
}
|
||||
|
||||
private static class PreKeyIndex {
|
||||
public static final String FILE_NAME = "index.dat";
|
||||
|
||||
private int nextPreKeyId;
|
||||
|
||||
public PreKeyIndex() {}
|
||||
|
||||
public PreKeyIndex(int nextPreKeyId) {
|
||||
this.nextPreKeyId = nextPreKeyId;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,6 +43,11 @@ public class PublicKey {
|
|||
this.id = id;
|
||||
}
|
||||
|
||||
public PublicKey(int preKeyId, PreKeyPublic publicKey) {
|
||||
this.id = preKeyId;
|
||||
this.publicKey = publicKey.getPublicKey();
|
||||
}
|
||||
|
||||
public PublicKey(byte[] bytes, int offset) throws InvalidKeyException {
|
||||
Log.w("PublicKey", "PublicKey Length: " + (bytes.length - offset));
|
||||
if ((bytes.length - offset) < KEY_SIZE)
|
||||
|
|
|
@ -18,17 +18,17 @@ import java.lang.reflect.Type;
|
|||
|
||||
public class PreKeyEntity {
|
||||
|
||||
private long keyId;
|
||||
private int keyId;
|
||||
private PreKeyPublic publicKey;
|
||||
private IdentityKey identityKey;
|
||||
|
||||
public PreKeyEntity(long keyId, PreKeyPublic publicKey, IdentityKey identityKey) {
|
||||
public PreKeyEntity(int keyId, PreKeyPublic publicKey, IdentityKey identityKey) {
|
||||
this.keyId = keyId;
|
||||
this.publicKey = publicKey;
|
||||
this.identityKey = identityKey;
|
||||
}
|
||||
|
||||
public long getKeyId() {
|
||||
public int getKeyId() {
|
||||
return keyId;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ public class PreKeyRecord extends Record {
|
|||
private final MasterSecret masterSecret;
|
||||
|
||||
private PreKeyPair keyPair;
|
||||
private long id;
|
||||
private int id;
|
||||
|
||||
public PreKeyRecord(Context context, MasterSecret masterSecret, long id)
|
||||
public PreKeyRecord(Context context, MasterSecret masterSecret, int id)
|
||||
throws InvalidKeyIdException
|
||||
{
|
||||
super(context, PREKEY_DIRECTORY, id+"");
|
||||
|
@ -35,7 +35,7 @@ public class PreKeyRecord extends Record {
|
|||
}
|
||||
|
||||
public PreKeyRecord(Context context, MasterSecret masterSecret,
|
||||
long id, PreKeyPair keyPair)
|
||||
int id, PreKeyPair keyPair)
|
||||
{
|
||||
super(context, PREKEY_DIRECTORY, id+"");
|
||||
this.id = id;
|
||||
|
@ -43,7 +43,7 @@ public class PreKeyRecord extends Record {
|
|||
this.masterSecret = masterSecret;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package org.whispersystems.textsecure.util;
|
||||
|
||||
public class Medium {
|
||||
public static int MAX_VALUE = 0xFFF;
|
||||
}
|
|
@ -119,4 +119,12 @@ public class Util {
|
|||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static SecureRandom getSecureRandom() {
|
||||
try {
|
||||
return SecureRandom.getInstance("SHA1PRNG");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import android.widget.TextView;
|
|||
|
||||
import org.whispersystems.textsecure.crypto.InvalidKeyException;
|
||||
import org.thoughtcrime.securesms.crypto.InvalidVersionException;
|
||||
import org.thoughtcrime.securesms.crypto.KeyExchangeMessage;
|
||||
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
|
||||
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.content.Context;
|
|||
import android.database.Cursor;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
|
|
|
@ -22,6 +22,7 @@ import android.content.DialogInterface;
|
|||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
|
||||
|
|
|
@ -20,9 +20,13 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||
import org.whispersystems.textsecure.crypto.KeyUtil;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
import org.whispersystems.textsecure.crypto.PublicKey;
|
||||
import org.whispersystems.textsecure.push.PreKeyEntity;
|
||||
import org.whispersystems.textsecure.storage.LocalKeyRecord;
|
||||
import org.whispersystems.textsecure.storage.RemoteKeyRecord;
|
||||
import org.whispersystems.textsecure.storage.SessionRecord;
|
||||
|
@ -65,8 +69,12 @@ public class KeyExchangeProcessor {
|
|||
return false;
|
||||
}
|
||||
|
||||
return isTrusted(message.getIdentityKey());
|
||||
}
|
||||
|
||||
public boolean isTrusted(IdentityKey identityKey) {
|
||||
return DatabaseFactory.getIdentityDatabase(context).isValidIdentity(masterSecret, recipient,
|
||||
message.getIdentityKey());
|
||||
identityKey);
|
||||
}
|
||||
|
||||
public boolean hasInitiatedSession() {
|
||||
|
@ -86,7 +94,25 @@ public class KeyExchangeProcessor {
|
|||
(localKeyRecord.getCurrentKeyPair() != null && localKeyRecord.getCurrentKeyPair().getId() != responseKeyId);
|
||||
}
|
||||
|
||||
public boolean processKeyExchangeMessage(KeyExchangeMessage message, long threadId) {
|
||||
public void processKeyExchangeMessage(PreKeyEntity message) {
|
||||
PublicKey remoteKey = new PublicKey(message.getKeyId(), message.getPublicKey());
|
||||
remoteKeyRecord.setCurrentRemoteKey(remoteKey);
|
||||
remoteKeyRecord.setLastRemoteKey(remoteKey);
|
||||
remoteKeyRecord.save();
|
||||
|
||||
localKeyRecord = KeyUtil.initializeRecordFor(recipient, context, masterSecret);
|
||||
|
||||
sessionRecord.setSessionId(localKeyRecord.getCurrentKeyPair().getPublicKey().getFingerprintBytes(),
|
||||
remoteKeyRecord.getCurrentRemoteKey().getFingerprintBytes());
|
||||
sessionRecord.setIdentityKey(message.getIdentityKey());
|
||||
sessionRecord.setSessionVersion(Message.SUPPORTED_VERSION);
|
||||
sessionRecord.save();
|
||||
|
||||
DatabaseFactory.getIdentityDatabase(context)
|
||||
.saveIdentity(masterSecret, recipient, message.getIdentityKey());
|
||||
}
|
||||
|
||||
public void processKeyExchangeMessage(KeyExchangeMessage message, long threadId) {
|
||||
int initiateKeyId = Conversions.lowBitsToMedium(message.getPublicKey().getId());
|
||||
message.getPublicKey().setId(initiateKeyId);
|
||||
|
||||
|
@ -123,8 +149,6 @@ public class KeyExchangeProcessor {
|
|||
intent.putExtra("thread_id", threadId);
|
||||
intent.setPackage(context.getPackageName());
|
||||
context.sendBroadcast(intent, KeyCachingService.KEY_PERMISSION);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,11 +14,13 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms.crypto;
|
||||
package org.thoughtcrime.securesms.crypto.protocol;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.InvalidVersionException;
|
||||
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||
import org.whispersystems.textsecure.crypto.InvalidKeyException;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
|
@ -58,9 +60,9 @@ public class KeyExchangeMessage {
|
|||
|
||||
private final int messageVersion;
|
||||
private final int supportedVersion;
|
||||
private final PublicKey publicKey;
|
||||
private final PublicKey publicKey;
|
||||
private final String serialized;
|
||||
private IdentityKey identityKey;
|
||||
private IdentityKey identityKey;
|
||||
|
||||
public KeyExchangeMessage(Context context, MasterSecret masterSecret, int messageVersion, LocalKeyRecord record, int highIdBits) {
|
||||
this.publicKey = new PublicKey(record.getCurrentKeyPair().getPublicKey());
|
|
@ -24,7 +24,7 @@ import android.util.Pair;
|
|||
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
|
||||
import org.whispersystems.textsecure.crypto.InvalidKeyException;
|
||||
import org.thoughtcrime.securesms.crypto.InvalidVersionException;
|
||||
import org.thoughtcrime.securesms.crypto.KeyExchangeMessage;
|
||||
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
|
||||
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
|
|
|
@ -3,14 +3,20 @@ package org.thoughtcrime.securesms.transport;
|
|||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.mms.PartParser;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.textsecure.crypto.SessionCipher;
|
||||
import org.whispersystems.textsecure.push.PreKeyEntity;
|
||||
import org.whispersystems.textsecure.push.PushAttachmentData;
|
||||
import org.whispersystems.textsecure.push.PushServiceSocket;
|
||||
import org.whispersystems.textsecure.push.RateLimitException;
|
||||
import org.whispersystems.textsecure.storage.SessionRecord;
|
||||
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -37,10 +43,22 @@ public class PushTransport extends BaseTransport {
|
|||
String password = TextSecurePreferences.getPushServerPassword(context);
|
||||
PushServiceSocket socket = new PushServiceSocket(context, localNumber, password);
|
||||
|
||||
String recipientNumber = message.getIndividualRecipient().getNumber();
|
||||
String recipientCanonicalNumber = PhoneNumberFormatter.formatNumber(recipientNumber,
|
||||
Recipient recipient = message.getIndividualRecipient();
|
||||
String plaintext = message.getBody().getBody();
|
||||
String recipientCanonicalNumber = PhoneNumberFormatter.formatNumber(recipient.getNumber(),
|
||||
localNumber);
|
||||
|
||||
// if (SessionRecord.hasSession(context, recipient)) {
|
||||
// byte[] cipherText = getEncryptedMessageForExistingSession(recipient, plaintext);
|
||||
// socket.sendMessage(recipientCanonicalNumber, new String(cipherText));
|
||||
// } else {
|
||||
// byte[] cipherText = getEncryptedMessageForNewSession(socket, recipient,
|
||||
// recipientCanonicalNumber,
|
||||
// plaintext);
|
||||
// socket.sendMessage(recipientCanonicalNumber, new String(cipherText));
|
||||
// }
|
||||
|
||||
|
||||
socket.sendMessage(recipientCanonicalNumber, message.getBody().getBody());
|
||||
|
||||
context.sendBroadcast(constructSentIntent(context, message.getId(), message.getType()));
|
||||
|
@ -82,4 +100,23 @@ public class PushTransport extends BaseTransport {
|
|||
|
||||
return attachments;
|
||||
}
|
||||
|
||||
private byte[] getEncryptedMessageForNewSession(PushServiceSocket socket, Recipient recipient, String canonicalRecipientNumber, String plaintext) throws IOException {
|
||||
PreKeyEntity preKey = socket.getPreKey(canonicalRecipientNumber);
|
||||
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipient);
|
||||
processor.processKeyExchangeMessage(preKey);
|
||||
|
||||
synchronized (SessionCipher.CIPHER_LOCK) {
|
||||
SessionCipher sessionCipher = new SessionCipher(context, masterSecret, recipient, new SmsTransportDetails());
|
||||
return sessionCipher.encryptMessage(plaintext.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getEncryptedMessageForExistingSession(Recipient recipient, String plaintext) {
|
||||
synchronized (SessionCipher.CIPHER_LOCK) {
|
||||
SessionCipher sessionCipher = new SessionCipher(context, masterSecret, recipient, new SmsTransportDetails());
|
||||
return sessionCipher.encryptMessage(plaintext.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue