parent
56d53f0b6a
commit
a96c8867ae
13 changed files with 431 additions and 365 deletions
|
@ -590,7 +590,7 @@ dependencies {
|
|||
}
|
||||
|
||||
testImplementation(testLibs.junit.junit)
|
||||
testImplementation(testLibs.assertj.core)
|
||||
testImplementation(testLibs.assertk)
|
||||
testImplementation(testLibs.androidx.test.core)
|
||||
testImplementation(testLibs.robolectric.robolectric) {
|
||||
exclude(group = "com.google.protobuf", module = "protobuf-java")
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
-dontwarn com.android.support.test.**
|
||||
-dontwarn sun.reflect.**
|
||||
-dontwarn sun.misc.**
|
||||
-dontwarn org.assertj.**
|
||||
-dontwarn assertk.**
|
||||
-dontwarn org.hamcrest.**
|
||||
-dontwarn com.squareup.**
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ The following dependencies are licensed under Apache License, Version 2.0:
|
|||
* Android Tracing (https://developer.android.com/jetpack/androidx/releases/tracing#1.0.0)
|
||||
* AndroidX Futures (https://developer.android.com/topic/libraries/architecture/index.html)
|
||||
* AndroidX Test Library (https://developer.android.com/testing)
|
||||
* AssertJ fluent assertions
|
||||
* AutoValue Annotations (https://github.com/google/auto/tree/master/value)
|
||||
* Byte Buddy (without dependencies)
|
||||
* Byte Buddy agent
|
||||
|
@ -336,6 +335,7 @@ The following dependencies are licensed under The MIT License:
|
|||
* annotations (http://robolectric.org)
|
||||
* framework (http://robolectric.org)
|
||||
* junit (http://robolectric.org)
|
||||
* assertk (https://github.com/willowtreeapps/assertk)
|
||||
* ktlint (https://github.com/pinterest/ktlint)
|
||||
* ktlint-cli-reporter (https://github.com/pinterest/ktlint)
|
||||
* ktlint-cli-ruleset-core (https://github.com/pinterest/ktlint)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -55,7 +55,6 @@ dependencies {
|
|||
implementation(libs.kotlinx.coroutines.core.jvm)
|
||||
|
||||
testImplementation(testLibs.junit.junit)
|
||||
testImplementation(testLibs.assertj.core)
|
||||
testImplementation(testLibs.junit.junit)
|
||||
testImplementation(testLibs.assertk)
|
||||
testImplementation(testLibs.kotlinx.coroutines.test)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ robolectric-robolectric = { module = "org.robolectric:robolectric", version.ref
|
|||
bouncycastle-bcprov-jdk15on = "org.bouncycastle:bcprov-jdk15on:1.70"
|
||||
bouncycastle-bcpkix-jdk15on = "org.bouncycastle:bcpkix-jdk15on:1.70"
|
||||
hamcrest-hamcrest = "org.hamcrest:hamcrest:2.2"
|
||||
assertj-core = "org.assertj:assertj-core:3.11.1"
|
||||
assertk = "com.willowtreeapps.assertk:assertk:0.28.1"
|
||||
square-okhttp-mockserver = "com.squareup.okhttp3:mockwebserver:4.12.0"
|
||||
mockk = "io.mockk:mockk:1.13.2"
|
||||
mockk-android = "io.mockk:mockk-android:1.13.2"
|
||||
|
|
|
@ -4526,6 +4526,38 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="0e09c60f88d446ff209c17a0a36503b49fdec475225d4381ae4fe9ed83fb912d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.willowtreeapps.assertk" name="assertk" version="0.28.1">
|
||||
<artifact name="assertk-0.28.1.module">
|
||||
<sha256 value="0326ac846e66a6f79a786c4fa0a120f15b9d10256610cd7d2bd5d0842b307bcd" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="assertk-metadata-0.28.1.jar">
|
||||
<sha256 value="1a624bd88c992dd9c0b628b41c7e1b2f2c22ca17439cd162b915d13549e70b56" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.willowtreeapps.assertk" name="assertk-jvm" version="0.28.1">
|
||||
<artifact name="assertk-jvm-0.28.1.jar">
|
||||
<sha256 value="0f8ec47cd657d47e36675d2e7f1e3a6ffc242399c1c6f2a6cd03fafb22fef3e3" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="assertk-jvm-0.28.1.module">
|
||||
<sha256 value="eeb033a1bfebd90d706c07d3847d75568095c94eb8509455965a191b763f0791" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.willowtreeapps.opentest4k" name="opentest4k" version="1.3.0">
|
||||
<artifact name="opentest4k-1.3.0.module">
|
||||
<sha256 value="80a43a89a61f9cfadffc531d02c495d862634c68be10db3bc0627ffbdafaffbf" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="opentest4k-metadata-1.3.0.jar">
|
||||
<sha256 value="8a5aa481a492c53fcab52144844a94740583235edf1d29defb2f2a063b0689fe" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.willowtreeapps.opentest4k" name="opentest4k-jvm" version="1.3.0">
|
||||
<artifact name="opentest4k-jvm-1.3.0.jar">
|
||||
<sha256 value="67bebae94b0cfcd01d97093ea100b68551afc3a4024be19f7341252556fea32f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="opentest4k-jvm-1.3.0.module">
|
||||
<sha256 value="bd09ab5d717ead0c16e4cfe8f162ae4abc76126f1ee22605aad64edb4e33d975" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="commons-codec" name="commons-codec" version="1.10">
|
||||
<artifact name="commons-codec-1.10.jar">
|
||||
<sha256 value="4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569" origin="Generated by Gradle"/>
|
||||
|
@ -5000,11 +5032,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.assertj" name="assertj-core" version="3.11.1">
|
||||
<artifact name="assertj-core-3.11.1.jar">
|
||||
<sha256 value="2ee2bd3e81fc818d423d442b658f28acf938d9078d6ba016a64b362fdd7779e8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.bitbucket.b_c" name="jose4j" version="0.7.0">
|
||||
<artifact name="jose4j-0.7.0.jar">
|
||||
<sha256 value="eb14f69c0395d4a106c6c46fe6dff080c4608ccabc99b1f03933d374383d9bbe" origin="Generated by Gradle"/>
|
||||
|
@ -5439,6 +5466,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="dccaa5d315470fab3920502886bbb85f2da6c86102c65d9c04410544eedb2019" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin" name="kotlin-stdlib" version="1.9.21">
|
||||
<artifact name="kotlin-stdlib-1.9.21-all.jar">
|
||||
<sha256 value="cec38bc3302e72a8aaf9cde436b5a9071ee0331e2ad05e84d8bb897334d7e9d4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlin-stdlib-1.9.21.module">
|
||||
<sha256 value="d3019f7f0d71924ce47298c9cc46af0245f75219719b35c5915fbcc7e7a69395" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin" name="kotlin-stdlib" version="1.9.22">
|
||||
<artifact name="kotlin-stdlib-1.9.22-all.jar">
|
||||
<sha256 value="cec38bc3302e72a8aaf9cde436b5a9071ee0331e2ad05e84d8bb897334d7e9d4" origin="Generated by Gradle"/>
|
||||
|
@ -5496,6 +5531,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="858828bc5191b9e602affa14e01d66489dafb08c4c18d2faee3cbed7ba7d9992" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-common" version="1.9.21">
|
||||
<artifact name="kotlin-stdlib-common-1.9.21.module">
|
||||
<sha256 value="68dc8e84aa05f5278c16505247d71346d3512b9edadd41f613d657c94c59993b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-common" version="1.9.22">
|
||||
<artifact name="kotlin-stdlib-common-1.9.22.module">
|
||||
<sha256 value="f93c9e9abf8d52d8e8fd8e851aa802ecec55132161c4aeee7d3cd924bf794246" origin="Generated by Gradle"/>
|
||||
|
@ -6045,6 +6085,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="58812de60898d976fb81ef3b62da05c6604c18fd4a249f5044282479fc286af2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.opentest4j" name="opentest4j" version="1.3.0">
|
||||
<artifact name="opentest4j-1.3.0.jar">
|
||||
<sha256 value="48e2df636cab6563ced64dcdff8abb2355627cb236ef0bf37598682ddf742f1b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="opentest4j-1.3.0.module">
|
||||
<sha256 value="48bf1d6c8b5dc94f74652bd17900f654deb714350248cf5e8fca27b9090c8e0d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2.asm" name="asm" version="9.5">
|
||||
<artifact name="asm-9.5.jar">
|
||||
<sha256 value="b62e84b5980729751b0458c534cf1366f727542bb8d158621335682a460f0353" origin="Generated by Gradle"/>
|
||||
|
|
|
@ -101,7 +101,7 @@ dependencies {
|
|||
implementation(project(":core-util-jvm"))
|
||||
|
||||
testImplementation(testLibs.junit.junit)
|
||||
testImplementation(testLibs.assertj.core)
|
||||
testImplementation(testLibs.assertk)
|
||||
testImplementation(testLibs.conscrypt.openjdk.uber)
|
||||
testImplementation(testLibs.mockk)
|
||||
testImplementation(testLibs.hamcrest.hamcrest)
|
||||
|
|
Loading…
Add table
Reference in a new issue