diff --git a/pom.xml b/pom.xml
index f0d0458c9..5506b5e24 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,12 +102,12 @@
org.whispersystems
websocket-resources
- 0.5.4
+ 0.5.8
org.whispersystems
- dropwizard-simpleauth
- 0.3.0
+ curve25519-java
+ 0.5.0
diff --git a/protobuf/TextSecure.proto b/protobuf/TextSecure.proto
index 65add730f..4ef758e95 100644
--- a/protobuf/TextSecure.proto
+++ b/protobuf/TextSecure.proto
@@ -21,22 +21,48 @@ option java_outer_classname = "MessageProtos";
message Envelope {
enum Type {
- UNKNOWN = 0;
- CIPHERTEXT = 1;
- KEY_EXCHANGE = 2;
- PREKEY_BUNDLE = 3;
- RECEIPT = 5;
+ UNKNOWN = 0;
+ CIPHERTEXT = 1;
+ KEY_EXCHANGE = 2;
+ PREKEY_BUNDLE = 3;
+ RECEIPT = 5;
+ UNIDENTIFIED_SENDER = 6;
}
- optional Type type = 1;
- optional string source = 2;
- optional uint32 sourceDevice = 7;
- optional string relay = 3;
- optional uint64 timestamp = 5;
- optional bytes legacyMessage = 6; // Contains an encrypted DataMessage XXX -- Remove after 10/01/15
- optional bytes content = 8; // Contains an encrypted Content
+ optional Type type = 1;
+ optional string source = 2;
+ optional uint32 sourceDevice = 7;
+ optional string relay = 3;
+ optional uint64 timestamp = 5;
+ optional bytes legacyMessage = 6; // Contains an encrypted DataMessage XXX -- Remove after 10/01/15
+ optional bytes content = 8; // Contains an encrypted Content
+ optional string serverGuid = 9;
+ optional uint64 server_timestamp = 10;
}
message ProvisioningUuid {
optional string uuid = 1;
-}
\ No newline at end of file
+}
+
+message ServerCertificate {
+ message Certificate {
+ optional uint32 id = 1;
+ optional bytes key = 2;
+ }
+
+ optional bytes certificate = 1;
+ optional bytes signature = 2;
+}
+
+message SenderCertificate {
+ message Certificate {
+ optional string sender = 1;
+ optional uint32 senderDevice = 2;
+ optional fixed64 expires = 3;
+ optional bytes identityKey = 4;
+ optional ServerCertificate signer = 5;
+ }
+
+ optional bytes certificate = 1;
+ optional bytes signature = 2;
+}
diff --git a/src/main/java/org/whispersystems/dispatch/DispatchManager.java b/src/main/java/org/whispersystems/dispatch/DispatchManager.java
index 6edc2f9dd..f90dadb8a 100644
--- a/src/main/java/org/whispersystems/dispatch/DispatchManager.java
+++ b/src/main/java/org/whispersystems/dispatch/DispatchManager.java
@@ -1,6 +1,5 @@
package org.whispersystems.dispatch;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.dispatch.io.RedisPubSubConnectionFactory;
@@ -9,10 +8,12 @@ import org.whispersystems.dispatch.redis.PubSubReply;
import java.io.IOException;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class DispatchManager extends Thread {
private final Logger logger = LoggerFactory.getLogger(DispatchManager.class);
@@ -45,7 +46,7 @@ public class DispatchManager extends Thread {
}
public synchronized void subscribe(String name, DispatchChannel dispatchChannel) {
- Optional previous = Optional.fromNullable(subscriptions.get(name));
+ Optional previous = Optional.ofNullable(subscriptions.get(name));
subscriptions.put(name, dispatchChannel);
try {
@@ -60,7 +61,7 @@ public class DispatchManager extends Thread {
}
public synchronized void unsubscribe(String name, DispatchChannel channel) {
- Optional subscription = Optional.fromNullable(subscriptions.get(name));
+ Optional subscription = Optional.ofNullable(subscriptions.get(name));
if (subscription.isPresent() && subscription.get() == channel) {
subscriptions.remove(name);
@@ -105,7 +106,7 @@ public class DispatchManager extends Thread {
}
private void dispatchSubscribe(final PubSubReply reply) {
- Optional subscription = Optional.fromNullable(subscriptions.get(reply.getChannel()));
+ Optional subscription = Optional.ofNullable(subscriptions.get(reply.getChannel()));
if (subscription.isPresent()) {
dispatchSubscription(reply.getChannel(), subscription.get());
@@ -115,7 +116,7 @@ public class DispatchManager extends Thread {
}
private void dispatchMessage(PubSubReply reply) {
- Optional subscription = Optional.fromNullable(subscriptions.get(reply.getChannel()));
+ Optional subscription = Optional.ofNullable(subscriptions.get(reply.getChannel()));
if (subscription.isPresent()) {
dispatchMessage(reply.getChannel(), subscription.get(), reply.getContent().get());
diff --git a/src/main/java/org/whispersystems/dispatch/redis/PubSubConnection.java b/src/main/java/org/whispersystems/dispatch/redis/PubSubConnection.java
index 599c3930a..171fcc736 100644
--- a/src/main/java/org/whispersystems/dispatch/redis/PubSubConnection.java
+++ b/src/main/java/org/whispersystems/dispatch/redis/PubSubConnection.java
@@ -1,6 +1,5 @@
package org.whispersystems.dispatch.redis;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.dispatch.io.RedisInputStream;
@@ -14,6 +13,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;
+import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
public class PubSubConnection {
@@ -98,12 +98,12 @@ public class PubSubConnection {
private PubSubReply readUnsubscribeReply() throws IOException {
String channelName = readSubscriptionReply();
- return new PubSubReply(PubSubReply.Type.UNSUBSCRIBE, channelName, Optional.absent());
+ return new PubSubReply(PubSubReply.Type.UNSUBSCRIBE, channelName, Optional.empty());
}
private PubSubReply readSubscribeReply() throws IOException {
String channelName = readSubscriptionReply();
- return new PubSubReply(PubSubReply.Type.SUBSCRIBE, channelName, Optional.absent());
+ return new PubSubReply(PubSubReply.Type.SUBSCRIBE, channelName, Optional.empty());
}
private String readSubscriptionReply() throws IOException {
diff --git a/src/main/java/org/whispersystems/dispatch/redis/PubSubReply.java b/src/main/java/org/whispersystems/dispatch/redis/PubSubReply.java
index d57797fae..4e76e05b8 100644
--- a/src/main/java/org/whispersystems/dispatch/redis/PubSubReply.java
+++ b/src/main/java/org/whispersystems/dispatch/redis/PubSubReply.java
@@ -1,7 +1,9 @@
package org.whispersystems.dispatch.redis;
-import com.google.common.base.Optional;
+import java.util.Optional;
+
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class PubSubReply {
public enum Type {
@@ -11,7 +13,7 @@ public class PubSubReply {
}
private final Type type;
- private final String channel;
+ private final String channel;
private final Optional content;
public PubSubReply(Type type, String channel, Optional content) {
diff --git a/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java b/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java
index fe6a93cad..9c9938c60 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java
@@ -17,20 +17,7 @@
package org.whispersystems.textsecuregcm;
import com.fasterxml.jackson.annotation.JsonProperty;
-import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
-import org.whispersystems.textsecuregcm.configuration.AttachmentsConfiguration;
-import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration;
-import org.whispersystems.textsecuregcm.configuration.FederationConfiguration;
-import org.whispersystems.textsecuregcm.configuration.GcmConfiguration;
-import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration;
-import org.whispersystems.textsecuregcm.configuration.MessageCacheConfiguration;
-import org.whispersystems.textsecuregcm.configuration.ProfilesConfiguration;
-import org.whispersystems.textsecuregcm.configuration.PushConfiguration;
-import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
-import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
-import org.whispersystems.textsecuregcm.configuration.TestDeviceConfiguration;
-import org.whispersystems.textsecuregcm.configuration.TurnConfiguration;
-import org.whispersystems.textsecuregcm.configuration.TwilioConfiguration;
+import org.whispersystems.textsecuregcm.configuration.*;
import org.whispersystems.websocket.configuration.WebSocketConfiguration;
import javax.validation.Valid;
@@ -101,10 +88,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private List maxDevices = new LinkedList<>();
- @Valid
- @JsonProperty
- private FederationConfiguration federation = new FederationConfiguration();
-
@Valid
@NotNull
@JsonProperty
@@ -143,6 +126,11 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private ApnConfiguration apn;
+ @Valid
+ @NotNull
+ @JsonProperty
+ private UnidentifiedDeliveryConfiguration unidentifiedDelivery;
+
public WebSocketConfiguration getWebSocketConfiguration() {
return webSocket;
}
@@ -195,10 +183,6 @@ public class WhisperServerConfiguration extends Configuration {
return limits;
}
- public FederationConfiguration getFederationConfiguration() {
- return federation;
- }
-
public TurnConfiguration getTurnConfiguration() {
return turn;
}
@@ -215,6 +199,10 @@ public class WhisperServerConfiguration extends Configuration {
return profiles;
}
+ public UnidentifiedDeliveryConfiguration getDeliveryCertificate() {
+ return unidentifiedDelivery;
+ }
+
public Map getTestDevices() {
Map results = new HashMap<>();
diff --git a/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java
index f6a13ce3c..8d138d821 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java
@@ -20,31 +20,24 @@ import com.codahale.metrics.SharedMetricRegistries;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.google.common.base.Optional;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import org.skife.jdbi.v2.DBI;
import org.whispersystems.dispatch.DispatchManager;
-import org.whispersystems.dropwizard.simpleauth.AuthDynamicFeature;
-import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider;
-import org.whispersystems.dropwizard.simpleauth.BasicCredentialAuthFilter;
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
+import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
import org.whispersystems.textsecuregcm.auth.DirectoryCredentialsGenerator;
-import org.whispersystems.textsecuregcm.auth.FederatedPeerAuthenticator;
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
import org.whispersystems.textsecuregcm.controllers.AccountController;
import org.whispersystems.textsecuregcm.controllers.AttachmentController;
+import org.whispersystems.textsecuregcm.controllers.CertificateController;
import org.whispersystems.textsecuregcm.controllers.DeviceController;
import org.whispersystems.textsecuregcm.controllers.DirectoryController;
-import org.whispersystems.textsecuregcm.controllers.FederationControllerV1;
-import org.whispersystems.textsecuregcm.controllers.FederationControllerV2;
import org.whispersystems.textsecuregcm.controllers.KeepAliveController;
import org.whispersystems.textsecuregcm.controllers.KeysController;
import org.whispersystems.textsecuregcm.controllers.MessageController;
import org.whispersystems.textsecuregcm.controllers.ProfileController;
import org.whispersystems.textsecuregcm.controllers.ProvisioningController;
-import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
-import org.whispersystems.textsecuregcm.federation.FederatedPeer;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.liquibase.NameableMigrationsBundle;
import org.whispersystems.textsecuregcm.mappers.DeviceLimitExceededExceptionMapper;
@@ -75,6 +68,7 @@ import org.whispersystems.textsecuregcm.websocket.AuthenticatedConnectListener;
import org.whispersystems.textsecuregcm.websocket.DeadLetterHandler;
import org.whispersystems.textsecuregcm.websocket.ProvisioningConnectListener;
import org.whispersystems.textsecuregcm.websocket.WebSocketAccountAuthenticator;
+import org.whispersystems.textsecuregcm.workers.CertificateCommand;
import org.whispersystems.textsecuregcm.workers.DeleteUserCommand;
import org.whispersystems.textsecuregcm.workers.DirectoryCommand;
import org.whispersystems.textsecuregcm.workers.PeriodicStatsCommand;
@@ -88,9 +82,13 @@ import javax.servlet.FilterRegistration;
import javax.servlet.ServletRegistration;
import java.security.Security;
import java.util.EnumSet;
+import java.util.Optional;
import static com.codahale.metrics.MetricRegistry.name;
import io.dropwizard.Application;
+import io.dropwizard.auth.AuthDynamicFeature;
+import io.dropwizard.auth.AuthValueFactoryProvider;
+import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.jdbi.DBIFactory;
import io.dropwizard.setup.Bootstrap;
@@ -109,6 +107,7 @@ public class WhisperServerService extends Application("accountdb", "accountsdb.xml") {
@Override
public DataSourceFactory getDataSourceFactory(WhisperServerConfiguration configuration) {
@@ -163,7 +162,6 @@ public class WhisperServerService extends Application()
.setAuthenticator(deviceAuthenticator)
- .setPrincipal(Account.class)
- .buildAuthFilter(),
- new BasicCredentialAuthFilter.Builder()
- .setAuthenticator(federatedPeerAuthenticator)
- .setPrincipal(FederatedPeer.class)
.buildAuthFilter()));
- environment.jersey().register(new AuthValueFactoryProvider.Binder());
+ environment.jersey().register(new AuthValueFactoryProvider.Binder<>(Account.class));
environment.jersey().register(new AccountController(pendingAccountsManager, accountsManager, rateLimiters, smsSender, directoryQueue, messagesManager, turnTokenGenerator, config.getTestDevices()));
environment.jersey().register(new DeviceController(pendingDevicesManager, accountsManager, messagesManager, directoryQueue, rateLimiters, config.getMaxDevices()));
environment.jersey().register(new DirectoryController(rateLimiters, directory, directoryCredentialsGenerator));
- environment.jersey().register(new FederationControllerV1(accountsManager, attachmentController, messageController));
- environment.jersey().register(new FederationControllerV2(accountsManager, attachmentController, messageController, keysController));
environment.jersey().register(new ProvisioningController(rateLimiters, pushSender));
+ environment.jersey().register(new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().getCertificate(), config.getDeliveryCertificate().getPrivateKey(), config.getDeliveryCertificate().getExpiresDays())));
environment.jersey().register(attachmentController);
environment.jersey().register(keysController);
environment.jersey().register(messageController);
diff --git a/src/main/java/org/whispersystems/textsecuregcm/auth/AccountAuthenticator.java b/src/main/java/org/whispersystems/textsecuregcm/auth/AccountAuthenticator.java
index 20b41ee67..78aa3d89b 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/auth/AccountAuthenticator.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/auth/AccountAuthenticator.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2013 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -19,18 +19,19 @@ package org.whispersystems.textsecuregcm.auth;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.whispersystems.dropwizard.simpleauth.Authenticator;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.Constants;
import org.whispersystems.textsecuregcm.util.Util;
+import java.util.Optional;
+
import static com.codahale.metrics.MetricRegistry.name;
import io.dropwizard.auth.AuthenticationException;
+import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
public class AccountAuthenticator implements Authenticator {
@@ -56,13 +57,13 @@ public class AccountAuthenticator implements Authenticator account = accountsManager.get(authorizationHeader.getNumber());
if (!account.isPresent()) {
- return Optional.absent();
+ return Optional.empty();
}
Optional device = account.get().getDevice(authorizationHeader.getDeviceId());
if (!device.isPresent()) {
- return Optional.absent();
+ return Optional.empty();
}
if (device.get().getAuthenticationCredentials().verify(basicCredentials.getPassword())) {
@@ -73,9 +74,9 @@ public class AccountAuthenticator implements Authenticator.
- */
-package org.whispersystems.textsecuregcm.auth;
-
-import com.codahale.metrics.Meter;
-import com.codahale.metrics.MetricRegistry;
-import com.codahale.metrics.SharedMetricRegistries;
-import com.google.common.base.Optional;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.whispersystems.dropwizard.simpleauth.Authenticator;
-import org.whispersystems.textsecuregcm.configuration.FederationConfiguration;
-import org.whispersystems.textsecuregcm.federation.FederatedPeer;
-import org.whispersystems.textsecuregcm.util.Constants;
-
-import java.util.List;
-
-import static com.codahale.metrics.MetricRegistry.name;
-import io.dropwizard.auth.AuthenticationException;
-import io.dropwizard.auth.basic.BasicCredentials;
-
-
-public class FederatedPeerAuthenticator implements Authenticator {
-
- private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
-
- private final Meter authenticationFailedMeter = metricRegistry.meter(name(getClass(),
- "authentication",
- "failed"));
-
- private final Meter authenticationSucceededMeter = metricRegistry.meter(name(getClass(),
- "authentication",
- "succeeded"));
-
- private final Logger logger = LoggerFactory.getLogger(FederatedPeerAuthenticator.class);
-
- private final List peers;
-
- public FederatedPeerAuthenticator(FederationConfiguration config) {
- this.peers = config.getPeers();
- }
-
- @Override
- public Optional authenticate(BasicCredentials basicCredentials)
- throws AuthenticationException
- {
-
- if (peers == null) {
- authenticationFailedMeter.mark();
- return Optional.absent();
- }
-
- for (FederatedPeer peer : peers) {
- if (basicCredentials.getUsername().equals(peer.getName()) &&
- basicCredentials.getPassword().equals(peer.getAuthenticationToken()))
- {
- authenticationSucceededMeter.mark();
- return Optional.of(peer);
- }
- }
-
- authenticationFailedMeter.mark();
- return Optional.absent();
- }
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/auth/OptionalAccess.java b/src/main/java/org/whispersystems/textsecuregcm/auth/OptionalAccess.java
new file mode 100644
index 000000000..77eb5ecb7
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/auth/OptionalAccess.java
@@ -0,0 +1,74 @@
+package org.whispersystems.textsecuregcm.auth;
+
+import org.whispersystems.textsecuregcm.storage.Account;
+import org.whispersystems.textsecuregcm.storage.Device;
+import org.whispersystems.textsecuregcm.util.Hex;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import java.security.MessageDigest;
+import java.util.Optional;
+
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+public class OptionalAccess {
+
+ public static final String UNIDENTIFIED = "Unidentified-Access-Key";
+
+ public static void verify(Optional requestAccount,
+ Optional accessKey,
+ Optional targetAccount,
+ String deviceSelector)
+ {
+ try {
+ verify(requestAccount, accessKey, targetAccount);
+
+ if (!deviceSelector.equals("*")) {
+ long deviceId = Long.parseLong(deviceSelector);
+
+ Optional targetDevice = targetAccount.get().getDevice(deviceId);
+
+ if (targetDevice.isPresent() && targetDevice.get().isActive()) {
+ return;
+ }
+
+ if (requestAccount.isPresent()) {
+ throw new WebApplicationException(Response.Status.NOT_FOUND);
+ } else {
+ throw new WebApplicationException(Response.Status.UNAUTHORIZED);
+ }
+ }
+ } catch (NumberFormatException e) {
+ throw new WebApplicationException(Response.status(422).build());
+ }
+ }
+
+ public static void verify(Optional requestAccount,
+ Optional accessKey,
+ Optional targetAccount)
+ {
+ if (requestAccount.isPresent() && targetAccount.isPresent() && targetAccount.get().isActive()) {
+ return;
+ }
+
+ //noinspection ConstantConditions
+ if (requestAccount.isPresent() && (!targetAccount.isPresent() || (targetAccount.isPresent() && !targetAccount.get().isActive()))) {
+ throw new WebApplicationException(Response.Status.NOT_FOUND);
+ }
+
+ if (accessKey.isPresent() && targetAccount.isPresent() && targetAccount.get().isActive() && targetAccount.get().isUnrestrictedUnidentifiedAccess()) {
+ return;
+ }
+
+ if (accessKey.isPresent() &&
+ targetAccount.isPresent() &&
+ targetAccount.get().getUnidentifiedAccessKey().isPresent() &&
+ targetAccount.get().isActive() &&
+ MessageDigest.isEqual(accessKey.get().getAccessKey(), targetAccount.get().getUnidentifiedAccessKey().get()))
+ {
+ return;
+ }
+
+ throw new WebApplicationException(Response.Status.UNAUTHORIZED);
+ }
+
+}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/auth/UnidentifiedAccessChecksum.java b/src/main/java/org/whispersystems/textsecuregcm/auth/UnidentifiedAccessChecksum.java
new file mode 100644
index 000000000..85661c946
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/auth/UnidentifiedAccessChecksum.java
@@ -0,0 +1,27 @@
+package org.whispersystems.textsecuregcm.auth;
+
+import org.whispersystems.textsecuregcm.util.Base64;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Optional;
+
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+public class UnidentifiedAccessChecksum {
+
+ public static String generateFor(Optional unidentifiedAccessKey) {
+ try {
+ if (!unidentifiedAccessKey.isPresent()|| unidentifiedAccessKey.get().length != 16) return null;
+
+ Mac mac = Mac.getInstance("HmacSHA256");
+ mac.init(new SecretKeySpec(unidentifiedAccessKey.get(), "HmacSHA256"));
+
+ return Base64.encodeBytes(mac.doFinal(new byte[32]));
+ } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/configuration/FederationConfiguration.java b/src/main/java/org/whispersystems/textsecuregcm/configuration/FederationConfiguration.java
deleted file mode 100644
index 6bc5ec802..000000000
--- a/src/main/java/org/whispersystems/textsecuregcm/configuration/FederationConfiguration.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Copyright (C) 2013 Open WhisperSystems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package org.whispersystems.textsecuregcm.configuration;
-
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import org.whispersystems.textsecuregcm.federation.FederatedPeer;
-
-import java.util.List;
-
-public class FederationConfiguration {
-
- @JsonProperty
- private List peers;
-
- @JsonProperty
- private String name;
-
- public List getPeers() {
- return peers;
- }
-
- public String getName() {
- return name;
- }
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/configuration/RedPhoneConfiguration.java b/src/main/java/org/whispersystems/textsecuregcm/configuration/RedPhoneConfiguration.java
deleted file mode 100644
index 68ee2f7c6..000000000
--- a/src/main/java/org/whispersystems/textsecuregcm/configuration/RedPhoneConfiguration.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.whispersystems.textsecuregcm.configuration;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.base.Optional;
-import org.apache.commons.codec.DecoderException;
-import org.apache.commons.codec.binary.Hex;
-
-public class RedPhoneConfiguration {
-
- @JsonProperty
- private String authKey;
-
- public Optional getAuthorizationKey() throws DecoderException {
- if (authKey == null || authKey.trim().length() == 0) {
- return Optional.absent();
- }
-
- return Optional.of(Hex.decodeHex(authKey.toCharArray()));
- }
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/configuration/UnidentifiedDeliveryConfiguration.java b/src/main/java/org/whispersystems/textsecuregcm/configuration/UnidentifiedDeliveryConfiguration.java
new file mode 100644
index 000000000..416ef303a
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/configuration/UnidentifiedDeliveryConfiguration.java
@@ -0,0 +1,42 @@
+package org.whispersystems.textsecuregcm.configuration;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.whispersystems.textsecuregcm.crypto.Curve;
+import org.whispersystems.textsecuregcm.crypto.ECPrivateKey;
+import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+public class UnidentifiedDeliveryConfiguration {
+
+ @JsonProperty
+ @JsonSerialize(using = ByteArrayAdapter.Serializing.class)
+ @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
+ @NotNull
+ private byte[] certificate;
+
+ @JsonProperty
+ @JsonSerialize(using = ByteArrayAdapter.Serializing.class)
+ @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
+ @NotNull
+ @Size(min = 32, max = 32)
+ private byte[] privateKey;
+
+ @NotNull
+ private int expiresDays;
+
+ public byte[] getCertificate() {
+ return certificate;
+ }
+
+ public ECPrivateKey getPrivateKey() {
+ return Curve.decodePrivatePoint(privateKey);
+ }
+
+ public int getExpiresDays() {
+ return expiresDays;
+ }
+}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java
index 9ea97bdda..7b9d0e2e0 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2013 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -21,7 +21,6 @@ import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.annotation.Timed;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
@@ -66,11 +65,13 @@ import java.io.IOException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static com.codahale.metrics.MetricRegistry.name;
import io.dropwizard.auth.Auth;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Path("/v1/accounts")
public class AccountController {
@@ -311,13 +312,14 @@ public class AccountController {
device.setFetchesMessages(attributes.getFetchesMessages());
device.setName(attributes.getName());
device.setLastSeen(Util.todayInMillis());
- device.setVoiceSupported(attributes.getVoice());
- device.setVideoSupported(attributes.getVideo());
+ device.setUnauthenticatedDeliverySupported(attributes.getUnidentifiedAccessKey() != null);
device.setRegistrationId(attributes.getRegistrationId());
device.setSignalingKey(attributes.getSignalingKey());
device.setUserAgent(userAgent);
account.setPin(attributes.getPin());
+ account.setUnidentifiedAccessKey(attributes.getUnidentifiedAccessKey());
+ account.setUnrestrictedUnidentifiedAccess(attributes.isUnrestrictedUnidentifiedAccess());
accounts.update(account);
}
@@ -339,8 +341,7 @@ public class AccountController {
device.setFetchesMessages(accountAttributes.getFetchesMessages());
device.setRegistrationId(accountAttributes.getRegistrationId());
device.setName(accountAttributes.getName());
- device.setVoiceSupported(accountAttributes.getVoice());
- device.setVideoSupported(accountAttributes.getVideo());
+ device.setUnauthenticatedDeliverySupported(accountAttributes.getUnidentifiedAccessKey() != null);
device.setCreated(System.currentTimeMillis());
device.setLastSeen(Util.todayInMillis());
device.setUserAgent(userAgent);
@@ -349,6 +350,8 @@ public class AccountController {
account.setNumber(number);
account.addDevice(device);
account.setPin(accountAttributes.getPin());
+ account.setUnidentifiedAccessKey(accountAttributes.getUnidentifiedAccessKey());
+ account.setUnrestrictedUnidentifiedAccess(accountAttributes.isUnrestrictedUnidentifiedAccess());
if (accounts.create(account)) {
newUserMeter.mark();
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentController.java
index 060efd1e0..9f5800b6b 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentController.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentController.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2013 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -18,26 +18,20 @@ package org.whispersystems.textsecuregcm.controllers;
import com.amazonaws.HttpMethod;
import com.codahale.metrics.annotation.Timed;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.entities.AttachmentDescriptor;
import org.whispersystems.textsecuregcm.entities.AttachmentUri;
-import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
-import org.whispersystems.textsecuregcm.federation.NoSuchPeerException;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
+import org.whispersystems.textsecuregcm.s3.UrlSigner;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.util.Conversions;
-import org.whispersystems.textsecuregcm.s3.UrlSigner;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
import java.io.IOException;
import java.net.URL;
import java.security.SecureRandom;
@@ -49,21 +43,18 @@ import io.dropwizard.auth.Auth;
@Path("/v1/attachments")
public class AttachmentController {
+ @SuppressWarnings("unused")
private final Logger logger = LoggerFactory.getLogger(AttachmentController.class);
private static final String[] UNACCELERATED_REGIONS = {"+20", "+971", "+968", "+974"};
- private final RateLimiters rateLimiters;
- private final FederatedClientManager federatedClientManager;
- private final UrlSigner urlSigner;
+ private final RateLimiters rateLimiters;
+ private final UrlSigner urlSigner;
- public AttachmentController(RateLimiters rateLimiters,
- FederatedClientManager federatedClientManager,
- UrlSigner urlSigner)
+ public AttachmentController(RateLimiters rateLimiters, UrlSigner urlSigner)
{
- this.rateLimiters = rateLimiters;
- this.federatedClientManager = federatedClientManager;
- this.urlSigner = urlSigner;
+ this.rateLimiters = rateLimiters;
+ this.urlSigner = urlSigner;
}
@Timed
@@ -88,20 +79,10 @@ public class AttachmentController {
@Produces(MediaType.APPLICATION_JSON)
@Path("/{attachmentId}")
public AttachmentUri redirectToAttachment(@Auth Account account,
- @PathParam("attachmentId") long attachmentId,
- @QueryParam("relay") Optional relay)
+ @PathParam("attachmentId") long attachmentId)
throws IOException
{
- try {
- if (!relay.isPresent()) {
- return new AttachmentUri(urlSigner.getPreSignedUrl(attachmentId, HttpMethod.GET, Stream.of(UNACCELERATED_REGIONS).anyMatch(region -> account.getNumber().startsWith(region))));
- } else {
- return new AttachmentUri(federatedClientManager.getClient(relay.get()).getSignedAttachmentUri(attachmentId));
- }
- } catch (NoSuchPeerException e) {
- logger.info("No such peer: " + relay);
- throw new WebApplicationException(Response.status(404).build());
- }
+ return new AttachmentUri(urlSigner.getPreSignedUrl(attachmentId, HttpMethod.GET, Stream.of(UNACCELERATED_REGIONS).anyMatch(region -> account.getNumber().startsWith(region))));
}
private long generateAttachmentId() {
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java
new file mode 100644
index 000000000..53ee52066
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java
@@ -0,0 +1,36 @@
+package org.whispersystems.textsecuregcm.controllers;
+
+import com.codahale.metrics.annotation.Timed;
+import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
+import org.whispersystems.textsecuregcm.entities.DeliveryCertificate;
+import org.whispersystems.textsecuregcm.storage.Account;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+
+import io.dropwizard.auth.Auth;
+
+@Path("/v1/certificate")
+public class CertificateController {
+
+ private final CertificateGenerator certificateGenerator;
+
+ public CertificateController(CertificateGenerator certificateGenerator) {
+ this.certificateGenerator = certificateGenerator;
+ }
+
+ @Timed
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("/delivery")
+ public DeliveryCertificate getDeliveryCertificate(@Auth Account account) throws IOException, InvalidKeyException {
+ if (!account.getAuthenticatedDevice().isPresent()) throw new AssertionError();
+ return new DeliveryCertificate(certificateGenerator.createFor(account, account.getAuthenticatedDevice().get()));
+ }
+
+}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java
index 83eb090d8..b6414d30c 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java
@@ -18,7 +18,6 @@ package org.whispersystems.textsecuregcm.controllers;
import com.codahale.metrics.annotation.Timed;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
@@ -55,6 +54,7 @@ import java.security.SecureRandom;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import io.dropwizard.auth.Auth;
@@ -213,6 +213,15 @@ public class DeviceController {
}
}
+ @Timed
+ @PUT
+ @Path("/unauthenticated_delivery")
+ public void setUnauthenticatedDelivery(@Auth Account account) {
+ assert(account.getAuthenticatedDevice().isPresent());
+ account.getAuthenticatedDevice().get().setUnauthenticatedDeliverySupported(true);
+ accounts.update(account);
+ }
+
@VisibleForTesting protected VerificationCode generateVerificationCode() {
SecureRandom random = new SecureRandom();
int randomInt = 100000 + random.nextInt(900000);
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java
index cf5582ab3..9023eba9c 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2013 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -20,13 +20,9 @@ import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.annotation.Timed;
-import com.google.common.base.Optional;
-import org.apache.commons.codec.DecoderException;
-import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.DirectoryCredentialsGenerator;
-import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration;
import org.whispersystems.textsecuregcm.entities.ClientContact;
import org.whispersystems.textsecuregcm.entities.ClientContactTokens;
import org.whispersystems.textsecuregcm.entities.ClientContacts;
@@ -49,6 +45,7 @@ import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import static com.codahale.metrics.MetricRegistry.name;
import io.dropwizard.auth.Auth;
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/FederationController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/FederationController.java
deleted file mode 100644
index e00c98376..000000000
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/FederationController.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.whispersystems.textsecuregcm.controllers;
-
-import org.whispersystems.textsecuregcm.storage.AccountsManager;
-
-public class FederationController {
-
- protected final AccountsManager accounts;
- protected final AttachmentController attachmentController;
- protected final MessageController messageController;
-
- public FederationController(AccountsManager accounts,
- AttachmentController attachmentController,
- MessageController messageController)
- {
- this.accounts = accounts;
- this.attachmentController = attachmentController;
- this.messageController = messageController;
- }
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/FederationControllerV1.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/FederationControllerV1.java
deleted file mode 100644
index 5bdcba059..000000000
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/FederationControllerV1.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * Copyright (C) 2013 Open WhisperSystems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package org.whispersystems.textsecuregcm.controllers;
-
-import com.codahale.metrics.annotation.Timed;
-import com.google.common.base.Optional;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.whispersystems.textsecuregcm.entities.AccountCount;
-import org.whispersystems.textsecuregcm.entities.AttachmentUri;
-import org.whispersystems.textsecuregcm.entities.ClientContact;
-import org.whispersystems.textsecuregcm.entities.ClientContacts;
-import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
-import org.whispersystems.textsecuregcm.federation.FederatedPeer;
-import org.whispersystems.textsecuregcm.federation.NonLimitedAccount;
-import org.whispersystems.textsecuregcm.storage.Account;
-import org.whispersystems.textsecuregcm.storage.AccountsManager;
-import org.whispersystems.textsecuregcm.util.Util;
-
-import javax.validation.Valid;
-import javax.ws.rs.GET;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import java.io.IOException;
-import java.util.LinkedList;
-import java.util.List;
-
-import io.dropwizard.auth.Auth;
-
-@Path("/v1/federation")
-public class FederationControllerV1 extends FederationController {
-
- private final Logger logger = LoggerFactory.getLogger(FederationControllerV1.class);
-
- private static final int ACCOUNT_CHUNK_SIZE = 10000;
-
- public FederationControllerV1(AccountsManager accounts,
- AttachmentController attachmentController,
- MessageController messageController)
- {
- super(accounts, attachmentController, messageController);
- }
-
- @Timed
- @GET
- @Path("/attachment/{attachmentId}")
- @Produces(MediaType.APPLICATION_JSON)
- public AttachmentUri getSignedAttachmentUri(@Auth FederatedPeer peer,
- @PathParam("attachmentId") long attachmentId)
- throws IOException
- {
- return attachmentController.redirectToAttachment(new NonLimitedAccount("Unknown", -1, peer.getName()),
- attachmentId, Optional.absent());
- }
-
- @Timed
- @PUT
- @Path("/messages/{source}/{sourceDeviceId}/{destination}")
- public void sendMessages(@Auth FederatedPeer peer,
- @PathParam("source") String source,
- @PathParam("sourceDeviceId") long sourceDeviceId,
- @PathParam("destination") String destination,
- @Valid IncomingMessageList messages)
- throws IOException
- {
- try {
- messages.setRelay(null);
- messageController.sendMessage(new NonLimitedAccount(source, sourceDeviceId, peer.getName()), destination, messages);
- } catch (RateLimitExceededException e) {
- logger.warn("Rate limiting on federated channel", e);
- throw new IOException(e);
- }
- }
-
- @Timed
- @GET
- @Path("/user_count")
- @Produces(MediaType.APPLICATION_JSON)
- public AccountCount getUserCount(@Auth FederatedPeer peer) {
- return new AccountCount((int)accounts.getCount());
- }
-
- @Timed
- @GET
- @Path("/user_tokens/{offset}")
- @Produces(MediaType.APPLICATION_JSON)
- public ClientContacts getUserTokens(@Auth FederatedPeer peer,
- @PathParam("offset") int offset)
- {
- List accountList = accounts.getAll(offset, ACCOUNT_CHUNK_SIZE);
- List clientContacts = new LinkedList<>();
-
- for (Account account : accountList) {
- byte[] token = Util.getContactToken(account.getNumber());
- ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported(), account.isVideoSupported());
-
- if (!account.isActive()) {
- clientContact.setInactive(true);
- }
-
- clientContacts.add(clientContact);
- }
-
- return new ClientContacts(clientContacts);
- }
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/FederationControllerV2.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/FederationControllerV2.java
deleted file mode 100644
index 57d877799..000000000
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/FederationControllerV2.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.whispersystems.textsecuregcm.controllers;
-
-import com.codahale.metrics.annotation.Timed;
-import com.google.common.base.Optional;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.whispersystems.textsecuregcm.entities.PreKeyResponse;
-import org.whispersystems.textsecuregcm.federation.FederatedPeer;
-import org.whispersystems.textsecuregcm.federation.NonLimitedAccount;
-import org.whispersystems.textsecuregcm.storage.AccountsManager;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import java.io.IOException;
-
-import io.dropwizard.auth.Auth;
-
-@Path("/v2/federation")
-public class FederationControllerV2 extends FederationController {
-
- private final Logger logger = LoggerFactory.getLogger(FederationControllerV2.class);
-
- private final KeysController keysController;
-
- public FederationControllerV2(AccountsManager accounts, AttachmentController attachmentController, MessageController messageController, KeysController keysController) {
- super(accounts, attachmentController, messageController);
- this.keysController = keysController;
- }
-
- @Timed
- @GET
- @Path("/key/{number}/{device}")
- @Produces(MediaType.APPLICATION_JSON)
- public Optional getKeysV2(@Auth FederatedPeer peer,
- @PathParam("number") String number,
- @PathParam("device") String device)
- throws IOException
- {
- try {
- return keysController.getDeviceKeys(new NonLimitedAccount("Unknown", -1, peer.getName()),
- number, device, Optional.absent());
- } catch (RateLimitExceededException e) {
- logger.warn("Rate limiting on federated channel", e);
- throw new IOException(e);
- }
- }
-
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysController.java
index 68a11a8fb..ab27504e5 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysController.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysController.java
@@ -17,18 +17,17 @@
package org.whispersystems.textsecuregcm.controllers;
import com.codahale.metrics.annotation.Timed;
-import com.google.common.base.Optional;
import org.skife.jdbi.v2.exceptions.UnableToExecuteStatementException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.whispersystems.textsecuregcm.entities.PreKeyCount;
-import org.whispersystems.textsecuregcm.entities.PreKeyResponseItem;
-import org.whispersystems.textsecuregcm.entities.PreKeyResponse;
-import org.whispersystems.textsecuregcm.entities.PreKeyState;
+import org.whispersystems.textsecuregcm.auth.Anonymous;
+import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.entities.PreKey;
+import org.whispersystems.textsecuregcm.entities.PreKeyCount;
+import org.whispersystems.textsecuregcm.entities.PreKeyResponse;
+import org.whispersystems.textsecuregcm.entities.PreKeyResponseItem;
+import org.whispersystems.textsecuregcm.entities.PreKeyState;
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
-import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
-import org.whispersystems.textsecuregcm.federation.NoSuchPeerException;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
@@ -39,36 +38,34 @@ import org.whispersystems.textsecuregcm.storage.Keys;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import io.dropwizard.auth.Auth;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Path("/v2/keys")
public class KeysController {
private static final Logger logger = LoggerFactory.getLogger(KeysController.class);
- private final RateLimiters rateLimiters;
- private final Keys keys;
- private final AccountsManager accounts;
- private final FederatedClientManager federatedClientManager;
+ private final RateLimiters rateLimiters;
+ private final Keys keys;
+ private final AccountsManager accounts;
- public KeysController(RateLimiters rateLimiters, Keys keys, AccountsManager accounts,
- FederatedClientManager federatedClientManager)
- {
- this.rateLimiters = rateLimiters;
- this.keys = keys;
- this.accounts = accounts;
- this.federatedClientManager = federatedClientManager;
+ public KeysController(RateLimiters rateLimiters, Keys keys, AccountsManager accounts) {
+ this.rateLimiters = rateLimiters;
+ this.keys = keys;
+ this.accounts = accounts;
}
@GET
@@ -111,50 +108,49 @@ public class KeysController {
@GET
@Path("/{number}/{device_id}")
@Produces(MediaType.APPLICATION_JSON)
- public Optional getDeviceKeys(@Auth Account account,
- @PathParam("number") String number,
- @PathParam("device_id") String deviceId,
- @QueryParam("relay") Optional relay)
+ public Optional getDeviceKeys(@Auth Optional account,
+ @HeaderParam(OptionalAccess.UNIDENTIFIED) Optional accessKey,
+ @PathParam("number") String number,
+ @PathParam("device_id") String deviceId)
throws RateLimitExceededException
{
- try {
- if (relay.isPresent()) {
- return federatedClientManager.getClient(relay.get()).getKeysV2(number, deviceId);
- }
+ if (!account.isPresent() && !accessKey.isPresent()) {
+ throw new WebApplicationException(Response.Status.UNAUTHORIZED);
+ }
- Account target = getAccount(number, deviceId);
+ if (account.isPresent()) {
+ rateLimiters.getPreKeysLimiter().validate(account.get().getNumber() + "__" + number + "." + deviceId);
+ }
- if (account.isRateLimited()) {
- rateLimiters.getPreKeysLimiter().validate(account.getNumber() + "__" + number + "." + deviceId);
- }
+ Optional target = accounts.get(number);
+ OptionalAccess.verify(account, accessKey, target, deviceId);
- Optional> targetKeys = getLocalKeys(target, deviceId);
- List devices = new LinkedList<>();
+ assert(target.isPresent());
- for (Device device : target.getDevices()) {
- if (device.isActive() && (deviceId.equals("*") || device.getId() == Long.parseLong(deviceId))) {
- SignedPreKey signedPreKey = device.getSignedPreKey();
- PreKey preKey = null;
+ Optional> targetKeys = getLocalKeys(target.get(), deviceId);
+ List devices = new LinkedList<>();
- if (targetKeys.isPresent()) {
- for (KeyRecord keyRecord : targetKeys.get()) {
- if (!keyRecord.isLastResort() && keyRecord.getDeviceId() == device.getId()) {
- preKey = new PreKey(keyRecord.getKeyId(), keyRecord.getPublicKey());
- }
+ for (Device device : target.get().getDevices()) {
+ if (device.isActive() && (deviceId.equals("*") || device.getId() == Long.parseLong(deviceId))) {
+ SignedPreKey signedPreKey = device.getSignedPreKey();
+ PreKey preKey = null;
+
+ if (targetKeys.isPresent()) {
+ for (KeyRecord keyRecord : targetKeys.get()) {
+ if (!keyRecord.isLastResort() && keyRecord.getDeviceId() == device.getId()) {
+ preKey = new PreKey(keyRecord.getKeyId(), keyRecord.getPublicKey());
}
}
+ }
- if (signedPreKey != null || preKey != null) {
- devices.add(new PreKeyResponseItem(device.getId(), device.getRegistrationId(), signedPreKey, preKey));
- }
+ if (signedPreKey != null || preKey != null) {
+ devices.add(new PreKeyResponseItem(device.getId(), device.getRegistrationId(), signedPreKey, preKey));
}
}
-
- if (devices.isEmpty()) return Optional.absent();
- else return Optional.of(new PreKeyResponse(target.getIdentityKey(), devices));
- } catch (NoSuchPeerException | NoSuchUserException e) {
- throw new WebApplicationException(Response.status(404).build());
}
+
+ if (devices.isEmpty()) return Optional.empty();
+ else return Optional.of(new PreKeyResponse(target.get().getIdentityKey(), devices));
}
@Timed
@@ -176,12 +172,10 @@ public class KeysController {
SignedPreKey signedPreKey = device.getSignedPreKey();
if (signedPreKey != null) return Optional.of(signedPreKey);
- else return Optional.absent();
+ else return Optional.empty();
}
- private Optional> getLocalKeys(Account destination, String deviceIdSelector)
- throws NoSuchUserException
- {
+ private Optional> getLocalKeys(Account destination, String deviceIdSelector) {
try {
if (deviceIdSelector.equals("*")) {
return keys.get(destination.getNumber());
@@ -202,30 +196,4 @@ public class KeysController {
throw new WebApplicationException(Response.status(422).build());
}
}
-
- private Account getAccount(String number, String deviceSelector)
- throws NoSuchUserException
- {
- try {
- Optional account = accounts.get(number);
-
- if (!account.isPresent() || !account.get().isActive()) {
- throw new NoSuchUserException("No active account");
- }
-
- if (!deviceSelector.equals("*")) {
- long deviceId = Long.parseLong(deviceSelector);
-
- Optional targetDevice = account.get().getDevice(deviceId);
-
- if (!targetDevice.isPresent() || !targetDevice.get().isActive()) {
- throw new NoSuchUserException("No active device");
- }
- }
-
- return account.get();
- } catch (NumberFormatException e) {
- throw new WebApplicationException(Response.status(422).build());
- }
- }
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java
index ff09f88b4..e53faf6d9 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java
@@ -17,10 +17,11 @@
package org.whispersystems.textsecuregcm.controllers;
import com.codahale.metrics.annotation.Timed;
-import com.google.common.base.Optional;
import com.google.protobuf.ByteString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.whispersystems.textsecuregcm.auth.Anonymous;
+import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.entities.IncomingMessage;
import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
import org.whispersystems.textsecuregcm.entities.MessageProtos.Envelope;
@@ -29,15 +30,11 @@ import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntity;
import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntityList;
import org.whispersystems.textsecuregcm.entities.SendMessageResponse;
import org.whispersystems.textsecuregcm.entities.StaleDevices;
-import org.whispersystems.textsecuregcm.federation.FederatedClient;
-import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
-import org.whispersystems.textsecuregcm.federation.NoSuchPeerException;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.push.ApnFallbackManager;
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
import org.whispersystems.textsecuregcm.push.PushSender;
import org.whispersystems.textsecuregcm.push.ReceiptSender;
-import org.whispersystems.textsecuregcm.push.TransientPushFailureException;
import org.whispersystems.textsecuregcm.redis.RedisOperation;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
@@ -51,6 +48,7 @@ import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -62,10 +60,13 @@ import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import io.dropwizard.auth.Auth;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Path("/v1/messages")
public class MessageController {
@@ -74,7 +75,6 @@ public class MessageController {
private final RateLimiters rateLimiters;
private final PushSender pushSender;
private final ReceiptSender receiptSender;
- private final FederatedClientManager federatedClientManager;
private final AccountsManager accountsManager;
private final MessagesManager messagesManager;
private final ApnFallbackManager apnFallbackManager;
@@ -84,7 +84,6 @@ public class MessageController {
ReceiptSender receiptSender,
AccountsManager accountsManager,
MessagesManager messagesManager,
- FederatedClientManager federatedClientManager,
ApnFallbackManager apnFallbackManager)
{
this.rateLimiters = rateLimiters;
@@ -92,7 +91,6 @@ public class MessageController {
this.receiptSender = receiptSender;
this.accountsManager = accountsManager;
this.messagesManager = messagesManager;
- this.federatedClientManager = federatedClientManager;
this.apnFallbackManager = apnFallbackManager;
}
@@ -101,22 +99,41 @@ public class MessageController {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
- public SendMessageResponse sendMessage(@Auth Account source,
- @PathParam("destination") String destinationName,
- @Valid IncomingMessageList messages)
- throws IOException, RateLimitExceededException
+ public SendMessageResponse sendMessage(@Auth Optional source,
+ @HeaderParam(OptionalAccess.UNIDENTIFIED) Optional accessKey,
+ @PathParam("destination") String destinationName,
+ @Valid IncomingMessageList messages)
+ throws RateLimitExceededException
{
- if (!source.getNumber().equals(destinationName)) {
- rateLimiters.getMessagesLimiter().validate(source.getNumber() + "__" + destinationName);
+ if (!source.isPresent() && !accessKey.isPresent()) {
+ throw new WebApplicationException(Response.Status.UNAUTHORIZED);
+ }
+
+ if (source.isPresent() && !source.get().getNumber().equals(destinationName)) {
+ rateLimiters.getMessagesLimiter().validate(source.get().getNumber() + "__" + destinationName);
}
try {
- boolean isSyncMessage = source.getNumber().equals(destinationName);
+ boolean isSyncMessage = source.isPresent() && source.get().getNumber().equals(destinationName);
- if (Util.isEmpty(messages.getRelay())) sendLocalMessage(source, destinationName, messages, isSyncMessage);
- else sendRelayMessage(source, destinationName, messages, isSyncMessage);
+ Optional destination;
- return new SendMessageResponse(!isSyncMessage && source.getActiveDeviceCount() > 1);
+ if (!isSyncMessage) destination = accountsManager.get(destinationName);
+ else destination = source;
+
+ OptionalAccess.verify(source, accessKey, destination);
+ validateCompleteDeviceList(destination.get(), messages.getMessages(), isSyncMessage);
+ validateRegistrationIds(destination.get(), messages.getMessages());
+
+ for (IncomingMessage incomingMessage : messages.getMessages()) {
+ Optional destinationDevice = destination.get().getDevice(incomingMessage.getDestinationDeviceId());
+
+ if (destinationDevice.isPresent()) {
+ sendMessage(source, destination.get(), destinationDevice.get(), messages.getTimestamp(), incomingMessage);
+ }
+ }
+
+ return new SendMessageResponse(!isSyncMessage && source.isPresent() && source.get().getActiveDeviceCount() > 1);
} catch (NoSuchUserException e) {
throw new WebApplicationException(Response.status(404).build());
} catch (MismatchedDevicesException e) {
@@ -130,8 +147,6 @@ public class MessageController {
.type(MediaType.APPLICATION_JSON)
.entity(new StaleDevices(e.getStaleDevices()))
.build());
- } catch (InvalidDestinationException e) {
- throw new WebApplicationException(Response.status(400).build());
}
}
@@ -155,7 +170,6 @@ public class MessageController {
public void removePendingMessage(@Auth Account account,
@PathParam("source") String source,
@PathParam("timestamp") long timestamp)
- throws IOException
{
try {
WebSocketConnection.messageTime.update(System.currentTimeMillis() - timestamp);
@@ -167,45 +181,41 @@ public class MessageController {
if (message.isPresent() && message.get().getType() != Envelope.Type.RECEIPT_VALUE) {
receiptSender.sendReceipt(account,
message.get().getSource(),
- message.get().getTimestamp(),
- Optional.fromNullable(message.get().getRelay()));
+ message.get().getTimestamp());
}
} catch (NotPushRegisteredException e) {
logger.info("User no longer push registered for delivery receipt: " + e.getMessage());
- } catch (NoSuchUserException | TransientPushFailureException e) {
+ } catch (NoSuchUserException e) {
logger.warn("Sending delivery receipt", e);
}
}
+ @Timed
+ @DELETE
+ @Path("/uuid/{uuid}")
+ public void removePendingMessage(@Auth Account account, @PathParam("uuid") UUID uuid) {
+ try {
+ Optional message = messagesManager.delete(account.getNumber(),
+ account.getAuthenticatedDevice().get().getId(),
+ uuid);
- private void sendLocalMessage(Account source,
- String destinationName,
- IncomingMessageList messages,
- boolean isSyncMessage)
- throws NoSuchUserException, MismatchedDevicesException, StaleDevicesException
- {
- Account destination;
+ message.ifPresent(outgoingMessageEntity -> WebSocketConnection.messageTime.update(System.currentTimeMillis() - outgoingMessageEntity.getTimestamp()));
- if (!isSyncMessage) destination = getDestinationAccount(destinationName);
- else destination = source;
-
- validateCompleteDeviceList(destination, messages.getMessages(), isSyncMessage);
- validateRegistrationIds(destination, messages.getMessages());
-
- for (IncomingMessage incomingMessage : messages.getMessages()) {
- Optional destinationDevice = destination.getDevice(incomingMessage.getDestinationDeviceId());
-
- if (destinationDevice.isPresent()) {
- sendLocalMessage(source, destination, destinationDevice.get(), messages.getTimestamp(), incomingMessage);
+ if (message.isPresent() && !Util.isEmpty(message.get().getSource()) && message.get().getType() != Envelope.Type.RECEIPT_VALUE) {
+ receiptSender.sendReceipt(account, message.get().getSource(), message.get().getTimestamp());
}
+ } catch (NoSuchUserException e) {
+ logger.warn("Sending delivery receipt", e);
+ } catch (NotPushRegisteredException e) {
+ logger.info("User no longer push registered for delivery receipt: " + e.getMessage());
}
}
- private void sendLocalMessage(Account source,
- Account destinationAccount,
- Device destinationDevice,
- long timestamp,
- IncomingMessage incomingMessage)
+ private void sendMessage(Optional source,
+ Account destinationAccount,
+ Device destinationDevice,
+ long timestamp,
+ IncomingMessage incomingMessage)
throws NoSuchUserException
{
try {
@@ -214,9 +224,13 @@ public class MessageController {
Envelope.Builder messageBuilder = Envelope.newBuilder();
messageBuilder.setType(Envelope.Type.valueOf(incomingMessage.getType()))
- .setSource(source.getNumber())
.setTimestamp(timestamp == 0 ? System.currentTimeMillis() : timestamp)
- .setSourceDevice((int) source.getAuthenticatedDevice().get().getId());
+ .setServerTimestamp(System.currentTimeMillis());
+
+ if (source.isPresent()) {
+ messageBuilder.setSource(source.get().getNumber())
+ .setSourceDevice((int)source.get().getAuthenticatedDevice().get().getId());
+ }
if (messageBody.isPresent()) {
messageBuilder.setLegacyMessage(ByteString.copyFrom(messageBody.get()));
@@ -226,10 +240,6 @@ public class MessageController {
messageBuilder.setContent(ByteString.copyFrom(messageContent.get()));
}
- if (source.getRelay().isPresent()) {
- messageBuilder.setRelay(source.getRelay().get());
- }
-
pushSender.sendMessage(destinationAccount, destinationDevice, messageBuilder.build());
} catch (NotPushRegisteredException e) {
if (destinationDevice.isMaster()) throw new NoSuchUserException(e);
@@ -237,35 +247,6 @@ public class MessageController {
}
}
- private void sendRelayMessage(Account source,
- String destinationName,
- IncomingMessageList messages,
- boolean isSyncMessage)
- throws IOException, NoSuchUserException, InvalidDestinationException
- {
- if (isSyncMessage) throw new InvalidDestinationException("Transcript messages can't be relayed!");
-
- try {
- FederatedClient client = federatedClientManager.getClient(messages.getRelay());
- client.sendMessages(source.getNumber(), source.getAuthenticatedDevice().get().getId(),
- destinationName, messages);
- } catch (NoSuchPeerException e) {
- throw new NoSuchUserException(e);
- }
- }
-
- private Account getDestinationAccount(String destination)
- throws NoSuchUserException
- {
- Optional account = accountsManager.get(destination);
-
- if (!account.isPresent() || !account.get().isActive()) {
- throw new NoSuchUserException(destination);
- }
-
- return account.get();
- }
-
private void validateRegistrationIds(Account account, List messages)
throws StaleDevicesException
{
@@ -326,24 +307,24 @@ public class MessageController {
}
private Optional getMessageBody(IncomingMessage message) {
- if (Util.isEmpty(message.getBody())) return Optional.absent();
+ if (Util.isEmpty(message.getBody())) return Optional.empty();
try {
return Optional.of(Base64.decode(message.getBody()));
} catch (IOException ioe) {
logger.debug("Bad B64", ioe);
- return Optional.absent();
+ return Optional.empty();
}
}
private Optional getMessageContent(IncomingMessage message) {
- if (Util.isEmpty(message.getContent())) return Optional.absent();
+ if (Util.isEmpty(message.getContent())) return Optional.empty();
try {
return Optional.of(Base64.decode(message.getContent()));
} catch (IOException ioe) {
logger.debug("Bad B64", ioe);
- return Optional.absent();
+ return Optional.empty();
}
}
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java
index d73f74870..80cdb399b 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java
@@ -7,10 +7,12 @@ import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.codahale.metrics.annotation.Timed;
-import com.google.common.base.Optional;
import org.apache.commons.codec.binary.Base64;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.valuehandling.UnwrapValidatedValue;
+import org.whispersystems.textsecuregcm.auth.OptionalAccess;
+import org.whispersystems.textsecuregcm.auth.Anonymous;
+import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
import org.whispersystems.textsecuregcm.configuration.ProfilesConfiguration;
import org.whispersystems.textsecuregcm.entities.Profile;
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
@@ -22,6 +24,7 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.util.Pair;
import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -33,9 +36,11 @@ import javax.ws.rs.core.Response;
import java.security.SecureRandom;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
+import java.util.Optional;
import io.dropwizard.auth.Auth;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Path("/v1/profile")
public class ProfileController {
@@ -75,22 +80,29 @@ public class ProfileController {
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{number}")
- public Profile getProfile(@Auth Account account,
- @PathParam("number") String number,
- @QueryParam("ca") boolean useCaCertificate)
+ public Profile getProfile(@Auth Optional requestAccount,
+ @HeaderParam(OptionalAccess.UNIDENTIFIED) Optional accessKey,
+ @PathParam("number") String number,
+ @QueryParam("ca") boolean useCaCertificate)
throws RateLimitExceededException
{
- rateLimiters.getProfileLimiter().validate(account.getNumber());
-
- Optional accountProfile = accountsManager.get(number);
-
- if (!accountProfile.isPresent()) {
- throw new WebApplicationException(Response.status(404).build());
+ if (!requestAccount.isPresent() && !accessKey.isPresent()) {
+ throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
- return new Profile(accountProfile.get().getName(),
+ if (requestAccount.isPresent()) {
+ rateLimiters.getProfileLimiter().validate(requestAccount.get().getNumber());
+ }
+
+ Optional accountProfile = accountsManager.get(number);
+ OptionalAccess.verify(requestAccount, accessKey, accountProfile);
+
+ //noinspection ConstantConditions,OptionalGetWithoutIsPresent
+ return new Profile(accountProfile.get().getProfileName(),
accountProfile.get().getAvatar(),
- accountProfile.get().getIdentityKey());
+ accountProfile.get().getIdentityKey(),
+ accountProfile.get().isUnauthenticatedDeliverySupported() ? UnidentifiedAccessChecksum.generateFor(accountProfile.get().getUnidentifiedAccessKey()) : null,
+ accountProfile.get().isUnrestrictedUnidentifiedAccess());
}
@Timed
@@ -98,7 +110,7 @@ public class ProfileController {
@Produces(MediaType.APPLICATION_JSON)
@Path("/name/{name}")
public void setProfile(@Auth Account account, @PathParam("name") @UnwrapValidatedValue(true) @Length(min = 72,max= 72) Optional name) {
- account.setName(name.orNull());
+ account.setProfileName(name.orElse(null));
accountsManager.update(account);
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/crypto/Curve.java b/src/main/java/org/whispersystems/textsecuregcm/crypto/Curve.java
new file mode 100644
index 000000000..d44dfb93c
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/crypto/Curve.java
@@ -0,0 +1,98 @@
+package org.whispersystems.textsecuregcm.crypto;
+
+import org.whispersystems.curve25519.Curve25519;
+import org.whispersystems.curve25519.Curve25519KeyPair;
+
+import java.security.InvalidKeyException;
+
+import static org.whispersystems.curve25519.Curve25519.BEST;
+
+public class Curve {
+
+ public static final int DJB_TYPE = 0x05;
+
+ public static ECKeyPair generateKeyPair() {
+ Curve25519KeyPair keyPair = Curve25519.getInstance(BEST).generateKeyPair();
+
+ return new ECKeyPair(new DjbECPublicKey(keyPair.getPublicKey()),
+ new DjbECPrivateKey(keyPair.getPrivateKey()));
+ }
+
+ public static ECPublicKey decodePoint(byte[] bytes, int offset)
+ throws InvalidKeyException
+ {
+ if (bytes == null || bytes.length - offset < 1) {
+ throw new InvalidKeyException("No key type identifier");
+ }
+
+ int type = bytes[offset] & 0xFF;
+
+ switch (type) {
+ case Curve.DJB_TYPE:
+ if (bytes.length - offset < 33) {
+ throw new InvalidKeyException("Bad key length: " + bytes.length);
+ }
+
+ byte[] keyBytes = new byte[32];
+ System.arraycopy(bytes, offset+1, keyBytes, 0, keyBytes.length);
+ return new DjbECPublicKey(keyBytes);
+ default:
+ throw new InvalidKeyException("Bad key type: " + type);
+ }
+ }
+
+ public static ECPrivateKey decodePrivatePoint(byte[] bytes) {
+ return new DjbECPrivateKey(bytes);
+ }
+
+ public static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey)
+ throws InvalidKeyException
+ {
+ if (publicKey == null) {
+ throw new InvalidKeyException("public value is null");
+ }
+
+ if (privateKey == null) {
+ throw new InvalidKeyException("private value is null");
+ }
+
+ if (publicKey.getType() != privateKey.getType()) {
+ throw new InvalidKeyException("Public and private keys must be of the same type!");
+ }
+
+ if (publicKey.getType() == DJB_TYPE) {
+ return Curve25519.getInstance(BEST)
+ .calculateAgreement(((DjbECPublicKey) publicKey).getPublicKey(),
+ ((DjbECPrivateKey) privateKey).getPrivateKey());
+ } else {
+ throw new InvalidKeyException("Unknown type: " + publicKey.getType());
+ }
+ }
+
+ public static byte[] calculateSignature(ECPrivateKey signingKey, byte[] message)
+ throws InvalidKeyException
+ {
+ if (signingKey == null || message == null) {
+ throw new InvalidKeyException("Values must not be null");
+ }
+
+ if (signingKey.getType() == DJB_TYPE) {
+ return Curve25519.getInstance(BEST)
+ .calculateSignature(((DjbECPrivateKey) signingKey).getPrivateKey(), message);
+ } else {
+ throw new InvalidKeyException("Unknown type: " + signingKey.getType());
+ }
+ }
+
+ public static boolean verifySignature(ECPublicKey signingKey, byte[] message, byte[] signature)
+ throws InvalidKeyException
+ {
+ if (signingKey.getType() == DJB_TYPE) {
+ return Curve25519.getInstance(BEST)
+ .verifySignature(((DjbECPublicKey) signingKey).getPublicKey(), message, signature);
+ } else {
+ throw new InvalidKeyException("Unknown type: " + signingKey.getType());
+ }
+ }
+
+}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/crypto/DjbECPrivateKey.java b/src/main/java/org/whispersystems/textsecuregcm/crypto/DjbECPrivateKey.java
new file mode 100644
index 000000000..ff8c64cb1
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/crypto/DjbECPrivateKey.java
@@ -0,0 +1,24 @@
+package org.whispersystems.textsecuregcm.crypto;
+
+public class DjbECPrivateKey implements ECPrivateKey {
+
+ private final byte[] privateKey;
+
+ DjbECPrivateKey(byte[] privateKey) {
+ this.privateKey = privateKey;
+ }
+
+ @Override
+ public byte[] serialize() {
+ return privateKey;
+ }
+
+ @Override
+ public int getType() {
+ return Curve.DJB_TYPE;
+ }
+
+ public byte[] getPrivateKey() {
+ return privateKey;
+ }
+}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/crypto/DjbECPublicKey.java b/src/main/java/org/whispersystems/textsecuregcm/crypto/DjbECPublicKey.java
new file mode 100644
index 000000000..87fdbc42c
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/crypto/DjbECPublicKey.java
@@ -0,0 +1,49 @@
+package org.whispersystems.textsecuregcm.crypto;
+
+import org.whispersystems.textsecuregcm.util.ByteUtil;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+
+public class DjbECPublicKey implements ECPublicKey {
+
+ private final byte[] publicKey;
+
+ DjbECPublicKey(byte[] publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] type = {Curve.DJB_TYPE};
+ return ByteUtil.combine(type, publicKey);
+ }
+
+ @Override
+ public int getType() {
+ return Curve.DJB_TYPE;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) return false;
+ if (!(other instanceof DjbECPublicKey)) return false;
+
+ DjbECPublicKey that = (DjbECPublicKey)other;
+ return Arrays.equals(this.publicKey, that.publicKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(publicKey);
+ }
+
+ @Override
+ public int compareTo(ECPublicKey another) {
+ return new BigInteger(publicKey).compareTo(new BigInteger(((DjbECPublicKey)another).publicKey));
+ }
+
+ public byte[] getPublicKey() {
+ return publicKey;
+ }
+}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/crypto/ECKeyPair.java b/src/main/java/org/whispersystems/textsecuregcm/crypto/ECKeyPair.java
new file mode 100644
index 000000000..c0ae79031
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/crypto/ECKeyPair.java
@@ -0,0 +1,20 @@
+package org.whispersystems.textsecuregcm.crypto;
+
+public class ECKeyPair {
+
+ private final ECPublicKey publicKey;
+ private final ECPrivateKey privateKey;
+
+ ECKeyPair(ECPublicKey publicKey, ECPrivateKey privateKey) {
+ this.publicKey = publicKey;
+ this.privateKey = privateKey;
+ }
+
+ public ECPublicKey getPublicKey() {
+ return publicKey;
+ }
+
+ public ECPrivateKey getPrivateKey() {
+ return privateKey;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/whispersystems/textsecuregcm/crypto/ECPrivateKey.java b/src/main/java/org/whispersystems/textsecuregcm/crypto/ECPrivateKey.java
new file mode 100644
index 000000000..2cc5652f6
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/crypto/ECPrivateKey.java
@@ -0,0 +1,7 @@
+package org.whispersystems.textsecuregcm.crypto;
+
+public interface ECPrivateKey {
+ public byte[] serialize();
+ public int getType();
+}
+
diff --git a/src/main/java/org/whispersystems/textsecuregcm/crypto/ECPublicKey.java b/src/main/java/org/whispersystems/textsecuregcm/crypto/ECPublicKey.java
new file mode 100644
index 000000000..4dd1e27f9
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/crypto/ECPublicKey.java
@@ -0,0 +1,10 @@
+package org.whispersystems.textsecuregcm.crypto;
+
+public interface ECPublicKey extends Comparable {
+
+ public static final int KEY_SIZE = 33;
+
+ public byte[] serialize();
+
+ public int getType();
+}
\ No newline at end of file
diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/AccountAttributes.java b/src/main/java/org/whispersystems/textsecuregcm/entities/AccountAttributes.java
index 5b0081e31..e598b060a 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/entities/AccountAttributes.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/entities/AccountAttributes.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2013 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -46,6 +46,12 @@ public class AccountAttributes {
@JsonProperty
private String pin;
+ @JsonProperty
+ private byte[] unidentifiedAccessKey;
+
+ @JsonProperty
+ private boolean unrestrictedUnidentifiedAccess;
+
public AccountAttributes() {}
@VisibleForTesting
@@ -91,4 +97,12 @@ public class AccountAttributes {
public String getPin() {
return pin;
}
+
+ public byte[] getUnidentifiedAccessKey() {
+ return unidentifiedAccessKey;
+ }
+
+ public boolean isUnrestrictedUnidentifiedAccess() {
+ return unrestrictedUnidentifiedAccess;
+ }
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/DeliveryCertificate.java b/src/main/java/org/whispersystems/textsecuregcm/entities/DeliveryCertificate.java
new file mode 100644
index 000000000..c925bfff5
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/entities/DeliveryCertificate.java
@@ -0,0 +1,50 @@
+package org.whispersystems.textsecuregcm.entities;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.google.common.annotations.VisibleForTesting;
+import org.whispersystems.textsecuregcm.util.Base64;
+import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
+
+import java.io.IOException;
+
+public class DeliveryCertificate {
+
+ @JsonProperty
+ @JsonSerialize(using = ByteArraySerializer.class)
+ @JsonDeserialize(using = ByteArrayDeserializer.class)
+ private byte[] certificate;
+
+ public DeliveryCertificate(byte[] certificate) {
+ this.certificate = certificate;
+ }
+
+ public DeliveryCertificate() {}
+
+ @VisibleForTesting
+ public byte[] getCertificate() {
+ return certificate;
+ }
+
+ public static class ByteArraySerializer extends JsonSerializer {
+ @Override
+ public void serialize(byte[] bytes, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+ jsonGenerator.writeString(Base64.encodeBytes(bytes));
+ }
+ }
+
+ public static class ByteArrayDeserializer extends JsonDeserializer {
+ @Override
+ public byte[] deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+ return Base64.decode(jsonParser.getValueAsString());
+ }
+ }
+}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/IncomingMessageList.java b/src/main/java/org/whispersystems/textsecuregcm/entities/IncomingMessageList.java
index 755744bda..092cc63a8 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/entities/IncomingMessageList.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/entities/IncomingMessageList.java
@@ -29,9 +29,6 @@ public class IncomingMessageList {
@Valid
private List messages;
- @JsonProperty
- private String relay;
-
@JsonProperty
private long timestamp;
@@ -41,14 +38,6 @@ public class IncomingMessageList {
return messages;
}
- public String getRelay() {
- return relay;
- }
-
- public void setRelay(String relay) {
- this.relay = relay;
- }
-
public long getTimestamp() {
return timestamp;
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/MessageProtos.java b/src/main/java/org/whispersystems/textsecuregcm/entities/MessageProtos.java
index 799beaaa5..5b52357ea 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/entities/MessageProtos.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/entities/MessageProtos.java
@@ -106,6 +106,31 @@ public final class MessageProtos {
*
*/
com.google.protobuf.ByteString getContent();
+
+ // optional string serverGuid = 9;
+ /**
+ * optional string serverGuid = 9;
+ */
+ boolean hasServerGuid();
+ /**
+ * optional string serverGuid = 9;
+ */
+ java.lang.String getServerGuid();
+ /**
+ * optional string serverGuid = 9;
+ */
+ com.google.protobuf.ByteString
+ getServerGuidBytes();
+
+ // optional uint64 server_timestamp = 10;
+ /**
+ * optional uint64 server_timestamp = 10;
+ */
+ boolean hasServerTimestamp();
+ /**
+ * optional uint64 server_timestamp = 10;
+ */
+ long getServerTimestamp();
}
/**
* Protobuf type {@code textsecure.Envelope}
@@ -199,6 +224,16 @@ public final class MessageProtos {
content_ = input.readBytes();
break;
}
+ case 74: {
+ bitField0_ |= 0x00000080;
+ serverGuid_ = input.readBytes();
+ break;
+ }
+ case 80: {
+ bitField0_ |= 0x00000100;
+ serverTimestamp_ = input.readUInt64();
+ break;
+ }
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -263,6 +298,10 @@ public final class MessageProtos {
* RECEIPT = 5;
*/
RECEIPT(4, 5),
+ /**
+ * UNIDENTIFIED_SENDER = 6;
+ */
+ UNIDENTIFIED_SENDER(5, 6),
;
/**
@@ -285,6 +324,10 @@ public final class MessageProtos {
* RECEIPT = 5;
*/
public static final int RECEIPT_VALUE = 5;
+ /**
+ * UNIDENTIFIED_SENDER = 6;
+ */
+ public static final int UNIDENTIFIED_SENDER_VALUE = 6;
public final int getNumber() { return value; }
@@ -296,6 +339,7 @@ public final class MessageProtos {
case 2: return KEY_EXCHANGE;
case 3: return PREKEY_BUNDLE;
case 5: return RECEIPT;
+ case 6: return UNIDENTIFIED_SENDER;
default: return null;
}
}
@@ -530,6 +574,65 @@ public final class MessageProtos {
return content_;
}
+ // optional string serverGuid = 9;
+ public static final int SERVERGUID_FIELD_NUMBER = 9;
+ private java.lang.Object serverGuid_;
+ /**
+ * optional string serverGuid = 9;
+ */
+ public boolean hasServerGuid() {
+ return ((bitField0_ & 0x00000080) == 0x00000080);
+ }
+ /**
+ * optional string serverGuid = 9;
+ */
+ public java.lang.String getServerGuid() {
+ java.lang.Object ref = serverGuid_;
+ if (ref instanceof java.lang.String) {
+ return (java.lang.String) ref;
+ } else {
+ com.google.protobuf.ByteString bs =
+ (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ if (bs.isValidUtf8()) {
+ serverGuid_ = s;
+ }
+ return s;
+ }
+ }
+ /**
+ * optional string serverGuid = 9;
+ */
+ public com.google.protobuf.ByteString
+ getServerGuidBytes() {
+ java.lang.Object ref = serverGuid_;
+ if (ref instanceof java.lang.String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8(
+ (java.lang.String) ref);
+ serverGuid_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ // optional uint64 server_timestamp = 10;
+ public static final int SERVER_TIMESTAMP_FIELD_NUMBER = 10;
+ private long serverTimestamp_;
+ /**
+ * optional uint64 server_timestamp = 10;
+ */
+ public boolean hasServerTimestamp() {
+ return ((bitField0_ & 0x00000100) == 0x00000100);
+ }
+ /**
+ * optional uint64 server_timestamp = 10;
+ */
+ public long getServerTimestamp() {
+ return serverTimestamp_;
+ }
+
private void initFields() {
type_ = org.whispersystems.textsecuregcm.entities.MessageProtos.Envelope.Type.UNKNOWN;
source_ = "";
@@ -538,6 +641,8 @@ public final class MessageProtos {
timestamp_ = 0L;
legacyMessage_ = com.google.protobuf.ByteString.EMPTY;
content_ = com.google.protobuf.ByteString.EMPTY;
+ serverGuid_ = "";
+ serverTimestamp_ = 0L;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -572,6 +677,12 @@ public final class MessageProtos {
if (((bitField0_ & 0x00000040) == 0x00000040)) {
output.writeBytes(8, content_);
}
+ if (((bitField0_ & 0x00000080) == 0x00000080)) {
+ output.writeBytes(9, getServerGuidBytes());
+ }
+ if (((bitField0_ & 0x00000100) == 0x00000100)) {
+ output.writeUInt64(10, serverTimestamp_);
+ }
getUnknownFields().writeTo(output);
}
@@ -609,6 +720,14 @@ public final class MessageProtos {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(8, content_);
}
+ if (((bitField0_ & 0x00000080) == 0x00000080)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeBytesSize(9, getServerGuidBytes());
+ }
+ if (((bitField0_ & 0x00000100) == 0x00000100)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeUInt64Size(10, serverTimestamp_);
+ }
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@@ -739,6 +858,10 @@ public final class MessageProtos {
bitField0_ = (bitField0_ & ~0x00000020);
content_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000040);
+ serverGuid_ = "";
+ bitField0_ = (bitField0_ & ~0x00000080);
+ serverTimestamp_ = 0L;
+ bitField0_ = (bitField0_ & ~0x00000100);
return this;
}
@@ -795,6 +918,14 @@ public final class MessageProtos {
to_bitField0_ |= 0x00000040;
}
result.content_ = content_;
+ if (((from_bitField0_ & 0x00000080) == 0x00000080)) {
+ to_bitField0_ |= 0x00000080;
+ }
+ result.serverGuid_ = serverGuid_;
+ if (((from_bitField0_ & 0x00000100) == 0x00000100)) {
+ to_bitField0_ |= 0x00000100;
+ }
+ result.serverTimestamp_ = serverTimestamp_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@@ -836,6 +967,14 @@ public final class MessageProtos {
if (other.hasContent()) {
setContent(other.getContent());
}
+ if (other.hasServerGuid()) {
+ bitField0_ |= 0x00000080;
+ serverGuid_ = other.serverGuid_;
+ onChanged();
+ }
+ if (other.hasServerTimestamp()) {
+ setServerTimestamp(other.getServerTimestamp());
+ }
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@@ -1217,6 +1356,113 @@ public final class MessageProtos {
return this;
}
+ // optional string serverGuid = 9;
+ private java.lang.Object serverGuid_ = "";
+ /**
+ * optional string serverGuid = 9;
+ */
+ public boolean hasServerGuid() {
+ return ((bitField0_ & 0x00000080) == 0x00000080);
+ }
+ /**
+ * optional string serverGuid = 9;
+ */
+ public java.lang.String getServerGuid() {
+ java.lang.Object ref = serverGuid_;
+ if (!(ref instanceof java.lang.String)) {
+ java.lang.String s = ((com.google.protobuf.ByteString) ref)
+ .toStringUtf8();
+ serverGuid_ = s;
+ return s;
+ } else {
+ return (java.lang.String) ref;
+ }
+ }
+ /**
+ * optional string serverGuid = 9;
+ */
+ public com.google.protobuf.ByteString
+ getServerGuidBytes() {
+ java.lang.Object ref = serverGuid_;
+ if (ref instanceof String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8(
+ (java.lang.String) ref);
+ serverGuid_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+ /**
+ * optional string serverGuid = 9;
+ */
+ public Builder setServerGuid(
+ java.lang.String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000080;
+ serverGuid_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional string serverGuid = 9;
+ */
+ public Builder clearServerGuid() {
+ bitField0_ = (bitField0_ & ~0x00000080);
+ serverGuid_ = getDefaultInstance().getServerGuid();
+ onChanged();
+ return this;
+ }
+ /**
+ * optional string serverGuid = 9;
+ */
+ public Builder setServerGuidBytes(
+ com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000080;
+ serverGuid_ = value;
+ onChanged();
+ return this;
+ }
+
+ // optional uint64 server_timestamp = 10;
+ private long serverTimestamp_ ;
+ /**
+ * optional uint64 server_timestamp = 10;
+ */
+ public boolean hasServerTimestamp() {
+ return ((bitField0_ & 0x00000100) == 0x00000100);
+ }
+ /**
+ * optional uint64 server_timestamp = 10;
+ */
+ public long getServerTimestamp() {
+ return serverTimestamp_;
+ }
+ /**
+ * optional uint64 server_timestamp = 10;
+ */
+ public Builder setServerTimestamp(long value) {
+ bitField0_ |= 0x00000100;
+ serverTimestamp_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional uint64 server_timestamp = 10;
+ */
+ public Builder clearServerTimestamp() {
+ bitField0_ = (bitField0_ & ~0x00000100);
+ serverTimestamp_ = 0L;
+ onChanged();
+ return this;
+ }
+
// @@protoc_insertion_point(builder_scope:textsecure.Envelope)
}
@@ -1699,6 +1945,2361 @@ public final class MessageProtos {
// @@protoc_insertion_point(class_scope:textsecure.ProvisioningUuid)
}
+ public interface ServerCertificateOrBuilder
+ extends com.google.protobuf.MessageOrBuilder {
+
+ // optional bytes certificate = 1;
+ /**
+ * optional bytes certificate = 1;
+ */
+ boolean hasCertificate();
+ /**
+ * optional bytes certificate = 1;
+ */
+ com.google.protobuf.ByteString getCertificate();
+
+ // optional bytes signature = 2;
+ /**
+ * optional bytes signature = 2;
+ */
+ boolean hasSignature();
+ /**
+ * optional bytes signature = 2;
+ */
+ com.google.protobuf.ByteString getSignature();
+ }
+ /**
+ * Protobuf type {@code textsecure.ServerCertificate}
+ */
+ public static final class ServerCertificate extends
+ com.google.protobuf.GeneratedMessage
+ implements ServerCertificateOrBuilder {
+ // Use ServerCertificate.newBuilder() to construct.
+ private ServerCertificate(com.google.protobuf.GeneratedMessage.Builder> builder) {
+ super(builder);
+ this.unknownFields = builder.getUnknownFields();
+ }
+ private ServerCertificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+ private static final ServerCertificate defaultInstance;
+ public static ServerCertificate getDefaultInstance() {
+ return defaultInstance;
+ }
+
+ public ServerCertificate getDefaultInstanceForType() {
+ return defaultInstance;
+ }
+
+ private final com.google.protobuf.UnknownFieldSet unknownFields;
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet
+ getUnknownFields() {
+ return this.unknownFields;
+ }
+ private ServerCertificate(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ initFields();
+ int mutable_bitField0_ = 0;
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ default: {
+ if (!parseUnknownField(input, unknownFields,
+ extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ case 10: {
+ bitField0_ |= 0x00000001;
+ certificate_ = input.readBytes();
+ break;
+ }
+ case 18: {
+ bitField0_ |= 0x00000002;
+ signature_ = input.readBytes();
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(
+ e.getMessage()).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_ServerCertificate_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_ServerCertificate_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.class, org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Builder.class);
+ }
+
+ public static com.google.protobuf.Parser PARSER =
+ new com.google.protobuf.AbstractParser() {
+ public ServerCertificate parsePartialFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return new ServerCertificate(input, extensionRegistry);
+ }
+ };
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ public interface CertificateOrBuilder
+ extends com.google.protobuf.MessageOrBuilder {
+
+ // optional uint32 id = 1;
+ /**
+ * optional uint32 id = 1;
+ */
+ boolean hasId();
+ /**
+ * optional uint32 id = 1;
+ */
+ int getId();
+
+ // optional bytes key = 2;
+ /**
+ * optional bytes key = 2;
+ */
+ boolean hasKey();
+ /**
+ * optional bytes key = 2;
+ */
+ com.google.protobuf.ByteString getKey();
+ }
+ /**
+ * Protobuf type {@code textsecure.ServerCertificate.Certificate}
+ */
+ public static final class Certificate extends
+ com.google.protobuf.GeneratedMessage
+ implements CertificateOrBuilder {
+ // Use Certificate.newBuilder() to construct.
+ private Certificate(com.google.protobuf.GeneratedMessage.Builder> builder) {
+ super(builder);
+ this.unknownFields = builder.getUnknownFields();
+ }
+ private Certificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+ private static final Certificate defaultInstance;
+ public static Certificate getDefaultInstance() {
+ return defaultInstance;
+ }
+
+ public Certificate getDefaultInstanceForType() {
+ return defaultInstance;
+ }
+
+ private final com.google.protobuf.UnknownFieldSet unknownFields;
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet
+ getUnknownFields() {
+ return this.unknownFields;
+ }
+ private Certificate(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ initFields();
+ int mutable_bitField0_ = 0;
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ default: {
+ if (!parseUnknownField(input, unknownFields,
+ extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ case 8: {
+ bitField0_ |= 0x00000001;
+ id_ = input.readUInt32();
+ break;
+ }
+ case 18: {
+ bitField0_ |= 0x00000002;
+ key_ = input.readBytes();
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(
+ e.getMessage()).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_ServerCertificate_Certificate_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_ServerCertificate_Certificate_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate.class, org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate.Builder.class);
+ }
+
+ public static com.google.protobuf.Parser PARSER =
+ new com.google.protobuf.AbstractParser() {
+ public Certificate parsePartialFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return new Certificate(input, extensionRegistry);
+ }
+ };
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ private int bitField0_;
+ // optional uint32 id = 1;
+ public static final int ID_FIELD_NUMBER = 1;
+ private int id_;
+ /**
+ * optional uint32 id = 1;
+ */
+ public boolean hasId() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional uint32 id = 1;
+ */
+ public int getId() {
+ return id_;
+ }
+
+ // optional bytes key = 2;
+ public static final int KEY_FIELD_NUMBER = 2;
+ private com.google.protobuf.ByteString key_;
+ /**
+ * optional bytes key = 2;
+ */
+ public boolean hasKey() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ /**
+ * optional bytes key = 2;
+ */
+ public com.google.protobuf.ByteString getKey() {
+ return key_;
+ }
+
+ private void initFields() {
+ id_ = 0;
+ key_ = com.google.protobuf.ByteString.EMPTY;
+ }
+ 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.writeUInt32(1, id_);
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ output.writeBytes(2, key_);
+ }
+ 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
+ .computeUInt32Size(1, id_);
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeBytesSize(2, key_);
+ }
+ 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.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+
+ public static Builder newBuilder() { return Builder.create(); }
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder(org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate 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;
+ }
+ /**
+ * Protobuf type {@code textsecure.ServerCertificate.Certificate}
+ */
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessage.Builder
+ implements org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.CertificateOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_ServerCertificate_Certificate_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_ServerCertificate_Certificate_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate.class, org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate.Builder.class);
+ }
+
+ // Construct using org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(
+ com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+ }
+ }
+ private static Builder create() {
+ return new Builder();
+ }
+
+ public Builder clear() {
+ super.clear();
+ id_ = 0;
+ bitField0_ = (bitField0_ & ~0x00000001);
+ key_ = com.google.protobuf.ByteString.EMPTY;
+ bitField0_ = (bitField0_ & ~0x00000002);
+ return this;
+ }
+
+ public Builder clone() {
+ return create().mergeFrom(buildPartial());
+ }
+
+ public com.google.protobuf.Descriptors.Descriptor
+ getDescriptorForType() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_ServerCertificate_Certificate_descriptor;
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate getDefaultInstanceForType() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate.getDefaultInstance();
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate build() {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate buildPartial() {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate result = new org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate(this);
+ int from_bitField0_ = bitField0_;
+ int to_bitField0_ = 0;
+ if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+ to_bitField0_ |= 0x00000001;
+ }
+ result.id_ = id_;
+ if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+ to_bitField0_ |= 0x00000002;
+ }
+ result.key_ = key_;
+ result.bitField0_ = to_bitField0_;
+ onBuilt();
+ return result;
+ }
+
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate) {
+ return mergeFrom((org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate)other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate other) {
+ if (other == org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate.getDefaultInstance()) return this;
+ if (other.hasId()) {
+ setId(other.getId());
+ }
+ if (other.hasKey()) {
+ setKey(other.getKey());
+ }
+ 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 {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Certificate) e.getUnfinishedMessage();
+ throw e;
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+ private int bitField0_;
+
+ // optional uint32 id = 1;
+ private int id_ ;
+ /**
+ * optional uint32 id = 1;
+ */
+ public boolean hasId() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional uint32 id = 1;
+ */
+ public int getId() {
+ return id_;
+ }
+ /**
+ * optional uint32 id = 1;
+ */
+ public Builder setId(int value) {
+ bitField0_ |= 0x00000001;
+ id_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional uint32 id = 1;
+ */
+ public Builder clearId() {
+ bitField0_ = (bitField0_ & ~0x00000001);
+ id_ = 0;
+ onChanged();
+ return this;
+ }
+
+ // optional bytes key = 2;
+ private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY;
+ /**
+ * optional bytes key = 2;
+ */
+ public boolean hasKey() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ /**
+ * optional bytes key = 2;
+ */
+ public com.google.protobuf.ByteString getKey() {
+ return key_;
+ }
+ /**
+ * optional bytes key = 2;
+ */
+ public Builder setKey(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000002;
+ key_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional bytes key = 2;
+ */
+ public Builder clearKey() {
+ bitField0_ = (bitField0_ & ~0x00000002);
+ key_ = getDefaultInstance().getKey();
+ onChanged();
+ return this;
+ }
+
+ // @@protoc_insertion_point(builder_scope:textsecure.ServerCertificate.Certificate)
+ }
+
+ static {
+ defaultInstance = new Certificate(true);
+ defaultInstance.initFields();
+ }
+
+ // @@protoc_insertion_point(class_scope:textsecure.ServerCertificate.Certificate)
+ }
+
+ private int bitField0_;
+ // optional bytes certificate = 1;
+ public static final int CERTIFICATE_FIELD_NUMBER = 1;
+ private com.google.protobuf.ByteString certificate_;
+ /**
+ * optional bytes certificate = 1;
+ */
+ public boolean hasCertificate() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional bytes certificate = 1;
+ */
+ public com.google.protobuf.ByteString getCertificate() {
+ return certificate_;
+ }
+
+ // optional bytes signature = 2;
+ public static final int SIGNATURE_FIELD_NUMBER = 2;
+ private com.google.protobuf.ByteString signature_;
+ /**
+ * optional bytes signature = 2;
+ */
+ public boolean hasSignature() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ /**
+ * optional bytes signature = 2;
+ */
+ public com.google.protobuf.ByteString getSignature() {
+ return signature_;
+ }
+
+ private void initFields() {
+ certificate_ = com.google.protobuf.ByteString.EMPTY;
+ signature_ = com.google.protobuf.ByteString.EMPTY;
+ }
+ 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.writeBytes(1, certificate_);
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ output.writeBytes(2, signature_);
+ }
+ 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
+ .computeBytesSize(1, certificate_);
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeBytesSize(2, signature_);
+ }
+ 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.textsecuregcm.entities.MessageProtos.ServerCertificate parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+
+ public static Builder newBuilder() { return Builder.create(); }
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder(org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate 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;
+ }
+ /**
+ * Protobuf type {@code textsecure.ServerCertificate}
+ */
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessage.Builder
+ implements org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificateOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_ServerCertificate_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_ServerCertificate_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.class, org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Builder.class);
+ }
+
+ // Construct using org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(
+ com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+ }
+ }
+ private static Builder create() {
+ return new Builder();
+ }
+
+ public Builder clear() {
+ super.clear();
+ certificate_ = com.google.protobuf.ByteString.EMPTY;
+ bitField0_ = (bitField0_ & ~0x00000001);
+ signature_ = com.google.protobuf.ByteString.EMPTY;
+ bitField0_ = (bitField0_ & ~0x00000002);
+ return this;
+ }
+
+ public Builder clone() {
+ return create().mergeFrom(buildPartial());
+ }
+
+ public com.google.protobuf.Descriptors.Descriptor
+ getDescriptorForType() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_ServerCertificate_descriptor;
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate getDefaultInstanceForType() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.getDefaultInstance();
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate build() {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate buildPartial() {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate result = new org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate(this);
+ int from_bitField0_ = bitField0_;
+ int to_bitField0_ = 0;
+ if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+ to_bitField0_ |= 0x00000001;
+ }
+ result.certificate_ = certificate_;
+ if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+ to_bitField0_ |= 0x00000002;
+ }
+ result.signature_ = signature_;
+ result.bitField0_ = to_bitField0_;
+ onBuilt();
+ return result;
+ }
+
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate) {
+ return mergeFrom((org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate)other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate other) {
+ if (other == org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.getDefaultInstance()) return this;
+ if (other.hasCertificate()) {
+ setCertificate(other.getCertificate());
+ }
+ if (other.hasSignature()) {
+ setSignature(other.getSignature());
+ }
+ 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 {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate) e.getUnfinishedMessage();
+ throw e;
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+ private int bitField0_;
+
+ // optional bytes certificate = 1;
+ private com.google.protobuf.ByteString certificate_ = com.google.protobuf.ByteString.EMPTY;
+ /**
+ * optional bytes certificate = 1;
+ */
+ public boolean hasCertificate() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional bytes certificate = 1;
+ */
+ public com.google.protobuf.ByteString getCertificate() {
+ return certificate_;
+ }
+ /**
+ * optional bytes certificate = 1;
+ */
+ public Builder setCertificate(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000001;
+ certificate_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional bytes certificate = 1;
+ */
+ public Builder clearCertificate() {
+ bitField0_ = (bitField0_ & ~0x00000001);
+ certificate_ = getDefaultInstance().getCertificate();
+ onChanged();
+ return this;
+ }
+
+ // optional bytes signature = 2;
+ private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY;
+ /**
+ * optional bytes signature = 2;
+ */
+ public boolean hasSignature() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ /**
+ * optional bytes signature = 2;
+ */
+ public com.google.protobuf.ByteString getSignature() {
+ return signature_;
+ }
+ /**
+ * optional bytes signature = 2;
+ */
+ public Builder setSignature(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000002;
+ signature_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional bytes signature = 2;
+ */
+ public Builder clearSignature() {
+ bitField0_ = (bitField0_ & ~0x00000002);
+ signature_ = getDefaultInstance().getSignature();
+ onChanged();
+ return this;
+ }
+
+ // @@protoc_insertion_point(builder_scope:textsecure.ServerCertificate)
+ }
+
+ static {
+ defaultInstance = new ServerCertificate(true);
+ defaultInstance.initFields();
+ }
+
+ // @@protoc_insertion_point(class_scope:textsecure.ServerCertificate)
+ }
+
+ public interface SenderCertificateOrBuilder
+ extends com.google.protobuf.MessageOrBuilder {
+
+ // optional bytes certificate = 1;
+ /**
+ * optional bytes certificate = 1;
+ */
+ boolean hasCertificate();
+ /**
+ * optional bytes certificate = 1;
+ */
+ com.google.protobuf.ByteString getCertificate();
+
+ // optional bytes signature = 2;
+ /**
+ * optional bytes signature = 2;
+ */
+ boolean hasSignature();
+ /**
+ * optional bytes signature = 2;
+ */
+ com.google.protobuf.ByteString getSignature();
+ }
+ /**
+ * Protobuf type {@code textsecure.SenderCertificate}
+ */
+ public static final class SenderCertificate extends
+ com.google.protobuf.GeneratedMessage
+ implements SenderCertificateOrBuilder {
+ // Use SenderCertificate.newBuilder() to construct.
+ private SenderCertificate(com.google.protobuf.GeneratedMessage.Builder> builder) {
+ super(builder);
+ this.unknownFields = builder.getUnknownFields();
+ }
+ private SenderCertificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+ private static final SenderCertificate defaultInstance;
+ public static SenderCertificate getDefaultInstance() {
+ return defaultInstance;
+ }
+
+ public SenderCertificate getDefaultInstanceForType() {
+ return defaultInstance;
+ }
+
+ private final com.google.protobuf.UnknownFieldSet unknownFields;
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet
+ getUnknownFields() {
+ return this.unknownFields;
+ }
+ private SenderCertificate(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ initFields();
+ int mutable_bitField0_ = 0;
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ default: {
+ if (!parseUnknownField(input, unknownFields,
+ extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ case 10: {
+ bitField0_ |= 0x00000001;
+ certificate_ = input.readBytes();
+ break;
+ }
+ case 18: {
+ bitField0_ |= 0x00000002;
+ signature_ = input.readBytes();
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(
+ e.getMessage()).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_SenderCertificate_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_SenderCertificate_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.class, org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Builder.class);
+ }
+
+ public static com.google.protobuf.Parser PARSER =
+ new com.google.protobuf.AbstractParser() {
+ public SenderCertificate parsePartialFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return new SenderCertificate(input, extensionRegistry);
+ }
+ };
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ public interface CertificateOrBuilder
+ extends com.google.protobuf.MessageOrBuilder {
+
+ // optional string sender = 1;
+ /**
+ * optional string sender = 1;
+ */
+ boolean hasSender();
+ /**
+ * optional string sender = 1;
+ */
+ java.lang.String getSender();
+ /**
+ * optional string sender = 1;
+ */
+ com.google.protobuf.ByteString
+ getSenderBytes();
+
+ // optional uint32 senderDevice = 2;
+ /**
+ * optional uint32 senderDevice = 2;
+ */
+ boolean hasSenderDevice();
+ /**
+ * optional uint32 senderDevice = 2;
+ */
+ int getSenderDevice();
+
+ // optional fixed64 expires = 3;
+ /**
+ * optional fixed64 expires = 3;
+ */
+ boolean hasExpires();
+ /**
+ * optional fixed64 expires = 3;
+ */
+ long getExpires();
+
+ // optional bytes identityKey = 4;
+ /**
+ * optional bytes identityKey = 4;
+ */
+ boolean hasIdentityKey();
+ /**
+ * optional bytes identityKey = 4;
+ */
+ com.google.protobuf.ByteString getIdentityKey();
+
+ // optional .textsecure.ServerCertificate signer = 5;
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ boolean hasSigner();
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate getSigner();
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificateOrBuilder getSignerOrBuilder();
+ }
+ /**
+ * Protobuf type {@code textsecure.SenderCertificate.Certificate}
+ */
+ public static final class Certificate extends
+ com.google.protobuf.GeneratedMessage
+ implements CertificateOrBuilder {
+ // Use Certificate.newBuilder() to construct.
+ private Certificate(com.google.protobuf.GeneratedMessage.Builder> builder) {
+ super(builder);
+ this.unknownFields = builder.getUnknownFields();
+ }
+ private Certificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+ private static final Certificate defaultInstance;
+ public static Certificate getDefaultInstance() {
+ return defaultInstance;
+ }
+
+ public Certificate getDefaultInstanceForType() {
+ return defaultInstance;
+ }
+
+ private final com.google.protobuf.UnknownFieldSet unknownFields;
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet
+ getUnknownFields() {
+ return this.unknownFields;
+ }
+ private Certificate(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ initFields();
+ int mutable_bitField0_ = 0;
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ default: {
+ if (!parseUnknownField(input, unknownFields,
+ extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ case 10: {
+ bitField0_ |= 0x00000001;
+ sender_ = input.readBytes();
+ break;
+ }
+ case 16: {
+ bitField0_ |= 0x00000002;
+ senderDevice_ = input.readUInt32();
+ break;
+ }
+ case 25: {
+ bitField0_ |= 0x00000004;
+ expires_ = input.readFixed64();
+ break;
+ }
+ case 34: {
+ bitField0_ |= 0x00000008;
+ identityKey_ = input.readBytes();
+ break;
+ }
+ case 42: {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Builder subBuilder = null;
+ if (((bitField0_ & 0x00000010) == 0x00000010)) {
+ subBuilder = signer_.toBuilder();
+ }
+ signer_ = input.readMessage(org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.PARSER, extensionRegistry);
+ if (subBuilder != null) {
+ subBuilder.mergeFrom(signer_);
+ signer_ = subBuilder.buildPartial();
+ }
+ bitField0_ |= 0x00000010;
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(
+ e.getMessage()).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_SenderCertificate_Certificate_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_SenderCertificate_Certificate_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate.class, org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate.Builder.class);
+ }
+
+ public static com.google.protobuf.Parser PARSER =
+ new com.google.protobuf.AbstractParser() {
+ public Certificate parsePartialFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return new Certificate(input, extensionRegistry);
+ }
+ };
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ private int bitField0_;
+ // optional string sender = 1;
+ public static final int SENDER_FIELD_NUMBER = 1;
+ private java.lang.Object sender_;
+ /**
+ * optional string sender = 1;
+ */
+ public boolean hasSender() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional string sender = 1;
+ */
+ public java.lang.String getSender() {
+ java.lang.Object ref = sender_;
+ if (ref instanceof java.lang.String) {
+ return (java.lang.String) ref;
+ } else {
+ com.google.protobuf.ByteString bs =
+ (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ if (bs.isValidUtf8()) {
+ sender_ = s;
+ }
+ return s;
+ }
+ }
+ /**
+ * optional string sender = 1;
+ */
+ public com.google.protobuf.ByteString
+ getSenderBytes() {
+ java.lang.Object ref = sender_;
+ if (ref instanceof java.lang.String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8(
+ (java.lang.String) ref);
+ sender_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ // optional uint32 senderDevice = 2;
+ public static final int SENDERDEVICE_FIELD_NUMBER = 2;
+ private int senderDevice_;
+ /**
+ * optional uint32 senderDevice = 2;
+ */
+ public boolean hasSenderDevice() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ /**
+ * optional uint32 senderDevice = 2;
+ */
+ public int getSenderDevice() {
+ return senderDevice_;
+ }
+
+ // optional fixed64 expires = 3;
+ public static final int EXPIRES_FIELD_NUMBER = 3;
+ private long expires_;
+ /**
+ * optional fixed64 expires = 3;
+ */
+ public boolean hasExpires() {
+ return ((bitField0_ & 0x00000004) == 0x00000004);
+ }
+ /**
+ * optional fixed64 expires = 3;
+ */
+ public long getExpires() {
+ return expires_;
+ }
+
+ // optional bytes identityKey = 4;
+ public static final int IDENTITYKEY_FIELD_NUMBER = 4;
+ private com.google.protobuf.ByteString identityKey_;
+ /**
+ * optional bytes identityKey = 4;
+ */
+ public boolean hasIdentityKey() {
+ return ((bitField0_ & 0x00000008) == 0x00000008);
+ }
+ /**
+ * optional bytes identityKey = 4;
+ */
+ public com.google.protobuf.ByteString getIdentityKey() {
+ return identityKey_;
+ }
+
+ // optional .textsecure.ServerCertificate signer = 5;
+ public static final int SIGNER_FIELD_NUMBER = 5;
+ private org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate signer_;
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public boolean hasSigner() {
+ return ((bitField0_ & 0x00000010) == 0x00000010);
+ }
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate getSigner() {
+ return signer_;
+ }
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificateOrBuilder getSignerOrBuilder() {
+ return signer_;
+ }
+
+ private void initFields() {
+ sender_ = "";
+ senderDevice_ = 0;
+ expires_ = 0L;
+ identityKey_ = com.google.protobuf.ByteString.EMPTY;
+ signer_ = org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.getDefaultInstance();
+ }
+ 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.writeBytes(1, getSenderBytes());
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ output.writeUInt32(2, senderDevice_);
+ }
+ if (((bitField0_ & 0x00000004) == 0x00000004)) {
+ output.writeFixed64(3, expires_);
+ }
+ if (((bitField0_ & 0x00000008) == 0x00000008)) {
+ output.writeBytes(4, identityKey_);
+ }
+ if (((bitField0_ & 0x00000010) == 0x00000010)) {
+ output.writeMessage(5, signer_);
+ }
+ 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
+ .computeBytesSize(1, getSenderBytes());
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeUInt32Size(2, senderDevice_);
+ }
+ if (((bitField0_ & 0x00000004) == 0x00000004)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeFixed64Size(3, expires_);
+ }
+ if (((bitField0_ & 0x00000008) == 0x00000008)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeBytesSize(4, identityKey_);
+ }
+ if (((bitField0_ & 0x00000010) == 0x00000010)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeMessageSize(5, signer_);
+ }
+ 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.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+
+ public static Builder newBuilder() { return Builder.create(); }
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder(org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate 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;
+ }
+ /**
+ * Protobuf type {@code textsecure.SenderCertificate.Certificate}
+ */
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessage.Builder
+ implements org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.CertificateOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_SenderCertificate_Certificate_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_SenderCertificate_Certificate_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate.class, org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate.Builder.class);
+ }
+
+ // Construct using org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(
+ com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+ getSignerFieldBuilder();
+ }
+ }
+ private static Builder create() {
+ return new Builder();
+ }
+
+ public Builder clear() {
+ super.clear();
+ sender_ = "";
+ bitField0_ = (bitField0_ & ~0x00000001);
+ senderDevice_ = 0;
+ bitField0_ = (bitField0_ & ~0x00000002);
+ expires_ = 0L;
+ bitField0_ = (bitField0_ & ~0x00000004);
+ identityKey_ = com.google.protobuf.ByteString.EMPTY;
+ bitField0_ = (bitField0_ & ~0x00000008);
+ if (signerBuilder_ == null) {
+ signer_ = org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.getDefaultInstance();
+ } else {
+ signerBuilder_.clear();
+ }
+ bitField0_ = (bitField0_ & ~0x00000010);
+ return this;
+ }
+
+ public Builder clone() {
+ return create().mergeFrom(buildPartial());
+ }
+
+ public com.google.protobuf.Descriptors.Descriptor
+ getDescriptorForType() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_SenderCertificate_Certificate_descriptor;
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate getDefaultInstanceForType() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate.getDefaultInstance();
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate build() {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate buildPartial() {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate result = new org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate(this);
+ int from_bitField0_ = bitField0_;
+ int to_bitField0_ = 0;
+ if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+ to_bitField0_ |= 0x00000001;
+ }
+ result.sender_ = sender_;
+ if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+ to_bitField0_ |= 0x00000002;
+ }
+ result.senderDevice_ = senderDevice_;
+ if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+ to_bitField0_ |= 0x00000004;
+ }
+ result.expires_ = expires_;
+ if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+ to_bitField0_ |= 0x00000008;
+ }
+ result.identityKey_ = identityKey_;
+ if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+ to_bitField0_ |= 0x00000010;
+ }
+ if (signerBuilder_ == null) {
+ result.signer_ = signer_;
+ } else {
+ result.signer_ = signerBuilder_.build();
+ }
+ result.bitField0_ = to_bitField0_;
+ onBuilt();
+ return result;
+ }
+
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate) {
+ return mergeFrom((org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate)other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate other) {
+ if (other == org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate.getDefaultInstance()) return this;
+ if (other.hasSender()) {
+ bitField0_ |= 0x00000001;
+ sender_ = other.sender_;
+ onChanged();
+ }
+ if (other.hasSenderDevice()) {
+ setSenderDevice(other.getSenderDevice());
+ }
+ if (other.hasExpires()) {
+ setExpires(other.getExpires());
+ }
+ if (other.hasIdentityKey()) {
+ setIdentityKey(other.getIdentityKey());
+ }
+ if (other.hasSigner()) {
+ mergeSigner(other.getSigner());
+ }
+ 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 {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Certificate) e.getUnfinishedMessage();
+ throw e;
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+ private int bitField0_;
+
+ // optional string sender = 1;
+ private java.lang.Object sender_ = "";
+ /**
+ * optional string sender = 1;
+ */
+ public boolean hasSender() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional string sender = 1;
+ */
+ public java.lang.String getSender() {
+ java.lang.Object ref = sender_;
+ if (!(ref instanceof java.lang.String)) {
+ java.lang.String s = ((com.google.protobuf.ByteString) ref)
+ .toStringUtf8();
+ sender_ = s;
+ return s;
+ } else {
+ return (java.lang.String) ref;
+ }
+ }
+ /**
+ * optional string sender = 1;
+ */
+ public com.google.protobuf.ByteString
+ getSenderBytes() {
+ java.lang.Object ref = sender_;
+ if (ref instanceof String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8(
+ (java.lang.String) ref);
+ sender_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+ /**
+ * optional string sender = 1;
+ */
+ public Builder setSender(
+ java.lang.String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000001;
+ sender_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional string sender = 1;
+ */
+ public Builder clearSender() {
+ bitField0_ = (bitField0_ & ~0x00000001);
+ sender_ = getDefaultInstance().getSender();
+ onChanged();
+ return this;
+ }
+ /**
+ * optional string sender = 1;
+ */
+ public Builder setSenderBytes(
+ com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000001;
+ sender_ = value;
+ onChanged();
+ return this;
+ }
+
+ // optional uint32 senderDevice = 2;
+ private int senderDevice_ ;
+ /**
+ * optional uint32 senderDevice = 2;
+ */
+ public boolean hasSenderDevice() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ /**
+ * optional uint32 senderDevice = 2;
+ */
+ public int getSenderDevice() {
+ return senderDevice_;
+ }
+ /**
+ * optional uint32 senderDevice = 2;
+ */
+ public Builder setSenderDevice(int value) {
+ bitField0_ |= 0x00000002;
+ senderDevice_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional uint32 senderDevice = 2;
+ */
+ public Builder clearSenderDevice() {
+ bitField0_ = (bitField0_ & ~0x00000002);
+ senderDevice_ = 0;
+ onChanged();
+ return this;
+ }
+
+ // optional fixed64 expires = 3;
+ private long expires_ ;
+ /**
+ * optional fixed64 expires = 3;
+ */
+ public boolean hasExpires() {
+ return ((bitField0_ & 0x00000004) == 0x00000004);
+ }
+ /**
+ * optional fixed64 expires = 3;
+ */
+ public long getExpires() {
+ return expires_;
+ }
+ /**
+ * optional fixed64 expires = 3;
+ */
+ public Builder setExpires(long value) {
+ bitField0_ |= 0x00000004;
+ expires_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional fixed64 expires = 3;
+ */
+ public Builder clearExpires() {
+ bitField0_ = (bitField0_ & ~0x00000004);
+ expires_ = 0L;
+ onChanged();
+ return this;
+ }
+
+ // optional bytes identityKey = 4;
+ private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY;
+ /**
+ * optional bytes identityKey = 4;
+ */
+ public boolean hasIdentityKey() {
+ return ((bitField0_ & 0x00000008) == 0x00000008);
+ }
+ /**
+ * optional bytes identityKey = 4;
+ */
+ public com.google.protobuf.ByteString getIdentityKey() {
+ return identityKey_;
+ }
+ /**
+ * optional bytes identityKey = 4;
+ */
+ public Builder setIdentityKey(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000008;
+ identityKey_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional bytes identityKey = 4;
+ */
+ public Builder clearIdentityKey() {
+ bitField0_ = (bitField0_ & ~0x00000008);
+ identityKey_ = getDefaultInstance().getIdentityKey();
+ onChanged();
+ return this;
+ }
+
+ // optional .textsecure.ServerCertificate signer = 5;
+ private org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate signer_ = org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.getDefaultInstance();
+ private com.google.protobuf.SingleFieldBuilder<
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate, org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Builder, org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificateOrBuilder> signerBuilder_;
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public boolean hasSigner() {
+ return ((bitField0_ & 0x00000010) == 0x00000010);
+ }
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate getSigner() {
+ if (signerBuilder_ == null) {
+ return signer_;
+ } else {
+ return signerBuilder_.getMessage();
+ }
+ }
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public Builder setSigner(org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate value) {
+ if (signerBuilder_ == null) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ signer_ = value;
+ onChanged();
+ } else {
+ signerBuilder_.setMessage(value);
+ }
+ bitField0_ |= 0x00000010;
+ return this;
+ }
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public Builder setSigner(
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Builder builderForValue) {
+ if (signerBuilder_ == null) {
+ signer_ = builderForValue.build();
+ onChanged();
+ } else {
+ signerBuilder_.setMessage(builderForValue.build());
+ }
+ bitField0_ |= 0x00000010;
+ return this;
+ }
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public Builder mergeSigner(org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate value) {
+ if (signerBuilder_ == null) {
+ if (((bitField0_ & 0x00000010) == 0x00000010) &&
+ signer_ != org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.getDefaultInstance()) {
+ signer_ =
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.newBuilder(signer_).mergeFrom(value).buildPartial();
+ } else {
+ signer_ = value;
+ }
+ onChanged();
+ } else {
+ signerBuilder_.mergeFrom(value);
+ }
+ bitField0_ |= 0x00000010;
+ return this;
+ }
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public Builder clearSigner() {
+ if (signerBuilder_ == null) {
+ signer_ = org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.getDefaultInstance();
+ onChanged();
+ } else {
+ signerBuilder_.clear();
+ }
+ bitField0_ = (bitField0_ & ~0x00000010);
+ return this;
+ }
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Builder getSignerBuilder() {
+ bitField0_ |= 0x00000010;
+ onChanged();
+ return getSignerFieldBuilder().getBuilder();
+ }
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificateOrBuilder getSignerOrBuilder() {
+ if (signerBuilder_ != null) {
+ return signerBuilder_.getMessageOrBuilder();
+ } else {
+ return signer_;
+ }
+ }
+ /**
+ * optional .textsecure.ServerCertificate signer = 5;
+ */
+ private com.google.protobuf.SingleFieldBuilder<
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate, org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Builder, org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificateOrBuilder>
+ getSignerFieldBuilder() {
+ if (signerBuilder_ == null) {
+ signerBuilder_ = new com.google.protobuf.SingleFieldBuilder<
+ org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate, org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate.Builder, org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificateOrBuilder>(
+ signer_,
+ getParentForChildren(),
+ isClean());
+ signer_ = null;
+ }
+ return signerBuilder_;
+ }
+
+ // @@protoc_insertion_point(builder_scope:textsecure.SenderCertificate.Certificate)
+ }
+
+ static {
+ defaultInstance = new Certificate(true);
+ defaultInstance.initFields();
+ }
+
+ // @@protoc_insertion_point(class_scope:textsecure.SenderCertificate.Certificate)
+ }
+
+ private int bitField0_;
+ // optional bytes certificate = 1;
+ public static final int CERTIFICATE_FIELD_NUMBER = 1;
+ private com.google.protobuf.ByteString certificate_;
+ /**
+ * optional bytes certificate = 1;
+ */
+ public boolean hasCertificate() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional bytes certificate = 1;
+ */
+ public com.google.protobuf.ByteString getCertificate() {
+ return certificate_;
+ }
+
+ // optional bytes signature = 2;
+ public static final int SIGNATURE_FIELD_NUMBER = 2;
+ private com.google.protobuf.ByteString signature_;
+ /**
+ * optional bytes signature = 2;
+ */
+ public boolean hasSignature() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ /**
+ * optional bytes signature = 2;
+ */
+ public com.google.protobuf.ByteString getSignature() {
+ return signature_;
+ }
+
+ private void initFields() {
+ certificate_ = com.google.protobuf.ByteString.EMPTY;
+ signature_ = com.google.protobuf.ByteString.EMPTY;
+ }
+ 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.writeBytes(1, certificate_);
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ output.writeBytes(2, signature_);
+ }
+ 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
+ .computeBytesSize(1, certificate_);
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeBytesSize(2, signature_);
+ }
+ 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.textsecuregcm.entities.MessageProtos.SenderCertificate parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input, extensionRegistry);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+
+ public static Builder newBuilder() { return Builder.create(); }
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder(org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate 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;
+ }
+ /**
+ * Protobuf type {@code textsecure.SenderCertificate}
+ */
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessage.Builder
+ implements org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificateOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_SenderCertificate_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_SenderCertificate_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.class, org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.Builder.class);
+ }
+
+ // Construct using org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(
+ com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+ }
+ }
+ private static Builder create() {
+ return new Builder();
+ }
+
+ public Builder clear() {
+ super.clear();
+ certificate_ = com.google.protobuf.ByteString.EMPTY;
+ bitField0_ = (bitField0_ & ~0x00000001);
+ signature_ = com.google.protobuf.ByteString.EMPTY;
+ bitField0_ = (bitField0_ & ~0x00000002);
+ return this;
+ }
+
+ public Builder clone() {
+ return create().mergeFrom(buildPartial());
+ }
+
+ public com.google.protobuf.Descriptors.Descriptor
+ getDescriptorForType() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.internal_static_textsecure_SenderCertificate_descriptor;
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate getDefaultInstanceForType() {
+ return org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.getDefaultInstance();
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate build() {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ public org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate buildPartial() {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate result = new org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate(this);
+ int from_bitField0_ = bitField0_;
+ int to_bitField0_ = 0;
+ if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+ to_bitField0_ |= 0x00000001;
+ }
+ result.certificate_ = certificate_;
+ if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+ to_bitField0_ |= 0x00000002;
+ }
+ result.signature_ = signature_;
+ result.bitField0_ = to_bitField0_;
+ onBuilt();
+ return result;
+ }
+
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate) {
+ return mergeFrom((org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate)other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate other) {
+ if (other == org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate.getDefaultInstance()) return this;
+ if (other.hasCertificate()) {
+ setCertificate(other.getCertificate());
+ }
+ if (other.hasSignature()) {
+ setSignature(other.getSignature());
+ }
+ 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 {
+ org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate) e.getUnfinishedMessage();
+ throw e;
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+ private int bitField0_;
+
+ // optional bytes certificate = 1;
+ private com.google.protobuf.ByteString certificate_ = com.google.protobuf.ByteString.EMPTY;
+ /**
+ * optional bytes certificate = 1;
+ */
+ public boolean hasCertificate() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional bytes certificate = 1;
+ */
+ public com.google.protobuf.ByteString getCertificate() {
+ return certificate_;
+ }
+ /**
+ * optional bytes certificate = 1;
+ */
+ public Builder setCertificate(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000001;
+ certificate_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional bytes certificate = 1;
+ */
+ public Builder clearCertificate() {
+ bitField0_ = (bitField0_ & ~0x00000001);
+ certificate_ = getDefaultInstance().getCertificate();
+ onChanged();
+ return this;
+ }
+
+ // optional bytes signature = 2;
+ private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY;
+ /**
+ * optional bytes signature = 2;
+ */
+ public boolean hasSignature() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ /**
+ * optional bytes signature = 2;
+ */
+ public com.google.protobuf.ByteString getSignature() {
+ return signature_;
+ }
+ /**
+ * optional bytes signature = 2;
+ */
+ public Builder setSignature(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000002;
+ signature_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional bytes signature = 2;
+ */
+ public Builder clearSignature() {
+ bitField0_ = (bitField0_ & ~0x00000002);
+ signature_ = getDefaultInstance().getSignature();
+ onChanged();
+ return this;
+ }
+
+ // @@protoc_insertion_point(builder_scope:textsecure.SenderCertificate)
+ }
+
+ static {
+ defaultInstance = new SenderCertificate(true);
+ defaultInstance.initFields();
+ }
+
+ // @@protoc_insertion_point(class_scope:textsecure.SenderCertificate)
+ }
+
private static com.google.protobuf.Descriptors.Descriptor
internal_static_textsecure_Envelope_descriptor;
private static
@@ -1709,6 +4310,26 @@ public final class MessageProtos {
private static
com.google.protobuf.GeneratedMessage.FieldAccessorTable
internal_static_textsecure_ProvisioningUuid_fieldAccessorTable;
+ private static com.google.protobuf.Descriptors.Descriptor
+ internal_static_textsecure_ServerCertificate_descriptor;
+ private static
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internal_static_textsecure_ServerCertificate_fieldAccessorTable;
+ private static com.google.protobuf.Descriptors.Descriptor
+ internal_static_textsecure_ServerCertificate_Certificate_descriptor;
+ private static
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internal_static_textsecure_ServerCertificate_Certificate_fieldAccessorTable;
+ private static com.google.protobuf.Descriptors.Descriptor
+ internal_static_textsecure_SenderCertificate_descriptor;
+ private static
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internal_static_textsecure_SenderCertificate_fieldAccessorTable;
+ private static com.google.protobuf.Descriptors.Descriptor
+ internal_static_textsecure_SenderCertificate_Certificate_descriptor;
+ private static
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internal_static_textsecure_SenderCertificate_Certificate_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
@@ -1718,16 +4339,25 @@ public final class MessageProtos {
descriptor;
static {
java.lang.String[] descriptorData = {
- "\n\020TextSecure.proto\022\ntextsecure\"\372\001\n\010Envel" +
+ "\n\020TextSecure.proto\022\ntextsecure\"\301\002\n\010Envel" +
"ope\022\'\n\004type\030\001 \001(\0162\031.textsecure.Envelope." +
"Type\022\016\n\006source\030\002 \001(\t\022\024\n\014sourceDevice\030\007 \001" +
"(\r\022\r\n\005relay\030\003 \001(\t\022\021\n\ttimestamp\030\005 \001(\004\022\025\n\r" +
- "legacyMessage\030\006 \001(\014\022\017\n\007content\030\010 \001(\014\"U\n\004" +
- "Type\022\013\n\007UNKNOWN\020\000\022\016\n\nCIPHERTEXT\020\001\022\020\n\014KEY" +
- "_EXCHANGE\020\002\022\021\n\rPREKEY_BUNDLE\020\003\022\013\n\007RECEIP" +
- "T\020\005\" \n\020ProvisioningUuid\022\014\n\004uuid\030\001 \001(\tB:\n" +
- ")org.whispersystems.textsecuregcm.entiti" +
- "esB\rMessageProtos"
+ "legacyMessage\030\006 \001(\014\022\017\n\007content\030\010 \001(\014\022\022\n\n" +
+ "serverGuid\030\t \001(\t\022\030\n\020server_timestamp\030\n \001" +
+ "(\004\"n\n\004Type\022\013\n\007UNKNOWN\020\000\022\016\n\nCIPHERTEXT\020\001\022" +
+ "\020\n\014KEY_EXCHANGE\020\002\022\021\n\rPREKEY_BUNDLE\020\003\022\013\n\007" +
+ "RECEIPT\020\005\022\027\n\023UNIDENTIFIED_SENDER\020\006\" \n\020Pr" +
+ "ovisioningUuid\022\014\n\004uuid\030\001 \001(\t\"c\n\021ServerCe",
+ "rtificate\022\023\n\013certificate\030\001 \001(\014\022\021\n\tsignat" +
+ "ure\030\002 \001(\014\032&\n\013Certificate\022\n\n\002id\030\001 \001(\r\022\013\n\003" +
+ "key\030\002 \001(\014\"\306\001\n\021SenderCertificate\022\023\n\013certi" +
+ "ficate\030\001 \001(\014\022\021\n\tsignature\030\002 \001(\014\032\210\001\n\013Cert" +
+ "ificate\022\016\n\006sender\030\001 \001(\t\022\024\n\014senderDevice\030" +
+ "\002 \001(\r\022\017\n\007expires\030\003 \001(\006\022\023\n\013identityKey\030\004 " +
+ "\001(\014\022-\n\006signer\030\005 \001(\0132\035.textsecure.ServerC" +
+ "ertificateB:\n)org.whispersystems.textsec" +
+ "uregcm.entitiesB\rMessageProtos"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -1739,13 +4369,37 @@ public final class MessageProtos {
internal_static_textsecure_Envelope_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_textsecure_Envelope_descriptor,
- new java.lang.String[] { "Type", "Source", "SourceDevice", "Relay", "Timestamp", "LegacyMessage", "Content", });
+ new java.lang.String[] { "Type", "Source", "SourceDevice", "Relay", "Timestamp", "LegacyMessage", "Content", "ServerGuid", "ServerTimestamp", });
internal_static_textsecure_ProvisioningUuid_descriptor =
getDescriptor().getMessageTypes().get(1);
internal_static_textsecure_ProvisioningUuid_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_textsecure_ProvisioningUuid_descriptor,
new java.lang.String[] { "Uuid", });
+ internal_static_textsecure_ServerCertificate_descriptor =
+ getDescriptor().getMessageTypes().get(2);
+ internal_static_textsecure_ServerCertificate_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+ internal_static_textsecure_ServerCertificate_descriptor,
+ new java.lang.String[] { "Certificate", "Signature", });
+ internal_static_textsecure_ServerCertificate_Certificate_descriptor =
+ internal_static_textsecure_ServerCertificate_descriptor.getNestedTypes().get(0);
+ internal_static_textsecure_ServerCertificate_Certificate_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+ internal_static_textsecure_ServerCertificate_Certificate_descriptor,
+ new java.lang.String[] { "Id", "Key", });
+ internal_static_textsecure_SenderCertificate_descriptor =
+ getDescriptor().getMessageTypes().get(3);
+ internal_static_textsecure_SenderCertificate_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+ internal_static_textsecure_SenderCertificate_descriptor,
+ new java.lang.String[] { "Certificate", "Signature", });
+ internal_static_textsecure_SenderCertificate_Certificate_descriptor =
+ internal_static_textsecure_SenderCertificate_descriptor.getNestedTypes().get(0);
+ internal_static_textsecure_SenderCertificate_Certificate_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+ internal_static_textsecure_SenderCertificate_Certificate_descriptor,
+ new java.lang.String[] { "Sender", "SenderDevice", "Expires", "IdentityKey", "Signer", });
return null;
}
};
diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/OutgoingMessageEntity.java b/src/main/java/org/whispersystems/textsecuregcm/entities/OutgoingMessageEntity.java
index fb0228af2..becf61557 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/entities/OutgoingMessageEntity.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/entities/OutgoingMessageEntity.java
@@ -3,6 +3,8 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.UUID;
+
public class OutgoingMessageEntity {
@JsonIgnore
@@ -11,6 +13,9 @@ public class OutgoingMessageEntity {
@JsonIgnore
private boolean cached;
+ @JsonProperty
+ private UUID guid;
+
@JsonProperty
private int type;
@@ -32,21 +37,31 @@ public class OutgoingMessageEntity {
@JsonProperty
private byte[] content;
+ @JsonProperty
+ private long serverTimestamp;
+
public OutgoingMessageEntity() {}
- public OutgoingMessageEntity(long id, boolean cached, int type, String relay, long timestamp,
+ public OutgoingMessageEntity(long id, boolean cached,
+ UUID guid, int type, String relay, long timestamp,
String source, int sourceDevice, byte[] message,
- byte[] content)
+ byte[] content, long serverTimestamp)
{
- this.id = id;
- this.cached = cached;
- this.type = type;
- this.relay = relay;
- this.timestamp = timestamp;
- this.source = source;
- this.sourceDevice = sourceDevice;
- this.message = message;
- this.content = content;
+ this.id = id;
+ this.cached = cached;
+ this.guid = guid;
+ this.type = type;
+ this.relay = relay;
+ this.timestamp = timestamp;
+ this.source = source;
+ this.sourceDevice = sourceDevice;
+ this.message = message;
+ this.content = content;
+ this.serverTimestamp = serverTimestamp;
+ }
+
+ public UUID getGuid() {
+ return guid;
}
public int getType() {
@@ -87,4 +102,8 @@ public class OutgoingMessageEntity {
return cached;
}
+ public long getServerTimestamp() {
+ return serverTimestamp;
+ }
+
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/Profile.java b/src/main/java/org/whispersystems/textsecuregcm/entities/Profile.java
index 72a49dfab..9dec49ae1 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/entities/Profile.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/entities/Profile.java
@@ -18,12 +18,20 @@ public class Profile {
@JsonProperty
private String avatar;
+ @JsonProperty
+ private String unidentifiedAccess;
+
+ @JsonProperty
+ private boolean unrestrictedUnidentifiedAccess;
+
public Profile() {}
- public Profile(String name, String avatar, String identityKey) {
- this.name = name;
- this.avatar = avatar;
- this.identityKey = identityKey;
+ public Profile(String name, String avatar, String identityKey, String unidentifiedAccess, boolean unrestrictedUnidentifiedAccess) {
+ this.name = name;
+ this.avatar = avatar;
+ this.identityKey = identityKey;
+ this.unidentifiedAccess = unidentifiedAccess;
+ this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess;
}
@VisibleForTesting
@@ -41,4 +49,14 @@ public class Profile {
return avatar;
}
+ @VisibleForTesting
+ public String getUnidentifiedAccess() {
+ return unidentifiedAccess;
+ }
+
+ @VisibleForTesting
+ public boolean isUnrestrictedUnidentifiedAccess() {
+ return unrestrictedUnidentifiedAccess;
+ }
+
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedClient.java b/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedClient.java
deleted file mode 100644
index 6a2493ef4..000000000
--- a/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedClient.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/**
- * Copyright (C) 2013 Open WhisperSystems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package org.whispersystems.textsecuregcm.federation;
-
-
-import com.google.common.base.Optional;
-import org.apache.http.config.Registry;
-import org.apache.http.config.RegistryBuilder;
-import org.apache.http.conn.socket.ConnectionSocketFactory;
-import org.apache.http.conn.ssl.DefaultHostnameVerifier;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.bouncycastle.openssl.PEMReader;
-import org.glassfish.jersey.client.ClientProperties;
-import org.glassfish.jersey.client.RequestEntityProcessing;
-import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.whispersystems.textsecuregcm.entities.AccountCount;
-import org.whispersystems.textsecuregcm.entities.AttachmentUri;
-import org.whispersystems.textsecuregcm.entities.ClientContact;
-import org.whispersystems.textsecuregcm.entities.ClientContacts;
-import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
-import org.whispersystems.textsecuregcm.entities.PreKeyResponse;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManagerFactory;
-import javax.ws.rs.ProcessingException;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.List;
-
-import io.dropwizard.client.JerseyClientBuilder;
-import io.dropwizard.client.JerseyClientConfiguration;
-import io.dropwizard.setup.Environment;
-
-public class FederatedClient {
-
- private final Logger logger = LoggerFactory.getLogger(FederatedClient.class);
-
- private static final String USER_COUNT_PATH = "/v1/federation/user_count";
- private static final String USER_TOKENS_PATH = "/v1/federation/user_tokens/%d";
- private static final String RELAY_MESSAGE_PATH = "/v1/federation/messages/%s/%d/%s";
- private static final String PREKEY_PATH_DEVICE_V1 = "/v1/federation/key/%s/%s";
- private static final String PREKEY_PATH_DEVICE_V2 = "/v2/federation/key/%s/%s";
- private static final String ATTACHMENT_URI_PATH = "/v1/federation/attachment/%d";
- private static final String RECEIPT_PATH = "/v1/receipt/%s/%d/%s/%d";
-
- private final FederatedPeer peer;
- private final Client client;
-
- public FederatedClient(Environment environment, JerseyClientConfiguration configuration,
- String federationName, FederatedPeer peer)
- throws IOException
- {
- try {
- this.client = createClient(environment, configuration, federationName, peer);
- this.peer = peer;
- } catch (NoSuchAlgorithmException e) {
- throw new AssertionError(e);
- } catch (KeyStoreException | KeyManagementException | CertificateException e) {
- throw new IOException(e);
- }
- }
-
- public URL getSignedAttachmentUri(long attachmentId) throws IOException {
- try {
- AttachmentUri response = client.target(peer.getUrl())
- .path(String.format(ATTACHMENT_URI_PATH, attachmentId))
- .request()
- .accept(MediaType.APPLICATION_JSON_TYPE)
- .get(AttachmentUri.class);
-
- return response.getLocation();
- } catch (ProcessingException e) {
- logger.warn("Bad URI", e);
- throw new IOException(e);
- }
- }
-
- public Optional getKeysV2(String destination, String device) {
- try {
- PreKeyResponse response = client.target(peer.getUrl())
- .path(String.format(PREKEY_PATH_DEVICE_V2, destination, device))
- .request()
- .accept(MediaType.APPLICATION_JSON_TYPE)
- .get(PreKeyResponse.class);
-
- return Optional.of(response);
- } catch (ProcessingException e) {
- logger.warn("PreKey", e);
- return Optional.absent();
- }
- }
-
- public int getUserCount() {
- try {
- AccountCount count = client.target(peer.getUrl())
- .path(USER_COUNT_PATH)
- .request()
- .accept(MediaType.APPLICATION_JSON_TYPE)
- .get(AccountCount.class);
-
- return count.getCount();
- } catch (ProcessingException e) {
- logger.warn("User Count", e);
- return 0;
- }
- }
-
- public List getUserTokens(int offset) {
- try {
- ClientContacts contacts = client.target(peer.getUrl())
- .path(String.format(USER_TOKENS_PATH, offset))
- .request()
- .accept(MediaType.APPLICATION_JSON_TYPE)
- .get(ClientContacts.class);
-
- return contacts.getContacts();
- } catch (ProcessingException e) {
- logger.warn("User Tokens", e);
- return null;
- }
- }
-
- public void sendMessages(String source, long sourceDeviceId, String destination, IncomingMessageList messages)
- throws IOException
- {
- Response response = null;
-
- try {
- response = client.target(peer.getUrl())
- .path(String.format(RELAY_MESSAGE_PATH, source, sourceDeviceId, destination))
- .request()
- .put(Entity.json(messages));
-
- if (response.getStatus() != 200 && response.getStatus() != 204) {
- if (response.getStatus() == 411) throw new WebApplicationException(Response.status(413).build());
- else throw new WebApplicationException(Response.status(response.getStatusInfo()).build());
- }
-
- } catch (ProcessingException e) {
- logger.warn("sendMessage", e);
- throw new IOException(e);
- } finally {
- if (response != null) response.close();
- }
- }
-
- public void sendDeliveryReceipt(String source, long sourceDeviceId, String destination, long messageId)
- throws IOException
- {
- Response response = null;
-
- try {
- response = client.target(peer.getUrl())
- .path(String.format(RECEIPT_PATH, source, sourceDeviceId, destination, messageId))
- .request()
- .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
- .put(Entity.entity("", MediaType.APPLICATION_JSON_TYPE));
-
- if (response.getStatus() != 200 && response.getStatus() != 204) {
- if (response.getStatus() == 411) throw new WebApplicationException(Response.status(413).build());
- else throw new WebApplicationException(Response.status(response.getStatusInfo()).build());
- }
- } catch (ProcessingException e) {
- logger.warn("sendMessage", e);
- throw new IOException(e);
- } finally {
- if (response != null) response.close();
- }
- }
-
- private Client createClient(Environment environment, JerseyClientConfiguration configuration,
- String federationName, FederatedPeer peer)
- throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, CertificateException
- {
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
- trustManagerFactory.init(initializeTrustStore(peer.getName(), peer.getCertificate()));
-
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
-
- SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new DefaultHostnameVerifier());
- Registry registry = RegistryBuilder.create().register("https", sslConnectionSocketFactory).build();
-
- Client client = new JerseyClientBuilder(environment).using(configuration)
- .using(registry)
- .build("FederatedClient");
-
- client.property(ClientProperties.CONNECT_TIMEOUT, 5000);
- client.property(ClientProperties.READ_TIMEOUT, 10000);
- client.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED);
- client.register(HttpAuthenticationFeature.basic(federationName, peer.getAuthenticationToken()));
-
- return client;
- }
-
- private KeyStore initializeTrustStore(String name, String pemCertificate)
- throws CertificateException
- {
- try {
- PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(pemCertificate.getBytes())));
- X509Certificate certificate = (X509Certificate) reader.readObject();
-
- if (certificate == null) {
- throw new CertificateException("No certificate found in parsing!");
- }
-
- KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
- keyStore.load(null);
- keyStore.setCertificateEntry(name, certificate);
-
- return keyStore;
- } catch (IOException | KeyStoreException e) {
- throw new CertificateException(e);
- } catch (NoSuchAlgorithmException e) {
- throw new AssertionError(e);
- }
- }
-
- public String getPeerName() {
- return peer.getName();
- }
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedClientManager.java b/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedClientManager.java
deleted file mode 100644
index 41e57c37e..000000000
--- a/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedClientManager.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Copyright (C) 2013 Open WhisperSystems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package org.whispersystems.textsecuregcm.federation;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.whispersystems.textsecuregcm.configuration.FederationConfiguration;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-
-import io.dropwizard.client.JerseyClientConfiguration;
-import io.dropwizard.setup.Environment;
-
-public class FederatedClientManager {
-
- private final Logger logger = LoggerFactory.getLogger(FederatedClientManager.class);
-
- private final HashMap clients = new HashMap<>();
-
- public FederatedClientManager(Environment environment,
- JerseyClientConfiguration clientConfig,
- FederationConfiguration federationConfig)
- throws IOException
- {
- List peers = federationConfig.getPeers();
- String identity = federationConfig.getName();
-
- if (peers != null) {
- for (FederatedPeer peer : peers) {
- logger.info("Adding peer: " + peer.getName());
- clients.put(peer.getName(), new FederatedClient(environment, clientConfig, identity, peer));
- }
- }
- }
-
- public FederatedClient getClient(String name) throws NoSuchPeerException {
- FederatedClient client = clients.get(name);
-
- if (client == null) {
- throw new NoSuchPeerException(name);
- }
-
- return client;
- }
-
- public List getClients() {
- return new LinkedList<>(clients.values());
- }
-
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedPeer.java b/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedPeer.java
deleted file mode 100644
index 197820638..000000000
--- a/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedPeer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Copyright (C) 2013 Open WhisperSystems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package org.whispersystems.textsecuregcm.federation;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import org.hibernate.validator.constraints.NotEmpty;
-import org.hibernate.validator.constraints.URL;
-
-public class FederatedPeer {
-
- @NotEmpty
- @JsonProperty
- private String name;
-
- @NotEmpty
- @URL
- @JsonProperty
- private String url;
-
- @NotEmpty
- @JsonProperty
- private String authenticationToken;
-
- @NotEmpty
- @JsonProperty
- private String certificate;
-
- public FederatedPeer() {}
-
- public FederatedPeer(String name, String url, String authenticationToken, String certificate) {
- this.name = name;
- this.url = url;
- this.authenticationToken = authenticationToken;
- this.certificate = certificate;
- }
-
- public String getUrl() {
- return url;
- }
-
- public String getName() {
- return name;
- }
-
- public String getAuthenticationToken() {
- return authenticationToken;
- }
-
- public String getCertificate() {
- return certificate;
- }
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/federation/NoSuchPeerException.java b/src/main/java/org/whispersystems/textsecuregcm/federation/NoSuchPeerException.java
deleted file mode 100644
index ad23809ba..000000000
--- a/src/main/java/org/whispersystems/textsecuregcm/federation/NoSuchPeerException.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (C) 2013 Open WhisperSystems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-package org.whispersystems.textsecuregcm.federation;
-
-
-public class NoSuchPeerException extends Exception {
- public NoSuchPeerException(String name) {
- super(name);
- }
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/federation/NonLimitedAccount.java b/src/main/java/org/whispersystems/textsecuregcm/federation/NonLimitedAccount.java
deleted file mode 100644
index 509ac88f9..000000000
--- a/src/main/java/org/whispersystems/textsecuregcm/federation/NonLimitedAccount.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.whispersystems.textsecuregcm.federation;
-
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.google.common.base.Optional;
-import org.whispersystems.textsecuregcm.storage.Account;
-import org.whispersystems.textsecuregcm.storage.Device;
-
-public class NonLimitedAccount extends Account {
-
- @JsonIgnore
- private final String number;
-
- @JsonIgnore
- private final String relay;
-
- @JsonIgnore
- private final long deviceId;
-
- public NonLimitedAccount(String number, long deviceId, String relay) {
- this.number = number;
- this.deviceId = deviceId;
- this.relay = relay;
- }
-
- @Override
- public String getNumber() {
- return number;
- }
-
- @Override
- public boolean isRateLimited() {
- return false;
- }
-
- @Override
- public Optional getRelay() {
- return Optional.of(relay);
- }
-
- @Override
- public Optional getAuthenticatedDevice() {
- return Optional.of(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, System.currentTimeMillis(), System.currentTimeMillis(), false, false, "NA"));
- }
-}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/push/APNSender.java b/src/main/java/org/whispersystems/textsecuregcm/push/APNSender.java
index d7d2775f6..4905c677e 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/push/APNSender.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/push/APNSender.java
@@ -20,7 +20,6 @@ import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -37,6 +36,7 @@ import org.whispersystems.textsecuregcm.util.Constants;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Date;
+import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
diff --git a/src/main/java/org/whispersystems/textsecuregcm/push/ApnFallbackManager.java b/src/main/java/org/whispersystems/textsecuregcm/push/ApnFallbackManager.java
index 929f06c75..7534a491e 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/push/ApnFallbackManager.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/push/ApnFallbackManager.java
@@ -4,7 +4,6 @@ import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.RatioGauge;
import com.codahale.metrics.SharedMetricRegistries;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.redis.LuaScript;
@@ -21,6 +20,7 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.codahale.metrics.MetricRegistry.name;
@@ -28,7 +28,6 @@ import io.dropwizard.lifecycle.Managed;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisException;
-@SuppressWarnings("Guava")
public class ApnFallbackManager implements Managed, Runnable {
private static final Logger logger = LoggerFactory.getLogger(ApnFallbackManager.class);
@@ -167,19 +166,19 @@ public class ApnFallbackManager implements Managed, Runnable {
private Optional> getSeparated(String encoded) {
try {
- if (encoded == null) return Optional.absent();
+ if (encoded == null) return Optional.empty();
String[] parts = encoded.split(":");
if (parts.length != 2) {
logger.warn("Got strange encoded number: " + encoded);
- return Optional.absent();
+ return Optional.empty();
}
return Optional.of(new Pair<>(parts[0], Long.parseLong(parts[1])));
} catch (NumberFormatException e) {
logger.warn("Badly formatted: " + encoded, e);
- return Optional.absent();
+ return Optional.empty();
}
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/push/GCMSender.java b/src/main/java/org/whispersystems/textsecuregcm/push/GCMSender.java
index 92cf43c94..cc69aa8b4 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/push/GCMSender.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/push/GCMSender.java
@@ -4,7 +4,6 @@ import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -22,6 +21,7 @@ import org.whispersystems.textsecuregcm.util.Constants;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -174,7 +174,7 @@ public class GCMSender implements Managed {
}
}
- return Optional.absent();
+ return Optional.empty();
}
private void markOutboundMeter(String key) {
diff --git a/src/main/java/org/whispersystems/textsecuregcm/push/ReceiptSender.java b/src/main/java/org/whispersystems/textsecuregcm/push/ReceiptSender.java
index f87ee86e5..f1ab1ad71 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/push/ReceiptSender.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/push/ReceiptSender.java
@@ -1,64 +1,33 @@
package org.whispersystems.textsecuregcm.push;
-import com.google.common.base.Optional;
import org.whispersystems.textsecuregcm.controllers.NoSuchUserException;
import org.whispersystems.textsecuregcm.entities.MessageProtos.Envelope;
-import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
-import org.whispersystems.textsecuregcm.federation.NoSuchPeerException;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
-import java.io.IOException;
+import java.util.Optional;
import java.util.Set;
public class ReceiptSender {
- private final PushSender pushSender;
- private final FederatedClientManager federatedClientManager;
- private final AccountsManager accountManager;
+ private final PushSender pushSender;
+ private final AccountsManager accountManager;
- public ReceiptSender(AccountsManager accountManager,
- PushSender pushSender,
- FederatedClientManager federatedClientManager)
+ public ReceiptSender(AccountsManager accountManager,
+ PushSender pushSender)
{
- this.federatedClientManager = federatedClientManager;
- this.accountManager = accountManager;
- this.pushSender = pushSender;
+ this.accountManager = accountManager;
+ this.pushSender = pushSender;
}
- public void sendReceipt(Account source, String destination,
- long messageId, Optional relay)
- throws IOException, NoSuchUserException,
- NotPushRegisteredException, TransientPushFailureException
+ public void sendReceipt(Account source, String destination, long messageId)
+ throws NoSuchUserException, NotPushRegisteredException
{
if (source.getNumber().equals(destination)) {
return;
}
- if (relay.isPresent() && !relay.get().isEmpty()) {
- sendRelayedReceipt(source, destination, messageId, relay.get());
- } else {
- sendDirectReceipt(source, destination, messageId);
- }
- }
-
- private void sendRelayedReceipt(Account source, String destination, long messageId, String relay)
- throws NoSuchUserException, IOException
- {
- try {
- federatedClientManager.getClient(relay)
- .sendDeliveryReceipt(source.getNumber(),
- source.getAuthenticatedDevice().get().getId(),
- destination, messageId);
- } catch (NoSuchPeerException e) {
- throw new NoSuchUserException(e);
- }
- }
-
- private void sendDirectReceipt(Account source, String destination, long messageId)
- throws NotPushRegisteredException, TransientPushFailureException, NoSuchUserException
- {
Account destinationAccount = getDestinationAccount(destination);
Set destinationDevices = destinationAccount.getDevices();
Envelope.Builder message = Envelope.newBuilder()
diff --git a/src/main/java/org/whispersystems/textsecuregcm/sms/SmsSender.java b/src/main/java/org/whispersystems/textsecuregcm/sms/SmsSender.java
index ade564554..8b62e0f53 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/sms/SmsSender.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/sms/SmsSender.java
@@ -17,13 +17,14 @@
package org.whispersystems.textsecuregcm.sms;
-import com.google.common.base.Optional;
import com.twilio.sdk.TwilioRestException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.util.Optional;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class SmsSender {
static final String SMS_IOS_VERIFICATION_TEXT = "Your Signal verification code: %s\n\nOr tap: sgnl://verify/%s";
diff --git a/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java b/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java
index 17c695ac8..481965d83 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java
@@ -19,7 +19,6 @@ package org.whispersystems.textsecuregcm.sms;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
-import com.google.common.base.Optional;
import com.twilio.sdk.TwilioRestClient;
import com.twilio.sdk.TwilioRestException;
import com.twilio.sdk.resource.factory.CallFactory;
@@ -36,10 +35,12 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Random;
import static com.codahale.metrics.MetricRegistry.name;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class TwilioSmsSender {
public static final String SAY_TWIML = "\n" +
@@ -81,7 +82,7 @@ public class TwilioSmsSender {
messageParams.add(new BasicNameValuePair("MessagingServiceSid", messagingServicesId));
}
- if ("ios".equals(clientType.orNull())) {
+ if ("ios".equals(clientType.orElse(null))) {
messageParams.add(new BasicNameValuePair("Body", String.format(SmsSender.SMS_IOS_VERIFICATION_TEXT, verificationCode, verificationCode)));
} else {
messageParams.add(new BasicNameValuePair("Body", String.format(SmsSender.SMS_VERIFICATION_TEXT, verificationCode)));
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java b/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java
index 66b8279ee..901c06b37 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java
@@ -20,13 +20,15 @@ package org.whispersystems.textsecuregcm.storage;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
+import javax.security.auth.Subject;
+import java.security.Principal;
import java.util.HashSet;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-public class Account {
+public class Account implements Principal {
public static final int MEMCACHE_VERION = 5;
@@ -51,19 +53,26 @@ public class Account {
@JsonProperty
private String pin;
+ @JsonProperty("uak")
+ private byte[] unidentifiedAccessKey;
+
+ @JsonProperty("uua")
+ private boolean unrestrictedUnidentifiedAccess;
+
@JsonIgnore
private Device authenticatedDevice;
public Account() {}
@VisibleForTesting
- public Account(String number, Set devices) {
- this.number = number;
- this.devices = devices;
+ public Account(String number, Set devices, byte[] unidentifiedAccessKey) {
+ this.number = number;
+ this.devices = devices;
+ this.unidentifiedAccessKey = unidentifiedAccessKey;
}
public Optional getAuthenticatedDevice() {
- return Optional.fromNullable(authenticatedDevice);
+ return Optional.ofNullable(authenticatedDevice);
}
public void setAuthenticatedDevice(Device device) {
@@ -84,7 +93,7 @@ public class Account {
}
public void removeDevice(long deviceId) {
- this.devices.remove(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, 0, 0, false, false, "NA"));
+ this.devices.remove(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, 0, 0, "NA", false));
}
public Set getDevices() {
@@ -102,27 +111,11 @@ public class Account {
}
}
- return Optional.absent();
+ return Optional.empty();
}
- public boolean isVoiceSupported() {
- for (Device device : devices) {
- if (device.isActive() && device.isVoiceSupported()) {
- return true;
- }
- }
-
- return false;
- }
-
- public boolean isVideoSupported() {
- for (Device device : devices) {
- if (device.isActive() && device.isVideoSupported()) {
- return true;
- }
- }
-
- return false;
+ public boolean isUnauthenticatedDeliverySupported() {
+ return devices.stream().filter(Device::isActive).allMatch(Device::isUnauthenticatedDeliverySupported);
}
public boolean isActive() {
@@ -161,7 +154,7 @@ public class Account {
}
public Optional getRelay() {
- return Optional.absent();
+ return Optional.empty();
}
public void setIdentityKey(String identityKey) {
@@ -184,11 +177,11 @@ public class Account {
return lastSeen;
}
- public String getName() {
+ public String getProfileName() {
return name;
}
- public void setName(String name) {
+ public void setProfileName(String name) {
this.name = name;
}
@@ -209,10 +202,40 @@ public class Account {
}
public Optional getPin() {
- return Optional.fromNullable(pin);
+ return Optional.ofNullable(pin);
}
public void setPin(String pin) {
this.pin = pin;
}
+
+ public Optional getUnidentifiedAccessKey() {
+ return Optional.ofNullable(unidentifiedAccessKey);
+ }
+
+ public void setUnidentifiedAccessKey(byte[] unidentifiedAccessKey) {
+ this.unidentifiedAccessKey = unidentifiedAccessKey;
+ }
+
+ public boolean isUnrestrictedUnidentifiedAccess() {
+ return unrestrictedUnidentifiedAccess;
+ }
+
+ public void setUnrestrictedUnidentifiedAccess(boolean unrestrictedUnidentifiedAccess) {
+ this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess;
+ }
+
+ // Principal implementation
+
+ @Override
+ @JsonIgnore
+ public String getName() {
+ return null;
+ }
+
+ @Override
+ @JsonIgnore
+ public boolean implies(Subject subject) {
+ return false;
+ }
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java b/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java
index 75bfde110..66a7bd648 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java
@@ -19,7 +19,6 @@ package org.whispersystems.textsecuregcm.storage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.entities.ClientContact;
@@ -30,6 +29,7 @@ import org.whispersystems.textsecuregcm.util.Util;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
+import java.util.Optional;
import redis.clients.jedis.Jedis;
@@ -79,7 +79,7 @@ public class AccountsManager {
Optional account = memcacheGet(number);
if (!account.isPresent()) {
- account = Optional.fromNullable(accounts.get(number));
+ account = Optional.ofNullable(accounts.get(number));
if (account.isPresent()) {
memcacheSet(number, account.get());
@@ -99,7 +99,7 @@ public class AccountsManager {
private void updateDirectory(Account account) {
if (account.isActive()) {
byte[] token = Util.getContactToken(account.getNumber());
- ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported(), account.isVideoSupported());
+ ClientContact clientContact = new ClientContact(token, null, true, true);
directory.add(clientContact);
} else {
directory.remove(account.getNumber());
@@ -123,10 +123,10 @@ public class AccountsManager {
String json = jedis.get(getKey(number));
if (json != null) return Optional.of(mapper.readValue(json, Account.class));
- else return Optional.absent();
+ else return Optional.empty();
} catch (IOException e) {
logger.warn("AccountsManager", "Deserialization error", e);
- return Optional.absent();
+ return Optional.empty();
}
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java b/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java
index 6e8402418..04d0adff7 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2013 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -70,40 +70,36 @@ public class Device {
@JsonProperty
private long created;
- @JsonProperty
- private boolean voice;
-
- @JsonProperty
- private boolean video;
-
@JsonProperty
private String userAgent;
+ @JsonProperty
+ private boolean unauthenticatedDelivery;
+
public Device() {}
public Device(long id, String name, String authToken, String salt,
String signalingKey, String gcmId, String apnId,
String voipApnId, boolean fetchesMessages,
int registrationId, SignedPreKey signedPreKey,
- long lastSeen, long created, boolean voice, boolean video,
- String userAgent)
+ long lastSeen, long created, String userAgent,
+ boolean unauthenticatedDelivery)
{
- this.id = id;
- this.name = name;
- this.authToken = authToken;
- this.salt = salt;
- this.signalingKey = signalingKey;
- this.gcmId = gcmId;
- this.apnId = apnId;
- this.voipApnId = voipApnId;
- this.fetchesMessages = fetchesMessages;
- this.registrationId = registrationId;
- this.signedPreKey = signedPreKey;
- this.lastSeen = lastSeen;
- this.created = created;
- this.voice = voice;
- this.video = video;
- this.userAgent = userAgent;
+ this.id = id;
+ this.name = name;
+ this.authToken = authToken;
+ this.salt = salt;
+ this.signalingKey = signalingKey;
+ this.gcmId = gcmId;
+ this.apnId = apnId;
+ this.voipApnId = voipApnId;
+ this.fetchesMessages = fetchesMessages;
+ this.registrationId = registrationId;
+ this.signedPreKey = signedPreKey;
+ this.lastSeen = lastSeen;
+ this.created = created;
+ this.userAgent = userAgent;
+ this.unauthenticatedDelivery = unauthenticatedDelivery;
}
public String getApnId() {
@@ -170,20 +166,12 @@ public class Device {
this.name = name;
}
- public boolean isVoiceSupported() {
- return voice;
+ public boolean isUnauthenticatedDeliverySupported() {
+ return unauthenticatedDelivery;
}
- public void setVoiceSupported(boolean voice) {
- this.voice = voice;
- }
-
- public boolean isVideoSupported() {
- return video;
- }
-
- public void setVideoSupported(boolean video) {
- this.video = video;
+ public void setUnauthenticatedDeliverySupported(boolean unauthenticatedDelivery) {
+ this.unauthenticatedDelivery = unauthenticatedDelivery;
}
public void setAuthenticationCredentials(AuthenticationCredentials credentials) {
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryManager.java b/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryManager.java
index cff2024af..36ae2b83c 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryManager.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryManager.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2013 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -20,7 +20,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.entities.ClientContact;
@@ -32,6 +31,7 @@ import org.whispersystems.textsecuregcm.util.Util;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
@@ -102,14 +102,14 @@ public class DirectoryManager {
byte[] result = jedis.hget(DIRECTORY_KEY, token);
if (result == null) {
- return Optional.absent();
+ return Optional.empty();
}
TokenValue tokenValue = objectMapper.readValue(result, TokenValue.class);
return Optional.of(new ClientContact(token, tokenValue.relay, tokenValue.voice, tokenValue.video));
} catch (IOException e) {
logger.warn("JSON Error", e);
- return Optional.absent();
+ return Optional.empty();
}
}
@@ -205,7 +205,7 @@ public class DirectoryManager {
byte[] result = response.get();
if (result == null) {
- return Optional.absent();
+ return Optional.empty();
}
TokenValue tokenValue = objectMapper.readValue(result, TokenValue.class);
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java b/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java
index 75b0f812a..9d7496012 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -21,8 +21,6 @@ import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-import io.dropwizard.lifecycle.Managed;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.entities.ClientContact;
@@ -37,10 +35,13 @@ import javax.ws.rs.ProcessingException;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
import static com.codahale.metrics.MetricRegistry.name;
+import io.dropwizard.lifecycle.Managed;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class DirectoryReconciler implements Managed, Runnable {
private static final Logger logger = LoggerFactory.getLogger(DirectoryReconciler.class);
@@ -62,7 +63,6 @@ public class DirectoryReconciler implements Managed, Runnable {
private final int chunkSize;
private final long chunkIntervalMs;
private final String workerId;
- private final SecureRandom random;
private boolean running;
private boolean finished;
@@ -72,21 +72,15 @@ public class DirectoryReconciler implements Managed, Runnable {
DirectoryManager directoryManager,
Accounts accounts,
int chunkSize,
- long chunkIntervalMs) {
+ long chunkIntervalMs)
+ {
this.accounts = accounts;
this.directoryManager = directoryManager;
this.reconciliationClient = reconciliationClient;
this.reconciliationCache = reconciliationCache;
this.chunkSize = chunkSize;
this.chunkIntervalMs = chunkIntervalMs;
- this.random = new SecureRandom();
- this.workerId = generateWorkerId(random);
- }
-
- private static String generateWorkerId(SecureRandom random) {
- byte[] workerIdBytes = new byte[16];
- random.nextBytes(workerIdBytes);
- return Hex.toString(workerIdBytes);
+ this.workerId = Hex.toString(Util.generateSecretBytes(16));
}
@Override
@@ -141,6 +135,94 @@ public class DirectoryReconciler implements Managed, Runnable {
return intervalMs;
}
+ private boolean processChunk() {
+ Optional fromNumber = reconciliationCache.getLastNumber();
+ List chunkAccounts = readChunk(fromNumber, chunkSize);
+
+ updateDirectoryCache(chunkAccounts);
+
+ DirectoryReconciliationRequest request = createChunkRequest(fromNumber, chunkAccounts);
+ DirectoryReconciliationResponse sendChunkResponse = sendChunk(request);
+
+ if (sendChunkResponse.getStatus() == DirectoryReconciliationResponse.Status.MISSING || request.getToNumber() == null) {
+ reconciliationCache.clearAccelerate();
+ }
+
+ if (sendChunkResponse.getStatus() == DirectoryReconciliationResponse.Status.OK) {
+ reconciliationCache.setLastNumber(Optional.ofNullable(request.getToNumber()));
+ } else if (sendChunkResponse.getStatus() == DirectoryReconciliationResponse.Status.MISSING) {
+ reconciliationCache.setLastNumber(Optional.empty());
+ }
+
+ return sendChunkResponse.getStatus() == DirectoryReconciliationResponse.Status.OK;
+ }
+
+ private List readChunk(Optional fromNumber, int chunkSize) {
+ try (Timer.Context timer = readChunkTimer.time()) {
+ Optional> chunkAccounts;
+
+ if (fromNumber.isPresent()) {
+ chunkAccounts = Optional.ofNullable(accounts.getAllFrom(fromNumber.get(), chunkSize));
+ } else {
+ chunkAccounts = Optional.ofNullable(accounts.getAllFrom(chunkSize));
+ }
+
+ return chunkAccounts.orElse(Collections.emptyList());
+ }
+ }
+
+ private void updateDirectoryCache(List accounts) {
+ if (accounts.isEmpty()) {
+ return;
+ }
+
+ BatchOperationHandle batchOperation = directoryManager.startBatchOperation();
+
+ try {
+ for (Account account : accounts) {
+ if (account.isActive()) {
+ byte[] token = Util.getContactToken(account.getNumber());
+ ClientContact clientContact = new ClientContact(token, null, true, true);
+
+ directoryManager.add(batchOperation, clientContact);
+ } else {
+ directoryManager.remove(batchOperation, account.getNumber());
+ }
+ }
+ } finally {
+ directoryManager.stopBatchOperation(batchOperation);
+ }
+ }
+
+ private DirectoryReconciliationRequest createChunkRequest(Optional fromNumber, List accounts) {
+ List numbers = accounts.stream()
+ .filter(Account::isActive)
+ .map(Account::getNumber)
+ .collect(Collectors.toList());
+
+ Optional toNumber = Optional.empty();
+ if (!accounts.isEmpty()) {
+ toNumber = Optional.of(accounts.get(accounts.size() - 1).getNumber());
+ }
+
+ return new DirectoryReconciliationRequest(fromNumber.orElse(null), toNumber.orElse(null), numbers);
+ }
+
+ private DirectoryReconciliationResponse sendChunk(DirectoryReconciliationRequest request) {
+ try (Timer.Context timer = sendChunkTimer.time()) {
+ DirectoryReconciliationResponse response = reconciliationClient.sendChunk(request);
+ if (response.getStatus() != DirectoryReconciliationResponse.Status.OK) {
+ sendChunkErrorMeter.mark();
+ logger.warn("reconciliation error: " + response.getStatus());
+ }
+ return response;
+ } catch (ProcessingException ex) {
+ sendChunkErrorMeter.mark();
+ logger.warn("request error: ", ex);
+ throw new ProcessingException(ex);
+ }
+ }
+
private synchronized boolean sleepWhileRunning(long delayMs) {
long startTimeMs = System.currentTimeMillis();
while (running && delayMs > 0) {
@@ -162,96 +244,8 @@ public class DirectoryReconciler implements Managed, Runnable {
}
private long getDelayWithJitter(long delayMs) {
- long randomJitterMs = (long) (random.nextDouble() * JITTER_MAX * delayMs);
+ long randomJitterMs = (long) (new SecureRandom().nextDouble() * JITTER_MAX * delayMs);
return delayMs + randomJitterMs;
}
- private boolean processChunk() {
- Optional fromNumber = reconciliationCache.getLastNumber();
- List chunkAccounts = readChunk(fromNumber, chunkSize);
-
- writeChunktoDirectoryCache(chunkAccounts);
-
- DirectoryReconciliationRequest request = createChunkRequest(fromNumber, chunkAccounts);
- DirectoryReconciliationResponse sendChunkResponse = sendChunk(request);
-
- if (sendChunkResponse.getStatus() == DirectoryReconciliationResponse.Status.MISSING ||
- request.getToNumber() == null) {
- reconciliationCache.clearAccelerate();
- }
-
- if (sendChunkResponse.getStatus() == DirectoryReconciliationResponse.Status.OK) {
- reconciliationCache.setLastNumber(Optional.fromNullable(request.getToNumber()));
- } else if (sendChunkResponse.getStatus() == DirectoryReconciliationResponse.Status.MISSING) {
- reconciliationCache.setLastNumber(Optional.absent());
- }
-
- return sendChunkResponse.getStatus() == DirectoryReconciliationResponse.Status.OK;
- }
-
- private List readChunk(Optional fromNumber, int chunkSize) {
- try (Timer.Context timer = readChunkTimer.time()) {
- Optional> chunkAccounts;
-
- if (fromNumber.isPresent()) {
- chunkAccounts = Optional.fromNullable(accounts.getAllFrom(fromNumber.get(), chunkSize));
- } else {
- chunkAccounts = Optional.fromNullable(accounts.getAllFrom(chunkSize));
- }
-
- return chunkAccounts.or(Collections::emptyList);
- }
- }
-
- private void writeChunktoDirectoryCache(List accounts) {
- if (accounts.isEmpty()) {
- return;
- }
-
- BatchOperationHandle batchOperation = directoryManager.startBatchOperation();
- try {
- for (Account account : accounts) {
- if (account.isActive()) {
- byte[] token = Util.getContactToken(account.getNumber());
- ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported(), account.isVideoSupported());
-
- directoryManager.add(batchOperation, clientContact);
- } else {
- directoryManager.remove(batchOperation, account.getNumber());
- }
- }
- } finally {
- directoryManager.stopBatchOperation(batchOperation);
- }
- }
-
- private DirectoryReconciliationRequest createChunkRequest(Optional fromNumber, List accounts) {
- List numbers = accounts.stream()
- .filter(Account::isActive)
- .map(Account::getNumber)
- .collect(Collectors.toList());
-
- Optional toNumber = Optional.absent();
- if (!accounts.isEmpty()) {
- toNumber = Optional.of(accounts.get(accounts.size() - 1).getNumber());
- }
-
- return new DirectoryReconciliationRequest(fromNumber.orNull(), toNumber.orNull(), numbers);
- }
-
- private DirectoryReconciliationResponse sendChunk(DirectoryReconciliationRequest request) {
- try (Timer.Context timer = sendChunkTimer.time()) {
- DirectoryReconciliationResponse response = reconciliationClient.sendChunk(request);
- if (response.getStatus() != DirectoryReconciliationResponse.Status.OK) {
- sendChunkErrorMeter.mark();
- logger.warn("reconciliation error: " + response.getStatus());
- }
- return response;
- } catch (ProcessingException ex) {
- sendChunkErrorMeter.mark();
- logger.warn("request error: ", ex);
- throw new ProcessingException(ex);
- }
- }
-
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciliationCache.java b/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciliationCache.java
index a44c0dcff..118b2ea60 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciliationCache.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciliationCache.java
@@ -16,7 +16,6 @@
*/
package org.whispersystems.textsecuregcm.storage;
-import com.google.common.base.Optional;
import org.whispersystems.textsecuregcm.redis.LuaScript;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import redis.clients.jedis.Jedis;
@@ -24,7 +23,9 @@ import redis.clients.jedis.Jedis;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class DirectoryReconciliationCache {
private static final String ACTIVE_WORKER_KEY = "directory_reconciliation_active_worker";
@@ -62,7 +63,7 @@ public class DirectoryReconciliationCache {
public Optional getLastNumber() {
try (Jedis jedis = jedisPool.getWriteResource()) {
- return Optional.fromNullable(jedis.get(LAST_NUMBER_KEY));
+ return Optional.ofNullable(jedis.get(LAST_NUMBER_KEY));
}
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/Keys.java b/src/main/java/org/whispersystems/textsecuregcm/storage/Keys.java
index 36a55cc2a..2082cf716 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/Keys.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/Keys.java
@@ -16,7 +16,6 @@
*/
package org.whispersystems.textsecuregcm.storage;
-import com.google.common.base.Optional;
import org.skife.jdbi.v2.SQLStatement;
import org.skife.jdbi.v2.StatementContext;
import org.skife.jdbi.v2.TransactionIsolationLevel;
@@ -41,6 +40,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
public abstract class Keys {
@@ -84,7 +84,7 @@ public abstract class Keys {
if (record != null && !record.isLastResort()) {
removeKey(record.getId());
} else if (record == null) {
- return Optional.absent();
+ return Optional.empty();
}
List results = new LinkedList<>();
@@ -106,7 +106,7 @@ public abstract class Keys {
}
if (preKeys != null) return Optional.of(preKeys);
- else return Optional.absent();
+ else return Optional.empty();
}
@SqlUpdate("VACUUM keys")
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/Messages.java b/src/main/java/org/whispersystems/textsecuregcm/storage/Messages.java
index 1439a34bc..4175b479a 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/Messages.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/Messages.java
@@ -21,15 +21,18 @@ import java.lang.annotation.Target;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
+import java.util.UUID;
public abstract class Messages {
static final int RESULT_SET_CHUNK_SIZE = 100;
private static final String ID = "id";
+ private static final String GUID = "guid";
private static final String TYPE = "type";
private static final String RELAY = "relay";
private static final String TIMESTAMP = "timestamp";
+ private static final String SERVER_TIMESTAMP = "server_timestamp";
private static final String SOURCE = "source";
private static final String SOURCE_DEVICE = "source_device";
private static final String DESTINATION = "destination";
@@ -37,11 +40,12 @@ public abstract class Messages {
private static final String MESSAGE = "message";
private static final String CONTENT = "content";
- @SqlUpdate("INSERT INTO messages (" + TYPE + ", " + RELAY + ", " + TIMESTAMP + ", " + SOURCE + ", " + SOURCE_DEVICE + ", " + DESTINATION + ", " + DESTINATION_DEVICE + ", " + MESSAGE + ", " + CONTENT + ") " +
- "VALUES (:type, :relay, :timestamp, :source, :source_device, :destination, :destination_device, :message, :content)")
- abstract void store(@MessageBinder Envelope message,
- @Bind("destination") String destination,
- @Bind("destination_device") long destinationDevice);
+ @SqlUpdate("INSERT INTO messages (" + GUID + ", " + TYPE + ", " + RELAY + ", " + TIMESTAMP + ", " + SERVER_TIMESTAMP + ", " + SOURCE + ", " + SOURCE_DEVICE + ", " + DESTINATION + ", " + DESTINATION_DEVICE + ", " + MESSAGE + ", " + CONTENT + ") " +
+ "VALUES (:guid, :type, :relay, :timestamp, :server_timestamp, :source, :source_device, :destination, :destination_device, :message, :content)")
+ abstract void store(@Bind("guid") UUID guid,
+ @MessageBinder Envelope message,
+ @Bind("destination") String destination,
+ @Bind("destination_device") long destinationDevice);
@Mapper(MessageMapper.class)
@SqlQuery("SELECT * FROM messages WHERE " + DESTINATION + " = :destination AND " + DESTINATION_DEVICE + " = :destination_device ORDER BY " + TIMESTAMP + " ASC LIMIT " + RESULT_SET_CHUNK_SIZE)
@@ -55,6 +59,10 @@ public abstract class Messages {
@Bind("source") String source,
@Bind("timestamp") long timestamp);
+ @Mapper(MessageMapper.class)
+ @SqlQuery("DELETE FROM messages WHERE "+ ID + " IN (SELECT " + ID + " FROM MESSAGES WHERE " + GUID + " = :guid AND " + DESTINATION + " = :destination ORDER BY " + ID + " LIMIT 1) RETURNING *")
+ abstract OutgoingMessageEntity remove(@Bind("destination") String destination, @Bind("guid") UUID guid);
+
@Mapper(MessageMapper.class)
@SqlUpdate("DELETE FROM messages WHERE " + ID + " = :id AND " + DESTINATION + " = :destination")
abstract void remove(@Bind("destination") String destination, @Bind("id") long id);
@@ -79,6 +87,7 @@ public abstract class Messages {
int type = resultSet.getInt(TYPE);
byte[] legacyMessage = resultSet.getBytes(MESSAGE);
+ String guid = resultSet.getString(GUID);
if (type == Envelope.Type.RECEIPT_VALUE && legacyMessage == null) {
/// XXX - REMOVE AFTER 10/01/15
@@ -87,13 +96,15 @@ public abstract class Messages {
return new OutgoingMessageEntity(resultSet.getLong(ID),
false,
+ guid == null ? null : UUID.fromString(guid),
type,
resultSet.getString(RELAY),
resultSet.getLong(TIMESTAMP),
resultSet.getString(SOURCE),
resultSet.getInt(SOURCE_DEVICE),
legacyMessage,
- resultSet.getBytes(CONTENT));
+ resultSet.getBytes(CONTENT),
+ resultSet.getLong(SERVER_TIMESTAMP));
}
}
@@ -113,8 +124,9 @@ public abstract class Messages {
sql.bind(TYPE, message.getType().getNumber());
sql.bind(RELAY, message.getRelay());
sql.bind(TIMESTAMP, message.getTimestamp());
- sql.bind(SOURCE, message.getSource());
- sql.bind(SOURCE_DEVICE, message.getSourceDevice());
+ sql.bind(SERVER_TIMESTAMP, message.getServerTimestamp());
+ sql.bind(SOURCE, message.hasSource() ? message.getSource() : null);
+ sql.bind(SOURCE_DEVICE, message.hasSourceDevice() ? message.getSourceDevice() : null);
sql.bind(MESSAGE, message.hasLegacyMessage() ? message.getLegacyMessage().toByteArray() : null);
sql.bind(CONTENT, message.hasContent() ? message.getContent().toByteArray() : null);
}
@@ -122,6 +134,4 @@ public abstract class Messages {
}
}
}
-
-
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesCache.java b/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesCache.java
index fd3db0678..aef6cbf2b 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesCache.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesCache.java
@@ -4,7 +4,6 @@ import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer;
-import com.google.common.base.Optional;
import com.google.protobuf.InvalidProtocolBufferException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -25,7 +24,9 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -43,6 +44,7 @@ public class MessagesCache implements Managed {
private static final Timer insertTimer = metricRegistry.timer(name(MessagesCache.class, "insert" ));
private static final Timer removeByIdTimer = metricRegistry.timer(name(MessagesCache.class, "removeById" ));
private static final Timer removeByNameTimer = metricRegistry.timer(name(MessagesCache.class, "removeByName"));
+ private static final Timer removeByGuidTimer = metricRegistry.timer(name(MessagesCache.class, "removeByGuid"));
private static final Timer getTimer = metricRegistry.timer(name(MessagesCache.class, "get" ));
private static final Timer clearAccountTimer = metricRegistry.timer(name(MessagesCache.class, "clearAccount"));
private static final Timer clearDeviceTimer = metricRegistry.timer(name(MessagesCache.class, "clearDevice" ));
@@ -67,11 +69,13 @@ public class MessagesCache implements Managed {
this.delayMinutes = delayMinutes;
}
- public void insert(String destination, long destinationDevice, Envelope message) {
+ public void insert(UUID guid, String destination, long destinationDevice, Envelope message) {
+ message = message.toBuilder().setServerGuid(guid.toString()).build();
+
Timer.Context timer = insertTimer.time();
try {
- insertOperation.insert(destination, destinationDevice, System.currentTimeMillis(), message);
+ insertOperation.insert(guid, destination, destinationDevice, System.currentTimeMillis(), message);
} finally {
timer.stop();
}
@@ -103,7 +107,26 @@ public class MessagesCache implements Managed {
timer.stop();
}
- return Optional.absent();
+ return Optional.empty();
+ }
+
+ public Optional remove(String destination, long destinationDevice, UUID guid) {
+ Timer.Context timer = removeByGuidTimer.time();
+
+ try {
+ byte[] serialized = removeOperation.remove(destination, destinationDevice, guid);
+
+ if (serialized != null) {
+ Envelope envelope = Envelope.parseFrom(serialized);
+ return Optional.of(constructEntityFromEnvelope(0, envelope));
+ }
+ } catch (InvalidProtocolBufferException e) {
+ logger.warn("Failed to parse envelope", e);
+ } finally {
+ timer.stop();
+ }
+
+ return Optional.empty();
}
public List get(String destination, long destinationDevice, int limit) {
@@ -175,13 +198,15 @@ public class MessagesCache implements Managed {
private OutgoingMessageEntity constructEntityFromEnvelope(long id, Envelope envelope) {
return new OutgoingMessageEntity(id, true,
+ envelope.hasServerGuid() ? UUID.fromString(envelope.getServerGuid()) : null,
envelope.getType().getNumber(),
envelope.getRelay(),
envelope.getTimestamp(),
envelope.getSource(),
envelope.getSourceDevice(),
envelope.hasLegacyMessage() ? envelope.getLegacyMessage().toByteArray() : null,
- envelope.hasContent() ? envelope.getContent().toByteArray() : null);
+ envelope.hasContent() ? envelope.getContent().toByteArray() : null,
+ envelope.hasServerTimestamp() ? envelope.getServerTimestamp() : 0);
}
private static class Key {
@@ -247,12 +272,12 @@ public class MessagesCache implements Managed {
this.insert = LuaScript.fromResource(jedisPool, "lua/insert_item.lua");
}
- public void insert(String destination, long destinationDevice, long timestamp, Envelope message) {
+ public void insert(UUID guid, String destination, long destinationDevice, long timestamp, Envelope message) {
Key key = new Key(destination, destinationDevice);
- String sender = message.getSource() + "::" + message.getTimestamp();
+ String sender = message.hasSource() ? (message.getSource() + "::" + message.getTimestamp()) : "nil";
List keys = Arrays.asList(key.getUserMessageQueue(), key.getUserMessageQueueMetadata(), Key.getUserMessageQueueIndex());
- List args = Arrays.asList(message.toByteArray(), String.valueOf(timestamp).getBytes(), sender.getBytes());
+ List args = Arrays.asList(message.toByteArray(), String.valueOf(timestamp).getBytes(), sender.getBytes(), guid.toString().getBytes());
insert.execute(keys, args);
}
@@ -262,11 +287,13 @@ public class MessagesCache implements Managed {
private final LuaScript removeById;
private final LuaScript removeBySender;
+ private final LuaScript removeByGuid;
private final LuaScript removeQueue;
RemoveOperation(ReplicatedJedisPool jedisPool) throws IOException {
this.removeById = LuaScript.fromResource(jedisPool, "lua/remove_item_by_id.lua" );
this.removeBySender = LuaScript.fromResource(jedisPool, "lua/remove_item_by_sender.lua");
+ this.removeByGuid = LuaScript.fromResource(jedisPool, "lua/remove_item_by_guid.lua" );
this.removeQueue = LuaScript.fromResource(jedisPool, "lua/remove_queue.lua" );
}
@@ -289,6 +316,15 @@ public class MessagesCache implements Managed {
return (byte[])this.removeBySender.execute(keys, args);
}
+ public byte[] remove(String destination, long destinationDevice, UUID guid) {
+ Key key = new Key(destination, destinationDevice);
+
+ List keys = Arrays.asList(key.getUserMessageQueue(), key.getUserMessageQueueMetadata(), Key.getUserMessageQueueIndex());
+ List args = Collections.singletonList(guid.toString().getBytes());
+
+ return (byte[])this.removeByGuid.execute(keys, args);
+ }
+
public void clear(String destination, long deviceId) {
Key key = new Key(destination, deviceId);
@@ -445,8 +481,11 @@ public class MessagesCache implements Managed {
private void persistMessage(Key key, long score, byte[] message) {
try {
Envelope envelope = Envelope.parseFrom(message);
- database.store(envelope, key.getAddress(), key.getDeviceId());
+ UUID guid = envelope.hasServerGuid() ? UUID.fromString(envelope.getServerGuid()) : null;
+ envelope = envelope.toBuilder().clearServerGuid().build();
+
+ database.store(guid, envelope, key.getAddress(), key.getDeviceId());
} catch (InvalidProtocolBufferException e) {
logger.error("Error parsing envelope", e);
}
@@ -464,9 +503,7 @@ public class MessagesCache implements Managed {
}
}
- private void notifyClients(AccountsManager accountsManager, PubSubManager pubSubManager, PushSender pushSender, Key key)
- throws IOException
- {
+ private void notifyClients(AccountsManager accountsManager, PubSubManager pubSubManager, PushSender pushSender, Key key) {
Timer.Context timer = notifyTimer.time();
try {
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesManager.java b/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesManager.java
index 228885e7a..d34d80444 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesManager.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesManager.java
@@ -4,13 +4,14 @@ package org.whispersystems.textsecuregcm.storage;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
-import com.google.common.base.Optional;
import org.whispersystems.textsecuregcm.entities.MessageProtos.Envelope;
import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntity;
import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntityList;
import org.whispersystems.textsecuregcm.util.Constants;
import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
import static com.codahale.metrics.MetricRegistry.name;
@@ -21,6 +22,9 @@ public class MessagesManager {
private static final Meter cacheMissByIdMeter = metricRegistry.meter(name(MessagesManager.class, "cacheMissById" ));
private static final Meter cacheHitByNameMeter = metricRegistry.meter(name(MessagesManager.class, "cacheHitByName" ));
private static final Meter cacheMissByNameMeter = metricRegistry.meter(name(MessagesManager.class, "cacheMissByName"));
+ private static final Meter cacheHitByGuidMeter = metricRegistry.meter(name(MessagesManager.class, "cacheHitByGuid" ));
+ private static final Meter cacheMissByGuidMeter = metricRegistry.meter(name(MessagesManager.class, "cacheMissByGuid"));
+
private final Messages messages;
private final MessagesCache messagesCache;
@@ -31,7 +35,8 @@ public class MessagesManager {
}
public void insert(String destination, long destinationDevice, Envelope message) {
- messagesCache.insert(destination, destinationDevice, message);
+ UUID guid = UUID.randomUUID();
+ messagesCache.insert(guid, destination, destinationDevice, message);
}
public OutgoingMessageEntityList getMessagesForDevice(String destination, long destinationDevice) {
@@ -59,7 +64,7 @@ public class MessagesManager {
Optional removed = this.messagesCache.remove(destination, destinationDevice, source, timestamp);
if (!removed.isPresent()) {
- removed = Optional.fromNullable(this.messages.remove(destination, destinationDevice, source, timestamp));
+ removed = Optional.ofNullable(this.messages.remove(destination, destinationDevice, source, timestamp));
cacheMissByNameMeter.mark();
} else {
cacheHitByNameMeter.mark();
@@ -68,6 +73,19 @@ public class MessagesManager {
return removed;
}
+ public Optional delete(String destination, long deviceId, UUID guid) {
+ Optional removed = this.messagesCache.remove(destination, deviceId, guid);
+
+ if (!removed.isPresent()) {
+ removed = Optional.ofNullable(this.messages.remove(destination, guid));
+ cacheMissByGuidMeter.mark();
+ } else {
+ cacheHitByGuidMeter.mark();
+ }
+
+ return removed;
+ }
+
public void delete(String destination, long deviceId, long id, boolean cached) {
if (cached) {
this.messagesCache.remove(destination, deviceId, id);
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/PendingAccountsManager.java b/src/main/java/org/whispersystems/textsecuregcm/storage/PendingAccountsManager.java
index a271bdba2..0963a0214 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/PendingAccountsManager.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/PendingAccountsManager.java
@@ -18,7 +18,6 @@ package org.whispersystems.textsecuregcm.storage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
@@ -26,6 +25,7 @@ import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import java.io.IOException;
+import java.util.Optional;
import redis.clients.jedis.Jedis;
@@ -60,7 +60,7 @@ public class PendingAccountsManager {
Optional code = memcacheGet(number);
if (!code.isPresent()) {
- code = Optional.fromNullable(pendingAccounts.getCodeForNumber(number));
+ code = Optional.ofNullable(pendingAccounts.getCodeForNumber(number));
if (code.isPresent()) {
memcacheSet(number, code.get());
@@ -82,11 +82,11 @@ public class PendingAccountsManager {
try (Jedis jedis = cacheClient.getReadResource()) {
String json = jedis.get(CACHE_PREFIX + number);
- if (json == null) return Optional.absent();
+ if (json == null) return Optional.empty();
else return Optional.of(mapper.readValue(json, StoredVerificationCode.class));
} catch (IOException e) {
logger.warn("Error deserializing value...", e);
- return Optional.absent();
+ return Optional.empty();
}
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/PendingDevicesManager.java b/src/main/java/org/whispersystems/textsecuregcm/storage/PendingDevicesManager.java
index 0c131f837..edd594073 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/storage/PendingDevicesManager.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/storage/PendingDevicesManager.java
@@ -18,7 +18,6 @@ package org.whispersystems.textsecuregcm.storage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
@@ -26,6 +25,7 @@ import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import java.io.IOException;
+import java.util.Optional;
import redis.clients.jedis.Jedis;
@@ -59,7 +59,7 @@ public class PendingDevicesManager {
Optional code = memcacheGet(number);
if (!code.isPresent()) {
- code = Optional.fromNullable(pendingDevices.getCodeForNumber(number));
+ code = Optional.ofNullable(pendingDevices.getCodeForNumber(number));
if (code.isPresent()) {
memcacheSet(number, code.get());
@@ -81,11 +81,11 @@ public class PendingDevicesManager {
try (Jedis jedis = cacheClient.getReadResource()) {
String json = jedis.get(CACHE_PREFIX + number);
- if (json == null) return Optional.absent();
+ if (json == null) return Optional.empty();
else return Optional.of(mapper.readValue(json, StoredVerificationCode.class));
} catch (IOException e) {
logger.warn("Could not parse pending device stored verification json");
- return Optional.absent();
+ return Optional.empty();
}
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/util/ByteUtil.java b/src/main/java/org/whispersystems/textsecuregcm/util/ByteUtil.java
new file mode 100644
index 000000000..fa372630e
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/util/ByteUtil.java
@@ -0,0 +1,21 @@
+package org.whispersystems.textsecuregcm.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class ByteUtil {
+
+ public static byte[] combine(byte[]... elements) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ for (byte[] element : elements) {
+ baos.write(element);
+ }
+
+ return baos.toByteArray();
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/util/Util.java b/src/main/java/org/whispersystems/textsecuregcm/util/Util.java
index 849e83083..5d4e60afc 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/util/Util.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/util/Util.java
@@ -20,6 +20,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -120,6 +121,12 @@ public class Util {
return parts;
}
+ public static byte[] generateSecretBytes(int size) {
+ byte[] data = new byte[size];
+ new SecureRandom().nextBytes(data);
+ return data;
+ }
+
public static void sleep(long i) {
try {
Thread.sleep(i);
diff --git a/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java b/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java
index b2864f696..083b5b2d4 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java
@@ -25,9 +25,10 @@ import static com.codahale.metrics.MetricRegistry.name;
public class AuthenticatedConnectListener implements WebSocketConnectListener {
- private static final Logger logger = LoggerFactory.getLogger(WebSocketConnection.class);
- private static final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
- private static final Timer durationTimer = metricRegistry.timer(name(WebSocketConnection.class, "connected_duration"));
+ private static final Logger logger = LoggerFactory.getLogger(WebSocketConnection.class);
+ private static final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
+ private static final Timer durationTimer = metricRegistry.timer(name(WebSocketConnection.class, "connected_duration" ));
+ private static final Timer unauthenticatedDurationTimer = metricRegistry.timer(name(WebSocketConnection.class, "unauthenticated_connection_duration"));
private final PushSender pushSender;
private final ReceiptSender receiptSender;
@@ -50,29 +51,34 @@ public class AuthenticatedConnectListener implements WebSocketConnectListener {
@Override
public void onWebSocketConnect(WebSocketSessionContext context) {
- final Account account = context.getAuthenticated(Account.class);
- final Device device = account.getAuthenticatedDevice().get();
- final String connectionId = String.valueOf(new SecureRandom().nextLong());
- final Timer.Context timer = durationTimer.time();
- final WebsocketAddress address = new WebsocketAddress(account.getNumber(), device.getId());
- final WebSocketConnection connection = new WebSocketConnection(pushSender, receiptSender,
- messagesManager, account, device,
- context.getClient(), connectionId);
- final PubSubMessage connectMessage = PubSubMessage.newBuilder().setType(PubSubMessage.Type.CONNECTED)
- .setContent(ByteString.copyFrom(connectionId.getBytes()))
- .build();
+ if (context.getAuthenticated() != null) {
+ final Account account = context.getAuthenticated(Account.class);
+ final Device device = account.getAuthenticatedDevice().get();
+ final String connectionId = String.valueOf(new SecureRandom().nextLong());
+ final Timer.Context timer = durationTimer.time();
+ final WebsocketAddress address = new WebsocketAddress(account.getNumber(), device.getId());
+ final WebSocketConnection connection = new WebSocketConnection(pushSender, receiptSender,
+ messagesManager, account, device,
+ context.getClient(), connectionId);
+ final PubSubMessage connectMessage = PubSubMessage.newBuilder().setType(PubSubMessage.Type.CONNECTED)
+ .setContent(ByteString.copyFrom(connectionId.getBytes()))
+ .build();
- RedisOperation.unchecked(() -> apnFallbackManager.cancel(account, device));
- pubSubManager.publish(address, connectMessage);
- pubSubManager.subscribe(address, connection);
+ RedisOperation.unchecked(() -> apnFallbackManager.cancel(account, device));
+ pubSubManager.publish(address, connectMessage);
+ pubSubManager.subscribe(address, connection);
- context.addListener(new WebSocketSessionContext.WebSocketEventListener() {
- @Override
- public void onWebSocketClose(WebSocketSessionContext context, int statusCode, String reason) {
- pubSubManager.unsubscribe(address, connection);
- timer.stop();
- }
- });
+ context.addListener(new WebSocketSessionContext.WebSocketEventListener() {
+ @Override
+ public void onWebSocketClose(WebSocketSessionContext context, int statusCode, String reason) {
+ pubSubManager.unsubscribe(address, connection);
+ timer.stop();
+ }
+ });
+ } else {
+ final Timer.Context timer = unauthenticatedDurationTimer.time();
+ context.addListener((context1, statusCode, reason) -> timer.stop());
+ }
}
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/websocket/ProvisioningConnection.java b/src/main/java/org/whispersystems/textsecuregcm/websocket/ProvisioningConnection.java
index 4855f63b7..44fefa510 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/websocket/ProvisioningConnection.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/websocket/ProvisioningConnection.java
@@ -1,6 +1,5 @@
package org.whispersystems.textsecuregcm.websocket;
-import com.google.common.base.Optional;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -13,6 +12,8 @@ import org.whispersystems.textsecuregcm.storage.PubSubProtos.PubSubMessage;
import org.whispersystems.websocket.WebSocketClient;
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
+import java.util.Optional;
+
public class ProvisioningConnection implements DispatchChannel {
private final Logger logger = LoggerFactory.getLogger(ProvisioningConnection.class);
diff --git a/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketAccountAuthenticator.java b/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketAccountAuthenticator.java
index 65a28a2f9..b71ec38f6 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketAccountAuthenticator.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketAccountAuthenticator.java
@@ -1,6 +1,5 @@
package org.whispersystems.textsecuregcm.websocket;
-import com.google.common.base.Optional;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
import org.whispersystems.textsecuregcm.storage.Account;
@@ -10,6 +9,7 @@ import org.whispersystems.websocket.auth.WebSocketAuthenticator;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import io.dropwizard.auth.basic.BasicCredentials;
@@ -23,7 +23,7 @@ public class WebSocketAccountAuthenticator implements WebSocketAuthenticator authenticate(UpgradeRequest request) throws AuthenticationException {
+ public AuthenticationResult authenticate(UpgradeRequest request) throws AuthenticationException {
try {
Map> parameters = request.getParameterMap();
List usernames = parameters.get("login");
@@ -32,13 +32,13 @@ public class WebSocketAccountAuthenticator implements WebSocketAuthenticator(Optional.empty(), false);
}
BasicCredentials credentials = new BasicCredentials(usernames.get(0).replace(" ", "+"),
passwords.get(0).replace(" ", "+"));
- return accountAuthenticator.authenticate(credentials);
+ return new AuthenticationResult<>(accountAuthenticator.authenticate(credentials), true);
} catch (io.dropwizard.auth.AuthenticationException e) {
throw new AuthenticationException(e);
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnection.java b/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnection.java
index 11313393e..b6d9465b6 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnection.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnection.java
@@ -3,7 +3,6 @@ package org.whispersystems.textsecuregcm.websocket;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
-import com.google.common.base.Optional;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -21,24 +20,25 @@ import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntityList;
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
import org.whispersystems.textsecuregcm.push.PushSender;
import org.whispersystems.textsecuregcm.push.ReceiptSender;
-import org.whispersystems.textsecuregcm.push.TransientPushFailureException;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.MessagesManager;
import org.whispersystems.textsecuregcm.util.Constants;
+import org.whispersystems.textsecuregcm.util.Util;
import org.whispersystems.websocket.WebSocketClient;
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.ws.rs.WebApplicationException;
-import java.io.IOException;
import java.util.Iterator;
+import java.util.Optional;
import static com.codahale.metrics.MetricRegistry.name;
import static org.whispersystems.textsecuregcm.entities.MessageProtos.Envelope;
import static org.whispersystems.textsecuregcm.storage.PubSubProtos.PubSubMessage;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class WebSocketConnection implements DispatchChannel {
private static final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
@@ -82,7 +82,7 @@ public class WebSocketConnection implements DispatchChannel {
processStoredMessages();
break;
case PubSubMessage.Type.DELIVER_VALUE:
- sendMessage(Envelope.parseFrom(pubSubMessage.getContent()), Optional.absent(), false);
+ sendMessage(Envelope.parseFrom(pubSubMessage.getContent()), Optional.empty(), false);
break;
case PubSubMessage.Type.CONNECTED_VALUE:
if (pubSubMessage.hasContent() && !new String(pubSubMessage.getContent().toByteArray()).equals(connectionId)) {
@@ -112,7 +112,7 @@ public class WebSocketConnection implements DispatchChannel {
{
try {
EncryptedOutgoingMessage encryptedMessage = new EncryptedOutgoingMessage(message, device.getSignalingKey());
- Optional body = Optional.fromNullable(encryptedMessage.toByteArray());
+ Optional body = Optional.ofNullable(encryptedMessage.toByteArray());
ListenableFuture response = client.sendRequest("PUT", "/api/v1/message", null, body);
Futures.addCallback(response, new FutureCallback() {
@@ -158,14 +158,12 @@ public class WebSocketConnection implements DispatchChannel {
}
private void sendDeliveryReceiptFor(Envelope message) {
+ if (!message.hasSource()) return;
+
try {
- receiptSender.sendReceipt(account, message.getSource(), message.getTimestamp(),
- message.hasRelay() ? Optional.of(message.getRelay()) :
- Optional.absent());
+ receiptSender.sendReceipt(account, message.getSource(), message.getTimestamp());
} catch (NoSuchUserException | NotPushRegisteredException e) {
logger.info("No longer registered " + e.getMessage());
- } catch(IOException | TransientPushFailureException e) {
- logger.warn("Something wrong while sending receipt", e);
} catch (WebApplicationException e) {
logger.warn("Bad federated response for receipt: " + e.getResponse().getStatus());
}
@@ -179,9 +177,13 @@ public class WebSocketConnection implements DispatchChannel {
OutgoingMessageEntity message = iterator.next();
Envelope.Builder builder = Envelope.newBuilder()
.setType(Envelope.Type.valueOf(message.getType()))
- .setSourceDevice(message.getSourceDevice())
- .setSource(message.getSource())
- .setTimestamp(message.getTimestamp());
+ .setTimestamp(message.getTimestamp())
+ .setServerTimestamp(message.getServerTimestamp());
+
+ if (!Util.isEmpty(message.getSource())) {
+ builder.setSource(message.getSource())
+ .setSourceDevice(message.getSourceDevice());
+ }
if (message.getMessage() != null) {
builder.setLegacyMessage(ByteString.copyFrom(message.getMessage()));
@@ -199,7 +201,7 @@ public class WebSocketConnection implements DispatchChannel {
}
if (!messages.hasMore()) {
- client.sendRequest("PUT", "/api/v1/queue/empty", null, Optional.absent());
+ client.sendRequest("PUT", "/api/v1/queue/empty", null, Optional.empty());
}
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/workers/CertificateCommand.java b/src/main/java/org/whispersystems/textsecuregcm/workers/CertificateCommand.java
new file mode 100644
index 000000000..ba199a41b
--- /dev/null
+++ b/src/main/java/org/whispersystems/textsecuregcm/workers/CertificateCommand.java
@@ -0,0 +1,92 @@
+package org.whispersystems.textsecuregcm.workers;
+
+import com.google.common.base.MoreObjects;
+import com.google.protobuf.ByteString;
+import net.sourceforge.argparse4j.impl.Arguments;
+import net.sourceforge.argparse4j.inf.Namespace;
+import net.sourceforge.argparse4j.inf.Subparser;
+import org.whispersystems.curve25519.Curve25519;
+import org.whispersystems.curve25519.Curve25519KeyPair;
+import org.whispersystems.textsecuregcm.crypto.Curve;
+import org.whispersystems.textsecuregcm.crypto.ECKeyPair;
+import org.whispersystems.textsecuregcm.crypto.ECPrivateKey;
+import org.whispersystems.textsecuregcm.entities.MessageProtos;
+import org.whispersystems.textsecuregcm.util.Base64;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+
+import io.dropwizard.cli.Command;
+import io.dropwizard.setup.Bootstrap;
+
+public class CertificateCommand extends Command {
+
+ public CertificateCommand() {
+ super("certificate", "Generates server certificates for unidentified delivery");
+ }
+
+ @Override
+ public void configure(Subparser subparser) {
+ subparser.addArgument("-ca", "--ca")
+ .dest("ca")
+ .action(Arguments.storeTrue())
+ .setDefault(Boolean.FALSE)
+ .help("Generate CA parameters");
+
+ subparser.addArgument("-k", "--key")
+ .dest("key")
+ .type(String.class)
+ .help("The CA private signing key");
+
+ subparser.addArgument("-i", "--id")
+ .dest("keyId")
+ .type(Integer.class)
+ .help("The key ID to create");
+ }
+
+ @Override
+ public void run(Bootstrap> bootstrap, Namespace namespace) throws Exception {
+ if (MoreObjects.firstNonNull(namespace.getBoolean("ca"), false)) runCaCommand();
+ else runCertificateCommand(namespace);
+ }
+
+ private void runCaCommand() {
+ ECKeyPair keyPair = Curve.generateKeyPair();
+ System.out.println("Public key : " + Base64.encodeBytes(keyPair.getPublicKey().serialize()));
+ System.out.println("Private key: " + Base64.encodeBytes(keyPair.getPrivateKey().serialize()));
+ }
+
+ private void runCertificateCommand(Namespace namespace) throws IOException, InvalidKeyException {
+ if (namespace.getString("key") == null) {
+ System.out.println("No key specified!");
+ return;
+ }
+
+ if (namespace.getInt("keyId") == null) {
+ System.out.print("No key id specified!");
+ return;
+ }
+
+ ECPrivateKey key = Curve.decodePrivatePoint(Base64.decode(namespace.getString("key")));
+ int keyId = namespace.getInt("keyId");
+
+ ECKeyPair keyPair = Curve.generateKeyPair();
+
+ byte[] certificate = MessageProtos.ServerCertificate.Certificate.newBuilder()
+ .setId(keyId)
+ .setKey(ByteString.copyFrom(keyPair.getPublicKey().serialize()))
+ .build()
+ .toByteArray();
+
+ byte[] signature = Curve.calculateSignature(key, certificate);
+
+ byte[] signedCertificate = MessageProtos.ServerCertificate.newBuilder()
+ .setCertificate(ByteString.copyFrom(certificate))
+ .setSignature(ByteString.copyFrom(signature))
+ .build()
+ .toByteArray();
+
+ System.out.println("Certificate: " + Base64.encodeBytes(signedCertificate));
+ System.out.println("Private key: " + Base64.encodeBytes(keyPair.getPrivateKey().serialize()));
+ }
+}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java b/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java
index 6c82cbf67..a7f04a0e1 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java
@@ -1,7 +1,6 @@
package org.whispersystems.textsecuregcm.workers;
import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.google.common.base.Optional;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
import org.skife.jdbi.v2.DBI;
@@ -19,6 +18,7 @@ import org.whispersystems.textsecuregcm.storage.DirectoryManager;
import org.whispersystems.textsecuregcm.util.Base64;
import java.security.SecureRandom;
+import java.util.Optional;
import io.dropwizard.Application;
import io.dropwizard.cli.EnvironmentCommand;
diff --git a/src/main/java/org/whispersystems/textsecuregcm/workers/DirectoryCommand.java b/src/main/java/org/whispersystems/textsecuregcm/workers/DirectoryCommand.java
index 824b89778..2f9970b35 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/workers/DirectoryCommand.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/workers/DirectoryCommand.java
@@ -22,7 +22,6 @@ import org.skife.jdbi.v2.DBI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
-import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
import org.whispersystems.textsecuregcm.providers.RedisClientFactory;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.storage.Accounts;
@@ -30,16 +29,13 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.DirectoryManager;
import io.dropwizard.Application;
-import io.dropwizard.cli.ConfiguredCommand;
import io.dropwizard.cli.EnvironmentCommand;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.jdbi.ImmutableListContainerFactory;
import io.dropwizard.jdbi.ImmutableSetContainerFactory;
import io.dropwizard.jdbi.OptionalContainerFactory;
import io.dropwizard.jdbi.args.OptionalArgumentFactory;
-import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
-import redis.clients.jedis.JedisPool;
public class DirectoryCommand extends EnvironmentCommand {
@@ -77,14 +73,10 @@ public class DirectoryCommand extends EnvironmentCommandCREATE RULE bounded_message_queue AS ON INSERT TO messages DO ALSO DELETE FROM messages WHERE id IN (SELECT id FROM messages WHERE destination = NEW.destination AND destination_device = NEW.destination_device ORDER BY timestamp DESC OFFSET 1000);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CREATE INDEX CONCURRENTLY guid_index ON messages (guid);
+
diff --git a/src/test/java/org/whispersystems/dispatch/DispatchManagerTest.java b/src/test/java/org/whispersystems/dispatch/DispatchManagerTest.java
index d6e81c518..784a26024 100644
--- a/src/test/java/org/whispersystems/dispatch/DispatchManagerTest.java
+++ b/src/test/java/org/whispersystems/dispatch/DispatchManagerTest.java
@@ -1,6 +1,6 @@
package org.whispersystems.dispatch;
-import com.google.common.base.Optional;
+import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
@@ -42,7 +42,7 @@ public class DispatchManagerTest {
}
});
- dispatchManager = new DispatchManager(socketFactory, Optional.absent());
+ dispatchManager = new DispatchManager(socketFactory, Optional.empty());
dispatchManager.start();
}
@@ -61,7 +61,7 @@ public class DispatchManagerTest {
public void testSubscribe() throws IOException {
DispatchChannel dispatchChannel = mock(DispatchChannel.class);
dispatchManager.subscribe("foo", dispatchChannel);
- pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.absent()));
+ pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.empty()));
verify(dispatchChannel, timeout(1000)).onDispatchSubscribed(eq("foo"));
}
@@ -72,8 +72,8 @@ public class DispatchManagerTest {
dispatchManager.subscribe("foo", dispatchChannel);
dispatchManager.unsubscribe("foo", dispatchChannel);
- pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.absent()));
- pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.UNSUBSCRIBE, "foo", Optional.absent()));
+ pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.empty()));
+ pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.UNSUBSCRIBE, "foo", Optional.empty()));
verify(dispatchChannel, timeout(1000)).onDispatchUnsubscribed(eq("foo"));
}
@@ -86,8 +86,8 @@ public class DispatchManagerTest {
dispatchManager.subscribe("foo", fooChannel);
dispatchManager.subscribe("bar", barChannel);
- pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.absent()));
- pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "bar", Optional.absent()));
+ pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.empty()));
+ pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "bar", Optional.empty()));
verify(fooChannel, timeout(1000)).onDispatchSubscribed(eq("foo"));
verify(barChannel, timeout(1000)).onDispatchSubscribed(eq("bar"));
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/auth/OptionalAccessTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/auth/OptionalAccessTest.java
new file mode 100644
index 000000000..049349f67
--- /dev/null
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/auth/OptionalAccessTest.java
@@ -0,0 +1,137 @@
+package org.whispersystems.textsecuregcm.tests.auth;
+
+import org.junit.Test;
+import org.whispersystems.textsecuregcm.auth.Anonymous;
+import org.whispersystems.textsecuregcm.auth.OptionalAccess;
+import org.whispersystems.textsecuregcm.storage.Account;
+import org.whispersystems.textsecuregcm.util.Base64;
+
+import javax.ws.rs.WebApplicationException;
+import java.util.Optional;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class OptionalAccessTest {
+
+ @Test
+ public void testUnidentifiedMissingTarget() {
+ try {
+ OptionalAccess.verify(Optional.empty(), Optional.empty(), Optional.empty());
+ throw new AssertionError("should fail");
+ } catch (WebApplicationException e) {
+ assertEquals(e.getResponse().getStatus(), 401);
+ }
+ }
+
+ @Test
+ public void testUnidentifiedMissingTargetDevice() {
+ Account account = mock(Account.class);
+ when(account.isActive()).thenReturn(true);
+ when(account.getDevice(eq(10))).thenReturn(Optional.empty());
+ when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of("1234".getBytes()));
+
+ try {
+ OptionalAccess.verify(Optional.empty(), Optional.of(new Anonymous(Base64.encodeBytes("1234".getBytes()))), Optional.of(account), "10");
+ } catch (WebApplicationException e) {
+ assertEquals(e.getResponse().getStatus(), 401);
+ }
+ }
+
+ @Test
+ public void testUnidentifiedBadTargetDevice() {
+ Account account = mock(Account.class);
+ when(account.isActive()).thenReturn(true);
+ when(account.getDevice(eq(10))).thenReturn(Optional.empty());
+ when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of("1234".getBytes()));
+
+ try {
+ OptionalAccess.verify(Optional.empty(), Optional.of(new Anonymous(Base64.encodeBytes("1234".getBytes()))), Optional.of(account), "$$");
+ } catch (WebApplicationException e) {
+ assertEquals(e.getResponse().getStatus(), 422);
+ }
+ }
+
+
+ @Test
+ public void testUnidentifiedBadCode() {
+ Account account = mock(Account.class);
+ when(account.isActive()).thenReturn(true);
+ when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of("1234".getBytes()));
+
+ try {
+ OptionalAccess.verify(Optional.empty(), Optional.of(new Anonymous(Base64.encodeBytes("5678".getBytes()))), Optional.of(account));
+ throw new AssertionError("should fail");
+ } catch (WebApplicationException e) {
+ assertEquals(e.getResponse().getStatus(), 401);
+ }
+ }
+
+ @Test
+ public void testIdentifiedMissingTarget() {
+ Account account = mock(Account.class);
+ when(account.isActive()).thenReturn(true);
+
+ try {
+ OptionalAccess.verify(Optional.of(account), Optional.empty(), Optional.empty());
+ throw new AssertionError("should fail");
+ } catch (WebApplicationException e) {
+ assertEquals(e.getResponse().getStatus(), 404);
+ }
+ }
+
+ @Test
+ public void testUnsolicitedBadTarget() {
+ Account account = mock(Account.class);
+ when(account.isUnrestrictedUnidentifiedAccess()).thenReturn(false);
+ when(account.isActive()).thenReturn(true);
+
+ try {
+ OptionalAccess.verify(Optional.empty(), Optional.empty(), Optional.of(account));
+ throw new AssertionError("shold fai");
+ } catch (WebApplicationException e) {
+ assertEquals(e.getResponse().getStatus(), 401);
+ }
+ }
+
+ @Test
+ public void testUnsolicitedGoodTarget() {
+ Account account = mock(Account.class);
+ Anonymous random = mock(Anonymous.class);
+ when(account.isUnrestrictedUnidentifiedAccess()).thenReturn(true);
+ when(account.isActive()).thenReturn(true);
+ OptionalAccess.verify(Optional.empty(), Optional.of(random), Optional.of(account));
+ }
+
+ @Test
+ public void testUnidentifiedGoodTarget() {
+ Account account = mock(Account.class);
+ when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of("1234".getBytes()));
+ when(account.isActive()).thenReturn(true);
+ OptionalAccess.verify(Optional.empty(), Optional.of(new Anonymous(Base64.encodeBytes("1234".getBytes()))), Optional.of(account));
+ }
+
+ @Test
+ public void testUnidentifiedInactive() {
+ Account account = mock(Account.class);
+ when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of("1234".getBytes()));
+ when(account.isActive()).thenReturn(false);
+
+ try {
+ OptionalAccess.verify(Optional.empty(), Optional.of(new Anonymous(Base64.encodeBytes("1234".getBytes()))), Optional.of(account));
+ throw new AssertionError();
+ } catch (WebApplicationException e) {
+ assertEquals(e.getResponse().getStatus(), 401);
+ }
+ }
+
+ @Test
+ public void testIdentifiedGoodTarget() {
+ Account source = mock(Account.class);
+ Account target = mock(Account.class);
+ when(target.isActive()).thenReturn(true);
+ OptionalAccess.verify(Optional.of(source), Optional.empty(), Optional.of(target));;
+ }
+}
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java
index 0b057f831..a3752f701 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java
@@ -1,11 +1,9 @@
package org.whispersystems.textsecuregcm.tests.controllers;
-import com.google.common.base.Optional;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider;
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
import org.whispersystems.textsecuregcm.controllers.AccountController;
@@ -30,8 +28,10 @@ import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.HashMap;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
+import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit.ResourceTestRule;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.anyString;
@@ -59,7 +59,7 @@ public class AccountControllerTest {
@Rule
public final ResourceTestRule resources = ResourceTestRule.builder()
.addProvider(AuthHelper.getAuthFilter())
- .addProvider(new AuthValueFactoryProvider.Binder())
+ .addProvider(new AuthValueFactoryProvider.Binder<>(Account.class))
.addProvider(new RateLimitExceededExceptionMapper())
.setMapper(SystemMapper.getMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
@@ -94,8 +94,8 @@ public class AccountControllerTest {
when(accountsManager.get(eq(SENDER_PIN))).thenReturn(Optional.of(senderPinAccount));
when(accountsManager.get(eq(SENDER_OVER_PIN))).thenReturn(Optional.of(senderPinAccount));
- when(accountsManager.get(eq(SENDER))).thenReturn(Optional.absent());
- when(accountsManager.get(eq(SENDER_OLD))).thenReturn(Optional.absent());
+ when(accountsManager.get(eq(SENDER))).thenReturn(Optional.empty());
+ when(accountsManager.get(eq(SENDER_OLD))).thenReturn(Optional.empty());
doThrow(new RateLimitExceededException(SENDER_OVER_PIN)).when(pinLimiter).validate(eq(SENDER_OVER_PIN));
}
@@ -110,7 +110,7 @@ public class AccountControllerTest {
assertThat(response.getStatus()).isEqualTo(200);
- verify(smsSender).deliverSmsVerification(eq(SENDER), eq(Optional.absent()), anyString());
+ verify(smsSender).deliverSmsVerification(eq(SENDER), eq(Optional.empty()), anyString());
}
@Test
@@ -270,6 +270,17 @@ public class AccountControllerTest {
verify(AuthHelper.VALID_ACCOUNT).setPin(eq("31337"));
}
+ @Test
+ public void testSetPinUnauthorized() throws Exception {
+ Response response =
+ resources.getJerseyTest()
+ .target("/v1/accounts/pin/")
+ .request()
+ .put(Entity.json(new RegistrationLock("31337")));
+
+ assertThat(response.getStatus()).isEqualTo(401);
+ }
+
@Test
public void testSetShortPin() throws Exception {
Response response =
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java
index f65451bf9..3e34d01cd 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java
@@ -3,20 +3,20 @@ package org.whispersystems.textsecuregcm.tests.controllers;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.ClassRule;
import org.junit.Test;
-import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider;
import org.whispersystems.textsecuregcm.configuration.AttachmentsConfiguration;
import org.whispersystems.textsecuregcm.controllers.AttachmentController;
import org.whispersystems.textsecuregcm.entities.AttachmentDescriptor;
import org.whispersystems.textsecuregcm.entities.AttachmentUri;
-import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.s3.UrlSigner;
+import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import java.net.MalformedURLException;
+import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit.ResourceTestRule;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -24,10 +24,9 @@ import static org.mockito.Mockito.when;
public class AttachmentControllerTest {
- private static AttachmentsConfiguration configuration = mock(AttachmentsConfiguration.class);
- private static FederatedClientManager federatedClientManager = mock(FederatedClientManager.class );
- private static RateLimiters rateLimiters = mock(RateLimiters.class );
- private static RateLimiter rateLimiter = mock(RateLimiter.class );
+ private static AttachmentsConfiguration configuration = mock(AttachmentsConfiguration.class);
+ private static RateLimiters rateLimiters = mock(RateLimiters.class );
+ private static RateLimiter rateLimiter = mock(RateLimiter.class );
private static UrlSigner urlSigner;
@@ -43,10 +42,10 @@ public class AttachmentControllerTest {
@ClassRule
public static final ResourceTestRule resources = ResourceTestRule.builder()
.addProvider(AuthHelper.getAuthFilter())
- .addProvider(new AuthValueFactoryProvider.Binder())
+ .addProvider(new AuthValueFactoryProvider.Binder<>(Account.class))
.setMapper(SystemMapper.getMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
- .addResource(new AttachmentController(rateLimiters, federatedClientManager, urlSigner))
+ .addResource(new AttachmentController(rateLimiters, urlSigner))
.build();
@Test
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java
new file mode 100644
index 000000000..c77aa8dd4
--- /dev/null
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java
@@ -0,0 +1,113 @@
+package org.whispersystems.textsecuregcm.tests.controllers;
+
+import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
+import org.whispersystems.textsecuregcm.auth.OptionalAccess;
+import org.whispersystems.textsecuregcm.controllers.CertificateController;
+import org.whispersystems.textsecuregcm.crypto.Curve;
+import org.whispersystems.textsecuregcm.entities.DeliveryCertificate;
+import org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate;
+import org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate;
+import org.whispersystems.textsecuregcm.storage.Account;
+import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
+import org.whispersystems.textsecuregcm.util.Base64;
+import org.whispersystems.textsecuregcm.util.SystemMapper;
+
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.util.Arrays;
+
+import io.dropwizard.auth.AuthValueFactoryProvider;
+import io.dropwizard.testing.junit.ResourceTestRule;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+public class CertificateControllerTest {
+
+ private static final String caPublicKey = "BWh+UOhT1hD8bkb+MFRvb6tVqhoG8YYGCzOd7mgjo8cV";
+ private static final String caPrivateKey = "EO3Mnf0kfVlVnwSaqPoQnAxhnnGL1JTdXqktCKEe9Eo=";
+
+ private static final String signingCertificate = "CiUIDBIhBbTz4h1My+tt+vw+TVscgUe/DeHS0W02tPWAWbTO2xc3EkD+go4bJnU0AcnFfbOLKoiBfCzouZtDYMOVi69rE7r4U9cXREEqOkUmU2WJBjykAxWPCcSTmVTYHDw7hkSp/puG";
+ private static final String signingKey = "ABOxG29xrfq4E7IrW11Eg7+HBbtba9iiS0500YoBjn4=";
+
+ private static CertificateGenerator certificateGenerator;
+
+ static {
+ try {
+ certificateGenerator = new CertificateGenerator(Base64.decode(signingCertificate), Curve.decodePrivatePoint(Base64.decode(signingKey)), 1);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+
+ @ClassRule
+ public static final ResourceTestRule resources = ResourceTestRule.builder()
+ .addProvider(AuthHelper.getAuthFilter())
+ .addProvider(new AuthValueFactoryProvider.Binder<>(Account.class))
+ .setMapper(SystemMapper.getMapper())
+ .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
+ .addResource(new CertificateController(certificateGenerator))
+ .build();
+
+
+ @Test
+ public void testValidCertificate() throws Exception {
+ DeliveryCertificate certificateObject = resources.getJerseyTest()
+ .target("/v1/certificate/delivery")
+ .request()
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
+ .get(DeliveryCertificate.class);
+
+
+ SenderCertificate certificateHolder = SenderCertificate.parseFrom(certificateObject.getCertificate());
+ SenderCertificate.Certificate certificate = SenderCertificate.Certificate.parseFrom(certificateHolder.getCertificate());
+
+ ServerCertificate serverCertificateHolder = certificate.getSigner();
+ ServerCertificate.Certificate serverCertificate = ServerCertificate.Certificate.parseFrom(serverCertificateHolder.getCertificate());
+
+ assertTrue(Curve.verifySignature(Curve.decodePoint(serverCertificate.getKey().toByteArray(), 0), certificateHolder.getCertificate().toByteArray(), certificateHolder.getSignature().toByteArray()));
+ assertTrue(Curve.verifySignature(Curve.decodePoint(Base64.decode(caPublicKey), 0), serverCertificateHolder.getCertificate().toByteArray(), serverCertificateHolder.getSignature().toByteArray()));
+
+ assertEquals(certificate.getSender(), AuthHelper.VALID_NUMBER);
+ assertEquals(certificate.getSenderDevice(), 1L);
+ assertTrue(Arrays.equals(certificate.getIdentityKey().toByteArray(), Base64.decode(AuthHelper.VALID_IDENTITY)));
+ }
+
+ @Test
+ public void testBadAuthentication() throws Exception {
+ Response response = resources.getJerseyTest()
+ .target("/v1/certificate/delivery")
+ .request()
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.INVALID_PASSWORD))
+ .get();
+
+ assertEquals(response.getStatus(), 401);
+ }
+
+
+ @Test
+ public void testNoAuthentication() throws Exception {
+ Response response = resources.getJerseyTest()
+ .target("/v1/certificate/delivery")
+ .request()
+ .get();
+
+ assertEquals(response.getStatus(), 401);
+ }
+
+
+ @Test
+ public void testUnidentifiedAuthentication() throws Exception {
+ Response response = resources.getJerseyTest()
+ .target("/v1/certificate/delivery")
+ .request()
+ .header(OptionalAccess.UNIDENTIFIED, AuthHelper.getUnidentifiedAccessHeader("1234".getBytes()))
+ .get();
+
+ assertEquals(response.getStatus(), 401);
+ }
+
+}
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java
index 5b4019160..02155987a 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java
@@ -16,12 +16,10 @@
*/
package org.whispersystems.textsecuregcm.tests.controllers;
-import com.google.common.base.Optional;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider;
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
import org.whispersystems.textsecuregcm.controllers.DeviceController;
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
@@ -40,8 +38,10 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
+import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit.ResourceTestRule;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
@@ -83,7 +83,7 @@ public class DeviceControllerTest {
@Rule
public final ResourceTestRule resources = ResourceTestRule.builder()
.addProvider(AuthHelper.getAuthFilter())
- .addProvider(new AuthValueFactoryProvider.Binder())
+ .addProvider(new AuthValueFactoryProvider.Binder<>(Account.class))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addProvider(new DeviceLimitExceededExceptionMapper())
.addResource(new DumbVerificationDeviceController(pendingDevicesManager,
@@ -202,15 +202,4 @@ public class DeviceControllerTest {
verifyNoMoreInteractions(messagesManager);
}
- @Test
- public void removeDeviceTest() throws Exception {
- Response response = resources.getJerseyTest()
- .target("/v1/devices/12345")
- .request()
- .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
- .delete();
-
- assertEquals(204, response.getStatus());
- verify(directoryQueue).deleteRegisteredUser(eq(AuthHelper.VALID_NUMBER));
- }
}
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java
index 342d3e124..553f7dc92 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java
@@ -1,21 +1,18 @@
package org.whispersystems.textsecuregcm.tests.controllers;
-import com.google.common.base.Optional;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider;
import org.whispersystems.textsecuregcm.auth.DirectoryCredentials;
import org.whispersystems.textsecuregcm.auth.DirectoryCredentialsGenerator;
-import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration;
-import org.whispersystems.textsecuregcm.configuration.DirectoryClientConfiguration;
import org.whispersystems.textsecuregcm.controllers.DirectoryController;
import org.whispersystems.textsecuregcm.entities.ClientContactTokens;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
+import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.DirectoryManager;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.util.Base64;
@@ -26,11 +23,11 @@ import javax.ws.rs.core.Response;
import java.util.LinkedList;
import java.util.List;
+import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit.ResourceTestRule;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyListOf;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyList;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -46,7 +43,7 @@ public class DirectoryControllerTest {
@Rule
public final ResourceTestRule resources = ResourceTestRule.builder()
.addProvider(AuthHelper.getAuthFilter())
- .addProvider(new AuthValueFactoryProvider.Binder())
+ .addProvider(new AuthValueFactoryProvider.Binder<>(Account.class))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new DirectoryController(rateLimiters,
directoryManager,
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java
deleted file mode 100644
index fe2516d12..000000000
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package org.whispersystems.textsecuregcm.tests.controllers;
-
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Optional;
-import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider;
-import org.whispersystems.textsecuregcm.controllers.FederationControllerV1;
-import org.whispersystems.textsecuregcm.controllers.FederationControllerV2;
-import org.whispersystems.textsecuregcm.controllers.KeysController;
-import org.whispersystems.textsecuregcm.controllers.MessageController;
-import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
-import org.whispersystems.textsecuregcm.entities.MessageProtos;
-import org.whispersystems.textsecuregcm.entities.PreKeyResponseItem;
-import org.whispersystems.textsecuregcm.entities.PreKeyResponse;
-import org.whispersystems.textsecuregcm.entities.SignedPreKey;
-import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
-import org.whispersystems.textsecuregcm.limits.RateLimiter;
-import org.whispersystems.textsecuregcm.limits.RateLimiters;
-import org.whispersystems.textsecuregcm.push.ApnFallbackManager;
-import org.whispersystems.textsecuregcm.push.PushSender;
-import org.whispersystems.textsecuregcm.push.ReceiptSender;
-import org.whispersystems.textsecuregcm.storage.Account;
-import org.whispersystems.textsecuregcm.storage.AccountsManager;
-import org.whispersystems.textsecuregcm.storage.Device;
-import org.whispersystems.textsecuregcm.storage.MessagesManager;
-import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
-
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Set;
-
-import io.dropwizard.testing.junit.ResourceTestRule;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
-import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.jsonFixture;
-
-public class FederatedControllerTest {
-
- private static final String SINGLE_DEVICE_RECIPIENT = "+14151111111";
- private static final String MULTI_DEVICE_RECIPIENT = "+14152222222";
-
- private PushSender pushSender = mock(PushSender.class );
- private ReceiptSender receiptSender = mock(ReceiptSender.class);
- private FederatedClientManager federatedClientManager = mock(FederatedClientManager.class);
- private AccountsManager accountsManager = mock(AccountsManager.class );
- private MessagesManager messagesManager = mock(MessagesManager.class);
- private RateLimiters rateLimiters = mock(RateLimiters.class );
- private RateLimiter rateLimiter = mock(RateLimiter.class );
- private ApnFallbackManager apnFallbackManager = mock(ApnFallbackManager.class);
-
- private final SignedPreKey signedPreKey = new SignedPreKey(3333, "foo", "baar");
- private final PreKeyResponse preKeyResponseV2 = new PreKeyResponse("foo", new LinkedList());
-
- private final ObjectMapper mapper = new ObjectMapper();
-
- private final MessageController messageController = new MessageController(rateLimiters, pushSender, receiptSender, accountsManager, messagesManager, federatedClientManager, apnFallbackManager);
- private final KeysController keysControllerV2 = mock(KeysController.class);
-
- @Rule
- public final ResourceTestRule resources = ResourceTestRule.builder()
- .addProvider(AuthHelper.getAuthFilter())
- .addProvider(new AuthValueFactoryProvider.Binder())
- .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
- .addResource(new FederationControllerV1(accountsManager, null, messageController))
- .addResource(new FederationControllerV2(accountsManager, null, messageController, keysControllerV2))
- .build();
-
-
-
- @Before
- public void setup() throws Exception {
- Set singleDeviceList = new HashSet() {{
- add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
- }};
-
- Set multiDeviceList = new HashSet() {{
- add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(222, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
- add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(333, "rad", "mad"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
- }};
-
- Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList);
- Account multiDeviceAccount = new Account(MULTI_DEVICE_RECIPIENT, multiDeviceList);
-
- when(accountsManager.get(eq(SINGLE_DEVICE_RECIPIENT))).thenReturn(Optional.of(singleDeviceAccount));
- when(accountsManager.get(eq(MULTI_DEVICE_RECIPIENT))).thenReturn(Optional.of(multiDeviceAccount));
-
- when(rateLimiters.getMessagesLimiter()).thenReturn(rateLimiter);
-
- when(keysControllerV2.getSignedKey(any(Account.class))).thenReturn(Optional.of(signedPreKey));
- when(keysControllerV2.getDeviceKeys(any(Account.class), anyString(), anyString(), any(Optional.class)))
- .thenReturn(Optional.of(preKeyResponseV2));
- }
-
- @Test
- public void testSingleDeviceCurrent() throws Exception {
- Response response =
- resources.getJerseyTest()
- .target(String.format("/v1/federation/messages/+14152223333/1/%s", SINGLE_DEVICE_RECIPIENT))
- .request()
- .header("Authorization", AuthHelper.getAuthHeader("cyanogen", "foofoo"))
- .put(Entity.entity(mapper.readValue(jsonFixture("fixtures/current_message_single_device.json"), IncomingMessageList.class),
- MediaType.APPLICATION_JSON_TYPE));
-
- assertThat("Good Response", response.getStatus(), is(equalTo(204)));
-
- verify(pushSender).sendMessage(any(Account.class), any(Device.class), any(MessageProtos.Envelope.class));
- }
-
- @Test
- public void testSignedPreKeyV2() throws Exception {
- PreKeyResponse response =
- resources.getJerseyTest()
- .target("/v2/federation/key/+14152223333/1")
- .request()
- .header("Authorization", AuthHelper.getAuthHeader("cyanogen", "foofoo"))
- .get(PreKeyResponse.class);
-
- assertThat("good response", response.getIdentityKey().equals(preKeyResponseV2.getIdentityKey()));
- }
-
-}
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java
index 2c585f44f..90377a81c 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java
@@ -1,12 +1,11 @@
package org.whispersystems.textsecuregcm.tests.controllers;
-import com.google.common.base.Optional;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
-import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider;
+import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.controllers.KeysController;
import org.whispersystems.textsecuregcm.entities.PreKey;
import org.whispersystems.textsecuregcm.entities.PreKeyCount;
@@ -28,8 +27,10 @@ import javax.ws.rs.core.Response;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
+import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit.ResourceTestRule;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
@@ -63,9 +64,9 @@ public class KeyControllerTest {
@Rule
public final ResourceTestRule resources = ResourceTestRule.builder()
.addProvider(AuthHelper.getAuthFilter())
- .addProvider(new AuthValueFactoryProvider.Binder())
+ .addProvider(new AuthValueFactoryProvider.Binder<>(Account.class))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
- .addResource(new KeysController(rateLimiters, keys, accounts, null))
+ .addResource(new KeysController(rateLimiters, keys, accounts))
.build();
@Before
@@ -103,14 +104,15 @@ public class KeyControllerTest {
when(existsAccount.getDevice(2L)).thenReturn(Optional.of(sampleDevice2));
when(existsAccount.getDevice(3L)).thenReturn(Optional.of(sampleDevice3));
when(existsAccount.getDevice(4L)).thenReturn(Optional.of(sampleDevice4));
- when(existsAccount.getDevice(22L)).thenReturn(Optional.absent());
+ when(existsAccount.getDevice(22L)).thenReturn(Optional.empty());
when(existsAccount.getDevices()).thenReturn(allDevices);
when(existsAccount.isActive()).thenReturn(true);
when(existsAccount.getIdentityKey()).thenReturn("existsidentitykey");
when(existsAccount.getNumber()).thenReturn(EXISTS_NUMBER);
+ when(existsAccount.getUnidentifiedAccessKey()).thenReturn(Optional.of("1337".getBytes()));
when(accounts.get(EXISTS_NUMBER)).thenReturn(Optional.of(existsAccount));
- when(accounts.get(NOT_EXISTS_NUMBER)).thenReturn(Optional.absent());
+ when(accounts.get(NOT_EXISTS_NUMBER)).thenReturn(Optional.empty());
when(rateLimiters.getPreKeysLimiter()).thenReturn(rateLimiter);
@@ -118,7 +120,7 @@ public class KeyControllerTest {
singleDevice.add(SAMPLE_KEY);
when(keys.get(eq(EXISTS_NUMBER), eq(1L))).thenReturn(Optional.of(singleDevice));
- when(keys.get(eq(NOT_EXISTS_NUMBER), eq(1L))).thenReturn(Optional.>absent());
+ when(keys.get(eq(NOT_EXISTS_NUMBER), eq(1L))).thenReturn(Optional.>empty());
List multiDevice = new LinkedList<>();
multiDevice.add(SAMPLE_KEY);
@@ -191,6 +193,49 @@ public class KeyControllerTest {
verifyNoMoreInteractions(keys);
}
+ @Test
+ public void testUnidentifiedRequest() throws Exception {
+ PreKeyResponse result = resources.getJerseyTest()
+ .target(String.format("/v2/keys/%s/1", EXISTS_NUMBER))
+ .request()
+ .header(OptionalAccess.UNIDENTIFIED, AuthHelper.getUnidentifiedAccessHeader("1337".getBytes()))
+ .get(PreKeyResponse.class);
+
+ assertThat(result.getIdentityKey()).isEqualTo(existsAccount.getIdentityKey());
+ assertThat(result.getDevicesCount()).isEqualTo(1);
+ assertThat(result.getDevice(1).getPreKey().getKeyId()).isEqualTo(SAMPLE_KEY.getKeyId());
+ assertThat(result.getDevice(1).getPreKey().getPublicKey()).isEqualTo(SAMPLE_KEY.getPublicKey());
+ assertThat(result.getDevice(1).getSignedPreKey()).isEqualTo(existsAccount.getDevice(1).get().getSignedPreKey());
+
+ verify(keys).get(eq(EXISTS_NUMBER), eq(1L));
+ verifyNoMoreInteractions(keys);
+ }
+
+ @Test
+ public void testUnauthorizedUnidentifiedRequest() throws Exception {
+ Response response = resources.getJerseyTest()
+ .target(String.format("/v2/keys/%s/1", EXISTS_NUMBER))
+ .request()
+ .header(OptionalAccess.UNIDENTIFIED, AuthHelper.getUnidentifiedAccessHeader("9999".getBytes()))
+ .get();
+
+ assertThat(response.getStatus()).isEqualTo(401);
+ verifyNoMoreInteractions(keys);
+ }
+
+ @Test
+ public void testMalformedUnidentifiedRequest() throws Exception {
+ Response response = resources.getJerseyTest()
+ .target(String.format("/v2/keys/%s/1", EXISTS_NUMBER))
+ .request()
+ .header(OptionalAccess.UNIDENTIFIED, "$$$$$$$$$")
+ .get();
+
+ assertThat(response.getStatus()).isEqualTo(401);
+ verifyNoMoreInteractions(keys);
+ }
+
+
@Test
public void validMultiRequestTestV2() throws Exception {
PreKeyResponse results = resources.getJerseyTest()
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java
index 8261e2928..1738ac6b2 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java
@@ -1,12 +1,12 @@
package org.whispersystems.textsecuregcm.tests.controllers;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Optional;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider;
+import org.mockito.ArgumentCaptor;
+import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.controllers.MessageController;
import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
import org.whispersystems.textsecuregcm.entities.MessageProtos.Envelope;
@@ -15,7 +15,6 @@ import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntity;
import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntityList;
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
import org.whispersystems.textsecuregcm.entities.StaleDevices;
-import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.push.ApnFallbackManager;
@@ -26,6 +25,7 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.MessagesManager;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
+import org.whispersystems.textsecuregcm.util.Base64;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
@@ -33,14 +33,17 @@ import javax.ws.rs.core.Response;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
+import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit.ResourceTestRule;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
@@ -54,7 +57,6 @@ public class MessageControllerTest {
private final PushSender pushSender = mock(PushSender.class );
private final ReceiptSender receiptSender = mock(ReceiptSender.class);
- private final FederatedClientManager federatedClientManager = mock(FederatedClientManager.class);
private final AccountsManager accountsManager = mock(AccountsManager.class );
private final MessagesManager messagesManager = mock(MessagesManager.class);
private final RateLimiters rateLimiters = mock(RateLimiters.class );
@@ -66,27 +68,27 @@ public class MessageControllerTest {
@Rule
public final ResourceTestRule resources = ResourceTestRule.builder()
.addProvider(AuthHelper.getAuthFilter())
- .addProvider(new AuthValueFactoryProvider.Binder())
+ .addProvider(new AuthValueFactoryProvider.Binder<>(Account.class))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new MessageController(rateLimiters, pushSender, receiptSender, accountsManager,
- messagesManager, federatedClientManager, apnFallbackManager))
+ messagesManager, apnFallbackManager))
.build();
@Before
public void setup() throws Exception {
Set singleDeviceList = new HashSet() {{
- add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
+ add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", true));
}};
Set multiDeviceList = new HashSet() {{
- add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
- add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
- add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis(), false, false, "Test"));
+ add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", true));
+ add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", true));
+ add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis(), "Test", true));
}};
- Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList);
- Account multiDeviceAccount = new Account(MULTI_DEVICE_RECIPIENT, multiDeviceList);
+ Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList, "1234".getBytes());
+ Account multiDeviceAccount = new Account(MULTI_DEVICE_RECIPIENT, multiDeviceList, "1234".getBytes());
when(accountsManager.get(eq(SINGLE_DEVICE_RECIPIENT))).thenReturn(Optional.of(singleDeviceAccount));
when(accountsManager.get(eq(MULTI_DEVICE_RECIPIENT))).thenReturn(Optional.of(multiDeviceAccount));
@@ -106,7 +108,43 @@ public class MessageControllerTest {
assertThat("Good Response", response.getStatus(), is(equalTo(200)));
- verify(pushSender, times(1)).sendMessage(any(Account.class), any(Device.class), any(Envelope.class));
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Envelope.class);
+ verify(pushSender, times(1)).sendMessage(any(Account.class), any(Device.class), captor.capture());
+
+ assertTrue(captor.getValue().hasSource());
+ assertTrue(captor.getValue().hasSourceDevice());
+ }
+
+ @Test
+ public synchronized void testSingleDeviceCurrentUnidentified() throws Exception {
+ Response response =
+ resources.getJerseyTest()
+ .target(String.format("/v1/messages/%s", SINGLE_DEVICE_RECIPIENT))
+ .request()
+ .header(OptionalAccess.UNIDENTIFIED, Base64.encodeBytes("1234".getBytes()))
+ .put(Entity.entity(mapper.readValue(jsonFixture("fixtures/current_message_single_device.json"), IncomingMessageList.class),
+ MediaType.APPLICATION_JSON_TYPE));
+
+ assertThat("Good Response", response.getStatus(), is(equalTo(200)));
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Envelope.class);
+ verify(pushSender, times(1)).sendMessage(any(Account.class), any(Device.class), captor.capture());
+
+ assertFalse(captor.getValue().hasSource());
+ assertFalse(captor.getValue().hasSourceDevice());
+ }
+
+
+ @Test
+ public synchronized void testSendBadAuth() throws Exception {
+ Response response =
+ resources.getJerseyTest()
+ .target(String.format("/v1/messages/%s", SINGLE_DEVICE_RECIPIENT))
+ .request()
+ .put(Entity.entity(mapper.readValue(jsonFixture("fixtures/current_message_single_device.json"), IncomingMessageList.class),
+ MediaType.APPLICATION_JSON_TYPE));
+
+ assertThat("Good Response", response.getStatus(), is(equalTo(401)));
}
@Test
@@ -187,9 +225,11 @@ public class MessageControllerTest {
final long timestampOne = 313377;
final long timestampTwo = 313388;
+ final UUID uuidOne = UUID.randomUUID();
+
List messages = new LinkedList() {{
- add(new OutgoingMessageEntity(1L, false, Envelope.Type.CIPHERTEXT_VALUE, null, timestampOne, "+14152222222", 2, "hi there".getBytes(), null));
- add(new OutgoingMessageEntity(2L, false, Envelope.Type.RECEIPT_VALUE, null, timestampTwo, "+14152222222", 2, null, null));
+ add(new OutgoingMessageEntity(1L, false, uuidOne, Envelope.Type.CIPHERTEXT_VALUE, null, timestampOne, "+14152222222", 2, "hi there".getBytes(), null, 0));
+ add(new OutgoingMessageEntity(2L, false, null, Envelope.Type.RECEIPT_VALUE, null, timestampTwo, "+14152222222", 2, null, null, 0));
}};
OutgoingMessageEntityList messagesList = new OutgoingMessageEntityList(messages, false);
@@ -211,6 +251,9 @@ public class MessageControllerTest {
assertEquals(response.getMessages().get(0).getTimestamp(), timestampOne);
assertEquals(response.getMessages().get(1).getTimestamp(), timestampTwo);
+
+ assertEquals(response.getMessages().get(0).getGuid(), uuidOne);
+ assertEquals(response.getMessages().get(1).getGuid(), null);
}
@Test
@@ -219,8 +262,8 @@ public class MessageControllerTest {
final long timestampTwo = 313388;
List messages = new LinkedList() {{
- add(new OutgoingMessageEntity(1L, false, Envelope.Type.CIPHERTEXT_VALUE, null, timestampOne, "+14152222222", 2, "hi there".getBytes(), null));
- add(new OutgoingMessageEntity(2L, false, Envelope.Type.RECEIPT_VALUE, null, timestampTwo, "+14152222222", 2, null, null));
+ add(new OutgoingMessageEntity(1L, false, UUID.randomUUID(), Envelope.Type.CIPHERTEXT_VALUE, null, timestampOne, "+14152222222", 2, "hi there".getBytes(), null, 0));
+ add(new OutgoingMessageEntity(2L, false, UUID.randomUUID(), Envelope.Type.RECEIPT_VALUE, null, timestampTwo, "+14152222222", 2, null, null, 0));
}};
OutgoingMessageEntityList messagesList = new OutgoingMessageEntityList(messages, false);
@@ -240,21 +283,22 @@ public class MessageControllerTest {
@Test
public synchronized void testDeleteMessages() throws Exception {
long timestamp = System.currentTimeMillis();
+
when(messagesManager.delete(AuthHelper.VALID_NUMBER, 1, "+14152222222", 31337))
- .thenReturn(Optional.of(new OutgoingMessageEntity(31337L, true,
+ .thenReturn(Optional.of(new OutgoingMessageEntity(31337L, true, null,
Envelope.Type.CIPHERTEXT_VALUE,
null, timestamp,
- "+14152222222", 1, "hi".getBytes(), null)));
+ "+14152222222", 1, "hi".getBytes(), null, 0)));
when(messagesManager.delete(AuthHelper.VALID_NUMBER, 1, "+14152222222", 31338))
- .thenReturn(Optional.of(new OutgoingMessageEntity(31337L, true,
+ .thenReturn(Optional.of(new OutgoingMessageEntity(31337L, true, null,
Envelope.Type.RECEIPT_VALUE,
null, System.currentTimeMillis(),
- "+14152222222", 1, null, null)));
+ "+14152222222", 1, null, null, 0)));
when(messagesManager.delete(AuthHelper.VALID_NUMBER, 1, "+14152222222", 31339))
- .thenReturn(Optional.absent());
+ .thenReturn(Optional.empty());
Response response = resources.getJerseyTest()
.target(String.format("/v1/messages/%s/%d", "+14152222222", 31337))
@@ -263,7 +307,7 @@ public class MessageControllerTest {
.delete();
assertThat("Good Response Code", response.getStatus(), is(equalTo(204)));
- verify(receiptSender).sendReceipt(any(Account.class), eq("+14152222222"), eq(timestamp), eq(Optional.absent()));
+ verify(receiptSender).sendReceipt(any(Account.class), eq("+14152222222"), eq(timestamp));
response = resources.getJerseyTest()
.target(String.format("/v1/messages/%s/%d", "+14152222222", 31338))
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java
index c105cbb08..fa427abc4 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java
@@ -1,11 +1,9 @@
package org.whispersystems.textsecuregcm.tests.controllers;
-import com.google.common.base.Optional;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
-import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider;
import org.whispersystems.textsecuregcm.configuration.ProfilesConfiguration;
import org.whispersystems.textsecuregcm.controllers.ProfileController;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
@@ -17,6 +15,10 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.util.SystemMapper;
+import javax.ws.rs.core.Response;
+import java.util.Optional;
+
+import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit.ResourceTestRule;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.mockito.Mockito.*;
@@ -38,7 +40,7 @@ public class ProfileControllerTest {
@ClassRule
public static final ResourceTestRule resources = ResourceTestRule.builder()
.addProvider(AuthHelper.getAuthFilter())
- .addProvider(new AuthValueFactoryProvider.Binder())
+ .addProvider(new AuthValueFactoryProvider.Binder<>(Account.class))
.setMapper(SystemMapper.getMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new ProfileController(rateLimiters,
@@ -53,9 +55,10 @@ public class ProfileControllerTest {
Account profileAccount = mock(Account.class);
when(profileAccount.getIdentityKey()).thenReturn("bar");
- when(profileAccount.getName()).thenReturn("baz");
+ when(profileAccount.getProfileName()).thenReturn("baz");
when(profileAccount.getAvatar()).thenReturn("profiles/bang");
when(profileAccount.getAvatarDigest()).thenReturn("buh");
+ when(profileAccount.isActive()).thenReturn(true);
when(accountsManager.get(AuthHelper.VALID_NUMBER_TWO)).thenReturn(Optional.of(profileAccount));
}
@@ -78,5 +81,14 @@ public class ProfileControllerTest {
verify(rateLimiter, times(1)).validate(AuthHelper.VALID_NUMBER);
}
+ @Test
+ public void testProfileGetUnauthorized() throws Exception {
+ Response response = resources.getJerseyTest()
+ .target("/v1/profile/" + AuthHelper.VALID_NUMBER_TWO)
+ .request()
+ .get();
+
+ assertThat(response.getStatus()).isEqualTo(401);
+ }
}
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/push/APNSenderTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/push/APNSenderTest.java
index 050854448..cbfc4e7f9 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/push/APNSenderTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/push/APNSenderTest.java
@@ -1,6 +1,5 @@
package org.whispersystems.textsecuregcm.tests.push;
-import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
import com.relayrides.pushy.apns.ApnsClient;
import com.relayrides.pushy.apns.ApnsServerException;
@@ -22,6 +21,7 @@ import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.tests.util.SynchronousExecutorService;
import java.util.Date;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import io.netty.util.concurrent.DefaultEventExecutor;
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/push/GCMSenderTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/push/GCMSenderTest.java
index 2b4742de5..1d84ba5a3 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/push/GCMSenderTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/push/GCMSenderTest.java
@@ -1,6 +1,5 @@
package org.whispersystems.textsecuregcm.tests.push;
-import com.google.common.base.Optional;
import com.google.common.util.concurrent.SettableFuture;
import org.junit.Test;
import org.mockito.Matchers;
@@ -15,6 +14,8 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.tests.util.SynchronousExecutorService;
+import java.util.Optional;
+
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java
index 62250f834..66e17bd87 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java
@@ -6,6 +6,7 @@ import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
import java.util.HashSet;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertFalse;
@@ -49,21 +50,21 @@ public class AccountTest {
Account recentAccount = new Account("+14152222222", new HashSet() {{
add(recentMasterDevice);
add(recentSecondaryDevice);
- }});
+ }}, "1234".getBytes());
assertTrue(recentAccount.isActive());
Account oldSecondaryAccount = new Account("+14152222222", new HashSet() {{
add(recentMasterDevice);
add(agingSecondaryDevice);
- }});
+ }}, "1234".getBytes());
assertTrue(oldSecondaryAccount.isActive());
Account agingPrimaryAccount = new Account("+14152222222", new HashSet() {{
add(oldMasterDevice);
add(agingSecondaryDevice);
- }});
+ }}, "1234".getBytes());
assertTrue(agingPrimaryAccount.isActive());
}
@@ -73,7 +74,7 @@ public class AccountTest {
Account oldPrimaryAccount = new Account("+14152222222", new HashSet() {{
add(oldMasterDevice);
add(oldSecondaryDevice);
- }});
+ }}, "1234".getBytes());
assertFalse(oldPrimaryAccount.isActive());
}
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/storage/DirectoryReconcilerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/storage/DirectoryReconcilerTest.java
index b225272ba..3120c59fb 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/storage/DirectoryReconcilerTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/storage/DirectoryReconcilerTest.java
@@ -1,6 +1,5 @@
package org.whispersystems.textsecuregcm.tests.storage;
-import com.google.common.base.Optional;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -18,17 +17,14 @@ import org.whispersystems.textsecuregcm.util.Util;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
public class DirectoryReconcilerTest {
@@ -53,8 +49,6 @@ public class DirectoryReconcilerTest {
public void setup() {
when(account.getNumber()).thenReturn(VALID_NUMBER);
when(account.isActive()).thenReturn(true);
- when(account.isVideoSupported()).thenReturn(true);
- when(account.isVoiceSupported()).thenReturn(true);
when(inactiveAccount.getNumber()).thenReturn(INACTIVE_NUMBER);
when(inactiveAccount.isActive()).thenReturn(false);
@@ -66,7 +60,7 @@ public class DirectoryReconcilerTest {
when(reconciliationClient.sendChunk(any())).thenReturn(successResponse);
- when(reconciliationCache.getLastNumber()).thenReturn(Optional.absent());
+ when(reconciliationCache.getLastNumber()).thenReturn(Optional.empty());
when(reconciliationCache.claimActiveWork(any(), anyLong())).thenReturn(true);
when(reconciliationCache.isAccelerated()).thenReturn(false);
}
@@ -155,7 +149,7 @@ public class DirectoryReconcilerTest {
assertThat(request.getValue().getNumbers()).isEqualTo(Collections.emptyList());
verify(reconciliationCache, times(1)).getLastNumber();
- verify(reconciliationCache, times(1)).setLastNumber(eq(Optional.absent()));
+ verify(reconciliationCache, times(1)).setLastNumber(eq(Optional.empty()));
verify(reconciliationCache, times(1)).clearAccelerate();
verify(reconciliationCache, times(1)).isAccelerated();
verify(reconciliationCache, times(2)).claimActiveWork(any(), anyLong());
@@ -192,7 +186,7 @@ public class DirectoryReconcilerTest {
assertThat(addedContact.getValue().getToken()).isEqualTo(Util.getContactToken(VALID_NUMBER));
verify(reconciliationCache, times(1)).getLastNumber();
- verify(reconciliationCache, times(1)).setLastNumber(eq(Optional.absent()));
+ verify(reconciliationCache, times(1)).setLastNumber(eq(Optional.empty()));
verify(reconciliationCache, times(1)).clearAccelerate();
verify(reconciliationCache, times(1)).claimActiveWork(any(), anyLong());
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/util/AuthHelper.java b/src/test/java/org/whispersystems/textsecuregcm/tests/util/AuthHelper.java
index 69e511f6c..be160d9e3 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/util/AuthHelper.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/util/AuthHelper.java
@@ -1,21 +1,16 @@
package org.whispersystems.textsecuregcm.tests.util;
-import com.google.common.base.Optional;
-import org.whispersystems.dropwizard.simpleauth.AuthDynamicFeature;
-import org.whispersystems.dropwizard.simpleauth.BasicCredentialAuthFilter;
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
-import org.whispersystems.textsecuregcm.auth.FederatedPeerAuthenticator;
-import org.whispersystems.textsecuregcm.configuration.FederationConfiguration;
-import org.whispersystems.textsecuregcm.federation.FederatedPeer;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.Base64;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.Optional;
+import io.dropwizard.auth.AuthDynamicFeature;
+import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -31,6 +26,8 @@ public class AuthHelper {
public static final String INVVALID_NUMBER = "+14151111111";
public static final String INVALID_PASSWORD = "bar";
+ public static final String VALID_IDENTITY = "BcxxDU9FGMda70E7+Uvm7pnQcEdXQ64aJCpPUeRSfcFo";
+
public static AccountsManager ACCOUNTS_MANAGER = mock(AccountsManager.class );
public static Account VALID_ACCOUNT = mock(Account.class );
public static Account VALID_ACCOUNT_TWO = mock(Account.class);
@@ -53,29 +50,24 @@ public class AuthHelper {
when(VALID_ACCOUNT_TWO.getNumber()).thenReturn(VALID_NUMBER_TWO);
when(VALID_ACCOUNT.getAuthenticatedDevice()).thenReturn(Optional.of(VALID_DEVICE));
when(VALID_ACCOUNT_TWO.getAuthenticatedDevice()).thenReturn(Optional.of(VALID_DEVICE_TWO));
- when(VALID_ACCOUNT.getRelay()).thenReturn(Optional.absent());
- when(VALID_ACCOUNT_TWO.getRelay()).thenReturn(Optional.absent());
+ when(VALID_ACCOUNT.getRelay()).thenReturn(Optional.empty());
+ when(VALID_ACCOUNT_TWO.getRelay()).thenReturn(Optional.empty());
+ when(VALID_ACCOUNT.isActive()).thenReturn(true);
+ when(VALID_ACCOUNT_TWO.isActive()).thenReturn(true);
+ when(VALID_ACCOUNT.getIdentityKey()).thenReturn(VALID_IDENTITY);
when(ACCOUNTS_MANAGER.get(VALID_NUMBER)).thenReturn(Optional.of(VALID_ACCOUNT));
when(ACCOUNTS_MANAGER.get(VALID_NUMBER_TWO)).thenReturn(Optional.of(VALID_ACCOUNT_TWO));
- List peer = new LinkedList() {{
- add(new FederatedPeer("cyanogen", "https://foo", "foofoo", "bazzzzz"));
- }};
-
- FederationConfiguration federationConfiguration = mock(FederationConfiguration.class);
- when(federationConfiguration.getPeers()).thenReturn(peer);
-
return new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder()
.setAuthenticator(new AccountAuthenticator(ACCOUNTS_MANAGER))
- .setPrincipal(Account.class)
- .buildAuthFilter(),
- new BasicCredentialAuthFilter.Builder()
- .setAuthenticator(new FederatedPeerAuthenticator(federationConfiguration))
- .setPrincipal(FederatedPeer.class)
.buildAuthFilter());
}
public static String getAuthHeader(String number, String password) {
return "Basic " + Base64.encodeBytes((number + ":" + password).getBytes());
}
+
+ public static String getUnidentifiedAccessHeader(byte[] key) {
+ return Base64.encodeBytes(key);
+ }
}
diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java
index fb7eb8c34..5de50f645 100644
--- a/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java
+++ b/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java
@@ -1,6 +1,5 @@
package org.whispersystems.textsecuregcm.tests.websocket;
-import com.google.common.base.Optional;
import com.google.common.util.concurrent.SettableFuture;
import com.google.protobuf.ByteString;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
@@ -27,6 +26,8 @@ import org.whispersystems.textsecuregcm.websocket.WebSocketAccountAuthenticator;
import org.whispersystems.textsecuregcm.websocket.WebSocketConnection;
import org.whispersystems.textsecuregcm.websocket.WebsocketAddress;
import org.whispersystems.websocket.WebSocketClient;
+import org.whispersystems.websocket.auth.WebSocketAuthenticator;
+import org.whispersystems.websocket.auth.WebSocketAuthenticator.AuthenticationResult;
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
import org.whispersystems.websocket.session.WebSocketSessionContext;
@@ -35,7 +36,9 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import io.dropwizard.auth.basic.BasicCredentials;
import static org.junit.Assert.*;
@@ -72,7 +75,7 @@ public class WebSocketConnectionTest {
.thenReturn(Optional.of(account));
when(accountAuthenticator.authenticate(eq(new BasicCredentials(INVALID_USER, INVALID_PASSWORD))))
- .thenReturn(Optional.absent());
+ .thenReturn(Optional.empty());
when(account.getAuthenticatedDevice()).thenReturn(Optional.of(device));
@@ -81,8 +84,8 @@ public class WebSocketConnectionTest {
put("password", new LinkedList() {{add(VALID_PASSWORD);}});
}});
- Optional account = webSocketAuthenticator.authenticate(upgradeRequest);
- when(sessionContext.getAuthenticated(Account.class)).thenReturn(account.get());
+ AuthenticationResult account = webSocketAuthenticator.authenticate(upgradeRequest);
+ when(sessionContext.getAuthenticated(Account.class)).thenReturn(account.getUser().orElse(null));
connectListener.onWebSocketConnect(sessionContext);
@@ -94,7 +97,8 @@ public class WebSocketConnectionTest {
}});
account = webSocketAuthenticator.authenticate(upgradeRequest);
- assertFalse(account.isPresent());
+ assertFalse(account.getUser().isPresent());
+ assertTrue(account.isRequired());
}
@Test
@@ -125,7 +129,7 @@ public class WebSocketConnectionTest {
when(sender1.getDevices()).thenReturn(sender1devices);
when(accountsManager.get("sender1")).thenReturn(Optional.of(sender1));
- when(accountsManager.get("sender2")).thenReturn(Optional.absent());
+ when(accountsManager.get("sender2")).thenReturn(Optional.empty());
when(storedMessages.getMessagesForDevice(account.getNumber(), device.getId()))
.thenReturn(outgoingMessagesList);
@@ -160,7 +164,7 @@ public class WebSocketConnectionTest {
futures.get(2).setException(new IOException());
verify(storedMessages, times(1)).delete(eq(account.getNumber()), eq(2L), eq(2L), eq(false));
- verify(receiptSender, times(1)).sendReceipt(eq(account), eq("sender1"), eq(2222L), eq(Optional.absent()));
+ verify(receiptSender, times(1)).sendReceipt(eq(account), eq("sender1"), eq(2222L));
connection.onDispatchUnsubscribed(websocketAddress.serialize());
verify(client).close(anyInt(), anyString());
@@ -208,7 +212,7 @@ public class WebSocketConnectionTest {
when(sender1.getDevices()).thenReturn(sender1devices);
when(accountsManager.get("sender1")).thenReturn(Optional.of(sender1));
- when(accountsManager.get("sender2")).thenReturn(Optional.absent());
+ when(accountsManager.get("sender2")).thenReturn(Optional.empty());
when(storedMessages.getMessagesForDevice(account.getNumber(), device.getId()))
.thenReturn(pendingMessagesList);
@@ -250,7 +254,7 @@ public class WebSocketConnectionTest {
futures.get(1).set(response);
futures.get(0).setException(new IOException());
- verify(receiptSender, times(1)).sendReceipt(eq(account), eq("sender2"), eq(secondMessage.getTimestamp()), eq(Optional.absent()));
+ verify(receiptSender, times(1)).sendReceipt(eq(account), eq("sender2"), eq(secondMessage.getTimestamp()));
verify(websocketSender, times(1)).queueMessage(eq(account), eq(device), any(Envelope.class));
verify(pushSender, times(1)).sendQueuedNotification(eq(account), eq(device));
@@ -285,14 +289,14 @@ public class WebSocketConnectionTest {
.build();
List pendingMessages = new LinkedList() {{
- add(new OutgoingMessageEntity(1, true, firstMessage.getType().getNumber(), firstMessage.getRelay(),
+ add(new OutgoingMessageEntity(1, true, UUID.randomUUID(), firstMessage.getType().getNumber(), firstMessage.getRelay(),
firstMessage.getTimestamp(), firstMessage.getSource(),
firstMessage.getSourceDevice(), firstMessage.getLegacyMessage().toByteArray(),
- firstMessage.getContent().toByteArray()));
- add(new OutgoingMessageEntity(2, false, secondMessage.getType().getNumber(), secondMessage.getRelay(),
+ firstMessage.getContent().toByteArray(), 0));
+ add(new OutgoingMessageEntity(2, false, UUID.randomUUID(), secondMessage.getType().getNumber(), secondMessage.getRelay(),
secondMessage.getTimestamp(), secondMessage.getSource(),
secondMessage.getSourceDevice(), secondMessage.getLegacyMessage().toByteArray(),
- secondMessage.getContent().toByteArray()));
+ secondMessage.getContent().toByteArray(), 0));
}};
OutgoingMessageEntityList pendingMessagesList = new OutgoingMessageEntityList(pendingMessages, false);
@@ -313,7 +317,7 @@ public class WebSocketConnectionTest {
when(sender1.getDevices()).thenReturn(sender1devices);
when(accountsManager.get("sender1")).thenReturn(Optional.of(sender1));
- when(accountsManager.get("sender2")).thenReturn(Optional.absent());
+ when(accountsManager.get("sender2")).thenReturn(Optional.empty());
when(storedMessages.getMessagesForDevice(account.getNumber(), device.getId()))
.thenReturn(pendingMessagesList);
@@ -346,7 +350,7 @@ public class WebSocketConnectionTest {
futures.get(1).set(response);
futures.get(0).setException(new IOException());
- verify(receiptSender, times(1)).sendReceipt(eq(account), eq("sender2"), eq(secondMessage.getTimestamp()), eq(Optional.absent()));
+ verify(receiptSender, times(1)).sendReceipt(eq(account), eq("sender2"), eq(secondMessage.getTimestamp()));
verifyNoMoreInteractions(websocketSender);
verifyNoMoreInteractions(pushSender);
@@ -356,8 +360,8 @@ public class WebSocketConnectionTest {
private OutgoingMessageEntity createMessage(long id, boolean cached, String sender, long timestamp, boolean receipt, String content) {
- return new OutgoingMessageEntity(id, cached, receipt ? Envelope.Type.RECEIPT_VALUE : Envelope.Type.CIPHERTEXT_VALUE,
- null, timestamp, sender, 1, content.getBytes(), null);
+ return new OutgoingMessageEntity(id, cached, UUID.randomUUID(), receipt ? Envelope.Type.RECEIPT_VALUE : Envelope.Type.CIPHERTEXT_VALUE,
+ null, timestamp, sender, 1, content.getBytes(), null, 0);
}
}