mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-28 08:49:17 +00:00
Rearrange decrypt API.
1) Change SessionBuilder to only establish sessions via KeyExchangeMessage and PreKeyBundles. 2) Change SessionCipher to decrypt either WhisperMessage or PreKeyWhisperMessage items, automatically building a session for the latter. 3) Change SessionCipher to tear down new sessions built with PreKeyWhisperMessages if the embedded WhsiperMessage fails to decrypt.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
package org.whispersystems.libaxolotl;
|
||||
|
||||
public class NoSessionException extends Exception {
|
||||
public NoSessionException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -89,31 +89,35 @@ public class SessionBuilder {
|
||||
* @throws org.whispersystems.libaxolotl.InvalidKeyException when the message is formatted incorrectly.
|
||||
* @throws org.whispersystems.libaxolotl.UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted.
|
||||
*/
|
||||
public void process(PreKeyWhisperMessage message)
|
||||
/*package*/ boolean process(PreKeyWhisperMessage message)
|
||||
throws InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException
|
||||
{
|
||||
int messageVersion = message.getMessageVersion();
|
||||
IdentityKey theirIdentityKey = message.getIdentityKey();
|
||||
|
||||
boolean createdSession;
|
||||
|
||||
if (!identityKeyStore.isTrustedIdentity(recipientId, theirIdentityKey)) {
|
||||
throw new UntrustedIdentityException();
|
||||
}
|
||||
|
||||
if (messageVersion == 2) processV2(message);
|
||||
else if (messageVersion == 3) processV3(message);
|
||||
if (messageVersion == 2) createdSession = processV2(message);
|
||||
else if (messageVersion == 3) createdSession = processV3(message);
|
||||
else throw new AssertionError("Unknown version: " + messageVersion);
|
||||
|
||||
identityKeyStore.saveIdentity(recipientId, theirIdentityKey);
|
||||
|
||||
return createdSession;
|
||||
}
|
||||
|
||||
private void processV3(PreKeyWhisperMessage message)
|
||||
private boolean processV3(PreKeyWhisperMessage message)
|
||||
throws UntrustedIdentityException, InvalidKeyIdException, InvalidKeyException
|
||||
{
|
||||
SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId);
|
||||
|
||||
if (sessionRecord.hasSessionState(message.getMessageVersion(), message.getBaseKey().serialize())) {
|
||||
Log.w(TAG, "We've already setup a session for this V3 message, letting bundled message fall through...");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean simultaneousInitiate = sessionRecord.getSessionState().hasUnacknowledgedPreKeyMessage();
|
||||
@@ -152,9 +156,11 @@ public class SessionBuilder {
|
||||
if (message.getPreKeyId() >= 0 && message.getPreKeyId() != Medium.MAX_VALUE) {
|
||||
preKeyStore.removePreKey(message.getPreKeyId());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void processV2(PreKeyWhisperMessage message)
|
||||
private boolean processV2(PreKeyWhisperMessage message)
|
||||
throws UntrustedIdentityException, InvalidKeyIdException, InvalidKeyException
|
||||
{
|
||||
|
||||
@@ -162,7 +168,7 @@ public class SessionBuilder {
|
||||
sessionStore.containsSession(recipientId, deviceId))
|
||||
{
|
||||
Log.w(TAG, "We've already processed the prekey part of this V2 session, letting bundled message fall through...");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId);
|
||||
@@ -194,6 +200,8 @@ public class SessionBuilder {
|
||||
if (message.getPreKeyId() != Medium.MAX_VALUE) {
|
||||
preKeyStore.removePreKey(message.getPreKeyId());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,9 +25,12 @@ import org.whispersystems.libaxolotl.protocol.WhisperMessage;
|
||||
import org.whispersystems.libaxolotl.ratchet.ChainKey;
|
||||
import org.whispersystems.libaxolotl.ratchet.MessageKeys;
|
||||
import org.whispersystems.libaxolotl.ratchet.RootKey;
|
||||
import org.whispersystems.libaxolotl.state.IdentityKeyStore;
|
||||
import org.whispersystems.libaxolotl.state.PreKeyStore;
|
||||
import org.whispersystems.libaxolotl.state.SessionRecord;
|
||||
import org.whispersystems.libaxolotl.state.SessionState;
|
||||
import org.whispersystems.libaxolotl.state.SessionStore;
|
||||
import org.whispersystems.libaxolotl.state.SignedPreKeyStore;
|
||||
import org.whispersystems.libaxolotl.util.ByteUtil;
|
||||
import org.whispersystems.libaxolotl.util.Pair;
|
||||
|
||||
@@ -58,9 +61,10 @@ public class SessionCipher {
|
||||
|
||||
private static final Object SESSION_LOCK = new Object();
|
||||
|
||||
private final SessionStore sessionStore;
|
||||
private final long recipientId;
|
||||
private final int deviceId;
|
||||
private final SessionStore sessionStore;
|
||||
private final SessionBuilder sessionBuilder;
|
||||
private final long recipientId;
|
||||
private final int deviceId;
|
||||
|
||||
/**
|
||||
* Construct a SessionCipher for encrypt/decrypt operations on a session.
|
||||
@@ -71,10 +75,15 @@ public class SessionCipher {
|
||||
* @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;
|
||||
public SessionCipher(SessionStore sessionStore, PreKeyStore preKeyStore,
|
||||
SignedPreKeyStore signedPreKeyStore, IdentityKeyStore identityKeyStore,
|
||||
long recipientId, int deviceId)
|
||||
{
|
||||
this.sessionStore = sessionStore;
|
||||
this.recipientId = recipientId;
|
||||
this.deviceId = deviceId;
|
||||
this.sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore,
|
||||
identityKeyStore, recipientId, deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,6 +124,26 @@ public class SessionCipher {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] decrypt(PreKeyWhisperMessage ciphertext)
|
||||
throws DuplicateMessageException, LegacyMessageException, InvalidMessageException,
|
||||
InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException, NoSessionException
|
||||
{
|
||||
synchronized (SESSION_LOCK) {
|
||||
boolean sessionCreated = sessionBuilder.process(ciphertext);
|
||||
|
||||
try {
|
||||
return decrypt(ciphertext.getWhisperMessage());
|
||||
} catch (InvalidMessageException | DuplicateMessageException | LegacyMessageException e) {
|
||||
if (sessionCreated) {
|
||||
sessionStore.deleteSession(recipientId, deviceId);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decrypt a message.
|
||||
*
|
||||
@@ -126,10 +155,15 @@ public class SessionCipher {
|
||||
* @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
|
||||
public byte[] decrypt(WhisperMessage ciphertext)
|
||||
throws InvalidMessageException, DuplicateMessageException, LegacyMessageException, NoSessionException
|
||||
{
|
||||
synchronized (SESSION_LOCK) {
|
||||
|
||||
if (!sessionStore.containsSession(recipientId, deviceId)) {
|
||||
throw new NoSessionException("No session for: " + recipientId + ", " + deviceId);
|
||||
}
|
||||
|
||||
SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId);
|
||||
SessionState sessionState = sessionRecord.getSessionState();
|
||||
List<SessionState> previousStates = sessionRecord.getPreviousSessionStates();
|
||||
@@ -159,15 +193,13 @@ public class SessionCipher {
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] decrypt(SessionState sessionState, byte[] decodedMessage)
|
||||
private byte[] decrypt(SessionState sessionState, WhisperMessage ciphertextMessage)
|
||||
throws InvalidMessageException, DuplicateMessageException, LegacyMessageException
|
||||
{
|
||||
if (!sessionState.hasSenderChain()) {
|
||||
throw new InvalidMessageException("Uninitialized session!");
|
||||
}
|
||||
|
||||
WhisperMessage ciphertextMessage = new WhisperMessage(decodedMessage);
|
||||
|
||||
if (ciphertextMessage.getMessageVersion() != sessionState.getSessionVersion()) {
|
||||
throw new InvalidMessageException(String.format("Message version %d, but session version %d",
|
||||
ciphertextMessage.getMessageVersion(),
|
||||
|
||||
Reference in New Issue
Block a user