extract channel binding types via XEP-0440

This commit is contained in:
Daniel Gultsch 2022-09-06 14:53:12 +02:00
parent a210568a9c
commit b78acb6fca
8 changed files with 63 additions and 18 deletions

View file

@ -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;
}
}
}

View file

@ -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)) {

View file

@ -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 = "";

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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";

View file

@ -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(