diff --git a/java/java/build.gradle b/java/java/build.gradle index ad8b62950..568fab570 100644 --- a/java/java/build.gradle +++ b/java/java/build.gradle @@ -65,6 +65,7 @@ test { } include 'org/whispersystems/**' + include 'org/signal/**' } def isReleaseBuild() { diff --git a/java/tests/src/test/java/org/signal/libsignal/metadata/SealedSessionCipherTest.java b/java/tests/src/test/java/org/signal/libsignal/metadata/SealedSessionCipherTest.java new file mode 100644 index 000000000..121d1020a --- /dev/null +++ b/java/tests/src/test/java/org/signal/libsignal/metadata/SealedSessionCipherTest.java @@ -0,0 +1,180 @@ +package org.signal.libsignal.metadata; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import junit.framework.TestCase; + +import org.signal.libsignal.metadata.SealedSessionCipher.DecryptionResult; +import org.signal.libsignal.metadata.certificate.CertificateValidator; +import org.signal.libsignal.metadata.certificate.InvalidCertificateException; +import org.signal.libsignal.metadata.certificate.SenderCertificate; +import org.signal.libsignal.metadata.certificate.ServerCertificate; +import org.whispersystems.libsignal.IdentityKeyPair; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.SessionBuilder; +import org.whispersystems.libsignal.SignalProtocolAddress; +import org.whispersystems.libsignal.UntrustedIdentityException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECKeyPair; +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.libsignal.state.PreKeyBundle; +import org.whispersystems.libsignal.state.PreKeyRecord; +import org.whispersystems.libsignal.state.SignedPreKeyRecord; +import org.whispersystems.libsignal.util.KeyHelper; +import org.whispersystems.libsignal.util.Pair; + +import java.util.UUID; + +public class SealedSessionCipherTest extends TestCase { + + public void testEncryptDecrypt() throws UntrustedIdentityException, InvalidKeyException, InvalidCertificateException, InvalidProtocolBufferException, InvalidMetadataMessageException, ProtocolDuplicateMessageException, ProtocolUntrustedIdentityException, ProtocolLegacyMessageException, ProtocolInvalidKeyException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolInvalidMessageException, ProtocolInvalidKeyIdException, ProtocolNoSessionException, SelfSendException { + TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); + TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); + + initializeSessions(aliceStore, bobStore); + + ECKeyPair trustRoot = Curve.generateKeyPair(); + SenderCertificate senderCertificate = createCertificateFor(trustRoot, UUID.fromString("9d0652a3-dcc3-4d11-975f-74d61598733f"), "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337); + SealedSessionCipher aliceCipher = new SealedSessionCipher(aliceStore, UUID.fromString("9d0652a3-dcc3-4d11-975f-74d61598733f"), "+14151111111", 1); + + byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1), + senderCertificate, "smert za smert".getBytes()); + + + SealedSessionCipher bobCipher = new SealedSessionCipher(bobStore, UUID.fromString("e80f7bbe-5b94-471e-bd8c-2173654ea3d1"), "+14152222222", 1); + + DecryptionResult plaintext = bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335); + + assertEquals(new String(plaintext.getPaddedMessage()), "smert za smert"); + assertEquals(plaintext.getSenderUuid().get(), "9d0652a3-dcc3-4d11-975f-74d61598733f"); + assertEquals(plaintext.getSenderE164().get(), "+14151111111"); + assertEquals(plaintext.getDeviceId(), 1); + } + + public void testEncryptDecryptUntrusted() throws Exception { + TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); + TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); + + initializeSessions(aliceStore, bobStore); + + ECKeyPair trustRoot = Curve.generateKeyPair(); + ECKeyPair falseTrustRoot = Curve.generateKeyPair(); + SenderCertificate senderCertificate = createCertificateFor(falseTrustRoot, UUID.fromString("9d0652a3-dcc3-4d11-975f-74d61598733f"), "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337); + SealedSessionCipher aliceCipher = new SealedSessionCipher(aliceStore, UUID.fromString("9d0652a3-dcc3-4d11-975f-74d61598733f"), "+14151111111", 1); + + byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1), + senderCertificate, "и вот я".getBytes()); + + SealedSessionCipher bobCipher = new SealedSessionCipher(bobStore, UUID.fromString("e80f7bbe-5b94-471e-bd8c-2173654ea3d1"), "+14152222222", 1); + + try { + bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335); + throw new AssertionError(); + } catch (InvalidMetadataMessageException e) { + // good + } + } + + public void testEncryptDecryptExpired() throws Exception { + TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); + TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); + + initializeSessions(aliceStore, bobStore); + + ECKeyPair trustRoot = Curve.generateKeyPair(); + SenderCertificate senderCertificate = createCertificateFor(trustRoot, UUID.fromString("9d0652a3-dcc3-4d11-975f-74d61598733f"), "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337); + SealedSessionCipher aliceCipher = new SealedSessionCipher(aliceStore, UUID.fromString("9d0652a3-dcc3-4d11-975f-74d61598733f"), "+14151111111", 1); + + byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1), + senderCertificate, "и вот я".getBytes()); + + SealedSessionCipher bobCipher = new SealedSessionCipher(bobStore, UUID.fromString("e80f7bbe-5b94-471e-bd8c-2173654ea3d1"), "+14152222222", 1); + + try { + bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31338); + throw new AssertionError(); + } catch (InvalidMetadataMessageException e) { + // good + } + } + + public void testEncryptFromWrongIdentity() throws Exception { + TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); + TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); + + initializeSessions(aliceStore, bobStore); + + ECKeyPair trustRoot = Curve.generateKeyPair(); + ECKeyPair randomKeyPair = Curve.generateKeyPair(); + SenderCertificate senderCertificate = createCertificateFor(trustRoot, UUID.fromString("9d0652a3-dcc3-4d11-975f-74d61598733f"), "+14151111111", 1, randomKeyPair.getPublicKey(), 31337); + SealedSessionCipher aliceCipher = new SealedSessionCipher(aliceStore, UUID.fromString("9d0652a3-dcc3-4d11-975f-74d61598733f"), "+14151111111", 1); + + byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1), + senderCertificate, "smert za smert".getBytes()); + + + SealedSessionCipher bobCipher = new SealedSessionCipher(bobStore, UUID.fromString("e80f7bbe-5b94-471e-bd8c-2173654ea3d1"), "+14152222222", 1); + + try { + bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335); + } catch (InvalidMetadataMessageException e) { + // good + } + } + + + + private SenderCertificate createCertificateFor(ECKeyPair trustRoot, UUID uuid, String e164, int deviceId, ECPublicKey identityKey, long expires) + throws InvalidKeyException, InvalidCertificateException, InvalidProtocolBufferException { + ECKeyPair serverKey = Curve.generateKeyPair(); + + byte[] serverCertificateBytes = SignalProtos.ServerCertificate.Certificate.newBuilder() + .setId(1) + .setKey(ByteString.copyFrom(serverKey.getPublicKey().serialize())) + .build() + .toByteArray(); + + byte[] serverCertificateSignature = Curve.calculateSignature(trustRoot.getPrivateKey(), serverCertificateBytes); + + ServerCertificate serverCertificate = new ServerCertificate(SignalProtos.ServerCertificate.newBuilder() + .setCertificate(ByteString.copyFrom(serverCertificateBytes)) + .setSignature(ByteString.copyFrom(serverCertificateSignature)) + .build() + .toByteArray()); + + byte[] senderCertificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder() + .setSenderUuid(uuid.toString()) + .setSenderE164(e164) + .setSenderDevice(deviceId) + .setIdentityKey(ByteString.copyFrom(identityKey.serialize())) + .setExpires(expires) + .setSigner(SignalProtos.ServerCertificate.parseFrom(serverCertificate.getSerialized())) + .build() + .toByteArray(); + + byte[] senderCertificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), senderCertificateBytes); + + return new SenderCertificate(SignalProtos.SenderCertificate.newBuilder() + .setCertificate(ByteString.copyFrom(senderCertificateBytes)) + .setSignature(ByteString.copyFrom(senderCertificateSignature)) + .build() + .toByteArray()); + } + + private void initializeSessions(TestInMemorySignalProtocolStore aliceStore, TestInMemorySignalProtocolStore bobStore) + throws InvalidKeyException, UntrustedIdentityException + { + ECKeyPair bobPreKey = Curve.generateKeyPair(); + IdentityKeyPair bobIdentityKey = bobStore.getIdentityKeyPair(); + SignedPreKeyRecord bobSignedPreKey = KeyHelper.generateSignedPreKey(bobIdentityKey, 2); + + PreKeyBundle bobBundle = new PreKeyBundle(1, 1, 1, bobPreKey.getPublicKey(), 2, bobSignedPreKey.getKeyPair().getPublicKey(), bobSignedPreKey.getSignature(), bobIdentityKey.getPublicKey()); + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, new SignalProtocolAddress("+14152222222", 1)); + aliceSessionBuilder.process(bobBundle); + + bobStore.storeSignedPreKey(2, bobSignedPreKey); + bobStore.storePreKey(1, new PreKeyRecord(1, bobPreKey)); + + } +} diff --git a/java/tests/src/test/java/org/signal/libsignal/metadata/TestInMemorySignalProtocolStore.java b/java/tests/src/test/java/org/signal/libsignal/metadata/TestInMemorySignalProtocolStore.java new file mode 100644 index 000000000..25ac41e82 --- /dev/null +++ b/java/tests/src/test/java/org/signal/libsignal/metadata/TestInMemorySignalProtocolStore.java @@ -0,0 +1,27 @@ +package org.signal.libsignal.metadata; + + +import org.whispersystems.libsignal.IdentityKey; +import org.whispersystems.libsignal.IdentityKeyPair; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECKeyPair; +import org.whispersystems.libsignal.state.SignedPreKeyRecord; +import org.whispersystems.libsignal.state.impl.InMemorySignalProtocolStore; +import org.whispersystems.libsignal.util.KeyHelper; + +public class TestInMemorySignalProtocolStore extends InMemorySignalProtocolStore { + public TestInMemorySignalProtocolStore() { + super(generateIdentityKeyPair(), generateRegistrationId()); + } + + private static IdentityKeyPair generateIdentityKeyPair() { + ECKeyPair identityKeyPairKeys = Curve.generateKeyPair(); + + return new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()), + identityKeyPairKeys.getPrivateKey()); + } + + private static int generateRegistrationId() { + return KeyHelper.generateRegistrationId(false); + } +} \ No newline at end of file diff --git a/java/tests/src/test/java/org/signal/libsignal/metadata/certificate/SenderCertificateTest.java b/java/tests/src/test/java/org/signal/libsignal/metadata/certificate/SenderCertificateTest.java new file mode 100644 index 000000000..fc35b5293 --- /dev/null +++ b/java/tests/src/test/java/org/signal/libsignal/metadata/certificate/SenderCertificateTest.java @@ -0,0 +1,130 @@ +package org.signal.libsignal.metadata.certificate; + + +import com.google.protobuf.ByteString; + +import junit.framework.TestCase; + +import org.signal.libsignal.metadata.SignalProtos; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECKeyPair; + +public class SenderCertificateTest extends TestCase { + + private final ECKeyPair trustRoot = Curve.generateKeyPair(); + + public void testSignature() throws InvalidCertificateException, InvalidKeyException { + ECKeyPair serverKey = Curve.generateKeyPair(); + ECKeyPair key = Curve.generateKeyPair(); + + byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder() + .setSenderUuid("9d0652a3-dcc3-4d11-975f-74d61598733f") + .setSenderE164("+14152222222") + .setSenderDevice(1) + .setExpires(31337) + .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize())) + .setSigner(getServerCertificate(serverKey)) + .build() + .toByteArray(); + + byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes); + + SenderCertificate senderCertificate = new SenderCertificate(SignalProtos.SenderCertificate.newBuilder() + .setCertificate(ByteString.copyFrom(certificateBytes)) + .setSignature(ByteString.copyFrom(certificateSignature)) + .build() + .toByteArray()); + + new CertificateValidator(trustRoot.getPublicKey()).validate(senderCertificate, 31336); + } + + public void testExpiredSignature() throws InvalidCertificateException, InvalidKeyException { + ECKeyPair serverKey = Curve.generateKeyPair(); + ECKeyPair key = Curve.generateKeyPair(); + + byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder() + .setSenderUuid("9d0652a3-dcc3-4d11-975f-74d61598733f") + .setSenderE164("+14152222222") + .setSenderDevice(1) + .setExpires(31337) + .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize())) + .setSigner(getServerCertificate(serverKey)) + .build() + .toByteArray(); + + byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes); + + SenderCertificate senderCertificate = new SenderCertificate(SignalProtos.SenderCertificate.newBuilder() + .setCertificate(ByteString.copyFrom(certificateBytes)) + .setSignature(ByteString.copyFrom(certificateSignature)) + .build() + .toByteArray()); + + try { + new CertificateValidator(trustRoot.getPublicKey()).validate(senderCertificate, 31338); + throw new AssertionError(); + } catch (InvalidCertificateException e) { + // good + } + } + + public void testBadSignature() throws InvalidCertificateException, InvalidKeyException { + ECKeyPair serverKey = Curve.generateKeyPair(); + ECKeyPair key = Curve.generateKeyPair(); + + byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder() + .setSenderUuid("9d0652a3-dcc3-4d11-975f-74d61598733f") + .setSenderE164("+14152222222") + .setSenderDevice(1) + .setExpires(31337) + .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize())) + .setSigner(getServerCertificate(serverKey)) + .build() + .toByteArray(); + + byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes); + + for (int i=0;i