mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-14 09:50:06 +00:00
Add first cut of protocol v3 support.
1) Use the new /v2/keys API for storing/retrieving prekey bundles. 2) For sessions built with PreKeyBundle and PreKeyWhisperMessage, use a v3 ratcheting session when available.
This commit is contained in:
@@ -9,32 +9,53 @@ import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.InvalidMessageException;
|
||||
import org.whispersystems.libaxolotl.LegacyMessageException;
|
||||
import org.whispersystems.libaxolotl.SessionCipher;
|
||||
import org.whispersystems.libaxolotl.state.SessionRecord;
|
||||
import org.whispersystems.libaxolotl.state.SessionState;
|
||||
import org.whispersystems.libaxolotl.state.SessionStore;
|
||||
import org.whispersystems.libaxolotl.ecc.Curve;
|
||||
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
|
||||
import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
|
||||
import org.whispersystems.libaxolotl.ratchet.RatchetingSession;
|
||||
import org.whispersystems.libaxolotl.ratchet.RatchetingSessionV2;
|
||||
import org.whispersystems.libaxolotl.ratchet.RatchetingSessionV3;
|
||||
import org.whispersystems.libaxolotl.state.SessionRecord;
|
||||
import org.whispersystems.libaxolotl.state.SessionState;
|
||||
import org.whispersystems.libaxolotl.state.SessionStore;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class SessionCipherTest extends AndroidTestCase {
|
||||
|
||||
public void testBasicSession()
|
||||
public void testBasicSessionV2()
|
||||
throws InvalidKeyException, DuplicateMessageException,
|
||||
LegacyMessageException, InvalidMessageException
|
||||
LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException
|
||||
{
|
||||
SessionRecord aliceSessionRecord = new SessionRecord();
|
||||
SessionRecord bobSessionRecord = new SessionRecord();
|
||||
|
||||
initializeSessions(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState());
|
||||
initializeSessionsV2(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState());
|
||||
runInteraction(aliceSessionRecord, bobSessionRecord);
|
||||
}
|
||||
|
||||
public void testBasicSessionV3()
|
||||
throws InvalidKeyException, DuplicateMessageException,
|
||||
LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException
|
||||
{
|
||||
SessionRecord aliceSessionRecord = new SessionRecord();
|
||||
SessionRecord bobSessionRecord = new SessionRecord();
|
||||
|
||||
initializeSessionsV3(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState());
|
||||
runInteraction(aliceSessionRecord, bobSessionRecord);
|
||||
}
|
||||
|
||||
private void runInteraction(SessionRecord aliceSessionRecord, SessionRecord bobSessionRecord)
|
||||
throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException {
|
||||
SessionStore aliceSessionStore = new InMemorySessionStore();
|
||||
SessionStore bobSessionStore = new InMemorySessionStore();
|
||||
|
||||
aliceSessionStore.store(2L, 1, aliceSessionRecord);
|
||||
bobSessionStore.store(3L, 1, bobSessionRecord);
|
||||
aliceSessionStore.storeSession(2L, 1, aliceSessionRecord);
|
||||
bobSessionStore.storeSession(3L, 1, bobSessionRecord);
|
||||
|
||||
SessionCipher aliceCipher = new SessionCipher(aliceSessionStore, 2L, 1);
|
||||
SessionCipher bobCipher = new SessionCipher(bobSessionStore, 3L, 1);
|
||||
@@ -50,10 +71,56 @@ public class SessionCipherTest extends AndroidTestCase {
|
||||
byte[] receivedReply = aliceCipher.decrypt(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(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(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(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(bobCiphertextMessages.get(i).serialize());
|
||||
assertTrue(Arrays.equals(receivedPlaintext, bobPlaintextMessages.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void initializeSessions(SessionState aliceSessionState, SessionState bobSessionState)
|
||||
private void initializeSessionsV2(SessionState aliceSessionState, SessionState bobSessionState)
|
||||
throws InvalidKeyException
|
||||
{
|
||||
ECKeyPair aliceIdentityKeyPair = Curve.generateKeyPair(false);
|
||||
@@ -69,12 +136,44 @@ public class SessionCipherTest extends AndroidTestCase {
|
||||
ECKeyPair bobEphemeralKey = bobBaseKey;
|
||||
|
||||
|
||||
RatchetingSession.initializeSession(aliceSessionState, aliceBaseKey, bobBaseKey.getPublicKey(),
|
||||
aliceEphemeralKey, bobEphemeralKey.getPublicKey(),
|
||||
aliceIdentityKey, bobIdentityKey.getPublicKey());
|
||||
RatchetingSessionV2.initializeSession(aliceSessionState, aliceBaseKey, bobBaseKey.getPublicKey(),
|
||||
aliceEphemeralKey, bobEphemeralKey.getPublicKey(),
|
||||
aliceIdentityKey, bobIdentityKey.getPublicKey());
|
||||
|
||||
RatchetingSessionV2.initializeSession(bobSessionState, bobBaseKey, aliceBaseKey.getPublicKey(),
|
||||
bobEphemeralKey, aliceEphemeralKey.getPublicKey(),
|
||||
bobIdentityKey, aliceIdentityKey.getPublicKey());
|
||||
}
|
||||
|
||||
private void initializeSessionsV3(SessionState aliceSessionState, SessionState bobSessionState)
|
||||
throws InvalidKeyException
|
||||
{
|
||||
ECKeyPair aliceIdentityKeyPair = Curve.generateKeyPair(false);
|
||||
IdentityKeyPair aliceIdentityKey = new IdentityKeyPair(new IdentityKey(aliceIdentityKeyPair.getPublicKey()),
|
||||
aliceIdentityKeyPair.getPrivateKey());
|
||||
ECKeyPair aliceBaseKey = Curve.generateKeyPair(true);
|
||||
ECKeyPair aliceEphemeralKey = Curve.generateKeyPair(true);
|
||||
|
||||
ECKeyPair alicePreKey = aliceBaseKey;
|
||||
|
||||
ECKeyPair bobIdentityKeyPair = Curve.generateKeyPair(false);
|
||||
IdentityKeyPair bobIdentityKey = new IdentityKeyPair(new IdentityKey(bobIdentityKeyPair.getPublicKey()),
|
||||
bobIdentityKeyPair.getPrivateKey());
|
||||
ECKeyPair bobBaseKey = Curve.generateKeyPair(true);
|
||||
ECKeyPair bobEphemeralKey = bobBaseKey;
|
||||
|
||||
ECKeyPair bobPreKey = Curve.generateKeyPair(true);
|
||||
|
||||
|
||||
RatchetingSessionV3.initializeSession(aliceSessionState, aliceBaseKey, bobBaseKey.getPublicKey(),
|
||||
aliceEphemeralKey, bobEphemeralKey.getPublicKey(),
|
||||
alicePreKey, bobPreKey.getPublicKey(),
|
||||
aliceIdentityKey, bobIdentityKey.getPublicKey());
|
||||
|
||||
RatchetingSessionV3.initializeSession(bobSessionState, bobBaseKey, aliceBaseKey.getPublicKey(),
|
||||
bobEphemeralKey, aliceEphemeralKey.getPublicKey(),
|
||||
bobPreKey, alicePreKey.getPublicKey(),
|
||||
bobIdentityKey, aliceIdentityKey.getPublicKey());
|
||||
|
||||
RatchetingSession.initializeSession(bobSessionState, bobBaseKey, aliceBaseKey.getPublicKey(),
|
||||
bobEphemeralKey, aliceEphemeralKey.getPublicKey(),
|
||||
bobIdentityKey, aliceIdentityKey.getPublicKey());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user