aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java')
-rw-r--r--src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java411
1 files changed, 0 insertions, 411 deletions
diff --git a/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java b/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java
deleted file mode 100644
index 2d1c8969..00000000
--- a/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java
+++ /dev/null
@@ -1,411 +0,0 @@
-package org.whispersystems.libaxolotl;
-
-import android.util.Log;
-
-import org.whispersystems.libaxolotl.ecc.Curve;
-import org.whispersystems.libaxolotl.ecc.ECKeyPair;
-import org.whispersystems.libaxolotl.ecc.ECPublicKey;
-import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
-import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
-import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
-import org.whispersystems.libaxolotl.ratchet.AliceAxolotlParameters;
-import org.whispersystems.libaxolotl.ratchet.BobAxolotlParameters;
-import org.whispersystems.libaxolotl.ratchet.RatchetingSession;
-import org.whispersystems.libaxolotl.ratchet.SymmetricAxolotlParameters;
-import org.whispersystems.libaxolotl.state.AxolotlStore;
-import org.whispersystems.libaxolotl.state.IdentityKeyStore;
-import org.whispersystems.libaxolotl.state.PreKeyBundle;
-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.KeyHelper;
-import org.whispersystems.libaxolotl.util.Medium;
-import org.whispersystems.libaxolotl.util.guava.Optional;
-
-/**
- * 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.PreKeyBundle} 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();
-
- private final SessionStore sessionStore;
- private final PreKeyStore preKeyStore;
- private final SignedPreKeyStore signedPreKeyStore;
- private final IdentityKeyStore identityKeyStore;
- 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,
- SignedPreKeyStore signedPreKeyStore,
- IdentityKeyStore identityKeyStore,
- long recipientId, int deviceId)
- {
- this.sessionStore = sessionStore;
- this.preKeyStore = preKeyStore;
- this.signedPreKeyStore = signedPreKeyStore;
- this.identityKeyStore = identityKeyStore;
- this.recipientId = recipientId;
- this.deviceId = deviceId;
- }
-
- /**
- * Constructs a SessionBuilder
- * @param store The {@link org.whispersystems.libaxolotl.state.AxolotlStore} to store all state information in.
- * @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(AxolotlStore store, long recipientId, int deviceId) {
- this(store, store, store, store, recipientId, 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.
- * @throws org.whispersystems.libaxolotl.UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted.
- */
- /*package*/ Optional<Integer> process(SessionRecord sessionRecord, PreKeyWhisperMessage message)
- throws InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException
- {
- int messageVersion = message.getMessageVersion();
- IdentityKey theirIdentityKey = message.getIdentityKey();
-
- Optional<Integer> unsignedPreKeyId;
-
- if (!identityKeyStore.isTrustedIdentity(recipientId, theirIdentityKey)) {
- throw new UntrustedIdentityException();
- }
-
- switch (messageVersion) {
- case 2: unsignedPreKeyId = processV2(sessionRecord, message); break;
- case 3: unsignedPreKeyId = processV3(sessionRecord, message); break;
- default: throw new AssertionError("Unknown version: " + messageVersion);
- }
-
- identityKeyStore.saveIdentity(recipientId, theirIdentityKey);
- return unsignedPreKeyId;
- }
-
- private Optional<Integer> processV3(SessionRecord sessionRecord, PreKeyWhisperMessage message)
- throws UntrustedIdentityException, InvalidKeyIdException, InvalidKeyException
- {
-
- 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 Optional.absent();
- }
-
- ECKeyPair ourSignedPreKey = signedPreKeyStore.loadSignedPreKey(message.getSignedPreKeyId()).getKeyPair();
-
- BobAxolotlParameters.Builder parameters = BobAxolotlParameters.newBuilder();
-
- parameters.setTheirBaseKey(message.getBaseKey())
- .setTheirIdentityKey(message.getIdentityKey())
- .setOurIdentityKey(identityKeyStore.getIdentityKeyPair())
- .setOurSignedPreKey(ourSignedPreKey)
- .setOurRatchetKey(ourSignedPreKey);
-
- if (message.getPreKeyId().isPresent()) {
- parameters.setOurOneTimePreKey(Optional.of(preKeyStore.loadPreKey(message.getPreKeyId().get()).getKeyPair()));
- } else {
- parameters.setOurOneTimePreKey(Optional.<ECKeyPair>absent());
- }
-
- if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState();
-
- RatchetingSession.initializeSession(sessionRecord.getSessionState(), message.getMessageVersion(), parameters.create());
-
- sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId());
- sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId());
- sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize());
-
- if (message.getPreKeyId().isPresent() && message.getPreKeyId().get() != Medium.MAX_VALUE) {
- return message.getPreKeyId();
- } else {
- return Optional.absent();
- }
- }
-
- private Optional<Integer> processV2(SessionRecord sessionRecord, PreKeyWhisperMessage message)
- throws UntrustedIdentityException, InvalidKeyIdException, InvalidKeyException
- {
- if (!message.getPreKeyId().isPresent()) {
- throw new InvalidKeyIdException("V2 message requires one time prekey id!");
- }
-
- if (!preKeyStore.containsPreKey(message.getPreKeyId().get()) &&
- sessionStore.containsSession(recipientId, deviceId))
- {
- Log.w(TAG, "We've already processed the prekey part of this V2 session, letting bundled message fall through...");
- return Optional.absent();
- }
-
- ECKeyPair ourPreKey = preKeyStore.loadPreKey(message.getPreKeyId().get()).getKeyPair();
-
- BobAxolotlParameters.Builder parameters = BobAxolotlParameters.newBuilder();
-
- parameters.setOurIdentityKey(identityKeyStore.getIdentityKeyPair())
- .setOurSignedPreKey(ourPreKey)
- .setOurRatchetKey(ourPreKey)
- .setOurOneTimePreKey(Optional.<ECKeyPair>absent())
- .setTheirIdentityKey(message.getIdentityKey())
- .setTheirBaseKey(message.getBaseKey());
-
- if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState();
-
- RatchetingSession.initializeSession(sessionRecord.getSessionState(), message.getMessageVersion(), parameters.create());
-
- sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId());
- sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId());
- sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize());
-
- if (message.getPreKeyId().get() != Medium.MAX_VALUE) {
- return message.getPreKeyId();
- } else {
- return Optional.absent();
- }
- }
-
- /**
- * Build a new session from a {@link org.whispersystems.libaxolotl.state.PreKeyBundle} 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.PreKeyBundle} is
- * badly formatted.
- * @throws org.whispersystems.libaxolotl.UntrustedIdentityException when the sender's
- * {@link IdentityKey} is not
- * trusted.
- */
- public void process(PreKeyBundle preKey) throws InvalidKeyException, UntrustedIdentityException {
- synchronized (SessionCipher.SESSION_LOCK) {
- if (!identityKeyStore.isTrustedIdentity(recipientId, preKey.getIdentityKey())) {
- throw new UntrustedIdentityException();
- }
-
- if (preKey.getSignedPreKey() != null &&
- !Curve.verifySignature(preKey.getIdentityKey().getPublicKey(),
- preKey.getSignedPreKey().serialize(),
- preKey.getSignedPreKeySignature()))
- {
- throw new InvalidKeyException("Invalid signature on device key!");
- }
-
- if (preKey.getSignedPreKey() == null && preKey.getPreKey() == null) {
- throw new InvalidKeyException("Both signed and unsigned prekeys are absent!");
- }
-
- boolean supportsV3 = preKey.getSignedPreKey() != null;
- SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId);
- ECKeyPair ourBaseKey = Curve.generateKeyPair();
- ECPublicKey theirSignedPreKey = supportsV3 ? preKey.getSignedPreKey() : preKey.getPreKey();
- Optional<ECPublicKey> theirOneTimePreKey = Optional.fromNullable(preKey.getPreKey());
- Optional<Integer> theirOneTimePreKeyId = theirOneTimePreKey.isPresent() ? Optional.of(preKey.getPreKeyId()) :
- Optional.<Integer>absent();
-
- AliceAxolotlParameters.Builder parameters = AliceAxolotlParameters.newBuilder();
-
- parameters.setOurBaseKey(ourBaseKey)
- .setOurIdentityKey(identityKeyStore.getIdentityKeyPair())
- .setTheirIdentityKey(preKey.getIdentityKey())
- .setTheirSignedPreKey(theirSignedPreKey)
- .setTheirRatchetKey(theirSignedPreKey)
- .setTheirOneTimePreKey(supportsV3 ? theirOneTimePreKey : Optional.<ECPublicKey>absent());
-
- if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState();
-
- RatchetingSession.initializeSession(sessionRecord.getSessionState(),
- supportsV3 ? 3 : 2,
- parameters.create());
-
- sessionRecord.getSessionState().setUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.getSignedPreKeyId(), ourBaseKey.getPublicKey());
- sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId());
- sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId());
- sessionRecord.getSessionState().setAliceBaseKey(ourBaseKey.getPublicKey().serialize());
-
- sessionStore.storeSession(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, UntrustedIdentityException, StaleKeyExchangeException
- {
- synchronized (SessionCipher.SESSION_LOCK) {
- if (!identityKeyStore.isTrustedIdentity(recipientId, message.getIdentityKey())) {
- throw new UntrustedIdentityException();
- }
-
- KeyExchangeMessage responseMessage = null;
-
- if (message.isInitiate()) responseMessage = processInitiate(message);
- else processResponse(message);
-
- return responseMessage;
- }
- }
-
- private KeyExchangeMessage processInitiate(KeyExchangeMessage message) throws InvalidKeyException {
- int flags = KeyExchangeMessage.RESPONSE_FLAG;
- SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId);
-
- if (message.getVersion() >= 3 &&
- !Curve.verifySignature(message.getIdentityKey().getPublicKey(),
- message.getBaseKey().serialize(),
- message.getBaseKeySignature()))
- {
- throw new InvalidKeyException("Bad signature!");
- }
-
- SymmetricAxolotlParameters.Builder builder = SymmetricAxolotlParameters.newBuilder();
-
- if (!sessionRecord.getSessionState().hasPendingKeyExchange()) {
- builder.setOurIdentityKey(identityKeyStore.getIdentityKeyPair())
- .setOurBaseKey(Curve.generateKeyPair())
- .setOurRatchetKey(Curve.generateKeyPair());
- } else {
- builder.setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey())
- .setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey())
- .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey());
- flags |= KeyExchangeMessage.SIMULTAENOUS_INITIATE_FLAG;
- }
-
- builder.setTheirBaseKey(message.getBaseKey())
- .setTheirRatchetKey(message.getRatchetKey())
- .setTheirIdentityKey(message.getIdentityKey());
-
- SymmetricAxolotlParameters parameters = builder.create();
-
- if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState();
-
- RatchetingSession.initializeSession(sessionRecord.getSessionState(),
- Math.min(message.getMaxVersion(), CiphertextMessage.CURRENT_VERSION),
- parameters);
-
- sessionStore.storeSession(recipientId, deviceId, sessionRecord);
- identityKeyStore.saveIdentity(recipientId, message.getIdentityKey());
-
- byte[] baseKeySignature = Curve.calculateSignature(parameters.getOurIdentityKey().getPrivateKey(),
- parameters.getOurBaseKey().getPublicKey().serialize());
-
- return new KeyExchangeMessage(sessionRecord.getSessionState().getSessionVersion(),
- message.getSequence(), flags,
- parameters.getOurBaseKey().getPublicKey(),
- baseKeySignature, parameters.getOurRatchetKey().getPublicKey(),
- parameters.getOurIdentityKey().getPublicKey());
- }
-
- private void processResponse(KeyExchangeMessage message)
- throws StaleKeyExchangeException, InvalidKeyException
- {
- SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId);
- SessionState sessionState = sessionRecord.getSessionState();
- boolean hasPendingKeyExchange = sessionState.hasPendingKeyExchange();
- boolean isSimultaneousInitiateResponse = message.isResponseForSimultaneousInitiate();
-
- if (!hasPendingKeyExchange || sessionState.getPendingKeyExchangeSequence() != message.getSequence()) {
- Log.w(TAG, "No matching sequence for response. Is simultaneous initiate response: " + isSimultaneousInitiateResponse);
- if (!isSimultaneousInitiateResponse) throw new StaleKeyExchangeException();
- else return;
- }
-
- SymmetricAxolotlParameters.Builder parameters = SymmetricAxolotlParameters.newBuilder();
-
- parameters.setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey())
- .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey())
- .setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey())
- .setTheirBaseKey(message.getBaseKey())
- .setTheirRatchetKey(message.getRatchetKey())
- .setTheirIdentityKey(message.getIdentityKey());
-
- if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState();
-
- RatchetingSession.initializeSession(sessionRecord.getSessionState(),
- Math.min(message.getMaxVersion(), CiphertextMessage.CURRENT_VERSION),
- parameters.create());
-
- if (sessionRecord.getSessionState().getSessionVersion() >= 3 &&
- !Curve.verifySignature(message.getIdentityKey().getPublicKey(),
- message.getBaseKey().serialize(),
- message.getBaseKeySignature()))
- {
- throw new InvalidKeyException("Base key signature doesn't match!");
- }
-
- sessionStore.storeSession(recipientId, deviceId, sessionRecord);
- identityKeyStore.saveIdentity(recipientId, message.getIdentityKey());
-
- }
-
- /**
- * 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(recipientId, deviceId);
-
- sessionRecord.getSessionState().setPendingKeyExchange(sequence, baseKey, ratchetKey, identityKey);
- sessionStore.storeSession(recipientId, deviceId, sessionRecord);
-
- return new KeyExchangeMessage(2, sequence, flags, baseKey.getPublicKey(), baseKeySignature,
- ratchetKey.getPublicKey(), identityKey.getPublicKey());
- } catch (InvalidKeyException e) {
- throw new AssertionError(e);
- }
- }
- }
-
-
-}