--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/java/otr4j/session/AuthContextImpl.java Sun Dec 05 18:43:51 2010 +0100
@@ -0,0 +1,766 @@
+/*
+ * 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.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.Vector;
+import java.util.logging.Logger;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+import net.java.otr4j.OtrException;
+import net.java.otr4j.crypto.OtrCryptoEngine;
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.io.SerializationUtils;
+import net.java.otr4j.io.messages.DHCommitMessage;
+import net.java.otr4j.io.messages.DHKeyMessage;
+import net.java.otr4j.io.messages.AbstractEncodedMessage;
+import net.java.otr4j.io.messages.AbstractMessage;
+import net.java.otr4j.io.messages.SignatureM;
+import net.java.otr4j.io.messages.SignatureX;
+import net.java.otr4j.io.messages.QueryMessage;
+import net.java.otr4j.io.messages.RevealSignatureMessage;
+import net.java.otr4j.io.messages.SignatureMessage;
+
+/**
+ *
+ * @author George Politis
+ */
+class AuthContextImpl implements AuthContext {
+
+ public AuthContextImpl(Session session) {
+ this.setSession(session);
+ this.reset();
+ }
+
+ private Session session;
+
+ private int authenticationState;
+ private byte[] r;
+
+ private DHPublicKey remoteDHPublicKey;
+ private byte[] remoteDHPublicKeyEncrypted;
+ private byte[] remoteDHPublicKeyHash;
+
+ private KeyPair localDHKeyPair;
+ private int localDHPrivateKeyID;
+ private byte[] localDHPublicKeyBytes;
+ private byte[] localDHPublicKeyHash;
+ private byte[] localDHPublicKeyEncrypted;
+
+ private BigInteger s;
+ private byte[] c;
+ private byte[] m1;
+ private byte[] m2;
+ private byte[] cp;
+ private byte[] m1p;
+ private byte[] m2p;
+
+ private KeyPair localLongTermKeyPair;
+ private Boolean isSecure = false;
+ private int protocolVersion;
+
+ private int getProtocolVersion() {
+ return this.protocolVersion;
+ }
+
+ private void setProtocolVersion(int protoVersion) {
+ this.protocolVersion = protoVersion;
+ }
+
+ private static Logger logger = Logger.getLogger(AuthContextImpl.class
+ .getName());
+
+ class MessageFactory {
+
+ private QueryMessage getQueryMessage() {
+ Vector<Integer> versions = new Vector<Integer>();
+ versions.add(2);
+ return new QueryMessage(versions);
+ }
+
+ private DHCommitMessage getDHCommitMessage() throws OtrException {
+ return new DHCommitMessage(getProtocolVersion(),
+ getLocalDHPublicKeyHash(), getLocalDHPublicKeyEncrypted());
+ }
+
+ private DHKeyMessage getDHKeyMessage() throws OtrException {
+ return new DHKeyMessage(getProtocolVersion(),
+ (DHPublicKey) getLocalDHKeyPair().getPublic());
+ }
+
+ private RevealSignatureMessage getRevealSignatureMessage()
+ throws OtrException {
+ try {
+ SignatureM m = new SignatureM((DHPublicKey) getLocalDHKeyPair()
+ .getPublic(), getRemoteDHPublicKey(),
+ getLocalLongTermKeyPair().getPublic(),
+ getLocalDHKeyPairID());
+
+ OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+ byte[] mhash = otrCryptoEngine.sha256Hmac(SerializationUtils
+ .toByteArray(m), getM1());
+ byte[] signature = otrCryptoEngine.sign(mhash,
+ getLocalLongTermKeyPair().getPrivate());
+
+ SignatureX mysteriousX = new SignatureX(
+ getLocalLongTermKeyPair().getPublic(),
+ getLocalDHKeyPairID(), signature);
+ byte[] xEncrypted = otrCryptoEngine.aesEncrypt(getC(), null,
+ SerializationUtils.toByteArray(mysteriousX));
+
+ byte[] tmp = SerializationUtils.writeData(xEncrypted);
+
+ byte[] xEncryptedHash = otrCryptoEngine.sha256Hmac160(tmp,
+ getM2());
+ return new RevealSignatureMessage(getProtocolVersion(),
+ xEncrypted, xEncryptedHash, getR());
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+ }
+
+ private SignatureMessage getSignatureMessage() throws OtrException {
+ SignatureM m = new SignatureM((DHPublicKey) getLocalDHKeyPair()
+ .getPublic(), getRemoteDHPublicKey(),
+ getLocalLongTermKeyPair().getPublic(),
+ getLocalDHKeyPairID());
+
+ OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+ byte[] mhash;
+ try {
+ mhash = otrCryptoEngine.sha256Hmac(SerializationUtils
+ .toByteArray(m), getM1p());
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ byte[] signature = otrCryptoEngine.sign(mhash,
+ getLocalLongTermKeyPair().getPrivate());
+
+ SignatureX mysteriousX = new SignatureX(getLocalLongTermKeyPair()
+ .getPublic(), getLocalDHKeyPairID(), signature);
+
+ byte[] xEncrypted;
+ try {
+ xEncrypted = otrCryptoEngine.aesEncrypt(getCp(), null,
+ SerializationUtils.toByteArray(mysteriousX));
+ byte[] tmp = SerializationUtils.writeData(xEncrypted);
+ byte[] xEncryptedHash = otrCryptoEngine.sha256Hmac160(tmp,
+ getM2p());
+ return new SignatureMessage(getProtocolVersion(), xEncrypted,
+ xEncryptedHash);
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+ }
+ }
+
+ private MessageFactory messageFactory = new MessageFactory();
+
+ public void reset() {
+ logger.finest("Resetting authentication state.");
+ authenticationState = AuthContext.NONE;
+ r = null;
+
+ remoteDHPublicKey = null;
+ remoteDHPublicKeyEncrypted = null;
+ remoteDHPublicKeyHash = null;
+
+ localDHKeyPair = null;
+ localDHPrivateKeyID = 1;
+ localDHPublicKeyBytes = null;
+ localDHPublicKeyHash = null;
+ localDHPublicKeyEncrypted = null;
+
+ s = null;
+ c = m1 = m2 = cp = m1p = m2p = null;
+
+ localLongTermKeyPair = null;
+ protocolVersion = 0;
+ setIsSecure(false);
+ }
+
+ private void setIsSecure(Boolean isSecure) {
+ this.isSecure = isSecure;
+ }
+
+ public boolean getIsSecure() {
+ return isSecure;
+ }
+
+ private void setAuthenticationState(int authenticationState) {
+ this.authenticationState = authenticationState;
+ }
+
+ private int getAuthenticationState() {
+ return authenticationState;
+ }
+
+ private byte[] getR() {
+ if (r == null) {
+ logger.finest("Picking random key r.");
+ r = new byte[OtrCryptoEngine.AES_KEY_BYTE_LENGTH];
+ new Random().nextBytes(r);
+ }
+ return r;
+ }
+
+ private void setRemoteDHPublicKey(DHPublicKey dhPublicKey) {
+ // Verifies that Alice's gy is a legal value (2 <= gy <= modulus-2)
+ if (dhPublicKey.getY().compareTo(OtrCryptoEngine.MODULUS_MINUS_TWO) > 0) {
+ throw new IllegalArgumentException(
+ "Illegal D-H Public Key value, Ignoring message.");
+ } else if (dhPublicKey.getY().compareTo(OtrCryptoEngine.BIGINTEGER_TWO) < 0) {
+ throw new IllegalArgumentException(
+ "Illegal D-H Public Key value, Ignoring message.");
+ }
+ logger.finest("Received D-H Public Key is a legal value.");
+
+ this.remoteDHPublicKey = dhPublicKey;
+ }
+
+ public DHPublicKey getRemoteDHPublicKey() {
+ return remoteDHPublicKey;
+ }
+
+ private void setRemoteDHPublicKeyEncrypted(byte[] remoteDHPublicKeyEncrypted) {
+ logger.finest("Storing encrypted remote public key.");
+ this.remoteDHPublicKeyEncrypted = remoteDHPublicKeyEncrypted;
+ }
+
+ private byte[] getRemoteDHPublicKeyEncrypted() {
+ return remoteDHPublicKeyEncrypted;
+ }
+
+ private void setRemoteDHPublicKeyHash(byte[] remoteDHPublicKeyHash) {
+ logger.finest("Storing encrypted remote public key hash.");
+ this.remoteDHPublicKeyHash = remoteDHPublicKeyHash;
+ }
+
+ private byte[] getRemoteDHPublicKeyHash() {
+ return remoteDHPublicKeyHash;
+ }
+
+ public KeyPair getLocalDHKeyPair() throws OtrException {
+ if (localDHKeyPair == null) {
+ localDHKeyPair = new OtrCryptoEngineImpl().generateDHKeyPair();
+ logger.finest("Generated local D-H key pair.");
+ }
+ return localDHKeyPair;
+ }
+
+ private int getLocalDHKeyPairID() {
+ return localDHPrivateKeyID;
+ }
+
+ private byte[] getLocalDHPublicKeyHash() throws OtrException {
+ if (localDHPublicKeyHash == null) {
+ localDHPublicKeyHash = new OtrCryptoEngineImpl()
+ .sha256Hash(getLocalDHPublicKeyBytes());
+ logger.finest("Hashed local D-H public key.");
+ }
+ return localDHPublicKeyHash;
+ }
+
+ private byte[] getLocalDHPublicKeyEncrypted() throws OtrException {
+ if (localDHPublicKeyEncrypted == null) {
+ localDHPublicKeyEncrypted = new OtrCryptoEngineImpl().aesEncrypt(
+ getR(), null, getLocalDHPublicKeyBytes());
+ logger.finest("Encrypted our D-H public key.");
+ }
+ return localDHPublicKeyEncrypted;
+ }
+
+ public BigInteger getS() throws OtrException {
+ if (s == null) {
+ s = new OtrCryptoEngineImpl().generateSecret(this
+ .getLocalDHKeyPair().getPrivate(), this
+ .getRemoteDHPublicKey());
+ logger.finest("Generated shared secret.");
+ }
+ return s;
+ }
+
+ private byte[] getC() throws OtrException {
+ if (c != null)
+ return c;
+
+ byte[] h2 = h2(C_START);
+ ByteBuffer buff = ByteBuffer.wrap(h2);
+ this.c = new byte[OtrCryptoEngine.AES_KEY_BYTE_LENGTH];
+ buff.get(this.c);
+ logger.finest("Computed c.");
+ return c;
+
+ }
+
+ private byte[] getM1() throws OtrException {
+ if (m1 != null)
+ return m1;
+
+ byte[] h2 = h2(M1_START);
+ ByteBuffer buff = ByteBuffer.wrap(h2);
+ byte[] m1 = new byte[OtrCryptoEngine.SHA256_HMAC_KEY_BYTE_LENGTH];
+ buff.get(m1);
+ logger.finest("Computed m1.");
+ this.m1 = m1;
+ return m1;
+ }
+
+ private byte[] getM2() throws OtrException {
+ if (m2 != null)
+ return m2;
+
+ byte[] h2 = h2(M2_START);
+ ByteBuffer buff = ByteBuffer.wrap(h2);
+ byte[] m2 = new byte[OtrCryptoEngine.SHA256_HMAC_KEY_BYTE_LENGTH];
+ buff.get(m2);
+ logger.finest("Computed m2.");
+ this.m2 = m2;
+ return m2;
+ }
+
+ private byte[] getCp() throws OtrException {
+ if (cp != null)
+ return cp;
+
+ byte[] h2 = h2(C_START);
+ ByteBuffer buff = ByteBuffer.wrap(h2);
+ byte[] cp = new byte[OtrCryptoEngine.AES_KEY_BYTE_LENGTH];
+ buff.position(OtrCryptoEngine.AES_KEY_BYTE_LENGTH);
+ buff.get(cp);
+ logger.finest("Computed c'.");
+ this.cp = cp;
+ return cp;
+ }
+
+ private byte[] getM1p() throws OtrException {
+ if (m1p != null)
+ return m1p;
+
+ byte[] h2 = h2(M1p_START);
+ ByteBuffer buff = ByteBuffer.wrap(h2);
+ byte[] m1p = new byte[OtrCryptoEngine.SHA256_HMAC_KEY_BYTE_LENGTH];
+ buff.get(m1p);
+ this.m1p = m1p;
+ logger.finest("Computed m1'.");
+ return m1p;
+ }
+
+ private byte[] getM2p() throws OtrException {
+ if (m2p != null)
+ return m2p;
+
+ byte[] h2 = h2(M2p_START);
+ ByteBuffer buff = ByteBuffer.wrap(h2);
+ byte[] m2p = new byte[OtrCryptoEngine.SHA256_HMAC_KEY_BYTE_LENGTH];
+ buff.get(m2p);
+ this.m2p = m2p;
+ logger.finest("Computed m2'.");
+ return m2p;
+ }
+
+ public KeyPair getLocalLongTermKeyPair() {
+ if (localLongTermKeyPair == null) {
+ localLongTermKeyPair = getSession().getLocalKeyPair();
+ }
+ return localLongTermKeyPair;
+ }
+
+ private byte[] h2(byte b) throws OtrException {
+ byte[] secbytes;
+ try {
+ secbytes = SerializationUtils.writeMpi(getS());
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ int len = secbytes.length + 1;
+ ByteBuffer buff = ByteBuffer.allocate(len);
+ buff.put(b);
+ buff.put(secbytes);
+ byte[] sdata = buff.array();
+ return new OtrCryptoEngineImpl().sha256Hash(sdata);
+ }
+
+ private byte[] getLocalDHPublicKeyBytes() throws OtrException {
+ if (localDHPublicKeyBytes == null) {
+ try {
+ this.localDHPublicKeyBytes = SerializationUtils
+ .writeMpi(((DHPublicKey) getLocalDHKeyPair()
+ .getPublic()).getY());
+
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ }
+ return localDHPublicKeyBytes;
+ }
+
+ public void handleReceivingMessage(AbstractMessage m) throws OtrException {
+
+ switch (m.messageType) {
+ case AbstractEncodedMessage.MESSAGE_DH_COMMIT:
+ handleDHCommitMessage((DHCommitMessage) m);
+ break;
+ case AbstractEncodedMessage.MESSAGE_DHKEY:
+ handleDHKeyMessage((DHKeyMessage) m);
+ break;
+ case AbstractEncodedMessage.MESSAGE_REVEALSIG:
+ handleRevealSignatureMessage((RevealSignatureMessage) m);
+ break;
+ case AbstractEncodedMessage.MESSAGE_SIGNATURE:
+ handleSignatureMessage((SignatureMessage) m);
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private void handleSignatureMessage(SignatureMessage m) throws OtrException {
+ Session session = getSession();
+ SessionID sessionID = session.getSessionID();
+ logger.finest(sessionID.getAccountID()
+ + " received a signature message from " + sessionID.getUserID()
+ + " throught " + sessionID.getProtocolName() + ".");
+ if (!session.getSessionPolicy().getAllowV2()) {
+ logger.finest("Policy does not allow OTRv2, ignoring message.");
+ return;
+ }
+
+ switch (this.getAuthenticationState()) {
+ case AWAITING_SIG:
+ // Verify MAC.
+ if (!m.verify(this.getM2p())) {
+ logger
+ .finest("Signature MACs are not equal, ignoring message.");
+ return;
+ }
+
+ // Decrypt X.
+ byte[] remoteXDecrypted = m.decrypt(this.getCp());
+ SignatureX remoteX;
+ try {
+ remoteX = SerializationUtils.toMysteriousX(remoteXDecrypted);
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+ // Compute signature.
+ PublicKey remoteLongTermPublicKey = remoteX.longTermPublicKey;
+ SignatureM remoteM = new SignatureM(this.getRemoteDHPublicKey(),
+ (DHPublicKey) this.getLocalDHKeyPair().getPublic(),
+ remoteLongTermPublicKey, remoteX.dhKeyID);
+ OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+ // Verify signature.
+ byte[] signature;
+ try {
+ signature = otrCryptoEngine.sha256Hmac(SerializationUtils
+ .toByteArray(remoteM), this.getM1p());
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+ if (!otrCryptoEngine.verify(signature, remoteLongTermPublicKey,
+ remoteX.signature)) {
+ logger.finest("Signature verification failed.");
+ return;
+ }
+
+ this.setIsSecure(true);
+ this.setRemoteLongTermPublicKey(remoteLongTermPublicKey);
+ break;
+ default:
+ logger
+ .finest("We were not expecting a signature, ignoring message.");
+ return;
+ }
+ }
+
+ private void handleRevealSignatureMessage(RevealSignatureMessage m)
+ throws OtrException {
+ Session session = getSession();
+ SessionID sessionID = session.getSessionID();
+ logger.finest(sessionID.getAccountID()
+ + " received a reveal signature message from "
+ + sessionID.getUserID() + " throught "
+ + sessionID.getProtocolName() + ".");
+
+ if (!session.getSessionPolicy().getAllowV2()) {
+ logger.finest("Policy does not allow OTRv2, ignoring message.");
+ return;
+ }
+
+ switch (this.getAuthenticationState()) {
+ case AWAITING_REVEALSIG:
+ // Use the received value of r to decrypt the value of gx
+ // received
+ // in the D-H Commit Message, and verify the hash therein.
+ // Decrypt
+ // the encrypted signature, and verify the signature and the
+ // MACs.
+ // If everything checks out:
+
+ // * Reply with a Signature Message.
+ // * Transition authstate to AUTHSTATE_NONE.
+ // * Transition msgstate to MSGSTATE_ENCRYPTED.
+ // * TODO If there is a recent stored message, encrypt it and
+ // send
+ // it as a Data Message.
+
+ OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl();
+ // Uses r to decrypt the value of gx sent earlier
+ byte[] remoteDHPublicKeyDecrypted = otrCryptoEngine.aesDecrypt(
+ m.revealedKey, null, this.getRemoteDHPublicKeyEncrypted());
+
+ // Verifies that HASH(gx) matches the value sent earlier
+ byte[] remoteDHPublicKeyHash = otrCryptoEngine
+ .sha256Hash(remoteDHPublicKeyDecrypted);
+ if (!Arrays.equals(remoteDHPublicKeyHash, this
+ .getRemoteDHPublicKeyHash())) {
+ logger.finest("Hashes don't match, ignoring message.");
+ return;
+ }
+
+ // Verifies that Bob's gx is a legal value (2 <= gx <=
+ // modulus-2)
+ BigInteger remoteDHPublicKeyMpi;
+ try {
+ remoteDHPublicKeyMpi = SerializationUtils
+ .readMpi(remoteDHPublicKeyDecrypted);
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ this.setRemoteDHPublicKey(otrCryptoEngine
+ .getDHPublicKey(remoteDHPublicKeyMpi));
+
+ // Verify received Data.
+ if (!m.verify(this.getM2())) {
+ logger
+ .finest("Signature MACs are not equal, ignoring message.");
+ return;
+ }
+
+ // Decrypt X.
+ byte[] remoteXDecrypted = m.decrypt(this.getC());
+ SignatureX remoteX;
+ try {
+ remoteX = SerializationUtils.toMysteriousX(remoteXDecrypted);
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ // Compute signature.
+ PublicKey remoteLongTermPublicKey = remoteX.longTermPublicKey;
+ SignatureM remoteM = new SignatureM(this.getRemoteDHPublicKey(),
+ (DHPublicKey) this.getLocalDHKeyPair().getPublic(),
+ remoteLongTermPublicKey, remoteX.dhKeyID);
+
+ // Verify signature.
+ byte[] signature;
+ try {
+ signature = otrCryptoEngine.sha256Hmac(SerializationUtils
+ .toByteArray(remoteM), this.getM1());
+ } catch (IOException e) {
+ throw new OtrException(e);
+ }
+
+ if (!otrCryptoEngine.verify(signature, remoteLongTermPublicKey,
+ remoteX.signature)) {
+ logger.finest("Signature verification failed.");
+ return;
+ }
+
+ logger.finest("Signature verification succeeded.");
+
+ this.setAuthenticationState(AuthContext.NONE);
+ this.setIsSecure(true);
+ this.setRemoteLongTermPublicKey(remoteLongTermPublicKey);
+ getSession().injectMessage(messageFactory.getSignatureMessage());
+ break;
+ default:
+ logger.finest("Ignoring message.");
+ break;
+ }
+ }
+
+ private void handleDHKeyMessage(DHKeyMessage m) throws OtrException {
+ Session session = getSession();
+ SessionID sessionID = session.getSessionID();
+ logger.finest(sessionID.getAccountID()
+ + " received a D-H key message from " + sessionID.getUserID()
+ + " throught " + sessionID.getProtocolName() + ".");
+
+ if (!session.getSessionPolicy().getAllowV2()) {
+ logger.finest("If ALLOW_V2 is not set, ignore this message.");
+ return;
+ }
+
+ switch (this.getAuthenticationState()) {
+ case AWAITING_DHKEY:
+ // Reply with a Reveal Signature Message and transition
+ // authstate to
+ // AUTHSTATE_AWAITING_SIG
+ this.setRemoteDHPublicKey(m.dhPublicKey);
+ this.setAuthenticationState(AuthContext.AWAITING_SIG);
+ getSession().injectMessage(
+ messageFactory.getRevealSignatureMessage());
+ logger.finest("Sent Reveal Signature.");
+ break;
+ case AWAITING_SIG:
+
+ if (m.dhPublicKey.getY().equals(this.getRemoteDHPublicKey().getY())) {
+ // If this D-H Key message is the same the one you received
+ // earlier (when you entered AUTHSTATE_AWAITING_SIG):
+ // Retransmit
+ // your Reveal Signature Message.
+ getSession().injectMessage(
+ messageFactory.getRevealSignatureMessage());
+ logger.finest("Resent Reveal Signature.");
+ } else {
+ // Otherwise: Ignore the message.
+ logger.finest("Ignoring message.");
+ }
+ break;
+ default:
+ // Ignore the message
+ break;
+ }
+ }
+
+ private void handleDHCommitMessage(DHCommitMessage m) throws OtrException {
+ Session session = getSession();
+ SessionID sessionID = session.getSessionID();
+ logger.finest(sessionID.getAccountID()
+ + " received a D-H commit message from "
+ + sessionID.getUserID() + " throught "
+ + sessionID.getProtocolName() + ".");
+
+ if (!session.getSessionPolicy().getAllowV2()) {
+ logger.finest("ALLOW_V2 is not set, ignore this message.");
+ return;
+ }
+
+ switch (this.getAuthenticationState()) {
+ case NONE:
+ // Reply with a D-H Key Message, and transition authstate to
+ // AUTHSTATE_AWAITING_REVEALSIG.
+ this.reset();
+ this.setProtocolVersion(2);
+ this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
+ this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
+ this.setAuthenticationState(AuthContext.AWAITING_REVEALSIG);
+ getSession().injectMessage(messageFactory.getDHKeyMessage());
+ logger.finest("Sent D-H key.");
+ break;
+
+ case AWAITING_DHKEY:
+ // This is the trickiest transition in the whole protocol. It
+ // indicates that you have already sent a D-H Commit message to
+ // your
+ // correspondent, but that he either didn't receive it, or just
+ // didn't receive it yet, and has sent you one as well. The
+ // symmetry
+ // will be broken by comparing the hashed gx you sent in your
+ // D-H
+ // Commit Message with the one you received, considered as
+ // 32-byte
+ // unsigned big-endian values.
+ BigInteger ourHash = new BigInteger(1, this
+ .getLocalDHPublicKeyHash());
+ BigInteger theirHash = new BigInteger(1, m.dhPublicKeyHash);
+
+ if (theirHash.compareTo(ourHash) == -1) {
+ // Ignore the incoming D-H Commit message, but resend your
+ // D-H
+ // Commit message.
+ getSession().injectMessage(messageFactory.getDHCommitMessage());
+ logger
+ .finest("Ignored the incoming D-H Commit message, but resent our D-H Commit message.");
+ } else {
+ // *Forget* your old gx value that you sent (encrypted)
+ // earlier,
+ // and pretend you're in AUTHSTATE_NONE; i.e. reply with a
+ // D-H
+ // Key Message, and transition authstate to
+ // AUTHSTATE_AWAITING_REVEALSIG.
+ this.reset();
+ this.setProtocolVersion(2);
+ this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
+ this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
+ this.setAuthenticationState(AuthContext.AWAITING_REVEALSIG);
+ getSession().injectMessage(messageFactory.getDHKeyMessage());
+ logger
+ .finest("Forgot our old gx value that we sent (encrypted) earlier, and pretended we're in AUTHSTATE_NONE -> Sent D-H key.");
+ }
+ break;
+
+ case AWAITING_REVEALSIG:
+ // Retransmit your D-H Key Message (the same one as you sent
+ // when
+ // you entered AUTHSTATE_AWAITING_REVEALSIG). Forget the old D-H
+ // Commit message, and use this new one instead.
+ this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
+ this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
+ getSession().injectMessage(messageFactory.getDHKeyMessage());
+ logger.finest("Sent D-H key.");
+ break;
+ case AWAITING_SIG:
+ // Reply with a new D-H Key message, and transition authstate to
+ // AUTHSTATE_AWAITING_REVEALSIG
+ this.reset();
+ this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
+ this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
+ this.setAuthenticationState(AuthContext.AWAITING_REVEALSIG);
+ getSession().injectMessage(messageFactory.getDHKeyMessage());
+ logger.finest("Sent D-H key.");
+ break;
+ case V1_SETUP:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public void startV2Auth() throws OtrException {
+ logger
+ .finest("Starting Authenticated Key Exchange, sending query message");
+ getSession().injectMessage(messageFactory.getQueryMessage());
+ }
+
+ public void respondV2Auth() throws OtrException {
+ logger.finest("Responding to Query Message");
+ this.reset();
+ this.setProtocolVersion(2);
+ this.setAuthenticationState(AuthContext.AWAITING_DHKEY);
+ logger.finest("Sending D-H Commit.");
+ getSession().injectMessage(messageFactory.getDHCommitMessage());
+ }
+
+ private void setSession(Session session) {
+ this.session = session;
+ }
+
+ private Session getSession() {
+ return session;
+ }
+
+ private PublicKey remoteLongTermPublicKey;
+
+ public PublicKey getRemoteLongTermPublicKey() {
+ return remoteLongTermPublicKey;
+ }
+
+ private void setRemoteLongTermPublicKey(PublicKey pubKey) {
+ this.remoteLongTermPublicKey = pubKey;
+ }
+}