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;
}
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) {
return new External(account);
} 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 Cache<CacheKey, KeyPair> CACHE =
CacheBuilder.newBuilder().maximumSize(10).build();
protected final ChannelBinding channelBinding;
private final String clientNonce;
protected State state = State.INITIAL;
private String clientFirstMessageBare;
private byte[] serverSignature = null;
ScramMechanism(final Account account) {
ScramMechanism(final Account account, final ChannelBinding channelBinding) {
super(account);
this.channelBinding = channelBinding;
// This nonce should be different for each authentication attempt.
this.clientNonce = CryptoHelper.random(100);
clientFirstMessageBare = "";

View file

@ -11,7 +11,7 @@ public class ScramSha1 extends ScramMechanism {
public static final String MECHANISM = "SCRAM-SHA-1";
public ScramSha1(final Account account) {
super(account);
super(account, ChannelBinding.NONE);
}
@Override

View file

@ -11,7 +11,7 @@ public class ScramSha256 extends ScramMechanism {
public static final String MECHANISM = "SCRAM-SHA-256";
public ScramSha256(final Account account) {
super(account);
super(account, ChannelBinding.NONE);
}
@Override

View file

@ -11,7 +11,7 @@ public class ScramSha512 extends ScramMechanism {
public static final String MECHANISM = "SCRAM-SHA-512";
public ScramSha512(final Account account) {
super(account);
super(account, ChannelBinding.NONE);
}
@Override

View file

@ -17,6 +17,7 @@ public final class Namespace {
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_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 PUBSUB = "http://jabber.org/protocol/pubsub";
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 com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
@ -62,14 +63,8 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.XmppDomainVerifier;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.sasl.Anonymous;
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.ChannelBinding;
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.Message;
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
@ -720,7 +715,7 @@ public class XmppConnection implements Runnable {
Log.d(
Config.LOGTAG,
account.getJid().asBareJid().toString() + ": logged in (using " + version + ")");
//TODO store mechanism name
// TODO store mechanism name
account.setKey(Account.PINNED_MECHANISM_KEY, String.valueOf(saslMechanism.getPriority()));
if (version == SaslMechanism.Version.SASL_2) {
final String authorizationIdentifier =
@ -784,7 +779,7 @@ public class XmppConnection implements Runnable {
account.getJid().asBareJid() + ": successfully enabled carbons");
features.carbonsEnabled = true;
}
//TODO if both are set mark account ready for pipelining
// TODO if both are set mark account ready for pipelining
sendPostBindInitialization(streamManagementEnabled != null, carbonsEnabled != null);
}
}
@ -1218,10 +1213,30 @@ public class XmppConnection implements Runnable {
}
private void authenticate(final SaslMechanism.Version version) throws IOException {
final Element element = streamFeatures.findChild("mechanisms");
final Collection<String> mechanisms = Collections2.transform(element.getChildren(), c -> c == null ? null : c.getContent());
Log.d(Config.LOGTAG, "stream features: " + this.streamFeatures);
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);
this.saslMechanism = factory.of(mechanisms);
this.saslMechanism = factory.of(mechanisms, channelBindings);
if (saslMechanism == null) {
Log.d(