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}