ChannelBinding.java

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