src/net/java/otr4j/session/SessionImpl.java
changeset 1040 197a85a35cba
parent 1039 7d6f2526244a
child 1041 e5a970600066
--- a/src/net/java/otr4j/session/SessionImpl.java	Sun Mar 15 17:28:04 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,792 +0,0 @@
-/*
- * 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());
-	}
-}