diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/PaymentsAddressException.java b/app/src/main/java/org/thoughtcrime/securesms/payments/PaymentsAddressException.java index 8803d15faa..dc7941abde 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/PaymentsAddressException.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/PaymentsAddressException.java @@ -21,7 +21,8 @@ public final class PaymentsAddressException extends Exception { COULD_NOT_DECRYPT("Payment address could not be decrypted"), INVALID_ADDRESS("Invalid MobileCoin address on payments address proto"), INVALID_ADDRESS_SIGNATURE("Invalid MobileCoin address signature on payments address proto"), - NO_ADDRESS("No MobileCoin address on payments address proto"); + NO_ADDRESS("No MobileCoin address on payments address proto"), + IDENTITY_MISMATCH("Server-provided identity key does not match locally-stored identity key"); private final String message; diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/confirm/ConfirmPaymentViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/payments/confirm/ConfirmPaymentViewModel.java index 1af8e20e5f..4ae8b8d00f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/confirm/ConfirmPaymentViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/confirm/ConfirmPaymentViewModel.java @@ -147,6 +147,7 @@ final class ConfirmPaymentViewModel extends ViewModel { case INVALID_ADDRESS: case INVALID_ADDRESS_SIGNATURE: case NO_ADDRESS: + case IDENTITY_MISMATCH: return ErrorType.NO_ADDRESS; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java index 0d45984394..dada994400 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java @@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; import org.thoughtcrime.securesms.crypto.SealedSenderAccessUtil; import org.thoughtcrime.securesms.database.RecipientTable; import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.database.model.IdentityStoreRecord; import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob; @@ -207,11 +208,18 @@ public final class ProfileUtil { } try { - IdentityKey identityKey = new IdentityKey(Base64.decode(profileAndCredential.getProfile().getIdentityKey()), 0); + IdentityKey remoteIdentityKey = new IdentityKey(Base64.decode(profileAndCredential.getProfile().getIdentityKey()), 0); + IdentityKey localIdentityKey = getLocalIdentityKey(recipient); + + if (localIdentityKey != null && !localIdentityKey.equals(remoteIdentityKey)) { + Log.w(TAG, "Server-provided identity key does not match locally-stored identity key for " + recipient.getId()); + throw new PaymentsAddressException(PaymentsAddressException.Code.IDENTITY_MISMATCH); + } + ProfileCipher profileCipher = new ProfileCipher(profileKey); byte[] decrypted = profileCipher.decryptWithLength(encryptedPaymentsAddress); PaymentAddress paymentAddress = PaymentAddress.ADAPTER.decode(decrypted); - byte[] bytes = MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(paymentAddress, identityKey); + byte[] bytes = MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(paymentAddress, localIdentityKey != null ? localIdentityKey : remoteIdentityKey); MobileCoinPublicAddress mobileCoinPublicAddress = MobileCoinPublicAddress.fromBytes(bytes); if (mobileCoinPublicAddress == null) { @@ -228,6 +236,15 @@ public final class ProfileUtil { } } + private static @Nullable IdentityKey getLocalIdentityKey(@NonNull Recipient recipient) { + if (!recipient.getHasServiceId()) { + return null; + } + + IdentityStoreRecord record = SignalDatabase.identities().getIdentityStoreRecord(recipient.requireServiceId()); + return record != null ? record.getIdentityKey() : null; + } + private static ProfileKey getProfileKey(@NonNull Recipient recipient) throws IOException { byte[] profileKeyBytes = recipient.getProfileKey();