Remove all the old V1 keys stuff

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2017-05-05 10:36:29 -07:00
parent 3200ba0ed0
commit 35180b41bc
18 changed files with 219 additions and 539 deletions

View File

@ -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<WhisperServerConfiguration
environment.lifecycle().manage(pushSender);
AttachmentController attachmentController = new AttachmentController(rateLimiters, federatedClientManager, urlSigner);
KeysControllerV2 keysControllerV2 = new KeysControllerV2(rateLimiters, keys, accountsManager, federatedClientManager);
KeysController keysController = new KeysController(rateLimiters, keys, accountsManager, federatedClientManager);
MessageController messageController = new MessageController(rateLimiters, pushSender, receiptSender, accountsManager, messagesManager, federatedClientManager);
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Account>()
@ -211,11 +211,11 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
environment.jersey().register(new DeviceController(pendingDevicesManager, accountsManager, messagesManager, rateLimiters, config.getMaxDevices()));
environment.jersey().register(new DirectoryController(rateLimiters, directory));
environment.jersey().register(new FederationControllerV1(accountsManager, attachmentController, messageController));
environment.jersey().register(new FederationControllerV2(accountsManager, attachmentController, messageController, keysControllerV2));
environment.jersey().register(new FederationControllerV2(accountsManager, attachmentController, messageController, keysController));
environment.jersey().register(new ReceiptController(receiptSender));
environment.jersey().register(new ProvisioningController(rateLimiters, pushSender));
environment.jersey().register(attachmentController);
environment.jersey().register(keysControllerV2);
environment.jersey().register(keysController);
environment.jersey().register(messageController);
///

View File

@ -4,7 +4,7 @@ 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.PreKeyResponseV2;
import org.whispersystems.textsecuregcm.entities.PreKeyResponse;
import org.whispersystems.textsecuregcm.federation.FederatedPeer;
import org.whispersystems.textsecuregcm.federation.NonLimitedAccount;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
@ -23,25 +23,25 @@ public class FederationControllerV2 extends FederationController {
private final Logger logger = LoggerFactory.getLogger(FederationControllerV2.class);
private final KeysControllerV2 keysControllerV2;
private final KeysController keysController;
public FederationControllerV2(AccountsManager accounts, AttachmentController attachmentController, MessageController messageController, KeysControllerV2 keysControllerV2) {
public FederationControllerV2(AccountsManager accounts, AttachmentController attachmentController, MessageController messageController, KeysController keysController) {
super(accounts, attachmentController, messageController);
this.keysControllerV2 = keysControllerV2;
this.keysController = keysController;
}
@Timed
@GET
@Path("/key/{number}/{device}")
@Produces(MediaType.APPLICATION_JSON)
public Optional<PreKeyResponseV2> getKeysV2(@Auth FederatedPeer peer,
@PathParam("number") String number,
@PathParam("device") String device)
public Optional<PreKeyResponse> 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.<String>absent());
return keysController.getDeviceKeys(new NonLimitedAccount("Unknown", -1, peer.getName()),
number, device, Optional.<String>absent());
} catch (RateLimitExceededException e) {
logger.warn("Rate limiting on federated channel", e);
throw new IOException(e);

View File

@ -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<List<KeyRecord>> 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<PreKeyResponse> getDeviceKeys(@Auth Account account,
@PathParam("number") String number,
@PathParam("device_id") String deviceId,
@QueryParam("relay") Optional<String> 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<List<KeyRecord>> targetKeys = getLocalKeys(target, deviceId);
List<PreKeyResponseItem> 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<SignedPreKey> 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<List<KeyRecord>> 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> 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<Device> 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());
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<PreKeyResponseV2> getDeviceKeys(@Auth Account account,
@PathParam("number") String number,
@PathParam("device_id") String deviceId,
@QueryParam("relay") Optional<String> 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<List<KeyRecord>> targetKeys = getLocalKeys(target, deviceId);
List<PreKeyResponseItemV2> 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<SignedPreKey> 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> 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<Device> 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());
}
}
}

View File

@ -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;

View File

@ -1,8 +0,0 @@
package org.whispersystems.textsecuregcm.entities;
public interface PreKeyBase {
public long getKeyId();
public String getPublicKey();
}

View File

@ -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<PreKeyResponseItemV2> devices;
private List<PreKeyResponseItem> devices;
public PreKeyResponseV2() {}
public PreKeyResponse() {}
public PreKeyResponseV2(String identityKey, List<PreKeyResponseItemV2> devices) {
public PreKeyResponse(String identityKey, List<PreKeyResponseItem> 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;
}

View File

@ -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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<PreKeyV1> keys;
@VisibleForTesting
public PreKeyResponseV1() {}
public PreKeyResponseV1(PreKeyV1 preKey) {
this.keys = new LinkedList<>();
this.keys.add(preKey);
}
public PreKeyResponseV1(List<PreKeyV1> preKeys) {
this.keys = preKeys;
}
public List<PreKeyV1> getKeys() {
return keys;
}
@VisibleForTesting
public boolean equals(Object o) {
if (!(o instanceof PreKeyResponseV1) ||
((PreKeyResponseV1) o).keys.size() != keys.size())
return false;
Iterator<PreKeyV1> 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;
}
}

View File

@ -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<PreKeyV2> preKeys;
private List<PreKey> 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<PreKeyV2> keys, PreKeyV2 lastResortKey)
{
public PreKeyState(String identityKey, SignedPreKey signedPreKey, List<PreKey> keys) {
this.identityKey = identityKey;
this.signedPreKey = signedPreKey;
this.preKeys = keys;
this.lastResortKey = lastResortKey;
}
public List<PreKeyV2> getPreKeys() {
public List<PreKey> getPreKeys() {
return preKeys;
}
@ -70,7 +62,4 @@ public class PreKeyStateV2 {
return identityKey;
}
public PreKeyV2 getLastResortKey() {
return lastResortKey;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<PreKeyV1> keys;
public List<PreKeyV1> getKeys() {
return keys;
}
@VisibleForTesting
public void setKeys(List<PreKeyV1> keys) {
this.keys = keys;
}
public PreKeyV1 getLastResortKey() {
return lastResortKey;
}
@VisibleForTesting
public void setLastResortKey(PreKeyV1 lastResortKey) {
this.lastResortKey = lastResortKey;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -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

View File

@ -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<PreKeyResponseV1> getKeysV1(String destination, String device) {
public Optional<PreKeyResponse> 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<PreKeyResponseV2> 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) {

View File

@ -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<? extends PreKeyBase> keys, PreKeyBase lastResortKey) {
public void store(String number, long deviceId, List<PreKey> keys) {
List<KeyRecord> 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);
}

View File

@ -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<PreKeyResponseItemV2>());
private final PreKeyResponse preKeyResponseV2 = new PreKeyResponse("foo", new LinkedList<PreKeyResponseItem>());
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()));
}

View File

@ -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<PreKeyV2> preKeys = new LinkedList<PreKeyV2>() {{
List<PreKey> preKeys = new LinkedList<PreKey>() {{
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<List> 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<PreKeyV2> capturedList = listCaptor.getValue();
List<PreKey> capturedList = listCaptor.getValue();
assertThat(capturedList.size() == 1);
assertThat(capturedList.get(0).getKeyId() == 31337);
assertThat(capturedList.get(0).getPublicKey().equals("foobar"));

View File

@ -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),