From 35180b41bcc968ef17ab0edc2268097092fea848 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Fri, 5 May 2017 10:36:29 -0700 Subject: [PATCH] Remove all the old V1 keys stuff // FREEBIE --- .../textsecuregcm/WhisperServerService.java | 8 +- .../controllers/FederationControllerV2.java | 18 +- .../controllers/KeysController.java | 147 +++++++++++++- .../controllers/KeysControllerV2.java | 183 ------------------ .../entities/{PreKeyV2.java => PreKey.java} | 12 +- .../textsecuregcm/entities/PreKeyBase.java | 8 - ...KeyResponseV2.java => PreKeyResponse.java} | 12 +- ...nseItemV2.java => PreKeyResponseItem.java} | 10 +- .../entities/PreKeyResponseV1.java | 70 ------- .../{PreKeyStateV2.java => PreKeyState.java} | 21 +- .../textsecuregcm/entities/PreKeyStateV1.java | 55 ------ .../textsecuregcm/entities/PreKeyV1.java | 96 --------- .../textsecuregcm/entities/SignedPreKey.java | 4 +- .../federation/FederatedClient.java | 30 +-- .../textsecuregcm/storage/Keys.java | 9 +- .../controllers/FederatedControllerTest.java | 14 +- .../tests/controllers/KeyControllerTest.java | 46 ++--- .../tests/entities/PreKeyTest.java | 15 +- 18 files changed, 219 insertions(+), 539 deletions(-) delete mode 100644 src/main/java/org/whispersystems/textsecuregcm/controllers/KeysControllerV2.java rename src/main/java/org/whispersystems/textsecuregcm/entities/{PreKeyV2.java => PreKey.java} (87%) delete mode 100644 src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyBase.java rename src/main/java/org/whispersystems/textsecuregcm/entities/{PreKeyResponseV2.java => PreKeyResponse.java} (82%) rename src/main/java/org/whispersystems/textsecuregcm/entities/{PreKeyResponseItemV2.java => PreKeyResponseItem.java} (86%) delete mode 100644 src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV1.java rename src/main/java/org/whispersystems/textsecuregcm/entities/{PreKeyStateV2.java => PreKeyState.java} (76%) delete mode 100644 src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyStateV1.java delete mode 100644 src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyV1.java diff --git a/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 1fb800d72..200662698 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -39,7 +39,7 @@ 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.KeysControllerV2; +import org.whispersystems.textsecuregcm.controllers.KeysController; import org.whispersystems.textsecuregcm.controllers.MessageController; import org.whispersystems.textsecuregcm.controllers.ProvisioningController; import org.whispersystems.textsecuregcm.controllers.ReceiptController; @@ -194,7 +194,7 @@ public class WhisperServerService extends Application() @@ -211,11 +211,11 @@ public class WhisperServerService extends Application getKeysV2(@Auth FederatedPeer peer, - @PathParam("number") String number, - @PathParam("device") String device) + public Optional getKeysV2(@Auth FederatedPeer peer, + @PathParam("number") String number, + @PathParam("device") String device) throws IOException { try { - return keysControllerV2.getDeviceKeys(new NonLimitedAccount("Unknown", -1, peer.getName()), - number, device, Optional.absent()); + 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 f7b1e0018..68a11a8fb 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysController.java +++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysController.java @@ -16,12 +16,19 @@ */ 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.entities.PreKey; +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; @@ -29,23 +36,31 @@ import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.KeyRecord; import org.whispersystems.textsecuregcm.storage.Keys; +import javax.validation.Valid; +import javax.ws.rs.Consumes; 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.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 io.dropwizard.auth.Auth; +@Path("/v2/keys") public class KeysController { private static final Logger logger = LoggerFactory.getLogger(KeysController.class); - protected final RateLimiters rateLimiters; - protected final Keys keys; - protected final AccountsManager accounts; - protected final FederatedClientManager federatedClientManager; + private final RateLimiters rateLimiters; + private final Keys keys; + private final AccountsManager accounts; + private final FederatedClientManager federatedClientManager; public KeysController(RateLimiters rateLimiters, Keys keys, AccountsManager accounts, FederatedClientManager federatedClientManager) @@ -68,7 +83,103 @@ public class KeysController { return new PreKeyCount(count); } - protected Optional> getLocalKeys(Account destination, String deviceIdSelector) + @Timed + @PUT + @Consumes(MediaType.APPLICATION_JSON) + public void setKeys(@Auth Account account, @Valid PreKeyState preKeys) { + Device device = account.getAuthenticatedDevice().get(); + boolean updateAccount = false; + + if (!preKeys.getSignedPreKey().equals(device.getSignedPreKey())) { + device.setSignedPreKey(preKeys.getSignedPreKey()); + updateAccount = true; + } + + if (!preKeys.getIdentityKey().equals(account.getIdentityKey())) { + account.setIdentityKey(preKeys.getIdentityKey()); + updateAccount = true; + } + + if (updateAccount) { + accounts.update(account); + } + + keys.store(account.getNumber(), device.getId(), preKeys.getPreKeys()); + } + + @Timed + @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) + throws RateLimitExceededException + { + try { + if (relay.isPresent()) { + return federatedClientManager.getClient(relay.get()).getKeysV2(number, deviceId); + } + + Account target = getAccount(number, deviceId); + + if (account.isRateLimited()) { + rateLimiters.getPreKeysLimiter().validate(account.getNumber() + "__" + number + "." + deviceId); + } + + Optional> targetKeys = getLocalKeys(target, deviceId); + List devices = new LinkedList<>(); + + for (Device device : target.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 (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()); + } + } + + @Timed + @PUT + @Path("/signed") + @Consumes(MediaType.APPLICATION_JSON) + public void setSignedKey(@Auth Account account, @Valid SignedPreKey signedPreKey) { + Device device = account.getAuthenticatedDevice().get(); + device.setSignedPreKey(signedPreKey); + accounts.update(account); + } + + @Timed + @GET + @Path("/signed") + @Produces(MediaType.APPLICATION_JSON) + public Optional getSignedKey(@Auth Account account) { + Device device = account.getAuthenticatedDevice().get(); + SignedPreKey signedPreKey = device.getSignedPreKey(); + + if (signedPreKey != null) return Optional.of(signedPreKey); + else return Optional.absent(); + } + + private Optional> getLocalKeys(Account destination, String deviceIdSelector) throws NoSuchUserException { try { @@ -91,4 +202,30 @@ 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/KeysControllerV2.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysControllerV2.java deleted file mode 100644 index d54f0646d..000000000 --- a/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysControllerV2.java +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Copyright (C) 2014 Open Whisper Systems - * - * 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.whispersystems.textsecuregcm.entities.SignedPreKey; -import org.whispersystems.textsecuregcm.entities.PreKeyResponseItemV2; -import org.whispersystems.textsecuregcm.entities.PreKeyResponseV2; -import org.whispersystems.textsecuregcm.entities.PreKeyStateV2; -import org.whispersystems.textsecuregcm.entities.PreKeyV2; -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; -import org.whispersystems.textsecuregcm.storage.Device; -import org.whispersystems.textsecuregcm.storage.KeyRecord; -import org.whispersystems.textsecuregcm.storage.Keys; - -import javax.validation.Valid; -import javax.ws.rs.Consumes; -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.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 io.dropwizard.auth.Auth; - -@Path("/v2/keys") -public class KeysControllerV2 extends KeysController { - - public KeysControllerV2(RateLimiters rateLimiters, Keys keys, AccountsManager accounts, - FederatedClientManager federatedClientManager) - { - super(rateLimiters, keys, accounts, federatedClientManager); - } - - - @Timed - @PUT - @Consumes(MediaType.APPLICATION_JSON) - public void setKeys(@Auth Account account, @Valid PreKeyStateV2 preKeys) { - Device device = account.getAuthenticatedDevice().get(); - boolean updateAccount = false; - - if (!preKeys.getSignedPreKey().equals(device.getSignedPreKey())) { - device.setSignedPreKey(preKeys.getSignedPreKey()); - updateAccount = true; - } - - if (!preKeys.getIdentityKey().equals(account.getIdentityKey())) { - account.setIdentityKey(preKeys.getIdentityKey()); - updateAccount = true; - } - - if (updateAccount) { - accounts.update(account); - } - - keys.store(account.getNumber(), device.getId(), preKeys.getPreKeys(), preKeys.getLastResortKey()); - } - - @Timed - @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) - throws RateLimitExceededException - { - try { - if (relay.isPresent()) { - return federatedClientManager.getClient(relay.get()).getKeysV2(number, deviceId); - } - - Account target = getAccount(number, deviceId); - - if (account.isRateLimited()) { - rateLimiters.getPreKeysLimiter().validate(account.getNumber() + "__" + number + "." + deviceId); - } - - Optional> targetKeys = getLocalKeys(target, deviceId); - List devices = new LinkedList<>(); - - for (Device device : target.getDevices()) { - if (device.isActive() && (deviceId.equals("*") || device.getId() == Long.parseLong(deviceId))) { - SignedPreKey signedPreKey = device.getSignedPreKey(); - PreKeyV2 preKey = null; - - if (targetKeys.isPresent()) { - for (KeyRecord keyRecord : targetKeys.get()) { - if (!keyRecord.isLastResort() && keyRecord.getDeviceId() == device.getId()) { - preKey = new PreKeyV2(keyRecord.getKeyId(), keyRecord.getPublicKey()); - } - } - } - - if (signedPreKey != null || preKey != null) { - devices.add(new PreKeyResponseItemV2(device.getId(), device.getRegistrationId(), signedPreKey, preKey)); - } - } - } - - if (devices.isEmpty()) return Optional.absent(); - else return Optional.of(new PreKeyResponseV2(target.getIdentityKey(), devices)); - } catch (NoSuchPeerException | NoSuchUserException e) { - throw new WebApplicationException(Response.status(404).build()); - } - } - - @Timed - @PUT - @Path("/signed") - @Consumes(MediaType.APPLICATION_JSON) - public void setSignedKey(@Auth Account account, @Valid SignedPreKey signedPreKey) { - Device device = account.getAuthenticatedDevice().get(); - device.setSignedPreKey(signedPreKey); - accounts.update(account); - } - - @Timed - @GET - @Path("/signed") - @Produces(MediaType.APPLICATION_JSON) - public Optional getSignedKey(@Auth Account account) { - Device device = account.getAuthenticatedDevice().get(); - SignedPreKey signedPreKey = device.getSignedPreKey(); - - if (signedPreKey != null) return Optional.of(signedPreKey); - else return Optional.absent(); - } - - 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/entities/PreKeyV2.java b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKey.java similarity index 87% rename from src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyV2.java rename to src/main/java/org/whispersystems/textsecuregcm/entities/PreKey.java index 59de13e80..9feb775fc 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyV2.java +++ b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKey.java @@ -22,7 +22,7 @@ import org.hibernate.validator.constraints.NotEmpty; import javax.validation.constraints.NotNull; -public class PreKeyV2 implements PreKeyBase { +public class PreKey { @JsonProperty @NotNull @@ -32,15 +32,14 @@ public class PreKeyV2 implements PreKeyBase { @NotEmpty private String publicKey; - public PreKeyV2() {} + public PreKey() {} - public PreKeyV2(long keyId, String publicKey) + public PreKey(long keyId, String publicKey) { this.keyId = keyId; this.publicKey = publicKey; } - @Override public String getPublicKey() { return publicKey; } @@ -49,7 +48,6 @@ public class PreKeyV2 implements PreKeyBase { this.publicKey = publicKey; } - @Override public long getKeyId() { return keyId; } @@ -60,8 +58,8 @@ public class PreKeyV2 implements PreKeyBase { @Override public boolean equals(Object object) { - if (object == null || !(object instanceof PreKeyV2)) return false; - PreKeyV2 that = (PreKeyV2)object; + if (object == null || !(object instanceof PreKey)) return false; + PreKey that = (PreKey)object; if (publicKey == null) { return this.keyId == that.keyId && that.publicKey == null; diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyBase.java b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyBase.java deleted file mode 100644 index cf83943c5..000000000 --- a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyBase.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.whispersystems.textsecuregcm.entities; - -public interface PreKeyBase { - - public long getKeyId(); - public String getPublicKey(); - -} diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV2.java b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponse.java similarity index 82% rename from src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV2.java rename to src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponse.java index 4f3e779fe..3ee9fb63c 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV2.java +++ b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponse.java @@ -22,17 +22,17 @@ import com.google.common.annotations.VisibleForTesting; import java.util.List; -public class PreKeyResponseV2 { +public class PreKeyResponse { @JsonProperty private String identityKey; @JsonProperty - private List devices; + private List devices; - public PreKeyResponseV2() {} + public PreKeyResponse() {} - public PreKeyResponseV2(String identityKey, List devices) { + public PreKeyResponse(String identityKey, List devices) { this.identityKey = identityKey; this.devices = devices; } @@ -44,8 +44,8 @@ public class PreKeyResponseV2 { @VisibleForTesting @JsonIgnore - public PreKeyResponseItemV2 getDevice(int deviceId) { - for (PreKeyResponseItemV2 device : devices) { + public PreKeyResponseItem getDevice(int deviceId) { + for (PreKeyResponseItem device : devices) { if (device.getDeviceId() == deviceId) return device; } diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseItemV2.java b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseItem.java similarity index 86% rename from src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseItemV2.java rename to src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseItem.java index 4e1d5973a..bbce557ad 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseItemV2.java +++ b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseItem.java @@ -19,7 +19,7 @@ package org.whispersystems.textsecuregcm.entities; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; -public class PreKeyResponseItemV2 { +public class PreKeyResponseItem { @JsonProperty private long deviceId; @@ -31,11 +31,11 @@ public class PreKeyResponseItemV2 { private SignedPreKey signedPreKey; @JsonProperty - private PreKeyV2 preKey; + private PreKey preKey; - public PreKeyResponseItemV2() {} + public PreKeyResponseItem() {} - public PreKeyResponseItemV2(long deviceId, int registrationId, SignedPreKey signedPreKey, PreKeyV2 preKey) { + public PreKeyResponseItem(long deviceId, int registrationId, SignedPreKey signedPreKey, PreKey preKey) { this.deviceId = deviceId; this.registrationId = registrationId; this.signedPreKey = signedPreKey; @@ -48,7 +48,7 @@ public class PreKeyResponseItemV2 { } @VisibleForTesting - public PreKeyV2 getPreKey() { + public PreKey getPreKey() { return preKey; } diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV1.java b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV1.java deleted file mode 100644 index b5fe49ecf..000000000 --- a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV1.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (C) 2014 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.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.annotations.VisibleForTesting; - -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -public class PreKeyResponseV1 { - - @JsonProperty - @NotNull - @Valid - private List keys; - - @VisibleForTesting - public PreKeyResponseV1() {} - - public PreKeyResponseV1(PreKeyV1 preKey) { - this.keys = new LinkedList<>(); - this.keys.add(preKey); - } - - public PreKeyResponseV1(List preKeys) { - this.keys = preKeys; - } - - public List getKeys() { - return keys; - } - - @VisibleForTesting - public boolean equals(Object o) { - if (!(o instanceof PreKeyResponseV1) || - ((PreKeyResponseV1) o).keys.size() != keys.size()) - return false; - Iterator otherKeys = ((PreKeyResponseV1) o).keys.iterator(); - for (PreKeyV1 key : keys) { - if (!otherKeys.next().equals(key)) - return false; - } - return true; - } - - public int hashCode() { - int ret = 0xFBA4C795 * keys.size(); - for (PreKeyV1 key : keys) - ret ^= key.getPublicKey().hashCode(); - return ret; - } -} diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyStateV2.java b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyState.java similarity index 76% rename from src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyStateV2.java rename to src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyState.java index 78ed3a7c5..55956b175 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyStateV2.java +++ b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyState.java @@ -25,40 +25,32 @@ import javax.validation.Valid; import javax.validation.constraints.NotNull; import java.util.List; -public class PreKeyStateV2 { +public class PreKeyState { @JsonProperty @NotNull @Valid - private List preKeys; + private List preKeys; @JsonProperty @NotNull @Valid private SignedPreKey signedPreKey; - @JsonProperty - @NotNull - @Valid - private PreKeyV2 lastResortKey; - @JsonProperty @NotEmpty private String identityKey; - public PreKeyStateV2() {} + public PreKeyState() {} @VisibleForTesting - public PreKeyStateV2(String identityKey, SignedPreKey signedPreKey, - List keys, PreKeyV2 lastResortKey) - { + public PreKeyState(String identityKey, SignedPreKey signedPreKey, List keys) { this.identityKey = identityKey; this.signedPreKey = signedPreKey; this.preKeys = keys; - this.lastResortKey = lastResortKey; } - public List getPreKeys() { + public List getPreKeys() { return preKeys; } @@ -70,7 +62,4 @@ public class PreKeyStateV2 { return identityKey; } - public PreKeyV2 getLastResortKey() { - return lastResortKey; - } } diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyStateV1.java b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyStateV1.java deleted file mode 100644 index 7c789eee2..000000000 --- a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyStateV1.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (C) 2014 Open Whisper Systems - * - * 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.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.annotations.VisibleForTesting; - -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import java.util.List; - -public class PreKeyStateV1 { - - @JsonProperty - @NotNull - @Valid - private PreKeyV1 lastResortKey; - - @JsonProperty - @NotNull - @Valid - private List keys; - - public List getKeys() { - return keys; - } - - @VisibleForTesting - public void setKeys(List keys) { - this.keys = keys; - } - - public PreKeyV1 getLastResortKey() { - return lastResortKey; - } - - @VisibleForTesting - public void setLastResortKey(PreKeyV1 lastResortKey) { - this.lastResortKey = lastResortKey; - } -} diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyV1.java b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyV1.java deleted file mode 100644 index deeb9d95e..000000000 --- a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyV1.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (C) 2014 Open Whisper Systems - * - * 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.entities; - - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.annotations.VisibleForTesting; - -import javax.validation.constraints.NotNull; - -@JsonInclude(JsonInclude.Include.NON_DEFAULT) -public class PreKeyV1 implements PreKeyBase { - - @JsonProperty - private long deviceId; - - @JsonProperty - @NotNull - private long keyId; - - @JsonProperty - @NotNull - private String publicKey; - - @JsonProperty - @NotNull - private String identityKey; - - @JsonProperty - private int registrationId; - - public PreKeyV1() {} - - public PreKeyV1(long deviceId, long keyId, String publicKey, String identityKey, int registrationId) - { - this.deviceId = deviceId; - this.keyId = keyId; - this.publicKey = publicKey; - this.identityKey = identityKey; - this.registrationId = registrationId; - } - - @VisibleForTesting - public PreKeyV1(long deviceId, long keyId, String publicKey, String identityKey) - { - this.deviceId = deviceId; - this.keyId = keyId; - this.publicKey = publicKey; - this.identityKey = identityKey; - } - - @Override - public String getPublicKey() { - return publicKey; - } - - @Override - public long getKeyId() { - return keyId; - } - - public String getIdentityKey() { - return identityKey; - } - - public void setDeviceId(long deviceId) { - this.deviceId = deviceId; - } - - public long getDeviceId() { - return deviceId; - } - - public int getRegistrationId() { - return registrationId; - } - - public void setRegistrationId(int registrationId) { - this.registrationId = registrationId; - } -} diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/SignedPreKey.java b/src/main/java/org/whispersystems/textsecuregcm/entities/SignedPreKey.java index 7d04e49d2..0dafbe99e 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/entities/SignedPreKey.java +++ b/src/main/java/org/whispersystems/textsecuregcm/entities/SignedPreKey.java @@ -3,9 +3,7 @@ package org.whispersystems.textsecuregcm.entities; import com.fasterxml.jackson.annotation.JsonProperty; import org.hibernate.validator.constraints.NotEmpty; -import java.io.Serializable; - -public class SignedPreKey extends PreKeyV2 { +public class SignedPreKey extends PreKey { @JsonProperty @NotEmpty diff --git a/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedClient.java b/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedClient.java index b7bfeae59..6a2493ef4 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedClient.java +++ b/src/main/java/org/whispersystems/textsecuregcm/federation/FederatedClient.java @@ -34,8 +34,7 @@ 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.PreKeyResponseV1; -import org.whispersystems.textsecuregcm.entities.PreKeyResponseV2; +import org.whispersystems.textsecuregcm.entities.PreKeyResponse; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; @@ -106,28 +105,13 @@ public class FederatedClient { } } - public Optional getKeysV1(String destination, String device) { + public Optional getKeysV2(String destination, String device) { try { - PreKeyResponseV1 response = client.target(peer.getUrl()) - .path(String.format(PREKEY_PATH_DEVICE_V1, destination, device)) - .request() - .accept(MediaType.APPLICATION_JSON_TYPE) - .get(PreKeyResponseV1.class); - - return Optional.of(response); - } catch (ProcessingException e) { - logger.warn("PreKey", e); - return Optional.absent(); - } - } - - public Optional getKeysV2(String destination, String device) { - try { - PreKeyResponseV2 response = client.target(peer.getUrl()) - .path(String.format(PREKEY_PATH_DEVICE_V2, destination, device)) - .request() - .accept(MediaType.APPLICATION_JSON_TYPE) - .get(PreKeyResponseV2.class); + 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) { diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/Keys.java b/src/main/java/org/whispersystems/textsecuregcm/storage/Keys.java index 50a980f26..36a55cc2a 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/storage/Keys.java +++ b/src/main/java/org/whispersystems/textsecuregcm/storage/Keys.java @@ -30,7 +30,7 @@ import org.skife.jdbi.v2.sqlobject.SqlUpdate; import org.skife.jdbi.v2.sqlobject.Transaction; import org.skife.jdbi.v2.sqlobject.customizers.Mapper; import org.skife.jdbi.v2.tweak.ResultSetMapper; -import org.whispersystems.textsecuregcm.entities.PreKeyBase; +import org.whispersystems.textsecuregcm.entities.PreKey; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; @@ -66,16 +66,13 @@ public abstract class Keys { public abstract int getCount(@Bind("number") String number, @Bind("device_id") long deviceId); @Transaction(TransactionIsolationLevel.SERIALIZABLE) - public void store(String number, long deviceId, List keys, PreKeyBase lastResortKey) { + public void store(String number, long deviceId, List keys) { List records = new LinkedList<>(); - for (PreKeyBase key : keys) { + for (PreKey key : keys) { records.add(new KeyRecord(0, number, deviceId, key.getKeyId(), key.getPublicKey(), false)); } - records.add(new KeyRecord(0, number, deviceId, lastResortKey.getKeyId(), - lastResortKey.getPublicKey(), true)); - removeKeys(number, deviceId); append(records); } diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java index b086e9c65..6f9bdca64 100644 --- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java +++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java @@ -10,12 +10,12 @@ 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.KeysControllerV2; +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.PreKeyResponseItemV2; -import org.whispersystems.textsecuregcm.entities.PreKeyResponseV2; +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; @@ -58,12 +58,12 @@ public class FederatedControllerTest { private RateLimiter rateLimiter = mock(RateLimiter.class ); private final SignedPreKey signedPreKey = new SignedPreKey(3333, "foo", "baar"); - private final PreKeyResponseV2 preKeyResponseV2 = new PreKeyResponseV2("foo", new LinkedList()); + 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); - private final KeysControllerV2 keysControllerV2 = mock(KeysControllerV2.class); + private final KeysController keysControllerV2 = mock(KeysController.class); @Rule public final ResourceTestRule resources = ResourceTestRule.builder() @@ -117,12 +117,12 @@ public class FederatedControllerTest { @Test public void testSignedPreKeyV2() throws Exception { - PreKeyResponseV2 response = + PreKeyResponse response = resources.getJerseyTest() .target("/v2/federation/key/+14152223333/1") .request() .header("Authorization", AuthHelper.getAuthHeader("cyanogen", "foofoo")) - .get(PreKeyResponseV2.class); + .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 e53d1c1dc..2c585f44f 100644 --- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java +++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java @@ -7,11 +7,11 @@ import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider; -import org.whispersystems.textsecuregcm.controllers.KeysControllerV2; +import org.whispersystems.textsecuregcm.controllers.KeysController; +import org.whispersystems.textsecuregcm.entities.PreKey; import org.whispersystems.textsecuregcm.entities.PreKeyCount; -import org.whispersystems.textsecuregcm.entities.PreKeyResponseV2; -import org.whispersystems.textsecuregcm.entities.PreKeyStateV2; -import org.whispersystems.textsecuregcm.entities.PreKeyV2; +import org.whispersystems.textsecuregcm.entities.PreKeyResponse; +import org.whispersystems.textsecuregcm.entities.PreKeyState; import org.whispersystems.textsecuregcm.entities.SignedPreKey; import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiters; @@ -65,7 +65,7 @@ public class KeyControllerTest { .addProvider(AuthHelper.getAuthFilter()) .addProvider(new AuthValueFactoryProvider.Binder()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory()) - .addResource(new KeysControllerV2(rateLimiters, keys, accounts, null)) + .addResource(new KeysController(rateLimiters, keys, accounts, null)) .build(); @Before @@ -175,11 +175,11 @@ public class KeyControllerTest { @Test public void validSingleRequestTestV2() throws Exception { - PreKeyResponseV2 result = resources.getJerseyTest() - .target(String.format("/v2/keys/%s/1", EXISTS_NUMBER)) - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) - .get(PreKeyResponseV2.class); + PreKeyResponse result = resources.getJerseyTest() + .target(String.format("/v2/keys/%s/1", EXISTS_NUMBER)) + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) + .get(PreKeyResponse.class); assertThat(result.getIdentityKey()).isEqualTo(existsAccount.getIdentityKey()); assertThat(result.getDevicesCount()).isEqualTo(1); @@ -193,17 +193,17 @@ public class KeyControllerTest { @Test public void validMultiRequestTestV2() throws Exception { - PreKeyResponseV2 results = resources.getJerseyTest() - .target(String.format("/v2/keys/%s/*", EXISTS_NUMBER)) - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) - .get(PreKeyResponseV2.class); + PreKeyResponse results = resources.getJerseyTest() + .target(String.format("/v2/keys/%s/*", EXISTS_NUMBER)) + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) + .get(PreKeyResponse.class); assertThat(results.getDevicesCount()).isEqualTo(3); assertThat(results.getIdentityKey()).isEqualTo(existsAccount.getIdentityKey()); - PreKeyV2 signedPreKey = results.getDevice(1).getSignedPreKey(); - PreKeyV2 preKey = results.getDevice(1).getPreKey(); + PreKey signedPreKey = results.getDevice(1).getSignedPreKey(); + PreKey preKey = results.getDevice(1).getPreKey(); long registrationId = results.getDevice(1).getRegistrationId(); long deviceId = results.getDevice(1).getDeviceId(); @@ -285,16 +285,16 @@ public class KeyControllerTest { @Test public void putKeysTestV2() throws Exception { - final PreKeyV2 preKey = new PreKeyV2(31337, "foobar"); - final PreKeyV2 lastResortKey = new PreKeyV2(31339, "barbar"); + final PreKey preKey = new PreKey(31337, "foobar"); + final PreKey lastResortKey = new PreKey(31339, "barbar"); final SignedPreKey signedPreKey = new SignedPreKey(31338, "foobaz", "myvalidsig"); final String identityKey = "barbar"; - List preKeys = new LinkedList() {{ + List preKeys = new LinkedList() {{ add(preKey); }}; - PreKeyStateV2 preKeyState = new PreKeyStateV2(identityKey, signedPreKey, preKeys, lastResortKey); + PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, preKeys); Response response = resources.getJerseyTest() @@ -306,9 +306,9 @@ public class KeyControllerTest { assertThat(response.getStatus()).isEqualTo(204); ArgumentCaptor listCaptor = ArgumentCaptor.forClass(List.class); - verify(keys).store(eq(AuthHelper.VALID_NUMBER), eq(1L), listCaptor.capture(), eq(lastResortKey)); + verify(keys).store(eq(AuthHelper.VALID_NUMBER), eq(1L), listCaptor.capture()); - List capturedList = listCaptor.getValue(); + List capturedList = listCaptor.getValue(); assertThat(capturedList.size() == 1); assertThat(capturedList.get(0).getKeyId() == 31337); assertThat(capturedList.get(0).getPublicKey().equals("foobar")); diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/entities/PreKeyTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/entities/PreKeyTest.java index 7b1056d20..49670167e 100644 --- a/src/test/java/org/whispersystems/textsecuregcm/tests/entities/PreKeyTest.java +++ b/src/test/java/org/whispersystems/textsecuregcm/tests/entities/PreKeyTest.java @@ -2,8 +2,7 @@ package org.whispersystems.textsecuregcm.tests.entities; import org.junit.Test; import org.whispersystems.textsecuregcm.entities.ClientContact; -import org.whispersystems.textsecuregcm.entities.PreKeyV1; -import org.whispersystems.textsecuregcm.entities.PreKeyV2; +import org.whispersystems.textsecuregcm.entities.PreKey; import org.whispersystems.textsecuregcm.util.Util; import static org.hamcrest.CoreMatchers.equalTo; @@ -13,16 +12,6 @@ import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.*; public class PreKeyTest { - @Test - public void serializeToJSONV1() throws Exception { - PreKeyV1 preKey = new PreKeyV1(1, 1234, "test", "identityTest"); - preKey.setRegistrationId(987); - - assertThat("Basic Contact Serialization works", - asJson(preKey), - is(equalTo(jsonFixture("fixtures/prekey.json")))); - } - @Test public void deserializeFromJSONV() throws Exception { ClientContact contact = new ClientContact(Util.getContactToken("+14152222222"), @@ -35,7 +24,7 @@ public class PreKeyTest { @Test public void serializeToJSONV2() throws Exception { - PreKeyV2 preKey = new PreKeyV2(1234, "test"); + PreKey preKey = new PreKey(1234, "test"); assertThat("PreKeyV2 Serialization works", asJson(preKey),