1package eu.siacs.conversations.crypto.sasl;
2
3import com.google.common.base.Strings;
4
5import java.util.Collection;
6
7import javax.net.ssl.SSLSocket;
8
9import eu.siacs.conversations.entities.Account;
10import eu.siacs.conversations.xml.Element;
11import eu.siacs.conversations.xml.Namespace;
12
13public abstract class SaslMechanism {
14
15 protected final Account account;
16
17 protected SaslMechanism(final Account account) {
18 this.account = account;
19 }
20
21 /**
22 * The priority is used to pin the authentication mechanism. If authentication fails, it MAY be
23 * retried with another mechanism of the same priority, but MUST NOT be tried with a mechanism
24 * of lower priority (to prevent downgrade attacks).
25 *
26 * @return An arbitrary int representing the priority
27 */
28 public abstract int getPriority();
29
30 public abstract String getMechanism();
31
32 public String getClientFirstMessage() {
33 return "";
34 }
35
36 public String getResponse(final String challenge, final SSLSocket sslSocket)
37 throws AuthenticationException {
38 return "";
39 }
40
41 protected enum State {
42 INITIAL,
43 AUTH_TEXT_SENT,
44 RESPONSE_SENT,
45 VALID_SERVER_RESPONSE,
46 }
47
48 public enum Version {
49 SASL,
50 SASL_2;
51
52 public static Version of(final Element element) {
53 switch (Strings.nullToEmpty(element.getNamespace())) {
54 case Namespace.SASL:
55 return SASL;
56 case Namespace.SASL_2:
57 return SASL_2;
58 default:
59 throw new IllegalArgumentException("Unrecognized SASL namespace");
60 }
61 }
62 }
63
64 public static class AuthenticationException extends Exception {
65 public AuthenticationException(final String message) {
66 super(message);
67 }
68
69 public AuthenticationException(final Exception inner) {
70 super(inner);
71 }
72
73 public AuthenticationException(final String message, final Exception exception) {
74 super(message, exception);
75 }
76 }
77
78 public static class InvalidStateException extends AuthenticationException {
79 public InvalidStateException(final String message) {
80 super(message);
81 }
82
83 public InvalidStateException(final State state) {
84 this("Invalid state: " + state.toString());
85 }
86 }
87
88 public static final class Factory {
89
90 private final Account account;
91
92 public Factory(final Account account) {
93 this.account = account;
94 }
95
96 public SaslMechanism of(
97 final Collection<String> mechanisms, final Collection<ChannelBinding> bindings) {
98 if (mechanisms.contains(External.MECHANISM) && account.getPrivateKeyAlias() != null) {
99 return new External(account);
100 } else if (mechanisms.contains(ScramSha512.MECHANISM)) {
101 return new ScramSha512(account);
102 } else if (mechanisms.contains(ScramSha256.MECHANISM)) {
103 return new ScramSha256(account);
104 } else if (mechanisms.contains(ScramSha1.MECHANISM)) {
105 return new ScramSha1(account);
106 } else if (mechanisms.contains(Plain.MECHANISM)
107 && !account.getServer().equals("nimbuzz.com")) {
108 return new Plain(account);
109 } else if (mechanisms.contains(DigestMd5.MECHANISM)) {
110 return new DigestMd5(account);
111 } else if (mechanisms.contains(Anonymous.MECHANISM)) {
112 return new Anonymous(account);
113 } else {
114 return null;
115 }
116 }
117 }
118
119 public static String namespace(final Version version) {
120 if (version == Version.SASL) {
121 return Namespace.SASL;
122 } else {
123 return Namespace.SASL_2;
124 }
125 }
126}