Improve handling of badly-serialized data.
h/t @i-infra
This commit is contained in:
parent
eb8de536e0
commit
ed8538547f
3 changed files with 55 additions and 41 deletions
|
@ -12,6 +12,7 @@ import androidx.lifecycle.LiveData;
|
|||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.mobilecoin.lib.exceptions.SerializationException;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
|
@ -30,6 +31,7 @@ import org.thoughtcrime.securesms.util.Base64;
|
|||
import org.thoughtcrime.securesms.util.CursorUtil;
|
||||
import org.thoughtcrime.securesms.util.SqlUtil;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||
import org.whispersystems.signalservice.api.InvalidMessageStructureException;
|
||||
import org.whispersystems.signalservice.api.payments.Money;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -105,7 +107,7 @@ public final class PaymentDatabase extends Database {
|
|||
@NonNull Money amount,
|
||||
@NonNull Money fee,
|
||||
@NonNull byte[] receipt)
|
||||
throws PublicKeyConflictException
|
||||
throws PublicKeyConflictException, SerializationException
|
||||
{
|
||||
create(uuid, fromRecipient, null, timestamp, 0, note, Direction.RECEIVED, State.SUBMITTED, amount, fee, null, receipt, null, false);
|
||||
}
|
||||
|
@ -122,7 +124,9 @@ public final class PaymentDatabase extends Database {
|
|||
create(uuid, toRecipient, publicAddress, timestamp, 0, note, Direction.SENT, State.INITIAL, amount, amount.toZero(), null, null, null, true);
|
||||
} catch (PublicKeyConflictException e) {
|
||||
Log.w(TAG, "Tried to create payment but the public key appears already in the database", e);
|
||||
throw new AssertionError(e);
|
||||
throw new IllegalArgumentException(e);
|
||||
} catch (SerializationException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,6 +146,7 @@ public final class PaymentDatabase extends Database {
|
|||
@NonNull Money fee,
|
||||
@NonNull byte[] receipt,
|
||||
@NonNull PaymentMetaData metaData)
|
||||
throws SerializationException
|
||||
{
|
||||
try {
|
||||
create(uuid, toRecipient, publicAddress, timestamp, blockIndex, note, Direction.SENT, State.SUCCESSFUL, amount, fee, null, receipt, metaData, true);
|
||||
|
@ -165,6 +170,8 @@ public final class PaymentDatabase extends Database {
|
|||
} catch (PublicKeyConflictException e) {
|
||||
Log.w(TAG, "Tried to create payment but the public key appears already in the database", e);
|
||||
throw new AssertionError(e);
|
||||
} catch (SerializationException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,7 +190,7 @@ public final class PaymentDatabase extends Database {
|
|||
@Nullable byte[] receipt,
|
||||
@Nullable PaymentMetaData metaData,
|
||||
boolean seen)
|
||||
throws PublicKeyConflictException
|
||||
throws PublicKeyConflictException, SerializationException
|
||||
{
|
||||
if (recipientId == null && publicAddress == null) {
|
||||
throw new AssertionError();
|
||||
|
@ -403,8 +410,12 @@ public final class PaymentDatabase extends Database {
|
|||
values.put(STATE, State.SUBMITTED.serialize());
|
||||
values.put(TRANSACTION, transaction);
|
||||
values.put(RECEIPT, receipt);
|
||||
values.put(PUBLIC_KEY, Base64.encodeBytes(PaymentMetaDataUtil.receiptPublic(PaymentMetaDataUtil.fromReceipt(receipt))));
|
||||
values.put(META_DATA, PaymentMetaDataUtil.fromReceiptAndTransaction(receipt, transaction).toByteArray());
|
||||
try {
|
||||
values.put(PUBLIC_KEY, Base64.encodeBytes(PaymentMetaDataUtil.receiptPublic(PaymentMetaDataUtil.fromReceipt(receipt))));
|
||||
values.put(META_DATA, PaymentMetaDataUtil.fromReceiptAndTransaction(receipt, transaction).toByteArray());
|
||||
} catch (SerializationException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
values.put(FEE, CryptoValueUtil.moneyToCryptoValue(fee).toByteArray());
|
||||
|
||||
database.beginTransaction();
|
||||
|
|
|
@ -23,11 +23,11 @@ public final class PaymentMetaDataUtil {
|
|||
try {
|
||||
return PaymentMetaData.parseFrom(requireBlob);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new AssertionError(e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static @NonNull PaymentMetaData fromReceipt(@Nullable byte[] receipt) {
|
||||
public static @NonNull PaymentMetaData fromReceipt(@Nullable byte[] receipt) throws SerializationException {
|
||||
PaymentMetaData.MobileCoinTxoIdentification.Builder builder = PaymentMetaData.MobileCoinTxoIdentification.newBuilder();
|
||||
|
||||
if (receipt != null) {
|
||||
|
@ -46,7 +46,7 @@ public final class PaymentMetaDataUtil {
|
|||
return PaymentMetaData.newBuilder().setMobileCoinTxoIdentification(builder).build();
|
||||
}
|
||||
|
||||
public static @NonNull PaymentMetaData fromReceiptAndTransaction(@Nullable byte[] receipt, @Nullable byte[] transaction) {
|
||||
public static @NonNull PaymentMetaData fromReceiptAndTransaction(@Nullable byte[] receipt, @Nullable byte[] transaction) throws SerializationException {
|
||||
PaymentMetaData.MobileCoinTxoIdentification.Builder builder = PaymentMetaData.MobileCoinTxoIdentification.newBuilder();
|
||||
|
||||
if (transaction != null) {
|
||||
|
@ -58,27 +58,19 @@ public final class PaymentMetaDataUtil {
|
|||
return PaymentMetaData.newBuilder().setMobileCoinTxoIdentification(builder).build();
|
||||
}
|
||||
|
||||
private static void addReceiptData(@NonNull byte[] receipt, PaymentMetaData.MobileCoinTxoIdentification.Builder builder) {
|
||||
try {
|
||||
RistrettoPublic publicKey = Receipt.fromBytes(receipt).getPublicKey();
|
||||
addPublicKey(builder, publicKey);
|
||||
} catch (SerializationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
private static void addReceiptData(@NonNull byte[] receipt, PaymentMetaData.MobileCoinTxoIdentification.Builder builder) throws SerializationException {
|
||||
RistrettoPublic publicKey = Receipt.fromBytes(receipt).getPublicKey();
|
||||
addPublicKey(builder, publicKey);
|
||||
}
|
||||
|
||||
private static void addTransactionData(@NonNull byte[] transactionBytes, PaymentMetaData.MobileCoinTxoIdentification.Builder builder) {
|
||||
try {
|
||||
Transaction transaction = Transaction.fromBytes(transactionBytes);
|
||||
Set<KeyImage> keyImages = transaction.getKeyImages();
|
||||
for (KeyImage keyImage : keyImages) {
|
||||
builder.addKeyImages(ByteString.copyFrom(keyImage.getData()));
|
||||
}
|
||||
for (RistrettoPublic publicKey : transaction.getOutputPublicKeys()) {
|
||||
addPublicKey(builder, publicKey);
|
||||
}
|
||||
} catch (SerializationException e) {
|
||||
throw new AssertionError(e);
|
||||
private static void addTransactionData(@NonNull byte[] transactionBytes, PaymentMetaData.MobileCoinTxoIdentification.Builder builder) throws SerializationException {
|
||||
Transaction transaction = Transaction.fromBytes(transactionBytes);
|
||||
Set<KeyImage> keyImages = transaction.getKeyImages();
|
||||
for (KeyImage keyImage : keyImages) {
|
||||
builder.addKeyImages(ByteString.copyFrom(keyImage.getData()));
|
||||
}
|
||||
for (RistrettoPublic publicKey : transaction.getOutputPublicKeys()) {
|
||||
addPublicKey(builder, publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import com.annimon.stream.Collectors;
|
||||
import com.annimon.stream.Stream;
|
||||
import com.mobilecoin.lib.exceptions.SerializationException;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.ringrtc.CallId;
|
||||
|
@ -309,7 +310,7 @@ public final class MessageContentProcessor {
|
|||
else if (syncMessage.getBlockedList().isPresent()) handleSynchronizeBlockedListMessage(syncMessage.getBlockedList().get());
|
||||
else if (syncMessage.getFetchType().isPresent()) handleSynchronizeFetchMessage(syncMessage.getFetchType().get());
|
||||
else if (syncMessage.getMessageRequestResponse().isPresent()) handleSynchronizeMessageRequestResponse(syncMessage.getMessageRequestResponse().get());
|
||||
else if (syncMessage.getOutgoingPaymentMessage().isPresent()) handleSynchronizeOutgoingPayment(syncMessage.getOutgoingPaymentMessage().get());
|
||||
else if (syncMessage.getOutgoingPaymentMessage().isPresent()) handleSynchronizeOutgoingPayment(content, syncMessage.getOutgoingPaymentMessage().get());
|
||||
else warn(String.valueOf(content.getTimestamp()), "Contains no known sync types...");
|
||||
} else if (content.getCallMessage().isPresent()) {
|
||||
log(String.valueOf(content.getTimestamp()), "Got call message...");
|
||||
|
@ -410,6 +411,8 @@ public final class MessageContentProcessor {
|
|||
} catch (PaymentDatabase.PublicKeyConflictException e) {
|
||||
warn(content.getTimestamp(), "Ignoring payment with public key already in database");
|
||||
return;
|
||||
} catch (SerializationException e) {
|
||||
warn(content.getTimestamp(), "Ignoring payment with bad data.", e);
|
||||
}
|
||||
|
||||
ApplicationDependencies.getJobManager()
|
||||
|
@ -1016,7 +1019,7 @@ public final class MessageContentProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleSynchronizeOutgoingPayment(@NonNull OutgoingPaymentMessage outgoingPaymentMessage) {
|
||||
private void handleSynchronizeOutgoingPayment(@NonNull SignalServiceContent content, @NonNull OutgoingPaymentMessage outgoingPaymentMessage) {
|
||||
RecipientId recipientId = outgoingPaymentMessage.getRecipient()
|
||||
.transform(uuid -> RecipientId.from(uuid, null))
|
||||
.orNull();
|
||||
|
@ -1027,23 +1030,27 @@ public final class MessageContentProcessor {
|
|||
|
||||
Optional<MobileCoinPublicAddress> address = outgoingPaymentMessage.getAddress().transform(MobileCoinPublicAddress::fromBytes);
|
||||
if (!address.isPresent() && recipientId == null) {
|
||||
log("Inserting defrag");
|
||||
log(content.getTimestamp(), "Inserting defrag");
|
||||
address = Optional.of(ApplicationDependencies.getPayments().getWallet().getMobileCoinPublicAddress());
|
||||
recipientId = Recipient.self().getId();
|
||||
}
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
DatabaseFactory.getPaymentDatabase(context)
|
||||
.createSuccessfulPayment(uuid,
|
||||
recipientId,
|
||||
address.get(),
|
||||
timestamp,
|
||||
outgoingPaymentMessage.getBlockIndex(),
|
||||
outgoingPaymentMessage.getNote().or(""),
|
||||
outgoingPaymentMessage.getAmount(),
|
||||
outgoingPaymentMessage.getFee(),
|
||||
outgoingPaymentMessage.getReceipt().toByteArray(),
|
||||
PaymentMetaDataUtil.fromKeysAndImages(outgoingPaymentMessage.getPublicKeys(), outgoingPaymentMessage.getKeyImages()));
|
||||
try {
|
||||
DatabaseFactory.getPaymentDatabase(context)
|
||||
.createSuccessfulPayment(uuid,
|
||||
recipientId,
|
||||
address.get(),
|
||||
timestamp,
|
||||
outgoingPaymentMessage.getBlockIndex(),
|
||||
outgoingPaymentMessage.getNote().or(""),
|
||||
outgoingPaymentMessage.getAmount(),
|
||||
outgoingPaymentMessage.getFee(),
|
||||
outgoingPaymentMessage.getReceipt().toByteArray(),
|
||||
PaymentMetaDataUtil.fromKeysAndImages(outgoingPaymentMessage.getPublicKeys(), outgoingPaymentMessage.getKeyImages()));
|
||||
} catch (SerializationException e) {
|
||||
warn(content.getTimestamp(), "Ignoring synchronized outgoing payment with bad data.", e);
|
||||
}
|
||||
|
||||
log("Inserted synchronized payment " + uuid);
|
||||
}
|
||||
|
@ -2254,6 +2261,10 @@ public final class MessageContentProcessor {
|
|||
warn(String.valueOf(timestamp), message);
|
||||
}
|
||||
|
||||
protected void warn(long timestamp, @NonNull String message, @Nullable Throwable t) {
|
||||
warn(String.valueOf(timestamp), message, t);
|
||||
}
|
||||
|
||||
protected void warn(@NonNull String message, @Nullable Throwable t) {
|
||||
warn("", message, t);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue