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