From 6d759bdc8848187fa63bc6e22e0077bc44387c67 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Tue, 3 Mar 2015 11:28:14 -0800 Subject: [PATCH] Upgrade to libaxolotl 1.3.0, simplify some interfaces. --- build.gradle | 2 +- .../api/TextSecureMessageSender.java | 35 ++++++++++--------- .../api/crypto/TextSecureCipher.java | 5 +-- .../api/push/TextSecureAddress.java | 31 ++++++++-------- .../internal/push/PushServiceSocket.java | 15 ++++---- 5 files changed, 47 insertions(+), 41 deletions(-) diff --git a/build.gradle b/build.gradle index 213eb4e5fc..dd070965bf 100644 --- a/build.gradle +++ b/build.gradle @@ -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 { diff --git a/java/src/main/java/org/whispersystems/textsecure/api/TextSecureMessageSender.java b/java/src/main/java/org/whispersystems/textsecure/api/TextSecureMessageSender.java index 12ae412544..6160feffaa 100644 --- a/java/src/main/java/org/whispersystems/textsecure/api/TextSecureMessageSender.java +++ b/java/src/main/java/org/whispersystems/textsecure/api/TextSecureMessageSender.java @@ -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) { 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 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); } } diff --git a/java/src/main/java/org/whispersystems/textsecure/api/crypto/TextSecureCipher.java b/java/src/main/java/org/whispersystems/textsecure/api/crypto/TextSecureCipher.java index 46376a4f2f..9c7cef8ecf 100644 --- a/java/src/main/java/org/whispersystems/textsecure/api/crypto/TextSecureCipher.java +++ b/java/src/main/java/org/whispersystems/textsecure/api/crypto/TextSecureCipher.java @@ -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) { diff --git a/java/src/main/java/org/whispersystems/textsecure/api/push/TextSecureAddress.java b/java/src/main/java/org/whispersystems/textsecure/api/push/TextSecureAddress.java index ed44fdde73..d6f18e8a3c 100644 --- a/java/src/main/java/org/whispersystems/textsecure/api/push/TextSecureAddress.java +++ b/java/src/main/java/org/whispersystems/textsecure/api/push/TextSecureAddress.java @@ -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 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 relay) { this.e164number = e164number; this.relay = relay; } + public TextSecureAddress(String e164number) { + this(e164number, Optional.absent()); + } + public String getNumber() { return e164number; } - public String getRelay() { + public Optional 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 one, Optional two) { + if (one.isPresent()) return two.isPresent() && one.get().equals(two.get()); + else return !two.isPresent(); + } } diff --git a/java/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java b/java/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java index e51d50cbfe..463fc31b80 100644 --- a/java/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java +++ b/java/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java @@ -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 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);