Javadocs, and some minor refactoring.

This commit is contained in:
Moxie Marlinspike 2014-04-24 12:28:38 -07:00
parent af45e5d544
commit 5a3c19fe3e
18 changed files with 333 additions and 46 deletions

View File

@ -16,7 +16,7 @@ public class InMemorySessionStore implements SessionStore {
public InMemorySessionStore() {}
@Override
public synchronized SessionRecord get(long recipientId, int deviceId) {
public synchronized SessionRecord load(long recipientId, int deviceId) {
if (contains(recipientId, deviceId)) {
return new InMemorySessionRecord(sessions.get(new Pair<>(recipientId, deviceId)));
} else {
@ -38,7 +38,7 @@ public class InMemorySessionStore implements SessionStore {
}
@Override
public synchronized void put(long recipientId, int deviceId, SessionRecord record) {
public synchronized void store(long recipientId, int deviceId, SessionRecord record) {
sessions.put(new Pair<>(recipientId, deviceId), record);
}

View File

@ -1,7 +1,6 @@
package org.whispersystems.test;
import android.test.AndroidTestCase;
import android.util.Log;
import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.IdentityKey;
@ -57,7 +56,7 @@ public class SessionBuilderTest extends AndroidTestCase {
aliceSessionBuilder.process(bobPreKey);
assertTrue(aliceSessionStore.contains(BOB_RECIPIENT_ID, 1));
assertTrue(!aliceSessionStore.get(BOB_RECIPIENT_ID, 1).getSessionState().getNeedsRefresh());
assertTrue(!aliceSessionStore.load(BOB_RECIPIENT_ID, 1).getSessionState().getNeedsRefresh());
String originalMessage = "L'homme est condamné à être libre";
SessionCipher aliceSessionCipher = new SessionCipher(aliceSessionStore, BOB_RECIPIENT_ID, 1);
@ -95,15 +94,11 @@ public class SessionBuilderTest extends AndroidTestCase {
KeyExchangeMessage aliceKeyExchangeMessage = aliceSessionBuilder.process();
KeyExchangeMessage bobKeyExchangeMessage = bobSessionBuilder.process(aliceKeyExchangeMessage);
Log.w("SessionBuilderTest", "Record from test: " + bobSessionStore.get(ALICE_RECIPIENT_ID, 1));
assertTrue(bobKeyExchangeMessage != null);
assertTrue(aliceKeyExchangeMessage != null);
KeyExchangeMessage response = aliceSessionBuilder.process(bobKeyExchangeMessage);
Log.w("SessionBuilderTest", "Record from test 2: " + bobSessionStore.get(ALICE_RECIPIENT_ID, 1));
assertTrue(response == null);
assertTrue(aliceSessionStore.contains(BOB_RECIPIENT_ID, 1));
assertTrue(bobSessionStore.contains(ALICE_RECIPIENT_ID, 1));
@ -171,7 +166,9 @@ public class SessionBuilderTest extends AndroidTestCase {
assertTrue(new String(plaintext).equals(originalMessage));
for (int i=0;i<10;i++) {
String loopingMessage = ("You can only desire based on what you know: " + i);
String loopingMessage = ("What do we mean by saying that existence precedes essence? " +
"We mean that man first of all exists, encounters himself, " +
"surges up in the world--and defines himself aftward. " + i);
CiphertextMessage aliceLoopingMessage = aliceSessionCipher.encrypt(loopingMessage.getBytes());
byte[] loopingPlaintext = bobSessionCipher.decrypt(aliceLoopingMessage.serialize());
@ -179,7 +176,9 @@ public class SessionBuilderTest extends AndroidTestCase {
}
for (int i=0;i<10;i++) {
String loopingMessage = ("You can only desire based on what you know: " + i);
String loopingMessage = ("What do we mean by saying that existence precedes essence? " +
"We mean that man first of all exists, encounters himself, " +
"surges up in the world--and defines himself aftward. " + i);
CiphertextMessage bobLoopingMessage = bobSessionCipher.encrypt(loopingMessage.getBytes());
byte[] loopingPlaintext = aliceSessionCipher.decrypt(bobLoopingMessage.serialize());
@ -189,14 +188,18 @@ public class SessionBuilderTest extends AndroidTestCase {
Set<Pair<String, CiphertextMessage>> aliceOutOfOrderMessages = new HashSet<>();
for (int i=0;i<10;i++) {
String loopingMessage = ("You can only desire based on what you know: " + i);
String loopingMessage = ("What do we mean by saying that existence precedes essence? " +
"We mean that man first of all exists, encounters himself, " +
"surges up in the world--and defines himself aftward. " + i);
CiphertextMessage aliceLoopingMessage = aliceSessionCipher.encrypt(loopingMessage.getBytes());
aliceOutOfOrderMessages.add(new Pair<>(loopingMessage, aliceLoopingMessage));
}
for (int i=0;i<10;i++) {
String loopingMessage = ("You can only desire based on what you know: " + i);
String loopingMessage = ("What do we mean by saying that existence precedes essence? " +
"We mean that man first of all exists, encounters himself, " +
"surges up in the world--and defines himself aftward. " + i);
CiphertextMessage aliceLoopingMessage = aliceSessionCipher.encrypt(loopingMessage.getBytes());
byte[] loopingPlaintext = bobSessionCipher.decrypt(aliceLoopingMessage.serialize());

View File

@ -33,8 +33,8 @@ public class SessionCipherTest extends AndroidTestCase {
SessionStore aliceSessionStore = new InMemorySessionStore();
SessionStore bobSessionStore = new InMemorySessionStore();
aliceSessionStore.put(2L, 1, aliceSessionRecord);
bobSessionStore.put(3L, 1, bobSessionRecord);
aliceSessionStore.store(2L, 1, aliceSessionRecord);
bobSessionStore.store(3L, 1, bobSessionRecord);
SessionCipher aliceCipher = new SessionCipher(aliceSessionStore, 2L, 1);
SessionCipher bobCipher = new SessionCipher(bobSessionStore, 3L, 1);

View File

@ -14,9 +14,26 @@ import org.whispersystems.libaxolotl.state.PreKeyRecord;
import org.whispersystems.libaxolotl.state.PreKeyStore;
import org.whispersystems.libaxolotl.state.SessionRecord;
import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.libaxolotl.util.Helper;
import org.whispersystems.libaxolotl.util.KeyHelper;
import org.whispersystems.libaxolotl.util.Medium;
/**
* SessionBuilder is responsible for setting up encrypted sessions.
* Once a session has been established, {@link org.whispersystems.libaxolotl.SessionCipher}
* can be used to encrypt/decrypt messages in that session.
* <p>
* Sessions are built from one of three different possible vectors:
* <ol>
* <li>A {@link org.whispersystems.libaxolotl.state.PreKey} retrieved from a server.</li>
* <li>A {@link org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage} received from a client.</li>
* <li>A {@link org.whispersystems.libaxolotl.protocol.KeyExchangeMessage} sent to or received from a client.</li>
* </ol>
*
* Sessions are constructed per recipientId + deviceId tuple. Remote logical users are identified
* by their recipientId, and each logical recipientId can have multiple physical devices.
*
* @author Moxie Marlinspike
*/
public class SessionBuilder {
private static final String TAG = SessionBuilder.class.getSimpleName();
@ -27,6 +44,15 @@ public class SessionBuilder {
private final long recipientId;
private final int deviceId;
/**
* Constructs a SessionBuilder.
*
* @param sessionStore The {@link org.whispersystems.libaxolotl.state.SessionStore} to store the constructed session in.
* @param preKeyStore The {@link org.whispersystems.libaxolotl.state.PreKeyStore} where the client's local {@link org.whispersystems.libaxolotl.state.PreKeyRecord}s are stored.
* @param identityKeyStore The {@link org.whispersystems.libaxolotl.state.IdentityKeyStore} containing the client's identity key information.
* @param recipientId The recipient ID of the remote user to build a session with.
* @param deviceId The device ID of the remote user's physical device.
*/
public SessionBuilder(SessionStore sessionStore,
PreKeyStore preKeyStore,
IdentityKeyStore identityKeyStore,
@ -39,6 +65,19 @@ public class SessionBuilder {
this.deviceId = deviceId;
}
/**
* Build a new session from a received {@link org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage}.
*
* After a session is constructed in this way, the embedded {@link org.whispersystems.libaxolotl.protocol.WhisperMessage}
* can be decrypted.
*
* @param message The received {@link org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage}.
* @throws org.whispersystems.libaxolotl.InvalidKeyIdException when there is no local
* {@link org.whispersystems.libaxolotl.state.PreKeyRecord}
* that corresponds to the PreKey ID in
* the message.
* @throws org.whispersystems.libaxolotl.InvalidKeyException when the message is formatted incorrectly.
*/
public void process(PreKeyWhisperMessage message)
throws InvalidKeyIdException, InvalidKeyException
{
@ -59,7 +98,7 @@ public class SessionBuilder {
if (!preKeyStore.contains(preKeyId))
throw new InvalidKeyIdException("No such prekey: " + preKeyId);
SessionRecord sessionRecord = sessionStore.get(recipientId, deviceId);
SessionRecord sessionRecord = sessionStore.load(recipientId, deviceId);
PreKeyRecord preKeyRecord = preKeyStore.load(preKeyId);
ECKeyPair ourBaseKey = preKeyRecord.getKeyPair();
ECKeyPair ourEphemeralKey = ourBaseKey;
@ -79,7 +118,7 @@ public class SessionBuilder {
if (simultaneousInitiate) sessionRecord.getSessionState().setNeedsRefresh(true);
sessionStore.put(recipientId, deviceId, sessionRecord);
sessionStore.store(recipientId, deviceId, sessionRecord);
if (preKeyId != Medium.MAX_VALUE) {
preKeyStore.remove(preKeyId);
@ -88,8 +127,16 @@ public class SessionBuilder {
identityKeyStore.saveIdentity(recipientId, theirIdentityKey);
}
/**
* Build a new session from a {@link org.whispersystems.libaxolotl.state.PreKey} retrieved from
* a server.
*
* @param preKey A PreKey for the destination recipient, retrieved from a server.
* @throws InvalidKeyException when the {@link org.whispersystems.libaxolotl.state.PreKey} is
* badly formatted.
*/
public void process(PreKey preKey) throws InvalidKeyException {
SessionRecord sessionRecord = sessionStore.get(recipientId, deviceId);
SessionRecord sessionRecord = sessionStore.load(recipientId, deviceId);
ECKeyPair ourBaseKey = Curve.generateKeyPair(true);
ECKeyPair ourEphemeralKey = Curve.generateKeyPair(true);
ECPublicKey theirBaseKey = preKey.getPublicKey();
@ -108,14 +155,22 @@ public class SessionBuilder {
sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId());
sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId());
sessionStore.put(recipientId, deviceId, sessionRecord);
sessionStore.store(recipientId, deviceId, sessionRecord);
identityKeyStore.saveIdentity(recipientId, preKey.getIdentityKey());
}
/**
* Build a new session from a {@link org.whispersystems.libaxolotl.protocol.KeyExchangeMessage}
* received from a remote client.
*
* @param message The received KeyExchangeMessage.
* @return The KeyExchangeMessage to respond with, or null if no response is necessary.
* @throws InvalidKeyException if the received KeyExchangeMessage is badly formatted.
*/
public KeyExchangeMessage process(KeyExchangeMessage message) throws InvalidKeyException {
KeyExchangeMessage responseMessage = null;
SessionRecord sessionRecord = sessionStore.get(recipientId, deviceId);
SessionRecord sessionRecord = sessionStore.load(recipientId, deviceId);
Log.w(TAG, "Received key exchange with sequence: " + message.getSequence());
@ -170,23 +225,28 @@ public class SessionBuilder {
ourIdentityKey, message.getIdentityKey());
sessionRecord.getSessionState().setSessionVersion(message.getVersion());
sessionStore.put(recipientId, deviceId, sessionRecord);
sessionStore.store(recipientId, deviceId, sessionRecord);
identityKeyStore.saveIdentity(recipientId, message.getIdentityKey());
return responseMessage;
}
/**
* Initiate a new session by sending an initial KeyExchangeMessage to the recipient.
*
* @return the KeyExchangeMessage to deliver.
*/
public KeyExchangeMessage process() {
int sequence = Helper.getRandomSequence(65534) + 1;
int sequence = KeyHelper.getRandomSequence(65534) + 1;
int flags = KeyExchangeMessage.INITIATE_FLAG;
ECKeyPair baseKey = Curve.generateKeyPair(true);
ECKeyPair ephemeralKey = Curve.generateKeyPair(true);
IdentityKeyPair identityKey = identityKeyStore.getIdentityKeyPair();
SessionRecord sessionRecord = sessionStore.get(recipientId, deviceId);
SessionRecord sessionRecord = sessionStore.load(recipientId, deviceId);
sessionRecord.getSessionState().setPendingKeyExchange(sequence, baseKey, ephemeralKey, identityKey);
sessionStore.put(recipientId, deviceId, sessionRecord);
sessionStore.store(recipientId, deviceId, sessionRecord);
return new KeyExchangeMessage(sequence, flags,
baseKey.getPublicKey(),

View File

@ -43,6 +43,15 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* The main entry point for Axolotl encrypt/decrypt operations.
*
* Once a session has been established with {@link SessionBuilder},
* this class can be used for all encrypt/decrypt operations within
* that session.
*
* @author Moxie Marlinspike
*/
public class SessionCipher {
private static final Object SESSION_LOCK = new Object();
@ -51,15 +60,30 @@ public class SessionCipher {
private final long recipientId;
private final int deviceId;
/**
* Construct a SessionCipher for encrypt/decrypt operations on a session.
* In order to use SessionCipher, a session must have already been created
* and stored using {@link SessionBuilder}.
*
* @param sessionStore The {@link SessionStore} that contains a session for this recipient.
* @param recipientId The remote ID that messages will be encrypted to or decrypted from.
* @param deviceId The device corresponding to the recipientId.
*/
public SessionCipher(SessionStore sessionStore, long recipientId, int deviceId) {
this.sessionStore = sessionStore;
this.recipientId = recipientId;
this.deviceId = deviceId;
}
/**
* Encrypt a message.
*
* @param paddedMessage The plaintext message bytes, optionally padded to a constant multiple.
* @return A ciphertext message encrypted to the recipient+device tuple.
*/
public CiphertextMessage encrypt(byte[] paddedMessage) {
synchronized (SESSION_LOCK) {
SessionRecord sessionRecord = sessionStore.get(recipientId, deviceId);
SessionRecord sessionRecord = sessionStore.load(recipientId, deviceId);
SessionState sessionState = sessionRecord.getSessionState();
ChainKey chainKey = sessionState.getSenderChainKey();
MessageKeys messageKeys = chainKey.getMessageKeys();
@ -82,23 +106,34 @@ public class SessionCipher {
}
sessionState.setSenderChainKey(chainKey.getNextChainKey());
sessionStore.put(recipientId, deviceId, sessionRecord);
sessionStore.store(recipientId, deviceId, sessionRecord);
return ciphertextMessage;
}
}
public byte[] decrypt(byte[] decodedMessage)
/**
* Decrypt a message.
*
* @param ciphertext The ciphertext message bytes corresponding to a serialized
* {@link WhisperMessage}.
* @return The plaintext.
* @throws InvalidMessageException if the input is not valid ciphertext.
* @throws DuplicateMessageException if the input is a message that has already been received.
* @throws LegacyMessageException if the input is a message formatted by a protocol version that
* is no longer supported.
*/
public byte[] decrypt(byte[] ciphertext)
throws InvalidMessageException, DuplicateMessageException, LegacyMessageException
{
synchronized (SESSION_LOCK) {
SessionRecord sessionRecord = sessionStore.get(recipientId, deviceId);
SessionRecord sessionRecord = sessionStore.load(recipientId, deviceId);
SessionState sessionState = sessionRecord.getSessionState();
List<SessionState> previousStates = sessionRecord.getPreviousSessionStates();
List<Exception> exceptions = new LinkedList<>();
try {
byte[] plaintext = decrypt(sessionState, decodedMessage);
sessionStore.put(recipientId, deviceId, sessionRecord);
byte[] plaintext = decrypt(sessionState, ciphertext);
sessionStore.store(recipientId, deviceId, sessionRecord);
return plaintext;
} catch (InvalidMessageException e) {
@ -107,8 +142,8 @@ public class SessionCipher {
for (SessionState previousState : previousStates) {
try {
byte[] plaintext = decrypt(previousState, decodedMessage);
sessionStore.put(recipientId, deviceId, sessionRecord);
byte[] plaintext = decrypt(previousState, ciphertext);
sessionStore.store(recipientId, deviceId, sessionRecord);
return plaintext;
} catch (InvalidMessageException e) {
@ -120,7 +155,7 @@ public class SessionCipher {
}
}
public byte[] decrypt(SessionState sessionState, byte[] decodedMessage)
private byte[] decrypt(SessionState sessionState, byte[] decodedMessage)
throws InvalidMessageException, DuplicateMessageException, LegacyMessageException
{
if (!sessionState.hasSenderChain()) {
@ -146,7 +181,7 @@ public class SessionCipher {
public int getRemoteRegistrationId() {
synchronized (SESSION_LOCK) {
SessionRecord record = sessionStore.get(recipientId, deviceId);
SessionRecord record = sessionStore.load(recipientId, deviceId);
return record.getSessionState().getRemoteRegistrationId();
}
}

View File

@ -3,10 +3,38 @@ package org.whispersystems.libaxolotl.state;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.IdentityKeyPair;
/**
* Provides an interface to identity information.
*
* @author Moxie Marlinspike
*/
public interface IdentityKeyStore {
/**
* Get the local client's identity key pair.
*
* @return The local client's persistent identity key pair.
*/
public IdentityKeyPair getIdentityKeyPair();
/**
* Return the local client's registration ID.
* <p>
* Clients should maintain a registration ID, a random number
* between 1 and 16380 that's generated once at install time.
*
* @return the local client's registration ID.
*/
public int getLocalRegistrationId();
/**
* Save a remote client's identity key
* <p>
* Store a remote client's identity key as trusted.
*
* @param recipientId The recipient ID of the remote client.
* @param identityKey The remote client's identity key.
*/
public void saveIdentity(long recipientId, IdentityKey identityKey);
}

View File

@ -3,10 +3,34 @@ package org.whispersystems.libaxolotl.state;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
/**
* An interface that describes a remote PreKey.
*
* @author Moxie Marlinspike
*/
public interface PreKey {
/**
* @return the device ID this PreKey belongs to.
*/
public int getDeviceId();
/**
* @return the unique key ID for this PreKey.
*/
public int getKeyId();
/**
* @return the public key for this PreKey.
*/
public ECPublicKey getPublicKey();
/**
* @return the {@link org.whispersystems.libaxolotl.IdentityKey} of this PreKeys owner.
*/
public IdentityKey getIdentityKey();
/**
* @return the registration ID associated with this PreKey.
*/
public int getRegistrationId();
}

View File

@ -2,8 +2,24 @@ package org.whispersystems.libaxolotl.state;
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
/**
* An interface describing a locally stored PreKey.
*
* @author Moxie Marlinspike
*/
public interface PreKeyRecord {
/**
* @return the PreKey's ID.
*/
public int getId();
/**
* @return the PreKey's key pair.
*/
public ECKeyPair getKeyPair();
/**
* @return a serialized version of this PreKey.
*/
public byte[] serialize();
}

View File

@ -2,11 +2,41 @@ package org.whispersystems.libaxolotl.state;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
/**
* An interface describing the local storage of {@link PreKeyRecord}s.
*
* @author Moxie Marlinspike
*/
public interface PreKeyStore {
/**
* Load a local PreKeyRecord.
*
* @param preKeyId the ID of the local PreKeyRecord.
* @return the corresponding PreKeyRecord.
* @throws InvalidKeyIdException when there is no corresponding PreKeyRecord.
*/
public PreKeyRecord load(int preKeyId) throws InvalidKeyIdException;
/**
* Store a local PreKeyRecord.
*
* @param preKeyId the ID of the PreKeyRecord to store.
* @param record the PreKeyRecord.
*/
public void store(int preKeyId, PreKeyRecord record);
/**
* @param preKeyId A PreKeyRecord ID.
* @return true if the store has a record for the preKeyId, otherwise false.
*/
public boolean contains(int preKeyId);
/**
* Delete a PreKeyRecord from local storage.
*
* @param preKeyId The ID of the PreKeyRecord to remove.
*/
public void remove(int preKeyId);
}

View File

@ -2,12 +2,44 @@ package org.whispersystems.libaxolotl.state;
import java.util.List;
/**
* A SessionRecord encapsulates the state of an ongoing session.
* <p>
* It contains the current {@link org.whispersystems.libaxolotl.state.SessionState},
* in addition to previous {@link SessionState}s for the same recipient, which need
* to be maintained in some situations.
*
* @author Moxie Marlinspike
*/
public interface SessionRecord {
/**
* @return the current {@link org.whispersystems.libaxolotl.state.SessionState}
*/
public SessionState getSessionState();
/**
* @return the list of all currently maintained "previous" session states.
*/
public List<SessionState> getPreviousSessionStates();
/**
* Reset the current SessionRecord, clearing all "previous" session states,
* and resetting the current {@link org.whispersystems.libaxolotl.state.SessionState}
* to a fresh state.
*/
public void reset();
/**
* Move the current {@link SessionState} into the list of "previous" session states,
* and replace the current {@link org.whispersystems.libaxolotl.state.SessionState}
* with a fresh reset instance.
*/
public void archiveCurrentState();
/**
* @return a serialized version of the current SessionRecord.
*/
public byte[] serialize();
}

View File

@ -10,6 +10,11 @@ import org.whispersystems.libaxolotl.ratchet.MessageKeys;
import org.whispersystems.libaxolotl.ratchet.RootKey;
import org.whispersystems.libaxolotl.util.Pair;
/**
* The current session state.
*
* @author Moxie Marlinspike
*/
public interface SessionState {
public void setNeedsRefresh(boolean needsRefresh);
public boolean getNeedsRefresh();

View File

@ -2,13 +2,67 @@ package org.whispersystems.libaxolotl.state;
import java.util.List;
/**
* The interface to the durable store of session state information
* for remote clients.
*
* @author Moxie Marlinspike
*/
public interface SessionStore {
public SessionRecord get(long recipientId, int deviceId);
/**
* Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple,
* or a new SessionRecord if one does not currently exist.
* <p>
* It is important that implementations return a copy of the current durable information. The
* returned SessionRecord may be modified, but those changes should not have an effect on the
* durable session state (what is returned by subsequent calls to this method) without the
* store method being called here first.
*
* @param recipientId The recipientID of the remote client.
* @param deviceId The deviceID of the remote client.
* @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or
* a new SessionRecord if one does not currently exist.
*/
public SessionRecord load(long recipientId, int deviceId);
/**
* Returns all known devices with active sessions for a recipient
*
* @param recipientId the recipient ID.
* @return all known sub-devices with active sessions.
*/
public List<Integer> getSubDeviceSessions(long recipientId);
public void put(long recipientId, int deviceId, SessionRecord record);
/**
* Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple.
* @param recipientId the recipient ID of the remote client.
* @param deviceId the device ID of the remote client.
* @param record the current SessionRecord for the remote client.
*/
public void store(long recipientId, int deviceId, SessionRecord record);
/**
* Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple.
* @param recipientId the recipient ID of the remote client.
* @param deviceId the device ID of the remote client.
* @return true if a {@link SessionRecord} exists, false otherwise.
*/
public boolean contains(long recipientId, int deviceId);
/**
* Remove a {@link SessionRecord} for a recipientId + deviceId tuple.
*
* @param recipientId the recipient ID of the remote client.
* @param deviceId the device ID of the remote client.
*/
public void delete(long recipientId, int deviceId);
/**
* Remove the {@link SessionRecord}s corresponding to all devices of a recipientId.
*
* @param recipientId the recipient ID of the remote client.
*/
public void deleteAll(long recipientId);
}

View File

@ -3,7 +3,7 @@ package org.whispersystems.libaxolotl.util;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class Helper {
public class KeyHelper {
public static int getRandomSequence(int max) {
try {

View File

@ -26,7 +26,7 @@ public class SessionUtil {
return
sessionStore.contains(recipientId, deviceId) &&
!sessionStore.get(recipientId, deviceId).getSessionState().getNeedsRefresh();
!sessionStore.load(recipientId, deviceId).getSessionState().getNeedsRefresh();
}
}

View File

@ -32,7 +32,7 @@ public class TextSecureSessionStore implements SessionStore {
}
@Override
public SessionRecord get(long recipientId, int deviceId) {
public SessionRecord load(long recipientId, int deviceId) {
synchronized (FILE_LOCK) {
try {
FileInputStream input = new FileInputStream(getSessionFile(recipientId, deviceId));
@ -45,7 +45,7 @@ public class TextSecureSessionStore implements SessionStore {
}
@Override
public void put(long recipientId, int deviceId, SessionRecord record) {
public void store(long recipientId, int deviceId, SessionRecord record) {
try {
RandomAccessFile sessionFile = new RandomAccessFile(getSessionFile(recipientId, deviceId), "rw");
FileChannel out = sessionFile.getChannel();
@ -63,7 +63,7 @@ public class TextSecureSessionStore implements SessionStore {
@Override
public boolean contains(long recipientId, int deviceId) {
return getSessionFile(recipientId, deviceId).exists() &&
get(recipientId, deviceId).getSessionState().hasSenderChain();
load(recipientId, deviceId).getSessionState().hasSenderChain();
}
@Override

View File

@ -183,8 +183,8 @@ public class VerifyIdentityActivity extends KeyScanningActivity {
private IdentityKey getRemoteIdentityKey(MasterSecret masterSecret, Recipient recipient) {
SessionStore sessionStore = new TextSecureSessionStore(this, masterSecret);
SessionRecord record = sessionStore.get(recipient.getRecipientId(),
RecipientDevice.DEFAULT_DEVICE_ID);
SessionRecord record = sessionStore.load(recipient.getRecipientId(),
RecipientDevice.DEFAULT_DEVICE_ID);
if (record == null) {
return null;

View File

@ -79,7 +79,7 @@ public class KeyExchangeInitiator {
Recipient recipient)
{
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret);
SessionRecord sessionRecord = sessionStore.get(recipient.getRecipientId(), RecipientDevice.DEFAULT_DEVICE_ID);
SessionRecord sessionRecord = sessionStore.load(recipient.getRecipientId(), RecipientDevice.DEFAULT_DEVICE_ID);
return sessionRecord.getSessionState().hasPendingPreKey();
}

View File

@ -77,8 +77,8 @@ public class KeyExchangeProcessor {
}
public boolean isStale(KeyExchangeMessage message) {
SessionRecord sessionRecord = sessionStore.get(recipientDevice.getRecipientId(),
recipientDevice.getDeviceId());
SessionRecord sessionRecord = sessionStore.load(recipientDevice.getRecipientId(),
recipientDevice.getDeviceId());
return
message.isResponse() &&