SaslMechanism.java

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