public void testMessageKeyLimits() throws Exception { SessionRecord aliceSessionRecord = new SessionRecord(); SessionRecord bobSessionRecord = new SessionRecord(); initializeSessionsV3(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState()); AxolotlStore aliceStore = new TestInMemoryAxolotlStore(); AxolotlStore bobStore = new TestInMemoryAxolotlStore(); aliceStore.storeSession(new AxolotlAddress("+14159999999", 1), aliceSessionRecord); bobStore.storeSession(new AxolotlAddress("+14158888888", 1), bobSessionRecord); SessionCipher aliceCipher = new SessionCipher(aliceStore, new AxolotlAddress("+14159999999", 1)); SessionCipher bobCipher = new SessionCipher(bobStore, new AxolotlAddress("+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 WhisperMessage(inflight.get(1000).serialize())); bobCipher.decrypt(new WhisperMessage(inflight.get(inflight.size() - 1).serialize())); try { bobCipher.decrypt(new WhisperMessage(inflight.get(0).serialize())); throw new AssertionError("Should have failed!"); } catch (DuplicateMessageException dme) { // good } }
private PreKeyBundle createBobPreKeyBundle(AxolotlStore bobStore) throws InvalidKeyException { ECKeyPair bobUnsignedPreKey = Curve.generateKeyPair(); int bobUnsignedPreKeyId = new Random().nextInt(Medium.MAX_VALUE); byte[] bobSignature = Curve.calculateSignature( bobStore.getIdentityKeyPair().getPrivateKey(), bobSignedPreKey.getPublicKey().serialize()); PreKeyBundle bobPreKeyBundle = new PreKeyBundle( 1, 1, bobUnsignedPreKeyId, bobUnsignedPreKey.getPublicKey(), bobSignedPreKeyId, bobSignedPreKey.getPublicKey(), bobSignature, bobStore.getIdentityKeyPair().getPublicKey()); bobStore.storeSignedPreKey( bobSignedPreKeyId, new SignedPreKeyRecord( bobSignedPreKeyId, System.currentTimeMillis(), bobSignedPreKey, bobSignature)); bobStore.storePreKey( bobUnsignedPreKeyId, new PreKeyRecord(bobUnsignedPreKeyId, bobUnsignedPreKey)); return bobPreKeyBundle; }
public void testBasicSimultaneousInitiate() throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, InvalidMessageException, DuplicateMessageException, LegacyMessageException, InvalidKeyIdException, NoSessionException { AxolotlStore aliceStore = new TestInMemoryAxolotlStore(); AxolotlStore bobStore = new TestInMemoryAxolotlStore(); 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 PreKeyWhisperMessage(messageForAlice.serialize())); byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(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)); CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes()); assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE); byte[] finalPlaintext = aliceSessionCipher.decrypt(new WhisperMessage(finalMessage.serialize())); assertTrue(new String(finalPlaintext).equals("third message")); assertTrue(isSessionIdEqual(aliceStore, bobStore)); }
private void runInteraction(SessionRecord aliceSessionRecord, SessionRecord bobSessionRecord) throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException { AxolotlStore aliceStore = new TestInMemoryAxolotlStore(); AxolotlStore bobStore = new TestInMemoryAxolotlStore(); aliceStore.storeSession(new AxolotlAddress("+14159999999", 1), aliceSessionRecord); bobStore.storeSession(new AxolotlAddress("+14158888888", 1), bobSessionRecord); SessionCipher aliceCipher = new SessionCipher(aliceStore, new AxolotlAddress("+14159999999", 1)); SessionCipher bobCipher = new SessionCipher(bobStore, new AxolotlAddress("+14158888888", 1)); byte[] alicePlaintext = "This is a plaintext message.".getBytes(); CiphertextMessage message = aliceCipher.encrypt(alicePlaintext); byte[] bobPlaintext = bobCipher.decrypt(new WhisperMessage(message.serialize())); assertTrue(Arrays.equals(alicePlaintext, bobPlaintext)); byte[] bobReply = "This is a message from Bob.".getBytes(); CiphertextMessage reply = bobCipher.encrypt(bobReply); byte[] receivedReply = aliceCipher.decrypt(new WhisperMessage(reply.serialize())); assertTrue(Arrays.equals(bobReply, receivedReply)); List<CiphertextMessage> aliceCiphertextMessages = new ArrayList<>(); List<byte[]> alicePlaintextMessages = new ArrayList<>(); for (int i = 0; i < 50; i++) { alicePlaintextMessages.add(("смерть за смерть " + i).getBytes()); aliceCiphertextMessages.add(aliceCipher.encrypt(("смерть за смерть " + i).getBytes())); } long seed = System.currentTimeMillis(); Collections.shuffle(aliceCiphertextMessages, new Random(seed)); Collections.shuffle(alicePlaintextMessages, new Random(seed)); for (int i = 0; i < aliceCiphertextMessages.size() / 2; i++) { byte[] receivedPlaintext = bobCipher.decrypt(new WhisperMessage(aliceCiphertextMessages.get(i).serialize())); assertTrue(Arrays.equals(receivedPlaintext, alicePlaintextMessages.get(i))); } List<CiphertextMessage> bobCiphertextMessages = new ArrayList<>(); List<byte[]> bobPlaintextMessages = new ArrayList<>(); for (int i = 0; i < 20; i++) { bobPlaintextMessages.add(("смерть за смерть " + i).getBytes()); bobCiphertextMessages.add(bobCipher.encrypt(("смерть за смерть " + i).getBytes())); } seed = System.currentTimeMillis(); Collections.shuffle(bobCiphertextMessages, new Random(seed)); Collections.shuffle(bobPlaintextMessages, new Random(seed)); for (int i = 0; i < bobCiphertextMessages.size() / 2; i++) { byte[] receivedPlaintext = aliceCipher.decrypt(new WhisperMessage(bobCiphertextMessages.get(i).serialize())); assertTrue(Arrays.equals(receivedPlaintext, bobPlaintextMessages.get(i))); } for (int i = aliceCiphertextMessages.size() / 2; i < aliceCiphertextMessages.size(); i++) { byte[] receivedPlaintext = bobCipher.decrypt(new WhisperMessage(aliceCiphertextMessages.get(i).serialize())); assertTrue(Arrays.equals(receivedPlaintext, alicePlaintextMessages.get(i))); } for (int i = bobCiphertextMessages.size() / 2; i < bobCiphertextMessages.size(); i++) { byte[] receivedPlaintext = aliceCipher.decrypt(new WhisperMessage(bobCiphertextMessages.get(i).serialize())); assertTrue(Arrays.equals(receivedPlaintext, bobPlaintextMessages.get(i))); } }
private boolean isSessionIdEqual(AxolotlStore aliceStore, AxolotlStore bobStore) { return Arrays.equals( aliceStore.loadSession(BOB_ADDRESS).getSessionState().getAliceBaseKey(), bobStore.loadSession(ALICE_ADDRESS).getSessionState().getAliceBaseKey()); }