Java Code Examples for org.whispersystems.libsignal.protocol.CiphertextMessage

The following examples show how to use org.whispersystems.libsignal.protocol.CiphertextMessage. These examples are extracted from open source projects. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source Project: bcm-android   Author: bcmapp   File: SignalServiceCipher.java    License: GNU General Public License v3.0 6 votes vote down vote up
public OutgoingPushMessage encrypt(SignalProtocolAddress destination, byte[] unpaddedMessage, PushPurpose pushPurpose)
    throws UntrustedIdentityException
{
  SessionCipher        sessionCipher        = new SessionCipher(signalProtocolStore, destination);
  PushTransportDetails transportDetails     = new PushTransportDetails(sessionCipher.getSessionVersion());
  CiphertextMessage    message              = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage));
  int                  remoteRegistrationId = sessionCipher.getRemoteRegistrationId();
  String               body                 = Base64.encodeBytes(message.serialize());

  int type;

  switch (message.getType()) {
    case CiphertextMessage.PREKEY_TYPE:  type = Type.PREKEY_BUNDLE_VALUE; break;
    case CiphertextMessage.WHISPER_TYPE: type = Type.CIPHERTEXT_VALUE;    break;
    default: throw new AssertionError("Bad type: " + message.getType());
  }

  return new OutgoingPushMessage(type, destination.getDeviceId(), remoteRegistrationId, body, pushPurpose);
}
 
Example #2
Source Project: Silence   Author: SilenceIM   File: SessionBuilder.java    License: GNU General Public License v3.0 6 votes vote down vote up
/**
 * Initiate a new session by sending an initial KeyExchangeMessage to the recipient.
 *
 * @return the KeyExchangeMessage to deliver.
 */
public KeyExchangeMessage process() {
  synchronized (SessionCipher.SESSION_LOCK) {
    try {
      int             sequence         = KeyHelper.getRandomSequence(65534) + 1;
      int             flags            = KeyExchangeMessage.INITIATE_FLAG;
      ECKeyPair       baseKey          = Curve.generateKeyPair();
      ECKeyPair       ratchetKey       = Curve.generateKeyPair();
      IdentityKeyPair identityKey      = identityKeyStore.getIdentityKeyPair();
      byte[]          baseKeySignature = Curve.calculateSignature(identityKey.getPrivateKey(), baseKey.getPublicKey().serialize());
      SessionRecord   sessionRecord    = sessionStore.loadSession(remoteAddress);

      sessionRecord.getSessionState().setPendingKeyExchange(sequence, baseKey, ratchetKey, identityKey);
      sessionStore.storeSession(remoteAddress, sessionRecord);

      return new KeyExchangeMessage(CiphertextMessage.CURRENT_VERSION,
                                    sequence, flags, baseKey.getPublicKey(), baseKeySignature,
                                    ratchetKey.getPublicKey(), identityKey.getPublicKey());
    } catch (InvalidKeyException e) {
      throw new AssertionError(e);
    }
  }
}
 
Example #3
Source Project: Silence   Author: SilenceIM   File: SmsCipher.java    License: GNU General Public License v3.0 6 votes vote down vote up
public OutgoingTextMessage encrypt(OutgoingTextMessage message)
  throws NoSessionException, UntrustedIdentityException
{
  byte[] paddedBody      = transportDetails.getPaddedMessageBody(message.getMessageBody().getBytes());
  String recipientNumber = message.getRecipients().getPrimaryRecipient().getNumber();

  if (!signalProtocolStore.containsSession(new SignalProtocolAddress(recipientNumber, 1))) {
    throw new NoSessionException("No session for: " + recipientNumber);
  }

  SessionCipher     cipher            = new SessionCipher(signalProtocolStore, new SignalProtocolAddress(recipientNumber, 1));
  CiphertextMessage ciphertextMessage = cipher.encrypt(paddedBody);
  String            encodedCiphertext = new String(transportDetails.getEncodedMessage(ciphertextMessage.serialize()));

  if (ciphertextMessage.getType() == CiphertextMessage.PREKEY_TYPE) {
    return new OutgoingPrekeyBundleMessage(message, encodedCiphertext);
  } else {
    return message.withBody(encodedCiphertext);
  }
}
 
Example #4
Source Project: mollyim-android   Author: mollyim   File: TextSecureSessionStore.java    License: GNU General Public License v3.0 5 votes vote down vote up
@Override
public boolean containsSession(SignalProtocolAddress address) {
  synchronized (FILE_LOCK) {
    if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
      RecipientId   recipientId   = Recipient.external(context, address.getName()).getId();
      SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());

      return sessionRecord != null &&
             sessionRecord.getSessionState().hasSenderChain() &&
             sessionRecord.getSessionState().getSessionVersion() == CiphertextMessage.CURRENT_VERSION;
    } else {
      return false;
    }
  }
}
 
Example #5
Source Project: bcm-android   Author: bcmapp   File: TextSecureSessionStore.java    License: GNU General Public License v3.0 5 votes vote down vote up
@Override
public boolean containsSession(SignalProtocolAddress address) {
    if (!getSessionFile(address).exists()) return false;

    SessionRecord sessionRecord = loadSession(address);

    return sessionRecord.getSessionState().hasSenderChain() &&
            sessionRecord.getSessionState().getSessionVersion() == CiphertextMessage.CURRENT_VERSION;
}
 
Example #6
Source Project: Pix-Art-Messenger   Author: kriztan   File: XmppAxolotlSession.java    License: GNU General Public License v3.0 5 votes vote down vote up
@Nullable
public AxolotlKey processSending(@NonNull byte[] outgoingMessage, boolean ignoreSessionTrust) {
    FingerprintStatus status = getTrust();
    if (ignoreSessionTrust || status.isTrustedAndActive()) {
        try {
            CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
            return new AxolotlKey(getRemoteAddress().getDeviceId(), ciphertextMessage.serialize(), ciphertextMessage.getType() == CiphertextMessage.PREKEY_TYPE);
        } catch (UntrustedIdentityException e) {
            return null;
        }
    } else {
        return null;
    }
}
 
Example #7
Source Project: Silence   Author: SilenceIM   File: KeyExchangeMessage.java    License: GNU General Public License v3.0 5 votes vote down vote up
public KeyExchangeMessage(byte[] serialized)
    throws InvalidMessageException, InvalidVersionException, LegacyMessageException
{
  try {
    byte[][] parts        = ByteUtil.split(serialized, 1, serialized.length - 1);
    this.version          = ByteUtil.highBitsToInt(parts[0][0]);
    this.supportedVersion = ByteUtil.lowBitsToInt(parts[0][0]);

    if (this.version < CiphertextMessage.CURRENT_VERSION) {
      throw new LegacyMessageException("Unsupported legacy version: " + this.version);
    }

    if (this.version > CiphertextMessage.CURRENT_VERSION) {
      throw new InvalidVersionException("Unknown version: " + this.version);
    }

    SignalProtos.KeyExchangeMessage message = SignalProtos.KeyExchangeMessage.parseFrom(parts[1]);

    if (!message.hasId()           || !message.hasBaseKey()     ||
        !message.hasRatchetKey()   || !message.hasIdentityKey() ||
        !message.hasBaseKeySignature())
    {
      throw new InvalidMessageException("Some required fields missing!");
    }

    this.sequence         = message.getId() >> 5;
    this.flags            = message.getId() & 0x1f;
    this.serialized       = serialized;
    this.baseKey          = Curve.decodePoint(message.getBaseKey().toByteArray(), 0);
    this.baseKeySignature = message.getBaseKeySignature().toByteArray();
    this.ratchetKey       = Curve.decodePoint(message.getRatchetKey().toByteArray(), 0);
    this.identityKey      = new IdentityKey(message.getIdentityKey().toByteArray(), 0);
  } catch (InvalidKeyException | IOException e) {
    throw new InvalidMessageException(e);
  }
}
 
Example #8
Source Project: Silence   Author: SilenceIM   File: SmsTransportDetails.java    License: GNU General Public License v3.0 5 votes vote down vote up
private int getMaxBodySizeForBytes(int bodyLength) {
  int encryptedBodyLength   = bodyLength + CiphertextMessage.ENCRYPTED_MESSAGE_OVERHEAD;
  int messageRecordsForBody = getMessageCountForBytes(encryptedBodyLength);

  if (messageRecordsForBody == 1) {
    return ENCRYPTED_SINGLE_MESSAGE_BODY_MAX_SIZE;
  } else {
    return
        FIRST_MULTI_MESSAGE_MAX_BYTES +
        (MULTI_MESSAGE_MAX_BYTES * (messageRecordsForBody-1)) -
            CiphertextMessage.ENCRYPTED_MESSAGE_OVERHEAD;
  }
}
 
Example #9
Source Project: Silence   Author: SilenceIM   File: MmsCipher.java    License: GNU General Public License v3.0 5 votes vote down vote up
public SendReq encrypt(Context context, SendReq message)
    throws NoSessionException, RecipientFormattingException, UndeliverableMessageException,
           UntrustedIdentityException
{
  EncodedStringValue[] encodedRecipient = message.getTo();
  String               recipientString  = encodedRecipient[0].getString();
  byte[]               pduBytes         = new PduComposer(context, message).make();

  if (pduBytes == null) {
    throw new UndeliverableMessageException("PDU composition failed, null payload");
  }

  if (!axolotlStore.containsSession(new SignalProtocolAddress(recipientString, 1))) {
    throw new NoSessionException("No session for: " + recipientString);
  }

  SessionCipher     cipher            = new SessionCipher(axolotlStore, new SignalProtocolAddress(recipientString, 1));
  CiphertextMessage ciphertextMessage = cipher.encrypt(pduBytes);
  byte[]            encryptedPduBytes = textTransport.getEncodedMessage(ciphertextMessage.serialize());

  PduBody body         = new PduBody();
  PduPart part         = new PduPart();

  part.setContentId((System.currentTimeMillis()+"").getBytes());
  part.setContentType(ContentType.TEXT_PLAIN.getBytes());
  part.setName((System.currentTimeMillis()+"").getBytes());
  part.setData(encryptedPduBytes);
  body.addPart(part);
  message.setSubject(new EncodedStringValue(WirePrefix.calculateEncryptedMmsSubject()));
  message.setBody(body);

  return message;
}
 
Example #10
Source Project: libsignal-protocol-java   Author: signalapp   File: RatchetingSession.java    License: GNU General Public License v3.0 5 votes vote down vote up
public static void initializeSession(SessionState sessionState, AliceSignalProtocolParameters parameters)
    throws InvalidKeyException
{
  try {
    sessionState.setSessionVersion(CiphertextMessage.CURRENT_VERSION);
    sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey());
    sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey());

    ECKeyPair             sendingRatchetKey = Curve.generateKeyPair();
    ByteArrayOutputStream secrets           = new ByteArrayOutputStream();

    secrets.write(getDiscontinuityBytes());

    secrets.write(Curve.calculateAgreement(parameters.getTheirSignedPreKey(),
                                           parameters.getOurIdentityKey().getPrivateKey()));
    secrets.write(Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(),
                                           parameters.getOurBaseKey().getPrivateKey()));
    secrets.write(Curve.calculateAgreement(parameters.getTheirSignedPreKey(),
                                           parameters.getOurBaseKey().getPrivateKey()));

    if (parameters.getTheirOneTimePreKey().isPresent()) {
      secrets.write(Curve.calculateAgreement(parameters.getTheirOneTimePreKey().get(),
                                             parameters.getOurBaseKey().getPrivateKey()));
    }

    DerivedKeys             derivedKeys  = calculateDerivedKeys(secrets.toByteArray());
    Pair<RootKey, ChainKey> sendingChain = derivedKeys.getRootKey().createChain(parameters.getTheirRatchetKey(), sendingRatchetKey);

    sessionState.addReceiverChain(parameters.getTheirRatchetKey(), derivedKeys.getChainKey());
    sessionState.setSenderChain(sendingRatchetKey, sendingChain.second());
    sessionState.setRootKey(sendingChain.first());
  } catch (IOException e) {
    throw new AssertionError(e);
  }
}
 
Example #11
Source Project: libsignal-protocol-java   Author: signalapp   File: RatchetingSession.java    License: GNU General Public License v3.0 5 votes vote down vote up
public static void initializeSession(SessionState sessionState, BobSignalProtocolParameters parameters)
    throws InvalidKeyException
{

  try {
    sessionState.setSessionVersion(CiphertextMessage.CURRENT_VERSION);
    sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey());
    sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey());

    ByteArrayOutputStream secrets = new ByteArrayOutputStream();

    secrets.write(getDiscontinuityBytes());

    secrets.write(Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(),
                                           parameters.getOurSignedPreKey().getPrivateKey()));
    secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(),
                                           parameters.getOurIdentityKey().getPrivateKey()));
    secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(),
                                           parameters.getOurSignedPreKey().getPrivateKey()));

    if (parameters.getOurOneTimePreKey().isPresent()) {
      secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(),
                                             parameters.getOurOneTimePreKey().get().getPrivateKey()));
    }

    DerivedKeys derivedKeys = calculateDerivedKeys(secrets.toByteArray());

    sessionState.setSenderChain(parameters.getOurRatchetKey(), derivedKeys.getChainKey());
    sessionState.setRootKey(derivedKeys.getRootKey());
  } catch (IOException e) {
    throw new AssertionError(e);
  }
}
 
Example #12
Source Project: libsignal-protocol-java   Author: signalapp   File: SessionCipherTest.java    License: GNU General Public License v3.0 5 votes vote down vote up
public void testMessageKeyLimits() throws Exception {
  SessionRecord aliceSessionRecord = new SessionRecord();
  SessionRecord bobSessionRecord   = new SessionRecord();

  initializeSessionsV3(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState());

  SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
  SignalProtocolStore bobStore   = new TestInMemorySignalProtocolStore();

  aliceStore.storeSession(new SignalProtocolAddress("+14159999999", 1), aliceSessionRecord);
  bobStore.storeSession(new SignalProtocolAddress("+14158888888", 1), bobSessionRecord);

  SessionCipher     aliceCipher    = new SessionCipher(aliceStore, new SignalProtocolAddress("+14159999999", 1));
  SessionCipher     bobCipher      = new SessionCipher(bobStore, new SignalProtocolAddress("+14158888888", 1));

  List<CiphertextMessage> inflight = new LinkedList<>();

  for (int i=0;i<2010;i++) {
    inflight.add(aliceCipher.encrypt("you've never been so hungry, you've never been so cold".getBytes()));
  }

  bobCipher.decrypt(new SignalMessage(inflight.get(1000).serialize()));
  bobCipher.decrypt(new SignalMessage(inflight.get(inflight.size()-1).serialize()));

  try {
    bobCipher.decrypt(new SignalMessage(inflight.get(0).serialize()));
    throw new AssertionError("Should have failed!");
  } catch (DuplicateMessageException dme) {
    // good
  }
}
 
Example #13
Source Project: Smack   Author: igniterealtime   File: SignalOmemoRatchet.java    License: Apache License 2.0 5 votes vote down vote up
@Override
public CiphertextTuple doubleRatchetEncrypt(OmemoDevice recipient, byte[] messageKey) {
    CiphertextMessage ciphertextMessage;
    try {
        ciphertextMessage = getCipher(recipient).encrypt(messageKey);
    } catch (UntrustedIdentityException e) {
        throw new AssertionError("Signals trust management MUST be disabled.");
    }

    int type = ciphertextMessage.getType() == CiphertextMessage.PREKEY_TYPE ?
            OmemoElement.TYPE_OMEMO_PREKEY_MESSAGE : OmemoElement.TYPE_OMEMO_MESSAGE;

    return new CiphertextTuple(ciphertextMessage.serialize(), type);
}
 
Example #14
Source Project: Conversations   Author: iNPUTmice   File: XmppAxolotlSession.java    License: GNU General Public License v3.0 5 votes vote down vote up
@Nullable
public AxolotlKey processSending(@NonNull byte[] outgoingMessage, boolean ignoreSessionTrust) {
	FingerprintStatus status = getTrust();
	if (ignoreSessionTrust || status.isTrustedAndActive()) {
		try {
			CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
			return new AxolotlKey(getRemoteAddress().getDeviceId(), ciphertextMessage.serialize(),ciphertextMessage.getType() == CiphertextMessage.PREKEY_TYPE);
		} catch (UntrustedIdentityException e) {
			return null;
		}
	} else {
		return null;
	}
}
 
Example #15
Source Project: libsignal-protocol-java   Author: signalapp   File: SessionBuilderTest.java    License: GNU General Public License v3.0 4 votes vote down vote up
public void testRepeatBundleMessageV3() throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, InvalidMessageException, InvalidKeyIdException, DuplicateMessageException, LegacyMessageException, NoSessionException {
  SignalProtocolStore aliceStore          = new TestInMemorySignalProtocolStore();
  SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);

  SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();

  ECKeyPair bobPreKeyPair            = Curve.generateKeyPair();
  ECKeyPair bobSignedPreKeyPair      = Curve.generateKeyPair();
  byte[]    bobSignedPreKeySignature = Curve.calculateSignature(bobStore.getIdentityKeyPair().getPrivateKey(),
                                                                bobSignedPreKeyPair.getPublicKey().serialize());

  PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.getLocalRegistrationId(), 1,
                                            31337, bobPreKeyPair.getPublicKey(),
                                            22, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature,
                                            bobStore.getIdentityKeyPair().getPublicKey());

  bobStore.storePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair));
  bobStore.storeSignedPreKey(22, new SignedPreKeyRecord(22, System.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature));

  aliceSessionBuilder.process(bobPreKey);

  String            originalMessage    = "L'homme est condamné à être libre";
  SessionCipher     aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
  CiphertextMessage outgoingMessageOne = aliceSessionCipher.encrypt(originalMessage.getBytes());
  CiphertextMessage outgoingMessageTwo = aliceSessionCipher.encrypt(originalMessage.getBytes());

  assertTrue(outgoingMessageOne.getType() == CiphertextMessage.PREKEY_TYPE);
  assertTrue(outgoingMessageTwo.getType() == CiphertextMessage.PREKEY_TYPE);

  PreKeySignalMessage incomingMessage = new PreKeySignalMessage(outgoingMessageOne.serialize());

  SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

  byte[]        plaintext        = bobSessionCipher.decrypt(incomingMessage);
  assertTrue(originalMessage.equals(new String(plaintext)));

  CiphertextMessage bobOutgoingMessage = bobSessionCipher.encrypt(originalMessage.getBytes());

  byte[] alicePlaintext = aliceSessionCipher.decrypt(new SignalMessage(bobOutgoingMessage.serialize()));
  assertTrue(originalMessage.equals(new String(alicePlaintext)));

  // The test

  PreKeySignalMessage incomingMessageTwo = new PreKeySignalMessage(outgoingMessageTwo.serialize());

  plaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(incomingMessageTwo.serialize()));
  assertTrue(originalMessage.equals(new String(plaintext)));

  bobOutgoingMessage = bobSessionCipher.encrypt(originalMessage.getBytes());
  alicePlaintext = aliceSessionCipher.decrypt(new SignalMessage(bobOutgoingMessage.serialize()));
  assertTrue(originalMessage.equals(new String(alicePlaintext)));

}
 
Example #16
Source Project: libsignal-protocol-java   Author: signalapp   File: SessionBuilderTest.java    License: GNU General Public License v3.0 4 votes vote down vote up
public void testBadMessageBundle() throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, InvalidMessageException, DuplicateMessageException, LegacyMessageException, InvalidKeyIdException {
  SignalProtocolStore aliceStore          = new TestInMemorySignalProtocolStore();
  SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);

  SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();

  ECKeyPair bobPreKeyPair            = Curve.generateKeyPair();
  ECKeyPair bobSignedPreKeyPair      = Curve.generateKeyPair();
  byte[]    bobSignedPreKeySignature = Curve.calculateSignature(bobStore.getIdentityKeyPair().getPrivateKey(),
                                                                bobSignedPreKeyPair.getPublicKey().serialize());

  PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.getLocalRegistrationId(), 1,
                                            31337, bobPreKeyPair.getPublicKey(),
                                            22, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature,
                                            bobStore.getIdentityKeyPair().getPublicKey());

  bobStore.storePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair));
  bobStore.storeSignedPreKey(22, new SignedPreKeyRecord(22, System.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature));

  aliceSessionBuilder.process(bobPreKey);

  String            originalMessage    = "L'homme est condamné à être libre";
  SessionCipher     aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
  CiphertextMessage outgoingMessageOne = aliceSessionCipher.encrypt(originalMessage.getBytes());

  assertTrue(outgoingMessageOne.getType() == CiphertextMessage.PREKEY_TYPE);

  byte[] goodMessage = outgoingMessageOne.serialize();
  byte[] badMessage  = new byte[goodMessage.length];
  System.arraycopy(goodMessage, 0, badMessage, 0, badMessage.length);

  badMessage[badMessage.length-10] ^= 0x01;

  PreKeySignalMessage incomingMessage  = new PreKeySignalMessage(badMessage);
  SessionCipher        bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

  byte[] plaintext = new byte[0];

  try {
    plaintext = bobSessionCipher.decrypt(incomingMessage);
    throw new AssertionError("Decrypt should have failed!");
  } catch (InvalidMessageException e) {
    // good.
  }

  assertTrue(bobStore.containsPreKey(31337));

  plaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(goodMessage));

  assertTrue(originalMessage.equals(new String(plaintext)));
  assertTrue(!bobStore.containsPreKey(31337));
}
 
Example #17
Source Project: libsignal-protocol-java   Author: signalapp   File: SessionBuilderTest.java    License: GNU General Public License v3.0 4 votes vote down vote up
public void testOptionalOneTimePreKey() throws Exception {
  SignalProtocolStore aliceStore          = new TestInMemorySignalProtocolStore();
  SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);

  SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();

  ECKeyPair bobPreKeyPair            = Curve.generateKeyPair();
  ECKeyPair bobSignedPreKeyPair      = Curve.generateKeyPair();
  byte[]    bobSignedPreKeySignature = Curve.calculateSignature(bobStore.getIdentityKeyPair().getPrivateKey(),
                                                                bobSignedPreKeyPair.getPublicKey().serialize());

  PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.getLocalRegistrationId(), 1,
                                            0, null,
                                            22, bobSignedPreKeyPair.getPublicKey(),
                                            bobSignedPreKeySignature,
                                            bobStore.getIdentityKeyPair().getPublicKey());

  aliceSessionBuilder.process(bobPreKey);

  assertTrue(aliceStore.containsSession(BOB_ADDRESS));
  assertTrue(aliceStore.loadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3);

  String            originalMessage    = "L'homme est condamné à être libre";
  SessionCipher     aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
  CiphertextMessage outgoingMessage    = aliceSessionCipher.encrypt(originalMessage.getBytes());

  assertTrue(outgoingMessage.getType() == CiphertextMessage.PREKEY_TYPE);

  PreKeySignalMessage incomingMessage = new PreKeySignalMessage(outgoingMessage.serialize());
  assertTrue(!incomingMessage.getPreKeyId().isPresent());

  bobStore.storePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair));
  bobStore.storeSignedPreKey(22, new SignedPreKeyRecord(22, System.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature));

  SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);
  byte[]        plaintext        = bobSessionCipher.decrypt(incomingMessage);

  assertTrue(bobStore.containsSession(ALICE_ADDRESS));
  assertTrue(bobStore.loadSession(ALICE_ADDRESS).getSessionState().getSessionVersion() == 3);
  assertTrue(bobStore.loadSession(ALICE_ADDRESS).getSessionState().getAliceBaseKey() != null);
  assertTrue(originalMessage.equals(new String(plaintext)));
}
 
Example #18
Source Project: libsignal-protocol-java   Author: signalapp   File: SimultaneousInitiateTests.java    License: GNU General Public License v3.0 4 votes vote down vote up
public void testSimultaneousInitiateLostMessage()
      throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException,
      InvalidMessageException, DuplicateMessageException, LegacyMessageException,
      InvalidKeyIdException, NoSessionException
  {
    SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
    SignalProtocolStore bobStore   = new TestInMemorySignalProtocolStore();

    PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore);
    PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore);

    SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);
    SessionBuilder bobSessionBuilder   = new SessionBuilder(bobStore, ALICE_ADDRESS);

    SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
    SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

    aliceSessionBuilder.process(bobPreKeyBundle);
    bobSessionBuilder.process(alicePreKeyBundle);

    CiphertextMessage messageForBob   = aliceSessionCipher.encrypt("hey there".getBytes());
    CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes());

    assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE);
    assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE);

    assertFalse(isSessionIdEqual(aliceStore, bobStore));

    byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeySignalMessage(messageForAlice.serialize()));
    byte[] bobPlaintext   = bobSessionCipher.decrypt(new PreKeySignalMessage(messageForBob.serialize()));

    assertTrue(new String(alicePlaintext).equals("sample message"));
    assertTrue(new String(bobPlaintext).equals("hey there"));

    assertTrue(aliceStore.loadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3);
    assertTrue(bobStore.loadSession(ALICE_ADDRESS).getSessionState().getSessionVersion() == 3);

    assertFalse(isSessionIdEqual(aliceStore, bobStore));

    CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes());

    assertTrue(aliceResponse.getType() == CiphertextMessage.WHISPER_TYPE);

//    byte[] responsePlaintext = bobSessionCipher.decrypt(new WhisperMessage(aliceResponse.serialize()));
//
//    assertTrue(new String(responsePlaintext).equals("second message"));
//    assertTrue(isSessionIdEqual(aliceStore, bobStore));
    assertFalse(isSessionIdEqual(aliceStore, bobStore));

    CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes());

    assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE);

    byte[] finalPlaintext = aliceSessionCipher.decrypt(new SignalMessage(finalMessage.serialize()));

    assertTrue(new String(finalPlaintext).equals("third message"));
    assertTrue(isSessionIdEqual(aliceStore, bobStore));
  }
 
Example #19
Source Project: libsignal-protocol-java   Author: signalapp   File: SimultaneousInitiateTests.java    License: GNU General Public License v3.0 2 votes vote down vote up
public void testBasicSimultaneousInitiate()
    throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException,
    InvalidMessageException, DuplicateMessageException, LegacyMessageException,
    InvalidKeyIdException, NoSessionException
{
  SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
  SignalProtocolStore bobStore   = new TestInMemorySignalProtocolStore();

  PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore);
  PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore);

  SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);
  SessionBuilder bobSessionBuilder   = new SessionBuilder(bobStore, ALICE_ADDRESS);

  SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
  SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

  aliceSessionBuilder.process(bobPreKeyBundle);
  bobSessionBuilder.process(alicePreKeyBundle);

  CiphertextMessage messageForBob   = aliceSessionCipher.encrypt("hey there".getBytes());
  CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes());

  assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE);
  assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE);

  assertFalse(isSessionIdEqual(aliceStore, bobStore));

  byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeySignalMessage(messageForAlice.serialize()));
  byte[] bobPlaintext   = bobSessionCipher.decrypt(new PreKeySignalMessage(messageForBob.serialize()));

  assertTrue(new String(alicePlaintext).equals("sample message"));
  assertTrue(new String(bobPlaintext).equals("hey there"));

  assertTrue(aliceStore.loadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3);
  assertTrue(bobStore.loadSession(ALICE_ADDRESS).getSessionState().getSessionVersion() == 3);

  assertFalse(isSessionIdEqual(aliceStore, bobStore));

  CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes());

  assertTrue(aliceResponse.getType() == CiphertextMessage.WHISPER_TYPE);

  byte[] responsePlaintext = bobSessionCipher.decrypt(new SignalMessage(aliceResponse.serialize()));

  assertTrue(new String(responsePlaintext).equals("second message"));
  assertTrue(isSessionIdEqual(aliceStore, bobStore));

  CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes());

  assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE);

  byte[] finalPlaintext = aliceSessionCipher.decrypt(new SignalMessage(finalMessage.serialize()));

  assertTrue(new String(finalPlaintext).equals("third message"));
  assertTrue(isSessionIdEqual(aliceStore, bobStore));
}
 
Example #20
Source Project: libsignal-protocol-java   Author: signalapp   File: SimultaneousInitiateTests.java    License: GNU General Public License v3.0 2 votes vote down vote up
public void testLostSimultaneousInitiate() throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, InvalidMessageException, DuplicateMessageException, LegacyMessageException, InvalidKeyIdException, NoSessionException {
  SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
  SignalProtocolStore bobStore   = new TestInMemorySignalProtocolStore();

  PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore);
  PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore);

  SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);
  SessionBuilder bobSessionBuilder   = new SessionBuilder(bobStore, ALICE_ADDRESS);

  SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
  SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

  aliceSessionBuilder.process(bobPreKeyBundle);
  bobSessionBuilder.process(alicePreKeyBundle);

  CiphertextMessage messageForBob   = aliceSessionCipher.encrypt("hey there".getBytes());
  CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes());

  assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE);
  assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE);

  assertFalse(isSessionIdEqual(aliceStore, bobStore));

  byte[] bobPlaintext   = bobSessionCipher.decrypt(new PreKeySignalMessage(messageForBob.serialize()));

  assertTrue(new String(bobPlaintext).equals("hey there"));
  assertTrue(bobStore.loadSession(ALICE_ADDRESS).getSessionState().getSessionVersion() == 3);

  CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes());

  assertTrue(aliceResponse.getType() == CiphertextMessage.PREKEY_TYPE);

  byte[] responsePlaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(aliceResponse.serialize()));

  assertTrue(new String(responsePlaintext).equals("second message"));
  assertTrue(isSessionIdEqual(aliceStore, bobStore));

  CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes());

  assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE);

  byte[] finalPlaintext = aliceSessionCipher.decrypt(new SignalMessage(finalMessage.serialize()));

  assertTrue(new String(finalPlaintext).equals("third message"));
  assertTrue(isSessionIdEqual(aliceStore, bobStore));
}
 
Example #21
Source Project: libsignal-protocol-java   Author: signalapp   File: SimultaneousInitiateTests.java    License: GNU General Public License v3.0 2 votes vote down vote up
public void testSimultaneousInitiateRepeatedMessages()
    throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException,
    InvalidMessageException, DuplicateMessageException, LegacyMessageException,
    InvalidKeyIdException, NoSessionException
{
  SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
  SignalProtocolStore bobStore   = new TestInMemorySignalProtocolStore();

  PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore);
  PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore);

  SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);
  SessionBuilder bobSessionBuilder   = new SessionBuilder(bobStore, ALICE_ADDRESS);

  SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
  SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

  aliceSessionBuilder.process(bobPreKeyBundle);
  bobSessionBuilder.process(alicePreKeyBundle);

  CiphertextMessage messageForBob   = aliceSessionCipher.encrypt("hey there".getBytes());
  CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes());

  assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE);
  assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE);

  assertFalse(isSessionIdEqual(aliceStore, bobStore));

  byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeySignalMessage(messageForAlice.serialize()));
  byte[] bobPlaintext   = bobSessionCipher.decrypt(new PreKeySignalMessage(messageForBob.serialize()));

  assertTrue(new String(alicePlaintext).equals("sample message"));
  assertTrue(new String(bobPlaintext).equals("hey there"));

  assertTrue(aliceStore.loadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3);
  assertTrue(bobStore.loadSession(ALICE_ADDRESS).getSessionState().getSessionVersion() == 3);

  assertFalse(isSessionIdEqual(aliceStore, bobStore));

  for (int i=0;i<50;i++) {
    CiphertextMessage messageForBobRepeat   = aliceSessionCipher.encrypt("hey there".getBytes());
    CiphertextMessage messageForAliceRepeat = bobSessionCipher.encrypt("sample message".getBytes());

    assertTrue(messageForBobRepeat.getType() == CiphertextMessage.WHISPER_TYPE);
    assertTrue(messageForAliceRepeat.getType() == CiphertextMessage.WHISPER_TYPE);

    assertFalse(isSessionIdEqual(aliceStore, bobStore));

    byte[] alicePlaintextRepeat = aliceSessionCipher.decrypt(new SignalMessage(messageForAliceRepeat.serialize()));
    byte[] bobPlaintextRepeat   = bobSessionCipher.decrypt(new SignalMessage(messageForBobRepeat.serialize()));

    assertTrue(new String(alicePlaintextRepeat).equals("sample message"));
    assertTrue(new String(bobPlaintextRepeat).equals("hey there"));

    assertFalse(isSessionIdEqual(aliceStore, bobStore));
  }

  CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes());

  assertTrue(aliceResponse.getType() == CiphertextMessage.WHISPER_TYPE);

  byte[] responsePlaintext = bobSessionCipher.decrypt(new SignalMessage(aliceResponse.serialize()));

  assertTrue(new String(responsePlaintext).equals("second message"));
  assertTrue(isSessionIdEqual(aliceStore, bobStore));

  CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes());

  assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE);

  byte[] finalPlaintext = aliceSessionCipher.decrypt(new SignalMessage(finalMessage.serialize()));

  assertTrue(new String(finalPlaintext).equals("third message"));
  assertTrue(isSessionIdEqual(aliceStore, bobStore));
}
 
Example #22
Source Project: libsignal-protocol-java   Author: signalapp   File: SimultaneousInitiateTests.java    License: GNU General Public License v3.0 2 votes vote down vote up
public void testRepeatedSimultaneousInitiateRepeatedMessages()
    throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException,
    InvalidMessageException, DuplicateMessageException, LegacyMessageException,
    InvalidKeyIdException, NoSessionException
{
  SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
  SignalProtocolStore bobStore   = new TestInMemorySignalProtocolStore();


  SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);
  SessionBuilder bobSessionBuilder   = new SessionBuilder(bobStore, ALICE_ADDRESS);

  SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
  SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

  for (int i=0;i<15;i++) {
    PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore);
    PreKeyBundle bobPreKeyBundle   = createBobPreKeyBundle(bobStore);

    aliceSessionBuilder.process(bobPreKeyBundle);
    bobSessionBuilder.process(alicePreKeyBundle);

    CiphertextMessage messageForBob = aliceSessionCipher.encrypt("hey there".getBytes());
    CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes());

    assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE);
    assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE);

    assertFalse(isSessionIdEqual(aliceStore, bobStore));

    byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeySignalMessage(messageForAlice.serialize()));
    byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(messageForBob.serialize()));

    assertTrue(new String(alicePlaintext).equals("sample message"));
    assertTrue(new String(bobPlaintext).equals("hey there"));

    assertTrue(aliceStore.loadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3);
    assertTrue(bobStore.loadSession(ALICE_ADDRESS).getSessionState().getSessionVersion() == 3);

    assertFalse(isSessionIdEqual(aliceStore, bobStore));
  }

  for (int i=0;i<50;i++) {
    CiphertextMessage messageForBobRepeat   = aliceSessionCipher.encrypt("hey there".getBytes());
    CiphertextMessage messageForAliceRepeat = bobSessionCipher.encrypt("sample message".getBytes());

    assertTrue(messageForBobRepeat.getType() == CiphertextMessage.WHISPER_TYPE);
    assertTrue(messageForAliceRepeat.getType() == CiphertextMessage.WHISPER_TYPE);

    assertFalse(isSessionIdEqual(aliceStore, bobStore));

    byte[] alicePlaintextRepeat = aliceSessionCipher.decrypt(new SignalMessage(messageForAliceRepeat.serialize()));
    byte[] bobPlaintextRepeat   = bobSessionCipher.decrypt(new SignalMessage(messageForBobRepeat.serialize()));

    assertTrue(new String(alicePlaintextRepeat).equals("sample message"));
    assertTrue(new String(bobPlaintextRepeat).equals("hey there"));

    assertFalse(isSessionIdEqual(aliceStore, bobStore));
  }

  CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes());

  assertTrue(aliceResponse.getType() == CiphertextMessage.WHISPER_TYPE);

  byte[] responsePlaintext = bobSessionCipher.decrypt(new SignalMessage(aliceResponse.serialize()));

  assertTrue(new String(responsePlaintext).equals("second message"));
  assertTrue(isSessionIdEqual(aliceStore, bobStore));

  CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes());

  assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE);

  byte[] finalPlaintext = aliceSessionCipher.decrypt(new SignalMessage(finalMessage.serialize()));

  assertTrue(new String(finalPlaintext).equals("third message"));
  assertTrue(isSessionIdEqual(aliceStore, bobStore));
}
 
Example #23
Source Project: libsignal-protocol-java   Author: signalapp   File: SimultaneousInitiateTests.java    License: GNU General Public License v3.0 2 votes vote down vote up
public void testRepeatedSimultaneousInitiateLostMessageRepeatedMessages()
      throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException,
      InvalidMessageException, DuplicateMessageException, LegacyMessageException,
      InvalidKeyIdException, NoSessionException
  {
    SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
    SignalProtocolStore bobStore   = new TestInMemorySignalProtocolStore();


    SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);
    SessionBuilder bobSessionBuilder   = new SessionBuilder(bobStore, ALICE_ADDRESS);

    SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
    SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

//    PreKeyBundle aliceLostPreKeyBundle = createAlicePreKeyBundle(aliceStore);
    PreKeyBundle bobLostPreKeyBundle   = createBobPreKeyBundle(bobStore);

    aliceSessionBuilder.process(bobLostPreKeyBundle);
//    bobSessionBuilder.process(aliceLostPreKeyBundle);

    CiphertextMessage lostMessageForBob   = aliceSessionCipher.encrypt("hey there".getBytes());
//    CiphertextMessage lostMessageForAlice = bobSessionCipher.encrypt("sample message".getBytes());

    for (int i=0;i<15;i++) {
      PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore);
      PreKeyBundle bobPreKeyBundle   = createBobPreKeyBundle(bobStore);

      aliceSessionBuilder.process(bobPreKeyBundle);
      bobSessionBuilder.process(alicePreKeyBundle);

      CiphertextMessage messageForBob = aliceSessionCipher.encrypt("hey there".getBytes());
      CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes());

      assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE);
      assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE);

      assertFalse(isSessionIdEqual(aliceStore, bobStore));

      byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeySignalMessage(messageForAlice.serialize()));
      byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(messageForBob.serialize()));

      assertTrue(new String(alicePlaintext).equals("sample message"));
      assertTrue(new String(bobPlaintext).equals("hey there"));

      assertTrue(aliceStore.loadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3);
      assertTrue(bobStore.loadSession(ALICE_ADDRESS).getSessionState().getSessionVersion() == 3);

      assertFalse(isSessionIdEqual(aliceStore, bobStore));
    }

    for (int i=0;i<50;i++) {
      CiphertextMessage messageForBobRepeat   = aliceSessionCipher.encrypt("hey there".getBytes());
      CiphertextMessage messageForAliceRepeat = bobSessionCipher.encrypt("sample message".getBytes());

      assertTrue(messageForBobRepeat.getType() == CiphertextMessage.WHISPER_TYPE);
      assertTrue(messageForAliceRepeat.getType() == CiphertextMessage.WHISPER_TYPE);

      assertFalse(isSessionIdEqual(aliceStore, bobStore));

      byte[] alicePlaintextRepeat = aliceSessionCipher.decrypt(new SignalMessage(messageForAliceRepeat.serialize()));
      byte[] bobPlaintextRepeat   = bobSessionCipher.decrypt(new SignalMessage(messageForBobRepeat.serialize()));

      assertTrue(new String(alicePlaintextRepeat).equals("sample message"));
      assertTrue(new String(bobPlaintextRepeat).equals("hey there"));

      assertFalse(isSessionIdEqual(aliceStore, bobStore));
    }

    CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes());

    assertTrue(aliceResponse.getType() == CiphertextMessage.WHISPER_TYPE);

    byte[] responsePlaintext = bobSessionCipher.decrypt(new SignalMessage(aliceResponse.serialize()));

    assertTrue(new String(responsePlaintext).equals("second message"));
    assertTrue(isSessionIdEqual(aliceStore, bobStore));

    CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes());

    assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE);

    byte[] finalPlaintext = aliceSessionCipher.decrypt(new SignalMessage(finalMessage.serialize()));

    assertTrue(new String(finalPlaintext).equals("third message"));
    assertTrue(isSessionIdEqual(aliceStore, bobStore));

    byte[] lostMessagePlaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(lostMessageForBob.serialize()));
    assertTrue(new String(lostMessagePlaintext).equals("hey there"));

    assertFalse(isSessionIdEqual(aliceStore, bobStore));

    CiphertextMessage blastFromThePast          = bobSessionCipher.encrypt("unexpected!".getBytes());
    byte[]            blastFromThePastPlaintext = aliceSessionCipher.decrypt(new SignalMessage(blastFromThePast.serialize()));

    assertTrue(new String(blastFromThePastPlaintext).equals("unexpected!"));
    assertTrue(isSessionIdEqual(aliceStore, bobStore));
  }