--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/src/main/java/net/java/otr4j/session/SessionImpl.java Sun Mar 15 18:03:03 2015 +0100
@@ -0,0 +1,792 @@
+/*
+ * otr4j, the open source java otr library.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.otr4j.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+import java.util.logging.Logger;
+import javax.crypto.interfaces.DHPublicKey;
+
+import net.java.otr4j.OtrEngineHost;
+import net.java.otr4j.OtrEngineListener;
+import net.java.otr4j.OtrException;
+import net.java.otr4j.OtrPolicy;
+import net.java.otr4j.crypto.OtrCryptoEngine;
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.io.OtrInputStream;
+import net.java.otr4j.io.OtrOutputStream;
+import net.java.otr4j.io.SerializationConstants;
+import net.java.otr4j.io.SerializationUtils;
+import net.java.otr4j.io.messages.DataMessage;
+import net.java.otr4j.io.messages.AbstractEncodedMessage;
+import net.java.otr4j.io.messages.ErrorMessage;
+import net.java.otr4j.io.messages.AbstractMessage;
+import net.java.otr4j.io.messages.MysteriousT;
+import net.java.otr4j.io.messages.PlainTextMessage;
+import net.java.otr4j.io.messages.QueryMessage;
+
+/**
+ *
+ * @author George Politis
+ */
+public class SessionImpl implements Session {
+
+ class TLV {
+ public TLV(int type, byte[] value) {
+ this.setType(type);
+ this.setValue(value);
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setValue(byte[] value) {
+ this.value = value;
+ }
+
+ public byte[] getValue() {
+ return value;
+ }
+
+ private int type;
+ private byte[] value;
+ }
+
+ private SessionID sessionID;
+ private OtrEngineHost host;
+ private SessionStatus sessionStatus;
+ private AuthContext authContext;
+ private SessionKeys[][] sessionKeys;
+ private Vector<byte[]> oldMacKeys;
+ private static Logger logger = Logger
+ .getLogger(SessionImpl.class.getName());
+
+ public SessionImpl(SessionID sessionID, OtrEngineHost listener) {
+
+ this.setSessionID(sessionID);
+ this.setHost(listener);
+
+ // client application calls OtrEngine.getSessionStatus()
+ // -> create new session if it does not exist, end up here
+ // -> setSessionStatus() fires statusChangedEvent
+ // -> client application calls OtrEngine.getSessionStatus()
+ this.sessionStatus = SessionStatus.PLAINTEXT;
+ }
+
+ private SessionKeys getEncryptionSessionKeys() {
+ logger.finest("Getting encryption keys");
+ return getSessionKeysByIndex(SessionKeys.Previous, SessionKeys.Current);
+ }
+
+ private SessionKeys getMostRecentSessionKeys() {
+ logger.finest("Getting most recent keys.");
+ return getSessionKeysByIndex(SessionKeys.Current, SessionKeys.Current);
+ }
+
+ private SessionKeys getSessionKeysByID(int localKeyID, int remoteKeyID) {
+ logger
+ .finest("Searching for session keys with (localKeyID, remoteKeyID) = ("
+ + localKeyID + "," + remoteKeyID + ")");
+
+ for (int i = 0; i < getSessionKeys().length; i++) {
+ for (int j = 0; j < getSessionKeys()[i].length; j++) {
+ SessionKeys current = getSessionKeysByIndex(i, j);
+ if (current.getLocalKeyID() == localKeyID
+ && current.getRemoteKeyID() == remoteKeyID) {
+ logger.finest("Matching keys found.");
+ return current;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private SessionKeys getSessionKeysByIndex(int localKeyIndex,
+ int remoteKeyIndex) {
+ if (getSessionKeys()[localKeyIndex][remoteKeyIndex] == null)
+ getSessionKeys()[localKeyIndex][remoteKeyIndex] = new SessionKeysImpl(
+ localKeyIndex, remoteKeyIndex);
+
+ return getSessionKeys()[localKeyIndex][remoteKeyIndex];
+ }
+
+ private void rotateRemoteSessionKeys(DHPublicKey pubKey)
+ throws OtrException {
+
+ logger.finest("Rotating remote keys.");
+ SessionKeys sess1 = getSessionKeysByIndex(SessionKeys.Current,
+ SessionKeys.Previous);
+ if (sess1.getIsUsedReceivingMACKey()) {
+ logger
+ .finest("Detected used Receiving MAC key. Adding to old MAC keys to reveal it.");
+ getOldMacKeys().add(sess1.getReceivingMACKey());
+ }
+
+ SessionKeys sess2 = getSessionKeysByIndex(SessionKeys.Previous,
+ SessionKeys.Previous);
+ if (sess2.getIsUsedReceivingMACKey()) {
+ logger
+ .finest("Detected used Receiving MAC key. Adding to old MAC keys to reveal it.");
+ getOldMacKeys().add(sess2.getReceivingMACKey());
+ }
+
+ SessionKeys sess3 = getSessionKeysByIndex(SessionKeys.Current,
+ SessionKeys.Current);
+ sess1
+ .setRemoteDHPublicKey(sess3.getRemoteKey(), sess3
+ .getRemoteKeyID());
+
+ SessionKeys sess4 = getSessionKeysByIndex(SessionKeys.Previous,
+ SessionKeys.Current);
+ sess2
+ .setRemoteDHPublicKey(sess4.getRemoteKey(), sess4
+ .getRemoteKeyID());
+
+ sess3.setRemoteDHPublicKey(pubKey, sess3.getRemoteKeyID() + 1);
+ sess4.setRemoteDHPublicKey(pubKey, sess4.getRemoteKeyID() + 1);
+ }
+
+ private void rotateLocalSessionKeys() throws OtrException {
+
+ logger.finest("Rotating local keys.");
+ SessionKeys sess1 = getSessionKeysByIndex(SessionKeys.Previous,
+ SessionKeys.Current);
+ if (sess1.getIsUsedReceivingMACKey()) {
+ logger
+ .finest("Detected used Receiving MAC key. Adding to old MAC keys to reveal it.");
+ getOldMacKeys().add(sess1.getReceivingMACKey());
+ }
+
+ SessionKeys sess2 = getSessionKeysByIndex(SessionKeys.Previous,
+ SessionKeys.Previous);
+ if (sess2.getIsUsedReceivingMACKey()) {
+ logger
+ .finest("Detected used Receiving MAC key. Adding to old MAC keys to reveal it.");
+ getOldMacKeys().add(sess2.getReceivingMACKey());
+ }
+
+ SessionKeys sess3 = getSessionKeysByIndex(SessionKeys.Current,
+ SessionKeys.Current);
+ sess1.setLocalPair(sess3.getLocalPair(), sess3.getLocalKeyID());
+ SessionKeys sess4 = getSessionKeysByIndex(SessionKeys.Current,
+ SessionKeys.Previous);
+ sess2.setLocalPair(sess4.getLocalPair(), sess4.getLocalKeyID());
+
+ KeyPair newPair = new OtrCryptoEngineImpl().generateDHKeyPair();
+ sess3.setLocalPair(newPair, sess3.getLocalKeyID() + 1);
+ sess4.setLocalPair(newPair, sess4.getLocalKeyID() + 1);
+ }
+
+ private byte[] collectOldMacKeys() {
+ logger.finest("Collecting old MAC keys to be revealed.");
+ int len = 0;
+ for (int i = 0; i < getOldMacKeys().size(); i++)
+ len += getOldMacKeys().get(i).length;
+
+ ByteBuffer buff = ByteBuffer.allocate(len);
+ for (int i = 0; i < getOldMacKeys().size(); i++)
+ buff.put(getOldMacKeys().get(i));
+
+ getOldMacKeys().clear();
+ return buff.array();
+ }
+
+ private void setSessionStatus(SessionStatus sessionStatus)
+ throws OtrException {
+
+ if (sessionStatus == this.sessionStatus)
+ return;
+
+ switch (sessionStatus) {
+ case ENCRYPTED:
+ AuthContext auth = this.getAuthContext();
+ logger.finest("Setting most recent session keys from auth.");
+ for (int i = 0; i < this.getSessionKeys()[0].length; i++) {
+ SessionKeys current = getSessionKeysByIndex(0, i);
+ current.setLocalPair(auth.getLocalDHKeyPair(), 1);
+ current.setRemoteDHPublicKey(auth.getRemoteDHPublicKey(), 1);
+ current.setS(auth.getS());
+ }
+
+ KeyPair nextDH = new OtrCryptoEngineImpl().generateDHKeyPair();
+ for (int i = 0; i < this.getSessionKeys()[1].length; i++) {
+ SessionKeys current = getSessionKeysByIndex(1, i);
+ current.setRemoteDHPublicKey(auth.getRemoteDHPublicKey(), 1);
+ current.setLocalPair(nextDH, 2);
+ }
+
+ this.setRemotePublicKey(auth.getRemoteLongTermPublicKey());
+
+ auth.reset();
+ break;
+ }
+
+ this.sessionStatus = sessionStatus;
+
+ for (OtrEngineListener l : this.listeners)
+ l.sessionStatusChanged(getSessionID());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.otr4j.session.ISession#getSessionStatus()
+ */
+
+ public SessionStatus getSessionStatus() {
+ return sessionStatus;
+ }
+
+ private void setSessionID(SessionID sessionID) {
+ this.sessionID = sessionID;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.otr4j.session.ISession#getSessionID()
+ */
+ public SessionID getSessionID() {
+ return sessionID;
+ }
+
+ private void setHost(OtrEngineHost host) {
+ this.host = host;
+ }
+
+ private OtrEngineHost getHost() {
+ return host;
+ }
+
+ private SessionKeys[][] getSessionKeys() {
+ if (sessionKeys == null)
+ sessionKeys = new SessionKeys[2][2];
+ return sessionKeys;
+ }
+
+ private AuthContext getAuthContext() {
+ if (authContext == null)
+ authContext = new AuthContextImpl(this);
+ return authContext;
+ }
+
+ private Vector<byte[]> getOldMacKeys() {
+ if (oldMacKeys == null)
+ oldMacKeys = new Vector<byte[]>();
+ return oldMacKeys;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * net.java.otr4j.session.ISession#handleReceivingMessage(java.lang.String)
+ */
+ public String transformReceiving(String msgText) throws OtrException {
+ OtrPolicy policy = getSessionPolicy();
+ if (!policy.getAllowV1() && !policy.getAllowV2()) {
+ logger
+ .finest("Policy does not allow neither V1 not V2, ignoring message.");
+ return msgText;
+ }
+
+ AbstractMessage m;
+ try {
+ m = SerializationUtils.toMessage(msgText);
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ if (m == null)
+ return msgText; // Propably null or empty.
+
+ switch (m.messageType) {
+ case AbstractEncodedMessage.MESSAGE_DATA:
+ return handleDataMessage((DataMessage) m);
+ case AbstractMessage.MESSAGE_ERROR:
+ handleErrorMessage((ErrorMessage) m);
+ return null;
+ case AbstractMessage.MESSAGE_PLAINTEXT:
+ return handlePlainTextMessage((PlainTextMessage) m);
+ case AbstractMessage.MESSAGE_QUERY:
+ handleQueryMessage((QueryMessage) m);
+ return null;
+ case AbstractEncodedMessage.MESSAGE_DH_COMMIT:
+ case AbstractEncodedMessage.MESSAGE_DHKEY:
+ case AbstractEncodedMessage.MESSAGE_REVEALSIG:
+ case AbstractEncodedMessage.MESSAGE_SIGNATURE:
+ AuthContext auth = this.getAuthContext();
+ auth.handleReceivingMessage(m);
+
+ if (auth.getIsSecure()) {
+ this.setSessionStatus(SessionStatus.ENCRYPTED);
+ logger.finest("Gone Secure.");
+ }
+ return null;
+ default:
+ throw new UnsupportedOperationException(
+ "Received an uknown message type.");
+ }
+ }
+
+ private void handleQueryMessage(QueryMessage queryMessage)
+ throws OtrException {
+ logger.finest(getSessionID().getAccountID()
+ + " received a query message from "
+ + getSessionID().getUserID() + " throught "
+ + getSessionID().getProtocolName() + ".");
+
+ setSessionStatus(SessionStatus.PLAINTEXT);
+
+ OtrPolicy policy = getSessionPolicy();
+ if (queryMessage.versions.contains(2) && policy.getAllowV2()) {
+ logger.finest("Query message with V2 support found.");
+ getAuthContext().respondV2Auth();
+ } else if (queryMessage.versions.contains(1) && policy.getAllowV1()) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private void handleErrorMessage(ErrorMessage errorMessage)
+ throws OtrException {
+ logger.finest(getSessionID().getAccountID()
+ + " received an error message from "
+ + getSessionID().getUserID() + " throught "
+ + getSessionID().getUserID() + ".");
+
+ getHost().showError(this.getSessionID(), errorMessage.error);
+
+ OtrPolicy policy = getSessionPolicy();
+ if (policy.getErrorStartAKE()) {
+ logger.finest("Error message starts AKE.");
+ Vector<Integer> versions = new Vector<Integer>();
+ if (policy.getAllowV1())
+ versions.add(1);
+
+ if (policy.getAllowV2())
+ versions.add(2);
+
+ logger.finest("Sending Query");
+ injectMessage(new QueryMessage(versions));
+ }
+ }
+
+ private String handleDataMessage(DataMessage data) throws OtrException {
+ logger.finest(getSessionID().getAccountID()
+ + " received a data message from " + getSessionID().getUserID()
+ + ".");
+
+ switch (this.getSessionStatus()) {
+ case ENCRYPTED:
+ logger
+ .finest("Message state is ENCRYPTED. Trying to decrypt message.");
+
+ // Find matching session keys.
+ int senderKeyID = data.senderKeyID;
+ int receipientKeyID = data.recipientKeyID;
+ SessionKeys matchingKeys = this.getSessionKeysByID(receipientKeyID,
+ senderKeyID);
+
+ if (matchingKeys == null) {
+ logger.finest("No matching keys found.");
+ return null;
+ }
+
+ // Verify received MAC with a locally calculated MAC.
+ logger
+ .finest("Transforming T to byte[] to calculate it's HmacSHA1.");
+
+ byte[] serializedT;
+ try {
+ serializedT = SerializationUtils.toByteArray(data.getT());
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+
+ byte[] computedMAC = otrCryptoEngine.sha1Hmac(serializedT,
+ matchingKeys.getReceivingMACKey(),
+ SerializationConstants.TYPE_LEN_MAC);
+
+ if (!Arrays.equals(computedMAC, data.mac)) {
+ logger.finest("MAC verification failed, ignoring message");
+ return null;
+ }
+
+ logger.finest("Computed HmacSHA1 value matches sent one.");
+
+ // Mark this MAC key as old to be revealed.
+ matchingKeys.setIsUsedReceivingMACKey(true);
+
+ matchingKeys.setReceivingCtr(data.ctr);
+
+ byte[] dmc = otrCryptoEngine.aesDecrypt(matchingKeys
+ .getReceivingAESKey(), matchingKeys.getReceivingCtr(),
+ data.encryptedMessage);
+ String decryptedMsgContent;
+ try {
+ // Expect bytes to be text encoded in UTF-8.
+ decryptedMsgContent = new String(dmc, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new OtrException(e);
+ }
+
+ logger.finest("Decrypted message: \"" + decryptedMsgContent + "\"");
+
+ // Rotate keys if necessary.
+ SessionKeys mostRecent = this.getMostRecentSessionKeys();
+ if (mostRecent.getLocalKeyID() == receipientKeyID)
+ this.rotateLocalSessionKeys();
+
+ if (mostRecent.getRemoteKeyID() == senderKeyID)
+ this.rotateRemoteSessionKeys(data.nextDH);
+
+ // Handle TLVs
+ List<TLV> tlvs = null;
+ int tlvIndex = decryptedMsgContent.indexOf((char) 0x0);
+ if (tlvIndex > -1) {
+ decryptedMsgContent = decryptedMsgContent
+ .substring(0, tlvIndex);
+ tlvIndex++;
+ byte[] tlvsb = new byte[dmc.length - tlvIndex];
+ System.arraycopy(dmc, tlvIndex, tlvsb, 0, tlvsb.length);
+
+ tlvs = new Vector<TLV>();
+ ByteArrayInputStream tin = new ByteArrayInputStream(tlvsb);
+ while (tin.available() > 0) {
+ int type;
+ byte[] tdata;
+ OtrInputStream eois = new OtrInputStream(tin);
+ try {
+ type = eois.readShort();
+ tdata = eois.readTlvData();
+ eois.close();
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ tlvs.add(new TLV(type, tdata));
+ }
+ }
+ if (tlvs != null && tlvs.size() > 0) {
+ for (TLV tlv : tlvs) {
+ switch (tlv.getType()) {
+ case 1:
+ this.setSessionStatus(SessionStatus.FINISHED);
+ return null;
+ default:
+ return decryptedMsgContent;
+ }
+ }
+ }
+
+ return decryptedMsgContent;
+
+ case FINISHED:
+ case PLAINTEXT:
+ getHost().showWarning(this.getSessionID(),
+ "Unreadable encrypted message was received.");
+
+ injectMessage(new ErrorMessage(AbstractMessage.MESSAGE_ERROR,
+ "You sent me an unreadable encrypted message.."));
+ break;
+ }
+
+ return null;
+ }
+
+ public void injectMessage(AbstractMessage m) throws OtrException {
+ String msg;
+ try {
+ msg = SerializationUtils.toString(m);
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+ getHost().injectMessage(getSessionID(), msg);
+ }
+
+ private String handlePlainTextMessage(PlainTextMessage plainTextMessage)
+ throws OtrException {
+ logger.finest(getSessionID().getAccountID()
+ + " received a plaintext message from "
+ + getSessionID().getUserID() + " throught "
+ + getSessionID().getProtocolName() + ".");
+
+ OtrPolicy policy = getSessionPolicy();
+ List<Integer> versions = plainTextMessage.versions;
+ if (versions == null || versions.size() < 1) {
+ logger
+ .finest("Received plaintext message without the whitespace tag.");
+ switch (this.getSessionStatus()) {
+ case ENCRYPTED:
+ case FINISHED:
+ // Display the message to the user, but warn him that the
+ // message was received unencrypted.
+ getHost().showWarning(this.getSessionID(),
+ "The message was received unencrypted.");
+ return plainTextMessage.cleanText;
+ case PLAINTEXT:
+ // Simply display the message to the user. If
+ // REQUIRE_ENCRYPTION
+ // is set, warn him that the message was received
+ // unencrypted.
+ if (policy.getRequireEncryption()) {
+ getHost().showWarning(this.getSessionID(),
+ "The message was received unencrypted.");
+ }
+ return plainTextMessage.cleanText;
+ }
+ } else {
+ logger
+ .finest("Received plaintext message with the whitespace tag.");
+ switch (this.getSessionStatus()) {
+ case ENCRYPTED:
+ case FINISHED:
+ // Remove the whitespace tag and display the message to the
+ // user, but warn him that the message was received
+ // unencrypted.
+ getHost().showWarning(this.getSessionID(),
+ "The message was received unencrypted.");
+ case PLAINTEXT:
+ // Remove the whitespace tag and display the message to the
+ // user. If REQUIRE_ENCRYPTION is set, warn him that the
+ // message
+ // was received unencrypted.
+ if (policy.getRequireEncryption())
+ getHost().showWarning(this.getSessionID(),
+ "The message was received unencrypted.");
+ }
+
+ if (policy.getWhitespaceStartAKE()) {
+ logger.finest("WHITESPACE_START_AKE is set");
+
+ if (plainTextMessage.versions.contains(2)
+ && policy.getAllowV2()) {
+ logger.finest("V2 tag found.");
+ getAuthContext().respondV2Auth();
+ } else if (plainTextMessage.versions.contains(1)
+ && policy.getAllowV1()) {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+
+ return plainTextMessage.cleanText;
+ }
+
+ // Retransmit last sent message. Spec document does not mention where or
+ // when that should happen, must check libotr code.
+ private String lastSentMessage;
+
+ public String transformSending(String msgText, List<TLV> tlvs)
+ throws OtrException {
+
+ switch (this.getSessionStatus()) {
+ case PLAINTEXT:
+ if (getSessionPolicy().getRequireEncryption()) {
+ this.lastSentMessage = msgText;
+ this.startSession();
+ } else
+ // TODO this does not precisly behave according to
+ // specification.
+ return msgText;
+ case ENCRYPTED:
+ this.lastSentMessage = msgText;
+ logger.finest(getSessionID().getAccountID()
+ + " sends an encrypted message to "
+ + getSessionID().getUserID() + " throught "
+ + getSessionID().getProtocolName() + ".");
+
+ // Get encryption keys.
+ SessionKeys encryptionKeys = this.getEncryptionSessionKeys();
+ int senderKeyID = encryptionKeys.getLocalKeyID();
+ int receipientKeyID = encryptionKeys.getRemoteKeyID();
+
+ // Increment CTR.
+ encryptionKeys.incrementSendingCtr();
+ byte[] ctr = encryptionKeys.getSendingCtr();
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ if (msgText != null && msgText.length() > 0)
+ try {
+ out.write(msgText.getBytes("UTF8"));
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ // Append tlvs
+ if (tlvs != null && tlvs.size() > 0) {
+ out.write((byte) 0x00);
+
+ OtrOutputStream eoos = new OtrOutputStream(out);
+ for (TLV tlv : tlvs) {
+ try {
+ eoos.writeShort(tlv.type);
+ eoos.writeTlvData(tlv.value);
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+ }
+ }
+
+ OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+
+ byte[] data = out.toByteArray();
+ // Encrypt message.
+ logger
+ .finest("Encrypting message with keyids (localKeyID, remoteKeyID) = ("
+ + senderKeyID + ", " + receipientKeyID + ")");
+ byte[] encryptedMsg = otrCryptoEngine.aesEncrypt(encryptionKeys
+ .getSendingAESKey(), ctr, data);
+
+ // Get most recent keys to get the next D-H public key.
+ SessionKeys mostRecentKeys = this.getMostRecentSessionKeys();
+ DHPublicKey nextDH = (DHPublicKey) mostRecentKeys.getLocalPair()
+ .getPublic();
+
+ // Calculate T.
+ MysteriousT t = new MysteriousT(2, 0, senderKeyID, receipientKeyID,
+ nextDH, ctr, encryptedMsg);
+
+ // Calculate T hash.
+ byte[] sendingMACKey = encryptionKeys.getSendingMACKey();
+
+ logger
+ .finest("Transforming T to byte[] to calculate it's HmacSHA1.");
+ byte[] serializedT;
+ try {
+ serializedT = SerializationUtils.toByteArray(t);
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ byte[] mac = otrCryptoEngine.sha1Hmac(serializedT, sendingMACKey,
+ SerializationConstants.TYPE_LEN_MAC);
+
+ // Get old MAC keys to be revealed.
+ byte[] oldKeys = this.collectOldMacKeys();
+ DataMessage m = new DataMessage(t, mac, oldKeys);
+
+ try {
+ return SerializationUtils.toString(m);
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+ case FINISHED:
+ this.lastSentMessage = msgText;
+ getHost()
+ .showError(
+ sessionID,
+ "Your message to "
+ + sessionID.getUserID()
+ + " was not sent. Either end your private conversation, or restart it.");
+ return null;
+ default:
+ logger.finest("Uknown message state, not processing.");
+ return msgText;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.otr4j.session.ISession#startSession()
+ */
+ public void startSession() throws OtrException {
+ if (this.getSessionStatus() == SessionStatus.ENCRYPTED)
+ return;
+
+ if (!getSessionPolicy().getAllowV2())
+ throw new UnsupportedOperationException();
+
+ this.getAuthContext().startV2Auth();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.otr4j.session.ISession#endSession()
+ */
+ public void endSession() throws OtrException {
+ SessionStatus status = this.getSessionStatus();
+ switch (status) {
+ case ENCRYPTED:
+ Vector<TLV> tlvs = new Vector<TLV>();
+ tlvs.add(new TLV(1, null));
+
+ String msg = this.transformSending(null, tlvs);
+ getHost().injectMessage(getSessionID(), msg);
+ this.setSessionStatus(SessionStatus.PLAINTEXT);
+ break;
+ case FINISHED:
+ this.setSessionStatus(SessionStatus.PLAINTEXT);
+ break;
+ case PLAINTEXT:
+ return;
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.otr4j.session.ISession#refreshSession()
+ */
+ public void refreshSession() throws OtrException {
+ this.endSession();
+ this.startSession();
+ }
+
+ private PublicKey remotePublicKey;
+
+ private void setRemotePublicKey(PublicKey pubKey) {
+ this.remotePublicKey = pubKey;
+ }
+
+ public PublicKey getRemotePublicKey() {
+ return remotePublicKey;
+ }
+
+ private List<OtrEngineListener> listeners = new Vector<OtrEngineListener>();
+
+ public void addOtrEngineListener(OtrEngineListener l) {
+ synchronized (listeners) {
+ if (!listeners.contains(l))
+ listeners.add(l);
+ }
+ }
+
+ public void removeOtrEngineListener(OtrEngineListener l) {
+ synchronized (listeners) {
+ listeners.remove(l);
+ }
+ }
+
+ public OtrPolicy getSessionPolicy() {
+ return getHost().getSessionPolicy(getSessionID());
+ }
+
+ public KeyPair getLocalKeyPair() {
+ return getHost().getKeyPair(this.getSessionID());
+ }
+}