extract channel binding types via XEP-0440
This commit is contained in:
parent
a210568a9c
commit
b78acb6fca
|
@ -0,0 +1,27 @@
|
||||||
|
package eu.siacs.conversations.crypto.sasl;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.common.base.CaseFormat;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
|
||||||
|
public enum ChannelBinding {
|
||||||
|
NONE,
|
||||||
|
TLS_EXPORTER,
|
||||||
|
TLS_SERVER_END_POINT,
|
||||||
|
TLS_UNIQUE;
|
||||||
|
|
||||||
|
public static ChannelBinding of(final String type) {
|
||||||
|
if (type == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return valueOf(
|
||||||
|
CaseFormat.LOWER_HYPHEN.converterTo(CaseFormat.UPPER_UNDERSCORE).convert(type));
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
Log.d(Config.LOGTAG, type + " is not a known channel binding");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,7 +90,8 @@ public abstract class SaslMechanism {
|
||||||
this.account = account;
|
this.account = account;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SaslMechanism of(final Collection<String> mechanisms) {
|
public SaslMechanism of(
|
||||||
|
final Collection<String> mechanisms, final Collection<ChannelBinding> bindings) {
|
||||||
if (mechanisms.contains(External.MECHANISM) && account.getPrivateKeyAlias() != null) {
|
if (mechanisms.contains(External.MECHANISM) && account.getPrivateKeyAlias() != null) {
|
||||||
return new External(account);
|
return new External(account);
|
||||||
} else if (mechanisms.contains(ScramSha512.MECHANISM)) {
|
} else if (mechanisms.contains(ScramSha512.MECHANISM)) {
|
||||||
|
|
|
@ -25,14 +25,15 @@ abstract class ScramMechanism extends SaslMechanism {
|
||||||
private static final byte[] SERVER_KEY_BYTES = "Server Key".getBytes();
|
private static final byte[] SERVER_KEY_BYTES = "Server Key".getBytes();
|
||||||
private static final Cache<CacheKey, KeyPair> CACHE =
|
private static final Cache<CacheKey, KeyPair> CACHE =
|
||||||
CacheBuilder.newBuilder().maximumSize(10).build();
|
CacheBuilder.newBuilder().maximumSize(10).build();
|
||||||
|
protected final ChannelBinding channelBinding;
|
||||||
private final String clientNonce;
|
private final String clientNonce;
|
||||||
protected State state = State.INITIAL;
|
protected State state = State.INITIAL;
|
||||||
private String clientFirstMessageBare;
|
private String clientFirstMessageBare;
|
||||||
private byte[] serverSignature = null;
|
private byte[] serverSignature = null;
|
||||||
|
|
||||||
ScramMechanism(final Account account) {
|
ScramMechanism(final Account account, final ChannelBinding channelBinding) {
|
||||||
super(account);
|
super(account);
|
||||||
|
this.channelBinding = channelBinding;
|
||||||
// This nonce should be different for each authentication attempt.
|
// This nonce should be different for each authentication attempt.
|
||||||
this.clientNonce = CryptoHelper.random(100);
|
this.clientNonce = CryptoHelper.random(100);
|
||||||
clientFirstMessageBare = "";
|
clientFirstMessageBare = "";
|
||||||
|
|
|
@ -11,7 +11,7 @@ public class ScramSha1 extends ScramMechanism {
|
||||||
public static final String MECHANISM = "SCRAM-SHA-1";
|
public static final String MECHANISM = "SCRAM-SHA-1";
|
||||||
|
|
||||||
public ScramSha1(final Account account) {
|
public ScramSha1(final Account account) {
|
||||||
super(account);
|
super(account, ChannelBinding.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,7 +11,7 @@ public class ScramSha256 extends ScramMechanism {
|
||||||
public static final String MECHANISM = "SCRAM-SHA-256";
|
public static final String MECHANISM = "SCRAM-SHA-256";
|
||||||
|
|
||||||
public ScramSha256(final Account account) {
|
public ScramSha256(final Account account) {
|
||||||
super(account);
|
super(account, ChannelBinding.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,7 +11,7 @@ public class ScramSha512 extends ScramMechanism {
|
||||||
public static final String MECHANISM = "SCRAM-SHA-512";
|
public static final String MECHANISM = "SCRAM-SHA-512";
|
||||||
|
|
||||||
public ScramSha512(final Account account) {
|
public ScramSha512(final Account account) {
|
||||||
super(account);
|
super(account, ChannelBinding.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,6 +17,7 @@ public final class Namespace {
|
||||||
public static final String OOB = "jabber:x:oob";
|
public static final String OOB = "jabber:x:oob";
|
||||||
public static final String SASL = "urn:ietf:params:xml:ns:xmpp-sasl";
|
public static final String SASL = "urn:ietf:params:xml:ns:xmpp-sasl";
|
||||||
public static final String SASL_2 = "urn:xmpp:sasl:1";
|
public static final String SASL_2 = "urn:xmpp:sasl:1";
|
||||||
|
public static final String CHANNEL_BINDING = "urn:xmpp:sasl-cb:0";
|
||||||
public static final String TLS = "urn:ietf:params:xml:ns:xmpp-tls";
|
public static final String TLS = "urn:ietf:params:xml:ns:xmpp-tls";
|
||||||
public static final String PUBSUB = "http://jabber.org/protocol/pubsub";
|
public static final String PUBSUB = "http://jabber.org/protocol/pubsub";
|
||||||
public static final String PUBSUB_PUBLISH_OPTIONS = PUBSUB + "#publish-options";
|
public static final String PUBSUB_PUBLISH_OPTIONS = PUBSUB + "#publish-options";
|
||||||
|
|
|
@ -14,6 +14,7 @@ import android.util.SparseArray;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
|
|
||||||
|
@ -62,14 +63,8 @@ import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.crypto.XmppDomainVerifier;
|
import eu.siacs.conversations.crypto.XmppDomainVerifier;
|
||||||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
||||||
import eu.siacs.conversations.crypto.sasl.Anonymous;
|
import eu.siacs.conversations.crypto.sasl.ChannelBinding;
|
||||||
import eu.siacs.conversations.crypto.sasl.DigestMd5;
|
|
||||||
import eu.siacs.conversations.crypto.sasl.External;
|
|
||||||
import eu.siacs.conversations.crypto.sasl.Plain;
|
|
||||||
import eu.siacs.conversations.crypto.sasl.SaslMechanism;
|
import eu.siacs.conversations.crypto.sasl.SaslMechanism;
|
||||||
import eu.siacs.conversations.crypto.sasl.ScramSha1;
|
|
||||||
import eu.siacs.conversations.crypto.sasl.ScramSha256;
|
|
||||||
import eu.siacs.conversations.crypto.sasl.ScramSha512;
|
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
|
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
|
||||||
|
@ -1218,10 +1213,30 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authenticate(final SaslMechanism.Version version) throws IOException {
|
private void authenticate(final SaslMechanism.Version version) throws IOException {
|
||||||
final Element element = streamFeatures.findChild("mechanisms");
|
Log.d(Config.LOGTAG, "stream features: " + this.streamFeatures);
|
||||||
final Collection<String> mechanisms = Collections2.transform(element.getChildren(), c -> c == null ? null : c.getContent());
|
final Element element =
|
||||||
|
this.streamFeatures.findChild("mechanisms"); // TODO get from correct NS
|
||||||
|
final Collection<String> mechanisms =
|
||||||
|
Collections2.transform(
|
||||||
|
Collections2.filter(
|
||||||
|
element.getChildren(),
|
||||||
|
c -> c != null && "mechanism".equals(c.getName())),
|
||||||
|
c -> c == null ? null : c.getContent());
|
||||||
|
final Element cbElement =
|
||||||
|
this.streamFeatures.findChild("sasl-channel-binding", Namespace.CHANNEL_BINDING);
|
||||||
|
final Collection<ChannelBinding> channelBindings =
|
||||||
|
Collections2.filter(
|
||||||
|
Collections2.transform(
|
||||||
|
Collections2.filter(
|
||||||
|
cbElement == null
|
||||||
|
? Collections.emptyList()
|
||||||
|
: cbElement.getChildren(),
|
||||||
|
c -> c != null && "channel-binding".equals(c.getName())),
|
||||||
|
c -> c == null ? null : ChannelBinding.of(c.getAttribute("type"))),
|
||||||
|
Predicates.notNull());
|
||||||
|
Log.d(Config.LOGTAG, "channel bindings: " + channelBindings);
|
||||||
final SaslMechanism.Factory factory = new SaslMechanism.Factory(account);
|
final SaslMechanism.Factory factory = new SaslMechanism.Factory(account);
|
||||||
this.saslMechanism = factory.of(mechanisms);
|
this.saslMechanism = factory.of(mechanisms, channelBindings);
|
||||||
|
|
||||||
if (saslMechanism == null) {
|
if (saslMechanism == null) {
|
||||||
Log.d(
|
Log.d(
|
||||||
|
|
Loading…
Reference in a new issue