mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-24 19:56:00 +00:00
committed by
Greyson Parrelli
parent
56d53f0b6a
commit
a96c8867ae
@@ -1,110 +0,0 @@
|
||||
package org.thoughtcrime.securesms.groups.v2;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.signal.core.util.Base64;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.storageservice.protos.groups.GroupInviteLink;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okio.ByteString;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public final class GroupInviteLinkUrl_InvalidGroupLinkException_Test {
|
||||
|
||||
@Test
|
||||
public void empty_string() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException {
|
||||
assertNull(GroupInviteLinkUrl.fromUri(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void not_a_url_string() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException {
|
||||
assertNull(GroupInviteLinkUrl.fromUri("abc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrong_host() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException {
|
||||
assertNull(GroupInviteLinkUrl.fromUri("https://x.signal.org/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrong_scheme() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException {
|
||||
assertNull(GroupInviteLinkUrl.fromUri("http://signal.group/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void has_path() {
|
||||
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/not_expected/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"))
|
||||
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
|
||||
.hasMessage("No path was expected in uri");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missing_ref() {
|
||||
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/"))
|
||||
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
|
||||
.hasMessage("No reference was in the uri");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empty_ref() {
|
||||
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/#"))
|
||||
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
|
||||
.hasMessage("No reference was in the uri");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bad_base64() {
|
||||
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCX;mwVi31USVY"))
|
||||
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
|
||||
.hasCauseExactlyInstanceOf(IOException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bad_protobuf() {
|
||||
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCXmwVi31USVY"))
|
||||
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
|
||||
.hasCauseExactlyInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void version_999_url() {
|
||||
String url = "https://signal.group/#uj4zCiDMSxlNUvF4bQ3z3fYzGyZTFbJ1xEqWbPE3uZSD8bjOrxIP8NxV-0GUz3jpxMLR1rN3";
|
||||
|
||||
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri(url))
|
||||
.isInstanceOf(GroupInviteLinkUrl.UnknownGroupLinkVersionException.class)
|
||||
.hasMessage("Url contains no known group link content");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bad_master_key_length() {
|
||||
byte[] masterKeyBytes = Util.getSecretBytes(33);
|
||||
GroupLinkPassword password = GroupLinkPassword.createNew();
|
||||
|
||||
String encoding = createEncodedProtobuf(masterKeyBytes, password.serialize());
|
||||
|
||||
String url = "https://signal.group/#" + encoding;
|
||||
|
||||
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri(url))
|
||||
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
|
||||
.hasCauseExactlyInstanceOf(InvalidInputException.class);
|
||||
}
|
||||
|
||||
private static String createEncodedProtobuf(@NonNull byte[] groupMasterKey,
|
||||
@NonNull byte[] passwordBytes)
|
||||
{
|
||||
return Base64.encodeUrlSafeWithoutPadding(new GroupInviteLink.Builder()
|
||||
.v1Contents(new GroupInviteLink.GroupInviteLinkContentsV1.Builder()
|
||||
.groupMasterKey(ByteString.of(groupMasterKey))
|
||||
.inviteLinkPassword(ByteString.of(passwordBytes))
|
||||
.build())
|
||||
.build()
|
||||
.encode());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package org.thoughtcrime.securesms.groups.v2
|
||||
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.hasMessage
|
||||
import assertk.assertions.isInstanceOf
|
||||
import assertk.assertions.isNull
|
||||
import assertk.assertions.messageContains
|
||||
import assertk.assertions.rootCause
|
||||
import okio.ByteString
|
||||
import org.junit.Test
|
||||
import org.signal.core.util.Base64.encodeUrlSafeWithoutPadding
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException
|
||||
import org.signal.storageservice.protos.groups.GroupInviteLink
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl.InvalidGroupLinkException
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl.UnknownGroupLinkVersionException
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import java.io.IOException
|
||||
|
||||
@Suppress("ClassName")
|
||||
class GroupInviteLinkUrl_InvalidGroupLinkException_Test {
|
||||
@Test
|
||||
fun empty_string() {
|
||||
val uri = ""
|
||||
assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun not_a_url_string() {
|
||||
val uri = "abc"
|
||||
assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wrong_host() {
|
||||
val uri = "https://x.signal.org/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"
|
||||
assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wrong_scheme() {
|
||||
val uri = "http://signal.group/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"
|
||||
assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun has_path() {
|
||||
val uri = "https://signal.group/not_expected/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"
|
||||
assertFailure { GroupInviteLinkUrl.fromUri(uri) }
|
||||
.isInstanceOf<InvalidGroupLinkException>()
|
||||
.hasMessage("No path was expected in uri")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun missing_ref() {
|
||||
val uri = "https://signal.group/"
|
||||
assertFailure { GroupInviteLinkUrl.fromUri(uri) }
|
||||
.isInstanceOf<InvalidGroupLinkException>()
|
||||
.hasMessage("No reference was in the uri")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun empty_ref() {
|
||||
val uri = "https://signal.group/#"
|
||||
assertFailure { GroupInviteLinkUrl.fromUri(uri) }
|
||||
.isInstanceOf<InvalidGroupLinkException>()
|
||||
.hasMessage("No reference was in the uri")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bad_base64() {
|
||||
val uri = "https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCX;mwVi31USVY"
|
||||
assertFailure { GroupInviteLinkUrl.fromUri(uri) }
|
||||
.isInstanceOf<InvalidGroupLinkException>()
|
||||
.rootCause()
|
||||
.isInstanceOf<IOException>()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bad_protobuf() {
|
||||
val uri = "https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCXmwVi31USVY"
|
||||
assertFailure {
|
||||
GroupInviteLinkUrl.fromUri(uri)
|
||||
}.isInstanceOf<InvalidGroupLinkException>()
|
||||
.rootCause()
|
||||
.isInstanceOf<IllegalStateException>()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun version_999_url() {
|
||||
val url = "https://signal.group/#uj4zCiDMSxlNUvF4bQ3z3fYzGyZTFbJ1xEqWbPE3uZSD8bjOrxIP8NxV-0GUz3jpxMLR1rN3"
|
||||
assertFailure { GroupInviteLinkUrl.fromUri(url) }
|
||||
.isInstanceOf<UnknownGroupLinkVersionException>()
|
||||
.messageContains("Url contains no known group link content")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bad_master_key_length() {
|
||||
val masterKeyBytes = Util.getSecretBytes(33)
|
||||
val password = GroupLinkPassword.createNew()
|
||||
|
||||
val encoding = createEncodedProtobuf(masterKeyBytes, password.serialize())
|
||||
|
||||
val url = "https://signal.group/#$encoding"
|
||||
|
||||
assertFailure { GroupInviteLinkUrl.fromUri(url) }
|
||||
.isInstanceOf<InvalidGroupLinkException>()
|
||||
.rootCause()
|
||||
.isInstanceOf<InvalidInputException>()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun createEncodedProtobuf(
|
||||
groupMasterKey: ByteArray,
|
||||
passwordBytes: ByteArray
|
||||
): String {
|
||||
return encodeUrlSafeWithoutPadding(
|
||||
GroupInviteLink.Builder()
|
||||
.v1Contents(
|
||||
GroupInviteLink.GroupInviteLinkContentsV1.Builder()
|
||||
.groupMasterKey(ByteString.of(*groupMasterKey))
|
||||
.inviteLinkPassword(ByteString.of(*passwordBytes))
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
.encode()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package org.thoughtcrime.securesms.payments;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.signal.libsignal.protocol.IdentityKey;
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.signalservice.internal.push.PaymentAddress;
|
||||
|
||||
import okio.ByteString;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.whispersystems.signalservice.test.LibSignalLibraryUtil.assumeLibSignalSupportedOnOS;
|
||||
|
||||
public final class MobileCoinPublicAddressProfileUtilTest {
|
||||
|
||||
@Before
|
||||
public void ensureNativeSupported() {
|
||||
assumeLibSignalSupportedOnOS();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_verify_an_address() throws PaymentsAddressException {
|
||||
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
|
||||
byte[] address = Util.getSecretBytes(100);
|
||||
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair);
|
||||
|
||||
byte[] paymentsAddress = MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, identityKeyPair.getPublicKey());
|
||||
|
||||
assertArrayEquals(address, paymentsAddress);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_not_verify_an_address_with_the_wrong_key() {
|
||||
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
|
||||
IdentityKey wrongPublicKey = IdentityKeyUtil.generateIdentityKeyPair().getPublicKey();
|
||||
byte[] address = Util.getSecretBytes(100);
|
||||
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair);
|
||||
|
||||
assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, wrongPublicKey))
|
||||
.isInstanceOf(PaymentsAddressException.class)
|
||||
.hasMessage("Invalid MobileCoin address signature on payments address proto");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_not_verify_a_tampered_signature() {
|
||||
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
|
||||
byte[] address = Util.getSecretBytes(100);
|
||||
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair);
|
||||
|
||||
byte[] signature = signedPaymentAddress.mobileCoinAddress.signature.toByteArray();
|
||||
signature[0] = (byte) (signature[0] ^ 0x01);
|
||||
PaymentAddress tamperedSignature = signedPaymentAddress.newBuilder()
|
||||
.mobileCoinAddress(signedPaymentAddress.mobileCoinAddress
|
||||
.newBuilder()
|
||||
.signature(ByteString.of(signature))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedSignature, identityKeyPair.getPublicKey()))
|
||||
.isInstanceOf(PaymentsAddressException.class)
|
||||
.hasMessage("Invalid MobileCoin address signature on payments address proto");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_not_verify_a_tampered_address() {
|
||||
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
|
||||
byte[] addressBytes = Util.getSecretBytes(100);
|
||||
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(addressBytes, identityKeyPair);
|
||||
|
||||
byte[] address = signedPaymentAddress.mobileCoinAddress.address.toByteArray();
|
||||
address[0] = (byte) (address[0] ^ 0x01);
|
||||
PaymentAddress tamperedAddress = signedPaymentAddress.newBuilder()
|
||||
.mobileCoinAddress(signedPaymentAddress.mobileCoinAddress
|
||||
.newBuilder()
|
||||
.address(ByteString.of(address))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedAddress, identityKeyPair.getPublicKey()))
|
||||
.isInstanceOf(PaymentsAddressException.class)
|
||||
.hasMessage("Invalid MobileCoin address signature on payments address proto");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_not_verify_a_missing_signature() {
|
||||
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
|
||||
byte[] address = Util.getSecretBytes(100);
|
||||
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair);
|
||||
|
||||
PaymentAddress removedSignature = signedPaymentAddress.newBuilder()
|
||||
.mobileCoinAddress(signedPaymentAddress.mobileCoinAddress
|
||||
.newBuilder()
|
||||
.signature(null)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedSignature, identityKeyPair.getPublicKey()))
|
||||
.isInstanceOf(PaymentsAddressException.class)
|
||||
.hasMessage("Invalid MobileCoin address signature on payments address proto");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_not_verify_a_missing_address() {
|
||||
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
|
||||
byte[] address = Util.getSecretBytes(100);
|
||||
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair);
|
||||
|
||||
PaymentAddress removedAddress = signedPaymentAddress.newBuilder()
|
||||
.mobileCoinAddress(signedPaymentAddress.mobileCoinAddress
|
||||
.newBuilder()
|
||||
.address(null)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedAddress, identityKeyPair.getPublicKey()))
|
||||
.isInstanceOf(PaymentsAddressException.class)
|
||||
.hasMessage("Invalid MobileCoin address signature on payments address proto");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package org.thoughtcrime.securesms.payments
|
||||
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.hasMessage
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isInstanceOf
|
||||
import okio.ByteString
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.test.LibSignalLibraryUtil
|
||||
|
||||
class MobileCoinPublicAddressProfileUtilTest {
|
||||
@Before
|
||||
fun ensureNativeSupported() {
|
||||
LibSignalLibraryUtil.assumeLibSignalSupportedOnOS()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun can_verify_an_address() {
|
||||
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
|
||||
val address = Util.getSecretBytes(100)
|
||||
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair)
|
||||
|
||||
val paymentsAddress = MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, identityKeyPair.publicKey)
|
||||
|
||||
assertThat(paymentsAddress).isEqualTo(address)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun can_not_verify_an_address_with_the_wrong_key() {
|
||||
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
|
||||
val wrongPublicKey = IdentityKeyUtil.generateIdentityKeyPair().publicKey
|
||||
val address = Util.getSecretBytes(100)
|
||||
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair)
|
||||
|
||||
assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, wrongPublicKey) }
|
||||
.isInstanceOf<PaymentsAddressException>()
|
||||
.hasMessage("Invalid MobileCoin address signature on payments address proto")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun can_not_verify_a_tampered_signature() {
|
||||
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
|
||||
val address = Util.getSecretBytes(100)
|
||||
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair)
|
||||
val mobileCoinAddress = signedPaymentAddress.mobileCoinAddress!!
|
||||
|
||||
val signature = mobileCoinAddress.signature!!.toByteArray()
|
||||
signature[0] = (signature[0].toInt() xor 0x01).toByte()
|
||||
val tamperedSignature = signedPaymentAddress.newBuilder()
|
||||
.mobileCoinAddress(
|
||||
mobileCoinAddress
|
||||
.newBuilder()
|
||||
.signature(ByteString.of(*signature))
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedSignature, identityKeyPair.publicKey) }
|
||||
.isInstanceOf<PaymentsAddressException>()
|
||||
.hasMessage("Invalid MobileCoin address signature on payments address proto")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun can_not_verify_a_tampered_address() {
|
||||
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
|
||||
val addressBytes = Util.getSecretBytes(100)
|
||||
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(addressBytes, identityKeyPair)
|
||||
val mobileCoinAddress = signedPaymentAddress.mobileCoinAddress!!
|
||||
|
||||
val address = mobileCoinAddress.address!!.toByteArray()
|
||||
address[0] = (address[0].toInt() xor 0x01).toByte()
|
||||
val tamperedAddress = signedPaymentAddress.newBuilder()
|
||||
.mobileCoinAddress(
|
||||
mobileCoinAddress
|
||||
.newBuilder()
|
||||
.address(ByteString.of(*address))
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedAddress, identityKeyPair.publicKey) }
|
||||
.isInstanceOf<PaymentsAddressException>()
|
||||
.hasMessage("Invalid MobileCoin address signature on payments address proto")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun can_not_verify_a_missing_signature() {
|
||||
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
|
||||
val address = Util.getSecretBytes(100)
|
||||
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair)
|
||||
|
||||
val removedSignature = signedPaymentAddress.newBuilder()
|
||||
.mobileCoinAddress(
|
||||
signedPaymentAddress.mobileCoinAddress!!
|
||||
.newBuilder()
|
||||
.signature(null)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedSignature, identityKeyPair.publicKey) }
|
||||
.isInstanceOf<PaymentsAddressException>()
|
||||
.hasMessage("Invalid MobileCoin address signature on payments address proto")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun can_not_verify_a_missing_address() {
|
||||
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
|
||||
val address = Util.getSecretBytes(100)
|
||||
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair)
|
||||
|
||||
val removedAddress = signedPaymentAddress.newBuilder()
|
||||
.mobileCoinAddress(
|
||||
signedPaymentAddress.mobileCoinAddress!!
|
||||
.newBuilder()
|
||||
.address(null)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedAddress, identityKeyPair.publicKey) }
|
||||
.isInstanceOf<PaymentsAddressException>()
|
||||
.hasMessage("Invalid MobileCoin address signature on payments address proto")
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package org.thoughtcrime.securesms.util.livedata;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestRule;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.thoughtcrime.securesms.util.livedata.LiveDataTestUtil.assertNoValue;
|
||||
import static org.thoughtcrime.securesms.util.livedata.LiveDataTestUtil.observeAndGetOneValue;
|
||||
|
||||
public final class LiveDataUtilTest_skip {
|
||||
|
||||
@Rule
|
||||
public TestRule rule = new LiveDataRule();
|
||||
|
||||
@Test
|
||||
public void skip_no_value() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 0);
|
||||
|
||||
assertNoValue(skipped);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_same_value_with_zero_skip() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 0);
|
||||
liveData.setValue("A");
|
||||
|
||||
assertEquals("A", observeAndGetOneValue(skipped));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_second_value_with_skip_one() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
TestObserver<String> testObserver = new TestObserver<>();
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 1);
|
||||
|
||||
skipped.observeForever(testObserver);
|
||||
liveData.setValue("A");
|
||||
liveData.setValue("B");
|
||||
skipped.removeObserver(testObserver);
|
||||
|
||||
Assertions.assertThat(testObserver.getValues())
|
||||
.containsExactly("B");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_no_value_with_skip() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 1);
|
||||
liveData.setValue("A");
|
||||
|
||||
assertNoValue(skipped);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_third_and_fourth_value_with_skip_two() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
TestObserver<String> testObserver = new TestObserver<>();
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 2);
|
||||
|
||||
skipped.observeForever(testObserver);
|
||||
liveData.setValue("A");
|
||||
liveData.setValue("B");
|
||||
liveData.setValue("C");
|
||||
liveData.setValue("D");
|
||||
skipped.removeObserver(testObserver);
|
||||
|
||||
Assertions.assertThat(testObserver.getValues())
|
||||
.containsExactly("C", "D");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_set_one_before_then_skip() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
TestObserver<String> testObserver = new TestObserver<>();
|
||||
|
||||
liveData.setValue("A");
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 2);
|
||||
|
||||
skipped.observeForever(testObserver);
|
||||
liveData.setValue("B");
|
||||
liveData.setValue("C");
|
||||
liveData.setValue("D");
|
||||
skipped.removeObserver(testObserver);
|
||||
|
||||
Assertions.assertThat(testObserver.getValues())
|
||||
.containsExactly("C", "D");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_set_two_before_then_skip() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
TestObserver<String> testObserver = new TestObserver<>();
|
||||
|
||||
liveData.setValue("A");
|
||||
liveData.setValue("B");
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 2);
|
||||
|
||||
skipped.observeForever(testObserver);
|
||||
liveData.setValue("C");
|
||||
liveData.setValue("D");
|
||||
skipped.removeObserver(testObserver);
|
||||
|
||||
Assertions.assertThat(testObserver.getValues())
|
||||
.containsExactly("D");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package org.thoughtcrime.securesms.util.livedata
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.containsExactlyInAnyOrder
|
||||
import assertk.assertions.isEqualTo
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
|
||||
@Suppress("ClassName")
|
||||
class LiveDataUtilTest_skip {
|
||||
@get:Rule
|
||||
val rule: TestRule = LiveDataRule()
|
||||
|
||||
@Test
|
||||
fun skip_no_value() {
|
||||
val liveData = MutableLiveData<String>()
|
||||
|
||||
val skipped = LiveDataUtil.skip(liveData, 0)
|
||||
|
||||
LiveDataTestUtil.assertNoValue(skipped)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun skip_same_value_with_zero_skip() {
|
||||
val liveData = MutableLiveData<String>()
|
||||
|
||||
val skipped = LiveDataUtil.skip(liveData, 0)
|
||||
liveData.value = "A"
|
||||
|
||||
assertThat(LiveDataTestUtil.observeAndGetOneValue(skipped)).isEqualTo("A")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun skip_second_value_with_skip_one() {
|
||||
val liveData = MutableLiveData<String>()
|
||||
val testObserver = TestObserver<String>()
|
||||
|
||||
val skipped = LiveDataUtil.skip(liveData, 1)
|
||||
|
||||
skipped.observeForever(testObserver)
|
||||
liveData.value = "A"
|
||||
liveData.value = "B"
|
||||
skipped.removeObserver(testObserver)
|
||||
|
||||
assertThat(testObserver.values).containsExactlyInAnyOrder("B")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun skip_no_value_with_skip() {
|
||||
val liveData = MutableLiveData<String>()
|
||||
|
||||
val skipped = LiveDataUtil.skip(liveData, 1)
|
||||
liveData.value = "A"
|
||||
|
||||
LiveDataTestUtil.assertNoValue(skipped)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun skip_third_and_fourth_value_with_skip_two() {
|
||||
val liveData = MutableLiveData<String>()
|
||||
val testObserver = TestObserver<String>()
|
||||
|
||||
val skipped = LiveDataUtil.skip(liveData, 2)
|
||||
|
||||
skipped.observeForever(testObserver)
|
||||
liveData.value = "A"
|
||||
liveData.value = "B"
|
||||
liveData.value = "C"
|
||||
liveData.value = "D"
|
||||
skipped.removeObserver(testObserver)
|
||||
|
||||
assertThat(testObserver.values).containsExactlyInAnyOrder("C", "D")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun skip_set_one_before_then_skip() {
|
||||
val liveData = MutableLiveData<String>()
|
||||
val testObserver = TestObserver<String>()
|
||||
|
||||
liveData.value = "A"
|
||||
|
||||
val skipped = LiveDataUtil.skip(liveData, 2)
|
||||
|
||||
skipped.observeForever(testObserver)
|
||||
liveData.value = "B"
|
||||
liveData.value = "C"
|
||||
liveData.value = "D"
|
||||
skipped.removeObserver(testObserver)
|
||||
|
||||
assertThat(testObserver.values).containsExactlyInAnyOrder("C", "D")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun skip_set_two_before_then_skip() {
|
||||
val liveData = MutableLiveData<String>()
|
||||
val testObserver = TestObserver<String>()
|
||||
|
||||
liveData.value = "A"
|
||||
liveData.value = "B"
|
||||
|
||||
val skipped = LiveDataUtil.skip(liveData, 2)
|
||||
|
||||
skipped.observeForever(testObserver)
|
||||
liveData.value = "C"
|
||||
liveData.value = "D"
|
||||
skipped.removeObserver(testObserver)
|
||||
|
||||
assertThat(testObserver.values).containsExactlyInAnyOrder("D")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user