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.Collections2;
10
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.Collections;
14
15import eu.siacs.conversations.Config;
16import eu.siacs.conversations.utils.SSLSockets;
17import eu.siacs.conversations.xml.Element;
18import eu.siacs.conversations.xml.Namespace;
19
20public enum ChannelBinding {
21    NONE,
22    TLS_EXPORTER,
23    TLS_SERVER_END_POINT,
24    TLS_UNIQUE;
25
26    public static Collection<ChannelBinding> of(final Element channelBinding) {
27        Preconditions.checkArgument(
28                channelBinding == null
29                        || ("sasl-channel-binding".equals(channelBinding.getName())
30                                && Namespace.CHANNEL_BINDING.equals(channelBinding.getNamespace())),
31                "pass null or a valid channel binding stream feature");
32        return Collections2.filter(
33                Collections2.transform(
34                        Collections2.filter(
35                                channelBinding == null
36                                        ? Collections.emptyList()
37                                        : channelBinding.getChildren(),
38                                c -> c != null && "channel-binding".equals(c.getName())),
39                        c -> c == null ? null : ChannelBinding.of(c.getAttribute("type"))),
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 ensureBest(final ChannelBinding channelBinding, final SSLSockets.Version sslVersion) {
89        return ChannelBinding.best(Collections.singleton(channelBinding), sslVersion) == channelBinding;
90    }
91}