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