Upgrade to libaxolotl 1.3.0, simplify some interfaces.

This commit is contained in:
Moxie Marlinspike 2015-03-03 11:28:14 -08:00
parent e5034134b3
commit 6d759bdc88
5 changed files with 47 additions and 41 deletions

View File

@ -1,7 +1,7 @@
subprojects {
ext.version_number = "1.0.0"
ext.group_info = "org.whispersystems"
ext.axolotl_version = "1.2.1"
ext.axolotl_version = "1.3.0"
if (JavaVersion.current().isJava8Compatible()) {
allprojects {

View File

@ -19,6 +19,7 @@ package org.whispersystems.textsecure.api;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.SessionBuilder;
import org.whispersystems.libaxolotl.logging.Log;
@ -81,19 +82,18 @@ public class TextSecureMessageSender {
* @param trustStore The trust store containing the TextSecure server's signing TLS certificate.
* @param user The TextSecure username (eg phone number).
* @param password The TextSecure user's password.
* @param userId The axolotl recipient id for the local TextSecure user.
* @param store The AxolotlStore.
* @param eventListener An optional event listener, which fires whenever sessions are
* setup or torn down for a recipient.
*/
public TextSecureMessageSender(String url, TrustStore trustStore,
String user, String password,
long userId, AxolotlStore store,
AxolotlStore store,
Optional<EventListener> eventListener)
{
this.socket = new PushServiceSocket(url, trustStore, new StaticCredentialsProvider(user, password, null));
this.store = store;
this.syncAddress = new TextSecureAddress(userId, user, null);
this.syncAddress = new TextSecureAddress(user);
this.eventListener = eventListener;
}
@ -129,10 +129,10 @@ public class TextSecureMessageSender {
}
if (message.isEndSession()) {
store.deleteAllSessions(recipient.getRecipientId());
store.deleteAllSessions(recipient.getNumber());
if (eventListener.isPresent()) {
eventListener.get().onSecurityEvent(recipient.getRecipientId());
eventListener.get().onSecurityEvent(recipient);
}
}
}
@ -308,24 +308,27 @@ public class TextSecureMessageSender {
messages.add(new OutgoingPushMessage(recipient, TextSecureAddress.DEFAULT_DEVICE_ID, masterBody));
}
for (int deviceId : store.getSubDeviceSessions(recipient.getRecipientId())) {
for (int deviceId : store.getSubDeviceSessions(recipient.getNumber())) {
PushBody body = getEncryptedMessage(socket, recipient, deviceId, plaintext);
messages.add(new OutgoingPushMessage(recipient, deviceId, body));
}
return new OutgoingPushMessageList(recipient.getNumber(), timestamp, recipient.getRelay(), messages);
return new OutgoingPushMessageList(recipient.getNumber(), timestamp, recipient.getRelay().orNull(), messages);
}
private PushBody getEncryptedMessage(PushServiceSocket socket, TextSecureAddress recipient, int deviceId, byte[] plaintext)
throws IOException, UntrustedIdentityException
{
if (!store.containsSession(recipient.getRecipientId(), deviceId)) {
AxolotlAddress axolotlAddress = new AxolotlAddress(recipient.getNumber(), deviceId);
if (!store.containsSession(axolotlAddress)) {
try {
List<PreKeyBundle> preKeys = socket.getPreKeys(recipient, deviceId);
for (PreKeyBundle preKey : preKeys) {
try {
SessionBuilder sessionBuilder = new SessionBuilder(store, recipient.getRecipientId(), deviceId);
AxolotlAddress preKeyAddress = new AxolotlAddress(recipient.getNumber(), preKey.getDeviceId());
SessionBuilder sessionBuilder = new SessionBuilder(store, preKeyAddress);
sessionBuilder.process(preKey);
} catch (org.whispersystems.libaxolotl.UntrustedIdentityException e) {
throw new UntrustedIdentityException("Untrusted identity key!", recipient.getNumber(), preKey.getIdentityKey());
@ -333,14 +336,14 @@ public class TextSecureMessageSender {
}
if (eventListener.isPresent()) {
eventListener.get().onSecurityEvent(recipient.getRecipientId());
eventListener.get().onSecurityEvent(recipient);
}
} catch (InvalidKeyException e) {
throw new IOException(e);
}
}
TextSecureCipher cipher = new TextSecureCipher(store, recipient.getRecipientId(), deviceId);
TextSecureCipher cipher = new TextSecureCipher(store, axolotlAddress);
CiphertextMessage message = cipher.encrypt(plaintext);
int remoteRegistrationId = cipher.getRemoteRegistrationId();
@ -359,14 +362,14 @@ public class TextSecureMessageSender {
{
try {
for (int extraDeviceId : mismatchedDevices.getExtraDevices()) {
store.deleteSession(recipient.getRecipientId(), extraDeviceId);
store.deleteSession(new AxolotlAddress(recipient.getNumber(), extraDeviceId));
}
for (int missingDeviceId : mismatchedDevices.getMissingDevices()) {
PreKeyBundle preKey = socket.getPreKey(recipient, missingDeviceId);
try {
SessionBuilder sessionBuilder = new SessionBuilder(store, recipient.getRecipientId(), missingDeviceId);
SessionBuilder sessionBuilder = new SessionBuilder(store, new AxolotlAddress(recipient.getNumber(), missingDeviceId));
sessionBuilder.process(preKey);
} catch (org.whispersystems.libaxolotl.UntrustedIdentityException e) {
throw new UntrustedIdentityException("Untrusted identity key!", recipient.getNumber(), preKey.getIdentityKey());
@ -378,15 +381,13 @@ public class TextSecureMessageSender {
}
private void handleStaleDevices(TextSecureAddress recipient, StaleDevices staleDevices) {
long recipientId = recipient.getRecipientId();
for (int staleDeviceId : staleDevices.getStaleDevices()) {
store.deleteSession(recipientId, staleDeviceId);
store.deleteSession(new AxolotlAddress(recipient.getNumber(), staleDeviceId));
}
}
public static interface EventListener {
public void onSecurityEvent(long recipientId);
public void onSecurityEvent(TextSecureAddress address);
}
}

View File

@ -18,6 +18,7 @@ package org.whispersystems.textsecure.api.crypto;
import com.google.protobuf.InvalidProtocolBufferException;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
@ -53,8 +54,8 @@ public class TextSecureCipher {
private final SessionCipher sessionCipher;
public TextSecureCipher(AxolotlStore axolotlStore, long recipientId, int deviceId) {
this.sessionCipher = new SessionCipher(axolotlStore, recipientId, deviceId);
public TextSecureCipher(AxolotlStore axolotlStore, AxolotlAddress destination) {
this.sessionCipher = new SessionCipher(axolotlStore, destination);
}
public CiphertextMessage encrypt(byte[] unpaddedMessage) {

View File

@ -16,6 +16,8 @@
*/
package org.whispersystems.textsecure.api.push;
import org.whispersystems.libaxolotl.util.guava.Optional;
/**
* A class representing a message destination or origin.
*/
@ -23,52 +25,48 @@ public class TextSecureAddress {
public static final int DEFAULT_DEVICE_ID = 1;
private final long recipientId;
private final String e164number;
private final String relay;
private final Optional<String> relay;
/**
* Construct a PushAddress.
*
* @param recipientId The axolotl recipient ID of this destination.
* @param e164number The TextSecure username of this destination (eg e164 representation of a phone number).
* @param relay The TextSecure federated server this user is registered with (if not your own server).
*/
public TextSecureAddress(long recipientId, String e164number, String relay) {
this.recipientId = recipientId;
public TextSecureAddress(String e164number, Optional<String> relay) {
this.e164number = e164number;
this.relay = relay;
}
public TextSecureAddress(String e164number) {
this(e164number, Optional.<String>absent());
}
public String getNumber() {
return e164number;
}
public String getRelay() {
public Optional<String> getRelay() {
return relay;
}
public long getRecipientId() {
return recipientId;
}
@Override
public boolean equals(Object other) {
if (other == null || !(other instanceof TextSecureAddress)) return false;
TextSecureAddress that = (TextSecureAddress)other;
return this.recipientId == that.recipientId &&
equals(this.e164number, that.e164number) &&
return equals(this.e164number, that.e164number) &&
equals(this.relay, that.relay);
}
@Override
public int hashCode() {
int hashCode = (int)this.recipientId;
int hashCode = 0;
if (this.e164number != null) hashCode ^= this.e164number.hashCode();
if (this.relay != null) hashCode ^= this.relay.hashCode();
if (this.relay.isPresent()) hashCode ^= this.relay.get().hashCode();
return hashCode;
}
@ -77,4 +75,9 @@ public class TextSecureAddress {
if (one == null) return two == null;
return one.equals(two);
}
private boolean equals(Optional<String> one, Optional<String> two) {
if (one.isPresent()) return two.isPresent() && one.get().equals(two.get());
else return !two.isPresent();
}
}

View File

@ -25,6 +25,7 @@ import org.whispersystems.libaxolotl.logging.Log;
import org.whispersystems.libaxolotl.state.PreKeyBundle;
import org.whispersystems.libaxolotl.state.PreKeyRecord;
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.crypto.AttachmentCipherOutputStream;
import org.whispersystems.textsecure.api.push.ContactTokenDetails;
import org.whispersystems.textsecure.api.push.TextSecureAddress;
@ -129,11 +130,11 @@ public class PushServiceSocket {
JsonUtil.toJson(new ProvisioningMessage(Base64.encodeBytes(body))));
}
public void sendReceipt(String destination, long messageId, String relay) throws IOException {
public void sendReceipt(String destination, long messageId, Optional<String> relay) throws IOException {
String path = String.format(RECEIPT_PATH, destination, messageId);
if (!Util.isEmpty(relay)) {
path += "?relay=" + relay;
if (relay.isPresent()) {
path += "?relay=" + relay.get();
}
makeRequest(path, "PUT", null);
@ -204,8 +205,8 @@ public class PushServiceSocket {
String path = String.format(PREKEY_DEVICE_PATH, destination.getNumber(), deviceId);
if (!Util.isEmpty(destination.getRelay())) {
path = path + "?relay=" + destination.getRelay();
if (destination.getRelay().isPresent()) {
path = path + "?relay=" + destination.getRelay().get();
}
String responseText = makeRequest(path, "GET", null);
@ -248,8 +249,8 @@ public class PushServiceSocket {
String path = String.format(PREKEY_DEVICE_PATH, destination.getNumber(),
String.valueOf(deviceId));
if (!Util.isEmpty(destination.getRelay())) {
path = path + "?relay=" + destination.getRelay();
if (destination.getRelay().isPresent()) {
path = path + "?relay=" + destination.getRelay().get();
}
String responseText = makeRequest(path, "GET", null);