diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java b/src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java index f973c8377..b54864fea 100644 --- a/src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java +++ b/src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java @@ -18,7 +18,9 @@ import eu.siacs.conversations.utils.SSLSockets; public abstract class HashedToken extends SaslMechanism { - private static List HASH_FUNCTIONS = Arrays.asList("SHA-512", "SHA-256"); + private static final String PREFIX = "HT"; + + private static final List HASH_FUNCTIONS = Arrays.asList("SHA-512", "SHA-256"); protected final ChannelBinding channelBinding; @@ -61,7 +63,7 @@ public abstract class HashedToken extends SaslMechanism { if (last <= first || mechanism.length() <= last) { throw new IllegalArgumentException("Not a valid HashedToken name"); } - if (mechanism.substring(0, first).equals("HT")) { + if (mechanism.substring(0, first).equals(PREFIX)) { final String hashFunction = mechanism.substring(first + 1, last); final String cbShortName = mechanism.substring(last + 1); final ChannelBinding channelBinding = @@ -110,5 +112,11 @@ public abstract class HashedToken extends SaslMechanism { .add("channelBinding", channelBinding) .toString(); } + + public String name() { + return String.format( + "%s-%s-%s", + PREFIX, hashFunction, ChannelBinding.SHORT_NAMES.get(channelBinding)); + } } } diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha512.java b/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha512.java new file mode 100644 index 000000000..fd1b7be51 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha512.java @@ -0,0 +1,24 @@ +package eu.siacs.conversations.crypto.sasl; + +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; + +import eu.siacs.conversations.entities.Account; + +public class HashedTokenSha512 extends HashedToken { + + public HashedTokenSha512(final Account account, final ChannelBinding channelBinding) { + super(account, channelBinding); + } + + @Override + protected HashFunction getHashFunction(final byte[] key) { + return Hashing.hmacSha512(key); + } + + @Override + public String getMechanism() { + final String cbShortName = ChannelBinding.SHORT_NAMES.get(this.channelBinding); + return String.format("HT-SHA-512-%s", cbShortName); + } +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index f2b4c932a..2fa8d6df3 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -181,6 +181,7 @@ public class XmppConnection implements Runnable { private OnBindListener bindListener = null; private OnMessageAcknowledged acknowledgedListener = null; private SaslMechanism saslMechanism; + private HashedToken.Mechanism hashTokenRequest; private HttpUrl redirectionUrl = null; private String verifiedHostname = null; private volatile Thread mThread; @@ -761,6 +762,8 @@ public class XmppConnection implements Runnable { final Element bound = success.findChild("bound", Namespace.BIND2); final Element resumed = success.findChild("resumed", "urn:xmpp:sm:3"); final Element failed = success.findChild("failed", "urn:xmpp:sm:3"); + final Element tokenWrapper = success.findChild("token", Namespace.FAST); + final String token = tokenWrapper == null ? null : tokenWrapper.getAttribute("token"); if (bound != null && resumed != null) { Log.d( Config.LOGTAG, @@ -795,6 +798,10 @@ public class XmppConnection implements Runnable { } sendPostBindInitialization(waitForDisco, carbonsEnabled != null); } + //TODO figure out name either by the existence of hashTokenRequest or if scramMechanism is of instance HashedToken + if (this.hashTokenRequest != null && !Strings.isNullOrEmpty(token)) { + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": storing hashed token "+this.hashTokenRequest.name()+ " "+token); + } } this.quickStartInProgress = false; if (version == SaslMechanism.Version.SASL) { @@ -1345,7 +1352,7 @@ public class XmppConnection implements Runnable { final boolean sm = inline != null && inline.hasChild("sm", "urn:xmpp:sm:3"); final Element fast = inline == null ? null : inline.findChild("fast", Namespace.FAST); final Collection fastMechanisms = SaslMechanism.mechanisms(fast); - Log.d(Config.LOGTAG,"fast mechanism: "+ HashedToken.Mechanism.best(fastMechanisms, SSLSockets.version(this.socket))); + final HashedToken.Mechanism hashTokenRequest = HashedToken.Mechanism.best(fastMechanisms, SSLSockets.version(this.socket)); final Collection bindFeatures = Bind2.features(inline); quickStartAvailable = sm @@ -1362,7 +1369,8 @@ public class XmppConnection implements Runnable { return; } } - authenticate = generateAuthenticationRequest(firstMessage, bindFeatures, sm); + this.hashTokenRequest = hashTokenRequest; + authenticate = generateAuthenticationRequest(firstMessage, hashTokenRequest, bindFeatures, sm); } else { throw new AssertionError("Missing implementation for " + version); } @@ -1383,11 +1391,12 @@ public class XmppConnection implements Runnable { } private Element generateAuthenticationRequest(final String firstMessage) { - return generateAuthenticationRequest(firstMessage, Bind2.QUICKSTART_FEATURES, true); + return generateAuthenticationRequest(firstMessage, null, Bind2.QUICKSTART_FEATURES, true); } private Element generateAuthenticationRequest( final String firstMessage, + final HashedToken.Mechanism hashedTokenRequest, final Collection bind, final boolean inlineStreamManagement) { final Element authenticate = new Element("authenticate", Namespace.SASL_2); @@ -1413,6 +1422,9 @@ public class XmppConnection implements Runnable { this.mWaitingForSmCatchup.set(true); authenticate.addChild(resume); } + if (hashedTokenRequest != null) { + authenticate.addChild("request-token", Namespace.FAST).setAttribute("mechanism", hashedTokenRequest.name()); + } return authenticate; }