SaslMechanism.java

  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}