Revert "Represent device names as byte arrays"

This reverts commit 5ae2e5281a.
This commit is contained in:
Jon Chambers
2023-12-06 17:05:04 -05:00
parent 4fa10e5783
commit 45848e7bfe
17 changed files with 46 additions and 177 deletions

View File

@@ -8,7 +8,6 @@ import static org.whispersystems.textsecuregcm.util.RegistrationIdValidator.vali
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import javax.annotation.Nullable;
@@ -31,10 +30,8 @@ public class AccountAttributes {
private int phoneNumberIdentityRegistrationId;
@JsonProperty
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@Size(max = 225)
private byte[] name;
@Size(max = 204, message = "This field must be less than 50 characters")
private String name;
@JsonProperty
private String registrationLock;
@@ -65,7 +62,7 @@ public class AccountAttributes {
final boolean fetchesMessages,
final int registrationId,
final int phoneNumberIdentifierRegistrationId,
final byte[] name,
final String name,
final String registrationLock,
final boolean discoverableByPhoneNumber,
final DeviceCapabilities capabilities) {
@@ -90,7 +87,7 @@ public class AccountAttributes {
return phoneNumberIdentityRegistrationId;
}
public byte[] getName() {
public String getName() {
return name;
}

View File

@@ -5,16 +5,5 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
public record DeviceInfo(long id,
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
byte[] name,
long lastSeen,
long created) {
public record DeviceInfo(long id, String name, long lastSeen, long created) {
}

View File

@@ -6,24 +6,19 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
public class DeviceName {
@JsonProperty
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@NotEmpty
@Size(max = 225)
private byte[] deviceName;
@Size(max = 300, message = "This field must be less than 300 characters")
private String deviceName;
public DeviceName() {}
public byte[] getDeviceName() {
public String getDeviceName() {
return deviceName;
}
}

View File

@@ -60,8 +60,8 @@ public class DevicesGrpcService extends ReactorDevicesGrpc.DevicesImplBase {
.reduce(GetDevicesResponse.newBuilder(), (builder, device) -> {
final GetDevicesResponse.LinkedDevice.Builder linkedDeviceBuilder = GetDevicesResponse.LinkedDevice.newBuilder();
if (device.getName() != null) {
linkedDeviceBuilder.setName(ByteString.copyFrom(device.getName()));
if (StringUtils.isNotBlank(device.getName())) {
linkedDeviceBuilder.setName(ByteString.copyFrom(Base64.getDecoder().decode(device.getName())));
}
return builder.addDevices(linkedDeviceBuilder
@@ -105,7 +105,7 @@ public class DevicesGrpcService extends ReactorDevicesGrpc.DevicesImplBase {
return Mono.fromFuture(() -> accountsManager.getByAccountIdentifierAsync(authenticatedDevice.accountIdentifier()))
.map(maybeAccount -> maybeAccount.orElseThrow(Status.UNAUTHENTICATED::asRuntimeException))
.flatMap(account -> Mono.fromFuture(() -> accountsManager.updateDeviceAsync(account, authenticatedDevice.deviceId(),
device -> device.setName(request.getName().toByteArray()))))
device -> device.setName(Base64.getEncoder().encodeToString(request.getName().toByteArray())))))
.thenReturn(SetDeviceNameResponse.newBuilder().build());
}

View File

@@ -7,7 +7,6 @@ package org.whispersystems.textsecuregcm.storage;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.List;
import java.util.OptionalInt;
import java.util.concurrent.TimeUnit;
@@ -18,7 +17,6 @@ import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
import org.whispersystems.textsecuregcm.identity.IdentityType;
import org.whispersystems.textsecuregcm.util.DeviceNameByteArrayAdapter;
public class Device {
@@ -33,9 +31,7 @@ public class Device {
private byte id;
@JsonProperty
@JsonSerialize(using = DeviceNameByteArrayAdapter.Serializer.class)
@JsonDeserialize(using = DeviceNameByteArrayAdapter.Deserializer.class)
private byte[] name;
private String name;
@JsonProperty
private String authToken;
@@ -150,11 +146,11 @@ public class Device {
this.id = id;
}
public byte[] getName() {
public String getName() {
return name;
}
public void setName(byte[] name) {
public void setName(String name) {
this.name = name;
}

View File

@@ -1,53 +0,0 @@
package org.whispersystems.textsecuregcm.util;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
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 io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Metrics;
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
import java.io.IOException;
import java.util.Base64;
/**
* Serializes a byte array as a standard Base64-encoded string and deserializes Base64-encoded strings to byte arrays,
* but treats any string that cannot be parsed as Base64 to {@code null}.
* <p/>
* Historically, device names were passed around as weakly-typed strings with the expectation that clients would provide
* Base64 strings, but nothing in the server ever verified that was the case. In the absence of strict validation, some
* third-party clients started submitting unencrypted names for devices, and so device names in persistent storage are a
* mix of Base64-encoded device name ciphertexts from first-party clients and plaintext device names from third-party
* clients. This adapter will discard the latter.
*/
public class DeviceNameByteArrayAdapter {
private static final Counter UNPARSEABLE_DEVICE_NAME_COUNTER =
Metrics.counter(MetricsUtil.name(DeviceNameByteArrayAdapter.class, "unparseableDeviceName"));
public static class Serializer extends JsonSerializer<byte[]> {
@Override
public void serialize(final byte[] bytes,
final JsonGenerator jsonGenerator,
final SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(Base64.getEncoder().encodeToString(bytes));
}
}
public static class Deserializer extends JsonDeserializer<byte[]> {
@Override
public byte[] deserialize(final JsonParser jsonParser,
final DeserializationContext deserializationContext) throws IOException {
try {
return Base64.getDecoder().decode(jsonParser.getValueAsString());
} catch (final IllegalArgumentException e) {
UNPARSEABLE_DEVICE_NAME_COUNTER.increment();
return null;
}
}
}
}