Fix incorrect type value being used for unknown storage records.

This commit is contained in:
Cody Henthorne
2023-08-31 15:57:23 -04:00
committed by Nicholas Tinsley
parent 1f2bfe8245
commit 6c4ebc9f58
6 changed files with 106 additions and 84 deletions
@@ -1,5 +1,7 @@
package org.whispersystems.signalservice.api.storage;
import org.whispersystems.signalservice.api.messages.multidevice.MessageRequestResponseMessage;
import org.whispersystems.signalservice.api.util.ProtoUtil;
import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord;
import org.whispersystems.signalservice.internal.storage.protos.StorageManifest;
@@ -87,10 +89,16 @@ public class SignalStorageManifest {
List<ManifestRecord.Identifier> ids = new ArrayList<>(storageIds.size());
for (StorageId id : storageIds) {
ids.add(new ManifestRecord.Identifier.Builder()
.type(ManifestRecord.Identifier.Type.Companion.fromValue(id.getType()))
.raw(ByteString.of(id.getRaw()))
.build());
ManifestRecord.Identifier.Type type = ManifestRecord.Identifier.Type.Companion.fromValue(id.getType());
if (type != null) {
ids.add(new ManifestRecord.Identifier.Builder()
.type(type)
.raw(ByteString.of(id.getRaw()))
.build());
} else {
ByteString unknownEnum = ProtoUtil.writeUnknownEnumValue(StorageRecordProtoUtil.STORAGE_ID_TYPE_TAG, id.getType());
ids.add(new ManifestRecord.Identifier(ByteString.of(id.getRaw()), ManifestRecord.Identifier.Type.UNKNOWN, unknownEnum));
}
}
ManifestRecord manifestRecord = new ManifestRecord.Builder()
@@ -3,6 +3,7 @@ package org.whispersystems.signalservice.api.storage;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.logging.Log;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.whispersystems.signalservice.api.util.ProtoUtil;
import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord;
import org.whispersystems.signalservice.internal.storage.protos.StorageItem;
import org.whispersystems.signalservice.internal.storage.protos.StorageManifest;
@@ -24,7 +25,10 @@ public final class SignalStorageModels {
List<StorageId> ids = new ArrayList<>(manifestRecord.identifiers.size());
for (ManifestRecord.Identifier id : manifestRecord.identifiers) {
ids.add(StorageId.forType(id.raw.toByteArray(), id.type.getValue()));
int typeValue = (id.type != ManifestRecord.Identifier.Type.UNKNOWN) ? id.type.getValue()
: ProtoUtil.getUnknownEnumValue(id, StorageRecordProtoUtil.STORAGE_ID_TYPE_TAG);
ids.add(StorageId.forType(id.raw.toByteArray(), typeValue));
}
return new SignalStorageManifest(manifestRecord.version, manifestRecord.sourceDevice, ids);
@@ -58,23 +58,13 @@ public class StorageId {
public static boolean isKnownType(int val) {
for (ManifestRecord.Identifier.Type type : ManifestRecord.Identifier.Type.values()) {
if (type.getValue() == val) {
if (type != ManifestRecord.Identifier.Type.UNKNOWN && type.getValue() == val) {
return true;
}
}
return false;
}
public static int largestKnownType() {
int max = 0;
for (ManifestRecord.Identifier.Type type : ManifestRecord.Identifier.Type.values()) {
max = Math.max(type.getValue(), max);
}
return max;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -13,6 +13,10 @@ import org.whispersystems.signalservice.internal.storage.protos.AccountRecord
* Provide helpers for various Storage Service protos.
*/
object StorageRecordProtoUtil {
/** Must match tag value specified for ManifestRecord.Identifier#type in StorageService.proto */
const val STORAGE_ID_TYPE_TAG = 2
@JvmStatic
val defaultAccountRecord by lazy { AccountRecord() }
}
@@ -1,68 +0,0 @@
package org.whispersystems.signalservice.api.util;
import com.squareup.wire.Message;
import org.signal.libsignal.protocol.logging.Log;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.List;
import okio.ByteString;
public final class ProtoUtil {
private static final String TAG = ProtoUtil.class.getSimpleName();
private static final String DEFAULT_INSTANCE = "DEFAULT_INSTANCE";
private ProtoUtil() {}
/**
* True if there are unknown fields anywhere inside the proto or its nested protos.
*/
@SuppressWarnings("rawtypes")
public static boolean hasUnknownFields(Message rootProto) {
List<Message> allProtos = getInnerProtos(rootProto);
allProtos.add(rootProto);
for (Message proto : allProtos) {
ByteString unknownFields = proto.unknownFields();
if (unknownFields.size() > 0) {
return true;
}
}
return false;
}
/**
* Recursively retrieves all inner complex proto types inside a given proto.
*/
@SuppressWarnings("rawtypes")
private static List<Message> getInnerProtos(Message proto) {
List<Message> innerProtos = new LinkedList<>();
try {
Field[] fields = proto.getClass().getDeclaredFields();
for (Field field : fields) {
if (!field.getName().equals(DEFAULT_INSTANCE) && Message.class.isAssignableFrom(field.getType())) {
field.setAccessible(true);
Message inner = (Message) field.get(proto);
if (inner != null) {
innerProtos.add(inner);
innerProtos.addAll(getInnerProtos(inner));
}
}
}
} catch (IllegalAccessException e) {
Log.w(TAG, "Failed to get inner protos!", e);
}
return innerProtos;
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
@file:JvmName("ProtoUtil")
package org.whispersystems.signalservice.api.util
import com.squareup.wire.FieldEncoding
import com.squareup.wire.Message
import com.squareup.wire.ProtoAdapter
import com.squareup.wire.ProtoReader
import com.squareup.wire.ProtoWriter
import okio.Buffer
import okio.ByteString
import org.signal.libsignal.protocol.logging.Log
import java.util.LinkedList
object ProtoUtil {
private val TAG = ProtoUtil::class.java.simpleName
/**
* True if there are unknown fields anywhere inside the proto or its nested protos.
*/
@JvmStatic
fun hasUnknownFields(rootProto: Message<*, *>): Boolean {
val allProtos = getInnerProtos(rootProto)
allProtos.add(rootProto)
for (proto in allProtos) {
val unknownFields = proto.unknownFields
if (unknownFields.size > 0) {
return true
}
}
return false
}
@JvmStatic
fun getUnknownEnumValue(proto: Message<*, *>, tag: Int): Int {
val reader = ProtoReader(Buffer().write(proto.unknownFields))
reader.forEachTag { unknownTag ->
if (unknownTag == tag) {
return ProtoAdapter.INT32.decode(reader)
}
}
throw AssertionError("Tag $tag not found in unknown fields")
}
@JvmStatic
fun writeUnknownEnumValue(tag: Int, enumValue: Int): ByteString {
val buffer = Buffer()
val writer = ProtoWriter(buffer)
@Suppress("UNCHECKED_CAST")
(FieldEncoding.VARINT.rawProtoAdapter() as ProtoAdapter<Any>).encodeWithTag(writer, tag, enumValue.toLong())
return buffer.readByteString()
}
/**
* Recursively retrieves all inner complex proto types inside a given proto.
*/
private fun getInnerProtos(proto: Message<*, *>): MutableList<Message<*, *>> {
val innerProtos: MutableList<Message<*, *>> = LinkedList()
try {
val fields = proto.javaClass.declaredFields
for (field in fields) {
if (Message::class.java.isAssignableFrom(field.type)) {
field.isAccessible = true
val inner = field[proto] as? Message<*, *>
if (inner != null) {
innerProtos.add(inner)
innerProtos.addAll(getInnerProtos(inner))
}
}
}
} catch (e: IllegalAccessException) {
Log.w(TAG, "Failed to get inner protos!", e)
}
return innerProtos
}
}