ChannelBinding.java

  1package eu.siacs.conversations.crypto.sasl;
  2
  3import android.util.Log;
  4import com.google.common.base.CaseFormat;
  5import com.google.common.base.Predicates;
  6import com.google.common.base.Strings;
  7import com.google.common.collect.BiMap;
  8import com.google.common.collect.Collections2;
  9import com.google.common.collect.ImmutableBiMap;
 10import eu.siacs.conversations.Config;
 11import eu.siacs.conversations.utils.SSLSockets;
 12import im.conversations.android.xmpp.model.cb.SaslChannelBinding;
 13import java.util.Arrays;
 14import java.util.Collection;
 15import java.util.Collections;
 16
 17public enum ChannelBinding {
 18    NONE,
 19    TLS_EXPORTER,
 20    TLS_SERVER_END_POINT,
 21    TLS_UNIQUE;
 22
 23    public static final BiMap<ChannelBinding, String> SHORT_NAMES;
 24
 25    static {
 26        final ImmutableBiMap.Builder<ChannelBinding, String> builder = ImmutableBiMap.builder();
 27        for (final ChannelBinding cb : values()) {
 28            builder.put(cb, shortName(cb));
 29        }
 30        SHORT_NAMES = builder.build();
 31    }
 32
 33    public static Collection<ChannelBinding> of(final SaslChannelBinding channelBinding) {
 34        if (channelBinding == null) {
 35            return Collections.emptyList();
 36        }
 37        return Collections2.filter(
 38                Collections2.transform(
 39                        channelBinding.getChannelBindings(), cb -> ChannelBinding.of(cb.getType())),
 40                Predicates.notNull());
 41    }
 42
 43    private static ChannelBinding of(final String type) {
 44        if (type == null) {
 45            return null;
 46        }
 47        try {
 48            return valueOf(
 49                    CaseFormat.LOWER_HYPHEN.converterTo(CaseFormat.UPPER_UNDERSCORE).convert(type));
 50        } catch (final IllegalArgumentException e) {
 51            Log.d(Config.LOGTAG, type + " is not a known channel binding");
 52            return null;
 53        }
 54    }
 55
 56    public static ChannelBinding get(final String name) {
 57        if (Strings.isNullOrEmpty(name)) {
 58            return NONE;
 59        }
 60        try {
 61            return valueOf(name);
 62        } catch (final IllegalArgumentException e) {
 63            return NONE;
 64        }
 65    }
 66
 67    public static ChannelBinding best(
 68            final Collection<ChannelBinding> bindings, final SSLSockets.Version sslVersion) {
 69        if (sslVersion == SSLSockets.Version.NONE) {
 70            return NONE;
 71        }
 72        if (bindings.contains(TLS_EXPORTER) && sslVersion == SSLSockets.Version.TLS_1_3) {
 73            return TLS_EXPORTER;
 74        } else if (bindings.contains(TLS_UNIQUE)
 75                && Arrays.asList(
 76                                SSLSockets.Version.TLS_1_0,
 77                                SSLSockets.Version.TLS_1_1,
 78                                SSLSockets.Version.TLS_1_2)
 79                        .contains(sslVersion)) {
 80            return TLS_UNIQUE;
 81        } else if (bindings.contains(TLS_SERVER_END_POINT)) {
 82            return TLS_SERVER_END_POINT;
 83        } else {
 84            return NONE;
 85        }
 86    }
 87
 88    public static boolean isAvailable(
 89            final ChannelBinding channelBinding, final SSLSockets.Version sslVersion) {
 90        return ChannelBinding.best(Collections.singleton(channelBinding), sslVersion)
 91                == channelBinding;
 92    }
 93
 94    private static String shortName(final ChannelBinding channelBinding) {
 95        return switch (channelBinding) {
 96            case TLS_UNIQUE -> "UNIQ";
 97            case TLS_EXPORTER -> "EXPR";
 98            case TLS_SERVER_END_POINT -> "ENDP";
 99            case NONE -> "NONE";
100            default -> throw new AssertionError("Missing short name for " + channelBinding);
101        };
102    }
103
104    public static int priority(final ChannelBinding channelBinding) {
105        if (Arrays.asList(TLS_EXPORTER, TLS_UNIQUE).contains(channelBinding)) {
106            return 2;
107        } else if (channelBinding == ChannelBinding.TLS_SERVER_END_POINT) {
108            return 1;
109        } else {
110            return 0;
111        }
112    }
113}